From 00ecdd6814a276a92e2cdd7a4da4dc33d23c45ee Mon Sep 17 00:00:00 2001 From: Tobias Anker Date: Sat, 4 Feb 2023 01:09:50 +0100 Subject: [PATCH] Add (#31): Add source-code of all repositories Added the plain source-code of all repositories, which were included as submodules until the previous commit. While doing so, there are some additional changes: - structured them into new subdirectories for a cleaner separation. - removed license-files an git-directores to keep all under the current Apache2-license - update license-header in all files to match with the license of the repository --- Hanami-AI.pro | 71 +- src/components/AzukiHeart/AzukiHeart.pro | 135 + src/components/AzukiHeart/CHANGELOG_old | 22 + src/components/AzukiHeart/README.md | 16 + src/components/AzukiHeart/build.sh | 145 + .../AzukiHeart/src/api/blossom_initializing.h | 83 + .../api/v1/measurements/power_consumption.cpp | 57 + .../api/v1/measurements/power_consumption.h | 41 + .../src/api/v1/measurements/speed.cpp | 57 + .../src/api/v1/measurements/speed.h | 41 + .../measurements/temperature_production.cpp | 57 + .../v1/measurements/temperature_production.h | 41 + .../api/v1/system_info/get_system_info.cpp | 63 + .../src/api/v1/system_info/get_system_info.h | 42 + .../api/v1/threading/get_thread_mapping.cpp | 65 + .../src/api/v1/threading/get_thread_mapping.h | 41 + src/components/AzukiHeart/src/args.h | 48 + src/components/AzukiHeart/src/azuki_root.cpp | 107 + src/components/AzukiHeart/src/azuki_root.h | 56 + src/components/AzukiHeart/src/callbacks.h | 161 + src/components/AzukiHeart/src/config.h | 39 + .../AzukiHeart/src/core/power_measuring.cpp | 79 + .../AzukiHeart/src/core/power_measuring.h | 56 + .../AzukiHeart/src/core/speed_measuring.cpp | 86 + .../AzukiHeart/src/core/speed_measuring.h | 56 + .../src/core/temperature_measuring.cpp | 68 + .../src/core/temperature_measuring.h | 56 + .../AzukiHeart/src/core/thread_binder.cpp | 421 ++ .../AzukiHeart/src/core/thread_binder.h | 76 + .../AzukiHeart/src/core/value_container.cpp | 124 + .../AzukiHeart/src/core/value_container.h | 58 + src/components/AzukiHeart/src/main.cpp | 76 + src/components/KyoukoMind/CHANGELOG_old | 111 + src/components/KyoukoMind/KyoukoMind.pro | 258 ++ src/components/KyoukoMind/README.md | 15 + src/components/KyoukoMind/build.sh | 146 + .../KyoukoMind/src/api/blossom_initializing.h | 201 + .../src/api/v1/cluster/create_cluster.cpp | 431 +++ .../src/api/v1/cluster/create_cluster.h | 68 + .../src/api/v1/cluster/delete_cluster.cpp | 97 + .../src/api/v1/cluster/delete_cluster.h | 41 + .../src/api/v1/cluster/list_cluster.cpp | 80 + .../src/api/v1/cluster/list_cluster.h | 41 + .../src/api/v1/cluster/load_cluster.cpp | 124 + .../src/api/v1/cluster/load_cluster.h | 41 + .../src/api/v1/cluster/save_cluster.cpp | 113 + .../src/api/v1/cluster/save_cluster.h | 41 + .../src/api/v1/cluster/set_cluster_mode.cpp | 159 + .../src/api/v1/cluster/set_cluster_mode.h | 41 + .../src/api/v1/cluster/show_cluster.cpp | 96 + .../src/api/v1/cluster/show_cluster.h | 41 + .../src/api/v1/task/create_task.cpp | 349 ++ .../KyoukoMind/src/api/v1/task/create_task.h | 65 + .../src/api/v1/task/delete_task.cpp | 89 + .../KyoukoMind/src/api/v1/task/delete_task.h | 41 + .../KyoukoMind/src/api/v1/task/list_task.cpp | 162 + .../KyoukoMind/src/api/v1/task/list_task.h | 41 + .../KyoukoMind/src/api/v1/task/show_task.cpp | 139 + .../KyoukoMind/src/api/v1/task/show_task.h | 41 + .../src/api/v1/template/delete_template.cpp | 89 + .../src/api/v1/template/delete_template.h | 41 + .../src/api/v1/template/list_templates.cpp | 84 + .../src/api/v1/template/list_templates.h | 41 + .../src/api/v1/template/show_template.cpp | 109 + .../src/api/v1/template/show_template.h | 41 + .../src/api/v1/template/upload_template.cpp | 155 + .../src/api/v1/template/upload_template.h | 41 + src/components/KyoukoMind/src/args.h | 47 + src/components/KyoukoMind/src/callbacks.cpp | 51 + src/components/KyoukoMind/src/callbacks.h | 39 + src/components/KyoukoMind/src/common.h | 33 + .../KyoukoMind/src/common/defines.h | 35 + src/components/KyoukoMind/src/common/enums.h | 32 + .../KyoukoMind/src/common/functions.h | 27 + .../KyoukoMind/src/common/includes.h | 74 + .../KyoukoMind/src/common/structs.h | 34 + .../KyoukoMind/src/common/typedefs.h | 35 + src/components/KyoukoMind/src/config.h | 38 + .../KyoukoMind/src/core/callbacks.h | 56 + .../KyoukoMind/src/core/cluster/cluster.cpp | 656 ++++ .../KyoukoMind/src/core/cluster/cluster.h | 169 + .../src/core/cluster/cluster_handler.cpp | 98 + .../src/core/cluster/cluster_handler.h | 43 + .../src/core/cluster/cluster_init.cpp | 288 ++ .../src/core/cluster/cluster_init.h | 54 + .../src/core/cluster/statemachine_init.cpp | 212 + .../src/core/cluster/statemachine_init.h | 86 + .../cluster/states/cycle_finish_state.cpp | 82 + .../core/cluster/states/cycle_finish_state.h | 43 + .../graphs/table_interpolation_state.cpp | 157 + .../states/graphs/table_interpolation_state.h | 43 + .../graphs/table_learn_forward_state.cpp | 150 + .../states/graphs/table_learn_forward_state.h | 43 + .../states/images/image_identify_state.cpp | 70 + .../states/images/image_identify_state.h | 43 + .../images/image_learn_forward_state.cpp | 78 + .../states/images/image_learn_forward_state.h | 43 + .../snapshots/restore_cluster_state.cpp | 184 + .../states/snapshots/restore_cluster_state.h | 51 + .../states/snapshots/save_cluster_state.cpp | 187 + .../states/snapshots/save_cluster_state.h | 54 + .../tables/table_interpolation_state.cpp | 75 + .../states/tables/table_interpolation_state.h | 43 + .../tables/table_learn_forward_state.cpp | 80 + .../states/tables/table_learn_forward_state.h | 43 + .../core/cluster/states/task_handle_state.cpp | 403 ++ .../core/cluster/states/task_handle_state.h | 64 + .../KyoukoMind/src/core/cluster/task.h | 78 + .../core/processing/cpu_processing_unit.cpp | 265 ++ .../src/core/processing/cpu_processing_unit.h | 50 + .../processing/processing_unit_handler.cpp | 55 + .../core/processing/processing_unit_handler.h | 42 + .../src/core/processing/segment_queue.cpp | 83 + .../src/core/processing/segment_queue.h | 45 + .../KyoukoMind/src/core/routing_functions.h | 302 ++ .../src/core/segments/abstract_segment.cpp | 202 + .../src/core/segments/abstract_segment.h | 86 + .../KyoukoMind/src/core/segments/brick.h | 49 + .../dynamic_segment/backpropagation.h | 224 ++ .../dynamic_segment/dynamic_segment.cpp | 776 ++++ .../dynamic_segment/dynamic_segment.h | 78 + .../segments/dynamic_segment/gpu_kernel.cl | 920 +++++ .../core/segments/dynamic_segment/objects.h | 156 + .../segments/dynamic_segment/processing.h | 389 ++ .../core/segments/dynamic_segment/reduction.h | 115 + .../segments/dynamic_segment/section_update.h | 135 + .../segments/input_segment/input_segment.cpp | 239 ++ .../segments/input_segment/input_segment.h | 55 + .../src/core/segments/input_segment/objects.h | 40 + .../core/segments/input_segment/processing.h | 54 + .../segments/output_segment/backpropagation.h | 58 + .../core/segments/output_segment/objects.h | 40 + .../output_segment/output_segment.cpp | 240 ++ .../segments/output_segment/output_segment.h | 58 + .../core/segments/output_segment/processing.h | 93 + .../src/core/segments/segment_meta.h | 192 + .../KyoukoMind/src/core/struct_validation.cpp | 53 + .../KyoukoMind/src/core/struct_validation.h | 30 + .../KyoukoMind/src/database/cluster_table.cpp | 187 + .../KyoukoMind/src/database/cluster_table.h | 60 + .../src/database/template_table.cpp | 192 + .../KyoukoMind/src/database/template_table.h | 60 + .../KyoukoMind/src/io/protobuf_messages.cpp | 246 ++ .../KyoukoMind/src/io/protobuf_messages.h | 37 + src/components/KyoukoMind/src/kyouko_root.cpp | 173 + src/components/KyoukoMind/src/kyouko_root.h | 63 + src/components/KyoukoMind/src/main.cpp | 104 + src/components/MisakiGuard/CHANGELOG_old | 32 + src/components/MisakiGuard/MisakiGuard.pro | 162 + src/components/MisakiGuard/README.md | 15 + src/components/MisakiGuard/build.sh | 144 + .../src/api/blossom_initializing.h | 214 ++ .../src/api/v1/auth/create_internal_token.cpp | 102 + .../src/api/v1/auth/create_internal_token.h | 41 + .../src/api/v1/auth/create_token.cpp | 169 + .../src/api/v1/auth/create_token.h | 41 + .../src/api/v1/auth/list_user_projects.cpp | 116 + .../src/api/v1/auth/list_user_projects.h | 41 + .../src/api/v1/auth/renew_token.cpp | 168 + .../MisakiGuard/src/api/v1/auth/renew_token.h | 45 + .../src/api/v1/auth/validate_access.cpp | 174 + .../src/api/v1/auth/validate_access.h | 42 + .../documentation/generate_rest_api_docu.cpp | 347 ++ .../v1/documentation/generate_rest_api_docu.h | 46 + .../src/api/v1/project/create_project.cpp | 138 + .../src/api/v1/project/create_project.h | 41 + .../src/api/v1/project/delete_project.cpp | 94 + .../src/api/v1/project/delete_project.h | 41 + .../src/api/v1/project/get_project.cpp | 99 + .../src/api/v1/project/get_project.h | 41 + .../src/api/v1/project/list_projects.cpp | 84 + .../src/api/v1/project/list_projects.h | 41 + .../src/api/v1/user/add_project_to_user.cpp | 176 + .../src/api/v1/user/add_project_to_user.h | 41 + .../src/api/v1/user/create_user.cpp | 161 + .../MisakiGuard/src/api/v1/user/create_user.h | 41 + .../src/api/v1/user/delete_user.cpp | 106 + .../MisakiGuard/src/api/v1/user/delete_user.h | 41 + .../MisakiGuard/src/api/v1/user/get_user.cpp | 105 + .../MisakiGuard/src/api/v1/user/get_user.h | 41 + .../src/api/v1/user/list_users.cpp | 86 + .../MisakiGuard/src/api/v1/user/list_users.h | 41 + .../api/v1/user/remove_project_from_user.cpp | 165 + .../api/v1/user/remove_project_from_user.h | 41 + src/components/MisakiGuard/src/args.h | 48 + src/components/MisakiGuard/src/callbacks.h | 46 + src/components/MisakiGuard/src/config.h | 43 + .../src/database/projects_table.cpp | 144 + .../MisakiGuard/src/database/projects_table.h | 53 + .../MisakiGuard/src/database/users_table.cpp | 316 ++ .../MisakiGuard/src/database/users_table.h | 61 + src/components/MisakiGuard/src/main.cpp | 80 + .../MisakiGuard/src/misaki_root.cpp | 198 + src/components/MisakiGuard/src/misaki_root.h | 50 + src/components/ShioriArchive/CHANGELOG_old | 36 + src/components/ShioriArchive/README.md | 15 + .../ShioriArchive/ShioriArchive.pro | 182 + src/components/ShioriArchive/build.sh | 140 + .../src/api/blossom_initializing.h | 237 ++ .../create_cluster_snapshot.cpp | 188 + .../create_cluster_snapshot.h | 41 + .../delete_cluster_snapshot.cpp | 98 + .../delete_cluster_snapshot.h | 41 + .../finish_cluster_snapshot.cpp | 138 + .../finish_cluster_snapshot.h | 41 + .../cluster_snapshot/get_cluster_snapshot.cpp | 97 + .../cluster_snapshot/get_cluster_snapshot.h | 41 + .../list_cluster_snapshot.cpp | 80 + .../cluster_snapshot/list_cluster_snapshot.h | 41 + .../src/api/v1/data_files/check_data_set.cpp | 159 + .../src/api/v1/data_files/check_data_set.h | 41 + .../v1/data_files/csv/create_csv_data_set.cpp | 156 + .../v1/data_files/csv/create_csv_data_set.h | 41 + .../data_files/csv/finalize_csv_data_set.cpp | 291 ++ .../v1/data_files/csv/finalize_csv_data_set.h | 50 + .../src/api/v1/data_files/delete_data_set.cpp | 96 + .../src/api/v1/data_files/delete_data_set.h | 41 + .../src/api/v1/data_files/get_data_set.cpp | 216 ++ .../src/api/v1/data_files/get_data_set.h | 45 + .../v1/data_files/get_progress_data_set.cpp | 119 + .../api/v1/data_files/get_progress_data_set.h | 42 + .../src/api/v1/data_files/list_data_set.cpp | 81 + .../src/api/v1/data_files/list_data_set.h | 41 + .../mnist/create_mnist_data_set.cpp | 179 + .../data_files/mnist/create_mnist_data_set.h | 41 + .../mnist/finalize_mnist_data_set.cpp | 269 ++ .../mnist/finalize_mnist_data_set.h | 48 + .../src/api/v1/logs/get_audit_log.cpp | 116 + .../src/api/v1/logs/get_audit_log.h | 41 + .../src/api/v1/logs/get_error_log.cpp | 110 + .../src/api/v1/logs/get_error_log.h | 41 + .../request_results/delete_request_result.cpp | 85 + .../request_results/delete_request_result.h | 41 + .../v1/request_results/get_request_result.cpp | 100 + .../v1/request_results/get_request_result.h | 41 + .../request_results/list_request_result.cpp | 80 + .../v1/request_results/list_request_result.h | 41 + src/components/ShioriArchive/src/args.h | 48 + src/components/ShioriArchive/src/callbacks.h | 438 +++ src/components/ShioriArchive/src/config.h | 42 + .../src/core/data_set_files/data_set_file.cpp | 204 + .../src/core/data_set_files/data_set_file.h | 119 + .../data_set_files/image_data_set_file.cpp | 120 + .../core/data_set_files/image_data_set_file.h | 46 + .../data_set_files/table_data_set_file.cpp | 237 ++ .../core/data_set_files/table_data_set_file.h | 49 + .../src/core/temp_file_handler.cpp | 236 ++ .../src/core/temp_file_handler.h | 58 + .../src/database/audit_log_table.cpp | 126 + .../src/database/audit_log_table.h | 52 + .../src/database/cluster_snapshot_table.cpp | 237 ++ .../src/database/cluster_snapshot_table.h | 59 + .../src/database/data_set_table.cpp | 237 ++ .../src/database/data_set_table.h | 60 + .../src/database/error_log_table.cpp | 135 + .../src/database/error_log_table.h | 53 + .../src/database/request_result_table.cpp | 164 + .../src/database/request_result_table.h | 56 + src/components/ShioriArchive/src/main.cpp | 83 + .../ShioriArchive/src/shiori_root.cpp | 126 + .../ShioriArchive/src/shiori_root.h | 54 + src/components/ToriiGateway/CHANGELOG_old | 94 + src/components/ToriiGateway/README.md | 15 + src/components/ToriiGateway/ToriiGateway.pro | 115 + src/components/ToriiGateway/build.sh | 135 + src/components/ToriiGateway/src/args.h | 47 + src/components/ToriiGateway/src/callbacks.cpp | 56 + src/components/ToriiGateway/src/callbacks.h | 45 + src/components/ToriiGateway/src/config.h | 82 + .../src/core/http_processing/file_send.cpp | 125 + .../src/core/http_processing/file_send.h | 46 + .../core/http_processing/http_processing.cpp | 313 ++ .../core/http_processing/http_processing.h | 82 + .../core/http_processing/response_builds.h | 107 + .../core/http_processing/string_functions.h | 155 + .../ToriiGateway/src/core/http_server.cpp | 182 + .../ToriiGateway/src/core/http_server.h | 78 + .../src/core/http_websocket_thread.cpp | 525 +++ .../src/core/http_websocket_thread.h | 92 + src/components/ToriiGateway/src/main.cpp | 74 + .../ToriiGateway/src/torii_root.cpp | 139 + src/components/ToriiGateway/src/torii_root.h | 52 + src/components/TsugumiTester/CHANGELOG_old | 25 + src/components/TsugumiTester/README.md | 15 + .../TsugumiTester/TsugumiTester.pro | 136 + src/components/TsugumiTester/build.sh | 136 + src/components/TsugumiTester/src/args.h | 48 + .../TsugumiTester/src/common/test_step.cpp | 47 + .../TsugumiTester/src/common/test_step.h | 49 + .../TsugumiTester/src/common/test_thread.cpp | 125 + .../TsugumiTester/src/common/test_thread.h | 60 + src/components/TsugumiTester/src/config.h | 51 + src/components/TsugumiTester/src/main.cpp | 49 + .../kyouko/cluster/cluster_create_test.cpp | 65 + .../kyouko/cluster/cluster_create_test.h | 38 + .../kyouko/cluster/cluster_delete_test.cpp | 62 + .../kyouko/cluster/cluster_delete_test.h | 38 + .../kyouko/cluster/cluster_get_test.cpp | 65 + .../kyouko/cluster/cluster_get_test.h | 42 + .../kyouko/cluster/cluster_list_test.cpp | 61 + .../kyouko/cluster/cluster_list_test.h | 38 + .../kyouko/cluster/cluster_load_test.cpp | 83 + .../kyouko/cluster/cluster_load_test.h | 38 + .../kyouko/cluster/cluster_save_test.cpp | 84 + .../kyouko/cluster/cluster_save_test.h | 38 + .../cluster/cluster_switch_to_direct_test.cpp | 60 + .../cluster/cluster_switch_to_direct_test.h | 38 + .../cluster/cluster_switch_to_task_test.cpp | 52 + .../cluster/cluster_switch_to_task_test.h | 38 + .../kyouko/io/direct_io_test.cpp | 923 +++++ .../rest_api_tests/kyouko/io/direct_io_test.h | 45 + .../kyouko/task/image_learn_task_test.cpp | 105 + .../kyouko/task/image_learn_task_test.h | 38 + .../kyouko/task/image_request_task_test.cpp | 113 + .../kyouko/task/image_request_task_test.h | 38 + .../kyouko/task/table_learn_task_test.cpp | 85 + .../kyouko/task/table_learn_task_test.h | 38 + .../kyouko/task/table_request_task_test.cpp | 93 + .../kyouko/task/table_request_task_test.h | 38 + .../kyouko/template/template_delete_test.cpp | 62 + .../kyouko/template/template_delete_test.h | 38 + .../kyouko/template/template_get_test.cpp | 67 + .../kyouko/template/template_get_test.h | 42 + .../kyouko/template/template_list_test.cpp | 61 + .../kyouko/template/template_list_test.h | 38 + .../kyouko/template/template_upload_test.cpp | 65 + .../kyouko/template/template_upload_test.h | 38 + .../misaki/project/project_create_test.cpp | 65 + .../misaki/project/project_create_test.h | 38 + .../misaki/project/project_delete_test.cpp | 76 + .../misaki/project/project_delete_test.h | 42 + .../misaki/project/project_get_test.cpp | 67 + .../misaki/project/project_get_test.h | 42 + .../misaki/project/project_list_test.cpp | 61 + .../misaki/project/project_list_test.h | 38 + .../misaki/user/user_create_test.cpp | 67 + .../misaki/user/user_create_test.h | 38 + .../misaki/user/user_delete_test.cpp | 76 + .../misaki/user/user_delete_test.h | 42 + .../misaki/user/user_get_test.cpp | 67 + .../misaki/user/user_get_test.h | 42 + .../misaki/user/user_list_test.cpp | 61 + .../misaki/user/user_list_test.h | 38 + .../src/rest_api_tests/rest_api_tests.cpp | 298 ++ .../src/rest_api_tests/rest_api_tests.h | 30 + .../shiori/datasets/dataset_check_test.cpp | 66 + .../shiori/datasets/dataset_check_test.h | 38 + .../datasets/dataset_create_csv_test.cpp | 65 + .../shiori/datasets/dataset_create_csv_test.h | 38 + .../datasets/dataset_create_mnist_test.cpp | 87 + .../datasets/dataset_create_mnist_test.h | 42 + .../shiori/datasets/dataset_delete_test.cpp | 72 + .../shiori/datasets/dataset_delete_test.h | 42 + .../shiori/datasets/dataset_get_test.cpp | 73 + .../shiori/datasets/dataset_get_test.h | 44 + .../shiori/datasets/dataset_list_test.cpp | 62 + .../shiori/datasets/dataset_list_test.h | 38 + .../request_result_delete_test.cpp | 62 + .../request_result_delete_test.h | 38 + .../request_result_get_test.cpp | 66 + .../request_results/request_result_get_test.h | 42 + .../request_result_list_test.cpp | 62 + .../request_result_list_test.h | 38 + .../shiori/snapshots/snapshot_delete_test.cpp | 62 + .../shiori/snapshots/snapshot_delete_test.h | 38 + .../shiori/snapshots/snapshot_get_test.cpp | 66 + .../shiori/snapshots/snapshot_get_test.h | 42 + .../shiori/snapshots/snapshot_list_test.cpp | 62 + .../shiori/snapshots/snapshot_list_test.h | 38 + src/components/components.pro | 11 + .../README.md | 25 + .../bootstrap_icons/check-lg.svg | 3 + .../bootstrap_icons/x-lg.svg | 3 + .../d3.v6.min.js | 2 + .../jquery.min.js | 2 + .../protobuf.min.js | 8 + .../protobuf.min.js.map | 1 + src/frontend/Hanami-AI-Dashboard/.gitmodules | 9 + .../Hanami-AI-Dashboard/CHANGELOG_old | 9 + src/frontend/Hanami-AI-Dashboard/README.md | 10 + .../README.md | 25 + .../bootstrap_icons/check-lg.svg | 3 + .../bootstrap_icons/x-lg.svg | 3 + .../d3.v6.min.js | 2 + .../jquery.min.js | 2 + .../protobuf.min.js | 8 + .../protobuf.min.js.map | 1 + .../Hanami-AI-Dashboard/src/favicon.ico | Bin 0 -> 165662 bytes .../Hanami-AI-Dashboard/src/index.html | 60 + .../ISSUE_TEMPLATE/bug_issue_template.md | 22 + .../ISSUE_TEMPLATE/cleanup_issue_template.md | 12 + .../.github/ISSUE_TEMPLATE/config.yml | 2 + .../feature_request_issue_template.md | 15 + .../tag_request_issue_template.md | 14 + .../.github/PULL_REQUEST_TEMPLATE.md | 5 + .../.github/workflows/build_test.yml | 28 + .../src/libHanamiAiSdk/.gitignore | 3 + .../issue_templates/bug_issue_template.md | 4 + .../issue_templates/cleanup_issue_template.md | 4 + .../feature_request_issue_template.md | 6 + .../tag_request_issue_template.md | 9 + .../merge_request_template.md | 5 + .../src/libHanamiAiSdk/CHANGELOG | 44 + .../src/libHanamiAiSdk/LICENSE | 201 + .../src/libHanamiAiSdk/README.md | 77 + .../src/libHanamiAiSdk/cpp/README.md | 1 + .../src/libHanamiAiSdk/cpp/build.sh | 122 + .../src/libHanamiAiSdk/cpp/defaults.pri | 2 + .../cpp/include/libHanamiAiSdk/cluster.h | 69 + .../libHanamiAiSdk/common/websocket_client.h | 74 + .../cpp/include/libHanamiAiSdk/data_set.h | 64 + .../cpp/include/libHanamiAiSdk/init.h | 39 + .../cpp/include/libHanamiAiSdk/io.h | 62 + .../cpp/include/libHanamiAiSdk/project.h | 49 + .../include/libHanamiAiSdk/request_result.h | 44 + .../cpp/include/libHanamiAiSdk/snapshot.h | 44 + .../cpp/include/libHanamiAiSdk/task.h | 54 + .../cpp/include/libHanamiAiSdk/template.h | 49 + .../cpp/include/libHanamiAiSdk/user.h | 70 + .../src/libHanamiAiSdk/cpp/libHanamiAiSdk.pro | 13 + .../src/libHanamiAiSdk/cpp/src/cluster.cpp | 324 ++ .../cpp/src/common/http_client.cpp | 483 +++ .../cpp/src/common/http_client.h | 120 + .../cpp/src/common/websocket_client.cpp | 250 ++ .../src/libHanamiAiSdk/cpp/src/data_set.cpp | 615 +++ .../src/libHanamiAiSdk/cpp/src/init.cpp | 58 + .../src/libHanamiAiSdk/cpp/src/io.cpp | 281 ++ .../src/libHanamiAiSdk/cpp/src/project.cpp | 152 + .../libHanamiAiSdk/cpp/src/request_result.cpp | 115 + .../src/libHanamiAiSdk/cpp/src/snapshot.cpp | 116 + .../src/libHanamiAiSdk/cpp/src/src.pro | 98 + .../src/libHanamiAiSdk/cpp/src/task.cpp | 183 + .../src/libHanamiAiSdk/cpp/src/template.cpp | 162 + .../src/libHanamiAiSdk/cpp/src/user.cpp | 321 ++ .../src/libHanamiAiSdk/cpp/tests/tests.pro | 7 + .../src/libHanamiAiSdk/go/README.md | 1 + .../go/kitsumi_ai_sdk/cluster_commands.go | 34 + .../kitsumi_ai_sdk/documentation_commands.go | 30 + .../libHanamiAiSdk/go/kitsumi_ai_sdk/go.mod | 5 + .../go/kitsumi_ai_sdk/http_request.go | 170 + .../go/kitsumi_ai_sdk/run_commands.go | 51 + .../go/kitsumi_ai_sdk/threading_commands.go | 32 + .../go/kitsumi_ai_sdk/train_data_commands.go | 51 + .../go/kitsumi_ai_sdk/user_commands.go | 53 + .../src/libHanamiAiSdk/javascript/cluster.js | 51 + .../src/libHanamiAiSdk/javascript/common.js | 67 + .../src/libHanamiAiSdk/javascript/data_set.js | 319 ++ .../src/libHanamiAiSdk/javascript/init.js | 72 + .../src/libHanamiAiSdk/javascript/io.js | 0 .../src/libHanamiAiSdk/javascript/logs.js | 31 + .../libHanamiAiSdk/javascript/measurements.js | 33 + .../src/libHanamiAiSdk/javascript/project.js | 34 + .../javascript/request_result.js | 33 + .../src/libHanamiAiSdk/javascript/snapshot.js | 27 + .../libHanamiAiSdk/javascript/system_info.js | 28 + .../src/libHanamiAiSdk/javascript/task.js | 40 + .../src/libHanamiAiSdk/javascript/template.js | 34 + .../src/libHanamiAiSdk/javascript/user.js | 55 + .../src/libKitsunemimiHanamiMessages/LICENSE | 201 + .../libKitsunemimiHanamiMessages/README.md | 1 + .../hanami_messages/kyouko_messages.h | 148 + .../hanami_messages/shiori_messages.h | 147 + .../protobuffers/kyouko_messages.proto3 | 22 + .../protobuffers/kyouko_messages.proto3.pb.cc | 475 +++ .../protobuffers/kyouko_messages.proto3.pb.h | 582 +++ .../protobuffers/shiori_messages.proto3 | 16 + .../protobuffers/shiori_messages.proto3.pb.cc | 469 +++ .../protobuffers/shiori_messages.proto3.pb.h | 664 ++++ .../Hanami-AI-Dashboard/src/login.html | 97 + .../src/scripts/actions.js | 81 + .../Hanami-AI-Dashboard/src/scripts/common.js | 288 ++ .../src/scripts/diagrams.js | 102 + .../Hanami-AI-Dashboard/src/scripts/login.js | 91 + .../src/scripts/sidebar.js | 242 ++ .../Hanami-AI-Dashboard/src/scripts/tabbar.js | 62 + .../src/scripts/table_processing.js | 102 + .../Hanami-AI-Dashboard/src/sidebar.html | 202 + .../Hanami-AI-Dashboard/src/styles/base.css | 392 ++ .../Hanami-AI-Dashboard/src/styles/modal.css | 123 + .../src/styles/sidebar.css | 78 + .../Hanami-AI-Dashboard/src/styles/table.css | 51 + .../Hanami-AI-Dashboard/src/styles/tabs.css | 50 + .../src/subsites/azuki/power.html | 88 + .../src/subsites/azuki/speed.html | 88 + .../src/subsites/azuki/system_info.html | 52 + .../src/subsites/azuki/temperature.html | 88 + .../src/subsites/azuki/thread_mapping.html | 52 + .../src/subsites/kyouko/cluster.html | 415 ++ .../src/subsites/kyouko/segment_template.html | 247 ++ .../src/subsites/kyouko/task.html | 248 ++ .../src/subsites/misaki/project.html | 243 ++ .../src/subsites/misaki/user.html | 441 +++ .../src/subsites/shiori/audit_log.html | 95 + .../src/subsites/shiori/cluster_snapshot.html | 149 + .../src/subsites/shiori/dataset.html | 341 ++ .../src/subsites/shiori/graphical_result.html | 74 + .../src/subsites/shiori/request_result.html | 182 + src/libraries/libAzukiHeart/CHANGELOG_old | 13 + src/libraries/libAzukiHeart/README.md | 2 + src/libraries/libAzukiHeart/build.sh | 126 + src/libraries/libAzukiHeart/defaults.pri | 2 + .../include/libAzukiHeart/azuki_input.h | 37 + .../include/libAzukiHeart/azuki_send.h | 39 + src/libraries/libAzukiHeart/libAzukiHeart.pro | 11 + .../libAzukiHeart/src/azuki_input.cpp | 75 + .../libAzukiHeart/src/azuki_send.cpp | 131 + .../libAzukiHeart/src/bind_thread_to_core.cpp | 104 + .../libAzukiHeart/src/bind_thread_to_core.h | 46 + .../libAzukiHeart/src/get_thread_mapping.cpp | 85 + .../libAzukiHeart/src/get_thread_mapping.h | 46 + src/libraries/libAzukiHeart/src/src.pro | 92 + .../libKitsunemimiArgs/CHANGELOG_old | 69 + src/libraries/libKitsunemimiArgs/README.md | 193 + src/libraries/libKitsunemimiArgs/build.sh | 103 + src/libraries/libKitsunemimiArgs/defaults.pri | 2 + .../include/libKitsunemimiArgs/arg_parser.h | 141 + .../libKitsunemimiArgs/libKitsunemimiArgs.pro | 11 + .../libKitsunemimiArgs/src/arg_parser.cpp | 996 +++++ src/libraries/libKitsunemimiArgs/src/src.pro | 21 + .../tests/cli_tests/cli_tests.pro | 18 + .../tests/cli_tests/main.cpp | 98 + .../libKitsunemimiArgs/tests/tests.pro | 10 + .../tests/unit_tests/arg_parser_test.cpp | 506 +++ .../tests/unit_tests/arg_parser_test.h | 67 + .../tests/unit_tests/main.cpp | 30 + .../tests/unit_tests/unit_tests.pro | 21 + .../libKitsunemimiCommon/CHANGELOG_old | 458 +++ src/libraries/libKitsunemimiCommon/Doxyfile | 2494 ++++++++++++ src/libraries/libKitsunemimiCommon/README.md | 985 +++++ src/libraries/libKitsunemimiCommon/build.sh | 99 + .../libKitsunemimiCommon/defaults.pri | 2 + .../libKitsunemimiCommon/buffer/data_buffer.h | 404 ++ .../libKitsunemimiCommon/buffer/item_buffer.h | 165 + .../libKitsunemimiCommon/buffer/ring_buffer.h | 216 ++ .../buffer/stack_buffer.h | 260 ++ .../buffer/stack_buffer_reserve.h | 60 + .../libKitsunemimiCommon/files/binary_file.h | 65 + .../files/binary_file_direct.h | 71 + .../libKitsunemimiCommon/files/text_file.h | 56 + .../libKitsunemimiCommon/items/data_items.h | 270 ++ .../libKitsunemimiCommon/items/table_item.h | 132 + .../include/libKitsunemimiCommon/logger.h | 156 + .../libKitsunemimiCommon/memory_counter.h | 51 + .../methods/file_methods.h | 56 + .../methods/string_methods.h | 59 + .../methods/vector_methods.h | 38 + .../libKitsunemimiCommon/process_execution.h | 48 + .../libKitsunemimiCommon/progress_bar.h | 50 + .../libKitsunemimiCommon/statemachine.h | 77 + .../test_helper/compare_test_helper.h | 83 + .../test_helper/memory_leak_test_helper.h | 77 + .../test_helper/speed_test_helper.h | 105 + .../libKitsunemimiCommon/threading/barrier.h | 57 + .../threading/cleanup_thread.h | 55 + .../libKitsunemimiCommon/threading/event.h | 62 + .../threading/event_queue.h | 48 + .../libKitsunemimiCommon/threading/thread.h | 102 + .../threading/thread_handler.h | 63 + .../libKitsunemimiCommon.pro | 10 + .../src/buffer/item_buffer.cpp | 272 ++ .../src/buffer/stack_buffer_reserve.cpp | 123 + .../src/files/binary_file.cpp | 363 ++ .../src/files/binary_file_direct.cpp | 439 +++ .../src/files/text_file.cpp | 268 ++ .../src/items/data_items.cpp | 1572 ++++++++ .../src/items/table_item.cpp | 857 +++++ .../libKitsunemimiCommon/src/logger.cpp | 354 ++ .../src/memory_counter.cpp | 65 + .../src/methods/file_methods.cpp | 263 ++ .../src/methods/string_methods.cpp | 199 + .../src/methods/vector_methods.cpp | 41 + .../src/process_execution.cpp | 105 + .../libKitsunemimiCommon/src/progress_bar.cpp | 80 + .../libKitsunemimiCommon/src/src.pro | 65 + .../libKitsunemimiCommon/src/state.h | 151 + .../libKitsunemimiCommon/src/statemachine.cpp | 369 ++ .../src/test_helper/compare_test_helper.cpp | 53 + .../test_helper/memory_leak_test_helper.cpp | 99 + .../src/test_helper/speed_test_helper.cpp | 87 + .../src/threading/barrier.cpp | 75 + .../src/threading/cleanup_thread.cpp | 99 + .../src/threading/event.cpp | 68 + .../src/threading/event_queue.cpp | 87 + .../src/threading/thread.cpp | 346 ++ .../src/threading/thread_handler.cpp | 179 + .../buffer/data_buffer_test.cpp | 55 + .../buffer/data_buffer_test.h | 30 + .../buffer/item_buffer_test.cpp | 37 + .../buffer/item_buffer_test.h | 29 + .../buffer/ring_buffer_test.cpp | 36 + .../buffer/ring_buffer_test.h | 29 + .../buffer/stack_buffer_reserve_test.cpp | 37 + .../buffer/stack_buffer_reserve_test.h | 29 + .../buffer/stack_buffer_test.cpp | 39 + .../buffer/stack_buffer_test.h | 29 + .../items/data_items_test.cpp | 136 + .../items/data_items_test.h | 34 + .../items/table_item_test.cpp | 85 + .../items/table_item_test.h | 32 + .../libKitsunemimiCommon/state_test.cpp | 36 + .../libKitsunemimiCommon/state_test.h | 29 + .../statemachine_test.cpp | 42 + .../libKitsunemimiCommon/statemachine_test.h | 29 + .../threading/bogus_event.cpp | 22 + .../threading/bogus_event.h | 28 + .../threading/bogus_thread.cpp | 24 + .../threading/bogus_thread.h | 29 + .../threading/thread_test.cpp | 67 + .../threading/thread_test.h | 31 + .../tests/memory_leak_tests/main.cpp | 40 + .../memory_leak_tests/memory_leak_tests.pro | 38 + .../libKitsunemimiCommon/tests/tests.pro | 10 + .../buffer/data_buffer_test.cpp | 262 ++ .../buffer/data_buffer_test.h | 38 + .../buffer/item_buffer_test.cpp | 180 + .../buffer/item_buffer_test.h | 33 + .../buffer/ring_buffer_test.cpp | 178 + .../buffer/ring_buffer_test.h | 35 + .../buffer/stack_buffer_reserve_test.cpp | 86 + .../buffer/stack_buffer_reserve_test.h | 31 + .../buffer/stack_buffer_test.cpp | 176 + .../buffer/stack_buffer_test.h | 35 + .../files/binary_file_with_directIO_test.cpp | 300 ++ .../files/binary_file_with_directIO_test.h | 41 + .../binary_file_without_directIO_test.cpp | 290 ++ .../files/binary_file_without_directIO_test.h | 41 + .../files/text_file_test.cpp | 218 ++ .../files/text_file_test.h | 39 + .../items/data_items_DataArray_test.cpp | 266 ++ .../items/data_items_DataArray_test.h | 46 + .../items/data_items_DataMap_test.cpp | 335 ++ .../items/data_items_DataMap_test.h | 50 + .../items/data_items_DataValue_test.cpp | 392 ++ .../items/data_items_DataValue_test.h | 45 + .../items/table_item_test.cpp | 434 +++ .../items/table_item_test.h | 59 + .../libKitsunemimiCommon/logger_test.cpp | 129 + .../libKitsunemimiCommon/logger_test.h | 31 + .../methods/file_methods_test.cpp | 193 + .../methods/file_methods_test.h | 33 + .../methods/string_methods_test.cpp | 194 + .../methods/string_methods_test.h | 39 + .../methods/vector_methods_test.cpp | 41 + .../methods/vector_methods_test.h | 29 + .../progress_bar_test.cpp | 45 + .../libKitsunemimiCommon/progress_bar_test.h | 29 + .../libKitsunemimiCommon/state_test.cpp | 90 + .../libKitsunemimiCommon/state_test.h | 44 + .../statemachine_test.cpp | 219 ++ .../libKitsunemimiCommon/statemachine_test.h | 49 + .../threading/thread_handler_test.cpp | 77 + .../threading/thread_handler_test.h | 45 + .../tests/unit_tests/main.cpp | 65 + .../tests/unit_tests/unit_tests.pro | 55 + .../libKitsunemimiConfig/CHANGELOG_old | 83 + src/libraries/libKitsunemimiConfig/README.md | 134 + src/libraries/libKitsunemimiConfig/build.sh | 106 + .../libKitsunemimiConfig/defaults.pri | 2 + .../libKitsunemimiConfig/config_handler.h | 196 + .../libKitsunemimiConfig.pro | 11 + .../src/config_handler.cpp | 906 +++++ .../libKitsunemimiConfig/src/src.pro | 26 + .../functional_tests/config_handler_test.cpp | 86 + .../functional_tests/config_handler_test.h | 46 + .../functional_tests/functional_tests.pro | 27 + .../tests/functional_tests/main.cpp | 30 + .../libKitsunemimiConfig/tests/tests.pro | 10 + .../tests/unit_tests/config_handler_test.cpp | 427 ++ .../tests/unit_tests/config_handler_test.h | 69 + .../tests/unit_tests/main.cpp | 30 + .../tests/unit_tests/unit_tests.pro | 27 + src/libraries/libKitsunemimiCpu/CHANGELOG_old | 41 + src/libraries/libKitsunemimiCpu/README.md | 111 + src/libraries/libKitsunemimiCpu/build.sh | 104 + src/libraries/libKitsunemimiCpu/defaults.pri | 2 + .../include/libKitsunemimiCpu/cpu.h | 66 + .../include/libKitsunemimiCpu/memory.h | 38 + .../include/libKitsunemimiCpu/rapl.h | 126 + .../libKitsunemimiCpu/libKitsunemimiCpu.pro | 11 + src/libraries/libKitsunemimiCpu/src/cpu.cpp | 828 ++++ .../libKitsunemimiCpu/src/memory.cpp | 59 + src/libraries/libKitsunemimiCpu/src/rapl.cpp | 270 ++ src/libraries/libKitsunemimiCpu/src/src.pro | 25 + .../tests/cli_tests/cli_tests.pro | 18 + .../tests/cli_tests/main.cpp | 151 + .../libKitsunemimiCpu/tests/tests.pro | 9 + .../libKitsunemimiCrypto/CHANGELOG_old | 23 + src/libraries/libKitsunemimiCrypto/README.md | 175 + src/libraries/libKitsunemimiCrypto/build.sh | 104 + .../libKitsunemimiCrypto/defaults.pri | 2 + .../include/libKitsunemimiCrypto/common.h | 55 + .../include/libKitsunemimiCrypto/hashes.h | 44 + .../include/libKitsunemimiCrypto/signing.h | 53 + .../symmetric_encryption.h | 53 + .../libKitsunemimiCrypto.pro | 11 + .../libKitsunemimiCrypto/src/common.cpp | 396 ++ .../libKitsunemimiCrypto/src/hashes.cpp | 87 + .../libKitsunemimiCrypto/src/signing.cpp | 110 + .../libKitsunemimiCrypto/src/src.pro | 29 + .../src/symmetric_encryption.cpp | 135 + .../libKitsunemimiCrypto/tests/tests.pro | 9 + .../tests/unit_tests/common_test.cpp | 127 + .../tests/unit_tests/common_test.h | 49 + .../tests/unit_tests/hashes_test.cpp | 50 + .../tests/unit_tests/hashes_test.h | 43 + .../tests/unit_tests/main.cpp | 40 + .../tests/unit_tests/signing_test.cpp | 63 + .../tests/unit_tests/signing_test.h | 43 + .../unit_tests/symmetric_encryption_test.cpp | 56 + .../unit_tests/symmetric_encryption_test.h | 43 + .../tests/unit_tests/unit_tests.pro | 30 + .../CHANGELOG_old | 2 + .../README.md | 65 + .../build.sh | 103 + .../defaults.pri | 2 + .../cluster_meta.h | 79 + .../libKitsunemimiHanamiClusterParser.pro | 13 + .../src/cluster_meta.cpp | 56 + .../cluster_parser_interface.cpp | 185 + .../cluster_parser_interface.h | 74 + .../src/grammar/cluster_lexer.l | 101 + .../src/grammar/cluster_parser.y | 248 ++ .../src/src.pro | 72 + .../cluster_parsestring_test.cpp | 70 + .../cluster_parsestring_test.h | 46 + .../tests/memory_leak_tests/main.cpp | 32 + .../memory_leak_tests/memory_leak_tests.pro | 21 + .../tests/tests.pro | 10 + .../unit_tests/cluster_parsestring_test.cpp | 181 + .../unit_tests/cluster_parsestring_test.h | 46 + .../tests/unit_tests/main.cpp | 32 + .../tests/unit_tests/unit_tests.pro | 22 + .../libKitsunemimiHanamiCommon/CHANGELOG_old | 18 + .../libKitsunemimiHanamiCommon/README.md | 72 + .../libKitsunemimiHanamiCommon/build.sh | 107 + .../libKitsunemimiHanamiCommon/defaults.pri | 2 + .../include/libKitsunemimiHanamiCommon/args.h | 64 + .../component_support.h | 60 + .../libKitsunemimiHanamiCommon/config.h | 43 + .../libKitsunemimiHanamiCommon/defines.h | 44 + .../libKitsunemimiHanamiCommon/enums.h | 128 + .../libKitsunemimiHanamiCommon/functions.h | 70 + .../libKitsunemimiHanamiCommon/generic_main.h | 102 + .../libKitsunemimiHanamiCommon/structs.h | 129 + .../include/libKitsunemimiHanamiCommon/uuid.h | 67 + .../libKitsunemimiHanamiCommon.pro | 11 + .../src/component_support.cpp | 53 + .../libKitsunemimiHanamiCommon/src/config.cpp | 71 + .../libKitsunemimiHanamiCommon/src/src.pro | 47 + .../tests/tests.pro | 9 + .../tests/unit_tests/main.cpp | 8 + .../tests/unit_tests/unit_tests.pro | 35 + .../CHANGELOG_old | 20 + .../libKitsunemimiHanamiDatabase/README.md | 70 + .../libKitsunemimiHanamiDatabase/build.sh | 117 + .../libKitsunemimiHanamiDatabase/defaults.pri | 2 + .../hanami_sql_admin_table.h | 57 + .../hanami_sql_log_table.h | 63 + .../hanami_sql_table.h | 84 + .../libKitsunemimiHanamiDatabase.pro | 11 + .../src/hanami_sql_admin_table.cpp | 68 + .../src/hanami_sql_log_table.cpp | 110 + .../src/hanami_sql_table.cpp | 217 ++ .../libKitsunemimiHanamiDatabase/src/src.pro | 47 + .../tests/tests.pro | 9 + .../tests/unit_tests/main.cpp | 6 + .../tests/unit_tests/unit_tests.pro | 18 + .../CHANGELOG_old | 8 + .../libKitsunemimiHanamiEndpoints/README.md | 76 + .../libKitsunemimiHanamiEndpoints/build.sh | 75 + .../defaults.pri | 2 + .../libKitsunemimiHanamiEndpoints/endpoint.h | 75 + .../libKitsunemimiHanamiEndpoints.pro | 11 + .../src/endpoint.cpp | 157 + .../endpoint_parser_interface.cpp | 186 + .../endpoint_parser_interface.h | 76 + .../src/grammar/endpoint_lexer.l | 95 + .../src/grammar/endpoint_parser.y | 194 + .../libKitsunemimiHanamiEndpoints/src/src.pro | 93 + .../tests/tests.pro | 9 + .../tests/unit_tests/endpoint_test.cpp | 113 + .../tests/unit_tests/endpoint_test.h | 49 + .../tests/unit_tests/main.cpp | 29 + .../tests/unit_tests/policy_test.cpp | 87 + .../tests/unit_tests/policy_test.h | 49 + .../tests/unit_tests/unit_tests.pro | 42 + .../libKitsunemimiHanamiMessages/README.md | 1 + .../message_sub_types.h | 34 + .../protobuffers/azuki_messages.proto3 | 29 + .../protobuffers/azuki_messages.proto3.pb.cc | 298 ++ .../protobuffers/azuki_messages.proto3.pb.h | 279 ++ .../protobuffers/kyouko_messages.proto3 | 39 + .../protobuffers/kyouko_messages.proto3.pb.cc | 475 +++ .../protobuffers/kyouko_messages.proto3.pb.h | 582 +++ .../protobuffers/shiori_messages.proto3 | 81 + .../protobuffers/shiori_messages.proto3.pb.cc | 2383 ++++++++++++ .../protobuffers/shiori_messages.proto3.pb.h | 3424 +++++++++++++++++ .../libKitsunemimiHanamiNetwork/CHANGELOG_old | 50 + .../libKitsunemimiHanamiNetwork/README.md | 76 + .../libKitsunemimiHanamiNetwork/build.sh | 125 + .../libKitsunemimiHanamiNetwork/defaults.pri | 2 + .../libKitsunemimiHanamiNetwork/blossom.h | 176 + .../hanami_messaging.h | 170 + .../hanami_messaging_client.h | 97 + .../libKitsunemimiHanamiNetwork.pro | 13 + .../src/blossom.cpp | 490 +++ .../src/callbacks.h | 177 + .../src/hanami_messaging.cpp | 828 ++++ .../src/hanami_messaging_client.cpp | 501 +++ .../src/items/item_methods.cpp | 187 + .../src/items/item_methods.h | 68 + .../src/items/sakura_items.cpp | 68 + .../src/items/sakura_items.h | 234 ++ .../src/items/value_item_map.cpp | 386 ++ .../src/items/value_item_map.h | 77 + .../src/items/value_items.h | 124 + .../message_handling/message_definitions.h | 67 + .../src/message_handling/messaging_event.cpp | 304 ++ .../src/message_handling/messaging_event.h | 82 + .../messaging_event_queue.cpp | 85 + .../message_handling/messaging_event_queue.h | 51 + .../src/message_handling/permission.cpp | 161 + .../src/message_handling/permission.h | 52 + .../src/runtime_validation.cpp | 240 ++ .../src/runtime_validation.h | 51 + .../libKitsunemimiHanamiNetwork/src/src.pro | 103 + .../functional_tests/functional_tests.pro | 75 + .../tests/functional_tests/main.cpp | 33 + .../tests/functional_tests/session_test.cpp | 210 + .../tests/functional_tests/session_test.h | 69 + .../tests/functional_tests/test_blossom.cpp | 59 + .../tests/functional_tests/test_blossom.h | 53 + .../tests/tests.pro | 9 + .../CHANGELOG_old | 7 + .../libKitsunemimiHanamiPolicies/README.md | 74 + .../libKitsunemimiHanamiPolicies/build.sh | 114 + .../libKitsunemimiHanamiPolicies/defaults.pri | 2 + .../libKitsunemimiHanamiPolicies/policy.h | 75 + .../libKitsunemimiHanamiPolicies.pro | 11 + .../src/grammar/policy_lexer.l | 98 + .../src/grammar/policy_parser.y | 212 + .../src/policy.cpp | 155 + .../policy_parser_interface.cpp | 187 + .../policy_parsing/policy_parser_interface.h | 75 + .../libKitsunemimiHanamiPolicies/src/src.pro | 93 + .../tests/tests.pro | 9 + .../tests/unit_tests/main.cpp | 30 + .../tests/unit_tests/policy_test.cpp | 90 + .../tests/unit_tests/policy_test.h | 49 + .../tests/unit_tests/unit_tests.pro | 42 + .../CHANGELOG_old | 2 + .../README.md | 69 + .../build.sh | 107 + .../defaults.pri | 2 + .../segment_meta.h | 93 + .../libKitsunemimiHanamiSegmentParser.pro | 13 + .../src/grammar/segment_lexer.l | 118 + .../src/grammar/segment_parser.y | 329 ++ .../src/segment_meta.cpp | 57 + .../segment_parser_interface.cpp | 186 + .../segment_parser_interface.h | 74 + .../src/src.pro | 77 + .../tests/memory_leak_tests/main.cpp | 32 + .../memory_leak_tests/memory_leak_tests.pro | 26 + .../segment_parsestring_test.cpp | 77 + .../segment_parsestring_test.h | 46 + .../tests/tests.pro | 10 + .../tests/unit_tests/main.cpp | 32 + .../unit_tests/segment_parsestring_test.cpp | 302 ++ .../unit_tests/segment_parsestring_test.h | 46 + .../tests/unit_tests/unit_tests.pro | 28 + src/libraries/libKitsunemimiIni/CHANGELOG_old | 138 + src/libraries/libKitsunemimiIni/README.md | 130 + src/libraries/libKitsunemimiIni/build.sh | 104 + src/libraries/libKitsunemimiIni/defaults.pri | 2 + .../include/libKitsunemimiIni/ini_item.h | 84 + .../libKitsunemimiIni/libKitsunemimiIni.pro | 11 + .../libKitsunemimiIni/src/grammar/ini_lexer.l | 105 + .../src/grammar/ini_parser.y | 256 ++ .../libKitsunemimiIni/src/ini_item.cpp | 347 ++ .../src/ini_parsing/ini_parser_interface.cpp | 181 + .../src/ini_parsing/ini_parser_interface.h | 65 + src/libraries/libKitsunemimiIni/src/src.pro | 72 + .../libKitsunemimiIni/tests/tests.pro | 9 + .../libKitsunemimiIni/ini_item_test.cpp | 235 ++ .../libKitsunemimiIni/ini_item_test.h | 36 + .../tests/unit_tests/main.cpp | 15 + .../tests/unit_tests/unit_tests.pro | 24 + .../libKitsunemimiJson/CHANGELOG_old | 191 + src/libraries/libKitsunemimiJson/README.md | 138 + src/libraries/libKitsunemimiJson/build.sh | 103 + src/libraries/libKitsunemimiJson/defaults.pri | 2 + .../include/libKitsunemimiJson/json_item.h | 105 + .../libKitsunemimiJson/libKitsunemimiJson.pro | 13 + .../src/grammar/json_lexer.l | 107 + .../src/grammar/json_parser.y | 302 ++ .../libKitsunemimiJson/src/json_item.cpp | 937 +++++ .../json_parsing/json_parser_interface.cpp | 200 + .../src/json_parsing/json_parser_interface.h | 59 + src/libraries/libKitsunemimiJson/src/src.pro | 72 + .../json_item_parseString_test.cpp | 79 + .../json_item_parseString_test.h | 28 + .../libKitsunemimiJson/json_item_test.cpp | 257 ++ .../libKitsunemimiJson/json_item_test.h | 42 + .../tests/memory_leak_tests/main.cpp | 17 + .../memory_leak_tests/memory_leak_tests.pro | 24 + .../libKitsunemimiJson/tests/tests.pro | 10 + .../json_item_parseString_test.cpp | 109 + .../json_item_parseString_test.h | 28 + .../libKitsunemimiJson/json_item_test.cpp | 450 +++ .../libKitsunemimiJson/json_item_test.h | 54 + .../tests/unit_tests/main.cpp | 17 + .../tests/unit_tests/unit_tests.pro | 24 + src/libraries/libKitsunemimiJwt/CHANGELOG_old | 38 + src/libraries/libKitsunemimiJwt/README.md | 75 + src/libraries/libKitsunemimiJwt/build.sh | 108 + src/libraries/libKitsunemimiJwt/defaults.pri | 2 + .../include/libKitsunemimiJwt/jwt.h | 76 + .../libKitsunemimiJwt/libKitsunemimiJwt.pro | 11 + src/libraries/libKitsunemimiJwt/src/jwt.cpp | 377 ++ src/libraries/libKitsunemimiJwt/src/src.pro | 33 + .../libKitsunemimiJwt/tests/tests.pro | 9 + .../tests/unit_tests/jwt_test.cpp | 87 + .../tests/unit_tests/jwt_test.h | 45 + .../tests/unit_tests/main.cpp | 30 + .../tests/unit_tests/unit_tests.pro | 35 + .../libKitsunemimiNetwork/CHANGELOG_old | 160 + src/libraries/libKitsunemimiNetwork/README.md | 283 ++ src/libraries/libKitsunemimiNetwork/build.sh | 103 + .../libKitsunemimiNetwork/defaults.pri | 2 + .../libKitsunemimiNetwork/abstract_server.h | 51 + .../libKitsunemimiNetwork/abstract_socket.h | 64 + .../libKitsunemimiNetwork/tcp/tcp_server.h | 64 + .../libKitsunemimiNetwork/tcp/tcp_socket.h | 89 + .../libKitsunemimiNetwork/template_server.h | 207 + .../libKitsunemimiNetwork/template_socket.h | 239 ++ .../tls_tcp/tls_tcp_server.h | 64 + .../tls_tcp/tls_tcp_socket.h | 80 + .../unix/unix_domain_server.h | 60 + .../unix/unix_domain_socket.h | 66 + .../libKitsunemimiNetwork.pro | 10 + .../src/abstract_server.cpp | 35 + .../src/abstract_socket.cpp | 47 + .../libKitsunemimiNetwork/src/src.pro | 39 + .../src/tcp/tcp_server.cpp | 110 + .../src/tcp/tcp_socket.cpp | 204 + .../src/tls_tcp/tls_tcp_server.cpp | 64 + .../src/tls_tcp/tls_tcp_socket.cpp | 265 ++ .../src/unix/unix_domain_server.cpp | 102 + .../src/unix/unix_domain_socket.cpp | 182 + .../tests/functional_tests/cert_init.h | 69 + .../functional_tests/example_certs/cert.pem | 21 + .../functional_tests/example_certs/key.pem | 28 + .../functional_tests/functional_tests.pro | 28 + .../libKitsunemimiNetwork/tcp/tcp_test.cpp | 173 + .../libKitsunemimiNetwork/tcp/tcp_test.h | 52 + .../tls_tcp/tls_tcp_test.cpp | 184 + .../tls_tcp/tls_tcp_test.h | 54 + .../unix/unix_domain_test.cpp | 174 + .../unix/unix_domain_test.h | 51 + .../tests/functional_tests/main.cpp | 22 + .../tests/memory_leak_tests/cert_init.h | 69 + .../libKitsunemimiNetwork/tcp/tcp_test.cpp | 111 + .../libKitsunemimiNetwork/tcp/tcp_test.h | 46 + .../unix/unix_domain_test.cpp | 111 + .../unix/unix_domain_test.h | 51 + .../tests/memory_leak_tests/main.cpp | 18 + .../memory_leak_tests/memory_leak_tests.pro | 26 + .../libKitsunemimiNetwork/tests/tests.pro | 10 + src/libraries/libKitsunemimiObj/CHANGELOG_old | 38 + src/libraries/libKitsunemimiObj/README.md | 136 + src/libraries/libKitsunemimiObj/build.sh | 104 + src/libraries/libKitsunemimiObj/defaults.pri | 2 + .../include/libKitsunemimiObj/obj_item.h | 54 + .../libKitsunemimiObj/libKitsunemimiObj.pro | 10 + .../libKitsunemimiObj/src/obj_creator.cpp | 107 + .../libKitsunemimiObj/src/obj_creator.h | 28 + .../libKitsunemimiObj/src/obj_item.cpp | 52 + .../libKitsunemimiObj/src/obj_parser.cpp | 296 ++ .../libKitsunemimiObj/src/obj_parser.h | 48 + src/libraries/libKitsunemimiObj/src/src.pro | 24 + .../functional_tests/functional_tests.pro | 21 + .../tests/functional_tests/main.cpp | 18 + .../tests/functional_tests/obj_item_test.cpp | 129 + .../tests/functional_tests/obj_item_test.h | 32 + .../libKitsunemimiObj/tests/tests.pro | 9 + .../libKitsunemimiOpencl/CHANGELOG_old | 51 + src/libraries/libKitsunemimiOpencl/README.md | 254 ++ src/libraries/libKitsunemimiOpencl/build.sh | 104 + .../libKitsunemimiOpencl/defaults.pri | 2 + .../include/libKitsunemimiOpencl/gpu_data.h | 100 + .../libKitsunemimiOpencl/gpu_handler.h | 57 + .../libKitsunemimiOpencl/gpu_interface.h | 102 + .../libKitsunemimiOpencl.pro | 12 + .../libKitsunemimiOpencl/src/gpu_data.cpp | 198 + .../libKitsunemimiOpencl/src/gpu_handler.cpp | 120 + .../src/gpu_interface.cpp | 620 +++ .../libKitsunemimiOpencl/src/src.pro | 27 + .../tests/benchmark_tests/benchmark_tests.pro | 26 + .../tests/benchmark_tests/main.cpp | 35 + .../tests/benchmark_tests/simple_test.cpp | 197 + .../tests/benchmark_tests/simple_test.h | 61 + .../functional_tests/functional_tests.pro | 25 + .../tests/functional_tests/main.cpp | 35 + .../tests/functional_tests/simple_test.cpp | 140 + .../tests/functional_tests/simple_test.h | 47 + .../libKitsunemimiOpencl/tests/tests.pro | 10 + .../CHANGELOG_old | 49 + .../libKitsunemimiSakuraDatabase/README.md | 72 + .../libKitsunemimiSakuraDatabase/build.sh | 108 + .../libKitsunemimiSakuraDatabase/defaults.pri | 2 + .../sql_database.h | 61 + .../libKitsunemimiSakuraDatabase/sql_table.h | 130 + .../libKitsunemimiSakuraDatabase.pro | 11 + .../src/sql_database.cpp | 130 + .../src/sql_table.cpp | 633 +++ .../libKitsunemimiSakuraDatabase/src/src.pro | 35 + .../functional_tests/functional_tests.pro | 36 + .../tests/functional_tests/main.cpp | 11 + .../tests/functional_tests/sql_table_test.cpp | 222 ++ .../tests/functional_tests/sql_table_test.h | 42 + .../tests/functional_tests/test_table.cpp | 120 + .../tests/functional_tests/test_table.h | 45 + .../tests/tests.pro | 9 + .../CHANGELOG_old | 12 + .../libKitsunemimiSakuraHardware/README.md | 69 + .../libKitsunemimiSakuraHardware/build.sh | 106 + .../libKitsunemimiSakuraHardware/defaults.pri | 2 + .../libKitsunemimiSakuraHardware/cpu_core.h | 61 + .../cpu_package.h | 62 + .../libKitsunemimiSakuraHardware/cpu_thread.h | 68 + .../libKitsunemimiSakuraHardware/host.h | 68 + .../libKitsunemimiSakuraHardware.pro | 11 + .../src/cpu_core.cpp | 212 + .../src/cpu_package.cpp | 180 + .../src/cpu_thread.cpp | 200 + .../libKitsunemimiSakuraHardware/src/host.cpp | 242 ++ .../libKitsunemimiSakuraHardware/src/src.pro | 32 + .../tests/cli_tests/cli_tests.pro | 28 + .../tests/cli_tests/main.cpp | 58 + .../tests/tests.pro | 9 + .../libKitsunemimiSakuraNetwork/CHANGELOG_old | 144 + .../libKitsunemimiSakuraNetwork/README.md | 83 + .../libKitsunemimiSakuraNetwork/build.sh | 106 + .../libKitsunemimiSakuraNetwork/defaults.pri | 2 + .../libKitsunemimiSakuraNetwork/session.h | 158 + .../session_controller.h | 94 + .../libKitsunemimiSakuraNetwork.pro | 13 + .../src/callbacks.h | 221 ++ .../src/handler/message_blocker_handler.cpp | 240 ++ .../src/handler/message_blocker_handler.h | 76 + .../src/handler/reply_handler.cpp | 258 ++ .../src/handler/reply_handler.h | 82 + .../src/handler/session_handler.cpp | 238 ++ .../src/handler/session_handler.h | 88 + .../src/message_definitions.h | 438 +++ .../messages_processing/error_processing.h | 188 + .../heartbeat_processing.h | 143 + .../multiblock_data_processing.h | 206 + .../messages_processing/session_processing.h | 296 ++ .../singleblock_data_processing.h | 195 + .../stream_data_processing.h | 214 ++ .../src/multiblock_io.cpp | 226 ++ .../src/multiblock_io.h | 87 + .../src/session.cpp | 588 +++ .../src/session_controller.cpp | 385 ++ .../libKitsunemimiSakuraNetwork/src/src.pro | 47 + .../functional_tests/functional_tests.pro | 28 + .../tests/functional_tests/main.cpp | 32 + .../tests/functional_tests/session_test.cpp | 299 ++ .../tests/functional_tests/session_test.h | 74 + .../tests/memory_leak_tests/main.cpp | 32 + .../memory_leak_tests/memory_leak_tests.pro | 28 + .../tests/memory_leak_tests/session_test.cpp | 299 ++ .../tests/memory_leak_tests/session_test.h | 70 + .../tests/tests.pro | 10 + .../libKitsunemimiSqlite/CHANGELOG_old | 19 + src/libraries/libKitsunemimiSqlite/README.md | 138 + src/libraries/libKitsunemimiSqlite/build.sh | 107 + .../libKitsunemimiSqlite/defaults.pri | 2 + .../include/libKitsunemimiSqlite/sqlite.h | 54 + .../libKitsunemimiSqlite.pro | 11 + .../libKitsunemimiSqlite/src/sqlite.cpp | 232 ++ .../libKitsunemimiSqlite/src/src.pro | 29 + .../libKitsunemimiSqlite/tests/tests.pro | 9 + .../tests/unit_tests/main.cpp | 9 + .../tests/unit_tests/sqlite_test.cpp | 199 + .../tests/unit_tests/sqlite_test.h | 38 + .../tests/unit_tests/unit_tests.pro | 30 + src/libraries/libMisakiGuard/CHANGELOG_old | 7 + src/libraries/libMisakiGuard/README.md | 2 + src/libraries/libMisakiGuard/build.sh | 128 + src/libraries/libMisakiGuard/defaults.pri | 2 + .../include/libMisakiGuard/misaki_input.h | 41 + .../libMisakiGuard/libMisakiGuard.pro | 11 + .../libMisakiGuard/src/generate_api_docu.cpp | 96 + .../libMisakiGuard/src/generate_api_docu.h | 46 + .../libMisakiGuard/src/md_docu_generation.cpp | 246 ++ .../libMisakiGuard/src/md_docu_generation.h | 31 + .../libMisakiGuard/src/misaki_input.cpp | 126 + .../src/rst_docu_generation.cpp | 248 ++ .../libMisakiGuard/src/rst_docu_generation.h | 31 + src/libraries/libMisakiGuard/src/src.pro | 75 + src/libraries/libShioriArchive/CHANGELOG_old | 18 + src/libraries/libShioriArchive/README.md | 2 + src/libraries/libShioriArchive/build.sh | 127 + src/libraries/libShioriArchive/defaults.pri | 2 + .../include/libShioriArchive/datasets.h | 51 + .../include/libShioriArchive/other.h | 61 + .../include/libShioriArchive/snapshots.h | 72 + .../libShioriArchive/libShioriArchive.pro | 11 + .../libShioriArchive/src/datasets.cpp | 158 + src/libraries/libShioriArchive/src/other.cpp | 230 ++ .../libShioriArchive/src/snapshots.cpp | 375 ++ src/libraries/libShioriArchive/src/src.pro | 91 + .../libShioriArchive/tests/tests.pro | 9 + .../tests/unit_tests/main.cpp | 26 + .../tests/unit_tests/unit_tests.pro | 76 + src/libraries/libraries.pro | 53 + src/sdk/CHANGELOG_old | 44 + src/sdk/README.md | 77 + src/sdk/cpp/libHanamiAiSdk/README.md | 1 + src/sdk/cpp/libHanamiAiSdk/build.sh | 122 + src/sdk/cpp/libHanamiAiSdk/defaults.pri | 2 + .../include/libHanamiAiSdk/cluster.h | 69 + .../libHanamiAiSdk/common/websocket_client.h | 74 + .../include/libHanamiAiSdk/data_set.h | 64 + .../include/libHanamiAiSdk/init.h | 39 + .../include/libHanamiAiSdk/io.h | 62 + .../include/libHanamiAiSdk/project.h | 49 + .../include/libHanamiAiSdk/request_result.h | 44 + .../include/libHanamiAiSdk/snapshot.h | 44 + .../include/libHanamiAiSdk/task.h | 54 + .../include/libHanamiAiSdk/template.h | 49 + .../include/libHanamiAiSdk/user.h | 70 + src/sdk/cpp/libHanamiAiSdk/libHanamiAiSdk.pro | 13 + src/sdk/cpp/libHanamiAiSdk/src/cluster.cpp | 324 ++ .../libHanamiAiSdk/src/common/http_client.cpp | 483 +++ .../libHanamiAiSdk/src/common/http_client.h | 120 + .../src/common/websocket_client.cpp | 250 ++ src/sdk/cpp/libHanamiAiSdk/src/data_set.cpp | 615 +++ src/sdk/cpp/libHanamiAiSdk/src/init.cpp | 58 + src/sdk/cpp/libHanamiAiSdk/src/io.cpp | 281 ++ src/sdk/cpp/libHanamiAiSdk/src/project.cpp | 152 + .../cpp/libHanamiAiSdk/src/request_result.cpp | 115 + src/sdk/cpp/libHanamiAiSdk/src/snapshot.cpp | 116 + src/sdk/cpp/libHanamiAiSdk/src/src.pro | 98 + src/sdk/cpp/libHanamiAiSdk/src/task.cpp | 183 + src/sdk/cpp/libHanamiAiSdk/src/template.cpp | 162 + src/sdk/cpp/libHanamiAiSdk/src/user.cpp | 321 ++ src/sdk/cpp/libHanamiAiSdk/tests/tests.pro | 7 + src/sdk/go/README.md | 1 + src/sdk/go/hanami_ai_sdk/cluster_commands.go | 34 + .../hanami_ai_sdk/documentation_commands.go | 30 + src/sdk/go/hanami_ai_sdk/go.mod | 5 + src/sdk/go/hanami_ai_sdk/http_request.go | 170 + src/sdk/go/hanami_ai_sdk/run_commands.go | 51 + .../go/hanami_ai_sdk/threading_commands.go | 32 + .../go/hanami_ai_sdk/train_data_commands.go | 51 + src/sdk/go/hanami_ai_sdk/user_commands.go | 53 + src/sdk/javascript/cluster.js | 51 + src/sdk/javascript/common.js | 67 + src/sdk/javascript/data_set.js | 319 ++ src/sdk/javascript/init.js | 72 + src/sdk/javascript/io.js | 0 src/sdk/javascript/logs.js | 31 + src/sdk/javascript/measurements.js | 33 + src/sdk/javascript/project.js | 34 + src/sdk/javascript/request_result.js | 33 + src/sdk/javascript/snapshot.js | 27 + src/sdk/javascript/system_info.js | 28 + src/sdk/javascript/task.js | 40 + src/sdk/javascript/template.js | 34 + src/sdk/javascript/user.js | 55 + 1174 files changed, 130881 insertions(+), 66 deletions(-) create mode 100644 src/components/AzukiHeart/AzukiHeart.pro create mode 100644 src/components/AzukiHeart/CHANGELOG_old create mode 100644 src/components/AzukiHeart/README.md create mode 100755 src/components/AzukiHeart/build.sh create mode 100644 src/components/AzukiHeart/src/api/blossom_initializing.h create mode 100644 src/components/AzukiHeart/src/api/v1/measurements/power_consumption.cpp create mode 100644 src/components/AzukiHeart/src/api/v1/measurements/power_consumption.h create mode 100644 src/components/AzukiHeart/src/api/v1/measurements/speed.cpp create mode 100644 src/components/AzukiHeart/src/api/v1/measurements/speed.h create mode 100644 src/components/AzukiHeart/src/api/v1/measurements/temperature_production.cpp create mode 100644 src/components/AzukiHeart/src/api/v1/measurements/temperature_production.h create mode 100644 src/components/AzukiHeart/src/api/v1/system_info/get_system_info.cpp create mode 100644 src/components/AzukiHeart/src/api/v1/system_info/get_system_info.h create mode 100644 src/components/AzukiHeart/src/api/v1/threading/get_thread_mapping.cpp create mode 100644 src/components/AzukiHeart/src/api/v1/threading/get_thread_mapping.h create mode 100644 src/components/AzukiHeart/src/args.h create mode 100644 src/components/AzukiHeart/src/azuki_root.cpp create mode 100644 src/components/AzukiHeart/src/azuki_root.h create mode 100644 src/components/AzukiHeart/src/callbacks.h create mode 100644 src/components/AzukiHeart/src/config.h create mode 100644 src/components/AzukiHeart/src/core/power_measuring.cpp create mode 100644 src/components/AzukiHeart/src/core/power_measuring.h create mode 100644 src/components/AzukiHeart/src/core/speed_measuring.cpp create mode 100644 src/components/AzukiHeart/src/core/speed_measuring.h create mode 100644 src/components/AzukiHeart/src/core/temperature_measuring.cpp create mode 100644 src/components/AzukiHeart/src/core/temperature_measuring.h create mode 100644 src/components/AzukiHeart/src/core/thread_binder.cpp create mode 100644 src/components/AzukiHeart/src/core/thread_binder.h create mode 100644 src/components/AzukiHeart/src/core/value_container.cpp create mode 100644 src/components/AzukiHeart/src/core/value_container.h create mode 100644 src/components/AzukiHeart/src/main.cpp create mode 100644 src/components/KyoukoMind/CHANGELOG_old create mode 100644 src/components/KyoukoMind/KyoukoMind.pro create mode 100644 src/components/KyoukoMind/README.md create mode 100755 src/components/KyoukoMind/build.sh create mode 100644 src/components/KyoukoMind/src/api/blossom_initializing.h create mode 100644 src/components/KyoukoMind/src/api/v1/cluster/create_cluster.cpp create mode 100644 src/components/KyoukoMind/src/api/v1/cluster/create_cluster.h create mode 100644 src/components/KyoukoMind/src/api/v1/cluster/delete_cluster.cpp create mode 100644 src/components/KyoukoMind/src/api/v1/cluster/delete_cluster.h create mode 100644 src/components/KyoukoMind/src/api/v1/cluster/list_cluster.cpp create mode 100644 src/components/KyoukoMind/src/api/v1/cluster/list_cluster.h create mode 100644 src/components/KyoukoMind/src/api/v1/cluster/load_cluster.cpp create mode 100644 src/components/KyoukoMind/src/api/v1/cluster/load_cluster.h create mode 100644 src/components/KyoukoMind/src/api/v1/cluster/save_cluster.cpp create mode 100644 src/components/KyoukoMind/src/api/v1/cluster/save_cluster.h create mode 100644 src/components/KyoukoMind/src/api/v1/cluster/set_cluster_mode.cpp create mode 100644 src/components/KyoukoMind/src/api/v1/cluster/set_cluster_mode.h create mode 100644 src/components/KyoukoMind/src/api/v1/cluster/show_cluster.cpp create mode 100644 src/components/KyoukoMind/src/api/v1/cluster/show_cluster.h create mode 100644 src/components/KyoukoMind/src/api/v1/task/create_task.cpp create mode 100644 src/components/KyoukoMind/src/api/v1/task/create_task.h create mode 100644 src/components/KyoukoMind/src/api/v1/task/delete_task.cpp create mode 100644 src/components/KyoukoMind/src/api/v1/task/delete_task.h create mode 100644 src/components/KyoukoMind/src/api/v1/task/list_task.cpp create mode 100644 src/components/KyoukoMind/src/api/v1/task/list_task.h create mode 100644 src/components/KyoukoMind/src/api/v1/task/show_task.cpp create mode 100644 src/components/KyoukoMind/src/api/v1/task/show_task.h create mode 100644 src/components/KyoukoMind/src/api/v1/template/delete_template.cpp create mode 100644 src/components/KyoukoMind/src/api/v1/template/delete_template.h create mode 100644 src/components/KyoukoMind/src/api/v1/template/list_templates.cpp create mode 100644 src/components/KyoukoMind/src/api/v1/template/list_templates.h create mode 100644 src/components/KyoukoMind/src/api/v1/template/show_template.cpp create mode 100644 src/components/KyoukoMind/src/api/v1/template/show_template.h create mode 100644 src/components/KyoukoMind/src/api/v1/template/upload_template.cpp create mode 100644 src/components/KyoukoMind/src/api/v1/template/upload_template.h create mode 100644 src/components/KyoukoMind/src/args.h create mode 100644 src/components/KyoukoMind/src/callbacks.cpp create mode 100644 src/components/KyoukoMind/src/callbacks.h create mode 100644 src/components/KyoukoMind/src/common.h create mode 100644 src/components/KyoukoMind/src/common/defines.h create mode 100644 src/components/KyoukoMind/src/common/enums.h create mode 100644 src/components/KyoukoMind/src/common/functions.h create mode 100644 src/components/KyoukoMind/src/common/includes.h create mode 100644 src/components/KyoukoMind/src/common/structs.h create mode 100644 src/components/KyoukoMind/src/common/typedefs.h create mode 100644 src/components/KyoukoMind/src/config.h create mode 100644 src/components/KyoukoMind/src/core/callbacks.h create mode 100644 src/components/KyoukoMind/src/core/cluster/cluster.cpp create mode 100644 src/components/KyoukoMind/src/core/cluster/cluster.h create mode 100644 src/components/KyoukoMind/src/core/cluster/cluster_handler.cpp create mode 100644 src/components/KyoukoMind/src/core/cluster/cluster_handler.h create mode 100644 src/components/KyoukoMind/src/core/cluster/cluster_init.cpp create mode 100644 src/components/KyoukoMind/src/core/cluster/cluster_init.h create mode 100644 src/components/KyoukoMind/src/core/cluster/statemachine_init.cpp create mode 100644 src/components/KyoukoMind/src/core/cluster/statemachine_init.h create mode 100644 src/components/KyoukoMind/src/core/cluster/states/cycle_finish_state.cpp create mode 100644 src/components/KyoukoMind/src/core/cluster/states/cycle_finish_state.h create mode 100644 src/components/KyoukoMind/src/core/cluster/states/graphs/table_interpolation_state.cpp create mode 100644 src/components/KyoukoMind/src/core/cluster/states/graphs/table_interpolation_state.h create mode 100644 src/components/KyoukoMind/src/core/cluster/states/graphs/table_learn_forward_state.cpp create mode 100644 src/components/KyoukoMind/src/core/cluster/states/graphs/table_learn_forward_state.h create mode 100644 src/components/KyoukoMind/src/core/cluster/states/images/image_identify_state.cpp create mode 100644 src/components/KyoukoMind/src/core/cluster/states/images/image_identify_state.h create mode 100644 src/components/KyoukoMind/src/core/cluster/states/images/image_learn_forward_state.cpp create mode 100644 src/components/KyoukoMind/src/core/cluster/states/images/image_learn_forward_state.h create mode 100644 src/components/KyoukoMind/src/core/cluster/states/snapshots/restore_cluster_state.cpp create mode 100644 src/components/KyoukoMind/src/core/cluster/states/snapshots/restore_cluster_state.h create mode 100644 src/components/KyoukoMind/src/core/cluster/states/snapshots/save_cluster_state.cpp create mode 100644 src/components/KyoukoMind/src/core/cluster/states/snapshots/save_cluster_state.h create mode 100644 src/components/KyoukoMind/src/core/cluster/states/tables/table_interpolation_state.cpp create mode 100644 src/components/KyoukoMind/src/core/cluster/states/tables/table_interpolation_state.h create mode 100644 src/components/KyoukoMind/src/core/cluster/states/tables/table_learn_forward_state.cpp create mode 100644 src/components/KyoukoMind/src/core/cluster/states/tables/table_learn_forward_state.h create mode 100644 src/components/KyoukoMind/src/core/cluster/states/task_handle_state.cpp create mode 100644 src/components/KyoukoMind/src/core/cluster/states/task_handle_state.h create mode 100644 src/components/KyoukoMind/src/core/cluster/task.h create mode 100644 src/components/KyoukoMind/src/core/processing/cpu_processing_unit.cpp create mode 100644 src/components/KyoukoMind/src/core/processing/cpu_processing_unit.h create mode 100644 src/components/KyoukoMind/src/core/processing/processing_unit_handler.cpp create mode 100644 src/components/KyoukoMind/src/core/processing/processing_unit_handler.h create mode 100644 src/components/KyoukoMind/src/core/processing/segment_queue.cpp create mode 100644 src/components/KyoukoMind/src/core/processing/segment_queue.h create mode 100644 src/components/KyoukoMind/src/core/routing_functions.h create mode 100644 src/components/KyoukoMind/src/core/segments/abstract_segment.cpp create mode 100644 src/components/KyoukoMind/src/core/segments/abstract_segment.h create mode 100644 src/components/KyoukoMind/src/core/segments/brick.h create mode 100644 src/components/KyoukoMind/src/core/segments/dynamic_segment/backpropagation.h create mode 100644 src/components/KyoukoMind/src/core/segments/dynamic_segment/dynamic_segment.cpp create mode 100644 src/components/KyoukoMind/src/core/segments/dynamic_segment/dynamic_segment.h create mode 100644 src/components/KyoukoMind/src/core/segments/dynamic_segment/gpu_kernel.cl create mode 100644 src/components/KyoukoMind/src/core/segments/dynamic_segment/objects.h create mode 100644 src/components/KyoukoMind/src/core/segments/dynamic_segment/processing.h create mode 100644 src/components/KyoukoMind/src/core/segments/dynamic_segment/reduction.h create mode 100644 src/components/KyoukoMind/src/core/segments/dynamic_segment/section_update.h create mode 100644 src/components/KyoukoMind/src/core/segments/input_segment/input_segment.cpp create mode 100644 src/components/KyoukoMind/src/core/segments/input_segment/input_segment.h create mode 100644 src/components/KyoukoMind/src/core/segments/input_segment/objects.h create mode 100644 src/components/KyoukoMind/src/core/segments/input_segment/processing.h create mode 100644 src/components/KyoukoMind/src/core/segments/output_segment/backpropagation.h create mode 100644 src/components/KyoukoMind/src/core/segments/output_segment/objects.h create mode 100644 src/components/KyoukoMind/src/core/segments/output_segment/output_segment.cpp create mode 100644 src/components/KyoukoMind/src/core/segments/output_segment/output_segment.h create mode 100644 src/components/KyoukoMind/src/core/segments/output_segment/processing.h create mode 100644 src/components/KyoukoMind/src/core/segments/segment_meta.h create mode 100644 src/components/KyoukoMind/src/core/struct_validation.cpp create mode 100644 src/components/KyoukoMind/src/core/struct_validation.h create mode 100644 src/components/KyoukoMind/src/database/cluster_table.cpp create mode 100644 src/components/KyoukoMind/src/database/cluster_table.h create mode 100644 src/components/KyoukoMind/src/database/template_table.cpp create mode 100644 src/components/KyoukoMind/src/database/template_table.h create mode 100644 src/components/KyoukoMind/src/io/protobuf_messages.cpp create mode 100644 src/components/KyoukoMind/src/io/protobuf_messages.h create mode 100644 src/components/KyoukoMind/src/kyouko_root.cpp create mode 100644 src/components/KyoukoMind/src/kyouko_root.h create mode 100644 src/components/KyoukoMind/src/main.cpp create mode 100644 src/components/MisakiGuard/CHANGELOG_old create mode 100644 src/components/MisakiGuard/MisakiGuard.pro create mode 100644 src/components/MisakiGuard/README.md create mode 100755 src/components/MisakiGuard/build.sh create mode 100644 src/components/MisakiGuard/src/api/blossom_initializing.h create mode 100644 src/components/MisakiGuard/src/api/v1/auth/create_internal_token.cpp create mode 100644 src/components/MisakiGuard/src/api/v1/auth/create_internal_token.h create mode 100644 src/components/MisakiGuard/src/api/v1/auth/create_token.cpp create mode 100644 src/components/MisakiGuard/src/api/v1/auth/create_token.h create mode 100644 src/components/MisakiGuard/src/api/v1/auth/list_user_projects.cpp create mode 100644 src/components/MisakiGuard/src/api/v1/auth/list_user_projects.h create mode 100644 src/components/MisakiGuard/src/api/v1/auth/renew_token.cpp create mode 100644 src/components/MisakiGuard/src/api/v1/auth/renew_token.h create mode 100644 src/components/MisakiGuard/src/api/v1/auth/validate_access.cpp create mode 100644 src/components/MisakiGuard/src/api/v1/auth/validate_access.h create mode 100644 src/components/MisakiGuard/src/api/v1/documentation/generate_rest_api_docu.cpp create mode 100644 src/components/MisakiGuard/src/api/v1/documentation/generate_rest_api_docu.h create mode 100644 src/components/MisakiGuard/src/api/v1/project/create_project.cpp create mode 100644 src/components/MisakiGuard/src/api/v1/project/create_project.h create mode 100644 src/components/MisakiGuard/src/api/v1/project/delete_project.cpp create mode 100644 src/components/MisakiGuard/src/api/v1/project/delete_project.h create mode 100644 src/components/MisakiGuard/src/api/v1/project/get_project.cpp create mode 100644 src/components/MisakiGuard/src/api/v1/project/get_project.h create mode 100644 src/components/MisakiGuard/src/api/v1/project/list_projects.cpp create mode 100644 src/components/MisakiGuard/src/api/v1/project/list_projects.h create mode 100644 src/components/MisakiGuard/src/api/v1/user/add_project_to_user.cpp create mode 100644 src/components/MisakiGuard/src/api/v1/user/add_project_to_user.h create mode 100644 src/components/MisakiGuard/src/api/v1/user/create_user.cpp create mode 100644 src/components/MisakiGuard/src/api/v1/user/create_user.h create mode 100644 src/components/MisakiGuard/src/api/v1/user/delete_user.cpp create mode 100644 src/components/MisakiGuard/src/api/v1/user/delete_user.h create mode 100644 src/components/MisakiGuard/src/api/v1/user/get_user.cpp create mode 100644 src/components/MisakiGuard/src/api/v1/user/get_user.h create mode 100644 src/components/MisakiGuard/src/api/v1/user/list_users.cpp create mode 100644 src/components/MisakiGuard/src/api/v1/user/list_users.h create mode 100644 src/components/MisakiGuard/src/api/v1/user/remove_project_from_user.cpp create mode 100644 src/components/MisakiGuard/src/api/v1/user/remove_project_from_user.h create mode 100644 src/components/MisakiGuard/src/args.h create mode 100644 src/components/MisakiGuard/src/callbacks.h create mode 100644 src/components/MisakiGuard/src/config.h create mode 100644 src/components/MisakiGuard/src/database/projects_table.cpp create mode 100644 src/components/MisakiGuard/src/database/projects_table.h create mode 100644 src/components/MisakiGuard/src/database/users_table.cpp create mode 100644 src/components/MisakiGuard/src/database/users_table.h create mode 100644 src/components/MisakiGuard/src/main.cpp create mode 100644 src/components/MisakiGuard/src/misaki_root.cpp create mode 100644 src/components/MisakiGuard/src/misaki_root.h create mode 100644 src/components/ShioriArchive/CHANGELOG_old create mode 100644 src/components/ShioriArchive/README.md create mode 100644 src/components/ShioriArchive/ShioriArchive.pro create mode 100755 src/components/ShioriArchive/build.sh create mode 100644 src/components/ShioriArchive/src/api/blossom_initializing.h create mode 100644 src/components/ShioriArchive/src/api/v1/cluster_snapshot/create_cluster_snapshot.cpp create mode 100644 src/components/ShioriArchive/src/api/v1/cluster_snapshot/create_cluster_snapshot.h create mode 100644 src/components/ShioriArchive/src/api/v1/cluster_snapshot/delete_cluster_snapshot.cpp create mode 100644 src/components/ShioriArchive/src/api/v1/cluster_snapshot/delete_cluster_snapshot.h create mode 100644 src/components/ShioriArchive/src/api/v1/cluster_snapshot/finish_cluster_snapshot.cpp create mode 100644 src/components/ShioriArchive/src/api/v1/cluster_snapshot/finish_cluster_snapshot.h create mode 100644 src/components/ShioriArchive/src/api/v1/cluster_snapshot/get_cluster_snapshot.cpp create mode 100644 src/components/ShioriArchive/src/api/v1/cluster_snapshot/get_cluster_snapshot.h create mode 100644 src/components/ShioriArchive/src/api/v1/cluster_snapshot/list_cluster_snapshot.cpp create mode 100644 src/components/ShioriArchive/src/api/v1/cluster_snapshot/list_cluster_snapshot.h create mode 100644 src/components/ShioriArchive/src/api/v1/data_files/check_data_set.cpp create mode 100644 src/components/ShioriArchive/src/api/v1/data_files/check_data_set.h create mode 100644 src/components/ShioriArchive/src/api/v1/data_files/csv/create_csv_data_set.cpp create mode 100644 src/components/ShioriArchive/src/api/v1/data_files/csv/create_csv_data_set.h create mode 100644 src/components/ShioriArchive/src/api/v1/data_files/csv/finalize_csv_data_set.cpp create mode 100644 src/components/ShioriArchive/src/api/v1/data_files/csv/finalize_csv_data_set.h create mode 100644 src/components/ShioriArchive/src/api/v1/data_files/delete_data_set.cpp create mode 100644 src/components/ShioriArchive/src/api/v1/data_files/delete_data_set.h create mode 100644 src/components/ShioriArchive/src/api/v1/data_files/get_data_set.cpp create mode 100644 src/components/ShioriArchive/src/api/v1/data_files/get_data_set.h create mode 100644 src/components/ShioriArchive/src/api/v1/data_files/get_progress_data_set.cpp create mode 100644 src/components/ShioriArchive/src/api/v1/data_files/get_progress_data_set.h create mode 100644 src/components/ShioriArchive/src/api/v1/data_files/list_data_set.cpp create mode 100644 src/components/ShioriArchive/src/api/v1/data_files/list_data_set.h create mode 100644 src/components/ShioriArchive/src/api/v1/data_files/mnist/create_mnist_data_set.cpp create mode 100644 src/components/ShioriArchive/src/api/v1/data_files/mnist/create_mnist_data_set.h create mode 100644 src/components/ShioriArchive/src/api/v1/data_files/mnist/finalize_mnist_data_set.cpp create mode 100644 src/components/ShioriArchive/src/api/v1/data_files/mnist/finalize_mnist_data_set.h create mode 100644 src/components/ShioriArchive/src/api/v1/logs/get_audit_log.cpp create mode 100644 src/components/ShioriArchive/src/api/v1/logs/get_audit_log.h create mode 100644 src/components/ShioriArchive/src/api/v1/logs/get_error_log.cpp create mode 100644 src/components/ShioriArchive/src/api/v1/logs/get_error_log.h create mode 100644 src/components/ShioriArchive/src/api/v1/request_results/delete_request_result.cpp create mode 100644 src/components/ShioriArchive/src/api/v1/request_results/delete_request_result.h create mode 100644 src/components/ShioriArchive/src/api/v1/request_results/get_request_result.cpp create mode 100644 src/components/ShioriArchive/src/api/v1/request_results/get_request_result.h create mode 100644 src/components/ShioriArchive/src/api/v1/request_results/list_request_result.cpp create mode 100644 src/components/ShioriArchive/src/api/v1/request_results/list_request_result.h create mode 100644 src/components/ShioriArchive/src/args.h create mode 100644 src/components/ShioriArchive/src/callbacks.h create mode 100644 src/components/ShioriArchive/src/config.h create mode 100644 src/components/ShioriArchive/src/core/data_set_files/data_set_file.cpp create mode 100644 src/components/ShioriArchive/src/core/data_set_files/data_set_file.h create mode 100644 src/components/ShioriArchive/src/core/data_set_files/image_data_set_file.cpp create mode 100644 src/components/ShioriArchive/src/core/data_set_files/image_data_set_file.h create mode 100644 src/components/ShioriArchive/src/core/data_set_files/table_data_set_file.cpp create mode 100644 src/components/ShioriArchive/src/core/data_set_files/table_data_set_file.h create mode 100644 src/components/ShioriArchive/src/core/temp_file_handler.cpp create mode 100644 src/components/ShioriArchive/src/core/temp_file_handler.h create mode 100644 src/components/ShioriArchive/src/database/audit_log_table.cpp create mode 100644 src/components/ShioriArchive/src/database/audit_log_table.h create mode 100644 src/components/ShioriArchive/src/database/cluster_snapshot_table.cpp create mode 100644 src/components/ShioriArchive/src/database/cluster_snapshot_table.h create mode 100644 src/components/ShioriArchive/src/database/data_set_table.cpp create mode 100644 src/components/ShioriArchive/src/database/data_set_table.h create mode 100644 src/components/ShioriArchive/src/database/error_log_table.cpp create mode 100644 src/components/ShioriArchive/src/database/error_log_table.h create mode 100644 src/components/ShioriArchive/src/database/request_result_table.cpp create mode 100644 src/components/ShioriArchive/src/database/request_result_table.h create mode 100644 src/components/ShioriArchive/src/main.cpp create mode 100644 src/components/ShioriArchive/src/shiori_root.cpp create mode 100644 src/components/ShioriArchive/src/shiori_root.h create mode 100644 src/components/ToriiGateway/CHANGELOG_old create mode 100644 src/components/ToriiGateway/README.md create mode 100644 src/components/ToriiGateway/ToriiGateway.pro create mode 100755 src/components/ToriiGateway/build.sh create mode 100644 src/components/ToriiGateway/src/args.h create mode 100644 src/components/ToriiGateway/src/callbacks.cpp create mode 100644 src/components/ToriiGateway/src/callbacks.h create mode 100644 src/components/ToriiGateway/src/config.h create mode 100644 src/components/ToriiGateway/src/core/http_processing/file_send.cpp create mode 100644 src/components/ToriiGateway/src/core/http_processing/file_send.h create mode 100644 src/components/ToriiGateway/src/core/http_processing/http_processing.cpp create mode 100644 src/components/ToriiGateway/src/core/http_processing/http_processing.h create mode 100644 src/components/ToriiGateway/src/core/http_processing/response_builds.h create mode 100644 src/components/ToriiGateway/src/core/http_processing/string_functions.h create mode 100644 src/components/ToriiGateway/src/core/http_server.cpp create mode 100644 src/components/ToriiGateway/src/core/http_server.h create mode 100644 src/components/ToriiGateway/src/core/http_websocket_thread.cpp create mode 100644 src/components/ToriiGateway/src/core/http_websocket_thread.h create mode 100644 src/components/ToriiGateway/src/main.cpp create mode 100644 src/components/ToriiGateway/src/torii_root.cpp create mode 100644 src/components/ToriiGateway/src/torii_root.h create mode 100644 src/components/TsugumiTester/CHANGELOG_old create mode 100644 src/components/TsugumiTester/README.md create mode 100644 src/components/TsugumiTester/TsugumiTester.pro create mode 100755 src/components/TsugumiTester/build.sh create mode 100644 src/components/TsugumiTester/src/args.h create mode 100644 src/components/TsugumiTester/src/common/test_step.cpp create mode 100644 src/components/TsugumiTester/src/common/test_step.h create mode 100644 src/components/TsugumiTester/src/common/test_thread.cpp create mode 100644 src/components/TsugumiTester/src/common/test_thread.h create mode 100644 src/components/TsugumiTester/src/config.h create mode 100644 src/components/TsugumiTester/src/main.cpp create mode 100644 src/components/TsugumiTester/src/rest_api_tests/kyouko/cluster/cluster_create_test.cpp create mode 100644 src/components/TsugumiTester/src/rest_api_tests/kyouko/cluster/cluster_create_test.h create mode 100644 src/components/TsugumiTester/src/rest_api_tests/kyouko/cluster/cluster_delete_test.cpp create mode 100644 src/components/TsugumiTester/src/rest_api_tests/kyouko/cluster/cluster_delete_test.h create mode 100644 src/components/TsugumiTester/src/rest_api_tests/kyouko/cluster/cluster_get_test.cpp create mode 100644 src/components/TsugumiTester/src/rest_api_tests/kyouko/cluster/cluster_get_test.h create mode 100644 src/components/TsugumiTester/src/rest_api_tests/kyouko/cluster/cluster_list_test.cpp create mode 100644 src/components/TsugumiTester/src/rest_api_tests/kyouko/cluster/cluster_list_test.h create mode 100644 src/components/TsugumiTester/src/rest_api_tests/kyouko/cluster/cluster_load_test.cpp create mode 100644 src/components/TsugumiTester/src/rest_api_tests/kyouko/cluster/cluster_load_test.h create mode 100644 src/components/TsugumiTester/src/rest_api_tests/kyouko/cluster/cluster_save_test.cpp create mode 100644 src/components/TsugumiTester/src/rest_api_tests/kyouko/cluster/cluster_save_test.h create mode 100644 src/components/TsugumiTester/src/rest_api_tests/kyouko/cluster/cluster_switch_to_direct_test.cpp create mode 100644 src/components/TsugumiTester/src/rest_api_tests/kyouko/cluster/cluster_switch_to_direct_test.h create mode 100644 src/components/TsugumiTester/src/rest_api_tests/kyouko/cluster/cluster_switch_to_task_test.cpp create mode 100644 src/components/TsugumiTester/src/rest_api_tests/kyouko/cluster/cluster_switch_to_task_test.h create mode 100644 src/components/TsugumiTester/src/rest_api_tests/kyouko/io/direct_io_test.cpp create mode 100644 src/components/TsugumiTester/src/rest_api_tests/kyouko/io/direct_io_test.h create mode 100644 src/components/TsugumiTester/src/rest_api_tests/kyouko/task/image_learn_task_test.cpp create mode 100644 src/components/TsugumiTester/src/rest_api_tests/kyouko/task/image_learn_task_test.h create mode 100644 src/components/TsugumiTester/src/rest_api_tests/kyouko/task/image_request_task_test.cpp create mode 100644 src/components/TsugumiTester/src/rest_api_tests/kyouko/task/image_request_task_test.h create mode 100644 src/components/TsugumiTester/src/rest_api_tests/kyouko/task/table_learn_task_test.cpp create mode 100644 src/components/TsugumiTester/src/rest_api_tests/kyouko/task/table_learn_task_test.h create mode 100644 src/components/TsugumiTester/src/rest_api_tests/kyouko/task/table_request_task_test.cpp create mode 100644 src/components/TsugumiTester/src/rest_api_tests/kyouko/task/table_request_task_test.h create mode 100644 src/components/TsugumiTester/src/rest_api_tests/kyouko/template/template_delete_test.cpp create mode 100644 src/components/TsugumiTester/src/rest_api_tests/kyouko/template/template_delete_test.h create mode 100644 src/components/TsugumiTester/src/rest_api_tests/kyouko/template/template_get_test.cpp create mode 100644 src/components/TsugumiTester/src/rest_api_tests/kyouko/template/template_get_test.h create mode 100644 src/components/TsugumiTester/src/rest_api_tests/kyouko/template/template_list_test.cpp create mode 100644 src/components/TsugumiTester/src/rest_api_tests/kyouko/template/template_list_test.h create mode 100644 src/components/TsugumiTester/src/rest_api_tests/kyouko/template/template_upload_test.cpp create mode 100644 src/components/TsugumiTester/src/rest_api_tests/kyouko/template/template_upload_test.h create mode 100644 src/components/TsugumiTester/src/rest_api_tests/misaki/project/project_create_test.cpp create mode 100644 src/components/TsugumiTester/src/rest_api_tests/misaki/project/project_create_test.h create mode 100644 src/components/TsugumiTester/src/rest_api_tests/misaki/project/project_delete_test.cpp create mode 100644 src/components/TsugumiTester/src/rest_api_tests/misaki/project/project_delete_test.h create mode 100644 src/components/TsugumiTester/src/rest_api_tests/misaki/project/project_get_test.cpp create mode 100644 src/components/TsugumiTester/src/rest_api_tests/misaki/project/project_get_test.h create mode 100644 src/components/TsugumiTester/src/rest_api_tests/misaki/project/project_list_test.cpp create mode 100644 src/components/TsugumiTester/src/rest_api_tests/misaki/project/project_list_test.h create mode 100644 src/components/TsugumiTester/src/rest_api_tests/misaki/user/user_create_test.cpp create mode 100644 src/components/TsugumiTester/src/rest_api_tests/misaki/user/user_create_test.h create mode 100644 src/components/TsugumiTester/src/rest_api_tests/misaki/user/user_delete_test.cpp create mode 100644 src/components/TsugumiTester/src/rest_api_tests/misaki/user/user_delete_test.h create mode 100644 src/components/TsugumiTester/src/rest_api_tests/misaki/user/user_get_test.cpp create mode 100644 src/components/TsugumiTester/src/rest_api_tests/misaki/user/user_get_test.h create mode 100644 src/components/TsugumiTester/src/rest_api_tests/misaki/user/user_list_test.cpp create mode 100644 src/components/TsugumiTester/src/rest_api_tests/misaki/user/user_list_test.h create mode 100644 src/components/TsugumiTester/src/rest_api_tests/rest_api_tests.cpp create mode 100644 src/components/TsugumiTester/src/rest_api_tests/rest_api_tests.h create mode 100644 src/components/TsugumiTester/src/rest_api_tests/shiori/datasets/dataset_check_test.cpp create mode 100644 src/components/TsugumiTester/src/rest_api_tests/shiori/datasets/dataset_check_test.h create mode 100644 src/components/TsugumiTester/src/rest_api_tests/shiori/datasets/dataset_create_csv_test.cpp create mode 100644 src/components/TsugumiTester/src/rest_api_tests/shiori/datasets/dataset_create_csv_test.h create mode 100644 src/components/TsugumiTester/src/rest_api_tests/shiori/datasets/dataset_create_mnist_test.cpp create mode 100644 src/components/TsugumiTester/src/rest_api_tests/shiori/datasets/dataset_create_mnist_test.h create mode 100644 src/components/TsugumiTester/src/rest_api_tests/shiori/datasets/dataset_delete_test.cpp create mode 100644 src/components/TsugumiTester/src/rest_api_tests/shiori/datasets/dataset_delete_test.h create mode 100644 src/components/TsugumiTester/src/rest_api_tests/shiori/datasets/dataset_get_test.cpp create mode 100644 src/components/TsugumiTester/src/rest_api_tests/shiori/datasets/dataset_get_test.h create mode 100644 src/components/TsugumiTester/src/rest_api_tests/shiori/datasets/dataset_list_test.cpp create mode 100644 src/components/TsugumiTester/src/rest_api_tests/shiori/datasets/dataset_list_test.h create mode 100644 src/components/TsugumiTester/src/rest_api_tests/shiori/request_results/request_result_delete_test.cpp create mode 100644 src/components/TsugumiTester/src/rest_api_tests/shiori/request_results/request_result_delete_test.h create mode 100644 src/components/TsugumiTester/src/rest_api_tests/shiori/request_results/request_result_get_test.cpp create mode 100644 src/components/TsugumiTester/src/rest_api_tests/shiori/request_results/request_result_get_test.h create mode 100644 src/components/TsugumiTester/src/rest_api_tests/shiori/request_results/request_result_list_test.cpp create mode 100644 src/components/TsugumiTester/src/rest_api_tests/shiori/request_results/request_result_list_test.h create mode 100644 src/components/TsugumiTester/src/rest_api_tests/shiori/snapshots/snapshot_delete_test.cpp create mode 100644 src/components/TsugumiTester/src/rest_api_tests/shiori/snapshots/snapshot_delete_test.h create mode 100644 src/components/TsugumiTester/src/rest_api_tests/shiori/snapshots/snapshot_get_test.cpp create mode 100644 src/components/TsugumiTester/src/rest_api_tests/shiori/snapshots/snapshot_get_test.h create mode 100644 src/components/TsugumiTester/src/rest_api_tests/shiori/snapshots/snapshot_list_test.cpp create mode 100644 src/components/TsugumiTester/src/rest_api_tests/shiori/snapshots/snapshot_list_test.h create mode 100644 src/components/components.pro create mode 100644 src/frontend/Hanami-AI-Dashboard-Dependencies/README.md create mode 100644 src/frontend/Hanami-AI-Dashboard-Dependencies/bootstrap_icons/check-lg.svg create mode 100644 src/frontend/Hanami-AI-Dashboard-Dependencies/bootstrap_icons/x-lg.svg create mode 100644 src/frontend/Hanami-AI-Dashboard-Dependencies/d3.v6.min.js create mode 100644 src/frontend/Hanami-AI-Dashboard-Dependencies/jquery.min.js create mode 100644 src/frontend/Hanami-AI-Dashboard-Dependencies/protobuf.min.js create mode 100644 src/frontend/Hanami-AI-Dashboard-Dependencies/protobuf.min.js.map create mode 100644 src/frontend/Hanami-AI-Dashboard/.gitmodules create mode 100644 src/frontend/Hanami-AI-Dashboard/CHANGELOG_old create mode 100644 src/frontend/Hanami-AI-Dashboard/README.md create mode 100644 src/frontend/Hanami-AI-Dashboard/src/Hanami-AI-Dashboard-Dependencies/README.md create mode 100644 src/frontend/Hanami-AI-Dashboard/src/Hanami-AI-Dashboard-Dependencies/bootstrap_icons/check-lg.svg create mode 100644 src/frontend/Hanami-AI-Dashboard/src/Hanami-AI-Dashboard-Dependencies/bootstrap_icons/x-lg.svg create mode 100644 src/frontend/Hanami-AI-Dashboard/src/Hanami-AI-Dashboard-Dependencies/d3.v6.min.js create mode 100644 src/frontend/Hanami-AI-Dashboard/src/Hanami-AI-Dashboard-Dependencies/jquery.min.js create mode 100644 src/frontend/Hanami-AI-Dashboard/src/Hanami-AI-Dashboard-Dependencies/protobuf.min.js create mode 100644 src/frontend/Hanami-AI-Dashboard/src/Hanami-AI-Dashboard-Dependencies/protobuf.min.js.map create mode 100644 src/frontend/Hanami-AI-Dashboard/src/favicon.ico create mode 100644 src/frontend/Hanami-AI-Dashboard/src/index.html create mode 100644 src/frontend/Hanami-AI-Dashboard/src/libHanamiAiSdk/.github/ISSUE_TEMPLATE/bug_issue_template.md create mode 100644 src/frontend/Hanami-AI-Dashboard/src/libHanamiAiSdk/.github/ISSUE_TEMPLATE/cleanup_issue_template.md create mode 100644 src/frontend/Hanami-AI-Dashboard/src/libHanamiAiSdk/.github/ISSUE_TEMPLATE/config.yml create mode 100644 src/frontend/Hanami-AI-Dashboard/src/libHanamiAiSdk/.github/ISSUE_TEMPLATE/feature_request_issue_template.md create mode 100644 src/frontend/Hanami-AI-Dashboard/src/libHanamiAiSdk/.github/ISSUE_TEMPLATE/tag_request_issue_template.md create mode 100644 src/frontend/Hanami-AI-Dashboard/src/libHanamiAiSdk/.github/PULL_REQUEST_TEMPLATE.md create mode 100644 src/frontend/Hanami-AI-Dashboard/src/libHanamiAiSdk/.github/workflows/build_test.yml create mode 100644 src/frontend/Hanami-AI-Dashboard/src/libHanamiAiSdk/.gitignore create mode 100644 src/frontend/Hanami-AI-Dashboard/src/libHanamiAiSdk/.gitlab/issue_templates/bug_issue_template.md create mode 100644 src/frontend/Hanami-AI-Dashboard/src/libHanamiAiSdk/.gitlab/issue_templates/cleanup_issue_template.md create mode 100644 src/frontend/Hanami-AI-Dashboard/src/libHanamiAiSdk/.gitlab/issue_templates/feature_request_issue_template.md create mode 100644 src/frontend/Hanami-AI-Dashboard/src/libHanamiAiSdk/.gitlab/issue_templates/tag_request_issue_template.md create mode 100644 src/frontend/Hanami-AI-Dashboard/src/libHanamiAiSdk/.gitlab/merge_request_templates/merge_request_template.md create mode 100644 src/frontend/Hanami-AI-Dashboard/src/libHanamiAiSdk/CHANGELOG create mode 100644 src/frontend/Hanami-AI-Dashboard/src/libHanamiAiSdk/LICENSE create mode 100644 src/frontend/Hanami-AI-Dashboard/src/libHanamiAiSdk/README.md create mode 100644 src/frontend/Hanami-AI-Dashboard/src/libHanamiAiSdk/cpp/README.md create mode 100755 src/frontend/Hanami-AI-Dashboard/src/libHanamiAiSdk/cpp/build.sh create mode 100644 src/frontend/Hanami-AI-Dashboard/src/libHanamiAiSdk/cpp/defaults.pri create mode 100644 src/frontend/Hanami-AI-Dashboard/src/libHanamiAiSdk/cpp/include/libHanamiAiSdk/cluster.h create mode 100644 src/frontend/Hanami-AI-Dashboard/src/libHanamiAiSdk/cpp/include/libHanamiAiSdk/common/websocket_client.h create mode 100644 src/frontend/Hanami-AI-Dashboard/src/libHanamiAiSdk/cpp/include/libHanamiAiSdk/data_set.h create mode 100644 src/frontend/Hanami-AI-Dashboard/src/libHanamiAiSdk/cpp/include/libHanamiAiSdk/init.h create mode 100644 src/frontend/Hanami-AI-Dashboard/src/libHanamiAiSdk/cpp/include/libHanamiAiSdk/io.h create mode 100644 src/frontend/Hanami-AI-Dashboard/src/libHanamiAiSdk/cpp/include/libHanamiAiSdk/project.h create mode 100644 src/frontend/Hanami-AI-Dashboard/src/libHanamiAiSdk/cpp/include/libHanamiAiSdk/request_result.h create mode 100644 src/frontend/Hanami-AI-Dashboard/src/libHanamiAiSdk/cpp/include/libHanamiAiSdk/snapshot.h create mode 100644 src/frontend/Hanami-AI-Dashboard/src/libHanamiAiSdk/cpp/include/libHanamiAiSdk/task.h create mode 100644 src/frontend/Hanami-AI-Dashboard/src/libHanamiAiSdk/cpp/include/libHanamiAiSdk/template.h create mode 100644 src/frontend/Hanami-AI-Dashboard/src/libHanamiAiSdk/cpp/include/libHanamiAiSdk/user.h create mode 100644 src/frontend/Hanami-AI-Dashboard/src/libHanamiAiSdk/cpp/libHanamiAiSdk.pro create mode 100644 src/frontend/Hanami-AI-Dashboard/src/libHanamiAiSdk/cpp/src/cluster.cpp create mode 100644 src/frontend/Hanami-AI-Dashboard/src/libHanamiAiSdk/cpp/src/common/http_client.cpp create mode 100644 src/frontend/Hanami-AI-Dashboard/src/libHanamiAiSdk/cpp/src/common/http_client.h create mode 100644 src/frontend/Hanami-AI-Dashboard/src/libHanamiAiSdk/cpp/src/common/websocket_client.cpp create mode 100644 src/frontend/Hanami-AI-Dashboard/src/libHanamiAiSdk/cpp/src/data_set.cpp create mode 100644 src/frontend/Hanami-AI-Dashboard/src/libHanamiAiSdk/cpp/src/init.cpp create mode 100644 src/frontend/Hanami-AI-Dashboard/src/libHanamiAiSdk/cpp/src/io.cpp create mode 100644 src/frontend/Hanami-AI-Dashboard/src/libHanamiAiSdk/cpp/src/project.cpp create mode 100644 src/frontend/Hanami-AI-Dashboard/src/libHanamiAiSdk/cpp/src/request_result.cpp create mode 100644 src/frontend/Hanami-AI-Dashboard/src/libHanamiAiSdk/cpp/src/snapshot.cpp create mode 100644 src/frontend/Hanami-AI-Dashboard/src/libHanamiAiSdk/cpp/src/src.pro create mode 100644 src/frontend/Hanami-AI-Dashboard/src/libHanamiAiSdk/cpp/src/task.cpp create mode 100644 src/frontend/Hanami-AI-Dashboard/src/libHanamiAiSdk/cpp/src/template.cpp create mode 100644 src/frontend/Hanami-AI-Dashboard/src/libHanamiAiSdk/cpp/src/user.cpp create mode 100644 src/frontend/Hanami-AI-Dashboard/src/libHanamiAiSdk/cpp/tests/tests.pro create mode 100644 src/frontend/Hanami-AI-Dashboard/src/libHanamiAiSdk/go/README.md create mode 100644 src/frontend/Hanami-AI-Dashboard/src/libHanamiAiSdk/go/kitsumi_ai_sdk/cluster_commands.go create mode 100644 src/frontend/Hanami-AI-Dashboard/src/libHanamiAiSdk/go/kitsumi_ai_sdk/documentation_commands.go create mode 100644 src/frontend/Hanami-AI-Dashboard/src/libHanamiAiSdk/go/kitsumi_ai_sdk/go.mod create mode 100644 src/frontend/Hanami-AI-Dashboard/src/libHanamiAiSdk/go/kitsumi_ai_sdk/http_request.go create mode 100644 src/frontend/Hanami-AI-Dashboard/src/libHanamiAiSdk/go/kitsumi_ai_sdk/run_commands.go create mode 100644 src/frontend/Hanami-AI-Dashboard/src/libHanamiAiSdk/go/kitsumi_ai_sdk/threading_commands.go create mode 100644 src/frontend/Hanami-AI-Dashboard/src/libHanamiAiSdk/go/kitsumi_ai_sdk/train_data_commands.go create mode 100644 src/frontend/Hanami-AI-Dashboard/src/libHanamiAiSdk/go/kitsumi_ai_sdk/user_commands.go create mode 100644 src/frontend/Hanami-AI-Dashboard/src/libHanamiAiSdk/javascript/cluster.js create mode 100644 src/frontend/Hanami-AI-Dashboard/src/libHanamiAiSdk/javascript/common.js create mode 100644 src/frontend/Hanami-AI-Dashboard/src/libHanamiAiSdk/javascript/data_set.js create mode 100644 src/frontend/Hanami-AI-Dashboard/src/libHanamiAiSdk/javascript/init.js create mode 100644 src/frontend/Hanami-AI-Dashboard/src/libHanamiAiSdk/javascript/io.js create mode 100644 src/frontend/Hanami-AI-Dashboard/src/libHanamiAiSdk/javascript/logs.js create mode 100644 src/frontend/Hanami-AI-Dashboard/src/libHanamiAiSdk/javascript/measurements.js create mode 100644 src/frontend/Hanami-AI-Dashboard/src/libHanamiAiSdk/javascript/project.js create mode 100644 src/frontend/Hanami-AI-Dashboard/src/libHanamiAiSdk/javascript/request_result.js create mode 100644 src/frontend/Hanami-AI-Dashboard/src/libHanamiAiSdk/javascript/snapshot.js create mode 100644 src/frontend/Hanami-AI-Dashboard/src/libHanamiAiSdk/javascript/system_info.js create mode 100644 src/frontend/Hanami-AI-Dashboard/src/libHanamiAiSdk/javascript/task.js create mode 100644 src/frontend/Hanami-AI-Dashboard/src/libHanamiAiSdk/javascript/template.js create mode 100644 src/frontend/Hanami-AI-Dashboard/src/libHanamiAiSdk/javascript/user.js create mode 100644 src/frontend/Hanami-AI-Dashboard/src/libKitsunemimiHanamiMessages/LICENSE create mode 100644 src/frontend/Hanami-AI-Dashboard/src/libKitsunemimiHanamiMessages/README.md create mode 100644 src/frontend/Hanami-AI-Dashboard/src/libKitsunemimiHanamiMessages/hanami_messages/kyouko_messages.h create mode 100644 src/frontend/Hanami-AI-Dashboard/src/libKitsunemimiHanamiMessages/hanami_messages/shiori_messages.h create mode 100644 src/frontend/Hanami-AI-Dashboard/src/libKitsunemimiHanamiMessages/protobuffers/kyouko_messages.proto3 create mode 100644 src/frontend/Hanami-AI-Dashboard/src/libKitsunemimiHanamiMessages/protobuffers/kyouko_messages.proto3.pb.cc create mode 100644 src/frontend/Hanami-AI-Dashboard/src/libKitsunemimiHanamiMessages/protobuffers/kyouko_messages.proto3.pb.h create mode 100644 src/frontend/Hanami-AI-Dashboard/src/libKitsunemimiHanamiMessages/protobuffers/shiori_messages.proto3 create mode 100644 src/frontend/Hanami-AI-Dashboard/src/libKitsunemimiHanamiMessages/protobuffers/shiori_messages.proto3.pb.cc create mode 100644 src/frontend/Hanami-AI-Dashboard/src/libKitsunemimiHanamiMessages/protobuffers/shiori_messages.proto3.pb.h create mode 100644 src/frontend/Hanami-AI-Dashboard/src/login.html create mode 100644 src/frontend/Hanami-AI-Dashboard/src/scripts/actions.js create mode 100644 src/frontend/Hanami-AI-Dashboard/src/scripts/common.js create mode 100644 src/frontend/Hanami-AI-Dashboard/src/scripts/diagrams.js create mode 100644 src/frontend/Hanami-AI-Dashboard/src/scripts/login.js create mode 100644 src/frontend/Hanami-AI-Dashboard/src/scripts/sidebar.js create mode 100644 src/frontend/Hanami-AI-Dashboard/src/scripts/tabbar.js create mode 100644 src/frontend/Hanami-AI-Dashboard/src/scripts/table_processing.js create mode 100644 src/frontend/Hanami-AI-Dashboard/src/sidebar.html create mode 100644 src/frontend/Hanami-AI-Dashboard/src/styles/base.css create mode 100644 src/frontend/Hanami-AI-Dashboard/src/styles/modal.css create mode 100644 src/frontend/Hanami-AI-Dashboard/src/styles/sidebar.css create mode 100644 src/frontend/Hanami-AI-Dashboard/src/styles/table.css create mode 100644 src/frontend/Hanami-AI-Dashboard/src/styles/tabs.css create mode 100644 src/frontend/Hanami-AI-Dashboard/src/subsites/azuki/power.html create mode 100644 src/frontend/Hanami-AI-Dashboard/src/subsites/azuki/speed.html create mode 100644 src/frontend/Hanami-AI-Dashboard/src/subsites/azuki/system_info.html create mode 100644 src/frontend/Hanami-AI-Dashboard/src/subsites/azuki/temperature.html create mode 100644 src/frontend/Hanami-AI-Dashboard/src/subsites/azuki/thread_mapping.html create mode 100644 src/frontend/Hanami-AI-Dashboard/src/subsites/kyouko/cluster.html create mode 100644 src/frontend/Hanami-AI-Dashboard/src/subsites/kyouko/segment_template.html create mode 100644 src/frontend/Hanami-AI-Dashboard/src/subsites/kyouko/task.html create mode 100644 src/frontend/Hanami-AI-Dashboard/src/subsites/misaki/project.html create mode 100644 src/frontend/Hanami-AI-Dashboard/src/subsites/misaki/user.html create mode 100644 src/frontend/Hanami-AI-Dashboard/src/subsites/shiori/audit_log.html create mode 100644 src/frontend/Hanami-AI-Dashboard/src/subsites/shiori/cluster_snapshot.html create mode 100644 src/frontend/Hanami-AI-Dashboard/src/subsites/shiori/dataset.html create mode 100644 src/frontend/Hanami-AI-Dashboard/src/subsites/shiori/graphical_result.html create mode 100644 src/frontend/Hanami-AI-Dashboard/src/subsites/shiori/request_result.html create mode 100644 src/libraries/libAzukiHeart/CHANGELOG_old create mode 100644 src/libraries/libAzukiHeart/README.md create mode 100755 src/libraries/libAzukiHeart/build.sh create mode 100644 src/libraries/libAzukiHeart/defaults.pri create mode 100644 src/libraries/libAzukiHeart/include/libAzukiHeart/azuki_input.h create mode 100644 src/libraries/libAzukiHeart/include/libAzukiHeart/azuki_send.h create mode 100644 src/libraries/libAzukiHeart/libAzukiHeart.pro create mode 100644 src/libraries/libAzukiHeart/src/azuki_input.cpp create mode 100644 src/libraries/libAzukiHeart/src/azuki_send.cpp create mode 100644 src/libraries/libAzukiHeart/src/bind_thread_to_core.cpp create mode 100644 src/libraries/libAzukiHeart/src/bind_thread_to_core.h create mode 100644 src/libraries/libAzukiHeart/src/get_thread_mapping.cpp create mode 100644 src/libraries/libAzukiHeart/src/get_thread_mapping.h create mode 100644 src/libraries/libAzukiHeart/src/src.pro create mode 100644 src/libraries/libKitsunemimiArgs/CHANGELOG_old create mode 100644 src/libraries/libKitsunemimiArgs/README.md create mode 100755 src/libraries/libKitsunemimiArgs/build.sh create mode 100644 src/libraries/libKitsunemimiArgs/defaults.pri create mode 100644 src/libraries/libKitsunemimiArgs/include/libKitsunemimiArgs/arg_parser.h create mode 100644 src/libraries/libKitsunemimiArgs/libKitsunemimiArgs.pro create mode 100644 src/libraries/libKitsunemimiArgs/src/arg_parser.cpp create mode 100644 src/libraries/libKitsunemimiArgs/src/src.pro create mode 100644 src/libraries/libKitsunemimiArgs/tests/cli_tests/cli_tests.pro create mode 100644 src/libraries/libKitsunemimiArgs/tests/cli_tests/main.cpp create mode 100644 src/libraries/libKitsunemimiArgs/tests/tests.pro create mode 100644 src/libraries/libKitsunemimiArgs/tests/unit_tests/arg_parser_test.cpp create mode 100644 src/libraries/libKitsunemimiArgs/tests/unit_tests/arg_parser_test.h create mode 100644 src/libraries/libKitsunemimiArgs/tests/unit_tests/main.cpp create mode 100644 src/libraries/libKitsunemimiArgs/tests/unit_tests/unit_tests.pro create mode 100644 src/libraries/libKitsunemimiCommon/CHANGELOG_old create mode 100644 src/libraries/libKitsunemimiCommon/Doxyfile create mode 100644 src/libraries/libKitsunemimiCommon/README.md create mode 100755 src/libraries/libKitsunemimiCommon/build.sh create mode 100644 src/libraries/libKitsunemimiCommon/defaults.pri create mode 100644 src/libraries/libKitsunemimiCommon/include/libKitsunemimiCommon/buffer/data_buffer.h create mode 100644 src/libraries/libKitsunemimiCommon/include/libKitsunemimiCommon/buffer/item_buffer.h create mode 100644 src/libraries/libKitsunemimiCommon/include/libKitsunemimiCommon/buffer/ring_buffer.h create mode 100644 src/libraries/libKitsunemimiCommon/include/libKitsunemimiCommon/buffer/stack_buffer.h create mode 100644 src/libraries/libKitsunemimiCommon/include/libKitsunemimiCommon/buffer/stack_buffer_reserve.h create mode 100644 src/libraries/libKitsunemimiCommon/include/libKitsunemimiCommon/files/binary_file.h create mode 100644 src/libraries/libKitsunemimiCommon/include/libKitsunemimiCommon/files/binary_file_direct.h create mode 100644 src/libraries/libKitsunemimiCommon/include/libKitsunemimiCommon/files/text_file.h create mode 100644 src/libraries/libKitsunemimiCommon/include/libKitsunemimiCommon/items/data_items.h create mode 100644 src/libraries/libKitsunemimiCommon/include/libKitsunemimiCommon/items/table_item.h create mode 100644 src/libraries/libKitsunemimiCommon/include/libKitsunemimiCommon/logger.h create mode 100644 src/libraries/libKitsunemimiCommon/include/libKitsunemimiCommon/memory_counter.h create mode 100644 src/libraries/libKitsunemimiCommon/include/libKitsunemimiCommon/methods/file_methods.h create mode 100644 src/libraries/libKitsunemimiCommon/include/libKitsunemimiCommon/methods/string_methods.h create mode 100644 src/libraries/libKitsunemimiCommon/include/libKitsunemimiCommon/methods/vector_methods.h create mode 100644 src/libraries/libKitsunemimiCommon/include/libKitsunemimiCommon/process_execution.h create mode 100644 src/libraries/libKitsunemimiCommon/include/libKitsunemimiCommon/progress_bar.h create mode 100644 src/libraries/libKitsunemimiCommon/include/libKitsunemimiCommon/statemachine.h create mode 100644 src/libraries/libKitsunemimiCommon/include/libKitsunemimiCommon/test_helper/compare_test_helper.h create mode 100644 src/libraries/libKitsunemimiCommon/include/libKitsunemimiCommon/test_helper/memory_leak_test_helper.h create mode 100644 src/libraries/libKitsunemimiCommon/include/libKitsunemimiCommon/test_helper/speed_test_helper.h create mode 100644 src/libraries/libKitsunemimiCommon/include/libKitsunemimiCommon/threading/barrier.h create mode 100644 src/libraries/libKitsunemimiCommon/include/libKitsunemimiCommon/threading/cleanup_thread.h create mode 100644 src/libraries/libKitsunemimiCommon/include/libKitsunemimiCommon/threading/event.h create mode 100644 src/libraries/libKitsunemimiCommon/include/libKitsunemimiCommon/threading/event_queue.h create mode 100644 src/libraries/libKitsunemimiCommon/include/libKitsunemimiCommon/threading/thread.h create mode 100644 src/libraries/libKitsunemimiCommon/include/libKitsunemimiCommon/threading/thread_handler.h create mode 100644 src/libraries/libKitsunemimiCommon/libKitsunemimiCommon.pro create mode 100644 src/libraries/libKitsunemimiCommon/src/buffer/item_buffer.cpp create mode 100644 src/libraries/libKitsunemimiCommon/src/buffer/stack_buffer_reserve.cpp create mode 100644 src/libraries/libKitsunemimiCommon/src/files/binary_file.cpp create mode 100644 src/libraries/libKitsunemimiCommon/src/files/binary_file_direct.cpp create mode 100644 src/libraries/libKitsunemimiCommon/src/files/text_file.cpp create mode 100644 src/libraries/libKitsunemimiCommon/src/items/data_items.cpp create mode 100644 src/libraries/libKitsunemimiCommon/src/items/table_item.cpp create mode 100644 src/libraries/libKitsunemimiCommon/src/logger.cpp create mode 100644 src/libraries/libKitsunemimiCommon/src/memory_counter.cpp create mode 100644 src/libraries/libKitsunemimiCommon/src/methods/file_methods.cpp create mode 100644 src/libraries/libKitsunemimiCommon/src/methods/string_methods.cpp create mode 100644 src/libraries/libKitsunemimiCommon/src/methods/vector_methods.cpp create mode 100644 src/libraries/libKitsunemimiCommon/src/process_execution.cpp create mode 100644 src/libraries/libKitsunemimiCommon/src/progress_bar.cpp create mode 100644 src/libraries/libKitsunemimiCommon/src/src.pro create mode 100644 src/libraries/libKitsunemimiCommon/src/state.h create mode 100644 src/libraries/libKitsunemimiCommon/src/statemachine.cpp create mode 100644 src/libraries/libKitsunemimiCommon/src/test_helper/compare_test_helper.cpp create mode 100644 src/libraries/libKitsunemimiCommon/src/test_helper/memory_leak_test_helper.cpp create mode 100644 src/libraries/libKitsunemimiCommon/src/test_helper/speed_test_helper.cpp create mode 100644 src/libraries/libKitsunemimiCommon/src/threading/barrier.cpp create mode 100644 src/libraries/libKitsunemimiCommon/src/threading/cleanup_thread.cpp create mode 100644 src/libraries/libKitsunemimiCommon/src/threading/event.cpp create mode 100644 src/libraries/libKitsunemimiCommon/src/threading/event_queue.cpp create mode 100644 src/libraries/libKitsunemimiCommon/src/threading/thread.cpp create mode 100644 src/libraries/libKitsunemimiCommon/src/threading/thread_handler.cpp create mode 100644 src/libraries/libKitsunemimiCommon/tests/memory_leak_tests/libKitsunemimiCommon/buffer/data_buffer_test.cpp create mode 100644 src/libraries/libKitsunemimiCommon/tests/memory_leak_tests/libKitsunemimiCommon/buffer/data_buffer_test.h create mode 100644 src/libraries/libKitsunemimiCommon/tests/memory_leak_tests/libKitsunemimiCommon/buffer/item_buffer_test.cpp create mode 100644 src/libraries/libKitsunemimiCommon/tests/memory_leak_tests/libKitsunemimiCommon/buffer/item_buffer_test.h create mode 100644 src/libraries/libKitsunemimiCommon/tests/memory_leak_tests/libKitsunemimiCommon/buffer/ring_buffer_test.cpp create mode 100644 src/libraries/libKitsunemimiCommon/tests/memory_leak_tests/libKitsunemimiCommon/buffer/ring_buffer_test.h create mode 100644 src/libraries/libKitsunemimiCommon/tests/memory_leak_tests/libKitsunemimiCommon/buffer/stack_buffer_reserve_test.cpp create mode 100644 src/libraries/libKitsunemimiCommon/tests/memory_leak_tests/libKitsunemimiCommon/buffer/stack_buffer_reserve_test.h create mode 100644 src/libraries/libKitsunemimiCommon/tests/memory_leak_tests/libKitsunemimiCommon/buffer/stack_buffer_test.cpp create mode 100644 src/libraries/libKitsunemimiCommon/tests/memory_leak_tests/libKitsunemimiCommon/buffer/stack_buffer_test.h create mode 100644 src/libraries/libKitsunemimiCommon/tests/memory_leak_tests/libKitsunemimiCommon/items/data_items_test.cpp create mode 100644 src/libraries/libKitsunemimiCommon/tests/memory_leak_tests/libKitsunemimiCommon/items/data_items_test.h create mode 100644 src/libraries/libKitsunemimiCommon/tests/memory_leak_tests/libKitsunemimiCommon/items/table_item_test.cpp create mode 100644 src/libraries/libKitsunemimiCommon/tests/memory_leak_tests/libKitsunemimiCommon/items/table_item_test.h create mode 100644 src/libraries/libKitsunemimiCommon/tests/memory_leak_tests/libKitsunemimiCommon/state_test.cpp create mode 100644 src/libraries/libKitsunemimiCommon/tests/memory_leak_tests/libKitsunemimiCommon/state_test.h create mode 100644 src/libraries/libKitsunemimiCommon/tests/memory_leak_tests/libKitsunemimiCommon/statemachine_test.cpp create mode 100644 src/libraries/libKitsunemimiCommon/tests/memory_leak_tests/libKitsunemimiCommon/statemachine_test.h create mode 100644 src/libraries/libKitsunemimiCommon/tests/memory_leak_tests/libKitsunemimiCommon/threading/bogus_event.cpp create mode 100644 src/libraries/libKitsunemimiCommon/tests/memory_leak_tests/libKitsunemimiCommon/threading/bogus_event.h create mode 100644 src/libraries/libKitsunemimiCommon/tests/memory_leak_tests/libKitsunemimiCommon/threading/bogus_thread.cpp create mode 100644 src/libraries/libKitsunemimiCommon/tests/memory_leak_tests/libKitsunemimiCommon/threading/bogus_thread.h create mode 100644 src/libraries/libKitsunemimiCommon/tests/memory_leak_tests/libKitsunemimiCommon/threading/thread_test.cpp create mode 100644 src/libraries/libKitsunemimiCommon/tests/memory_leak_tests/libKitsunemimiCommon/threading/thread_test.h create mode 100644 src/libraries/libKitsunemimiCommon/tests/memory_leak_tests/main.cpp create mode 100644 src/libraries/libKitsunemimiCommon/tests/memory_leak_tests/memory_leak_tests.pro create mode 100644 src/libraries/libKitsunemimiCommon/tests/tests.pro create mode 100644 src/libraries/libKitsunemimiCommon/tests/unit_tests/libKitsunemimiCommon/buffer/data_buffer_test.cpp create mode 100644 src/libraries/libKitsunemimiCommon/tests/unit_tests/libKitsunemimiCommon/buffer/data_buffer_test.h create mode 100644 src/libraries/libKitsunemimiCommon/tests/unit_tests/libKitsunemimiCommon/buffer/item_buffer_test.cpp create mode 100644 src/libraries/libKitsunemimiCommon/tests/unit_tests/libKitsunemimiCommon/buffer/item_buffer_test.h create mode 100644 src/libraries/libKitsunemimiCommon/tests/unit_tests/libKitsunemimiCommon/buffer/ring_buffer_test.cpp create mode 100644 src/libraries/libKitsunemimiCommon/tests/unit_tests/libKitsunemimiCommon/buffer/ring_buffer_test.h create mode 100644 src/libraries/libKitsunemimiCommon/tests/unit_tests/libKitsunemimiCommon/buffer/stack_buffer_reserve_test.cpp create mode 100644 src/libraries/libKitsunemimiCommon/tests/unit_tests/libKitsunemimiCommon/buffer/stack_buffer_reserve_test.h create mode 100644 src/libraries/libKitsunemimiCommon/tests/unit_tests/libKitsunemimiCommon/buffer/stack_buffer_test.cpp create mode 100644 src/libraries/libKitsunemimiCommon/tests/unit_tests/libKitsunemimiCommon/buffer/stack_buffer_test.h create mode 100644 src/libraries/libKitsunemimiCommon/tests/unit_tests/libKitsunemimiCommon/files/binary_file_with_directIO_test.cpp create mode 100644 src/libraries/libKitsunemimiCommon/tests/unit_tests/libKitsunemimiCommon/files/binary_file_with_directIO_test.h create mode 100644 src/libraries/libKitsunemimiCommon/tests/unit_tests/libKitsunemimiCommon/files/binary_file_without_directIO_test.cpp create mode 100644 src/libraries/libKitsunemimiCommon/tests/unit_tests/libKitsunemimiCommon/files/binary_file_without_directIO_test.h create mode 100644 src/libraries/libKitsunemimiCommon/tests/unit_tests/libKitsunemimiCommon/files/text_file_test.cpp create mode 100644 src/libraries/libKitsunemimiCommon/tests/unit_tests/libKitsunemimiCommon/files/text_file_test.h create mode 100644 src/libraries/libKitsunemimiCommon/tests/unit_tests/libKitsunemimiCommon/items/data_items_DataArray_test.cpp create mode 100644 src/libraries/libKitsunemimiCommon/tests/unit_tests/libKitsunemimiCommon/items/data_items_DataArray_test.h create mode 100644 src/libraries/libKitsunemimiCommon/tests/unit_tests/libKitsunemimiCommon/items/data_items_DataMap_test.cpp create mode 100644 src/libraries/libKitsunemimiCommon/tests/unit_tests/libKitsunemimiCommon/items/data_items_DataMap_test.h create mode 100644 src/libraries/libKitsunemimiCommon/tests/unit_tests/libKitsunemimiCommon/items/data_items_DataValue_test.cpp create mode 100644 src/libraries/libKitsunemimiCommon/tests/unit_tests/libKitsunemimiCommon/items/data_items_DataValue_test.h create mode 100644 src/libraries/libKitsunemimiCommon/tests/unit_tests/libKitsunemimiCommon/items/table_item_test.cpp create mode 100644 src/libraries/libKitsunemimiCommon/tests/unit_tests/libKitsunemimiCommon/items/table_item_test.h create mode 100644 src/libraries/libKitsunemimiCommon/tests/unit_tests/libKitsunemimiCommon/logger_test.cpp create mode 100644 src/libraries/libKitsunemimiCommon/tests/unit_tests/libKitsunemimiCommon/logger_test.h create mode 100644 src/libraries/libKitsunemimiCommon/tests/unit_tests/libKitsunemimiCommon/methods/file_methods_test.cpp create mode 100644 src/libraries/libKitsunemimiCommon/tests/unit_tests/libKitsunemimiCommon/methods/file_methods_test.h create mode 100644 src/libraries/libKitsunemimiCommon/tests/unit_tests/libKitsunemimiCommon/methods/string_methods_test.cpp create mode 100644 src/libraries/libKitsunemimiCommon/tests/unit_tests/libKitsunemimiCommon/methods/string_methods_test.h create mode 100644 src/libraries/libKitsunemimiCommon/tests/unit_tests/libKitsunemimiCommon/methods/vector_methods_test.cpp create mode 100644 src/libraries/libKitsunemimiCommon/tests/unit_tests/libKitsunemimiCommon/methods/vector_methods_test.h create mode 100644 src/libraries/libKitsunemimiCommon/tests/unit_tests/libKitsunemimiCommon/progress_bar_test.cpp create mode 100644 src/libraries/libKitsunemimiCommon/tests/unit_tests/libKitsunemimiCommon/progress_bar_test.h create mode 100644 src/libraries/libKitsunemimiCommon/tests/unit_tests/libKitsunemimiCommon/state_test.cpp create mode 100644 src/libraries/libKitsunemimiCommon/tests/unit_tests/libKitsunemimiCommon/state_test.h create mode 100644 src/libraries/libKitsunemimiCommon/tests/unit_tests/libKitsunemimiCommon/statemachine_test.cpp create mode 100644 src/libraries/libKitsunemimiCommon/tests/unit_tests/libKitsunemimiCommon/statemachine_test.h create mode 100644 src/libraries/libKitsunemimiCommon/tests/unit_tests/libKitsunemimiCommon/threading/thread_handler_test.cpp create mode 100644 src/libraries/libKitsunemimiCommon/tests/unit_tests/libKitsunemimiCommon/threading/thread_handler_test.h create mode 100644 src/libraries/libKitsunemimiCommon/tests/unit_tests/main.cpp create mode 100644 src/libraries/libKitsunemimiCommon/tests/unit_tests/unit_tests.pro create mode 100644 src/libraries/libKitsunemimiConfig/CHANGELOG_old create mode 100644 src/libraries/libKitsunemimiConfig/README.md create mode 100755 src/libraries/libKitsunemimiConfig/build.sh create mode 100644 src/libraries/libKitsunemimiConfig/defaults.pri create mode 100644 src/libraries/libKitsunemimiConfig/include/libKitsunemimiConfig/config_handler.h create mode 100644 src/libraries/libKitsunemimiConfig/libKitsunemimiConfig.pro create mode 100644 src/libraries/libKitsunemimiConfig/src/config_handler.cpp create mode 100644 src/libraries/libKitsunemimiConfig/src/src.pro create mode 100644 src/libraries/libKitsunemimiConfig/tests/functional_tests/config_handler_test.cpp create mode 100644 src/libraries/libKitsunemimiConfig/tests/functional_tests/config_handler_test.h create mode 100644 src/libraries/libKitsunemimiConfig/tests/functional_tests/functional_tests.pro create mode 100644 src/libraries/libKitsunemimiConfig/tests/functional_tests/main.cpp create mode 100644 src/libraries/libKitsunemimiConfig/tests/tests.pro create mode 100644 src/libraries/libKitsunemimiConfig/tests/unit_tests/config_handler_test.cpp create mode 100644 src/libraries/libKitsunemimiConfig/tests/unit_tests/config_handler_test.h create mode 100644 src/libraries/libKitsunemimiConfig/tests/unit_tests/main.cpp create mode 100644 src/libraries/libKitsunemimiConfig/tests/unit_tests/unit_tests.pro create mode 100644 src/libraries/libKitsunemimiCpu/CHANGELOG_old create mode 100644 src/libraries/libKitsunemimiCpu/README.md create mode 100755 src/libraries/libKitsunemimiCpu/build.sh create mode 100644 src/libraries/libKitsunemimiCpu/defaults.pri create mode 100644 src/libraries/libKitsunemimiCpu/include/libKitsunemimiCpu/cpu.h create mode 100644 src/libraries/libKitsunemimiCpu/include/libKitsunemimiCpu/memory.h create mode 100644 src/libraries/libKitsunemimiCpu/include/libKitsunemimiCpu/rapl.h create mode 100644 src/libraries/libKitsunemimiCpu/libKitsunemimiCpu.pro create mode 100644 src/libraries/libKitsunemimiCpu/src/cpu.cpp create mode 100644 src/libraries/libKitsunemimiCpu/src/memory.cpp create mode 100644 src/libraries/libKitsunemimiCpu/src/rapl.cpp create mode 100644 src/libraries/libKitsunemimiCpu/src/src.pro create mode 100644 src/libraries/libKitsunemimiCpu/tests/cli_tests/cli_tests.pro create mode 100644 src/libraries/libKitsunemimiCpu/tests/cli_tests/main.cpp create mode 100644 src/libraries/libKitsunemimiCpu/tests/tests.pro create mode 100644 src/libraries/libKitsunemimiCrypto/CHANGELOG_old create mode 100644 src/libraries/libKitsunemimiCrypto/README.md create mode 100755 src/libraries/libKitsunemimiCrypto/build.sh create mode 100644 src/libraries/libKitsunemimiCrypto/defaults.pri create mode 100644 src/libraries/libKitsunemimiCrypto/include/libKitsunemimiCrypto/common.h create mode 100644 src/libraries/libKitsunemimiCrypto/include/libKitsunemimiCrypto/hashes.h create mode 100644 src/libraries/libKitsunemimiCrypto/include/libKitsunemimiCrypto/signing.h create mode 100644 src/libraries/libKitsunemimiCrypto/include/libKitsunemimiCrypto/symmetric_encryption.h create mode 100644 src/libraries/libKitsunemimiCrypto/libKitsunemimiCrypto.pro create mode 100644 src/libraries/libKitsunemimiCrypto/src/common.cpp create mode 100644 src/libraries/libKitsunemimiCrypto/src/hashes.cpp create mode 100644 src/libraries/libKitsunemimiCrypto/src/signing.cpp create mode 100644 src/libraries/libKitsunemimiCrypto/src/src.pro create mode 100644 src/libraries/libKitsunemimiCrypto/src/symmetric_encryption.cpp create mode 100644 src/libraries/libKitsunemimiCrypto/tests/tests.pro create mode 100644 src/libraries/libKitsunemimiCrypto/tests/unit_tests/common_test.cpp create mode 100644 src/libraries/libKitsunemimiCrypto/tests/unit_tests/common_test.h create mode 100644 src/libraries/libKitsunemimiCrypto/tests/unit_tests/hashes_test.cpp create mode 100644 src/libraries/libKitsunemimiCrypto/tests/unit_tests/hashes_test.h create mode 100644 src/libraries/libKitsunemimiCrypto/tests/unit_tests/main.cpp create mode 100644 src/libraries/libKitsunemimiCrypto/tests/unit_tests/signing_test.cpp create mode 100644 src/libraries/libKitsunemimiCrypto/tests/unit_tests/signing_test.h create mode 100644 src/libraries/libKitsunemimiCrypto/tests/unit_tests/symmetric_encryption_test.cpp create mode 100644 src/libraries/libKitsunemimiCrypto/tests/unit_tests/symmetric_encryption_test.h create mode 100644 src/libraries/libKitsunemimiCrypto/tests/unit_tests/unit_tests.pro create mode 100644 src/libraries/libKitsunemimiHanamiClusterParser/CHANGELOG_old create mode 100644 src/libraries/libKitsunemimiHanamiClusterParser/README.md create mode 100755 src/libraries/libKitsunemimiHanamiClusterParser/build.sh create mode 100644 src/libraries/libKitsunemimiHanamiClusterParser/defaults.pri create mode 100644 src/libraries/libKitsunemimiHanamiClusterParser/include/libKitsunemimiHanamiClusterParser/cluster_meta.h create mode 100644 src/libraries/libKitsunemimiHanamiClusterParser/libKitsunemimiHanamiClusterParser.pro create mode 100644 src/libraries/libKitsunemimiHanamiClusterParser/src/cluster_meta.cpp create mode 100644 src/libraries/libKitsunemimiHanamiClusterParser/src/cluster_parsing/cluster_parser_interface.cpp create mode 100644 src/libraries/libKitsunemimiHanamiClusterParser/src/cluster_parsing/cluster_parser_interface.h create mode 100644 src/libraries/libKitsunemimiHanamiClusterParser/src/grammar/cluster_lexer.l create mode 100644 src/libraries/libKitsunemimiHanamiClusterParser/src/grammar/cluster_parser.y create mode 100644 src/libraries/libKitsunemimiHanamiClusterParser/src/src.pro create mode 100644 src/libraries/libKitsunemimiHanamiClusterParser/tests/memory_leak_tests/cluster_parsestring_test.cpp create mode 100644 src/libraries/libKitsunemimiHanamiClusterParser/tests/memory_leak_tests/cluster_parsestring_test.h create mode 100644 src/libraries/libKitsunemimiHanamiClusterParser/tests/memory_leak_tests/main.cpp create mode 100644 src/libraries/libKitsunemimiHanamiClusterParser/tests/memory_leak_tests/memory_leak_tests.pro create mode 100644 src/libraries/libKitsunemimiHanamiClusterParser/tests/tests.pro create mode 100644 src/libraries/libKitsunemimiHanamiClusterParser/tests/unit_tests/cluster_parsestring_test.cpp create mode 100644 src/libraries/libKitsunemimiHanamiClusterParser/tests/unit_tests/cluster_parsestring_test.h create mode 100644 src/libraries/libKitsunemimiHanamiClusterParser/tests/unit_tests/main.cpp create mode 100644 src/libraries/libKitsunemimiHanamiClusterParser/tests/unit_tests/unit_tests.pro create mode 100644 src/libraries/libKitsunemimiHanamiCommon/CHANGELOG_old create mode 100644 src/libraries/libKitsunemimiHanamiCommon/README.md create mode 100755 src/libraries/libKitsunemimiHanamiCommon/build.sh create mode 100644 src/libraries/libKitsunemimiHanamiCommon/defaults.pri create mode 100644 src/libraries/libKitsunemimiHanamiCommon/include/libKitsunemimiHanamiCommon/args.h create mode 100644 src/libraries/libKitsunemimiHanamiCommon/include/libKitsunemimiHanamiCommon/component_support.h create mode 100644 src/libraries/libKitsunemimiHanamiCommon/include/libKitsunemimiHanamiCommon/config.h create mode 100644 src/libraries/libKitsunemimiHanamiCommon/include/libKitsunemimiHanamiCommon/defines.h create mode 100644 src/libraries/libKitsunemimiHanamiCommon/include/libKitsunemimiHanamiCommon/enums.h create mode 100644 src/libraries/libKitsunemimiHanamiCommon/include/libKitsunemimiHanamiCommon/functions.h create mode 100644 src/libraries/libKitsunemimiHanamiCommon/include/libKitsunemimiHanamiCommon/generic_main.h create mode 100644 src/libraries/libKitsunemimiHanamiCommon/include/libKitsunemimiHanamiCommon/structs.h create mode 100644 src/libraries/libKitsunemimiHanamiCommon/include/libKitsunemimiHanamiCommon/uuid.h create mode 100644 src/libraries/libKitsunemimiHanamiCommon/libKitsunemimiHanamiCommon.pro create mode 100644 src/libraries/libKitsunemimiHanamiCommon/src/component_support.cpp create mode 100644 src/libraries/libKitsunemimiHanamiCommon/src/config.cpp create mode 100644 src/libraries/libKitsunemimiHanamiCommon/src/src.pro create mode 100644 src/libraries/libKitsunemimiHanamiCommon/tests/tests.pro create mode 100644 src/libraries/libKitsunemimiHanamiCommon/tests/unit_tests/main.cpp create mode 100644 src/libraries/libKitsunemimiHanamiCommon/tests/unit_tests/unit_tests.pro create mode 100644 src/libraries/libKitsunemimiHanamiDatabase/CHANGELOG_old create mode 100644 src/libraries/libKitsunemimiHanamiDatabase/README.md create mode 100755 src/libraries/libKitsunemimiHanamiDatabase/build.sh create mode 100644 src/libraries/libKitsunemimiHanamiDatabase/defaults.pri create mode 100644 src/libraries/libKitsunemimiHanamiDatabase/include/libKitsunemimiHanamiDatabase/hanami_sql_admin_table.h create mode 100644 src/libraries/libKitsunemimiHanamiDatabase/include/libKitsunemimiHanamiDatabase/hanami_sql_log_table.h create mode 100644 src/libraries/libKitsunemimiHanamiDatabase/include/libKitsunemimiHanamiDatabase/hanami_sql_table.h create mode 100644 src/libraries/libKitsunemimiHanamiDatabase/libKitsunemimiHanamiDatabase.pro create mode 100644 src/libraries/libKitsunemimiHanamiDatabase/src/hanami_sql_admin_table.cpp create mode 100644 src/libraries/libKitsunemimiHanamiDatabase/src/hanami_sql_log_table.cpp create mode 100644 src/libraries/libKitsunemimiHanamiDatabase/src/hanami_sql_table.cpp create mode 100644 src/libraries/libKitsunemimiHanamiDatabase/src/src.pro create mode 100644 src/libraries/libKitsunemimiHanamiDatabase/tests/tests.pro create mode 100644 src/libraries/libKitsunemimiHanamiDatabase/tests/unit_tests/main.cpp create mode 100644 src/libraries/libKitsunemimiHanamiDatabase/tests/unit_tests/unit_tests.pro create mode 100644 src/libraries/libKitsunemimiHanamiEndpoints/CHANGELOG_old create mode 100644 src/libraries/libKitsunemimiHanamiEndpoints/README.md create mode 100755 src/libraries/libKitsunemimiHanamiEndpoints/build.sh create mode 100644 src/libraries/libKitsunemimiHanamiEndpoints/defaults.pri create mode 100644 src/libraries/libKitsunemimiHanamiEndpoints/include/libKitsunemimiHanamiEndpoints/endpoint.h create mode 100644 src/libraries/libKitsunemimiHanamiEndpoints/libKitsunemimiHanamiEndpoints.pro create mode 100644 src/libraries/libKitsunemimiHanamiEndpoints/src/endpoint.cpp create mode 100644 src/libraries/libKitsunemimiHanamiEndpoints/src/endpoint_parsing/endpoint_parser_interface.cpp create mode 100644 src/libraries/libKitsunemimiHanamiEndpoints/src/endpoint_parsing/endpoint_parser_interface.h create mode 100644 src/libraries/libKitsunemimiHanamiEndpoints/src/grammar/endpoint_lexer.l create mode 100644 src/libraries/libKitsunemimiHanamiEndpoints/src/grammar/endpoint_parser.y create mode 100644 src/libraries/libKitsunemimiHanamiEndpoints/src/src.pro create mode 100644 src/libraries/libKitsunemimiHanamiEndpoints/tests/tests.pro create mode 100644 src/libraries/libKitsunemimiHanamiEndpoints/tests/unit_tests/endpoint_test.cpp create mode 100644 src/libraries/libKitsunemimiHanamiEndpoints/tests/unit_tests/endpoint_test.h create mode 100644 src/libraries/libKitsunemimiHanamiEndpoints/tests/unit_tests/main.cpp create mode 100644 src/libraries/libKitsunemimiHanamiEndpoints/tests/unit_tests/policy_test.cpp create mode 100644 src/libraries/libKitsunemimiHanamiEndpoints/tests/unit_tests/policy_test.h create mode 100644 src/libraries/libKitsunemimiHanamiEndpoints/tests/unit_tests/unit_tests.pro create mode 100644 src/libraries/libKitsunemimiHanamiMessages/README.md create mode 100644 src/libraries/libKitsunemimiHanamiMessages/message_sub_types.h create mode 100644 src/libraries/libKitsunemimiHanamiMessages/protobuffers/azuki_messages.proto3 create mode 100644 src/libraries/libKitsunemimiHanamiMessages/protobuffers/azuki_messages.proto3.pb.cc create mode 100644 src/libraries/libKitsunemimiHanamiMessages/protobuffers/azuki_messages.proto3.pb.h create mode 100644 src/libraries/libKitsunemimiHanamiMessages/protobuffers/kyouko_messages.proto3 create mode 100644 src/libraries/libKitsunemimiHanamiMessages/protobuffers/kyouko_messages.proto3.pb.cc create mode 100644 src/libraries/libKitsunemimiHanamiMessages/protobuffers/kyouko_messages.proto3.pb.h create mode 100644 src/libraries/libKitsunemimiHanamiMessages/protobuffers/shiori_messages.proto3 create mode 100644 src/libraries/libKitsunemimiHanamiMessages/protobuffers/shiori_messages.proto3.pb.cc create mode 100644 src/libraries/libKitsunemimiHanamiMessages/protobuffers/shiori_messages.proto3.pb.h create mode 100644 src/libraries/libKitsunemimiHanamiNetwork/CHANGELOG_old create mode 100644 src/libraries/libKitsunemimiHanamiNetwork/README.md create mode 100755 src/libraries/libKitsunemimiHanamiNetwork/build.sh create mode 100644 src/libraries/libKitsunemimiHanamiNetwork/defaults.pri create mode 100644 src/libraries/libKitsunemimiHanamiNetwork/include/libKitsunemimiHanamiNetwork/blossom.h create mode 100644 src/libraries/libKitsunemimiHanamiNetwork/include/libKitsunemimiHanamiNetwork/hanami_messaging.h create mode 100644 src/libraries/libKitsunemimiHanamiNetwork/include/libKitsunemimiHanamiNetwork/hanami_messaging_client.h create mode 100644 src/libraries/libKitsunemimiHanamiNetwork/libKitsunemimiHanamiNetwork.pro create mode 100644 src/libraries/libKitsunemimiHanamiNetwork/src/blossom.cpp create mode 100644 src/libraries/libKitsunemimiHanamiNetwork/src/callbacks.h create mode 100644 src/libraries/libKitsunemimiHanamiNetwork/src/hanami_messaging.cpp create mode 100644 src/libraries/libKitsunemimiHanamiNetwork/src/hanami_messaging_client.cpp create mode 100644 src/libraries/libKitsunemimiHanamiNetwork/src/items/item_methods.cpp create mode 100644 src/libraries/libKitsunemimiHanamiNetwork/src/items/item_methods.h create mode 100644 src/libraries/libKitsunemimiHanamiNetwork/src/items/sakura_items.cpp create mode 100644 src/libraries/libKitsunemimiHanamiNetwork/src/items/sakura_items.h create mode 100644 src/libraries/libKitsunemimiHanamiNetwork/src/items/value_item_map.cpp create mode 100644 src/libraries/libKitsunemimiHanamiNetwork/src/items/value_item_map.h create mode 100644 src/libraries/libKitsunemimiHanamiNetwork/src/items/value_items.h create mode 100644 src/libraries/libKitsunemimiHanamiNetwork/src/message_handling/message_definitions.h create mode 100644 src/libraries/libKitsunemimiHanamiNetwork/src/message_handling/messaging_event.cpp create mode 100644 src/libraries/libKitsunemimiHanamiNetwork/src/message_handling/messaging_event.h create mode 100644 src/libraries/libKitsunemimiHanamiNetwork/src/message_handling/messaging_event_queue.cpp create mode 100644 src/libraries/libKitsunemimiHanamiNetwork/src/message_handling/messaging_event_queue.h create mode 100644 src/libraries/libKitsunemimiHanamiNetwork/src/message_handling/permission.cpp create mode 100644 src/libraries/libKitsunemimiHanamiNetwork/src/message_handling/permission.h create mode 100644 src/libraries/libKitsunemimiHanamiNetwork/src/runtime_validation.cpp create mode 100644 src/libraries/libKitsunemimiHanamiNetwork/src/runtime_validation.h create mode 100644 src/libraries/libKitsunemimiHanamiNetwork/src/src.pro create mode 100644 src/libraries/libKitsunemimiHanamiNetwork/tests/functional_tests/functional_tests.pro create mode 100644 src/libraries/libKitsunemimiHanamiNetwork/tests/functional_tests/main.cpp create mode 100644 src/libraries/libKitsunemimiHanamiNetwork/tests/functional_tests/session_test.cpp create mode 100644 src/libraries/libKitsunemimiHanamiNetwork/tests/functional_tests/session_test.h create mode 100644 src/libraries/libKitsunemimiHanamiNetwork/tests/functional_tests/test_blossom.cpp create mode 100644 src/libraries/libKitsunemimiHanamiNetwork/tests/functional_tests/test_blossom.h create mode 100644 src/libraries/libKitsunemimiHanamiNetwork/tests/tests.pro create mode 100644 src/libraries/libKitsunemimiHanamiPolicies/CHANGELOG_old create mode 100644 src/libraries/libKitsunemimiHanamiPolicies/README.md create mode 100755 src/libraries/libKitsunemimiHanamiPolicies/build.sh create mode 100644 src/libraries/libKitsunemimiHanamiPolicies/defaults.pri create mode 100644 src/libraries/libKitsunemimiHanamiPolicies/include/libKitsunemimiHanamiPolicies/policy.h create mode 100644 src/libraries/libKitsunemimiHanamiPolicies/libKitsunemimiHanamiPolicies.pro create mode 100644 src/libraries/libKitsunemimiHanamiPolicies/src/grammar/policy_lexer.l create mode 100644 src/libraries/libKitsunemimiHanamiPolicies/src/grammar/policy_parser.y create mode 100644 src/libraries/libKitsunemimiHanamiPolicies/src/policy.cpp create mode 100644 src/libraries/libKitsunemimiHanamiPolicies/src/policy_parsing/policy_parser_interface.cpp create mode 100644 src/libraries/libKitsunemimiHanamiPolicies/src/policy_parsing/policy_parser_interface.h create mode 100644 src/libraries/libKitsunemimiHanamiPolicies/src/src.pro create mode 100644 src/libraries/libKitsunemimiHanamiPolicies/tests/tests.pro create mode 100644 src/libraries/libKitsunemimiHanamiPolicies/tests/unit_tests/main.cpp create mode 100644 src/libraries/libKitsunemimiHanamiPolicies/tests/unit_tests/policy_test.cpp create mode 100644 src/libraries/libKitsunemimiHanamiPolicies/tests/unit_tests/policy_test.h create mode 100644 src/libraries/libKitsunemimiHanamiPolicies/tests/unit_tests/unit_tests.pro create mode 100644 src/libraries/libKitsunemimiHanamiSegmentParser/CHANGELOG_old create mode 100644 src/libraries/libKitsunemimiHanamiSegmentParser/README.md create mode 100755 src/libraries/libKitsunemimiHanamiSegmentParser/build.sh create mode 100644 src/libraries/libKitsunemimiHanamiSegmentParser/defaults.pri create mode 100644 src/libraries/libKitsunemimiHanamiSegmentParser/include/libKitsunemimiHanamiSegmentParser/segment_meta.h create mode 100644 src/libraries/libKitsunemimiHanamiSegmentParser/libKitsunemimiHanamiSegmentParser.pro create mode 100644 src/libraries/libKitsunemimiHanamiSegmentParser/src/grammar/segment_lexer.l create mode 100644 src/libraries/libKitsunemimiHanamiSegmentParser/src/grammar/segment_parser.y create mode 100644 src/libraries/libKitsunemimiHanamiSegmentParser/src/segment_meta.cpp create mode 100644 src/libraries/libKitsunemimiHanamiSegmentParser/src/segment_parsing/segment_parser_interface.cpp create mode 100644 src/libraries/libKitsunemimiHanamiSegmentParser/src/segment_parsing/segment_parser_interface.h create mode 100644 src/libraries/libKitsunemimiHanamiSegmentParser/src/src.pro create mode 100644 src/libraries/libKitsunemimiHanamiSegmentParser/tests/memory_leak_tests/main.cpp create mode 100644 src/libraries/libKitsunemimiHanamiSegmentParser/tests/memory_leak_tests/memory_leak_tests.pro create mode 100644 src/libraries/libKitsunemimiHanamiSegmentParser/tests/memory_leak_tests/segment_parsestring_test.cpp create mode 100644 src/libraries/libKitsunemimiHanamiSegmentParser/tests/memory_leak_tests/segment_parsestring_test.h create mode 100644 src/libraries/libKitsunemimiHanamiSegmentParser/tests/tests.pro create mode 100644 src/libraries/libKitsunemimiHanamiSegmentParser/tests/unit_tests/main.cpp create mode 100644 src/libraries/libKitsunemimiHanamiSegmentParser/tests/unit_tests/segment_parsestring_test.cpp create mode 100644 src/libraries/libKitsunemimiHanamiSegmentParser/tests/unit_tests/segment_parsestring_test.h create mode 100644 src/libraries/libKitsunemimiHanamiSegmentParser/tests/unit_tests/unit_tests.pro create mode 100644 src/libraries/libKitsunemimiIni/CHANGELOG_old create mode 100644 src/libraries/libKitsunemimiIni/README.md create mode 100755 src/libraries/libKitsunemimiIni/build.sh create mode 100644 src/libraries/libKitsunemimiIni/defaults.pri create mode 100644 src/libraries/libKitsunemimiIni/include/libKitsunemimiIni/ini_item.h create mode 100644 src/libraries/libKitsunemimiIni/libKitsunemimiIni.pro create mode 100644 src/libraries/libKitsunemimiIni/src/grammar/ini_lexer.l create mode 100644 src/libraries/libKitsunemimiIni/src/grammar/ini_parser.y create mode 100644 src/libraries/libKitsunemimiIni/src/ini_item.cpp create mode 100644 src/libraries/libKitsunemimiIni/src/ini_parsing/ini_parser_interface.cpp create mode 100644 src/libraries/libKitsunemimiIni/src/ini_parsing/ini_parser_interface.h create mode 100644 src/libraries/libKitsunemimiIni/src/src.pro create mode 100644 src/libraries/libKitsunemimiIni/tests/tests.pro create mode 100644 src/libraries/libKitsunemimiIni/tests/unit_tests/libKitsunemimiIni/ini_item_test.cpp create mode 100644 src/libraries/libKitsunemimiIni/tests/unit_tests/libKitsunemimiIni/ini_item_test.h create mode 100644 src/libraries/libKitsunemimiIni/tests/unit_tests/main.cpp create mode 100644 src/libraries/libKitsunemimiIni/tests/unit_tests/unit_tests.pro create mode 100644 src/libraries/libKitsunemimiJson/CHANGELOG_old create mode 100644 src/libraries/libKitsunemimiJson/README.md create mode 100755 src/libraries/libKitsunemimiJson/build.sh create mode 100644 src/libraries/libKitsunemimiJson/defaults.pri create mode 100644 src/libraries/libKitsunemimiJson/include/libKitsunemimiJson/json_item.h create mode 100644 src/libraries/libKitsunemimiJson/libKitsunemimiJson.pro create mode 100644 src/libraries/libKitsunemimiJson/src/grammar/json_lexer.l create mode 100644 src/libraries/libKitsunemimiJson/src/grammar/json_parser.y create mode 100644 src/libraries/libKitsunemimiJson/src/json_item.cpp create mode 100644 src/libraries/libKitsunemimiJson/src/json_parsing/json_parser_interface.cpp create mode 100644 src/libraries/libKitsunemimiJson/src/json_parsing/json_parser_interface.h create mode 100644 src/libraries/libKitsunemimiJson/src/src.pro create mode 100644 src/libraries/libKitsunemimiJson/tests/memory_leak_tests/libKitsunemimiJson/json_item_parseString_test.cpp create mode 100644 src/libraries/libKitsunemimiJson/tests/memory_leak_tests/libKitsunemimiJson/json_item_parseString_test.h create mode 100644 src/libraries/libKitsunemimiJson/tests/memory_leak_tests/libKitsunemimiJson/json_item_test.cpp create mode 100644 src/libraries/libKitsunemimiJson/tests/memory_leak_tests/libKitsunemimiJson/json_item_test.h create mode 100644 src/libraries/libKitsunemimiJson/tests/memory_leak_tests/main.cpp create mode 100644 src/libraries/libKitsunemimiJson/tests/memory_leak_tests/memory_leak_tests.pro create mode 100644 src/libraries/libKitsunemimiJson/tests/tests.pro create mode 100644 src/libraries/libKitsunemimiJson/tests/unit_tests/libKitsunemimiJson/json_item_parseString_test.cpp create mode 100644 src/libraries/libKitsunemimiJson/tests/unit_tests/libKitsunemimiJson/json_item_parseString_test.h create mode 100644 src/libraries/libKitsunemimiJson/tests/unit_tests/libKitsunemimiJson/json_item_test.cpp create mode 100644 src/libraries/libKitsunemimiJson/tests/unit_tests/libKitsunemimiJson/json_item_test.h create mode 100644 src/libraries/libKitsunemimiJson/tests/unit_tests/main.cpp create mode 100644 src/libraries/libKitsunemimiJson/tests/unit_tests/unit_tests.pro create mode 100644 src/libraries/libKitsunemimiJwt/CHANGELOG_old create mode 100644 src/libraries/libKitsunemimiJwt/README.md create mode 100755 src/libraries/libKitsunemimiJwt/build.sh create mode 100644 src/libraries/libKitsunemimiJwt/defaults.pri create mode 100644 src/libraries/libKitsunemimiJwt/include/libKitsunemimiJwt/jwt.h create mode 100644 src/libraries/libKitsunemimiJwt/libKitsunemimiJwt.pro create mode 100644 src/libraries/libKitsunemimiJwt/src/jwt.cpp create mode 100644 src/libraries/libKitsunemimiJwt/src/src.pro create mode 100644 src/libraries/libKitsunemimiJwt/tests/tests.pro create mode 100644 src/libraries/libKitsunemimiJwt/tests/unit_tests/jwt_test.cpp create mode 100644 src/libraries/libKitsunemimiJwt/tests/unit_tests/jwt_test.h create mode 100644 src/libraries/libKitsunemimiJwt/tests/unit_tests/main.cpp create mode 100644 src/libraries/libKitsunemimiJwt/tests/unit_tests/unit_tests.pro create mode 100644 src/libraries/libKitsunemimiNetwork/CHANGELOG_old create mode 100644 src/libraries/libKitsunemimiNetwork/README.md create mode 100755 src/libraries/libKitsunemimiNetwork/build.sh create mode 100644 src/libraries/libKitsunemimiNetwork/defaults.pri create mode 100644 src/libraries/libKitsunemimiNetwork/include/libKitsunemimiNetwork/abstract_server.h create mode 100644 src/libraries/libKitsunemimiNetwork/include/libKitsunemimiNetwork/abstract_socket.h create mode 100644 src/libraries/libKitsunemimiNetwork/include/libKitsunemimiNetwork/tcp/tcp_server.h create mode 100644 src/libraries/libKitsunemimiNetwork/include/libKitsunemimiNetwork/tcp/tcp_socket.h create mode 100644 src/libraries/libKitsunemimiNetwork/include/libKitsunemimiNetwork/template_server.h create mode 100644 src/libraries/libKitsunemimiNetwork/include/libKitsunemimiNetwork/template_socket.h create mode 100644 src/libraries/libKitsunemimiNetwork/include/libKitsunemimiNetwork/tls_tcp/tls_tcp_server.h create mode 100644 src/libraries/libKitsunemimiNetwork/include/libKitsunemimiNetwork/tls_tcp/tls_tcp_socket.h create mode 100644 src/libraries/libKitsunemimiNetwork/include/libKitsunemimiNetwork/unix/unix_domain_server.h create mode 100644 src/libraries/libKitsunemimiNetwork/include/libKitsunemimiNetwork/unix/unix_domain_socket.h create mode 100644 src/libraries/libKitsunemimiNetwork/libKitsunemimiNetwork.pro create mode 100644 src/libraries/libKitsunemimiNetwork/src/abstract_server.cpp create mode 100644 src/libraries/libKitsunemimiNetwork/src/abstract_socket.cpp create mode 100644 src/libraries/libKitsunemimiNetwork/src/src.pro create mode 100644 src/libraries/libKitsunemimiNetwork/src/tcp/tcp_server.cpp create mode 100644 src/libraries/libKitsunemimiNetwork/src/tcp/tcp_socket.cpp create mode 100644 src/libraries/libKitsunemimiNetwork/src/tls_tcp/tls_tcp_server.cpp create mode 100644 src/libraries/libKitsunemimiNetwork/src/tls_tcp/tls_tcp_socket.cpp create mode 100644 src/libraries/libKitsunemimiNetwork/src/unix/unix_domain_server.cpp create mode 100644 src/libraries/libKitsunemimiNetwork/src/unix/unix_domain_socket.cpp create mode 100644 src/libraries/libKitsunemimiNetwork/tests/functional_tests/cert_init.h create mode 100644 src/libraries/libKitsunemimiNetwork/tests/functional_tests/example_certs/cert.pem create mode 100644 src/libraries/libKitsunemimiNetwork/tests/functional_tests/example_certs/key.pem create mode 100644 src/libraries/libKitsunemimiNetwork/tests/functional_tests/functional_tests.pro create mode 100644 src/libraries/libKitsunemimiNetwork/tests/functional_tests/libKitsunemimiNetwork/tcp/tcp_test.cpp create mode 100644 src/libraries/libKitsunemimiNetwork/tests/functional_tests/libKitsunemimiNetwork/tcp/tcp_test.h create mode 100644 src/libraries/libKitsunemimiNetwork/tests/functional_tests/libKitsunemimiNetwork/tls_tcp/tls_tcp_test.cpp create mode 100644 src/libraries/libKitsunemimiNetwork/tests/functional_tests/libKitsunemimiNetwork/tls_tcp/tls_tcp_test.h create mode 100644 src/libraries/libKitsunemimiNetwork/tests/functional_tests/libKitsunemimiNetwork/unix/unix_domain_test.cpp create mode 100644 src/libraries/libKitsunemimiNetwork/tests/functional_tests/libKitsunemimiNetwork/unix/unix_domain_test.h create mode 100644 src/libraries/libKitsunemimiNetwork/tests/functional_tests/main.cpp create mode 100644 src/libraries/libKitsunemimiNetwork/tests/memory_leak_tests/cert_init.h create mode 100644 src/libraries/libKitsunemimiNetwork/tests/memory_leak_tests/libKitsunemimiNetwork/tcp/tcp_test.cpp create mode 100644 src/libraries/libKitsunemimiNetwork/tests/memory_leak_tests/libKitsunemimiNetwork/tcp/tcp_test.h create mode 100644 src/libraries/libKitsunemimiNetwork/tests/memory_leak_tests/libKitsunemimiNetwork/unix/unix_domain_test.cpp create mode 100644 src/libraries/libKitsunemimiNetwork/tests/memory_leak_tests/libKitsunemimiNetwork/unix/unix_domain_test.h create mode 100644 src/libraries/libKitsunemimiNetwork/tests/memory_leak_tests/main.cpp create mode 100644 src/libraries/libKitsunemimiNetwork/tests/memory_leak_tests/memory_leak_tests.pro create mode 100644 src/libraries/libKitsunemimiNetwork/tests/tests.pro create mode 100644 src/libraries/libKitsunemimiObj/CHANGELOG_old create mode 100644 src/libraries/libKitsunemimiObj/README.md create mode 100755 src/libraries/libKitsunemimiObj/build.sh create mode 100644 src/libraries/libKitsunemimiObj/defaults.pri create mode 100644 src/libraries/libKitsunemimiObj/include/libKitsunemimiObj/obj_item.h create mode 100644 src/libraries/libKitsunemimiObj/libKitsunemimiObj.pro create mode 100644 src/libraries/libKitsunemimiObj/src/obj_creator.cpp create mode 100644 src/libraries/libKitsunemimiObj/src/obj_creator.h create mode 100644 src/libraries/libKitsunemimiObj/src/obj_item.cpp create mode 100644 src/libraries/libKitsunemimiObj/src/obj_parser.cpp create mode 100644 src/libraries/libKitsunemimiObj/src/obj_parser.h create mode 100644 src/libraries/libKitsunemimiObj/src/src.pro create mode 100644 src/libraries/libKitsunemimiObj/tests/functional_tests/functional_tests.pro create mode 100644 src/libraries/libKitsunemimiObj/tests/functional_tests/main.cpp create mode 100644 src/libraries/libKitsunemimiObj/tests/functional_tests/obj_item_test.cpp create mode 100644 src/libraries/libKitsunemimiObj/tests/functional_tests/obj_item_test.h create mode 100644 src/libraries/libKitsunemimiObj/tests/tests.pro create mode 100644 src/libraries/libKitsunemimiOpencl/CHANGELOG_old create mode 100644 src/libraries/libKitsunemimiOpencl/README.md create mode 100755 src/libraries/libKitsunemimiOpencl/build.sh create mode 100644 src/libraries/libKitsunemimiOpencl/defaults.pri create mode 100644 src/libraries/libKitsunemimiOpencl/include/libKitsunemimiOpencl/gpu_data.h create mode 100644 src/libraries/libKitsunemimiOpencl/include/libKitsunemimiOpencl/gpu_handler.h create mode 100644 src/libraries/libKitsunemimiOpencl/include/libKitsunemimiOpencl/gpu_interface.h create mode 100644 src/libraries/libKitsunemimiOpencl/libKitsunemimiOpencl.pro create mode 100644 src/libraries/libKitsunemimiOpencl/src/gpu_data.cpp create mode 100644 src/libraries/libKitsunemimiOpencl/src/gpu_handler.cpp create mode 100644 src/libraries/libKitsunemimiOpencl/src/gpu_interface.cpp create mode 100644 src/libraries/libKitsunemimiOpencl/src/src.pro create mode 100644 src/libraries/libKitsunemimiOpencl/tests/benchmark_tests/benchmark_tests.pro create mode 100644 src/libraries/libKitsunemimiOpencl/tests/benchmark_tests/main.cpp create mode 100644 src/libraries/libKitsunemimiOpencl/tests/benchmark_tests/simple_test.cpp create mode 100644 src/libraries/libKitsunemimiOpencl/tests/benchmark_tests/simple_test.h create mode 100644 src/libraries/libKitsunemimiOpencl/tests/functional_tests/functional_tests.pro create mode 100644 src/libraries/libKitsunemimiOpencl/tests/functional_tests/main.cpp create mode 100644 src/libraries/libKitsunemimiOpencl/tests/functional_tests/simple_test.cpp create mode 100644 src/libraries/libKitsunemimiOpencl/tests/functional_tests/simple_test.h create mode 100644 src/libraries/libKitsunemimiOpencl/tests/tests.pro create mode 100644 src/libraries/libKitsunemimiSakuraDatabase/CHANGELOG_old create mode 100644 src/libraries/libKitsunemimiSakuraDatabase/README.md create mode 100755 src/libraries/libKitsunemimiSakuraDatabase/build.sh create mode 100644 src/libraries/libKitsunemimiSakuraDatabase/defaults.pri create mode 100644 src/libraries/libKitsunemimiSakuraDatabase/include/libKitsunemimiSakuraDatabase/sql_database.h create mode 100644 src/libraries/libKitsunemimiSakuraDatabase/include/libKitsunemimiSakuraDatabase/sql_table.h create mode 100644 src/libraries/libKitsunemimiSakuraDatabase/libKitsunemimiSakuraDatabase.pro create mode 100644 src/libraries/libKitsunemimiSakuraDatabase/src/sql_database.cpp create mode 100644 src/libraries/libKitsunemimiSakuraDatabase/src/sql_table.cpp create mode 100644 src/libraries/libKitsunemimiSakuraDatabase/src/src.pro create mode 100644 src/libraries/libKitsunemimiSakuraDatabase/tests/functional_tests/functional_tests.pro create mode 100644 src/libraries/libKitsunemimiSakuraDatabase/tests/functional_tests/main.cpp create mode 100644 src/libraries/libKitsunemimiSakuraDatabase/tests/functional_tests/sql_table_test.cpp create mode 100644 src/libraries/libKitsunemimiSakuraDatabase/tests/functional_tests/sql_table_test.h create mode 100644 src/libraries/libKitsunemimiSakuraDatabase/tests/functional_tests/test_table.cpp create mode 100644 src/libraries/libKitsunemimiSakuraDatabase/tests/functional_tests/test_table.h create mode 100644 src/libraries/libKitsunemimiSakuraDatabase/tests/tests.pro create mode 100644 src/libraries/libKitsunemimiSakuraHardware/CHANGELOG_old create mode 100644 src/libraries/libKitsunemimiSakuraHardware/README.md create mode 100755 src/libraries/libKitsunemimiSakuraHardware/build.sh create mode 100644 src/libraries/libKitsunemimiSakuraHardware/defaults.pri create mode 100644 src/libraries/libKitsunemimiSakuraHardware/include/libKitsunemimiSakuraHardware/cpu_core.h create mode 100644 src/libraries/libKitsunemimiSakuraHardware/include/libKitsunemimiSakuraHardware/cpu_package.h create mode 100644 src/libraries/libKitsunemimiSakuraHardware/include/libKitsunemimiSakuraHardware/cpu_thread.h create mode 100644 src/libraries/libKitsunemimiSakuraHardware/include/libKitsunemimiSakuraHardware/host.h create mode 100644 src/libraries/libKitsunemimiSakuraHardware/libKitsunemimiSakuraHardware.pro create mode 100644 src/libraries/libKitsunemimiSakuraHardware/src/cpu_core.cpp create mode 100644 src/libraries/libKitsunemimiSakuraHardware/src/cpu_package.cpp create mode 100644 src/libraries/libKitsunemimiSakuraHardware/src/cpu_thread.cpp create mode 100644 src/libraries/libKitsunemimiSakuraHardware/src/host.cpp create mode 100644 src/libraries/libKitsunemimiSakuraHardware/src/src.pro create mode 100644 src/libraries/libKitsunemimiSakuraHardware/tests/cli_tests/cli_tests.pro create mode 100644 src/libraries/libKitsunemimiSakuraHardware/tests/cli_tests/main.cpp create mode 100644 src/libraries/libKitsunemimiSakuraHardware/tests/tests.pro create mode 100644 src/libraries/libKitsunemimiSakuraNetwork/CHANGELOG_old create mode 100644 src/libraries/libKitsunemimiSakuraNetwork/README.md create mode 100755 src/libraries/libKitsunemimiSakuraNetwork/build.sh create mode 100644 src/libraries/libKitsunemimiSakuraNetwork/defaults.pri create mode 100644 src/libraries/libKitsunemimiSakuraNetwork/include/libKitsunemimiSakuraNetwork/session.h create mode 100644 src/libraries/libKitsunemimiSakuraNetwork/include/libKitsunemimiSakuraNetwork/session_controller.h create mode 100644 src/libraries/libKitsunemimiSakuraNetwork/libKitsunemimiSakuraNetwork.pro create mode 100644 src/libraries/libKitsunemimiSakuraNetwork/src/callbacks.h create mode 100644 src/libraries/libKitsunemimiSakuraNetwork/src/handler/message_blocker_handler.cpp create mode 100644 src/libraries/libKitsunemimiSakuraNetwork/src/handler/message_blocker_handler.h create mode 100644 src/libraries/libKitsunemimiSakuraNetwork/src/handler/reply_handler.cpp create mode 100644 src/libraries/libKitsunemimiSakuraNetwork/src/handler/reply_handler.h create mode 100644 src/libraries/libKitsunemimiSakuraNetwork/src/handler/session_handler.cpp create mode 100644 src/libraries/libKitsunemimiSakuraNetwork/src/handler/session_handler.h create mode 100644 src/libraries/libKitsunemimiSakuraNetwork/src/message_definitions.h create mode 100644 src/libraries/libKitsunemimiSakuraNetwork/src/messages_processing/error_processing.h create mode 100644 src/libraries/libKitsunemimiSakuraNetwork/src/messages_processing/heartbeat_processing.h create mode 100644 src/libraries/libKitsunemimiSakuraNetwork/src/messages_processing/multiblock_data_processing.h create mode 100644 src/libraries/libKitsunemimiSakuraNetwork/src/messages_processing/session_processing.h create mode 100644 src/libraries/libKitsunemimiSakuraNetwork/src/messages_processing/singleblock_data_processing.h create mode 100644 src/libraries/libKitsunemimiSakuraNetwork/src/messages_processing/stream_data_processing.h create mode 100644 src/libraries/libKitsunemimiSakuraNetwork/src/multiblock_io.cpp create mode 100644 src/libraries/libKitsunemimiSakuraNetwork/src/multiblock_io.h create mode 100644 src/libraries/libKitsunemimiSakuraNetwork/src/session.cpp create mode 100644 src/libraries/libKitsunemimiSakuraNetwork/src/session_controller.cpp create mode 100644 src/libraries/libKitsunemimiSakuraNetwork/src/src.pro create mode 100644 src/libraries/libKitsunemimiSakuraNetwork/tests/functional_tests/functional_tests.pro create mode 100644 src/libraries/libKitsunemimiSakuraNetwork/tests/functional_tests/main.cpp create mode 100644 src/libraries/libKitsunemimiSakuraNetwork/tests/functional_tests/session_test.cpp create mode 100644 src/libraries/libKitsunemimiSakuraNetwork/tests/functional_tests/session_test.h create mode 100644 src/libraries/libKitsunemimiSakuraNetwork/tests/memory_leak_tests/main.cpp create mode 100644 src/libraries/libKitsunemimiSakuraNetwork/tests/memory_leak_tests/memory_leak_tests.pro create mode 100644 src/libraries/libKitsunemimiSakuraNetwork/tests/memory_leak_tests/session_test.cpp create mode 100644 src/libraries/libKitsunemimiSakuraNetwork/tests/memory_leak_tests/session_test.h create mode 100644 src/libraries/libKitsunemimiSakuraNetwork/tests/tests.pro create mode 100644 src/libraries/libKitsunemimiSqlite/CHANGELOG_old create mode 100644 src/libraries/libKitsunemimiSqlite/README.md create mode 100755 src/libraries/libKitsunemimiSqlite/build.sh create mode 100644 src/libraries/libKitsunemimiSqlite/defaults.pri create mode 100644 src/libraries/libKitsunemimiSqlite/include/libKitsunemimiSqlite/sqlite.h create mode 100644 src/libraries/libKitsunemimiSqlite/libKitsunemimiSqlite.pro create mode 100644 src/libraries/libKitsunemimiSqlite/src/sqlite.cpp create mode 100644 src/libraries/libKitsunemimiSqlite/src/src.pro create mode 100644 src/libraries/libKitsunemimiSqlite/tests/tests.pro create mode 100644 src/libraries/libKitsunemimiSqlite/tests/unit_tests/main.cpp create mode 100644 src/libraries/libKitsunemimiSqlite/tests/unit_tests/sqlite_test.cpp create mode 100644 src/libraries/libKitsunemimiSqlite/tests/unit_tests/sqlite_test.h create mode 100644 src/libraries/libKitsunemimiSqlite/tests/unit_tests/unit_tests.pro create mode 100644 src/libraries/libMisakiGuard/CHANGELOG_old create mode 100644 src/libraries/libMisakiGuard/README.md create mode 100755 src/libraries/libMisakiGuard/build.sh create mode 100644 src/libraries/libMisakiGuard/defaults.pri create mode 100644 src/libraries/libMisakiGuard/include/libMisakiGuard/misaki_input.h create mode 100644 src/libraries/libMisakiGuard/libMisakiGuard.pro create mode 100644 src/libraries/libMisakiGuard/src/generate_api_docu.cpp create mode 100644 src/libraries/libMisakiGuard/src/generate_api_docu.h create mode 100644 src/libraries/libMisakiGuard/src/md_docu_generation.cpp create mode 100644 src/libraries/libMisakiGuard/src/md_docu_generation.h create mode 100644 src/libraries/libMisakiGuard/src/misaki_input.cpp create mode 100644 src/libraries/libMisakiGuard/src/rst_docu_generation.cpp create mode 100644 src/libraries/libMisakiGuard/src/rst_docu_generation.h create mode 100644 src/libraries/libMisakiGuard/src/src.pro create mode 100644 src/libraries/libShioriArchive/CHANGELOG_old create mode 100644 src/libraries/libShioriArchive/README.md create mode 100755 src/libraries/libShioriArchive/build.sh create mode 100644 src/libraries/libShioriArchive/defaults.pri create mode 100644 src/libraries/libShioriArchive/include/libShioriArchive/datasets.h create mode 100644 src/libraries/libShioriArchive/include/libShioriArchive/other.h create mode 100644 src/libraries/libShioriArchive/include/libShioriArchive/snapshots.h create mode 100644 src/libraries/libShioriArchive/libShioriArchive.pro create mode 100644 src/libraries/libShioriArchive/src/datasets.cpp create mode 100644 src/libraries/libShioriArchive/src/other.cpp create mode 100644 src/libraries/libShioriArchive/src/snapshots.cpp create mode 100644 src/libraries/libShioriArchive/src/src.pro create mode 100644 src/libraries/libShioriArchive/tests/tests.pro create mode 100644 src/libraries/libShioriArchive/tests/unit_tests/main.cpp create mode 100644 src/libraries/libShioriArchive/tests/unit_tests/unit_tests.pro create mode 100644 src/libraries/libraries.pro create mode 100644 src/sdk/CHANGELOG_old create mode 100644 src/sdk/README.md create mode 100644 src/sdk/cpp/libHanamiAiSdk/README.md create mode 100755 src/sdk/cpp/libHanamiAiSdk/build.sh create mode 100644 src/sdk/cpp/libHanamiAiSdk/defaults.pri create mode 100644 src/sdk/cpp/libHanamiAiSdk/include/libHanamiAiSdk/cluster.h create mode 100644 src/sdk/cpp/libHanamiAiSdk/include/libHanamiAiSdk/common/websocket_client.h create mode 100644 src/sdk/cpp/libHanamiAiSdk/include/libHanamiAiSdk/data_set.h create mode 100644 src/sdk/cpp/libHanamiAiSdk/include/libHanamiAiSdk/init.h create mode 100644 src/sdk/cpp/libHanamiAiSdk/include/libHanamiAiSdk/io.h create mode 100644 src/sdk/cpp/libHanamiAiSdk/include/libHanamiAiSdk/project.h create mode 100644 src/sdk/cpp/libHanamiAiSdk/include/libHanamiAiSdk/request_result.h create mode 100644 src/sdk/cpp/libHanamiAiSdk/include/libHanamiAiSdk/snapshot.h create mode 100644 src/sdk/cpp/libHanamiAiSdk/include/libHanamiAiSdk/task.h create mode 100644 src/sdk/cpp/libHanamiAiSdk/include/libHanamiAiSdk/template.h create mode 100644 src/sdk/cpp/libHanamiAiSdk/include/libHanamiAiSdk/user.h create mode 100644 src/sdk/cpp/libHanamiAiSdk/libHanamiAiSdk.pro create mode 100644 src/sdk/cpp/libHanamiAiSdk/src/cluster.cpp create mode 100644 src/sdk/cpp/libHanamiAiSdk/src/common/http_client.cpp create mode 100644 src/sdk/cpp/libHanamiAiSdk/src/common/http_client.h create mode 100644 src/sdk/cpp/libHanamiAiSdk/src/common/websocket_client.cpp create mode 100644 src/sdk/cpp/libHanamiAiSdk/src/data_set.cpp create mode 100644 src/sdk/cpp/libHanamiAiSdk/src/init.cpp create mode 100644 src/sdk/cpp/libHanamiAiSdk/src/io.cpp create mode 100644 src/sdk/cpp/libHanamiAiSdk/src/project.cpp create mode 100644 src/sdk/cpp/libHanamiAiSdk/src/request_result.cpp create mode 100644 src/sdk/cpp/libHanamiAiSdk/src/snapshot.cpp create mode 100644 src/sdk/cpp/libHanamiAiSdk/src/src.pro create mode 100644 src/sdk/cpp/libHanamiAiSdk/src/task.cpp create mode 100644 src/sdk/cpp/libHanamiAiSdk/src/template.cpp create mode 100644 src/sdk/cpp/libHanamiAiSdk/src/user.cpp create mode 100644 src/sdk/cpp/libHanamiAiSdk/tests/tests.pro create mode 100644 src/sdk/go/README.md create mode 100644 src/sdk/go/hanami_ai_sdk/cluster_commands.go create mode 100644 src/sdk/go/hanami_ai_sdk/documentation_commands.go create mode 100644 src/sdk/go/hanami_ai_sdk/go.mod create mode 100644 src/sdk/go/hanami_ai_sdk/http_request.go create mode 100644 src/sdk/go/hanami_ai_sdk/run_commands.go create mode 100644 src/sdk/go/hanami_ai_sdk/threading_commands.go create mode 100644 src/sdk/go/hanami_ai_sdk/train_data_commands.go create mode 100644 src/sdk/go/hanami_ai_sdk/user_commands.go create mode 100644 src/sdk/javascript/cluster.js create mode 100644 src/sdk/javascript/common.js create mode 100644 src/sdk/javascript/data_set.js create mode 100644 src/sdk/javascript/init.js create mode 100644 src/sdk/javascript/io.js create mode 100644 src/sdk/javascript/logs.js create mode 100644 src/sdk/javascript/measurements.js create mode 100644 src/sdk/javascript/project.js create mode 100644 src/sdk/javascript/request_result.js create mode 100644 src/sdk/javascript/snapshot.js create mode 100644 src/sdk/javascript/system_info.js create mode 100644 src/sdk/javascript/task.js create mode 100644 src/sdk/javascript/template.js create mode 100644 src/sdk/javascript/user.js diff --git a/Hanami-AI.pro b/Hanami-AI.pro index c6435c29..5db6795e 100644 --- a/Hanami-AI.pro +++ b/Hanami-AI.pro @@ -3,70 +3,9 @@ CONFIG += ordered QT -= qt core gui CONFIG += c++17 -libHanamiAiSdk.file = libHanamiAiSdk/cpp/libHanamiAiSdk.pro - -SUBDIRS = libKitsunemimiCommon -SUBDIRS += libKitsunemimiNetwork -SUBDIRS += libKitsunemimiJson -SUBDIRS += libKitsunemimiCrypto -SUBDIRS += libKitsunemimiJwt -SUBDIRS += libKitsunemimiIni -SUBDIRS += libKitsunemimiArgs -SUBDIRS += libKitsunemimiConfig -SUBDIRS += libKitsunemimiCpu -SUBDIRS += libKitsunemimiSqlite -SUBDIRS += libKitsunemimiOpencl -SUBDIRS += libKitsunemimiSakuraHardware -SUBDIRS += libKitsunemimiSakuraDatabase -SUBDIRS += libKitsunemimiSakuraNetwork -SUBDIRS += libKitsunemimiHanamiCommon -SUBDIRS += libKitsunemimiHanamiPolicies -SUBDIRS += libKitsunemimiHanamiDatabase -SUBDIRS += libKitsunemimiHanamiSegmentParser -SUBDIRS += libKitsunemimiHanamiClusterParser -SUBDIRS += libKitsunemimiHanamiNetwork -SUBDIRS += libHanamiAiSdk -SUBDIRS += libShioriArchive -SUBDIRS += libMisakiGuard -SUBDIRS += libAzukiHeart -SUBDIRS += ToriiGateway -SUBDIRS += ShioriArchive -SUBDIRS += MisakiGuard -SUBDIRS += AzukiHeart -SUBDIRS += KyoukoMind -SUBDIRS += TsugumiTester - -libKitsunemimiNetwork.depends = libKitsunemimiCommon -libKitsunemimiJson.depends = libKitsunemimiCommon -libKitsunemimiCrypto.depends = libKitsunemimiCommon -libKitsunemimiJwt.depends = libKitsunemimiCrypto libKitsunemimiJson -libKitsunemimiIni.depends = libKitsunemimiCommon -libKitsunemimiArgs.depends = libKitsunemimiCommon -libKitsunemimiConfig.depends = libKitsunemimiIni -libKitsunemimiCpu.depends = libKitsunemimiCommon -libKitsunemimiSqlite.depends = libKitsunemimiCommon -libKitsunemimiOpencl.depends = libKitsunemimiCommon -libKitsunemimiSakuraHardware.depends = libKitsunemimiCpu -libKitsunemimiSakuraDatabase.depends = libKitsunemimiSqlite -libKitsunemimiSakuraNetwork.depends = libKitsunemimiNetwork -libKitsunemimiHanamiCommon.depends = libKitsunemimiConfig libKitsunemimiArgs -libKitsunemimiHanamiPolicies.depends = libKitsunemimiConfig libKitsunemimiArgs -libKitsunemimiHanamiDatabase.depends = libKitsunemimiJson libKitsunemimiSakuraDatabase -libKitsunemimiHanamiSegmentParser.depends = libKitsunemimiConfig libKitsunemimiArgs libKitsunemimiHanamiCommon -libKitsunemimiHanamiClusterParser.depends = libKitsunemimiConfig libKitsunemimiArgs libKitsunemimiHanamiCommon -libKitsunemimiHanamiNetwork.depends = libKitsunemimiHanamiCommon libKitsunemimiCrypto libKitsunemimiJson -libHanamiAiSdk.depends = libKitsunemimiHanamiCommon libKitsunemimiCrypto libKitsunemimiJson -libShioriArchive.depends = libKitsunemimiHanamiNetwork -libMisakiGuard.depends = libKitsunemimiHanamiNetwork -libAzukiHeart.depends = libKitsunemimiHanamiNetwork -ToriiGateway.depends = libKitsunemimiHanamiNetwork -ShioriArchive.depends = libKitsunemimiHanamiNetwork libKitsunemimiHanamiDatabase -MisakiGuard.depends = libKitsunemimiHanamiNetwork libKitsunemimiHanamiDatabase libKitsunemimiHanamiPolicies -AzukiHeart.depends = libKitsunemimiHanamiNetwork libKitsunemimiHanamiDatabase -KyoukoMind.depends = libKitsunemimiHanamiNetwork libKitsunemimiOpencl libKitsunemimiHanamiClusterParser libKitsunemimiHanamiSegmentParser libKitsunemimiHanamiDatabase libHanamiAiSdk -TsugumiTester.depends = libKitsunemimiHanamiNetwork ibKitsunemimiHanamiClusterParser libKitsunemimiHanamiSegmentParser libKitsunemimiHanamiDatabase libHanamiAiSdk - - - - +SUBDIRS = src/libraries +SUBDIRS += src/components +SUBDIRS += src/sdk/cpp/libHanamiAiSdk +src/sdk/cpp/libHanamiAiSdk.depends = libraries +src/components.depends = libraries libHanamiAiSdk diff --git a/src/components/AzukiHeart/AzukiHeart.pro b/src/components/AzukiHeart/AzukiHeart.pro new file mode 100644 index 00000000..d7c41fe7 --- /dev/null +++ b/src/components/AzukiHeart/AzukiHeart.pro @@ -0,0 +1,135 @@ +QT -= qt core gui + +TARGET = AzukiHeart +CONFIG += console +CONFIG += c++17 + +LIBS += -L../../libraries/../libraries/libAzukiHeart/src -lAzukiHeart +LIBS += -L../../libraries/../libraries/libAzukiHeart/src/debug -lAzukiHeart +LIBS += -L../../libraries/../libraries/libAzukiHeart/src/release -lAzukiHeart +INCLUDEPATH += ../../libraries/libAzukiHeart/include + +LIBS += -L../../libraries/../libraries/libMisakiGuard/src -lMisakiGuard +LIBS += -L../../libraries/../libraries/libMisakiGuard/src/debug -lMisakiGuard +LIBS += -L../../libraries/../libraries/libMisakiGuard/src/release -lMisakiGuard +INCLUDEPATH += ../../libraries/libMisakiGuard/include + +LIBS += -L../../libraries/../libraries/libKitsunemimiHanamiNetwork/src -lKitsunemimiHanamiNetwork +LIBS += -L../../libraries/../libraries/libKitsunemimiHanamiNetwork/src/debug -lKitsunemimiHanamiNetwork +LIBS += -L../../libraries/../libraries/libKitsunemimiHanamiNetwork/src/release -lKitsunemimiHanamiNetwork +INCLUDEPATH += ../../libraries/libKitsunemimiHanamiNetwork/include + +LIBS += -L../../libraries/../libraries/libKitsunemimiHanamiCommon/src -lKitsunemimiHanamiCommon +LIBS += -L../../libraries/../libraries/libKitsunemimiHanamiCommon/src/debug -lKitsunemimiHanamiCommon +LIBS += -L../../libraries/../libraries/libKitsunemimiHanamiCommon/src/release -lKitsunemimiHanamiCommon +INCLUDEPATH += ../../libraries/libKitsunemimiHanamiCommon/include + +LIBS += -L../../libraries/../libraries/libKitsunemimiSakuraHardware/src -lKitsunemimiSakuraHardware +LIBS += -L../../libraries/../libraries/libKitsunemimiSakuraHardware/src/debug -lKitsunemimiSakuraHardware +LIBS += -L../../libraries/../libraries/libKitsunemimiSakuraHardware/src/release -lKitsunemimiSakuraHardware +INCLUDEPATH += ../../libraries/libKitsunemimiSakuraHardware/include + +LIBS += -L../../libraries/../libraries/libKitsunemimiArgs/src -lKitsunemimiArgs +LIBS += -L../../libraries/../libraries/libKitsunemimiArgs/src/debug -lKitsunemimiArgs +LIBS += -L../../libraries/../libraries/libKitsunemimiArgs/src/release -lKitsunemimiArgs +INCLUDEPATH += ../../libraries/libKitsunemimiArgs/include + +LIBS += -L../../libraries/../libraries/libKitsunemimiConfig/src -lKitsunemimiConfig +LIBS += -L../../libraries/../libraries/libKitsunemimiConfig/src/debug -lKitsunemimiConfig +LIBS += -L../../libraries/../libraries/libKitsunemimiConfig/src/release -lKitsunemimiConfig +INCLUDEPATH += ../../libraries/libKitsunemimiConfig/include + +LIBS += -L../../libraries/../libraries/libKitsunemimiSakuraNetwork/src -lKitsunemimiSakuraNetwork +LIBS += -L../../libraries/../libraries/libKitsunemimiSakuraNetwork/src/debug -lKitsunemimiSakuraNetwork +LIBS += -L../../libraries/../libraries/libKitsunemimiSakuraNetwork/src/release -lKitsunemimiSakuraNetwork +INCLUDEPATH += ../../libraries/libKitsunemimiSakuraNetwork/include + +LIBS += -L../../libraries/../libraries/libKitsunemimiCommon/src -lKitsunemimiCommon +LIBS += -L../../libraries/../libraries/libKitsunemimiCommon/src/debug -lKitsunemimiCommon +LIBS += -L../../libraries/../libraries/libKitsunemimiCommon/src/release -lKitsunemimiCommon +INCLUDEPATH += ../../libraries/libKitsunemimiCommon/include + +LIBS += -L../../libraries/../libraries/libKitsunemimiNetwork/src -lKitsunemimiNetwork +LIBS += -L../../libraries/../libraries/libKitsunemimiNetwork/src/debug -lKitsunemimiNetwork +LIBS += -L../../libraries/../libraries/libKitsunemimiNetwork/src/release -lKitsunemimiNetwork +INCLUDEPATH += ../../libraries/libKitsunemimiNetwork/include + +LIBS += -L../../libraries/../libraries/libKitsunemimiJson/src -lKitsunemimiJson +LIBS += -L../../libraries/../libraries/libKitsunemimiJson/src/debug -lKitsunemimiJson +LIBS += -L../../libraries/../libraries/libKitsunemimiJson/src/release -lKitsunemimiJson +INCLUDEPATH += ../../libraries/libKitsunemimiJson/include + +LIBS += -L../../libraries/../libraries/libKitsunemimiIni/src -lKitsunemimiIni +LIBS += -L../../libraries/../libraries/libKitsunemimiIni/src/debug -lKitsunemimiIni +LIBS += -L../../libraries/../libraries/libKitsunemimiIni/src/release -lKitsunemimiIni +INCLUDEPATH += ../../libraries/libKitsunemimiIni/include + +LIBS += -L../../libraries/../libraries/libKitsunemimiJwt/src -lKitsunemimiJwt +LIBS += -L../../libraries/../libraries/libKitsunemimiJwt/src/debug -lKitsunemimiJwt +LIBS += -L../../libraries/../libraries/libKitsunemimiJwti/src/release -lKitsunemimiJwt +INCLUDEPATH += ../../libraries/libKitsunemimiJwt/include + +LIBS += -L../../libraries/../libraries/libKitsunemimiCrypto/src -lKitsunemimiCrypto +LIBS += -L../../libraries/../libraries/libKitsunemimiCrypto/src/debug -lKitsunemimiCrypto +LIBS += -L../../libraries/../libraries/libKitsunemimiCrypto/src/release -lKitsunemimiCrypto +INCLUDEPATH += ../../libraries/libKitsunemimiCrypto/include + +LIBS += -L../../libraries/../libraries/libKitsunemimiCpu/src -lKitsunemimiCpu +LIBS += -L../../libraries/../libraries/libKitsunemimiCpu/src/debug -lKitsunemimiCpu +LIBS += -L../../libraries/../libraries/libKitsunemimiCpu/src/release -lKitsunemimiCpu +INCLUDEPATH += ../../libraries/libKitsunemimiCpu/include + +LIBS += -lcryptopp -lssl -luuid -lcrypto -lprotobuf -lpthread + +INCLUDEPATH += $$PWD \ + src + +SOURCES += src/main.cpp \ + src/api/v1/measurements/power_consumption.cpp \ + src/api/v1/measurements/speed.cpp \ + src/api/v1/measurements/temperature_production.cpp \ + src/api/v1/system_info/get_system_info.cpp \ + src/api/v1/threading/get_thread_mapping.cpp \ + src/azuki_root.cpp \ + src/core/power_measuring.cpp \ + src/core/speed_measuring.cpp \ + src/core/temperature_measuring.cpp \ + src/core/thread_binder.cpp \ + src/core/value_container.cpp + +HEADERS += \ + src/api/blossom_initializing.h \ + src/api/v1/measurements/power_consumption.h \ + src/api/v1/measurements/speed.h \ + src/api/v1/measurements/temperature_production.h \ + src/api/v1/system_info/get_system_info.h \ + src/api/v1/threading/get_thread_mapping.h \ + src/args.h \ + src/azuki_root.h \ + src/callbacks.h \ + src/config.h \ + src/core/power_measuring.h \ + src/core/speed_measuring.h \ + src/core/temperature_measuring.h \ + src/core/thread_binder.h \ + src/core/value_container.h + +AZUKI_PROTO_BUFFER = ../../libraries/libKitsunemimiHanamiMessages/protobuffers/azuki_messages.proto3 + +OTHER_FILES += $$AZUKI_PROTO_BUFFER + +protobuf_decl.name = protobuf headers +protobuf_decl.name = protobuf headers +protobuf_decl.input = KYOUKO_PROTO_BUFFER +protobuf_decl.output = ${QMAKE_FILE_IN_PATH}/${QMAKE_FILE_BASE}.proto3.pb.h +protobuf_decl.commands = protoc --cpp_out=${QMAKE_FILE_IN_PATH} --proto_path=${QMAKE_FILE_IN_PATH} ${QMAKE_FILE_NAME} +protobuf_decl.variable_out = HEADERS +QMAKE_EXTRA_COMPILERS += protobuf_decl + +protobuf_impl.name = protobuf sources +protobuf_impl.input = KYOUKO_PROTO_BUFFER +protobuf_impl.output = ${QMAKE_FILE_IN_PATH}/${QMAKE_FILE_BASE}.proto3.pb.cc +protobuf_impl.depends = ${QMAKE_FILE_IN_PATH}/${QMAKE_FILE_BASE}.proto3.pb.h +protobuf_impl.commands = $$escape_expand(\n) +protobuf_impl.variable_out = SOURCES +QMAKE_EXTRA_COMPILERS += protobuf_impl diff --git a/src/components/AzukiHeart/CHANGELOG_old b/src/components/AzukiHeart/CHANGELOG_old new file mode 100644 index 00000000..f52d6b05 --- /dev/null +++ b/src/components/AzukiHeart/CHANGELOG_old @@ -0,0 +1,22 @@ +# Changelog + +## [0.2.0] - 2022-07-02 + +### Changed +- only update thread-bindings, in case there has something to be updated to avoid unnecessary requests +- merged files for better structure +- allow multiple core-ids when binding threads +- use separate core-ids for processing-threads + + +## [0.1.1] - 2022-02-14 + +### Changed +- use new component-libraries + + +## [0.1.0] - 2022-01-09 + +### Added +- request thread-mapping of all components +- update thread-binding for all components over networt diff --git a/src/components/AzukiHeart/README.md b/src/components/AzukiHeart/README.md new file mode 100644 index 00000000..1b4a4da0 --- /dev/null +++ b/src/components/AzukiHeart/README.md @@ -0,0 +1,16 @@ +# AzukiHeart + +## Description + +Resouce-managment- and Monitoring-component of the Hanami-AI-Project: https://github.com/kitsudaiki/Hanami-AI + +## Author + +Tobias Anker + +eMail: tobias.anker@kitsunemimi.moe + +## License + +This project is licensed under the Apache License Version 2.0 - see the [LICENSE](LICENSE) file for details + diff --git a/src/components/AzukiHeart/build.sh b/src/components/AzukiHeart/build.sh new file mode 100755 index 00000000..9f330045 --- /dev/null +++ b/src/components/AzukiHeart/build.sh @@ -0,0 +1,145 @@ +#!/bin/bash + +# get current directory-path and the path of the parent-directory +DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )" +PARENT_DIR="$(dirname "$DIR")" +CURRENT_BRANCH=$(git rev-parse --abbrev-ref HEAD) + +# create build-directory +BUILD_DIR="$PARENT_DIR/build" +mkdir -p $BUILD_DIR + +# create directory for the final result +RESULT_DIR="$PARENT_DIR/result" +mkdir -p $RESULT_DIR + +#----------------------------------------------------------------------------------------------------------------- + +function build_kitsune_lib_repo () { + REPO_NAME=$1 + NUMBER_OF_THREADS=$2 + ADDITIONAL_CONFIGS=$3 + + # create build directory for repo and go into this directory + REPO_DIR="$BUILD_DIR/$REPO_NAME" + mkdir -p $REPO_DIR + cd $REPO_DIR + + # build repo library with qmake + /usr/lib/x86_64-linux-gnu/qt5/bin/qmake "$PARENT_DIR/$REPO_NAME/$REPO_NAME.pro" -spec linux-g++ "CONFIG += optimize_full staticlib $ADDITIONAL_CONFIGS" + /usr/bin/make -j$NUMBER_OF_THREADS + + # copy build-result and include-files into the result-directory + echo "----------------------------------------------------------------------" + echo $RESULT_DIR + cp $REPO_DIR/src/$REPO_NAME.a $RESULT_DIR/ + cp -r $PARENT_DIR/$REPO_NAME/include $RESULT_DIR/ + ls -l $RESULT_DIR/include/ + ls -l $RESULT_DIR +} + +function download_repo_github () { + REPO_NAME=$1 + TAG_OR_BRANCH=$2 + + echo "" + echo "" + echo "++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++" + echo "$REPO_NAME" + echo "++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++" + echo "Branch/Tag: $TAG_OR_BRANCH" + + # clone repo + git clone https://github.com/kitsudaiki/$REPO_NAME.git "$PARENT_DIR/$REPO_NAME" + cd "$PARENT_DIR/$REPO_NAME" + + # checkout branch + if [[ $CURRENT_BRANCH =~ ^tag.* ]] || [[ $CURRENT_BRANCH =~ ^hotfix.* ]] || [[ $CURRENT_BRANCH =~ ^v.* ]] || [[ $CURRENT_BRANCH =~ ^rolling$ ]] || [[ $CURRENT_BRANCH =~ ^staging$ ]]; then + # if a stable branch, then use the defined tag of branch + # check if defined branch even exist + BRANCH_EXIST=$(git ls-remote --heads origin $TAG_OR_BRANCH) + if [[ -z "$BRANCH_EXIST" ]]; then + echo "" + echo "-------------------------------------------------------------------------------------" + echo "Branch or tag '$TAG_OR_BRANCH' does not exist for the repository '$REPO_NAME'" + echo "-------------------------------------------------------------------------------------" + echo "" + exit 1 + fi + git checkout $TAG_OR_BRANCH + else + # if develop or feature branch, then try to checkout the feature-branch in the other repo as well + # or otherwise use the develop-branch as default + BRANCH_EXIST=$(git ls-remote --heads origin $CURRENT_BRANCH) + if [[ -n "$BRANCH_EXIST" ]]; then + git checkout $CURRENT_BRANCH + else + git checkout develop + fi + fi +} + +function get_required_kitsune_lib_repo () { + REPO_NAME=$1 + TAG_OR_BRANCH=$2 + NUMBER_OF_THREADS=$3 + + download_repo_github $REPO_NAME $TAG_OR_BRANCH + build_kitsune_lib_repo $REPO_NAME $NUMBER_OF_THREADS +} + +#----------------------------------------------------------------------------------------------------------------- + +echo "" +echo "###########################################################################################################" +echo "" +get_required_kitsune_lib_repo "libKitsunemimiCommon" "develop" 8 +get_required_kitsune_lib_repo "libKitsunemimiJson" "develop" 1 +get_required_kitsune_lib_repo "libKitsunemimiIni" "develop" 1 +get_required_kitsune_lib_repo "libKitsunemimiNetwork" "develop" 8 +get_required_kitsune_lib_repo "libKitsunemimiArgs" "develop" 8 +get_required_kitsune_lib_repo "libKitsunemimiConfig" "develop" 8 +echo "" +echo "###########################################################################################################" +echo "" +get_required_kitsune_lib_repo "libKitsunemimiCpu" "develop" 8 +get_required_kitsune_lib_repo "libKitsunemimiSqlite" "develop" 8 +get_required_kitsune_lib_repo "libKitsunemimiCrypto" "develop" 8 +get_required_kitsune_lib_repo "libKitsunemimiJwt" "develop" 8 +echo "" +echo "###########################################################################################################" +echo "" +get_required_kitsune_lib_repo "libKitsunemimiSakuraNetwork" "develop" 8 +get_required_kitsune_lib_repo "libKitsunemimiSakuraHardware" "develop" 8 +get_required_kitsune_lib_repo "libKitsunemimiSakuraDatabase" "develop" 8 +echo "" +echo "###########################################################################################################" +echo "" +get_required_kitsune_lib_repo "libKitsunemimiHanamiCommon" "develop" 8 +get_required_kitsune_lib_repo "libKitsunemimiHanamiDatabase" "develop" 8 +download_repo_github "libKitsunemimiHanamiMessages" "develop" +get_required_kitsune_lib_repo "libKitsunemimiHanamiNetwork" "develop" 8 +echo "" +echo "###########################################################################################################" +echo "" +get_required_kitsune_lib_repo "libAzukiHeart" "develop" 8 +get_required_kitsune_lib_repo "libMisakiGuard" "develop" 8 +echo "" +echo "###########################################################################################################" + +#----------------------------------------------------------------------------------------------------------------- + +# create build directory for KyoukoMind and go into this directory +LIB_KITSUNE_SAKURA_TREE_DIR="$BUILD_DIR/AzukiHeart" +mkdir -p $LIB_KITSUNE_SAKURA_TREE_DIR +cd $LIB_KITSUNE_SAKURA_TREE_DIR + +# build AzukiHeart with qmake +/usr/lib/x86_64-linux-gnu/qt5/bin/qmake "$PARENT_DIR/AzukiHeart/AzukiHeart.pro" -spec linux-g++ "CONFIG += optimize_full" +/usr/bin/make -j8 + +# copy build-result and include-files into the result-directory +cp "$LIB_KITSUNE_SAKURA_TREE_DIR/AzukiHeart" "$RESULT_DIR/" + +#----------------------------------------------------------------------------------------------------------------- + diff --git a/src/components/AzukiHeart/src/api/blossom_initializing.h b/src/components/AzukiHeart/src/api/blossom_initializing.h new file mode 100644 index 00000000..ecd0c0f6 --- /dev/null +++ b/src/components/AzukiHeart/src/api/blossom_initializing.h @@ -0,0 +1,83 @@ +/** + * @file blossom_initializing.h + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2022 Tobias Anker + * + * 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. + */ + +#ifndef AZUKIHEART_BLOSSOM_INITIALIZING_H +#define AZUKIHEART_BLOSSOM_INITIALIZING_H + +#include + +#include + +#include + +#include + +#include +#include +#include + +using Kitsunemimi::Hanami::HanamiMessaging; + +void +initBlossoms() +{ + HanamiMessaging* interface = HanamiMessaging::getInstance(); + + assert(interface->addBlossom("system", "get_info", new GetSystemInfo())); + assert(interface->addEndpoint("v1/system_info", + Kitsunemimi::Hanami::GET_TYPE, + Kitsunemimi::Hanami::BLOSSOM_TYPE, + "system", + "get_info")); + + assert(interface->addBlossom("threading", "get_mapping", new GetThreadMapping())); + assert(interface->addEndpoint("v1/threading", + Kitsunemimi::Hanami::GET_TYPE, + Kitsunemimi::Hanami::BLOSSOM_TYPE, + "threading", + "get_mapping")); + + assert(interface->addBlossom("measurements", "get_power_consumption", new PowerConsumption())); + assert(interface->addEndpoint("v1/power_consumption", + Kitsunemimi::Hanami::GET_TYPE, + Kitsunemimi::Hanami::BLOSSOM_TYPE, + "measurements", + "get_power_consumption")); + + assert(interface->addBlossom("measurements", "get_speed", new Speed())); + assert(interface->addEndpoint("v1/speed", + Kitsunemimi::Hanami::GET_TYPE, + Kitsunemimi::Hanami::BLOSSOM_TYPE, + "measurements", + "get_speed")); + + assert(interface->addBlossom("measurements", + "get_temperature_production", + new ThermalProduction())); + assert(interface->addEndpoint("v1/temperature_production", + Kitsunemimi::Hanami::GET_TYPE, + Kitsunemimi::Hanami::BLOSSOM_TYPE, + "measurements", + "get_temperature_production")); +} + +#endif // AZUKIHEART_BLOSSOM_INITIALIZING_H diff --git a/src/components/AzukiHeart/src/api/v1/measurements/power_consumption.cpp b/src/components/AzukiHeart/src/api/v1/measurements/power_consumption.cpp new file mode 100644 index 00000000..f21c1542 --- /dev/null +++ b/src/components/AzukiHeart/src/api/v1/measurements/power_consumption.cpp @@ -0,0 +1,57 @@ +/** + * @file power_consumption.cpp + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2022 Tobias Anker + * + * 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 "power_consumption.h" +#include +#include + +using namespace Kitsunemimi::Hanami; + +PowerConsumption::PowerConsumption() + : Kitsunemimi::Hanami::Blossom("Request the power-measurement of the CPU") +{ + //---------------------------------------------------------------------------------------------- + // output + //---------------------------------------------------------------------------------------------- + + registerOutputField("power", + SAKURA_MAP_TYPE, + "Json-object with power-measurements"); + + //---------------------------------------------------------------------------------------------- + // + //---------------------------------------------------------------------------------------------- +} + +/** + * @brief runTask + */ +bool +PowerConsumption::runTask(BlossomIO &blossomIO, + const Kitsunemimi::DataMap &, + BlossomStatus &, + Kitsunemimi::ErrorContainer &) +{ + blossomIO.output.insert("power", AzukiRoot::powerMeasuring->getJson()); + + return true; +} diff --git a/src/components/AzukiHeart/src/api/v1/measurements/power_consumption.h b/src/components/AzukiHeart/src/api/v1/measurements/power_consumption.h new file mode 100644 index 00000000..405a98de --- /dev/null +++ b/src/components/AzukiHeart/src/api/v1/measurements/power_consumption.h @@ -0,0 +1,41 @@ +/** + * @file power_consumption.h + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2022 Tobias Anker + * + * 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. + */ + +#ifndef AZUKIHEART_POWER_CONSUMPTION_H +#define AZUKIHEART_POWER_CONSUMPTION_H + +#include + +class PowerConsumption + : public Kitsunemimi::Hanami::Blossom +{ +public: + PowerConsumption(); + +protected: + bool runTask(Kitsunemimi::Hanami::BlossomIO &blossomIO, + const Kitsunemimi::DataMap &, + Kitsunemimi::Hanami::BlossomStatus &, + Kitsunemimi::ErrorContainer &); +}; + +#endif // AZUKIHEART_POWER_CONSUMPTION_H diff --git a/src/components/AzukiHeart/src/api/v1/measurements/speed.cpp b/src/components/AzukiHeart/src/api/v1/measurements/speed.cpp new file mode 100644 index 00000000..0a264e00 --- /dev/null +++ b/src/components/AzukiHeart/src/api/v1/measurements/speed.cpp @@ -0,0 +1,57 @@ +/** + * @file speed.cpp + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2022 Tobias Anker + * + * 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 "speed.h" +#include +#include + +using namespace Kitsunemimi::Hanami; + +Speed::Speed() + : Kitsunemimi::Hanami::Blossom("Request the speed of the CPU") +{ + //---------------------------------------------------------------------------------------------- + // output + //---------------------------------------------------------------------------------------------- + + registerOutputField("current_speed", + SAKURA_MAP_TYPE, + "Json-object with current-speed-measurements"); + + //---------------------------------------------------------------------------------------------- + // + //---------------------------------------------------------------------------------------------- +} + +/** + * @brief runTask + */ +bool +Speed::runTask(BlossomIO &blossomIO, + const Kitsunemimi::DataMap &, + BlossomStatus &, + Kitsunemimi::ErrorContainer &) +{ + blossomIO.output.insert("current_speed", AzukiRoot::speedMeasuring->getJson()); + + return true; +} diff --git a/src/components/AzukiHeart/src/api/v1/measurements/speed.h b/src/components/AzukiHeart/src/api/v1/measurements/speed.h new file mode 100644 index 00000000..72cdddcd --- /dev/null +++ b/src/components/AzukiHeart/src/api/v1/measurements/speed.h @@ -0,0 +1,41 @@ +/** + * @file speed.h + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2022 Tobias Anker + * + * 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. + */ + +#ifndef AZUKIHEART_SPEED_H +#define AZUKIHEART_SPEED_H + +#include + +class Speed + : public Kitsunemimi::Hanami::Blossom +{ +public: + Speed(); + +protected: + bool runTask(Kitsunemimi::Hanami::BlossomIO &blossomIO, + const Kitsunemimi::DataMap &, + Kitsunemimi::Hanami::BlossomStatus &, + Kitsunemimi::ErrorContainer &); +}; + +#endif // AZUKIHEART_SPEED_H diff --git a/src/components/AzukiHeart/src/api/v1/measurements/temperature_production.cpp b/src/components/AzukiHeart/src/api/v1/measurements/temperature_production.cpp new file mode 100644 index 00000000..39bf9361 --- /dev/null +++ b/src/components/AzukiHeart/src/api/v1/measurements/temperature_production.cpp @@ -0,0 +1,57 @@ +/** + * @file temperature_production.cpp + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2022 Tobias Anker + * + * 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 "temperature_production.h" +#include +#include + +using namespace Kitsunemimi::Hanami; + +ThermalProduction::ThermalProduction() + : Kitsunemimi::Hanami::Blossom("Request the temperature-measurement of the CPU") +{ + //---------------------------------------------------------------------------------------------- + // output + //---------------------------------------------------------------------------------------------- + + registerOutputField("temperature", + SAKURA_MAP_TYPE, + "Json-object with temperature-measurements"); + + //---------------------------------------------------------------------------------------------- + // + //---------------------------------------------------------------------------------------------- +} + +/** + * @brief runTask + */ +bool +ThermalProduction::runTask(BlossomIO &blossomIO, + const Kitsunemimi::DataMap &, + BlossomStatus &, + Kitsunemimi::ErrorContainer &) +{ + blossomIO.output.insert("temperature", AzukiRoot::temperatureMeasuring->getJson()); + + return true; +} diff --git a/src/components/AzukiHeart/src/api/v1/measurements/temperature_production.h b/src/components/AzukiHeart/src/api/v1/measurements/temperature_production.h new file mode 100644 index 00000000..f9c8b51b --- /dev/null +++ b/src/components/AzukiHeart/src/api/v1/measurements/temperature_production.h @@ -0,0 +1,41 @@ +/** + * @file temperature_production.h + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2022 Tobias Anker + * + * 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. + */ + +#ifndef AZUKIHEART_TEMPERATURE_PRODUCTION_H +#define AZUKIHEART_TEMPERATURE_PRODUCTION_H + +#include + +class ThermalProduction + : public Kitsunemimi::Hanami::Blossom +{ +public: + ThermalProduction(); + +protected: + bool runTask(Kitsunemimi::Hanami::BlossomIO &blossomIO, + const Kitsunemimi::DataMap &, + Kitsunemimi::Hanami::BlossomStatus &, + Kitsunemimi::ErrorContainer &); +}; + +#endif // AZUKIHEART_TEMPERATURE_PRODUCTION_H diff --git a/src/components/AzukiHeart/src/api/v1/system_info/get_system_info.cpp b/src/components/AzukiHeart/src/api/v1/system_info/get_system_info.cpp new file mode 100644 index 00000000..1a15dbba --- /dev/null +++ b/src/components/AzukiHeart/src/api/v1/system_info/get_system_info.cpp @@ -0,0 +1,63 @@ +/** + * @file create_token.h + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2022 Tobias Anker + * + * 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 "get_system_info.h" +#include + +#include + +#include + +using namespace Kitsunemimi::Hanami; + +GetSystemInfo::GetSystemInfo() + : Kitsunemimi::Hanami::Blossom("Get all available information of the local system.\n" + " - Topology of the cpu-resources\n" + " - Speed of the cpu") +{ + //---------------------------------------------------------------------------------------------- + // output + //---------------------------------------------------------------------------------------------- + + registerOutputField("info", + SAKURA_MAP_TYPE, + "All available information of the local system."); + + //---------------------------------------------------------------------------------------------- + // + //---------------------------------------------------------------------------------------------- +} + +/** + * @brief runTask + */ +bool +GetSystemInfo::runTask(BlossomIO &blossomIO, + const Kitsunemimi::DataMap &, + BlossomStatus &, + Kitsunemimi::ErrorContainer &) +{ + // creat output + blossomIO.output.insert("info", AzukiRoot::host->toJson()); + + return true; +} diff --git a/src/components/AzukiHeart/src/api/v1/system_info/get_system_info.h b/src/components/AzukiHeart/src/api/v1/system_info/get_system_info.h new file mode 100644 index 00000000..349b1336 --- /dev/null +++ b/src/components/AzukiHeart/src/api/v1/system_info/get_system_info.h @@ -0,0 +1,42 @@ +/** + * @file create_token.h + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2022 Tobias Anker + * + * 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. + */ + +#ifndef AZUKIHEART_GETSYSTEMINFO_H +#define AZUKIHEART_GETSYSTEMINFO_H + +#include + +class GetSystemInfo + : public Kitsunemimi::Hanami::Blossom +{ +public: + GetSystemInfo(); + +protected: + bool runTask(Kitsunemimi::Hanami::BlossomIO &blossomIO, + const Kitsunemimi::DataMap &, + Kitsunemimi::Hanami::BlossomStatus &, + Kitsunemimi::ErrorContainer &); +}; + + +#endif // AZUKIHEART_GETSYSTEMINFO_H diff --git a/src/components/AzukiHeart/src/api/v1/threading/get_thread_mapping.cpp b/src/components/AzukiHeart/src/api/v1/threading/get_thread_mapping.cpp new file mode 100644 index 00000000..c787a8bd --- /dev/null +++ b/src/components/AzukiHeart/src/api/v1/threading/get_thread_mapping.cpp @@ -0,0 +1,65 @@ +/** + * @file create_token.h + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2022 Tobias Anker + * + * 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 "get_thread_mapping.h" + +#include +#include +#include +#include + +#include +#include + +using namespace Kitsunemimi::Hanami; +using Kitsunemimi::Hanami::SupportedComponents; + +GetThreadMapping::GetThreadMapping() + : Kitsunemimi::Hanami::Blossom("Get Mapping of the all threads of all components " + "to its bound cpu-core") +{ + //---------------------------------------------------------------------------------------------- + // output + //---------------------------------------------------------------------------------------------- + + registerOutputField("thread_map", + SAKURA_MAP_TYPE, + "Map with all thread-names and its core-id as json-string."); + + //---------------------------------------------------------------------------------------------- + // + //---------------------------------------------------------------------------------------------- +} + +/** + * @brief runTask + */ +bool +GetThreadMapping::runTask(BlossomIO &blossomIO, + const Kitsunemimi::DataMap &, + BlossomStatus &, + Kitsunemimi::ErrorContainer &) +{ + blossomIO.output.insert("thread_map", AzukiRoot::threadBinder->getMapping()); + + return true; +} diff --git a/src/components/AzukiHeart/src/api/v1/threading/get_thread_mapping.h b/src/components/AzukiHeart/src/api/v1/threading/get_thread_mapping.h new file mode 100644 index 00000000..0f8130f4 --- /dev/null +++ b/src/components/AzukiHeart/src/api/v1/threading/get_thread_mapping.h @@ -0,0 +1,41 @@ +/** + * @file create_token.h + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2022 Tobias Anker + * + * 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. + */ + +#ifndef AZUKIHEART_GETTHREADMAPPING_H +#define AZUKIHEART_GETTHREADMAPPING_H + +#include + +class GetThreadMapping + : public Kitsunemimi::Hanami::Blossom +{ +public: + GetThreadMapping(); + +protected: + bool runTask(Kitsunemimi::Hanami::BlossomIO &blossomIO, + const Kitsunemimi::DataMap &, + Kitsunemimi::Hanami::BlossomStatus &, + Kitsunemimi::ErrorContainer &); +}; + +#endif // AZUKIHEART_GETTHREADMAPPING_H diff --git a/src/components/AzukiHeart/src/args.h b/src/components/AzukiHeart/src/args.h new file mode 100644 index 00000000..7a6719cf --- /dev/null +++ b/src/components/AzukiHeart/src/args.h @@ -0,0 +1,48 @@ +/** + * @file args.h + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2022 Tobias Anker + * + * 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. + */ + +#ifndef AZUKIHEART_ARGS_H +#define AZUKIHEART_ARGS_H + +#include +#include +#include + +/** + * @brief register cli-arguments + * + * @param argparser reference to argument parser + * + * @return true if successful, else false + */ +bool +registerArguments(Kitsunemimi::ArgParser* argparser, + Kitsunemimi::ErrorContainer &error) +{ + if(Kitsunemimi::Hanami::registerArguments(*argparser, error) == false) { + return false; + } + + return true; +} + +#endif // AZUKIHEART_ARGS_H diff --git a/src/components/AzukiHeart/src/azuki_root.cpp b/src/components/AzukiHeart/src/azuki_root.cpp new file mode 100644 index 00000000..dae94370 --- /dev/null +++ b/src/components/AzukiHeart/src/azuki_root.cpp @@ -0,0 +1,107 @@ +/** + * @file azuki_root.cpp + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2022 Tobias Anker + * + * 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 "azuki_root.h" + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include + +#include + +#include + +using namespace Kitsunemimi::Hanami; +using Kitsunemimi::Hanami::SupportedComponents; +using Kitsunemimi::Hanami::HanamiMessaging; + +std::string* AzukiRoot::componentToken = nullptr; +ThreadBinder* AzukiRoot::threadBinder = nullptr; +PowerMeasuring* AzukiRoot::powerMeasuring = nullptr; +SpeedMeasuring* AzukiRoot::speedMeasuring = nullptr; +TemperatureMeasuring* AzukiRoot::temperatureMeasuring = nullptr; +Kitsunemimi::Sakura::Host* AzukiRoot::host = nullptr; + +/** + * @brief constructor + */ +AzukiRoot::AzukiRoot() {} + +/** + * @brief init azuki + * + * @return true, if successful, else false + */ +bool +AzukiRoot::init() +{ + Kitsunemimi::ErrorContainer error; + initBlossoms(); + + // init internal token for access to other components + std::string token = ""; + if(Misaki::getInternalToken(token, "azuki", error) == false) + { + error.addMeesage("Failed to get internal token"); + LOG_ERROR(error); + return 1; + } + AzukiRoot::componentToken = new std::string(); + *AzukiRoot::componentToken = token; + + // init overview of all resources of the host + AzukiRoot::host = new Kitsunemimi::Sakura::Host(); + if(AzukiRoot::host->initHost(error) == false) + { + error.addMeesage("Failed read resource-information of the local host"); + LOG_ERROR(error); + return 1; + } + + // create thread-binder + AzukiRoot::threadBinder = new ThreadBinder(); + AzukiRoot::threadBinder->startThread(); + + // create power-measuring-loop + AzukiRoot::powerMeasuring = new PowerMeasuring(); + AzukiRoot::powerMeasuring->startThread(); + + // create speed-measuring-loop + AzukiRoot::speedMeasuring = new SpeedMeasuring(); + AzukiRoot::speedMeasuring->startThread(); + + // create temperature-measuring-loop + AzukiRoot::temperatureMeasuring = new TemperatureMeasuring(); + AzukiRoot::temperatureMeasuring->startThread(); + + return true; +} diff --git a/src/components/AzukiHeart/src/azuki_root.h b/src/components/AzukiHeart/src/azuki_root.h new file mode 100644 index 00000000..16d2a2cf --- /dev/null +++ b/src/components/AzukiHeart/src/azuki_root.h @@ -0,0 +1,56 @@ +/** + * @file azuki_root.h + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2022 Tobias Anker + * + * 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. + */ + +#ifndef AZUKIHEART_AZUKIROOT_H +#define AZUKIHEART_AZUKIROOT_H + +#include + +#include + +class ThreadBinder; +class SpeedMeasuring; +class PowerMeasuring; +class TemperatureMeasuring; + +namespace Kitsunemimi { +namespace Sakura { +class Host; +} +} + +class AzukiRoot +{ +public: + AzukiRoot(); + + bool init(); + + static std::string* componentToken; + static ThreadBinder* threadBinder; + static SpeedMeasuring* speedMeasuring; + static PowerMeasuring* powerMeasuring; + static TemperatureMeasuring* temperatureMeasuring; + static Kitsunemimi::Sakura::Host* host; +}; + +#endif // AZUKIHEART_AZUKIROOT_H diff --git a/src/components/AzukiHeart/src/callbacks.h b/src/components/AzukiHeart/src/callbacks.h new file mode 100644 index 00000000..51bd62ad --- /dev/null +++ b/src/components/AzukiHeart/src/callbacks.h @@ -0,0 +1,161 @@ +/** + * @file callbacks.h + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2022 Tobias Anker + * + * 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. + */ + +#ifndef AZUKIHEART_CALLBACKS_H +#define AZUKIHEART_CALLBACKS_H + +#include +#include + +#include +#include +#include +#include +#include +#include + +#include <../../libraries/libKitsunemimiHanamiMessages/protobuffers/azuki_messages.proto3.pb.h> +#include <../../libraries/libKitsunemimiHanamiMessages/message_sub_types.h> + +void streamDataCallback(void*, + Kitsunemimi::Sakura::Session*, + const void*, + const uint64_t) +{ + +} + +/** + * @brief handle errors of message which to requires a response + * + * @param msg error-message + */ +inline void +handleFail(const std::string &msg, + Kitsunemimi::Sakura::Session* session, + const uint64_t blockerId) +{ + Kitsunemimi::ErrorContainer error; + error.addMeesage(msg); + LOG_ERROR(error); + + const std::string ret = "-"; + session->sendResponse(ret.c_str(), ret.size(), blockerId, error); + return; +} + +/** + * @brief handleSetCpuSpeedRequest + * @param msg + * @param session + * @param blockerId + */ +inline void +handleSetCpuSpeedRequest(const SetCpuSpeed_Message &msg) +{ + // TODO: move the setting of the speed correctly to libKitsunemimiSakuraHardware + Kitsunemimi::ErrorContainer error; + uint64_t numberCpuThreads = 0; + uint64_t minimumSpeed = 0; + uint64_t maximumSpeed = 0; + + if(Kitsunemimi::getNumberOfCpuThreads(numberCpuThreads, error) == false) + { + + LOG_ERROR(error); + return; + } + + if(Kitsunemimi::getMinimumSpeed(minimumSpeed, 0, error) == false) + { + LOG_ERROR(error); + return; + } + if(Kitsunemimi::getMaximumSpeed(maximumSpeed, 0, error) == false) + { + LOG_ERROR(error); + return; + } + + if(msg.type() == SpeedState::MINIMUM_SPEED) + { + for(uint64_t i = 0; i < numberCpuThreads; i++) + { + Kitsunemimi::setMinimumSpeed(i, minimumSpeed, error); + Kitsunemimi::setMaximumSpeed(i, minimumSpeed, error); + } + } + else if(msg.type() == SpeedState::MAXIMUM_SPEED) + { + for(uint64_t i = 0; i < numberCpuThreads; i++) + { + Kitsunemimi::setMinimumSpeed(i, maximumSpeed, error); + Kitsunemimi::setMaximumSpeed(i, maximumSpeed, error); + } + } + else + { + for(uint64_t i = 0; i < numberCpuThreads; i++) { + Kitsunemimi::resetSpeed(i, error); + } + } + + return; +} + +/** + * @brief genericMessageCallback + * @param session + * @param data + * @param dataSize + * @param blockerId + */ +void +genericCallback(Kitsunemimi::Sakura::Session* session, + const uint32_t subtype, + void* data, + const uint64_t dataSize, + const uint64_t blockerId) +{ + switch(subtype) + { + case AZUKI_SPEED_SET_MESSAGE_TYPE: + { + SetCpuSpeed_Message msg; + if(msg.ParseFromArray(data, dataSize) == false) + { + handleFail("Receive broken set-cpu-speed-message", session, blockerId); + return; + } + + handleSetCpuSpeedRequest(msg); + } + break; + default: + handleFail("Received unknown generic message", session, blockerId); + break; + } + +} + + +#endif // AZUKIHEART_CALLBACKS_H diff --git a/src/components/AzukiHeart/src/config.h b/src/components/AzukiHeart/src/config.h new file mode 100644 index 00000000..83253c69 --- /dev/null +++ b/src/components/AzukiHeart/src/config.h @@ -0,0 +1,39 @@ +/** + * @file config.h + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2022 Tobias Anker + * + * 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. + */ + +#ifndef AZUKIHEART_CONFIG_H +#define AZUKIHEART_CONFIG_H + +#include +#include +#include + +/** + * @brief register configs + */ +void +registerConfigs(Kitsunemimi::ErrorContainer &error) +{ + Kitsunemimi::Hanami::registerBasicConfigs(error); +} + +#endif // AZUKIHEART_CONFIG_H diff --git a/src/components/AzukiHeart/src/core/power_measuring.cpp b/src/components/AzukiHeart/src/core/power_measuring.cpp new file mode 100644 index 00000000..30a27f6b --- /dev/null +++ b/src/components/AzukiHeart/src/core/power_measuring.cpp @@ -0,0 +1,79 @@ +/** + * @file power_measuring.cpp + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2022 Tobias Anker + * + * 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 "power_measuring.h" +#include +#include + +#include +#include +#include +#include + +#include + +using namespace Kitsunemimi::Hanami; + +PowerMeasuring::PowerMeasuring() + : Kitsunemimi::Thread("Azuki_EnergyMeasuring") +{ + m_valueContainer = new ValueContainer(); +} + +PowerMeasuring::~PowerMeasuring() +{ + delete m_valueContainer; +} + +Kitsunemimi::DataMap* +PowerMeasuring::getJson() +{ + return m_valueContainer->toJson(); +} + +/** + * @brief ThreadBinder::run + */ +void +PowerMeasuring::run() +{ + Kitsunemimi::ErrorContainer error; + while(m_abort == false) + { + double power = 0.0; + for(uint64_t i = 0; i < AzukiRoot::host->cpuPackages.size(); i++) { + power += AzukiRoot::host->getPackage(i)->getTotalPackagePower(); + } + + // HINT(kitsudaiki): first tests were made on a notebook. When this notebook came from + // standby, then there was a single extremly high value, which broke the outout. + // So this is only a workaround for this edge-case. I this there is no node, + // which takes more then 1MW per node and so this workaround shouldn't break any setup. + if(power > 1000000.0) { + power = 0.0f; + } + + m_valueContainer->addValue(power); + + sleep(1); + } +} diff --git a/src/components/AzukiHeart/src/core/power_measuring.h b/src/components/AzukiHeart/src/core/power_measuring.h new file mode 100644 index 00000000..55f1fc16 --- /dev/null +++ b/src/components/AzukiHeart/src/core/power_measuring.h @@ -0,0 +1,56 @@ +/** + * @file power_measuring.h + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2022 Tobias Anker + * + * 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. + */ + +#ifndef AZUKIHEART_POWER_MEASURING_H +#define AZUKIHEART_POWER_MEASURING_H + +#include + +#include +#include +#include + +namespace Kitsunemimi { +namespace Hanami { +struct RequestMessage; +} +} + +class ValueContainer; + +class PowerMeasuring + : public Kitsunemimi::Thread +{ +public: + PowerMeasuring(); + ~PowerMeasuring(); + + Kitsunemimi::DataMap* getJson(); + +protected: + void run(); + +private: + ValueContainer* m_valueContainer; +}; + +#endif // AZUKIHEART_POWER_MEASURING_H diff --git a/src/components/AzukiHeart/src/core/speed_measuring.cpp b/src/components/AzukiHeart/src/core/speed_measuring.cpp new file mode 100644 index 00000000..06eb96f6 --- /dev/null +++ b/src/components/AzukiHeart/src/core/speed_measuring.cpp @@ -0,0 +1,86 @@ +/** + * @file speed_measuring.cpp + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2022 Tobias Anker + * + * 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 "speed_measuring.h" +#include +#include + +#include + +#include +#include +#include +#include + +#include + +using namespace Kitsunemimi::Hanami; + +SpeedMeasuring::SpeedMeasuring() + : Kitsunemimi::Thread("Azuki_SpeedMeasuring") +{ + m_valueContainer = new ValueContainer(); +} + +SpeedMeasuring::~SpeedMeasuring() +{ + delete m_valueContainer; +} + +Kitsunemimi::DataMap* +SpeedMeasuring::getJson() +{ + return m_valueContainer->toJson(); +} + +/** + * @brief ThreadBinder::run + */ +void +SpeedMeasuring::run() +{ + Kitsunemimi::ErrorContainer error; + Kitsunemimi::Sakura::CpuThread* thread = nullptr; + + while(m_abort == false) + { + uint64_t currentSpeed = 0; + uint64_t numberOfValues = 0; + + for(uint64_t i = 0; i < AzukiRoot::host->cpuPackages.size(); i++) + { + for(uint64_t j = 0; j < AzukiRoot::host->getPackage(i)->cpuCores.size(); j++) + { + numberOfValues++; + thread = AzukiRoot::host->getPackage(i)->cpuCores.at(j)->cpuThreads.at(0); + currentSpeed += thread->getCurrentThreadSpeed(); + } + } + + double curSpeed = static_cast(currentSpeed) / static_cast(numberOfValues); + curSpeed /= 1000.0; // convert from KHz to MHz + + m_valueContainer->addValue(curSpeed); + + sleep(1); + } +} diff --git a/src/components/AzukiHeart/src/core/speed_measuring.h b/src/components/AzukiHeart/src/core/speed_measuring.h new file mode 100644 index 00000000..53f7af55 --- /dev/null +++ b/src/components/AzukiHeart/src/core/speed_measuring.h @@ -0,0 +1,56 @@ +/** + * @file speed_measuring.h + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2022 Tobias Anker + * + * 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. + */ + +#ifndef AZUKIHEART_SPEED_MEASURING_H +#define AZUKIHEART_SPEED_MEASURING_H + +#include + +#include +#include +#include + +namespace Kitsunemimi { +namespace Hanami { +struct RequestMessage; +} +} + +class ValueContainer; + +class SpeedMeasuring + : public Kitsunemimi::Thread +{ +public: + SpeedMeasuring(); + ~SpeedMeasuring(); + + Kitsunemimi::DataMap* getJson(); + +protected: + void run(); + +private: + ValueContainer* m_valueContainer; +}; + +#endif // AZUKIHEART_SPEED_MEASURING_H diff --git a/src/components/AzukiHeart/src/core/temperature_measuring.cpp b/src/components/AzukiHeart/src/core/temperature_measuring.cpp new file mode 100644 index 00000000..b1c4d2c9 --- /dev/null +++ b/src/components/AzukiHeart/src/core/temperature_measuring.cpp @@ -0,0 +1,68 @@ +/** + * @file temperature_measuring.cpp + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2022 Tobias Anker + * + * 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 "temperature_measuring.h" +#include +#include + +#include +#include +#include +#include + +#include + +using namespace Kitsunemimi::Hanami; + +TemperatureMeasuring::TemperatureMeasuring() + : Kitsunemimi::Thread("Azuki_TemperatureMeasuring") +{ + m_valueContainer = new ValueContainer(); +} + +TemperatureMeasuring::~TemperatureMeasuring() +{ + delete m_valueContainer; +} + +Kitsunemimi::DataMap* +TemperatureMeasuring::getJson() +{ + return m_valueContainer->toJson(); +} + +/** + * @brief ThreadBinder::run + */ +void +TemperatureMeasuring::run() +{ + + Kitsunemimi::ErrorContainer error; + while(m_abort == false) + { + const double temperature = AzukiRoot::host->getTotalTemperature(error); + m_valueContainer->addValue(temperature); + + sleep(1); + } +} diff --git a/src/components/AzukiHeart/src/core/temperature_measuring.h b/src/components/AzukiHeart/src/core/temperature_measuring.h new file mode 100644 index 00000000..caecdb29 --- /dev/null +++ b/src/components/AzukiHeart/src/core/temperature_measuring.h @@ -0,0 +1,56 @@ +/** + * @file temperature_measuring.h + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2022 Tobias Anker + * + * 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. + */ + +#ifndef AZUKIHEART_TEMPERATURE_MEASURING_H +#define AZUKIHEART_TEMPERATURE_MEASURING_H + +#include + +#include +#include +#include + +namespace Kitsunemimi { +namespace Hanami { +struct RequestMessage; +} +} + +class ValueContainer; + +class TemperatureMeasuring + : public Kitsunemimi::Thread +{ +public: + TemperatureMeasuring(); + ~TemperatureMeasuring(); + + Kitsunemimi::DataMap* getJson(); + +protected: + void run(); + +private: + ValueContainer* m_valueContainer; +}; + +#endif // AZUKIHEART_TEMPERATURE_MEASURING_H diff --git a/src/components/AzukiHeart/src/core/thread_binder.cpp b/src/components/AzukiHeart/src/core/thread_binder.cpp new file mode 100644 index 00000000..f1376812 --- /dev/null +++ b/src/components/AzukiHeart/src/core/thread_binder.cpp @@ -0,0 +1,421 @@ +/** + * @file thread_binder.cpp + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2022 Tobias Anker + * + * 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 "thread_binder.h" +#include + +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include + +using namespace Kitsunemimi::Hanami; +using Kitsunemimi::Hanami::HanamiMessagingClient; +using Kitsunemimi::Hanami::HanamiMessaging; +using Kitsunemimi::Hanami::SupportedComponents; + +ThreadBinder::ThreadBinder() + : Kitsunemimi::Thread("Azuki_ThreadBinder") +{ +} + +/** + * @brief ThreadBinder::getMappingString + * @return + */ +Kitsunemimi::DataMap* +ThreadBinder::getMapping() +{ + std::lock_guard guard(m_mapLock); + return m_completeMap.copy()->toMap(); +} + +/** + * @brief change core-ids of the threads of azuki itself + * + * @param threadNames name of the thread-type + * @param coreId is of the core (physical thread) to bind to + * + * @return true, if successful, false if core-id is out-of-range + */ +bool +ThreadBinder::changeInternalCoreIds(const std::vector &threadNames, + const std::vector coreIds) +{ + Kitsunemimi::ThreadHandler* threadHandler = Kitsunemimi::ThreadHandler::getInstance(); + for(const std::string &name : threadNames) + { + const std::vector threads = threadHandler->getThreads(name); + for(Kitsunemimi::Thread* thread : threads) + { + if(thread->bindThreadToCores(coreIds) == false) { + return false; + } + } + } + + return true; +} + +/** + * @brief tell another component a new thread-binding + * + * @param component name of the component which has to be modified + * @param request base-request for the trigger + * @param threadNames name of the thread-type + * @param coreId is of the core (physical thread) to bind to + */ +void +ThreadBinder::changeRemoteCoreIds(const std::string &component, + Kitsunemimi::Hanami::RequestMessage &request, + const std::vector &threadNames) +{ + for(const std::string &name : threadNames) + { + Kitsunemimi::ErrorContainer error; + Kitsunemimi::Hanami::ResponseMessage response; + + // set values for thread-binding + request.inputValues = "{ \"token\":\"" + + *AzukiRoot::componentToken + + "\",\"core_ids\":["; + + if(name == "CpuProcessingUnit") { + request.inputValues.append(convertCoreIdList(m_processingCoreIds)); + } else { + request.inputValues.append(convertCoreIdList(m_controlCoreIds)); + } + + request.inputValues.append("],\"thread_name\":\"" + name + "\"}"); + + // get internal client for interaction with shiori + HanamiMessagingClient* client = HanamiMessaging::getInstance()->getOutgoingClient(component); + if(client == nullptr) + { + error.addMeesage("Failed to get client to '" + component + "'"); + error.addSolution("Check if '" + component + "' is correctly configured"); + return; + } + + // trigger remote-action for thread-binding + if(client->triggerSakuraFile(response, request, error) == false) + { + LOG_ERROR(error); + } + + // check response + if(response.success == false) + { + error.addMeesage(response.responseContent); + LOG_ERROR(error); + } + } +} + + +/** + * @brief request thread-mapping of another component + * + * @param completeMap pointer for the result to attach the thread-mapping of the requested component + * @param component name of the component of which the thread-mapping should be requested + * @param request request for getting the thread-mapping of the remote-component + * @param error reference for error-output + * + * @return true, if successful, else false + */ +bool +ThreadBinder::requestComponent(Kitsunemimi::DataMap* completeMap, + const std::string &component, + const Kitsunemimi::Hanami::RequestMessage &request, + Kitsunemimi::ErrorContainer &error) +{ + // get internal client for interaction with shiori + HanamiMessagingClient* client = HanamiMessaging::getInstance()->getOutgoingClient(component); + if(client == nullptr) + { + error.addMeesage("Failed to get client to '" + component + "'"); + error.addSolution("Check if '" + component + "' is correctly configured"); + return false; + } + + Kitsunemimi::Hanami::ResponseMessage response; + if(client->triggerSakuraFile(response, request, error) == false) { + return false; + } + + // check request-result + if(response.success == false) { + return false; + } + + // parse response + Kitsunemimi::JsonItem jsonItem; + if(jsonItem.parse(response.responseContent, error) == false) { + return false; + } + + // check if response has valid content + if(jsonItem.get("thread_map").getItemContent()->isMap() == false) { + return false; + } + + // add part to the complete map + completeMap->insert(component, jsonItem.get("thread_map").getItemContent()->copy()->toMap()); + + return true; +} + +/** + * @brief request the thread-mapping of azuki itself + * + * @param completeMap pointer for the result to attach the thread-mapping of azuki + * + * @return always true + */ +bool +ThreadBinder::makeInternalRequest(Kitsunemimi::DataMap* completeMap, + Kitsunemimi::ErrorContainer &) +{ + Kitsunemimi::ThreadHandler* threadHandler = Kitsunemimi::ThreadHandler::getInstance(); + const std::vector names = threadHandler->getRegisteredNames(); + Kitsunemimi::DataMap* result = new Kitsunemimi::DataMap(); + + for(const std::string &name : names) + { + const std::vector threads = threadHandler->getThreads(name); + Kitsunemimi::DataArray* threadArray = new Kitsunemimi::DataArray(); + for(Kitsunemimi::Thread* thread : threads) + { + const std::vector coreIds = thread->getCoreIds(); + Kitsunemimi::DataArray* cores = new Kitsunemimi::DataArray(); + for(const uint64_t coreId : coreIds) { + cores->append(new Kitsunemimi::DataValue(static_cast(coreId))); + } + threadArray->append(cores); + } + result->insert(name, threadArray); + } + + completeMap->insert("azuki", result); + + return true; +} + +/** + * @brief get thread-mapping of all components + * + * @param completeMap map with mapping of all threads of all components + * @param token token for the access to the other components + * @param error reference for error-output + * + * @return true, if successful, else false + */ +bool +ThreadBinder::requestThreadMapping(Kitsunemimi::DataMap* completeMap, + const std::string &token, + Kitsunemimi::ErrorContainer &error) +{ + // create request + Kitsunemimi::Hanami::RequestMessage request; + request.id = "v1/get_thread_mapping"; + request.httpType = Kitsunemimi::Hanami::GET_TYPE; + request.inputValues = "{ \"token\" : \"" + token + "\"}"; + + SupportedComponents* scomp = SupportedComponents::getInstance(); + + //---------------------------------------------------------------------------------------------- + // request from azuki itself + if(makeInternalRequest(completeMap, error) == false) { + return false; + } + //---------------------------------------------------------------------------------------------- + if(scomp->support[Kitsunemimi::Hanami::KYOUKO] + && requestComponent(completeMap, "kyouko", request, error) == false) + { + return false; + } + //---------------------------------------------------------------------------------------------- + if(scomp->support[Kitsunemimi::Hanami::MISAKI] + && requestComponent(completeMap, "misaki", request, error) == false) + { + return false; + } + //---------------------------------------------------------------------------------------------- + if(scomp->support[Kitsunemimi::Hanami::SHIORI] + && requestComponent(completeMap, "shiori", request, error) == false) + { + return false; + } + //---------------------------------------------------------------------------------------------- + if(scomp->support[Kitsunemimi::Hanami::NOZOMI] + && requestComponent(completeMap, "nozomi", request, error) == false) + { + return false; + } + //---------------------------------------------------------------------------------------------- + if(scomp->support[Kitsunemimi::Hanami::INORI] + && requestComponent(completeMap, "inori", request, error) == false) + { + return false; + } + //---------------------------------------------------------------------------------------------- + if(requestComponent(completeMap, "torii", request, error) == false) { + return false; + } + //---------------------------------------------------------------------------------------------- + + return true; +} + +/** + * @brief fill lists with ids for the binding + * + * @param controlCoreIds reference to the list for all ids of control-processes + * @param processingCoreIds reference to the list for all ids of processing-processes + * + * @return false, if a list is empty, else true + */ +bool +ThreadBinder::fillCoreIds(std::vector &controlCoreIds, + std::vector &processingCoreIds) +{ + Kitsunemimi::Sakura::CpuCore* phyCore = nullptr; + + // control-cores + phyCore = AzukiRoot::host->cpuPackages[0]->cpuCores[0]; + for(Kitsunemimi::Sakura::CpuThread* singleThread : phyCore->cpuThreads) { + controlCoreIds.push_back(singleThread->threadId); + } + + // processing-cores + for(uint64_t i = 1; i < AzukiRoot::host->cpuPackages[0]->cpuCores.size(); i++) + { + phyCore = AzukiRoot::host->cpuPackages[0]->cpuCores[i]; + for(Kitsunemimi::Sakura::CpuThread* singleThread : phyCore->cpuThreads) { + processingCoreIds.push_back(singleThread->threadId); + } + } + + if(controlCoreIds.size() == 0) { + // TODO: error + return false; + } + + if(processingCoreIds.size() == 0) { + // TODO: error + return false; + } + + return true; +} + +/** + * @brief ThreadBinder::run + */ +void +ThreadBinder::run() +{ + if(fillCoreIds(m_controlCoreIds, m_processingCoreIds) == false) { + return; + } + + while(m_abort == false) + { + m_mapLock.lock(); + + Kitsunemimi::ErrorContainer error; + + // get thread-mapping of all components + Kitsunemimi::DataMap newMapping; + if(requestThreadMapping(&newMapping, *AzukiRoot::componentToken, error) == false) { + LOG_ERROR(error); + } + + const std::string newMappingStr = newMapping.toString(); + if(m_lastMapping != newMappingStr) + { + m_completeMap = newMapping; + // debug-output + //std::cout<<"#############################################################"<::const_iterator it; + for(it = newMapping.map.begin(); + it != newMapping.map.end(); + it++) + { + const std::vector threadNames = it->second->toMap()->getKeys(); + if(it->first == "azuki") { + changeInternalCoreIds(threadNames, m_controlCoreIds); + } else { + changeRemoteCoreIds(it->first, request, threadNames); + } + } + + m_lastMapping = newMappingStr; + } + + m_mapLock.unlock(); + + sleep(10); + } +} + +/** + * @brief convert list to a string + * + * @param coreIds list with ids + * + * @return comma-separated string with the input-values + */ +const std::string +ThreadBinder::convertCoreIdList(const std::vector coreIds) +{ + std::string result = ""; + for(uint64_t i = 0; i < coreIds.size(); i++) + { + if(i != 0) { + result.append(","); + } + result.append(std::to_string(coreIds[i])); + } + + return result; +} diff --git a/src/components/AzukiHeart/src/core/thread_binder.h b/src/components/AzukiHeart/src/core/thread_binder.h new file mode 100644 index 00000000..6df06930 --- /dev/null +++ b/src/components/AzukiHeart/src/core/thread_binder.h @@ -0,0 +1,76 @@ +/** + * @file thread_binder.h + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2022 Tobias Anker + * + * 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. + */ + +#ifndef AZUKIHEART_THREADBINDER_H +#define AZUKIHEART_THREADBINDER_H + +#include + +#include +#include +#include + +namespace Kitsunemimi { +namespace Hanami { +struct RequestMessage; +} +} + +class ThreadBinder + : public Kitsunemimi::Thread +{ +public: + ThreadBinder(); + + Kitsunemimi::DataMap* getMapping(); + +protected: + void run(); + +private: + const std::string convertCoreIdList(const std::vector coreIds); + bool changeInternalCoreIds(const std::vector &threadNames, + const std::vector coreIds); + void changeRemoteCoreIds(const std::string &component, + Kitsunemimi::Hanami::RequestMessage &request, + const std::vector &threadNames); + bool requestComponent(Kitsunemimi::DataMap *completeMap, + const std::string &component, + const Kitsunemimi::Hanami::RequestMessage &request, + Kitsunemimi::ErrorContainer &error); + bool makeInternalRequest(Kitsunemimi::DataMap *completeMap, + Kitsunemimi::ErrorContainer &); + bool requestThreadMapping(Kitsunemimi::DataMap *completeMap, + const std::string &token, + Kitsunemimi::ErrorContainer &error); + + bool fillCoreIds(std::vector &coreIds, + std::vector &processingCoreIds); + + std::mutex m_mapLock; + Kitsunemimi::DataMap m_completeMap; + std::string m_lastMapping = ""; + std::vector m_controlCoreIds; + std::vector m_processingCoreIds; +}; + +#endif // AZUKIHEART_THREADBINDER_H diff --git a/src/components/AzukiHeart/src/core/value_container.cpp b/src/components/AzukiHeart/src/core/value_container.cpp new file mode 100644 index 00000000..d7bbcb13 --- /dev/null +++ b/src/components/AzukiHeart/src/core/value_container.cpp @@ -0,0 +1,124 @@ +/** + * @file value_container.cpp + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2022 Tobias Anker + * + * 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 "value_container.h" + +/** + * @brief constructor + */ +ValueContainer::ValueContainer() +{ + m_valueSections.push_back(ValueSection(60)); + m_valueSections.push_back(ValueSection(60)); + m_valueSections.push_back(ValueSection(24)); + m_valueSections.push_back(ValueSection(365)); +} + +/** + * @brief add new value + * + * @param newValue new value to add the the list of seconds + */ +void +ValueContainer::addValue(const float newValue) +{ + addValue(newValue, 0); +} + +/** + * @brief add new value to a specific value-section + * + * @param newValue new value to add + * @param sectionId position of the value-section inside the vector + */ +void +ValueContainer::addValue(const float newValue, const uint64_t sectionId) +{ + // break-condition + if(sectionId >= m_valueSections.size()) { + return; + } + + // set new value + ValueSection* currentSection = &m_valueSections[sectionId]; + currentSection->values[currentSection->pos] = newValue; + currentSection->pos++; + + // handle overflow + if(currentSection->pos >= currentSection->values.size()) + { + currentSection->pos = 0; + + // calc overflow-value + float valueOverflow = 0.0f; + for(const float val : currentSection->values) { + valueOverflow += val; + } + valueOverflow /= currentSection->values.size(); + + addValue(valueOverflow, sectionId + 1); + } +} + +/** + * @brief convert all value-sections to a json-like object + * + * @param result data-item with all information + */ +Kitsunemimi::DataMap* +ValueContainer::toJson() +{ + Kitsunemimi::DataMap* result = new Kitsunemimi::DataMap(); + result->insert("seconds",appendSectionToJson(0)); + result->insert("minutes",appendSectionToJson(1)); + result->insert("hours",appendSectionToJson(2)); + result->insert("days",appendSectionToJson(3)); + return result; +} + +/** + * @brief convert all value of a value-section into a json-like array object + * + * @param sectionId id of the value-section, which should be converted + * + * @return data-item with all value of the selected value-section + */ +Kitsunemimi::DataArray* +ValueContainer::appendSectionToJson(const uint64_t sectionId) +{ + // precheck + if(sectionId >= m_valueSections.size()) { + return nullptr; + } + + // fill value in a array + Kitsunemimi::DataArray* valueList = new Kitsunemimi::DataArray(); + ValueSection* tempValueSection = &m_valueSections[sectionId]; + uint64_t pos = tempValueSection->pos; + for(uint64_t i = 0; i < tempValueSection->values.size(); i++) + { + valueList->append(new Kitsunemimi::DataValue(tempValueSection->values.at(pos))); + pos = (pos + 1) % tempValueSection->values.size(); + } + + return valueList; +} diff --git a/src/components/AzukiHeart/src/core/value_container.h b/src/components/AzukiHeart/src/core/value_container.h new file mode 100644 index 00000000..90df8239 --- /dev/null +++ b/src/components/AzukiHeart/src/core/value_container.h @@ -0,0 +1,58 @@ +/** + * @file value_container.h + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2022 Tobias Anker + * + * 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. + */ + +#ifndef AZUKIHEART_VALUECONTAINER_H +#define AZUKIHEART_VALUECONTAINER_H + +#include +#include +#include + +#include + +class ValueContainer +{ +public: + ValueContainer(); + + void addValue(const float newValue); + Kitsunemimi::DataMap* toJson(); + +private: + struct ValueSection + { + std::vector values; + uint64_t pos = 0; + + ValueSection(const uint64_t numberOfValues) + { + values = std::vector(numberOfValues, 0.0f); + } + }; + + std::vector m_valueSections; + + void addValue(const float newValue, const uint64_t sectionId); + Kitsunemimi::DataArray* appendSectionToJson(const uint64_t sectionId); +}; + +#endif // AZUKIHEART_VALUECONTAINER_H diff --git a/src/components/AzukiHeart/src/main.cpp b/src/components/AzukiHeart/src/main.cpp new file mode 100644 index 00000000..4038fdcd --- /dev/null +++ b/src/components/AzukiHeart/src/main.cpp @@ -0,0 +1,76 @@ +/** + * @file main.cpp + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2022 Tobias Anker + * + * 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 +#include +#include +#include +#include +#include + +#include +#include + +#include +#include + +using Kitsunemimi::Hanami::HanamiMessaging; +using Kitsunemimi::Hanami::initMain; + +int main(int argc, char *argv[]) +{ + Kitsunemimi::ErrorContainer error; + if(initMain(argc, argv, "azuki", ®isterArguments, ®isterConfigs, error) == false) + { + LOG_ERROR(error); + return 1; + } + + // init included components + Azuki::initAzukiBlossoms(); + Misaki::initMisakiBlossoms(); + + // initialize server and connections based on the config-file + const std::vector groupNames = {"misaki", "shiori", "kyouko", "torii"}; + if(HanamiMessaging::getInstance()->initialize("azuki", + groupNames, + nullptr, + streamDataCallback, + &genericCallback, + error, + true) == false) + { + LOG_ERROR(error); + return 1; + } + + // init root-object + AzukiRoot rootObj; + if(rootObj.init() == false) { + return 1; + } + + // sleep forever + std::this_thread::sleep_until(std::chrono::time_point::max()); + + return 0; +} diff --git a/src/components/KyoukoMind/CHANGELOG_old b/src/components/KyoukoMind/CHANGELOG_old new file mode 100644 index 00000000..0f67f4b0 --- /dev/null +++ b/src/components/KyoukoMind/CHANGELOG_old @@ -0,0 +1,111 @@ +# Changelog + +## [0.8.1] - 2022-07-02 + +### Changed +- updated dependencies + + +## [0.8.0] - 2022-06-28 + +### Added +- direct-io to learn and requests single values directly without tasks +- backup and restore clusters, which are saved and in sagiri +- new statemachine for cluster-tasks for a cleaner better handable structure +- prototypical not fully functioning graph-learning + +### Changed +- consistant usage of uuid's instead of names +- restructured directories +- number of outputs is not longer restricted to MNIST-tests anymore +- input-weight is not limited to 1 anymore, but it is still not the desired final state +- general optimization of the core-code +- changed activation-function in dynamic segments to log2 + +### Fixed +- handling of empty synapses was fixed + +### Removed +- gpu-code was removed for now, because it is not usable at the moment +- outdated dev-class was removed + + +## [0.7.0] - 2022-02-14 + +### Changed +- results of request-tasks are now stored on sagiri +- creating templates gets its input-values from a specific data-set from sagiri +- data-sets from sagiri are now pulled in a generic data-format to avoid converting on kyouko +- changed file-transfer from sagiri to be more faster + + +## [0.6.0] - 2022-01-09 + +### Added +- store clusters in new database +- generate basic templates and store in new databse +- usage of train-data coming from sagiri + +### Changed +- reworked api + + +## [0.5.0] - 2021-09-26 + +### Added +- readd scalable synapse-sections +- support for multiple segments +- multi-threading with worker-threads and central queue +- task-queue for asynchronous task-processing and as preparation for later multi-tenant + +### Changed +- rework of the initial input-file to be able to handle multi-segments and make bricks configurable + +### Removed +- GPU was disabled again, because it has really bad performance compared to a CPU in this setup + + +## [0.4.0] - 2021-08-01 + +### Changed +- complete reworked processing by merging the conecept with standard deep-learning +- complete reworked data-structure to be more scalable and easier to handle + +### Added +- readded GPU-support for the new structure, but still relative slow in the small MNIST-example, compared to the CPU +- new blossoms for the input of data + + +## [0.3.0] - 2021-03-29 + +### Added +- artificial cerebellum for better learing-output +- optional layered mode +- first automatic learning process (requires the layered mode at the moment) + +### Changed +- fundamental rework of the data-structures for better performance and reduce memory consumption + +### Removed +- GPU-Support is broken at the moment because of the rework (will be fixed in the next version) + + +## [0.2.0] - 2020-12-29 + +### Added +- first GPU-Support for 1 GPU with OpenCL +- new network-stack to connect to the new ToriiGateway, which provides client- and monitoring-connection + +### Changed +- complete rework of the processing-structures to make the whole process faster with less memory consumption and compatible for the GPU-support +- improved the basic concept to reduce the number of created synapses and reduce some overwriting-problems in the output + + +## [0.1.0] - 2019-08-19 + +### Added +- PoC-state: + - this is basically the PoC-state with tcp-connection to ToriiGateway/KistuneClien/MiyuMonitoring + - its primary to have a consistent tagged state which is linked against tagged versions of kitsune-repos + - at the moment it can only learn simple outputs and is still really static + diff --git a/src/components/KyoukoMind/KyoukoMind.pro b/src/components/KyoukoMind/KyoukoMind.pro new file mode 100644 index 00000000..aec565f4 --- /dev/null +++ b/src/components/KyoukoMind/KyoukoMind.pro @@ -0,0 +1,258 @@ +QT -= qt core gui + +TARGET = KyoukoMind +CONFIG += console +CONFIG += c++17 + +LIBS += -L../../libraries/libShioriArchive/src -lShioriArchive +LIBS += -L../../libraries/libShioriArchive/src/debug -lShioriArchive +LIBS += -L../../libraries/libShioriArchive/src/release -lShioriArchive +INCLUDEPATH += ../../libraries/libShioriArchive/include + +LIBS += -L../../libraries/libAzukiHeart/src -lAzukiHeart +LIBS += -L../../libraries/libAzukiHeart/src/debug -lAzukiHeart +LIBS += -L../../libraries/libAzukiHeart/src/release -lAzukiHeart +INCLUDEPATH += ../../libraries/libAzukiHeart/include + +LIBS += -L../../libraries/libMisakiGuard/src -lMisakiGuard +LIBS += -L../../libraries/libMisakiGuard/src/debug -lMisakiGuard +LIBS += -L../../libraries/libMisakiGuard/src/release -lMisakiGuard +INCLUDEPATH += ../../libraries/libMisakiGuard/include + +LIBS += -L../../libraries/libKitsunemimiHanamiNetwork/src -lKitsunemimiHanamiNetwork +LIBS += -L../../libraries/libKitsunemimiHanamiNetwork/src/debug -lKitsunemimiHanamiNetwork +LIBS += -L../../libraries/libKitsunemimiHanamiNetwork/src/release -lKitsunemimiHanamiNetwork +INCLUDEPATH += ../../libraries/libKitsunemimiHanamiNetwork/include + +LIBS += -L../../libraries/libKitsunemimiHanamiDatabase/src -lKitsunemimiHanamiDatabase +LIBS += -L../../libraries/libKitsunemimiHanamiDatabase/src/debug -lKitsunemimiHanamiDatabase +LIBS += -L../../libraries/libKitsunemimiHanamiDatabase/src/release -lKitsunemimiHanamiDatabase +INCLUDEPATH += ../../libraries/libKitsunemimiHanamiDatabase/include + +LIBS += -L../../libraries/libKitsunemimiHanamiCommon/src -lKitsunemimiHanamiCommon +LIBS += -L../../libraries/libKitsunemimiHanamiCommon/src/debug -lKitsunemimiHanamiCommon +LIBS += -L../../libraries/libKitsunemimiHanamiCommon/src/release -lKitsunemimiHanamiCommon +INCLUDEPATH += ../../libraries/libKitsunemimiHanamiCommon/include + +LIBS += -L../../libraries/libKitsunemimiHanamiSegmentParser/src -lKitsunemimiHanamiSegmentParser +LIBS += -L../../libraries/libKitsunemimiHanamiSegmentParser/src/debug -lKitsunemimiHanamiSegmentParser +LIBS += -L../../libraries/libKitsunemimiHanamiSegmentParser/src/release -lKitsunemimiHanamiSegmentParser +INCLUDEPATH += ../../libraries/libKitsunemimiHanamiSegmentParser/include + +LIBS += -L../../libraries/libKitsunemimiHanamiClusterParser/src -lKitsunemimiHanamiClusterParser +LIBS += -L../../libraries/libKitsunemimiHanamiClusterParser/src/debug -lKitsunemimiHanamiClusterParser +LIBS += -L../../libraries/libKitsunemimiHanamiClusterParser/src/release -lKitsunemimiHanamiClusterParser +INCLUDEPATH += ../../libraries/libKitsunemimiHanamiClusterParser/include + +LIBS += -L../../libraries/libKitsunemimiArgs/src -lKitsunemimiArgs +LIBS += -L../../libraries/libKitsunemimiArgs/src/debug -lKitsunemimiArgs +LIBS += -L../../libraries/libKitsunemimiArgs/src/release -lKitsunemimiArgs +INCLUDEPATH += ../../libraries/libKitsunemimiArgs/include + +LIBS += -L../../libraries/libKitsunemimiConfig/src -lKitsunemimiConfig +LIBS += -L../../libraries/libKitsunemimiConfig/src/debug -lKitsunemimiConfig +LIBS += -L../../libraries/libKitsunemimiConfig/src/release -lKitsunemimiConfig +INCLUDEPATH += ../../libraries/libKitsunemimiConfig/include + +LIBS += -L../../libraries/libKitsunemimiSakuraDatabase/src -lKitsunemimiSakuraDatabase +LIBS += -L../../libraries/libKitsunemimiSakuraDatabase/src/debug -lKitsunemimiSakuraDatabase +LIBS += -L../../libraries/libKitsunemimiSakuraDatabase/src/release -lKitsunemimiSakuraDatabase +INCLUDEPATH += ../../libraries/libKitsunemimiSakuraDatabase/include + +LIBS += -L../../libraries/libKitsunemimiSakuraNetwork/src -lKitsunemimiSakuraNetwork +LIBS += -L../../libraries/libKitsunemimiSakuraNetwork/src/debug -lKitsunemimiSakuraNetwork +LIBS += -L../../libraries/libKitsunemimiSakuraNetwork/src/release -lKitsunemimiSakuraNetwork +INCLUDEPATH += ../../libraries/libKitsunemimiSakuraNetwork/include + +LIBS += -L../../libraries/libKitsunemimiNetwork/src -lKitsunemimiNetwork +LIBS += -L../../libraries/libKitsunemimiNetwork/src/debug -lKitsunemimiNetwork +LIBS += -L../../libraries/libKitsunemimiNetwork/src/release -lKitsunemimiNetwork +INCLUDEPATH += ../../libraries/libKitsunemimiNetwork/include + +LIBS += -L../../libraries/libKitsunemimiCommon/src -lKitsunemimiCommon +LIBS += -L../../libraries/libKitsunemimiCommon/src/debug -lKitsunemimiCommon +LIBS += -L../../libraries/libKitsunemimiCommon/src/release -lKitsunemimiCommon +INCLUDEPATH += ../../libraries/libKitsunemimiCommon/include + +LIBS += -L../../libraries/libKitsunemimiSqlite/src -lKitsunemimiSqlite +LIBS += -L../../libraries/libKitsunemimiSqlite/src/debug -lKitsunemimiSqlite +LIBS += -L../../libraries/libKitsunemimiSqlite/src/release -lKitsunemimiSqlite +INCLUDEPATH += ../../libraries/libKitsunemimiSqlite/include + +LIBS += -L../../libraries/libKitsunemimiIni/src -lKitsunemimiIni +LIBS += -L../../libraries/libKitsunemimiIni/src/debug -lKitsunemimiIni +LIBS += -L../../libraries/libKitsunemimiIni/src/release -lKitsunemimiIni +INCLUDEPATH += ../../libraries/libKitsunemimiIni/include + +LIBS += -L../../libraries/libKitsunemimiJson/src -lKitsunemimiJson +LIBS += -L../../libraries/libKitsunemimiJson/src/debug -lKitsunemimiJson +LIBS += -L../../libraries/libKitsunemimiJson/src/release -lKitsunemimiJson +INCLUDEPATH += ../../libraries/libKitsunemimiJson/include + +LIBS += -L../../libraries/libKitsunemimiJwt/src -lKitsunemimiJwt +LIBS += -L../../libraries/libKitsunemimiJwt/src/debug -lKitsunemimiJwt +LIBS += -L../../libraries/libKitsunemimiJwti/src/release -lKitsunemimiJwt +INCLUDEPATH += ../../libraries/libKitsunemimiJwt/include + +LIBS += -L../../libraries/libKitsunemimiCrypto/src -lKitsunemimiCrypto +LIBS += -L../../libraries/libKitsunemimiCrypto/src/debug -lKitsunemimiCrypto +LIBS += -L../../libraries/libKitsunemimiCrypto/src/release -lKitsunemimiCrypto +INCLUDEPATH += ../../libraries/libKitsunemimiCrypto/include + +LIBS += -L../../libraries/libKitsunemimiOpencl/src -lKitsunemimiOpencl +LIBS += -L../../libraries/libKitsunemimiOpencl/src/debug -lKitsunemimiOpencl +LIBS += -L../../libraries/libKitsunemimiOpencl/src/release -lKitsunemimiOpencl +INCLUDEPATH += ../../libraries/libKitsunemimiOpencl/include + +LIBS += -lcryptopp -lssl -lsqlite3 -luuid -lcrypto -pthread -lprotobuf -lOpenCL + +INCLUDEPATH += $$PWD \ + src + +HEADERS += \ + src/api/blossom_initializing.h \ + src/api/v1/cluster/create_cluster.h \ + src/api/v1/cluster/delete_cluster.h \ + src/api/v1/cluster/list_cluster.h \ + src/api/v1/cluster/load_cluster.h \ + src/api/v1/cluster/save_cluster.h \ + src/api/v1/cluster/set_cluster_mode.h \ + src/api/v1/cluster/show_cluster.h \ + src/api/v1/task/create_task.h \ + src/api/v1/task/delete_task.h \ + src/api/v1/task/list_task.h \ + src/api/v1/task/show_task.h \ + src/api/v1/template/delete_template.h \ + src/api/v1/template/list_templates.h \ + src/api/v1/template/show_template.h \ + src/api/v1/template/upload_template.h \ + src/args.h \ + src/callbacks.h \ + src/common.h \ + src/common/defines.h \ + src/common/enums.h \ + src/common/functions.h \ + src/common/includes.h \ + src/common/structs.h \ + src/common/typedefs.h \ + src/config.h \ + src/core/callbacks.h \ + src/core/cluster/cluster.h \ + src/core/cluster/cluster_handler.h \ + src/core/cluster/cluster_init.h \ + src/core/cluster/statemachine_init.h \ + src/core/cluster/states/cycle_finish_state.h \ + src/core/cluster/states/tables/table_interpolation_state.h \ + src/core/cluster/states/tables/table_learn_forward_state.h \ + src/core/cluster/states/images/image_identify_state.h \ + src/core/cluster/states/images/image_learn_forward_state.h \ + src/core/cluster/states/snapshots/restore_cluster_state.h \ + src/core/cluster/states/snapshots/save_cluster_state.h \ + src/core/cluster/states/task_handle_state.h \ + src/core/cluster/task.h \ + src/core/processing/cpu_processing_unit.h \ + src/core/processing/processing_unit_handler.h \ + src/core/processing/segment_queue.h \ + src/core/routing_functions.h \ + src/core/segments/abstract_segment.h \ + src/core/segments/brick.h \ + src/core/segments/dynamic_segment/backpropagation.h \ + src/core/segments/dynamic_segment/dynamic_segment.h \ + src/core/segments/dynamic_segment/objects.h \ + src/core/segments/dynamic_segment/processing.h \ + src/core/segments/dynamic_segment/reduction.h \ + src/core/segments/dynamic_segment/section_update.h \ + src/core/segments/input_segment/input_segment.h \ + src/core/segments/input_segment/objects.h \ + src/core/segments/input_segment/processing.h \ + src/core/segments/output_segment/backpropagation.h \ + src/core/segments/output_segment/objects.h \ + src/core/segments/output_segment/output_segment.h \ + src/core/segments/output_segment/processing.h \ + src/core/segments/segment_meta.h \ + src/core/struct_validation.h \ + src/database/cluster_table.h \ + src/database/template_table.h \ + src/io/protobuf_messages.h \ + src/kyouko_root.h + +SOURCES += \ + src/api/v1/cluster/create_cluster.cpp \ + src/api/v1/cluster/delete_cluster.cpp \ + src/api/v1/cluster/list_cluster.cpp \ + src/api/v1/cluster/load_cluster.cpp \ + src/api/v1/cluster/save_cluster.cpp \ + src/api/v1/cluster/set_cluster_mode.cpp \ + src/api/v1/cluster/show_cluster.cpp \ + src/api/v1/task/create_task.cpp \ + src/api/v1/task/delete_task.cpp \ + src/api/v1/task/list_task.cpp \ + src/api/v1/task/show_task.cpp \ + src/api/v1/template/delete_template.cpp \ + src/api/v1/template/list_templates.cpp \ + src/api/v1/template/show_template.cpp \ + src/api/v1/template/upload_template.cpp \ + src/callbacks.cpp \ + src/core/cluster/cluster.cpp \ + src/core/cluster/cluster_handler.cpp \ + src/core/cluster/cluster_init.cpp \ + src/core/cluster/statemachine_init.cpp \ + src/core/cluster/states/cycle_finish_state.cpp \ + src/core/cluster/states/tables/table_interpolation_state.cpp \ + src/core/cluster/states/tables/table_learn_forward_state.cpp \ + src/core/cluster/states/images/image_identify_state.cpp \ + src/core/cluster/states/images/image_learn_forward_state.cpp \ + src/core/cluster/states/snapshots/restore_cluster_state.cpp \ + src/core/cluster/states/snapshots/save_cluster_state.cpp \ + src/core/cluster/states/task_handle_state.cpp \ + src/core/processing/cpu_processing_unit.cpp \ + src/core/processing/processing_unit_handler.cpp \ + src/core/processing/segment_queue.cpp \ + src/core/segments/abstract_segment.cpp \ + src/core/segments/dynamic_segment/dynamic_segment.cpp \ + src/core/segments/input_segment/input_segment.cpp \ + src/core/segments/output_segment/output_segment.cpp \ + src/core/struct_validation.cpp \ + src/database/cluster_table.cpp \ + src/database/template_table.cpp \ + src/io/protobuf_messages.cpp \ + src/kyouko_root.cpp \ + src/main.cpp + +KYOUKO_PROTO_BUFFER = ../../libraries/libKitsunemimiHanamiMessages/protobuffers/kyouko_messages.proto3 +# GPU_KERNEL = src/core/segments/dynamic_segment/gpu_kernel.cl + +OTHER_FILES += $$KYOUKO_PROTO_BUFFER # \ + # $$GPU_KERNEL + +protobuf_decl.name = protobuf headers +protobuf_decl.name = protobuf headers +protobuf_decl.input = KYOUKO_PROTO_BUFFER +protobuf_decl.output = ${QMAKE_FILE_IN_PATH}/${QMAKE_FILE_BASE}.proto3.pb.h +protobuf_decl.commands = protoc --cpp_out=${QMAKE_FILE_IN_PATH} --proto_path=${QMAKE_FILE_IN_PATH} ${QMAKE_FILE_NAME} +protobuf_decl.variable_out = HEADERS +QMAKE_EXTRA_COMPILERS += protobuf_decl + +protobuf_impl.name = protobuf sources +protobuf_impl.input = KYOUKO_PROTO_BUFFER +protobuf_impl.output = ${QMAKE_FILE_IN_PATH}/${QMAKE_FILE_BASE}.proto3.pb.cc +protobuf_impl.depends = ${QMAKE_FILE_IN_PATH}/${QMAKE_FILE_BASE}.proto3.pb.h +protobuf_impl.commands = $$escape_expand(\n) +protobuf_impl.variable_out = SOURCES +QMAKE_EXTRA_COMPILERS += protobuf_impl + +# gpu_processing.input = GPU_KERNEL +# gpu_processing.output = ${QMAKE_FILE_BASE}.h +# gpu_processing.commands = xxd -i ${QMAKE_FILE_IN} \ +# | sed 's/_________Hanami_AI_KyoukoMind_src_core_segments_dynamic_segment_//g' \ +# | sed 's/________KyoukoMind_src_core_segments_dynamic_segment_//g' \ +# | sed 's/_______KyoukoMind_src_core_segments_dynamic_segment_//g' \ +# | sed 's/______KyoukoMind_src_core_segments_dynamic_segment_//g' \ +# | sed 's/_____KyoukoMind_src_core_segments_dynamic_segment_//g' \ +# | sed 's/____KyoukoMind_src_core_segments_dynamic_segment_//g' \ +# | sed 's/___KyoukoMind_src_core_segments_dynamic_segment_//g' > ${QMAKE_FILE_BASE}.h +# gpu_processing.variable_out = HEADERS +# gpu_processing.CONFIG += target_predeps no_link + +# QMAKE_EXTRA_COMPILERS += gpu_processing + diff --git a/src/components/KyoukoMind/README.md b/src/components/KyoukoMind/README.md new file mode 100644 index 00000000..f56a238b --- /dev/null +++ b/src/components/KyoukoMind/README.md @@ -0,0 +1,15 @@ +# KyoukoMind + +## Description + +Core-component of the Hanami-AI-Project: https://github.com/kitsudaiki/Hanami-AI + +## Author + +Tobias Anker + +eMail: tobias.anker@kitsunemimi.moe + +## License + +This project is licensed under the Apache License Version 2.0 - see the [LICENSE](LICENSE) file for details diff --git a/src/components/KyoukoMind/build.sh b/src/components/KyoukoMind/build.sh new file mode 100755 index 00000000..3a9cf38a --- /dev/null +++ b/src/components/KyoukoMind/build.sh @@ -0,0 +1,146 @@ +#!/bin/bash + +# get current directory-path and the path of the parent-directory +DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )" +PARENT_DIR="$(dirname "$DIR")" +CURRENT_BRANCH=$(git rev-parse --abbrev-ref HEAD) + +# create build-directory +BUILD_DIR="$PARENT_DIR/build" +mkdir -p $BUILD_DIR + +# create directory for the final result +RESULT_DIR="$PARENT_DIR/result" +mkdir -p $RESULT_DIR + +#----------------------------------------------------------------------------------------------------------------- + +function build_kitsune_lib_repo () { + REPO_NAME=$1 + NUMBER_OF_THREADS=$2 + ADDITIONAL_CONFIGS=$3 + + # create build directory for repo and go into this directory + REPO_DIR="$BUILD_DIR/$REPO_NAME" + mkdir -p $REPO_DIR + cd $REPO_DIR + + # build repo library with qmake + /usr/lib/x86_64-linux-gnu/qt5/bin/qmake "$PARENT_DIR/$REPO_NAME/$REPO_NAME.pro" -spec linux-g++ "CONFIG += optimize_full staticlib $ADDITIONAL_CONFIGS" + /usr/bin/make -j$NUMBER_OF_THREADS + + # copy build-result and include-files into the result-directory + echo "----------------------------------------------------------------------" + echo $RESULT_DIR + cp $REPO_DIR/src/$REPO_NAME.a $RESULT_DIR/ + cp -r $PARENT_DIR/$REPO_NAME/include $RESULT_DIR/ + ls -l $RESULT_DIR/include/ + ls -l $RESULT_DIR +} + +function download_repo_github () { + REPO_NAME=$1 + TAG_OR_BRANCH=$2 + + echo "" + echo "" + echo "++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++" + echo "$REPO_NAME" + echo "++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++" + echo "Branch/Tag: $TAG_OR_BRANCH" + + # clone repo + git clone https://github.com/kitsudaiki/$REPO_NAME.git "$PARENT_DIR/$REPO_NAME" + cd "$PARENT_DIR/$REPO_NAME" + + # checkout branch + if [[ $CURRENT_BRANCH =~ ^tag.* ]] || [[ $CURRENT_BRANCH =~ ^hotfix.* ]] || [[ $CURRENT_BRANCH =~ ^v.* ]] || [[ $CURRENT_BRANCH =~ ^rolling$ ]] || [[ $CURRENT_BRANCH =~ ^staging$ ]]; then + # if a stable branch, then use the defined tag of branch + # check if defined branch even exist + BRANCH_EXIST=$(git ls-remote --heads origin $TAG_OR_BRANCH) + if [[ -z "$BRANCH_EXIST" ]]; then + echo "" + echo "-------------------------------------------------------------------------------------" + echo "Branch or tag '$TAG_OR_BRANCH' does not exist for the repository '$REPO_NAME'" + echo "-------------------------------------------------------------------------------------" + echo "" + exit 1 + fi + git checkout $TAG_OR_BRANCH + else + # if develop or feature branch, then try to checkout the feature-branch in the other repo as well + # or otherwise use the develop-branch as default + BRANCH_EXIST=$(git ls-remote --heads origin $CURRENT_BRANCH) + if [[ -n "$BRANCH_EXIST" ]]; then + git checkout $CURRENT_BRANCH + else + git checkout develop + fi + fi +} + +function get_required_kitsune_lib_repo () { + REPO_NAME=$1 + TAG_OR_BRANCH=$2 + NUMBER_OF_THREADS=$3 + + download_repo_github $REPO_NAME $TAG_OR_BRANCH + build_kitsune_lib_repo $REPO_NAME $NUMBER_OF_THREADS +} + +#----------------------------------------------------------------------------------------------------------------- + +echo "###########################################################################################################" +echo "" +get_required_kitsune_lib_repo "libKitsunemimiCommon" "develop" 8 +get_required_kitsune_lib_repo "libKitsunemimiJson" "develop" 1 +get_required_kitsune_lib_repo "libKitsunemimiIni" "develop" 1 +get_required_kitsune_lib_repo "libKitsunemimiNetwork" "develop" 8 +get_required_kitsune_lib_repo "libKitsunemimiArgs" "develop" 8 +get_required_kitsune_lib_repo "libKitsunemimiConfig" "develop" 8 +get_required_kitsune_lib_repo "libKitsunemimiObj" "develop" 8 +get_required_kitsune_lib_repo "libKitsunemimiOpencl" "develop" 8 +echo "" +echo "###########################################################################################################" +echo "" +get_required_kitsune_lib_repo "libKitsunemimiSqlite" "develop" 8 +get_required_kitsune_lib_repo "libKitsunemimiCrypto" "develop" 8 +get_required_kitsune_lib_repo "libKitsunemimiJwt" "develop" 8 +echo "" +echo "###########################################################################################################" +echo "" +get_required_kitsune_lib_repo "libKitsunemimiSakuraNetwork" "develop" 8 +get_required_kitsune_lib_repo "libKitsunemimiSakuraDatabase" "develop" 8 +echo "" +echo "###########################################################################################################" +echo "" +get_required_kitsune_lib_repo "libKitsunemimiHanamiCommon" "develop" 8 +get_required_kitsune_lib_repo "libKitsunemimiHanamiClusterParser" "develop" 1 +get_required_kitsune_lib_repo "libKitsunemimiHanamiSegmentParser" "develop" 1 +get_required_kitsune_lib_repo "libKitsunemimiHanamiDatabase" "develop" 8 +download_repo_github "libKitsunemimiHanamiMessages" "develop" +get_required_kitsune_lib_repo "libKitsunemimiHanamiNetwork" "develop" 8 +echo "" +echo "###########################################################################################################" +echo "" +get_required_kitsune_lib_repo "libAzukiHeart" "develop" 8 +get_required_kitsune_lib_repo "libMisakiGuard" "develop" 8 +get_required_kitsune_lib_repo "libShioriArchive" "develop" 8 +echo "" +echo "###########################################################################################################" + +#----------------------------------------------------------------------------------------------------------------- + +# create build directory for KyoukoMind and go into this directory +LIB_KITSUNE_SAKURA_TREE_DIR="$BUILD_DIR/KyoukoMind" +mkdir -p $LIB_KITSUNE_SAKURA_TREE_DIR +cd $LIB_KITSUNE_SAKURA_TREE_DIR + +# build KyoukoMind library with qmake +/usr/lib/x86_64-linux-gnu/qt5/bin/qmake "$PARENT_DIR/KyoukoMind/KyoukoMind.pro" -spec linux-g++ "CONFIG += optimize_full" +/usr/bin/make -j8 +# copy build-result and include-files into the result-directory +cp "$LIB_KITSUNE_SAKURA_TREE_DIR/KyoukoMind" "$RESULT_DIR/" + +#----------------------------------------------------------------------------------------------------------------- + diff --git a/src/components/KyoukoMind/src/api/blossom_initializing.h b/src/components/KyoukoMind/src/api/blossom_initializing.h new file mode 100644 index 00000000..ff414552 --- /dev/null +++ b/src/components/KyoukoMind/src/api/blossom_initializing.h @@ -0,0 +1,201 @@ +/** + * @file blossom_initializing.h + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2022 Tobias Anker + * + * 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. + */ + +#ifndef KYOUKOMIND_BLOSSOM_INITIALIZING_H +#define KYOUKOMIND_BLOSSOM_INITIALIZING_H + +#include + +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include + +using Kitsunemimi::Hanami::HanamiMessaging; + +/** + * @brief initClusterBlossoms + */ +void +initClusterBlossoms() +{ + HanamiMessaging* interface = HanamiMessaging::getInstance(); + const std::string group = "cluster"; + + assert(interface->addBlossom(group, "create", new CreateCluster())); + interface->addEndpoint("v1/cluster", + Kitsunemimi::Hanami::POST_TYPE, + Kitsunemimi::Hanami::BLOSSOM_TYPE, + group, + "create"); + + assert(interface->addBlossom(group, "show", new ShowCluster())); + interface->addEndpoint("v1/cluster", + Kitsunemimi::Hanami::GET_TYPE, + Kitsunemimi::Hanami::BLOSSOM_TYPE, + group, + "show"); + + assert(interface->addBlossom(group, "list", new ListCluster())); + interface->addEndpoint("v1/cluster/all", + Kitsunemimi::Hanami::GET_TYPE, + Kitsunemimi::Hanami::BLOSSOM_TYPE, + group, + "list"); + + assert(interface->addBlossom(group, "delete", new DeleteCluster())); + interface->addEndpoint("v1/cluster", + Kitsunemimi::Hanami::DELETE_TYPE, + Kitsunemimi::Hanami::BLOSSOM_TYPE, + group, + "delete"); + + assert(interface->addBlossom(group, "save", new SaveCluster())); + interface->addEndpoint("v1/cluster/save", + Kitsunemimi::Hanami::POST_TYPE, + Kitsunemimi::Hanami::BLOSSOM_TYPE, + group, + "save"); + + assert(interface->addBlossom(group, "load", new LoadCluster())); + interface->addEndpoint("v1/cluster/load", + Kitsunemimi::Hanami::POST_TYPE, + Kitsunemimi::Hanami::BLOSSOM_TYPE, + group, + "load"); + + assert(interface->addBlossom(group, "set_mode", new SetClusterMode())); + interface->addEndpoint("v1/cluster/set_mode", + Kitsunemimi::Hanami::PUT_TYPE, + Kitsunemimi::Hanami::BLOSSOM_TYPE, + group, + "set_mode"); +} + +/** + * @brief initTemplateBlossoms + */ +void +initTemplateBlossoms() +{ + HanamiMessaging* interface = HanamiMessaging::getInstance(); + const std::string group = "template"; + + assert(interface->addBlossom(group, "upload", new UploadTemplate())); + interface->addEndpoint("v1/template/upload", + Kitsunemimi::Hanami::POST_TYPE, + Kitsunemimi::Hanami::BLOSSOM_TYPE, + group, + "upload"); + + assert(interface->addBlossom(group, "show", new ShowTemplate())); + interface->addEndpoint("v1/template", + Kitsunemimi::Hanami::GET_TYPE, + Kitsunemimi::Hanami::BLOSSOM_TYPE, + group, + "show"); + + assert(interface->addBlossom(group, "list", new ListTemplates())); + interface->addEndpoint("v1/template/all", + Kitsunemimi::Hanami::GET_TYPE, + Kitsunemimi::Hanami::BLOSSOM_TYPE, + group, + "list"); + + assert(interface->addBlossom(group, "delete", new DeleteTemplate())); + interface->addEndpoint("v1/template", + Kitsunemimi::Hanami::DELETE_TYPE, + Kitsunemimi::Hanami::BLOSSOM_TYPE, + group, + "delete"); +} + +/** + * @brief initTaskBlossoms + */ +void +initTaskBlossoms() +{ + HanamiMessaging* interface = HanamiMessaging::getInstance(); + const std::string group = "task"; + + assert(interface->addBlossom(group, "create", new CreateTask())); + interface->addEndpoint("v1/task", + Kitsunemimi::Hanami::POST_TYPE, + Kitsunemimi::Hanami::BLOSSOM_TYPE, + group, + "create"); + + assert(interface->addBlossom(group, "show", new ShowTask())); + interface->addEndpoint("v1/task", + Kitsunemimi::Hanami::GET_TYPE, + Kitsunemimi::Hanami::BLOSSOM_TYPE, + group, + "show"); + + assert(interface->addBlossom(group, "list", new ListTask())); + interface->addEndpoint("v1/task/all", + Kitsunemimi::Hanami::GET_TYPE, + Kitsunemimi::Hanami::BLOSSOM_TYPE, + group, + "list"); + + assert(interface->addBlossom(group, "delete", new DeleteTask())); + interface->addEndpoint("v1/task", + Kitsunemimi::Hanami::DELETE_TYPE, + Kitsunemimi::Hanami::BLOSSOM_TYPE, + group, + "delete"); +} + +/** + * @brief initBlossoms + */ +void +initBlossoms() +{ + initClusterBlossoms(); + initTemplateBlossoms(); + initTaskBlossoms(); +} + +#endif // KYOUKOMIND_BLOSSOM_INITIALIZING_H diff --git a/src/components/KyoukoMind/src/api/v1/cluster/create_cluster.cpp b/src/components/KyoukoMind/src/api/v1/cluster/create_cluster.cpp new file mode 100644 index 00000000..9927f6f0 --- /dev/null +++ b/src/components/KyoukoMind/src/api/v1/cluster/create_cluster.cpp @@ -0,0 +1,431 @@ +/** + * @file create_cluster_template.cpp + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2022 Tobias Anker + * + * 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 "create_cluster.h" + +#include +#include + +#include +#include +#include + +#include +#include +#include + +#include + +using namespace Kitsunemimi::Hanami; + +CreateCluster::CreateCluster() + : Blossom("Create new cluster.") +{ + //---------------------------------------------------------------------------------------------- + // input + //---------------------------------------------------------------------------------------------- + + registerInputField("name", + SAKURA_STRING_TYPE, + true, + "Name for the new cluster."); + assert(addFieldBorder("name", 4, 256)); + assert(addFieldRegex("name", NAME_REGEX)); + + registerInputField("template", + SAKURA_STRING_TYPE, + false, + "Cluster-template as base64-string."); + + //---------------------------------------------------------------------------------------------- + // output + //---------------------------------------------------------------------------------------------- + + registerOutputField("uuid", + SAKURA_STRING_TYPE, + "UUID of the new created cluster."); + registerOutputField("name", + SAKURA_STRING_TYPE, + "Name of the new created cluster."); + registerOutputField("owner_id", + SAKURA_STRING_TYPE, + "ID of the user, who created the new cluster."); + registerOutputField("project_id", + SAKURA_STRING_TYPE, + "ID of the project, where the new cluster belongs to."); + registerOutputField("visibility", + SAKURA_STRING_TYPE, + "Visibility of the new created cluster (private, shared, public)."); + + //---------------------------------------------------------------------------------------------- + // + //---------------------------------------------------------------------------------------------- +} + +/** + * @brief runTask + */ +bool +CreateCluster::runTask(BlossomIO &blossomIO, + const Kitsunemimi::DataMap &context, + BlossomStatus &status, + Kitsunemimi::ErrorContainer &error) +{ + const std::string clusterName = blossomIO.input.get("name").getString(); + const std::string base64Template = blossomIO.input.get("template").getString(); + const Kitsunemimi::Hanami::UserContext userContext(context); + + // check if user already exist within the table + JsonItem getResult; + if(KyoukoRoot::clustersTable->getClusterByName(getResult, clusterName, userContext, error)) + { + status.errorMessage = "Cluster with name '" + clusterName + "' already exist."; + error.addMeesage(status.errorMessage); + status.statusCode = Kitsunemimi::Hanami::CONFLICT_RTYPE; + return false; + } + error._errorMessages.clear(); + error._possibleSolution.clear(); + + Kitsunemimi::Hanami::ClusterMeta parsedCluster; + if(base64Template != "") + { + // decode base64 formated template to check if valid base64-string + DataBuffer convertedTemplate; + if(Kitsunemimi::decodeBase64(convertedTemplate, base64Template) == false) + { + status.errorMessage = "Uploaded template is not a valid base64-String."; + status.statusCode = Kitsunemimi::Hanami::BAD_REQUEST_RTYPE; + error.addMeesage(status.errorMessage); + return false; + } + + // parse segment-template to validate syntax + const std::string convertedTemplateStr(static_cast(convertedTemplate.data), + convertedTemplate.usedBufferSize); + if(Kitsunemimi::Hanami::parseCluster(&parsedCluster, convertedTemplateStr, error) == false) + { + status.errorMessage = "Uploaded template is not a valid cluster-template: \n"; + status.errorMessage += error.toString(); + status.statusCode = Kitsunemimi::Hanami::BAD_REQUEST_RTYPE; + error.addMeesage(status.errorMessage); + return false; + } + } + + // convert values + JsonItem clusterData; + clusterData.insert("name", clusterName); + clusterData.insert("project_id", userContext.projectId); + clusterData.insert("owner_id", userContext.userId); + clusterData.insert("visibility", "private"); + + // add new user to table + if(KyoukoRoot::clustersTable->addCluster(clusterData, userContext, error) == false) + { + status.statusCode = Kitsunemimi::Hanami::INTERNAL_SERVER_ERROR_RTYPE; + error.addMeesage("Failed to add cluster to database"); + return false; + } + + // get new created user from database + if(KyoukoRoot::clustersTable->getClusterByName(blossomIO.output, + clusterName, + userContext, + error) == false) + { + error.addMeesage("Failed to get cluster from database by name '" + clusterName + "'"); + status.statusCode = Kitsunemimi::Hanami::INTERNAL_SERVER_ERROR_RTYPE; + return false; + } + + const std::string uuid = blossomIO.output.get("uuid").getString(); + + // create new cluster + Cluster* newCluster = new Cluster(); + if(base64Template != "") + { + if(initCluster(newCluster, uuid, parsedCluster, userContext, status, error) == false) + { + delete newCluster; + error.addMeesage("Failed to initialize cluster"); + KyoukoRoot::clustersTable->deleteCluster(uuid, userContext, error); + return false; + } + } + + KyoukoRoot::m_clusterHandler->addCluster(uuid, newCluster); + + // remove irrelevant fields + blossomIO.output.remove("owner_id"); + blossomIO.output.remove("project_id"); + blossomIO.output.remove("visibility"); + + return true; +} + +/** + * @brief CreateCluster::initCluster + * + * @param cluster pointer to the cluster, which should be initialized + * @param clusterUuid uuid of the cluster + * @param clusterDefinition definition, which describe the new cluster + * @param userContext context-object with date for the access to the database-tables + * @param status reference for status-output + * @param error reference for error-output + * + * @return true, if successful, else false + */ +bool +CreateCluster::initCluster(Cluster* cluster, + const std::string &clusterUuid, + Kitsunemimi::Hanami::ClusterMeta &clusterDefinition, + const Kitsunemimi::Hanami::UserContext &userContext, + Kitsunemimi::Hanami::BlossomStatus &status, + Kitsunemimi::ErrorContainer &error) +{ + // collect all segment-templates, which are required by the cluster-template + std::map segmentTemplates; + for(const Kitsunemimi::Hanami::SegmentMetaPtr& segmentCon : clusterDefinition.segments) + { + // skip input- and output-segments, because they are generated anyway + if(segmentCon.type == "input" + || segmentCon.type == "output") + { + continue; + } + + // get the content of the segment-template + Kitsunemimi::Hanami::SegmentMeta segmentMeta; + if(getSegmentTemplate(&segmentMeta, segmentCon.type, userContext, error) == false) + { + status.errorMessage = "Failed to get segment-template with name '" + + segmentCon.type + + "'"; + status.statusCode = Kitsunemimi::Hanami::NOT_FOUND_RTYPE; + error.addMeesage(status.errorMessage); + return false; + } + + // add segment-template to a map, which is generated later when creating the segments + // based on these templates + segmentTemplates.emplace(segmentCon.type, segmentMeta); + } + + // check if all connections within the cluster-definition are valid + if(checkConnections(clusterDefinition, segmentTemplates, status, error) == false) + { + error.addMeesage("Validation of the connections within the cluster-definition failed"); + return false; + } + + // generate and initialize the cluster based on the cluster- and segment-templates + if(cluster->init(clusterDefinition, segmentTemplates, clusterUuid) == false) + { + error.addMeesage("Failed to initialize cluster based on a template"); + status.statusCode = Kitsunemimi::Hanami::INTERNAL_SERVER_ERROR_RTYPE; + return false; + } + + return true; +} + +/** + * @brief Request a segment-template from the database and convert and parse the content + * + * @param segmentMeta pointer of the output of the result + * @param name name of the segment-template, which should be loaded from the database + * @param userContext user-context for filtering database-access + * @param error reference for internal error-output + * + * @return true, if successful, else false + */ +bool +CreateCluster::getSegmentTemplate(Kitsunemimi::Hanami::SegmentMeta* segmentMeta, + const std::string &name, + const Kitsunemimi::Hanami::UserContext &userContext, + Kitsunemimi::ErrorContainer &error) +{ + // get segment-template from database + JsonItem templateData; + if(KyoukoRoot::templateTable->getTemplateByName(templateData, + name, + userContext, + error, + true) == false) + { + return false; + } + + // decode template + std::string decodedTemplate = ""; + if(Kitsunemimi::decodeBase64(decodedTemplate, + templateData.get("data").getString()) == false) + { + // TODO: better error-messages with uuid + error.addMeesage("base64-decoding of the template failes"); + return false; + } + + // parse segment-template + if(Kitsunemimi::Hanami::parseSegment(segmentMeta, decodedTemplate, error) == false) + { + error.addMeesage("Failed to parse decoded segment-template"); + return false; + } + + return true; +} + +/** + * @brief Check all connections of the cluster-definition and initialize input- and output-segments + * + * @param clusterTemplate template with the cluster-definition + * @param segmentTemplates list with all required segment-templates for the cluster + * @param status reference for status-output in case of an error + * @param error reference for internal error-output + * + * @return true, if successful, else false + */ +bool +CreateCluster::checkConnections(Kitsunemimi::Hanami::ClusterMeta &clusterTemplate, + std::map &segmentTemplates, + Kitsunemimi::Hanami::BlossomStatus &status, + Kitsunemimi::ErrorContainer &error) +{ + for(Kitsunemimi::Hanami::SegmentMetaPtr &sourceSegmentPtr : clusterTemplate.segments) + { + for(Kitsunemimi::Hanami::ClusterConnection &conn : sourceSegmentPtr.outputs) + { + // skip output-segments, because they have not outgoing connections + if(sourceSegmentPtr.type == "output") { + continue; + } + + // get segment-meta-data of the target-segment + SegmentMetaPtr* targetSegmentPtr = clusterTemplate.getSegmentMetaPtr(conn.targetSegment); + if(targetSegmentPtr == nullptr) + { + status.errorMessage = "Segment-template with name '" + + conn.targetSegment + + "' not found."; + status.statusCode = Kitsunemimi::Hanami::NOT_FOUND_RTYPE; + error.addMeesage(status.errorMessage); + return false; + } + + // check that input is not directly connected to output + if(sourceSegmentPtr.type == "input" + && targetSegmentPtr->type == "output") + { + status.errorMessage = "Input- and Output-segments are not allowed to be directly " + "connected with each other."; + status.statusCode = Kitsunemimi::Hanami::BAD_REQUEST_RTYPE; + error.addMeesage(status.errorMessage); + return false; + } + + if(targetSegmentPtr->type != "output") + { + // get segment-meta-data of the target-segment + std::map::iterator targetSegmentIt; + targetSegmentIt = segmentTemplates.find(targetSegmentPtr->type); + if(targetSegmentIt == segmentTemplates.end()) + { + status.errorMessage = "Segment-template with name '" + + targetSegmentPtr->type + + "' not found."; + status.statusCode = Kitsunemimi::Hanami::NOT_FOUND_RTYPE; + error.addMeesage(status.errorMessage); + return false; + } + + // get target-brick of the target-segment + BrickMeta* brickMeta = targetSegmentIt->second.getBrick(conn.targetBrick); + if(brickMeta == nullptr) + { + status.errorMessage = "Segment-template with name '" + + targetSegmentPtr->type + + "' has no brick with name '" + + conn.targetBrick + + "'"; + status.statusCode = Kitsunemimi::Hanami::NOT_FOUND_RTYPE; + error.addMeesage(status.errorMessage); + return false; + } + + // if source of the connection is an input-segment, then create a new segment-meta- + // object for this input with the number of inputs depending on the target-brick + if(sourceSegmentPtr.type == "input") + { + BrickMeta inputBrick; + inputBrick.numberOfNeurons = brickMeta->numberOfNeurons; + SegmentMeta inputSegment; + inputSegment.bricks.push_back(inputBrick); + // TODO: check if name already exist + segmentTemplates.emplace(sourceSegmentPtr.name, inputSegment); + } + } + else + { + // get segment-meta-data of the source-segment + std::map::iterator sourceSegmentIt; + sourceSegmentIt = segmentTemplates.find(sourceSegmentPtr.type); + if(sourceSegmentIt == segmentTemplates.end()) + { + status.errorMessage = "Segment-template with name '" + + sourceSegmentPtr.type + + "' not found."; + status.statusCode = Kitsunemimi::Hanami::NOT_FOUND_RTYPE; + error.addMeesage(status.errorMessage); + return false; + } + + // get source-brick of the source-segment + BrickMeta* brickMeta = sourceSegmentIt->second.getBrick(conn.sourceBrick); + if(brickMeta == nullptr) + { + status.errorMessage = "Segment-template with name '" + + sourceSegmentPtr.type + + "' has no brick with name '" + + conn.sourceBrick + + "'"; + status.statusCode = Kitsunemimi::Hanami::NOT_FOUND_RTYPE; + error.addMeesage(status.errorMessage); + return false; + } + + // if target of the connection is an output-segment, then create a new segment-meta- + // object for this output with the number of outputs depending on the source-brick + Kitsunemimi::Hanami::BrickMeta outputBrick; + outputBrick.numberOfNeurons = brickMeta->numberOfNeurons; + SegmentMeta inputSegment; + inputSegment.bricks.push_back(outputBrick); + // TODO: check if name already exist + segmentTemplates.emplace(targetSegmentPtr->name, inputSegment); + } + + } + } + + return true; +} diff --git a/src/components/KyoukoMind/src/api/v1/cluster/create_cluster.h b/src/components/KyoukoMind/src/api/v1/cluster/create_cluster.h new file mode 100644 index 00000000..d5e6f3bc --- /dev/null +++ b/src/components/KyoukoMind/src/api/v1/cluster/create_cluster.h @@ -0,0 +1,68 @@ +/** + * @file create_cluster_template.h + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2022 Tobias Anker + * + * 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. + */ + +#ifndef KYOUKOMIND_CREATECLUSTER_H +#define KYOUKOMIND_CREATECLUSTER_H + +#include +#include +#include +#include + +class Cluster; + +using Kitsunemimi::Hanami::SegmentMeta; +using Kitsunemimi::Hanami::SegmentMetaPtr; +using Kitsunemimi::Hanami::BrickMeta; + +class CreateCluster + : public Kitsunemimi::Hanami::Blossom +{ +public: + CreateCluster(); + +protected: + bool runTask(Kitsunemimi::Hanami::BlossomIO &blossomIO, + const Kitsunemimi::DataMap &context, + Kitsunemimi::Hanami::BlossomStatus &status, + Kitsunemimi::ErrorContainer &error); + +private: + bool initCluster(Cluster* cluster, + const std::string &clusterUuid, + Kitsunemimi::Hanami::ClusterMeta &clusterDefinition, + const Kitsunemimi::Hanami::UserContext &userContext, + Kitsunemimi::Hanami::BlossomStatus &status, + Kitsunemimi::ErrorContainer &error); + + bool getSegmentTemplate(Kitsunemimi::Hanami::SegmentMeta* segmentMeta, + const std::string &name, + const Kitsunemimi::Hanami::UserContext &userContext, + Kitsunemimi::ErrorContainer &error); + + bool checkConnections(Kitsunemimi::Hanami::ClusterMeta &clusterTemplate, + std::map &segmentTemplates, + Kitsunemimi::Hanami::BlossomStatus &status, + Kitsunemimi::ErrorContainer &error); +}; + +#endif // KYOUKOMIND_CREATECLUSTER_H diff --git a/src/components/KyoukoMind/src/api/v1/cluster/delete_cluster.cpp b/src/components/KyoukoMind/src/api/v1/cluster/delete_cluster.cpp new file mode 100644 index 00000000..405cc5ca --- /dev/null +++ b/src/components/KyoukoMind/src/api/v1/cluster/delete_cluster.cpp @@ -0,0 +1,97 @@ +/** + * @file delete_cluster.cpp + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2022 Tobias Anker + * + * 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 "delete_cluster.h" + +#include + +#include +#include + +#include + +#include + +using namespace Kitsunemimi::Hanami; + +DeleteCluster::DeleteCluster() + : Blossom("Delete a cluster.") +{ + //---------------------------------------------------------------------------------------------- + // input + //---------------------------------------------------------------------------------------------- + + registerInputField("uuid", + SAKURA_STRING_TYPE, + true, + "UUID of the cluster."); + assert(addFieldRegex("uuid", UUID_REGEX)); + + //---------------------------------------------------------------------------------------------- + // + //---------------------------------------------------------------------------------------------- +} + +/** + * @brief runTask + */ +bool +DeleteCluster::runTask(BlossomIO &blossomIO, + const Kitsunemimi::DataMap &context, + BlossomStatus &status, + Kitsunemimi::ErrorContainer &error) +{ + const Kitsunemimi::Hanami::UserContext userContext(context); + const std::string clusterUuid = blossomIO.input.get("uuid").getString(); + + // check if user exist within the table + JsonItem getResult; + if(KyoukoRoot::clustersTable->getCluster(getResult, clusterUuid, userContext, error) == false) + { + status.errorMessage = "Cluster with uuid '" + clusterUuid + "' not found."; + status.statusCode = Kitsunemimi::Hanami::NOT_FOUND_RTYPE; + error.addMeesage(status.errorMessage); + return false; + } + + // remove data from table + if(KyoukoRoot::clustersTable->deleteCluster(clusterUuid, userContext, error) == false) + { + status.statusCode = Kitsunemimi::Hanami::INTERNAL_SERVER_ERROR_RTYPE; + error.addMeesage("Failed to delete cluster with UUID '" + clusterUuid + "' from database"); + return false; + } + + // remove internal data + const std::string uuid = getResult.get("uuid").getString(); + if(KyoukoRoot::m_clusterHandler->removeCluster(uuid) == false) + { + // should never be false, because the uuid is already defined as unique by the database + status.statusCode = Kitsunemimi::Hanami::INTERNAL_SERVER_ERROR_RTYPE; + error.addMeesage("Failed to delete cluster with UUID '" + + clusterUuid + + "' from cluster-handler"); + return false; + } + + return true; +} diff --git a/src/components/KyoukoMind/src/api/v1/cluster/delete_cluster.h b/src/components/KyoukoMind/src/api/v1/cluster/delete_cluster.h new file mode 100644 index 00000000..dc977429 --- /dev/null +++ b/src/components/KyoukoMind/src/api/v1/cluster/delete_cluster.h @@ -0,0 +1,41 @@ +/** + * @file delete_cluster.h + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2022 Tobias Anker + * + * 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. + */ + +#ifndef KYOUKOMIND_DELETECLUSTER_H +#define KYOUKOMIND_DELETECLUSTER_H + +#include + +class DeleteCluster + : public Kitsunemimi::Hanami::Blossom +{ +public: + DeleteCluster(); + +protected: + bool runTask(Kitsunemimi::Hanami::BlossomIO &blossomIO, + const Kitsunemimi::DataMap &context, + Kitsunemimi::Hanami::BlossomStatus &status, + Kitsunemimi::ErrorContainer &error); +}; + +#endif // KYOUKOMIND_DELETECLUSTER_H diff --git a/src/components/KyoukoMind/src/api/v1/cluster/list_cluster.cpp b/src/components/KyoukoMind/src/api/v1/cluster/list_cluster.cpp new file mode 100644 index 00000000..e847284c --- /dev/null +++ b/src/components/KyoukoMind/src/api/v1/cluster/list_cluster.cpp @@ -0,0 +1,80 @@ +/** + * @file list_cluster.cpp + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2022 Tobias Anker + * + * 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 "list_cluster.h" + +#include + +#include + +using namespace Kitsunemimi::Hanami; + +ListCluster::ListCluster() + : Blossom("List all visible clusters.") +{ + //---------------------------------------------------------------------------------------------- + // output + //---------------------------------------------------------------------------------------------- + + registerOutputField("header", + SAKURA_ARRAY_TYPE, + "Array with the names all columns of the table."); + assert(addFieldMatch("header", new Kitsunemimi::DataValue("[\"uuid\"," + "\"project_id\"," + "\"owner_id\"," + "\"visibility\"," + "\"name\"]"))); + registerOutputField("body", + SAKURA_ARRAY_TYPE, + "Json-string with all information of all vilible clusters."); + + //---------------------------------------------------------------------------------------------- + // + //---------------------------------------------------------------------------------------------- +} + +/** + * @brief runTask + */ +bool +ListCluster::runTask(BlossomIO &blossomIO, + const Kitsunemimi::DataMap &context, + BlossomStatus &status, + Kitsunemimi::ErrorContainer &error) +{ + const Kitsunemimi::Hanami::UserContext userContext(context); + + // get data from table + Kitsunemimi::TableItem table; + if(KyoukoRoot::clustersTable->getAllCluster(table, userContext, error) == false) + { + status.statusCode = Kitsunemimi::Hanami::INTERNAL_SERVER_ERROR_RTYPE; + error.addMeesage("Failed to get all clusters form database"); + return false; + } + + // create output + blossomIO.output.insert("header", table.getInnerHeader()); + blossomIO.output.insert("body", table.getBody()); + + return true; +} diff --git a/src/components/KyoukoMind/src/api/v1/cluster/list_cluster.h b/src/components/KyoukoMind/src/api/v1/cluster/list_cluster.h new file mode 100644 index 00000000..b3356c2b --- /dev/null +++ b/src/components/KyoukoMind/src/api/v1/cluster/list_cluster.h @@ -0,0 +1,41 @@ +/** + * @file list_cluster.h + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2022 Tobias Anker + * + * 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. + */ + +#ifndef KYOUKOMIND_SHOWCLUSTERS_H +#define KYOUKOMIND_SHOWCLUSTERS_H + +#include + +class ListCluster + : public Kitsunemimi::Hanami::Blossom +{ +public: + ListCluster(); + +protected: + bool runTask(Kitsunemimi::Hanami::BlossomIO &blossomIO, + const Kitsunemimi::DataMap &context, + Kitsunemimi::Hanami::BlossomStatus &status, + Kitsunemimi::ErrorContainer &error); +}; + +#endif // KYOUKOMIND_SHOWCLUSTERS_H diff --git a/src/components/KyoukoMind/src/api/v1/cluster/load_cluster.cpp b/src/components/KyoukoMind/src/api/v1/cluster/load_cluster.cpp new file mode 100644 index 00000000..4ae41647 --- /dev/null +++ b/src/components/KyoukoMind/src/api/v1/cluster/load_cluster.cpp @@ -0,0 +1,124 @@ +/** + * @file load_cluster.cpp + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2022 Tobias Anker + * + * 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 "load_cluster.h" +#include +#include +#include + +#include + +#include +#include +#include + +using namespace Kitsunemimi::Hanami; +using Kitsunemimi::Hanami::SupportedComponents; + +LoadCluster::LoadCluster() + : Blossom("Load a snapshot from shiori into an existing cluster and override the old data.") +{ + //---------------------------------------------------------------------------------------------- + // input + //---------------------------------------------------------------------------------------------- + + registerInputField("cluster_uuid", + SAKURA_STRING_TYPE, + true, + "UUID of the cluster, where the snapshot should be loaded into."); + assert(addFieldRegex("cluster_uuid", UUID_REGEX)); + + registerInputField("snapshot_uuid", + SAKURA_STRING_TYPE, + true, + "UUID of the snapshot, which should be loaded from shiori " + "into the cluster."); + assert(addFieldRegex("snapshot_uuid", UUID_REGEX)); + + //---------------------------------------------------------------------------------------------- + // output + //---------------------------------------------------------------------------------------------- + + registerOutputField("uuid", + SAKURA_STRING_TYPE, + "UUID of the load-task in the queue of the cluster."); + + //---------------------------------------------------------------------------------------------- + // + //---------------------------------------------------------------------------------------------- +} + +/** + * @brief runTask + */ +bool +LoadCluster::runTask(BlossomIO &blossomIO, + const Kitsunemimi::DataMap &context, + BlossomStatus &status, + Kitsunemimi::ErrorContainer &error) +{ + const std::string clusterUuid = blossomIO.input.get("cluster_uuid").getString(); + const std::string snapshotUuid = blossomIO.input.get("snapshot_uuid").getString(); + const Kitsunemimi::Hanami::UserContext userContext(context); + + // check if shiori is available + SupportedComponents* scomp = SupportedComponents::getInstance(); + if(scomp->support[Kitsunemimi::Hanami::SHIORI] == false) + { + status.statusCode = Kitsunemimi::Hanami::SERVICE_UNAVAILABLE_RTYPE; + status.errorMessage = "Shiori is not configured for Kyouko."; + error.addMeesage(status.errorMessage); + return false; + } + + // get cluster + Cluster* cluster = KyoukoRoot::m_clusterHandler->getCluster(clusterUuid); + if(cluster == nullptr) + { + status.errorMessage = "Cluster with UUID '" + clusterUuid + "' not found"; + status.statusCode = Kitsunemimi::Hanami::NOT_FOUND_RTYPE; + error.addMeesage(status.errorMessage); + return false; + } + + // get meta-infos of data-set from shiori + JsonItem parsedSnapshotInfo; + if(Shiori::getSnapshotInformation(parsedSnapshotInfo, + snapshotUuid, + userContext.token, + error) == false) + { + error.addMeesage("Failed to get information from shiori for UUID '" + snapshotUuid + "'"); + status.statusCode = Kitsunemimi::Hanami::NOT_FOUND_RTYPE; + return false; + } + + // init request-task + const std::string infoStr = parsedSnapshotInfo.toString(); + const std::string taskUuid = cluster->addClusterSnapshotRestoreTask("", + infoStr, + userContext.userId, + userContext.projectId); + blossomIO.output.insert("uuid", taskUuid); + + return true; +} diff --git a/src/components/KyoukoMind/src/api/v1/cluster/load_cluster.h b/src/components/KyoukoMind/src/api/v1/cluster/load_cluster.h new file mode 100644 index 00000000..57a9bb95 --- /dev/null +++ b/src/components/KyoukoMind/src/api/v1/cluster/load_cluster.h @@ -0,0 +1,41 @@ +/** + * @file load_cluster.h + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2022 Tobias Anker + * + * 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. + */ + +#ifndef LOADCLUSTER_H +#define LOADCLUSTER_H + +#include + +class LoadCluster + : public Kitsunemimi::Hanami::Blossom +{ +public: + LoadCluster(); + +protected: + bool runTask(Kitsunemimi::Hanami::BlossomIO &blossomIO, + const Kitsunemimi::DataMap &context, + Kitsunemimi::Hanami::BlossomStatus &status, + Kitsunemimi::ErrorContainer &error); +}; + +#endif // LOADCLUSTER_H diff --git a/src/components/KyoukoMind/src/api/v1/cluster/save_cluster.cpp b/src/components/KyoukoMind/src/api/v1/cluster/save_cluster.cpp new file mode 100644 index 00000000..d21f82ac --- /dev/null +++ b/src/components/KyoukoMind/src/api/v1/cluster/save_cluster.cpp @@ -0,0 +1,113 @@ +/** + * @file save_cluster.cpp + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2022 Tobias Anker + * + * 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 "save_cluster.h" +#include +#include +#include + +#include +#include +#include + +using namespace Kitsunemimi::Hanami; +using Kitsunemimi::Hanami::SupportedComponents; + +SaveCluster::SaveCluster() + : Blossom("Save a cluster.") +{ + //---------------------------------------------------------------------------------------------- + // input + //---------------------------------------------------------------------------------------------- + + registerInputField("name", + SAKURA_STRING_TYPE, + true, + "Name for task, which is place in the task-queue and for the new snapshot."); + assert(addFieldBorder("name", 4, 256)); + assert(addFieldRegex("name", NAME_REGEX)); + + registerInputField("cluster_uuid", + SAKURA_STRING_TYPE, + true, + "UUID of the cluster, which should be saved as new snapstho to shiori."); + assert(addFieldRegex("cluster_uuid", UUID_REGEX)); + + //---------------------------------------------------------------------------------------------- + // output + //---------------------------------------------------------------------------------------------- + + registerOutputField("uuid", + SAKURA_STRING_TYPE, + "UUID of the save-task in the queue of the cluster."); + registerOutputField("name", + SAKURA_STRING_TYPE, + "Name of the new created task and of the snapshot, " + "which should be created by the task."); + + //---------------------------------------------------------------------------------------------- + // + //---------------------------------------------------------------------------------------------- +} + +/** + * @brief runTask + */ +bool +SaveCluster::runTask(BlossomIO &blossomIO, + const Kitsunemimi::DataMap &context, + BlossomStatus &status, + Kitsunemimi::ErrorContainer &error) +{ + const std::string clusterUuid = blossomIO.input.get("cluster_uuid").getString(); + const std::string name = blossomIO.input.get("name").getString(); + const Kitsunemimi::Hanami::UserContext userContext(context); + + // check if shiori is available + SupportedComponents* scomp = SupportedComponents::getInstance(); + if(scomp->support[Kitsunemimi::Hanami::SHIORI] == false) + { + status.statusCode = Kitsunemimi::Hanami::SERVICE_UNAVAILABLE_RTYPE; + status.errorMessage = "Shiori is not configured for Kyouko."; + error.addMeesage(status.errorMessage); + return false; + } + + // get cluster + Cluster* cluster = KyoukoRoot::m_clusterHandler->getCluster(clusterUuid); + if(cluster == nullptr) + { + status.errorMessage = "Cluster with UUID '" + clusterUuid + "'not found"; + status.statusCode = Kitsunemimi::Hanami::NOT_FOUND_RTYPE; + error.addMeesage(status.errorMessage); + return false; + } + + // init request-task + const std::string taskUuid = cluster->addClusterSnapshotSaveTask(name, + userContext.userId, + userContext.projectId); + blossomIO.output.insert("uuid", taskUuid); + blossomIO.output.insert("name", name); + + return true; +} diff --git a/src/components/KyoukoMind/src/api/v1/cluster/save_cluster.h b/src/components/KyoukoMind/src/api/v1/cluster/save_cluster.h new file mode 100644 index 00000000..7b84d547 --- /dev/null +++ b/src/components/KyoukoMind/src/api/v1/cluster/save_cluster.h @@ -0,0 +1,41 @@ +/** + * @file load_cluster.h + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2022 Tobias Anker + * + * 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. + */ + +#ifndef SAVECLUSTER_H +#define SAVECLUSTER_H + +#include + +class SaveCluster + : public Kitsunemimi::Hanami::Blossom +{ +public: + SaveCluster(); + +protected: + bool runTask(Kitsunemimi::Hanami::BlossomIO &blossomIO, + const Kitsunemimi::DataMap &context, + Kitsunemimi::Hanami::BlossomStatus &status, + Kitsunemimi::ErrorContainer &error); +}; + +#endif // SAVECLUSTER_H diff --git a/src/components/KyoukoMind/src/api/v1/cluster/set_cluster_mode.cpp b/src/components/KyoukoMind/src/api/v1/cluster/set_cluster_mode.cpp new file mode 100644 index 00000000..248bedf3 --- /dev/null +++ b/src/components/KyoukoMind/src/api/v1/cluster/set_cluster_mode.cpp @@ -0,0 +1,159 @@ +/** + * @file set_cluster_mode.cpp + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2022 Tobias Anker + * + * 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 "set_cluster_mode.h" + +#include +#include +#include +#include + +#include +#include +#include + +using namespace Kitsunemimi::Hanami; +using Kitsunemimi::Hanami::HanamiMessaging; +using Kitsunemimi::Hanami::HanamiMessagingClient; + +SetClusterMode::SetClusterMode() + : Blossom("Set mode of the cluster.") +{ + //---------------------------------------------------------------------------------------------- + // input + //---------------------------------------------------------------------------------------------- + + registerInputField("uuid", + SAKURA_STRING_TYPE, + true, + "UUID of the cluster."); + assert(addFieldRegex("uuid", UUID_REGEX)); + registerInputField("connection_uuid", + SAKURA_STRING_TYPE, + false, + "UUID of the connection for input and output."); + assert(addFieldRegex("connection_uuid", UUID_REGEX)); + registerInputField("new_state", + SAKURA_STRING_TYPE, + true, + "New desired state for the cluster."); + assert(addFieldRegex("new_state", "^(TASK|DIRECT)$")); + + //---------------------------------------------------------------------------------------------- + // output + //---------------------------------------------------------------------------------------------- + + registerOutputField("uuid", + SAKURA_STRING_TYPE, + "UUID of the cluster."); + registerOutputField("name", + SAKURA_STRING_TYPE, + "Name of the cluster."); + registerOutputField("new_state", + SAKURA_STRING_TYPE, + "New desired state for the cluster."); + + //---------------------------------------------------------------------------------------------- + // + //---------------------------------------------------------------------------------------------- +} + +/** + * @brief runTask + */ +bool +SetClusterMode::runTask(BlossomIO &blossomIO, + const Kitsunemimi::DataMap &context, + BlossomStatus &status, + Kitsunemimi::ErrorContainer &error) +{ + const std::string clusterUuid = blossomIO.input.get("uuid").getString(); + const std::string connectionUuid = blossomIO.input.get("connection_uuid").getString(); + const std::string newState = blossomIO.input.get("new_state").getString(); + const Kitsunemimi::Hanami::UserContext userContext(context); + + // get data from table + if(KyoukoRoot::clustersTable->getCluster(blossomIO.output, + clusterUuid, + userContext, + error) == false) + { + status.errorMessage = "Cluster with UUID '" + clusterUuid + "' not found."; + status.statusCode = Kitsunemimi::Hanami::NOT_FOUND_RTYPE; + error.addMeesage(status.errorMessage); + return false; + } + + // get cluster + Cluster* cluster = KyoukoRoot::m_clusterHandler->getCluster(clusterUuid); + if(cluster == nullptr) + { + status.errorMessage = "Cluster with UUID '" + clusterUuid + "'not found"; + status.statusCode = Kitsunemimi::Hanami::NOT_FOUND_RTYPE; + error.addMeesage(status.errorMessage); + return false; + } + + // switch mode of cluster + if(cluster->setClusterState(newState) == false) + { + status.errorMessage = "Can not switch Cluster with uuid '" + + clusterUuid + + "' to new mode '" + + newState + + "'"; + // TODO: get exact reason, why it was not successful + status.statusCode = Kitsunemimi::Hanami::INTERNAL_SERVER_ERROR_RTYPE; + error.addMeesage(status.errorMessage); + return false; + } + + // handle client + if(newState == "DIRECT") + { + // get internal connection + HanamiMessaging* messageing = HanamiMessaging::getInstance(); + HanamiMessagingClient* client = messageing->getIncomingClient(connectionUuid); + if(client == nullptr) + { + status.errorMessage = "Connection with UUID '" + connectionUuid + "'not found"; + status.statusCode = Kitsunemimi::Hanami::NOT_FOUND_RTYPE; + error.addMeesage(status.errorMessage); + return false; + } + client->setStreamCallback(cluster, streamDataCallback); + cluster->msgClient = client; + } + else + { + cluster->msgClient = nullptr; + } + + blossomIO.output.insert("new_state", newState); + + // remove irrelevant fields + blossomIO.output.remove("owner_id"); + blossomIO.output.remove("project_id"); + blossomIO.output.remove("visibility"); + + return true; +} diff --git a/src/components/KyoukoMind/src/api/v1/cluster/set_cluster_mode.h b/src/components/KyoukoMind/src/api/v1/cluster/set_cluster_mode.h new file mode 100644 index 00000000..d8a201fe --- /dev/null +++ b/src/components/KyoukoMind/src/api/v1/cluster/set_cluster_mode.h @@ -0,0 +1,41 @@ +/** + * @file set_cluster_mode.h + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2022 Tobias Anker + * + * 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. + */ + +#ifndef KYOUKOMIND_SET_CLUSTER_MODE_H +#define KYOUKOMIND_SET_CLUSTER_MODE_H + +#include + +class SetClusterMode + : public Kitsunemimi::Hanami::Blossom +{ +public: + SetClusterMode(); + +protected: + bool runTask(Kitsunemimi::Hanami::BlossomIO &blossomIO, + const Kitsunemimi::DataMap &context, + Kitsunemimi::Hanami::BlossomStatus &status, + Kitsunemimi::ErrorContainer &error); +}; + +#endif // KYOUKOMIND_SET_CLUSTER_MODE_H diff --git a/src/components/KyoukoMind/src/api/v1/cluster/show_cluster.cpp b/src/components/KyoukoMind/src/api/v1/cluster/show_cluster.cpp new file mode 100644 index 00000000..51fed5b0 --- /dev/null +++ b/src/components/KyoukoMind/src/api/v1/cluster/show_cluster.cpp @@ -0,0 +1,96 @@ +/** + * @file show_cluster.cpp + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2022 Tobias Anker + * + * 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 "show_cluster.h" + +#include + +#include + +#include + +using namespace Kitsunemimi::Hanami; + +ShowCluster::ShowCluster() + : Blossom("Show information of a specific cluster.") +{ + //---------------------------------------------------------------------------------------------- + // input + //---------------------------------------------------------------------------------------------- + + registerInputField("uuid", + SAKURA_STRING_TYPE, + true, + "uuid of the cluster."); + assert(addFieldRegex("uuid", UUID_REGEX)); + + //---------------------------------------------------------------------------------------------- + // output + //---------------------------------------------------------------------------------------------- + + registerOutputField("uuid", + SAKURA_STRING_TYPE, + "UUID of the cluster."); + registerOutputField("name", + SAKURA_STRING_TYPE, + "Name of the cluster."); + registerOutputField("owner_id", + SAKURA_STRING_TYPE, + "ID of the user, who created the cluster."); + registerOutputField("project_id", + SAKURA_STRING_TYPE, + "ID of the project, where the cluster belongs to."); + registerOutputField("visibility", + SAKURA_STRING_TYPE, + "Visibility of the cluster (private, shared, public)."); + + //---------------------------------------------------------------------------------------------- + // + //---------------------------------------------------------------------------------------------- +} + +/** + * @brief runTask + */ +bool +ShowCluster::runTask(BlossomIO &blossomIO, + const Kitsunemimi::DataMap &context, + BlossomStatus &status, + Kitsunemimi::ErrorContainer &error) +{ + const Kitsunemimi::Hanami::UserContext userContext(context); + const std::string clusterUuid = blossomIO.input.get("uuid").getString(); + + // get data from table + if(KyoukoRoot::clustersTable->getCluster(blossomIO.output, + clusterUuid, + userContext, + error) == false) + { + status.errorMessage = "Cluster with UUID '" + clusterUuid + "' not found."; + status.statusCode = Kitsunemimi::Hanami::NOT_FOUND_RTYPE; + error.addMeesage(status.errorMessage); + return false; + } + + return true; +} diff --git a/src/components/KyoukoMind/src/api/v1/cluster/show_cluster.h b/src/components/KyoukoMind/src/api/v1/cluster/show_cluster.h new file mode 100644 index 00000000..4bef4868 --- /dev/null +++ b/src/components/KyoukoMind/src/api/v1/cluster/show_cluster.h @@ -0,0 +1,41 @@ +/** + * @file show_cluster.h + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2022 Tobias Anker + * + * 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. + */ + +#ifndef KYOUKOMIND_SHOWCLUSTER_H +#define KYOUKOMIND_SHOWCLUSTER_H + +#include + +class ShowCluster + : public Kitsunemimi::Hanami::Blossom +{ +public: + ShowCluster(); + +protected: + bool runTask(Kitsunemimi::Hanami::BlossomIO &blossomIO, + const Kitsunemimi::DataMap &context, + Kitsunemimi::Hanami::BlossomStatus &status, + Kitsunemimi::ErrorContainer &error); +}; + +#endif // KYOUKOMIND_SHOWCLUSTER_H diff --git a/src/components/KyoukoMind/src/api/v1/task/create_task.cpp b/src/components/KyoukoMind/src/api/v1/task/create_task.cpp new file mode 100644 index 00000000..419ee8de --- /dev/null +++ b/src/components/KyoukoMind/src/api/v1/task/create_task.cpp @@ -0,0 +1,349 @@ +/** + * @file create_image_learn_task.cpp + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2022 Tobias Anker + * + * 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 "create_task.h" +#include + +#include + +#include +#include +#include +#include + +#include +#include +#include + +#include + +using namespace Kitsunemimi::Hanami; +using Kitsunemimi::Hanami::SupportedComponents; + +CreateTask::CreateTask() + : Blossom("Add new task to the task-queue of a cluster.") +{ + //---------------------------------------------------------------------------------------------- + // input + //---------------------------------------------------------------------------------------------- + + registerInputField("name", + SAKURA_STRING_TYPE, + true, + "Name for the new task for better identification."); + assert(addFieldBorder("name", 4, 256)); + assert(addFieldRegex("name", NAME_REGEX)); + + registerInputField("type", + SAKURA_STRING_TYPE, + true, + "UUID of the data-set with the input, which coming from shiori."); + assert(addFieldRegex("type", "^(learn|request)$")); + + registerInputField("cluster_uuid", + SAKURA_STRING_TYPE, + true, + "UUID of the cluster, which should process the request"); + assert(addFieldRegex("cluster_uuid", UUID_REGEX)); + + registerInputField("data_set_uuid", + SAKURA_STRING_TYPE, + true, + "UUID of the data-set with the input, which coming from shiori."); + assert(addFieldRegex("data_set_uuid", UUID_REGEX)); + + //---------------------------------------------------------------------------------------------- + // output + //---------------------------------------------------------------------------------------------- + + registerOutputField("uuid", + SAKURA_STRING_TYPE, + "UUID of the new created task."); + registerOutputField("name", + SAKURA_STRING_TYPE, + "Name of the new created task."); + + //---------------------------------------------------------------------------------------------- + // + //---------------------------------------------------------------------------------------------- +} + +/** + * @brief runTask + */ +bool +CreateTask::runTask(BlossomIO &blossomIO, + const Kitsunemimi::DataMap &context, + BlossomStatus &status, + Kitsunemimi::ErrorContainer &error) +{ + const std::string name = blossomIO.input.get("name").getString(); + const std::string clusterUuid = blossomIO.input.get("cluster_uuid").getString(); + const std::string dataSetUuid = blossomIO.input.get("data_set_uuid").getString(); + const std::string taskType = blossomIO.input.get("type").getString(); + const Kitsunemimi::Hanami::UserContext userContext(context); + + // check if shiori is available + SupportedComponents* scomp = SupportedComponents::getInstance(); + if(scomp->support[Kitsunemimi::Hanami::SHIORI] == false) + { + status.statusCode = Kitsunemimi::Hanami::SERVICE_UNAVAILABLE_RTYPE; + status.errorMessage = "Shiori is not configured for Kyouko."; + error.addMeesage(status.errorMessage); + return false; + } + + // get cluster + Cluster* cluster = KyoukoRoot::m_clusterHandler->getCluster(clusterUuid); + if(cluster == nullptr) + { + status.errorMessage = "Cluster with UUID '" + clusterUuid + "'not found"; + status.statusCode = Kitsunemimi::Hanami::NOT_FOUND_RTYPE; + error.addMeesage(status.errorMessage); + return false; + } + + // get meta-infos of data-set from shiori + JsonItem dataSetInfo; + if(Shiori::getDataSetInformation(dataSetInfo, dataSetUuid, userContext.token, error) == false) + { + error.addMeesage("Failed to get information from shiori for UUID '" + dataSetUuid + "'"); + // TODO: add status-error from response from shiori + status.statusCode = Kitsunemimi::Hanami::UNAUTHORIZED_RTYPE; + return false; + } + + // create task + std::string taskUuid = ""; + if(dataSetInfo.get("type").getString() == "mnist") + { + imageTask(taskUuid, + name, + taskType, + dataSetUuid, + userContext, + cluster, + dataSetInfo, + status, + error); + } + else if(dataSetInfo.get("type").getString() == "csv") + { + tableTask(taskUuid, + name, + taskType, + dataSetUuid, + userContext, + cluster, + dataSetInfo, + status, + error); + } + else + { + status.errorMessage = "Invalid dataset-type '" + + dataSetInfo.get("type").getString() + + "' given for to create new task"; + status.statusCode = Kitsunemimi::Hanami::BAD_REQUEST_RTYPE; + error.addMeesage(status.errorMessage); + return false; + } + + // create output + blossomIO.output.insert("uuid", taskUuid); + blossomIO.output.insert("name", name); + + return true; +} + +/** + * @brief add image-task to queue of cluster + * + * @param taskUuid reference for the output of the uuid of the new task + * @param name name of the task + * @param taskType type of the task (learn or request) + * @param dataSetUuid uuid of the base-dataset for the task + * @param userContext user-context + * @param cluster pointer to the cluster, which should process the new task + * @param dataSetInfo info-object with information about the dataset + * @param status reference for status-output in error-case + * @param error reference for error-output + * + * @return true, if successful, else false + */ +bool +CreateTask::imageTask(std::string &taskUuid, + const std::string &name, + const std::string &taskType, + const std::string &dataSetUuid, + const Kitsunemimi::Hanami::UserContext &userContext, + Cluster* cluster, + JsonItem &dataSetInfo, + BlossomStatus &status, + Kitsunemimi::ErrorContainer &error) +{ + // get input-data + DataBuffer* dataSetBuffer = Shiori::getDatasetData(userContext.token, dataSetUuid, "", error); + if(dataSetBuffer == nullptr) + { + error.addMeesage("Failed to get data from shiori for dataset with UUID '" + + dataSetUuid + + "'"); + status.statusCode = Kitsunemimi::Hanami::INTERNAL_SERVER_ERROR_RTYPE; + return false; + } + + // get relevant information from output + const uint64_t numberOfInputs = dataSetInfo.get("inputs").getLong(); + const uint64_t numberOfOutputs = dataSetInfo.get("outputs").getLong(); + const uint64_t numberOfLines = dataSetInfo.get("lines").getLong(); + + float* floatData = static_cast(dataSetBuffer->data); + if(taskType == "learn") + { + taskUuid = cluster->addImageLearnTask(name, + userContext.userId, + userContext.projectId, + floatData, + numberOfInputs, + numberOfOutputs, + numberOfLines); + } + else + { + taskUuid = cluster->addImageRequestTask(name, + userContext.userId, + userContext.projectId, + floatData, + numberOfInputs, + numberOfOutputs, + numberOfLines); + } + + return true; +} + +/** + * @brief add table-task to queue of cluster + * + * @param taskUuid reference for the output of the uuid of the new task + * @param name name of the task + * @param taskType type of the task (learn or request) + * @param dataSetUuid uuid of the base-dataset for the task + * @param userContext user-context + * @param cluster pointer to the cluster, which should process the new task + * @param dataSetInfo info-object with information about the dataset + * @param status reference for status-output in error-case + * @param error reference for error-output + * + * @return true, if successful, else false + */ +bool +CreateTask::tableTask(std::string &taskUuid, + const std::string &name, + const std::string &taskType, + const std::string &dataSetUuid, + const Kitsunemimi::Hanami::UserContext &userContext, + Cluster* cluster, + JsonItem &dataSetInfo, + BlossomStatus &status, + Kitsunemimi::ErrorContainer &error) +{ + // init request-task + InputSegment* inSegment = cluster->inputSegments.begin()->second; + OutputSegment* outSegment = cluster->outputSegments.begin()->second; + const uint64_t numberOfInputs = inSegment->segmentHeader->inputs.count; + const uint64_t numberOfOutputs = outSegment->segmentHeader->outputs.count; + const uint64_t numberOfLines = dataSetInfo.get("lines").getLong(); + + // get input-data + const std::string inputColumnName = inSegment->getName(); + DataBuffer* inputBuffer = Shiori::getDatasetData(userContext.token, + dataSetUuid, + inputColumnName, + error); + if(inputBuffer == nullptr) + { + status.statusCode = Kitsunemimi::Hanami::INTERNAL_SERVER_ERROR_RTYPE; + error.addMeesage("Got no data from shiori for dataset with UUID '" + + dataSetUuid + + "' and column with name '" + + inputColumnName + + "'"); + delete inputBuffer; + return false; + } + + if(taskType == "request") + { + taskUuid = cluster->addTableRequestTask(name, + userContext.userId, + userContext.projectId, + static_cast(inputBuffer->data), + numberOfInputs, + numberOfOutputs, + numberOfLines - numberOfInputs); + + } + else + { + // get output-data + const std::string outputColumnName = outSegment->getName(); + DataBuffer* outputBuffer = Shiori::getDatasetData(userContext.token, + dataSetUuid, + outputColumnName, + error); + if(outputBuffer == nullptr) + { + status.statusCode = Kitsunemimi::Hanami::INTERNAL_SERVER_ERROR_RTYPE; + error.addMeesage("Got no data from shiori for dataset with UUID '" + + dataSetUuid + + "' and column with name '" + + outputColumnName + + "'"); + delete inputBuffer; + delete outputBuffer; + return false; + } + + // create task + const uint64_t numberOfLines = dataSetInfo.get("lines").getLong(); + taskUuid = cluster->addTableLearnTask(name, + userContext.userId, + userContext.projectId, + static_cast(inputBuffer->data), + static_cast(outputBuffer->data), + numberOfInputs, + numberOfOutputs, + numberOfLines - numberOfInputs); + + // clear leftover of the buffer + outputBuffer->data = nullptr; + delete outputBuffer; + } + + // clear leftover of the buffer + inputBuffer->data = nullptr; + delete inputBuffer; + + return true; +} diff --git a/src/components/KyoukoMind/src/api/v1/task/create_task.h b/src/components/KyoukoMind/src/api/v1/task/create_task.h new file mode 100644 index 00000000..23987078 --- /dev/null +++ b/src/components/KyoukoMind/src/api/v1/task/create_task.h @@ -0,0 +1,65 @@ +/** + * @file create_image_learn_task.h + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2022 Tobias Anker + * + * 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. + */ + +#ifndef KYOUKOMIND_CREATE_IMAGE_LEARNTASK_H +#define KYOUKOMIND_CREATE_IMAGE_LEARNTASK_H + +#include +#include + +class Cluster; + +class CreateTask + : public Kitsunemimi::Hanami::Blossom +{ +public: + CreateTask(); + +protected: + bool runTask(Kitsunemimi::Hanami::BlossomIO &blossomIO, + const Kitsunemimi::DataMap &context, + Kitsunemimi::Hanami::BlossomStatus &status, + Kitsunemimi::ErrorContainer &error); + +private: + bool imageTask(std::string &taskUuid, + const std::string &name, + const std::string &taskType, + const std::string &dataSetUuid, + const Kitsunemimi::Hanami::UserContext &userContext, + Cluster* cluster, + JsonItem &dataSetInfo, + Kitsunemimi::Hanami::BlossomStatus &status, + Kitsunemimi::ErrorContainer &error); + + bool tableTask(std::string &taskUuid, + const std::string &name, + const std::string &taskType, + const std::string &dataSetUuid, + const Kitsunemimi::Hanami::UserContext &userContext, + Cluster* cluster, + JsonItem &dataSetInfo, + Kitsunemimi::Hanami::BlossomStatus &status, + Kitsunemimi::ErrorContainer &error); +}; + +#endif // KYOUKOMIND_CREATE_IMAGE_LEARNTASK_H diff --git a/src/components/KyoukoMind/src/api/v1/task/delete_task.cpp b/src/components/KyoukoMind/src/api/v1/task/delete_task.cpp new file mode 100644 index 00000000..0e4fe834 --- /dev/null +++ b/src/components/KyoukoMind/src/api/v1/task/delete_task.cpp @@ -0,0 +1,89 @@ +/** + * @file delete_task.cpp + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2022 Tobias Anker + * + * 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 "delete_task.h" + +#include +#include +#include + +using namespace Kitsunemimi::Hanami; + +DeleteTask::DeleteTask() + : Blossom("Delete a task or abort a task, if it is actually running.") +{ + //---------------------------------------------------------------------------------------------- + // input + //---------------------------------------------------------------------------------------------- + + registerInputField("uuid", + SAKURA_STRING_TYPE, + true, + "UUID of the task, which should be deleted"); + assert(addFieldRegex("uuid", UUID_REGEX)); + + registerInputField("cluster_uuid", + SAKURA_STRING_TYPE, + true, + "UUID of the cluster, which contains the task in its queue"); + assert(addFieldRegex("cluster_uuid", UUID_REGEX)); + + //---------------------------------------------------------------------------------------------- + // + //---------------------------------------------------------------------------------------------- +} + +/** + * @brief runTask + */ +bool +DeleteTask::runTask(BlossomIO &blossomIO, + const Kitsunemimi::DataMap &context, + BlossomStatus &status, + Kitsunemimi::ErrorContainer &error) +{ + const Kitsunemimi::Hanami::UserContext userContext(context); + const std::string taskUuid = blossomIO.input.get("uuid").getString(); + const std::string clusterUuid = blossomIO.input.get("cluster_uuid").getString(); + + // get cluster + Cluster* cluster = KyoukoRoot::m_clusterHandler->getCluster(clusterUuid); + if(cluster == nullptr) + { + status.errorMessage = "Cluster with UUID '" + clusterUuid + "'not found"; + status.statusCode = Kitsunemimi::Hanami::NOT_FOUND_RTYPE; + error.addMeesage(status.errorMessage); + return false; + } + + // delete task + if(cluster->removeTask(taskUuid) == false) + { + status.errorMessage = "Task with UUID '" + clusterUuid + "'not found in " + "Cluster with UUID '" + clusterUuid; + status.statusCode = Kitsunemimi::Hanami::NOT_FOUND_RTYPE; + error.addMeesage(status.errorMessage); + return false; + } + + return true; +} diff --git a/src/components/KyoukoMind/src/api/v1/task/delete_task.h b/src/components/KyoukoMind/src/api/v1/task/delete_task.h new file mode 100644 index 00000000..9293136e --- /dev/null +++ b/src/components/KyoukoMind/src/api/v1/task/delete_task.h @@ -0,0 +1,41 @@ +/** + * @file delete_task.h + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2022 Tobias Anker + * + * 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. + */ + +#ifndef KYOUKOMIND_DELETETASK_H +#define KYOUKOMIND_DELETETASK_H + +#include + +class DeleteTask + : public Kitsunemimi::Hanami::Blossom +{ +public: + DeleteTask(); + +protected: + bool runTask(Kitsunemimi::Hanami::BlossomIO &blossomIO, + const Kitsunemimi::DataMap &context, + Kitsunemimi::Hanami::BlossomStatus &status, + Kitsunemimi::ErrorContainer &error); +}; + +#endif // KYOUKOMIND_DELETETASK_H diff --git a/src/components/KyoukoMind/src/api/v1/task/list_task.cpp b/src/components/KyoukoMind/src/api/v1/task/list_task.cpp new file mode 100644 index 00000000..bee60c35 --- /dev/null +++ b/src/components/KyoukoMind/src/api/v1/task/list_task.cpp @@ -0,0 +1,162 @@ +/** + * @file list_task.cpp + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2022 Tobias Anker + * + * 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 "list_task.h" + +#include +#include +#include + +#include +#include + +using namespace Kitsunemimi::Hanami; + +ListTask::ListTask() + : Blossom("List all visible tasks of a specific cluster.") +{ + //---------------------------------------------------------------------------------------------- + // input + //---------------------------------------------------------------------------------------------- + + registerInputField("cluster_uuid", + SAKURA_STRING_TYPE, + true, + "UUID of the cluster, whos tasks should be listed"); + assert(addFieldRegex("cluster_uuid", UUID_REGEX)); + + //---------------------------------------------------------------------------------------------- + // output + //---------------------------------------------------------------------------------------------- + + registerOutputField("header", + SAKURA_ARRAY_TYPE, + "Array with the namings all columns of the table."); + assert(addFieldMatch("header", new Kitsunemimi::DataValue("[\"uuid\"," + "\"state\"," + "\"percentage\"," + "\"queued\"," + "\"start\"," + "\"end\"]"))); + + registerOutputField("body", + SAKURA_ARRAY_TYPE, + "Array with all rows of the table, which array arrays too."); + + //---------------------------------------------------------------------------------------------- + // + //---------------------------------------------------------------------------------------------- +} + +/** + * @brief runTask + */ +bool +ListTask::runTask(BlossomIO &blossomIO, + const Kitsunemimi::DataMap &context, + BlossomStatus &status, + Kitsunemimi::ErrorContainer &error) +{ + const Kitsunemimi::Hanami::UserContext userContext(context); + const std::string clusterUuid = blossomIO.input.get("cluster_uuid").getString(); + + // get cluster + Cluster* cluster = KyoukoRoot::m_clusterHandler->getCluster(clusterUuid); + if(cluster == nullptr) + { + status.errorMessage = "Cluster with UUID '" + clusterUuid + "'not found"; + status.statusCode = Kitsunemimi::Hanami::NOT_FOUND_RTYPE; + error.addMeesage(status.errorMessage); + return false; + } + + // get progress of all tasks + std::map progressOverview; + cluster->getAllProgress(progressOverview); + + // init table-header + Kitsunemimi::TableItem result; + result.addColumn("uuid"); + result.addColumn("state"); + result.addColumn("percentage"); + result.addColumn("queued"); + result.addColumn("start"); + result.addColumn("end"); + + // build table-content + std::map::iterator it; + for(it = progressOverview.begin(); + it != progressOverview.end(); + it++) + { + if(it->second.state == QUEUED_TASK_STATE) + { + result.addRow(std::vector{ + it->first, + "queued", + std::to_string(it->second.percentageFinished), + serializeTimePoint(it->second.queuedTimeStamp), + "-", + "-" + }); + } + else if(it->second.state == ACTIVE_TASK_STATE) + { + result.addRow(std::vector{ + it->first, + "active", + std::to_string(it->second.percentageFinished), + serializeTimePoint(it->second.queuedTimeStamp), + serializeTimePoint(it->second.startActiveTimeStamp), + "-" + }); + } + else if(it->second.state == ABORTED_TASK_STATE) + { + result.addRow(std::vector{ + it->first, + "aborted", + std::to_string(it->second.percentageFinished), + serializeTimePoint(it->second.queuedTimeStamp), + serializeTimePoint(it->second.startActiveTimeStamp), + "-" + }); + } + else if(it->second.state == FINISHED_TASK_STATE) + { + result.addRow(std::vector{ + it->first, + "finished", + std::to_string(it->second.percentageFinished), + serializeTimePoint(it->second.queuedTimeStamp), + serializeTimePoint(it->second.startActiveTimeStamp), + serializeTimePoint(it->second.endActiveTimeStamp) + }); + } + } + + // prepare for output + blossomIO.output.insert("header", result.getInnerHeader()); + blossomIO.output.insert("body", result.getBody()); + + return true; +} diff --git a/src/components/KyoukoMind/src/api/v1/task/list_task.h b/src/components/KyoukoMind/src/api/v1/task/list_task.h new file mode 100644 index 00000000..c1684879 --- /dev/null +++ b/src/components/KyoukoMind/src/api/v1/task/list_task.h @@ -0,0 +1,41 @@ +/** + * @file list_task.h + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2022 Tobias Anker + * + * 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. + */ + +#ifndef KYOUKOMIND_LISTTASK_H +#define KYOUKOMIND_LISTTASK_H + +#include + +class ListTask + : public Kitsunemimi::Hanami::Blossom +{ +public: + ListTask(); + +protected: + bool runTask(Kitsunemimi::Hanami::BlossomIO &blossomIO, + const Kitsunemimi::DataMap &context, + Kitsunemimi::Hanami::BlossomStatus &status, + Kitsunemimi::ErrorContainer &error); +}; + +#endif // KYOUKOMIND_LISTTASK_H diff --git a/src/components/KyoukoMind/src/api/v1/task/show_task.cpp b/src/components/KyoukoMind/src/api/v1/task/show_task.cpp new file mode 100644 index 00000000..61228849 --- /dev/null +++ b/src/components/KyoukoMind/src/api/v1/task/show_task.cpp @@ -0,0 +1,139 @@ +/** + * @file show_task.cpp + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2022 Tobias Anker + * + * 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 "show_task.h" + +#include +#include +#include + +#include +#include + +using namespace Kitsunemimi::Hanami; + +ShowTask::ShowTask() + : Blossom("Show information of a specific task.") +{ + //---------------------------------------------------------------------------------------------- + // input + //---------------------------------------------------------------------------------------------- + + registerInputField("uuid", + SAKURA_STRING_TYPE, + true, + "UUID of the cluster, which should process the request"); + assert(addFieldRegex("uuid", UUID_REGEX)); + + registerInputField("cluster_uuid", + SAKURA_STRING_TYPE, + true, + "UUID of the cluster, which should process the request"); + assert(addFieldRegex("cluster_uuid", UUID_REGEX)); + + //---------------------------------------------------------------------------------------------- + // output + //---------------------------------------------------------------------------------------------- + + registerOutputField("percentage_finished", + SAKURA_FLOAT_TYPE, + "Percentation of the progress between 0.0 and 1.0."); + registerOutputField("state", + SAKURA_STRING_TYPE, + "Actual state of the task (queued, active, aborted or finished)."); + registerOutputField("queue_timestamp", + SAKURA_STRING_TYPE, + "Timestamp in UTC when the task entered the queued state, " + "which is basicall the timestamp when the task was created"); + registerOutputField("start_timestamp", + SAKURA_STRING_TYPE, + "Timestamp in UTC when the task entered the active state."); + registerOutputField("end_timestamp", + SAKURA_STRING_TYPE, + "Timestamp in UTC when the task was finished."); + + //---------------------------------------------------------------------------------------------- + // + //---------------------------------------------------------------------------------------------- +} + + +/** + * @brief runTask + */ +bool +ShowTask::runTask(BlossomIO &blossomIO, + const Kitsunemimi::DataMap &context, + BlossomStatus &status, + Kitsunemimi::ErrorContainer &error) +{ + const std::string clusterUuid = blossomIO.input.get("cluster_uuid").getString(); + const std::string taskUuid = blossomIO.input.get("uuid").getString(); + const Kitsunemimi::Hanami::UserContext userContext(context); + + // get cluster + Cluster* cluster = KyoukoRoot::m_clusterHandler->getCluster(clusterUuid); + if(cluster == nullptr) + { + status.errorMessage = "Cluster with UUID '" + clusterUuid + "'not found"; + status.statusCode = Kitsunemimi::Hanami::NOT_FOUND_RTYPE; + error.addMeesage(status.errorMessage); + return false; + } + + const TaskProgress progress = cluster->getProgress(taskUuid); + + // get basic information + blossomIO.output.insert("percentage_finished", progress.percentageFinished); + blossomIO.output.insert("queue_timestamp", serializeTimePoint(progress.queuedTimeStamp)); + + // get timestamps + if(progress.state == QUEUED_TASK_STATE) + { + blossomIO.output.insert("state", "queued"); + blossomIO.output.insert("start_timestamp", "-"); + blossomIO.output.insert("end_timestamp", "-"); + } + else if(progress.state == ACTIVE_TASK_STATE) + { + blossomIO.output.insert("state", "active"); + blossomIO.output.insert("start_timestamp", + serializeTimePoint(progress.startActiveTimeStamp)); + blossomIO.output.insert("end_timestamp", "-"); + } + else if(progress.state == ABORTED_TASK_STATE) + { + blossomIO.output.insert("state", "aborted"); + blossomIO.output.insert("start_timestamp", + serializeTimePoint(progress.startActiveTimeStamp)); + blossomIO.output.insert("end_timestamp", "-"); + } + else if(progress.state == FINISHED_TASK_STATE) + { + blossomIO.output.insert("state", "finished"); + blossomIO.output.insert("start_timestamp", + serializeTimePoint(progress.startActiveTimeStamp)); + blossomIO.output.insert("end_timestamp", serializeTimePoint(progress.endActiveTimeStamp)); + } + + return true; +} diff --git a/src/components/KyoukoMind/src/api/v1/task/show_task.h b/src/components/KyoukoMind/src/api/v1/task/show_task.h new file mode 100644 index 00000000..bad1771b --- /dev/null +++ b/src/components/KyoukoMind/src/api/v1/task/show_task.h @@ -0,0 +1,41 @@ +/** + * @file show_task.h + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2022 Tobias Anker + * + * 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. + */ + +#ifndef KYOUKOMIND_SHOWTASK_H +#define KYOUKOMIND_SHOWTASK_H + +#include + +class ShowTask + : public Kitsunemimi::Hanami::Blossom +{ +public: + ShowTask(); + +protected: + bool runTask(Kitsunemimi::Hanami::BlossomIO &blossomIO, + const Kitsunemimi::DataMap &context, + Kitsunemimi::Hanami::BlossomStatus &status, + Kitsunemimi::ErrorContainer &error); +}; + +#endif // KYOUKOMIND_SHOWTASK_H diff --git a/src/components/KyoukoMind/src/api/v1/template/delete_template.cpp b/src/components/KyoukoMind/src/api/v1/template/delete_template.cpp new file mode 100644 index 00000000..9952b7b6 --- /dev/null +++ b/src/components/KyoukoMind/src/api/v1/template/delete_template.cpp @@ -0,0 +1,89 @@ +/** + * @file create_cluster_template.h + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2022 Tobias Anker + * + * 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 "delete_template.h" + +#include +#include + +#include + +#include + +using namespace Kitsunemimi::Hanami; + +DeleteTemplate::DeleteTemplate() + : Blossom("Delete a template from the database.") +{ + //---------------------------------------------------------------------------------------------- + // input + //---------------------------------------------------------------------------------------------- + + registerInputField("uuid", + SAKURA_STRING_TYPE, + true, + "UUID of the template."); + assert(addFieldRegex("uuid", UUID_REGEX)); + + //---------------------------------------------------------------------------------------------- + // + //---------------------------------------------------------------------------------------------- +} + +/** + * @brief runTask + */ +bool +DeleteTemplate::runTask(BlossomIO &blossomIO, + const Kitsunemimi::DataMap &context, + BlossomStatus &status, + Kitsunemimi::ErrorContainer &error) +{ + // get information from request + const std::string templateUuid = blossomIO.input.get("uuid").getString(); + const Kitsunemimi::Hanami::UserContext userContext(context); + + // check if user exist within the table + JsonItem getResult; + if(KyoukoRoot::templateTable->getTemplate(getResult, + templateUuid, + userContext, + error) == false) + { + status.errorMessage = "Template with UUID '" + templateUuid + "' not found."; + status.statusCode = Kitsunemimi::Hanami::NOT_FOUND_RTYPE; + error.addMeesage(status.errorMessage); + return false; + } + + // remove data from table + if(KyoukoRoot::templateTable->deleteTemplate(templateUuid, userContext, error) == false) + { + status.statusCode = Kitsunemimi::Hanami::INTERNAL_SERVER_ERROR_RTYPE; + error.addMeesage("Failed to delete template with UUID '" + + templateUuid + + "' from database"); + return false; + } + + return true; +} diff --git a/src/components/KyoukoMind/src/api/v1/template/delete_template.h b/src/components/KyoukoMind/src/api/v1/template/delete_template.h new file mode 100644 index 00000000..cc44cbe8 --- /dev/null +++ b/src/components/KyoukoMind/src/api/v1/template/delete_template.h @@ -0,0 +1,41 @@ +/** + * @file create_cluster_template.h + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2022 Tobias Anker + * + * 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. + */ + +#ifndef KYOUKOMIND_DELETETEMPLATE_H +#define KYOUKOMIND_DELETETEMPLATE_H + +#include + +class DeleteTemplate + : public Kitsunemimi::Hanami::Blossom +{ +public: + DeleteTemplate(); + +protected: + bool runTask(Kitsunemimi::Hanami::BlossomIO &blossomIO, + const Kitsunemimi::DataMap &context, + Kitsunemimi::Hanami::BlossomStatus &status, + Kitsunemimi::ErrorContainer &error); +}; + +#endif // DELETETEMPLATE_H diff --git a/src/components/KyoukoMind/src/api/v1/template/list_templates.cpp b/src/components/KyoukoMind/src/api/v1/template/list_templates.cpp new file mode 100644 index 00000000..45234aac --- /dev/null +++ b/src/components/KyoukoMind/src/api/v1/template/list_templates.cpp @@ -0,0 +1,84 @@ +/** + * @file create_cluster_template.h + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2022 Tobias Anker + * + * 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 "list_templates.h" + +#include +#include + +#include + +using namespace Kitsunemimi::Hanami; + +ListTemplates::ListTemplates() + : Blossom("List all visible templates.") +{ + //---------------------------------------------------------------------------------------------- + // output + //---------------------------------------------------------------------------------------------- + + registerOutputField("header", + SAKURA_ARRAY_TYPE, + "Array with the namings all columns of the table."); + assert(addFieldMatch("header", new Kitsunemimi::DataValue("[\"uuid\"," + "\"name\"]"))); + registerOutputField("body", + SAKURA_ARRAY_TYPE, + "Array with all rows of the table, which array arrays too."); + + //---------------------------------------------------------------------------------------------- + // + //---------------------------------------------------------------------------------------------- +} + +/** + * @brief runTask + */ +bool +ListTemplates::runTask(BlossomIO &blossomIO, + const Kitsunemimi::DataMap &context, + BlossomStatus &status, + Kitsunemimi::ErrorContainer &error) +{ + const std::string type = blossomIO.input.get("template").get("type").getString(); + // TODO: check type-field + + const Kitsunemimi::Hanami::UserContext userContext(context); + + // get data from table + Kitsunemimi::TableItem table; + if(KyoukoRoot::templateTable->getAllTemplate(table, userContext, error) == false) + { + status.statusCode = Kitsunemimi::Hanami::INTERNAL_SERVER_ERROR_RTYPE; + error.addMeesage("Failed to get all templates from database"); + return false; + } + + table.deleteColumn("visibility"); + table.deleteColumn("owner_id"); + table.deleteColumn("project_id"); + + blossomIO.output.insert("header", table.getInnerHeader()); + blossomIO.output.insert("body", table.getBody()); + + return true; +} diff --git a/src/components/KyoukoMind/src/api/v1/template/list_templates.h b/src/components/KyoukoMind/src/api/v1/template/list_templates.h new file mode 100644 index 00000000..4eaae0bd --- /dev/null +++ b/src/components/KyoukoMind/src/api/v1/template/list_templates.h @@ -0,0 +1,41 @@ +/** + * @file create_cluster_template.h + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2022 Tobias Anker + * + * 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. + */ + +#ifndef KYOUKOMIND_LISTTEMPLATES_H +#define KYOUKOMIND_LISTTEMPLATES_H + +#include + +class ListTemplates + : public Kitsunemimi::Hanami::Blossom +{ +public: + ListTemplates(); + +protected: + bool runTask(Kitsunemimi::Hanami::BlossomIO &blossomIO, + const Kitsunemimi::DataMap &context, + Kitsunemimi::Hanami::BlossomStatus &status, + Kitsunemimi::ErrorContainer &error); +}; + +#endif // LISTTEMPLATES_H diff --git a/src/components/KyoukoMind/src/api/v1/template/show_template.cpp b/src/components/KyoukoMind/src/api/v1/template/show_template.cpp new file mode 100644 index 00000000..9ce9682d --- /dev/null +++ b/src/components/KyoukoMind/src/api/v1/template/show_template.cpp @@ -0,0 +1,109 @@ +/** + * @file create_cluster_template.h + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2022 Tobias Anker + * + * 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 "show_template.h" + +#include +#include + +#include + +#include + +using namespace Kitsunemimi::Hanami; + +ShowTemplate::ShowTemplate() + : Blossom("Show a specific template.") +{ + //---------------------------------------------------------------------------------------------- + // input + //---------------------------------------------------------------------------------------------- + + registerInputField("uuid", + SAKURA_STRING_TYPE, + true, + "UUID of the template."); + assert(addFieldRegex("uuid", "[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-" + "[a-fA-F0-9]{4}-[a-fA-F0-9]{12}")); + + //---------------------------------------------------------------------------------------------- + // output + //---------------------------------------------------------------------------------------------- + + registerOutputField("uuid", + SAKURA_STRING_TYPE, + "UUID of the template."); + registerOutputField("name", + SAKURA_STRING_TYPE, + "Name of the template."); + registerOutputField("template", + SAKURA_STRING_TYPE, + "The template itself."); + registerOutputField("owner_id", + SAKURA_STRING_TYPE, + "ID of the user, who created the template."); + registerOutputField("project_id", + SAKURA_STRING_TYPE, + "ID of the project, where the template belongs to."); + registerOutputField("visibility", + SAKURA_STRING_TYPE, + "Visibility of the template (private, shared, public)."); + + + //---------------------------------------------------------------------------------------------- + // + //---------------------------------------------------------------------------------------------- +} + +/** + * @brief runTask + */ +bool +ShowTemplate::runTask(BlossomIO &blossomIO, + const Kitsunemimi::DataMap &context, + BlossomStatus &status, + Kitsunemimi::ErrorContainer &error) +{ + const Kitsunemimi::Hanami::UserContext userContext(context); + const std::string uuid = blossomIO.input.get("uuid").getString(); + // TODO: check type-field + + // get data from table + if(KyoukoRoot::templateTable->getTemplate(blossomIO.output, + uuid, + userContext, + error, + true) == false) + { + status.errorMessage = "Tempalte with UUID '" + uuid + "' not found."; + status.statusCode = Kitsunemimi::Hanami::NOT_FOUND_RTYPE; + error.addMeesage(status.errorMessage); + return false; + } + + blossomIO.output.insert("template", blossomIO.output.get("data").getString()); + + // remove irrelevant fields + blossomIO.output.remove("data"); + + return true; +} diff --git a/src/components/KyoukoMind/src/api/v1/template/show_template.h b/src/components/KyoukoMind/src/api/v1/template/show_template.h new file mode 100644 index 00000000..a387be76 --- /dev/null +++ b/src/components/KyoukoMind/src/api/v1/template/show_template.h @@ -0,0 +1,41 @@ +/** + * @file create_cluster_template.h + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2022 Tobias Anker + * + * 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. + */ + +#ifndef KYOUKOMIND_SHOW_TEMPLATE_H +#define KYOUKOMIND_SHOW_TEMPLATE_H + +#include + +class ShowTemplate + : public Kitsunemimi::Hanami::Blossom +{ +public: + ShowTemplate(); + +protected: + bool runTask(Kitsunemimi::Hanami::BlossomIO &blossomIO, + const Kitsunemimi::DataMap &context, + Kitsunemimi::Hanami::BlossomStatus &status, + Kitsunemimi::ErrorContainer &error); +}; + +#endif // SHOW_TEMPLATE_H diff --git a/src/components/KyoukoMind/src/api/v1/template/upload_template.cpp b/src/components/KyoukoMind/src/api/v1/template/upload_template.cpp new file mode 100644 index 00000000..f4e8f744 --- /dev/null +++ b/src/components/KyoukoMind/src/api/v1/template/upload_template.cpp @@ -0,0 +1,155 @@ +/** + * @file upload_template.cpp + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2022 Tobias Anker + * + * 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 "upload_template.h" +#include + +#include + +#include +#include +#include + +#include + +using namespace Kitsunemimi::Hanami; + +UploadTemplate::UploadTemplate() + : Blossom("Upload a new template and store it within the database.") +{ + //---------------------------------------------------------------------------------------------- + // input + //---------------------------------------------------------------------------------------------- + + registerInputField("name", + SAKURA_STRING_TYPE, + true, + "Name for the new template."); + // column in database is limited to 256 characters size + assert(addFieldBorder("name", 4, 256)); + assert(addFieldRegex("name", "[a-zA-Z][a-zA-Z_0-9]*")); + + registerInputField("template", + SAKURA_STRING_TYPE, + true, + "New template to upload as base64 string."); + + //---------------------------------------------------------------------------------------------- + // output + //---------------------------------------------------------------------------------------------- + + registerOutputField("uuid", + SAKURA_STRING_TYPE, + "UUID of the new uploaded template."); + registerOutputField("name", + SAKURA_STRING_TYPE, + "Name of the new uploaded template."); + registerOutputField("owner_id", + SAKURA_STRING_TYPE, + "ID of the user, who created the new template."); + registerOutputField("project_id", + SAKURA_STRING_TYPE, + "ID of the project, where the new template belongs to."); + registerOutputField("visibility", + SAKURA_STRING_TYPE, + "Visibility of the new created template (private, shared, public)."); + + //---------------------------------------------------------------------------------------------- + // + //---------------------------------------------------------------------------------------------- +} + +/** + * @brief runTask + */ +bool +UploadTemplate::runTask(BlossomIO &blossomIO, + const Kitsunemimi::DataMap &context, + BlossomStatus &status, + Kitsunemimi::ErrorContainer &error) +{ + const std::string name = blossomIO.input.get("name").getString(); + const std::string stringContent = blossomIO.input.get("template").toString(); + const Kitsunemimi::Hanami::UserContext userContext(context); + + // check if template with the name already exist within the table + JsonItem getResult; + if(KyoukoRoot::templateTable->getTemplateByName(getResult, name, userContext, error)) + { + status.errorMessage = "Template with name '" + name + "' already exist."; + status.statusCode = Kitsunemimi::Hanami::CONFLICT_RTYPE; + error.addMeesage(status.errorMessage); + return false; + } + error._errorMessages.clear(); + error._possibleSolution.clear(); + + // decode base64 formated template to check if valid base64-string + DataBuffer convertedTemplate; + if(Kitsunemimi::decodeBase64(convertedTemplate, stringContent) == false) + { + status.errorMessage = "Uploaded template is not a valid base64-String."; + status.statusCode = Kitsunemimi::Hanami::BAD_REQUEST_RTYPE; + error.addMeesage(status.errorMessage); + return false; + } + + // parse segment-template to validate syntax + Kitsunemimi::Hanami::SegmentMeta parsedSegment; + const std::string convertedTemplateStr(static_cast(convertedTemplate.data), + convertedTemplate.usedBufferSize); + if(Kitsunemimi::Hanami::parseSegment(&parsedSegment, convertedTemplateStr, error) == false) + { + status.errorMessage = "Uploaded template is not a valid segment-template: \n"; + status.errorMessage += error.toString(); + status.statusCode = Kitsunemimi::Hanami::BAD_REQUEST_RTYPE; + error.addMeesage(status.errorMessage); + return false; + } + + // convert values + JsonItem templateData; + templateData.insert("name", name); + templateData.insert("data", stringContent); + templateData.insert("visibility", "private"); + + // add new user to table + if(KyoukoRoot::templateTable->addTemplate(templateData, userContext, error) == false) + { + error.addMeesage("Failed to add new template to database"); + status.statusCode = Kitsunemimi::Hanami::INTERNAL_SERVER_ERROR_RTYPE; + return false; + } + + // get new created user from database + if(KyoukoRoot::templateTable->getTemplateByName(blossomIO.output, + name, + userContext, + error) == false) + { + error.addMeesage("Failed to get new template from database"); + status.statusCode = Kitsunemimi::Hanami::INTERNAL_SERVER_ERROR_RTYPE; + return false; + } + + return true; +} diff --git a/src/components/KyoukoMind/src/api/v1/template/upload_template.h b/src/components/KyoukoMind/src/api/v1/template/upload_template.h new file mode 100644 index 00000000..1ed590c0 --- /dev/null +++ b/src/components/KyoukoMind/src/api/v1/template/upload_template.h @@ -0,0 +1,41 @@ +/** + * @file upload_template.h + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2022 Tobias Anker + * + * 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. + */ + +#ifndef KYOUKOMIND_UPLOADTEMPLATE_H +#define KYOUKOMIND_UPLOADTEMPLATE_H + +#include + +class UploadTemplate + : public Kitsunemimi::Hanami::Blossom +{ +public: + UploadTemplate(); + +protected: + bool runTask(Kitsunemimi::Hanami::BlossomIO &blossomIO, + const Kitsunemimi::DataMap &context, + Kitsunemimi::Hanami::BlossomStatus &status, + Kitsunemimi::ErrorContainer &error); +}; + +#endif // KYOUKOMIND_UPLOADTEMPLATE_H diff --git a/src/components/KyoukoMind/src/args.h b/src/components/KyoukoMind/src/args.h new file mode 100644 index 00000000..98d69765 --- /dev/null +++ b/src/components/KyoukoMind/src/args.h @@ -0,0 +1,47 @@ +/** + * @file args.h + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2022 Tobias Anker + * + * 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. + */ + +#ifndef KYOUKOMIND_ARGS_H +#define KYOUKOMIND_ARGS_H + +#include +#include + +/** + * @brief register all available arguments for the CLI input + * + * @param argparser reference to predefined argument-parser + * + * @return false, if registering argument failed, else true + */ +bool +registerArguments(Kitsunemimi::ArgParser* argparser, + Kitsunemimi::ErrorContainer &error) +{ + if(Kitsunemimi::Hanami::registerArguments(*argparser, error) == false) { + return false; + } + + return true; +} + +#endif // KYOUKOMIND_ARGS_H diff --git a/src/components/KyoukoMind/src/callbacks.cpp b/src/components/KyoukoMind/src/callbacks.cpp new file mode 100644 index 00000000..5bd2314b --- /dev/null +++ b/src/components/KyoukoMind/src/callbacks.cpp @@ -0,0 +1,51 @@ +/** + * @file callbacks.cpp + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2022 Tobias Anker + * + * 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 +#include + +#include + +class Cluster; + +/** + * @brief process stream-message + * + * @param target target-cluster of the session-endpoint + * @param data incoming data + * @param dataSize number of incoming data + */ +void +streamDataCallback(void* target, + Kitsunemimi::Sakura::Session*, + const void* data, + const uint64_t dataSize) +{ + Cluster* cluster = static_cast(target); + recvClusterInputMessage(cluster, data, dataSize); + + /**std::cout<<"#################################################"< + * + * @copyright Apache License Version 2.0 + * + * Copyright 2022 Tobias Anker + * + * 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. + */ + +#ifndef CALLBACKS_H +#define CALLBACKS_H + +#include + +namespace Kitsunemimi { +namespace Sakura { +class Session; +} +} + +void streamDataCallback(void* target, + Kitsunemimi::Sakura::Session*, + const void* data, + const uint64_t dataSize); + +#endif // CALLBACKS_H diff --git a/src/components/KyoukoMind/src/common.h b/src/components/KyoukoMind/src/common.h new file mode 100644 index 00000000..f98b7ba3 --- /dev/null +++ b/src/components/KyoukoMind/src/common.h @@ -0,0 +1,33 @@ +/** + * @file common.h + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2022 Tobias Anker + * + * 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. + */ + +#ifndef KYOUKOMIND_COMMON_H +#define KYOUKOMIND_COMMON_H + +#include +#include +#include +#include +#include +#include + +#endif // KYOUKOMIND_COMMON_H diff --git a/src/components/KyoukoMind/src/common/defines.h b/src/components/KyoukoMind/src/common/defines.h new file mode 100644 index 00000000..e13f3e7c --- /dev/null +++ b/src/components/KyoukoMind/src/common/defines.h @@ -0,0 +1,35 @@ +/** + * @file defines.h + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2022 Tobias Anker + * + * 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 +#include + +#define UNINTI_POINT_32 0x0FFFFFFF + +// network-predefines +#define SYNAPSES_PER_SYNAPSESECTION 31 +#define NEURONS_PER_NEURONSECTION 62 +#define POSSIBLE_NEXT_AXON_STEP 80 + +// processing +#define NUMBER_OF_PROCESSING_UNITS 1 +#define NUMBER_OF_RAND_VALUES 10485760 diff --git a/src/components/KyoukoMind/src/common/enums.h b/src/components/KyoukoMind/src/common/enums.h new file mode 100644 index 00000000..b648fcd2 --- /dev/null +++ b/src/components/KyoukoMind/src/common/enums.h @@ -0,0 +1,32 @@ +/** + * @file enums.h + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2022 Tobias Anker + * + * 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. + */ + +#ifndef KYOUKOMIND_ENUMS_H +#define KYOUKOMIND_ENUMS_H + +enum ObjectTypes +{ + CLUSTER_OBJECT = 0, + SEGMENT_OBJECT = 1, +}; + +#endif // KYOUKOMIND_ENUMS_H diff --git a/src/components/KyoukoMind/src/common/functions.h b/src/components/KyoukoMind/src/common/functions.h new file mode 100644 index 00000000..e3650505 --- /dev/null +++ b/src/components/KyoukoMind/src/common/functions.h @@ -0,0 +1,27 @@ +/** + * @file functions.h + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2022 Tobias Anker + * + * 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. + */ + +#ifndef KYOUKOMIND_FUNCTIONS_H +#define KYOUKOMIND_FUNCTIONS_H + + +#endif // KYOUKOMIND_FUNCTIONS_H diff --git a/src/components/KyoukoMind/src/common/includes.h b/src/components/KyoukoMind/src/common/includes.h new file mode 100644 index 00000000..5f27170b --- /dev/null +++ b/src/components/KyoukoMind/src/common/includes.h @@ -0,0 +1,74 @@ +/** + * @file includes.h + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2022 Tobias Anker + * + * 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. + */ + +#ifndef KYOUKOMIND_INCLUDES_H +#define KYOUKOMIND_INCLUDES_H + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +using Kitsunemimi::DataBuffer; + +#include +using Kitsunemimi::StackBuffer; + +#include +using Kitsunemimi::DataItem; +using Kitsunemimi::DataArray; +using Kitsunemimi::DataValue; +using Kitsunemimi::DataMap; + +#include +using Kitsunemimi::JsonItem; + +#include + +#endif // KYOUKOMIND_INCLUDES_H diff --git a/src/components/KyoukoMind/src/common/structs.h b/src/components/KyoukoMind/src/common/structs.h new file mode 100644 index 00000000..e45907b1 --- /dev/null +++ b/src/components/KyoukoMind/src/common/structs.h @@ -0,0 +1,34 @@ +/** + * @file structs.h + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2022 Tobias Anker + * + * 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. + */ + +#ifndef KYOUKOMIND_STRUCTS_H +#define KYOUKOMIND_STRUCTS_H + +#include +#include + +struct NextSides +{ + uint8_t sides[5]; +}; + +#endif // KYOUKOMIND_STRUCTS_H diff --git a/src/components/KyoukoMind/src/common/typedefs.h b/src/components/KyoukoMind/src/common/typedefs.h new file mode 100644 index 00000000..f83e6924 --- /dev/null +++ b/src/components/KyoukoMind/src/common/typedefs.h @@ -0,0 +1,35 @@ +/** + * @file typedefs.h + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2022 Tobias Anker + * + * 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. + */ + +#ifndef KYOUKOMIND_TYPEDEFS_H +#define KYOUKOMIND_TYPEDEFS_H + +#include + +typedef std::chrono::milliseconds chronoMilliSec; +typedef std::chrono::microseconds chronoMicroSec; +typedef std::chrono::nanoseconds chronoNanoSec; +typedef std::chrono::seconds chronoSec; +typedef std::chrono::high_resolution_clock::time_point chronoTimePoint; +typedef std::chrono::high_resolution_clock chronoClock; + +#endif // KYOUKOMIND_TYPEDEFS_H diff --git a/src/components/KyoukoMind/src/config.h b/src/components/KyoukoMind/src/config.h new file mode 100644 index 00000000..da6bc4d9 --- /dev/null +++ b/src/components/KyoukoMind/src/config.h @@ -0,0 +1,38 @@ +/** + * @file config.h + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2022 Tobias Anker + * + * 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. + */ + +#ifndef KYOUKOMIND_CONFIG_H +#define KYOUKOMIND_CONFIG_H + +#include +#include + +/** + * @brief define all available entries in the config file with default-values + */ +void +registerConfigs(Kitsunemimi::ErrorContainer &error) +{ + Kitsunemimi::Hanami::registerBasicConfigs(error); +} + +#endif // KYOUKOMIND_CONFIG_H diff --git a/src/components/KyoukoMind/src/core/callbacks.h b/src/components/KyoukoMind/src/core/callbacks.h new file mode 100644 index 00000000..ee7823ea --- /dev/null +++ b/src/components/KyoukoMind/src/core/callbacks.h @@ -0,0 +1,56 @@ +/** + * @file callbacks.h + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2022 Tobias Anker + * + * 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. + */ + +#ifndef KYOUKOMIND_CALLBACKS_H +#define KYOUKOMIND_CALLBACKS_H + +#include + +#include + +#include + +/** + * @brief clientDataCallback + * @param data + * @param dataSize + */ +void +clientDataCallback(Kitsunemimi::Sakura::Session*, + const void* data, + const uint64_t dataSize) +{ + const char* charData = static_cast(data); + const std::string text(charData, dataSize); + LOG_WARNING("client-text: " + text); +} + +void genericCallback(Kitsunemimi::Sakura::Session*, + const uint32_t, + void*, + const uint64_t, + const uint64_t) +{ + +} + +#endif // KYOUKOMIND_CALLBACKS_H diff --git a/src/components/KyoukoMind/src/core/cluster/cluster.cpp b/src/components/KyoukoMind/src/core/cluster/cluster.cpp new file mode 100644 index 00000000..e2e3c332 --- /dev/null +++ b/src/components/KyoukoMind/src/core/cluster/cluster.cpp @@ -0,0 +1,656 @@ +/** + * @file cluster_interface.cpp + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2022 Tobias Anker + * + * 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 "cluster.h" +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include + +/** + * @brief constructor + */ +Cluster::Cluster() +{ + m_stateMachine = new Kitsunemimi::Statemachine(); + m_taskHandleState = new TaskHandle_State(this); + + initStatemachine(*m_stateMachine, this, m_taskHandleState); +} + +/** + * @brief destructor + */ +Cluster::~Cluster() +{ + delete m_stateMachine; + + // already deleted in the destructor of the statemachine + // delete m_taskHandleState; + + for(AbstractSegment* segment : allSegments) { + delete segment; + } +} + +/** + * @brief get uuid of the cluster + * + * @return uuid of the cluster + */ +const +std::string Cluster::getUuid() +{ + return networkMetaData->uuid.toString(); +} + +/** + * @brief init the cluster + * + * @param parsedContent TODO + * @param segmentTemplates TODO + * @param uuid UUID of the new cluster + * + * @return true, if successful, else false + */ +bool +Cluster::init(const Kitsunemimi::Hanami::ClusterMeta &clusterTemplate, + const std::map &segmentTemplates, + const std::string &uuid) +{ + return initNewCluster(this, clusterTemplate, segmentTemplates, uuid); +} + +/** + * @brief Cluster::connectSlot + * + * @param sourceSegment + * @param sourceSlotName + * @param targetSegment + * @param targetSlotName + * + * @return + */ +bool +Cluster::connectSlot(const std::string &sourceSegmentName, + const std::string &sourceSlotName, + const std::string &targetSegmentName, + const std::string &targetSlotName) +{ + const uint64_t sourceSegmentId = getSegmentId(sourceSegmentName); + if(sourceSegmentId == UNINIT_STATE_64) { + return false; + } + + const uint64_t targetSegmentId = getSegmentId(targetSegmentName); + if(targetSegmentId == UNINIT_STATE_64) { + return false; + } + + AbstractSegment* sourceSegment = allSegments.at(sourceSegmentId); + AbstractSegment* targetSegment = allSegments.at(targetSegmentId); + + const uint8_t sourceSlotId = sourceSegment->getSlotId(sourceSlotName); + if(sourceSlotId == UNINIT_STATE_8) { + return false; + } + + const uint8_t targetSlotId = targetSegment->getSlotId(targetSlotName); + if(targetSlotId == UNINIT_STATE_8) { + return false; + } + + SegmentSlot* sourceSlot = &sourceSegment->segmentSlots->slots[sourceSlotId]; + SegmentSlot* targetSlot = &targetSegment->segmentSlots->slots[targetSlotId]; + + sourceSlot->inUse = true; + sourceSlot->targetSegmentId = targetSegmentId; + sourceSlot->targetSlotId = targetSlotId; + + targetSlot->inUse = true; + targetSlot->targetSegmentId = sourceSegmentId; + targetSlot->targetSlotId = sourceSlotId; + + return true; +} + +/** + * @brief Cluster::getSegment + * @param name + * @return + */ +uint64_t +Cluster::getSegmentId(const std::string &name) +{ + for(uint64_t i = 0; i < allSegments.size(); i++) + { + if(allSegments.at(i)->getName() == name) { + return i; + } + } + + return UNINIT_STATE_64; +} + +/** + * @brief get the name of the clsuter + * + * @return name of the cluster + */ +const std::string +Cluster::getName() +{ + // precheck + if(networkMetaData == nullptr) { + return std::string(""); + } + + return std::string(networkMetaData->name); +} + +/** + * @brief set new name for the cluster + * + * @param newName new name + * + * @return true, if successful, else false + */ +bool +Cluster::setName(const std::string newName) +{ + // precheck + if(networkMetaData == nullptr + || newName.size() > 1023 + || newName.size() == 0) + { + return false; + } + + // copy string into char-buffer and set explicit the escape symbol to be absolut sure + // that it is set to absolut avoid buffer-overflows + strncpy(networkMetaData->name, newName.c_str(), newName.size()); + networkMetaData->name[newName.size()] = '\0'; + + return true; +} + +/** + * @brief start a new forward learn-cycle + */ +void +Cluster::startForwardCycle() +{ + // set ready-states of all neighbors of all segments + for(AbstractSegment* segment : allSegments) + { + for(uint8_t side = 0; side < 16; side++) + { + SegmentSlot* neighbor = &segment->segmentSlots->slots[side]; + // TODO: check possible crash here + neighbor->inputReady = neighbor->direction != INPUT_DIRECTION; + } + } + + segmentCounter = 0; + KyoukoRoot::m_segmentQueue->addSegmentListToQueue(allSegments); +} + +/** + * @brief start a new backward learn-cycle + */ +void +Cluster::startBackwardCycle() +{ + // set ready-states of all neighbors of all segments + for(AbstractSegment* segment : allSegments) + { + for(uint8_t side = 0; side < 16; side++) + { + SegmentSlot* neighbor = &segment->segmentSlots->slots[side]; + neighbor->inputReady = neighbor->direction != OUTPUT_DIRECTION; + } + } + + segmentCounter = 0; + KyoukoRoot::m_segmentQueue->addSegmentListToQueue(allSegments); +} + +/** + * @brief switch state of the cluster between task and direct mode + * + * @param newState new desired state + * + * @return true, if switch in statemachine was successful, else false + */ +bool +Cluster::setClusterState(const std::string &newState) +{ + if(newState == "DIRECT") { + return goToNextState(SWITCH_TO_DIRECT_MODE); + } + + if(newState == "TASK") { + return goToNextState(SWITCH_TO_TASK_MODE); + } + + return false; +} + +/** + * @brief update state of the cluster, which is caled for each finalized segment + */ +void +Cluster::updateClusterState() +{ + std::lock_guard guard(m_segmentCounterLock); + + segmentCounter++; + if(segmentCounter < allSegments.size()) { + return; + } + + // trigger next lerning phase, if already in phase 1 + if(mode == Cluster::LEARN_FORWARD_MODE) + { + mode = Cluster::LEARN_BACKWARD_MODE; + startBackwardCycle(); + return; + } + + // send message, that process was finished + if(mode == Cluster::LEARN_BACKWARD_MODE) { + sendClusterLearnEndMessage(this); + } else if(mode == Cluster::NORMAL_MODE) { + sendClusterNormalEndMessage(this); + } + + goToNextState(NEXT); +} + +/** + * @brief create a learn-task and add it to the task-queue + * + * @param inputData input-data + * @param numberOfInputsPerCycle number of inputs per cycle + * @param numberOfOuputsPerCycle number of outputs per cycle + * @param numberOfCycle number of cycles + * + * @return task-uuid + */ +const std::string +Cluster::addImageLearnTask(const std::string &name, + const std::string &userId, + const std::string &projectId, + float* inputData, + const uint64_t numberOfInputsPerCycle, + const uint64_t numberOfOuputsPerCycle, + const uint64_t numberOfCycle) +{ + // create new learn-task + Task newTask; + newTask.uuid = Kitsunemimi::Hanami::generateUuid(); + newTask.name = name; + newTask.userId = userId; + newTask.projectId = projectId; + newTask.inputData = inputData; + newTask.type = IMAGE_LEARN_TASK; + newTask.progress.state = QUEUED_TASK_STATE; + newTask.progress.queuedTimeStamp = std::chrono::system_clock::now(); + + // fill metadata + newTask.metaData.insert("number_of_cycles", + new DataValue(static_cast(numberOfCycle))); + newTask.metaData.insert("number_of_inputs_per_cycle", + new DataValue(static_cast(numberOfInputsPerCycle))); + newTask.metaData.insert("number_of_outputs_per_cycle", + new DataValue(static_cast(numberOfOuputsPerCycle))); + + // add task to queue + const std::string uuid = newTask.uuid.toString(); + m_taskHandleState->addTask(uuid, newTask); + + m_stateMachine->goToNextState(PROCESS_TASK); + + return uuid; +} + +/** + * @brief create a request-task and add it to the task-queue + * + * @param inputData input-data + * @param numberOfInputsPerCycle number of inputs per cycle + * @param numberOfOuputsPerCycle number of outputs per cycle + * @param numberOfCycle number of cycles + * + * @return task-uuid + */ +const std::string +Cluster::addImageRequestTask(const std::string &name, + const std::string &userId, + const std::string &projectId, + float* inputData, + const uint64_t numberOfInputsPerCycle, + const uint64_t numberOfOuputsPerCycle, + const uint64_t numberOfCycle) +{ + // create new request-task + Task newTask; + newTask.uuid = Kitsunemimi::Hanami::generateUuid(); + newTask.name = name; + newTask.userId = userId; + newTask.projectId = projectId; + newTask.inputData = inputData; + newTask.resultData = new DataArray(); + for(uint64_t i = 0; i < numberOfCycle; i++) { + newTask.resultData->append(new DataValue(0)); + } + newTask.type = IMAGE_REQUEST_TASK; + newTask.progress.state = QUEUED_TASK_STATE; + newTask.progress.queuedTimeStamp = std::chrono::system_clock::now(); + + // fill metadata + newTask.metaData.insert("number_of_cycles", + new DataValue(static_cast(numberOfCycle))); + newTask.metaData.insert("number_of_inputs_per_cycle", + new DataValue(static_cast(numberOfInputsPerCycle))); + newTask.metaData.insert("number_of_outputs_per_cycle", + new DataValue(static_cast(numberOfOuputsPerCycle))); + + // add task to queue + const std::string uuid = newTask.uuid.toString(); + m_taskHandleState->addTask(uuid, newTask); + + m_stateMachine->goToNextState(PROCESS_TASK); + + return uuid; +} + +/** + * @brief create task to learn table-data and add it to the task-queue + * + * @param inputData input-data + * @param numberOfInputs number of inputs per cycle + * @param numberOfCycle number of cycles + * + * @return task-uuid + */ +const std::string +Cluster::addTableLearnTask(const std::string &name, + const std::string &userId, + const std::string &projectId, + float* inputData, + float* outputData, + const uint64_t numberOfInputs, + const uint64_t numberOfOutputs, + const uint64_t numberOfCycle) +{ + // create new learn-task + Task newTask; + newTask.uuid = Kitsunemimi::Hanami::generateUuid(); + newTask.name = name; + newTask.userId = userId; + newTask.projectId = projectId; + newTask.inputData = inputData; + newTask.outputData = outputData; + newTask.type = TABLE_LEARN_TASK; + newTask.progress.state = QUEUED_TASK_STATE; + newTask.progress.queuedTimeStamp = std::chrono::system_clock::now(); + + // fill metadata + newTask.metaData.insert("number_of_cycles", + new DataValue(static_cast(numberOfCycle))); + newTask.metaData.insert("number_of_inputs_per_cycle", + new DataValue(static_cast(numberOfInputs))); + newTask.metaData.insert("number_of_outputs_per_cycle", + new DataValue(static_cast(numberOfOutputs))); + + // add task to queue + const std::string uuid = newTask.uuid.toString(); + m_taskHandleState->addTask(uuid, newTask); + + m_stateMachine->goToNextState(PROCESS_TASK); + + return uuid; +} + +/** + * @brief create task to request table-data and add it to the task-queue + * + * @param inputData input-data + * @param numberOfInputs number of inputs per cycle + * @param numberOfCycle number of cycles + * + * @return task-uuid + */ +const std::string +Cluster::addTableRequestTask(const std::string &name, + const std::string &userId, + const std::string &projectId, + float* inputData, + const uint64_t numberOfInputs, + const uint64_t numberOfOutputs, + const uint64_t numberOfCycle) +{ + // create new request-task + Task newTask; + newTask.uuid = Kitsunemimi::Hanami::generateUuid(); + newTask.name = name; + newTask.userId = userId; + newTask.projectId = projectId; + newTask.inputData = inputData; + newTask.resultData = new DataArray(); + for(uint64_t i = 0; i < numberOfCycle; i++) { + newTask.resultData->append(new DataValue(0.0f)); + } + newTask.type = TABLE_REQUEST_TASK; + newTask.progress.state = QUEUED_TASK_STATE; + newTask.progress.queuedTimeStamp = std::chrono::system_clock::now(); + + // fill metadata + newTask.metaData.insert("number_of_cycles", + new DataValue(static_cast(numberOfCycle))); + newTask.metaData.insert("number_of_inputs_per_cycle", + new DataValue(static_cast(numberOfInputs))); + newTask.metaData.insert("number_of_outputs_per_cycle", + new DataValue(static_cast(numberOfOutputs))); + + // add tasgetNextTaskk to queue + const std::string uuid = newTask.uuid.toString(); + m_taskHandleState->addTask(uuid, newTask); + + m_stateMachine->goToNextState(PROCESS_TASK); + + return uuid; +} + +/** + * @brief create task to create a snapshot from a cluster and add it to the task-queue + * + * @param snapshotName name for the snapshot + * @param userId uuid of the user, where the snapshot belongs to + * @param projectId uuid of the project, where the snapshot belongs to + * + * @return task-uuid + */ +const std::string +Cluster::addClusterSnapshotSaveTask(const std::string &snapshotName, + const std::string &userId, + const std::string &projectId) +{ + // create new request-task + Task newTask; + newTask.uuid = Kitsunemimi::Hanami::generateUuid(); + newTask.name = snapshotName; + newTask.userId = userId; + newTask.projectId = projectId; + newTask.type = CLUSTER_SNAPSHOT_SAVE_TASK; + newTask.progress.state = QUEUED_TASK_STATE; + newTask.progress.queuedTimeStamp = std::chrono::system_clock::now(); + + // fill metadata + newTask.metaData.insert("snapshot_name", new DataValue(snapshotName)); + newTask.metaData.insert("user_id", new DataValue(userId)); + newTask.metaData.insert("project_id", new DataValue(projectId)); + + // add tasgetNextTaskk to queue + const std::string uuid = newTask.uuid.toString(); + m_taskHandleState->addTask(uuid, newTask); + + m_stateMachine->goToNextState(PROCESS_TASK); + + return uuid; +} + +/** + * @brief create task to restore a cluster from a snapshot and add it to the task-queue + * + * @param snapshotUuid uuid of the snapshot + * @param userId uuid of the user, where the snapshot belongs to + * @param projectId uuid of the project, where the snapshot belongs to + * + * @return task-uuid + */ +const std::string +Cluster::addClusterSnapshotRestoreTask(const std::string &name, + const std::string &snapshotInfo, + const std::string &userId, + const std::string &projectId) +{ + // create new request-task + Task newTask; + newTask.uuid = Kitsunemimi::Hanami::generateUuid(); + newTask.name = name; + newTask.userId = userId; + newTask.projectId = projectId; + newTask.type = CLUSTER_SNAPSHOT_RESTORE_TASK; + newTask.progress.state = QUEUED_TASK_STATE; + newTask.progress.queuedTimeStamp = std::chrono::system_clock::now(); + + // fill metadata + newTask.metaData.insert("snapshot_info", new DataValue(snapshotInfo)); + newTask.metaData.insert("user_id", new DataValue(userId)); + newTask.metaData.insert("project_id", new DataValue(projectId)); + + // add tasgetNextTaskk to queue + const std::string uuid = newTask.uuid.toString(); + m_taskHandleState->addTask(uuid, newTask); + + m_stateMachine->goToNextState(PROCESS_TASK); + + return uuid; +} + +/** + * @brief get actual task + * + * @return pointer to the actual task + */ +Task* +Cluster::getActualTask() const +{ + return m_taskHandleState->getActualTask(); +} + +/** + * @brief get cycle of the actual task + * + * @return cycle of the actual task + */ +uint64_t +Cluster::getActualTaskCycle() const +{ + return m_taskHandleState->getActualTask()->actualCycle; +} + +/** + * @brief get task-progress + * + * @param taskUuid UUID of the task + * + * @return task-progress + */ +const TaskProgress +Cluster::getProgress(const std::string &taskUuid) +{ + return m_taskHandleState->getProgress(taskUuid); +} + +/** + * @brief remove task from queue of abort the task, if actual in progress + * + * @param taskUuid UUID of the task + * + * @return always true + */ +bool +Cluster::removeTask(const std::string &taskUuid) +{ + return m_taskHandleState->removeTask(taskUuid); +} + +/** + * @brief check if a task is finished + * + * @param taskUuid UUID of the task + * + * @return true, if task is finished, else false + */ +bool +Cluster::isFinish(const std::string &taskUuid) +{ + return m_taskHandleState->isFinish(taskUuid); +} + +/** + * @brief Cluster::getAllProgress + * @param result + */ +void +Cluster::getAllProgress(std::map &result) +{ + return m_taskHandleState->getAllProgress(result); +} + +/** + * @brief switch statemachine of cluster to next state + * + * @param nextStateId id of the next state + * + * @return true, if statemachine switch was successful, else false + */ +bool +Cluster::goToNextState(const uint32_t nextStateId) +{ + return m_stateMachine->goToNextState(nextStateId); +} diff --git a/src/components/KyoukoMind/src/core/cluster/cluster.h b/src/components/KyoukoMind/src/core/cluster/cluster.h new file mode 100644 index 00000000..4be2e7e2 --- /dev/null +++ b/src/components/KyoukoMind/src/core/cluster/cluster.h @@ -0,0 +1,169 @@ +/** + * @file cluster_interface.h + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2022 Tobias Anker + * + * 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. + */ + +#ifndef KYOUKOMIND_CLUSTER_INTERFACE_H +#define KYOUKOMIND_CLUSTER_INTERFACE_H + +#include +#include + +#include +#include + +class AbstractSegment; +class InputSegment; +class OutputSegment; +class AbstractSegment; +class TaskHandle_State; + +namespace Kitsunemimi { +class EventQueue; +class Statemachine; +namespace Hanami { +class HanamiMessagingClient; +} +} + +class Cluster +{ +public: + Cluster(); + ~Cluster(); + + enum ClusterProcessingMode + { + NORMAL_MODE = 0, + LEARN_FORWARD_MODE = 1, + LEARN_BACKWARD_MODE = 2, + }; + + struct MetaData + { + uint8_t objectType = CLUSTER_OBJECT; + uint8_t version = 1; + uint8_t padding1[6]; + uint64_t clusterSize = 0; + + Kitsunemimi::Hanami::kuuid uuid; + char name[1024]; + + uint32_t numberOfInputSegments = 0; + uint32_t numberOfOutputSegments = 0; + uint32_t numberOfSegments = 0; + + uint8_t padding2[956]; + + // total size: 2048 Byte + }; + + struct Settings + { + float lerningValue = 0.0f; + + uint8_t padding[252]; + // total size: 256 Byte + }; + + // cluster-data + Kitsunemimi::DataBuffer clusterData; + Cluster::MetaData* networkMetaData = nullptr; + Cluster::Settings* networkSettings = nullptr; + std::map inputSegments; + std::map outputSegments; + std::map coreSegments; + std::vector allSegments; + + const std::string getUuid(); + const std::string getName(); + bool setName(const std::string newName); + bool init(const Kitsunemimi::Hanami::ClusterMeta &clusterTemplate, + const std::map &segmentTemplates, + const std::string &uuid); + bool connectSlot(const std::string &sourceSegmentName, + const std::string &sourceSlotName, + const std::string &targetSegmentName, + const std::string &targetSlotName); + uint64_t getSegmentId(const std::string &name); + + // task-handling + void updateClusterState(); + const std::string addImageLearnTask(const std::string &name, + const std::string &userId, + const std::string &projectId, + float* inputData, + const uint64_t numberOfInputsPerCycle, + const uint64_t numberOfOuputsPerCycle, + const uint64_t numberOfCycle); + const std::string addImageRequestTask(const std::string &name, + const std::string &userId, + const std::string &projectId, + float* inputData, + const uint64_t numberOfInputsPerCycle, + const uint64_t numberOfOuputsPerCycle, + const uint64_t numberOfCycle); + const std::string addTableLearnTask(const std::string &name, + const std::string &userId, + const std::string &projectId, + float* inputData, + float* outputData, + const uint64_t numberOfInputs, + const uint64_t numberOfOutputs, + const uint64_t numberOfCycle); + const std::string addTableRequestTask(const std::string &name, + const std::string &userId, + const std::string &projectId, + float* inputData, + const uint64_t numberOfInputs, + const uint64_t numberOfOutputs, + const uint64_t numberOfCycle); + const std::string addClusterSnapshotSaveTask(const std::string &snapshotName, + const std::string &userId, + const std::string &projectId); + const std::string addClusterSnapshotRestoreTask(const std::string &name, + const std::string &snapshotInfo, + const std::string &userId, + const std::string &projectId); + + // tasks + Task* getActualTask() const; + uint64_t getActualTaskCycle() const; + const TaskProgress getProgress(const std::string &taskUuid); + bool removeTask(const std::string &taskUuid); + bool isFinish(const std::string &taskUuid); + void getAllProgress(std::map &result); + + bool goToNextState(const uint32_t nextStateId); + void startForwardCycle(); + void startBackwardCycle(); + bool setClusterState(const std::string &newState); + + uint32_t segmentCounter = 0; + ClusterProcessingMode mode = NORMAL_MODE; + Kitsunemimi::Hanami::HanamiMessagingClient* msgClient = nullptr; + +private: + Kitsunemimi::Statemachine* m_stateMachine = nullptr; + TaskHandle_State* m_taskHandleState = nullptr; + std::mutex m_segmentCounterLock; +}; + +#endif // KYOUKOMIND_CLUSTER_INTERFACE_H diff --git a/src/components/KyoukoMind/src/core/cluster/cluster_handler.cpp b/src/components/KyoukoMind/src/core/cluster/cluster_handler.cpp new file mode 100644 index 00000000..5178fc1a --- /dev/null +++ b/src/components/KyoukoMind/src/core/cluster/cluster_handler.cpp @@ -0,0 +1,98 @@ +/** + * @file cluster_handler.cpp + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2022 Tobias Anker + * + * 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 "cluster_handler.h" + +#include + +/** + * @brief constructor + */ +ClusterHandler::ClusterHandler() {} + +/** + * @brief add new cluster to handler + * + * @param uuid uuid of the cluster + * @param newCluster pointer to cluster-object + * + * @return false if uuid is already registered, else true + */ +bool +ClusterHandler::addCluster(const std::string uuid, Cluster* newCluster) +{ + // check if key already exist + std::map::iterator it; + it = m_allCluster.find(uuid); + if(it != m_allCluster.end()) { + return false; + } + + m_allCluster.insert(std::make_pair(uuid, newCluster)); + + return true; +} + +/** + * @brief remove a cluster from the handler + * + * @param uuid uuid of the cluster + * + * @return false, if uuid was not found, else true + */ +bool +ClusterHandler::removeCluster(const std::string uuid) +{ + std::map::const_iterator it; + it = m_allCluster.find(uuid); + + if(it != m_allCluster.end()) + { + if(it->second != nullptr) { + delete it->second; + } + m_allCluster.erase(it); + return true; + } + + return false; +} + +/** + * @brief request a specific cluster from the handler + * + * @param uuid uuid of the cluster + * + * @return pointer to the requested cluster + */ +Cluster* +ClusterHandler::getCluster(const std::string uuid) +{ + std::map::const_iterator it; + it = m_allCluster.find(uuid); + + if(it != m_allCluster.end()) { + return it->second; + } + + return nullptr; +} diff --git a/src/components/KyoukoMind/src/core/cluster/cluster_handler.h b/src/components/KyoukoMind/src/core/cluster/cluster_handler.h new file mode 100644 index 00000000..b447bb0c --- /dev/null +++ b/src/components/KyoukoMind/src/core/cluster/cluster_handler.h @@ -0,0 +1,43 @@ +/** + * @file cluster_handler.h + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2022 Tobias Anker + * + * 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. + */ + +#ifndef KYOUKOMIND_CLUSTERHANDLER_H +#define KYOUKOMIND_CLUSTERHANDLER_H + +#include + +class Cluster; + +class ClusterHandler +{ +public: + ClusterHandler(); + + bool addCluster(const std::string uuid, Cluster* newCluster); + bool removeCluster(const std::string uuid); + Cluster* getCluster(const std::string uuid); + +private: + std::map m_allCluster; +}; + +#endif // KYOUKOMIND_CLUSTERHANDLER_H diff --git a/src/components/KyoukoMind/src/core/cluster/cluster_init.cpp b/src/components/KyoukoMind/src/core/cluster/cluster_init.cpp new file mode 100644 index 00000000..f393b892 --- /dev/null +++ b/src/components/KyoukoMind/src/core/cluster/cluster_init.cpp @@ -0,0 +1,288 @@ +/** + * @file cluster_init.cpp + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2022 Tobias Anker + * + * 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 "cluster_init.h" + + +#include +#include +#include +#include + +#include +#include + +#include +#include + +#include +#include + +/** + * @brief re-initialize the pointer in the header of the cluster after restoring the cluster + * from a snapshot + * + * @param cluster pointer to the cluster + * @param uuid new uuid of the cluster to override the old one coming from the snapshot + * + * @return true, if successful, else false + */ +bool +reinitPointer(Cluster* cluster, + const std::string &uuid) +{ + uint8_t* dataPtr = static_cast(cluster->clusterData.data); + uint64_t pos = 0; + + // write metadata to buffer + cluster->networkMetaData = reinterpret_cast(dataPtr + pos); + pos += sizeof(Cluster::MetaData); + + // write settings to buffer + cluster->networkSettings = reinterpret_cast(dataPtr + pos); + pos += sizeof(Cluster::Settings); + + // override old uuid with the new one + strncpy(cluster->networkMetaData->uuid.uuid, uuid.c_str(), 36); + cluster->networkMetaData->uuid.uuid[36] = '\0'; + + cluster->clusterData.usedBufferSize = pos; + + return true; +} + +/** + * @brief init header for a new cluster + * + * @param cluster pointer to the cluster where the header belongs to + * @param metaData metadata-object to write into the header + * @param settings settings-object to write into the header + */ +void +initHeader(Cluster* cluster, + const Cluster::MetaData &metaData, + const Cluster::Settings &settings) +{ + // allocate memory + const uint32_t numberOfBlocks = 1; + Kitsunemimi::allocateBlocks_DataBuffer(cluster->clusterData, numberOfBlocks); + + uint8_t* dataPtr = static_cast(cluster->clusterData.data); + uint64_t pos = 0; + + // write metadata to buffer + cluster->networkMetaData = reinterpret_cast(dataPtr + pos); + cluster->networkMetaData[0] = metaData; + pos += sizeof(Cluster::MetaData); + + // write settings to buffer + cluster->networkSettings = reinterpret_cast(dataPtr + pos); + cluster->networkSettings[0] = settings; + pos += sizeof(Cluster::Settings); + + cluster->clusterData.usedBufferSize = pos; +} + +/** + * @brief initalize new cluster + * + * @param cluster pointer to the uninitionalized cluster + * @param parsedContent parsed json with the information of the cluster + * @param segmentTemplates TODO + * @param uuid uuid for the new cluster + * + * @return true, if successful, else false + */ +bool +initNewCluster(Cluster* cluster, + const Kitsunemimi::Hanami::ClusterMeta &clusterTemplate, + const std::map &segmentTemplates, + const std::string &uuid) +{ + // meta-data + Cluster::MetaData newMetaData; + strncpy(newMetaData.uuid.uuid, uuid.c_str(), 36); + newMetaData.uuid.uuid[36] = '\0'; + + // settings + Cluster::Settings newSettings; + initHeader(cluster, newMetaData, newSettings); + + //const std::string clusterName = clusterTemplate.get("name").getString(); + //const bool ret = cluster->setName(name); // TODO: handle return + + LOG_INFO("create new cluster with uuid: " + cluster->networkMetaData->uuid.toString()); + + // parse and create segments + for(const Kitsunemimi::Hanami::SegmentMetaPtr& segmentPtr : clusterTemplate.segments) + { + AbstractSegment* newSegment = nullptr; + std::map::const_iterator it; + it = segmentTemplates.find(segmentPtr.type); + if(it != segmentTemplates.end()) + { + if(segmentPtr.type == "input") { + newSegment = addInputSegment(cluster, segmentPtr.name, it->second); + } else if(segmentPtr.type == "output") { + newSegment = addOutputSegment(cluster, segmentPtr.name, it->second); + } else { + newSegment = addDynamicSegment(cluster, segmentPtr.name, it->second); + } + } + else + { + // TODO: error-handling + std::cout<<"failed to init segment 1"<segmentHeader->parentClusterId = cluster->networkMetaData->uuid; + newSegment->parentCluster = cluster; + } + + // connect all segments + for(const Kitsunemimi::Hanami::SegmentMetaPtr& sourceSegmentPtr : clusterTemplate.segments) + { + const std::string sourceSegment = sourceSegmentPtr.name; + + for(const Kitsunemimi::Hanami::ClusterConnection &conn : sourceSegmentPtr.outputs) + { + std::string targetSlot = conn.targetBrick; + if(targetSlot == "x") { + targetSlot = "input"; + } + std::string sourceSlot = conn.sourceBrick; + if(sourceSlot == "x") { + sourceSlot = "output"; + } + + if(cluster->connectSlot(sourceSegment, + sourceSlot, + conn.targetSegment, + targetSlot) == false) + { + std::cout<<"Faild to connect segment '" + << sourceSegment + << "' with segment '" + << conn.targetSegment + << "'"<initSegment(name, segmentMeta)) + { + cluster->inputSegments.insert(std::make_pair(name, newSegment)); + cluster->allSegments.push_back(newSegment); + } + else + { + delete newSegment; + newSegment = nullptr; + } + + return newSegment; +} + +/** + * @brief add new output-segment to cluster + * + * @param cluster pointer to the uninitionalized cluster + * @param clusterTemplatePart parsed json with the information of the cluster + * + * @return true, if successful, else false + */ +AbstractSegment* +addOutputSegment(Cluster* cluster, + const std::string &name, + const Kitsunemimi::Hanami::SegmentMeta &segmentMeta) +{ + OutputSegment* newSegment = new OutputSegment(); + JsonItem placeHolder; + + if(newSegment->initSegment(name, segmentMeta)) + { + cluster->outputSegments.insert(std::make_pair(name, newSegment)); + cluster->allSegments.push_back(newSegment); + } + else + { + delete newSegment; + newSegment = nullptr; + } + + return newSegment; +} + +/** + * @brief add new dynamic-segment to cluster + * + * @param cluster pointer to the uninitionalized cluster + * @param clusterTemplatePart parsed json with the information of the cluster + * + * @return true, if successful, else false + */ +AbstractSegment* +addDynamicSegment(Cluster* cluster, + const std::string &name, + const Kitsunemimi::Hanami::SegmentMeta &segmentMeta) +{ + DynamicSegment* newSegment = new DynamicSegment(); + if(newSegment->initSegment(name, segmentMeta)) + { + cluster->coreSegments.insert(std::make_pair(name, newSegment)); + cluster->allSegments.push_back(newSegment); + } + else + { + delete newSegment; + newSegment = nullptr; + } + + return newSegment; +} diff --git a/src/components/KyoukoMind/src/core/cluster/cluster_init.h b/src/components/KyoukoMind/src/core/cluster/cluster_init.h new file mode 100644 index 00000000..b3a0e0c8 --- /dev/null +++ b/src/components/KyoukoMind/src/core/cluster/cluster_init.h @@ -0,0 +1,54 @@ +/** + * @file cluster_init.h + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2022 Tobias Anker + * + * 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. + */ + +#ifndef KYOUKOMIND_CLUSTERINIT_H +#define KYOUKOMIND_CLUSTERINIT_H + +#include + +#include +#include + +class InputSegment; +class OutputSegment; +class AbstractSegment; + +class Cluster; + +bool reinitPointer(Cluster* cluster, const std::string &uuid); + +bool initNewCluster(Cluster* cluster, + const Kitsunemimi::Hanami::ClusterMeta &clusterTemplate, + const std::map &segmentTemplates, + const std::string &uuid); + +AbstractSegment* addInputSegment(Cluster* cluster, + const std::string &name, + const Kitsunemimi::Hanami::SegmentMeta &segmentMeta); +AbstractSegment* addOutputSegment(Cluster* cluster, + const std::string &name, + const Kitsunemimi::Hanami::SegmentMeta &segmentMeta); +AbstractSegment* addDynamicSegment(Cluster* cluster, + const std::string &name, + const Kitsunemimi::Hanami::SegmentMeta &segmentMeta); + +#endif // KYOUKOMIND_CLUSTERINIT_H diff --git a/src/components/KyoukoMind/src/core/cluster/statemachine_init.cpp b/src/components/KyoukoMind/src/core/cluster/statemachine_init.cpp new file mode 100644 index 00000000..3b1ac467 --- /dev/null +++ b/src/components/KyoukoMind/src/core/cluster/statemachine_init.cpp @@ -0,0 +1,212 @@ +/** + * @file statemachine_init.cpp + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2022 Tobias Anker + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use cluster 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 "statemachine_init.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +/** + * @brief initialize all possible states of the statemachine + * + * @param sm reference to the statemachine, which should be initialized + */ +void +initStates(Kitsunemimi::Statemachine &sm) +{ + sm.createNewState(TASK_STATE, "Task-handling mode"); + sm.createNewState(LEARN_STATE, "Learn-State"); + sm.createNewState(IMAGE_LEARN_STATE, "Image-learn state"); + sm.createNewState(IMAGE_LEARN_FORWARD_STATE, "Image-learn state: run"); + sm.createNewState(IMAGE_LEARN_CYCLE_FINISH_STATE, "Image-learn state: finish-cycle"); + sm.createNewState(TABLE_LEARN_STATE, "Table-learn state"); + sm.createNewState(TABLE_LEARN_FORWARD_STATE, "Table-learn state: run"); + sm.createNewState(TABLE_LEARN_CYCLE_FINISH_STATE, "Table-learn state: finish-cycle"); + sm.createNewState(REQUEST_STATE, "Request-State"); + sm.createNewState(IMAGE_REQUEST_STATE, "Image-request state"); + sm.createNewState(IMAGE_REQUEST_FORWARD_STATE, "Image-request state: forward-propagation"); + sm.createNewState(IMAGE_REQUEST_CYCLE_FINISH_STATE, "Image-request state: finish-cycle"); + sm.createNewState(TABLE_REQUEST_STATE, "Table-request state"); + sm.createNewState(TABLE_REQUEST_FORWARD_STATE, "Table-request state: forward-propagation"); + sm.createNewState(TABLE_REQUEST_CYCLE_FINISH_STATE, "Table-request state: finish-cycle"); + sm.createNewState(SNAPSHOT_STATE, "Snapshot state"); + sm.createNewState(CLUSTER_SNAPSHOT_STATE, "Cluster-snapshot state"); + sm.createNewState(CLUSTER_SNAPSHOT_SAVE_STATE, "Cluster-snapshot state: save"); + sm.createNewState(CLUSTER_SNAPSHOT_RESTORE_STATE, "Cluster-snapshot state: restore"); + sm.createNewState(DIRECT_STATE, "Direct mode"); +} + +/** + * @brief initialize events, which should be triggered for specific states + * + * @param sm reference to the statemachine, which should be initialized + * @param cluster pointer to the cluster, where the statemachine belongs to + * @param taskState pointer the the cluster-specific task-handling-state + */ +void +initEvents(Kitsunemimi::Statemachine &sm, + Cluster* cluster, + TaskHandle_State* taskState) +{ + sm.addEventToState(TASK_STATE, taskState); + sm.addEventToState(IMAGE_LEARN_FORWARD_STATE, new ImageLearnForward_State(cluster)); + sm.addEventToState(TABLE_LEARN_FORWARD_STATE, new TableLearnForward_State(cluster)); + sm.addEventToState(IMAGE_REQUEST_FORWARD_STATE, new ImageIdentify_State(cluster)); + sm.addEventToState(TABLE_REQUEST_FORWARD_STATE, new TableInterpolation_State(cluster)); + sm.addEventToState(IMAGE_LEARN_CYCLE_FINISH_STATE, new CycleFinish_State(cluster)); + sm.addEventToState(TABLE_LEARN_CYCLE_FINISH_STATE, new CycleFinish_State(cluster)); + sm.addEventToState(IMAGE_REQUEST_CYCLE_FINISH_STATE, new CycleFinish_State(cluster)); + sm.addEventToState(TABLE_REQUEST_CYCLE_FINISH_STATE, new CycleFinish_State(cluster)); + sm.addEventToState(CLUSTER_SNAPSHOT_SAVE_STATE, new SaveCluster_State(cluster)); + sm.addEventToState(CLUSTER_SNAPSHOT_RESTORE_STATE, new RestoreCluster_State(cluster)); +} + +/** + * @brief initialize child-states + * + * @param sm reference to the statemachine, which should be initialized + */ +void +initChildStates(Kitsunemimi::Statemachine &sm) +{ + // child states image learn + sm.addChildState(LEARN_STATE, IMAGE_LEARN_STATE); + sm.addChildState(IMAGE_LEARN_STATE, IMAGE_LEARN_FORWARD_STATE); + sm.addChildState(IMAGE_LEARN_STATE, IMAGE_LEARN_CYCLE_FINISH_STATE); + + // child states table learn + sm.addChildState(LEARN_STATE, TABLE_LEARN_STATE); + sm.addChildState(TABLE_LEARN_STATE, TABLE_LEARN_FORWARD_STATE); + sm.addChildState(TABLE_LEARN_STATE, TABLE_LEARN_CYCLE_FINISH_STATE); + + // child states image request + sm.addChildState(REQUEST_STATE, IMAGE_REQUEST_STATE); + sm.addChildState(IMAGE_REQUEST_STATE, IMAGE_REQUEST_FORWARD_STATE); + sm.addChildState(IMAGE_REQUEST_STATE, IMAGE_REQUEST_CYCLE_FINISH_STATE); + + // child states table request + sm.addChildState(REQUEST_STATE, TABLE_REQUEST_STATE); + sm.addChildState(TABLE_REQUEST_STATE, TABLE_REQUEST_FORWARD_STATE); + sm.addChildState(TABLE_REQUEST_STATE, TABLE_REQUEST_CYCLE_FINISH_STATE); + + // child states snapshot + sm.addChildState(SNAPSHOT_STATE, CLUSTER_SNAPSHOT_STATE); + sm.addChildState(CLUSTER_SNAPSHOT_STATE, CLUSTER_SNAPSHOT_SAVE_STATE); + sm.addChildState(CLUSTER_SNAPSHOT_STATE, CLUSTER_SNAPSHOT_RESTORE_STATE); +} + +/** + * @brief set initial + * + * @param sm reference to the statemachine, which should be initialized + */ +void +initInitialChildStates(Kitsunemimi::Statemachine &sm) +{ + sm.setInitialChildState(IMAGE_LEARN_STATE, IMAGE_LEARN_FORWARD_STATE); + sm.setInitialChildState(TABLE_LEARN_STATE, TABLE_LEARN_FORWARD_STATE); + sm.setInitialChildState(IMAGE_REQUEST_STATE, IMAGE_REQUEST_FORWARD_STATE); + sm.setInitialChildState(TABLE_REQUEST_STATE, TABLE_REQUEST_FORWARD_STATE); +} + +/** + * @brief initialize transitions between states + * + * @param sm reference to the statemachine, which should be initialized + */ +void +initTransitions(Kitsunemimi::Statemachine &sm) +{ + // transtions learn init + sm.addTransition(TASK_STATE, LEARN, LEARN_STATE); + sm.addTransition(LEARN_STATE, IMAGE, IMAGE_LEARN_STATE); + sm.addTransition(LEARN_STATE, TABLE, TABLE_LEARN_STATE); + + // transitions request init + sm.addTransition(TASK_STATE, REQUEST, REQUEST_STATE); + sm.addTransition(REQUEST_STATE, IMAGE, IMAGE_REQUEST_STATE); + sm.addTransition(REQUEST_STATE, TABLE, TABLE_REQUEST_STATE); + + // transitions snapshot init + sm.addTransition(TASK_STATE, SNAPSHOT, SNAPSHOT_STATE); + sm.addTransition(SNAPSHOT_STATE, CLUSTER, CLUSTER_SNAPSHOT_STATE); + sm.addTransition(CLUSTER_SNAPSHOT_STATE, SAVE, CLUSTER_SNAPSHOT_SAVE_STATE); + sm.addTransition(CLUSTER_SNAPSHOT_STATE, RESTORE, CLUSTER_SNAPSHOT_RESTORE_STATE); + + // trainsition learn-internal + sm.addTransition(IMAGE_LEARN_FORWARD_STATE, NEXT, IMAGE_LEARN_CYCLE_FINISH_STATE ); + sm.addTransition(IMAGE_LEARN_CYCLE_FINISH_STATE, NEXT, IMAGE_LEARN_FORWARD_STATE ); + sm.addTransition(TABLE_LEARN_FORWARD_STATE, NEXT, TABLE_LEARN_CYCLE_FINISH_STATE ); + sm.addTransition(TABLE_LEARN_CYCLE_FINISH_STATE, NEXT, TABLE_LEARN_FORWARD_STATE ); + + // trainsition request-internal + sm.addTransition(IMAGE_REQUEST_FORWARD_STATE, NEXT, IMAGE_REQUEST_CYCLE_FINISH_STATE ); + sm.addTransition(IMAGE_REQUEST_CYCLE_FINISH_STATE, NEXT, IMAGE_REQUEST_FORWARD_STATE ); + sm.addTransition(TABLE_REQUEST_FORWARD_STATE, NEXT, TABLE_REQUEST_CYCLE_FINISH_STATE ); + sm.addTransition(TABLE_REQUEST_CYCLE_FINISH_STATE, NEXT, TABLE_REQUEST_FORWARD_STATE ); + + // transition finish back to task-state + sm.addTransition(LEARN_STATE, FINISH_TASK, TASK_STATE); + sm.addTransition(REQUEST_STATE, FINISH_TASK, TASK_STATE); + sm.addTransition(SNAPSHOT_STATE, FINISH_TASK, TASK_STATE); + sm.addTransition(CLUSTER_SNAPSHOT_SAVE_STATE, FINISH_TASK, TASK_STATE); + sm.addTransition(CLUSTER_SNAPSHOT_RESTORE_STATE, FINISH_TASK, TASK_STATE); + + // special transition to tigger the task-state again + sm.addTransition(TASK_STATE, PROCESS_TASK, TASK_STATE); + + // mode-switches + sm.addTransition(TASK_STATE, SWITCH_TO_DIRECT_MODE, DIRECT_STATE); + sm.addTransition(DIRECT_STATE, SWITCH_TO_TASK_MODE, TASK_STATE); +} + +/** + * @brief initialize statemachine of the cluster + * + * @param sm reference to the statemachine, which should be initialized + * @param cluster pointer to the cluster, where the statemachine belongs to + * @param taskState pointer the the cluster-specific task-handling-state + */ +void +initStatemachine(Kitsunemimi::Statemachine &sm, + Cluster* cluster, + TaskHandle_State* taskState) +{ + initStates(sm); + initEvents(sm, cluster, taskState); + initChildStates(sm); + initInitialChildStates(sm); + initTransitions(sm); + + // set initial state for the state-machine + sm.setCurrentState(TASK_STATE); +} diff --git a/src/components/KyoukoMind/src/core/cluster/statemachine_init.h b/src/components/KyoukoMind/src/core/cluster/statemachine_init.h new file mode 100644 index 00000000..fd1ae14c --- /dev/null +++ b/src/components/KyoukoMind/src/core/cluster/statemachine_init.h @@ -0,0 +1,86 @@ +/** + * @file statemachine_init.h + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2022 Tobias Anker + * + * 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. + */ + +#ifndef STATEMACHINE_INIT_H +#define STATEMACHINE_INIT_H + +class TaskHandle_State; +class CycleFinish_State; +class TableInterpolation_State; +class TableLearnBackward_State; +class TableLearnForward_State; +class ImageIdentify_State; +class ImageLearnBackward_State; +class ImageLearnForward_State; +class Cluster; + +namespace Kitsunemimi { +class EventQueue; +class Statemachine; +} + +enum ClusterStates +{ + TASK_STATE = 0, + LEARN_STATE = 1, + IMAGE_LEARN_STATE = 2, + IMAGE_LEARN_FORWARD_STATE = 3, + IMAGE_LEARN_CYCLE_FINISH_STATE = 5, + TABLE_LEARN_STATE = 6, + TABLE_LEARN_FORWARD_STATE = 7, + TABLE_LEARN_CYCLE_FINISH_STATE = 9, + REQUEST_STATE = 10, + IMAGE_REQUEST_STATE = 11, + IMAGE_REQUEST_FORWARD_STATE = 12, + IMAGE_REQUEST_CYCLE_FINISH_STATE = 13, + TABLE_REQUEST_STATE = 14, + TABLE_REQUEST_FORWARD_STATE = 15, + TABLE_REQUEST_CYCLE_FINISH_STATE = 16, + SNAPSHOT_STATE = 17, + CLUSTER_SNAPSHOT_STATE = 18, + CLUSTER_SNAPSHOT_SAVE_STATE = 19, + CLUSTER_SNAPSHOT_RESTORE_STATE = 20, + DIRECT_STATE = 21, +}; + +enum ClusterTransitions +{ + LEARN = 100, + REQUEST = 101, + SNAPSHOT = 102, + IMAGE = 103, + TABLE = 104, + CLUSTER = 105, + SAVE = 106, + RESTORE = 107, + NEXT = 108, + FINISH_TASK = 109, + PROCESS_TASK = 110, + SWITCH_TO_DIRECT_MODE = 111, + SWITCH_TO_TASK_MODE = 112, +}; + +void initStatemachine(Kitsunemimi::Statemachine &sm, + Cluster* cluster, + TaskHandle_State* taskState); + +#endif // STATEMACHINE_INIT_H diff --git a/src/components/KyoukoMind/src/core/cluster/states/cycle_finish_state.cpp b/src/components/KyoukoMind/src/core/cluster/states/cycle_finish_state.cpp new file mode 100644 index 00000000..161e0c7b --- /dev/null +++ b/src/components/KyoukoMind/src/core/cluster/states/cycle_finish_state.cpp @@ -0,0 +1,82 @@ +/** + * @file cycle_finish_state.cpp + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2022 Tobias Anker + * + * 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 "cycle_finish_state.h" + +#include +#include +#include +#include + +/** + * @brief constructor + * + * @param cluster pointer to the cluster, where the event and the statemachine belongs to + */ +CycleFinish_State::CycleFinish_State(Cluster* cluster) +{ + m_cluster = cluster; +} + +/** + * @brief destructor + */ +CycleFinish_State::~CycleFinish_State() {} + +/** + * @brief prcess event + * + * @return alway true + */ +bool +CycleFinish_State::processEvent() +{ + Task* actualTask = m_cluster->getActualTask(); + DataValue* numberOfCyclesVal = actualTask->metaData.get("number_of_cycles")->toValue(); + const uint64_t numberOfCycles = numberOfCyclesVal->getLong(); + + // update progress-counter + actualTask->actualCycle++; + const float actualF = static_cast(actualTask->actualCycle); + const float shouldF = static_cast(numberOfCycles); + actualTask->progress.percentageFinished = actualF / shouldF; + + // to go next state of finish the task to goal is reached + if(actualTask->actualCycle == numberOfCycles) { + m_cluster->goToNextState(FINISH_TASK); + + /*DynamicSegment* segment = static_cast(m_cluster->coreSegments.begin()->second); + u_int64_t counter = 0; + for(uint64_t i = 0; i < segment->segmentHeader->synapseSections.count; i++) { + for(uint64_t j = 0; j < SYNAPSES_PER_SYNAPSESECTION; j++) { + counter += segment->synapseSections[i].synapses[j].targetNeuronId != UNINIT_STATE_16; + } + } + std::cout<<"============================================"<goToNextState(NEXT); + } + + return true; +} diff --git a/src/components/KyoukoMind/src/core/cluster/states/cycle_finish_state.h b/src/components/KyoukoMind/src/core/cluster/states/cycle_finish_state.h new file mode 100644 index 00000000..a151fcaa --- /dev/null +++ b/src/components/KyoukoMind/src/core/cluster/states/cycle_finish_state.h @@ -0,0 +1,43 @@ +/** + * @file cycle_finish_state.h + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2022 Tobias Anker + * + * 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. + */ + +#ifndef CYCLEFINISH_STATE_H +#define CYCLEFINISH_STATE_H + +#include + +class Cluster; + +class CycleFinish_State + : public Kitsunemimi::Event +{ +public: + CycleFinish_State(Cluster* cluster); + ~CycleFinish_State(); + + bool processEvent(); + +private: + Cluster* m_cluster = nullptr; +}; + +#endif // CYCLEFINISH_STATE_H diff --git a/src/components/KyoukoMind/src/core/cluster/states/graphs/table_interpolation_state.cpp b/src/components/KyoukoMind/src/core/cluster/states/graphs/table_interpolation_state.cpp new file mode 100644 index 00000000..62514084 --- /dev/null +++ b/src/components/KyoukoMind/src/core/cluster/states/graphs/table_interpolation_state.cpp @@ -0,0 +1,157 @@ +/** + * @file graph_interpolation_state.cpp + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2019 Tobias Anker + * + * 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 "graph_interpolation_state.h" + +#include +#include +#include + +#include + +/** + * @brief constructor + * + * @param cluster pointer to the cluster, where the event and the statemachine belongs to + */ +GraphInterpolation_State::GraphInterpolation_State(Cluster* cluster) +{ + m_cluster = cluster; +} + +/** + * @brief destructor + */ +GraphInterpolation_State::~GraphInterpolation_State() {} + +/** + * @brief prcess event + * + * @return alway true + */ +bool +GraphInterpolation_State::processEvent() +{ + + Task* actualTask = m_cluster->getActualTask(); + if(actualTask->isInit) + { + // set input + InputNode* inputNodes = m_cluster->inputSegments[0]->inputs; + for(uint64_t i = 4; i < 4*365; i++) { + inputNodes[i - 4].weight = inputNodes[i].weight; + } + + OutputNode* outputNodes = m_cluster->outputSegments[0]->outputs; + inputNodes[4*365 - 4].weight = outputNodes[0].outputWeight * outputNodes[0].maxWeight; + inputNodes[4*365 - 3].weight = outputNodes[1].outputWeight * outputNodes[1].maxWeight; + inputNodes[4*365 - 2].weight = outputNodes[2].outputWeight * outputNodes[2].maxWeight; + inputNodes[4*365 - 1].weight = outputNodes[3].outputWeight * outputNodes[3].maxWeight; + + //std::cout<<"x: "<<(outputNodes[0].outputWeight / 5.0f)<<" y: "<<(outputNodes[1].outputWeight / 5.0f)< val2) { + std::cout<<"x: "<<(val1 - val2)<<"\t y: "<<0.0f<getIntVal("number_of_inputs_per_cycle"); + const float* data = &actualTask->inputData[2 * numberInputCycles - 2*366]; + + //std::cout<<"------------------------actualTask->numberOfInputsPerCycle "<numberOfInputsPerCycle<actualCycle + 1]; + float actualVal = 0.0f; + float newVal = 0.0f; + uint64_t pos = 0; + + // set input + InputNode* inputNodes = m_cluster->inputSegments[0]->inputs; + for(uint64_t i = actualTask->actualCycle + 1; + i < actualTask->actualCycle + 1 + 365; + i++) + { + // open-part + actualVal = data[i * 2]; + newVal = (100.0f / actualVal) * lastVal; + if(newVal > 100.0f) + { + inputNodes[pos * 2].weight = newVal - 100.0f; + if(inputNodes[pos * 2].weight > 2.0f) { + inputNodes[pos * 2].weight = 2.0f; + } + inputNodes[pos * 2 + 1].weight = 0.0f; + } + else + { + inputNodes[pos * 2].weight = 0.0f; + inputNodes[pos * 2 + 1].weight = 100.0f - newVal; + if(inputNodes[pos * 2+1].weight > 2.0f) { + inputNodes[pos * 2+1].weight = 2.0f; + } + } + lastVal = actualVal; + pos++; + + // close-part + actualVal = data[i * 2 + 1]; + newVal = (100.0f / actualVal) * lastVal; + if(newVal > 100.0f) + { + inputNodes[pos * 2].weight = newVal - 100.0f; + if(inputNodes[pos * 2].weight > 2.0f) { + inputNodes[pos * 2].weight = 2.0f; + } + inputNodes[pos * 2 + 1].weight = 0.0f; + } + else + { + inputNodes[pos * 2].weight = 0.0f; + inputNodes[pos * 2 + 1].weight = 100.0f - newVal; + if(inputNodes[pos * 2+1].weight > 2.0f) { + inputNodes[pos * 2+1].weight = 2.0f; + } + } + lastVal = actualVal; + pos++; + } + } + + actualTask->isInit = true; + + m_cluster->mode = Cluster::NORMAL_MODE; + m_cluster->startForwardCycle(); + + return true; +} diff --git a/src/components/KyoukoMind/src/core/cluster/states/graphs/table_interpolation_state.h b/src/components/KyoukoMind/src/core/cluster/states/graphs/table_interpolation_state.h new file mode 100644 index 00000000..e462e678 --- /dev/null +++ b/src/components/KyoukoMind/src/core/cluster/states/graphs/table_interpolation_state.h @@ -0,0 +1,43 @@ +/** + * @file graph_interpolation_state.h + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2019 Tobias Anker + * + * 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. + */ + +#ifndef GRAPHINTERPOLATION_STATE_H +#define GRAPHINTERPOLATION_STATE_H + +#include + +class Cluster; + +class GraphInterpolation_State + : public Kitsunemimi::Event +{ +public: + GraphInterpolation_State(Cluster* cluster); + ~GraphInterpolation_State(); + + bool processEvent(); + +private: + Cluster* m_cluster = nullptr; +}; + +#endif // GRAPHINTERPOLATION_STATE_H diff --git a/src/components/KyoukoMind/src/core/cluster/states/graphs/table_learn_forward_state.cpp b/src/components/KyoukoMind/src/core/cluster/states/graphs/table_learn_forward_state.cpp new file mode 100644 index 00000000..aa7ed44a --- /dev/null +++ b/src/components/KyoukoMind/src/core/cluster/states/graphs/table_learn_forward_state.cpp @@ -0,0 +1,150 @@ +/** + * @file graph_learn_forward_state.cpp + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2019 Tobias Anker + * + * 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 "graph_learn_forward_state.h" + +#include +#include +#include + +#include + +/** + * @brief constructor + * + * @param cluster pointer to the cluster, where the event and the statemachine belongs to + */ +GraphLearnForward_State::GraphLearnForward_State(Cluster* cluster) +{ + m_cluster = cluster; +} + +/** + * @brief destructor + */ +GraphLearnForward_State::~GraphLearnForward_State() {} + +/** + * @brief prcess event + * + * @return alway true + */ +bool +GraphLearnForward_State::processEvent() +{ + Task* actualTask = m_cluster->getActualTask(); + + float lastVal = actualTask->inputData[actualTask->actualCycle + 1]; + + float actualVal = 0.0f; + float newVal = 0.0f; + uint64_t pos = 0; + + // set input + InputNode* inputNodes = m_cluster->inputSegments.begin()->second->inputs; + OutputNode* outputNodes = m_cluster->outputSegments.begin()->second->outputs; + uint64_t i = actualTask->actualCycle + 1; + + while(i < actualTask->actualCycle + 1 + 365) + { + // open-part + actualVal = actualTask->inputData[i * 2]; + newVal = (100.0f / actualVal) * lastVal; + + if(newVal > 100.0f) + { + inputNodes[pos * 2].weight = newVal - 100.0f; + if(inputNodes[pos * 2].weight > 2.0f) { + inputNodes[pos * 2].weight = 2.0f; + } + inputNodes[pos * 2 + 1].weight = 0.0f; + } + else + { + inputNodes[pos * 2].weight = 0.0f; + inputNodes[pos * 2 + 1].weight = 100.0f - newVal; + if(inputNodes[pos * 2+1].weight > 2.0f) { + inputNodes[pos * 2+1].weight = 2.0f; + } + } + + lastVal = actualVal; + pos++; + + // close-part + actualVal = actualTask->inputData[i * 2 + 1]; + newVal = (100.0f / actualVal) * lastVal; + + if(newVal > 100.0f) + { + inputNodes[pos * 2].weight = newVal - 100.0f; + if(inputNodes[pos * 2].weight > 2.0f) { + inputNodes[pos * 2].weight = 2.0f; + } + inputNodes[pos * 2 + 1].weight = 0.0f; + } + else + { + inputNodes[pos * 2].weight = 0.0f; + inputNodes[pos * 2 + 1].weight = 100.0f - newVal; + if(inputNodes[pos * 2+1].weight > 2.0f) { + inputNodes[pos * 2+1].weight = 2.0f; + } + } + + lastVal = actualVal; + pos++; + + i++; + } + + // set exprected output + actualVal = actualTask->inputData[i * 2]; + if(actualVal > lastVal) + { + outputNodes[0].shouldValue = 1.0f; + outputNodes[1].shouldValue = 0.0f; + } + else + { + outputNodes[0].shouldValue = 0.0f; + outputNodes[1].shouldValue = 1.0f; + } + lastVal = actualVal; + + actualVal = actualTask->inputData[i * 2 + 1]; + if(actualVal > lastVal) + { + outputNodes[2].shouldValue = 1.0f; + outputNodes[3].shouldValue = 0.0f; + } + else + { + outputNodes[2].shouldValue = 0.0f; + outputNodes[3].shouldValue = 1.0f; + } + + m_cluster->mode = Cluster::LEARN_FORWARD_MODE; + m_cluster->startForwardCycle(); + + return true; +} diff --git a/src/components/KyoukoMind/src/core/cluster/states/graphs/table_learn_forward_state.h b/src/components/KyoukoMind/src/core/cluster/states/graphs/table_learn_forward_state.h new file mode 100644 index 00000000..cc3aea86 --- /dev/null +++ b/src/components/KyoukoMind/src/core/cluster/states/graphs/table_learn_forward_state.h @@ -0,0 +1,43 @@ +/** + * @file graph_learn_forward_state.h + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2019 Tobias Anker + * + * 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. + */ + +#ifndef GRAPHLEARNFORWARD_STATE_H +#define GRAPHLEARNFORWARD_STATE_H + +#include + +class Cluster; + +class GraphLearnForward_State + : public Kitsunemimi::Event +{ +public: + GraphLearnForward_State(Cluster* cluster); + ~GraphLearnForward_State(); + + bool processEvent(); + +private: + Cluster* m_cluster = nullptr; +}; + +#endif // GRAPHLEARNFORWARD_STATE_H diff --git a/src/components/KyoukoMind/src/core/cluster/states/images/image_identify_state.cpp b/src/components/KyoukoMind/src/core/cluster/states/images/image_identify_state.cpp new file mode 100644 index 00000000..d3c76775 --- /dev/null +++ b/src/components/KyoukoMind/src/core/cluster/states/images/image_identify_state.cpp @@ -0,0 +1,70 @@ +/** + * @file image_identify_state.cpp + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2022 Tobias Anker + * + * 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 "image_identify_state.h" + +#include +#include +#include + +#include + +/** + * @brief constructor + * + * @param cluster pointer to the cluster, where the event and the statemachine belongs to + */ +ImageIdentify_State::ImageIdentify_State(Cluster* cluster) +{ + m_cluster = cluster; +} + +/** + * @brief destructor + */ +ImageIdentify_State::~ImageIdentify_State() {} + +/** + * @brief prcess event + * + * @return alway true + */ +bool +ImageIdentify_State::processEvent() +{ + Task* actualTask = m_cluster->getActualTask(); + const uint64_t numberOfInputsPerCycle = actualTask->getIntVal("number_of_inputs_per_cycle"); + const uint64_t numberOfOuputsPerCycle = actualTask->getIntVal("number_of_outputs_per_cycle"); + const uint64_t entriesPerCycle = numberOfInputsPerCycle + numberOfOuputsPerCycle; + const uint64_t offsetInput = entriesPerCycle * actualTask->actualCycle; + + // set input + InputNeuron* inputNeurons = m_cluster->inputSegments.begin()->second->inputs; + for(uint64_t i = 0; i < numberOfInputsPerCycle; i++) { + inputNeurons[i].weight = actualTask->inputData[offsetInput + i]; + } + + m_cluster->mode = Cluster::NORMAL_MODE; + m_cluster->startForwardCycle(); + + return true; +} diff --git a/src/components/KyoukoMind/src/core/cluster/states/images/image_identify_state.h b/src/components/KyoukoMind/src/core/cluster/states/images/image_identify_state.h new file mode 100644 index 00000000..652b2768 --- /dev/null +++ b/src/components/KyoukoMind/src/core/cluster/states/images/image_identify_state.h @@ -0,0 +1,43 @@ +/** + * @file image_identify_state.h + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2022 Tobias Anker + * + * 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. + */ + +#ifndef IMAGEIDENTIFY_STATE_H +#define IMAGEIDENTIFY_STATE_H + +#include + +class Cluster; + +class ImageIdentify_State + : public Kitsunemimi::Event +{ +public: + ImageIdentify_State(Cluster* cluster); + ~ImageIdentify_State(); + + bool processEvent(); + +private: + Cluster* m_cluster = nullptr; +}; + +#endif // IMAGEIDENTIFY_STATE_H diff --git a/src/components/KyoukoMind/src/core/cluster/states/images/image_learn_forward_state.cpp b/src/components/KyoukoMind/src/core/cluster/states/images/image_learn_forward_state.cpp new file mode 100644 index 00000000..6e36f088 --- /dev/null +++ b/src/components/KyoukoMind/src/core/cluster/states/images/image_learn_forward_state.cpp @@ -0,0 +1,78 @@ +/** + * @file image_learn_forward_state.cpp + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2022 Tobias Anker + * + * 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 "image_learn_forward_state.h" + +#include +#include +#include + +#include + +/** + * @brief constructor + * + * @param cluster pointer to the cluster, where the event and the statemachine belongs to + */ +ImageLearnForward_State::ImageLearnForward_State(Cluster* cluster) +{ + m_cluster = cluster; +} + +/** + * @brief destructor + */ +ImageLearnForward_State::~ImageLearnForward_State() {} + +/** + * @brief prcess event + * + * @return alway true + */ +bool +ImageLearnForward_State::processEvent() +{ + Task* actualTask = m_cluster->getActualTask(); + const uint64_t numberOfInputsPerCycle = actualTask->getIntVal("number_of_inputs_per_cycle"); + const uint64_t numberOfOuputsPerCycle = actualTask->getIntVal("number_of_outputs_per_cycle"); + const uint64_t entriesPerCycle = numberOfInputsPerCycle + numberOfOuputsPerCycle; + const uint64_t offsetInput = entriesPerCycle * actualTask->actualCycle; + + // set input + InputNeuron* inputNeurons = m_cluster->inputSegments.begin()->second->inputs; + for(uint64_t i = 0; i < numberOfInputsPerCycle; i++) { + inputNeurons[i].weight = actualTask->inputData[offsetInput + i]; + } + + // set exprected output + OutputNeuron* outputNeurons = m_cluster->outputSegments.begin()->second->outputs; + for(uint64_t i = 0; i < numberOfOuputsPerCycle; i++) + { + const uint64_t numberOfCycles = numberOfInputsPerCycle; + outputNeurons[i].shouldValue = actualTask->inputData[offsetInput + numberOfCycles + i]; + } + + m_cluster->mode = Cluster::LEARN_FORWARD_MODE; + m_cluster->startForwardCycle(); + + return true; +} diff --git a/src/components/KyoukoMind/src/core/cluster/states/images/image_learn_forward_state.h b/src/components/KyoukoMind/src/core/cluster/states/images/image_learn_forward_state.h new file mode 100644 index 00000000..491c7e90 --- /dev/null +++ b/src/components/KyoukoMind/src/core/cluster/states/images/image_learn_forward_state.h @@ -0,0 +1,43 @@ +/** + * @file image_learn_forward_state.h + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2022 Tobias Anker + * + * 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. + */ + +#ifndef IMAGELEARNFORWARD_STATE_H +#define IMAGELEARNFORWARD_STATE_H + +#include + +class Cluster; + +class ImageLearnForward_State + : public Kitsunemimi::Event +{ +public: + ImageLearnForward_State(Cluster* cluster); + ~ImageLearnForward_State(); + + bool processEvent(); + +private: + Cluster* m_cluster = nullptr; +}; + +#endif // IMAGELEARNFORWARD_STATE_H diff --git a/src/components/KyoukoMind/src/core/cluster/states/snapshots/restore_cluster_state.cpp b/src/components/KyoukoMind/src/core/cluster/states/snapshots/restore_cluster_state.cpp new file mode 100644 index 00000000..1992b19c --- /dev/null +++ b/src/components/KyoukoMind/src/core/cluster/states/snapshots/restore_cluster_state.cpp @@ -0,0 +1,184 @@ +/** + * @file restore_cluster_state.cpp + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2022 Tobias Anker + * + * 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 "restore_cluster_state.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include + +#include + +using Kitsunemimi::Hanami::HanamiMessaging; +using Kitsunemimi::Hanami::HanamiMessagingClient; + +/** + * @brief constructor + * + * @param cluster pointer to the cluster, where the event and the statemachine belongs to + */ +RestoreCluster_State::RestoreCluster_State(Cluster* cluster) +{ + m_cluster = cluster; +} + +/** + * @brief destructor + */ +RestoreCluster_State::~RestoreCluster_State() {} + +/** + * @brief prcess event + * + * @return alway true + */ +bool +RestoreCluster_State::processEvent() +{ + Task* actualTask = m_cluster->getActualTask(); + Kitsunemimi::ErrorContainer error; + const std::string originalUuid = m_cluster->getUuid(); + + // get client to shiori + HanamiMessaging* messaging = HanamiMessaging::getInstance(); + m_client = messaging->shioriClient; + if(m_client == nullptr) + { + error.addMeesage("Failed to get client to shiori"); + error.addSolution("Check if shiori is correctly configured"); + m_cluster->goToNextState(FINISH_TASK); + return false; + } + + // get meta-infos of data-set from shiori + const std::string snapshotInfo = actualTask->metaData.get("snapshot_info")->getString(); + JsonItem parsedSnapshotInfo; + parsedSnapshotInfo.parse(snapshotInfo, error); + + // get other information + const std::string snapshotUuid = parsedSnapshotInfo.get("uuid").getString(); + const std::string location = parsedSnapshotInfo.get("location").toString(); + const std::string userId = actualTask->metaData.get("user_id")->getString(); + const std::string projectId = actualTask->metaData.get("project_id")->getString(); + + // get header + const std::string header = parsedSnapshotInfo.get("header").toString(); + JsonItem parsedHeader; + if(parsedHeader.parse(header, error) == false) + { + m_cluster->goToNextState(FINISH_TASK); + return false; + } + + // get snapshot-data + DataBuffer* snapshotBuffer = Shiori::getSnapshotData(location, error); + if(snapshotBuffer == nullptr) + { + error.addMeesage("failed to get snapshot-data from shiori"); + m_cluster->goToNextState(FINISH_TASK); + return false; + } + + // clrear all old segments of the cluster, if there are some exist + for(uint64_t i = 0; i < m_cluster->allSegments.size(); i++) + { + AbstractSegment* segment = m_cluster->allSegments.at(i); + delete segment; + } + m_cluster->allSegments.clear(); + m_cluster->inputSegments.clear(); + m_cluster->outputSegments.clear(); + + // copy meta-data of cluster + const uint64_t headerSize = parsedHeader.get("header").getLong(); + if(Kitsunemimi::reset_DataBuffer(m_cluster->clusterData, + Kitsunemimi::calcBytesToBlocks(headerSize)) == false) + { + // TODO: handle error + m_cluster->goToNextState(FINISH_TASK); + return false; + } + const uint8_t* u8Data = static_cast(snapshotBuffer->data); + memcpy(m_cluster->clusterData.data, &u8Data[0], headerSize); + reinitPointer(m_cluster, originalUuid); + + // copy single segments + uint64_t posCounter = headerSize; + for(uint64_t i = 0; i < parsedHeader.get("segments").size(); i++) + { + JsonItem segment = parsedHeader.get("segments").get(i); + const SegmentTypes type = static_cast(segment.get("type").getInt()); + const uint64_t size = static_cast(segment.get("size").getLong()); + + switch(type) + { + case INPUT_SEGMENT: + { + InputSegment* newSegment = new InputSegment(&u8Data[posCounter], size); + newSegment->reinitPointer(size); + newSegment->parentCluster = m_cluster; + m_cluster->inputSegments.insert(std::make_pair(newSegment->getName(), newSegment)); + m_cluster->allSegments.push_back(newSegment); + break; + } + case OUTPUT_SEGMENT: + { + OutputSegment* newSegment = new OutputSegment(&u8Data[posCounter], size); + newSegment->reinitPointer(size); + newSegment->parentCluster = m_cluster; + m_cluster->outputSegments.insert(std::make_pair(newSegment->getName(), newSegment)); + m_cluster->allSegments.push_back(newSegment); + break; + } + case DYNAMIC_SEGMENT: + { + DynamicSegment* newSegment = new DynamicSegment(&u8Data[posCounter], size); + newSegment->reinitPointer(size); + newSegment->parentCluster = m_cluster; + m_cluster->allSegments.push_back(newSegment); + break; + } + case UNDEFINED_SEGMENT: + { + break; + } + } + + posCounter += size; + } + + delete snapshotBuffer; + + m_cluster->goToNextState(FINISH_TASK); + + return true; +} diff --git a/src/components/KyoukoMind/src/core/cluster/states/snapshots/restore_cluster_state.h b/src/components/KyoukoMind/src/core/cluster/states/snapshots/restore_cluster_state.h new file mode 100644 index 00000000..5f98f83c --- /dev/null +++ b/src/components/KyoukoMind/src/core/cluster/states/snapshots/restore_cluster_state.h @@ -0,0 +1,51 @@ +/** + * @file restore_cluster_state.h + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2022 Tobias Anker + * + * 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. + */ + +#ifndef RESTORECLUSTERSTATE_H +#define RESTORECLUSTERSTATE_H + +#include +#include + +class Cluster; + +namespace Kitsunemimi { +namespace Hanami { +class HanamiMessagingClient; +} +} + +class RestoreCluster_State + : public Kitsunemimi::Event +{ +public: + RestoreCluster_State(Cluster* cluster); + ~RestoreCluster_State(); + + bool processEvent(); + +private: + Cluster* m_cluster = nullptr; + Kitsunemimi::Hanami::HanamiMessagingClient* m_client = nullptr; +}; + +#endif // RESTORECLUSTERSTATE_H diff --git a/src/components/KyoukoMind/src/core/cluster/states/snapshots/save_cluster_state.cpp b/src/components/KyoukoMind/src/core/cluster/states/snapshots/save_cluster_state.cpp new file mode 100644 index 00000000..a3bd30af --- /dev/null +++ b/src/components/KyoukoMind/src/core/cluster/states/snapshots/save_cluster_state.cpp @@ -0,0 +1,187 @@ +/** + * @file save_cluster_state.cpp + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2022 Tobias Anker + * + * 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 "save_cluster_state.h" + +#include +#include +#include +#include + +#include +#include + +#include + +#include + +using Kitsunemimi::Hanami::HanamiMessaging; +using Kitsunemimi::Hanami::HanamiMessagingClient; + +/** + * @brief constructor + * + * @param cluster pointer to the cluster, where the event and the statemachine belongs to + */ +SaveCluster_State::SaveCluster_State(Cluster* cluster) +{ + m_cluster = cluster; +} + +/** + * @brief destructor + */ +SaveCluster_State::~SaveCluster_State() {} + +/** + * @brief prcess event + * + * @return true, if successful, else false + */ +bool +SaveCluster_State::processEvent() +{ + bool result = false; + Kitsunemimi::ErrorContainer error; + + do + { + Task* actualTask = m_cluster->getActualTask(); + uint64_t totalSize = 0; + std::string headerMessage = ""; + + // create message to shiori and calculate total size of storage of the cluster + totalSize = m_cluster->clusterData.usedBufferSize; + headerMessage = "{\"header\":" + std::to_string(totalSize) + ",\"segments\":["; + for(uint64_t i = 0; i < m_cluster->allSegments.size(); i++) + { + if(i != 0) { + headerMessage += ","; + } + const uint64_t segSize = m_cluster->allSegments.at(i)->segmentData.buffer.usedBufferSize; + headerMessage += "{\"size\":" + + std::to_string(segSize) + + ",\"type\":" + + std::to_string(m_cluster->allSegments.at(i)->getType()) + + "}"; + totalSize += segSize; + } + headerMessage += "]}"; + + // send snapshot to shiori + std::string fileUuid = ""; + if(Shiori::runSnapshotInitProcess(fileUuid, + actualTask->uuid.toString(), + actualTask->metaData.getStringByKey("snapshot_name"), + actualTask->userId, + actualTask->projectId, + totalSize, + headerMessage, + *KyoukoRoot::componentToken, + error) == false) + { + error.addMeesage("Failed to run initializing a snapshot-transfer to shiori"); + break; + } + + if(sendData(actualTask->uuid.toString(), + fileUuid, + error) == false) + { + error.addMeesage("Failed to send data of snapshot to shiori"); + break; + } + + if(Shiori::runSnapshotFinalizeProcess(actualTask->uuid.toString(), + fileUuid, + *KyoukoRoot::componentToken, + actualTask->userId, + actualTask->projectId, + error) == false) + { + error.addMeesage("Failed to run finalizing a snapshot-transfer to shiori"); + break; + } + + result = true; + break; + } + while(true); + + m_cluster->goToNextState(FINISH_TASK); + + if(result == false) + { + error.addMeesage("Failed to create snapshot of cluster with UUID '" + + m_cluster->getUuid() + + "'"); + // TODO: give the user a feedback by setting the task to failed-state + } + + return result; +} + +/** + * @brief send all data of the snapshot to shiori + * + * @param error reference for error-output + * + * @return true, if successful, else false + */ +bool +SaveCluster_State::sendData(const std::string &snapshotUuid, + const std::string &fileUuid, + Kitsunemimi::ErrorContainer &error) +{ + // global byte-counter to identifiy the position within the complete snapshot + uint64_t posCounter = 0; + + // send cluster-metadata + if(Shiori::sendData(&m_cluster->clusterData, + posCounter, + snapshotUuid, + fileUuid, + error) == false) + { + error.addMeesage("Failed to send metadata of cluster for snapshot to shiori"); + return false; + } + + // send segments of cluster + for(uint64_t i = 0; i < m_cluster->allSegments.size(); i++) + { + if(Shiori::sendData(&m_cluster->allSegments.at(i)->segmentData.buffer, + posCounter, + snapshotUuid, + fileUuid, + error) == false) + { + error.addMeesage("Failed to send snapshot of segment '" + + std::to_string(i) + + "' to shiori"); + return false; + } + } + + return true; +} + diff --git a/src/components/KyoukoMind/src/core/cluster/states/snapshots/save_cluster_state.h b/src/components/KyoukoMind/src/core/cluster/states/snapshots/save_cluster_state.h new file mode 100644 index 00000000..a1dbd86e --- /dev/null +++ b/src/components/KyoukoMind/src/core/cluster/states/snapshots/save_cluster_state.h @@ -0,0 +1,54 @@ +/** + * @file save_cluster_state.h + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2022 Tobias Anker + * + * 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. + */ + +#ifndef SAVECLUSTERSTATE_H +#define SAVECLUSTERSTATE_H + +#include +#include + +class Cluster; + +namespace Kitsunemimi { +namespace Hanami { +class HanamiMessagingClient; +} +} + +class SaveCluster_State + : public Kitsunemimi::Event +{ +public: + SaveCluster_State(Cluster* cluster); + ~SaveCluster_State(); + + bool processEvent(); + +private: + Cluster* m_cluster = nullptr; + + bool sendData(const std::string &snapshotUuid, + const std::string &fileUuid, + Kitsunemimi::ErrorContainer &error); +}; + +#endif // SAVECLUSTERSTATE_H diff --git a/src/components/KyoukoMind/src/core/cluster/states/tables/table_interpolation_state.cpp b/src/components/KyoukoMind/src/core/cluster/states/tables/table_interpolation_state.cpp new file mode 100644 index 00000000..d129c3fc --- /dev/null +++ b/src/components/KyoukoMind/src/core/cluster/states/tables/table_interpolation_state.cpp @@ -0,0 +1,75 @@ +/** + * @file table_interpolation_state.cpp + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2022 Tobias Anker + * + * 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 "table_interpolation_state.h" + +#include +#include +#include + +#include + +/** + * @brief constructor + * + * @param cluster pointer to the cluster, where the event and the statemachine belongs to + */ +TableInterpolation_State::TableInterpolation_State(Cluster* cluster) +{ + m_cluster = cluster; +} + +/** + * @brief destructor + */ +TableInterpolation_State::~TableInterpolation_State() {} + +/** + * @brief prcess event + * + * @return alway true + */ +bool +TableInterpolation_State::processEvent() +{ + + Task* actualTask = m_cluster->getActualTask(); + const uint64_t numberOfInputsPerCycle = actualTask->getIntVal("number_of_inputs_per_cycle"); + const uint64_t numberOfOuputsPerCycle = actualTask->getIntVal("number_of_outputs_per_cycle"); + uint64_t offset = actualTask->actualCycle; + if(numberOfInputsPerCycle > numberOfOuputsPerCycle) { + offset += numberOfInputsPerCycle; + } else { + offset += numberOfOuputsPerCycle; + } + + // set input + InputNeuron* inputNeurons = m_cluster->inputSegments.begin()->second->inputs; + for(uint64_t i = 0; i < numberOfInputsPerCycle; i++) { + inputNeurons[i].weight = actualTask->inputData[(offset - numberOfInputsPerCycle) + i]; + } + + m_cluster->mode = Cluster::NORMAL_MODE; + m_cluster->startForwardCycle(); + + return true; +} diff --git a/src/components/KyoukoMind/src/core/cluster/states/tables/table_interpolation_state.h b/src/components/KyoukoMind/src/core/cluster/states/tables/table_interpolation_state.h new file mode 100644 index 00000000..8569aac2 --- /dev/null +++ b/src/components/KyoukoMind/src/core/cluster/states/tables/table_interpolation_state.h @@ -0,0 +1,43 @@ +/** + * @file table_interpolation_state.h + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2022 Tobias Anker + * + * 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. + */ + +#ifndef TABLEINTERPOLATION_STATE_H +#define TABLEINTERPOLATION_STATE_H + +#include + +class Cluster; + +class TableInterpolation_State + : public Kitsunemimi::Event +{ +public: + TableInterpolation_State(Cluster* cluster); + ~TableInterpolation_State(); + + bool processEvent(); + +private: + Cluster* m_cluster = nullptr; +}; + +#endif // TABLEINTERPOLATION_STATE_H diff --git a/src/components/KyoukoMind/src/core/cluster/states/tables/table_learn_forward_state.cpp b/src/components/KyoukoMind/src/core/cluster/states/tables/table_learn_forward_state.cpp new file mode 100644 index 00000000..91e12dd9 --- /dev/null +++ b/src/components/KyoukoMind/src/core/cluster/states/tables/table_learn_forward_state.cpp @@ -0,0 +1,80 @@ +/** + * @file table_learn_forward_state.cpp + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2022 Tobias Anker + * + * 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 "table_learn_forward_state.h" + +#include +#include +#include + +#include + +/** + * @brief constructor + * + * @param cluster pointer to the cluster, where the event and the statemachine belongs to + */ +TableLearnForward_State::TableLearnForward_State(Cluster* cluster) +{ + m_cluster = cluster; +} + +/** + * @brief destructor + */ +TableLearnForward_State::~TableLearnForward_State() {} + +/** + * @brief prcess event + * + * @return alway true + */ +bool +TableLearnForward_State::processEvent() +{ + Task* actualTask = m_cluster->getActualTask(); + const uint64_t numberOfInputsPerCycle = actualTask->getIntVal("number_of_inputs_per_cycle"); + const uint64_t numberOfOuputsPerCycle = actualTask->getIntVal("number_of_outputs_per_cycle"); + uint64_t offset = actualTask->actualCycle; + if(numberOfInputsPerCycle > numberOfOuputsPerCycle) { + offset += numberOfInputsPerCycle; + } else { + offset += numberOfOuputsPerCycle; + } + + // set input + InputNeuron* inputNeurons = m_cluster->inputSegments.begin()->second->inputs; + for(uint64_t i = 0; i < numberOfInputsPerCycle; i++) { + inputNeurons[i].weight = actualTask->inputData[(offset - numberOfInputsPerCycle) + i]; + } + + // set exprected output + OutputNeuron* outputNeurons = m_cluster->outputSegments.begin()->second->outputs; + for(uint64_t i = 0; i < numberOfOuputsPerCycle; i++) { + outputNeurons[i].shouldValue = actualTask->outputData[(offset - numberOfOuputsPerCycle) + i]; + } + + m_cluster->mode = Cluster::LEARN_FORWARD_MODE; + m_cluster->startForwardCycle(); + + return true; +} diff --git a/src/components/KyoukoMind/src/core/cluster/states/tables/table_learn_forward_state.h b/src/components/KyoukoMind/src/core/cluster/states/tables/table_learn_forward_state.h new file mode 100644 index 00000000..cbf45ff1 --- /dev/null +++ b/src/components/KyoukoMind/src/core/cluster/states/tables/table_learn_forward_state.h @@ -0,0 +1,43 @@ +/** + * @file table_learn_forward_state.h + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2022 Tobias Anker + * + * 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. + */ + +#ifndef TABLELEARNFORWARD_STATE_H +#define TABLELEARNFORWARD_STATE_H + +#include + +class Cluster; + +class TableLearnForward_State + : public Kitsunemimi::Event +{ +public: + TableLearnForward_State(Cluster* cluster); + ~TableLearnForward_State(); + + bool processEvent(); + +private: + Cluster* m_cluster = nullptr; +}; + +#endif // TABLELEARNFORWARD_STATE_H diff --git a/src/components/KyoukoMind/src/core/cluster/states/task_handle_state.cpp b/src/components/KyoukoMind/src/core/cluster/states/task_handle_state.cpp new file mode 100644 index 00000000..3bf19f46 --- /dev/null +++ b/src/components/KyoukoMind/src/core/cluster/states/task_handle_state.cpp @@ -0,0 +1,403 @@ +/** + * @file task_handle_state.cpp + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2022 Tobias Anker + * + * 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 "task_handle_state.h" + +#include +#include + +#include +#include + +/** + * @brief constructor + * + * @param cluster pointer to the cluster, where the event and the statemachine belongs to + */ +TaskHandle_State::TaskHandle_State(Cluster* cluster) +{ + m_cluster = cluster; +} + +/** + * @brief destructor + */ +TaskHandle_State::~TaskHandle_State() {} + +/** + * @brief prcess event + * + * @return false, if statechange failed, else true + */ +bool +TaskHandle_State::processEvent() +{ + Kitsunemimi::ErrorContainer error; + m_task_mutex.lock(); + finishTask(); + const bool hasNextState = getNextTask(); + m_task_mutex.unlock(); + + // handle empty queue + if(hasNextState == false) + { + Azuki::setSpeedToMinimum(error); + return true; + } + + switch(actualTask->type) + { + case IMAGE_LEARN_TASK: + { + if(m_cluster->goToNextState(LEARN)) { + m_cluster->goToNextState(IMAGE); + Azuki::setSpeedToAutomatic(error); + } else { + // TODO: error-message + return false; + } + break; + } + case IMAGE_REQUEST_TASK: + { + if(m_cluster->goToNextState(REQUEST)) { + m_cluster->goToNextState(IMAGE); + Azuki::setSpeedToAutomatic(error); + } else { + // TODO: error-message + return false; + } + break; + } + case TABLE_LEARN_TASK: + { + if(m_cluster->goToNextState(LEARN)) { + m_cluster->goToNextState(TABLE); + Azuki::setSpeedToAutomatic(error); + } else { + // TODO: error-message + return false; + } + break; + } + case TABLE_REQUEST_TASK: + { + if(m_cluster->goToNextState(REQUEST)) { + m_cluster->goToNextState(TABLE); + Azuki::setSpeedToAutomatic(error); + } else { + // TODO: error-message + return false; + } + break; + } + case CLUSTER_SNAPSHOT_SAVE_TASK: + { + if(m_cluster->goToNextState(SNAPSHOT)) { + if(m_cluster->goToNextState(CLUSTER)) { + m_cluster->goToNextState(SAVE); + Azuki::setSpeedToAutomatic(error); + } else { + // TODO: error-message + return false; + } + } else { + // TODO: error-message + return false; + } + break; + } + case CLUSTER_SNAPSHOT_RESTORE_TASK: + { + if(m_cluster->goToNextState(SNAPSHOT)) { + if(m_cluster->goToNextState(CLUSTER)) { + m_cluster->goToNextState(RESTORE); + Azuki::setSpeedToAutomatic(error); + } else { + // TODO: error-message + return false; + } + } else { + // TODO: error-message + return false; + } + break; + } + default: { + // TODO: error-message + Azuki::setSpeedToMinimum(error); + return false; + } + } + + return true; +} + +/** + * @brief add new task + * + * @param uuid uuid of the new task for identification + * @param task task itself + * + * @return alsways true + */ +bool +TaskHandle_State::addTask(const std::string &uuid, + const Task &task) +{ + std::lock_guard guard(m_task_mutex); + + // TODO: check if uuid already exist (update comment) + m_taskMap.insert(std::make_pair(uuid, task)); + m_taskQueue.push_back(uuid); + + return true; +} + +/** + * @brief get pointer to the actual active task + * + * @return pointer to the actual task of nullptr, if no task is active at the moment + */ +Task* +TaskHandle_State::getActualTask() +{ + std::lock_guard guard(m_task_mutex); + + return actualTask; +} + +/** + * @brief run next task from the queue + * + * @return false, if task-queue if empty, else true + */ +bool +TaskHandle_State::getNextTask() +{ + // check number of tasks in queue + if(m_taskQueue.size() == 0) { + return false; + } + + // remove task from queue + const std::string nextUuid = m_taskQueue.front(); + m_taskQueue.pop_front(); + + // init the new task + std::map::iterator it; + it = m_taskMap.find(nextUuid); + it->second.progress.state = ACTIVE_TASK_STATE; + it->second.progress.startActiveTimeStamp = std::chrono::system_clock::now(); + actualTask = &it->second; + + return true; +} + +/** + * @brief finish actual task + */ +void +TaskHandle_State::finishTask() +{ + // precheck + if(actualTask == nullptr) { + return; + } + + // send results to shiori, if some are attached to the task + if(actualTask->resultData != nullptr) + { + // results of tables a aggregated values, so they have to be fixed to its average value + if(actualTask->type == TABLE_REQUEST_TASK) + { + const long outputC = actualTask->metaData.get("number_of_outputs_per_cycle")->getLong(); + const float numberOfOutputs = static_cast(outputC); + float val = 0.0f; + for(uint64_t i = 0; i < actualTask->resultData->array.size(); i++) + { + DataValue* value = actualTask->resultData->array[i]->toValue(); + val = value->getFloat() / numberOfOutputs; + value->setValue(val); + } + } + + // send result to shiori + Kitsunemimi::ErrorContainer error; + if(Shiori::sendResults(actualTask->uuid.toString(), + actualTask->name, + actualTask->userId, + actualTask->projectId, + *actualTask->resultData, + error) == false) + { + LOG_ERROR(error); + } + + delete actualTask->resultData; + actualTask->resultData = nullptr; + } + + // remove task from map and free its data + std::map::iterator it; + it = m_taskMap.find(actualTask->uuid.toString()); + if(it != m_taskMap.end()) + { + delete it->second.inputData; + it->second.progress.state = FINISHED_TASK_STATE; + it->second.progress.endActiveTimeStamp = std::chrono::system_clock::now(); + } + + actualTask = nullptr; +} + +/** + * @brief get task-progress + * + * @param taskUuid UUID of the task + * + * @return task-progress + */ +const TaskProgress +TaskHandle_State::getProgress(const std::string &taskUuid) +{ + std::lock_guard guard(m_task_mutex); + + std::map::const_iterator it; + it = m_taskMap.find(taskUuid); + if(it != m_taskMap.end()) { + return it->second.progress; + } + + TaskProgress progress; + return progress; +} + +/** + * @brief get state of a task + * + * @param taskUuid UUID of the task + * + * @return state of the requested task + */ +TaskState +TaskHandle_State::getTaskState(const std::string &taskUuid) +{ + TaskState state = UNDEFINED_TASK_STATE; + + std::map::const_iterator it; + it = m_taskMap.find(taskUuid); + if(it != m_taskMap.end()) { + state = it->second.progress.state; + } + + return state; +} + +/** + * @brief TaskHandle_State::getAllProgress + * @param result + */ +void +TaskHandle_State::getAllProgress(std::map &result) +{ + std::map::const_iterator it; + for(it = m_taskMap.begin(); + it != m_taskMap.end(); + it++) + { + result.emplace(it->second.uuid.toString(), it->second.progress); + } +} + +/** + * @brief remove task from queue or abort the task, if actual in progress + * + * @param taskUuid UUID of the task + * + * @return false, if task-uuid was not found, else true + */ +bool +TaskHandle_State::removeTask(const std::string &taskUuid) +{ + std::lock_guard guard(m_task_mutex); + + TaskState state = UNDEFINED_TASK_STATE; + + // check and update map + std::map::iterator itMap; + itMap = m_taskMap.find(taskUuid); + if(itMap != m_taskMap.end()) + { + state = itMap->second.progress.state; + + // if only queue but not activly processed at the moment, it can easily deleted + if(state == QUEUED_TASK_STATE) + { + delete itMap->second.inputData; + m_taskMap.erase(itMap); + + // update queue + std::deque::const_iterator itQueue; + itQueue = std::find(m_taskQueue.begin(), m_taskQueue.end(), taskUuid); + if(itQueue != m_taskQueue.end()) { + m_taskQueue.erase(itQueue); + } + + return true; + } + + // if task is active at the moment, then only mark it as aborted + if(state == ACTIVE_TASK_STATE) + { + itMap->second.progress.state = ABORTED_TASK_STATE; + return true; + } + + // handle finished and aborted state + if(state == FINISHED_TASK_STATE + || state == ABORTED_TASK_STATE) + { + // input-data are automatically deleted, when the task was finished, + // so removing from the list is enough + m_taskMap.erase(itMap); + return true; + } + } + + return false; +} + +/** + * @brief check if a task is finished + * + * @param taskUuid UUID of the task + * + * @return true, if task is finished, else false + */ +bool +TaskHandle_State::isFinish(const std::string &taskUuid) +{ + std::lock_guard guard(m_task_mutex); + + return getTaskState(taskUuid) == FINISHED_TASK_STATE; +} diff --git a/src/components/KyoukoMind/src/core/cluster/states/task_handle_state.h b/src/components/KyoukoMind/src/core/cluster/states/task_handle_state.h new file mode 100644 index 00000000..d2255c35 --- /dev/null +++ b/src/components/KyoukoMind/src/core/cluster/states/task_handle_state.h @@ -0,0 +1,64 @@ +/** + * @file task_handle_state.h + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2022 Tobias Anker + * + * 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. + */ + +#ifndef TASKHANGLESTATE_H +#define TASKHANGLESTATE_H + +#include +#include + +#include + +class Cluster; + +class TaskHandle_State + : public Kitsunemimi::Event +{ +public: + TaskHandle_State(Cluster* cluster); + ~TaskHandle_State(); + + bool processEvent(); + + bool addTask(const std::string &uuid, const Task &task); + Task* getActualTask(); + + const TaskProgress getProgress(const std::string &taskUuid); + bool removeTask(const std::string &taskUuid); + bool isFinish(const std::string &taskUuid); + TaskState getTaskState(const std::string &taskUuid); + void getAllProgress(std::map &result); + +private: + Cluster* m_cluster = nullptr; + + std::deque m_taskQueue; + std::map m_taskMap; + std::mutex m_task_mutex; + Task* actualTask = nullptr; + bool m_abort = false; + + bool getNextTask(); + void finishTask(); +}; + +#endif // TASKHANGLESTATE_H diff --git a/src/components/KyoukoMind/src/core/cluster/task.h b/src/components/KyoukoMind/src/core/cluster/task.h new file mode 100644 index 00000000..9ff50c91 --- /dev/null +++ b/src/components/KyoukoMind/src/core/cluster/task.h @@ -0,0 +1,78 @@ +/** + * @file task.h + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2022 Tobias Anker + * + * 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. + */ + +#ifndef KYOUKOMIND_TASK_H +#define KYOUKOMIND_TASK_H + +#include + +enum TaskType +{ + UNDEFINED_TASK = 0, + IMAGE_LEARN_TASK = 1, + IMAGE_REQUEST_TASK = 2, + TABLE_LEARN_TASK = 3, + TABLE_REQUEST_TASK = 4, + CLUSTER_SNAPSHOT_SAVE_TASK = 5, + CLUSTER_SNAPSHOT_RESTORE_TASK = 6, +}; + +enum TaskState +{ + UNDEFINED_TASK_STATE = 0, + QUEUED_TASK_STATE = 1, + ACTIVE_TASK_STATE = 2, + ABORTED_TASK_STATE = 3, + FINISHED_TASK_STATE = 4, +}; + +struct TaskProgress +{ + TaskState state = UNDEFINED_TASK_STATE; + float percentageFinished = 0.0f; + std::chrono::high_resolution_clock::time_point queuedTimeStamp; + std::chrono::high_resolution_clock::time_point startActiveTimeStamp; + std::chrono::high_resolution_clock::time_point endActiveTimeStamp; + uint64_t estimatedRemaningTime = 0; +}; + +struct Task +{ + Kitsunemimi::Hanami::kuuid uuid; + std::string name = ""; + std::string userId = ""; + std::string projectId = ""; + float* inputData = nullptr; + float* outputData = nullptr; + DataArray* resultData = nullptr; + DataMap metaData; + uint64_t actualCycle = 0; + TaskType type = UNDEFINED_TASK; + TaskProgress progress; + + uint64_t getIntVal(const std::string &name) + { + return static_cast(metaData.get(name)->toValue()->getLong()); + } +}; + +#endif // KYOUKOMIND_TASK_H diff --git a/src/components/KyoukoMind/src/core/processing/cpu_processing_unit.cpp b/src/components/KyoukoMind/src/core/processing/cpu_processing_unit.cpp new file mode 100644 index 00000000..4edd2206 --- /dev/null +++ b/src/components/KyoukoMind/src/core/processing/cpu_processing_unit.cpp @@ -0,0 +1,265 @@ +/** + * @file cpu_processing_unit.cpp + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2022 Tobias Anker + * + * 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 "cpu_processing_unit.h" + +#include +#include +#include + +#include +#include + +#include + +#include +#include +#include +#include + +#include +#include + +#include + +#include +#include + +uint32_t counter = 0; + +/** + * @brief constructor + */ +CpuProcessingUnit::CpuProcessingUnit() + : Kitsunemimi::Thread("CpuProcessingUnit") {} + +/** + * @brief destructor + */ +CpuProcessingUnit::~CpuProcessingUnit() {} + +/** + * @brief run forward-propagation on a segment + * + * @param segment segment to process + */ +void +CpuProcessingUnit::learnSegmentForward(AbstractSegment* segment) +{ + Kitsunemimi::ErrorContainer error; + + switch(segment->getType()) + { + case DYNAMIC_SEGMENT: + { + DynamicSegment* seg = static_cast(segment); + seg->dynamicSegmentSettings->doLearn = 1; + seg->dynamicSegmentSettings->doLearn = 1; + prcessDynamicSegment(*seg); + if(seg->dynamicSegmentSettings->updateSections != 0) { + updateSections(*seg); + } + seg->dynamicSegmentSettings->updateSections = 0; + + seg->dynamicSegmentSettings->doLearn = 0; + break; + } + case INPUT_SEGMENT: + { + InputSegment* seg = static_cast(segment); + prcessInputSegment(*seg); + break; + } + case OUTPUT_SEGMENT: + { + OutputSegment* seg = static_cast(segment); + prcessOutputSegment(*seg); + + break; + } + default: + break; + } +} + +/** + * @brief run back-propagation on a segment + * + * @param segment segment to process + */ +void +CpuProcessingUnit::learnSegmentBackward(AbstractSegment* segment) +{ + Kitsunemimi::ErrorContainer error; + + switch(segment->getType()) + { + case DYNAMIC_SEGMENT: + { + DynamicSegment* seg = static_cast(segment); + rewightDynamicSegment(*seg); + if(reductionCounter == 100) { + //reduceNeurons(*seg); + reductionCounter = 0; + } + reductionCounter++; + break; + } + case OUTPUT_SEGMENT: + { + OutputSegment* seg = static_cast(segment); + backpropagateOutput(*seg); + break; + } + default: + break; + } +} + +/** + * @brief process segments + * + * @param segment segment to process + */ +void +CpuProcessingUnit::processSegment(AbstractSegment* segment) +{ + Kitsunemimi::ErrorContainer error; + + switch(segment->getType()) + { + case DYNAMIC_SEGMENT: + { + DynamicSegment* seg = static_cast(segment); + prcessDynamicSegment(*seg); + break; + } + case INPUT_SEGMENT: + { + InputSegment* seg = static_cast(segment); + prcessInputSegment(*seg); + break; + } + case OUTPUT_SEGMENT: + { + OutputSegment* seg = static_cast(segment); + prcessOutputSegment(*seg); + if(seg->parentCluster->msgClient == nullptr) + { + Task* actualTask = seg->parentCluster->getActualTask(); + const uint64_t cycle = actualTask->actualCycle; + if(actualTask->type == IMAGE_REQUEST_TASK) + { + // TODO: check for cluster-state instead of client + const uint32_t hightest = getHighestOutput(*seg); + DataValue* value = actualTask->resultData->array[cycle]->toValue(); + value->setValue(static_cast(hightest)); + } + else if(actualTask->type == TABLE_REQUEST_TASK) + { + float val = 0.0f; + for(uint64_t i = 0; i < seg->segmentHeader->outputs.count; i++) + { + DataValue* value = actualTask->resultData->array[cycle]->toValue(); + val = value->getFloat() + seg->outputs[i].outputWeight; + value->setValue(val); + } + } + } + break; + } + default: + break; + } +} + +/** + * @brief run loop to process all available segments + */ +void +CpuProcessingUnit::run() +{ + AbstractSegment* currentSegment = nullptr; + + while(m_abort == false) + { + currentSegment = KyoukoRoot::m_segmentQueue->getSegmentFromQueue(); + if(currentSegment != nullptr) + { + // check if segment is ready, else requeue + if(currentSegment->isReady() == false) + { + KyoukoRoot::m_segmentQueue->addSegmentToQueue(currentSegment); + continue; + } + + // reset input ready status + for(uint8_t side = 0; side < 16; side++) { + currentSegment->segmentSlots->slots[side].inputReady = false; + } + + // handle type of processing + Cluster* clusterInterface = currentSegment->parentCluster; + if(clusterInterface->mode == Cluster::LEARN_FORWARD_MODE) { + learnSegmentForward(currentSegment); + } else if(clusterInterface->mode == Cluster::LEARN_BACKWARD_MODE) { + learnSegmentBackward(currentSegment); + } else { + processSegment(currentSegment); + } + + // finish segment by sharing border-buffer and register in cluster + currentSegment->finishSegment(); + } + else + { + // if no segments are available then sleep + sleepThread(1000); + } + } +} + + +/** + * @brief SingleThreadProcessingStatic::reductionLearning + +void +CpuProcessingUnit::reductionLearning(DynamicSegment* synapseSegment) +{ + const float initError = calculateSegmentError(synapseSegment); + float error = initError; + + if(initError > 0.1f) + { + int16_t timeout = 10; + while(error >= initError + && timeout >= 0) + { + reduceSegment(synapseSegment); + execute(synapseSegment); + error = calculateSegmentError(synapseSegment); + + timeout--; + } + } +}*/ + diff --git a/src/components/KyoukoMind/src/core/processing/cpu_processing_unit.h b/src/components/KyoukoMind/src/core/processing/cpu_processing_unit.h new file mode 100644 index 00000000..2760b21c --- /dev/null +++ b/src/components/KyoukoMind/src/core/processing/cpu_processing_unit.h @@ -0,0 +1,50 @@ +/** + * @file cpu_processing_unit.h + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2022 Tobias Anker + * + * 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. + */ + +#ifndef KYOUKOMIND_CPU_PROCESSING_UNIT_H +#define KYOUKOMIND_CPU_PROCESSING_UNIT_H + +#include +#include + +class AbstractSegment; + +class CpuProcessingUnit + : public Kitsunemimi::Thread +{ +public: + CpuProcessingUnit(); + ~CpuProcessingUnit(); + +protected: + void run(); + + +private: + uint64_t reductionCounter = 0; + + void learnSegmentForward(AbstractSegment* segment); + void learnSegmentBackward(AbstractSegment *segment); + void processSegment(AbstractSegment* segment); +}; + +#endif // KYOUKOMIND_CPU_PROCESSING_UNIT_H diff --git a/src/components/KyoukoMind/src/core/processing/processing_unit_handler.cpp b/src/components/KyoukoMind/src/core/processing/processing_unit_handler.cpp new file mode 100644 index 00000000..928c9736 --- /dev/null +++ b/src/components/KyoukoMind/src/core/processing/processing_unit_handler.cpp @@ -0,0 +1,55 @@ +/** + * @file processing_unit_handler.cpp + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2022 Tobias Anker + * + * 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 +#include + +/** + * @brief constructor + */ +ProcessingUnitHandler::ProcessingUnitHandler() {} + +/** + * @brief destructor + */ +ProcessingUnitHandler::~ProcessingUnitHandler() {} + +/** + * @brief init processing-threads + * + * @param numberOfThreads number of threads to create + * + * @return always true + */ +bool +ProcessingUnitHandler::initProcessingUnits(const uint16_t numberOfThreads) +{ + // init cpu + for(uint16_t i = 0; i < numberOfThreads; i++) + { + CpuProcessingUnit* newUnit = new CpuProcessingUnit(); + m_processingUnits.push_back(newUnit); + newUnit->startThread(); + } + + return true; +} diff --git a/src/components/KyoukoMind/src/core/processing/processing_unit_handler.h b/src/components/KyoukoMind/src/core/processing/processing_unit_handler.h new file mode 100644 index 00000000..dff2bca5 --- /dev/null +++ b/src/components/KyoukoMind/src/core/processing/processing_unit_handler.h @@ -0,0 +1,42 @@ +/** + * @file processing_unit_handler.h + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2022 Tobias Anker + * + * 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. + */ + +#ifndef KYOUKOMIND_PROCESSING_UNIT_HANDLER_H +#define KYOUKOMIND_PROCESSING_UNIT_HANDLER_H + +#include + +class CpuProcessingUnit; + +class ProcessingUnitHandler +{ +public: + ProcessingUnitHandler(); + ~ProcessingUnitHandler(); + + bool initProcessingUnits(const uint16_t numberOfThreads); + +private: + std::vector m_processingUnits; +}; + +#endif // KYOUKOMIND_PROCESSING_UNIT_HANDLER_H diff --git a/src/components/KyoukoMind/src/core/processing/segment_queue.cpp b/src/components/KyoukoMind/src/core/processing/segment_queue.cpp new file mode 100644 index 00000000..b4d98032 --- /dev/null +++ b/src/components/KyoukoMind/src/core/processing/segment_queue.cpp @@ -0,0 +1,83 @@ +/** + * @file processing_unit_handler.h + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2022 Tobias Anker + * + * 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 "segment_queue.h" + +#include + +/** + * @brief constructor + */ +SegmentQueue::SegmentQueue() {} + +/** + * @brief add segment to queue + * + * @param newSegment segment to add to queue + */ +void +SegmentQueue::addSegmentToQueue(AbstractSegment* newSegment) +{ + while(m_queue_lock.test_and_set(std::memory_order_acquire)) { asm(""); } + m_segmentQueue.push_back(newSegment); + m_queue_lock.clear(std::memory_order_release); +} + +/** + * @brief add a list of segments to the queue + * + * @param semgnetList list with segments to add + */ +void +SegmentQueue::addSegmentListToQueue(const std::vector &semgnetList) +{ + while(m_queue_lock.test_and_set(std::memory_order_acquire)) { asm(""); } + + for(AbstractSegment* segment : semgnetList) { + m_segmentQueue.push_back(segment); + } + + m_queue_lock.clear(std::memory_order_release); +} + +/** + * @brief get next segment in the queue + * + * @return nullptr, if queue is empty, else next segment in queue + */ +AbstractSegment* +SegmentQueue::getSegmentFromQueue() +{ + AbstractSegment* result = nullptr; + + while(m_queue_lock.test_and_set(std::memory_order_acquire)) { asm(""); } + + if(m_segmentQueue.size() > 0) + { + result = m_segmentQueue.front(); + m_segmentQueue.pop_front(); + } + + m_queue_lock.clear(std::memory_order_release); + + return result; +} diff --git a/src/components/KyoukoMind/src/core/processing/segment_queue.h b/src/components/KyoukoMind/src/core/processing/segment_queue.h new file mode 100644 index 00000000..ada150a6 --- /dev/null +++ b/src/components/KyoukoMind/src/core/processing/segment_queue.h @@ -0,0 +1,45 @@ +/** + * @file processing_unit_handler.h + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2022 Tobias Anker + * + * 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. + */ + +#ifndef KYOUKOMIND_SEGMENTQUEUE_H +#define KYOUKOMIND_SEGMENTQUEUE_H + +#include + +class AbstractSegment; + +class SegmentQueue +{ +public: + SegmentQueue(); + + void addSegmentToQueue(AbstractSegment* newSegment); + void addSegmentListToQueue(const std::vector &semgnetList); + + AbstractSegment* getSegmentFromQueue(); + +private: + std::atomic_flag m_queue_lock = ATOMIC_FLAG_INIT; + std::deque m_segmentQueue; +}; + +#endif // KYOUKOMIND_SEGMENTQUEUE_H diff --git a/src/components/KyoukoMind/src/core/routing_functions.h b/src/components/KyoukoMind/src/core/routing_functions.h new file mode 100644 index 00000000..c1374d77 --- /dev/null +++ b/src/components/KyoukoMind/src/core/routing_functions.h @@ -0,0 +1,302 @@ +/** + * @file functions.h + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2022 Tobias Anker + * + * 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. + */ + +#ifndef ROUTING_FUNCTIONS_H +#define ROUTING_FUNCTIONS_H + +#include +#include + +#include + +/** + * @brief get neighbor-position for a specific side in the hexagon-grid + * + * @param sourcePos base-position + * @param side side + * + * @return position of the object, which is connected to this side + */ +inline Kitsunemimi::Hanami::Position +getNeighborPos(const Kitsunemimi::Hanami::Position sourcePos, const uint8_t side) +{ + Kitsunemimi::Hanami::Position result; + + switch(side) + { + case 0: + { + if(sourcePos.y % 2 == 0) { + result.x = sourcePos.x - 1; + } else { + result.x = sourcePos.x; + } + result.y = sourcePos.y - 1; + result.z = sourcePos.z - 1; + break; + } + case 1: + { + if(sourcePos.y % 2 == 0) { + result.x = sourcePos.x; + } else { + result.x = sourcePos.x + 1; + } + result.y = sourcePos.y - 1; + result.z = sourcePos.z - 1; + break; + } + case 2: + { + result.x = sourcePos.x; + result.y = sourcePos.y; + result.z = sourcePos.z - 1; + break; + } + case 3: + { + if(sourcePos.y % 2 == 0) { + result.x = sourcePos.x; + } else { + result.x = sourcePos.x + 1; + } + result.y = sourcePos.y - 1; + result.z = sourcePos.z; + break; + } + case 4: + { + result.x = sourcePos.x + 1; + result.y = sourcePos.y; + result.z = sourcePos.z; + break; + } + case 5: + { + if(sourcePos.y % 2 == 0) { + result.x = sourcePos.x; + } else { + result.x = sourcePos.x + 1; + } + result.y = sourcePos.y + 1; + result.z = sourcePos.z; + break; + } + case 8: + { + if(sourcePos.y % 2 == 0) { + result.x = sourcePos.x - 1; + } else { + result.x = sourcePos.x; + } + result.y = sourcePos.y + 1; + result.z = sourcePos.z; + break; + } + case 7: + { + result.x = sourcePos.x - 1; + result.y = sourcePos.y; + result.z = sourcePos.z; + break; + } + case 6: + { + if(sourcePos.y % 2 == 0) { + result.x = sourcePos.x - 1; + } else { + result.x = sourcePos.x; + } + result.y = sourcePos.y - 1; + result.z = sourcePos.z; + break; + } + case 9: + { + result.x = sourcePos.x; + result.y = sourcePos.y; + result.z = sourcePos.z + 1; + break; + } + case 10: + { + if(sourcePos.y % 2 == 0) { + result.x = sourcePos.x - 1; + } else { + result.x = sourcePos.x; + } + result.y = sourcePos.y + 1; + result.z = sourcePos.z + 1; + break; + } + case 11: + { + if(sourcePos.y % 2 == 0) { + result.x = sourcePos.x; + } else { + result.x = sourcePos.x + 1; + } + result.y = sourcePos.y + 1; + result.z = sourcePos.z + 1; + break; + } + default: + // this state is never ever allowed. If this is reached, there is something totally + // broken within the source-code + assert(false); + } + + return result; +} + +/** + * @brief get possible next sides based on an incoming side + * + * @param side incoming-side + * + * @return object with all possible next sides + */ +inline NextSides +getNextSides(const uint8_t side) +{ + NextSides nextSides; + + switch (side) + { + case 0: + { + nextSides.sides[0] = 1; + nextSides.sides[1] = 4; + nextSides.sides[2] = 11; + nextSides.sides[3] = 5; + nextSides.sides[4] = 2; + break; + } + case 1: + { + nextSides.sides[0] = 2; + nextSides.sides[1] = 8; + nextSides.sides[2] = 10; + nextSides.sides[3] = 7; + nextSides.sides[4] = 0; + break; + } + case 2: + { + nextSides.sides[0] = 0; + nextSides.sides[1] = 6; + nextSides.sides[2] = 9; + nextSides.sides[3] = 3; + nextSides.sides[4] = 1; + break; + } + case 3: + { + nextSides.sides[0] = 5; + nextSides.sides[1] = 2; + nextSides.sides[2] = 8; + nextSides.sides[3] = 10; + nextSides.sides[4] = 7; + break; + } + case 4: + { + nextSides.sides[0] = 8; + nextSides.sides[1] = 10; + nextSides.sides[2] = 7; + nextSides.sides[3] = 0; + nextSides.sides[4] = 6; + break; + } + case 5: + { + nextSides.sides[0] = 7; + nextSides.sides[1] = 0; + nextSides.sides[2] = 6; + nextSides.sides[3] = 9; + nextSides.sides[4] = 3; + break; + } + case 8: + { + nextSides.sides[0] = 6; + nextSides.sides[1] = 9; + nextSides.sides[2] = 3; + nextSides.sides[3] = 1; + nextSides.sides[4] = 4; + break; + } + case 7: + { + nextSides.sides[0] = 3; + nextSides.sides[1] = 1; + nextSides.sides[2] = 4; + nextSides.sides[3] = 11; + nextSides.sides[4] = 5; + break; + } + case 6: + { + nextSides.sides[0] = 4; + nextSides.sides[1] = 11; + nextSides.sides[2] = 5; + nextSides.sides[3] = 2; + nextSides.sides[4] = 8; + break; + } + case 9: + { + nextSides.sides[0] = 11; + nextSides.sides[1] = 5; + nextSides.sides[2] = 2; + nextSides.sides[3] = 8; + nextSides.sides[4] = 10; + break; + } + case 10: + { + nextSides.sides[0] = 9; + nextSides.sides[1] = 3; + nextSides.sides[2] = 1; + nextSides.sides[3] = 4; + nextSides.sides[4] = 11; + break; + } + case 11: + { + nextSides.sides[0] = 10; + nextSides.sides[1] = 7; + nextSides.sides[2] = 0; + nextSides.sides[3] = 6; + nextSides.sides[4] = 9; + break; + } + default: + // this state is never ever allowed. If this is reached, there is something totally + // broken within the source-code + assert(false); + } + + return nextSides; +} + +#endif // ROUTING_FUNCTIONS_H diff --git a/src/components/KyoukoMind/src/core/segments/abstract_segment.cpp b/src/components/KyoukoMind/src/core/segments/abstract_segment.cpp new file mode 100644 index 00000000..0887da17 --- /dev/null +++ b/src/components/KyoukoMind/src/core/segments/abstract_segment.cpp @@ -0,0 +1,202 @@ +/** + * @file abstract_segment.cpp + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2022 Tobias Anker + * + * 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 "abstract_segment.h" + +#include + +/** + * @brief constructor + */ +AbstractSegment::AbstractSegment() {} + +AbstractSegment::AbstractSegment(const void* data, const uint64_t dataSize) +{ + segmentData.initBuffer(data, dataSize); +} + +/** + * @brief destructor + */ +AbstractSegment::~AbstractSegment() {} + +/** + * @brief get type of the segment + * + * @return type of the segment + */ +SegmentTypes +AbstractSegment::getType() const +{ + return m_type; +} + +/** + * @brief get name of the segment + * + * @return name of the segment + */ +const std::string +AbstractSegment::getName() const +{ + return segmentName->getName(); +} + +/** + * @brief set new name for the segment + * + * @param name new name + * + * @return false, if name is too long or empty, else true + */ +bool +AbstractSegment::setName(const std::string &name) +{ + return segmentName->setName(name); +} + +/** + * @brief AbstractSegment::getSlotId + * @param name + * @return + */ +uint8_t +AbstractSegment::getSlotId(const std::string &name) +{ + for(uint64_t i = 0; i < 16; i++) + { + if(segmentSlots->slots[i].getName() == name) { + return i; + } + } + + return UNINIT_STATE_8; +} + +/** + * @brief check if all border-buffer, which are in use, are ready for processing + * + * @return true, if all border-buffer are ready, else false + */ +bool +AbstractSegment::isReady() +{ + for(uint8_t i = 0; i < 16; i++) + { + if(segmentSlots->slots[i].inUse == true + && segmentSlots->slots[i].inputReady == false) + { + return false; + } + } + + return true; +} + +/** + * @brief run finishing step of the segment-processing to share the border-buffer with the + * neighbor segments + */ +void +AbstractSegment::finishSegment() +{ + float* sourceBuffer = nullptr; + float* targetBuffer = nullptr; + uint32_t targetId = 0; + uint8_t targetSide = 0; + uint64_t targetBufferPos = 0; + AbstractSegment* targetSegment = nullptr; + SegmentSlotList* targetNeighbors = nullptr; + + for(uint8_t i = 0; i < 16; i++) + { + if(segmentSlots->slots[i].inUse == 1) + { + // get information of the neighbor + sourceBuffer = &outputTransfers[segmentSlots->slots[i].outputTransferBufferPos]; + targetId = segmentSlots->slots[i].targetSegmentId; + targetSide = segmentSlots->slots[i].targetSlotId; + + // copy data to the target buffer and wipe the source buffer + targetSegment = parentCluster->allSegments.at(targetId); + targetNeighbors = targetSegment->segmentSlots; + targetBufferPos = targetNeighbors->slots[targetSide].inputTransferBufferPos; + targetBuffer = &targetSegment->inputTransfers[targetBufferPos]; + memcpy(targetBuffer, + sourceBuffer, + segmentSlots->slots[i].numberOfNeurons * sizeof(float)); + memset(sourceBuffer, + 0, + segmentSlots->slots[i].numberOfNeurons * sizeof(float)); + + // mark the target as ready for processing + targetSegment->segmentSlots->slots[targetSide].inputReady = true; + } + } + + parentCluster->updateClusterState(); +} + +/** + * @brief generate header with generic segment-information + * + * @param header reference to the header-object to fill + * @param borderbufferSize size of the border-buffer in bytes + * + * @return number of required bytes to the generic information + */ +uint32_t +AbstractSegment::createGenericNewHeader(SegmentHeader &header, + const uint64_t borderbufferSize) +{ + uint32_t segmentDataPos = 0; + + // init header + segmentDataPos += sizeof(SegmentHeader); + + // init name + header.name.count = 1; + header.name.bytePos = segmentDataPos; + segmentDataPos += sizeof(SegmentName); + + // init settings + header.settings.count = 1; + header.settings.bytePos = segmentDataPos; + segmentDataPos += sizeof(DynamicSegmentSettings); + + // init neighborList + header.slotList.count = 1; + header.slotList.bytePos = segmentDataPos; + segmentDataPos += sizeof(SegmentSlotList); + + // init inputTransfers + header.inputTransfers.count = borderbufferSize; + header.inputTransfers.bytePos = segmentDataPos; + segmentDataPos += borderbufferSize * sizeof(float); + + // init outputTransfers + header.outputTransfers.count = borderbufferSize; + header.outputTransfers.bytePos = segmentDataPos; + segmentDataPos += borderbufferSize * sizeof(float); + + return segmentDataPos; +} diff --git a/src/components/KyoukoMind/src/core/segments/abstract_segment.h b/src/components/KyoukoMind/src/core/segments/abstract_segment.h new file mode 100644 index 00000000..e5e16c5d --- /dev/null +++ b/src/components/KyoukoMind/src/core/segments/abstract_segment.h @@ -0,0 +1,86 @@ +/** + * @file abstract_segment.h + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2022 Tobias Anker + * + * 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. + */ + +#ifndef KYOUKOMIND_ABSTRACT_SEGMENTS_H +#define KYOUKOMIND_ABSTRACT_SEGMENTS_H + +#include + +#include +#include + +#include +#include + +#include +#include + +#include + +class Cluster; + +class AbstractSegment +{ +public: + AbstractSegment(); + AbstractSegment(const void* data, const uint64_t dataSize); + virtual ~AbstractSegment(); + + SegmentTypes getType() const; + const std::string getName() const; + bool setName(const std::string &name); + + Kitsunemimi::ItemBuffer segmentData; + + SegmentHeader* segmentHeader = nullptr; + DynamicSegmentSettings* dynamicSegmentSettings = nullptr; + + SegmentName* segmentName = nullptr; + SegmentSlotList* segmentSlots = nullptr; + float* inputTransfers = nullptr; + float* outputTransfers = nullptr; + Cluster* parentCluster = nullptr; + + virtual bool initSegment(const std::string &name, + const Kitsunemimi::Hanami::SegmentMeta &segmentMeta) = 0; + virtual bool reinitPointer(const uint64_t numberOfBytes) = 0; + uint8_t getSlotId(const std::string &name); + + bool isReady(); + void finishSegment(); + +protected: + SegmentTypes m_type = UNDEFINED_SEGMENT; + + uint32_t createGenericNewHeader(SegmentHeader &header, + const uint64_t borderbufferSize); + bool reinitGenericPointer(); + +private: + virtual void initSegmentPointer(const SegmentHeader &header) = 0; + virtual bool connectBorderBuffer() = 0; + virtual void allocateSegment(SegmentHeader &header) = 0; +}; + +//================================================================================================== + +#endif // KYOUKOMIND_ABSTRACT_SEGMENTS_H diff --git a/src/components/KyoukoMind/src/core/segments/brick.h b/src/components/KyoukoMind/src/core/segments/brick.h new file mode 100644 index 00000000..330cfb1d --- /dev/null +++ b/src/components/KyoukoMind/src/core/segments/brick.h @@ -0,0 +1,49 @@ +/** + * @file brick.h + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2022 Tobias Anker + * + * 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. + */ + +#ifndef KYOUKOMIND_BRICK_H +#define KYOUKOMIND_BRICK_H + +#include +#include + +struct Brick +{ + // common + uint32_t brickId = UNINIT_STATE_32; + bool isOutputBrick = false; + bool isTransactionBrick = false; + bool isInputBrick = false; + uint8_t padding1[13]; + uint32_t neuronSectionPos = UNINIT_STATE_32; + + Kitsunemimi::Hanami::Position brickPos; + uint32_t neighbors[12]; + + uint32_t possibleTargetNeuronBrickIds[1000]; + uint32_t numberOfNeurons = 0; + uint32_t numberOfNeuronSections = 0; + + // total size: 4096 Bytes +}; + +#endif // KYOUKOMIND_BRICK_H diff --git a/src/components/KyoukoMind/src/core/segments/dynamic_segment/backpropagation.h b/src/components/KyoukoMind/src/core/segments/dynamic_segment/backpropagation.h new file mode 100644 index 00000000..18cae265 --- /dev/null +++ b/src/components/KyoukoMind/src/core/segments/dynamic_segment/backpropagation.h @@ -0,0 +1,224 @@ +/** + * @file backpropagation.h + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2022 Tobias Anker + * + * 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. + */ + +#ifndef KYOUKOMIND_DYNAMIC_BACKPROPAGATION_H +#define KYOUKOMIND_DYNAMIC_BACKPROPAGATION_H + +#include + +#include +#include +#include + +#include "objects.h" + +/** + * @brief backpropagate values of an output-brick + * + * @param brick brick to process + * @param segment segment where the brick belongs to + */ +inline bool +backpropagateOutput(const Brick* brick, + float* inputTransfers, + NeuronSection* neuronSections, + DynamicSegmentSettings* dynamicSegmentSettings) +{ + DynamicNeuron* neuron = nullptr; + NeuronSection* section = nullptr; + float totalDelta = 0.0f; + + // iterate over all neurons within the brick + for(uint32_t neuronSectionId = brick->neuronSectionPos; + neuronSectionId < brick->numberOfNeuronSections + brick->neuronSectionPos; + neuronSectionId++) + { + section = &neuronSections[neuronSectionId]; + for(uint32_t neuronId = 0; + neuronId < section->numberOfNeurons; + neuronId++) + { + neuron = §ion->neurons[neuronId]; + neuron->delta = inputTransfers[neuron->targetBorderId]; + inputTransfers[neuron->targetBorderId] = 0.0f; + totalDelta += abs(neuron->delta); + } + } + + return totalDelta > dynamicSegmentSettings->backpropagationBorder; + //return true; +} + +/** + * @brief run backpropagation for a single synapse-section + * + * @param section pointer to section to process + * @param sourceNeuron pointer to the neuron, who triggered the section + * @param netH neuron-potential + * @param outH output-multiplicator + * @param brick brick where the seciton is located + * @param segment segment where section belongs to + */ +inline void +backpropagateSection(SynapseSection* section, + DynamicNeuron* sourceNeuron, + float netH, + const Brick* brick, + NeuronSection* neuronSections, + SynapseSection* synapseSections) +{ + Synapse* synapse = nullptr; + DynamicNeuron* targetNeuron = nullptr; + NeuronSection* neuronSection = &neuronSections[section->targetNeuronSectionId]; + float learnValue = 0.2f; + uint16_t pos = 0; + + // iterate over all synapses in the section + while(pos < SYNAPSES_PER_SYNAPSESECTION + && netH > 0.0f) + { + // break look, if no more synapses to process + synapse = §ion->synapses[pos]; + + // update weight + learnValue = static_cast(126 - synapse->activeCounter) * 0.0002f; + learnValue += 0.05f; + targetNeuron = &neuronSection->neurons[synapse->targetNeuronId]; + sourceNeuron->delta += targetNeuron->delta * synapse->weight; + synapse->weight -= learnValue * targetNeuron->delta; + + netH -= synapse->border; + pos++; + } + + if(section->nextId != UNINIT_STATE_32 + && netH > 0.01f) + { + backpropagateSection(&synapseSections[section->nextId], + sourceNeuron, + netH, + brick, + neuronSections, + synapseSections); + } +} + +/** + * @brief run back-propagation over the hidden neurons + * + * @param brick pointer to current brick + * @param segment pointer to currect segment to process, which contains the brick + */ +inline void +backpropagateNeurons(const Brick* brick, + NeuronSection* neuronSections, + SynapseSection* synapseSections, + UpdatePosSection* updatePosSections, + float* outputTransfers) +{ + DynamicNeuron* sourceNeuron = nullptr; + NeuronSection* neuronSection = nullptr; + UpdatePosSection* updatePosSection = nullptr; + + // iterate over all neurons within the brick + for(uint32_t neuronSectionId = brick->neuronSectionPos; + neuronSectionId < brick->numberOfNeuronSections + brick->neuronSectionPos; + neuronSectionId++) + { + neuronSection = &neuronSections[neuronSectionId]; + updatePosSection = &updatePosSections[neuronSectionId]; + for(uint32_t neuronId = 0; + neuronId < neuronSection->numberOfNeurons; + neuronId++) + { + // skip section, if not active + sourceNeuron = &neuronSection->neurons[neuronId]; + //UpdatePos* updatePos = &updatePosSection->positions[neuronId]; + if(sourceNeuron->targetSectionId == UNINIT_STATE_32) { + continue; + } + + sourceNeuron->delta = 0.0f; + + // set start-values + if(sourceNeuron->active) + { + backpropagateSection(&synapseSections[sourceNeuron->targetSectionId], + sourceNeuron, + sourceNeuron->potential, + brick, + neuronSections, + synapseSections); + + sourceNeuron->delta *= 1.4427f * pow(0.5f, sourceNeuron->potential); + } + + if(brick->isInputBrick) { + outputTransfers[sourceNeuron->targetBorderId] = sourceNeuron->delta; + } + } + } +} + +/** + * @brief correct wight of synapses within + * + * @param segment segment to process + */ +void +rewightDynamicSegment(const DynamicSegment &segment) +{ + Brick* bricks = segment.bricks; + uint32_t* brickOrder = segment.brickOrder; + NeuronSection* neuronSections = segment.neuronSections; + SynapseSection* synapseSections = segment.synapseSections; + SegmentHeader* segmentHeader = segment.segmentHeader; + DynamicSegmentSettings* dynamicSegmentSettings = segment.dynamicSegmentSettings; + UpdatePosSection* updatePosSections = segment.updatePosSections; + float* inputTransfers = segment.inputTransfers; + float* outputTransfers = segment.outputTransfers; + + // run back-propagation over all internal neurons and synapses + const uint32_t numberOfBricks = segmentHeader->bricks.count; + for(int32_t pos = numberOfBricks - 1; pos >= 0; pos--) + { + const uint32_t brickId = brickOrder[pos]; + Brick* brick = &bricks[brickId]; + if(brick->isOutputBrick) + { + if(backpropagateOutput(brick, + inputTransfers, + neuronSections, + dynamicSegmentSettings) == false) + { + return; + } + } + backpropagateNeurons(brick, + neuronSections, + synapseSections, + updatePosSections, + outputTransfers); + } +} + +#endif // KYOUKOMIND_DYNAMIC_BACKPROPAGATION_H diff --git a/src/components/KyoukoMind/src/core/segments/dynamic_segment/dynamic_segment.cpp b/src/components/KyoukoMind/src/core/segments/dynamic_segment/dynamic_segment.cpp new file mode 100644 index 00000000..77ce5e22 --- /dev/null +++ b/src/components/KyoukoMind/src/core/segments/dynamic_segment/dynamic_segment.cpp @@ -0,0 +1,776 @@ +/** + * @file dynamic_segment.cpp + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2022 Tobias Anker + * + * 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 "dynamic_segment.h" + +#include +// #include + +#include + +#include +#include +#include + +#include +#include + +/** + * @brief constructor + */ +DynamicSegment::DynamicSegment() + : AbstractSegment() +{ + m_type = DYNAMIC_SEGMENT; +} + +/** + * @brief constructor to create segment from a snapshot + * + * @param data pointer to data with snapshot + * @param dataSize size of snapshot in number of bytes + */ +DynamicSegment::DynamicSegment(const void* data, const uint64_t dataSize) + : AbstractSegment(data, dataSize) +{ + m_type = DYNAMIC_SEGMENT; +} + +/** + * @brief destructor + */ +DynamicSegment::~DynamicSegment() {} + +uint32_t +getNumberOfNeuronSections(const uint32_t numberOfNeurons) +{ + uint32_t numberOfSections = numberOfNeurons / NEURONS_PER_NEURONSECTION; + if(numberOfNeurons % NEURONS_PER_NEURONSECTION != 0) { + numberOfSections++; + } + + return numberOfSections; +} + +/** + * @brief DynamicSegment::initGpu + */ +/*void +DynamicSegment::initGpu() +{ + Kitsunemimi::ErrorContainer error; + + // create data-object + data = new Kitsunemimi::GpuData(); + data->numberOfWg.x = 20; + data->threadsPerWg.x = 10; + const std::string kernelString(reinterpret_cast(gpu_kernel_cl), + gpu_kernel_cl_len); + if(KyoukoRoot::gpuInterface->addKernel(*data, + "prcessDynamicSegment", + kernelString, + error) == false) + { + LOG_ERROR(error); + error._errorMessages.clear(); + } + + if(KyoukoRoot::gpuInterface->addKernel(*data, + "rewightDynamicSegment", + kernelString, + error) == false) + { + LOG_ERROR(error); + error._errorMessages.clear(); + } + + assert(data->addBuffer("bricks", segmentHeader->bricks.count, sizeof(Brick), false, bricks )); + assert(data->addBuffer("brickOrder", segmentHeader->brickOrder.count, sizeof(uint32_t), false, brickOrder )); + assert(data->addBuffer("neuronSections", segmentHeader->neuronSections.count, sizeof(NeuronSection), false, neuronSections )); + assert(data->addBuffer("synapseSections", segmentHeader->synapseSections.count, sizeof(SynapseSection), false, synapseSections )); + assert(data->addBuffer("segmentHeader", 1, sizeof(SegmentHeader), false, segmentHeader )); + assert(data->addBuffer("dynamicSegmentSettings", 1, sizeof(DynamicSegmentSettings), false, dynamicSegmentSettings )); + assert(data->addBuffer("inputTransfers", segmentHeader->inputTransfers.count, sizeof(float), false, inputTransfers )); + assert(data->addBuffer("outputTransfers", segmentHeader->outputTransfers.count, sizeof(float), false, outputTransfers )); + assert(data->addBuffer("updatePosSections", segmentHeader->updatePosSections.count, sizeof(UpdatePosSection), false, updatePosSections )); + assert(data->addBuffer("randomValues", NUMBER_OF_RAND_VALUES, sizeof(uint32_t), false, KyoukoRoot::m_randomValues)); + + if(KyoukoRoot::gpuInterface->initCopyToDevice(*data, error) == false) { + LOG_ERROR(error); + } + + assert(KyoukoRoot::gpuInterface->bindKernelToBuffer(*data, "prcessDynamicSegment", "bricks", error)); + assert(KyoukoRoot::gpuInterface->bindKernelToBuffer(*data, "prcessDynamicSegment", "brickOrder", error)); + assert(KyoukoRoot::gpuInterface->bindKernelToBuffer(*data, "prcessDynamicSegment", "neuronSections", error)); + assert(KyoukoRoot::gpuInterface->bindKernelToBuffer(*data, "prcessDynamicSegment", "synapseSections", error)); + assert(KyoukoRoot::gpuInterface->bindKernelToBuffer(*data, "prcessDynamicSegment", "updatePosSections", error)); + assert(KyoukoRoot::gpuInterface->bindKernelToBuffer(*data, "prcessDynamicSegment", "segmentHeader", error)); + assert(KyoukoRoot::gpuInterface->bindKernelToBuffer(*data, "prcessDynamicSegment", "dynamicSegmentSettings", error)); + assert(KyoukoRoot::gpuInterface->bindKernelToBuffer(*data, "prcessDynamicSegment", "inputTransfers", error)); + assert(KyoukoRoot::gpuInterface->bindKernelToBuffer(*data, "prcessDynamicSegment", "outputTransfers", error)); + assert(KyoukoRoot::gpuInterface->bindKernelToBuffer(*data, "prcessDynamicSegment", "randomValues", error)); + + assert(KyoukoRoot::gpuInterface->bindKernelToBuffer(*data, "rewightDynamicSegment", "bricks", error)); + assert(KyoukoRoot::gpuInterface->bindKernelToBuffer(*data, "rewightDynamicSegment", "brickOrder", error)); + assert(KyoukoRoot::gpuInterface->bindKernelToBuffer(*data, "rewightDynamicSegment", "neuronSections", error)); + assert(KyoukoRoot::gpuInterface->bindKernelToBuffer(*data, "rewightDynamicSegment", "synapseSections", error)); + assert(KyoukoRoot::gpuInterface->bindKernelToBuffer(*data, "rewightDynamicSegment", "updatePosSections", error)); + assert(KyoukoRoot::gpuInterface->bindKernelToBuffer(*data, "rewightDynamicSegment", "segmentHeader", error)); + assert(KyoukoRoot::gpuInterface->bindKernelToBuffer(*data, "rewightDynamicSegment", "dynamicSegmentSettings", error)); + assert(KyoukoRoot::gpuInterface->bindKernelToBuffer(*data, "rewightDynamicSegment", "inputTransfers", error)); + assert(KyoukoRoot::gpuInterface->bindKernelToBuffer(*data, "rewightDynamicSegment", "outputTransfers", error)); +}*/ + +/** + * @brief initalize segment + * + * @param parsedContent json-object with the segment-description + * + * @return true, if successful, else false + */ +bool +DynamicSegment::initSegment(const std::string &name, + const Kitsunemimi::Hanami::SegmentMeta &segmentMeta) +{ + uint32_t numberOfNeurons = 0; + uint32_t numberOfNeuronSections = 0; + uint32_t totalBorderSize = 0; + Kitsunemimi::ErrorContainer error; + + // calculate sizes + uint32_t neuronsInBrick = 0; + for(uint32_t i = 0; i < segmentMeta.bricks.size(); i++) + { + neuronsInBrick = segmentMeta.bricks.at(i).numberOfNeurons; + numberOfNeurons += neuronsInBrick; + numberOfNeuronSections += getNumberOfNeuronSections(neuronsInBrick); + + if(segmentMeta.bricks.at(i).type == Kitsunemimi::Hanami::INPUT_BRICK_TYPE + || segmentMeta.bricks.at(i).type == Kitsunemimi::Hanami::OUTPUT_BRICK_TYPE) + { + totalBorderSize += neuronsInBrick; + } + } + + // create segment metadata + const DynamicSegmentSettings settings = initSettings(segmentMeta); + SegmentHeader header = createNewHeader(segmentMeta.bricks.size(), + numberOfNeuronSections, + settings.maxSynapseSections, + totalBorderSize); + + // initialize segment itself + allocateSegment(header); + initSegmentPointer(header); + initDefaultValues(); + dynamicSegmentSettings[0] = settings; + + // init content + initializeNeurons(segmentMeta); + addBricksToSegment(segmentMeta); + connectAllBricks(); + initTargetBrickList(); + + // init border + initSlots(segmentMeta); + connectBorderBuffer(); + + // TODO: check result + setName(name); + + //initGpu(); + + return true; +} + +/** + * @brief DynamicSegment::reinitPointer + * @return + */ +bool +DynamicSegment::reinitPointer(const uint64_t numberOfBytes) +{ + // TODO: checks + uint8_t* dataPtr = static_cast(segmentData.staticData); + + uint64_t pos = 0; + uint64_t byteCounter = 0; + segmentHeader = reinterpret_cast(dataPtr + pos); + byteCounter += sizeof(SegmentHeader); + + pos = segmentHeader->name.bytePos; + segmentName = reinterpret_cast(dataPtr + pos); + byteCounter += sizeof(SegmentName); + + pos = segmentHeader->settings.bytePos; + dynamicSegmentSettings = reinterpret_cast(dataPtr + pos); + byteCounter += sizeof(DynamicSegmentSettings); + + pos = segmentHeader->slotList.bytePos; + segmentSlots = reinterpret_cast(dataPtr + pos); + byteCounter += segmentHeader->slotList.count * sizeof(SegmentSlotList); + + pos = segmentHeader->inputTransfers.bytePos; + inputTransfers = reinterpret_cast(dataPtr + pos); + byteCounter += segmentHeader->inputTransfers.count * sizeof(float); + + pos = segmentHeader->outputTransfers.bytePos; + outputTransfers = reinterpret_cast(dataPtr + pos); + byteCounter += segmentHeader->outputTransfers.count * sizeof(float); + + pos = segmentHeader->bricks.bytePos; + bricks = reinterpret_cast(dataPtr + pos); + byteCounter += segmentHeader->bricks.count * sizeof(Brick); + + pos = segmentHeader->brickOrder.bytePos; + brickOrder = reinterpret_cast(dataPtr + pos); + byteCounter += segmentHeader->brickOrder.count * sizeof(uint32_t); + + pos = segmentHeader->neuronSections.bytePos; + neuronSections = reinterpret_cast(dataPtr + pos); + byteCounter += segmentHeader->neuronSections.count * sizeof(NeuronSection); + + pos = segmentHeader->updatePosSections.bytePos; + updatePosSections = reinterpret_cast(dataPtr + pos); + byteCounter += segmentHeader->updatePosSections.count * sizeof(UpdatePosSection); + + dataPtr = static_cast(segmentData.itemData); + //pos = segmentHeader->synapseSections.bytePos; + synapseSections = reinterpret_cast(dataPtr); + byteCounter += segmentHeader->synapseSections.count * sizeof(SynapseSection); + + //initGpu(); + + // check result + if(byteCounter != numberOfBytes - 48) { + std::cout<<"fail!!!!!!!!!!! byteCounter: "<= NEURONS_PER_NEURONSECTION) + { + for(uint32_t i = 0; i < NEURONS_PER_NEURONSECTION; i++) { + section->neurons[i].border = 0.0f; + } + section->numberOfNeurons = NEURONS_PER_NEURONSECTION; + updatePosSection->numberOfPositions = NEURONS_PER_NEURONSECTION; + neuronsInBrick -= NEURONS_PER_NEURONSECTION; + } + else + { + for(uint32_t i = 0; i < neuronsInBrick; i++) { + section->neurons[i].border = 0.0f; + } + section->numberOfNeurons = neuronsInBrick; + updatePosSection->numberOfPositions = neuronsInBrick; + break; + } + sectionCounter++; + } + sectionPositionOffset += numberOfNeuronSectionsInBrick; + } + + return true; +} + +/** + * @brief init border-buffer + * + * @return true, if successful, else false + */ +bool +DynamicSegment::connectBorderBuffer() +{ + NeuronSection* section = nullptr; + Brick* brick = nullptr; + + uint64_t transferCounter = 0; + + for(uint32_t i = 0; i < segmentHeader->bricks.count; i++) + { + brick = &bricks[i]; + if(brick->isInputBrick) + { + const uint32_t numberOfNeuronSections = getNumberOfNeuronSections(brick->numberOfNeurons); + for(uint32_t j = 0; j < numberOfNeuronSections; j++) + { + if(transferCounter >= segmentHeader->inputTransfers.count) { + break; + } + + section = &neuronSections[brick->neuronSectionPos + j]; + for(uint32_t k = 0; k < section->numberOfNeurons; k++) + { + section->neurons[k].targetBorderId = transferCounter; + transferCounter++; + } + } + } + + // connect output-bricks with border-buffer + if(brick->isOutputBrick + || brick->isTransactionBrick) + { + const uint32_t numberOfNeuronSections = getNumberOfNeuronSections(brick->numberOfNeurons); + for(uint32_t j = 0; j < numberOfNeuronSections; j++) + { + if(transferCounter >= segmentHeader->outputTransfers.count) { + break; + } + + section = &neuronSections[brick->neuronSectionPos + j]; + for(uint32_t k = 0; k < section->numberOfNeurons; k++) + { + section->neurons[k].targetBorderId = transferCounter; + transferCounter++; + } + } + } + } + + return true; +} + +/** + * @brief init sttings-block for the segment + * + * @param parsedContent json-object with the segment-description + * + * @return settings-object + */ +DynamicSegmentSettings +DynamicSegment::initSettings(const Kitsunemimi::Hanami::SegmentMeta &segmentMeta) +{ + DynamicSegmentSettings settings; + + // parse settings + settings.synapseSegmentation = segmentMeta.synapseSegmentation; + settings.signNeg = segmentMeta.signNeg; + settings.maxSynapseSections = segmentMeta.maxSynapseSections; + + return settings; +} + +/** + * @brief create new segment-header with size and position information + * + * @param numberOfBricks number of bricks + * @param numberOfNeurons number of neurons + * @param numberOfSynapseSections number of synapse-sections + * @param borderbufferSize size of border-buffer + * + * @return new segment-header + */ +SegmentHeader +DynamicSegment::createNewHeader(const uint32_t numberOfBricks, + const uint32_t numberOfNeuronSections, + const uint64_t numberOfSynapseSections, + const uint64_t borderbufferSize) +{ + SegmentHeader segmentHeader; + segmentHeader.segmentType = m_type; + uint32_t segmentDataPos = createGenericNewHeader(segmentHeader, borderbufferSize); + + // init bricks + segmentHeader.bricks.count = numberOfBricks; + segmentHeader.bricks.bytePos = segmentDataPos; + segmentDataPos += numberOfBricks * sizeof(Brick); + + // init brick-order + segmentHeader.brickOrder.count = numberOfBricks; + segmentHeader.brickOrder.bytePos = segmentDataPos; + segmentDataPos += numberOfBricks * sizeof(uint32_t); + + // init neurons + segmentHeader.neuronSections.count = numberOfNeuronSections; + segmentHeader.neuronSections.bytePos = segmentDataPos; + segmentDataPos += numberOfNeuronSections * sizeof(NeuronSection); + + // init section-updates + segmentHeader.updatePosSections.count = numberOfNeuronSections; + segmentHeader.updatePosSections.bytePos = segmentDataPos; + segmentDataPos += numberOfNeuronSections * sizeof(UpdatePosSection); + + segmentHeader.staticDataSize = segmentDataPos; + + // init synapse sections + segmentDataPos = 0; + segmentHeader.synapseSections.count = numberOfSynapseSections; + segmentHeader.synapseSections.bytePos = segmentDataPos; + + return segmentHeader; +} + +/** + * @brief init pointer within the segment-header + * + * @param header segment-header + */ +void +DynamicSegment::initSegmentPointer(const SegmentHeader &header) +{ + uint8_t* dataPtr = static_cast(segmentData.staticData); + uint64_t pos = 0; + + segmentHeader = reinterpret_cast(dataPtr + pos); + segmentHeader[0] = header; + + pos = segmentHeader->name.bytePos; + segmentName = reinterpret_cast(dataPtr + pos); + + pos = segmentHeader->settings.bytePos; + dynamicSegmentSettings = reinterpret_cast(dataPtr + pos); + + pos = segmentHeader->slotList.bytePos; + segmentSlots = reinterpret_cast(dataPtr + pos); + + pos = segmentHeader->inputTransfers.bytePos; + inputTransfers = reinterpret_cast(dataPtr + pos); + + pos = segmentHeader->outputTransfers.bytePos; + outputTransfers = reinterpret_cast(dataPtr + pos); + + pos = segmentHeader->bricks.bytePos; + bricks = reinterpret_cast(dataPtr + pos); + + pos = segmentHeader->brickOrder.bytePos; + brickOrder = reinterpret_cast(dataPtr + pos); + + pos = segmentHeader->neuronSections.bytePos; + neuronSections = reinterpret_cast(dataPtr + pos); + + pos = segmentHeader->updatePosSections.bytePos; + updatePosSections = reinterpret_cast(dataPtr + pos); + + dataPtr = static_cast(segmentData.itemData); + pos = segmentHeader->synapseSections.bytePos; + synapseSections = reinterpret_cast(dataPtr + pos); +} + +/** + * @brief allocate memory for the segment + * + * @param header header with the size-information + */ +void +DynamicSegment::allocateSegment(SegmentHeader &header) +{ + segmentData.initBuffer(header.synapseSections.count, header.staticDataSize); + segmentData.deleteAll(); +} + +/** + * @brief init buffer to avoid undefined values + */ +void +DynamicSegment::initDefaultValues() +{ + // init header and metadata + dynamicSegmentSettings[0] = DynamicSegmentSettings(); + + // init bricks; + for(uint32_t i = 0; i < segmentHeader->bricks.count; i++) { + bricks[i] = Brick(); + } + + // init brick-order + for(uint32_t i = 0; i < segmentHeader->bricks.count; i++) { + brickOrder[i] = i; + } + + // init neurons + for(uint32_t i = 0; i < segmentHeader->neuronSections.count; i++) { + neuronSections[i] = NeuronSection(); + neuronSections[i].id = i; + } + + // init section-updates + for(uint32_t i = 0; i < segmentHeader->updatePosSections.count; i++) { + updatePosSections[i] = UpdatePosSection(); + } +} + +/** + * @brief create a new brick-object + * + * @param brickDef json with all brick-definitions + * @param id brick-id + * + * @return new brick with parsed information + */ +Brick +DynamicSegment::createNewBrick(const Kitsunemimi::Hanami::BrickMeta &brickMeta, + const uint32_t id) +{ + Brick newBrick; + + // copy metadata + newBrick.brickId = id; + if(brickMeta.type == Kitsunemimi::Hanami::OUTPUT_BRICK_TYPE) { + newBrick.isOutputBrick = true; + } + /*if(brickDef.get("type").getString() == "transaction") { + newBrick.isTransactionBrick = true; + }*/ + if(brickMeta.type == Kitsunemimi::Hanami::INPUT_BRICK_TYPE) { + newBrick.isInputBrick = true; + } + + // convert other values + newBrick.brickPos = brickMeta.position; + newBrick.numberOfNeurons = brickMeta.numberOfNeurons; + newBrick.numberOfNeuronSections = getNumberOfNeuronSections(brickMeta.numberOfNeurons); + + for(uint8_t side = 0; side < 12; side++) { + newBrick.neighbors[side] = UNINIT_STATE_32; + } + + return newBrick; +} + +/** + * @brief init all bricks + * + * @param metaBase json with all brick-definitions + */ +void +DynamicSegment::addBricksToSegment(const Kitsunemimi::Hanami::SegmentMeta &segmentMeta) +{ + uint32_t neuronBrickIdCounter = 0; + uint32_t neuronSectionPosCounter = 0; + NeuronSection* section = nullptr; + uint32_t neuronIdCounter = 0; + + for(uint32_t i = 0; i < segmentMeta.bricks.size(); i++) + { + Brick newBrick = createNewBrick(segmentMeta.bricks.at(i), i); + newBrick.neuronSectionPos = neuronSectionPosCounter; + + for(uint32_t j = 0; j < newBrick.numberOfNeuronSections; j++) + { + section = &neuronSections[j + neuronSectionPosCounter]; + section->brickId = newBrick.brickId; + for(uint32_t k = 0; k < section->numberOfNeurons; k++) + { + section->neurons[k].id = neuronIdCounter; + neuronIdCounter++; + } + } + + // copy new brick to segment + bricks[neuronBrickIdCounter] = newBrick; + assert(neuronBrickIdCounter == newBrick.brickId); + neuronBrickIdCounter++; + neuronSectionPosCounter += newBrick.numberOfNeuronSections; + } + + return; +} + +/** + * @brief connect a single side of a specific brick + * + * @param sourceBrick pointer to the brick + * @param side side of the brick to connect + */ +void +DynamicSegment::connectBrick(Brick* sourceBrick, + const uint8_t side) +{ + const Kitsunemimi::Hanami::Position next = getNeighborPos(sourceBrick->brickPos, side); + // debug-output + // std::cout<bricks.count; t++) + { + Brick* targetBrick = &bricks[t]; + if(targetBrick->brickPos == next) + { + sourceBrick->neighbors[side] = targetBrick->brickId; + targetBrick->neighbors[11 - side] = sourceBrick->brickId; + } + } + } +} + +/** + * @brief connect all breaks of the segment + */ +void +DynamicSegment::connectAllBricks() +{ + for(uint32_t i = 0; i < segmentHeader->bricks.count; i++) + { + Brick* sourceBrick = &bricks[i]; + for(uint8_t side = 0; side < 12; side++) { + connectBrick(sourceBrick, side); + } + } +} + +/** + * @brief get next possible brick + * + * @param currentBrick actual brick + * @param maxPathLength maximum path length left + * + * @return last brick-id of the gone path + */ +uint32_t +DynamicSegment::goToNextInitBrick(Brick* currentBrick, uint32_t* maxPathLength) +{ + // check path-length to not go too far + (*maxPathLength)--; + if(*maxPathLength == 0) { + return currentBrick->brickId; + } + + // check based on the chance, if you go to the next, or not + const float chanceForNext = 0.0f; // TODO: make hard-coded value configurable + if(1000.0f * chanceForNext > (rand() % 1000)) { + return currentBrick->brickId; + } + + // get a random possible next brick + const uint8_t possibleNextSides[7] = {9, 3, 1, 4, 11, 5, 2}; + const uint8_t startSide = possibleNextSides[rand() % 7]; + for(uint32_t i = 0; i < 7; i++) + { + const uint8_t side = possibleNextSides[(i + startSide) % 7]; + const uint32_t nextBrickId = currentBrick->neighbors[side]; + if(nextBrickId != UNINIT_STATE_32) { + return goToNextInitBrick(&bricks[nextBrickId], maxPathLength); + } + } + + // if no further next brick was found, the give back tha actual one as end of the path + return currentBrick->brickId; +} + +/** + * @brief init target-brick-list of all bricks + * + * @return true, if successful, else false + */ +bool +DynamicSegment::initTargetBrickList() +{ + for(uint32_t i = 0; i < segmentHeader->bricks.count; i++) + { + Brick* baseBrick = &bricks[i]; + + // ignore output- and transaction-bricks, because they only forward to the border-buffer + // and not to other bricks + if(baseBrick->isOutputBrick + || baseBrick->isTransactionBrick) + { + continue; + } + + // test 1000 samples for possible next bricks + for(uint32_t counter = 0; counter < 1000; counter++) + { + uint32_t maxPathLength = 2; // TODO: make configurable + const uint32_t brickId = goToNextInitBrick(baseBrick, &maxPathLength); + if(brickId == baseBrick->brickId) + { + LOG_WARNING("brick has no next brick and is a dead-end. Brick-ID: " + + std::to_string(brickId)); + } + baseBrick->possibleTargetNeuronBrickIds[counter] = brickId; + } + } + + return true; +} + +/** + * @brief initialize the border-buffer and neighbor-list of the segment for each side + * + * @param segmentTemplate parsend content with the required information + * + * @return true, if successful, else false + */ +bool +DynamicSegment::initSlots(const Kitsunemimi::Hanami::SegmentMeta &segmentMeta) +{ + uint64_t posCounter = 0; + uint32_t slotCounter = 0; + + for(uint32_t i = 0; i < segmentMeta.bricks.size(); i++) + { + if(segmentMeta.bricks.at(i).type != Kitsunemimi::Hanami::INPUT_BRICK_TYPE + && segmentMeta.bricks.at(i).type != Kitsunemimi::Hanami::OUTPUT_BRICK_TYPE) + { + continue; + } + + const uint32_t numberOfNeurons = segmentMeta.bricks.at(i).numberOfNeurons; + SegmentSlot* currentSlot = &segmentSlots->slots[slotCounter]; + currentSlot->setName(segmentMeta.bricks.at(i).name); + currentSlot->numberOfNeurons = numberOfNeurons; + currentSlot->inputTransferBufferPos = posCounter; + currentSlot->outputTransferBufferPos = posCounter; + + if(segmentMeta.bricks.at(i).type == Kitsunemimi::Hanami::INPUT_BRICK_TYPE) { + currentSlot->direction = INPUT_DIRECTION; + } else { + currentSlot->direction = OUTPUT_DIRECTION; + } + + // update total position pointer, because all border-buffers are in the same blog + // beside each other + posCounter += numberOfNeurons; + slotCounter++; + } + + assert(posCounter == segmentHeader->inputTransfers.count); + + return true; +} diff --git a/src/components/KyoukoMind/src/core/segments/dynamic_segment/dynamic_segment.h b/src/components/KyoukoMind/src/core/segments/dynamic_segment/dynamic_segment.h new file mode 100644 index 00000000..fc235cf6 --- /dev/null +++ b/src/components/KyoukoMind/src/core/segments/dynamic_segment/dynamic_segment.h @@ -0,0 +1,78 @@ +/** + * @file dynamic_segment.h + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2022 Tobias Anker + * + * 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. + */ + +#ifndef KYOUKOMIND_DYNAMIC_SEGMENTS_H +#define KYOUKOMIND_DYNAMIC_SEGMENTS_H + +#include + +#include +#include "objects.h" + +namespace Kitsunemimi { +class GpuData; +} + +class DynamicSegment + : public AbstractSegment +{ +public: + DynamicSegment(); + DynamicSegment(const void* data, const uint64_t dataSize); + ~DynamicSegment(); + + bool initSegment(const std::string &name, + const Kitsunemimi::Hanami::SegmentMeta &segmentMeta); + bool reinitPointer(const uint64_t numberOfBytes); + + Brick* bricks = nullptr; + uint32_t* brickOrder = nullptr; + NeuronSection* neuronSections = nullptr; + SynapseSection* synapseSections = nullptr; + UpdatePosSection* updatePosSections = nullptr; + + Kitsunemimi::GpuData* data = nullptr; + +private: + DynamicSegmentSettings initSettings(const Kitsunemimi::Hanami::SegmentMeta &segmentMeta); + SegmentHeader createNewHeader(const uint32_t numberOfBricks, + const uint32_t numberOfNeuronSections, + const uint64_t numberOfSynapseSections, + const uint64_t borderbufferSize); + void initSegmentPointer(const SegmentHeader &header); + bool connectBorderBuffer(); + void allocateSegment(SegmentHeader &header); + void initDefaultValues(); + void initGpu(); + + void addBricksToSegment(const Kitsunemimi::Hanami::SegmentMeta &segmentMeta); + bool initTargetBrickList(); + + Brick createNewBrick(const Kitsunemimi::Hanami::BrickMeta &brickMeta, const uint32_t id); + void connectBrick(Brick *sourceBrick, const uint8_t side); + void connectAllBricks(); + bool initializeNeurons(const Kitsunemimi::Hanami::SegmentMeta &segmentMeta); + uint32_t goToNextInitBrick(Brick* currentBrick, uint32_t* maxPathLength); + bool initSlots(const Kitsunemimi::Hanami::SegmentMeta &segmentMeta); +}; + +#endif // KYOUKOMIND_DYNAMIC_SEGMENTS_H diff --git a/src/components/KyoukoMind/src/core/segments/dynamic_segment/gpu_kernel.cl b/src/components/KyoukoMind/src/core/segments/dynamic_segment/gpu_kernel.cl new file mode 100644 index 00000000..adbf2214 --- /dev/null +++ b/src/components/KyoukoMind/src/core/segments/dynamic_segment/gpu_kernel.cl @@ -0,0 +1,920 @@ +/** + * @file gpu_kernel.cl + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2019 Tobias Anker + * + * 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. + */ + +// const predefined values +#define UNINIT_STATE_64 0xFFFFFFFFFFFFFFFF +#define UNINIT_STATE_32 0xFFFFFFFF +#define UNINIT_STATE_24 0xFFFFFF +#define UNINIT_STATE_16 0xFFFF +#define UNINIT_STATE_8 0xFF + +// common information +#define SYNAPSES_PER_SYNAPSESECTION 30 +#define NEURONS_PER_NEURONSECTION 62 +#define NUMBER_OF_RAND_VALUES 10485760 +#define RAND_MAX 2147483647 + +typedef struct kuuid_struct +{ + char uuid[37]; + uchar padding[3]; + // total size: 40 Bytes +} kuuid; + + +typedef struct Position_struct +{ + uint x; + uint y; + uint z; + uint w; +} Position; + +enum SegmentTypes +{ + UNDEFINED_SEGMENT = 0, + INPUT_SEGMENT = 1, + OUTPUT_SEGMENT = 2, + DYNAMIC_SEGMENT = 3, +}; + +typedef struct SegmentHeaderEntry +{ + ulong bytePos; + ulong count; + + // total size: 16 Byte +} SegmentHeaderEntry; + +typedef struct SegmentHeader_struct +{ + uchar objectType; + uchar version; + uchar segmentType; + uchar padding; + uint segmentID; + ulong staticDataSize; + Position position; + + kuuid parentClusterId; + + // synapse-segment + SegmentHeaderEntry name; + SegmentHeaderEntry settings; + SegmentHeaderEntry slotList; + SegmentHeaderEntry inputTransfers; + SegmentHeaderEntry outputTransfers; + + SegmentHeaderEntry bricks; + SegmentHeaderEntry brickOrder; + SegmentHeaderEntry neuronSections; + SegmentHeaderEntry inputs; + SegmentHeaderEntry outputs; + SegmentHeaderEntry updatePosSections; + + SegmentHeaderEntry synapseSections; + + uchar padding2[246]; + + // total size: 512 Byte +} SegmentHeader; + +//================================================================================================== + +typedef struct DynamicNeuron_struct +{ + float input; + float border; + float potential; + float delta; + + uchar refractionTime; + uchar active; + uchar padding[2]; + + uint id; + + uint targetBorderId; + uint targetSectionId; + + // total size: 32 Byte +} DynamicNeuron; + +//================================================================================================== + +typedef struct Synapse_struct +{ + float weight; + float border; + ushort targetNeuronId; + char activeCounter; + uchar active; + uchar padding[4]; + // total size: 16 Byte +} Synapse; + +//================================================================================================== + +typedef struct Brick_struct +{ + // common + uint brickId; + bool isOutputBrick; + bool isTransactionBrick; + bool isInputBrick; + uchar padding1[13]; + uint neuronSectionPos; + + Position brickPos; + uint neighbors[12]; + + uint possibleTargetNeuronBrickIds[1000]; + uint numberOfNeurons; + uint numberOfNeuronSections; + + // total size: 4096 Bytes +} Brick; + +//================================================================================================== + +typedef struct NeuronSection_struct +{ + DynamicNeuron neurons[NEURONS_PER_NEURONSECTION]; + uint numberOfNeurons; + uint id; + uint brickId; + uint backwardNextId; + uchar padding[48]; + // total size: 2048 Byte +} NeuronSection; + +//================================================================================================== + +typedef struct SynapseSection_struct +{ + uchar active; + uchar padding[3]; + uint randomPos; + + uint targetNeuronSectionId; + uint brickId; + uchar padding2[8]; + uint forwardNext; + uint backwardNext; + + Synapse synapses[SYNAPSES_PER_SYNAPSESECTION]; + + // total size: 512 Byte +} SynapseSection; + +//================================================================================================== + +typedef struct UpdatePos_struct +{ + uint type; + uint forwardNewId; + uint randomPos; + uint targetNeuronSectionId; +} UpdatePos; + +//================================================================================================== + +typedef struct UpdatePosSection_struct +{ + UpdatePos positions[NEURONS_PER_NEURONSECTION]; + uint numberOfPositions; + uint backwardNewId; + uchar padding[24]; + + // total size: 512 Byte +} UpdatePosSection; + +//================================================================================================== + +typedef struct DynamicSegmentSettings_struct +{ + ulong maxSynapseSections; + float synapseDeleteBorder; + float neuronCooldown; + float memorizing; + float gliaValue; + float signNeg; + float potentialOverflow; + float synapseSegmentation; + float backpropagationBorder; + uchar refractionTime; + uchar doLearn; + uchar updateSections; + + uchar padding[213]; + + // total size: 256 Byte +} DynamicSegmentSettings; + +//================================================================================================== + +inline void +initNewSection(const uint position, + __global SynapseSection* synapseSections, + __global UpdatePos* updatePos, + __global const uint* randomValues) +{ + __global SynapseSection* targetSection = &synapseSections[position]; + targetSection->active = 1; + targetSection->randomPos = updatePos->randomPos; + targetSection->targetNeuronSectionId = updatePos->targetNeuronSectionId; +} + +/** + * @brief initialize a new specific synapse + * + * @param section current processed synapse-section + * @param synapse new synapse, which has to be initialized + * @param bricks array of all bricks + * @param sourceNeuron source-neuron, who triggered the section + * @param segmentSettings settings of the section + * @param remainingWeight weight of which to cut of a part for the new synapse + */ +inline void +createNewSynapse(__global SynapseSection* section, + __global Synapse* synapse, + __global const NeuronSection* neuronSections, + __global const DynamicSegmentSettings* segmentSettings, + const float remainingWeight, + const float outH, + __global const uint* randomValues) +{ + const float randMax = (float)(RAND_MAX); + const float maxWeight = outH / (float)(segmentSettings->synapseSegmentation); + uint signRand = 0; + const float sigNeg = 0.5f; + + // set activation-border + section->randomPos = (section->randomPos + 1) % NUMBER_OF_RAND_VALUES; + float newWeight = maxWeight * ((float)(randomValues[section->randomPos]) / randMax); + synapse->border = (float)(remainingWeight < newWeight) * remainingWeight + + (float)(remainingWeight >= newWeight) * newWeight; + + // set target neuron + section->randomPos = (section->randomPos + 1) % NUMBER_OF_RAND_VALUES; + synapse->targetNeuronId = (ushort)(randomValues[section->randomPos] + % neuronSections[section->targetNeuronSectionId].numberOfNeurons); + + section->randomPos = (section->randomPos + 1) % NUMBER_OF_RAND_VALUES; + synapse->weight = ((float)(randomValues[section->randomPos]) / randMax) / 10.0f; + + // update weight with sign + section->randomPos = (section->randomPos + 1) % NUMBER_OF_RAND_VALUES; + signRand = randomValues[section->randomPos] % 1000; + synapse->weight *= (float)(1.0f - (1000.0f * sigNeg > signRand) * 2); + synapse->active = 0; + + synapse->activeCounter = 1; +} + +inline void +synapseProcessingBackward(const uint neuronSectionId, + __global NeuronSection* neuronSection, + __global SynapseSection* section, + __global SynapseSection* synapseSections, + __global UpdatePosSection* updatePosSections) +{ + uint pos = 0; + __global Synapse* synapse = NULL; + __global DynamicNeuron* targetNeuron = NULL; + uchar active = 0; + + // iterate over all synapses in the section + while(pos < SYNAPSES_PER_SYNAPSESECTION) + { + synapse = §ion->synapses[pos]; + neuronSection->neurons[synapse->targetNeuronId].input += ((float)(synapse->active)) * synapse->weight; + synapse->active = 0; + pos++; + } + + if(section->backwardNext == UNINIT_STATE_32) + { + __global UpdatePosSection* updatePosSection = &updatePosSections[neuronSectionId]; + section->backwardNext = updatePosSection->backwardNewId; + updatePosSection->backwardNewId = UNINIT_STATE_32; + + if(section->backwardNext != UNINIT_STATE_32) + { + __global SynapseSection* targetSection = &synapseSections[section->backwardNext]; + if(targetSection->active != 1) + { + targetSection->active = 1; + targetSection->randomPos = neuronSectionId % NUMBER_OF_RAND_VALUES; + targetSection->targetNeuronSectionId = neuronSectionId; + } + } + } + + if(section->backwardNext != UNINIT_STATE_32) + { + synapseProcessingBackward(neuronSectionId, + neuronSection, + &synapseSections[section->backwardNext], + synapseSections, + updatePosSections); + } +} + +inline void +processSingleSectionBackward(const uint neuronSectionId, + __global NeuronSection* neuronSections, + __global SynapseSection* synapseSections, + __global UpdatePosSection* updatePosSections) +{ + __global NeuronSection* section = &neuronSections[neuronSectionId]; + + if(section->backwardNextId == UNINIT_STATE_32) + { + __global UpdatePosSection* updatePosSection = &updatePosSections[neuronSectionId]; + section->backwardNextId = updatePosSection->backwardNewId; + updatePosSection->backwardNewId = UNINIT_STATE_32; + + if(section->backwardNextId != UNINIT_STATE_32) + { + __global SynapseSection* targetSection = &synapseSections[section->backwardNextId]; + if(targetSection->active != 1) + { + targetSection->active = 1; + targetSection->randomPos = neuronSectionId % NUMBER_OF_RAND_VALUES; + targetSection->targetNeuronSectionId = neuronSectionId; + } + } + } + + if(section->backwardNextId != UNINIT_STATE_32) + { + synapseProcessingBackward(neuronSectionId, + section, + &synapseSections[section->backwardNextId], + synapseSections, + updatePosSections); + } +} + + +/** + * @brief process synapse-section + * + * @param section current processed synapse-section + * @param segment refernece to the processed segment + * @param sourceNeuron source-neuron, who triggered the section + * @param netH wight-value, which comes into the section + * @param outH multiplicator + */ +inline void +synapseProcessing(const uint neuronId, + const uint neuronSectionId, + __global SynapseSection* section, + __global const DynamicNeuron* sourceNeuron, + __global NeuronSection* neuronSections, + __global SynapseSection* synapseSections, + __global UpdatePosSection* updatePosSections, + __global DynamicSegmentSettings* dynamicSegmentSettings, + float netH, + const float outH, + __global const uint* randomValues) +{ + uint pos = 0; + __global Synapse* synapse = NULL; + __global DynamicNeuron* targetNeuron = NULL; + uchar active = 0; + + // iterate over all synapses in the section + while(pos < SYNAPSES_PER_SYNAPSESECTION + && netH > 0.0f) + { + synapse = §ion->synapses[pos]; + + // create new synapse if necesarry and learning is active + if(synapse->targetNeuronId == UNINIT_STATE_16) + { + createNewSynapse(section, + synapse, + neuronSections, + dynamicSegmentSettings, + netH, + outH, + randomValues); + } + + // update target-neuron + //targetNeuron = &(neuronSections[section->targetNeuronSectionId].neurons[synapse->targetNeuronId]); + //targetNeuron->input += synapse->weight; + + // update active-counter + active = (synapse->weight > 0) == (targetNeuron->potential > targetNeuron->border); + synapse->activeCounter += active * (uchar)(synapse->activeCounter < 126); + synapse->active = 1; + + // update loop-counter + netH -= synapse->border; + pos++; + } + + if(netH > 0.01f) + { + if(section->forwardNext == UNINIT_STATE_32) + { + __global UpdatePos* updatePos = &updatePosSections[neuronSectionId].positions[neuronId]; + section->forwardNext= updatePos->forwardNewId; + updatePos->forwardNewId = UNINIT_STATE_32; + updatePos->type = section->forwardNext == UNINIT_STATE_32; + + if(section->forwardNext != UNINIT_STATE_32) + { + __global SynapseSection* targetSection = &synapseSections[section->forwardNext]; + targetSection->active = 1; + targetSection->randomPos = updatePos->randomPos; + targetSection->targetNeuronSectionId = updatePos->targetNeuronSectionId; + } + } + + if(section->forwardNext != UNINIT_STATE_32) + { + + synapseProcessing(neuronId, + neuronSectionId, + &synapseSections[section->forwardNext], + sourceNeuron, + neuronSections, + synapseSections, + updatePosSections, + dynamicSegmentSettings, + netH, + outH, + randomValues); + } + } +} + +/** + * @brief process only a single neuron + * + * @param neuron pointer to neuron to process + * @param segment segment where the neuron belongs to + */ +inline void +processSingleNeuron(const uint neuronId, + const uint neuronSectionId, + __global DynamicNeuron* neuron, + __global NeuronSection* neuronSections, + __global SynapseSection* synapseSections, + __global UpdatePosSection* updatePosSections, + __global DynamicSegmentSettings* dynamicSegmentSettings, + __global const uint* randomValues) +{ + // handle active-state + if(neuron->active != 0) + { + if(neuron->targetSectionId == UNINIT_STATE_32) + { + __global UpdatePos* updatePos = &updatePosSections[neuronSectionId].positions[neuronId]; + neuron->targetSectionId = updatePos->forwardNewId; + updatePos->forwardNewId = UNINIT_STATE_32; + updatePos->type = neuron->targetSectionId == UNINIT_STATE_32; + + if(neuron->targetSectionId != UNINIT_STATE_32) + { + __global SynapseSection* targetSection = &synapseSections[neuron->targetSectionId]; + targetSection->active = 1; + targetSection->randomPos = updatePos->randomPos; + targetSection->targetNeuronSectionId = updatePos->targetNeuronSectionId; + } + } + + if(neuron->targetSectionId != UNINIT_STATE_32) + { + synapseProcessing(neuronId, + neuronSectionId, + &synapseSections[neuron->targetSectionId], + neuron, + neuronSections, + synapseSections, + updatePosSections, + dynamicSegmentSettings, + neuron->potential, + neuron->potential, + randomValues); + } + } +} + +/** + * @brief processNeuron + * @param neuron + * @param segment + */ +inline void +processNeuron(__global DynamicNeuron* neuron, + __global DynamicSegmentSettings* dynamicSegmentSettings) +{ + neuron->potential /= dynamicSegmentSettings->neuronCooldown; + neuron->refractionTime = neuron->refractionTime >> 1; + + if(neuron->refractionTime == 0) + { + neuron->potential = dynamicSegmentSettings->potentialOverflow * neuron->input; + neuron->refractionTime = dynamicSegmentSettings->refractionTime; + } + + // update neuron + neuron->potential -= neuron->border; + neuron->active = neuron->potential > 0.0f; + neuron->input = 0.0f; + neuron->potential = log2(neuron->potential + 1.0f); +} + +/** + * @brief reset neurons of a output brick + * + * @param brick pointer to the brick + * @param segment segment where the brick belongs to + */ +inline void +processNeuronsOfOutputBrick(__global const Brick* brick, + __global NeuronSection* neuronSections, + __global float* outputTransfers, + __global SynapseSection* synapseSections, + __global UpdatePosSection* updatePosSections, + __global DynamicSegmentSettings* dynamicSegmentSettings) +{ + __global DynamicNeuron* neuron = NULL; + __global NeuronSection* section = NULL; + + // iterate over all neurons within the brick + for(uint neuronSectionId = brick->neuronSectionPos + get_global_id(0); + neuronSectionId < brick->numberOfNeuronSections + brick->neuronSectionPos; + neuronSectionId += get_global_size(0)) + { + processSingleSectionBackward(neuronSectionId, + neuronSections, + synapseSections, + updatePosSections); + barrier(CLK_GLOBAL_MEM_FENCE); + + section = &neuronSections[neuronSectionId]; + for(uint neuronId = 0; + neuronId < section->numberOfNeurons; + neuronId++) + { + neuron = §ion->neurons[neuronId]; + neuron->potential = dynamicSegmentSettings->potentialOverflow * neuron->input; + outputTransfers[neuron->targetBorderId] = neuron->potential; + neuron->input = 0.0f; + } + } +} + +/** + * @brief reset neurons of a input brick + * + * @param brick pointer to the brick + * @param segment segment where the brick belongs to + */ +inline void +processNeuronsOfInputBrick(__global const Brick* brick, + __global NeuronSection* neuronSections, + __global float* inputTransfers, + __global SynapseSection* synapseSections, + __global UpdatePosSection* updatePosSections, + __global DynamicSegmentSettings* dynamicSegmentSettings, + __global const uint* randomValues) +{ + __global DynamicNeuron* neuron = NULL; + __global NeuronSection* section = NULL; + + // iterate over all neurons within the brick + for(uint neuronSectionId = brick->neuronSectionPos + get_global_id(0); + neuronSectionId < brick->numberOfNeuronSections + brick->neuronSectionPos; + neuronSectionId += get_global_size(0)) + { + section = &neuronSections[neuronSectionId]; + for(uint neuronId = 0; + neuronId < section->numberOfNeurons; + neuronId++) + { + neuron = §ion->neurons[neuronId]; + neuron->potential = inputTransfers[neuron->targetBorderId]; + neuron->active = neuron->potential > 0.0f; + + processSingleNeuron(neuronId, + neuronSectionId, + neuron, + neuronSections, + synapseSections, + updatePosSections, + dynamicSegmentSettings, + randomValues); + } + } +} + +/** + * @brief reset neurons of a normal brick + * + * @param brick pointer to the brick + * @param segment segment where the brick belongs to + */ +inline void +processNeuronsOfNormalBrick(__global const Brick* brick, + __global NeuronSection* neuronSections, + __global SynapseSection* synapseSections, + __global UpdatePosSection* updatePosSections, + __global DynamicSegmentSettings* dynamicSegmentSettings, + __global const uint* randomValues) +{ + __global DynamicNeuron* neuron = NULL; + __global NeuronSection* section = NULL; + + // iterate over all neurons within the brick + for(uint neuronSectionId = brick->neuronSectionPos + get_global_id(0); + neuronSectionId < brick->numberOfNeuronSections + brick->neuronSectionPos; + neuronSectionId += get_global_size(0)) + { + processSingleSectionBackward(neuronSectionId, + neuronSections, + synapseSections, + updatePosSections); + barrier(CLK_GLOBAL_MEM_FENCE); + + section = &neuronSections[neuronSectionId]; + for(uint neuronId = 0; + neuronId < section->numberOfNeurons; + neuronId++) + { + neuron = §ion->neurons[neuronId]; + processNeuron(neuron, dynamicSegmentSettings); + processSingleNeuron(neuronId, + neuronSectionId, + neuron, + neuronSections, + synapseSections, + updatePosSections, + dynamicSegmentSettings, + randomValues); + } + } +} + +/** + * @brief process all neurons within a specific brick and also all synapse-sections, + * which are connected to an active neuron + * + * @param segment segment to process + */ +__kernel void +prcessDynamicSegment(__global Brick* bricks, + __global uint* brickOrder, + __global NeuronSection* neuronSections, + __global SynapseSection* synapseSections, + __global UpdatePosSection* updatePosSections, + __global SegmentHeader* segmentHeader, + __global DynamicSegmentSettings* dynamicSegmentSettings, + __global float* inputTransfers, + __global float* outputTransfers, + __global uint* randomValues) +{ + const uint numberOfBricks = segmentHeader->bricks.count; + for(uint pos = 0; pos < numberOfBricks; pos++) + { + const uint brickId = brickOrder[pos]; + __global Brick* brick = &bricks[brickId]; + if(brick->isInputBrick) + { + processNeuronsOfInputBrick(brick, + neuronSections, + inputTransfers, + synapseSections, + updatePosSections, + dynamicSegmentSettings, + randomValues); + } + else if(brick->isOutputBrick) + { + processNeuronsOfOutputBrick(brick, + neuronSections, + outputTransfers, + synapseSections, + updatePosSections, + dynamicSegmentSettings); + } + else + { + processNeuronsOfNormalBrick(brick, + neuronSections, + synapseSections, + updatePosSections, + dynamicSegmentSettings, + randomValues); + } + + barrier(CLK_GLOBAL_MEM_FENCE); + } +} + + +/** + * @brief backpropagate values of an output-brick + * + * @param brick brick to process + * @param segment segment where the brick belongs to + */ +inline void +backpropagateOutput(__global const Brick* brick, + __global float* inputTransfers, + __global NeuronSection* neuronSections, + __global DynamicSegmentSettings* dynamicSegmentSettings) +{ + __global DynamicNeuron* neuron = NULL; + __global NeuronSection* section = NULL; + + for(uint neuronSectionId = brick->neuronSectionPos + get_group_id(0); + neuronSectionId < brick->numberOfNeuronSections + brick->neuronSectionPos; + neuronSectionId += get_global_size(0)) + { + section = &neuronSections[neuronSectionId]; + for(uint neuronId = get_local_id(0); + neuronId < section->numberOfNeurons; + neuronId += get_local_size(0)) + { + neuron = §ion->neurons[neuronId]; + neuron->delta = inputTransfers[neuron->targetBorderId]; + inputTransfers[neuron->targetBorderId] = 0.0f; + } + } +} + +/** + * @brief run backpropagation for a single synapse-section + * + * @param section pointer to section to process + * @param sourceNeuron pointer to the neuron, who triggered the section + * @param netH neuron-potential + * @param outH output-multiplicator + * @param brick brick where the seciton is located + * @param segment segment where section belongs to + */ +inline void +backpropagateSection(__global SynapseSection* section, + __global DynamicNeuron* sourceNeuron, + float netH, + __global const Brick* brick, + __global NeuronSection* neuronSections, + __global SynapseSection* synapseSections) +{ + __global Synapse* synapse = NULL; + __global DynamicNeuron* targetNeuron = NULL; + __global NeuronSection* neuronSection = &neuronSections[section->targetNeuronSectionId]; + float learnValue = 0.2f; + ushort pos = 0; + + // iterate over all synapses in the section + while(pos < SYNAPSES_PER_SYNAPSESECTION + && netH > 0.0f) + { + // break look, if no more synapses to process + synapse = §ion->synapses[pos]; + + // update weight + learnValue = (float)(126 - synapse->activeCounter) * 0.0002f; + learnValue += 0.05f; + targetNeuron = &neuronSection->neurons[synapse->targetNeuronId]; + sourceNeuron->delta += targetNeuron->delta * synapse->weight; + synapse->weight -= learnValue * targetNeuron->delta; + + netH -= synapse->border; + pos++; + } + + if(section->forwardNext != UNINIT_STATE_32 + && netH > 0.01f) + { + backpropagateSection(&synapseSections[section->forwardNext], + sourceNeuron, + netH, + brick, + neuronSections, + synapseSections); + } +} + +/** + * @brief run back-propagation over the hidden neurons + * + * @param brick pointer to current brick + * @param segment pointer to currect segment to process, which contains the brick + */ +inline void +backpropagateNeurons(__global const Brick* brick, + __global NeuronSection* neuronSections, + __global SynapseSection* synapseSections, + __global UpdatePosSection* updatePosSections, + __global float* outputTransfers) +{ + __global DynamicNeuron* sourceNeuron = NULL; + __global NeuronSection* neuronSection = NULL; + __global UpdatePosSection* updatePosSection = NULL; + + // iterate over all neurons within the brick + for(uint neuronSectionId = brick->neuronSectionPos + get_group_id(0); + neuronSectionId < brick->numberOfNeuronSections + brick->neuronSectionPos; + neuronSectionId += get_global_size(0)) + { + neuronSection = &neuronSections[neuronSectionId]; + updatePosSection = &updatePosSections[neuronSectionId]; + for(uint neuronId = get_local_id(0); + neuronId < neuronSection->numberOfNeurons; + neuronId += get_local_size(0)) + { + // skip section, if not active + sourceNeuron = &neuronSection->neurons[neuronId]; + //UpdatePos* updatePos = &updatePosSection->positions[neuronId]; + if(sourceNeuron->targetSectionId != UNINIT_STATE_32) + { + sourceNeuron->delta = 0.0f; + + // set start-values + if(sourceNeuron->active) + { + backpropagateSection(&synapseSections[sourceNeuron->targetSectionId], + sourceNeuron, + sourceNeuron->potential, + brick, + neuronSections, + synapseSections); + + sourceNeuron->delta *= 1.4427f * pow(0.5f, sourceNeuron->potential); + } + + if(brick->isInputBrick) { + outputTransfers[sourceNeuron->targetBorderId] = sourceNeuron->delta; + } + } + } + } +} + +/** + * @brief correct wight of synapses within + * + * @param segment segment to process + */ +__kernel void +rewightDynamicSegment(__global Brick* bricks, + __global uint* brickOrder, + __global NeuronSection* neuronSections, + __global SynapseSection* synapseSections, + __global UpdatePosSection* updatePosSections, + __global SegmentHeader* segmentHeader, + __global DynamicSegmentSettings* dynamicSegmentSettings, + __global float* inputTransfers, + __global float* outputTransfers) +{ + // run back-propagation over all internal neurons and synapses + const uint numberOfBricks = segmentHeader->bricks.count; + for(int pos = numberOfBricks - 1; pos >= 0; pos--) + { + const uint brickId = brickOrder[pos]; + __global Brick* brick = &bricks[brickId]; + if(brick->isOutputBrick) + { + backpropagateOutput(brick, + inputTransfers, + neuronSections, + dynamicSegmentSettings); + barrier(CLK_GLOBAL_MEM_FENCE); + } + + backpropagateNeurons(brick, + neuronSections, + synapseSections, + updatePosSections, + outputTransfers); + barrier(CLK_GLOBAL_MEM_FENCE); + } +} diff --git a/src/components/KyoukoMind/src/core/segments/dynamic_segment/objects.h b/src/components/KyoukoMind/src/core/segments/dynamic_segment/objects.h new file mode 100644 index 00000000..b156269b --- /dev/null +++ b/src/components/KyoukoMind/src/core/segments/dynamic_segment/objects.h @@ -0,0 +1,156 @@ +/** + * @file objects.h + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2022 Tobias Anker + * + * 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. + */ + +#ifndef KYOUKOMIND_DYNAMIC_SEGMENT_OBJECTS_H +#define KYOUKOMIND_DYNAMIC_SEGMENT_OBJECTS_H + +#include +#include + +//================================================================================================== + +struct DynamicNeuron +{ + float input = 0.0f; + float border = 100.0f; + float potential = 0.0f; + float delta = 0.0f; + + uint8_t refractionTime = 1; + uint8_t active = 0; + uint8_t padding[2]; + + uint32_t id = 0; + + uint32_t targetBorderId = UNINIT_STATE_32; + uint32_t targetSectionId = UNINIT_STATE_32; + + // total size: 32 Byte +}; + +//================================================================================================== + +struct NeuronSection +{ + DynamicNeuron neurons[NEURONS_PER_NEURONSECTION]; + uint32_t numberOfNeurons = 0; + uint32_t id = 0; + uint32_t brickId = 0; + uint32_t backwardNextId = UNINIT_STATE_32; + uint8_t padding[48]; + + NeuronSection() + { + for(uint32_t i = 0; i < NEURONS_PER_NEURONSECTION; i++) { + neurons[i] = DynamicNeuron(); + } + } + // total size: 2048 Byte +}; + +//================================================================================================== + +struct Synapse +{ + float weight = 0.0f; + float border = 0.0f; + uint16_t targetNeuronId = UNINIT_STATE_16; + int8_t activeCounter = 0; + uint8_t padding[5]; + // total size: 16 Byte +}; + +//================================================================================================== + +struct SynapseSection +{ + uint8_t active = Kitsunemimi::ItemBuffer::ACTIVE_SECTION; + uint8_t padding[3]; + uint32_t randomPos = 0; + + uint32_t targetNeuronSectionId = 0; + uint32_t nextId = UNINIT_STATE_32; + + Synapse synapses[SYNAPSES_PER_SYNAPSESECTION]; + + SynapseSection() + { + for(uint32_t i = 0; i < SYNAPSES_PER_SYNAPSESECTION; i++) { + synapses[i] = Synapse(); + } + } + // total size: 512 Byte +}; + +//================================================================================================== + +struct UpdatePos +{ + uint32_t type = 0; + uint32_t forwardNewId = UNINIT_STATE_32; + uint32_t randomPos = UNINIT_STATE_32; + uint32_t targetNeuronSectionId = UNINIT_STATE_32; + // total size: 16 Byte +}; + +//================================================================================================== + +struct UpdatePosSection +{ + UpdatePos positions[NEURONS_PER_NEURONSECTION]; + uint32_t numberOfPositions = 0; + uint32_t backwardNewId = UNINIT_STATE_32; + uint8_t padding[24]; + + UpdatePosSection() + { + for(uint32_t i = 0; i < NEURONS_PER_NEURONSECTION; i++) { + positions[i] = UpdatePos(); + } + } + // total size: 1024 Byte +}; + +//================================================================================================== + +struct DynamicSegmentSettings +{ + uint64_t maxSynapseSections = 0; + float synapseDeleteBorder = 1.0f; + float neuronCooldown = 100.0f; + float memorizing = 0.1f; + float gliaValue = 1.0f; + float signNeg = 0.6f; + float potentialOverflow = 1.0f; + float synapseSegmentation = 10.0f; + float backpropagationBorder = 0.00001f; + uint8_t refractionTime = 1; + uint8_t doLearn = 0; + uint8_t updateSections = 0; + + uint8_t padding[213]; + + // total size: 256 Byte +}; + +//================================================================================================== +#endif // KYOUKOMIND_DYNAMIC_SEGMENT_OBJECTS_H diff --git a/src/components/KyoukoMind/src/core/segments/dynamic_segment/processing.h b/src/components/KyoukoMind/src/core/segments/dynamic_segment/processing.h new file mode 100644 index 00000000..a4be6849 --- /dev/null +++ b/src/components/KyoukoMind/src/core/segments/dynamic_segment/processing.h @@ -0,0 +1,389 @@ +/** + * @file processing.h + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2022 Tobias Anker + * + * 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. + */ + +#ifndef KYOUKOMIND_DYNAMIC_PROCESSING_H +#define KYOUKOMIND_DYNAMIC_PROCESSING_H + +#include + +#include +#include + +#include "objects.h" +#include "dynamic_segment.h" + +/** + * @brief initialize a new specific synapse + * + * @param section current processed synapse-section + * @param synapse new synapse, which has to be initialized + * @param bricks array of all bricks + * @param sourceNeuron source-neuron, who triggered the section + * @param segmentSettings settings of the section + * @param remainingWeight weight of which to cut of a part for the new synapse + */ +inline void +createNewSynapse(SynapseSection* section, + Synapse* synapse, + const NeuronSection* neuronSections, + const DynamicSegmentSettings* segmentSettings, + const float remainingWeight, + const float outH) +{ + const uint32_t* randomValues = KyoukoRoot::m_randomValues; + const float randMax = static_cast(RAND_MAX); + const float maxWeight = outH / static_cast(segmentSettings->synapseSegmentation); + uint32_t signRand = 0; + const float sigNeg = segmentSettings->signNeg; + + // set activation-border + section->randomPos = (section->randomPos + 1) % NUMBER_OF_RAND_VALUES; + float newWeight = maxWeight * (static_cast(randomValues[section->randomPos]) / randMax); + synapse->border = static_cast(remainingWeight < newWeight) * remainingWeight + + static_cast(remainingWeight >= newWeight) * newWeight; + + // set target neuron + section->randomPos = (section->randomPos + 1) % NUMBER_OF_RAND_VALUES; + synapse->targetNeuronId = static_cast(randomValues[section->randomPos] + % neuronSections[section->targetNeuronSectionId].numberOfNeurons); + + + section->randomPos = (section->randomPos + 1) % NUMBER_OF_RAND_VALUES; + synapse->weight = (static_cast(randomValues[section->randomPos]) / randMax) / 10.0f; + + // update weight with sign + section->randomPos = (section->randomPos + 1) % NUMBER_OF_RAND_VALUES; + signRand = randomValues[section->randomPos] % 1000; + synapse->weight *= static_cast(1.0f - (1000.0f * sigNeg > signRand) * 2); + + + synapse->activeCounter = 1; +} + +/** + * @brief process synapse-section + * + * @param section current processed synapse-section + * @param segment refernece to the processed segment + * @param sourceNeuron source-neuron, who triggered the section + * @param netH wight-value, which comes into the section + * @param outH multiplicator + */ +inline void +synapseProcessing(const uint32_t neuronId, + const uint32_t neuronSectionId, + SynapseSection* section, + const DynamicNeuron* sourceNeuron, + NeuronSection* neuronSections, + SynapseSection* synapseSections, + UpdatePosSection* updatePosSections, + DynamicSegmentSettings* dynamicSegmentSettings, + float netH, + const float outH) +{ + uint32_t pos = 0; + Synapse* synapse = nullptr; + DynamicNeuron* targetNeuron = nullptr; + uint8_t active = 0; + + // iterate over all synapses in the section + while(pos < SYNAPSES_PER_SYNAPSESECTION + && netH > 0.0f) + { + synapse = §ion->synapses[pos]; + + // create new synapse if necesarry and learning is active + if(synapse->targetNeuronId == UNINIT_STATE_16) + { + createNewSynapse(section, + synapse, + neuronSections, + dynamicSegmentSettings, + netH, + outH); + } + + // update target-neuron + targetNeuron = &(neuronSections[section->targetNeuronSectionId].neurons[synapse->targetNeuronId]); + targetNeuron->input += synapse->weight; + + // update active-counter + active = (synapse->weight > 0) == (targetNeuron->potential > targetNeuron->border); + synapse->activeCounter += active * static_cast(synapse->activeCounter < 126); + + // update loop-counter + netH -= synapse->border; + pos++; + } + + if(netH > 0.01f) + { + if(section->nextId == UNINIT_STATE_32) + { + updatePosSections[neuronSectionId].positions[neuronId].type = 1; + dynamicSegmentSettings->updateSections = 1; + return; + } + + synapseProcessing(neuronId, + neuronSectionId, + &synapseSections[section->nextId], + sourceNeuron, + neuronSections, + synapseSections, + updatePosSections, + dynamicSegmentSettings, + netH, + outH); + } +} + +/** + * @brief process only a single neuron + * + * @param neuron pointer to neuron to process + * @param segment segment where the neuron belongs to + */ +inline void +processSingleNeuron(const uint32_t neuronId, + const uint32_t neuronSectionId, + DynamicNeuron* neuron, + NeuronSection* neuronSections, + SynapseSection* synapseSections, + UpdatePosSection* updatePosSections, + DynamicSegmentSettings* dynamicSegmentSettings) +{ + // handle active-state + if(neuron->active == 0) { + return; + } + + if(neuron->targetSectionId == UNINIT_STATE_32) + { + updatePosSections[neuronSectionId].positions[neuronId].type = 1; + dynamicSegmentSettings->updateSections = 1; + return; + } + + synapseProcessing(neuronId, + neuronSectionId, + &synapseSections[neuron->targetSectionId], + neuron, + neuronSections, + synapseSections, + updatePosSections, + dynamicSegmentSettings, + neuron->potential, + neuron->potential); +} + +/** + * @brief processNeuron + * @param neuron + * @param segment + */ +inline void +processNeuron(DynamicNeuron* neuron, + DynamicSegmentSettings* dynamicSegmentSettings) +{ + neuron->potential /= dynamicSegmentSettings->neuronCooldown; + neuron->refractionTime = neuron->refractionTime >> 1; + + if(neuron->refractionTime == 0) + { + neuron->potential = dynamicSegmentSettings->potentialOverflow * neuron->input; + neuron->refractionTime = dynamicSegmentSettings->refractionTime; + } + + // update neuron + neuron->potential -= neuron->border; + neuron->active = neuron->potential > 0.0f; + neuron->input = 0.0f; + neuron->potential = log2(neuron->potential + 1.0f); +} + +/** + * @brief reset neurons of a output brick + * + * @param brick pointer to the brick + * @param segment segment where the brick belongs to + */ +inline void +processNeuronsOfOutputBrick(const Brick* brick, + NeuronSection* neuronSections, + float* outputTransfers, + DynamicSegmentSettings* dynamicSegmentSettings) +{ + DynamicNeuron* neuron = nullptr; + NeuronSection* section = nullptr; + + // iterate over all neurons within the brick + for(uint32_t neuronSectionId = brick->neuronSectionPos; + neuronSectionId < brick->numberOfNeuronSections + brick->neuronSectionPos; + neuronSectionId++) + { + section = &neuronSections[neuronSectionId]; + for(uint32_t neuronId = 0; + neuronId < section->numberOfNeurons; + neuronId++) + { + neuron = §ion->neurons[neuronId]; + neuron->potential = dynamicSegmentSettings->potentialOverflow * neuron->input; + outputTransfers[neuron->targetBorderId] = neuron->potential; + neuron->input = 0.0f; + } + } +} + +/** + * @brief reset neurons of a input brick + * + * @param brick pointer to the brick + * @param segment segment where the brick belongs to + */ +inline void +processNeuronsOfInputBrick(const Brick* brick, + NeuronSection* neuronSections, + float* inputTransfers, + SynapseSection* synapseSections, + UpdatePosSection* updatePosSections, + DynamicSegmentSettings* dynamicSegmentSettings) +{ + DynamicNeuron* neuron = nullptr; + NeuronSection* section = nullptr; + + // iterate over all neurons within the brick + for(uint32_t neuronSectionId = brick->neuronSectionPos; + neuronSectionId < brick->numberOfNeuronSections + brick->neuronSectionPos; + neuronSectionId++) + { + section = &neuronSections[neuronSectionId]; + for(uint32_t neuronId = 0; + neuronId < section->numberOfNeurons; + neuronId++) + { + neuron = §ion->neurons[neuronId]; + neuron->potential = inputTransfers[neuron->targetBorderId]; + neuron->active = neuron->potential > 0.0f; + + processSingleNeuron(neuronId, + neuronSectionId, + neuron, + neuronSections, + synapseSections, + updatePosSections, + dynamicSegmentSettings); + } + } +} + +/** + * @brief reset neurons of a normal brick + * + * @param brick pointer to the brick + * @param segment segment where the brick belongs to + */ +inline void +processNeuronsOfNormalBrick(const Brick* brick, + NeuronSection* neuronSections, + SynapseSection* synapseSections, + UpdatePosSection* updatePosSections, + DynamicSegmentSettings* dynamicSegmentSettings) +{ + DynamicNeuron* neuron = nullptr; + NeuronSection* section = nullptr; + + // iterate over all neurons within the brick + for(uint32_t neuronSectionId = brick->neuronSectionPos; + neuronSectionId < brick->numberOfNeuronSections + brick->neuronSectionPos; + neuronSectionId++) + { + section = &neuronSections[neuronSectionId]; + for(uint32_t neuronId = 0; + neuronId < section->numberOfNeurons; + neuronId++) + { + neuron = §ion->neurons[neuronId]; + processNeuron(neuron, dynamicSegmentSettings); + processSingleNeuron(neuronId, + neuronSectionId, + neuron, + neuronSections, + synapseSections, + updatePosSections, + dynamicSegmentSettings); + } + } +} + +/** + * @brief process all neurons within a specific brick and also all synapse-sections, + * which are connected to an active neuron + * + * @param segment segment to process + */ +inline void +prcessDynamicSegment(DynamicSegment &segment) +{ + Brick* bricks = segment.bricks; + uint32_t* brickOrder = segment.brickOrder; + NeuronSection* neuronSections = segment.neuronSections; + SynapseSection* synapseSections = segment.synapseSections; + UpdatePosSection* updatePosSections = segment.updatePosSections; + SegmentHeader* segmentHeader = segment.segmentHeader; + DynamicSegmentSettings* dynamicSegmentSettings = segment.dynamicSegmentSettings; + float* inputTransfers = segment.inputTransfers; + float* outputTransfers = segment.outputTransfers; + + const uint32_t numberOfBricks = segmentHeader->bricks.count; + for(uint32_t pos = 0; pos < numberOfBricks; pos++) + { + const uint32_t brickId = brickOrder[pos]; + Brick* brick = &bricks[brickId]; + if(brick->isInputBrick) + { + processNeuronsOfInputBrick(brick, + neuronSections, + inputTransfers, + synapseSections, + updatePosSections, + dynamicSegmentSettings); + } + else if(brick->isOutputBrick) + { + processNeuronsOfOutputBrick(brick, + neuronSections, + outputTransfers, + dynamicSegmentSettings); + } + else + { + processNeuronsOfNormalBrick(brick, + neuronSections, + synapseSections, + updatePosSections, + dynamicSegmentSettings); + } + } +} + +#endif // KYOUKOMIND_DYNAMIC_PROCESSING_H diff --git a/src/components/KyoukoMind/src/core/segments/dynamic_segment/reduction.h b/src/components/KyoukoMind/src/core/segments/dynamic_segment/reduction.h new file mode 100644 index 00000000..aca8632c --- /dev/null +++ b/src/components/KyoukoMind/src/core/segments/dynamic_segment/reduction.h @@ -0,0 +1,115 @@ +/** + * @file create_reduce.h + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2022 Tobias Anker + * + * 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. + */ + +#ifndef KYOUKOMIND_CREATE_REDUCE_H +#define KYOUKOMIND_CREATE_REDUCE_H + +#include + +#include +#include + +#include "objects.h" +#include "dynamic_segment.h" + +/** + * @brief reduce synapses of a specific section + * + * @param segment segment where the section belongs to + * @param section section to resuce + * + * @return true, if section is empty and can be deleted, else false + */ +inline bool +reduceSynapses(DynamicSegment &segment, + SynapseSection §ion) +{ + bool foundEnd = false; + + /*if(section.next != UNINIT_STATE_32) + { + // delete if sections is empty + foundEnd = true; + if(reduceSynapses(segment, segment.synapseSections[section.next]) == false) + { + segment.segmentData.deleteItem(section.next); + section.next = UNINIT_STATE_32; + foundEnd = false; + } + } + + Synapse* synapse = nullptr; + + // iterate over all synapses in synapse-section + for(int32_t pos = SYNAPSES_PER_SYNAPSESECTION - 1; + pos >= 0; + pos--) + { + // skip not connected synapses + synapse = §ion.synapses[pos]; + + synapse->activeCounter -= synapse->activeCounter < 100; + if(synapse->activeCounter < 5) { + synapse->targetNeuronId = UNINIT_STATE_16; + } + + if(synapse->targetNeuronId != UNINIT_STATE_16) { + foundEnd = true; + } + }*/ + + return foundEnd; +} + +/** + * @brief reduce all synapses within the segment and delete them, if the reach a deletion-border + * + * @param segment current segemnt to process + */ +inline void +reduceNeurons(DynamicSegment &segment) +{ + SynapseSection* section = nullptr; + DynamicNeuron* sourceNeuron = nullptr; + + /*for(uint32_t neuronId = 0; + neuronId < segment.segmentHeader->neuronSections.count; + neuronId++) + { + //sourceNeuron = &segment.neurons[neuronId]; + if(sourceNeuron->targetSectionId == UNINIT_STATE_32) { + continue; + } + + // set start-values + section = &segment.synapseSections[sourceNeuron->targetSectionId]; + + // delete if sections is empty + if(reduceSynapses(segment, *section) == false) + { + segment.segmentData.deleteItem(sourceNeuron->targetSectionId); + sourceNeuron->targetSectionId = UNINIT_STATE_32; + } + }*/ +} + +#endif // KYOUKOMIND_CREATE_REDUCE_H diff --git a/src/components/KyoukoMind/src/core/segments/dynamic_segment/section_update.h b/src/components/KyoukoMind/src/core/segments/dynamic_segment/section_update.h new file mode 100644 index 00000000..540454d1 --- /dev/null +++ b/src/components/KyoukoMind/src/core/segments/dynamic_segment/section_update.h @@ -0,0 +1,135 @@ +/** + * @file section_update.h + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2022 Tobias Anker + * + * 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. + */ + +#ifndef KYOUKOMIND_SECTION_UPDATE_H +#define KYOUKOMIND_SECTION_UPDATE_H + +#include + +#include +#include + +#include "objects.h" +#include "dynamic_segment.h" + +/** + * @brief getForwardLast + * @param sourceId + * @param sectionConnections + * @return + */ +inline SynapseSection* +getForwardLast(const uint32_t sourceId, + SynapseSection* sectionConnections) +{ + SynapseSection* connection = §ionConnections[sourceId]; + if(connection->nextId == UNINIT_STATE_32) { + return connection; + } + + return getForwardLast(connection->nextId, sectionConnections); +} + +/** + * @brief add new basic synapse-section to segment + * + * @param segment refernce to segment + * + * @return position in buffer, where the section was added + */ +inline void +createNewSection(SynapseSection &result, + DynamicSegment &segment, + const Brick ¤tBrick) +{ + result.active = Kitsunemimi::ItemBuffer::ACTIVE_SECTION; + result.randomPos = rand() % NUMBER_OF_RAND_VALUES; + result.randomPos = (result.randomPos + 1) % NUMBER_OF_RAND_VALUES; + const uint32_t randVal = KyoukoRoot::m_randomValues[result.randomPos] % 1000; + const uint32_t brickId = currentBrick.possibleTargetNeuronBrickIds[randVal]; + result.randomPos = (result.randomPos + 1) % NUMBER_OF_RAND_VALUES; + result.targetNeuronSectionId = segment.bricks[brickId].neuronSectionPos; + result.targetNeuronSectionId += KyoukoRoot::m_randomValues[result.randomPos] + % segment.bricks[brickId].numberOfNeuronSections; +} + +/** + * @brief processUpdatePositon_Cpu + * @param segment + * @param sectionId + * @param sourceUpdatePos + */ +inline void +processUpdatePositon_Cpu(DynamicSegment &segment, + const uint32_t sectionId, + const uint32_t neuronId) +{ + NeuronSection* sourceSection = &segment.neuronSections[sectionId]; + Brick* currentBrick = &segment.bricks[sourceSection->brickId]; + + SynapseSection newSection; + createNewSection(newSection, segment, *currentBrick); + const uint64_t newId = segment.segmentData.addNewItem(newSection); + if(newId == ITEM_BUFFER_UNDEFINE_POS) { + return; + } + + DynamicNeuron* neuron = &sourceSection->neurons[neuronId]; + if(neuron->targetSectionId == UNINIT_STATE_32) { + neuron->targetSectionId = newId; + } else { + getForwardLast(neuron->targetSectionId, segment.synapseSections)->nextId = newId; + } +} + +/** + * @brief updateSections + * @param segment + */ +inline void +updateSections(DynamicSegment &segment) +{ + UpdatePosSection* sourceUpdatePosSection = nullptr; + UpdatePos* sourceUpdatePos = nullptr; + + // iterate over all neurons and add new synapse-section, if required + for(uint32_t i = 0; + i < segment.segmentHeader->updatePosSections.count; + i++) + { + sourceUpdatePosSection = &segment.updatePosSections[i]; + for(uint32_t pos = 0; + pos < sourceUpdatePosSection->numberOfPositions; + pos++) + { + sourceUpdatePos = &sourceUpdatePosSection->positions[pos]; + if(sourceUpdatePos->type == 1) + { + sourceUpdatePos->type = 0; + sourceUpdatePos->forwardNewId = UNINIT_STATE_32; + processUpdatePositon_Cpu(segment, i, pos); + } + } + } +} + +#endif // KYOUKOMIND_SECTION_UPDATE_H diff --git a/src/components/KyoukoMind/src/core/segments/input_segment/input_segment.cpp b/src/components/KyoukoMind/src/core/segments/input_segment/input_segment.cpp new file mode 100644 index 00000000..5ea2bde9 --- /dev/null +++ b/src/components/KyoukoMind/src/core/segments/input_segment/input_segment.cpp @@ -0,0 +1,239 @@ +/** + * @file input_segment.cpp + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2022 Tobias Anker + * + * 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 "input_segment.h" + +/** + * @brief constructor + */ +InputSegment::InputSegment() + : AbstractSegment() +{ + m_type = INPUT_SEGMENT; +} + +/** + * @brief constructor to create segment from a snapshot + * + * @param data pointer to data with snapshot + * @param dataSize size of snapshot in number of bytes + */ +InputSegment::InputSegment(const void* data, const uint64_t dataSize) + : AbstractSegment(data, dataSize) +{ + m_type = INPUT_SEGMENT; +} + +/** + * @brief destructor + */ +InputSegment::~InputSegment() {} + +/** + * @brief initalize segment + * + * @param parsedContent json-object with the segment-description + * + * @return true, if successful, else false + */ +bool +InputSegment::initSegment(const std::string &name, + const Kitsunemimi::Hanami::SegmentMeta &segmentMeta) +{ + const uint32_t numberOfInputs = segmentMeta.bricks.at(0).numberOfNeurons; + const uint32_t totalBorderSize = numberOfInputs; + + SegmentHeader header = createNewHeader(numberOfInputs, totalBorderSize); + + allocateSegment(header); + initSegmentPointer(header); + connectBorderBuffer(); + + initSlots(numberOfInputs); + + // TODO: check result + setName(name); + + return true; +} + +/** + * @brief InputSegment::reinitPointer + * @return + */ +bool +InputSegment::reinitPointer(const uint64_t numberOfBytes) +{ + uint8_t* dataPtr = static_cast(segmentData.staticData); + + uint64_t pos = 0; + uint64_t byteCounter = 0; + segmentHeader = reinterpret_cast(dataPtr + pos); + byteCounter += sizeof(SegmentHeader); + + pos = segmentHeader->name.bytePos; + segmentName = reinterpret_cast(dataPtr + pos); + byteCounter += sizeof(SegmentName); + + pos = segmentHeader->settings.bytePos; + dynamicSegmentSettings = reinterpret_cast(dataPtr + pos); + byteCounter += sizeof(DynamicSegmentSettings); + + pos = segmentHeader->slotList.bytePos; + segmentSlots = reinterpret_cast(dataPtr + pos); + byteCounter += segmentHeader->slotList.count * sizeof(SegmentSlotList); + + pos = segmentHeader->inputTransfers.bytePos; + inputTransfers = reinterpret_cast(dataPtr + pos); + byteCounter += segmentHeader->inputTransfers.count * sizeof(float); + + pos = segmentHeader->outputTransfers.bytePos; + outputTransfers = reinterpret_cast(dataPtr + pos); + byteCounter += segmentHeader->outputTransfers.count * sizeof(float); + + pos = segmentHeader->inputs.bytePos; + inputs = reinterpret_cast(dataPtr + pos); + byteCounter += segmentHeader->inputs.count * sizeof(InputNeuron); + + // check result + if(byteCounter != numberOfBytes) { + return false; + } + + return true; +} + +/** + * @brief init border-buffer + * + * @return true, if successful, else false + */ +bool +InputSegment::connectBorderBuffer() +{ + for(uint32_t i = 0; i < segmentHeader->inputs.count; i++) + { + inputs[i] = InputNeuron(); + inputs[i].targetBorderId = i; + } + + return true; +} + +/** + * @brief create new segment-header with size and position information + * + * @param numberOfInputs number of inputs + * @param borderbufferSize size of border-buffer + * + * @return new segment-header + */ +SegmentHeader +InputSegment::createNewHeader(const uint32_t numberOfInputs, + const uint64_t borderbufferSize) +{ + SegmentHeader segmentHeader; + segmentHeader.segmentType = m_type; + uint32_t segmentDataPos = createGenericNewHeader(segmentHeader, borderbufferSize); + + // init bricks + segmentHeader.inputs.count = numberOfInputs; + segmentHeader.inputs.bytePos = segmentDataPos; + segmentDataPos += numberOfInputs * sizeof(InputNeuron); + + segmentHeader.staticDataSize = segmentDataPos; + + return segmentHeader; +} + +/** + * @brief init pointer within the segment-header + * + * @param header segment-header + */ +void +InputSegment::initSegmentPointer(const SegmentHeader &header) +{ + uint8_t* dataPtr = static_cast(segmentData.staticData); + uint64_t pos = 0; + + segmentHeader = reinterpret_cast(dataPtr + pos); + segmentHeader[0] = header; + + pos = segmentHeader->name.bytePos; + segmentName = reinterpret_cast(dataPtr + pos); + + pos = segmentHeader->settings.bytePos; + dynamicSegmentSettings = reinterpret_cast(dataPtr + pos); + + pos = segmentHeader->slotList.bytePos; + segmentSlots = reinterpret_cast(dataPtr + pos); + + pos = segmentHeader->inputTransfers.bytePos; + inputTransfers = reinterpret_cast(dataPtr + pos); + + pos = segmentHeader->outputTransfers.bytePos; + outputTransfers = reinterpret_cast(dataPtr + pos); + + pos = segmentHeader->inputs.bytePos; + inputs = reinterpret_cast(dataPtr + pos); +} + +/** + * @brief allocate memory for the segment + * + * @param header header with the size-information + */ +void +InputSegment::allocateSegment(SegmentHeader &header) +{ + const uint32_t numberOfBlocks = (header.staticDataSize / 4096) + 1; + header.staticDataSize = numberOfBlocks * 4096; + segmentData.initBuffer(header.staticDataSize); +} + +/** + * @brief initialize the slots + * + * @param numberOfInputs number of inputs + * + * @return true, if successful, else false + */ +bool +InputSegment::initSlots(const uint32_t numberOfInputs) +{ + for(uint32_t i = 0; i < 16; i++) + { + const uint32_t size = numberOfInputs; + + // init new segment-neighbor + SegmentSlot* currentSlot = &segmentSlots->slots[i]; + currentSlot->setName("output"); + currentSlot->inUse = false; + currentSlot->numberOfNeurons = size; + currentSlot->inputTransferBufferPos = 0; + currentSlot->outputTransferBufferPos = 0; + currentSlot->direction = OUTPUT_DIRECTION; + } + + return true; +} diff --git a/src/components/KyoukoMind/src/core/segments/input_segment/input_segment.h b/src/components/KyoukoMind/src/core/segments/input_segment/input_segment.h new file mode 100644 index 00000000..393b5dcb --- /dev/null +++ b/src/components/KyoukoMind/src/core/segments/input_segment/input_segment.h @@ -0,0 +1,55 @@ +/** + * @file input_segment.h + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2022 Tobias Anker + * + * 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. + */ + +#ifndef KYOUKOMIND_INPUT_SEGMENTS_H +#define KYOUKOMIND_INPUT_SEGMENTS_H + +#include + +#include + +#include "objects.h" + +class InputSegment + : public AbstractSegment +{ +public: + InputSegment(); + InputSegment(const void* data, const uint64_t dataSize); + ~InputSegment(); + + InputNeuron* inputs = nullptr; + + bool initSegment(const std::string &name, + const Kitsunemimi::Hanami::SegmentMeta &segmentMeta); + bool reinitPointer(const uint64_t numberOfBytes); + +private: + SegmentHeader createNewHeader(const uint32_t numberOfInputs, + const uint64_t borderbufferSize); + void initSegmentPointer(const SegmentHeader &header); + bool connectBorderBuffer(); + void allocateSegment(SegmentHeader &header); + bool initSlots(const uint32_t numberOfInputs); +}; + +#endif // KYOUKOMIND_INPUT_SEGMENTS_H diff --git a/src/components/KyoukoMind/src/core/segments/input_segment/objects.h b/src/components/KyoukoMind/src/core/segments/input_segment/objects.h new file mode 100644 index 00000000..96cff4c8 --- /dev/null +++ b/src/components/KyoukoMind/src/core/segments/input_segment/objects.h @@ -0,0 +1,40 @@ +/** + * @file objects.h + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2022 Tobias Anker + * + * 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. + */ + +#ifndef KYOUKOMIND_INPUT_SEGMENT_OBJECTS_H +#define KYOUKOMIND_INPUT_SEGMENT_OBJECTS_H + +#include + +struct InputNeuron +{ + float weight = 0.0f; + uint32_t targetBorderId = 0; + + uint8_t padding[8]; + + // total size: 16 Byte +}; + +//================================================================================================== + +#endif // KYOUKOMIND_INPUT_SEGMENT_OBJECTS_H diff --git a/src/components/KyoukoMind/src/core/segments/input_segment/processing.h b/src/components/KyoukoMind/src/core/segments/input_segment/processing.h new file mode 100644 index 00000000..4b4c0ff9 --- /dev/null +++ b/src/components/KyoukoMind/src/core/segments/input_segment/processing.h @@ -0,0 +1,54 @@ +/** + * @file processing.h + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2022 Tobias Anker + * + * 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. + */ + +#ifndef KYOUKOMIND_INPUT_PROCESSING_H +#define KYOUKOMIND_INPUT_PROCESSING_H + +#include + +#include +#include + +#include "objects.h" +#include "input_segment.h" + +/** + * @brief process all neurons within a specific brick and also all synapse-sections, + * which are connected to an active neuron + * + * @param segment input-segment to process + */ +void +prcessInputSegment(const InputSegment &segment) +{ + InputNeuron* neuron = nullptr; + const uint64_t numberOfInputs = segment.segmentHeader->inputs.count; + float* outputTransfers = segment.outputTransfers; + + for(uint64_t pos = 0; pos < numberOfInputs; pos++) + { + neuron = &segment.inputs[pos]; + outputTransfers[neuron->targetBorderId] = neuron->weight; + } +} + +#endif // KYOUKOMIND_INPUT_PROCESSING_H diff --git a/src/components/KyoukoMind/src/core/segments/output_segment/backpropagation.h b/src/components/KyoukoMind/src/core/segments/output_segment/backpropagation.h new file mode 100644 index 00000000..4a89509a --- /dev/null +++ b/src/components/KyoukoMind/src/core/segments/output_segment/backpropagation.h @@ -0,0 +1,58 @@ +/** + * @file backpropagation.h + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2022 Tobias Anker + * + * 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. + */ + +#ifndef KYOUKOMIND_OUTPUT_BACKPROPAGATION_H +#define KYOUKOMIND_OUTPUT_BACKPROPAGATION_H + +#include + +#include +#include +#include + +#include "objects.h" +#include "output_segment.h" + +/** + * @brief backpropagate output + * + * @param segment pointer to currect output-segment to process + */ +inline void +backpropagateOutput(const OutputSegment &segment) +{ + OutputNeuron out; + float delta = 0.0f; + + // iterate over all output-neurons + for(uint64_t outputNeuronId = 0; + outputNeuronId < segment.segmentHeader->outputs.count; + outputNeuronId++) + { + out = segment.outputs[outputNeuronId]; + delta = (out.outputWeight - out.shouldValue); + delta *= out.outputWeight * (1.0f - out.outputWeight); + segment.outputTransfers[out.targetBorderId] = delta; + } +} + +#endif // KYOUKOMIND_OUTPUT_BACKPROPAGATION_H diff --git a/src/components/KyoukoMind/src/core/segments/output_segment/objects.h b/src/components/KyoukoMind/src/core/segments/output_segment/objects.h new file mode 100644 index 00000000..0386628b --- /dev/null +++ b/src/components/KyoukoMind/src/core/segments/output_segment/objects.h @@ -0,0 +1,40 @@ +/** + * @file objects.h + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2022 Tobias Anker + * + * 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. + */ + +#ifndef KYOUKOMIND_OUTPUT_SEGMENT_OBJECTS_H +#define KYOUKOMIND_OUTPUT_SEGMENT_OBJECTS_H + +#include + +struct OutputNeuron +{ + float outputWeight = 0.0f; + float shouldValue = 0.0f; + uint32_t targetBorderId = 0; + float maxWeight = 0.00001f; + + // total size: 16 Byte +}; + +//================================================================================================== + +#endif // KYOUKOMIND_OUTPUT_SEGMENT_OBJECTS_H diff --git a/src/components/KyoukoMind/src/core/segments/output_segment/output_segment.cpp b/src/components/KyoukoMind/src/core/segments/output_segment/output_segment.cpp new file mode 100644 index 00000000..9dcfa1ee --- /dev/null +++ b/src/components/KyoukoMind/src/core/segments/output_segment/output_segment.cpp @@ -0,0 +1,240 @@ +/** + * @file output_segment.cpp + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2022 Tobias Anker + * + * 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 "output_segment.h" + +/** + * @brief constructor + */ +OutputSegment::OutputSegment() + : AbstractSegment() +{ + m_type = OUTPUT_SEGMENT; +} + +/** + * @brief constructor to create segment from a snapshot + * + * @param data pointer to data with snapshot + * @param dataSize size of snapshot in number of bytes + */ +OutputSegment::OutputSegment(const void* data, const uint64_t dataSize) + : AbstractSegment(data, dataSize) +{ + m_type = OUTPUT_SEGMENT; +} + +/** + * @brief destructor + */ +OutputSegment::~OutputSegment() {} + +/** + * @brief initalize segment + * + * @param parsedContent json-object with the segment-description + * + * @return true, if successful, else false + */ +bool +OutputSegment::initSegment(const std::string &name, + const Kitsunemimi::Hanami::SegmentMeta &segmentMeta) +{ + const uint32_t numberOfOutputs = segmentMeta.bricks.at(0).numberOfNeurons; + const uint32_t totalBorderSize = numberOfOutputs; + + SegmentHeader header = createNewHeader(numberOfOutputs, totalBorderSize); + + allocateSegment(header); + initSegmentPointer(header); + connectBorderBuffer(); + + initSlots(numberOfOutputs); + + // TODO: check result + setName(name); + + return true; +} + +/** + * @brief OutputSegment::reinitPointer + * @return + */ +bool +OutputSegment::reinitPointer(const uint64_t numberOfBytes) +{ + uint8_t* dataPtr = static_cast(segmentData.staticData); + + uint64_t pos = 0; + uint64_t byteCounter = 0; + segmentHeader = reinterpret_cast(dataPtr + pos); + byteCounter += sizeof(SegmentHeader); + + pos = segmentHeader->name.bytePos; + segmentName = reinterpret_cast(dataPtr + pos); + byteCounter += sizeof(SegmentName); + + pos = segmentHeader->settings.bytePos; + dynamicSegmentSettings = reinterpret_cast(dataPtr + pos); + byteCounter += sizeof(DynamicSegmentSettings); + + pos = segmentHeader->slotList.bytePos; + segmentSlots = reinterpret_cast(dataPtr + pos); + byteCounter += segmentHeader->slotList.count * sizeof(SegmentSlotList); + + pos = segmentHeader->inputTransfers.bytePos; + inputTransfers = reinterpret_cast(dataPtr + pos); + byteCounter += segmentHeader->inputTransfers.count * sizeof(float); + + pos = segmentHeader->outputTransfers.bytePos; + outputTransfers = reinterpret_cast(dataPtr + pos); + byteCounter += segmentHeader->outputTransfers.count * sizeof(float); + + pos = segmentHeader->outputs.bytePos; + outputs = reinterpret_cast(dataPtr + pos); + byteCounter += segmentHeader->outputs.count * sizeof(OutputNeuron); + + // check result + if(byteCounter != numberOfBytes) { + return false; + } + + return true; +} + +/** + * @brief init border-buffer + * + * @return true, if successful, else false + */ +bool +OutputSegment::connectBorderBuffer() +{ + for(uint32_t i = 0; i < segmentHeader->outputs.count; i++) + { + outputs[i] = OutputNeuron(); + outputs[i].targetBorderId = i; + } + + return true; +} + +/** + * @brief create new segment-header with size and position information + * + * @param numberOfOutputs number of outputs + * @param borderbufferSize size of border-buffer + * + * @return new segment-header + */ +SegmentHeader +OutputSegment::createNewHeader(const uint32_t numberOfOutputs, + const uint64_t borderbufferSize) +{ + SegmentHeader segmentHeader; + segmentHeader.segmentType = m_type; + uint32_t segmentDataPos = createGenericNewHeader(segmentHeader, borderbufferSize); + + // init outputs + segmentHeader.outputs.count = numberOfOutputs; + segmentHeader.outputs.bytePos = segmentDataPos; + segmentDataPos += numberOfOutputs * sizeof(OutputNeuron); + + // set total size of the segment + segmentHeader.staticDataSize = segmentDataPos; + + return segmentHeader; +} + +/** + * @brief init pointer within the segment-header + * + * @param header segment-header + */ +void +OutputSegment::initSegmentPointer(const SegmentHeader &header) +{ + uint8_t* dataPtr = static_cast(segmentData.staticData); + uint64_t pos = 0; + + segmentHeader = reinterpret_cast(dataPtr + pos); + segmentHeader[0] = header; + + pos = segmentHeader->name.bytePos; + segmentName = reinterpret_cast(dataPtr + pos); + + pos = segmentHeader->settings.bytePos; + dynamicSegmentSettings = reinterpret_cast(dataPtr + pos); + + pos = segmentHeader->slotList.bytePos; + segmentSlots = reinterpret_cast(dataPtr + pos); + + pos = segmentHeader->inputTransfers.bytePos; + inputTransfers = reinterpret_cast(dataPtr + pos); + + pos = segmentHeader->outputTransfers.bytePos; + outputTransfers = reinterpret_cast(dataPtr + pos); + + pos = segmentHeader->outputs.bytePos; + outputs = reinterpret_cast(dataPtr + pos); +} + +/** + * @brief allocate memory for the segment + * + * @param header header with the size-information + */ +void +OutputSegment::allocateSegment(SegmentHeader &header) +{ + const uint32_t numberOfBlocks = (header.staticDataSize / 4096) + 1; + header.staticDataSize = numberOfBlocks * 4096; + segmentData.initBuffer(header.staticDataSize); +} + +/** + * @brief initialize the slots + * + * @param numberOfInputs number of inputs + * + * @return true, if successful, else false + */ +bool +OutputSegment::initSlots(const uint32_t numberOfInputs) +{ + for(uint32_t i = 0; i < 1; i++) + { + const uint32_t size = numberOfInputs; + + // init new segment-neighbor + SegmentSlot* currentSlot = &segmentSlots->slots[i]; + currentSlot->setName("input"); + currentSlot->inUse = false; + currentSlot->numberOfNeurons = size; + currentSlot->inputTransferBufferPos = 0; + currentSlot->outputTransferBufferPos = 0; + currentSlot->direction = INPUT_DIRECTION; + } + + return true; +} diff --git a/src/components/KyoukoMind/src/core/segments/output_segment/output_segment.h b/src/components/KyoukoMind/src/core/segments/output_segment/output_segment.h new file mode 100644 index 00000000..748b8e33 --- /dev/null +++ b/src/components/KyoukoMind/src/core/segments/output_segment/output_segment.h @@ -0,0 +1,58 @@ +/** + * @file output_segment.h + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2022 Tobias Anker + * + * 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. + */ + +#ifndef KYOUKOMIND_OUTPUT_SEGMENTS_H +#define KYOUKOMIND_OUTPUT_SEGMENTS_H + +#include + +#include + +#include "objects.h" + +class OutputSegment + : public AbstractSegment +{ +public: + OutputSegment(); + OutputSegment(const void* data, const uint64_t dataSize); + ~OutputSegment(); + + float lastTotalError = 0.0f; + float actualTotalError = 0.0f; + + OutputNeuron* outputs = nullptr; + + bool initSegment(const std::string &name, + const Kitsunemimi::Hanami::SegmentMeta &segmentMeta); + bool reinitPointer(const uint64_t numberOfBytes); + +private: + SegmentHeader createNewHeader(const uint32_t numberOfOutputs, + const uint64_t borderbufferSize); + void initSegmentPointer(const SegmentHeader &header); + bool connectBorderBuffer(); + void allocateSegment(SegmentHeader &header); + bool initSlots(const uint32_t numberOfInputs); +}; + +#endif // KYOUKOMIND_OUTPUT_SEGMENTS_H diff --git a/src/components/KyoukoMind/src/core/segments/output_segment/processing.h b/src/components/KyoukoMind/src/core/segments/output_segment/processing.h new file mode 100644 index 00000000..dc407524 --- /dev/null +++ b/src/components/KyoukoMind/src/core/segments/output_segment/processing.h @@ -0,0 +1,93 @@ +/** + * @file processing.h + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2022 Tobias Anker + * + * 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. + */ + +#ifndef KYOUKOMIND_OUTPUT_PROCESSING_H +#define KYOUKOMIND_OUTPUT_PROCESSING_H + +#include + +#include +#include +#include +#include +#include "objects.h" +#include "output_segment.h" + +/** + * @brief get position of the highest output-position + * + * @param segment output-segment to check + * + * @return position of the highest output. + */ +inline uint32_t +getHighestOutput(const OutputSegment &segment) +{ + float hightest = -0.1f; + uint32_t hightestPos = 0; + OutputNeuron* out = nullptr; + + for(uint32_t outputNeuronId = 0; + outputNeuronId < segment.segmentHeader->outputs.count; + outputNeuronId++) + { + out = &segment.outputs[outputNeuronId]; + if(out->outputWeight > hightest) + { + hightest = out->outputWeight; + hightestPos = outputNeuronId; + } + } + + return hightestPos; +} + +/** + * @brief process all neurons within a specific brick and also all synapse-sections, + * which are connected to an active neuron + * + * @param segment segment to process + */ +inline void +prcessOutputSegment(const OutputSegment &segment) +{ + OutputNeuron* neuron = nullptr; + + for(uint64_t outputNeuronId = 0; + outputNeuronId < segment.segmentHeader->outputs.count; + outputNeuronId++) + { + neuron = &segment.outputs[outputNeuronId]; + neuron->outputWeight = segment.inputTransfers[neuron->targetBorderId]; + neuron->outputWeight = 1.0f / (1.0f + exp(-1.0f * neuron->outputWeight)); + } + + // send output back if a client-connection is set + if(segment.parentCluster->msgClient != nullptr + && segment.parentCluster->mode == Cluster::NORMAL_MODE) + { + sendClusterOutputMessage(segment); + } +} + + +#endif // KYOUKOMIND_OUTPUT_PROCESSING_H diff --git a/src/components/KyoukoMind/src/core/segments/segment_meta.h b/src/components/KyoukoMind/src/core/segments/segment_meta.h new file mode 100644 index 00000000..972b5efe --- /dev/null +++ b/src/components/KyoukoMind/src/core/segments/segment_meta.h @@ -0,0 +1,192 @@ +/** + * @file segment_meta.h + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2022 Tobias Anker + * + * 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. + */ + +#ifndef KYOUKOMIND_SEGMENT_META_H +#define KYOUKOMIND_SEGMENT_META_H + +#include + +#include + +enum SegmentTypes +{ + UNDEFINED_SEGMENT = 0, + INPUT_SEGMENT = 1, + OUTPUT_SEGMENT = 2, + DYNAMIC_SEGMENT = 3, +}; + +struct SegmentHeaderEntry +{ + uint64_t bytePos = 0; + uint64_t count = 0; + + // total size: 16 Byte +}; + +struct SegmentHeader +{ + uint8_t objectType = SEGMENT_OBJECT; + uint8_t version = 1; + uint8_t segmentType = UNDEFINED_SEGMENT; + uint8_t padding; + uint32_t segmentID = UNINIT_STATE_32; + uint64_t staticDataSize = 0; + Kitsunemimi::Hanami::Position position; + + Kitsunemimi::Hanami::kuuid parentClusterId; + + // synapse-segment + SegmentHeaderEntry name; + SegmentHeaderEntry settings; + SegmentHeaderEntry slotList; + SegmentHeaderEntry inputTransfers; + SegmentHeaderEntry outputTransfers; + + SegmentHeaderEntry bricks; + SegmentHeaderEntry brickOrder; + SegmentHeaderEntry neuronSections; + SegmentHeaderEntry inputs; + SegmentHeaderEntry outputs; + SegmentHeaderEntry updatePosSections; + + SegmentHeaderEntry synapseSections; + + uint8_t padding2[246]; + + // total size: 512 Byte +}; + +enum SlotDirection +{ + UNDEFINED_DIRECTION = 0, + INPUT_DIRECTION = 1, + OUTPUT_DIRECTION = 2, +}; + +struct SegmentSlot +{ + uint32_t targetSegmentId = UNINIT_STATE_32; + + uint8_t targetSlotId = 0; + uint8_t direction = UNDEFINED_DIRECTION; + + bool inUse = false; + bool inputReady = false; + + uint32_t numberOfNeurons = 0; + uint32_t length = 0; + char name[32]; + + uint64_t inputTransferBufferPos = UNINIT_STATE_64; + uint64_t outputTransferBufferPos = UNINIT_STATE_64; + + /** + * @brief get name of the segment + * + * @return name of the segment + */ + const std::string + getName() const + { + return std::string(name, length); + } + + /** + * @brief set new name for the segment + * + * @param newName new name + * + * @return false, if name is too long or empty, else true + */ + bool + setName(const std::string &newName) + { + if(newName.size() == 0 + || newName.size() >= 32) + { + return false; + } + + memcpy(name, newName.c_str(), newName.size()); + name[newName.size()] = '\0'; + // I know, that the \0 should be enough, but I like string limitations more explicit to + // avoid the risk of buffer overflows + length = newName.size(); + + return true; + } + + // total size: 64 Byte +}; + +struct SegmentSlotList +{ + SegmentSlot slots[16]; + // total size: 1024 Byte +}; + +struct SegmentName +{ + char name[248]; + uint64_t length = 0; + + /** + * @brief get name of the segment + * + * @return name of the segment + */ + const std::string + getName() const + { + return std::string(name, length); + } + + /** + * @brief set new name for the segment + * + * @param newName new name + * + * @return false, if name is too long or empty, else true + */ + bool + setName(const std::string &newName) + { + if(newName.size() == 0 + || newName.size() >= 248) + { + return false; + } + + memcpy(name, newName.c_str(), newName.size()); + name[newName.size()] = '\0'; + // I know, that the \0 should be enough, but I like string limitations more explicit to + // avoid the risk of buffer overflows + length = newName.size(); + + return true; + } + + // total size: 256 Byte +}; + +#endif // SEGMENT_META_H diff --git a/src/components/KyoukoMind/src/core/struct_validation.cpp b/src/components/KyoukoMind/src/core/struct_validation.cpp new file mode 100644 index 00000000..07deb165 --- /dev/null +++ b/src/components/KyoukoMind/src/core/struct_validation.cpp @@ -0,0 +1,53 @@ +/** + * @file validation.cpp + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2022 Tobias Anker + * + * 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 "struct_validation.h" + +#include +#include +#include + +#include + +/** + * @brief validate to sized of all structs to ensure, that they have all the defined size of 2^x + */ +void +validateStructSizes() +{ + assert(sizeof(SynapseSection) == 512); + assert(sizeof(SegmentHeader) == 512); + assert(sizeof(SegmentName) == 256); + assert(sizeof(Brick) == 4096); + assert(sizeof(DynamicNeuron) == 32); + assert(sizeof(NeuronSection) == 2048); + assert(sizeof(SegmentSlot) == 64); + assert(sizeof(SegmentSlotList) == 1024); + + assert(sizeof(Cluster::MetaData) == 2048); + assert(sizeof(Cluster::Settings) == 256); + assert(sizeof(DynamicSegmentSettings) == 256); + assert(sizeof(Kitsunemimi::Hanami::kuuid) == 40); + assert(sizeof(Synapse) == 16); + assert(sizeof(UpdatePosSection) == 1024); + return; +} diff --git a/src/components/KyoukoMind/src/core/struct_validation.h b/src/components/KyoukoMind/src/core/struct_validation.h new file mode 100644 index 00000000..6ddb99c4 --- /dev/null +++ b/src/components/KyoukoMind/src/core/struct_validation.h @@ -0,0 +1,30 @@ +/** + * @file validation.h + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2022 Tobias Anker + * + * 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. + */ + +#ifndef KYOUKOMIND_STRUCT_VALIDATION_H +#define KYOUKOMIND_STRUCT_VALIDATION_H + +#include + +void validateStructSizes(); + +#endif // KYOUKOMIND_STRUCT_VALIDATION_H diff --git a/src/components/KyoukoMind/src/database/cluster_table.cpp b/src/components/KyoukoMind/src/database/cluster_table.cpp new file mode 100644 index 00000000..618a364c --- /dev/null +++ b/src/components/KyoukoMind/src/database/cluster_table.cpp @@ -0,0 +1,187 @@ +/** + * @file cluster_table.cpp + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2022 Tobias Anker + * + * 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 "cluster_table.h" + +#include +#include +#include + +#include + +/** + * @brief constructor + */ +ClusterTable::ClusterTable(Kitsunemimi::Sakura::SqlDatabase* db) + : HanamiSqlTable(db) +{ + m_tableName = "clusters"; + + DbHeaderEntry clusterName; + clusterName.name = "name"; + clusterName.maxLength = 256; + m_tableHeader.push_back(clusterName); +} + +/** + * @brief destructor + */ +ClusterTable::~ClusterTable() {} + +/** + * @brief add a new cluster to the database + * + * @param userData json-item with all information of the cluster to add to database + * @param userContext context-object with all user specific information + * @param error reference for error-output + * + * @return true, if successful, else false + */ +bool +ClusterTable::addCluster(Kitsunemimi::JsonItem &clusterData, + const Kitsunemimi::Hanami::UserContext &userContext, + Kitsunemimi::ErrorContainer &error) +{ + if(add(clusterData, userContext, error) == false) + { + error.addMeesage("Failed to add cluster-meta to database"); + return false; + } + + return true; +} + +/** + * @brief get a cluster from the database by his name + * + * @param result reference for the result-output in case that a cluster with this name was found + * @param clusterUuid uuid of the requested cluster + * @param userContext context-object with all user specific information + * @param error reference for error-output + * @param showHiddenValues set to true to also show as hidden marked fields + * + * @return true, if successful, else false + */ +bool +ClusterTable::getCluster(Kitsunemimi::JsonItem &result, + const std::string &clusterUuid, + const Kitsunemimi::Hanami::UserContext &userContext, + Kitsunemimi::ErrorContainer &error, + const bool showHiddenValues) +{ + std::vector conditions; + conditions.emplace_back("uuid", clusterUuid); + + // get user from db + if(get(result, userContext, conditions, error, showHiddenValues) == false) + { + error.addMeesage("Failed to get cluster-meta with UUID '" + + clusterUuid + + "' from database"); + return false; + } + + return true; +} + +/** + * @brief get a cluster from the database by his name + * + * @param result reference for the result-output in case that a cluster with this name was found + * @param clusterName name of the requested cluster + * @param userContext context-object with all user specific information + * @param error reference for error-output + * @param showHiddenValues set to true to also show as hidden marked fields + * + * @return true, if successful, else false + */ +bool +ClusterTable::getClusterByName(Kitsunemimi::JsonItem &result, + const std::string &clusterName, + const Kitsunemimi::Hanami::UserContext &userContext, + Kitsunemimi::ErrorContainer &error, + const bool showHiddenValues) +{ + std::vector conditions; + conditions.emplace_back("name", clusterName); + + // get user from db + if(get(result, userContext, conditions, error, showHiddenValues) == false) + { + error.addMeesage("Failed to get cluster-meta from database by name '" + clusterName + "'"); + return false; + } + + return true; +} + +/** + * @brief get all clusters from the database table + * + * @param result reference for the result-output + * @param userContext context-object with all user specific information + * @param error reference for error-output + * + * @return true, if successful, else false + */ +bool +ClusterTable::getAllCluster(Kitsunemimi::TableItem &result, + const Kitsunemimi::Hanami::UserContext &userContext, + Kitsunemimi::ErrorContainer &error) +{ + std::vector conditions; + if(getAll(result, userContext, conditions, error) == false) + { + error.addMeesage("Failed to get all cluster-meta from database"); + return false; + } + + return true; +} + +/** + * @brief delete a cluster from the table + * + * @param clusterUuid uuid of the cluster to delete + * @param userContext context-object with all user specific information + * @param error reference for error-output + * + * @return true, if successful, else false + */ +bool +ClusterTable::deleteCluster(const std::string &clusterUuid, + const Kitsunemimi::Hanami::UserContext &userContext, + Kitsunemimi::ErrorContainer &error) +{ + std::vector conditions; + conditions.emplace_back("uuid", clusterUuid); + + if(del(conditions, userContext, error) == false) + { + error.addMeesage("Failed to delete cluster-meta with UUID '" + + clusterUuid + + "' from database"); + return false; + } + + return true; +} diff --git a/src/components/KyoukoMind/src/database/cluster_table.h b/src/components/KyoukoMind/src/database/cluster_table.h new file mode 100644 index 00000000..f4511148 --- /dev/null +++ b/src/components/KyoukoMind/src/database/cluster_table.h @@ -0,0 +1,60 @@ +/** + * @file cluster_table.h + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2022 Tobias Anker + * + * 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. + */ + +#ifndef CLUSTERTABLE_H +#define CLUSTERTABLE_H + +#include +#include + +namespace Kitsunemimi { +class JsonItem; +} +class ClusterTable + : public Kitsunemimi::Hanami::HanamiSqlTable +{ +public: + ClusterTable(Kitsunemimi::Sakura::SqlDatabase* db); + ~ClusterTable(); + + bool addCluster(Kitsunemimi::JsonItem &clusterData, + const Kitsunemimi::Hanami::UserContext &userContext, + Kitsunemimi::ErrorContainer &error); + bool getCluster(Kitsunemimi::JsonItem &result, + const std::string &clusterUuid, + const Kitsunemimi::Hanami::UserContext &userContext, + Kitsunemimi::ErrorContainer &error, + const bool showHiddenValues = false); + bool getClusterByName(Kitsunemimi::JsonItem &result, + const std::string &clusterName, + const Kitsunemimi::Hanami::UserContext &userContext, + Kitsunemimi::ErrorContainer &error, + const bool showHiddenValues = false); + bool getAllCluster(Kitsunemimi::TableItem &result, + const Kitsunemimi::Hanami::UserContext &userContext, + Kitsunemimi::ErrorContainer &error); + bool deleteCluster(const std::string &clusterUuid, + const Kitsunemimi::Hanami::UserContext &userContext, + Kitsunemimi::ErrorContainer &error); +}; + +#endif // CLUSTERTABLE_H diff --git a/src/components/KyoukoMind/src/database/template_table.cpp b/src/components/KyoukoMind/src/database/template_table.cpp new file mode 100644 index 00000000..2a7e9077 --- /dev/null +++ b/src/components/KyoukoMind/src/database/template_table.cpp @@ -0,0 +1,192 @@ +/** + * @file cluster_table.cpp + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2022 Tobias Anker + * + * 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 "template_table.h" + +#include +#include +#include + +#include + +/** + * @brief constructor + */ +TemplateTable::TemplateTable(Kitsunemimi::Sakura::SqlDatabase* db) + : HanamiSqlTable(db) +{ + m_tableName = "templates"; + + DbHeaderEntry templateName; + templateName.name = "name"; + templateName.maxLength = 256; + m_tableHeader.push_back(templateName); + + DbHeaderEntry templateString; + templateString.name = "data"; + templateString.hide = true; + m_tableHeader.push_back(templateString); +} + +/** + * @brief destructor + */ +TemplateTable::~TemplateTable() {} + +/** + * @brief add a new template to the database + * + * @param userData json-item with all information of the cluster to add to database + * @param userContext context-object with all user specific information + * @param error reference for error-output + * + * @return true, if successful, else false + */ +bool +TemplateTable::addTemplate(Kitsunemimi::JsonItem &clusterData, + const Kitsunemimi::Hanami::UserContext &userContext, + Kitsunemimi::ErrorContainer &error) +{ + if(add(clusterData, userContext, error) == false) + { + error.addMeesage("Failed to add template to database"); + return false; + } + + return true; +} + +/** + * @brief get a template from the database by his name + * + * @param result reference for the result-output in case that a cluster with this name was found + * @param templateUuid uuid of the requested template + * @param userContext context-object with all user specific information + * @param error reference for error-output + * @param showHiddenValues set to true to also show as hidden marked fields + * + * @return true, if successful, else false + */ +bool +TemplateTable::getTemplate(Kitsunemimi::JsonItem &result, + const std::string &templateUuid, + const Kitsunemimi::Hanami::UserContext &userContext, + Kitsunemimi::ErrorContainer &error, + const bool showHiddenValues) +{ + std::vector conditions; + conditions.emplace_back("uuid", templateUuid); + + // get user from db + if(get(result, userContext, conditions, error, showHiddenValues) == false) + { + error.addMeesage("Failed to get template with UUID '" + + templateUuid + + "' from database"); + return false; + } + + return true; +} + +/** + * @brief get a template from the database by his name + * + * @param result reference for the result-output in case that a cluster with this name was found + * @param templateName name of the requested template + * @param userContext context-object with all user specific information + * @param error reference for error-output + * @param showHiddenValues set to true to also show as hidden marked fields + * + * @return true, if successful, else false + */ +bool +TemplateTable::getTemplateByName(Kitsunemimi::JsonItem &result, + const std::string &templateName, + const Kitsunemimi::Hanami::UserContext &userContext, + Kitsunemimi::ErrorContainer &error, + const bool showHiddenValues) +{ + std::vector conditions; + conditions.emplace_back("name", templateName); + + // get user from db + if(get(result, userContext, conditions, error, showHiddenValues) == false) + { + error.addMeesage("Failed to get template from database by name '" + templateName + "'"); + return false; + } + + return true; +} + +/** + * @brief get all templates from the database table + * + * @param result reference for the result-output + * @param userContext context-object with all user specific information + * @param error reference for error-output + * + * @return true, if successful, else false + */ +bool +TemplateTable::getAllTemplate(Kitsunemimi::TableItem &result, + const Kitsunemimi::Hanami::UserContext &userContext, + Kitsunemimi::ErrorContainer &error) +{ + std::vector conditions; + if(getAll(result, userContext, conditions, error) == false) + { + error.addMeesage("Failed to get all templates from database"); + return false; + } + + return true; +} + +/** + * @brief delete a cluster from the table + * + * @param templateUuid uuid of the template to delete + * @param userContext context-object with all user specific information + * @param error reference for error-output + * + * @return true, if successful, else false + */ +bool +TemplateTable::deleteTemplate(const std::string &templateUuid, + const Kitsunemimi::Hanami::UserContext &userContext, + Kitsunemimi::ErrorContainer &error) +{ + std::vector conditions; + conditions.emplace_back("uuid", templateUuid); + + if(del(conditions, userContext, error) == false) + { + error.addMeesage("Failed to delete template with UUID '" + + templateUuid + + "' from database"); + return false; + } + + return true; +} diff --git a/src/components/KyoukoMind/src/database/template_table.h b/src/components/KyoukoMind/src/database/template_table.h new file mode 100644 index 00000000..13158f2c --- /dev/null +++ b/src/components/KyoukoMind/src/database/template_table.h @@ -0,0 +1,60 @@ +/** + * @file cluster_table.h + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2022 Tobias Anker + * + * 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. + */ + +#ifndef TEMPLATETABLE_H +#define TEMPLATETABLE_H + +#include +#include + +namespace Kitsunemimi { +class JsonItem; +} +class TemplateTable + : public Kitsunemimi::Hanami::HanamiSqlTable +{ +public: + TemplateTable(Kitsunemimi::Sakura::SqlDatabase* db); + ~TemplateTable(); + + bool addTemplate(Kitsunemimi::JsonItem &clusterData, + const Kitsunemimi::Hanami::UserContext &userContext, + Kitsunemimi::ErrorContainer &error); + bool getTemplate(Kitsunemimi::JsonItem &result, + const std::string &templateUuid, + const Kitsunemimi::Hanami::UserContext &userContext, + Kitsunemimi::ErrorContainer &error, + const bool showHiddenValues = false); + bool getTemplateByName(Kitsunemimi::JsonItem &result, + const std::string &templateName, + const Kitsunemimi::Hanami::UserContext &userContext, + Kitsunemimi::ErrorContainer &error, + const bool showHiddenValues = false); + bool getAllTemplate(Kitsunemimi::TableItem &result, + const Kitsunemimi::Hanami::UserContext &userContext, + Kitsunemimi::ErrorContainer &error); + bool deleteTemplate(const std::string &templateUuid, + const Kitsunemimi::Hanami::UserContext &userContext, + Kitsunemimi::ErrorContainer &error); +}; + +#endif // TEMPLATETABLE_H diff --git a/src/components/KyoukoMind/src/io/protobuf_messages.cpp b/src/components/KyoukoMind/src/io/protobuf_messages.cpp new file mode 100644 index 00000000..63625879 --- /dev/null +++ b/src/components/KyoukoMind/src/io/protobuf_messages.cpp @@ -0,0 +1,246 @@ +/** + * @file protobuf_messages.cpp + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2022 Tobias Anker + * + * 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 "protobuf_messages.h" + +#include +#include <../../libraries/libKitsunemimiHanamiMessages/protobuffers/kyouko_messages.proto3.pb.h> +#include +#include +#include + +/** + * @brief send output of an output-segment as protobuf-message + * + * @param segment segment, which output-data should send + */ +void +sendClusterOutputMessage(const OutputSegment &segment) +{ + if(segment.parentCluster->msgClient == nullptr) { + return; + } + + // build message + ClusterIO_Message msg; + msg.set_segmentname(segment.getName()); + msg.set_islast(false); + msg.set_processtype(ClusterProcessType::REQUEST_TYPE); + msg.set_datatype(ClusterDataType::OUTPUT_TYPE); + msg.set_numberofvalues(segment.segmentHeader->outputs.count); + + for(uint64_t outputNeuronId = 0; + outputNeuronId < segment.segmentHeader->outputs.count; + outputNeuronId++) + { + msg.add_values(segment.outputs[outputNeuronId].outputWeight); + } + + // serialize message + uint8_t buffer[96*1024]; + const uint64_t size = msg.ByteSizeLong(); + if(msg.SerializeToArray(buffer, size) == false) + { + Kitsunemimi::ErrorContainer error; + error.addMeesage("Failed to serialize request-message"); + return; + } + + // send message + Kitsunemimi::Hanami::HanamiMessagingClient* client = segment.parentCluster->msgClient; + Kitsunemimi::ErrorContainer error; + client->sendStreamMessage(buffer, size, false, error); +} + +void +sendProtobufGotInputMessage(Cluster* cluster) +{ + if(cluster->msgClient == nullptr) { + return; + } + + // build message + ClusterIO_Message msg; + msg.set_segmentname("output"); + msg.set_islast(false); + msg.set_processtype(ClusterProcessType::LEARN_TYPE); + msg.set_datatype(ClusterDataType::SHOULD_TYPE); + msg.set_numberofvalues(1); + msg.add_values(0.0); + + // serialize message + uint8_t buffer[96*1024]; + const uint64_t size = msg.ByteSizeLong(); + if(msg.SerializeToArray(buffer, size) == false) + { + Kitsunemimi::ErrorContainer error; + error.addMeesage("Failed to serialize request-message"); + LOG_ERROR(error); + return; + } + + // send message + Kitsunemimi::ErrorContainer error; + cluster->msgClient->sendStreamMessage(buffer, size, false, error); +} + +/** + * @brief sendProtobufNormalEndMessage + * @param cluster + */ +void +sendClusterNormalEndMessage(Cluster* cluster) +{ + if(cluster->msgClient == nullptr) { + return; + } + + // build message + ClusterIO_Message msg; + msg.set_islast(true); + msg.set_processtype(ClusterProcessType::REQUEST_TYPE); + msg.set_datatype(ClusterDataType::OUTPUT_TYPE); + msg.add_values(0.0); + msg.set_numberofvalues(1); + + // serialize message + uint8_t buffer[96*1024]; + const uint64_t size = msg.ByteSizeLong(); + if(msg.SerializeToArray(buffer, size) == false) + { + Kitsunemimi::ErrorContainer error; + error.addMeesage("Failed to serialize request-message"); + return; + } + + // send message + Kitsunemimi::ErrorContainer error; + cluster->msgClient->sendStreamMessage(buffer, size, false, error); +} + +/** + * @brief sendProtobufLearnEndMessage + * @param cluster + */ +void +sendClusterLearnEndMessage(Cluster* cluster) +{ + if(cluster->msgClient == nullptr) { + return; + } + + // build message + ClusterIO_Message msg; + msg.set_islast(true); + msg.set_processtype(ClusterProcessType::LEARN_TYPE); + msg.set_datatype(ClusterDataType::OUTPUT_TYPE); + msg.add_values(0.0); + msg.set_numberofvalues(1); + + // serialize message + uint8_t buffer[96*1024]; + const uint64_t size = msg.ByteSizeLong(); + if(msg.SerializeToArray(buffer, size) == false) + { + Kitsunemimi::ErrorContainer error; + error.addMeesage("Failed to serialize request-message"); + return; + } + + // send message + Kitsunemimi::ErrorContainer error; + cluster->msgClient->sendStreamMessage(buffer, size, false, error); +} + +/** + * @brief process incoming data as protobuf-message + * + * @param cluster cluster which receive the data + * @param data incoming data + * @param dataSize incoming number of bytes + * + * @return false, if message is broken, else true + */ +bool +recvClusterInputMessage(Cluster* cluster, + const void* data, + const uint64_t dataSize) +{ + // parse incoming data + ClusterIO_Message msg; + if(msg.ParseFromArray(data, dataSize) == false) + { + Kitsunemimi::ErrorContainer error; + error.addMeesage("Got invalid Protobuf-ClusterIO-Message"); + LOG_ERROR(error); + return false; + } + + // fill given data into the target-segment + if(msg.datatype() == ClusterDataType::INPUT_TYPE) + { + std::map::iterator it; + it = cluster->inputSegments.find(msg.segmentname()); + if(it != cluster->inputSegments.end()) + { + InputNeuron* inputNeurons = it->second->inputs; + for(uint64_t i = 0; i < msg.numberofvalues(); i++) { + inputNeurons[i].weight = msg.values(i); + } + } + } + if(msg.datatype() == ClusterDataType::SHOULD_TYPE) + { + std::map::iterator it; + it = cluster->outputSegments.find(msg.segmentname()); + if(it != cluster->outputSegments.end()) + { + OutputNeuron* outputNeurons = it->second->outputs; + for(uint64_t i = 0; i < msg.numberofvalues(); i++) { + outputNeurons[i].shouldValue = msg.values(i); + } + } + } + + if(msg.islast()) + { + // start request + if(msg.processtype() == ClusterProcessType::REQUEST_TYPE) + { + cluster->mode = Cluster::NORMAL_MODE; + cluster->startForwardCycle(); + } + + // start learn + if(msg.processtype() == ClusterProcessType::LEARN_TYPE) + { + cluster->mode = Cluster::LEARN_FORWARD_MODE; + cluster->startForwardCycle(); + } + } + else + { + sendProtobufGotInputMessage(cluster); + } + + return true; +} diff --git a/src/components/KyoukoMind/src/io/protobuf_messages.h b/src/components/KyoukoMind/src/io/protobuf_messages.h new file mode 100644 index 00000000..480347a5 --- /dev/null +++ b/src/components/KyoukoMind/src/io/protobuf_messages.h @@ -0,0 +1,37 @@ +/** + * @file protobuf_messages.h + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2022 Tobias Anker + * + * 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. + */ + +#ifndef KYOUKOMIND_PROTOBUF_MESSAGES_H +#define KYOUKOMIND_PROTOBUF_MESSAGES_H + +#include + +void sendClusterOutputMessage(const OutputSegment &segment); +void sendClusterNormalEndMessage(Cluster* cluster); +void sendClusterLearnEndMessage(Cluster* cluster); + +bool recvClusterInputMessage(Cluster* cluster, + const void* data, + const uint64_t dataSize); + + +#endif // KYOUKOMIND_PROTOBUF_MESSAGES_H diff --git a/src/components/KyoukoMind/src/kyouko_root.cpp b/src/components/KyoukoMind/src/kyouko_root.cpp new file mode 100644 index 00000000..22c17fa1 --- /dev/null +++ b/src/components/KyoukoMind/src/kyouko_root.cpp @@ -0,0 +1,173 @@ +/** + * @file kyouko_root.cpp + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2022 Tobias Anker + * + * 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 + +#include +#include + +#include +#include +#include + +#include +#include + +#include +#include +#include + +#include +#include +#include + +#include + +#include +#include + +using Kitsunemimi::Hanami::SupportedComponents; +using Kitsunemimi::Hanami::HanamiMessaging; +using Kitsunemimi::Hanami::HanamiMessagingClient; + +// init static variables +ClusterHandler* KyoukoRoot::m_clusterHandler = nullptr; +uint32_t* KyoukoRoot::m_randomValues = nullptr; +SegmentQueue* KyoukoRoot::m_segmentQueue = nullptr; +ProcessingUnitHandler* KyoukoRoot::m_processingUnitHandler = nullptr; +Kitsunemimi::Sakura::SqlDatabase* KyoukoRoot::database = nullptr; +ClusterTable* KyoukoRoot::clustersTable = nullptr; +TemplateTable* KyoukoRoot::templateTable = nullptr; +std::string* KyoukoRoot::componentToken = nullptr; +Kitsunemimi::GpuInterface* KyoukoRoot::gpuInterface = nullptr; + +/** + * @brief KyoukoRoot::KyoukoRoot + */ +KyoukoRoot::KyoukoRoot() {} + +/** + * @brief KyoukoRoot::~KyoukoRoot + */ +KyoukoRoot::~KyoukoRoot() {} + +/** + * @brief init root-object + * + * @param error reference for error-output + * + * @return true, if successful, else false + */ +bool +KyoukoRoot::init(Kitsunemimi::ErrorContainer &error) +{ + Kitsunemimi::GpuHandler oclHandler; + assert(oclHandler.initDevice(error)); + assert(oclHandler.m_interfaces.size() == 1); + gpuInterface = oclHandler.m_interfaces.at(0); + + validateStructSizes(); + + // init predefinde random-values + m_randomValues = new uint32_t[NUMBER_OF_RAND_VALUES]; + srand(time(NULL)); + for(uint32_t i = 0; i < NUMBER_OF_RAND_VALUES; i++) { + m_randomValues[i] = static_cast(rand()); + } + + // init db + if(initDatabase(error) == false) { + return false; + } + + m_clusterHandler = new ClusterHandler(); + m_segmentQueue = new SegmentQueue(); + + return true; +} + +/** + * @brief create processing-threads + * + * @return true, if successful, else false + */ +bool +KyoukoRoot::initThreads() +{ + m_processingUnitHandler = new ProcessingUnitHandler(); + if(m_processingUnitHandler->initProcessingUnits(1) == false) { + return false; + } + + return true; +} + +/** + * @brief init database + * + * @param error reference for error-output + * + * @return true, if successful, else false + */ +bool +KyoukoRoot::initDatabase(Kitsunemimi::ErrorContainer &error) +{ + bool success = false; + + // read database-path from config + database = new Kitsunemimi::Sakura::SqlDatabase(); + const std::string databasePath = GET_STRING_CONFIG("DEFAULT", "database", success); + if(success == false) + { + error.addMeesage("No database-path defined in config."); + LOG_ERROR(error); + return false; + } + + // initalize database + if(database->initDatabase(databasePath, error) == false) + { + error.addMeesage("Failed to initialize sql-database."); + LOG_ERROR(error); + return false; + } + + // initialize cluster-table + clustersTable = new ClusterTable(database); + if(clustersTable->initTable(error) == false) + { + error.addMeesage("Failed to initialize cluster-table in database."); + LOG_ERROR(error); + return false; + } + + // initialize template-table + templateTable = new TemplateTable(database); + if(templateTable->initTable(error) == false) + { + error.addMeesage("Failed to initialize template-table in database."); + LOG_ERROR(error); + return false; + } + + return true; +} diff --git a/src/components/KyoukoMind/src/kyouko_root.h b/src/components/KyoukoMind/src/kyouko_root.h new file mode 100644 index 00000000..b6e4170f --- /dev/null +++ b/src/components/KyoukoMind/src/kyouko_root.h @@ -0,0 +1,63 @@ +/** + * @file kyouko_root.h + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2022 Tobias Anker + * + * 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. + */ + +#ifndef KYOUKOMIND_KYOUKO_ROOT_H +#define KYOUKOMIND_KYOUKO_ROOT_H + +#include +#include +#include + +class ClusterHandler; +class SegmentQueue; +class ProcessingUnitHandler; + +namespace Kitsunemimi { +class GpuInterface; +} + +class KyoukoRoot +{ + +public: + KyoukoRoot(); + ~KyoukoRoot(); + + bool init(Kitsunemimi::ErrorContainer &error); + bool initThreads(); + + static ClusterHandler* m_clusterHandler; + static uint32_t* m_randomValues; + static SegmentQueue* m_segmentQueue; + static ProcessingUnitHandler* m_processingUnitHandler; + static Kitsunemimi::Sakura::SqlDatabase* database; + static ClusterTable* clustersTable; + static TemplateTable* templateTable; + static std::string* componentToken; + static Kitsunemimi::GpuInterface* gpuInterface; + +private: + uint32_t m_serverId = 0; + bool initDatabase(Kitsunemimi::ErrorContainer &error); +}; + +#endif //KYOUKOMIND_KYOUKO_ROOT_H diff --git a/src/components/KyoukoMind/src/main.cpp b/src/components/KyoukoMind/src/main.cpp new file mode 100644 index 00000000..dd765274 --- /dev/null +++ b/src/components/KyoukoMind/src/main.cpp @@ -0,0 +1,104 @@ +/** + * @file main.cpp + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2022 Tobias Anker + * + * 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 + +#include +#include +#include +#include +#include + +#include +#include + +#include + +#include +#include + +#include +#include + +#include +#include + +using Kitsunemimi::Hanami::HanamiMessaging; +using Kitsunemimi::Hanami::initMain; + +int +main(int argc, char *argv[]) +{ + Kitsunemimi::ErrorContainer error; + KyoukoRoot rootObj; + + if(initMain(argc, argv, "kyouko", ®isterArguments, ®isterConfigs, error) == false) { + return 1; + } + + initBlossoms(); + + // init blossoms + if(rootObj.init(error) == false) + { + LOG_ERROR(error); + return 1; + } + + // init included components + Azuki::initAzukiBlossoms(); + Misaki::initMisakiBlossoms(); + + rootObj.initThreads(); + + // initialize server and connections based on the config-file + const std::vector groupNames = {"misaki", "shiori", "azuki"}; + if(HanamiMessaging::getInstance()->initialize("kyouko", + groupNames, + nullptr, + &streamDataCallback, + &genericCallback, + error, + true) == false) + { + LOG_ERROR(error); + return 1; + } + + Azuki::setSpeedToMinimum(error); + + // init internal token for access to other components + std::string token = ""; + if(Misaki::getInternalToken(token, "kyouko", error) == false) + { + error.addMeesage("Failed to get internal token"); + LOG_ERROR(error); + return 1; + } + KyoukoRoot::componentToken = new std::string(); + *KyoukoRoot::componentToken = token; + + // sleep forever + std::this_thread::sleep_until(std::chrono::time_point::max()); + + return 0; +} diff --git a/src/components/MisakiGuard/CHANGELOG_old b/src/components/MisakiGuard/CHANGELOG_old new file mode 100644 index 00000000..f0930bc8 --- /dev/null +++ b/src/components/MisakiGuard/CHANGELOG_old @@ -0,0 +1,32 @@ +# Changelog + +## [0.2.0] - 2022-07-02 + +### Added +- added salt-value to password-hashing + +### Changed +- use POST instead of GET requests for the auth-endpoint +- rename 'pw' to 'password' for better error-messages + +### Fixed +- a user can not delete himself anymore to avoid that a user break his own account + + +## [0.1.1] - 2022-02-14 + +### Added +- send audit-logs to sagiri + +### Changed +- consider user-scope in database-requests + + +## [0.1.0] - 2022-01-09 + +### Added +- handling for users +- create and validate jwt-token +- check user-permissions against policies +- rest-api-docu generation + diff --git a/src/components/MisakiGuard/MisakiGuard.pro b/src/components/MisakiGuard/MisakiGuard.pro new file mode 100644 index 00000000..fed53555 --- /dev/null +++ b/src/components/MisakiGuard/MisakiGuard.pro @@ -0,0 +1,162 @@ +QT -= qt core gui + +TARGET = MisakiGuard +CONFIG += console c++17 +CONFIG -= app_bundle + +LIBS += -L../../libraries/libAzukiHeart/src -lAzukiHeart +LIBS += -L../../libraries/libAzukiHeart/src/debug -lAzukiHeart +LIBS += -L../../libraries/libAzukiHeart/src/release -lAzukiHeart +INCLUDEPATH += ../../libraries/libAzukiHeart/include + +LIBS += -L../../libraries/libMisakiGuard/src -lMisakiGuard +LIBS += -L../../libraries/libMisakiGuard/src/debug -lMisakiGuard +LIBS += -L../../libraries/libMisakiGuard/src/release -lMisakiGuard +INCLUDEPATH += ../../libraries/libMisakiGuard/include + +LIBS += -L../../libraries/libKitsunemimiHanamiNetwork/src -lKitsunemimiHanamiNetwork +LIBS += -L../../libraries/libKitsunemimiHanamiNetwork/src/debug -lKitsunemimiHanamiNetwork +LIBS += -L../../libraries/libKitsunemimiHanamiNetwork/src/release -lKitsunemimiHanamiNetwork +INCLUDEPATH += ../../libraries/libKitsunemimiHanamiNetwork/include + +LIBS += -L../../libraries/libKitsunemimiHanamiDatabase/src -lKitsunemimiHanamiDatabase +LIBS += -L../../libraries/libKitsunemimiHanamiDatabase/src/debug -lKitsunemimiHanamiDatabase +LIBS += -L../../libraries/libKitsunemimiHanamiDatabase/src/release -lKitsunemimiHanamiDatabase +INCLUDEPATH += ../../libraries/libKitsunemimiHanamiDatabase/include + +LIBS += -L../../libraries/libKitsunemimiHanamiPolicies/src -lKitsunemimiHanamiPolicies +LIBS += -L../../libraries/libKitsunemimiHanamiPolicies/src/debug -lKitsunemimiHanamiPolicies +LIBS += -L../../libraries/libKitsunemimiHanamiPolicies/src/release -lKitsunemimiHanamiPolicies +INCLUDEPATH += ../../libraries/libKitsunemimiHanamiPolicies/include + +LIBS += -L../../libraries/libKitsunemimiHanamiCommon/src -lKitsunemimiHanamiCommon +LIBS += -L../../libraries/libKitsunemimiHanamiCommon/src/debug -lKitsunemimiHanamiCommon +LIBS += -L../../libraries/libKitsunemimiHanamiCommon/src/release -lKitsunemimiHanamiCommon +INCLUDEPATH += ../../libraries/libKitsunemimiHanamiCommon/include + +LIBS += -L../../libraries/libKitsunemimiSakuraDatabase/src -lKitsunemimiSakuraDatabase +LIBS += -L../../libraries/libKitsunemimiSakuraDatabase/src/debug -lKitsunemimiSakuraDatabase +LIBS += -L../../libraries/libKitsunemimiSakuraDatabase/src/release -lKitsunemimiSakuraDatabase +INCLUDEPATH += ../../libraries/libKitsunemimiSakuraDatabase/include + +LIBS += -L../../libraries/libKitsunemimiArgs/src -lKitsunemimiArgs +LIBS += -L../../libraries/libKitsunemimiArgs/src/debug -lKitsunemimiArgs +LIBS += -L../../libraries/libKitsunemimiArgs/src/release -lKitsunemimiArgs +INCLUDEPATH += ../../libraries/libKitsunemimiArgs/include + +LIBS += -L../../libraries/libKitsunemimiConfig/src -lKitsunemimiConfig +LIBS += -L../../libraries/libKitsunemimiConfig/src/debug -lKitsunemimiConfig +LIBS += -L../../libraries/libKitsunemimiConfig/src/release -lKitsunemimiConfig +INCLUDEPATH += ../../libraries/libKitsunemimiConfig/include + +LIBS += -L../../libraries/libKitsunemimiSqlite/src -lKitsunemimiSqlite +LIBS += -L../../libraries/libKitsunemimiSqlite/src/debug -lKitsunemimiSqlite +LIBS += -L../../libraries/libKitsunemimiSqlite/src/release -lKitsunemimiSqlite +INCLUDEPATH += ../../libraries/libKitsunemimiSqlite/include + +LIBS += -L../../libraries/libKitsunemimiSakuraNetwork/src -lKitsunemimiSakuraNetwork +LIBS += -L../../libraries/libKitsunemimiSakuraNetwork/src/debug -lKitsunemimiSakuraNetwork +LIBS += -L../../libraries/libKitsunemimiSakuraNetwork/src/release -lKitsunemimiSakuraNetwork +INCLUDEPATH += ../../libraries/libKitsunemimiSakuraNetwork/include + +LIBS += -L../../libraries/libKitsunemimiCommon/src -lKitsunemimiCommon +LIBS += -L../../libraries/libKitsunemimiCommon/src/debug -lKitsunemimiCommon +LIBS += -L../../libraries/libKitsunemimiCommon/src/release -lKitsunemimiCommon +INCLUDEPATH += ../../libraries/libKitsunemimiCommon/include + +LIBS += -L../../libraries/libKitsunemimiNetwork/src -lKitsunemimiNetwork +LIBS += -L../../libraries/libKitsunemimiNetwork/src/debug -lKitsunemimiNetwork +LIBS += -L../../libraries/libKitsunemimiNetwork/src/release -lKitsunemimiNetwork +INCLUDEPATH += ../../libraries/libKitsunemimiNetwork/include + +LIBS += -L../../libraries/libKitsunemimiJson/src -lKitsunemimiJson +LIBS += -L../../libraries/libKitsunemimiJson/src/debug -lKitsunemimiJson +LIBS += -L../../libraries/libKitsunemimiJson/src/release -lKitsunemimiJson +INCLUDEPATH += ../../libraries/libKitsunemimiJson/include + +LIBS += -L../../libraries/libKitsunemimiIni/src -lKitsunemimiIni +LIBS += -L../../libraries/libKitsunemimiIni/src/debug -lKitsunemimiIni +LIBS += -L../../libraries/libKitsunemimiIni/src/release -lKitsunemimiIni +INCLUDEPATH += ../../libraries/libKitsunemimiIni/include + +LIBS += -L../../libraries/libKitsunemimiJwt/src -lKitsunemimiJwt +LIBS += -L../../libraries/libKitsunemimiJwt/src/debug -lKitsunemimiJwt +LIBS += -L../../libraries/libKitsunemimiJwti/src/release -lKitsunemimiJwt +INCLUDEPATH += ../../libraries/libKitsunemimiJwt/include + +LIBS += -L../../libraries/libKitsunemimiCrypto/src -lKitsunemimiCrypto +LIBS += -L../../libraries/libKitsunemimiCrypto/src/debug -lKitsunemimiCrypto +LIBS += -L../../libraries/libKitsunemimiCrypto/src/release -lKitsunemimiCrypto +INCLUDEPATH += ../../libraries/libKitsunemimiCrypto/include + + +LIBS += -lcryptopp -lssl -lsqlite3 -luuid -lcrypto -pthread -lprotobuf + +INCLUDEPATH += $$PWD \ + src + +SOURCES += src/main.cpp \ + src/api/v1/auth/create_internal_token.cpp \ + src/api/v1/auth/create_token.cpp \ + src/api/v1/auth/list_user_projects.cpp \ + src/api/v1/auth/renew_token.cpp \ + src/api/v1/auth/validate_access.cpp \ + src/api/v1/project/create_project.cpp \ + src/api/v1/project/delete_project.cpp \ + src/api/v1/project/get_project.cpp \ + src/api/v1/project/list_projects.cpp \ + src/api/v1/user/add_project_to_user.cpp \ + src/api/v1/user/create_user.cpp \ + src/api/v1/user/delete_user.cpp \ + src/api/v1/user/get_user.cpp \ + src/api/v1/user/list_users.cpp \ + src/api/v1/documentation/generate_rest_api_docu.cpp \ + src/api/v1/user/remove_project_from_user.cpp \ + src/database/projects_table.cpp \ + src/misaki_root.cpp \ + src/database/users_table.cpp + +HEADERS += \ + src/api/v1/auth/create_internal_token.h \ + src/api/v1/auth/create_token.h \ + src/api/v1/auth/list_user_projects.h \ + src/api/v1/auth/renew_token.h \ + src/api/v1/auth/validate_access.h \ + src/api/blossom_initializing.h \ + src/api/v1/project/create_project.h \ + src/api/v1/project/delete_project.h \ + src/api/v1/project/get_project.h \ + src/api/v1/project/list_projects.h \ + src/api/v1/user/add_project_to_user.h \ + src/api/v1/user/create_user.h \ + src/api/v1/user/delete_user.h \ + src/api/v1/user/get_user.h \ + src/api/v1/user/list_users.h \ + src/api/v1/user/remove_project_from_user.h \ + src/args.h \ + src/callbacks.h \ + src/config.h \ + src/api/v1/documentation/generate_rest_api_docu.h \ + src/database/projects_table.h \ + src/misaki_root.h \ + src/database/users_table.h + +AZUKI_PROTO_BUFFER = ../../libraries/libKitsunemimiHanamiMessages/protobuffers/azuki_messages.proto3 + +OTHER_FILES += $$AZUKI_PROTO_BUFFER + +protobuf_decl.name = protobuf headers +protobuf_decl.name = protobuf headers +protobuf_decl.input = KYOUKO_PROTO_BUFFER +protobuf_decl.output = ${QMAKE_FILE_IN_PATH}/${QMAKE_FILE_BASE}.proto3.pb.h +protobuf_decl.commands = protoc --cpp_out=${QMAKE_FILE_IN_PATH} --proto_path=${QMAKE_FILE_IN_PATH} ${QMAKE_FILE_NAME} +protobuf_decl.variable_out = HEADERS +QMAKE_EXTRA_COMPILERS += protobuf_decl + +protobuf_impl.name = protobuf sources +protobuf_impl.input = KYOUKO_PROTO_BUFFER +protobuf_impl.output = ${QMAKE_FILE_IN_PATH}/${QMAKE_FILE_BASE}.proto3.pb.cc +protobuf_impl.depends = ${QMAKE_FILE_IN_PATH}/${QMAKE_FILE_BASE}.proto3.pb.h +protobuf_impl.commands = $$escape_expand(\n) +protobuf_impl.variable_out = SOURCES +QMAKE_EXTRA_COMPILERS += protobuf_impl diff --git a/src/components/MisakiGuard/README.md b/src/components/MisakiGuard/README.md new file mode 100644 index 00000000..101d891e --- /dev/null +++ b/src/components/MisakiGuard/README.md @@ -0,0 +1,15 @@ +# MisakiGuard + +## Description + +User-managment- and Authentication-component of the Hanami-AI-Project: https://github.com/kitsudaiki/Hanami-AI + +## Author + +Tobias Anker + +eMail: tobias.anker@kitsunemimi.moe + +## License + +This project is licensed under the Apache License Version 2.0 - see the [LICENSE](LICENSE) file for details diff --git a/src/components/MisakiGuard/build.sh b/src/components/MisakiGuard/build.sh new file mode 100755 index 00000000..a991b0c4 --- /dev/null +++ b/src/components/MisakiGuard/build.sh @@ -0,0 +1,144 @@ +#!/bin/bash + +# get current directory-path and the path of the parent-directory +DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )" +PARENT_DIR="$(dirname "$DIR")" +CURRENT_BRANCH=$(git rev-parse --abbrev-ref HEAD) + +# create build-directory +BUILD_DIR="$PARENT_DIR/build" +mkdir -p $BUILD_DIR + +# create directory for the final result +RESULT_DIR="$PARENT_DIR/result" +mkdir -p $RESULT_DIR + +#----------------------------------------------------------------------------------------------------------------- + +function build_kitsune_lib_repo () { + REPO_NAME=$1 + NUMBER_OF_THREADS=$2 + ADDITIONAL_CONFIGS=$3 + + # create build directory for repo and go into this directory + REPO_DIR="$BUILD_DIR/$REPO_NAME" + mkdir -p $REPO_DIR + cd $REPO_DIR + + # build repo library with qmake + /usr/lib/x86_64-linux-gnu/qt5/bin/qmake "$PARENT_DIR/$REPO_NAME/$REPO_NAME.pro" -spec linux-g++ "CONFIG += optimize_full staticlib $ADDITIONAL_CONFIGS" + /usr/bin/make -j$NUMBER_OF_THREADS + + # copy build-result and include-files into the result-directory + echo "----------------------------------------------------------------------" + echo $RESULT_DIR + cp $REPO_DIR/src/$REPO_NAME.a $RESULT_DIR/ + cp -r $PARENT_DIR/$REPO_NAME/include $RESULT_DIR/ + ls -l $RESULT_DIR/include/ + ls -l $RESULT_DIR +} + +function download_repo_github () { + REPO_NAME=$1 + TAG_OR_BRANCH=$2 + + echo "" + echo "" + echo "++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++" + echo "$REPO_NAME" + echo "++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++" + echo "Branch/Tag: $TAG_OR_BRANCH" + + # clone repo + git clone https://github.com/kitsudaiki/$REPO_NAME.git "$PARENT_DIR/$REPO_NAME" + cd "$PARENT_DIR/$REPO_NAME" + + # checkout branch + if [[ $CURRENT_BRANCH =~ ^tag.* ]] || [[ $CURRENT_BRANCH =~ ^hotfix.* ]] || [[ $CURRENT_BRANCH =~ ^v.* ]] || [[ $CURRENT_BRANCH =~ ^rolling$ ]] || [[ $CURRENT_BRANCH =~ ^staging$ ]]; then + # if a stable branch, then use the defined tag of branch + # check if defined branch even exist + BRANCH_EXIST=$(git ls-remote --heads origin $TAG_OR_BRANCH) + if [[ -z "$BRANCH_EXIST" ]]; then + echo "" + echo "-------------------------------------------------------------------------------------" + echo "Branch or tag '$TAG_OR_BRANCH' does not exist for the repository '$REPO_NAME'" + echo "-------------------------------------------------------------------------------------" + echo "" + exit 1 + fi + git checkout $TAG_OR_BRANCH + else + # if develop or feature branch, then try to checkout the feature-branch in the other repo as well + # or otherwise use the develop-branch as default + BRANCH_EXIST=$(git ls-remote --heads origin $CURRENT_BRANCH) + if [[ -n "$BRANCH_EXIST" ]]; then + git checkout $CURRENT_BRANCH + else + git checkout develop + fi + fi +} + +function get_required_kitsune_lib_repo () { + REPO_NAME=$1 + TAG_OR_BRANCH=$2 + NUMBER_OF_THREADS=$3 + + download_repo_github $REPO_NAME $TAG_OR_BRANCH + build_kitsune_lib_repo $REPO_NAME $NUMBER_OF_THREADS +} + + +#----------------------------------------------------------------------------------------------------------------- + +echo "###########################################################################################################" +echo "" +get_required_kitsune_lib_repo "libKitsunemimiCommon" "develop" 8 +get_required_kitsune_lib_repo "libKitsunemimiJson" "develop" 1 +get_required_kitsune_lib_repo "libKitsunemimiIni" "develop" 1 +get_required_kitsune_lib_repo "libKitsunemimiNetwork" "develop" 8 +get_required_kitsune_lib_repo "libKitsunemimiArgs" "develop" 8 +get_required_kitsune_lib_repo "libKitsunemimiConfig" "develop" 8 +echo "" +echo "###########################################################################################################" +echo "" +get_required_kitsune_lib_repo "libKitsunemimiSqlite" "develop" 8 +get_required_kitsune_lib_repo "libKitsunemimiCrypto" "develop" 8 +get_required_kitsune_lib_repo "libKitsunemimiJwt" "develop" 8 +echo "" +echo "###########################################################################################################" +echo "" +get_required_kitsune_lib_repo "libKitsunemimiSakuraNetwork" "develop" 8 +get_required_kitsune_lib_repo "libKitsunemimiSakuraDatabase" "develop" 8 +echo "" +echo "###########################################################################################################" +echo "" +get_required_kitsune_lib_repo "libKitsunemimiHanamiCommon" "develop" 8 +get_required_kitsune_lib_repo "libKitsunemimiHanamiDatabase" "develop" 8 +get_required_kitsune_lib_repo "libKitsunemimiHanamiPolicies" "develop" 1 +download_repo_github "libKitsunemimiHanamiMessages" "develop" +get_required_kitsune_lib_repo "libKitsunemimiHanamiNetwork" "develop" 8 +echo "" +echo "###########################################################################################################" +echo "" +get_required_kitsune_lib_repo "libAzukiHeart" "develop" 8 +get_required_kitsune_lib_repo "libMisakiGuard" "develop" 8 +echo "" +echo "###########################################################################################################" + +#----------------------------------------------------------------------------------------------------------------- + +# create build directory for KyoukoMind and go into this directory +LIB_KITSUNE_SAKURA_TREE_DIR="$BUILD_DIR/MisakiGuard" +mkdir -p $LIB_KITSUNE_SAKURA_TREE_DIR +cd $LIB_KITSUNE_SAKURA_TREE_DIR + +# build MisakiGuard with qmake +/usr/lib/x86_64-linux-gnu/qt5/bin/qmake "$PARENT_DIR/MisakiGuard/MisakiGuard.pro" -spec linux-g++ "CONFIG += optimize_full" +/usr/bin/make -j8 + +# copy build-result and include-files into the result-directory +cp "$LIB_KITSUNE_SAKURA_TREE_DIR/MisakiGuard" "$RESULT_DIR/" + +#----------------------------------------------------------------------------------------------------------------- + diff --git a/src/components/MisakiGuard/src/api/blossom_initializing.h b/src/components/MisakiGuard/src/api/blossom_initializing.h new file mode 100644 index 00000000..74bedd02 --- /dev/null +++ b/src/components/MisakiGuard/src/api/blossom_initializing.h @@ -0,0 +1,214 @@ +/** + * @file blossom_initializing.h + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2022 Tobias Anker + * + * 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. + */ + +#ifndef MISAKIGUARD_BLOSSOM_INITIALIZING_H +#define MISAKIGUARD_BLOSSOM_INITIALIZING_H + +#include + +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include + +using Kitsunemimi::Hanami::HanamiMessaging; + +/** + * @brief init token endpoints + */ +void +tokenBlossomes() +{ + HanamiMessaging* interface = HanamiMessaging::getInstance(); + const std::string group = "token"; + + assert(interface->addBlossom(group, "create", new CreateToken())); + interface->addEndpoint("v1/token", + Kitsunemimi::Hanami::POST_TYPE, + Kitsunemimi::Hanami::BLOSSOM_TYPE, + group, + "create"); + + assert(interface->addBlossom(group, "renew", new RenewToken())); + interface->addEndpoint("v1/token", + Kitsunemimi::Hanami::PUT_TYPE, + Kitsunemimi::Hanami::BLOSSOM_TYPE, + group, + "renew"); + + assert(interface->addBlossom(group, "create_internal", new CreateInternalToken())); + interface->addEndpoint("v1/token/internal", + Kitsunemimi::Hanami::POST_TYPE, + Kitsunemimi::Hanami::BLOSSOM_TYPE, + group, + "create_internal"); + + assert(interface->addBlossom(group, "validate", new ValidateAccess())); + interface->addEndpoint("v1/auth", + Kitsunemimi::Hanami::GET_TYPE, + Kitsunemimi::Hanami::BLOSSOM_TYPE, + group, + "validate"); +} + +/** + * @brief documentation endpoints + */ +void +documentationBlossomes() +{ + HanamiMessaging* interface = HanamiMessaging::getInstance(); + const std::string group = "documentation"; + + assert(interface->addBlossom(group, "generate_rest_api", new GenerateRestApiDocu())); + interface->addEndpoint("v1/documentation/api/rest", + Kitsunemimi::Hanami::GET_TYPE, + Kitsunemimi::Hanami::BLOSSOM_TYPE, + group, + "generate_rest_api"); +} + +/** + * @brief init user endpoints + */ +void +userBlossomes() +{ + HanamiMessaging* interface = HanamiMessaging::getInstance(); + const std::string group = "user"; + + assert(interface->addBlossom(group, "create", new CreateUser())); + interface->addEndpoint("v1/user", + Kitsunemimi::Hanami::POST_TYPE, + Kitsunemimi::Hanami::BLOSSOM_TYPE, + group, + "create"); + + assert(interface->addBlossom(group, "get", new GetUser())); + interface->addEndpoint("v1/user", + Kitsunemimi::Hanami::GET_TYPE, + Kitsunemimi::Hanami::BLOSSOM_TYPE, + group, + "get"); + + assert(interface->addBlossom(group, "list", new ListUsers())); + interface->addEndpoint("v1/user/all", + Kitsunemimi::Hanami::GET_TYPE, + Kitsunemimi::Hanami::BLOSSOM_TYPE, + group, + "list"); + + assert(interface->addBlossom(group, "delete", new DeleteUser())); + interface->addEndpoint("v1/user", + Kitsunemimi::Hanami::DELETE_TYPE, + Kitsunemimi::Hanami::BLOSSOM_TYPE, + group, + "delete"); + + assert(interface->addBlossom(group, "add_project", new AddProjectToUser())); + interface->addEndpoint("v1/user/project", + Kitsunemimi::Hanami::POST_TYPE, + Kitsunemimi::Hanami::BLOSSOM_TYPE, + group, + "add_project"); + + assert(interface->addBlossom(group, "remove_project", new RemoveProjectFromUser())); + interface->addEndpoint("v1/user/project", + Kitsunemimi::Hanami::DELETE_TYPE, + Kitsunemimi::Hanami::BLOSSOM_TYPE, + group, + "remove_project"); + + // TODO: move ListUserProjects-class in user-directory + assert(interface->addBlossom(group, "list_user_projects", new ListUserProjects())); + interface->addEndpoint("v1/user/project", + Kitsunemimi::Hanami::GET_TYPE, + Kitsunemimi::Hanami::BLOSSOM_TYPE, + group, + "list_user_projects"); +} + +/** + * @brief init special endpoints + */ +void +projectBlossomes() +{ + HanamiMessaging* interface = HanamiMessaging::getInstance(); + const std::string group = "project"; + + assert(interface->addBlossom(group, "create", new CreateProject())); + interface->addEndpoint("v1/project", + Kitsunemimi::Hanami::POST_TYPE, + Kitsunemimi::Hanami::BLOSSOM_TYPE, + group, + "create"); + + assert(interface->addBlossom(group, "get", new GetProject())); + interface->addEndpoint("v1/project", + Kitsunemimi::Hanami::GET_TYPE, + Kitsunemimi::Hanami::BLOSSOM_TYPE, + group, + "get"); + + assert(interface->addBlossom(group, "list", new ListProjects())); + interface->addEndpoint("v1/project/all", + Kitsunemimi::Hanami::GET_TYPE, + Kitsunemimi::Hanami::BLOSSOM_TYPE, + group, + "list"); + + assert(interface->addBlossom(group, "delete", new DeleteProject())); + interface->addEndpoint("v1/project", + Kitsunemimi::Hanami::DELETE_TYPE, + Kitsunemimi::Hanami::BLOSSOM_TYPE, + group, + "delete"); +} + +void +initBlossoms() +{ + projectBlossomes(); + userBlossomes(); + documentationBlossomes(); + tokenBlossomes(); +} + +#endif // MISAKIGUARD_BLOSSOM_INITIALIZING_H diff --git a/src/components/MisakiGuard/src/api/v1/auth/create_internal_token.cpp b/src/components/MisakiGuard/src/api/v1/auth/create_internal_token.cpp new file mode 100644 index 00000000..56b41f52 --- /dev/null +++ b/src/components/MisakiGuard/src/api/v1/auth/create_internal_token.cpp @@ -0,0 +1,102 @@ +/** + * @file create_internal_token.cpp + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2022 Tobias Anker + * + * 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 "create_internal_token.h" + +#include + +#include +#include +#include +#include + +#include +#include + +using namespace Kitsunemimi::Hanami; + +/** + * @brief constructor + */ +CreateInternalToken::CreateInternalToken() + : Blossom("Create a JWT-access-token for a internal services, " + "which can not be used from the outside.") +{ + //---------------------------------------------------------------------------------------------- + // input + //---------------------------------------------------------------------------------------------- + + registerInputField("service_name", + SAKURA_STRING_TYPE, + true, + "Name of the service."); + assert(addFieldBorder("service_name", 4, 256)); + assert(addFieldRegex("service_name", ID_REGEX)); + + //---------------------------------------------------------------------------------------------- + // output + //---------------------------------------------------------------------------------------------- + + registerOutputField("token", + SAKURA_STRING_TYPE, + "New JWT-access-token for the service."); + + //---------------------------------------------------------------------------------------------- + // + //---------------------------------------------------------------------------------------------- +} + +/** + * @brief runTask + */ +bool +CreateInternalToken::runTask(BlossomIO &blossomIO, + const Kitsunemimi::DataMap &, + BlossomStatus &status, + Kitsunemimi::ErrorContainer &error) +{ + // get information from request + const std::string serviceName = blossomIO.input.get("service_name").getString(); + + // create struct with the payload for the token + Kitsunemimi::JsonItem serviceData; + serviceData.insert("name", new Kitsunemimi::DataValue(serviceName)); + + // TODO: make validation-time configurable + std::string jwtToken; + if(MisakiRoot::jwt->create_HS256_Token(jwtToken, serviceData, 3600, error) == false) + { + error.addMeesage("Failed to create JWT-Token"); + status.statusCode = Kitsunemimi::Hanami::INTERNAL_SERVER_ERROR_RTYPE; + return false; + } + + // cut of signature to make the token invalid for the usage for outside of the node + std::vector splitedContent; + Kitsunemimi::splitStringByDelimiter(splitedContent, jwtToken, '.'); + jwtToken = splitedContent.at(0) + "." + splitedContent.at(1) + "._"; + + // create output + blossomIO.output.insert("token", jwtToken); + + return true; +} diff --git a/src/components/MisakiGuard/src/api/v1/auth/create_internal_token.h b/src/components/MisakiGuard/src/api/v1/auth/create_internal_token.h new file mode 100644 index 00000000..15cdff57 --- /dev/null +++ b/src/components/MisakiGuard/src/api/v1/auth/create_internal_token.h @@ -0,0 +1,41 @@ +/** + * @file create_internal_token.h + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2022 Tobias Anker + * + * 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. + */ + +#ifndef MISAKIGUARD_CREATEINTERNALTOKEN_H +#define MISAKIGUARD_CREATEINTERNALTOKEN_H + +#include + +class CreateInternalToken + : public Kitsunemimi::Hanami::Blossom +{ +public: + CreateInternalToken(); + +protected: + bool runTask(Kitsunemimi::Hanami::BlossomIO &blossomIO, + const Kitsunemimi::DataMap &, + Kitsunemimi::Hanami::BlossomStatus &status, + Kitsunemimi::ErrorContainer &error); +}; + +#endif // MISAKIGUARD_CREATEINTERNALTOKEN_H diff --git a/src/components/MisakiGuard/src/api/v1/auth/create_token.cpp b/src/components/MisakiGuard/src/api/v1/auth/create_token.cpp new file mode 100644 index 00000000..5130d9cd --- /dev/null +++ b/src/components/MisakiGuard/src/api/v1/auth/create_token.cpp @@ -0,0 +1,169 @@ +/** + * @file create_token.cpp + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2022 Tobias Anker + * + * 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 "create_token.h" + +#include + +#include +#include +#include + +#include +#include + +using namespace Kitsunemimi::Hanami; + +/** + * @brief constructor + */ +CreateToken::CreateToken() + : Blossom("Create a JWT-access-token for a specific user.") +{ + //---------------------------------------------------------------------------------------------- + // input + //---------------------------------------------------------------------------------------------- + + registerInputField("id", + SAKURA_STRING_TYPE, + true, + "ID of the user."); + assert(addFieldBorder("id", 4, 256)); + assert(addFieldRegex("id", ID_EXT_REGEX)); + + registerInputField("password", + SAKURA_STRING_TYPE, + true, + "Passphrase of the user, to verify the access."); + assert(addFieldBorder("password", 8, 4096)); + + //---------------------------------------------------------------------------------------------- + // output + //---------------------------------------------------------------------------------------------- + + registerOutputField("id", + SAKURA_STRING_TYPE, + "ID of the user."); + registerOutputField("name", + SAKURA_STRING_TYPE, + "Name of the user."); + registerOutputField("is_admin", + SAKURA_BOOL_TYPE, + "Set this to true to register the new user as admin."); + registerOutputField("token", + SAKURA_STRING_TYPE, + "New JWT-access-token for the user."); + + //---------------------------------------------------------------------------------------------- + // + //---------------------------------------------------------------------------------------------- +} + +/** + * @brief runTask + */ +bool +CreateToken::runTask(BlossomIO &blossomIO, + const Kitsunemimi::DataMap &, + BlossomStatus &status, + Kitsunemimi::ErrorContainer &error) +{ + const std::string userId = blossomIO.input.get("id").getString(); + + // get data from table + Kitsunemimi::JsonItem userData; + if(MisakiRoot::usersTable->getUser(userData, userId, error, true) == false) + { + status.errorMessage = "ACCESS DENIED!\n" + "User or password is incorrect."; + error.addMeesage(status.errorMessage); + status.statusCode = Kitsunemimi::Hanami::UNAUTHORIZED_RTYPE; + return false; + } + + // regenerate password-hash for comparism + std::string compareHash = ""; + const std::string saltedPw = blossomIO.input.get("password").getString() + + userData.get("salt").getString(); + Kitsunemimi::generate_SHA_256(compareHash, saltedPw); + + // check password + const std::string pwHash = userData.get("pw_hash").getString(); + if(pwHash.size() != compareHash.size() + || memcmp(pwHash.c_str(), compareHash.c_str(), pwHash.size()) != 0) + { + status.errorMessage = "ACCESS DENIED!\n" + "User or password is incorrect."; + error.addMeesage(status.errorMessage); + status.statusCode = Kitsunemimi::Hanami::UNAUTHORIZED_RTYPE; + return false; + } + + // remove entries, which are NOT allowed to be part of the token + std::string jwtToken; + userData.remove("pw_hash"); + userData.remove("salt"); + + Kitsunemimi::JsonItem parsedProjects = userData.get("projects"); + + // get project + const bool isAdmin = userData.get("is_admin").getBool(); + if(isAdmin) + { + // admin user get alway the admin-project per default + userData.insert("project_id", "admin"); + userData.insert("role", "admin"); + userData.insert("is_project_admin", true); + userData.remove("projects"); + } + else if(parsedProjects.size() != 0) + { + // normal user get assigned to first project in their project-list at beginning + userData.insert("project_id", parsedProjects.get(0).get("project_id")); + userData.insert("role", parsedProjects.get(0).get("role")); + userData.insert("is_project_admin", parsedProjects.get(0).get("is_project_admin")); + userData.remove("projects"); + } + else + { + status.errorMessage = "User with id '" + userId + "' has no project assigned."; + error.addMeesage(status.errorMessage); + status.statusCode = Kitsunemimi::Hanami::UNAUTHORIZED_RTYPE; + return false; + } + + // create token + // TODO: make validation-time configurable + if(MisakiRoot::jwt->create_HS256_Token(jwtToken, userData, 3600, error) == false) + { + error.addMeesage("Failed to create JWT-Token"); + status.statusCode = Kitsunemimi::Hanami::INTERNAL_SERVER_ERROR_RTYPE; + return false; + } + + blossomIO.output.insert("id", userId); + blossomIO.output.insert("is_admin", isAdmin); + blossomIO.output.insert("name", userData.get("name").getString()); + blossomIO.output.insert("token", jwtToken); + + return true; +} diff --git a/src/components/MisakiGuard/src/api/v1/auth/create_token.h b/src/components/MisakiGuard/src/api/v1/auth/create_token.h new file mode 100644 index 00000000..aa00dec3 --- /dev/null +++ b/src/components/MisakiGuard/src/api/v1/auth/create_token.h @@ -0,0 +1,41 @@ +/** + * @file create_token.h + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2022 Tobias Anker + * + * 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. + */ + +#ifndef MISAKIGUARD_CREATETOKEN_H +#define MISAKIGUARD_CREATETOKEN_H + +#include + +class CreateToken + : public Kitsunemimi::Hanami::Blossom +{ +public: + CreateToken(); + +protected: + bool runTask(Kitsunemimi::Hanami::BlossomIO &blossomIO, + const Kitsunemimi::DataMap &, + Kitsunemimi::Hanami::BlossomStatus &status, + Kitsunemimi::ErrorContainer &error); +}; + +#endif // MISAKIGUARD_CREATETOKEN_H diff --git a/src/components/MisakiGuard/src/api/v1/auth/list_user_projects.cpp b/src/components/MisakiGuard/src/api/v1/auth/list_user_projects.cpp new file mode 100644 index 00000000..69516fc4 --- /dev/null +++ b/src/components/MisakiGuard/src/api/v1/auth/list_user_projects.cpp @@ -0,0 +1,116 @@ +/** + * @file list_user_projects.cpp + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2022 Tobias Anker + * + * 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 "list_user_projects.h" + +#include + +#include +#include +#include +#include + +#include +#include +#include + +using namespace Kitsunemimi::Hanami; + +/** + * @brief constructor + */ +ListUserProjects::ListUserProjects() + : Blossom("List all available projects of the user, who made the request.") +{ + //---------------------------------------------------------------------------------------------- + // input + //---------------------------------------------------------------------------------------------- + + registerInputField("user_id", + SAKURA_STRING_TYPE, + false, + "ID of the user."); + assert(addFieldBorder("user_id", 4, 256)); + assert(addFieldRegex("user_id", ID_EXT_REGEX)); + + //---------------------------------------------------------------------------------------------- + // output + //---------------------------------------------------------------------------------------------- + + registerOutputField("projects", + SAKURA_ARRAY_TYPE, + "Json-array with all assigned projects " + "together with role and project-admin-status."); + + //---------------------------------------------------------------------------------------------- + // + //---------------------------------------------------------------------------------------------- +} + +/** + * @brief runTask + */ +bool +ListUserProjects::runTask(BlossomIO &blossomIO, + const Kitsunemimi::DataMap &context, + BlossomStatus &status, + Kitsunemimi::ErrorContainer &error) +{ + const Kitsunemimi::Hanami::UserContext userContext(context); + std::string userId = blossomIO.input.get("user_id").getString(); + + // only admin is allowed to request the project-list of other users + if(userId != "" + && userContext.isAdmin == false) + { + status.statusCode = Kitsunemimi::Hanami::UNAUTHORIZED_RTYPE; + return false; + } + + // if no other user defined, use the user, who made this request + if(userId == "") { + userId = userContext.userId; + } + + // get data from table + Kitsunemimi::JsonItem userData; + if(MisakiRoot::usersTable->getUser(userData, userId, error, false) == false) + { + status.statusCode = Kitsunemimi::Hanami::INTERNAL_SERVER_ERROR_RTYPE; + return false; + } + + // if user is global admin, add the admin-project to the list of choosable projects + const bool isAdmin = userData.get("is_admin").getBool(); + if(isAdmin) + { + Kitsunemimi::DataMap* adminProject = new Kitsunemimi::DataMap(); + adminProject->insert("project_id", new Kitsunemimi::DataValue("admin")); + adminProject->insert("role", new Kitsunemimi::DataValue("admin")); + adminProject->insert("is_project_admin", new Kitsunemimi::DataValue(true)); + userData.get("projects").append(adminProject); + } + + blossomIO.output.insert("projects", userData.get("projects").stealItemContent()); + + return true; +} diff --git a/src/components/MisakiGuard/src/api/v1/auth/list_user_projects.h b/src/components/MisakiGuard/src/api/v1/auth/list_user_projects.h new file mode 100644 index 00000000..634df7b5 --- /dev/null +++ b/src/components/MisakiGuard/src/api/v1/auth/list_user_projects.h @@ -0,0 +1,41 @@ +/** + * @file list_user_projects.h + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2022 Tobias Anker + * + * 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. + */ + +#ifndef MISAKIGUARD_LIST_USER_PROJECTS_H +#define MISAKIGUARD_LIST_USER_PROJECTS_H + +#include + +class ListUserProjects + : public Kitsunemimi::Hanami::Blossom +{ +public: + ListUserProjects(); + +protected: + bool runTask(Kitsunemimi::Hanami::BlossomIO &blossomIO, + const Kitsunemimi::DataMap &context, + Kitsunemimi::Hanami::BlossomStatus &status, + Kitsunemimi::ErrorContainer &error); +}; + +#endif // MISAKIGUARD_LIST_USER_PROJECTS_H diff --git a/src/components/MisakiGuard/src/api/v1/auth/renew_token.cpp b/src/components/MisakiGuard/src/api/v1/auth/renew_token.cpp new file mode 100644 index 00000000..6012e8f2 --- /dev/null +++ b/src/components/MisakiGuard/src/api/v1/auth/renew_token.cpp @@ -0,0 +1,168 @@ +/** + * @file renew_token.cpp + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2022 Tobias Anker + * + * 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 "renew_token.h" + +#include + +#include +#include +#include + +#include +#include +#include + +using namespace Kitsunemimi::Hanami; + +/** + * @brief constructor + */ +RenewToken::RenewToken() + : Blossom("Create a JWT-access-token for a specific user.") +{ + //---------------------------------------------------------------------------------------------- + // input + //---------------------------------------------------------------------------------------------- + + registerInputField("project_id", + SAKURA_STRING_TYPE, + true, + "ID of the project, which has to be used for the new token."); + assert(addFieldBorder("project_id", 4, 256)); + assert(addFieldRegex("project_id", ID_REGEX)); + + //---------------------------------------------------------------------------------------------- + // output + //---------------------------------------------------------------------------------------------- + + registerOutputField("id", + SAKURA_STRING_TYPE, + "ID of the user."); + registerOutputField("name", + SAKURA_STRING_TYPE, + "Name of the user."); + registerOutputField("is_admin", + SAKURA_BOOL_TYPE, + "Set this to true to register the new user as admin."); + registerOutputField("token", + SAKURA_STRING_TYPE, + "New JWT-access-token for the user."); + + //---------------------------------------------------------------------------------------------- + // + //---------------------------------------------------------------------------------------------- +} + +/** + * @brief runTask + */ +bool +RenewToken::runTask(BlossomIO &blossomIO, + const Kitsunemimi::DataMap &context, + BlossomStatus &status, + Kitsunemimi::ErrorContainer &error) +{ + const Kitsunemimi::Hanami::UserContext userContext(context); + const std::string projectId = blossomIO.input.get("project_id").getString(); + + // get data from table + Kitsunemimi::JsonItem userData; + if(MisakiRoot::usersTable->getUser(userData, userContext.userId, error, false) == false) + { + status.statusCode = Kitsunemimi::Hanami::INTERNAL_SERVER_ERROR_RTYPE; + return false; + } + + Kitsunemimi::JsonItem parsedProjects = userData.get("projects"); + + // if user is global admin, add the admin-project to the list of choosable projects + const bool isAdmin = userData.get("is_admin").getBool(); + if(isAdmin) + { + Kitsunemimi::DataMap* adminProject = new Kitsunemimi::DataMap(); + adminProject->insert("project_id", new Kitsunemimi::DataValue("admin")); + adminProject->insert("role", new Kitsunemimi::DataValue("admin")); + adminProject->insert("is_project_admin", new Kitsunemimi::DataValue(true)); + parsedProjects.append(adminProject); + } + + // select project + if(chooseProject(userData, parsedProjects, projectId) == false) + { + status.errorMessage = "User with id '" + + userContext.userId + + "' is not assigned to the project with id '" + + projectId + + "'."; + error.addMeesage(status.errorMessage); + status.statusCode = Kitsunemimi::Hanami::UNAUTHORIZED_RTYPE; + return false; + } + + // create token + // TODO: make validation-time configurable + std::string jwtToken; + if(MisakiRoot::jwt->create_HS256_Token(jwtToken, userData, 3600, error) == false) + { + error.addMeesage("Failed to create JWT-Token"); + status.statusCode = Kitsunemimi::Hanami::INTERNAL_SERVER_ERROR_RTYPE; + return false; + } + + blossomIO.output.insert("id", userContext.userId); + blossomIO.output.insert("is_admin", isAdmin); + blossomIO.output.insert("name", userData.get("name").getString()); + blossomIO.output.insert("token", jwtToken); + + return true; +} + +/** + * @brief get project information for a new selected project from the user-assigned data + * + * @param userData user-data coming from database + * @param parsedProjects list of projects, which are assigned to the user + * @param selectedProjectId new desired project-id for the new token + * + * @return true, if selectedProjectId is available for the user, else false + */ +bool +RenewToken::chooseProject(Kitsunemimi::JsonItem &userData, + Kitsunemimi::JsonItem &parsedProjects, + const std::string selectedProjectId) +{ + for(uint64_t i = 0; i < parsedProjects.size(); i++) + { + if(parsedProjects.get(i).get("project_id").getString() == selectedProjectId) + { + userData.insert("project_id", parsedProjects.get(i).get("project_id")); + userData.insert("role", parsedProjects.get(i).get("role")); + userData.insert("is_project_admin", parsedProjects.get(i).get("is_project_admin")); + userData.remove("projects"); + + return true; + } + } + + return false; +} diff --git a/src/components/MisakiGuard/src/api/v1/auth/renew_token.h b/src/components/MisakiGuard/src/api/v1/auth/renew_token.h new file mode 100644 index 00000000..3587cdbb --- /dev/null +++ b/src/components/MisakiGuard/src/api/v1/auth/renew_token.h @@ -0,0 +1,45 @@ +/** + * @file renew_token.h + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2022 Tobias Anker + * + * 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. + */ + +#ifndef MISAKIGUARD_RENEW_TOKEN_H +#define MISAKIGUARD_RENEW_TOKEN_H + +#include + +class RenewToken + : public Kitsunemimi::Hanami::Blossom +{ +public: + RenewToken(); + +protected: + bool runTask(Kitsunemimi::Hanami::BlossomIO &blossomIO, + const Kitsunemimi::DataMap &context, + Kitsunemimi::Hanami::BlossomStatus &status, + Kitsunemimi::ErrorContainer &error); +private: + bool chooseProject(Kitsunemimi::JsonItem &userData, + Kitsunemimi::JsonItem &parsedProjects, + const std::string selectedProjectId); +}; + +#endif // MISAKIGUARD_RENEW_TOKEN_H diff --git a/src/components/MisakiGuard/src/api/v1/auth/validate_access.cpp b/src/components/MisakiGuard/src/api/v1/auth/validate_access.cpp new file mode 100644 index 00000000..38ed9579 --- /dev/null +++ b/src/components/MisakiGuard/src/api/v1/auth/validate_access.cpp @@ -0,0 +1,174 @@ +/** + * @file validate_access.cpp + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2022 Tobias Anker + * + * 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 "validate_access.h" + +#include +#include +#include +#include + +#include +#include +#include +#include + +#include + +using namespace Kitsunemimi::Hanami; +using Kitsunemimi::Hanami::HttpRequestType; +using Kitsunemimi::Hanami::SupportedComponents; +using Kitsunemimi::Hanami::HanamiMessaging; +using Kitsunemimi::Hanami::HanamiMessagingClient; + +/** + * @brief constructor + */ +ValidateAccess::ValidateAccess() + : Blossom("Checks if a JWT-access-token of a user is valid or not " + "and optional check if the user is allowed by its roles " + "and the policy to access a specific endpoint.") +{ + //---------------------------------------------------------------------------------------------- + // input + //---------------------------------------------------------------------------------------------- + + registerInputField("token", + SAKURA_STRING_TYPE, + true, + "User specific JWT-access-token."); + assert(addFieldRegex("token", "[a-zA-Z_.\\-0-9]*")); + + registerInputField("component", + SAKURA_STRING_TYPE, + false, + "Requested component-name of the request. If this is not set, then only " + "the token in itself will be validated."); + assert(addFieldBorder("component", 4, 256)); + assert(addFieldRegex("component", "[a-zA-Z][a-zA-Z_0-9]*")); + + registerInputField("endpoint", + SAKURA_STRING_TYPE, + false, + "Requesed endpoint within the component."); + assert(addFieldBorder("endpoint", 4, 256)); + assert(addFieldRegex("endpoint", "[a-zA-Z][a-zA-Z_/0-9]*")); + + registerInputField("http_type", + SAKURA_INT_TYPE, + false, + "Type of the HTTP-request as enum " + "(DELETE = 1, GET = 2, HEAD = 3, POST = 4, PUT = 5)."); + assert(addFieldBorder("http_type", 1, 5)); + + //---------------------------------------------------------------------------------------------- + // output + //---------------------------------------------------------------------------------------------- + + registerOutputField("id", + SAKURA_STRING_TYPE, + "ID of the user."); + registerOutputField("name", + SAKURA_STRING_TYPE, + "Name of the user."); + registerOutputField("is_admin", + SAKURA_BOOL_TYPE, + "Show if the user is an admin or not."); + registerOutputField("project_id", + SAKURA_STRING_TYPE, + "Selected project of the user."); + registerOutputField("role", + SAKURA_STRING_TYPE, + "Role of the user within the project."); + registerOutputField("is_project_admin", + SAKURA_BOOL_TYPE, + "True, if the user is admin within the selected project."); + + //---------------------------------------------------------------------------------------------- + // + //---------------------------------------------------------------------------------------------- +} + +/** + * @brief runTask + */ +bool +ValidateAccess::runTask(BlossomIO &blossomIO, + const Kitsunemimi::DataMap &, + BlossomStatus &status, + Kitsunemimi::ErrorContainer &error) +{ + // collect information from the input + const std::string token = blossomIO.input.get("token").getString(); + const std::string component = blossomIO.input.get("component").getString(); + const std::string endpoint = blossomIO.input.get("endpoint").getString(); + + // validate token + std::string publicError; + if(MisakiRoot::jwt->validateToken(blossomIO.output, token, publicError, error) == false) + { + error.addMeesage("Misaki failed to validate JWT-Token"); + status.errorMessage = publicError; + status.statusCode = Kitsunemimi::Hanami::UNAUTHORIZED_RTYPE; + return false; + } + + // allow skipping policy-check + // TODO: find better solution to make a difference, if policy should be checked or not + if(component != "") + { + if(blossomIO.input.contains("http_type") == false) + { + error.addMeesage("http_type is missing in token-request"); + status.statusCode = Kitsunemimi::Hanami::INTERNAL_SERVER_ERROR_RTYPE; + return false; + } + + const uint32_t httpTypeValue = blossomIO.input.get("http_type").getInt(); + const HttpRequestType httpType = static_cast(httpTypeValue); + + // process payload to get role of user + const std::string role = blossomIO.output.get("role").getString(); + + // check policy + if(MisakiRoot::policies->checkUserAgainstPolicy(component, + endpoint, + httpType, + role) == false) + { + status.errorMessage = "Access denied by policy"; + status.statusCode = Kitsunemimi::Hanami::UNAUTHORIZED_RTYPE; + error.addMeesage(status.errorMessage); + return false; + } + } + + // remove irrelevant fields + blossomIO.output.remove("pw_hash"); + blossomIO.output.remove("creator_id"); + blossomIO.output.remove("exp"); + blossomIO.output.remove("iat"); + blossomIO.output.remove("nbf"); + + return true; +} + diff --git a/src/components/MisakiGuard/src/api/v1/auth/validate_access.h b/src/components/MisakiGuard/src/api/v1/auth/validate_access.h new file mode 100644 index 00000000..e6beacbe --- /dev/null +++ b/src/components/MisakiGuard/src/api/v1/auth/validate_access.h @@ -0,0 +1,42 @@ +/** + * @file validate_access.h + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2022 Tobias Anker + * + * 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. + */ + +#ifndef MISAKIGUARD_VALIDATE_ACCESS_H +#define MISAKIGUARD_VALIDATE_ACCESS_H + +#include +#include + +class ValidateAccess + : public Kitsunemimi::Hanami::Blossom +{ +public: + ValidateAccess(); + +protected: + bool runTask(Kitsunemimi::Hanami::BlossomIO &blossomIO, + const Kitsunemimi::DataMap &, + Kitsunemimi::Hanami::BlossomStatus &status, + Kitsunemimi::ErrorContainer &error); +}; + +#endif // MISAKIGUARD_VALIDATE_ACCESS_H diff --git a/src/components/MisakiGuard/src/api/v1/documentation/generate_rest_api_docu.cpp b/src/components/MisakiGuard/src/api/v1/documentation/generate_rest_api_docu.cpp new file mode 100644 index 00000000..3154ac6f --- /dev/null +++ b/src/components/MisakiGuard/src/api/v1/documentation/generate_rest_api_docu.cpp @@ -0,0 +1,347 @@ +/** + * @file generate_rest_api_docu.cpp + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2022 Tobias Anker + * + * 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 "generate_rest_api_docu.h" + +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +using namespace Kitsunemimi::Hanami; +using Kitsunemimi::Hanami::SupportedComponents; +using Kitsunemimi::Hanami::HanamiMessaging; + +/** + * @brief constructor + */ +GenerateRestApiDocu::GenerateRestApiDocu() + : Blossom("Generate a documentation for the REST-API of all available components.") +{ + //---------------------------------------------------------------------------------------------- + // input + //---------------------------------------------------------------------------------------------- + + registerInputField("type", + SAKURA_STRING_TYPE, + false, + "Output-type of the document (pdf, rst, md)."); + assert(addFieldDefault("type", new Kitsunemimi::DataValue("pdf"))); + assert(addFieldRegex("type", "^(pdf|rst|md)$")); + + //---------------------------------------------------------------------------------------------- + // output + //---------------------------------------------------------------------------------------------- + + registerOutputField("documentation", + SAKURA_STRING_TYPE, + "REST-API-documentation as base64 converted string."); + + //---------------------------------------------------------------------------------------------- + // + //---------------------------------------------------------------------------------------------- +} + +bool +appendDocu(std::string &completeDocumentation, + const std::string &componentDocu, + Kitsunemimi::ErrorContainer &error) +{ + std::string rstDocu; + if(Kitsunemimi::decodeBase64(rstDocu, componentDocu) == false) + { + error.addMeesage("Unable to convert documentation-payload from base64 back to rst"); + return false; + } + + // attach new text to the final document + completeDocumentation.append("\n"); + completeDocumentation.append(rstDocu); + + return true; +} + +/** + * @brief request another component for its documentation + * + * @param completeDocumentation reference for the final document to attach new content + * @param component name of the requested component + * @param request prebuild request-object + * @param error reference for error-output + * + * @return true, if successful and response is positive, else false + */ +bool +requestComponent(std::string &completeDocumentation, + const std::string &component, + const Kitsunemimi::Hanami::RequestMessage &request, + Kitsunemimi::ErrorContainer &error) +{ + Kitsunemimi::Hanami::HanamiMessaging* msg = Kitsunemimi::Hanami::HanamiMessaging::getInstance(); + Kitsunemimi::Hanami::ResponseMessage response; + Kitsunemimi::Hanami::HanamiMessagingClient* client = msg->getOutgoingClient(component); + + if(client == nullptr) { + return false; + } + + // send request to the target + if(client->triggerSakuraFile(response, request, error) == false) { + return false; + } + + // check response + if(response.success == false) + { + error.addMeesage(response.responseContent); + return false; + } + + // parse result + Kitsunemimi::JsonItem jsonItem; + if(jsonItem.parse(response.responseContent, error) == false) + { + return false; + } + + // get payload and convert it from base64 back to rst-file-format + const std::string componentDocu = jsonItem.get("documentation").getString(); + + return appendDocu(completeDocumentation, componentDocu, error); +} + +/** + * @brief request endpoint-documentation from misaki itself + * + * @param completeDocumentation reference for the final document to attach new content + */ +bool +makeInternalRequest(std::string &completeDocumentation, + const std::string &type) +{ + HanamiMessaging* interface = HanamiMessaging::getInstance(); + Kitsunemimi::DataMap result; + Kitsunemimi::ErrorContainer error; + Kitsunemimi::Hanami::BlossomStatus status; + Kitsunemimi::DataMap values; + values.insert("type", new Kitsunemimi::DataValue(type)); + + const bool ret = interface->triggerBlossom(result, + "get_api_documentation", + "-", + Kitsunemimi::DataMap(), + values, + status, + error); + if(ret == false) { + return false; + } + + return appendDocu(completeDocumentation, result.getStringByKey("documentation"), error); +} + +/** + * @brief runTask + */ +bool +GenerateRestApiDocu::runTask(BlossomIO &blossomIO, + const Kitsunemimi::DataMap &context, + BlossomStatus &status, + Kitsunemimi::ErrorContainer &error) +{ + const std::string role = context.getStringByKey("role"); + const std::string type = blossomIO.input.get("type").getString(); + const std::string token = context.getStringByKey("token"); + + // create request for remote-calls + Kitsunemimi::Hanami::RequestMessage request; + request.id = "v1/documentation/api"; + request.httpType = Kitsunemimi::Hanami::GET_TYPE; + request.inputValues = "{\"token\":\"" + token + "\",\"type\":\"" + type + "\"}"; + + // create header of the final document + std::string completeDocumentation = ""; + + if(type == "pdf" + || type == "rst") + { + completeDocumentation.append("*****************\n"); + completeDocumentation.append("API documentation\n"); + completeDocumentation.append("*****************\n\n"); + } + else if(type == "md") + { + completeDocumentation.append("# API documentation\n"); + } + + SupportedComponents* scomp = SupportedComponents::getInstance(); + + //---------------------------------------------------------------------------------------------- + makeInternalRequest(completeDocumentation, type); + //---------------------------------------------------------------------------------------------- + if(scomp->support[Kitsunemimi::Hanami::KYOUKO] + && requestComponent(completeDocumentation, "kyouko", request, error) == false) + { + status.statusCode = Kitsunemimi::Hanami::INTERNAL_SERVER_ERROR_RTYPE; + } + //---------------------------------------------------------------------------------------------- + if(scomp->support[Kitsunemimi::Hanami::AZUKI] + && requestComponent(completeDocumentation, "azuki", request, error) == false) + { + status.statusCode = Kitsunemimi::Hanami::INTERNAL_SERVER_ERROR_RTYPE; + } + //---------------------------------------------------------------------------------------------- + if(scomp->support[Kitsunemimi::Hanami::SHIORI] + && requestComponent(completeDocumentation, "shiori", request, error) == false) + { + status.statusCode = Kitsunemimi::Hanami::INTERNAL_SERVER_ERROR_RTYPE; + } + //---------------------------------------------------------------------------------------------- + if(scomp->support[Kitsunemimi::Hanami::NOZOMI] + && requestComponent(completeDocumentation, "nozomi", request, error) == false) + { + status.statusCode = Kitsunemimi::Hanami::INTERNAL_SERVER_ERROR_RTYPE; + } + //---------------------------------------------------------------------------------------------- + if(scomp->support[Kitsunemimi::Hanami::INORI] + && requestComponent(completeDocumentation, "inori", request, error) == false) + { + status.statusCode = Kitsunemimi::Hanami::INTERNAL_SERVER_ERROR_RTYPE; + } + //---------------------------------------------------------------------------------------------- + + std::string output; + + if(type == "pdf") + { + if(convertRstToPdf(output, completeDocumentation, error) == false) + { + status.statusCode = Kitsunemimi::Hanami::INTERNAL_SERVER_ERROR_RTYPE; + error.addMeesage("Failed to convert documentation from 'rst' to 'pdf'"); + return false; + } + } + else + { + Kitsunemimi::encodeBase64(output, + completeDocumentation.c_str(), + completeDocumentation.size()); + } + + blossomIO.output.insert("documentation", output); + + return true; +} + +/** + * @brief convert rst-document into a pdf-document + * + * @param pdfOutput reference to return the resulting pdf-document as base64 converted string + * @param rstInput string with rst-formated document + * @param error reference for error-output + * + * @return true, if conversion was successful, else false + */ +bool +GenerateRestApiDocu::convertRstToPdf(std::string &pdfOutput, + const std::string &rstInput, + Kitsunemimi::ErrorContainer &error) +{ + bool result = false; + const std::string uuid = Kitsunemimi::Hanami::generateUuid().toString(); + const std::string tempDir = "/tmp/" + uuid; + + do + { + // create unique temporary directory + if(Kitsunemimi::createDirectory(tempDir, error) == false) + { + error.addMeesage("Failed to create temporary rst-directory to path '" + tempDir + "'"); + break; + } + + // define file-paths + const std::string rstPath = "/tmp/" + uuid + "/rest_api_docu.rst"; + const std::string pdfPath = "/tmp/" + uuid + "/output.pdf"; + + // write complete rst-content to the source-file + if(Kitsunemimi::writeFile(rstPath, rstInput, error) == false) + { + error.addMeesage("Failed to write temporary rst-file to path '" + rstPath + "'"); + break; + } + + // run rst2pdf to convert the rst-document into a pdf-document + std::vector args; + args.reserve(2); + args.emplace_back(rstPath); + args.emplace_back(pdfPath); + Kitsunemimi::ProcessResult ret = Kitsunemimi::runSyncProcess("rst2pdf", args); + if(ret.success == false) + { + error.addMeesage("Failed execute 'rst2pdf' to convert rst-file '" + + rstPath + + "' to pdf-file '" + + pdfPath + + "'"); + error.addSolution("Check if tool 'rst2pdf' is installed."); + error.addSolution("Check if tool 'rst2pdf' is executable " + "and if not fix this with 'chmod +x /PATH/TO/BINARY'."); + error.addSolution("Check if enough memory and storage is available."); + break; + } + + // read pdf-document into a byte-buffer + Kitsunemimi::DataBuffer pdfContent; + Kitsunemimi::BinaryFile pdfFile(pdfPath); + + if(pdfFile.readCompleteFile(pdfContent, error) == false) + { + error.addMeesage("Failed to read pdf-file on path '" + pdfPath + "'"); + break; + } + pdfFile.closeFile(error); + + // create output for the client + Kitsunemimi::encodeBase64(pdfOutput, pdfContent.data, pdfContent.usedBufferSize); + + result = true; + break; + } + while(true); + + // HINT(kitsudaiki): ignore result here, because it is only for cleanup + Kitsunemimi::deleteFileOrDir(tempDir, error); + + return result; +} diff --git a/src/components/MisakiGuard/src/api/v1/documentation/generate_rest_api_docu.h b/src/components/MisakiGuard/src/api/v1/documentation/generate_rest_api_docu.h new file mode 100644 index 00000000..9e1bb6f8 --- /dev/null +++ b/src/components/MisakiGuard/src/api/v1/documentation/generate_rest_api_docu.h @@ -0,0 +1,46 @@ +/** + * @file generate_rest_api_docu.h + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2022 Tobias Anker + * + * 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. + */ + +#ifndef MISAKIGUARD_GENERATERESTAPIDOCU_H +#define MISAKIGUARD_GENERATERESTAPIDOCU_H + +#include + +class GenerateRestApiDocu + : public Kitsunemimi::Hanami::Blossom +{ +public: + GenerateRestApiDocu(); + +protected: + bool runTask(Kitsunemimi::Hanami::BlossomIO &blossomIO, + const Kitsunemimi::DataMap &context, + Kitsunemimi::Hanami::BlossomStatus &status, + Kitsunemimi::ErrorContainer &error); + +private: + bool convertRstToPdf(std::string &pdfOutput, + const std::string &rstInput, + Kitsunemimi::ErrorContainer &error); +}; + +#endif // MISAKIGUARD_GENERATERESTAPIDOCU_H diff --git a/src/components/MisakiGuard/src/api/v1/project/create_project.cpp b/src/components/MisakiGuard/src/api/v1/project/create_project.cpp new file mode 100644 index 00000000..a30b4e3b --- /dev/null +++ b/src/components/MisakiGuard/src/api/v1/project/create_project.cpp @@ -0,0 +1,138 @@ +/** + * @file create_project.cpp + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2022 Tobias Anker + * + * 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 "create_project.h" + +#include + +#include +#include +#include + +#include +#include +#include + +using namespace Kitsunemimi::Hanami; + +/** + * @brief constructor + */ +CreateProject::CreateProject() + : Blossom("Register a new project within Misaki.") +{ + //---------------------------------------------------------------------------------------------- + // input + //---------------------------------------------------------------------------------------------- + + registerInputField("id", + SAKURA_STRING_TYPE, + true, + "ID of the new project."); + // column in database is limited to 256 characters size + assert(addFieldBorder("id", 4, 256)); + assert(addFieldRegex("id", ID_REGEX)); + + registerInputField("name", + SAKURA_STRING_TYPE, + true, + "Name of the new project."); + // column in database is limited to 256 characters size + assert(addFieldBorder("name", 4, 256)); + assert(addFieldRegex("name", NAME_REGEX)); + + //---------------------------------------------------------------------------------------------- + // output + //---------------------------------------------------------------------------------------------- + + registerOutputField("id", + SAKURA_STRING_TYPE, + "ID of the new project."); + + registerOutputField("name", + SAKURA_STRING_TYPE, + "Name of the new project."); + + registerOutputField("creator_id", + SAKURA_STRING_TYPE, + "Id of the creator of the project."); + + //---------------------------------------------------------------------------------------------- + // + //---------------------------------------------------------------------------------------------- +} + +/** + * @brief runTask + */ +bool +CreateProject::runTask(BlossomIO &blossomIO, + const Kitsunemimi::DataMap &context, + BlossomStatus &status, + Kitsunemimi::ErrorContainer &error) +{ + // check if admin + if(context.getBoolByKey("is_admin") == false) + { + status.statusCode = Kitsunemimi::Hanami::UNAUTHORIZED_RTYPE; + return false; + } + + // get information from request + const std::string projectId = blossomIO.input.get("id").getString(); + const std::string projectName = blossomIO.input.get("name").getString(); + const std::string creatorId = context.getStringByKey("id"); + + // check if user already exist within the table + Kitsunemimi::JsonItem getResult; + if(MisakiRoot::projectsTable->getProject(getResult, projectId, error)) + { + status.errorMessage = "Project with id '" + projectId + "' already exist."; + status.statusCode = Kitsunemimi::Hanami::CONFLICT_RTYPE; + return false; + } + + // convert values + Kitsunemimi::JsonItem userData; + userData.insert("id", projectId); + userData.insert("name", projectName); + userData.insert("creator_id", creatorId); + + // add new user to table + if(MisakiRoot::projectsTable->addProject(userData, error) == false) + { + status.errorMessage = error.toString(); + status.statusCode = Kitsunemimi::Hanami::INTERNAL_SERVER_ERROR_RTYPE; + return false; + } + + // get new created user from database + if(MisakiRoot::projectsTable->getProject(blossomIO.output, + projectId, + error) == false) + { + status.statusCode = Kitsunemimi::Hanami::INTERNAL_SERVER_ERROR_RTYPE; + return false; + } + + return true; +} diff --git a/src/components/MisakiGuard/src/api/v1/project/create_project.h b/src/components/MisakiGuard/src/api/v1/project/create_project.h new file mode 100644 index 00000000..bed2c824 --- /dev/null +++ b/src/components/MisakiGuard/src/api/v1/project/create_project.h @@ -0,0 +1,41 @@ +/** + * @file create_project.h + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2022 Tobias Anker + * + * 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. + */ + +#ifndef MISAKIGUARD_CREATEPROJECT_H +#define MISAKIGUARD_CREATEPROJECT_H + +#include + +class CreateProject + : public Kitsunemimi::Hanami::Blossom +{ +public: + CreateProject(); + +protected: + bool runTask(Kitsunemimi::Hanami::BlossomIO &blossomIO, + const Kitsunemimi::DataMap &context, + Kitsunemimi::Hanami::BlossomStatus &status, + Kitsunemimi::ErrorContainer &error); +}; + +#endif // MISAKIGUARD_CREATEPROJECT_H diff --git a/src/components/MisakiGuard/src/api/v1/project/delete_project.cpp b/src/components/MisakiGuard/src/api/v1/project/delete_project.cpp new file mode 100644 index 00000000..8f268b60 --- /dev/null +++ b/src/components/MisakiGuard/src/api/v1/project/delete_project.cpp @@ -0,0 +1,94 @@ +/** + * @file delete_project.cpp + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2022 Tobias Anker + * + * 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 "delete_project.h" + +#include + +#include + +#include +#include + +using namespace Kitsunemimi::Hanami; + +/** + * @brief constructor + */ +DeleteProject::DeleteProject() + : Blossom("Delete a specific user from the database.") +{ + //---------------------------------------------------------------------------------------------- + // input + //---------------------------------------------------------------------------------------------- + + registerInputField("id", + SAKURA_STRING_TYPE, + true, + "ID of the project."); + // column in database is limited to 256 characters size + assert(addFieldBorder("id", 4, 256)); + assert(addFieldRegex("id", ID_REGEX)); + + //---------------------------------------------------------------------------------------------- + // + //---------------------------------------------------------------------------------------------- +} + +/** + * @brief runTask + */ +bool +DeleteProject::runTask(BlossomIO &blossomIO, + const Kitsunemimi::DataMap &context, + BlossomStatus &status, + Kitsunemimi::ErrorContainer &error) +{ + // check if admin + if(context.getBoolByKey("is_admin") == false) + { + status.statusCode = Kitsunemimi::Hanami::UNAUTHORIZED_RTYPE; + return false; + } + + // get information from request + const std::string projectId = blossomIO.input.get("id").getString(); + + // check if user exist within the table + Kitsunemimi::JsonItem result; + if(MisakiRoot::projectsTable->getProject(result, projectId, error) == false) + { + status.errorMessage = "Project with id '" + projectId + "' not found."; + status.statusCode = Kitsunemimi::Hanami::NOT_FOUND_RTYPE; + error.addMeesage(status.errorMessage); + return false; + } + + // get data from table + if(MisakiRoot::projectsTable->deleteProject(projectId, error) == false) + { + status.statusCode = Kitsunemimi::Hanami::INTERNAL_SERVER_ERROR_RTYPE; + return false; + } + + return true; +} diff --git a/src/components/MisakiGuard/src/api/v1/project/delete_project.h b/src/components/MisakiGuard/src/api/v1/project/delete_project.h new file mode 100644 index 00000000..75e6ac1b --- /dev/null +++ b/src/components/MisakiGuard/src/api/v1/project/delete_project.h @@ -0,0 +1,41 @@ +/** + * @file delete_project.h + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2022 Tobias Anker + * + * 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. + */ + +#ifndef MISAKIGUARD_DELETEPROJECT_H +#define MISAKIGUARD_DELETEPROJECT_H + +#include + +class DeleteProject + : public Kitsunemimi::Hanami::Blossom +{ +public: + DeleteProject(); + +protected: + bool runTask(Kitsunemimi::Hanami::BlossomIO &blossomIO, + const Kitsunemimi::DataMap &context, + Kitsunemimi::Hanami::BlossomStatus &status, + Kitsunemimi::ErrorContainer &error); +}; + +#endif // MISAKIGUARD_DELETEPROJECT_H diff --git a/src/components/MisakiGuard/src/api/v1/project/get_project.cpp b/src/components/MisakiGuard/src/api/v1/project/get_project.cpp new file mode 100644 index 00000000..c210af53 --- /dev/null +++ b/src/components/MisakiGuard/src/api/v1/project/get_project.cpp @@ -0,0 +1,99 @@ +/** + * @file get_project.cpp + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2022 Tobias Anker + * + * 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 "get_project.h" + +#include + +#include + +#include +#include + +using namespace Kitsunemimi::Hanami; + +/** + * @brief constructor + */ +GetProject::GetProject() + : Blossom("Show information of a specific registered user.") +{ + //---------------------------------------------------------------------------------------------- + // input + //---------------------------------------------------------------------------------------------- + + registerInputField("id", + SAKURA_STRING_TYPE, + true, + "Id of the user."); + // column in database is limited to 256 characters size + assert(addFieldBorder("id", 4, 256)); + assert(addFieldRegex("id", ID_REGEX)); + + //---------------------------------------------------------------------------------------------- + // output + //---------------------------------------------------------------------------------------------- + + registerOutputField("id", + SAKURA_STRING_TYPE, + "ID of the new user."); + registerOutputField("name", + SAKURA_STRING_TYPE, + "Name of the new user."); + registerOutputField("creator_id", + SAKURA_STRING_TYPE, + "Id of the creator of the user."); + + //---------------------------------------------------------------------------------------------- + // + //---------------------------------------------------------------------------------------------- +} + +/** + * @brief runTask + */ +bool +GetProject::runTask(BlossomIO &blossomIO, + const Kitsunemimi::DataMap &context, + BlossomStatus &status, + Kitsunemimi::ErrorContainer &error) +{ + // check if admin + if(context.getBoolByKey("is_admin") == false) + { + status.statusCode = Kitsunemimi::Hanami::UNAUTHORIZED_RTYPE; + return false; + } + + // get information from request + const std::string projectId = blossomIO.input.get("id").getString(); + + // get data from table + if(MisakiRoot::projectsTable->getProject(blossomIO.output, projectId, error) == false) + { + status.errorMessage = "Project with id '" + projectId + "' not found."; + status.statusCode = Kitsunemimi::Hanami::NOT_FOUND_RTYPE; + return false; + } + + return true; +} diff --git a/src/components/MisakiGuard/src/api/v1/project/get_project.h b/src/components/MisakiGuard/src/api/v1/project/get_project.h new file mode 100644 index 00000000..0fa2e962 --- /dev/null +++ b/src/components/MisakiGuard/src/api/v1/project/get_project.h @@ -0,0 +1,41 @@ +/** + * @file get_project.h + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2022 Tobias Anker + * + * 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. + */ + +#ifndef MISAKIGUARD_GETPROJECT_H +#define MISAKIGUARD_GETPROJECT_H + +#include + +class GetProject + : public Kitsunemimi::Hanami::Blossom +{ +public: + GetProject(); + +protected: + bool runTask(Kitsunemimi::Hanami::BlossomIO &blossomIO, + const Kitsunemimi::DataMap &context, + Kitsunemimi::Hanami::BlossomStatus &status, + Kitsunemimi::ErrorContainer &error); +}; + +#endif // MISAKIGUARD_GETPROJECT_H diff --git a/src/components/MisakiGuard/src/api/v1/project/list_projects.cpp b/src/components/MisakiGuard/src/api/v1/project/list_projects.cpp new file mode 100644 index 00000000..d3ae0ea8 --- /dev/null +++ b/src/components/MisakiGuard/src/api/v1/project/list_projects.cpp @@ -0,0 +1,84 @@ +/** + * @file list_projects.cpp + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2022 Tobias Anker + * + * 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 "list_projects.h" + +#include + +#include + +using namespace Kitsunemimi::Hanami; + +/** + * @brief constructor + */ +ListProjects::ListProjects() + : Blossom("Get information of all registered user as table.") +{ + //---------------------------------------------------------------------------------------------- + // output + //---------------------------------------------------------------------------------------------- + + registerOutputField("header", + SAKURA_ARRAY_TYPE, + "Array with the namings all columns of the table."); + assert(addFieldMatch("header", new Kitsunemimi::DataValue("[\"id\"" + ",\"name\"" + ",\"creator_id\"]"))); + registerOutputField("body", + SAKURA_ARRAY_TYPE, + "Array with all rows of the table, which array arrays too."); + + //---------------------------------------------------------------------------------------------- + // + //---------------------------------------------------------------------------------------------- +} + +/** + * @brief runTask + */ +bool +ListProjects::runTask(BlossomIO &blossomIO, + const Kitsunemimi::DataMap &context, + BlossomStatus &status, + Kitsunemimi::ErrorContainer &error) +{ + // check if admin + if(context.getBoolByKey("is_admin") == false) + { + status.statusCode = Kitsunemimi::Hanami::UNAUTHORIZED_RTYPE; + return false; + } + + // get data from table + Kitsunemimi::TableItem table; + if(MisakiRoot::projectsTable->getAllProjects(table, error) == false) + { + status.statusCode = Kitsunemimi::Hanami::INTERNAL_SERVER_ERROR_RTYPE; + return false; + } + + blossomIO.output.insert("header", table.getInnerHeader()); + blossomIO.output.insert("body", table.getBody()); + + return true; +} diff --git a/src/components/MisakiGuard/src/api/v1/project/list_projects.h b/src/components/MisakiGuard/src/api/v1/project/list_projects.h new file mode 100644 index 00000000..144c7c1d --- /dev/null +++ b/src/components/MisakiGuard/src/api/v1/project/list_projects.h @@ -0,0 +1,41 @@ +/** + * @file list_projects.h + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2022 Tobias Anker + * + * 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. + */ + +#ifndef MISAKIGUARD_LISTPROJECTS_H +#define MISAKIGUARD_LISTPROJECTS_H + +#include + +class ListProjects + : public Kitsunemimi::Hanami::Blossom +{ +public: + ListProjects(); + +protected: + bool runTask(Kitsunemimi::Hanami::BlossomIO &blossomIO, + const Kitsunemimi::DataMap &context, + Kitsunemimi::Hanami::BlossomStatus &status, + Kitsunemimi::ErrorContainer &error); +}; + +#endif // MISAKIGUARD_LISTPROJECTS_H diff --git a/src/components/MisakiGuard/src/api/v1/user/add_project_to_user.cpp b/src/components/MisakiGuard/src/api/v1/user/add_project_to_user.cpp new file mode 100644 index 00000000..df4b45f5 --- /dev/null +++ b/src/components/MisakiGuard/src/api/v1/user/add_project_to_user.cpp @@ -0,0 +1,176 @@ +/** + * @file add_project_to_user.cpp + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2022 Tobias Anker + * + * 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 "add_project_to_user.h" + +#include +#include +#include +#include + +#include +#include +#include + +using namespace Kitsunemimi::Hanami; + +/** + * @brief constructor + */ +AddProjectToUser::AddProjectToUser() + : Blossom("Add a project to a specific user.") +{ + //---------------------------------------------------------------------------------------------- + // input + //---------------------------------------------------------------------------------------------- + + registerInputField("id", + SAKURA_STRING_TYPE, + true, + "ID of the user."); + assert(addFieldBorder("id", 4, 256)); + assert(addFieldRegex("id", ID_EXT_REGEX)); + + registerInputField("project_id", + SAKURA_STRING_TYPE, + true, + "ID of the project, which has to be added to the user."); + assert(addFieldBorder("project_id", 4, 256)); + assert(addFieldRegex("project_id", ID_REGEX)); + + registerInputField("role", + SAKURA_STRING_TYPE, + true, + "Role, which has to be assigned to the user within the project"); + assert(addFieldBorder("role", 4, 256)); + assert(addFieldRegex("role", ID_REGEX)); + + registerInputField("is_project_admin", + SAKURA_BOOL_TYPE, + false, + "Set this to true, if the user should be an admin " + "within the assigned project."); + assert(addFieldDefault("is_project_admin", new Kitsunemimi::DataValue(false))); + + //---------------------------------------------------------------------------------------------- + // output + //---------------------------------------------------------------------------------------------- + + registerOutputField("id", + SAKURA_STRING_TYPE, + "ID of the user."); + registerOutputField("name", + SAKURA_STRING_TYPE, + "Name of the user."); + registerOutputField("is_admin", + SAKURA_BOOL_TYPE, + "True, if user is an admin."); + registerOutputField("creator_id", + SAKURA_STRING_TYPE, + "Id of the creator of the user."); + registerOutputField("projects", + SAKURA_ARRAY_TYPE, + "Json-array with all assigned projects " + "together with role and project-admin-status."); + + //---------------------------------------------------------------------------------------------- + // + //---------------------------------------------------------------------------------------------- +} + +/** + * @brief runTask + */ +bool +AddProjectToUser::runTask(BlossomIO &blossomIO, + const Kitsunemimi::DataMap &context, + BlossomStatus &status, + Kitsunemimi::ErrorContainer &error) +{ + // check if admin + if(context.getBoolByKey("is_admin") == false) + { + status.statusCode = Kitsunemimi::Hanami::UNAUTHORIZED_RTYPE; + return false; + } + + const std::string userId = blossomIO.input.get("id").getString(); + const std::string projectId = blossomIO.input.get("project_id").getString(); + const std::string role = blossomIO.input.get("role").getString(); + const bool isProjectAdmin = blossomIO.input.get("is_project_admin").getBool(); + const std::string creatorId = context.getStringByKey("id"); + + // check if user already exist within the table + Kitsunemimi::JsonItem getResult; + if(MisakiRoot::usersTable->getUser(getResult, userId, error, false) == false) + { + status.errorMessage = "User with id '" + userId + "' not found."; + status.statusCode = Kitsunemimi::Hanami::NOT_FOUND_RTYPE; + return false; + } + + // check if project is already assigned to user + Kitsunemimi::JsonItem parsedProjects = getResult.get("projects"); + for(uint64_t i = 0; i < parsedProjects.size(); i++) + { + if(parsedProjects.get(i).get("project_id").getString() == projectId) + { + status.errorMessage = "Project with ID '" + + projectId + + "' is already assigned to user with id '" + + userId + + "'."; + error.addMeesage(status.errorMessage); + status.statusCode = Kitsunemimi::Hanami::CONFLICT_RTYPE; + return false; + } + } + + // create new entry + Kitsunemimi::JsonItem newEntry; + newEntry.insert("project_id", projectId); + newEntry.insert("role", role); + newEntry.insert("is_project_admin", isProjectAdmin); + parsedProjects.append(newEntry); + + // updated projects of user in database + if(MisakiRoot::usersTable->updateProjectsOfUser(userId, + parsedProjects.toString(), + error) == false) + { + error.addMeesage("Failed to update projects of user with id '" + userId + "'."); + status.statusCode = Kitsunemimi::Hanami::INTERNAL_SERVER_ERROR_RTYPE; + return false; + } + + // get new created user from database + if(MisakiRoot::usersTable->getUser(blossomIO.output, + userId, + error, + false) == false) + { + status.statusCode = Kitsunemimi::Hanami::INTERNAL_SERVER_ERROR_RTYPE; + return false; + } + + return true; +} diff --git a/src/components/MisakiGuard/src/api/v1/user/add_project_to_user.h b/src/components/MisakiGuard/src/api/v1/user/add_project_to_user.h new file mode 100644 index 00000000..d0aececa --- /dev/null +++ b/src/components/MisakiGuard/src/api/v1/user/add_project_to_user.h @@ -0,0 +1,41 @@ +/** + * @file add_project_to_user.h + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2022 Tobias Anker + * + * 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. + */ + +#ifndef MISAKIGUARD_ADD_PROJECT_TO_USER_H +#define MISAKIGUARD_ADD_PROJECT_TO_USER_H + +#include + +class AddProjectToUser + : public Kitsunemimi::Hanami::Blossom +{ +public: + AddProjectToUser(); + +protected: + bool runTask(Kitsunemimi::Hanami::BlossomIO &blossomIO, + const Kitsunemimi::DataMap &context, + Kitsunemimi::Hanami::BlossomStatus &status, + Kitsunemimi::ErrorContainer &error); +}; + +#endif // MISAKIGUARD_ADD_PROJECT_TO_USER_H diff --git a/src/components/MisakiGuard/src/api/v1/user/create_user.cpp b/src/components/MisakiGuard/src/api/v1/user/create_user.cpp new file mode 100644 index 00000000..ce1f3dcf --- /dev/null +++ b/src/components/MisakiGuard/src/api/v1/user/create_user.cpp @@ -0,0 +1,161 @@ +/** + * @file create_user.cpp + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2022 Tobias Anker + * + * 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 "create_user.h" + +#include +#include +#include +#include + +#include +#include +#include + +using namespace Kitsunemimi::Hanami; + +/** + * @brief constructor + */ +CreateUser::CreateUser() + : Blossom("Register a new user within Misaki.") +{ + //---------------------------------------------------------------------------------------------- + // input + //---------------------------------------------------------------------------------------------- + + registerInputField("id", + SAKURA_STRING_TYPE, + true, + "ID of the new user."); + assert(addFieldBorder("id", 4, 256)); + assert(addFieldRegex("id", ID_EXT_REGEX)); + + registerInputField("name", + SAKURA_STRING_TYPE, + true, + "Name of the new user."); + assert(addFieldBorder("name", 4, 256)); + assert(addFieldRegex("name", NAME_REGEX)); + + registerInputField("password", + SAKURA_STRING_TYPE, + true, + "Passphrase of the user."); + assert(addFieldBorder("password", 8, 4096)); + + registerInputField("is_admin", + SAKURA_BOOL_TYPE, + false, + "Set this to 1 to register the new user as admin."); + assert(addFieldDefault("is_admin", new Kitsunemimi::DataValue(false))); + + //---------------------------------------------------------------------------------------------- + // output + //---------------------------------------------------------------------------------------------- + + registerOutputField("id", + SAKURA_STRING_TYPE, + "ID of the new user."); + registerOutputField("name", + SAKURA_STRING_TYPE, + "Name of the new user."); + registerOutputField("is_admin", + SAKURA_BOOL_TYPE, + "True, if user is an admin."); + registerOutputField("creator_id", + SAKURA_STRING_TYPE, + "Id of the creator of the user."); + registerOutputField("projects", + SAKURA_ARRAY_TYPE, + "Json-array with all assigned projects " + "together with role and project-admin-status."); + + //---------------------------------------------------------------------------------------------- + // + //---------------------------------------------------------------------------------------------- +} + +/** + * @brief runTask + */ +bool +CreateUser::runTask(BlossomIO &blossomIO, + const Kitsunemimi::DataMap &context, + BlossomStatus &status, + Kitsunemimi::ErrorContainer &error) +{ + // check if admin + if(context.getBoolByKey("is_admin") == false) + { + status.statusCode = Kitsunemimi::Hanami::UNAUTHORIZED_RTYPE; + return false; + } + + const std::string newUserId = blossomIO.input.get("id").getString(); + const std::string creatorId = context.getStringByKey("id"); + + // check if user already exist within the table + Kitsunemimi::JsonItem getResult; + if(MisakiRoot::usersTable->getUser(getResult, newUserId, error, false)) + { + status.errorMessage = "User with id '" + newUserId + "' already exist."; + status.statusCode = Kitsunemimi::Hanami::CONFLICT_RTYPE; + return false; + } + + // genreate hash from password and random salt + std::string pwHash; + const std::string salt = Kitsunemimi::Hanami::generateUuid().toString(); + const std::string saltedPw = blossomIO.input.get("password").getString() + salt; + Kitsunemimi::generate_SHA_256(pwHash, saltedPw); + + // convert values + Kitsunemimi::JsonItem userData; + userData.insert("id", newUserId); + userData.insert("name", blossomIO.input.get("name").getString()); + userData.insert("projects", new Kitsunemimi::DataArray()); + userData.insert("pw_hash", pwHash); + userData.insert("is_admin", blossomIO.input.get("is_admin").getBool()); + userData.insert("creator_id", creatorId); + userData.insert("salt", salt); + + // add new user to table + if(MisakiRoot::usersTable->addUser(userData, error) == false) + { + status.errorMessage = error.toString(); + status.statusCode = Kitsunemimi::Hanami::INTERNAL_SERVER_ERROR_RTYPE; + return false; + } + + // get new created user from database + if(MisakiRoot::usersTable->getUser(blossomIO.output, + newUserId, + error, + false) == false) + { + status.statusCode = Kitsunemimi::Hanami::INTERNAL_SERVER_ERROR_RTYPE; + return false; + } + + return true; +} diff --git a/src/components/MisakiGuard/src/api/v1/user/create_user.h b/src/components/MisakiGuard/src/api/v1/user/create_user.h new file mode 100644 index 00000000..1c9b3c34 --- /dev/null +++ b/src/components/MisakiGuard/src/api/v1/user/create_user.h @@ -0,0 +1,41 @@ +/** + * @file create_user.h + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2022 Tobias Anker + * + * 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. + */ + +#ifndef MISAKIGUARD_CREATEUSER_H +#define MISAKIGUARD_CREATEUSER_H + +#include + +class CreateUser + : public Kitsunemimi::Hanami::Blossom +{ +public: + CreateUser(); + +protected: + bool runTask(Kitsunemimi::Hanami::BlossomIO &blossomIO, + const Kitsunemimi::DataMap &context, + Kitsunemimi::Hanami::BlossomStatus &status, + Kitsunemimi::ErrorContainer &error); +}; + +#endif // MISAKIGUARD_CREATEUSER_H diff --git a/src/components/MisakiGuard/src/api/v1/user/delete_user.cpp b/src/components/MisakiGuard/src/api/v1/user/delete_user.cpp new file mode 100644 index 00000000..0200adee --- /dev/null +++ b/src/components/MisakiGuard/src/api/v1/user/delete_user.cpp @@ -0,0 +1,106 @@ +/** + * @file delete_user.cpp + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2022 Tobias Anker + * + * 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 "delete_user.h" + +#include + +#include + +#include +#include + +using namespace Kitsunemimi::Hanami; + +/** + * @brief constructor + */ +DeleteUser::DeleteUser() + : Blossom("Delete a specific user from the database.") +{ + //---------------------------------------------------------------------------------------------- + // input + //---------------------------------------------------------------------------------------------- + + registerInputField("id", + SAKURA_STRING_TYPE, + true, + "ID of the user."); + // column in database is limited to 256 characters size + assert(addFieldBorder("id", 4, 256)); + assert(addFieldRegex("id", ID_EXT_REGEX)); + + //---------------------------------------------------------------------------------------------- + // + //---------------------------------------------------------------------------------------------- +} + +/** + * @brief runTask + */ +bool +DeleteUser::runTask(BlossomIO &blossomIO, + const Kitsunemimi::DataMap &context, + BlossomStatus &status, + Kitsunemimi::ErrorContainer &error) +{ + // check if admin + if(context.getBoolByKey("is_admin") == false) + { + status.statusCode = Kitsunemimi::Hanami::UNAUTHORIZED_RTYPE; + return false; + } + + // get information from request + const std::string deleterId = context.getStringByKey("id"); + const std::string userId = blossomIO.input.get("id").getString(); + + // check if user exist within the table + Kitsunemimi::JsonItem result; + if(MisakiRoot::usersTable->getUser(result, userId, error, false) == false) + { + status.errorMessage = "User with id '" + userId + "' not found."; + status.statusCode = Kitsunemimi::Hanami::NOT_FOUND_RTYPE; + error.addMeesage(status.errorMessage); + return false; + } + + // prevent user from deleting himself + if(result.get("id").getString() == deleterId) + { + status.errorMessage = "User with id '" + + userId + + "' tries to delete himself, which is not allowed."; + status.statusCode = Kitsunemimi::Hanami::BAD_REQUEST_RTYPE; + error.addMeesage(status.errorMessage); + return false; + } + + // get data from table + if(MisakiRoot::usersTable->deleteUser(userId, error) == false) + { + status.statusCode = Kitsunemimi::Hanami::INTERNAL_SERVER_ERROR_RTYPE; + return false; + } + + return true; +} diff --git a/src/components/MisakiGuard/src/api/v1/user/delete_user.h b/src/components/MisakiGuard/src/api/v1/user/delete_user.h new file mode 100644 index 00000000..dcc11ac6 --- /dev/null +++ b/src/components/MisakiGuard/src/api/v1/user/delete_user.h @@ -0,0 +1,41 @@ +/** + * @file delete_user.h + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2022 Tobias Anker + * + * 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. + */ + +#ifndef MISAKIGUARD_DELETEUSER_H +#define MISAKIGUARD_DELETEUSER_H + +#include + +class DeleteUser + : public Kitsunemimi::Hanami::Blossom +{ +public: + DeleteUser(); + +protected: + bool runTask(Kitsunemimi::Hanami::BlossomIO &blossomIO, + const Kitsunemimi::DataMap &context, + Kitsunemimi::Hanami::BlossomStatus &status, + Kitsunemimi::ErrorContainer &error); +}; + +#endif // MISAKIGUARD_DELETEUSER_H diff --git a/src/components/MisakiGuard/src/api/v1/user/get_user.cpp b/src/components/MisakiGuard/src/api/v1/user/get_user.cpp new file mode 100644 index 00000000..fa5dc0e7 --- /dev/null +++ b/src/components/MisakiGuard/src/api/v1/user/get_user.cpp @@ -0,0 +1,105 @@ +/** + * @file get_user.cpp + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2022 Tobias Anker + * + * 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 "get_user.h" + +#include + +#include + +#include +#include + +using namespace Kitsunemimi::Hanami; + +/** + * @brief constructor + */ +GetUser::GetUser() + : Blossom("Show information of a specific user.") +{ + //---------------------------------------------------------------------------------------------- + // input + //---------------------------------------------------------------------------------------------- + + registerInputField("id", + SAKURA_STRING_TYPE, + true, + "Id of the user."); + assert(addFieldBorder("id", 4, 256)); + assert(addFieldRegex("id", ID_EXT_REGEX)); + + //---------------------------------------------------------------------------------------------- + // output + //---------------------------------------------------------------------------------------------- + + registerOutputField("id", + SAKURA_STRING_TYPE, + "ID of the user."); + registerOutputField("name", + SAKURA_STRING_TYPE, + "Name of the user."); + registerOutputField("creator_id", + SAKURA_STRING_TYPE, + "Id of the creator of the user."); + registerOutputField("is_admin", + SAKURA_BOOL_TYPE, + "Set this to true to register the new user as admin."); + registerOutputField("projects", + SAKURA_ARRAY_TYPE, + "Json-array with all assigned projects " + "together with role and project-admin-status."); + + //---------------------------------------------------------------------------------------------- + // + //---------------------------------------------------------------------------------------------- +} + +/** + * @brief runTask + */ +bool +GetUser::runTask(BlossomIO &blossomIO, + const Kitsunemimi::DataMap &context, + BlossomStatus &status, + Kitsunemimi::ErrorContainer &error) +{ + // check if admin + if(context.getBoolByKey("is_admin") == false) + { + status.statusCode = Kitsunemimi::Hanami::UNAUTHORIZED_RTYPE; + return false; + } + + // get information from request + const std::string userId = blossomIO.input.get("id").getString(); + + // get data from table + if(MisakiRoot::usersTable->getUser(blossomIO.output, userId, error, false) == false) + { + status.errorMessage = "User with id '" + userId + "' not found."; + status.statusCode = Kitsunemimi::Hanami::NOT_FOUND_RTYPE; + return false; + } + + return true; +} diff --git a/src/components/MisakiGuard/src/api/v1/user/get_user.h b/src/components/MisakiGuard/src/api/v1/user/get_user.h new file mode 100644 index 00000000..d6acd002 --- /dev/null +++ b/src/components/MisakiGuard/src/api/v1/user/get_user.h @@ -0,0 +1,41 @@ +/** + * @file get_user.h + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2022 Tobias Anker + * + * 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. + */ + +#ifndef MISAKIGUARD_GETUSER_H +#define MISAKIGUARD_GETUSER_H + +#include + +class GetUser + : public Kitsunemimi::Hanami::Blossom +{ +public: + GetUser(); + +protected: + bool runTask(Kitsunemimi::Hanami::BlossomIO &blossomIO, + const Kitsunemimi::DataMap &context, + Kitsunemimi::Hanami::BlossomStatus &status, + Kitsunemimi::ErrorContainer &error); +}; + +#endif // MISAKIGUARD_GETUSER_H diff --git a/src/components/MisakiGuard/src/api/v1/user/list_users.cpp b/src/components/MisakiGuard/src/api/v1/user/list_users.cpp new file mode 100644 index 00000000..80f6db4a --- /dev/null +++ b/src/components/MisakiGuard/src/api/v1/user/list_users.cpp @@ -0,0 +1,86 @@ +/** + * @file list_users.cpp + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2022 Tobias Anker + * + * 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 "list_users.h" + +#include + +#include + +using namespace Kitsunemimi::Hanami; + +/** + * @brief constructor + */ +ListUsers::ListUsers() + : Blossom("Get information of all registered users.") +{ + //---------------------------------------------------------------------------------------------- + // output + //---------------------------------------------------------------------------------------------- + + registerOutputField("header", + SAKURA_ARRAY_TYPE, + "Array with the namings all columns of the table."); + assert(addFieldMatch("header", new Kitsunemimi::DataValue("[\"id\"," + "\"name\"," + "\"creator_id\"," + "\"projects\"," + "\"is_admin\"]"))); + registerOutputField("body", + SAKURA_ARRAY_TYPE, + "Array with all rows of the table, which array arrays too."); + + //---------------------------------------------------------------------------------------------- + // + //---------------------------------------------------------------------------------------------- +} + +/** + * @brief runTask + */ +bool +ListUsers::runTask(BlossomIO &blossomIO, + const Kitsunemimi::DataMap &context, + BlossomStatus &status, + Kitsunemimi::ErrorContainer &error) +{ + // check if admin + if(context.getBoolByKey("is_admin") == false) + { + status.statusCode = Kitsunemimi::Hanami::UNAUTHORIZED_RTYPE; + return false; + } + + // get data from table + Kitsunemimi::TableItem table; + if(MisakiRoot::usersTable->getAllUser(table, error) == false) + { + status.statusCode = Kitsunemimi::Hanami::INTERNAL_SERVER_ERROR_RTYPE; + return false; + } + + blossomIO.output.insert("header", table.getInnerHeader()); + blossomIO.output.insert("body", table.getBody()); + + return true; +} diff --git a/src/components/MisakiGuard/src/api/v1/user/list_users.h b/src/components/MisakiGuard/src/api/v1/user/list_users.h new file mode 100644 index 00000000..515d84cb --- /dev/null +++ b/src/components/MisakiGuard/src/api/v1/user/list_users.h @@ -0,0 +1,41 @@ +/** + * @file list_users.h + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2022 Tobias Anker + * + * 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. + */ + +#ifndef MISAKIGUARD_LISTUSERS_H +#define MISAKIGUARD_LISTUSERS_H + +#include + +class ListUsers + : public Kitsunemimi::Hanami::Blossom +{ +public: + ListUsers(); + +protected: + bool runTask(Kitsunemimi::Hanami::BlossomIO &blossomIO, + const Kitsunemimi::DataMap &context, + Kitsunemimi::Hanami::BlossomStatus &status, + Kitsunemimi::ErrorContainer &error); +}; + +#endif // MISAKIGUARD_LISTUSERS_H diff --git a/src/components/MisakiGuard/src/api/v1/user/remove_project_from_user.cpp b/src/components/MisakiGuard/src/api/v1/user/remove_project_from_user.cpp new file mode 100644 index 00000000..6c9f221a --- /dev/null +++ b/src/components/MisakiGuard/src/api/v1/user/remove_project_from_user.cpp @@ -0,0 +1,165 @@ +/** + * @file remove_project_from_user.cpp + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2022 Tobias Anker + * + * 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 "remove_project_from_user.h" + +#include +#include +#include +#include + +#include +#include +#include + +using namespace Kitsunemimi::Hanami; + +/** + * @brief constructor + */ +RemoveProjectFromUser::RemoveProjectFromUser() + : Blossom("Remove a project from a specific user") +{ + //---------------------------------------------------------------------------------------------- + // input + //---------------------------------------------------------------------------------------------- + + registerInputField("id", + SAKURA_STRING_TYPE, + true, + "ID of the user."); + assert(addFieldBorder("id", 4, 256)); + assert(addFieldRegex("id", ID_EXT_REGEX)); + + registerInputField("project_id", + SAKURA_STRING_TYPE, + true, + "ID of the project, which has to be removed from the user."); + // column in database is limited to 256 characters size + assert(addFieldBorder("project_id", 4, 256)); + assert(addFieldRegex("project_id", ID_REGEX)); + + //---------------------------------------------------------------------------------------------- + // output + //---------------------------------------------------------------------------------------------- + + registerOutputField("id", + SAKURA_STRING_TYPE, + "ID of the user."); + registerOutputField("name", + SAKURA_STRING_TYPE, + "Name of the user."); + registerOutputField("is_admin", + SAKURA_BOOL_TYPE, + "True, if user is an admin."); + registerOutputField("creator_id", + SAKURA_STRING_TYPE, + "Id of the creator of the user."); + registerOutputField("projects", + SAKURA_ARRAY_TYPE, + "Json-array with all assigned projects " + "together with role and project-admin-status."); + + //---------------------------------------------------------------------------------------------- + // + //---------------------------------------------------------------------------------------------- +} + +/** + * @brief runTask + */ +bool +RemoveProjectFromUser::runTask(BlossomIO &blossomIO, + const Kitsunemimi::DataMap &context, + BlossomStatus &status, + Kitsunemimi::ErrorContainer &error) +{ + // check if admin + if(context.getBoolByKey("is_admin") == false) + { + status.statusCode = Kitsunemimi::Hanami::UNAUTHORIZED_RTYPE; + return false; + } + + const std::string userId = blossomIO.input.get("id").getString(); + const std::string projectId = blossomIO.input.get("project_id").getString(); + const std::string creatorId = context.getStringByKey("id"); + + // check if user already exist within the table + Kitsunemimi::JsonItem getResult; + if(MisakiRoot::usersTable->getUser(getResult, userId, error, false) == false) + { + status.errorMessage = "User with id '" + userId + "' not found."; + status.statusCode = Kitsunemimi::Hanami::NOT_FOUND_RTYPE; + return false; + } + + // check if project is assigned to user and remove it if found + bool found = false; + Kitsunemimi::JsonItem parsedProjects = getResult.get("projects"); + for(uint64_t i = 0; i < parsedProjects.size(); i++) + { + if(parsedProjects.get(i).get("project_id").getString() == projectId) + { + parsedProjects.remove(i); + found = true; + break; + } + } + + // handle error that project is not assigned to user + if(found == false) + { + status.errorMessage = "Project with ID '" + + projectId + + "' is not assigned to user with id '" + + userId + + "' and so it can not be removed from the user."; + error.addMeesage(status.errorMessage); + status.statusCode = Kitsunemimi::Hanami::NOT_FOUND_RTYPE; + return false; + } + + // updated projects of user in database + if(MisakiRoot::usersTable->updateProjectsOfUser(userId, + parsedProjects.toString(), + error) == false) + { + error.addMeesage("Failed to update projects of user with id '" + + userId + + "'."); + status.statusCode = Kitsunemimi::Hanami::INTERNAL_SERVER_ERROR_RTYPE; + return false; + } + + // get new created user from database + if(MisakiRoot::usersTable->getUser(blossomIO.output, + userId, + error, + false) == false) + { + status.statusCode = Kitsunemimi::Hanami::INTERNAL_SERVER_ERROR_RTYPE; + return false; + } + + return true; +} diff --git a/src/components/MisakiGuard/src/api/v1/user/remove_project_from_user.h b/src/components/MisakiGuard/src/api/v1/user/remove_project_from_user.h new file mode 100644 index 00000000..b9d6da41 --- /dev/null +++ b/src/components/MisakiGuard/src/api/v1/user/remove_project_from_user.h @@ -0,0 +1,41 @@ +/** + * @file remove_project_from_user.h + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2022 Tobias Anker + * + * 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. + */ + +#ifndef MISAKIGUARD_REMOVE_PROJECT_FROM_USER_H +#define MISAKIGUARD_REMOVE_PROJECT_FROM_USER_H + +#include + +class RemoveProjectFromUser + : public Kitsunemimi::Hanami::Blossom +{ +public: + RemoveProjectFromUser(); + +protected: + bool runTask(Kitsunemimi::Hanami::BlossomIO &blossomIO, + const Kitsunemimi::DataMap &context, + Kitsunemimi::Hanami::BlossomStatus &status, + Kitsunemimi::ErrorContainer &error); +}; + +#endif // MISAKIGUARD_REMOVE_PROJECT_FROM_USER_H diff --git a/src/components/MisakiGuard/src/args.h b/src/components/MisakiGuard/src/args.h new file mode 100644 index 00000000..b4409c7e --- /dev/null +++ b/src/components/MisakiGuard/src/args.h @@ -0,0 +1,48 @@ +/** + * @file args.h + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2022 Tobias Anker + * + * 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. + */ + +#ifndef MISAKIGUARD_ARGS_H +#define MISAKIGUARD_ARGS_H + +#include +#include +#include + +/** + * @brief register cli-arguments + * + * @param argparser reference to argument parser + * + * @return true if successful, else false + */ +bool +registerArguments(Kitsunemimi::ArgParser* argparser, + Kitsunemimi::ErrorContainer &error) +{ + if(Kitsunemimi::Hanami::registerArguments(*argparser, error) == false) { + return false; + } + + return true; +} + +#endif // MISAKIGUARD_ARGS_H diff --git a/src/components/MisakiGuard/src/callbacks.h b/src/components/MisakiGuard/src/callbacks.h new file mode 100644 index 00000000..f2c8f733 --- /dev/null +++ b/src/components/MisakiGuard/src/callbacks.h @@ -0,0 +1,46 @@ +/** + * @file callbacks.h + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2022 Tobias Anker + * + * 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. + */ + +#ifndef MISAKIGUARD_CALLBACKS_H +#define MISAKIGUARD_CALLBACKS_H + +#include +#include + +void streamDataCallback(void*, + Kitsunemimi::Sakura::Session*, + const void*, + const uint64_t) +{ + +} + +void genericCallback(Kitsunemimi::Sakura::Session*, + const uint32_t, + void*, + const uint64_t, + const uint64_t) +{ + +} + +#endif // MISAKIGUARD_CALLBACKS_H diff --git a/src/components/MisakiGuard/src/config.h b/src/components/MisakiGuard/src/config.h new file mode 100644 index 00000000..8baf31b0 --- /dev/null +++ b/src/components/MisakiGuard/src/config.h @@ -0,0 +1,43 @@ +/** + * @file config.h + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2022 Tobias Anker + * + * 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. + */ + +#ifndef MISAKIGUARD_CONFIG_H +#define MISAKIGUARD_CONFIG_H + +#include +#include +#include + +/** + * @brief register configs + */ +void +registerConfigs(Kitsunemimi::ErrorContainer &error) +{ + Kitsunemimi::Hanami::registerBasicConfigs(error); + + REGISTER_STRING_CONFIG("misaki", "token_key_path", error, "", true); + REGISTER_STRING_CONFIG("misaki", "policies", error, "", true); + +} + +#endif // MISAKIGUARD_CONFIG_H diff --git a/src/components/MisakiGuard/src/database/projects_table.cpp b/src/components/MisakiGuard/src/database/projects_table.cpp new file mode 100644 index 00000000..25db30eb --- /dev/null +++ b/src/components/MisakiGuard/src/database/projects_table.cpp @@ -0,0 +1,144 @@ +/** + * @file projects_table.cpp + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2022 Tobias Anker + * + * 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 + +#include +#include +#include +#include + +#include + +/** + * @brief constructor + */ +ProjectsTable::ProjectsTable(Kitsunemimi::Sakura::SqlDatabase* db) + : HanamiSqlAdminTable(db) +{ + m_tableName = "projects"; +} + +/** + * @brief destructor + */ +ProjectsTable::~ProjectsTable() {} + +/** + * @brief add a new project to the database + * + * @param userData json-item with all information of the project to add to database + * @param error reference for error-output + * + * @return true, if successful, else false + */ +bool +ProjectsTable::addProject(Kitsunemimi::JsonItem &userData, + Kitsunemimi::ErrorContainer &error) +{ + if(insertToDb(userData, error) == false) + { + error.addMeesage("Failed to add user to database"); + return false; + } + + return true; +} + +/** + * @brief get a project from the database by its id + * + * @param result reference for the result-output in case that a project with this name was found + * @param projectId id of the requested project + * @param error reference for error-output + * @param showHiddenValues set to true to also show as hidden marked fields + * + * @return true, if successful, else false + */ +bool +ProjectsTable::getProject(Kitsunemimi::JsonItem &result, + const std::string &projectId, + Kitsunemimi::ErrorContainer &error, + const bool showHiddenValues) +{ + std::vector conditions; + conditions.emplace_back("id", projectId); + + if(getFromDb(result, conditions, error, showHiddenValues) == false) + { + error.addMeesage("Failed to get user with id '" + + projectId + + "' from database"); + LOG_ERROR(error); + return false; + } + + return true; +} + +/** + * @brief get all projects from the database table + * + * @param result reference for the result-output + * @param error reference for error-output + * + * @return true, if successful, else false + */ +bool +ProjectsTable::getAllProjects(Kitsunemimi::TableItem &result, + Kitsunemimi::ErrorContainer &error) +{ + std::vector conditions; + if(getFromDb(result, conditions, error, false) == false) + { + error.addMeesage("Failed to get all users from database"); + return false; + } + + return true; +} + +/** + * @brief delete a project from the table + * + * @param projectId id of the project to delete + * @param error reference for error-output + * + * @return true, if successful, else false + */ +bool +ProjectsTable::deleteProject(const std::string &projectId, + Kitsunemimi::ErrorContainer &error) +{ + std::vector conditions; + conditions.emplace_back("id", projectId); + + if(deleteFromDb(conditions, error) == false) + { + error.addMeesage("Failed to delete user with id '" + + projectId + + "' from database"); + return false; + } + + return true; +} diff --git a/src/components/MisakiGuard/src/database/projects_table.h b/src/components/MisakiGuard/src/database/projects_table.h new file mode 100644 index 00000000..2162b6af --- /dev/null +++ b/src/components/MisakiGuard/src/database/projects_table.h @@ -0,0 +1,53 @@ +/** + * @file projects_table.h + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2022 Tobias Anker + * + * 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. + */ + +#ifndef MISAKIGUARD_PROJECTS_TABLE_H +#define MISAKIGUARD_PROJECTS_TABLE_H + +#include +#include + +namespace Kitsunemimi { +namespace Json { +class JsonItem; +} +} +class ProjectsTable + : public Kitsunemimi::Hanami::HanamiSqlAdminTable +{ +public: + ProjectsTable(Kitsunemimi::Sakura::SqlDatabase* db); + ~ProjectsTable(); + + bool addProject(Kitsunemimi::JsonItem &userData, + Kitsunemimi::ErrorContainer &error); + bool getProject(Kitsunemimi::JsonItem &result, + const std::string &projectName, + Kitsunemimi::ErrorContainer &error, + const bool showHiddenValues = false); + bool getAllProjects(Kitsunemimi::TableItem &result, + Kitsunemimi::ErrorContainer &error); + bool deleteProject(const std::string &projectName, + Kitsunemimi::ErrorContainer &error); +}; + +#endif // MISAKIGUARD_PROJECTS_TABLE_H diff --git a/src/components/MisakiGuard/src/database/users_table.cpp b/src/components/MisakiGuard/src/database/users_table.cpp new file mode 100644 index 00000000..cc92a08c --- /dev/null +++ b/src/components/MisakiGuard/src/database/users_table.cpp @@ -0,0 +1,316 @@ +/** + * @file users_database.cpp + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2022 Tobias Anker + * + * 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 + +#include +#include +#include +#include + +#include + +/** + * @brief constructor + */ +UsersTable::UsersTable(Kitsunemimi::Sakura::SqlDatabase* db) + : HanamiSqlAdminTable(db) +{ + m_tableName = "users"; + + DbHeaderEntry projects; + projects.name = "projects"; + m_tableHeader.push_back(projects); + + DbHeaderEntry isAdmin; + isAdmin.name = "is_admin"; + isAdmin.type = BOOL_TYPE; + m_tableHeader.push_back(isAdmin); + + DbHeaderEntry pwHash; + pwHash.name = "pw_hash"; + pwHash.maxLength = 64; + pwHash.hide = true; + m_tableHeader.push_back(pwHash); + + DbHeaderEntry saltVal; + saltVal.name = "salt"; + saltVal.maxLength = 64; + saltVal.hide = true; + m_tableHeader.push_back(saltVal); +} + +/** + * @brief destructor + */ +UsersTable::~UsersTable() {} + +/** + * @brief get content of an environment-variable + * + * @param content reference for output + * @param key name of the environment-variable + * + * @return false, if varibale is not set, else true + */ +bool +UsersTable::getEnvVar(std::string &content, + const std::string &key) const +{ + const char* val = getenv(key.c_str()); + if(val == NULL) { + return false; + } + + content = std::string(val); + return true; +} + +/** + * @brief get list of all users, who have admin-status + * + * @param error reference for error-output + * + * @return true, if seccuessful, else false + */ +bool +UsersTable::getAllAdminUser(Kitsunemimi::ErrorContainer &error) +{ + std::vector conditions; + conditions.emplace_back("is_admin", "true"); + + // get admin-user from db + Kitsunemimi::JsonItem users; + if(getFromDb(users, conditions, error, false) == false) + { + error.addMeesage("Failed to get admin-users from database"); + LOG_ERROR(error); + return false; + } + + return true; +} + +/** + * @brief try to initialize a new admin-user in database + * + * @param error reference for error-output + * + * @return true, if seccuessful, else false + */ +bool +UsersTable::initNewAdminUser(Kitsunemimi::ErrorContainer &error) +{ + std::string userId = ""; + std::string userName = ""; + std::string pw = ""; + + // check if there is already an admin-user in the databasae + if(getAllAdminUser(error)) { + return true; + } + LOG_DEBUG("Found no admin-users in database, so try to create a new one"); + + // get env with new admin-user id + if(getEnvVar(userId, "HANAMI_ADMIN_USER_ID") == false) + { + error.addMeesage("environment variable 'HANAMI_ADMIN_USER_ID' was not set, " + "but is required to initialize a new admin-user"); + LOG_ERROR(error); + return false; + } + + // get env with new admin-user name + if(getEnvVar(userName, "HANAMI_ADMIN_USER_NAME") == false) + { + error.addMeesage("environment variable 'HANAMI_ADMIN_USER_NAME' was not set, " + "but is required to initialize a new admin-user"); + LOG_ERROR(error); + return false; + } + + // get env with new admin-user password + if(getEnvVar(pw, "HANAMI_ADMIN_PASSWORD") == false) + { + error.addMeesage("environment variable 'HANAMI_ADMIN_PASSWORD' was not set, " + "but is required to initialize a new admin-user"); + LOG_ERROR(error); + return false; + } + + // generate hash from password + std::string pwHash; + const std::string salt = "e307bee0-9286-49bd-9273-6f644c12da1d"; + const std::string saltedPw = pw + salt; + Kitsunemimi::generate_SHA_256(pwHash, saltedPw); + + Kitsunemimi::JsonItem userData; + userData.insert("id", userId); + userData.insert("name", userName); + userData.insert("projects", "[]"); + userData.insert("is_admin", true); + userData.insert("creator_id", "MISAKI"); + userData.insert("pw_hash", pwHash); + userData.insert("salt", salt); + + // add new admin-user to db + if(addUser(userData, error) == false) + { + error.addMeesage("Failed to add new initial admin-user to database"); + LOG_ERROR(error); + return false; + } + + return true; +} + +/** + * @brief add a new user to the database + * + * @param userData json-item with all information of the user to add to database + * @param error reference for error-output + * + * @return true, if successful, else false + */ +bool +UsersTable::addUser(Kitsunemimi::JsonItem &userData, + Kitsunemimi::ErrorContainer &error) +{ + if(insertToDb(userData, error) == false) + { + error.addMeesage("Failed to add user to database"); + return false; + } + + return true; +} + +/** + * @brief get a user from the database + * + * @param result reference for the result-output in case that a user with this name was found + * @param userId id of the requested user + * @param error reference for error-output + * @param showHiddenValues set to true to also show as hidden marked fields + * + * @return true, if successful, else false + */ +bool +UsersTable::getUser(Kitsunemimi::JsonItem &result, + const std::string &userId, + Kitsunemimi::ErrorContainer &error, + const bool showHiddenValues) +{ + std::vector conditions; + conditions.emplace_back("id", userId); + + // get user from db + if(getFromDb(result, conditions, error, showHiddenValues) == false) + { + error.addMeesage("Failed to get user with id '" + + userId + + "' from database"); + LOG_ERROR(error); + return false; + } + + return true; +} + +/** + * @brief get all users from the database table + * + * @param result reference for the result-output + * @param error reference for error-output + * + * @return true, if successful, else false + */ +bool +UsersTable::getAllUser(Kitsunemimi::TableItem &result, + Kitsunemimi::ErrorContainer &error) +{ + std::vector conditions; + if(getFromDb(result, conditions, error, false) == false) + { + error.addMeesage("Failed to get all users from database"); + return false; + } + + return true; +} + +/** + * @brief delete a user from the table + * + * @param userId id of the user to delete + * @param error reference for error-output + * + * @return true, if successful, else false + */ +bool +UsersTable::deleteUser(const std::string &userId, + Kitsunemimi::ErrorContainer &error) +{ + std::vector conditions; + conditions.emplace_back("id", userId); + + if(deleteFromDb(conditions, error) == false) + { + error.addMeesage("Failed to delete user with id '" + + userId + + "' from database"); + return false; + } + + return true; +} + +/** + * @brief update the projects-frild of a specific user + * + * @param userId id of the user, who has to be updated + * @param newProjects new projects-entry for the database + * @param error reference for error-output + * + * @return true, if successful, else false + */ +bool +UsersTable::updateProjectsOfUser(const std::string &userId, + const std::string &newProjects, + Kitsunemimi::ErrorContainer &error) +{ + Kitsunemimi::JsonItem newValues; + newValues.insert("projects", Kitsunemimi::JsonItem(newProjects)); + + std::vector conditions; + conditions.emplace_back("id", userId); + + if(updateInDb(conditions, newValues, error) == false) + { + error.addMeesage("Failed to update projects for user with id '" + + userId + + "' from database"); + return false; + } + + return true; +} diff --git a/src/components/MisakiGuard/src/database/users_table.h b/src/components/MisakiGuard/src/database/users_table.h new file mode 100644 index 00000000..ccb00310 --- /dev/null +++ b/src/components/MisakiGuard/src/database/users_table.h @@ -0,0 +1,61 @@ +/** + * @file users_database.h + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2022 Tobias Anker + * + * 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. + */ + +#ifndef MISAKIGUARD_USERS_TABLE_H +#define MISAKIGUARD_USERS_TABLE_H + +#include +#include + +namespace Kitsunemimi { +class JsonItem; +} +class UsersTable + : public Kitsunemimi::Hanami::HanamiSqlAdminTable +{ +public: + UsersTable(Kitsunemimi::Sakura::SqlDatabase* db); + ~UsersTable(); + + bool initNewAdminUser(Kitsunemimi::ErrorContainer &error); + + bool addUser(Kitsunemimi::JsonItem &userData, + Kitsunemimi::ErrorContainer &error); + bool getUser(Kitsunemimi::JsonItem &result, + const std::string &userId, + Kitsunemimi::ErrorContainer &error, + const bool showHiddenValues); + bool getAllUser(Kitsunemimi::TableItem &result, + Kitsunemimi::ErrorContainer &error); + bool deleteUser(const std::string &userId, + Kitsunemimi::ErrorContainer &error); + bool updateProjectsOfUser(const std::string &userId, + const std::string &newProjects, + Kitsunemimi::ErrorContainer &error); + +private: + bool getEnvVar(std::string &content, const std::string &key) const; + + bool getAllAdminUser(Kitsunemimi::ErrorContainer &error); +}; + +#endif // MISAKIGUARD_USERS_TABLE_H diff --git a/src/components/MisakiGuard/src/main.cpp b/src/components/MisakiGuard/src/main.cpp new file mode 100644 index 00000000..b029c183 --- /dev/null +++ b/src/components/MisakiGuard/src/main.cpp @@ -0,0 +1,80 @@ +/** + * @file main.cpp + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2022 Tobias Anker + * + * 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 +#include +#include +#include +#include +#include + +#include + +#include +#include + +#include +#include + +using Kitsunemimi::Hanami::HanamiMessaging; +using Kitsunemimi::Hanami::initMain; + +int main(int argc, char *argv[]) +{ + Kitsunemimi::ErrorContainer error; + if(initMain(argc, argv, "misaki", ®isterArguments, ®isterConfigs, error) == false) + { + LOG_ERROR(error); + return 1; + } + + // init included components + Azuki::initAzukiBlossoms(); + Misaki::initMisakiBlossoms(); + + // initialize server and connections based on the config-file + const std::vector groupNames = {"kyouko", "azuki", "shiori"}; + if(HanamiMessaging::getInstance()->initialize("misaki", + groupNames, + nullptr, + &streamDataCallback, + &genericCallback, + error, + true) == false) + { + LOG_ERROR(error); + return 1; + } + + // create root-object to start all remaining functions + MisakiRoot rootObj; + if(rootObj.init(error) == false) + { + LOG_ERROR(error); + return 1; + } + + // sleep forever + std::this_thread::sleep_until(std::chrono::time_point::max()); + + return 0; +} diff --git a/src/components/MisakiGuard/src/misaki_root.cpp b/src/components/MisakiGuard/src/misaki_root.cpp new file mode 100644 index 00000000..6d917ace --- /dev/null +++ b/src/components/MisakiGuard/src/misaki_root.cpp @@ -0,0 +1,198 @@ +/** + * @file misaki_root.cpp + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2022 Tobias Anker + * + * 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 "misaki_root.h" + +#include +#include +#include + +#include + +Kitsunemimi::Jwt* MisakiRoot::jwt = nullptr; +UsersTable* MisakiRoot::usersTable = nullptr; +ProjectsTable* MisakiRoot::projectsTable = nullptr; +Kitsunemimi::Sakura::SqlDatabase* MisakiRoot::database = nullptr; +Kitsunemimi::Hanami::Policy* MisakiRoot::policies = nullptr; + +/** + * @brief constructor + */ +MisakiRoot::MisakiRoot() {} + +/** + * @brief init root-object + * + * @param error reference for error-output + * + * @return true, if successful, else false + */ +bool +MisakiRoot::init(Kitsunemimi::ErrorContainer &error) +{ + if(initDatabase(error) == false) + { + error.addMeesage("Failed to initialize database"); + return false; + } + + initBlossoms(); + + if(initJwt(error) == false) + { + error.addMeesage("Failed to initialize jwt"); + return false; + } + + if(initPolicies(error) == false) + { + error.addMeesage("Failed to initialize policies"); + return false; + } + + return true; +} + +/** + * @brief init database + * + * @param error reference for error-output + * + * @return true, if successful, else false + */ +bool +MisakiRoot::initDatabase(Kitsunemimi::ErrorContainer &error) +{ + bool success = false; + + // read database-path from config + database = new Kitsunemimi::Sakura::SqlDatabase(); + const std::string databasePath = GET_STRING_CONFIG("DEFAULT", "database", success); + if(success == false) + { + error.addMeesage("No database-path defined in config."); + return false; + } + + // initalize database + if(database->initDatabase(databasePath, error) == false) + { + error.addMeesage("Failed to initialize sql-database."); + return false; + } + + // initialize projects-table + projectsTable = new ProjectsTable(database); + if(projectsTable->initTable(error) == false) + { + error.addMeesage("Failed to initialize project-table in database."); + return false; + } + + // initialize users-table + usersTable = new UsersTable(database); + if(usersTable->initTable(error) == false) + { + error.addMeesage("Failed to initialize user-table in database."); + return false; + } + if(usersTable->initNewAdminUser(error) == false) + { + error.addMeesage("Failed to initialize new admin-user even this is necessary."); + return false; + } + + return true; +} + +/** + * @brief init policies + * + * @param error reference for error-output + * + * @return true, if successful, else false + */ +bool +MisakiRoot::initPolicies(Kitsunemimi::ErrorContainer &error) +{ + bool success = false; + + // read policy-file-path from config + const std::string policyFilePath = GET_STRING_CONFIG("misaki", "policies", success); + if(success == false) + { + error.addMeesage("No policy-file defined in config."); + return false; + } + + // read policy-file + std::string policyFileContent; + if(Kitsunemimi::readFile(policyFileContent, policyFilePath, error) == false) + { + error.addMeesage("Failed to read policy-file"); + return false; + } + + // parse policy-file + policies = new Kitsunemimi::Hanami::Policy(); + if(policies->parse(policyFileContent, error) == false) + { + error.addMeesage("Failed to parser policy-file"); + return false; + } + + return true; +} + +/** + * @brief init jwt-class to validate incoming requested + * + * @param error reference for error-output + * + * @return true, if successful, else false + */ +bool +MisakiRoot::initJwt(Kitsunemimi::ErrorContainer &error) +{ + bool success = false; + + // read jwt-token-key from config + const std::string tokenKeyPath = GET_STRING_CONFIG("misaki", "token_key_path", success); + if(success == false) + { + error.addMeesage("No token_key_path defined in config."); + return false; + } + + std::string tokenKeyString; + if(Kitsunemimi::readFile(tokenKeyString, tokenKeyPath, error) == false) + { + error.addMeesage("Failed to read token-file '" + tokenKeyPath + "'"); + return false; + } + + // init jwt for token create and sign + CryptoPP::SecByteBlock tokenKey((unsigned char*)tokenKeyString.c_str(), tokenKeyString.size()); + jwt = new Kitsunemimi::Jwt(tokenKey); + + return true; +} diff --git a/src/components/MisakiGuard/src/misaki_root.h b/src/components/MisakiGuard/src/misaki_root.h new file mode 100644 index 00000000..0099f535 --- /dev/null +++ b/src/components/MisakiGuard/src/misaki_root.h @@ -0,0 +1,50 @@ +/** + * @file misaki_root.h + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2022 Tobias Anker + * + * 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. + */ + +#ifndef MISAKIGUARD_MISAKIROOT_H +#define MISAKIGUARD_MISAKIROOT_H + +#include +#include +#include +#include + +class MisakiRoot +{ +public: + MisakiRoot(); + + bool init(Kitsunemimi::ErrorContainer &error); + + static Kitsunemimi::Jwt* jwt; + static UsersTable* usersTable; + static ProjectsTable* projectsTable; + static Kitsunemimi::Sakura::SqlDatabase* database; + static Kitsunemimi::Hanami::Policy* policies; + +private: + bool initDatabase(Kitsunemimi::ErrorContainer &error); + bool initPolicies(Kitsunemimi::ErrorContainer &error); + bool initJwt(Kitsunemimi::ErrorContainer &error); +}; + +#endif // MISAKIGUARD_MISAKIROOT_H diff --git a/src/components/ShioriArchive/CHANGELOG_old b/src/components/ShioriArchive/CHANGELOG_old new file mode 100644 index 00000000..d180078a --- /dev/null +++ b/src/components/ShioriArchive/CHANGELOG_old @@ -0,0 +1,36 @@ +# Changelog + +## [0.3.1] - 2022-07-02 + +### Changed +- update dependencies + + +## [0.3.0] - 2022-06-28 + +### Added +- storing and handling of cluster-snaphosts from kyouko +- upload of csv-files in form of tables +- upload-progress for file-uploads to check, if file was fully transfered +- allow string as messages for dataset-upload, which was used for first tests wiht uploads over the dashboard +- audit-log + +### Changed +- reworked dataset-file-handling in order to support uploads of csv-files + + +## [0.2.0] - 2022-01-14 + +### Added +- error-log +- storage for results from kyouko + +### Changed +- use generic file-structure for data-sets and also convert uploaded files into this new format +- upload data-sets with stream-messages to avoid logging of the data and to be much faster + + +## [0.1.0] - 2022-01-09 + +### Added +- handling for train-data-sets diff --git a/src/components/ShioriArchive/README.md b/src/components/ShioriArchive/README.md new file mode 100644 index 00000000..d381456c --- /dev/null +++ b/src/components/ShioriArchive/README.md @@ -0,0 +1,15 @@ +# SagiriArchive + +## Description + +Storage-component of the Hanami-AI-Project: https://github.com/kitsudaiki/Hanami-AI + +## Author + +Tobias Anker + +eMail: tobias.anker@kitsunemimi.moe + +## License + +This project is licensed under the Apache License Version 2.0 - see the [LICENSE](LICENSE) file for details diff --git a/src/components/ShioriArchive/ShioriArchive.pro b/src/components/ShioriArchive/ShioriArchive.pro new file mode 100644 index 00000000..4f98332d --- /dev/null +++ b/src/components/ShioriArchive/ShioriArchive.pro @@ -0,0 +1,182 @@ +QT -= qt core gui + +TARGET = ShioriArchive +CONFIG += console c++17 +CONFIG -= app_bundle + + +LIBS += -L../../libraries/libShioriArchive/src -lShioriArchive +LIBS += -L../../libraries/libShioriArchive/src/debug -lShioriArchive +LIBS += -L../../libraries/libShioriArchive/src/release -lShioriArchive +INCLUDEPATH += ../../libraries/libShioriArchive/include + +LIBS += -L../../libraries/libAzukiHeart/src -lAzukiHeart +LIBS += -L../../libraries/libAzukiHeart/src/debug -lAzukiHeart +LIBS += -L../../libraries/libAzukiHeart/src/release -lAzukiHeart +INCLUDEPATH += ../../libraries/libAzukiHeart/include + +LIBS += -L../../libraries/libMisakiGuard/src -lMisakiGuard +LIBS += -L../../libraries/libMisakiGuard/src/debug -lMisakiGuard +LIBS += -L../../libraries/libMisakiGuard/src/release -lMisakiGuard +INCLUDEPATH += ../../libraries/libMisakiGuard/include + +LIBS += -L../../libraries/libKitsunemimiHanamiNetwork/src -lKitsunemimiHanamiNetwork +LIBS += -L../../libraries/libKitsunemimiHanamiNetwork/src/debug -lKitsunemimiHanamiNetwork +LIBS += -L../../libraries/libKitsunemimiHanamiNetwork/src/release -lKitsunemimiHanamiNetwork +INCLUDEPATH += ../../libraries/libKitsunemimiHanamiNetwork/include + +LIBS += -L../../libraries/libKitsunemimiHanamiDatabase/src -lKitsunemimiHanamiDatabase +LIBS += -L../../libraries/libKitsunemimiHanamiDatabase/src/debug -lKitsunemimiHanamiDatabase +LIBS += -L../../libraries/libKitsunemimiHanamiDatabase/src/release -lKitsunemimiHanamiDatabase +INCLUDEPATH += ../../libraries/libKitsunemimiHanamiDatabase/include + +LIBS += -L../../libraries/libKitsunemimiHanamiCommon/src -lKitsunemimiHanamiCommon +LIBS += -L../../libraries/libKitsunemimiHanamiCommon/src/debug -lKitsunemimiHanamiCommon +LIBS += -L../../libraries/libKitsunemimiHanamiCommon/src/release -lKitsunemimiHanamiCommon +INCLUDEPATH += ../../libraries/libKitsunemimiHanamiCommon/include + +LIBS += -L../../libraries/libKitsunemimiSakuraDatabase/src -lKitsunemimiSakuraDatabase +LIBS += -L../../libraries/libKitsunemimiSakuraDatabase/src/debug -lKitsunemimiSakuraDatabase +LIBS += -L../../libraries/libKitsunemimiSakuraDatabase/src/release -lKitsunemimiSakuraDatabase +INCLUDEPATH += ../../libraries/libKitsunemimiSakuraDatabase/include + +LIBS += -L../../libraries/libKitsunemimiArgs/src -lKitsunemimiArgs +LIBS += -L../../libraries/libKitsunemimiArgs/src/debug -lKitsunemimiArgs +LIBS += -L../../libraries/libKitsunemimiArgs/src/release -lKitsunemimiArgs +INCLUDEPATH += ../../libraries/libKitsunemimiArgs/include + +LIBS += -L../../libraries/libKitsunemimiConfig/src -lKitsunemimiConfig +LIBS += -L../../libraries/libKitsunemimiConfig/src/debug -lKitsunemimiConfig +LIBS += -L../../libraries/libKitsunemimiConfig/src/release -lKitsunemimiConfig +INCLUDEPATH += ../../libraries/libKitsunemimiConfig/include + +LIBS += -L../../libraries/libKitsunemimiSakuraNetwork/src -lKitsunemimiSakuraNetwork +LIBS += -L../../libraries/libKitsunemimiSakuraNetwork/src/debug -lKitsunemimiSakuraNetwork +LIBS += -L../../libraries/libKitsunemimiSakuraNetwork/src/release -lKitsunemimiSakuraNetwork +INCLUDEPATH += ../../libraries/libKitsunemimiSakuraNetwork/include + +LIBS += -L../../libraries/libKitsunemimiSqlite/src -lKitsunemimiSqlite +LIBS += -L../../libraries/libKitsunemimiSqlite/src/debug -lKitsunemimiSqlite +LIBS += -L../../libraries/libKitsunemimiSqlite/src/release -lKitsunemimiSqlite +INCLUDEPATH += ../../libraries/libKitsunemimiSqlite/include + +LIBS += -L../../libraries/libKitsunemimiCommon/src -lKitsunemimiCommon +LIBS += -L../../libraries/libKitsunemimiCommon/src/debug -lKitsunemimiCommon +LIBS += -L../../libraries/libKitsunemimiCommon/src/release -lKitsunemimiCommon +INCLUDEPATH += ../../libraries/libKitsunemimiCommon/include + +LIBS += -L../../libraries/libKitsunemimiNetwork/src -lKitsunemimiNetwork +LIBS += -L../../libraries/libKitsunemimiNetwork/src/debug -lKitsunemimiNetwork +LIBS += -L../../libraries/libKitsunemimiNetwork/src/release -lKitsunemimiNetwork +INCLUDEPATH += ../../libraries/libKitsunemimiNetwork/include + +LIBS += -L../../libraries/libKitsunemimiJson/src -lKitsunemimiJson +LIBS += -L../../libraries/libKitsunemimiJson/src/debug -lKitsunemimiJson +LIBS += -L../../libraries/libKitsunemimiJson/src/release -lKitsunemimiJson +INCLUDEPATH += ../../libraries/libKitsunemimiJson/include + +LIBS += -L../../libraries/libKitsunemimiIni/src -lKitsunemimiIni +LIBS += -L../../libraries/libKitsunemimiIni/src/debug -lKitsunemimiIni +LIBS += -L../../libraries/libKitsunemimiIni/src/release -lKitsunemimiIni +INCLUDEPATH += ../../libraries/libKitsunemimiIni/include + +LIBS += -L../../libraries/libKitsunemimiJwt/src -lKitsunemimiJwt +LIBS += -L../../libraries/libKitsunemimiJwt/src/debug -lKitsunemimiJwt +LIBS += -L../../libraries/libKitsunemimiJwti/src/release -lKitsunemimiJwt +INCLUDEPATH += ../../libraries/libKitsunemimiJwt/include + +LIBS += -L../../libraries/libKitsunemimiCrypto/src -lKitsunemimiCrypto +LIBS += -L../../libraries/libKitsunemimiCrypto/src/debug -lKitsunemimiCrypto +LIBS += -L../../libraries/libKitsunemimiCrypto/src/release -lKitsunemimiCrypto +INCLUDEPATH += ../../libraries/libKitsunemimiCrypto/include + +LIBS += -lcryptopp -lssl -lsqlite3 -luuid -lcrypto -pthread -lprotobuf -lpthread + +INCLUDEPATH += $$PWD \ + src + +SOURCES += src/main.cpp \ + src/api/v1/cluster_snapshot/create_cluster_snapshot.cpp \ + src/api/v1/cluster_snapshot/delete_cluster_snapshot.cpp \ + src/api/v1/cluster_snapshot/finish_cluster_snapshot.cpp \ + src/api/v1/cluster_snapshot/get_cluster_snapshot.cpp \ + src/api/v1/cluster_snapshot/list_cluster_snapshot.cpp \ + src/api/v1/data_files/check_data_set.cpp \ + src/api/v1/data_files/csv/create_csv_data_set.cpp \ + src/api/v1/data_files/csv/finalize_csv_data_set.cpp \ + src/api/v1/data_files/delete_data_set.cpp \ + src/api/v1/data_files/get_data_set.cpp \ + src/api/v1/data_files/get_progress_data_set.cpp \ + src/api/v1/data_files/list_data_set.cpp \ + src/api/v1/data_files/mnist/create_mnist_data_set.cpp \ + src/api/v1/data_files/mnist/finalize_mnist_data_set.cpp \ + src/api/v1/logs/get_audit_log.cpp \ + src/api/v1/logs/get_error_log.cpp \ + src/api/v1/request_results/delete_request_result.cpp \ + src/api/v1/request_results/get_request_result.cpp \ + src/api/v1/request_results/list_request_result.cpp \ + src/core/data_set_files/data_set_file.cpp \ + src/core/data_set_files/image_data_set_file.cpp \ + src/core/data_set_files/table_data_set_file.cpp \ + src/core/temp_file_handler.cpp \ + src/database/audit_log_table.cpp \ + src/database/cluster_snapshot_table.cpp \ + src/database/data_set_table.cpp \ + src/database/error_log_table.cpp \ + src/database/request_result_table.cpp \ + src/shiori_root.cpp + +HEADERS += \ + src/api/blossom_initializing.h \ + src/api/v1/cluster_snapshot/create_cluster_snapshot.h \ + src/api/v1/cluster_snapshot/delete_cluster_snapshot.h \ + src/api/v1/cluster_snapshot/finish_cluster_snapshot.h \ + src/api/v1/cluster_snapshot/get_cluster_snapshot.h \ + src/api/v1/cluster_snapshot/list_cluster_snapshot.h \ + src/api/v1/data_files/check_data_set.h \ + src/api/v1/data_files/csv/create_csv_data_set.h \ + src/api/v1/data_files/csv/finalize_csv_data_set.h \ + src/api/v1/data_files/delete_data_set.h \ + src/api/v1/data_files/get_data_set.h \ + src/api/v1/data_files/get_progress_data_set.h \ + src/api/v1/data_files/list_data_set.h \ + src/api/v1/data_files/mnist/create_mnist_data_set.h \ + src/api/v1/data_files/mnist/finalize_mnist_data_set.h \ + src/api/v1/logs/get_audit_log.h \ + src/api/v1/logs/get_error_log.h \ + src/api/v1/request_results/delete_request_result.h \ + src/api/v1/request_results/get_request_result.h \ + src/api/v1/request_results/list_request_result.h \ + src/args.h \ + src/callbacks.h \ + src/config.h \ + src/core/data_set_files/data_set_file.h \ + src/core/data_set_files/image_data_set_file.h \ + src/core/data_set_files/table_data_set_file.h \ + src/core/temp_file_handler.h \ + src/database/audit_log_table.h \ + src/database/cluster_snapshot_table.h \ + src/database/data_set_table.h \ + src/database/error_log_table.h \ + src/database/request_result_table.h \ + src/shiori_root.h \ + ../../libraries/libKitsunemimiHanamiMessages/hanami_messages/shiori_messages.h + +SHIORI_PROTO_BUFFER = ../../libraries/libKitsunemimiHanamiMessages/protobuffers/shiori_messages.proto3 + +OTHER_FILES += $$SHIORI_PROTO_BUFFER + +protobuf_decl.name = protobuf headers +protobuf_decl.input = SHIORI_PROTO_BUFFER +protobuf_decl.output = ${QMAKE_FILE_IN_PATH}/${QMAKE_FILE_BASE}.proto3.pb.h +protobuf_decl.commands = protoc --cpp_out=${QMAKE_FILE_IN_PATH} --proto_path=${QMAKE_FILE_IN_PATH} ${QMAKE_FILE_NAME} +protobuf_decl.variable_out = HEADERS +QMAKE_EXTRA_COMPILERS += protobuf_decl + +protobuf_impl.name = protobuf sources +protobuf_impl.input = SHIORI_PROTO_BUFFER +protobuf_impl.output = ${QMAKE_FILE_IN_PATH}/${QMAKE_FILE_BASE}.proto3.pb.cc +protobuf_impl.depends = ${QMAKE_FILE_IN_PATH}/${QMAKE_FILE_BASE}.proto3.pb.h +protobuf_impl.commands = $$escape_expand(\n) +protobuf_impl.variable_out = SOURCES +QMAKE_EXTRA_COMPILERS += protobuf_impl diff --git a/src/components/ShioriArchive/build.sh b/src/components/ShioriArchive/build.sh new file mode 100755 index 00000000..708707e2 --- /dev/null +++ b/src/components/ShioriArchive/build.sh @@ -0,0 +1,140 @@ +#!/bin/bash + +# get current directory-path and the path of the parent-directory +DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )" +PARENT_DIR="$(dirname "$DIR")" +CURRENT_BRANCH=$(git rev-parse --abbrev-ref HEAD) + +# create build-directory +BUILD_DIR="$PARENT_DIR/build" +mkdir -p $BUILD_DIR + +# create directory for the final result +RESULT_DIR="$PARENT_DIR/result" +mkdir -p $RESULT_DIR + +#----------------------------------------------------------------------------------------------------------------- + +function build_kitsune_lib_repo () { + REPO_NAME=$1 + NUMBER_OF_THREADS=$2 + ADDITIONAL_CONFIGS=$3 + + # create build directory for repo and go into this directory + REPO_DIR="$BUILD_DIR/$REPO_NAME" + mkdir -p $REPO_DIR + cd $REPO_DIR + + # build repo library with qmake + /usr/lib/x86_64-linux-gnu/qt5/bin/qmake "$PARENT_DIR/$REPO_NAME/$REPO_NAME.pro" -spec linux-g++ "CONFIG += optimize_full staticlib $ADDITIONAL_CONFIGS" + /usr/bin/make -j$NUMBER_OF_THREADS + + # copy build-result and include-files into the result-directory + echo "----------------------------------------------------------------------" + echo $RESULT_DIR + cp $REPO_DIR/src/$REPO_NAME.a $RESULT_DIR/ + cp -r $PARENT_DIR/$REPO_NAME/include $RESULT_DIR/ + ls -l $RESULT_DIR/include/ + ls -l $RESULT_DIR +} + +function download_repo_github () { + REPO_NAME=$1 + TAG_OR_BRANCH=$2 + + echo "" + echo "" + echo "++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++" + echo "$REPO_NAME" + echo "++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++" + echo "Branch/Tag: $TAG_OR_BRANCH" + + # clone repo + git clone https://github.com/kitsudaiki/$REPO_NAME.git "$PARENT_DIR/$REPO_NAME" + cd "$PARENT_DIR/$REPO_NAME" + + # checkout branch + if [[ $CURRENT_BRANCH =~ ^tag.* ]] || [[ $CURRENT_BRANCH =~ ^hotfix.* ]] || [[ $CURRENT_BRANCH =~ ^v.* ]] || [[ $CURRENT_BRANCH =~ ^rolling$ ]] || [[ $CURRENT_BRANCH =~ ^staging$ ]]; then + # if a stable branch, then use the defined tag of branch + # check if defined branch even exist + BRANCH_EXIST=$(git ls-remote --heads origin $TAG_OR_BRANCH) + if [[ -z "$BRANCH_EXIST" ]]; then + echo "" + echo "-------------------------------------------------------------------------------------" + echo "Branch or tag '$TAG_OR_BRANCH' does not exist for the repository '$REPO_NAME'" + echo "-------------------------------------------------------------------------------------" + echo "" + exit 1 + fi + git checkout $TAG_OR_BRANCH + else + # if develop or feature branch, then try to checkout the feature-branch in the other repo as well + # or otherwise use the develop-branch as default + BRANCH_EXIST=$(git ls-remote --heads origin $CURRENT_BRANCH) + if [[ -n "$BRANCH_EXIST" ]]; then + git checkout $CURRENT_BRANCH + else + git checkout develop + fi + fi +} + +function get_required_kitsune_lib_repo () { + REPO_NAME=$1 + TAG_OR_BRANCH=$2 + NUMBER_OF_THREADS=$3 + + download_repo_github $REPO_NAME $TAG_OR_BRANCH + build_kitsune_lib_repo $REPO_NAME $NUMBER_OF_THREADS +} + +#----------------------------------------------------------------------------------------------------------------- + +echo "###########################################################################################################" +echo "" +get_required_kitsune_lib_repo "libKitsunemimiCommon" "develop" 8 +get_required_kitsune_lib_repo "libKitsunemimiJson" "develop" 1 +get_required_kitsune_lib_repo "libKitsunemimiIni" "develop" 1 +get_required_kitsune_lib_repo "libKitsunemimiNetwork" "develop" 8 +get_required_kitsune_lib_repo "libKitsunemimiArgs" "develop" 8 +get_required_kitsune_lib_repo "libKitsunemimiConfig" "develop" 8 +get_required_kitsune_lib_repo "libKitsunemimiSqlite" "develop" 8 +get_required_kitsune_lib_repo "libKitsunemimiCrypto" "develop" 8 +get_required_kitsune_lib_repo "libKitsunemimiJwt" "develop" 8 +echo "" +echo "###########################################################################################################" +echo "" +get_required_kitsune_lib_repo "libKitsunemimiSakuraNetwork" "develop" 8 +get_required_kitsune_lib_repo "libKitsunemimiSakuraDatabase" "develop" 8 +echo "" +echo "###########################################################################################################" +echo "" +get_required_kitsune_lib_repo "libKitsunemimiHanamiCommon" "develop" 8 +get_required_kitsune_lib_repo "libKitsunemimiHanamiDatabase" "develop" 8 +download_repo_github "libKitsunemimiHanamiMessages" "develop" +get_required_kitsune_lib_repo "libKitsunemimiHanamiNetwork" "develop" 8 +echo "" +echo "###########################################################################################################" +echo "" +get_required_kitsune_lib_repo "libAzukiHeart" "develop" 8 +get_required_kitsune_lib_repo "libMisakiGuard" "develop" 8 +get_required_kitsune_lib_repo "libShioriArchive" "develop" 8 +echo "" +echo "###########################################################################################################" + +#----------------------------------------------------------------------------------------------------------------- + +# create build directory for ShioriArchive and go into this directory +LIB_KITSUNE_SAKURA_TREE_DIR="$BUILD_DIR/ShioriArchive" +mkdir -p $LIB_KITSUNE_SAKURA_TREE_DIR +cd $LIB_KITSUNE_SAKURA_TREE_DIR + +# build ShioriArchive with qmake +/usr/lib/x86_64-linux-gnu/qt5/bin/qmake "$PARENT_DIR/ShioriArchive/ShioriArchive.pro" -spec linux-g++ "CONFIG += optimize_full" +/usr/bin/make -j8 + +# copy build-result and include-files into the result-directory +cp "$LIB_KITSUNE_SAKURA_TREE_DIR/ShioriArchive" "$RESULT_DIR/" + +#----------------------------------------------------------------------------------------------------------------- + diff --git a/src/components/ShioriArchive/src/api/blossom_initializing.h b/src/components/ShioriArchive/src/api/blossom_initializing.h new file mode 100644 index 00000000..d76a11a6 --- /dev/null +++ b/src/components/ShioriArchive/src/api/blossom_initializing.h @@ -0,0 +1,237 @@ +/** + * @file blossom_initializing.h + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2022 Tobias Anker + * + * 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. + */ + +#ifndef SHIORIARCHIVE_BLOSSOM_INITIALIZING_H +#define SHIORIARCHIVE_BLOSSOM_INITIALIZING_H + +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include + +using Kitsunemimi::Hanami::HanamiMessaging; + +/** + * @brief init data_set blossoms + */ +void +dataSetBlossoms() +{ + HanamiMessaging* interface = HanamiMessaging::getInstance(); + const std::string group = "data_set"; + + assert(interface->addBlossom(group, "create_mnist", new CreateMnistDataSet())); + interface->addEndpoint("v1/mnist/data_set", + Kitsunemimi::Hanami::POST_TYPE, + Kitsunemimi::Hanami::BLOSSOM_TYPE, + group, + "create_mnist"); + + assert(interface->addBlossom(group, "finalize_mnist", new FinalizeMnistDataSet())); + interface->addEndpoint("v1/mnist/data_set", + Kitsunemimi::Hanami::PUT_TYPE, + Kitsunemimi::Hanami::BLOSSOM_TYPE, + group, + "finalize_mnist"); + + assert(interface->addBlossom(group, "create_csv", new CreateCsvDataSet())); + interface->addEndpoint("v1/csv/data_set", + Kitsunemimi::Hanami::POST_TYPE, + Kitsunemimi::Hanami::BLOSSOM_TYPE, + group, + "create_csv"); + + assert(interface->addBlossom(group, "finalize_csv", new FinalizeCsvDataSet())); + interface->addEndpoint("v1/csv/data_set", + Kitsunemimi::Hanami::PUT_TYPE, + Kitsunemimi::Hanami::BLOSSOM_TYPE, + group, + "finalize_csv"); + + assert(interface->addBlossom(group, "check", new CheckDataSet())); + interface->addEndpoint("v1/data_set/check", + Kitsunemimi::Hanami::POST_TYPE, + Kitsunemimi::Hanami::BLOSSOM_TYPE, + group, + "check"); + + assert(interface->addBlossom(group, "progress", new GetProgressDataSet())); + interface->addEndpoint("v1/data_set/progress", + Kitsunemimi::Hanami::GET_TYPE, + Kitsunemimi::Hanami::BLOSSOM_TYPE, + group, + "progress"); + + assert(interface->addBlossom(group, "get", new GetDataSet())); + interface->addEndpoint("v1/data_set", + Kitsunemimi::Hanami::GET_TYPE, + Kitsunemimi::Hanami::BLOSSOM_TYPE, + group, + "get"); + + assert(interface->addBlossom(group, "delete", new DeleteDataSet())); + interface->addEndpoint("v1/data_set", + Kitsunemimi::Hanami::DELETE_TYPE, + Kitsunemimi::Hanami::BLOSSOM_TYPE, + group, + "delete"); + + assert(interface->addBlossom(group, "list", new ListDataSet())); + interface->addEndpoint("v1/data_set/all", + Kitsunemimi::Hanami::GET_TYPE, + Kitsunemimi::Hanami::BLOSSOM_TYPE, + group, + "list"); +} + +/** + * @brief init cluster_snaptho blossoms + */ +void +clusterSnapshotBlossoms() +{ + HanamiMessaging* interface = HanamiMessaging::getInstance(); + const std::string group = "cluster_snapshot"; + + assert(interface->addBlossom(group, "create", new CreateClusterSnapshot())); + interface->addEndpoint("v1/cluster_snapshot", + Kitsunemimi::Hanami::POST_TYPE, + Kitsunemimi::Hanami::BLOSSOM_TYPE, + group, + "create"); + + assert(interface->addBlossom(group, "finalize", new FinalizeClusterSnapshot())); + interface->addEndpoint("v1/cluster_snapshot", + Kitsunemimi::Hanami::PUT_TYPE, + Kitsunemimi::Hanami::BLOSSOM_TYPE, + group, + "finalize"); + + assert(interface->addBlossom(group, "get", new GetClusterSnapshot())); + interface->addEndpoint("v1/cluster_snapshot", + Kitsunemimi::Hanami::GET_TYPE, + Kitsunemimi::Hanami::BLOSSOM_TYPE, + group, + "get"); + + assert(interface->addBlossom(group, "delete", new DeleteClusterSnapshot())); + interface->addEndpoint("v1/cluster_snapshot", + Kitsunemimi::Hanami::DELETE_TYPE, + Kitsunemimi::Hanami::BLOSSOM_TYPE, + group, + "delete"); + + assert(interface->addBlossom(group, "list", new ListClusterSnapshot())); + interface->addEndpoint("v1/cluster_snapshot/all", + Kitsunemimi::Hanami::GET_TYPE, + Kitsunemimi::Hanami::BLOSSOM_TYPE, + group, + "list"); +} + +/** + * @brief init request_result blossoms + */ +void +resultBlossoms() +{ + HanamiMessaging* interface = HanamiMessaging::getInstance(); + const std::string group = "request_result"; + + assert(interface->addBlossom(group, "get", new GetRequestResult())); + interface->addEndpoint("v1/request_result", + Kitsunemimi::Hanami::GET_TYPE, + Kitsunemimi::Hanami::BLOSSOM_TYPE, + group, + "get"); + + assert(interface->addBlossom(group, "list", new ListRequestResult())); + interface->addEndpoint("v1/request_result/all", + Kitsunemimi::Hanami::GET_TYPE, + Kitsunemimi::Hanami::BLOSSOM_TYPE, + group, + "list"); + + assert(interface->addBlossom(group, "delete", new DeleteRequestResult())); + interface->addEndpoint("v1/request_result", + Kitsunemimi::Hanami::DELETE_TYPE, + Kitsunemimi::Hanami::BLOSSOM_TYPE, + group, + "delete"); +} + +/** + * @brief init logs blossoms + */ +void +logsBlossoms() +{ + HanamiMessaging* interface = HanamiMessaging::getInstance(); + const std::string group = "logs"; + + assert(interface->addBlossom(group, "get_audit_log", new GetAuditLog())); + interface->addEndpoint("v1/audit_log", + Kitsunemimi::Hanami::GET_TYPE, + Kitsunemimi::Hanami::BLOSSOM_TYPE, + group, + "get_audit_log"); + + assert(interface->addBlossom(group, "get_error_log", new GetErrorLog())); + interface->addEndpoint("v1/error_log", + Kitsunemimi::Hanami::GET_TYPE, + Kitsunemimi::Hanami::BLOSSOM_TYPE, + group, + "get_error_log"); +} + +void +initBlossoms() +{ + dataSetBlossoms(); + clusterSnapshotBlossoms(); + resultBlossoms(); + logsBlossoms(); +} + +#endif // SHIORIARCHIVE_BLOSSOM_INITIALIZING_H diff --git a/src/components/ShioriArchive/src/api/v1/cluster_snapshot/create_cluster_snapshot.cpp b/src/components/ShioriArchive/src/api/v1/cluster_snapshot/create_cluster_snapshot.cpp new file mode 100644 index 00000000..e4b1fb27 --- /dev/null +++ b/src/components/ShioriArchive/src/api/v1/cluster_snapshot/create_cluster_snapshot.cpp @@ -0,0 +1,188 @@ +/** + * @file create_cluster_snapshot.cpp + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2022 Tobias Anker + * + * 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 "create_cluster_snapshot.h" + +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include + +using namespace Kitsunemimi::Hanami; + +CreateClusterSnapshot::CreateClusterSnapshot() + : Blossom("Init new snapshot of a cluster.") +{ + //---------------------------------------------------------------------------------------------- + // input + //---------------------------------------------------------------------------------------------- + + // HINT(kitsudaiki): Snapshots are created internally asynchrous by the cluster and get the same + // uuid as the task, where the snapshot was created. Because of this the uuid + // has to be predefined. + registerInputField("uuid", + SAKURA_STRING_TYPE, + true, + "UUID of the new snapshot."); + assert(addFieldRegex("uuid", UUID_REGEX)); + + registerInputField("name", + SAKURA_STRING_TYPE, + true, + "Name of the new snapshot."); + assert(addFieldBorder("name", 4, 256)); + assert(addFieldRegex("name", NAME_REGEX)); + + registerInputField("user_id", + SAKURA_STRING_TYPE, + true, + "ID of the user, who owns the snapshot."); + assert(addFieldBorder("user_id", 4, 256)); + assert(addFieldRegex("user_id", ID_EXT_REGEX)); + + registerInputField("project_id", + SAKURA_STRING_TYPE, + true, + "ID of the project, where the snapshot belongs to."); + assert(addFieldBorder("project_id", 4, 256)); + assert(addFieldRegex("project_id", ID_REGEX)); + + registerInputField("header", + SAKURA_MAP_TYPE, + true, + "Header of the file with information of the splits."); + + registerInputField("input_data_size", + SAKURA_INT_TYPE, + true, + "Total size of the snapshot in number of bytes."); + assert(addFieldBorder("input_data_size", 1, 10000000000)); + + //---------------------------------------------------------------------------------------------- + // output + //---------------------------------------------------------------------------------------------- + + registerOutputField("uuid", + SAKURA_STRING_TYPE, + "UUID of the new snapshot."); + registerOutputField("name", + SAKURA_STRING_TYPE, + "Name of the new snapshot."); + registerOutputField("uuid_input_file", + SAKURA_STRING_TYPE, + "UUID to identify the file for data upload of the snapshot."); + + //---------------------------------------------------------------------------------------------- + // + //---------------------------------------------------------------------------------------------- +} + +/** + * @brief runTask + */ +bool +CreateClusterSnapshot::runTask(BlossomIO &blossomIO, + const Kitsunemimi::DataMap &, + BlossomStatus &status, + Kitsunemimi::ErrorContainer &error) +{ + const std::string uuid = blossomIO.input.get("uuid").getString(); + const std::string name = blossomIO.input.get("name").getString(); + const std::string userId = blossomIO.input.get("id").getString(); + const std::string projectId = blossomIO.input.get("project_id").getString(); + const long inputDataSize = blossomIO.input.get("input_data_size").getLong(); + + // snapshots are created by another internal process, which gives the id's not in the context + // object, but as normal values + Kitsunemimi::Hanami::UserContext userContext; + userContext.userId = userId; + userContext.projectId = projectId; + + // get directory to store data from config + bool success = false; + std::string targetFilePath = GET_STRING_CONFIG("shiori", "cluster_snapshot_location", success); + if(success == false) + { + status.statusCode = Kitsunemimi::Hanami::INTERNAL_SERVER_ERROR_RTYPE; + error.addMeesage("snapshot-location to store cluster-snapshot is missing in the config"); + return false; + } + + // init temp-file for input-data + const std::string tempFileUuid = Kitsunemimi::Hanami::generateUuid().toString(); + if(ShioriRoot::tempFileHandler->initNewFile(tempFileUuid, inputDataSize) == false) + { + status.statusCode = Kitsunemimi::Hanami::INTERNAL_SERVER_ERROR_RTYPE; + error.addMeesage("Failed to initialize temporary file for new input-data."); + return false; + } + + // build absolut file-path to store the file + if(targetFilePath.at(targetFilePath.size() - 1) != '/') { + targetFilePath.append("/"); + } + targetFilePath.append(uuid + "_snapshot_" + userId); + + // register in database + blossomIO.output.insert("uuid", uuid); + blossomIO.output.insert("name", name); + blossomIO.output.insert("location", targetFilePath); + blossomIO.output.insert("header", blossomIO.input.get("header")); + blossomIO.output.insert("project_id", projectId); + blossomIO.output.insert("owner_id", userId); + blossomIO.output.insert("visibility", "private"); + + // init placeholder for temp-file progress to database + Kitsunemimi::JsonItem tempFiles; + tempFiles.insert(tempFileUuid, Kitsunemimi::JsonItem(0.0f)); + blossomIO.output.insert("temp_files", tempFiles); + + // add to database + if(ShioriRoot::clusterSnapshotTable->addClusterSnapshot(blossomIO.output, + userContext, + error) == false) + { + status.statusCode = Kitsunemimi::Hanami::INTERNAL_SERVER_ERROR_RTYPE; + return false; + } + + // add values to output + blossomIO.output.insert("uuid_input_file", tempFileUuid); + + // remove blocked values from output + blossomIO.output.remove("location"); + blossomIO.output.remove("header"); + blossomIO.output.remove("project_id"); + blossomIO.output.remove("owner_id"); + blossomIO.output.remove("visibility"); + blossomIO.output.remove("temp_files"); + + return true; +} diff --git a/src/components/ShioriArchive/src/api/v1/cluster_snapshot/create_cluster_snapshot.h b/src/components/ShioriArchive/src/api/v1/cluster_snapshot/create_cluster_snapshot.h new file mode 100644 index 00000000..1e10d280 --- /dev/null +++ b/src/components/ShioriArchive/src/api/v1/cluster_snapshot/create_cluster_snapshot.h @@ -0,0 +1,41 @@ +/** + * @file create_cluster_snapshot.h + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2022 Tobias Anker + * + * 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. + */ + +#ifndef CREATE_CLUSTER_SNAPSHOT_H +#define CREATE_CLUSTER_SNAPSHOT_H + +#include + +class CreateClusterSnapshot + : public Kitsunemimi::Hanami::Blossom +{ +public: + CreateClusterSnapshot(); + +protected: + bool runTask(Kitsunemimi::Hanami::BlossomIO &blossomIO, + const Kitsunemimi::DataMap &, + Kitsunemimi::Hanami::BlossomStatus &status, + Kitsunemimi::ErrorContainer &error); +}; + +#endif // CREATE_CLUSTER_SNAPSHOT_H diff --git a/src/components/ShioriArchive/src/api/v1/cluster_snapshot/delete_cluster_snapshot.cpp b/src/components/ShioriArchive/src/api/v1/cluster_snapshot/delete_cluster_snapshot.cpp new file mode 100644 index 00000000..8be4b39b --- /dev/null +++ b/src/components/ShioriArchive/src/api/v1/cluster_snapshot/delete_cluster_snapshot.cpp @@ -0,0 +1,98 @@ +/** + * @file delete_cluster_snapshot.cpp + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2022 Tobias Anker + * + * 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 "delete_cluster_snapshot.h" + +#include +#include + +#include +#include + +#include +#include + +using namespace Kitsunemimi::Hanami; + +DeleteClusterSnapshot::DeleteClusterSnapshot() + : Blossom("Delete a result-set from shiori.") +{ + //---------------------------------------------------------------------------------------------- + // input + //---------------------------------------------------------------------------------------------- + + registerInputField("uuid", + SAKURA_STRING_TYPE, + true, + "UUID of the cluster-snapshot to delete."); + assert(addFieldRegex("uuid", UUID_REGEX)); + + //---------------------------------------------------------------------------------------------- + // + //---------------------------------------------------------------------------------------------- +} + +/** + * @brief runTask + */ +bool +DeleteClusterSnapshot::runTask(BlossomIO &blossomIO, + const Kitsunemimi::DataMap &context, + BlossomStatus &status, + Kitsunemimi::ErrorContainer &error) +{ + const std::string dataUuid = blossomIO.input.get("uuid").getString(); + const Kitsunemimi::Hanami::UserContext userContext(context); + + // get location from database + Kitsunemimi::JsonItem result; + if(ShioriRoot::clusterSnapshotTable->getClusterSnapshot(result, + dataUuid, + userContext, + error, + true) == false) + { + status.statusCode = Kitsunemimi::Hanami::INTERNAL_SERVER_ERROR_RTYPE; + return false; + } + + // get location from response + const std::string location = result.get("location").getString(); + + // delete entry from db + if(ShioriRoot::clusterSnapshotTable->deleteClusterSnapshot(dataUuid, + userContext, + error) == false) + { + status.statusCode = Kitsunemimi::Hanami::INTERNAL_SERVER_ERROR_RTYPE; + return false; + } + + // delete local files + if(Kitsunemimi::deleteFileOrDir(location, error) == false) + { + status.statusCode = Kitsunemimi::Hanami::INTERNAL_SERVER_ERROR_RTYPE; + return false; + } + + return true; +} diff --git a/src/components/ShioriArchive/src/api/v1/cluster_snapshot/delete_cluster_snapshot.h b/src/components/ShioriArchive/src/api/v1/cluster_snapshot/delete_cluster_snapshot.h new file mode 100644 index 00000000..1ae3e2ca --- /dev/null +++ b/src/components/ShioriArchive/src/api/v1/cluster_snapshot/delete_cluster_snapshot.h @@ -0,0 +1,41 @@ +/** + * @file delete_cluster_snapshot.h + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2022 Tobias Anker + * + * 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. + */ + +#ifndef DELETE_CLUSTER_SNAPSHOT_H +#define DELETE_CLUSTER_SNAPSHOT_H + +#include + +class DeleteClusterSnapshot + : public Kitsunemimi::Hanami::Blossom +{ +public: + DeleteClusterSnapshot(); + +protected: + bool runTask(Kitsunemimi::Hanami::BlossomIO &blossomIO, + const Kitsunemimi::DataMap &context, + Kitsunemimi::Hanami::BlossomStatus &status, + Kitsunemimi::ErrorContainer &error); +}; + +#endif // DELETE_CLUSTER_SNAPSHOT_H diff --git a/src/components/ShioriArchive/src/api/v1/cluster_snapshot/finish_cluster_snapshot.cpp b/src/components/ShioriArchive/src/api/v1/cluster_snapshot/finish_cluster_snapshot.cpp new file mode 100644 index 00000000..ebe6d24b --- /dev/null +++ b/src/components/ShioriArchive/src/api/v1/cluster_snapshot/finish_cluster_snapshot.cpp @@ -0,0 +1,138 @@ +/** + * @file finish_cluster_snapshot.cpp + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2022 Tobias Anker + * + * 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 "finish_cluster_snapshot.h" + +#include +#include +#include + +#include + +#include + +using namespace Kitsunemimi::Hanami; + +FinalizeClusterSnapshot::FinalizeClusterSnapshot() + : Blossom("Finish snapshot of a cluster.") +{ + //---------------------------------------------------------------------------------------------- + // input + //---------------------------------------------------------------------------------------------- + + registerInputField("uuid", + SAKURA_STRING_TYPE, + true, + "Name of the new set."); + assert(addFieldRegex("uuid", "[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-" + "[a-fA-F0-9]{12}")); + + registerInputField("user_id", + SAKURA_STRING_TYPE, + true, + "ID of the user, who belongs to the snapshot."); + assert(addFieldBorder("user_id", 4, 256)); + assert(addFieldRegex("user_id", "[a-zA-Z][a-zA-Z_0-9]*")); + + registerInputField("project_id", + SAKURA_STRING_TYPE, + true, + "Name of the new set."); + // TODO: issue Hanami-Meta#17 + //assert(addFieldRegex("project_id", "[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-" + // "[a-fA-F0-9]{4}-[a-fA-F0-9]{12}")); + + registerInputField("uuid_input_file", + SAKURA_STRING_TYPE, + true, + "UUID to identify the file for date upload of input-data."); + assert(addFieldRegex("uuid_input_file", "[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-" + "[a-fA-F0-9]{4}-[a-fA-F0-9]{12}")); + + //---------------------------------------------------------------------------------------------- + // output + //---------------------------------------------------------------------------------------------- + + registerOutputField("uuid", + SAKURA_STRING_TYPE, + "UUID of the new set."); + + //---------------------------------------------------------------------------------------------- + // + //---------------------------------------------------------------------------------------------- +} + +/** + * @brief runTask + */ +bool +FinalizeClusterSnapshot::runTask(BlossomIO &blossomIO, + const Kitsunemimi::DataMap &, + BlossomStatus &status, + Kitsunemimi::ErrorContainer &error) +{ + const std::string uuid = blossomIO.input.get("uuid").getString(); + const std::string inputUuid = blossomIO.input.get("uuid_input_file").getString(); + const std::string userId = blossomIO.input.get("id").getString(); + const std::string projectId = blossomIO.input.get("project_id").getString(); + + // snapshots are created by another internal process, which gives the id's not in the context + // object, but as normal values + Kitsunemimi::Hanami::UserContext userContext; + userContext.userId = userId; + userContext.projectId = projectId; + + // get location from database + Kitsunemimi::JsonItem result; + if(ShioriRoot::clusterSnapshotTable->getClusterSnapshot(result, + uuid, + userContext, + error, + true) == false) + { + status.errorMessage = "Snapshot with uuid '" + uuid + "' not found."; + status.statusCode = Kitsunemimi::Hanami::NOT_FOUND_RTYPE; + return false; + } + + // read input-data from temp-file + Kitsunemimi::DataBuffer inputBuffer; + if(ShioriRoot::tempFileHandler->getData(inputBuffer, inputUuid) == false) + { + status.errorMessage = "Input-data with uuid '" + inputUuid + "' not found."; + status.statusCode = Kitsunemimi::Hanami::NOT_FOUND_RTYPE; + return false; + } + + // move temp-file to target-location + const std::string targetLocation = result.get("location").getString(); + if(ShioriRoot::tempFileHandler->moveData(inputUuid, targetLocation, error) == false) + { + status.statusCode = Kitsunemimi::Hanami::INTERNAL_SERVER_ERROR_RTYPE; + return false; + } + + // create output + blossomIO.output.insert("uuid", uuid); + + return true; +} diff --git a/src/components/ShioriArchive/src/api/v1/cluster_snapshot/finish_cluster_snapshot.h b/src/components/ShioriArchive/src/api/v1/cluster_snapshot/finish_cluster_snapshot.h new file mode 100644 index 00000000..7b477b7d --- /dev/null +++ b/src/components/ShioriArchive/src/api/v1/cluster_snapshot/finish_cluster_snapshot.h @@ -0,0 +1,41 @@ +/** + * @file get_cluster_snapshot.h + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2022 Tobias Anker + * + * 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. + */ + +#ifndef FINISH_CLUSTER_SNAPSHOT_H +#define FINISH_CLUSTER_SNAPSHOT_H + +#include + +class FinalizeClusterSnapshot + : public Kitsunemimi::Hanami::Blossom +{ +public: + FinalizeClusterSnapshot(); + +protected: + bool runTask(Kitsunemimi::Hanami::BlossomIO &blossomIO, + const Kitsunemimi::DataMap &, + Kitsunemimi::Hanami::BlossomStatus &status, + Kitsunemimi::ErrorContainer &error); +}; + +#endif // FINISH_CLUSTER_SNAPSHOT_H diff --git a/src/components/ShioriArchive/src/api/v1/cluster_snapshot/get_cluster_snapshot.cpp b/src/components/ShioriArchive/src/api/v1/cluster_snapshot/get_cluster_snapshot.cpp new file mode 100644 index 00000000..850b42c8 --- /dev/null +++ b/src/components/ShioriArchive/src/api/v1/cluster_snapshot/get_cluster_snapshot.cpp @@ -0,0 +1,97 @@ +/** + * @file get_cluster_snapshot.cpp + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2022 Tobias Anker + * + * 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 "get_cluster_snapshot.h" + +#include +#include + +#include + +using namespace Kitsunemimi::Hanami; + +GetClusterSnapshot::GetClusterSnapshot() + : Blossom("Get snapshot of a cluster.") +{ + //---------------------------------------------------------------------------------------------- + // input + //---------------------------------------------------------------------------------------------- + + registerInputField("uuid", + SAKURA_STRING_TYPE, + true, + "UUID of the original request-task, which placed the result in shiori."); + assert(addFieldRegex("uuid", "[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-" + "[a-fA-F0-9]{4}-[a-fA-F0-9]{12}")); + + //---------------------------------------------------------------------------------------------- + // output + //---------------------------------------------------------------------------------------------- + + registerOutputField("uuid", + SAKURA_STRING_TYPE, + "UUID of the data-set."); + registerOutputField("name", + SAKURA_STRING_TYPE, + "Name of the data-set."); + registerOutputField("location", + SAKURA_STRING_TYPE, + "File path on local storage."); + registerOutputField("header", + SAKURA_MAP_TYPE, + "Header-information of the snapshot-file."); + + //---------------------------------------------------------------------------------------------- + // + //---------------------------------------------------------------------------------------------- +} + +/** + * @brief runTask + */ +bool +GetClusterSnapshot::runTask(BlossomIO &blossomIO, + const Kitsunemimi::DataMap &context, + BlossomStatus &status, + Kitsunemimi::ErrorContainer &error) +{ + const std::string dataUuid = blossomIO.input.get("uuid").getString(); + const Kitsunemimi::Hanami::UserContext userContext(context); + + if(ShioriRoot::clusterSnapshotTable->getClusterSnapshot(blossomIO.output, + dataUuid, + userContext, + error, + true) == false) + { + status.statusCode = Kitsunemimi::Hanami::INTERNAL_SERVER_ERROR_RTYPE; + return false; + } + + // remove irrelevant fields + blossomIO.output.remove("owner_id"); + blossomIO.output.remove("project_id"); + blossomIO.output.remove("visibility"); + blossomIO.output.remove("temp_files"); + + return true; +} diff --git a/src/components/ShioriArchive/src/api/v1/cluster_snapshot/get_cluster_snapshot.h b/src/components/ShioriArchive/src/api/v1/cluster_snapshot/get_cluster_snapshot.h new file mode 100644 index 00000000..3678d6c5 --- /dev/null +++ b/src/components/ShioriArchive/src/api/v1/cluster_snapshot/get_cluster_snapshot.h @@ -0,0 +1,41 @@ +/** + * @file get_cluster_snapshot.h + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2022 Tobias Anker + * + * 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. + */ + +#ifndef GET_CLUSTER_SNAPSHOT_H +#define GET_CLUSTER_SNAPSHOT_H + +#include + +class GetClusterSnapshot + : public Kitsunemimi::Hanami::Blossom +{ +public: + GetClusterSnapshot(); + +protected: + bool runTask(Kitsunemimi::Hanami::BlossomIO &blossomIO, + const Kitsunemimi::DataMap &context, + Kitsunemimi::Hanami::BlossomStatus &status, + Kitsunemimi::ErrorContainer &error); +}; + +#endif // GET_CLUSTER_SNAPSHOT_H diff --git a/src/components/ShioriArchive/src/api/v1/cluster_snapshot/list_cluster_snapshot.cpp b/src/components/ShioriArchive/src/api/v1/cluster_snapshot/list_cluster_snapshot.cpp new file mode 100644 index 00000000..090f50ca --- /dev/null +++ b/src/components/ShioriArchive/src/api/v1/cluster_snapshot/list_cluster_snapshot.cpp @@ -0,0 +1,80 @@ +/** + * @file list_cluster_snapshot.cpp + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2022 Tobias Anker + * + * 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 "list_cluster_snapshot.h" + +#include +#include + +#include + +using namespace Kitsunemimi::Hanami; + +ListClusterSnapshot::ListClusterSnapshot() + : Blossom("List snapshots of all visible cluster.") +{ + //---------------------------------------------------------------------------------------------- + // output + //---------------------------------------------------------------------------------------------- + + registerOutputField("header", + SAKURA_ARRAY_TYPE, + "Array with the namings all columns of the table."); + assert(addFieldMatch("header", new Kitsunemimi::DataValue("[\"uuid\"," + "\"project_id\"," + "\"owner_id\"," + "\"visibility\"," + "\"name\"]"))); + + registerOutputField("body", + SAKURA_ARRAY_TYPE, + "Array with all rows of the table, which array arrays too."); + + //---------------------------------------------------------------------------------------------- + // + //---------------------------------------------------------------------------------------------- +} + +/** + * @brief runTask + */ +bool +ListClusterSnapshot::runTask(BlossomIO &blossomIO, + const Kitsunemimi::DataMap &context, + BlossomStatus &status, + Kitsunemimi::ErrorContainer &error) +{ + const Kitsunemimi::Hanami::UserContext userContext(context); + + // get data from table + Kitsunemimi::TableItem table; + if(ShioriRoot::clusterSnapshotTable->getAllClusterSnapshot(table, userContext, error) == false) + { + status.statusCode = Kitsunemimi::Hanami::INTERNAL_SERVER_ERROR_RTYPE; + return false; + } + + blossomIO.output.insert("header", table.getInnerHeader()); + blossomIO.output.insert("body", table.getBody()); + + return true; +} diff --git a/src/components/ShioriArchive/src/api/v1/cluster_snapshot/list_cluster_snapshot.h b/src/components/ShioriArchive/src/api/v1/cluster_snapshot/list_cluster_snapshot.h new file mode 100644 index 00000000..55842a1d --- /dev/null +++ b/src/components/ShioriArchive/src/api/v1/cluster_snapshot/list_cluster_snapshot.h @@ -0,0 +1,41 @@ +/** + * @file list_cluster_snapshot.h + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2022 Tobias Anker + * + * 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. + */ + +#ifndef LIST_CLUSTER_SNAPSHOT_H +#define LIST_CLUSTER_SNAPSHOT_H + +#include + +class ListClusterSnapshot + : public Kitsunemimi::Hanami::Blossom +{ +public: + ListClusterSnapshot(); + +protected: + bool runTask(Kitsunemimi::Hanami::BlossomIO &blossomIO, + const Kitsunemimi::DataMap &, + Kitsunemimi::Hanami::BlossomStatus &status, + Kitsunemimi::ErrorContainer &error); +}; + +#endif // LIST_CLUSTER_SNAPSHOT_H diff --git a/src/components/ShioriArchive/src/api/v1/data_files/check_data_set.cpp b/src/components/ShioriArchive/src/api/v1/data_files/check_data_set.cpp new file mode 100644 index 00000000..9223bdc7 --- /dev/null +++ b/src/components/ShioriArchive/src/api/v1/data_files/check_data_set.cpp @@ -0,0 +1,159 @@ +/** + * @file check_data_set.cpp + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2022 Tobias Anker + * + * 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 "check_data_set.h" + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include + +using namespace Kitsunemimi; + +CheckDataSet::CheckDataSet() + : Blossom("Compare a list of values with a data-set to check correctness.") +{ + //---------------------------------------------------------------------------------------------- + // input + //---------------------------------------------------------------------------------------------- + + registerInputField("result_uuid", + Hanami::SAKURA_STRING_TYPE, + true, + "UUID of the data-set to compare to."); + assert(addFieldRegex("result_uuid", UUID_REGEX)); + + registerInputField("data_set_uuid", + Hanami::SAKURA_STRING_TYPE, + true, + "UUID of the data-set to compare to."); + assert(addFieldRegex("data_set_uuid", UUID_REGEX)); + + //---------------------------------------------------------------------------------------------- + // output + //---------------------------------------------------------------------------------------------- + + registerOutputField("correctness", + Hanami::SAKURA_FLOAT_TYPE, + "Correctness of the values compared to the data-set."); + + //---------------------------------------------------------------------------------------------- + // + //---------------------------------------------------------------------------------------------- +} + +/** + * @brief runTask + */ +bool +CheckDataSet::runTask(Hanami::BlossomIO &blossomIO, + const Kitsunemimi::DataMap &context, + Hanami::BlossomStatus &status, + ErrorContainer &error) +{ + const std::string resultUuid = blossomIO.input.get("result_uuid").getString(); + const std::string dataUuid = blossomIO.input.get("data_set_uuid").getString(); + const Kitsunemimi::Hanami::UserContext userContext(context); + + // get result + // check if request-result exist within the table + JsonItem result; + if(ShioriRoot::requestResultTable->getRequestResult(result, + resultUuid, + userContext, + error, + true) == false) + { + status.errorMessage = "Request-result with UUID '" + resultUuid + "' not found."; + status.statusCode = Kitsunemimi::Hanami::NOT_FOUND_RTYPE; + error.addMeesage(status.errorMessage); + return false; + } + + // get data-info from database + if(ShioriRoot::dataSetTable->getDataSet(blossomIO.output, + dataUuid, + userContext, + error, + true) == false) + { + status.statusCode = Kitsunemimi::Hanami::INTERNAL_SERVER_ERROR_RTYPE; + return false; + } + + // get file information + const std::string location = blossomIO.output.get("location").getString(); + + Kitsunemimi::DataBuffer buffer; + DataSetFile::DataSetHeader dataSetHeader; + DataSetFile::ImageTypeHeader imageTypeHeader; + Kitsunemimi::BinaryFile file(location); + + // read data-set-header + if(file.readCompleteFile(buffer, error) == false) + { + error.addMeesage("Failed to read data-set-header from file '" + location + "'"); + return false; + } + + // prepare values + uint64_t correctValues = 0; + uint64_t dataPos = sizeof(DataSetFile::DataSetHeader) + sizeof(DataSetFile::ImageTypeHeader); + const uint8_t* u8Data = static_cast(buffer.data); + memcpy(&dataSetHeader, buffer.data, sizeof(DataSetFile::DataSetHeader)); + memcpy(&imageTypeHeader, + &u8Data[sizeof(DataSetFile::DataSetHeader)], + sizeof(DataSetFile::ImageTypeHeader)); + const uint64_t lineOffset = imageTypeHeader.numberOfInputsX * imageTypeHeader.numberOfInputsY; + const uint64_t lineSize = (imageTypeHeader.numberOfInputsX * imageTypeHeader.numberOfInputsY) + + imageTypeHeader.numberOfOutputs; + const float* content = reinterpret_cast(&u8Data[dataPos]); + + // iterate over all values and check + DataArray* compareData = result.get("data").getItemContent()->toArray(); + for(uint64_t i = 0; i < compareData->size(); i++) + { + const uint64_t actualPos = (i * lineSize) + lineOffset; + const uint64_t checkVal = compareData->get(i)->toValue()->getInt(); + if(content[actualPos + checkVal] > 0.0f) { + correctValues++; + } + } + + // add result to output + const float correctness = (100.0f / static_cast(compareData->size())) + * static_cast(correctValues); + blossomIO.output.deleteContent(); + blossomIO.output.insert("correctness", correctness); + + return true; +} diff --git a/src/components/ShioriArchive/src/api/v1/data_files/check_data_set.h b/src/components/ShioriArchive/src/api/v1/data_files/check_data_set.h new file mode 100644 index 00000000..002b5805 --- /dev/null +++ b/src/components/ShioriArchive/src/api/v1/data_files/check_data_set.h @@ -0,0 +1,41 @@ +/** + * @file check_data_set.h + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2022 Tobias Anker + * + * 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. + */ + +#ifndef SHIORIARCHIVE_CHECKDATASET_H +#define SHIORIARCHIVE_CHECKDATASET_H + +#include + +class CheckDataSet + : public Kitsunemimi::Hanami::Blossom +{ +public: + CheckDataSet(); + +protected: + bool runTask(Kitsunemimi::Hanami::BlossomIO &blossomIO, + const Kitsunemimi::DataMap &context, + Kitsunemimi::Hanami::BlossomStatus &status, + Kitsunemimi::ErrorContainer &error); +}; + +#endif // SHIORIARCHIVE_CHECKDATASET_H diff --git a/src/components/ShioriArchive/src/api/v1/data_files/csv/create_csv_data_set.cpp b/src/components/ShioriArchive/src/api/v1/data_files/csv/create_csv_data_set.cpp new file mode 100644 index 00000000..a43f1674 --- /dev/null +++ b/src/components/ShioriArchive/src/api/v1/data_files/csv/create_csv_data_set.cpp @@ -0,0 +1,156 @@ +/** + * @file create_csv_data_set.cpp + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2022 Tobias Anker + * + * 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 "create_csv_data_set.h" + +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +using namespace Kitsunemimi::Hanami; + +CreateCsvDataSet::CreateCsvDataSet() + : Blossom("Init new csv-file data-set.") +{ + //---------------------------------------------------------------------------------------------- + // input + //---------------------------------------------------------------------------------------------- + + registerInputField("name", + SAKURA_STRING_TYPE, + true, + "Name of the new data-set."); + assert(addFieldBorder("name", 4, 256)); + assert(addFieldRegex("name", NAME_REGEX)); + + registerInputField("input_data_size", + SAKURA_INT_TYPE, + true, + "Total size of the input-data."); + assert(addFieldBorder("input_data_size", 1, 10000000000)); + + //---------------------------------------------------------------------------------------------- + // output + //---------------------------------------------------------------------------------------------- + + registerOutputField("uuid", + SAKURA_STRING_TYPE, + "UUID of the new data-set."); + registerOutputField("name", + SAKURA_STRING_TYPE, + "Name of the new data-set."); + registerOutputField("owner_id", + SAKURA_STRING_TYPE, + "ID of the user, who created the data-set."); + registerOutputField("project_id", + SAKURA_STRING_TYPE, + "ID of the project, where the data-set belongs to."); + registerOutputField("visibility", + SAKURA_STRING_TYPE, + "Visibility of the data-set (private, shared, public)."); + registerOutputField("type", + SAKURA_STRING_TYPE, + "Type of the new set (csv)"); + registerOutputField("uuid_input_file", + SAKURA_STRING_TYPE, + "UUID to identify the file for date upload of input-data."); + + //---------------------------------------------------------------------------------------------- + // + //---------------------------------------------------------------------------------------------- +} + +bool +CreateCsvDataSet::runTask(BlossomIO &blossomIO, + const Kitsunemimi::DataMap &context, + BlossomStatus &status, + Kitsunemimi::ErrorContainer &error) +{ + const std::string name = blossomIO.input.get("name").getString(); + const long inputDataSize = blossomIO.input.get("input_data_size").getLong(); + const Kitsunemimi::Hanami::UserContext userContext(context); + + // get directory to store data from config + bool success = false; + std::string targetFilePath = GET_STRING_CONFIG("shiori", "data_set_location", success); + if(success == false) + { + status.statusCode = Kitsunemimi::Hanami::INTERNAL_SERVER_ERROR_RTYPE; + error.addMeesage("file-location to store dataset is missing in the config"); + return false; + } + + // init temp-file for input-data + const std::string inputUuid = Kitsunemimi::Hanami::generateUuid().toString(); + if(ShioriRoot::tempFileHandler->initNewFile(inputUuid, inputDataSize) == false) + { + status.statusCode = Kitsunemimi::Hanami::INTERNAL_SERVER_ERROR_RTYPE; + error.addMeesage("Failed to initialize temporary file for new input-data."); + return false; + } + + // build absolut file-path to store the file + if(targetFilePath.at(targetFilePath.size() - 1) != '/') { + targetFilePath.append("/"); + } + targetFilePath.append(name + "_csv_" +userContext. userId); + + // register in database + blossomIO.output.insert("name", name); + blossomIO.output.insert("type", "csv"); + blossomIO.output.insert("location", targetFilePath); + blossomIO.output.insert("project_id", userContext.projectId); + blossomIO.output.insert("owner_id", userContext.userId); + blossomIO.output.insert("visibility", "private"); + + // init placeholder for temp-file progress to database + Kitsunemimi::JsonItem tempFiles; + tempFiles.insert(inputUuid, Kitsunemimi::JsonItem(0.0f)); + blossomIO.output.insert("temp_files", tempFiles); + + // add to database + if(ShioriRoot::dataSetTable->addDataSet(blossomIO.output, userContext, error) == false) + { + status.statusCode = Kitsunemimi::Hanami::INTERNAL_SERVER_ERROR_RTYPE; + return false; + } + + // add values to output + blossomIO.output.insert("uuid_input_file", inputUuid); + + // remove blocked values from output + blossomIO.output.remove("location"); + blossomIO.output.remove("temp_files"); + + return true; +} diff --git a/src/components/ShioriArchive/src/api/v1/data_files/csv/create_csv_data_set.h b/src/components/ShioriArchive/src/api/v1/data_files/csv/create_csv_data_set.h new file mode 100644 index 00000000..5021bafe --- /dev/null +++ b/src/components/ShioriArchive/src/api/v1/data_files/csv/create_csv_data_set.h @@ -0,0 +1,41 @@ +/** + * @file create_csv_data_set.h + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2022 Tobias Anker + * + * 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. + */ + +#ifndef SHIORIARCHIVE_CSV_CREATE_DATA_SET_H +#define SHIORIARCHIVE_CSV_CREATE_DATA_SET_H + +#include + +class CreateCsvDataSet + : public Kitsunemimi::Hanami::Blossom +{ +public: + CreateCsvDataSet(); + +protected: + bool runTask(Kitsunemimi::Hanami::BlossomIO &blossomIO, + const Kitsunemimi::DataMap &context, + Kitsunemimi::Hanami::BlossomStatus &status, + Kitsunemimi::ErrorContainer &error); +}; + +#endif // SHIORIARCHIVE_CSV_CREATE_DATA_SET_H diff --git a/src/components/ShioriArchive/src/api/v1/data_files/csv/finalize_csv_data_set.cpp b/src/components/ShioriArchive/src/api/v1/data_files/csv/finalize_csv_data_set.cpp new file mode 100644 index 00000000..c8499b63 --- /dev/null +++ b/src/components/ShioriArchive/src/api/v1/data_files/csv/finalize_csv_data_set.cpp @@ -0,0 +1,291 @@ +/** + * @file finalize_csv_data_set.cpp + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2022 Tobias Anker + * + * 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 "finalize_csv_data_set.h" + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +using namespace Kitsunemimi::Hanami; + +FinalizeCsvDataSet::FinalizeCsvDataSet() + : Blossom("Finalize uploaded data-set by checking completeness of the " + "uploaded and convert into generic format.") +{ + //---------------------------------------------------------------------------------------------- + // input + //---------------------------------------------------------------------------------------------- + + registerInputField("uuid", + SAKURA_STRING_TYPE, + true, + "UUID of the new data-set."); + assert(addFieldRegex("uuid", UUID_REGEX)); + + registerInputField("uuid_input_file", + SAKURA_STRING_TYPE, + true, + "UUID to identify the file for date upload of input-data."); + assert(addFieldRegex("uuid_input_file", UUID_REGEX)); + + //---------------------------------------------------------------------------------------------- + // output + //---------------------------------------------------------------------------------------------- + + registerOutputField("uuid", + SAKURA_STRING_TYPE, + "UUID of the new data-set."); + + //---------------------------------------------------------------------------------------------- + // + //---------------------------------------------------------------------------------------------- +} + +/** + * @brief runTask + */ +bool +FinalizeCsvDataSet::runTask(BlossomIO &blossomIO, + const Kitsunemimi::DataMap &context, + BlossomStatus &status, + Kitsunemimi::ErrorContainer &error) +{ + const std::string uuid = blossomIO.input.get("uuid").getString(); + const std::string inputUuid = blossomIO.input.get("uuid_input_file").getString(); + const Kitsunemimi::Hanami::UserContext userContext(context); + + // get location from database + Kitsunemimi::JsonItem result; + if(ShioriRoot::dataSetTable->getDataSet(result, uuid, userContext, error, true) == false) + { + status.errorMessage = "Data with uuid '" + uuid + "' not found."; + status.statusCode = Kitsunemimi::Hanami::NOT_FOUND_RTYPE; + return false; + } + + // read input-data from temp-file + Kitsunemimi::DataBuffer inputBuffer; + if(ShioriRoot::tempFileHandler->getData(inputBuffer, inputUuid) == false) + { + status.errorMessage = "Input-data with uuid '" + inputUuid + "' not found."; + status.statusCode = Kitsunemimi::Hanami::NOT_FOUND_RTYPE; + return false; + } + + // write data to file + if(convertCsvData(result.get("location").getString(), + result.get("name").getString().c_str(), + inputBuffer) == false) + { + status.statusCode = Kitsunemimi:: Hanami::INTERNAL_SERVER_ERROR_RTYPE; + error.addMeesage("Failed to convert csv-data"); + return false; + } + + // delete temp-files + ShioriRoot::tempFileHandler->removeData(inputUuid); + + // create output + blossomIO.output.insert("uuid", uuid); + + return true; +} + +void +FinalizeCsvDataSet::convertField(float* segmentPos, + const std::string &cell, + const float lastVal) +{ + // true + if(cell == "Null" + || cell == "null" + || cell == "NULL") + { + *segmentPos = lastVal; + } + // true + else if(cell == "True" + || cell == "true" + || cell == "TRUE") + { + *segmentPos = 1.0f; + } + // false + else if(cell == "False" + || cell == "false" + || cell == "FALSE") + { + *segmentPos = 0.0f; + } + // int/long + else if(regex_match(cell, std::regex(INT_VALUE_REGEX))) + { + *segmentPos = static_cast(std::stoi(cell.c_str())); + } + // float/double + else if(regex_match(cell, std::regex(FLOAT_VALUE_REGEX))) + { + *segmentPos = std::strtof(cell.c_str(), NULL); + } + else + { + // ignore other lines + *segmentPos = 0.0f; + } +} + +/** + * @brief convert csv-data into generic format + * + * @param filePath path to the resulting file + * @param name data-set name + * @param inputBuffer buffer with input-data + * + * @return true, if successfull, else false + */ +bool +FinalizeCsvDataSet::convertCsvData(const std::string &filePath, + const std::string &name, + const Kitsunemimi::DataBuffer &inputBuffer) +{ + TableDataSetFile file(filePath); + file.type = DataSetFile::TABLE_TYPE; + file.name = name; + + // prepare content-processing + const std::string stringContent(static_cast(inputBuffer.data), + inputBuffer.usedBufferSize); + + // buffer for values to reduce write-access to file + const uint32_t segmentSize = 10000000; + std::vector segment(segmentSize, 0.0f); + std::vector lastLine; + uint64_t segmentPos = 0; + uint64_t segmentCounter = 0; + + // split content + std::vector lines; + Kitsunemimi::splitStringByDelimiter(lines, stringContent, '\n'); + + bool isHeader = true; + + for(uint64_t lineNum = 0; lineNum < lines.size(); lineNum++) + { + const std::string* line = &lines[lineNum]; + + // check if the line is relevant to ignore broken lines + const uint64_t numberOfColumns = std::count(line->begin(), line->end(), ',') + 1; + if(numberOfColumns == 1) { + continue; + } + + // split line + std::vector lineContent; + Kitsunemimi::splitStringByDelimiter(lineContent, *line, ','); + + if(isHeader) + { + file.tableHeader.numberOfColumns = numberOfColumns; + file.tableHeader.numberOfLines = lines.size(); + + for(const std::string &col : lineContent) + { + // create and add header-entry + DataSetFile::TableHeaderEntry entry; + entry.setName(col); + file.tableColumns.push_back(entry); + } + isHeader = false; + + if(file.initNewFile() == false) { + return false; + } + + // this was the max value. While iterating over all lines, this value will be new + // calculated with the correct value + file.tableHeader.numberOfLines = 0; + lastLine = std::vector(numberOfColumns, 0.0f); + } + else + { + for(uint64_t colNum = 0; colNum < lineContent.size(); colNum++) + { + const std::string* cell = &lineContent[colNum]; + if(lastLine.size() > 0) + { + const float lastVal = lastLine[colNum]; + convertField(&segment[segmentPos], *cell, lastVal); + } + else + { + convertField(&segment[segmentPos], *cell, 0.0f); + } + + lastLine[colNum] = segment[segmentPos]; + + // write next segment to file + segmentPos++; + if(segmentPos == segmentSize) + { + file.addBlock(segmentCounter, &segment[0], segmentSize); + segmentPos = 0; + segmentCounter++; + } + } + + file.tableHeader.numberOfLines++; + } + } + + // write last incomplete segment to file + if(segmentPos != 0) { + file.addBlock(segmentCounter, &segment[0], segmentPos); + } + + // update header in file for the final number of lines for the case, + // that there were invalid lines + if(file.updateHeader() == false) { + return false; + } + + // debug-output + //file.print(); + + return true; +} + diff --git a/src/components/ShioriArchive/src/api/v1/data_files/csv/finalize_csv_data_set.h b/src/components/ShioriArchive/src/api/v1/data_files/csv/finalize_csv_data_set.h new file mode 100644 index 00000000..17470136 --- /dev/null +++ b/src/components/ShioriArchive/src/api/v1/data_files/csv/finalize_csv_data_set.h @@ -0,0 +1,50 @@ +/** + * @file finalize_csv_data_set.h + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2022 Tobias Anker + * + * 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. + */ + +#ifndef SHIORIARCHIVE_CSV_FINALIZE_DATA_SET_H +#define SHIORIARCHIVE_CSV_FINALIZE_DATA_SET_H + +#include +#include + +class FinalizeCsvDataSet + : public Kitsunemimi::Hanami::Blossom +{ +public: + FinalizeCsvDataSet(); + +protected: + bool runTask(Kitsunemimi::Hanami::BlossomIO &blossomIO, + const Kitsunemimi::DataMap &context, + Kitsunemimi::Hanami::BlossomStatus &status, + Kitsunemimi::ErrorContainer &error); + +private: + bool convertCsvData(const std::string &filePath, + const std::string &name, + const Kitsunemimi::DataBuffer &inputBuffer); + void convertField(float* segmentPos, + const std::string &cell, + const float lastVal); +}; + +#endif // SHIORIARCHIVE_CSV_FINALIZE_DATA_SET_H diff --git a/src/components/ShioriArchive/src/api/v1/data_files/delete_data_set.cpp b/src/components/ShioriArchive/src/api/v1/data_files/delete_data_set.cpp new file mode 100644 index 00000000..1f6bfea0 --- /dev/null +++ b/src/components/ShioriArchive/src/api/v1/data_files/delete_data_set.cpp @@ -0,0 +1,96 @@ +/** + * @file delete_data_set.cpp + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2022 Tobias Anker + * + * 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 "delete_data_set.h" + +#include +#include + +#include +#include + +#include +#include + +using namespace Kitsunemimi; + +DeleteDataSet::DeleteDataSet() + : Blossom("Delete a speific data-set.") +{ + //---------------------------------------------------------------------------------------------- + // input + //---------------------------------------------------------------------------------------------- + + registerInputField("uuid", + Hanami::SAKURA_STRING_TYPE, + true, + "UUID of the data-set to delete."); + assert(addFieldRegex("uuid", UUID_REGEX)); + + //---------------------------------------------------------------------------------------------- + // + //---------------------------------------------------------------------------------------------- +} + +/** + * @brief runTask + */ +bool +DeleteDataSet::runTask(Hanami::BlossomIO &blossomIO, + const Kitsunemimi::DataMap &context, + Hanami::BlossomStatus &status, + ErrorContainer &error) +{ + const std::string dataUuid = blossomIO.input.get("uuid").getString(); + const Kitsunemimi::Hanami::UserContext userContext(context); + + // get location from database + JsonItem result; + if(ShioriRoot::dataSetTable->getDataSet(result, + dataUuid, + userContext, + error, + true) == false) + { + status.statusCode = Hanami::INTERNAL_SERVER_ERROR_RTYPE; + return false; + } + + // get location from response + const std::string location = result.get("location").getString(); + + // delete entry from db + if(ShioriRoot::dataSetTable->deleteDataSet(dataUuid, userContext, error) == false) + { + status.statusCode = Hanami::INTERNAL_SERVER_ERROR_RTYPE; + return false; + } + + // delete local files + if(Kitsunemimi::deleteFileOrDir(location, error) == false) + { + status.statusCode = Hanami::INTERNAL_SERVER_ERROR_RTYPE; + return false; + } + + return true; +} diff --git a/src/components/ShioriArchive/src/api/v1/data_files/delete_data_set.h b/src/components/ShioriArchive/src/api/v1/data_files/delete_data_set.h new file mode 100644 index 00000000..d39685fc --- /dev/null +++ b/src/components/ShioriArchive/src/api/v1/data_files/delete_data_set.h @@ -0,0 +1,41 @@ +/** + * @file delete_data_set.h + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2022 Tobias Anker + * + * 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. + */ + +#ifndef SHIORIARCHIVE_DELETE_DATA_SET_H +#define SHIORIARCHIVE_DELETE_DATA_SET_H + +#include + +class DeleteDataSet + : public Kitsunemimi::Hanami::Blossom +{ +public: + DeleteDataSet(); + +protected: + bool runTask(Kitsunemimi::Hanami::BlossomIO &blossomIO, + const Kitsunemimi::DataMap &context, + Kitsunemimi::Hanami::BlossomStatus &status, + Kitsunemimi::ErrorContainer &error); +}; + +#endif // SHIORIARCHIVE_DELETE_DATA_SET_H diff --git a/src/components/ShioriArchive/src/api/v1/data_files/get_data_set.cpp b/src/components/ShioriArchive/src/api/v1/data_files/get_data_set.cpp new file mode 100644 index 00000000..0261fa3f --- /dev/null +++ b/src/components/ShioriArchive/src/api/v1/data_files/get_data_set.cpp @@ -0,0 +1,216 @@ +/** + * @file get_data_set.cpp + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2022 Tobias Anker + * + * 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 "get_data_set.h" + +#include +#include +#include +#include +#include + +#include +#include + +#include +#include + +using namespace Kitsunemimi::Hanami; + +GetDataSet::GetDataSet() + : Blossom("Get information of a specific data-set.") +{ + //---------------------------------------------------------------------------------------------- + // input + //---------------------------------------------------------------------------------------------- + + registerInputField("uuid", + SAKURA_STRING_TYPE, + true, + "UUID of the data-set set to delete."); + assert(addFieldRegex("uuid", UUID_REGEX)); + + //---------------------------------------------------------------------------------------------- + // output + //---------------------------------------------------------------------------------------------- + + registerOutputField("uuid", + SAKURA_STRING_TYPE, + "UUID of the data-set."); + registerOutputField("name", + SAKURA_STRING_TYPE, + "Name of the data-set."); + registerOutputField("owner_id", + SAKURA_STRING_TYPE, + "ID of the user, who created the data-set."); + registerOutputField("project_id", + SAKURA_STRING_TYPE, + "ID of the project, where the data-set belongs to."); + registerOutputField("visibility", + SAKURA_STRING_TYPE, + "Visibility of the data-set (private, shared, public)."); + registerOutputField("location", + SAKURA_STRING_TYPE, + "Local file-path of the data-set."); + registerOutputField("type", + SAKURA_STRING_TYPE, + "Type of the new set (csv or mnist)"); + registerOutputField("inputs", + SAKURA_INT_TYPE, + "Number of inputs."); + registerOutputField("outputs", + SAKURA_INT_TYPE, + "Number of outputs."); + registerOutputField("lines", + SAKURA_INT_TYPE, + "Number of lines."); + + //---------------------------------------------------------------------------------------------- + // + //---------------------------------------------------------------------------------------------- +} + +/** + * @brief runTask + */ +bool +GetDataSet::runTask(BlossomIO &blossomIO, + const Kitsunemimi::DataMap &context, + BlossomStatus &status, + Kitsunemimi::ErrorContainer &error) +{ + const std::string dataUuid = blossomIO.input.get("uuid").getString(); + const Kitsunemimi::Hanami::UserContext userContext(context); + + if(ShioriRoot::dataSetTable->getDataSet(blossomIO.output, + dataUuid, + userContext, + error, + true) == false) + { + status.statusCode = Kitsunemimi::Hanami::INTERNAL_SERVER_ERROR_RTYPE; + return false; + } + + // get file information + const std::string location = blossomIO.output.get("location").getString(); + if(getHeaderInformation(blossomIO.output, location, error) == false) + { + error.addMeesage("Failed the read information from file '" + location + "'"); + status.statusCode = Kitsunemimi::Hanami::INTERNAL_SERVER_ERROR_RTYPE; + return false; + } + + // remove irrelevant fields + blossomIO.output.remove("temp_files"); + + return true; +} + +/** + * @brief get information from header of file + * + * @param result reference for result-output + * @param location location of the file to read + * @param error reference for error-output + * + * @return true, if successful, else false + */ +bool +GetDataSet::getHeaderInformation(Kitsunemimi::JsonItem &result, + const std::string &location, + Kitsunemimi::ErrorContainer &error) +{ + bool ret = false; + + DataSetFile* file = readDataSetFile(location); + if(file == nullptr) { + return ret; + } + + do + { + // read data-set-header + if(file->readFromFile() == false) + { + error.addMeesage("Failed to read header from file '" + location + "'"); + break; + } + + if(file->type == DataSetFile::IMAGE_TYPE) + { + ImageDataSetFile* imgF = dynamic_cast(file); + if(imgF == nullptr) { + break; + } + + // write information to result + const uint64_t size = imgF->imageHeader.numberOfInputsX * imgF->imageHeader.numberOfInputsY; + result.insert("inputs", static_cast(size)); + result.insert("outputs", static_cast(imgF->imageHeader.numberOfOutputs)); + result.insert("lines", static_cast(imgF->imageHeader.numberOfImages)); + // result.insert("average_value", static_cast(imgF->imageHeader.avgValue)); + // result.insert("max_value", static_cast(imgF->imageHeader.maxValue)); + + ret = true; + break; + } + else if(file->type == DataSetFile::TABLE_TYPE) + { + TableDataSetFile* imgT = dynamic_cast(file); + if(imgT == nullptr) { + break; + } + + long inputs = 0; + long outputs = 0; + + // get number of inputs and outputs + for(const DataSetFile::TableHeaderEntry &entry : imgT->tableColumns) + { + if(entry.isInput) { + inputs++; + } + if(entry.isOutput) { + outputs++; + } + } + + result.insert("inputs", inputs); + result.insert("outputs", outputs); + result.insert("lines", static_cast(imgT->tableHeader.numberOfLines)); + // result.insert("average_value", 0.0f); + // result.insert("max_value", 0.0f); + + ret = true; + break; + } + + // TODO: handle other types + break; + } + while(true); + + delete file; + + return ret; +} diff --git a/src/components/ShioriArchive/src/api/v1/data_files/get_data_set.h b/src/components/ShioriArchive/src/api/v1/data_files/get_data_set.h new file mode 100644 index 00000000..788bdca0 --- /dev/null +++ b/src/components/ShioriArchive/src/api/v1/data_files/get_data_set.h @@ -0,0 +1,45 @@ +/** + * @file get_data_set.h + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2022 Tobias Anker + * + * 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. + */ + +#ifndef SHIORIARCHIVE_GET_DATA_SET_H +#define SHIORIARCHIVE_GET_DATA_SET_H + +#include + +class GetDataSet + : public Kitsunemimi::Hanami::Blossom +{ +public: + GetDataSet(); + +protected: + bool runTask(Kitsunemimi::Hanami::BlossomIO &blossomIO, + const Kitsunemimi::DataMap &, + Kitsunemimi::Hanami::BlossomStatus &status, + Kitsunemimi::ErrorContainer &error); +private: + bool getHeaderInformation(Kitsunemimi::JsonItem &result, + const std::string &location, + Kitsunemimi::ErrorContainer &error); +}; + +#endif // SHIORIARCHIVE_GET_DATA_SET_H diff --git a/src/components/ShioriArchive/src/api/v1/data_files/get_progress_data_set.cpp b/src/components/ShioriArchive/src/api/v1/data_files/get_progress_data_set.cpp new file mode 100644 index 00000000..8af77a62 --- /dev/null +++ b/src/components/ShioriArchive/src/api/v1/data_files/get_progress_data_set.cpp @@ -0,0 +1,119 @@ +/** + * @file get_progress_data_set.cpp + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2022 Tobias Anker + * + * 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 "get_progress_data_set.h" + +#include +#include +#include +#include +#include + +#include +#include + +#include +#include + +using namespace Kitsunemimi::Hanami; + +GetProgressDataSet::GetProgressDataSet() + : Blossom("Get upload progress of a specific data-set.") +{ + //---------------------------------------------------------------------------------------------- + // input + //---------------------------------------------------------------------------------------------- + + registerInputField("uuid", + SAKURA_STRING_TYPE, + true, + "UUID of the dataset set to delete."); + assert(addFieldRegex("uuid", UUID_REGEX)); + + //---------------------------------------------------------------------------------------------- + // output + //---------------------------------------------------------------------------------------------- + + registerOutputField("uuid", + SAKURA_STRING_TYPE, + "UUID of the data-set."); + registerOutputField("temp_files", + SAKURA_MAP_TYPE, + "Map with the uuids of the temporary files and it's upload progress"); + registerOutputField("complete", + SAKURA_BOOL_TYPE, + "True, if all temporary files for complete."); + + //---------------------------------------------------------------------------------------------- + // + //---------------------------------------------------------------------------------------------- +} + +/** + * @brief runTask + */ +bool +GetProgressDataSet::runTask(BlossomIO &blossomIO, + const Kitsunemimi::DataMap &context, + BlossomStatus &status, + Kitsunemimi::ErrorContainer &error) +{ + const std::string dataUuid = blossomIO.input.get("uuid").getString(); + const Kitsunemimi::Hanami::UserContext userContext(context); + + Kitsunemimi::JsonItem databaseOutput; + if(ShioriRoot::dataSetTable->getDataSet(databaseOutput, + dataUuid, + userContext, + error, + true) == false) + { + status.statusCode = Kitsunemimi::Hanami::INTERNAL_SERVER_ERROR_RTYPE; + return false; + } + + // add uuid + blossomIO.output.insert("uuid", databaseOutput.get("uuid")); + + // parse and add temp-file-information + const std::string tempFilesStr = databaseOutput.get("temp_files").toString(); + Kitsunemimi::JsonItem tempFiles; + if(tempFiles.parse(tempFilesStr, error) == false) + { + status.statusCode = Kitsunemimi::Hanami::INTERNAL_SERVER_ERROR_RTYPE; + return false; + } + blossomIO.output.insert("temp_files", tempFiles); + + // check and add if complete + const std::vector keys = tempFiles.getKeys(); + bool finishedAll = true; + for(uint32_t i = 0; i < keys.size(); i++) + { + if(tempFiles.get(keys.at(i)).getFloat() < 1.0f) { + finishedAll = false; + } + } + blossomIO.output.insert("complete", finishedAll); + + return true; +} diff --git a/src/components/ShioriArchive/src/api/v1/data_files/get_progress_data_set.h b/src/components/ShioriArchive/src/api/v1/data_files/get_progress_data_set.h new file mode 100644 index 00000000..60a08f03 --- /dev/null +++ b/src/components/ShioriArchive/src/api/v1/data_files/get_progress_data_set.h @@ -0,0 +1,42 @@ +/** + * @file get_progress_data_set.h + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2022 Tobias Anker + * + * 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. + */ + +#ifndef SHIORIARCHIVE_GET_PROGRESS_DATA_SET_H +#define SHIORIARCHIVE_GET_PROGRESS_DATA_SET_H + +#include + +class GetProgressDataSet + : public Kitsunemimi::Hanami::Blossom +{ +public: + GetProgressDataSet(); + +protected: + bool runTask(Kitsunemimi::Hanami::BlossomIO &blossomIO, + const Kitsunemimi::DataMap &, + Kitsunemimi::Hanami::BlossomStatus &status, + Kitsunemimi::ErrorContainer &error); +}; + + +#endif // SHIORIARCHIVE_GET_PROGRESS_DATA_SET_H diff --git a/src/components/ShioriArchive/src/api/v1/data_files/list_data_set.cpp b/src/components/ShioriArchive/src/api/v1/data_files/list_data_set.cpp new file mode 100644 index 00000000..b8030172 --- /dev/null +++ b/src/components/ShioriArchive/src/api/v1/data_files/list_data_set.cpp @@ -0,0 +1,81 @@ +/** + * @file list_data_set.cpp + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2022 Tobias Anker + * + * 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 "list_data_set.h" + +#include +#include + +#include + +using namespace Kitsunemimi::Hanami; + +ListDataSet::ListDataSet() + : Blossom("List all visible data-sets.") +{ + //---------------------------------------------------------------------------------------------- + // output + //---------------------------------------------------------------------------------------------- + + registerOutputField("header", + SAKURA_ARRAY_TYPE, + "Array with the namings all columns of the table."); + assert(addFieldMatch("header", new Kitsunemimi::DataValue("[\"uuid\"," + "\"project_id\"," + "\"owner_id\"," + "\"visibility\"," + "\"name\"," + "\"type\"]"))); + + registerOutputField("body", + SAKURA_ARRAY_TYPE, + "Array with all rows of the table, which array arrays too."); + + //---------------------------------------------------------------------------------------------- + // + //---------------------------------------------------------------------------------------------- +} + +/** + * @brief runTask + */ +bool +ListDataSet::runTask(BlossomIO &blossomIO, + const Kitsunemimi::DataMap &context, + BlossomStatus &status, + Kitsunemimi::ErrorContainer &error) +{ + const Kitsunemimi::Hanami::UserContext userContext(context); + + // get data from table + Kitsunemimi::TableItem table; + if(ShioriRoot::dataSetTable->getAllDataSet(table, userContext, error) == false) + { + status.statusCode = Kitsunemimi::Hanami::INTERNAL_SERVER_ERROR_RTYPE; + return false; + } + + blossomIO.output.insert("header", table.getInnerHeader()); + blossomIO.output.insert("body", table.getBody()); + + return true; +} diff --git a/src/components/ShioriArchive/src/api/v1/data_files/list_data_set.h b/src/components/ShioriArchive/src/api/v1/data_files/list_data_set.h new file mode 100644 index 00000000..66e09527 --- /dev/null +++ b/src/components/ShioriArchive/src/api/v1/data_files/list_data_set.h @@ -0,0 +1,41 @@ +/** + * @file list_data_set.h + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2022 Tobias Anker + * + * 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. + */ + +#ifndef SHIORIARCHIVE_LIST_DATA_SET_H +#define SHIORIARCHIVE_LIST_DATA_SET_H + +#include + +class ListDataSet + : public Kitsunemimi::Hanami::Blossom +{ +public: + ListDataSet(); + +protected: + bool runTask(Kitsunemimi::Hanami::BlossomIO &blossomIO, + const Kitsunemimi::DataMap &context, + Kitsunemimi::Hanami::BlossomStatus &status, + Kitsunemimi::ErrorContainer &error); +}; + +#endif // SHIORIARCHIVE_LIST_DATA_SET_H diff --git a/src/components/ShioriArchive/src/api/v1/data_files/mnist/create_mnist_data_set.cpp b/src/components/ShioriArchive/src/api/v1/data_files/mnist/create_mnist_data_set.cpp new file mode 100644 index 00000000..64896745 --- /dev/null +++ b/src/components/ShioriArchive/src/api/v1/data_files/mnist/create_mnist_data_set.cpp @@ -0,0 +1,179 @@ +/** + * @file create_mnist_data_set.cpp + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2022 Tobias Anker + * + * 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 "create_mnist_data_set.h" + +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +using namespace Kitsunemimi::Hanami; + +CreateMnistDataSet::CreateMnistDataSet() + : Blossom("Init new mnist-file data-set.") +{ + //---------------------------------------------------------------------------------------------- + // input + //---------------------------------------------------------------------------------------------- + + registerInputField("name", + SAKURA_STRING_TYPE, + true, + "Name of the new set."); + assert(addFieldBorder("name", 4, 256)); + assert(addFieldRegex("name", NAME_REGEX)); + + registerInputField("input_data_size", + SAKURA_INT_TYPE, + true, + "Total size of the input-data."); + assert(addFieldBorder("input_data_size", 1, 10000000000)); + + registerInputField("label_data_size", + SAKURA_INT_TYPE, + true, + "Total size of the label-data."); + assert(addFieldBorder("label_data_size", 1, 10000000000)); + + //---------------------------------------------------------------------------------------------- + // output + //---------------------------------------------------------------------------------------------- + + registerOutputField("uuid", + SAKURA_STRING_TYPE, + "UUID of the new data-set."); + registerOutputField("name", + SAKURA_STRING_TYPE, + "Name of the new data-set."); + registerOutputField("owner_id", + SAKURA_STRING_TYPE, + "ID of the user, who created the data-set."); + registerOutputField("project_id", + SAKURA_STRING_TYPE, + "ID of the project, where the data-set belongs to."); + registerOutputField("visibility", + SAKURA_STRING_TYPE, + "Visibility of the data-set (private, shared, public)."); + registerOutputField("type", + SAKURA_STRING_TYPE, + "Type of the new set (mnist)"); + registerOutputField("uuid_input_file", + SAKURA_STRING_TYPE, + "UUID to identify the file for date upload of input-data."); + registerOutputField("uuid_label_file", + SAKURA_STRING_TYPE, + "UUID to identify the file for date upload of label-data."); + + //---------------------------------------------------------------------------------------------- + // + //---------------------------------------------------------------------------------------------- +} + +bool +CreateMnistDataSet::runTask(BlossomIO &blossomIO, + const Kitsunemimi::DataMap &context, + BlossomStatus &status, + Kitsunemimi::ErrorContainer &error) +{ + const std::string name = blossomIO.input.get("name").getString(); + const long inputDataSize = blossomIO.input.get("input_data_size").getLong(); + const long labelDataSize = blossomIO.input.get("label_data_size").getLong(); + const std::string uuid = Kitsunemimi::Hanami::generateUuid().toString(); + const Kitsunemimi::Hanami::UserContext userContext(context); + + // get directory to store data from config + bool success = false; + std::string targetFilePath = GET_STRING_CONFIG("shiori", "data_set_location", success); + if(success == false) + { + status.statusCode = Kitsunemimi::Hanami::INTERNAL_SERVER_ERROR_RTYPE; + error.addMeesage("file-location to store dataset is missing in the config"); + return false; + } + + // init temp-file for input-data + const std::string inputUuid = Kitsunemimi::Hanami::generateUuid().toString(); + if(ShioriRoot::tempFileHandler->initNewFile(inputUuid, inputDataSize) == false) + { + status.statusCode = Kitsunemimi::Hanami::INTERNAL_SERVER_ERROR_RTYPE; + error.addMeesage("Failed to initialize temporary file for new input-data."); + return false; + } + + // init temp-file for label-data + const std::string labelUuid = Kitsunemimi::Hanami::generateUuid().toString(); + if(ShioriRoot::tempFileHandler->initNewFile(labelUuid, labelDataSize) == false) + { + status.statusCode = Kitsunemimi::Hanami::INTERNAL_SERVER_ERROR_RTYPE; + error.addMeesage("Failed to initialize temporary file for new label-data."); + return false; + } + + // build absolut file-path to store the file + if(targetFilePath.at(targetFilePath.size() - 1) != '/') { + targetFilePath.append("/"); + } + targetFilePath.append(uuid + "_mnist_" + userContext.userId); + + // register in database + blossomIO.output.insert("uuid", uuid); + blossomIO.output.insert("name", name); + blossomIO.output.insert("type", "mnist"); + blossomIO.output.insert("location", targetFilePath); + blossomIO.output.insert("project_id", userContext.projectId); + blossomIO.output.insert("owner_id", userContext.userId); + blossomIO.output.insert("visibility", "private"); + + // init placeholder for temp-file progress to database + Kitsunemimi::JsonItem tempFiles; + tempFiles.insert(inputUuid, Kitsunemimi::JsonItem(0.0f)); + tempFiles.insert(labelUuid, Kitsunemimi::JsonItem(0.0f)); + blossomIO.output.insert("temp_files", tempFiles); + + // add to database + if(ShioriRoot::dataSetTable->addDataSet(blossomIO.output, userContext, error) == false) + { + status.statusCode = Kitsunemimi::Hanami::INTERNAL_SERVER_ERROR_RTYPE; + return false; + } + + // add values to output + blossomIO.output.insert("uuid_input_file", inputUuid); + blossomIO.output.insert("uuid_label_file", labelUuid); + + // remove blocked values from output + blossomIO.output.remove("location"); + blossomIO.output.remove("temp_files"); + + return true; +} diff --git a/src/components/ShioriArchive/src/api/v1/data_files/mnist/create_mnist_data_set.h b/src/components/ShioriArchive/src/api/v1/data_files/mnist/create_mnist_data_set.h new file mode 100644 index 00000000..9d4796a1 --- /dev/null +++ b/src/components/ShioriArchive/src/api/v1/data_files/mnist/create_mnist_data_set.h @@ -0,0 +1,41 @@ +/** + * @file create_mnist_data_set.h + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2022 Tobias Anker + * + * 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. + */ + +#ifndef SHIORIARCHIVE_MNIST_CREATE_DATA_SET_H +#define SHIORIARCHIVE_MNIST_CREATE_DATA_SET_H + +#include + +class CreateMnistDataSet + : public Kitsunemimi::Hanami::Blossom +{ +public: + CreateMnistDataSet(); + +protected: + bool runTask(Kitsunemimi::Hanami::BlossomIO &blossomIO, + const Kitsunemimi::DataMap &context, + Kitsunemimi::Hanami::BlossomStatus &status, + Kitsunemimi::ErrorContainer &error); +}; + +#endif // SHIORIARCHIVE_MNIST_CREATE_DATA_SET_H diff --git a/src/components/ShioriArchive/src/api/v1/data_files/mnist/finalize_mnist_data_set.cpp b/src/components/ShioriArchive/src/api/v1/data_files/mnist/finalize_mnist_data_set.cpp new file mode 100644 index 00000000..a98fd14f --- /dev/null +++ b/src/components/ShioriArchive/src/api/v1/data_files/mnist/finalize_mnist_data_set.cpp @@ -0,0 +1,269 @@ +/** + * @file finalize_mnist_data_set.cpp + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2022 Tobias Anker + * + * 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 "finalize_mnist_data_set.h" + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +using namespace Kitsunemimi::Hanami; + +FinalizeMnistDataSet::FinalizeMnistDataSet() + : Blossom("Finalize uploaded data-set by checking completeness of the " + "uploaded and convert into generic format.") +{ + //---------------------------------------------------------------------------------------------- + // input + //---------------------------------------------------------------------------------------------- + + registerInputField("uuid", + SAKURA_STRING_TYPE, + true, + "UUID of the new data-set."); + assert(addFieldRegex("uuid", UUID_REGEX)); + registerInputField("uuid_input_file", + SAKURA_STRING_TYPE, + true, + "UUID to identify the file for date upload of input-data."); + assert(addFieldRegex("uuid_input_file", UUID_REGEX)); + registerInputField("uuid_label_file", + SAKURA_STRING_TYPE, + true, + "UUID to identify the file for date upload of label-data."); + assert(addFieldRegex("uuid_label_file", UUID_REGEX)); + + //---------------------------------------------------------------------------------------------- + // output + //---------------------------------------------------------------------------------------------- + + registerOutputField("uuid", + SAKURA_STRING_TYPE, + "UUID of the new data-set."); + + //---------------------------------------------------------------------------------------------- + // + //---------------------------------------------------------------------------------------------- +} + +/** + * @brief runTask + */ +bool +FinalizeMnistDataSet::runTask(BlossomIO &blossomIO, + const Kitsunemimi::DataMap &context, + BlossomStatus &status, + Kitsunemimi::ErrorContainer &error) +{ + const std::string uuid = blossomIO.input.get("uuid").getString(); + const std::string inputUuid = blossomIO.input.get("uuid_input_file").getString(); + const std::string labelUuid = blossomIO.input.get("uuid_label_file").getString(); + const Kitsunemimi::Hanami::UserContext userContext(context); + + // get location from database + Kitsunemimi::JsonItem result; + if(ShioriRoot::dataSetTable->getDataSet(result, uuid, userContext, error, true) == false) + { + status.errorMessage = "Data with uuid '" + uuid + "' not found."; + status.statusCode = Kitsunemimi::Hanami::NOT_FOUND_RTYPE; + return false; + } + + // read input-data from temp-file + Kitsunemimi::DataBuffer inputBuffer; + if(ShioriRoot::tempFileHandler->getData(inputBuffer, inputUuid) == false) + { + status.errorMessage = "Input-data with uuid '" + inputUuid + "' not found."; + status.statusCode = Kitsunemimi::Hanami::NOT_FOUND_RTYPE; + return false; + } + + // read label from temp-file + Kitsunemimi::DataBuffer labelBuffer; + if(ShioriRoot::tempFileHandler->getData(labelBuffer, labelUuid) == false) + { + status.errorMessage = "Label-data with uuid '" + inputUuid + "' not found."; + status.statusCode = Kitsunemimi::Hanami::NOT_FOUND_RTYPE; + return false; + } + + // write data to file + if(convertMnistData(result.get("location").getString(), + result.get("name").getString().c_str(), + inputBuffer, + labelBuffer) == false) + { + status.statusCode =Kitsunemimi:: Hanami::INTERNAL_SERVER_ERROR_RTYPE; + error.addMeesage("Failed to convert mnist-data"); + return false; + } + + // delete temp-files + ShioriRoot::tempFileHandler->removeData(inputUuid); + ShioriRoot::tempFileHandler->removeData(labelUuid); + + // create output + blossomIO.output.insert("uuid", uuid); + + return true; +} + +/** + * @brief convert mnist-data into generic format + * + * @param filePath path to the resulting file + * @param name data-set name + * @param inputBuffer buffer with input-data + * @param labelBuffer buffer with label-data + * + * @return true, if successfull, else false + */ +bool +FinalizeMnistDataSet::convertMnistData(const std::string &filePath, + const std::string &name, + const Kitsunemimi::DataBuffer &inputBuffer, + const Kitsunemimi::DataBuffer &labelBuffer) +{ + ImageDataSetFile file(filePath); + file.type = DataSetFile::IMAGE_TYPE; + file.name = name; + + // source-data + const uint64_t dataOffset = 16; + const uint64_t labelOffset = 8; + const uint8_t* dataBufferPtr = static_cast(inputBuffer.data); + const uint8_t* labelBufferPtr = static_cast(labelBuffer.data); + + // get number of images + uint32_t numberOfImages = 0; + numberOfImages |= dataBufferPtr[7]; + numberOfImages |= static_cast(dataBufferPtr[6]) << 8; + numberOfImages |= static_cast(dataBufferPtr[5]) << 16; + numberOfImages |= static_cast(dataBufferPtr[4]) << 24; + + // get number of rows + uint32_t numberOfRows = 0; + numberOfRows |= dataBufferPtr[11]; + numberOfRows |= static_cast(dataBufferPtr[10]) << 8; + numberOfRows |= static_cast(dataBufferPtr[9]) << 16; + numberOfRows |= static_cast(dataBufferPtr[8]) << 24; + + // get number of columns + uint32_t numberOfColumns = 0; + numberOfColumns |= dataBufferPtr[15]; + numberOfColumns |= static_cast(dataBufferPtr[14]) << 8; + numberOfColumns |= static_cast(dataBufferPtr[13]) << 16; + numberOfColumns |= static_cast(dataBufferPtr[12]) << 24; + + // set information in header + file.imageHeader.numberOfInputsX = numberOfColumns; + file.imageHeader.numberOfInputsY = numberOfRows; + // TODO: read number of labels from file + file.imageHeader.numberOfOutputs = 10; + file.imageHeader.numberOfImages = numberOfImages; + + // buffer for values to reduce write-access to file + const uint64_t lineSize = (numberOfColumns * numberOfRows) * 10; + const uint32_t segmentSize = lineSize * 10000; + std::vector segment(segmentSize, 0.0f); + uint64_t segmentPos = 0; + uint64_t segmentCounter = 0; + + // init file + if(file.initNewFile() == false) { + return false; + } + + // get pictures + const uint32_t pictureSize = numberOfRows * numberOfColumns; + double averageVal = 0.0f; + uint64_t valueCounter = 0; + float maxVal = 0.0f; + + // copy values of each pixel into the resulting file + for(uint32_t pic = 0; pic < numberOfImages; pic++) + { + // input + for(uint32_t i = 0; i < pictureSize; i++) + { + const uint32_t pos = pic * pictureSize + i + dataOffset; + segment[segmentPos] = static_cast(dataBufferPtr[pos]); + + // update values for metadata + averageVal += segment[segmentPos]; + valueCounter++; + if(maxVal < segment[segmentPos]) { + maxVal = segment[segmentPos]; + } + + segmentPos++; + } + + // label + for(uint32_t i = 0; i < 10; i++) + { + segment[segmentPos] = 0.0f; + segmentPos++; + } + const uint32_t label = labelBufferPtr[pic + labelOffset]; + segment[(segmentPos - 10) + label] = 1; + + // write line to file, if segment is full + if(segmentPos == segmentSize) + { + file.addBlock(segmentCounter * segmentSize, &segment[0], segmentSize); + segmentPos = 0; + segmentCounter++; + } + } + + // write last incomplete segment to file + if(segmentPos != 0) { + file.addBlock(segmentCounter * segmentSize, &segment[0], segmentPos); + } + + // write additional information to header + file.imageHeader.avgValue = averageVal / static_cast(valueCounter); + file.imageHeader.maxValue = maxVal; + + // update header in file for the final number of lines for the case, + // that there were invalid lines + if(file.updateHeader() == false) { + return false; + } + + return true; +} + diff --git a/src/components/ShioriArchive/src/api/v1/data_files/mnist/finalize_mnist_data_set.h b/src/components/ShioriArchive/src/api/v1/data_files/mnist/finalize_mnist_data_set.h new file mode 100644 index 00000000..1f80e089 --- /dev/null +++ b/src/components/ShioriArchive/src/api/v1/data_files/mnist/finalize_mnist_data_set.h @@ -0,0 +1,48 @@ +/** + * @file finalize_mnist_data_set.h + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2022 Tobias Anker + * + * 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. + */ + +#ifndef SHIORIARCHIVE_MNIST_FINALIZE_DATA_SET_H +#define SHIORIARCHIVE_MNIST_FINALIZE_DATA_SET_H + +#include +#include + +class FinalizeMnistDataSet + : public Kitsunemimi::Hanami::Blossom +{ +public: + FinalizeMnistDataSet(); + +protected: + bool runTask(Kitsunemimi::Hanami::BlossomIO &blossomIO, + const Kitsunemimi::DataMap &context, + Kitsunemimi::Hanami::BlossomStatus &status, + Kitsunemimi::ErrorContainer &error); + +private: + bool convertMnistData(const std::string &filePath, + const std::string &name, + const Kitsunemimi::DataBuffer &inputBuffer, + const Kitsunemimi::DataBuffer &labelBuffer); +}; + +#endif // SHIORIARCHIVE_MNIST_FINALIZE_DATA_SET_H diff --git a/src/components/ShioriArchive/src/api/v1/logs/get_audit_log.cpp b/src/components/ShioriArchive/src/api/v1/logs/get_audit_log.cpp new file mode 100644 index 00000000..2850de35 --- /dev/null +++ b/src/components/ShioriArchive/src/api/v1/logs/get_audit_log.cpp @@ -0,0 +1,116 @@ +/** + * @file get_audit_log.cpp + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2022 Tobias Anker + * + * 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 "get_audit_log.h" + +#include +#include + +#include +#include +#include + +using namespace Kitsunemimi::Hanami; + +GetAuditLog::GetAuditLog() + : Blossom("Get audit-log of a user.") +{ + //---------------------------------------------------------------------------------------------- + // input + //---------------------------------------------------------------------------------------------- + registerInputField("user_id", + SAKURA_STRING_TYPE, + false, + "ID of the user, whos entries are requested. Only an admin is allowed to " + "set this values. Any other user get only its own log output based on the " + "token-context."); + assert(addFieldBorder("user_id", 4, 256)); + assert(addFieldRegex("user_id", ID_EXT_REGEX)); + + registerInputField("page", + SAKURA_INT_TYPE, + true, + "Page-number starting with 0 to access the logs. " + "A page has up to 100 entries."); + assert(addFieldBorder("page", 0, 1000000000)); + + //---------------------------------------------------------------------------------------------- + // output + //---------------------------------------------------------------------------------------------- + + registerOutputField("header", + SAKURA_ARRAY_TYPE, + "Array with the namings all columns of the table."); + assert(addFieldMatch("header", new Kitsunemimi::DataValue("[\"timestamp\"," + "\"user_id\"," + "\"component\"," + "\"endpoint\"," + "\"request_type\"]"))); + registerOutputField("body", + SAKURA_ARRAY_TYPE, + "Array with all rows of the table, which array arrays too."); + + //---------------------------------------------------------------------------------------------- + // + //---------------------------------------------------------------------------------------------- +} + +/** + * @brief runTask + */ +bool +GetAuditLog::runTask(BlossomIO &blossomIO, + const Kitsunemimi::DataMap &context, + BlossomStatus &status, + Kitsunemimi::ErrorContainer &error) +{ + const Kitsunemimi::Hanami::UserContext userContext(context); + std::string userId = blossomIO.input.get("user_id").getString(); + const uint64_t page = blossomIO.input.get("page").getLong(); + + // check that if user-id is set, that the user is also an admin + if(userContext.isAdmin == false + && userId.length() != 0) + { + status.statusCode = Kitsunemimi::Hanami::UNAUTHORIZED_RTYPE; + status.errorMessage = "'user_id' can only be set by an admin"; + return false; + } + + // if no user-id was defined, use the id of the context + if(userId.length() == 0) { + userId = userContext.userId; + } + + // get data from table + Kitsunemimi::TableItem table; + if(ShioriRoot::auditLogTable->getAllAuditLogEntries(table, userId, page, error) == false) + { + status.statusCode = Kitsunemimi::Hanami::INTERNAL_SERVER_ERROR_RTYPE; + return false; + } + + blossomIO.output.insert("header", table.getInnerHeader()); + blossomIO.output.insert("body", table.getBody()); + + return true; +} diff --git a/src/components/ShioriArchive/src/api/v1/logs/get_audit_log.h b/src/components/ShioriArchive/src/api/v1/logs/get_audit_log.h new file mode 100644 index 00000000..51436f7b --- /dev/null +++ b/src/components/ShioriArchive/src/api/v1/logs/get_audit_log.h @@ -0,0 +1,41 @@ +/** + * @file get_audit_log.h + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2022 Tobias Anker + * + * 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. + */ + +#ifndef GETAUDITLOG_H +#define GETAUDITLOG_H + +#include + +class GetAuditLog + : public Kitsunemimi::Hanami::Blossom +{ +public: + GetAuditLog(); + +protected: + bool runTask(Kitsunemimi::Hanami::BlossomIO &blossomIO, + const Kitsunemimi::DataMap &, + Kitsunemimi::Hanami::BlossomStatus &status, + Kitsunemimi::ErrorContainer &error); +}; + +#endif // GETAUDITLOG_H diff --git a/src/components/ShioriArchive/src/api/v1/logs/get_error_log.cpp b/src/components/ShioriArchive/src/api/v1/logs/get_error_log.cpp new file mode 100644 index 00000000..a318f343 --- /dev/null +++ b/src/components/ShioriArchive/src/api/v1/logs/get_error_log.cpp @@ -0,0 +1,110 @@ +/** + * @file get_audit_log.cpp + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2022 Tobias Anker + * + * 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 "get_error_log.h" + +#include +#include + +#include +#include +#include + +using namespace Kitsunemimi::Hanami; + +GetErrorLog::GetErrorLog() + : Blossom("Get error-log of a user. Only an admin is allowed to request the error-log.") +{ + //---------------------------------------------------------------------------------------------- + // input + //---------------------------------------------------------------------------------------------- + registerInputField("user_id", + SAKURA_STRING_TYPE, + false, + "ID of the user, whos entries are requested."); + assert(addFieldBorder("user_id", 4, 256)); + assert(addFieldRegex("user_id", ID_EXT_REGEX)); + + registerInputField("page", + SAKURA_INT_TYPE, + true, + "Page-number starting with 0 to access the logs. " + "A page has up to 100 entries."); + assert(addFieldBorder("page", 0, 1000000000)); + + //---------------------------------------------------------------------------------------------- + // output + //---------------------------------------------------------------------------------------------- + + registerOutputField("header", + SAKURA_ARRAY_TYPE, + "Array with the namings all columns of the table."); + assert(addFieldMatch("header", new Kitsunemimi::DataValue("[\"timestamp\"," + "\"user_id\"," + "\"component\"," + "\"context\"," + "\"input_values\"," + "\"message\"]"))); + registerOutputField("body", + SAKURA_ARRAY_TYPE, + "Array with all rows of the table, which array arrays too."); + + //---------------------------------------------------------------------------------------------- + // + //---------------------------------------------------------------------------------------------- +} + +/** + * @brief runTask + */ +bool +GetErrorLog::runTask(BlossomIO &blossomIO, + const Kitsunemimi::DataMap &context, + BlossomStatus &status, + Kitsunemimi::ErrorContainer &error) +{ + const Kitsunemimi::Hanami::UserContext userContext(context); + const uint64_t page = blossomIO.input.get("page").getLong(); + + // check that the user is an admin + if(userContext.isAdmin == false) + { + status.statusCode = Kitsunemimi::Hanami::UNAUTHORIZED_RTYPE; + status.errorMessage = "only an admin is allowed to request error-logs"; + return false; + } + + const std::string userId = blossomIO.input.get("user_id").getString(); + + // get data from table + Kitsunemimi::TableItem table; + if(ShioriRoot::errorLogTable->getAllErrorLogEntries(table, userId, page, error) == false) + { + status.statusCode = Kitsunemimi::Hanami::INTERNAL_SERVER_ERROR_RTYPE; + return false; + } + + blossomIO.output.insert("header", table.getInnerHeader()); + blossomIO.output.insert("body", table.getBody()); + + return true; +} diff --git a/src/components/ShioriArchive/src/api/v1/logs/get_error_log.h b/src/components/ShioriArchive/src/api/v1/logs/get_error_log.h new file mode 100644 index 00000000..e0fee5a1 --- /dev/null +++ b/src/components/ShioriArchive/src/api/v1/logs/get_error_log.h @@ -0,0 +1,41 @@ +/** + * @file get_audit_log.h + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2022 Tobias Anker + * + * 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. + */ + +#ifndef GETERRORLOG_H +#define GETERRORLOG_H + +#include + +class GetErrorLog + : public Kitsunemimi::Hanami::Blossom +{ +public: + GetErrorLog(); + +protected: + bool runTask(Kitsunemimi::Hanami::BlossomIO &blossomIO, + const Kitsunemimi::DataMap &, + Kitsunemimi::Hanami::BlossomStatus &status, + Kitsunemimi::ErrorContainer &error); +}; + +#endif // GETERRORLOG_H diff --git a/src/components/ShioriArchive/src/api/v1/request_results/delete_request_result.cpp b/src/components/ShioriArchive/src/api/v1/request_results/delete_request_result.cpp new file mode 100644 index 00000000..1be6b2de --- /dev/null +++ b/src/components/ShioriArchive/src/api/v1/request_results/delete_request_result.cpp @@ -0,0 +1,85 @@ +/** + * @file delete_request_result.cpp + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2022 Tobias Anker + * + * 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 "delete_request_result.h" + +#include +#include + +#include +#include + +using namespace Kitsunemimi; + +DeleteRequestResult::DeleteRequestResult() + : Blossom("Delete a request-result from shiori.") +{ + //---------------------------------------------------------------------------------------------- + // input + //---------------------------------------------------------------------------------------------- + + registerInputField("uuid", + Hanami::SAKURA_STRING_TYPE, + true, + "UUID of the original request-task, which placed the result in shiori."); + assert(addFieldRegex("uuid", UUID_REGEX)); + + //---------------------------------------------------------------------------------------------- + // + //---------------------------------------------------------------------------------------------- +} + +/** + * @brief runTask + */ +bool +DeleteRequestResult::runTask(Hanami::BlossomIO &blossomIO, + const Kitsunemimi::DataMap &context, + Hanami::BlossomStatus &status, + ErrorContainer &error) +{ + const std::string uuid = blossomIO.input.get("uuid").getString(); + const Kitsunemimi::Hanami::UserContext userContext(context); + + // check if request-result exist within the table + JsonItem result; + if(ShioriRoot::requestResultTable->getRequestResult(result, + uuid, + userContext, + error, + false) == false) + { + status.errorMessage = "Request-result with UUID '" + uuid + "' not found."; + status.statusCode = Kitsunemimi::Hanami::NOT_FOUND_RTYPE; + error.addMeesage(status.errorMessage); + return false; + } + + // delete entry from db + if(ShioriRoot::requestResultTable->deleteRequestResult(uuid, userContext, error) == false) + { + status.statusCode = Hanami::INTERNAL_SERVER_ERROR_RTYPE; + return false; + } + + return true; +} diff --git a/src/components/ShioriArchive/src/api/v1/request_results/delete_request_result.h b/src/components/ShioriArchive/src/api/v1/request_results/delete_request_result.h new file mode 100644 index 00000000..8a80bf30 --- /dev/null +++ b/src/components/ShioriArchive/src/api/v1/request_results/delete_request_result.h @@ -0,0 +1,41 @@ +/** + * @file delete_request_result.h + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2022 Tobias Anker + * + * 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. + */ + +#ifndef DELETEREQUESTRESULT_H +#define DELETEREQUESTRESULT_H + +#include + +class DeleteRequestResult + : public Kitsunemimi::Hanami::Blossom +{ +public: + DeleteRequestResult(); + +protected: + bool runTask(Kitsunemimi::Hanami::BlossomIO &blossomIO, + const Kitsunemimi::DataMap &context, + Kitsunemimi::Hanami::BlossomStatus &status, + Kitsunemimi::ErrorContainer &error); +}; + +#endif // DELETEREQUESTRESULT_H diff --git a/src/components/ShioriArchive/src/api/v1/request_results/get_request_result.cpp b/src/components/ShioriArchive/src/api/v1/request_results/get_request_result.cpp new file mode 100644 index 00000000..1be39cff --- /dev/null +++ b/src/components/ShioriArchive/src/api/v1/request_results/get_request_result.cpp @@ -0,0 +1,100 @@ +/** + * @file get_request_result.cpp + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2022 Tobias Anker + * + * 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 "get_request_result.h" + +#include +#include + +#include +#include + +using namespace Kitsunemimi::Hanami; + +GetRequestResult::GetRequestResult() + : Blossom("Get a specific request-result") +{ + //---------------------------------------------------------------------------------------------- + // input + //---------------------------------------------------------------------------------------------- + + registerInputField("uuid", + SAKURA_STRING_TYPE, + true, + "UUID of the original request-task, which placed the result in shiori."); + assert(addFieldRegex("uuid", UUID_REGEX)); + + //---------------------------------------------------------------------------------------------- + // output + //---------------------------------------------------------------------------------------------- + + registerOutputField("uuid", + SAKURA_STRING_TYPE, + "UUID of the request-result."); + registerOutputField("name", + SAKURA_STRING_TYPE, + "Name of the request-result."); + registerOutputField("owner_id", + SAKURA_STRING_TYPE, + "ID of the user, who created the request-result."); + registerOutputField("project_id", + SAKURA_STRING_TYPE, + "ID of the project, where the request-result belongs to."); + registerOutputField("visibility", + SAKURA_STRING_TYPE, + "Visibility of the request-result (private, shared, public)."); + registerOutputField("data", + SAKURA_ARRAY_TYPE, + "Result of the request-task as json-array."); + + //---------------------------------------------------------------------------------------------- + // + //---------------------------------------------------------------------------------------------- +} + +/** + * @brief runTask + */ +bool +GetRequestResult::runTask(BlossomIO &blossomIO, + const Kitsunemimi::DataMap &context, + BlossomStatus &status, + Kitsunemimi::ErrorContainer &error) +{ + const std::string uuid = blossomIO.input.get("uuid").getString(); + const Kitsunemimi::Hanami::UserContext userContext(context); + + // check if request-result exist within the table + if(ShioriRoot::requestResultTable->getRequestResult(blossomIO.output, + uuid, + userContext, + error, + true) == false) + { + status.errorMessage = "Request-result with UUID '" + uuid + "' not found."; + status.statusCode = Kitsunemimi::Hanami::NOT_FOUND_RTYPE; + error.addMeesage(status.errorMessage); + return false; + } + + return true; +} diff --git a/src/components/ShioriArchive/src/api/v1/request_results/get_request_result.h b/src/components/ShioriArchive/src/api/v1/request_results/get_request_result.h new file mode 100644 index 00000000..8024db11 --- /dev/null +++ b/src/components/ShioriArchive/src/api/v1/request_results/get_request_result.h @@ -0,0 +1,41 @@ +/** + * @file get_request_result.h + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2022 Tobias Anker + * + * 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. + */ + +#ifndef GETREQUESTRESULT_H +#define GETREQUESTRESULT_H + +#include + +class GetRequestResult + : public Kitsunemimi::Hanami::Blossom +{ +public: + GetRequestResult(); + +protected: + bool runTask(Kitsunemimi::Hanami::BlossomIO &blossomIO, + const Kitsunemimi::DataMap &context, + Kitsunemimi::Hanami::BlossomStatus &status, + Kitsunemimi::ErrorContainer &error); +}; + +#endif // GETREQUESTRESULT_H diff --git a/src/components/ShioriArchive/src/api/v1/request_results/list_request_result.cpp b/src/components/ShioriArchive/src/api/v1/request_results/list_request_result.cpp new file mode 100644 index 00000000..85e0c3f3 --- /dev/null +++ b/src/components/ShioriArchive/src/api/v1/request_results/list_request_result.cpp @@ -0,0 +1,80 @@ +/** + * @file list_request_result.cpp + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2022 Tobias Anker + * + * 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 "list_request_result.h" + +#include +#include + +#include + +using namespace Kitsunemimi::Hanami; + +ListRequestResult::ListRequestResult() + : Blossom("List all visilbe request-results.") +{ + //---------------------------------------------------------------------------------------------- + // output + //---------------------------------------------------------------------------------------------- + + registerOutputField("header", + SAKURA_ARRAY_TYPE, + "Array with the namings all columns of the table."); + assert(addFieldMatch("header", new Kitsunemimi::DataValue("[\"uuid\"," + "\"project_id\"," + "\"owner_id\"," + "\"visibility\"," + "\"name\"]"))); + + registerOutputField("body", + SAKURA_ARRAY_TYPE, + "Array with all rows of the table, which array arrays too."); + + //---------------------------------------------------------------------------------------------- + // + //---------------------------------------------------------------------------------------------- +} + +/** + * @brief runTask + */ +bool +ListRequestResult::runTask(BlossomIO &blossomIO, + const Kitsunemimi::DataMap &context, + BlossomStatus &status, + Kitsunemimi::ErrorContainer &error) +{ + const Kitsunemimi::Hanami::UserContext userContext(context); + + // get data from table + Kitsunemimi::TableItem table; + if(ShioriRoot::requestResultTable->getAllRequestResult(table, userContext, error) == false) + { + status.statusCode = Kitsunemimi::Hanami::INTERNAL_SERVER_ERROR_RTYPE; + return false; + } + + blossomIO.output.insert("header", table.getInnerHeader()); + blossomIO.output.insert("body", table.getBody()); + + return true; +} diff --git a/src/components/ShioriArchive/src/api/v1/request_results/list_request_result.h b/src/components/ShioriArchive/src/api/v1/request_results/list_request_result.h new file mode 100644 index 00000000..69e2c48e --- /dev/null +++ b/src/components/ShioriArchive/src/api/v1/request_results/list_request_result.h @@ -0,0 +1,41 @@ +/** + * @file list_request_result.h + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2022 Tobias Anker + * + * 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. + */ + +#ifndef LISTREQUESTRESULT_H +#define LISTREQUESTRESULT_H + +#include + +class ListRequestResult + : public Kitsunemimi::Hanami::Blossom +{ +public: + ListRequestResult(); + +protected: + bool runTask(Kitsunemimi::Hanami::BlossomIO &blossomIO, + const Kitsunemimi::DataMap &, + Kitsunemimi::Hanami::BlossomStatus &status, + Kitsunemimi::ErrorContainer &error); +}; + +#endif // LISTREQUESTRESULT_H diff --git a/src/components/ShioriArchive/src/args.h b/src/components/ShioriArchive/src/args.h new file mode 100644 index 00000000..5e52c27f --- /dev/null +++ b/src/components/ShioriArchive/src/args.h @@ -0,0 +1,48 @@ +/** + * @file args.h + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2022 Tobias Anker + * + * 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. + */ + +#ifndef SHIORIARCHIVE_ARGS_H +#define SHIORIARCHIVE_ARGS_H + +#include +#include +#include + +/** + * @brief register cli-arguments + * + * @param argparser reference to argument parser + * + * @return true if successful, else false + */ +bool +registerArguments(Kitsunemimi::ArgParser* argparser, + Kitsunemimi::ErrorContainer &error) +{ + if(Kitsunemimi::Hanami::registerArguments(*argparser, error) == false) { + return false; + } + + return true; +} + +#endif // SHIORIARCHIVE_ARGS_H diff --git a/src/components/ShioriArchive/src/callbacks.h b/src/components/ShioriArchive/src/callbacks.h new file mode 100644 index 00000000..9b8a4734 --- /dev/null +++ b/src/components/ShioriArchive/src/callbacks.h @@ -0,0 +1,438 @@ +/** + * @file callbacks.h + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2022 Tobias Anker + * + * 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. + */ + +#ifndef SHIORIARCHIVE_CALLBACKS_H +#define SHIORIARCHIVE_CALLBACKS_H + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include + +#include <../../libraries/libKitsunemimiHanamiMessages/protobuffers/shiori_messages.proto3.pb.h> +#include <../../libraries/libKitsunemimiHanamiMessages/message_sub_types.h> + +/** + * @brief handleProtobufFileUpload + * @param data + * @param dataSize + * @return + */ +bool +handleProtobufFileUpload(Kitsunemimi::Sakura::Session* session, + const void* data, + const uint64_t dataSize) +{ + FileUpload_Message msg; + if(msg.ParseFromArray(data, dataSize) == false) + { + Kitsunemimi::ErrorContainer error; + error.addMeesage("Got invalid FileUpload-Message"); + LOG_ERROR(error); + return false; + } + + if(ShioriRoot::tempFileHandler->addDataToPos(msg.fileuuid(), + msg.position(), + msg.data().c_str(), + msg.data().size()) == false) + { + // TODO: error-handling + std::cout<<"failed to write data"<setUploadFinish(msg.datasetuuid(), + msg.fileuuid(), + error) == false) + { + // TODO: error-handling + return false; + } + } + + if(msg.type() == UploadDataType::CLUSTER_SNAPSHOT_TYPE) + { + if(ShioriRoot::clusterSnapshotTable->setUploadFinish(msg.datasetuuid(), + msg.fileuuid(), + error) == false) + { + // TODO: error-handling + return false; + } + } + + return true; +} + +/** + * @brief streamDataCallback + * @param data + * @param dataSize + */ +void streamDataCallback(void*, + Kitsunemimi::Sakura::Session* session, + const void* data, + const uint64_t dataSize) +{ + if(dataSize <= 40) { + return; + } + + handleProtobufFileUpload(session, data, dataSize); +} + +/** + * @brief get the current datetime of the system + * + * @return datetime as string + */ +const std::string +getDatetime() +{ + const time_t now = time(nullptr); + tm *ltm = localtime(&now); + + const std::string datatime = + std::to_string(1900 + ltm->tm_year) + + "-" + + std::to_string(1 + ltm->tm_mon) + + "-" + + std::to_string(ltm->tm_mday) + + " " + + std::to_string(ltm->tm_hour) + + ":" + + std::to_string(ltm->tm_min) + + ":" + + std::to_string(ltm->tm_sec); + + return datatime; +} + +/** + * @brief handle cluster-snapshot-message + * + * @param msg message to process + * @param session pointer to the session, which received the message + * @param blockerId blocker-id for the response + */ +inline void +handleClusterSnapshotRequest(const ClusterSnapshotPull_Message &msg, + Kitsunemimi::Sakura::Session* session, + const uint64_t blockerId) +{ + Kitsunemimi::ErrorContainer error; + + // init file + Kitsunemimi::BinaryFile targetFile(msg.location()); + DataSetFile::DataSetHeader header; + Kitsunemimi::DataBuffer content; + if(targetFile.readCompleteFile(content, error) == false) + { + //TODO: handle error + LOG_ERROR(error); + } + + // send data + if(session->sendResponse(content.data, + content.usedBufferSize, + blockerId, + error) == false) + { + LOG_ERROR(error); + } + + return; +} + +/** + * @brief handle dataset-request-message + * + * @param msg message to process + * @param session pointer to the session, which received the message + * @param blockerId blocker-id for the response + */ +inline void +handleDataSetRequest(const DatasetRequest_Message &msg, + Kitsunemimi::Sakura::Session* session, + const uint64_t blockerId) +{ + // init file + DataSetFile* file = readDataSetFile(msg.location()); + if(file == nullptr) { + return; + } + + float* payload = nullptr; + + do + { + // get payload + uint64_t payloadSize = 0; + payload = file->getPayload(payloadSize, msg.columnname()); + if(payload == nullptr) + { + // TODO: error + break; + } + + // send data + Kitsunemimi::ErrorContainer error; + if(session->sendResponse(payload, payloadSize, blockerId, error) == false) { + LOG_ERROR(error); + } + + break; + } + while(true); + + delete file; + if(payload != nullptr) { + delete[] payload; + } + + return; +} + +/** + * @brief handle result-push-message + * + * @param msg message to process + * @param session pointer to the session, which received the message + * @param blockerId blocker-id for the response + */ +inline void +handleResultPush(const ResultPush_Message &msg, + Kitsunemimi::Sakura::Session* session, + const uint64_t blockerId) +{ + Kitsunemimi::ErrorContainer error; + + Kitsunemimi::JsonItem dataParser; + if(dataParser.parse(msg.results(), error) == false) + { + error.addMeesage("Error while receivind result-data"); + LOG_ERROR(error); + return; + } + + Kitsunemimi::JsonItem resultData; + resultData.insert("uuid", msg.uuid()); + resultData.insert("name", msg.name()); + resultData.insert("data", dataParser.stealItemContent()); + resultData.insert("visibility", "private"); + + Kitsunemimi::Hanami::UserContext userContext; + userContext.userId = msg.userid(); + userContext.projectId = msg.projectid(); + + if(ShioriRoot::requestResultTable->addRequestResult(resultData, userContext, error) == false) + { + LOG_ERROR(error); + + const std::string ret = "fail"; + if(session->sendResponse(ret.c_str(), ret.size(), blockerId, error) == false) { + LOG_ERROR(error); + } + return; + } + + const std::string ret = "success"; + if(session->sendResponse(ret.c_str(), ret.size(), blockerId, error) == false) { + LOG_ERROR(error); + } +} + +/** + * @brief handle error-log-message + * + * @param msg message to process + */ +inline void +handleErrorLog(const ErrorLog_Message &msg) +{ + Kitsunemimi::ErrorContainer error; + if(ShioriRoot::errorLogTable->addErrorLogEntry(getDatetime(), + msg.userid(), + msg.component(), + msg.context(), + msg.values(), + msg.errormsg(), + error) == false) + { + error.addMeesage("ERROR: Failed to write error-log into database"); + + // HINT(kitsudaiki): use normal stdout, because LOG_ERROR would trigger this function again + // and could create a crash because of a stack-overflow + std::cout<addAuditLogEntry(getDatetime(), + msg.userid(), + msg.component(), + msg.endpoint(), + msg.type(), + error) == false) + { + error.addMeesage("ERROR: Failed to write audit-log into database"); + LOG_ERROR(error); + } +} + +/** + * @brief handle errors of message which to requires a response + * + * @param msg error-message + */ +inline void +handleFail(const std::string &msg, + Kitsunemimi::Sakura::Session* session, + const uint64_t blockerId) +{ + Kitsunemimi::ErrorContainer error; + error.addMeesage(msg); + LOG_ERROR(error); + + const std::string ret = "-"; + session->sendResponse(ret.c_str(), ret.size(), blockerId, error); + return; +} + +/** + * @brief handle generic message-content + * + * @param session pointer to the session, which received the message + * @param data received bytes + * @param dataSize number of bytes in the received message + * @param blockerId blocker-id for the response + */ +void +genericMessageCallback(Kitsunemimi::Sakura::Session* session, + const uint32_t messageSubType, + void* data, + const uint64_t dataSize, + const uint64_t blockerId) +{ + switch(messageSubType) + { + case SHIORI_CLUSTER_SNAPSHOT_PULL_MESSAGE_TYPE: + { + ClusterSnapshotPull_Message msg; + if(msg.ParseFromArray(data, dataSize) == false) + { + handleFail("Receive broken cluster-snapshot-message", session, blockerId); + return; + } + + handleClusterSnapshotRequest(msg, session, blockerId); + } + break; + case SHIORI_DATASET_REQUEST_MESSAGE_TYPE: + { + DatasetRequest_Message msg; + if(msg.ParseFromArray(data, dataSize) == false) + { + handleFail("Receive broken dataset-requests-message", session, blockerId); + return; + } + + handleDataSetRequest(msg, session, blockerId); + } + break; + case SHIORI_RESULT_PUSH_MESSAGE_TYPE: + { + ResultPush_Message msg; + if(msg.ParseFromArray(data, dataSize) == false) + { + handleFail("Receive broken result-push-message", session, blockerId); + return; + } + + handleResultPush(msg, session, blockerId); + } + break; + case SHIORI_AUDIT_LOG_MESSAGE_TYPE: + { + AuditLog_Message msg; + if(msg.ParseFromArray(data, dataSize) == false) + { + Kitsunemimi::ErrorContainer error; + error.addMeesage("Receive broken audit-log-message"); + LOG_ERROR(error); + return; + } + + handleAuditLog(msg); + } + break; + case SHIORI_ERROR_LOG_MESSAGE_TYPE: + { + ErrorLog_Message msg; + if(msg.ParseFromArray(data, dataSize) == false) + { + Kitsunemimi::ErrorContainer error; + error.addMeesage("Receive broken error-log-message"); + LOG_ERROR(error); + return; + } + + handleErrorLog(msg); + } + break; + default: + handleFail("Received unknown generic message", session, blockerId); + break; + } +} + +#endif // SHIORIARCHIVE_CALLBACKS_H diff --git a/src/components/ShioriArchive/src/config.h b/src/components/ShioriArchive/src/config.h new file mode 100644 index 00000000..15a80dc9 --- /dev/null +++ b/src/components/ShioriArchive/src/config.h @@ -0,0 +1,42 @@ +/** + * @file config.h + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2022 Tobias Anker + * + * 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. + */ + +#ifndef SHIORIARCHIVE_CONFIG_H +#define SHIORIARCHIVE_CONFIG_H + +#include +#include +#include + +/** + * @brief register configs + */ +void +registerConfigs(Kitsunemimi::ErrorContainer &error) +{ + Kitsunemimi::Hanami::registerBasicConfigs(error); + + REGISTER_STRING_CONFIG( "shiori", "data_set_location", error, "", true ); + REGISTER_STRING_CONFIG( "shiori", "cluster_snapshot_location", error, "", true ); +} + +#endif // SHIORIARCHIVE_CONFIG_H diff --git a/src/components/ShioriArchive/src/core/data_set_files/data_set_file.cpp b/src/components/ShioriArchive/src/core/data_set_files/data_set_file.cpp new file mode 100644 index 00000000..dbc506fa --- /dev/null +++ b/src/components/ShioriArchive/src/core/data_set_files/data_set_file.cpp @@ -0,0 +1,204 @@ +/** + * @file image_data_set_file.cpp + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2022 Tobias Anker + * + * 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 "data_set_file.h" + +#include + +#include +#include + +/** + * @brief constructor + * + * @param filePath path to file + */ +DataSetFile::DataSetFile(const std::string &filePath) +{ + m_targetFile = new Kitsunemimi::BinaryFile(filePath); +} + +/** + * @brief constructor + * + * @param file pointer to binary-file object + */ +DataSetFile::DataSetFile(Kitsunemimi::BinaryFile* file) +{ + m_targetFile = file; +} + +/** + * @brief destructor + */ +DataSetFile::~DataSetFile() +{ + delete m_targetFile; +} + +/** + * @brief initialize a new file with the already created header + */ +bool +DataSetFile::initNewFile() +{ + // TODO: check if header are set + Kitsunemimi::ErrorContainer error; + + initHeader(); + + // allocate storage + if(m_targetFile->allocateStorage(m_totalFileSize, error) == false) + { + LOG_ERROR(error); + // TODO: error-message + return false; + } + + // prepare dataset-header + DataSetHeader dataSetHeader; + dataSetHeader.type = type; + uint32_t nameSize = name.size(); + if(nameSize > 255) { + nameSize = 255; + } + memcpy(dataSetHeader.name, name.c_str(), nameSize); + dataSetHeader.name[nameSize] = '\0'; + + // write dataset-header to file + if(m_targetFile->writeDataIntoFile(&dataSetHeader, 0, sizeof(DataSetHeader), error) == false) + { + LOG_ERROR(error); + // TODO: error-message + return false; + } + + // write data to file + return updateHeader(); +} + +/** + * @brief read complete file into memory + * + * @return true, if successful, else false + */ +bool +DataSetFile::readFromFile() +{ + Kitsunemimi::ErrorContainer error; + + // create complete file + Kitsunemimi::DataBuffer buffer; + if(m_targetFile->readCompleteFile(buffer, error) == false) + { + LOG_ERROR(error); + // TODO: error-message + return false; + } + + // prepare + const uint8_t* u8buffer = static_cast(buffer.data); + + // read data-set-header + DataSetHeader dataSetHeader; + memcpy(&dataSetHeader, u8buffer, sizeof(DataSetHeader)); + type = static_cast(dataSetHeader.type); + name = dataSetHeader.name; + + readHeader(u8buffer); + + return true; +} + +/** + * @brief add data to the file + * + * @param pos value-position, where to start to write to file + * @param data values to write + * @param numberOfValues number of values to write + * + * @return true, if successful, else false + */ +bool +DataSetFile::addBlock(const uint64_t pos, + const float* data, + const u_int64_t numberOfValues) +{ + Kitsunemimi::ErrorContainer error; + + // check size to not write over the end of the file + if(m_headerSize + ((pos + numberOfValues) * sizeof(float)) > m_totalFileSize) + { + // TODO: error-message + return false; + } + + // add add data to file + if(m_targetFile->writeDataIntoFile(data, + m_headerSize + pos * sizeof(float), + numberOfValues * sizeof(float), + error) == false) + { + LOG_ERROR(error); + // TODO: error-message + return false; + } + + return true; +} + +/** + * @brief read file as data-set + * + * @param filePath path to file + * + * @return pointer to file-handler, if successful, else nullptr + */ +DataSetFile* +readDataSetFile(const std::string &filePath) +{ + Kitsunemimi::ErrorContainer error; + + // read header of file to identify type + Kitsunemimi::BinaryFile* targetFile = new Kitsunemimi::BinaryFile(filePath); + DataSetFile::DataSetHeader header; + if(targetFile->readDataFromFile(&header, 0 , sizeof(DataSetFile::DataSetHeader), error) == false) + { + //TODO: handle error + LOG_ERROR(error); + } + + // create file-handling object based on the type from the header + DataSetFile* file = nullptr; + if(header.type == DataSetFile::IMAGE_TYPE) + { + file = new ImageDataSetFile(targetFile); + file->readFromFile(); + } + else if(header.type == DataSetFile::TABLE_TYPE) + { + file = new TableDataSetFile(targetFile); + file->readFromFile(); + } + + return file; +} diff --git a/src/components/ShioriArchive/src/core/data_set_files/data_set_file.h b/src/components/ShioriArchive/src/core/data_set_files/data_set_file.h new file mode 100644 index 00000000..4f99c141 --- /dev/null +++ b/src/components/ShioriArchive/src/core/data_set_files/data_set_file.h @@ -0,0 +1,119 @@ +/** + * @file image_data_set_file.cpp + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2022 Tobias Anker + * + * 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. + */ + +#ifndef SHIORIARCHIVE_DATASETFILE_H +#define SHIORIARCHIVE_DATASETFILE_H + +#include + +#include +#include +#include +#include + +namespace Kitsunemimi { +struct DataBuffer; +class BinaryFile; +} + +class DataSetFile +{ +public: + enum DataSetType + { + UNDEFINED_TYPE = 0, + IMAGE_TYPE = 1, + TABLE_TYPE = 2 + }; + + struct DataSetHeader + { + uint8_t type = UNDEFINED_TYPE; + char name[256]; + }; + + struct ImageTypeHeader + { + uint64_t numberOfInputsX = 0; + uint64_t numberOfInputsY = 0; + uint64_t numberOfOutputs = 0; + uint64_t numberOfImages = 0; + float maxValue = 0.0f; + float avgValue = 0.0f; + }; + + struct TableTypeHeader + { + uint64_t numberOfColumns = 0; + uint64_t numberOfLines = 0; + }; + + struct TableHeaderEntry + { + char name[256]; + bool isInput = false; + bool isOutput = false; + float multiplicator = 1.0f; + float averageVal = 0.0f; + float maxVal = 0.0f; + + void setName(const std::string &name) + { + uint32_t nameSize = name.size(); + if(nameSize > 255) { + nameSize = 255; + } + memcpy(this->name, name.c_str(), nameSize); + this->name[nameSize] = '\0'; + } + }; + + DataSetFile(const std::string &filePath); + DataSetFile(Kitsunemimi::BinaryFile* file); + virtual ~DataSetFile(); + + bool initNewFile(); + bool readFromFile(); + + bool addBlock(const uint64_t pos, + const float* data, + const u_int64_t numberOfValues); + virtual float* getPayload(uint64_t &payloadSize, + const std::string &columnName = "") = 0; + virtual bool updateHeader() = 0; + + DataSetType type = UNDEFINED_TYPE; + std::string name = ""; + +protected: + virtual void initHeader() = 0; + virtual void readHeader(const uint8_t* u8buffer) = 0; + + Kitsunemimi::BinaryFile* m_targetFile = nullptr; + + uint64_t m_headerSize = 0; + uint64_t m_totalFileSize = 0; +}; + +DataSetFile* readDataSetFile(const std::string &filePath); + +#endif // SHIORIARCHIVE_DATASETFILE_H diff --git a/src/components/ShioriArchive/src/core/data_set_files/image_data_set_file.cpp b/src/components/ShioriArchive/src/core/data_set_files/image_data_set_file.cpp new file mode 100644 index 00000000..46910b1b --- /dev/null +++ b/src/components/ShioriArchive/src/core/data_set_files/image_data_set_file.cpp @@ -0,0 +1,120 @@ +/** + * @file image_data_set_file.cpp + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2022 Tobias Anker + * + * 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 "image_data_set_file.h" + +#include + +/** + * @brief constructor + * + * @param filePath path to file + */ +ImageDataSetFile::ImageDataSetFile(const std::string &filePath) + : DataSetFile(filePath) {} + +/** + * @brief constructor + * + * @param file pointer to binary-file object + */ +ImageDataSetFile::ImageDataSetFile(Kitsunemimi::BinaryFile* file) + : DataSetFile(file) {} + +/** + * @brief destructor + */ +ImageDataSetFile::~ImageDataSetFile() {} + +/** + * @brief init header-sizes + */ +void +ImageDataSetFile::initHeader() +{ + m_headerSize = sizeof(DataSetHeader) + sizeof(ImageTypeHeader); + + uint64_t lineSize = (imageHeader.numberOfInputsX * imageHeader.numberOfInputsY) + + imageHeader.numberOfOutputs; + m_totalFileSize = m_headerSize + (lineSize * imageHeader.numberOfImages * sizeof(float)); +} + +/** + * @brief read header from buffer + * + * @param u8buffer buffer to read + */ +void +ImageDataSetFile::readHeader(const uint8_t* u8buffer) +{ + // read image-header + m_headerSize = sizeof(DataSetHeader) + sizeof(ImageTypeHeader); + memcpy(&imageHeader, &u8buffer[sizeof(DataSetHeader)], sizeof(ImageTypeHeader)); + + // get sizes + uint64_t lineSize = (imageHeader.numberOfInputsX * imageHeader.numberOfInputsY) + + imageHeader.numberOfOutputs; + m_totalFileSize = m_headerSize + (lineSize * imageHeader.numberOfImages * sizeof(float)); +} + +/** + * @brief update header in file + * + * @return true, if successful, else false + */ +bool +ImageDataSetFile::updateHeader() +{ + // write image-header to file + Kitsunemimi::ErrorContainer error; + if(m_targetFile->writeDataIntoFile(&imageHeader, + sizeof(DataSetHeader), + sizeof(ImageTypeHeader), + error) == false) + { + LOG_ERROR(error); + return false; + } + + return true; +} + +/** + * @brief get pointer to payload of a file + * + * @param payloadSize reference for size of the read payload + * + * @return pointer to the payload + */ +float* +ImageDataSetFile::getPayload(uint64_t &payloadSize, + const std::string &) +{ + payloadSize = m_totalFileSize - m_headerSize; + float* payload = new float[payloadSize / sizeof(float)]; + Kitsunemimi::ErrorContainer error; + if(m_targetFile->readDataFromFile(payload, m_headerSize, payloadSize, error) == false) { + LOG_ERROR(error); + // TODO: handle error + } + return payload; +} diff --git a/src/components/ShioriArchive/src/core/data_set_files/image_data_set_file.h b/src/components/ShioriArchive/src/core/data_set_files/image_data_set_file.h new file mode 100644 index 00000000..a559a81f --- /dev/null +++ b/src/components/ShioriArchive/src/core/data_set_files/image_data_set_file.h @@ -0,0 +1,46 @@ +/** + * @file image_data_set_file.h + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2022 Tobias Anker + * + * 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. + */ + +#ifndef SHIORIARCHIVE_IMAGEDATASETFILE_H +#define SHIORIARCHIVE_IMAGEDATASETFILE_H + +#include + +class ImageDataSetFile + : public DataSetFile +{ +public: + ImageDataSetFile(const std::string &filePath); + ImageDataSetFile(Kitsunemimi::BinaryFile* file); + ~ImageDataSetFile(); + bool updateHeader(); + float* getPayload(uint64_t &payloadSize, + const std::string &columnName = ""); + + ImageTypeHeader imageHeader; + +protected: + void initHeader(); + void readHeader(const uint8_t* u8buffer); +}; + +#endif // SHIORIARCHIVE_IMAGEDATASETFILE_H diff --git a/src/components/ShioriArchive/src/core/data_set_files/table_data_set_file.cpp b/src/components/ShioriArchive/src/core/data_set_files/table_data_set_file.cpp new file mode 100644 index 00000000..baaf0b13 --- /dev/null +++ b/src/components/ShioriArchive/src/core/data_set_files/table_data_set_file.cpp @@ -0,0 +1,237 @@ +/** + * @file table_data_set_file.cpp + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2022 Tobias Anker + * + * 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 "table_data_set_file.h" + +#include + +/** + * @brief constructor + * + * @param filePath path to file + */ +TableDataSetFile::TableDataSetFile(const std::string &filePath) + : DataSetFile(filePath) {} + +/** + * @brief constructor + * + * @param file pointer to binary-file object + */ +TableDataSetFile::TableDataSetFile(Kitsunemimi::BinaryFile* file) + : DataSetFile(file) {} + +/** + * @brief destructor + */ +TableDataSetFile::~TableDataSetFile() {} + +/** + * @brief init header-sizes + */ +void +TableDataSetFile::initHeader() +{ + m_headerSize = sizeof(DataSetHeader) + sizeof(TableTypeHeader); + m_headerSize += tableColumns.size() * sizeof(TableHeaderEntry); + + tableHeader.numberOfColumns = tableColumns.size(); + m_totalFileSize = m_headerSize; + m_totalFileSize += tableHeader.numberOfColumns * sizeof(float) * tableHeader.numberOfLines; +} + +/** + * @brief read header from buffer + * + * @param u8buffer buffer to read + */ +void +TableDataSetFile::readHeader(const uint8_t* u8buffer) +{ + // read table-header + m_headerSize = sizeof(DataSetHeader) + sizeof(TableTypeHeader); + memcpy(&tableHeader, &u8buffer[sizeof(DataSetHeader)], sizeof(TableTypeHeader)); + + // header header + for(uint64_t i = 0; i < tableHeader.numberOfColumns; i++) + { + TableHeaderEntry entry; + memcpy(&entry, + &u8buffer[m_headerSize + (i * sizeof(TableHeaderEntry))], + sizeof(TableHeaderEntry)); + tableColumns.push_back(entry); + } + + // get sizes + m_headerSize += tableHeader.numberOfColumns * sizeof(TableHeaderEntry); + m_totalFileSize = m_headerSize; + m_totalFileSize += tableHeader.numberOfColumns * sizeof(float) * tableHeader.numberOfLines; +} + +/** + * @brief update header in file + * + * @return true, if successful, else false + */ +bool +TableDataSetFile::updateHeader() +{ + // write table-header to file + Kitsunemimi::ErrorContainer error; + if(m_targetFile->writeDataIntoFile(&tableHeader, + sizeof(DataSetHeader), + sizeof(TableTypeHeader), + error) == false) + { + LOG_ERROR(error); + return false; + } + + // write table-header-entries to file + const uint64_t offset = sizeof(DataSetHeader) + sizeof(TableTypeHeader); + for(uint64_t i = 0; i < tableColumns.size(); i++) + { + if(m_targetFile->writeDataIntoFile(&tableColumns[i], + offset + (i * sizeof(TableHeaderEntry)), + sizeof(TableHeaderEntry), + error) == false) + { + LOG_ERROR(error); + return false; + } + } + + return true; +} + +/** + * @brief get pointer to payload of a file + * + * @param payloadSize reference for size of the read payload + * + * @return pointer to the payload + */ +float* +TableDataSetFile::getPayload(uint64_t &payloadSize, + const std::string &columnName) +{ + Kitsunemimi::ErrorContainer error; + + float* payload = new float[(m_totalFileSize - m_headerSize) / sizeof(float)]; + if(m_targetFile->readDataFromFile(payload, + m_headerSize, + m_totalFileSize - m_headerSize, + error) == false) + { + //TODO: handle error + LOG_ERROR(error); + return payload; + } + + uint64_t columnPos = 0; + for(uint64_t i = 0; i < tableColumns.size(); i++) + { + if(tableColumns[i].name == columnName) { + columnPos = i; + } + } + + payloadSize = tableHeader.numberOfLines * sizeof(float); + float* filteredData = new float[tableHeader.numberOfLines]; + for(uint64_t line = 0; line < tableHeader.numberOfLines; line++) { + filteredData[line] = payload[line * tableHeader.numberOfColumns + columnPos]; + } + + delete[] payload; + + return filteredData; +} + +/** + * @brief print-function for manually debugging only + */ +void +TableDataSetFile::print() +{ + std::cout<<"======================================================="<readCompleteFile(completeFile, error) == false) + { + error.addMeesage("Failed to read file"); + LOG_ERROR(error); + } + + // read data-set-header + DataSetHeader dataSetHeader; + memcpy(&dataSetHeader, completeFile.data, sizeof(DataSetHeader)); + std::cout<<"name: "<(completeFile.data); + uint32_t headerSize = sizeof(DataSetHeader) + sizeof(TableTypeHeader); + memcpy(&tableHeader, &u8buffer[sizeof(DataSetHeader)], sizeof(TableTypeHeader)); + + std::cout<<"number of columns: "<(&u8buffer[headerSize]); + + for(uint64_t line = 0; line < tableHeader.numberOfLines; line++) + { + for(uint64_t col = 0; col < tableHeader.numberOfColumns; col++) + { + std::cout< + * + * @copyright Apache License Version 2.0 + * + * Copyright 2022 Tobias Anker + * + * 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. + */ + +#ifndef SHIORIARCHIVE_TABLEDATASETFILE_H +#define SHIORIARCHIVE_TABLEDATASETFILE_H + +#include + +class TableDataSetFile + : public DataSetFile +{ +public: + TableDataSetFile(const std::string &filePath); + TableDataSetFile(Kitsunemimi::BinaryFile* file); + ~TableDataSetFile(); + bool updateHeader(); + float* getPayload(uint64_t &payloadSize, + const std::string &columnName = ""); + + void print(); + + TableTypeHeader tableHeader; + std::vector tableColumns; + +protected: + void initHeader(); + void readHeader(const uint8_t* u8buffer); +}; + +#endif // SHIORIARCHIVE_TABLEDATASETFILE_H diff --git a/src/components/ShioriArchive/src/core/temp_file_handler.cpp b/src/components/ShioriArchive/src/core/temp_file_handler.cpp new file mode 100644 index 00000000..8a9b75ed --- /dev/null +++ b/src/components/ShioriArchive/src/core/temp_file_handler.cpp @@ -0,0 +1,236 @@ +/** + * @file temp_file_handler.cpp + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2022 Tobias Anker + * + * 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 "temp_file_handler.h" + +#include +#include +#include + +/** + * @brief constructor + */ +TempFileHandler::TempFileHandler() {} + +/** + * @brief destructor and delete all registered temporary files + */ +TempFileHandler::~TempFileHandler() +{ + bool success = false; + Kitsunemimi::ErrorContainer error; + const std::string targetFilePath = GET_STRING_CONFIG("shiori", "data_set_location", success); + + std::vector result; + std::map::iterator it; + for(it = m_tempFiles.begin(); + it != m_tempFiles.end(); + it++) + { + Kitsunemimi::BinaryFile* ptr = it->second; + if(ptr->closeFile(error) == false) { + //TODO: handle error + } + delete ptr; + Kitsunemimi::deleteFileOrDir(targetFilePath + "/" + it->first, error); + m_tempFiles.erase(it); + } +} + +/** + * @brief initialize new temporary file + * + * @param id id of the new temporary file + * @param size size to allocate + * + * @return false, if id already exist or storage-allocation failed, else true + */ +bool +TempFileHandler::initNewFile(const std::string &id, const uint64_t size) +{ + if(m_tempFiles.find(id) != m_tempFiles.end()) { + return false; + } + + bool success = false; + std::string targetFilePath = GET_STRING_CONFIG("shiori", "data_set_location", success); + targetFilePath += "/" + id; + + Kitsunemimi::ErrorContainer error; + Kitsunemimi::BinaryFile* tempfile = new Kitsunemimi::BinaryFile(targetFilePath); + if(tempfile->allocateStorage(size, error) == false) + { + LOG_ERROR(error); + return false; + } + m_tempFiles.insert(std::make_pair(id, tempfile)); + + return true; +} + +/** + * @brief add data to the temporary file + * + * @param uuid uuid of the temporary file + * @param pos position in the file where to add the data + * @param data pointer to the data to add + * @param size size of the data to add + * + * @return false, if id not found, else true + */ +bool +TempFileHandler::addDataToPos(const std::string &uuid, + const uint64_t pos, + const void* data, + const uint64_t size) +{ + Kitsunemimi::ErrorContainer error; + + std::map::const_iterator it; + it = m_tempFiles.find(uuid); + if(it != m_tempFiles.end()) + { + Kitsunemimi::BinaryFile* ptr = it->second; + if(ptr->writeDataIntoFile(data, pos, size, error) == false) + { + LOG_ERROR(error); + return false; + } + + return true; + } + + LOG_ERROR(error); + + return false; +} + +/** + * @brief get data from the temporary file + * + * @param result data-buffer for the resulting data of the file + * @param uuid uuid of the temporary file + * + * @return false, if id not found, else true + */ +bool +TempFileHandler::getData(Kitsunemimi::DataBuffer &result, const std::string &uuid) +{ + Kitsunemimi::ErrorContainer error; + + std::map::const_iterator it; + it = m_tempFiles.find(uuid); + if(it != m_tempFiles.end()) + { + Kitsunemimi::BinaryFile* ptr = it->second; + return ptr->readCompleteFile(result, error); + } + + return false; +} + +/** + * @brief remove an id from this class and delete the file within the storage + * + * @param id id of the temporary file + * + * @return false, if id not found, else true + */ +bool +TempFileHandler::removeData(const std::string &id) +{ + bool success = false; + Kitsunemimi::ErrorContainer error; + std::string targetFilePath = GET_STRING_CONFIG("shiori", "data_set_location", success); + + std::map::const_iterator it; + it = m_tempFiles.find(id); + if(it != m_tempFiles.end()) + { + Kitsunemimi::BinaryFile* ptr = it->second; + if(ptr->closeFile(error) == false) { + //TODO: handle error + } + delete ptr; + Kitsunemimi::deleteFileOrDir(targetFilePath + "/" + it->first, error); + m_tempFiles.erase(it); + + return true; + } + + return false; +} + +/** + * @brief move tempfile to its target-location after tempfile was finished + * + * @param uuid uuid of the tempfile, which should be moved + * @param targetLocation target-location on the local direct, where to move the file to + * @param error reference for error-output + * + * @return true, if successful, else false + */ +bool +TempFileHandler::moveData(const std::string &uuid, + const std::string &targetLocation, + Kitsunemimi::ErrorContainer &error) +{ + bool success = false; + std::string targetFilePath = GET_STRING_CONFIG("shiori", "data_set_location", success); + + std::map::const_iterator it; + it = m_tempFiles.find(uuid); + if(it != m_tempFiles.end()) + { + Kitsunemimi::BinaryFile* ptr = it->second; + if(ptr->closeFile(error) == false) + { + LOG_ERROR(error); + return false; + } + + if(Kitsunemimi::renameFileOrDir(targetFilePath + "/" + it->first, + targetLocation, + error) == false) + { + error.addMeesage("Failed to move temp-file with uuid '" + + uuid + + "' to target-locateion '" + + targetLocation + + "'"); + LOG_ERROR(error); + return false; + } + + delete ptr; + m_tempFiles.erase(it); + + return true; + } + + error.addMeesage("Failed to move temp-file with uuid '" + + uuid + + ", because it can not be found."); + LOG_ERROR(error); + + return false; +} diff --git a/src/components/ShioriArchive/src/core/temp_file_handler.h b/src/components/ShioriArchive/src/core/temp_file_handler.h new file mode 100644 index 00000000..7efd491f --- /dev/null +++ b/src/components/ShioriArchive/src/core/temp_file_handler.h @@ -0,0 +1,58 @@ +/** + * @file temp_file_handler.h + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2022 Tobias Anker + * + * 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. + */ + +#ifndef SHIORIARCHIVE_TEMPFILEHANDLER_H +#define SHIORIARCHIVE_TEMPFILEHANDLER_H + +#include +#include +#include + +namespace Kitsunemimi { +class BinaryFile; +struct DataBuffer; +} + +class TempFileHandler +{ +public: + TempFileHandler(); + ~TempFileHandler(); + + bool initNewFile(const std::string &id, + const uint64_t size); + bool addDataToPos(const std::string &uuid, + const uint64_t pos, + const void* data, + const uint64_t size); + bool getData(Kitsunemimi::DataBuffer &result, + const std::string &uuid); + bool removeData(const std::string &id); + bool moveData(const std::string &uuid, + const std::string &targetLocation, + Kitsunemimi::ErrorContainer &error); + +private: + std::map m_tempFiles; +}; + +#endif // SHIORIARCHIVE_TEMPFILEHANDLER_H diff --git a/src/components/ShioriArchive/src/database/audit_log_table.cpp b/src/components/ShioriArchive/src/database/audit_log_table.cpp new file mode 100644 index 00000000..2e8313ff --- /dev/null +++ b/src/components/ShioriArchive/src/database/audit_log_table.cpp @@ -0,0 +1,126 @@ +/** + * @file audit_log_table.cpp + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2022 Tobias Anker + * + * 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 + +#include +#include +#include + +#include + +/** + * @brief constructor + * + * @param db pointer to database + */ +AuditLogTable::AuditLogTable(Kitsunemimi::Sakura::SqlDatabase* db) + : HanamiSqlLogTable(db) +{ + m_tableName = "audit_log"; + + DbHeaderEntry userid; + userid.name = "user_id"; + userid.maxLength = 256; + m_tableHeader.push_back(userid); + + DbHeaderEntry component; + component.name = "component"; + component.maxLength = 128; + m_tableHeader.push_back(component); + + DbHeaderEntry endpoint; + endpoint.name = "endpoint"; + endpoint.maxLength = 1024; + m_tableHeader.push_back(endpoint); + + DbHeaderEntry requestType; + requestType.name = "request_type"; + requestType.maxLength = 16; + m_tableHeader.push_back(requestType); +} + +/** + * @brief destructor + */ +AuditLogTable::~AuditLogTable() {} + +/** + * @brief add new audit-log-entry into the database + * + * @param timestamp UTC-timestamp of the request as string + * @param userId id of the user, who made the request + * @param component requested componen + * @param endpoint requested endpoint + * @param requestType HTTP-type of the request + * @param error reference for error-output + * + * @return true, if successful, else false + */ +bool +AuditLogTable::addAuditLogEntry(const std::string ×tamp, + const std::string &userId, + const std::string &component, + const std::string &endpoint, + const std::string &requestType, + Kitsunemimi::ErrorContainer &error) +{ + Kitsunemimi::JsonItem data; + data.insert("timestamp", timestamp); + data.insert("user_id", userId); + data.insert("endpoint", endpoint); + data.insert("component", component); + data.insert("request_type", requestType); + + if(insertToDb(data, error) == false) + { + error.addMeesage("Failed to add audit-log-entry to database"); + return false; + } + + return true; +} + +/** + * @brief get all audit-log-entries from the database + * + * @param result reference for the result-output + * @param userId id of the user, whos logs are requested + * @param page a page has 100 entries so (page * 100) + * @param error reference for error-output + * + * @return true, if successful, else false + */ +bool +AuditLogTable::getAllAuditLogEntries(Kitsunemimi::TableItem &result, + const std::string &userId, + const uint64_t page, + Kitsunemimi::ErrorContainer &error) +{ + if(getPageFromDb(result, userId, page, error) == false) + { + error.addMeesage("Failed to get all audit-log-entries from database"); + return false; + } + + return true; +} diff --git a/src/components/ShioriArchive/src/database/audit_log_table.h b/src/components/ShioriArchive/src/database/audit_log_table.h new file mode 100644 index 00000000..a6afa199 --- /dev/null +++ b/src/components/ShioriArchive/src/database/audit_log_table.h @@ -0,0 +1,52 @@ +/** + * @file audit_log_table.h + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2022 Tobias Anker + * + * 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. + */ + +#ifndef SHIORIARCHIVE_AUDIT_LOG_TABLE_H +#define SHIORIARCHIVE_AUDIT_LOG_TABLE_H + +#include +#include + +namespace Kitsunemimi { +class JsonItem; +} + +class AuditLogTable + : public Kitsunemimi::Hanami::HanamiSqlLogTable +{ +public: + AuditLogTable(Kitsunemimi::Sakura::SqlDatabase* db); + ~AuditLogTable(); + + bool addAuditLogEntry(const std::string ×tamp, + const std::string &userId, + const std::string &component, + const std::string &endpoint, + const std::string &requestType, + Kitsunemimi::ErrorContainer &error); + bool getAllAuditLogEntries(Kitsunemimi::TableItem &result, + const std::string &userId, + const uint64_t page, + Kitsunemimi::ErrorContainer &error); +}; + +#endif // SHIORIARCHIVE_AUDIT_LOG_TABLE_H diff --git a/src/components/ShioriArchive/src/database/cluster_snapshot_table.cpp b/src/components/ShioriArchive/src/database/cluster_snapshot_table.cpp new file mode 100644 index 00000000..8c7caf2e --- /dev/null +++ b/src/components/ShioriArchive/src/database/cluster_snapshot_table.cpp @@ -0,0 +1,237 @@ +/** + * @file cluster_snapshot_table.cpp + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2022 Tobias Anker + * + * 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 + +#include +#include +#include + +#include + +/** + * @brief constructor + * + * @param db pointer to database + */ +ClusterSnapshotTable::ClusterSnapshotTable(Kitsunemimi::Sakura::SqlDatabase* db) + : HanamiSqlTable(db) +{ + m_tableName = "cluster_snapshot"; + + DbHeaderEntry name; + name.name = "name"; + name.maxLength = 256; + m_tableHeader.push_back(name); + + DbHeaderEntry header; + header.name = "header"; + header.hide = true; + m_tableHeader.push_back(header); + + DbHeaderEntry location; + location.name = "location"; + location.hide = true; + m_tableHeader.push_back(location); + + DbHeaderEntry tempFiles; + tempFiles.name = "temp_files"; + tempFiles.hide = true; + m_tableHeader.push_back(tempFiles); +} + +/** + * @brief destructor + */ +ClusterSnapshotTable::~ClusterSnapshotTable() {} + +/** + * @brief add new metadata of a snapshot into the database + * + * @param userData json-item with all information of the data to add to database + * @param userContext context-object with all user specific information + * @param error reference for error-output + * + * @return true, if successful, else false + */ +bool +ClusterSnapshotTable::addClusterSnapshot(Kitsunemimi::JsonItem &data, + const Kitsunemimi::Hanami::UserContext &userContext, + Kitsunemimi::ErrorContainer &error) +{ + if(add(data, userContext, error) == false) + { + error.addMeesage("Failed to add snapshot to database"); + return false; + } + + return true; +} + +/** + * @brief get a metadata-entry for a specific snapshot from the database + * + * @param result reference for the result-output + * @param snapshotUuid uuid of the data + * @param userContext context-object with all user specific information + * @param error reference for error-output + * @param showHiddenValues set to true to also show as hidden marked fields + * + * @return true, if successful, else false + */ +bool +ClusterSnapshotTable::getClusterSnapshot(Kitsunemimi::JsonItem &result, + const std::string &snapshotUuid, + const Kitsunemimi::Hanami::UserContext &userContext, + Kitsunemimi::ErrorContainer &error, + const bool showHiddenValues) +{ + // get user from db + std::vector conditions; + conditions.emplace_back("uuid", snapshotUuid); + + // get dataset from db + if(get(result, userContext, conditions, error, showHiddenValues) == false) + { + error.addMeesage("Failed to get snapshot with UUID '" + + snapshotUuid + + "' from database"); + LOG_ERROR(error); + return false; + } + + return true; +} + +/** + * @brief get metadata of all snapshots from the database + * + * @param result reference for the result-output + * @param userContext context-object with all user specific information + * @param error reference for error-output + * + * @return true, if successful, else false + */ +bool +ClusterSnapshotTable::getAllClusterSnapshot(Kitsunemimi::TableItem &result, + const Kitsunemimi::Hanami::UserContext &userContext, + Kitsunemimi::ErrorContainer &error) +{ + std::vector conditions; + if(getAll(result, userContext, conditions, error) == false) + { + error.addMeesage("Failed to get all snapshots from database"); + return false; + } + + return true; +} + +/** + * @brief delete metadata of a snapshot from the database + * + * @param snapshotUuid uuid of the data + * @param userContext context-object with all user specific information + * @param error reference for error-output + * + * @return true, if successful, else false + */ +bool +ClusterSnapshotTable::deleteClusterSnapshot(const std::string &snapshotUuid, + const Kitsunemimi::Hanami::UserContext &userContext, + Kitsunemimi::ErrorContainer &error) +{ + std::vector conditions; + conditions.emplace_back("uuid", snapshotUuid); + if(del(conditions, userContext, error) == false) + { + error.addMeesage("Failed to delete snapshot with UUID '" + + snapshotUuid + + "' from database"); + return false; + } + + return true; +} + +/** + * @brief update snapshot in database to fully uploaded + * + * @param uuid uuid of the snapshot + * @param fileUuid uuid of the temporary file + * @param error reference for error-output + * + * @return true, if successful, else false + */ +bool +ClusterSnapshotTable::setUploadFinish(const std::string &uuid, + const std::string &fileUuid, + Kitsunemimi::ErrorContainer &error) +{ + std::vector conditions; + conditions.emplace_back("uuid", uuid); + Kitsunemimi::JsonItem result; + + Kitsunemimi::Hanami::UserContext userContext; + userContext.isAdmin = true; + + // get snapshot from db + if(get(result, userContext, conditions, error, true) == false) + { + error.addMeesage("Failed to get snapshot with UUID '" + uuid + "' from database"); + LOG_ERROR(error); + return false; + } + + // check response from database + if(result.contains("temp_files") == false) + { + error.addMeesage("Entry get for the snaphost with UUID '" + uuid + "' is broken"); + LOG_ERROR(error); + return false; + } + + // update temp-files entry to 100% + const std::string tempFilesStr = result.get("temp_files").toString(); + Kitsunemimi::JsonItem tempFiles; + if(tempFiles.parse(tempFilesStr, error) == false) + { + error.addMeesage("Failed to parse temp_files entry of snapshot with UUID '" + + uuid + + "' from database"); + LOG_ERROR(error); + return false; + } + tempFiles.insert(fileUuid, Kitsunemimi::JsonItem(1.0f), true); + + // update new entry within the database + Kitsunemimi::JsonItem newValues; + newValues.insert("temp_files", Kitsunemimi::JsonItem(tempFiles.toString())); + if(update(newValues, userContext, conditions, error) == false) + { + error.addMeesage("Failed to update entry of snapshot with UUID '" + uuid + "' in database"); + LOG_ERROR(error); + return false; + } + + return true; +} diff --git a/src/components/ShioriArchive/src/database/cluster_snapshot_table.h b/src/components/ShioriArchive/src/database/cluster_snapshot_table.h new file mode 100644 index 00000000..4eda7005 --- /dev/null +++ b/src/components/ShioriArchive/src/database/cluster_snapshot_table.h @@ -0,0 +1,59 @@ +/** + * @file cluster_snapshot_table.h + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2022 Tobias Anker + * + * 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. + */ + +#ifndef SHIORIARCHIVE_CLUSTER_SNAPSHOT_TABLE_H +#define SHIORIARCHIVE_CLUSTER_SNAPSHOT_TABLE_H + +#include +#include + +namespace Kitsunemimi { +class JsonItem; +} + +class ClusterSnapshotTable + : public Kitsunemimi::Hanami::HanamiSqlTable +{ +public: + ClusterSnapshotTable(Kitsunemimi::Sakura::SqlDatabase* db); + ~ClusterSnapshotTable(); + + bool addClusterSnapshot(Kitsunemimi::JsonItem &data, + const Kitsunemimi::Hanami::UserContext &userContext, + Kitsunemimi::ErrorContainer &error); + bool getClusterSnapshot(Kitsunemimi::JsonItem &result, + const std::string &snapshotUuid, + const Kitsunemimi::Hanami::UserContext &userContext, + Kitsunemimi::ErrorContainer &error, + const bool showHiddenValues); + bool getAllClusterSnapshot(Kitsunemimi::TableItem &result, + const Kitsunemimi::Hanami::UserContext &userContext, + Kitsunemimi::ErrorContainer &error); + bool deleteClusterSnapshot(const std::string &snapshotUuid, + const Kitsunemimi::Hanami::UserContext &userContext, + Kitsunemimi::ErrorContainer &error); + bool setUploadFinish(const std::string &uuid, + const std::string &fileUuid, + Kitsunemimi::ErrorContainer &error); +}; + +#endif // SHIORIARCHIVE_CLUSTER_SNAPSHOT_TABLE_H diff --git a/src/components/ShioriArchive/src/database/data_set_table.cpp b/src/components/ShioriArchive/src/database/data_set_table.cpp new file mode 100644 index 00000000..70a80b09 --- /dev/null +++ b/src/components/ShioriArchive/src/database/data_set_table.cpp @@ -0,0 +1,237 @@ +/** + * @file data_set_table.cpp + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2022 Tobias Anker + * + * 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 + +#include +#include +#include + +#include + +/** + * @brief constructor + * + * @param db pointer to database + */ +DataSetTable::DataSetTable(Kitsunemimi::Sakura::SqlDatabase* db) + : HanamiSqlTable(db) +{ + m_tableName = "data_set"; + + DbHeaderEntry name; + name.name = "name"; + name.maxLength = 256; + m_tableHeader.push_back(name); + + DbHeaderEntry type; + type.name = "type"; + type.maxLength = 64; + m_tableHeader.push_back(type); + + DbHeaderEntry location; + location.name = "location"; + location.hide = true; + m_tableHeader.push_back(location); + + DbHeaderEntry tempFiles; + tempFiles.name = "temp_files"; + tempFiles.hide = true; + m_tableHeader.push_back(tempFiles); +} + +/** + * @brief destructor + */ +DataSetTable::~DataSetTable() {} + +/** + * @brief add new metadata of a dataset into the database + * + * @param data json-item with all information of the data to add to database + * @param userContext context-object with all user specific information + * @param error reference for error-output + * + * @return true, if successful, else false + */ +bool +DataSetTable::addDataSet(Kitsunemimi::JsonItem &data, + const Kitsunemimi::Hanami::UserContext &userContext, + Kitsunemimi::ErrorContainer &error) +{ + if(add(data, userContext, error) == false) + { + error.addMeesage("Failed to add snapshot to database"); + return false; + } + + return true; +} + +/** + * @brief get a metadata-entry for a specific dataset from the database + * + * @param result reference for the result-output + * @param datasetUuid uuid of the data + * @param userContext context-object with all user specific information + * @param error reference for error-output + * @param showHiddenValues set to true to also show as hidden marked fields + * + * @return true, if successful, else false + */ +bool +DataSetTable::getDataSet(Kitsunemimi::JsonItem &result, + const std::string &datasetUuid, + const Kitsunemimi::Hanami::UserContext &userContext, + Kitsunemimi::ErrorContainer &error, + const bool showHiddenValues) +{ + // get user from db + std::vector conditions; + conditions.emplace_back("uuid", datasetUuid); + + // get dataset from db + if(get(result, userContext, conditions, error, showHiddenValues) == false) + { + error.addMeesage("Failed to get dataset with UUID '" + + datasetUuid + + "' from database"); + LOG_ERROR(error); + return false; + } + + return true; +} + +/** + * @brief get metadata of all datasets from the database + * + * @param result reference for the result-output + * @param userContext context-object with all user specific information + * @param error reference for error-output + * + * @return true, if successful, else false + */ +bool +DataSetTable::getAllDataSet(Kitsunemimi::TableItem &result, + const Kitsunemimi::Hanami::UserContext &userContext, + Kitsunemimi::ErrorContainer &error) +{ + std::vector conditions; + if(getAll(result, userContext, conditions, error) == false) + { + error.addMeesage("Failed to get all datasets from database"); + return false; + } + + return true; +} + +/** + * @brief delete metadata of a datasett from the database + * + * @param datasetUuid uuid of the data + * @param userContext context-object with all user specific information + * @param error reference for error-output + * + * @return true, if successful, else false + */ +bool +DataSetTable::deleteDataSet(const std::string &datasetUuid, + const Kitsunemimi::Hanami::UserContext &userContext, + Kitsunemimi::ErrorContainer &error) +{ + std::vector conditions; + conditions.emplace_back("uuid", datasetUuid); + if(del(conditions, userContext, error) == false) + { + error.addMeesage("Failed to delete dataset with UUID '" + + datasetUuid + + "' from database"); + return false; + } + + return true; +} + +/** + * @brief update dataset in database to fully uploaded + * + * @param uuid uuid of the dataset + * @param fileUuid uuid of the temporary file + * @param error reference for error-output + * + * @return true, if successful, else false + */ +bool +DataSetTable::setUploadFinish(const std::string &uuid, + const std::string &fileUuid, + Kitsunemimi::ErrorContainer &error) +{ + std::vector conditions; + conditions.emplace_back("uuid", uuid); + Kitsunemimi::JsonItem result; + + Kitsunemimi::Hanami::UserContext userContext; + userContext.isAdmin = true; + + // get dataset from db + if(get(result, userContext, conditions, error, true) == false) + { + error.addMeesage("Failed to get dataset with UUID '" + uuid + "' from database"); + LOG_ERROR(error); + return false; + } + + // check response from database + if(result.contains("temp_files") == false) + { + error.addMeesage("Entry get for the dataset with UUID '" + uuid + "' is broken"); + LOG_ERROR(error); + return false; + } + + // update temp-files entry to 100% + const std::string tempFilesStr = result.get("temp_files").toString(); + Kitsunemimi::JsonItem tempFiles; + if(tempFiles.parse(tempFilesStr, error) == false) + { + error.addMeesage("Failed to parse temp_files entry of dataset with UUID '" + + uuid + + "' from database"); + LOG_ERROR(error); + return false; + } + tempFiles.insert(fileUuid, Kitsunemimi::JsonItem(1.0f), true); + + // update new entry within the database + Kitsunemimi::JsonItem newValues; + newValues.insert("temp_files", Kitsunemimi::JsonItem(tempFiles.toString())); + if(update(newValues, userContext, conditions, error) == false) + { + error.addMeesage("Failed to update entry of dataset with UUID '" + uuid + "' in database"); + LOG_ERROR(error); + return false; + } + + return true; +} diff --git a/src/components/ShioriArchive/src/database/data_set_table.h b/src/components/ShioriArchive/src/database/data_set_table.h new file mode 100644 index 00000000..cddb5067 --- /dev/null +++ b/src/components/ShioriArchive/src/database/data_set_table.h @@ -0,0 +1,60 @@ +/** + * @file data_set_table.h + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2022 Tobias Anker + * + * 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. + */ + +#ifndef SHIORIARCHIVE_DATA_SET_TABLE_H +#define SHIORIARCHIVE_DATA_SET_TABLE_H + +#include +#include + +namespace Kitsunemimi { +class JsonItem; +} + +class DataSetTable + : public Kitsunemimi::Hanami::HanamiSqlTable +{ +public: + DataSetTable(Kitsunemimi::Sakura::SqlDatabase* db); + ~DataSetTable(); + + bool addDataSet(Kitsunemimi::JsonItem &data, + const Kitsunemimi::Hanami::UserContext &userContext, + Kitsunemimi::ErrorContainer &error); + bool getDataSet(Kitsunemimi::JsonItem &result, + const std::string &datasetUuid, + const Kitsunemimi::Hanami::UserContext &userContext, + Kitsunemimi::ErrorContainer &error, + const bool showHiddenValues); + bool getAllDataSet(Kitsunemimi::TableItem &result, + const Kitsunemimi::Hanami::UserContext &userContext, + Kitsunemimi::ErrorContainer &error); + bool deleteDataSet(const std::string &uuid, + const Kitsunemimi::Hanami::UserContext &userContext, + Kitsunemimi::ErrorContainer &error); + + bool setUploadFinish(const std::string &uuid, + const std::string &fileUuid, + Kitsunemimi::ErrorContainer &error); +}; + +#endif // SHIORIARCHIVE_DATA_SET_TABLE_H diff --git a/src/components/ShioriArchive/src/database/error_log_table.cpp b/src/components/ShioriArchive/src/database/error_log_table.cpp new file mode 100644 index 00000000..ec03f1bf --- /dev/null +++ b/src/components/ShioriArchive/src/database/error_log_table.cpp @@ -0,0 +1,135 @@ +/** + * @file error_log_table.cpp + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2022 Tobias Anker + * + * 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 + +#include +#include +#include +#include + +#include + +/** + * @brief constructor + * + * @param db pointer to database + */ +ErrorLogTable::ErrorLogTable(Kitsunemimi::Sakura::SqlDatabase* db) + : HanamiSqlLogTable(db) +{ + m_tableName = "error_log"; + + DbHeaderEntry userid; + userid.name = "user_id"; + userid.maxLength = 256; + m_tableHeader.push_back(userid); + + DbHeaderEntry component; + component.name = "component"; + component.maxLength = 128; + m_tableHeader.push_back(component); + + DbHeaderEntry context; + context.name = "context"; + m_tableHeader.push_back(context); + + DbHeaderEntry values; + values.name = "input_values"; + m_tableHeader.push_back(values); + + DbHeaderEntry message; + message.name = "message"; + m_tableHeader.push_back(message); +} + +/** + * @brief destructor + */ +ErrorLogTable::~ErrorLogTable() {} + +/** + * @brief add new error-log-entry into the database + * + * @param timestamp UTC-timestamp of the error + * @param userid id of the user, who had the error + * @param component component, where the error appeared + * @param context + * @param values + * @param message + * @param error reference for error-output + * + * @return true, if successful, else false + */ +bool +ErrorLogTable::addErrorLogEntry(const std::string ×tamp, + const std::string &userid, + const std::string &component, + const std::string &context, + const std::string &values, + const std::string &message, + Kitsunemimi::ErrorContainer &error) +{ + Kitsunemimi::JsonItem data; + data.insert("timestamp", timestamp); + data.insert("user_id", userid); + data.insert("component", component); + data.insert("context", context); + data.insert("input_values", values); + + std::string base64Msg; + Kitsunemimi::encodeBase64(base64Msg, message.c_str(), message.size()); + data.insert("message", base64Msg); + + if(insertToDb(data, error) == false) + { + error.addMeesage("Failed to add error-log-entry to database"); + return false; + } + + return true; +} + +/** + * @brief get all error-log-entries from the database + * + * @param result reference for the result-output + * @param userId id of the user, whos logs are requested + * @param page a page has 100 entries so (page * 100) + * @param error reference for error-output + * + * @return true, if successful, else false + */ +bool +ErrorLogTable::getAllErrorLogEntries(Kitsunemimi::TableItem &result, + const std::string &userId, + const uint64_t page, + Kitsunemimi::ErrorContainer &error) +{ + if(getPageFromDb(result, userId, page, error) == false) + { + error.addMeesage("Failed to get all error-log-entries from database"); + return false; + } + + return true; +} diff --git a/src/components/ShioriArchive/src/database/error_log_table.h b/src/components/ShioriArchive/src/database/error_log_table.h new file mode 100644 index 00000000..63bd6fdb --- /dev/null +++ b/src/components/ShioriArchive/src/database/error_log_table.h @@ -0,0 +1,53 @@ +/** + * @file error_log_table.h + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2022 Tobias Anker + * + * 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. + */ + +#ifndef SHIORIARCHIVE_ERROR_LOG_TABLE_H +#define SHIORIARCHIVE_ERROR_LOG_TABLE_H + +#include +#include + +namespace Kitsunemimi { +class JsonItem; +} + +class ErrorLogTable + : public Kitsunemimi::Hanami::HanamiSqlLogTable +{ +public: + ErrorLogTable(Kitsunemimi::Sakura::SqlDatabase* db); + ~ErrorLogTable(); + + bool addErrorLogEntry(const std::string ×tamp, + const std::string &userid, + const std::string &component, + const std::string &context, + const std::string &values, + const std::string &message, + Kitsunemimi::ErrorContainer &error); + bool getAllErrorLogEntries(Kitsunemimi::TableItem &result, + const std::string &userId, + const uint64_t page, + Kitsunemimi::ErrorContainer &error); +}; + +#endif // SHIORIARCHIVE_ERROR_LOG_TABLE_H diff --git a/src/components/ShioriArchive/src/database/request_result_table.cpp b/src/components/ShioriArchive/src/database/request_result_table.cpp new file mode 100644 index 00000000..c67f7a94 --- /dev/null +++ b/src/components/ShioriArchive/src/database/request_result_table.cpp @@ -0,0 +1,164 @@ +/** + * @file request_result_table.cpp + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2022 Tobias Anker + * + * 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 + +#include +#include +#include + +#include + +/** + * @brief constructor + * + * @param db pointer to database + */ +RequestResultTable::RequestResultTable(Kitsunemimi::Sakura::SqlDatabase* db) + : HanamiSqlTable(db) +{ + m_tableName = "request_result"; + + DbHeaderEntry name; + name.name = "name"; + name.maxLength = 256; + m_tableHeader.push_back(name); + + DbHeaderEntry data; + data.name = "data"; + data.hide = true; + m_tableHeader.push_back(data); +} + +/** + * @brief destructor + */ +RequestResultTable::~RequestResultTable() {} + +/** + * @brief add new metadata of a dataset into the database + * + * @param data json-item with all information of the data to add to database + * @param userContext context-object with all user specific information + * @param error reference for error-output + * + * @return true, if successful, else false + */ +bool +RequestResultTable::addRequestResult(Kitsunemimi::JsonItem &data, + const Kitsunemimi::Hanami::UserContext &userContext, + Kitsunemimi::ErrorContainer &error) +{ + if(add(data, userContext, error) == false) + { + error.addMeesage("Failed to add snapshot to database"); + return false; + } + + return true; +} + +/** + * @brief get a metadata-entry for a specific dataset from the database + * + * @param result reference for the result-output + * @param resultUuid uuid of the data + * @param userContext context-object with all user specific information + * @param error reference for error-output + * @param showHiddenValues set to true to also show as hidden marked fields + * + * @return true, if successful, else false + */ +bool +RequestResultTable::getRequestResult(Kitsunemimi::JsonItem &result, + const std::string &resultUuid, + const Kitsunemimi::Hanami::UserContext &userContext, + Kitsunemimi::ErrorContainer &error, + const bool showHiddenValues) +{ + // get user from db + std::vector conditions; + conditions.emplace_back("uuid", resultUuid); + + // get dataset from db + if(get(result, userContext, conditions, error, showHiddenValues) == false) + { + error.addMeesage("Failed to get request-result with UUID '" + + resultUuid + + "' from database"); + LOG_ERROR(error); + return false; + } + + return true; +} + +/** + * @brief get metadata of all datasets from the database + * + * @param result reference for the result-output + * @param userContext context-object with all user specific information + * @param error reference for error-output + * + * @return true, if successful, else false + */ +bool +RequestResultTable::getAllRequestResult(Kitsunemimi::TableItem &result, + const Kitsunemimi::Hanami::UserContext &userContext, + Kitsunemimi::ErrorContainer &error) +{ + std::vector conditions; + if(getAll(result, userContext, conditions, error) == false) + { + error.addMeesage("Failed to get all request-results from database"); + return false; + } + + return true; +} + +/** + * @brief delete metadata of a datasett from the database + * + * @param datasetUuid uuid of the data + * @param userContext context-object with all user specific information + * @param error reference for error-output + * + * @return true, if successful, else false + */ +bool +RequestResultTable::deleteRequestResult(const std::string &resultUuid, + const Kitsunemimi::Hanami::UserContext &userContext, + Kitsunemimi::ErrorContainer &error) +{ + std::vector conditions; + conditions.emplace_back("uuid", resultUuid); + if(del(conditions, userContext, error) == false) + { + error.addMeesage("Failed to delete request-result with UUID '" + + resultUuid + + "' from database"); + return false; + } + + return true; +} diff --git a/src/components/ShioriArchive/src/database/request_result_table.h b/src/components/ShioriArchive/src/database/request_result_table.h new file mode 100644 index 00000000..706068a8 --- /dev/null +++ b/src/components/ShioriArchive/src/database/request_result_table.h @@ -0,0 +1,56 @@ +/** + * @file request_result_table.h + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2022 Tobias Anker + * + * 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. + */ + +#ifndef SHIORIARCHIVE_REQUEST_RESULT_TABLE_H +#define SHIORIARCHIVE_REQUEST_RESULT_TABLE_H + +#include +#include + +namespace Kitsunemimi { +class JsonItem; +} + +class RequestResultTable + : public Kitsunemimi::Hanami::HanamiSqlTable +{ +public: + RequestResultTable(Kitsunemimi::Sakura::SqlDatabase* db); + ~RequestResultTable(); + + bool addRequestResult(Kitsunemimi::JsonItem &data, + const Kitsunemimi::Hanami::UserContext &userContext, + Kitsunemimi::ErrorContainer &error); + bool getRequestResult(Kitsunemimi::JsonItem &result, + const std::string &resultUuid, + const Kitsunemimi::Hanami::UserContext &userContext, + Kitsunemimi::ErrorContainer &error, + const bool showHiddenValues); + bool getAllRequestResult(Kitsunemimi::TableItem &result, + const Kitsunemimi::Hanami::UserContext &userContext, + Kitsunemimi::ErrorContainer &error); + bool deleteRequestResult(const std::string &resultUuid, + const Kitsunemimi::Hanami::UserContext &userContext, + Kitsunemimi::ErrorContainer &error); +}; + +#endif // SHIORIARCHIVE_REQUEST_RESULT_TABLE_H diff --git a/src/components/ShioriArchive/src/main.cpp b/src/components/ShioriArchive/src/main.cpp new file mode 100644 index 00000000..668efb3f --- /dev/null +++ b/src/components/ShioriArchive/src/main.cpp @@ -0,0 +1,83 @@ +/** + * @file main.cpp + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2022 Tobias Anker + * + * 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 + +#include +#include +#include +#include +#include + +#include +#include + +#include +#include + +#include +#include + +using Kitsunemimi::Hanami::HanamiMessaging; +using Kitsunemimi::Hanami::initMain; + +int main(int argc, char *argv[]) +{ + Kitsunemimi::ErrorContainer error; + if(initMain(argc, argv, "shiori", ®isterArguments, ®isterConfigs, error) == false) + { + LOG_ERROR(error); + return 1; + } + + // init included components + Azuki::initAzukiBlossoms(); + Misaki::initMisakiBlossoms(); + + // initialize server and connections based on the config-file + const std::vector groupNames = {"misaki"}; + if(HanamiMessaging::getInstance()->initialize("shiori", + groupNames, + nullptr, + &streamDataCallback, + &genericMessageCallback, + error, + true) == false) + { + LOG_ERROR(error); + return 1; + } + + // init included components + Azuki::initAzukiBlossoms(); + Misaki::initMisakiBlossoms(); + + ShioriRoot rootObj; + if(rootObj.init() == false) { + return 1; + } + + // sleep forever + std::this_thread::sleep_until(std::chrono::time_point::max()); + + return 0; +} diff --git a/src/components/ShioriArchive/src/shiori_root.cpp b/src/components/ShioriArchive/src/shiori_root.cpp new file mode 100644 index 00000000..7a86fa23 --- /dev/null +++ b/src/components/ShioriArchive/src/shiori_root.cpp @@ -0,0 +1,126 @@ +/** + * @file shiori_root.cpp + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2022 Tobias Anker + * + * 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 "shiori_root.h" + +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +TempFileHandler* ShioriRoot::tempFileHandler = nullptr; +DataSetTable* ShioriRoot::dataSetTable = nullptr; +ClusterSnapshotTable* ShioriRoot::clusterSnapshotTable = nullptr; +RequestResultTable* ShioriRoot::requestResultTable = nullptr; +ErrorLogTable* ShioriRoot::errorLogTable = nullptr; +AuditLogTable* ShioriRoot::auditLogTable = nullptr; +Kitsunemimi::Sakura::SqlDatabase* ShioriRoot::database = nullptr; + +ShioriRoot::ShioriRoot() {} + +/** + * @brief init shiori-root-object + * + * @return true, if successfull, else false + */ +bool +ShioriRoot::init() +{ + Kitsunemimi::ErrorContainer error; + bool success = false; + + // read database-path from config + database = new Kitsunemimi::Sakura::SqlDatabase(); + const std::string databasePath = GET_STRING_CONFIG("DEFAULT", "database", success); + if(success == false) + { + error.addMeesage("No database-path defined in config."); + LOG_ERROR(error); + return false; + } + + // initalize database + if(database->initDatabase(databasePath, error) == false) + { + error.addMeesage("Failed to initialize sql-database."); + LOG_ERROR(error); + return false; + } + + // initialize dataset-table + dataSetTable = new DataSetTable(database); + if(dataSetTable->initTable(error) == false) + { + error.addMeesage("Failed to initialize dataset-table in database."); + LOG_ERROR(error); + return false; + } + + // initialize request-result-table + requestResultTable = new RequestResultTable(database); + if(requestResultTable->initTable(error) == false) + { + error.addMeesage("Failed to initialize request-result-table in database."); + LOG_ERROR(error); + return false; + } + + // initialize cluster-snapshot-table + clusterSnapshotTable = new ClusterSnapshotTable(database); + if(clusterSnapshotTable->initTable(error) == false) + { + error.addMeesage("Failed to initialize cluster-snapshot-table in database."); + LOG_ERROR(error); + return false; + } + + // initialize error-log-table + errorLogTable = new ErrorLogTable(database); + if(errorLogTable->initTable(error) == false) + { + error.addMeesage("Failed to initialize error-log-table in database."); + LOG_ERROR(error); + return false; + } + + // initialize audit-log-table + auditLogTable = new AuditLogTable(database); + if(auditLogTable->initTable(error) == false) + { + error.addMeesage("Failed to initialize audit-log-table in database."); + LOG_ERROR(error); + return false; + } + + // create new tempfile-handler + tempFileHandler = new TempFileHandler(); + + initBlossoms(); + + return true; +} diff --git a/src/components/ShioriArchive/src/shiori_root.h b/src/components/ShioriArchive/src/shiori_root.h new file mode 100644 index 00000000..839cc2b2 --- /dev/null +++ b/src/components/ShioriArchive/src/shiori_root.h @@ -0,0 +1,54 @@ +/** + * @file shiori_root.h + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2022 Tobias Anker + * + * 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. + */ + +#ifndef SHIORIARCHIVE_SHIORIROOT_H +#define SHIORIARCHIVE_SHIORIROOT_H + +namespace Kitsunemimi { +namespace Sakura { +class SqlDatabase; +} +} +class DataSetTable; +class ClusterSnapshotTable; +class RequestResultTable; +class ErrorLogTable; +class AuditLogTable; +class TempFileHandler; + +class ShioriRoot +{ +public: + ShioriRoot(); + + bool init(); + + static TempFileHandler* tempFileHandler; + static DataSetTable* dataSetTable; + static ClusterSnapshotTable* clusterSnapshotTable; + static RequestResultTable* requestResultTable; + static ErrorLogTable* errorLogTable; + static AuditLogTable* auditLogTable; + static Kitsunemimi::Sakura::SqlDatabase* database; +}; + +#endif // SHIORIARCHIVE_SHIORIROOT_H diff --git a/src/components/ToriiGateway/CHANGELOG_old b/src/components/ToriiGateway/CHANGELOG_old new file mode 100644 index 00000000..b561d00d --- /dev/null +++ b/src/components/ToriiGateway/CHANGELOG_old @@ -0,0 +1,94 @@ +# Changelog + +## [0.6.1] - 2022-07-02 + +### Changed +- updated dependencies + + +## [0.6.0] - 2022-06-28 + +### Added +- send audit-logs to sagiri +- message-forwarding to kyouko over a temporeary session now possible, which also allows messages from kyouko to the outside + +### Changed +- Websockets now on the same port like the REST-API +- use websockets instead of sakura-network to interact with the backend +- changes in config + +### Fixed +- fixed file-requests for dashboard + + +## [0.5.0] - 2022-02-14 + +### Added +- forwarding of stream-messages with a new proxy-endpoint + + +## [0.4.0] - 2022-01-09 + +### Changed +- updated http-requests +- connection to misaka to validate incomming requesets +- updated to c++17 + + +## [0.3.0] - 2021-07-30 + +### Added +- support request of png-images + +### Removed +- monitoring-endpoint removed + + +## [0.2.0] - 2021-03-29 + +### Changed +- only use one port for all functionalities to avoid cross-site-scripting problems with policies in web-browser + + +## [0.1.4] - 2021-01-02 + +### Fixed +- handling of the websocket for the client was broken +- in some cases a http-request was broken because of an improper copy of a string + + +## [0.1.3] - 2020-12-12 + +### Fixed +- error-code handling for web-sockets +- close-process for websockets + + +## [0.1.2] - 2020-12-06 + +### Added +- some more error-output + +### Changed +- updated client-handling and internal callbacks for current version of libKitsunemimiSakuraMessaging + +### Fixed +- race-condition at the start, which sometime could break the programm +- added missing try-catch for web-sockets, which could also result in seg-fault + + +## [0.1.1] - 2020-12-05 + +### Added +- add support to process POST-requests for control-connection + + +## [0.1.0] - 2020-12-02 + +### Added +- initial version + - http-session and -server for client, monitoring and control + - websocket-session and -server for client and monitoring + + + diff --git a/src/components/ToriiGateway/README.md b/src/components/ToriiGateway/README.md new file mode 100644 index 00000000..299460eb --- /dev/null +++ b/src/components/ToriiGateway/README.md @@ -0,0 +1,15 @@ +# ToriiGateway + +## Description + +Proxy-component of the Hanami-AI-Project: https://github.com/kitsudaiki/Hanami-AI + +## Author + +Tobias Anker + +eMail: tobias.anker@kitsunemimi.moe + +## License + +This project is licensed under the Apache License Version 2.0 - see the [LICENSE](LICENSE) file for details diff --git a/src/components/ToriiGateway/ToriiGateway.pro b/src/components/ToriiGateway/ToriiGateway.pro new file mode 100644 index 00000000..a2c5dd88 --- /dev/null +++ b/src/components/ToriiGateway/ToriiGateway.pro @@ -0,0 +1,115 @@ +QT -= qt core gui + +TARGET = ToriiGateway +CONFIG += console c++17 +CONFIG -= app_bundle + +LIBS += -L../../libraries/libShioriArchive/src -lShioriArchive +LIBS += -L../../libraries/libShioriArchive/src/debug -lShioriArchive +LIBS += -L../../libraries/libShioriArchive/src/release -lShioriArchive +INCLUDEPATH += ../../libraries/libShioriArchive/include + +LIBS += -L../../libraries/libAzukiHeart/src -lAzukiHeart +LIBS += -L../../libraries/libAzukiHeart/src/debug -lAzukiHeart +LIBS += -L../../libraries/libAzukiHeart/src/release -lAzukiHeart +INCLUDEPATH += ../../libraries/libAzukiHeart/include + +LIBS += -L../../libraries/libKitsunemimiHanamiNetwork/src -lKitsunemimiHanamiNetwork +LIBS += -L../../libraries/libKitsunemimiHanamiNetwork/src/debug -lKitsunemimiHanamiNetwork +LIBS += -L../../libraries/libKitsunemimiHanamiNetwork/src/release -lKitsunemimiHanamiNetwork +INCLUDEPATH += ../../libraries/libKitsunemimiHanamiNetwork/include + +LIBS += -L../../libraries/libKitsunemimiArgs/src -lKitsunemimiArgs +LIBS += -L../../libraries/libKitsunemimiArgs/src/debug -lKitsunemimiArgs +LIBS += -L../../libraries/libKitsunemimiArgs/src/release -lKitsunemimiArgs +INCLUDEPATH += ../../libraries/libKitsunemimiArgs/include + +LIBS += -L../../libraries/libKitsunemimiConfig/src -lKitsunemimiConfig +LIBS += -L../../libraries/libKitsunemimiConfig/src/debug -lKitsunemimiConfig +LIBS += -L../../libraries/libKitsunemimiConfig/src/release -lKitsunemimiConfig +INCLUDEPATH += ../../libraries/libKitsunemimiConfig/include + +LIBS += -L../../libraries/libKitsunemimiSakuraNetwork/src -lKitsunemimiSakuraNetwork +LIBS += -L../../libraries/libKitsunemimiSakuraNetwork/src/debug -lKitsunemimiSakuraNetwork +LIBS += -L../../libraries/libKitsunemimiSakuraNetwork/src/release -lKitsunemimiSakuraNetwork +INCLUDEPATH += ../../libraries/libKitsunemimiSakuraNetwork/include + +LIBS += -L../../libraries/libKitsunemimiCommon/src -lKitsunemimiCommon +LIBS += -L../../libraries/libKitsunemimiCommon/src/debug -lKitsunemimiCommon +LIBS += -L../../libraries/libKitsunemimiCommon/src/release -lKitsunemimiCommon +INCLUDEPATH += ../../libraries/libKitsunemimiCommon/include + +LIBS += -L../../libraries/libKitsunemimiNetwork/src -lKitsunemimiNetwork +LIBS += -L../../libraries/libKitsunemimiNetwork/src/debug -lKitsunemimiNetwork +LIBS += -L../../libraries/libKitsunemimiNetwork/src/release -lKitsunemimiNetwork +INCLUDEPATH += ../../libraries/libKitsunemimiNetwork/include + +LIBS += -L../../libraries/libKitsunemimiJson/src -lKitsunemimiJson +LIBS += -L../../libraries/libKitsunemimiJson/src/debug -lKitsunemimiJson +LIBS += -L../../libraries/libKitsunemimiJson/src/release -lKitsunemimiJson +INCLUDEPATH += ../../libraries/libKitsunemimiJson/include + +LIBS += -L../../libraries/libKitsunemimiIni/src -lKitsunemimiIni +LIBS += -L../../libraries/libKitsunemimiIni/src/debug -lKitsunemimiIni +LIBS += -L../../libraries/libKitsunemimiIni/src/release -lKitsunemimiIni +INCLUDEPATH += ../../libraries/libKitsunemimiIni/include + +LIBS += -L../../libraries/libKitsunemimiHanamiCommon/src -lKitsunemimiHanamiCommon +LIBS += -L../../libraries/libKitsunemimiHanamiCommon/src/debug -lKitsunemimiHanamiCommon +LIBS += -L../../libraries/libKitsunemimiHanamiCommon/src/release -lKitsunemimiHanamiCommon +INCLUDEPATH += ../../libraries/libKitsunemimiHanamiCommon/include + +LIBS += -L../../libraries/libKitsunemimiJwt/src -lKitsunemimiJwt +LIBS += -L../../libraries/libKitsunemimiJwt/src/debug -lKitsunemimiJwt +LIBS += -L../../libraries/libKitsunemimiJwti/src/release -lKitsunemimiJwt +INCLUDEPATH += ../../libraries/libKitsunemimiJwt/include + +LIBS += -L../../libraries/libKitsunemimiCrypto/src -lKitsunemimiCrypto +LIBS += -L../../libraries/libKitsunemimiCrypto/src/debug -lKitsunemimiCrypto +LIBS += -L../../libraries/libKitsunemimiCrypto/src/release -lKitsunemimiCrypto +INCLUDEPATH += ../../libraries/libKitsunemimiCrypto/include + +LIBS += -lcryptopp -lssl -lcrypto -luuid -pthread -lprotobuf + +INCLUDEPATH += $$PWD \ + src + +SOURCES += \ + src/callbacks.cpp \ + src/core/http_processing/file_send.cpp \ + src/core/http_processing/http_processing.cpp \ + src/core/http_websocket_thread.cpp \ + src/main.cpp \ + src/torii_root.cpp \ + src/core/http_server.cpp + +HEADERS += \ + src/args.h \ + src/core/http_processing/file_send.h \ + src/core/http_processing/http_processing.h \ + src/core/http_processing/response_builds.h \ + src/core/http_processing/string_functions.h \ + src/core/http_websocket_thread.h \ + src/torii_root.h \ + src/core/http_server.h \ + src/config.h \ + src/callbacks.h + +SHIORI_PROTO_BUFFER = ../../libraries/libKitsunemimiHanamiMessages/protobuffers/shiori_messages.proto3 + +OTHER_FILES += $$SHIORI_PROTO_BUFFER + +protobuf_decl.name = protobuf headers +protobuf_decl.input = SHIORI_PROTO_BUFFER +protobuf_decl.output = ${QMAKE_FILE_IN_PATH}/${QMAKE_FILE_BASE}.proto3.pb.h +protobuf_decl.commands = protoc --cpp_out=${QMAKE_FILE_IN_PATH} --proto_path=${QMAKE_FILE_IN_PATH} ${QMAKE_FILE_NAME} +protobuf_decl.variable_out = HEADERS +QMAKE_EXTRA_COMPILERS += protobuf_decl + +protobuf_impl.name = protobuf sources +protobuf_impl.input = SHIORI_PROTO_BUFFER +protobuf_impl.output = ${QMAKE_FILE_IN_PATH}/${QMAKE_FILE_BASE}.proto3.pb.cc +protobuf_impl.depends = ${QMAKE_FILE_IN_PATH}/${QMAKE_FILE_BASE}.proto3.pb.h +protobuf_impl.commands = $$escape_expand(\n) +protobuf_impl.variable_out = SOURCES +QMAKE_EXTRA_COMPILERS += protobuf_impl diff --git a/src/components/ToriiGateway/build.sh b/src/components/ToriiGateway/build.sh new file mode 100755 index 00000000..cace156d --- /dev/null +++ b/src/components/ToriiGateway/build.sh @@ -0,0 +1,135 @@ +#!/bin/bash + +# get current directory-path and the path of the parent-directory +DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )" +PARENT_DIR="$(dirname "$DIR")" +CURRENT_BRANCH=$(git rev-parse --abbrev-ref HEAD) + +# create build-directory +BUILD_DIR="$PARENT_DIR/build" +mkdir -p $BUILD_DIR + +# create directory for the final result +RESULT_DIR="$PARENT_DIR/result" +mkdir -p $RESULT_DIR + +#----------------------------------------------------------------------------------------------------------------- + +function build_kitsune_lib_repo () { + REPO_NAME=$1 + NUMBER_OF_THREADS=$2 + ADDITIONAL_CONFIGS=$3 + + # create build directory for repo and go into this directory + REPO_DIR="$BUILD_DIR/$REPO_NAME" + mkdir -p $REPO_DIR + cd $REPO_DIR + + # build repo library with qmake + /usr/lib/x86_64-linux-gnu/qt5/bin/qmake "$PARENT_DIR/$REPO_NAME/$REPO_NAME.pro" -spec linux-g++ "CONFIG += optimize_full staticlib $ADDITIONAL_CONFIGS" + /usr/bin/make -j$NUMBER_OF_THREADS + + # copy build-result and include-files into the result-directory + echo "----------------------------------------------------------------------" + echo $RESULT_DIR + cp $REPO_DIR/src/$REPO_NAME.a $RESULT_DIR/ + cp -r $PARENT_DIR/$REPO_NAME/include $RESULT_DIR/ + ls -l $RESULT_DIR/include/ + ls -l $RESULT_DIR +} + +function download_repo_github () { + REPO_NAME=$1 + TAG_OR_BRANCH=$2 + + echo "" + echo "" + echo "++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++" + echo "$REPO_NAME" + echo "++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++" + echo "Branch/Tag: $TAG_OR_BRANCH" + + # clone repo + git clone https://github.com/kitsudaiki/$REPO_NAME.git "$PARENT_DIR/$REPO_NAME" + cd "$PARENT_DIR/$REPO_NAME" + + # checkout branch + if [[ $CURRENT_BRANCH =~ ^tag.* ]] || [[ $CURRENT_BRANCH =~ ^hotfix.* ]] || [[ $CURRENT_BRANCH =~ ^v.* ]] || [[ $CURRENT_BRANCH =~ ^rolling$ ]] || [[ $CURRENT_BRANCH =~ ^staging$ ]]; then + # if a stable branch, then use the defined tag of branch + # check if defined branch even exist + BRANCH_EXIST=$(git ls-remote --heads origin $TAG_OR_BRANCH) + if [[ -z "$BRANCH_EXIST" ]]; then + echo "" + echo "-------------------------------------------------------------------------------------" + echo "Branch or tag '$TAG_OR_BRANCH' does not exist for the repository '$REPO_NAME'" + echo "-------------------------------------------------------------------------------------" + echo "" + exit 1 + fi + git checkout $TAG_OR_BRANCH + else + # if develop or feature branch, then try to checkout the feature-branch in the other repo as well + # or otherwise use the develop-branch as default + BRANCH_EXIST=$(git ls-remote --heads origin $CURRENT_BRANCH) + if [[ -n "$BRANCH_EXIST" ]]; then + git checkout $CURRENT_BRANCH + else + git checkout develop + fi + fi +} + +function get_required_kitsune_lib_repo () { + REPO_NAME=$1 + TAG_OR_BRANCH=$2 + NUMBER_OF_THREADS=$3 + + download_repo_github $REPO_NAME $TAG_OR_BRANCH + build_kitsune_lib_repo $REPO_NAME $NUMBER_OF_THREADS +} + +#----------------------------------------------------------------------------------------------------------------- + +echo "###########################################################################################################" +echo "" +get_required_kitsune_lib_repo "libKitsunemimiCommon" "develop" 8 +get_required_kitsune_lib_repo "libKitsunemimiJson" "develop" 1 +get_required_kitsune_lib_repo "libKitsunemimiIni" "develop" 1 +get_required_kitsune_lib_repo "libKitsunemimiNetwork" "develop" 8 +get_required_kitsune_lib_repo "libKitsunemimiArgs" "develop" 8 +get_required_kitsune_lib_repo "libKitsunemimiConfig" "develop" 8 +get_required_kitsune_lib_repo "libKitsunemimiCrypto" "develop" 8 +get_required_kitsune_lib_repo "libKitsunemimiJwt" "develop" 8 +echo "" +echo "###########################################################################################################" +echo "" +get_required_kitsune_lib_repo "libKitsunemimiSakuraNetwork" "develop" 8 +echo "" +echo "###########################################################################################################" +echo "" +get_required_kitsune_lib_repo "libKitsunemimiHanamiCommon" "develop" 8 +download_repo_github "libKitsunemimiHanamiMessages" "develop" +get_required_kitsune_lib_repo "libKitsunemimiHanamiNetwork" "develop" 8 +echo "" +echo "###########################################################################################################" +echo "" +get_required_kitsune_lib_repo "libAzukiHeart" "develop" 8 +get_required_kitsune_lib_repo "libShioriArchive" "develop" 8 +echo "" +echo "###########################################################################################################" + +#----------------------------------------------------------------------------------------------------------------- + +# create build directory for ToriiGateway and go into this directory +LIB_KITSUNE_SAKURA_TREE_DIR="$BUILD_DIR/ToriiGateway" +mkdir -p $LIB_KITSUNE_SAKURA_TREE_DIR +cd $LIB_KITSUNE_SAKURA_TREE_DIR + +# build ToriiGateway library with qmake +/usr/lib/x86_64-linux-gnu/qt5/bin/qmake "$PARENT_DIR/ToriiGateway/ToriiGateway.pro" -spec linux-g++ "CONFIG += optimize_full" +/usr/bin/make -j4 + +# copy build-result and include-files into the result-directory +cp "$LIB_KITSUNE_SAKURA_TREE_DIR/ToriiGateway" "$RESULT_DIR/" + +#----------------------------------------------------------------------------------------------------------------- diff --git a/src/components/ToriiGateway/src/args.h b/src/components/ToriiGateway/src/args.h new file mode 100644 index 00000000..898e3508 --- /dev/null +++ b/src/components/ToriiGateway/src/args.h @@ -0,0 +1,47 @@ +/** + * @file args.h + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2022 Tobias Anker + * + * 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. + */ + +#ifndef TORIIGATEWAY_ARGS_H +#define TORIIGATEWAY_ARGS_H + +#include +#include + +/** + * @brief register cli-arguments + * + * @param argparser reference to argument parser + * + * @return true if successful, else false + */ +bool +registerArguments(Kitsunemimi::ArgParser* argparser, + Kitsunemimi::ErrorContainer &error) +{ + if(Kitsunemimi::Hanami::registerArguments(*argparser, error) == false) { + return false; + } + + return true; +} + +#endif // TORIIGATEWAY_ARGS_H diff --git a/src/components/ToriiGateway/src/callbacks.cpp b/src/components/ToriiGateway/src/callbacks.cpp new file mode 100644 index 00000000..7fc63313 --- /dev/null +++ b/src/components/ToriiGateway/src/callbacks.cpp @@ -0,0 +1,56 @@ +/** + * @file callbacks.cpp + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2022 Tobias Anker + * + * 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 +#include + +#include + +#include + +/** + * @brief forward data of a stream-message to a linked session + * + * @param target target-session to forward to + * @param data data to farward + * @param dataSize number of bytes to forward + */ +void +streamDataCallback(void* receiver, + Kitsunemimi::Sakura::Session*, + const void* data, + const uint64_t dataSize) +{ + HttpWebsocketThread* webSocket = static_cast(receiver); + webSocket->sendData(data, dataSize); +} + +/** + * @brief unused placeholder + */ +void +genericCallback(Kitsunemimi::Sakura::Session*, + const uint32_t, + void*, + const uint64_t, + const uint64_t) +{} diff --git a/src/components/ToriiGateway/src/callbacks.h b/src/components/ToriiGateway/src/callbacks.h new file mode 100644 index 00000000..076735a1 --- /dev/null +++ b/src/components/ToriiGateway/src/callbacks.h @@ -0,0 +1,45 @@ +/** + * @file callbacks.h + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2022 Tobias Anker + * + * 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. + */ + +#ifndef TORIIGATEWAY_CALLBACKS_H +#define TORIIGATEWAY_CALLBACKS_H + +#include + +namespace Kitsunemimi { +namespace Sakura { +class Session; +} +} + +void streamDataCallback(void* receiver, + Kitsunemimi::Sakura::Session*, + const void* data, + const uint64_t dataSize); + +void genericCallback(Kitsunemimi::Sakura::Session*, + const uint32_t, + void *, + const uint64_t, + const uint64_t); + +#endif // TORIIGATEWAY_CALLBACKS_H diff --git a/src/components/ToriiGateway/src/config.h b/src/components/ToriiGateway/src/config.h new file mode 100644 index 00000000..b62ed43b --- /dev/null +++ b/src/components/ToriiGateway/src/config.h @@ -0,0 +1,82 @@ +/** + * @file config.h + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2022 Tobias Anker + * + * 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. + */ + +#ifndef TORIIGATEWAY_GATEWAY_CONFIG_H +#define TORIIGATEWAY_GATEWAY_CONFIG_H + +#include + +#include +#include +#include + +/** + * @brief register configs + */ +void +registerConfigs(Kitsunemimi::ErrorContainer &error) +{ + Kitsunemimi::Hanami::registerBasicConfigs(error); + + // http-section + const std::string httpGroup = "http"; + REGISTER_BOOL_CONFIG( httpGroup, "enable", error, false); + REGISTER_BOOL_CONFIG( httpGroup, "enable_dashboard", error, false); + REGISTER_STRING_CONFIG( httpGroup, "dashboard_files", error, ""); + REGISTER_STRING_CONFIG( httpGroup, "certificate", error, "", true); + REGISTER_STRING_CONFIG( httpGroup, "key", error, "", true); + REGISTER_STRING_CONFIG( httpGroup, "ip", error, "0.0.0.0", true); + REGISTER_INT_CONFIG( httpGroup, "port", error, 12345, true); + REGISTER_INT_CONFIG( httpGroup, "number_of_threads", error, 4); + + // sakura-section + const std::string sakuraGroup = "sakura"; + REGISTER_BOOL_CONFIG( sakuraGroup, "enable", error, false); + REGISTER_STRING_CONFIG( sakuraGroup, "certificate", error, "", true); + REGISTER_STRING_CONFIG( sakuraGroup, "key", error, "", true); + REGISTER_STRING_CONFIG( sakuraGroup, "ip", error, "0.0.0.0", true); + REGISTER_INT_CONFIG( sakuraGroup, "port", error, 12345, true); +} + +bool +validateConfig(Kitsunemimi::ErrorContainer &) +{ + bool valid = Kitsunemimi::isConfigValid(); + if(valid == false) { + return false; + } + + const std::string fileLocation = GET_STRING_CONFIG("server", "dashboard_files", valid); + if(valid == false) { + return false; + } + + if(std::filesystem::exists(fileLocation) == false) { + return false; + } + + const std::string ip = GET_STRING_CONFIG("server", "ip", valid); + + return true; +} + +#endif // TORIIGATEWAY_GATEWAY_CONFIG_H diff --git a/src/components/ToriiGateway/src/core/http_processing/file_send.cpp b/src/components/ToriiGateway/src/core/http_processing/file_send.cpp new file mode 100644 index 00000000..0dc9c611 --- /dev/null +++ b/src/components/ToriiGateway/src/core/http_processing/file_send.cpp @@ -0,0 +1,125 @@ +/** + * @file file_send.cpp + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2022 Tobias Anker + * + * 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 "file_send.h" + +#include +#include +#include +#include + +/** + * @brief get response-type for the requested file + * + * @param ext file-extension + * + * @return response-type + */ +const std::string +getResponseType(const std::string &ext) +{ + if(beast::iequals(ext, ".htm")) { return "text/html"; } + if(beast::iequals(ext, ".html")) { return "text/html"; } + if(beast::iequals(ext, ".php")) { return "text/html"; } + if(beast::iequals(ext, ".css")) { return "text/css"; } + if(beast::iequals(ext, ".txt")) { return "text/plain"; } + if(beast::iequals(ext, ".js")) { return "application/javascript"; } + if(beast::iequals(ext, ".json")) { return "application/json"; } + if(beast::iequals(ext, ".xml")) { return "application/xml"; } + if(beast::iequals(ext, ".swf")) { return "application/x-shockwave-flash"; } + if(beast::iequals(ext, ".flv")) { return "video/x-flv"; } + if(beast::iequals(ext, ".png")) { return "image/png"; } + if(beast::iequals(ext, ".jpe")) { return "image/jpeg"; } + if(beast::iequals(ext, ".jpeg")) { return "image/jpeg"; } + if(beast::iequals(ext, ".jpg")) { return "image/jpeg"; } + if(beast::iequals(ext, ".gif")) { return "image/gif"; } + if(beast::iequals(ext, ".bmp")) { return "image/bmp"; } + if(beast::iequals(ext, ".ico")) { return "image/vnd.microsoft.icon"; } + if(beast::iequals(ext, ".tiff")) { return "image/tiff"; } + if(beast::iequals(ext, ".tif")) { return "image/tiff"; } + if(beast::iequals(ext, ".svg")) { return "image/svg+xml"; } + if(beast::iequals(ext, ".svgz")) { return "image/svg+xml"; } + + return "application/text"; +} + +/** + * @brief send file, which was requested + * + * @return true, if successful, else false + */ +bool +sendFileFromLocalLocation(http::response &response, + const std::string &dir, + const std::string &relativePath, + Kitsunemimi::ErrorContainer &error) +{ + // create file-path + std::string path = dir; + if(relativePath == "/" + || relativePath == "") + { + path += "/index.html"; + } + else + { + path += relativePath; + } + + LOG_DEBUG("load file " + path); + + // set response-type based on file-type + std::filesystem::path pathObj(path); + const std::string extension = pathObj.extension().string(); + response.set(http::field::content_type, getResponseType(extension)); + + // read file and send content back + std::string fileContent = ""; + if(Kitsunemimi::readFile(fileContent, path, error)) + { + beast::ostream(response.body()) << fileContent; + return true; + } + + response.result(http::status::internal_server_error); + response.set(http::field::content_type, "text/plain"); + + return false; +} + +/** + * @brief process file-request + * + * @param path requested file-apth + * + * @return false, if file not found, else true + */ +bool +processClientRequest(http::response &response, + const std::string &path, + Kitsunemimi::ErrorContainer &error) +{ + bool success = false; + const std::string fileLocation = GET_STRING_CONFIG("http", "dashboard_files", success); + // TODO: check success-flag + return sendFileFromLocalLocation(response, fileLocation, path, error); +} diff --git a/src/components/ToriiGateway/src/core/http_processing/file_send.h b/src/components/ToriiGateway/src/core/http_processing/file_send.h new file mode 100644 index 00000000..8f168ad0 --- /dev/null +++ b/src/components/ToriiGateway/src/core/http_processing/file_send.h @@ -0,0 +1,46 @@ +/** + * @file file_send.h + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2022 Tobias Anker + * + * 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. + */ + +#ifndef TORIIGATEWAY_FILE_SEND_H +#define TORIIGATEWAY_FILE_SEND_H + +#include +#include + +#include +#include + +#include + +namespace beast = boost::beast; // from +namespace http = beast::http; // from + +const std::string getResponseType(const std::string &ext); +bool sendFileFromLocalLocation(http::response &response, + const std::string &dir, + const std::string &relativePath, + Kitsunemimi::ErrorContainer &error); +bool processClientRequest(http::response &response, + const std::string &path, + Kitsunemimi::ErrorContainer &error); + +#endif // TORIIGATEWAY_FILE_SEND_H diff --git a/src/components/ToriiGateway/src/core/http_processing/http_processing.cpp b/src/components/ToriiGateway/src/core/http_processing/http_processing.cpp new file mode 100644 index 00000000..cdd0ae57 --- /dev/null +++ b/src/components/ToriiGateway/src/core/http_processing/http_processing.cpp @@ -0,0 +1,313 @@ +/** + * @file http_processing.cpp + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2022 Tobias Anker + * + * 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 "http_processing.h" + +#include +#include +#include +#include +#include + +#include +#include + +#include +#include + +#include + +using Kitsunemimi::Hanami::HanamiMessaging; +using Kitsunemimi::Hanami::HanamiMessagingClient; + +/** + * @brief process request and build response + */ +bool +processRequest(http::request &httpRequest, + http::response &httpResponse, + Kitsunemimi::ErrorContainer &error) +{ + // build default-header for response + httpResponse.version(httpRequest.version()); + httpResponse.keep_alive(false); + httpResponse.set(http::field::server, "ToriiGateway"); + httpResponse.result(http::status::ok); + httpResponse.set(http::field::content_type, "text/plain"); + + // collect and prepare relevant data + const http::verb messageType = httpRequest.method(); + std::string path = httpRequest.target().to_string(); + std::string payload = "{}"; + + // Request path must be absolute and not contain "..". + if(checkPath(path) == false) + { + error.addMeesage("Path '" + path + "' is not valid"); + httpResponse.result(http::status::bad_request); + return false; + } + + // check if http-type is supported + if(messageType != http::verb::get + && messageType != http::verb::post + && messageType != http::verb::put + && messageType != http::verb::delete_) + { + httpResponse.result(http::status::bad_request); + error.addMeesage("Invalid request-method '" + + std::string(httpRequest.method_string()) + + "'"); + beast::ostream(httpResponse.body()) << error.toString(); + return false; + } + + // check for dashboard-client-request + if(messageType == http::verb::get + && path.compare(0, 8, "/control") != 0) + { + if(processClientRequest(httpResponse, path, error) == false) + { + error.addMeesage("Failed to send dashboard-files"); + return false; + } + return true; + } + + // get payload of message + if(messageType == http::verb::post + || messageType == http::verb::put) + { + payload = httpRequest.body().data(); + } + + // get token from request-header + std::string token = ""; + if(httpRequest.count("X-Auth-Token") > 0) { + token = httpRequest.at("X-Auth-Token").to_string(); + } + + // handle control-messages + if(cutPath(path, "/control/")) + { + HttpRequestType hType = static_cast(messageType); + if(processControlRequest(httpResponse, path, token, payload, hType, error) == false) + { + error.addMeesage("Failed to process control-request"); + return false; + } + return true; + } + + // handle default, if nothing was found + error.addMeesage("no matching endpoint found for path '" + path + "'"); + genericError_ResponseBuild(httpResponse, + HttpResponseTypes::NOT_FOUND_RTYPE, + error.toString()); + + return false; +} + +/** + * @brief request token from misaki + * + * @param target target (misaki) + * @param hanamiRequest hanami-request for the token-request + * @param hanamiResponse reference for the response + * @param error reference for error-output + * + * @return true, if successful, else false + */ +bool +requestToken(http::response &httpResponse, + const std::string &target, + const Kitsunemimi::Hanami::RequestMessage &hanamiRequest, + Kitsunemimi::Hanami::ResponseMessage &hanamiResponse, + Kitsunemimi::ErrorContainer &error) +{ + HanamiMessaging* messaging = HanamiMessaging::getInstance(); + HanamiMessagingClient* client = messaging->getOutgoingClient(target); + if(client == nullptr) + { + return genericError_ResponseBuild(httpResponse, + hanamiResponse.type, + "Client '" + target + "' not found"); + } + + // make token-request + if(client->triggerSakuraFile(hanamiResponse, hanamiRequest, error) == false) + { + return genericError_ResponseBuild(httpResponse, + hanamiResponse.type, + hanamiResponse.responseContent); + } + + + // handle failed authentication + if(hanamiResponse.type == Kitsunemimi::Hanami::UNAUTHORIZED_RTYPE + || hanamiResponse.success == false) + { + return genericError_ResponseBuild(httpResponse, + hanamiResponse.type, + hanamiResponse.responseContent); + } + + return success_ResponseBuild(httpResponse, hanamiResponse.responseContent); +} + +/** + * @brief send request to misaki to check permissions + * + * @param token token to validate + * @param component requested compoent + * @param hanamiRequest hanami-request to the requested endpoint + * @param responseMsg reference for the response + * @param error reference for error-output + * + * @return true, if successful, else false + */ +bool +checkPermission(const std::string &token, + const std::string &component, + const Kitsunemimi::Hanami::RequestMessage &hanamiRequest, + Kitsunemimi::Hanami::ResponseMessage &responseMsg, + Kitsunemimi::ErrorContainer &error) +{ + Kitsunemimi::Hanami::RequestMessage requestMsg; + + requestMsg.id = "v1/auth"; + requestMsg.httpType = HttpRequestType::GET_TYPE; + requestMsg.inputValues = ""; + + requestMsg.inputValues.append("{\"token\":\""); + requestMsg.inputValues.append(token); + requestMsg.inputValues.append("\",\"component\":\""); + requestMsg.inputValues.append(component); + requestMsg.inputValues.append("\",\"endpoint\":\""); + requestMsg.inputValues.append(hanamiRequest.id); + requestMsg.inputValues.append("\",\"http_type\":"); + requestMsg.inputValues.append(std::to_string(static_cast(hanamiRequest.httpType))); + requestMsg.inputValues.append("}"); + + HanamiMessaging* messaging = HanamiMessaging::getInstance(); + if(messaging->misakiClient == nullptr) + { + // TODO: handle error + return false; + } + return messaging->misakiClient->triggerSakuraFile(responseMsg, requestMsg, error); +} + +/** + * @brief process control request + * + * @param uri requested uri + * @param token given token coming from the http-header + * @param inputValues json-formated input-values + * @param httpType type of the http-request + * @param error reference for error-output + * + * @return true, if successful, else false + */ +bool +processControlRequest(http::response &httpResponse, + const std::string &uri, + const std::string &token, + const std::string &inputValues, + const HttpRequestType httpType, + Kitsunemimi::ErrorContainer &error) +{ + std::string target = ""; + Kitsunemimi::Hanami::RequestMessage hanamiRequest; + Kitsunemimi::Hanami::ResponseMessage hanamiResponse; + HanamiMessaging* messaging = HanamiMessaging::getInstance(); + + // parse uri + hanamiRequest.httpType = httpType; + hanamiRequest.inputValues = inputValues; + if(parseUri(target, token, hanamiRequest, uri, error) == false) { + return invalid_ResponseBuild(httpResponse, error); + } + + // handle token-request + if(hanamiRequest.id == "v1/token" + && hanamiRequest.httpType == Kitsunemimi::Hanami::POST_TYPE) + { + return requestToken(httpResponse, target, hanamiRequest, hanamiResponse, error); + } + + // check authentication + if(checkPermission(token, target, hanamiRequest, hanamiResponse, error) == false) { + return internalError_ResponseBuild(httpResponse, error); + } + + // handle failed authentication + if(hanamiResponse.type == Kitsunemimi::Hanami::UNAUTHORIZED_RTYPE + || hanamiResponse.success == false) + { + return genericError_ResponseBuild(httpResponse, + hanamiResponse.type, + hanamiResponse.responseContent); + } + + // parse response to get user-uuid of the token + Kitsunemimi::JsonItem userData; + if(userData.parse(hanamiResponse.responseContent, error) == false) { + return internalError_ResponseBuild(httpResponse, error); + } + + // send audit-message to shiori + if(Shiori::sendAuditMessage(target, + hanamiRequest.id, + userData.get("id").getString(), + hanamiRequest.httpType, + error) == false) + { + error.addMeesage("Failed to send audit-log to Shiori"); + return internalError_ResponseBuild(httpResponse, error); + } + + // forward real request + HanamiMessagingClient* client = messaging->getOutgoingClient(target); + if(client == nullptr) + { + return genericError_ResponseBuild(httpResponse, + hanamiResponse.type, + "Client '" + target + "' not found"); + } + + // make real request + if(client->triggerSakuraFile(hanamiResponse, hanamiRequest, error) == false) { + return internalError_ResponseBuild(httpResponse, error); + } + + // handle error-response + if(hanamiResponse.success == false) + { + return genericError_ResponseBuild(httpResponse, + hanamiResponse.type, + hanamiResponse.responseContent); + } + + // handle success + return success_ResponseBuild(httpResponse, hanamiResponse.responseContent); +} diff --git a/src/components/ToriiGateway/src/core/http_processing/http_processing.h b/src/components/ToriiGateway/src/core/http_processing/http_processing.h new file mode 100644 index 00000000..0da97e47 --- /dev/null +++ b/src/components/ToriiGateway/src/core/http_processing/http_processing.h @@ -0,0 +1,82 @@ +/** + * @file http_processing.h + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2022 Tobias Anker + * + * 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. + */ + +#ifndef TORIIGATEWAY_HTTP_PROCESSING_H +#define TORIIGATEWAY_HTTP_PROCESSING_H + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include + +#include +#include + +namespace beast = boost::beast; // from +namespace http = beast::http; // from +namespace net = boost::asio; // from +using tcp = boost::asio::ip::tcp; // from +namespace websocket = beast::websocket; // from +namespace ssl = boost::asio::ssl; // from + +using Kitsunemimi::Hanami::HttpRequestType; +using Kitsunemimi::Hanami::HttpResponseTypes; + +namespace Kitsunemimi { +namespace Json { +class JsonItem; +} +} + +bool processRequest(http::request &httpRequest, + http::response &httpResponse, + Kitsunemimi::ErrorContainer &error); + +bool requestToken(http::response &httpResponse, + const std::string &target, + const Kitsunemimi::Hanami::RequestMessage &hanamiRequest, + Kitsunemimi::Hanami::ResponseMessage &hanamiResponse, + Kitsunemimi::ErrorContainer &error); +bool checkPermission(const std::string &token, + const std::string &component, + const Kitsunemimi::Hanami::RequestMessage &hanamiRequest, + Kitsunemimi::Hanami::ResponseMessage &responseMsg, + Kitsunemimi::ErrorContainer &error); +bool processControlRequest(http::response &httpResponse, + const std::string &uri, + const std::string &token, + const std::string &inputValues, + const Kitsunemimi::Hanami::HttpRequestType httpType, + Kitsunemimi::ErrorContainer &error); + +#endif // TORIIGATEWAY_HTTP_PROCESSING_H diff --git a/src/components/ToriiGateway/src/core/http_processing/response_builds.h b/src/components/ToriiGateway/src/core/http_processing/response_builds.h new file mode 100644 index 00000000..0929e5d6 --- /dev/null +++ b/src/components/ToriiGateway/src/core/http_processing/response_builds.h @@ -0,0 +1,107 @@ +/** + * @file response_builds.h + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2022 Tobias Anker + * + * 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. + */ + +#ifndef TORIIGATEWAY_RESPONSE_BUILDS_H +#define TORIIGATEWAY_RESPONSE_BUILDS_H + +#include +#include +#include + +#include +#include +#include + +namespace beast = boost::beast; // from +namespace http = beast::http; // from +namespace net = boost::asio; // from +using tcp = boost::asio::ip::tcp; // from + +/** + * @brief success_ResponseBuild + * @param httpResp + * @param message + */ +bool +success_ResponseBuild(http::response &httpResp, + const std::string &message) +{ + httpResp.result(http::status::ok); + httpResp.set(http::field::content_type, "text/json"); + beast::ostream(httpResp.body()) << message; + return true; +} + +/** + * @brief invalid_ResponseBuild + * @param httpResp + * @param message + */ +bool +invalid_ResponseBuild(http::response &httpResp, + Kitsunemimi::ErrorContainer &error) +{ + httpResp.result(http::status::bad_request); + httpResp.set(http::field::content_type, "text/plain"); + beast::ostream(httpResp.body()) << error.toString(); + LOG_ERROR(error); + return false; +} + +/** + * @brief internalError_ResponseBuild + * @param httpResp + * @param message + */ +bool +internalError_ResponseBuild(http::response &httpResp, + Kitsunemimi::ErrorContainer &error) +{ + httpResp.result(http::status::internal_server_error); + httpResp.set(http::field::content_type, "text/plain"); + // beast::ostream(httpResp.body()) << error.toString(); + LOG_ERROR(error); + return false; +} + +/** + * @brief genericError_ResponseBuild + * @param httpResp + * @param type + * @param message + */ +bool +genericError_ResponseBuild(http::response &httpResp, + const Kitsunemimi::Hanami::HttpResponseTypes type, + const std::string &errorMessage) +{ + httpResp.result(static_cast(type)); + httpResp.set(http::field::content_type, "text/plain"); + beast::ostream(httpResp.body()) << errorMessage; + + Kitsunemimi::ErrorContainer error; + error.addMeesage(errorMessage); + LOG_ERROR(error); + return false; +} + +#endif // TORIIGATEWAY_RESPONSE_BUILDS_H diff --git a/src/components/ToriiGateway/src/core/http_processing/string_functions.h b/src/components/ToriiGateway/src/core/http_processing/string_functions.h new file mode 100644 index 00000000..3263ee81 --- /dev/null +++ b/src/components/ToriiGateway/src/core/http_processing/string_functions.h @@ -0,0 +1,155 @@ +/** + * @file string_functions.h + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2022 Tobias Anker + * + * 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. + */ + +#ifndef TORIIGATEWAY_STRING_FUNCTIONS_H +#define TORIIGATEWAY_STRING_FUNCTIONS_H + +#include +#include + +#include +#include +#include +#include + +using Kitsunemimi::Hanami::HttpRequestType; +using Kitsunemimi::Hanami::HttpResponseTypes; + +/** + * @brief precheck path + * + * @param path file-path to check + * + * @return false, if path is invalid, else true + */ +inline bool +checkPath(const std::string &path) +{ + if(path.empty() + || path[0] != '/' + || path.find("..") != std::string::npos) + { + return false; + } + + return true; +} + +/** + * @brief parse uri + * + * @param target reference for teh target, which has to be parsed from the uri + * @param token jwt-token to add the the request + * @param request reference for the request, which should be filled by values from the uri + * @param uri uri to parse + * @param error reference for error-output + * + * @return true, if successful, else false + */ +inline bool +parseUri(std::string &target, + const std::string &token, + Kitsunemimi::Hanami::RequestMessage &request, + const std::string &uri, + Kitsunemimi::ErrorContainer &error) +{ + // first split of uri + Kitsunemimi::JsonItem parsedInputValues; + std::vector parts; + Kitsunemimi::splitStringByDelimiter(parts, uri, '?'); + + // check split-result + if(parts.size() == 0) + { + error.addMeesage("Uri is empty."); + return false; + } + if(parts.at(0).find("/") == std::string::npos) + { + error.addMeesage("Uri doesn't start with '/'."); + return false; + } + + if(parsedInputValues.parse(request.inputValues, error) == false) + { + error.addMeesage("Failed to parse input-values."); + return false; + } + + // split first part again to get target and real part + const size_t cutPos = parts.at(0).find("/"); + const std::string* mainPart = &parts[0]; + target = mainPart->substr(0, cutPos); + request.id = mainPart->substr(cutPos + 1, mainPart->size() - 1); + + // prepare payload, if exist + if(parts.size() > 1) + { + std::vector kvPairs; + Kitsunemimi::splitStringByDelimiter(kvPairs, parts[1], '&'); + + for(const std::string &kvPair : kvPairs) + { + const size_t cutPos = kvPair.find('='); + const std::string key = kvPair.substr(0, cutPos); + const std::string val = kvPair.substr(cutPos + 1, kvPair.size() - 1); + + // convert result if number and add to resulting map + if(regex_match(val, std::regex(INT_VALUE_REGEX))) { + parsedInputValues.insert(key, std::stoi(val.c_str(), NULL), true); + } else if(regex_match(val, std::regex(FLOAT_VALUE_REGEX))) { + parsedInputValues.insert(key, std::strtof(val.c_str(), NULL), true); + } else { + parsedInputValues.insert(key, val, true); + } + } + } + + // add token to list of normal values + parsedInputValues.insert("token", token); + + request.inputValues = parsedInputValues.toString(); + + return true; +} + +/** + * @brief cut first part of a string, if match + * + * @param path reference for the incoming path and updated path + * @param cut part, which have to match at the beginning of the path + * + * @return true, if first part match, else false + */ +inline bool +cutPath(std::string &path, const std::string &cut) +{ + if(path.compare(0, cut.size(), cut) == 0) + { + path.erase(0, cut.size()); + return true; + } + + return false; +} + +#endif // TORIIGATEWAY_STRING_FUNCTIONS_H diff --git a/src/components/ToriiGateway/src/core/http_server.cpp b/src/components/ToriiGateway/src/core/http_server.cpp new file mode 100644 index 00000000..a66cdea0 --- /dev/null +++ b/src/components/ToriiGateway/src/core/http_server.cpp @@ -0,0 +1,182 @@ +/** + * @file http_server.cpp + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2022 Tobias Anker + * + * 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 "http_server.h" + +#include + +#include +#include + +#include +#include + +/** + * @brief constructor + * + * @param address listen address for the server + * @param port listen port for the server + * @param certFilePath path to cert-file + * @param keyFilePath path to key-file + */ +HttpServer::HttpServer(const std::string &address, + const uint16_t port, + const std::string &certFilePath, + const std::string &keyFilePath) + : Kitsunemimi::Thread("HttpServer"), + m_ctx{boost::asio::ssl::context::tlsv13_server}, + m_address(address), + m_port(port), + m_certFilePath(certFilePath), + m_keyFilePath(keyFilePath) +{} + +/** + * @brief load certificates from files into ssl-context + * + * @param ctx ssl-context + * @param error reference for error-output + * + * @return true, if successful, else false + */ +bool +HttpServer::loadCertificates(boost::asio::ssl::context &ctx, + Kitsunemimi::ErrorContainer &error) +{ + std::string errorMessage = ""; + std::string cert = ""; + std::string key = ""; + bool ret = false; + + // read certificate + ret = Kitsunemimi::readFile(cert, m_certFilePath, error); + if(ret == false) + { + error.addMeesage("failed to read certificate-file: '" + m_certFilePath + "'"); + return false; + } + + // read key + ret = Kitsunemimi::readFile(key, m_keyFilePath, error); + if(ret == false) + { + error.addMeesage("failed to read key-file: '" + m_certFilePath + "'"); + return false; + } + + // TODO: replace hard-coded string by a file + /*const std::string dh = "-----BEGIN DH PARAMETERS-----\n" + "MIIBCAKCAQEArzQc5mpm0Fs8yahDeySj31JZlwEphUdZ9StM2D8+Fo7TMduGtSi+\n" + "/HRWVwHcTFAgrxVdm+dl474mOUqqaz4MpzIb6+6OVfWHbQJmXPepZKyu4LgUPvY/\n" + "4q3/iDMjIS0fLOu/bLuObwU5ccZmDgfhmz1GanRlTQOiYRty3FiOATWZBRh6uv4u\n" + "tff4A9Bm3V9tLx9S6djq31w31Gl7OQhryodW28kc16t9TvO1BzcV3HjRPwpe701X\n" + "oEEZdnZWANkkpR/m/pfgdmGPU66S2sXMHgsliViQWpDCYeehrvFRHEdR9NV+XJfC\n" + "QMUk26jPTIVTLfXmmwU0u8vUkpR7LQKkwwIBAg==\n" + "-----END DH PARAMETERS-----\n";*/ + + + ctx.set_options(boost::asio::ssl::context::default_workarounds | + boost::asio::ssl::context::no_sslv2); + //boost::asio::ssl::context::single_dh_use); + + ctx.use_certificate_chain(boost::asio::buffer(cert.data(), cert.size())); + + ctx.use_private_key(boost::asio::buffer(key.data(), key.size()), + boost::asio::ssl::context::file_format::pem); + + //ctx.use_tmp_dh(boost::asio::buffer(dh.data(), dh.size())); + + return true; +} + +/** + * @brief run server-thread + */ +void +HttpServer::run() +{ + Kitsunemimi::ErrorContainer error; + + LOG_INFO("start HTTP-server on address " + + m_address + + " and port " + + std::to_string(m_port)); + try + { + // create server + const net::ip::address address = net::ip::make_address(m_address); + net::io_context ioc{1}; + tcp::acceptor acceptor{ioc, {address, m_port}}; + if(loadCertificates(m_ctx, error) == false) + { + LOG_ERROR(error); + return; + } + + while(m_abort == false) + { + // create socket-object for incoming connection + tcp::socket* socket = new tcp::socket{ioc}; + acceptor.accept(*socket); + addSocket(socket); + } + } + catch (const std::exception& e) + { + error.addMeesage("Error-message while running http-server: '" + + std::string(e.what()) + + "'"); + LOG_ERROR(error); + } +} + +/** + * @brief get socket from the queue + * + * @return socket + */ +tcp::socket* +HttpServer::getSocket() +{ + std::lock_guard guard(m_queueMutex); + + tcp::socket* result = nullptr; + if(m_queue.size() > 0) + { + result = m_queue.front(); + m_queue.pop_front(); + } + + return result; +} + +/** + * @brief add socket to queue + * + * @param socket new socket for the queue + */ +void +HttpServer::addSocket(tcp::socket* socket) +{ + std::lock_guard guard(m_queueMutex); + m_queue.push_back(socket); +} diff --git a/src/components/ToriiGateway/src/core/http_server.h b/src/components/ToriiGateway/src/core/http_server.h new file mode 100644 index 00000000..f9bda471 --- /dev/null +++ b/src/components/ToriiGateway/src/core/http_server.h @@ -0,0 +1,78 @@ +/** + * @file http_server.h + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2022 Tobias Anker + * + * 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. + */ + +#ifndef TORIIGATEWAY_HTTP_SERVER_H +#define TORIIGATEWAY_HTTP_SERVER_H + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +using tcp = boost::asio::ip::tcp; + +class HttpWebsocketThread; + +class HttpServer + : public Kitsunemimi::Thread +{ +public: + HttpServer(const std::string &address, + const uint16_t port, + const std::string &cert, + const std::string &key); + + boost::asio::ssl::context m_ctx; + + tcp::socket* getSocket(); + void addSocket(tcp::socket* socket); + +protected: + void run(); + +private: + const std::string m_address = ""; + const uint16_t m_port = 0; + const std::string m_certFilePath = ""; + const std::string m_keyFilePath = ""; + + std::deque m_queue; + std::mutex m_queueMutex; + + bool loadCertificates(boost::asio::ssl::context &ctx, + Kitsunemimi::ErrorContainer &error); +}; + +#endif // TORIIGATEWAY_HTTP_SERVER_H diff --git a/src/components/ToriiGateway/src/core/http_websocket_thread.cpp b/src/components/ToriiGateway/src/core/http_websocket_thread.cpp new file mode 100644 index 00000000..186b36a1 --- /dev/null +++ b/src/components/ToriiGateway/src/core/http_websocket_thread.cpp @@ -0,0 +1,525 @@ +/** + * @file http_websocket_thread.cpp + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2022 Tobias Anker + * + * 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 "http_websocket_thread.h" + +#include +#include +#include +#include + +#include +#include +#include + +#include +#include + +using namespace Kitsunemimi; + +/** + * @brief constructor + */ +HttpWebsocketThread::HttpWebsocketThread(const std::string &threadName) + : Thread(threadName) {} + +/** + * @brief HttpThread::run + */ +void +HttpWebsocketThread::run() +{ + while(m_abort == false) + { + tcp::socket* socket = ToriiGateway::httpServer->getSocket(); + if(socket != nullptr) + { + ErrorContainer error; + + if(handleSocket(socket, error) == false) { + LOG_ERROR(error); + } + delete socket; + } + else + { + sleepThread(10000); + } + } +} + +/** + * @brief handle new incoming http-connection + * + * @param socket pointer to new socket to process + * @param error reference for error-output + * + * @return true, if successful, else false + */ +bool +HttpWebsocketThread::handleSocket(tcp::socket* socket, + ErrorContainer &error) +{ + beast::ssl_stream stream{*socket, std::ref(ToriiGateway::httpServer->m_ctx)}; + http::request httpRequest; + http::response httpResponse; + bool processResult = true; + + // perform the SSL handshake + beast::error_code ec; + stream.handshake(boost::asio::ssl::stream_base::server, ec); + if(ec.failed()) + { + error.addMeesage("SSL-Handshake failed while receiving new http-connection"); + return false; + } + + // read http-message + if(readMessage(stream, httpRequest, error) == false) + { + error.addMeesage("Can read http-request"); + return false; + } + + // check if request belongs to a new websocket-request + if(websocket::is_upgrade(httpRequest)) + { + // initialize new websocket-session + websocket::stream> webSocket(std::move(stream)); + m_webSocket = &webSocket; + if(initWebsocket(httpRequest) == false) + { + error.addMeesage("Can not init websocket."); + return false; + } + + runWebsocket(); + m_client = nullptr; + m_uuid = ""; + m_target = ""; + } + else + { + // process request + processResult = processRequest(httpRequest, httpResponse, error); + if(processResult == false) + { + error.addMeesage("Failed to process http-request."); + // IMPORANT: no return false here, because the reponse should retunred anyway + } + if(sendResponse(stream, httpResponse, error) == false) + { + error.addMeesage("Can not send http-response."); + return false; + } + + // close socket gain + beast::error_code ec; + stream.shutdown(ec); + + if(ec) + { + error.addMeesage("Error while closing http-stream: " + ec.message()); + return false; + } + } + + return processResult; +} + +/** + * @brief handle message coming over the http-socket + * + * @param stream incoming stream + * @param httpRequest incoming request over the stream + * @param error reference for error-output + * + * @return true, if successful, else false + */ +bool +HttpWebsocketThread::readMessage(beast::ssl_stream &stream, + http::request &httpRequest, + ErrorContainer &error) +{ + beast::error_code ec; + beast::flat_buffer buffer; + http::read(stream, buffer, httpRequest, ec); + + if(ec == http::error::end_of_stream) { + return true; + } + + if(ec) + { + error.addMeesage("Error while reading http-message: '" + ec.message() + "'"); + LOG_ERROR(error); + return false; + } + + return true; +} + +/** + * @brief send message over the http-socket + * + * @param stream outgoing stream + * @param httpResponse response to send over the stream + * @param error reference for error-output + * + * @return true, if successful, else false + */ +bool +HttpWebsocketThread::sendResponse(beast::ssl_stream &stream, + http::response &httpResponse, + ErrorContainer &error) +{ + beast::error_code ec; + httpResponse.content_length(httpResponse.body().size()); + http::write(stream, httpResponse, ec); + + if(ec) + { + error.addMeesage("Error while writing http-message: '" + ec.message() + "'"); + LOG_ERROR(error); + return false; + } + + return true; +} + +/** + * @brief initialize websocket-connection + * + * @param httpRequest initial http-request to init the websocket + * + * @return true, if successful, else false + */ +bool +HttpWebsocketThread::initWebsocket(http::request &httpRequest) +{ + try + { + // Set a decorator to change the Server of the handshake + m_webSocket->set_option(websocket::stream_base::decorator( + [](websocket::response_type& res) + { + res.set(http::field::server, + std::string(BOOST_BEAST_VERSION_STRING) + + " torii-websocket-ssl"); + })); + + // Accept the websocket handshake + m_webSocket->accept(std::move(httpRequest)); + } + catch(const beast::system_error& se) + { + if(se.code() == websocket::error::closed) + { + LOG_INFO("Close websocket"); + } + else + { + ErrorContainer error; + error.addMeesage("Error while receiving data over websocket with message: " + + se.code().message()); + LOG_ERROR(error); + return false; + } + } + catch(const std::exception& e) + { + ErrorContainer error; + error.addMeesage("Error while receiving data over websocket with message: " + + std::string(e.what())); + LOG_ERROR(error); + return false; + } + + return true; +} + +/** + * @brief send data over web-socket + * + * @param data pointer to buffer with data to send + * @param dataSize number of bytes to send + * + * @return true, if successful, else false + */ +bool +HttpWebsocketThread::sendData(const void* data, + const uint64_t dataSize) +{ + if(m_abort) { + return false; + } + + try + { + while(m_waitForInput == true + && m_websocketClosed == false) + { + usleep(1000); + } + + if(m_websocketClosed) { + return false; + } + + beast::flat_buffer buffer; + m_webSocket->binary(true); + m_webSocket->write(net::buffer(data, dataSize)); + m_waitForInput = true; + + return true; + } + catch(const beast::system_error& se) + { + if(se.code() == websocket::error::closed) + { + LOG_INFO("Close websocket"); + } + else + { + ErrorContainer error; + error.addMeesage("Error while sending data over websocket with message: " + + se.code().message()); + LOG_ERROR(error); + } + } + catch(const std::exception& e) + { + ErrorContainer error; + error.addMeesage("Error while sending data over websocket with message: " + + std::string(e.what())); + LOG_ERROR(error); + } + + m_waitForInput = true; + + return false; +} + +/** + * @brief run the initial process to forward a websocket-connection to the backend + * + * @param message initial message to enable message-forwarding + * @param error reference for error-output + * + * @return true, if successful, else false + */ +bool +HttpWebsocketThread::processInitialMessage(const std::string &message, + ErrorContainer &error) +{ + Hanami::HanamiMessaging* messageInterface = Hanami::HanamiMessaging::getInstance(); + JsonItem content; + if(content.parse(message, error) == false) + { + error.addMeesage("Parsing of initial websocket-message failed"); + LOG_ERROR(error); + return false; + } + + m_target = content.get("target").getString(); + + Hanami::RequestMessage requestMsg; + Hanami::ResponseMessage responseMsg; + + requestMsg.id = "v1/foreward"; + requestMsg.httpType = HttpRequestType::GET_TYPE; + + // check authentication + if(checkPermission(content.get("token").getString(), + m_target, + requestMsg, + responseMsg, + error) == false) + { + error.addMeesage("Request to misaki for token-check failed"); + LOG_ERROR(error); + return false; + } + + // handle failed authentication + if(responseMsg.type == Hanami::UNAUTHORIZED_RTYPE + || responseMsg.success == false) + { + error.addMeesage("Permission-check for token over websocket failed"); + LOG_ERROR(error); + return false; + } + + // forward connection to shiori or kyouko + if(m_target == "shiori") + { + // get target-session by name + m_client = messageInterface->getOutgoingClient(m_target); + if(m_client == nullptr) + { + error.addMeesage("Forwarind of websocket to target '" + m_target + "' failed"); + LOG_ERROR(error); + return false; + } + + return true; + } + else if(m_target == "kyouko") + { + m_client = messageInterface->createTemporaryClient(m_uuid, m_target, error); + if(m_client == nullptr) + { + error.addMeesage("Forwarind of websocket to target '" + m_target + "' failed"); + LOG_ERROR(error); + return false; + } + + m_client->setStreamCallback(this, streamDataCallback); + + return true; + } + + error.addMeesage("Session-forwarding to target '" + m_target + "' is not allowed"); + LOG_ERROR(error); + + return false; +} + +/** + * @brief close temporary client, if exist + * + * @param error reference for error-output + */ +void +HttpWebsocketThread::closeClient(ErrorContainer &error) +{ + if(m_client != nullptr) + { + if(m_target == "kyouko") + { + if(m_client->closeClient(error) == false) + { + error.addMeesage("Failed to close temporary connection to kyouko"); + LOG_ERROR(error); + } + m_client->scheduleThreadForDeletion(); + } + + m_client = nullptr; + } +} + +/** + * @brief run the websocket + */ +void +HttpWebsocketThread::runWebsocket() +{ + ErrorContainer error; + m_websocketClosed = false; + + try + { + while(m_abort == false) + { + // read message from socket + beast::flat_buffer buffer; + while(m_waitForInput == false + && m_websocketClosed == false) + { + usleep(1000); + } + + if(m_websocketClosed) { + break; + } + + m_webSocket->read(buffer); + m_waitForInput = false; + + //m_webSocket.text(m_webSocket.got_text()); + if(m_client == nullptr) + { + const std::string msg(static_cast(buffer.data().data()), + buffer.data().size()); + m_uuid = Hanami::generateUuid().toString(); + + LOG_DEBUG("got initial websocket-message: '" + msg + "'"); + bool success = true; + if(processInitialMessage(msg, error) == false) + { + success = false; + error.addMeesage("Failed initializing of websocket-forwarding"); + LOG_ERROR(error); + error = ErrorContainer(); + } + + // build response-message + std::string response = "{\"success\":" + std::to_string(success); + if(success) + { + response.append(",\"uuid\":"); + response.append(m_uuid); + } + response.append("}"); + + m_webSocket->binary(true); + m_webSocket->write(net::buffer(response, response.size())); + m_waitForInput = true; + } + else + { + m_client->sendStreamMessage(buffer.data().data(), + buffer.data().size(), + false, + error); + + if(m_target == "shiori") { + m_waitForInput = true; + } + } + } + } + catch(const beast::system_error& se) + { + if(se.code() == websocket::error::closed) + { + LOG_INFO("Close websocket"); + } + else + { + ErrorContainer error; + error.addMeesage("Error while receiving data over websocket with message: " + + se.code().message()); + } + } + catch(const std::exception& e) + { + ErrorContainer error; + error.addMeesage("Error while receiving data over websocket with message: " + + std::string(e.what())); + } + + m_websocketClosed = true; + closeClient(error); + LOG_ERROR(error); +} diff --git a/src/components/ToriiGateway/src/core/http_websocket_thread.h b/src/components/ToriiGateway/src/core/http_websocket_thread.h new file mode 100644 index 00000000..b357b1c8 --- /dev/null +++ b/src/components/ToriiGateway/src/core/http_websocket_thread.h @@ -0,0 +1,92 @@ +/** + * @file http_websocket_thread.h + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2022 Tobias Anker + * + * 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. + */ + +#ifndef TORIIGATEWAY_HTTP_THREAD_H +#define TORIIGATEWAY_HTTP_THREAD_H + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include + +namespace beast = boost::beast; // from +namespace http = beast::http; // from +namespace net = boost::asio; // from +using tcp = boost::asio::ip::tcp; // from +namespace websocket = beast::websocket; // from +namespace ssl = boost::asio::ssl; // from + +namespace Kitsunemimi { +namespace Hanami { +class HanamiMessagingClient; +} +} + +class HttpWebsocketThread + : public Kitsunemimi::Thread +{ +public: + HttpWebsocketThread(const std::string &threadName); + + bool sendData(const void* data, const uint64_t dataSize); + +protected: + void run(); + +private: + bool handleSocket(tcp::socket* socket, + Kitsunemimi::ErrorContainer &error); + bool readMessage(beast::ssl_stream &stream, + http::request &httpRequest, + Kitsunemimi::ErrorContainer &error); + bool sendResponse(beast::ssl_stream &stream, + http::response &httpResponse, + Kitsunemimi::ErrorContainer &error); + + // websocket-functions and variables + bool initWebsocket(http::request &httpRequest); + void runWebsocket(); + void closeClient(Kitsunemimi::ErrorContainer &error); + bool processInitialMessage(const std::string &message, + Kitsunemimi::ErrorContainer &error); + + websocket::stream>* m_webSocket = nullptr; + std::string m_uuid = ""; + std::string m_target = ""; + bool m_waitForInput = true; + bool m_websocketClosed = true; + Kitsunemimi::Hanami::HanamiMessagingClient* m_client = nullptr; +}; + +#endif // TORIIGATEWAY_HTTP_THREAD_H diff --git a/src/components/ToriiGateway/src/main.cpp b/src/components/ToriiGateway/src/main.cpp new file mode 100644 index 00000000..5340bc4b --- /dev/null +++ b/src/components/ToriiGateway/src/main.cpp @@ -0,0 +1,74 @@ +/** + * @file main.cpp + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2022 Tobias Anker + * + * 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 + +#include +#include +#include +#include +#include + +#include +#include + +#include + +using Kitsunemimi::Hanami::HanamiMessaging; +using Kitsunemimi::Hanami::initMain; +using Kitsunemimi::Sakura::Session; + +int main(int argc, char *argv[]) +{ + Kitsunemimi::ErrorContainer error; + if(initMain(argc, argv, "torii", ®isterArguments, ®isterConfigs, error) == false) { + return 1; + } + + // init included components + Azuki::initAzukiBlossoms(); + + // initialize server and connections based on the config-file + const std::vector groupNames = { "misaki", "azuki", "shiori", "kyouko"}; + if(HanamiMessaging::getInstance()->initialize("torii", + groupNames, + nullptr, + &streamDataCallback, + &genericCallback, + error, + true) == false) + { + LOG_ERROR(error); + return 1; + } + + // init gateway + ToriiGateway rootObj; + if(rootObj.init() == false) { + return 1; + } + + // sleep forever + std::this_thread::sleep_until(std::chrono::time_point::max()); + + return 0; +} diff --git a/src/components/ToriiGateway/src/torii_root.cpp b/src/components/ToriiGateway/src/torii_root.cpp new file mode 100644 index 00000000..721f6afb --- /dev/null +++ b/src/components/ToriiGateway/src/torii_root.cpp @@ -0,0 +1,139 @@ +/** + * @file torii_root.cpp + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2022 Tobias Anker + * + * 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 "torii_root.h" + +#include + +#include +#include +#include + +#include + +#include + +using Kitsunemimi::Hanami::HanamiMessaging; + +#include + +HttpServer* ToriiGateway::httpServer = nullptr; + +/** + * @brief constructor + */ +ToriiGateway::ToriiGateway() {} + +/** + * @brief init torii-root-object + * + * @return true, if successfull, else false + */ +bool +ToriiGateway::init() +{ + Kitsunemimi::ErrorContainer error; + + if(initHttpServer() == false) + { + error.addMeesage("initializing http-server failed"); + LOG_ERROR(error); + return false; + } + + if(initSakuraServer() == false) + { + error.addMeesage("initializing sakura-server failed"); + LOG_ERROR(error); + return false; + } + + return true; +} + +/** + * @brief initialze http server + * + * @return true, if successful, else false + */ +bool +ToriiGateway::initHttpServer() +{ + bool success = false; + + // check if http is enabled + if(GET_BOOL_CONFIG("http", "enable", success) == false) { + return true; + } + + // get stuff from config + const uint16_t port = GET_INT_CONFIG( "http", "port", success); + const std::string ip = GET_STRING_CONFIG( "http", "ip", success); + const std::string cert = GET_STRING_CONFIG( "http", "certificate", success); + const std::string key = GET_STRING_CONFIG( "http", "key", success); + const uint32_t numberOfThreads = GET_INT_CONFIG( "http", "number_of_threads", success); + + + // create server + httpServer = new HttpServer(ip, port, cert, key); + httpServer->startThread(); + + // start threads + for(uint32_t i = 0; i < numberOfThreads; i++) + { + const std::string name = "HttpWebsocketThread"; + HttpWebsocketThread* httpWebsocketThread = new HttpWebsocketThread(name); + httpWebsocketThread->startThread(); + m_threads.push_back(httpWebsocketThread); + } + + return true; +} + +/** + * @brief initialze sakura server + * + * @return true, if successful, else false + */ +bool +ToriiGateway::initSakuraServer() +{ + bool success = false; + + // check if sakura-server is enabled + if(GET_BOOL_CONFIG("sakura", "enable", success) == false) { + return true; + } + + // get stuff from config + const uint16_t port = GET_INT_CONFIG( "sakura", "port", success); + const std::string ip = GET_STRING_CONFIG( "sakura", "ip", success); + const std::string cert = GET_STRING_CONFIG( "sakura", "certificate", success); + const std::string key = GET_STRING_CONFIG( "sakura", "key", success); + + // create server + Kitsunemimi::ErrorContainer error; + HanamiMessaging* messaging = HanamiMessaging::getInstance(); + messaging->addServer(ip, error, port, cert, key); + + return true; +} diff --git a/src/components/ToriiGateway/src/torii_root.h b/src/components/ToriiGateway/src/torii_root.h new file mode 100644 index 00000000..c374324f --- /dev/null +++ b/src/components/ToriiGateway/src/torii_root.h @@ -0,0 +1,52 @@ +/** + * @file torii_root.h + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2022 Tobias Anker + * + * 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. + */ + +#ifndef TORIIGATEWAY_TORIIROOT_H +#define TORIIGATEWAY_TORIIROOT_H + +#include +#include +#include +#include + +class WebSocketServer; +class HttpServer; +class HttpWebsocketThread; + +class ToriiGateway +{ +public: + ToriiGateway(); + + bool init(); + + WebSocketServer* websocketServer = nullptr; + static HttpServer* httpServer; + +private: + std::vector m_threads; + + bool initHttpServer(); + bool initSakuraServer(); +}; + +#endif // TORIIGATEWAY_TORIIROOT_H diff --git a/src/components/TsugumiTester/CHANGELOG_old b/src/components/TsugumiTester/CHANGELOG_old new file mode 100644 index 00000000..dfbccb52 --- /dev/null +++ b/src/components/TsugumiTester/CHANGELOG_old @@ -0,0 +1,25 @@ +# Changelog + +## [0.3.1] - 2022-07-02 + +### Changed +- updated dependencies + + +## [0.3.0] - 2022-06-28 + +### Changed +- updates for the current SDK and backend +- updated test-structure in general + + +## [0.2.0] - 2022-02-14 + +### Changed +- updates for the current SDK and backend + + +## [0.1.0] - 2022-01-09 + +### Added +- basic tests for kyouko, misaka and sagiri diff --git a/src/components/TsugumiTester/README.md b/src/components/TsugumiTester/README.md new file mode 100644 index 00000000..6a62d36c --- /dev/null +++ b/src/components/TsugumiTester/README.md @@ -0,0 +1,15 @@ +# TsugumiTester + +## Description + +Test-Tool of the Hanami-AI-Project: https://github.com/kitsudaiki/Hanami-AI + +## Author + +Tobias Anker + +eMail: tobias.anker@kitsunemimi.moe + +## License + +This project is licensed under the Apache License Version 2.0 - see the [LICENSE](LICENSE) file for details diff --git a/src/components/TsugumiTester/TsugumiTester.pro b/src/components/TsugumiTester/TsugumiTester.pro new file mode 100644 index 00000000..3ebc6242 --- /dev/null +++ b/src/components/TsugumiTester/TsugumiTester.pro @@ -0,0 +1,136 @@ +QT -= qt core gui + +TARGET = TsugumiTester +CONFIG += console c++17 +CONFIG -= app_bundle + +LIBS += -L../../sdk/cpp/libHanamiAiSdk/src -lHanamiAiSdk +LIBS += -L../../sdk/cpp/libHanamiAiSdk/src/debug -lHanamiAiSdk +LIBS += -L../../sdk/cpp/libHanamiAiSdk/src/release -lHanamiAiSdk +INCLUDEPATH += ../../sdk/cpp/libHanamiAiSdk/include + +LIBS += -L../../libraries/libKitsunemimiHanamiCommon/src -lKitsunemimiHanamiCommon +LIBS += -L../../libraries/libKitsunemimiHanamiCommon/src/debug -lKitsunemimiHanamiCommon +LIBS += -L../../libraries/libKitsunemimiHanamiCommon/src/release -lKitsunemimiHanamiCommon +INCLUDEPATH += ../../libraries/libKitsunemimiHanamiCommon/include + +LIBS += -L../../libraries/libKitsunemimiConfig/src -lKitsunemimiConfig +LIBS += -L../../libraries/libKitsunemimiConfig/src/debug -lKitsunemimiConfig +LIBS += -L../../libraries/libKitsunemimiConfig/src/release -lKitsunemimiConfig +INCLUDEPATH += ../../libraries/libKitsunemimiConfig/include + +LIBS += -L../../libraries/libKitsunemimiJson/src -lKitsunemimiJson +LIBS += -L../../libraries/libKitsunemimiJson/src/debug -lKitsunemimiJson +LIBS += -L../../libraries/libKitsunemimiJson/src/release -lKitsunemimiJson +INCLUDEPATH += ../../libraries/libKitsunemimiJson/include + +LIBS += -L../../libraries/libKitsunemimiIni/src -lKitsunemimiIni +LIBS += -L../../libraries/libKitsunemimiIni/src/debug -lKitsunemimiIni +LIBS += -L../../libraries/libKitsunemimiIni/src/release -lKitsunemimiIni +INCLUDEPATH += ../../libraries/libKitsunemimiIni/include + +LIBS += -L../../libraries/libKitsunemimiArgs/src -lKitsunemimiArgs +LIBS += -L../../libraries/libKitsunemimiArgs/src/debug -lKitsunemimiArgs +LIBS += -L../../libraries/libKitsunemimiArgs/src/release -lKitsunemimiArgs +INCLUDEPATH += ../../libraries/libKitsunemimiArgs/include + +LIBS += -L../../libraries/libKitsunemimiCrypto/src -lKitsunemimiCrypto +LIBS += -L../../libraries/libKitsunemimiCrypto/src/debug -lKitsunemimiCrypto +LIBS += -L../../libraries/libKitsunemimiCrypto/src/release -lKitsunemimiCrypto +INCLUDEPATH += ../../libraries/libKitsunemimiCrypto/include + +LIBS += -L../../libraries/libKitsunemimiCommon/src -lKitsunemimiCommon +LIBS += -L../../libraries/libKitsunemimiCommon/src/debug -lKitsunemimiCommon +LIBS += -L../../libraries/libKitsunemimiCommon/src/release -lKitsunemimiCommon +INCLUDEPATH += ../../libraries/libKitsunemimiCommon/include + +LIBS += -lcryptopp -lssl -luuid -lcrypto -pthread -lprotobuf + +INCLUDEPATH += $$PWD \ + src + +SOURCES += src/main.cpp \ + src/common/test_step.cpp \ + src/common/test_thread.cpp \ + src/rest_api_tests/kyouko/cluster/cluster_create_test.cpp \ + src/rest_api_tests/kyouko/cluster/cluster_delete_test.cpp \ + src/rest_api_tests/kyouko/cluster/cluster_get_test.cpp \ + src/rest_api_tests/kyouko/cluster/cluster_list_test.cpp \ + src/rest_api_tests/kyouko/cluster/cluster_load_test.cpp \ + src/rest_api_tests/kyouko/cluster/cluster_save_test.cpp \ + src/rest_api_tests/kyouko/cluster/cluster_switch_to_direct_test.cpp \ + src/rest_api_tests/kyouko/cluster/cluster_switch_to_task_test.cpp \ + src/rest_api_tests/kyouko/io/direct_io_test.cpp \ + src/rest_api_tests/kyouko/task/image_learn_task_test.cpp \ + src/rest_api_tests/kyouko/task/image_request_task_test.cpp \ + src/rest_api_tests/kyouko/task/table_learn_task_test.cpp \ + src/rest_api_tests/kyouko/task/table_request_task_test.cpp \ + src/rest_api_tests/kyouko/template/template_delete_test.cpp \ + src/rest_api_tests/kyouko/template/template_get_test.cpp \ + src/rest_api_tests/kyouko/template/template_list_test.cpp \ + src/rest_api_tests/kyouko/template/template_upload_test.cpp \ + src/rest_api_tests/misaki/project/project_create_test.cpp \ + src/rest_api_tests/misaki/project/project_delete_test.cpp \ + src/rest_api_tests/misaki/project/project_get_test.cpp \ + src/rest_api_tests/misaki/project/project_list_test.cpp \ + src/rest_api_tests/misaki/user/user_create_test.cpp \ + src/rest_api_tests/misaki/user/user_delete_test.cpp \ + src/rest_api_tests/misaki/user/user_get_test.cpp \ + src/rest_api_tests/misaki/user/user_list_test.cpp \ + src/rest_api_tests/rest_api_tests.cpp \ + src/rest_api_tests/shiori/datasets/dataset_check_test.cpp \ + src/rest_api_tests/shiori/datasets/dataset_create_csv_test.cpp \ + src/rest_api_tests/shiori/datasets/dataset_create_mnist_test.cpp \ + src/rest_api_tests/shiori/datasets/dataset_delete_test.cpp \ + src/rest_api_tests/shiori/datasets/dataset_get_test.cpp \ + src/rest_api_tests/shiori/datasets/dataset_list_test.cpp \ + src/rest_api_tests/shiori/request_results/request_result_delete_test.cpp \ + src/rest_api_tests/shiori/request_results/request_result_get_test.cpp \ + src/rest_api_tests/shiori/request_results/request_result_list_test.cpp \ + src/rest_api_tests/shiori/snapshots/snapshot_delete_test.cpp \ + src/rest_api_tests/shiori/snapshots/snapshot_get_test.cpp \ + src/rest_api_tests/shiori/snapshots/snapshot_list_test.cpp + +HEADERS += \ + src/args.h \ + src/common/test_step.h \ + src/common/test_thread.h \ + src/config.h \ + src/rest_api_tests/kyouko/cluster/cluster_create_test.h \ + src/rest_api_tests/kyouko/cluster/cluster_delete_test.h \ + src/rest_api_tests/kyouko/cluster/cluster_get_test.h \ + src/rest_api_tests/kyouko/cluster/cluster_list_test.h \ + src/rest_api_tests/kyouko/cluster/cluster_load_test.h \ + src/rest_api_tests/kyouko/cluster/cluster_save_test.h \ + src/rest_api_tests/kyouko/cluster/cluster_switch_to_direct_test.h \ + src/rest_api_tests/kyouko/cluster/cluster_switch_to_task_test.h \ + src/rest_api_tests/kyouko/io/direct_io_test.h \ + src/rest_api_tests/kyouko/task/image_learn_task_test.h \ + src/rest_api_tests/kyouko/task/image_request_task_test.h \ + src/rest_api_tests/kyouko/task/table_learn_task_test.h \ + src/rest_api_tests/kyouko/task/table_request_task_test.h \ + src/rest_api_tests/kyouko/template/template_delete_test.h \ + src/rest_api_tests/kyouko/template/template_get_test.h \ + src/rest_api_tests/kyouko/template/template_list_test.h \ + src/rest_api_tests/kyouko/template/template_upload_test.h \ + src/rest_api_tests/misaki/project/project_create_test.h \ + src/rest_api_tests/misaki/project/project_delete_test.h \ + src/rest_api_tests/misaki/project/project_get_test.h \ + src/rest_api_tests/misaki/project/project_list_test.h \ + src/rest_api_tests/misaki/user/user_create_test.h \ + src/rest_api_tests/misaki/user/user_delete_test.h \ + src/rest_api_tests/misaki/user/user_get_test.h \ + src/rest_api_tests/misaki/user/user_list_test.h \ + src/rest_api_tests/rest_api_tests.h \ + src/rest_api_tests/shiori/datasets/dataset_check_test.h \ + src/rest_api_tests/shiori/datasets/dataset_create_csv_test.h \ + src/rest_api_tests/shiori/datasets/dataset_create_mnist_test.h \ + src/rest_api_tests/shiori/datasets/dataset_delete_test.h \ + src/rest_api_tests/shiori/datasets/dataset_get_test.h \ + src/rest_api_tests/shiori/datasets/dataset_list_test.h \ + src/rest_api_tests/shiori/request_results/request_result_delete_test.h \ + src/rest_api_tests/shiori/request_results/request_result_get_test.h \ + src/rest_api_tests/shiori/request_results/request_result_list_test.h \ + src/rest_api_tests/shiori/snapshots/snapshot_delete_test.h \ + src/rest_api_tests/shiori/snapshots/snapshot_get_test.h \ + src/rest_api_tests/shiori/snapshots/snapshot_list_test.h diff --git a/src/components/TsugumiTester/build.sh b/src/components/TsugumiTester/build.sh new file mode 100755 index 00000000..a53c7c5f --- /dev/null +++ b/src/components/TsugumiTester/build.sh @@ -0,0 +1,136 @@ +#!/bin/bash + +# get current directory-path and the path of the parent-directory +DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )" +PARENT_DIR="$(dirname "$DIR")" +CURRENT_BRANCH=$(git rev-parse --abbrev-ref HEAD) + +# create build-directory +BUILD_DIR="$PARENT_DIR/build" +mkdir -p $BUILD_DIR + +# create directory for the final result +RESULT_DIR="$PARENT_DIR/result" +mkdir -p $RESULT_DIR + +#----------------------------------------------------------------------------------------------------------------- + +function build_kitsune_lib_repo () { + REPO_NAME=$1 + NUMBER_OF_THREADS=$2 + ADDITIONAL_CONFIGS=$3 + + # create build directory for repo and go into this directory + REPO_DIR="$BUILD_DIR/$REPO_NAME" + mkdir -p $REPO_DIR + cd $REPO_DIR + + # build repo library with qmake + if [[ $REPO_NAME == "libHanamiAiSdk" ]] + then + /usr/lib/x86_64-linux-gnu/qt5/bin/qmake "$PARENT_DIR/$REPO_NAME/cpp/$REPO_NAME.pro" -spec linux-g++ "CONFIG += optimize_full staticlib $ADDITIONAL_CONFIGS" + else + /usr/lib/x86_64-linux-gnu/qt5/bin/qmake "$PARENT_DIR/$REPO_NAME/$REPO_NAME.pro" -spec linux-g++ "CONFIG += optimize_full staticlib $ADDITIONAL_CONFIGS" + fi + /usr/bin/make -j$NUMBER_OF_THREADS + + # copy build-result and include-files into the result-directory + echo "----------------------------------------------------------------------" + echo $RESULT_DIR + cp $REPO_DIR/src/$REPO_NAME.a $RESULT_DIR/ + cp -r $PARENT_DIR/$REPO_NAME/include $RESULT_DIR/ + ls -l $RESULT_DIR/include/ + ls -l $RESULT_DIR +} + +function download_repo_github () { + REPO_NAME=$1 + TAG_OR_BRANCH=$2 + + echo "" + echo "" + echo "++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++" + echo "$REPO_NAME" + echo "++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++" + echo "Branch/Tag: $TAG_OR_BRANCH" + + # clone repo + git clone https://github.com/kitsudaiki/$REPO_NAME.git "$PARENT_DIR/$REPO_NAME" + cd "$PARENT_DIR/$REPO_NAME" + + # checkout branch + if [[ $CURRENT_BRANCH =~ ^tag.* ]] || [[ $CURRENT_BRANCH =~ ^hotfix.* ]] || [[ $CURRENT_BRANCH =~ ^v.* ]] || [[ $CURRENT_BRANCH =~ ^rolling$ ]] || [[ $CURRENT_BRANCH =~ ^staging$ ]]; then + # if a stable branch, then use the defined tag of branch + # check if defined branch even exist + BRANCH_EXIST=$(git ls-remote --heads origin $TAG_OR_BRANCH) + if [[ -z "$BRANCH_EXIST" ]]; then + echo "" + echo "-------------------------------------------------------------------------------------" + echo "Branch or tag '$TAG_OR_BRANCH' does not exist for the repository '$REPO_NAME'" + echo "-------------------------------------------------------------------------------------" + echo "" + exit 1 + fi + git checkout $TAG_OR_BRANCH + else + # if develop or feature branch, then try to checkout the feature-branch in the other repo as well + # or otherwise use the develop-branch as default + BRANCH_EXIST=$(git ls-remote --heads origin $CURRENT_BRANCH) + if [[ -n "$BRANCH_EXIST" ]]; then + git checkout $CURRENT_BRANCH + else + git checkout develop + fi + fi +} + +function get_required_kitsune_lib_repo () { + REPO_NAME=$1 + TAG_OR_BRANCH=$2 + NUMBER_OF_THREADS=$3 + + download_repo_github $REPO_NAME $TAG_OR_BRANCH + build_kitsune_lib_repo $REPO_NAME $NUMBER_OF_THREADS +} + +#----------------------------------------------------------------------------------------------------------------- + +echo "" +echo "###########################################################################################################" +echo "" +get_required_kitsune_lib_repo "libKitsunemimiCommon" "develop" 8 +get_required_kitsune_lib_repo "libKitsunemimiJson" "develop" 1 +get_required_kitsune_lib_repo "libKitsunemimiIni" "develop" 1 +get_required_kitsune_lib_repo "libKitsunemimiArgs" "develop" 8 +get_required_kitsune_lib_repo "libKitsunemimiCrypto" "develop" 8 +get_required_kitsune_lib_repo "libKitsunemimiConfig" "develop" 8 +echo "" +echo "###########################################################################################################" +echo "" +get_required_private_repo_github "libKitsunemimiCrypto" "develop" 8 +echo "" +echo "###########################################################################################################" +echo "" +get_required_kitsune_lib_repo "libKitsunemimiHanamiCommon" "develop" 8 +download_repo_github "libKitsunemimiHanamiMessages" "develop" +get_required_kitsune_lib_repo "libHanamiAiSdk" "develop" 8 +echo "" +echo "###########################################################################################################" +echo "" + +#----------------------------------------------------------------------------------------------------------------- + +# create build directory for KyoukoMind and go into this directory +LIB_KITSUNE_SAKURA_TREE_DIR="$BUILD_DIR/TsugumiTester" +mkdir -p $LIB_KITSUNE_SAKURA_TREE_DIR +cd $LIB_KITSUNE_SAKURA_TREE_DIR + +# build TsugumiTester with qmake +/usr/lib/x86_64-linux-gnu/qt5/bin/qmake "$PARENT_DIR/TsugumiTester/TsugumiTester.pro" -spec linux-g++ "CONFIG += optimize_full" +/usr/bin/make -j8 + +# copy build-result and include-files into the result-directory +cp "$LIB_KITSUNE_SAKURA_TREE_DIR/TsugumiTester" "$RESULT_DIR/" + +#----------------------------------------------------------------------------------------------------------------- + diff --git a/src/components/TsugumiTester/src/args.h b/src/components/TsugumiTester/src/args.h new file mode 100644 index 00000000..695f07cb --- /dev/null +++ b/src/components/TsugumiTester/src/args.h @@ -0,0 +1,48 @@ +/** + * @file args.h + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2022 Tobias Anker + * + * 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. + */ + +#ifndef TSUGUMITESTER_ARGS_H +#define TSUGUMITESTER_ARGS_H + +#include +#include +#include + +/** + * @brief register cli-arguments + * + * @param argparser reference to argument parser + * + * @return true if successful, else false + */ +bool +registerArguments(Kitsunemimi::ArgParser* argparser, + Kitsunemimi::ErrorContainer &error) +{ + if(Kitsunemimi::Hanami::registerArguments(*argparser, error) == false) { + return false; + } + + return true; +} + +#endif // TSUGUMITESTER_ARGS_H diff --git a/src/components/TsugumiTester/src/common/test_step.cpp b/src/components/TsugumiTester/src/common/test_step.cpp new file mode 100644 index 00000000..4cfd4fb1 --- /dev/null +++ b/src/components/TsugumiTester/src/common/test_step.cpp @@ -0,0 +1,47 @@ +/** + * @file test_step.cpp + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2022 Tobias Anker + * + * 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 "test_step.h" + +/** + * @brief constructor + * + * @param expectSuccess sould success or fail + */ +TestStep::TestStep(const bool expectSuccess) + : m_expectSuccess(expectSuccess) {} + +/** + * @brief destructor + */ +TestStep::~TestStep() {} + +/** + * @brief get test-name + * + * @return name of the test + */ +const std::string +TestStep::getTestName() const +{ + return m_testName; +} diff --git a/src/components/TsugumiTester/src/common/test_step.h b/src/components/TsugumiTester/src/common/test_step.h new file mode 100644 index 00000000..8cec1c5d --- /dev/null +++ b/src/components/TsugumiTester/src/common/test_step.h @@ -0,0 +1,49 @@ +/** + * @file test_step.h + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2022 Tobias Anker + * + * 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. + */ + +#ifndef TSUGUMI_TESTSTEP_H +#define TSUGUMI_TESTSTEP_H + +#include +#include + +#include +#include +#include + +class TestStep +{ +public: + TestStep(const bool expectSuccess); + virtual ~TestStep(); + + const std::string getTestName() const; + + virtual bool runTest(Kitsunemimi::JsonItem &inputData, + Kitsunemimi::ErrorContainer &error) = 0; + +protected: + const bool m_expectSuccess; + std::string m_testName; +}; + +#endif // TSUGUMI_TESTSTEP_H diff --git a/src/components/TsugumiTester/src/common/test_thread.cpp b/src/components/TsugumiTester/src/common/test_thread.cpp new file mode 100644 index 00000000..ef6a9850 --- /dev/null +++ b/src/components/TsugumiTester/src/common/test_thread.cpp @@ -0,0 +1,125 @@ +/** + * @file test_thread.cpp + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2022 Tobias Anker + * + * 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 "test_thread.h" + +#include + +HanamiAI::WebsocketClient* TestThread::m_wsClient = nullptr; + +/** + * @brief constructor + */ +TestThread::TestThread(const std::string &name, + Kitsunemimi::JsonItem &inputData) + : Kitsunemimi::Thread(name) +{ + m_inputData = inputData; +} + +/** + * @brief destructor + */ +TestThread::~TestThread() +{ + if(m_wsClient != nullptr) { + delete m_wsClient; + } +} + +/** + * @brief add new test to queue + * + * @param newStep new test-step to process + */ +void +TestThread::addTest(TestStep* newStep) +{ + std::lock_guard guard(m_queueLock); + + m_taskQueue.push_back(newStep); +} + +/** + * @brief execute tests + */ +void +TestThread::run() +{ + TestStep* currentStep = getTest(); + while(currentStep != nullptr) + { + std::cout<<"==================================================================="<getTestName()<<"'"<runTest(m_inputData, error) == false) + { + error.addMeesage("Test '" + + currentStep->getTestName() + + "' in Thread '" + + getThreadName() + + "' has failed"); + LOG_ERROR(error); + delete currentStep; + std::cout< guard(m_queueLock); + + if(m_taskQueue.size() > 0) + { + TestStep* result = m_taskQueue.front(); + m_taskQueue.pop_front(); + return result; + } + + return nullptr; +} diff --git a/src/components/TsugumiTester/src/common/test_thread.h b/src/components/TsugumiTester/src/common/test_thread.h new file mode 100644 index 00000000..77d8ba12 --- /dev/null +++ b/src/components/TsugumiTester/src/common/test_thread.h @@ -0,0 +1,60 @@ +/** + * @file test_thread.h + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2022 Tobias Anker + * + * 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. + */ + +#ifndef TSUGUMI_TESTTHREAD_H +#define TSUGUMI_TESTTHREAD_H + +#include +#include + +#include + +#include +#include + +class TestStep; + +class TestThread + : public Kitsunemimi::Thread +{ +public: + TestThread(const std::string &name, + Kitsunemimi::JsonItem &inputData); + ~TestThread(); + + void addTest(TestStep* newStep); + + bool isFinished = false; + static HanamiAI::WebsocketClient* m_wsClient; + +protected: + void run(); + +private: + std::deque m_taskQueue; + std::mutex m_queueLock; + Kitsunemimi::JsonItem m_inputData; + + TestStep* getTest(); +}; + +#endif // TSUGUMI_TESTTHREAD_H diff --git a/src/components/TsugumiTester/src/config.h b/src/components/TsugumiTester/src/config.h new file mode 100644 index 00000000..0068f255 --- /dev/null +++ b/src/components/TsugumiTester/src/config.h @@ -0,0 +1,51 @@ +/** + * @file config.h + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2022 Tobias Anker + * + * 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. + */ + +#ifndef TSUGUMITESTER_CONFIG_H +#define TSUGUMITESTER_CONFIG_H + +#include +#include +#include + +/** + * @brief register configs + */ +void +registerConfigs(Kitsunemimi::ErrorContainer &error) +{ + Kitsunemimi::Hanami::registerBasicConfigs(error); + + REGISTER_STRING_CONFIG( "connection", "host", error, "", true); + REGISTER_INT_CONFIG( "connection", "port", error, 0, true); + REGISTER_STRING_CONFIG( "connection", "test_user", error, "", true); + REGISTER_STRING_CONFIG( "connection", "test_pw", error, "", true); + + REGISTER_STRING_CONFIG( "test_data", "type", error, "", true); + REGISTER_STRING_CONFIG( "test_data", "learn_inputs", error, "", true); + REGISTER_STRING_CONFIG( "test_data", "learn_labels", error, "", true); + REGISTER_STRING_CONFIG( "test_data", "request_inputs", error, "", true); + REGISTER_STRING_CONFIG( "test_data", "request_labels", error, "", true); + REGISTER_STRING_CONFIG( "test_data", "base_inputs", error, "", true); +} + +#endif // TSUGUMITESTER_CONFIG_H diff --git a/src/components/TsugumiTester/src/main.cpp b/src/components/TsugumiTester/src/main.cpp new file mode 100644 index 00000000..96875990 --- /dev/null +++ b/src/components/TsugumiTester/src/main.cpp @@ -0,0 +1,49 @@ +/** + * @file main.cpp + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2022 Tobias Anker + * + * 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 + +#include +#include +#include + +#include + +#include +#include + +using Kitsunemimi::Hanami::initMain; + +int main(int argc, char *argv[]) +{ + Kitsunemimi::ErrorContainer error; + Kitsunemimi::initConsoleLogger(false); + if(initMain(argc, argv, "tsugumi", ®isterArguments, ®isterConfigs, error) == false) + { + LOG_ERROR(error); + return 1; + } + + runRestApiTests(); + + return 0; +} diff --git a/src/components/TsugumiTester/src/rest_api_tests/kyouko/cluster/cluster_create_test.cpp b/src/components/TsugumiTester/src/rest_api_tests/kyouko/cluster/cluster_create_test.cpp new file mode 100644 index 00000000..3835b49a --- /dev/null +++ b/src/components/TsugumiTester/src/rest_api_tests/kyouko/cluster/cluster_create_test.cpp @@ -0,0 +1,65 @@ +/** + * @file cluster_create_test.cpp + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2022 Tobias Anker + * + * 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 "cluster_create_test.h" + +#include + +ClusterCreateTest::ClusterCreateTest(const bool expectSuccess) + : TestStep(expectSuccess) +{ + m_testName = "create cluster"; + if(expectSuccess) { + m_testName += " (success)"; + } else { + m_testName += " (fail)"; + } +} + +bool +ClusterCreateTest::runTest(Kitsunemimi::JsonItem &inputData, + Kitsunemimi::ErrorContainer &error) +{ + // create new cluster + std::string result; + if(HanamiAI::createCluster(result, + inputData.get("cluster_name").getString(), + inputData.get("cluster_definition").getString(), + error) != m_expectSuccess) + { + return false; + } + + if(m_expectSuccess == false) { + return true; + } + + // parse output + Kitsunemimi::JsonItem jsonItem; + if(jsonItem.parse(result, error) == false) { + return false; + } + + inputData.insert("cluster_uuid", jsonItem.get("uuid").getString(), true); + + return true; +} diff --git a/src/components/TsugumiTester/src/rest_api_tests/kyouko/cluster/cluster_create_test.h b/src/components/TsugumiTester/src/rest_api_tests/kyouko/cluster/cluster_create_test.h new file mode 100644 index 00000000..e36464dc --- /dev/null +++ b/src/components/TsugumiTester/src/rest_api_tests/kyouko/cluster/cluster_create_test.h @@ -0,0 +1,38 @@ +/** + * @file cluster_create_test.h + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2022 Tobias Anker + * + * 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. + */ + +#ifndef TSUGUMITESTER_CLUSTERCREATETEST_H +#define TSUGUMITESTER_CLUSTERCREATETEST_H + +#include + +class ClusterCreateTest + : public TestStep +{ +public: + ClusterCreateTest(const bool expectSuccess); + + bool runTest(Kitsunemimi::JsonItem &inputData, + Kitsunemimi::ErrorContainer &error); +}; + +#endif // TSUGUMITESTER_CLUSTERCREATETEST_H diff --git a/src/components/TsugumiTester/src/rest_api_tests/kyouko/cluster/cluster_delete_test.cpp b/src/components/TsugumiTester/src/rest_api_tests/kyouko/cluster/cluster_delete_test.cpp new file mode 100644 index 00000000..d4169fd8 --- /dev/null +++ b/src/components/TsugumiTester/src/rest_api_tests/kyouko/cluster/cluster_delete_test.cpp @@ -0,0 +1,62 @@ +/** + * @file cluster_delete_test.cpp + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2022 Tobias Anker + * + * 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 "cluster_delete_test.h" + +#include + +ClusterDeleteTest::ClusterDeleteTest(const bool expectSuccess) + : TestStep(expectSuccess) +{ + m_testName = "delete cluster"; + if(expectSuccess) { + m_testName += " (success)"; + } else { + m_testName += " (fail)"; + } +} + +bool +ClusterDeleteTest::runTest(Kitsunemimi::JsonItem &inputData, + Kitsunemimi::ErrorContainer &error) +{ + // delete cluster + std::string result; + if(HanamiAI::deleteCluster(result, + inputData.get("cluster_uuid").getString(), + error) != m_expectSuccess) + { + return false; + } + + if(m_expectSuccess == false) { + return true; + } + + // parse output + Kitsunemimi::JsonItem jsonItem; + if(jsonItem.parse(result, error) == false) { + return false; + } + + return true; +} diff --git a/src/components/TsugumiTester/src/rest_api_tests/kyouko/cluster/cluster_delete_test.h b/src/components/TsugumiTester/src/rest_api_tests/kyouko/cluster/cluster_delete_test.h new file mode 100644 index 00000000..104ad310 --- /dev/null +++ b/src/components/TsugumiTester/src/rest_api_tests/kyouko/cluster/cluster_delete_test.h @@ -0,0 +1,38 @@ +/** + * @file cluster_delete_test.h + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2022 Tobias Anker + * + * 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. + */ + +#ifndef TSUGUMITESTER_CLUSTERDELETETEST_H +#define TSUGUMITESTER_CLUSTERDELETETEST_H + +#include + +class ClusterDeleteTest + : public TestStep +{ +public: + ClusterDeleteTest(const bool expectSuccess); + + bool runTest(Kitsunemimi::JsonItem &inputData, + Kitsunemimi::ErrorContainer &error); +}; + +#endif // TSUGUMITESTER_CLUSTERDELETETEST_H diff --git a/src/components/TsugumiTester/src/rest_api_tests/kyouko/cluster/cluster_get_test.cpp b/src/components/TsugumiTester/src/rest_api_tests/kyouko/cluster/cluster_get_test.cpp new file mode 100644 index 00000000..1e3923b1 --- /dev/null +++ b/src/components/TsugumiTester/src/rest_api_tests/kyouko/cluster/cluster_get_test.cpp @@ -0,0 +1,65 @@ +/** + * @file cluster_get_test.cpp + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2022 Tobias Anker + * + * 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 "cluster_get_test.h" + +#include + +ClusterGetTest::ClusterGetTest(const bool expectSuccess, + const std::string &nameOverride) + : TestStep(expectSuccess) +{ + m_testName = "get cluster"; + if(expectSuccess) { + m_testName += " (success)"; + } else { + m_testName += " (fail)"; + } + m_uuid = nameOverride; +} + +bool +ClusterGetTest::runTest(Kitsunemimi::JsonItem &inputData, + Kitsunemimi::ErrorContainer &error) +{ + if(m_uuid == "") { + m_uuid = inputData.get("cluster_uuid").getString(); + } + + // get cluster-infos + std::string result; + if(HanamiAI::getCluster(result, m_uuid, error) != m_expectSuccess) { + return false; + } + + if(m_expectSuccess == false) { + return true; + } + + // parse output + Kitsunemimi::JsonItem jsonItem; + if(jsonItem.parse(result, error) == false) { + return false; + } + + return true; +} diff --git a/src/components/TsugumiTester/src/rest_api_tests/kyouko/cluster/cluster_get_test.h b/src/components/TsugumiTester/src/rest_api_tests/kyouko/cluster/cluster_get_test.h new file mode 100644 index 00000000..efb1c6f5 --- /dev/null +++ b/src/components/TsugumiTester/src/rest_api_tests/kyouko/cluster/cluster_get_test.h @@ -0,0 +1,42 @@ +/** + * @file cluster_get_test.h + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2022 Tobias Anker + * + * 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. + */ + +#ifndef TSUGUMITESTER_CLUSTERGETTEST_H +#define TSUGUMITESTER_CLUSTERGETTEST_H + +#include + +class ClusterGetTest + : public TestStep +{ +public: + ClusterGetTest(const bool expectSuccess, + const std::string &nameOverride = ""); + + bool runTest(Kitsunemimi::JsonItem &inputData, + Kitsunemimi::ErrorContainer &error); + +private: + std::string m_uuid = ""; +}; + +#endif // TSUGUMITESTER_CLUSTERGETTEST_H diff --git a/src/components/TsugumiTester/src/rest_api_tests/kyouko/cluster/cluster_list_test.cpp b/src/components/TsugumiTester/src/rest_api_tests/kyouko/cluster/cluster_list_test.cpp new file mode 100644 index 00000000..60fc9b82 --- /dev/null +++ b/src/components/TsugumiTester/src/rest_api_tests/kyouko/cluster/cluster_list_test.cpp @@ -0,0 +1,61 @@ +/** + * @file cluster_list_test.cpp + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2022 Tobias Anker + * + * 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 "cluster_list_test.h" + +#include + +ClusterListTest::ClusterListTest(const bool expectSuccess) + : TestStep(expectSuccess) +{ + m_testName = "list cluster"; + if(expectSuccess) { + m_testName += " (success)"; + } else { + m_testName += " (fail)"; + } +} + +bool +ClusterListTest::runTest(Kitsunemimi::JsonItem &inputData, + Kitsunemimi::ErrorContainer &error) +{ + // list clusters + std::string result; + if(HanamiAI::listCluster(result, error) != m_expectSuccess) { + return false; + } + + if(m_expectSuccess == false) { + return true; + } + + // parse output + Kitsunemimi::JsonItem jsonItem; + if(jsonItem.parse(result, error) == false) { + return false; + } + + inputData.insert("number_of_clusters", static_cast(jsonItem.size()), true); + + return true; +} diff --git a/src/components/TsugumiTester/src/rest_api_tests/kyouko/cluster/cluster_list_test.h b/src/components/TsugumiTester/src/rest_api_tests/kyouko/cluster/cluster_list_test.h new file mode 100644 index 00000000..0d3d5d4e --- /dev/null +++ b/src/components/TsugumiTester/src/rest_api_tests/kyouko/cluster/cluster_list_test.h @@ -0,0 +1,38 @@ +/** + * @file cluster_list_test.h + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2022 Tobias Anker + * + * 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. + */ + +#ifndef TSUGUMITESTER_CLUSTERLISTTEST_H +#define TSUGUMITESTER_CLUSTERLISTTEST_H + +#include + +class ClusterListTest + : public TestStep +{ +public: + ClusterListTest(const bool expectSuccess); + + bool runTest(Kitsunemimi::JsonItem &inputData, + Kitsunemimi::ErrorContainer &error); +}; + +#endif // TSUGUMITESTER_CLUSTERLISTTEST_H diff --git a/src/components/TsugumiTester/src/rest_api_tests/kyouko/cluster/cluster_load_test.cpp b/src/components/TsugumiTester/src/rest_api_tests/kyouko/cluster/cluster_load_test.cpp new file mode 100644 index 00000000..b122d6c0 --- /dev/null +++ b/src/components/TsugumiTester/src/rest_api_tests/kyouko/cluster/cluster_load_test.cpp @@ -0,0 +1,83 @@ +/** + * @file cluster_save_test.cpp + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2022 Tobias Anker + * + * 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 "cluster_load_test.h" + +#include +#include + +ClusterLoadTest::ClusterLoadTest(const bool expectSuccess) + : TestStep(expectSuccess) +{ + m_testName = "save cluster"; + if(expectSuccess) { + m_testName += " (success)"; + } else { + m_testName += " (fail)"; + } +} + +bool +ClusterLoadTest::runTest(Kitsunemimi::JsonItem &inputData, + Kitsunemimi::ErrorContainer &error) +{ + // create new cluster + std::string result; + if(HanamiAI::restoreCluster(result, + inputData.get("cluster_uuid").getString(), + inputData.get("cluster_snapshot_uuid").getString(), + error) != m_expectSuccess) + { + return false; + } + + if(m_expectSuccess == false) { + return true; + } + + // parse output + Kitsunemimi::JsonItem jsonItem; + if(jsonItem.parse(result, error) == false) { + return false; + } + + // wait until task is finished + do + { + sleep(1); + + HanamiAI::getTask(result, + jsonItem.get("uuid").getString(), + inputData.get("cluster_uuid").getString(), + error); + + // parse output + if(jsonItem.parse(result, error) == false) { + return false; + } + std::cout< + * + * @copyright Apache License Version 2.0 + * + * Copyright 2022 Tobias Anker + * + * 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. + */ + +#ifndef TSUGUMITESTER_CLUSTERLOADTEST_H +#define TSUGUMITESTER_CLUSTERLOADTEST_H + +#include + +class ClusterLoadTest + : public TestStep +{ +public: + ClusterLoadTest(const bool expectSuccess); + + bool runTest(Kitsunemimi::JsonItem &inputData, + Kitsunemimi::ErrorContainer &error); +}; + +#endif // TSUGUMITESTER_CLUSTERLOADTEST_H diff --git a/src/components/TsugumiTester/src/rest_api_tests/kyouko/cluster/cluster_save_test.cpp b/src/components/TsugumiTester/src/rest_api_tests/kyouko/cluster/cluster_save_test.cpp new file mode 100644 index 00000000..277ad9d5 --- /dev/null +++ b/src/components/TsugumiTester/src/rest_api_tests/kyouko/cluster/cluster_save_test.cpp @@ -0,0 +1,84 @@ +/** + * @file cluster_save_test.cpp + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2022 Tobias Anker + * + * 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 "cluster_save_test.h" + +#include +#include + +ClusterSaveTest::ClusterSaveTest(const bool expectSuccess) + : TestStep(expectSuccess) +{ + m_testName = "save cluster"; + if(expectSuccess) { + m_testName += " (success)"; + } else { + m_testName += " (fail)"; + } +} + +bool +ClusterSaveTest::runTest(Kitsunemimi::JsonItem &inputData, + Kitsunemimi::ErrorContainer &error) +{ + // create new cluster + std::string result; + if(HanamiAI::saveCluster(result, + inputData.get("cluster_uuid").getString(), + inputData.get("cluster_snapshot_name").getString(), + error) != m_expectSuccess) + { + return false; + } + + if(m_expectSuccess == false) { + return true; + } + + // parse output + Kitsunemimi::JsonItem jsonItem; + if(jsonItem.parse(result, error) == false) { + return false; + } + + inputData.insert("cluster_snapshot_uuid", jsonItem.get("uuid").getString(), true); + + // wait until task is finished + do + { + sleep(1); + + HanamiAI::getTask(result, + inputData.get("cluster_snapshot_uuid").getString(), + inputData.get("cluster_uuid").getString(), + error); + + // parse output + if(jsonItem.parse(result, error) == false) { + return false; + } + std::cout< + * + * @copyright Apache License Version 2.0 + * + * Copyright 2022 Tobias Anker + * + * 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. + */ + +#ifndef TSUGUMITESTER_CLUSTERSAVETEST_H +#define TSUGUMITESTER_CLUSTERSAVETEST_H + +#include + +class ClusterSaveTest + : public TestStep +{ +public: + ClusterSaveTest(const bool expectSuccess); + + bool runTest(Kitsunemimi::JsonItem &inputData, + Kitsunemimi::ErrorContainer &error); +}; + +#endif // TSUGUMITESTER_CLUSTERSAVETEST_H diff --git a/src/components/TsugumiTester/src/rest_api_tests/kyouko/cluster/cluster_switch_to_direct_test.cpp b/src/components/TsugumiTester/src/rest_api_tests/kyouko/cluster/cluster_switch_to_direct_test.cpp new file mode 100644 index 00000000..76dd69c0 --- /dev/null +++ b/src/components/TsugumiTester/src/rest_api_tests/kyouko/cluster/cluster_switch_to_direct_test.cpp @@ -0,0 +1,60 @@ +/** + * @file cluster_switch_to_direct_test.cpp + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2022 Tobias Anker + * + * 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 "cluster_switch_to_direct_test.h" + +#include + +#include +#include + +ClusterSwitchToDirectTest::ClusterSwitchToDirectTest(const bool expectSuccess) + : TestStep(expectSuccess) +{ + m_testName = "switch cluster to direct mode"; + if(expectSuccess) { + m_testName += " (success)"; + } else { + m_testName += " (fail)"; + } +} + +bool +ClusterSwitchToDirectTest::runTest(Kitsunemimi::JsonItem &inputData, + Kitsunemimi::ErrorContainer &error) +{ + // create new cluster + std::string result; + HanamiAI::WebsocketClient* client = nullptr; + client = HanamiAI::switchToDirectMode(result, + inputData.get("cluster_uuid").getString(), + error); + bool success = client != nullptr; + if(success != m_expectSuccess) + { + return false; + } + + TestThread::m_wsClient = client; + + return true; +} diff --git a/src/components/TsugumiTester/src/rest_api_tests/kyouko/cluster/cluster_switch_to_direct_test.h b/src/components/TsugumiTester/src/rest_api_tests/kyouko/cluster/cluster_switch_to_direct_test.h new file mode 100644 index 00000000..5d99bb44 --- /dev/null +++ b/src/components/TsugumiTester/src/rest_api_tests/kyouko/cluster/cluster_switch_to_direct_test.h @@ -0,0 +1,38 @@ +/** + * @file cluster_switch_to_direct_test.h + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2022 Tobias Anker + * + * 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. + */ + +#ifndef TSUGUMITESTER_CLUSTERSWITCHTODIRECTTEST_H +#define TSUGUMITESTER_CLUSTERSWITCHTODIRECTTEST_H + +#include + +class ClusterSwitchToDirectTest + : public TestStep +{ +public: + ClusterSwitchToDirectTest(const bool expectSuccess); + + bool runTest(Kitsunemimi::JsonItem &inputData, + Kitsunemimi::ErrorContainer &error); +}; + +#endif // TSUGUMITESTER_CLUSTERSWITCHTODIRECTTEST_H diff --git a/src/components/TsugumiTester/src/rest_api_tests/kyouko/cluster/cluster_switch_to_task_test.cpp b/src/components/TsugumiTester/src/rest_api_tests/kyouko/cluster/cluster_switch_to_task_test.cpp new file mode 100644 index 00000000..5cd66c3b --- /dev/null +++ b/src/components/TsugumiTester/src/rest_api_tests/kyouko/cluster/cluster_switch_to_task_test.cpp @@ -0,0 +1,52 @@ +/** + * @file cluster_switch_to_task_test.cpp + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2022 Tobias Anker + * + * 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 "cluster_switch_to_task_test.h" + +#include + +ClusterSwitchToTaskTest::ClusterSwitchToTaskTest(const bool expectSuccess) + : TestStep(expectSuccess) +{ + m_testName = "switch cluster to task mode"; + if(expectSuccess) { + m_testName += " (success)"; + } else { + m_testName += " (fail)"; + } +} + +bool +ClusterSwitchToTaskTest::runTest(Kitsunemimi::JsonItem &inputData, + Kitsunemimi::ErrorContainer &error) +{ + // create new cluster + std::string result; + if(HanamiAI::switchToTaskMode(result, + inputData.get("cluster_uuid").getString(), + error) != m_expectSuccess) + { + return false; + } + + return true; +} diff --git a/src/components/TsugumiTester/src/rest_api_tests/kyouko/cluster/cluster_switch_to_task_test.h b/src/components/TsugumiTester/src/rest_api_tests/kyouko/cluster/cluster_switch_to_task_test.h new file mode 100644 index 00000000..6d119c62 --- /dev/null +++ b/src/components/TsugumiTester/src/rest_api_tests/kyouko/cluster/cluster_switch_to_task_test.h @@ -0,0 +1,38 @@ +/** + * @file cluster_switch_to_task_test.h + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2022 Tobias Anker + * + * 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. + */ + +#ifndef TSUGUMITESTER_CLUSTERSWITCHTOTASKTEST_H +#define TSUGUMITESTER_CLUSTERSWITCHTOTASKTEST_H + +#include + +class ClusterSwitchToTaskTest + : public TestStep +{ +public: + ClusterSwitchToTaskTest(const bool expectSuccess); + + bool runTest(Kitsunemimi::JsonItem &inputData, + Kitsunemimi::ErrorContainer &error); +}; + +#endif // TSUGUMITESTER_CLUSTERSWITCHTOTASKTEST_H diff --git a/src/components/TsugumiTester/src/rest_api_tests/kyouko/io/direct_io_test.cpp b/src/components/TsugumiTester/src/rest_api_tests/kyouko/io/direct_io_test.cpp new file mode 100644 index 00000000..027d1542 --- /dev/null +++ b/src/components/TsugumiTester/src/rest_api_tests/kyouko/io/direct_io_test.cpp @@ -0,0 +1,923 @@ +/** + * @file direct_io_test.cpp + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2022 Tobias Anker + * + * 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 "direct_io_test.h" + +#include +#include + +DirectIoTest::DirectIoTest(const bool expectSuccess) + : TestStep(expectSuccess) +{ + m_testName = "io-test"; + if(expectSuccess) { + m_testName += " (success)"; + } else { + m_testName += " (fail)"; + } +} + +bool +DirectIoTest::runTest(Kitsunemimi::JsonItem &, + Kitsunemimi::ErrorContainer &) +{ + if(learnTest() == false) { + return false; + } + + if(requestTest() == false) { + return false; + } + + return true; +} + +bool +DirectIoTest::learnTest() +{ + Kitsunemimi::ErrorContainer error; + + // create input + float inputValues[784]; + fillInputValues(&inputValues[0]); + + // create should + float shouldValues[10]; + fillShouldValues(shouldValues); + + for(uint64_t i = 0; i < 100; i++) + { + if(i % 50 == 0) { + std::cout<<"run: "< + * + * @copyright Apache License Version 2.0 + * + * Copyright 2022 Tobias Anker + * + * 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. + */ + +#ifndef TSUGUMITESTER_DIRECTIOTEST_H +#define TSUGUMITESTER_DIRECTIOTEST_H + +#include + +class DirectIoTest + : public TestStep +{ +public: + DirectIoTest(const bool expectSuccess); + + bool runTest(Kitsunemimi::JsonItem &, + Kitsunemimi::ErrorContainer &); + +private: + void fillInputValues(float* inputValues); + void fillShouldValues(float* shouldValues); + + bool learnTest(); + bool requestTest(); +}; + +#endif // TSUGUMITESTER_DIRECTIOTEST_H diff --git a/src/components/TsugumiTester/src/rest_api_tests/kyouko/task/image_learn_task_test.cpp b/src/components/TsugumiTester/src/rest_api_tests/kyouko/task/image_learn_task_test.cpp new file mode 100644 index 00000000..4a97fdc0 --- /dev/null +++ b/src/components/TsugumiTester/src/rest_api_tests/kyouko/task/image_learn_task_test.cpp @@ -0,0 +1,105 @@ +/** + * @file learn_task_test.cpp + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2022 Tobias Anker + * + * 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 "image_learn_task_test.h" +#include + +#include + +typedef std::chrono::milliseconds chronoMilliSec; +typedef std::chrono::microseconds chronoMicroSec; +typedef std::chrono::nanoseconds chronoNanoSec; +typedef std::chrono::seconds chronoSec; +typedef std::chrono::high_resolution_clock::time_point chronoTimePoint; +typedef std::chrono::high_resolution_clock chronoClock; + + +ImageLearnTaskTest::ImageLearnTaskTest(const bool expectSuccess) + : TestStep(expectSuccess) +{ + m_testName = "learn-task"; + if(expectSuccess) { + m_testName += " (success)"; + } else { + m_testName += " (fail)"; + } +} + +bool +ImageLearnTaskTest::runTest(Kitsunemimi::JsonItem &inputData, + Kitsunemimi::ErrorContainer &error) +{ + // create new user + std::string result; + if(HanamiAI::createTask(result, + inputData.get("generic_task_name").getString(), + "learn", + inputData.get("cluster_uuid").getString(), + inputData.get("learn_dataset_uuid").getString(), + error) != m_expectSuccess) + { + return false; + } + + if(m_expectSuccess == false) { + return true; + } + + // parse output + Kitsunemimi::JsonItem jsonItem; + if(jsonItem.parse(result, error) == false) { + return false; + } + + inputData.insert("learn_task_uuid", jsonItem.get("uuid").getString(), true); + + std::chrono::high_resolution_clock::time_point start; + std::chrono::high_resolution_clock::time_point end; + + start = std::chrono::system_clock::now(); + + // wait until task is finished + do + { + usleep(1000000); + + HanamiAI::getTask(result, + inputData.get("learn_task_uuid").getString(), + inputData.get("cluster_uuid").getString(), + error); + + // parse output + if(jsonItem.parse(result, error) == false) { + return false; + } + //std::cout<(end - start).count(); + + std::cout<<"#######################################################################"< + * + * @copyright Apache License Version 2.0 + * + * Copyright 2022 Tobias Anker + * + * 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. + */ + +#ifndef TSUGUMITESTER_LEARNTASKTEST_H +#define TSUGUMITESTER_LEARNTASKTEST_H + +#include + +class ImageLearnTaskTest + : public TestStep +{ +public: + ImageLearnTaskTest(const bool expectSuccess); + + bool runTest(Kitsunemimi::JsonItem &inputData, + Kitsunemimi::ErrorContainer &error); +}; + +#endif // TSUGUMITESTER_LEARNTASKTEST_H diff --git a/src/components/TsugumiTester/src/rest_api_tests/kyouko/task/image_request_task_test.cpp b/src/components/TsugumiTester/src/rest_api_tests/kyouko/task/image_request_task_test.cpp new file mode 100644 index 00000000..0265112d --- /dev/null +++ b/src/components/TsugumiTester/src/rest_api_tests/kyouko/task/image_request_task_test.cpp @@ -0,0 +1,113 @@ +/** + * @file request_task_test.cpp + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2022 Tobias Anker + * + * 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 "image_request_task_test.h" +#include + +#include + +typedef std::chrono::milliseconds chronoMilliSec; +typedef std::chrono::microseconds chronoMicroSec; +typedef std::chrono::nanoseconds chronoNanoSec; +typedef std::chrono::seconds chronoSec; +typedef std::chrono::high_resolution_clock::time_point chronoTimePoint; +typedef std::chrono::high_resolution_clock chronoClock; + + +ImageRequestTaskTest::ImageRequestTaskTest(const bool expectSuccess) + : TestStep(expectSuccess) +{ + m_testName = "request-task"; + if(expectSuccess) { + m_testName += " (success)"; + } else { + m_testName += " (fail)"; + } +} + +bool +ImageRequestTaskTest::runTest(Kitsunemimi::JsonItem &inputData, + Kitsunemimi::ErrorContainer &error) +{ + // create new user + std::string result; + if(HanamiAI::createTask(result, + inputData.get("generic_task_name").getString(), + "request", + inputData.get("cluster_uuid").getString(), + inputData.get("request_dataset_uuid").getString(), + error) != m_expectSuccess) + { + return false; + } + + if(m_expectSuccess == false) { + return true; + } + + // parse output + Kitsunemimi::JsonItem jsonItem; + if(jsonItem.parse(result, error) == false) { + return false; + } + + inputData.insert("request_task_uuid", jsonItem.get("uuid").getString(), true); + + std::chrono::high_resolution_clock::time_point start; + std::chrono::high_resolution_clock::time_point end; + + start = std::chrono::system_clock::now(); + + // wait until task is finished + do + { + usleep(1000000); + HanamiAI::getTask(result, + inputData.get("request_task_uuid").getString(), + inputData.get("cluster_uuid").getString(), + error); + + // parse output + if(jsonItem.parse(result, error) == false) { + return false; + } + //std::cout<(end - start).count(); + + std::cout<<"#######################################################################"< + * + * @copyright Apache License Version 2.0 + * + * Copyright 2022 Tobias Anker + * + * 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. + */ + +#ifndef TSUGUMITESTER_REQUESTTASKTEST_H +#define TSUGUMITESTER_REQUESTTASKTEST_H + +#include + +class ImageRequestTaskTest + : public TestStep +{ +public: + ImageRequestTaskTest(const bool expectSuccess); + + bool runTest(Kitsunemimi::JsonItem &inputData, + Kitsunemimi::ErrorContainer &error); +}; + +#endif // TSUGUMITESTER_REQUESTTASKTEST_H diff --git a/src/components/TsugumiTester/src/rest_api_tests/kyouko/task/table_learn_task_test.cpp b/src/components/TsugumiTester/src/rest_api_tests/kyouko/task/table_learn_task_test.cpp new file mode 100644 index 00000000..5ffa54d5 --- /dev/null +++ b/src/components/TsugumiTester/src/rest_api_tests/kyouko/task/table_learn_task_test.cpp @@ -0,0 +1,85 @@ +/** + * @file table_learn_task_test.cpp + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2022 Tobias Anker + * + * 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 "table_learn_task_test.h" + +#include + +TableLearnTaskTest::TableLearnTaskTest(const bool expectSuccess) + : TestStep(expectSuccess) +{ + m_testName = "table-learn-task"; + if(expectSuccess) { + m_testName += " (success)"; + } else { + m_testName += " (fail)"; + } +} + +bool +TableLearnTaskTest::runTest(Kitsunemimi::JsonItem &inputData, + Kitsunemimi::ErrorContainer &error) +{ + // create new user + std::string result; + if(HanamiAI::createTask(result, + inputData.get("generic_task_name").getString(), + "learn", + inputData.get("cluster_uuid").getString(), + inputData.get("base_dataset_uuid").getString(), + error) != m_expectSuccess) + { + return false; + } + + if(m_expectSuccess == false) { + return true; + } + + // parse output + Kitsunemimi::JsonItem jsonItem; + if(jsonItem.parse(result, error) == false) { + return false; + } + + inputData.insert("learn_task_uuid", jsonItem.get("uuid").getString(), true); + + // wait until task is finished + do + { + sleep(1); + + HanamiAI::getTask(result, + inputData.get("learn_task_uuid").getString(), + inputData.get("cluster_uuid").getString(), + error); + + // parse output + if(jsonItem.parse(result, error) == false) { + return false; + } + std::cout< + * + * @copyright Apache License Version 2.0 + * + * Copyright 2022 Tobias Anker + * + * 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. + */ + +#ifndef TSUGUMITESTER_TABLE_LEARNTASKTEST_H +#define TSUGUMITESTER_TABLE_LEARNTASKTEST_H + +#include + +class TableLearnTaskTest + : public TestStep +{ +public: + TableLearnTaskTest(const bool expectSuccess); + + bool runTest(Kitsunemimi::JsonItem &inputData, + Kitsunemimi::ErrorContainer &error); +}; + +#endif // TSUGUMITESTER_TABLE_LEARNTASKTEST_H diff --git a/src/components/TsugumiTester/src/rest_api_tests/kyouko/task/table_request_task_test.cpp b/src/components/TsugumiTester/src/rest_api_tests/kyouko/task/table_request_task_test.cpp new file mode 100644 index 00000000..a5e70da3 --- /dev/null +++ b/src/components/TsugumiTester/src/rest_api_tests/kyouko/task/table_request_task_test.cpp @@ -0,0 +1,93 @@ +/** + * @file table_request_task_test.cpp + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2022 Tobias Anker + * + * 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 "table_request_task_test.h" + +#include + +TableRequestTaskTest::TableRequestTaskTest(const bool expectSuccess) + : TestStep(expectSuccess) +{ + m_testName = "graph-request-task"; + if(expectSuccess) { + m_testName += " (success)"; + } else { + m_testName += " (fail)"; + } +} + +bool +TableRequestTaskTest::runTest(Kitsunemimi::JsonItem &inputData, + Kitsunemimi::ErrorContainer &error) +{ + // create new user + std::string result; + if(HanamiAI::createTask(result, + inputData.get("generic_task_name").getString(), + "request", + inputData.get("cluster_uuid").getString(), + inputData.get("base_dataset_uuid").getString(), + error) != m_expectSuccess) + { + return false; + } + + if(m_expectSuccess == false) { + return true; + } + + // parse output + Kitsunemimi::JsonItem jsonItem; + if(jsonItem.parse(result, error) == false) { + return false; + } + + inputData.insert("request_task_uuid", jsonItem.get("uuid").getString(), true); + + // wait until task is finished + do + { + sleep(1); + HanamiAI::getTask(result, + inputData.get("request_task_uuid").getString(), + inputData.get("cluster_uuid").getString(), + error); + + // parse output + if(jsonItem.parse(result, error) == false) { + return false; + } + std::cout< + * + * @copyright Apache License Version 2.0 + * + * Copyright 2022 Tobias Anker + * + * 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. + */ + +#ifndef TSUGUMITESTER_TABLE_REQUESTTASKTEST_H +#define TSUGUMITESTER_TABLE_REQUESTTASKTEST_H + +#include + +class TableRequestTaskTest + : public TestStep +{ +public: + TableRequestTaskTest(const bool expectSuccess); + + bool runTest(Kitsunemimi::JsonItem &inputData, + Kitsunemimi::ErrorContainer &error); +}; + +#endif // TSUGUMITESTER_TABLE_REQUESTTASKTEST_H diff --git a/src/components/TsugumiTester/src/rest_api_tests/kyouko/template/template_delete_test.cpp b/src/components/TsugumiTester/src/rest_api_tests/kyouko/template/template_delete_test.cpp new file mode 100644 index 00000000..3485f93e --- /dev/null +++ b/src/components/TsugumiTester/src/rest_api_tests/kyouko/template/template_delete_test.cpp @@ -0,0 +1,62 @@ +/** + * @file template_delete_test.cpp + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2022 Tobias Anker + * + * 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 "template_delete_test.h" + +#include + +TemplateDeleteTest::TemplateDeleteTest(const bool expectSuccess) + : TestStep(expectSuccess) +{ + m_testName = "delete template"; + if(expectSuccess) { + m_testName += " (success)"; + } else { + m_testName += " (fail)"; + } +} + +bool +TemplateDeleteTest::runTest(Kitsunemimi::JsonItem &inputData, + Kitsunemimi::ErrorContainer &error) +{ + // delete template by name + std::string result; + if(HanamiAI::deleteTemplate(result, + inputData.get("template_uuid").getString(), + error) != m_expectSuccess) + { + return false; + } + + if(m_expectSuccess == false) { + return true; + } + + // parse output + Kitsunemimi::JsonItem jsonItem; + if(jsonItem.parse(result, error) == false) { + return false; + } + + return true; +} diff --git a/src/components/TsugumiTester/src/rest_api_tests/kyouko/template/template_delete_test.h b/src/components/TsugumiTester/src/rest_api_tests/kyouko/template/template_delete_test.h new file mode 100644 index 00000000..cd9244ec --- /dev/null +++ b/src/components/TsugumiTester/src/rest_api_tests/kyouko/template/template_delete_test.h @@ -0,0 +1,38 @@ +/** + * @file template_delete_test.h + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2022 Tobias Anker + * + * 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. + */ + +#ifndef TSUGUMITESTER_TEMPLATEDELETETEST_H +#define TSUGUMITESTER_TEMPLATEDELETETEST_H + +#include + +class TemplateDeleteTest + : public TestStep +{ +public: + TemplateDeleteTest(const bool expectSuccess); + + bool runTest(Kitsunemimi::JsonItem &inputData, + Kitsunemimi::ErrorContainer &error); +}; + +#endif // TSUGUMITESTER_TEMPLATEDELETETEST_H diff --git a/src/components/TsugumiTester/src/rest_api_tests/kyouko/template/template_get_test.cpp b/src/components/TsugumiTester/src/rest_api_tests/kyouko/template/template_get_test.cpp new file mode 100644 index 00000000..4e2916a5 --- /dev/null +++ b/src/components/TsugumiTester/src/rest_api_tests/kyouko/template/template_get_test.cpp @@ -0,0 +1,67 @@ +/** + * @file template_get_test.cpp + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2022 Tobias Anker + * + * 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 "template_get_test.h" + +#include + +TemplateGetTest::TemplateGetTest(const bool expectSuccess, + const std::string &nameOverride) + : TestStep(expectSuccess) +{ + m_testName = "get template"; + if(expectSuccess) { + m_testName += " (success)"; + } else { + m_testName += " (fail)"; + } + m_name = nameOverride; +} + +bool +TemplateGetTest::runTest(Kitsunemimi::JsonItem &inputData, + Kitsunemimi::ErrorContainer &error) +{ + if(m_name == "") { + m_name = inputData.get("template_uuid").getString(); + } + + // get template by name + std::string result; + if(HanamiAI::getTemplate(result, m_name, error) != m_expectSuccess) { + return false; + } + + if(m_expectSuccess == false) { + return true; + } + + // parse output + Kitsunemimi::JsonItem jsonItem; + if(jsonItem.parse(result, error) == false) { + return false; + } + + inputData.insert("template", jsonItem.get("template").toString(), true); + + return true; +} diff --git a/src/components/TsugumiTester/src/rest_api_tests/kyouko/template/template_get_test.h b/src/components/TsugumiTester/src/rest_api_tests/kyouko/template/template_get_test.h new file mode 100644 index 00000000..86923610 --- /dev/null +++ b/src/components/TsugumiTester/src/rest_api_tests/kyouko/template/template_get_test.h @@ -0,0 +1,42 @@ +/** + * @file template_get_test.h + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2022 Tobias Anker + * + * 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. + */ + +#ifndef TSUGUMITESTER_TEMPLATEGETTEST_H +#define TSUGUMITESTER_TEMPLATEGETTEST_H + +#include + +class TemplateGetTest + : public TestStep +{ +public: + TemplateGetTest(const bool expectSuccess, + const std::string &nameOverride = ""); + + bool runTest(Kitsunemimi::JsonItem &inputData, + Kitsunemimi::ErrorContainer &error); + +private: + std::string m_name = ""; +}; + +#endif // TSUGUMITESTER_TEMPLATEGETTEST_H diff --git a/src/components/TsugumiTester/src/rest_api_tests/kyouko/template/template_list_test.cpp b/src/components/TsugumiTester/src/rest_api_tests/kyouko/template/template_list_test.cpp new file mode 100644 index 00000000..4035b3a2 --- /dev/null +++ b/src/components/TsugumiTester/src/rest_api_tests/kyouko/template/template_list_test.cpp @@ -0,0 +1,61 @@ +/** + * @file template_list_test.cpp + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2022 Tobias Anker + * + * 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 "template_list_test.h" + +#include + +TemplateListTest::TemplateListTest(const bool expectSuccess) + : TestStep(expectSuccess) +{ + m_testName = "list template"; + if(expectSuccess) { + m_testName += " (success)"; + } else { + m_testName += " (fail)"; + } +} + +bool +TemplateListTest::runTest(Kitsunemimi::JsonItem &inputData, + Kitsunemimi::ErrorContainer &error) +{ + // list templates + std::string result; + if(HanamiAI::listTemplate(result, error) != m_expectSuccess) { + return false; + } + + if(m_expectSuccess == false) { + return true; + } + + // parse output + Kitsunemimi::JsonItem jsonItem; + if(jsonItem.parse(result, error) == false) { + return false; + } + + inputData.insert("number_of_templates", static_cast(jsonItem.size()), true); + + return true; +} diff --git a/src/components/TsugumiTester/src/rest_api_tests/kyouko/template/template_list_test.h b/src/components/TsugumiTester/src/rest_api_tests/kyouko/template/template_list_test.h new file mode 100644 index 00000000..e6fa5432 --- /dev/null +++ b/src/components/TsugumiTester/src/rest_api_tests/kyouko/template/template_list_test.h @@ -0,0 +1,38 @@ +/** + * @file template_list_test.h + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2022 Tobias Anker + * + * 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. + */ + +#ifndef TSUGUMITESTER_TEMPLATELISTTEST_H +#define TSUGUMITESTER_TEMPLATELISTTEST_H + +#include + +class TemplateListTest + : public TestStep +{ +public: + TemplateListTest(const bool expectSuccess); + + bool runTest(Kitsunemimi::JsonItem &inputData, + Kitsunemimi::ErrorContainer &error); +}; + +#endif // TSUGUMITESTER_TEMPLATELISTTEST_H diff --git a/src/components/TsugumiTester/src/rest_api_tests/kyouko/template/template_upload_test.cpp b/src/components/TsugumiTester/src/rest_api_tests/kyouko/template/template_upload_test.cpp new file mode 100644 index 00000000..a13a8910 --- /dev/null +++ b/src/components/TsugumiTester/src/rest_api_tests/kyouko/template/template_upload_test.cpp @@ -0,0 +1,65 @@ +/** + * @file template_upload_test.cpp + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2022 Tobias Anker + * + * 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 "template_upload_test.h" + +#include + +TemplateUploadTest::TemplateUploadTest(const bool expectSuccess) + : TestStep(expectSuccess) +{ + m_testName = "upload template"; + if(expectSuccess) { + m_testName += " (success)"; + } else { + m_testName += " (fail)"; + } +} + +bool +TemplateUploadTest::runTest(Kitsunemimi::JsonItem &inputData, + Kitsunemimi::ErrorContainer &error) +{ + // create new template + std::string result = ""; + if(HanamiAI::uploadTemplate(result, + inputData.get("template_name").getString(), + inputData.get("template_segment").getString(), + error) != m_expectSuccess) + { + return false; + } + + if(m_expectSuccess == false) { + return true; + } + + // parse output + Kitsunemimi::JsonItem jsonItem; + if(jsonItem.parse(result, error) == false) { + return false; + } + + inputData.insert("template_uuid", jsonItem.get("uuid").getString(), true); + + return true; +} diff --git a/src/components/TsugumiTester/src/rest_api_tests/kyouko/template/template_upload_test.h b/src/components/TsugumiTester/src/rest_api_tests/kyouko/template/template_upload_test.h new file mode 100644 index 00000000..2d4d2a9e --- /dev/null +++ b/src/components/TsugumiTester/src/rest_api_tests/kyouko/template/template_upload_test.h @@ -0,0 +1,38 @@ +/** + * @file template_upload_test.h + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2022 Tobias Anker + * + * 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. + */ + +#ifndef TSUGUMITESTER_TEMPLATE_UPLOAD_TEST_H +#define TSUGUMITESTER_TEMPLATE_UPLOAD_TEST_H + +#include + +class TemplateUploadTest + : public TestStep +{ +public: + TemplateUploadTest(const bool expectSuccess); + + bool runTest(Kitsunemimi::JsonItem &inputData, + Kitsunemimi::ErrorContainer &error); +}; + +#endif // TSUGUMITESTER_TEMPLATE_UPLOAD_TEST_H diff --git a/src/components/TsugumiTester/src/rest_api_tests/misaki/project/project_create_test.cpp b/src/components/TsugumiTester/src/rest_api_tests/misaki/project/project_create_test.cpp new file mode 100644 index 00000000..5688e3a7 --- /dev/null +++ b/src/components/TsugumiTester/src/rest_api_tests/misaki/project/project_create_test.cpp @@ -0,0 +1,65 @@ +/** + * @file project_create_test.cpp + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2022 Tobias Anker + * + * 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 "project_create_test.h" + +#include + +ProjectCreateTest::ProjectCreateTest(const bool expectSuccess) + : TestStep(expectSuccess) +{ + m_testName = "create user"; + if(expectSuccess) { + m_testName += " (success)"; + } else { + m_testName += " (fail)"; + } +} + +bool +ProjectCreateTest::runTest(Kitsunemimi::JsonItem &inputData, + Kitsunemimi::ErrorContainer &error) +{ + // create new user + std::string result; + if(HanamiAI::createProject(result, + inputData.get("project_id").getString(), + inputData.get("project_name").getString(), + error) != m_expectSuccess) + { + return false; + } + + if(m_expectSuccess == false) { + return true; + } + + // parse output + Kitsunemimi::JsonItem jsonItem; + if(jsonItem.parse(result, error) == false) { + return false; + } + + inputData.insert("project_id", jsonItem.get("id").getString(), true); + + return true; +} diff --git a/src/components/TsugumiTester/src/rest_api_tests/misaki/project/project_create_test.h b/src/components/TsugumiTester/src/rest_api_tests/misaki/project/project_create_test.h new file mode 100644 index 00000000..3e4f372d --- /dev/null +++ b/src/components/TsugumiTester/src/rest_api_tests/misaki/project/project_create_test.h @@ -0,0 +1,38 @@ +/** + * @file project_create_test.h + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2022 Tobias Anker + * + * 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. + */ + +#ifndef TSUGUMITESTER_PROJECTCREATESTEP_H +#define TSUGUMITESTER_PROJECTCREATESTEP_H + +#include + +class ProjectCreateTest + : public TestStep +{ +public: + ProjectCreateTest(const bool expectSuccess); + + bool runTest(Kitsunemimi::JsonItem &inputData, + Kitsunemimi::ErrorContainer &error); +}; + +#endif // TSUGUMITESTER_PROJECTCREATESTEP_H diff --git a/src/components/TsugumiTester/src/rest_api_tests/misaki/project/project_delete_test.cpp b/src/components/TsugumiTester/src/rest_api_tests/misaki/project/project_delete_test.cpp new file mode 100644 index 00000000..20537b84 --- /dev/null +++ b/src/components/TsugumiTester/src/rest_api_tests/misaki/project/project_delete_test.cpp @@ -0,0 +1,76 @@ +/** + * @file project_delete_test.cpp + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2022 Tobias Anker + * + * 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 "project_delete_test.h" + +#include + +ProjectDeleteTest::ProjectDeleteTest(const bool expectSuccess, + const std::string &nameOverride) + : TestStep(expectSuccess) +{ + m_testName = "delete project"; + if(expectSuccess) { + m_testName += " (success)"; + } else { + m_testName += " (fail)"; + } + m_nameOverride = nameOverride; +} + +bool +ProjectDeleteTest::runTest(Kitsunemimi::JsonItem &inputData, + Kitsunemimi::ErrorContainer &error) +{ + // delete user by name + std::string result; + if(m_nameOverride != "") + { + if(HanamiAI::deleteProject(result, + m_nameOverride, + error) != m_expectSuccess) + { + return false; + } + } + else + { + if(HanamiAI::deleteProject(result, + inputData.get("project_id").getString(), + error) != m_expectSuccess) + { + return false; + } + } + + if(m_expectSuccess == false) { + return true; + } + + // parse output + Kitsunemimi::JsonItem jsonItem; + if(jsonItem.parse(result, error) == false) { + return false; + } + + return true; +} diff --git a/src/components/TsugumiTester/src/rest_api_tests/misaki/project/project_delete_test.h b/src/components/TsugumiTester/src/rest_api_tests/misaki/project/project_delete_test.h new file mode 100644 index 00000000..cba8d3c4 --- /dev/null +++ b/src/components/TsugumiTester/src/rest_api_tests/misaki/project/project_delete_test.h @@ -0,0 +1,42 @@ +/** + * @file project_delete_test.h + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2022 Tobias Anker + * + * 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. + */ + +#ifndef TSUGUMITESTER_PROJECTDELETESTEP_H +#define TSUGUMITESTER_PROJECTDELETESTEP_H + +#include + +class ProjectDeleteTest + : public TestStep +{ +public: + ProjectDeleteTest(const bool expectSuccess, + const std::string &nameOverride = ""); + + bool runTest(Kitsunemimi::JsonItem &inputData, + Kitsunemimi::ErrorContainer &error); + +private: + std::string m_nameOverride = ""; +}; + +#endif // TSUGUMITESTER_PROJECTDELETESTEP_H diff --git a/src/components/TsugumiTester/src/rest_api_tests/misaki/project/project_get_test.cpp b/src/components/TsugumiTester/src/rest_api_tests/misaki/project/project_get_test.cpp new file mode 100644 index 00000000..145d391a --- /dev/null +++ b/src/components/TsugumiTester/src/rest_api_tests/misaki/project/project_get_test.cpp @@ -0,0 +1,67 @@ +/** + * @file project_get_test.cpp + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2022 Tobias Anker + * + * 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 "project_get_test.h" + +#include + +ProjectGetTest::ProjectGetTest(const bool expectSuccess, + const std::string &nameOverride) + : TestStep(expectSuccess) +{ + m_testName = "get project"; + if(expectSuccess) { + m_testName += " (success)"; + } else { + m_testName += " (fail)"; + } + m_name = nameOverride; +} + +bool +ProjectGetTest::runTest(Kitsunemimi::JsonItem &inputData, + Kitsunemimi::ErrorContainer &error) +{ + if(m_name == "") { + m_name = inputData.get("project_id").getString(); + } + + // get user by name + std::string result; + if(HanamiAI::getProject(result, m_name, error) != m_expectSuccess) { + return false; + } + + if(m_expectSuccess == false) { + return true; + } + + // parse output + Kitsunemimi::JsonItem jsonItem; + if(jsonItem.parse(result, error) == false) { + return false; + } + + inputData.insert("project_id", jsonItem.get("id").getString(), true); + + return true; +} diff --git a/src/components/TsugumiTester/src/rest_api_tests/misaki/project/project_get_test.h b/src/components/TsugumiTester/src/rest_api_tests/misaki/project/project_get_test.h new file mode 100644 index 00000000..bc09ca0e --- /dev/null +++ b/src/components/TsugumiTester/src/rest_api_tests/misaki/project/project_get_test.h @@ -0,0 +1,42 @@ +/** + * @file project_get_test.h + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2022 Tobias Anker + * + * 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. + */ + +#ifndef TSUGUMITESTER_PROJECTGETSTEP_H +#define TSUGUMITESTER_PROJECTGETSTEP_H + +#include + +class ProjectGetTest + : public TestStep +{ +public: + ProjectGetTest(const bool expectSuccess, + const std::string &nameOverride = ""); + + bool runTest(Kitsunemimi::JsonItem &inputData, + Kitsunemimi::ErrorContainer &error); + +private: + std::string m_name = ""; +}; + +#endif // TSUGUMITESTER_PROJECTGETSTEP_H diff --git a/src/components/TsugumiTester/src/rest_api_tests/misaki/project/project_list_test.cpp b/src/components/TsugumiTester/src/rest_api_tests/misaki/project/project_list_test.cpp new file mode 100644 index 00000000..8e4b7cfb --- /dev/null +++ b/src/components/TsugumiTester/src/rest_api_tests/misaki/project/project_list_test.cpp @@ -0,0 +1,61 @@ +/** + * @file project_list_test.cpp + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2022 Tobias Anker + * + * 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 "project_list_test.h" + +#include + +ProjectListTest::ProjectListTest(const bool expectSuccess) + : TestStep(expectSuccess) +{ + m_testName = "list projects"; + if(expectSuccess) { + m_testName += " (success)"; + } else { + m_testName += " (fail)"; + } +} + +bool +ProjectListTest::runTest(Kitsunemimi::JsonItem &inputData, + Kitsunemimi::ErrorContainer &error) +{ + // list all users + std::string result; + if(HanamiAI::listProject(result, error) != m_expectSuccess) { + return false; + } + + if(m_expectSuccess == false) { + return true; + } + + // parse output + Kitsunemimi::JsonItem jsonItem; + if(jsonItem.parse(result, error) == false) { + return false; + } + + inputData.insert("number_of_projects", static_cast(jsonItem.size()), true); + + return true; +} diff --git a/src/components/TsugumiTester/src/rest_api_tests/misaki/project/project_list_test.h b/src/components/TsugumiTester/src/rest_api_tests/misaki/project/project_list_test.h new file mode 100644 index 00000000..46062e75 --- /dev/null +++ b/src/components/TsugumiTester/src/rest_api_tests/misaki/project/project_list_test.h @@ -0,0 +1,38 @@ +/** + * @file project_list_test.h + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2022 Tobias Anker + * + * 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. + */ + +#ifndef TSUGUMITESTER_PROJECTLISTSTEP_H +#define TSUGUMITESTER_PROJECTLISTSTEP_H + +#include + +class ProjectListTest + : public TestStep +{ +public: + ProjectListTest(const bool expectSuccess); + + bool runTest(Kitsunemimi::JsonItem &inputData, + Kitsunemimi::ErrorContainer &error); +}; + +#endif // TSUGUMITESTER_PROJECTLISTSTEP_H diff --git a/src/components/TsugumiTester/src/rest_api_tests/misaki/user/user_create_test.cpp b/src/components/TsugumiTester/src/rest_api_tests/misaki/user/user_create_test.cpp new file mode 100644 index 00000000..1d746879 --- /dev/null +++ b/src/components/TsugumiTester/src/rest_api_tests/misaki/user/user_create_test.cpp @@ -0,0 +1,67 @@ +/** + * @file user_create_test.cpp + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2022 Tobias Anker + * + * 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 "user_create_test.h" + +#include + +UserCreateTest::UserCreateTest(const bool expectSuccess) + : TestStep(expectSuccess) +{ + m_testName = "create user"; + if(expectSuccess) { + m_testName += " (success)"; + } else { + m_testName += " (fail)"; + } +} + +bool +UserCreateTest::runTest(Kitsunemimi::JsonItem &inputData, + Kitsunemimi::ErrorContainer &error) +{ + // create new user + std::string result; + if(HanamiAI::createUser(result, + inputData.get("user_id").getString(), + inputData.get("user_name").getString(), + inputData.get("password").getString(), + inputData.get("is_admin").getBool(), + error) != m_expectSuccess) + { + return false; + } + + if(m_expectSuccess == false) { + return true; + } + + // parse output + Kitsunemimi::JsonItem jsonItem; + if(jsonItem.parse(result, error) == false) { + return false; + } + + inputData.insert("user_id", jsonItem.get("id").getString(), true); + + return true; +} diff --git a/src/components/TsugumiTester/src/rest_api_tests/misaki/user/user_create_test.h b/src/components/TsugumiTester/src/rest_api_tests/misaki/user/user_create_test.h new file mode 100644 index 00000000..fdbeac80 --- /dev/null +++ b/src/components/TsugumiTester/src/rest_api_tests/misaki/user/user_create_test.h @@ -0,0 +1,38 @@ +/** + * @file user_create_test.h + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2022 Tobias Anker + * + * 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. + */ + +#ifndef TSUGUMITESTER_USERCREATESTEP_H +#define TSUGUMITESTER_USERCREATESTEP_H + +#include + +class UserCreateTest + : public TestStep +{ +public: + UserCreateTest(const bool expectSuccess); + + bool runTest(Kitsunemimi::JsonItem &inputData, + Kitsunemimi::ErrorContainer &error); +}; + +#endif // TSUGUMITESTER_USERCREATESTEP_H diff --git a/src/components/TsugumiTester/src/rest_api_tests/misaki/user/user_delete_test.cpp b/src/components/TsugumiTester/src/rest_api_tests/misaki/user/user_delete_test.cpp new file mode 100644 index 00000000..2f67ee3f --- /dev/null +++ b/src/components/TsugumiTester/src/rest_api_tests/misaki/user/user_delete_test.cpp @@ -0,0 +1,76 @@ +/** + * @file user_delete_test.cpp + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2022 Tobias Anker + * + * 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 "user_delete_test.h" + +#include + +UserDeleteTest::UserDeleteTest(const bool expectSuccess, + const std::string &nameOverride) + : TestStep(expectSuccess) +{ + m_testName = "delete user"; + if(expectSuccess) { + m_testName += " (success)"; + } else { + m_testName += " (fail)"; + } + m_nameOverride = nameOverride; +} + +bool +UserDeleteTest::runTest(Kitsunemimi::JsonItem &inputData, + Kitsunemimi::ErrorContainer &error) +{ + // delete user by name + std::string result; + if(m_nameOverride != "") + { + if(HanamiAI::deleteUser(result, + m_nameOverride, + error) != m_expectSuccess) + { + return false; + } + } + else + { + if(HanamiAI::deleteUser(result, + inputData.get("user_id").getString(), + error) != m_expectSuccess) + { + return false; + } + } + + if(m_expectSuccess == false) { + return true; + } + + // parse output + Kitsunemimi::JsonItem jsonItem; + if(jsonItem.parse(result, error) == false) { + return false; + } + + return true; +} diff --git a/src/components/TsugumiTester/src/rest_api_tests/misaki/user/user_delete_test.h b/src/components/TsugumiTester/src/rest_api_tests/misaki/user/user_delete_test.h new file mode 100644 index 00000000..a87c5bc6 --- /dev/null +++ b/src/components/TsugumiTester/src/rest_api_tests/misaki/user/user_delete_test.h @@ -0,0 +1,42 @@ +/** + * @file user_delete_test.h + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2022 Tobias Anker + * + * 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. + */ + +#ifndef TSUGUMITESTER_USERDELETESTEP_H +#define TSUGUMITESTER_USERDELETESTEP_H + +#include + +class UserDeleteTest + : public TestStep +{ +public: + UserDeleteTest(const bool expectSuccess, + const std::string &nameOverride = ""); + + bool runTest(Kitsunemimi::JsonItem &inputData, + Kitsunemimi::ErrorContainer &error); + +private: + std::string m_nameOverride = ""; +}; + +#endif // TSUGUMITESTER_USERDELETESTEP_H diff --git a/src/components/TsugumiTester/src/rest_api_tests/misaki/user/user_get_test.cpp b/src/components/TsugumiTester/src/rest_api_tests/misaki/user/user_get_test.cpp new file mode 100644 index 00000000..28ee2676 --- /dev/null +++ b/src/components/TsugumiTester/src/rest_api_tests/misaki/user/user_get_test.cpp @@ -0,0 +1,67 @@ +/** + * @file user_get_test.cpp + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2022 Tobias Anker + * + * 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 "user_get_test.h" + +#include + +UserGetTest::UserGetTest(const bool expectSuccess, + const std::string &nameOverride) + : TestStep(expectSuccess) +{ + m_testName = "get user"; + if(expectSuccess) { + m_testName += " (success)"; + } else { + m_testName += " (fail)"; + } + m_id = nameOverride; +} + +bool +UserGetTest::runTest(Kitsunemimi::JsonItem &inputData, + Kitsunemimi::ErrorContainer &error) +{ + if(m_id == "") { + m_id = inputData.get("user_id").getString(); + } + + // get user by name + std::string result; + if(HanamiAI::getUser(result, m_id, error) != m_expectSuccess) { + return false; + } + + if(m_expectSuccess == false) { + return true; + } + + // parse output + Kitsunemimi::JsonItem jsonItem; + if(jsonItem.parse(result, error) == false) { + return false; + } + + inputData.insert("user_id", jsonItem.get("id").getString(), true); + + return true; +} diff --git a/src/components/TsugumiTester/src/rest_api_tests/misaki/user/user_get_test.h b/src/components/TsugumiTester/src/rest_api_tests/misaki/user/user_get_test.h new file mode 100644 index 00000000..bc9cfd24 --- /dev/null +++ b/src/components/TsugumiTester/src/rest_api_tests/misaki/user/user_get_test.h @@ -0,0 +1,42 @@ +/** + * @file user_get_test.h + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2022 Tobias Anker + * + * 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. + */ + +#ifndef TSUGUMITESTER_USERGETSTEP_H +#define TSUGUMITESTER_USERGETSTEP_H + +#include + +class UserGetTest + : public TestStep +{ +public: + UserGetTest(const bool expectSuccess, + const std::string &nameOverride = ""); + + bool runTest(Kitsunemimi::JsonItem &inputData, + Kitsunemimi::ErrorContainer &error); + +private: + std::string m_id = ""; +}; + +#endif // TSUGUMITESTER_USERGETSTEP_H diff --git a/src/components/TsugumiTester/src/rest_api_tests/misaki/user/user_list_test.cpp b/src/components/TsugumiTester/src/rest_api_tests/misaki/user/user_list_test.cpp new file mode 100644 index 00000000..50b44394 --- /dev/null +++ b/src/components/TsugumiTester/src/rest_api_tests/misaki/user/user_list_test.cpp @@ -0,0 +1,61 @@ +/** + * @file user_list_test.cpp + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2022 Tobias Anker + * + * 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 "user_list_test.h" + +#include + +UserListTest::UserListTest(const bool expectSuccess) + : TestStep(expectSuccess) +{ + m_testName = "list user"; + if(expectSuccess) { + m_testName += " (success)"; + } else { + m_testName += " (fail)"; + } +} + +bool +UserListTest::runTest(Kitsunemimi::JsonItem &inputData, + Kitsunemimi::ErrorContainer &error) +{ + // list all users + std::string result; + if(HanamiAI::listUser(result, error) != m_expectSuccess) { + return false; + } + + if(m_expectSuccess == false) { + return true; + } + + // parse output + Kitsunemimi::JsonItem jsonItem; + if(jsonItem.parse(result, error) == false) { + return false; + } + + inputData.insert("number_of_users", static_cast(jsonItem.size()), true); + + return true; +} diff --git a/src/components/TsugumiTester/src/rest_api_tests/misaki/user/user_list_test.h b/src/components/TsugumiTester/src/rest_api_tests/misaki/user/user_list_test.h new file mode 100644 index 00000000..4c011540 --- /dev/null +++ b/src/components/TsugumiTester/src/rest_api_tests/misaki/user/user_list_test.h @@ -0,0 +1,38 @@ +/** + * @file user_list_test.h + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2022 Tobias Anker + * + * 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. + */ + +#ifndef TSUGUMITESTER_USERLISTSTEP_H +#define TSUGUMITESTER_USERLISTSTEP_H + +#include + +class UserListTest + : public TestStep +{ +public: + UserListTest(const bool expectSuccess); + + bool runTest(Kitsunemimi::JsonItem &inputData, + Kitsunemimi::ErrorContainer &error); +}; + +#endif // TSUGUMITESTER_USERLISTSTEP_H diff --git a/src/components/TsugumiTester/src/rest_api_tests/rest_api_tests.cpp b/src/components/TsugumiTester/src/rest_api_tests/rest_api_tests.cpp new file mode 100644 index 00000000..365e3589 --- /dev/null +++ b/src/components/TsugumiTester/src/rest_api_tests/rest_api_tests.cpp @@ -0,0 +1,298 @@ +/** + * @file rest_api_tests.cpp + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2022 Tobias Anker + * + * 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 "rest_api_tests.h" + +#include +#include +#include + +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include + +#include +#include +#include +#include + +/** + * @brief initialize client by requesting a token, which is used for all tests + */ +bool +initClient() +{ + Kitsunemimi::ErrorContainer error; + + bool success = false; + const std::string host = GET_STRING_CONFIG("connection", "host", success); + const std::string port = std::to_string(GET_INT_CONFIG("connection", "port", success)); + const std::string user = GET_STRING_CONFIG("connection", "test_user", success); + const std::string pw = GET_STRING_CONFIG("connection", "test_pw", success); + + if(HanamiAI::initClient(host, port, user, pw, error) == false) + { + LOG_ERROR(error); + return false; + } + + return true; +} + +/** + * @brief delete all templates of the test-user to avoid name-conflics + */ +void +deleteTemplate() +{ + std::string result = ""; + Kitsunemimi::ErrorContainer error; + HanamiAI::listTemplate(result, error); + + Kitsunemimi::JsonItem parsedList; + parsedList.parse(result, error); + + Kitsunemimi::JsonItem body = parsedList.get("body"); + for(uint64_t i = 0; i < body.size(); i++) + { + const std::string uuid = body.get(i).get(0).getString(); + HanamiAI::deleteTemplate(result, uuid, error); + } +} + +/** + * @brief run tests with the usage of MNIST-images + * + * @param inputData json-item with names and other predefined values for the tests + */ +void +runImageTest(Kitsunemimi::JsonItem &inputData) +{ + deleteTemplate(); + + TestThread testThread("test_thread", inputData); + + Kitsunemimi::JsonItem overrideData; + + // test project in misaki + testThread.addTest(new ProjectCreateTest(true)); + testThread.addTest(new ProjectCreateTest(false)); + testThread.addTest(new ProjectListTest(true)); + testThread.addTest(new ProjectGetTest(true)); + testThread.addTest(new ProjectGetTest(false, "fail_project")); + + // test user in misaki + testThread.addTest(new UserCreateTest(true)); + testThread.addTest(new UserCreateTest(false)); + testThread.addTest(new UserListTest(true)); + testThread.addTest(new UserGetTest(true)); + testThread.addTest(new UserGetTest(false, "fail_user")); + + // test data-sets of shiori + testThread.addTest(new DataSetCreateMnistTest(true, "request")); + testThread.addTest(new DataSetCreateMnistTest(true, "learn")); + testThread.addTest(new DataSetListTest(true)); + testThread.addTest(new DataSetGetTest(true, "learn")); + testThread.addTest(new DataSetGetTest(false, "learn", "fail_user")); + + // test templates of kyouko + testThread.addTest(new TemplateUploadTest(true)); + testThread.addTest(new TemplateGetTest(true)); + testThread.addTest(new TemplateListTest(true)); + + // test cluster of kyouko + testThread.addTest(new ClusterCreateTest(true)); + testThread.addTest(new ClusterGetTest(true)); + testThread.addTest(new ClusterListTest(true)); + + // test learning-tasks of kyouko + for(int i = 0; i < 1; i++) { + testThread.addTest(new ImageLearnTaskTest(true)); + } + + // test cluster load and restore of kyouko and shiori + testThread.addTest(new ClusterSaveTest(true)); + testThread.addTest(new ClusterDeleteTest(true)); + testThread.addTest(new ClusterCreateTest(true)); + testThread.addTest(new ClusterLoadTest(true)); + + // test request-tasks of kyouko + testThread.addTest(new ImageRequestTaskTest(true)); + testThread.addTest(new DataSetCheckTest(true)); + + // test request-result endpoints in shiori + testThread.addTest(new RequestResultGetTest(true)); + testThread.addTest(new RequestResultListTest(true)); + + // test snapshots of shiori + testThread.addTest(new SnapshotGetTest(true)); + testThread.addTest(new SnapshotListTest(true)); + + // test direct-io of kyouko + testThread.addTest(new ClusterSwitchToDirectTest(true)); + testThread.addTest(new DirectIoTest(true)); + testThread.addTest(new ClusterSwitchToTaskTest(true)); + + // test delete of all + testThread.addTest(new UserDeleteTest(true)); + testThread.addTest(new UserDeleteTest(false)); + testThread.addTest(new ProjectDeleteTest(true)); + testThread.addTest(new ProjectDeleteTest(false)); + testThread.addTest(new SnapshotDeleteTest(true)); + testThread.addTest(new ClusterDeleteTest(true)); + testThread.addTest(new ClusterDeleteTest(false)); + testThread.addTest(new TemplateDeleteTest(true)); + testThread.addTest(new TemplateDeleteTest(false)); + testThread.addTest(new RequestResultDeleteTest(true)); + testThread.addTest(new RequestResultDeleteTest(false)); + testThread.addTest(new DataSetDeleteTest(true, "request")); + testThread.addTest(new DataSetDeleteTest(true, "learn")); + testThread.addTest(new DataSetDeleteTest(false, "learn")); + + // check that the running user can not delete himself + bool success = false; + const std::string user = GET_STRING_CONFIG("connection", "test_user", success); + testThread.addTest(new UserDeleteTest(false, user)); + + testThread.startThread(); + + while(testThread.isFinished == false) { + usleep(100000); + } +} + +/** + * @brief runRestApiTests + */ +bool +runRestApiTests() +{ + bool success = false; + + if(initClient() == false) { + return false; + } + + const std::string segmentTemplate("version: 1\n" + "segment_type: dynamic_segment\n" + "settings:\n" + " max_synapse_sections: 100000\n" + " synapse_segmentation: 7\n" + " sign_neg: 0.5\n" + " \n" + "bricks:\n" + " 1,1,1\n" + " input: test_input\n" + " number_of_neurons: 784\n" + " 2,1,1\n" + " number_of_neurons: 300\n" + " 3,1,1\n" + " output: test_output\n" + " number_of_neurons: 10"); + + const std::string clusterDefinition("version: 1\n" + "segments:\n" + " input\n" + " name: input\n" + " out: -> central : test_input\n" + " \n" + " dynamic\n" + " name: central\n" + " out: test_output -> output\n" + "\n" + " output\n" + " name: output\n"); + + Kitsunemimi::JsonItem inputData; + + // add data for the test-user to create + inputData.insert("user_id", "tsugumi"); + inputData.insert("user_name", "Tsugumi"); + inputData.insert("password", "new password"); + inputData.insert("admin", true); + inputData.insert("role", "tester"); + inputData.insert("project_id", "test_project"); + inputData.insert("project_name", "Test Project"); + + // add data from config + inputData.insert("learn_inputs", GET_STRING_CONFIG("test_data", "learn_inputs", success)), + inputData.insert("learn_labels", GET_STRING_CONFIG("test_data", "learn_labels", success)), + inputData.insert("request_inputs", GET_STRING_CONFIG("test_data", "request_inputs", success)), + inputData.insert("request_labels", GET_STRING_CONFIG("test_data", "request_labels", success)), + inputData.insert("base_inputs", GET_STRING_CONFIG("test_data", "base_inputs", success)), + + // add predefined names for the coming test-resources + inputData.insert("cluster_name", "test_cluster"); + inputData.insert("cluster_snapshot_name", "test_snapshot"); + inputData.insert("generic_task_name", "test_task"); + inputData.insert("template_name", "dynamic"); + inputData.insert("template_segment", segmentTemplate); + inputData.insert("cluster_definition", clusterDefinition); + inputData.insert("request_dataset_name", "request_test_dataset"); + inputData.insert("learn_dataset_name", "learn_test_dataset"); + inputData.insert("base_dataset_name", "base_test_dataset"); + + runImageTest(inputData); + + return true; +} + diff --git a/src/components/TsugumiTester/src/rest_api_tests/rest_api_tests.h b/src/components/TsugumiTester/src/rest_api_tests/rest_api_tests.h new file mode 100644 index 00000000..b7c5d7f2 --- /dev/null +++ b/src/components/TsugumiTester/src/rest_api_tests/rest_api_tests.h @@ -0,0 +1,30 @@ +/** + * @file rest_api_tests.h + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2022 Tobias Anker + * + * 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. + */ + +#ifndef TSUGUMITESTER_REST_API_TESTS_H +#define TSUGUMITESTER_REST_API_TESTS_H + +#include + +bool runRestApiTests(); + +#endif // TSUGUMITESTER_REST_API_TESTS_H diff --git a/src/components/TsugumiTester/src/rest_api_tests/shiori/datasets/dataset_check_test.cpp b/src/components/TsugumiTester/src/rest_api_tests/shiori/datasets/dataset_check_test.cpp new file mode 100644 index 00000000..1a9fb331 --- /dev/null +++ b/src/components/TsugumiTester/src/rest_api_tests/shiori/datasets/dataset_check_test.cpp @@ -0,0 +1,66 @@ +/** + * @file dataset_check_test.cpp + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2022 Tobias Anker + * + * 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 "dataset_check_test.h" + +#include +#include + +DataSetCheckTest::DataSetCheckTest(const bool expectSuccess) + : TestStep(expectSuccess) +{ + m_testName = "check data-set"; + if(expectSuccess) { + m_testName += " (success)"; + } else { + m_testName += " (fail)"; + } +} + +bool +DataSetCheckTest::runTest(Kitsunemimi::JsonItem &inputData, + Kitsunemimi::ErrorContainer &error) +{ + // get template by name + std::string result; + if(HanamiAI::checkDataset(result, + inputData.get("request_dataset_uuid").getString(), + inputData.get("request_task_uuid").getString(), + error) != m_expectSuccess) + { + return false; + } + + if(m_expectSuccess == false) { + return true; + } + + // parse output + Kitsunemimi::JsonItem jsonItem; + if(jsonItem.parse(result, error) == false) { + return false; + } + + std::cout< + * + * @copyright Apache License Version 2.0 + * + * Copyright 2022 Tobias Anker + * + * 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. + */ + +#ifndef TSUGUMITESTER_DATASETCHECKTEST_H +#define TSUGUMITESTER_DATASETCHECKTEST_H + +#include + +class DataSetCheckTest + : public TestStep +{ +public: + DataSetCheckTest(const bool expectSuccess); + + bool runTest(Kitsunemimi::JsonItem &inputData, + Kitsunemimi::ErrorContainer &error); +}; + +#endif // TSUGUMITESTER_DATASETCHECKTEST_H diff --git a/src/components/TsugumiTester/src/rest_api_tests/shiori/datasets/dataset_create_csv_test.cpp b/src/components/TsugumiTester/src/rest_api_tests/shiori/datasets/dataset_create_csv_test.cpp new file mode 100644 index 00000000..4e7cfea1 --- /dev/null +++ b/src/components/TsugumiTester/src/rest_api_tests/shiori/datasets/dataset_create_csv_test.cpp @@ -0,0 +1,65 @@ +/** + * @file dataset_create_csv_test.cpp + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2022 Tobias Anker + * + * 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 "dataset_create_csv_test.h" + +#include +#include + +DataSetCreateCsvTest::DataSetCreateCsvTest(const bool expectSuccess) + : TestStep(expectSuccess) +{ + m_testName = "create csv data-set"; + if(expectSuccess) { + m_testName += " (success)"; + } else { + m_testName += " (fail)"; + } +} + +bool +DataSetCreateCsvTest::runTest(Kitsunemimi::JsonItem &inputData, + Kitsunemimi::ErrorContainer &error) +{ + std::string result; + if(HanamiAI::uploadCsvData(result, + inputData.get("base_dataset_name").getString(), + inputData.get("base_inputs").getString(), + error) != m_expectSuccess) + { + return false; + } + + if(m_expectSuccess == false) { + return true; + } + + // parse output + Kitsunemimi::JsonItem jsonItem; + if(jsonItem.parse(result, error) == false) { + return false; + } + + inputData.insert("base_dataset_uuid", jsonItem.get("uuid").getString(), true); + + return true; +} diff --git a/src/components/TsugumiTester/src/rest_api_tests/shiori/datasets/dataset_create_csv_test.h b/src/components/TsugumiTester/src/rest_api_tests/shiori/datasets/dataset_create_csv_test.h new file mode 100644 index 00000000..7a47b741 --- /dev/null +++ b/src/components/TsugumiTester/src/rest_api_tests/shiori/datasets/dataset_create_csv_test.h @@ -0,0 +1,38 @@ +/** + * @file dataset_create_csv_test.h + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2022 Tobias Anker + * + * 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. + */ + +#ifndef TSUGUMITESTER_DATASETCREATE_CSV_TEST_H +#define TSUGUMITESTER_DATASETCREATE_CSV_TEST_H + +#include + +class DataSetCreateCsvTest + : public TestStep +{ +public: + DataSetCreateCsvTest(const bool expectSuccess); + + bool runTest(Kitsunemimi::JsonItem &inputData, + Kitsunemimi::ErrorContainer &error); +}; + +#endif // TSUGUMITESTER_DATASETCREATE_CSV_TEST_H diff --git a/src/components/TsugumiTester/src/rest_api_tests/shiori/datasets/dataset_create_mnist_test.cpp b/src/components/TsugumiTester/src/rest_api_tests/shiori/datasets/dataset_create_mnist_test.cpp new file mode 100644 index 00000000..19b4eb7e --- /dev/null +++ b/src/components/TsugumiTester/src/rest_api_tests/shiori/datasets/dataset_create_mnist_test.cpp @@ -0,0 +1,87 @@ +/** + * @file dataset_create_mnist_test.cpp + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2022 Tobias Anker + * + * 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 "dataset_create_mnist_test.h" + +#include +#include + +DataSetCreateMnistTest::DataSetCreateMnistTest(const bool expectSuccess, + const std::string &type) + : TestStep(expectSuccess) +{ + m_testName = "create mnist data-set"; + if(expectSuccess) { + m_testName += " (success)"; + } else { + m_testName += " (fail)"; + } + m_type = type; +} + +bool +DataSetCreateMnistTest::runTest(Kitsunemimi::JsonItem &inputData, + Kitsunemimi::ErrorContainer &error) +{ + std::string result; + if(m_type == "learn") + { + if(HanamiAI::uploadMnistData(result, + inputData.get("learn_dataset_name").getString(), + inputData.get("learn_inputs").getString(), + inputData.get("learn_labels").getString(), + error) != m_expectSuccess) + { + return false; + } + } + else + { + if(HanamiAI::uploadMnistData(result, + inputData.get("request_dataset_name").getString(), + inputData.get("request_inputs").getString(), + inputData.get("request_labels").getString(), + error) != m_expectSuccess) + { + return false; + } + } + + + if(m_expectSuccess == false) { + return true; + } + + // parse output + Kitsunemimi::JsonItem jsonItem; + if(jsonItem.parse(result, error) == false) { + return false; + } + + if(m_type == "learn") { + inputData.insert("learn_dataset_uuid", jsonItem.get("uuid").getString(), true); + } else { + inputData.insert("request_dataset_uuid", jsonItem.get("uuid").getString(), true); + } + + return true; +} diff --git a/src/components/TsugumiTester/src/rest_api_tests/shiori/datasets/dataset_create_mnist_test.h b/src/components/TsugumiTester/src/rest_api_tests/shiori/datasets/dataset_create_mnist_test.h new file mode 100644 index 00000000..43fa49e9 --- /dev/null +++ b/src/components/TsugumiTester/src/rest_api_tests/shiori/datasets/dataset_create_mnist_test.h @@ -0,0 +1,42 @@ +/** + * @file dataset_create_mnist_test.h + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2022 Tobias Anker + * + * 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. + */ + +#ifndef TSUGUMITESTER_DATASETCREATE_MNIST_TEST_H +#define TSUGUMITESTER_DATASETCREATE_MNIST_TEST_H + +#include + +class DataSetCreateMnistTest + : public TestStep +{ +public: + DataSetCreateMnistTest(const bool expectSuccess, + const std::string &type); + + bool runTest(Kitsunemimi::JsonItem &inputData, + Kitsunemimi::ErrorContainer &error); + +private: + std::string m_type = ""; +}; + +#endif // TSUGUMITESTER_DATASETCREATE_MNIST_TEST_H diff --git a/src/components/TsugumiTester/src/rest_api_tests/shiori/datasets/dataset_delete_test.cpp b/src/components/TsugumiTester/src/rest_api_tests/shiori/datasets/dataset_delete_test.cpp new file mode 100644 index 00000000..961a4eab --- /dev/null +++ b/src/components/TsugumiTester/src/rest_api_tests/shiori/datasets/dataset_delete_test.cpp @@ -0,0 +1,72 @@ +/** + * @file dataset_delete_test.cpp + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2022 Tobias Anker + * + * 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 "dataset_delete_test.h" + +#include + +DataSetDeleteTest::DataSetDeleteTest(const bool expectSuccess, + const std::string &type) + : TestStep(expectSuccess) +{ + m_testName = "delete data-set"; + if(expectSuccess) { + m_testName += " (success)"; + } else { + m_testName += " (fail)"; + } + m_type = type; +} + +bool +DataSetDeleteTest::runTest(Kitsunemimi::JsonItem &inputData, + Kitsunemimi::ErrorContainer &error) +{ + std::string uuid = ""; + if(m_type == "learn") { + uuid = inputData.get("learn_dataset_uuid").getString(); + } else if(m_type == "request") { + uuid = inputData.get("request_dataset_uuid").getString(); + } else { + uuid = inputData.get("base_dataset_uuid").getString(); + } + + // delete user by name + std::string result; + if(HanamiAI::deleteDataset(result, uuid, error) != m_expectSuccess) + { + return false; + } + + if(m_expectSuccess == false) { + return true; + } + + // parse output + Kitsunemimi::JsonItem jsonItem; + if(jsonItem.parse(result, error) == false) { + return false; + } + + return true; +} + diff --git a/src/components/TsugumiTester/src/rest_api_tests/shiori/datasets/dataset_delete_test.h b/src/components/TsugumiTester/src/rest_api_tests/shiori/datasets/dataset_delete_test.h new file mode 100644 index 00000000..35bfd87a --- /dev/null +++ b/src/components/TsugumiTester/src/rest_api_tests/shiori/datasets/dataset_delete_test.h @@ -0,0 +1,42 @@ +/** + * @file dataset_delete_test.h + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2022 Tobias Anker + * + * 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. + */ + +#ifndef TSUGUMITESTER_DATASETDELETETEST_H +#define TSUGUMITESTER_DATASETDELETETEST_H + +#include + +class DataSetDeleteTest + : public TestStep +{ +public: + DataSetDeleteTest(const bool expectSuccess, + const std::string &type); + + bool runTest(Kitsunemimi::JsonItem &inputData, + Kitsunemimi::ErrorContainer &error); + +private: + std::string m_type = ""; +}; + +#endif // TSUGUMITESTER_DATASETDELETETEST_H diff --git a/src/components/TsugumiTester/src/rest_api_tests/shiori/datasets/dataset_get_test.cpp b/src/components/TsugumiTester/src/rest_api_tests/shiori/datasets/dataset_get_test.cpp new file mode 100644 index 00000000..10b0f8ab --- /dev/null +++ b/src/components/TsugumiTester/src/rest_api_tests/shiori/datasets/dataset_get_test.cpp @@ -0,0 +1,73 @@ +/** + * @file dataset_get_test.cpp + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2022 Tobias Anker + * + * 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 "dataset_get_test.h" + +#include + +DataSetGetTest::DataSetGetTest(const bool expectSuccess, + const std::string &type, + const std::string &uuidOverride) + : TestStep(expectSuccess) +{ + m_testName = "get data-set"; + if(expectSuccess) { + m_testName += " (success)"; + } else { + m_testName += " (fail)"; + } + m_type = type; + m_uuid = uuidOverride; +} + +bool +DataSetGetTest::runTest(Kitsunemimi::JsonItem &inputData, + Kitsunemimi::ErrorContainer &error) +{ + if(m_uuid == "") + { + if(m_type == "learn") { + m_uuid = inputData.get("learn_dataset_uuid").getString(); + } else { + m_uuid = inputData.get("request_dataset_uuid").getString(); + } + } + + // get user by name + std::string result; + if(HanamiAI::getDataset(result, m_uuid, error) != m_expectSuccess) { + return false; + } + + if(m_expectSuccess == false) { + return true; + } + + // parse output + Kitsunemimi::JsonItem jsonItem; + if(jsonItem.parse(result, error) == false) { + return false; + } + + return true; +} + diff --git a/src/components/TsugumiTester/src/rest_api_tests/shiori/datasets/dataset_get_test.h b/src/components/TsugumiTester/src/rest_api_tests/shiori/datasets/dataset_get_test.h new file mode 100644 index 00000000..d7d8aee5 --- /dev/null +++ b/src/components/TsugumiTester/src/rest_api_tests/shiori/datasets/dataset_get_test.h @@ -0,0 +1,44 @@ +/** + * @file dataset_get_test.h + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2022 Tobias Anker + * + * 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. + */ + +#ifndef TSUGUMITESTER_DATASETGETTEST_H +#define TSUGUMITESTER_DATASETGETTEST_H + +#include + +class DataSetGetTest + : public TestStep +{ +public: + DataSetGetTest(const bool expectSuccess, + const std::string &type, + const std::string &uuidOverride = ""); + + bool runTest(Kitsunemimi::JsonItem &inputData, + Kitsunemimi::ErrorContainer &error); + +private: + std::string m_type = ""; + std::string m_uuid = ""; +}; + +#endif // TSUGUMITESTER_DATASETGETTEST_H diff --git a/src/components/TsugumiTester/src/rest_api_tests/shiori/datasets/dataset_list_test.cpp b/src/components/TsugumiTester/src/rest_api_tests/shiori/datasets/dataset_list_test.cpp new file mode 100644 index 00000000..2b0046f4 --- /dev/null +++ b/src/components/TsugumiTester/src/rest_api_tests/shiori/datasets/dataset_list_test.cpp @@ -0,0 +1,62 @@ +/** + * @file dataset_list_test.cpp + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2022 Tobias Anker + * + * 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 "dataset_list_test.h" + +#include + +DataSetListTest::DataSetListTest(const bool expectSuccess) + : TestStep(expectSuccess) +{ + m_testName = "list data-set"; + if(expectSuccess) { + m_testName += " (success)"; + } else { + m_testName += " (fail)"; + } +} + +bool +DataSetListTest::runTest(Kitsunemimi::JsonItem &inputData, + Kitsunemimi::ErrorContainer &error) +{ + // list all data + std::string result; + if(HanamiAI::listDatasets(result, error) != m_expectSuccess) { + return false; + } + + if(m_expectSuccess == false) { + return true; + } + + // parse output + Kitsunemimi::JsonItem jsonItem; + if(jsonItem.parse(result, error) == false) { + return false; + } + + inputData.insert("number_of_datasets", static_cast(jsonItem.size()), true); + + return true; +} + diff --git a/src/components/TsugumiTester/src/rest_api_tests/shiori/datasets/dataset_list_test.h b/src/components/TsugumiTester/src/rest_api_tests/shiori/datasets/dataset_list_test.h new file mode 100644 index 00000000..afc09cb6 --- /dev/null +++ b/src/components/TsugumiTester/src/rest_api_tests/shiori/datasets/dataset_list_test.h @@ -0,0 +1,38 @@ +/** + * @file dataset_list_test.h + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2022 Tobias Anker + * + * 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. + */ + +#ifndef TSUGUMITESTER_DATASETLISTTEST_H +#define TSUGUMITESTER_DATASETLISTTEST_H + +#include + +class DataSetListTest + : public TestStep +{ +public: + DataSetListTest(const bool expectSuccess); + + bool runTest(Kitsunemimi::JsonItem &inputData, + Kitsunemimi::ErrorContainer &error); +}; + +#endif // TSUGUMITESTER_DATASETLISTTEST_H diff --git a/src/components/TsugumiTester/src/rest_api_tests/shiori/request_results/request_result_delete_test.cpp b/src/components/TsugumiTester/src/rest_api_tests/shiori/request_results/request_result_delete_test.cpp new file mode 100644 index 00000000..b2d1cfee --- /dev/null +++ b/src/components/TsugumiTester/src/rest_api_tests/shiori/request_results/request_result_delete_test.cpp @@ -0,0 +1,62 @@ +/** + * @file request_result_delete_test.cpp + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2022 Tobias Anker + * + * 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 "request_result_delete_test.h" + +#include + +RequestResultDeleteTest::RequestResultDeleteTest(const bool expectSuccess) + : TestStep(expectSuccess) +{ + m_testName = "delete snapshot"; + if(expectSuccess) { + m_testName += " (success)"; + } else { + m_testName += " (fail)"; + } +} + +bool +RequestResultDeleteTest::runTest(Kitsunemimi::JsonItem &inputData, + Kitsunemimi::ErrorContainer &error) +{ + const std::string uuid = inputData.get("request_task_uuid").getString(); + + // delete user by name + std::string result; + if(HanamiAI::deleteRequestResult(result, uuid, error) != m_expectSuccess) { + return false; + } + + if(m_expectSuccess == false) { + return true; + } + + // parse output + Kitsunemimi::JsonItem jsonItem; + if(jsonItem.parse(result, error) == false) { + return false; + } + + return true; +} + diff --git a/src/components/TsugumiTester/src/rest_api_tests/shiori/request_results/request_result_delete_test.h b/src/components/TsugumiTester/src/rest_api_tests/shiori/request_results/request_result_delete_test.h new file mode 100644 index 00000000..6a230c0b --- /dev/null +++ b/src/components/TsugumiTester/src/rest_api_tests/shiori/request_results/request_result_delete_test.h @@ -0,0 +1,38 @@ +/** + * @file request_result_delete_test.h + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2022 Tobias Anker + * + * 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. + */ + +#ifndef TSUGUMITESTER_REQUEST_RESULT_DELETE_TEST_H +#define TSUGUMITESTER_REQUEST_RESULT_DELETE_TEST_H + +#include + +class RequestResultDeleteTest + : public TestStep +{ +public: + RequestResultDeleteTest(const bool expectSuccess); + + bool runTest(Kitsunemimi::JsonItem &inputData, + Kitsunemimi::ErrorContainer &error); +}; + +#endif // TSUGUMITESTER_REQUEST_RESULT_DELETE_TEST_H diff --git a/src/components/TsugumiTester/src/rest_api_tests/shiori/request_results/request_result_get_test.cpp b/src/components/TsugumiTester/src/rest_api_tests/shiori/request_results/request_result_get_test.cpp new file mode 100644 index 00000000..c3b95d80 --- /dev/null +++ b/src/components/TsugumiTester/src/rest_api_tests/shiori/request_results/request_result_get_test.cpp @@ -0,0 +1,66 @@ +/** + * @file request_result_get_test.cpp + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2022 Tobias Anker + * + * 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 "request_result_get_test.h" + +#include + +RequestResultGetTest::RequestResultGetTest(const bool expectSuccess, + const std::string &uuidOverride) + : TestStep(expectSuccess) +{ + m_testName = "get snapshot"; + if(expectSuccess) { + m_testName += " (success)"; + } else { + m_testName += " (fail)"; + } + m_uuid = uuidOverride; +} + +bool +RequestResultGetTest::runTest(Kitsunemimi::JsonItem &inputData, + Kitsunemimi::ErrorContainer &error) +{ + if(m_uuid == "") { + m_uuid = inputData.get("request_task_uuid").getString(); + } + + // get user by name + std::string result; + if(HanamiAI::getRequestResult(result, m_uuid, error) != m_expectSuccess) { + return false; + } + + if(m_expectSuccess == false) { + return true; + } + + // parse output + Kitsunemimi::JsonItem jsonItem; + if(jsonItem.parse(result, error) == false) { + return false; + } + + return true; +} + diff --git a/src/components/TsugumiTester/src/rest_api_tests/shiori/request_results/request_result_get_test.h b/src/components/TsugumiTester/src/rest_api_tests/shiori/request_results/request_result_get_test.h new file mode 100644 index 00000000..55bba9e5 --- /dev/null +++ b/src/components/TsugumiTester/src/rest_api_tests/shiori/request_results/request_result_get_test.h @@ -0,0 +1,42 @@ +/** + * @file request_result_get_test.h + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2022 Tobias Anker + * + * 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. + */ + +#ifndef TSUGUMITESTER_REQUEST_RESULT_GET_TEST_H +#define TSUGUMITESTER_REQUEST_RESULT_GET_TEST_H + +#include + +class RequestResultGetTest + : public TestStep +{ +public: + RequestResultGetTest(const bool expectSuccess, + const std::string &uuidOverride = ""); + + bool runTest(Kitsunemimi::JsonItem &inputData, + Kitsunemimi::ErrorContainer &error); + +private: + std::string m_uuid = ""; +}; + +#endif // TSUGUMITESTER_REQUEST_RESULT_GET_TEST_H diff --git a/src/components/TsugumiTester/src/rest_api_tests/shiori/request_results/request_result_list_test.cpp b/src/components/TsugumiTester/src/rest_api_tests/shiori/request_results/request_result_list_test.cpp new file mode 100644 index 00000000..148aa5c8 --- /dev/null +++ b/src/components/TsugumiTester/src/rest_api_tests/shiori/request_results/request_result_list_test.cpp @@ -0,0 +1,62 @@ +/** + * @file request_result_list_test.cpp + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2022 Tobias Anker + * + * 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 "request_result_list_test.h" + +#include + +RequestResultListTest::RequestResultListTest(const bool expectSuccess) + : TestStep(expectSuccess) +{ + m_testName = "list snapshot"; + if(expectSuccess) { + m_testName += " (success)"; + } else { + m_testName += " (fail)"; + } +} + +bool +RequestResultListTest::runTest(Kitsunemimi::JsonItem &inputData, + Kitsunemimi::ErrorContainer &error) +{ + // list all data + std::string result; + if(HanamiAI::listRequestResult(result, error) != m_expectSuccess) { + return false; + } + + if(m_expectSuccess == false) { + return true; + } + + // parse output + Kitsunemimi::JsonItem jsonItem; + if(jsonItem.parse(result, error) == false) { + return false; + } + + inputData.insert("number_of_request_results", static_cast(jsonItem.size()), true); + + return true; +} + diff --git a/src/components/TsugumiTester/src/rest_api_tests/shiori/request_results/request_result_list_test.h b/src/components/TsugumiTester/src/rest_api_tests/shiori/request_results/request_result_list_test.h new file mode 100644 index 00000000..93438761 --- /dev/null +++ b/src/components/TsugumiTester/src/rest_api_tests/shiori/request_results/request_result_list_test.h @@ -0,0 +1,38 @@ +/** + * @file request_result_list_test.h + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2022 Tobias Anker + * + * 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. + */ + +#ifndef TSUGUMITESTER_REQUEST_RESULT_LIST_TEST_H +#define TSUGUMITESTER_REQUEST_RESULT_LIST_TEST_H + +#include + +class RequestResultListTest + : public TestStep +{ +public: + RequestResultListTest(const bool expectSuccess); + + bool runTest(Kitsunemimi::JsonItem &inputData, + Kitsunemimi::ErrorContainer &error); +}; + +#endif // TSUGUMITESTER_REQUEST_RESULT_LIST_TEST_H diff --git a/src/components/TsugumiTester/src/rest_api_tests/shiori/snapshots/snapshot_delete_test.cpp b/src/components/TsugumiTester/src/rest_api_tests/shiori/snapshots/snapshot_delete_test.cpp new file mode 100644 index 00000000..420e726b --- /dev/null +++ b/src/components/TsugumiTester/src/rest_api_tests/shiori/snapshots/snapshot_delete_test.cpp @@ -0,0 +1,62 @@ +/** + * @file snapshot_delete_test.cpp + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2022 Tobias Anker + * + * 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 "snapshot_delete_test.h" + +#include + +SnapshotDeleteTest::SnapshotDeleteTest(const bool expectSuccess) + : TestStep(expectSuccess) +{ + m_testName = "delete snapshot"; + if(expectSuccess) { + m_testName += " (success)"; + } else { + m_testName += " (fail)"; + } +} + +bool +SnapshotDeleteTest::runTest(Kitsunemimi::JsonItem &inputData, + Kitsunemimi::ErrorContainer &error) +{ + const std::string uuid = inputData.get("cluster_snapshot_uuid").getString(); + + // delete user by name + std::string result; + if(HanamiAI::deleteSnapshot(result, uuid, error) != m_expectSuccess) { + return false; + } + + if(m_expectSuccess == false) { + return true; + } + + // parse output + Kitsunemimi::JsonItem jsonItem; + if(jsonItem.parse(result, error) == false) { + return false; + } + + return true; +} + diff --git a/src/components/TsugumiTester/src/rest_api_tests/shiori/snapshots/snapshot_delete_test.h b/src/components/TsugumiTester/src/rest_api_tests/shiori/snapshots/snapshot_delete_test.h new file mode 100644 index 00000000..90398569 --- /dev/null +++ b/src/components/TsugumiTester/src/rest_api_tests/shiori/snapshots/snapshot_delete_test.h @@ -0,0 +1,38 @@ +/** + * @file snapshot_delete_test.h + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2022 Tobias Anker + * + * 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. + */ + +#ifndef TSUGUMITESTER_SNAPSHOTDELETETEST_H +#define TSUGUMITESTER_SNAPSHOTDELETETEST_H + +#include + +class SnapshotDeleteTest + : public TestStep +{ +public: + SnapshotDeleteTest(const bool expectSuccess); + + bool runTest(Kitsunemimi::JsonItem &inputData, + Kitsunemimi::ErrorContainer &error); +}; + +#endif // TSUGUMITESTER_SNAPSHOTDELETETEST_H diff --git a/src/components/TsugumiTester/src/rest_api_tests/shiori/snapshots/snapshot_get_test.cpp b/src/components/TsugumiTester/src/rest_api_tests/shiori/snapshots/snapshot_get_test.cpp new file mode 100644 index 00000000..712bee67 --- /dev/null +++ b/src/components/TsugumiTester/src/rest_api_tests/shiori/snapshots/snapshot_get_test.cpp @@ -0,0 +1,66 @@ +/** + * @file snapshot_get_test.cpp + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2022 Tobias Anker + * + * 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 "snapshot_get_test.h" + +#include + +SnapshotGetTest::SnapshotGetTest(const bool expectSuccess, + const std::string &uuidOverride) + : TestStep(expectSuccess) +{ + m_testName = "get snapshot"; + if(expectSuccess) { + m_testName += " (success)"; + } else { + m_testName += " (fail)"; + } + m_uuid = uuidOverride; +} + +bool +SnapshotGetTest::runTest(Kitsunemimi::JsonItem &inputData, + Kitsunemimi::ErrorContainer &error) +{ + if(m_uuid == "") { + m_uuid = inputData.get("cluster_snapshot_uuid").getString(); + } + + // get user by name + std::string result; + if(HanamiAI::getSnapshot(result, m_uuid, error) != m_expectSuccess) { + return false; + } + + if(m_expectSuccess == false) { + return true; + } + + // parse output + Kitsunemimi::JsonItem jsonItem; + if(jsonItem.parse(result, error) == false) { + return false; + } + + return true; +} + diff --git a/src/components/TsugumiTester/src/rest_api_tests/shiori/snapshots/snapshot_get_test.h b/src/components/TsugumiTester/src/rest_api_tests/shiori/snapshots/snapshot_get_test.h new file mode 100644 index 00000000..3c7194d1 --- /dev/null +++ b/src/components/TsugumiTester/src/rest_api_tests/shiori/snapshots/snapshot_get_test.h @@ -0,0 +1,42 @@ +/** + * @file snapshot_get_test.h + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2022 Tobias Anker + * + * 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. + */ + +#ifndef TSUGUMITESTER_SNAPSHOTGETTEST_H +#define TSUGUMITESTER_SNAPSHOTGETTEST_H + +#include + +class SnapshotGetTest + : public TestStep +{ +public: + SnapshotGetTest(const bool expectSuccess, + const std::string &uuidOverride = ""); + + bool runTest(Kitsunemimi::JsonItem &inputData, + Kitsunemimi::ErrorContainer &error); + +private: + std::string m_uuid = ""; +}; + +#endif // TSUGUMITESTER_SNAPSHOTGETTEST_H diff --git a/src/components/TsugumiTester/src/rest_api_tests/shiori/snapshots/snapshot_list_test.cpp b/src/components/TsugumiTester/src/rest_api_tests/shiori/snapshots/snapshot_list_test.cpp new file mode 100644 index 00000000..946e2b4e --- /dev/null +++ b/src/components/TsugumiTester/src/rest_api_tests/shiori/snapshots/snapshot_list_test.cpp @@ -0,0 +1,62 @@ +/** + * @file snapshot_list_test.cpp + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2022 Tobias Anker + * + * 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 "snapshot_list_test.h" + +#include + +SnapshotListTest::SnapshotListTest(const bool expectSuccess) + : TestStep(expectSuccess) +{ + m_testName = "list snapshot"; + if(expectSuccess) { + m_testName += " (success)"; + } else { + m_testName += " (fail)"; + } +} + +bool +SnapshotListTest::runTest(Kitsunemimi::JsonItem &inputData, + Kitsunemimi::ErrorContainer &error) +{ + // list all data + std::string result; + if(HanamiAI::listSnapshot(result, error) != m_expectSuccess) { + return false; + } + + if(m_expectSuccess == false) { + return true; + } + + // parse output + Kitsunemimi::JsonItem jsonItem; + if(jsonItem.parse(result, error) == false) { + return false; + } + + inputData.insert("number_of_snapshotss", static_cast(jsonItem.size()), true); + + return true; +} + diff --git a/src/components/TsugumiTester/src/rest_api_tests/shiori/snapshots/snapshot_list_test.h b/src/components/TsugumiTester/src/rest_api_tests/shiori/snapshots/snapshot_list_test.h new file mode 100644 index 00000000..76618325 --- /dev/null +++ b/src/components/TsugumiTester/src/rest_api_tests/shiori/snapshots/snapshot_list_test.h @@ -0,0 +1,38 @@ +/** + * @file snapshot_list_test.h + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2022 Tobias Anker + * + * 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. + */ + +#ifndef TSUGUMITESTER_SNAPSHOTLISTTEST_H +#define TSUGUMITESTER_SNAPSHOTLISTTEST_H + +#include + +class SnapshotListTest + : public TestStep +{ +public: + SnapshotListTest(const bool expectSuccess); + + bool runTest(Kitsunemimi::JsonItem &inputData, + Kitsunemimi::ErrorContainer &error); +}; + +#endif // TSUGUMITESTER_SNAPSHOTLISTTEST_H diff --git a/src/components/components.pro b/src/components/components.pro new file mode 100644 index 00000000..a1e2e3bc --- /dev/null +++ b/src/components/components.pro @@ -0,0 +1,11 @@ +TEMPLATE = subdirs +CONFIG += ordered +QT -= qt core gui +CONFIG += c++17 + +SUBDIRS += ToriiGateway +SUBDIRS += ShioriArchive +SUBDIRS += MisakiGuard +SUBDIRS += AzukiHeart +SUBDIRS += KyoukoMind +SUBDIRS += TsugumiTester diff --git a/src/frontend/Hanami-AI-Dashboard-Dependencies/README.md b/src/frontend/Hanami-AI-Dashboard-Dependencies/README.md new file mode 100644 index 00000000..12462e88 --- /dev/null +++ b/src/frontend/Hanami-AI-Dashboard-Dependencies/README.md @@ -0,0 +1,25 @@ +# Hanami-AI-Dashboard-Dependencies + +This repo contains all foreign dependencies, which are used by the Hanami-AI-Dashboard (https://github.com/kitsudaiki/Hanami-AI-Dashboard): + +`d3.v6.min.js` + +- **source**: https://d3js.org/d3.v6.min.js +- **related repository**: https://github.com/d3/d3 + +`jquery.min.js` + +- **source**: https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js +- **related repository**: https://github.com/jquery/jquery + +`protobuf.min.js` and `protobuf.min.js.map` + +- **source**: + - https://cdn.rawgit.com/dcodeIO/protobuf.js/6.11.3/dist/protobuf.min.js + - https://cdn.rawgit.com/dcodeIO/protobuf.js/6.11.3/dist/protobuf.min.js.map +- **related repository**: https://github.com/protobufjs/protobuf.js/ + +`bootstrap_icons` +- **source-site**: https://icons.getbootstrap.com/ + +The files in this repository have no changes compared to the originals. It is basically only a mirror. This is primary to make the HanamiAI also completely without internet-access executable, if necessary. diff --git a/src/frontend/Hanami-AI-Dashboard-Dependencies/bootstrap_icons/check-lg.svg b/src/frontend/Hanami-AI-Dashboard-Dependencies/bootstrap_icons/check-lg.svg new file mode 100644 index 00000000..7afb0ae1 --- /dev/null +++ b/src/frontend/Hanami-AI-Dashboard-Dependencies/bootstrap_icons/check-lg.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/src/frontend/Hanami-AI-Dashboard-Dependencies/bootstrap_icons/x-lg.svg b/src/frontend/Hanami-AI-Dashboard-Dependencies/bootstrap_icons/x-lg.svg new file mode 100644 index 00000000..53aec00d --- /dev/null +++ b/src/frontend/Hanami-AI-Dashboard-Dependencies/bootstrap_icons/x-lg.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/src/frontend/Hanami-AI-Dashboard-Dependencies/d3.v6.min.js b/src/frontend/Hanami-AI-Dashboard-Dependencies/d3.v6.min.js new file mode 100644 index 00000000..05cd5cae --- /dev/null +++ b/src/frontend/Hanami-AI-Dashboard-Dependencies/d3.v6.min.js @@ -0,0 +1,2 @@ +// https://d3js.org v6.7.0 Copyright 2021 Mike Bostock +!function(t,n){"object"==typeof exports&&"undefined"!=typeof module?n(exports):"function"==typeof define&&define.amd?define(["exports"],n):n((t="undefined"!=typeof globalThis?globalThis:t||self).d3=t.d3||{})}(this,(function(t){"use strict";function n(t,n){return tn?1:t>=n?0:NaN}function e(t){let e=t,r=t;function i(t,n,e,i){for(null==e&&(e=0),null==i&&(i=t.length);e>>1;r(t[o],n)<0?e=o+1:i=o}return e}return 1===t.length&&(e=(n,e)=>t(n)-e,r=function(t){return(e,r)=>n(t(e),r)}(t)),{left:i,center:function(t,n,r,o){null==r&&(r=0),null==o&&(o=t.length);const a=i(t,n,r,o-1);return a>r&&e(t[a-1],n)>-e(t[a],n)?a-1:a},right:function(t,n,e,i){for(null==e&&(e=0),null==i&&(i=t.length);e>>1;r(t[o],n)>0?i=o:e=o+1}return e}}}function r(t){return null===t?NaN:+t}const i=e(n),o=i.right,a=i.left,u=e(r).center;function c(t,n){let e=0;if(void 0===n)for(let n of t)null!=n&&(n=+n)>=n&&++e;else{let r=-1;for(let i of t)null!=(i=n(i,++r,t))&&(i=+i)>=i&&++e}return e}function f(t){return 0|t.length}function s(t){return!(t>0)}function l(t){return"object"!=typeof t||"length"in t?t:Array.from(t)}function h(t,n){let e,r=0,i=0,o=0;if(void 0===n)for(let n of t)null!=n&&(n=+n)>=n&&(e=n-i,i+=e/++r,o+=e*(n-i));else{let a=-1;for(let u of t)null!=(u=n(u,++a,t))&&(u=+u)>=u&&(e=u-i,i+=e/++r,o+=e*(u-i))}if(r>1)return o/(r-1)}function d(t,n){const e=h(t,n);return e?Math.sqrt(e):e}function p(t,n){let e,r;if(void 0===n)for(const n of t)null!=n&&(void 0===e?n>=n&&(e=r=n):(e>n&&(e=n),r=o&&(e=r=o):(e>o&&(e=o),r0){for(o=t[--i];i>0&&(n=o,e=t[--i],o=n+e,r=e-(o-n),!r););i>0&&(r<0&&t[i-1]<0||r>0&&t[i-1]>0)&&(e=2*r,n=o+e,e==n-o&&(o=n))}return o}}class y extends Map{constructor(t,n=x){if(super(),Object.defineProperties(this,{_intern:{value:new Map},_key:{value:n}}),null!=t)for(const[n,e]of t)this.set(n,e)}get(t){return super.get(_(this,t))}has(t){return super.has(_(this,t))}set(t,n){return super.set(b(this,t),n)}delete(t){return super.delete(m(this,t))}}class v extends Set{constructor(t,n=x){if(super(),Object.defineProperties(this,{_intern:{value:new Map},_key:{value:n}}),null!=t)for(const n of t)this.add(n)}has(t){return super.has(_(this,t))}add(t){return super.add(b(this,t))}delete(t){return super.delete(m(this,t))}}function _({_intern:t,_key:n},e){const r=n(e);return t.has(r)?t.get(r):e}function b({_intern:t,_key:n},e){const r=n(e);return t.has(r)?t.get(r):(t.set(r,e),e)}function m({_intern:t,_key:n},e){const r=n(e);return t.has(r)&&(e=t.get(e),t.delete(r)),e}function x(t){return null!==t&&"object"==typeof t?t.valueOf():t}function w(t){return t}function M(t,...n){return S(t,w,w,n)}function A(t,n,...e){return S(t,w,n,e)}function T(t){if(1!==t.length)throw new Error("duplicate key");return t[0]}function S(t,n,e,r){return function t(i,o){if(o>=r.length)return e(i);const a=new y,u=r[o++];let c=-1;for(const t of i){const n=u(t,++c,i),e=a.get(n);e?e.push(t):a.set(n,[t])}for(const[n,e]of a)a.set(n,t(e,o));return n(a)}(t,0)}function E(t,n){return Array.from(n,(n=>t[n]))}function k(t,...e){if("function"!=typeof t[Symbol.iterator])throw new TypeError("values is not iterable");t=Array.from(t);let[r=n]=e;if(1===r.length||e.length>1){const i=Uint32Array.from(t,((t,n)=>n));return e.length>1?(e=e.map((n=>t.map(n))),i.sort(((t,r)=>{for(const i of e){const e=n(i[t],i[r]);if(e)return e}}))):(r=t.map(r),i.sort(((t,e)=>n(r[t],r[e])))),E(t,i)}return t.sort(r)}var N=Array.prototype.slice;function C(t){return function(){return t}}var P=Math.sqrt(50),z=Math.sqrt(10),D=Math.sqrt(2);function q(t,n,e){var r,i,o,a,u=-1;if(e=+e,(t=+t)===(n=+n)&&e>0)return[t];if((r=n0){let e=Math.round(t/a),r=Math.round(n/a);for(e*an&&--r,o=new Array(i=r-e+1);++un&&--r,o=new Array(i=r-e+1);++u=0?(o>=P?10:o>=z?5:o>=D?2:1)*Math.pow(10,i):-Math.pow(10,-i)/(o>=P?10:o>=z?5:o>=D?2:1)}function F(t,n,e){var r=Math.abs(n-t)/Math.max(0,e),i=Math.pow(10,Math.floor(Math.log(r)/Math.LN10)),o=r/i;return o>=P?i*=10:o>=z?i*=5:o>=D&&(i*=2),n0?(t=Math.floor(t/i)*i,n=Math.ceil(n/i)*i):i<0&&(t=Math.ceil(t*i)/i,n=Math.floor(n*i)/i),r=i}}function I(t){return Math.ceil(Math.log(c(t))/Math.LN2)+1}function U(){var t=w,n=p,e=I;function r(r){Array.isArray(r)||(r=Array.from(r));var i,a,u=r.length,c=new Array(u);for(i=0;i=l)if(t>=l&&n===p){const t=R(s,l,e);isFinite(t)&&(t>0?l=(Math.floor(l/t)+1)*t:t<0&&(l=(Math.ceil(l*-t)+1)/-t))}else h.pop()}for(var d=h.length;h[0]<=s;)h.shift(),--d;for(;h[d-1]>l;)h.pop(),--d;var g,y=new Array(d+1);for(i=0;i<=d;++i)(g=y[i]=[]).x0=i>0?h[i-1]:s,g.x1=i=n)&&(e=n);else{let r=-1;for(let i of t)null!=(i=n(i,++r,t))&&(e=i)&&(e=i)}return e}function Y(t,n){let e;if(void 0===n)for(const n of t)null!=n&&(e>n||void 0===e&&n>=n)&&(e=n);else{let r=-1;for(let i of t)null!=(i=n(i,++r,t))&&(e>i||void 0===e&&i>=i)&&(e=i)}return e}function L(t,e,r=0,i=t.length-1,o=n){for(;i>r;){if(i-r>600){const n=i-r+1,a=e-r+1,u=Math.log(n),c=.5*Math.exp(2*u/3),f=.5*Math.sqrt(u*c*(n-c)/n)*(a-n/2<0?-1:1);L(t,e,Math.max(r,Math.floor(e-a*c/n+f)),Math.min(i,Math.floor(e+(n-a)*c/n+f)),o)}const n=t[e];let a=r,u=i;for(j(t,r,e),o(t[i],n)>0&&j(t,r,i);a0;)--u}0===o(t[r],n)?j(t,r,u):(++u,j(t,u,i)),u<=e&&(r=u+1),e<=u&&(i=u-1)}return t}function j(t,n,e){const r=t[n];t[n]=t[e],t[e]=r}function H(t,n,e){if(r=(t=Float64Array.from(function*(t,n){if(void 0===n)for(let n of t)null!=n&&(n=+n)>=n&&(yield n);else{let e=-1;for(let r of t)null!=(r=n(r,++e,t))&&(r=+r)>=r&&(yield r)}}(t,e))).length){if((n=+n)<=0||r<2)return Y(t);if(n>=1)return B(t);var r,i=(r-1)*n,o=Math.floor(i),a=B(L(t,o).subarray(0,o+1));return a+(Y(t.subarray(o+1))-a)*(i-o)}}function X(t,n,e=r){if(i=t.length){if((n=+n)<=0||i<2)return+e(t[0],0,t);if(n>=1)return+e(t[i-1],i-1,t);var i,o=(i-1)*n,a=Math.floor(o),u=+e(t[a],a,t);return u+(+e(t[a+1],a+1,t)-u)*(o-a)}}function G(t,n){let e,r=-1,i=-1;if(void 0===n)for(const n of t)++i,null!=n&&(e=n)&&(e=n,r=i);else for(let o of t)null!=(o=n(o,++i,t))&&(e=o)&&(e=o,r=i);return r}function V(t){return Array.from(function*(t){for(const n of t)yield*n}(t))}function $(t,n){let e,r=-1,i=-1;if(void 0===n)for(const n of t)++i,null!=n&&(e>n||void 0===e&&n>=n)&&(e=n,r=i);else for(let o of t)null!=(o=n(o,++i,t))&&(e>o||void 0===e&&o>=o)&&(e=o,r=i);return r}function W(t,n){return[t,n]}function Z(t,n,e){t=+t,n=+n,e=(i=arguments.length)<2?(n=t,t=0,1):i<3?1:+e;for(var r=-1,i=0|Math.max(0,Math.ceil((n-t)/e)),o=new Array(i);++r+t(n)}function st(t,n){return n=Math.max(0,t.bandwidth()-2*n)/2,t.round()&&(n=Math.round(n)),e=>+t(e)+n}function lt(){return!this.__axis}function ht(t,n){var e=[],r=null,i=null,o=6,a=6,u=3,c="undefined"!=typeof window&&window.devicePixelRatio>1?0:.5,f=1===t||4===t?-1:1,s=4===t||2===t?"x":"y",l=1===t||3===t?ut:ct;function h(h){var d=null==r?n.ticks?n.ticks.apply(n,e):n.domain():r,p=null==i?n.tickFormat?n.tickFormat.apply(n,e):ot:i,g=Math.max(o,0)+u,y=n.range(),v=+y[0]+c,_=+y[y.length-1]+c,b=(n.bandwidth?st:ft)(n.copy(),c),m=h.selection?h.selection():h,x=m.selectAll(".domain").data([null]),w=m.selectAll(".tick").data(d,n).order(),M=w.exit(),A=w.enter().append("g").attr("class","tick"),T=w.select("line"),S=w.select("text");x=x.merge(x.enter().insert("path",".tick").attr("class","domain").attr("stroke","currentColor")),w=w.merge(A),T=T.merge(A.append("line").attr("stroke","currentColor").attr(s+"2",f*o)),S=S.merge(A.append("text").attr("fill","currentColor").attr(s,f*g).attr("dy",1===t?"0em":3===t?"0.71em":"0.32em")),h!==m&&(x=x.transition(h),w=w.transition(h),T=T.transition(h),S=S.transition(h),M=M.transition(h).attr("opacity",at).attr("transform",(function(t){return isFinite(t=b(t))?l(t+c):this.getAttribute("transform")})),A.attr("opacity",at).attr("transform",(function(t){var n=this.parentNode.__axis;return l((n&&isFinite(n=n(t))?n:b(t))+c)}))),M.remove(),x.attr("d",4===t||2===t?a?"M"+f*a+","+v+"H"+c+"V"+_+"H"+f*a:"M"+c+","+v+"V"+_:a?"M"+v+","+f*a+"V"+c+"H"+_+"V"+f*a:"M"+v+","+c+"H"+_),w.attr("opacity",1).attr("transform",(function(t){return l(b(t)+c)})),T.attr(s+"2",f*o),S.attr(s,f*g).text(p),m.filter(lt).attr("fill","none").attr("font-size",10).attr("font-family","sans-serif").attr("text-anchor",2===t?"start":4===t?"end":"middle"),m.each((function(){this.__axis=b}))}return h.scale=function(t){return arguments.length?(n=t,h):n},h.ticks=function(){return e=it.call(arguments),h},h.tickArguments=function(t){return arguments.length?(e=null==t?[]:it.call(t),h):e.slice()},h.tickValues=function(t){return arguments.length?(r=null==t?null:it.call(t),h):r&&r.slice()},h.tickFormat=function(t){return arguments.length?(i=t,h):i},h.tickSize=function(t){return arguments.length?(o=a=+t,h):o},h.tickSizeInner=function(t){return arguments.length?(o=+t,h):o},h.tickSizeOuter=function(t){return arguments.length?(a=+t,h):a},h.tickPadding=function(t){return arguments.length?(u=+t,h):u},h.offset=function(t){return arguments.length?(c=+t,h):c},h}var dt={value:()=>{}};function pt(){for(var t,n=0,e=arguments.length,r={};n=0&&(e=t.slice(r+1),t=t.slice(0,r)),t&&!n.hasOwnProperty(t))throw new Error("unknown type: "+t);return{type:t,name:e}}))}function vt(t,n){for(var e,r=0,i=t.length;r0)for(var e,r,i=new Array(e),o=0;o=0&&"xmlns"!==(n=t.slice(0,e))&&(t=t.slice(e+1)),mt.hasOwnProperty(n)?{space:mt[n],local:t}:t}function wt(t){return function(){var n=this.ownerDocument,e=this.namespaceURI;return e===bt&&n.documentElement.namespaceURI===bt?n.createElement(t):n.createElementNS(e,t)}}function Mt(t){return function(){return this.ownerDocument.createElementNS(t.space,t.local)}}function At(t){var n=xt(t);return(n.local?Mt:wt)(n)}function Tt(){}function St(t){return null==t?Tt:function(){return this.querySelector(t)}}function Et(t){return"object"==typeof t&&"length"in t?t:Array.from(t)}function kt(){return[]}function Nt(t){return null==t?kt:function(){return this.querySelectorAll(t)}}function Ct(t){return function(){return this.matches(t)}}function Pt(t){return function(n){return n.matches(t)}}var zt=Array.prototype.find;function Dt(){return this.firstElementChild}var qt=Array.prototype.filter;function Rt(){return this.children}function Ft(t){return new Array(t.length)}function Ot(t,n){this.ownerDocument=t.ownerDocument,this.namespaceURI=t.namespaceURI,this._next=null,this._parent=t,this.__data__=n}function It(t){return function(){return t}}function Ut(t,n,e,r,i,o){for(var a,u=0,c=n.length,f=o.length;un?1:t>=n?0:NaN}function jt(t){return function(){this.removeAttribute(t)}}function Ht(t){return function(){this.removeAttributeNS(t.space,t.local)}}function Xt(t,n){return function(){this.setAttribute(t,n)}}function Gt(t,n){return function(){this.setAttributeNS(t.space,t.local,n)}}function Vt(t,n){return function(){var e=n.apply(this,arguments);null==e?this.removeAttribute(t):this.setAttribute(t,e)}}function $t(t,n){return function(){var e=n.apply(this,arguments);null==e?this.removeAttributeNS(t.space,t.local):this.setAttributeNS(t.space,t.local,e)}}function Wt(t){return t.ownerDocument&&t.ownerDocument.defaultView||t.document&&t||t.defaultView}function Zt(t){return function(){this.style.removeProperty(t)}}function Kt(t,n,e){return function(){this.style.setProperty(t,n,e)}}function Qt(t,n,e){return function(){var r=n.apply(this,arguments);null==r?this.style.removeProperty(t):this.style.setProperty(t,r,e)}}function Jt(t,n){return t.style.getPropertyValue(n)||Wt(t).getComputedStyle(t,null).getPropertyValue(n)}function tn(t){return function(){delete this[t]}}function nn(t,n){return function(){this[t]=n}}function en(t,n){return function(){var e=n.apply(this,arguments);null==e?delete this[t]:this[t]=e}}function rn(t){return t.trim().split(/^|\s+/)}function on(t){return t.classList||new an(t)}function an(t){this._node=t,this._names=rn(t.getAttribute("class")||"")}function un(t,n){for(var e=on(t),r=-1,i=n.length;++r=0&&(n=t.slice(e+1),t=t.slice(0,e)),{type:t,name:n}}))}function Tn(t){return function(){var n=this.__on;if(n){for(var e,r=0,i=-1,o=n.length;r=0&&(this._names.splice(n,1),this._node.setAttribute("class",this._names.join(" ")))},contains:function(t){return this._names.indexOf(t)>=0}};var Cn=[null];function Pn(t,n){this._groups=t,this._parents=n}function zn(){return new Pn([[document.documentElement]],Cn)}function Dn(t){return"string"==typeof t?new Pn([[document.querySelector(t)]],[document.documentElement]):new Pn([[t]],Cn)}Pn.prototype=zn.prototype={constructor:Pn,select:function(t){"function"!=typeof t&&(t=St(t));for(var n=this._groups,e=n.length,r=new Array(e),i=0;i=x&&(x=m+1);!(b=y[x])&&++x=0;)(r=i[o])&&(a&&4^r.compareDocumentPosition(a)&&a.parentNode.insertBefore(r,a),a=r);return this},sort:function(t){function n(n,e){return n&&e?t(n.__data__,e.__data__):!n-!e}t||(t=Lt);for(var e=this._groups,r=e.length,i=new Array(r),o=0;o1?this.each((null==n?Zt:"function"==typeof n?Qt:Kt)(t,n,null==e?"":e)):Jt(this.node(),t)},property:function(t,n){return arguments.length>1?this.each((null==n?tn:"function"==typeof n?en:nn)(t,n)):this.node()[t]},classed:function(t,n){var e=rn(t+"");if(arguments.length<2){for(var r=on(this.node()),i=-1,o=e.length;++i()=>t;function Hn(t,{sourceEvent:n,subject:e,target:r,identifier:i,active:o,x:a,y:u,dx:c,dy:f,dispatch:s}){Object.defineProperties(this,{type:{value:t,enumerable:!0,configurable:!0},sourceEvent:{value:n,enumerable:!0,configurable:!0},subject:{value:e,enumerable:!0,configurable:!0},target:{value:r,enumerable:!0,configurable:!0},identifier:{value:i,enumerable:!0,configurable:!0},active:{value:o,enumerable:!0,configurable:!0},x:{value:a,enumerable:!0,configurable:!0},y:{value:u,enumerable:!0,configurable:!0},dx:{value:c,enumerable:!0,configurable:!0},dy:{value:f,enumerable:!0,configurable:!0},_:{value:s}})}function Xn(t){return!t.ctrlKey&&!t.button}function Gn(){return this.parentNode}function Vn(t,n){return null==n?{x:t.x,y:t.y}:n}function $n(){return navigator.maxTouchPoints||"ontouchstart"in this}function Wn(t,n,e){t.prototype=n.prototype=e,e.constructor=t}function Zn(t,n){var e=Object.create(t.prototype);for(var r in n)e[r]=n[r];return e}function Kn(){}Hn.prototype.on=function(){var t=this._.on.apply(this._,arguments);return t===this._?this:t};var Qn=.7,Jn=1/Qn,te="\\s*([+-]?\\d+)\\s*",ne="\\s*([+-]?\\d*\\.?\\d+(?:[eE][+-]?\\d+)?)\\s*",ee="\\s*([+-]?\\d*\\.?\\d+(?:[eE][+-]?\\d+)?)%\\s*",re=/^#([0-9a-f]{3,8})$/,ie=new RegExp("^rgb\\("+[te,te,te]+"\\)$"),oe=new RegExp("^rgb\\("+[ee,ee,ee]+"\\)$"),ae=new RegExp("^rgba\\("+[te,te,te,ne]+"\\)$"),ue=new RegExp("^rgba\\("+[ee,ee,ee,ne]+"\\)$"),ce=new RegExp("^hsl\\("+[ne,ee,ee]+"\\)$"),fe=new RegExp("^hsla\\("+[ne,ee,ee,ne]+"\\)$"),se={aliceblue:15792383,antiquewhite:16444375,aqua:65535,aquamarine:8388564,azure:15794175,beige:16119260,bisque:16770244,black:0,blanchedalmond:16772045,blue:255,blueviolet:9055202,brown:10824234,burlywood:14596231,cadetblue:6266528,chartreuse:8388352,chocolate:13789470,coral:16744272,cornflowerblue:6591981,cornsilk:16775388,crimson:14423100,cyan:65535,darkblue:139,darkcyan:35723,darkgoldenrod:12092939,darkgray:11119017,darkgreen:25600,darkgrey:11119017,darkkhaki:12433259,darkmagenta:9109643,darkolivegreen:5597999,darkorange:16747520,darkorchid:10040012,darkred:9109504,darksalmon:15308410,darkseagreen:9419919,darkslateblue:4734347,darkslategray:3100495,darkslategrey:3100495,darkturquoise:52945,darkviolet:9699539,deeppink:16716947,deepskyblue:49151,dimgray:6908265,dimgrey:6908265,dodgerblue:2003199,firebrick:11674146,floralwhite:16775920,forestgreen:2263842,fuchsia:16711935,gainsboro:14474460,ghostwhite:16316671,gold:16766720,goldenrod:14329120,gray:8421504,green:32768,greenyellow:11403055,grey:8421504,honeydew:15794160,hotpink:16738740,indianred:13458524,indigo:4915330,ivory:16777200,khaki:15787660,lavender:15132410,lavenderblush:16773365,lawngreen:8190976,lemonchiffon:16775885,lightblue:11393254,lightcoral:15761536,lightcyan:14745599,lightgoldenrodyellow:16448210,lightgray:13882323,lightgreen:9498256,lightgrey:13882323,lightpink:16758465,lightsalmon:16752762,lightseagreen:2142890,lightskyblue:8900346,lightslategray:7833753,lightslategrey:7833753,lightsteelblue:11584734,lightyellow:16777184,lime:65280,limegreen:3329330,linen:16445670,magenta:16711935,maroon:8388608,mediumaquamarine:6737322,mediumblue:205,mediumorchid:12211667,mediumpurple:9662683,mediumseagreen:3978097,mediumslateblue:8087790,mediumspringgreen:64154,mediumturquoise:4772300,mediumvioletred:13047173,midnightblue:1644912,mintcream:16121850,mistyrose:16770273,moccasin:16770229,navajowhite:16768685,navy:128,oldlace:16643558,olive:8421376,olivedrab:7048739,orange:16753920,orangered:16729344,orchid:14315734,palegoldenrod:15657130,palegreen:10025880,paleturquoise:11529966,palevioletred:14381203,papayawhip:16773077,peachpuff:16767673,peru:13468991,pink:16761035,plum:14524637,powderblue:11591910,purple:8388736,rebeccapurple:6697881,red:16711680,rosybrown:12357519,royalblue:4286945,saddlebrown:9127187,salmon:16416882,sandybrown:16032864,seagreen:3050327,seashell:16774638,sienna:10506797,silver:12632256,skyblue:8900331,slateblue:6970061,slategray:7372944,slategrey:7372944,snow:16775930,springgreen:65407,steelblue:4620980,tan:13808780,teal:32896,thistle:14204888,tomato:16737095,turquoise:4251856,violet:15631086,wheat:16113331,white:16777215,whitesmoke:16119285,yellow:16776960,yellowgreen:10145074};function le(){return this.rgb().formatHex()}function he(){return this.rgb().formatRgb()}function de(t){var n,e;return t=(t+"").trim().toLowerCase(),(n=re.exec(t))?(e=n[1].length,n=parseInt(n[1],16),6===e?pe(n):3===e?new _e(n>>8&15|n>>4&240,n>>4&15|240&n,(15&n)<<4|15&n,1):8===e?ge(n>>24&255,n>>16&255,n>>8&255,(255&n)/255):4===e?ge(n>>12&15|n>>8&240,n>>8&15|n>>4&240,n>>4&15|240&n,((15&n)<<4|15&n)/255):null):(n=ie.exec(t))?new _e(n[1],n[2],n[3],1):(n=oe.exec(t))?new _e(255*n[1]/100,255*n[2]/100,255*n[3]/100,1):(n=ae.exec(t))?ge(n[1],n[2],n[3],n[4]):(n=ue.exec(t))?ge(255*n[1]/100,255*n[2]/100,255*n[3]/100,n[4]):(n=ce.exec(t))?we(n[1],n[2]/100,n[3]/100,1):(n=fe.exec(t))?we(n[1],n[2]/100,n[3]/100,n[4]):se.hasOwnProperty(t)?pe(se[t]):"transparent"===t?new _e(NaN,NaN,NaN,0):null}function pe(t){return new _e(t>>16&255,t>>8&255,255&t,1)}function ge(t,n,e,r){return r<=0&&(t=n=e=NaN),new _e(t,n,e,r)}function ye(t){return t instanceof Kn||(t=de(t)),t?new _e((t=t.rgb()).r,t.g,t.b,t.opacity):new _e}function ve(t,n,e,r){return 1===arguments.length?ye(t):new _e(t,n,e,null==r?1:r)}function _e(t,n,e,r){this.r=+t,this.g=+n,this.b=+e,this.opacity=+r}function be(){return"#"+xe(this.r)+xe(this.g)+xe(this.b)}function me(){var t=this.opacity;return(1===(t=isNaN(t)?1:Math.max(0,Math.min(1,t)))?"rgb(":"rgba(")+Math.max(0,Math.min(255,Math.round(this.r)||0))+", "+Math.max(0,Math.min(255,Math.round(this.g)||0))+", "+Math.max(0,Math.min(255,Math.round(this.b)||0))+(1===t?")":", "+t+")")}function xe(t){return((t=Math.max(0,Math.min(255,Math.round(t)||0)))<16?"0":"")+t.toString(16)}function we(t,n,e,r){return r<=0?t=n=e=NaN:e<=0||e>=1?t=n=NaN:n<=0&&(t=NaN),new Te(t,n,e,r)}function Me(t){if(t instanceof Te)return new Te(t.h,t.s,t.l,t.opacity);if(t instanceof Kn||(t=de(t)),!t)return new Te;if(t instanceof Te)return t;var n=(t=t.rgb()).r/255,e=t.g/255,r=t.b/255,i=Math.min(n,e,r),o=Math.max(n,e,r),a=NaN,u=o-i,c=(o+i)/2;return u?(a=n===o?(e-r)/u+6*(e0&&c<1?0:a,new Te(a,u,c,t.opacity)}function Ae(t,n,e,r){return 1===arguments.length?Me(t):new Te(t,n,e,null==r?1:r)}function Te(t,n,e,r){this.h=+t,this.s=+n,this.l=+e,this.opacity=+r}function Se(t,n,e){return 255*(t<60?n+(e-n)*t/60:t<180?e:t<240?n+(e-n)*(240-t)/60:n)}Wn(Kn,de,{copy:function(t){return Object.assign(new this.constructor,this,t)},displayable:function(){return this.rgb().displayable()},hex:le,formatHex:le,formatHsl:function(){return Me(this).formatHsl()},formatRgb:he,toString:he}),Wn(_e,ve,Zn(Kn,{brighter:function(t){return t=null==t?Jn:Math.pow(Jn,t),new _e(this.r*t,this.g*t,this.b*t,this.opacity)},darker:function(t){return t=null==t?Qn:Math.pow(Qn,t),new _e(this.r*t,this.g*t,this.b*t,this.opacity)},rgb:function(){return this},displayable:function(){return-.5<=this.r&&this.r<255.5&&-.5<=this.g&&this.g<255.5&&-.5<=this.b&&this.b<255.5&&0<=this.opacity&&this.opacity<=1},hex:be,formatHex:be,formatRgb:me,toString:me})),Wn(Te,Ae,Zn(Kn,{brighter:function(t){return t=null==t?Jn:Math.pow(Jn,t),new Te(this.h,this.s,this.l*t,this.opacity)},darker:function(t){return t=null==t?Qn:Math.pow(Qn,t),new Te(this.h,this.s,this.l*t,this.opacity)},rgb:function(){var t=this.h%360+360*(this.h<0),n=isNaN(t)||isNaN(this.s)?0:this.s,e=this.l,r=e+(e<.5?e:1-e)*n,i=2*e-r;return new _e(Se(t>=240?t-240:t+120,i,r),Se(t,i,r),Se(t<120?t+240:t-120,i,r),this.opacity)},displayable:function(){return(0<=this.s&&this.s<=1||isNaN(this.s))&&0<=this.l&&this.l<=1&&0<=this.opacity&&this.opacity<=1},formatHsl:function(){var t=this.opacity;return(1===(t=isNaN(t)?1:Math.max(0,Math.min(1,t)))?"hsl(":"hsla(")+(this.h||0)+", "+100*(this.s||0)+"%, "+100*(this.l||0)+"%"+(1===t?")":", "+t+")")}}));const Ee=Math.PI/180,ke=180/Math.PI,Ne=.96422,Ce=.82521,Pe=4/29,ze=6/29,De=3*ze*ze;function qe(t){if(t instanceof Fe)return new Fe(t.l,t.a,t.b,t.opacity);if(t instanceof je)return He(t);t instanceof _e||(t=ye(t));var n,e,r=Be(t.r),i=Be(t.g),o=Be(t.b),a=Oe((.2225045*r+.7168786*i+.0606169*o)/1);return r===i&&i===o?n=e=a:(n=Oe((.4360747*r+.3850649*i+.1430804*o)/Ne),e=Oe((.0139322*r+.0971045*i+.7141733*o)/Ce)),new Fe(116*a-16,500*(n-a),200*(a-e),t.opacity)}function Re(t,n,e,r){return 1===arguments.length?qe(t):new Fe(t,n,e,null==r?1:r)}function Fe(t,n,e,r){this.l=+t,this.a=+n,this.b=+e,this.opacity=+r}function Oe(t){return t>.008856451679035631?Math.pow(t,1/3):t/De+Pe}function Ie(t){return t>ze?t*t*t:De*(t-Pe)}function Ue(t){return 255*(t<=.0031308?12.92*t:1.055*Math.pow(t,1/2.4)-.055)}function Be(t){return(t/=255)<=.04045?t/12.92:Math.pow((t+.055)/1.055,2.4)}function Ye(t){if(t instanceof je)return new je(t.h,t.c,t.l,t.opacity);if(t instanceof Fe||(t=qe(t)),0===t.a&&0===t.b)return new je(NaN,0=1?(e=1,n-1):Math.floor(e*n),i=t[r],o=t[r+1],a=r>0?t[r-1]:2*i-o,u=r()=>t;function ar(t,n){return function(e){return t+e*n}}function ur(t,n){var e=n-t;return e?ar(t,e>180||e<-180?e-360*Math.round(e/360):e):or(isNaN(t)?n:t)}function cr(t){return 1==(t=+t)?fr:function(n,e){return e-n?function(t,n,e){return t=Math.pow(t,e),n=Math.pow(n,e)-t,e=1/e,function(r){return Math.pow(t+r*n,e)}}(n,e,t):or(isNaN(n)?e:n)}}function fr(t,n){var e=n-t;return e?ar(t,e):or(isNaN(t)?n:t)}var sr=function t(n){var e=cr(n);function r(t,n){var r=e((t=ve(t)).r,(n=ve(n)).r),i=e(t.g,n.g),o=e(t.b,n.b),a=fr(t.opacity,n.opacity);return function(n){return t.r=r(n),t.g=i(n),t.b=o(n),t.opacity=a(n),t+""}}return r.gamma=t,r}(1);function lr(t){return function(n){var e,r,i=n.length,o=new Array(i),a=new Array(i),u=new Array(i);for(e=0;eo&&(i=n.slice(o,i),u[a]?u[a]+=i:u[++a]=i),(e=e[0])===(r=r[0])?u[a]?u[a]+=r:u[++a]=r:(u[++a]=null,c.push({i:a,x:_r(e,r)})),o=xr.lastIndex;return o180?n+=360:n-t>180&&(t+=360),o.push({i:e.push(i(e)+"rotate(",null,r)-2,x:_r(t,n)})):n&&e.push(i(e)+"rotate("+n+r)}(o.rotate,a.rotate,u,c),function(t,n,e,o){t!==n?o.push({i:e.push(i(e)+"skewX(",null,r)-2,x:_r(t,n)}):n&&e.push(i(e)+"skewX("+n+r)}(o.skewX,a.skewX,u,c),function(t,n,e,r,o,a){if(t!==e||n!==r){var u=o.push(i(o)+"scale(",null,",",null,")");a.push({i:u-4,x:_r(t,e)},{i:u-2,x:_r(n,r)})}else 1===e&&1===r||o.push(i(o)+"scale("+e+","+r+")")}(o.scaleX,o.scaleY,a.scaleX,a.scaleY,u,c),o=a=null,function(t){for(var n,e=-1,r=c.length;++e=0&&n._call.call(null,t),n=n._next;--Gr}function oi(){Zr=(Wr=Qr.now())+Kr,Gr=Vr=0;try{ii()}finally{Gr=0,function(){var t,n,e=Hr,r=1/0;for(;e;)e._call?(r>e._time&&(r=e._time),t=e,e=e._next):(n=e._next,e._next=null,e=t?t._next=n:Hr=n);Xr=t,ui(r)}(),Zr=0}}function ai(){var t=Qr.now(),n=t-Wr;n>1e3&&(Kr-=n,Wr=t)}function ui(t){Gr||(Vr&&(Vr=clearTimeout(Vr)),t-Zr>24?(t<1/0&&(Vr=setTimeout(oi,t-Qr.now()-Kr)),$r&&($r=clearInterval($r))):($r||(Wr=Qr.now(),$r=setInterval(ai,1e3)),Gr=1,Jr(oi)))}function ci(t,n,e){var r=new ei;return n=null==n?0:+n,r.restart((e=>{r.stop(),t(e+n)}),n,e),r}ei.prototype=ri.prototype={constructor:ei,restart:function(t,n,e){if("function"!=typeof t)throw new TypeError("callback is not a function");e=(null==e?ti():+e)+(null==n?0:+n),this._next||Xr===this||(Xr?Xr._next=this:Hr=this,Xr=this),this._call=t,this._time=e,ui()},stop:function(){this._call&&(this._call=null,this._time=1/0,ui())}};var fi=pt("start","end","cancel","interrupt"),si=[];function li(t,n,e,r,i,o){var a=t.__transition;if(a){if(e in a)return}else t.__transition={};!function(t,n,e){var r,i=t.__transition;function o(t){e.state=1,e.timer.restart(a,e.delay,e.time),e.delay<=t&&a(t-e.delay)}function a(o){var f,s,l,h;if(1!==e.state)return c();for(f in i)if((h=i[f]).name===e.name){if(3===h.state)return ci(a);4===h.state?(h.state=6,h.timer.stop(),h.on.call("interrupt",t,t.__data__,h.index,h.group),delete i[f]):+f0)throw new Error("too late; already scheduled");return e}function di(t,n){var e=pi(t,n);if(e.state>3)throw new Error("too late; already running");return e}function pi(t,n){var e=t.__transition;if(!e||!(e=e[n]))throw new Error("transition not found");return e}function gi(t,n){var e,r,i,o=t.__transition,a=!0;if(o){for(i in n=null==n?null:n+"",o)(e=o[i]).name===n?(r=e.state>2&&e.state<5,e.state=6,e.timer.stop(),e.on.call(r?"interrupt":"cancel",t,t.__data__,e.index,e.group),delete o[i]):a=!1;a&&delete t.__transition}}function yi(t,n){var e,r;return function(){var i=di(this,t),o=i.tween;if(o!==e)for(var a=0,u=(r=e=o).length;a=0&&(t=t.slice(0,n)),!t||"start"===t}))}(n)?hi:di;return function(){var a=o(this,t),u=a.on;u!==r&&(i=(r=u).copy()).on(n,e),a.on=i}}var Fi=zn.prototype.constructor;function Oi(t){return function(){this.style.removeProperty(t)}}function Ii(t,n,e){return function(r){this.style.setProperty(t,n.call(this,r),e)}}function Ui(t,n,e){var r,i;function o(){var o=n.apply(this,arguments);return o!==i&&(r=(i=o)&&Ii(t,o,e)),r}return o._value=n,o}function Bi(t){return function(n){this.textContent=t.call(this,n)}}function Yi(t){var n,e;function r(){var r=t.apply(this,arguments);return r!==e&&(n=(e=r)&&Bi(r)),n}return r._value=t,r}var Li=0;function ji(t,n,e,r){this._groups=t,this._parents=n,this._name=e,this._id=r}function Hi(t){return zn().transition(t)}function Xi(){return++Li}var Gi=zn.prototype;ji.prototype=Hi.prototype={constructor:ji,select:function(t){var n=this._name,e=this._id;"function"!=typeof t&&(t=St(t));for(var r=this._groups,i=r.length,o=new Array(i),a=0;a()=>t;function mo(t,{sourceEvent:n,target:e,selection:r,mode:i,dispatch:o}){Object.defineProperties(this,{type:{value:t,enumerable:!0,configurable:!0},sourceEvent:{value:n,enumerable:!0,configurable:!0},target:{value:e,enumerable:!0,configurable:!0},selection:{value:r,enumerable:!0,configurable:!0},mode:{value:i,enumerable:!0,configurable:!0},_:{value:o}})}function xo(t){t.stopImmediatePropagation()}function wo(t){t.preventDefault(),t.stopImmediatePropagation()}var Mo={name:"drag"},Ao={name:"space"},To={name:"handle"},So={name:"center"};const{abs:Eo,max:ko,min:No}=Math;function Co(t){return[+t[0],+t[1]]}function Po(t){return[Co(t[0]),Co(t[1])]}var zo={name:"x",handles:["w","e"].map(Bo),input:function(t,n){return null==t?null:[[+t[0],n[0][1]],[+t[1],n[1][1]]]},output:function(t){return t&&[t[0][0],t[1][0]]}},Do={name:"y",handles:["n","s"].map(Bo),input:function(t,n){return null==t?null:[[n[0][0],+t[0]],[n[1][0],+t[1]]]},output:function(t){return t&&[t[0][1],t[1][1]]}},qo={name:"xy",handles:["n","w","e","s","nw","ne","sw","se"].map(Bo),input:function(t){return null==t?null:Po(t)},output:function(t){return t}},Ro={overlay:"crosshair",selection:"move",n:"ns-resize",e:"ew-resize",s:"ns-resize",w:"ew-resize",nw:"nwse-resize",ne:"nesw-resize",se:"nwse-resize",sw:"nesw-resize"},Fo={e:"w",w:"e",nw:"ne",ne:"nw",se:"sw",sw:"se"},Oo={n:"s",s:"n",nw:"sw",ne:"se",se:"ne",sw:"nw"},Io={overlay:1,selection:1,n:null,e:1,s:null,w:-1,nw:-1,ne:1,se:1,sw:-1},Uo={overlay:1,selection:1,n:-1,e:null,s:1,w:null,nw:-1,ne:-1,se:1,sw:1};function Bo(t){return{type:t}}function Yo(t){return!t.ctrlKey&&!t.button}function Lo(){var t=this.ownerSVGElement||this;return t.hasAttribute("viewBox")?[[(t=t.viewBox.baseVal).x,t.y],[t.x+t.width,t.y+t.height]]:[[0,0],[t.width.baseVal.value,t.height.baseVal.value]]}function jo(){return navigator.maxTouchPoints||"ontouchstart"in this}function Ho(t){for(;!t.__brush;)if(!(t=t.parentNode))return;return t.__brush}function Xo(t){return t[0][0]===t[1][0]||t[0][1]===t[1][1]}function Go(t){var n,e=Lo,r=Yo,i=jo,o=!0,a=pt("start","brush","end"),u=6;function c(n){var e=n.property("__brush",g).selectAll(".overlay").data([Bo("overlay")]);e.enter().append("rect").attr("class","overlay").attr("pointer-events","all").attr("cursor",Ro.overlay).merge(e).each((function(){var t=Ho(this).extent;Dn(this).attr("x",t[0][0]).attr("y",t[0][1]).attr("width",t[1][0]-t[0][0]).attr("height",t[1][1]-t[0][1])})),n.selectAll(".selection").data([Bo("selection")]).enter().append("rect").attr("class","selection").attr("cursor",Ro.selection).attr("fill","#777").attr("fill-opacity",.3).attr("stroke","#fff").attr("shape-rendering","crispEdges");var r=n.selectAll(".handle").data(t.handles,(function(t){return t.type}));r.exit().remove(),r.enter().append("rect").attr("class",(function(t){return"handle handle--"+t.type})).attr("cursor",(function(t){return Ro[t.type]})),n.each(f).attr("fill","none").attr("pointer-events","all").on("mousedown.brush",h).filter(i).on("touchstart.brush",h).on("touchmove.brush",d).on("touchend.brush touchcancel.brush",p).style("touch-action","none").style("-webkit-tap-highlight-color","rgba(0,0,0,0)")}function f(){var t=Dn(this),n=Ho(this).selection;n?(t.selectAll(".selection").style("display",null).attr("x",n[0][0]).attr("y",n[0][1]).attr("width",n[1][0]-n[0][0]).attr("height",n[1][1]-n[0][1]),t.selectAll(".handle").style("display",null).attr("x",(function(t){return"e"===t.type[t.type.length-1]?n[1][0]-u/2:n[0][0]-u/2})).attr("y",(function(t){return"s"===t.type[0]?n[1][1]-u/2:n[0][1]-u/2})).attr("width",(function(t){return"n"===t.type||"s"===t.type?n[1][0]-n[0][0]+u:u})).attr("height",(function(t){return"e"===t.type||"w"===t.type?n[1][1]-n[0][1]+u:u}))):t.selectAll(".selection,.handle").style("display","none").attr("x",null).attr("y",null).attr("width",null).attr("height",null)}function s(t,n,e){var r=t.__brush.emitter;return!r||e&&r.clean?new l(t,n,e):r}function l(t,n,e){this.that=t,this.args=n,this.state=t.__brush,this.active=0,this.clean=e}function h(e){if((!n||e.touches)&&r.apply(this,arguments)){var i,a,u,c,l,h,d,p,g,y,v,_=this,b=e.target.__data__.type,m="selection"===(o&&e.metaKey?b="overlay":b)?Mo:o&&e.altKey?So:To,x=t===Do?null:Io[b],w=t===zo?null:Uo[b],M=Ho(_),A=M.extent,T=M.selection,S=A[0][0],E=A[0][1],k=A[1][0],N=A[1][1],C=0,P=0,z=x&&w&&o&&e.shiftKey,D=Array.from(e.touches||[e],(t=>{const n=t.identifier;return(t=In(t,_)).point0=t.slice(),t.identifier=n,t}));if("overlay"===b){T&&(g=!0);const n=[D[0],D[1]||D[0]];M.selection=T=[[i=t===Do?S:No(n[0][0],n[1][0]),u=t===zo?E:No(n[0][1],n[1][1])],[l=t===Do?k:ko(n[0][0],n[1][0]),d=t===zo?N:ko(n[0][1],n[1][1])]],D.length>1&&U()}else i=T[0][0],u=T[0][1],l=T[1][0],d=T[1][1];a=i,c=u,h=l,p=d;var q=Dn(_).attr("pointer-events","none"),R=q.selectAll(".overlay").attr("cursor",Ro[b]);gi(_);var F=s(_,arguments,!0).beforestart();if(e.touches)F.moved=I,F.ended=B;else{var O=Dn(e.view).on("mousemove.brush",I,!0).on("mouseup.brush",B,!0);o&&O.on("keydown.brush",Y,!0).on("keyup.brush",L,!0),Yn(e.view)}f.call(_),F.start(e,m.name)}function I(t){for(const n of t.changedTouches||[t])for(const t of D)t.identifier===n.identifier&&(t.cur=In(n,_));if(z&&!y&&!v&&1===D.length){const t=D[0];Eo(t.cur[0]-t[0])>Eo(t.cur[1]-t[1])?v=!0:y=!0}for(const t of D)t.cur&&(t[0]=t.cur[0],t[1]=t.cur[1]);g=!0,wo(t),U(t)}function U(t){const n=D[0],e=n.point0;var r;switch(C=n[0]-e[0],P=n[1]-e[1],m){case Ao:case Mo:x&&(C=ko(S-i,No(k-l,C)),a=i+C,h=l+C),w&&(P=ko(E-u,No(N-d,P)),c=u+P,p=d+P);break;case To:D[1]?(x&&(a=ko(S,No(k,D[0][0])),h=ko(S,No(k,D[1][0])),x=1),w&&(c=ko(E,No(N,D[0][1])),p=ko(E,No(N,D[1][1])),w=1)):(x<0?(C=ko(S-i,No(k-i,C)),a=i+C,h=l):x>0&&(C=ko(S-l,No(k-l,C)),a=i,h=l+C),w<0?(P=ko(E-u,No(N-u,P)),c=u+P,p=d):w>0&&(P=ko(E-d,No(N-d,P)),c=u,p=d+P));break;case So:x&&(a=ko(S,No(k,i-C*x)),h=ko(S,No(k,l+C*x))),w&&(c=ko(E,No(N,u-P*w)),p=ko(E,No(N,d+P*w)))}h0&&(i=a-C),w<0?d=p-P:w>0&&(u=c-P),m=Ao,R.attr("cursor",Ro.selection),U());break;default:return}wo(t)}function L(t){switch(t.keyCode){case 16:z&&(y=v=z=!1,U());break;case 18:m===So&&(x<0?l=h:x>0&&(i=a),w<0?d=p:w>0&&(u=c),m=To,U());break;case 32:m===Ao&&(t.altKey?(x&&(l=h-C*x,i=a+C*x),w&&(d=p-P*w,u=c+P*w),m=So):(x<0?l=h:x>0&&(i=a),w<0?d=p:w>0&&(u=c),m=To),R.attr("cursor",Ro[b]),U());break;default:return}wo(t)}}function d(t){s(this,arguments).moved(t)}function p(t){s(this,arguments).ended(t)}function g(){var n=this.__brush||{selection:null};return n.extent=Po(e.apply(this,arguments)),n.dim=t,n}return c.move=function(n,e){n.tween?n.on("start.brush",(function(t){s(this,arguments).beforestart().start(t)})).on("interrupt.brush end.brush",(function(t){s(this,arguments).end(t)})).tween("brush",(function(){var n=this,r=n.__brush,i=s(n,arguments),o=r.selection,a=t.input("function"==typeof e?e.apply(this,arguments):e,r.extent),u=Mr(o,a);function c(t){r.selection=1===t&&null===a?null:u(t),f.call(n),i.brush()}return null!==o&&null!==a?c:c(1)})):n.each((function(){var n=this,r=arguments,i=n.__brush,o=t.input("function"==typeof e?e.apply(n,r):e,i.extent),a=s(n,r).beforestart();gi(n),i.selection=null===o?null:o,f.call(n),a.start().brush().end()}))},c.clear=function(t){c.move(t,null)},l.prototype={beforestart:function(){return 1==++this.active&&(this.state.emitter=this,this.starting=!0),this},start:function(t,n){return this.starting?(this.starting=!1,this.emit("start",t,n)):this.emit("brush",t),this},brush:function(t,n){return this.emit("brush",t,n),this},end:function(t,n){return 0==--this.active&&(delete this.state.emitter,this.emit("end",t,n)),this},emit:function(n,e,r){var i=Dn(this.that).datum();a.call(n,this.that,new mo(n,{sourceEvent:e,target:c,selection:t.output(this.state.selection),mode:r,dispatch:a}),i)}},c.extent=function(t){return arguments.length?(e="function"==typeof t?t:bo(Po(t)),c):e},c.filter=function(t){return arguments.length?(r="function"==typeof t?t:bo(!!t),c):r},c.touchable=function(t){return arguments.length?(i="function"==typeof t?t:bo(!!t),c):i},c.handleSize=function(t){return arguments.length?(u=+t,c):u},c.keyModifiers=function(t){return arguments.length?(o=!!t,c):o},c.on=function(){var t=a.on.apply(a,arguments);return t===a?c:t},c}var Vo=Math.abs,$o=Math.cos,Wo=Math.sin,Zo=Math.PI,Ko=Zo/2,Qo=2*Zo,Jo=Math.max,ta=1e-12;function na(t,n){return Array.from({length:n-t},((n,e)=>t+e))}function ea(t){return function(n,e){return t(n.source.value+n.target.value,e.source.value+e.target.value)}}function ra(t,n){var e=0,r=null,i=null,o=null;function a(a){var u,c=a.length,f=new Array(c),s=na(0,c),l=new Array(c*c),h=new Array(c),d=0;a=Float64Array.from({length:c*c},n?(t,n)=>a[n%c][n/c|0]:(t,n)=>a[n/c|0][n%c]);for(let n=0;nr(f[t],f[n])));for(const e of s){const r=n;if(t){const t=na(1+~c,c).filter((t=>t<0?a[~t*c+e]:a[e*c+t]));i&&t.sort(((t,n)=>i(t<0?-a[~t*c+e]:a[e*c+t],n<0?-a[~n*c+e]:a[e*c+n])));for(const r of t)if(r<0){(l[~r*c+e]||(l[~r*c+e]={source:null,target:null})).target={index:e,startAngle:n,endAngle:n+=a[~r*c+e]*d,value:a[~r*c+e]}}else{(l[e*c+r]||(l[e*c+r]={source:null,target:null})).source={index:e,startAngle:n,endAngle:n+=a[e*c+r]*d,value:a[e*c+r]}}h[e]={index:e,startAngle:r,endAngle:n,value:f[e]}}else{const t=na(0,c).filter((t=>a[e*c+t]||a[t*c+e]));i&&t.sort(((t,n)=>i(a[e*c+t],a[e*c+n])));for(const r of t){let t;if(eaa)if(Math.abs(s*u-c*f)>aa&&i){var h=e-o,d=r-a,p=u*u+c*c,g=h*h+d*d,y=Math.sqrt(p),v=Math.sqrt(l),_=i*Math.tan((ia-Math.acos((p+l-g)/(2*y*v)))/2),b=_/v,m=_/y;Math.abs(b-1)>aa&&(this._+="L"+(t+b*f)+","+(n+b*s)),this._+="A"+i+","+i+",0,0,"+ +(s*h>f*d)+","+(this._x1=t+m*u)+","+(this._y1=n+m*c)}else this._+="L"+(this._x1=t)+","+(this._y1=n);else;},arc:function(t,n,e,r,i,o){t=+t,n=+n,o=!!o;var a=(e=+e)*Math.cos(r),u=e*Math.sin(r),c=t+a,f=n+u,s=1^o,l=o?r-i:i-r;if(e<0)throw new Error("negative radius: "+e);null===this._x1?this._+="M"+c+","+f:(Math.abs(this._x1-c)>aa||Math.abs(this._y1-f)>aa)&&(this._+="L"+c+","+f),e&&(l<0&&(l=l%oa+oa),l>ua?this._+="A"+e+","+e+",0,1,"+s+","+(t-a)+","+(n-u)+"A"+e+","+e+",0,1,"+s+","+(this._x1=c)+","+(this._y1=f):l>aa&&(this._+="A"+e+","+e+",0,"+ +(l>=ia)+","+s+","+(this._x1=t+e*Math.cos(i))+","+(this._y1=n+e*Math.sin(i))))},rect:function(t,n,e,r){this._+="M"+(this._x0=this._x1=+t)+","+(this._y0=this._y1=+n)+"h"+ +e+"v"+ +r+"h"+-e+"Z"},toString:function(){return this._}};var sa=Array.prototype.slice;function la(t){return function(){return t}}function ha(t){return t.source}function da(t){return t.target}function pa(t){return t.radius}function ga(t){return t.startAngle}function ya(t){return t.endAngle}function va(){return 0}function _a(){return 10}function ba(t){var n=ha,e=da,r=pa,i=pa,o=ga,a=ya,u=va,c=null;function f(){var f,s=n.apply(this,arguments),l=e.apply(this,arguments),h=u.apply(this,arguments)/2,d=sa.call(arguments),p=+r.apply(this,(d[0]=s,d)),g=o.apply(this,d)-Ko,y=a.apply(this,d)-Ko,v=+i.apply(this,(d[0]=l,d)),_=o.apply(this,d)-Ko,b=a.apply(this,d)-Ko;if(c||(c=f=fa()),h>ta&&(Vo(y-g)>2*h+ta?y>g?(g+=h,y-=h):(g-=h,y+=h):g=y=(g+y)/2,Vo(b-_)>2*h+ta?b>_?(_+=h,b-=h):(_-=h,b+=h):_=b=(_+b)/2),c.moveTo(p*$o(g),p*Wo(g)),c.arc(0,0,p,g,y),g!==_||y!==b)if(t){var m=+t.apply(this,arguments),x=v-m,w=(_+b)/2;c.quadraticCurveTo(0,0,x*$o(_),x*Wo(_)),c.lineTo(v*$o(w),v*Wo(w)),c.lineTo(x*$o(b),x*Wo(b))}else c.quadraticCurveTo(0,0,v*$o(_),v*Wo(_)),c.arc(0,0,v,_,b);if(c.quadraticCurveTo(0,0,p*$o(g),p*Wo(g)),c.closePath(),f)return c=null,f+""||null}return t&&(f.headRadius=function(n){return arguments.length?(t="function"==typeof n?n:la(+n),f):t}),f.radius=function(t){return arguments.length?(r=i="function"==typeof t?t:la(+t),f):r},f.sourceRadius=function(t){return arguments.length?(r="function"==typeof t?t:la(+t),f):r},f.targetRadius=function(t){return arguments.length?(i="function"==typeof t?t:la(+t),f):i},f.startAngle=function(t){return arguments.length?(o="function"==typeof t?t:la(+t),f):o},f.endAngle=function(t){return arguments.length?(a="function"==typeof t?t:la(+t),f):a},f.padAngle=function(t){return arguments.length?(u="function"==typeof t?t:la(+t),f):u},f.source=function(t){return arguments.length?(n=t,f):n},f.target=function(t){return arguments.length?(e=t,f):e},f.context=function(t){return arguments.length?(c=null==t?null:t,f):c},f}var ma=Array.prototype.slice;function xa(t,n){return t-n}var wa=t=>()=>t;function Ma(t,n){for(var e,r=-1,i=n.length;++rr!=d>r&&e<(h-f)*(r-s)/(d-s)+f&&(i=-i)}return i}function Ta(t,n,e){var r,i,o,a;return function(t,n,e){return(n[0]-t[0])*(e[1]-t[1])==(e[0]-t[0])*(n[1]-t[1])}(t,n,e)&&(i=t[r=+(t[0]===n[0])],o=e[r],a=n[r],i<=o&&o<=a||a<=o&&o<=i)}function Sa(){}var Ea=[[],[[[1,1.5],[.5,1]]],[[[1.5,1],[1,1.5]]],[[[1.5,1],[.5,1]]],[[[1,.5],[1.5,1]]],[[[1,1.5],[.5,1]],[[1,.5],[1.5,1]]],[[[1,.5],[1,1.5]]],[[[1,.5],[.5,1]]],[[[.5,1],[1,.5]]],[[[1,1.5],[1,.5]]],[[[.5,1],[1,.5]],[[1.5,1],[1,1.5]]],[[[1.5,1],[1,.5]]],[[[.5,1],[1.5,1]]],[[[1,1.5],[1.5,1]]],[[[.5,1],[1,1.5]]],[]];function ka(){var t=1,n=1,e=I,r=u;function i(t){var n=e(t);if(Array.isArray(n))n=n.slice().sort(xa);else{var r=p(t),i=r[0],a=r[1];n=F(i,a,n),n=Z(Math.floor(i/n)*n,Math.floor(a/n)*n,n)}return n.map((function(n){return o(t,n)}))}function o(e,i){var o=[],u=[];return function(e,r,i){var o,u,c,f,s,l,h=new Array,d=new Array;o=u=-1,f=e[0]>=r,Ea[f<<1].forEach(p);for(;++o=r,Ea[c|f<<1].forEach(p);Ea[f<<0].forEach(p);for(;++u=r,s=e[u*t]>=r,Ea[f<<1|s<<2].forEach(p);++o=r,l=s,s=e[u*t+o+1]>=r,Ea[c|f<<1|s<<2|l<<3].forEach(p);Ea[f|s<<3].forEach(p)}o=-1,s=e[u*t]>=r,Ea[s<<2].forEach(p);for(;++o=r,Ea[s<<2|l<<3].forEach(p);function p(t){var n,e,r=[t[0][0]+o,t[0][1]+u],c=[t[1][0]+o,t[1][1]+u],f=a(r),s=a(c);(n=d[f])?(e=h[s])?(delete d[n.end],delete h[e.start],n===e?(n.ring.push(c),i(n.ring)):h[n.start]=d[e.end]={start:n.start,end:e.end,ring:n.ring.concat(e.ring)}):(delete d[n.end],n.ring.push(c),d[n.end=s]=n):(n=h[s])?(e=d[f])?(delete h[n.start],delete d[e.end],n===e?(n.ring.push(c),i(n.ring)):h[e.start]=d[n.end]={start:e.start,end:n.end,ring:e.ring.concat(n.ring)}):(delete h[n.start],n.ring.unshift(r),h[n.start=f]=n):h[f]=d[s]={start:f,end:s,ring:[r,c]}}Ea[s<<3].forEach(p)}(e,i,(function(t){r(t,e,i),function(t){for(var n=0,e=t.length,r=t[e-1][1]*t[0][0]-t[e-1][0]*t[0][1];++n0?o.push([t]):u.push(t)})),u.forEach((function(t){for(var n,e=0,r=o.length;e0&&a0&&u=0&&o>=0))throw new Error("invalid size");return t=r,n=o,i},i.thresholds=function(t){return arguments.length?(e="function"==typeof t?t:Array.isArray(t)?wa(ma.call(t)):wa(t),i):e},i.smooth=function(t){return arguments.length?(r=t?u:Sa,i):r===u},i}function Na(t,n,e){for(var r=t.width,i=t.height,o=1+(e<<1),a=0;a=e&&(u>=o&&(c-=t.data[u-o+a*r]),n.data[u-e+a*r]=c/Math.min(u+1,r-1+o-u,o))}function Ca(t,n,e){for(var r=t.width,i=t.height,o=1+(e<<1),a=0;a=e&&(u>=o&&(c-=t.data[a+(u-o)*r]),n.data[a+(u-e)*r]=c/Math.min(u+1,i-1+o-u,o))}function Pa(t){return t[0]}function za(t){return t[1]}function Da(){return 1}const qa=Math.pow(2,-52),Ra=new Uint32Array(512);class Fa{static from(t,n=Ha,e=Xa){const r=t.length,i=new Float64Array(2*r);for(let o=0;o>1;if(n>0&&"number"!=typeof t[0])throw new Error("Expected coords to contain numbers.");this.coords=t;const e=Math.max(2*n-5,0);this._triangles=new Uint32Array(3*e),this._halfedges=new Int32Array(3*e),this._hashSize=Math.ceil(Math.sqrt(n)),this._hullPrev=new Uint32Array(n),this._hullNext=new Uint32Array(n),this._hullTri=new Uint32Array(n),this._hullHash=new Int32Array(this._hashSize).fill(-1),this._ids=new Uint32Array(n),this._dists=new Float64Array(n),this.update()}update(){const{coords:t,_hullPrev:n,_hullNext:e,_hullTri:r,_hullHash:i}=this,o=t.length>>1;let a=1/0,u=1/0,c=-1/0,f=-1/0;for(let n=0;nc&&(c=e),r>f&&(f=r),this._ids[n]=n}const s=(a+c)/2,l=(u+f)/2;let h,d,p,g=1/0;for(let n=0;n0&&(d=n,g=e)}let _=t[2*d],b=t[2*d+1],m=1/0;for(let n=0;nr&&(n[e++]=i,r=this._dists[i])}return this.hull=n.subarray(0,e),this.triangles=new Uint32Array(0),void(this.halfedges=new Uint32Array(0))}if(Ua(y,v,_,b,x,w)){const t=d,n=_,e=b;d=p,_=x,b=w,p=t,x=n,w=e}const M=function(t,n,e,r,i,o){const a=e-t,u=r-n,c=i-t,f=o-n,s=a*a+u*u,l=c*c+f*f,h=.5/(a*f-u*c);return{x:t+(f*s-u*l)*h,y:n+(a*l-c*s)*h}}(y,v,_,b,x,w);this._cx=M.x,this._cy=M.y;for(let n=0;n0&&Math.abs(f-o)<=qa&&Math.abs(s-a)<=qa)continue;if(o=f,a=s,c===h||c===d||c===p)continue;let l=0;for(let t=0,n=this._hashKey(f,s);t0?3-e:1+e)/4}(t-this._cx,n-this._cy)*this._hashSize)%this._hashSize}_legalize(t){const{_triangles:n,_halfedges:e,coords:r}=this;let i=0,o=0;for(;;){const a=e[t],u=t-t%3;if(o=u+(t+2)%3,-1===a){if(0===i)break;t=Ra[--i];continue}const c=a-a%3,f=u+(t+1)%3,s=c+(a+2)%3,l=n[o],h=n[t],d=n[f],p=n[s];if(Ba(r[2*l],r[2*l+1],r[2*h],r[2*h+1],r[2*d],r[2*d+1],r[2*p],r[2*p+1])){n[t]=p,n[a]=l;const r=e[s];if(-1===r){let n=this._hullStart;do{if(this._hullTri[n]===s){this._hullTri[n]=t;break}n=this._hullPrev[n]}while(n!==this._hullStart)}this._link(t,r),this._link(a,e[o]),this._link(o,s);const u=c+(a+1)%3;i=33306690738754716e-32*Math.abs(a+u)?a-u:0}function Ua(t,n,e,r,i,o){return(Ia(i,o,t,n,e,r)||Ia(t,n,e,r,i,o)||Ia(e,r,i,o,t,n))<0}function Ba(t,n,e,r,i,o,a,u){const c=t-a,f=n-u,s=e-a,l=r-u,h=i-a,d=o-u,p=s*s+l*l,g=h*h+d*d;return c*(l*g-p*d)-f*(s*g-p*h)+(c*c+f*f)*(s*d-l*h)<0}function Ya(t,n,e,r,i,o){const a=e-t,u=r-n,c=i-t,f=o-n,s=a*a+u*u,l=c*c+f*f,h=.5/(a*f-u*c),d=(f*s-u*l)*h,p=(a*l-c*s)*h;return d*d+p*p}function La(t,n,e,r){if(r-e<=20)for(let i=e+1;i<=r;i++){const r=t[i],o=n[r];let a=i-1;for(;a>=e&&n[t[a]]>o;)t[a+1]=t[a--];t[a+1]=r}else{let i=e+1,o=r;ja(t,e+r>>1,i),n[t[e]]>n[t[r]]&&ja(t,e,r),n[t[i]]>n[t[r]]&&ja(t,i,r),n[t[e]]>n[t[i]]&&ja(t,e,i);const a=t[i],u=n[a];for(;;){do{i++}while(n[t[i]]u);if(o=o-e?(La(t,n,i,r),La(t,n,e,o-1)):(La(t,n,e,o-1),La(t,n,i,r))}}function ja(t,n,e){const r=t[n];t[n]=t[e],t[e]=r}function Ha(t){return t[0]}function Xa(t){return t[1]}const Ga=1e-6;class Va{constructor(){this._x0=this._y0=this._x1=this._y1=null,this._=""}moveTo(t,n){this._+=`M${this._x0=this._x1=+t},${this._y0=this._y1=+n}`}closePath(){null!==this._x1&&(this._x1=this._x0,this._y1=this._y0,this._+="Z")}lineTo(t,n){this._+=`L${this._x1=+t},${this._y1=+n}`}arc(t,n,e){const r=(t=+t)+(e=+e),i=n=+n;if(e<0)throw new Error("negative radius");null===this._x1?this._+=`M${r},${i}`:(Math.abs(this._x1-r)>Ga||Math.abs(this._y1-i)>Ga)&&(this._+="L"+r+","+i),e&&(this._+=`A${e},${e},0,1,1,${t-e},${n}A${e},${e},0,1,1,${this._x1=r},${this._y1=i}`)}rect(t,n,e,r){this._+=`M${this._x0=this._x1=+t},${this._y0=this._y1=+n}h${+e}v${+r}h${-e}Z`}value(){return this._||null}}class $a{constructor(){this._=[]}moveTo(t,n){this._.push([t,n])}closePath(){this._.push(this._[0].slice())}lineTo(t,n){this._.push([t,n])}value(){return this._.length?this._:null}}class Wa{constructor(t,[n,e,r,i]=[0,0,960,500]){if(!((r=+r)>=(n=+n)&&(i=+i)>=(e=+e)))throw new Error("invalid bounds");this.delaunay=t,this._circumcenters=new Float64Array(2*t.points.length),this.vectors=new Float64Array(2*t.points.length),this.xmax=r,this.xmin=n,this.ymax=i,this.ymin=e,this._init()}update(){return this.delaunay.update(),this._init(),this}_init(){const{delaunay:{points:t,hull:n,triangles:e},vectors:r}=this,i=this.circumcenters=this._circumcenters.subarray(0,e.length/3*2);for(let n,r,o=0,a=0,u=e.length;o1;)i-=2;for(let t=2;t4)for(let t=0;t0){if(n>=this.ymax)return null;(i=(this.ymax-n)/r)0){if(t>=this.xmax)return null;(i=(this.xmax-t)/e)this.xmax?2:0)|(nthis.ymax?8:0)}}const Za=2*Math.PI,Ka=Math.pow;function Qa(t){return t[0]}function Ja(t){return t[1]}function tu(t,n,e){return[t+Math.sin(t+n)*e,n+Math.cos(t-n)*e]}class nu{static from(t,n=Qa,e=Ja,r){return new nu("length"in t?function(t,n,e,r){const i=t.length,o=new Float64Array(2*i);for(let a=0;a2&&function(t){const{triangles:n,coords:e}=t;for(let t=0;t1e-10)return!1}return!0}(t)){this.collinear=Int32Array.from({length:n.length/2},((t,n)=>n)).sort(((t,e)=>n[2*t]-n[2*e]||n[2*t+1]-n[2*e+1]));const t=this.collinear[0],e=this.collinear[this.collinear.length-1],r=[n[2*t],n[2*t+1],n[2*e],n[2*e+1]],i=1e-8*Math.hypot(r[3]-r[1],r[2]-r[0]);for(let t=0,e=n.length/2;t0&&(this.triangles=new Int32Array(3).fill(-1),this.halfedges=new Int32Array(3).fill(-1),this.triangles[0]=r[0],this.triangles[1]=r[1],this.triangles[2]=r[1],o[r[0]]=1,2===r.length&&(o[r[1]]=0))}voronoi(t){return new Wa(this,t)}*neighbors(t){const{inedges:n,hull:e,_hullIndex:r,halfedges:i,triangles:o,collinear:a}=this;if(a){const n=a.indexOf(t);return n>0&&(yield a[n-1]),void(n=0&&i!==e&&i!==r;)e=i;return i}_step(t,n,e){const{inedges:r,hull:i,_hullIndex:o,halfedges:a,triangles:u,points:c}=this;if(-1===r[t]||!c.length)return(t+1)%(c.length>>1);let f=t,s=Ka(n-c[2*t],2)+Ka(e-c[2*t+1],2);const l=r[t];let h=l;do{let r=u[h];const l=Ka(n-c[2*r],2)+Ka(e-c[2*r+1],2);if(l9999?"+"+au(t,6):au(t,4)}(t.getUTCFullYear())+"-"+au(t.getUTCMonth()+1,2)+"-"+au(t.getUTCDate(),2)+(i?"T"+au(n,2)+":"+au(e,2)+":"+au(r,2)+"."+au(i,3)+"Z":r?"T"+au(n,2)+":"+au(e,2)+":"+au(r,2)+"Z":e||n?"T"+au(n,2)+":"+au(e,2)+"Z":"")}function cu(t){var n=new RegExp('["'+t+"\n\r]"),e=t.charCodeAt(0);function r(t,n){var r,i=[],o=t.length,a=0,u=0,c=o<=0,f=!1;function s(){if(c)return ru;if(f)return f=!1,eu;var n,r,i=a;if(34===t.charCodeAt(i)){for(;a++=o?c=!0:10===(r=t.charCodeAt(a++))?f=!0:13===r&&(f=!0,10===t.charCodeAt(a)&&++a),t.slice(i+1,n-1).replace(/""/g,'"')}for(;aNu(n,e).then((n=>(new DOMParser).parseFromString(n,t)))}var Ru=qu("application/xml"),Fu=qu("text/html"),Ou=qu("image/svg+xml");function Iu(t,n,e,r){if(isNaN(n)||isNaN(e))return t;var i,o,a,u,c,f,s,l,h,d=t._root,p={data:r},g=t._x0,y=t._y0,v=t._x1,_=t._y1;if(!d)return t._root=p,t;for(;d.length;)if((f=n>=(o=(g+v)/2))?g=o:v=o,(s=e>=(a=(y+_)/2))?y=a:_=a,i=d,!(d=d[l=s<<1|f]))return i[l]=p,t;if(u=+t._x.call(null,d.data),c=+t._y.call(null,d.data),n===u&&e===c)return p.next=d,i?i[l]=p:t._root=p,t;do{i=i?i[l]=new Array(4):t._root=new Array(4),(f=n>=(o=(g+v)/2))?g=o:v=o,(s=e>=(a=(y+_)/2))?y=a:_=a}while((l=s<<1|f)==(h=(c>=a)<<1|u>=o));return i[h]=d,i[l]=p,t}function Uu(t,n,e,r,i){this.node=t,this.x0=n,this.y0=e,this.x1=r,this.y1=i}function Bu(t){return t[0]}function Yu(t){return t[1]}function Lu(t,n,e){var r=new ju(null==n?Bu:n,null==e?Yu:e,NaN,NaN,NaN,NaN);return null==t?r:r.addAll(t)}function ju(t,n,e,r,i,o){this._x=t,this._y=n,this._x0=e,this._y0=r,this._x1=i,this._y1=o,this._root=void 0}function Hu(t){for(var n={data:t.data},e=n;t=t.next;)e=e.next={data:t.data};return n}var Xu=Lu.prototype=ju.prototype;function Gu(t){return function(){return t}}function Vu(t){return 1e-6*(t()-.5)}function $u(t){return t.x+t.vx}function Wu(t){return t.y+t.vy}function Zu(t){return t.index}function Ku(t,n){var e=t.get(n);if(!e)throw new Error("node not found: "+n);return e}Xu.copy=function(){var t,n,e=new ju(this._x,this._y,this._x0,this._y0,this._x1,this._y1),r=this._root;if(!r)return e;if(!r.length)return e._root=Hu(r),e;for(t=[{source:r,target:e._root=new Array(4)}];r=t.pop();)for(var i=0;i<4;++i)(n=r.source[i])&&(n.length?t.push({source:n,target:r.target[i]=new Array(4)}):r.target[i]=Hu(n));return e},Xu.add=function(t){const n=+this._x.call(null,t),e=+this._y.call(null,t);return Iu(this.cover(n,e),n,e,t)},Xu.addAll=function(t){var n,e,r,i,o=t.length,a=new Array(o),u=new Array(o),c=1/0,f=1/0,s=-1/0,l=-1/0;for(e=0;es&&(s=r),il&&(l=i));if(c>s||f>l)return this;for(this.cover(c,f).cover(s,l),e=0;et||t>=i||r>n||n>=o;)switch(u=(nh||(o=c.y0)>d||(a=c.x1)=v)<<1|t>=y)&&(c=p[p.length-1],p[p.length-1]=p[p.length-1-f],p[p.length-1-f]=c)}else{var _=t-+this._x.call(null,g.data),b=n-+this._y.call(null,g.data),m=_*_+b*b;if(m=(u=(p+y)/2))?p=u:y=u,(s=a>=(c=(g+v)/2))?g=c:v=c,n=d,!(d=d[l=s<<1|f]))return this;if(!d.length)break;(n[l+1&3]||n[l+2&3]||n[l+3&3])&&(e=n,h=l)}for(;d.data!==t;)if(r=d,!(d=d.next))return this;return(i=d.next)&&delete d.next,r?(i?r.next=i:delete r.next,this):n?(i?n[l]=i:delete n[l],(d=n[0]||n[1]||n[2]||n[3])&&d===(n[3]||n[2]||n[1]||n[0])&&!d.length&&(e?e[h]=d:this._root=d),this):(this._root=i,this)},Xu.removeAll=function(t){for(var n=0,e=t.length;n1?r[0]+r.slice(2):r,+t.slice(e+1)]}function rc(t){return(t=ec(Math.abs(t)))?t[1]:NaN}var ic,oc=/^(?:(.)?([<>=^]))?([+\-( ])?([$#])?(0)?(\d+)?(,)?(\.\d+)?(~)?([a-z%])?$/i;function ac(t){if(!(n=oc.exec(t)))throw new Error("invalid format: "+t);var n;return new uc({fill:n[1],align:n[2],sign:n[3],symbol:n[4],zero:n[5],width:n[6],comma:n[7],precision:n[8]&&n[8].slice(1),trim:n[9],type:n[10]})}function uc(t){this.fill=void 0===t.fill?" ":t.fill+"",this.align=void 0===t.align?">":t.align+"",this.sign=void 0===t.sign?"-":t.sign+"",this.symbol=void 0===t.symbol?"":t.symbol+"",this.zero=!!t.zero,this.width=void 0===t.width?void 0:+t.width,this.comma=!!t.comma,this.precision=void 0===t.precision?void 0:+t.precision,this.trim=!!t.trim,this.type=void 0===t.type?"":t.type+""}function cc(t,n){var e=ec(t,n);if(!e)return t+"";var r=e[0],i=e[1];return i<0?"0."+new Array(-i).join("0")+r:r.length>i+1?r.slice(0,i+1)+"."+r.slice(i+1):r+new Array(i-r.length+2).join("0")}ac.prototype=uc.prototype,uc.prototype.toString=function(){return this.fill+this.align+this.sign+this.symbol+(this.zero?"0":"")+(void 0===this.width?"":Math.max(1,0|this.width))+(this.comma?",":"")+(void 0===this.precision?"":"."+Math.max(0,0|this.precision))+(this.trim?"~":"")+this.type};var fc={"%":(t,n)=>(100*t).toFixed(n),b:t=>Math.round(t).toString(2),c:t=>t+"",d:function(t){return Math.abs(t=Math.round(t))>=1e21?t.toLocaleString("en").replace(/,/g,""):t.toString(10)},e:(t,n)=>t.toExponential(n),f:(t,n)=>t.toFixed(n),g:(t,n)=>t.toPrecision(n),o:t=>Math.round(t).toString(8),p:(t,n)=>cc(100*t,n),r:cc,s:function(t,n){var e=ec(t,n);if(!e)return t+"";var r=e[0],i=e[1],o=i-(ic=3*Math.max(-8,Math.min(8,Math.floor(i/3))))+1,a=r.length;return o===a?r:o>a?r+new Array(o-a+1).join("0"):o>0?r.slice(0,o)+"."+r.slice(o):"0."+new Array(1-o).join("0")+ec(t,Math.max(0,n+o-1))[0]},X:t=>Math.round(t).toString(16).toUpperCase(),x:t=>Math.round(t).toString(16)};function sc(t){return t}var lc,hc=Array.prototype.map,dc=["y","z","a","f","p","n","µ","m","","k","M","G","T","P","E","Z","Y"];function pc(t){var n,e,r=void 0===t.grouping||void 0===t.thousands?sc:(n=hc.call(t.grouping,Number),e=t.thousands+"",function(t,r){for(var i=t.length,o=[],a=0,u=n[0],c=0;i>0&&u>0&&(c+u+1>r&&(u=Math.max(1,r-c)),o.push(t.substring(i-=u,i+u)),!((c+=u+1)>r));)u=n[a=(a+1)%n.length];return o.reverse().join(e)}),i=void 0===t.currency?"":t.currency[0]+"",o=void 0===t.currency?"":t.currency[1]+"",a=void 0===t.decimal?".":t.decimal+"",u=void 0===t.numerals?sc:function(t){return function(n){return n.replace(/[0-9]/g,(function(n){return t[+n]}))}}(hc.call(t.numerals,String)),c=void 0===t.percent?"%":t.percent+"",f=void 0===t.minus?"−":t.minus+"",s=void 0===t.nan?"NaN":t.nan+"";function l(t){var n=(t=ac(t)).fill,e=t.align,l=t.sign,h=t.symbol,d=t.zero,p=t.width,g=t.comma,y=t.precision,v=t.trim,_=t.type;"n"===_?(g=!0,_="g"):fc[_]||(void 0===y&&(y=12),v=!0,_="g"),(d||"0"===n&&"="===e)&&(d=!0,n="0",e="=");var b="$"===h?i:"#"===h&&/[boxX]/.test(_)?"0"+_.toLowerCase():"",m="$"===h?o:/[%p]/.test(_)?c:"",x=fc[_],w=/[defgprs%]/.test(_);function M(t){var i,o,c,h=b,M=m;if("c"===_)M=x(t)+M,t="";else{var A=(t=+t)<0||1/t<0;if(t=isNaN(t)?s:x(Math.abs(t),y),v&&(t=function(t){t:for(var n,e=t.length,r=1,i=-1;r0&&(i=0)}return i>0?t.slice(0,i)+t.slice(n+1):t}(t)),A&&0==+t&&"+"!==l&&(A=!1),h=(A?"("===l?l:f:"-"===l||"("===l?"":l)+h,M=("s"===_?dc[8+ic/3]:"")+M+(A&&"("===l?")":""),w)for(i=-1,o=t.length;++i(c=t.charCodeAt(i))||c>57){M=(46===c?a+t.slice(i+1):t.slice(i))+M,t=t.slice(0,i);break}}g&&!d&&(t=r(t,1/0));var T=h.length+t.length+M.length,S=T>1)+h+t+M+S.slice(T);break;default:t=S+h+t+M}return u(t)}return y=void 0===y?6:/[gprs]/.test(_)?Math.max(1,Math.min(21,y)):Math.max(0,Math.min(20,y)),M.toString=function(){return t+""},M}return{format:l,formatPrefix:function(t,n){var e=l(((t=ac(t)).type="f",t)),r=3*Math.max(-8,Math.min(8,Math.floor(rc(n)/3))),i=Math.pow(10,-r),o=dc[8+r/3];return function(t){return e(i*t)+o}}}}function gc(n){return lc=pc(n),t.format=lc.format,t.formatPrefix=lc.formatPrefix,lc}function yc(t){return Math.max(0,-rc(Math.abs(t)))}function vc(t,n){return Math.max(0,3*Math.max(-8,Math.min(8,Math.floor(rc(n)/3)))-rc(Math.abs(t)))}function _c(t,n){return t=Math.abs(t),n=Math.abs(n)-t,Math.max(0,rc(n)-rc(t))+1}t.format=void 0,t.formatPrefix=void 0,gc({thousands:",",grouping:[3],currency:["$",""]});var bc=1e-6,mc=1e-12,xc=Math.PI,wc=xc/2,Mc=xc/4,Ac=2*xc,Tc=180/xc,Sc=xc/180,Ec=Math.abs,kc=Math.atan,Nc=Math.atan2,Cc=Math.cos,Pc=Math.ceil,zc=Math.exp,Dc=Math.hypot,qc=Math.log,Rc=Math.pow,Fc=Math.sin,Oc=Math.sign||function(t){return t>0?1:t<0?-1:0},Ic=Math.sqrt,Uc=Math.tan;function Bc(t){return t>1?0:t<-1?xc:Math.acos(t)}function Yc(t){return t>1?wc:t<-1?-wc:Math.asin(t)}function Lc(t){return(t=Fc(t/2))*t}function jc(){}function Hc(t,n){t&&Gc.hasOwnProperty(t.type)&&Gc[t.type](t,n)}var Xc={Feature:function(t,n){Hc(t.geometry,n)},FeatureCollection:function(t,n){for(var e=t.features,r=-1,i=e.length;++r=0?1:-1,i=r*e,o=Cc(n=(n*=Sc)/2+Mc),a=Fc(n),u=tf*a,c=Jc*o+u*Cc(i),f=u*r*Fc(i);df.add(Nc(f,c)),Qc=t,Jc=o,tf=a}function mf(t){return[Nc(t[1],t[0]),Yc(t[2])]}function xf(t){var n=t[0],e=t[1],r=Cc(e);return[r*Cc(n),r*Fc(n),Fc(e)]}function wf(t,n){return t[0]*n[0]+t[1]*n[1]+t[2]*n[2]}function Mf(t,n){return[t[1]*n[2]-t[2]*n[1],t[2]*n[0]-t[0]*n[2],t[0]*n[1]-t[1]*n[0]]}function Af(t,n){t[0]+=n[0],t[1]+=n[1],t[2]+=n[2]}function Tf(t,n){return[t[0]*n,t[1]*n,t[2]*n]}function Sf(t){var n=Ic(t[0]*t[0]+t[1]*t[1]+t[2]*t[2]);t[0]/=n,t[1]/=n,t[2]/=n}var Ef,kf,Nf,Cf,Pf,zf,Df,qf,Rf,Ff,Of,If,Uf,Bf,Yf,Lf,jf={point:Hf,lineStart:Gf,lineEnd:Vf,polygonStart:function(){jf.point=$f,jf.lineStart=Wf,jf.lineEnd=Zf,sf=new g,gf.polygonStart()},polygonEnd:function(){gf.polygonEnd(),jf.point=Hf,jf.lineStart=Gf,jf.lineEnd=Vf,df<0?(nf=-(rf=180),ef=-(of=90)):sf>bc?of=90:sf<-1e-6&&(ef=-90),hf[0]=nf,hf[1]=rf},sphere:function(){nf=-(rf=180),ef=-(of=90)}};function Hf(t,n){lf.push(hf=[nf=t,rf=t]),nof&&(of=n)}function Xf(t,n){var e=xf([t*Sc,n*Sc]);if(ff){var r=Mf(ff,e),i=Mf([r[1],-r[0],0],r);Sf(i),i=mf(i);var o,a=t-af,u=a>0?1:-1,c=i[0]*Tc*u,f=Ec(a)>180;f^(u*afof&&(of=o):f^(u*af<(c=(c+360)%360-180)&&cof&&(of=n)),f?tKf(nf,rf)&&(rf=t):Kf(t,rf)>Kf(nf,rf)&&(nf=t):rf>=nf?(trf&&(rf=t)):t>af?Kf(nf,t)>Kf(nf,rf)&&(rf=t):Kf(t,rf)>Kf(nf,rf)&&(nf=t)}else lf.push(hf=[nf=t,rf=t]);nof&&(of=n),ff=e,af=t}function Gf(){jf.point=Xf}function Vf(){hf[0]=nf,hf[1]=rf,jf.point=Hf,ff=null}function $f(t,n){if(ff){var e=t-af;sf.add(Ec(e)>180?e+(e>0?360:-360):e)}else uf=t,cf=n;gf.point(t,n),Xf(t,n)}function Wf(){gf.lineStart()}function Zf(){$f(uf,cf),gf.lineEnd(),Ec(sf)>bc&&(nf=-(rf=180)),hf[0]=nf,hf[1]=rf,ff=null}function Kf(t,n){return(n-=t)<0?n+360:n}function Qf(t,n){return t[0]-n[0]}function Jf(t,n){return t[0]<=t[1]?t[0]<=n&&n<=t[1]:nxc?t+Math.round(-t/Ac)*Ac:t,n]}function ps(t,n,e){return(t%=Ac)?n||e?hs(ys(t),vs(n,e)):ys(t):n||e?vs(n,e):ds}function gs(t){return function(n,e){return[(n+=t)>xc?n-Ac:n<-xc?n+Ac:n,e]}}function ys(t){var n=gs(t);return n.invert=gs(-t),n}function vs(t,n){var e=Cc(t),r=Fc(t),i=Cc(n),o=Fc(n);function a(t,n){var a=Cc(n),u=Cc(t)*a,c=Fc(t)*a,f=Fc(n),s=f*e+u*r;return[Nc(c*i-s*o,u*e-f*r),Yc(s*i+c*o)]}return a.invert=function(t,n){var a=Cc(n),u=Cc(t)*a,c=Fc(t)*a,f=Fc(n),s=f*i-c*o;return[Nc(c*i+f*o,u*e+s*r),Yc(s*e-u*r)]},a}function _s(t){function n(n){return(n=t(n[0]*Sc,n[1]*Sc))[0]*=Tc,n[1]*=Tc,n}return t=ps(t[0]*Sc,t[1]*Sc,t.length>2?t[2]*Sc:0),n.invert=function(n){return(n=t.invert(n[0]*Sc,n[1]*Sc))[0]*=Tc,n[1]*=Tc,n},n}function bs(t,n,e,r,i,o){if(e){var a=Cc(n),u=Fc(n),c=r*e;null==i?(i=n+r*Ac,o=n-c/2):(i=ms(a,i),o=ms(a,o),(r>0?io)&&(i+=r*Ac));for(var f,s=i;r>0?s>o:s1&&n.push(n.pop().concat(n.shift()))},result:function(){var e=n;return n=[],t=null,e}}}function ws(t,n){return Ec(t[0]-n[0])=0;--o)i.point((s=f[o])[0],s[1]);else r(h.x,h.p.x,-1,i);h=h.p}f=(h=h.o).z,d=!d}while(!h.v);i.lineEnd()}}}function Ts(t){if(n=t.length){for(var n,e,r=0,i=t[0];++r=0?1:-1,E=S*T,k=E>xc,N=v*M;if(c.add(Nc(N*S*Fc(E),_*A+N*Cc(E))),a+=k?T+S*Ac:T,k^p>=e^x>=e){var C=Mf(xf(d),xf(m));Sf(C);var P=Mf(o,C);Sf(P);var z=(k^T>=0?-1:1)*Yc(P[2]);(r>z||r===z&&(C[0]||C[1]))&&(u+=k^T>=0?1:-1)}}return(a<-1e-6||a0){for(l||(i.polygonStart(),l=!0),i.lineStart(),t=0;t1&&2&c&&h.push(h.pop().concat(h.shift())),a.push(h.filter(Ns))}return h}}function Ns(t){return t.length>1}function Cs(t,n){return((t=t.x)[0]<0?t[1]-wc-bc:wc-t[1])-((n=n.x)[0]<0?n[1]-wc-bc:wc-n[1])}ds.invert=ds;var Ps=ks((function(){return!0}),(function(t){var n,e=NaN,r=NaN,i=NaN;return{lineStart:function(){t.lineStart(),n=1},point:function(o,a){var u=o>0?xc:-xc,c=Ec(o-e);Ec(c-xc)0?wc:-wc),t.point(i,r),t.lineEnd(),t.lineStart(),t.point(u,r),t.point(o,r),n=0):i!==u&&c>=xc&&(Ec(e-i)bc?kc((Fc(n)*(o=Cc(r))*Fc(e)-Fc(r)*(i=Cc(n))*Fc(t))/(i*o*a)):(n+r)/2}(e,r,o,a),t.point(i,r),t.lineEnd(),t.lineStart(),t.point(u,r),n=0),t.point(e=o,r=a),i=u},lineEnd:function(){t.lineEnd(),e=r=NaN},clean:function(){return 2-n}}}),(function(t,n,e,r){var i;if(null==t)i=e*wc,r.point(-xc,i),r.point(0,i),r.point(xc,i),r.point(xc,0),r.point(xc,-i),r.point(0,-i),r.point(-xc,-i),r.point(-xc,0),r.point(-xc,i);else if(Ec(t[0]-n[0])>bc){var o=t[0]0,i=Ec(n)>bc;function o(t,e){return Cc(t)*Cc(e)>n}function a(t,e,r){var i=[1,0,0],o=Mf(xf(t),xf(e)),a=wf(o,o),u=o[0],c=a-u*u;if(!c)return!r&&t;var f=n*a/c,s=-n*u/c,l=Mf(i,o),h=Tf(i,f);Af(h,Tf(o,s));var d=l,p=wf(h,d),g=wf(d,d),y=p*p-g*(wf(h,h)-1);if(!(y<0)){var v=Ic(y),_=Tf(d,(-p-v)/g);if(Af(_,h),_=mf(_),!r)return _;var b,m=t[0],x=e[0],w=t[1],M=e[1];x0^_[1]<(Ec(_[0]-m)xc^(m<=_[0]&&_[0]<=x)){var S=Tf(d,(-p+v)/g);return Af(S,h),[_,mf(S)]}}}function u(n,e){var i=r?t:xc-t,o=0;return n<-i?o|=1:n>i&&(o|=2),e<-i?o|=4:e>i&&(o|=8),o}return ks(o,(function(t){var n,e,c,f,s;return{lineStart:function(){f=c=!1,s=1},point:function(l,h){var d,p=[l,h],g=o(l,h),y=r?g?0:u(l,h):g?u(l+(l<0?xc:-xc),h):0;if(!n&&(f=c=g)&&t.lineStart(),g!==c&&(!(d=a(n,p))||ws(n,d)||ws(p,d))&&(p[2]=1),g!==c)s=0,g?(t.lineStart(),d=a(p,n),t.point(d[0],d[1])):(d=a(n,p),t.point(d[0],d[1],2),t.lineEnd()),n=d;else if(i&&n&&r^g){var v;y&e||!(v=a(p,n,!0))||(s=0,r?(t.lineStart(),t.point(v[0][0],v[0][1]),t.point(v[1][0],v[1][1]),t.lineEnd()):(t.point(v[1][0],v[1][1]),t.lineEnd(),t.lineStart(),t.point(v[0][0],v[0][1],3)))}!g||n&&ws(n,p)||t.point(p[0],p[1]),n=p,c=g,e=y},lineEnd:function(){c&&t.lineEnd(),n=null},clean:function(){return s|(f&&c)<<1}}}),(function(n,r,i,o){bs(o,t,e,i,n,r)}),r?[0,-t]:[-xc,t-xc])}var Ds,qs,Rs,Fs,Os=1e9,Is=-Os;function Us(t,n,e,r){function i(i,o){return t<=i&&i<=e&&n<=o&&o<=r}function o(i,o,u,f){var s=0,l=0;if(null==i||(s=a(i,u))!==(l=a(o,u))||c(i,o)<0^u>0)do{f.point(0===s||3===s?t:e,s>1?r:n)}while((s=(s+u+4)%4)!==l);else f.point(o[0],o[1])}function a(r,i){return Ec(r[0]-t)0?0:3:Ec(r[0]-e)0?2:1:Ec(r[1]-n)0?1:0:i>0?3:2}function u(t,n){return c(t.x,n.x)}function c(t,n){var e=a(t,1),r=a(n,1);return e!==r?e-r:0===e?n[1]-t[1]:1===e?t[0]-n[0]:2===e?t[1]-n[1]:n[0]-t[0]}return function(a){var c,f,s,l,h,d,p,g,y,v,_,b=a,m=xs(),x={point:w,lineStart:function(){x.point=M,f&&f.push(s=[]);v=!0,y=!1,p=g=NaN},lineEnd:function(){c&&(M(l,h),d&&y&&m.rejoin(),c.push(m.result()));x.point=w,y&&b.lineEnd()},polygonStart:function(){b=m,c=[],f=[],_=!0},polygonEnd:function(){var n=function(){for(var n=0,e=0,i=f.length;er&&(h-o)*(r-a)>(d-a)*(t-o)&&++n:d<=r&&(h-o)*(r-a)<(d-a)*(t-o)&&--n;return n}(),e=_&&n,i=(c=V(c)).length;(e||i)&&(a.polygonStart(),e&&(a.lineStart(),o(null,null,1,a),a.lineEnd()),i&&As(c,u,n,o,a),a.polygonEnd());b=a,c=f=s=null}};function w(t,n){i(t,n)&&b.point(t,n)}function M(o,a){var u=i(o,a);if(f&&s.push([o,a]),v)l=o,h=a,d=u,v=!1,u&&(b.lineStart(),b.point(o,a));else if(u&&y)b.point(o,a);else{var c=[p=Math.max(Is,Math.min(Os,p)),g=Math.max(Is,Math.min(Os,g))],m=[o=Math.max(Is,Math.min(Os,o)),a=Math.max(Is,Math.min(Os,a))];!function(t,n,e,r,i,o){var a,u=t[0],c=t[1],f=0,s=1,l=n[0]-u,h=n[1]-c;if(a=e-u,l||!(a>0)){if(a/=l,l<0){if(a0){if(a>s)return;a>f&&(f=a)}if(a=i-u,l||!(a<0)){if(a/=l,l<0){if(a>s)return;a>f&&(f=a)}else if(l>0){if(a0)){if(a/=h,h<0){if(a0){if(a>s)return;a>f&&(f=a)}if(a=o-c,h||!(a<0)){if(a/=h,h<0){if(a>s)return;a>f&&(f=a)}else if(h>0){if(a0&&(t[0]=u+f*l,t[1]=c+f*h),s<1&&(n[0]=u+s*l,n[1]=c+s*h),!0}}}}}(c,m,t,n,e,r)?u&&(b.lineStart(),b.point(o,a),_=!1):(y||(b.lineStart(),b.point(c[0],c[1])),b.point(m[0],m[1]),u||b.lineEnd(),_=!1)}p=o,g=a,y=u}return x}}var Bs={sphere:jc,point:jc,lineStart:function(){Bs.point=Ls,Bs.lineEnd=Ys},lineEnd:jc,polygonStart:jc,polygonEnd:jc};function Ys(){Bs.point=Bs.lineEnd=jc}function Ls(t,n){qs=t*=Sc,Rs=Fc(n*=Sc),Fs=Cc(n),Bs.point=js}function js(t,n){t*=Sc;var e=Fc(n*=Sc),r=Cc(n),i=Ec(t-qs),o=Cc(i),a=r*Fc(i),u=Fs*e-Rs*r*o,c=Rs*e+Fs*r*o;Ds.add(Nc(Ic(a*a+u*u),c)),qs=t,Rs=e,Fs=r}function Hs(t){return Ds=new g,Wc(t,Bs),+Ds}var Xs=[null,null],Gs={type:"LineString",coordinates:Xs};function Vs(t,n){return Xs[0]=t,Xs[1]=n,Hs(Gs)}var $s={Feature:function(t,n){return Zs(t.geometry,n)},FeatureCollection:function(t,n){for(var e=t.features,r=-1,i=e.length;++r0&&(i=Vs(t[o],t[o-1]))>0&&e<=i&&r<=i&&(e+r-i)*(1-Math.pow((e-r)/i,2))bc})).map(c)).concat(Z(Pc(o/d)*d,i,d).filter((function(t){return Ec(t%g)>bc})).map(f))}return v.lines=function(){return _().map((function(t){return{type:"LineString",coordinates:t}}))},v.outline=function(){return{type:"Polygon",coordinates:[s(r).concat(l(a).slice(1),s(e).reverse().slice(1),l(u).reverse().slice(1))]}},v.extent=function(t){return arguments.length?v.extentMajor(t).extentMinor(t):v.extentMinor()},v.extentMajor=function(t){return arguments.length?(r=+t[0][0],e=+t[1][0],u=+t[0][1],a=+t[1][1],r>e&&(t=r,r=e,e=t),u>a&&(t=u,u=a,a=t),v.precision(y)):[[r,u],[e,a]]},v.extentMinor=function(e){return arguments.length?(n=+e[0][0],t=+e[1][0],o=+e[0][1],i=+e[1][1],n>t&&(e=n,n=t,t=e),o>i&&(e=o,o=i,i=e),v.precision(y)):[[n,o],[t,i]]},v.step=function(t){return arguments.length?v.stepMajor(t).stepMinor(t):v.stepMinor()},v.stepMajor=function(t){return arguments.length?(p=+t[0],g=+t[1],v):[p,g]},v.stepMinor=function(t){return arguments.length?(h=+t[0],d=+t[1],v):[h,d]},v.precision=function(h){return arguments.length?(y=+h,c=el(o,i,90),f=rl(n,t,y),s=el(u,a,90),l=rl(r,e,y),v):y},v.extentMajor([[-180,-89.999999],[180,89.999999]]).extentMinor([[-180,-80.000001],[180,80.000001]])}var ol,al,ul,cl,fl=t=>t,sl=new g,ll=new g,hl={point:jc,lineStart:jc,lineEnd:jc,polygonStart:function(){hl.lineStart=dl,hl.lineEnd=yl},polygonEnd:function(){hl.lineStart=hl.lineEnd=hl.point=jc,sl.add(Ec(ll)),ll=new g},result:function(){var t=sl/2;return sl=new g,t}};function dl(){hl.point=pl}function pl(t,n){hl.point=gl,ol=ul=t,al=cl=n}function gl(t,n){ll.add(cl*t-ul*n),ul=t,cl=n}function yl(){gl(ol,al)}var vl=1/0,_l=vl,bl=-vl,ml=bl,xl={point:function(t,n){tbl&&(bl=t);n<_l&&(_l=n);n>ml&&(ml=n)},lineStart:jc,lineEnd:jc,polygonStart:jc,polygonEnd:jc,result:function(){var t=[[vl,_l],[bl,ml]];return bl=ml=-(_l=vl=1/0),t}};var wl,Ml,Al,Tl,Sl=0,El=0,kl=0,Nl=0,Cl=0,Pl=0,zl=0,Dl=0,ql=0,Rl={point:Fl,lineStart:Ol,lineEnd:Bl,polygonStart:function(){Rl.lineStart=Yl,Rl.lineEnd=Ll},polygonEnd:function(){Rl.point=Fl,Rl.lineStart=Ol,Rl.lineEnd=Bl},result:function(){var t=ql?[zl/ql,Dl/ql]:Pl?[Nl/Pl,Cl/Pl]:kl?[Sl/kl,El/kl]:[NaN,NaN];return Sl=El=kl=Nl=Cl=Pl=zl=Dl=ql=0,t}};function Fl(t,n){Sl+=t,El+=n,++kl}function Ol(){Rl.point=Il}function Il(t,n){Rl.point=Ul,Fl(Al=t,Tl=n)}function Ul(t,n){var e=t-Al,r=n-Tl,i=Ic(e*e+r*r);Nl+=i*(Al+t)/2,Cl+=i*(Tl+n)/2,Pl+=i,Fl(Al=t,Tl=n)}function Bl(){Rl.point=Fl}function Yl(){Rl.point=jl}function Ll(){Hl(wl,Ml)}function jl(t,n){Rl.point=Hl,Fl(wl=Al=t,Ml=Tl=n)}function Hl(t,n){var e=t-Al,r=n-Tl,i=Ic(e*e+r*r);Nl+=i*(Al+t)/2,Cl+=i*(Tl+n)/2,Pl+=i,zl+=(i=Tl*t-Al*n)*(Al+t),Dl+=i*(Tl+n),ql+=3*i,Fl(Al=t,Tl=n)}function Xl(t){this._context=t}Xl.prototype={_radius:4.5,pointRadius:function(t){return this._radius=t,this},polygonStart:function(){this._line=0},polygonEnd:function(){this._line=NaN},lineStart:function(){this._point=0},lineEnd:function(){0===this._line&&this._context.closePath(),this._point=NaN},point:function(t,n){switch(this._point){case 0:this._context.moveTo(t,n),this._point=1;break;case 1:this._context.lineTo(t,n);break;default:this._context.moveTo(t+this._radius,n),this._context.arc(t,n,this._radius,0,Ac)}},result:jc};var Gl,Vl,$l,Wl,Zl,Kl=new g,Ql={point:jc,lineStart:function(){Ql.point=Jl},lineEnd:function(){Gl&&th(Vl,$l),Ql.point=jc},polygonStart:function(){Gl=!0},polygonEnd:function(){Gl=null},result:function(){var t=+Kl;return Kl=new g,t}};function Jl(t,n){Ql.point=th,Vl=Wl=t,$l=Zl=n}function th(t,n){Wl-=t,Zl-=n,Kl.add(Ic(Wl*Wl+Zl*Zl)),Wl=t,Zl=n}function nh(){this._string=[]}function eh(t){return"m0,"+t+"a"+t+","+t+" 0 1,1 0,"+-2*t+"a"+t+","+t+" 0 1,1 0,"+2*t+"z"}function rh(t){return function(n){var e=new ih;for(var r in t)e[r]=t[r];return e.stream=n,e}}function ih(){}function oh(t,n,e){var r=t.clipExtent&&t.clipExtent();return t.scale(150).translate([0,0]),null!=r&&t.clipExtent(null),Wc(e,t.stream(xl)),n(xl.result()),null!=r&&t.clipExtent(r),t}function ah(t,n,e){return oh(t,(function(e){var r=n[1][0]-n[0][0],i=n[1][1]-n[0][1],o=Math.min(r/(e[1][0]-e[0][0]),i/(e[1][1]-e[0][1])),a=+n[0][0]+(r-o*(e[1][0]+e[0][0]))/2,u=+n[0][1]+(i-o*(e[1][1]+e[0][1]))/2;t.scale(150*o).translate([a,u])}),e)}function uh(t,n,e){return ah(t,[[0,0],n],e)}function ch(t,n,e){return oh(t,(function(e){var r=+n,i=r/(e[1][0]-e[0][0]),o=(r-i*(e[1][0]+e[0][0]))/2,a=-i*e[0][1];t.scale(150*i).translate([o,a])}),e)}function fh(t,n,e){return oh(t,(function(e){var r=+n,i=r/(e[1][1]-e[0][1]),o=-i*e[0][0],a=(r-i*(e[1][1]+e[0][1]))/2;t.scale(150*i).translate([o,a])}),e)}nh.prototype={_radius:4.5,_circle:eh(4.5),pointRadius:function(t){return(t=+t)!==this._radius&&(this._radius=t,this._circle=null),this},polygonStart:function(){this._line=0},polygonEnd:function(){this._line=NaN},lineStart:function(){this._point=0},lineEnd:function(){0===this._line&&this._string.push("Z"),this._point=NaN},point:function(t,n){switch(this._point){case 0:this._string.push("M",t,",",n),this._point=1;break;case 1:this._string.push("L",t,",",n);break;default:null==this._circle&&(this._circle=eh(this._radius)),this._string.push("M",t,",",n,this._circle)}},result:function(){if(this._string.length){var t=this._string.join("");return this._string=[],t}return null}},ih.prototype={constructor:ih,point:function(t,n){this.stream.point(t,n)},sphere:function(){this.stream.sphere()},lineStart:function(){this.stream.lineStart()},lineEnd:function(){this.stream.lineEnd()},polygonStart:function(){this.stream.polygonStart()},polygonEnd:function(){this.stream.polygonEnd()}};var sh=Cc(30*Sc);function lh(t,n){return+n?function(t,n){function e(r,i,o,a,u,c,f,s,l,h,d,p,g,y){var v=f-r,_=s-i,b=v*v+_*_;if(b>4*n&&g--){var m=a+h,x=u+d,w=c+p,M=Ic(m*m+x*x+w*w),A=Yc(w/=M),T=Ec(Ec(w)-1)n||Ec((v*N+_*C)/b-.5)>.3||a*h+u*d+c*p2?t[2]%360*Sc:0,N()):[y*Tc,v*Tc,_*Tc]},E.angle=function(t){return arguments.length?(b=t%360*Sc,N()):b*Tc},E.reflectX=function(t){return arguments.length?(m=t?-1:1,N()):m<0},E.reflectY=function(t){return arguments.length?(x=t?-1:1,N()):x<0},E.precision=function(t){return arguments.length?(a=lh(u,S=t*t),C()):Ic(S)},E.fitExtent=function(t,n){return ah(E,t,n)},E.fitSize=function(t,n){return uh(E,t,n)},E.fitWidth=function(t,n){return ch(E,t,n)},E.fitHeight=function(t,n){return fh(E,t,n)},function(){return n=t.apply(this,arguments),E.invert=n.invert&&k,N()}}function yh(t){var n=0,e=xc/3,r=gh(t),i=r(n,e);return i.parallels=function(t){return arguments.length?r(n=t[0]*Sc,e=t[1]*Sc):[n*Tc,e*Tc]},i}function vh(t,n){var e=Fc(t),r=(e+Fc(n))/2;if(Ec(r)0?n<-wc+bc&&(n=-wc+bc):n>wc-bc&&(n=wc-bc);var e=i/Rc(Sh(n),r);return[e*Fc(r*t),i-e*Cc(r*t)]}return o.invert=function(t,n){var e=i-n,o=Oc(r)*Ic(t*t+e*e),a=Nc(t,Ec(e))*Oc(e);return e*r<0&&(a-=xc*Oc(t)*Oc(e)),[a/r,2*kc(Rc(i/o,1/r))-wc]},o}function kh(t,n){return[t,n]}function Nh(t,n){var e=Cc(t),r=t===n?Fc(t):(e-Cc(n))/(n-t),i=e/r+t;if(Ec(r)=0;)n+=e[r].value;else n=1;t.value=n}function Xh(t,n){t instanceof Map?(t=[void 0,t],void 0===n&&(n=Vh)):void 0===n&&(n=Gh);for(var e,r,i,o,a,u=new Zh(t),c=[u];e=c.pop();)if((i=n(e.data))&&(a=(i=Array.from(i)).length))for(e.children=i,o=a-1;o>=0;--o)c.push(r=i[o]=new Zh(i[o])),r.parent=e,r.depth=e.depth+1;return u.eachBefore(Wh)}function Gh(t){return t.children}function Vh(t){return Array.isArray(t)?t[1]:null}function $h(t){void 0!==t.data.value&&(t.value=t.data.value),t.data=t.data.data}function Wh(t){var n=0;do{t.height=n}while((t=t.parent)&&t.height<++n)}function Zh(t){this.data=t,this.depth=this.height=0,this.parent=null}function Kh(t){for(var n,e,r=0,i=(t=function(t){for(var n,e,r=t.length;r;)e=Math.random()*r--|0,n=t[r],t[r]=t[e],t[e]=n;return t}(Array.from(t))).length,o=[];r0&&e*e>r*r+i*i}function nd(t,n){for(var e=0;e(a*=a)?(r=(f+a-i)/(2*f),o=Math.sqrt(Math.max(0,a/f-r*r)),e.x=t.x-r*u-o*c,e.y=t.y-r*c+o*u):(r=(f+i-a)/(2*f),o=Math.sqrt(Math.max(0,i/f-r*r)),e.x=n.x+r*u-o*c,e.y=n.y+r*c+o*u)):(e.x=n.x+e.r,e.y=n.y)}function ad(t,n){var e=t.r+n.r-1e-6,r=n.x-t.x,i=n.y-t.y;return e>0&&e*e>r*r+i*i}function ud(t){var n=t._,e=t.next._,r=n.r+e.r,i=(n.x*e.r+e.x*n.r)/r,o=(n.y*e.r+e.y*n.r)/r;return i*i+o*o}function cd(t){this._=t,this.next=null,this.previous=null}function fd(t){if(!(i=(t=function(t){return"object"==typeof t&&"length"in t?t:Array.from(t)}(t)).length))return 0;var n,e,r,i,o,a,u,c,f,s,l;if((n=t[0]).x=0,n.y=0,!(i>1))return n.r;if(e=t[1],n.x=-e.r,e.x=n.r,e.y=0,!(i>2))return n.r+e.r;od(e,n,r=t[2]),n=new cd(n),e=new cd(e),r=new cd(r),n.next=r.previous=e,e.next=n.previous=r,r.next=e.previous=n;t:for(u=3;ubc&&--i>0);return[t/(.8707+(o=r*r)*(o*(o*o*o*(.003971-.001529*o)-.013791)-.131979)),r]},Ih.invert=xh(Yc),Uh.invert=xh((function(t){return 2*kc(t)})),Bh.invert=function(t,n){return[-n,2*kc(zc(t))-wc]},Zh.prototype=Xh.prototype={constructor:Zh,count:function(){return this.eachAfter(Hh)},each:function(t,n){let e=-1;for(const r of this)t.call(n,r,++e,this);return this},eachAfter:function(t,n){for(var e,r,i,o=this,a=[o],u=[],c=-1;o=a.pop();)if(u.push(o),e=o.children)for(r=0,i=e.length;r=0;--r)o.push(e[r]);return this},find:function(t,n){let e=-1;for(const r of this)if(t.call(n,r,++e,this))return r},sum:function(t){return this.eachAfter((function(n){for(var e=+t(n.data)||0,r=n.children,i=r&&r.length;--i>=0;)e+=r[i].value;n.value=e}))},sort:function(t){return this.eachBefore((function(n){n.children&&n.children.sort(t)}))},path:function(t){for(var n=this,e=function(t,n){if(t===n)return t;var e=t.ancestors(),r=n.ancestors(),i=null;t=e.pop(),n=r.pop();for(;t===n;)i=t,t=e.pop(),n=r.pop();return i}(n,t),r=[n];n!==e;)n=n.parent,r.push(n);for(var i=r.length;t!==e;)r.splice(i,0,t),t=t.parent;return r},ancestors:function(){for(var t=this,n=[t];t=t.parent;)n.push(t);return n},descendants:function(){return Array.from(this)},leaves:function(){var t=[];return this.eachBefore((function(n){n.children||t.push(n)})),t},links:function(){var t=this,n=[];return t.each((function(e){e!==t&&n.push({source:e.parent,target:e})})),n},copy:function(){return Xh(this).eachBefore($h)},[Symbol.iterator]:function*(){var t,n,e,r,i=this,o=[i];do{for(t=o.reverse(),o=[];i=t.pop();)if(yield i,n=i.children)for(e=0,r=n.length;eh&&(h=u),y=s*s*g,(d=Math.max(h/y,y/l))>p){s-=u;break}p=d}v.push(a={value:s,dice:c1?n:1)},e}(Pd);var qd=function t(n){function e(t,e,r,i,o){if((a=t._squarify)&&a.ratio===n)for(var a,u,c,f,s,l=-1,h=a.length,d=t.value;++l1?n:1)},e}(Pd);function Rd(t,n,e){return(n[0]-t[0])*(e[1]-t[1])-(n[1]-t[1])*(e[0]-t[0])}function Fd(t,n){return t[0]-n[0]||t[1]-n[1]}function Od(t){const n=t.length,e=[0,1];let r,i=2;for(r=2;r1&&Rd(t[e[i-2]],t[e[i-1]],t[r])<=0;)--i;e[i++]=r}return e.slice(0,i)}var Id=Math.random,Ud=function t(n){function e(t,e){return t=null==t?0:+t,e=null==e?1:+e,1===arguments.length?(e=t,t=0):e-=t,function(){return n()*e+t}}return e.source=t,e}(Id),Bd=function t(n){function e(t,e){return arguments.length<2&&(e=t,t=0),t=Math.floor(t),e=Math.floor(e)-t,function(){return Math.floor(n()*e+t)}}return e.source=t,e}(Id),Yd=function t(n){function e(t,e){var r,i;return t=null==t?0:+t,e=null==e?1:+e,function(){var o;if(null!=r)o=r,r=null;else do{r=2*n()-1,o=2*n()-1,i=r*r+o*o}while(!i||i>1);return t+e*o*Math.sqrt(-2*Math.log(i)/i)}}return e.source=t,e}(Id),Ld=function t(n){var e=Yd.source(n);function r(){var t=e.apply(this,arguments);return function(){return Math.exp(t())}}return r.source=t,r}(Id),jd=function t(n){function e(t){return(t=+t)<=0?()=>0:function(){for(var e=0,r=t;r>1;--r)e+=n();return e+r*n()}}return e.source=t,e}(Id),Hd=function t(n){var e=jd.source(n);function r(t){if(0==(t=+t))return n;var r=e(t);return function(){return r()/t}}return r.source=t,r}(Id),Xd=function t(n){function e(t){return function(){return-Math.log1p(-n())/t}}return e.source=t,e}(Id),Gd=function t(n){function e(t){if((t=+t)<0)throw new RangeError("invalid alpha");return t=1/-t,function(){return Math.pow(1-n(),t)}}return e.source=t,e}(Id),Vd=function t(n){function e(t){if((t=+t)<0||t>1)throw new RangeError("invalid p");return function(){return Math.floor(n()+t)}}return e.source=t,e}(Id),$d=function t(n){function e(t){if((t=+t)<0||t>1)throw new RangeError("invalid p");return 0===t?()=>1/0:1===t?()=>1:(t=Math.log1p(-t),function(){return 1+Math.floor(Math.log1p(-n())/t)})}return e.source=t,e}(Id),Wd=function t(n){var e=Yd.source(n)();function r(t,r){if((t=+t)<0)throw new RangeError("invalid k");if(0===t)return()=>0;if(r=null==r?1:+r,1===t)return()=>-Math.log1p(-n())*r;var i=(t<1?t+1:t)-1/3,o=1/(3*Math.sqrt(i)),a=t<1?()=>Math.pow(n(),1/t):()=>1;return function(){do{do{var t=e(),u=1+o*t}while(u<=0);u*=u*u;var c=1-n()}while(c>=1-.0331*t*t*t*t&&Math.log(c)>=.5*t*t+i*(1-u+Math.log(u)));return i*u*a()*r}}return r.source=t,r}(Id),Zd=function t(n){var e=Wd.source(n);function r(t,n){var r=e(t),i=e(n);return function(){var t=r();return 0===t?0:t/(t+i())}}return r.source=t,r}(Id),Kd=function t(n){var e=$d.source(n),r=Zd.source(n);function i(t,n){return t=+t,(n=+n)>=1?()=>t:n<=0?()=>0:function(){for(var i=0,o=t,a=n;o*a>16&&o*(1-a)>16;){var u=Math.floor((o+1)*a),c=r(u,o-u+1)();c<=a?(i+=u,o-=u,a=(a-c)/(1-c)):(o=u-1,a/=c)}for(var f=a<.5,s=e(f?a:1-a),l=s(),h=0;l<=o;++h)l+=s();return i+(f?h:o-h)}}return i.source=t,i}(Id),Qd=function t(n){function e(t,e,r){var i;return 0==(t=+t)?i=t=>-Math.log(t):(t=1/t,i=n=>Math.pow(n,t)),e=null==e?0:+e,r=null==r?1:+r,function(){return e+r*i(-Math.log1p(-n()))}}return e.source=t,e}(Id),Jd=function t(n){function e(t,e){return t=null==t?0:+t,e=null==e?1:+e,function(){return t+e*Math.tan(Math.PI*n())}}return e.source=t,e}(Id),tp=function t(n){function e(t,e){return t=null==t?0:+t,e=null==e?1:+e,function(){var r=n();return t+e*Math.log(r/(1-r))}}return e.source=t,e}(Id),np=function t(n){var e=Wd.source(n),r=Kd.source(n);function i(t){return function(){for(var i=0,o=t;o>16;){var a=Math.floor(.875*o),u=e(a)();if(u>o)return i+r(a-1,o/u)();i+=a,o-=u}for(var c=-Math.log1p(-n()),f=0;c<=o;++f)c-=Math.log1p(-n());return i+f}}return i.source=t,i}(Id);const ep=1/4294967296;function rp(t,n){switch(arguments.length){case 0:break;case 1:this.range(t);break;default:this.range(n).domain(t)}return this}function ip(t,n){switch(arguments.length){case 0:break;case 1:"function"==typeof t?this.interpolator(t):this.range(t);break;default:this.domain(t),"function"==typeof n?this.interpolator(n):this.range(n)}return this}const op=Symbol("implicit");function ap(){var t=new Map,n=[],e=[],r=op;function i(i){var o=i+"",a=t.get(o);if(!a){if(r!==op)return r;t.set(o,a=n.push(i))}return e[(a-1)%e.length]}return i.domain=function(e){if(!arguments.length)return n.slice();n=[],t=new Map;for(const r of e){const e=r+"";t.has(e)||t.set(e,n.push(r))}return i},i.range=function(t){return arguments.length?(e=Array.from(t),i):e.slice()},i.unknown=function(t){return arguments.length?(r=t,i):r},i.copy=function(){return ap(n,e).unknown(r)},rp.apply(i,arguments),i}function up(){var t,n,e=ap().unknown(void 0),r=e.domain,i=e.range,o=0,a=1,u=!1,c=0,f=0,s=.5;function l(){var e=r().length,l=an&&(e=t,t=n,n=e),function(e){return Math.max(t,Math.min(n,e))}}(a[0],a[t-1])),r=t>2?pp:dp,i=o=null,l}function l(n){return null==n||isNaN(n=+n)?e:(i||(i=r(a.map(t),u,c)))(t(f(n)))}return l.invert=function(e){return f(n((o||(o=r(u,a.map(t),_r)))(e)))},l.domain=function(t){return arguments.length?(a=Array.from(t,fp),s()):a.slice()},l.range=function(t){return arguments.length?(u=Array.from(t),s()):u.slice()},l.rangeRound=function(t){return u=Array.from(t),c=Ar,s()},l.clamp=function(t){return arguments.length?(f=!!t||lp,s()):f!==lp},l.interpolate=function(t){return arguments.length?(c=t,s()):c},l.unknown=function(t){return arguments.length?(e=t,l):e},function(e,r){return t=e,n=r,s()}}function vp(){return yp()(lp,lp)}function _p(n,e,r,i){var o,a=F(n,e,r);switch((i=ac(null==i?",f":i)).type){case"s":var u=Math.max(Math.abs(n),Math.abs(e));return null!=i.precision||isNaN(o=vc(a,u))||(i.precision=o),t.formatPrefix(i,u);case"":case"e":case"g":case"p":case"r":null!=i.precision||isNaN(o=_c(a,Math.max(Math.abs(n),Math.abs(e))))||(i.precision=o-("e"===i.type));break;case"f":case"%":null!=i.precision||isNaN(o=yc(a))||(i.precision=o-2*("%"===i.type))}return t.format(i)}function bp(t){var n=t.domain;return t.ticks=function(t){var e=n();return q(e[0],e[e.length-1],null==t?10:t)},t.tickFormat=function(t,e){var r=n();return _p(r[0],r[r.length-1],null==t?10:t,e)},t.nice=function(e){null==e&&(e=10);var r,i,o=n(),a=0,u=o.length-1,c=o[a],f=o[u],s=10;for(f0;){if((i=R(c,f,e))===r)return o[a]=c,o[u]=f,n(o);if(i>0)c=Math.floor(c/i)*i,f=Math.ceil(f/i)*i;else{if(!(i<0))break;c=Math.ceil(c*i)/i,f=Math.floor(f*i)/i}r=i}return t},t}function mp(t,n){var e,r=0,i=(t=t.slice()).length-1,o=t[r],a=t[i];return a0){for(;h<=d;++h)for(s=1,f=r(h);sc)break;g.push(l)}}else for(;h<=d;++h)for(s=a-1,f=r(h);s>=1;--s)if(!((l=f*s)c)break;g.push(l)}2*g.length0))return u;do{u.push(a=new Date(+e)),n(e,o),t(e)}while(a=n)for(;t(n),!e(n);)n.setTime(n-1)}),(function(t,r){if(t>=t)if(r<0)for(;++r<=0;)for(;n(t,-1),!e(t););else for(;--r>=0;)for(;n(t,1),!e(t););}))},e&&(i.count=function(n,r){return Ip.setTime(+n),Up.setTime(+r),t(Ip),t(Up),Math.floor(e(Ip,Up))},i.every=function(t){return t=Math.floor(t),isFinite(t)&&t>0?t>1?i.filter(r?function(n){return r(n)%t==0}:function(n){return i.count(0,n)%t==0}):i:null}),i}var Yp=Bp((function(){}),(function(t,n){t.setTime(+t+n)}),(function(t,n){return n-t}));Yp.every=function(t){return t=Math.floor(t),isFinite(t)&&t>0?t>1?Bp((function(n){n.setTime(Math.floor(n/t)*t)}),(function(n,e){n.setTime(+n+e*t)}),(function(n,e){return(e-n)/t})):Yp:null};var Lp=Yp.range;const jp=1e3,Hp=6e4,Xp=36e5,Gp=864e5,Vp=6048e5,$p=2592e6,Wp=31536e6;var Zp=Bp((function(t){t.setTime(t-t.getMilliseconds())}),(function(t,n){t.setTime(+t+n*jp)}),(function(t,n){return(n-t)/jp}),(function(t){return t.getUTCSeconds()})),Kp=Zp.range,Qp=Bp((function(t){t.setTime(t-t.getMilliseconds()-t.getSeconds()*jp)}),(function(t,n){t.setTime(+t+n*Hp)}),(function(t,n){return(n-t)/Hp}),(function(t){return t.getMinutes()})),Jp=Qp.range,tg=Bp((function(t){t.setTime(t-t.getMilliseconds()-t.getSeconds()*jp-t.getMinutes()*Hp)}),(function(t,n){t.setTime(+t+n*Xp)}),(function(t,n){return(n-t)/Xp}),(function(t){return t.getHours()})),ng=tg.range,eg=Bp((t=>t.setHours(0,0,0,0)),((t,n)=>t.setDate(t.getDate()+n)),((t,n)=>(n-t-(n.getTimezoneOffset()-t.getTimezoneOffset())*Hp)/Gp),(t=>t.getDate()-1)),rg=eg.range;function ig(t){return Bp((function(n){n.setDate(n.getDate()-(n.getDay()+7-t)%7),n.setHours(0,0,0,0)}),(function(t,n){t.setDate(t.getDate()+7*n)}),(function(t,n){return(n-t-(n.getTimezoneOffset()-t.getTimezoneOffset())*Hp)/Vp}))}var og=ig(0),ag=ig(1),ug=ig(2),cg=ig(3),fg=ig(4),sg=ig(5),lg=ig(6),hg=og.range,dg=ag.range,pg=ug.range,gg=cg.range,yg=fg.range,vg=sg.range,_g=lg.range,bg=Bp((function(t){t.setDate(1),t.setHours(0,0,0,0)}),(function(t,n){t.setMonth(t.getMonth()+n)}),(function(t,n){return n.getMonth()-t.getMonth()+12*(n.getFullYear()-t.getFullYear())}),(function(t){return t.getMonth()})),mg=bg.range,xg=Bp((function(t){t.setMonth(0,1),t.setHours(0,0,0,0)}),(function(t,n){t.setFullYear(t.getFullYear()+n)}),(function(t,n){return n.getFullYear()-t.getFullYear()}),(function(t){return t.getFullYear()}));xg.every=function(t){return isFinite(t=Math.floor(t))&&t>0?Bp((function(n){n.setFullYear(Math.floor(n.getFullYear()/t)*t),n.setMonth(0,1),n.setHours(0,0,0,0)}),(function(n,e){n.setFullYear(n.getFullYear()+e*t)})):null};var wg=xg.range,Mg=Bp((function(t){t.setUTCSeconds(0,0)}),(function(t,n){t.setTime(+t+n*Hp)}),(function(t,n){return(n-t)/Hp}),(function(t){return t.getUTCMinutes()})),Ag=Mg.range,Tg=Bp((function(t){t.setUTCMinutes(0,0,0)}),(function(t,n){t.setTime(+t+n*Xp)}),(function(t,n){return(n-t)/Xp}),(function(t){return t.getUTCHours()})),Sg=Tg.range,Eg=Bp((function(t){t.setUTCHours(0,0,0,0)}),(function(t,n){t.setUTCDate(t.getUTCDate()+n)}),(function(t,n){return(n-t)/Gp}),(function(t){return t.getUTCDate()-1})),kg=Eg.range;function Ng(t){return Bp((function(n){n.setUTCDate(n.getUTCDate()-(n.getUTCDay()+7-t)%7),n.setUTCHours(0,0,0,0)}),(function(t,n){t.setUTCDate(t.getUTCDate()+7*n)}),(function(t,n){return(n-t)/Vp}))}var Cg=Ng(0),Pg=Ng(1),zg=Ng(2),Dg=Ng(3),qg=Ng(4),Rg=Ng(5),Fg=Ng(6),Og=Cg.range,Ig=Pg.range,Ug=zg.range,Bg=Dg.range,Yg=qg.range,Lg=Rg.range,jg=Fg.range,Hg=Bp((function(t){t.setUTCDate(1),t.setUTCHours(0,0,0,0)}),(function(t,n){t.setUTCMonth(t.getUTCMonth()+n)}),(function(t,n){return n.getUTCMonth()-t.getUTCMonth()+12*(n.getUTCFullYear()-t.getUTCFullYear())}),(function(t){return t.getUTCMonth()})),Xg=Hg.range,Gg=Bp((function(t){t.setUTCMonth(0,1),t.setUTCHours(0,0,0,0)}),(function(t,n){t.setUTCFullYear(t.getUTCFullYear()+n)}),(function(t,n){return n.getUTCFullYear()-t.getUTCFullYear()}),(function(t){return t.getUTCFullYear()}));Gg.every=function(t){return isFinite(t=Math.floor(t))&&t>0?Bp((function(n){n.setUTCFullYear(Math.floor(n.getUTCFullYear()/t)*t),n.setUTCMonth(0,1),n.setUTCHours(0,0,0,0)}),(function(n,e){n.setUTCFullYear(n.getUTCFullYear()+e*t)})):null};var Vg=Gg.range;function $g(t,n,r,i,o,a){const u=[[Zp,1,jp],[Zp,5,5e3],[Zp,15,15e3],[Zp,30,3e4],[a,1,Hp],[a,5,3e5],[a,15,9e5],[a,30,18e5],[o,1,Xp],[o,3,108e5],[o,6,216e5],[o,12,432e5],[i,1,Gp],[i,2,1728e5],[r,1,Vp],[n,1,$p],[n,3,7776e6],[t,1,Wp]];function c(n,r,i){const o=Math.abs(r-n)/i,a=e((([,,t])=>t)).right(u,o);if(a===u.length)return t.every(F(n/Wp,r/Wp,i));if(0===a)return Yp.every(Math.max(F(n,r,i),1));const[c,f]=u[o/u[a-1][2]=12)]},q:function(t){return 1+~~(t.getMonth()/3)},Q:bv,s:mv,S:By,u:Yy,U:Ly,V:Hy,w:Xy,W:Gy,x:null,X:null,y:Vy,Y:Wy,Z:Ky,"%":_v},m={a:function(t){return a[t.getUTCDay()]},A:function(t){return o[t.getUTCDay()]},b:function(t){return c[t.getUTCMonth()]},B:function(t){return u[t.getUTCMonth()]},c:null,d:Qy,e:Qy,f:rv,g:pv,G:yv,H:Jy,I:tv,j:nv,L:ev,m:iv,M:ov,p:function(t){return i[+(t.getUTCHours()>=12)]},q:function(t){return 1+~~(t.getUTCMonth()/3)},Q:bv,s:mv,S:av,u:uv,U:cv,V:sv,w:lv,W:hv,x:null,X:null,y:dv,Y:gv,Z:vv,"%":_v},x={a:function(t,n,e){var r=d.exec(n.slice(e));return r?(t.w=p.get(r[0].toLowerCase()),e+r[0].length):-1},A:function(t,n,e){var r=l.exec(n.slice(e));return r?(t.w=h.get(r[0].toLowerCase()),e+r[0].length):-1},b:function(t,n,e){var r=v.exec(n.slice(e));return r?(t.m=_.get(r[0].toLowerCase()),e+r[0].length):-1},B:function(t,n,e){var r=g.exec(n.slice(e));return r?(t.m=y.get(r[0].toLowerCase()),e+r[0].length):-1},c:function(t,e,r){return A(t,n,e,r)},d:wy,e:wy,f:ky,g:_y,G:vy,H:Ay,I:Ay,j:My,L:Ey,m:xy,M:Ty,p:function(t,n,e){var r=f.exec(n.slice(e));return r?(t.p=s.get(r[0].toLowerCase()),e+r[0].length):-1},q:my,Q:Cy,s:Py,S:Sy,u:dy,U:py,V:gy,w:hy,W:yy,x:function(t,n,r){return A(t,e,n,r)},X:function(t,n,e){return A(t,r,n,e)},y:_y,Y:vy,Z:by,"%":Ny};function w(t,n){return function(e){var r,i,o,a=[],u=-1,c=0,f=t.length;for(e instanceof Date||(e=new Date(+e));++u53)return null;"w"in o||(o.w=1),"Z"in o?(i=(r=ty(ny(o.y,0,1))).getUTCDay(),r=i>4||0===i?Pg.ceil(r):Pg(r),r=Eg.offset(r,7*(o.V-1)),o.y=r.getUTCFullYear(),o.m=r.getUTCMonth(),o.d=r.getUTCDate()+(o.w+6)%7):(i=(r=Jg(ny(o.y,0,1))).getDay(),r=i>4||0===i?ag.ceil(r):ag(r),r=eg.offset(r,7*(o.V-1)),o.y=r.getFullYear(),o.m=r.getMonth(),o.d=r.getDate()+(o.w+6)%7)}else("W"in o||"U"in o)&&("w"in o||(o.w="u"in o?o.u%7:"W"in o?1:0),i="Z"in o?ty(ny(o.y,0,1)).getUTCDay():Jg(ny(o.y,0,1)).getDay(),o.m=0,o.d="W"in o?(o.w+6)%7+7*o.W-(i+5)%7:o.w+7*o.U-(i+6)%7);return"Z"in o?(o.H+=o.Z/100|0,o.M+=o.Z%100,ty(o)):Jg(o)}}function A(t,n,e,r){for(var i,o,a=0,u=n.length,c=e.length;a=c)return-1;if(37===(i=n.charCodeAt(a++))){if(i=n.charAt(a++),!(o=x[i in iy?n.charAt(a++):i])||(r=o(t,e,r))<0)return-1}else if(i!=e.charCodeAt(r++))return-1}return r}return b.x=w(e,b),b.X=w(r,b),b.c=w(n,b),m.x=w(e,m),m.X=w(r,m),m.c=w(n,m),{format:function(t){var n=w(t+="",b);return n.toString=function(){return t},n},parse:function(t){var n=M(t+="",!1);return n.toString=function(){return t},n},utcFormat:function(t){var n=w(t+="",m);return n.toString=function(){return t},n},utcParse:function(t){var n=M(t+="",!0);return n.toString=function(){return t},n}}}var ry,iy={"-":"",_:" ",0:"0"},oy=/^\s*\d+/,ay=/^%/,uy=/[\\^$*+?|[\]().{}]/g;function cy(t,n,e){var r=t<0?"-":"",i=(r?-t:t)+"",o=i.length;return r+(o[t.toLowerCase(),n])))}function hy(t,n,e){var r=oy.exec(n.slice(e,e+1));return r?(t.w=+r[0],e+r[0].length):-1}function dy(t,n,e){var r=oy.exec(n.slice(e,e+1));return r?(t.u=+r[0],e+r[0].length):-1}function py(t,n,e){var r=oy.exec(n.slice(e,e+2));return r?(t.U=+r[0],e+r[0].length):-1}function gy(t,n,e){var r=oy.exec(n.slice(e,e+2));return r?(t.V=+r[0],e+r[0].length):-1}function yy(t,n,e){var r=oy.exec(n.slice(e,e+2));return r?(t.W=+r[0],e+r[0].length):-1}function vy(t,n,e){var r=oy.exec(n.slice(e,e+4));return r?(t.y=+r[0],e+r[0].length):-1}function _y(t,n,e){var r=oy.exec(n.slice(e,e+2));return r?(t.y=+r[0]+(+r[0]>68?1900:2e3),e+r[0].length):-1}function by(t,n,e){var r=/^(Z)|([+-]\d\d)(?::?(\d\d))?/.exec(n.slice(e,e+6));return r?(t.Z=r[1]?0:-(r[2]+(r[3]||"00")),e+r[0].length):-1}function my(t,n,e){var r=oy.exec(n.slice(e,e+1));return r?(t.q=3*r[0]-3,e+r[0].length):-1}function xy(t,n,e){var r=oy.exec(n.slice(e,e+2));return r?(t.m=r[0]-1,e+r[0].length):-1}function wy(t,n,e){var r=oy.exec(n.slice(e,e+2));return r?(t.d=+r[0],e+r[0].length):-1}function My(t,n,e){var r=oy.exec(n.slice(e,e+3));return r?(t.m=0,t.d=+r[0],e+r[0].length):-1}function Ay(t,n,e){var r=oy.exec(n.slice(e,e+2));return r?(t.H=+r[0],e+r[0].length):-1}function Ty(t,n,e){var r=oy.exec(n.slice(e,e+2));return r?(t.M=+r[0],e+r[0].length):-1}function Sy(t,n,e){var r=oy.exec(n.slice(e,e+2));return r?(t.S=+r[0],e+r[0].length):-1}function Ey(t,n,e){var r=oy.exec(n.slice(e,e+3));return r?(t.L=+r[0],e+r[0].length):-1}function ky(t,n,e){var r=oy.exec(n.slice(e,e+6));return r?(t.L=Math.floor(r[0]/1e3),e+r[0].length):-1}function Ny(t,n,e){var r=ay.exec(n.slice(e,e+1));return r?e+r[0].length:-1}function Cy(t,n,e){var r=oy.exec(n.slice(e));return r?(t.Q=+r[0],e+r[0].length):-1}function Py(t,n,e){var r=oy.exec(n.slice(e));return r?(t.s=+r[0],e+r[0].length):-1}function zy(t,n){return cy(t.getDate(),n,2)}function Dy(t,n){return cy(t.getHours(),n,2)}function qy(t,n){return cy(t.getHours()%12||12,n,2)}function Ry(t,n){return cy(1+eg.count(xg(t),t),n,3)}function Fy(t,n){return cy(t.getMilliseconds(),n,3)}function Oy(t,n){return Fy(t,n)+"000"}function Iy(t,n){return cy(t.getMonth()+1,n,2)}function Uy(t,n){return cy(t.getMinutes(),n,2)}function By(t,n){return cy(t.getSeconds(),n,2)}function Yy(t){var n=t.getDay();return 0===n?7:n}function Ly(t,n){return cy(og.count(xg(t)-1,t),n,2)}function jy(t){var n=t.getDay();return n>=4||0===n?fg(t):fg.ceil(t)}function Hy(t,n){return t=jy(t),cy(fg.count(xg(t),t)+(4===xg(t).getDay()),n,2)}function Xy(t){return t.getDay()}function Gy(t,n){return cy(ag.count(xg(t)-1,t),n,2)}function Vy(t,n){return cy(t.getFullYear()%100,n,2)}function $y(t,n){return cy((t=jy(t)).getFullYear()%100,n,2)}function Wy(t,n){return cy(t.getFullYear()%1e4,n,4)}function Zy(t,n){var e=t.getDay();return cy((t=e>=4||0===e?fg(t):fg.ceil(t)).getFullYear()%1e4,n,4)}function Ky(t){var n=t.getTimezoneOffset();return(n>0?"-":(n*=-1,"+"))+cy(n/60|0,"0",2)+cy(n%60,"0",2)}function Qy(t,n){return cy(t.getUTCDate(),n,2)}function Jy(t,n){return cy(t.getUTCHours(),n,2)}function tv(t,n){return cy(t.getUTCHours()%12||12,n,2)}function nv(t,n){return cy(1+Eg.count(Gg(t),t),n,3)}function ev(t,n){return cy(t.getUTCMilliseconds(),n,3)}function rv(t,n){return ev(t,n)+"000"}function iv(t,n){return cy(t.getUTCMonth()+1,n,2)}function ov(t,n){return cy(t.getUTCMinutes(),n,2)}function av(t,n){return cy(t.getUTCSeconds(),n,2)}function uv(t){var n=t.getUTCDay();return 0===n?7:n}function cv(t,n){return cy(Cg.count(Gg(t)-1,t),n,2)}function fv(t){var n=t.getUTCDay();return n>=4||0===n?qg(t):qg.ceil(t)}function sv(t,n){return t=fv(t),cy(qg.count(Gg(t),t)+(4===Gg(t).getUTCDay()),n,2)}function lv(t){return t.getUTCDay()}function hv(t,n){return cy(Pg.count(Gg(t)-1,t),n,2)}function dv(t,n){return cy(t.getUTCFullYear()%100,n,2)}function pv(t,n){return cy((t=fv(t)).getUTCFullYear()%100,n,2)}function gv(t,n){return cy(t.getUTCFullYear()%1e4,n,4)}function yv(t,n){var e=t.getUTCDay();return cy((t=e>=4||0===e?qg(t):qg.ceil(t)).getUTCFullYear()%1e4,n,4)}function vv(){return"+0000"}function _v(){return"%"}function bv(t){return+t}function mv(t){return Math.floor(+t/1e3)}function xv(n){return ry=ey(n),t.timeFormat=ry.format,t.timeParse=ry.parse,t.utcFormat=ry.utcFormat,t.utcParse=ry.utcParse,ry}t.timeFormat=void 0,t.timeParse=void 0,t.utcFormat=void 0,t.utcParse=void 0,xv({dateTime:"%x, %X",date:"%-m/%-d/%Y",time:"%-I:%M:%S %p",periods:["AM","PM"],days:["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"],shortDays:["Sun","Mon","Tue","Wed","Thu","Fri","Sat"],months:["January","February","March","April","May","June","July","August","September","October","November","December"],shortMonths:["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"]});var wv="%Y-%m-%dT%H:%M:%S.%LZ";var Mv=Date.prototype.toISOString?function(t){return t.toISOString()}:t.utcFormat(wv);var Av=+new Date("2000-01-01T00:00:00.000Z")?function(t){var n=new Date(t);return isNaN(n)?null:n}:t.utcParse(wv);function Tv(t){return new Date(t)}function Sv(t){return t instanceof Date?+t:+new Date(+t)}function Ev(t,n,e,r,i,o,a,u,c,f){var s=vp(),l=s.invert,h=s.domain,d=f(".%L"),p=f(":%S"),g=f("%I:%M"),y=f("%I %p"),v=f("%a %d"),_=f("%b %d"),b=f("%B"),m=f("%Y");function x(t){return(c(t)hr(t[t.length-1]),Xv=new Array(3).concat("d8b365f5f5f55ab4ac","a6611adfc27d80cdc1018571","a6611adfc27df5f5f580cdc1018571","8c510ad8b365f6e8c3c7eae55ab4ac01665e","8c510ad8b365f6e8c3f5f5f5c7eae55ab4ac01665e","8c510abf812ddfc27df6e8c3c7eae580cdc135978f01665e","8c510abf812ddfc27df6e8c3f5f5f5c7eae580cdc135978f01665e","5430058c510abf812ddfc27df6e8c3c7eae580cdc135978f01665e003c30","5430058c510abf812ddfc27df6e8c3f5f5f5c7eae580cdc135978f01665e003c30").map(Dv),Gv=Hv(Xv),Vv=new Array(3).concat("af8dc3f7f7f77fbf7b","7b3294c2a5cfa6dba0008837","7b3294c2a5cff7f7f7a6dba0008837","762a83af8dc3e7d4e8d9f0d37fbf7b1b7837","762a83af8dc3e7d4e8f7f7f7d9f0d37fbf7b1b7837","762a839970abc2a5cfe7d4e8d9f0d3a6dba05aae611b7837","762a839970abc2a5cfe7d4e8f7f7f7d9f0d3a6dba05aae611b7837","40004b762a839970abc2a5cfe7d4e8d9f0d3a6dba05aae611b783700441b","40004b762a839970abc2a5cfe7d4e8f7f7f7d9f0d3a6dba05aae611b783700441b").map(Dv),$v=Hv(Vv),Wv=new Array(3).concat("e9a3c9f7f7f7a1d76a","d01c8bf1b6dab8e1864dac26","d01c8bf1b6daf7f7f7b8e1864dac26","c51b7de9a3c9fde0efe6f5d0a1d76a4d9221","c51b7de9a3c9fde0eff7f7f7e6f5d0a1d76a4d9221","c51b7dde77aef1b6dafde0efe6f5d0b8e1867fbc414d9221","c51b7dde77aef1b6dafde0eff7f7f7e6f5d0b8e1867fbc414d9221","8e0152c51b7dde77aef1b6dafde0efe6f5d0b8e1867fbc414d9221276419","8e0152c51b7dde77aef1b6dafde0eff7f7f7e6f5d0b8e1867fbc414d9221276419").map(Dv),Zv=Hv(Wv),Kv=new Array(3).concat("998ec3f7f7f7f1a340","5e3c99b2abd2fdb863e66101","5e3c99b2abd2f7f7f7fdb863e66101","542788998ec3d8daebfee0b6f1a340b35806","542788998ec3d8daebf7f7f7fee0b6f1a340b35806","5427888073acb2abd2d8daebfee0b6fdb863e08214b35806","5427888073acb2abd2d8daebf7f7f7fee0b6fdb863e08214b35806","2d004b5427888073acb2abd2d8daebfee0b6fdb863e08214b358067f3b08","2d004b5427888073acb2abd2d8daebf7f7f7fee0b6fdb863e08214b358067f3b08").map(Dv),Qv=Hv(Kv),Jv=new Array(3).concat("ef8a62f7f7f767a9cf","ca0020f4a58292c5de0571b0","ca0020f4a582f7f7f792c5de0571b0","b2182bef8a62fddbc7d1e5f067a9cf2166ac","b2182bef8a62fddbc7f7f7f7d1e5f067a9cf2166ac","b2182bd6604df4a582fddbc7d1e5f092c5de4393c32166ac","b2182bd6604df4a582fddbc7f7f7f7d1e5f092c5de4393c32166ac","67001fb2182bd6604df4a582fddbc7d1e5f092c5de4393c32166ac053061","67001fb2182bd6604df4a582fddbc7f7f7f7d1e5f092c5de4393c32166ac053061").map(Dv),t_=Hv(Jv),n_=new Array(3).concat("ef8a62ffffff999999","ca0020f4a582bababa404040","ca0020f4a582ffffffbababa404040","b2182bef8a62fddbc7e0e0e09999994d4d4d","b2182bef8a62fddbc7ffffffe0e0e09999994d4d4d","b2182bd6604df4a582fddbc7e0e0e0bababa8787874d4d4d","b2182bd6604df4a582fddbc7ffffffe0e0e0bababa8787874d4d4d","67001fb2182bd6604df4a582fddbc7e0e0e0bababa8787874d4d4d1a1a1a","67001fb2182bd6604df4a582fddbc7ffffffe0e0e0bababa8787874d4d4d1a1a1a").map(Dv),e_=Hv(n_),r_=new Array(3).concat("fc8d59ffffbf91bfdb","d7191cfdae61abd9e92c7bb6","d7191cfdae61ffffbfabd9e92c7bb6","d73027fc8d59fee090e0f3f891bfdb4575b4","d73027fc8d59fee090ffffbfe0f3f891bfdb4575b4","d73027f46d43fdae61fee090e0f3f8abd9e974add14575b4","d73027f46d43fdae61fee090ffffbfe0f3f8abd9e974add14575b4","a50026d73027f46d43fdae61fee090e0f3f8abd9e974add14575b4313695","a50026d73027f46d43fdae61fee090ffffbfe0f3f8abd9e974add14575b4313695").map(Dv),i_=Hv(r_),o_=new Array(3).concat("fc8d59ffffbf91cf60","d7191cfdae61a6d96a1a9641","d7191cfdae61ffffbfa6d96a1a9641","d73027fc8d59fee08bd9ef8b91cf601a9850","d73027fc8d59fee08bffffbfd9ef8b91cf601a9850","d73027f46d43fdae61fee08bd9ef8ba6d96a66bd631a9850","d73027f46d43fdae61fee08bffffbfd9ef8ba6d96a66bd631a9850","a50026d73027f46d43fdae61fee08bd9ef8ba6d96a66bd631a9850006837","a50026d73027f46d43fdae61fee08bffffbfd9ef8ba6d96a66bd631a9850006837").map(Dv),a_=Hv(o_),u_=new Array(3).concat("fc8d59ffffbf99d594","d7191cfdae61abdda42b83ba","d7191cfdae61ffffbfabdda42b83ba","d53e4ffc8d59fee08be6f59899d5943288bd","d53e4ffc8d59fee08bffffbfe6f59899d5943288bd","d53e4ff46d43fdae61fee08be6f598abdda466c2a53288bd","d53e4ff46d43fdae61fee08bffffbfe6f598abdda466c2a53288bd","9e0142d53e4ff46d43fdae61fee08be6f598abdda466c2a53288bd5e4fa2","9e0142d53e4ff46d43fdae61fee08bffffbfe6f598abdda466c2a53288bd5e4fa2").map(Dv),c_=Hv(u_),f_=new Array(3).concat("e5f5f999d8c92ca25f","edf8fbb2e2e266c2a4238b45","edf8fbb2e2e266c2a42ca25f006d2c","edf8fbccece699d8c966c2a42ca25f006d2c","edf8fbccece699d8c966c2a441ae76238b45005824","f7fcfde5f5f9ccece699d8c966c2a441ae76238b45005824","f7fcfde5f5f9ccece699d8c966c2a441ae76238b45006d2c00441b").map(Dv),s_=Hv(f_),l_=new Array(3).concat("e0ecf49ebcda8856a7","edf8fbb3cde38c96c688419d","edf8fbb3cde38c96c68856a7810f7c","edf8fbbfd3e69ebcda8c96c68856a7810f7c","edf8fbbfd3e69ebcda8c96c68c6bb188419d6e016b","f7fcfde0ecf4bfd3e69ebcda8c96c68c6bb188419d6e016b","f7fcfde0ecf4bfd3e69ebcda8c96c68c6bb188419d810f7c4d004b").map(Dv),h_=Hv(l_),d_=new Array(3).concat("e0f3dba8ddb543a2ca","f0f9e8bae4bc7bccc42b8cbe","f0f9e8bae4bc7bccc443a2ca0868ac","f0f9e8ccebc5a8ddb57bccc443a2ca0868ac","f0f9e8ccebc5a8ddb57bccc44eb3d32b8cbe08589e","f7fcf0e0f3dbccebc5a8ddb57bccc44eb3d32b8cbe08589e","f7fcf0e0f3dbccebc5a8ddb57bccc44eb3d32b8cbe0868ac084081").map(Dv),p_=Hv(d_),g_=new Array(3).concat("fee8c8fdbb84e34a33","fef0d9fdcc8afc8d59d7301f","fef0d9fdcc8afc8d59e34a33b30000","fef0d9fdd49efdbb84fc8d59e34a33b30000","fef0d9fdd49efdbb84fc8d59ef6548d7301f990000","fff7ecfee8c8fdd49efdbb84fc8d59ef6548d7301f990000","fff7ecfee8c8fdd49efdbb84fc8d59ef6548d7301fb300007f0000").map(Dv),y_=Hv(g_),v_=new Array(3).concat("ece2f0a6bddb1c9099","f6eff7bdc9e167a9cf02818a","f6eff7bdc9e167a9cf1c9099016c59","f6eff7d0d1e6a6bddb67a9cf1c9099016c59","f6eff7d0d1e6a6bddb67a9cf3690c002818a016450","fff7fbece2f0d0d1e6a6bddb67a9cf3690c002818a016450","fff7fbece2f0d0d1e6a6bddb67a9cf3690c002818a016c59014636").map(Dv),__=Hv(v_),b_=new Array(3).concat("ece7f2a6bddb2b8cbe","f1eef6bdc9e174a9cf0570b0","f1eef6bdc9e174a9cf2b8cbe045a8d","f1eef6d0d1e6a6bddb74a9cf2b8cbe045a8d","f1eef6d0d1e6a6bddb74a9cf3690c00570b0034e7b","fff7fbece7f2d0d1e6a6bddb74a9cf3690c00570b0034e7b","fff7fbece7f2d0d1e6a6bddb74a9cf3690c00570b0045a8d023858").map(Dv),m_=Hv(b_),x_=new Array(3).concat("e7e1efc994c7dd1c77","f1eef6d7b5d8df65b0ce1256","f1eef6d7b5d8df65b0dd1c77980043","f1eef6d4b9dac994c7df65b0dd1c77980043","f1eef6d4b9dac994c7df65b0e7298ace125691003f","f7f4f9e7e1efd4b9dac994c7df65b0e7298ace125691003f","f7f4f9e7e1efd4b9dac994c7df65b0e7298ace125698004367001f").map(Dv),w_=Hv(x_),M_=new Array(3).concat("fde0ddfa9fb5c51b8a","feebe2fbb4b9f768a1ae017e","feebe2fbb4b9f768a1c51b8a7a0177","feebe2fcc5c0fa9fb5f768a1c51b8a7a0177","feebe2fcc5c0fa9fb5f768a1dd3497ae017e7a0177","fff7f3fde0ddfcc5c0fa9fb5f768a1dd3497ae017e7a0177","fff7f3fde0ddfcc5c0fa9fb5f768a1dd3497ae017e7a017749006a").map(Dv),A_=Hv(M_),T_=new Array(3).concat("edf8b17fcdbb2c7fb8","ffffcca1dab441b6c4225ea8","ffffcca1dab441b6c42c7fb8253494","ffffccc7e9b47fcdbb41b6c42c7fb8253494","ffffccc7e9b47fcdbb41b6c41d91c0225ea80c2c84","ffffd9edf8b1c7e9b47fcdbb41b6c41d91c0225ea80c2c84","ffffd9edf8b1c7e9b47fcdbb41b6c41d91c0225ea8253494081d58").map(Dv),S_=Hv(T_),E_=new Array(3).concat("f7fcb9addd8e31a354","ffffccc2e69978c679238443","ffffccc2e69978c67931a354006837","ffffccd9f0a3addd8e78c67931a354006837","ffffccd9f0a3addd8e78c67941ab5d238443005a32","ffffe5f7fcb9d9f0a3addd8e78c67941ab5d238443005a32","ffffe5f7fcb9d9f0a3addd8e78c67941ab5d238443006837004529").map(Dv),k_=Hv(E_),N_=new Array(3).concat("fff7bcfec44fd95f0e","ffffd4fed98efe9929cc4c02","ffffd4fed98efe9929d95f0e993404","ffffd4fee391fec44ffe9929d95f0e993404","ffffd4fee391fec44ffe9929ec7014cc4c028c2d04","ffffe5fff7bcfee391fec44ffe9929ec7014cc4c028c2d04","ffffe5fff7bcfee391fec44ffe9929ec7014cc4c02993404662506").map(Dv),C_=Hv(N_),P_=new Array(3).concat("ffeda0feb24cf03b20","ffffb2fecc5cfd8d3ce31a1c","ffffb2fecc5cfd8d3cf03b20bd0026","ffffb2fed976feb24cfd8d3cf03b20bd0026","ffffb2fed976feb24cfd8d3cfc4e2ae31a1cb10026","ffffccffeda0fed976feb24cfd8d3cfc4e2ae31a1cb10026","ffffccffeda0fed976feb24cfd8d3cfc4e2ae31a1cbd0026800026").map(Dv),z_=Hv(P_),D_=new Array(3).concat("deebf79ecae13182bd","eff3ffbdd7e76baed62171b5","eff3ffbdd7e76baed63182bd08519c","eff3ffc6dbef9ecae16baed63182bd08519c","eff3ffc6dbef9ecae16baed64292c62171b5084594","f7fbffdeebf7c6dbef9ecae16baed64292c62171b5084594","f7fbffdeebf7c6dbef9ecae16baed64292c62171b508519c08306b").map(Dv),q_=Hv(D_),R_=new Array(3).concat("e5f5e0a1d99b31a354","edf8e9bae4b374c476238b45","edf8e9bae4b374c47631a354006d2c","edf8e9c7e9c0a1d99b74c47631a354006d2c","edf8e9c7e9c0a1d99b74c47641ab5d238b45005a32","f7fcf5e5f5e0c7e9c0a1d99b74c47641ab5d238b45005a32","f7fcf5e5f5e0c7e9c0a1d99b74c47641ab5d238b45006d2c00441b").map(Dv),F_=Hv(R_),O_=new Array(3).concat("f0f0f0bdbdbd636363","f7f7f7cccccc969696525252","f7f7f7cccccc969696636363252525","f7f7f7d9d9d9bdbdbd969696636363252525","f7f7f7d9d9d9bdbdbd969696737373525252252525","fffffff0f0f0d9d9d9bdbdbd969696737373525252252525","fffffff0f0f0d9d9d9bdbdbd969696737373525252252525000000").map(Dv),I_=Hv(O_),U_=new Array(3).concat("efedf5bcbddc756bb1","f2f0f7cbc9e29e9ac86a51a3","f2f0f7cbc9e29e9ac8756bb154278f","f2f0f7dadaebbcbddc9e9ac8756bb154278f","f2f0f7dadaebbcbddc9e9ac8807dba6a51a34a1486","fcfbfdefedf5dadaebbcbddc9e9ac8807dba6a51a34a1486","fcfbfdefedf5dadaebbcbddc9e9ac8807dba6a51a354278f3f007d").map(Dv),B_=Hv(U_),Y_=new Array(3).concat("fee0d2fc9272de2d26","fee5d9fcae91fb6a4acb181d","fee5d9fcae91fb6a4ade2d26a50f15","fee5d9fcbba1fc9272fb6a4ade2d26a50f15","fee5d9fcbba1fc9272fb6a4aef3b2ccb181d99000d","fff5f0fee0d2fcbba1fc9272fb6a4aef3b2ccb181d99000d","fff5f0fee0d2fcbba1fc9272fb6a4aef3b2ccb181da50f1567000d").map(Dv),L_=Hv(Y_),j_=new Array(3).concat("fee6cefdae6be6550d","feeddefdbe85fd8d3cd94701","feeddefdbe85fd8d3ce6550da63603","feeddefdd0a2fdae6bfd8d3ce6550da63603","feeddefdd0a2fdae6bfd8d3cf16913d948018c2d04","fff5ebfee6cefdd0a2fdae6bfd8d3cf16913d948018c2d04","fff5ebfee6cefdd0a2fdae6bfd8d3cf16913d94801a636037f2704").map(Dv),H_=Hv(j_);var X_=Lr(tr(300,.5,0),tr(-240,.5,1)),G_=Lr(tr(-100,.75,.35),tr(80,1.5,.8)),V_=Lr(tr(260,.75,.35),tr(80,1.5,.8)),$_=tr();var W_=ve(),Z_=Math.PI/3,K_=2*Math.PI/3;function Q_(t){var n=t.length;return function(e){return t[Math.max(0,Math.min(n-1,Math.floor(e*n)))]}}var J_=Q_(Dv("44015444025645045745055946075a46085c460a5d460b5e470d60470e6147106347116447136548146748166848176948186a481a6c481b6d481c6e481d6f481f70482071482173482374482475482576482677482878482979472a7a472c7a472d7b472e7c472f7d46307e46327e46337f463480453581453781453882443983443a83443b84433d84433e85423f854240864241864142874144874045884046883f47883f48893e49893e4a893e4c8a3d4d8a3d4e8a3c4f8a3c508b3b518b3b528b3a538b3a548c39558c39568c38588c38598c375a8c375b8d365c8d365d8d355e8d355f8d34608d34618d33628d33638d32648e32658e31668e31678e31688e30698e306a8e2f6b8e2f6c8e2e6d8e2e6e8e2e6f8e2d708e2d718e2c718e2c728e2c738e2b748e2b758e2a768e2a778e2a788e29798e297a8e297b8e287c8e287d8e277e8e277f8e27808e26818e26828e26828e25838e25848e25858e24868e24878e23888e23898e238a8d228b8d228c8d228d8d218e8d218f8d21908d21918c20928c20928c20938c1f948c1f958b1f968b1f978b1f988b1f998a1f9a8a1e9b8a1e9c891e9d891f9e891f9f881fa0881fa1881fa1871fa28720a38620a48621a58521a68522a78522a88423a98324aa8325ab8225ac8226ad8127ad8128ae8029af7f2ab07f2cb17e2db27d2eb37c2fb47c31b57b32b67a34b67935b77937b87838b9773aba763bbb753dbc743fbc7340bd7242be7144bf7046c06f48c16e4ac16d4cc26c4ec36b50c46a52c56954c56856c66758c7655ac8645cc8635ec96260ca6063cb5f65cb5e67cc5c69cd5b6ccd5a6ece5870cf5773d05675d05477d1537ad1517cd2507fd34e81d34d84d44b86d54989d5488bd6468ed64590d74393d74195d84098d83e9bd93c9dd93ba0da39a2da37a5db36a8db34aadc32addc30b0dd2fb2dd2db5de2bb8de29bade28bddf26c0df25c2df23c5e021c8e020cae11fcde11dd0e11cd2e21bd5e21ad8e219dae319dde318dfe318e2e418e5e419e7e419eae51aece51befe51cf1e51df4e61ef6e620f8e621fbe723fde725")),tb=Q_(Dv("00000401000501010601010802010902020b02020d03030f03031204041405041606051806051a07061c08071e0907200a08220b09240c09260d0a290e0b2b100b2d110c2f120d31130d34140e36150e38160f3b180f3d19103f1a10421c10441d11471e114920114b21114e22115024125325125527125829115a2a115c2c115f2d11612f116331116533106734106936106b38106c390f6e3b0f703d0f713f0f72400f74420f75440f764510774710784910784a10794c117a4e117b4f127b51127c52137c54137d56147d57157e59157e5a167e5c167f5d177f5f187f601880621980641a80651a80671b80681c816a1c816b1d816d1d816e1e81701f81721f817320817521817621817822817922827b23827c23827e24828025828125818326818426818627818827818928818b29818c29818e2a81902a81912b81932b80942c80962c80982d80992d809b2e7f9c2e7f9e2f7fa02f7fa1307ea3307ea5317ea6317da8327daa337dab337cad347cae347bb0357bb2357bb3367ab5367ab73779b83779ba3878bc3978bd3977bf3a77c03a76c23b75c43c75c53c74c73d73c83e73ca3e72cc3f71cd4071cf4070d0416fd2426fd3436ed5446dd6456cd8456cd9466bdb476adc4869de4968df4a68e04c67e24d66e34e65e44f64e55064e75263e85362e95462ea5661eb5760ec5860ed5a5fee5b5eef5d5ef05f5ef1605df2625df2645cf3655cf4675cf4695cf56b5cf66c5cf66e5cf7705cf7725cf8745cf8765cf9785df9795df97b5dfa7d5efa7f5efa815ffb835ffb8560fb8761fc8961fc8a62fc8c63fc8e64fc9065fd9266fd9467fd9668fd9869fd9a6afd9b6bfe9d6cfe9f6dfea16efea36ffea571fea772fea973feaa74feac76feae77feb078feb27afeb47bfeb67cfeb77efeb97ffebb81febd82febf84fec185fec287fec488fec68afec88cfeca8dfecc8ffecd90fecf92fed194fed395fed597fed799fed89afdda9cfddc9efddea0fde0a1fde2a3fde3a5fde5a7fde7a9fde9aafdebacfcecaefceeb0fcf0b2fcf2b4fcf4b6fcf6b8fcf7b9fcf9bbfcfbbdfcfdbf")),nb=Q_(Dv("00000401000501010601010802010a02020c02020e03021004031204031405041706041907051b08051d09061f0a07220b07240c08260d08290e092b10092d110a30120a32140b34150b37160b39180c3c190c3e1b0c411c0c431e0c451f0c48210c4a230c4c240c4f260c51280b53290b552b0b572d0b592f0a5b310a5c320a5e340a5f3609613809623909633b09643d09653e0966400a67420a68440a68450a69470b6a490b6a4a0c6b4c0c6b4d0d6c4f0d6c510e6c520e6d540f6d550f6d57106e59106e5a116e5c126e5d126e5f136e61136e62146e64156e65156e67166e69166e6a176e6c186e6d186e6f196e71196e721a6e741a6e751b6e771c6d781c6d7a1d6d7c1d6d7d1e6d7f1e6c801f6c82206c84206b85216b87216b88226a8a226a8c23698d23698f24699025689225689326679526679727669827669a28659b29649d29649f2a63a02a63a22b62a32c61a52c60a62d60a82e5fa92e5eab2f5ead305dae305cb0315bb1325ab3325ab43359b63458b73557b93556ba3655bc3754bd3853bf3952c03a51c13a50c33b4fc43c4ec63d4dc73e4cc83f4bca404acb4149cc4248ce4347cf4446d04545d24644d34743d44842d54a41d74b3fd84c3ed94d3dda4e3cdb503bdd513ade5238df5337e05536e15635e25734e35933e45a31e55c30e65d2fe75e2ee8602de9612bea632aeb6429eb6628ec6726ed6925ee6a24ef6c23ef6e21f06f20f1711ff1731df2741cf3761bf37819f47918f57b17f57d15f67e14f68013f78212f78410f8850ff8870ef8890cf98b0bf98c0af98e09fa9008fa9207fa9407fb9606fb9706fb9906fb9b06fb9d07fc9f07fca108fca309fca50afca60cfca80dfcaa0ffcac11fcae12fcb014fcb216fcb418fbb61afbb81dfbba1ffbbc21fbbe23fac026fac228fac42afac62df9c72ff9c932f9cb35f8cd37f8cf3af7d13df7d340f6d543f6d746f5d949f5db4cf4dd4ff4df53f4e156f3e35af3e55df2e661f2e865f2ea69f1ec6df1ed71f1ef75f1f179f2f27df2f482f3f586f3f68af4f88ef5f992f6fa96f8fb9af9fc9dfafda1fcffa4")),eb=Q_(Dv("0d088710078813078916078a19068c1b068d1d068e20068f2206902406912605912805922a05932c05942e05952f059631059733059735049837049938049a3a049a3c049b3e049c3f049c41049d43039e44039e46039f48039f4903a04b03a14c02a14e02a25002a25102a35302a35502a45601a45801a45901a55b01a55c01a65e01a66001a66100a76300a76400a76600a76700a86900a86a00a86c00a86e00a86f00a87100a87201a87401a87501a87701a87801a87a02a87b02a87d03a87e03a88004a88104a78305a78405a78606a68707a68808a68a09a58b0aa58d0ba58e0ca48f0da4910ea3920fa39410a29511a19613a19814a099159f9a169f9c179e9d189d9e199da01a9ca11b9ba21d9aa31e9aa51f99a62098a72197a82296aa2395ab2494ac2694ad2793ae2892b02991b12a90b22b8fb32c8eb42e8db52f8cb6308bb7318ab83289ba3388bb3488bc3587bd3786be3885bf3984c03a83c13b82c23c81c33d80c43e7fc5407ec6417dc7427cc8437bc9447aca457acb4679cc4778cc4977cd4a76ce4b75cf4c74d04d73d14e72d24f71d35171d45270d5536fd5546ed6556dd7566cd8576bd9586ada5a6ada5b69db5c68dc5d67dd5e66de5f65de6164df6263e06363e16462e26561e26660e3685fe4695ee56a5de56b5de66c5ce76e5be76f5ae87059e97158e97257ea7457eb7556eb7655ec7754ed7953ed7a52ee7b51ef7c51ef7e50f07f4ff0804ef1814df1834cf2844bf3854bf3874af48849f48948f58b47f58c46f68d45f68f44f79044f79143f79342f89441f89540f9973ff9983ef99a3efa9b3dfa9c3cfa9e3bfb9f3afba139fba238fca338fca537fca636fca835fca934fdab33fdac33fdae32fdaf31fdb130fdb22ffdb42ffdb52efeb72dfeb82cfeba2cfebb2bfebd2afebe2afec029fdc229fdc328fdc527fdc627fdc827fdca26fdcb26fccd25fcce25fcd025fcd225fbd324fbd524fbd724fad824fada24f9dc24f9dd25f8df25f8e125f7e225f7e425f6e626f6e826f5e926f5eb27f4ed27f3ee27f3f027f2f227f1f426f1f525f0f724f0f921"));function rb(t){return function(){return t}}var ib=Math.abs,ob=Math.atan2,ab=Math.cos,ub=Math.max,cb=Math.min,fb=Math.sin,sb=Math.sqrt,lb=1e-12,hb=Math.PI,db=hb/2,pb=2*hb;function gb(t){return t>1?0:t<-1?hb:Math.acos(t)}function yb(t){return t>=1?db:t<=-1?-db:Math.asin(t)}function vb(t){return t.innerRadius}function _b(t){return t.outerRadius}function bb(t){return t.startAngle}function mb(t){return t.endAngle}function xb(t){return t&&t.padAngle}function wb(t,n,e,r,i,o,a,u){var c=e-t,f=r-n,s=a-i,l=u-o,h=l*c-s*f;if(!(h*hC*C+P*P&&(A=S,T=E),{cx:A,cy:T,x01:-s,y01:-l,x11:A*(i/x-1),y11:T*(i/x-1)}}var Ab=Array.prototype.slice;function Tb(t){return"object"==typeof t&&"length"in t?t:Array.from(t)}function Sb(t){this._context=t}function Eb(t){return new Sb(t)}function kb(t){return t[0]}function Nb(t){return t[1]}function Cb(t,n){var e=rb(!0),r=null,i=Eb,o=null;function a(a){var u,c,f,s=(a=Tb(a)).length,l=!1;for(null==r&&(o=i(f=fa())),u=0;u<=s;++u)!(u=s;--l)u.point(y[l],v[l]);u.lineEnd(),u.areaEnd()}g&&(y[f]=+t(h,f,c),v[f]=+n(h,f,c),u.point(r?+r(h,f,c):y[f],e?+e(h,f,c):v[f]))}if(d)return u=null,d+""||null}function f(){return Cb().defined(i).curve(a).context(o)}return t="function"==typeof t?t:void 0===t?kb:rb(+t),n="function"==typeof n?n:rb(void 0===n?0:+n),e="function"==typeof e?e:void 0===e?Nb:rb(+e),c.x=function(n){return arguments.length?(t="function"==typeof n?n:rb(+n),r=null,c):t},c.x0=function(n){return arguments.length?(t="function"==typeof n?n:rb(+n),c):t},c.x1=function(t){return arguments.length?(r=null==t?null:"function"==typeof t?t:rb(+t),c):r},c.y=function(t){return arguments.length?(n="function"==typeof t?t:rb(+t),e=null,c):n},c.y0=function(t){return arguments.length?(n="function"==typeof t?t:rb(+t),c):n},c.y1=function(t){return arguments.length?(e=null==t?null:"function"==typeof t?t:rb(+t),c):e},c.lineX0=c.lineY0=function(){return f().x(t).y(n)},c.lineY1=function(){return f().x(t).y(e)},c.lineX1=function(){return f().x(r).y(n)},c.defined=function(t){return arguments.length?(i="function"==typeof t?t:rb(!!t),c):i},c.curve=function(t){return arguments.length?(a=t,null!=o&&(u=a(o)),c):a},c.context=function(t){return arguments.length?(null==t?o=u=null:u=a(o=t),c):o},c}function zb(t,n){return nt?1:n>=t?0:NaN}function Db(t){return t}Sb.prototype={areaStart:function(){this._line=0},areaEnd:function(){this._line=NaN},lineStart:function(){this._point=0},lineEnd:function(){(this._line||0!==this._line&&1===this._point)&&this._context.closePath(),this._line=1-this._line},point:function(t,n){switch(t=+t,n=+n,this._point){case 0:this._point=1,this._line?this._context.lineTo(t,n):this._context.moveTo(t,n);break;case 1:this._point=2;default:this._context.lineTo(t,n)}}};var qb=Fb(Eb);function Rb(t){this._curve=t}function Fb(t){function n(n){return new Rb(t(n))}return n._curve=t,n}function Ob(t){var n=t.curve;return t.angle=t.x,delete t.x,t.radius=t.y,delete t.y,t.curve=function(t){return arguments.length?n(Fb(t)):n()._curve},t}function Ib(){return Ob(Cb().curve(qb))}function Ub(){var t=Pb().curve(qb),n=t.curve,e=t.lineX0,r=t.lineX1,i=t.lineY0,o=t.lineY1;return t.angle=t.x,delete t.x,t.startAngle=t.x0,delete t.x0,t.endAngle=t.x1,delete t.x1,t.radius=t.y,delete t.y,t.innerRadius=t.y0,delete t.y0,t.outerRadius=t.y1,delete t.y1,t.lineStartAngle=function(){return Ob(e())},delete t.lineX0,t.lineEndAngle=function(){return Ob(r())},delete t.lineX1,t.lineInnerRadius=function(){return Ob(i())},delete t.lineY0,t.lineOuterRadius=function(){return Ob(o())},delete t.lineY1,t.curve=function(t){return arguments.length?n(Fb(t)):n()._curve},t}function Bb(t,n){return[(n=+n)*Math.cos(t-=Math.PI/2),n*Math.sin(t)]}function Yb(t){return t.source}function Lb(t){return t.target}function jb(t){var n=Yb,e=Lb,r=kb,i=Nb,o=null;function a(){var a,u=Ab.call(arguments),c=n.apply(this,u),f=e.apply(this,u);if(o||(o=a=fa()),t(o,+r.apply(this,(u[0]=c,u)),+i.apply(this,u),+r.apply(this,(u[0]=f,u)),+i.apply(this,u)),a)return o=null,a+""||null}return a.source=function(t){return arguments.length?(n=t,a):n},a.target=function(t){return arguments.length?(e=t,a):e},a.x=function(t){return arguments.length?(r="function"==typeof t?t:rb(+t),a):r},a.y=function(t){return arguments.length?(i="function"==typeof t?t:rb(+t),a):i},a.context=function(t){return arguments.length?(o=null==t?null:t,a):o},a}function Hb(t,n,e,r,i){t.moveTo(n,e),t.bezierCurveTo(n=(n+r)/2,e,n,i,r,i)}function Xb(t,n,e,r,i){t.moveTo(n,e),t.bezierCurveTo(n,e=(e+i)/2,r,e,r,i)}function Gb(t,n,e,r,i){var o=Bb(n,e),a=Bb(n,e=(e+i)/2),u=Bb(r,e),c=Bb(r,i);t.moveTo(o[0],o[1]),t.bezierCurveTo(a[0],a[1],u[0],u[1],c[0],c[1])}Rb.prototype={areaStart:function(){this._curve.areaStart()},areaEnd:function(){this._curve.areaEnd()},lineStart:function(){this._curve.lineStart()},lineEnd:function(){this._curve.lineEnd()},point:function(t,n){this._curve.point(n*Math.sin(t),n*-Math.cos(t))}};var Vb={draw:function(t,n){var e=Math.sqrt(n/hb);t.moveTo(e,0),t.arc(0,0,e,0,pb)}},$b={draw:function(t,n){var e=Math.sqrt(n/5)/2;t.moveTo(-3*e,-e),t.lineTo(-e,-e),t.lineTo(-e,-3*e),t.lineTo(e,-3*e),t.lineTo(e,-e),t.lineTo(3*e,-e),t.lineTo(3*e,e),t.lineTo(e,e),t.lineTo(e,3*e),t.lineTo(-e,3*e),t.lineTo(-e,e),t.lineTo(-3*e,e),t.closePath()}},Wb=Math.sqrt(1/3),Zb=2*Wb,Kb={draw:function(t,n){var e=Math.sqrt(n/Zb),r=e*Wb;t.moveTo(0,-e),t.lineTo(r,0),t.lineTo(0,e),t.lineTo(-r,0),t.closePath()}},Qb=Math.sin(hb/10)/Math.sin(7*hb/10),Jb=Math.sin(pb/10)*Qb,tm=-Math.cos(pb/10)*Qb,nm={draw:function(t,n){var e=Math.sqrt(.8908130915292852*n),r=Jb*e,i=tm*e;t.moveTo(0,-e),t.lineTo(r,i);for(var o=1;o<5;++o){var a=pb*o/5,u=Math.cos(a),c=Math.sin(a);t.lineTo(c*e,-u*e),t.lineTo(u*r-c*i,c*r+u*i)}t.closePath()}},em={draw:function(t,n){var e=Math.sqrt(n),r=-e/2;t.rect(r,r,e,e)}},rm=Math.sqrt(3),im={draw:function(t,n){var e=-Math.sqrt(n/(3*rm));t.moveTo(0,2*e),t.lineTo(-rm*e,-e),t.lineTo(rm*e,-e),t.closePath()}},om=-.5,am=Math.sqrt(3)/2,um=1/Math.sqrt(12),cm=3*(um/2+1),fm={draw:function(t,n){var e=Math.sqrt(n/cm),r=e/2,i=e*um,o=r,a=e*um+e,u=-o,c=a;t.moveTo(r,i),t.lineTo(o,a),t.lineTo(u,c),t.lineTo(om*r-am*i,am*r+om*i),t.lineTo(om*o-am*a,am*o+om*a),t.lineTo(om*u-am*c,am*u+om*c),t.lineTo(om*r+am*i,om*i-am*r),t.lineTo(om*o+am*a,om*a-am*o),t.lineTo(om*u+am*c,om*c-am*u),t.closePath()}},sm=[Vb,$b,Kb,em,nm,im,fm];function lm(){}function hm(t,n,e){t._context.bezierCurveTo((2*t._x0+t._x1)/3,(2*t._y0+t._y1)/3,(t._x0+2*t._x1)/3,(t._y0+2*t._y1)/3,(t._x0+4*t._x1+n)/6,(t._y0+4*t._y1+e)/6)}function dm(t){this._context=t}function pm(t){this._context=t}function gm(t){this._context=t}dm.prototype={areaStart:function(){this._line=0},areaEnd:function(){this._line=NaN},lineStart:function(){this._x0=this._x1=this._y0=this._y1=NaN,this._point=0},lineEnd:function(){switch(this._point){case 3:hm(this,this._x1,this._y1);case 2:this._context.lineTo(this._x1,this._y1)}(this._line||0!==this._line&&1===this._point)&&this._context.closePath(),this._line=1-this._line},point:function(t,n){switch(t=+t,n=+n,this._point){case 0:this._point=1,this._line?this._context.lineTo(t,n):this._context.moveTo(t,n);break;case 1:this._point=2;break;case 2:this._point=3,this._context.lineTo((5*this._x0+this._x1)/6,(5*this._y0+this._y1)/6);default:hm(this,t,n)}this._x0=this._x1,this._x1=t,this._y0=this._y1,this._y1=n}},pm.prototype={areaStart:lm,areaEnd:lm,lineStart:function(){this._x0=this._x1=this._x2=this._x3=this._x4=this._y0=this._y1=this._y2=this._y3=this._y4=NaN,this._point=0},lineEnd:function(){switch(this._point){case 1:this._context.moveTo(this._x2,this._y2),this._context.closePath();break;case 2:this._context.moveTo((this._x2+2*this._x3)/3,(this._y2+2*this._y3)/3),this._context.lineTo((this._x3+2*this._x2)/3,(this._y3+2*this._y2)/3),this._context.closePath();break;case 3:this.point(this._x2,this._y2),this.point(this._x3,this._y3),this.point(this._x4,this._y4)}},point:function(t,n){switch(t=+t,n=+n,this._point){case 0:this._point=1,this._x2=t,this._y2=n;break;case 1:this._point=2,this._x3=t,this._y3=n;break;case 2:this._point=3,this._x4=t,this._y4=n,this._context.moveTo((this._x0+4*this._x1+t)/6,(this._y0+4*this._y1+n)/6);break;default:hm(this,t,n)}this._x0=this._x1,this._x1=t,this._y0=this._y1,this._y1=n}},gm.prototype={areaStart:function(){this._line=0},areaEnd:function(){this._line=NaN},lineStart:function(){this._x0=this._x1=this._y0=this._y1=NaN,this._point=0},lineEnd:function(){(this._line||0!==this._line&&3===this._point)&&this._context.closePath(),this._line=1-this._line},point:function(t,n){switch(t=+t,n=+n,this._point){case 0:this._point=1;break;case 1:this._point=2;break;case 2:this._point=3;var e=(this._x0+4*this._x1+t)/6,r=(this._y0+4*this._y1+n)/6;this._line?this._context.lineTo(e,r):this._context.moveTo(e,r);break;case 3:this._point=4;default:hm(this,t,n)}this._x0=this._x1,this._x1=t,this._y0=this._y1,this._y1=n}};class ym{constructor(t,n){this._context=t,this._x=n}areaStart(){this._line=0}areaEnd(){this._line=NaN}lineStart(){this._point=0}lineEnd(){(this._line||0!==this._line&&1===this._point)&&this._context.closePath(),this._line=1-this._line}point(t,n){switch(t=+t,n=+n,this._point){case 0:this._point=1,this._line?this._context.lineTo(t,n):this._context.moveTo(t,n);break;case 1:this._point=2;default:this._x?this._context.bezierCurveTo(this._x0=(this._x0+t)/2,this._y0,this._x0,n,t,n):this._context.bezierCurveTo(this._x0,this._y0=(this._y0+n)/2,t,this._y0,t,n)}this._x0=t,this._y0=n}}function vm(t,n){this._basis=new dm(t),this._beta=n}vm.prototype={lineStart:function(){this._x=[],this._y=[],this._basis.lineStart()},lineEnd:function(){var t=this._x,n=this._y,e=t.length-1;if(e>0)for(var r,i=t[0],o=n[0],a=t[e]-i,u=n[e]-o,c=-1;++c<=e;)r=c/e,this._basis.point(this._beta*t[c]+(1-this._beta)*(i+r*a),this._beta*n[c]+(1-this._beta)*(o+r*u));this._x=this._y=null,this._basis.lineEnd()},point:function(t,n){this._x.push(+t),this._y.push(+n)}};var _m=function t(n){function e(t){return 1===n?new dm(t):new vm(t,n)}return e.beta=function(n){return t(+n)},e}(.85);function bm(t,n,e){t._context.bezierCurveTo(t._x1+t._k*(t._x2-t._x0),t._y1+t._k*(t._y2-t._y0),t._x2+t._k*(t._x1-n),t._y2+t._k*(t._y1-e),t._x2,t._y2)}function mm(t,n){this._context=t,this._k=(1-n)/6}mm.prototype={areaStart:function(){this._line=0},areaEnd:function(){this._line=NaN},lineStart:function(){this._x0=this._x1=this._x2=this._y0=this._y1=this._y2=NaN,this._point=0},lineEnd:function(){switch(this._point){case 2:this._context.lineTo(this._x2,this._y2);break;case 3:bm(this,this._x1,this._y1)}(this._line||0!==this._line&&1===this._point)&&this._context.closePath(),this._line=1-this._line},point:function(t,n){switch(t=+t,n=+n,this._point){case 0:this._point=1,this._line?this._context.lineTo(t,n):this._context.moveTo(t,n);break;case 1:this._point=2,this._x1=t,this._y1=n;break;case 2:this._point=3;default:bm(this,t,n)}this._x0=this._x1,this._x1=this._x2,this._x2=t,this._y0=this._y1,this._y1=this._y2,this._y2=n}};var xm=function t(n){function e(t){return new mm(t,n)}return e.tension=function(n){return t(+n)},e}(0);function wm(t,n){this._context=t,this._k=(1-n)/6}wm.prototype={areaStart:lm,areaEnd:lm,lineStart:function(){this._x0=this._x1=this._x2=this._x3=this._x4=this._x5=this._y0=this._y1=this._y2=this._y3=this._y4=this._y5=NaN,this._point=0},lineEnd:function(){switch(this._point){case 1:this._context.moveTo(this._x3,this._y3),this._context.closePath();break;case 2:this._context.lineTo(this._x3,this._y3),this._context.closePath();break;case 3:this.point(this._x3,this._y3),this.point(this._x4,this._y4),this.point(this._x5,this._y5)}},point:function(t,n){switch(t=+t,n=+n,this._point){case 0:this._point=1,this._x3=t,this._y3=n;break;case 1:this._point=2,this._context.moveTo(this._x4=t,this._y4=n);break;case 2:this._point=3,this._x5=t,this._y5=n;break;default:bm(this,t,n)}this._x0=this._x1,this._x1=this._x2,this._x2=t,this._y0=this._y1,this._y1=this._y2,this._y2=n}};var Mm=function t(n){function e(t){return new wm(t,n)}return e.tension=function(n){return t(+n)},e}(0);function Am(t,n){this._context=t,this._k=(1-n)/6}Am.prototype={areaStart:function(){this._line=0},areaEnd:function(){this._line=NaN},lineStart:function(){this._x0=this._x1=this._x2=this._y0=this._y1=this._y2=NaN,this._point=0},lineEnd:function(){(this._line||0!==this._line&&3===this._point)&&this._context.closePath(),this._line=1-this._line},point:function(t,n){switch(t=+t,n=+n,this._point){case 0:this._point=1;break;case 1:this._point=2;break;case 2:this._point=3,this._line?this._context.lineTo(this._x2,this._y2):this._context.moveTo(this._x2,this._y2);break;case 3:this._point=4;default:bm(this,t,n)}this._x0=this._x1,this._x1=this._x2,this._x2=t,this._y0=this._y1,this._y1=this._y2,this._y2=n}};var Tm=function t(n){function e(t){return new Am(t,n)}return e.tension=function(n){return t(+n)},e}(0);function Sm(t,n,e){var r=t._x1,i=t._y1,o=t._x2,a=t._y2;if(t._l01_a>lb){var u=2*t._l01_2a+3*t._l01_a*t._l12_a+t._l12_2a,c=3*t._l01_a*(t._l01_a+t._l12_a);r=(r*u-t._x0*t._l12_2a+t._x2*t._l01_2a)/c,i=(i*u-t._y0*t._l12_2a+t._y2*t._l01_2a)/c}if(t._l23_a>lb){var f=2*t._l23_2a+3*t._l23_a*t._l12_a+t._l12_2a,s=3*t._l23_a*(t._l23_a+t._l12_a);o=(o*f+t._x1*t._l23_2a-n*t._l12_2a)/s,a=(a*f+t._y1*t._l23_2a-e*t._l12_2a)/s}t._context.bezierCurveTo(r,i,o,a,t._x2,t._y2)}function Em(t,n){this._context=t,this._alpha=n}Em.prototype={areaStart:function(){this._line=0},areaEnd:function(){this._line=NaN},lineStart:function(){this._x0=this._x1=this._x2=this._y0=this._y1=this._y2=NaN,this._l01_a=this._l12_a=this._l23_a=this._l01_2a=this._l12_2a=this._l23_2a=this._point=0},lineEnd:function(){switch(this._point){case 2:this._context.lineTo(this._x2,this._y2);break;case 3:this.point(this._x2,this._y2)}(this._line||0!==this._line&&1===this._point)&&this._context.closePath(),this._line=1-this._line},point:function(t,n){if(t=+t,n=+n,this._point){var e=this._x2-t,r=this._y2-n;this._l23_a=Math.sqrt(this._l23_2a=Math.pow(e*e+r*r,this._alpha))}switch(this._point){case 0:this._point=1,this._line?this._context.lineTo(t,n):this._context.moveTo(t,n);break;case 1:this._point=2;break;case 2:this._point=3;default:Sm(this,t,n)}this._l01_a=this._l12_a,this._l12_a=this._l23_a,this._l01_2a=this._l12_2a,this._l12_2a=this._l23_2a,this._x0=this._x1,this._x1=this._x2,this._x2=t,this._y0=this._y1,this._y1=this._y2,this._y2=n}};var km=function t(n){function e(t){return n?new Em(t,n):new mm(t,0)}return e.alpha=function(n){return t(+n)},e}(.5);function Nm(t,n){this._context=t,this._alpha=n}Nm.prototype={areaStart:lm,areaEnd:lm,lineStart:function(){this._x0=this._x1=this._x2=this._x3=this._x4=this._x5=this._y0=this._y1=this._y2=this._y3=this._y4=this._y5=NaN,this._l01_a=this._l12_a=this._l23_a=this._l01_2a=this._l12_2a=this._l23_2a=this._point=0},lineEnd:function(){switch(this._point){case 1:this._context.moveTo(this._x3,this._y3),this._context.closePath();break;case 2:this._context.lineTo(this._x3,this._y3),this._context.closePath();break;case 3:this.point(this._x3,this._y3),this.point(this._x4,this._y4),this.point(this._x5,this._y5)}},point:function(t,n){if(t=+t,n=+n,this._point){var e=this._x2-t,r=this._y2-n;this._l23_a=Math.sqrt(this._l23_2a=Math.pow(e*e+r*r,this._alpha))}switch(this._point){case 0:this._point=1,this._x3=t,this._y3=n;break;case 1:this._point=2,this._context.moveTo(this._x4=t,this._y4=n);break;case 2:this._point=3,this._x5=t,this._y5=n;break;default:Sm(this,t,n)}this._l01_a=this._l12_a,this._l12_a=this._l23_a,this._l01_2a=this._l12_2a,this._l12_2a=this._l23_2a,this._x0=this._x1,this._x1=this._x2,this._x2=t,this._y0=this._y1,this._y1=this._y2,this._y2=n}};var Cm=function t(n){function e(t){return n?new Nm(t,n):new wm(t,0)}return e.alpha=function(n){return t(+n)},e}(.5);function Pm(t,n){this._context=t,this._alpha=n}Pm.prototype={areaStart:function(){this._line=0},areaEnd:function(){this._line=NaN},lineStart:function(){this._x0=this._x1=this._x2=this._y0=this._y1=this._y2=NaN,this._l01_a=this._l12_a=this._l23_a=this._l01_2a=this._l12_2a=this._l23_2a=this._point=0},lineEnd:function(){(this._line||0!==this._line&&3===this._point)&&this._context.closePath(),this._line=1-this._line},point:function(t,n){if(t=+t,n=+n,this._point){var e=this._x2-t,r=this._y2-n;this._l23_a=Math.sqrt(this._l23_2a=Math.pow(e*e+r*r,this._alpha))}switch(this._point){case 0:this._point=1;break;case 1:this._point=2;break;case 2:this._point=3,this._line?this._context.lineTo(this._x2,this._y2):this._context.moveTo(this._x2,this._y2);break;case 3:this._point=4;default:Sm(this,t,n)}this._l01_a=this._l12_a,this._l12_a=this._l23_a,this._l01_2a=this._l12_2a,this._l12_2a=this._l23_2a,this._x0=this._x1,this._x1=this._x2,this._x2=t,this._y0=this._y1,this._y1=this._y2,this._y2=n}};var zm=function t(n){function e(t){return n?new Pm(t,n):new Am(t,0)}return e.alpha=function(n){return t(+n)},e}(.5);function Dm(t){this._context=t}function qm(t){return t<0?-1:1}function Rm(t,n,e){var r=t._x1-t._x0,i=n-t._x1,o=(t._y1-t._y0)/(r||i<0&&-0),a=(e-t._y1)/(i||r<0&&-0),u=(o*i+a*r)/(r+i);return(qm(o)+qm(a))*Math.min(Math.abs(o),Math.abs(a),.5*Math.abs(u))||0}function Fm(t,n){var e=t._x1-t._x0;return e?(3*(t._y1-t._y0)/e-n)/2:n}function Om(t,n,e){var r=t._x0,i=t._y0,o=t._x1,a=t._y1,u=(o-r)/3;t._context.bezierCurveTo(r+u,i+u*n,o-u,a-u*e,o,a)}function Im(t){this._context=t}function Um(t){this._context=new Bm(t)}function Bm(t){this._context=t}function Ym(t){this._context=t}function Lm(t){var n,e,r=t.length-1,i=new Array(r),o=new Array(r),a=new Array(r);for(i[0]=0,o[0]=2,a[0]=t[0]+2*t[1],n=1;n=0;--n)i[n]=(a[n]-i[n+1])/o[n];for(o[r-1]=(t[r]+i[r-1])/2,n=0;n1)for(var e,r,i,o=1,a=t[n[0]],u=a.length;o=0;)e[n]=n;return e}function Gm(t,n){return t[n]}function Vm(t){const n=[];return n.key=t,n}function $m(t){var n=t.map(Wm);return Xm(t).sort((function(t,e){return n[t]-n[e]}))}function Wm(t){for(var n,e=-1,r=0,i=t.length,o=-1/0;++eo&&(o=n,r=e);return r}function Zm(t){var n=t.map(Km);return Xm(t).sort((function(t,e){return n[t]-n[e]}))}function Km(t){for(var n,e=0,r=-1,i=t.length;++r=0&&(this._t=1-this._t,this._line=1-this._line)},point:function(t,n){switch(t=+t,n=+n,this._point){case 0:this._point=1,this._line?this._context.lineTo(t,n):this._context.moveTo(t,n);break;case 1:this._point=2;default:if(this._t<=0)this._context.lineTo(this._x,n),this._context.lineTo(t,n);else{var e=this._x*(1-this._t)+t*this._t;this._context.lineTo(e,this._y),this._context.lineTo(e,n)}}this._x=t,this._y=n}};var Qm=t=>()=>t;function Jm(t,{sourceEvent:n,target:e,transform:r,dispatch:i}){Object.defineProperties(this,{type:{value:t,enumerable:!0,configurable:!0},sourceEvent:{value:n,enumerable:!0,configurable:!0},target:{value:e,enumerable:!0,configurable:!0},transform:{value:r,enumerable:!0,configurable:!0},_:{value:i}})}function tx(t,n,e){this.k=t,this.x=n,this.y=e}tx.prototype={constructor:tx,scale:function(t){return 1===t?this:new tx(this.k*t,this.x,this.y)},translate:function(t,n){return 0===t&0===n?this:new tx(this.k,this.x+this.k*t,this.y+this.k*n)},apply:function(t){return[t[0]*this.k+this.x,t[1]*this.k+this.y]},applyX:function(t){return t*this.k+this.x},applyY:function(t){return t*this.k+this.y},invert:function(t){return[(t[0]-this.x)/this.k,(t[1]-this.y)/this.k]},invertX:function(t){return(t-this.x)/this.k},invertY:function(t){return(t-this.y)/this.k},rescaleX:function(t){return t.copy().domain(t.range().map(this.invertX,this).map(t.invert,t))},rescaleY:function(t){return t.copy().domain(t.range().map(this.invertY,this).map(t.invert,t))},toString:function(){return"translate("+this.x+","+this.y+") scale("+this.k+")"}};var nx=new tx(1,0,0);function ex(t){for(;!t.__zoom;)if(!(t=t.parentNode))return nx;return t.__zoom}function rx(t){t.stopImmediatePropagation()}function ix(t){t.preventDefault(),t.stopImmediatePropagation()}function ox(t){return!(t.ctrlKey&&"wheel"!==t.type||t.button)}function ax(){var t=this;return t instanceof SVGElement?(t=t.ownerSVGElement||t).hasAttribute("viewBox")?[[(t=t.viewBox.baseVal).x,t.y],[t.x+t.width,t.y+t.height]]:[[0,0],[t.width.baseVal.value,t.height.baseVal.value]]:[[0,0],[t.clientWidth,t.clientHeight]]}function ux(){return this.__zoom||nx}function cx(t){return-t.deltaY*(1===t.deltaMode?.05:t.deltaMode?1:.002)*(t.ctrlKey?10:1)}function fx(){return navigator.maxTouchPoints||"ontouchstart"in this}function sx(t,n,e){var r=t.invertX(n[0][0])-e[0][0],i=t.invertX(n[1][0])-e[1][0],o=t.invertY(n[0][1])-e[0][1],a=t.invertY(n[1][1])-e[1][1];return t.translate(i>r?(r+i)/2:Math.min(0,r)||Math.max(0,i),a>o?(o+a)/2:Math.min(0,o)||Math.max(0,a))}ex.prototype=tx.prototype,t.Adder=g,t.Delaunay=nu,t.FormatSpecifier=uc,t.InternMap=y,t.InternSet=v,t.Voronoi=Wa,t.active=function(t,n){var e,r,i=t.__transition;if(i)for(r in n=null==n?null:n+"",i)if((e=i[r]).state>1&&e.name===n)return new ji([[t]],_o,n,+r);return null},t.arc=function(){var t=vb,n=_b,e=rb(0),r=null,i=bb,o=mb,a=xb,u=null;function c(){var c,f,s=+t.apply(this,arguments),l=+n.apply(this,arguments),h=i.apply(this,arguments)-db,d=o.apply(this,arguments)-db,p=ib(d-h),g=d>h;if(u||(u=c=fa()),llb)if(p>pb-lb)u.moveTo(l*ab(h),l*fb(h)),u.arc(0,0,l,h,d,!g),s>lb&&(u.moveTo(s*ab(d),s*fb(d)),u.arc(0,0,s,d,h,g));else{var y,v,_=h,b=d,m=h,x=d,w=p,M=p,A=a.apply(this,arguments)/2,T=A>lb&&(r?+r.apply(this,arguments):sb(s*s+l*l)),S=cb(ib(l-s)/2,+e.apply(this,arguments)),E=S,k=S;if(T>lb){var N=yb(T/s*fb(A)),C=yb(T/l*fb(A));(w-=2*N)>lb?(m+=N*=g?1:-1,x-=N):(w=0,m=x=(h+d)/2),(M-=2*C)>lb?(_+=C*=g?1:-1,b-=C):(M=0,_=b=(h+d)/2)}var P=l*ab(_),z=l*fb(_),D=s*ab(x),q=s*fb(x);if(S>lb){var R,F=l*ab(b),O=l*fb(b),I=s*ab(m),U=s*fb(m);if(plb?k>lb?(y=Mb(I,U,P,z,l,k,g),v=Mb(F,O,D,q,l,k,g),u.moveTo(y.cx+y.x01,y.cy+y.y01),klb&&w>lb?E>lb?(y=Mb(D,q,F,O,s,-E,g),v=Mb(P,z,I,U,s,-E,g),u.lineTo(y.cx+y.x01,y.cy+y.y01),E>a,f=i+2*u>>a,s=wa(20);function l(r){var i=new Float32Array(c*f),l=new Float32Array(c*f);r.forEach((function(r,o,s){var l=+t(r,o,s)+u>>a,h=+n(r,o,s)+u>>a,d=+e(r,o,s);l>=0&&l=0&&h>a),Ca({width:c,height:f,data:l},{width:c,height:f,data:i},o>>a),Na({width:c,height:f,data:i},{width:c,height:f,data:l},o>>a),Ca({width:c,height:f,data:l},{width:c,height:f,data:i},o>>a),Na({width:c,height:f,data:i},{width:c,height:f,data:l},o>>a),Ca({width:c,height:f,data:l},{width:c,height:f,data:i},o>>a);var d=s(i);if(!Array.isArray(d)){var p=B(i);d=F(0,p,d),(d=Z(0,Math.floor(p/d)*d,d)).shift()}return ka().thresholds(d).size([c,f])(i).map(h)}function h(t){return t.value*=Math.pow(2,-2*a),t.coordinates.forEach(d),t}function d(t){t.forEach(p)}function p(t){t.forEach(g)}function g(t){t[0]=t[0]*Math.pow(2,a)-u,t[1]=t[1]*Math.pow(2,a)-u}function y(){return c=r+2*(u=3*o)>>a,f=i+2*u>>a,l}return l.x=function(n){return arguments.length?(t="function"==typeof n?n:wa(+n),l):t},l.y=function(t){return arguments.length?(n="function"==typeof t?t:wa(+t),l):n},l.weight=function(t){return arguments.length?(e="function"==typeof t?t:wa(+t),l):e},l.size=function(t){if(!arguments.length)return[r,i];var n=+t[0],e=+t[1];if(!(n>=0&&e>=0))throw new Error("invalid size");return r=n,i=e,y()},l.cellSize=function(t){if(!arguments.length)return 1<=1))throw new Error("invalid cell size");return a=Math.floor(Math.log(t)/Math.LN2),y()},l.thresholds=function(t){return arguments.length?(s="function"==typeof t?t:Array.isArray(t)?wa(ma.call(t)):wa(t),l):s},l.bandwidth=function(t){if(!arguments.length)return Math.sqrt(o*(o+1));if(!((t=+t)>=0))throw new Error("invalid bandwidth");return o=Math.round((Math.sqrt(4*t*t+1)-1)/2),y()},l},t.contours=ka,t.count=c,t.create=function(t){return Dn(At(t).call(document.documentElement))},t.creator=At,t.cross=function(...t){const n="function"==typeof t[t.length-1]&&function(t){return n=>t(...n)}(t.pop()),e=(t=t.map(l)).map(f),r=t.length-1,i=new Array(r+1).fill(0),o=[];if(r<0||e.some(s))return o;for(;;){o.push(i.map(((n,e)=>t[e][n])));let a=r;for(;++i[a]===e[a];){if(0===a)return n?o.map(n):o;i[a--]=0}}},t.csv=Pu,t.csvFormat=hu,t.csvFormatBody=du,t.csvFormatRow=gu,t.csvFormatRows=pu,t.csvFormatValue=yu,t.csvParse=su,t.csvParseRows=lu,t.cubehelix=tr,t.cumsum=function(t,n){var e=0,r=0;return Float64Array.from(t,void 0===n?t=>e+=+t||0:i=>e+=+n(i,r++,t)||0)},t.curveBasis=function(t){return new dm(t)},t.curveBasisClosed=function(t){return new pm(t)},t.curveBasisOpen=function(t){return new gm(t)},t.curveBumpX=function(t){return new ym(t,!0)},t.curveBumpY=function(t){return new ym(t,!1)},t.curveBundle=_m,t.curveCardinal=xm,t.curveCardinalClosed=Mm,t.curveCardinalOpen=Tm,t.curveCatmullRom=km,t.curveCatmullRomClosed=Cm,t.curveCatmullRomOpen=zm,t.curveLinear=Eb,t.curveLinearClosed=function(t){return new Dm(t)},t.curveMonotoneX=function(t){return new Im(t)},t.curveMonotoneY=function(t){return new Um(t)},t.curveNatural=function(t){return new Ym(t)},t.curveStep=function(t){return new jm(t,.5)},t.curveStepAfter=function(t){return new jm(t,1)},t.curveStepBefore=function(t){return new jm(t,0)},t.descending=function(t,n){return nt?1:n>=t?0:NaN},t.deviation=d,t.difference=function(t,...n){t=new Set(t);for(const e of n)for(const n of e)t.delete(n);return t},t.disjoint=function(t,n){const e=n[Symbol.iterator](),r=new Set;for(const n of t){if(r.has(n))return!1;let t,i;for(;({value:t,done:i}=e.next())&&!i;){if(Object.is(n,t))return!1;r.add(t)}}return!0},t.dispatch=pt,t.drag=function(){var t,n,e,r,i=Xn,o=Gn,a=Vn,u=$n,c={},f=pt("start","drag","end"),s=0,l=0;function h(t){t.on("mousedown.drag",d).filter(u).on("touchstart.drag",y).on("touchmove.drag",v).on("touchend.drag touchcancel.drag",_).style("touch-action","none").style("-webkit-tap-highlight-color","rgba(0,0,0,0)")}function d(a,u){if(!r&&i.call(this,a,u)){var c=b(this,o.call(this,a,u),a,u,"mouse");c&&(Dn(a.view).on("mousemove.drag",p,!0).on("mouseup.drag",g,!0),Yn(a.view),Un(a),e=!1,t=a.clientX,n=a.clientY,c("start",a))}}function p(r){if(Bn(r),!e){var i=r.clientX-t,o=r.clientY-n;e=i*i+o*o>l}c.mouse("drag",r)}function g(t){Dn(t.view).on("mousemove.drag mouseup.drag",null),Ln(t.view,e),Bn(t),c.mouse("end",t)}function y(t,n){if(i.call(this,t,n)){var e,r,a=t.changedTouches,u=o.call(this,t,n),c=a.length;for(e=0;e+t,t.easePoly=Ki,t.easePolyIn=Wi,t.easePolyInOut=Ki,t.easePolyOut=Zi,t.easeQuad=Vi,t.easeQuadIn=function(t){return t*t},t.easeQuadInOut=Vi,t.easeQuadOut=function(t){return t*(2-t)},t.easeSin=to,t.easeSinIn=function(t){return 1==+t?1:1-Math.cos(t*Ji)},t.easeSinInOut=to,t.easeSinOut=function(t){return Math.sin(t*Ji)},t.every=function(t,n){if("function"!=typeof n)throw new TypeError("test is not a function");let e=-1;for(const r of t)if(!n(r,++e,t))return!1;return!0},t.extent=p,t.fcumsum=function(t,n){const e=new g;let r=-1;return Float64Array.from(t,void 0===n?t=>e.add(+t||0):i=>e.add(+n(i,++r,t)||0))},t.filter=function(t,n){if("function"!=typeof n)throw new TypeError("test is not a function");const e=[];let r=-1;for(const i of t)n(i,++r,t)&&e.push(i);return e},t.forceCenter=function(t,n){var e,r=1;function i(){var i,o,a=e.length,u=0,c=0;for(i=0;if+p||os+p||ac.index){var g=f-u.x-u.vx,y=s-u.y-u.vy,v=g*g+y*y;vt.r&&(t.r=t[n].r)}function c(){if(n){var r,i,o=n.length;for(e=new Array(o),r=0;r[u(t,n,r),t])));for(a=0,i=new Array(f);a=u)){(t.data!==n||t.next)&&(0===l&&(p+=(l=Vu(e))*l),0===h&&(p+=(h=Vu(e))*h),p(t=(1664525*t+1013904223)%Qu)/Qu}();function l(){h(),f.call("tick",n),e1?(null==e?u.delete(t):u.set(t,p(e)),n):u.get(t)},find:function(n,e,r){var i,o,a,u,c,f=0,s=t.length;for(null==r?r=1/0:r*=r,f=0;f1?(f.on(t,e),n):f.on(t)}}},t.forceX=function(t){var n,e,r,i=Gu(.1);function o(t){for(var i,o=0,a=n.length;o=.12&&i<.234&&r>=-.425&&r<-.214?u:i>=.166&&i<.234&&r>=-.214&&r<-.115?c:a).invert(t)},s.stream=function(e){return t&&n===e?t:(r=[a.stream(n=e),u.stream(e),c.stream(e)],i=r.length,t={point:function(t,n){for(var e=-1;++eKf(r[0],r[1])&&(r[1]=i[1]),Kf(i[0],r[1])>Kf(r[0],r[1])&&(r[0]=i[0])):o.push(r=i);for(a=-1/0,n=0,r=o[e=o.length-1];n<=e;r=i,++n)i=o[n],(u=Kf(r[1],i[0]))>a&&(a=u,nf=i[0],rf=r[1])}return lf=hf=null,nf===1/0||ef===1/0?[[NaN,NaN],[NaN,NaN]]:[[nf,ef],[rf,of]]},t.geoCentroid=function(t){Ef=kf=Nf=Cf=Pf=zf=Df=qf=0,Rf=new g,Ff=new g,Of=new g,Wc(t,ts);var n=+Rf,e=+Ff,r=+Of,i=Dc(n,e,r);return i2?t[2]+90:90]):[(t=e())[0],t[1],t[2]-90]},e([0,0,90]).scale(159.155)},t.geoTransverseMercatorRaw=Bh,t.gray=function(t,n){return new Fe(t,0,0,null==n?1:n)},t.greatest=function(t,e=n){let r,i=!1;if(1===e.length){let o;for(const a of t){const t=e(a);(i?n(t,o)>0:0===n(t,t))&&(r=a,o=t,i=!0)}}else for(const n of t)(i?e(n,r)>0:0===e(n,n))&&(r=n,i=!0);return r},t.greatestIndex=function(t,e=n){if(1===e.length)return G(t,e);let r,i=-1,o=-1;for(const n of t)++o,(i<0?0===e(n,n):e(n,r)>0)&&(r=n,i=o);return i},t.group=M,t.groupSort=function(t,e,r){return(1===e.length?k(A(t,e,r),(([t,e],[r,i])=>n(e,i)||n(t,r))):k(M(t,r),(([t,r],[i,o])=>e(r,o)||n(t,i)))).map((([t])=>t))},t.groups=function(t,...n){return S(t,Array.from,w,n)},t.hcl=Le,t.hierarchy=Xh,t.histogram=U,t.hsl=Ae,t.html=Fu,t.image=function(t,n){return new Promise((function(e,r){var i=new Image;for(var o in n)i[o]=n[o];i.onerror=r,i.onload=function(){e(i)},i.src=t}))},t.index=function(t,...n){return S(t,w,T,n)},t.indexes=function(t,...n){return S(t,Array.from,T,n)},t.interpolate=Mr,t.interpolateArray=function(t,n){return(gr(n)?pr:yr)(t,n)},t.interpolateBasis=rr,t.interpolateBasisClosed=ir,t.interpolateBlues=q_,t.interpolateBrBG=Gv,t.interpolateBuGn=s_,t.interpolateBuPu=h_,t.interpolateCividis=function(t){return t=Math.max(0,Math.min(1,t)),"rgb("+Math.max(0,Math.min(255,Math.round(-4.54-t*(35.34-t*(2381.73-t*(6402.7-t*(7024.72-2710.57*t)))))))+", "+Math.max(0,Math.min(255,Math.round(32.49+t*(170.73+t*(52.82-t*(131.46-t*(176.58-67.37*t)))))))+", "+Math.max(0,Math.min(255,Math.round(81.24+t*(442.36-t*(2482.43-t*(6167.24-t*(6614.94-2475.67*t)))))))+")"},t.interpolateCool=V_,t.interpolateCubehelix=Yr,t.interpolateCubehelixDefault=X_,t.interpolateCubehelixLong=Lr,t.interpolateDate=vr,t.interpolateDiscrete=function(t){var n=t.length;return function(e){return t[Math.max(0,Math.min(n-1,Math.floor(e*n)))]}},t.interpolateGnBu=p_,t.interpolateGreens=F_,t.interpolateGreys=I_,t.interpolateHcl=Ir,t.interpolateHclLong=Ur,t.interpolateHsl=Rr,t.interpolateHslLong=Fr,t.interpolateHue=function(t,n){var e=ur(+t,+n);return function(t){var n=e(t);return n-360*Math.floor(n/360)}},t.interpolateInferno=nb,t.interpolateLab=function(t,n){var e=fr((t=Re(t)).l,(n=Re(n)).l),r=fr(t.a,n.a),i=fr(t.b,n.b),o=fr(t.opacity,n.opacity);return function(n){return t.l=e(n),t.a=r(n),t.b=i(n),t.opacity=o(n),t+""}},t.interpolateMagma=tb,t.interpolateNumber=_r,t.interpolateNumberArray=pr,t.interpolateObject=br,t.interpolateOrRd=y_,t.interpolateOranges=H_,t.interpolatePRGn=$v,t.interpolatePiYG=Zv,t.interpolatePlasma=eb,t.interpolatePuBu=m_,t.interpolatePuBuGn=__,t.interpolatePuOr=Qv,t.interpolatePuRd=w_,t.interpolatePurples=B_,t.interpolateRainbow=function(t){(t<0||t>1)&&(t-=Math.floor(t));var n=Math.abs(t-.5);return $_.h=360*t-100,$_.s=1.5-1.5*n,$_.l=.8-.9*n,$_+""},t.interpolateRdBu=t_,t.interpolateRdGy=e_,t.interpolateRdPu=A_,t.interpolateRdYlBu=i_,t.interpolateRdYlGn=a_,t.interpolateReds=L_,t.interpolateRgb=sr,t.interpolateRgbBasis=hr,t.interpolateRgbBasisClosed=dr,t.interpolateRound=Ar,t.interpolateSinebow=function(t){var n;return t=(.5-t)*Math.PI,W_.r=255*(n=Math.sin(t))*n,W_.g=255*(n=Math.sin(t+Z_))*n,W_.b=255*(n=Math.sin(t+K_))*n,W_+""},t.interpolateSpectral=c_,t.interpolateString=wr,t.interpolateTransformCss=Cr,t.interpolateTransformSvg=Pr,t.interpolateTurbo=function(t){return t=Math.max(0,Math.min(1,t)),"rgb("+Math.max(0,Math.min(255,Math.round(34.61+t*(1172.33-t*(10793.56-t*(33300.12-t*(38394.49-14825.05*t)))))))+", "+Math.max(0,Math.min(255,Math.round(23.31+t*(557.33+t*(1225.33-t*(3574.96-t*(1073.77+707.56*t)))))))+", "+Math.max(0,Math.min(255,Math.round(27.2+t*(3211.1-t*(15327.97-t*(27814-t*(22569.18-6838.66*t)))))))+")"},t.interpolateViridis=J_,t.interpolateWarm=G_,t.interpolateYlGn=k_,t.interpolateYlGnBu=S_,t.interpolateYlOrBr=C_,t.interpolateYlOrRd=z_,t.interpolateZoom=Dr,t.interrupt=gi,t.intersection=function(t,...n){t=new Set(t),n=n.map(et);t:for(const e of t)for(const r of n)if(!r.has(e)){t.delete(e);continue t}return t},t.interval=function(t,n,e){var r=new ei,i=n;return null==n?(r.restart(t,n,e),r):(r._restart=r.restart,r.restart=function(t,n,e){n=+n,e=null==e?ti():+e,r._restart((function o(a){a+=i,r._restart(o,i+=n,e),t(a)}),n,e)},r.restart(t,n,e),r)},t.isoFormat=Mv,t.isoParse=Av,t.json=function(t,n){return fetch(t,n).then(Du)},t.lab=Re,t.lch=function(t,n,e,r){return 1===arguments.length?Ye(t):new je(e,n,t,null==r?1:r)},t.least=function(t,e=n){let r,i=!1;if(1===e.length){let o;for(const a of t){const t=e(a);(i?n(t,o)<0:0===n(t,t))&&(r=a,o=t,i=!0)}}else for(const n of t)(i?e(n,r)<0:0===e(n,n))&&(r=n,i=!0);return r},t.leastIndex=K,t.line=Cb,t.lineRadial=Ib,t.linkHorizontal=function(){return jb(Hb)},t.linkRadial=function(){var t=jb(Gb);return t.angle=t.x,delete t.x,t.radius=t.y,delete t.y,t},t.linkVertical=function(){return jb(Xb)},t.local=Rn,t.map=function(t,n){if("function"!=typeof t[Symbol.iterator])throw new TypeError("values is not iterable");if("function"!=typeof n)throw new TypeError("mapper is not a function");return Array.from(t,((e,r)=>n(e,r,t)))},t.matcher=Ct,t.max=B,t.maxIndex=G,t.mean=function(t,n){let e=0,r=0;if(void 0===n)for(let n of t)null!=n&&(n=+n)>=n&&(++e,r+=n);else{let i=-1;for(let o of t)null!=(o=n(o,++i,t))&&(o=+o)>=o&&(++e,r+=o)}if(e)return r/e},t.median=function(t,n){return H(t,.5,n)},t.merge=V,t.min=Y,t.minIndex=$,t.namespace=xt,t.namespaces=mt,t.nice=O,t.now=ti,t.pack=function(){var t=null,n=1,e=1,r=hd;function i(i){return i.x=n/2,i.y=e/2,t?i.eachBefore(gd(t)).eachAfter(yd(r,.5)).eachBefore(vd(1)):i.eachBefore(gd(pd)).eachAfter(yd(hd,1)).eachAfter(yd(r,i.r/Math.min(n,e))).eachBefore(vd(Math.min(n,e)/(2*i.r))),i}return i.radius=function(n){return arguments.length?(t=sd(n),i):t},i.size=function(t){return arguments.length?(n=+t[0],e=+t[1],i):[n,e]},i.padding=function(t){return arguments.length?(r="function"==typeof t?t:dd(+t),i):r},i},t.packEnclose=Kh,t.packSiblings=function(t){return fd(t),t},t.pairs=function(t,n=W){const e=[];let r,i=!1;for(const o of t)i&&e.push(n(r,o)),r=o,i=!0;return e},t.partition=function(){var t=1,n=1,e=0,r=!1;function i(i){var o=i.height+1;return i.x0=i.y0=e,i.x1=t,i.y1=n/o,i.eachBefore(function(t,n){return function(r){r.children&&bd(r,r.x0,t*(r.depth+1)/n,r.x1,t*(r.depth+2)/n);var i=r.x0,o=r.y0,a=r.x1-e,u=r.y1-e;a0&&(d+=l);for(null!=n?p.sort((function(t,e){return n(g[t],g[e])})):null!=e&&p.sort((function(t,n){return e(a[t],a[n])})),u=0,f=d?(v-h*b)/d:0;u0?l*f:0)+b,g[c]={data:a[c],index:u,value:l,startAngle:y,endAngle:s,padAngle:_};return g}return a.value=function(n){return arguments.length?(t="function"==typeof n?n:rb(+n),a):t},a.sortValues=function(t){return arguments.length?(n=t,e=null,a):n},a.sort=function(t){return arguments.length?(e=t,n=null,a):e},a.startAngle=function(t){return arguments.length?(r="function"==typeof t?t:rb(+t),a):r},a.endAngle=function(t){return arguments.length?(i="function"==typeof t?t:rb(+t),a):i},a.padAngle=function(t){return arguments.length?(o="function"==typeof t?t:rb(+t),a):o},a},t.piecewise=jr,t.pointRadial=Bb,t.pointer=In,t.pointers=function(t,n){return t.target&&(t=On(t),void 0===n&&(n=t.currentTarget),t=t.touches||[t]),Array.from(t,(t=>In(t,n)))},t.polygonArea=function(t){for(var n,e=-1,r=t.length,i=t[r-1],o=0;++eu!=f>u&&a<(c-e)*(u-r)/(f-r)+e&&(s=!s),c=e,f=r;return s},t.polygonHull=function(t){if((e=t.length)<3)return null;var n,e,r=new Array(e),i=new Array(e);for(n=0;n=0;--n)f.push(t[r[o[n]][2]]);for(n=+u;n(n=1664525*n+1013904223|0,ep*(n>>>0))},t.randomLogNormal=Ld,t.randomLogistic=tp,t.randomNormal=Yd,t.randomPareto=Gd,t.randomPoisson=np,t.randomUniform=Ud,t.randomWeibull=Qd,t.range=Z,t.reduce=function(t,n,e){if("function"!=typeof n)throw new TypeError("reducer is not a function");const r=t[Symbol.iterator]();let i,o,a=-1;if(arguments.length<3){if(({done:i,value:e}=r.next()),i)return;++a}for(;({done:i,value:o}=r.next()),!i;)e=n(e,o,++a,t);return e},t.reverse=function(t){if("function"!=typeof t[Symbol.iterator])throw new TypeError("values is not iterable");return Array.from(t).reverse()},t.rgb=ve,t.ribbon=function(){return ba()},t.ribbonArrow=function(){return ba(_a)},t.rollup=A,t.rollups=function(t,n,...e){return S(t,Array.from,n,e)},t.scaleBand=up,t.scaleDiverging=function t(){var n=bp(Pv()(lp));return n.copy=function(){return Nv(n,t())},ip.apply(n,arguments)},t.scaleDivergingLog=function t(){var n=Ep(Pv()).domain([.1,1,10]);return n.copy=function(){return Nv(n,t()).base(n.base())},ip.apply(n,arguments)},t.scaleDivergingPow=zv,t.scaleDivergingSqrt=function(){return zv.apply(null,arguments).exponent(.5)},t.scaleDivergingSymlog=function t(){var n=Cp(Pv());return n.copy=function(){return Nv(n,t()).constant(n.constant())},ip.apply(n,arguments)},t.scaleIdentity=function t(n){var e;function r(t){return null==t||isNaN(t=+t)?e:t}return r.invert=r,r.domain=r.range=function(t){return arguments.length?(n=Array.from(t,fp),r):n.slice()},r.unknown=function(t){return arguments.length?(e=t,r):e},r.copy=function(){return t(n).unknown(e)},n=arguments.length?Array.from(n,fp):[0,1],bp(r)},t.scaleImplicit=op,t.scaleLinear=function t(){var n=vp();return n.copy=function(){return gp(n,t())},rp.apply(n,arguments),bp(n)},t.scaleLog=function t(){var n=Ep(yp()).domain([1,10]);return n.copy=function(){return gp(n,t()).base(n.base())},rp.apply(n,arguments),n},t.scaleOrdinal=ap,t.scalePoint=function(){return cp(up.apply(null,arguments).paddingInner(1))},t.scalePow=Rp,t.scaleQuantile=function t(){var e,r=[],i=[],a=[];function u(){var t=0,n=Math.max(1,i.length);for(a=new Array(n-1);++t0?a[n-1]:r[0],n=i?[a[i-1],r]:[a[n-1],a[n]]},c.unknown=function(t){return arguments.length?(n=t,c):c},c.thresholds=function(){return a.slice()},c.copy=function(){return t().domain([e,r]).range(u).unknown(n)},rp.apply(bp(c),arguments)},t.scaleRadial=function t(){var n,e=vp(),r=[0,1],i=!1;function o(t){var r=Op(e(t));return isNaN(r)?n:i?Math.round(r):r}return o.invert=function(t){return e.invert(Fp(t))},o.domain=function(t){return arguments.length?(e.domain(t),o):e.domain()},o.range=function(t){return arguments.length?(e.range((r=Array.from(t,fp)).map(Fp)),o):r.slice()},o.rangeRound=function(t){return o.range(t).round(!0)},o.round=function(t){return arguments.length?(i=!!t,o):i},o.clamp=function(t){return arguments.length?(e.clamp(t),o):e.clamp()},o.unknown=function(t){return arguments.length?(n=t,o):n},o.copy=function(){return t(e.domain(),r).round(i).clamp(e.clamp()).unknown(n)},rp.apply(o,arguments),bp(o)},t.scaleSequential=function t(){var n=bp(kv()(lp));return n.copy=function(){return Nv(n,t())},ip.apply(n,arguments)},t.scaleSequentialLog=function t(){var n=Ep(kv()).domain([1,10]);return n.copy=function(){return Nv(n,t()).base(n.base())},ip.apply(n,arguments)},t.scaleSequentialPow=Cv,t.scaleSequentialQuantile=function t(){var e=[],r=lp;function i(t){if(null!=t&&!isNaN(t=+t))return r((o(e,t,1)-1)/(e.length-1))}return i.domain=function(t){if(!arguments.length)return e.slice();e=[];for(let n of t)null==n||isNaN(n=+n)||e.push(n);return e.sort(n),i},i.interpolator=function(t){return arguments.length?(r=t,i):r},i.range=function(){return e.map(((t,n)=>r(n/(e.length-1))))},i.quantiles=function(t){return Array.from({length:t+1},((n,r)=>H(e,r/t)))},i.copy=function(){return t(r).domain(e)},ip.apply(i,arguments)},t.scaleSequentialSqrt=function(){return Cv.apply(null,arguments).exponent(.5)},t.scaleSequentialSymlog=function t(){var n=Cp(kv());return n.copy=function(){return Nv(n,t()).constant(n.constant())},ip.apply(n,arguments)},t.scaleSqrt=function(){return Rp.apply(null,arguments).exponent(.5)},t.scaleSymlog=function t(){var n=Cp(yp());return n.copy=function(){return gp(n,t()).constant(n.constant())},rp.apply(n,arguments)},t.scaleThreshold=function t(){var n,e=[.5],r=[0,1],i=1;function a(t){return null!=t&&t<=t?r[o(e,t,0,i)]:n}return a.domain=function(t){return arguments.length?(e=Array.from(t),i=Math.min(e.length,r.length-1),a):e.slice()},a.range=function(t){return arguments.length?(r=Array.from(t),i=Math.min(e.length,r.length-1),a):r.slice()},a.invertExtent=function(t){var n=r.indexOf(t);return[e[n-1],e[n]]},a.unknown=function(t){return arguments.length?(n=t,a):n},a.copy=function(){return t().domain(e).range(r).unknown(n)},rp.apply(a,arguments)},t.scaleTime=function(){return rp.apply(Ev(Kg,Qg,xg,bg,og,eg,tg,Qp,Zp,t.timeFormat).domain([new Date(2e3,0,1),new Date(2e3,0,2)]),arguments)},t.scaleUtc=function(){return rp.apply(Ev(Wg,Zg,Gg,Hg,Cg,Eg,Tg,Mg,Zp,t.utcFormat).domain([Date.UTC(2e3,0,1),Date.UTC(2e3,0,2)]),arguments)},t.scan=function(t,n){const e=K(t,n);return e<0?void 0:e},t.schemeAccent=Rv,t.schemeBlues=D_,t.schemeBrBG=Xv,t.schemeBuGn=f_,t.schemeBuPu=l_,t.schemeCategory10=qv,t.schemeDark2=Fv,t.schemeGnBu=d_,t.schemeGreens=R_,t.schemeGreys=O_,t.schemeOrRd=g_,t.schemeOranges=j_,t.schemePRGn=Vv,t.schemePaired=Ov,t.schemePastel1=Iv,t.schemePastel2=Uv,t.schemePiYG=Wv,t.schemePuBu=b_,t.schemePuBuGn=v_,t.schemePuOr=Kv,t.schemePuRd=x_,t.schemePurples=U_,t.schemeRdBu=Jv,t.schemeRdGy=n_,t.schemeRdPu=M_,t.schemeRdYlBu=r_,t.schemeRdYlGn=o_,t.schemeReds=Y_,t.schemeSet1=Bv,t.schemeSet2=Yv,t.schemeSet3=Lv,t.schemeSpectral=u_,t.schemeTableau10=jv,t.schemeYlGn=E_,t.schemeYlGnBu=T_,t.schemeYlOrBr=N_,t.schemeYlOrRd=P_,t.select=Dn,t.selectAll=function(t){return"string"==typeof t?new Pn([document.querySelectorAll(t)],[document.documentElement]):new Pn([null==t?[]:Et(t)],Cn)},t.selection=zn,t.selector=St,t.selectorAll=Nt,t.shuffle=Q,t.shuffler=J,t.some=function(t,n){if("function"!=typeof n)throw new TypeError("test is not a function");let e=-1;for(const r of t)if(n(r,++e,t))return!0;return!1},t.sort=k,t.stack=function(){var t=rb([]),n=Xm,e=Hm,r=Gm;function i(i){var o,a,u=Array.from(t.apply(this,arguments),Vm),c=u.length,f=-1;for(const t of i)for(o=0,++f;o0)for(var e,r,i,o,a,u,c=0,f=t[n[0]].length;c0?(r[0]=o,r[1]=o+=i):i<0?(r[1]=a,r[0]=a+=i):(r[0]=0,r[1]=i)},t.stackOffsetExpand=function(t,n){if((r=t.length)>0){for(var e,r,i,o=0,a=t[0].length;o0){for(var e,r=0,i=t[n[0]],o=i.length;r0&&(r=(e=t[n[0]]).length)>0){for(var e,r,i,o=0,a=1;a0)throw new Error("cycle");return o}return e.id=function(n){return arguments.length?(t=ld(n),e):t},e.parentId=function(t){return arguments.length?(n=ld(t),e):n},e},t.style=Jt,t.subset=function(t,n){return rt(n,t)},t.sum=function(t,n){let e=0;if(void 0===n)for(let n of t)(n=+n)&&(e+=n);else{let r=-1;for(let i of t)(i=+n(i,++r,t))&&(e+=i)}return e},t.superset=rt,t.svg=Ou,t.symbol=function(t,n){var e=null;function r(){var r;if(e||(e=r=fa()),t.apply(this,arguments).draw(e,+n.apply(this,arguments)),r)return e=null,r+""||null}return t="function"==typeof t?t:rb(t||Vb),n="function"==typeof n?n:rb(void 0===n?64:+n),r.type=function(n){return arguments.length?(t="function"==typeof n?n:rb(n),r):t},r.size=function(t){return arguments.length?(n="function"==typeof t?t:rb(+t),r):n},r.context=function(t){return arguments.length?(e=null==t?null:t,r):e},r},t.symbolCircle=Vb,t.symbolCross=$b,t.symbolDiamond=Kb,t.symbolSquare=em,t.symbolStar=nm,t.symbolTriangle=im,t.symbolWye=fm,t.symbols=sm,t.text=Nu,t.thresholdFreedmanDiaconis=function(t,n,e){return Math.ceil((e-n)/(2*(H(t,.75)-H(t,.25))*Math.pow(c(t),-1/3)))},t.thresholdScott=function(t,n,e){return Math.ceil((e-n)/(3.5*d(t)*Math.pow(c(t),-1/3)))},t.thresholdSturges=I,t.tickFormat=_p,t.tickIncrement=R,t.tickStep=F,t.ticks=q,t.timeDay=eg,t.timeDays=rg,t.timeFormatDefaultLocale=xv,t.timeFormatLocale=ey,t.timeFriday=sg,t.timeFridays=vg,t.timeHour=tg,t.timeHours=ng,t.timeInterval=Bp,t.timeMillisecond=Yp,t.timeMilliseconds=Lp,t.timeMinute=Qp,t.timeMinutes=Jp,t.timeMonday=ag,t.timeMondays=dg,t.timeMonth=bg,t.timeMonths=mg,t.timeSaturday=lg,t.timeSaturdays=_g,t.timeSecond=Zp,t.timeSeconds=Kp,t.timeSunday=og,t.timeSundays=hg,t.timeThursday=fg,t.timeThursdays=yg,t.timeTickInterval=Qg,t.timeTicks=Kg,t.timeTuesday=ug,t.timeTuesdays=pg,t.timeWednesday=cg,t.timeWednesdays=gg,t.timeWeek=og,t.timeWeeks=hg,t.timeYear=xg,t.timeYears=wg,t.timeout=ci,t.timer=ri,t.timerFlush=ii,t.transition=Hi,t.transpose=tt,t.tree=function(){var t=Ad,n=1,e=1,r=null;function i(i){var c=function(t){for(var n,e,r,i,o,a=new Nd(t,0),u=[a];n=u.pop();)if(r=n._.children)for(n.children=new Array(o=r.length),i=o-1;i>=0;--i)u.push(e=n.children[i]=new Nd(r[i],i)),e.parent=n;return(a.parent=new Nd(null,0)).children=[a],a}(i);if(c.eachAfter(o),c.parent.m=-c.z,c.eachBefore(a),r)i.eachBefore(u);else{var f=i,s=i,l=i;i.eachBefore((function(t){t.xs.x&&(s=t),t.depth>l.depth&&(l=t)}));var h=f===s?1:t(f,s)/2,d=h-f.x,p=n/(s.x+h+d),g=e/(l.depth||1);i.eachBefore((function(t){t.x=(t.x+d)*p,t.y=t.depth*g}))}return i}function o(n){var e=n.children,r=n.parent.children,i=n.i?r[n.i-1]:null;if(e){!function(t){for(var n,e=0,r=0,i=t.children,o=i.length;--o>=0;)(n=i[o]).z+=e,n.m+=e,e+=n.s+(r+=n.c)}(n);var o=(e[0].z+e[e.length-1].z)/2;i?(n.z=i.z+t(n._,i._),n.m=n.z-o):n.z=o}else i&&(n.z=i.z+t(n._,i._));n.parent.A=function(n,e,r){if(e){for(var i,o=n,a=n,u=e,c=o.parent.children[0],f=o.m,s=a.m,l=u.m,h=c.m;u=Sd(u),o=Td(o),u&&o;)c=Td(c),(a=Sd(a)).a=n,(i=u.z+l-o.z-f+t(u._,o._))>0&&(Ed(kd(u,n,r),n,i),f+=i,s+=i),l+=u.m,f+=o.m,h+=c.m,s+=a.m;u&&!Sd(a)&&(a.t=u,a.m+=l-s),o&&!Td(c)&&(c.t=o,c.m+=f-h,r=n)}return r}(n,i,n.parent.A||r[0])}function a(t){t._.x=t.z+t.parent.m,t.m+=t.parent.m}function u(t){t.x*=n,t.y=t.depth*e}return i.separation=function(n){return arguments.length?(t=n,i):t},i.size=function(t){return arguments.length?(r=!1,n=+t[0],e=+t[1],i):r?null:[n,e]},i.nodeSize=function(t){return arguments.length?(r=!0,n=+t[0],e=+t[1],i):r?[n,e]:null},i},t.treemap=function(){var t=Dd,n=!1,e=1,r=1,i=[0],o=hd,a=hd,u=hd,c=hd,f=hd;function s(t){return t.x0=t.y0=0,t.x1=e,t.y1=r,t.eachBefore(l),i=[0],n&&t.eachBefore(_d),t}function l(n){var e=i[n.depth],r=n.x0+e,s=n.y0+e,l=n.x1-e,h=n.y1-e;l=e-1){var s=u[n];return s.x0=i,s.y0=o,s.x1=a,void(s.y1=c)}var l=f[n],h=r/2+l,d=n+1,p=e-1;for(;d>>1;f[g]c-o){var _=r?(i*v+a*y)/r:a;t(n,d,y,i,o,_,c),t(d,e,v,_,o,a,c)}else{var b=r?(o*v+c*y)/r:c;t(n,d,y,i,o,a,b),t(d,e,v,i,b,a,c)}}(0,c,t.value,n,e,r,i)},t.treemapDice=bd,t.treemapResquarify=qd,t.treemapSlice=Cd,t.treemapSliceDice=function(t,n,e,r,i){(1&t.depth?Cd:bd)(t,n,e,r,i)},t.treemapSquarify=Dd,t.tsv=zu,t.tsvFormat=mu,t.tsvFormatBody=xu,t.tsvFormatRow=Mu,t.tsvFormatRows=wu,t.tsvFormatValue=Au,t.tsvParse=_u,t.tsvParseRows=bu,t.union=function(...t){const n=new Set;for(const e of t)for(const t of e)n.add(t);return n},t.utcDay=Eg,t.utcDays=kg,t.utcFriday=Rg,t.utcFridays=Lg,t.utcHour=Tg,t.utcHours=Sg,t.utcMillisecond=Yp,t.utcMilliseconds=Lp,t.utcMinute=Mg,t.utcMinutes=Ag,t.utcMonday=Pg,t.utcMondays=Ig,t.utcMonth=Hg,t.utcMonths=Xg,t.utcSaturday=Fg,t.utcSaturdays=jg,t.utcSecond=Zp,t.utcSeconds=Kp,t.utcSunday=Cg,t.utcSundays=Og,t.utcThursday=qg,t.utcThursdays=Yg,t.utcTickInterval=Zg,t.utcTicks=Wg,t.utcTuesday=zg,t.utcTuesdays=Ug,t.utcWednesday=Dg,t.utcWednesdays=Bg,t.utcWeek=Cg,t.utcWeeks=Og,t.utcYear=Gg,t.utcYears=Vg,t.variance=h,t.version="6.7.0",t.window=Wt,t.xml=Ru,t.zip=function(){return tt(arguments)},t.zoom=function(){var t,n,e,r=ox,i=ax,o=sx,a=cx,u=fx,c=[0,1/0],f=[[-1/0,-1/0],[1/0,1/0]],s=250,l=Dr,h=pt("start","zoom","end"),d=500,p=0,g=10;function y(t){t.property("__zoom",ux).on("wheel.zoom",M).on("mousedown.zoom",A).on("dblclick.zoom",T).filter(u).on("touchstart.zoom",S).on("touchmove.zoom",E).on("touchend.zoom touchcancel.zoom",k).style("-webkit-tap-highlight-color","rgba(0,0,0,0)")}function v(t,n){return(n=Math.max(c[0],Math.min(c[1],n)))===t.k?t:new tx(n,t.x,t.y)}function _(t,n,e){var r=n[0]-e[0]*t.k,i=n[1]-e[1]*t.k;return r===t.x&&i===t.y?t:new tx(t.k,r,i)}function b(t){return[(+t[0][0]+ +t[1][0])/2,(+t[0][1]+ +t[1][1])/2]}function m(t,n,e,r){t.on("start.zoom",(function(){x(this,arguments).event(r).start()})).on("interrupt.zoom end.zoom",(function(){x(this,arguments).event(r).end()})).tween("zoom",(function(){var t=this,o=arguments,a=x(t,o).event(r),u=i.apply(t,o),c=null==e?b(u):"function"==typeof e?e.apply(t,o):e,f=Math.max(u[1][0]-u[0][0],u[1][1]-u[0][1]),s=t.__zoom,h="function"==typeof n?n.apply(t,o):n,d=l(s.invert(c).concat(f/s.k),h.invert(c).concat(f/h.k));return function(t){if(1===t)t=h;else{var n=d(t),e=f/n[2];t=new tx(e,c[0]-n[0]*e,c[1]-n[1]*e)}a.zoom(null,t)}}))}function x(t,n,e){return!e&&t.__zooming||new w(t,n)}function w(t,n){this.that=t,this.args=n,this.active=0,this.sourceEvent=null,this.extent=i.apply(t,n),this.taps=0}function M(t,...n){if(r.apply(this,arguments)){var e=x(this,n).event(t),i=this.__zoom,u=Math.max(c[0],Math.min(c[1],i.k*Math.pow(2,a.apply(this,arguments)))),s=In(t);if(e.wheel)e.mouse[0][0]===s[0]&&e.mouse[0][1]===s[1]||(e.mouse[1]=i.invert(e.mouse[0]=s)),clearTimeout(e.wheel);else{if(i.k===u)return;e.mouse=[s,i.invert(s)],gi(this),e.start()}ix(t),e.wheel=setTimeout(l,150),e.zoom("mouse",o(_(v(i,u),e.mouse[0],e.mouse[1]),e.extent,f))}function l(){e.wheel=null,e.end()}}function A(t,...n){if(!e&&r.apply(this,arguments)){var i=x(this,n,!0).event(t),a=Dn(t.view).on("mousemove.zoom",h,!0).on("mouseup.zoom",d,!0),u=In(t,c),c=t.currentTarget,s=t.clientX,l=t.clientY;Yn(t.view),rx(t),i.mouse=[u,this.__zoom.invert(u)],gi(this),i.start()}function h(t){if(ix(t),!i.moved){var n=t.clientX-s,e=t.clientY-l;i.moved=n*n+e*e>p}i.event(t).zoom("mouse",o(_(i.that.__zoom,i.mouse[0]=In(t,c),i.mouse[1]),i.extent,f))}function d(t){a.on("mousemove.zoom mouseup.zoom",null),Ln(t.view,i.moved),ix(t),i.event(t).end()}}function T(t,...n){if(r.apply(this,arguments)){var e=this.__zoom,a=In(t.changedTouches?t.changedTouches[0]:t,this),u=e.invert(a),c=e.k*(t.shiftKey?.5:2),l=o(_(v(e,c),a,u),i.apply(this,n),f);ix(t),s>0?Dn(this).transition().duration(s).call(m,l,a,t):Dn(this).call(y.transform,l,a,t)}}function S(e,...i){if(r.apply(this,arguments)){var o,a,u,c,f=e.touches,s=f.length,l=x(this,i,e.changedTouches.length===s).event(e);for(rx(e),a=0;a+~]|"+M+")"+M+"*"),U=new RegExp(M+"|>"),X=new RegExp(F),V=new RegExp("^"+I+"$"),G={ID:new RegExp("^#("+I+")"),CLASS:new RegExp("^\\.("+I+")"),TAG:new RegExp("^("+I+"|[*])"),ATTR:new RegExp("^"+W),PSEUDO:new RegExp("^"+F),CHILD:new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+M+"*(even|odd|(([+-]|)(\\d*)n|)"+M+"*(?:([+-]|)"+M+"*(\\d+)|))"+M+"*\\)|)","i"),bool:new RegExp("^(?:"+R+")$","i"),needsContext:new RegExp("^"+M+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+M+"*((?:-\\d)?\\d*)"+M+"*\\)|)(?=[^-]|$)","i")},Y=/HTML$/i,Q=/^(?:input|select|textarea|button)$/i,J=/^h\d$/i,K=/^[^{]+\{\s*\[native \w/,Z=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,ee=/[+~]/,te=new RegExp("\\\\[\\da-fA-F]{1,6}"+M+"?|\\\\([^\\r\\n\\f])","g"),ne=function(e,t){var n="0x"+e.slice(1)-65536;return t||(n<0?String.fromCharCode(n+65536):String.fromCharCode(n>>10|55296,1023&n|56320))},re=/([\0-\x1f\x7f]|^-?\d)|^-$|[^\0-\x1f\x7f-\uFFFF\w-]/g,ie=function(e,t){return t?"\0"===e?"\ufffd":e.slice(0,-1)+"\\"+e.charCodeAt(e.length-1).toString(16)+" ":"\\"+e},oe=function(){T()},ae=be(function(e){return!0===e.disabled&&"fieldset"===e.nodeName.toLowerCase()},{dir:"parentNode",next:"legend"});try{H.apply(t=O.call(p.childNodes),p.childNodes),t[p.childNodes.length].nodeType}catch(e){H={apply:t.length?function(e,t){L.apply(e,O.call(t))}:function(e,t){var n=e.length,r=0;while(e[n++]=t[r++]);e.length=n-1}}}function se(t,e,n,r){var i,o,a,s,u,l,c,f=e&&e.ownerDocument,p=e?e.nodeType:9;if(n=n||[],"string"!=typeof t||!t||1!==p&&9!==p&&11!==p)return n;if(!r&&(T(e),e=e||C,E)){if(11!==p&&(u=Z.exec(t)))if(i=u[1]){if(9===p){if(!(a=e.getElementById(i)))return n;if(a.id===i)return n.push(a),n}else if(f&&(a=f.getElementById(i))&&y(e,a)&&a.id===i)return n.push(a),n}else{if(u[2])return H.apply(n,e.getElementsByTagName(t)),n;if((i=u[3])&&d.getElementsByClassName&&e.getElementsByClassName)return H.apply(n,e.getElementsByClassName(i)),n}if(d.qsa&&!N[t+" "]&&(!v||!v.test(t))&&(1!==p||"object"!==e.nodeName.toLowerCase())){if(c=t,f=e,1===p&&(U.test(t)||z.test(t))){(f=ee.test(t)&&ye(e.parentNode)||e)===e&&d.scope||((s=e.getAttribute("id"))?s=s.replace(re,ie):e.setAttribute("id",s=S)),o=(l=h(t)).length;while(o--)l[o]=(s?"#"+s:":scope")+" "+xe(l[o]);c=l.join(",")}try{return H.apply(n,f.querySelectorAll(c)),n}catch(e){N(t,!0)}finally{s===S&&e.removeAttribute("id")}}}return g(t.replace($,"$1"),e,n,r)}function ue(){var r=[];return function e(t,n){return r.push(t+" ")>b.cacheLength&&delete e[r.shift()],e[t+" "]=n}}function le(e){return e[S]=!0,e}function ce(e){var t=C.createElement("fieldset");try{return!!e(t)}catch(e){return!1}finally{t.parentNode&&t.parentNode.removeChild(t),t=null}}function fe(e,t){var n=e.split("|"),r=n.length;while(r--)b.attrHandle[n[r]]=t}function pe(e,t){var n=t&&e,r=n&&1===e.nodeType&&1===t.nodeType&&e.sourceIndex-t.sourceIndex;if(r)return r;if(n)while(n=n.nextSibling)if(n===t)return-1;return e?1:-1}function de(t){return function(e){return"input"===e.nodeName.toLowerCase()&&e.type===t}}function he(n){return function(e){var t=e.nodeName.toLowerCase();return("input"===t||"button"===t)&&e.type===n}}function ge(t){return function(e){return"form"in e?e.parentNode&&!1===e.disabled?"label"in e?"label"in e.parentNode?e.parentNode.disabled===t:e.disabled===t:e.isDisabled===t||e.isDisabled!==!t&&ae(e)===t:e.disabled===t:"label"in e&&e.disabled===t}}function ve(a){return le(function(o){return o=+o,le(function(e,t){var n,r=a([],e.length,o),i=r.length;while(i--)e[n=r[i]]&&(e[n]=!(t[n]=e[n]))})})}function ye(e){return e&&"undefined"!=typeof e.getElementsByTagName&&e}for(e in d=se.support={},i=se.isXML=function(e){var t=e.namespaceURI,n=(e.ownerDocument||e).documentElement;return!Y.test(t||n&&n.nodeName||"HTML")},T=se.setDocument=function(e){var t,n,r=e?e.ownerDocument||e:p;return r!=C&&9===r.nodeType&&r.documentElement&&(a=(C=r).documentElement,E=!i(C),p!=C&&(n=C.defaultView)&&n.top!==n&&(n.addEventListener?n.addEventListener("unload",oe,!1):n.attachEvent&&n.attachEvent("onunload",oe)),d.scope=ce(function(e){return a.appendChild(e).appendChild(C.createElement("div")),"undefined"!=typeof e.querySelectorAll&&!e.querySelectorAll(":scope fieldset div").length}),d.attributes=ce(function(e){return e.className="i",!e.getAttribute("className")}),d.getElementsByTagName=ce(function(e){return e.appendChild(C.createComment("")),!e.getElementsByTagName("*").length}),d.getElementsByClassName=K.test(C.getElementsByClassName),d.getById=ce(function(e){return a.appendChild(e).id=S,!C.getElementsByName||!C.getElementsByName(S).length}),d.getById?(b.filter.ID=function(e){var t=e.replace(te,ne);return function(e){return e.getAttribute("id")===t}},b.find.ID=function(e,t){if("undefined"!=typeof t.getElementById&&E){var n=t.getElementById(e);return n?[n]:[]}}):(b.filter.ID=function(e){var n=e.replace(te,ne);return function(e){var t="undefined"!=typeof e.getAttributeNode&&e.getAttributeNode("id");return t&&t.value===n}},b.find.ID=function(e,t){if("undefined"!=typeof t.getElementById&&E){var n,r,i,o=t.getElementById(e);if(o){if((n=o.getAttributeNode("id"))&&n.value===e)return[o];i=t.getElementsByName(e),r=0;while(o=i[r++])if((n=o.getAttributeNode("id"))&&n.value===e)return[o]}return[]}}),b.find.TAG=d.getElementsByTagName?function(e,t){return"undefined"!=typeof t.getElementsByTagName?t.getElementsByTagName(e):d.qsa?t.querySelectorAll(e):void 0}:function(e,t){var n,r=[],i=0,o=t.getElementsByTagName(e);if("*"===e){while(n=o[i++])1===n.nodeType&&r.push(n);return r}return o},b.find.CLASS=d.getElementsByClassName&&function(e,t){if("undefined"!=typeof t.getElementsByClassName&&E)return t.getElementsByClassName(e)},s=[],v=[],(d.qsa=K.test(C.querySelectorAll))&&(ce(function(e){var t;a.appendChild(e).innerHTML="",e.querySelectorAll("[msallowcapture^='']").length&&v.push("[*^$]="+M+"*(?:''|\"\")"),e.querySelectorAll("[selected]").length||v.push("\\["+M+"*(?:value|"+R+")"),e.querySelectorAll("[id~="+S+"-]").length||v.push("~="),(t=C.createElement("input")).setAttribute("name",""),e.appendChild(t),e.querySelectorAll("[name='']").length||v.push("\\["+M+"*name"+M+"*="+M+"*(?:''|\"\")"),e.querySelectorAll(":checked").length||v.push(":checked"),e.querySelectorAll("a#"+S+"+*").length||v.push(".#.+[+~]"),e.querySelectorAll("\\\f"),v.push("[\\r\\n\\f]")}),ce(function(e){e.innerHTML="";var t=C.createElement("input");t.setAttribute("type","hidden"),e.appendChild(t).setAttribute("name","D"),e.querySelectorAll("[name=d]").length&&v.push("name"+M+"*[*^$|!~]?="),2!==e.querySelectorAll(":enabled").length&&v.push(":enabled",":disabled"),a.appendChild(e).disabled=!0,2!==e.querySelectorAll(":disabled").length&&v.push(":enabled",":disabled"),e.querySelectorAll("*,:x"),v.push(",.*:")})),(d.matchesSelector=K.test(c=a.matches||a.webkitMatchesSelector||a.mozMatchesSelector||a.oMatchesSelector||a.msMatchesSelector))&&ce(function(e){d.disconnectedMatch=c.call(e,"*"),c.call(e,"[s!='']:x"),s.push("!=",F)}),v=v.length&&new RegExp(v.join("|")),s=s.length&&new RegExp(s.join("|")),t=K.test(a.compareDocumentPosition),y=t||K.test(a.contains)?function(e,t){var n=9===e.nodeType?e.documentElement:e,r=t&&t.parentNode;return e===r||!(!r||1!==r.nodeType||!(n.contains?n.contains(r):e.compareDocumentPosition&&16&e.compareDocumentPosition(r)))}:function(e,t){if(t)while(t=t.parentNode)if(t===e)return!0;return!1},D=t?function(e,t){if(e===t)return l=!0,0;var n=!e.compareDocumentPosition-!t.compareDocumentPosition;return n||(1&(n=(e.ownerDocument||e)==(t.ownerDocument||t)?e.compareDocumentPosition(t):1)||!d.sortDetached&&t.compareDocumentPosition(e)===n?e==C||e.ownerDocument==p&&y(p,e)?-1:t==C||t.ownerDocument==p&&y(p,t)?1:u?P(u,e)-P(u,t):0:4&n?-1:1)}:function(e,t){if(e===t)return l=!0,0;var n,r=0,i=e.parentNode,o=t.parentNode,a=[e],s=[t];if(!i||!o)return e==C?-1:t==C?1:i?-1:o?1:u?P(u,e)-P(u,t):0;if(i===o)return pe(e,t);n=e;while(n=n.parentNode)a.unshift(n);n=t;while(n=n.parentNode)s.unshift(n);while(a[r]===s[r])r++;return r?pe(a[r],s[r]):a[r]==p?-1:s[r]==p?1:0}),C},se.matches=function(e,t){return se(e,null,null,t)},se.matchesSelector=function(e,t){if(T(e),d.matchesSelector&&E&&!N[t+" "]&&(!s||!s.test(t))&&(!v||!v.test(t)))try{var n=c.call(e,t);if(n||d.disconnectedMatch||e.document&&11!==e.document.nodeType)return n}catch(e){N(t,!0)}return 0":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(e){return e[1]=e[1].replace(te,ne),e[3]=(e[3]||e[4]||e[5]||"").replace(te,ne),"~="===e[2]&&(e[3]=" "+e[3]+" "),e.slice(0,4)},CHILD:function(e){return e[1]=e[1].toLowerCase(),"nth"===e[1].slice(0,3)?(e[3]||se.error(e[0]),e[4]=+(e[4]?e[5]+(e[6]||1):2*("even"===e[3]||"odd"===e[3])),e[5]=+(e[7]+e[8]||"odd"===e[3])):e[3]&&se.error(e[0]),e},PSEUDO:function(e){var t,n=!e[6]&&e[2];return G.CHILD.test(e[0])?null:(e[3]?e[2]=e[4]||e[5]||"":n&&X.test(n)&&(t=h(n,!0))&&(t=n.indexOf(")",n.length-t)-n.length)&&(e[0]=e[0].slice(0,t),e[2]=n.slice(0,t)),e.slice(0,3))}},filter:{TAG:function(e){var t=e.replace(te,ne).toLowerCase();return"*"===e?function(){return!0}:function(e){return e.nodeName&&e.nodeName.toLowerCase()===t}},CLASS:function(e){var t=m[e+" "];return t||(t=new RegExp("(^|"+M+")"+e+"("+M+"|$)"))&&m(e,function(e){return t.test("string"==typeof e.className&&e.className||"undefined"!=typeof e.getAttribute&&e.getAttribute("class")||"")})},ATTR:function(n,r,i){return function(e){var t=se.attr(e,n);return null==t?"!="===r:!r||(t+="","="===r?t===i:"!="===r?t!==i:"^="===r?i&&0===t.indexOf(i):"*="===r?i&&-1:\x20\t\r\n\f]*)[\x20\t\r\n\f]*\/?>(?:<\/\1>|)$/i;function D(e,n,r){return m(n)?S.grep(e,function(e,t){return!!n.call(e,t,e)!==r}):n.nodeType?S.grep(e,function(e){return e===n!==r}):"string"!=typeof n?S.grep(e,function(e){return-1)[^>]*|#([\w-]+))$/;(S.fn.init=function(e,t,n){var r,i;if(!e)return this;if(n=n||j,"string"==typeof e){if(!(r="<"===e[0]&&">"===e[e.length-1]&&3<=e.length?[null,e,null]:q.exec(e))||!r[1]&&t)return!t||t.jquery?(t||n).find(e):this.constructor(t).find(e);if(r[1]){if(t=t instanceof S?t[0]:t,S.merge(this,S.parseHTML(r[1],t&&t.nodeType?t.ownerDocument||t:E,!0)),N.test(r[1])&&S.isPlainObject(t))for(r in t)m(this[r])?this[r](t[r]):this.attr(r,t[r]);return this}return(i=E.getElementById(r[2]))&&(this[0]=i,this.length=1),this}return e.nodeType?(this[0]=e,this.length=1,this):m(e)?void 0!==n.ready?n.ready(e):e(S):S.makeArray(e,this)}).prototype=S.fn,j=S(E);var L=/^(?:parents|prev(?:Until|All))/,H={children:!0,contents:!0,next:!0,prev:!0};function O(e,t){while((e=e[t])&&1!==e.nodeType);return e}S.fn.extend({has:function(e){var t=S(e,this),n=t.length;return this.filter(function(){for(var e=0;e\x20\t\r\n\f]*)/i,he=/^$|^module$|\/(?:java|ecma)script/i;ce=E.createDocumentFragment().appendChild(E.createElement("div")),(fe=E.createElement("input")).setAttribute("type","radio"),fe.setAttribute("checked","checked"),fe.setAttribute("name","t"),ce.appendChild(fe),y.checkClone=ce.cloneNode(!0).cloneNode(!0).lastChild.checked,ce.innerHTML="",y.noCloneChecked=!!ce.cloneNode(!0).lastChild.defaultValue,ce.innerHTML="",y.option=!!ce.lastChild;var ge={thead:[1,"","
"],col:[2,"","
"],tr:[2,"","
"],td:[3,"","
"],_default:[0,"",""]};function ve(e,t){var n;return n="undefined"!=typeof e.getElementsByTagName?e.getElementsByTagName(t||"*"):"undefined"!=typeof e.querySelectorAll?e.querySelectorAll(t||"*"):[],void 0===t||t&&A(e,t)?S.merge([e],n):n}function ye(e,t){for(var n=0,r=e.length;n",""]);var me=/<|&#?\w+;/;function xe(e,t,n,r,i){for(var o,a,s,u,l,c,f=t.createDocumentFragment(),p=[],d=0,h=e.length;d\s*$/g;function qe(e,t){return A(e,"table")&&A(11!==t.nodeType?t:t.firstChild,"tr")&&S(e).children("tbody")[0]||e}function Le(e){return e.type=(null!==e.getAttribute("type"))+"/"+e.type,e}function He(e){return"true/"===(e.type||"").slice(0,5)?e.type=e.type.slice(5):e.removeAttribute("type"),e}function Oe(e,t){var n,r,i,o,a,s;if(1===t.nodeType){if(Y.hasData(e)&&(s=Y.get(e).events))for(i in Y.remove(t,"handle events"),s)for(n=0,r=s[i].length;n").attr(n.scriptAttrs||{}).prop({charset:n.scriptCharset,src:n.url}).on("load error",i=function(e){r.remove(),i=null,e&&t("error"===e.type?404:200,e.type)}),E.head.appendChild(r[0])},abort:function(){i&&i()}}});var Ut,Xt=[],Vt=/(=)\?(?=&|$)|\?\?/;S.ajaxSetup({jsonp:"callback",jsonpCallback:function(){var e=Xt.pop()||S.expando+"_"+Ct.guid++;return this[e]=!0,e}}),S.ajaxPrefilter("json jsonp",function(e,t,n){var r,i,o,a=!1!==e.jsonp&&(Vt.test(e.url)?"url":"string"==typeof e.data&&0===(e.contentType||"").indexOf("application/x-www-form-urlencoded")&&Vt.test(e.data)&&"data");if(a||"jsonp"===e.dataTypes[0])return r=e.jsonpCallback=m(e.jsonpCallback)?e.jsonpCallback():e.jsonpCallback,a?e[a]=e[a].replace(Vt,"$1"+r):!1!==e.jsonp&&(e.url+=(Et.test(e.url)?"&":"?")+e.jsonp+"="+r),e.converters["script json"]=function(){return o||S.error(r+" was not called"),o[0]},e.dataTypes[0]="json",i=C[r],C[r]=function(){o=arguments},n.always(function(){void 0===i?S(C).removeProp(r):C[r]=i,e[r]&&(e.jsonpCallback=t.jsonpCallback,Xt.push(r)),o&&m(i)&&i(o[0]),o=i=void 0}),"script"}),y.createHTMLDocument=((Ut=E.implementation.createHTMLDocument("").body).innerHTML="
",2===Ut.childNodes.length),S.parseHTML=function(e,t,n){return"string"!=typeof e?[]:("boolean"==typeof t&&(n=t,t=!1),t||(y.createHTMLDocument?((r=(t=E.implementation.createHTMLDocument("")).createElement("base")).href=E.location.href,t.head.appendChild(r)):t=E),o=!n&&[],(i=N.exec(e))?[t.createElement(i[1])]:(i=xe([e],t,o),o&&o.length&&S(o).remove(),S.merge([],i.childNodes)));var r,i,o},S.fn.load=function(e,t,n){var r,i,o,a=this,s=e.indexOf(" ");return-1").append(S.parseHTML(e)).find(r):e)}).always(n&&function(e,t){a.each(function(){n.apply(this,o||[e.responseText,t,e])})}),this},S.expr.pseudos.animated=function(t){return S.grep(S.timers,function(e){return t===e.elem}).length},S.offset={setOffset:function(e,t,n){var r,i,o,a,s,u,l=S.css(e,"position"),c=S(e),f={};"static"===l&&(e.style.position="relative"),s=c.offset(),o=S.css(e,"top"),u=S.css(e,"left"),("absolute"===l||"fixed"===l)&&-1<(o+u).indexOf("auto")?(a=(r=c.position()).top,i=r.left):(a=parseFloat(o)||0,i=parseFloat(u)||0),m(t)&&(t=t.call(e,n,S.extend({},s))),null!=t.top&&(f.top=t.top-s.top+a),null!=t.left&&(f.left=t.left-s.left+i),"using"in t?t.using.call(e,f):("number"==typeof f.top&&(f.top+="px"),"number"==typeof f.left&&(f.left+="px"),c.css(f))}},S.fn.extend({offset:function(t){if(arguments.length)return void 0===t?this:this.each(function(e){S.offset.setOffset(this,t,e)});var e,n,r=this[0];return r?r.getClientRects().length?(e=r.getBoundingClientRect(),n=r.ownerDocument.defaultView,{top:e.top+n.pageYOffset,left:e.left+n.pageXOffset}):{top:0,left:0}:void 0},position:function(){if(this[0]){var e,t,n,r=this[0],i={top:0,left:0};if("fixed"===S.css(r,"position"))t=r.getBoundingClientRect();else{t=this.offset(),n=r.ownerDocument,e=r.offsetParent||n.documentElement;while(e&&(e===n.body||e===n.documentElement)&&"static"===S.css(e,"position"))e=e.parentNode;e&&e!==r&&1===e.nodeType&&((i=S(e).offset()).top+=S.css(e,"borderTopWidth",!0),i.left+=S.css(e,"borderLeftWidth",!0))}return{top:t.top-i.top-S.css(r,"marginTop",!0),left:t.left-i.left-S.css(r,"marginLeft",!0)}}},offsetParent:function(){return this.map(function(){var e=this.offsetParent;while(e&&"static"===S.css(e,"position"))e=e.offsetParent;return e||re})}}),S.each({scrollLeft:"pageXOffset",scrollTop:"pageYOffset"},function(t,i){var o="pageYOffset"===i;S.fn[t]=function(e){return $(this,function(e,t,n){var r;if(x(e)?r=e:9===e.nodeType&&(r=e.defaultView),void 0===n)return r?r[i]:e[t];r?r.scrollTo(o?r.pageXOffset:n,o?n:r.pageYOffset):e[t]=n},t,e,arguments.length)}}),S.each(["top","left"],function(e,n){S.cssHooks[n]=$e(y.pixelPosition,function(e,t){if(t)return t=Be(e,n),Me.test(t)?S(e).position()[n]+"px":t})}),S.each({Height:"height",Width:"width"},function(a,s){S.each({padding:"inner"+a,content:s,"":"outer"+a},function(r,o){S.fn[o]=function(e,t){var n=arguments.length&&(r||"boolean"!=typeof e),i=r||(!0===e||!0===t?"margin":"border");return $(this,function(e,t,n){var r;return x(e)?0===o.indexOf("outer")?e["inner"+a]:e.document.documentElement["client"+a]:9===e.nodeType?(r=e.documentElement,Math.max(e.body["scroll"+a],r["scroll"+a],e.body["offset"+a],r["offset"+a],r["client"+a])):void 0===n?S.css(e,t,i):S.style(e,t,n,i)},s,n?e:void 0,n)}})}),S.each(["ajaxStart","ajaxStop","ajaxComplete","ajaxError","ajaxSuccess","ajaxSend"],function(e,t){S.fn[t]=function(e){return this.on(t,e)}}),S.fn.extend({bind:function(e,t,n){return this.on(e,null,t,n)},unbind:function(e,t){return this.off(e,null,t)},delegate:function(e,t,n,r){return this.on(t,e,n,r)},undelegate:function(e,t,n){return 1===arguments.length?this.off(e,"**"):this.off(t,e||"**",n)},hover:function(e,t){return this.mouseenter(e).mouseleave(t||e)}}),S.each("blur focus focusin focusout resize scroll click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select submit keydown keypress keyup contextmenu".split(" "),function(e,n){S.fn[n]=function(e,t){return 0>2],r=(3&f)<<4,u=1;break;case 1:s[o++]=h[r|f>>4],r=(15&f)<<2,u=2;break;case 2:s[o++]=h[r|f>>6],s[o++]=h[63&f],u=0}8191>4,r=u,s=2;break;case 2:i[n++]=(15&r)<<4|(60&u)>>2,r=u,s=3;break;case 3:i[n++]=(3&r)<<6|u,s=0}}if(1===s)throw Error(a);return n-e},n.test=function(t){return/^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$/.test(t)}},{}],3:[function(t,i,n){function c(i,n){"string"==typeof i&&(n=i,i=Q);var f=[];function h(t){if("string"!=typeof t){var i=a();if(c.verbose&&console.log("codegen: "+i),i="return "+i,t){for(var n=Object.keys(t),r=Array(n.length+1),e=Array(n.length),s=0;s>>0:i<11754943508222875e-54?(e<<31|Math.round(i/1401298464324817e-60))>>>0:(e<<31|127+(e=Math.floor(Math.log(i)/Math.LN2))<<23|8388607&Math.round(i*Math.pow(2,-e)*8388608))>>>0,n,r)}function n(t,i,n){t=t(i,n),i=2*(t>>31)+1,n=t>>>23&255,t&=8388607;return 255==n?t?NaN:1/0*i:0==n?1401298464324817e-60*i*t:i*Math.pow(2,n-150)*(8388608+t)}function r(t,i,n){u[0]=t,i[n]=f[0],i[n+1]=f[1],i[n+2]=f[2],i[n+3]=f[3]}function e(t,i,n){u[0]=t,i[n]=f[3],i[n+1]=f[2],i[n+2]=f[1],i[n+3]=f[0]}function s(t,i){return f[0]=t[i],f[1]=t[i+1],f[2]=t[i+2],f[3]=t[i+3],u[0]}function o(t,i){return f[3]=t[i],f[2]=t[i+1],f[1]=t[i+2],f[0]=t[i+3],u[0]}var u,f,h,a,c;function l(t,i,n,r,e,s){var o,u=r<0?1:0;0===(r=u?-r:r)?(t(0,e,s+i),t(0<1/r?0:2147483648,e,s+n)):isNaN(r)?(t(0,e,s+i),t(2146959360,e,s+n)):17976931348623157e292>>0,e,s+n)):r<22250738585072014e-324?(t((o=r/5e-324)>>>0,e,s+i),t((u<<31|o/4294967296)>>>0,e,s+n)):(t(4503599627370496*(o=r*Math.pow(2,-(r=1024===(r=Math.floor(Math.log(r)/Math.LN2))?1023:r)))>>>0,e,s+i),t((u<<31|r+1023<<20|1048576*o&1048575)>>>0,e,s+n))}function v(t,i,n,r,e){i=t(r,e+i),r=t(r,e+n),e=2*(r>>31)+1,n=r>>>20&2047,i=4294967296*(1048575&r)+i;return 2047==n?i?NaN:1/0*e:0==n?5e-324*e*i:e*Math.pow(2,n-1075)*(i+4503599627370496)}function d(t,i,n){h[0]=t,i[n]=a[0],i[n+1]=a[1],i[n+2]=a[2],i[n+3]=a[3],i[n+4]=a[4],i[n+5]=a[5],i[n+6]=a[6],i[n+7]=a[7]}function p(t,i,n){h[0]=t,i[n]=a[7],i[n+1]=a[6],i[n+2]=a[5],i[n+3]=a[4],i[n+4]=a[3],i[n+5]=a[2],i[n+6]=a[1],i[n+7]=a[0]}function b(t,i){return a[0]=t[i],a[1]=t[i+1],a[2]=t[i+2],a[3]=t[i+3],a[4]=t[i+4],a[5]=t[i+5],a[6]=t[i+6],a[7]=t[i+7],h[0]}function w(t,i){return a[7]=t[i],a[6]=t[i+1],a[5]=t[i+2],a[4]=t[i+3],a[3]=t[i+4],a[2]=t[i+5],a[1]=t[i+6],a[0]=t[i+7],h[0]}return"undefined"!=typeof Float32Array?(u=new Float32Array([-0]),f=new Uint8Array(u.buffer),c=128===f[3],t.writeFloatLE=c?r:e,t.writeFloatBE=c?e:r,t.readFloatLE=c?s:o,t.readFloatBE=c?o:s):(t.writeFloatLE=i.bind(null,y),t.writeFloatBE=i.bind(null,m),t.readFloatLE=n.bind(null,g),t.readFloatBE=n.bind(null,j)),"undefined"!=typeof Float64Array?(h=new Float64Array([-0]),a=new Uint8Array(h.buffer),c=128===a[7],t.writeDoubleLE=c?d:p,t.writeDoubleBE=c?p:d,t.readDoubleLE=c?b:w,t.readDoubleBE=c?w:b):(t.writeDoubleLE=l.bind(null,y,0,4),t.writeDoubleBE=l.bind(null,m,4,0),t.readDoubleLE=v.bind(null,g,0,4),t.readDoubleBE=v.bind(null,j,4,0)),t}function y(t,i,n){i[n]=255&t,i[n+1]=t>>>8&255,i[n+2]=t>>>16&255,i[n+3]=t>>>24}function m(t,i,n){i[n]=t>>>24,i[n+1]=t>>>16&255,i[n+2]=t>>>8&255,i[n+3]=255&t}function g(t,i){return(t[i]|t[i+1]<<8|t[i+2]<<16|t[i+3]<<24)>>>0}function j(t,i){return(t[i]<<24|t[i+1]<<16|t[i+2]<<8|t[i+3])>>>0}i.exports=r(r)},{}],7:[function(t,i,n){function r(t){try{var i=eval("require")(t);if(i&&(i.length||Object.keys(i).length))return i}catch(t){}return null}i.exports=r},{}],8:[function(t,i,n){var n=n,e=n.isAbsolute=function(t){return/^(?:\/|\w+:)/.test(t)},r=n.normalize=function(t){var i=(t=t.replace(/\\/g,"/").replace(/\/{2,}/g,"/")).split("/"),n=e(t),t="";n&&(t=i.shift()+"/");for(var r=0;r>>1,s=null,o=r;return function(t){if(t<1||e>10),s[o++]=56320+(1023&r)):s[o++]=(15&r)<<12|(63&t[i++])<<6|63&t[i++],8191>6|192:(55296==(64512&r)&&56320==(64512&(e=t.charCodeAt(o+1)))?(++o,i[n++]=(r=65536+((1023&r)<<10)+(1023&e))>>18|240,i[n++]=r>>12&63|128):i[n++]=r>>12|224,i[n++]=r>>6&63|128),i[n++]=63&r|128);return n-s}},{}],11:[function(t,i,n){i.exports=e;var r=/\/|\./;function e(t,i){r.test(t)||(t="google/protobuf/"+t+".proto",i={nested:{google:{nested:{protobuf:{nested:i}}}}}),e[t]=i}e("any",{Any:{fields:{type_url:{type:"string",id:1},value:{type:"bytes",id:2}}}}),e("duration",{Duration:i={fields:{seconds:{type:"int64",id:1},nanos:{type:"int32",id:2}}}}),e("timestamp",{Timestamp:i}),e("empty",{Empty:{fields:{}}}),e("struct",{Struct:{fields:{fields:{keyType:"string",type:"Value",id:1}}},Value:{oneofs:{kind:{oneof:["nullValue","numberValue","stringValue","boolValue","structValue","listValue"]}},fields:{nullValue:{type:"NullValue",id:1},numberValue:{type:"double",id:2},stringValue:{type:"string",id:3},boolValue:{type:"bool",id:4},structValue:{type:"Struct",id:5},listValue:{type:"ListValue",id:6}}},NullValue:{values:{NULL_VALUE:0}},ListValue:{fields:{values:{rule:"repeated",type:"Value",id:1}}}}),e("wrappers",{DoubleValue:{fields:{value:{type:"double",id:1}}},FloatValue:{fields:{value:{type:"float",id:1}}},Int64Value:{fields:{value:{type:"int64",id:1}}},UInt64Value:{fields:{value:{type:"uint64",id:1}}},Int32Value:{fields:{value:{type:"int32",id:1}}},UInt32Value:{fields:{value:{type:"uint32",id:1}}},BoolValue:{fields:{value:{type:"bool",id:1}}},StringValue:{fields:{value:{type:"string",id:1}}},BytesValue:{fields:{value:{type:"bytes",id:1}}}}),e("field_mask",{FieldMask:{fields:{paths:{rule:"repeated",type:"string",id:1}}}}),e.get=function(t){return e[t]||null}},{}],12:[function(t,i,n){var n=n,l=t(15),v=t(37);function o(t,i,n,r){if(i.resolvedType)if(i.resolvedType instanceof l){t("switch(d%s){",r);for(var e=i.resolvedType.values,s=Object.keys(e),o=0;o>>0",r,r);break;case"int32":case"sint32":case"sfixed32":t("m%s=d%s|0",r,r);break;case"uint64":u=!0;case"int64":case"sint64":case"fixed64":case"sfixed64":t("if(util.Long)")("(m%s=util.Long.fromValue(d%s)).unsigned=%j",r,r,u)('else if(typeof d%s==="string")',r)("m%s=parseInt(d%s,10)",r,r)('else if(typeof d%s==="number")',r)("m%s=d%s",r,r)('else if(typeof d%s==="object")',r)("m%s=new util.LongBits(d%s.low>>>0,d%s.high>>>0).toNumber(%s)",r,r,r,u?"true":"");break;case"bytes":t('if(typeof d%s==="string")',r)("util.base64.decode(d%s,m%s=util.newBuffer(util.base64.length(d%s)),0)",r,r,r)("else if(d%s.length)",r)("m%s=d%s",r,r);break;case"string":t("m%s=String(d%s)",r,r);break;case"bool":t("m%s=Boolean(d%s)",r,r)}}return t}function d(t,i,n,r){if(i.resolvedType)i.resolvedType instanceof l?t("d%s=o.enums===String?types[%i].values[m%s]:m%s",r,n,r,r):t("d%s=types[%i].toObject(m%s,o)",r,n,r);else{var e=!1;switch(i.type){case"double":case"float":t("d%s=o.json&&!isFinite(m%s)?String(m%s):m%s",r,r,r,r);break;case"uint64":e=!0;case"int64":case"sint64":case"fixed64":case"sfixed64":t('if(typeof m%s==="number")',r)("d%s=o.longs===String?String(m%s):m%s",r,r,r)("else")("d%s=o.longs===String?util.Long.prototype.toString.call(m%s):o.longs===Number?new util.LongBits(m%s.low>>>0,m%s.high>>>0).toNumber(%s):m%s",r,r,r,r,e?"true":"",r);break;case"bytes":t("d%s=o.bytes===String?util.base64.encode(m%s,0,m%s.length):o.bytes===Array?Array.prototype.slice.call(m%s):m%s",r,r,r,r,r);break;default:t("d%s=m%s",r,r)}}return t}n.fromObject=function(t){var i=t.fieldsArray,n=v.codegen(["d"],t.name+"$fromObject")("if(d instanceof this.ctor)")("return d");if(!i.length)return n("return new this.ctor");n("var m=new this.ctor");for(var r=0;r>>3){");for(var n=0;n>>3){")("case 1: k=r.%s(); break",r.keyType)("case 2:"),f.basic[e]===Q?i("value=types[%i].decode(r,r.uint32())",n):i("value=r.%s()",e),i("break")("default:")("r.skipType(tag2&7)")("break")("}")("}"),f.long[r.keyType]!==Q?i('%s[typeof k==="object"?util.longToHash(k):k]=value',s):i("%s[k]=value",s)):r.repeated?(i("if(!(%s&&%s.length))",s,s)("%s=[]",s),f.packed[e]!==Q&&i("if((t&7)===2){")("var c2=r.uint32()+r.pos")("while(r.pos>>0,8|a.mapKey[s.keyType],s.keyType),f===Q?n("types[%i].encode(%s[ks[i]],w.uint32(18).fork()).ldelim().ldelim()",o,i):n(".uint32(%i).%s(%s[ks[i]]).ldelim()",16|f,u,i),n("}")("}")):s.repeated?(n("if(%s!=null&&%s.length){",i,i),s.packed&&a.packed[u]!==Q?n("w.uint32(%i).fork()",(s.id<<3|2)>>>0)("for(var i=0;i<%s.length;++i)",i)("w.%s(%s[i])",u,i)("w.ldelim()"):(n("for(var i=0;i<%s.length;++i)",i),f===Q?l(n,s,o,i+"[i]"):n("w.uint32(%i).%s(%s[i])",(s.id<<3|f)>>>0,u,i)),n("}")):(s.optional&&n("if(%s!=null&&Object.hasOwnProperty.call(m,%j))",i,s.name),f===Q?l(n,s,o,i):n("w.uint32(%i).%s(%s)",(s.id<<3|f)>>>0,u,i))}return n("return w")};var h=t(15),a=t(36),c=t(37);function l(t,i,n,r){return i.resolvedType.group?t("types[%i].encode(%s,w.uint32(%i)).uint32(%i)",n,r,(i.id<<3|3)>>>0,(i.id<<3|4)>>>0):t("types[%i].encode(%s,w.uint32(%i).fork()).ldelim()",n,r,(i.id<<3|2)>>>0)}},{15:15,36:36,37:37}],15:[function(t,i,n){i.exports=s;var u=t(24);((s.prototype=Object.create(u.prototype)).constructor=s).className="Enum";var r=t(23),e=t(37);function s(t,i,n,r,e){if(u.call(this,t,n),i&&"object"!=typeof i)throw TypeError("values must be an object");if(this.valuesById={},this.values=Object.create(this.valuesById),this.comment=r,this.comments=e||{},this.reserved=Q,i)for(var s=Object.keys(i),o=0;oi)return!0;return!1},a.isReservedName=function(t,i){if(t)for(var n=0;n");var r=a();if(!J.test(r))throw m(r,"name");v("=");var e=new I(y(r),O(a()),i,n);A(e,function(t){if("option"!==t)throw m(t);T(e,t),v(";")},function(){N(e)}),t.add(e)}(n);break;case"required":case"repeated":x(n,t);break;case"optional":x(n,b?"proto3_optional":"optional");break;case"oneof":!function(t,i){if(!J.test(i=a()))throw m(i,"name");var n=new F(y(i));A(n,function(t){"option"===t?(T(n,t),v(";")):(c(t),x(n,"optional"))}),t.add(n)}(n,t);break;case"extensions":k(n.extensions||(n.extensions=[]));break;case"reserved":k(n.reserved||(n.reserved=[]),!0);break;default:if(!b||!W.test(t))throw m(t);c(t),x(n,"optional")}}),t.add(n)}(t,i),1;case"enum":return function(t,i){if(!J.test(i=a()))throw m(i,"name");var n=new L(i);A(n,function(t){switch(t){case"option":T(n,t),v(";");break;case"reserved":k(n.reserved||(n.reserved=[]),!0);break;default:!function(t,i){if(!J.test(i))throw m(i,"name");v("=");var n=O(a(),!0),r={};A(r,function(t){if("option"!==t)throw m(t);T(r,t),v(";")},function(){N(r)}),t.add(i,n,r.comment)}(n,t)}}),t.add(n)}(t,i),1;case"service":return function(t,i){if(!J.test(i=a()))throw m(i,"service name");var n=new U(i);A(n,function(t){if(!E(n,t)){if("rpc"!==t)throw m(t);!function(t,i){var n=d(),r=i;if(!J.test(i=a()))throw m(i,"name");var e,s,o,u=i;v("("),v("stream",!0)&&(s=!0);if(!W.test(i=a()))throw m(i);e=i,v(")"),v("returns"),v("("),v("stream",!0)&&(o=!0);if(!W.test(i=a()))throw m(i);i=i,v(")");var f=new q(u,r,e,i,s,o);f.comment=n,A(f,function(t){if("option"!==t)throw m(t);T(f,t),v(";")}),t.add(f)}(n,t)}}),t.add(n)}(t,i),1;case"extend":return function(i,t){if(!W.test(t=a()))throw m(t,"reference");var n=t;A(null,function(t){switch(t){case"required":case"repeated":x(i,t,n);break;case"optional":x(i,b?"proto3_optional":"optional",n);break;default:if(!b||!W.test(t))throw m(t);c(t),x(i,"optional",n)}})}(t,i),1}}function A(t,i,n){var r,e=h.line;if(t&&("string"!=typeof t.comment&&(t.comment=d()),t.filename=K.filename),v("{",!0)){for(;"}"!==(r=a());)i(r);v(";",!0)}else n&&n(),v(";"),t&&("string"!=typeof t.comment||u)&&(t.comment=d(e)||t.comment)}function x(t,i,n){var r=a();if("group"!==r){if(!W.test(r))throw m(r,"type");var e=a();if(!J.test(e))throw m(e,"name");e=y(e),v("=");var s=new _(e,O(a()),r,i,n);A(s,function(t){if("option"!==t)throw m(t);T(s,t),v(";")},function(){N(s)}),"proto3_optional"===i?(e=new F("_"+e),s.setOption("proto3_optional",!0),e.add(s),t.add(e)):t.add(s),b||!s.repeated||R.packed[r]===Q&&R.basic[r]!==Q||s.setOption("packed",!1,!0)}else!function(t,i){var n=a();if(!J.test(n))throw m(n,"name");var r=z.lcFirst(n);n===r&&(n=z.ucFirst(n));v("=");var e=O(a()),s=new M(n);s.group=!0;i=new _(r,e,n,i);i.filename=K.filename,A(s,function(t){switch(t){case"option":T(s,t),v(";");break;case"required":case"repeated":x(s,t);break;case"optional":x(s,b?"proto3_optional":"optional");break;default:throw m(t)}}),t.add(s).add(i)}(t,i)}function T(t,i){var n=v("(",!0);if(!W.test(i=a()))throw m(i,"name");var r=i,e=r;n&&(v(")"),e=r="("+r+")",i=l(),G.test(i)&&(s=i.substr(1),r+=i,a())),v("=");var s,r=function t(i,n){if(v("{",!0)){for(var r={};!v("}",!0);){if(!J.test(f=a()))throw m(f,"name");var e,s=f;"{"===l()?e=t(i,n+"."+f):(v(":"),"{"===l()?e=t(i,n+"."+f):(e=j(!0),S(i,n+"."+f,e)));var o=r[s];o&&(e=[].concat(o).concat(e)),r[s]=e,v(",",!0)}return r}var u=j(!0);S(i,n,u);return u}(t,r);e=e,r=r,s=s,(t=t).setParsedOption&&t.setParsedOption(e,r,s)}function S(t,i,n){t.setOption&&t.setOption(i,n)}function N(t){if(v("[",!0)){for(;T(t,"option"),v(",",!0););v("]")}return t}for(;null!==(f=a());)switch(f){case"package":if(!p)throw m(f);!function(){if(r!==Q)throw m("package");if(r=a(),!W.test(r))throw m(r,"name");w=w.define(r),v(";")}();break;case"import":if(!p)throw m(f);!function(){var t,i=l();switch(i){case"weak":t=s=s||[],a();break;case"public":a();default:t=e=e||[]}i=g(),v(";"),t.push(i)}();break;case"syntax":if(!p)throw m(f);!function(){if(v("="),o=g(),!(b="proto3"===o)&&"proto2"!==o)throw m(o,"syntax");v(";")}();break;case"option":T(w,f),v(";");break;default:if(E(w,f)){p=!1;continue}throw m(f)}return K.filename=null,{package:r,imports:e,weakImports:s,syntax:o,root:i}}},{15:15,16:16,20:20,22:22,25:25,29:29,33:33,34:34,35:35,36:36,37:37}],27:[function(t,i,n){i.exports=f;var r,e=t(39),s=e.LongBits,o=e.utf8;function u(t,i){return RangeError("index out of range: "+t.pos+" + "+(i||1)+" > "+t.len)}function f(t){this.buf=t,this.pos=0,this.len=t.length}function h(){return e.Buffer?function(t){return(f.create=function(t){return e.Buffer.isBuffer(t)?new r(t):c(t)})(t)}:c}var a,c="undefined"!=typeof Uint8Array?function(t){if(t instanceof Uint8Array||Array.isArray(t))return new f(t);throw Error("illegal buffer")}:function(t){if(Array.isArray(t))return new f(t);throw Error("illegal buffer")};function l(){var t=new s(0,0),i=0;if(!(4=this.len)throw u(this);if(t.lo=(t.lo|(127&this.buf[this.pos])<<7*i)>>>0,this.buf[this.pos++]<128)return t}return t.lo=(t.lo|(127&this.buf[this.pos++])<<7*i)>>>0,t}for(;i<4;++i)if(t.lo=(t.lo|(127&this.buf[this.pos])<<7*i)>>>0,this.buf[this.pos++]<128)return t;if(t.lo=(t.lo|(127&this.buf[this.pos])<<28)>>>0,t.hi=(t.hi|(127&this.buf[this.pos])>>4)>>>0,this.buf[this.pos++]<128)return t;if(i=0,4>>0,this.buf[this.pos++]<128)return t}else for(;i<5;++i){if(this.pos>=this.len)throw u(this);if(t.hi=(t.hi|(127&this.buf[this.pos])<<7*i+3)>>>0,this.buf[this.pos++]<128)return t}throw Error("invalid varint encoding")}function v(t,i){return(t[i-4]|t[i-3]<<8|t[i-2]<<16|t[i-1]<<24)>>>0}function d(){if(this.pos+8>this.len)throw u(this,8);return new s(v(this.buf,this.pos+=4),v(this.buf,this.pos+=4))}f.create=h(),f.prototype.c=e.Array.prototype.subarray||e.Array.prototype.slice,f.prototype.uint32=(a=4294967295,function(){if(a=(127&this.buf[this.pos])>>>0,this.buf[this.pos++]<128)return a;if(a=(a|(127&this.buf[this.pos])<<7)>>>0,this.buf[this.pos++]<128)return a;if(a=(a|(127&this.buf[this.pos])<<14)>>>0,this.buf[this.pos++]<128)return a;if(a=(a|(127&this.buf[this.pos])<<21)>>>0,this.buf[this.pos++]<128)return a;if(a=(a|(15&this.buf[this.pos])<<28)>>>0,this.buf[this.pos++]<128)return a;if((this.pos+=5)>this.len)throw this.pos=this.len,u(this,10);return a}),f.prototype.int32=function(){return 0|this.uint32()},f.prototype.sint32=function(){var t=this.uint32();return t>>>1^-(1&t)|0},f.prototype.bool=function(){return 0!==this.uint32()},f.prototype.fixed32=function(){if(this.pos+4>this.len)throw u(this,4);return v(this.buf,this.pos+=4)},f.prototype.sfixed32=function(){if(this.pos+4>this.len)throw u(this,4);return 0|v(this.buf,this.pos+=4)},f.prototype.float=function(){if(this.pos+4>this.len)throw u(this,4);var t=e.float.readFloatLE(this.buf,this.pos);return this.pos+=4,t},f.prototype.double=function(){if(this.pos+8>this.len)throw u(this,4);var t=e.float.readDoubleLE(this.buf,this.pos);return this.pos+=8,t},f.prototype.bytes=function(){var t=this.uint32(),i=this.pos,n=this.pos+t;if(n>this.len)throw u(this,t);return this.pos+=t,Array.isArray(this.buf)?this.buf.slice(i,n):i===n?new this.buf.constructor(0):this.c.call(this.buf,i,n)},f.prototype.string=function(){var t=this.bytes();return o.read(t,0,t.length)},f.prototype.skip=function(t){if("number"==typeof t){if(this.pos+t>this.len)throw u(this,t);this.pos+=t}else do{if(this.pos>=this.len)throw u(this)}while(128&this.buf[this.pos++]);return this},f.prototype.skipType=function(t){switch(t){case 0:this.skip();break;case 1:this.skip(8);break;case 2:this.skip(this.uint32());break;case 3:for(;4!=(t=7&this.uint32());)this.skipType(t);break;case 5:this.skip(4);break;default:throw Error("invalid wire type "+t+" at offset "+this.pos)}return this},f.u=function(t){r=t,f.create=h(),r.u();var i=e.Long?"toLong":"toNumber";e.merge(f.prototype,{int64:function(){return l.call(this)[i](!1)},uint64:function(){return l.call(this)[i](!0)},sint64:function(){return l.call(this).zzDecode()[i](!1)},fixed64:function(){return d.call(this)[i](!0)},sfixed64:function(){return d.call(this)[i](!1)}})}},{39:39}],28:[function(t,i,n){i.exports=s;var r=t(27);(s.prototype=Object.create(r.prototype)).constructor=s;var e=t(39);function s(t){r.call(this,t)}s.u=function(){e.Buffer&&(s.prototype.c=e.Buffer.prototype.slice)},s.prototype.string=function(){var t=this.uint32();return this.buf.utf8Slice?this.buf.utf8Slice(this.pos,this.pos=Math.min(this.pos+t,this.len)):this.buf.toString("utf-8",this.pos,this.pos=Math.min(this.pos+t,this.len))},s.u()},{27:27,39:39}],29:[function(t,i,n){i.exports=f;var r=t(23);((f.prototype=Object.create(r.prototype)).constructor=f).className="Root";var e,v,d,s=t(16),o=t(15),u=t(25),p=t(37);function f(t){r.call(this,"",t),this.deferred=[],this.files=[]}function b(){}f.fromJSON=function(t,i){return i=i||new f,t.options&&i.setOptions(t.options),i.addJSON(t.nested)},f.prototype.resolvePath=p.path.resolve,f.prototype.fetch=p.fetch,f.prototype.load=function t(i,s,e){"function"==typeof s&&(e=s,s=Q);var o=this;if(!e)return p.asPromise(t,o,i,s);var u=e===b;function f(t,i){if(e){var n=e;if(e=null,u)throw t;n(t,i)}}function h(t){var i=t.lastIndexOf("google/protobuf/");if(-1]/g,x=/(?:"([^"\\]*(?:\\.[^"\\]*)*)")/g,T=/(?:'([^'\\]*(?:\\.[^'\\]*)*)')/g,S=/^ *[*/]+ */,N=/^\s*\*?\/*/,V=/\n/g,$=/\s/,r=/\\(.?)/g,e={0:"\0",r:"\r",n:"\n",t:"\t"};function M(t){return t.replace(r,function(t,i){switch(i){case"\\":case"":return i;default:return e[i]||""}})}function s(f,h){f=f.toString();var a=0,c=f.length,l=1,u=null,v=null,d=0,p=!1,b=!1,w=[],y=null;function m(t){return Error("illegal "+t+" (line "+l+")")}function g(t){return f[0|t]||""}function j(t,i,n){u=f[0|t++]||"",d=l,p=!1,b=n;var r,e=t-(h?2:3);do{if(--e<0||"\n"==(r=f[0|e]||"")){p=!0;break}}while(" "===r||"\t"===r);for(var s=f.substring(t,i).split(V),o=0;o>>0,this.hi=i>>>0}var s=e.zero=new e(0,0);s.toNumber=function(){return 0},s.zzEncode=s.zzDecode=function(){return this},s.length=function(){return 1};e.zeroHash="\0\0\0\0\0\0\0\0";e.fromNumber=function(t){if(0===t)return s;var i=t<0,n=(t=i?-t:t)>>>0,t=(t-n)/4294967296>>>0;return i&&(t=~t>>>0,n=~n>>>0,4294967295<++n&&(n=0,4294967295<++t&&(t=0))),new e(n,t)},e.from=function(t){if("number"==typeof t)return e.fromNumber(t);if(r.isString(t)){if(!r.Long)return e.fromNumber(parseInt(t,10));t=r.Long.fromString(t)}return t.low||t.high?new e(t.low>>>0,t.high>>>0):s},e.prototype.toNumber=function(t){if(!t&&this.hi>>>31){var i=1+~this.lo>>>0,t=~this.hi>>>0;return-(i+4294967296*(t=!i?t+1>>>0:t))}return this.lo+4294967296*this.hi},e.prototype.toLong=function(t){return r.Long?new r.Long(0|this.lo,0|this.hi,!!t):{low:0|this.lo,high:0|this.hi,unsigned:!!t}};var o=String.prototype.charCodeAt;e.fromHash=function(t){return"\0\0\0\0\0\0\0\0"===t?s:new e((o.call(t,0)|o.call(t,1)<<8|o.call(t,2)<<16|o.call(t,3)<<24)>>>0,(o.call(t,4)|o.call(t,5)<<8|o.call(t,6)<<16|o.call(t,7)<<24)>>>0)},e.prototype.toHash=function(){return String.fromCharCode(255&this.lo,this.lo>>>8&255,this.lo>>>16&255,this.lo>>>24,255&this.hi,this.hi>>>8&255,this.hi>>>16&255,this.hi>>>24)},e.prototype.zzEncode=function(){var t=this.hi>>31;return this.hi=((this.hi<<1|this.lo>>>31)^t)>>>0,this.lo=(this.lo<<1^t)>>>0,this},e.prototype.zzDecode=function(){var t=-(1&this.lo);return this.lo=((this.lo>>>1|this.hi<<31)^t)>>>0,this.hi=(this.hi>>>1^t)>>>0,this},e.prototype.length=function(){var t=this.lo,i=(this.lo>>>28|this.hi<<4)>>>0,n=this.hi>>>24;return 0==n?0==i?t<16384?t<128?1:2:t<2097152?3:4:i<16384?i<128?5:6:i<2097152?7:8:n<128?9:10}},{39:39}],39:[function(t,i,n){var r=n;function e(t,i,n){for(var r=Object.keys(i),e=0;e>>7|t.hi<<25)>>>0,t.hi>>>=7;for(;127>>7;i[n++]=t.lo}function b(t,i,n){i[n]=255&t,i[n+1]=t>>>8&255,i[n+2]=t>>>16&255,i[n+3]=t>>>24}c.create=l(),c.alloc=function(t){return new e.Array(t)},e.Array!==Array&&(c.alloc=e.pool(c.alloc,e.Array.prototype.subarray)),c.prototype.g=function(t,i,n){return this.tail=this.tail.next=new f(t,i,n),this.len+=i,this},(d.prototype=Object.create(f.prototype)).fn=function(t,i,n){for(;127>>=7;i[n]=t},c.prototype.uint32=function(t){return this.len+=(this.tail=this.tail.next=new d((t>>>=0)<128?1:t<16384?2:t<2097152?3:t<268435456?4:5,t)).len,this},c.prototype.int32=function(t){return t<0?this.g(p,10,s.fromNumber(t)):this.uint32(t)},c.prototype.sint32=function(t){return this.uint32((t<<1^t>>31)>>>0)},c.prototype.int64=c.prototype.uint64=function(t){t=s.from(t);return this.g(p,t.length(),t)},c.prototype.sint64=function(t){t=s.from(t).zzEncode();return this.g(p,t.length(),t)},c.prototype.bool=function(t){return this.g(v,1,t?1:0)},c.prototype.sfixed32=c.prototype.fixed32=function(t){return this.g(b,4,t>>>0)},c.prototype.sfixed64=c.prototype.fixed64=function(t){t=s.from(t);return this.g(b,4,t.lo).g(b,4,t.hi)},c.prototype.float=function(t){return this.g(e.float.writeFloatLE,4,t)},c.prototype.double=function(t){return this.g(e.float.writeDoubleLE,8,t)};var w=e.Array.prototype.set?function(t,i,n){i.set(t,n)}:function(t,i,n){for(var r=0;r>>0;return n?(e.isString(t)&&(i=c.alloc(n=o.length(t)),o.decode(t,i,0),t=i),this.uint32(n).g(w,n,t)):this.g(v,1,0)},c.prototype.string=function(t){var i=u.length(t);return i?this.uint32(i).g(u.write,i,t):this.g(v,1,0)},c.prototype.fork=function(){return this.states=new a(this),this.head=this.tail=new f(h,0,0),this.len=0,this},c.prototype.reset=function(){return this.states?(this.head=this.states.head,this.tail=this.states.tail,this.len=this.states.len,this.states=this.states.next):(this.head=this.tail=new f(h,0,0),this.len=0),this},c.prototype.ldelim=function(){var t=this.head,i=this.tail,n=this.len;return this.reset().uint32(n),n&&(this.tail.next=t.next,this.tail=i,this.len+=n),this},c.prototype.finish=function(){for(var t=this.head.next,i=this.constructor.alloc(this.len),n=0;t;)t.fn(t.val,i,n),n+=t.len,t=t.next;return i},c.u=function(t){r=t,c.create=l(),r.u()}},{39:39}],43:[function(t,i,n){i.exports=s;var r=t(42);(s.prototype=Object.create(r.prototype)).constructor=s;var e=t(39);function s(){r.call(this)}function o(t,i,n){t.length<40?e.utf8.write(t,i,n):i.utf8Write?i.utf8Write(t,n):i.write(t,n)}s.u=function(){s.alloc=e.y,s.writeBytesBuffer=e.Buffer&&e.Buffer.prototype instanceof Uint8Array&&"set"===e.Buffer.prototype.set.name?function(t,i,n){i.set(t,n)}:function(t,i,n){if(t.copy)t.copy(i,n,0,t.length);else for(var r=0;r>>0;return this.uint32(i),i&&this.g(s.writeBytesBuffer,i,t),this},s.prototype.string=function(t){var i=e.Buffer.byteLength(t);return this.uint32(i),i&&this.g(o,i,t),this},s.u()},{39:39,42:42}]},e={},t=[19],i=function t(i){var n=e[i];return n||r[i][0].call(n=e[i]={exports:{}},t,n,n.exports),n.exports}(t[0]),i.util.global.protobuf=i,"function"==typeof define&&define.amd&&define(["long"],function(t){return t&&t.isLong&&(i.util.Long=t,i.configure()),i}),"object"==typeof module&&module&&module.exports&&(module.exports=i)}(); +//# sourceMappingURL=protobuf.min.js.map diff --git a/src/frontend/Hanami-AI-Dashboard-Dependencies/protobuf.min.js.map b/src/frontend/Hanami-AI-Dashboard-Dependencies/protobuf.min.js.map new file mode 100644 index 00000000..b7e88da4 --- /dev/null +++ b/src/frontend/Hanami-AI-Dashboard-Dependencies/protobuf.min.js.map @@ -0,0 +1 @@ +{"version":3,"sources":["lib/prelude.js","../node_modules/@protobufjs/aspromise/index.js","../node_modules/@protobufjs/base64/index.js","../node_modules/@protobufjs/codegen/index.js","../node_modules/@protobufjs/eventemitter/index.js","../node_modules/@protobufjs/fetch/index.js","../node_modules/@protobufjs/float/index.js","../node_modules/@protobufjs/inquire/index.js","../node_modules/@protobufjs/path/index.js","../node_modules/@protobufjs/pool/index.js","../node_modules/@protobufjs/utf8/index.js","../src/common.js","../src/converter.js","../src/decoder.js","../src/encoder.js","../src/enum.js","../src/field.js","../src/index-light.js","../src/index-minimal.js","../src/index","../src/mapfield.js","../src/message.js","../src/method.js","../src/namespace.js","../src/object.js","../src/oneof.js","../src/parse.js","../src/reader.js","../src/reader_buffer.js","../src/root.js","../src/roots.js","../src/rpc.js","../src/rpc/service.js","../src/service.js","../src/tokenize.js","../src/type.js","../src/types.js","../src/util.js","../src/util/longbits.js","../src/util/minimal.js","../src/verifier.js","../src/wrappers.js","../src/writer.js","../src/writer_buffer.js"],"names":["undefined","modules","cache","entries","protobuf","1","require","module","exports","fn","ctx","params","Array","arguments","length","offset","index","pending","Promise","resolve","reject","err","apply","base64","string","p","n","Math","ceil","b64","s64","i","encode","buffer","start","end","t","parts","chunk","j","b","push","String","fromCharCode","slice","join","invalidEncoding","decode","c","charCodeAt","Error","test","codegen","functionParams","functionName","body","Codegen","formatStringOrScope","source","toString","verbose","console","log","scopeKeys","Object","keys","scopeParams","scopeValues","scopeOffset","Function","formatParams","formatOffset","replace","$0","$1","value","Number","floor","JSON","stringify","functionNameOverride","EventEmitter","this","_listeners","prototype","on","evt","off","listeners","splice","emit","args","fetch","asPromise","fs","inquire","filename","options","callback","xhr","readFile","contents","XMLHttpRequest","binary","onreadystatechange","readyState","status","response","responseText","Uint8Array","overrideMimeType","responseType","open","send","factory","writeFloat_ieee754","writeUint","val","buf","pos","sign","isNaN","round","exponent","LN2","pow","readFloat_ieee754","readUint","uint","mantissa","NaN","Infinity","writeFloat_f32_cpy","f32","f8b","writeFloat_f32_rev","readFloat_f32_cpy","readFloat_f32_rev","f64","le","writeDouble_ieee754","off0","off1","readDouble_ieee754","lo","hi","writeDouble_f64_cpy","writeDouble_f64_rev","readDouble_f64_cpy","readDouble_f64_rev","Float32Array","writeFloatLE","writeFloatBE","readFloatLE","readFloatBE","bind","writeUintLE","writeUintBE","readUintLE","readUintBE","Float64Array","writeDoubleLE","writeDoubleBE","readDoubleLE","readDoubleBE","moduleName","mod","eval","e","path","isAbsolute","normalize","split","absolute","prefix","shift","originPath","includePath","alreadyNormalized","alloc","size","SIZE","MAX","slab","call","utf8","len","read","write","c1","c2","common","commonRe","name","json","nested","google","Any","fields","type_url","type","id","Duration","timeType","seconds","nanos","Timestamp","Empty","Struct","keyType","Value","oneofs","kind","oneof","nullValue","numberValue","stringValue","boolValue","structValue","listValue","NullValue","values","NULL_VALUE","ListValue","rule","DoubleValue","FloatValue","Int64Value","UInt64Value","Int32Value","UInt32Value","BoolValue","StringValue","BytesValue","FieldMask","paths","get","file","converter","Enum","util","genValuePartial_fromObject","gen","field","fieldIndex","prop","resolvedType","repeated","typeDefault","fullName","isUnsigned","genValuePartial_toObject","fromObject","mtype","fieldsArray","safeProp","map","toObject","sort","compareFieldsById","repeatedFields","mapFields","normalFields","partOf","arrayDefault","valuesById","long","low","high","unsigned","toNumber","bytes","hasKs2","_fieldsArray","indexOf","filter","group","ref","types","defaults","basic","packed","rfield","required","wireType","mapKey","genTypePartial","optional","ReflectionObject","create","constructor","className","Namespace","comment","comments","TypeError","reserved","fromJSON","enm","toJSON","toJSONOptions","keepComments","add","isString","isInteger","isReservedId","isReservedName","allow_alias","remove","Field","Type","ruleRe","extend","isObject","toLowerCase","message","defaultValue","Long","extensionField","declaringField","_packed","defineProperty","getOption","setOption","ifNotSet","resolved","parent","lookupTypeOrEnum","fromNumber","freeze","newBuffer","emptyObject","emptyArray","ctor","d","fieldId","fieldType","fieldRule","decorateType","decorateEnum","fieldName","default","_configure","Type_","build","load","root","Root","loadSync","encoder","decoder","verifier","OneOf","MapField","Service","Method","Message","wrappers","configure","Writer","BufferWriter","Reader","BufferReader","rpc","roots","tokenize","parse","resolvedKeyType","fieldKeyType","fieldValueType","properties","$type","writer","encodeDelimited","reader","decodeDelimited","verify","object","requestType","requestStream","responseStream","parsedOptions","resolvedRequestType","resolvedResponseType","lookupType","arrayToJSON","array","obj","_nestedArray","clearCache","namespace","addJSON","toArray","nestedArray","nestedJson","names","methods","getEnum","prev","setOptions","onAdd","onRemove","define","isArray","ptr","part","resolveAll","lookup","filterTypes","parentAlreadyChecked","found","lookupEnum","lookupService","Service_","Enum_","defineProperties","unshift","_handleAdd","_handleRemove","setParsedOption","propName","newValue","newOpt","opt","find","hasOwnProperty","setProperty","Root_","fieldNames","addFieldsToParent","oneofName","oneOfGetter","set","oneOfSetter","keepCase","base10Re","base10NegRe","base16Re","base16NegRe","base8Re","base8NegRe","numberRe","nameRe","typeRefRe","fqTypeRefRe","pkg","imports","weakImports","syntax","token","preferTrailingComment","tn","alternateCommentMode","next","peek","skip","cmnt","head","isProto3","applyCase","camelCase","illegal","insideTryCatch","line","readString","readValue","acceptTypeRef","substring","parseInt","parseFloat","parseNumber","readRanges","target","acceptStrings","parseId","acceptNegative","parseCommon","parseOption","ifBlock","valueType","parseInlineOptions","parseMapField","parseField","parseOneOf","extensions","parseType","dummy","parseEnumValue","parseEnum","service","commentText","method","parseMethod","parseService","reference","parseExtension","fnIf","fnElse","trailingLine","lcFirst","ucFirst","parseGroup","isCustom","option","substr","optionValue","parseOptionValue","result","prevValue","concat","simpleValue","parsePackage","whichImports","parseImport","parseSyntax","package","LongBits","indexOutOfRange","writeLength","RangeError","Buffer","isBuffer","create_array","readLongVarint","bits","readFixed32_end","readFixed64","_slice","subarray","uint32","int32","sint32","bool","fixed32","sfixed32","float","double","skipType","BufferReader_","merge","int64","uint64","sint64","zzDecode","fixed64","sfixed64","utf8Slice","min","deferred","files","SYNC","resolvePath","self","sync","finish","cb","getBundledFileName","idx","lastIndexOf","altname","process","parsed","queued","weak","setTimeout","readFileSync","isNode","exposeRe","tryHandleExtension","extendedType","sisterField","parse_","common_","rpcImpl","requestDelimited","responseDelimited","rpcCall","requestCtor","responseCtor","request","endedByRPC","_methodsArray","inherited","methodsArray","rpcService","methodName","isReserved","m","q","s","delimRe","stringDoubleRe","stringSingleRe","setCommentRe","setCommentAltRe","setCommentSplitRe","whitespaceRe","unescapeRe","unescapeMap","0","r","unescape","str","commentType","commentLine","commentLineEmpty","commentIsLeading","stack","stringDelim","subject","charAt","setComment","isLeading","commentOffset","lines","trim","isDoubleSlashCommentLine","startOffset","endOffset","findEndOfLine","lineText","cursor","re","lastIndex","match","exec","repeat","curr","isDoc","isLeadingComment","expected","actual","ret","_fieldsById","_oneofsArray","_ctor","fieldsById","oneofsArray","generateConstructor","ctorProperties","setup","wrapper","originalThis","fork","ldelim","typeName","bake","o","key","safePropBackslashRe","safePropQuoteRe","toUpperCase","camelCaseRe","a","decorateRoot","enumerable","decorateEnumIndex","dst","setProp","zero","zzEncode","zeroHash","from","fromString","toLong","fromHash","hash","toHash","mask","part0","part1","part2","src","newError","CustomError","captureStackTrace","pool","global","versions","node","window","isFinite","isset","isSet","utf8Write","_Buffer_from","_Buffer_allocUnsafe","sizeOrArray","dcodeIO","key2Re","key32Re","key64Re","longToHash","longFromHash","fromBits","ProtocolError","fieldMap","longs","enums","encoding","allocUnsafe","seenFirstField","oneofProp","invalid","genVerifyKey","genVerifyValue","messageName","Op","noop","State","tail","states","writeByte","VarintOp","writeVarint64","writeFixed32","_push","writeBytes","reset","BufferWriter_","writeStringBuffer","writeBytesBuffer","copy","byteLength","$require","$module","amd","isLong"],"mappings":"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","file":"protobuf.min.js","sourcesContent":["(function prelude(modules, cache, entries) {\n\n // This is the prelude used to bundle protobuf.js for the browser. Wraps up the CommonJS\n // sources through a conflict-free require shim and is again wrapped within an iife that\n // provides a minification-friendly `undefined` var plus a global \"use strict\" directive\n // so that minification can remove the directives of each module.\n\n function $require(name) {\n var $module = cache[name];\n if (!$module)\n modules[name][0].call($module = cache[name] = { exports: {} }, $require, $module, $module.exports);\n return $module.exports;\n }\n\n var protobuf = $require(entries[0]);\n\n // Expose globally\n protobuf.util.global.protobuf = protobuf;\n\n // Be nice to AMD\n if (typeof define === \"function\" && define.amd)\n define([\"long\"], function(Long) {\n if (Long && Long.isLong) {\n protobuf.util.Long = Long;\n protobuf.configure();\n }\n return protobuf;\n });\n\n // Be nice to CommonJS\n if (typeof module === \"object\" && module && module.exports)\n module.exports = protobuf;\n\n})/* end of prelude */","\"use strict\";\r\nmodule.exports = asPromise;\r\n\r\n/**\r\n * Callback as used by {@link util.asPromise}.\r\n * @typedef asPromiseCallback\r\n * @type {function}\r\n * @param {Error|null} error Error, if any\r\n * @param {...*} params Additional arguments\r\n * @returns {undefined}\r\n */\r\n\r\n/**\r\n * Returns a promise from a node-style callback function.\r\n * @memberof util\r\n * @param {asPromiseCallback} fn Function to call\r\n * @param {*} ctx Function context\r\n * @param {...*} params Function arguments\r\n * @returns {Promise<*>} Promisified function\r\n */\r\nfunction asPromise(fn, ctx/*, varargs */) {\r\n var params = new Array(arguments.length - 1),\r\n offset = 0,\r\n index = 2,\r\n pending = true;\r\n while (index < arguments.length)\r\n params[offset++] = arguments[index++];\r\n return new Promise(function executor(resolve, reject) {\r\n params[offset] = function callback(err/*, varargs */) {\r\n if (pending) {\r\n pending = false;\r\n if (err)\r\n reject(err);\r\n else {\r\n var params = new Array(arguments.length - 1),\r\n offset = 0;\r\n while (offset < params.length)\r\n params[offset++] = arguments[offset];\r\n resolve.apply(null, params);\r\n }\r\n }\r\n };\r\n try {\r\n fn.apply(ctx || null, params);\r\n } catch (err) {\r\n if (pending) {\r\n pending = false;\r\n reject(err);\r\n }\r\n }\r\n });\r\n}\r\n","\"use strict\";\r\n\r\n/**\r\n * A minimal base64 implementation for number arrays.\r\n * @memberof util\r\n * @namespace\r\n */\r\nvar base64 = exports;\r\n\r\n/**\r\n * Calculates the byte length of a base64 encoded string.\r\n * @param {string} string Base64 encoded string\r\n * @returns {number} Byte length\r\n */\r\nbase64.length = function length(string) {\r\n var p = string.length;\r\n if (!p)\r\n return 0;\r\n var n = 0;\r\n while (--p % 4 > 1 && string.charAt(p) === \"=\")\r\n ++n;\r\n return Math.ceil(string.length * 3) / 4 - n;\r\n};\r\n\r\n// Base64 encoding table\r\nvar b64 = new Array(64);\r\n\r\n// Base64 decoding table\r\nvar s64 = new Array(123);\r\n\r\n// 65..90, 97..122, 48..57, 43, 47\r\nfor (var i = 0; i < 64;)\r\n s64[b64[i] = i < 26 ? i + 65 : i < 52 ? i + 71 : i < 62 ? i - 4 : i - 59 | 43] = i++;\r\n\r\n/**\r\n * Encodes a buffer to a base64 encoded string.\r\n * @param {Uint8Array} buffer Source buffer\r\n * @param {number} start Source start\r\n * @param {number} end Source end\r\n * @returns {string} Base64 encoded string\r\n */\r\nbase64.encode = function encode(buffer, start, end) {\r\n var parts = null,\r\n chunk = [];\r\n var i = 0, // output index\r\n j = 0, // goto index\r\n t; // temporary\r\n while (start < end) {\r\n var b = buffer[start++];\r\n switch (j) {\r\n case 0:\r\n chunk[i++] = b64[b >> 2];\r\n t = (b & 3) << 4;\r\n j = 1;\r\n break;\r\n case 1:\r\n chunk[i++] = b64[t | b >> 4];\r\n t = (b & 15) << 2;\r\n j = 2;\r\n break;\r\n case 2:\r\n chunk[i++] = b64[t | b >> 6];\r\n chunk[i++] = b64[b & 63];\r\n j = 0;\r\n break;\r\n }\r\n if (i > 8191) {\r\n (parts || (parts = [])).push(String.fromCharCode.apply(String, chunk));\r\n i = 0;\r\n }\r\n }\r\n if (j) {\r\n chunk[i++] = b64[t];\r\n chunk[i++] = 61;\r\n if (j === 1)\r\n chunk[i++] = 61;\r\n }\r\n if (parts) {\r\n if (i)\r\n parts.push(String.fromCharCode.apply(String, chunk.slice(0, i)));\r\n return parts.join(\"\");\r\n }\r\n return String.fromCharCode.apply(String, chunk.slice(0, i));\r\n};\r\n\r\nvar invalidEncoding = \"invalid encoding\";\r\n\r\n/**\r\n * Decodes a base64 encoded string to a buffer.\r\n * @param {string} string Source string\r\n * @param {Uint8Array} buffer Destination buffer\r\n * @param {number} offset Destination offset\r\n * @returns {number} Number of bytes written\r\n * @throws {Error} If encoding is invalid\r\n */\r\nbase64.decode = function decode(string, buffer, offset) {\r\n var start = offset;\r\n var j = 0, // goto index\r\n t; // temporary\r\n for (var i = 0; i < string.length;) {\r\n var c = string.charCodeAt(i++);\r\n if (c === 61 && j > 1)\r\n break;\r\n if ((c = s64[c]) === undefined)\r\n throw Error(invalidEncoding);\r\n switch (j) {\r\n case 0:\r\n t = c;\r\n j = 1;\r\n break;\r\n case 1:\r\n buffer[offset++] = t << 2 | (c & 48) >> 4;\r\n t = c;\r\n j = 2;\r\n break;\r\n case 2:\r\n buffer[offset++] = (t & 15) << 4 | (c & 60) >> 2;\r\n t = c;\r\n j = 3;\r\n break;\r\n case 3:\r\n buffer[offset++] = (t & 3) << 6 | c;\r\n j = 0;\r\n break;\r\n }\r\n }\r\n if (j === 1)\r\n throw Error(invalidEncoding);\r\n return offset - start;\r\n};\r\n\r\n/**\r\n * Tests if the specified string appears to be base64 encoded.\r\n * @param {string} string String to test\r\n * @returns {boolean} `true` if probably base64 encoded, otherwise false\r\n */\r\nbase64.test = function test(string) {\r\n return /^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$/.test(string);\r\n};\r\n","\"use strict\";\r\nmodule.exports = codegen;\r\n\r\n/**\r\n * Begins generating a function.\r\n * @memberof util\r\n * @param {string[]} functionParams Function parameter names\r\n * @param {string} [functionName] Function name if not anonymous\r\n * @returns {Codegen} Appender that appends code to the function's body\r\n */\r\nfunction codegen(functionParams, functionName) {\r\n\r\n /* istanbul ignore if */\r\n if (typeof functionParams === \"string\") {\r\n functionName = functionParams;\r\n functionParams = undefined;\r\n }\r\n\r\n var body = [];\r\n\r\n /**\r\n * Appends code to the function's body or finishes generation.\r\n * @typedef Codegen\r\n * @type {function}\r\n * @param {string|Object.} [formatStringOrScope] Format string or, to finish the function, an object of additional scope variables, if any\r\n * @param {...*} [formatParams] Format parameters\r\n * @returns {Codegen|Function} Itself or the generated function if finished\r\n * @throws {Error} If format parameter counts do not match\r\n */\r\n\r\n function Codegen(formatStringOrScope) {\r\n // note that explicit array handling below makes this ~50% faster\r\n\r\n // finish the function\r\n if (typeof formatStringOrScope !== \"string\") {\r\n var source = toString();\r\n if (codegen.verbose)\r\n console.log(\"codegen: \" + source); // eslint-disable-line no-console\r\n source = \"return \" + source;\r\n if (formatStringOrScope) {\r\n var scopeKeys = Object.keys(formatStringOrScope),\r\n scopeParams = new Array(scopeKeys.length + 1),\r\n scopeValues = new Array(scopeKeys.length),\r\n scopeOffset = 0;\r\n while (scopeOffset < scopeKeys.length) {\r\n scopeParams[scopeOffset] = scopeKeys[scopeOffset];\r\n scopeValues[scopeOffset] = formatStringOrScope[scopeKeys[scopeOffset++]];\r\n }\r\n scopeParams[scopeOffset] = source;\r\n return Function.apply(null, scopeParams).apply(null, scopeValues); // eslint-disable-line no-new-func\r\n }\r\n return Function(source)(); // eslint-disable-line no-new-func\r\n }\r\n\r\n // otherwise append to body\r\n var formatParams = new Array(arguments.length - 1),\r\n formatOffset = 0;\r\n while (formatOffset < formatParams.length)\r\n formatParams[formatOffset] = arguments[++formatOffset];\r\n formatOffset = 0;\r\n formatStringOrScope = formatStringOrScope.replace(/%([%dfijs])/g, function replace($0, $1) {\r\n var value = formatParams[formatOffset++];\r\n switch ($1) {\r\n case \"d\": case \"f\": return String(Number(value));\r\n case \"i\": return String(Math.floor(value));\r\n case \"j\": return JSON.stringify(value);\r\n case \"s\": return String(value);\r\n }\r\n return \"%\";\r\n });\r\n if (formatOffset !== formatParams.length)\r\n throw Error(\"parameter count mismatch\");\r\n body.push(formatStringOrScope);\r\n return Codegen;\r\n }\r\n\r\n function toString(functionNameOverride) {\r\n return \"function \" + (functionNameOverride || functionName || \"\") + \"(\" + (functionParams && functionParams.join(\",\") || \"\") + \"){\\n \" + body.join(\"\\n \") + \"\\n}\";\r\n }\r\n\r\n Codegen.toString = toString;\r\n return Codegen;\r\n}\r\n\r\n/**\r\n * Begins generating a function.\r\n * @memberof util\r\n * @function codegen\r\n * @param {string} [functionName] Function name if not anonymous\r\n * @returns {Codegen} Appender that appends code to the function's body\r\n * @variation 2\r\n */\r\n\r\n/**\r\n * When set to `true`, codegen will log generated code to console. Useful for debugging.\r\n * @name util.codegen.verbose\r\n * @type {boolean}\r\n */\r\ncodegen.verbose = false;\r\n","\"use strict\";\r\nmodule.exports = EventEmitter;\r\n\r\n/**\r\n * Constructs a new event emitter instance.\r\n * @classdesc A minimal event emitter.\r\n * @memberof util\r\n * @constructor\r\n */\r\nfunction EventEmitter() {\r\n\r\n /**\r\n * Registered listeners.\r\n * @type {Object.}\r\n * @private\r\n */\r\n this._listeners = {};\r\n}\r\n\r\n/**\r\n * Registers an event listener.\r\n * @param {string} evt Event name\r\n * @param {function} fn Listener\r\n * @param {*} [ctx] Listener context\r\n * @returns {util.EventEmitter} `this`\r\n */\r\nEventEmitter.prototype.on = function on(evt, fn, ctx) {\r\n (this._listeners[evt] || (this._listeners[evt] = [])).push({\r\n fn : fn,\r\n ctx : ctx || this\r\n });\r\n return this;\r\n};\r\n\r\n/**\r\n * Removes an event listener or any matching listeners if arguments are omitted.\r\n * @param {string} [evt] Event name. Removes all listeners if omitted.\r\n * @param {function} [fn] Listener to remove. Removes all listeners of `evt` if omitted.\r\n * @returns {util.EventEmitter} `this`\r\n */\r\nEventEmitter.prototype.off = function off(evt, fn) {\r\n if (evt === undefined)\r\n this._listeners = {};\r\n else {\r\n if (fn === undefined)\r\n this._listeners[evt] = [];\r\n else {\r\n var listeners = this._listeners[evt];\r\n for (var i = 0; i < listeners.length;)\r\n if (listeners[i].fn === fn)\r\n listeners.splice(i, 1);\r\n else\r\n ++i;\r\n }\r\n }\r\n return this;\r\n};\r\n\r\n/**\r\n * Emits an event by calling its listeners with the specified arguments.\r\n * @param {string} evt Event name\r\n * @param {...*} args Arguments\r\n * @returns {util.EventEmitter} `this`\r\n */\r\nEventEmitter.prototype.emit = function emit(evt) {\r\n var listeners = this._listeners[evt];\r\n if (listeners) {\r\n var args = [],\r\n i = 1;\r\n for (; i < arguments.length;)\r\n args.push(arguments[i++]);\r\n for (i = 0; i < listeners.length;)\r\n listeners[i].fn.apply(listeners[i++].ctx, args);\r\n }\r\n return this;\r\n};\r\n","\"use strict\";\r\nmodule.exports = fetch;\r\n\r\nvar asPromise = require(1),\r\n inquire = require(7);\r\n\r\nvar fs = inquire(\"fs\");\r\n\r\n/**\r\n * Node-style callback as used by {@link util.fetch}.\r\n * @typedef FetchCallback\r\n * @type {function}\r\n * @param {?Error} error Error, if any, otherwise `null`\r\n * @param {string} [contents] File contents, if there hasn't been an error\r\n * @returns {undefined}\r\n */\r\n\r\n/**\r\n * Options as used by {@link util.fetch}.\r\n * @typedef FetchOptions\r\n * @type {Object}\r\n * @property {boolean} [binary=false] Whether expecting a binary response\r\n * @property {boolean} [xhr=false] If `true`, forces the use of XMLHttpRequest\r\n */\r\n\r\n/**\r\n * Fetches the contents of a file.\r\n * @memberof util\r\n * @param {string} filename File path or url\r\n * @param {FetchOptions} options Fetch options\r\n * @param {FetchCallback} callback Callback function\r\n * @returns {undefined}\r\n */\r\nfunction fetch(filename, options, callback) {\r\n if (typeof options === \"function\") {\r\n callback = options;\r\n options = {};\r\n } else if (!options)\r\n options = {};\r\n\r\n if (!callback)\r\n return asPromise(fetch, this, filename, options); // eslint-disable-line no-invalid-this\r\n\r\n // if a node-like filesystem is present, try it first but fall back to XHR if nothing is found.\r\n if (!options.xhr && fs && fs.readFile)\r\n return fs.readFile(filename, function fetchReadFileCallback(err, contents) {\r\n return err && typeof XMLHttpRequest !== \"undefined\"\r\n ? fetch.xhr(filename, options, callback)\r\n : err\r\n ? callback(err)\r\n : callback(null, options.binary ? contents : contents.toString(\"utf8\"));\r\n });\r\n\r\n // use the XHR version otherwise.\r\n return fetch.xhr(filename, options, callback);\r\n}\r\n\r\n/**\r\n * Fetches the contents of a file.\r\n * @name util.fetch\r\n * @function\r\n * @param {string} path File path or url\r\n * @param {FetchCallback} callback Callback function\r\n * @returns {undefined}\r\n * @variation 2\r\n */\r\n\r\n/**\r\n * Fetches the contents of a file.\r\n * @name util.fetch\r\n * @function\r\n * @param {string} path File path or url\r\n * @param {FetchOptions} [options] Fetch options\r\n * @returns {Promise} Promise\r\n * @variation 3\r\n */\r\n\r\n/**/\r\nfetch.xhr = function fetch_xhr(filename, options, callback) {\r\n var xhr = new XMLHttpRequest();\r\n xhr.onreadystatechange /* works everywhere */ = function fetchOnReadyStateChange() {\r\n\r\n if (xhr.readyState !== 4)\r\n return undefined;\r\n\r\n // local cors security errors return status 0 / empty string, too. afaik this cannot be\r\n // reliably distinguished from an actually empty file for security reasons. feel free\r\n // to send a pull request if you are aware of a solution.\r\n if (xhr.status !== 0 && xhr.status !== 200)\r\n return callback(Error(\"status \" + xhr.status));\r\n\r\n // if binary data is expected, make sure that some sort of array is returned, even if\r\n // ArrayBuffers are not supported. the binary string fallback, however, is unsafe.\r\n if (options.binary) {\r\n var buffer = xhr.response;\r\n if (!buffer) {\r\n buffer = [];\r\n for (var i = 0; i < xhr.responseText.length; ++i)\r\n buffer.push(xhr.responseText.charCodeAt(i) & 255);\r\n }\r\n return callback(null, typeof Uint8Array !== \"undefined\" ? new Uint8Array(buffer) : buffer);\r\n }\r\n return callback(null, xhr.responseText);\r\n };\r\n\r\n if (options.binary) {\r\n // ref: https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/Sending_and_Receiving_Binary_Data#Receiving_binary_data_in_older_browsers\r\n if (\"overrideMimeType\" in xhr)\r\n xhr.overrideMimeType(\"text/plain; charset=x-user-defined\");\r\n xhr.responseType = \"arraybuffer\";\r\n }\r\n\r\n xhr.open(\"GET\", filename);\r\n xhr.send();\r\n};\r\n","\"use strict\";\r\n\r\nmodule.exports = factory(factory);\r\n\r\n/**\r\n * Reads / writes floats / doubles from / to buffers.\r\n * @name util.float\r\n * @namespace\r\n */\r\n\r\n/**\r\n * Writes a 32 bit float to a buffer using little endian byte order.\r\n * @name util.float.writeFloatLE\r\n * @function\r\n * @param {number} val Value to write\r\n * @param {Uint8Array} buf Target buffer\r\n * @param {number} pos Target buffer offset\r\n * @returns {undefined}\r\n */\r\n\r\n/**\r\n * Writes a 32 bit float to a buffer using big endian byte order.\r\n * @name util.float.writeFloatBE\r\n * @function\r\n * @param {number} val Value to write\r\n * @param {Uint8Array} buf Target buffer\r\n * @param {number} pos Target buffer offset\r\n * @returns {undefined}\r\n */\r\n\r\n/**\r\n * Reads a 32 bit float from a buffer using little endian byte order.\r\n * @name util.float.readFloatLE\r\n * @function\r\n * @param {Uint8Array} buf Source buffer\r\n * @param {number} pos Source buffer offset\r\n * @returns {number} Value read\r\n */\r\n\r\n/**\r\n * Reads a 32 bit float from a buffer using big endian byte order.\r\n * @name util.float.readFloatBE\r\n * @function\r\n * @param {Uint8Array} buf Source buffer\r\n * @param {number} pos Source buffer offset\r\n * @returns {number} Value read\r\n */\r\n\r\n/**\r\n * Writes a 64 bit double to a buffer using little endian byte order.\r\n * @name util.float.writeDoubleLE\r\n * @function\r\n * @param {number} val Value to write\r\n * @param {Uint8Array} buf Target buffer\r\n * @param {number} pos Target buffer offset\r\n * @returns {undefined}\r\n */\r\n\r\n/**\r\n * Writes a 64 bit double to a buffer using big endian byte order.\r\n * @name util.float.writeDoubleBE\r\n * @function\r\n * @param {number} val Value to write\r\n * @param {Uint8Array} buf Target buffer\r\n * @param {number} pos Target buffer offset\r\n * @returns {undefined}\r\n */\r\n\r\n/**\r\n * Reads a 64 bit double from a buffer using little endian byte order.\r\n * @name util.float.readDoubleLE\r\n * @function\r\n * @param {Uint8Array} buf Source buffer\r\n * @param {number} pos Source buffer offset\r\n * @returns {number} Value read\r\n */\r\n\r\n/**\r\n * Reads a 64 bit double from a buffer using big endian byte order.\r\n * @name util.float.readDoubleBE\r\n * @function\r\n * @param {Uint8Array} buf Source buffer\r\n * @param {number} pos Source buffer offset\r\n * @returns {number} Value read\r\n */\r\n\r\n// Factory function for the purpose of node-based testing in modified global environments\r\nfunction factory(exports) {\r\n\r\n // float: typed array\r\n if (typeof Float32Array !== \"undefined\") (function() {\r\n\r\n var f32 = new Float32Array([ -0 ]),\r\n f8b = new Uint8Array(f32.buffer),\r\n le = f8b[3] === 128;\r\n\r\n function writeFloat_f32_cpy(val, buf, pos) {\r\n f32[0] = val;\r\n buf[pos ] = f8b[0];\r\n buf[pos + 1] = f8b[1];\r\n buf[pos + 2] = f8b[2];\r\n buf[pos + 3] = f8b[3];\r\n }\r\n\r\n function writeFloat_f32_rev(val, buf, pos) {\r\n f32[0] = val;\r\n buf[pos ] = f8b[3];\r\n buf[pos + 1] = f8b[2];\r\n buf[pos + 2] = f8b[1];\r\n buf[pos + 3] = f8b[0];\r\n }\r\n\r\n /* istanbul ignore next */\r\n exports.writeFloatLE = le ? writeFloat_f32_cpy : writeFloat_f32_rev;\r\n /* istanbul ignore next */\r\n exports.writeFloatBE = le ? writeFloat_f32_rev : writeFloat_f32_cpy;\r\n\r\n function readFloat_f32_cpy(buf, pos) {\r\n f8b[0] = buf[pos ];\r\n f8b[1] = buf[pos + 1];\r\n f8b[2] = buf[pos + 2];\r\n f8b[3] = buf[pos + 3];\r\n return f32[0];\r\n }\r\n\r\n function readFloat_f32_rev(buf, pos) {\r\n f8b[3] = buf[pos ];\r\n f8b[2] = buf[pos + 1];\r\n f8b[1] = buf[pos + 2];\r\n f8b[0] = buf[pos + 3];\r\n return f32[0];\r\n }\r\n\r\n /* istanbul ignore next */\r\n exports.readFloatLE = le ? readFloat_f32_cpy : readFloat_f32_rev;\r\n /* istanbul ignore next */\r\n exports.readFloatBE = le ? readFloat_f32_rev : readFloat_f32_cpy;\r\n\r\n // float: ieee754\r\n })(); else (function() {\r\n\r\n function writeFloat_ieee754(writeUint, val, buf, pos) {\r\n var sign = val < 0 ? 1 : 0;\r\n if (sign)\r\n val = -val;\r\n if (val === 0)\r\n writeUint(1 / val > 0 ? /* positive */ 0 : /* negative 0 */ 2147483648, buf, pos);\r\n else if (isNaN(val))\r\n writeUint(2143289344, buf, pos);\r\n else if (val > 3.4028234663852886e+38) // +-Infinity\r\n writeUint((sign << 31 | 2139095040) >>> 0, buf, pos);\r\n else if (val < 1.1754943508222875e-38) // denormal\r\n writeUint((sign << 31 | Math.round(val / 1.401298464324817e-45)) >>> 0, buf, pos);\r\n else {\r\n var exponent = Math.floor(Math.log(val) / Math.LN2),\r\n mantissa = Math.round(val * Math.pow(2, -exponent) * 8388608) & 8388607;\r\n writeUint((sign << 31 | exponent + 127 << 23 | mantissa) >>> 0, buf, pos);\r\n }\r\n }\r\n\r\n exports.writeFloatLE = writeFloat_ieee754.bind(null, writeUintLE);\r\n exports.writeFloatBE = writeFloat_ieee754.bind(null, writeUintBE);\r\n\r\n function readFloat_ieee754(readUint, buf, pos) {\r\n var uint = readUint(buf, pos),\r\n sign = (uint >> 31) * 2 + 1,\r\n exponent = uint >>> 23 & 255,\r\n mantissa = uint & 8388607;\r\n return exponent === 255\r\n ? mantissa\r\n ? NaN\r\n : sign * Infinity\r\n : exponent === 0 // denormal\r\n ? sign * 1.401298464324817e-45 * mantissa\r\n : sign * Math.pow(2, exponent - 150) * (mantissa + 8388608);\r\n }\r\n\r\n exports.readFloatLE = readFloat_ieee754.bind(null, readUintLE);\r\n exports.readFloatBE = readFloat_ieee754.bind(null, readUintBE);\r\n\r\n })();\r\n\r\n // double: typed array\r\n if (typeof Float64Array !== \"undefined\") (function() {\r\n\r\n var f64 = new Float64Array([-0]),\r\n f8b = new Uint8Array(f64.buffer),\r\n le = f8b[7] === 128;\r\n\r\n function writeDouble_f64_cpy(val, buf, pos) {\r\n f64[0] = val;\r\n buf[pos ] = f8b[0];\r\n buf[pos + 1] = f8b[1];\r\n buf[pos + 2] = f8b[2];\r\n buf[pos + 3] = f8b[3];\r\n buf[pos + 4] = f8b[4];\r\n buf[pos + 5] = f8b[5];\r\n buf[pos + 6] = f8b[6];\r\n buf[pos + 7] = f8b[7];\r\n }\r\n\r\n function writeDouble_f64_rev(val, buf, pos) {\r\n f64[0] = val;\r\n buf[pos ] = f8b[7];\r\n buf[pos + 1] = f8b[6];\r\n buf[pos + 2] = f8b[5];\r\n buf[pos + 3] = f8b[4];\r\n buf[pos + 4] = f8b[3];\r\n buf[pos + 5] = f8b[2];\r\n buf[pos + 6] = f8b[1];\r\n buf[pos + 7] = f8b[0];\r\n }\r\n\r\n /* istanbul ignore next */\r\n exports.writeDoubleLE = le ? writeDouble_f64_cpy : writeDouble_f64_rev;\r\n /* istanbul ignore next */\r\n exports.writeDoubleBE = le ? writeDouble_f64_rev : writeDouble_f64_cpy;\r\n\r\n function readDouble_f64_cpy(buf, pos) {\r\n f8b[0] = buf[pos ];\r\n f8b[1] = buf[pos + 1];\r\n f8b[2] = buf[pos + 2];\r\n f8b[3] = buf[pos + 3];\r\n f8b[4] = buf[pos + 4];\r\n f8b[5] = buf[pos + 5];\r\n f8b[6] = buf[pos + 6];\r\n f8b[7] = buf[pos + 7];\r\n return f64[0];\r\n }\r\n\r\n function readDouble_f64_rev(buf, pos) {\r\n f8b[7] = buf[pos ];\r\n f8b[6] = buf[pos + 1];\r\n f8b[5] = buf[pos + 2];\r\n f8b[4] = buf[pos + 3];\r\n f8b[3] = buf[pos + 4];\r\n f8b[2] = buf[pos + 5];\r\n f8b[1] = buf[pos + 6];\r\n f8b[0] = buf[pos + 7];\r\n return f64[0];\r\n }\r\n\r\n /* istanbul ignore next */\r\n exports.readDoubleLE = le ? readDouble_f64_cpy : readDouble_f64_rev;\r\n /* istanbul ignore next */\r\n exports.readDoubleBE = le ? readDouble_f64_rev : readDouble_f64_cpy;\r\n\r\n // double: ieee754\r\n })(); else (function() {\r\n\r\n function writeDouble_ieee754(writeUint, off0, off1, val, buf, pos) {\r\n var sign = val < 0 ? 1 : 0;\r\n if (sign)\r\n val = -val;\r\n if (val === 0) {\r\n writeUint(0, buf, pos + off0);\r\n writeUint(1 / val > 0 ? /* positive */ 0 : /* negative 0 */ 2147483648, buf, pos + off1);\r\n } else if (isNaN(val)) {\r\n writeUint(0, buf, pos + off0);\r\n writeUint(2146959360, buf, pos + off1);\r\n } else if (val > 1.7976931348623157e+308) { // +-Infinity\r\n writeUint(0, buf, pos + off0);\r\n writeUint((sign << 31 | 2146435072) >>> 0, buf, pos + off1);\r\n } else {\r\n var mantissa;\r\n if (val < 2.2250738585072014e-308) { // denormal\r\n mantissa = val / 5e-324;\r\n writeUint(mantissa >>> 0, buf, pos + off0);\r\n writeUint((sign << 31 | mantissa / 4294967296) >>> 0, buf, pos + off1);\r\n } else {\r\n var exponent = Math.floor(Math.log(val) / Math.LN2);\r\n if (exponent === 1024)\r\n exponent = 1023;\r\n mantissa = val * Math.pow(2, -exponent);\r\n writeUint(mantissa * 4503599627370496 >>> 0, buf, pos + off0);\r\n writeUint((sign << 31 | exponent + 1023 << 20 | mantissa * 1048576 & 1048575) >>> 0, buf, pos + off1);\r\n }\r\n }\r\n }\r\n\r\n exports.writeDoubleLE = writeDouble_ieee754.bind(null, writeUintLE, 0, 4);\r\n exports.writeDoubleBE = writeDouble_ieee754.bind(null, writeUintBE, 4, 0);\r\n\r\n function readDouble_ieee754(readUint, off0, off1, buf, pos) {\r\n var lo = readUint(buf, pos + off0),\r\n hi = readUint(buf, pos + off1);\r\n var sign = (hi >> 31) * 2 + 1,\r\n exponent = hi >>> 20 & 2047,\r\n mantissa = 4294967296 * (hi & 1048575) + lo;\r\n return exponent === 2047\r\n ? mantissa\r\n ? NaN\r\n : sign * Infinity\r\n : exponent === 0 // denormal\r\n ? sign * 5e-324 * mantissa\r\n : sign * Math.pow(2, exponent - 1075) * (mantissa + 4503599627370496);\r\n }\r\n\r\n exports.readDoubleLE = readDouble_ieee754.bind(null, readUintLE, 0, 4);\r\n exports.readDoubleBE = readDouble_ieee754.bind(null, readUintBE, 4, 0);\r\n\r\n })();\r\n\r\n return exports;\r\n}\r\n\r\n// uint helpers\r\n\r\nfunction writeUintLE(val, buf, pos) {\r\n buf[pos ] = val & 255;\r\n buf[pos + 1] = val >>> 8 & 255;\r\n buf[pos + 2] = val >>> 16 & 255;\r\n buf[pos + 3] = val >>> 24;\r\n}\r\n\r\nfunction writeUintBE(val, buf, pos) {\r\n buf[pos ] = val >>> 24;\r\n buf[pos + 1] = val >>> 16 & 255;\r\n buf[pos + 2] = val >>> 8 & 255;\r\n buf[pos + 3] = val & 255;\r\n}\r\n\r\nfunction readUintLE(buf, pos) {\r\n return (buf[pos ]\r\n | buf[pos + 1] << 8\r\n | buf[pos + 2] << 16\r\n | buf[pos + 3] << 24) >>> 0;\r\n}\r\n\r\nfunction readUintBE(buf, pos) {\r\n return (buf[pos ] << 24\r\n | buf[pos + 1] << 16\r\n | buf[pos + 2] << 8\r\n | buf[pos + 3]) >>> 0;\r\n}\r\n","\"use strict\";\r\nmodule.exports = inquire;\r\n\r\n/**\r\n * Requires a module only if available.\r\n * @memberof util\r\n * @param {string} moduleName Module to require\r\n * @returns {?Object} Required module if available and not empty, otherwise `null`\r\n */\r\nfunction inquire(moduleName) {\r\n try {\r\n var mod = eval(\"quire\".replace(/^/,\"re\"))(moduleName); // eslint-disable-line no-eval\r\n if (mod && (mod.length || Object.keys(mod).length))\r\n return mod;\r\n } catch (e) {} // eslint-disable-line no-empty\r\n return null;\r\n}\r\n","\"use strict\";\r\n\r\n/**\r\n * A minimal path module to resolve Unix, Windows and URL paths alike.\r\n * @memberof util\r\n * @namespace\r\n */\r\nvar path = exports;\r\n\r\nvar isAbsolute =\r\n/**\r\n * Tests if the specified path is absolute.\r\n * @param {string} path Path to test\r\n * @returns {boolean} `true` if path is absolute\r\n */\r\npath.isAbsolute = function isAbsolute(path) {\r\n return /^(?:\\/|\\w+:)/.test(path);\r\n};\r\n\r\nvar normalize =\r\n/**\r\n * Normalizes the specified path.\r\n * @param {string} path Path to normalize\r\n * @returns {string} Normalized path\r\n */\r\npath.normalize = function normalize(path) {\r\n path = path.replace(/\\\\/g, \"/\")\r\n .replace(/\\/{2,}/g, \"/\");\r\n var parts = path.split(\"/\"),\r\n absolute = isAbsolute(path),\r\n prefix = \"\";\r\n if (absolute)\r\n prefix = parts.shift() + \"/\";\r\n for (var i = 0; i < parts.length;) {\r\n if (parts[i] === \"..\") {\r\n if (i > 0 && parts[i - 1] !== \"..\")\r\n parts.splice(--i, 2);\r\n else if (absolute)\r\n parts.splice(i, 1);\r\n else\r\n ++i;\r\n } else if (parts[i] === \".\")\r\n parts.splice(i, 1);\r\n else\r\n ++i;\r\n }\r\n return prefix + parts.join(\"/\");\r\n};\r\n\r\n/**\r\n * Resolves the specified include path against the specified origin path.\r\n * @param {string} originPath Path to the origin file\r\n * @param {string} includePath Include path relative to origin path\r\n * @param {boolean} [alreadyNormalized=false] `true` if both paths are already known to be normalized\r\n * @returns {string} Path to the include file\r\n */\r\npath.resolve = function resolve(originPath, includePath, alreadyNormalized) {\r\n if (!alreadyNormalized)\r\n includePath = normalize(includePath);\r\n if (isAbsolute(includePath))\r\n return includePath;\r\n if (!alreadyNormalized)\r\n originPath = normalize(originPath);\r\n return (originPath = originPath.replace(/(?:\\/|^)[^/]+$/, \"\")).length ? normalize(originPath + \"/\" + includePath) : includePath;\r\n};\r\n","\"use strict\";\r\nmodule.exports = pool;\r\n\r\n/**\r\n * An allocator as used by {@link util.pool}.\r\n * @typedef PoolAllocator\r\n * @type {function}\r\n * @param {number} size Buffer size\r\n * @returns {Uint8Array} Buffer\r\n */\r\n\r\n/**\r\n * A slicer as used by {@link util.pool}.\r\n * @typedef PoolSlicer\r\n * @type {function}\r\n * @param {number} start Start offset\r\n * @param {number} end End offset\r\n * @returns {Uint8Array} Buffer slice\r\n * @this {Uint8Array}\r\n */\r\n\r\n/**\r\n * A general purpose buffer pool.\r\n * @memberof util\r\n * @function\r\n * @param {PoolAllocator} alloc Allocator\r\n * @param {PoolSlicer} slice Slicer\r\n * @param {number} [size=8192] Slab size\r\n * @returns {PoolAllocator} Pooled allocator\r\n */\r\nfunction pool(alloc, slice, size) {\r\n var SIZE = size || 8192;\r\n var MAX = SIZE >>> 1;\r\n var slab = null;\r\n var offset = SIZE;\r\n return function pool_alloc(size) {\r\n if (size < 1 || size > MAX)\r\n return alloc(size);\r\n if (offset + size > SIZE) {\r\n slab = alloc(SIZE);\r\n offset = 0;\r\n }\r\n var buf = slice.call(slab, offset, offset += size);\r\n if (offset & 7) // align to 32 bit\r\n offset = (offset | 7) + 1;\r\n return buf;\r\n };\r\n}\r\n","\"use strict\";\r\n\r\n/**\r\n * A minimal UTF8 implementation for number arrays.\r\n * @memberof util\r\n * @namespace\r\n */\r\nvar utf8 = exports;\r\n\r\n/**\r\n * Calculates the UTF8 byte length of a string.\r\n * @param {string} string String\r\n * @returns {number} Byte length\r\n */\r\nutf8.length = function utf8_length(string) {\r\n var len = 0,\r\n c = 0;\r\n for (var i = 0; i < string.length; ++i) {\r\n c = string.charCodeAt(i);\r\n if (c < 128)\r\n len += 1;\r\n else if (c < 2048)\r\n len += 2;\r\n else if ((c & 0xFC00) === 0xD800 && (string.charCodeAt(i + 1) & 0xFC00) === 0xDC00) {\r\n ++i;\r\n len += 4;\r\n } else\r\n len += 3;\r\n }\r\n return len;\r\n};\r\n\r\n/**\r\n * Reads UTF8 bytes as a string.\r\n * @param {Uint8Array} buffer Source buffer\r\n * @param {number} start Source start\r\n * @param {number} end Source end\r\n * @returns {string} String read\r\n */\r\nutf8.read = function utf8_read(buffer, start, end) {\r\n var len = end - start;\r\n if (len < 1)\r\n return \"\";\r\n var parts = null,\r\n chunk = [],\r\n i = 0, // char offset\r\n t; // temporary\r\n while (start < end) {\r\n t = buffer[start++];\r\n if (t < 128)\r\n chunk[i++] = t;\r\n else if (t > 191 && t < 224)\r\n chunk[i++] = (t & 31) << 6 | buffer[start++] & 63;\r\n else if (t > 239 && t < 365) {\r\n t = ((t & 7) << 18 | (buffer[start++] & 63) << 12 | (buffer[start++] & 63) << 6 | buffer[start++] & 63) - 0x10000;\r\n chunk[i++] = 0xD800 + (t >> 10);\r\n chunk[i++] = 0xDC00 + (t & 1023);\r\n } else\r\n chunk[i++] = (t & 15) << 12 | (buffer[start++] & 63) << 6 | buffer[start++] & 63;\r\n if (i > 8191) {\r\n (parts || (parts = [])).push(String.fromCharCode.apply(String, chunk));\r\n i = 0;\r\n }\r\n }\r\n if (parts) {\r\n if (i)\r\n parts.push(String.fromCharCode.apply(String, chunk.slice(0, i)));\r\n return parts.join(\"\");\r\n }\r\n return String.fromCharCode.apply(String, chunk.slice(0, i));\r\n};\r\n\r\n/**\r\n * Writes a string as UTF8 bytes.\r\n * @param {string} string Source string\r\n * @param {Uint8Array} buffer Destination buffer\r\n * @param {number} offset Destination offset\r\n * @returns {number} Bytes written\r\n */\r\nutf8.write = function utf8_write(string, buffer, offset) {\r\n var start = offset,\r\n c1, // character 1\r\n c2; // character 2\r\n for (var i = 0; i < string.length; ++i) {\r\n c1 = string.charCodeAt(i);\r\n if (c1 < 128) {\r\n buffer[offset++] = c1;\r\n } else if (c1 < 2048) {\r\n buffer[offset++] = c1 >> 6 | 192;\r\n buffer[offset++] = c1 & 63 | 128;\r\n } else if ((c1 & 0xFC00) === 0xD800 && ((c2 = string.charCodeAt(i + 1)) & 0xFC00) === 0xDC00) {\r\n c1 = 0x10000 + ((c1 & 0x03FF) << 10) + (c2 & 0x03FF);\r\n ++i;\r\n buffer[offset++] = c1 >> 18 | 240;\r\n buffer[offset++] = c1 >> 12 & 63 | 128;\r\n buffer[offset++] = c1 >> 6 & 63 | 128;\r\n buffer[offset++] = c1 & 63 | 128;\r\n } else {\r\n buffer[offset++] = c1 >> 12 | 224;\r\n buffer[offset++] = c1 >> 6 & 63 | 128;\r\n buffer[offset++] = c1 & 63 | 128;\r\n }\r\n }\r\n return offset - start;\r\n};\r\n","\"use strict\";\nmodule.exports = common;\n\nvar commonRe = /\\/|\\./;\n\n/**\n * Provides common type definitions.\n * Can also be used to provide additional google types or your own custom types.\n * @param {string} name Short name as in `google/protobuf/[name].proto` or full file name\n * @param {Object.} json JSON definition within `google.protobuf` if a short name, otherwise the file's root definition\n * @returns {undefined}\n * @property {INamespace} google/protobuf/any.proto Any\n * @property {INamespace} google/protobuf/duration.proto Duration\n * @property {INamespace} google/protobuf/empty.proto Empty\n * @property {INamespace} google/protobuf/field_mask.proto FieldMask\n * @property {INamespace} google/protobuf/struct.proto Struct, Value, NullValue and ListValue\n * @property {INamespace} google/protobuf/timestamp.proto Timestamp\n * @property {INamespace} google/protobuf/wrappers.proto Wrappers\n * @example\n * // manually provides descriptor.proto (assumes google/protobuf/ namespace and .proto extension)\n * protobuf.common(\"descriptor\", descriptorJson);\n *\n * // manually provides a custom definition (uses my.foo namespace)\n * protobuf.common(\"my/foo/bar.proto\", myFooBarJson);\n */\nfunction common(name, json) {\n if (!commonRe.test(name)) {\n name = \"google/protobuf/\" + name + \".proto\";\n json = { nested: { google: { nested: { protobuf: { nested: json } } } } };\n }\n common[name] = json;\n}\n\n// Not provided because of limited use (feel free to discuss or to provide yourself):\n//\n// google/protobuf/descriptor.proto\n// google/protobuf/source_context.proto\n// google/protobuf/type.proto\n//\n// Stripped and pre-parsed versions of these non-bundled files are instead available as part of\n// the repository or package within the google/protobuf directory.\n\ncommon(\"any\", {\n\n /**\n * Properties of a google.protobuf.Any message.\n * @interface IAny\n * @type {Object}\n * @property {string} [typeUrl]\n * @property {Uint8Array} [bytes]\n * @memberof common\n */\n Any: {\n fields: {\n type_url: {\n type: \"string\",\n id: 1\n },\n value: {\n type: \"bytes\",\n id: 2\n }\n }\n }\n});\n\nvar timeType;\n\ncommon(\"duration\", {\n\n /**\n * Properties of a google.protobuf.Duration message.\n * @interface IDuration\n * @type {Object}\n * @property {number|Long} [seconds]\n * @property {number} [nanos]\n * @memberof common\n */\n Duration: timeType = {\n fields: {\n seconds: {\n type: \"int64\",\n id: 1\n },\n nanos: {\n type: \"int32\",\n id: 2\n }\n }\n }\n});\n\ncommon(\"timestamp\", {\n\n /**\n * Properties of a google.protobuf.Timestamp message.\n * @interface ITimestamp\n * @type {Object}\n * @property {number|Long} [seconds]\n * @property {number} [nanos]\n * @memberof common\n */\n Timestamp: timeType\n});\n\ncommon(\"empty\", {\n\n /**\n * Properties of a google.protobuf.Empty message.\n * @interface IEmpty\n * @memberof common\n */\n Empty: {\n fields: {}\n }\n});\n\ncommon(\"struct\", {\n\n /**\n * Properties of a google.protobuf.Struct message.\n * @interface IStruct\n * @type {Object}\n * @property {Object.} [fields]\n * @memberof common\n */\n Struct: {\n fields: {\n fields: {\n keyType: \"string\",\n type: \"Value\",\n id: 1\n }\n }\n },\n\n /**\n * Properties of a google.protobuf.Value message.\n * @interface IValue\n * @type {Object}\n * @property {string} [kind]\n * @property {0} [nullValue]\n * @property {number} [numberValue]\n * @property {string} [stringValue]\n * @property {boolean} [boolValue]\n * @property {IStruct} [structValue]\n * @property {IListValue} [listValue]\n * @memberof common\n */\n Value: {\n oneofs: {\n kind: {\n oneof: [\n \"nullValue\",\n \"numberValue\",\n \"stringValue\",\n \"boolValue\",\n \"structValue\",\n \"listValue\"\n ]\n }\n },\n fields: {\n nullValue: {\n type: \"NullValue\",\n id: 1\n },\n numberValue: {\n type: \"double\",\n id: 2\n },\n stringValue: {\n type: \"string\",\n id: 3\n },\n boolValue: {\n type: \"bool\",\n id: 4\n },\n structValue: {\n type: \"Struct\",\n id: 5\n },\n listValue: {\n type: \"ListValue\",\n id: 6\n }\n }\n },\n\n NullValue: {\n values: {\n NULL_VALUE: 0\n }\n },\n\n /**\n * Properties of a google.protobuf.ListValue message.\n * @interface IListValue\n * @type {Object}\n * @property {Array.} [values]\n * @memberof common\n */\n ListValue: {\n fields: {\n values: {\n rule: \"repeated\",\n type: \"Value\",\n id: 1\n }\n }\n }\n});\n\ncommon(\"wrappers\", {\n\n /**\n * Properties of a google.protobuf.DoubleValue message.\n * @interface IDoubleValue\n * @type {Object}\n * @property {number} [value]\n * @memberof common\n */\n DoubleValue: {\n fields: {\n value: {\n type: \"double\",\n id: 1\n }\n }\n },\n\n /**\n * Properties of a google.protobuf.FloatValue message.\n * @interface IFloatValue\n * @type {Object}\n * @property {number} [value]\n * @memberof common\n */\n FloatValue: {\n fields: {\n value: {\n type: \"float\",\n id: 1\n }\n }\n },\n\n /**\n * Properties of a google.protobuf.Int64Value message.\n * @interface IInt64Value\n * @type {Object}\n * @property {number|Long} [value]\n * @memberof common\n */\n Int64Value: {\n fields: {\n value: {\n type: \"int64\",\n id: 1\n }\n }\n },\n\n /**\n * Properties of a google.protobuf.UInt64Value message.\n * @interface IUInt64Value\n * @type {Object}\n * @property {number|Long} [value]\n * @memberof common\n */\n UInt64Value: {\n fields: {\n value: {\n type: \"uint64\",\n id: 1\n }\n }\n },\n\n /**\n * Properties of a google.protobuf.Int32Value message.\n * @interface IInt32Value\n * @type {Object}\n * @property {number} [value]\n * @memberof common\n */\n Int32Value: {\n fields: {\n value: {\n type: \"int32\",\n id: 1\n }\n }\n },\n\n /**\n * Properties of a google.protobuf.UInt32Value message.\n * @interface IUInt32Value\n * @type {Object}\n * @property {number} [value]\n * @memberof common\n */\n UInt32Value: {\n fields: {\n value: {\n type: \"uint32\",\n id: 1\n }\n }\n },\n\n /**\n * Properties of a google.protobuf.BoolValue message.\n * @interface IBoolValue\n * @type {Object}\n * @property {boolean} [value]\n * @memberof common\n */\n BoolValue: {\n fields: {\n value: {\n type: \"bool\",\n id: 1\n }\n }\n },\n\n /**\n * Properties of a google.protobuf.StringValue message.\n * @interface IStringValue\n * @type {Object}\n * @property {string} [value]\n * @memberof common\n */\n StringValue: {\n fields: {\n value: {\n type: \"string\",\n id: 1\n }\n }\n },\n\n /**\n * Properties of a google.protobuf.BytesValue message.\n * @interface IBytesValue\n * @type {Object}\n * @property {Uint8Array} [value]\n * @memberof common\n */\n BytesValue: {\n fields: {\n value: {\n type: \"bytes\",\n id: 1\n }\n }\n }\n});\n\ncommon(\"field_mask\", {\n\n /**\n * Properties of a google.protobuf.FieldMask message.\n * @interface IDoubleValue\n * @type {Object}\n * @property {number} [value]\n * @memberof common\n */\n FieldMask: {\n fields: {\n paths: {\n rule: \"repeated\",\n type: \"string\",\n id: 1\n }\n }\n }\n});\n\n/**\n * Gets the root definition of the specified common proto file.\n *\n * Bundled definitions are:\n * - google/protobuf/any.proto\n * - google/protobuf/duration.proto\n * - google/protobuf/empty.proto\n * - google/protobuf/field_mask.proto\n * - google/protobuf/struct.proto\n * - google/protobuf/timestamp.proto\n * - google/protobuf/wrappers.proto\n *\n * @param {string} file Proto file name\n * @returns {INamespace|null} Root definition or `null` if not defined\n */\ncommon.get = function get(file) {\n return common[file] || null;\n};\n","\"use strict\";\n/**\n * Runtime message from/to plain object converters.\n * @namespace\n */\nvar converter = exports;\n\nvar Enum = require(15),\n util = require(37);\n\n/**\n * Generates a partial value fromObject conveter.\n * @param {Codegen} gen Codegen instance\n * @param {Field} field Reflected field\n * @param {number} fieldIndex Field index\n * @param {string} prop Property reference\n * @returns {Codegen} Codegen instance\n * @ignore\n */\nfunction genValuePartial_fromObject(gen, field, fieldIndex, prop) {\n /* eslint-disable no-unexpected-multiline, block-scoped-var, no-redeclare */\n if (field.resolvedType) {\n if (field.resolvedType instanceof Enum) { gen\n (\"switch(d%s){\", prop);\n for (var values = field.resolvedType.values, keys = Object.keys(values), i = 0; i < keys.length; ++i) {\n if (field.repeated && values[keys[i]] === field.typeDefault) gen\n (\"default:\");\n gen\n (\"case%j:\", keys[i])\n (\"case %i:\", values[keys[i]])\n (\"m%s=%j\", prop, values[keys[i]])\n (\"break\");\n } gen\n (\"}\");\n } else gen\n (\"if(typeof d%s!==\\\"object\\\")\", prop)\n (\"throw TypeError(%j)\", field.fullName + \": object expected\")\n (\"m%s=types[%i].fromObject(d%s)\", prop, fieldIndex, prop);\n } else {\n var isUnsigned = false;\n switch (field.type) {\n case \"double\":\n case \"float\": gen\n (\"m%s=Number(d%s)\", prop, prop); // also catches \"NaN\", \"Infinity\"\n break;\n case \"uint32\":\n case \"fixed32\": gen\n (\"m%s=d%s>>>0\", prop, prop);\n break;\n case \"int32\":\n case \"sint32\":\n case \"sfixed32\": gen\n (\"m%s=d%s|0\", prop, prop);\n break;\n case \"uint64\":\n isUnsigned = true;\n // eslint-disable-line no-fallthrough\n case \"int64\":\n case \"sint64\":\n case \"fixed64\":\n case \"sfixed64\": gen\n (\"if(util.Long)\")\n (\"(m%s=util.Long.fromValue(d%s)).unsigned=%j\", prop, prop, isUnsigned)\n (\"else if(typeof d%s===\\\"string\\\")\", prop)\n (\"m%s=parseInt(d%s,10)\", prop, prop)\n (\"else if(typeof d%s===\\\"number\\\")\", prop)\n (\"m%s=d%s\", prop, prop)\n (\"else if(typeof d%s===\\\"object\\\")\", prop)\n (\"m%s=new util.LongBits(d%s.low>>>0,d%s.high>>>0).toNumber(%s)\", prop, prop, prop, isUnsigned ? \"true\" : \"\");\n break;\n case \"bytes\": gen\n (\"if(typeof d%s===\\\"string\\\")\", prop)\n (\"util.base64.decode(d%s,m%s=util.newBuffer(util.base64.length(d%s)),0)\", prop, prop, prop)\n (\"else if(d%s.length)\", prop)\n (\"m%s=d%s\", prop, prop);\n break;\n case \"string\": gen\n (\"m%s=String(d%s)\", prop, prop);\n break;\n case \"bool\": gen\n (\"m%s=Boolean(d%s)\", prop, prop);\n break;\n /* default: gen\n (\"m%s=d%s\", prop, prop);\n break; */\n }\n }\n return gen;\n /* eslint-enable no-unexpected-multiline, block-scoped-var, no-redeclare */\n}\n\n/**\n * Generates a plain object to runtime message converter specific to the specified message type.\n * @param {Type} mtype Message type\n * @returns {Codegen} Codegen instance\n */\nconverter.fromObject = function fromObject(mtype) {\n /* eslint-disable no-unexpected-multiline, block-scoped-var, no-redeclare */\n var fields = mtype.fieldsArray;\n var gen = util.codegen([\"d\"], mtype.name + \"$fromObject\")\n (\"if(d instanceof this.ctor)\")\n (\"return d\");\n if (!fields.length) return gen\n (\"return new this.ctor\");\n gen\n (\"var m=new this.ctor\");\n for (var i = 0; i < fields.length; ++i) {\n var field = fields[i].resolve(),\n prop = util.safeProp(field.name);\n\n // Map fields\n if (field.map) { gen\n (\"if(d%s){\", prop)\n (\"if(typeof d%s!==\\\"object\\\")\", prop)\n (\"throw TypeError(%j)\", field.fullName + \": object expected\")\n (\"m%s={}\", prop)\n (\"for(var ks=Object.keys(d%s),i=0;i>>0,m%s.high>>>0).toNumber(%s):m%s\", prop, prop, prop, prop, isUnsigned ? \"true\": \"\", prop);\n break;\n case \"bytes\": gen\n (\"d%s=o.bytes===String?util.base64.encode(m%s,0,m%s.length):o.bytes===Array?Array.prototype.slice.call(m%s):m%s\", prop, prop, prop, prop, prop);\n break;\n default: gen\n (\"d%s=m%s\", prop, prop);\n break;\n }\n }\n return gen;\n /* eslint-enable no-unexpected-multiline, block-scoped-var, no-redeclare */\n}\n\n/**\n * Generates a runtime message to plain object converter specific to the specified message type.\n * @param {Type} mtype Message type\n * @returns {Codegen} Codegen instance\n */\nconverter.toObject = function toObject(mtype) {\n /* eslint-disable no-unexpected-multiline, block-scoped-var, no-redeclare */\n var fields = mtype.fieldsArray.slice().sort(util.compareFieldsById);\n if (!fields.length)\n return util.codegen()(\"return {}\");\n var gen = util.codegen([\"m\", \"o\"], mtype.name + \"$toObject\")\n (\"if(!o)\")\n (\"o={}\")\n (\"var d={}\");\n\n var repeatedFields = [],\n mapFields = [],\n normalFields = [],\n i = 0;\n for (; i < fields.length; ++i)\n if (!fields[i].partOf)\n ( fields[i].resolve().repeated ? repeatedFields\n : fields[i].map ? mapFields\n : normalFields).push(fields[i]);\n\n if (repeatedFields.length) { gen\n (\"if(o.arrays||o.defaults){\");\n for (i = 0; i < repeatedFields.length; ++i) gen\n (\"d%s=[]\", util.safeProp(repeatedFields[i].name));\n gen\n (\"}\");\n }\n\n if (mapFields.length) { gen\n (\"if(o.objects||o.defaults){\");\n for (i = 0; i < mapFields.length; ++i) gen\n (\"d%s={}\", util.safeProp(mapFields[i].name));\n gen\n (\"}\");\n }\n\n if (normalFields.length) { gen\n (\"if(o.defaults){\");\n for (i = 0; i < normalFields.length; ++i) {\n var field = normalFields[i],\n prop = util.safeProp(field.name);\n if (field.resolvedType instanceof Enum) gen\n (\"d%s=o.enums===String?%j:%j\", prop, field.resolvedType.valuesById[field.typeDefault], field.typeDefault);\n else if (field.long) gen\n (\"if(util.Long){\")\n (\"var n=new util.Long(%i,%i,%j)\", field.typeDefault.low, field.typeDefault.high, field.typeDefault.unsigned)\n (\"d%s=o.longs===String?n.toString():o.longs===Number?n.toNumber():n\", prop)\n (\"}else\")\n (\"d%s=o.longs===String?%j:%i\", prop, field.typeDefault.toString(), field.typeDefault.toNumber());\n else if (field.bytes) {\n var arrayDefault = \"[\" + Array.prototype.slice.call(field.typeDefault).join(\",\") + \"]\";\n gen\n (\"if(o.bytes===String)d%s=%j\", prop, String.fromCharCode.apply(String, field.typeDefault))\n (\"else{\")\n (\"d%s=%s\", prop, arrayDefault)\n (\"if(o.bytes!==Array)d%s=util.newBuffer(d%s)\", prop, prop)\n (\"}\");\n } else gen\n (\"d%s=%j\", prop, field.typeDefault); // also messages (=null)\n } gen\n (\"}\");\n }\n var hasKs2 = false;\n for (i = 0; i < fields.length; ++i) {\n var field = fields[i],\n index = mtype._fieldsArray.indexOf(field),\n prop = util.safeProp(field.name);\n if (field.map) {\n if (!hasKs2) { hasKs2 = true; gen\n (\"var ks2\");\n } gen\n (\"if(m%s&&(ks2=Object.keys(m%s)).length){\", prop, prop)\n (\"d%s={}\", prop)\n (\"for(var j=0;j>>3){\");\n\n var i = 0;\n for (; i < /* initializes */ mtype.fieldsArray.length; ++i) {\n var field = mtype._fieldsArray[i].resolve(),\n type = field.resolvedType instanceof Enum ? \"int32\" : field.type,\n ref = \"m\" + util.safeProp(field.name); gen\n (\"case %i:\", field.id);\n\n // Map fields\n if (field.map) { gen\n (\"if(%s===util.emptyObject)\", ref)\n (\"%s={}\", ref)\n (\"var c2 = r.uint32()+r.pos\");\n\n if (types.defaults[field.keyType] !== undefined) gen\n (\"k=%j\", types.defaults[field.keyType]);\n else gen\n (\"k=null\");\n\n if (types.defaults[type] !== undefined) gen\n (\"value=%j\", types.defaults[type]);\n else gen\n (\"value=null\");\n\n gen\n (\"while(r.pos>>3){\")\n (\"case 1: k=r.%s(); break\", field.keyType)\n (\"case 2:\");\n\n if (types.basic[type] === undefined) gen\n (\"value=types[%i].decode(r,r.uint32())\", i); // can't be groups\n else gen\n (\"value=r.%s()\", type);\n\n gen\n (\"break\")\n (\"default:\")\n (\"r.skipType(tag2&7)\")\n (\"break\")\n (\"}\")\n (\"}\");\n\n if (types.long[field.keyType] !== undefined) gen\n (\"%s[typeof k===\\\"object\\\"?util.longToHash(k):k]=value\", ref);\n else gen\n (\"%s[k]=value\", ref);\n\n // Repeated fields\n } else if (field.repeated) { gen\n\n (\"if(!(%s&&%s.length))\", ref, ref)\n (\"%s=[]\", ref);\n\n // Packable (always check for forward and backward compatiblity)\n if (types.packed[type] !== undefined) gen\n (\"if((t&7)===2){\")\n (\"var c2=r.uint32()+r.pos\")\n (\"while(r.pos>> 0, (field.id << 3 | 4) >>> 0)\n : gen(\"types[%i].encode(%s,w.uint32(%i).fork()).ldelim()\", fieldIndex, ref, (field.id << 3 | 2) >>> 0);\n}\n\n/**\n * Generates an encoder specific to the specified message type.\n * @param {Type} mtype Message type\n * @returns {Codegen} Codegen instance\n */\nfunction encoder(mtype) {\n /* eslint-disable no-unexpected-multiline, block-scoped-var, no-redeclare */\n var gen = util.codegen([\"m\", \"w\"], mtype.name + \"$encode\")\n (\"if(!w)\")\n (\"w=Writer.create()\");\n\n var i, ref;\n\n // \"when a message is serialized its known fields should be written sequentially by field number\"\n var fields = /* initializes */ mtype.fieldsArray.slice().sort(util.compareFieldsById);\n\n for (var i = 0; i < fields.length; ++i) {\n var field = fields[i].resolve(),\n index = mtype._fieldsArray.indexOf(field),\n type = field.resolvedType instanceof Enum ? \"int32\" : field.type,\n wireType = types.basic[type];\n ref = \"m\" + util.safeProp(field.name);\n\n // Map fields\n if (field.map) {\n gen\n (\"if(%s!=null&&Object.hasOwnProperty.call(m,%j)){\", ref, field.name) // !== undefined && !== null\n (\"for(var ks=Object.keys(%s),i=0;i>> 0, 8 | types.mapKey[field.keyType], field.keyType);\n if (wireType === undefined) gen\n (\"types[%i].encode(%s[ks[i]],w.uint32(18).fork()).ldelim().ldelim()\", index, ref); // can't be groups\n else gen\n (\".uint32(%i).%s(%s[ks[i]]).ldelim()\", 16 | wireType, type, ref);\n gen\n (\"}\")\n (\"}\");\n\n // Repeated fields\n } else if (field.repeated) { gen\n (\"if(%s!=null&&%s.length){\", ref, ref); // !== undefined && !== null\n\n // Packed repeated\n if (field.packed && types.packed[type] !== undefined) { gen\n\n (\"w.uint32(%i).fork()\", (field.id << 3 | 2) >>> 0)\n (\"for(var i=0;i<%s.length;++i)\", ref)\n (\"w.%s(%s[i])\", type, ref)\n (\"w.ldelim()\");\n\n // Non-packed\n } else { gen\n\n (\"for(var i=0;i<%s.length;++i)\", ref);\n if (wireType === undefined)\n genTypePartial(gen, field, index, ref + \"[i]\");\n else gen\n (\"w.uint32(%i).%s(%s[i])\", (field.id << 3 | wireType) >>> 0, type, ref);\n\n } gen\n (\"}\");\n\n // Non-repeated\n } else {\n if (field.optional) gen\n (\"if(%s!=null&&Object.hasOwnProperty.call(m,%j))\", ref, field.name); // !== undefined && !== null\n\n if (wireType === undefined)\n genTypePartial(gen, field, index, ref);\n else gen\n (\"w.uint32(%i).%s(%s)\", (field.id << 3 | wireType) >>> 0, type, ref);\n\n }\n }\n\n return gen\n (\"return w\");\n /* eslint-enable no-unexpected-multiline, block-scoped-var, no-redeclare */\n}\n","\"use strict\";\nmodule.exports = Enum;\n\n// extends ReflectionObject\nvar ReflectionObject = require(24);\n((Enum.prototype = Object.create(ReflectionObject.prototype)).constructor = Enum).className = \"Enum\";\n\nvar Namespace = require(23),\n util = require(37);\n\n/**\n * Constructs a new enum instance.\n * @classdesc Reflected enum.\n * @extends ReflectionObject\n * @constructor\n * @param {string} name Unique name within its namespace\n * @param {Object.} [values] Enum values as an object, by name\n * @param {Object.} [options] Declared options\n * @param {string} [comment] The comment for this enum\n * @param {Object.} [comments] The value comments for this enum\n */\nfunction Enum(name, values, options, comment, comments) {\n ReflectionObject.call(this, name, options);\n\n if (values && typeof values !== \"object\")\n throw TypeError(\"values must be an object\");\n\n /**\n * Enum values by id.\n * @type {Object.}\n */\n this.valuesById = {};\n\n /**\n * Enum values by name.\n * @type {Object.}\n */\n this.values = Object.create(this.valuesById); // toJSON, marker\n\n /**\n * Enum comment text.\n * @type {string|null}\n */\n this.comment = comment;\n\n /**\n * Value comment texts, if any.\n * @type {Object.}\n */\n this.comments = comments || {};\n\n /**\n * Reserved ranges, if any.\n * @type {Array.}\n */\n this.reserved = undefined; // toJSON\n\n // Note that values inherit valuesById on their prototype which makes them a TypeScript-\n // compatible enum. This is used by pbts to write actual enum definitions that work for\n // static and reflection code alike instead of emitting generic object definitions.\n\n if (values)\n for (var keys = Object.keys(values), i = 0; i < keys.length; ++i)\n if (typeof values[keys[i]] === \"number\") // use forward entries only\n this.valuesById[ this.values[keys[i]] = values[keys[i]] ] = keys[i];\n}\n\n/**\n * Enum descriptor.\n * @interface IEnum\n * @property {Object.} values Enum values\n * @property {Object.} [options] Enum options\n */\n\n/**\n * Constructs an enum from an enum descriptor.\n * @param {string} name Enum name\n * @param {IEnum} json Enum descriptor\n * @returns {Enum} Created enum\n * @throws {TypeError} If arguments are invalid\n */\nEnum.fromJSON = function fromJSON(name, json) {\n var enm = new Enum(name, json.values, json.options, json.comment, json.comments);\n enm.reserved = json.reserved;\n return enm;\n};\n\n/**\n * Converts this enum to an enum descriptor.\n * @param {IToJSONOptions} [toJSONOptions] JSON conversion options\n * @returns {IEnum} Enum descriptor\n */\nEnum.prototype.toJSON = function toJSON(toJSONOptions) {\n var keepComments = toJSONOptions ? Boolean(toJSONOptions.keepComments) : false;\n return util.toObject([\n \"options\" , this.options,\n \"values\" , this.values,\n \"reserved\" , this.reserved && this.reserved.length ? this.reserved : undefined,\n \"comment\" , keepComments ? this.comment : undefined,\n \"comments\" , keepComments ? this.comments : undefined\n ]);\n};\n\n/**\n * Adds a value to this enum.\n * @param {string} name Value name\n * @param {number} id Value id\n * @param {string} [comment] Comment, if any\n * @returns {Enum} `this`\n * @throws {TypeError} If arguments are invalid\n * @throws {Error} If there is already a value with this name or id\n */\nEnum.prototype.add = function add(name, id, comment) {\n // utilized by the parser but not by .fromJSON\n\n if (!util.isString(name))\n throw TypeError(\"name must be a string\");\n\n if (!util.isInteger(id))\n throw TypeError(\"id must be an integer\");\n\n if (this.values[name] !== undefined)\n throw Error(\"duplicate name '\" + name + \"' in \" + this);\n\n if (this.isReservedId(id))\n throw Error(\"id \" + id + \" is reserved in \" + this);\n\n if (this.isReservedName(name))\n throw Error(\"name '\" + name + \"' is reserved in \" + this);\n\n if (this.valuesById[id] !== undefined) {\n if (!(this.options && this.options.allow_alias))\n throw Error(\"duplicate id \" + id + \" in \" + this);\n this.values[name] = id;\n } else\n this.valuesById[this.values[name] = id] = name;\n\n this.comments[name] = comment || null;\n return this;\n};\n\n/**\n * Removes a value from this enum\n * @param {string} name Value name\n * @returns {Enum} `this`\n * @throws {TypeError} If arguments are invalid\n * @throws {Error} If `name` is not a name of this enum\n */\nEnum.prototype.remove = function remove(name) {\n\n if (!util.isString(name))\n throw TypeError(\"name must be a string\");\n\n var val = this.values[name];\n if (val == null)\n throw Error(\"name '\" + name + \"' does not exist in \" + this);\n\n delete this.valuesById[val];\n delete this.values[name];\n delete this.comments[name];\n\n return this;\n};\n\n/**\n * Tests if the specified id is reserved.\n * @param {number} id Id to test\n * @returns {boolean} `true` if reserved, otherwise `false`\n */\nEnum.prototype.isReservedId = function isReservedId(id) {\n return Namespace.isReservedId(this.reserved, id);\n};\n\n/**\n * Tests if the specified name is reserved.\n * @param {string} name Name to test\n * @returns {boolean} `true` if reserved, otherwise `false`\n */\nEnum.prototype.isReservedName = function isReservedName(name) {\n return Namespace.isReservedName(this.reserved, name);\n};\n","\"use strict\";\nmodule.exports = Field;\n\n// extends ReflectionObject\nvar ReflectionObject = require(24);\n((Field.prototype = Object.create(ReflectionObject.prototype)).constructor = Field).className = \"Field\";\n\nvar Enum = require(15),\n types = require(36),\n util = require(37);\n\nvar Type; // cyclic\n\nvar ruleRe = /^required|optional|repeated$/;\n\n/**\n * Constructs a new message field instance. Note that {@link MapField|map fields} have their own class.\n * @name Field\n * @classdesc Reflected message field.\n * @extends FieldBase\n * @constructor\n * @param {string} name Unique name within its namespace\n * @param {number} id Unique id within its namespace\n * @param {string} type Value type\n * @param {string|Object.} [rule=\"optional\"] Field rule\n * @param {string|Object.} [extend] Extended type if different from parent\n * @param {Object.} [options] Declared options\n */\n\n/**\n * Constructs a field from a field descriptor.\n * @param {string} name Field name\n * @param {IField} json Field descriptor\n * @returns {Field} Created field\n * @throws {TypeError} If arguments are invalid\n */\nField.fromJSON = function fromJSON(name, json) {\n return new Field(name, json.id, json.type, json.rule, json.extend, json.options, json.comment);\n};\n\n/**\n * Not an actual constructor. Use {@link Field} instead.\n * @classdesc Base class of all reflected message fields. This is not an actual class but here for the sake of having consistent type definitions.\n * @exports FieldBase\n * @extends ReflectionObject\n * @constructor\n * @param {string} name Unique name within its namespace\n * @param {number} id Unique id within its namespace\n * @param {string} type Value type\n * @param {string|Object.} [rule=\"optional\"] Field rule\n * @param {string|Object.} [extend] Extended type if different from parent\n * @param {Object.} [options] Declared options\n * @param {string} [comment] Comment associated with this field\n */\nfunction Field(name, id, type, rule, extend, options, comment) {\n\n if (util.isObject(rule)) {\n comment = extend;\n options = rule;\n rule = extend = undefined;\n } else if (util.isObject(extend)) {\n comment = options;\n options = extend;\n extend = undefined;\n }\n\n ReflectionObject.call(this, name, options);\n\n if (!util.isInteger(id) || id < 0)\n throw TypeError(\"id must be a non-negative integer\");\n\n if (!util.isString(type))\n throw TypeError(\"type must be a string\");\n\n if (rule !== undefined && !ruleRe.test(rule = rule.toString().toLowerCase()))\n throw TypeError(\"rule must be a string rule\");\n\n if (extend !== undefined && !util.isString(extend))\n throw TypeError(\"extend must be a string\");\n\n /**\n * Field rule, if any.\n * @type {string|undefined}\n */\n if (rule === \"proto3_optional\") {\n rule = \"optional\";\n }\n this.rule = rule && rule !== \"optional\" ? rule : undefined; // toJSON\n\n /**\n * Field type.\n * @type {string}\n */\n this.type = type; // toJSON\n\n /**\n * Unique field id.\n * @type {number}\n */\n this.id = id; // toJSON, marker\n\n /**\n * Extended type if different from parent.\n * @type {string|undefined}\n */\n this.extend = extend || undefined; // toJSON\n\n /**\n * Whether this field is required.\n * @type {boolean}\n */\n this.required = rule === \"required\";\n\n /**\n * Whether this field is optional.\n * @type {boolean}\n */\n this.optional = !this.required;\n\n /**\n * Whether this field is repeated.\n * @type {boolean}\n */\n this.repeated = rule === \"repeated\";\n\n /**\n * Whether this field is a map or not.\n * @type {boolean}\n */\n this.map = false;\n\n /**\n * Message this field belongs to.\n * @type {Type|null}\n */\n this.message = null;\n\n /**\n * OneOf this field belongs to, if any,\n * @type {OneOf|null}\n */\n this.partOf = null;\n\n /**\n * The field type's default value.\n * @type {*}\n */\n this.typeDefault = null;\n\n /**\n * The field's default value on prototypes.\n * @type {*}\n */\n this.defaultValue = null;\n\n /**\n * Whether this field's value should be treated as a long.\n * @type {boolean}\n */\n this.long = util.Long ? types.long[type] !== undefined : /* istanbul ignore next */ false;\n\n /**\n * Whether this field's value is a buffer.\n * @type {boolean}\n */\n this.bytes = type === \"bytes\";\n\n /**\n * Resolved type if not a basic type.\n * @type {Type|Enum|null}\n */\n this.resolvedType = null;\n\n /**\n * Sister-field within the extended type if a declaring extension field.\n * @type {Field|null}\n */\n this.extensionField = null;\n\n /**\n * Sister-field within the declaring namespace if an extended field.\n * @type {Field|null}\n */\n this.declaringField = null;\n\n /**\n * Internally remembers whether this field is packed.\n * @type {boolean|null}\n * @private\n */\n this._packed = null;\n\n /**\n * Comment for this field.\n * @type {string|null}\n */\n this.comment = comment;\n}\n\n/**\n * Determines whether this field is packed. Only relevant when repeated and working with proto2.\n * @name Field#packed\n * @type {boolean}\n * @readonly\n */\nObject.defineProperty(Field.prototype, \"packed\", {\n get: function() {\n // defaults to packed=true if not explicity set to false\n if (this._packed === null)\n this._packed = this.getOption(\"packed\") !== false;\n return this._packed;\n }\n});\n\n/**\n * @override\n */\nField.prototype.setOption = function setOption(name, value, ifNotSet) {\n if (name === \"packed\") // clear cached before setting\n this._packed = null;\n return ReflectionObject.prototype.setOption.call(this, name, value, ifNotSet);\n};\n\n/**\n * Field descriptor.\n * @interface IField\n * @property {string} [rule=\"optional\"] Field rule\n * @property {string} type Field type\n * @property {number} id Field id\n * @property {Object.} [options] Field options\n */\n\n/**\n * Extension field descriptor.\n * @interface IExtensionField\n * @extends IField\n * @property {string} extend Extended type\n */\n\n/**\n * Converts this field to a field descriptor.\n * @param {IToJSONOptions} [toJSONOptions] JSON conversion options\n * @returns {IField} Field descriptor\n */\nField.prototype.toJSON = function toJSON(toJSONOptions) {\n var keepComments = toJSONOptions ? Boolean(toJSONOptions.keepComments) : false;\n return util.toObject([\n \"rule\" , this.rule !== \"optional\" && this.rule || undefined,\n \"type\" , this.type,\n \"id\" , this.id,\n \"extend\" , this.extend,\n \"options\" , this.options,\n \"comment\" , keepComments ? this.comment : undefined\n ]);\n};\n\n/**\n * Resolves this field's type references.\n * @returns {Field} `this`\n * @throws {Error} If any reference cannot be resolved\n */\nField.prototype.resolve = function resolve() {\n\n if (this.resolved)\n return this;\n\n if ((this.typeDefault = types.defaults[this.type]) === undefined) { // if not a basic type, resolve it\n this.resolvedType = (this.declaringField ? this.declaringField.parent : this.parent).lookupTypeOrEnum(this.type);\n if (this.resolvedType instanceof Type)\n this.typeDefault = null;\n else // instanceof Enum\n this.typeDefault = this.resolvedType.values[Object.keys(this.resolvedType.values)[0]]; // first defined\n }\n\n // use explicitly set default value if present\n if (this.options && this.options[\"default\"] != null) {\n this.typeDefault = this.options[\"default\"];\n if (this.resolvedType instanceof Enum && typeof this.typeDefault === \"string\")\n this.typeDefault = this.resolvedType.values[this.typeDefault];\n }\n\n // remove unnecessary options\n if (this.options) {\n if (this.options.packed === true || this.options.packed !== undefined && this.resolvedType && !(this.resolvedType instanceof Enum))\n delete this.options.packed;\n if (!Object.keys(this.options).length)\n this.options = undefined;\n }\n\n // convert to internal data type if necesssary\n if (this.long) {\n this.typeDefault = util.Long.fromNumber(this.typeDefault, this.type.charAt(0) === \"u\");\n\n /* istanbul ignore else */\n if (Object.freeze)\n Object.freeze(this.typeDefault); // long instances are meant to be immutable anyway (i.e. use small int cache that even requires it)\n\n } else if (this.bytes && typeof this.typeDefault === \"string\") {\n var buf;\n if (util.base64.test(this.typeDefault))\n util.base64.decode(this.typeDefault, buf = util.newBuffer(util.base64.length(this.typeDefault)), 0);\n else\n util.utf8.write(this.typeDefault, buf = util.newBuffer(util.utf8.length(this.typeDefault)), 0);\n this.typeDefault = buf;\n }\n\n // take special care of maps and repeated fields\n if (this.map)\n this.defaultValue = util.emptyObject;\n else if (this.repeated)\n this.defaultValue = util.emptyArray;\n else\n this.defaultValue = this.typeDefault;\n\n // ensure proper value on prototype\n if (this.parent instanceof Type)\n this.parent.ctor.prototype[this.name] = this.defaultValue;\n\n return ReflectionObject.prototype.resolve.call(this);\n};\n\n/**\n * Decorator function as returned by {@link Field.d} and {@link MapField.d} (TypeScript).\n * @typedef FieldDecorator\n * @type {function}\n * @param {Object} prototype Target prototype\n * @param {string} fieldName Field name\n * @returns {undefined}\n */\n\n/**\n * Field decorator (TypeScript).\n * @name Field.d\n * @function\n * @param {number} fieldId Field id\n * @param {\"double\"|\"float\"|\"int32\"|\"uint32\"|\"sint32\"|\"fixed32\"|\"sfixed32\"|\"int64\"|\"uint64\"|\"sint64\"|\"fixed64\"|\"sfixed64\"|\"string\"|\"bool\"|\"bytes\"|Object} fieldType Field type\n * @param {\"optional\"|\"required\"|\"repeated\"} [fieldRule=\"optional\"] Field rule\n * @param {T} [defaultValue] Default value\n * @returns {FieldDecorator} Decorator function\n * @template T extends number | number[] | Long | Long[] | string | string[] | boolean | boolean[] | Uint8Array | Uint8Array[] | Buffer | Buffer[]\n */\nField.d = function decorateField(fieldId, fieldType, fieldRule, defaultValue) {\n\n // submessage: decorate the submessage and use its name as the type\n if (typeof fieldType === \"function\")\n fieldType = util.decorateType(fieldType).name;\n\n // enum reference: create a reflected copy of the enum and keep reuseing it\n else if (fieldType && typeof fieldType === \"object\")\n fieldType = util.decorateEnum(fieldType).name;\n\n return function fieldDecorator(prototype, fieldName) {\n util.decorateType(prototype.constructor)\n .add(new Field(fieldName, fieldId, fieldType, fieldRule, { \"default\": defaultValue }));\n };\n};\n\n/**\n * Field decorator (TypeScript).\n * @name Field.d\n * @function\n * @param {number} fieldId Field id\n * @param {Constructor|string} fieldType Field type\n * @param {\"optional\"|\"required\"|\"repeated\"} [fieldRule=\"optional\"] Field rule\n * @returns {FieldDecorator} Decorator function\n * @template T extends Message\n * @variation 2\n */\n// like Field.d but without a default value\n\n// Sets up cyclic dependencies (called in index-light)\nField._configure = function configure(Type_) {\n Type = Type_;\n};\n","\"use strict\";\nvar protobuf = module.exports = require(18);\n\nprotobuf.build = \"light\";\n\n/**\n * A node-style callback as used by {@link load} and {@link Root#load}.\n * @typedef LoadCallback\n * @type {function}\n * @param {Error|null} error Error, if any, otherwise `null`\n * @param {Root} [root] Root, if there hasn't been an error\n * @returns {undefined}\n */\n\n/**\n * Loads one or multiple .proto or preprocessed .json files into a common root namespace and calls the callback.\n * @param {string|string[]} filename One or multiple files to load\n * @param {Root} root Root namespace, defaults to create a new one if omitted.\n * @param {LoadCallback} callback Callback function\n * @returns {undefined}\n * @see {@link Root#load}\n */\nfunction load(filename, root, callback) {\n if (typeof root === \"function\") {\n callback = root;\n root = new protobuf.Root();\n } else if (!root)\n root = new protobuf.Root();\n return root.load(filename, callback);\n}\n\n/**\n * Loads one or multiple .proto or preprocessed .json files into a common root namespace and calls the callback.\n * @name load\n * @function\n * @param {string|string[]} filename One or multiple files to load\n * @param {LoadCallback} callback Callback function\n * @returns {undefined}\n * @see {@link Root#load}\n * @variation 2\n */\n// function load(filename:string, callback:LoadCallback):undefined\n\n/**\n * Loads one or multiple .proto or preprocessed .json files into a common root namespace and returns a promise.\n * @name load\n * @function\n * @param {string|string[]} filename One or multiple files to load\n * @param {Root} [root] Root namespace, defaults to create a new one if omitted.\n * @returns {Promise} Promise\n * @see {@link Root#load}\n * @variation 3\n */\n// function load(filename:string, [root:Root]):Promise\n\nprotobuf.load = load;\n\n/**\n * Synchronously loads one or multiple .proto or preprocessed .json files into a common root namespace (node only).\n * @param {string|string[]} filename One or multiple files to load\n * @param {Root} [root] Root namespace, defaults to create a new one if omitted.\n * @returns {Root} Root namespace\n * @throws {Error} If synchronous fetching is not supported (i.e. in browsers) or if a file's syntax is invalid\n * @see {@link Root#loadSync}\n */\nfunction loadSync(filename, root) {\n if (!root)\n root = new protobuf.Root();\n return root.loadSync(filename);\n}\n\nprotobuf.loadSync = loadSync;\n\n// Serialization\nprotobuf.encoder = require(14);\nprotobuf.decoder = require(13);\nprotobuf.verifier = require(40);\nprotobuf.converter = require(12);\n\n// Reflection\nprotobuf.ReflectionObject = require(24);\nprotobuf.Namespace = require(23);\nprotobuf.Root = require(29);\nprotobuf.Enum = require(15);\nprotobuf.Type = require(35);\nprotobuf.Field = require(16);\nprotobuf.OneOf = require(25);\nprotobuf.MapField = require(20);\nprotobuf.Service = require(33);\nprotobuf.Method = require(22);\n\n// Runtime\nprotobuf.Message = require(21);\nprotobuf.wrappers = require(41);\n\n// Utility\nprotobuf.types = require(36);\nprotobuf.util = require(37);\n\n// Set up possibly cyclic reflection dependencies\nprotobuf.ReflectionObject._configure(protobuf.Root);\nprotobuf.Namespace._configure(protobuf.Type, protobuf.Service, protobuf.Enum);\nprotobuf.Root._configure(protobuf.Type);\nprotobuf.Field._configure(protobuf.Type);\n","\"use strict\";\nvar protobuf = exports;\n\n/**\n * Build type, one of `\"full\"`, `\"light\"` or `\"minimal\"`.\n * @name build\n * @type {string}\n * @const\n */\nprotobuf.build = \"minimal\";\n\n// Serialization\nprotobuf.Writer = require(42);\nprotobuf.BufferWriter = require(43);\nprotobuf.Reader = require(27);\nprotobuf.BufferReader = require(28);\n\n// Utility\nprotobuf.util = require(39);\nprotobuf.rpc = require(31);\nprotobuf.roots = require(30);\nprotobuf.configure = configure;\n\n/* istanbul ignore next */\n/**\n * Reconfigures the library according to the environment.\n * @returns {undefined}\n */\nfunction configure() {\n protobuf.util._configure();\n protobuf.Writer._configure(protobuf.BufferWriter);\n protobuf.Reader._configure(protobuf.BufferReader);\n}\n\n// Set up buffer utility according to the environment\nconfigure();\n","\"use strict\";\nvar protobuf = module.exports = require(17);\n\nprotobuf.build = \"full\";\n\n// Parser\nprotobuf.tokenize = require(34);\nprotobuf.parse = require(26);\nprotobuf.common = require(11);\n\n// Configure parser\nprotobuf.Root._configure(protobuf.Type, protobuf.parse, protobuf.common);\n","\"use strict\";\nmodule.exports = MapField;\n\n// extends Field\nvar Field = require(16);\n((MapField.prototype = Object.create(Field.prototype)).constructor = MapField).className = \"MapField\";\n\nvar types = require(36),\n util = require(37);\n\n/**\n * Constructs a new map field instance.\n * @classdesc Reflected map field.\n * @extends FieldBase\n * @constructor\n * @param {string} name Unique name within its namespace\n * @param {number} id Unique id within its namespace\n * @param {string} keyType Key type\n * @param {string} type Value type\n * @param {Object.} [options] Declared options\n * @param {string} [comment] Comment associated with this field\n */\nfunction MapField(name, id, keyType, type, options, comment) {\n Field.call(this, name, id, type, undefined, undefined, options, comment);\n\n /* istanbul ignore if */\n if (!util.isString(keyType))\n throw TypeError(\"keyType must be a string\");\n\n /**\n * Key type.\n * @type {string}\n */\n this.keyType = keyType; // toJSON, marker\n\n /**\n * Resolved key type if not a basic type.\n * @type {ReflectionObject|null}\n */\n this.resolvedKeyType = null;\n\n // Overrides Field#map\n this.map = true;\n}\n\n/**\n * Map field descriptor.\n * @interface IMapField\n * @extends {IField}\n * @property {string} keyType Key type\n */\n\n/**\n * Extension map field descriptor.\n * @interface IExtensionMapField\n * @extends IMapField\n * @property {string} extend Extended type\n */\n\n/**\n * Constructs a map field from a map field descriptor.\n * @param {string} name Field name\n * @param {IMapField} json Map field descriptor\n * @returns {MapField} Created map field\n * @throws {TypeError} If arguments are invalid\n */\nMapField.fromJSON = function fromJSON(name, json) {\n return new MapField(name, json.id, json.keyType, json.type, json.options, json.comment);\n};\n\n/**\n * Converts this map field to a map field descriptor.\n * @param {IToJSONOptions} [toJSONOptions] JSON conversion options\n * @returns {IMapField} Map field descriptor\n */\nMapField.prototype.toJSON = function toJSON(toJSONOptions) {\n var keepComments = toJSONOptions ? Boolean(toJSONOptions.keepComments) : false;\n return util.toObject([\n \"keyType\" , this.keyType,\n \"type\" , this.type,\n \"id\" , this.id,\n \"extend\" , this.extend,\n \"options\" , this.options,\n \"comment\" , keepComments ? this.comment : undefined\n ]);\n};\n\n/**\n * @override\n */\nMapField.prototype.resolve = function resolve() {\n if (this.resolved)\n return this;\n\n // Besides a value type, map fields have a key type that may be \"any scalar type except for floating point types and bytes\"\n if (types.mapKey[this.keyType] === undefined)\n throw Error(\"invalid key type: \" + this.keyType);\n\n return Field.prototype.resolve.call(this);\n};\n\n/**\n * Map field decorator (TypeScript).\n * @name MapField.d\n * @function\n * @param {number} fieldId Field id\n * @param {\"int32\"|\"uint32\"|\"sint32\"|\"fixed32\"|\"sfixed32\"|\"int64\"|\"uint64\"|\"sint64\"|\"fixed64\"|\"sfixed64\"|\"bool\"|\"string\"} fieldKeyType Field key type\n * @param {\"double\"|\"float\"|\"int32\"|\"uint32\"|\"sint32\"|\"fixed32\"|\"sfixed32\"|\"int64\"|\"uint64\"|\"sint64\"|\"fixed64\"|\"sfixed64\"|\"bool\"|\"string\"|\"bytes\"|Object|Constructor<{}>} fieldValueType Field value type\n * @returns {FieldDecorator} Decorator function\n * @template T extends { [key: string]: number | Long | string | boolean | Uint8Array | Buffer | number[] | Message<{}> }\n */\nMapField.d = function decorateMapField(fieldId, fieldKeyType, fieldValueType) {\n\n // submessage value: decorate the submessage and use its name as the type\n if (typeof fieldValueType === \"function\")\n fieldValueType = util.decorateType(fieldValueType).name;\n\n // enum reference value: create a reflected copy of the enum and keep reuseing it\n else if (fieldValueType && typeof fieldValueType === \"object\")\n fieldValueType = util.decorateEnum(fieldValueType).name;\n\n return function mapFieldDecorator(prototype, fieldName) {\n util.decorateType(prototype.constructor)\n .add(new MapField(fieldName, fieldId, fieldKeyType, fieldValueType));\n };\n};\n","\"use strict\";\nmodule.exports = Message;\n\nvar util = require(39);\n\n/**\n * Constructs a new message instance.\n * @classdesc Abstract runtime message.\n * @constructor\n * @param {Properties} [properties] Properties to set\n * @template T extends object = object\n */\nfunction Message(properties) {\n // not used internally\n if (properties)\n for (var keys = Object.keys(properties), i = 0; i < keys.length; ++i)\n this[keys[i]] = properties[keys[i]];\n}\n\n/**\n * Reference to the reflected type.\n * @name Message.$type\n * @type {Type}\n * @readonly\n */\n\n/**\n * Reference to the reflected type.\n * @name Message#$type\n * @type {Type}\n * @readonly\n */\n\n/*eslint-disable valid-jsdoc*/\n\n/**\n * Creates a new message of this type using the specified properties.\n * @param {Object.} [properties] Properties to set\n * @returns {Message} Message instance\n * @template T extends Message\n * @this Constructor\n */\nMessage.create = function create(properties) {\n return this.$type.create(properties);\n};\n\n/**\n * Encodes a message of this type.\n * @param {T|Object.} message Message to encode\n * @param {Writer} [writer] Writer to use\n * @returns {Writer} Writer\n * @template T extends Message\n * @this Constructor\n */\nMessage.encode = function encode(message, writer) {\n return this.$type.encode(message, writer);\n};\n\n/**\n * Encodes a message of this type preceeded by its length as a varint.\n * @param {T|Object.} message Message to encode\n * @param {Writer} [writer] Writer to use\n * @returns {Writer} Writer\n * @template T extends Message\n * @this Constructor\n */\nMessage.encodeDelimited = function encodeDelimited(message, writer) {\n return this.$type.encodeDelimited(message, writer);\n};\n\n/**\n * Decodes a message of this type.\n * @name Message.decode\n * @function\n * @param {Reader|Uint8Array} reader Reader or buffer to decode\n * @returns {T} Decoded message\n * @template T extends Message\n * @this Constructor\n */\nMessage.decode = function decode(reader) {\n return this.$type.decode(reader);\n};\n\n/**\n * Decodes a message of this type preceeded by its length as a varint.\n * @name Message.decodeDelimited\n * @function\n * @param {Reader|Uint8Array} reader Reader or buffer to decode\n * @returns {T} Decoded message\n * @template T extends Message\n * @this Constructor\n */\nMessage.decodeDelimited = function decodeDelimited(reader) {\n return this.$type.decodeDelimited(reader);\n};\n\n/**\n * Verifies a message of this type.\n * @name Message.verify\n * @function\n * @param {Object.} message Plain object to verify\n * @returns {string|null} `null` if valid, otherwise the reason why it is not\n */\nMessage.verify = function verify(message) {\n return this.$type.verify(message);\n};\n\n/**\n * Creates a new message of this type from a plain object. Also converts values to their respective internal types.\n * @param {Object.} object Plain object\n * @returns {T} Message instance\n * @template T extends Message\n * @this Constructor\n */\nMessage.fromObject = function fromObject(object) {\n return this.$type.fromObject(object);\n};\n\n/**\n * Creates a plain object from a message of this type. Also converts values to other types if specified.\n * @param {T} message Message instance\n * @param {IConversionOptions} [options] Conversion options\n * @returns {Object.} Plain object\n * @template T extends Message\n * @this Constructor\n */\nMessage.toObject = function toObject(message, options) {\n return this.$type.toObject(message, options);\n};\n\n/**\n * Converts this message to JSON.\n * @returns {Object.} JSON object\n */\nMessage.prototype.toJSON = function toJSON() {\n return this.$type.toObject(this, util.toJSONOptions);\n};\n\n/*eslint-enable valid-jsdoc*/","\"use strict\";\nmodule.exports = Method;\n\n// extends ReflectionObject\nvar ReflectionObject = require(24);\n((Method.prototype = Object.create(ReflectionObject.prototype)).constructor = Method).className = \"Method\";\n\nvar util = require(37);\n\n/**\n * Constructs a new service method instance.\n * @classdesc Reflected service method.\n * @extends ReflectionObject\n * @constructor\n * @param {string} name Method name\n * @param {string|undefined} type Method type, usually `\"rpc\"`\n * @param {string} requestType Request message type\n * @param {string} responseType Response message type\n * @param {boolean|Object.} [requestStream] Whether the request is streamed\n * @param {boolean|Object.} [responseStream] Whether the response is streamed\n * @param {Object.} [options] Declared options\n * @param {string} [comment] The comment for this method\n * @param {Object.} [parsedOptions] Declared options, properly parsed into an object\n */\nfunction Method(name, type, requestType, responseType, requestStream, responseStream, options, comment, parsedOptions) {\n\n /* istanbul ignore next */\n if (util.isObject(requestStream)) {\n options = requestStream;\n requestStream = responseStream = undefined;\n } else if (util.isObject(responseStream)) {\n options = responseStream;\n responseStream = undefined;\n }\n\n /* istanbul ignore if */\n if (!(type === undefined || util.isString(type)))\n throw TypeError(\"type must be a string\");\n\n /* istanbul ignore if */\n if (!util.isString(requestType))\n throw TypeError(\"requestType must be a string\");\n\n /* istanbul ignore if */\n if (!util.isString(responseType))\n throw TypeError(\"responseType must be a string\");\n\n ReflectionObject.call(this, name, options);\n\n /**\n * Method type.\n * @type {string}\n */\n this.type = type || \"rpc\"; // toJSON\n\n /**\n * Request type.\n * @type {string}\n */\n this.requestType = requestType; // toJSON, marker\n\n /**\n * Whether requests are streamed or not.\n * @type {boolean|undefined}\n */\n this.requestStream = requestStream ? true : undefined; // toJSON\n\n /**\n * Response type.\n * @type {string}\n */\n this.responseType = responseType; // toJSON\n\n /**\n * Whether responses are streamed or not.\n * @type {boolean|undefined}\n */\n this.responseStream = responseStream ? true : undefined; // toJSON\n\n /**\n * Resolved request type.\n * @type {Type|null}\n */\n this.resolvedRequestType = null;\n\n /**\n * Resolved response type.\n * @type {Type|null}\n */\n this.resolvedResponseType = null;\n\n /**\n * Comment for this method\n * @type {string|null}\n */\n this.comment = comment;\n\n /**\n * Options properly parsed into an object\n */\n this.parsedOptions = parsedOptions;\n}\n\n/**\n * Method descriptor.\n * @interface IMethod\n * @property {string} [type=\"rpc\"] Method type\n * @property {string} requestType Request type\n * @property {string} responseType Response type\n * @property {boolean} [requestStream=false] Whether requests are streamed\n * @property {boolean} [responseStream=false] Whether responses are streamed\n * @property {Object.} [options] Method options\n * @property {string} comment Method comments\n * @property {Object.} [parsedOptions] Method options properly parsed into an object\n */\n\n/**\n * Constructs a method from a method descriptor.\n * @param {string} name Method name\n * @param {IMethod} json Method descriptor\n * @returns {Method} Created method\n * @throws {TypeError} If arguments are invalid\n */\nMethod.fromJSON = function fromJSON(name, json) {\n return new Method(name, json.type, json.requestType, json.responseType, json.requestStream, json.responseStream, json.options, json.comment, json.parsedOptions);\n};\n\n/**\n * Converts this method to a method descriptor.\n * @param {IToJSONOptions} [toJSONOptions] JSON conversion options\n * @returns {IMethod} Method descriptor\n */\nMethod.prototype.toJSON = function toJSON(toJSONOptions) {\n var keepComments = toJSONOptions ? Boolean(toJSONOptions.keepComments) : false;\n return util.toObject([\n \"type\" , this.type !== \"rpc\" && /* istanbul ignore next */ this.type || undefined,\n \"requestType\" , this.requestType,\n \"requestStream\" , this.requestStream,\n \"responseType\" , this.responseType,\n \"responseStream\" , this.responseStream,\n \"options\" , this.options,\n \"comment\" , keepComments ? this.comment : undefined,\n \"parsedOptions\" , this.parsedOptions,\n ]);\n};\n\n/**\n * @override\n */\nMethod.prototype.resolve = function resolve() {\n\n /* istanbul ignore if */\n if (this.resolved)\n return this;\n\n this.resolvedRequestType = this.parent.lookupType(this.requestType);\n this.resolvedResponseType = this.parent.lookupType(this.responseType);\n\n return ReflectionObject.prototype.resolve.call(this);\n};\n","\"use strict\";\nmodule.exports = Namespace;\n\n// extends ReflectionObject\nvar ReflectionObject = require(24);\n((Namespace.prototype = Object.create(ReflectionObject.prototype)).constructor = Namespace).className = \"Namespace\";\n\nvar Field = require(16),\n util = require(37);\n\nvar Type, // cyclic\n Service,\n Enum;\n\n/**\n * Constructs a new namespace instance.\n * @name Namespace\n * @classdesc Reflected namespace.\n * @extends NamespaceBase\n * @constructor\n * @param {string} name Namespace name\n * @param {Object.} [options] Declared options\n */\n\n/**\n * Constructs a namespace from JSON.\n * @memberof Namespace\n * @function\n * @param {string} name Namespace name\n * @param {Object.} json JSON object\n * @returns {Namespace} Created namespace\n * @throws {TypeError} If arguments are invalid\n */\nNamespace.fromJSON = function fromJSON(name, json) {\n return new Namespace(name, json.options).addJSON(json.nested);\n};\n\n/**\n * Converts an array of reflection objects to JSON.\n * @memberof Namespace\n * @param {ReflectionObject[]} array Object array\n * @param {IToJSONOptions} [toJSONOptions] JSON conversion options\n * @returns {Object.|undefined} JSON object or `undefined` when array is empty\n */\nfunction arrayToJSON(array, toJSONOptions) {\n if (!(array && array.length))\n return undefined;\n var obj = {};\n for (var i = 0; i < array.length; ++i)\n obj[array[i].name] = array[i].toJSON(toJSONOptions);\n return obj;\n}\n\nNamespace.arrayToJSON = arrayToJSON;\n\n/**\n * Tests if the specified id is reserved.\n * @param {Array.|undefined} reserved Array of reserved ranges and names\n * @param {number} id Id to test\n * @returns {boolean} `true` if reserved, otherwise `false`\n */\nNamespace.isReservedId = function isReservedId(reserved, id) {\n if (reserved)\n for (var i = 0; i < reserved.length; ++i)\n if (typeof reserved[i] !== \"string\" && reserved[i][0] <= id && reserved[i][1] > id)\n return true;\n return false;\n};\n\n/**\n * Tests if the specified name is reserved.\n * @param {Array.|undefined} reserved Array of reserved ranges and names\n * @param {string} name Name to test\n * @returns {boolean} `true` if reserved, otherwise `false`\n */\nNamespace.isReservedName = function isReservedName(reserved, name) {\n if (reserved)\n for (var i = 0; i < reserved.length; ++i)\n if (reserved[i] === name)\n return true;\n return false;\n};\n\n/**\n * Not an actual constructor. Use {@link Namespace} instead.\n * @classdesc Base class of all reflection objects containing nested objects. This is not an actual class but here for the sake of having consistent type definitions.\n * @exports NamespaceBase\n * @extends ReflectionObject\n * @abstract\n * @constructor\n * @param {string} name Namespace name\n * @param {Object.} [options] Declared options\n * @see {@link Namespace}\n */\nfunction Namespace(name, options) {\n ReflectionObject.call(this, name, options);\n\n /**\n * Nested objects by name.\n * @type {Object.|undefined}\n */\n this.nested = undefined; // toJSON\n\n /**\n * Cached nested objects as an array.\n * @type {ReflectionObject[]|null}\n * @private\n */\n this._nestedArray = null;\n}\n\nfunction clearCache(namespace) {\n namespace._nestedArray = null;\n return namespace;\n}\n\n/**\n * Nested objects of this namespace as an array for iteration.\n * @name NamespaceBase#nestedArray\n * @type {ReflectionObject[]}\n * @readonly\n */\nObject.defineProperty(Namespace.prototype, \"nestedArray\", {\n get: function() {\n return this._nestedArray || (this._nestedArray = util.toArray(this.nested));\n }\n});\n\n/**\n * Namespace descriptor.\n * @interface INamespace\n * @property {Object.} [options] Namespace options\n * @property {Object.} [nested] Nested object descriptors\n */\n\n/**\n * Any extension field descriptor.\n * @typedef AnyExtensionField\n * @type {IExtensionField|IExtensionMapField}\n */\n\n/**\n * Any nested object descriptor.\n * @typedef AnyNestedObject\n * @type {IEnum|IType|IService|AnyExtensionField|INamespace}\n */\n// ^ BEWARE: VSCode hangs forever when using more than 5 types (that's why AnyExtensionField exists in the first place)\n\n/**\n * Converts this namespace to a namespace descriptor.\n * @param {IToJSONOptions} [toJSONOptions] JSON conversion options\n * @returns {INamespace} Namespace descriptor\n */\nNamespace.prototype.toJSON = function toJSON(toJSONOptions) {\n return util.toObject([\n \"options\" , this.options,\n \"nested\" , arrayToJSON(this.nestedArray, toJSONOptions)\n ]);\n};\n\n/**\n * Adds nested objects to this namespace from nested object descriptors.\n * @param {Object.} nestedJson Any nested object descriptors\n * @returns {Namespace} `this`\n */\nNamespace.prototype.addJSON = function addJSON(nestedJson) {\n var ns = this;\n /* istanbul ignore else */\n if (nestedJson) {\n for (var names = Object.keys(nestedJson), i = 0, nested; i < names.length; ++i) {\n nested = nestedJson[names[i]];\n ns.add( // most to least likely\n ( nested.fields !== undefined\n ? Type.fromJSON\n : nested.values !== undefined\n ? Enum.fromJSON\n : nested.methods !== undefined\n ? Service.fromJSON\n : nested.id !== undefined\n ? Field.fromJSON\n : Namespace.fromJSON )(names[i], nested)\n );\n }\n }\n return this;\n};\n\n/**\n * Gets the nested object of the specified name.\n * @param {string} name Nested object name\n * @returns {ReflectionObject|null} The reflection object or `null` if it doesn't exist\n */\nNamespace.prototype.get = function get(name) {\n return this.nested && this.nested[name]\n || null;\n};\n\n/**\n * Gets the values of the nested {@link Enum|enum} of the specified name.\n * This methods differs from {@link Namespace#get|get} in that it returns an enum's values directly and throws instead of returning `null`.\n * @param {string} name Nested enum name\n * @returns {Object.} Enum values\n * @throws {Error} If there is no such enum\n */\nNamespace.prototype.getEnum = function getEnum(name) {\n if (this.nested && this.nested[name] instanceof Enum)\n return this.nested[name].values;\n throw Error(\"no such enum: \" + name);\n};\n\n/**\n * Adds a nested object to this namespace.\n * @param {ReflectionObject} object Nested object to add\n * @returns {Namespace} `this`\n * @throws {TypeError} If arguments are invalid\n * @throws {Error} If there is already a nested object with this name\n */\nNamespace.prototype.add = function add(object) {\n\n if (!(object instanceof Field && object.extend !== undefined || object instanceof Type || object instanceof Enum || object instanceof Service || object instanceof Namespace))\n throw TypeError(\"object must be a valid nested object\");\n\n if (!this.nested)\n this.nested = {};\n else {\n var prev = this.get(object.name);\n if (prev) {\n if (prev instanceof Namespace && object instanceof Namespace && !(prev instanceof Type || prev instanceof Service)) {\n // replace plain namespace but keep existing nested elements and options\n var nested = prev.nestedArray;\n for (var i = 0; i < nested.length; ++i)\n object.add(nested[i]);\n this.remove(prev);\n if (!this.nested)\n this.nested = {};\n object.setOptions(prev.options, true);\n\n } else\n throw Error(\"duplicate name '\" + object.name + \"' in \" + this);\n }\n }\n this.nested[object.name] = object;\n object.onAdd(this);\n return clearCache(this);\n};\n\n/**\n * Removes a nested object from this namespace.\n * @param {ReflectionObject} object Nested object to remove\n * @returns {Namespace} `this`\n * @throws {TypeError} If arguments are invalid\n * @throws {Error} If `object` is not a member of this namespace\n */\nNamespace.prototype.remove = function remove(object) {\n\n if (!(object instanceof ReflectionObject))\n throw TypeError(\"object must be a ReflectionObject\");\n if (object.parent !== this)\n throw Error(object + \" is not a member of \" + this);\n\n delete this.nested[object.name];\n if (!Object.keys(this.nested).length)\n this.nested = undefined;\n\n object.onRemove(this);\n return clearCache(this);\n};\n\n/**\n * Defines additial namespaces within this one if not yet existing.\n * @param {string|string[]} path Path to create\n * @param {*} [json] Nested types to create from JSON\n * @returns {Namespace} Pointer to the last namespace created or `this` if path is empty\n */\nNamespace.prototype.define = function define(path, json) {\n\n if (util.isString(path))\n path = path.split(\".\");\n else if (!Array.isArray(path))\n throw TypeError(\"illegal path\");\n if (path && path.length && path[0] === \"\")\n throw Error(\"path must be relative\");\n\n var ptr = this;\n while (path.length > 0) {\n var part = path.shift();\n if (ptr.nested && ptr.nested[part]) {\n ptr = ptr.nested[part];\n if (!(ptr instanceof Namespace))\n throw Error(\"path conflicts with non-namespace objects\");\n } else\n ptr.add(ptr = new Namespace(part));\n }\n if (json)\n ptr.addJSON(json);\n return ptr;\n};\n\n/**\n * Resolves this namespace's and all its nested objects' type references. Useful to validate a reflection tree, but comes at a cost.\n * @returns {Namespace} `this`\n */\nNamespace.prototype.resolveAll = function resolveAll() {\n var nested = this.nestedArray, i = 0;\n while (i < nested.length)\n if (nested[i] instanceof Namespace)\n nested[i++].resolveAll();\n else\n nested[i++].resolve();\n return this.resolve();\n};\n\n/**\n * Recursively looks up the reflection object matching the specified path in the scope of this namespace.\n * @param {string|string[]} path Path to look up\n * @param {*|Array.<*>} filterTypes Filter types, any combination of the constructors of `protobuf.Type`, `protobuf.Enum`, `protobuf.Service` etc.\n * @param {boolean} [parentAlreadyChecked=false] If known, whether the parent has already been checked\n * @returns {ReflectionObject|null} Looked up object or `null` if none could be found\n */\nNamespace.prototype.lookup = function lookup(path, filterTypes, parentAlreadyChecked) {\n\n /* istanbul ignore next */\n if (typeof filterTypes === \"boolean\") {\n parentAlreadyChecked = filterTypes;\n filterTypes = undefined;\n } else if (filterTypes && !Array.isArray(filterTypes))\n filterTypes = [ filterTypes ];\n\n if (util.isString(path) && path.length) {\n if (path === \".\")\n return this.root;\n path = path.split(\".\");\n } else if (!path.length)\n return this;\n\n // Start at root if path is absolute\n if (path[0] === \"\")\n return this.root.lookup(path.slice(1), filterTypes);\n\n // Test if the first part matches any nested object, and if so, traverse if path contains more\n var found = this.get(path[0]);\n if (found) {\n if (path.length === 1) {\n if (!filterTypes || filterTypes.indexOf(found.constructor) > -1)\n return found;\n } else if (found instanceof Namespace && (found = found.lookup(path.slice(1), filterTypes, true)))\n return found;\n\n // Otherwise try each nested namespace\n } else\n for (var i = 0; i < this.nestedArray.length; ++i)\n if (this._nestedArray[i] instanceof Namespace && (found = this._nestedArray[i].lookup(path, filterTypes, true)))\n return found;\n\n // If there hasn't been a match, try again at the parent\n if (this.parent === null || parentAlreadyChecked)\n return null;\n return this.parent.lookup(path, filterTypes);\n};\n\n/**\n * Looks up the reflection object at the specified path, relative to this namespace.\n * @name NamespaceBase#lookup\n * @function\n * @param {string|string[]} path Path to look up\n * @param {boolean} [parentAlreadyChecked=false] Whether the parent has already been checked\n * @returns {ReflectionObject|null} Looked up object or `null` if none could be found\n * @variation 2\n */\n// lookup(path: string, [parentAlreadyChecked: boolean])\n\n/**\n * Looks up the {@link Type|type} at the specified path, relative to this namespace.\n * Besides its signature, this methods differs from {@link Namespace#lookup|lookup} in that it throws instead of returning `null`.\n * @param {string|string[]} path Path to look up\n * @returns {Type} Looked up type\n * @throws {Error} If `path` does not point to a type\n */\nNamespace.prototype.lookupType = function lookupType(path) {\n var found = this.lookup(path, [ Type ]);\n if (!found)\n throw Error(\"no such type: \" + path);\n return found;\n};\n\n/**\n * Looks up the values of the {@link Enum|enum} at the specified path, relative to this namespace.\n * Besides its signature, this methods differs from {@link Namespace#lookup|lookup} in that it throws instead of returning `null`.\n * @param {string|string[]} path Path to look up\n * @returns {Enum} Looked up enum\n * @throws {Error} If `path` does not point to an enum\n */\nNamespace.prototype.lookupEnum = function lookupEnum(path) {\n var found = this.lookup(path, [ Enum ]);\n if (!found)\n throw Error(\"no such Enum '\" + path + \"' in \" + this);\n return found;\n};\n\n/**\n * Looks up the {@link Type|type} or {@link Enum|enum} at the specified path, relative to this namespace.\n * Besides its signature, this methods differs from {@link Namespace#lookup|lookup} in that it throws instead of returning `null`.\n * @param {string|string[]} path Path to look up\n * @returns {Type} Looked up type or enum\n * @throws {Error} If `path` does not point to a type or enum\n */\nNamespace.prototype.lookupTypeOrEnum = function lookupTypeOrEnum(path) {\n var found = this.lookup(path, [ Type, Enum ]);\n if (!found)\n throw Error(\"no such Type or Enum '\" + path + \"' in \" + this);\n return found;\n};\n\n/**\n * Looks up the {@link Service|service} at the specified path, relative to this namespace.\n * Besides its signature, this methods differs from {@link Namespace#lookup|lookup} in that it throws instead of returning `null`.\n * @param {string|string[]} path Path to look up\n * @returns {Service} Looked up service\n * @throws {Error} If `path` does not point to a service\n */\nNamespace.prototype.lookupService = function lookupService(path) {\n var found = this.lookup(path, [ Service ]);\n if (!found)\n throw Error(\"no such Service '\" + path + \"' in \" + this);\n return found;\n};\n\n// Sets up cyclic dependencies (called in index-light)\nNamespace._configure = function(Type_, Service_, Enum_) {\n Type = Type_;\n Service = Service_;\n Enum = Enum_;\n};\n","\"use strict\";\nmodule.exports = ReflectionObject;\n\nReflectionObject.className = \"ReflectionObject\";\n\nvar util = require(37);\n\nvar Root; // cyclic\n\n/**\n * Constructs a new reflection object instance.\n * @classdesc Base class of all reflection objects.\n * @constructor\n * @param {string} name Object name\n * @param {Object.} [options] Declared options\n * @abstract\n */\nfunction ReflectionObject(name, options) {\n\n if (!util.isString(name))\n throw TypeError(\"name must be a string\");\n\n if (options && !util.isObject(options))\n throw TypeError(\"options must be an object\");\n\n /**\n * Options.\n * @type {Object.|undefined}\n */\n this.options = options; // toJSON\n\n /**\n * Parsed Options.\n * @type {Array.>|undefined}\n */\n this.parsedOptions = null;\n\n /**\n * Unique name within its namespace.\n * @type {string}\n */\n this.name = name;\n\n /**\n * Parent namespace.\n * @type {Namespace|null}\n */\n this.parent = null;\n\n /**\n * Whether already resolved or not.\n * @type {boolean}\n */\n this.resolved = false;\n\n /**\n * Comment text, if any.\n * @type {string|null}\n */\n this.comment = null;\n\n /**\n * Defining file name.\n * @type {string|null}\n */\n this.filename = null;\n}\n\nObject.defineProperties(ReflectionObject.prototype, {\n\n /**\n * Reference to the root namespace.\n * @name ReflectionObject#root\n * @type {Root}\n * @readonly\n */\n root: {\n get: function() {\n var ptr = this;\n while (ptr.parent !== null)\n ptr = ptr.parent;\n return ptr;\n }\n },\n\n /**\n * Full name including leading dot.\n * @name ReflectionObject#fullName\n * @type {string}\n * @readonly\n */\n fullName: {\n get: function() {\n var path = [ this.name ],\n ptr = this.parent;\n while (ptr) {\n path.unshift(ptr.name);\n ptr = ptr.parent;\n }\n return path.join(\".\");\n }\n }\n});\n\n/**\n * Converts this reflection object to its descriptor representation.\n * @returns {Object.} Descriptor\n * @abstract\n */\nReflectionObject.prototype.toJSON = /* istanbul ignore next */ function toJSON() {\n throw Error(); // not implemented, shouldn't happen\n};\n\n/**\n * Called when this object is added to a parent.\n * @param {ReflectionObject} parent Parent added to\n * @returns {undefined}\n */\nReflectionObject.prototype.onAdd = function onAdd(parent) {\n if (this.parent && this.parent !== parent)\n this.parent.remove(this);\n this.parent = parent;\n this.resolved = false;\n var root = parent.root;\n if (root instanceof Root)\n root._handleAdd(this);\n};\n\n/**\n * Called when this object is removed from a parent.\n * @param {ReflectionObject} parent Parent removed from\n * @returns {undefined}\n */\nReflectionObject.prototype.onRemove = function onRemove(parent) {\n var root = parent.root;\n if (root instanceof Root)\n root._handleRemove(this);\n this.parent = null;\n this.resolved = false;\n};\n\n/**\n * Resolves this objects type references.\n * @returns {ReflectionObject} `this`\n */\nReflectionObject.prototype.resolve = function resolve() {\n if (this.resolved)\n return this;\n if (this.root instanceof Root)\n this.resolved = true; // only if part of a root\n return this;\n};\n\n/**\n * Gets an option value.\n * @param {string} name Option name\n * @returns {*} Option value or `undefined` if not set\n */\nReflectionObject.prototype.getOption = function getOption(name) {\n if (this.options)\n return this.options[name];\n return undefined;\n};\n\n/**\n * Sets an option.\n * @param {string} name Option name\n * @param {*} value Option value\n * @param {boolean} [ifNotSet] Sets the option only if it isn't currently set\n * @returns {ReflectionObject} `this`\n */\nReflectionObject.prototype.setOption = function setOption(name, value, ifNotSet) {\n if (!ifNotSet || !this.options || this.options[name] === undefined)\n (this.options || (this.options = {}))[name] = value;\n return this;\n};\n\n/**\n * Sets a parsed option.\n * @param {string} name parsed Option name\n * @param {*} value Option value\n * @param {string} propName dot '.' delimited full path of property within the option to set. if undefined\\empty, will add a new option with that value\n * @returns {ReflectionObject} `this`\n */\nReflectionObject.prototype.setParsedOption = function setParsedOption(name, value, propName) {\n if (!this.parsedOptions) {\n this.parsedOptions = [];\n }\n var parsedOptions = this.parsedOptions;\n if (propName) {\n // If setting a sub property of an option then try to merge it\n // with an existing option\n var opt = parsedOptions.find(function (opt) {\n return Object.prototype.hasOwnProperty.call(opt, name);\n });\n if (opt) {\n // If we found an existing option - just merge the property value\n var newValue = opt[name];\n util.setProperty(newValue, propName, value);\n } else {\n // otherwise, create a new option, set it's property and add it to the list\n opt = {};\n opt[name] = util.setProperty({}, propName, value);\n parsedOptions.push(opt);\n }\n } else {\n // Always create a new option when setting the value of the option itself\n var newOpt = {};\n newOpt[name] = value;\n parsedOptions.push(newOpt);\n }\n return this;\n};\n\n/**\n * Sets multiple options.\n * @param {Object.} options Options to set\n * @param {boolean} [ifNotSet] Sets an option only if it isn't currently set\n * @returns {ReflectionObject} `this`\n */\nReflectionObject.prototype.setOptions = function setOptions(options, ifNotSet) {\n if (options)\n for (var keys = Object.keys(options), i = 0; i < keys.length; ++i)\n this.setOption(keys[i], options[keys[i]], ifNotSet);\n return this;\n};\n\n/**\n * Converts this instance to its string representation.\n * @returns {string} Class name[, space, full name]\n */\nReflectionObject.prototype.toString = function toString() {\n var className = this.constructor.className,\n fullName = this.fullName;\n if (fullName.length)\n return className + \" \" + fullName;\n return className;\n};\n\n// Sets up cyclic dependencies (called in index-light)\nReflectionObject._configure = function(Root_) {\n Root = Root_;\n};\n","\"use strict\";\nmodule.exports = OneOf;\n\n// extends ReflectionObject\nvar ReflectionObject = require(24);\n((OneOf.prototype = Object.create(ReflectionObject.prototype)).constructor = OneOf).className = \"OneOf\";\n\nvar Field = require(16),\n util = require(37);\n\n/**\n * Constructs a new oneof instance.\n * @classdesc Reflected oneof.\n * @extends ReflectionObject\n * @constructor\n * @param {string} name Oneof name\n * @param {string[]|Object.} [fieldNames] Field names\n * @param {Object.} [options] Declared options\n * @param {string} [comment] Comment associated with this field\n */\nfunction OneOf(name, fieldNames, options, comment) {\n if (!Array.isArray(fieldNames)) {\n options = fieldNames;\n fieldNames = undefined;\n }\n ReflectionObject.call(this, name, options);\n\n /* istanbul ignore if */\n if (!(fieldNames === undefined || Array.isArray(fieldNames)))\n throw TypeError(\"fieldNames must be an Array\");\n\n /**\n * Field names that belong to this oneof.\n * @type {string[]}\n */\n this.oneof = fieldNames || []; // toJSON, marker\n\n /**\n * Fields that belong to this oneof as an array for iteration.\n * @type {Field[]}\n * @readonly\n */\n this.fieldsArray = []; // declared readonly for conformance, possibly not yet added to parent\n\n /**\n * Comment for this field.\n * @type {string|null}\n */\n this.comment = comment;\n}\n\n/**\n * Oneof descriptor.\n * @interface IOneOf\n * @property {Array.} oneof Oneof field names\n * @property {Object.} [options] Oneof options\n */\n\n/**\n * Constructs a oneof from a oneof descriptor.\n * @param {string} name Oneof name\n * @param {IOneOf} json Oneof descriptor\n * @returns {OneOf} Created oneof\n * @throws {TypeError} If arguments are invalid\n */\nOneOf.fromJSON = function fromJSON(name, json) {\n return new OneOf(name, json.oneof, json.options, json.comment);\n};\n\n/**\n * Converts this oneof to a oneof descriptor.\n * @param {IToJSONOptions} [toJSONOptions] JSON conversion options\n * @returns {IOneOf} Oneof descriptor\n */\nOneOf.prototype.toJSON = function toJSON(toJSONOptions) {\n var keepComments = toJSONOptions ? Boolean(toJSONOptions.keepComments) : false;\n return util.toObject([\n \"options\" , this.options,\n \"oneof\" , this.oneof,\n \"comment\" , keepComments ? this.comment : undefined\n ]);\n};\n\n/**\n * Adds the fields of the specified oneof to the parent if not already done so.\n * @param {OneOf} oneof The oneof\n * @returns {undefined}\n * @inner\n * @ignore\n */\nfunction addFieldsToParent(oneof) {\n if (oneof.parent)\n for (var i = 0; i < oneof.fieldsArray.length; ++i)\n if (!oneof.fieldsArray[i].parent)\n oneof.parent.add(oneof.fieldsArray[i]);\n}\n\n/**\n * Adds a field to this oneof and removes it from its current parent, if any.\n * @param {Field} field Field to add\n * @returns {OneOf} `this`\n */\nOneOf.prototype.add = function add(field) {\n\n /* istanbul ignore if */\n if (!(field instanceof Field))\n throw TypeError(\"field must be a Field\");\n\n if (field.parent && field.parent !== this.parent)\n field.parent.remove(field);\n this.oneof.push(field.name);\n this.fieldsArray.push(field);\n field.partOf = this; // field.parent remains null\n addFieldsToParent(this);\n return this;\n};\n\n/**\n * Removes a field from this oneof and puts it back to the oneof's parent.\n * @param {Field} field Field to remove\n * @returns {OneOf} `this`\n */\nOneOf.prototype.remove = function remove(field) {\n\n /* istanbul ignore if */\n if (!(field instanceof Field))\n throw TypeError(\"field must be a Field\");\n\n var index = this.fieldsArray.indexOf(field);\n\n /* istanbul ignore if */\n if (index < 0)\n throw Error(field + \" is not a member of \" + this);\n\n this.fieldsArray.splice(index, 1);\n index = this.oneof.indexOf(field.name);\n\n /* istanbul ignore else */\n if (index > -1) // theoretical\n this.oneof.splice(index, 1);\n\n field.partOf = null;\n return this;\n};\n\n/**\n * @override\n */\nOneOf.prototype.onAdd = function onAdd(parent) {\n ReflectionObject.prototype.onAdd.call(this, parent);\n var self = this;\n // Collect present fields\n for (var i = 0; i < this.oneof.length; ++i) {\n var field = parent.get(this.oneof[i]);\n if (field && !field.partOf) {\n field.partOf = self;\n self.fieldsArray.push(field);\n }\n }\n // Add not yet present fields\n addFieldsToParent(this);\n};\n\n/**\n * @override\n */\nOneOf.prototype.onRemove = function onRemove(parent) {\n for (var i = 0, field; i < this.fieldsArray.length; ++i)\n if ((field = this.fieldsArray[i]).parent)\n field.parent.remove(field);\n ReflectionObject.prototype.onRemove.call(this, parent);\n};\n\n/**\n * Decorator function as returned by {@link OneOf.d} (TypeScript).\n * @typedef OneOfDecorator\n * @type {function}\n * @param {Object} prototype Target prototype\n * @param {string} oneofName OneOf name\n * @returns {undefined}\n */\n\n/**\n * OneOf decorator (TypeScript).\n * @function\n * @param {...string} fieldNames Field names\n * @returns {OneOfDecorator} Decorator function\n * @template T extends string\n */\nOneOf.d = function decorateOneOf() {\n var fieldNames = new Array(arguments.length),\n index = 0;\n while (index < arguments.length)\n fieldNames[index] = arguments[index++];\n return function oneOfDecorator(prototype, oneofName) {\n util.decorateType(prototype.constructor)\n .add(new OneOf(oneofName, fieldNames));\n Object.defineProperty(prototype, oneofName, {\n get: util.oneOfGetter(fieldNames),\n set: util.oneOfSetter(fieldNames)\n });\n };\n};\n","\"use strict\";\nmodule.exports = parse;\n\nparse.filename = null;\nparse.defaults = { keepCase: false };\n\nvar tokenize = require(34),\n Root = require(29),\n Type = require(35),\n Field = require(16),\n MapField = require(20),\n OneOf = require(25),\n Enum = require(15),\n Service = require(33),\n Method = require(22),\n types = require(36),\n util = require(37);\n\nvar base10Re = /^[1-9][0-9]*$/,\n base10NegRe = /^-?[1-9][0-9]*$/,\n base16Re = /^0[x][0-9a-fA-F]+$/,\n base16NegRe = /^-?0[x][0-9a-fA-F]+$/,\n base8Re = /^0[0-7]+$/,\n base8NegRe = /^-?0[0-7]+$/,\n numberRe = /^(?![eE])[0-9]*(?:\\.[0-9]*)?(?:[eE][+-]?[0-9]+)?$/,\n nameRe = /^[a-zA-Z_][a-zA-Z_0-9]*$/,\n typeRefRe = /^(?:\\.?[a-zA-Z_][a-zA-Z_0-9]*)(?:\\.[a-zA-Z_][a-zA-Z_0-9]*)*$/,\n fqTypeRefRe = /^(?:\\.[a-zA-Z_][a-zA-Z_0-9]*)+$/;\n\n/**\n * Result object returned from {@link parse}.\n * @interface IParserResult\n * @property {string|undefined} package Package name, if declared\n * @property {string[]|undefined} imports Imports, if any\n * @property {string[]|undefined} weakImports Weak imports, if any\n * @property {string|undefined} syntax Syntax, if specified (either `\"proto2\"` or `\"proto3\"`)\n * @property {Root} root Populated root instance\n */\n\n/**\n * Options modifying the behavior of {@link parse}.\n * @interface IParseOptions\n * @property {boolean} [keepCase=false] Keeps field casing instead of converting to camel case\n * @property {boolean} [alternateCommentMode=false] Recognize double-slash comments in addition to doc-block comments.\n * @property {boolean} [preferTrailingComment=false] Use trailing comment when both leading comment and trailing comment exist.\n */\n\n/**\n * Options modifying the behavior of JSON serialization.\n * @interface IToJSONOptions\n * @property {boolean} [keepComments=false] Serializes comments.\n */\n\n/**\n * Parses the given .proto source and returns an object with the parsed contents.\n * @param {string} source Source contents\n * @param {Root} root Root to populate\n * @param {IParseOptions} [options] Parse options. Defaults to {@link parse.defaults} when omitted.\n * @returns {IParserResult} Parser result\n * @property {string} filename=null Currently processing file name for error reporting, if known\n * @property {IParseOptions} defaults Default {@link IParseOptions}\n */\nfunction parse(source, root, options) {\n /* eslint-disable callback-return */\n if (!(root instanceof Root)) {\n options = root;\n root = new Root();\n }\n if (!options)\n options = parse.defaults;\n\n var preferTrailingComment = options.preferTrailingComment || false;\n var tn = tokenize(source, options.alternateCommentMode || false),\n next = tn.next,\n push = tn.push,\n peek = tn.peek,\n skip = tn.skip,\n cmnt = tn.cmnt;\n\n var head = true,\n pkg,\n imports,\n weakImports,\n syntax,\n isProto3 = false;\n\n var ptr = root;\n\n var applyCase = options.keepCase ? function(name) { return name; } : util.camelCase;\n\n /* istanbul ignore next */\n function illegal(token, name, insideTryCatch) {\n var filename = parse.filename;\n if (!insideTryCatch)\n parse.filename = null;\n return Error(\"illegal \" + (name || \"token\") + \" '\" + token + \"' (\" + (filename ? filename + \", \" : \"\") + \"line \" + tn.line + \")\");\n }\n\n function readString() {\n var values = [],\n token;\n do {\n /* istanbul ignore if */\n if ((token = next()) !== \"\\\"\" && token !== \"'\")\n throw illegal(token);\n\n values.push(next());\n skip(token);\n token = peek();\n } while (token === \"\\\"\" || token === \"'\");\n return values.join(\"\");\n }\n\n function readValue(acceptTypeRef) {\n var token = next();\n switch (token) {\n case \"'\":\n case \"\\\"\":\n push(token);\n return readString();\n case \"true\": case \"TRUE\":\n return true;\n case \"false\": case \"FALSE\":\n return false;\n }\n try {\n return parseNumber(token, /* insideTryCatch */ true);\n } catch (e) {\n\n /* istanbul ignore else */\n if (acceptTypeRef && typeRefRe.test(token))\n return token;\n\n /* istanbul ignore next */\n throw illegal(token, \"value\");\n }\n }\n\n function readRanges(target, acceptStrings) {\n var token, start;\n do {\n if (acceptStrings && ((token = peek()) === \"\\\"\" || token === \"'\"))\n target.push(readString());\n else\n target.push([ start = parseId(next()), skip(\"to\", true) ? parseId(next()) : start ]);\n } while (skip(\",\", true));\n skip(\";\");\n }\n\n function parseNumber(token, insideTryCatch) {\n var sign = 1;\n if (token.charAt(0) === \"-\") {\n sign = -1;\n token = token.substring(1);\n }\n switch (token) {\n case \"inf\": case \"INF\": case \"Inf\":\n return sign * Infinity;\n case \"nan\": case \"NAN\": case \"Nan\": case \"NaN\":\n return NaN;\n case \"0\":\n return 0;\n }\n if (base10Re.test(token))\n return sign * parseInt(token, 10);\n if (base16Re.test(token))\n return sign * parseInt(token, 16);\n if (base8Re.test(token))\n return sign * parseInt(token, 8);\n\n /* istanbul ignore else */\n if (numberRe.test(token))\n return sign * parseFloat(token);\n\n /* istanbul ignore next */\n throw illegal(token, \"number\", insideTryCatch);\n }\n\n function parseId(token, acceptNegative) {\n switch (token) {\n case \"max\": case \"MAX\": case \"Max\":\n return 536870911;\n case \"0\":\n return 0;\n }\n\n /* istanbul ignore if */\n if (!acceptNegative && token.charAt(0) === \"-\")\n throw illegal(token, \"id\");\n\n if (base10NegRe.test(token))\n return parseInt(token, 10);\n if (base16NegRe.test(token))\n return parseInt(token, 16);\n\n /* istanbul ignore else */\n if (base8NegRe.test(token))\n return parseInt(token, 8);\n\n /* istanbul ignore next */\n throw illegal(token, \"id\");\n }\n\n function parsePackage() {\n\n /* istanbul ignore if */\n if (pkg !== undefined)\n throw illegal(\"package\");\n\n pkg = next();\n\n /* istanbul ignore if */\n if (!typeRefRe.test(pkg))\n throw illegal(pkg, \"name\");\n\n ptr = ptr.define(pkg);\n skip(\";\");\n }\n\n function parseImport() {\n var token = peek();\n var whichImports;\n switch (token) {\n case \"weak\":\n whichImports = weakImports || (weakImports = []);\n next();\n break;\n case \"public\":\n next();\n // eslint-disable-line no-fallthrough\n default:\n whichImports = imports || (imports = []);\n break;\n }\n token = readString();\n skip(\";\");\n whichImports.push(token);\n }\n\n function parseSyntax() {\n skip(\"=\");\n syntax = readString();\n isProto3 = syntax === \"proto3\";\n\n /* istanbul ignore if */\n if (!isProto3 && syntax !== \"proto2\")\n throw illegal(syntax, \"syntax\");\n\n skip(\";\");\n }\n\n function parseCommon(parent, token) {\n switch (token) {\n\n case \"option\":\n parseOption(parent, token);\n skip(\";\");\n return true;\n\n case \"message\":\n parseType(parent, token);\n return true;\n\n case \"enum\":\n parseEnum(parent, token);\n return true;\n\n case \"service\":\n parseService(parent, token);\n return true;\n\n case \"extend\":\n parseExtension(parent, token);\n return true;\n }\n return false;\n }\n\n function ifBlock(obj, fnIf, fnElse) {\n var trailingLine = tn.line;\n if (obj) {\n if(typeof obj.comment !== \"string\") {\n obj.comment = cmnt(); // try block-type comment\n }\n obj.filename = parse.filename;\n }\n if (skip(\"{\", true)) {\n var token;\n while ((token = next()) !== \"}\")\n fnIf(token);\n skip(\";\", true);\n } else {\n if (fnElse)\n fnElse();\n skip(\";\");\n if (obj && (typeof obj.comment !== \"string\" || preferTrailingComment))\n obj.comment = cmnt(trailingLine) || obj.comment; // try line-type comment\n }\n }\n\n function parseType(parent, token) {\n\n /* istanbul ignore if */\n if (!nameRe.test(token = next()))\n throw illegal(token, \"type name\");\n\n var type = new Type(token);\n ifBlock(type, function parseType_block(token) {\n if (parseCommon(type, token))\n return;\n\n switch (token) {\n\n case \"map\":\n parseMapField(type, token);\n break;\n\n case \"required\":\n case \"repeated\":\n parseField(type, token);\n break;\n\n case \"optional\":\n /* istanbul ignore if */\n if (isProto3) {\n parseField(type, \"proto3_optional\");\n } else {\n parseField(type, \"optional\");\n }\n break;\n\n case \"oneof\":\n parseOneOf(type, token);\n break;\n\n case \"extensions\":\n readRanges(type.extensions || (type.extensions = []));\n break;\n\n case \"reserved\":\n readRanges(type.reserved || (type.reserved = []), true);\n break;\n\n default:\n /* istanbul ignore if */\n if (!isProto3 || !typeRefRe.test(token))\n throw illegal(token);\n\n push(token);\n parseField(type, \"optional\");\n break;\n }\n });\n parent.add(type);\n }\n\n function parseField(parent, rule, extend) {\n var type = next();\n if (type === \"group\") {\n parseGroup(parent, rule);\n return;\n }\n\n /* istanbul ignore if */\n if (!typeRefRe.test(type))\n throw illegal(type, \"type\");\n\n var name = next();\n\n /* istanbul ignore if */\n if (!nameRe.test(name))\n throw illegal(name, \"name\");\n\n name = applyCase(name);\n skip(\"=\");\n\n var field = new Field(name, parseId(next()), type, rule, extend);\n ifBlock(field, function parseField_block(token) {\n\n /* istanbul ignore else */\n if (token === \"option\") {\n parseOption(field, token);\n skip(\";\");\n } else\n throw illegal(token);\n\n }, function parseField_line() {\n parseInlineOptions(field);\n });\n\n if (rule === \"proto3_optional\") {\n // for proto3 optional fields, we create a single-member Oneof to mimic \"optional\" behavior\n var oneof = new OneOf(\"_\" + name);\n field.setOption(\"proto3_optional\", true);\n oneof.add(field);\n parent.add(oneof);\n } else {\n parent.add(field);\n }\n\n // JSON defaults to packed=true if not set so we have to set packed=false explicity when\n // parsing proto2 descriptors without the option, where applicable. This must be done for\n // all known packable types and anything that could be an enum (= is not a basic type).\n if (!isProto3 && field.repeated && (types.packed[type] !== undefined || types.basic[type] === undefined))\n field.setOption(\"packed\", false, /* ifNotSet */ true);\n }\n\n function parseGroup(parent, rule) {\n var name = next();\n\n /* istanbul ignore if */\n if (!nameRe.test(name))\n throw illegal(name, \"name\");\n\n var fieldName = util.lcFirst(name);\n if (name === fieldName)\n name = util.ucFirst(name);\n skip(\"=\");\n var id = parseId(next());\n var type = new Type(name);\n type.group = true;\n var field = new Field(fieldName, id, name, rule);\n field.filename = parse.filename;\n ifBlock(type, function parseGroup_block(token) {\n switch (token) {\n\n case \"option\":\n parseOption(type, token);\n skip(\";\");\n break;\n\n case \"required\":\n case \"repeated\":\n parseField(type, token);\n break;\n\n case \"optional\":\n /* istanbul ignore if */\n if (isProto3) {\n parseField(type, \"proto3_optional\");\n } else {\n parseField(type, \"optional\");\n }\n break;\n\n /* istanbul ignore next */\n default:\n throw illegal(token); // there are no groups with proto3 semantics\n }\n });\n parent.add(type)\n .add(field);\n }\n\n function parseMapField(parent) {\n skip(\"<\");\n var keyType = next();\n\n /* istanbul ignore if */\n if (types.mapKey[keyType] === undefined)\n throw illegal(keyType, \"type\");\n\n skip(\",\");\n var valueType = next();\n\n /* istanbul ignore if */\n if (!typeRefRe.test(valueType))\n throw illegal(valueType, \"type\");\n\n skip(\">\");\n var name = next();\n\n /* istanbul ignore if */\n if (!nameRe.test(name))\n throw illegal(name, \"name\");\n\n skip(\"=\");\n var field = new MapField(applyCase(name), parseId(next()), keyType, valueType);\n ifBlock(field, function parseMapField_block(token) {\n\n /* istanbul ignore else */\n if (token === \"option\") {\n parseOption(field, token);\n skip(\";\");\n } else\n throw illegal(token);\n\n }, function parseMapField_line() {\n parseInlineOptions(field);\n });\n parent.add(field);\n }\n\n function parseOneOf(parent, token) {\n\n /* istanbul ignore if */\n if (!nameRe.test(token = next()))\n throw illegal(token, \"name\");\n\n var oneof = new OneOf(applyCase(token));\n ifBlock(oneof, function parseOneOf_block(token) {\n if (token === \"option\") {\n parseOption(oneof, token);\n skip(\";\");\n } else {\n push(token);\n parseField(oneof, \"optional\");\n }\n });\n parent.add(oneof);\n }\n\n function parseEnum(parent, token) {\n\n /* istanbul ignore if */\n if (!nameRe.test(token = next()))\n throw illegal(token, \"name\");\n\n var enm = new Enum(token);\n ifBlock(enm, function parseEnum_block(token) {\n switch(token) {\n case \"option\":\n parseOption(enm, token);\n skip(\";\");\n break;\n\n case \"reserved\":\n readRanges(enm.reserved || (enm.reserved = []), true);\n break;\n\n default:\n parseEnumValue(enm, token);\n }\n });\n parent.add(enm);\n }\n\n function parseEnumValue(parent, token) {\n\n /* istanbul ignore if */\n if (!nameRe.test(token))\n throw illegal(token, \"name\");\n\n skip(\"=\");\n var value = parseId(next(), true),\n dummy = {};\n ifBlock(dummy, function parseEnumValue_block(token) {\n\n /* istanbul ignore else */\n if (token === \"option\") {\n parseOption(dummy, token); // skip\n skip(\";\");\n } else\n throw illegal(token);\n\n }, function parseEnumValue_line() {\n parseInlineOptions(dummy); // skip\n });\n parent.add(token, value, dummy.comment);\n }\n\n function parseOption(parent, token) {\n var isCustom = skip(\"(\", true);\n\n /* istanbul ignore if */\n if (!typeRefRe.test(token = next()))\n throw illegal(token, \"name\");\n\n var name = token;\n var option = name;\n var propName;\n\n if (isCustom) {\n skip(\")\");\n name = \"(\" + name + \")\";\n option = name;\n token = peek();\n if (fqTypeRefRe.test(token)) {\n propName = token.substr(1); //remove '.' before property name\n name += token;\n next();\n }\n }\n skip(\"=\");\n var optionValue = parseOptionValue(parent, name);\n setParsedOption(parent, option, optionValue, propName);\n }\n\n function parseOptionValue(parent, name) {\n if (skip(\"{\", true)) { // { a: \"foo\" b { c: \"bar\" } }\n var result = {};\n while (!skip(\"}\", true)) {\n /* istanbul ignore if */\n if (!nameRe.test(token = next()))\n throw illegal(token, \"name\");\n\n var value;\n var propName = token;\n if (peek() === \"{\")\n value = parseOptionValue(parent, name + \".\" + token);\n else {\n skip(\":\");\n if (peek() === \"{\")\n value = parseOptionValue(parent, name + \".\" + token);\n else {\n value = readValue(true);\n setOption(parent, name + \".\" + token, value);\n }\n }\n var prevValue = result[propName];\n if (prevValue)\n value = [].concat(prevValue).concat(value);\n result[propName] = value;\n skip(\",\", true);\n }\n return result;\n }\n\n var simpleValue = readValue(true);\n setOption(parent, name, simpleValue);\n return simpleValue;\n // Does not enforce a delimiter to be universal\n }\n\n function setOption(parent, name, value) {\n if (parent.setOption)\n parent.setOption(name, value);\n }\n\n function setParsedOption(parent, name, value, propName) {\n if (parent.setParsedOption)\n parent.setParsedOption(name, value, propName);\n }\n\n function parseInlineOptions(parent) {\n if (skip(\"[\", true)) {\n do {\n parseOption(parent, \"option\");\n } while (skip(\",\", true));\n skip(\"]\");\n }\n return parent;\n }\n\n function parseService(parent, token) {\n\n /* istanbul ignore if */\n if (!nameRe.test(token = next()))\n throw illegal(token, \"service name\");\n\n var service = new Service(token);\n ifBlock(service, function parseService_block(token) {\n if (parseCommon(service, token))\n return;\n\n /* istanbul ignore else */\n if (token === \"rpc\")\n parseMethod(service, token);\n else\n throw illegal(token);\n });\n parent.add(service);\n }\n\n function parseMethod(parent, token) {\n // Get the comment of the preceding line now (if one exists) in case the\n // method is defined across multiple lines.\n var commentText = cmnt();\n\n var type = token;\n\n /* istanbul ignore if */\n if (!nameRe.test(token = next()))\n throw illegal(token, \"name\");\n\n var name = token,\n requestType, requestStream,\n responseType, responseStream;\n\n skip(\"(\");\n if (skip(\"stream\", true))\n requestStream = true;\n\n /* istanbul ignore if */\n if (!typeRefRe.test(token = next()))\n throw illegal(token);\n\n requestType = token;\n skip(\")\"); skip(\"returns\"); skip(\"(\");\n if (skip(\"stream\", true))\n responseStream = true;\n\n /* istanbul ignore if */\n if (!typeRefRe.test(token = next()))\n throw illegal(token);\n\n responseType = token;\n skip(\")\");\n\n var method = new Method(name, type, requestType, responseType, requestStream, responseStream);\n method.comment = commentText;\n ifBlock(method, function parseMethod_block(token) {\n\n /* istanbul ignore else */\n if (token === \"option\") {\n parseOption(method, token);\n skip(\";\");\n } else\n throw illegal(token);\n\n });\n parent.add(method);\n }\n\n function parseExtension(parent, token) {\n\n /* istanbul ignore if */\n if (!typeRefRe.test(token = next()))\n throw illegal(token, \"reference\");\n\n var reference = token;\n ifBlock(null, function parseExtension_block(token) {\n switch (token) {\n\n case \"required\":\n case \"repeated\":\n parseField(parent, token, reference);\n break;\n\n case \"optional\":\n /* istanbul ignore if */\n if (isProto3) {\n parseField(parent, \"proto3_optional\", reference);\n } else {\n parseField(parent, \"optional\", reference);\n }\n break;\n\n default:\n /* istanbul ignore if */\n if (!isProto3 || !typeRefRe.test(token))\n throw illegal(token);\n push(token);\n parseField(parent, \"optional\", reference);\n break;\n }\n });\n }\n\n var token;\n while ((token = next()) !== null) {\n switch (token) {\n\n case \"package\":\n\n /* istanbul ignore if */\n if (!head)\n throw illegal(token);\n\n parsePackage();\n break;\n\n case \"import\":\n\n /* istanbul ignore if */\n if (!head)\n throw illegal(token);\n\n parseImport();\n break;\n\n case \"syntax\":\n\n /* istanbul ignore if */\n if (!head)\n throw illegal(token);\n\n parseSyntax();\n break;\n\n case \"option\":\n\n parseOption(ptr, token);\n skip(\";\");\n break;\n\n default:\n\n /* istanbul ignore else */\n if (parseCommon(ptr, token)) {\n head = false;\n continue;\n }\n\n /* istanbul ignore next */\n throw illegal(token);\n }\n }\n\n parse.filename = null;\n return {\n \"package\" : pkg,\n \"imports\" : imports,\n weakImports : weakImports,\n syntax : syntax,\n root : root\n };\n}\n\n/**\n * Parses the given .proto source and returns an object with the parsed contents.\n * @name parse\n * @function\n * @param {string} source Source contents\n * @param {IParseOptions} [options] Parse options. Defaults to {@link parse.defaults} when omitted.\n * @returns {IParserResult} Parser result\n * @property {string} filename=null Currently processing file name for error reporting, if known\n * @property {IParseOptions} defaults Default {@link IParseOptions}\n * @variation 2\n */\n","\"use strict\";\nmodule.exports = Reader;\n\nvar util = require(39);\n\nvar BufferReader; // cyclic\n\nvar LongBits = util.LongBits,\n utf8 = util.utf8;\n\n/* istanbul ignore next */\nfunction indexOutOfRange(reader, writeLength) {\n return RangeError(\"index out of range: \" + reader.pos + \" + \" + (writeLength || 1) + \" > \" + reader.len);\n}\n\n/**\n * Constructs a new reader instance using the specified buffer.\n * @classdesc Wire format reader using `Uint8Array` if available, otherwise `Array`.\n * @constructor\n * @param {Uint8Array} buffer Buffer to read from\n */\nfunction Reader(buffer) {\n\n /**\n * Read buffer.\n * @type {Uint8Array}\n */\n this.buf = buffer;\n\n /**\n * Read buffer position.\n * @type {number}\n */\n this.pos = 0;\n\n /**\n * Read buffer length.\n * @type {number}\n */\n this.len = buffer.length;\n}\n\nvar create_array = typeof Uint8Array !== \"undefined\"\n ? function create_typed_array(buffer) {\n if (buffer instanceof Uint8Array || Array.isArray(buffer))\n return new Reader(buffer);\n throw Error(\"illegal buffer\");\n }\n /* istanbul ignore next */\n : function create_array(buffer) {\n if (Array.isArray(buffer))\n return new Reader(buffer);\n throw Error(\"illegal buffer\");\n };\n\nvar create = function create() {\n return util.Buffer\n ? function create_buffer_setup(buffer) {\n return (Reader.create = function create_buffer(buffer) {\n return util.Buffer.isBuffer(buffer)\n ? new BufferReader(buffer)\n /* istanbul ignore next */\n : create_array(buffer);\n })(buffer);\n }\n /* istanbul ignore next */\n : create_array;\n};\n\n/**\n * Creates a new reader using the specified buffer.\n * @function\n * @param {Uint8Array|Buffer} buffer Buffer to read from\n * @returns {Reader|BufferReader} A {@link BufferReader} if `buffer` is a Buffer, otherwise a {@link Reader}\n * @throws {Error} If `buffer` is not a valid buffer\n */\nReader.create = create();\n\nReader.prototype._slice = util.Array.prototype.subarray || /* istanbul ignore next */ util.Array.prototype.slice;\n\n/**\n * Reads a varint as an unsigned 32 bit value.\n * @function\n * @returns {number} Value read\n */\nReader.prototype.uint32 = (function read_uint32_setup() {\n var value = 4294967295; // optimizer type-hint, tends to deopt otherwise (?!)\n return function read_uint32() {\n value = ( this.buf[this.pos] & 127 ) >>> 0; if (this.buf[this.pos++] < 128) return value;\n value = (value | (this.buf[this.pos] & 127) << 7) >>> 0; if (this.buf[this.pos++] < 128) return value;\n value = (value | (this.buf[this.pos] & 127) << 14) >>> 0; if (this.buf[this.pos++] < 128) return value;\n value = (value | (this.buf[this.pos] & 127) << 21) >>> 0; if (this.buf[this.pos++] < 128) return value;\n value = (value | (this.buf[this.pos] & 15) << 28) >>> 0; if (this.buf[this.pos++] < 128) return value;\n\n /* istanbul ignore if */\n if ((this.pos += 5) > this.len) {\n this.pos = this.len;\n throw indexOutOfRange(this, 10);\n }\n return value;\n };\n})();\n\n/**\n * Reads a varint as a signed 32 bit value.\n * @returns {number} Value read\n */\nReader.prototype.int32 = function read_int32() {\n return this.uint32() | 0;\n};\n\n/**\n * Reads a zig-zag encoded varint as a signed 32 bit value.\n * @returns {number} Value read\n */\nReader.prototype.sint32 = function read_sint32() {\n var value = this.uint32();\n return value >>> 1 ^ -(value & 1) | 0;\n};\n\n/* eslint-disable no-invalid-this */\n\nfunction readLongVarint() {\n // tends to deopt with local vars for octet etc.\n var bits = new LongBits(0, 0);\n var i = 0;\n if (this.len - this.pos > 4) { // fast route (lo)\n for (; i < 4; ++i) {\n // 1st..4th\n bits.lo = (bits.lo | (this.buf[this.pos] & 127) << i * 7) >>> 0;\n if (this.buf[this.pos++] < 128)\n return bits;\n }\n // 5th\n bits.lo = (bits.lo | (this.buf[this.pos] & 127) << 28) >>> 0;\n bits.hi = (bits.hi | (this.buf[this.pos] & 127) >> 4) >>> 0;\n if (this.buf[this.pos++] < 128)\n return bits;\n i = 0;\n } else {\n for (; i < 3; ++i) {\n /* istanbul ignore if */\n if (this.pos >= this.len)\n throw indexOutOfRange(this);\n // 1st..3th\n bits.lo = (bits.lo | (this.buf[this.pos] & 127) << i * 7) >>> 0;\n if (this.buf[this.pos++] < 128)\n return bits;\n }\n // 4th\n bits.lo = (bits.lo | (this.buf[this.pos++] & 127) << i * 7) >>> 0;\n return bits;\n }\n if (this.len - this.pos > 4) { // fast route (hi)\n for (; i < 5; ++i) {\n // 6th..10th\n bits.hi = (bits.hi | (this.buf[this.pos] & 127) << i * 7 + 3) >>> 0;\n if (this.buf[this.pos++] < 128)\n return bits;\n }\n } else {\n for (; i < 5; ++i) {\n /* istanbul ignore if */\n if (this.pos >= this.len)\n throw indexOutOfRange(this);\n // 6th..10th\n bits.hi = (bits.hi | (this.buf[this.pos] & 127) << i * 7 + 3) >>> 0;\n if (this.buf[this.pos++] < 128)\n return bits;\n }\n }\n /* istanbul ignore next */\n throw Error(\"invalid varint encoding\");\n}\n\n/* eslint-enable no-invalid-this */\n\n/**\n * Reads a varint as a signed 64 bit value.\n * @name Reader#int64\n * @function\n * @returns {Long} Value read\n */\n\n/**\n * Reads a varint as an unsigned 64 bit value.\n * @name Reader#uint64\n * @function\n * @returns {Long} Value read\n */\n\n/**\n * Reads a zig-zag encoded varint as a signed 64 bit value.\n * @name Reader#sint64\n * @function\n * @returns {Long} Value read\n */\n\n/**\n * Reads a varint as a boolean.\n * @returns {boolean} Value read\n */\nReader.prototype.bool = function read_bool() {\n return this.uint32() !== 0;\n};\n\nfunction readFixed32_end(buf, end) { // note that this uses `end`, not `pos`\n return (buf[end - 4]\n | buf[end - 3] << 8\n | buf[end - 2] << 16\n | buf[end - 1] << 24) >>> 0;\n}\n\n/**\n * Reads fixed 32 bits as an unsigned 32 bit integer.\n * @returns {number} Value read\n */\nReader.prototype.fixed32 = function read_fixed32() {\n\n /* istanbul ignore if */\n if (this.pos + 4 > this.len)\n throw indexOutOfRange(this, 4);\n\n return readFixed32_end(this.buf, this.pos += 4);\n};\n\n/**\n * Reads fixed 32 bits as a signed 32 bit integer.\n * @returns {number} Value read\n */\nReader.prototype.sfixed32 = function read_sfixed32() {\n\n /* istanbul ignore if */\n if (this.pos + 4 > this.len)\n throw indexOutOfRange(this, 4);\n\n return readFixed32_end(this.buf, this.pos += 4) | 0;\n};\n\n/* eslint-disable no-invalid-this */\n\nfunction readFixed64(/* this: Reader */) {\n\n /* istanbul ignore if */\n if (this.pos + 8 > this.len)\n throw indexOutOfRange(this, 8);\n\n return new LongBits(readFixed32_end(this.buf, this.pos += 4), readFixed32_end(this.buf, this.pos += 4));\n}\n\n/* eslint-enable no-invalid-this */\n\n/**\n * Reads fixed 64 bits.\n * @name Reader#fixed64\n * @function\n * @returns {Long} Value read\n */\n\n/**\n * Reads zig-zag encoded fixed 64 bits.\n * @name Reader#sfixed64\n * @function\n * @returns {Long} Value read\n */\n\n/**\n * Reads a float (32 bit) as a number.\n * @function\n * @returns {number} Value read\n */\nReader.prototype.float = function read_float() {\n\n /* istanbul ignore if */\n if (this.pos + 4 > this.len)\n throw indexOutOfRange(this, 4);\n\n var value = util.float.readFloatLE(this.buf, this.pos);\n this.pos += 4;\n return value;\n};\n\n/**\n * Reads a double (64 bit float) as a number.\n * @function\n * @returns {number} Value read\n */\nReader.prototype.double = function read_double() {\n\n /* istanbul ignore if */\n if (this.pos + 8 > this.len)\n throw indexOutOfRange(this, 4);\n\n var value = util.float.readDoubleLE(this.buf, this.pos);\n this.pos += 8;\n return value;\n};\n\n/**\n * Reads a sequence of bytes preceeded by its length as a varint.\n * @returns {Uint8Array} Value read\n */\nReader.prototype.bytes = function read_bytes() {\n var length = this.uint32(),\n start = this.pos,\n end = this.pos + length;\n\n /* istanbul ignore if */\n if (end > this.len)\n throw indexOutOfRange(this, length);\n\n this.pos += length;\n if (Array.isArray(this.buf)) // plain array\n return this.buf.slice(start, end);\n return start === end // fix for IE 10/Win8 and others' subarray returning array of size 1\n ? new this.buf.constructor(0)\n : this._slice.call(this.buf, start, end);\n};\n\n/**\n * Reads a string preceeded by its byte length as a varint.\n * @returns {string} Value read\n */\nReader.prototype.string = function read_string() {\n var bytes = this.bytes();\n return utf8.read(bytes, 0, bytes.length);\n};\n\n/**\n * Skips the specified number of bytes if specified, otherwise skips a varint.\n * @param {number} [length] Length if known, otherwise a varint is assumed\n * @returns {Reader} `this`\n */\nReader.prototype.skip = function skip(length) {\n if (typeof length === \"number\") {\n /* istanbul ignore if */\n if (this.pos + length > this.len)\n throw indexOutOfRange(this, length);\n this.pos += length;\n } else {\n do {\n /* istanbul ignore if */\n if (this.pos >= this.len)\n throw indexOutOfRange(this);\n } while (this.buf[this.pos++] & 128);\n }\n return this;\n};\n\n/**\n * Skips the next element of the specified wire type.\n * @param {number} wireType Wire type received\n * @returns {Reader} `this`\n */\nReader.prototype.skipType = function(wireType) {\n switch (wireType) {\n case 0:\n this.skip();\n break;\n case 1:\n this.skip(8);\n break;\n case 2:\n this.skip(this.uint32());\n break;\n case 3:\n while ((wireType = this.uint32() & 7) !== 4) {\n this.skipType(wireType);\n }\n break;\n case 5:\n this.skip(4);\n break;\n\n /* istanbul ignore next */\n default:\n throw Error(\"invalid wire type \" + wireType + \" at offset \" + this.pos);\n }\n return this;\n};\n\nReader._configure = function(BufferReader_) {\n BufferReader = BufferReader_;\n Reader.create = create();\n BufferReader._configure();\n\n var fn = util.Long ? \"toLong\" : /* istanbul ignore next */ \"toNumber\";\n util.merge(Reader.prototype, {\n\n int64: function read_int64() {\n return readLongVarint.call(this)[fn](false);\n },\n\n uint64: function read_uint64() {\n return readLongVarint.call(this)[fn](true);\n },\n\n sint64: function read_sint64() {\n return readLongVarint.call(this).zzDecode()[fn](false);\n },\n\n fixed64: function read_fixed64() {\n return readFixed64.call(this)[fn](true);\n },\n\n sfixed64: function read_sfixed64() {\n return readFixed64.call(this)[fn](false);\n }\n\n });\n};\n","\"use strict\";\nmodule.exports = BufferReader;\n\n// extends Reader\nvar Reader = require(27);\n(BufferReader.prototype = Object.create(Reader.prototype)).constructor = BufferReader;\n\nvar util = require(39);\n\n/**\n * Constructs a new buffer reader instance.\n * @classdesc Wire format reader using node buffers.\n * @extends Reader\n * @constructor\n * @param {Buffer} buffer Buffer to read from\n */\nfunction BufferReader(buffer) {\n Reader.call(this, buffer);\n\n /**\n * Read buffer.\n * @name BufferReader#buf\n * @type {Buffer}\n */\n}\n\nBufferReader._configure = function () {\n /* istanbul ignore else */\n if (util.Buffer)\n BufferReader.prototype._slice = util.Buffer.prototype.slice;\n};\n\n\n/**\n * @override\n */\nBufferReader.prototype.string = function read_string_buffer() {\n var len = this.uint32(); // modifies pos\n return this.buf.utf8Slice\n ? this.buf.utf8Slice(this.pos, this.pos = Math.min(this.pos + len, this.len))\n : this.buf.toString(\"utf-8\", this.pos, this.pos = Math.min(this.pos + len, this.len));\n};\n\n/**\n * Reads a sequence of bytes preceeded by its length as a varint.\n * @name BufferReader#bytes\n * @function\n * @returns {Buffer} Value read\n */\n\nBufferReader._configure();\n","\"use strict\";\nmodule.exports = Root;\n\n// extends Namespace\nvar Namespace = require(23);\n((Root.prototype = Object.create(Namespace.prototype)).constructor = Root).className = \"Root\";\n\nvar Field = require(16),\n Enum = require(15),\n OneOf = require(25),\n util = require(37);\n\nvar Type, // cyclic\n parse, // might be excluded\n common; // \"\n\n/**\n * Constructs a new root namespace instance.\n * @classdesc Root namespace wrapping all types, enums, services, sub-namespaces etc. that belong together.\n * @extends NamespaceBase\n * @constructor\n * @param {Object.} [options] Top level options\n */\nfunction Root(options) {\n Namespace.call(this, \"\", options);\n\n /**\n * Deferred extension fields.\n * @type {Field[]}\n */\n this.deferred = [];\n\n /**\n * Resolved file names of loaded files.\n * @type {string[]}\n */\n this.files = [];\n}\n\n/**\n * Loads a namespace descriptor into a root namespace.\n * @param {INamespace} json Nameespace descriptor\n * @param {Root} [root] Root namespace, defaults to create a new one if omitted\n * @returns {Root} Root namespace\n */\nRoot.fromJSON = function fromJSON(json, root) {\n if (!root)\n root = new Root();\n if (json.options)\n root.setOptions(json.options);\n return root.addJSON(json.nested);\n};\n\n/**\n * Resolves the path of an imported file, relative to the importing origin.\n * This method exists so you can override it with your own logic in case your imports are scattered over multiple directories.\n * @function\n * @param {string} origin The file name of the importing file\n * @param {string} target The file name being imported\n * @returns {string|null} Resolved path to `target` or `null` to skip the file\n */\nRoot.prototype.resolvePath = util.path.resolve;\n\n/**\n * Fetch content from file path or url\n * This method exists so you can override it with your own logic.\n * @function\n * @param {string} path File path or url\n * @param {FetchCallback} callback Callback function\n * @returns {undefined}\n */\nRoot.prototype.fetch = util.fetch;\n\n// A symbol-like function to safely signal synchronous loading\n/* istanbul ignore next */\nfunction SYNC() {} // eslint-disable-line no-empty-function\n\n/**\n * Loads one or multiple .proto or preprocessed .json files into this root namespace and calls the callback.\n * @param {string|string[]} filename Names of one or multiple files to load\n * @param {IParseOptions} options Parse options\n * @param {LoadCallback} callback Callback function\n * @returns {undefined}\n */\nRoot.prototype.load = function load(filename, options, callback) {\n if (typeof options === \"function\") {\n callback = options;\n options = undefined;\n }\n var self = this;\n if (!callback)\n return util.asPromise(load, self, filename, options);\n\n var sync = callback === SYNC; // undocumented\n\n // Finishes loading by calling the callback (exactly once)\n function finish(err, root) {\n /* istanbul ignore if */\n if (!callback)\n return;\n var cb = callback;\n callback = null;\n if (sync)\n throw err;\n cb(err, root);\n }\n\n // Bundled definition existence checking\n function getBundledFileName(filename) {\n var idx = filename.lastIndexOf(\"google/protobuf/\");\n if (idx > -1) {\n var altname = filename.substring(idx);\n if (altname in common) return altname;\n }\n return null;\n }\n\n // Processes a single file\n function process(filename, source) {\n try {\n if (util.isString(source) && source.charAt(0) === \"{\")\n source = JSON.parse(source);\n if (!util.isString(source))\n self.setOptions(source.options).addJSON(source.nested);\n else {\n parse.filename = filename;\n var parsed = parse(source, self, options),\n resolved,\n i = 0;\n if (parsed.imports)\n for (; i < parsed.imports.length; ++i)\n if (resolved = getBundledFileName(parsed.imports[i]) || self.resolvePath(filename, parsed.imports[i]))\n fetch(resolved);\n if (parsed.weakImports)\n for (i = 0; i < parsed.weakImports.length; ++i)\n if (resolved = getBundledFileName(parsed.weakImports[i]) || self.resolvePath(filename, parsed.weakImports[i]))\n fetch(resolved, true);\n }\n } catch (err) {\n finish(err);\n }\n if (!sync && !queued)\n finish(null, self); // only once anyway\n }\n\n // Fetches a single file\n function fetch(filename, weak) {\n\n // Skip if already loaded / attempted\n if (self.files.indexOf(filename) > -1)\n return;\n self.files.push(filename);\n\n // Shortcut bundled definitions\n if (filename in common) {\n if (sync)\n process(filename, common[filename]);\n else {\n ++queued;\n setTimeout(function() {\n --queued;\n process(filename, common[filename]);\n });\n }\n return;\n }\n\n // Otherwise fetch from disk or network\n if (sync) {\n var source;\n try {\n source = util.fs.readFileSync(filename).toString(\"utf8\");\n } catch (err) {\n if (!weak)\n finish(err);\n return;\n }\n process(filename, source);\n } else {\n ++queued;\n self.fetch(filename, function(err, source) {\n --queued;\n /* istanbul ignore if */\n if (!callback)\n return; // terminated meanwhile\n if (err) {\n /* istanbul ignore else */\n if (!weak)\n finish(err);\n else if (!queued) // can't be covered reliably\n finish(null, self);\n return;\n }\n process(filename, source);\n });\n }\n }\n var queued = 0;\n\n // Assembling the root namespace doesn't require working type\n // references anymore, so we can load everything in parallel\n if (util.isString(filename))\n filename = [ filename ];\n for (var i = 0, resolved; i < filename.length; ++i)\n if (resolved = self.resolvePath(\"\", filename[i]))\n fetch(resolved);\n\n if (sync)\n return self;\n if (!queued)\n finish(null, self);\n return undefined;\n};\n// function load(filename:string, options:IParseOptions, callback:LoadCallback):undefined\n\n/**\n * Loads one or multiple .proto or preprocessed .json files into this root namespace and calls the callback.\n * @function Root#load\n * @param {string|string[]} filename Names of one or multiple files to load\n * @param {LoadCallback} callback Callback function\n * @returns {undefined}\n * @variation 2\n */\n// function load(filename:string, callback:LoadCallback):undefined\n\n/**\n * Loads one or multiple .proto or preprocessed .json files into this root namespace and returns a promise.\n * @function Root#load\n * @param {string|string[]} filename Names of one or multiple files to load\n * @param {IParseOptions} [options] Parse options. Defaults to {@link parse.defaults} when omitted.\n * @returns {Promise} Promise\n * @variation 3\n */\n// function load(filename:string, [options:IParseOptions]):Promise\n\n/**\n * Synchronously loads one or multiple .proto or preprocessed .json files into this root namespace (node only).\n * @function Root#loadSync\n * @param {string|string[]} filename Names of one or multiple files to load\n * @param {IParseOptions} [options] Parse options. Defaults to {@link parse.defaults} when omitted.\n * @returns {Root} Root namespace\n * @throws {Error} If synchronous fetching is not supported (i.e. in browsers) or if a file's syntax is invalid\n */\nRoot.prototype.loadSync = function loadSync(filename, options) {\n if (!util.isNode)\n throw Error(\"not supported\");\n return this.load(filename, options, SYNC);\n};\n\n/**\n * @override\n */\nRoot.prototype.resolveAll = function resolveAll() {\n if (this.deferred.length)\n throw Error(\"unresolvable extensions: \" + this.deferred.map(function(field) {\n return \"'extend \" + field.extend + \"' in \" + field.parent.fullName;\n }).join(\", \"));\n return Namespace.prototype.resolveAll.call(this);\n};\n\n// only uppercased (and thus conflict-free) children are exposed, see below\nvar exposeRe = /^[A-Z]/;\n\n/**\n * Handles a deferred declaring extension field by creating a sister field to represent it within its extended type.\n * @param {Root} root Root instance\n * @param {Field} field Declaring extension field witin the declaring type\n * @returns {boolean} `true` if successfully added to the extended type, `false` otherwise\n * @inner\n * @ignore\n */\nfunction tryHandleExtension(root, field) {\n var extendedType = field.parent.lookup(field.extend);\n if (extendedType) {\n var sisterField = new Field(field.fullName, field.id, field.type, field.rule, undefined, field.options);\n sisterField.declaringField = field;\n field.extensionField = sisterField;\n extendedType.add(sisterField);\n return true;\n }\n return false;\n}\n\n/**\n * Called when any object is added to this root or its sub-namespaces.\n * @param {ReflectionObject} object Object added\n * @returns {undefined}\n * @private\n */\nRoot.prototype._handleAdd = function _handleAdd(object) {\n if (object instanceof Field) {\n\n if (/* an extension field (implies not part of a oneof) */ object.extend !== undefined && /* not already handled */ !object.extensionField)\n if (!tryHandleExtension(this, object))\n this.deferred.push(object);\n\n } else if (object instanceof Enum) {\n\n if (exposeRe.test(object.name))\n object.parent[object.name] = object.values; // expose enum values as property of its parent\n\n } else if (!(object instanceof OneOf)) /* everything else is a namespace */ {\n\n if (object instanceof Type) // Try to handle any deferred extensions\n for (var i = 0; i < this.deferred.length;)\n if (tryHandleExtension(this, this.deferred[i]))\n this.deferred.splice(i, 1);\n else\n ++i;\n for (var j = 0; j < /* initializes */ object.nestedArray.length; ++j) // recurse into the namespace\n this._handleAdd(object._nestedArray[j]);\n if (exposeRe.test(object.name))\n object.parent[object.name] = object; // expose namespace as property of its parent\n }\n\n // The above also adds uppercased (and thus conflict-free) nested types, services and enums as\n // properties of namespaces just like static code does. This allows using a .d.ts generated for\n // a static module with reflection-based solutions where the condition is met.\n};\n\n/**\n * Called when any object is removed from this root or its sub-namespaces.\n * @param {ReflectionObject} object Object removed\n * @returns {undefined}\n * @private\n */\nRoot.prototype._handleRemove = function _handleRemove(object) {\n if (object instanceof Field) {\n\n if (/* an extension field */ object.extend !== undefined) {\n if (/* already handled */ object.extensionField) { // remove its sister field\n object.extensionField.parent.remove(object.extensionField);\n object.extensionField = null;\n } else { // cancel the extension\n var index = this.deferred.indexOf(object);\n /* istanbul ignore else */\n if (index > -1)\n this.deferred.splice(index, 1);\n }\n }\n\n } else if (object instanceof Enum) {\n\n if (exposeRe.test(object.name))\n delete object.parent[object.name]; // unexpose enum values\n\n } else if (object instanceof Namespace) {\n\n for (var i = 0; i < /* initializes */ object.nestedArray.length; ++i) // recurse into the namespace\n this._handleRemove(object._nestedArray[i]);\n\n if (exposeRe.test(object.name))\n delete object.parent[object.name]; // unexpose namespaces\n\n }\n};\n\n// Sets up cyclic dependencies (called in index-light)\nRoot._configure = function(Type_, parse_, common_) {\n Type = Type_;\n parse = parse_;\n common = common_;\n};\n","\"use strict\";\nmodule.exports = {};\n\n/**\n * Named roots.\n * This is where pbjs stores generated structures (the option `-r, --root` specifies a name).\n * Can also be used manually to make roots available accross modules.\n * @name roots\n * @type {Object.}\n * @example\n * // pbjs -r myroot -o compiled.js ...\n *\n * // in another module:\n * require(\"./compiled.js\");\n *\n * // in any subsequent module:\n * var root = protobuf.roots[\"myroot\"];\n */\n","\"use strict\";\n\n/**\n * Streaming RPC helpers.\n * @namespace\n */\nvar rpc = exports;\n\n/**\n * RPC implementation passed to {@link Service#create} performing a service request on network level, i.e. by utilizing http requests or websockets.\n * @typedef RPCImpl\n * @type {function}\n * @param {Method|rpc.ServiceMethod,Message<{}>>} method Reflected or static method being called\n * @param {Uint8Array} requestData Request data\n * @param {RPCImplCallback} callback Callback function\n * @returns {undefined}\n * @example\n * function rpcImpl(method, requestData, callback) {\n * if (protobuf.util.lcFirst(method.name) !== \"myMethod\") // compatible with static code\n * throw Error(\"no such method\");\n * asynchronouslyObtainAResponse(requestData, function(err, responseData) {\n * callback(err, responseData);\n * });\n * }\n */\n\n/**\n * Node-style callback as used by {@link RPCImpl}.\n * @typedef RPCImplCallback\n * @type {function}\n * @param {Error|null} error Error, if any, otherwise `null`\n * @param {Uint8Array|null} [response] Response data or `null` to signal end of stream, if there hasn't been an error\n * @returns {undefined}\n */\n\nrpc.Service = require(32);\n","\"use strict\";\nmodule.exports = Service;\n\nvar util = require(39);\n\n// Extends EventEmitter\n(Service.prototype = Object.create(util.EventEmitter.prototype)).constructor = Service;\n\n/**\n * A service method callback as used by {@link rpc.ServiceMethod|ServiceMethod}.\n *\n * Differs from {@link RPCImplCallback} in that it is an actual callback of a service method which may not return `response = null`.\n * @typedef rpc.ServiceMethodCallback\n * @template TRes extends Message\n * @type {function}\n * @param {Error|null} error Error, if any\n * @param {TRes} [response] Response message\n * @returns {undefined}\n */\n\n/**\n * A service method part of a {@link rpc.Service} as created by {@link Service.create}.\n * @typedef rpc.ServiceMethod\n * @template TReq extends Message\n * @template TRes extends Message\n * @type {function}\n * @param {TReq|Properties} request Request message or plain object\n * @param {rpc.ServiceMethodCallback} [callback] Node-style callback called with the error, if any, and the response message\n * @returns {Promise>} Promise if `callback` has been omitted, otherwise `undefined`\n */\n\n/**\n * Constructs a new RPC service instance.\n * @classdesc An RPC service as returned by {@link Service#create}.\n * @exports rpc.Service\n * @extends util.EventEmitter\n * @constructor\n * @param {RPCImpl} rpcImpl RPC implementation\n * @param {boolean} [requestDelimited=false] Whether requests are length-delimited\n * @param {boolean} [responseDelimited=false] Whether responses are length-delimited\n */\nfunction Service(rpcImpl, requestDelimited, responseDelimited) {\n\n if (typeof rpcImpl !== \"function\")\n throw TypeError(\"rpcImpl must be a function\");\n\n util.EventEmitter.call(this);\n\n /**\n * RPC implementation. Becomes `null` once the service is ended.\n * @type {RPCImpl|null}\n */\n this.rpcImpl = rpcImpl;\n\n /**\n * Whether requests are length-delimited.\n * @type {boolean}\n */\n this.requestDelimited = Boolean(requestDelimited);\n\n /**\n * Whether responses are length-delimited.\n * @type {boolean}\n */\n this.responseDelimited = Boolean(responseDelimited);\n}\n\n/**\n * Calls a service method through {@link rpc.Service#rpcImpl|rpcImpl}.\n * @param {Method|rpc.ServiceMethod} method Reflected or static method\n * @param {Constructor} requestCtor Request constructor\n * @param {Constructor} responseCtor Response constructor\n * @param {TReq|Properties} request Request message or plain object\n * @param {rpc.ServiceMethodCallback} callback Service callback\n * @returns {undefined}\n * @template TReq extends Message\n * @template TRes extends Message\n */\nService.prototype.rpcCall = function rpcCall(method, requestCtor, responseCtor, request, callback) {\n\n if (!request)\n throw TypeError(\"request must be specified\");\n\n var self = this;\n if (!callback)\n return util.asPromise(rpcCall, self, method, requestCtor, responseCtor, request);\n\n if (!self.rpcImpl) {\n setTimeout(function() { callback(Error(\"already ended\")); }, 0);\n return undefined;\n }\n\n try {\n return self.rpcImpl(\n method,\n requestCtor[self.requestDelimited ? \"encodeDelimited\" : \"encode\"](request).finish(),\n function rpcCallback(err, response) {\n\n if (err) {\n self.emit(\"error\", err, method);\n return callback(err);\n }\n\n if (response === null) {\n self.end(/* endedByRPC */ true);\n return undefined;\n }\n\n if (!(response instanceof responseCtor)) {\n try {\n response = responseCtor[self.responseDelimited ? \"decodeDelimited\" : \"decode\"](response);\n } catch (err) {\n self.emit(\"error\", err, method);\n return callback(err);\n }\n }\n\n self.emit(\"data\", response, method);\n return callback(null, response);\n }\n );\n } catch (err) {\n self.emit(\"error\", err, method);\n setTimeout(function() { callback(err); }, 0);\n return undefined;\n }\n};\n\n/**\n * Ends this service and emits the `end` event.\n * @param {boolean} [endedByRPC=false] Whether the service has been ended by the RPC implementation.\n * @returns {rpc.Service} `this`\n */\nService.prototype.end = function end(endedByRPC) {\n if (this.rpcImpl) {\n if (!endedByRPC) // signal end to rpcImpl\n this.rpcImpl(null, null, null);\n this.rpcImpl = null;\n this.emit(\"end\").off();\n }\n return this;\n};\n","\"use strict\";\nmodule.exports = Service;\n\n// extends Namespace\nvar Namespace = require(23);\n((Service.prototype = Object.create(Namespace.prototype)).constructor = Service).className = \"Service\";\n\nvar Method = require(22),\n util = require(37),\n rpc = require(31);\n\n/**\n * Constructs a new service instance.\n * @classdesc Reflected service.\n * @extends NamespaceBase\n * @constructor\n * @param {string} name Service name\n * @param {Object.} [options] Service options\n * @throws {TypeError} If arguments are invalid\n */\nfunction Service(name, options) {\n Namespace.call(this, name, options);\n\n /**\n * Service methods.\n * @type {Object.}\n */\n this.methods = {}; // toJSON, marker\n\n /**\n * Cached methods as an array.\n * @type {Method[]|null}\n * @private\n */\n this._methodsArray = null;\n}\n\n/**\n * Service descriptor.\n * @interface IService\n * @extends INamespace\n * @property {Object.} methods Method descriptors\n */\n\n/**\n * Constructs a service from a service descriptor.\n * @param {string} name Service name\n * @param {IService} json Service descriptor\n * @returns {Service} Created service\n * @throws {TypeError} If arguments are invalid\n */\nService.fromJSON = function fromJSON(name, json) {\n var service = new Service(name, json.options);\n /* istanbul ignore else */\n if (json.methods)\n for (var names = Object.keys(json.methods), i = 0; i < names.length; ++i)\n service.add(Method.fromJSON(names[i], json.methods[names[i]]));\n if (json.nested)\n service.addJSON(json.nested);\n service.comment = json.comment;\n return service;\n};\n\n/**\n * Converts this service to a service descriptor.\n * @param {IToJSONOptions} [toJSONOptions] JSON conversion options\n * @returns {IService} Service descriptor\n */\nService.prototype.toJSON = function toJSON(toJSONOptions) {\n var inherited = Namespace.prototype.toJSON.call(this, toJSONOptions);\n var keepComments = toJSONOptions ? Boolean(toJSONOptions.keepComments) : false;\n return util.toObject([\n \"options\" , inherited && inherited.options || undefined,\n \"methods\" , Namespace.arrayToJSON(this.methodsArray, toJSONOptions) || /* istanbul ignore next */ {},\n \"nested\" , inherited && inherited.nested || undefined,\n \"comment\" , keepComments ? this.comment : undefined\n ]);\n};\n\n/**\n * Methods of this service as an array for iteration.\n * @name Service#methodsArray\n * @type {Method[]}\n * @readonly\n */\nObject.defineProperty(Service.prototype, \"methodsArray\", {\n get: function() {\n return this._methodsArray || (this._methodsArray = util.toArray(this.methods));\n }\n});\n\nfunction clearCache(service) {\n service._methodsArray = null;\n return service;\n}\n\n/**\n * @override\n */\nService.prototype.get = function get(name) {\n return this.methods[name]\n || Namespace.prototype.get.call(this, name);\n};\n\n/**\n * @override\n */\nService.prototype.resolveAll = function resolveAll() {\n var methods = this.methodsArray;\n for (var i = 0; i < methods.length; ++i)\n methods[i].resolve();\n return Namespace.prototype.resolve.call(this);\n};\n\n/**\n * @override\n */\nService.prototype.add = function add(object) {\n\n /* istanbul ignore if */\n if (this.get(object.name))\n throw Error(\"duplicate name '\" + object.name + \"' in \" + this);\n\n if (object instanceof Method) {\n this.methods[object.name] = object;\n object.parent = this;\n return clearCache(this);\n }\n return Namespace.prototype.add.call(this, object);\n};\n\n/**\n * @override\n */\nService.prototype.remove = function remove(object) {\n if (object instanceof Method) {\n\n /* istanbul ignore if */\n if (this.methods[object.name] !== object)\n throw Error(object + \" is not a member of \" + this);\n\n delete this.methods[object.name];\n object.parent = null;\n return clearCache(this);\n }\n return Namespace.prototype.remove.call(this, object);\n};\n\n/**\n * Creates a runtime service using the specified rpc implementation.\n * @param {RPCImpl} rpcImpl RPC implementation\n * @param {boolean} [requestDelimited=false] Whether requests are length-delimited\n * @param {boolean} [responseDelimited=false] Whether responses are length-delimited\n * @returns {rpc.Service} RPC service. Useful where requests and/or responses are streamed.\n */\nService.prototype.create = function create(rpcImpl, requestDelimited, responseDelimited) {\n var rpcService = new rpc.Service(rpcImpl, requestDelimited, responseDelimited);\n for (var i = 0, method; i < /* initializes */ this.methodsArray.length; ++i) {\n var methodName = util.lcFirst((method = this._methodsArray[i]).resolve().name).replace(/[^$\\w_]/g, \"\");\n rpcService[methodName] = util.codegen([\"r\",\"c\"], util.isReserved(methodName) ? methodName + \"_\" : methodName)(\"return this.rpcCall(m,q,s,r,c)\")({\n m: method,\n q: method.resolvedRequestType.ctor,\n s: method.resolvedResponseType.ctor\n });\n }\n return rpcService;\n};\n","\"use strict\";\nmodule.exports = tokenize;\n\nvar delimRe = /[\\s{}=;:[\\],'\"()<>]/g,\n stringDoubleRe = /(?:\"([^\"\\\\]*(?:\\\\.[^\"\\\\]*)*)\")/g,\n stringSingleRe = /(?:'([^'\\\\]*(?:\\\\.[^'\\\\]*)*)')/g;\n\nvar setCommentRe = /^ *[*/]+ */,\n setCommentAltRe = /^\\s*\\*?\\/*/,\n setCommentSplitRe = /\\n/g,\n whitespaceRe = /\\s/,\n unescapeRe = /\\\\(.?)/g;\n\nvar unescapeMap = {\n \"0\": \"\\0\",\n \"r\": \"\\r\",\n \"n\": \"\\n\",\n \"t\": \"\\t\"\n};\n\n/**\n * Unescapes a string.\n * @param {string} str String to unescape\n * @returns {string} Unescaped string\n * @property {Object.} map Special characters map\n * @memberof tokenize\n */\nfunction unescape(str) {\n return str.replace(unescapeRe, function($0, $1) {\n switch ($1) {\n case \"\\\\\":\n case \"\":\n return $1;\n default:\n return unescapeMap[$1] || \"\";\n }\n });\n}\n\ntokenize.unescape = unescape;\n\n/**\n * Gets the next token and advances.\n * @typedef TokenizerHandleNext\n * @type {function}\n * @returns {string|null} Next token or `null` on eof\n */\n\n/**\n * Peeks for the next token.\n * @typedef TokenizerHandlePeek\n * @type {function}\n * @returns {string|null} Next token or `null` on eof\n */\n\n/**\n * Pushes a token back to the stack.\n * @typedef TokenizerHandlePush\n * @type {function}\n * @param {string} token Token\n * @returns {undefined}\n */\n\n/**\n * Skips the next token.\n * @typedef TokenizerHandleSkip\n * @type {function}\n * @param {string} expected Expected token\n * @param {boolean} [optional=false] If optional\n * @returns {boolean} Whether the token matched\n * @throws {Error} If the token didn't match and is not optional\n */\n\n/**\n * Gets the comment on the previous line or, alternatively, the line comment on the specified line.\n * @typedef TokenizerHandleCmnt\n * @type {function}\n * @param {number} [line] Line number\n * @returns {string|null} Comment text or `null` if none\n */\n\n/**\n * Handle object returned from {@link tokenize}.\n * @interface ITokenizerHandle\n * @property {TokenizerHandleNext} next Gets the next token and advances (`null` on eof)\n * @property {TokenizerHandlePeek} peek Peeks for the next token (`null` on eof)\n * @property {TokenizerHandlePush} push Pushes a token back to the stack\n * @property {TokenizerHandleSkip} skip Skips a token, returns its presence and advances or, if non-optional and not present, throws\n * @property {TokenizerHandleCmnt} cmnt Gets the comment on the previous line or the line comment on the specified line, if any\n * @property {number} line Current line number\n */\n\n/**\n * Tokenizes the given .proto source and returns an object with useful utility functions.\n * @param {string} source Source contents\n * @param {boolean} alternateCommentMode Whether we should activate alternate comment parsing mode.\n * @returns {ITokenizerHandle} Tokenizer handle\n */\nfunction tokenize(source, alternateCommentMode) {\n /* eslint-disable callback-return */\n source = source.toString();\n\n var offset = 0,\n length = source.length,\n line = 1,\n commentType = null,\n commentText = null,\n commentLine = 0,\n commentLineEmpty = false,\n commentIsLeading = false;\n\n var stack = [];\n\n var stringDelim = null;\n\n /* istanbul ignore next */\n /**\n * Creates an error for illegal syntax.\n * @param {string} subject Subject\n * @returns {Error} Error created\n * @inner\n */\n function illegal(subject) {\n return Error(\"illegal \" + subject + \" (line \" + line + \")\");\n }\n\n /**\n * Reads a string till its end.\n * @returns {string} String read\n * @inner\n */\n function readString() {\n var re = stringDelim === \"'\" ? stringSingleRe : stringDoubleRe;\n re.lastIndex = offset - 1;\n var match = re.exec(source);\n if (!match)\n throw illegal(\"string\");\n offset = re.lastIndex;\n push(stringDelim);\n stringDelim = null;\n return unescape(match[1]);\n }\n\n /**\n * Gets the character at `pos` within the source.\n * @param {number} pos Position\n * @returns {string} Character\n * @inner\n */\n function charAt(pos) {\n return source.charAt(pos);\n }\n\n /**\n * Sets the current comment text.\n * @param {number} start Start offset\n * @param {number} end End offset\n * @param {boolean} isLeading set if a leading comment\n * @returns {undefined}\n * @inner\n */\n function setComment(start, end, isLeading) {\n commentType = source.charAt(start++);\n commentLine = line;\n commentLineEmpty = false;\n commentIsLeading = isLeading;\n var lookback;\n if (alternateCommentMode) {\n lookback = 2; // alternate comment parsing: \"//\" or \"/*\"\n } else {\n lookback = 3; // \"///\" or \"/**\"\n }\n var commentOffset = start - lookback,\n c;\n do {\n if (--commentOffset < 0 ||\n (c = source.charAt(commentOffset)) === \"\\n\") {\n commentLineEmpty = true;\n break;\n }\n } while (c === \" \" || c === \"\\t\");\n var lines = source\n .substring(start, end)\n .split(setCommentSplitRe);\n for (var i = 0; i < lines.length; ++i)\n lines[i] = lines[i]\n .replace(alternateCommentMode ? setCommentAltRe : setCommentRe, \"\")\n .trim();\n commentText = lines\n .join(\"\\n\")\n .trim();\n }\n\n function isDoubleSlashCommentLine(startOffset) {\n var endOffset = findEndOfLine(startOffset);\n\n // see if remaining line matches comment pattern\n var lineText = source.substring(startOffset, endOffset);\n // look for 1 or 2 slashes since startOffset would already point past\n // the first slash that started the comment.\n var isComment = /^\\s*\\/{1,2}/.test(lineText);\n return isComment;\n }\n\n function findEndOfLine(cursor) {\n // find end of cursor's line\n var endOffset = cursor;\n while (endOffset < length && charAt(endOffset) !== \"\\n\") {\n endOffset++;\n }\n return endOffset;\n }\n\n /**\n * Obtains the next token.\n * @returns {string|null} Next token or `null` on eof\n * @inner\n */\n function next() {\n if (stack.length > 0)\n return stack.shift();\n if (stringDelim)\n return readString();\n var repeat,\n prev,\n curr,\n start,\n isDoc,\n isLeadingComment = offset === 0;\n do {\n if (offset === length)\n return null;\n repeat = false;\n while (whitespaceRe.test(curr = charAt(offset))) {\n if (curr === \"\\n\") {\n isLeadingComment = true;\n ++line;\n }\n if (++offset === length)\n return null;\n }\n\n if (charAt(offset) === \"/\") {\n if (++offset === length) {\n throw illegal(\"comment\");\n }\n if (charAt(offset) === \"/\") { // Line\n if (!alternateCommentMode) {\n // check for triple-slash comment\n isDoc = charAt(start = offset + 1) === \"/\";\n\n while (charAt(++offset) !== \"\\n\") {\n if (offset === length) {\n return null;\n }\n }\n ++offset;\n if (isDoc) {\n setComment(start, offset - 1, isLeadingComment);\n }\n ++line;\n repeat = true;\n } else {\n // check for double-slash comments, consolidating consecutive lines\n start = offset;\n isDoc = false;\n if (isDoubleSlashCommentLine(offset)) {\n isDoc = true;\n do {\n offset = findEndOfLine(offset);\n if (offset === length) {\n break;\n }\n offset++;\n } while (isDoubleSlashCommentLine(offset));\n } else {\n offset = Math.min(length, findEndOfLine(offset) + 1);\n }\n if (isDoc) {\n setComment(start, offset, isLeadingComment);\n }\n line++;\n repeat = true;\n }\n } else if ((curr = charAt(offset)) === \"*\") { /* Block */\n // check for /** (regular comment mode) or /* (alternate comment mode)\n start = offset + 1;\n isDoc = alternateCommentMode || charAt(start) === \"*\";\n do {\n if (curr === \"\\n\") {\n ++line;\n }\n if (++offset === length) {\n throw illegal(\"comment\");\n }\n prev = curr;\n curr = charAt(offset);\n } while (prev !== \"*\" || curr !== \"/\");\n ++offset;\n if (isDoc) {\n setComment(start, offset - 2, isLeadingComment);\n }\n repeat = true;\n } else {\n return \"/\";\n }\n }\n } while (repeat);\n\n // offset !== length if we got here\n\n var end = offset;\n delimRe.lastIndex = 0;\n var delim = delimRe.test(charAt(end++));\n if (!delim)\n while (end < length && !delimRe.test(charAt(end)))\n ++end;\n var token = source.substring(offset, offset = end);\n if (token === \"\\\"\" || token === \"'\")\n stringDelim = token;\n return token;\n }\n\n /**\n * Pushes a token back to the stack.\n * @param {string} token Token\n * @returns {undefined}\n * @inner\n */\n function push(token) {\n stack.push(token);\n }\n\n /**\n * Peeks for the next token.\n * @returns {string|null} Token or `null` on eof\n * @inner\n */\n function peek() {\n if (!stack.length) {\n var token = next();\n if (token === null)\n return null;\n push(token);\n }\n return stack[0];\n }\n\n /**\n * Skips a token.\n * @param {string} expected Expected token\n * @param {boolean} [optional=false] Whether the token is optional\n * @returns {boolean} `true` when skipped, `false` if not\n * @throws {Error} When a required token is not present\n * @inner\n */\n function skip(expected, optional) {\n var actual = peek(),\n equals = actual === expected;\n if (equals) {\n next();\n return true;\n }\n if (!optional)\n throw illegal(\"token '\" + actual + \"', '\" + expected + \"' expected\");\n return false;\n }\n\n /**\n * Gets a comment.\n * @param {number} [trailingLine] Line number if looking for a trailing comment\n * @returns {string|null} Comment text\n * @inner\n */\n function cmnt(trailingLine) {\n var ret = null;\n if (trailingLine === undefined) {\n if (commentLine === line - 1 && (alternateCommentMode || commentType === \"*\" || commentLineEmpty)) {\n ret = commentIsLeading ? commentText : null;\n }\n } else {\n /* istanbul ignore else */\n if (commentLine < trailingLine) {\n peek();\n }\n if (commentLine === trailingLine && !commentLineEmpty && (alternateCommentMode || commentType === \"/\")) {\n ret = commentIsLeading ? null : commentText;\n }\n }\n return ret;\n }\n\n return Object.defineProperty({\n next: next,\n peek: peek,\n push: push,\n skip: skip,\n cmnt: cmnt\n }, \"line\", {\n get: function() { return line; }\n });\n /* eslint-enable callback-return */\n}\n","\"use strict\";\nmodule.exports = Type;\n\n// extends Namespace\nvar Namespace = require(23);\n((Type.prototype = Object.create(Namespace.prototype)).constructor = Type).className = \"Type\";\n\nvar Enum = require(15),\n OneOf = require(25),\n Field = require(16),\n MapField = require(20),\n Service = require(33),\n Message = require(21),\n Reader = require(27),\n Writer = require(42),\n util = require(37),\n encoder = require(14),\n decoder = require(13),\n verifier = require(40),\n converter = require(12),\n wrappers = require(41);\n\n/**\n * Constructs a new reflected message type instance.\n * @classdesc Reflected message type.\n * @extends NamespaceBase\n * @constructor\n * @param {string} name Message name\n * @param {Object.} [options] Declared options\n */\nfunction Type(name, options) {\n Namespace.call(this, name, options);\n\n /**\n * Message fields.\n * @type {Object.}\n */\n this.fields = {}; // toJSON, marker\n\n /**\n * Oneofs declared within this namespace, if any.\n * @type {Object.}\n */\n this.oneofs = undefined; // toJSON\n\n /**\n * Extension ranges, if any.\n * @type {number[][]}\n */\n this.extensions = undefined; // toJSON\n\n /**\n * Reserved ranges, if any.\n * @type {Array.}\n */\n this.reserved = undefined; // toJSON\n\n /*?\n * Whether this type is a legacy group.\n * @type {boolean|undefined}\n */\n this.group = undefined; // toJSON\n\n /**\n * Cached fields by id.\n * @type {Object.|null}\n * @private\n */\n this._fieldsById = null;\n\n /**\n * Cached fields as an array.\n * @type {Field[]|null}\n * @private\n */\n this._fieldsArray = null;\n\n /**\n * Cached oneofs as an array.\n * @type {OneOf[]|null}\n * @private\n */\n this._oneofsArray = null;\n\n /**\n * Cached constructor.\n * @type {Constructor<{}>}\n * @private\n */\n this._ctor = null;\n}\n\nObject.defineProperties(Type.prototype, {\n\n /**\n * Message fields by id.\n * @name Type#fieldsById\n * @type {Object.}\n * @readonly\n */\n fieldsById: {\n get: function() {\n\n /* istanbul ignore if */\n if (this._fieldsById)\n return this._fieldsById;\n\n this._fieldsById = {};\n for (var names = Object.keys(this.fields), i = 0; i < names.length; ++i) {\n var field = this.fields[names[i]],\n id = field.id;\n\n /* istanbul ignore if */\n if (this._fieldsById[id])\n throw Error(\"duplicate id \" + id + \" in \" + this);\n\n this._fieldsById[id] = field;\n }\n return this._fieldsById;\n }\n },\n\n /**\n * Fields of this message as an array for iteration.\n * @name Type#fieldsArray\n * @type {Field[]}\n * @readonly\n */\n fieldsArray: {\n get: function() {\n return this._fieldsArray || (this._fieldsArray = util.toArray(this.fields));\n }\n },\n\n /**\n * Oneofs of this message as an array for iteration.\n * @name Type#oneofsArray\n * @type {OneOf[]}\n * @readonly\n */\n oneofsArray: {\n get: function() {\n return this._oneofsArray || (this._oneofsArray = util.toArray(this.oneofs));\n }\n },\n\n /**\n * The registered constructor, if any registered, otherwise a generic constructor.\n * Assigning a function replaces the internal constructor. If the function does not extend {@link Message} yet, its prototype will be setup accordingly and static methods will be populated. If it already extends {@link Message}, it will just replace the internal constructor.\n * @name Type#ctor\n * @type {Constructor<{}>}\n */\n ctor: {\n get: function() {\n return this._ctor || (this.ctor = Type.generateConstructor(this)());\n },\n set: function(ctor) {\n\n // Ensure proper prototype\n var prototype = ctor.prototype;\n if (!(prototype instanceof Message)) {\n (ctor.prototype = new Message()).constructor = ctor;\n util.merge(ctor.prototype, prototype);\n }\n\n // Classes and messages reference their reflected type\n ctor.$type = ctor.prototype.$type = this;\n\n // Mix in static methods\n util.merge(ctor, Message, true);\n\n this._ctor = ctor;\n\n // Messages have non-enumerable default values on their prototype\n var i = 0;\n for (; i < /* initializes */ this.fieldsArray.length; ++i)\n this._fieldsArray[i].resolve(); // ensures a proper value\n\n // Messages have non-enumerable getters and setters for each virtual oneof field\n var ctorProperties = {};\n for (i = 0; i < /* initializes */ this.oneofsArray.length; ++i)\n ctorProperties[this._oneofsArray[i].resolve().name] = {\n get: util.oneOfGetter(this._oneofsArray[i].oneof),\n set: util.oneOfSetter(this._oneofsArray[i].oneof)\n };\n if (i)\n Object.defineProperties(ctor.prototype, ctorProperties);\n }\n }\n});\n\n/**\n * Generates a constructor function for the specified type.\n * @param {Type} mtype Message type\n * @returns {Codegen} Codegen instance\n */\nType.generateConstructor = function generateConstructor(mtype) {\n /* eslint-disable no-unexpected-multiline */\n var gen = util.codegen([\"p\"], mtype.name);\n // explicitly initialize mutable object/array fields so that these aren't just inherited from the prototype\n for (var i = 0, field; i < mtype.fieldsArray.length; ++i)\n if ((field = mtype._fieldsArray[i]).map) gen\n (\"this%s={}\", util.safeProp(field.name));\n else if (field.repeated) gen\n (\"this%s=[]\", util.safeProp(field.name));\n return gen\n (\"if(p)for(var ks=Object.keys(p),i=0;i} [oneofs] Oneof descriptors\n * @property {Object.} fields Field descriptors\n * @property {number[][]} [extensions] Extension ranges\n * @property {number[][]} [reserved] Reserved ranges\n * @property {boolean} [group=false] Whether a legacy group or not\n */\n\n/**\n * Creates a message type from a message type descriptor.\n * @param {string} name Message name\n * @param {IType} json Message type descriptor\n * @returns {Type} Created message type\n */\nType.fromJSON = function fromJSON(name, json) {\n var type = new Type(name, json.options);\n type.extensions = json.extensions;\n type.reserved = json.reserved;\n var names = Object.keys(json.fields),\n i = 0;\n for (; i < names.length; ++i)\n type.add(\n ( typeof json.fields[names[i]].keyType !== \"undefined\"\n ? MapField.fromJSON\n : Field.fromJSON )(names[i], json.fields[names[i]])\n );\n if (json.oneofs)\n for (names = Object.keys(json.oneofs), i = 0; i < names.length; ++i)\n type.add(OneOf.fromJSON(names[i], json.oneofs[names[i]]));\n if (json.nested)\n for (names = Object.keys(json.nested), i = 0; i < names.length; ++i) {\n var nested = json.nested[names[i]];\n type.add( // most to least likely\n ( nested.id !== undefined\n ? Field.fromJSON\n : nested.fields !== undefined\n ? Type.fromJSON\n : nested.values !== undefined\n ? Enum.fromJSON\n : nested.methods !== undefined\n ? Service.fromJSON\n : Namespace.fromJSON )(names[i], nested)\n );\n }\n if (json.extensions && json.extensions.length)\n type.extensions = json.extensions;\n if (json.reserved && json.reserved.length)\n type.reserved = json.reserved;\n if (json.group)\n type.group = true;\n if (json.comment)\n type.comment = json.comment;\n return type;\n};\n\n/**\n * Converts this message type to a message type descriptor.\n * @param {IToJSONOptions} [toJSONOptions] JSON conversion options\n * @returns {IType} Message type descriptor\n */\nType.prototype.toJSON = function toJSON(toJSONOptions) {\n var inherited = Namespace.prototype.toJSON.call(this, toJSONOptions);\n var keepComments = toJSONOptions ? Boolean(toJSONOptions.keepComments) : false;\n return util.toObject([\n \"options\" , inherited && inherited.options || undefined,\n \"oneofs\" , Namespace.arrayToJSON(this.oneofsArray, toJSONOptions),\n \"fields\" , Namespace.arrayToJSON(this.fieldsArray.filter(function(obj) { return !obj.declaringField; }), toJSONOptions) || {},\n \"extensions\" , this.extensions && this.extensions.length ? this.extensions : undefined,\n \"reserved\" , this.reserved && this.reserved.length ? this.reserved : undefined,\n \"group\" , this.group || undefined,\n \"nested\" , inherited && inherited.nested || undefined,\n \"comment\" , keepComments ? this.comment : undefined\n ]);\n};\n\n/**\n * @override\n */\nType.prototype.resolveAll = function resolveAll() {\n var fields = this.fieldsArray, i = 0;\n while (i < fields.length)\n fields[i++].resolve();\n var oneofs = this.oneofsArray; i = 0;\n while (i < oneofs.length)\n oneofs[i++].resolve();\n return Namespace.prototype.resolveAll.call(this);\n};\n\n/**\n * @override\n */\nType.prototype.get = function get(name) {\n return this.fields[name]\n || this.oneofs && this.oneofs[name]\n || this.nested && this.nested[name]\n || null;\n};\n\n/**\n * Adds a nested object to this type.\n * @param {ReflectionObject} object Nested object to add\n * @returns {Type} `this`\n * @throws {TypeError} If arguments are invalid\n * @throws {Error} If there is already a nested object with this name or, if a field, when there is already a field with this id\n */\nType.prototype.add = function add(object) {\n\n if (this.get(object.name))\n throw Error(\"duplicate name '\" + object.name + \"' in \" + this);\n\n if (object instanceof Field && object.extend === undefined) {\n // NOTE: Extension fields aren't actual fields on the declaring type, but nested objects.\n // The root object takes care of adding distinct sister-fields to the respective extended\n // type instead.\n\n // avoids calling the getter if not absolutely necessary because it's called quite frequently\n if (this._fieldsById ? /* istanbul ignore next */ this._fieldsById[object.id] : this.fieldsById[object.id])\n throw Error(\"duplicate id \" + object.id + \" in \" + this);\n if (this.isReservedId(object.id))\n throw Error(\"id \" + object.id + \" is reserved in \" + this);\n if (this.isReservedName(object.name))\n throw Error(\"name '\" + object.name + \"' is reserved in \" + this);\n\n if (object.parent)\n object.parent.remove(object);\n this.fields[object.name] = object;\n object.message = this;\n object.onAdd(this);\n return clearCache(this);\n }\n if (object instanceof OneOf) {\n if (!this.oneofs)\n this.oneofs = {};\n this.oneofs[object.name] = object;\n object.onAdd(this);\n return clearCache(this);\n }\n return Namespace.prototype.add.call(this, object);\n};\n\n/**\n * Removes a nested object from this type.\n * @param {ReflectionObject} object Nested object to remove\n * @returns {Type} `this`\n * @throws {TypeError} If arguments are invalid\n * @throws {Error} If `object` is not a member of this type\n */\nType.prototype.remove = function remove(object) {\n if (object instanceof Field && object.extend === undefined) {\n // See Type#add for the reason why extension fields are excluded here.\n\n /* istanbul ignore if */\n if (!this.fields || this.fields[object.name] !== object)\n throw Error(object + \" is not a member of \" + this);\n\n delete this.fields[object.name];\n object.parent = null;\n object.onRemove(this);\n return clearCache(this);\n }\n if (object instanceof OneOf) {\n\n /* istanbul ignore if */\n if (!this.oneofs || this.oneofs[object.name] !== object)\n throw Error(object + \" is not a member of \" + this);\n\n delete this.oneofs[object.name];\n object.parent = null;\n object.onRemove(this);\n return clearCache(this);\n }\n return Namespace.prototype.remove.call(this, object);\n};\n\n/**\n * Tests if the specified id is reserved.\n * @param {number} id Id to test\n * @returns {boolean} `true` if reserved, otherwise `false`\n */\nType.prototype.isReservedId = function isReservedId(id) {\n return Namespace.isReservedId(this.reserved, id);\n};\n\n/**\n * Tests if the specified name is reserved.\n * @param {string} name Name to test\n * @returns {boolean} `true` if reserved, otherwise `false`\n */\nType.prototype.isReservedName = function isReservedName(name) {\n return Namespace.isReservedName(this.reserved, name);\n};\n\n/**\n * Creates a new message of this type using the specified properties.\n * @param {Object.} [properties] Properties to set\n * @returns {Message<{}>} Message instance\n */\nType.prototype.create = function create(properties) {\n return new this.ctor(properties);\n};\n\n/**\n * Sets up {@link Type#encode|encode}, {@link Type#decode|decode} and {@link Type#verify|verify}.\n * @returns {Type} `this`\n */\nType.prototype.setup = function setup() {\n // Sets up everything at once so that the prototype chain does not have to be re-evaluated\n // multiple times (V8, soft-deopt prototype-check).\n\n var fullName = this.fullName,\n types = [];\n for (var i = 0; i < /* initializes */ this.fieldsArray.length; ++i)\n types.push(this._fieldsArray[i].resolve().resolvedType);\n\n // Replace setup methods with type-specific generated functions\n this.encode = encoder(this)({\n Writer : Writer,\n types : types,\n util : util\n });\n this.decode = decoder(this)({\n Reader : Reader,\n types : types,\n util : util\n });\n this.verify = verifier(this)({\n types : types,\n util : util\n });\n this.fromObject = converter.fromObject(this)({\n types : types,\n util : util\n });\n this.toObject = converter.toObject(this)({\n types : types,\n util : util\n });\n\n // Inject custom wrappers for common types\n var wrapper = wrappers[fullName];\n if (wrapper) {\n var originalThis = Object.create(this);\n // if (wrapper.fromObject) {\n originalThis.fromObject = this.fromObject;\n this.fromObject = wrapper.fromObject.bind(originalThis);\n // }\n // if (wrapper.toObject) {\n originalThis.toObject = this.toObject;\n this.toObject = wrapper.toObject.bind(originalThis);\n // }\n }\n\n return this;\n};\n\n/**\n * Encodes a message of this type. Does not implicitly {@link Type#verify|verify} messages.\n * @param {Message<{}>|Object.} message Message instance or plain object\n * @param {Writer} [writer] Writer to encode to\n * @returns {Writer} writer\n */\nType.prototype.encode = function encode_setup(message, writer) {\n return this.setup().encode(message, writer); // overrides this method\n};\n\n/**\n * Encodes a message of this type preceeded by its byte length as a varint. Does not implicitly {@link Type#verify|verify} messages.\n * @param {Message<{}>|Object.} message Message instance or plain object\n * @param {Writer} [writer] Writer to encode to\n * @returns {Writer} writer\n */\nType.prototype.encodeDelimited = function encodeDelimited(message, writer) {\n return this.encode(message, writer && writer.len ? writer.fork() : writer).ldelim();\n};\n\n/**\n * Decodes a message of this type.\n * @param {Reader|Uint8Array} reader Reader or buffer to decode from\n * @param {number} [length] Length of the message, if known beforehand\n * @returns {Message<{}>} Decoded message\n * @throws {Error} If the payload is not a reader or valid buffer\n * @throws {util.ProtocolError<{}>} If required fields are missing\n */\nType.prototype.decode = function decode_setup(reader, length) {\n return this.setup().decode(reader, length); // overrides this method\n};\n\n/**\n * Decodes a message of this type preceeded by its byte length as a varint.\n * @param {Reader|Uint8Array} reader Reader or buffer to decode from\n * @returns {Message<{}>} Decoded message\n * @throws {Error} If the payload is not a reader or valid buffer\n * @throws {util.ProtocolError} If required fields are missing\n */\nType.prototype.decodeDelimited = function decodeDelimited(reader) {\n if (!(reader instanceof Reader))\n reader = Reader.create(reader);\n return this.decode(reader, reader.uint32());\n};\n\n/**\n * Verifies that field values are valid and that required fields are present.\n * @param {Object.} message Plain object to verify\n * @returns {null|string} `null` if valid, otherwise the reason why it is not\n */\nType.prototype.verify = function verify_setup(message) {\n return this.setup().verify(message); // overrides this method\n};\n\n/**\n * Creates a new message of this type from a plain object. Also converts values to their respective internal types.\n * @param {Object.} object Plain object to convert\n * @returns {Message<{}>} Message instance\n */\nType.prototype.fromObject = function fromObject(object) {\n return this.setup().fromObject(object);\n};\n\n/**\n * Conversion options as used by {@link Type#toObject} and {@link Message.toObject}.\n * @interface IConversionOptions\n * @property {Function} [longs] Long conversion type.\n * Valid values are `String` and `Number` (the global types).\n * Defaults to copy the present value, which is a possibly unsafe number without and a {@link Long} with a long library.\n * @property {Function} [enums] Enum value conversion type.\n * Only valid value is `String` (the global type).\n * Defaults to copy the present value, which is the numeric id.\n * @property {Function} [bytes] Bytes value conversion type.\n * Valid values are `Array` and (a base64 encoded) `String` (the global types).\n * Defaults to copy the present value, which usually is a Buffer under node and an Uint8Array in the browser.\n * @property {boolean} [defaults=false] Also sets default values on the resulting object\n * @property {boolean} [arrays=false] Sets empty arrays for missing repeated fields even if `defaults=false`\n * @property {boolean} [objects=false] Sets empty objects for missing map fields even if `defaults=false`\n * @property {boolean} [oneofs=false] Includes virtual oneof properties set to the present field's name, if any\n * @property {boolean} [json=false] Performs additional JSON compatibility conversions, i.e. NaN and Infinity to strings\n */\n\n/**\n * Creates a plain object from a message of this type. Also converts values to other types if specified.\n * @param {Message<{}>} message Message instance\n * @param {IConversionOptions} [options] Conversion options\n * @returns {Object.} Plain object\n */\nType.prototype.toObject = function toObject(message, options) {\n return this.setup().toObject(message, options);\n};\n\n/**\n * Decorator function as returned by {@link Type.d} (TypeScript).\n * @typedef TypeDecorator\n * @type {function}\n * @param {Constructor} target Target constructor\n * @returns {undefined}\n * @template T extends Message\n */\n\n/**\n * Type decorator (TypeScript).\n * @param {string} [typeName] Type name, defaults to the constructor's name\n * @returns {TypeDecorator} Decorator function\n * @template T extends Message\n */\nType.d = function decorateType(typeName) {\n return function typeDecorator(target) {\n util.decorateType(target, typeName);\n };\n};\n","\"use strict\";\n\n/**\n * Common type constants.\n * @namespace\n */\nvar types = exports;\n\nvar util = require(37);\n\nvar s = [\n \"double\", // 0\n \"float\", // 1\n \"int32\", // 2\n \"uint32\", // 3\n \"sint32\", // 4\n \"fixed32\", // 5\n \"sfixed32\", // 6\n \"int64\", // 7\n \"uint64\", // 8\n \"sint64\", // 9\n \"fixed64\", // 10\n \"sfixed64\", // 11\n \"bool\", // 12\n \"string\", // 13\n \"bytes\" // 14\n];\n\nfunction bake(values, offset) {\n var i = 0, o = {};\n offset |= 0;\n while (i < values.length) o[s[i + offset]] = values[i++];\n return o;\n}\n\n/**\n * Basic type wire types.\n * @type {Object.}\n * @const\n * @property {number} double=1 Fixed64 wire type\n * @property {number} float=5 Fixed32 wire type\n * @property {number} int32=0 Varint wire type\n * @property {number} uint32=0 Varint wire type\n * @property {number} sint32=0 Varint wire type\n * @property {number} fixed32=5 Fixed32 wire type\n * @property {number} sfixed32=5 Fixed32 wire type\n * @property {number} int64=0 Varint wire type\n * @property {number} uint64=0 Varint wire type\n * @property {number} sint64=0 Varint wire type\n * @property {number} fixed64=1 Fixed64 wire type\n * @property {number} sfixed64=1 Fixed64 wire type\n * @property {number} bool=0 Varint wire type\n * @property {number} string=2 Ldelim wire type\n * @property {number} bytes=2 Ldelim wire type\n */\ntypes.basic = bake([\n /* double */ 1,\n /* float */ 5,\n /* int32 */ 0,\n /* uint32 */ 0,\n /* sint32 */ 0,\n /* fixed32 */ 5,\n /* sfixed32 */ 5,\n /* int64 */ 0,\n /* uint64 */ 0,\n /* sint64 */ 0,\n /* fixed64 */ 1,\n /* sfixed64 */ 1,\n /* bool */ 0,\n /* string */ 2,\n /* bytes */ 2\n]);\n\n/**\n * Basic type defaults.\n * @type {Object.}\n * @const\n * @property {number} double=0 Double default\n * @property {number} float=0 Float default\n * @property {number} int32=0 Int32 default\n * @property {number} uint32=0 Uint32 default\n * @property {number} sint32=0 Sint32 default\n * @property {number} fixed32=0 Fixed32 default\n * @property {number} sfixed32=0 Sfixed32 default\n * @property {number} int64=0 Int64 default\n * @property {number} uint64=0 Uint64 default\n * @property {number} sint64=0 Sint32 default\n * @property {number} fixed64=0 Fixed64 default\n * @property {number} sfixed64=0 Sfixed64 default\n * @property {boolean} bool=false Bool default\n * @property {string} string=\"\" String default\n * @property {Array.} bytes=Array(0) Bytes default\n * @property {null} message=null Message default\n */\ntypes.defaults = bake([\n /* double */ 0,\n /* float */ 0,\n /* int32 */ 0,\n /* uint32 */ 0,\n /* sint32 */ 0,\n /* fixed32 */ 0,\n /* sfixed32 */ 0,\n /* int64 */ 0,\n /* uint64 */ 0,\n /* sint64 */ 0,\n /* fixed64 */ 0,\n /* sfixed64 */ 0,\n /* bool */ false,\n /* string */ \"\",\n /* bytes */ util.emptyArray,\n /* message */ null\n]);\n\n/**\n * Basic long type wire types.\n * @type {Object.}\n * @const\n * @property {number} int64=0 Varint wire type\n * @property {number} uint64=0 Varint wire type\n * @property {number} sint64=0 Varint wire type\n * @property {number} fixed64=1 Fixed64 wire type\n * @property {number} sfixed64=1 Fixed64 wire type\n */\ntypes.long = bake([\n /* int64 */ 0,\n /* uint64 */ 0,\n /* sint64 */ 0,\n /* fixed64 */ 1,\n /* sfixed64 */ 1\n], 7);\n\n/**\n * Allowed types for map keys with their associated wire type.\n * @type {Object.}\n * @const\n * @property {number} int32=0 Varint wire type\n * @property {number} uint32=0 Varint wire type\n * @property {number} sint32=0 Varint wire type\n * @property {number} fixed32=5 Fixed32 wire type\n * @property {number} sfixed32=5 Fixed32 wire type\n * @property {number} int64=0 Varint wire type\n * @property {number} uint64=0 Varint wire type\n * @property {number} sint64=0 Varint wire type\n * @property {number} fixed64=1 Fixed64 wire type\n * @property {number} sfixed64=1 Fixed64 wire type\n * @property {number} bool=0 Varint wire type\n * @property {number} string=2 Ldelim wire type\n */\ntypes.mapKey = bake([\n /* int32 */ 0,\n /* uint32 */ 0,\n /* sint32 */ 0,\n /* fixed32 */ 5,\n /* sfixed32 */ 5,\n /* int64 */ 0,\n /* uint64 */ 0,\n /* sint64 */ 0,\n /* fixed64 */ 1,\n /* sfixed64 */ 1,\n /* bool */ 0,\n /* string */ 2\n], 2);\n\n/**\n * Allowed types for packed repeated fields with their associated wire type.\n * @type {Object.}\n * @const\n * @property {number} double=1 Fixed64 wire type\n * @property {number} float=5 Fixed32 wire type\n * @property {number} int32=0 Varint wire type\n * @property {number} uint32=0 Varint wire type\n * @property {number} sint32=0 Varint wire type\n * @property {number} fixed32=5 Fixed32 wire type\n * @property {number} sfixed32=5 Fixed32 wire type\n * @property {number} int64=0 Varint wire type\n * @property {number} uint64=0 Varint wire type\n * @property {number} sint64=0 Varint wire type\n * @property {number} fixed64=1 Fixed64 wire type\n * @property {number} sfixed64=1 Fixed64 wire type\n * @property {number} bool=0 Varint wire type\n */\ntypes.packed = bake([\n /* double */ 1,\n /* float */ 5,\n /* int32 */ 0,\n /* uint32 */ 0,\n /* sint32 */ 0,\n /* fixed32 */ 5,\n /* sfixed32 */ 5,\n /* int64 */ 0,\n /* uint64 */ 0,\n /* sint64 */ 0,\n /* fixed64 */ 1,\n /* sfixed64 */ 1,\n /* bool */ 0\n]);\n","\"use strict\";\n\n/**\n * Various utility functions.\n * @namespace\n */\nvar util = module.exports = require(39);\n\nvar roots = require(30);\n\nvar Type, // cyclic\n Enum;\n\nutil.codegen = require(3);\nutil.fetch = require(5);\nutil.path = require(8);\n\n/**\n * Node's fs module if available.\n * @type {Object.}\n */\nutil.fs = util.inquire(\"fs\");\n\n/**\n * Converts an object's values to an array.\n * @param {Object.} object Object to convert\n * @returns {Array.<*>} Converted array\n */\nutil.toArray = function toArray(object) {\n if (object) {\n var keys = Object.keys(object),\n array = new Array(keys.length),\n index = 0;\n while (index < keys.length)\n array[index] = object[keys[index++]];\n return array;\n }\n return [];\n};\n\n/**\n * Converts an array of keys immediately followed by their respective value to an object, omitting undefined values.\n * @param {Array.<*>} array Array to convert\n * @returns {Object.} Converted object\n */\nutil.toObject = function toObject(array) {\n var object = {},\n index = 0;\n while (index < array.length) {\n var key = array[index++],\n val = array[index++];\n if (val !== undefined)\n object[key] = val;\n }\n return object;\n};\n\nvar safePropBackslashRe = /\\\\/g,\n safePropQuoteRe = /\"/g;\n\n/**\n * Tests whether the specified name is a reserved word in JS.\n * @param {string} name Name to test\n * @returns {boolean} `true` if reserved, otherwise `false`\n */\nutil.isReserved = function isReserved(name) {\n return /^(?:do|if|in|for|let|new|try|var|case|else|enum|eval|false|null|this|true|void|with|break|catch|class|const|super|throw|while|yield|delete|export|import|public|return|static|switch|typeof|default|extends|finally|package|private|continue|debugger|function|arguments|interface|protected|implements|instanceof)$/.test(name);\n};\n\n/**\n * Returns a safe property accessor for the specified property name.\n * @param {string} prop Property name\n * @returns {string} Safe accessor\n */\nutil.safeProp = function safeProp(prop) {\n if (!/^[$\\w_]+$/.test(prop) || util.isReserved(prop))\n return \"[\\\"\" + prop.replace(safePropBackslashRe, \"\\\\\\\\\").replace(safePropQuoteRe, \"\\\\\\\"\") + \"\\\"]\";\n return \".\" + prop;\n};\n\n/**\n * Converts the first character of a string to upper case.\n * @param {string} str String to convert\n * @returns {string} Converted string\n */\nutil.ucFirst = function ucFirst(str) {\n return str.charAt(0).toUpperCase() + str.substring(1);\n};\n\nvar camelCaseRe = /_([a-z])/g;\n\n/**\n * Converts a string to camel case.\n * @param {string} str String to convert\n * @returns {string} Converted string\n */\nutil.camelCase = function camelCase(str) {\n return str.substring(0, 1)\n + str.substring(1)\n .replace(camelCaseRe, function($0, $1) { return $1.toUpperCase(); });\n};\n\n/**\n * Compares reflected fields by id.\n * @param {Field} a First field\n * @param {Field} b Second field\n * @returns {number} Comparison value\n */\nutil.compareFieldsById = function compareFieldsById(a, b) {\n return a.id - b.id;\n};\n\n/**\n * Decorator helper for types (TypeScript).\n * @param {Constructor} ctor Constructor function\n * @param {string} [typeName] Type name, defaults to the constructor's name\n * @returns {Type} Reflected type\n * @template T extends Message\n * @property {Root} root Decorators root\n */\nutil.decorateType = function decorateType(ctor, typeName) {\n\n /* istanbul ignore if */\n if (ctor.$type) {\n if (typeName && ctor.$type.name !== typeName) {\n util.decorateRoot.remove(ctor.$type);\n ctor.$type.name = typeName;\n util.decorateRoot.add(ctor.$type);\n }\n return ctor.$type;\n }\n\n /* istanbul ignore next */\n if (!Type)\n Type = require(35);\n\n var type = new Type(typeName || ctor.name);\n util.decorateRoot.add(type);\n type.ctor = ctor; // sets up .encode, .decode etc.\n Object.defineProperty(ctor, \"$type\", { value: type, enumerable: false });\n Object.defineProperty(ctor.prototype, \"$type\", { value: type, enumerable: false });\n return type;\n};\n\nvar decorateEnumIndex = 0;\n\n/**\n * Decorator helper for enums (TypeScript).\n * @param {Object} object Enum object\n * @returns {Enum} Reflected enum\n */\nutil.decorateEnum = function decorateEnum(object) {\n\n /* istanbul ignore if */\n if (object.$type)\n return object.$type;\n\n /* istanbul ignore next */\n if (!Enum)\n Enum = require(15);\n\n var enm = new Enum(\"Enum\" + decorateEnumIndex++, object);\n util.decorateRoot.add(enm);\n Object.defineProperty(object, \"$type\", { value: enm, enumerable: false });\n return enm;\n};\n\n\n/**\n * Sets the value of a property by property path. If a value already exists, it is turned to an array\n * @param {Object.} dst Destination object\n * @param {string} path dot '.' delimited path of the property to set\n * @param {Object} value the value to set\n * @returns {Object.} Destination object\n */\nutil.setProperty = function setProperty(dst, path, value) {\n function setProp(dst, path, value) {\n var part = path.shift();\n if (path.length > 0) {\n dst[part] = setProp(dst[part] || {}, path, value);\n } else {\n var prevValue = dst[part];\n if (prevValue)\n value = [].concat(prevValue).concat(value);\n dst[part] = value;\n }\n return dst;\n }\n\n if (typeof dst !== \"object\")\n throw TypeError(\"dst must be an object\");\n if (!path)\n throw TypeError(\"path must be specified\");\n\n path = path.split(\".\");\n return setProp(dst, path, value);\n};\n\n/**\n * Decorator root (TypeScript).\n * @name util.decorateRoot\n * @type {Root}\n * @readonly\n */\nObject.defineProperty(util, \"decorateRoot\", {\n get: function() {\n return roots[\"decorated\"] || (roots[\"decorated\"] = new (require(29))());\n }\n});\n","\"use strict\";\nmodule.exports = LongBits;\n\nvar util = require(39);\n\n/**\n * Constructs new long bits.\n * @classdesc Helper class for working with the low and high bits of a 64 bit value.\n * @memberof util\n * @constructor\n * @param {number} lo Low 32 bits, unsigned\n * @param {number} hi High 32 bits, unsigned\n */\nfunction LongBits(lo, hi) {\n\n // note that the casts below are theoretically unnecessary as of today, but older statically\n // generated converter code might still call the ctor with signed 32bits. kept for compat.\n\n /**\n * Low bits.\n * @type {number}\n */\n this.lo = lo >>> 0;\n\n /**\n * High bits.\n * @type {number}\n */\n this.hi = hi >>> 0;\n}\n\n/**\n * Zero bits.\n * @memberof util.LongBits\n * @type {util.LongBits}\n */\nvar zero = LongBits.zero = new LongBits(0, 0);\n\nzero.toNumber = function() { return 0; };\nzero.zzEncode = zero.zzDecode = function() { return this; };\nzero.length = function() { return 1; };\n\n/**\n * Zero hash.\n * @memberof util.LongBits\n * @type {string}\n */\nvar zeroHash = LongBits.zeroHash = \"\\0\\0\\0\\0\\0\\0\\0\\0\";\n\n/**\n * Constructs new long bits from the specified number.\n * @param {number} value Value\n * @returns {util.LongBits} Instance\n */\nLongBits.fromNumber = function fromNumber(value) {\n if (value === 0)\n return zero;\n var sign = value < 0;\n if (sign)\n value = -value;\n var lo = value >>> 0,\n hi = (value - lo) / 4294967296 >>> 0;\n if (sign) {\n hi = ~hi >>> 0;\n lo = ~lo >>> 0;\n if (++lo > 4294967295) {\n lo = 0;\n if (++hi > 4294967295)\n hi = 0;\n }\n }\n return new LongBits(lo, hi);\n};\n\n/**\n * Constructs new long bits from a number, long or string.\n * @param {Long|number|string} value Value\n * @returns {util.LongBits} Instance\n */\nLongBits.from = function from(value) {\n if (typeof value === \"number\")\n return LongBits.fromNumber(value);\n if (util.isString(value)) {\n /* istanbul ignore else */\n if (util.Long)\n value = util.Long.fromString(value);\n else\n return LongBits.fromNumber(parseInt(value, 10));\n }\n return value.low || value.high ? new LongBits(value.low >>> 0, value.high >>> 0) : zero;\n};\n\n/**\n * Converts this long bits to a possibly unsafe JavaScript number.\n * @param {boolean} [unsigned=false] Whether unsigned or not\n * @returns {number} Possibly unsafe number\n */\nLongBits.prototype.toNumber = function toNumber(unsigned) {\n if (!unsigned && this.hi >>> 31) {\n var lo = ~this.lo + 1 >>> 0,\n hi = ~this.hi >>> 0;\n if (!lo)\n hi = hi + 1 >>> 0;\n return -(lo + hi * 4294967296);\n }\n return this.lo + this.hi * 4294967296;\n};\n\n/**\n * Converts this long bits to a long.\n * @param {boolean} [unsigned=false] Whether unsigned or not\n * @returns {Long} Long\n */\nLongBits.prototype.toLong = function toLong(unsigned) {\n return util.Long\n ? new util.Long(this.lo | 0, this.hi | 0, Boolean(unsigned))\n /* istanbul ignore next */\n : { low: this.lo | 0, high: this.hi | 0, unsigned: Boolean(unsigned) };\n};\n\nvar charCodeAt = String.prototype.charCodeAt;\n\n/**\n * Constructs new long bits from the specified 8 characters long hash.\n * @param {string} hash Hash\n * @returns {util.LongBits} Bits\n */\nLongBits.fromHash = function fromHash(hash) {\n if (hash === zeroHash)\n return zero;\n return new LongBits(\n ( charCodeAt.call(hash, 0)\n | charCodeAt.call(hash, 1) << 8\n | charCodeAt.call(hash, 2) << 16\n | charCodeAt.call(hash, 3) << 24) >>> 0\n ,\n ( charCodeAt.call(hash, 4)\n | charCodeAt.call(hash, 5) << 8\n | charCodeAt.call(hash, 6) << 16\n | charCodeAt.call(hash, 7) << 24) >>> 0\n );\n};\n\n/**\n * Converts this long bits to a 8 characters long hash.\n * @returns {string} Hash\n */\nLongBits.prototype.toHash = function toHash() {\n return String.fromCharCode(\n this.lo & 255,\n this.lo >>> 8 & 255,\n this.lo >>> 16 & 255,\n this.lo >>> 24 ,\n this.hi & 255,\n this.hi >>> 8 & 255,\n this.hi >>> 16 & 255,\n this.hi >>> 24\n );\n};\n\n/**\n * Zig-zag encodes this long bits.\n * @returns {util.LongBits} `this`\n */\nLongBits.prototype.zzEncode = function zzEncode() {\n var mask = this.hi >> 31;\n this.hi = ((this.hi << 1 | this.lo >>> 31) ^ mask) >>> 0;\n this.lo = ( this.lo << 1 ^ mask) >>> 0;\n return this;\n};\n\n/**\n * Zig-zag decodes this long bits.\n * @returns {util.LongBits} `this`\n */\nLongBits.prototype.zzDecode = function zzDecode() {\n var mask = -(this.lo & 1);\n this.lo = ((this.lo >>> 1 | this.hi << 31) ^ mask) >>> 0;\n this.hi = ( this.hi >>> 1 ^ mask) >>> 0;\n return this;\n};\n\n/**\n * Calculates the length of this longbits when encoded as a varint.\n * @returns {number} Length\n */\nLongBits.prototype.length = function length() {\n var part0 = this.lo,\n part1 = (this.lo >>> 28 | this.hi << 4) >>> 0,\n part2 = this.hi >>> 24;\n return part2 === 0\n ? part1 === 0\n ? part0 < 16384\n ? part0 < 128 ? 1 : 2\n : part0 < 2097152 ? 3 : 4\n : part1 < 16384\n ? part1 < 128 ? 5 : 6\n : part1 < 2097152 ? 7 : 8\n : part2 < 128 ? 9 : 10;\n};\n","\"use strict\";\nvar util = exports;\n\n// used to return a Promise where callback is omitted\nutil.asPromise = require(1);\n\n// converts to / from base64 encoded strings\nutil.base64 = require(2);\n\n// base class of rpc.Service\nutil.EventEmitter = require(4);\n\n// float handling accross browsers\nutil.float = require(6);\n\n// requires modules optionally and hides the call from bundlers\nutil.inquire = require(7);\n\n// converts to / from utf8 encoded strings\nutil.utf8 = require(10);\n\n// provides a node-like buffer pool in the browser\nutil.pool = require(9);\n\n// utility to work with the low and high bits of a 64 bit value\nutil.LongBits = require(38);\n\n/**\n * Whether running within node or not.\n * @memberof util\n * @type {boolean}\n */\nutil.isNode = Boolean(typeof global !== \"undefined\"\n && global\n && global.process\n && global.process.versions\n && global.process.versions.node);\n\n/**\n * Global object reference.\n * @memberof util\n * @type {Object}\n */\nutil.global = util.isNode && global\n || typeof window !== \"undefined\" && window\n || typeof self !== \"undefined\" && self\n || this; // eslint-disable-line no-invalid-this\n\n/**\n * An immuable empty array.\n * @memberof util\n * @type {Array.<*>}\n * @const\n */\nutil.emptyArray = Object.freeze ? Object.freeze([]) : /* istanbul ignore next */ []; // used on prototypes\n\n/**\n * An immutable empty object.\n * @type {Object}\n * @const\n */\nutil.emptyObject = Object.freeze ? Object.freeze({}) : /* istanbul ignore next */ {}; // used on prototypes\n\n/**\n * Tests if the specified value is an integer.\n * @function\n * @param {*} value Value to test\n * @returns {boolean} `true` if the value is an integer\n */\nutil.isInteger = Number.isInteger || /* istanbul ignore next */ function isInteger(value) {\n return typeof value === \"number\" && isFinite(value) && Math.floor(value) === value;\n};\n\n/**\n * Tests if the specified value is a string.\n * @param {*} value Value to test\n * @returns {boolean} `true` if the value is a string\n */\nutil.isString = function isString(value) {\n return typeof value === \"string\" || value instanceof String;\n};\n\n/**\n * Tests if the specified value is a non-null object.\n * @param {*} value Value to test\n * @returns {boolean} `true` if the value is a non-null object\n */\nutil.isObject = function isObject(value) {\n return value && typeof value === \"object\";\n};\n\n/**\n * Checks if a property on a message is considered to be present.\n * This is an alias of {@link util.isSet}.\n * @function\n * @param {Object} obj Plain object or message instance\n * @param {string} prop Property name\n * @returns {boolean} `true` if considered to be present, otherwise `false`\n */\nutil.isset =\n\n/**\n * Checks if a property on a message is considered to be present.\n * @param {Object} obj Plain object or message instance\n * @param {string} prop Property name\n * @returns {boolean} `true` if considered to be present, otherwise `false`\n */\nutil.isSet = function isSet(obj, prop) {\n var value = obj[prop];\n if (value != null && obj.hasOwnProperty(prop)) // eslint-disable-line eqeqeq, no-prototype-builtins\n return typeof value !== \"object\" || (Array.isArray(value) ? value.length : Object.keys(value).length) > 0;\n return false;\n};\n\n/**\n * Any compatible Buffer instance.\n * This is a minimal stand-alone definition of a Buffer instance. The actual type is that exported by node's typings.\n * @interface Buffer\n * @extends Uint8Array\n */\n\n/**\n * Node's Buffer class if available.\n * @type {Constructor}\n */\nutil.Buffer = (function() {\n try {\n var Buffer = util.inquire(\"buffer\").Buffer;\n // refuse to use non-node buffers if not explicitly assigned (perf reasons):\n return Buffer.prototype.utf8Write ? Buffer : /* istanbul ignore next */ null;\n } catch (e) {\n /* istanbul ignore next */\n return null;\n }\n})();\n\n// Internal alias of or polyfull for Buffer.from.\nutil._Buffer_from = null;\n\n// Internal alias of or polyfill for Buffer.allocUnsafe.\nutil._Buffer_allocUnsafe = null;\n\n/**\n * Creates a new buffer of whatever type supported by the environment.\n * @param {number|number[]} [sizeOrArray=0] Buffer size or number array\n * @returns {Uint8Array|Buffer} Buffer\n */\nutil.newBuffer = function newBuffer(sizeOrArray) {\n /* istanbul ignore next */\n return typeof sizeOrArray === \"number\"\n ? util.Buffer\n ? util._Buffer_allocUnsafe(sizeOrArray)\n : new util.Array(sizeOrArray)\n : util.Buffer\n ? util._Buffer_from(sizeOrArray)\n : typeof Uint8Array === \"undefined\"\n ? sizeOrArray\n : new Uint8Array(sizeOrArray);\n};\n\n/**\n * Array implementation used in the browser. `Uint8Array` if supported, otherwise `Array`.\n * @type {Constructor}\n */\nutil.Array = typeof Uint8Array !== \"undefined\" ? Uint8Array /* istanbul ignore next */ : Array;\n\n/**\n * Any compatible Long instance.\n * This is a minimal stand-alone definition of a Long instance. The actual type is that exported by long.js.\n * @interface Long\n * @property {number} low Low bits\n * @property {number} high High bits\n * @property {boolean} unsigned Whether unsigned or not\n */\n\n/**\n * Long.js's Long class if available.\n * @type {Constructor}\n */\nutil.Long = /* istanbul ignore next */ util.global.dcodeIO && /* istanbul ignore next */ util.global.dcodeIO.Long\n || /* istanbul ignore next */ util.global.Long\n || util.inquire(\"long\");\n\n/**\n * Regular expression used to verify 2 bit (`bool`) map keys.\n * @type {RegExp}\n * @const\n */\nutil.key2Re = /^true|false|0|1$/;\n\n/**\n * Regular expression used to verify 32 bit (`int32` etc.) map keys.\n * @type {RegExp}\n * @const\n */\nutil.key32Re = /^-?(?:0|[1-9][0-9]*)$/;\n\n/**\n * Regular expression used to verify 64 bit (`int64` etc.) map keys.\n * @type {RegExp}\n * @const\n */\nutil.key64Re = /^(?:[\\\\x00-\\\\xff]{8}|-?(?:0|[1-9][0-9]*))$/;\n\n/**\n * Converts a number or long to an 8 characters long hash string.\n * @param {Long|number} value Value to convert\n * @returns {string} Hash\n */\nutil.longToHash = function longToHash(value) {\n return value\n ? util.LongBits.from(value).toHash()\n : util.LongBits.zeroHash;\n};\n\n/**\n * Converts an 8 characters long hash string to a long or number.\n * @param {string} hash Hash\n * @param {boolean} [unsigned=false] Whether unsigned or not\n * @returns {Long|number} Original value\n */\nutil.longFromHash = function longFromHash(hash, unsigned) {\n var bits = util.LongBits.fromHash(hash);\n if (util.Long)\n return util.Long.fromBits(bits.lo, bits.hi, unsigned);\n return bits.toNumber(Boolean(unsigned));\n};\n\n/**\n * Merges the properties of the source object into the destination object.\n * @memberof util\n * @param {Object.} dst Destination object\n * @param {Object.} src Source object\n * @param {boolean} [ifNotSet=false] Merges only if the key is not already set\n * @returns {Object.} Destination object\n */\nfunction merge(dst, src, ifNotSet) { // used by converters\n for (var keys = Object.keys(src), i = 0; i < keys.length; ++i)\n if (dst[keys[i]] === undefined || !ifNotSet)\n dst[keys[i]] = src[keys[i]];\n return dst;\n}\n\nutil.merge = merge;\n\n/**\n * Converts the first character of a string to lower case.\n * @param {string} str String to convert\n * @returns {string} Converted string\n */\nutil.lcFirst = function lcFirst(str) {\n return str.charAt(0).toLowerCase() + str.substring(1);\n};\n\n/**\n * Creates a custom error constructor.\n * @memberof util\n * @param {string} name Error name\n * @returns {Constructor} Custom error constructor\n */\nfunction newError(name) {\n\n function CustomError(message, properties) {\n\n if (!(this instanceof CustomError))\n return new CustomError(message, properties);\n\n // Error.call(this, message);\n // ^ just returns a new error instance because the ctor can be called as a function\n\n Object.defineProperty(this, \"message\", { get: function() { return message; } });\n\n /* istanbul ignore next */\n if (Error.captureStackTrace) // node\n Error.captureStackTrace(this, CustomError);\n else\n Object.defineProperty(this, \"stack\", { value: new Error().stack || \"\" });\n\n if (properties)\n merge(this, properties);\n }\n\n (CustomError.prototype = Object.create(Error.prototype)).constructor = CustomError;\n\n Object.defineProperty(CustomError.prototype, \"name\", { get: function() { return name; } });\n\n CustomError.prototype.toString = function toString() {\n return this.name + \": \" + this.message;\n };\n\n return CustomError;\n}\n\nutil.newError = newError;\n\n/**\n * Constructs a new protocol error.\n * @classdesc Error subclass indicating a protocol specifc error.\n * @memberof util\n * @extends Error\n * @template T extends Message\n * @constructor\n * @param {string} message Error message\n * @param {Object.} [properties] Additional properties\n * @example\n * try {\n * MyMessage.decode(someBuffer); // throws if required fields are missing\n * } catch (e) {\n * if (e instanceof ProtocolError && e.instance)\n * console.log(\"decoded so far: \" + JSON.stringify(e.instance));\n * }\n */\nutil.ProtocolError = newError(\"ProtocolError\");\n\n/**\n * So far decoded message instance.\n * @name util.ProtocolError#instance\n * @type {Message}\n */\n\n/**\n * A OneOf getter as returned by {@link util.oneOfGetter}.\n * @typedef OneOfGetter\n * @type {function}\n * @returns {string|undefined} Set field name, if any\n */\n\n/**\n * Builds a getter for a oneof's present field name.\n * @param {string[]} fieldNames Field names\n * @returns {OneOfGetter} Unbound getter\n */\nutil.oneOfGetter = function getOneOf(fieldNames) {\n var fieldMap = {};\n for (var i = 0; i < fieldNames.length; ++i)\n fieldMap[fieldNames[i]] = 1;\n\n /**\n * @returns {string|undefined} Set field name, if any\n * @this Object\n * @ignore\n */\n return function() { // eslint-disable-line consistent-return\n for (var keys = Object.keys(this), i = keys.length - 1; i > -1; --i)\n if (fieldMap[keys[i]] === 1 && this[keys[i]] !== undefined && this[keys[i]] !== null)\n return keys[i];\n };\n};\n\n/**\n * A OneOf setter as returned by {@link util.oneOfSetter}.\n * @typedef OneOfSetter\n * @type {function}\n * @param {string|undefined} value Field name\n * @returns {undefined}\n */\n\n/**\n * Builds a setter for a oneof's present field name.\n * @param {string[]} fieldNames Field names\n * @returns {OneOfSetter} Unbound setter\n */\nutil.oneOfSetter = function setOneOf(fieldNames) {\n\n /**\n * @param {string} name Field name\n * @returns {undefined}\n * @this Object\n * @ignore\n */\n return function(name) {\n for (var i = 0; i < fieldNames.length; ++i)\n if (fieldNames[i] !== name)\n delete this[fieldNames[i]];\n };\n};\n\n/**\n * Default conversion options used for {@link Message#toJSON} implementations.\n *\n * These options are close to proto3's JSON mapping with the exception that internal types like Any are handled just like messages. More precisely:\n *\n * - Longs become strings\n * - Enums become string keys\n * - Bytes become base64 encoded strings\n * - (Sub-)Messages become plain objects\n * - Maps become plain objects with all string keys\n * - Repeated fields become arrays\n * - NaN and Infinity for float and double fields become strings\n *\n * @type {IConversionOptions}\n * @see https://developers.google.com/protocol-buffers/docs/proto3?hl=en#json\n */\nutil.toJSONOptions = {\n longs: String,\n enums: String,\n bytes: String,\n json: true\n};\n\n// Sets up buffer utility according to the environment (called in index-minimal)\nutil._configure = function() {\n var Buffer = util.Buffer;\n /* istanbul ignore if */\n if (!Buffer) {\n util._Buffer_from = util._Buffer_allocUnsafe = null;\n return;\n }\n // because node 4.x buffers are incompatible & immutable\n // see: https://github.com/dcodeIO/protobuf.js/pull/665\n util._Buffer_from = Buffer.from !== Uint8Array.from && Buffer.from ||\n /* istanbul ignore next */\n function Buffer_from(value, encoding) {\n return new Buffer(value, encoding);\n };\n util._Buffer_allocUnsafe = Buffer.allocUnsafe ||\n /* istanbul ignore next */\n function Buffer_allocUnsafe(size) {\n return new Buffer(size);\n };\n};\n","\"use strict\";\nmodule.exports = verifier;\n\nvar Enum = require(15),\n util = require(37);\n\nfunction invalid(field, expected) {\n return field.name + \": \" + expected + (field.repeated && expected !== \"array\" ? \"[]\" : field.map && expected !== \"object\" ? \"{k:\"+field.keyType+\"}\" : \"\") + \" expected\";\n}\n\n/**\n * Generates a partial value verifier.\n * @param {Codegen} gen Codegen instance\n * @param {Field} field Reflected field\n * @param {number} fieldIndex Field index\n * @param {string} ref Variable reference\n * @returns {Codegen} Codegen instance\n * @ignore\n */\nfunction genVerifyValue(gen, field, fieldIndex, ref) {\n /* eslint-disable no-unexpected-multiline */\n if (field.resolvedType) {\n if (field.resolvedType instanceof Enum) { gen\n (\"switch(%s){\", ref)\n (\"default:\")\n (\"return%j\", invalid(field, \"enum value\"));\n for (var keys = Object.keys(field.resolvedType.values), j = 0; j < keys.length; ++j) gen\n (\"case %i:\", field.resolvedType.values[keys[j]]);\n gen\n (\"break\")\n (\"}\");\n } else {\n gen\n (\"{\")\n (\"var e=types[%i].verify(%s);\", fieldIndex, ref)\n (\"if(e)\")\n (\"return%j+e\", field.name + \".\")\n (\"}\");\n }\n } else {\n switch (field.type) {\n case \"int32\":\n case \"uint32\":\n case \"sint32\":\n case \"fixed32\":\n case \"sfixed32\": gen\n (\"if(!util.isInteger(%s))\", ref)\n (\"return%j\", invalid(field, \"integer\"));\n break;\n case \"int64\":\n case \"uint64\":\n case \"sint64\":\n case \"fixed64\":\n case \"sfixed64\": gen\n (\"if(!util.isInteger(%s)&&!(%s&&util.isInteger(%s.low)&&util.isInteger(%s.high)))\", ref, ref, ref, ref)\n (\"return%j\", invalid(field, \"integer|Long\"));\n break;\n case \"float\":\n case \"double\": gen\n (\"if(typeof %s!==\\\"number\\\")\", ref)\n (\"return%j\", invalid(field, \"number\"));\n break;\n case \"bool\": gen\n (\"if(typeof %s!==\\\"boolean\\\")\", ref)\n (\"return%j\", invalid(field, \"boolean\"));\n break;\n case \"string\": gen\n (\"if(!util.isString(%s))\", ref)\n (\"return%j\", invalid(field, \"string\"));\n break;\n case \"bytes\": gen\n (\"if(!(%s&&typeof %s.length===\\\"number\\\"||util.isString(%s)))\", ref, ref, ref)\n (\"return%j\", invalid(field, \"buffer\"));\n break;\n }\n }\n return gen;\n /* eslint-enable no-unexpected-multiline */\n}\n\n/**\n * Generates a partial key verifier.\n * @param {Codegen} gen Codegen instance\n * @param {Field} field Reflected field\n * @param {string} ref Variable reference\n * @returns {Codegen} Codegen instance\n * @ignore\n */\nfunction genVerifyKey(gen, field, ref) {\n /* eslint-disable no-unexpected-multiline */\n switch (field.keyType) {\n case \"int32\":\n case \"uint32\":\n case \"sint32\":\n case \"fixed32\":\n case \"sfixed32\": gen\n (\"if(!util.key32Re.test(%s))\", ref)\n (\"return%j\", invalid(field, \"integer key\"));\n break;\n case \"int64\":\n case \"uint64\":\n case \"sint64\":\n case \"fixed64\":\n case \"sfixed64\": gen\n (\"if(!util.key64Re.test(%s))\", ref) // see comment above: x is ok, d is not\n (\"return%j\", invalid(field, \"integer|Long key\"));\n break;\n case \"bool\": gen\n (\"if(!util.key2Re.test(%s))\", ref)\n (\"return%j\", invalid(field, \"boolean key\"));\n break;\n }\n return gen;\n /* eslint-enable no-unexpected-multiline */\n}\n\n/**\n * Generates a verifier specific to the specified message type.\n * @param {Type} mtype Message type\n * @returns {Codegen} Codegen instance\n */\nfunction verifier(mtype) {\n /* eslint-disable no-unexpected-multiline */\n\n var gen = util.codegen([\"m\"], mtype.name + \"$verify\")\n (\"if(typeof m!==\\\"object\\\"||m===null)\")\n (\"return%j\", \"object expected\");\n var oneofs = mtype.oneofsArray,\n seenFirstField = {};\n if (oneofs.length) gen\n (\"var p={}\");\n\n for (var i = 0; i < /* initializes */ mtype.fieldsArray.length; ++i) {\n var field = mtype._fieldsArray[i].resolve(),\n ref = \"m\" + util.safeProp(field.name);\n\n if (field.optional) gen\n (\"if(%s!=null&&m.hasOwnProperty(%j)){\", ref, field.name); // !== undefined && !== null\n\n // map fields\n if (field.map) { gen\n (\"if(!util.isObject(%s))\", ref)\n (\"return%j\", invalid(field, \"object\"))\n (\"var k=Object.keys(%s)\", ref)\n (\"for(var i=0;i}\n * @const\n */\nvar wrappers = exports;\n\nvar Message = require(21);\n\n/**\n * From object converter part of an {@link IWrapper}.\n * @typedef WrapperFromObjectConverter\n * @type {function}\n * @param {Object.} object Plain object\n * @returns {Message<{}>} Message instance\n * @this Type\n */\n\n/**\n * To object converter part of an {@link IWrapper}.\n * @typedef WrapperToObjectConverter\n * @type {function}\n * @param {Message<{}>} message Message instance\n * @param {IConversionOptions} [options] Conversion options\n * @returns {Object.} Plain object\n * @this Type\n */\n\n/**\n * Common type wrapper part of {@link wrappers}.\n * @interface IWrapper\n * @property {WrapperFromObjectConverter} [fromObject] From object converter\n * @property {WrapperToObjectConverter} [toObject] To object converter\n */\n\n// Custom wrapper for Any\nwrappers[\".google.protobuf.Any\"] = {\n\n fromObject: function(object) {\n\n // unwrap value type if mapped\n if (object && object[\"@type\"]) {\n // Only use fully qualified type name after the last '/'\n var name = object[\"@type\"].substring(object[\"@type\"].lastIndexOf(\"/\") + 1);\n var type = this.lookup(name);\n /* istanbul ignore else */\n if (type) {\n // type_url does not accept leading \".\"\n var type_url = object[\"@type\"].charAt(0) === \".\" ?\n object[\"@type\"].substr(1) : object[\"@type\"];\n // type_url prefix is optional, but path seperator is required\n if (type_url.indexOf(\"/\") === -1) {\n type_url = \"/\" + type_url;\n }\n return this.create({\n type_url: type_url,\n value: type.encode(type.fromObject(object)).finish()\n });\n }\n }\n\n return this.fromObject(object);\n },\n\n toObject: function(message, options) {\n\n // Default prefix\n var googleApi = \"type.googleapis.com/\";\n var prefix = \"\";\n var name = \"\";\n\n // decode value if requested and unmapped\n if (options && options.json && message.type_url && message.value) {\n // Only use fully qualified type name after the last '/'\n name = message.type_url.substring(message.type_url.lastIndexOf(\"/\") + 1);\n // Separate the prefix used\n prefix = message.type_url.substring(0, message.type_url.lastIndexOf(\"/\") + 1);\n var type = this.lookup(name);\n /* istanbul ignore else */\n if (type)\n message = type.decode(message.value);\n }\n\n // wrap value if unmapped\n if (!(message instanceof this.ctor) && message instanceof Message) {\n var object = message.$type.toObject(message, options);\n var messageName = message.$type.fullName[0] === \".\" ?\n message.$type.fullName.substr(1) : message.$type.fullName;\n // Default to type.googleapis.com prefix if no prefix is used\n if (prefix === \"\") {\n prefix = googleApi;\n }\n name = prefix + messageName;\n object[\"@type\"] = name;\n return object;\n }\n\n return this.toObject(message, options);\n }\n};\n","\"use strict\";\nmodule.exports = Writer;\n\nvar util = require(39);\n\nvar BufferWriter; // cyclic\n\nvar LongBits = util.LongBits,\n base64 = util.base64,\n utf8 = util.utf8;\n\n/**\n * Constructs a new writer operation instance.\n * @classdesc Scheduled writer operation.\n * @constructor\n * @param {function(*, Uint8Array, number)} fn Function to call\n * @param {number} len Value byte length\n * @param {*} val Value to write\n * @ignore\n */\nfunction Op(fn, len, val) {\n\n /**\n * Function to call.\n * @type {function(Uint8Array, number, *)}\n */\n this.fn = fn;\n\n /**\n * Value byte length.\n * @type {number}\n */\n this.len = len;\n\n /**\n * Next operation.\n * @type {Writer.Op|undefined}\n */\n this.next = undefined;\n\n /**\n * Value to write.\n * @type {*}\n */\n this.val = val; // type varies\n}\n\n/* istanbul ignore next */\nfunction noop() {} // eslint-disable-line no-empty-function\n\n/**\n * Constructs a new writer state instance.\n * @classdesc Copied writer state.\n * @memberof Writer\n * @constructor\n * @param {Writer} writer Writer to copy state from\n * @ignore\n */\nfunction State(writer) {\n\n /**\n * Current head.\n * @type {Writer.Op}\n */\n this.head = writer.head;\n\n /**\n * Current tail.\n * @type {Writer.Op}\n */\n this.tail = writer.tail;\n\n /**\n * Current buffer length.\n * @type {number}\n */\n this.len = writer.len;\n\n /**\n * Next state.\n * @type {State|null}\n */\n this.next = writer.states;\n}\n\n/**\n * Constructs a new writer instance.\n * @classdesc Wire format writer using `Uint8Array` if available, otherwise `Array`.\n * @constructor\n */\nfunction Writer() {\n\n /**\n * Current length.\n * @type {number}\n */\n this.len = 0;\n\n /**\n * Operations head.\n * @type {Object}\n */\n this.head = new Op(noop, 0, 0);\n\n /**\n * Operations tail\n * @type {Object}\n */\n this.tail = this.head;\n\n /**\n * Linked forked states.\n * @type {Object|null}\n */\n this.states = null;\n\n // When a value is written, the writer calculates its byte length and puts it into a linked\n // list of operations to perform when finish() is called. This both allows us to allocate\n // buffers of the exact required size and reduces the amount of work we have to do compared\n // to first calculating over objects and then encoding over objects. In our case, the encoding\n // part is just a linked list walk calling operations with already prepared values.\n}\n\nvar create = function create() {\n return util.Buffer\n ? function create_buffer_setup() {\n return (Writer.create = function create_buffer() {\n return new BufferWriter();\n })();\n }\n /* istanbul ignore next */\n : function create_array() {\n return new Writer();\n };\n};\n\n/**\n * Creates a new writer.\n * @function\n * @returns {BufferWriter|Writer} A {@link BufferWriter} when Buffers are supported, otherwise a {@link Writer}\n */\nWriter.create = create();\n\n/**\n * Allocates a buffer of the specified size.\n * @param {number} size Buffer size\n * @returns {Uint8Array} Buffer\n */\nWriter.alloc = function alloc(size) {\n return new util.Array(size);\n};\n\n// Use Uint8Array buffer pool in the browser, just like node does with buffers\n/* istanbul ignore else */\nif (util.Array !== Array)\n Writer.alloc = util.pool(Writer.alloc, util.Array.prototype.subarray);\n\n/**\n * Pushes a new operation to the queue.\n * @param {function(Uint8Array, number, *)} fn Function to call\n * @param {number} len Value byte length\n * @param {number} val Value to write\n * @returns {Writer} `this`\n * @private\n */\nWriter.prototype._push = function push(fn, len, val) {\n this.tail = this.tail.next = new Op(fn, len, val);\n this.len += len;\n return this;\n};\n\nfunction writeByte(val, buf, pos) {\n buf[pos] = val & 255;\n}\n\nfunction writeVarint32(val, buf, pos) {\n while (val > 127) {\n buf[pos++] = val & 127 | 128;\n val >>>= 7;\n }\n buf[pos] = val;\n}\n\n/**\n * Constructs a new varint writer operation instance.\n * @classdesc Scheduled varint writer operation.\n * @extends Op\n * @constructor\n * @param {number} len Value byte length\n * @param {number} val Value to write\n * @ignore\n */\nfunction VarintOp(len, val) {\n this.len = len;\n this.next = undefined;\n this.val = val;\n}\n\nVarintOp.prototype = Object.create(Op.prototype);\nVarintOp.prototype.fn = writeVarint32;\n\n/**\n * Writes an unsigned 32 bit value as a varint.\n * @param {number} value Value to write\n * @returns {Writer} `this`\n */\nWriter.prototype.uint32 = function write_uint32(value) {\n // here, the call to this.push has been inlined and a varint specific Op subclass is used.\n // uint32 is by far the most frequently used operation and benefits significantly from this.\n this.len += (this.tail = this.tail.next = new VarintOp(\n (value = value >>> 0)\n < 128 ? 1\n : value < 16384 ? 2\n : value < 2097152 ? 3\n : value < 268435456 ? 4\n : 5,\n value)).len;\n return this;\n};\n\n/**\n * Writes a signed 32 bit value as a varint.\n * @function\n * @param {number} value Value to write\n * @returns {Writer} `this`\n */\nWriter.prototype.int32 = function write_int32(value) {\n return value < 0\n ? this._push(writeVarint64, 10, LongBits.fromNumber(value)) // 10 bytes per spec\n : this.uint32(value);\n};\n\n/**\n * Writes a 32 bit value as a varint, zig-zag encoded.\n * @param {number} value Value to write\n * @returns {Writer} `this`\n */\nWriter.prototype.sint32 = function write_sint32(value) {\n return this.uint32((value << 1 ^ value >> 31) >>> 0);\n};\n\nfunction writeVarint64(val, buf, pos) {\n while (val.hi) {\n buf[pos++] = val.lo & 127 | 128;\n val.lo = (val.lo >>> 7 | val.hi << 25) >>> 0;\n val.hi >>>= 7;\n }\n while (val.lo > 127) {\n buf[pos++] = val.lo & 127 | 128;\n val.lo = val.lo >>> 7;\n }\n buf[pos++] = val.lo;\n}\n\n/**\n * Writes an unsigned 64 bit value as a varint.\n * @param {Long|number|string} value Value to write\n * @returns {Writer} `this`\n * @throws {TypeError} If `value` is a string and no long library is present.\n */\nWriter.prototype.uint64 = function write_uint64(value) {\n var bits = LongBits.from(value);\n return this._push(writeVarint64, bits.length(), bits);\n};\n\n/**\n * Writes a signed 64 bit value as a varint.\n * @function\n * @param {Long|number|string} value Value to write\n * @returns {Writer} `this`\n * @throws {TypeError} If `value` is a string and no long library is present.\n */\nWriter.prototype.int64 = Writer.prototype.uint64;\n\n/**\n * Writes a signed 64 bit value as a varint, zig-zag encoded.\n * @param {Long|number|string} value Value to write\n * @returns {Writer} `this`\n * @throws {TypeError} If `value` is a string and no long library is present.\n */\nWriter.prototype.sint64 = function write_sint64(value) {\n var bits = LongBits.from(value).zzEncode();\n return this._push(writeVarint64, bits.length(), bits);\n};\n\n/**\n * Writes a boolish value as a varint.\n * @param {boolean} value Value to write\n * @returns {Writer} `this`\n */\nWriter.prototype.bool = function write_bool(value) {\n return this._push(writeByte, 1, value ? 1 : 0);\n};\n\nfunction writeFixed32(val, buf, pos) {\n buf[pos ] = val & 255;\n buf[pos + 1] = val >>> 8 & 255;\n buf[pos + 2] = val >>> 16 & 255;\n buf[pos + 3] = val >>> 24;\n}\n\n/**\n * Writes an unsigned 32 bit value as fixed 32 bits.\n * @param {number} value Value to write\n * @returns {Writer} `this`\n */\nWriter.prototype.fixed32 = function write_fixed32(value) {\n return this._push(writeFixed32, 4, value >>> 0);\n};\n\n/**\n * Writes a signed 32 bit value as fixed 32 bits.\n * @function\n * @param {number} value Value to write\n * @returns {Writer} `this`\n */\nWriter.prototype.sfixed32 = Writer.prototype.fixed32;\n\n/**\n * Writes an unsigned 64 bit value as fixed 64 bits.\n * @param {Long|number|string} value Value to write\n * @returns {Writer} `this`\n * @throws {TypeError} If `value` is a string and no long library is present.\n */\nWriter.prototype.fixed64 = function write_fixed64(value) {\n var bits = LongBits.from(value);\n return this._push(writeFixed32, 4, bits.lo)._push(writeFixed32, 4, bits.hi);\n};\n\n/**\n * Writes a signed 64 bit value as fixed 64 bits.\n * @function\n * @param {Long|number|string} value Value to write\n * @returns {Writer} `this`\n * @throws {TypeError} If `value` is a string and no long library is present.\n */\nWriter.prototype.sfixed64 = Writer.prototype.fixed64;\n\n/**\n * Writes a float (32 bit).\n * @function\n * @param {number} value Value to write\n * @returns {Writer} `this`\n */\nWriter.prototype.float = function write_float(value) {\n return this._push(util.float.writeFloatLE, 4, value);\n};\n\n/**\n * Writes a double (64 bit float).\n * @function\n * @param {number} value Value to write\n * @returns {Writer} `this`\n */\nWriter.prototype.double = function write_double(value) {\n return this._push(util.float.writeDoubleLE, 8, value);\n};\n\nvar writeBytes = util.Array.prototype.set\n ? function writeBytes_set(val, buf, pos) {\n buf.set(val, pos); // also works for plain array values\n }\n /* istanbul ignore next */\n : function writeBytes_for(val, buf, pos) {\n for (var i = 0; i < val.length; ++i)\n buf[pos + i] = val[i];\n };\n\n/**\n * Writes a sequence of bytes.\n * @param {Uint8Array|string} value Buffer or base64 encoded string to write\n * @returns {Writer} `this`\n */\nWriter.prototype.bytes = function write_bytes(value) {\n var len = value.length >>> 0;\n if (!len)\n return this._push(writeByte, 1, 0);\n if (util.isString(value)) {\n var buf = Writer.alloc(len = base64.length(value));\n base64.decode(value, buf, 0);\n value = buf;\n }\n return this.uint32(len)._push(writeBytes, len, value);\n};\n\n/**\n * Writes a string.\n * @param {string} value Value to write\n * @returns {Writer} `this`\n */\nWriter.prototype.string = function write_string(value) {\n var len = utf8.length(value);\n return len\n ? this.uint32(len)._push(utf8.write, len, value)\n : this._push(writeByte, 1, 0);\n};\n\n/**\n * Forks this writer's state by pushing it to a stack.\n * Calling {@link Writer#reset|reset} or {@link Writer#ldelim|ldelim} resets the writer to the previous state.\n * @returns {Writer} `this`\n */\nWriter.prototype.fork = function fork() {\n this.states = new State(this);\n this.head = this.tail = new Op(noop, 0, 0);\n this.len = 0;\n return this;\n};\n\n/**\n * Resets this instance to the last state.\n * @returns {Writer} `this`\n */\nWriter.prototype.reset = function reset() {\n if (this.states) {\n this.head = this.states.head;\n this.tail = this.states.tail;\n this.len = this.states.len;\n this.states = this.states.next;\n } else {\n this.head = this.tail = new Op(noop, 0, 0);\n this.len = 0;\n }\n return this;\n};\n\n/**\n * Resets to the last state and appends the fork state's current write length as a varint followed by its operations.\n * @returns {Writer} `this`\n */\nWriter.prototype.ldelim = function ldelim() {\n var head = this.head,\n tail = this.tail,\n len = this.len;\n this.reset().uint32(len);\n if (len) {\n this.tail.next = head.next; // skip noop\n this.tail = tail;\n this.len += len;\n }\n return this;\n};\n\n/**\n * Finishes the write operation.\n * @returns {Uint8Array} Finished buffer\n */\nWriter.prototype.finish = function finish() {\n var head = this.head.next, // skip noop\n buf = this.constructor.alloc(this.len),\n pos = 0;\n while (head) {\n head.fn(head.val, buf, pos);\n pos += head.len;\n head = head.next;\n }\n // this.head = this.tail = null;\n return buf;\n};\n\nWriter._configure = function(BufferWriter_) {\n BufferWriter = BufferWriter_;\n Writer.create = create();\n BufferWriter._configure();\n};\n","\"use strict\";\nmodule.exports = BufferWriter;\n\n// extends Writer\nvar Writer = require(42);\n(BufferWriter.prototype = Object.create(Writer.prototype)).constructor = BufferWriter;\n\nvar util = require(39);\n\n/**\n * Constructs a new buffer writer instance.\n * @classdesc Wire format writer using node buffers.\n * @extends Writer\n * @constructor\n */\nfunction BufferWriter() {\n Writer.call(this);\n}\n\nBufferWriter._configure = function () {\n /**\n * Allocates a buffer of the specified size.\n * @function\n * @param {number} size Buffer size\n * @returns {Buffer} Buffer\n */\n BufferWriter.alloc = util._Buffer_allocUnsafe;\n\n BufferWriter.writeBytesBuffer = util.Buffer && util.Buffer.prototype instanceof Uint8Array && util.Buffer.prototype.set.name === \"set\"\n ? function writeBytesBuffer_set(val, buf, pos) {\n buf.set(val, pos); // faster than copy (requires node >= 4 where Buffers extend Uint8Array and set is properly inherited)\n // also works for plain array values\n }\n /* istanbul ignore next */\n : function writeBytesBuffer_copy(val, buf, pos) {\n if (val.copy) // Buffer values\n val.copy(buf, pos, 0, val.length);\n else for (var i = 0; i < val.length;) // plain array values\n buf[pos++] = val[i++];\n };\n};\n\n\n/**\n * @override\n */\nBufferWriter.prototype.bytes = function write_bytes_buffer(value) {\n if (util.isString(value))\n value = util._Buffer_from(value, \"base64\");\n var len = value.length >>> 0;\n this.uint32(len);\n if (len)\n this._push(BufferWriter.writeBytesBuffer, len, value);\n return this;\n};\n\nfunction writeStringBuffer(val, buf, pos) {\n if (val.length < 40) // plain js is faster for short strings (probably due to redundant assertions)\n util.utf8.write(val, buf, pos);\n else if (buf.utf8Write)\n buf.utf8Write(val, pos);\n else\n buf.write(val, pos);\n}\n\n/**\n * @override\n */\nBufferWriter.prototype.string = function write_string_buffer(value) {\n var len = util.Buffer.byteLength(value);\n this.uint32(len);\n if (len)\n this._push(writeStringBuffer, len, value);\n return this;\n};\n\n\n/**\n * Finishes the write operation.\n * @name BufferWriter#finish\n * @function\n * @returns {Buffer} Finished buffer\n */\n\nBufferWriter._configure();\n"],"sourceRoot":"."} \ No newline at end of file diff --git a/src/frontend/Hanami-AI-Dashboard/.gitmodules b/src/frontend/Hanami-AI-Dashboard/.gitmodules new file mode 100644 index 00000000..f951b64d --- /dev/null +++ b/src/frontend/Hanami-AI-Dashboard/.gitmodules @@ -0,0 +1,9 @@ +[submodule "src/libKitsunemimiHanamiMessages"] + path = src/libKitsunemimiHanamiMessages + url = git@github.com:kitsudaiki/libKitsunemimiHanamiMessages.git +[submodule "src/libHanamiAiSdk"] + path = src/libHanamiAiSdk + url = git@github.com:kitsudaiki/libHanamiAiSdk.git +[submodule "src/Hanami-AI-Dashboard-Dependencies"] + path = src/Hanami-AI-Dashboard-Dependencies + url = git@github.com:kitsudaiki/Hanami-AI-Dashboard-Dependencies.git diff --git a/src/frontend/Hanami-AI-Dashboard/CHANGELOG_old b/src/frontend/Hanami-AI-Dashboard/CHANGELOG_old new file mode 100644 index 00000000..6990a91d --- /dev/null +++ b/src/frontend/Hanami-AI-Dashboard/CHANGELOG_old @@ -0,0 +1,9 @@ +# Changelog + +## [0.1.0] - 2021-01-02 + +### Added +- initial version for first tests and debugging + + + diff --git a/src/frontend/Hanami-AI-Dashboard/README.md b/src/frontend/Hanami-AI-Dashboard/README.md new file mode 100644 index 00000000..d8c58365 --- /dev/null +++ b/src/frontend/Hanami-AI-Dashboard/README.md @@ -0,0 +1,10 @@ +# Hanami-AI-Dashboard + +## Description + +Dashboard of the Hanami-AI-Project: https://github.com/kitsudaiki/Hanami-AI + +## License + +This project is licensed under the Apache License Version 2.0 - see the [LICENSE](LICENSE) file for details + diff --git a/src/frontend/Hanami-AI-Dashboard/src/Hanami-AI-Dashboard-Dependencies/README.md b/src/frontend/Hanami-AI-Dashboard/src/Hanami-AI-Dashboard-Dependencies/README.md new file mode 100644 index 00000000..12462e88 --- /dev/null +++ b/src/frontend/Hanami-AI-Dashboard/src/Hanami-AI-Dashboard-Dependencies/README.md @@ -0,0 +1,25 @@ +# Hanami-AI-Dashboard-Dependencies + +This repo contains all foreign dependencies, which are used by the Hanami-AI-Dashboard (https://github.com/kitsudaiki/Hanami-AI-Dashboard): + +`d3.v6.min.js` + +- **source**: https://d3js.org/d3.v6.min.js +- **related repository**: https://github.com/d3/d3 + +`jquery.min.js` + +- **source**: https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js +- **related repository**: https://github.com/jquery/jquery + +`protobuf.min.js` and `protobuf.min.js.map` + +- **source**: + - https://cdn.rawgit.com/dcodeIO/protobuf.js/6.11.3/dist/protobuf.min.js + - https://cdn.rawgit.com/dcodeIO/protobuf.js/6.11.3/dist/protobuf.min.js.map +- **related repository**: https://github.com/protobufjs/protobuf.js/ + +`bootstrap_icons` +- **source-site**: https://icons.getbootstrap.com/ + +The files in this repository have no changes compared to the originals. It is basically only a mirror. This is primary to make the HanamiAI also completely without internet-access executable, if necessary. diff --git a/src/frontend/Hanami-AI-Dashboard/src/Hanami-AI-Dashboard-Dependencies/bootstrap_icons/check-lg.svg b/src/frontend/Hanami-AI-Dashboard/src/Hanami-AI-Dashboard-Dependencies/bootstrap_icons/check-lg.svg new file mode 100644 index 00000000..7afb0ae1 --- /dev/null +++ b/src/frontend/Hanami-AI-Dashboard/src/Hanami-AI-Dashboard-Dependencies/bootstrap_icons/check-lg.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/src/frontend/Hanami-AI-Dashboard/src/Hanami-AI-Dashboard-Dependencies/bootstrap_icons/x-lg.svg b/src/frontend/Hanami-AI-Dashboard/src/Hanami-AI-Dashboard-Dependencies/bootstrap_icons/x-lg.svg new file mode 100644 index 00000000..53aec00d --- /dev/null +++ b/src/frontend/Hanami-AI-Dashboard/src/Hanami-AI-Dashboard-Dependencies/bootstrap_icons/x-lg.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/src/frontend/Hanami-AI-Dashboard/src/Hanami-AI-Dashboard-Dependencies/d3.v6.min.js b/src/frontend/Hanami-AI-Dashboard/src/Hanami-AI-Dashboard-Dependencies/d3.v6.min.js new file mode 100644 index 00000000..05cd5cae --- /dev/null +++ b/src/frontend/Hanami-AI-Dashboard/src/Hanami-AI-Dashboard-Dependencies/d3.v6.min.js @@ -0,0 +1,2 @@ +// https://d3js.org v6.7.0 Copyright 2021 Mike Bostock +!function(t,n){"object"==typeof exports&&"undefined"!=typeof module?n(exports):"function"==typeof define&&define.amd?define(["exports"],n):n((t="undefined"!=typeof globalThis?globalThis:t||self).d3=t.d3||{})}(this,(function(t){"use strict";function n(t,n){return tn?1:t>=n?0:NaN}function e(t){let e=t,r=t;function i(t,n,e,i){for(null==e&&(e=0),null==i&&(i=t.length);e>>1;r(t[o],n)<0?e=o+1:i=o}return e}return 1===t.length&&(e=(n,e)=>t(n)-e,r=function(t){return(e,r)=>n(t(e),r)}(t)),{left:i,center:function(t,n,r,o){null==r&&(r=0),null==o&&(o=t.length);const a=i(t,n,r,o-1);return a>r&&e(t[a-1],n)>-e(t[a],n)?a-1:a},right:function(t,n,e,i){for(null==e&&(e=0),null==i&&(i=t.length);e>>1;r(t[o],n)>0?i=o:e=o+1}return e}}}function r(t){return null===t?NaN:+t}const i=e(n),o=i.right,a=i.left,u=e(r).center;function c(t,n){let e=0;if(void 0===n)for(let n of t)null!=n&&(n=+n)>=n&&++e;else{let r=-1;for(let i of t)null!=(i=n(i,++r,t))&&(i=+i)>=i&&++e}return e}function f(t){return 0|t.length}function s(t){return!(t>0)}function l(t){return"object"!=typeof t||"length"in t?t:Array.from(t)}function h(t,n){let e,r=0,i=0,o=0;if(void 0===n)for(let n of t)null!=n&&(n=+n)>=n&&(e=n-i,i+=e/++r,o+=e*(n-i));else{let a=-1;for(let u of t)null!=(u=n(u,++a,t))&&(u=+u)>=u&&(e=u-i,i+=e/++r,o+=e*(u-i))}if(r>1)return o/(r-1)}function d(t,n){const e=h(t,n);return e?Math.sqrt(e):e}function p(t,n){let e,r;if(void 0===n)for(const n of t)null!=n&&(void 0===e?n>=n&&(e=r=n):(e>n&&(e=n),r=o&&(e=r=o):(e>o&&(e=o),r0){for(o=t[--i];i>0&&(n=o,e=t[--i],o=n+e,r=e-(o-n),!r););i>0&&(r<0&&t[i-1]<0||r>0&&t[i-1]>0)&&(e=2*r,n=o+e,e==n-o&&(o=n))}return o}}class y extends Map{constructor(t,n=x){if(super(),Object.defineProperties(this,{_intern:{value:new Map},_key:{value:n}}),null!=t)for(const[n,e]of t)this.set(n,e)}get(t){return super.get(_(this,t))}has(t){return super.has(_(this,t))}set(t,n){return super.set(b(this,t),n)}delete(t){return super.delete(m(this,t))}}class v extends Set{constructor(t,n=x){if(super(),Object.defineProperties(this,{_intern:{value:new Map},_key:{value:n}}),null!=t)for(const n of t)this.add(n)}has(t){return super.has(_(this,t))}add(t){return super.add(b(this,t))}delete(t){return super.delete(m(this,t))}}function _({_intern:t,_key:n},e){const r=n(e);return t.has(r)?t.get(r):e}function b({_intern:t,_key:n},e){const r=n(e);return t.has(r)?t.get(r):(t.set(r,e),e)}function m({_intern:t,_key:n},e){const r=n(e);return t.has(r)&&(e=t.get(e),t.delete(r)),e}function x(t){return null!==t&&"object"==typeof t?t.valueOf():t}function w(t){return t}function M(t,...n){return S(t,w,w,n)}function A(t,n,...e){return S(t,w,n,e)}function T(t){if(1!==t.length)throw new Error("duplicate key");return t[0]}function S(t,n,e,r){return function t(i,o){if(o>=r.length)return e(i);const a=new y,u=r[o++];let c=-1;for(const t of i){const n=u(t,++c,i),e=a.get(n);e?e.push(t):a.set(n,[t])}for(const[n,e]of a)a.set(n,t(e,o));return n(a)}(t,0)}function E(t,n){return Array.from(n,(n=>t[n]))}function k(t,...e){if("function"!=typeof t[Symbol.iterator])throw new TypeError("values is not iterable");t=Array.from(t);let[r=n]=e;if(1===r.length||e.length>1){const i=Uint32Array.from(t,((t,n)=>n));return e.length>1?(e=e.map((n=>t.map(n))),i.sort(((t,r)=>{for(const i of e){const e=n(i[t],i[r]);if(e)return e}}))):(r=t.map(r),i.sort(((t,e)=>n(r[t],r[e])))),E(t,i)}return t.sort(r)}var N=Array.prototype.slice;function C(t){return function(){return t}}var P=Math.sqrt(50),z=Math.sqrt(10),D=Math.sqrt(2);function q(t,n,e){var r,i,o,a,u=-1;if(e=+e,(t=+t)===(n=+n)&&e>0)return[t];if((r=n0){let e=Math.round(t/a),r=Math.round(n/a);for(e*an&&--r,o=new Array(i=r-e+1);++un&&--r,o=new Array(i=r-e+1);++u=0?(o>=P?10:o>=z?5:o>=D?2:1)*Math.pow(10,i):-Math.pow(10,-i)/(o>=P?10:o>=z?5:o>=D?2:1)}function F(t,n,e){var r=Math.abs(n-t)/Math.max(0,e),i=Math.pow(10,Math.floor(Math.log(r)/Math.LN10)),o=r/i;return o>=P?i*=10:o>=z?i*=5:o>=D&&(i*=2),n0?(t=Math.floor(t/i)*i,n=Math.ceil(n/i)*i):i<0&&(t=Math.ceil(t*i)/i,n=Math.floor(n*i)/i),r=i}}function I(t){return Math.ceil(Math.log(c(t))/Math.LN2)+1}function U(){var t=w,n=p,e=I;function r(r){Array.isArray(r)||(r=Array.from(r));var i,a,u=r.length,c=new Array(u);for(i=0;i=l)if(t>=l&&n===p){const t=R(s,l,e);isFinite(t)&&(t>0?l=(Math.floor(l/t)+1)*t:t<0&&(l=(Math.ceil(l*-t)+1)/-t))}else h.pop()}for(var d=h.length;h[0]<=s;)h.shift(),--d;for(;h[d-1]>l;)h.pop(),--d;var g,y=new Array(d+1);for(i=0;i<=d;++i)(g=y[i]=[]).x0=i>0?h[i-1]:s,g.x1=i=n)&&(e=n);else{let r=-1;for(let i of t)null!=(i=n(i,++r,t))&&(e=i)&&(e=i)}return e}function Y(t,n){let e;if(void 0===n)for(const n of t)null!=n&&(e>n||void 0===e&&n>=n)&&(e=n);else{let r=-1;for(let i of t)null!=(i=n(i,++r,t))&&(e>i||void 0===e&&i>=i)&&(e=i)}return e}function L(t,e,r=0,i=t.length-1,o=n){for(;i>r;){if(i-r>600){const n=i-r+1,a=e-r+1,u=Math.log(n),c=.5*Math.exp(2*u/3),f=.5*Math.sqrt(u*c*(n-c)/n)*(a-n/2<0?-1:1);L(t,e,Math.max(r,Math.floor(e-a*c/n+f)),Math.min(i,Math.floor(e+(n-a)*c/n+f)),o)}const n=t[e];let a=r,u=i;for(j(t,r,e),o(t[i],n)>0&&j(t,r,i);a0;)--u}0===o(t[r],n)?j(t,r,u):(++u,j(t,u,i)),u<=e&&(r=u+1),e<=u&&(i=u-1)}return t}function j(t,n,e){const r=t[n];t[n]=t[e],t[e]=r}function H(t,n,e){if(r=(t=Float64Array.from(function*(t,n){if(void 0===n)for(let n of t)null!=n&&(n=+n)>=n&&(yield n);else{let e=-1;for(let r of t)null!=(r=n(r,++e,t))&&(r=+r)>=r&&(yield r)}}(t,e))).length){if((n=+n)<=0||r<2)return Y(t);if(n>=1)return B(t);var r,i=(r-1)*n,o=Math.floor(i),a=B(L(t,o).subarray(0,o+1));return a+(Y(t.subarray(o+1))-a)*(i-o)}}function X(t,n,e=r){if(i=t.length){if((n=+n)<=0||i<2)return+e(t[0],0,t);if(n>=1)return+e(t[i-1],i-1,t);var i,o=(i-1)*n,a=Math.floor(o),u=+e(t[a],a,t);return u+(+e(t[a+1],a+1,t)-u)*(o-a)}}function G(t,n){let e,r=-1,i=-1;if(void 0===n)for(const n of t)++i,null!=n&&(e=n)&&(e=n,r=i);else for(let o of t)null!=(o=n(o,++i,t))&&(e=o)&&(e=o,r=i);return r}function V(t){return Array.from(function*(t){for(const n of t)yield*n}(t))}function $(t,n){let e,r=-1,i=-1;if(void 0===n)for(const n of t)++i,null!=n&&(e>n||void 0===e&&n>=n)&&(e=n,r=i);else for(let o of t)null!=(o=n(o,++i,t))&&(e>o||void 0===e&&o>=o)&&(e=o,r=i);return r}function W(t,n){return[t,n]}function Z(t,n,e){t=+t,n=+n,e=(i=arguments.length)<2?(n=t,t=0,1):i<3?1:+e;for(var r=-1,i=0|Math.max(0,Math.ceil((n-t)/e)),o=new Array(i);++r+t(n)}function st(t,n){return n=Math.max(0,t.bandwidth()-2*n)/2,t.round()&&(n=Math.round(n)),e=>+t(e)+n}function lt(){return!this.__axis}function ht(t,n){var e=[],r=null,i=null,o=6,a=6,u=3,c="undefined"!=typeof window&&window.devicePixelRatio>1?0:.5,f=1===t||4===t?-1:1,s=4===t||2===t?"x":"y",l=1===t||3===t?ut:ct;function h(h){var d=null==r?n.ticks?n.ticks.apply(n,e):n.domain():r,p=null==i?n.tickFormat?n.tickFormat.apply(n,e):ot:i,g=Math.max(o,0)+u,y=n.range(),v=+y[0]+c,_=+y[y.length-1]+c,b=(n.bandwidth?st:ft)(n.copy(),c),m=h.selection?h.selection():h,x=m.selectAll(".domain").data([null]),w=m.selectAll(".tick").data(d,n).order(),M=w.exit(),A=w.enter().append("g").attr("class","tick"),T=w.select("line"),S=w.select("text");x=x.merge(x.enter().insert("path",".tick").attr("class","domain").attr("stroke","currentColor")),w=w.merge(A),T=T.merge(A.append("line").attr("stroke","currentColor").attr(s+"2",f*o)),S=S.merge(A.append("text").attr("fill","currentColor").attr(s,f*g).attr("dy",1===t?"0em":3===t?"0.71em":"0.32em")),h!==m&&(x=x.transition(h),w=w.transition(h),T=T.transition(h),S=S.transition(h),M=M.transition(h).attr("opacity",at).attr("transform",(function(t){return isFinite(t=b(t))?l(t+c):this.getAttribute("transform")})),A.attr("opacity",at).attr("transform",(function(t){var n=this.parentNode.__axis;return l((n&&isFinite(n=n(t))?n:b(t))+c)}))),M.remove(),x.attr("d",4===t||2===t?a?"M"+f*a+","+v+"H"+c+"V"+_+"H"+f*a:"M"+c+","+v+"V"+_:a?"M"+v+","+f*a+"V"+c+"H"+_+"V"+f*a:"M"+v+","+c+"H"+_),w.attr("opacity",1).attr("transform",(function(t){return l(b(t)+c)})),T.attr(s+"2",f*o),S.attr(s,f*g).text(p),m.filter(lt).attr("fill","none").attr("font-size",10).attr("font-family","sans-serif").attr("text-anchor",2===t?"start":4===t?"end":"middle"),m.each((function(){this.__axis=b}))}return h.scale=function(t){return arguments.length?(n=t,h):n},h.ticks=function(){return e=it.call(arguments),h},h.tickArguments=function(t){return arguments.length?(e=null==t?[]:it.call(t),h):e.slice()},h.tickValues=function(t){return arguments.length?(r=null==t?null:it.call(t),h):r&&r.slice()},h.tickFormat=function(t){return arguments.length?(i=t,h):i},h.tickSize=function(t){return arguments.length?(o=a=+t,h):o},h.tickSizeInner=function(t){return arguments.length?(o=+t,h):o},h.tickSizeOuter=function(t){return arguments.length?(a=+t,h):a},h.tickPadding=function(t){return arguments.length?(u=+t,h):u},h.offset=function(t){return arguments.length?(c=+t,h):c},h}var dt={value:()=>{}};function pt(){for(var t,n=0,e=arguments.length,r={};n=0&&(e=t.slice(r+1),t=t.slice(0,r)),t&&!n.hasOwnProperty(t))throw new Error("unknown type: "+t);return{type:t,name:e}}))}function vt(t,n){for(var e,r=0,i=t.length;r0)for(var e,r,i=new Array(e),o=0;o=0&&"xmlns"!==(n=t.slice(0,e))&&(t=t.slice(e+1)),mt.hasOwnProperty(n)?{space:mt[n],local:t}:t}function wt(t){return function(){var n=this.ownerDocument,e=this.namespaceURI;return e===bt&&n.documentElement.namespaceURI===bt?n.createElement(t):n.createElementNS(e,t)}}function Mt(t){return function(){return this.ownerDocument.createElementNS(t.space,t.local)}}function At(t){var n=xt(t);return(n.local?Mt:wt)(n)}function Tt(){}function St(t){return null==t?Tt:function(){return this.querySelector(t)}}function Et(t){return"object"==typeof t&&"length"in t?t:Array.from(t)}function kt(){return[]}function Nt(t){return null==t?kt:function(){return this.querySelectorAll(t)}}function Ct(t){return function(){return this.matches(t)}}function Pt(t){return function(n){return n.matches(t)}}var zt=Array.prototype.find;function Dt(){return this.firstElementChild}var qt=Array.prototype.filter;function Rt(){return this.children}function Ft(t){return new Array(t.length)}function Ot(t,n){this.ownerDocument=t.ownerDocument,this.namespaceURI=t.namespaceURI,this._next=null,this._parent=t,this.__data__=n}function It(t){return function(){return t}}function Ut(t,n,e,r,i,o){for(var a,u=0,c=n.length,f=o.length;un?1:t>=n?0:NaN}function jt(t){return function(){this.removeAttribute(t)}}function Ht(t){return function(){this.removeAttributeNS(t.space,t.local)}}function Xt(t,n){return function(){this.setAttribute(t,n)}}function Gt(t,n){return function(){this.setAttributeNS(t.space,t.local,n)}}function Vt(t,n){return function(){var e=n.apply(this,arguments);null==e?this.removeAttribute(t):this.setAttribute(t,e)}}function $t(t,n){return function(){var e=n.apply(this,arguments);null==e?this.removeAttributeNS(t.space,t.local):this.setAttributeNS(t.space,t.local,e)}}function Wt(t){return t.ownerDocument&&t.ownerDocument.defaultView||t.document&&t||t.defaultView}function Zt(t){return function(){this.style.removeProperty(t)}}function Kt(t,n,e){return function(){this.style.setProperty(t,n,e)}}function Qt(t,n,e){return function(){var r=n.apply(this,arguments);null==r?this.style.removeProperty(t):this.style.setProperty(t,r,e)}}function Jt(t,n){return t.style.getPropertyValue(n)||Wt(t).getComputedStyle(t,null).getPropertyValue(n)}function tn(t){return function(){delete this[t]}}function nn(t,n){return function(){this[t]=n}}function en(t,n){return function(){var e=n.apply(this,arguments);null==e?delete this[t]:this[t]=e}}function rn(t){return t.trim().split(/^|\s+/)}function on(t){return t.classList||new an(t)}function an(t){this._node=t,this._names=rn(t.getAttribute("class")||"")}function un(t,n){for(var e=on(t),r=-1,i=n.length;++r=0&&(n=t.slice(e+1),t=t.slice(0,e)),{type:t,name:n}}))}function Tn(t){return function(){var n=this.__on;if(n){for(var e,r=0,i=-1,o=n.length;r=0&&(this._names.splice(n,1),this._node.setAttribute("class",this._names.join(" ")))},contains:function(t){return this._names.indexOf(t)>=0}};var Cn=[null];function Pn(t,n){this._groups=t,this._parents=n}function zn(){return new Pn([[document.documentElement]],Cn)}function Dn(t){return"string"==typeof t?new Pn([[document.querySelector(t)]],[document.documentElement]):new Pn([[t]],Cn)}Pn.prototype=zn.prototype={constructor:Pn,select:function(t){"function"!=typeof t&&(t=St(t));for(var n=this._groups,e=n.length,r=new Array(e),i=0;i=x&&(x=m+1);!(b=y[x])&&++x=0;)(r=i[o])&&(a&&4^r.compareDocumentPosition(a)&&a.parentNode.insertBefore(r,a),a=r);return this},sort:function(t){function n(n,e){return n&&e?t(n.__data__,e.__data__):!n-!e}t||(t=Lt);for(var e=this._groups,r=e.length,i=new Array(r),o=0;o1?this.each((null==n?Zt:"function"==typeof n?Qt:Kt)(t,n,null==e?"":e)):Jt(this.node(),t)},property:function(t,n){return arguments.length>1?this.each((null==n?tn:"function"==typeof n?en:nn)(t,n)):this.node()[t]},classed:function(t,n){var e=rn(t+"");if(arguments.length<2){for(var r=on(this.node()),i=-1,o=e.length;++i()=>t;function Hn(t,{sourceEvent:n,subject:e,target:r,identifier:i,active:o,x:a,y:u,dx:c,dy:f,dispatch:s}){Object.defineProperties(this,{type:{value:t,enumerable:!0,configurable:!0},sourceEvent:{value:n,enumerable:!0,configurable:!0},subject:{value:e,enumerable:!0,configurable:!0},target:{value:r,enumerable:!0,configurable:!0},identifier:{value:i,enumerable:!0,configurable:!0},active:{value:o,enumerable:!0,configurable:!0},x:{value:a,enumerable:!0,configurable:!0},y:{value:u,enumerable:!0,configurable:!0},dx:{value:c,enumerable:!0,configurable:!0},dy:{value:f,enumerable:!0,configurable:!0},_:{value:s}})}function Xn(t){return!t.ctrlKey&&!t.button}function Gn(){return this.parentNode}function Vn(t,n){return null==n?{x:t.x,y:t.y}:n}function $n(){return navigator.maxTouchPoints||"ontouchstart"in this}function Wn(t,n,e){t.prototype=n.prototype=e,e.constructor=t}function Zn(t,n){var e=Object.create(t.prototype);for(var r in n)e[r]=n[r];return e}function Kn(){}Hn.prototype.on=function(){var t=this._.on.apply(this._,arguments);return t===this._?this:t};var Qn=.7,Jn=1/Qn,te="\\s*([+-]?\\d+)\\s*",ne="\\s*([+-]?\\d*\\.?\\d+(?:[eE][+-]?\\d+)?)\\s*",ee="\\s*([+-]?\\d*\\.?\\d+(?:[eE][+-]?\\d+)?)%\\s*",re=/^#([0-9a-f]{3,8})$/,ie=new RegExp("^rgb\\("+[te,te,te]+"\\)$"),oe=new RegExp("^rgb\\("+[ee,ee,ee]+"\\)$"),ae=new RegExp("^rgba\\("+[te,te,te,ne]+"\\)$"),ue=new RegExp("^rgba\\("+[ee,ee,ee,ne]+"\\)$"),ce=new RegExp("^hsl\\("+[ne,ee,ee]+"\\)$"),fe=new RegExp("^hsla\\("+[ne,ee,ee,ne]+"\\)$"),se={aliceblue:15792383,antiquewhite:16444375,aqua:65535,aquamarine:8388564,azure:15794175,beige:16119260,bisque:16770244,black:0,blanchedalmond:16772045,blue:255,blueviolet:9055202,brown:10824234,burlywood:14596231,cadetblue:6266528,chartreuse:8388352,chocolate:13789470,coral:16744272,cornflowerblue:6591981,cornsilk:16775388,crimson:14423100,cyan:65535,darkblue:139,darkcyan:35723,darkgoldenrod:12092939,darkgray:11119017,darkgreen:25600,darkgrey:11119017,darkkhaki:12433259,darkmagenta:9109643,darkolivegreen:5597999,darkorange:16747520,darkorchid:10040012,darkred:9109504,darksalmon:15308410,darkseagreen:9419919,darkslateblue:4734347,darkslategray:3100495,darkslategrey:3100495,darkturquoise:52945,darkviolet:9699539,deeppink:16716947,deepskyblue:49151,dimgray:6908265,dimgrey:6908265,dodgerblue:2003199,firebrick:11674146,floralwhite:16775920,forestgreen:2263842,fuchsia:16711935,gainsboro:14474460,ghostwhite:16316671,gold:16766720,goldenrod:14329120,gray:8421504,green:32768,greenyellow:11403055,grey:8421504,honeydew:15794160,hotpink:16738740,indianred:13458524,indigo:4915330,ivory:16777200,khaki:15787660,lavender:15132410,lavenderblush:16773365,lawngreen:8190976,lemonchiffon:16775885,lightblue:11393254,lightcoral:15761536,lightcyan:14745599,lightgoldenrodyellow:16448210,lightgray:13882323,lightgreen:9498256,lightgrey:13882323,lightpink:16758465,lightsalmon:16752762,lightseagreen:2142890,lightskyblue:8900346,lightslategray:7833753,lightslategrey:7833753,lightsteelblue:11584734,lightyellow:16777184,lime:65280,limegreen:3329330,linen:16445670,magenta:16711935,maroon:8388608,mediumaquamarine:6737322,mediumblue:205,mediumorchid:12211667,mediumpurple:9662683,mediumseagreen:3978097,mediumslateblue:8087790,mediumspringgreen:64154,mediumturquoise:4772300,mediumvioletred:13047173,midnightblue:1644912,mintcream:16121850,mistyrose:16770273,moccasin:16770229,navajowhite:16768685,navy:128,oldlace:16643558,olive:8421376,olivedrab:7048739,orange:16753920,orangered:16729344,orchid:14315734,palegoldenrod:15657130,palegreen:10025880,paleturquoise:11529966,palevioletred:14381203,papayawhip:16773077,peachpuff:16767673,peru:13468991,pink:16761035,plum:14524637,powderblue:11591910,purple:8388736,rebeccapurple:6697881,red:16711680,rosybrown:12357519,royalblue:4286945,saddlebrown:9127187,salmon:16416882,sandybrown:16032864,seagreen:3050327,seashell:16774638,sienna:10506797,silver:12632256,skyblue:8900331,slateblue:6970061,slategray:7372944,slategrey:7372944,snow:16775930,springgreen:65407,steelblue:4620980,tan:13808780,teal:32896,thistle:14204888,tomato:16737095,turquoise:4251856,violet:15631086,wheat:16113331,white:16777215,whitesmoke:16119285,yellow:16776960,yellowgreen:10145074};function le(){return this.rgb().formatHex()}function he(){return this.rgb().formatRgb()}function de(t){var n,e;return t=(t+"").trim().toLowerCase(),(n=re.exec(t))?(e=n[1].length,n=parseInt(n[1],16),6===e?pe(n):3===e?new _e(n>>8&15|n>>4&240,n>>4&15|240&n,(15&n)<<4|15&n,1):8===e?ge(n>>24&255,n>>16&255,n>>8&255,(255&n)/255):4===e?ge(n>>12&15|n>>8&240,n>>8&15|n>>4&240,n>>4&15|240&n,((15&n)<<4|15&n)/255):null):(n=ie.exec(t))?new _e(n[1],n[2],n[3],1):(n=oe.exec(t))?new _e(255*n[1]/100,255*n[2]/100,255*n[3]/100,1):(n=ae.exec(t))?ge(n[1],n[2],n[3],n[4]):(n=ue.exec(t))?ge(255*n[1]/100,255*n[2]/100,255*n[3]/100,n[4]):(n=ce.exec(t))?we(n[1],n[2]/100,n[3]/100,1):(n=fe.exec(t))?we(n[1],n[2]/100,n[3]/100,n[4]):se.hasOwnProperty(t)?pe(se[t]):"transparent"===t?new _e(NaN,NaN,NaN,0):null}function pe(t){return new _e(t>>16&255,t>>8&255,255&t,1)}function ge(t,n,e,r){return r<=0&&(t=n=e=NaN),new _e(t,n,e,r)}function ye(t){return t instanceof Kn||(t=de(t)),t?new _e((t=t.rgb()).r,t.g,t.b,t.opacity):new _e}function ve(t,n,e,r){return 1===arguments.length?ye(t):new _e(t,n,e,null==r?1:r)}function _e(t,n,e,r){this.r=+t,this.g=+n,this.b=+e,this.opacity=+r}function be(){return"#"+xe(this.r)+xe(this.g)+xe(this.b)}function me(){var t=this.opacity;return(1===(t=isNaN(t)?1:Math.max(0,Math.min(1,t)))?"rgb(":"rgba(")+Math.max(0,Math.min(255,Math.round(this.r)||0))+", "+Math.max(0,Math.min(255,Math.round(this.g)||0))+", "+Math.max(0,Math.min(255,Math.round(this.b)||0))+(1===t?")":", "+t+")")}function xe(t){return((t=Math.max(0,Math.min(255,Math.round(t)||0)))<16?"0":"")+t.toString(16)}function we(t,n,e,r){return r<=0?t=n=e=NaN:e<=0||e>=1?t=n=NaN:n<=0&&(t=NaN),new Te(t,n,e,r)}function Me(t){if(t instanceof Te)return new Te(t.h,t.s,t.l,t.opacity);if(t instanceof Kn||(t=de(t)),!t)return new Te;if(t instanceof Te)return t;var n=(t=t.rgb()).r/255,e=t.g/255,r=t.b/255,i=Math.min(n,e,r),o=Math.max(n,e,r),a=NaN,u=o-i,c=(o+i)/2;return u?(a=n===o?(e-r)/u+6*(e0&&c<1?0:a,new Te(a,u,c,t.opacity)}function Ae(t,n,e,r){return 1===arguments.length?Me(t):new Te(t,n,e,null==r?1:r)}function Te(t,n,e,r){this.h=+t,this.s=+n,this.l=+e,this.opacity=+r}function Se(t,n,e){return 255*(t<60?n+(e-n)*t/60:t<180?e:t<240?n+(e-n)*(240-t)/60:n)}Wn(Kn,de,{copy:function(t){return Object.assign(new this.constructor,this,t)},displayable:function(){return this.rgb().displayable()},hex:le,formatHex:le,formatHsl:function(){return Me(this).formatHsl()},formatRgb:he,toString:he}),Wn(_e,ve,Zn(Kn,{brighter:function(t){return t=null==t?Jn:Math.pow(Jn,t),new _e(this.r*t,this.g*t,this.b*t,this.opacity)},darker:function(t){return t=null==t?Qn:Math.pow(Qn,t),new _e(this.r*t,this.g*t,this.b*t,this.opacity)},rgb:function(){return this},displayable:function(){return-.5<=this.r&&this.r<255.5&&-.5<=this.g&&this.g<255.5&&-.5<=this.b&&this.b<255.5&&0<=this.opacity&&this.opacity<=1},hex:be,formatHex:be,formatRgb:me,toString:me})),Wn(Te,Ae,Zn(Kn,{brighter:function(t){return t=null==t?Jn:Math.pow(Jn,t),new Te(this.h,this.s,this.l*t,this.opacity)},darker:function(t){return t=null==t?Qn:Math.pow(Qn,t),new Te(this.h,this.s,this.l*t,this.opacity)},rgb:function(){var t=this.h%360+360*(this.h<0),n=isNaN(t)||isNaN(this.s)?0:this.s,e=this.l,r=e+(e<.5?e:1-e)*n,i=2*e-r;return new _e(Se(t>=240?t-240:t+120,i,r),Se(t,i,r),Se(t<120?t+240:t-120,i,r),this.opacity)},displayable:function(){return(0<=this.s&&this.s<=1||isNaN(this.s))&&0<=this.l&&this.l<=1&&0<=this.opacity&&this.opacity<=1},formatHsl:function(){var t=this.opacity;return(1===(t=isNaN(t)?1:Math.max(0,Math.min(1,t)))?"hsl(":"hsla(")+(this.h||0)+", "+100*(this.s||0)+"%, "+100*(this.l||0)+"%"+(1===t?")":", "+t+")")}}));const Ee=Math.PI/180,ke=180/Math.PI,Ne=.96422,Ce=.82521,Pe=4/29,ze=6/29,De=3*ze*ze;function qe(t){if(t instanceof Fe)return new Fe(t.l,t.a,t.b,t.opacity);if(t instanceof je)return He(t);t instanceof _e||(t=ye(t));var n,e,r=Be(t.r),i=Be(t.g),o=Be(t.b),a=Oe((.2225045*r+.7168786*i+.0606169*o)/1);return r===i&&i===o?n=e=a:(n=Oe((.4360747*r+.3850649*i+.1430804*o)/Ne),e=Oe((.0139322*r+.0971045*i+.7141733*o)/Ce)),new Fe(116*a-16,500*(n-a),200*(a-e),t.opacity)}function Re(t,n,e,r){return 1===arguments.length?qe(t):new Fe(t,n,e,null==r?1:r)}function Fe(t,n,e,r){this.l=+t,this.a=+n,this.b=+e,this.opacity=+r}function Oe(t){return t>.008856451679035631?Math.pow(t,1/3):t/De+Pe}function Ie(t){return t>ze?t*t*t:De*(t-Pe)}function Ue(t){return 255*(t<=.0031308?12.92*t:1.055*Math.pow(t,1/2.4)-.055)}function Be(t){return(t/=255)<=.04045?t/12.92:Math.pow((t+.055)/1.055,2.4)}function Ye(t){if(t instanceof je)return new je(t.h,t.c,t.l,t.opacity);if(t instanceof Fe||(t=qe(t)),0===t.a&&0===t.b)return new je(NaN,0=1?(e=1,n-1):Math.floor(e*n),i=t[r],o=t[r+1],a=r>0?t[r-1]:2*i-o,u=r()=>t;function ar(t,n){return function(e){return t+e*n}}function ur(t,n){var e=n-t;return e?ar(t,e>180||e<-180?e-360*Math.round(e/360):e):or(isNaN(t)?n:t)}function cr(t){return 1==(t=+t)?fr:function(n,e){return e-n?function(t,n,e){return t=Math.pow(t,e),n=Math.pow(n,e)-t,e=1/e,function(r){return Math.pow(t+r*n,e)}}(n,e,t):or(isNaN(n)?e:n)}}function fr(t,n){var e=n-t;return e?ar(t,e):or(isNaN(t)?n:t)}var sr=function t(n){var e=cr(n);function r(t,n){var r=e((t=ve(t)).r,(n=ve(n)).r),i=e(t.g,n.g),o=e(t.b,n.b),a=fr(t.opacity,n.opacity);return function(n){return t.r=r(n),t.g=i(n),t.b=o(n),t.opacity=a(n),t+""}}return r.gamma=t,r}(1);function lr(t){return function(n){var e,r,i=n.length,o=new Array(i),a=new Array(i),u=new Array(i);for(e=0;eo&&(i=n.slice(o,i),u[a]?u[a]+=i:u[++a]=i),(e=e[0])===(r=r[0])?u[a]?u[a]+=r:u[++a]=r:(u[++a]=null,c.push({i:a,x:_r(e,r)})),o=xr.lastIndex;return o180?n+=360:n-t>180&&(t+=360),o.push({i:e.push(i(e)+"rotate(",null,r)-2,x:_r(t,n)})):n&&e.push(i(e)+"rotate("+n+r)}(o.rotate,a.rotate,u,c),function(t,n,e,o){t!==n?o.push({i:e.push(i(e)+"skewX(",null,r)-2,x:_r(t,n)}):n&&e.push(i(e)+"skewX("+n+r)}(o.skewX,a.skewX,u,c),function(t,n,e,r,o,a){if(t!==e||n!==r){var u=o.push(i(o)+"scale(",null,",",null,")");a.push({i:u-4,x:_r(t,e)},{i:u-2,x:_r(n,r)})}else 1===e&&1===r||o.push(i(o)+"scale("+e+","+r+")")}(o.scaleX,o.scaleY,a.scaleX,a.scaleY,u,c),o=a=null,function(t){for(var n,e=-1,r=c.length;++e=0&&n._call.call(null,t),n=n._next;--Gr}function oi(){Zr=(Wr=Qr.now())+Kr,Gr=Vr=0;try{ii()}finally{Gr=0,function(){var t,n,e=Hr,r=1/0;for(;e;)e._call?(r>e._time&&(r=e._time),t=e,e=e._next):(n=e._next,e._next=null,e=t?t._next=n:Hr=n);Xr=t,ui(r)}(),Zr=0}}function ai(){var t=Qr.now(),n=t-Wr;n>1e3&&(Kr-=n,Wr=t)}function ui(t){Gr||(Vr&&(Vr=clearTimeout(Vr)),t-Zr>24?(t<1/0&&(Vr=setTimeout(oi,t-Qr.now()-Kr)),$r&&($r=clearInterval($r))):($r||(Wr=Qr.now(),$r=setInterval(ai,1e3)),Gr=1,Jr(oi)))}function ci(t,n,e){var r=new ei;return n=null==n?0:+n,r.restart((e=>{r.stop(),t(e+n)}),n,e),r}ei.prototype=ri.prototype={constructor:ei,restart:function(t,n,e){if("function"!=typeof t)throw new TypeError("callback is not a function");e=(null==e?ti():+e)+(null==n?0:+n),this._next||Xr===this||(Xr?Xr._next=this:Hr=this,Xr=this),this._call=t,this._time=e,ui()},stop:function(){this._call&&(this._call=null,this._time=1/0,ui())}};var fi=pt("start","end","cancel","interrupt"),si=[];function li(t,n,e,r,i,o){var a=t.__transition;if(a){if(e in a)return}else t.__transition={};!function(t,n,e){var r,i=t.__transition;function o(t){e.state=1,e.timer.restart(a,e.delay,e.time),e.delay<=t&&a(t-e.delay)}function a(o){var f,s,l,h;if(1!==e.state)return c();for(f in i)if((h=i[f]).name===e.name){if(3===h.state)return ci(a);4===h.state?(h.state=6,h.timer.stop(),h.on.call("interrupt",t,t.__data__,h.index,h.group),delete i[f]):+f0)throw new Error("too late; already scheduled");return e}function di(t,n){var e=pi(t,n);if(e.state>3)throw new Error("too late; already running");return e}function pi(t,n){var e=t.__transition;if(!e||!(e=e[n]))throw new Error("transition not found");return e}function gi(t,n){var e,r,i,o=t.__transition,a=!0;if(o){for(i in n=null==n?null:n+"",o)(e=o[i]).name===n?(r=e.state>2&&e.state<5,e.state=6,e.timer.stop(),e.on.call(r?"interrupt":"cancel",t,t.__data__,e.index,e.group),delete o[i]):a=!1;a&&delete t.__transition}}function yi(t,n){var e,r;return function(){var i=di(this,t),o=i.tween;if(o!==e)for(var a=0,u=(r=e=o).length;a=0&&(t=t.slice(0,n)),!t||"start"===t}))}(n)?hi:di;return function(){var a=o(this,t),u=a.on;u!==r&&(i=(r=u).copy()).on(n,e),a.on=i}}var Fi=zn.prototype.constructor;function Oi(t){return function(){this.style.removeProperty(t)}}function Ii(t,n,e){return function(r){this.style.setProperty(t,n.call(this,r),e)}}function Ui(t,n,e){var r,i;function o(){var o=n.apply(this,arguments);return o!==i&&(r=(i=o)&&Ii(t,o,e)),r}return o._value=n,o}function Bi(t){return function(n){this.textContent=t.call(this,n)}}function Yi(t){var n,e;function r(){var r=t.apply(this,arguments);return r!==e&&(n=(e=r)&&Bi(r)),n}return r._value=t,r}var Li=0;function ji(t,n,e,r){this._groups=t,this._parents=n,this._name=e,this._id=r}function Hi(t){return zn().transition(t)}function Xi(){return++Li}var Gi=zn.prototype;ji.prototype=Hi.prototype={constructor:ji,select:function(t){var n=this._name,e=this._id;"function"!=typeof t&&(t=St(t));for(var r=this._groups,i=r.length,o=new Array(i),a=0;a()=>t;function mo(t,{sourceEvent:n,target:e,selection:r,mode:i,dispatch:o}){Object.defineProperties(this,{type:{value:t,enumerable:!0,configurable:!0},sourceEvent:{value:n,enumerable:!0,configurable:!0},target:{value:e,enumerable:!0,configurable:!0},selection:{value:r,enumerable:!0,configurable:!0},mode:{value:i,enumerable:!0,configurable:!0},_:{value:o}})}function xo(t){t.stopImmediatePropagation()}function wo(t){t.preventDefault(),t.stopImmediatePropagation()}var Mo={name:"drag"},Ao={name:"space"},To={name:"handle"},So={name:"center"};const{abs:Eo,max:ko,min:No}=Math;function Co(t){return[+t[0],+t[1]]}function Po(t){return[Co(t[0]),Co(t[1])]}var zo={name:"x",handles:["w","e"].map(Bo),input:function(t,n){return null==t?null:[[+t[0],n[0][1]],[+t[1],n[1][1]]]},output:function(t){return t&&[t[0][0],t[1][0]]}},Do={name:"y",handles:["n","s"].map(Bo),input:function(t,n){return null==t?null:[[n[0][0],+t[0]],[n[1][0],+t[1]]]},output:function(t){return t&&[t[0][1],t[1][1]]}},qo={name:"xy",handles:["n","w","e","s","nw","ne","sw","se"].map(Bo),input:function(t){return null==t?null:Po(t)},output:function(t){return t}},Ro={overlay:"crosshair",selection:"move",n:"ns-resize",e:"ew-resize",s:"ns-resize",w:"ew-resize",nw:"nwse-resize",ne:"nesw-resize",se:"nwse-resize",sw:"nesw-resize"},Fo={e:"w",w:"e",nw:"ne",ne:"nw",se:"sw",sw:"se"},Oo={n:"s",s:"n",nw:"sw",ne:"se",se:"ne",sw:"nw"},Io={overlay:1,selection:1,n:null,e:1,s:null,w:-1,nw:-1,ne:1,se:1,sw:-1},Uo={overlay:1,selection:1,n:-1,e:null,s:1,w:null,nw:-1,ne:-1,se:1,sw:1};function Bo(t){return{type:t}}function Yo(t){return!t.ctrlKey&&!t.button}function Lo(){var t=this.ownerSVGElement||this;return t.hasAttribute("viewBox")?[[(t=t.viewBox.baseVal).x,t.y],[t.x+t.width,t.y+t.height]]:[[0,0],[t.width.baseVal.value,t.height.baseVal.value]]}function jo(){return navigator.maxTouchPoints||"ontouchstart"in this}function Ho(t){for(;!t.__brush;)if(!(t=t.parentNode))return;return t.__brush}function Xo(t){return t[0][0]===t[1][0]||t[0][1]===t[1][1]}function Go(t){var n,e=Lo,r=Yo,i=jo,o=!0,a=pt("start","brush","end"),u=6;function c(n){var e=n.property("__brush",g).selectAll(".overlay").data([Bo("overlay")]);e.enter().append("rect").attr("class","overlay").attr("pointer-events","all").attr("cursor",Ro.overlay).merge(e).each((function(){var t=Ho(this).extent;Dn(this).attr("x",t[0][0]).attr("y",t[0][1]).attr("width",t[1][0]-t[0][0]).attr("height",t[1][1]-t[0][1])})),n.selectAll(".selection").data([Bo("selection")]).enter().append("rect").attr("class","selection").attr("cursor",Ro.selection).attr("fill","#777").attr("fill-opacity",.3).attr("stroke","#fff").attr("shape-rendering","crispEdges");var r=n.selectAll(".handle").data(t.handles,(function(t){return t.type}));r.exit().remove(),r.enter().append("rect").attr("class",(function(t){return"handle handle--"+t.type})).attr("cursor",(function(t){return Ro[t.type]})),n.each(f).attr("fill","none").attr("pointer-events","all").on("mousedown.brush",h).filter(i).on("touchstart.brush",h).on("touchmove.brush",d).on("touchend.brush touchcancel.brush",p).style("touch-action","none").style("-webkit-tap-highlight-color","rgba(0,0,0,0)")}function f(){var t=Dn(this),n=Ho(this).selection;n?(t.selectAll(".selection").style("display",null).attr("x",n[0][0]).attr("y",n[0][1]).attr("width",n[1][0]-n[0][0]).attr("height",n[1][1]-n[0][1]),t.selectAll(".handle").style("display",null).attr("x",(function(t){return"e"===t.type[t.type.length-1]?n[1][0]-u/2:n[0][0]-u/2})).attr("y",(function(t){return"s"===t.type[0]?n[1][1]-u/2:n[0][1]-u/2})).attr("width",(function(t){return"n"===t.type||"s"===t.type?n[1][0]-n[0][0]+u:u})).attr("height",(function(t){return"e"===t.type||"w"===t.type?n[1][1]-n[0][1]+u:u}))):t.selectAll(".selection,.handle").style("display","none").attr("x",null).attr("y",null).attr("width",null).attr("height",null)}function s(t,n,e){var r=t.__brush.emitter;return!r||e&&r.clean?new l(t,n,e):r}function l(t,n,e){this.that=t,this.args=n,this.state=t.__brush,this.active=0,this.clean=e}function h(e){if((!n||e.touches)&&r.apply(this,arguments)){var i,a,u,c,l,h,d,p,g,y,v,_=this,b=e.target.__data__.type,m="selection"===(o&&e.metaKey?b="overlay":b)?Mo:o&&e.altKey?So:To,x=t===Do?null:Io[b],w=t===zo?null:Uo[b],M=Ho(_),A=M.extent,T=M.selection,S=A[0][0],E=A[0][1],k=A[1][0],N=A[1][1],C=0,P=0,z=x&&w&&o&&e.shiftKey,D=Array.from(e.touches||[e],(t=>{const n=t.identifier;return(t=In(t,_)).point0=t.slice(),t.identifier=n,t}));if("overlay"===b){T&&(g=!0);const n=[D[0],D[1]||D[0]];M.selection=T=[[i=t===Do?S:No(n[0][0],n[1][0]),u=t===zo?E:No(n[0][1],n[1][1])],[l=t===Do?k:ko(n[0][0],n[1][0]),d=t===zo?N:ko(n[0][1],n[1][1])]],D.length>1&&U()}else i=T[0][0],u=T[0][1],l=T[1][0],d=T[1][1];a=i,c=u,h=l,p=d;var q=Dn(_).attr("pointer-events","none"),R=q.selectAll(".overlay").attr("cursor",Ro[b]);gi(_);var F=s(_,arguments,!0).beforestart();if(e.touches)F.moved=I,F.ended=B;else{var O=Dn(e.view).on("mousemove.brush",I,!0).on("mouseup.brush",B,!0);o&&O.on("keydown.brush",Y,!0).on("keyup.brush",L,!0),Yn(e.view)}f.call(_),F.start(e,m.name)}function I(t){for(const n of t.changedTouches||[t])for(const t of D)t.identifier===n.identifier&&(t.cur=In(n,_));if(z&&!y&&!v&&1===D.length){const t=D[0];Eo(t.cur[0]-t[0])>Eo(t.cur[1]-t[1])?v=!0:y=!0}for(const t of D)t.cur&&(t[0]=t.cur[0],t[1]=t.cur[1]);g=!0,wo(t),U(t)}function U(t){const n=D[0],e=n.point0;var r;switch(C=n[0]-e[0],P=n[1]-e[1],m){case Ao:case Mo:x&&(C=ko(S-i,No(k-l,C)),a=i+C,h=l+C),w&&(P=ko(E-u,No(N-d,P)),c=u+P,p=d+P);break;case To:D[1]?(x&&(a=ko(S,No(k,D[0][0])),h=ko(S,No(k,D[1][0])),x=1),w&&(c=ko(E,No(N,D[0][1])),p=ko(E,No(N,D[1][1])),w=1)):(x<0?(C=ko(S-i,No(k-i,C)),a=i+C,h=l):x>0&&(C=ko(S-l,No(k-l,C)),a=i,h=l+C),w<0?(P=ko(E-u,No(N-u,P)),c=u+P,p=d):w>0&&(P=ko(E-d,No(N-d,P)),c=u,p=d+P));break;case So:x&&(a=ko(S,No(k,i-C*x)),h=ko(S,No(k,l+C*x))),w&&(c=ko(E,No(N,u-P*w)),p=ko(E,No(N,d+P*w)))}h0&&(i=a-C),w<0?d=p-P:w>0&&(u=c-P),m=Ao,R.attr("cursor",Ro.selection),U());break;default:return}wo(t)}function L(t){switch(t.keyCode){case 16:z&&(y=v=z=!1,U());break;case 18:m===So&&(x<0?l=h:x>0&&(i=a),w<0?d=p:w>0&&(u=c),m=To,U());break;case 32:m===Ao&&(t.altKey?(x&&(l=h-C*x,i=a+C*x),w&&(d=p-P*w,u=c+P*w),m=So):(x<0?l=h:x>0&&(i=a),w<0?d=p:w>0&&(u=c),m=To),R.attr("cursor",Ro[b]),U());break;default:return}wo(t)}}function d(t){s(this,arguments).moved(t)}function p(t){s(this,arguments).ended(t)}function g(){var n=this.__brush||{selection:null};return n.extent=Po(e.apply(this,arguments)),n.dim=t,n}return c.move=function(n,e){n.tween?n.on("start.brush",(function(t){s(this,arguments).beforestart().start(t)})).on("interrupt.brush end.brush",(function(t){s(this,arguments).end(t)})).tween("brush",(function(){var n=this,r=n.__brush,i=s(n,arguments),o=r.selection,a=t.input("function"==typeof e?e.apply(this,arguments):e,r.extent),u=Mr(o,a);function c(t){r.selection=1===t&&null===a?null:u(t),f.call(n),i.brush()}return null!==o&&null!==a?c:c(1)})):n.each((function(){var n=this,r=arguments,i=n.__brush,o=t.input("function"==typeof e?e.apply(n,r):e,i.extent),a=s(n,r).beforestart();gi(n),i.selection=null===o?null:o,f.call(n),a.start().brush().end()}))},c.clear=function(t){c.move(t,null)},l.prototype={beforestart:function(){return 1==++this.active&&(this.state.emitter=this,this.starting=!0),this},start:function(t,n){return this.starting?(this.starting=!1,this.emit("start",t,n)):this.emit("brush",t),this},brush:function(t,n){return this.emit("brush",t,n),this},end:function(t,n){return 0==--this.active&&(delete this.state.emitter,this.emit("end",t,n)),this},emit:function(n,e,r){var i=Dn(this.that).datum();a.call(n,this.that,new mo(n,{sourceEvent:e,target:c,selection:t.output(this.state.selection),mode:r,dispatch:a}),i)}},c.extent=function(t){return arguments.length?(e="function"==typeof t?t:bo(Po(t)),c):e},c.filter=function(t){return arguments.length?(r="function"==typeof t?t:bo(!!t),c):r},c.touchable=function(t){return arguments.length?(i="function"==typeof t?t:bo(!!t),c):i},c.handleSize=function(t){return arguments.length?(u=+t,c):u},c.keyModifiers=function(t){return arguments.length?(o=!!t,c):o},c.on=function(){var t=a.on.apply(a,arguments);return t===a?c:t},c}var Vo=Math.abs,$o=Math.cos,Wo=Math.sin,Zo=Math.PI,Ko=Zo/2,Qo=2*Zo,Jo=Math.max,ta=1e-12;function na(t,n){return Array.from({length:n-t},((n,e)=>t+e))}function ea(t){return function(n,e){return t(n.source.value+n.target.value,e.source.value+e.target.value)}}function ra(t,n){var e=0,r=null,i=null,o=null;function a(a){var u,c=a.length,f=new Array(c),s=na(0,c),l=new Array(c*c),h=new Array(c),d=0;a=Float64Array.from({length:c*c},n?(t,n)=>a[n%c][n/c|0]:(t,n)=>a[n/c|0][n%c]);for(let n=0;nr(f[t],f[n])));for(const e of s){const r=n;if(t){const t=na(1+~c,c).filter((t=>t<0?a[~t*c+e]:a[e*c+t]));i&&t.sort(((t,n)=>i(t<0?-a[~t*c+e]:a[e*c+t],n<0?-a[~n*c+e]:a[e*c+n])));for(const r of t)if(r<0){(l[~r*c+e]||(l[~r*c+e]={source:null,target:null})).target={index:e,startAngle:n,endAngle:n+=a[~r*c+e]*d,value:a[~r*c+e]}}else{(l[e*c+r]||(l[e*c+r]={source:null,target:null})).source={index:e,startAngle:n,endAngle:n+=a[e*c+r]*d,value:a[e*c+r]}}h[e]={index:e,startAngle:r,endAngle:n,value:f[e]}}else{const t=na(0,c).filter((t=>a[e*c+t]||a[t*c+e]));i&&t.sort(((t,n)=>i(a[e*c+t],a[e*c+n])));for(const r of t){let t;if(eaa)if(Math.abs(s*u-c*f)>aa&&i){var h=e-o,d=r-a,p=u*u+c*c,g=h*h+d*d,y=Math.sqrt(p),v=Math.sqrt(l),_=i*Math.tan((ia-Math.acos((p+l-g)/(2*y*v)))/2),b=_/v,m=_/y;Math.abs(b-1)>aa&&(this._+="L"+(t+b*f)+","+(n+b*s)),this._+="A"+i+","+i+",0,0,"+ +(s*h>f*d)+","+(this._x1=t+m*u)+","+(this._y1=n+m*c)}else this._+="L"+(this._x1=t)+","+(this._y1=n);else;},arc:function(t,n,e,r,i,o){t=+t,n=+n,o=!!o;var a=(e=+e)*Math.cos(r),u=e*Math.sin(r),c=t+a,f=n+u,s=1^o,l=o?r-i:i-r;if(e<0)throw new Error("negative radius: "+e);null===this._x1?this._+="M"+c+","+f:(Math.abs(this._x1-c)>aa||Math.abs(this._y1-f)>aa)&&(this._+="L"+c+","+f),e&&(l<0&&(l=l%oa+oa),l>ua?this._+="A"+e+","+e+",0,1,"+s+","+(t-a)+","+(n-u)+"A"+e+","+e+",0,1,"+s+","+(this._x1=c)+","+(this._y1=f):l>aa&&(this._+="A"+e+","+e+",0,"+ +(l>=ia)+","+s+","+(this._x1=t+e*Math.cos(i))+","+(this._y1=n+e*Math.sin(i))))},rect:function(t,n,e,r){this._+="M"+(this._x0=this._x1=+t)+","+(this._y0=this._y1=+n)+"h"+ +e+"v"+ +r+"h"+-e+"Z"},toString:function(){return this._}};var sa=Array.prototype.slice;function la(t){return function(){return t}}function ha(t){return t.source}function da(t){return t.target}function pa(t){return t.radius}function ga(t){return t.startAngle}function ya(t){return t.endAngle}function va(){return 0}function _a(){return 10}function ba(t){var n=ha,e=da,r=pa,i=pa,o=ga,a=ya,u=va,c=null;function f(){var f,s=n.apply(this,arguments),l=e.apply(this,arguments),h=u.apply(this,arguments)/2,d=sa.call(arguments),p=+r.apply(this,(d[0]=s,d)),g=o.apply(this,d)-Ko,y=a.apply(this,d)-Ko,v=+i.apply(this,(d[0]=l,d)),_=o.apply(this,d)-Ko,b=a.apply(this,d)-Ko;if(c||(c=f=fa()),h>ta&&(Vo(y-g)>2*h+ta?y>g?(g+=h,y-=h):(g-=h,y+=h):g=y=(g+y)/2,Vo(b-_)>2*h+ta?b>_?(_+=h,b-=h):(_-=h,b+=h):_=b=(_+b)/2),c.moveTo(p*$o(g),p*Wo(g)),c.arc(0,0,p,g,y),g!==_||y!==b)if(t){var m=+t.apply(this,arguments),x=v-m,w=(_+b)/2;c.quadraticCurveTo(0,0,x*$o(_),x*Wo(_)),c.lineTo(v*$o(w),v*Wo(w)),c.lineTo(x*$o(b),x*Wo(b))}else c.quadraticCurveTo(0,0,v*$o(_),v*Wo(_)),c.arc(0,0,v,_,b);if(c.quadraticCurveTo(0,0,p*$o(g),p*Wo(g)),c.closePath(),f)return c=null,f+""||null}return t&&(f.headRadius=function(n){return arguments.length?(t="function"==typeof n?n:la(+n),f):t}),f.radius=function(t){return arguments.length?(r=i="function"==typeof t?t:la(+t),f):r},f.sourceRadius=function(t){return arguments.length?(r="function"==typeof t?t:la(+t),f):r},f.targetRadius=function(t){return arguments.length?(i="function"==typeof t?t:la(+t),f):i},f.startAngle=function(t){return arguments.length?(o="function"==typeof t?t:la(+t),f):o},f.endAngle=function(t){return arguments.length?(a="function"==typeof t?t:la(+t),f):a},f.padAngle=function(t){return arguments.length?(u="function"==typeof t?t:la(+t),f):u},f.source=function(t){return arguments.length?(n=t,f):n},f.target=function(t){return arguments.length?(e=t,f):e},f.context=function(t){return arguments.length?(c=null==t?null:t,f):c},f}var ma=Array.prototype.slice;function xa(t,n){return t-n}var wa=t=>()=>t;function Ma(t,n){for(var e,r=-1,i=n.length;++rr!=d>r&&e<(h-f)*(r-s)/(d-s)+f&&(i=-i)}return i}function Ta(t,n,e){var r,i,o,a;return function(t,n,e){return(n[0]-t[0])*(e[1]-t[1])==(e[0]-t[0])*(n[1]-t[1])}(t,n,e)&&(i=t[r=+(t[0]===n[0])],o=e[r],a=n[r],i<=o&&o<=a||a<=o&&o<=i)}function Sa(){}var Ea=[[],[[[1,1.5],[.5,1]]],[[[1.5,1],[1,1.5]]],[[[1.5,1],[.5,1]]],[[[1,.5],[1.5,1]]],[[[1,1.5],[.5,1]],[[1,.5],[1.5,1]]],[[[1,.5],[1,1.5]]],[[[1,.5],[.5,1]]],[[[.5,1],[1,.5]]],[[[1,1.5],[1,.5]]],[[[.5,1],[1,.5]],[[1.5,1],[1,1.5]]],[[[1.5,1],[1,.5]]],[[[.5,1],[1.5,1]]],[[[1,1.5],[1.5,1]]],[[[.5,1],[1,1.5]]],[]];function ka(){var t=1,n=1,e=I,r=u;function i(t){var n=e(t);if(Array.isArray(n))n=n.slice().sort(xa);else{var r=p(t),i=r[0],a=r[1];n=F(i,a,n),n=Z(Math.floor(i/n)*n,Math.floor(a/n)*n,n)}return n.map((function(n){return o(t,n)}))}function o(e,i){var o=[],u=[];return function(e,r,i){var o,u,c,f,s,l,h=new Array,d=new Array;o=u=-1,f=e[0]>=r,Ea[f<<1].forEach(p);for(;++o=r,Ea[c|f<<1].forEach(p);Ea[f<<0].forEach(p);for(;++u=r,s=e[u*t]>=r,Ea[f<<1|s<<2].forEach(p);++o=r,l=s,s=e[u*t+o+1]>=r,Ea[c|f<<1|s<<2|l<<3].forEach(p);Ea[f|s<<3].forEach(p)}o=-1,s=e[u*t]>=r,Ea[s<<2].forEach(p);for(;++o=r,Ea[s<<2|l<<3].forEach(p);function p(t){var n,e,r=[t[0][0]+o,t[0][1]+u],c=[t[1][0]+o,t[1][1]+u],f=a(r),s=a(c);(n=d[f])?(e=h[s])?(delete d[n.end],delete h[e.start],n===e?(n.ring.push(c),i(n.ring)):h[n.start]=d[e.end]={start:n.start,end:e.end,ring:n.ring.concat(e.ring)}):(delete d[n.end],n.ring.push(c),d[n.end=s]=n):(n=h[s])?(e=d[f])?(delete h[n.start],delete d[e.end],n===e?(n.ring.push(c),i(n.ring)):h[e.start]=d[n.end]={start:e.start,end:n.end,ring:e.ring.concat(n.ring)}):(delete h[n.start],n.ring.unshift(r),h[n.start=f]=n):h[f]=d[s]={start:f,end:s,ring:[r,c]}}Ea[s<<3].forEach(p)}(e,i,(function(t){r(t,e,i),function(t){for(var n=0,e=t.length,r=t[e-1][1]*t[0][0]-t[e-1][0]*t[0][1];++n0?o.push([t]):u.push(t)})),u.forEach((function(t){for(var n,e=0,r=o.length;e0&&a0&&u=0&&o>=0))throw new Error("invalid size");return t=r,n=o,i},i.thresholds=function(t){return arguments.length?(e="function"==typeof t?t:Array.isArray(t)?wa(ma.call(t)):wa(t),i):e},i.smooth=function(t){return arguments.length?(r=t?u:Sa,i):r===u},i}function Na(t,n,e){for(var r=t.width,i=t.height,o=1+(e<<1),a=0;a=e&&(u>=o&&(c-=t.data[u-o+a*r]),n.data[u-e+a*r]=c/Math.min(u+1,r-1+o-u,o))}function Ca(t,n,e){for(var r=t.width,i=t.height,o=1+(e<<1),a=0;a=e&&(u>=o&&(c-=t.data[a+(u-o)*r]),n.data[a+(u-e)*r]=c/Math.min(u+1,i-1+o-u,o))}function Pa(t){return t[0]}function za(t){return t[1]}function Da(){return 1}const qa=Math.pow(2,-52),Ra=new Uint32Array(512);class Fa{static from(t,n=Ha,e=Xa){const r=t.length,i=new Float64Array(2*r);for(let o=0;o>1;if(n>0&&"number"!=typeof t[0])throw new Error("Expected coords to contain numbers.");this.coords=t;const e=Math.max(2*n-5,0);this._triangles=new Uint32Array(3*e),this._halfedges=new Int32Array(3*e),this._hashSize=Math.ceil(Math.sqrt(n)),this._hullPrev=new Uint32Array(n),this._hullNext=new Uint32Array(n),this._hullTri=new Uint32Array(n),this._hullHash=new Int32Array(this._hashSize).fill(-1),this._ids=new Uint32Array(n),this._dists=new Float64Array(n),this.update()}update(){const{coords:t,_hullPrev:n,_hullNext:e,_hullTri:r,_hullHash:i}=this,o=t.length>>1;let a=1/0,u=1/0,c=-1/0,f=-1/0;for(let n=0;nc&&(c=e),r>f&&(f=r),this._ids[n]=n}const s=(a+c)/2,l=(u+f)/2;let h,d,p,g=1/0;for(let n=0;n0&&(d=n,g=e)}let _=t[2*d],b=t[2*d+1],m=1/0;for(let n=0;nr&&(n[e++]=i,r=this._dists[i])}return this.hull=n.subarray(0,e),this.triangles=new Uint32Array(0),void(this.halfedges=new Uint32Array(0))}if(Ua(y,v,_,b,x,w)){const t=d,n=_,e=b;d=p,_=x,b=w,p=t,x=n,w=e}const M=function(t,n,e,r,i,o){const a=e-t,u=r-n,c=i-t,f=o-n,s=a*a+u*u,l=c*c+f*f,h=.5/(a*f-u*c);return{x:t+(f*s-u*l)*h,y:n+(a*l-c*s)*h}}(y,v,_,b,x,w);this._cx=M.x,this._cy=M.y;for(let n=0;n0&&Math.abs(f-o)<=qa&&Math.abs(s-a)<=qa)continue;if(o=f,a=s,c===h||c===d||c===p)continue;let l=0;for(let t=0,n=this._hashKey(f,s);t0?3-e:1+e)/4}(t-this._cx,n-this._cy)*this._hashSize)%this._hashSize}_legalize(t){const{_triangles:n,_halfedges:e,coords:r}=this;let i=0,o=0;for(;;){const a=e[t],u=t-t%3;if(o=u+(t+2)%3,-1===a){if(0===i)break;t=Ra[--i];continue}const c=a-a%3,f=u+(t+1)%3,s=c+(a+2)%3,l=n[o],h=n[t],d=n[f],p=n[s];if(Ba(r[2*l],r[2*l+1],r[2*h],r[2*h+1],r[2*d],r[2*d+1],r[2*p],r[2*p+1])){n[t]=p,n[a]=l;const r=e[s];if(-1===r){let n=this._hullStart;do{if(this._hullTri[n]===s){this._hullTri[n]=t;break}n=this._hullPrev[n]}while(n!==this._hullStart)}this._link(t,r),this._link(a,e[o]),this._link(o,s);const u=c+(a+1)%3;i=33306690738754716e-32*Math.abs(a+u)?a-u:0}function Ua(t,n,e,r,i,o){return(Ia(i,o,t,n,e,r)||Ia(t,n,e,r,i,o)||Ia(e,r,i,o,t,n))<0}function Ba(t,n,e,r,i,o,a,u){const c=t-a,f=n-u,s=e-a,l=r-u,h=i-a,d=o-u,p=s*s+l*l,g=h*h+d*d;return c*(l*g-p*d)-f*(s*g-p*h)+(c*c+f*f)*(s*d-l*h)<0}function Ya(t,n,e,r,i,o){const a=e-t,u=r-n,c=i-t,f=o-n,s=a*a+u*u,l=c*c+f*f,h=.5/(a*f-u*c),d=(f*s-u*l)*h,p=(a*l-c*s)*h;return d*d+p*p}function La(t,n,e,r){if(r-e<=20)for(let i=e+1;i<=r;i++){const r=t[i],o=n[r];let a=i-1;for(;a>=e&&n[t[a]]>o;)t[a+1]=t[a--];t[a+1]=r}else{let i=e+1,o=r;ja(t,e+r>>1,i),n[t[e]]>n[t[r]]&&ja(t,e,r),n[t[i]]>n[t[r]]&&ja(t,i,r),n[t[e]]>n[t[i]]&&ja(t,e,i);const a=t[i],u=n[a];for(;;){do{i++}while(n[t[i]]u);if(o=o-e?(La(t,n,i,r),La(t,n,e,o-1)):(La(t,n,e,o-1),La(t,n,i,r))}}function ja(t,n,e){const r=t[n];t[n]=t[e],t[e]=r}function Ha(t){return t[0]}function Xa(t){return t[1]}const Ga=1e-6;class Va{constructor(){this._x0=this._y0=this._x1=this._y1=null,this._=""}moveTo(t,n){this._+=`M${this._x0=this._x1=+t},${this._y0=this._y1=+n}`}closePath(){null!==this._x1&&(this._x1=this._x0,this._y1=this._y0,this._+="Z")}lineTo(t,n){this._+=`L${this._x1=+t},${this._y1=+n}`}arc(t,n,e){const r=(t=+t)+(e=+e),i=n=+n;if(e<0)throw new Error("negative radius");null===this._x1?this._+=`M${r},${i}`:(Math.abs(this._x1-r)>Ga||Math.abs(this._y1-i)>Ga)&&(this._+="L"+r+","+i),e&&(this._+=`A${e},${e},0,1,1,${t-e},${n}A${e},${e},0,1,1,${this._x1=r},${this._y1=i}`)}rect(t,n,e,r){this._+=`M${this._x0=this._x1=+t},${this._y0=this._y1=+n}h${+e}v${+r}h${-e}Z`}value(){return this._||null}}class $a{constructor(){this._=[]}moveTo(t,n){this._.push([t,n])}closePath(){this._.push(this._[0].slice())}lineTo(t,n){this._.push([t,n])}value(){return this._.length?this._:null}}class Wa{constructor(t,[n,e,r,i]=[0,0,960,500]){if(!((r=+r)>=(n=+n)&&(i=+i)>=(e=+e)))throw new Error("invalid bounds");this.delaunay=t,this._circumcenters=new Float64Array(2*t.points.length),this.vectors=new Float64Array(2*t.points.length),this.xmax=r,this.xmin=n,this.ymax=i,this.ymin=e,this._init()}update(){return this.delaunay.update(),this._init(),this}_init(){const{delaunay:{points:t,hull:n,triangles:e},vectors:r}=this,i=this.circumcenters=this._circumcenters.subarray(0,e.length/3*2);for(let n,r,o=0,a=0,u=e.length;o1;)i-=2;for(let t=2;t4)for(let t=0;t0){if(n>=this.ymax)return null;(i=(this.ymax-n)/r)0){if(t>=this.xmax)return null;(i=(this.xmax-t)/e)this.xmax?2:0)|(nthis.ymax?8:0)}}const Za=2*Math.PI,Ka=Math.pow;function Qa(t){return t[0]}function Ja(t){return t[1]}function tu(t,n,e){return[t+Math.sin(t+n)*e,n+Math.cos(t-n)*e]}class nu{static from(t,n=Qa,e=Ja,r){return new nu("length"in t?function(t,n,e,r){const i=t.length,o=new Float64Array(2*i);for(let a=0;a2&&function(t){const{triangles:n,coords:e}=t;for(let t=0;t1e-10)return!1}return!0}(t)){this.collinear=Int32Array.from({length:n.length/2},((t,n)=>n)).sort(((t,e)=>n[2*t]-n[2*e]||n[2*t+1]-n[2*e+1]));const t=this.collinear[0],e=this.collinear[this.collinear.length-1],r=[n[2*t],n[2*t+1],n[2*e],n[2*e+1]],i=1e-8*Math.hypot(r[3]-r[1],r[2]-r[0]);for(let t=0,e=n.length/2;t0&&(this.triangles=new Int32Array(3).fill(-1),this.halfedges=new Int32Array(3).fill(-1),this.triangles[0]=r[0],this.triangles[1]=r[1],this.triangles[2]=r[1],o[r[0]]=1,2===r.length&&(o[r[1]]=0))}voronoi(t){return new Wa(this,t)}*neighbors(t){const{inedges:n,hull:e,_hullIndex:r,halfedges:i,triangles:o,collinear:a}=this;if(a){const n=a.indexOf(t);return n>0&&(yield a[n-1]),void(n=0&&i!==e&&i!==r;)e=i;return i}_step(t,n,e){const{inedges:r,hull:i,_hullIndex:o,halfedges:a,triangles:u,points:c}=this;if(-1===r[t]||!c.length)return(t+1)%(c.length>>1);let f=t,s=Ka(n-c[2*t],2)+Ka(e-c[2*t+1],2);const l=r[t];let h=l;do{let r=u[h];const l=Ka(n-c[2*r],2)+Ka(e-c[2*r+1],2);if(l9999?"+"+au(t,6):au(t,4)}(t.getUTCFullYear())+"-"+au(t.getUTCMonth()+1,2)+"-"+au(t.getUTCDate(),2)+(i?"T"+au(n,2)+":"+au(e,2)+":"+au(r,2)+"."+au(i,3)+"Z":r?"T"+au(n,2)+":"+au(e,2)+":"+au(r,2)+"Z":e||n?"T"+au(n,2)+":"+au(e,2)+"Z":"")}function cu(t){var n=new RegExp('["'+t+"\n\r]"),e=t.charCodeAt(0);function r(t,n){var r,i=[],o=t.length,a=0,u=0,c=o<=0,f=!1;function s(){if(c)return ru;if(f)return f=!1,eu;var n,r,i=a;if(34===t.charCodeAt(i)){for(;a++=o?c=!0:10===(r=t.charCodeAt(a++))?f=!0:13===r&&(f=!0,10===t.charCodeAt(a)&&++a),t.slice(i+1,n-1).replace(/""/g,'"')}for(;aNu(n,e).then((n=>(new DOMParser).parseFromString(n,t)))}var Ru=qu("application/xml"),Fu=qu("text/html"),Ou=qu("image/svg+xml");function Iu(t,n,e,r){if(isNaN(n)||isNaN(e))return t;var i,o,a,u,c,f,s,l,h,d=t._root,p={data:r},g=t._x0,y=t._y0,v=t._x1,_=t._y1;if(!d)return t._root=p,t;for(;d.length;)if((f=n>=(o=(g+v)/2))?g=o:v=o,(s=e>=(a=(y+_)/2))?y=a:_=a,i=d,!(d=d[l=s<<1|f]))return i[l]=p,t;if(u=+t._x.call(null,d.data),c=+t._y.call(null,d.data),n===u&&e===c)return p.next=d,i?i[l]=p:t._root=p,t;do{i=i?i[l]=new Array(4):t._root=new Array(4),(f=n>=(o=(g+v)/2))?g=o:v=o,(s=e>=(a=(y+_)/2))?y=a:_=a}while((l=s<<1|f)==(h=(c>=a)<<1|u>=o));return i[h]=d,i[l]=p,t}function Uu(t,n,e,r,i){this.node=t,this.x0=n,this.y0=e,this.x1=r,this.y1=i}function Bu(t){return t[0]}function Yu(t){return t[1]}function Lu(t,n,e){var r=new ju(null==n?Bu:n,null==e?Yu:e,NaN,NaN,NaN,NaN);return null==t?r:r.addAll(t)}function ju(t,n,e,r,i,o){this._x=t,this._y=n,this._x0=e,this._y0=r,this._x1=i,this._y1=o,this._root=void 0}function Hu(t){for(var n={data:t.data},e=n;t=t.next;)e=e.next={data:t.data};return n}var Xu=Lu.prototype=ju.prototype;function Gu(t){return function(){return t}}function Vu(t){return 1e-6*(t()-.5)}function $u(t){return t.x+t.vx}function Wu(t){return t.y+t.vy}function Zu(t){return t.index}function Ku(t,n){var e=t.get(n);if(!e)throw new Error("node not found: "+n);return e}Xu.copy=function(){var t,n,e=new ju(this._x,this._y,this._x0,this._y0,this._x1,this._y1),r=this._root;if(!r)return e;if(!r.length)return e._root=Hu(r),e;for(t=[{source:r,target:e._root=new Array(4)}];r=t.pop();)for(var i=0;i<4;++i)(n=r.source[i])&&(n.length?t.push({source:n,target:r.target[i]=new Array(4)}):r.target[i]=Hu(n));return e},Xu.add=function(t){const n=+this._x.call(null,t),e=+this._y.call(null,t);return Iu(this.cover(n,e),n,e,t)},Xu.addAll=function(t){var n,e,r,i,o=t.length,a=new Array(o),u=new Array(o),c=1/0,f=1/0,s=-1/0,l=-1/0;for(e=0;es&&(s=r),il&&(l=i));if(c>s||f>l)return this;for(this.cover(c,f).cover(s,l),e=0;et||t>=i||r>n||n>=o;)switch(u=(nh||(o=c.y0)>d||(a=c.x1)=v)<<1|t>=y)&&(c=p[p.length-1],p[p.length-1]=p[p.length-1-f],p[p.length-1-f]=c)}else{var _=t-+this._x.call(null,g.data),b=n-+this._y.call(null,g.data),m=_*_+b*b;if(m=(u=(p+y)/2))?p=u:y=u,(s=a>=(c=(g+v)/2))?g=c:v=c,n=d,!(d=d[l=s<<1|f]))return this;if(!d.length)break;(n[l+1&3]||n[l+2&3]||n[l+3&3])&&(e=n,h=l)}for(;d.data!==t;)if(r=d,!(d=d.next))return this;return(i=d.next)&&delete d.next,r?(i?r.next=i:delete r.next,this):n?(i?n[l]=i:delete n[l],(d=n[0]||n[1]||n[2]||n[3])&&d===(n[3]||n[2]||n[1]||n[0])&&!d.length&&(e?e[h]=d:this._root=d),this):(this._root=i,this)},Xu.removeAll=function(t){for(var n=0,e=t.length;n1?r[0]+r.slice(2):r,+t.slice(e+1)]}function rc(t){return(t=ec(Math.abs(t)))?t[1]:NaN}var ic,oc=/^(?:(.)?([<>=^]))?([+\-( ])?([$#])?(0)?(\d+)?(,)?(\.\d+)?(~)?([a-z%])?$/i;function ac(t){if(!(n=oc.exec(t)))throw new Error("invalid format: "+t);var n;return new uc({fill:n[1],align:n[2],sign:n[3],symbol:n[4],zero:n[5],width:n[6],comma:n[7],precision:n[8]&&n[8].slice(1),trim:n[9],type:n[10]})}function uc(t){this.fill=void 0===t.fill?" ":t.fill+"",this.align=void 0===t.align?">":t.align+"",this.sign=void 0===t.sign?"-":t.sign+"",this.symbol=void 0===t.symbol?"":t.symbol+"",this.zero=!!t.zero,this.width=void 0===t.width?void 0:+t.width,this.comma=!!t.comma,this.precision=void 0===t.precision?void 0:+t.precision,this.trim=!!t.trim,this.type=void 0===t.type?"":t.type+""}function cc(t,n){var e=ec(t,n);if(!e)return t+"";var r=e[0],i=e[1];return i<0?"0."+new Array(-i).join("0")+r:r.length>i+1?r.slice(0,i+1)+"."+r.slice(i+1):r+new Array(i-r.length+2).join("0")}ac.prototype=uc.prototype,uc.prototype.toString=function(){return this.fill+this.align+this.sign+this.symbol+(this.zero?"0":"")+(void 0===this.width?"":Math.max(1,0|this.width))+(this.comma?",":"")+(void 0===this.precision?"":"."+Math.max(0,0|this.precision))+(this.trim?"~":"")+this.type};var fc={"%":(t,n)=>(100*t).toFixed(n),b:t=>Math.round(t).toString(2),c:t=>t+"",d:function(t){return Math.abs(t=Math.round(t))>=1e21?t.toLocaleString("en").replace(/,/g,""):t.toString(10)},e:(t,n)=>t.toExponential(n),f:(t,n)=>t.toFixed(n),g:(t,n)=>t.toPrecision(n),o:t=>Math.round(t).toString(8),p:(t,n)=>cc(100*t,n),r:cc,s:function(t,n){var e=ec(t,n);if(!e)return t+"";var r=e[0],i=e[1],o=i-(ic=3*Math.max(-8,Math.min(8,Math.floor(i/3))))+1,a=r.length;return o===a?r:o>a?r+new Array(o-a+1).join("0"):o>0?r.slice(0,o)+"."+r.slice(o):"0."+new Array(1-o).join("0")+ec(t,Math.max(0,n+o-1))[0]},X:t=>Math.round(t).toString(16).toUpperCase(),x:t=>Math.round(t).toString(16)};function sc(t){return t}var lc,hc=Array.prototype.map,dc=["y","z","a","f","p","n","µ","m","","k","M","G","T","P","E","Z","Y"];function pc(t){var n,e,r=void 0===t.grouping||void 0===t.thousands?sc:(n=hc.call(t.grouping,Number),e=t.thousands+"",function(t,r){for(var i=t.length,o=[],a=0,u=n[0],c=0;i>0&&u>0&&(c+u+1>r&&(u=Math.max(1,r-c)),o.push(t.substring(i-=u,i+u)),!((c+=u+1)>r));)u=n[a=(a+1)%n.length];return o.reverse().join(e)}),i=void 0===t.currency?"":t.currency[0]+"",o=void 0===t.currency?"":t.currency[1]+"",a=void 0===t.decimal?".":t.decimal+"",u=void 0===t.numerals?sc:function(t){return function(n){return n.replace(/[0-9]/g,(function(n){return t[+n]}))}}(hc.call(t.numerals,String)),c=void 0===t.percent?"%":t.percent+"",f=void 0===t.minus?"−":t.minus+"",s=void 0===t.nan?"NaN":t.nan+"";function l(t){var n=(t=ac(t)).fill,e=t.align,l=t.sign,h=t.symbol,d=t.zero,p=t.width,g=t.comma,y=t.precision,v=t.trim,_=t.type;"n"===_?(g=!0,_="g"):fc[_]||(void 0===y&&(y=12),v=!0,_="g"),(d||"0"===n&&"="===e)&&(d=!0,n="0",e="=");var b="$"===h?i:"#"===h&&/[boxX]/.test(_)?"0"+_.toLowerCase():"",m="$"===h?o:/[%p]/.test(_)?c:"",x=fc[_],w=/[defgprs%]/.test(_);function M(t){var i,o,c,h=b,M=m;if("c"===_)M=x(t)+M,t="";else{var A=(t=+t)<0||1/t<0;if(t=isNaN(t)?s:x(Math.abs(t),y),v&&(t=function(t){t:for(var n,e=t.length,r=1,i=-1;r0&&(i=0)}return i>0?t.slice(0,i)+t.slice(n+1):t}(t)),A&&0==+t&&"+"!==l&&(A=!1),h=(A?"("===l?l:f:"-"===l||"("===l?"":l)+h,M=("s"===_?dc[8+ic/3]:"")+M+(A&&"("===l?")":""),w)for(i=-1,o=t.length;++i(c=t.charCodeAt(i))||c>57){M=(46===c?a+t.slice(i+1):t.slice(i))+M,t=t.slice(0,i);break}}g&&!d&&(t=r(t,1/0));var T=h.length+t.length+M.length,S=T>1)+h+t+M+S.slice(T);break;default:t=S+h+t+M}return u(t)}return y=void 0===y?6:/[gprs]/.test(_)?Math.max(1,Math.min(21,y)):Math.max(0,Math.min(20,y)),M.toString=function(){return t+""},M}return{format:l,formatPrefix:function(t,n){var e=l(((t=ac(t)).type="f",t)),r=3*Math.max(-8,Math.min(8,Math.floor(rc(n)/3))),i=Math.pow(10,-r),o=dc[8+r/3];return function(t){return e(i*t)+o}}}}function gc(n){return lc=pc(n),t.format=lc.format,t.formatPrefix=lc.formatPrefix,lc}function yc(t){return Math.max(0,-rc(Math.abs(t)))}function vc(t,n){return Math.max(0,3*Math.max(-8,Math.min(8,Math.floor(rc(n)/3)))-rc(Math.abs(t)))}function _c(t,n){return t=Math.abs(t),n=Math.abs(n)-t,Math.max(0,rc(n)-rc(t))+1}t.format=void 0,t.formatPrefix=void 0,gc({thousands:",",grouping:[3],currency:["$",""]});var bc=1e-6,mc=1e-12,xc=Math.PI,wc=xc/2,Mc=xc/4,Ac=2*xc,Tc=180/xc,Sc=xc/180,Ec=Math.abs,kc=Math.atan,Nc=Math.atan2,Cc=Math.cos,Pc=Math.ceil,zc=Math.exp,Dc=Math.hypot,qc=Math.log,Rc=Math.pow,Fc=Math.sin,Oc=Math.sign||function(t){return t>0?1:t<0?-1:0},Ic=Math.sqrt,Uc=Math.tan;function Bc(t){return t>1?0:t<-1?xc:Math.acos(t)}function Yc(t){return t>1?wc:t<-1?-wc:Math.asin(t)}function Lc(t){return(t=Fc(t/2))*t}function jc(){}function Hc(t,n){t&&Gc.hasOwnProperty(t.type)&&Gc[t.type](t,n)}var Xc={Feature:function(t,n){Hc(t.geometry,n)},FeatureCollection:function(t,n){for(var e=t.features,r=-1,i=e.length;++r=0?1:-1,i=r*e,o=Cc(n=(n*=Sc)/2+Mc),a=Fc(n),u=tf*a,c=Jc*o+u*Cc(i),f=u*r*Fc(i);df.add(Nc(f,c)),Qc=t,Jc=o,tf=a}function mf(t){return[Nc(t[1],t[0]),Yc(t[2])]}function xf(t){var n=t[0],e=t[1],r=Cc(e);return[r*Cc(n),r*Fc(n),Fc(e)]}function wf(t,n){return t[0]*n[0]+t[1]*n[1]+t[2]*n[2]}function Mf(t,n){return[t[1]*n[2]-t[2]*n[1],t[2]*n[0]-t[0]*n[2],t[0]*n[1]-t[1]*n[0]]}function Af(t,n){t[0]+=n[0],t[1]+=n[1],t[2]+=n[2]}function Tf(t,n){return[t[0]*n,t[1]*n,t[2]*n]}function Sf(t){var n=Ic(t[0]*t[0]+t[1]*t[1]+t[2]*t[2]);t[0]/=n,t[1]/=n,t[2]/=n}var Ef,kf,Nf,Cf,Pf,zf,Df,qf,Rf,Ff,Of,If,Uf,Bf,Yf,Lf,jf={point:Hf,lineStart:Gf,lineEnd:Vf,polygonStart:function(){jf.point=$f,jf.lineStart=Wf,jf.lineEnd=Zf,sf=new g,gf.polygonStart()},polygonEnd:function(){gf.polygonEnd(),jf.point=Hf,jf.lineStart=Gf,jf.lineEnd=Vf,df<0?(nf=-(rf=180),ef=-(of=90)):sf>bc?of=90:sf<-1e-6&&(ef=-90),hf[0]=nf,hf[1]=rf},sphere:function(){nf=-(rf=180),ef=-(of=90)}};function Hf(t,n){lf.push(hf=[nf=t,rf=t]),nof&&(of=n)}function Xf(t,n){var e=xf([t*Sc,n*Sc]);if(ff){var r=Mf(ff,e),i=Mf([r[1],-r[0],0],r);Sf(i),i=mf(i);var o,a=t-af,u=a>0?1:-1,c=i[0]*Tc*u,f=Ec(a)>180;f^(u*afof&&(of=o):f^(u*af<(c=(c+360)%360-180)&&cof&&(of=n)),f?tKf(nf,rf)&&(rf=t):Kf(t,rf)>Kf(nf,rf)&&(nf=t):rf>=nf?(trf&&(rf=t)):t>af?Kf(nf,t)>Kf(nf,rf)&&(rf=t):Kf(t,rf)>Kf(nf,rf)&&(nf=t)}else lf.push(hf=[nf=t,rf=t]);nof&&(of=n),ff=e,af=t}function Gf(){jf.point=Xf}function Vf(){hf[0]=nf,hf[1]=rf,jf.point=Hf,ff=null}function $f(t,n){if(ff){var e=t-af;sf.add(Ec(e)>180?e+(e>0?360:-360):e)}else uf=t,cf=n;gf.point(t,n),Xf(t,n)}function Wf(){gf.lineStart()}function Zf(){$f(uf,cf),gf.lineEnd(),Ec(sf)>bc&&(nf=-(rf=180)),hf[0]=nf,hf[1]=rf,ff=null}function Kf(t,n){return(n-=t)<0?n+360:n}function Qf(t,n){return t[0]-n[0]}function Jf(t,n){return t[0]<=t[1]?t[0]<=n&&n<=t[1]:nxc?t+Math.round(-t/Ac)*Ac:t,n]}function ps(t,n,e){return(t%=Ac)?n||e?hs(ys(t),vs(n,e)):ys(t):n||e?vs(n,e):ds}function gs(t){return function(n,e){return[(n+=t)>xc?n-Ac:n<-xc?n+Ac:n,e]}}function ys(t){var n=gs(t);return n.invert=gs(-t),n}function vs(t,n){var e=Cc(t),r=Fc(t),i=Cc(n),o=Fc(n);function a(t,n){var a=Cc(n),u=Cc(t)*a,c=Fc(t)*a,f=Fc(n),s=f*e+u*r;return[Nc(c*i-s*o,u*e-f*r),Yc(s*i+c*o)]}return a.invert=function(t,n){var a=Cc(n),u=Cc(t)*a,c=Fc(t)*a,f=Fc(n),s=f*i-c*o;return[Nc(c*i+f*o,u*e+s*r),Yc(s*e-u*r)]},a}function _s(t){function n(n){return(n=t(n[0]*Sc,n[1]*Sc))[0]*=Tc,n[1]*=Tc,n}return t=ps(t[0]*Sc,t[1]*Sc,t.length>2?t[2]*Sc:0),n.invert=function(n){return(n=t.invert(n[0]*Sc,n[1]*Sc))[0]*=Tc,n[1]*=Tc,n},n}function bs(t,n,e,r,i,o){if(e){var a=Cc(n),u=Fc(n),c=r*e;null==i?(i=n+r*Ac,o=n-c/2):(i=ms(a,i),o=ms(a,o),(r>0?io)&&(i+=r*Ac));for(var f,s=i;r>0?s>o:s1&&n.push(n.pop().concat(n.shift()))},result:function(){var e=n;return n=[],t=null,e}}}function ws(t,n){return Ec(t[0]-n[0])=0;--o)i.point((s=f[o])[0],s[1]);else r(h.x,h.p.x,-1,i);h=h.p}f=(h=h.o).z,d=!d}while(!h.v);i.lineEnd()}}}function Ts(t){if(n=t.length){for(var n,e,r=0,i=t[0];++r=0?1:-1,E=S*T,k=E>xc,N=v*M;if(c.add(Nc(N*S*Fc(E),_*A+N*Cc(E))),a+=k?T+S*Ac:T,k^p>=e^x>=e){var C=Mf(xf(d),xf(m));Sf(C);var P=Mf(o,C);Sf(P);var z=(k^T>=0?-1:1)*Yc(P[2]);(r>z||r===z&&(C[0]||C[1]))&&(u+=k^T>=0?1:-1)}}return(a<-1e-6||a0){for(l||(i.polygonStart(),l=!0),i.lineStart(),t=0;t1&&2&c&&h.push(h.pop().concat(h.shift())),a.push(h.filter(Ns))}return h}}function Ns(t){return t.length>1}function Cs(t,n){return((t=t.x)[0]<0?t[1]-wc-bc:wc-t[1])-((n=n.x)[0]<0?n[1]-wc-bc:wc-n[1])}ds.invert=ds;var Ps=ks((function(){return!0}),(function(t){var n,e=NaN,r=NaN,i=NaN;return{lineStart:function(){t.lineStart(),n=1},point:function(o,a){var u=o>0?xc:-xc,c=Ec(o-e);Ec(c-xc)0?wc:-wc),t.point(i,r),t.lineEnd(),t.lineStart(),t.point(u,r),t.point(o,r),n=0):i!==u&&c>=xc&&(Ec(e-i)bc?kc((Fc(n)*(o=Cc(r))*Fc(e)-Fc(r)*(i=Cc(n))*Fc(t))/(i*o*a)):(n+r)/2}(e,r,o,a),t.point(i,r),t.lineEnd(),t.lineStart(),t.point(u,r),n=0),t.point(e=o,r=a),i=u},lineEnd:function(){t.lineEnd(),e=r=NaN},clean:function(){return 2-n}}}),(function(t,n,e,r){var i;if(null==t)i=e*wc,r.point(-xc,i),r.point(0,i),r.point(xc,i),r.point(xc,0),r.point(xc,-i),r.point(0,-i),r.point(-xc,-i),r.point(-xc,0),r.point(-xc,i);else if(Ec(t[0]-n[0])>bc){var o=t[0]0,i=Ec(n)>bc;function o(t,e){return Cc(t)*Cc(e)>n}function a(t,e,r){var i=[1,0,0],o=Mf(xf(t),xf(e)),a=wf(o,o),u=o[0],c=a-u*u;if(!c)return!r&&t;var f=n*a/c,s=-n*u/c,l=Mf(i,o),h=Tf(i,f);Af(h,Tf(o,s));var d=l,p=wf(h,d),g=wf(d,d),y=p*p-g*(wf(h,h)-1);if(!(y<0)){var v=Ic(y),_=Tf(d,(-p-v)/g);if(Af(_,h),_=mf(_),!r)return _;var b,m=t[0],x=e[0],w=t[1],M=e[1];x0^_[1]<(Ec(_[0]-m)xc^(m<=_[0]&&_[0]<=x)){var S=Tf(d,(-p+v)/g);return Af(S,h),[_,mf(S)]}}}function u(n,e){var i=r?t:xc-t,o=0;return n<-i?o|=1:n>i&&(o|=2),e<-i?o|=4:e>i&&(o|=8),o}return ks(o,(function(t){var n,e,c,f,s;return{lineStart:function(){f=c=!1,s=1},point:function(l,h){var d,p=[l,h],g=o(l,h),y=r?g?0:u(l,h):g?u(l+(l<0?xc:-xc),h):0;if(!n&&(f=c=g)&&t.lineStart(),g!==c&&(!(d=a(n,p))||ws(n,d)||ws(p,d))&&(p[2]=1),g!==c)s=0,g?(t.lineStart(),d=a(p,n),t.point(d[0],d[1])):(d=a(n,p),t.point(d[0],d[1],2),t.lineEnd()),n=d;else if(i&&n&&r^g){var v;y&e||!(v=a(p,n,!0))||(s=0,r?(t.lineStart(),t.point(v[0][0],v[0][1]),t.point(v[1][0],v[1][1]),t.lineEnd()):(t.point(v[1][0],v[1][1]),t.lineEnd(),t.lineStart(),t.point(v[0][0],v[0][1],3)))}!g||n&&ws(n,p)||t.point(p[0],p[1]),n=p,c=g,e=y},lineEnd:function(){c&&t.lineEnd(),n=null},clean:function(){return s|(f&&c)<<1}}}),(function(n,r,i,o){bs(o,t,e,i,n,r)}),r?[0,-t]:[-xc,t-xc])}var Ds,qs,Rs,Fs,Os=1e9,Is=-Os;function Us(t,n,e,r){function i(i,o){return t<=i&&i<=e&&n<=o&&o<=r}function o(i,o,u,f){var s=0,l=0;if(null==i||(s=a(i,u))!==(l=a(o,u))||c(i,o)<0^u>0)do{f.point(0===s||3===s?t:e,s>1?r:n)}while((s=(s+u+4)%4)!==l);else f.point(o[0],o[1])}function a(r,i){return Ec(r[0]-t)0?0:3:Ec(r[0]-e)0?2:1:Ec(r[1]-n)0?1:0:i>0?3:2}function u(t,n){return c(t.x,n.x)}function c(t,n){var e=a(t,1),r=a(n,1);return e!==r?e-r:0===e?n[1]-t[1]:1===e?t[0]-n[0]:2===e?t[1]-n[1]:n[0]-t[0]}return function(a){var c,f,s,l,h,d,p,g,y,v,_,b=a,m=xs(),x={point:w,lineStart:function(){x.point=M,f&&f.push(s=[]);v=!0,y=!1,p=g=NaN},lineEnd:function(){c&&(M(l,h),d&&y&&m.rejoin(),c.push(m.result()));x.point=w,y&&b.lineEnd()},polygonStart:function(){b=m,c=[],f=[],_=!0},polygonEnd:function(){var n=function(){for(var n=0,e=0,i=f.length;er&&(h-o)*(r-a)>(d-a)*(t-o)&&++n:d<=r&&(h-o)*(r-a)<(d-a)*(t-o)&&--n;return n}(),e=_&&n,i=(c=V(c)).length;(e||i)&&(a.polygonStart(),e&&(a.lineStart(),o(null,null,1,a),a.lineEnd()),i&&As(c,u,n,o,a),a.polygonEnd());b=a,c=f=s=null}};function w(t,n){i(t,n)&&b.point(t,n)}function M(o,a){var u=i(o,a);if(f&&s.push([o,a]),v)l=o,h=a,d=u,v=!1,u&&(b.lineStart(),b.point(o,a));else if(u&&y)b.point(o,a);else{var c=[p=Math.max(Is,Math.min(Os,p)),g=Math.max(Is,Math.min(Os,g))],m=[o=Math.max(Is,Math.min(Os,o)),a=Math.max(Is,Math.min(Os,a))];!function(t,n,e,r,i,o){var a,u=t[0],c=t[1],f=0,s=1,l=n[0]-u,h=n[1]-c;if(a=e-u,l||!(a>0)){if(a/=l,l<0){if(a0){if(a>s)return;a>f&&(f=a)}if(a=i-u,l||!(a<0)){if(a/=l,l<0){if(a>s)return;a>f&&(f=a)}else if(l>0){if(a0)){if(a/=h,h<0){if(a0){if(a>s)return;a>f&&(f=a)}if(a=o-c,h||!(a<0)){if(a/=h,h<0){if(a>s)return;a>f&&(f=a)}else if(h>0){if(a0&&(t[0]=u+f*l,t[1]=c+f*h),s<1&&(n[0]=u+s*l,n[1]=c+s*h),!0}}}}}(c,m,t,n,e,r)?u&&(b.lineStart(),b.point(o,a),_=!1):(y||(b.lineStart(),b.point(c[0],c[1])),b.point(m[0],m[1]),u||b.lineEnd(),_=!1)}p=o,g=a,y=u}return x}}var Bs={sphere:jc,point:jc,lineStart:function(){Bs.point=Ls,Bs.lineEnd=Ys},lineEnd:jc,polygonStart:jc,polygonEnd:jc};function Ys(){Bs.point=Bs.lineEnd=jc}function Ls(t,n){qs=t*=Sc,Rs=Fc(n*=Sc),Fs=Cc(n),Bs.point=js}function js(t,n){t*=Sc;var e=Fc(n*=Sc),r=Cc(n),i=Ec(t-qs),o=Cc(i),a=r*Fc(i),u=Fs*e-Rs*r*o,c=Rs*e+Fs*r*o;Ds.add(Nc(Ic(a*a+u*u),c)),qs=t,Rs=e,Fs=r}function Hs(t){return Ds=new g,Wc(t,Bs),+Ds}var Xs=[null,null],Gs={type:"LineString",coordinates:Xs};function Vs(t,n){return Xs[0]=t,Xs[1]=n,Hs(Gs)}var $s={Feature:function(t,n){return Zs(t.geometry,n)},FeatureCollection:function(t,n){for(var e=t.features,r=-1,i=e.length;++r0&&(i=Vs(t[o],t[o-1]))>0&&e<=i&&r<=i&&(e+r-i)*(1-Math.pow((e-r)/i,2))bc})).map(c)).concat(Z(Pc(o/d)*d,i,d).filter((function(t){return Ec(t%g)>bc})).map(f))}return v.lines=function(){return _().map((function(t){return{type:"LineString",coordinates:t}}))},v.outline=function(){return{type:"Polygon",coordinates:[s(r).concat(l(a).slice(1),s(e).reverse().slice(1),l(u).reverse().slice(1))]}},v.extent=function(t){return arguments.length?v.extentMajor(t).extentMinor(t):v.extentMinor()},v.extentMajor=function(t){return arguments.length?(r=+t[0][0],e=+t[1][0],u=+t[0][1],a=+t[1][1],r>e&&(t=r,r=e,e=t),u>a&&(t=u,u=a,a=t),v.precision(y)):[[r,u],[e,a]]},v.extentMinor=function(e){return arguments.length?(n=+e[0][0],t=+e[1][0],o=+e[0][1],i=+e[1][1],n>t&&(e=n,n=t,t=e),o>i&&(e=o,o=i,i=e),v.precision(y)):[[n,o],[t,i]]},v.step=function(t){return arguments.length?v.stepMajor(t).stepMinor(t):v.stepMinor()},v.stepMajor=function(t){return arguments.length?(p=+t[0],g=+t[1],v):[p,g]},v.stepMinor=function(t){return arguments.length?(h=+t[0],d=+t[1],v):[h,d]},v.precision=function(h){return arguments.length?(y=+h,c=el(o,i,90),f=rl(n,t,y),s=el(u,a,90),l=rl(r,e,y),v):y},v.extentMajor([[-180,-89.999999],[180,89.999999]]).extentMinor([[-180,-80.000001],[180,80.000001]])}var ol,al,ul,cl,fl=t=>t,sl=new g,ll=new g,hl={point:jc,lineStart:jc,lineEnd:jc,polygonStart:function(){hl.lineStart=dl,hl.lineEnd=yl},polygonEnd:function(){hl.lineStart=hl.lineEnd=hl.point=jc,sl.add(Ec(ll)),ll=new g},result:function(){var t=sl/2;return sl=new g,t}};function dl(){hl.point=pl}function pl(t,n){hl.point=gl,ol=ul=t,al=cl=n}function gl(t,n){ll.add(cl*t-ul*n),ul=t,cl=n}function yl(){gl(ol,al)}var vl=1/0,_l=vl,bl=-vl,ml=bl,xl={point:function(t,n){tbl&&(bl=t);n<_l&&(_l=n);n>ml&&(ml=n)},lineStart:jc,lineEnd:jc,polygonStart:jc,polygonEnd:jc,result:function(){var t=[[vl,_l],[bl,ml]];return bl=ml=-(_l=vl=1/0),t}};var wl,Ml,Al,Tl,Sl=0,El=0,kl=0,Nl=0,Cl=0,Pl=0,zl=0,Dl=0,ql=0,Rl={point:Fl,lineStart:Ol,lineEnd:Bl,polygonStart:function(){Rl.lineStart=Yl,Rl.lineEnd=Ll},polygonEnd:function(){Rl.point=Fl,Rl.lineStart=Ol,Rl.lineEnd=Bl},result:function(){var t=ql?[zl/ql,Dl/ql]:Pl?[Nl/Pl,Cl/Pl]:kl?[Sl/kl,El/kl]:[NaN,NaN];return Sl=El=kl=Nl=Cl=Pl=zl=Dl=ql=0,t}};function Fl(t,n){Sl+=t,El+=n,++kl}function Ol(){Rl.point=Il}function Il(t,n){Rl.point=Ul,Fl(Al=t,Tl=n)}function Ul(t,n){var e=t-Al,r=n-Tl,i=Ic(e*e+r*r);Nl+=i*(Al+t)/2,Cl+=i*(Tl+n)/2,Pl+=i,Fl(Al=t,Tl=n)}function Bl(){Rl.point=Fl}function Yl(){Rl.point=jl}function Ll(){Hl(wl,Ml)}function jl(t,n){Rl.point=Hl,Fl(wl=Al=t,Ml=Tl=n)}function Hl(t,n){var e=t-Al,r=n-Tl,i=Ic(e*e+r*r);Nl+=i*(Al+t)/2,Cl+=i*(Tl+n)/2,Pl+=i,zl+=(i=Tl*t-Al*n)*(Al+t),Dl+=i*(Tl+n),ql+=3*i,Fl(Al=t,Tl=n)}function Xl(t){this._context=t}Xl.prototype={_radius:4.5,pointRadius:function(t){return this._radius=t,this},polygonStart:function(){this._line=0},polygonEnd:function(){this._line=NaN},lineStart:function(){this._point=0},lineEnd:function(){0===this._line&&this._context.closePath(),this._point=NaN},point:function(t,n){switch(this._point){case 0:this._context.moveTo(t,n),this._point=1;break;case 1:this._context.lineTo(t,n);break;default:this._context.moveTo(t+this._radius,n),this._context.arc(t,n,this._radius,0,Ac)}},result:jc};var Gl,Vl,$l,Wl,Zl,Kl=new g,Ql={point:jc,lineStart:function(){Ql.point=Jl},lineEnd:function(){Gl&&th(Vl,$l),Ql.point=jc},polygonStart:function(){Gl=!0},polygonEnd:function(){Gl=null},result:function(){var t=+Kl;return Kl=new g,t}};function Jl(t,n){Ql.point=th,Vl=Wl=t,$l=Zl=n}function th(t,n){Wl-=t,Zl-=n,Kl.add(Ic(Wl*Wl+Zl*Zl)),Wl=t,Zl=n}function nh(){this._string=[]}function eh(t){return"m0,"+t+"a"+t+","+t+" 0 1,1 0,"+-2*t+"a"+t+","+t+" 0 1,1 0,"+2*t+"z"}function rh(t){return function(n){var e=new ih;for(var r in t)e[r]=t[r];return e.stream=n,e}}function ih(){}function oh(t,n,e){var r=t.clipExtent&&t.clipExtent();return t.scale(150).translate([0,0]),null!=r&&t.clipExtent(null),Wc(e,t.stream(xl)),n(xl.result()),null!=r&&t.clipExtent(r),t}function ah(t,n,e){return oh(t,(function(e){var r=n[1][0]-n[0][0],i=n[1][1]-n[0][1],o=Math.min(r/(e[1][0]-e[0][0]),i/(e[1][1]-e[0][1])),a=+n[0][0]+(r-o*(e[1][0]+e[0][0]))/2,u=+n[0][1]+(i-o*(e[1][1]+e[0][1]))/2;t.scale(150*o).translate([a,u])}),e)}function uh(t,n,e){return ah(t,[[0,0],n],e)}function ch(t,n,e){return oh(t,(function(e){var r=+n,i=r/(e[1][0]-e[0][0]),o=(r-i*(e[1][0]+e[0][0]))/2,a=-i*e[0][1];t.scale(150*i).translate([o,a])}),e)}function fh(t,n,e){return oh(t,(function(e){var r=+n,i=r/(e[1][1]-e[0][1]),o=-i*e[0][0],a=(r-i*(e[1][1]+e[0][1]))/2;t.scale(150*i).translate([o,a])}),e)}nh.prototype={_radius:4.5,_circle:eh(4.5),pointRadius:function(t){return(t=+t)!==this._radius&&(this._radius=t,this._circle=null),this},polygonStart:function(){this._line=0},polygonEnd:function(){this._line=NaN},lineStart:function(){this._point=0},lineEnd:function(){0===this._line&&this._string.push("Z"),this._point=NaN},point:function(t,n){switch(this._point){case 0:this._string.push("M",t,",",n),this._point=1;break;case 1:this._string.push("L",t,",",n);break;default:null==this._circle&&(this._circle=eh(this._radius)),this._string.push("M",t,",",n,this._circle)}},result:function(){if(this._string.length){var t=this._string.join("");return this._string=[],t}return null}},ih.prototype={constructor:ih,point:function(t,n){this.stream.point(t,n)},sphere:function(){this.stream.sphere()},lineStart:function(){this.stream.lineStart()},lineEnd:function(){this.stream.lineEnd()},polygonStart:function(){this.stream.polygonStart()},polygonEnd:function(){this.stream.polygonEnd()}};var sh=Cc(30*Sc);function lh(t,n){return+n?function(t,n){function e(r,i,o,a,u,c,f,s,l,h,d,p,g,y){var v=f-r,_=s-i,b=v*v+_*_;if(b>4*n&&g--){var m=a+h,x=u+d,w=c+p,M=Ic(m*m+x*x+w*w),A=Yc(w/=M),T=Ec(Ec(w)-1)n||Ec((v*N+_*C)/b-.5)>.3||a*h+u*d+c*p2?t[2]%360*Sc:0,N()):[y*Tc,v*Tc,_*Tc]},E.angle=function(t){return arguments.length?(b=t%360*Sc,N()):b*Tc},E.reflectX=function(t){return arguments.length?(m=t?-1:1,N()):m<0},E.reflectY=function(t){return arguments.length?(x=t?-1:1,N()):x<0},E.precision=function(t){return arguments.length?(a=lh(u,S=t*t),C()):Ic(S)},E.fitExtent=function(t,n){return ah(E,t,n)},E.fitSize=function(t,n){return uh(E,t,n)},E.fitWidth=function(t,n){return ch(E,t,n)},E.fitHeight=function(t,n){return fh(E,t,n)},function(){return n=t.apply(this,arguments),E.invert=n.invert&&k,N()}}function yh(t){var n=0,e=xc/3,r=gh(t),i=r(n,e);return i.parallels=function(t){return arguments.length?r(n=t[0]*Sc,e=t[1]*Sc):[n*Tc,e*Tc]},i}function vh(t,n){var e=Fc(t),r=(e+Fc(n))/2;if(Ec(r)0?n<-wc+bc&&(n=-wc+bc):n>wc-bc&&(n=wc-bc);var e=i/Rc(Sh(n),r);return[e*Fc(r*t),i-e*Cc(r*t)]}return o.invert=function(t,n){var e=i-n,o=Oc(r)*Ic(t*t+e*e),a=Nc(t,Ec(e))*Oc(e);return e*r<0&&(a-=xc*Oc(t)*Oc(e)),[a/r,2*kc(Rc(i/o,1/r))-wc]},o}function kh(t,n){return[t,n]}function Nh(t,n){var e=Cc(t),r=t===n?Fc(t):(e-Cc(n))/(n-t),i=e/r+t;if(Ec(r)=0;)n+=e[r].value;else n=1;t.value=n}function Xh(t,n){t instanceof Map?(t=[void 0,t],void 0===n&&(n=Vh)):void 0===n&&(n=Gh);for(var e,r,i,o,a,u=new Zh(t),c=[u];e=c.pop();)if((i=n(e.data))&&(a=(i=Array.from(i)).length))for(e.children=i,o=a-1;o>=0;--o)c.push(r=i[o]=new Zh(i[o])),r.parent=e,r.depth=e.depth+1;return u.eachBefore(Wh)}function Gh(t){return t.children}function Vh(t){return Array.isArray(t)?t[1]:null}function $h(t){void 0!==t.data.value&&(t.value=t.data.value),t.data=t.data.data}function Wh(t){var n=0;do{t.height=n}while((t=t.parent)&&t.height<++n)}function Zh(t){this.data=t,this.depth=this.height=0,this.parent=null}function Kh(t){for(var n,e,r=0,i=(t=function(t){for(var n,e,r=t.length;r;)e=Math.random()*r--|0,n=t[r],t[r]=t[e],t[e]=n;return t}(Array.from(t))).length,o=[];r0&&e*e>r*r+i*i}function nd(t,n){for(var e=0;e(a*=a)?(r=(f+a-i)/(2*f),o=Math.sqrt(Math.max(0,a/f-r*r)),e.x=t.x-r*u-o*c,e.y=t.y-r*c+o*u):(r=(f+i-a)/(2*f),o=Math.sqrt(Math.max(0,i/f-r*r)),e.x=n.x+r*u-o*c,e.y=n.y+r*c+o*u)):(e.x=n.x+e.r,e.y=n.y)}function ad(t,n){var e=t.r+n.r-1e-6,r=n.x-t.x,i=n.y-t.y;return e>0&&e*e>r*r+i*i}function ud(t){var n=t._,e=t.next._,r=n.r+e.r,i=(n.x*e.r+e.x*n.r)/r,o=(n.y*e.r+e.y*n.r)/r;return i*i+o*o}function cd(t){this._=t,this.next=null,this.previous=null}function fd(t){if(!(i=(t=function(t){return"object"==typeof t&&"length"in t?t:Array.from(t)}(t)).length))return 0;var n,e,r,i,o,a,u,c,f,s,l;if((n=t[0]).x=0,n.y=0,!(i>1))return n.r;if(e=t[1],n.x=-e.r,e.x=n.r,e.y=0,!(i>2))return n.r+e.r;od(e,n,r=t[2]),n=new cd(n),e=new cd(e),r=new cd(r),n.next=r.previous=e,e.next=n.previous=r,r.next=e.previous=n;t:for(u=3;ubc&&--i>0);return[t/(.8707+(o=r*r)*(o*(o*o*o*(.003971-.001529*o)-.013791)-.131979)),r]},Ih.invert=xh(Yc),Uh.invert=xh((function(t){return 2*kc(t)})),Bh.invert=function(t,n){return[-n,2*kc(zc(t))-wc]},Zh.prototype=Xh.prototype={constructor:Zh,count:function(){return this.eachAfter(Hh)},each:function(t,n){let e=-1;for(const r of this)t.call(n,r,++e,this);return this},eachAfter:function(t,n){for(var e,r,i,o=this,a=[o],u=[],c=-1;o=a.pop();)if(u.push(o),e=o.children)for(r=0,i=e.length;r=0;--r)o.push(e[r]);return this},find:function(t,n){let e=-1;for(const r of this)if(t.call(n,r,++e,this))return r},sum:function(t){return this.eachAfter((function(n){for(var e=+t(n.data)||0,r=n.children,i=r&&r.length;--i>=0;)e+=r[i].value;n.value=e}))},sort:function(t){return this.eachBefore((function(n){n.children&&n.children.sort(t)}))},path:function(t){for(var n=this,e=function(t,n){if(t===n)return t;var e=t.ancestors(),r=n.ancestors(),i=null;t=e.pop(),n=r.pop();for(;t===n;)i=t,t=e.pop(),n=r.pop();return i}(n,t),r=[n];n!==e;)n=n.parent,r.push(n);for(var i=r.length;t!==e;)r.splice(i,0,t),t=t.parent;return r},ancestors:function(){for(var t=this,n=[t];t=t.parent;)n.push(t);return n},descendants:function(){return Array.from(this)},leaves:function(){var t=[];return this.eachBefore((function(n){n.children||t.push(n)})),t},links:function(){var t=this,n=[];return t.each((function(e){e!==t&&n.push({source:e.parent,target:e})})),n},copy:function(){return Xh(this).eachBefore($h)},[Symbol.iterator]:function*(){var t,n,e,r,i=this,o=[i];do{for(t=o.reverse(),o=[];i=t.pop();)if(yield i,n=i.children)for(e=0,r=n.length;eh&&(h=u),y=s*s*g,(d=Math.max(h/y,y/l))>p){s-=u;break}p=d}v.push(a={value:s,dice:c1?n:1)},e}(Pd);var qd=function t(n){function e(t,e,r,i,o){if((a=t._squarify)&&a.ratio===n)for(var a,u,c,f,s,l=-1,h=a.length,d=t.value;++l1?n:1)},e}(Pd);function Rd(t,n,e){return(n[0]-t[0])*(e[1]-t[1])-(n[1]-t[1])*(e[0]-t[0])}function Fd(t,n){return t[0]-n[0]||t[1]-n[1]}function Od(t){const n=t.length,e=[0,1];let r,i=2;for(r=2;r1&&Rd(t[e[i-2]],t[e[i-1]],t[r])<=0;)--i;e[i++]=r}return e.slice(0,i)}var Id=Math.random,Ud=function t(n){function e(t,e){return t=null==t?0:+t,e=null==e?1:+e,1===arguments.length?(e=t,t=0):e-=t,function(){return n()*e+t}}return e.source=t,e}(Id),Bd=function t(n){function e(t,e){return arguments.length<2&&(e=t,t=0),t=Math.floor(t),e=Math.floor(e)-t,function(){return Math.floor(n()*e+t)}}return e.source=t,e}(Id),Yd=function t(n){function e(t,e){var r,i;return t=null==t?0:+t,e=null==e?1:+e,function(){var o;if(null!=r)o=r,r=null;else do{r=2*n()-1,o=2*n()-1,i=r*r+o*o}while(!i||i>1);return t+e*o*Math.sqrt(-2*Math.log(i)/i)}}return e.source=t,e}(Id),Ld=function t(n){var e=Yd.source(n);function r(){var t=e.apply(this,arguments);return function(){return Math.exp(t())}}return r.source=t,r}(Id),jd=function t(n){function e(t){return(t=+t)<=0?()=>0:function(){for(var e=0,r=t;r>1;--r)e+=n();return e+r*n()}}return e.source=t,e}(Id),Hd=function t(n){var e=jd.source(n);function r(t){if(0==(t=+t))return n;var r=e(t);return function(){return r()/t}}return r.source=t,r}(Id),Xd=function t(n){function e(t){return function(){return-Math.log1p(-n())/t}}return e.source=t,e}(Id),Gd=function t(n){function e(t){if((t=+t)<0)throw new RangeError("invalid alpha");return t=1/-t,function(){return Math.pow(1-n(),t)}}return e.source=t,e}(Id),Vd=function t(n){function e(t){if((t=+t)<0||t>1)throw new RangeError("invalid p");return function(){return Math.floor(n()+t)}}return e.source=t,e}(Id),$d=function t(n){function e(t){if((t=+t)<0||t>1)throw new RangeError("invalid p");return 0===t?()=>1/0:1===t?()=>1:(t=Math.log1p(-t),function(){return 1+Math.floor(Math.log1p(-n())/t)})}return e.source=t,e}(Id),Wd=function t(n){var e=Yd.source(n)();function r(t,r){if((t=+t)<0)throw new RangeError("invalid k");if(0===t)return()=>0;if(r=null==r?1:+r,1===t)return()=>-Math.log1p(-n())*r;var i=(t<1?t+1:t)-1/3,o=1/(3*Math.sqrt(i)),a=t<1?()=>Math.pow(n(),1/t):()=>1;return function(){do{do{var t=e(),u=1+o*t}while(u<=0);u*=u*u;var c=1-n()}while(c>=1-.0331*t*t*t*t&&Math.log(c)>=.5*t*t+i*(1-u+Math.log(u)));return i*u*a()*r}}return r.source=t,r}(Id),Zd=function t(n){var e=Wd.source(n);function r(t,n){var r=e(t),i=e(n);return function(){var t=r();return 0===t?0:t/(t+i())}}return r.source=t,r}(Id),Kd=function t(n){var e=$d.source(n),r=Zd.source(n);function i(t,n){return t=+t,(n=+n)>=1?()=>t:n<=0?()=>0:function(){for(var i=0,o=t,a=n;o*a>16&&o*(1-a)>16;){var u=Math.floor((o+1)*a),c=r(u,o-u+1)();c<=a?(i+=u,o-=u,a=(a-c)/(1-c)):(o=u-1,a/=c)}for(var f=a<.5,s=e(f?a:1-a),l=s(),h=0;l<=o;++h)l+=s();return i+(f?h:o-h)}}return i.source=t,i}(Id),Qd=function t(n){function e(t,e,r){var i;return 0==(t=+t)?i=t=>-Math.log(t):(t=1/t,i=n=>Math.pow(n,t)),e=null==e?0:+e,r=null==r?1:+r,function(){return e+r*i(-Math.log1p(-n()))}}return e.source=t,e}(Id),Jd=function t(n){function e(t,e){return t=null==t?0:+t,e=null==e?1:+e,function(){return t+e*Math.tan(Math.PI*n())}}return e.source=t,e}(Id),tp=function t(n){function e(t,e){return t=null==t?0:+t,e=null==e?1:+e,function(){var r=n();return t+e*Math.log(r/(1-r))}}return e.source=t,e}(Id),np=function t(n){var e=Wd.source(n),r=Kd.source(n);function i(t){return function(){for(var i=0,o=t;o>16;){var a=Math.floor(.875*o),u=e(a)();if(u>o)return i+r(a-1,o/u)();i+=a,o-=u}for(var c=-Math.log1p(-n()),f=0;c<=o;++f)c-=Math.log1p(-n());return i+f}}return i.source=t,i}(Id);const ep=1/4294967296;function rp(t,n){switch(arguments.length){case 0:break;case 1:this.range(t);break;default:this.range(n).domain(t)}return this}function ip(t,n){switch(arguments.length){case 0:break;case 1:"function"==typeof t?this.interpolator(t):this.range(t);break;default:this.domain(t),"function"==typeof n?this.interpolator(n):this.range(n)}return this}const op=Symbol("implicit");function ap(){var t=new Map,n=[],e=[],r=op;function i(i){var o=i+"",a=t.get(o);if(!a){if(r!==op)return r;t.set(o,a=n.push(i))}return e[(a-1)%e.length]}return i.domain=function(e){if(!arguments.length)return n.slice();n=[],t=new Map;for(const r of e){const e=r+"";t.has(e)||t.set(e,n.push(r))}return i},i.range=function(t){return arguments.length?(e=Array.from(t),i):e.slice()},i.unknown=function(t){return arguments.length?(r=t,i):r},i.copy=function(){return ap(n,e).unknown(r)},rp.apply(i,arguments),i}function up(){var t,n,e=ap().unknown(void 0),r=e.domain,i=e.range,o=0,a=1,u=!1,c=0,f=0,s=.5;function l(){var e=r().length,l=an&&(e=t,t=n,n=e),function(e){return Math.max(t,Math.min(n,e))}}(a[0],a[t-1])),r=t>2?pp:dp,i=o=null,l}function l(n){return null==n||isNaN(n=+n)?e:(i||(i=r(a.map(t),u,c)))(t(f(n)))}return l.invert=function(e){return f(n((o||(o=r(u,a.map(t),_r)))(e)))},l.domain=function(t){return arguments.length?(a=Array.from(t,fp),s()):a.slice()},l.range=function(t){return arguments.length?(u=Array.from(t),s()):u.slice()},l.rangeRound=function(t){return u=Array.from(t),c=Ar,s()},l.clamp=function(t){return arguments.length?(f=!!t||lp,s()):f!==lp},l.interpolate=function(t){return arguments.length?(c=t,s()):c},l.unknown=function(t){return arguments.length?(e=t,l):e},function(e,r){return t=e,n=r,s()}}function vp(){return yp()(lp,lp)}function _p(n,e,r,i){var o,a=F(n,e,r);switch((i=ac(null==i?",f":i)).type){case"s":var u=Math.max(Math.abs(n),Math.abs(e));return null!=i.precision||isNaN(o=vc(a,u))||(i.precision=o),t.formatPrefix(i,u);case"":case"e":case"g":case"p":case"r":null!=i.precision||isNaN(o=_c(a,Math.max(Math.abs(n),Math.abs(e))))||(i.precision=o-("e"===i.type));break;case"f":case"%":null!=i.precision||isNaN(o=yc(a))||(i.precision=o-2*("%"===i.type))}return t.format(i)}function bp(t){var n=t.domain;return t.ticks=function(t){var e=n();return q(e[0],e[e.length-1],null==t?10:t)},t.tickFormat=function(t,e){var r=n();return _p(r[0],r[r.length-1],null==t?10:t,e)},t.nice=function(e){null==e&&(e=10);var r,i,o=n(),a=0,u=o.length-1,c=o[a],f=o[u],s=10;for(f0;){if((i=R(c,f,e))===r)return o[a]=c,o[u]=f,n(o);if(i>0)c=Math.floor(c/i)*i,f=Math.ceil(f/i)*i;else{if(!(i<0))break;c=Math.ceil(c*i)/i,f=Math.floor(f*i)/i}r=i}return t},t}function mp(t,n){var e,r=0,i=(t=t.slice()).length-1,o=t[r],a=t[i];return a0){for(;h<=d;++h)for(s=1,f=r(h);sc)break;g.push(l)}}else for(;h<=d;++h)for(s=a-1,f=r(h);s>=1;--s)if(!((l=f*s)c)break;g.push(l)}2*g.length0))return u;do{u.push(a=new Date(+e)),n(e,o),t(e)}while(a=n)for(;t(n),!e(n);)n.setTime(n-1)}),(function(t,r){if(t>=t)if(r<0)for(;++r<=0;)for(;n(t,-1),!e(t););else for(;--r>=0;)for(;n(t,1),!e(t););}))},e&&(i.count=function(n,r){return Ip.setTime(+n),Up.setTime(+r),t(Ip),t(Up),Math.floor(e(Ip,Up))},i.every=function(t){return t=Math.floor(t),isFinite(t)&&t>0?t>1?i.filter(r?function(n){return r(n)%t==0}:function(n){return i.count(0,n)%t==0}):i:null}),i}var Yp=Bp((function(){}),(function(t,n){t.setTime(+t+n)}),(function(t,n){return n-t}));Yp.every=function(t){return t=Math.floor(t),isFinite(t)&&t>0?t>1?Bp((function(n){n.setTime(Math.floor(n/t)*t)}),(function(n,e){n.setTime(+n+e*t)}),(function(n,e){return(e-n)/t})):Yp:null};var Lp=Yp.range;const jp=1e3,Hp=6e4,Xp=36e5,Gp=864e5,Vp=6048e5,$p=2592e6,Wp=31536e6;var Zp=Bp((function(t){t.setTime(t-t.getMilliseconds())}),(function(t,n){t.setTime(+t+n*jp)}),(function(t,n){return(n-t)/jp}),(function(t){return t.getUTCSeconds()})),Kp=Zp.range,Qp=Bp((function(t){t.setTime(t-t.getMilliseconds()-t.getSeconds()*jp)}),(function(t,n){t.setTime(+t+n*Hp)}),(function(t,n){return(n-t)/Hp}),(function(t){return t.getMinutes()})),Jp=Qp.range,tg=Bp((function(t){t.setTime(t-t.getMilliseconds()-t.getSeconds()*jp-t.getMinutes()*Hp)}),(function(t,n){t.setTime(+t+n*Xp)}),(function(t,n){return(n-t)/Xp}),(function(t){return t.getHours()})),ng=tg.range,eg=Bp((t=>t.setHours(0,0,0,0)),((t,n)=>t.setDate(t.getDate()+n)),((t,n)=>(n-t-(n.getTimezoneOffset()-t.getTimezoneOffset())*Hp)/Gp),(t=>t.getDate()-1)),rg=eg.range;function ig(t){return Bp((function(n){n.setDate(n.getDate()-(n.getDay()+7-t)%7),n.setHours(0,0,0,0)}),(function(t,n){t.setDate(t.getDate()+7*n)}),(function(t,n){return(n-t-(n.getTimezoneOffset()-t.getTimezoneOffset())*Hp)/Vp}))}var og=ig(0),ag=ig(1),ug=ig(2),cg=ig(3),fg=ig(4),sg=ig(5),lg=ig(6),hg=og.range,dg=ag.range,pg=ug.range,gg=cg.range,yg=fg.range,vg=sg.range,_g=lg.range,bg=Bp((function(t){t.setDate(1),t.setHours(0,0,0,0)}),(function(t,n){t.setMonth(t.getMonth()+n)}),(function(t,n){return n.getMonth()-t.getMonth()+12*(n.getFullYear()-t.getFullYear())}),(function(t){return t.getMonth()})),mg=bg.range,xg=Bp((function(t){t.setMonth(0,1),t.setHours(0,0,0,0)}),(function(t,n){t.setFullYear(t.getFullYear()+n)}),(function(t,n){return n.getFullYear()-t.getFullYear()}),(function(t){return t.getFullYear()}));xg.every=function(t){return isFinite(t=Math.floor(t))&&t>0?Bp((function(n){n.setFullYear(Math.floor(n.getFullYear()/t)*t),n.setMonth(0,1),n.setHours(0,0,0,0)}),(function(n,e){n.setFullYear(n.getFullYear()+e*t)})):null};var wg=xg.range,Mg=Bp((function(t){t.setUTCSeconds(0,0)}),(function(t,n){t.setTime(+t+n*Hp)}),(function(t,n){return(n-t)/Hp}),(function(t){return t.getUTCMinutes()})),Ag=Mg.range,Tg=Bp((function(t){t.setUTCMinutes(0,0,0)}),(function(t,n){t.setTime(+t+n*Xp)}),(function(t,n){return(n-t)/Xp}),(function(t){return t.getUTCHours()})),Sg=Tg.range,Eg=Bp((function(t){t.setUTCHours(0,0,0,0)}),(function(t,n){t.setUTCDate(t.getUTCDate()+n)}),(function(t,n){return(n-t)/Gp}),(function(t){return t.getUTCDate()-1})),kg=Eg.range;function Ng(t){return Bp((function(n){n.setUTCDate(n.getUTCDate()-(n.getUTCDay()+7-t)%7),n.setUTCHours(0,0,0,0)}),(function(t,n){t.setUTCDate(t.getUTCDate()+7*n)}),(function(t,n){return(n-t)/Vp}))}var Cg=Ng(0),Pg=Ng(1),zg=Ng(2),Dg=Ng(3),qg=Ng(4),Rg=Ng(5),Fg=Ng(6),Og=Cg.range,Ig=Pg.range,Ug=zg.range,Bg=Dg.range,Yg=qg.range,Lg=Rg.range,jg=Fg.range,Hg=Bp((function(t){t.setUTCDate(1),t.setUTCHours(0,0,0,0)}),(function(t,n){t.setUTCMonth(t.getUTCMonth()+n)}),(function(t,n){return n.getUTCMonth()-t.getUTCMonth()+12*(n.getUTCFullYear()-t.getUTCFullYear())}),(function(t){return t.getUTCMonth()})),Xg=Hg.range,Gg=Bp((function(t){t.setUTCMonth(0,1),t.setUTCHours(0,0,0,0)}),(function(t,n){t.setUTCFullYear(t.getUTCFullYear()+n)}),(function(t,n){return n.getUTCFullYear()-t.getUTCFullYear()}),(function(t){return t.getUTCFullYear()}));Gg.every=function(t){return isFinite(t=Math.floor(t))&&t>0?Bp((function(n){n.setUTCFullYear(Math.floor(n.getUTCFullYear()/t)*t),n.setUTCMonth(0,1),n.setUTCHours(0,0,0,0)}),(function(n,e){n.setUTCFullYear(n.getUTCFullYear()+e*t)})):null};var Vg=Gg.range;function $g(t,n,r,i,o,a){const u=[[Zp,1,jp],[Zp,5,5e3],[Zp,15,15e3],[Zp,30,3e4],[a,1,Hp],[a,5,3e5],[a,15,9e5],[a,30,18e5],[o,1,Xp],[o,3,108e5],[o,6,216e5],[o,12,432e5],[i,1,Gp],[i,2,1728e5],[r,1,Vp],[n,1,$p],[n,3,7776e6],[t,1,Wp]];function c(n,r,i){const o=Math.abs(r-n)/i,a=e((([,,t])=>t)).right(u,o);if(a===u.length)return t.every(F(n/Wp,r/Wp,i));if(0===a)return Yp.every(Math.max(F(n,r,i),1));const[c,f]=u[o/u[a-1][2]=12)]},q:function(t){return 1+~~(t.getMonth()/3)},Q:bv,s:mv,S:By,u:Yy,U:Ly,V:Hy,w:Xy,W:Gy,x:null,X:null,y:Vy,Y:Wy,Z:Ky,"%":_v},m={a:function(t){return a[t.getUTCDay()]},A:function(t){return o[t.getUTCDay()]},b:function(t){return c[t.getUTCMonth()]},B:function(t){return u[t.getUTCMonth()]},c:null,d:Qy,e:Qy,f:rv,g:pv,G:yv,H:Jy,I:tv,j:nv,L:ev,m:iv,M:ov,p:function(t){return i[+(t.getUTCHours()>=12)]},q:function(t){return 1+~~(t.getUTCMonth()/3)},Q:bv,s:mv,S:av,u:uv,U:cv,V:sv,w:lv,W:hv,x:null,X:null,y:dv,Y:gv,Z:vv,"%":_v},x={a:function(t,n,e){var r=d.exec(n.slice(e));return r?(t.w=p.get(r[0].toLowerCase()),e+r[0].length):-1},A:function(t,n,e){var r=l.exec(n.slice(e));return r?(t.w=h.get(r[0].toLowerCase()),e+r[0].length):-1},b:function(t,n,e){var r=v.exec(n.slice(e));return r?(t.m=_.get(r[0].toLowerCase()),e+r[0].length):-1},B:function(t,n,e){var r=g.exec(n.slice(e));return r?(t.m=y.get(r[0].toLowerCase()),e+r[0].length):-1},c:function(t,e,r){return A(t,n,e,r)},d:wy,e:wy,f:ky,g:_y,G:vy,H:Ay,I:Ay,j:My,L:Ey,m:xy,M:Ty,p:function(t,n,e){var r=f.exec(n.slice(e));return r?(t.p=s.get(r[0].toLowerCase()),e+r[0].length):-1},q:my,Q:Cy,s:Py,S:Sy,u:dy,U:py,V:gy,w:hy,W:yy,x:function(t,n,r){return A(t,e,n,r)},X:function(t,n,e){return A(t,r,n,e)},y:_y,Y:vy,Z:by,"%":Ny};function w(t,n){return function(e){var r,i,o,a=[],u=-1,c=0,f=t.length;for(e instanceof Date||(e=new Date(+e));++u53)return null;"w"in o||(o.w=1),"Z"in o?(i=(r=ty(ny(o.y,0,1))).getUTCDay(),r=i>4||0===i?Pg.ceil(r):Pg(r),r=Eg.offset(r,7*(o.V-1)),o.y=r.getUTCFullYear(),o.m=r.getUTCMonth(),o.d=r.getUTCDate()+(o.w+6)%7):(i=(r=Jg(ny(o.y,0,1))).getDay(),r=i>4||0===i?ag.ceil(r):ag(r),r=eg.offset(r,7*(o.V-1)),o.y=r.getFullYear(),o.m=r.getMonth(),o.d=r.getDate()+(o.w+6)%7)}else("W"in o||"U"in o)&&("w"in o||(o.w="u"in o?o.u%7:"W"in o?1:0),i="Z"in o?ty(ny(o.y,0,1)).getUTCDay():Jg(ny(o.y,0,1)).getDay(),o.m=0,o.d="W"in o?(o.w+6)%7+7*o.W-(i+5)%7:o.w+7*o.U-(i+6)%7);return"Z"in o?(o.H+=o.Z/100|0,o.M+=o.Z%100,ty(o)):Jg(o)}}function A(t,n,e,r){for(var i,o,a=0,u=n.length,c=e.length;a=c)return-1;if(37===(i=n.charCodeAt(a++))){if(i=n.charAt(a++),!(o=x[i in iy?n.charAt(a++):i])||(r=o(t,e,r))<0)return-1}else if(i!=e.charCodeAt(r++))return-1}return r}return b.x=w(e,b),b.X=w(r,b),b.c=w(n,b),m.x=w(e,m),m.X=w(r,m),m.c=w(n,m),{format:function(t){var n=w(t+="",b);return n.toString=function(){return t},n},parse:function(t){var n=M(t+="",!1);return n.toString=function(){return t},n},utcFormat:function(t){var n=w(t+="",m);return n.toString=function(){return t},n},utcParse:function(t){var n=M(t+="",!0);return n.toString=function(){return t},n}}}var ry,iy={"-":"",_:" ",0:"0"},oy=/^\s*\d+/,ay=/^%/,uy=/[\\^$*+?|[\]().{}]/g;function cy(t,n,e){var r=t<0?"-":"",i=(r?-t:t)+"",o=i.length;return r+(o[t.toLowerCase(),n])))}function hy(t,n,e){var r=oy.exec(n.slice(e,e+1));return r?(t.w=+r[0],e+r[0].length):-1}function dy(t,n,e){var r=oy.exec(n.slice(e,e+1));return r?(t.u=+r[0],e+r[0].length):-1}function py(t,n,e){var r=oy.exec(n.slice(e,e+2));return r?(t.U=+r[0],e+r[0].length):-1}function gy(t,n,e){var r=oy.exec(n.slice(e,e+2));return r?(t.V=+r[0],e+r[0].length):-1}function yy(t,n,e){var r=oy.exec(n.slice(e,e+2));return r?(t.W=+r[0],e+r[0].length):-1}function vy(t,n,e){var r=oy.exec(n.slice(e,e+4));return r?(t.y=+r[0],e+r[0].length):-1}function _y(t,n,e){var r=oy.exec(n.slice(e,e+2));return r?(t.y=+r[0]+(+r[0]>68?1900:2e3),e+r[0].length):-1}function by(t,n,e){var r=/^(Z)|([+-]\d\d)(?::?(\d\d))?/.exec(n.slice(e,e+6));return r?(t.Z=r[1]?0:-(r[2]+(r[3]||"00")),e+r[0].length):-1}function my(t,n,e){var r=oy.exec(n.slice(e,e+1));return r?(t.q=3*r[0]-3,e+r[0].length):-1}function xy(t,n,e){var r=oy.exec(n.slice(e,e+2));return r?(t.m=r[0]-1,e+r[0].length):-1}function wy(t,n,e){var r=oy.exec(n.slice(e,e+2));return r?(t.d=+r[0],e+r[0].length):-1}function My(t,n,e){var r=oy.exec(n.slice(e,e+3));return r?(t.m=0,t.d=+r[0],e+r[0].length):-1}function Ay(t,n,e){var r=oy.exec(n.slice(e,e+2));return r?(t.H=+r[0],e+r[0].length):-1}function Ty(t,n,e){var r=oy.exec(n.slice(e,e+2));return r?(t.M=+r[0],e+r[0].length):-1}function Sy(t,n,e){var r=oy.exec(n.slice(e,e+2));return r?(t.S=+r[0],e+r[0].length):-1}function Ey(t,n,e){var r=oy.exec(n.slice(e,e+3));return r?(t.L=+r[0],e+r[0].length):-1}function ky(t,n,e){var r=oy.exec(n.slice(e,e+6));return r?(t.L=Math.floor(r[0]/1e3),e+r[0].length):-1}function Ny(t,n,e){var r=ay.exec(n.slice(e,e+1));return r?e+r[0].length:-1}function Cy(t,n,e){var r=oy.exec(n.slice(e));return r?(t.Q=+r[0],e+r[0].length):-1}function Py(t,n,e){var r=oy.exec(n.slice(e));return r?(t.s=+r[0],e+r[0].length):-1}function zy(t,n){return cy(t.getDate(),n,2)}function Dy(t,n){return cy(t.getHours(),n,2)}function qy(t,n){return cy(t.getHours()%12||12,n,2)}function Ry(t,n){return cy(1+eg.count(xg(t),t),n,3)}function Fy(t,n){return cy(t.getMilliseconds(),n,3)}function Oy(t,n){return Fy(t,n)+"000"}function Iy(t,n){return cy(t.getMonth()+1,n,2)}function Uy(t,n){return cy(t.getMinutes(),n,2)}function By(t,n){return cy(t.getSeconds(),n,2)}function Yy(t){var n=t.getDay();return 0===n?7:n}function Ly(t,n){return cy(og.count(xg(t)-1,t),n,2)}function jy(t){var n=t.getDay();return n>=4||0===n?fg(t):fg.ceil(t)}function Hy(t,n){return t=jy(t),cy(fg.count(xg(t),t)+(4===xg(t).getDay()),n,2)}function Xy(t){return t.getDay()}function Gy(t,n){return cy(ag.count(xg(t)-1,t),n,2)}function Vy(t,n){return cy(t.getFullYear()%100,n,2)}function $y(t,n){return cy((t=jy(t)).getFullYear()%100,n,2)}function Wy(t,n){return cy(t.getFullYear()%1e4,n,4)}function Zy(t,n){var e=t.getDay();return cy((t=e>=4||0===e?fg(t):fg.ceil(t)).getFullYear()%1e4,n,4)}function Ky(t){var n=t.getTimezoneOffset();return(n>0?"-":(n*=-1,"+"))+cy(n/60|0,"0",2)+cy(n%60,"0",2)}function Qy(t,n){return cy(t.getUTCDate(),n,2)}function Jy(t,n){return cy(t.getUTCHours(),n,2)}function tv(t,n){return cy(t.getUTCHours()%12||12,n,2)}function nv(t,n){return cy(1+Eg.count(Gg(t),t),n,3)}function ev(t,n){return cy(t.getUTCMilliseconds(),n,3)}function rv(t,n){return ev(t,n)+"000"}function iv(t,n){return cy(t.getUTCMonth()+1,n,2)}function ov(t,n){return cy(t.getUTCMinutes(),n,2)}function av(t,n){return cy(t.getUTCSeconds(),n,2)}function uv(t){var n=t.getUTCDay();return 0===n?7:n}function cv(t,n){return cy(Cg.count(Gg(t)-1,t),n,2)}function fv(t){var n=t.getUTCDay();return n>=4||0===n?qg(t):qg.ceil(t)}function sv(t,n){return t=fv(t),cy(qg.count(Gg(t),t)+(4===Gg(t).getUTCDay()),n,2)}function lv(t){return t.getUTCDay()}function hv(t,n){return cy(Pg.count(Gg(t)-1,t),n,2)}function dv(t,n){return cy(t.getUTCFullYear()%100,n,2)}function pv(t,n){return cy((t=fv(t)).getUTCFullYear()%100,n,2)}function gv(t,n){return cy(t.getUTCFullYear()%1e4,n,4)}function yv(t,n){var e=t.getUTCDay();return cy((t=e>=4||0===e?qg(t):qg.ceil(t)).getUTCFullYear()%1e4,n,4)}function vv(){return"+0000"}function _v(){return"%"}function bv(t){return+t}function mv(t){return Math.floor(+t/1e3)}function xv(n){return ry=ey(n),t.timeFormat=ry.format,t.timeParse=ry.parse,t.utcFormat=ry.utcFormat,t.utcParse=ry.utcParse,ry}t.timeFormat=void 0,t.timeParse=void 0,t.utcFormat=void 0,t.utcParse=void 0,xv({dateTime:"%x, %X",date:"%-m/%-d/%Y",time:"%-I:%M:%S %p",periods:["AM","PM"],days:["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"],shortDays:["Sun","Mon","Tue","Wed","Thu","Fri","Sat"],months:["January","February","March","April","May","June","July","August","September","October","November","December"],shortMonths:["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"]});var wv="%Y-%m-%dT%H:%M:%S.%LZ";var Mv=Date.prototype.toISOString?function(t){return t.toISOString()}:t.utcFormat(wv);var Av=+new Date("2000-01-01T00:00:00.000Z")?function(t){var n=new Date(t);return isNaN(n)?null:n}:t.utcParse(wv);function Tv(t){return new Date(t)}function Sv(t){return t instanceof Date?+t:+new Date(+t)}function Ev(t,n,e,r,i,o,a,u,c,f){var s=vp(),l=s.invert,h=s.domain,d=f(".%L"),p=f(":%S"),g=f("%I:%M"),y=f("%I %p"),v=f("%a %d"),_=f("%b %d"),b=f("%B"),m=f("%Y");function x(t){return(c(t)hr(t[t.length-1]),Xv=new Array(3).concat("d8b365f5f5f55ab4ac","a6611adfc27d80cdc1018571","a6611adfc27df5f5f580cdc1018571","8c510ad8b365f6e8c3c7eae55ab4ac01665e","8c510ad8b365f6e8c3f5f5f5c7eae55ab4ac01665e","8c510abf812ddfc27df6e8c3c7eae580cdc135978f01665e","8c510abf812ddfc27df6e8c3f5f5f5c7eae580cdc135978f01665e","5430058c510abf812ddfc27df6e8c3c7eae580cdc135978f01665e003c30","5430058c510abf812ddfc27df6e8c3f5f5f5c7eae580cdc135978f01665e003c30").map(Dv),Gv=Hv(Xv),Vv=new Array(3).concat("af8dc3f7f7f77fbf7b","7b3294c2a5cfa6dba0008837","7b3294c2a5cff7f7f7a6dba0008837","762a83af8dc3e7d4e8d9f0d37fbf7b1b7837","762a83af8dc3e7d4e8f7f7f7d9f0d37fbf7b1b7837","762a839970abc2a5cfe7d4e8d9f0d3a6dba05aae611b7837","762a839970abc2a5cfe7d4e8f7f7f7d9f0d3a6dba05aae611b7837","40004b762a839970abc2a5cfe7d4e8d9f0d3a6dba05aae611b783700441b","40004b762a839970abc2a5cfe7d4e8f7f7f7d9f0d3a6dba05aae611b783700441b").map(Dv),$v=Hv(Vv),Wv=new Array(3).concat("e9a3c9f7f7f7a1d76a","d01c8bf1b6dab8e1864dac26","d01c8bf1b6daf7f7f7b8e1864dac26","c51b7de9a3c9fde0efe6f5d0a1d76a4d9221","c51b7de9a3c9fde0eff7f7f7e6f5d0a1d76a4d9221","c51b7dde77aef1b6dafde0efe6f5d0b8e1867fbc414d9221","c51b7dde77aef1b6dafde0eff7f7f7e6f5d0b8e1867fbc414d9221","8e0152c51b7dde77aef1b6dafde0efe6f5d0b8e1867fbc414d9221276419","8e0152c51b7dde77aef1b6dafde0eff7f7f7e6f5d0b8e1867fbc414d9221276419").map(Dv),Zv=Hv(Wv),Kv=new Array(3).concat("998ec3f7f7f7f1a340","5e3c99b2abd2fdb863e66101","5e3c99b2abd2f7f7f7fdb863e66101","542788998ec3d8daebfee0b6f1a340b35806","542788998ec3d8daebf7f7f7fee0b6f1a340b35806","5427888073acb2abd2d8daebfee0b6fdb863e08214b35806","5427888073acb2abd2d8daebf7f7f7fee0b6fdb863e08214b35806","2d004b5427888073acb2abd2d8daebfee0b6fdb863e08214b358067f3b08","2d004b5427888073acb2abd2d8daebf7f7f7fee0b6fdb863e08214b358067f3b08").map(Dv),Qv=Hv(Kv),Jv=new Array(3).concat("ef8a62f7f7f767a9cf","ca0020f4a58292c5de0571b0","ca0020f4a582f7f7f792c5de0571b0","b2182bef8a62fddbc7d1e5f067a9cf2166ac","b2182bef8a62fddbc7f7f7f7d1e5f067a9cf2166ac","b2182bd6604df4a582fddbc7d1e5f092c5de4393c32166ac","b2182bd6604df4a582fddbc7f7f7f7d1e5f092c5de4393c32166ac","67001fb2182bd6604df4a582fddbc7d1e5f092c5de4393c32166ac053061","67001fb2182bd6604df4a582fddbc7f7f7f7d1e5f092c5de4393c32166ac053061").map(Dv),t_=Hv(Jv),n_=new Array(3).concat("ef8a62ffffff999999","ca0020f4a582bababa404040","ca0020f4a582ffffffbababa404040","b2182bef8a62fddbc7e0e0e09999994d4d4d","b2182bef8a62fddbc7ffffffe0e0e09999994d4d4d","b2182bd6604df4a582fddbc7e0e0e0bababa8787874d4d4d","b2182bd6604df4a582fddbc7ffffffe0e0e0bababa8787874d4d4d","67001fb2182bd6604df4a582fddbc7e0e0e0bababa8787874d4d4d1a1a1a","67001fb2182bd6604df4a582fddbc7ffffffe0e0e0bababa8787874d4d4d1a1a1a").map(Dv),e_=Hv(n_),r_=new Array(3).concat("fc8d59ffffbf91bfdb","d7191cfdae61abd9e92c7bb6","d7191cfdae61ffffbfabd9e92c7bb6","d73027fc8d59fee090e0f3f891bfdb4575b4","d73027fc8d59fee090ffffbfe0f3f891bfdb4575b4","d73027f46d43fdae61fee090e0f3f8abd9e974add14575b4","d73027f46d43fdae61fee090ffffbfe0f3f8abd9e974add14575b4","a50026d73027f46d43fdae61fee090e0f3f8abd9e974add14575b4313695","a50026d73027f46d43fdae61fee090ffffbfe0f3f8abd9e974add14575b4313695").map(Dv),i_=Hv(r_),o_=new Array(3).concat("fc8d59ffffbf91cf60","d7191cfdae61a6d96a1a9641","d7191cfdae61ffffbfa6d96a1a9641","d73027fc8d59fee08bd9ef8b91cf601a9850","d73027fc8d59fee08bffffbfd9ef8b91cf601a9850","d73027f46d43fdae61fee08bd9ef8ba6d96a66bd631a9850","d73027f46d43fdae61fee08bffffbfd9ef8ba6d96a66bd631a9850","a50026d73027f46d43fdae61fee08bd9ef8ba6d96a66bd631a9850006837","a50026d73027f46d43fdae61fee08bffffbfd9ef8ba6d96a66bd631a9850006837").map(Dv),a_=Hv(o_),u_=new Array(3).concat("fc8d59ffffbf99d594","d7191cfdae61abdda42b83ba","d7191cfdae61ffffbfabdda42b83ba","d53e4ffc8d59fee08be6f59899d5943288bd","d53e4ffc8d59fee08bffffbfe6f59899d5943288bd","d53e4ff46d43fdae61fee08be6f598abdda466c2a53288bd","d53e4ff46d43fdae61fee08bffffbfe6f598abdda466c2a53288bd","9e0142d53e4ff46d43fdae61fee08be6f598abdda466c2a53288bd5e4fa2","9e0142d53e4ff46d43fdae61fee08bffffbfe6f598abdda466c2a53288bd5e4fa2").map(Dv),c_=Hv(u_),f_=new Array(3).concat("e5f5f999d8c92ca25f","edf8fbb2e2e266c2a4238b45","edf8fbb2e2e266c2a42ca25f006d2c","edf8fbccece699d8c966c2a42ca25f006d2c","edf8fbccece699d8c966c2a441ae76238b45005824","f7fcfde5f5f9ccece699d8c966c2a441ae76238b45005824","f7fcfde5f5f9ccece699d8c966c2a441ae76238b45006d2c00441b").map(Dv),s_=Hv(f_),l_=new Array(3).concat("e0ecf49ebcda8856a7","edf8fbb3cde38c96c688419d","edf8fbb3cde38c96c68856a7810f7c","edf8fbbfd3e69ebcda8c96c68856a7810f7c","edf8fbbfd3e69ebcda8c96c68c6bb188419d6e016b","f7fcfde0ecf4bfd3e69ebcda8c96c68c6bb188419d6e016b","f7fcfde0ecf4bfd3e69ebcda8c96c68c6bb188419d810f7c4d004b").map(Dv),h_=Hv(l_),d_=new Array(3).concat("e0f3dba8ddb543a2ca","f0f9e8bae4bc7bccc42b8cbe","f0f9e8bae4bc7bccc443a2ca0868ac","f0f9e8ccebc5a8ddb57bccc443a2ca0868ac","f0f9e8ccebc5a8ddb57bccc44eb3d32b8cbe08589e","f7fcf0e0f3dbccebc5a8ddb57bccc44eb3d32b8cbe08589e","f7fcf0e0f3dbccebc5a8ddb57bccc44eb3d32b8cbe0868ac084081").map(Dv),p_=Hv(d_),g_=new Array(3).concat("fee8c8fdbb84e34a33","fef0d9fdcc8afc8d59d7301f","fef0d9fdcc8afc8d59e34a33b30000","fef0d9fdd49efdbb84fc8d59e34a33b30000","fef0d9fdd49efdbb84fc8d59ef6548d7301f990000","fff7ecfee8c8fdd49efdbb84fc8d59ef6548d7301f990000","fff7ecfee8c8fdd49efdbb84fc8d59ef6548d7301fb300007f0000").map(Dv),y_=Hv(g_),v_=new Array(3).concat("ece2f0a6bddb1c9099","f6eff7bdc9e167a9cf02818a","f6eff7bdc9e167a9cf1c9099016c59","f6eff7d0d1e6a6bddb67a9cf1c9099016c59","f6eff7d0d1e6a6bddb67a9cf3690c002818a016450","fff7fbece2f0d0d1e6a6bddb67a9cf3690c002818a016450","fff7fbece2f0d0d1e6a6bddb67a9cf3690c002818a016c59014636").map(Dv),__=Hv(v_),b_=new Array(3).concat("ece7f2a6bddb2b8cbe","f1eef6bdc9e174a9cf0570b0","f1eef6bdc9e174a9cf2b8cbe045a8d","f1eef6d0d1e6a6bddb74a9cf2b8cbe045a8d","f1eef6d0d1e6a6bddb74a9cf3690c00570b0034e7b","fff7fbece7f2d0d1e6a6bddb74a9cf3690c00570b0034e7b","fff7fbece7f2d0d1e6a6bddb74a9cf3690c00570b0045a8d023858").map(Dv),m_=Hv(b_),x_=new Array(3).concat("e7e1efc994c7dd1c77","f1eef6d7b5d8df65b0ce1256","f1eef6d7b5d8df65b0dd1c77980043","f1eef6d4b9dac994c7df65b0dd1c77980043","f1eef6d4b9dac994c7df65b0e7298ace125691003f","f7f4f9e7e1efd4b9dac994c7df65b0e7298ace125691003f","f7f4f9e7e1efd4b9dac994c7df65b0e7298ace125698004367001f").map(Dv),w_=Hv(x_),M_=new Array(3).concat("fde0ddfa9fb5c51b8a","feebe2fbb4b9f768a1ae017e","feebe2fbb4b9f768a1c51b8a7a0177","feebe2fcc5c0fa9fb5f768a1c51b8a7a0177","feebe2fcc5c0fa9fb5f768a1dd3497ae017e7a0177","fff7f3fde0ddfcc5c0fa9fb5f768a1dd3497ae017e7a0177","fff7f3fde0ddfcc5c0fa9fb5f768a1dd3497ae017e7a017749006a").map(Dv),A_=Hv(M_),T_=new Array(3).concat("edf8b17fcdbb2c7fb8","ffffcca1dab441b6c4225ea8","ffffcca1dab441b6c42c7fb8253494","ffffccc7e9b47fcdbb41b6c42c7fb8253494","ffffccc7e9b47fcdbb41b6c41d91c0225ea80c2c84","ffffd9edf8b1c7e9b47fcdbb41b6c41d91c0225ea80c2c84","ffffd9edf8b1c7e9b47fcdbb41b6c41d91c0225ea8253494081d58").map(Dv),S_=Hv(T_),E_=new Array(3).concat("f7fcb9addd8e31a354","ffffccc2e69978c679238443","ffffccc2e69978c67931a354006837","ffffccd9f0a3addd8e78c67931a354006837","ffffccd9f0a3addd8e78c67941ab5d238443005a32","ffffe5f7fcb9d9f0a3addd8e78c67941ab5d238443005a32","ffffe5f7fcb9d9f0a3addd8e78c67941ab5d238443006837004529").map(Dv),k_=Hv(E_),N_=new Array(3).concat("fff7bcfec44fd95f0e","ffffd4fed98efe9929cc4c02","ffffd4fed98efe9929d95f0e993404","ffffd4fee391fec44ffe9929d95f0e993404","ffffd4fee391fec44ffe9929ec7014cc4c028c2d04","ffffe5fff7bcfee391fec44ffe9929ec7014cc4c028c2d04","ffffe5fff7bcfee391fec44ffe9929ec7014cc4c02993404662506").map(Dv),C_=Hv(N_),P_=new Array(3).concat("ffeda0feb24cf03b20","ffffb2fecc5cfd8d3ce31a1c","ffffb2fecc5cfd8d3cf03b20bd0026","ffffb2fed976feb24cfd8d3cf03b20bd0026","ffffb2fed976feb24cfd8d3cfc4e2ae31a1cb10026","ffffccffeda0fed976feb24cfd8d3cfc4e2ae31a1cb10026","ffffccffeda0fed976feb24cfd8d3cfc4e2ae31a1cbd0026800026").map(Dv),z_=Hv(P_),D_=new Array(3).concat("deebf79ecae13182bd","eff3ffbdd7e76baed62171b5","eff3ffbdd7e76baed63182bd08519c","eff3ffc6dbef9ecae16baed63182bd08519c","eff3ffc6dbef9ecae16baed64292c62171b5084594","f7fbffdeebf7c6dbef9ecae16baed64292c62171b5084594","f7fbffdeebf7c6dbef9ecae16baed64292c62171b508519c08306b").map(Dv),q_=Hv(D_),R_=new Array(3).concat("e5f5e0a1d99b31a354","edf8e9bae4b374c476238b45","edf8e9bae4b374c47631a354006d2c","edf8e9c7e9c0a1d99b74c47631a354006d2c","edf8e9c7e9c0a1d99b74c47641ab5d238b45005a32","f7fcf5e5f5e0c7e9c0a1d99b74c47641ab5d238b45005a32","f7fcf5e5f5e0c7e9c0a1d99b74c47641ab5d238b45006d2c00441b").map(Dv),F_=Hv(R_),O_=new Array(3).concat("f0f0f0bdbdbd636363","f7f7f7cccccc969696525252","f7f7f7cccccc969696636363252525","f7f7f7d9d9d9bdbdbd969696636363252525","f7f7f7d9d9d9bdbdbd969696737373525252252525","fffffff0f0f0d9d9d9bdbdbd969696737373525252252525","fffffff0f0f0d9d9d9bdbdbd969696737373525252252525000000").map(Dv),I_=Hv(O_),U_=new Array(3).concat("efedf5bcbddc756bb1","f2f0f7cbc9e29e9ac86a51a3","f2f0f7cbc9e29e9ac8756bb154278f","f2f0f7dadaebbcbddc9e9ac8756bb154278f","f2f0f7dadaebbcbddc9e9ac8807dba6a51a34a1486","fcfbfdefedf5dadaebbcbddc9e9ac8807dba6a51a34a1486","fcfbfdefedf5dadaebbcbddc9e9ac8807dba6a51a354278f3f007d").map(Dv),B_=Hv(U_),Y_=new Array(3).concat("fee0d2fc9272de2d26","fee5d9fcae91fb6a4acb181d","fee5d9fcae91fb6a4ade2d26a50f15","fee5d9fcbba1fc9272fb6a4ade2d26a50f15","fee5d9fcbba1fc9272fb6a4aef3b2ccb181d99000d","fff5f0fee0d2fcbba1fc9272fb6a4aef3b2ccb181d99000d","fff5f0fee0d2fcbba1fc9272fb6a4aef3b2ccb181da50f1567000d").map(Dv),L_=Hv(Y_),j_=new Array(3).concat("fee6cefdae6be6550d","feeddefdbe85fd8d3cd94701","feeddefdbe85fd8d3ce6550da63603","feeddefdd0a2fdae6bfd8d3ce6550da63603","feeddefdd0a2fdae6bfd8d3cf16913d948018c2d04","fff5ebfee6cefdd0a2fdae6bfd8d3cf16913d948018c2d04","fff5ebfee6cefdd0a2fdae6bfd8d3cf16913d94801a636037f2704").map(Dv),H_=Hv(j_);var X_=Lr(tr(300,.5,0),tr(-240,.5,1)),G_=Lr(tr(-100,.75,.35),tr(80,1.5,.8)),V_=Lr(tr(260,.75,.35),tr(80,1.5,.8)),$_=tr();var W_=ve(),Z_=Math.PI/3,K_=2*Math.PI/3;function Q_(t){var n=t.length;return function(e){return t[Math.max(0,Math.min(n-1,Math.floor(e*n)))]}}var J_=Q_(Dv("44015444025645045745055946075a46085c460a5d460b5e470d60470e6147106347116447136548146748166848176948186a481a6c481b6d481c6e481d6f481f70482071482173482374482475482576482677482878482979472a7a472c7a472d7b472e7c472f7d46307e46327e46337f463480453581453781453882443983443a83443b84433d84433e85423f854240864241864142874144874045884046883f47883f48893e49893e4a893e4c8a3d4d8a3d4e8a3c4f8a3c508b3b518b3b528b3a538b3a548c39558c39568c38588c38598c375a8c375b8d365c8d365d8d355e8d355f8d34608d34618d33628d33638d32648e32658e31668e31678e31688e30698e306a8e2f6b8e2f6c8e2e6d8e2e6e8e2e6f8e2d708e2d718e2c718e2c728e2c738e2b748e2b758e2a768e2a778e2a788e29798e297a8e297b8e287c8e287d8e277e8e277f8e27808e26818e26828e26828e25838e25848e25858e24868e24878e23888e23898e238a8d228b8d228c8d228d8d218e8d218f8d21908d21918c20928c20928c20938c1f948c1f958b1f968b1f978b1f988b1f998a1f9a8a1e9b8a1e9c891e9d891f9e891f9f881fa0881fa1881fa1871fa28720a38620a48621a58521a68522a78522a88423a98324aa8325ab8225ac8226ad8127ad8128ae8029af7f2ab07f2cb17e2db27d2eb37c2fb47c31b57b32b67a34b67935b77937b87838b9773aba763bbb753dbc743fbc7340bd7242be7144bf7046c06f48c16e4ac16d4cc26c4ec36b50c46a52c56954c56856c66758c7655ac8645cc8635ec96260ca6063cb5f65cb5e67cc5c69cd5b6ccd5a6ece5870cf5773d05675d05477d1537ad1517cd2507fd34e81d34d84d44b86d54989d5488bd6468ed64590d74393d74195d84098d83e9bd93c9dd93ba0da39a2da37a5db36a8db34aadc32addc30b0dd2fb2dd2db5de2bb8de29bade28bddf26c0df25c2df23c5e021c8e020cae11fcde11dd0e11cd2e21bd5e21ad8e219dae319dde318dfe318e2e418e5e419e7e419eae51aece51befe51cf1e51df4e61ef6e620f8e621fbe723fde725")),tb=Q_(Dv("00000401000501010601010802010902020b02020d03030f03031204041405041606051806051a07061c08071e0907200a08220b09240c09260d0a290e0b2b100b2d110c2f120d31130d34140e36150e38160f3b180f3d19103f1a10421c10441d11471e114920114b21114e22115024125325125527125829115a2a115c2c115f2d11612f116331116533106734106936106b38106c390f6e3b0f703d0f713f0f72400f74420f75440f764510774710784910784a10794c117a4e117b4f127b51127c52137c54137d56147d57157e59157e5a167e5c167f5d177f5f187f601880621980641a80651a80671b80681c816a1c816b1d816d1d816e1e81701f81721f817320817521817621817822817922827b23827c23827e24828025828125818326818426818627818827818928818b29818c29818e2a81902a81912b81932b80942c80962c80982d80992d809b2e7f9c2e7f9e2f7fa02f7fa1307ea3307ea5317ea6317da8327daa337dab337cad347cae347bb0357bb2357bb3367ab5367ab73779b83779ba3878bc3978bd3977bf3a77c03a76c23b75c43c75c53c74c73d73c83e73ca3e72cc3f71cd4071cf4070d0416fd2426fd3436ed5446dd6456cd8456cd9466bdb476adc4869de4968df4a68e04c67e24d66e34e65e44f64e55064e75263e85362e95462ea5661eb5760ec5860ed5a5fee5b5eef5d5ef05f5ef1605df2625df2645cf3655cf4675cf4695cf56b5cf66c5cf66e5cf7705cf7725cf8745cf8765cf9785df9795df97b5dfa7d5efa7f5efa815ffb835ffb8560fb8761fc8961fc8a62fc8c63fc8e64fc9065fd9266fd9467fd9668fd9869fd9a6afd9b6bfe9d6cfe9f6dfea16efea36ffea571fea772fea973feaa74feac76feae77feb078feb27afeb47bfeb67cfeb77efeb97ffebb81febd82febf84fec185fec287fec488fec68afec88cfeca8dfecc8ffecd90fecf92fed194fed395fed597fed799fed89afdda9cfddc9efddea0fde0a1fde2a3fde3a5fde5a7fde7a9fde9aafdebacfcecaefceeb0fcf0b2fcf2b4fcf4b6fcf6b8fcf7b9fcf9bbfcfbbdfcfdbf")),nb=Q_(Dv("00000401000501010601010802010a02020c02020e03021004031204031405041706041907051b08051d09061f0a07220b07240c08260d08290e092b10092d110a30120a32140b34150b37160b39180c3c190c3e1b0c411c0c431e0c451f0c48210c4a230c4c240c4f260c51280b53290b552b0b572d0b592f0a5b310a5c320a5e340a5f3609613809623909633b09643d09653e0966400a67420a68440a68450a69470b6a490b6a4a0c6b4c0c6b4d0d6c4f0d6c510e6c520e6d540f6d550f6d57106e59106e5a116e5c126e5d126e5f136e61136e62146e64156e65156e67166e69166e6a176e6c186e6d186e6f196e71196e721a6e741a6e751b6e771c6d781c6d7a1d6d7c1d6d7d1e6d7f1e6c801f6c82206c84206b85216b87216b88226a8a226a8c23698d23698f24699025689225689326679526679727669827669a28659b29649d29649f2a63a02a63a22b62a32c61a52c60a62d60a82e5fa92e5eab2f5ead305dae305cb0315bb1325ab3325ab43359b63458b73557b93556ba3655bc3754bd3853bf3952c03a51c13a50c33b4fc43c4ec63d4dc73e4cc83f4bca404acb4149cc4248ce4347cf4446d04545d24644d34743d44842d54a41d74b3fd84c3ed94d3dda4e3cdb503bdd513ade5238df5337e05536e15635e25734e35933e45a31e55c30e65d2fe75e2ee8602de9612bea632aeb6429eb6628ec6726ed6925ee6a24ef6c23ef6e21f06f20f1711ff1731df2741cf3761bf37819f47918f57b17f57d15f67e14f68013f78212f78410f8850ff8870ef8890cf98b0bf98c0af98e09fa9008fa9207fa9407fb9606fb9706fb9906fb9b06fb9d07fc9f07fca108fca309fca50afca60cfca80dfcaa0ffcac11fcae12fcb014fcb216fcb418fbb61afbb81dfbba1ffbbc21fbbe23fac026fac228fac42afac62df9c72ff9c932f9cb35f8cd37f8cf3af7d13df7d340f6d543f6d746f5d949f5db4cf4dd4ff4df53f4e156f3e35af3e55df2e661f2e865f2ea69f1ec6df1ed71f1ef75f1f179f2f27df2f482f3f586f3f68af4f88ef5f992f6fa96f8fb9af9fc9dfafda1fcffa4")),eb=Q_(Dv("0d088710078813078916078a19068c1b068d1d068e20068f2206902406912605912805922a05932c05942e05952f059631059733059735049837049938049a3a049a3c049b3e049c3f049c41049d43039e44039e46039f48039f4903a04b03a14c02a14e02a25002a25102a35302a35502a45601a45801a45901a55b01a55c01a65e01a66001a66100a76300a76400a76600a76700a86900a86a00a86c00a86e00a86f00a87100a87201a87401a87501a87701a87801a87a02a87b02a87d03a87e03a88004a88104a78305a78405a78606a68707a68808a68a09a58b0aa58d0ba58e0ca48f0da4910ea3920fa39410a29511a19613a19814a099159f9a169f9c179e9d189d9e199da01a9ca11b9ba21d9aa31e9aa51f99a62098a72197a82296aa2395ab2494ac2694ad2793ae2892b02991b12a90b22b8fb32c8eb42e8db52f8cb6308bb7318ab83289ba3388bb3488bc3587bd3786be3885bf3984c03a83c13b82c23c81c33d80c43e7fc5407ec6417dc7427cc8437bc9447aca457acb4679cc4778cc4977cd4a76ce4b75cf4c74d04d73d14e72d24f71d35171d45270d5536fd5546ed6556dd7566cd8576bd9586ada5a6ada5b69db5c68dc5d67dd5e66de5f65de6164df6263e06363e16462e26561e26660e3685fe4695ee56a5de56b5de66c5ce76e5be76f5ae87059e97158e97257ea7457eb7556eb7655ec7754ed7953ed7a52ee7b51ef7c51ef7e50f07f4ff0804ef1814df1834cf2844bf3854bf3874af48849f48948f58b47f58c46f68d45f68f44f79044f79143f79342f89441f89540f9973ff9983ef99a3efa9b3dfa9c3cfa9e3bfb9f3afba139fba238fca338fca537fca636fca835fca934fdab33fdac33fdae32fdaf31fdb130fdb22ffdb42ffdb52efeb72dfeb82cfeba2cfebb2bfebd2afebe2afec029fdc229fdc328fdc527fdc627fdc827fdca26fdcb26fccd25fcce25fcd025fcd225fbd324fbd524fbd724fad824fada24f9dc24f9dd25f8df25f8e125f7e225f7e425f6e626f6e826f5e926f5eb27f4ed27f3ee27f3f027f2f227f1f426f1f525f0f724f0f921"));function rb(t){return function(){return t}}var ib=Math.abs,ob=Math.atan2,ab=Math.cos,ub=Math.max,cb=Math.min,fb=Math.sin,sb=Math.sqrt,lb=1e-12,hb=Math.PI,db=hb/2,pb=2*hb;function gb(t){return t>1?0:t<-1?hb:Math.acos(t)}function yb(t){return t>=1?db:t<=-1?-db:Math.asin(t)}function vb(t){return t.innerRadius}function _b(t){return t.outerRadius}function bb(t){return t.startAngle}function mb(t){return t.endAngle}function xb(t){return t&&t.padAngle}function wb(t,n,e,r,i,o,a,u){var c=e-t,f=r-n,s=a-i,l=u-o,h=l*c-s*f;if(!(h*hC*C+P*P&&(A=S,T=E),{cx:A,cy:T,x01:-s,y01:-l,x11:A*(i/x-1),y11:T*(i/x-1)}}var Ab=Array.prototype.slice;function Tb(t){return"object"==typeof t&&"length"in t?t:Array.from(t)}function Sb(t){this._context=t}function Eb(t){return new Sb(t)}function kb(t){return t[0]}function Nb(t){return t[1]}function Cb(t,n){var e=rb(!0),r=null,i=Eb,o=null;function a(a){var u,c,f,s=(a=Tb(a)).length,l=!1;for(null==r&&(o=i(f=fa())),u=0;u<=s;++u)!(u=s;--l)u.point(y[l],v[l]);u.lineEnd(),u.areaEnd()}g&&(y[f]=+t(h,f,c),v[f]=+n(h,f,c),u.point(r?+r(h,f,c):y[f],e?+e(h,f,c):v[f]))}if(d)return u=null,d+""||null}function f(){return Cb().defined(i).curve(a).context(o)}return t="function"==typeof t?t:void 0===t?kb:rb(+t),n="function"==typeof n?n:rb(void 0===n?0:+n),e="function"==typeof e?e:void 0===e?Nb:rb(+e),c.x=function(n){return arguments.length?(t="function"==typeof n?n:rb(+n),r=null,c):t},c.x0=function(n){return arguments.length?(t="function"==typeof n?n:rb(+n),c):t},c.x1=function(t){return arguments.length?(r=null==t?null:"function"==typeof t?t:rb(+t),c):r},c.y=function(t){return arguments.length?(n="function"==typeof t?t:rb(+t),e=null,c):n},c.y0=function(t){return arguments.length?(n="function"==typeof t?t:rb(+t),c):n},c.y1=function(t){return arguments.length?(e=null==t?null:"function"==typeof t?t:rb(+t),c):e},c.lineX0=c.lineY0=function(){return f().x(t).y(n)},c.lineY1=function(){return f().x(t).y(e)},c.lineX1=function(){return f().x(r).y(n)},c.defined=function(t){return arguments.length?(i="function"==typeof t?t:rb(!!t),c):i},c.curve=function(t){return arguments.length?(a=t,null!=o&&(u=a(o)),c):a},c.context=function(t){return arguments.length?(null==t?o=u=null:u=a(o=t),c):o},c}function zb(t,n){return nt?1:n>=t?0:NaN}function Db(t){return t}Sb.prototype={areaStart:function(){this._line=0},areaEnd:function(){this._line=NaN},lineStart:function(){this._point=0},lineEnd:function(){(this._line||0!==this._line&&1===this._point)&&this._context.closePath(),this._line=1-this._line},point:function(t,n){switch(t=+t,n=+n,this._point){case 0:this._point=1,this._line?this._context.lineTo(t,n):this._context.moveTo(t,n);break;case 1:this._point=2;default:this._context.lineTo(t,n)}}};var qb=Fb(Eb);function Rb(t){this._curve=t}function Fb(t){function n(n){return new Rb(t(n))}return n._curve=t,n}function Ob(t){var n=t.curve;return t.angle=t.x,delete t.x,t.radius=t.y,delete t.y,t.curve=function(t){return arguments.length?n(Fb(t)):n()._curve},t}function Ib(){return Ob(Cb().curve(qb))}function Ub(){var t=Pb().curve(qb),n=t.curve,e=t.lineX0,r=t.lineX1,i=t.lineY0,o=t.lineY1;return t.angle=t.x,delete t.x,t.startAngle=t.x0,delete t.x0,t.endAngle=t.x1,delete t.x1,t.radius=t.y,delete t.y,t.innerRadius=t.y0,delete t.y0,t.outerRadius=t.y1,delete t.y1,t.lineStartAngle=function(){return Ob(e())},delete t.lineX0,t.lineEndAngle=function(){return Ob(r())},delete t.lineX1,t.lineInnerRadius=function(){return Ob(i())},delete t.lineY0,t.lineOuterRadius=function(){return Ob(o())},delete t.lineY1,t.curve=function(t){return arguments.length?n(Fb(t)):n()._curve},t}function Bb(t,n){return[(n=+n)*Math.cos(t-=Math.PI/2),n*Math.sin(t)]}function Yb(t){return t.source}function Lb(t){return t.target}function jb(t){var n=Yb,e=Lb,r=kb,i=Nb,o=null;function a(){var a,u=Ab.call(arguments),c=n.apply(this,u),f=e.apply(this,u);if(o||(o=a=fa()),t(o,+r.apply(this,(u[0]=c,u)),+i.apply(this,u),+r.apply(this,(u[0]=f,u)),+i.apply(this,u)),a)return o=null,a+""||null}return a.source=function(t){return arguments.length?(n=t,a):n},a.target=function(t){return arguments.length?(e=t,a):e},a.x=function(t){return arguments.length?(r="function"==typeof t?t:rb(+t),a):r},a.y=function(t){return arguments.length?(i="function"==typeof t?t:rb(+t),a):i},a.context=function(t){return arguments.length?(o=null==t?null:t,a):o},a}function Hb(t,n,e,r,i){t.moveTo(n,e),t.bezierCurveTo(n=(n+r)/2,e,n,i,r,i)}function Xb(t,n,e,r,i){t.moveTo(n,e),t.bezierCurveTo(n,e=(e+i)/2,r,e,r,i)}function Gb(t,n,e,r,i){var o=Bb(n,e),a=Bb(n,e=(e+i)/2),u=Bb(r,e),c=Bb(r,i);t.moveTo(o[0],o[1]),t.bezierCurveTo(a[0],a[1],u[0],u[1],c[0],c[1])}Rb.prototype={areaStart:function(){this._curve.areaStart()},areaEnd:function(){this._curve.areaEnd()},lineStart:function(){this._curve.lineStart()},lineEnd:function(){this._curve.lineEnd()},point:function(t,n){this._curve.point(n*Math.sin(t),n*-Math.cos(t))}};var Vb={draw:function(t,n){var e=Math.sqrt(n/hb);t.moveTo(e,0),t.arc(0,0,e,0,pb)}},$b={draw:function(t,n){var e=Math.sqrt(n/5)/2;t.moveTo(-3*e,-e),t.lineTo(-e,-e),t.lineTo(-e,-3*e),t.lineTo(e,-3*e),t.lineTo(e,-e),t.lineTo(3*e,-e),t.lineTo(3*e,e),t.lineTo(e,e),t.lineTo(e,3*e),t.lineTo(-e,3*e),t.lineTo(-e,e),t.lineTo(-3*e,e),t.closePath()}},Wb=Math.sqrt(1/3),Zb=2*Wb,Kb={draw:function(t,n){var e=Math.sqrt(n/Zb),r=e*Wb;t.moveTo(0,-e),t.lineTo(r,0),t.lineTo(0,e),t.lineTo(-r,0),t.closePath()}},Qb=Math.sin(hb/10)/Math.sin(7*hb/10),Jb=Math.sin(pb/10)*Qb,tm=-Math.cos(pb/10)*Qb,nm={draw:function(t,n){var e=Math.sqrt(.8908130915292852*n),r=Jb*e,i=tm*e;t.moveTo(0,-e),t.lineTo(r,i);for(var o=1;o<5;++o){var a=pb*o/5,u=Math.cos(a),c=Math.sin(a);t.lineTo(c*e,-u*e),t.lineTo(u*r-c*i,c*r+u*i)}t.closePath()}},em={draw:function(t,n){var e=Math.sqrt(n),r=-e/2;t.rect(r,r,e,e)}},rm=Math.sqrt(3),im={draw:function(t,n){var e=-Math.sqrt(n/(3*rm));t.moveTo(0,2*e),t.lineTo(-rm*e,-e),t.lineTo(rm*e,-e),t.closePath()}},om=-.5,am=Math.sqrt(3)/2,um=1/Math.sqrt(12),cm=3*(um/2+1),fm={draw:function(t,n){var e=Math.sqrt(n/cm),r=e/2,i=e*um,o=r,a=e*um+e,u=-o,c=a;t.moveTo(r,i),t.lineTo(o,a),t.lineTo(u,c),t.lineTo(om*r-am*i,am*r+om*i),t.lineTo(om*o-am*a,am*o+om*a),t.lineTo(om*u-am*c,am*u+om*c),t.lineTo(om*r+am*i,om*i-am*r),t.lineTo(om*o+am*a,om*a-am*o),t.lineTo(om*u+am*c,om*c-am*u),t.closePath()}},sm=[Vb,$b,Kb,em,nm,im,fm];function lm(){}function hm(t,n,e){t._context.bezierCurveTo((2*t._x0+t._x1)/3,(2*t._y0+t._y1)/3,(t._x0+2*t._x1)/3,(t._y0+2*t._y1)/3,(t._x0+4*t._x1+n)/6,(t._y0+4*t._y1+e)/6)}function dm(t){this._context=t}function pm(t){this._context=t}function gm(t){this._context=t}dm.prototype={areaStart:function(){this._line=0},areaEnd:function(){this._line=NaN},lineStart:function(){this._x0=this._x1=this._y0=this._y1=NaN,this._point=0},lineEnd:function(){switch(this._point){case 3:hm(this,this._x1,this._y1);case 2:this._context.lineTo(this._x1,this._y1)}(this._line||0!==this._line&&1===this._point)&&this._context.closePath(),this._line=1-this._line},point:function(t,n){switch(t=+t,n=+n,this._point){case 0:this._point=1,this._line?this._context.lineTo(t,n):this._context.moveTo(t,n);break;case 1:this._point=2;break;case 2:this._point=3,this._context.lineTo((5*this._x0+this._x1)/6,(5*this._y0+this._y1)/6);default:hm(this,t,n)}this._x0=this._x1,this._x1=t,this._y0=this._y1,this._y1=n}},pm.prototype={areaStart:lm,areaEnd:lm,lineStart:function(){this._x0=this._x1=this._x2=this._x3=this._x4=this._y0=this._y1=this._y2=this._y3=this._y4=NaN,this._point=0},lineEnd:function(){switch(this._point){case 1:this._context.moveTo(this._x2,this._y2),this._context.closePath();break;case 2:this._context.moveTo((this._x2+2*this._x3)/3,(this._y2+2*this._y3)/3),this._context.lineTo((this._x3+2*this._x2)/3,(this._y3+2*this._y2)/3),this._context.closePath();break;case 3:this.point(this._x2,this._y2),this.point(this._x3,this._y3),this.point(this._x4,this._y4)}},point:function(t,n){switch(t=+t,n=+n,this._point){case 0:this._point=1,this._x2=t,this._y2=n;break;case 1:this._point=2,this._x3=t,this._y3=n;break;case 2:this._point=3,this._x4=t,this._y4=n,this._context.moveTo((this._x0+4*this._x1+t)/6,(this._y0+4*this._y1+n)/6);break;default:hm(this,t,n)}this._x0=this._x1,this._x1=t,this._y0=this._y1,this._y1=n}},gm.prototype={areaStart:function(){this._line=0},areaEnd:function(){this._line=NaN},lineStart:function(){this._x0=this._x1=this._y0=this._y1=NaN,this._point=0},lineEnd:function(){(this._line||0!==this._line&&3===this._point)&&this._context.closePath(),this._line=1-this._line},point:function(t,n){switch(t=+t,n=+n,this._point){case 0:this._point=1;break;case 1:this._point=2;break;case 2:this._point=3;var e=(this._x0+4*this._x1+t)/6,r=(this._y0+4*this._y1+n)/6;this._line?this._context.lineTo(e,r):this._context.moveTo(e,r);break;case 3:this._point=4;default:hm(this,t,n)}this._x0=this._x1,this._x1=t,this._y0=this._y1,this._y1=n}};class ym{constructor(t,n){this._context=t,this._x=n}areaStart(){this._line=0}areaEnd(){this._line=NaN}lineStart(){this._point=0}lineEnd(){(this._line||0!==this._line&&1===this._point)&&this._context.closePath(),this._line=1-this._line}point(t,n){switch(t=+t,n=+n,this._point){case 0:this._point=1,this._line?this._context.lineTo(t,n):this._context.moveTo(t,n);break;case 1:this._point=2;default:this._x?this._context.bezierCurveTo(this._x0=(this._x0+t)/2,this._y0,this._x0,n,t,n):this._context.bezierCurveTo(this._x0,this._y0=(this._y0+n)/2,t,this._y0,t,n)}this._x0=t,this._y0=n}}function vm(t,n){this._basis=new dm(t),this._beta=n}vm.prototype={lineStart:function(){this._x=[],this._y=[],this._basis.lineStart()},lineEnd:function(){var t=this._x,n=this._y,e=t.length-1;if(e>0)for(var r,i=t[0],o=n[0],a=t[e]-i,u=n[e]-o,c=-1;++c<=e;)r=c/e,this._basis.point(this._beta*t[c]+(1-this._beta)*(i+r*a),this._beta*n[c]+(1-this._beta)*(o+r*u));this._x=this._y=null,this._basis.lineEnd()},point:function(t,n){this._x.push(+t),this._y.push(+n)}};var _m=function t(n){function e(t){return 1===n?new dm(t):new vm(t,n)}return e.beta=function(n){return t(+n)},e}(.85);function bm(t,n,e){t._context.bezierCurveTo(t._x1+t._k*(t._x2-t._x0),t._y1+t._k*(t._y2-t._y0),t._x2+t._k*(t._x1-n),t._y2+t._k*(t._y1-e),t._x2,t._y2)}function mm(t,n){this._context=t,this._k=(1-n)/6}mm.prototype={areaStart:function(){this._line=0},areaEnd:function(){this._line=NaN},lineStart:function(){this._x0=this._x1=this._x2=this._y0=this._y1=this._y2=NaN,this._point=0},lineEnd:function(){switch(this._point){case 2:this._context.lineTo(this._x2,this._y2);break;case 3:bm(this,this._x1,this._y1)}(this._line||0!==this._line&&1===this._point)&&this._context.closePath(),this._line=1-this._line},point:function(t,n){switch(t=+t,n=+n,this._point){case 0:this._point=1,this._line?this._context.lineTo(t,n):this._context.moveTo(t,n);break;case 1:this._point=2,this._x1=t,this._y1=n;break;case 2:this._point=3;default:bm(this,t,n)}this._x0=this._x1,this._x1=this._x2,this._x2=t,this._y0=this._y1,this._y1=this._y2,this._y2=n}};var xm=function t(n){function e(t){return new mm(t,n)}return e.tension=function(n){return t(+n)},e}(0);function wm(t,n){this._context=t,this._k=(1-n)/6}wm.prototype={areaStart:lm,areaEnd:lm,lineStart:function(){this._x0=this._x1=this._x2=this._x3=this._x4=this._x5=this._y0=this._y1=this._y2=this._y3=this._y4=this._y5=NaN,this._point=0},lineEnd:function(){switch(this._point){case 1:this._context.moveTo(this._x3,this._y3),this._context.closePath();break;case 2:this._context.lineTo(this._x3,this._y3),this._context.closePath();break;case 3:this.point(this._x3,this._y3),this.point(this._x4,this._y4),this.point(this._x5,this._y5)}},point:function(t,n){switch(t=+t,n=+n,this._point){case 0:this._point=1,this._x3=t,this._y3=n;break;case 1:this._point=2,this._context.moveTo(this._x4=t,this._y4=n);break;case 2:this._point=3,this._x5=t,this._y5=n;break;default:bm(this,t,n)}this._x0=this._x1,this._x1=this._x2,this._x2=t,this._y0=this._y1,this._y1=this._y2,this._y2=n}};var Mm=function t(n){function e(t){return new wm(t,n)}return e.tension=function(n){return t(+n)},e}(0);function Am(t,n){this._context=t,this._k=(1-n)/6}Am.prototype={areaStart:function(){this._line=0},areaEnd:function(){this._line=NaN},lineStart:function(){this._x0=this._x1=this._x2=this._y0=this._y1=this._y2=NaN,this._point=0},lineEnd:function(){(this._line||0!==this._line&&3===this._point)&&this._context.closePath(),this._line=1-this._line},point:function(t,n){switch(t=+t,n=+n,this._point){case 0:this._point=1;break;case 1:this._point=2;break;case 2:this._point=3,this._line?this._context.lineTo(this._x2,this._y2):this._context.moveTo(this._x2,this._y2);break;case 3:this._point=4;default:bm(this,t,n)}this._x0=this._x1,this._x1=this._x2,this._x2=t,this._y0=this._y1,this._y1=this._y2,this._y2=n}};var Tm=function t(n){function e(t){return new Am(t,n)}return e.tension=function(n){return t(+n)},e}(0);function Sm(t,n,e){var r=t._x1,i=t._y1,o=t._x2,a=t._y2;if(t._l01_a>lb){var u=2*t._l01_2a+3*t._l01_a*t._l12_a+t._l12_2a,c=3*t._l01_a*(t._l01_a+t._l12_a);r=(r*u-t._x0*t._l12_2a+t._x2*t._l01_2a)/c,i=(i*u-t._y0*t._l12_2a+t._y2*t._l01_2a)/c}if(t._l23_a>lb){var f=2*t._l23_2a+3*t._l23_a*t._l12_a+t._l12_2a,s=3*t._l23_a*(t._l23_a+t._l12_a);o=(o*f+t._x1*t._l23_2a-n*t._l12_2a)/s,a=(a*f+t._y1*t._l23_2a-e*t._l12_2a)/s}t._context.bezierCurveTo(r,i,o,a,t._x2,t._y2)}function Em(t,n){this._context=t,this._alpha=n}Em.prototype={areaStart:function(){this._line=0},areaEnd:function(){this._line=NaN},lineStart:function(){this._x0=this._x1=this._x2=this._y0=this._y1=this._y2=NaN,this._l01_a=this._l12_a=this._l23_a=this._l01_2a=this._l12_2a=this._l23_2a=this._point=0},lineEnd:function(){switch(this._point){case 2:this._context.lineTo(this._x2,this._y2);break;case 3:this.point(this._x2,this._y2)}(this._line||0!==this._line&&1===this._point)&&this._context.closePath(),this._line=1-this._line},point:function(t,n){if(t=+t,n=+n,this._point){var e=this._x2-t,r=this._y2-n;this._l23_a=Math.sqrt(this._l23_2a=Math.pow(e*e+r*r,this._alpha))}switch(this._point){case 0:this._point=1,this._line?this._context.lineTo(t,n):this._context.moveTo(t,n);break;case 1:this._point=2;break;case 2:this._point=3;default:Sm(this,t,n)}this._l01_a=this._l12_a,this._l12_a=this._l23_a,this._l01_2a=this._l12_2a,this._l12_2a=this._l23_2a,this._x0=this._x1,this._x1=this._x2,this._x2=t,this._y0=this._y1,this._y1=this._y2,this._y2=n}};var km=function t(n){function e(t){return n?new Em(t,n):new mm(t,0)}return e.alpha=function(n){return t(+n)},e}(.5);function Nm(t,n){this._context=t,this._alpha=n}Nm.prototype={areaStart:lm,areaEnd:lm,lineStart:function(){this._x0=this._x1=this._x2=this._x3=this._x4=this._x5=this._y0=this._y1=this._y2=this._y3=this._y4=this._y5=NaN,this._l01_a=this._l12_a=this._l23_a=this._l01_2a=this._l12_2a=this._l23_2a=this._point=0},lineEnd:function(){switch(this._point){case 1:this._context.moveTo(this._x3,this._y3),this._context.closePath();break;case 2:this._context.lineTo(this._x3,this._y3),this._context.closePath();break;case 3:this.point(this._x3,this._y3),this.point(this._x4,this._y4),this.point(this._x5,this._y5)}},point:function(t,n){if(t=+t,n=+n,this._point){var e=this._x2-t,r=this._y2-n;this._l23_a=Math.sqrt(this._l23_2a=Math.pow(e*e+r*r,this._alpha))}switch(this._point){case 0:this._point=1,this._x3=t,this._y3=n;break;case 1:this._point=2,this._context.moveTo(this._x4=t,this._y4=n);break;case 2:this._point=3,this._x5=t,this._y5=n;break;default:Sm(this,t,n)}this._l01_a=this._l12_a,this._l12_a=this._l23_a,this._l01_2a=this._l12_2a,this._l12_2a=this._l23_2a,this._x0=this._x1,this._x1=this._x2,this._x2=t,this._y0=this._y1,this._y1=this._y2,this._y2=n}};var Cm=function t(n){function e(t){return n?new Nm(t,n):new wm(t,0)}return e.alpha=function(n){return t(+n)},e}(.5);function Pm(t,n){this._context=t,this._alpha=n}Pm.prototype={areaStart:function(){this._line=0},areaEnd:function(){this._line=NaN},lineStart:function(){this._x0=this._x1=this._x2=this._y0=this._y1=this._y2=NaN,this._l01_a=this._l12_a=this._l23_a=this._l01_2a=this._l12_2a=this._l23_2a=this._point=0},lineEnd:function(){(this._line||0!==this._line&&3===this._point)&&this._context.closePath(),this._line=1-this._line},point:function(t,n){if(t=+t,n=+n,this._point){var e=this._x2-t,r=this._y2-n;this._l23_a=Math.sqrt(this._l23_2a=Math.pow(e*e+r*r,this._alpha))}switch(this._point){case 0:this._point=1;break;case 1:this._point=2;break;case 2:this._point=3,this._line?this._context.lineTo(this._x2,this._y2):this._context.moveTo(this._x2,this._y2);break;case 3:this._point=4;default:Sm(this,t,n)}this._l01_a=this._l12_a,this._l12_a=this._l23_a,this._l01_2a=this._l12_2a,this._l12_2a=this._l23_2a,this._x0=this._x1,this._x1=this._x2,this._x2=t,this._y0=this._y1,this._y1=this._y2,this._y2=n}};var zm=function t(n){function e(t){return n?new Pm(t,n):new Am(t,0)}return e.alpha=function(n){return t(+n)},e}(.5);function Dm(t){this._context=t}function qm(t){return t<0?-1:1}function Rm(t,n,e){var r=t._x1-t._x0,i=n-t._x1,o=(t._y1-t._y0)/(r||i<0&&-0),a=(e-t._y1)/(i||r<0&&-0),u=(o*i+a*r)/(r+i);return(qm(o)+qm(a))*Math.min(Math.abs(o),Math.abs(a),.5*Math.abs(u))||0}function Fm(t,n){var e=t._x1-t._x0;return e?(3*(t._y1-t._y0)/e-n)/2:n}function Om(t,n,e){var r=t._x0,i=t._y0,o=t._x1,a=t._y1,u=(o-r)/3;t._context.bezierCurveTo(r+u,i+u*n,o-u,a-u*e,o,a)}function Im(t){this._context=t}function Um(t){this._context=new Bm(t)}function Bm(t){this._context=t}function Ym(t){this._context=t}function Lm(t){var n,e,r=t.length-1,i=new Array(r),o=new Array(r),a=new Array(r);for(i[0]=0,o[0]=2,a[0]=t[0]+2*t[1],n=1;n=0;--n)i[n]=(a[n]-i[n+1])/o[n];for(o[r-1]=(t[r]+i[r-1])/2,n=0;n1)for(var e,r,i,o=1,a=t[n[0]],u=a.length;o=0;)e[n]=n;return e}function Gm(t,n){return t[n]}function Vm(t){const n=[];return n.key=t,n}function $m(t){var n=t.map(Wm);return Xm(t).sort((function(t,e){return n[t]-n[e]}))}function Wm(t){for(var n,e=-1,r=0,i=t.length,o=-1/0;++eo&&(o=n,r=e);return r}function Zm(t){var n=t.map(Km);return Xm(t).sort((function(t,e){return n[t]-n[e]}))}function Km(t){for(var n,e=0,r=-1,i=t.length;++r=0&&(this._t=1-this._t,this._line=1-this._line)},point:function(t,n){switch(t=+t,n=+n,this._point){case 0:this._point=1,this._line?this._context.lineTo(t,n):this._context.moveTo(t,n);break;case 1:this._point=2;default:if(this._t<=0)this._context.lineTo(this._x,n),this._context.lineTo(t,n);else{var e=this._x*(1-this._t)+t*this._t;this._context.lineTo(e,this._y),this._context.lineTo(e,n)}}this._x=t,this._y=n}};var Qm=t=>()=>t;function Jm(t,{sourceEvent:n,target:e,transform:r,dispatch:i}){Object.defineProperties(this,{type:{value:t,enumerable:!0,configurable:!0},sourceEvent:{value:n,enumerable:!0,configurable:!0},target:{value:e,enumerable:!0,configurable:!0},transform:{value:r,enumerable:!0,configurable:!0},_:{value:i}})}function tx(t,n,e){this.k=t,this.x=n,this.y=e}tx.prototype={constructor:tx,scale:function(t){return 1===t?this:new tx(this.k*t,this.x,this.y)},translate:function(t,n){return 0===t&0===n?this:new tx(this.k,this.x+this.k*t,this.y+this.k*n)},apply:function(t){return[t[0]*this.k+this.x,t[1]*this.k+this.y]},applyX:function(t){return t*this.k+this.x},applyY:function(t){return t*this.k+this.y},invert:function(t){return[(t[0]-this.x)/this.k,(t[1]-this.y)/this.k]},invertX:function(t){return(t-this.x)/this.k},invertY:function(t){return(t-this.y)/this.k},rescaleX:function(t){return t.copy().domain(t.range().map(this.invertX,this).map(t.invert,t))},rescaleY:function(t){return t.copy().domain(t.range().map(this.invertY,this).map(t.invert,t))},toString:function(){return"translate("+this.x+","+this.y+") scale("+this.k+")"}};var nx=new tx(1,0,0);function ex(t){for(;!t.__zoom;)if(!(t=t.parentNode))return nx;return t.__zoom}function rx(t){t.stopImmediatePropagation()}function ix(t){t.preventDefault(),t.stopImmediatePropagation()}function ox(t){return!(t.ctrlKey&&"wheel"!==t.type||t.button)}function ax(){var t=this;return t instanceof SVGElement?(t=t.ownerSVGElement||t).hasAttribute("viewBox")?[[(t=t.viewBox.baseVal).x,t.y],[t.x+t.width,t.y+t.height]]:[[0,0],[t.width.baseVal.value,t.height.baseVal.value]]:[[0,0],[t.clientWidth,t.clientHeight]]}function ux(){return this.__zoom||nx}function cx(t){return-t.deltaY*(1===t.deltaMode?.05:t.deltaMode?1:.002)*(t.ctrlKey?10:1)}function fx(){return navigator.maxTouchPoints||"ontouchstart"in this}function sx(t,n,e){var r=t.invertX(n[0][0])-e[0][0],i=t.invertX(n[1][0])-e[1][0],o=t.invertY(n[0][1])-e[0][1],a=t.invertY(n[1][1])-e[1][1];return t.translate(i>r?(r+i)/2:Math.min(0,r)||Math.max(0,i),a>o?(o+a)/2:Math.min(0,o)||Math.max(0,a))}ex.prototype=tx.prototype,t.Adder=g,t.Delaunay=nu,t.FormatSpecifier=uc,t.InternMap=y,t.InternSet=v,t.Voronoi=Wa,t.active=function(t,n){var e,r,i=t.__transition;if(i)for(r in n=null==n?null:n+"",i)if((e=i[r]).state>1&&e.name===n)return new ji([[t]],_o,n,+r);return null},t.arc=function(){var t=vb,n=_b,e=rb(0),r=null,i=bb,o=mb,a=xb,u=null;function c(){var c,f,s=+t.apply(this,arguments),l=+n.apply(this,arguments),h=i.apply(this,arguments)-db,d=o.apply(this,arguments)-db,p=ib(d-h),g=d>h;if(u||(u=c=fa()),llb)if(p>pb-lb)u.moveTo(l*ab(h),l*fb(h)),u.arc(0,0,l,h,d,!g),s>lb&&(u.moveTo(s*ab(d),s*fb(d)),u.arc(0,0,s,d,h,g));else{var y,v,_=h,b=d,m=h,x=d,w=p,M=p,A=a.apply(this,arguments)/2,T=A>lb&&(r?+r.apply(this,arguments):sb(s*s+l*l)),S=cb(ib(l-s)/2,+e.apply(this,arguments)),E=S,k=S;if(T>lb){var N=yb(T/s*fb(A)),C=yb(T/l*fb(A));(w-=2*N)>lb?(m+=N*=g?1:-1,x-=N):(w=0,m=x=(h+d)/2),(M-=2*C)>lb?(_+=C*=g?1:-1,b-=C):(M=0,_=b=(h+d)/2)}var P=l*ab(_),z=l*fb(_),D=s*ab(x),q=s*fb(x);if(S>lb){var R,F=l*ab(b),O=l*fb(b),I=s*ab(m),U=s*fb(m);if(plb?k>lb?(y=Mb(I,U,P,z,l,k,g),v=Mb(F,O,D,q,l,k,g),u.moveTo(y.cx+y.x01,y.cy+y.y01),klb&&w>lb?E>lb?(y=Mb(D,q,F,O,s,-E,g),v=Mb(P,z,I,U,s,-E,g),u.lineTo(y.cx+y.x01,y.cy+y.y01),E>a,f=i+2*u>>a,s=wa(20);function l(r){var i=new Float32Array(c*f),l=new Float32Array(c*f);r.forEach((function(r,o,s){var l=+t(r,o,s)+u>>a,h=+n(r,o,s)+u>>a,d=+e(r,o,s);l>=0&&l=0&&h>a),Ca({width:c,height:f,data:l},{width:c,height:f,data:i},o>>a),Na({width:c,height:f,data:i},{width:c,height:f,data:l},o>>a),Ca({width:c,height:f,data:l},{width:c,height:f,data:i},o>>a),Na({width:c,height:f,data:i},{width:c,height:f,data:l},o>>a),Ca({width:c,height:f,data:l},{width:c,height:f,data:i},o>>a);var d=s(i);if(!Array.isArray(d)){var p=B(i);d=F(0,p,d),(d=Z(0,Math.floor(p/d)*d,d)).shift()}return ka().thresholds(d).size([c,f])(i).map(h)}function h(t){return t.value*=Math.pow(2,-2*a),t.coordinates.forEach(d),t}function d(t){t.forEach(p)}function p(t){t.forEach(g)}function g(t){t[0]=t[0]*Math.pow(2,a)-u,t[1]=t[1]*Math.pow(2,a)-u}function y(){return c=r+2*(u=3*o)>>a,f=i+2*u>>a,l}return l.x=function(n){return arguments.length?(t="function"==typeof n?n:wa(+n),l):t},l.y=function(t){return arguments.length?(n="function"==typeof t?t:wa(+t),l):n},l.weight=function(t){return arguments.length?(e="function"==typeof t?t:wa(+t),l):e},l.size=function(t){if(!arguments.length)return[r,i];var n=+t[0],e=+t[1];if(!(n>=0&&e>=0))throw new Error("invalid size");return r=n,i=e,y()},l.cellSize=function(t){if(!arguments.length)return 1<=1))throw new Error("invalid cell size");return a=Math.floor(Math.log(t)/Math.LN2),y()},l.thresholds=function(t){return arguments.length?(s="function"==typeof t?t:Array.isArray(t)?wa(ma.call(t)):wa(t),l):s},l.bandwidth=function(t){if(!arguments.length)return Math.sqrt(o*(o+1));if(!((t=+t)>=0))throw new Error("invalid bandwidth");return o=Math.round((Math.sqrt(4*t*t+1)-1)/2),y()},l},t.contours=ka,t.count=c,t.create=function(t){return Dn(At(t).call(document.documentElement))},t.creator=At,t.cross=function(...t){const n="function"==typeof t[t.length-1]&&function(t){return n=>t(...n)}(t.pop()),e=(t=t.map(l)).map(f),r=t.length-1,i=new Array(r+1).fill(0),o=[];if(r<0||e.some(s))return o;for(;;){o.push(i.map(((n,e)=>t[e][n])));let a=r;for(;++i[a]===e[a];){if(0===a)return n?o.map(n):o;i[a--]=0}}},t.csv=Pu,t.csvFormat=hu,t.csvFormatBody=du,t.csvFormatRow=gu,t.csvFormatRows=pu,t.csvFormatValue=yu,t.csvParse=su,t.csvParseRows=lu,t.cubehelix=tr,t.cumsum=function(t,n){var e=0,r=0;return Float64Array.from(t,void 0===n?t=>e+=+t||0:i=>e+=+n(i,r++,t)||0)},t.curveBasis=function(t){return new dm(t)},t.curveBasisClosed=function(t){return new pm(t)},t.curveBasisOpen=function(t){return new gm(t)},t.curveBumpX=function(t){return new ym(t,!0)},t.curveBumpY=function(t){return new ym(t,!1)},t.curveBundle=_m,t.curveCardinal=xm,t.curveCardinalClosed=Mm,t.curveCardinalOpen=Tm,t.curveCatmullRom=km,t.curveCatmullRomClosed=Cm,t.curveCatmullRomOpen=zm,t.curveLinear=Eb,t.curveLinearClosed=function(t){return new Dm(t)},t.curveMonotoneX=function(t){return new Im(t)},t.curveMonotoneY=function(t){return new Um(t)},t.curveNatural=function(t){return new Ym(t)},t.curveStep=function(t){return new jm(t,.5)},t.curveStepAfter=function(t){return new jm(t,1)},t.curveStepBefore=function(t){return new jm(t,0)},t.descending=function(t,n){return nt?1:n>=t?0:NaN},t.deviation=d,t.difference=function(t,...n){t=new Set(t);for(const e of n)for(const n of e)t.delete(n);return t},t.disjoint=function(t,n){const e=n[Symbol.iterator](),r=new Set;for(const n of t){if(r.has(n))return!1;let t,i;for(;({value:t,done:i}=e.next())&&!i;){if(Object.is(n,t))return!1;r.add(t)}}return!0},t.dispatch=pt,t.drag=function(){var t,n,e,r,i=Xn,o=Gn,a=Vn,u=$n,c={},f=pt("start","drag","end"),s=0,l=0;function h(t){t.on("mousedown.drag",d).filter(u).on("touchstart.drag",y).on("touchmove.drag",v).on("touchend.drag touchcancel.drag",_).style("touch-action","none").style("-webkit-tap-highlight-color","rgba(0,0,0,0)")}function d(a,u){if(!r&&i.call(this,a,u)){var c=b(this,o.call(this,a,u),a,u,"mouse");c&&(Dn(a.view).on("mousemove.drag",p,!0).on("mouseup.drag",g,!0),Yn(a.view),Un(a),e=!1,t=a.clientX,n=a.clientY,c("start",a))}}function p(r){if(Bn(r),!e){var i=r.clientX-t,o=r.clientY-n;e=i*i+o*o>l}c.mouse("drag",r)}function g(t){Dn(t.view).on("mousemove.drag mouseup.drag",null),Ln(t.view,e),Bn(t),c.mouse("end",t)}function y(t,n){if(i.call(this,t,n)){var e,r,a=t.changedTouches,u=o.call(this,t,n),c=a.length;for(e=0;e+t,t.easePoly=Ki,t.easePolyIn=Wi,t.easePolyInOut=Ki,t.easePolyOut=Zi,t.easeQuad=Vi,t.easeQuadIn=function(t){return t*t},t.easeQuadInOut=Vi,t.easeQuadOut=function(t){return t*(2-t)},t.easeSin=to,t.easeSinIn=function(t){return 1==+t?1:1-Math.cos(t*Ji)},t.easeSinInOut=to,t.easeSinOut=function(t){return Math.sin(t*Ji)},t.every=function(t,n){if("function"!=typeof n)throw new TypeError("test is not a function");let e=-1;for(const r of t)if(!n(r,++e,t))return!1;return!0},t.extent=p,t.fcumsum=function(t,n){const e=new g;let r=-1;return Float64Array.from(t,void 0===n?t=>e.add(+t||0):i=>e.add(+n(i,++r,t)||0))},t.filter=function(t,n){if("function"!=typeof n)throw new TypeError("test is not a function");const e=[];let r=-1;for(const i of t)n(i,++r,t)&&e.push(i);return e},t.forceCenter=function(t,n){var e,r=1;function i(){var i,o,a=e.length,u=0,c=0;for(i=0;if+p||os+p||ac.index){var g=f-u.x-u.vx,y=s-u.y-u.vy,v=g*g+y*y;vt.r&&(t.r=t[n].r)}function c(){if(n){var r,i,o=n.length;for(e=new Array(o),r=0;r[u(t,n,r),t])));for(a=0,i=new Array(f);a=u)){(t.data!==n||t.next)&&(0===l&&(p+=(l=Vu(e))*l),0===h&&(p+=(h=Vu(e))*h),p(t=(1664525*t+1013904223)%Qu)/Qu}();function l(){h(),f.call("tick",n),e1?(null==e?u.delete(t):u.set(t,p(e)),n):u.get(t)},find:function(n,e,r){var i,o,a,u,c,f=0,s=t.length;for(null==r?r=1/0:r*=r,f=0;f1?(f.on(t,e),n):f.on(t)}}},t.forceX=function(t){var n,e,r,i=Gu(.1);function o(t){for(var i,o=0,a=n.length;o=.12&&i<.234&&r>=-.425&&r<-.214?u:i>=.166&&i<.234&&r>=-.214&&r<-.115?c:a).invert(t)},s.stream=function(e){return t&&n===e?t:(r=[a.stream(n=e),u.stream(e),c.stream(e)],i=r.length,t={point:function(t,n){for(var e=-1;++eKf(r[0],r[1])&&(r[1]=i[1]),Kf(i[0],r[1])>Kf(r[0],r[1])&&(r[0]=i[0])):o.push(r=i);for(a=-1/0,n=0,r=o[e=o.length-1];n<=e;r=i,++n)i=o[n],(u=Kf(r[1],i[0]))>a&&(a=u,nf=i[0],rf=r[1])}return lf=hf=null,nf===1/0||ef===1/0?[[NaN,NaN],[NaN,NaN]]:[[nf,ef],[rf,of]]},t.geoCentroid=function(t){Ef=kf=Nf=Cf=Pf=zf=Df=qf=0,Rf=new g,Ff=new g,Of=new g,Wc(t,ts);var n=+Rf,e=+Ff,r=+Of,i=Dc(n,e,r);return i2?t[2]+90:90]):[(t=e())[0],t[1],t[2]-90]},e([0,0,90]).scale(159.155)},t.geoTransverseMercatorRaw=Bh,t.gray=function(t,n){return new Fe(t,0,0,null==n?1:n)},t.greatest=function(t,e=n){let r,i=!1;if(1===e.length){let o;for(const a of t){const t=e(a);(i?n(t,o)>0:0===n(t,t))&&(r=a,o=t,i=!0)}}else for(const n of t)(i?e(n,r)>0:0===e(n,n))&&(r=n,i=!0);return r},t.greatestIndex=function(t,e=n){if(1===e.length)return G(t,e);let r,i=-1,o=-1;for(const n of t)++o,(i<0?0===e(n,n):e(n,r)>0)&&(r=n,i=o);return i},t.group=M,t.groupSort=function(t,e,r){return(1===e.length?k(A(t,e,r),(([t,e],[r,i])=>n(e,i)||n(t,r))):k(M(t,r),(([t,r],[i,o])=>e(r,o)||n(t,i)))).map((([t])=>t))},t.groups=function(t,...n){return S(t,Array.from,w,n)},t.hcl=Le,t.hierarchy=Xh,t.histogram=U,t.hsl=Ae,t.html=Fu,t.image=function(t,n){return new Promise((function(e,r){var i=new Image;for(var o in n)i[o]=n[o];i.onerror=r,i.onload=function(){e(i)},i.src=t}))},t.index=function(t,...n){return S(t,w,T,n)},t.indexes=function(t,...n){return S(t,Array.from,T,n)},t.interpolate=Mr,t.interpolateArray=function(t,n){return(gr(n)?pr:yr)(t,n)},t.interpolateBasis=rr,t.interpolateBasisClosed=ir,t.interpolateBlues=q_,t.interpolateBrBG=Gv,t.interpolateBuGn=s_,t.interpolateBuPu=h_,t.interpolateCividis=function(t){return t=Math.max(0,Math.min(1,t)),"rgb("+Math.max(0,Math.min(255,Math.round(-4.54-t*(35.34-t*(2381.73-t*(6402.7-t*(7024.72-2710.57*t)))))))+", "+Math.max(0,Math.min(255,Math.round(32.49+t*(170.73+t*(52.82-t*(131.46-t*(176.58-67.37*t)))))))+", "+Math.max(0,Math.min(255,Math.round(81.24+t*(442.36-t*(2482.43-t*(6167.24-t*(6614.94-2475.67*t)))))))+")"},t.interpolateCool=V_,t.interpolateCubehelix=Yr,t.interpolateCubehelixDefault=X_,t.interpolateCubehelixLong=Lr,t.interpolateDate=vr,t.interpolateDiscrete=function(t){var n=t.length;return function(e){return t[Math.max(0,Math.min(n-1,Math.floor(e*n)))]}},t.interpolateGnBu=p_,t.interpolateGreens=F_,t.interpolateGreys=I_,t.interpolateHcl=Ir,t.interpolateHclLong=Ur,t.interpolateHsl=Rr,t.interpolateHslLong=Fr,t.interpolateHue=function(t,n){var e=ur(+t,+n);return function(t){var n=e(t);return n-360*Math.floor(n/360)}},t.interpolateInferno=nb,t.interpolateLab=function(t,n){var e=fr((t=Re(t)).l,(n=Re(n)).l),r=fr(t.a,n.a),i=fr(t.b,n.b),o=fr(t.opacity,n.opacity);return function(n){return t.l=e(n),t.a=r(n),t.b=i(n),t.opacity=o(n),t+""}},t.interpolateMagma=tb,t.interpolateNumber=_r,t.interpolateNumberArray=pr,t.interpolateObject=br,t.interpolateOrRd=y_,t.interpolateOranges=H_,t.interpolatePRGn=$v,t.interpolatePiYG=Zv,t.interpolatePlasma=eb,t.interpolatePuBu=m_,t.interpolatePuBuGn=__,t.interpolatePuOr=Qv,t.interpolatePuRd=w_,t.interpolatePurples=B_,t.interpolateRainbow=function(t){(t<0||t>1)&&(t-=Math.floor(t));var n=Math.abs(t-.5);return $_.h=360*t-100,$_.s=1.5-1.5*n,$_.l=.8-.9*n,$_+""},t.interpolateRdBu=t_,t.interpolateRdGy=e_,t.interpolateRdPu=A_,t.interpolateRdYlBu=i_,t.interpolateRdYlGn=a_,t.interpolateReds=L_,t.interpolateRgb=sr,t.interpolateRgbBasis=hr,t.interpolateRgbBasisClosed=dr,t.interpolateRound=Ar,t.interpolateSinebow=function(t){var n;return t=(.5-t)*Math.PI,W_.r=255*(n=Math.sin(t))*n,W_.g=255*(n=Math.sin(t+Z_))*n,W_.b=255*(n=Math.sin(t+K_))*n,W_+""},t.interpolateSpectral=c_,t.interpolateString=wr,t.interpolateTransformCss=Cr,t.interpolateTransformSvg=Pr,t.interpolateTurbo=function(t){return t=Math.max(0,Math.min(1,t)),"rgb("+Math.max(0,Math.min(255,Math.round(34.61+t*(1172.33-t*(10793.56-t*(33300.12-t*(38394.49-14825.05*t)))))))+", "+Math.max(0,Math.min(255,Math.round(23.31+t*(557.33+t*(1225.33-t*(3574.96-t*(1073.77+707.56*t)))))))+", "+Math.max(0,Math.min(255,Math.round(27.2+t*(3211.1-t*(15327.97-t*(27814-t*(22569.18-6838.66*t)))))))+")"},t.interpolateViridis=J_,t.interpolateWarm=G_,t.interpolateYlGn=k_,t.interpolateYlGnBu=S_,t.interpolateYlOrBr=C_,t.interpolateYlOrRd=z_,t.interpolateZoom=Dr,t.interrupt=gi,t.intersection=function(t,...n){t=new Set(t),n=n.map(et);t:for(const e of t)for(const r of n)if(!r.has(e)){t.delete(e);continue t}return t},t.interval=function(t,n,e){var r=new ei,i=n;return null==n?(r.restart(t,n,e),r):(r._restart=r.restart,r.restart=function(t,n,e){n=+n,e=null==e?ti():+e,r._restart((function o(a){a+=i,r._restart(o,i+=n,e),t(a)}),n,e)},r.restart(t,n,e),r)},t.isoFormat=Mv,t.isoParse=Av,t.json=function(t,n){return fetch(t,n).then(Du)},t.lab=Re,t.lch=function(t,n,e,r){return 1===arguments.length?Ye(t):new je(e,n,t,null==r?1:r)},t.least=function(t,e=n){let r,i=!1;if(1===e.length){let o;for(const a of t){const t=e(a);(i?n(t,o)<0:0===n(t,t))&&(r=a,o=t,i=!0)}}else for(const n of t)(i?e(n,r)<0:0===e(n,n))&&(r=n,i=!0);return r},t.leastIndex=K,t.line=Cb,t.lineRadial=Ib,t.linkHorizontal=function(){return jb(Hb)},t.linkRadial=function(){var t=jb(Gb);return t.angle=t.x,delete t.x,t.radius=t.y,delete t.y,t},t.linkVertical=function(){return jb(Xb)},t.local=Rn,t.map=function(t,n){if("function"!=typeof t[Symbol.iterator])throw new TypeError("values is not iterable");if("function"!=typeof n)throw new TypeError("mapper is not a function");return Array.from(t,((e,r)=>n(e,r,t)))},t.matcher=Ct,t.max=B,t.maxIndex=G,t.mean=function(t,n){let e=0,r=0;if(void 0===n)for(let n of t)null!=n&&(n=+n)>=n&&(++e,r+=n);else{let i=-1;for(let o of t)null!=(o=n(o,++i,t))&&(o=+o)>=o&&(++e,r+=o)}if(e)return r/e},t.median=function(t,n){return H(t,.5,n)},t.merge=V,t.min=Y,t.minIndex=$,t.namespace=xt,t.namespaces=mt,t.nice=O,t.now=ti,t.pack=function(){var t=null,n=1,e=1,r=hd;function i(i){return i.x=n/2,i.y=e/2,t?i.eachBefore(gd(t)).eachAfter(yd(r,.5)).eachBefore(vd(1)):i.eachBefore(gd(pd)).eachAfter(yd(hd,1)).eachAfter(yd(r,i.r/Math.min(n,e))).eachBefore(vd(Math.min(n,e)/(2*i.r))),i}return i.radius=function(n){return arguments.length?(t=sd(n),i):t},i.size=function(t){return arguments.length?(n=+t[0],e=+t[1],i):[n,e]},i.padding=function(t){return arguments.length?(r="function"==typeof t?t:dd(+t),i):r},i},t.packEnclose=Kh,t.packSiblings=function(t){return fd(t),t},t.pairs=function(t,n=W){const e=[];let r,i=!1;for(const o of t)i&&e.push(n(r,o)),r=o,i=!0;return e},t.partition=function(){var t=1,n=1,e=0,r=!1;function i(i){var o=i.height+1;return i.x0=i.y0=e,i.x1=t,i.y1=n/o,i.eachBefore(function(t,n){return function(r){r.children&&bd(r,r.x0,t*(r.depth+1)/n,r.x1,t*(r.depth+2)/n);var i=r.x0,o=r.y0,a=r.x1-e,u=r.y1-e;a0&&(d+=l);for(null!=n?p.sort((function(t,e){return n(g[t],g[e])})):null!=e&&p.sort((function(t,n){return e(a[t],a[n])})),u=0,f=d?(v-h*b)/d:0;u0?l*f:0)+b,g[c]={data:a[c],index:u,value:l,startAngle:y,endAngle:s,padAngle:_};return g}return a.value=function(n){return arguments.length?(t="function"==typeof n?n:rb(+n),a):t},a.sortValues=function(t){return arguments.length?(n=t,e=null,a):n},a.sort=function(t){return arguments.length?(e=t,n=null,a):e},a.startAngle=function(t){return arguments.length?(r="function"==typeof t?t:rb(+t),a):r},a.endAngle=function(t){return arguments.length?(i="function"==typeof t?t:rb(+t),a):i},a.padAngle=function(t){return arguments.length?(o="function"==typeof t?t:rb(+t),a):o},a},t.piecewise=jr,t.pointRadial=Bb,t.pointer=In,t.pointers=function(t,n){return t.target&&(t=On(t),void 0===n&&(n=t.currentTarget),t=t.touches||[t]),Array.from(t,(t=>In(t,n)))},t.polygonArea=function(t){for(var n,e=-1,r=t.length,i=t[r-1],o=0;++eu!=f>u&&a<(c-e)*(u-r)/(f-r)+e&&(s=!s),c=e,f=r;return s},t.polygonHull=function(t){if((e=t.length)<3)return null;var n,e,r=new Array(e),i=new Array(e);for(n=0;n=0;--n)f.push(t[r[o[n]][2]]);for(n=+u;n(n=1664525*n+1013904223|0,ep*(n>>>0))},t.randomLogNormal=Ld,t.randomLogistic=tp,t.randomNormal=Yd,t.randomPareto=Gd,t.randomPoisson=np,t.randomUniform=Ud,t.randomWeibull=Qd,t.range=Z,t.reduce=function(t,n,e){if("function"!=typeof n)throw new TypeError("reducer is not a function");const r=t[Symbol.iterator]();let i,o,a=-1;if(arguments.length<3){if(({done:i,value:e}=r.next()),i)return;++a}for(;({done:i,value:o}=r.next()),!i;)e=n(e,o,++a,t);return e},t.reverse=function(t){if("function"!=typeof t[Symbol.iterator])throw new TypeError("values is not iterable");return Array.from(t).reverse()},t.rgb=ve,t.ribbon=function(){return ba()},t.ribbonArrow=function(){return ba(_a)},t.rollup=A,t.rollups=function(t,n,...e){return S(t,Array.from,n,e)},t.scaleBand=up,t.scaleDiverging=function t(){var n=bp(Pv()(lp));return n.copy=function(){return Nv(n,t())},ip.apply(n,arguments)},t.scaleDivergingLog=function t(){var n=Ep(Pv()).domain([.1,1,10]);return n.copy=function(){return Nv(n,t()).base(n.base())},ip.apply(n,arguments)},t.scaleDivergingPow=zv,t.scaleDivergingSqrt=function(){return zv.apply(null,arguments).exponent(.5)},t.scaleDivergingSymlog=function t(){var n=Cp(Pv());return n.copy=function(){return Nv(n,t()).constant(n.constant())},ip.apply(n,arguments)},t.scaleIdentity=function t(n){var e;function r(t){return null==t||isNaN(t=+t)?e:t}return r.invert=r,r.domain=r.range=function(t){return arguments.length?(n=Array.from(t,fp),r):n.slice()},r.unknown=function(t){return arguments.length?(e=t,r):e},r.copy=function(){return t(n).unknown(e)},n=arguments.length?Array.from(n,fp):[0,1],bp(r)},t.scaleImplicit=op,t.scaleLinear=function t(){var n=vp();return n.copy=function(){return gp(n,t())},rp.apply(n,arguments),bp(n)},t.scaleLog=function t(){var n=Ep(yp()).domain([1,10]);return n.copy=function(){return gp(n,t()).base(n.base())},rp.apply(n,arguments),n},t.scaleOrdinal=ap,t.scalePoint=function(){return cp(up.apply(null,arguments).paddingInner(1))},t.scalePow=Rp,t.scaleQuantile=function t(){var e,r=[],i=[],a=[];function u(){var t=0,n=Math.max(1,i.length);for(a=new Array(n-1);++t0?a[n-1]:r[0],n=i?[a[i-1],r]:[a[n-1],a[n]]},c.unknown=function(t){return arguments.length?(n=t,c):c},c.thresholds=function(){return a.slice()},c.copy=function(){return t().domain([e,r]).range(u).unknown(n)},rp.apply(bp(c),arguments)},t.scaleRadial=function t(){var n,e=vp(),r=[0,1],i=!1;function o(t){var r=Op(e(t));return isNaN(r)?n:i?Math.round(r):r}return o.invert=function(t){return e.invert(Fp(t))},o.domain=function(t){return arguments.length?(e.domain(t),o):e.domain()},o.range=function(t){return arguments.length?(e.range((r=Array.from(t,fp)).map(Fp)),o):r.slice()},o.rangeRound=function(t){return o.range(t).round(!0)},o.round=function(t){return arguments.length?(i=!!t,o):i},o.clamp=function(t){return arguments.length?(e.clamp(t),o):e.clamp()},o.unknown=function(t){return arguments.length?(n=t,o):n},o.copy=function(){return t(e.domain(),r).round(i).clamp(e.clamp()).unknown(n)},rp.apply(o,arguments),bp(o)},t.scaleSequential=function t(){var n=bp(kv()(lp));return n.copy=function(){return Nv(n,t())},ip.apply(n,arguments)},t.scaleSequentialLog=function t(){var n=Ep(kv()).domain([1,10]);return n.copy=function(){return Nv(n,t()).base(n.base())},ip.apply(n,arguments)},t.scaleSequentialPow=Cv,t.scaleSequentialQuantile=function t(){var e=[],r=lp;function i(t){if(null!=t&&!isNaN(t=+t))return r((o(e,t,1)-1)/(e.length-1))}return i.domain=function(t){if(!arguments.length)return e.slice();e=[];for(let n of t)null==n||isNaN(n=+n)||e.push(n);return e.sort(n),i},i.interpolator=function(t){return arguments.length?(r=t,i):r},i.range=function(){return e.map(((t,n)=>r(n/(e.length-1))))},i.quantiles=function(t){return Array.from({length:t+1},((n,r)=>H(e,r/t)))},i.copy=function(){return t(r).domain(e)},ip.apply(i,arguments)},t.scaleSequentialSqrt=function(){return Cv.apply(null,arguments).exponent(.5)},t.scaleSequentialSymlog=function t(){var n=Cp(kv());return n.copy=function(){return Nv(n,t()).constant(n.constant())},ip.apply(n,arguments)},t.scaleSqrt=function(){return Rp.apply(null,arguments).exponent(.5)},t.scaleSymlog=function t(){var n=Cp(yp());return n.copy=function(){return gp(n,t()).constant(n.constant())},rp.apply(n,arguments)},t.scaleThreshold=function t(){var n,e=[.5],r=[0,1],i=1;function a(t){return null!=t&&t<=t?r[o(e,t,0,i)]:n}return a.domain=function(t){return arguments.length?(e=Array.from(t),i=Math.min(e.length,r.length-1),a):e.slice()},a.range=function(t){return arguments.length?(r=Array.from(t),i=Math.min(e.length,r.length-1),a):r.slice()},a.invertExtent=function(t){var n=r.indexOf(t);return[e[n-1],e[n]]},a.unknown=function(t){return arguments.length?(n=t,a):n},a.copy=function(){return t().domain(e).range(r).unknown(n)},rp.apply(a,arguments)},t.scaleTime=function(){return rp.apply(Ev(Kg,Qg,xg,bg,og,eg,tg,Qp,Zp,t.timeFormat).domain([new Date(2e3,0,1),new Date(2e3,0,2)]),arguments)},t.scaleUtc=function(){return rp.apply(Ev(Wg,Zg,Gg,Hg,Cg,Eg,Tg,Mg,Zp,t.utcFormat).domain([Date.UTC(2e3,0,1),Date.UTC(2e3,0,2)]),arguments)},t.scan=function(t,n){const e=K(t,n);return e<0?void 0:e},t.schemeAccent=Rv,t.schemeBlues=D_,t.schemeBrBG=Xv,t.schemeBuGn=f_,t.schemeBuPu=l_,t.schemeCategory10=qv,t.schemeDark2=Fv,t.schemeGnBu=d_,t.schemeGreens=R_,t.schemeGreys=O_,t.schemeOrRd=g_,t.schemeOranges=j_,t.schemePRGn=Vv,t.schemePaired=Ov,t.schemePastel1=Iv,t.schemePastel2=Uv,t.schemePiYG=Wv,t.schemePuBu=b_,t.schemePuBuGn=v_,t.schemePuOr=Kv,t.schemePuRd=x_,t.schemePurples=U_,t.schemeRdBu=Jv,t.schemeRdGy=n_,t.schemeRdPu=M_,t.schemeRdYlBu=r_,t.schemeRdYlGn=o_,t.schemeReds=Y_,t.schemeSet1=Bv,t.schemeSet2=Yv,t.schemeSet3=Lv,t.schemeSpectral=u_,t.schemeTableau10=jv,t.schemeYlGn=E_,t.schemeYlGnBu=T_,t.schemeYlOrBr=N_,t.schemeYlOrRd=P_,t.select=Dn,t.selectAll=function(t){return"string"==typeof t?new Pn([document.querySelectorAll(t)],[document.documentElement]):new Pn([null==t?[]:Et(t)],Cn)},t.selection=zn,t.selector=St,t.selectorAll=Nt,t.shuffle=Q,t.shuffler=J,t.some=function(t,n){if("function"!=typeof n)throw new TypeError("test is not a function");let e=-1;for(const r of t)if(n(r,++e,t))return!0;return!1},t.sort=k,t.stack=function(){var t=rb([]),n=Xm,e=Hm,r=Gm;function i(i){var o,a,u=Array.from(t.apply(this,arguments),Vm),c=u.length,f=-1;for(const t of i)for(o=0,++f;o0)for(var e,r,i,o,a,u,c=0,f=t[n[0]].length;c0?(r[0]=o,r[1]=o+=i):i<0?(r[1]=a,r[0]=a+=i):(r[0]=0,r[1]=i)},t.stackOffsetExpand=function(t,n){if((r=t.length)>0){for(var e,r,i,o=0,a=t[0].length;o0){for(var e,r=0,i=t[n[0]],o=i.length;r0&&(r=(e=t[n[0]]).length)>0){for(var e,r,i,o=0,a=1;a0)throw new Error("cycle");return o}return e.id=function(n){return arguments.length?(t=ld(n),e):t},e.parentId=function(t){return arguments.length?(n=ld(t),e):n},e},t.style=Jt,t.subset=function(t,n){return rt(n,t)},t.sum=function(t,n){let e=0;if(void 0===n)for(let n of t)(n=+n)&&(e+=n);else{let r=-1;for(let i of t)(i=+n(i,++r,t))&&(e+=i)}return e},t.superset=rt,t.svg=Ou,t.symbol=function(t,n){var e=null;function r(){var r;if(e||(e=r=fa()),t.apply(this,arguments).draw(e,+n.apply(this,arguments)),r)return e=null,r+""||null}return t="function"==typeof t?t:rb(t||Vb),n="function"==typeof n?n:rb(void 0===n?64:+n),r.type=function(n){return arguments.length?(t="function"==typeof n?n:rb(n),r):t},r.size=function(t){return arguments.length?(n="function"==typeof t?t:rb(+t),r):n},r.context=function(t){return arguments.length?(e=null==t?null:t,r):e},r},t.symbolCircle=Vb,t.symbolCross=$b,t.symbolDiamond=Kb,t.symbolSquare=em,t.symbolStar=nm,t.symbolTriangle=im,t.symbolWye=fm,t.symbols=sm,t.text=Nu,t.thresholdFreedmanDiaconis=function(t,n,e){return Math.ceil((e-n)/(2*(H(t,.75)-H(t,.25))*Math.pow(c(t),-1/3)))},t.thresholdScott=function(t,n,e){return Math.ceil((e-n)/(3.5*d(t)*Math.pow(c(t),-1/3)))},t.thresholdSturges=I,t.tickFormat=_p,t.tickIncrement=R,t.tickStep=F,t.ticks=q,t.timeDay=eg,t.timeDays=rg,t.timeFormatDefaultLocale=xv,t.timeFormatLocale=ey,t.timeFriday=sg,t.timeFridays=vg,t.timeHour=tg,t.timeHours=ng,t.timeInterval=Bp,t.timeMillisecond=Yp,t.timeMilliseconds=Lp,t.timeMinute=Qp,t.timeMinutes=Jp,t.timeMonday=ag,t.timeMondays=dg,t.timeMonth=bg,t.timeMonths=mg,t.timeSaturday=lg,t.timeSaturdays=_g,t.timeSecond=Zp,t.timeSeconds=Kp,t.timeSunday=og,t.timeSundays=hg,t.timeThursday=fg,t.timeThursdays=yg,t.timeTickInterval=Qg,t.timeTicks=Kg,t.timeTuesday=ug,t.timeTuesdays=pg,t.timeWednesday=cg,t.timeWednesdays=gg,t.timeWeek=og,t.timeWeeks=hg,t.timeYear=xg,t.timeYears=wg,t.timeout=ci,t.timer=ri,t.timerFlush=ii,t.transition=Hi,t.transpose=tt,t.tree=function(){var t=Ad,n=1,e=1,r=null;function i(i){var c=function(t){for(var n,e,r,i,o,a=new Nd(t,0),u=[a];n=u.pop();)if(r=n._.children)for(n.children=new Array(o=r.length),i=o-1;i>=0;--i)u.push(e=n.children[i]=new Nd(r[i],i)),e.parent=n;return(a.parent=new Nd(null,0)).children=[a],a}(i);if(c.eachAfter(o),c.parent.m=-c.z,c.eachBefore(a),r)i.eachBefore(u);else{var f=i,s=i,l=i;i.eachBefore((function(t){t.xs.x&&(s=t),t.depth>l.depth&&(l=t)}));var h=f===s?1:t(f,s)/2,d=h-f.x,p=n/(s.x+h+d),g=e/(l.depth||1);i.eachBefore((function(t){t.x=(t.x+d)*p,t.y=t.depth*g}))}return i}function o(n){var e=n.children,r=n.parent.children,i=n.i?r[n.i-1]:null;if(e){!function(t){for(var n,e=0,r=0,i=t.children,o=i.length;--o>=0;)(n=i[o]).z+=e,n.m+=e,e+=n.s+(r+=n.c)}(n);var o=(e[0].z+e[e.length-1].z)/2;i?(n.z=i.z+t(n._,i._),n.m=n.z-o):n.z=o}else i&&(n.z=i.z+t(n._,i._));n.parent.A=function(n,e,r){if(e){for(var i,o=n,a=n,u=e,c=o.parent.children[0],f=o.m,s=a.m,l=u.m,h=c.m;u=Sd(u),o=Td(o),u&&o;)c=Td(c),(a=Sd(a)).a=n,(i=u.z+l-o.z-f+t(u._,o._))>0&&(Ed(kd(u,n,r),n,i),f+=i,s+=i),l+=u.m,f+=o.m,h+=c.m,s+=a.m;u&&!Sd(a)&&(a.t=u,a.m+=l-s),o&&!Td(c)&&(c.t=o,c.m+=f-h,r=n)}return r}(n,i,n.parent.A||r[0])}function a(t){t._.x=t.z+t.parent.m,t.m+=t.parent.m}function u(t){t.x*=n,t.y=t.depth*e}return i.separation=function(n){return arguments.length?(t=n,i):t},i.size=function(t){return arguments.length?(r=!1,n=+t[0],e=+t[1],i):r?null:[n,e]},i.nodeSize=function(t){return arguments.length?(r=!0,n=+t[0],e=+t[1],i):r?[n,e]:null},i},t.treemap=function(){var t=Dd,n=!1,e=1,r=1,i=[0],o=hd,a=hd,u=hd,c=hd,f=hd;function s(t){return t.x0=t.y0=0,t.x1=e,t.y1=r,t.eachBefore(l),i=[0],n&&t.eachBefore(_d),t}function l(n){var e=i[n.depth],r=n.x0+e,s=n.y0+e,l=n.x1-e,h=n.y1-e;l=e-1){var s=u[n];return s.x0=i,s.y0=o,s.x1=a,void(s.y1=c)}var l=f[n],h=r/2+l,d=n+1,p=e-1;for(;d>>1;f[g]c-o){var _=r?(i*v+a*y)/r:a;t(n,d,y,i,o,_,c),t(d,e,v,_,o,a,c)}else{var b=r?(o*v+c*y)/r:c;t(n,d,y,i,o,a,b),t(d,e,v,i,b,a,c)}}(0,c,t.value,n,e,r,i)},t.treemapDice=bd,t.treemapResquarify=qd,t.treemapSlice=Cd,t.treemapSliceDice=function(t,n,e,r,i){(1&t.depth?Cd:bd)(t,n,e,r,i)},t.treemapSquarify=Dd,t.tsv=zu,t.tsvFormat=mu,t.tsvFormatBody=xu,t.tsvFormatRow=Mu,t.tsvFormatRows=wu,t.tsvFormatValue=Au,t.tsvParse=_u,t.tsvParseRows=bu,t.union=function(...t){const n=new Set;for(const e of t)for(const t of e)n.add(t);return n},t.utcDay=Eg,t.utcDays=kg,t.utcFriday=Rg,t.utcFridays=Lg,t.utcHour=Tg,t.utcHours=Sg,t.utcMillisecond=Yp,t.utcMilliseconds=Lp,t.utcMinute=Mg,t.utcMinutes=Ag,t.utcMonday=Pg,t.utcMondays=Ig,t.utcMonth=Hg,t.utcMonths=Xg,t.utcSaturday=Fg,t.utcSaturdays=jg,t.utcSecond=Zp,t.utcSeconds=Kp,t.utcSunday=Cg,t.utcSundays=Og,t.utcThursday=qg,t.utcThursdays=Yg,t.utcTickInterval=Zg,t.utcTicks=Wg,t.utcTuesday=zg,t.utcTuesdays=Ug,t.utcWednesday=Dg,t.utcWednesdays=Bg,t.utcWeek=Cg,t.utcWeeks=Og,t.utcYear=Gg,t.utcYears=Vg,t.variance=h,t.version="6.7.0",t.window=Wt,t.xml=Ru,t.zip=function(){return tt(arguments)},t.zoom=function(){var t,n,e,r=ox,i=ax,o=sx,a=cx,u=fx,c=[0,1/0],f=[[-1/0,-1/0],[1/0,1/0]],s=250,l=Dr,h=pt("start","zoom","end"),d=500,p=0,g=10;function y(t){t.property("__zoom",ux).on("wheel.zoom",M).on("mousedown.zoom",A).on("dblclick.zoom",T).filter(u).on("touchstart.zoom",S).on("touchmove.zoom",E).on("touchend.zoom touchcancel.zoom",k).style("-webkit-tap-highlight-color","rgba(0,0,0,0)")}function v(t,n){return(n=Math.max(c[0],Math.min(c[1],n)))===t.k?t:new tx(n,t.x,t.y)}function _(t,n,e){var r=n[0]-e[0]*t.k,i=n[1]-e[1]*t.k;return r===t.x&&i===t.y?t:new tx(t.k,r,i)}function b(t){return[(+t[0][0]+ +t[1][0])/2,(+t[0][1]+ +t[1][1])/2]}function m(t,n,e,r){t.on("start.zoom",(function(){x(this,arguments).event(r).start()})).on("interrupt.zoom end.zoom",(function(){x(this,arguments).event(r).end()})).tween("zoom",(function(){var t=this,o=arguments,a=x(t,o).event(r),u=i.apply(t,o),c=null==e?b(u):"function"==typeof e?e.apply(t,o):e,f=Math.max(u[1][0]-u[0][0],u[1][1]-u[0][1]),s=t.__zoom,h="function"==typeof n?n.apply(t,o):n,d=l(s.invert(c).concat(f/s.k),h.invert(c).concat(f/h.k));return function(t){if(1===t)t=h;else{var n=d(t),e=f/n[2];t=new tx(e,c[0]-n[0]*e,c[1]-n[1]*e)}a.zoom(null,t)}}))}function x(t,n,e){return!e&&t.__zooming||new w(t,n)}function w(t,n){this.that=t,this.args=n,this.active=0,this.sourceEvent=null,this.extent=i.apply(t,n),this.taps=0}function M(t,...n){if(r.apply(this,arguments)){var e=x(this,n).event(t),i=this.__zoom,u=Math.max(c[0],Math.min(c[1],i.k*Math.pow(2,a.apply(this,arguments)))),s=In(t);if(e.wheel)e.mouse[0][0]===s[0]&&e.mouse[0][1]===s[1]||(e.mouse[1]=i.invert(e.mouse[0]=s)),clearTimeout(e.wheel);else{if(i.k===u)return;e.mouse=[s,i.invert(s)],gi(this),e.start()}ix(t),e.wheel=setTimeout(l,150),e.zoom("mouse",o(_(v(i,u),e.mouse[0],e.mouse[1]),e.extent,f))}function l(){e.wheel=null,e.end()}}function A(t,...n){if(!e&&r.apply(this,arguments)){var i=x(this,n,!0).event(t),a=Dn(t.view).on("mousemove.zoom",h,!0).on("mouseup.zoom",d,!0),u=In(t,c),c=t.currentTarget,s=t.clientX,l=t.clientY;Yn(t.view),rx(t),i.mouse=[u,this.__zoom.invert(u)],gi(this),i.start()}function h(t){if(ix(t),!i.moved){var n=t.clientX-s,e=t.clientY-l;i.moved=n*n+e*e>p}i.event(t).zoom("mouse",o(_(i.that.__zoom,i.mouse[0]=In(t,c),i.mouse[1]),i.extent,f))}function d(t){a.on("mousemove.zoom mouseup.zoom",null),Ln(t.view,i.moved),ix(t),i.event(t).end()}}function T(t,...n){if(r.apply(this,arguments)){var e=this.__zoom,a=In(t.changedTouches?t.changedTouches[0]:t,this),u=e.invert(a),c=e.k*(t.shiftKey?.5:2),l=o(_(v(e,c),a,u),i.apply(this,n),f);ix(t),s>0?Dn(this).transition().duration(s).call(m,l,a,t):Dn(this).call(y.transform,l,a,t)}}function S(e,...i){if(r.apply(this,arguments)){var o,a,u,c,f=e.touches,s=f.length,l=x(this,i,e.changedTouches.length===s).event(e);for(rx(e),a=0;a+~]|"+M+")"+M+"*"),U=new RegExp(M+"|>"),X=new RegExp(F),V=new RegExp("^"+I+"$"),G={ID:new RegExp("^#("+I+")"),CLASS:new RegExp("^\\.("+I+")"),TAG:new RegExp("^("+I+"|[*])"),ATTR:new RegExp("^"+W),PSEUDO:new RegExp("^"+F),CHILD:new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+M+"*(even|odd|(([+-]|)(\\d*)n|)"+M+"*(?:([+-]|)"+M+"*(\\d+)|))"+M+"*\\)|)","i"),bool:new RegExp("^(?:"+R+")$","i"),needsContext:new RegExp("^"+M+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+M+"*((?:-\\d)?\\d*)"+M+"*\\)|)(?=[^-]|$)","i")},Y=/HTML$/i,Q=/^(?:input|select|textarea|button)$/i,J=/^h\d$/i,K=/^[^{]+\{\s*\[native \w/,Z=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,ee=/[+~]/,te=new RegExp("\\\\[\\da-fA-F]{1,6}"+M+"?|\\\\([^\\r\\n\\f])","g"),ne=function(e,t){var n="0x"+e.slice(1)-65536;return t||(n<0?String.fromCharCode(n+65536):String.fromCharCode(n>>10|55296,1023&n|56320))},re=/([\0-\x1f\x7f]|^-?\d)|^-$|[^\0-\x1f\x7f-\uFFFF\w-]/g,ie=function(e,t){return t?"\0"===e?"\ufffd":e.slice(0,-1)+"\\"+e.charCodeAt(e.length-1).toString(16)+" ":"\\"+e},oe=function(){T()},ae=be(function(e){return!0===e.disabled&&"fieldset"===e.nodeName.toLowerCase()},{dir:"parentNode",next:"legend"});try{H.apply(t=O.call(p.childNodes),p.childNodes),t[p.childNodes.length].nodeType}catch(e){H={apply:t.length?function(e,t){L.apply(e,O.call(t))}:function(e,t){var n=e.length,r=0;while(e[n++]=t[r++]);e.length=n-1}}}function se(t,e,n,r){var i,o,a,s,u,l,c,f=e&&e.ownerDocument,p=e?e.nodeType:9;if(n=n||[],"string"!=typeof t||!t||1!==p&&9!==p&&11!==p)return n;if(!r&&(T(e),e=e||C,E)){if(11!==p&&(u=Z.exec(t)))if(i=u[1]){if(9===p){if(!(a=e.getElementById(i)))return n;if(a.id===i)return n.push(a),n}else if(f&&(a=f.getElementById(i))&&y(e,a)&&a.id===i)return n.push(a),n}else{if(u[2])return H.apply(n,e.getElementsByTagName(t)),n;if((i=u[3])&&d.getElementsByClassName&&e.getElementsByClassName)return H.apply(n,e.getElementsByClassName(i)),n}if(d.qsa&&!N[t+" "]&&(!v||!v.test(t))&&(1!==p||"object"!==e.nodeName.toLowerCase())){if(c=t,f=e,1===p&&(U.test(t)||z.test(t))){(f=ee.test(t)&&ye(e.parentNode)||e)===e&&d.scope||((s=e.getAttribute("id"))?s=s.replace(re,ie):e.setAttribute("id",s=S)),o=(l=h(t)).length;while(o--)l[o]=(s?"#"+s:":scope")+" "+xe(l[o]);c=l.join(",")}try{return H.apply(n,f.querySelectorAll(c)),n}catch(e){N(t,!0)}finally{s===S&&e.removeAttribute("id")}}}return g(t.replace($,"$1"),e,n,r)}function ue(){var r=[];return function e(t,n){return r.push(t+" ")>b.cacheLength&&delete e[r.shift()],e[t+" "]=n}}function le(e){return e[S]=!0,e}function ce(e){var t=C.createElement("fieldset");try{return!!e(t)}catch(e){return!1}finally{t.parentNode&&t.parentNode.removeChild(t),t=null}}function fe(e,t){var n=e.split("|"),r=n.length;while(r--)b.attrHandle[n[r]]=t}function pe(e,t){var n=t&&e,r=n&&1===e.nodeType&&1===t.nodeType&&e.sourceIndex-t.sourceIndex;if(r)return r;if(n)while(n=n.nextSibling)if(n===t)return-1;return e?1:-1}function de(t){return function(e){return"input"===e.nodeName.toLowerCase()&&e.type===t}}function he(n){return function(e){var t=e.nodeName.toLowerCase();return("input"===t||"button"===t)&&e.type===n}}function ge(t){return function(e){return"form"in e?e.parentNode&&!1===e.disabled?"label"in e?"label"in e.parentNode?e.parentNode.disabled===t:e.disabled===t:e.isDisabled===t||e.isDisabled!==!t&&ae(e)===t:e.disabled===t:"label"in e&&e.disabled===t}}function ve(a){return le(function(o){return o=+o,le(function(e,t){var n,r=a([],e.length,o),i=r.length;while(i--)e[n=r[i]]&&(e[n]=!(t[n]=e[n]))})})}function ye(e){return e&&"undefined"!=typeof e.getElementsByTagName&&e}for(e in d=se.support={},i=se.isXML=function(e){var t=e.namespaceURI,n=(e.ownerDocument||e).documentElement;return!Y.test(t||n&&n.nodeName||"HTML")},T=se.setDocument=function(e){var t,n,r=e?e.ownerDocument||e:p;return r!=C&&9===r.nodeType&&r.documentElement&&(a=(C=r).documentElement,E=!i(C),p!=C&&(n=C.defaultView)&&n.top!==n&&(n.addEventListener?n.addEventListener("unload",oe,!1):n.attachEvent&&n.attachEvent("onunload",oe)),d.scope=ce(function(e){return a.appendChild(e).appendChild(C.createElement("div")),"undefined"!=typeof e.querySelectorAll&&!e.querySelectorAll(":scope fieldset div").length}),d.attributes=ce(function(e){return e.className="i",!e.getAttribute("className")}),d.getElementsByTagName=ce(function(e){return e.appendChild(C.createComment("")),!e.getElementsByTagName("*").length}),d.getElementsByClassName=K.test(C.getElementsByClassName),d.getById=ce(function(e){return a.appendChild(e).id=S,!C.getElementsByName||!C.getElementsByName(S).length}),d.getById?(b.filter.ID=function(e){var t=e.replace(te,ne);return function(e){return e.getAttribute("id")===t}},b.find.ID=function(e,t){if("undefined"!=typeof t.getElementById&&E){var n=t.getElementById(e);return n?[n]:[]}}):(b.filter.ID=function(e){var n=e.replace(te,ne);return function(e){var t="undefined"!=typeof e.getAttributeNode&&e.getAttributeNode("id");return t&&t.value===n}},b.find.ID=function(e,t){if("undefined"!=typeof t.getElementById&&E){var n,r,i,o=t.getElementById(e);if(o){if((n=o.getAttributeNode("id"))&&n.value===e)return[o];i=t.getElementsByName(e),r=0;while(o=i[r++])if((n=o.getAttributeNode("id"))&&n.value===e)return[o]}return[]}}),b.find.TAG=d.getElementsByTagName?function(e,t){return"undefined"!=typeof t.getElementsByTagName?t.getElementsByTagName(e):d.qsa?t.querySelectorAll(e):void 0}:function(e,t){var n,r=[],i=0,o=t.getElementsByTagName(e);if("*"===e){while(n=o[i++])1===n.nodeType&&r.push(n);return r}return o},b.find.CLASS=d.getElementsByClassName&&function(e,t){if("undefined"!=typeof t.getElementsByClassName&&E)return t.getElementsByClassName(e)},s=[],v=[],(d.qsa=K.test(C.querySelectorAll))&&(ce(function(e){var t;a.appendChild(e).innerHTML="",e.querySelectorAll("[msallowcapture^='']").length&&v.push("[*^$]="+M+"*(?:''|\"\")"),e.querySelectorAll("[selected]").length||v.push("\\["+M+"*(?:value|"+R+")"),e.querySelectorAll("[id~="+S+"-]").length||v.push("~="),(t=C.createElement("input")).setAttribute("name",""),e.appendChild(t),e.querySelectorAll("[name='']").length||v.push("\\["+M+"*name"+M+"*="+M+"*(?:''|\"\")"),e.querySelectorAll(":checked").length||v.push(":checked"),e.querySelectorAll("a#"+S+"+*").length||v.push(".#.+[+~]"),e.querySelectorAll("\\\f"),v.push("[\\r\\n\\f]")}),ce(function(e){e.innerHTML="";var t=C.createElement("input");t.setAttribute("type","hidden"),e.appendChild(t).setAttribute("name","D"),e.querySelectorAll("[name=d]").length&&v.push("name"+M+"*[*^$|!~]?="),2!==e.querySelectorAll(":enabled").length&&v.push(":enabled",":disabled"),a.appendChild(e).disabled=!0,2!==e.querySelectorAll(":disabled").length&&v.push(":enabled",":disabled"),e.querySelectorAll("*,:x"),v.push(",.*:")})),(d.matchesSelector=K.test(c=a.matches||a.webkitMatchesSelector||a.mozMatchesSelector||a.oMatchesSelector||a.msMatchesSelector))&&ce(function(e){d.disconnectedMatch=c.call(e,"*"),c.call(e,"[s!='']:x"),s.push("!=",F)}),v=v.length&&new RegExp(v.join("|")),s=s.length&&new RegExp(s.join("|")),t=K.test(a.compareDocumentPosition),y=t||K.test(a.contains)?function(e,t){var n=9===e.nodeType?e.documentElement:e,r=t&&t.parentNode;return e===r||!(!r||1!==r.nodeType||!(n.contains?n.contains(r):e.compareDocumentPosition&&16&e.compareDocumentPosition(r)))}:function(e,t){if(t)while(t=t.parentNode)if(t===e)return!0;return!1},D=t?function(e,t){if(e===t)return l=!0,0;var n=!e.compareDocumentPosition-!t.compareDocumentPosition;return n||(1&(n=(e.ownerDocument||e)==(t.ownerDocument||t)?e.compareDocumentPosition(t):1)||!d.sortDetached&&t.compareDocumentPosition(e)===n?e==C||e.ownerDocument==p&&y(p,e)?-1:t==C||t.ownerDocument==p&&y(p,t)?1:u?P(u,e)-P(u,t):0:4&n?-1:1)}:function(e,t){if(e===t)return l=!0,0;var n,r=0,i=e.parentNode,o=t.parentNode,a=[e],s=[t];if(!i||!o)return e==C?-1:t==C?1:i?-1:o?1:u?P(u,e)-P(u,t):0;if(i===o)return pe(e,t);n=e;while(n=n.parentNode)a.unshift(n);n=t;while(n=n.parentNode)s.unshift(n);while(a[r]===s[r])r++;return r?pe(a[r],s[r]):a[r]==p?-1:s[r]==p?1:0}),C},se.matches=function(e,t){return se(e,null,null,t)},se.matchesSelector=function(e,t){if(T(e),d.matchesSelector&&E&&!N[t+" "]&&(!s||!s.test(t))&&(!v||!v.test(t)))try{var n=c.call(e,t);if(n||d.disconnectedMatch||e.document&&11!==e.document.nodeType)return n}catch(e){N(t,!0)}return 0":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(e){return e[1]=e[1].replace(te,ne),e[3]=(e[3]||e[4]||e[5]||"").replace(te,ne),"~="===e[2]&&(e[3]=" "+e[3]+" "),e.slice(0,4)},CHILD:function(e){return e[1]=e[1].toLowerCase(),"nth"===e[1].slice(0,3)?(e[3]||se.error(e[0]),e[4]=+(e[4]?e[5]+(e[6]||1):2*("even"===e[3]||"odd"===e[3])),e[5]=+(e[7]+e[8]||"odd"===e[3])):e[3]&&se.error(e[0]),e},PSEUDO:function(e){var t,n=!e[6]&&e[2];return G.CHILD.test(e[0])?null:(e[3]?e[2]=e[4]||e[5]||"":n&&X.test(n)&&(t=h(n,!0))&&(t=n.indexOf(")",n.length-t)-n.length)&&(e[0]=e[0].slice(0,t),e[2]=n.slice(0,t)),e.slice(0,3))}},filter:{TAG:function(e){var t=e.replace(te,ne).toLowerCase();return"*"===e?function(){return!0}:function(e){return e.nodeName&&e.nodeName.toLowerCase()===t}},CLASS:function(e){var t=m[e+" "];return t||(t=new RegExp("(^|"+M+")"+e+"("+M+"|$)"))&&m(e,function(e){return t.test("string"==typeof e.className&&e.className||"undefined"!=typeof e.getAttribute&&e.getAttribute("class")||"")})},ATTR:function(n,r,i){return function(e){var t=se.attr(e,n);return null==t?"!="===r:!r||(t+="","="===r?t===i:"!="===r?t!==i:"^="===r?i&&0===t.indexOf(i):"*="===r?i&&-1:\x20\t\r\n\f]*)[\x20\t\r\n\f]*\/?>(?:<\/\1>|)$/i;function D(e,n,r){return m(n)?S.grep(e,function(e,t){return!!n.call(e,t,e)!==r}):n.nodeType?S.grep(e,function(e){return e===n!==r}):"string"!=typeof n?S.grep(e,function(e){return-1)[^>]*|#([\w-]+))$/;(S.fn.init=function(e,t,n){var r,i;if(!e)return this;if(n=n||j,"string"==typeof e){if(!(r="<"===e[0]&&">"===e[e.length-1]&&3<=e.length?[null,e,null]:q.exec(e))||!r[1]&&t)return!t||t.jquery?(t||n).find(e):this.constructor(t).find(e);if(r[1]){if(t=t instanceof S?t[0]:t,S.merge(this,S.parseHTML(r[1],t&&t.nodeType?t.ownerDocument||t:E,!0)),N.test(r[1])&&S.isPlainObject(t))for(r in t)m(this[r])?this[r](t[r]):this.attr(r,t[r]);return this}return(i=E.getElementById(r[2]))&&(this[0]=i,this.length=1),this}return e.nodeType?(this[0]=e,this.length=1,this):m(e)?void 0!==n.ready?n.ready(e):e(S):S.makeArray(e,this)}).prototype=S.fn,j=S(E);var L=/^(?:parents|prev(?:Until|All))/,H={children:!0,contents:!0,next:!0,prev:!0};function O(e,t){while((e=e[t])&&1!==e.nodeType);return e}S.fn.extend({has:function(e){var t=S(e,this),n=t.length;return this.filter(function(){for(var e=0;e\x20\t\r\n\f]*)/i,he=/^$|^module$|\/(?:java|ecma)script/i;ce=E.createDocumentFragment().appendChild(E.createElement("div")),(fe=E.createElement("input")).setAttribute("type","radio"),fe.setAttribute("checked","checked"),fe.setAttribute("name","t"),ce.appendChild(fe),y.checkClone=ce.cloneNode(!0).cloneNode(!0).lastChild.checked,ce.innerHTML="",y.noCloneChecked=!!ce.cloneNode(!0).lastChild.defaultValue,ce.innerHTML="",y.option=!!ce.lastChild;var ge={thead:[1,"","
"],col:[2,"","
"],tr:[2,"","
"],td:[3,"","
"],_default:[0,"",""]};function ve(e,t){var n;return n="undefined"!=typeof e.getElementsByTagName?e.getElementsByTagName(t||"*"):"undefined"!=typeof e.querySelectorAll?e.querySelectorAll(t||"*"):[],void 0===t||t&&A(e,t)?S.merge([e],n):n}function ye(e,t){for(var n=0,r=e.length;n",""]);var me=/<|&#?\w+;/;function xe(e,t,n,r,i){for(var o,a,s,u,l,c,f=t.createDocumentFragment(),p=[],d=0,h=e.length;d\s*$/g;function qe(e,t){return A(e,"table")&&A(11!==t.nodeType?t:t.firstChild,"tr")&&S(e).children("tbody")[0]||e}function Le(e){return e.type=(null!==e.getAttribute("type"))+"/"+e.type,e}function He(e){return"true/"===(e.type||"").slice(0,5)?e.type=e.type.slice(5):e.removeAttribute("type"),e}function Oe(e,t){var n,r,i,o,a,s;if(1===t.nodeType){if(Y.hasData(e)&&(s=Y.get(e).events))for(i in Y.remove(t,"handle events"),s)for(n=0,r=s[i].length;n").attr(n.scriptAttrs||{}).prop({charset:n.scriptCharset,src:n.url}).on("load error",i=function(e){r.remove(),i=null,e&&t("error"===e.type?404:200,e.type)}),E.head.appendChild(r[0])},abort:function(){i&&i()}}});var Ut,Xt=[],Vt=/(=)\?(?=&|$)|\?\?/;S.ajaxSetup({jsonp:"callback",jsonpCallback:function(){var e=Xt.pop()||S.expando+"_"+Ct.guid++;return this[e]=!0,e}}),S.ajaxPrefilter("json jsonp",function(e,t,n){var r,i,o,a=!1!==e.jsonp&&(Vt.test(e.url)?"url":"string"==typeof e.data&&0===(e.contentType||"").indexOf("application/x-www-form-urlencoded")&&Vt.test(e.data)&&"data");if(a||"jsonp"===e.dataTypes[0])return r=e.jsonpCallback=m(e.jsonpCallback)?e.jsonpCallback():e.jsonpCallback,a?e[a]=e[a].replace(Vt,"$1"+r):!1!==e.jsonp&&(e.url+=(Et.test(e.url)?"&":"?")+e.jsonp+"="+r),e.converters["script json"]=function(){return o||S.error(r+" was not called"),o[0]},e.dataTypes[0]="json",i=C[r],C[r]=function(){o=arguments},n.always(function(){void 0===i?S(C).removeProp(r):C[r]=i,e[r]&&(e.jsonpCallback=t.jsonpCallback,Xt.push(r)),o&&m(i)&&i(o[0]),o=i=void 0}),"script"}),y.createHTMLDocument=((Ut=E.implementation.createHTMLDocument("").body).innerHTML="
",2===Ut.childNodes.length),S.parseHTML=function(e,t,n){return"string"!=typeof e?[]:("boolean"==typeof t&&(n=t,t=!1),t||(y.createHTMLDocument?((r=(t=E.implementation.createHTMLDocument("")).createElement("base")).href=E.location.href,t.head.appendChild(r)):t=E),o=!n&&[],(i=N.exec(e))?[t.createElement(i[1])]:(i=xe([e],t,o),o&&o.length&&S(o).remove(),S.merge([],i.childNodes)));var r,i,o},S.fn.load=function(e,t,n){var r,i,o,a=this,s=e.indexOf(" ");return-1").append(S.parseHTML(e)).find(r):e)}).always(n&&function(e,t){a.each(function(){n.apply(this,o||[e.responseText,t,e])})}),this},S.expr.pseudos.animated=function(t){return S.grep(S.timers,function(e){return t===e.elem}).length},S.offset={setOffset:function(e,t,n){var r,i,o,a,s,u,l=S.css(e,"position"),c=S(e),f={};"static"===l&&(e.style.position="relative"),s=c.offset(),o=S.css(e,"top"),u=S.css(e,"left"),("absolute"===l||"fixed"===l)&&-1<(o+u).indexOf("auto")?(a=(r=c.position()).top,i=r.left):(a=parseFloat(o)||0,i=parseFloat(u)||0),m(t)&&(t=t.call(e,n,S.extend({},s))),null!=t.top&&(f.top=t.top-s.top+a),null!=t.left&&(f.left=t.left-s.left+i),"using"in t?t.using.call(e,f):("number"==typeof f.top&&(f.top+="px"),"number"==typeof f.left&&(f.left+="px"),c.css(f))}},S.fn.extend({offset:function(t){if(arguments.length)return void 0===t?this:this.each(function(e){S.offset.setOffset(this,t,e)});var e,n,r=this[0];return r?r.getClientRects().length?(e=r.getBoundingClientRect(),n=r.ownerDocument.defaultView,{top:e.top+n.pageYOffset,left:e.left+n.pageXOffset}):{top:0,left:0}:void 0},position:function(){if(this[0]){var e,t,n,r=this[0],i={top:0,left:0};if("fixed"===S.css(r,"position"))t=r.getBoundingClientRect();else{t=this.offset(),n=r.ownerDocument,e=r.offsetParent||n.documentElement;while(e&&(e===n.body||e===n.documentElement)&&"static"===S.css(e,"position"))e=e.parentNode;e&&e!==r&&1===e.nodeType&&((i=S(e).offset()).top+=S.css(e,"borderTopWidth",!0),i.left+=S.css(e,"borderLeftWidth",!0))}return{top:t.top-i.top-S.css(r,"marginTop",!0),left:t.left-i.left-S.css(r,"marginLeft",!0)}}},offsetParent:function(){return this.map(function(){var e=this.offsetParent;while(e&&"static"===S.css(e,"position"))e=e.offsetParent;return e||re})}}),S.each({scrollLeft:"pageXOffset",scrollTop:"pageYOffset"},function(t,i){var o="pageYOffset"===i;S.fn[t]=function(e){return $(this,function(e,t,n){var r;if(x(e)?r=e:9===e.nodeType&&(r=e.defaultView),void 0===n)return r?r[i]:e[t];r?r.scrollTo(o?r.pageXOffset:n,o?n:r.pageYOffset):e[t]=n},t,e,arguments.length)}}),S.each(["top","left"],function(e,n){S.cssHooks[n]=$e(y.pixelPosition,function(e,t){if(t)return t=Be(e,n),Me.test(t)?S(e).position()[n]+"px":t})}),S.each({Height:"height",Width:"width"},function(a,s){S.each({padding:"inner"+a,content:s,"":"outer"+a},function(r,o){S.fn[o]=function(e,t){var n=arguments.length&&(r||"boolean"!=typeof e),i=r||(!0===e||!0===t?"margin":"border");return $(this,function(e,t,n){var r;return x(e)?0===o.indexOf("outer")?e["inner"+a]:e.document.documentElement["client"+a]:9===e.nodeType?(r=e.documentElement,Math.max(e.body["scroll"+a],r["scroll"+a],e.body["offset"+a],r["offset"+a],r["client"+a])):void 0===n?S.css(e,t,i):S.style(e,t,n,i)},s,n?e:void 0,n)}})}),S.each(["ajaxStart","ajaxStop","ajaxComplete","ajaxError","ajaxSuccess","ajaxSend"],function(e,t){S.fn[t]=function(e){return this.on(t,e)}}),S.fn.extend({bind:function(e,t,n){return this.on(e,null,t,n)},unbind:function(e,t){return this.off(e,null,t)},delegate:function(e,t,n,r){return this.on(t,e,n,r)},undelegate:function(e,t,n){return 1===arguments.length?this.off(e,"**"):this.off(t,e||"**",n)},hover:function(e,t){return this.mouseenter(e).mouseleave(t||e)}}),S.each("blur focus focusin focusout resize scroll click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select submit keydown keypress keyup contextmenu".split(" "),function(e,n){S.fn[n]=function(e,t){return 0>2],r=(3&f)<<4,u=1;break;case 1:s[o++]=h[r|f>>4],r=(15&f)<<2,u=2;break;case 2:s[o++]=h[r|f>>6],s[o++]=h[63&f],u=0}8191>4,r=u,s=2;break;case 2:i[n++]=(15&r)<<4|(60&u)>>2,r=u,s=3;break;case 3:i[n++]=(3&r)<<6|u,s=0}}if(1===s)throw Error(a);return n-e},n.test=function(t){return/^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$/.test(t)}},{}],3:[function(t,i,n){function c(i,n){"string"==typeof i&&(n=i,i=Q);var f=[];function h(t){if("string"!=typeof t){var i=a();if(c.verbose&&console.log("codegen: "+i),i="return "+i,t){for(var n=Object.keys(t),r=Array(n.length+1),e=Array(n.length),s=0;s>>0:i<11754943508222875e-54?(e<<31|Math.round(i/1401298464324817e-60))>>>0:(e<<31|127+(e=Math.floor(Math.log(i)/Math.LN2))<<23|8388607&Math.round(i*Math.pow(2,-e)*8388608))>>>0,n,r)}function n(t,i,n){t=t(i,n),i=2*(t>>31)+1,n=t>>>23&255,t&=8388607;return 255==n?t?NaN:1/0*i:0==n?1401298464324817e-60*i*t:i*Math.pow(2,n-150)*(8388608+t)}function r(t,i,n){u[0]=t,i[n]=f[0],i[n+1]=f[1],i[n+2]=f[2],i[n+3]=f[3]}function e(t,i,n){u[0]=t,i[n]=f[3],i[n+1]=f[2],i[n+2]=f[1],i[n+3]=f[0]}function s(t,i){return f[0]=t[i],f[1]=t[i+1],f[2]=t[i+2],f[3]=t[i+3],u[0]}function o(t,i){return f[3]=t[i],f[2]=t[i+1],f[1]=t[i+2],f[0]=t[i+3],u[0]}var u,f,h,a,c;function l(t,i,n,r,e,s){var o,u=r<0?1:0;0===(r=u?-r:r)?(t(0,e,s+i),t(0<1/r?0:2147483648,e,s+n)):isNaN(r)?(t(0,e,s+i),t(2146959360,e,s+n)):17976931348623157e292>>0,e,s+n)):r<22250738585072014e-324?(t((o=r/5e-324)>>>0,e,s+i),t((u<<31|o/4294967296)>>>0,e,s+n)):(t(4503599627370496*(o=r*Math.pow(2,-(r=1024===(r=Math.floor(Math.log(r)/Math.LN2))?1023:r)))>>>0,e,s+i),t((u<<31|r+1023<<20|1048576*o&1048575)>>>0,e,s+n))}function v(t,i,n,r,e){i=t(r,e+i),r=t(r,e+n),e=2*(r>>31)+1,n=r>>>20&2047,i=4294967296*(1048575&r)+i;return 2047==n?i?NaN:1/0*e:0==n?5e-324*e*i:e*Math.pow(2,n-1075)*(i+4503599627370496)}function d(t,i,n){h[0]=t,i[n]=a[0],i[n+1]=a[1],i[n+2]=a[2],i[n+3]=a[3],i[n+4]=a[4],i[n+5]=a[5],i[n+6]=a[6],i[n+7]=a[7]}function p(t,i,n){h[0]=t,i[n]=a[7],i[n+1]=a[6],i[n+2]=a[5],i[n+3]=a[4],i[n+4]=a[3],i[n+5]=a[2],i[n+6]=a[1],i[n+7]=a[0]}function b(t,i){return a[0]=t[i],a[1]=t[i+1],a[2]=t[i+2],a[3]=t[i+3],a[4]=t[i+4],a[5]=t[i+5],a[6]=t[i+6],a[7]=t[i+7],h[0]}function w(t,i){return a[7]=t[i],a[6]=t[i+1],a[5]=t[i+2],a[4]=t[i+3],a[3]=t[i+4],a[2]=t[i+5],a[1]=t[i+6],a[0]=t[i+7],h[0]}return"undefined"!=typeof Float32Array?(u=new Float32Array([-0]),f=new Uint8Array(u.buffer),c=128===f[3],t.writeFloatLE=c?r:e,t.writeFloatBE=c?e:r,t.readFloatLE=c?s:o,t.readFloatBE=c?o:s):(t.writeFloatLE=i.bind(null,y),t.writeFloatBE=i.bind(null,m),t.readFloatLE=n.bind(null,g),t.readFloatBE=n.bind(null,j)),"undefined"!=typeof Float64Array?(h=new Float64Array([-0]),a=new Uint8Array(h.buffer),c=128===a[7],t.writeDoubleLE=c?d:p,t.writeDoubleBE=c?p:d,t.readDoubleLE=c?b:w,t.readDoubleBE=c?w:b):(t.writeDoubleLE=l.bind(null,y,0,4),t.writeDoubleBE=l.bind(null,m,4,0),t.readDoubleLE=v.bind(null,g,0,4),t.readDoubleBE=v.bind(null,j,4,0)),t}function y(t,i,n){i[n]=255&t,i[n+1]=t>>>8&255,i[n+2]=t>>>16&255,i[n+3]=t>>>24}function m(t,i,n){i[n]=t>>>24,i[n+1]=t>>>16&255,i[n+2]=t>>>8&255,i[n+3]=255&t}function g(t,i){return(t[i]|t[i+1]<<8|t[i+2]<<16|t[i+3]<<24)>>>0}function j(t,i){return(t[i]<<24|t[i+1]<<16|t[i+2]<<8|t[i+3])>>>0}i.exports=r(r)},{}],7:[function(t,i,n){function r(t){try{var i=eval("require")(t);if(i&&(i.length||Object.keys(i).length))return i}catch(t){}return null}i.exports=r},{}],8:[function(t,i,n){var n=n,e=n.isAbsolute=function(t){return/^(?:\/|\w+:)/.test(t)},r=n.normalize=function(t){var i=(t=t.replace(/\\/g,"/").replace(/\/{2,}/g,"/")).split("/"),n=e(t),t="";n&&(t=i.shift()+"/");for(var r=0;r>>1,s=null,o=r;return function(t){if(t<1||e>10),s[o++]=56320+(1023&r)):s[o++]=(15&r)<<12|(63&t[i++])<<6|63&t[i++],8191>6|192:(55296==(64512&r)&&56320==(64512&(e=t.charCodeAt(o+1)))?(++o,i[n++]=(r=65536+((1023&r)<<10)+(1023&e))>>18|240,i[n++]=r>>12&63|128):i[n++]=r>>12|224,i[n++]=r>>6&63|128),i[n++]=63&r|128);return n-s}},{}],11:[function(t,i,n){i.exports=e;var r=/\/|\./;function e(t,i){r.test(t)||(t="google/protobuf/"+t+".proto",i={nested:{google:{nested:{protobuf:{nested:i}}}}}),e[t]=i}e("any",{Any:{fields:{type_url:{type:"string",id:1},value:{type:"bytes",id:2}}}}),e("duration",{Duration:i={fields:{seconds:{type:"int64",id:1},nanos:{type:"int32",id:2}}}}),e("timestamp",{Timestamp:i}),e("empty",{Empty:{fields:{}}}),e("struct",{Struct:{fields:{fields:{keyType:"string",type:"Value",id:1}}},Value:{oneofs:{kind:{oneof:["nullValue","numberValue","stringValue","boolValue","structValue","listValue"]}},fields:{nullValue:{type:"NullValue",id:1},numberValue:{type:"double",id:2},stringValue:{type:"string",id:3},boolValue:{type:"bool",id:4},structValue:{type:"Struct",id:5},listValue:{type:"ListValue",id:6}}},NullValue:{values:{NULL_VALUE:0}},ListValue:{fields:{values:{rule:"repeated",type:"Value",id:1}}}}),e("wrappers",{DoubleValue:{fields:{value:{type:"double",id:1}}},FloatValue:{fields:{value:{type:"float",id:1}}},Int64Value:{fields:{value:{type:"int64",id:1}}},UInt64Value:{fields:{value:{type:"uint64",id:1}}},Int32Value:{fields:{value:{type:"int32",id:1}}},UInt32Value:{fields:{value:{type:"uint32",id:1}}},BoolValue:{fields:{value:{type:"bool",id:1}}},StringValue:{fields:{value:{type:"string",id:1}}},BytesValue:{fields:{value:{type:"bytes",id:1}}}}),e("field_mask",{FieldMask:{fields:{paths:{rule:"repeated",type:"string",id:1}}}}),e.get=function(t){return e[t]||null}},{}],12:[function(t,i,n){var n=n,l=t(15),v=t(37);function o(t,i,n,r){if(i.resolvedType)if(i.resolvedType instanceof l){t("switch(d%s){",r);for(var e=i.resolvedType.values,s=Object.keys(e),o=0;o>>0",r,r);break;case"int32":case"sint32":case"sfixed32":t("m%s=d%s|0",r,r);break;case"uint64":u=!0;case"int64":case"sint64":case"fixed64":case"sfixed64":t("if(util.Long)")("(m%s=util.Long.fromValue(d%s)).unsigned=%j",r,r,u)('else if(typeof d%s==="string")',r)("m%s=parseInt(d%s,10)",r,r)('else if(typeof d%s==="number")',r)("m%s=d%s",r,r)('else if(typeof d%s==="object")',r)("m%s=new util.LongBits(d%s.low>>>0,d%s.high>>>0).toNumber(%s)",r,r,r,u?"true":"");break;case"bytes":t('if(typeof d%s==="string")',r)("util.base64.decode(d%s,m%s=util.newBuffer(util.base64.length(d%s)),0)",r,r,r)("else if(d%s.length)",r)("m%s=d%s",r,r);break;case"string":t("m%s=String(d%s)",r,r);break;case"bool":t("m%s=Boolean(d%s)",r,r)}}return t}function d(t,i,n,r){if(i.resolvedType)i.resolvedType instanceof l?t("d%s=o.enums===String?types[%i].values[m%s]:m%s",r,n,r,r):t("d%s=types[%i].toObject(m%s,o)",r,n,r);else{var e=!1;switch(i.type){case"double":case"float":t("d%s=o.json&&!isFinite(m%s)?String(m%s):m%s",r,r,r,r);break;case"uint64":e=!0;case"int64":case"sint64":case"fixed64":case"sfixed64":t('if(typeof m%s==="number")',r)("d%s=o.longs===String?String(m%s):m%s",r,r,r)("else")("d%s=o.longs===String?util.Long.prototype.toString.call(m%s):o.longs===Number?new util.LongBits(m%s.low>>>0,m%s.high>>>0).toNumber(%s):m%s",r,r,r,r,e?"true":"",r);break;case"bytes":t("d%s=o.bytes===String?util.base64.encode(m%s,0,m%s.length):o.bytes===Array?Array.prototype.slice.call(m%s):m%s",r,r,r,r,r);break;default:t("d%s=m%s",r,r)}}return t}n.fromObject=function(t){var i=t.fieldsArray,n=v.codegen(["d"],t.name+"$fromObject")("if(d instanceof this.ctor)")("return d");if(!i.length)return n("return new this.ctor");n("var m=new this.ctor");for(var r=0;r>>3){");for(var n=0;n>>3){")("case 1: k=r.%s(); break",r.keyType)("case 2:"),f.basic[e]===Q?i("value=types[%i].decode(r,r.uint32())",n):i("value=r.%s()",e),i("break")("default:")("r.skipType(tag2&7)")("break")("}")("}"),f.long[r.keyType]!==Q?i('%s[typeof k==="object"?util.longToHash(k):k]=value',s):i("%s[k]=value",s)):r.repeated?(i("if(!(%s&&%s.length))",s,s)("%s=[]",s),f.packed[e]!==Q&&i("if((t&7)===2){")("var c2=r.uint32()+r.pos")("while(r.pos>>0,8|a.mapKey[s.keyType],s.keyType),f===Q?n("types[%i].encode(%s[ks[i]],w.uint32(18).fork()).ldelim().ldelim()",o,i):n(".uint32(%i).%s(%s[ks[i]]).ldelim()",16|f,u,i),n("}")("}")):s.repeated?(n("if(%s!=null&&%s.length){",i,i),s.packed&&a.packed[u]!==Q?n("w.uint32(%i).fork()",(s.id<<3|2)>>>0)("for(var i=0;i<%s.length;++i)",i)("w.%s(%s[i])",u,i)("w.ldelim()"):(n("for(var i=0;i<%s.length;++i)",i),f===Q?l(n,s,o,i+"[i]"):n("w.uint32(%i).%s(%s[i])",(s.id<<3|f)>>>0,u,i)),n("}")):(s.optional&&n("if(%s!=null&&Object.hasOwnProperty.call(m,%j))",i,s.name),f===Q?l(n,s,o,i):n("w.uint32(%i).%s(%s)",(s.id<<3|f)>>>0,u,i))}return n("return w")};var h=t(15),a=t(36),c=t(37);function l(t,i,n,r){return i.resolvedType.group?t("types[%i].encode(%s,w.uint32(%i)).uint32(%i)",n,r,(i.id<<3|3)>>>0,(i.id<<3|4)>>>0):t("types[%i].encode(%s,w.uint32(%i).fork()).ldelim()",n,r,(i.id<<3|2)>>>0)}},{15:15,36:36,37:37}],15:[function(t,i,n){i.exports=s;var u=t(24);((s.prototype=Object.create(u.prototype)).constructor=s).className="Enum";var r=t(23),e=t(37);function s(t,i,n,r,e){if(u.call(this,t,n),i&&"object"!=typeof i)throw TypeError("values must be an object");if(this.valuesById={},this.values=Object.create(this.valuesById),this.comment=r,this.comments=e||{},this.reserved=Q,i)for(var s=Object.keys(i),o=0;oi)return!0;return!1},a.isReservedName=function(t,i){if(t)for(var n=0;n");var r=a();if(!J.test(r))throw m(r,"name");v("=");var e=new I(y(r),O(a()),i,n);A(e,function(t){if("option"!==t)throw m(t);T(e,t),v(";")},function(){N(e)}),t.add(e)}(n);break;case"required":case"repeated":x(n,t);break;case"optional":x(n,b?"proto3_optional":"optional");break;case"oneof":!function(t,i){if(!J.test(i=a()))throw m(i,"name");var n=new F(y(i));A(n,function(t){"option"===t?(T(n,t),v(";")):(c(t),x(n,"optional"))}),t.add(n)}(n,t);break;case"extensions":k(n.extensions||(n.extensions=[]));break;case"reserved":k(n.reserved||(n.reserved=[]),!0);break;default:if(!b||!W.test(t))throw m(t);c(t),x(n,"optional")}}),t.add(n)}(t,i),1;case"enum":return function(t,i){if(!J.test(i=a()))throw m(i,"name");var n=new L(i);A(n,function(t){switch(t){case"option":T(n,t),v(";");break;case"reserved":k(n.reserved||(n.reserved=[]),!0);break;default:!function(t,i){if(!J.test(i))throw m(i,"name");v("=");var n=O(a(),!0),r={};A(r,function(t){if("option"!==t)throw m(t);T(r,t),v(";")},function(){N(r)}),t.add(i,n,r.comment)}(n,t)}}),t.add(n)}(t,i),1;case"service":return function(t,i){if(!J.test(i=a()))throw m(i,"service name");var n=new U(i);A(n,function(t){if(!E(n,t)){if("rpc"!==t)throw m(t);!function(t,i){var n=d(),r=i;if(!J.test(i=a()))throw m(i,"name");var e,s,o,u=i;v("("),v("stream",!0)&&(s=!0);if(!W.test(i=a()))throw m(i);e=i,v(")"),v("returns"),v("("),v("stream",!0)&&(o=!0);if(!W.test(i=a()))throw m(i);i=i,v(")");var f=new q(u,r,e,i,s,o);f.comment=n,A(f,function(t){if("option"!==t)throw m(t);T(f,t),v(";")}),t.add(f)}(n,t)}}),t.add(n)}(t,i),1;case"extend":return function(i,t){if(!W.test(t=a()))throw m(t,"reference");var n=t;A(null,function(t){switch(t){case"required":case"repeated":x(i,t,n);break;case"optional":x(i,b?"proto3_optional":"optional",n);break;default:if(!b||!W.test(t))throw m(t);c(t),x(i,"optional",n)}})}(t,i),1}}function A(t,i,n){var r,e=h.line;if(t&&("string"!=typeof t.comment&&(t.comment=d()),t.filename=K.filename),v("{",!0)){for(;"}"!==(r=a());)i(r);v(";",!0)}else n&&n(),v(";"),t&&("string"!=typeof t.comment||u)&&(t.comment=d(e)||t.comment)}function x(t,i,n){var r=a();if("group"!==r){if(!W.test(r))throw m(r,"type");var e=a();if(!J.test(e))throw m(e,"name");e=y(e),v("=");var s=new _(e,O(a()),r,i,n);A(s,function(t){if("option"!==t)throw m(t);T(s,t),v(";")},function(){N(s)}),"proto3_optional"===i?(e=new F("_"+e),s.setOption("proto3_optional",!0),e.add(s),t.add(e)):t.add(s),b||!s.repeated||R.packed[r]===Q&&R.basic[r]!==Q||s.setOption("packed",!1,!0)}else!function(t,i){var n=a();if(!J.test(n))throw m(n,"name");var r=z.lcFirst(n);n===r&&(n=z.ucFirst(n));v("=");var e=O(a()),s=new M(n);s.group=!0;i=new _(r,e,n,i);i.filename=K.filename,A(s,function(t){switch(t){case"option":T(s,t),v(";");break;case"required":case"repeated":x(s,t);break;case"optional":x(s,b?"proto3_optional":"optional");break;default:throw m(t)}}),t.add(s).add(i)}(t,i)}function T(t,i){var n=v("(",!0);if(!W.test(i=a()))throw m(i,"name");var r=i,e=r;n&&(v(")"),e=r="("+r+")",i=l(),G.test(i)&&(s=i.substr(1),r+=i,a())),v("=");var s,r=function t(i,n){if(v("{",!0)){for(var r={};!v("}",!0);){if(!J.test(f=a()))throw m(f,"name");var e,s=f;"{"===l()?e=t(i,n+"."+f):(v(":"),"{"===l()?e=t(i,n+"."+f):(e=j(!0),S(i,n+"."+f,e)));var o=r[s];o&&(e=[].concat(o).concat(e)),r[s]=e,v(",",!0)}return r}var u=j(!0);S(i,n,u);return u}(t,r);e=e,r=r,s=s,(t=t).setParsedOption&&t.setParsedOption(e,r,s)}function S(t,i,n){t.setOption&&t.setOption(i,n)}function N(t){if(v("[",!0)){for(;T(t,"option"),v(",",!0););v("]")}return t}for(;null!==(f=a());)switch(f){case"package":if(!p)throw m(f);!function(){if(r!==Q)throw m("package");if(r=a(),!W.test(r))throw m(r,"name");w=w.define(r),v(";")}();break;case"import":if(!p)throw m(f);!function(){var t,i=l();switch(i){case"weak":t=s=s||[],a();break;case"public":a();default:t=e=e||[]}i=g(),v(";"),t.push(i)}();break;case"syntax":if(!p)throw m(f);!function(){if(v("="),o=g(),!(b="proto3"===o)&&"proto2"!==o)throw m(o,"syntax");v(";")}();break;case"option":T(w,f),v(";");break;default:if(E(w,f)){p=!1;continue}throw m(f)}return K.filename=null,{package:r,imports:e,weakImports:s,syntax:o,root:i}}},{15:15,16:16,20:20,22:22,25:25,29:29,33:33,34:34,35:35,36:36,37:37}],27:[function(t,i,n){i.exports=f;var r,e=t(39),s=e.LongBits,o=e.utf8;function u(t,i){return RangeError("index out of range: "+t.pos+" + "+(i||1)+" > "+t.len)}function f(t){this.buf=t,this.pos=0,this.len=t.length}function h(){return e.Buffer?function(t){return(f.create=function(t){return e.Buffer.isBuffer(t)?new r(t):c(t)})(t)}:c}var a,c="undefined"!=typeof Uint8Array?function(t){if(t instanceof Uint8Array||Array.isArray(t))return new f(t);throw Error("illegal buffer")}:function(t){if(Array.isArray(t))return new f(t);throw Error("illegal buffer")};function l(){var t=new s(0,0),i=0;if(!(4=this.len)throw u(this);if(t.lo=(t.lo|(127&this.buf[this.pos])<<7*i)>>>0,this.buf[this.pos++]<128)return t}return t.lo=(t.lo|(127&this.buf[this.pos++])<<7*i)>>>0,t}for(;i<4;++i)if(t.lo=(t.lo|(127&this.buf[this.pos])<<7*i)>>>0,this.buf[this.pos++]<128)return t;if(t.lo=(t.lo|(127&this.buf[this.pos])<<28)>>>0,t.hi=(t.hi|(127&this.buf[this.pos])>>4)>>>0,this.buf[this.pos++]<128)return t;if(i=0,4>>0,this.buf[this.pos++]<128)return t}else for(;i<5;++i){if(this.pos>=this.len)throw u(this);if(t.hi=(t.hi|(127&this.buf[this.pos])<<7*i+3)>>>0,this.buf[this.pos++]<128)return t}throw Error("invalid varint encoding")}function v(t,i){return(t[i-4]|t[i-3]<<8|t[i-2]<<16|t[i-1]<<24)>>>0}function d(){if(this.pos+8>this.len)throw u(this,8);return new s(v(this.buf,this.pos+=4),v(this.buf,this.pos+=4))}f.create=h(),f.prototype.c=e.Array.prototype.subarray||e.Array.prototype.slice,f.prototype.uint32=(a=4294967295,function(){if(a=(127&this.buf[this.pos])>>>0,this.buf[this.pos++]<128)return a;if(a=(a|(127&this.buf[this.pos])<<7)>>>0,this.buf[this.pos++]<128)return a;if(a=(a|(127&this.buf[this.pos])<<14)>>>0,this.buf[this.pos++]<128)return a;if(a=(a|(127&this.buf[this.pos])<<21)>>>0,this.buf[this.pos++]<128)return a;if(a=(a|(15&this.buf[this.pos])<<28)>>>0,this.buf[this.pos++]<128)return a;if((this.pos+=5)>this.len)throw this.pos=this.len,u(this,10);return a}),f.prototype.int32=function(){return 0|this.uint32()},f.prototype.sint32=function(){var t=this.uint32();return t>>>1^-(1&t)|0},f.prototype.bool=function(){return 0!==this.uint32()},f.prototype.fixed32=function(){if(this.pos+4>this.len)throw u(this,4);return v(this.buf,this.pos+=4)},f.prototype.sfixed32=function(){if(this.pos+4>this.len)throw u(this,4);return 0|v(this.buf,this.pos+=4)},f.prototype.float=function(){if(this.pos+4>this.len)throw u(this,4);var t=e.float.readFloatLE(this.buf,this.pos);return this.pos+=4,t},f.prototype.double=function(){if(this.pos+8>this.len)throw u(this,4);var t=e.float.readDoubleLE(this.buf,this.pos);return this.pos+=8,t},f.prototype.bytes=function(){var t=this.uint32(),i=this.pos,n=this.pos+t;if(n>this.len)throw u(this,t);return this.pos+=t,Array.isArray(this.buf)?this.buf.slice(i,n):i===n?new this.buf.constructor(0):this.c.call(this.buf,i,n)},f.prototype.string=function(){var t=this.bytes();return o.read(t,0,t.length)},f.prototype.skip=function(t){if("number"==typeof t){if(this.pos+t>this.len)throw u(this,t);this.pos+=t}else do{if(this.pos>=this.len)throw u(this)}while(128&this.buf[this.pos++]);return this},f.prototype.skipType=function(t){switch(t){case 0:this.skip();break;case 1:this.skip(8);break;case 2:this.skip(this.uint32());break;case 3:for(;4!=(t=7&this.uint32());)this.skipType(t);break;case 5:this.skip(4);break;default:throw Error("invalid wire type "+t+" at offset "+this.pos)}return this},f.u=function(t){r=t,f.create=h(),r.u();var i=e.Long?"toLong":"toNumber";e.merge(f.prototype,{int64:function(){return l.call(this)[i](!1)},uint64:function(){return l.call(this)[i](!0)},sint64:function(){return l.call(this).zzDecode()[i](!1)},fixed64:function(){return d.call(this)[i](!0)},sfixed64:function(){return d.call(this)[i](!1)}})}},{39:39}],28:[function(t,i,n){i.exports=s;var r=t(27);(s.prototype=Object.create(r.prototype)).constructor=s;var e=t(39);function s(t){r.call(this,t)}s.u=function(){e.Buffer&&(s.prototype.c=e.Buffer.prototype.slice)},s.prototype.string=function(){var t=this.uint32();return this.buf.utf8Slice?this.buf.utf8Slice(this.pos,this.pos=Math.min(this.pos+t,this.len)):this.buf.toString("utf-8",this.pos,this.pos=Math.min(this.pos+t,this.len))},s.u()},{27:27,39:39}],29:[function(t,i,n){i.exports=f;var r=t(23);((f.prototype=Object.create(r.prototype)).constructor=f).className="Root";var e,v,d,s=t(16),o=t(15),u=t(25),p=t(37);function f(t){r.call(this,"",t),this.deferred=[],this.files=[]}function b(){}f.fromJSON=function(t,i){return i=i||new f,t.options&&i.setOptions(t.options),i.addJSON(t.nested)},f.prototype.resolvePath=p.path.resolve,f.prototype.fetch=p.fetch,f.prototype.load=function t(i,s,e){"function"==typeof s&&(e=s,s=Q);var o=this;if(!e)return p.asPromise(t,o,i,s);var u=e===b;function f(t,i){if(e){var n=e;if(e=null,u)throw t;n(t,i)}}function h(t){var i=t.lastIndexOf("google/protobuf/");if(-1]/g,x=/(?:"([^"\\]*(?:\\.[^"\\]*)*)")/g,T=/(?:'([^'\\]*(?:\\.[^'\\]*)*)')/g,S=/^ *[*/]+ */,N=/^\s*\*?\/*/,V=/\n/g,$=/\s/,r=/\\(.?)/g,e={0:"\0",r:"\r",n:"\n",t:"\t"};function M(t){return t.replace(r,function(t,i){switch(i){case"\\":case"":return i;default:return e[i]||""}})}function s(f,h){f=f.toString();var a=0,c=f.length,l=1,u=null,v=null,d=0,p=!1,b=!1,w=[],y=null;function m(t){return Error("illegal "+t+" (line "+l+")")}function g(t){return f[0|t]||""}function j(t,i,n){u=f[0|t++]||"",d=l,p=!1,b=n;var r,e=t-(h?2:3);do{if(--e<0||"\n"==(r=f[0|e]||"")){p=!0;break}}while(" "===r||"\t"===r);for(var s=f.substring(t,i).split(V),o=0;o>>0,this.hi=i>>>0}var s=e.zero=new e(0,0);s.toNumber=function(){return 0},s.zzEncode=s.zzDecode=function(){return this},s.length=function(){return 1};e.zeroHash="\0\0\0\0\0\0\0\0";e.fromNumber=function(t){if(0===t)return s;var i=t<0,n=(t=i?-t:t)>>>0,t=(t-n)/4294967296>>>0;return i&&(t=~t>>>0,n=~n>>>0,4294967295<++n&&(n=0,4294967295<++t&&(t=0))),new e(n,t)},e.from=function(t){if("number"==typeof t)return e.fromNumber(t);if(r.isString(t)){if(!r.Long)return e.fromNumber(parseInt(t,10));t=r.Long.fromString(t)}return t.low||t.high?new e(t.low>>>0,t.high>>>0):s},e.prototype.toNumber=function(t){if(!t&&this.hi>>>31){var i=1+~this.lo>>>0,t=~this.hi>>>0;return-(i+4294967296*(t=!i?t+1>>>0:t))}return this.lo+4294967296*this.hi},e.prototype.toLong=function(t){return r.Long?new r.Long(0|this.lo,0|this.hi,!!t):{low:0|this.lo,high:0|this.hi,unsigned:!!t}};var o=String.prototype.charCodeAt;e.fromHash=function(t){return"\0\0\0\0\0\0\0\0"===t?s:new e((o.call(t,0)|o.call(t,1)<<8|o.call(t,2)<<16|o.call(t,3)<<24)>>>0,(o.call(t,4)|o.call(t,5)<<8|o.call(t,6)<<16|o.call(t,7)<<24)>>>0)},e.prototype.toHash=function(){return String.fromCharCode(255&this.lo,this.lo>>>8&255,this.lo>>>16&255,this.lo>>>24,255&this.hi,this.hi>>>8&255,this.hi>>>16&255,this.hi>>>24)},e.prototype.zzEncode=function(){var t=this.hi>>31;return this.hi=((this.hi<<1|this.lo>>>31)^t)>>>0,this.lo=(this.lo<<1^t)>>>0,this},e.prototype.zzDecode=function(){var t=-(1&this.lo);return this.lo=((this.lo>>>1|this.hi<<31)^t)>>>0,this.hi=(this.hi>>>1^t)>>>0,this},e.prototype.length=function(){var t=this.lo,i=(this.lo>>>28|this.hi<<4)>>>0,n=this.hi>>>24;return 0==n?0==i?t<16384?t<128?1:2:t<2097152?3:4:i<16384?i<128?5:6:i<2097152?7:8:n<128?9:10}},{39:39}],39:[function(t,i,n){var r=n;function e(t,i,n){for(var r=Object.keys(i),e=0;e>>7|t.hi<<25)>>>0,t.hi>>>=7;for(;127>>7;i[n++]=t.lo}function b(t,i,n){i[n]=255&t,i[n+1]=t>>>8&255,i[n+2]=t>>>16&255,i[n+3]=t>>>24}c.create=l(),c.alloc=function(t){return new e.Array(t)},e.Array!==Array&&(c.alloc=e.pool(c.alloc,e.Array.prototype.subarray)),c.prototype.g=function(t,i,n){return this.tail=this.tail.next=new f(t,i,n),this.len+=i,this},(d.prototype=Object.create(f.prototype)).fn=function(t,i,n){for(;127>>=7;i[n]=t},c.prototype.uint32=function(t){return this.len+=(this.tail=this.tail.next=new d((t>>>=0)<128?1:t<16384?2:t<2097152?3:t<268435456?4:5,t)).len,this},c.prototype.int32=function(t){return t<0?this.g(p,10,s.fromNumber(t)):this.uint32(t)},c.prototype.sint32=function(t){return this.uint32((t<<1^t>>31)>>>0)},c.prototype.int64=c.prototype.uint64=function(t){t=s.from(t);return this.g(p,t.length(),t)},c.prototype.sint64=function(t){t=s.from(t).zzEncode();return this.g(p,t.length(),t)},c.prototype.bool=function(t){return this.g(v,1,t?1:0)},c.prototype.sfixed32=c.prototype.fixed32=function(t){return this.g(b,4,t>>>0)},c.prototype.sfixed64=c.prototype.fixed64=function(t){t=s.from(t);return this.g(b,4,t.lo).g(b,4,t.hi)},c.prototype.float=function(t){return this.g(e.float.writeFloatLE,4,t)},c.prototype.double=function(t){return this.g(e.float.writeDoubleLE,8,t)};var w=e.Array.prototype.set?function(t,i,n){i.set(t,n)}:function(t,i,n){for(var r=0;r>>0;return n?(e.isString(t)&&(i=c.alloc(n=o.length(t)),o.decode(t,i,0),t=i),this.uint32(n).g(w,n,t)):this.g(v,1,0)},c.prototype.string=function(t){var i=u.length(t);return i?this.uint32(i).g(u.write,i,t):this.g(v,1,0)},c.prototype.fork=function(){return this.states=new a(this),this.head=this.tail=new f(h,0,0),this.len=0,this},c.prototype.reset=function(){return this.states?(this.head=this.states.head,this.tail=this.states.tail,this.len=this.states.len,this.states=this.states.next):(this.head=this.tail=new f(h,0,0),this.len=0),this},c.prototype.ldelim=function(){var t=this.head,i=this.tail,n=this.len;return this.reset().uint32(n),n&&(this.tail.next=t.next,this.tail=i,this.len+=n),this},c.prototype.finish=function(){for(var t=this.head.next,i=this.constructor.alloc(this.len),n=0;t;)t.fn(t.val,i,n),n+=t.len,t=t.next;return i},c.u=function(t){r=t,c.create=l(),r.u()}},{39:39}],43:[function(t,i,n){i.exports=s;var r=t(42);(s.prototype=Object.create(r.prototype)).constructor=s;var e=t(39);function s(){r.call(this)}function o(t,i,n){t.length<40?e.utf8.write(t,i,n):i.utf8Write?i.utf8Write(t,n):i.write(t,n)}s.u=function(){s.alloc=e.y,s.writeBytesBuffer=e.Buffer&&e.Buffer.prototype instanceof Uint8Array&&"set"===e.Buffer.prototype.set.name?function(t,i,n){i.set(t,n)}:function(t,i,n){if(t.copy)t.copy(i,n,0,t.length);else for(var r=0;r>>0;return this.uint32(i),i&&this.g(s.writeBytesBuffer,i,t),this},s.prototype.string=function(t){var i=e.Buffer.byteLength(t);return this.uint32(i),i&&this.g(o,i,t),this},s.u()},{39:39,42:42}]},e={},t=[19],i=function t(i){var n=e[i];return n||r[i][0].call(n=e[i]={exports:{}},t,n,n.exports),n.exports}(t[0]),i.util.global.protobuf=i,"function"==typeof define&&define.amd&&define(["long"],function(t){return t&&t.isLong&&(i.util.Long=t,i.configure()),i}),"object"==typeof module&&module&&module.exports&&(module.exports=i)}(); +//# sourceMappingURL=protobuf.min.js.map diff --git a/src/frontend/Hanami-AI-Dashboard/src/Hanami-AI-Dashboard-Dependencies/protobuf.min.js.map b/src/frontend/Hanami-AI-Dashboard/src/Hanami-AI-Dashboard-Dependencies/protobuf.min.js.map new file mode 100644 index 00000000..b7e88da4 --- /dev/null +++ b/src/frontend/Hanami-AI-Dashboard/src/Hanami-AI-Dashboard-Dependencies/protobuf.min.js.map @@ -0,0 +1 @@ +{"version":3,"sources":["lib/prelude.js","../node_modules/@protobufjs/aspromise/index.js","../node_modules/@protobufjs/base64/index.js","../node_modules/@protobufjs/codegen/index.js","../node_modules/@protobufjs/eventemitter/index.js","../node_modules/@protobufjs/fetch/index.js","../node_modules/@protobufjs/float/index.js","../node_modules/@protobufjs/inquire/index.js","../node_modules/@protobufjs/path/index.js","../node_modules/@protobufjs/pool/index.js","../node_modules/@protobufjs/utf8/index.js","../src/common.js","../src/converter.js","../src/decoder.js","../src/encoder.js","../src/enum.js","../src/field.js","../src/index-light.js","../src/index-minimal.js","../src/index","../src/mapfield.js","../src/message.js","../src/method.js","../src/namespace.js","../src/object.js","../src/oneof.js","../src/parse.js","../src/reader.js","../src/reader_buffer.js","../src/root.js","../src/roots.js","../src/rpc.js","../src/rpc/service.js","../src/service.js","../src/tokenize.js","../src/type.js","../src/types.js","../src/util.js","../src/util/longbits.js","../src/util/minimal.js","../src/verifier.js","../src/wrappers.js","../src/writer.js","../src/writer_buffer.js"],"names":["undefined","modules","cache","entries","protobuf","1","require","module","exports","fn","ctx","params","Array","arguments","length","offset","index","pending","Promise","resolve","reject","err","apply","base64","string","p","n","Math","ceil","b64","s64","i","encode","buffer","start","end","t","parts","chunk","j","b","push","String","fromCharCode","slice","join","invalidEncoding","decode","c","charCodeAt","Error","test","codegen","functionParams","functionName","body","Codegen","formatStringOrScope","source","toString","verbose","console","log","scopeKeys","Object","keys","scopeParams","scopeValues","scopeOffset","Function","formatParams","formatOffset","replace","$0","$1","value","Number","floor","JSON","stringify","functionNameOverride","EventEmitter","this","_listeners","prototype","on","evt","off","listeners","splice","emit","args","fetch","asPromise","fs","inquire","filename","options","callback","xhr","readFile","contents","XMLHttpRequest","binary","onreadystatechange","readyState","status","response","responseText","Uint8Array","overrideMimeType","responseType","open","send","factory","writeFloat_ieee754","writeUint","val","buf","pos","sign","isNaN","round","exponent","LN2","pow","readFloat_ieee754","readUint","uint","mantissa","NaN","Infinity","writeFloat_f32_cpy","f32","f8b","writeFloat_f32_rev","readFloat_f32_cpy","readFloat_f32_rev","f64","le","writeDouble_ieee754","off0","off1","readDouble_ieee754","lo","hi","writeDouble_f64_cpy","writeDouble_f64_rev","readDouble_f64_cpy","readDouble_f64_rev","Float32Array","writeFloatLE","writeFloatBE","readFloatLE","readFloatBE","bind","writeUintLE","writeUintBE","readUintLE","readUintBE","Float64Array","writeDoubleLE","writeDoubleBE","readDoubleLE","readDoubleBE","moduleName","mod","eval","e","path","isAbsolute","normalize","split","absolute","prefix","shift","originPath","includePath","alreadyNormalized","alloc","size","SIZE","MAX","slab","call","utf8","len","read","write","c1","c2","common","commonRe","name","json","nested","google","Any","fields","type_url","type","id","Duration","timeType","seconds","nanos","Timestamp","Empty","Struct","keyType","Value","oneofs","kind","oneof","nullValue","numberValue","stringValue","boolValue","structValue","listValue","NullValue","values","NULL_VALUE","ListValue","rule","DoubleValue","FloatValue","Int64Value","UInt64Value","Int32Value","UInt32Value","BoolValue","StringValue","BytesValue","FieldMask","paths","get","file","converter","Enum","util","genValuePartial_fromObject","gen","field","fieldIndex","prop","resolvedType","repeated","typeDefault","fullName","isUnsigned","genValuePartial_toObject","fromObject","mtype","fieldsArray","safeProp","map","toObject","sort","compareFieldsById","repeatedFields","mapFields","normalFields","partOf","arrayDefault","valuesById","long","low","high","unsigned","toNumber","bytes","hasKs2","_fieldsArray","indexOf","filter","group","ref","types","defaults","basic","packed","rfield","required","wireType","mapKey","genTypePartial","optional","ReflectionObject","create","constructor","className","Namespace","comment","comments","TypeError","reserved","fromJSON","enm","toJSON","toJSONOptions","keepComments","add","isString","isInteger","isReservedId","isReservedName","allow_alias","remove","Field","Type","ruleRe","extend","isObject","toLowerCase","message","defaultValue","Long","extensionField","declaringField","_packed","defineProperty","getOption","setOption","ifNotSet","resolved","parent","lookupTypeOrEnum","fromNumber","freeze","newBuffer","emptyObject","emptyArray","ctor","d","fieldId","fieldType","fieldRule","decorateType","decorateEnum","fieldName","default","_configure","Type_","build","load","root","Root","loadSync","encoder","decoder","verifier","OneOf","MapField","Service","Method","Message","wrappers","configure","Writer","BufferWriter","Reader","BufferReader","rpc","roots","tokenize","parse","resolvedKeyType","fieldKeyType","fieldValueType","properties","$type","writer","encodeDelimited","reader","decodeDelimited","verify","object","requestType","requestStream","responseStream","parsedOptions","resolvedRequestType","resolvedResponseType","lookupType","arrayToJSON","array","obj","_nestedArray","clearCache","namespace","addJSON","toArray","nestedArray","nestedJson","names","methods","getEnum","prev","setOptions","onAdd","onRemove","define","isArray","ptr","part","resolveAll","lookup","filterTypes","parentAlreadyChecked","found","lookupEnum","lookupService","Service_","Enum_","defineProperties","unshift","_handleAdd","_handleRemove","setParsedOption","propName","newValue","newOpt","opt","find","hasOwnProperty","setProperty","Root_","fieldNames","addFieldsToParent","oneofName","oneOfGetter","set","oneOfSetter","keepCase","base10Re","base10NegRe","base16Re","base16NegRe","base8Re","base8NegRe","numberRe","nameRe","typeRefRe","fqTypeRefRe","pkg","imports","weakImports","syntax","token","preferTrailingComment","tn","alternateCommentMode","next","peek","skip","cmnt","head","isProto3","applyCase","camelCase","illegal","insideTryCatch","line","readString","readValue","acceptTypeRef","substring","parseInt","parseFloat","parseNumber","readRanges","target","acceptStrings","parseId","acceptNegative","parseCommon","parseOption","ifBlock","valueType","parseInlineOptions","parseMapField","parseField","parseOneOf","extensions","parseType","dummy","parseEnumValue","parseEnum","service","commentText","method","parseMethod","parseService","reference","parseExtension","fnIf","fnElse","trailingLine","lcFirst","ucFirst","parseGroup","isCustom","option","substr","optionValue","parseOptionValue","result","prevValue","concat","simpleValue","parsePackage","whichImports","parseImport","parseSyntax","package","LongBits","indexOutOfRange","writeLength","RangeError","Buffer","isBuffer","create_array","readLongVarint","bits","readFixed32_end","readFixed64","_slice","subarray","uint32","int32","sint32","bool","fixed32","sfixed32","float","double","skipType","BufferReader_","merge","int64","uint64","sint64","zzDecode","fixed64","sfixed64","utf8Slice","min","deferred","files","SYNC","resolvePath","self","sync","finish","cb","getBundledFileName","idx","lastIndexOf","altname","process","parsed","queued","weak","setTimeout","readFileSync","isNode","exposeRe","tryHandleExtension","extendedType","sisterField","parse_","common_","rpcImpl","requestDelimited","responseDelimited","rpcCall","requestCtor","responseCtor","request","endedByRPC","_methodsArray","inherited","methodsArray","rpcService","methodName","isReserved","m","q","s","delimRe","stringDoubleRe","stringSingleRe","setCommentRe","setCommentAltRe","setCommentSplitRe","whitespaceRe","unescapeRe","unescapeMap","0","r","unescape","str","commentType","commentLine","commentLineEmpty","commentIsLeading","stack","stringDelim","subject","charAt","setComment","isLeading","commentOffset","lines","trim","isDoubleSlashCommentLine","startOffset","endOffset","findEndOfLine","lineText","cursor","re","lastIndex","match","exec","repeat","curr","isDoc","isLeadingComment","expected","actual","ret","_fieldsById","_oneofsArray","_ctor","fieldsById","oneofsArray","generateConstructor","ctorProperties","setup","wrapper","originalThis","fork","ldelim","typeName","bake","o","key","safePropBackslashRe","safePropQuoteRe","toUpperCase","camelCaseRe","a","decorateRoot","enumerable","decorateEnumIndex","dst","setProp","zero","zzEncode","zeroHash","from","fromString","toLong","fromHash","hash","toHash","mask","part0","part1","part2","src","newError","CustomError","captureStackTrace","pool","global","versions","node","window","isFinite","isset","isSet","utf8Write","_Buffer_from","_Buffer_allocUnsafe","sizeOrArray","dcodeIO","key2Re","key32Re","key64Re","longToHash","longFromHash","fromBits","ProtocolError","fieldMap","longs","enums","encoding","allocUnsafe","seenFirstField","oneofProp","invalid","genVerifyKey","genVerifyValue","messageName","Op","noop","State","tail","states","writeByte","VarintOp","writeVarint64","writeFixed32","_push","writeBytes","reset","BufferWriter_","writeStringBuffer","writeBytesBuffer","copy","byteLength","$require","$module","amd","isLong"],"mappings":"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","file":"protobuf.min.js","sourcesContent":["(function prelude(modules, cache, entries) {\n\n // This is the prelude used to bundle protobuf.js for the browser. Wraps up the CommonJS\n // sources through a conflict-free require shim and is again wrapped within an iife that\n // provides a minification-friendly `undefined` var plus a global \"use strict\" directive\n // so that minification can remove the directives of each module.\n\n function $require(name) {\n var $module = cache[name];\n if (!$module)\n modules[name][0].call($module = cache[name] = { exports: {} }, $require, $module, $module.exports);\n return $module.exports;\n }\n\n var protobuf = $require(entries[0]);\n\n // Expose globally\n protobuf.util.global.protobuf = protobuf;\n\n // Be nice to AMD\n if (typeof define === \"function\" && define.amd)\n define([\"long\"], function(Long) {\n if (Long && Long.isLong) {\n protobuf.util.Long = Long;\n protobuf.configure();\n }\n return protobuf;\n });\n\n // Be nice to CommonJS\n if (typeof module === \"object\" && module && module.exports)\n module.exports = protobuf;\n\n})/* end of prelude */","\"use strict\";\r\nmodule.exports = asPromise;\r\n\r\n/**\r\n * Callback as used by {@link util.asPromise}.\r\n * @typedef asPromiseCallback\r\n * @type {function}\r\n * @param {Error|null} error Error, if any\r\n * @param {...*} params Additional arguments\r\n * @returns {undefined}\r\n */\r\n\r\n/**\r\n * Returns a promise from a node-style callback function.\r\n * @memberof util\r\n * @param {asPromiseCallback} fn Function to call\r\n * @param {*} ctx Function context\r\n * @param {...*} params Function arguments\r\n * @returns {Promise<*>} Promisified function\r\n */\r\nfunction asPromise(fn, ctx/*, varargs */) {\r\n var params = new Array(arguments.length - 1),\r\n offset = 0,\r\n index = 2,\r\n pending = true;\r\n while (index < arguments.length)\r\n params[offset++] = arguments[index++];\r\n return new Promise(function executor(resolve, reject) {\r\n params[offset] = function callback(err/*, varargs */) {\r\n if (pending) {\r\n pending = false;\r\n if (err)\r\n reject(err);\r\n else {\r\n var params = new Array(arguments.length - 1),\r\n offset = 0;\r\n while (offset < params.length)\r\n params[offset++] = arguments[offset];\r\n resolve.apply(null, params);\r\n }\r\n }\r\n };\r\n try {\r\n fn.apply(ctx || null, params);\r\n } catch (err) {\r\n if (pending) {\r\n pending = false;\r\n reject(err);\r\n }\r\n }\r\n });\r\n}\r\n","\"use strict\";\r\n\r\n/**\r\n * A minimal base64 implementation for number arrays.\r\n * @memberof util\r\n * @namespace\r\n */\r\nvar base64 = exports;\r\n\r\n/**\r\n * Calculates the byte length of a base64 encoded string.\r\n * @param {string} string Base64 encoded string\r\n * @returns {number} Byte length\r\n */\r\nbase64.length = function length(string) {\r\n var p = string.length;\r\n if (!p)\r\n return 0;\r\n var n = 0;\r\n while (--p % 4 > 1 && string.charAt(p) === \"=\")\r\n ++n;\r\n return Math.ceil(string.length * 3) / 4 - n;\r\n};\r\n\r\n// Base64 encoding table\r\nvar b64 = new Array(64);\r\n\r\n// Base64 decoding table\r\nvar s64 = new Array(123);\r\n\r\n// 65..90, 97..122, 48..57, 43, 47\r\nfor (var i = 0; i < 64;)\r\n s64[b64[i] = i < 26 ? i + 65 : i < 52 ? i + 71 : i < 62 ? i - 4 : i - 59 | 43] = i++;\r\n\r\n/**\r\n * Encodes a buffer to a base64 encoded string.\r\n * @param {Uint8Array} buffer Source buffer\r\n * @param {number} start Source start\r\n * @param {number} end Source end\r\n * @returns {string} Base64 encoded string\r\n */\r\nbase64.encode = function encode(buffer, start, end) {\r\n var parts = null,\r\n chunk = [];\r\n var i = 0, // output index\r\n j = 0, // goto index\r\n t; // temporary\r\n while (start < end) {\r\n var b = buffer[start++];\r\n switch (j) {\r\n case 0:\r\n chunk[i++] = b64[b >> 2];\r\n t = (b & 3) << 4;\r\n j = 1;\r\n break;\r\n case 1:\r\n chunk[i++] = b64[t | b >> 4];\r\n t = (b & 15) << 2;\r\n j = 2;\r\n break;\r\n case 2:\r\n chunk[i++] = b64[t | b >> 6];\r\n chunk[i++] = b64[b & 63];\r\n j = 0;\r\n break;\r\n }\r\n if (i > 8191) {\r\n (parts || (parts = [])).push(String.fromCharCode.apply(String, chunk));\r\n i = 0;\r\n }\r\n }\r\n if (j) {\r\n chunk[i++] = b64[t];\r\n chunk[i++] = 61;\r\n if (j === 1)\r\n chunk[i++] = 61;\r\n }\r\n if (parts) {\r\n if (i)\r\n parts.push(String.fromCharCode.apply(String, chunk.slice(0, i)));\r\n return parts.join(\"\");\r\n }\r\n return String.fromCharCode.apply(String, chunk.slice(0, i));\r\n};\r\n\r\nvar invalidEncoding = \"invalid encoding\";\r\n\r\n/**\r\n * Decodes a base64 encoded string to a buffer.\r\n * @param {string} string Source string\r\n * @param {Uint8Array} buffer Destination buffer\r\n * @param {number} offset Destination offset\r\n * @returns {number} Number of bytes written\r\n * @throws {Error} If encoding is invalid\r\n */\r\nbase64.decode = function decode(string, buffer, offset) {\r\n var start = offset;\r\n var j = 0, // goto index\r\n t; // temporary\r\n for (var i = 0; i < string.length;) {\r\n var c = string.charCodeAt(i++);\r\n if (c === 61 && j > 1)\r\n break;\r\n if ((c = s64[c]) === undefined)\r\n throw Error(invalidEncoding);\r\n switch (j) {\r\n case 0:\r\n t = c;\r\n j = 1;\r\n break;\r\n case 1:\r\n buffer[offset++] = t << 2 | (c & 48) >> 4;\r\n t = c;\r\n j = 2;\r\n break;\r\n case 2:\r\n buffer[offset++] = (t & 15) << 4 | (c & 60) >> 2;\r\n t = c;\r\n j = 3;\r\n break;\r\n case 3:\r\n buffer[offset++] = (t & 3) << 6 | c;\r\n j = 0;\r\n break;\r\n }\r\n }\r\n if (j === 1)\r\n throw Error(invalidEncoding);\r\n return offset - start;\r\n};\r\n\r\n/**\r\n * Tests if the specified string appears to be base64 encoded.\r\n * @param {string} string String to test\r\n * @returns {boolean} `true` if probably base64 encoded, otherwise false\r\n */\r\nbase64.test = function test(string) {\r\n return /^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$/.test(string);\r\n};\r\n","\"use strict\";\r\nmodule.exports = codegen;\r\n\r\n/**\r\n * Begins generating a function.\r\n * @memberof util\r\n * @param {string[]} functionParams Function parameter names\r\n * @param {string} [functionName] Function name if not anonymous\r\n * @returns {Codegen} Appender that appends code to the function's body\r\n */\r\nfunction codegen(functionParams, functionName) {\r\n\r\n /* istanbul ignore if */\r\n if (typeof functionParams === \"string\") {\r\n functionName = functionParams;\r\n functionParams = undefined;\r\n }\r\n\r\n var body = [];\r\n\r\n /**\r\n * Appends code to the function's body or finishes generation.\r\n * @typedef Codegen\r\n * @type {function}\r\n * @param {string|Object.} [formatStringOrScope] Format string or, to finish the function, an object of additional scope variables, if any\r\n * @param {...*} [formatParams] Format parameters\r\n * @returns {Codegen|Function} Itself or the generated function if finished\r\n * @throws {Error} If format parameter counts do not match\r\n */\r\n\r\n function Codegen(formatStringOrScope) {\r\n // note that explicit array handling below makes this ~50% faster\r\n\r\n // finish the function\r\n if (typeof formatStringOrScope !== \"string\") {\r\n var source = toString();\r\n if (codegen.verbose)\r\n console.log(\"codegen: \" + source); // eslint-disable-line no-console\r\n source = \"return \" + source;\r\n if (formatStringOrScope) {\r\n var scopeKeys = Object.keys(formatStringOrScope),\r\n scopeParams = new Array(scopeKeys.length + 1),\r\n scopeValues = new Array(scopeKeys.length),\r\n scopeOffset = 0;\r\n while (scopeOffset < scopeKeys.length) {\r\n scopeParams[scopeOffset] = scopeKeys[scopeOffset];\r\n scopeValues[scopeOffset] = formatStringOrScope[scopeKeys[scopeOffset++]];\r\n }\r\n scopeParams[scopeOffset] = source;\r\n return Function.apply(null, scopeParams).apply(null, scopeValues); // eslint-disable-line no-new-func\r\n }\r\n return Function(source)(); // eslint-disable-line no-new-func\r\n }\r\n\r\n // otherwise append to body\r\n var formatParams = new Array(arguments.length - 1),\r\n formatOffset = 0;\r\n while (formatOffset < formatParams.length)\r\n formatParams[formatOffset] = arguments[++formatOffset];\r\n formatOffset = 0;\r\n formatStringOrScope = formatStringOrScope.replace(/%([%dfijs])/g, function replace($0, $1) {\r\n var value = formatParams[formatOffset++];\r\n switch ($1) {\r\n case \"d\": case \"f\": return String(Number(value));\r\n case \"i\": return String(Math.floor(value));\r\n case \"j\": return JSON.stringify(value);\r\n case \"s\": return String(value);\r\n }\r\n return \"%\";\r\n });\r\n if (formatOffset !== formatParams.length)\r\n throw Error(\"parameter count mismatch\");\r\n body.push(formatStringOrScope);\r\n return Codegen;\r\n }\r\n\r\n function toString(functionNameOverride) {\r\n return \"function \" + (functionNameOverride || functionName || \"\") + \"(\" + (functionParams && functionParams.join(\",\") || \"\") + \"){\\n \" + body.join(\"\\n \") + \"\\n}\";\r\n }\r\n\r\n Codegen.toString = toString;\r\n return Codegen;\r\n}\r\n\r\n/**\r\n * Begins generating a function.\r\n * @memberof util\r\n * @function codegen\r\n * @param {string} [functionName] Function name if not anonymous\r\n * @returns {Codegen} Appender that appends code to the function's body\r\n * @variation 2\r\n */\r\n\r\n/**\r\n * When set to `true`, codegen will log generated code to console. Useful for debugging.\r\n * @name util.codegen.verbose\r\n * @type {boolean}\r\n */\r\ncodegen.verbose = false;\r\n","\"use strict\";\r\nmodule.exports = EventEmitter;\r\n\r\n/**\r\n * Constructs a new event emitter instance.\r\n * @classdesc A minimal event emitter.\r\n * @memberof util\r\n * @constructor\r\n */\r\nfunction EventEmitter() {\r\n\r\n /**\r\n * Registered listeners.\r\n * @type {Object.}\r\n * @private\r\n */\r\n this._listeners = {};\r\n}\r\n\r\n/**\r\n * Registers an event listener.\r\n * @param {string} evt Event name\r\n * @param {function} fn Listener\r\n * @param {*} [ctx] Listener context\r\n * @returns {util.EventEmitter} `this`\r\n */\r\nEventEmitter.prototype.on = function on(evt, fn, ctx) {\r\n (this._listeners[evt] || (this._listeners[evt] = [])).push({\r\n fn : fn,\r\n ctx : ctx || this\r\n });\r\n return this;\r\n};\r\n\r\n/**\r\n * Removes an event listener or any matching listeners if arguments are omitted.\r\n * @param {string} [evt] Event name. Removes all listeners if omitted.\r\n * @param {function} [fn] Listener to remove. Removes all listeners of `evt` if omitted.\r\n * @returns {util.EventEmitter} `this`\r\n */\r\nEventEmitter.prototype.off = function off(evt, fn) {\r\n if (evt === undefined)\r\n this._listeners = {};\r\n else {\r\n if (fn === undefined)\r\n this._listeners[evt] = [];\r\n else {\r\n var listeners = this._listeners[evt];\r\n for (var i = 0; i < listeners.length;)\r\n if (listeners[i].fn === fn)\r\n listeners.splice(i, 1);\r\n else\r\n ++i;\r\n }\r\n }\r\n return this;\r\n};\r\n\r\n/**\r\n * Emits an event by calling its listeners with the specified arguments.\r\n * @param {string} evt Event name\r\n * @param {...*} args Arguments\r\n * @returns {util.EventEmitter} `this`\r\n */\r\nEventEmitter.prototype.emit = function emit(evt) {\r\n var listeners = this._listeners[evt];\r\n if (listeners) {\r\n var args = [],\r\n i = 1;\r\n for (; i < arguments.length;)\r\n args.push(arguments[i++]);\r\n for (i = 0; i < listeners.length;)\r\n listeners[i].fn.apply(listeners[i++].ctx, args);\r\n }\r\n return this;\r\n};\r\n","\"use strict\";\r\nmodule.exports = fetch;\r\n\r\nvar asPromise = require(1),\r\n inquire = require(7);\r\n\r\nvar fs = inquire(\"fs\");\r\n\r\n/**\r\n * Node-style callback as used by {@link util.fetch}.\r\n * @typedef FetchCallback\r\n * @type {function}\r\n * @param {?Error} error Error, if any, otherwise `null`\r\n * @param {string} [contents] File contents, if there hasn't been an error\r\n * @returns {undefined}\r\n */\r\n\r\n/**\r\n * Options as used by {@link util.fetch}.\r\n * @typedef FetchOptions\r\n * @type {Object}\r\n * @property {boolean} [binary=false] Whether expecting a binary response\r\n * @property {boolean} [xhr=false] If `true`, forces the use of XMLHttpRequest\r\n */\r\n\r\n/**\r\n * Fetches the contents of a file.\r\n * @memberof util\r\n * @param {string} filename File path or url\r\n * @param {FetchOptions} options Fetch options\r\n * @param {FetchCallback} callback Callback function\r\n * @returns {undefined}\r\n */\r\nfunction fetch(filename, options, callback) {\r\n if (typeof options === \"function\") {\r\n callback = options;\r\n options = {};\r\n } else if (!options)\r\n options = {};\r\n\r\n if (!callback)\r\n return asPromise(fetch, this, filename, options); // eslint-disable-line no-invalid-this\r\n\r\n // if a node-like filesystem is present, try it first but fall back to XHR if nothing is found.\r\n if (!options.xhr && fs && fs.readFile)\r\n return fs.readFile(filename, function fetchReadFileCallback(err, contents) {\r\n return err && typeof XMLHttpRequest !== \"undefined\"\r\n ? fetch.xhr(filename, options, callback)\r\n : err\r\n ? callback(err)\r\n : callback(null, options.binary ? contents : contents.toString(\"utf8\"));\r\n });\r\n\r\n // use the XHR version otherwise.\r\n return fetch.xhr(filename, options, callback);\r\n}\r\n\r\n/**\r\n * Fetches the contents of a file.\r\n * @name util.fetch\r\n * @function\r\n * @param {string} path File path or url\r\n * @param {FetchCallback} callback Callback function\r\n * @returns {undefined}\r\n * @variation 2\r\n */\r\n\r\n/**\r\n * Fetches the contents of a file.\r\n * @name util.fetch\r\n * @function\r\n * @param {string} path File path or url\r\n * @param {FetchOptions} [options] Fetch options\r\n * @returns {Promise} Promise\r\n * @variation 3\r\n */\r\n\r\n/**/\r\nfetch.xhr = function fetch_xhr(filename, options, callback) {\r\n var xhr = new XMLHttpRequest();\r\n xhr.onreadystatechange /* works everywhere */ = function fetchOnReadyStateChange() {\r\n\r\n if (xhr.readyState !== 4)\r\n return undefined;\r\n\r\n // local cors security errors return status 0 / empty string, too. afaik this cannot be\r\n // reliably distinguished from an actually empty file for security reasons. feel free\r\n // to send a pull request if you are aware of a solution.\r\n if (xhr.status !== 0 && xhr.status !== 200)\r\n return callback(Error(\"status \" + xhr.status));\r\n\r\n // if binary data is expected, make sure that some sort of array is returned, even if\r\n // ArrayBuffers are not supported. the binary string fallback, however, is unsafe.\r\n if (options.binary) {\r\n var buffer = xhr.response;\r\n if (!buffer) {\r\n buffer = [];\r\n for (var i = 0; i < xhr.responseText.length; ++i)\r\n buffer.push(xhr.responseText.charCodeAt(i) & 255);\r\n }\r\n return callback(null, typeof Uint8Array !== \"undefined\" ? new Uint8Array(buffer) : buffer);\r\n }\r\n return callback(null, xhr.responseText);\r\n };\r\n\r\n if (options.binary) {\r\n // ref: https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/Sending_and_Receiving_Binary_Data#Receiving_binary_data_in_older_browsers\r\n if (\"overrideMimeType\" in xhr)\r\n xhr.overrideMimeType(\"text/plain; charset=x-user-defined\");\r\n xhr.responseType = \"arraybuffer\";\r\n }\r\n\r\n xhr.open(\"GET\", filename);\r\n xhr.send();\r\n};\r\n","\"use strict\";\r\n\r\nmodule.exports = factory(factory);\r\n\r\n/**\r\n * Reads / writes floats / doubles from / to buffers.\r\n * @name util.float\r\n * @namespace\r\n */\r\n\r\n/**\r\n * Writes a 32 bit float to a buffer using little endian byte order.\r\n * @name util.float.writeFloatLE\r\n * @function\r\n * @param {number} val Value to write\r\n * @param {Uint8Array} buf Target buffer\r\n * @param {number} pos Target buffer offset\r\n * @returns {undefined}\r\n */\r\n\r\n/**\r\n * Writes a 32 bit float to a buffer using big endian byte order.\r\n * @name util.float.writeFloatBE\r\n * @function\r\n * @param {number} val Value to write\r\n * @param {Uint8Array} buf Target buffer\r\n * @param {number} pos Target buffer offset\r\n * @returns {undefined}\r\n */\r\n\r\n/**\r\n * Reads a 32 bit float from a buffer using little endian byte order.\r\n * @name util.float.readFloatLE\r\n * @function\r\n * @param {Uint8Array} buf Source buffer\r\n * @param {number} pos Source buffer offset\r\n * @returns {number} Value read\r\n */\r\n\r\n/**\r\n * Reads a 32 bit float from a buffer using big endian byte order.\r\n * @name util.float.readFloatBE\r\n * @function\r\n * @param {Uint8Array} buf Source buffer\r\n * @param {number} pos Source buffer offset\r\n * @returns {number} Value read\r\n */\r\n\r\n/**\r\n * Writes a 64 bit double to a buffer using little endian byte order.\r\n * @name util.float.writeDoubleLE\r\n * @function\r\n * @param {number} val Value to write\r\n * @param {Uint8Array} buf Target buffer\r\n * @param {number} pos Target buffer offset\r\n * @returns {undefined}\r\n */\r\n\r\n/**\r\n * Writes a 64 bit double to a buffer using big endian byte order.\r\n * @name util.float.writeDoubleBE\r\n * @function\r\n * @param {number} val Value to write\r\n * @param {Uint8Array} buf Target buffer\r\n * @param {number} pos Target buffer offset\r\n * @returns {undefined}\r\n */\r\n\r\n/**\r\n * Reads a 64 bit double from a buffer using little endian byte order.\r\n * @name util.float.readDoubleLE\r\n * @function\r\n * @param {Uint8Array} buf Source buffer\r\n * @param {number} pos Source buffer offset\r\n * @returns {number} Value read\r\n */\r\n\r\n/**\r\n * Reads a 64 bit double from a buffer using big endian byte order.\r\n * @name util.float.readDoubleBE\r\n * @function\r\n * @param {Uint8Array} buf Source buffer\r\n * @param {number} pos Source buffer offset\r\n * @returns {number} Value read\r\n */\r\n\r\n// Factory function for the purpose of node-based testing in modified global environments\r\nfunction factory(exports) {\r\n\r\n // float: typed array\r\n if (typeof Float32Array !== \"undefined\") (function() {\r\n\r\n var f32 = new Float32Array([ -0 ]),\r\n f8b = new Uint8Array(f32.buffer),\r\n le = f8b[3] === 128;\r\n\r\n function writeFloat_f32_cpy(val, buf, pos) {\r\n f32[0] = val;\r\n buf[pos ] = f8b[0];\r\n buf[pos + 1] = f8b[1];\r\n buf[pos + 2] = f8b[2];\r\n buf[pos + 3] = f8b[3];\r\n }\r\n\r\n function writeFloat_f32_rev(val, buf, pos) {\r\n f32[0] = val;\r\n buf[pos ] = f8b[3];\r\n buf[pos + 1] = f8b[2];\r\n buf[pos + 2] = f8b[1];\r\n buf[pos + 3] = f8b[0];\r\n }\r\n\r\n /* istanbul ignore next */\r\n exports.writeFloatLE = le ? writeFloat_f32_cpy : writeFloat_f32_rev;\r\n /* istanbul ignore next */\r\n exports.writeFloatBE = le ? writeFloat_f32_rev : writeFloat_f32_cpy;\r\n\r\n function readFloat_f32_cpy(buf, pos) {\r\n f8b[0] = buf[pos ];\r\n f8b[1] = buf[pos + 1];\r\n f8b[2] = buf[pos + 2];\r\n f8b[3] = buf[pos + 3];\r\n return f32[0];\r\n }\r\n\r\n function readFloat_f32_rev(buf, pos) {\r\n f8b[3] = buf[pos ];\r\n f8b[2] = buf[pos + 1];\r\n f8b[1] = buf[pos + 2];\r\n f8b[0] = buf[pos + 3];\r\n return f32[0];\r\n }\r\n\r\n /* istanbul ignore next */\r\n exports.readFloatLE = le ? readFloat_f32_cpy : readFloat_f32_rev;\r\n /* istanbul ignore next */\r\n exports.readFloatBE = le ? readFloat_f32_rev : readFloat_f32_cpy;\r\n\r\n // float: ieee754\r\n })(); else (function() {\r\n\r\n function writeFloat_ieee754(writeUint, val, buf, pos) {\r\n var sign = val < 0 ? 1 : 0;\r\n if (sign)\r\n val = -val;\r\n if (val === 0)\r\n writeUint(1 / val > 0 ? /* positive */ 0 : /* negative 0 */ 2147483648, buf, pos);\r\n else if (isNaN(val))\r\n writeUint(2143289344, buf, pos);\r\n else if (val > 3.4028234663852886e+38) // +-Infinity\r\n writeUint((sign << 31 | 2139095040) >>> 0, buf, pos);\r\n else if (val < 1.1754943508222875e-38) // denormal\r\n writeUint((sign << 31 | Math.round(val / 1.401298464324817e-45)) >>> 0, buf, pos);\r\n else {\r\n var exponent = Math.floor(Math.log(val) / Math.LN2),\r\n mantissa = Math.round(val * Math.pow(2, -exponent) * 8388608) & 8388607;\r\n writeUint((sign << 31 | exponent + 127 << 23 | mantissa) >>> 0, buf, pos);\r\n }\r\n }\r\n\r\n exports.writeFloatLE = writeFloat_ieee754.bind(null, writeUintLE);\r\n exports.writeFloatBE = writeFloat_ieee754.bind(null, writeUintBE);\r\n\r\n function readFloat_ieee754(readUint, buf, pos) {\r\n var uint = readUint(buf, pos),\r\n sign = (uint >> 31) * 2 + 1,\r\n exponent = uint >>> 23 & 255,\r\n mantissa = uint & 8388607;\r\n return exponent === 255\r\n ? mantissa\r\n ? NaN\r\n : sign * Infinity\r\n : exponent === 0 // denormal\r\n ? sign * 1.401298464324817e-45 * mantissa\r\n : sign * Math.pow(2, exponent - 150) * (mantissa + 8388608);\r\n }\r\n\r\n exports.readFloatLE = readFloat_ieee754.bind(null, readUintLE);\r\n exports.readFloatBE = readFloat_ieee754.bind(null, readUintBE);\r\n\r\n })();\r\n\r\n // double: typed array\r\n if (typeof Float64Array !== \"undefined\") (function() {\r\n\r\n var f64 = new Float64Array([-0]),\r\n f8b = new Uint8Array(f64.buffer),\r\n le = f8b[7] === 128;\r\n\r\n function writeDouble_f64_cpy(val, buf, pos) {\r\n f64[0] = val;\r\n buf[pos ] = f8b[0];\r\n buf[pos + 1] = f8b[1];\r\n buf[pos + 2] = f8b[2];\r\n buf[pos + 3] = f8b[3];\r\n buf[pos + 4] = f8b[4];\r\n buf[pos + 5] = f8b[5];\r\n buf[pos + 6] = f8b[6];\r\n buf[pos + 7] = f8b[7];\r\n }\r\n\r\n function writeDouble_f64_rev(val, buf, pos) {\r\n f64[0] = val;\r\n buf[pos ] = f8b[7];\r\n buf[pos + 1] = f8b[6];\r\n buf[pos + 2] = f8b[5];\r\n buf[pos + 3] = f8b[4];\r\n buf[pos + 4] = f8b[3];\r\n buf[pos + 5] = f8b[2];\r\n buf[pos + 6] = f8b[1];\r\n buf[pos + 7] = f8b[0];\r\n }\r\n\r\n /* istanbul ignore next */\r\n exports.writeDoubleLE = le ? writeDouble_f64_cpy : writeDouble_f64_rev;\r\n /* istanbul ignore next */\r\n exports.writeDoubleBE = le ? writeDouble_f64_rev : writeDouble_f64_cpy;\r\n\r\n function readDouble_f64_cpy(buf, pos) {\r\n f8b[0] = buf[pos ];\r\n f8b[1] = buf[pos + 1];\r\n f8b[2] = buf[pos + 2];\r\n f8b[3] = buf[pos + 3];\r\n f8b[4] = buf[pos + 4];\r\n f8b[5] = buf[pos + 5];\r\n f8b[6] = buf[pos + 6];\r\n f8b[7] = buf[pos + 7];\r\n return f64[0];\r\n }\r\n\r\n function readDouble_f64_rev(buf, pos) {\r\n f8b[7] = buf[pos ];\r\n f8b[6] = buf[pos + 1];\r\n f8b[5] = buf[pos + 2];\r\n f8b[4] = buf[pos + 3];\r\n f8b[3] = buf[pos + 4];\r\n f8b[2] = buf[pos + 5];\r\n f8b[1] = buf[pos + 6];\r\n f8b[0] = buf[pos + 7];\r\n return f64[0];\r\n }\r\n\r\n /* istanbul ignore next */\r\n exports.readDoubleLE = le ? readDouble_f64_cpy : readDouble_f64_rev;\r\n /* istanbul ignore next */\r\n exports.readDoubleBE = le ? readDouble_f64_rev : readDouble_f64_cpy;\r\n\r\n // double: ieee754\r\n })(); else (function() {\r\n\r\n function writeDouble_ieee754(writeUint, off0, off1, val, buf, pos) {\r\n var sign = val < 0 ? 1 : 0;\r\n if (sign)\r\n val = -val;\r\n if (val === 0) {\r\n writeUint(0, buf, pos + off0);\r\n writeUint(1 / val > 0 ? /* positive */ 0 : /* negative 0 */ 2147483648, buf, pos + off1);\r\n } else if (isNaN(val)) {\r\n writeUint(0, buf, pos + off0);\r\n writeUint(2146959360, buf, pos + off1);\r\n } else if (val > 1.7976931348623157e+308) { // +-Infinity\r\n writeUint(0, buf, pos + off0);\r\n writeUint((sign << 31 | 2146435072) >>> 0, buf, pos + off1);\r\n } else {\r\n var mantissa;\r\n if (val < 2.2250738585072014e-308) { // denormal\r\n mantissa = val / 5e-324;\r\n writeUint(mantissa >>> 0, buf, pos + off0);\r\n writeUint((sign << 31 | mantissa / 4294967296) >>> 0, buf, pos + off1);\r\n } else {\r\n var exponent = Math.floor(Math.log(val) / Math.LN2);\r\n if (exponent === 1024)\r\n exponent = 1023;\r\n mantissa = val * Math.pow(2, -exponent);\r\n writeUint(mantissa * 4503599627370496 >>> 0, buf, pos + off0);\r\n writeUint((sign << 31 | exponent + 1023 << 20 | mantissa * 1048576 & 1048575) >>> 0, buf, pos + off1);\r\n }\r\n }\r\n }\r\n\r\n exports.writeDoubleLE = writeDouble_ieee754.bind(null, writeUintLE, 0, 4);\r\n exports.writeDoubleBE = writeDouble_ieee754.bind(null, writeUintBE, 4, 0);\r\n\r\n function readDouble_ieee754(readUint, off0, off1, buf, pos) {\r\n var lo = readUint(buf, pos + off0),\r\n hi = readUint(buf, pos + off1);\r\n var sign = (hi >> 31) * 2 + 1,\r\n exponent = hi >>> 20 & 2047,\r\n mantissa = 4294967296 * (hi & 1048575) + lo;\r\n return exponent === 2047\r\n ? mantissa\r\n ? NaN\r\n : sign * Infinity\r\n : exponent === 0 // denormal\r\n ? sign * 5e-324 * mantissa\r\n : sign * Math.pow(2, exponent - 1075) * (mantissa + 4503599627370496);\r\n }\r\n\r\n exports.readDoubleLE = readDouble_ieee754.bind(null, readUintLE, 0, 4);\r\n exports.readDoubleBE = readDouble_ieee754.bind(null, readUintBE, 4, 0);\r\n\r\n })();\r\n\r\n return exports;\r\n}\r\n\r\n// uint helpers\r\n\r\nfunction writeUintLE(val, buf, pos) {\r\n buf[pos ] = val & 255;\r\n buf[pos + 1] = val >>> 8 & 255;\r\n buf[pos + 2] = val >>> 16 & 255;\r\n buf[pos + 3] = val >>> 24;\r\n}\r\n\r\nfunction writeUintBE(val, buf, pos) {\r\n buf[pos ] = val >>> 24;\r\n buf[pos + 1] = val >>> 16 & 255;\r\n buf[pos + 2] = val >>> 8 & 255;\r\n buf[pos + 3] = val & 255;\r\n}\r\n\r\nfunction readUintLE(buf, pos) {\r\n return (buf[pos ]\r\n | buf[pos + 1] << 8\r\n | buf[pos + 2] << 16\r\n | buf[pos + 3] << 24) >>> 0;\r\n}\r\n\r\nfunction readUintBE(buf, pos) {\r\n return (buf[pos ] << 24\r\n | buf[pos + 1] << 16\r\n | buf[pos + 2] << 8\r\n | buf[pos + 3]) >>> 0;\r\n}\r\n","\"use strict\";\r\nmodule.exports = inquire;\r\n\r\n/**\r\n * Requires a module only if available.\r\n * @memberof util\r\n * @param {string} moduleName Module to require\r\n * @returns {?Object} Required module if available and not empty, otherwise `null`\r\n */\r\nfunction inquire(moduleName) {\r\n try {\r\n var mod = eval(\"quire\".replace(/^/,\"re\"))(moduleName); // eslint-disable-line no-eval\r\n if (mod && (mod.length || Object.keys(mod).length))\r\n return mod;\r\n } catch (e) {} // eslint-disable-line no-empty\r\n return null;\r\n}\r\n","\"use strict\";\r\n\r\n/**\r\n * A minimal path module to resolve Unix, Windows and URL paths alike.\r\n * @memberof util\r\n * @namespace\r\n */\r\nvar path = exports;\r\n\r\nvar isAbsolute =\r\n/**\r\n * Tests if the specified path is absolute.\r\n * @param {string} path Path to test\r\n * @returns {boolean} `true` if path is absolute\r\n */\r\npath.isAbsolute = function isAbsolute(path) {\r\n return /^(?:\\/|\\w+:)/.test(path);\r\n};\r\n\r\nvar normalize =\r\n/**\r\n * Normalizes the specified path.\r\n * @param {string} path Path to normalize\r\n * @returns {string} Normalized path\r\n */\r\npath.normalize = function normalize(path) {\r\n path = path.replace(/\\\\/g, \"/\")\r\n .replace(/\\/{2,}/g, \"/\");\r\n var parts = path.split(\"/\"),\r\n absolute = isAbsolute(path),\r\n prefix = \"\";\r\n if (absolute)\r\n prefix = parts.shift() + \"/\";\r\n for (var i = 0; i < parts.length;) {\r\n if (parts[i] === \"..\") {\r\n if (i > 0 && parts[i - 1] !== \"..\")\r\n parts.splice(--i, 2);\r\n else if (absolute)\r\n parts.splice(i, 1);\r\n else\r\n ++i;\r\n } else if (parts[i] === \".\")\r\n parts.splice(i, 1);\r\n else\r\n ++i;\r\n }\r\n return prefix + parts.join(\"/\");\r\n};\r\n\r\n/**\r\n * Resolves the specified include path against the specified origin path.\r\n * @param {string} originPath Path to the origin file\r\n * @param {string} includePath Include path relative to origin path\r\n * @param {boolean} [alreadyNormalized=false] `true` if both paths are already known to be normalized\r\n * @returns {string} Path to the include file\r\n */\r\npath.resolve = function resolve(originPath, includePath, alreadyNormalized) {\r\n if (!alreadyNormalized)\r\n includePath = normalize(includePath);\r\n if (isAbsolute(includePath))\r\n return includePath;\r\n if (!alreadyNormalized)\r\n originPath = normalize(originPath);\r\n return (originPath = originPath.replace(/(?:\\/|^)[^/]+$/, \"\")).length ? normalize(originPath + \"/\" + includePath) : includePath;\r\n};\r\n","\"use strict\";\r\nmodule.exports = pool;\r\n\r\n/**\r\n * An allocator as used by {@link util.pool}.\r\n * @typedef PoolAllocator\r\n * @type {function}\r\n * @param {number} size Buffer size\r\n * @returns {Uint8Array} Buffer\r\n */\r\n\r\n/**\r\n * A slicer as used by {@link util.pool}.\r\n * @typedef PoolSlicer\r\n * @type {function}\r\n * @param {number} start Start offset\r\n * @param {number} end End offset\r\n * @returns {Uint8Array} Buffer slice\r\n * @this {Uint8Array}\r\n */\r\n\r\n/**\r\n * A general purpose buffer pool.\r\n * @memberof util\r\n * @function\r\n * @param {PoolAllocator} alloc Allocator\r\n * @param {PoolSlicer} slice Slicer\r\n * @param {number} [size=8192] Slab size\r\n * @returns {PoolAllocator} Pooled allocator\r\n */\r\nfunction pool(alloc, slice, size) {\r\n var SIZE = size || 8192;\r\n var MAX = SIZE >>> 1;\r\n var slab = null;\r\n var offset = SIZE;\r\n return function pool_alloc(size) {\r\n if (size < 1 || size > MAX)\r\n return alloc(size);\r\n if (offset + size > SIZE) {\r\n slab = alloc(SIZE);\r\n offset = 0;\r\n }\r\n var buf = slice.call(slab, offset, offset += size);\r\n if (offset & 7) // align to 32 bit\r\n offset = (offset | 7) + 1;\r\n return buf;\r\n };\r\n}\r\n","\"use strict\";\r\n\r\n/**\r\n * A minimal UTF8 implementation for number arrays.\r\n * @memberof util\r\n * @namespace\r\n */\r\nvar utf8 = exports;\r\n\r\n/**\r\n * Calculates the UTF8 byte length of a string.\r\n * @param {string} string String\r\n * @returns {number} Byte length\r\n */\r\nutf8.length = function utf8_length(string) {\r\n var len = 0,\r\n c = 0;\r\n for (var i = 0; i < string.length; ++i) {\r\n c = string.charCodeAt(i);\r\n if (c < 128)\r\n len += 1;\r\n else if (c < 2048)\r\n len += 2;\r\n else if ((c & 0xFC00) === 0xD800 && (string.charCodeAt(i + 1) & 0xFC00) === 0xDC00) {\r\n ++i;\r\n len += 4;\r\n } else\r\n len += 3;\r\n }\r\n return len;\r\n};\r\n\r\n/**\r\n * Reads UTF8 bytes as a string.\r\n * @param {Uint8Array} buffer Source buffer\r\n * @param {number} start Source start\r\n * @param {number} end Source end\r\n * @returns {string} String read\r\n */\r\nutf8.read = function utf8_read(buffer, start, end) {\r\n var len = end - start;\r\n if (len < 1)\r\n return \"\";\r\n var parts = null,\r\n chunk = [],\r\n i = 0, // char offset\r\n t; // temporary\r\n while (start < end) {\r\n t = buffer[start++];\r\n if (t < 128)\r\n chunk[i++] = t;\r\n else if (t > 191 && t < 224)\r\n chunk[i++] = (t & 31) << 6 | buffer[start++] & 63;\r\n else if (t > 239 && t < 365) {\r\n t = ((t & 7) << 18 | (buffer[start++] & 63) << 12 | (buffer[start++] & 63) << 6 | buffer[start++] & 63) - 0x10000;\r\n chunk[i++] = 0xD800 + (t >> 10);\r\n chunk[i++] = 0xDC00 + (t & 1023);\r\n } else\r\n chunk[i++] = (t & 15) << 12 | (buffer[start++] & 63) << 6 | buffer[start++] & 63;\r\n if (i > 8191) {\r\n (parts || (parts = [])).push(String.fromCharCode.apply(String, chunk));\r\n i = 0;\r\n }\r\n }\r\n if (parts) {\r\n if (i)\r\n parts.push(String.fromCharCode.apply(String, chunk.slice(0, i)));\r\n return parts.join(\"\");\r\n }\r\n return String.fromCharCode.apply(String, chunk.slice(0, i));\r\n};\r\n\r\n/**\r\n * Writes a string as UTF8 bytes.\r\n * @param {string} string Source string\r\n * @param {Uint8Array} buffer Destination buffer\r\n * @param {number} offset Destination offset\r\n * @returns {number} Bytes written\r\n */\r\nutf8.write = function utf8_write(string, buffer, offset) {\r\n var start = offset,\r\n c1, // character 1\r\n c2; // character 2\r\n for (var i = 0; i < string.length; ++i) {\r\n c1 = string.charCodeAt(i);\r\n if (c1 < 128) {\r\n buffer[offset++] = c1;\r\n } else if (c1 < 2048) {\r\n buffer[offset++] = c1 >> 6 | 192;\r\n buffer[offset++] = c1 & 63 | 128;\r\n } else if ((c1 & 0xFC00) === 0xD800 && ((c2 = string.charCodeAt(i + 1)) & 0xFC00) === 0xDC00) {\r\n c1 = 0x10000 + ((c1 & 0x03FF) << 10) + (c2 & 0x03FF);\r\n ++i;\r\n buffer[offset++] = c1 >> 18 | 240;\r\n buffer[offset++] = c1 >> 12 & 63 | 128;\r\n buffer[offset++] = c1 >> 6 & 63 | 128;\r\n buffer[offset++] = c1 & 63 | 128;\r\n } else {\r\n buffer[offset++] = c1 >> 12 | 224;\r\n buffer[offset++] = c1 >> 6 & 63 | 128;\r\n buffer[offset++] = c1 & 63 | 128;\r\n }\r\n }\r\n return offset - start;\r\n};\r\n","\"use strict\";\nmodule.exports = common;\n\nvar commonRe = /\\/|\\./;\n\n/**\n * Provides common type definitions.\n * Can also be used to provide additional google types or your own custom types.\n * @param {string} name Short name as in `google/protobuf/[name].proto` or full file name\n * @param {Object.} json JSON definition within `google.protobuf` if a short name, otherwise the file's root definition\n * @returns {undefined}\n * @property {INamespace} google/protobuf/any.proto Any\n * @property {INamespace} google/protobuf/duration.proto Duration\n * @property {INamespace} google/protobuf/empty.proto Empty\n * @property {INamespace} google/protobuf/field_mask.proto FieldMask\n * @property {INamespace} google/protobuf/struct.proto Struct, Value, NullValue and ListValue\n * @property {INamespace} google/protobuf/timestamp.proto Timestamp\n * @property {INamespace} google/protobuf/wrappers.proto Wrappers\n * @example\n * // manually provides descriptor.proto (assumes google/protobuf/ namespace and .proto extension)\n * protobuf.common(\"descriptor\", descriptorJson);\n *\n * // manually provides a custom definition (uses my.foo namespace)\n * protobuf.common(\"my/foo/bar.proto\", myFooBarJson);\n */\nfunction common(name, json) {\n if (!commonRe.test(name)) {\n name = \"google/protobuf/\" + name + \".proto\";\n json = { nested: { google: { nested: { protobuf: { nested: json } } } } };\n }\n common[name] = json;\n}\n\n// Not provided because of limited use (feel free to discuss or to provide yourself):\n//\n// google/protobuf/descriptor.proto\n// google/protobuf/source_context.proto\n// google/protobuf/type.proto\n//\n// Stripped and pre-parsed versions of these non-bundled files are instead available as part of\n// the repository or package within the google/protobuf directory.\n\ncommon(\"any\", {\n\n /**\n * Properties of a google.protobuf.Any message.\n * @interface IAny\n * @type {Object}\n * @property {string} [typeUrl]\n * @property {Uint8Array} [bytes]\n * @memberof common\n */\n Any: {\n fields: {\n type_url: {\n type: \"string\",\n id: 1\n },\n value: {\n type: \"bytes\",\n id: 2\n }\n }\n }\n});\n\nvar timeType;\n\ncommon(\"duration\", {\n\n /**\n * Properties of a google.protobuf.Duration message.\n * @interface IDuration\n * @type {Object}\n * @property {number|Long} [seconds]\n * @property {number} [nanos]\n * @memberof common\n */\n Duration: timeType = {\n fields: {\n seconds: {\n type: \"int64\",\n id: 1\n },\n nanos: {\n type: \"int32\",\n id: 2\n }\n }\n }\n});\n\ncommon(\"timestamp\", {\n\n /**\n * Properties of a google.protobuf.Timestamp message.\n * @interface ITimestamp\n * @type {Object}\n * @property {number|Long} [seconds]\n * @property {number} [nanos]\n * @memberof common\n */\n Timestamp: timeType\n});\n\ncommon(\"empty\", {\n\n /**\n * Properties of a google.protobuf.Empty message.\n * @interface IEmpty\n * @memberof common\n */\n Empty: {\n fields: {}\n }\n});\n\ncommon(\"struct\", {\n\n /**\n * Properties of a google.protobuf.Struct message.\n * @interface IStruct\n * @type {Object}\n * @property {Object.} [fields]\n * @memberof common\n */\n Struct: {\n fields: {\n fields: {\n keyType: \"string\",\n type: \"Value\",\n id: 1\n }\n }\n },\n\n /**\n * Properties of a google.protobuf.Value message.\n * @interface IValue\n * @type {Object}\n * @property {string} [kind]\n * @property {0} [nullValue]\n * @property {number} [numberValue]\n * @property {string} [stringValue]\n * @property {boolean} [boolValue]\n * @property {IStruct} [structValue]\n * @property {IListValue} [listValue]\n * @memberof common\n */\n Value: {\n oneofs: {\n kind: {\n oneof: [\n \"nullValue\",\n \"numberValue\",\n \"stringValue\",\n \"boolValue\",\n \"structValue\",\n \"listValue\"\n ]\n }\n },\n fields: {\n nullValue: {\n type: \"NullValue\",\n id: 1\n },\n numberValue: {\n type: \"double\",\n id: 2\n },\n stringValue: {\n type: \"string\",\n id: 3\n },\n boolValue: {\n type: \"bool\",\n id: 4\n },\n structValue: {\n type: \"Struct\",\n id: 5\n },\n listValue: {\n type: \"ListValue\",\n id: 6\n }\n }\n },\n\n NullValue: {\n values: {\n NULL_VALUE: 0\n }\n },\n\n /**\n * Properties of a google.protobuf.ListValue message.\n * @interface IListValue\n * @type {Object}\n * @property {Array.} [values]\n * @memberof common\n */\n ListValue: {\n fields: {\n values: {\n rule: \"repeated\",\n type: \"Value\",\n id: 1\n }\n }\n }\n});\n\ncommon(\"wrappers\", {\n\n /**\n * Properties of a google.protobuf.DoubleValue message.\n * @interface IDoubleValue\n * @type {Object}\n * @property {number} [value]\n * @memberof common\n */\n DoubleValue: {\n fields: {\n value: {\n type: \"double\",\n id: 1\n }\n }\n },\n\n /**\n * Properties of a google.protobuf.FloatValue message.\n * @interface IFloatValue\n * @type {Object}\n * @property {number} [value]\n * @memberof common\n */\n FloatValue: {\n fields: {\n value: {\n type: \"float\",\n id: 1\n }\n }\n },\n\n /**\n * Properties of a google.protobuf.Int64Value message.\n * @interface IInt64Value\n * @type {Object}\n * @property {number|Long} [value]\n * @memberof common\n */\n Int64Value: {\n fields: {\n value: {\n type: \"int64\",\n id: 1\n }\n }\n },\n\n /**\n * Properties of a google.protobuf.UInt64Value message.\n * @interface IUInt64Value\n * @type {Object}\n * @property {number|Long} [value]\n * @memberof common\n */\n UInt64Value: {\n fields: {\n value: {\n type: \"uint64\",\n id: 1\n }\n }\n },\n\n /**\n * Properties of a google.protobuf.Int32Value message.\n * @interface IInt32Value\n * @type {Object}\n * @property {number} [value]\n * @memberof common\n */\n Int32Value: {\n fields: {\n value: {\n type: \"int32\",\n id: 1\n }\n }\n },\n\n /**\n * Properties of a google.protobuf.UInt32Value message.\n * @interface IUInt32Value\n * @type {Object}\n * @property {number} [value]\n * @memberof common\n */\n UInt32Value: {\n fields: {\n value: {\n type: \"uint32\",\n id: 1\n }\n }\n },\n\n /**\n * Properties of a google.protobuf.BoolValue message.\n * @interface IBoolValue\n * @type {Object}\n * @property {boolean} [value]\n * @memberof common\n */\n BoolValue: {\n fields: {\n value: {\n type: \"bool\",\n id: 1\n }\n }\n },\n\n /**\n * Properties of a google.protobuf.StringValue message.\n * @interface IStringValue\n * @type {Object}\n * @property {string} [value]\n * @memberof common\n */\n StringValue: {\n fields: {\n value: {\n type: \"string\",\n id: 1\n }\n }\n },\n\n /**\n * Properties of a google.protobuf.BytesValue message.\n * @interface IBytesValue\n * @type {Object}\n * @property {Uint8Array} [value]\n * @memberof common\n */\n BytesValue: {\n fields: {\n value: {\n type: \"bytes\",\n id: 1\n }\n }\n }\n});\n\ncommon(\"field_mask\", {\n\n /**\n * Properties of a google.protobuf.FieldMask message.\n * @interface IDoubleValue\n * @type {Object}\n * @property {number} [value]\n * @memberof common\n */\n FieldMask: {\n fields: {\n paths: {\n rule: \"repeated\",\n type: \"string\",\n id: 1\n }\n }\n }\n});\n\n/**\n * Gets the root definition of the specified common proto file.\n *\n * Bundled definitions are:\n * - google/protobuf/any.proto\n * - google/protobuf/duration.proto\n * - google/protobuf/empty.proto\n * - google/protobuf/field_mask.proto\n * - google/protobuf/struct.proto\n * - google/protobuf/timestamp.proto\n * - google/protobuf/wrappers.proto\n *\n * @param {string} file Proto file name\n * @returns {INamespace|null} Root definition or `null` if not defined\n */\ncommon.get = function get(file) {\n return common[file] || null;\n};\n","\"use strict\";\n/**\n * Runtime message from/to plain object converters.\n * @namespace\n */\nvar converter = exports;\n\nvar Enum = require(15),\n util = require(37);\n\n/**\n * Generates a partial value fromObject conveter.\n * @param {Codegen} gen Codegen instance\n * @param {Field} field Reflected field\n * @param {number} fieldIndex Field index\n * @param {string} prop Property reference\n * @returns {Codegen} Codegen instance\n * @ignore\n */\nfunction genValuePartial_fromObject(gen, field, fieldIndex, prop) {\n /* eslint-disable no-unexpected-multiline, block-scoped-var, no-redeclare */\n if (field.resolvedType) {\n if (field.resolvedType instanceof Enum) { gen\n (\"switch(d%s){\", prop);\n for (var values = field.resolvedType.values, keys = Object.keys(values), i = 0; i < keys.length; ++i) {\n if (field.repeated && values[keys[i]] === field.typeDefault) gen\n (\"default:\");\n gen\n (\"case%j:\", keys[i])\n (\"case %i:\", values[keys[i]])\n (\"m%s=%j\", prop, values[keys[i]])\n (\"break\");\n } gen\n (\"}\");\n } else gen\n (\"if(typeof d%s!==\\\"object\\\")\", prop)\n (\"throw TypeError(%j)\", field.fullName + \": object expected\")\n (\"m%s=types[%i].fromObject(d%s)\", prop, fieldIndex, prop);\n } else {\n var isUnsigned = false;\n switch (field.type) {\n case \"double\":\n case \"float\": gen\n (\"m%s=Number(d%s)\", prop, prop); // also catches \"NaN\", \"Infinity\"\n break;\n case \"uint32\":\n case \"fixed32\": gen\n (\"m%s=d%s>>>0\", prop, prop);\n break;\n case \"int32\":\n case \"sint32\":\n case \"sfixed32\": gen\n (\"m%s=d%s|0\", prop, prop);\n break;\n case \"uint64\":\n isUnsigned = true;\n // eslint-disable-line no-fallthrough\n case \"int64\":\n case \"sint64\":\n case \"fixed64\":\n case \"sfixed64\": gen\n (\"if(util.Long)\")\n (\"(m%s=util.Long.fromValue(d%s)).unsigned=%j\", prop, prop, isUnsigned)\n (\"else if(typeof d%s===\\\"string\\\")\", prop)\n (\"m%s=parseInt(d%s,10)\", prop, prop)\n (\"else if(typeof d%s===\\\"number\\\")\", prop)\n (\"m%s=d%s\", prop, prop)\n (\"else if(typeof d%s===\\\"object\\\")\", prop)\n (\"m%s=new util.LongBits(d%s.low>>>0,d%s.high>>>0).toNumber(%s)\", prop, prop, prop, isUnsigned ? \"true\" : \"\");\n break;\n case \"bytes\": gen\n (\"if(typeof d%s===\\\"string\\\")\", prop)\n (\"util.base64.decode(d%s,m%s=util.newBuffer(util.base64.length(d%s)),0)\", prop, prop, prop)\n (\"else if(d%s.length)\", prop)\n (\"m%s=d%s\", prop, prop);\n break;\n case \"string\": gen\n (\"m%s=String(d%s)\", prop, prop);\n break;\n case \"bool\": gen\n (\"m%s=Boolean(d%s)\", prop, prop);\n break;\n /* default: gen\n (\"m%s=d%s\", prop, prop);\n break; */\n }\n }\n return gen;\n /* eslint-enable no-unexpected-multiline, block-scoped-var, no-redeclare */\n}\n\n/**\n * Generates a plain object to runtime message converter specific to the specified message type.\n * @param {Type} mtype Message type\n * @returns {Codegen} Codegen instance\n */\nconverter.fromObject = function fromObject(mtype) {\n /* eslint-disable no-unexpected-multiline, block-scoped-var, no-redeclare */\n var fields = mtype.fieldsArray;\n var gen = util.codegen([\"d\"], mtype.name + \"$fromObject\")\n (\"if(d instanceof this.ctor)\")\n (\"return d\");\n if (!fields.length) return gen\n (\"return new this.ctor\");\n gen\n (\"var m=new this.ctor\");\n for (var i = 0; i < fields.length; ++i) {\n var field = fields[i].resolve(),\n prop = util.safeProp(field.name);\n\n // Map fields\n if (field.map) { gen\n (\"if(d%s){\", prop)\n (\"if(typeof d%s!==\\\"object\\\")\", prop)\n (\"throw TypeError(%j)\", field.fullName + \": object expected\")\n (\"m%s={}\", prop)\n (\"for(var ks=Object.keys(d%s),i=0;i>>0,m%s.high>>>0).toNumber(%s):m%s\", prop, prop, prop, prop, isUnsigned ? \"true\": \"\", prop);\n break;\n case \"bytes\": gen\n (\"d%s=o.bytes===String?util.base64.encode(m%s,0,m%s.length):o.bytes===Array?Array.prototype.slice.call(m%s):m%s\", prop, prop, prop, prop, prop);\n break;\n default: gen\n (\"d%s=m%s\", prop, prop);\n break;\n }\n }\n return gen;\n /* eslint-enable no-unexpected-multiline, block-scoped-var, no-redeclare */\n}\n\n/**\n * Generates a runtime message to plain object converter specific to the specified message type.\n * @param {Type} mtype Message type\n * @returns {Codegen} Codegen instance\n */\nconverter.toObject = function toObject(mtype) {\n /* eslint-disable no-unexpected-multiline, block-scoped-var, no-redeclare */\n var fields = mtype.fieldsArray.slice().sort(util.compareFieldsById);\n if (!fields.length)\n return util.codegen()(\"return {}\");\n var gen = util.codegen([\"m\", \"o\"], mtype.name + \"$toObject\")\n (\"if(!o)\")\n (\"o={}\")\n (\"var d={}\");\n\n var repeatedFields = [],\n mapFields = [],\n normalFields = [],\n i = 0;\n for (; i < fields.length; ++i)\n if (!fields[i].partOf)\n ( fields[i].resolve().repeated ? repeatedFields\n : fields[i].map ? mapFields\n : normalFields).push(fields[i]);\n\n if (repeatedFields.length) { gen\n (\"if(o.arrays||o.defaults){\");\n for (i = 0; i < repeatedFields.length; ++i) gen\n (\"d%s=[]\", util.safeProp(repeatedFields[i].name));\n gen\n (\"}\");\n }\n\n if (mapFields.length) { gen\n (\"if(o.objects||o.defaults){\");\n for (i = 0; i < mapFields.length; ++i) gen\n (\"d%s={}\", util.safeProp(mapFields[i].name));\n gen\n (\"}\");\n }\n\n if (normalFields.length) { gen\n (\"if(o.defaults){\");\n for (i = 0; i < normalFields.length; ++i) {\n var field = normalFields[i],\n prop = util.safeProp(field.name);\n if (field.resolvedType instanceof Enum) gen\n (\"d%s=o.enums===String?%j:%j\", prop, field.resolvedType.valuesById[field.typeDefault], field.typeDefault);\n else if (field.long) gen\n (\"if(util.Long){\")\n (\"var n=new util.Long(%i,%i,%j)\", field.typeDefault.low, field.typeDefault.high, field.typeDefault.unsigned)\n (\"d%s=o.longs===String?n.toString():o.longs===Number?n.toNumber():n\", prop)\n (\"}else\")\n (\"d%s=o.longs===String?%j:%i\", prop, field.typeDefault.toString(), field.typeDefault.toNumber());\n else if (field.bytes) {\n var arrayDefault = \"[\" + Array.prototype.slice.call(field.typeDefault).join(\",\") + \"]\";\n gen\n (\"if(o.bytes===String)d%s=%j\", prop, String.fromCharCode.apply(String, field.typeDefault))\n (\"else{\")\n (\"d%s=%s\", prop, arrayDefault)\n (\"if(o.bytes!==Array)d%s=util.newBuffer(d%s)\", prop, prop)\n (\"}\");\n } else gen\n (\"d%s=%j\", prop, field.typeDefault); // also messages (=null)\n } gen\n (\"}\");\n }\n var hasKs2 = false;\n for (i = 0; i < fields.length; ++i) {\n var field = fields[i],\n index = mtype._fieldsArray.indexOf(field),\n prop = util.safeProp(field.name);\n if (field.map) {\n if (!hasKs2) { hasKs2 = true; gen\n (\"var ks2\");\n } gen\n (\"if(m%s&&(ks2=Object.keys(m%s)).length){\", prop, prop)\n (\"d%s={}\", prop)\n (\"for(var j=0;j>>3){\");\n\n var i = 0;\n for (; i < /* initializes */ mtype.fieldsArray.length; ++i) {\n var field = mtype._fieldsArray[i].resolve(),\n type = field.resolvedType instanceof Enum ? \"int32\" : field.type,\n ref = \"m\" + util.safeProp(field.name); gen\n (\"case %i:\", field.id);\n\n // Map fields\n if (field.map) { gen\n (\"if(%s===util.emptyObject)\", ref)\n (\"%s={}\", ref)\n (\"var c2 = r.uint32()+r.pos\");\n\n if (types.defaults[field.keyType] !== undefined) gen\n (\"k=%j\", types.defaults[field.keyType]);\n else gen\n (\"k=null\");\n\n if (types.defaults[type] !== undefined) gen\n (\"value=%j\", types.defaults[type]);\n else gen\n (\"value=null\");\n\n gen\n (\"while(r.pos>>3){\")\n (\"case 1: k=r.%s(); break\", field.keyType)\n (\"case 2:\");\n\n if (types.basic[type] === undefined) gen\n (\"value=types[%i].decode(r,r.uint32())\", i); // can't be groups\n else gen\n (\"value=r.%s()\", type);\n\n gen\n (\"break\")\n (\"default:\")\n (\"r.skipType(tag2&7)\")\n (\"break\")\n (\"}\")\n (\"}\");\n\n if (types.long[field.keyType] !== undefined) gen\n (\"%s[typeof k===\\\"object\\\"?util.longToHash(k):k]=value\", ref);\n else gen\n (\"%s[k]=value\", ref);\n\n // Repeated fields\n } else if (field.repeated) { gen\n\n (\"if(!(%s&&%s.length))\", ref, ref)\n (\"%s=[]\", ref);\n\n // Packable (always check for forward and backward compatiblity)\n if (types.packed[type] !== undefined) gen\n (\"if((t&7)===2){\")\n (\"var c2=r.uint32()+r.pos\")\n (\"while(r.pos>> 0, (field.id << 3 | 4) >>> 0)\n : gen(\"types[%i].encode(%s,w.uint32(%i).fork()).ldelim()\", fieldIndex, ref, (field.id << 3 | 2) >>> 0);\n}\n\n/**\n * Generates an encoder specific to the specified message type.\n * @param {Type} mtype Message type\n * @returns {Codegen} Codegen instance\n */\nfunction encoder(mtype) {\n /* eslint-disable no-unexpected-multiline, block-scoped-var, no-redeclare */\n var gen = util.codegen([\"m\", \"w\"], mtype.name + \"$encode\")\n (\"if(!w)\")\n (\"w=Writer.create()\");\n\n var i, ref;\n\n // \"when a message is serialized its known fields should be written sequentially by field number\"\n var fields = /* initializes */ mtype.fieldsArray.slice().sort(util.compareFieldsById);\n\n for (var i = 0; i < fields.length; ++i) {\n var field = fields[i].resolve(),\n index = mtype._fieldsArray.indexOf(field),\n type = field.resolvedType instanceof Enum ? \"int32\" : field.type,\n wireType = types.basic[type];\n ref = \"m\" + util.safeProp(field.name);\n\n // Map fields\n if (field.map) {\n gen\n (\"if(%s!=null&&Object.hasOwnProperty.call(m,%j)){\", ref, field.name) // !== undefined && !== null\n (\"for(var ks=Object.keys(%s),i=0;i>> 0, 8 | types.mapKey[field.keyType], field.keyType);\n if (wireType === undefined) gen\n (\"types[%i].encode(%s[ks[i]],w.uint32(18).fork()).ldelim().ldelim()\", index, ref); // can't be groups\n else gen\n (\".uint32(%i).%s(%s[ks[i]]).ldelim()\", 16 | wireType, type, ref);\n gen\n (\"}\")\n (\"}\");\n\n // Repeated fields\n } else if (field.repeated) { gen\n (\"if(%s!=null&&%s.length){\", ref, ref); // !== undefined && !== null\n\n // Packed repeated\n if (field.packed && types.packed[type] !== undefined) { gen\n\n (\"w.uint32(%i).fork()\", (field.id << 3 | 2) >>> 0)\n (\"for(var i=0;i<%s.length;++i)\", ref)\n (\"w.%s(%s[i])\", type, ref)\n (\"w.ldelim()\");\n\n // Non-packed\n } else { gen\n\n (\"for(var i=0;i<%s.length;++i)\", ref);\n if (wireType === undefined)\n genTypePartial(gen, field, index, ref + \"[i]\");\n else gen\n (\"w.uint32(%i).%s(%s[i])\", (field.id << 3 | wireType) >>> 0, type, ref);\n\n } gen\n (\"}\");\n\n // Non-repeated\n } else {\n if (field.optional) gen\n (\"if(%s!=null&&Object.hasOwnProperty.call(m,%j))\", ref, field.name); // !== undefined && !== null\n\n if (wireType === undefined)\n genTypePartial(gen, field, index, ref);\n else gen\n (\"w.uint32(%i).%s(%s)\", (field.id << 3 | wireType) >>> 0, type, ref);\n\n }\n }\n\n return gen\n (\"return w\");\n /* eslint-enable no-unexpected-multiline, block-scoped-var, no-redeclare */\n}\n","\"use strict\";\nmodule.exports = Enum;\n\n// extends ReflectionObject\nvar ReflectionObject = require(24);\n((Enum.prototype = Object.create(ReflectionObject.prototype)).constructor = Enum).className = \"Enum\";\n\nvar Namespace = require(23),\n util = require(37);\n\n/**\n * Constructs a new enum instance.\n * @classdesc Reflected enum.\n * @extends ReflectionObject\n * @constructor\n * @param {string} name Unique name within its namespace\n * @param {Object.} [values] Enum values as an object, by name\n * @param {Object.} [options] Declared options\n * @param {string} [comment] The comment for this enum\n * @param {Object.} [comments] The value comments for this enum\n */\nfunction Enum(name, values, options, comment, comments) {\n ReflectionObject.call(this, name, options);\n\n if (values && typeof values !== \"object\")\n throw TypeError(\"values must be an object\");\n\n /**\n * Enum values by id.\n * @type {Object.}\n */\n this.valuesById = {};\n\n /**\n * Enum values by name.\n * @type {Object.}\n */\n this.values = Object.create(this.valuesById); // toJSON, marker\n\n /**\n * Enum comment text.\n * @type {string|null}\n */\n this.comment = comment;\n\n /**\n * Value comment texts, if any.\n * @type {Object.}\n */\n this.comments = comments || {};\n\n /**\n * Reserved ranges, if any.\n * @type {Array.}\n */\n this.reserved = undefined; // toJSON\n\n // Note that values inherit valuesById on their prototype which makes them a TypeScript-\n // compatible enum. This is used by pbts to write actual enum definitions that work for\n // static and reflection code alike instead of emitting generic object definitions.\n\n if (values)\n for (var keys = Object.keys(values), i = 0; i < keys.length; ++i)\n if (typeof values[keys[i]] === \"number\") // use forward entries only\n this.valuesById[ this.values[keys[i]] = values[keys[i]] ] = keys[i];\n}\n\n/**\n * Enum descriptor.\n * @interface IEnum\n * @property {Object.} values Enum values\n * @property {Object.} [options] Enum options\n */\n\n/**\n * Constructs an enum from an enum descriptor.\n * @param {string} name Enum name\n * @param {IEnum} json Enum descriptor\n * @returns {Enum} Created enum\n * @throws {TypeError} If arguments are invalid\n */\nEnum.fromJSON = function fromJSON(name, json) {\n var enm = new Enum(name, json.values, json.options, json.comment, json.comments);\n enm.reserved = json.reserved;\n return enm;\n};\n\n/**\n * Converts this enum to an enum descriptor.\n * @param {IToJSONOptions} [toJSONOptions] JSON conversion options\n * @returns {IEnum} Enum descriptor\n */\nEnum.prototype.toJSON = function toJSON(toJSONOptions) {\n var keepComments = toJSONOptions ? Boolean(toJSONOptions.keepComments) : false;\n return util.toObject([\n \"options\" , this.options,\n \"values\" , this.values,\n \"reserved\" , this.reserved && this.reserved.length ? this.reserved : undefined,\n \"comment\" , keepComments ? this.comment : undefined,\n \"comments\" , keepComments ? this.comments : undefined\n ]);\n};\n\n/**\n * Adds a value to this enum.\n * @param {string} name Value name\n * @param {number} id Value id\n * @param {string} [comment] Comment, if any\n * @returns {Enum} `this`\n * @throws {TypeError} If arguments are invalid\n * @throws {Error} If there is already a value with this name or id\n */\nEnum.prototype.add = function add(name, id, comment) {\n // utilized by the parser but not by .fromJSON\n\n if (!util.isString(name))\n throw TypeError(\"name must be a string\");\n\n if (!util.isInteger(id))\n throw TypeError(\"id must be an integer\");\n\n if (this.values[name] !== undefined)\n throw Error(\"duplicate name '\" + name + \"' in \" + this);\n\n if (this.isReservedId(id))\n throw Error(\"id \" + id + \" is reserved in \" + this);\n\n if (this.isReservedName(name))\n throw Error(\"name '\" + name + \"' is reserved in \" + this);\n\n if (this.valuesById[id] !== undefined) {\n if (!(this.options && this.options.allow_alias))\n throw Error(\"duplicate id \" + id + \" in \" + this);\n this.values[name] = id;\n } else\n this.valuesById[this.values[name] = id] = name;\n\n this.comments[name] = comment || null;\n return this;\n};\n\n/**\n * Removes a value from this enum\n * @param {string} name Value name\n * @returns {Enum} `this`\n * @throws {TypeError} If arguments are invalid\n * @throws {Error} If `name` is not a name of this enum\n */\nEnum.prototype.remove = function remove(name) {\n\n if (!util.isString(name))\n throw TypeError(\"name must be a string\");\n\n var val = this.values[name];\n if (val == null)\n throw Error(\"name '\" + name + \"' does not exist in \" + this);\n\n delete this.valuesById[val];\n delete this.values[name];\n delete this.comments[name];\n\n return this;\n};\n\n/**\n * Tests if the specified id is reserved.\n * @param {number} id Id to test\n * @returns {boolean} `true` if reserved, otherwise `false`\n */\nEnum.prototype.isReservedId = function isReservedId(id) {\n return Namespace.isReservedId(this.reserved, id);\n};\n\n/**\n * Tests if the specified name is reserved.\n * @param {string} name Name to test\n * @returns {boolean} `true` if reserved, otherwise `false`\n */\nEnum.prototype.isReservedName = function isReservedName(name) {\n return Namespace.isReservedName(this.reserved, name);\n};\n","\"use strict\";\nmodule.exports = Field;\n\n// extends ReflectionObject\nvar ReflectionObject = require(24);\n((Field.prototype = Object.create(ReflectionObject.prototype)).constructor = Field).className = \"Field\";\n\nvar Enum = require(15),\n types = require(36),\n util = require(37);\n\nvar Type; // cyclic\n\nvar ruleRe = /^required|optional|repeated$/;\n\n/**\n * Constructs a new message field instance. Note that {@link MapField|map fields} have their own class.\n * @name Field\n * @classdesc Reflected message field.\n * @extends FieldBase\n * @constructor\n * @param {string} name Unique name within its namespace\n * @param {number} id Unique id within its namespace\n * @param {string} type Value type\n * @param {string|Object.} [rule=\"optional\"] Field rule\n * @param {string|Object.} [extend] Extended type if different from parent\n * @param {Object.} [options] Declared options\n */\n\n/**\n * Constructs a field from a field descriptor.\n * @param {string} name Field name\n * @param {IField} json Field descriptor\n * @returns {Field} Created field\n * @throws {TypeError} If arguments are invalid\n */\nField.fromJSON = function fromJSON(name, json) {\n return new Field(name, json.id, json.type, json.rule, json.extend, json.options, json.comment);\n};\n\n/**\n * Not an actual constructor. Use {@link Field} instead.\n * @classdesc Base class of all reflected message fields. This is not an actual class but here for the sake of having consistent type definitions.\n * @exports FieldBase\n * @extends ReflectionObject\n * @constructor\n * @param {string} name Unique name within its namespace\n * @param {number} id Unique id within its namespace\n * @param {string} type Value type\n * @param {string|Object.} [rule=\"optional\"] Field rule\n * @param {string|Object.} [extend] Extended type if different from parent\n * @param {Object.} [options] Declared options\n * @param {string} [comment] Comment associated with this field\n */\nfunction Field(name, id, type, rule, extend, options, comment) {\n\n if (util.isObject(rule)) {\n comment = extend;\n options = rule;\n rule = extend = undefined;\n } else if (util.isObject(extend)) {\n comment = options;\n options = extend;\n extend = undefined;\n }\n\n ReflectionObject.call(this, name, options);\n\n if (!util.isInteger(id) || id < 0)\n throw TypeError(\"id must be a non-negative integer\");\n\n if (!util.isString(type))\n throw TypeError(\"type must be a string\");\n\n if (rule !== undefined && !ruleRe.test(rule = rule.toString().toLowerCase()))\n throw TypeError(\"rule must be a string rule\");\n\n if (extend !== undefined && !util.isString(extend))\n throw TypeError(\"extend must be a string\");\n\n /**\n * Field rule, if any.\n * @type {string|undefined}\n */\n if (rule === \"proto3_optional\") {\n rule = \"optional\";\n }\n this.rule = rule && rule !== \"optional\" ? rule : undefined; // toJSON\n\n /**\n * Field type.\n * @type {string}\n */\n this.type = type; // toJSON\n\n /**\n * Unique field id.\n * @type {number}\n */\n this.id = id; // toJSON, marker\n\n /**\n * Extended type if different from parent.\n * @type {string|undefined}\n */\n this.extend = extend || undefined; // toJSON\n\n /**\n * Whether this field is required.\n * @type {boolean}\n */\n this.required = rule === \"required\";\n\n /**\n * Whether this field is optional.\n * @type {boolean}\n */\n this.optional = !this.required;\n\n /**\n * Whether this field is repeated.\n * @type {boolean}\n */\n this.repeated = rule === \"repeated\";\n\n /**\n * Whether this field is a map or not.\n * @type {boolean}\n */\n this.map = false;\n\n /**\n * Message this field belongs to.\n * @type {Type|null}\n */\n this.message = null;\n\n /**\n * OneOf this field belongs to, if any,\n * @type {OneOf|null}\n */\n this.partOf = null;\n\n /**\n * The field type's default value.\n * @type {*}\n */\n this.typeDefault = null;\n\n /**\n * The field's default value on prototypes.\n * @type {*}\n */\n this.defaultValue = null;\n\n /**\n * Whether this field's value should be treated as a long.\n * @type {boolean}\n */\n this.long = util.Long ? types.long[type] !== undefined : /* istanbul ignore next */ false;\n\n /**\n * Whether this field's value is a buffer.\n * @type {boolean}\n */\n this.bytes = type === \"bytes\";\n\n /**\n * Resolved type if not a basic type.\n * @type {Type|Enum|null}\n */\n this.resolvedType = null;\n\n /**\n * Sister-field within the extended type if a declaring extension field.\n * @type {Field|null}\n */\n this.extensionField = null;\n\n /**\n * Sister-field within the declaring namespace if an extended field.\n * @type {Field|null}\n */\n this.declaringField = null;\n\n /**\n * Internally remembers whether this field is packed.\n * @type {boolean|null}\n * @private\n */\n this._packed = null;\n\n /**\n * Comment for this field.\n * @type {string|null}\n */\n this.comment = comment;\n}\n\n/**\n * Determines whether this field is packed. Only relevant when repeated and working with proto2.\n * @name Field#packed\n * @type {boolean}\n * @readonly\n */\nObject.defineProperty(Field.prototype, \"packed\", {\n get: function() {\n // defaults to packed=true if not explicity set to false\n if (this._packed === null)\n this._packed = this.getOption(\"packed\") !== false;\n return this._packed;\n }\n});\n\n/**\n * @override\n */\nField.prototype.setOption = function setOption(name, value, ifNotSet) {\n if (name === \"packed\") // clear cached before setting\n this._packed = null;\n return ReflectionObject.prototype.setOption.call(this, name, value, ifNotSet);\n};\n\n/**\n * Field descriptor.\n * @interface IField\n * @property {string} [rule=\"optional\"] Field rule\n * @property {string} type Field type\n * @property {number} id Field id\n * @property {Object.} [options] Field options\n */\n\n/**\n * Extension field descriptor.\n * @interface IExtensionField\n * @extends IField\n * @property {string} extend Extended type\n */\n\n/**\n * Converts this field to a field descriptor.\n * @param {IToJSONOptions} [toJSONOptions] JSON conversion options\n * @returns {IField} Field descriptor\n */\nField.prototype.toJSON = function toJSON(toJSONOptions) {\n var keepComments = toJSONOptions ? Boolean(toJSONOptions.keepComments) : false;\n return util.toObject([\n \"rule\" , this.rule !== \"optional\" && this.rule || undefined,\n \"type\" , this.type,\n \"id\" , this.id,\n \"extend\" , this.extend,\n \"options\" , this.options,\n \"comment\" , keepComments ? this.comment : undefined\n ]);\n};\n\n/**\n * Resolves this field's type references.\n * @returns {Field} `this`\n * @throws {Error} If any reference cannot be resolved\n */\nField.prototype.resolve = function resolve() {\n\n if (this.resolved)\n return this;\n\n if ((this.typeDefault = types.defaults[this.type]) === undefined) { // if not a basic type, resolve it\n this.resolvedType = (this.declaringField ? this.declaringField.parent : this.parent).lookupTypeOrEnum(this.type);\n if (this.resolvedType instanceof Type)\n this.typeDefault = null;\n else // instanceof Enum\n this.typeDefault = this.resolvedType.values[Object.keys(this.resolvedType.values)[0]]; // first defined\n }\n\n // use explicitly set default value if present\n if (this.options && this.options[\"default\"] != null) {\n this.typeDefault = this.options[\"default\"];\n if (this.resolvedType instanceof Enum && typeof this.typeDefault === \"string\")\n this.typeDefault = this.resolvedType.values[this.typeDefault];\n }\n\n // remove unnecessary options\n if (this.options) {\n if (this.options.packed === true || this.options.packed !== undefined && this.resolvedType && !(this.resolvedType instanceof Enum))\n delete this.options.packed;\n if (!Object.keys(this.options).length)\n this.options = undefined;\n }\n\n // convert to internal data type if necesssary\n if (this.long) {\n this.typeDefault = util.Long.fromNumber(this.typeDefault, this.type.charAt(0) === \"u\");\n\n /* istanbul ignore else */\n if (Object.freeze)\n Object.freeze(this.typeDefault); // long instances are meant to be immutable anyway (i.e. use small int cache that even requires it)\n\n } else if (this.bytes && typeof this.typeDefault === \"string\") {\n var buf;\n if (util.base64.test(this.typeDefault))\n util.base64.decode(this.typeDefault, buf = util.newBuffer(util.base64.length(this.typeDefault)), 0);\n else\n util.utf8.write(this.typeDefault, buf = util.newBuffer(util.utf8.length(this.typeDefault)), 0);\n this.typeDefault = buf;\n }\n\n // take special care of maps and repeated fields\n if (this.map)\n this.defaultValue = util.emptyObject;\n else if (this.repeated)\n this.defaultValue = util.emptyArray;\n else\n this.defaultValue = this.typeDefault;\n\n // ensure proper value on prototype\n if (this.parent instanceof Type)\n this.parent.ctor.prototype[this.name] = this.defaultValue;\n\n return ReflectionObject.prototype.resolve.call(this);\n};\n\n/**\n * Decorator function as returned by {@link Field.d} and {@link MapField.d} (TypeScript).\n * @typedef FieldDecorator\n * @type {function}\n * @param {Object} prototype Target prototype\n * @param {string} fieldName Field name\n * @returns {undefined}\n */\n\n/**\n * Field decorator (TypeScript).\n * @name Field.d\n * @function\n * @param {number} fieldId Field id\n * @param {\"double\"|\"float\"|\"int32\"|\"uint32\"|\"sint32\"|\"fixed32\"|\"sfixed32\"|\"int64\"|\"uint64\"|\"sint64\"|\"fixed64\"|\"sfixed64\"|\"string\"|\"bool\"|\"bytes\"|Object} fieldType Field type\n * @param {\"optional\"|\"required\"|\"repeated\"} [fieldRule=\"optional\"] Field rule\n * @param {T} [defaultValue] Default value\n * @returns {FieldDecorator} Decorator function\n * @template T extends number | number[] | Long | Long[] | string | string[] | boolean | boolean[] | Uint8Array | Uint8Array[] | Buffer | Buffer[]\n */\nField.d = function decorateField(fieldId, fieldType, fieldRule, defaultValue) {\n\n // submessage: decorate the submessage and use its name as the type\n if (typeof fieldType === \"function\")\n fieldType = util.decorateType(fieldType).name;\n\n // enum reference: create a reflected copy of the enum and keep reuseing it\n else if (fieldType && typeof fieldType === \"object\")\n fieldType = util.decorateEnum(fieldType).name;\n\n return function fieldDecorator(prototype, fieldName) {\n util.decorateType(prototype.constructor)\n .add(new Field(fieldName, fieldId, fieldType, fieldRule, { \"default\": defaultValue }));\n };\n};\n\n/**\n * Field decorator (TypeScript).\n * @name Field.d\n * @function\n * @param {number} fieldId Field id\n * @param {Constructor|string} fieldType Field type\n * @param {\"optional\"|\"required\"|\"repeated\"} [fieldRule=\"optional\"] Field rule\n * @returns {FieldDecorator} Decorator function\n * @template T extends Message\n * @variation 2\n */\n// like Field.d but without a default value\n\n// Sets up cyclic dependencies (called in index-light)\nField._configure = function configure(Type_) {\n Type = Type_;\n};\n","\"use strict\";\nvar protobuf = module.exports = require(18);\n\nprotobuf.build = \"light\";\n\n/**\n * A node-style callback as used by {@link load} and {@link Root#load}.\n * @typedef LoadCallback\n * @type {function}\n * @param {Error|null} error Error, if any, otherwise `null`\n * @param {Root} [root] Root, if there hasn't been an error\n * @returns {undefined}\n */\n\n/**\n * Loads one or multiple .proto or preprocessed .json files into a common root namespace and calls the callback.\n * @param {string|string[]} filename One or multiple files to load\n * @param {Root} root Root namespace, defaults to create a new one if omitted.\n * @param {LoadCallback} callback Callback function\n * @returns {undefined}\n * @see {@link Root#load}\n */\nfunction load(filename, root, callback) {\n if (typeof root === \"function\") {\n callback = root;\n root = new protobuf.Root();\n } else if (!root)\n root = new protobuf.Root();\n return root.load(filename, callback);\n}\n\n/**\n * Loads one or multiple .proto or preprocessed .json files into a common root namespace and calls the callback.\n * @name load\n * @function\n * @param {string|string[]} filename One or multiple files to load\n * @param {LoadCallback} callback Callback function\n * @returns {undefined}\n * @see {@link Root#load}\n * @variation 2\n */\n// function load(filename:string, callback:LoadCallback):undefined\n\n/**\n * Loads one or multiple .proto or preprocessed .json files into a common root namespace and returns a promise.\n * @name load\n * @function\n * @param {string|string[]} filename One or multiple files to load\n * @param {Root} [root] Root namespace, defaults to create a new one if omitted.\n * @returns {Promise} Promise\n * @see {@link Root#load}\n * @variation 3\n */\n// function load(filename:string, [root:Root]):Promise\n\nprotobuf.load = load;\n\n/**\n * Synchronously loads one or multiple .proto or preprocessed .json files into a common root namespace (node only).\n * @param {string|string[]} filename One or multiple files to load\n * @param {Root} [root] Root namespace, defaults to create a new one if omitted.\n * @returns {Root} Root namespace\n * @throws {Error} If synchronous fetching is not supported (i.e. in browsers) or if a file's syntax is invalid\n * @see {@link Root#loadSync}\n */\nfunction loadSync(filename, root) {\n if (!root)\n root = new protobuf.Root();\n return root.loadSync(filename);\n}\n\nprotobuf.loadSync = loadSync;\n\n// Serialization\nprotobuf.encoder = require(14);\nprotobuf.decoder = require(13);\nprotobuf.verifier = require(40);\nprotobuf.converter = require(12);\n\n// Reflection\nprotobuf.ReflectionObject = require(24);\nprotobuf.Namespace = require(23);\nprotobuf.Root = require(29);\nprotobuf.Enum = require(15);\nprotobuf.Type = require(35);\nprotobuf.Field = require(16);\nprotobuf.OneOf = require(25);\nprotobuf.MapField = require(20);\nprotobuf.Service = require(33);\nprotobuf.Method = require(22);\n\n// Runtime\nprotobuf.Message = require(21);\nprotobuf.wrappers = require(41);\n\n// Utility\nprotobuf.types = require(36);\nprotobuf.util = require(37);\n\n// Set up possibly cyclic reflection dependencies\nprotobuf.ReflectionObject._configure(protobuf.Root);\nprotobuf.Namespace._configure(protobuf.Type, protobuf.Service, protobuf.Enum);\nprotobuf.Root._configure(protobuf.Type);\nprotobuf.Field._configure(protobuf.Type);\n","\"use strict\";\nvar protobuf = exports;\n\n/**\n * Build type, one of `\"full\"`, `\"light\"` or `\"minimal\"`.\n * @name build\n * @type {string}\n * @const\n */\nprotobuf.build = \"minimal\";\n\n// Serialization\nprotobuf.Writer = require(42);\nprotobuf.BufferWriter = require(43);\nprotobuf.Reader = require(27);\nprotobuf.BufferReader = require(28);\n\n// Utility\nprotobuf.util = require(39);\nprotobuf.rpc = require(31);\nprotobuf.roots = require(30);\nprotobuf.configure = configure;\n\n/* istanbul ignore next */\n/**\n * Reconfigures the library according to the environment.\n * @returns {undefined}\n */\nfunction configure() {\n protobuf.util._configure();\n protobuf.Writer._configure(protobuf.BufferWriter);\n protobuf.Reader._configure(protobuf.BufferReader);\n}\n\n// Set up buffer utility according to the environment\nconfigure();\n","\"use strict\";\nvar protobuf = module.exports = require(17);\n\nprotobuf.build = \"full\";\n\n// Parser\nprotobuf.tokenize = require(34);\nprotobuf.parse = require(26);\nprotobuf.common = require(11);\n\n// Configure parser\nprotobuf.Root._configure(protobuf.Type, protobuf.parse, protobuf.common);\n","\"use strict\";\nmodule.exports = MapField;\n\n// extends Field\nvar Field = require(16);\n((MapField.prototype = Object.create(Field.prototype)).constructor = MapField).className = \"MapField\";\n\nvar types = require(36),\n util = require(37);\n\n/**\n * Constructs a new map field instance.\n * @classdesc Reflected map field.\n * @extends FieldBase\n * @constructor\n * @param {string} name Unique name within its namespace\n * @param {number} id Unique id within its namespace\n * @param {string} keyType Key type\n * @param {string} type Value type\n * @param {Object.} [options] Declared options\n * @param {string} [comment] Comment associated with this field\n */\nfunction MapField(name, id, keyType, type, options, comment) {\n Field.call(this, name, id, type, undefined, undefined, options, comment);\n\n /* istanbul ignore if */\n if (!util.isString(keyType))\n throw TypeError(\"keyType must be a string\");\n\n /**\n * Key type.\n * @type {string}\n */\n this.keyType = keyType; // toJSON, marker\n\n /**\n * Resolved key type if not a basic type.\n * @type {ReflectionObject|null}\n */\n this.resolvedKeyType = null;\n\n // Overrides Field#map\n this.map = true;\n}\n\n/**\n * Map field descriptor.\n * @interface IMapField\n * @extends {IField}\n * @property {string} keyType Key type\n */\n\n/**\n * Extension map field descriptor.\n * @interface IExtensionMapField\n * @extends IMapField\n * @property {string} extend Extended type\n */\n\n/**\n * Constructs a map field from a map field descriptor.\n * @param {string} name Field name\n * @param {IMapField} json Map field descriptor\n * @returns {MapField} Created map field\n * @throws {TypeError} If arguments are invalid\n */\nMapField.fromJSON = function fromJSON(name, json) {\n return new MapField(name, json.id, json.keyType, json.type, json.options, json.comment);\n};\n\n/**\n * Converts this map field to a map field descriptor.\n * @param {IToJSONOptions} [toJSONOptions] JSON conversion options\n * @returns {IMapField} Map field descriptor\n */\nMapField.prototype.toJSON = function toJSON(toJSONOptions) {\n var keepComments = toJSONOptions ? Boolean(toJSONOptions.keepComments) : false;\n return util.toObject([\n \"keyType\" , this.keyType,\n \"type\" , this.type,\n \"id\" , this.id,\n \"extend\" , this.extend,\n \"options\" , this.options,\n \"comment\" , keepComments ? this.comment : undefined\n ]);\n};\n\n/**\n * @override\n */\nMapField.prototype.resolve = function resolve() {\n if (this.resolved)\n return this;\n\n // Besides a value type, map fields have a key type that may be \"any scalar type except for floating point types and bytes\"\n if (types.mapKey[this.keyType] === undefined)\n throw Error(\"invalid key type: \" + this.keyType);\n\n return Field.prototype.resolve.call(this);\n};\n\n/**\n * Map field decorator (TypeScript).\n * @name MapField.d\n * @function\n * @param {number} fieldId Field id\n * @param {\"int32\"|\"uint32\"|\"sint32\"|\"fixed32\"|\"sfixed32\"|\"int64\"|\"uint64\"|\"sint64\"|\"fixed64\"|\"sfixed64\"|\"bool\"|\"string\"} fieldKeyType Field key type\n * @param {\"double\"|\"float\"|\"int32\"|\"uint32\"|\"sint32\"|\"fixed32\"|\"sfixed32\"|\"int64\"|\"uint64\"|\"sint64\"|\"fixed64\"|\"sfixed64\"|\"bool\"|\"string\"|\"bytes\"|Object|Constructor<{}>} fieldValueType Field value type\n * @returns {FieldDecorator} Decorator function\n * @template T extends { [key: string]: number | Long | string | boolean | Uint8Array | Buffer | number[] | Message<{}> }\n */\nMapField.d = function decorateMapField(fieldId, fieldKeyType, fieldValueType) {\n\n // submessage value: decorate the submessage and use its name as the type\n if (typeof fieldValueType === \"function\")\n fieldValueType = util.decorateType(fieldValueType).name;\n\n // enum reference value: create a reflected copy of the enum and keep reuseing it\n else if (fieldValueType && typeof fieldValueType === \"object\")\n fieldValueType = util.decorateEnum(fieldValueType).name;\n\n return function mapFieldDecorator(prototype, fieldName) {\n util.decorateType(prototype.constructor)\n .add(new MapField(fieldName, fieldId, fieldKeyType, fieldValueType));\n };\n};\n","\"use strict\";\nmodule.exports = Message;\n\nvar util = require(39);\n\n/**\n * Constructs a new message instance.\n * @classdesc Abstract runtime message.\n * @constructor\n * @param {Properties} [properties] Properties to set\n * @template T extends object = object\n */\nfunction Message(properties) {\n // not used internally\n if (properties)\n for (var keys = Object.keys(properties), i = 0; i < keys.length; ++i)\n this[keys[i]] = properties[keys[i]];\n}\n\n/**\n * Reference to the reflected type.\n * @name Message.$type\n * @type {Type}\n * @readonly\n */\n\n/**\n * Reference to the reflected type.\n * @name Message#$type\n * @type {Type}\n * @readonly\n */\n\n/*eslint-disable valid-jsdoc*/\n\n/**\n * Creates a new message of this type using the specified properties.\n * @param {Object.} [properties] Properties to set\n * @returns {Message} Message instance\n * @template T extends Message\n * @this Constructor\n */\nMessage.create = function create(properties) {\n return this.$type.create(properties);\n};\n\n/**\n * Encodes a message of this type.\n * @param {T|Object.} message Message to encode\n * @param {Writer} [writer] Writer to use\n * @returns {Writer} Writer\n * @template T extends Message\n * @this Constructor\n */\nMessage.encode = function encode(message, writer) {\n return this.$type.encode(message, writer);\n};\n\n/**\n * Encodes a message of this type preceeded by its length as a varint.\n * @param {T|Object.} message Message to encode\n * @param {Writer} [writer] Writer to use\n * @returns {Writer} Writer\n * @template T extends Message\n * @this Constructor\n */\nMessage.encodeDelimited = function encodeDelimited(message, writer) {\n return this.$type.encodeDelimited(message, writer);\n};\n\n/**\n * Decodes a message of this type.\n * @name Message.decode\n * @function\n * @param {Reader|Uint8Array} reader Reader or buffer to decode\n * @returns {T} Decoded message\n * @template T extends Message\n * @this Constructor\n */\nMessage.decode = function decode(reader) {\n return this.$type.decode(reader);\n};\n\n/**\n * Decodes a message of this type preceeded by its length as a varint.\n * @name Message.decodeDelimited\n * @function\n * @param {Reader|Uint8Array} reader Reader or buffer to decode\n * @returns {T} Decoded message\n * @template T extends Message\n * @this Constructor\n */\nMessage.decodeDelimited = function decodeDelimited(reader) {\n return this.$type.decodeDelimited(reader);\n};\n\n/**\n * Verifies a message of this type.\n * @name Message.verify\n * @function\n * @param {Object.} message Plain object to verify\n * @returns {string|null} `null` if valid, otherwise the reason why it is not\n */\nMessage.verify = function verify(message) {\n return this.$type.verify(message);\n};\n\n/**\n * Creates a new message of this type from a plain object. Also converts values to their respective internal types.\n * @param {Object.} object Plain object\n * @returns {T} Message instance\n * @template T extends Message\n * @this Constructor\n */\nMessage.fromObject = function fromObject(object) {\n return this.$type.fromObject(object);\n};\n\n/**\n * Creates a plain object from a message of this type. Also converts values to other types if specified.\n * @param {T} message Message instance\n * @param {IConversionOptions} [options] Conversion options\n * @returns {Object.} Plain object\n * @template T extends Message\n * @this Constructor\n */\nMessage.toObject = function toObject(message, options) {\n return this.$type.toObject(message, options);\n};\n\n/**\n * Converts this message to JSON.\n * @returns {Object.} JSON object\n */\nMessage.prototype.toJSON = function toJSON() {\n return this.$type.toObject(this, util.toJSONOptions);\n};\n\n/*eslint-enable valid-jsdoc*/","\"use strict\";\nmodule.exports = Method;\n\n// extends ReflectionObject\nvar ReflectionObject = require(24);\n((Method.prototype = Object.create(ReflectionObject.prototype)).constructor = Method).className = \"Method\";\n\nvar util = require(37);\n\n/**\n * Constructs a new service method instance.\n * @classdesc Reflected service method.\n * @extends ReflectionObject\n * @constructor\n * @param {string} name Method name\n * @param {string|undefined} type Method type, usually `\"rpc\"`\n * @param {string} requestType Request message type\n * @param {string} responseType Response message type\n * @param {boolean|Object.} [requestStream] Whether the request is streamed\n * @param {boolean|Object.} [responseStream] Whether the response is streamed\n * @param {Object.} [options] Declared options\n * @param {string} [comment] The comment for this method\n * @param {Object.} [parsedOptions] Declared options, properly parsed into an object\n */\nfunction Method(name, type, requestType, responseType, requestStream, responseStream, options, comment, parsedOptions) {\n\n /* istanbul ignore next */\n if (util.isObject(requestStream)) {\n options = requestStream;\n requestStream = responseStream = undefined;\n } else if (util.isObject(responseStream)) {\n options = responseStream;\n responseStream = undefined;\n }\n\n /* istanbul ignore if */\n if (!(type === undefined || util.isString(type)))\n throw TypeError(\"type must be a string\");\n\n /* istanbul ignore if */\n if (!util.isString(requestType))\n throw TypeError(\"requestType must be a string\");\n\n /* istanbul ignore if */\n if (!util.isString(responseType))\n throw TypeError(\"responseType must be a string\");\n\n ReflectionObject.call(this, name, options);\n\n /**\n * Method type.\n * @type {string}\n */\n this.type = type || \"rpc\"; // toJSON\n\n /**\n * Request type.\n * @type {string}\n */\n this.requestType = requestType; // toJSON, marker\n\n /**\n * Whether requests are streamed or not.\n * @type {boolean|undefined}\n */\n this.requestStream = requestStream ? true : undefined; // toJSON\n\n /**\n * Response type.\n * @type {string}\n */\n this.responseType = responseType; // toJSON\n\n /**\n * Whether responses are streamed or not.\n * @type {boolean|undefined}\n */\n this.responseStream = responseStream ? true : undefined; // toJSON\n\n /**\n * Resolved request type.\n * @type {Type|null}\n */\n this.resolvedRequestType = null;\n\n /**\n * Resolved response type.\n * @type {Type|null}\n */\n this.resolvedResponseType = null;\n\n /**\n * Comment for this method\n * @type {string|null}\n */\n this.comment = comment;\n\n /**\n * Options properly parsed into an object\n */\n this.parsedOptions = parsedOptions;\n}\n\n/**\n * Method descriptor.\n * @interface IMethod\n * @property {string} [type=\"rpc\"] Method type\n * @property {string} requestType Request type\n * @property {string} responseType Response type\n * @property {boolean} [requestStream=false] Whether requests are streamed\n * @property {boolean} [responseStream=false] Whether responses are streamed\n * @property {Object.} [options] Method options\n * @property {string} comment Method comments\n * @property {Object.} [parsedOptions] Method options properly parsed into an object\n */\n\n/**\n * Constructs a method from a method descriptor.\n * @param {string} name Method name\n * @param {IMethod} json Method descriptor\n * @returns {Method} Created method\n * @throws {TypeError} If arguments are invalid\n */\nMethod.fromJSON = function fromJSON(name, json) {\n return new Method(name, json.type, json.requestType, json.responseType, json.requestStream, json.responseStream, json.options, json.comment, json.parsedOptions);\n};\n\n/**\n * Converts this method to a method descriptor.\n * @param {IToJSONOptions} [toJSONOptions] JSON conversion options\n * @returns {IMethod} Method descriptor\n */\nMethod.prototype.toJSON = function toJSON(toJSONOptions) {\n var keepComments = toJSONOptions ? Boolean(toJSONOptions.keepComments) : false;\n return util.toObject([\n \"type\" , this.type !== \"rpc\" && /* istanbul ignore next */ this.type || undefined,\n \"requestType\" , this.requestType,\n \"requestStream\" , this.requestStream,\n \"responseType\" , this.responseType,\n \"responseStream\" , this.responseStream,\n \"options\" , this.options,\n \"comment\" , keepComments ? this.comment : undefined,\n \"parsedOptions\" , this.parsedOptions,\n ]);\n};\n\n/**\n * @override\n */\nMethod.prototype.resolve = function resolve() {\n\n /* istanbul ignore if */\n if (this.resolved)\n return this;\n\n this.resolvedRequestType = this.parent.lookupType(this.requestType);\n this.resolvedResponseType = this.parent.lookupType(this.responseType);\n\n return ReflectionObject.prototype.resolve.call(this);\n};\n","\"use strict\";\nmodule.exports = Namespace;\n\n// extends ReflectionObject\nvar ReflectionObject = require(24);\n((Namespace.prototype = Object.create(ReflectionObject.prototype)).constructor = Namespace).className = \"Namespace\";\n\nvar Field = require(16),\n util = require(37);\n\nvar Type, // cyclic\n Service,\n Enum;\n\n/**\n * Constructs a new namespace instance.\n * @name Namespace\n * @classdesc Reflected namespace.\n * @extends NamespaceBase\n * @constructor\n * @param {string} name Namespace name\n * @param {Object.} [options] Declared options\n */\n\n/**\n * Constructs a namespace from JSON.\n * @memberof Namespace\n * @function\n * @param {string} name Namespace name\n * @param {Object.} json JSON object\n * @returns {Namespace} Created namespace\n * @throws {TypeError} If arguments are invalid\n */\nNamespace.fromJSON = function fromJSON(name, json) {\n return new Namespace(name, json.options).addJSON(json.nested);\n};\n\n/**\n * Converts an array of reflection objects to JSON.\n * @memberof Namespace\n * @param {ReflectionObject[]} array Object array\n * @param {IToJSONOptions} [toJSONOptions] JSON conversion options\n * @returns {Object.|undefined} JSON object or `undefined` when array is empty\n */\nfunction arrayToJSON(array, toJSONOptions) {\n if (!(array && array.length))\n return undefined;\n var obj = {};\n for (var i = 0; i < array.length; ++i)\n obj[array[i].name] = array[i].toJSON(toJSONOptions);\n return obj;\n}\n\nNamespace.arrayToJSON = arrayToJSON;\n\n/**\n * Tests if the specified id is reserved.\n * @param {Array.|undefined} reserved Array of reserved ranges and names\n * @param {number} id Id to test\n * @returns {boolean} `true` if reserved, otherwise `false`\n */\nNamespace.isReservedId = function isReservedId(reserved, id) {\n if (reserved)\n for (var i = 0; i < reserved.length; ++i)\n if (typeof reserved[i] !== \"string\" && reserved[i][0] <= id && reserved[i][1] > id)\n return true;\n return false;\n};\n\n/**\n * Tests if the specified name is reserved.\n * @param {Array.|undefined} reserved Array of reserved ranges and names\n * @param {string} name Name to test\n * @returns {boolean} `true` if reserved, otherwise `false`\n */\nNamespace.isReservedName = function isReservedName(reserved, name) {\n if (reserved)\n for (var i = 0; i < reserved.length; ++i)\n if (reserved[i] === name)\n return true;\n return false;\n};\n\n/**\n * Not an actual constructor. Use {@link Namespace} instead.\n * @classdesc Base class of all reflection objects containing nested objects. This is not an actual class but here for the sake of having consistent type definitions.\n * @exports NamespaceBase\n * @extends ReflectionObject\n * @abstract\n * @constructor\n * @param {string} name Namespace name\n * @param {Object.} [options] Declared options\n * @see {@link Namespace}\n */\nfunction Namespace(name, options) {\n ReflectionObject.call(this, name, options);\n\n /**\n * Nested objects by name.\n * @type {Object.|undefined}\n */\n this.nested = undefined; // toJSON\n\n /**\n * Cached nested objects as an array.\n * @type {ReflectionObject[]|null}\n * @private\n */\n this._nestedArray = null;\n}\n\nfunction clearCache(namespace) {\n namespace._nestedArray = null;\n return namespace;\n}\n\n/**\n * Nested objects of this namespace as an array for iteration.\n * @name NamespaceBase#nestedArray\n * @type {ReflectionObject[]}\n * @readonly\n */\nObject.defineProperty(Namespace.prototype, \"nestedArray\", {\n get: function() {\n return this._nestedArray || (this._nestedArray = util.toArray(this.nested));\n }\n});\n\n/**\n * Namespace descriptor.\n * @interface INamespace\n * @property {Object.} [options] Namespace options\n * @property {Object.} [nested] Nested object descriptors\n */\n\n/**\n * Any extension field descriptor.\n * @typedef AnyExtensionField\n * @type {IExtensionField|IExtensionMapField}\n */\n\n/**\n * Any nested object descriptor.\n * @typedef AnyNestedObject\n * @type {IEnum|IType|IService|AnyExtensionField|INamespace}\n */\n// ^ BEWARE: VSCode hangs forever when using more than 5 types (that's why AnyExtensionField exists in the first place)\n\n/**\n * Converts this namespace to a namespace descriptor.\n * @param {IToJSONOptions} [toJSONOptions] JSON conversion options\n * @returns {INamespace} Namespace descriptor\n */\nNamespace.prototype.toJSON = function toJSON(toJSONOptions) {\n return util.toObject([\n \"options\" , this.options,\n \"nested\" , arrayToJSON(this.nestedArray, toJSONOptions)\n ]);\n};\n\n/**\n * Adds nested objects to this namespace from nested object descriptors.\n * @param {Object.} nestedJson Any nested object descriptors\n * @returns {Namespace} `this`\n */\nNamespace.prototype.addJSON = function addJSON(nestedJson) {\n var ns = this;\n /* istanbul ignore else */\n if (nestedJson) {\n for (var names = Object.keys(nestedJson), i = 0, nested; i < names.length; ++i) {\n nested = nestedJson[names[i]];\n ns.add( // most to least likely\n ( nested.fields !== undefined\n ? Type.fromJSON\n : nested.values !== undefined\n ? Enum.fromJSON\n : nested.methods !== undefined\n ? Service.fromJSON\n : nested.id !== undefined\n ? Field.fromJSON\n : Namespace.fromJSON )(names[i], nested)\n );\n }\n }\n return this;\n};\n\n/**\n * Gets the nested object of the specified name.\n * @param {string} name Nested object name\n * @returns {ReflectionObject|null} The reflection object or `null` if it doesn't exist\n */\nNamespace.prototype.get = function get(name) {\n return this.nested && this.nested[name]\n || null;\n};\n\n/**\n * Gets the values of the nested {@link Enum|enum} of the specified name.\n * This methods differs from {@link Namespace#get|get} in that it returns an enum's values directly and throws instead of returning `null`.\n * @param {string} name Nested enum name\n * @returns {Object.} Enum values\n * @throws {Error} If there is no such enum\n */\nNamespace.prototype.getEnum = function getEnum(name) {\n if (this.nested && this.nested[name] instanceof Enum)\n return this.nested[name].values;\n throw Error(\"no such enum: \" + name);\n};\n\n/**\n * Adds a nested object to this namespace.\n * @param {ReflectionObject} object Nested object to add\n * @returns {Namespace} `this`\n * @throws {TypeError} If arguments are invalid\n * @throws {Error} If there is already a nested object with this name\n */\nNamespace.prototype.add = function add(object) {\n\n if (!(object instanceof Field && object.extend !== undefined || object instanceof Type || object instanceof Enum || object instanceof Service || object instanceof Namespace))\n throw TypeError(\"object must be a valid nested object\");\n\n if (!this.nested)\n this.nested = {};\n else {\n var prev = this.get(object.name);\n if (prev) {\n if (prev instanceof Namespace && object instanceof Namespace && !(prev instanceof Type || prev instanceof Service)) {\n // replace plain namespace but keep existing nested elements and options\n var nested = prev.nestedArray;\n for (var i = 0; i < nested.length; ++i)\n object.add(nested[i]);\n this.remove(prev);\n if (!this.nested)\n this.nested = {};\n object.setOptions(prev.options, true);\n\n } else\n throw Error(\"duplicate name '\" + object.name + \"' in \" + this);\n }\n }\n this.nested[object.name] = object;\n object.onAdd(this);\n return clearCache(this);\n};\n\n/**\n * Removes a nested object from this namespace.\n * @param {ReflectionObject} object Nested object to remove\n * @returns {Namespace} `this`\n * @throws {TypeError} If arguments are invalid\n * @throws {Error} If `object` is not a member of this namespace\n */\nNamespace.prototype.remove = function remove(object) {\n\n if (!(object instanceof ReflectionObject))\n throw TypeError(\"object must be a ReflectionObject\");\n if (object.parent !== this)\n throw Error(object + \" is not a member of \" + this);\n\n delete this.nested[object.name];\n if (!Object.keys(this.nested).length)\n this.nested = undefined;\n\n object.onRemove(this);\n return clearCache(this);\n};\n\n/**\n * Defines additial namespaces within this one if not yet existing.\n * @param {string|string[]} path Path to create\n * @param {*} [json] Nested types to create from JSON\n * @returns {Namespace} Pointer to the last namespace created or `this` if path is empty\n */\nNamespace.prototype.define = function define(path, json) {\n\n if (util.isString(path))\n path = path.split(\".\");\n else if (!Array.isArray(path))\n throw TypeError(\"illegal path\");\n if (path && path.length && path[0] === \"\")\n throw Error(\"path must be relative\");\n\n var ptr = this;\n while (path.length > 0) {\n var part = path.shift();\n if (ptr.nested && ptr.nested[part]) {\n ptr = ptr.nested[part];\n if (!(ptr instanceof Namespace))\n throw Error(\"path conflicts with non-namespace objects\");\n } else\n ptr.add(ptr = new Namespace(part));\n }\n if (json)\n ptr.addJSON(json);\n return ptr;\n};\n\n/**\n * Resolves this namespace's and all its nested objects' type references. Useful to validate a reflection tree, but comes at a cost.\n * @returns {Namespace} `this`\n */\nNamespace.prototype.resolveAll = function resolveAll() {\n var nested = this.nestedArray, i = 0;\n while (i < nested.length)\n if (nested[i] instanceof Namespace)\n nested[i++].resolveAll();\n else\n nested[i++].resolve();\n return this.resolve();\n};\n\n/**\n * Recursively looks up the reflection object matching the specified path in the scope of this namespace.\n * @param {string|string[]} path Path to look up\n * @param {*|Array.<*>} filterTypes Filter types, any combination of the constructors of `protobuf.Type`, `protobuf.Enum`, `protobuf.Service` etc.\n * @param {boolean} [parentAlreadyChecked=false] If known, whether the parent has already been checked\n * @returns {ReflectionObject|null} Looked up object or `null` if none could be found\n */\nNamespace.prototype.lookup = function lookup(path, filterTypes, parentAlreadyChecked) {\n\n /* istanbul ignore next */\n if (typeof filterTypes === \"boolean\") {\n parentAlreadyChecked = filterTypes;\n filterTypes = undefined;\n } else if (filterTypes && !Array.isArray(filterTypes))\n filterTypes = [ filterTypes ];\n\n if (util.isString(path) && path.length) {\n if (path === \".\")\n return this.root;\n path = path.split(\".\");\n } else if (!path.length)\n return this;\n\n // Start at root if path is absolute\n if (path[0] === \"\")\n return this.root.lookup(path.slice(1), filterTypes);\n\n // Test if the first part matches any nested object, and if so, traverse if path contains more\n var found = this.get(path[0]);\n if (found) {\n if (path.length === 1) {\n if (!filterTypes || filterTypes.indexOf(found.constructor) > -1)\n return found;\n } else if (found instanceof Namespace && (found = found.lookup(path.slice(1), filterTypes, true)))\n return found;\n\n // Otherwise try each nested namespace\n } else\n for (var i = 0; i < this.nestedArray.length; ++i)\n if (this._nestedArray[i] instanceof Namespace && (found = this._nestedArray[i].lookup(path, filterTypes, true)))\n return found;\n\n // If there hasn't been a match, try again at the parent\n if (this.parent === null || parentAlreadyChecked)\n return null;\n return this.parent.lookup(path, filterTypes);\n};\n\n/**\n * Looks up the reflection object at the specified path, relative to this namespace.\n * @name NamespaceBase#lookup\n * @function\n * @param {string|string[]} path Path to look up\n * @param {boolean} [parentAlreadyChecked=false] Whether the parent has already been checked\n * @returns {ReflectionObject|null} Looked up object or `null` if none could be found\n * @variation 2\n */\n// lookup(path: string, [parentAlreadyChecked: boolean])\n\n/**\n * Looks up the {@link Type|type} at the specified path, relative to this namespace.\n * Besides its signature, this methods differs from {@link Namespace#lookup|lookup} in that it throws instead of returning `null`.\n * @param {string|string[]} path Path to look up\n * @returns {Type} Looked up type\n * @throws {Error} If `path` does not point to a type\n */\nNamespace.prototype.lookupType = function lookupType(path) {\n var found = this.lookup(path, [ Type ]);\n if (!found)\n throw Error(\"no such type: \" + path);\n return found;\n};\n\n/**\n * Looks up the values of the {@link Enum|enum} at the specified path, relative to this namespace.\n * Besides its signature, this methods differs from {@link Namespace#lookup|lookup} in that it throws instead of returning `null`.\n * @param {string|string[]} path Path to look up\n * @returns {Enum} Looked up enum\n * @throws {Error} If `path` does not point to an enum\n */\nNamespace.prototype.lookupEnum = function lookupEnum(path) {\n var found = this.lookup(path, [ Enum ]);\n if (!found)\n throw Error(\"no such Enum '\" + path + \"' in \" + this);\n return found;\n};\n\n/**\n * Looks up the {@link Type|type} or {@link Enum|enum} at the specified path, relative to this namespace.\n * Besides its signature, this methods differs from {@link Namespace#lookup|lookup} in that it throws instead of returning `null`.\n * @param {string|string[]} path Path to look up\n * @returns {Type} Looked up type or enum\n * @throws {Error} If `path` does not point to a type or enum\n */\nNamespace.prototype.lookupTypeOrEnum = function lookupTypeOrEnum(path) {\n var found = this.lookup(path, [ Type, Enum ]);\n if (!found)\n throw Error(\"no such Type or Enum '\" + path + \"' in \" + this);\n return found;\n};\n\n/**\n * Looks up the {@link Service|service} at the specified path, relative to this namespace.\n * Besides its signature, this methods differs from {@link Namespace#lookup|lookup} in that it throws instead of returning `null`.\n * @param {string|string[]} path Path to look up\n * @returns {Service} Looked up service\n * @throws {Error} If `path` does not point to a service\n */\nNamespace.prototype.lookupService = function lookupService(path) {\n var found = this.lookup(path, [ Service ]);\n if (!found)\n throw Error(\"no such Service '\" + path + \"' in \" + this);\n return found;\n};\n\n// Sets up cyclic dependencies (called in index-light)\nNamespace._configure = function(Type_, Service_, Enum_) {\n Type = Type_;\n Service = Service_;\n Enum = Enum_;\n};\n","\"use strict\";\nmodule.exports = ReflectionObject;\n\nReflectionObject.className = \"ReflectionObject\";\n\nvar util = require(37);\n\nvar Root; // cyclic\n\n/**\n * Constructs a new reflection object instance.\n * @classdesc Base class of all reflection objects.\n * @constructor\n * @param {string} name Object name\n * @param {Object.} [options] Declared options\n * @abstract\n */\nfunction ReflectionObject(name, options) {\n\n if (!util.isString(name))\n throw TypeError(\"name must be a string\");\n\n if (options && !util.isObject(options))\n throw TypeError(\"options must be an object\");\n\n /**\n * Options.\n * @type {Object.|undefined}\n */\n this.options = options; // toJSON\n\n /**\n * Parsed Options.\n * @type {Array.>|undefined}\n */\n this.parsedOptions = null;\n\n /**\n * Unique name within its namespace.\n * @type {string}\n */\n this.name = name;\n\n /**\n * Parent namespace.\n * @type {Namespace|null}\n */\n this.parent = null;\n\n /**\n * Whether already resolved or not.\n * @type {boolean}\n */\n this.resolved = false;\n\n /**\n * Comment text, if any.\n * @type {string|null}\n */\n this.comment = null;\n\n /**\n * Defining file name.\n * @type {string|null}\n */\n this.filename = null;\n}\n\nObject.defineProperties(ReflectionObject.prototype, {\n\n /**\n * Reference to the root namespace.\n * @name ReflectionObject#root\n * @type {Root}\n * @readonly\n */\n root: {\n get: function() {\n var ptr = this;\n while (ptr.parent !== null)\n ptr = ptr.parent;\n return ptr;\n }\n },\n\n /**\n * Full name including leading dot.\n * @name ReflectionObject#fullName\n * @type {string}\n * @readonly\n */\n fullName: {\n get: function() {\n var path = [ this.name ],\n ptr = this.parent;\n while (ptr) {\n path.unshift(ptr.name);\n ptr = ptr.parent;\n }\n return path.join(\".\");\n }\n }\n});\n\n/**\n * Converts this reflection object to its descriptor representation.\n * @returns {Object.} Descriptor\n * @abstract\n */\nReflectionObject.prototype.toJSON = /* istanbul ignore next */ function toJSON() {\n throw Error(); // not implemented, shouldn't happen\n};\n\n/**\n * Called when this object is added to a parent.\n * @param {ReflectionObject} parent Parent added to\n * @returns {undefined}\n */\nReflectionObject.prototype.onAdd = function onAdd(parent) {\n if (this.parent && this.parent !== parent)\n this.parent.remove(this);\n this.parent = parent;\n this.resolved = false;\n var root = parent.root;\n if (root instanceof Root)\n root._handleAdd(this);\n};\n\n/**\n * Called when this object is removed from a parent.\n * @param {ReflectionObject} parent Parent removed from\n * @returns {undefined}\n */\nReflectionObject.prototype.onRemove = function onRemove(parent) {\n var root = parent.root;\n if (root instanceof Root)\n root._handleRemove(this);\n this.parent = null;\n this.resolved = false;\n};\n\n/**\n * Resolves this objects type references.\n * @returns {ReflectionObject} `this`\n */\nReflectionObject.prototype.resolve = function resolve() {\n if (this.resolved)\n return this;\n if (this.root instanceof Root)\n this.resolved = true; // only if part of a root\n return this;\n};\n\n/**\n * Gets an option value.\n * @param {string} name Option name\n * @returns {*} Option value or `undefined` if not set\n */\nReflectionObject.prototype.getOption = function getOption(name) {\n if (this.options)\n return this.options[name];\n return undefined;\n};\n\n/**\n * Sets an option.\n * @param {string} name Option name\n * @param {*} value Option value\n * @param {boolean} [ifNotSet] Sets the option only if it isn't currently set\n * @returns {ReflectionObject} `this`\n */\nReflectionObject.prototype.setOption = function setOption(name, value, ifNotSet) {\n if (!ifNotSet || !this.options || this.options[name] === undefined)\n (this.options || (this.options = {}))[name] = value;\n return this;\n};\n\n/**\n * Sets a parsed option.\n * @param {string} name parsed Option name\n * @param {*} value Option value\n * @param {string} propName dot '.' delimited full path of property within the option to set. if undefined\\empty, will add a new option with that value\n * @returns {ReflectionObject} `this`\n */\nReflectionObject.prototype.setParsedOption = function setParsedOption(name, value, propName) {\n if (!this.parsedOptions) {\n this.parsedOptions = [];\n }\n var parsedOptions = this.parsedOptions;\n if (propName) {\n // If setting a sub property of an option then try to merge it\n // with an existing option\n var opt = parsedOptions.find(function (opt) {\n return Object.prototype.hasOwnProperty.call(opt, name);\n });\n if (opt) {\n // If we found an existing option - just merge the property value\n var newValue = opt[name];\n util.setProperty(newValue, propName, value);\n } else {\n // otherwise, create a new option, set it's property and add it to the list\n opt = {};\n opt[name] = util.setProperty({}, propName, value);\n parsedOptions.push(opt);\n }\n } else {\n // Always create a new option when setting the value of the option itself\n var newOpt = {};\n newOpt[name] = value;\n parsedOptions.push(newOpt);\n }\n return this;\n};\n\n/**\n * Sets multiple options.\n * @param {Object.} options Options to set\n * @param {boolean} [ifNotSet] Sets an option only if it isn't currently set\n * @returns {ReflectionObject} `this`\n */\nReflectionObject.prototype.setOptions = function setOptions(options, ifNotSet) {\n if (options)\n for (var keys = Object.keys(options), i = 0; i < keys.length; ++i)\n this.setOption(keys[i], options[keys[i]], ifNotSet);\n return this;\n};\n\n/**\n * Converts this instance to its string representation.\n * @returns {string} Class name[, space, full name]\n */\nReflectionObject.prototype.toString = function toString() {\n var className = this.constructor.className,\n fullName = this.fullName;\n if (fullName.length)\n return className + \" \" + fullName;\n return className;\n};\n\n// Sets up cyclic dependencies (called in index-light)\nReflectionObject._configure = function(Root_) {\n Root = Root_;\n};\n","\"use strict\";\nmodule.exports = OneOf;\n\n// extends ReflectionObject\nvar ReflectionObject = require(24);\n((OneOf.prototype = Object.create(ReflectionObject.prototype)).constructor = OneOf).className = \"OneOf\";\n\nvar Field = require(16),\n util = require(37);\n\n/**\n * Constructs a new oneof instance.\n * @classdesc Reflected oneof.\n * @extends ReflectionObject\n * @constructor\n * @param {string} name Oneof name\n * @param {string[]|Object.} [fieldNames] Field names\n * @param {Object.} [options] Declared options\n * @param {string} [comment] Comment associated with this field\n */\nfunction OneOf(name, fieldNames, options, comment) {\n if (!Array.isArray(fieldNames)) {\n options = fieldNames;\n fieldNames = undefined;\n }\n ReflectionObject.call(this, name, options);\n\n /* istanbul ignore if */\n if (!(fieldNames === undefined || Array.isArray(fieldNames)))\n throw TypeError(\"fieldNames must be an Array\");\n\n /**\n * Field names that belong to this oneof.\n * @type {string[]}\n */\n this.oneof = fieldNames || []; // toJSON, marker\n\n /**\n * Fields that belong to this oneof as an array for iteration.\n * @type {Field[]}\n * @readonly\n */\n this.fieldsArray = []; // declared readonly for conformance, possibly not yet added to parent\n\n /**\n * Comment for this field.\n * @type {string|null}\n */\n this.comment = comment;\n}\n\n/**\n * Oneof descriptor.\n * @interface IOneOf\n * @property {Array.} oneof Oneof field names\n * @property {Object.} [options] Oneof options\n */\n\n/**\n * Constructs a oneof from a oneof descriptor.\n * @param {string} name Oneof name\n * @param {IOneOf} json Oneof descriptor\n * @returns {OneOf} Created oneof\n * @throws {TypeError} If arguments are invalid\n */\nOneOf.fromJSON = function fromJSON(name, json) {\n return new OneOf(name, json.oneof, json.options, json.comment);\n};\n\n/**\n * Converts this oneof to a oneof descriptor.\n * @param {IToJSONOptions} [toJSONOptions] JSON conversion options\n * @returns {IOneOf} Oneof descriptor\n */\nOneOf.prototype.toJSON = function toJSON(toJSONOptions) {\n var keepComments = toJSONOptions ? Boolean(toJSONOptions.keepComments) : false;\n return util.toObject([\n \"options\" , this.options,\n \"oneof\" , this.oneof,\n \"comment\" , keepComments ? this.comment : undefined\n ]);\n};\n\n/**\n * Adds the fields of the specified oneof to the parent if not already done so.\n * @param {OneOf} oneof The oneof\n * @returns {undefined}\n * @inner\n * @ignore\n */\nfunction addFieldsToParent(oneof) {\n if (oneof.parent)\n for (var i = 0; i < oneof.fieldsArray.length; ++i)\n if (!oneof.fieldsArray[i].parent)\n oneof.parent.add(oneof.fieldsArray[i]);\n}\n\n/**\n * Adds a field to this oneof and removes it from its current parent, if any.\n * @param {Field} field Field to add\n * @returns {OneOf} `this`\n */\nOneOf.prototype.add = function add(field) {\n\n /* istanbul ignore if */\n if (!(field instanceof Field))\n throw TypeError(\"field must be a Field\");\n\n if (field.parent && field.parent !== this.parent)\n field.parent.remove(field);\n this.oneof.push(field.name);\n this.fieldsArray.push(field);\n field.partOf = this; // field.parent remains null\n addFieldsToParent(this);\n return this;\n};\n\n/**\n * Removes a field from this oneof and puts it back to the oneof's parent.\n * @param {Field} field Field to remove\n * @returns {OneOf} `this`\n */\nOneOf.prototype.remove = function remove(field) {\n\n /* istanbul ignore if */\n if (!(field instanceof Field))\n throw TypeError(\"field must be a Field\");\n\n var index = this.fieldsArray.indexOf(field);\n\n /* istanbul ignore if */\n if (index < 0)\n throw Error(field + \" is not a member of \" + this);\n\n this.fieldsArray.splice(index, 1);\n index = this.oneof.indexOf(field.name);\n\n /* istanbul ignore else */\n if (index > -1) // theoretical\n this.oneof.splice(index, 1);\n\n field.partOf = null;\n return this;\n};\n\n/**\n * @override\n */\nOneOf.prototype.onAdd = function onAdd(parent) {\n ReflectionObject.prototype.onAdd.call(this, parent);\n var self = this;\n // Collect present fields\n for (var i = 0; i < this.oneof.length; ++i) {\n var field = parent.get(this.oneof[i]);\n if (field && !field.partOf) {\n field.partOf = self;\n self.fieldsArray.push(field);\n }\n }\n // Add not yet present fields\n addFieldsToParent(this);\n};\n\n/**\n * @override\n */\nOneOf.prototype.onRemove = function onRemove(parent) {\n for (var i = 0, field; i < this.fieldsArray.length; ++i)\n if ((field = this.fieldsArray[i]).parent)\n field.parent.remove(field);\n ReflectionObject.prototype.onRemove.call(this, parent);\n};\n\n/**\n * Decorator function as returned by {@link OneOf.d} (TypeScript).\n * @typedef OneOfDecorator\n * @type {function}\n * @param {Object} prototype Target prototype\n * @param {string} oneofName OneOf name\n * @returns {undefined}\n */\n\n/**\n * OneOf decorator (TypeScript).\n * @function\n * @param {...string} fieldNames Field names\n * @returns {OneOfDecorator} Decorator function\n * @template T extends string\n */\nOneOf.d = function decorateOneOf() {\n var fieldNames = new Array(arguments.length),\n index = 0;\n while (index < arguments.length)\n fieldNames[index] = arguments[index++];\n return function oneOfDecorator(prototype, oneofName) {\n util.decorateType(prototype.constructor)\n .add(new OneOf(oneofName, fieldNames));\n Object.defineProperty(prototype, oneofName, {\n get: util.oneOfGetter(fieldNames),\n set: util.oneOfSetter(fieldNames)\n });\n };\n};\n","\"use strict\";\nmodule.exports = parse;\n\nparse.filename = null;\nparse.defaults = { keepCase: false };\n\nvar tokenize = require(34),\n Root = require(29),\n Type = require(35),\n Field = require(16),\n MapField = require(20),\n OneOf = require(25),\n Enum = require(15),\n Service = require(33),\n Method = require(22),\n types = require(36),\n util = require(37);\n\nvar base10Re = /^[1-9][0-9]*$/,\n base10NegRe = /^-?[1-9][0-9]*$/,\n base16Re = /^0[x][0-9a-fA-F]+$/,\n base16NegRe = /^-?0[x][0-9a-fA-F]+$/,\n base8Re = /^0[0-7]+$/,\n base8NegRe = /^-?0[0-7]+$/,\n numberRe = /^(?![eE])[0-9]*(?:\\.[0-9]*)?(?:[eE][+-]?[0-9]+)?$/,\n nameRe = /^[a-zA-Z_][a-zA-Z_0-9]*$/,\n typeRefRe = /^(?:\\.?[a-zA-Z_][a-zA-Z_0-9]*)(?:\\.[a-zA-Z_][a-zA-Z_0-9]*)*$/,\n fqTypeRefRe = /^(?:\\.[a-zA-Z_][a-zA-Z_0-9]*)+$/;\n\n/**\n * Result object returned from {@link parse}.\n * @interface IParserResult\n * @property {string|undefined} package Package name, if declared\n * @property {string[]|undefined} imports Imports, if any\n * @property {string[]|undefined} weakImports Weak imports, if any\n * @property {string|undefined} syntax Syntax, if specified (either `\"proto2\"` or `\"proto3\"`)\n * @property {Root} root Populated root instance\n */\n\n/**\n * Options modifying the behavior of {@link parse}.\n * @interface IParseOptions\n * @property {boolean} [keepCase=false] Keeps field casing instead of converting to camel case\n * @property {boolean} [alternateCommentMode=false] Recognize double-slash comments in addition to doc-block comments.\n * @property {boolean} [preferTrailingComment=false] Use trailing comment when both leading comment and trailing comment exist.\n */\n\n/**\n * Options modifying the behavior of JSON serialization.\n * @interface IToJSONOptions\n * @property {boolean} [keepComments=false] Serializes comments.\n */\n\n/**\n * Parses the given .proto source and returns an object with the parsed contents.\n * @param {string} source Source contents\n * @param {Root} root Root to populate\n * @param {IParseOptions} [options] Parse options. Defaults to {@link parse.defaults} when omitted.\n * @returns {IParserResult} Parser result\n * @property {string} filename=null Currently processing file name for error reporting, if known\n * @property {IParseOptions} defaults Default {@link IParseOptions}\n */\nfunction parse(source, root, options) {\n /* eslint-disable callback-return */\n if (!(root instanceof Root)) {\n options = root;\n root = new Root();\n }\n if (!options)\n options = parse.defaults;\n\n var preferTrailingComment = options.preferTrailingComment || false;\n var tn = tokenize(source, options.alternateCommentMode || false),\n next = tn.next,\n push = tn.push,\n peek = tn.peek,\n skip = tn.skip,\n cmnt = tn.cmnt;\n\n var head = true,\n pkg,\n imports,\n weakImports,\n syntax,\n isProto3 = false;\n\n var ptr = root;\n\n var applyCase = options.keepCase ? function(name) { return name; } : util.camelCase;\n\n /* istanbul ignore next */\n function illegal(token, name, insideTryCatch) {\n var filename = parse.filename;\n if (!insideTryCatch)\n parse.filename = null;\n return Error(\"illegal \" + (name || \"token\") + \" '\" + token + \"' (\" + (filename ? filename + \", \" : \"\") + \"line \" + tn.line + \")\");\n }\n\n function readString() {\n var values = [],\n token;\n do {\n /* istanbul ignore if */\n if ((token = next()) !== \"\\\"\" && token !== \"'\")\n throw illegal(token);\n\n values.push(next());\n skip(token);\n token = peek();\n } while (token === \"\\\"\" || token === \"'\");\n return values.join(\"\");\n }\n\n function readValue(acceptTypeRef) {\n var token = next();\n switch (token) {\n case \"'\":\n case \"\\\"\":\n push(token);\n return readString();\n case \"true\": case \"TRUE\":\n return true;\n case \"false\": case \"FALSE\":\n return false;\n }\n try {\n return parseNumber(token, /* insideTryCatch */ true);\n } catch (e) {\n\n /* istanbul ignore else */\n if (acceptTypeRef && typeRefRe.test(token))\n return token;\n\n /* istanbul ignore next */\n throw illegal(token, \"value\");\n }\n }\n\n function readRanges(target, acceptStrings) {\n var token, start;\n do {\n if (acceptStrings && ((token = peek()) === \"\\\"\" || token === \"'\"))\n target.push(readString());\n else\n target.push([ start = parseId(next()), skip(\"to\", true) ? parseId(next()) : start ]);\n } while (skip(\",\", true));\n skip(\";\");\n }\n\n function parseNumber(token, insideTryCatch) {\n var sign = 1;\n if (token.charAt(0) === \"-\") {\n sign = -1;\n token = token.substring(1);\n }\n switch (token) {\n case \"inf\": case \"INF\": case \"Inf\":\n return sign * Infinity;\n case \"nan\": case \"NAN\": case \"Nan\": case \"NaN\":\n return NaN;\n case \"0\":\n return 0;\n }\n if (base10Re.test(token))\n return sign * parseInt(token, 10);\n if (base16Re.test(token))\n return sign * parseInt(token, 16);\n if (base8Re.test(token))\n return sign * parseInt(token, 8);\n\n /* istanbul ignore else */\n if (numberRe.test(token))\n return sign * parseFloat(token);\n\n /* istanbul ignore next */\n throw illegal(token, \"number\", insideTryCatch);\n }\n\n function parseId(token, acceptNegative) {\n switch (token) {\n case \"max\": case \"MAX\": case \"Max\":\n return 536870911;\n case \"0\":\n return 0;\n }\n\n /* istanbul ignore if */\n if (!acceptNegative && token.charAt(0) === \"-\")\n throw illegal(token, \"id\");\n\n if (base10NegRe.test(token))\n return parseInt(token, 10);\n if (base16NegRe.test(token))\n return parseInt(token, 16);\n\n /* istanbul ignore else */\n if (base8NegRe.test(token))\n return parseInt(token, 8);\n\n /* istanbul ignore next */\n throw illegal(token, \"id\");\n }\n\n function parsePackage() {\n\n /* istanbul ignore if */\n if (pkg !== undefined)\n throw illegal(\"package\");\n\n pkg = next();\n\n /* istanbul ignore if */\n if (!typeRefRe.test(pkg))\n throw illegal(pkg, \"name\");\n\n ptr = ptr.define(pkg);\n skip(\";\");\n }\n\n function parseImport() {\n var token = peek();\n var whichImports;\n switch (token) {\n case \"weak\":\n whichImports = weakImports || (weakImports = []);\n next();\n break;\n case \"public\":\n next();\n // eslint-disable-line no-fallthrough\n default:\n whichImports = imports || (imports = []);\n break;\n }\n token = readString();\n skip(\";\");\n whichImports.push(token);\n }\n\n function parseSyntax() {\n skip(\"=\");\n syntax = readString();\n isProto3 = syntax === \"proto3\";\n\n /* istanbul ignore if */\n if (!isProto3 && syntax !== \"proto2\")\n throw illegal(syntax, \"syntax\");\n\n skip(\";\");\n }\n\n function parseCommon(parent, token) {\n switch (token) {\n\n case \"option\":\n parseOption(parent, token);\n skip(\";\");\n return true;\n\n case \"message\":\n parseType(parent, token);\n return true;\n\n case \"enum\":\n parseEnum(parent, token);\n return true;\n\n case \"service\":\n parseService(parent, token);\n return true;\n\n case \"extend\":\n parseExtension(parent, token);\n return true;\n }\n return false;\n }\n\n function ifBlock(obj, fnIf, fnElse) {\n var trailingLine = tn.line;\n if (obj) {\n if(typeof obj.comment !== \"string\") {\n obj.comment = cmnt(); // try block-type comment\n }\n obj.filename = parse.filename;\n }\n if (skip(\"{\", true)) {\n var token;\n while ((token = next()) !== \"}\")\n fnIf(token);\n skip(\";\", true);\n } else {\n if (fnElse)\n fnElse();\n skip(\";\");\n if (obj && (typeof obj.comment !== \"string\" || preferTrailingComment))\n obj.comment = cmnt(trailingLine) || obj.comment; // try line-type comment\n }\n }\n\n function parseType(parent, token) {\n\n /* istanbul ignore if */\n if (!nameRe.test(token = next()))\n throw illegal(token, \"type name\");\n\n var type = new Type(token);\n ifBlock(type, function parseType_block(token) {\n if (parseCommon(type, token))\n return;\n\n switch (token) {\n\n case \"map\":\n parseMapField(type, token);\n break;\n\n case \"required\":\n case \"repeated\":\n parseField(type, token);\n break;\n\n case \"optional\":\n /* istanbul ignore if */\n if (isProto3) {\n parseField(type, \"proto3_optional\");\n } else {\n parseField(type, \"optional\");\n }\n break;\n\n case \"oneof\":\n parseOneOf(type, token);\n break;\n\n case \"extensions\":\n readRanges(type.extensions || (type.extensions = []));\n break;\n\n case \"reserved\":\n readRanges(type.reserved || (type.reserved = []), true);\n break;\n\n default:\n /* istanbul ignore if */\n if (!isProto3 || !typeRefRe.test(token))\n throw illegal(token);\n\n push(token);\n parseField(type, \"optional\");\n break;\n }\n });\n parent.add(type);\n }\n\n function parseField(parent, rule, extend) {\n var type = next();\n if (type === \"group\") {\n parseGroup(parent, rule);\n return;\n }\n\n /* istanbul ignore if */\n if (!typeRefRe.test(type))\n throw illegal(type, \"type\");\n\n var name = next();\n\n /* istanbul ignore if */\n if (!nameRe.test(name))\n throw illegal(name, \"name\");\n\n name = applyCase(name);\n skip(\"=\");\n\n var field = new Field(name, parseId(next()), type, rule, extend);\n ifBlock(field, function parseField_block(token) {\n\n /* istanbul ignore else */\n if (token === \"option\") {\n parseOption(field, token);\n skip(\";\");\n } else\n throw illegal(token);\n\n }, function parseField_line() {\n parseInlineOptions(field);\n });\n\n if (rule === \"proto3_optional\") {\n // for proto3 optional fields, we create a single-member Oneof to mimic \"optional\" behavior\n var oneof = new OneOf(\"_\" + name);\n field.setOption(\"proto3_optional\", true);\n oneof.add(field);\n parent.add(oneof);\n } else {\n parent.add(field);\n }\n\n // JSON defaults to packed=true if not set so we have to set packed=false explicity when\n // parsing proto2 descriptors without the option, where applicable. This must be done for\n // all known packable types and anything that could be an enum (= is not a basic type).\n if (!isProto3 && field.repeated && (types.packed[type] !== undefined || types.basic[type] === undefined))\n field.setOption(\"packed\", false, /* ifNotSet */ true);\n }\n\n function parseGroup(parent, rule) {\n var name = next();\n\n /* istanbul ignore if */\n if (!nameRe.test(name))\n throw illegal(name, \"name\");\n\n var fieldName = util.lcFirst(name);\n if (name === fieldName)\n name = util.ucFirst(name);\n skip(\"=\");\n var id = parseId(next());\n var type = new Type(name);\n type.group = true;\n var field = new Field(fieldName, id, name, rule);\n field.filename = parse.filename;\n ifBlock(type, function parseGroup_block(token) {\n switch (token) {\n\n case \"option\":\n parseOption(type, token);\n skip(\";\");\n break;\n\n case \"required\":\n case \"repeated\":\n parseField(type, token);\n break;\n\n case \"optional\":\n /* istanbul ignore if */\n if (isProto3) {\n parseField(type, \"proto3_optional\");\n } else {\n parseField(type, \"optional\");\n }\n break;\n\n /* istanbul ignore next */\n default:\n throw illegal(token); // there are no groups with proto3 semantics\n }\n });\n parent.add(type)\n .add(field);\n }\n\n function parseMapField(parent) {\n skip(\"<\");\n var keyType = next();\n\n /* istanbul ignore if */\n if (types.mapKey[keyType] === undefined)\n throw illegal(keyType, \"type\");\n\n skip(\",\");\n var valueType = next();\n\n /* istanbul ignore if */\n if (!typeRefRe.test(valueType))\n throw illegal(valueType, \"type\");\n\n skip(\">\");\n var name = next();\n\n /* istanbul ignore if */\n if (!nameRe.test(name))\n throw illegal(name, \"name\");\n\n skip(\"=\");\n var field = new MapField(applyCase(name), parseId(next()), keyType, valueType);\n ifBlock(field, function parseMapField_block(token) {\n\n /* istanbul ignore else */\n if (token === \"option\") {\n parseOption(field, token);\n skip(\";\");\n } else\n throw illegal(token);\n\n }, function parseMapField_line() {\n parseInlineOptions(field);\n });\n parent.add(field);\n }\n\n function parseOneOf(parent, token) {\n\n /* istanbul ignore if */\n if (!nameRe.test(token = next()))\n throw illegal(token, \"name\");\n\n var oneof = new OneOf(applyCase(token));\n ifBlock(oneof, function parseOneOf_block(token) {\n if (token === \"option\") {\n parseOption(oneof, token);\n skip(\";\");\n } else {\n push(token);\n parseField(oneof, \"optional\");\n }\n });\n parent.add(oneof);\n }\n\n function parseEnum(parent, token) {\n\n /* istanbul ignore if */\n if (!nameRe.test(token = next()))\n throw illegal(token, \"name\");\n\n var enm = new Enum(token);\n ifBlock(enm, function parseEnum_block(token) {\n switch(token) {\n case \"option\":\n parseOption(enm, token);\n skip(\";\");\n break;\n\n case \"reserved\":\n readRanges(enm.reserved || (enm.reserved = []), true);\n break;\n\n default:\n parseEnumValue(enm, token);\n }\n });\n parent.add(enm);\n }\n\n function parseEnumValue(parent, token) {\n\n /* istanbul ignore if */\n if (!nameRe.test(token))\n throw illegal(token, \"name\");\n\n skip(\"=\");\n var value = parseId(next(), true),\n dummy = {};\n ifBlock(dummy, function parseEnumValue_block(token) {\n\n /* istanbul ignore else */\n if (token === \"option\") {\n parseOption(dummy, token); // skip\n skip(\";\");\n } else\n throw illegal(token);\n\n }, function parseEnumValue_line() {\n parseInlineOptions(dummy); // skip\n });\n parent.add(token, value, dummy.comment);\n }\n\n function parseOption(parent, token) {\n var isCustom = skip(\"(\", true);\n\n /* istanbul ignore if */\n if (!typeRefRe.test(token = next()))\n throw illegal(token, \"name\");\n\n var name = token;\n var option = name;\n var propName;\n\n if (isCustom) {\n skip(\")\");\n name = \"(\" + name + \")\";\n option = name;\n token = peek();\n if (fqTypeRefRe.test(token)) {\n propName = token.substr(1); //remove '.' before property name\n name += token;\n next();\n }\n }\n skip(\"=\");\n var optionValue = parseOptionValue(parent, name);\n setParsedOption(parent, option, optionValue, propName);\n }\n\n function parseOptionValue(parent, name) {\n if (skip(\"{\", true)) { // { a: \"foo\" b { c: \"bar\" } }\n var result = {};\n while (!skip(\"}\", true)) {\n /* istanbul ignore if */\n if (!nameRe.test(token = next()))\n throw illegal(token, \"name\");\n\n var value;\n var propName = token;\n if (peek() === \"{\")\n value = parseOptionValue(parent, name + \".\" + token);\n else {\n skip(\":\");\n if (peek() === \"{\")\n value = parseOptionValue(parent, name + \".\" + token);\n else {\n value = readValue(true);\n setOption(parent, name + \".\" + token, value);\n }\n }\n var prevValue = result[propName];\n if (prevValue)\n value = [].concat(prevValue).concat(value);\n result[propName] = value;\n skip(\",\", true);\n }\n return result;\n }\n\n var simpleValue = readValue(true);\n setOption(parent, name, simpleValue);\n return simpleValue;\n // Does not enforce a delimiter to be universal\n }\n\n function setOption(parent, name, value) {\n if (parent.setOption)\n parent.setOption(name, value);\n }\n\n function setParsedOption(parent, name, value, propName) {\n if (parent.setParsedOption)\n parent.setParsedOption(name, value, propName);\n }\n\n function parseInlineOptions(parent) {\n if (skip(\"[\", true)) {\n do {\n parseOption(parent, \"option\");\n } while (skip(\",\", true));\n skip(\"]\");\n }\n return parent;\n }\n\n function parseService(parent, token) {\n\n /* istanbul ignore if */\n if (!nameRe.test(token = next()))\n throw illegal(token, \"service name\");\n\n var service = new Service(token);\n ifBlock(service, function parseService_block(token) {\n if (parseCommon(service, token))\n return;\n\n /* istanbul ignore else */\n if (token === \"rpc\")\n parseMethod(service, token);\n else\n throw illegal(token);\n });\n parent.add(service);\n }\n\n function parseMethod(parent, token) {\n // Get the comment of the preceding line now (if one exists) in case the\n // method is defined across multiple lines.\n var commentText = cmnt();\n\n var type = token;\n\n /* istanbul ignore if */\n if (!nameRe.test(token = next()))\n throw illegal(token, \"name\");\n\n var name = token,\n requestType, requestStream,\n responseType, responseStream;\n\n skip(\"(\");\n if (skip(\"stream\", true))\n requestStream = true;\n\n /* istanbul ignore if */\n if (!typeRefRe.test(token = next()))\n throw illegal(token);\n\n requestType = token;\n skip(\")\"); skip(\"returns\"); skip(\"(\");\n if (skip(\"stream\", true))\n responseStream = true;\n\n /* istanbul ignore if */\n if (!typeRefRe.test(token = next()))\n throw illegal(token);\n\n responseType = token;\n skip(\")\");\n\n var method = new Method(name, type, requestType, responseType, requestStream, responseStream);\n method.comment = commentText;\n ifBlock(method, function parseMethod_block(token) {\n\n /* istanbul ignore else */\n if (token === \"option\") {\n parseOption(method, token);\n skip(\";\");\n } else\n throw illegal(token);\n\n });\n parent.add(method);\n }\n\n function parseExtension(parent, token) {\n\n /* istanbul ignore if */\n if (!typeRefRe.test(token = next()))\n throw illegal(token, \"reference\");\n\n var reference = token;\n ifBlock(null, function parseExtension_block(token) {\n switch (token) {\n\n case \"required\":\n case \"repeated\":\n parseField(parent, token, reference);\n break;\n\n case \"optional\":\n /* istanbul ignore if */\n if (isProto3) {\n parseField(parent, \"proto3_optional\", reference);\n } else {\n parseField(parent, \"optional\", reference);\n }\n break;\n\n default:\n /* istanbul ignore if */\n if (!isProto3 || !typeRefRe.test(token))\n throw illegal(token);\n push(token);\n parseField(parent, \"optional\", reference);\n break;\n }\n });\n }\n\n var token;\n while ((token = next()) !== null) {\n switch (token) {\n\n case \"package\":\n\n /* istanbul ignore if */\n if (!head)\n throw illegal(token);\n\n parsePackage();\n break;\n\n case \"import\":\n\n /* istanbul ignore if */\n if (!head)\n throw illegal(token);\n\n parseImport();\n break;\n\n case \"syntax\":\n\n /* istanbul ignore if */\n if (!head)\n throw illegal(token);\n\n parseSyntax();\n break;\n\n case \"option\":\n\n parseOption(ptr, token);\n skip(\";\");\n break;\n\n default:\n\n /* istanbul ignore else */\n if (parseCommon(ptr, token)) {\n head = false;\n continue;\n }\n\n /* istanbul ignore next */\n throw illegal(token);\n }\n }\n\n parse.filename = null;\n return {\n \"package\" : pkg,\n \"imports\" : imports,\n weakImports : weakImports,\n syntax : syntax,\n root : root\n };\n}\n\n/**\n * Parses the given .proto source and returns an object with the parsed contents.\n * @name parse\n * @function\n * @param {string} source Source contents\n * @param {IParseOptions} [options] Parse options. Defaults to {@link parse.defaults} when omitted.\n * @returns {IParserResult} Parser result\n * @property {string} filename=null Currently processing file name for error reporting, if known\n * @property {IParseOptions} defaults Default {@link IParseOptions}\n * @variation 2\n */\n","\"use strict\";\nmodule.exports = Reader;\n\nvar util = require(39);\n\nvar BufferReader; // cyclic\n\nvar LongBits = util.LongBits,\n utf8 = util.utf8;\n\n/* istanbul ignore next */\nfunction indexOutOfRange(reader, writeLength) {\n return RangeError(\"index out of range: \" + reader.pos + \" + \" + (writeLength || 1) + \" > \" + reader.len);\n}\n\n/**\n * Constructs a new reader instance using the specified buffer.\n * @classdesc Wire format reader using `Uint8Array` if available, otherwise `Array`.\n * @constructor\n * @param {Uint8Array} buffer Buffer to read from\n */\nfunction Reader(buffer) {\n\n /**\n * Read buffer.\n * @type {Uint8Array}\n */\n this.buf = buffer;\n\n /**\n * Read buffer position.\n * @type {number}\n */\n this.pos = 0;\n\n /**\n * Read buffer length.\n * @type {number}\n */\n this.len = buffer.length;\n}\n\nvar create_array = typeof Uint8Array !== \"undefined\"\n ? function create_typed_array(buffer) {\n if (buffer instanceof Uint8Array || Array.isArray(buffer))\n return new Reader(buffer);\n throw Error(\"illegal buffer\");\n }\n /* istanbul ignore next */\n : function create_array(buffer) {\n if (Array.isArray(buffer))\n return new Reader(buffer);\n throw Error(\"illegal buffer\");\n };\n\nvar create = function create() {\n return util.Buffer\n ? function create_buffer_setup(buffer) {\n return (Reader.create = function create_buffer(buffer) {\n return util.Buffer.isBuffer(buffer)\n ? new BufferReader(buffer)\n /* istanbul ignore next */\n : create_array(buffer);\n })(buffer);\n }\n /* istanbul ignore next */\n : create_array;\n};\n\n/**\n * Creates a new reader using the specified buffer.\n * @function\n * @param {Uint8Array|Buffer} buffer Buffer to read from\n * @returns {Reader|BufferReader} A {@link BufferReader} if `buffer` is a Buffer, otherwise a {@link Reader}\n * @throws {Error} If `buffer` is not a valid buffer\n */\nReader.create = create();\n\nReader.prototype._slice = util.Array.prototype.subarray || /* istanbul ignore next */ util.Array.prototype.slice;\n\n/**\n * Reads a varint as an unsigned 32 bit value.\n * @function\n * @returns {number} Value read\n */\nReader.prototype.uint32 = (function read_uint32_setup() {\n var value = 4294967295; // optimizer type-hint, tends to deopt otherwise (?!)\n return function read_uint32() {\n value = ( this.buf[this.pos] & 127 ) >>> 0; if (this.buf[this.pos++] < 128) return value;\n value = (value | (this.buf[this.pos] & 127) << 7) >>> 0; if (this.buf[this.pos++] < 128) return value;\n value = (value | (this.buf[this.pos] & 127) << 14) >>> 0; if (this.buf[this.pos++] < 128) return value;\n value = (value | (this.buf[this.pos] & 127) << 21) >>> 0; if (this.buf[this.pos++] < 128) return value;\n value = (value | (this.buf[this.pos] & 15) << 28) >>> 0; if (this.buf[this.pos++] < 128) return value;\n\n /* istanbul ignore if */\n if ((this.pos += 5) > this.len) {\n this.pos = this.len;\n throw indexOutOfRange(this, 10);\n }\n return value;\n };\n})();\n\n/**\n * Reads a varint as a signed 32 bit value.\n * @returns {number} Value read\n */\nReader.prototype.int32 = function read_int32() {\n return this.uint32() | 0;\n};\n\n/**\n * Reads a zig-zag encoded varint as a signed 32 bit value.\n * @returns {number} Value read\n */\nReader.prototype.sint32 = function read_sint32() {\n var value = this.uint32();\n return value >>> 1 ^ -(value & 1) | 0;\n};\n\n/* eslint-disable no-invalid-this */\n\nfunction readLongVarint() {\n // tends to deopt with local vars for octet etc.\n var bits = new LongBits(0, 0);\n var i = 0;\n if (this.len - this.pos > 4) { // fast route (lo)\n for (; i < 4; ++i) {\n // 1st..4th\n bits.lo = (bits.lo | (this.buf[this.pos] & 127) << i * 7) >>> 0;\n if (this.buf[this.pos++] < 128)\n return bits;\n }\n // 5th\n bits.lo = (bits.lo | (this.buf[this.pos] & 127) << 28) >>> 0;\n bits.hi = (bits.hi | (this.buf[this.pos] & 127) >> 4) >>> 0;\n if (this.buf[this.pos++] < 128)\n return bits;\n i = 0;\n } else {\n for (; i < 3; ++i) {\n /* istanbul ignore if */\n if (this.pos >= this.len)\n throw indexOutOfRange(this);\n // 1st..3th\n bits.lo = (bits.lo | (this.buf[this.pos] & 127) << i * 7) >>> 0;\n if (this.buf[this.pos++] < 128)\n return bits;\n }\n // 4th\n bits.lo = (bits.lo | (this.buf[this.pos++] & 127) << i * 7) >>> 0;\n return bits;\n }\n if (this.len - this.pos > 4) { // fast route (hi)\n for (; i < 5; ++i) {\n // 6th..10th\n bits.hi = (bits.hi | (this.buf[this.pos] & 127) << i * 7 + 3) >>> 0;\n if (this.buf[this.pos++] < 128)\n return bits;\n }\n } else {\n for (; i < 5; ++i) {\n /* istanbul ignore if */\n if (this.pos >= this.len)\n throw indexOutOfRange(this);\n // 6th..10th\n bits.hi = (bits.hi | (this.buf[this.pos] & 127) << i * 7 + 3) >>> 0;\n if (this.buf[this.pos++] < 128)\n return bits;\n }\n }\n /* istanbul ignore next */\n throw Error(\"invalid varint encoding\");\n}\n\n/* eslint-enable no-invalid-this */\n\n/**\n * Reads a varint as a signed 64 bit value.\n * @name Reader#int64\n * @function\n * @returns {Long} Value read\n */\n\n/**\n * Reads a varint as an unsigned 64 bit value.\n * @name Reader#uint64\n * @function\n * @returns {Long} Value read\n */\n\n/**\n * Reads a zig-zag encoded varint as a signed 64 bit value.\n * @name Reader#sint64\n * @function\n * @returns {Long} Value read\n */\n\n/**\n * Reads a varint as a boolean.\n * @returns {boolean} Value read\n */\nReader.prototype.bool = function read_bool() {\n return this.uint32() !== 0;\n};\n\nfunction readFixed32_end(buf, end) { // note that this uses `end`, not `pos`\n return (buf[end - 4]\n | buf[end - 3] << 8\n | buf[end - 2] << 16\n | buf[end - 1] << 24) >>> 0;\n}\n\n/**\n * Reads fixed 32 bits as an unsigned 32 bit integer.\n * @returns {number} Value read\n */\nReader.prototype.fixed32 = function read_fixed32() {\n\n /* istanbul ignore if */\n if (this.pos + 4 > this.len)\n throw indexOutOfRange(this, 4);\n\n return readFixed32_end(this.buf, this.pos += 4);\n};\n\n/**\n * Reads fixed 32 bits as a signed 32 bit integer.\n * @returns {number} Value read\n */\nReader.prototype.sfixed32 = function read_sfixed32() {\n\n /* istanbul ignore if */\n if (this.pos + 4 > this.len)\n throw indexOutOfRange(this, 4);\n\n return readFixed32_end(this.buf, this.pos += 4) | 0;\n};\n\n/* eslint-disable no-invalid-this */\n\nfunction readFixed64(/* this: Reader */) {\n\n /* istanbul ignore if */\n if (this.pos + 8 > this.len)\n throw indexOutOfRange(this, 8);\n\n return new LongBits(readFixed32_end(this.buf, this.pos += 4), readFixed32_end(this.buf, this.pos += 4));\n}\n\n/* eslint-enable no-invalid-this */\n\n/**\n * Reads fixed 64 bits.\n * @name Reader#fixed64\n * @function\n * @returns {Long} Value read\n */\n\n/**\n * Reads zig-zag encoded fixed 64 bits.\n * @name Reader#sfixed64\n * @function\n * @returns {Long} Value read\n */\n\n/**\n * Reads a float (32 bit) as a number.\n * @function\n * @returns {number} Value read\n */\nReader.prototype.float = function read_float() {\n\n /* istanbul ignore if */\n if (this.pos + 4 > this.len)\n throw indexOutOfRange(this, 4);\n\n var value = util.float.readFloatLE(this.buf, this.pos);\n this.pos += 4;\n return value;\n};\n\n/**\n * Reads a double (64 bit float) as a number.\n * @function\n * @returns {number} Value read\n */\nReader.prototype.double = function read_double() {\n\n /* istanbul ignore if */\n if (this.pos + 8 > this.len)\n throw indexOutOfRange(this, 4);\n\n var value = util.float.readDoubleLE(this.buf, this.pos);\n this.pos += 8;\n return value;\n};\n\n/**\n * Reads a sequence of bytes preceeded by its length as a varint.\n * @returns {Uint8Array} Value read\n */\nReader.prototype.bytes = function read_bytes() {\n var length = this.uint32(),\n start = this.pos,\n end = this.pos + length;\n\n /* istanbul ignore if */\n if (end > this.len)\n throw indexOutOfRange(this, length);\n\n this.pos += length;\n if (Array.isArray(this.buf)) // plain array\n return this.buf.slice(start, end);\n return start === end // fix for IE 10/Win8 and others' subarray returning array of size 1\n ? new this.buf.constructor(0)\n : this._slice.call(this.buf, start, end);\n};\n\n/**\n * Reads a string preceeded by its byte length as a varint.\n * @returns {string} Value read\n */\nReader.prototype.string = function read_string() {\n var bytes = this.bytes();\n return utf8.read(bytes, 0, bytes.length);\n};\n\n/**\n * Skips the specified number of bytes if specified, otherwise skips a varint.\n * @param {number} [length] Length if known, otherwise a varint is assumed\n * @returns {Reader} `this`\n */\nReader.prototype.skip = function skip(length) {\n if (typeof length === \"number\") {\n /* istanbul ignore if */\n if (this.pos + length > this.len)\n throw indexOutOfRange(this, length);\n this.pos += length;\n } else {\n do {\n /* istanbul ignore if */\n if (this.pos >= this.len)\n throw indexOutOfRange(this);\n } while (this.buf[this.pos++] & 128);\n }\n return this;\n};\n\n/**\n * Skips the next element of the specified wire type.\n * @param {number} wireType Wire type received\n * @returns {Reader} `this`\n */\nReader.prototype.skipType = function(wireType) {\n switch (wireType) {\n case 0:\n this.skip();\n break;\n case 1:\n this.skip(8);\n break;\n case 2:\n this.skip(this.uint32());\n break;\n case 3:\n while ((wireType = this.uint32() & 7) !== 4) {\n this.skipType(wireType);\n }\n break;\n case 5:\n this.skip(4);\n break;\n\n /* istanbul ignore next */\n default:\n throw Error(\"invalid wire type \" + wireType + \" at offset \" + this.pos);\n }\n return this;\n};\n\nReader._configure = function(BufferReader_) {\n BufferReader = BufferReader_;\n Reader.create = create();\n BufferReader._configure();\n\n var fn = util.Long ? \"toLong\" : /* istanbul ignore next */ \"toNumber\";\n util.merge(Reader.prototype, {\n\n int64: function read_int64() {\n return readLongVarint.call(this)[fn](false);\n },\n\n uint64: function read_uint64() {\n return readLongVarint.call(this)[fn](true);\n },\n\n sint64: function read_sint64() {\n return readLongVarint.call(this).zzDecode()[fn](false);\n },\n\n fixed64: function read_fixed64() {\n return readFixed64.call(this)[fn](true);\n },\n\n sfixed64: function read_sfixed64() {\n return readFixed64.call(this)[fn](false);\n }\n\n });\n};\n","\"use strict\";\nmodule.exports = BufferReader;\n\n// extends Reader\nvar Reader = require(27);\n(BufferReader.prototype = Object.create(Reader.prototype)).constructor = BufferReader;\n\nvar util = require(39);\n\n/**\n * Constructs a new buffer reader instance.\n * @classdesc Wire format reader using node buffers.\n * @extends Reader\n * @constructor\n * @param {Buffer} buffer Buffer to read from\n */\nfunction BufferReader(buffer) {\n Reader.call(this, buffer);\n\n /**\n * Read buffer.\n * @name BufferReader#buf\n * @type {Buffer}\n */\n}\n\nBufferReader._configure = function () {\n /* istanbul ignore else */\n if (util.Buffer)\n BufferReader.prototype._slice = util.Buffer.prototype.slice;\n};\n\n\n/**\n * @override\n */\nBufferReader.prototype.string = function read_string_buffer() {\n var len = this.uint32(); // modifies pos\n return this.buf.utf8Slice\n ? this.buf.utf8Slice(this.pos, this.pos = Math.min(this.pos + len, this.len))\n : this.buf.toString(\"utf-8\", this.pos, this.pos = Math.min(this.pos + len, this.len));\n};\n\n/**\n * Reads a sequence of bytes preceeded by its length as a varint.\n * @name BufferReader#bytes\n * @function\n * @returns {Buffer} Value read\n */\n\nBufferReader._configure();\n","\"use strict\";\nmodule.exports = Root;\n\n// extends Namespace\nvar Namespace = require(23);\n((Root.prototype = Object.create(Namespace.prototype)).constructor = Root).className = \"Root\";\n\nvar Field = require(16),\n Enum = require(15),\n OneOf = require(25),\n util = require(37);\n\nvar Type, // cyclic\n parse, // might be excluded\n common; // \"\n\n/**\n * Constructs a new root namespace instance.\n * @classdesc Root namespace wrapping all types, enums, services, sub-namespaces etc. that belong together.\n * @extends NamespaceBase\n * @constructor\n * @param {Object.} [options] Top level options\n */\nfunction Root(options) {\n Namespace.call(this, \"\", options);\n\n /**\n * Deferred extension fields.\n * @type {Field[]}\n */\n this.deferred = [];\n\n /**\n * Resolved file names of loaded files.\n * @type {string[]}\n */\n this.files = [];\n}\n\n/**\n * Loads a namespace descriptor into a root namespace.\n * @param {INamespace} json Nameespace descriptor\n * @param {Root} [root] Root namespace, defaults to create a new one if omitted\n * @returns {Root} Root namespace\n */\nRoot.fromJSON = function fromJSON(json, root) {\n if (!root)\n root = new Root();\n if (json.options)\n root.setOptions(json.options);\n return root.addJSON(json.nested);\n};\n\n/**\n * Resolves the path of an imported file, relative to the importing origin.\n * This method exists so you can override it with your own logic in case your imports are scattered over multiple directories.\n * @function\n * @param {string} origin The file name of the importing file\n * @param {string} target The file name being imported\n * @returns {string|null} Resolved path to `target` or `null` to skip the file\n */\nRoot.prototype.resolvePath = util.path.resolve;\n\n/**\n * Fetch content from file path or url\n * This method exists so you can override it with your own logic.\n * @function\n * @param {string} path File path or url\n * @param {FetchCallback} callback Callback function\n * @returns {undefined}\n */\nRoot.prototype.fetch = util.fetch;\n\n// A symbol-like function to safely signal synchronous loading\n/* istanbul ignore next */\nfunction SYNC() {} // eslint-disable-line no-empty-function\n\n/**\n * Loads one or multiple .proto or preprocessed .json files into this root namespace and calls the callback.\n * @param {string|string[]} filename Names of one or multiple files to load\n * @param {IParseOptions} options Parse options\n * @param {LoadCallback} callback Callback function\n * @returns {undefined}\n */\nRoot.prototype.load = function load(filename, options, callback) {\n if (typeof options === \"function\") {\n callback = options;\n options = undefined;\n }\n var self = this;\n if (!callback)\n return util.asPromise(load, self, filename, options);\n\n var sync = callback === SYNC; // undocumented\n\n // Finishes loading by calling the callback (exactly once)\n function finish(err, root) {\n /* istanbul ignore if */\n if (!callback)\n return;\n var cb = callback;\n callback = null;\n if (sync)\n throw err;\n cb(err, root);\n }\n\n // Bundled definition existence checking\n function getBundledFileName(filename) {\n var idx = filename.lastIndexOf(\"google/protobuf/\");\n if (idx > -1) {\n var altname = filename.substring(idx);\n if (altname in common) return altname;\n }\n return null;\n }\n\n // Processes a single file\n function process(filename, source) {\n try {\n if (util.isString(source) && source.charAt(0) === \"{\")\n source = JSON.parse(source);\n if (!util.isString(source))\n self.setOptions(source.options).addJSON(source.nested);\n else {\n parse.filename = filename;\n var parsed = parse(source, self, options),\n resolved,\n i = 0;\n if (parsed.imports)\n for (; i < parsed.imports.length; ++i)\n if (resolved = getBundledFileName(parsed.imports[i]) || self.resolvePath(filename, parsed.imports[i]))\n fetch(resolved);\n if (parsed.weakImports)\n for (i = 0; i < parsed.weakImports.length; ++i)\n if (resolved = getBundledFileName(parsed.weakImports[i]) || self.resolvePath(filename, parsed.weakImports[i]))\n fetch(resolved, true);\n }\n } catch (err) {\n finish(err);\n }\n if (!sync && !queued)\n finish(null, self); // only once anyway\n }\n\n // Fetches a single file\n function fetch(filename, weak) {\n\n // Skip if already loaded / attempted\n if (self.files.indexOf(filename) > -1)\n return;\n self.files.push(filename);\n\n // Shortcut bundled definitions\n if (filename in common) {\n if (sync)\n process(filename, common[filename]);\n else {\n ++queued;\n setTimeout(function() {\n --queued;\n process(filename, common[filename]);\n });\n }\n return;\n }\n\n // Otherwise fetch from disk or network\n if (sync) {\n var source;\n try {\n source = util.fs.readFileSync(filename).toString(\"utf8\");\n } catch (err) {\n if (!weak)\n finish(err);\n return;\n }\n process(filename, source);\n } else {\n ++queued;\n self.fetch(filename, function(err, source) {\n --queued;\n /* istanbul ignore if */\n if (!callback)\n return; // terminated meanwhile\n if (err) {\n /* istanbul ignore else */\n if (!weak)\n finish(err);\n else if (!queued) // can't be covered reliably\n finish(null, self);\n return;\n }\n process(filename, source);\n });\n }\n }\n var queued = 0;\n\n // Assembling the root namespace doesn't require working type\n // references anymore, so we can load everything in parallel\n if (util.isString(filename))\n filename = [ filename ];\n for (var i = 0, resolved; i < filename.length; ++i)\n if (resolved = self.resolvePath(\"\", filename[i]))\n fetch(resolved);\n\n if (sync)\n return self;\n if (!queued)\n finish(null, self);\n return undefined;\n};\n// function load(filename:string, options:IParseOptions, callback:LoadCallback):undefined\n\n/**\n * Loads one or multiple .proto or preprocessed .json files into this root namespace and calls the callback.\n * @function Root#load\n * @param {string|string[]} filename Names of one or multiple files to load\n * @param {LoadCallback} callback Callback function\n * @returns {undefined}\n * @variation 2\n */\n// function load(filename:string, callback:LoadCallback):undefined\n\n/**\n * Loads one or multiple .proto or preprocessed .json files into this root namespace and returns a promise.\n * @function Root#load\n * @param {string|string[]} filename Names of one or multiple files to load\n * @param {IParseOptions} [options] Parse options. Defaults to {@link parse.defaults} when omitted.\n * @returns {Promise} Promise\n * @variation 3\n */\n// function load(filename:string, [options:IParseOptions]):Promise\n\n/**\n * Synchronously loads one or multiple .proto or preprocessed .json files into this root namespace (node only).\n * @function Root#loadSync\n * @param {string|string[]} filename Names of one or multiple files to load\n * @param {IParseOptions} [options] Parse options. Defaults to {@link parse.defaults} when omitted.\n * @returns {Root} Root namespace\n * @throws {Error} If synchronous fetching is not supported (i.e. in browsers) or if a file's syntax is invalid\n */\nRoot.prototype.loadSync = function loadSync(filename, options) {\n if (!util.isNode)\n throw Error(\"not supported\");\n return this.load(filename, options, SYNC);\n};\n\n/**\n * @override\n */\nRoot.prototype.resolveAll = function resolveAll() {\n if (this.deferred.length)\n throw Error(\"unresolvable extensions: \" + this.deferred.map(function(field) {\n return \"'extend \" + field.extend + \"' in \" + field.parent.fullName;\n }).join(\", \"));\n return Namespace.prototype.resolveAll.call(this);\n};\n\n// only uppercased (and thus conflict-free) children are exposed, see below\nvar exposeRe = /^[A-Z]/;\n\n/**\n * Handles a deferred declaring extension field by creating a sister field to represent it within its extended type.\n * @param {Root} root Root instance\n * @param {Field} field Declaring extension field witin the declaring type\n * @returns {boolean} `true` if successfully added to the extended type, `false` otherwise\n * @inner\n * @ignore\n */\nfunction tryHandleExtension(root, field) {\n var extendedType = field.parent.lookup(field.extend);\n if (extendedType) {\n var sisterField = new Field(field.fullName, field.id, field.type, field.rule, undefined, field.options);\n sisterField.declaringField = field;\n field.extensionField = sisterField;\n extendedType.add(sisterField);\n return true;\n }\n return false;\n}\n\n/**\n * Called when any object is added to this root or its sub-namespaces.\n * @param {ReflectionObject} object Object added\n * @returns {undefined}\n * @private\n */\nRoot.prototype._handleAdd = function _handleAdd(object) {\n if (object instanceof Field) {\n\n if (/* an extension field (implies not part of a oneof) */ object.extend !== undefined && /* not already handled */ !object.extensionField)\n if (!tryHandleExtension(this, object))\n this.deferred.push(object);\n\n } else if (object instanceof Enum) {\n\n if (exposeRe.test(object.name))\n object.parent[object.name] = object.values; // expose enum values as property of its parent\n\n } else if (!(object instanceof OneOf)) /* everything else is a namespace */ {\n\n if (object instanceof Type) // Try to handle any deferred extensions\n for (var i = 0; i < this.deferred.length;)\n if (tryHandleExtension(this, this.deferred[i]))\n this.deferred.splice(i, 1);\n else\n ++i;\n for (var j = 0; j < /* initializes */ object.nestedArray.length; ++j) // recurse into the namespace\n this._handleAdd(object._nestedArray[j]);\n if (exposeRe.test(object.name))\n object.parent[object.name] = object; // expose namespace as property of its parent\n }\n\n // The above also adds uppercased (and thus conflict-free) nested types, services and enums as\n // properties of namespaces just like static code does. This allows using a .d.ts generated for\n // a static module with reflection-based solutions where the condition is met.\n};\n\n/**\n * Called when any object is removed from this root or its sub-namespaces.\n * @param {ReflectionObject} object Object removed\n * @returns {undefined}\n * @private\n */\nRoot.prototype._handleRemove = function _handleRemove(object) {\n if (object instanceof Field) {\n\n if (/* an extension field */ object.extend !== undefined) {\n if (/* already handled */ object.extensionField) { // remove its sister field\n object.extensionField.parent.remove(object.extensionField);\n object.extensionField = null;\n } else { // cancel the extension\n var index = this.deferred.indexOf(object);\n /* istanbul ignore else */\n if (index > -1)\n this.deferred.splice(index, 1);\n }\n }\n\n } else if (object instanceof Enum) {\n\n if (exposeRe.test(object.name))\n delete object.parent[object.name]; // unexpose enum values\n\n } else if (object instanceof Namespace) {\n\n for (var i = 0; i < /* initializes */ object.nestedArray.length; ++i) // recurse into the namespace\n this._handleRemove(object._nestedArray[i]);\n\n if (exposeRe.test(object.name))\n delete object.parent[object.name]; // unexpose namespaces\n\n }\n};\n\n// Sets up cyclic dependencies (called in index-light)\nRoot._configure = function(Type_, parse_, common_) {\n Type = Type_;\n parse = parse_;\n common = common_;\n};\n","\"use strict\";\nmodule.exports = {};\n\n/**\n * Named roots.\n * This is where pbjs stores generated structures (the option `-r, --root` specifies a name).\n * Can also be used manually to make roots available accross modules.\n * @name roots\n * @type {Object.}\n * @example\n * // pbjs -r myroot -o compiled.js ...\n *\n * // in another module:\n * require(\"./compiled.js\");\n *\n * // in any subsequent module:\n * var root = protobuf.roots[\"myroot\"];\n */\n","\"use strict\";\n\n/**\n * Streaming RPC helpers.\n * @namespace\n */\nvar rpc = exports;\n\n/**\n * RPC implementation passed to {@link Service#create} performing a service request on network level, i.e. by utilizing http requests or websockets.\n * @typedef RPCImpl\n * @type {function}\n * @param {Method|rpc.ServiceMethod,Message<{}>>} method Reflected or static method being called\n * @param {Uint8Array} requestData Request data\n * @param {RPCImplCallback} callback Callback function\n * @returns {undefined}\n * @example\n * function rpcImpl(method, requestData, callback) {\n * if (protobuf.util.lcFirst(method.name) !== \"myMethod\") // compatible with static code\n * throw Error(\"no such method\");\n * asynchronouslyObtainAResponse(requestData, function(err, responseData) {\n * callback(err, responseData);\n * });\n * }\n */\n\n/**\n * Node-style callback as used by {@link RPCImpl}.\n * @typedef RPCImplCallback\n * @type {function}\n * @param {Error|null} error Error, if any, otherwise `null`\n * @param {Uint8Array|null} [response] Response data or `null` to signal end of stream, if there hasn't been an error\n * @returns {undefined}\n */\n\nrpc.Service = require(32);\n","\"use strict\";\nmodule.exports = Service;\n\nvar util = require(39);\n\n// Extends EventEmitter\n(Service.prototype = Object.create(util.EventEmitter.prototype)).constructor = Service;\n\n/**\n * A service method callback as used by {@link rpc.ServiceMethod|ServiceMethod}.\n *\n * Differs from {@link RPCImplCallback} in that it is an actual callback of a service method which may not return `response = null`.\n * @typedef rpc.ServiceMethodCallback\n * @template TRes extends Message\n * @type {function}\n * @param {Error|null} error Error, if any\n * @param {TRes} [response] Response message\n * @returns {undefined}\n */\n\n/**\n * A service method part of a {@link rpc.Service} as created by {@link Service.create}.\n * @typedef rpc.ServiceMethod\n * @template TReq extends Message\n * @template TRes extends Message\n * @type {function}\n * @param {TReq|Properties} request Request message or plain object\n * @param {rpc.ServiceMethodCallback} [callback] Node-style callback called with the error, if any, and the response message\n * @returns {Promise>} Promise if `callback` has been omitted, otherwise `undefined`\n */\n\n/**\n * Constructs a new RPC service instance.\n * @classdesc An RPC service as returned by {@link Service#create}.\n * @exports rpc.Service\n * @extends util.EventEmitter\n * @constructor\n * @param {RPCImpl} rpcImpl RPC implementation\n * @param {boolean} [requestDelimited=false] Whether requests are length-delimited\n * @param {boolean} [responseDelimited=false] Whether responses are length-delimited\n */\nfunction Service(rpcImpl, requestDelimited, responseDelimited) {\n\n if (typeof rpcImpl !== \"function\")\n throw TypeError(\"rpcImpl must be a function\");\n\n util.EventEmitter.call(this);\n\n /**\n * RPC implementation. Becomes `null` once the service is ended.\n * @type {RPCImpl|null}\n */\n this.rpcImpl = rpcImpl;\n\n /**\n * Whether requests are length-delimited.\n * @type {boolean}\n */\n this.requestDelimited = Boolean(requestDelimited);\n\n /**\n * Whether responses are length-delimited.\n * @type {boolean}\n */\n this.responseDelimited = Boolean(responseDelimited);\n}\n\n/**\n * Calls a service method through {@link rpc.Service#rpcImpl|rpcImpl}.\n * @param {Method|rpc.ServiceMethod} method Reflected or static method\n * @param {Constructor} requestCtor Request constructor\n * @param {Constructor} responseCtor Response constructor\n * @param {TReq|Properties} request Request message or plain object\n * @param {rpc.ServiceMethodCallback} callback Service callback\n * @returns {undefined}\n * @template TReq extends Message\n * @template TRes extends Message\n */\nService.prototype.rpcCall = function rpcCall(method, requestCtor, responseCtor, request, callback) {\n\n if (!request)\n throw TypeError(\"request must be specified\");\n\n var self = this;\n if (!callback)\n return util.asPromise(rpcCall, self, method, requestCtor, responseCtor, request);\n\n if (!self.rpcImpl) {\n setTimeout(function() { callback(Error(\"already ended\")); }, 0);\n return undefined;\n }\n\n try {\n return self.rpcImpl(\n method,\n requestCtor[self.requestDelimited ? \"encodeDelimited\" : \"encode\"](request).finish(),\n function rpcCallback(err, response) {\n\n if (err) {\n self.emit(\"error\", err, method);\n return callback(err);\n }\n\n if (response === null) {\n self.end(/* endedByRPC */ true);\n return undefined;\n }\n\n if (!(response instanceof responseCtor)) {\n try {\n response = responseCtor[self.responseDelimited ? \"decodeDelimited\" : \"decode\"](response);\n } catch (err) {\n self.emit(\"error\", err, method);\n return callback(err);\n }\n }\n\n self.emit(\"data\", response, method);\n return callback(null, response);\n }\n );\n } catch (err) {\n self.emit(\"error\", err, method);\n setTimeout(function() { callback(err); }, 0);\n return undefined;\n }\n};\n\n/**\n * Ends this service and emits the `end` event.\n * @param {boolean} [endedByRPC=false] Whether the service has been ended by the RPC implementation.\n * @returns {rpc.Service} `this`\n */\nService.prototype.end = function end(endedByRPC) {\n if (this.rpcImpl) {\n if (!endedByRPC) // signal end to rpcImpl\n this.rpcImpl(null, null, null);\n this.rpcImpl = null;\n this.emit(\"end\").off();\n }\n return this;\n};\n","\"use strict\";\nmodule.exports = Service;\n\n// extends Namespace\nvar Namespace = require(23);\n((Service.prototype = Object.create(Namespace.prototype)).constructor = Service).className = \"Service\";\n\nvar Method = require(22),\n util = require(37),\n rpc = require(31);\n\n/**\n * Constructs a new service instance.\n * @classdesc Reflected service.\n * @extends NamespaceBase\n * @constructor\n * @param {string} name Service name\n * @param {Object.} [options] Service options\n * @throws {TypeError} If arguments are invalid\n */\nfunction Service(name, options) {\n Namespace.call(this, name, options);\n\n /**\n * Service methods.\n * @type {Object.}\n */\n this.methods = {}; // toJSON, marker\n\n /**\n * Cached methods as an array.\n * @type {Method[]|null}\n * @private\n */\n this._methodsArray = null;\n}\n\n/**\n * Service descriptor.\n * @interface IService\n * @extends INamespace\n * @property {Object.} methods Method descriptors\n */\n\n/**\n * Constructs a service from a service descriptor.\n * @param {string} name Service name\n * @param {IService} json Service descriptor\n * @returns {Service} Created service\n * @throws {TypeError} If arguments are invalid\n */\nService.fromJSON = function fromJSON(name, json) {\n var service = new Service(name, json.options);\n /* istanbul ignore else */\n if (json.methods)\n for (var names = Object.keys(json.methods), i = 0; i < names.length; ++i)\n service.add(Method.fromJSON(names[i], json.methods[names[i]]));\n if (json.nested)\n service.addJSON(json.nested);\n service.comment = json.comment;\n return service;\n};\n\n/**\n * Converts this service to a service descriptor.\n * @param {IToJSONOptions} [toJSONOptions] JSON conversion options\n * @returns {IService} Service descriptor\n */\nService.prototype.toJSON = function toJSON(toJSONOptions) {\n var inherited = Namespace.prototype.toJSON.call(this, toJSONOptions);\n var keepComments = toJSONOptions ? Boolean(toJSONOptions.keepComments) : false;\n return util.toObject([\n \"options\" , inherited && inherited.options || undefined,\n \"methods\" , Namespace.arrayToJSON(this.methodsArray, toJSONOptions) || /* istanbul ignore next */ {},\n \"nested\" , inherited && inherited.nested || undefined,\n \"comment\" , keepComments ? this.comment : undefined\n ]);\n};\n\n/**\n * Methods of this service as an array for iteration.\n * @name Service#methodsArray\n * @type {Method[]}\n * @readonly\n */\nObject.defineProperty(Service.prototype, \"methodsArray\", {\n get: function() {\n return this._methodsArray || (this._methodsArray = util.toArray(this.methods));\n }\n});\n\nfunction clearCache(service) {\n service._methodsArray = null;\n return service;\n}\n\n/**\n * @override\n */\nService.prototype.get = function get(name) {\n return this.methods[name]\n || Namespace.prototype.get.call(this, name);\n};\n\n/**\n * @override\n */\nService.prototype.resolveAll = function resolveAll() {\n var methods = this.methodsArray;\n for (var i = 0; i < methods.length; ++i)\n methods[i].resolve();\n return Namespace.prototype.resolve.call(this);\n};\n\n/**\n * @override\n */\nService.prototype.add = function add(object) {\n\n /* istanbul ignore if */\n if (this.get(object.name))\n throw Error(\"duplicate name '\" + object.name + \"' in \" + this);\n\n if (object instanceof Method) {\n this.methods[object.name] = object;\n object.parent = this;\n return clearCache(this);\n }\n return Namespace.prototype.add.call(this, object);\n};\n\n/**\n * @override\n */\nService.prototype.remove = function remove(object) {\n if (object instanceof Method) {\n\n /* istanbul ignore if */\n if (this.methods[object.name] !== object)\n throw Error(object + \" is not a member of \" + this);\n\n delete this.methods[object.name];\n object.parent = null;\n return clearCache(this);\n }\n return Namespace.prototype.remove.call(this, object);\n};\n\n/**\n * Creates a runtime service using the specified rpc implementation.\n * @param {RPCImpl} rpcImpl RPC implementation\n * @param {boolean} [requestDelimited=false] Whether requests are length-delimited\n * @param {boolean} [responseDelimited=false] Whether responses are length-delimited\n * @returns {rpc.Service} RPC service. Useful where requests and/or responses are streamed.\n */\nService.prototype.create = function create(rpcImpl, requestDelimited, responseDelimited) {\n var rpcService = new rpc.Service(rpcImpl, requestDelimited, responseDelimited);\n for (var i = 0, method; i < /* initializes */ this.methodsArray.length; ++i) {\n var methodName = util.lcFirst((method = this._methodsArray[i]).resolve().name).replace(/[^$\\w_]/g, \"\");\n rpcService[methodName] = util.codegen([\"r\",\"c\"], util.isReserved(methodName) ? methodName + \"_\" : methodName)(\"return this.rpcCall(m,q,s,r,c)\")({\n m: method,\n q: method.resolvedRequestType.ctor,\n s: method.resolvedResponseType.ctor\n });\n }\n return rpcService;\n};\n","\"use strict\";\nmodule.exports = tokenize;\n\nvar delimRe = /[\\s{}=;:[\\],'\"()<>]/g,\n stringDoubleRe = /(?:\"([^\"\\\\]*(?:\\\\.[^\"\\\\]*)*)\")/g,\n stringSingleRe = /(?:'([^'\\\\]*(?:\\\\.[^'\\\\]*)*)')/g;\n\nvar setCommentRe = /^ *[*/]+ */,\n setCommentAltRe = /^\\s*\\*?\\/*/,\n setCommentSplitRe = /\\n/g,\n whitespaceRe = /\\s/,\n unescapeRe = /\\\\(.?)/g;\n\nvar unescapeMap = {\n \"0\": \"\\0\",\n \"r\": \"\\r\",\n \"n\": \"\\n\",\n \"t\": \"\\t\"\n};\n\n/**\n * Unescapes a string.\n * @param {string} str String to unescape\n * @returns {string} Unescaped string\n * @property {Object.} map Special characters map\n * @memberof tokenize\n */\nfunction unescape(str) {\n return str.replace(unescapeRe, function($0, $1) {\n switch ($1) {\n case \"\\\\\":\n case \"\":\n return $1;\n default:\n return unescapeMap[$1] || \"\";\n }\n });\n}\n\ntokenize.unescape = unescape;\n\n/**\n * Gets the next token and advances.\n * @typedef TokenizerHandleNext\n * @type {function}\n * @returns {string|null} Next token or `null` on eof\n */\n\n/**\n * Peeks for the next token.\n * @typedef TokenizerHandlePeek\n * @type {function}\n * @returns {string|null} Next token or `null` on eof\n */\n\n/**\n * Pushes a token back to the stack.\n * @typedef TokenizerHandlePush\n * @type {function}\n * @param {string} token Token\n * @returns {undefined}\n */\n\n/**\n * Skips the next token.\n * @typedef TokenizerHandleSkip\n * @type {function}\n * @param {string} expected Expected token\n * @param {boolean} [optional=false] If optional\n * @returns {boolean} Whether the token matched\n * @throws {Error} If the token didn't match and is not optional\n */\n\n/**\n * Gets the comment on the previous line or, alternatively, the line comment on the specified line.\n * @typedef TokenizerHandleCmnt\n * @type {function}\n * @param {number} [line] Line number\n * @returns {string|null} Comment text or `null` if none\n */\n\n/**\n * Handle object returned from {@link tokenize}.\n * @interface ITokenizerHandle\n * @property {TokenizerHandleNext} next Gets the next token and advances (`null` on eof)\n * @property {TokenizerHandlePeek} peek Peeks for the next token (`null` on eof)\n * @property {TokenizerHandlePush} push Pushes a token back to the stack\n * @property {TokenizerHandleSkip} skip Skips a token, returns its presence and advances or, if non-optional and not present, throws\n * @property {TokenizerHandleCmnt} cmnt Gets the comment on the previous line or the line comment on the specified line, if any\n * @property {number} line Current line number\n */\n\n/**\n * Tokenizes the given .proto source and returns an object with useful utility functions.\n * @param {string} source Source contents\n * @param {boolean} alternateCommentMode Whether we should activate alternate comment parsing mode.\n * @returns {ITokenizerHandle} Tokenizer handle\n */\nfunction tokenize(source, alternateCommentMode) {\n /* eslint-disable callback-return */\n source = source.toString();\n\n var offset = 0,\n length = source.length,\n line = 1,\n commentType = null,\n commentText = null,\n commentLine = 0,\n commentLineEmpty = false,\n commentIsLeading = false;\n\n var stack = [];\n\n var stringDelim = null;\n\n /* istanbul ignore next */\n /**\n * Creates an error for illegal syntax.\n * @param {string} subject Subject\n * @returns {Error} Error created\n * @inner\n */\n function illegal(subject) {\n return Error(\"illegal \" + subject + \" (line \" + line + \")\");\n }\n\n /**\n * Reads a string till its end.\n * @returns {string} String read\n * @inner\n */\n function readString() {\n var re = stringDelim === \"'\" ? stringSingleRe : stringDoubleRe;\n re.lastIndex = offset - 1;\n var match = re.exec(source);\n if (!match)\n throw illegal(\"string\");\n offset = re.lastIndex;\n push(stringDelim);\n stringDelim = null;\n return unescape(match[1]);\n }\n\n /**\n * Gets the character at `pos` within the source.\n * @param {number} pos Position\n * @returns {string} Character\n * @inner\n */\n function charAt(pos) {\n return source.charAt(pos);\n }\n\n /**\n * Sets the current comment text.\n * @param {number} start Start offset\n * @param {number} end End offset\n * @param {boolean} isLeading set if a leading comment\n * @returns {undefined}\n * @inner\n */\n function setComment(start, end, isLeading) {\n commentType = source.charAt(start++);\n commentLine = line;\n commentLineEmpty = false;\n commentIsLeading = isLeading;\n var lookback;\n if (alternateCommentMode) {\n lookback = 2; // alternate comment parsing: \"//\" or \"/*\"\n } else {\n lookback = 3; // \"///\" or \"/**\"\n }\n var commentOffset = start - lookback,\n c;\n do {\n if (--commentOffset < 0 ||\n (c = source.charAt(commentOffset)) === \"\\n\") {\n commentLineEmpty = true;\n break;\n }\n } while (c === \" \" || c === \"\\t\");\n var lines = source\n .substring(start, end)\n .split(setCommentSplitRe);\n for (var i = 0; i < lines.length; ++i)\n lines[i] = lines[i]\n .replace(alternateCommentMode ? setCommentAltRe : setCommentRe, \"\")\n .trim();\n commentText = lines\n .join(\"\\n\")\n .trim();\n }\n\n function isDoubleSlashCommentLine(startOffset) {\n var endOffset = findEndOfLine(startOffset);\n\n // see if remaining line matches comment pattern\n var lineText = source.substring(startOffset, endOffset);\n // look for 1 or 2 slashes since startOffset would already point past\n // the first slash that started the comment.\n var isComment = /^\\s*\\/{1,2}/.test(lineText);\n return isComment;\n }\n\n function findEndOfLine(cursor) {\n // find end of cursor's line\n var endOffset = cursor;\n while (endOffset < length && charAt(endOffset) !== \"\\n\") {\n endOffset++;\n }\n return endOffset;\n }\n\n /**\n * Obtains the next token.\n * @returns {string|null} Next token or `null` on eof\n * @inner\n */\n function next() {\n if (stack.length > 0)\n return stack.shift();\n if (stringDelim)\n return readString();\n var repeat,\n prev,\n curr,\n start,\n isDoc,\n isLeadingComment = offset === 0;\n do {\n if (offset === length)\n return null;\n repeat = false;\n while (whitespaceRe.test(curr = charAt(offset))) {\n if (curr === \"\\n\") {\n isLeadingComment = true;\n ++line;\n }\n if (++offset === length)\n return null;\n }\n\n if (charAt(offset) === \"/\") {\n if (++offset === length) {\n throw illegal(\"comment\");\n }\n if (charAt(offset) === \"/\") { // Line\n if (!alternateCommentMode) {\n // check for triple-slash comment\n isDoc = charAt(start = offset + 1) === \"/\";\n\n while (charAt(++offset) !== \"\\n\") {\n if (offset === length) {\n return null;\n }\n }\n ++offset;\n if (isDoc) {\n setComment(start, offset - 1, isLeadingComment);\n }\n ++line;\n repeat = true;\n } else {\n // check for double-slash comments, consolidating consecutive lines\n start = offset;\n isDoc = false;\n if (isDoubleSlashCommentLine(offset)) {\n isDoc = true;\n do {\n offset = findEndOfLine(offset);\n if (offset === length) {\n break;\n }\n offset++;\n } while (isDoubleSlashCommentLine(offset));\n } else {\n offset = Math.min(length, findEndOfLine(offset) + 1);\n }\n if (isDoc) {\n setComment(start, offset, isLeadingComment);\n }\n line++;\n repeat = true;\n }\n } else if ((curr = charAt(offset)) === \"*\") { /* Block */\n // check for /** (regular comment mode) or /* (alternate comment mode)\n start = offset + 1;\n isDoc = alternateCommentMode || charAt(start) === \"*\";\n do {\n if (curr === \"\\n\") {\n ++line;\n }\n if (++offset === length) {\n throw illegal(\"comment\");\n }\n prev = curr;\n curr = charAt(offset);\n } while (prev !== \"*\" || curr !== \"/\");\n ++offset;\n if (isDoc) {\n setComment(start, offset - 2, isLeadingComment);\n }\n repeat = true;\n } else {\n return \"/\";\n }\n }\n } while (repeat);\n\n // offset !== length if we got here\n\n var end = offset;\n delimRe.lastIndex = 0;\n var delim = delimRe.test(charAt(end++));\n if (!delim)\n while (end < length && !delimRe.test(charAt(end)))\n ++end;\n var token = source.substring(offset, offset = end);\n if (token === \"\\\"\" || token === \"'\")\n stringDelim = token;\n return token;\n }\n\n /**\n * Pushes a token back to the stack.\n * @param {string} token Token\n * @returns {undefined}\n * @inner\n */\n function push(token) {\n stack.push(token);\n }\n\n /**\n * Peeks for the next token.\n * @returns {string|null} Token or `null` on eof\n * @inner\n */\n function peek() {\n if (!stack.length) {\n var token = next();\n if (token === null)\n return null;\n push(token);\n }\n return stack[0];\n }\n\n /**\n * Skips a token.\n * @param {string} expected Expected token\n * @param {boolean} [optional=false] Whether the token is optional\n * @returns {boolean} `true` when skipped, `false` if not\n * @throws {Error} When a required token is not present\n * @inner\n */\n function skip(expected, optional) {\n var actual = peek(),\n equals = actual === expected;\n if (equals) {\n next();\n return true;\n }\n if (!optional)\n throw illegal(\"token '\" + actual + \"', '\" + expected + \"' expected\");\n return false;\n }\n\n /**\n * Gets a comment.\n * @param {number} [trailingLine] Line number if looking for a trailing comment\n * @returns {string|null} Comment text\n * @inner\n */\n function cmnt(trailingLine) {\n var ret = null;\n if (trailingLine === undefined) {\n if (commentLine === line - 1 && (alternateCommentMode || commentType === \"*\" || commentLineEmpty)) {\n ret = commentIsLeading ? commentText : null;\n }\n } else {\n /* istanbul ignore else */\n if (commentLine < trailingLine) {\n peek();\n }\n if (commentLine === trailingLine && !commentLineEmpty && (alternateCommentMode || commentType === \"/\")) {\n ret = commentIsLeading ? null : commentText;\n }\n }\n return ret;\n }\n\n return Object.defineProperty({\n next: next,\n peek: peek,\n push: push,\n skip: skip,\n cmnt: cmnt\n }, \"line\", {\n get: function() { return line; }\n });\n /* eslint-enable callback-return */\n}\n","\"use strict\";\nmodule.exports = Type;\n\n// extends Namespace\nvar Namespace = require(23);\n((Type.prototype = Object.create(Namespace.prototype)).constructor = Type).className = \"Type\";\n\nvar Enum = require(15),\n OneOf = require(25),\n Field = require(16),\n MapField = require(20),\n Service = require(33),\n Message = require(21),\n Reader = require(27),\n Writer = require(42),\n util = require(37),\n encoder = require(14),\n decoder = require(13),\n verifier = require(40),\n converter = require(12),\n wrappers = require(41);\n\n/**\n * Constructs a new reflected message type instance.\n * @classdesc Reflected message type.\n * @extends NamespaceBase\n * @constructor\n * @param {string} name Message name\n * @param {Object.} [options] Declared options\n */\nfunction Type(name, options) {\n Namespace.call(this, name, options);\n\n /**\n * Message fields.\n * @type {Object.}\n */\n this.fields = {}; // toJSON, marker\n\n /**\n * Oneofs declared within this namespace, if any.\n * @type {Object.}\n */\n this.oneofs = undefined; // toJSON\n\n /**\n * Extension ranges, if any.\n * @type {number[][]}\n */\n this.extensions = undefined; // toJSON\n\n /**\n * Reserved ranges, if any.\n * @type {Array.}\n */\n this.reserved = undefined; // toJSON\n\n /*?\n * Whether this type is a legacy group.\n * @type {boolean|undefined}\n */\n this.group = undefined; // toJSON\n\n /**\n * Cached fields by id.\n * @type {Object.|null}\n * @private\n */\n this._fieldsById = null;\n\n /**\n * Cached fields as an array.\n * @type {Field[]|null}\n * @private\n */\n this._fieldsArray = null;\n\n /**\n * Cached oneofs as an array.\n * @type {OneOf[]|null}\n * @private\n */\n this._oneofsArray = null;\n\n /**\n * Cached constructor.\n * @type {Constructor<{}>}\n * @private\n */\n this._ctor = null;\n}\n\nObject.defineProperties(Type.prototype, {\n\n /**\n * Message fields by id.\n * @name Type#fieldsById\n * @type {Object.}\n * @readonly\n */\n fieldsById: {\n get: function() {\n\n /* istanbul ignore if */\n if (this._fieldsById)\n return this._fieldsById;\n\n this._fieldsById = {};\n for (var names = Object.keys(this.fields), i = 0; i < names.length; ++i) {\n var field = this.fields[names[i]],\n id = field.id;\n\n /* istanbul ignore if */\n if (this._fieldsById[id])\n throw Error(\"duplicate id \" + id + \" in \" + this);\n\n this._fieldsById[id] = field;\n }\n return this._fieldsById;\n }\n },\n\n /**\n * Fields of this message as an array for iteration.\n * @name Type#fieldsArray\n * @type {Field[]}\n * @readonly\n */\n fieldsArray: {\n get: function() {\n return this._fieldsArray || (this._fieldsArray = util.toArray(this.fields));\n }\n },\n\n /**\n * Oneofs of this message as an array for iteration.\n * @name Type#oneofsArray\n * @type {OneOf[]}\n * @readonly\n */\n oneofsArray: {\n get: function() {\n return this._oneofsArray || (this._oneofsArray = util.toArray(this.oneofs));\n }\n },\n\n /**\n * The registered constructor, if any registered, otherwise a generic constructor.\n * Assigning a function replaces the internal constructor. If the function does not extend {@link Message} yet, its prototype will be setup accordingly and static methods will be populated. If it already extends {@link Message}, it will just replace the internal constructor.\n * @name Type#ctor\n * @type {Constructor<{}>}\n */\n ctor: {\n get: function() {\n return this._ctor || (this.ctor = Type.generateConstructor(this)());\n },\n set: function(ctor) {\n\n // Ensure proper prototype\n var prototype = ctor.prototype;\n if (!(prototype instanceof Message)) {\n (ctor.prototype = new Message()).constructor = ctor;\n util.merge(ctor.prototype, prototype);\n }\n\n // Classes and messages reference their reflected type\n ctor.$type = ctor.prototype.$type = this;\n\n // Mix in static methods\n util.merge(ctor, Message, true);\n\n this._ctor = ctor;\n\n // Messages have non-enumerable default values on their prototype\n var i = 0;\n for (; i < /* initializes */ this.fieldsArray.length; ++i)\n this._fieldsArray[i].resolve(); // ensures a proper value\n\n // Messages have non-enumerable getters and setters for each virtual oneof field\n var ctorProperties = {};\n for (i = 0; i < /* initializes */ this.oneofsArray.length; ++i)\n ctorProperties[this._oneofsArray[i].resolve().name] = {\n get: util.oneOfGetter(this._oneofsArray[i].oneof),\n set: util.oneOfSetter(this._oneofsArray[i].oneof)\n };\n if (i)\n Object.defineProperties(ctor.prototype, ctorProperties);\n }\n }\n});\n\n/**\n * Generates a constructor function for the specified type.\n * @param {Type} mtype Message type\n * @returns {Codegen} Codegen instance\n */\nType.generateConstructor = function generateConstructor(mtype) {\n /* eslint-disable no-unexpected-multiline */\n var gen = util.codegen([\"p\"], mtype.name);\n // explicitly initialize mutable object/array fields so that these aren't just inherited from the prototype\n for (var i = 0, field; i < mtype.fieldsArray.length; ++i)\n if ((field = mtype._fieldsArray[i]).map) gen\n (\"this%s={}\", util.safeProp(field.name));\n else if (field.repeated) gen\n (\"this%s=[]\", util.safeProp(field.name));\n return gen\n (\"if(p)for(var ks=Object.keys(p),i=0;i} [oneofs] Oneof descriptors\n * @property {Object.} fields Field descriptors\n * @property {number[][]} [extensions] Extension ranges\n * @property {number[][]} [reserved] Reserved ranges\n * @property {boolean} [group=false] Whether a legacy group or not\n */\n\n/**\n * Creates a message type from a message type descriptor.\n * @param {string} name Message name\n * @param {IType} json Message type descriptor\n * @returns {Type} Created message type\n */\nType.fromJSON = function fromJSON(name, json) {\n var type = new Type(name, json.options);\n type.extensions = json.extensions;\n type.reserved = json.reserved;\n var names = Object.keys(json.fields),\n i = 0;\n for (; i < names.length; ++i)\n type.add(\n ( typeof json.fields[names[i]].keyType !== \"undefined\"\n ? MapField.fromJSON\n : Field.fromJSON )(names[i], json.fields[names[i]])\n );\n if (json.oneofs)\n for (names = Object.keys(json.oneofs), i = 0; i < names.length; ++i)\n type.add(OneOf.fromJSON(names[i], json.oneofs[names[i]]));\n if (json.nested)\n for (names = Object.keys(json.nested), i = 0; i < names.length; ++i) {\n var nested = json.nested[names[i]];\n type.add( // most to least likely\n ( nested.id !== undefined\n ? Field.fromJSON\n : nested.fields !== undefined\n ? Type.fromJSON\n : nested.values !== undefined\n ? Enum.fromJSON\n : nested.methods !== undefined\n ? Service.fromJSON\n : Namespace.fromJSON )(names[i], nested)\n );\n }\n if (json.extensions && json.extensions.length)\n type.extensions = json.extensions;\n if (json.reserved && json.reserved.length)\n type.reserved = json.reserved;\n if (json.group)\n type.group = true;\n if (json.comment)\n type.comment = json.comment;\n return type;\n};\n\n/**\n * Converts this message type to a message type descriptor.\n * @param {IToJSONOptions} [toJSONOptions] JSON conversion options\n * @returns {IType} Message type descriptor\n */\nType.prototype.toJSON = function toJSON(toJSONOptions) {\n var inherited = Namespace.prototype.toJSON.call(this, toJSONOptions);\n var keepComments = toJSONOptions ? Boolean(toJSONOptions.keepComments) : false;\n return util.toObject([\n \"options\" , inherited && inherited.options || undefined,\n \"oneofs\" , Namespace.arrayToJSON(this.oneofsArray, toJSONOptions),\n \"fields\" , Namespace.arrayToJSON(this.fieldsArray.filter(function(obj) { return !obj.declaringField; }), toJSONOptions) || {},\n \"extensions\" , this.extensions && this.extensions.length ? this.extensions : undefined,\n \"reserved\" , this.reserved && this.reserved.length ? this.reserved : undefined,\n \"group\" , this.group || undefined,\n \"nested\" , inherited && inherited.nested || undefined,\n \"comment\" , keepComments ? this.comment : undefined\n ]);\n};\n\n/**\n * @override\n */\nType.prototype.resolveAll = function resolveAll() {\n var fields = this.fieldsArray, i = 0;\n while (i < fields.length)\n fields[i++].resolve();\n var oneofs = this.oneofsArray; i = 0;\n while (i < oneofs.length)\n oneofs[i++].resolve();\n return Namespace.prototype.resolveAll.call(this);\n};\n\n/**\n * @override\n */\nType.prototype.get = function get(name) {\n return this.fields[name]\n || this.oneofs && this.oneofs[name]\n || this.nested && this.nested[name]\n || null;\n};\n\n/**\n * Adds a nested object to this type.\n * @param {ReflectionObject} object Nested object to add\n * @returns {Type} `this`\n * @throws {TypeError} If arguments are invalid\n * @throws {Error} If there is already a nested object with this name or, if a field, when there is already a field with this id\n */\nType.prototype.add = function add(object) {\n\n if (this.get(object.name))\n throw Error(\"duplicate name '\" + object.name + \"' in \" + this);\n\n if (object instanceof Field && object.extend === undefined) {\n // NOTE: Extension fields aren't actual fields on the declaring type, but nested objects.\n // The root object takes care of adding distinct sister-fields to the respective extended\n // type instead.\n\n // avoids calling the getter if not absolutely necessary because it's called quite frequently\n if (this._fieldsById ? /* istanbul ignore next */ this._fieldsById[object.id] : this.fieldsById[object.id])\n throw Error(\"duplicate id \" + object.id + \" in \" + this);\n if (this.isReservedId(object.id))\n throw Error(\"id \" + object.id + \" is reserved in \" + this);\n if (this.isReservedName(object.name))\n throw Error(\"name '\" + object.name + \"' is reserved in \" + this);\n\n if (object.parent)\n object.parent.remove(object);\n this.fields[object.name] = object;\n object.message = this;\n object.onAdd(this);\n return clearCache(this);\n }\n if (object instanceof OneOf) {\n if (!this.oneofs)\n this.oneofs = {};\n this.oneofs[object.name] = object;\n object.onAdd(this);\n return clearCache(this);\n }\n return Namespace.prototype.add.call(this, object);\n};\n\n/**\n * Removes a nested object from this type.\n * @param {ReflectionObject} object Nested object to remove\n * @returns {Type} `this`\n * @throws {TypeError} If arguments are invalid\n * @throws {Error} If `object` is not a member of this type\n */\nType.prototype.remove = function remove(object) {\n if (object instanceof Field && object.extend === undefined) {\n // See Type#add for the reason why extension fields are excluded here.\n\n /* istanbul ignore if */\n if (!this.fields || this.fields[object.name] !== object)\n throw Error(object + \" is not a member of \" + this);\n\n delete this.fields[object.name];\n object.parent = null;\n object.onRemove(this);\n return clearCache(this);\n }\n if (object instanceof OneOf) {\n\n /* istanbul ignore if */\n if (!this.oneofs || this.oneofs[object.name] !== object)\n throw Error(object + \" is not a member of \" + this);\n\n delete this.oneofs[object.name];\n object.parent = null;\n object.onRemove(this);\n return clearCache(this);\n }\n return Namespace.prototype.remove.call(this, object);\n};\n\n/**\n * Tests if the specified id is reserved.\n * @param {number} id Id to test\n * @returns {boolean} `true` if reserved, otherwise `false`\n */\nType.prototype.isReservedId = function isReservedId(id) {\n return Namespace.isReservedId(this.reserved, id);\n};\n\n/**\n * Tests if the specified name is reserved.\n * @param {string} name Name to test\n * @returns {boolean} `true` if reserved, otherwise `false`\n */\nType.prototype.isReservedName = function isReservedName(name) {\n return Namespace.isReservedName(this.reserved, name);\n};\n\n/**\n * Creates a new message of this type using the specified properties.\n * @param {Object.} [properties] Properties to set\n * @returns {Message<{}>} Message instance\n */\nType.prototype.create = function create(properties) {\n return new this.ctor(properties);\n};\n\n/**\n * Sets up {@link Type#encode|encode}, {@link Type#decode|decode} and {@link Type#verify|verify}.\n * @returns {Type} `this`\n */\nType.prototype.setup = function setup() {\n // Sets up everything at once so that the prototype chain does not have to be re-evaluated\n // multiple times (V8, soft-deopt prototype-check).\n\n var fullName = this.fullName,\n types = [];\n for (var i = 0; i < /* initializes */ this.fieldsArray.length; ++i)\n types.push(this._fieldsArray[i].resolve().resolvedType);\n\n // Replace setup methods with type-specific generated functions\n this.encode = encoder(this)({\n Writer : Writer,\n types : types,\n util : util\n });\n this.decode = decoder(this)({\n Reader : Reader,\n types : types,\n util : util\n });\n this.verify = verifier(this)({\n types : types,\n util : util\n });\n this.fromObject = converter.fromObject(this)({\n types : types,\n util : util\n });\n this.toObject = converter.toObject(this)({\n types : types,\n util : util\n });\n\n // Inject custom wrappers for common types\n var wrapper = wrappers[fullName];\n if (wrapper) {\n var originalThis = Object.create(this);\n // if (wrapper.fromObject) {\n originalThis.fromObject = this.fromObject;\n this.fromObject = wrapper.fromObject.bind(originalThis);\n // }\n // if (wrapper.toObject) {\n originalThis.toObject = this.toObject;\n this.toObject = wrapper.toObject.bind(originalThis);\n // }\n }\n\n return this;\n};\n\n/**\n * Encodes a message of this type. Does not implicitly {@link Type#verify|verify} messages.\n * @param {Message<{}>|Object.} message Message instance or plain object\n * @param {Writer} [writer] Writer to encode to\n * @returns {Writer} writer\n */\nType.prototype.encode = function encode_setup(message, writer) {\n return this.setup().encode(message, writer); // overrides this method\n};\n\n/**\n * Encodes a message of this type preceeded by its byte length as a varint. Does not implicitly {@link Type#verify|verify} messages.\n * @param {Message<{}>|Object.} message Message instance or plain object\n * @param {Writer} [writer] Writer to encode to\n * @returns {Writer} writer\n */\nType.prototype.encodeDelimited = function encodeDelimited(message, writer) {\n return this.encode(message, writer && writer.len ? writer.fork() : writer).ldelim();\n};\n\n/**\n * Decodes a message of this type.\n * @param {Reader|Uint8Array} reader Reader or buffer to decode from\n * @param {number} [length] Length of the message, if known beforehand\n * @returns {Message<{}>} Decoded message\n * @throws {Error} If the payload is not a reader or valid buffer\n * @throws {util.ProtocolError<{}>} If required fields are missing\n */\nType.prototype.decode = function decode_setup(reader, length) {\n return this.setup().decode(reader, length); // overrides this method\n};\n\n/**\n * Decodes a message of this type preceeded by its byte length as a varint.\n * @param {Reader|Uint8Array} reader Reader or buffer to decode from\n * @returns {Message<{}>} Decoded message\n * @throws {Error} If the payload is not a reader or valid buffer\n * @throws {util.ProtocolError} If required fields are missing\n */\nType.prototype.decodeDelimited = function decodeDelimited(reader) {\n if (!(reader instanceof Reader))\n reader = Reader.create(reader);\n return this.decode(reader, reader.uint32());\n};\n\n/**\n * Verifies that field values are valid and that required fields are present.\n * @param {Object.} message Plain object to verify\n * @returns {null|string} `null` if valid, otherwise the reason why it is not\n */\nType.prototype.verify = function verify_setup(message) {\n return this.setup().verify(message); // overrides this method\n};\n\n/**\n * Creates a new message of this type from a plain object. Also converts values to their respective internal types.\n * @param {Object.} object Plain object to convert\n * @returns {Message<{}>} Message instance\n */\nType.prototype.fromObject = function fromObject(object) {\n return this.setup().fromObject(object);\n};\n\n/**\n * Conversion options as used by {@link Type#toObject} and {@link Message.toObject}.\n * @interface IConversionOptions\n * @property {Function} [longs] Long conversion type.\n * Valid values are `String` and `Number` (the global types).\n * Defaults to copy the present value, which is a possibly unsafe number without and a {@link Long} with a long library.\n * @property {Function} [enums] Enum value conversion type.\n * Only valid value is `String` (the global type).\n * Defaults to copy the present value, which is the numeric id.\n * @property {Function} [bytes] Bytes value conversion type.\n * Valid values are `Array` and (a base64 encoded) `String` (the global types).\n * Defaults to copy the present value, which usually is a Buffer under node and an Uint8Array in the browser.\n * @property {boolean} [defaults=false] Also sets default values on the resulting object\n * @property {boolean} [arrays=false] Sets empty arrays for missing repeated fields even if `defaults=false`\n * @property {boolean} [objects=false] Sets empty objects for missing map fields even if `defaults=false`\n * @property {boolean} [oneofs=false] Includes virtual oneof properties set to the present field's name, if any\n * @property {boolean} [json=false] Performs additional JSON compatibility conversions, i.e. NaN and Infinity to strings\n */\n\n/**\n * Creates a plain object from a message of this type. Also converts values to other types if specified.\n * @param {Message<{}>} message Message instance\n * @param {IConversionOptions} [options] Conversion options\n * @returns {Object.} Plain object\n */\nType.prototype.toObject = function toObject(message, options) {\n return this.setup().toObject(message, options);\n};\n\n/**\n * Decorator function as returned by {@link Type.d} (TypeScript).\n * @typedef TypeDecorator\n * @type {function}\n * @param {Constructor} target Target constructor\n * @returns {undefined}\n * @template T extends Message\n */\n\n/**\n * Type decorator (TypeScript).\n * @param {string} [typeName] Type name, defaults to the constructor's name\n * @returns {TypeDecorator} Decorator function\n * @template T extends Message\n */\nType.d = function decorateType(typeName) {\n return function typeDecorator(target) {\n util.decorateType(target, typeName);\n };\n};\n","\"use strict\";\n\n/**\n * Common type constants.\n * @namespace\n */\nvar types = exports;\n\nvar util = require(37);\n\nvar s = [\n \"double\", // 0\n \"float\", // 1\n \"int32\", // 2\n \"uint32\", // 3\n \"sint32\", // 4\n \"fixed32\", // 5\n \"sfixed32\", // 6\n \"int64\", // 7\n \"uint64\", // 8\n \"sint64\", // 9\n \"fixed64\", // 10\n \"sfixed64\", // 11\n \"bool\", // 12\n \"string\", // 13\n \"bytes\" // 14\n];\n\nfunction bake(values, offset) {\n var i = 0, o = {};\n offset |= 0;\n while (i < values.length) o[s[i + offset]] = values[i++];\n return o;\n}\n\n/**\n * Basic type wire types.\n * @type {Object.}\n * @const\n * @property {number} double=1 Fixed64 wire type\n * @property {number} float=5 Fixed32 wire type\n * @property {number} int32=0 Varint wire type\n * @property {number} uint32=0 Varint wire type\n * @property {number} sint32=0 Varint wire type\n * @property {number} fixed32=5 Fixed32 wire type\n * @property {number} sfixed32=5 Fixed32 wire type\n * @property {number} int64=0 Varint wire type\n * @property {number} uint64=0 Varint wire type\n * @property {number} sint64=0 Varint wire type\n * @property {number} fixed64=1 Fixed64 wire type\n * @property {number} sfixed64=1 Fixed64 wire type\n * @property {number} bool=0 Varint wire type\n * @property {number} string=2 Ldelim wire type\n * @property {number} bytes=2 Ldelim wire type\n */\ntypes.basic = bake([\n /* double */ 1,\n /* float */ 5,\n /* int32 */ 0,\n /* uint32 */ 0,\n /* sint32 */ 0,\n /* fixed32 */ 5,\n /* sfixed32 */ 5,\n /* int64 */ 0,\n /* uint64 */ 0,\n /* sint64 */ 0,\n /* fixed64 */ 1,\n /* sfixed64 */ 1,\n /* bool */ 0,\n /* string */ 2,\n /* bytes */ 2\n]);\n\n/**\n * Basic type defaults.\n * @type {Object.}\n * @const\n * @property {number} double=0 Double default\n * @property {number} float=0 Float default\n * @property {number} int32=0 Int32 default\n * @property {number} uint32=0 Uint32 default\n * @property {number} sint32=0 Sint32 default\n * @property {number} fixed32=0 Fixed32 default\n * @property {number} sfixed32=0 Sfixed32 default\n * @property {number} int64=0 Int64 default\n * @property {number} uint64=0 Uint64 default\n * @property {number} sint64=0 Sint32 default\n * @property {number} fixed64=0 Fixed64 default\n * @property {number} sfixed64=0 Sfixed64 default\n * @property {boolean} bool=false Bool default\n * @property {string} string=\"\" String default\n * @property {Array.} bytes=Array(0) Bytes default\n * @property {null} message=null Message default\n */\ntypes.defaults = bake([\n /* double */ 0,\n /* float */ 0,\n /* int32 */ 0,\n /* uint32 */ 0,\n /* sint32 */ 0,\n /* fixed32 */ 0,\n /* sfixed32 */ 0,\n /* int64 */ 0,\n /* uint64 */ 0,\n /* sint64 */ 0,\n /* fixed64 */ 0,\n /* sfixed64 */ 0,\n /* bool */ false,\n /* string */ \"\",\n /* bytes */ util.emptyArray,\n /* message */ null\n]);\n\n/**\n * Basic long type wire types.\n * @type {Object.}\n * @const\n * @property {number} int64=0 Varint wire type\n * @property {number} uint64=0 Varint wire type\n * @property {number} sint64=0 Varint wire type\n * @property {number} fixed64=1 Fixed64 wire type\n * @property {number} sfixed64=1 Fixed64 wire type\n */\ntypes.long = bake([\n /* int64 */ 0,\n /* uint64 */ 0,\n /* sint64 */ 0,\n /* fixed64 */ 1,\n /* sfixed64 */ 1\n], 7);\n\n/**\n * Allowed types for map keys with their associated wire type.\n * @type {Object.}\n * @const\n * @property {number} int32=0 Varint wire type\n * @property {number} uint32=0 Varint wire type\n * @property {number} sint32=0 Varint wire type\n * @property {number} fixed32=5 Fixed32 wire type\n * @property {number} sfixed32=5 Fixed32 wire type\n * @property {number} int64=0 Varint wire type\n * @property {number} uint64=0 Varint wire type\n * @property {number} sint64=0 Varint wire type\n * @property {number} fixed64=1 Fixed64 wire type\n * @property {number} sfixed64=1 Fixed64 wire type\n * @property {number} bool=0 Varint wire type\n * @property {number} string=2 Ldelim wire type\n */\ntypes.mapKey = bake([\n /* int32 */ 0,\n /* uint32 */ 0,\n /* sint32 */ 0,\n /* fixed32 */ 5,\n /* sfixed32 */ 5,\n /* int64 */ 0,\n /* uint64 */ 0,\n /* sint64 */ 0,\n /* fixed64 */ 1,\n /* sfixed64 */ 1,\n /* bool */ 0,\n /* string */ 2\n], 2);\n\n/**\n * Allowed types for packed repeated fields with their associated wire type.\n * @type {Object.}\n * @const\n * @property {number} double=1 Fixed64 wire type\n * @property {number} float=5 Fixed32 wire type\n * @property {number} int32=0 Varint wire type\n * @property {number} uint32=0 Varint wire type\n * @property {number} sint32=0 Varint wire type\n * @property {number} fixed32=5 Fixed32 wire type\n * @property {number} sfixed32=5 Fixed32 wire type\n * @property {number} int64=0 Varint wire type\n * @property {number} uint64=0 Varint wire type\n * @property {number} sint64=0 Varint wire type\n * @property {number} fixed64=1 Fixed64 wire type\n * @property {number} sfixed64=1 Fixed64 wire type\n * @property {number} bool=0 Varint wire type\n */\ntypes.packed = bake([\n /* double */ 1,\n /* float */ 5,\n /* int32 */ 0,\n /* uint32 */ 0,\n /* sint32 */ 0,\n /* fixed32 */ 5,\n /* sfixed32 */ 5,\n /* int64 */ 0,\n /* uint64 */ 0,\n /* sint64 */ 0,\n /* fixed64 */ 1,\n /* sfixed64 */ 1,\n /* bool */ 0\n]);\n","\"use strict\";\n\n/**\n * Various utility functions.\n * @namespace\n */\nvar util = module.exports = require(39);\n\nvar roots = require(30);\n\nvar Type, // cyclic\n Enum;\n\nutil.codegen = require(3);\nutil.fetch = require(5);\nutil.path = require(8);\n\n/**\n * Node's fs module if available.\n * @type {Object.}\n */\nutil.fs = util.inquire(\"fs\");\n\n/**\n * Converts an object's values to an array.\n * @param {Object.} object Object to convert\n * @returns {Array.<*>} Converted array\n */\nutil.toArray = function toArray(object) {\n if (object) {\n var keys = Object.keys(object),\n array = new Array(keys.length),\n index = 0;\n while (index < keys.length)\n array[index] = object[keys[index++]];\n return array;\n }\n return [];\n};\n\n/**\n * Converts an array of keys immediately followed by their respective value to an object, omitting undefined values.\n * @param {Array.<*>} array Array to convert\n * @returns {Object.} Converted object\n */\nutil.toObject = function toObject(array) {\n var object = {},\n index = 0;\n while (index < array.length) {\n var key = array[index++],\n val = array[index++];\n if (val !== undefined)\n object[key] = val;\n }\n return object;\n};\n\nvar safePropBackslashRe = /\\\\/g,\n safePropQuoteRe = /\"/g;\n\n/**\n * Tests whether the specified name is a reserved word in JS.\n * @param {string} name Name to test\n * @returns {boolean} `true` if reserved, otherwise `false`\n */\nutil.isReserved = function isReserved(name) {\n return /^(?:do|if|in|for|let|new|try|var|case|else|enum|eval|false|null|this|true|void|with|break|catch|class|const|super|throw|while|yield|delete|export|import|public|return|static|switch|typeof|default|extends|finally|package|private|continue|debugger|function|arguments|interface|protected|implements|instanceof)$/.test(name);\n};\n\n/**\n * Returns a safe property accessor for the specified property name.\n * @param {string} prop Property name\n * @returns {string} Safe accessor\n */\nutil.safeProp = function safeProp(prop) {\n if (!/^[$\\w_]+$/.test(prop) || util.isReserved(prop))\n return \"[\\\"\" + prop.replace(safePropBackslashRe, \"\\\\\\\\\").replace(safePropQuoteRe, \"\\\\\\\"\") + \"\\\"]\";\n return \".\" + prop;\n};\n\n/**\n * Converts the first character of a string to upper case.\n * @param {string} str String to convert\n * @returns {string} Converted string\n */\nutil.ucFirst = function ucFirst(str) {\n return str.charAt(0).toUpperCase() + str.substring(1);\n};\n\nvar camelCaseRe = /_([a-z])/g;\n\n/**\n * Converts a string to camel case.\n * @param {string} str String to convert\n * @returns {string} Converted string\n */\nutil.camelCase = function camelCase(str) {\n return str.substring(0, 1)\n + str.substring(1)\n .replace(camelCaseRe, function($0, $1) { return $1.toUpperCase(); });\n};\n\n/**\n * Compares reflected fields by id.\n * @param {Field} a First field\n * @param {Field} b Second field\n * @returns {number} Comparison value\n */\nutil.compareFieldsById = function compareFieldsById(a, b) {\n return a.id - b.id;\n};\n\n/**\n * Decorator helper for types (TypeScript).\n * @param {Constructor} ctor Constructor function\n * @param {string} [typeName] Type name, defaults to the constructor's name\n * @returns {Type} Reflected type\n * @template T extends Message\n * @property {Root} root Decorators root\n */\nutil.decorateType = function decorateType(ctor, typeName) {\n\n /* istanbul ignore if */\n if (ctor.$type) {\n if (typeName && ctor.$type.name !== typeName) {\n util.decorateRoot.remove(ctor.$type);\n ctor.$type.name = typeName;\n util.decorateRoot.add(ctor.$type);\n }\n return ctor.$type;\n }\n\n /* istanbul ignore next */\n if (!Type)\n Type = require(35);\n\n var type = new Type(typeName || ctor.name);\n util.decorateRoot.add(type);\n type.ctor = ctor; // sets up .encode, .decode etc.\n Object.defineProperty(ctor, \"$type\", { value: type, enumerable: false });\n Object.defineProperty(ctor.prototype, \"$type\", { value: type, enumerable: false });\n return type;\n};\n\nvar decorateEnumIndex = 0;\n\n/**\n * Decorator helper for enums (TypeScript).\n * @param {Object} object Enum object\n * @returns {Enum} Reflected enum\n */\nutil.decorateEnum = function decorateEnum(object) {\n\n /* istanbul ignore if */\n if (object.$type)\n return object.$type;\n\n /* istanbul ignore next */\n if (!Enum)\n Enum = require(15);\n\n var enm = new Enum(\"Enum\" + decorateEnumIndex++, object);\n util.decorateRoot.add(enm);\n Object.defineProperty(object, \"$type\", { value: enm, enumerable: false });\n return enm;\n};\n\n\n/**\n * Sets the value of a property by property path. If a value already exists, it is turned to an array\n * @param {Object.} dst Destination object\n * @param {string} path dot '.' delimited path of the property to set\n * @param {Object} value the value to set\n * @returns {Object.} Destination object\n */\nutil.setProperty = function setProperty(dst, path, value) {\n function setProp(dst, path, value) {\n var part = path.shift();\n if (path.length > 0) {\n dst[part] = setProp(dst[part] || {}, path, value);\n } else {\n var prevValue = dst[part];\n if (prevValue)\n value = [].concat(prevValue).concat(value);\n dst[part] = value;\n }\n return dst;\n }\n\n if (typeof dst !== \"object\")\n throw TypeError(\"dst must be an object\");\n if (!path)\n throw TypeError(\"path must be specified\");\n\n path = path.split(\".\");\n return setProp(dst, path, value);\n};\n\n/**\n * Decorator root (TypeScript).\n * @name util.decorateRoot\n * @type {Root}\n * @readonly\n */\nObject.defineProperty(util, \"decorateRoot\", {\n get: function() {\n return roots[\"decorated\"] || (roots[\"decorated\"] = new (require(29))());\n }\n});\n","\"use strict\";\nmodule.exports = LongBits;\n\nvar util = require(39);\n\n/**\n * Constructs new long bits.\n * @classdesc Helper class for working with the low and high bits of a 64 bit value.\n * @memberof util\n * @constructor\n * @param {number} lo Low 32 bits, unsigned\n * @param {number} hi High 32 bits, unsigned\n */\nfunction LongBits(lo, hi) {\n\n // note that the casts below are theoretically unnecessary as of today, but older statically\n // generated converter code might still call the ctor with signed 32bits. kept for compat.\n\n /**\n * Low bits.\n * @type {number}\n */\n this.lo = lo >>> 0;\n\n /**\n * High bits.\n * @type {number}\n */\n this.hi = hi >>> 0;\n}\n\n/**\n * Zero bits.\n * @memberof util.LongBits\n * @type {util.LongBits}\n */\nvar zero = LongBits.zero = new LongBits(0, 0);\n\nzero.toNumber = function() { return 0; };\nzero.zzEncode = zero.zzDecode = function() { return this; };\nzero.length = function() { return 1; };\n\n/**\n * Zero hash.\n * @memberof util.LongBits\n * @type {string}\n */\nvar zeroHash = LongBits.zeroHash = \"\\0\\0\\0\\0\\0\\0\\0\\0\";\n\n/**\n * Constructs new long bits from the specified number.\n * @param {number} value Value\n * @returns {util.LongBits} Instance\n */\nLongBits.fromNumber = function fromNumber(value) {\n if (value === 0)\n return zero;\n var sign = value < 0;\n if (sign)\n value = -value;\n var lo = value >>> 0,\n hi = (value - lo) / 4294967296 >>> 0;\n if (sign) {\n hi = ~hi >>> 0;\n lo = ~lo >>> 0;\n if (++lo > 4294967295) {\n lo = 0;\n if (++hi > 4294967295)\n hi = 0;\n }\n }\n return new LongBits(lo, hi);\n};\n\n/**\n * Constructs new long bits from a number, long or string.\n * @param {Long|number|string} value Value\n * @returns {util.LongBits} Instance\n */\nLongBits.from = function from(value) {\n if (typeof value === \"number\")\n return LongBits.fromNumber(value);\n if (util.isString(value)) {\n /* istanbul ignore else */\n if (util.Long)\n value = util.Long.fromString(value);\n else\n return LongBits.fromNumber(parseInt(value, 10));\n }\n return value.low || value.high ? new LongBits(value.low >>> 0, value.high >>> 0) : zero;\n};\n\n/**\n * Converts this long bits to a possibly unsafe JavaScript number.\n * @param {boolean} [unsigned=false] Whether unsigned or not\n * @returns {number} Possibly unsafe number\n */\nLongBits.prototype.toNumber = function toNumber(unsigned) {\n if (!unsigned && this.hi >>> 31) {\n var lo = ~this.lo + 1 >>> 0,\n hi = ~this.hi >>> 0;\n if (!lo)\n hi = hi + 1 >>> 0;\n return -(lo + hi * 4294967296);\n }\n return this.lo + this.hi * 4294967296;\n};\n\n/**\n * Converts this long bits to a long.\n * @param {boolean} [unsigned=false] Whether unsigned or not\n * @returns {Long} Long\n */\nLongBits.prototype.toLong = function toLong(unsigned) {\n return util.Long\n ? new util.Long(this.lo | 0, this.hi | 0, Boolean(unsigned))\n /* istanbul ignore next */\n : { low: this.lo | 0, high: this.hi | 0, unsigned: Boolean(unsigned) };\n};\n\nvar charCodeAt = String.prototype.charCodeAt;\n\n/**\n * Constructs new long bits from the specified 8 characters long hash.\n * @param {string} hash Hash\n * @returns {util.LongBits} Bits\n */\nLongBits.fromHash = function fromHash(hash) {\n if (hash === zeroHash)\n return zero;\n return new LongBits(\n ( charCodeAt.call(hash, 0)\n | charCodeAt.call(hash, 1) << 8\n | charCodeAt.call(hash, 2) << 16\n | charCodeAt.call(hash, 3) << 24) >>> 0\n ,\n ( charCodeAt.call(hash, 4)\n | charCodeAt.call(hash, 5) << 8\n | charCodeAt.call(hash, 6) << 16\n | charCodeAt.call(hash, 7) << 24) >>> 0\n );\n};\n\n/**\n * Converts this long bits to a 8 characters long hash.\n * @returns {string} Hash\n */\nLongBits.prototype.toHash = function toHash() {\n return String.fromCharCode(\n this.lo & 255,\n this.lo >>> 8 & 255,\n this.lo >>> 16 & 255,\n this.lo >>> 24 ,\n this.hi & 255,\n this.hi >>> 8 & 255,\n this.hi >>> 16 & 255,\n this.hi >>> 24\n );\n};\n\n/**\n * Zig-zag encodes this long bits.\n * @returns {util.LongBits} `this`\n */\nLongBits.prototype.zzEncode = function zzEncode() {\n var mask = this.hi >> 31;\n this.hi = ((this.hi << 1 | this.lo >>> 31) ^ mask) >>> 0;\n this.lo = ( this.lo << 1 ^ mask) >>> 0;\n return this;\n};\n\n/**\n * Zig-zag decodes this long bits.\n * @returns {util.LongBits} `this`\n */\nLongBits.prototype.zzDecode = function zzDecode() {\n var mask = -(this.lo & 1);\n this.lo = ((this.lo >>> 1 | this.hi << 31) ^ mask) >>> 0;\n this.hi = ( this.hi >>> 1 ^ mask) >>> 0;\n return this;\n};\n\n/**\n * Calculates the length of this longbits when encoded as a varint.\n * @returns {number} Length\n */\nLongBits.prototype.length = function length() {\n var part0 = this.lo,\n part1 = (this.lo >>> 28 | this.hi << 4) >>> 0,\n part2 = this.hi >>> 24;\n return part2 === 0\n ? part1 === 0\n ? part0 < 16384\n ? part0 < 128 ? 1 : 2\n : part0 < 2097152 ? 3 : 4\n : part1 < 16384\n ? part1 < 128 ? 5 : 6\n : part1 < 2097152 ? 7 : 8\n : part2 < 128 ? 9 : 10;\n};\n","\"use strict\";\nvar util = exports;\n\n// used to return a Promise where callback is omitted\nutil.asPromise = require(1);\n\n// converts to / from base64 encoded strings\nutil.base64 = require(2);\n\n// base class of rpc.Service\nutil.EventEmitter = require(4);\n\n// float handling accross browsers\nutil.float = require(6);\n\n// requires modules optionally and hides the call from bundlers\nutil.inquire = require(7);\n\n// converts to / from utf8 encoded strings\nutil.utf8 = require(10);\n\n// provides a node-like buffer pool in the browser\nutil.pool = require(9);\n\n// utility to work with the low and high bits of a 64 bit value\nutil.LongBits = require(38);\n\n/**\n * Whether running within node or not.\n * @memberof util\n * @type {boolean}\n */\nutil.isNode = Boolean(typeof global !== \"undefined\"\n && global\n && global.process\n && global.process.versions\n && global.process.versions.node);\n\n/**\n * Global object reference.\n * @memberof util\n * @type {Object}\n */\nutil.global = util.isNode && global\n || typeof window !== \"undefined\" && window\n || typeof self !== \"undefined\" && self\n || this; // eslint-disable-line no-invalid-this\n\n/**\n * An immuable empty array.\n * @memberof util\n * @type {Array.<*>}\n * @const\n */\nutil.emptyArray = Object.freeze ? Object.freeze([]) : /* istanbul ignore next */ []; // used on prototypes\n\n/**\n * An immutable empty object.\n * @type {Object}\n * @const\n */\nutil.emptyObject = Object.freeze ? Object.freeze({}) : /* istanbul ignore next */ {}; // used on prototypes\n\n/**\n * Tests if the specified value is an integer.\n * @function\n * @param {*} value Value to test\n * @returns {boolean} `true` if the value is an integer\n */\nutil.isInteger = Number.isInteger || /* istanbul ignore next */ function isInteger(value) {\n return typeof value === \"number\" && isFinite(value) && Math.floor(value) === value;\n};\n\n/**\n * Tests if the specified value is a string.\n * @param {*} value Value to test\n * @returns {boolean} `true` if the value is a string\n */\nutil.isString = function isString(value) {\n return typeof value === \"string\" || value instanceof String;\n};\n\n/**\n * Tests if the specified value is a non-null object.\n * @param {*} value Value to test\n * @returns {boolean} `true` if the value is a non-null object\n */\nutil.isObject = function isObject(value) {\n return value && typeof value === \"object\";\n};\n\n/**\n * Checks if a property on a message is considered to be present.\n * This is an alias of {@link util.isSet}.\n * @function\n * @param {Object} obj Plain object or message instance\n * @param {string} prop Property name\n * @returns {boolean} `true` if considered to be present, otherwise `false`\n */\nutil.isset =\n\n/**\n * Checks if a property on a message is considered to be present.\n * @param {Object} obj Plain object or message instance\n * @param {string} prop Property name\n * @returns {boolean} `true` if considered to be present, otherwise `false`\n */\nutil.isSet = function isSet(obj, prop) {\n var value = obj[prop];\n if (value != null && obj.hasOwnProperty(prop)) // eslint-disable-line eqeqeq, no-prototype-builtins\n return typeof value !== \"object\" || (Array.isArray(value) ? value.length : Object.keys(value).length) > 0;\n return false;\n};\n\n/**\n * Any compatible Buffer instance.\n * This is a minimal stand-alone definition of a Buffer instance. The actual type is that exported by node's typings.\n * @interface Buffer\n * @extends Uint8Array\n */\n\n/**\n * Node's Buffer class if available.\n * @type {Constructor}\n */\nutil.Buffer = (function() {\n try {\n var Buffer = util.inquire(\"buffer\").Buffer;\n // refuse to use non-node buffers if not explicitly assigned (perf reasons):\n return Buffer.prototype.utf8Write ? Buffer : /* istanbul ignore next */ null;\n } catch (e) {\n /* istanbul ignore next */\n return null;\n }\n})();\n\n// Internal alias of or polyfull for Buffer.from.\nutil._Buffer_from = null;\n\n// Internal alias of or polyfill for Buffer.allocUnsafe.\nutil._Buffer_allocUnsafe = null;\n\n/**\n * Creates a new buffer of whatever type supported by the environment.\n * @param {number|number[]} [sizeOrArray=0] Buffer size or number array\n * @returns {Uint8Array|Buffer} Buffer\n */\nutil.newBuffer = function newBuffer(sizeOrArray) {\n /* istanbul ignore next */\n return typeof sizeOrArray === \"number\"\n ? util.Buffer\n ? util._Buffer_allocUnsafe(sizeOrArray)\n : new util.Array(sizeOrArray)\n : util.Buffer\n ? util._Buffer_from(sizeOrArray)\n : typeof Uint8Array === \"undefined\"\n ? sizeOrArray\n : new Uint8Array(sizeOrArray);\n};\n\n/**\n * Array implementation used in the browser. `Uint8Array` if supported, otherwise `Array`.\n * @type {Constructor}\n */\nutil.Array = typeof Uint8Array !== \"undefined\" ? Uint8Array /* istanbul ignore next */ : Array;\n\n/**\n * Any compatible Long instance.\n * This is a minimal stand-alone definition of a Long instance. The actual type is that exported by long.js.\n * @interface Long\n * @property {number} low Low bits\n * @property {number} high High bits\n * @property {boolean} unsigned Whether unsigned or not\n */\n\n/**\n * Long.js's Long class if available.\n * @type {Constructor}\n */\nutil.Long = /* istanbul ignore next */ util.global.dcodeIO && /* istanbul ignore next */ util.global.dcodeIO.Long\n || /* istanbul ignore next */ util.global.Long\n || util.inquire(\"long\");\n\n/**\n * Regular expression used to verify 2 bit (`bool`) map keys.\n * @type {RegExp}\n * @const\n */\nutil.key2Re = /^true|false|0|1$/;\n\n/**\n * Regular expression used to verify 32 bit (`int32` etc.) map keys.\n * @type {RegExp}\n * @const\n */\nutil.key32Re = /^-?(?:0|[1-9][0-9]*)$/;\n\n/**\n * Regular expression used to verify 64 bit (`int64` etc.) map keys.\n * @type {RegExp}\n * @const\n */\nutil.key64Re = /^(?:[\\\\x00-\\\\xff]{8}|-?(?:0|[1-9][0-9]*))$/;\n\n/**\n * Converts a number or long to an 8 characters long hash string.\n * @param {Long|number} value Value to convert\n * @returns {string} Hash\n */\nutil.longToHash = function longToHash(value) {\n return value\n ? util.LongBits.from(value).toHash()\n : util.LongBits.zeroHash;\n};\n\n/**\n * Converts an 8 characters long hash string to a long or number.\n * @param {string} hash Hash\n * @param {boolean} [unsigned=false] Whether unsigned or not\n * @returns {Long|number} Original value\n */\nutil.longFromHash = function longFromHash(hash, unsigned) {\n var bits = util.LongBits.fromHash(hash);\n if (util.Long)\n return util.Long.fromBits(bits.lo, bits.hi, unsigned);\n return bits.toNumber(Boolean(unsigned));\n};\n\n/**\n * Merges the properties of the source object into the destination object.\n * @memberof util\n * @param {Object.} dst Destination object\n * @param {Object.} src Source object\n * @param {boolean} [ifNotSet=false] Merges only if the key is not already set\n * @returns {Object.} Destination object\n */\nfunction merge(dst, src, ifNotSet) { // used by converters\n for (var keys = Object.keys(src), i = 0; i < keys.length; ++i)\n if (dst[keys[i]] === undefined || !ifNotSet)\n dst[keys[i]] = src[keys[i]];\n return dst;\n}\n\nutil.merge = merge;\n\n/**\n * Converts the first character of a string to lower case.\n * @param {string} str String to convert\n * @returns {string} Converted string\n */\nutil.lcFirst = function lcFirst(str) {\n return str.charAt(0).toLowerCase() + str.substring(1);\n};\n\n/**\n * Creates a custom error constructor.\n * @memberof util\n * @param {string} name Error name\n * @returns {Constructor} Custom error constructor\n */\nfunction newError(name) {\n\n function CustomError(message, properties) {\n\n if (!(this instanceof CustomError))\n return new CustomError(message, properties);\n\n // Error.call(this, message);\n // ^ just returns a new error instance because the ctor can be called as a function\n\n Object.defineProperty(this, \"message\", { get: function() { return message; } });\n\n /* istanbul ignore next */\n if (Error.captureStackTrace) // node\n Error.captureStackTrace(this, CustomError);\n else\n Object.defineProperty(this, \"stack\", { value: new Error().stack || \"\" });\n\n if (properties)\n merge(this, properties);\n }\n\n (CustomError.prototype = Object.create(Error.prototype)).constructor = CustomError;\n\n Object.defineProperty(CustomError.prototype, \"name\", { get: function() { return name; } });\n\n CustomError.prototype.toString = function toString() {\n return this.name + \": \" + this.message;\n };\n\n return CustomError;\n}\n\nutil.newError = newError;\n\n/**\n * Constructs a new protocol error.\n * @classdesc Error subclass indicating a protocol specifc error.\n * @memberof util\n * @extends Error\n * @template T extends Message\n * @constructor\n * @param {string} message Error message\n * @param {Object.} [properties] Additional properties\n * @example\n * try {\n * MyMessage.decode(someBuffer); // throws if required fields are missing\n * } catch (e) {\n * if (e instanceof ProtocolError && e.instance)\n * console.log(\"decoded so far: \" + JSON.stringify(e.instance));\n * }\n */\nutil.ProtocolError = newError(\"ProtocolError\");\n\n/**\n * So far decoded message instance.\n * @name util.ProtocolError#instance\n * @type {Message}\n */\n\n/**\n * A OneOf getter as returned by {@link util.oneOfGetter}.\n * @typedef OneOfGetter\n * @type {function}\n * @returns {string|undefined} Set field name, if any\n */\n\n/**\n * Builds a getter for a oneof's present field name.\n * @param {string[]} fieldNames Field names\n * @returns {OneOfGetter} Unbound getter\n */\nutil.oneOfGetter = function getOneOf(fieldNames) {\n var fieldMap = {};\n for (var i = 0; i < fieldNames.length; ++i)\n fieldMap[fieldNames[i]] = 1;\n\n /**\n * @returns {string|undefined} Set field name, if any\n * @this Object\n * @ignore\n */\n return function() { // eslint-disable-line consistent-return\n for (var keys = Object.keys(this), i = keys.length - 1; i > -1; --i)\n if (fieldMap[keys[i]] === 1 && this[keys[i]] !== undefined && this[keys[i]] !== null)\n return keys[i];\n };\n};\n\n/**\n * A OneOf setter as returned by {@link util.oneOfSetter}.\n * @typedef OneOfSetter\n * @type {function}\n * @param {string|undefined} value Field name\n * @returns {undefined}\n */\n\n/**\n * Builds a setter for a oneof's present field name.\n * @param {string[]} fieldNames Field names\n * @returns {OneOfSetter} Unbound setter\n */\nutil.oneOfSetter = function setOneOf(fieldNames) {\n\n /**\n * @param {string} name Field name\n * @returns {undefined}\n * @this Object\n * @ignore\n */\n return function(name) {\n for (var i = 0; i < fieldNames.length; ++i)\n if (fieldNames[i] !== name)\n delete this[fieldNames[i]];\n };\n};\n\n/**\n * Default conversion options used for {@link Message#toJSON} implementations.\n *\n * These options are close to proto3's JSON mapping with the exception that internal types like Any are handled just like messages. More precisely:\n *\n * - Longs become strings\n * - Enums become string keys\n * - Bytes become base64 encoded strings\n * - (Sub-)Messages become plain objects\n * - Maps become plain objects with all string keys\n * - Repeated fields become arrays\n * - NaN and Infinity for float and double fields become strings\n *\n * @type {IConversionOptions}\n * @see https://developers.google.com/protocol-buffers/docs/proto3?hl=en#json\n */\nutil.toJSONOptions = {\n longs: String,\n enums: String,\n bytes: String,\n json: true\n};\n\n// Sets up buffer utility according to the environment (called in index-minimal)\nutil._configure = function() {\n var Buffer = util.Buffer;\n /* istanbul ignore if */\n if (!Buffer) {\n util._Buffer_from = util._Buffer_allocUnsafe = null;\n return;\n }\n // because node 4.x buffers are incompatible & immutable\n // see: https://github.com/dcodeIO/protobuf.js/pull/665\n util._Buffer_from = Buffer.from !== Uint8Array.from && Buffer.from ||\n /* istanbul ignore next */\n function Buffer_from(value, encoding) {\n return new Buffer(value, encoding);\n };\n util._Buffer_allocUnsafe = Buffer.allocUnsafe ||\n /* istanbul ignore next */\n function Buffer_allocUnsafe(size) {\n return new Buffer(size);\n };\n};\n","\"use strict\";\nmodule.exports = verifier;\n\nvar Enum = require(15),\n util = require(37);\n\nfunction invalid(field, expected) {\n return field.name + \": \" + expected + (field.repeated && expected !== \"array\" ? \"[]\" : field.map && expected !== \"object\" ? \"{k:\"+field.keyType+\"}\" : \"\") + \" expected\";\n}\n\n/**\n * Generates a partial value verifier.\n * @param {Codegen} gen Codegen instance\n * @param {Field} field Reflected field\n * @param {number} fieldIndex Field index\n * @param {string} ref Variable reference\n * @returns {Codegen} Codegen instance\n * @ignore\n */\nfunction genVerifyValue(gen, field, fieldIndex, ref) {\n /* eslint-disable no-unexpected-multiline */\n if (field.resolvedType) {\n if (field.resolvedType instanceof Enum) { gen\n (\"switch(%s){\", ref)\n (\"default:\")\n (\"return%j\", invalid(field, \"enum value\"));\n for (var keys = Object.keys(field.resolvedType.values), j = 0; j < keys.length; ++j) gen\n (\"case %i:\", field.resolvedType.values[keys[j]]);\n gen\n (\"break\")\n (\"}\");\n } else {\n gen\n (\"{\")\n (\"var e=types[%i].verify(%s);\", fieldIndex, ref)\n (\"if(e)\")\n (\"return%j+e\", field.name + \".\")\n (\"}\");\n }\n } else {\n switch (field.type) {\n case \"int32\":\n case \"uint32\":\n case \"sint32\":\n case \"fixed32\":\n case \"sfixed32\": gen\n (\"if(!util.isInteger(%s))\", ref)\n (\"return%j\", invalid(field, \"integer\"));\n break;\n case \"int64\":\n case \"uint64\":\n case \"sint64\":\n case \"fixed64\":\n case \"sfixed64\": gen\n (\"if(!util.isInteger(%s)&&!(%s&&util.isInteger(%s.low)&&util.isInteger(%s.high)))\", ref, ref, ref, ref)\n (\"return%j\", invalid(field, \"integer|Long\"));\n break;\n case \"float\":\n case \"double\": gen\n (\"if(typeof %s!==\\\"number\\\")\", ref)\n (\"return%j\", invalid(field, \"number\"));\n break;\n case \"bool\": gen\n (\"if(typeof %s!==\\\"boolean\\\")\", ref)\n (\"return%j\", invalid(field, \"boolean\"));\n break;\n case \"string\": gen\n (\"if(!util.isString(%s))\", ref)\n (\"return%j\", invalid(field, \"string\"));\n break;\n case \"bytes\": gen\n (\"if(!(%s&&typeof %s.length===\\\"number\\\"||util.isString(%s)))\", ref, ref, ref)\n (\"return%j\", invalid(field, \"buffer\"));\n break;\n }\n }\n return gen;\n /* eslint-enable no-unexpected-multiline */\n}\n\n/**\n * Generates a partial key verifier.\n * @param {Codegen} gen Codegen instance\n * @param {Field} field Reflected field\n * @param {string} ref Variable reference\n * @returns {Codegen} Codegen instance\n * @ignore\n */\nfunction genVerifyKey(gen, field, ref) {\n /* eslint-disable no-unexpected-multiline */\n switch (field.keyType) {\n case \"int32\":\n case \"uint32\":\n case \"sint32\":\n case \"fixed32\":\n case \"sfixed32\": gen\n (\"if(!util.key32Re.test(%s))\", ref)\n (\"return%j\", invalid(field, \"integer key\"));\n break;\n case \"int64\":\n case \"uint64\":\n case \"sint64\":\n case \"fixed64\":\n case \"sfixed64\": gen\n (\"if(!util.key64Re.test(%s))\", ref) // see comment above: x is ok, d is not\n (\"return%j\", invalid(field, \"integer|Long key\"));\n break;\n case \"bool\": gen\n (\"if(!util.key2Re.test(%s))\", ref)\n (\"return%j\", invalid(field, \"boolean key\"));\n break;\n }\n return gen;\n /* eslint-enable no-unexpected-multiline */\n}\n\n/**\n * Generates a verifier specific to the specified message type.\n * @param {Type} mtype Message type\n * @returns {Codegen} Codegen instance\n */\nfunction verifier(mtype) {\n /* eslint-disable no-unexpected-multiline */\n\n var gen = util.codegen([\"m\"], mtype.name + \"$verify\")\n (\"if(typeof m!==\\\"object\\\"||m===null)\")\n (\"return%j\", \"object expected\");\n var oneofs = mtype.oneofsArray,\n seenFirstField = {};\n if (oneofs.length) gen\n (\"var p={}\");\n\n for (var i = 0; i < /* initializes */ mtype.fieldsArray.length; ++i) {\n var field = mtype._fieldsArray[i].resolve(),\n ref = \"m\" + util.safeProp(field.name);\n\n if (field.optional) gen\n (\"if(%s!=null&&m.hasOwnProperty(%j)){\", ref, field.name); // !== undefined && !== null\n\n // map fields\n if (field.map) { gen\n (\"if(!util.isObject(%s))\", ref)\n (\"return%j\", invalid(field, \"object\"))\n (\"var k=Object.keys(%s)\", ref)\n (\"for(var i=0;i}\n * @const\n */\nvar wrappers = exports;\n\nvar Message = require(21);\n\n/**\n * From object converter part of an {@link IWrapper}.\n * @typedef WrapperFromObjectConverter\n * @type {function}\n * @param {Object.} object Plain object\n * @returns {Message<{}>} Message instance\n * @this Type\n */\n\n/**\n * To object converter part of an {@link IWrapper}.\n * @typedef WrapperToObjectConverter\n * @type {function}\n * @param {Message<{}>} message Message instance\n * @param {IConversionOptions} [options] Conversion options\n * @returns {Object.} Plain object\n * @this Type\n */\n\n/**\n * Common type wrapper part of {@link wrappers}.\n * @interface IWrapper\n * @property {WrapperFromObjectConverter} [fromObject] From object converter\n * @property {WrapperToObjectConverter} [toObject] To object converter\n */\n\n// Custom wrapper for Any\nwrappers[\".google.protobuf.Any\"] = {\n\n fromObject: function(object) {\n\n // unwrap value type if mapped\n if (object && object[\"@type\"]) {\n // Only use fully qualified type name after the last '/'\n var name = object[\"@type\"].substring(object[\"@type\"].lastIndexOf(\"/\") + 1);\n var type = this.lookup(name);\n /* istanbul ignore else */\n if (type) {\n // type_url does not accept leading \".\"\n var type_url = object[\"@type\"].charAt(0) === \".\" ?\n object[\"@type\"].substr(1) : object[\"@type\"];\n // type_url prefix is optional, but path seperator is required\n if (type_url.indexOf(\"/\") === -1) {\n type_url = \"/\" + type_url;\n }\n return this.create({\n type_url: type_url,\n value: type.encode(type.fromObject(object)).finish()\n });\n }\n }\n\n return this.fromObject(object);\n },\n\n toObject: function(message, options) {\n\n // Default prefix\n var googleApi = \"type.googleapis.com/\";\n var prefix = \"\";\n var name = \"\";\n\n // decode value if requested and unmapped\n if (options && options.json && message.type_url && message.value) {\n // Only use fully qualified type name after the last '/'\n name = message.type_url.substring(message.type_url.lastIndexOf(\"/\") + 1);\n // Separate the prefix used\n prefix = message.type_url.substring(0, message.type_url.lastIndexOf(\"/\") + 1);\n var type = this.lookup(name);\n /* istanbul ignore else */\n if (type)\n message = type.decode(message.value);\n }\n\n // wrap value if unmapped\n if (!(message instanceof this.ctor) && message instanceof Message) {\n var object = message.$type.toObject(message, options);\n var messageName = message.$type.fullName[0] === \".\" ?\n message.$type.fullName.substr(1) : message.$type.fullName;\n // Default to type.googleapis.com prefix if no prefix is used\n if (prefix === \"\") {\n prefix = googleApi;\n }\n name = prefix + messageName;\n object[\"@type\"] = name;\n return object;\n }\n\n return this.toObject(message, options);\n }\n};\n","\"use strict\";\nmodule.exports = Writer;\n\nvar util = require(39);\n\nvar BufferWriter; // cyclic\n\nvar LongBits = util.LongBits,\n base64 = util.base64,\n utf8 = util.utf8;\n\n/**\n * Constructs a new writer operation instance.\n * @classdesc Scheduled writer operation.\n * @constructor\n * @param {function(*, Uint8Array, number)} fn Function to call\n * @param {number} len Value byte length\n * @param {*} val Value to write\n * @ignore\n */\nfunction Op(fn, len, val) {\n\n /**\n * Function to call.\n * @type {function(Uint8Array, number, *)}\n */\n this.fn = fn;\n\n /**\n * Value byte length.\n * @type {number}\n */\n this.len = len;\n\n /**\n * Next operation.\n * @type {Writer.Op|undefined}\n */\n this.next = undefined;\n\n /**\n * Value to write.\n * @type {*}\n */\n this.val = val; // type varies\n}\n\n/* istanbul ignore next */\nfunction noop() {} // eslint-disable-line no-empty-function\n\n/**\n * Constructs a new writer state instance.\n * @classdesc Copied writer state.\n * @memberof Writer\n * @constructor\n * @param {Writer} writer Writer to copy state from\n * @ignore\n */\nfunction State(writer) {\n\n /**\n * Current head.\n * @type {Writer.Op}\n */\n this.head = writer.head;\n\n /**\n * Current tail.\n * @type {Writer.Op}\n */\n this.tail = writer.tail;\n\n /**\n * Current buffer length.\n * @type {number}\n */\n this.len = writer.len;\n\n /**\n * Next state.\n * @type {State|null}\n */\n this.next = writer.states;\n}\n\n/**\n * Constructs a new writer instance.\n * @classdesc Wire format writer using `Uint8Array` if available, otherwise `Array`.\n * @constructor\n */\nfunction Writer() {\n\n /**\n * Current length.\n * @type {number}\n */\n this.len = 0;\n\n /**\n * Operations head.\n * @type {Object}\n */\n this.head = new Op(noop, 0, 0);\n\n /**\n * Operations tail\n * @type {Object}\n */\n this.tail = this.head;\n\n /**\n * Linked forked states.\n * @type {Object|null}\n */\n this.states = null;\n\n // When a value is written, the writer calculates its byte length and puts it into a linked\n // list of operations to perform when finish() is called. This both allows us to allocate\n // buffers of the exact required size and reduces the amount of work we have to do compared\n // to first calculating over objects and then encoding over objects. In our case, the encoding\n // part is just a linked list walk calling operations with already prepared values.\n}\n\nvar create = function create() {\n return util.Buffer\n ? function create_buffer_setup() {\n return (Writer.create = function create_buffer() {\n return new BufferWriter();\n })();\n }\n /* istanbul ignore next */\n : function create_array() {\n return new Writer();\n };\n};\n\n/**\n * Creates a new writer.\n * @function\n * @returns {BufferWriter|Writer} A {@link BufferWriter} when Buffers are supported, otherwise a {@link Writer}\n */\nWriter.create = create();\n\n/**\n * Allocates a buffer of the specified size.\n * @param {number} size Buffer size\n * @returns {Uint8Array} Buffer\n */\nWriter.alloc = function alloc(size) {\n return new util.Array(size);\n};\n\n// Use Uint8Array buffer pool in the browser, just like node does with buffers\n/* istanbul ignore else */\nif (util.Array !== Array)\n Writer.alloc = util.pool(Writer.alloc, util.Array.prototype.subarray);\n\n/**\n * Pushes a new operation to the queue.\n * @param {function(Uint8Array, number, *)} fn Function to call\n * @param {number} len Value byte length\n * @param {number} val Value to write\n * @returns {Writer} `this`\n * @private\n */\nWriter.prototype._push = function push(fn, len, val) {\n this.tail = this.tail.next = new Op(fn, len, val);\n this.len += len;\n return this;\n};\n\nfunction writeByte(val, buf, pos) {\n buf[pos] = val & 255;\n}\n\nfunction writeVarint32(val, buf, pos) {\n while (val > 127) {\n buf[pos++] = val & 127 | 128;\n val >>>= 7;\n }\n buf[pos] = val;\n}\n\n/**\n * Constructs a new varint writer operation instance.\n * @classdesc Scheduled varint writer operation.\n * @extends Op\n * @constructor\n * @param {number} len Value byte length\n * @param {number} val Value to write\n * @ignore\n */\nfunction VarintOp(len, val) {\n this.len = len;\n this.next = undefined;\n this.val = val;\n}\n\nVarintOp.prototype = Object.create(Op.prototype);\nVarintOp.prototype.fn = writeVarint32;\n\n/**\n * Writes an unsigned 32 bit value as a varint.\n * @param {number} value Value to write\n * @returns {Writer} `this`\n */\nWriter.prototype.uint32 = function write_uint32(value) {\n // here, the call to this.push has been inlined and a varint specific Op subclass is used.\n // uint32 is by far the most frequently used operation and benefits significantly from this.\n this.len += (this.tail = this.tail.next = new VarintOp(\n (value = value >>> 0)\n < 128 ? 1\n : value < 16384 ? 2\n : value < 2097152 ? 3\n : value < 268435456 ? 4\n : 5,\n value)).len;\n return this;\n};\n\n/**\n * Writes a signed 32 bit value as a varint.\n * @function\n * @param {number} value Value to write\n * @returns {Writer} `this`\n */\nWriter.prototype.int32 = function write_int32(value) {\n return value < 0\n ? this._push(writeVarint64, 10, LongBits.fromNumber(value)) // 10 bytes per spec\n : this.uint32(value);\n};\n\n/**\n * Writes a 32 bit value as a varint, zig-zag encoded.\n * @param {number} value Value to write\n * @returns {Writer} `this`\n */\nWriter.prototype.sint32 = function write_sint32(value) {\n return this.uint32((value << 1 ^ value >> 31) >>> 0);\n};\n\nfunction writeVarint64(val, buf, pos) {\n while (val.hi) {\n buf[pos++] = val.lo & 127 | 128;\n val.lo = (val.lo >>> 7 | val.hi << 25) >>> 0;\n val.hi >>>= 7;\n }\n while (val.lo > 127) {\n buf[pos++] = val.lo & 127 | 128;\n val.lo = val.lo >>> 7;\n }\n buf[pos++] = val.lo;\n}\n\n/**\n * Writes an unsigned 64 bit value as a varint.\n * @param {Long|number|string} value Value to write\n * @returns {Writer} `this`\n * @throws {TypeError} If `value` is a string and no long library is present.\n */\nWriter.prototype.uint64 = function write_uint64(value) {\n var bits = LongBits.from(value);\n return this._push(writeVarint64, bits.length(), bits);\n};\n\n/**\n * Writes a signed 64 bit value as a varint.\n * @function\n * @param {Long|number|string} value Value to write\n * @returns {Writer} `this`\n * @throws {TypeError} If `value` is a string and no long library is present.\n */\nWriter.prototype.int64 = Writer.prototype.uint64;\n\n/**\n * Writes a signed 64 bit value as a varint, zig-zag encoded.\n * @param {Long|number|string} value Value to write\n * @returns {Writer} `this`\n * @throws {TypeError} If `value` is a string and no long library is present.\n */\nWriter.prototype.sint64 = function write_sint64(value) {\n var bits = LongBits.from(value).zzEncode();\n return this._push(writeVarint64, bits.length(), bits);\n};\n\n/**\n * Writes a boolish value as a varint.\n * @param {boolean} value Value to write\n * @returns {Writer} `this`\n */\nWriter.prototype.bool = function write_bool(value) {\n return this._push(writeByte, 1, value ? 1 : 0);\n};\n\nfunction writeFixed32(val, buf, pos) {\n buf[pos ] = val & 255;\n buf[pos + 1] = val >>> 8 & 255;\n buf[pos + 2] = val >>> 16 & 255;\n buf[pos + 3] = val >>> 24;\n}\n\n/**\n * Writes an unsigned 32 bit value as fixed 32 bits.\n * @param {number} value Value to write\n * @returns {Writer} `this`\n */\nWriter.prototype.fixed32 = function write_fixed32(value) {\n return this._push(writeFixed32, 4, value >>> 0);\n};\n\n/**\n * Writes a signed 32 bit value as fixed 32 bits.\n * @function\n * @param {number} value Value to write\n * @returns {Writer} `this`\n */\nWriter.prototype.sfixed32 = Writer.prototype.fixed32;\n\n/**\n * Writes an unsigned 64 bit value as fixed 64 bits.\n * @param {Long|number|string} value Value to write\n * @returns {Writer} `this`\n * @throws {TypeError} If `value` is a string and no long library is present.\n */\nWriter.prototype.fixed64 = function write_fixed64(value) {\n var bits = LongBits.from(value);\n return this._push(writeFixed32, 4, bits.lo)._push(writeFixed32, 4, bits.hi);\n};\n\n/**\n * Writes a signed 64 bit value as fixed 64 bits.\n * @function\n * @param {Long|number|string} value Value to write\n * @returns {Writer} `this`\n * @throws {TypeError} If `value` is a string and no long library is present.\n */\nWriter.prototype.sfixed64 = Writer.prototype.fixed64;\n\n/**\n * Writes a float (32 bit).\n * @function\n * @param {number} value Value to write\n * @returns {Writer} `this`\n */\nWriter.prototype.float = function write_float(value) {\n return this._push(util.float.writeFloatLE, 4, value);\n};\n\n/**\n * Writes a double (64 bit float).\n * @function\n * @param {number} value Value to write\n * @returns {Writer} `this`\n */\nWriter.prototype.double = function write_double(value) {\n return this._push(util.float.writeDoubleLE, 8, value);\n};\n\nvar writeBytes = util.Array.prototype.set\n ? function writeBytes_set(val, buf, pos) {\n buf.set(val, pos); // also works for plain array values\n }\n /* istanbul ignore next */\n : function writeBytes_for(val, buf, pos) {\n for (var i = 0; i < val.length; ++i)\n buf[pos + i] = val[i];\n };\n\n/**\n * Writes a sequence of bytes.\n * @param {Uint8Array|string} value Buffer or base64 encoded string to write\n * @returns {Writer} `this`\n */\nWriter.prototype.bytes = function write_bytes(value) {\n var len = value.length >>> 0;\n if (!len)\n return this._push(writeByte, 1, 0);\n if (util.isString(value)) {\n var buf = Writer.alloc(len = base64.length(value));\n base64.decode(value, buf, 0);\n value = buf;\n }\n return this.uint32(len)._push(writeBytes, len, value);\n};\n\n/**\n * Writes a string.\n * @param {string} value Value to write\n * @returns {Writer} `this`\n */\nWriter.prototype.string = function write_string(value) {\n var len = utf8.length(value);\n return len\n ? this.uint32(len)._push(utf8.write, len, value)\n : this._push(writeByte, 1, 0);\n};\n\n/**\n * Forks this writer's state by pushing it to a stack.\n * Calling {@link Writer#reset|reset} or {@link Writer#ldelim|ldelim} resets the writer to the previous state.\n * @returns {Writer} `this`\n */\nWriter.prototype.fork = function fork() {\n this.states = new State(this);\n this.head = this.tail = new Op(noop, 0, 0);\n this.len = 0;\n return this;\n};\n\n/**\n * Resets this instance to the last state.\n * @returns {Writer} `this`\n */\nWriter.prototype.reset = function reset() {\n if (this.states) {\n this.head = this.states.head;\n this.tail = this.states.tail;\n this.len = this.states.len;\n this.states = this.states.next;\n } else {\n this.head = this.tail = new Op(noop, 0, 0);\n this.len = 0;\n }\n return this;\n};\n\n/**\n * Resets to the last state and appends the fork state's current write length as a varint followed by its operations.\n * @returns {Writer} `this`\n */\nWriter.prototype.ldelim = function ldelim() {\n var head = this.head,\n tail = this.tail,\n len = this.len;\n this.reset().uint32(len);\n if (len) {\n this.tail.next = head.next; // skip noop\n this.tail = tail;\n this.len += len;\n }\n return this;\n};\n\n/**\n * Finishes the write operation.\n * @returns {Uint8Array} Finished buffer\n */\nWriter.prototype.finish = function finish() {\n var head = this.head.next, // skip noop\n buf = this.constructor.alloc(this.len),\n pos = 0;\n while (head) {\n head.fn(head.val, buf, pos);\n pos += head.len;\n head = head.next;\n }\n // this.head = this.tail = null;\n return buf;\n};\n\nWriter._configure = function(BufferWriter_) {\n BufferWriter = BufferWriter_;\n Writer.create = create();\n BufferWriter._configure();\n};\n","\"use strict\";\nmodule.exports = BufferWriter;\n\n// extends Writer\nvar Writer = require(42);\n(BufferWriter.prototype = Object.create(Writer.prototype)).constructor = BufferWriter;\n\nvar util = require(39);\n\n/**\n * Constructs a new buffer writer instance.\n * @classdesc Wire format writer using node buffers.\n * @extends Writer\n * @constructor\n */\nfunction BufferWriter() {\n Writer.call(this);\n}\n\nBufferWriter._configure = function () {\n /**\n * Allocates a buffer of the specified size.\n * @function\n * @param {number} size Buffer size\n * @returns {Buffer} Buffer\n */\n BufferWriter.alloc = util._Buffer_allocUnsafe;\n\n BufferWriter.writeBytesBuffer = util.Buffer && util.Buffer.prototype instanceof Uint8Array && util.Buffer.prototype.set.name === \"set\"\n ? function writeBytesBuffer_set(val, buf, pos) {\n buf.set(val, pos); // faster than copy (requires node >= 4 where Buffers extend Uint8Array and set is properly inherited)\n // also works for plain array values\n }\n /* istanbul ignore next */\n : function writeBytesBuffer_copy(val, buf, pos) {\n if (val.copy) // Buffer values\n val.copy(buf, pos, 0, val.length);\n else for (var i = 0; i < val.length;) // plain array values\n buf[pos++] = val[i++];\n };\n};\n\n\n/**\n * @override\n */\nBufferWriter.prototype.bytes = function write_bytes_buffer(value) {\n if (util.isString(value))\n value = util._Buffer_from(value, \"base64\");\n var len = value.length >>> 0;\n this.uint32(len);\n if (len)\n this._push(BufferWriter.writeBytesBuffer, len, value);\n return this;\n};\n\nfunction writeStringBuffer(val, buf, pos) {\n if (val.length < 40) // plain js is faster for short strings (probably due to redundant assertions)\n util.utf8.write(val, buf, pos);\n else if (buf.utf8Write)\n buf.utf8Write(val, pos);\n else\n buf.write(val, pos);\n}\n\n/**\n * @override\n */\nBufferWriter.prototype.string = function write_string_buffer(value) {\n var len = util.Buffer.byteLength(value);\n this.uint32(len);\n if (len)\n this._push(writeStringBuffer, len, value);\n return this;\n};\n\n\n/**\n * Finishes the write operation.\n * @name BufferWriter#finish\n * @function\n * @returns {Buffer} Finished buffer\n */\n\nBufferWriter._configure();\n"],"sourceRoot":"."} \ No newline at end of file diff --git a/src/frontend/Hanami-AI-Dashboard/src/favicon.ico b/src/frontend/Hanami-AI-Dashboard/src/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..9fde1ad13b91f73c15c6c42aa0c6a739749eadb1 GIT binary patch literal 165662 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 Zp}4uE&*H$QbsX#-_Brt|-=~1t{{d+21c?9u literal 0 HcmV?d00001 diff --git a/src/frontend/Hanami-AI-Dashboard/src/index.html b/src/frontend/Hanami-AI-Dashboard/src/index.html new file mode 100644 index 00000000..cdd1e77a --- /dev/null +++ b/src/frontend/Hanami-AI-Dashboard/src/index.html @@ -0,0 +1,60 @@ + + + + + + + + + Hanami-AI-Dashboard + + + + + + + + + + + + + + + + + +
+ +
+ +
+ +
+ + + + + + diff --git a/src/frontend/Hanami-AI-Dashboard/src/libHanamiAiSdk/.github/ISSUE_TEMPLATE/bug_issue_template.md b/src/frontend/Hanami-AI-Dashboard/src/libHanamiAiSdk/.github/ISSUE_TEMPLATE/bug_issue_template.md new file mode 100644 index 00000000..44c724d6 --- /dev/null +++ b/src/frontend/Hanami-AI-Dashboard/src/libHanamiAiSdk/.github/ISSUE_TEMPLATE/bug_issue_template.md @@ -0,0 +1,22 @@ +--- +name: bug-issue template +about: Use this template to report bugs and problem +labels: bug +--- + +## BUG-issue + +### What you wanted to do + +### Description of the bug you run into + +### Possible Solution + +### Steps to Reproduce + +1. +2. +3. + +### Environment + diff --git a/src/frontend/Hanami-AI-Dashboard/src/libHanamiAiSdk/.github/ISSUE_TEMPLATE/cleanup_issue_template.md b/src/frontend/Hanami-AI-Dashboard/src/libHanamiAiSdk/.github/ISSUE_TEMPLATE/cleanup_issue_template.md new file mode 100644 index 00000000..58821f9e --- /dev/null +++ b/src/frontend/Hanami-AI-Dashboard/src/libHanamiAiSdk/.github/ISSUE_TEMPLATE/cleanup_issue_template.md @@ -0,0 +1,12 @@ +--- +name: cleanup-issue template +about: Use this template to request cleanup and code restructuring +labels: code cleanup / QA +--- + +## Cleanup-request + +### Description + +### Possible Implementation + diff --git a/src/frontend/Hanami-AI-Dashboard/src/libHanamiAiSdk/.github/ISSUE_TEMPLATE/config.yml b/src/frontend/Hanami-AI-Dashboard/src/libHanamiAiSdk/.github/ISSUE_TEMPLATE/config.yml new file mode 100644 index 00000000..99d680b0 --- /dev/null +++ b/src/frontend/Hanami-AI-Dashboard/src/libHanamiAiSdk/.github/ISSUE_TEMPLATE/config.yml @@ -0,0 +1,2 @@ +blank_issues_enabled: false + diff --git a/src/frontend/Hanami-AI-Dashboard/src/libHanamiAiSdk/.github/ISSUE_TEMPLATE/feature_request_issue_template.md b/src/frontend/Hanami-AI-Dashboard/src/libHanamiAiSdk/.github/ISSUE_TEMPLATE/feature_request_issue_template.md new file mode 100644 index 00000000..8f03a14c --- /dev/null +++ b/src/frontend/Hanami-AI-Dashboard/src/libHanamiAiSdk/.github/ISSUE_TEMPLATE/feature_request_issue_template.md @@ -0,0 +1,15 @@ +--- +name: feature-request template +about: Use this template to request new features and enhancements +labels: feature / enhancement, documentation +--- + +## Feature-request + +### Description + +### Related Issue + +### Kitsunemimi-Repos, which have to be updated + +### Possible Implementation diff --git a/src/frontend/Hanami-AI-Dashboard/src/libHanamiAiSdk/.github/ISSUE_TEMPLATE/tag_request_issue_template.md b/src/frontend/Hanami-AI-Dashboard/src/libHanamiAiSdk/.github/ISSUE_TEMPLATE/tag_request_issue_template.md new file mode 100644 index 00000000..bdbb7c67 --- /dev/null +++ b/src/frontend/Hanami-AI-Dashboard/src/libHanamiAiSdk/.github/ISSUE_TEMPLATE/tag_request_issue_template.md @@ -0,0 +1,14 @@ +--- +name: tag-request template +about: Tag check-list +--- + +## Tag-request + +### Checklist + +- [ ] Changelog +- [ ] Version-tag in pro-file +- [ ] Version-tag of requirements in Readme +- [ ] Version-tag of requirements in build-script +- [ ] Add tag diff --git a/src/frontend/Hanami-AI-Dashboard/src/libHanamiAiSdk/.github/PULL_REQUEST_TEMPLATE.md b/src/frontend/Hanami-AI-Dashboard/src/libHanamiAiSdk/.github/PULL_REQUEST_TEMPLATE.md new file mode 100644 index 00000000..a39ba486 --- /dev/null +++ b/src/frontend/Hanami-AI-Dashboard/src/libHanamiAiSdk/.github/PULL_REQUEST_TEMPLATE.md @@ -0,0 +1,5 @@ +## Description + +## Related Issues + +## How it was tested? diff --git a/src/frontend/Hanami-AI-Dashboard/src/libHanamiAiSdk/.github/workflows/build_test.yml b/src/frontend/Hanami-AI-Dashboard/src/libHanamiAiSdk/.github/workflows/build_test.yml new file mode 100644 index 00000000..cab61323 --- /dev/null +++ b/src/frontend/Hanami-AI-Dashboard/src/libHanamiAiSdk/.github/workflows/build_test.yml @@ -0,0 +1,28 @@ +name: build-and-test +on: [push] + +jobs: + build: + runs-on: ubuntu-latest + container: + image: registry.gitlab.com/kitsudaiki/kitsunemimi-ci-docker-images/normal-tests:1.2.0 + name: build-job + steps: + - name: "Checkout repository" + run: | + # use manually clone, because with the "actions/checkout@v3" action the name of the + # branch can not be read by the git commands, which is necessary for the build-script + git clone https://github.com/kitsudaiki/${GITHUB_REPOSITORY#*/}.git + cd ${GITHUB_REPOSITORY#*/} + git checkout ${GITHUB_REF#refs/heads/} + - name: "update package-list" + run: apt-get update + - name: "install missing packages" + run: apt-get install -y libssl-dev libcrypto++-dev + - name: "Build project" + env: + CLONE_TOKEN: ${{ secrets.CLONE_TOKEN }} + run: | + cd ${GITHUB_REPOSITORY#*/} + ls -l + ./cpp/build.sh test \ No newline at end of file diff --git a/src/frontend/Hanami-AI-Dashboard/src/libHanamiAiSdk/.gitignore b/src/frontend/Hanami-AI-Dashboard/src/libHanamiAiSdk/.gitignore new file mode 100644 index 00000000..2f01727b --- /dev/null +++ b/src/frontend/Hanami-AI-Dashboard/src/libHanamiAiSdk/.gitignore @@ -0,0 +1,3 @@ +build +*.pro.user + diff --git a/src/frontend/Hanami-AI-Dashboard/src/libHanamiAiSdk/.gitlab/issue_templates/bug_issue_template.md b/src/frontend/Hanami-AI-Dashboard/src/libHanamiAiSdk/.gitlab/issue_templates/bug_issue_template.md new file mode 100644 index 00000000..9bcf93e8 --- /dev/null +++ b/src/frontend/Hanami-AI-Dashboard/src/libHanamiAiSdk/.gitlab/issue_templates/bug_issue_template.md @@ -0,0 +1,4 @@ +## BUG-issue + +### Description + diff --git a/src/frontend/Hanami-AI-Dashboard/src/libHanamiAiSdk/.gitlab/issue_templates/cleanup_issue_template.md b/src/frontend/Hanami-AI-Dashboard/src/libHanamiAiSdk/.gitlab/issue_templates/cleanup_issue_template.md new file mode 100644 index 00000000..294b86ec --- /dev/null +++ b/src/frontend/Hanami-AI-Dashboard/src/libHanamiAiSdk/.gitlab/issue_templates/cleanup_issue_template.md @@ -0,0 +1,4 @@ +## Cleanup-request + +### Description + diff --git a/src/frontend/Hanami-AI-Dashboard/src/libHanamiAiSdk/.gitlab/issue_templates/feature_request_issue_template.md b/src/frontend/Hanami-AI-Dashboard/src/libHanamiAiSdk/.gitlab/issue_templates/feature_request_issue_template.md new file mode 100644 index 00000000..7f90c3f1 --- /dev/null +++ b/src/frontend/Hanami-AI-Dashboard/src/libHanamiAiSdk/.gitlab/issue_templates/feature_request_issue_template.md @@ -0,0 +1,6 @@ +## Feature-request + +### Description + +### Related Issue + diff --git a/src/frontend/Hanami-AI-Dashboard/src/libHanamiAiSdk/.gitlab/issue_templates/tag_request_issue_template.md b/src/frontend/Hanami-AI-Dashboard/src/libHanamiAiSdk/.gitlab/issue_templates/tag_request_issue_template.md new file mode 100644 index 00000000..de20429d --- /dev/null +++ b/src/frontend/Hanami-AI-Dashboard/src/libHanamiAiSdk/.gitlab/issue_templates/tag_request_issue_template.md @@ -0,0 +1,9 @@ +## Tag-request + +### Checklist + +- [ ] Changelog +- [ ] Version-tag in pro-file +- [ ] Version-tag of requirements in Readme +- [ ] Version-tag of requirements in build-script +- [ ] Add tag diff --git a/src/frontend/Hanami-AI-Dashboard/src/libHanamiAiSdk/.gitlab/merge_request_templates/merge_request_template.md b/src/frontend/Hanami-AI-Dashboard/src/libHanamiAiSdk/.gitlab/merge_request_templates/merge_request_template.md new file mode 100644 index 00000000..a39ba486 --- /dev/null +++ b/src/frontend/Hanami-AI-Dashboard/src/libHanamiAiSdk/.gitlab/merge_request_templates/merge_request_template.md @@ -0,0 +1,5 @@ +## Description + +## Related Issues + +## How it was tested? diff --git a/src/frontend/Hanami-AI-Dashboard/src/libHanamiAiSdk/CHANGELOG b/src/frontend/Hanami-AI-Dashboard/src/libHanamiAiSdk/CHANGELOG new file mode 100644 index 00000000..5eb1daa9 --- /dev/null +++ b/src/frontend/Hanami-AI-Dashboard/src/libHanamiAiSdk/CHANGELOG @@ -0,0 +1,44 @@ +# Changelog + +## [0.3.1] - 2022-07-02 + +### Changed +- use POST instead of GET requests for token-requests +- rename 'pw' to 'password' in token-request for better error-messages + + +## [0.3.0] - 2022-06-28 + +### Added +- cpp: + - snapshot-handling + - direct-io + +### Changed +- cpp: + - change upload of data from sakura-network to websockets + + +## [0.2.0] - 2022-02-14 + +### Changed +- cpp: + - process to upload data by using sakura-networking instead of http + - changed request of results + + +## [0.1.0] - 2022-01-09 + +### Added +- basic version with: + - go: + - user-handling + - request and download rest-api-docu + + - cpp: + - user-handing + - upload train-data + - handling templates + - handling clusters + - create learn- and requests-tasks + diff --git a/src/frontend/Hanami-AI-Dashboard/src/libHanamiAiSdk/LICENSE b/src/frontend/Hanami-AI-Dashboard/src/libHanamiAiSdk/LICENSE new file mode 100644 index 00000000..de9e0763 --- /dev/null +++ b/src/frontend/Hanami-AI-Dashboard/src/libHanamiAiSdk/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright 2021 Tobias Anker + + 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. diff --git a/src/frontend/Hanami-AI-Dashboard/src/libHanamiAiSdk/README.md b/src/frontend/Hanami-AI-Dashboard/src/libHanamiAiSdk/README.md new file mode 100644 index 00000000..e26e6ea4 --- /dev/null +++ b/src/frontend/Hanami-AI-Dashboard/src/libHanamiAiSdk/README.md @@ -0,0 +1,77 @@ +# libKitsumiAiSdk + +![Github workfloat status](https://img.shields.io/github/actions/workflow/status/kitsudaiki/libKitsumiAiSdk/build_test.yml?branch=develop&style=flat-square&label=build%20and%20test) +![GitHub tag (latest SemVer)](https://img.shields.io/github/v/tag/kitsudaiki/libKitsumiAiSdk?label=version&style=flat-square) +![GitHub](https://img.shields.io/github/license/kitsudaiki/libKitsumiAiSdk?style=flat-square) +![C++Version](https://img.shields.io/badge/c%2B%2B-17-blue?style=flat-square) +![Platform](https://img.shields.io/badge/platform-Linux--x64-lightgrey?style=flat-square) + +## Description + +SDK-library for Hanami-AI: https://github.com/kitsudaiki/Hanami-AI + +## Build + +#### Required build tools for C++-part + +name | repository | version | task +--- | --- | --- | --- +g++ | g++ | >= 8.0 | Compiler for the C++ code. +make | make | >= 4.0 | process the make-file, which is created by qmake to build the programm with g++ +qmake | qt5-qmake | >= 5.0 | This package provides the tool qmake, which is similar to cmake and create the make-file for compilation. +FLEX | flex | >= 2.6 | Build the lexer-code for all used parser. +GNU Bison | bison | >= 3.0 | Build the parser-code together with the lexer-code. + +#### Required generic libraries for C++-part + +name | repository | version | task +--- | --- | --- | --- +ssl library | libssl-dev | 1.1.x | encryption for tls connections +crpyto++ | libcrypto++-dev | >= 5.6 | provides encryption-functions like AES +boost-library | libboost1.71-dev | >= 1.71 | provides boost beast library for HTTP and Websocket client + +#### Required kitsunemimi libraries for C++-part + +Repository-Name | Version-Tag | Download-Path +--- | --- | --- +libKitsunemimiCommon | develop | https://github.com/kitsudaiki/libKitsunemimiCommon.git +libKitsunemimiJson | develop | https://github.com/kitsudaiki/libKitsunemimiJson.git +libKitsunemimiIni | develop | https://github.com/kitsudaiki/libKitsunemimiIni.git +ibKitsunemimiArgs | develop | https://github.com/kitsudaiki/ibKitsunemimiArgs.git +libKitsunemimiConfig | develop | https://github.com/kitsudaiki/libKitsunemimiConfig.git +libKitsunemimiCrypto | develop | https://github.com/kitsudaiki/libKitsunemimiCrypto.git +libKitsunemimiHanamiCommon | develop | https://github.com/kitsudaiki/libKitsunemimiHanamiCommon.git +libKitsunemimiHanamiMessages | develop | https://github.com/kitsudaiki/libKitsunemimiHanamiMessages.git + +#### Required build tools for go-part + +name | repository | version | task +--- | --- | --- | --- +go | golang | >= 1.13 | Compiler for the go code. + +### build library (C++-part) + +In all of my repositories you will find a `build.sh`. You only have to run this script. It doesn't required sudo, because you have to install required tool via apt, for example, by yourself. But if other projects from me are required, it download them from github and build them in the correct version too. This script is also use by the ci-pipeline, so its tested with every commit. + + +Run the following commands: + +``` +git clone https://github.com/kitsudaiki/libKitsumiAiSdk.git +cd libKitsumiAiSdk/cpp +./build.sh +cd ../result +``` + +It create automatic a `build` and `result` directory in the directory, where you have cloned the project. At first it build all into the `build`-directory and after all build-steps are finished, it copy the include directory from the cloned repository and the build library into the `result`-directory. So you have all in one single place. + +Tested on Debian and Ubuntu. If you use Centos, Arch, etc and the build-script fails on your machine, then please write me a mail and I will try to fix the script. + + +## Contributing + +Please give me as many inputs as possible: Bugs, bad code style, bad documentation and so on. + +## License + +This project is licensed under the Apache License Version 2.0 - see the [LICENSE](LICENSE) file for details diff --git a/src/frontend/Hanami-AI-Dashboard/src/libHanamiAiSdk/cpp/README.md b/src/frontend/Hanami-AI-Dashboard/src/libHanamiAiSdk/cpp/README.md new file mode 100644 index 00000000..85c24036 --- /dev/null +++ b/src/frontend/Hanami-AI-Dashboard/src/libHanamiAiSdk/cpp/README.md @@ -0,0 +1 @@ +# libKitsumiAiSdk \ No newline at end of file diff --git a/src/frontend/Hanami-AI-Dashboard/src/libHanamiAiSdk/cpp/build.sh b/src/frontend/Hanami-AI-Dashboard/src/libHanamiAiSdk/cpp/build.sh new file mode 100755 index 00000000..56762036 --- /dev/null +++ b/src/frontend/Hanami-AI-Dashboard/src/libHanamiAiSdk/cpp/build.sh @@ -0,0 +1,122 @@ +#!/bin/bash + +# get current directory-path and the path of the parent-directory +DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )" +PARENT_DIR="$(dirname "$DIR")" +CURRENT_BRANCH=$(git rev-parse --abbrev-ref HEAD) + +# create build-directory +BUILD_DIR="$PARENT_DIR/build" +mkdir -p $BUILD_DIR + +# create directory for the final result +RESULT_DIR="$PARENT_DIR/result" +mkdir -p $RESULT_DIR + +#----------------------------------------------------------------------------------------------------------------- + +function build_kitsune_lib_repo () { + REPO_NAME=$1 + NUMBER_OF_THREADS=$2 + ADDITIONAL_CONFIGS=$3 + + # create build directory for repo and go into this directory + REPO_DIR="$BUILD_DIR/$REPO_NAME" + mkdir -p $REPO_DIR + cd $REPO_DIR + + # build repo library with qmake + /usr/lib/x86_64-linux-gnu/qt5/bin/qmake "$PARENT_DIR/$REPO_NAME/$REPO_NAME.pro" -spec linux-g++ "CONFIG += optimize_full staticlib $ADDITIONAL_CONFIGS" + /usr/bin/make -j$NUMBER_OF_THREADS + + # copy build-result and include-files into the result-directory + echo "----------------------------------------------------------------------" + echo $RESULT_DIR + cp $REPO_DIR/src/$REPO_NAME.a $RESULT_DIR/ + cp -r $PARENT_DIR/$REPO_NAME/include $RESULT_DIR/ + ls -l $RESULT_DIR/include/ + ls -l $RESULT_DIR +} + +function download_repo_github () { + REPO_NAME=$1 + TAG_OR_BRANCH=$2 + + echo "" + echo "" + echo "++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++" + echo "$REPO_NAME" + echo "++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++" + echo "Branch/Tag: $TAG_OR_BRANCH" + + # clone repo + git clone https://github.com/kitsudaiki/$REPO_NAME.git "$PARENT_DIR/$REPO_NAME" + cd "$PARENT_DIR/$REPO_NAME" + + # checkout branch + if [[ $CURRENT_BRANCH =~ ^tag.* ]] || [[ $CURRENT_BRANCH =~ ^hotfix.* ]] || [[ $CURRENT_BRANCH =~ ^v.* ]]; then + # if a stable branch, then use the defined tag of branch + # check if defined branch even exist + BRANCH_EXIST=$(git ls-remote --heads origin $TAG_OR_BRANCH) + if [[ -z "$BRANCH_EXIST" ]]; then + echo "" + echo "-------------------------------------------------------------------------------------" + echo "Branch or tag '$TAG_OR_BRANCH' does not exist for the repository '$REPO_NAME'" + echo "-------------------------------------------------------------------------------------" + echo "" + exit 1 + fi + git checkout $TAG_OR_BRANCH + else + # if develop or feature branch, then try to checkout the feature-branch in the other repo as well + # or otherwise use the develop-branch as default + BRANCH_EXIST=$(git ls-remote --heads origin $CURRENT_BRANCH) + if [[ -n "$BRANCH_EXIST" ]]; then + git checkout $CURRENT_BRANCH + else + git checkout develop + fi + fi +} + +function get_required_kitsune_lib_repo () { + REPO_NAME=$1 + TAG_OR_BRANCH=$2 + NUMBER_OF_THREADS=$3 + + download_repo_github $REPO_NAME $TAG_OR_BRANCH + build_kitsune_lib_repo $REPO_NAME $NUMBER_OF_THREADS +} + +#----------------------------------------------------------------------------------------------------------------- + +echo "" +echo "###########################################################################################################" +echo "" +get_required_kitsune_lib_repo "libKitsunemimiCommon" "develop" 8 +get_required_kitsune_lib_repo "libKitsunemimiJson" "develop" 1 +get_required_kitsune_lib_repo "libKitsunemimiIni" "develop" 1 +get_required_kitsune_lib_repo "libKitsunemimiArgs" "develop" 8 +get_required_kitsune_lib_repo "libKitsunemimiConfig" "develop" 8 +echo "" +echo "###########################################################################################################" +echo "" +get_required_kitsune_lib_repo "libKitsunemimiCrypto" "develop" 8 +echo "" +echo "###########################################################################################################" +echo "" +get_required_kitsune_lib_repo "libKitsunemimiHanamiCommon" "develop" 8 +download_repo_github "libKitsunemimiHanamiMessages" "develop" +echo "" +echo "###########################################################################################################" + +#----------------------------------------------------------------------------------------------------------------- + +if [ $1 = "test" ]; then + build_kitsune_lib_repo "libKitsumiAiSdk" 8 "run_tests" +else + build_kitsune_lib_repo "libKitsumiAiSdk" 8 +fi + +#----------------------------------------------------------------------------------------------------------------- + diff --git a/src/frontend/Hanami-AI-Dashboard/src/libHanamiAiSdk/cpp/defaults.pri b/src/frontend/Hanami-AI-Dashboard/src/libHanamiAiSdk/cpp/defaults.pri new file mode 100644 index 00000000..6f76e98b --- /dev/null +++ b/src/frontend/Hanami-AI-Dashboard/src/libHanamiAiSdk/cpp/defaults.pri @@ -0,0 +1,2 @@ +INCLUDEPATH += $$PWD/src \ + $$PWD/include diff --git a/src/frontend/Hanami-AI-Dashboard/src/libHanamiAiSdk/cpp/include/libHanamiAiSdk/cluster.h b/src/frontend/Hanami-AI-Dashboard/src/libHanamiAiSdk/cpp/include/libHanamiAiSdk/cluster.h new file mode 100644 index 00000000..94ba24da --- /dev/null +++ b/src/frontend/Hanami-AI-Dashboard/src/libHanamiAiSdk/cpp/include/libHanamiAiSdk/cluster.h @@ -0,0 +1,69 @@ +/** + * @file cluster.h + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2021 Tobias Anker + * + * 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. + */ + +#ifndef KITSUNEMIMI_HANAMISDK_CLUSTER_H +#define KITSUNEMIMI_HANAMISDK_CLUSTER_H + +#include + +namespace HanamiAI +{ + +class WebsocketClient; + +bool createCluster(std::string &result, + const std::string &clusterName, + const std::string &clusterTemplate, + Kitsunemimi::ErrorContainer &error); + +bool getCluster(std::string &result, + const std::string &clusterUuid, + Kitsunemimi::ErrorContainer &error); + +bool listCluster(std::string &result, + Kitsunemimi::ErrorContainer &error); + +bool deleteCluster(std::string &result, + const std::string &clusterUuid, + Kitsunemimi::ErrorContainer &error); + +bool saveCluster(std::string &result, + const std::string &clusterUuid, + const std::string &snapshotName, + Kitsunemimi::ErrorContainer &error); + +bool restoreCluster(std::string &result, + const std::string &clusterUuid, + const std::string &snapshotUuid, + Kitsunemimi::ErrorContainer &error); + +bool switchToTaskMode(std::string &result, + const std::string &clusterUuid, + Kitsunemimi::ErrorContainer &error); + +WebsocketClient* switchToDirectMode(std::string &result, + const std::string &clusterUuid, + Kitsunemimi::ErrorContainer &error); + +} // namespace HanamiAI + +#endif // KITSUNEMIMI_HANAMISDK_CLUSTER_H diff --git a/src/frontend/Hanami-AI-Dashboard/src/libHanamiAiSdk/cpp/include/libHanamiAiSdk/common/websocket_client.h b/src/frontend/Hanami-AI-Dashboard/src/libHanamiAiSdk/cpp/include/libHanamiAiSdk/common/websocket_client.h new file mode 100644 index 00000000..97e3df73 --- /dev/null +++ b/src/frontend/Hanami-AI-Dashboard/src/libHanamiAiSdk/cpp/include/libHanamiAiSdk/common/websocket_client.h @@ -0,0 +1,74 @@ +/** + * @file websocket_client.h + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2021 Tobias Anker + * + * 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. + */ + +#ifndef WEBSOCKETCLIENT_H +#define WEBSOCKETCLIENT_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace beast = boost::beast; // from +namespace http = beast::http; // from +namespace websocket = beast::websocket; // from +namespace net = boost::asio; // from +namespace ssl = boost::asio::ssl; // from +using tcp = boost::asio::ip::tcp; // from + +namespace HanamiAI +{ + +class WebsocketClient +{ +public: + WebsocketClient(); + ~WebsocketClient(); + + bool initClient(std::string &socketUuid, + const std::string &token, + const std::string &target, + const std::string &host, + const std::string &port, + Kitsunemimi::ErrorContainer &error); + bool sendMessage(const void* data, + const uint64_t dataSize, + Kitsunemimi::ErrorContainer &error); + + uint8_t* readMessage(uint64_t &numberOfByes, + Kitsunemimi::ErrorContainer &error); + +private: + websocket::stream>* m_websocket = nullptr; + bool loadCertificates(boost::asio::ssl::context &ctx); +}; + +} // namespace HanamiAI + +#endif // WEBSOCKETCLIENT_H diff --git a/src/frontend/Hanami-AI-Dashboard/src/libHanamiAiSdk/cpp/include/libHanamiAiSdk/data_set.h b/src/frontend/Hanami-AI-Dashboard/src/libHanamiAiSdk/cpp/include/libHanamiAiSdk/data_set.h new file mode 100644 index 00000000..c63922e4 --- /dev/null +++ b/src/frontend/Hanami-AI-Dashboard/src/libHanamiAiSdk/cpp/include/libHanamiAiSdk/data_set.h @@ -0,0 +1,64 @@ +/** + * @file data_set.h + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2021 Tobias Anker + * + * 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. + */ + +#ifndef KITSUNEMIMI_HANAMISDK_DATA_SET_H +#define KITSUNEMIMI_HANAMISDK_DATA_SET_H + +#include + +namespace HanamiAI +{ + +bool uploadCsvData(std::string &result, + const std::string &dataSetName, + const std::string &inputFilePath, + Kitsunemimi::ErrorContainer &error); + +bool uploadMnistData(std::string &result, + const std::string &dataSetName, + const std::string &inputFilePath, + const std::string &labelFilePath, + Kitsunemimi::ErrorContainer &error); + +bool checkDataset(std::string &result, + const std::string &dataUuid, + const std::string &resultUuid, + Kitsunemimi::ErrorContainer &error); + +bool getDataset(std::string &result, + const std::string &dataUuid, + Kitsunemimi::ErrorContainer &error); + +bool listDatasets(std::string &result, + Kitsunemimi::ErrorContainer &error); + +bool deleteDataset(std::string &result, + const std::string &dataUuid, + Kitsunemimi::ErrorContainer &error); + +bool getDatasetProgress(std::string &result, + const std::string &dataUuid, + Kitsunemimi::ErrorContainer &error); + +} // namespace HanamiAI + +#endif // KITSUNEMIMI_HANAMISDK_DATA_SET_H diff --git a/src/frontend/Hanami-AI-Dashboard/src/libHanamiAiSdk/cpp/include/libHanamiAiSdk/init.h b/src/frontend/Hanami-AI-Dashboard/src/libHanamiAiSdk/cpp/include/libHanamiAiSdk/init.h new file mode 100644 index 00000000..5b6f5460 --- /dev/null +++ b/src/frontend/Hanami-AI-Dashboard/src/libHanamiAiSdk/cpp/include/libHanamiAiSdk/init.h @@ -0,0 +1,39 @@ +/** + * @file init.h + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2021 Tobias Anker + * + * 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. + */ + +#ifndef KITSUNEMIMI_HANAMISDK_INIT_H +#define KITSUNEMIMI_HANAMISDK_INIT_H + +#include + +namespace HanamiAI +{ + +bool initClient(const std::string &host, + const std::string &port, + const std::string &user, + const std::string &password, + Kitsunemimi::ErrorContainer &error); + +} // namespace HanamiAI + +#endif // KITSUNEMIMI_HANAMISDK_INIT_H diff --git a/src/frontend/Hanami-AI-Dashboard/src/libHanamiAiSdk/cpp/include/libHanamiAiSdk/io.h b/src/frontend/Hanami-AI-Dashboard/src/libHanamiAiSdk/cpp/include/libHanamiAiSdk/io.h new file mode 100644 index 00000000..72b204cd --- /dev/null +++ b/src/frontend/Hanami-AI-Dashboard/src/libHanamiAiSdk/cpp/include/libHanamiAiSdk/io.h @@ -0,0 +1,62 @@ +/** + * @file io.cpp + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2021 Tobias Anker + * + * 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. + */ + +#ifndef IO_H +#define IO_H + +#include +#include +#include +#include + +#include + +namespace HanamiAI +{ +class WebsocketClient; + +bool learn(WebsocketClient* wsClient, + std::vector &inputValues, + std::vector &shouldValues, + Kitsunemimi::ErrorContainer &error); + +float* request(WebsocketClient* wsClient, + std::vector &inputValues, + uint64_t &numberOfOutputValues, + Kitsunemimi::ErrorContainer &error); + +bool learn(WebsocketClient* wsClient, + float* inputValues, + const uint64_t numberOfInputValues, + float* shouldValues, + const uint64_t numberOfShouldValues, + Kitsunemimi::ErrorContainer &error); + +float* request(WebsocketClient* wsClient, + float* inputData, + const uint64_t numberOfInputValues, + uint64_t &numberOfOutputValues, + Kitsunemimi::ErrorContainer &error); + +} // namespace HanamiAI + +#endif // IO_H diff --git a/src/frontend/Hanami-AI-Dashboard/src/libHanamiAiSdk/cpp/include/libHanamiAiSdk/project.h b/src/frontend/Hanami-AI-Dashboard/src/libHanamiAiSdk/cpp/include/libHanamiAiSdk/project.h new file mode 100644 index 00000000..fee129dc --- /dev/null +++ b/src/frontend/Hanami-AI-Dashboard/src/libHanamiAiSdk/cpp/include/libHanamiAiSdk/project.h @@ -0,0 +1,49 @@ +/** + * @file project.h + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2021 Tobias Anker + * + * 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. + */ + +#ifndef KITSUNEMIMI_HANAMISDK_PROJECT_H +#define KITSUNEMIMI_HANAMISDK_PROJECT_H + +#include + +namespace HanamiAI +{ + +bool createProject(std::string &result, + const std::string &projectId, + const std::string &projectName, + Kitsunemimi::ErrorContainer &error); + +bool getProject(std::string &result, + const std::string &projectId, + Kitsunemimi::ErrorContainer &error); + +bool listProject(std::string &result, + Kitsunemimi::ErrorContainer &error); + +bool deleteProject(std::string &result, + const std::string &projectId, + Kitsunemimi::ErrorContainer &error); + +} // namespace HanamiAI + +#endif // KITSUNEMIMI_HANAMISDK_PROJECT_H diff --git a/src/frontend/Hanami-AI-Dashboard/src/libHanamiAiSdk/cpp/include/libHanamiAiSdk/request_result.h b/src/frontend/Hanami-AI-Dashboard/src/libHanamiAiSdk/cpp/include/libHanamiAiSdk/request_result.h new file mode 100644 index 00000000..eed2e207 --- /dev/null +++ b/src/frontend/Hanami-AI-Dashboard/src/libHanamiAiSdk/cpp/include/libHanamiAiSdk/request_result.h @@ -0,0 +1,44 @@ +/** + * @file request_result.h + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2021 Tobias Anker + * + * 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. + */ + +#ifndef KITSUNEMIMI_HANAMISDK_REQUEST_RESULT_H +#define KITSUNEMIMI_HANAMISDK_REQUEST_RESULT_H + +#include + +namespace HanamiAI +{ + +bool getRequestResult(std::string &result, + const std::string &userId, + Kitsunemimi::ErrorContainer &error); + +bool listRequestResult(std::string &result, + Kitsunemimi::ErrorContainer &error); + +bool deleteRequestResult(std::string &result, + const std::string &userId, + Kitsunemimi::ErrorContainer &error); + +} // namespace HanamiAI + +#endif // KITSUNEMIMI_HANAMISDK_REQUEST_RESULT_H diff --git a/src/frontend/Hanami-AI-Dashboard/src/libHanamiAiSdk/cpp/include/libHanamiAiSdk/snapshot.h b/src/frontend/Hanami-AI-Dashboard/src/libHanamiAiSdk/cpp/include/libHanamiAiSdk/snapshot.h new file mode 100644 index 00000000..6fa0bad2 --- /dev/null +++ b/src/frontend/Hanami-AI-Dashboard/src/libHanamiAiSdk/cpp/include/libHanamiAiSdk/snapshot.h @@ -0,0 +1,44 @@ +/** + * @file snapshot.h + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2021 Tobias Anker + * + * 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. + */ + +#ifndef KITSUNEMIMI_HANAMISDK_SNAPSHOT_H +#define KITSUNEMIMI_HANAMISDK_SNAPSHOT_H + +#include + +namespace HanamiAI +{ + +bool getSnapshot(std::string &result, + const std::string &snapshotUuid, + Kitsunemimi::ErrorContainer &error); + +bool listSnapshot(std::string &result, + Kitsunemimi::ErrorContainer &error); + +bool deleteSnapshot(std::string &result, + const std::string &snapshotUuid, + Kitsunemimi::ErrorContainer &error); + +} // namespace HanamiAI + +#endif // KITSUNEMIMI_HANAMISDK_SNAPSHOT_H diff --git a/src/frontend/Hanami-AI-Dashboard/src/libHanamiAiSdk/cpp/include/libHanamiAiSdk/task.h b/src/frontend/Hanami-AI-Dashboard/src/libHanamiAiSdk/cpp/include/libHanamiAiSdk/task.h new file mode 100644 index 00000000..3b15a580 --- /dev/null +++ b/src/frontend/Hanami-AI-Dashboard/src/libHanamiAiSdk/cpp/include/libHanamiAiSdk/task.h @@ -0,0 +1,54 @@ +/** + * @file task.h + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2021 Tobias Anker + * + * 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. + */ + +#ifndef KITSUNEMIMI_HANAMISDK_TASK_H +#define KITSUNEMIMI_HANAMISDK_TASK_H + +#include + +namespace HanamiAI +{ + +bool createTask(std::string &result, + const std::string &name, + const std::string &type, + const std::string &clusterUuid, + const std::string &dataSetUuid, + Kitsunemimi::ErrorContainer &error); + +bool getTask(std::string &result, + const std::string &taskUuid, + const std::string &clusterUuid, + Kitsunemimi::ErrorContainer &error); + +bool listTask(std::string &result, + const std::string &clusterUuid, + Kitsunemimi::ErrorContainer &error); + +bool deleteTask(std::string &result, + const std::string &taskUuid, + const std::string &clusterUuid, + Kitsunemimi::ErrorContainer &error); + +} // namespace HanamiAI + +#endif // KITSUNEMIMI_HANAMISDK_TASK_H diff --git a/src/frontend/Hanami-AI-Dashboard/src/libHanamiAiSdk/cpp/include/libHanamiAiSdk/template.h b/src/frontend/Hanami-AI-Dashboard/src/libHanamiAiSdk/cpp/include/libHanamiAiSdk/template.h new file mode 100644 index 00000000..b6e180fe --- /dev/null +++ b/src/frontend/Hanami-AI-Dashboard/src/libHanamiAiSdk/cpp/include/libHanamiAiSdk/template.h @@ -0,0 +1,49 @@ +/** + * @file template.h + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2021 Tobias Anker + * + * 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. + */ + +#ifndef KITSUNEMIMI_HANAMISDK_TEMPLATE_H +#define KITSUNEMIMI_HANAMISDK_TEMPLATE_H + +#include + +namespace HanamiAI +{ + +bool uploadTemplate(std::string &result, + const std::string &templateName, + const std::string &segmentTemplate, + Kitsunemimi::ErrorContainer &error); + +bool getTemplate(std::string &result, + const std::string &templateUuid, + Kitsunemimi::ErrorContainer &error); + +bool listTemplate(std::string &result, + Kitsunemimi::ErrorContainer &error); + +bool deleteTemplate(std::string &result, + const std::string &templateUuid, + Kitsunemimi::ErrorContainer &error); + +} // namespace HanamiAI + +#endif // KITSUNEMIMI_HANAMISDK_TEMPLATE_H diff --git a/src/frontend/Hanami-AI-Dashboard/src/libHanamiAiSdk/cpp/include/libHanamiAiSdk/user.h b/src/frontend/Hanami-AI-Dashboard/src/libHanamiAiSdk/cpp/include/libHanamiAiSdk/user.h new file mode 100644 index 00000000..c5191976 --- /dev/null +++ b/src/frontend/Hanami-AI-Dashboard/src/libHanamiAiSdk/cpp/include/libHanamiAiSdk/user.h @@ -0,0 +1,70 @@ +/** + * @file user.h + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2021 Tobias Anker + * + * 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. + */ + +#ifndef KITSUNEMIMI_HANAMISDK_USER_H +#define KITSUNEMIMI_HANAMISDK_USER_H + +#include + +namespace HanamiAI +{ + +bool createUser(std::string &result, + const std::string &userId, + const std::string &userName, + const std::string &password, + const bool isAdmin, + Kitsunemimi::ErrorContainer &error); + +bool getUser(std::string &result, + const std::string &userId, + Kitsunemimi::ErrorContainer &error); + +bool listUser(std::string &result, + Kitsunemimi::ErrorContainer &error); + +bool deleteUser(std::string &result, + const std::string &userId, + Kitsunemimi::ErrorContainer &error); + +bool addProjectToUser(std::string &result, + const std::string &userId, + const std::string &projectId, + const std::string &role, + const bool isProjectAdmin, + Kitsunemimi::ErrorContainer &error); + +bool removeProjectFromUser(std::string &result, + const std::string &userId, + const std::string &projectId, + Kitsunemimi::ErrorContainer &error); + +bool listProjectsOfUser(std::string &result, + Kitsunemimi::ErrorContainer &error); + +bool switchProject(std::string &result, + const std::string &projectId, + Kitsunemimi::ErrorContainer &error); + +} // namespace HanamiAI + +#endif // KITSUNEMIMI_HANAMISDK_USER_H diff --git a/src/frontend/Hanami-AI-Dashboard/src/libHanamiAiSdk/cpp/libHanamiAiSdk.pro b/src/frontend/Hanami-AI-Dashboard/src/libHanamiAiSdk/cpp/libHanamiAiSdk.pro new file mode 100644 index 00000000..b1fa54d7 --- /dev/null +++ b/src/frontend/Hanami-AI-Dashboard/src/libHanamiAiSdk/cpp/libHanamiAiSdk.pro @@ -0,0 +1,13 @@ +TEMPLATE = subdirs +CONFIG += ordered +QT -= qt core gui +CONFIG += c++14 + +SUBDIRS = src + +run_tests { + SUBDIRS += tests + + tests.depends = src +} + diff --git a/src/frontend/Hanami-AI-Dashboard/src/libHanamiAiSdk/cpp/src/cluster.cpp b/src/frontend/Hanami-AI-Dashboard/src/libHanamiAiSdk/cpp/src/cluster.cpp new file mode 100644 index 00000000..7d4c291f --- /dev/null +++ b/src/frontend/Hanami-AI-Dashboard/src/libHanamiAiSdk/cpp/src/cluster.cpp @@ -0,0 +1,324 @@ +/** + * @file cluster.cpp + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2021 Tobias Anker + * + * 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 +#include +#include +#include + +namespace HanamiAI +{ + +/** + * @brief create a new cluster from a template on kyouko + * + * @param result reference for response-message + * @param clusterName name of the new cluster + * @param clusterTemplate information to build the new cluster + * @param error reference for error-output + * + * @return true, if successful, else false + */ +bool +createCluster(std::string &result, + const std::string &clusterName, + const std::string &clusterTemplate, + Kitsunemimi::ErrorContainer &error) +{ + HanamiRequest* request = HanamiAI::HanamiRequest::getInstance(); + + // convert template into base64-string + std::string clusterTemplateB64; + Kitsunemimi::encodeBase64(clusterTemplateB64, + clusterTemplate.c_str(), + clusterTemplate.size()); + + // create request + const std::string path = "/control/kyouko/v1/cluster"; + const std::string vars = ""; + const std::string jsonBody = "{\"name\":\"" + + clusterName + + "\",\"template\":\"" + + clusterTemplateB64 + + "\"}"; + + // send request + if(request->sendPostRequest(result, path, vars, jsonBody, error) == false) + { + error.addMeesage("Failed to create cluster"); + LOG_ERROR(error); + return false; + } + + return true; +} + +/** + * @brief get information of a cluster from kyouko + * + * @param result reference for response-message + * @param clusterUuid uuid of the cluster to get + * @param error reference for error-output + * + * @return true, if successful, else false + */ +bool +getCluster(std::string &result, + const std::string &clusterUuid, + Kitsunemimi::ErrorContainer &error) +{ + // create request + HanamiRequest* request = HanamiRequest::getInstance(); + const std::string path = "/control/kyouko/v1/cluster"; + const std::string vars = "uuid=" + clusterUuid; + + // send request + if(request->sendGetRequest(result, path, vars, error) == false) + { + error.addMeesage("Failed to get cluster with UUID '" + clusterUuid + "'"); + LOG_ERROR(error); + return false; + } + + return true; +} + +/** + * @brief list all visible cluster on kyouko + * + * @param result reference for response-message + * @param error reference for error-output + * + * @return true, if successful, else false + */ +bool +listCluster(std::string &result, + Kitsunemimi::ErrorContainer &error) +{ + // create request + HanamiRequest* request = HanamiRequest::getInstance(); + const std::string path = "/control/kyouko/v1/cluster/all"; + + // send request + if(request->sendGetRequest(result, path, "", error) == false) + { + error.addMeesage("Failed to list clusters"); + LOG_ERROR(error); + return false; + } + + return true; +} + +/** + * @brief delete a cluster with all its tasks from kyouko + * + * @param result reference for response-message + * @param clusterUuid uuid of the cluster to delete + * @param error reference for error-output + * + * @return true, if successful, else false + */ +bool +deleteCluster(std::string &result, + const std::string &clusterUuid, + Kitsunemimi::ErrorContainer &error) +{ + // create request + HanamiRequest* request = HanamiRequest::getInstance(); + const std::string path = "/control/kyouko/v1/cluster"; + const std::string vars = "uuid=" + clusterUuid; + + // send request + if(request->sendDeleteRequest(result, path, vars, error) == false) + { + error.addMeesage("Failed to delete cluster with UUID '" + clusterUuid + "'"); + LOG_ERROR(error); + return false; + } + + return true; +} + +/** + * @brief create a snapshot of a cluster + * + * @param result reference for response-message + * @param clusterUuid uuid of the cluster to delete + * @param snapshotName name of the new snapshot + * @param error reference for error-output + * + * @return true, if successful, else false + */ +bool +saveCluster(std::string &result, + const std::string &clusterUuid, + const std::string &snapshotName, + Kitsunemimi::ErrorContainer &error) +{ + // create request + HanamiRequest* request = HanamiRequest::getInstance(); + const std::string path = "/control/kyouko/v1/cluster/save"; + const std::string vars = ""; + const std::string jsonBody = "{\"name\":\"" + + snapshotName + + "\",\"cluster_uuid\":\"" + + clusterUuid + + "\"}"; + + // send request + if(request->sendPostRequest(result, path, vars, jsonBody, error) == false) + { + error.addMeesage("Failed to save cluster with UUID '" + clusterUuid + "'"); + LOG_ERROR(error); + return false; + } + + return true; +} + +/** + * @brief restore cluster from a snapshot + * + * @param result reference for response-message + * @param clusterUuid uuid of the cluster to delete + * @param snapshotUuid uuid of the snapshot, which should be loaded into the cluster + * @param error reference for error-output + * + * @return true, if successful, else false + */ +bool +restoreCluster(std::string &result, + const std::string &clusterUuid, + const std::string &snapshotUuid, + Kitsunemimi::ErrorContainer &error) +{ + // create request + HanamiRequest* request = HanamiRequest::getInstance(); + const std::string path = "/control/kyouko/v1/cluster/load"; + const std::string vars = ""; + const std::string jsonBody = "{\"snapshot_uuid\":\"" + + snapshotUuid + + "\",\"cluster_uuid\":\"" + + clusterUuid + + "\"}"; + + // send request + if(request->sendPostRequest(result, path, vars, jsonBody, error) == false) + { + error.addMeesage("Failed to restore snapshot with UUID '" + snapshotUuid + "'"); + LOG_ERROR(error); + return false; + } + + return true; +} + +/** + * @brief switch cluster to task-mode + * + * @param result reference for response-message + * @param clusterUuid uuid of the cluster to swtich + * @param error reference for error-output + * + * @return true, if successful, else false + */ +bool +switchToTaskMode(std::string &result, + const std::string &clusterUuid, + Kitsunemimi::ErrorContainer &error) +{ + // create request + HanamiRequest* request = HanamiRequest::getInstance(); + const std::string path = "/control/kyouko/v1/cluster/set_mode"; + const std::string vars = ""; + const std::string jsonBody = "{\"new_state\":\"TASK\"" + ",\"uuid\":\"" + + clusterUuid + + "\"}"; + + // send request + if(request->sendPutRequest(result, path, vars, jsonBody, error) == false) + { + error.addMeesage("Failed to swith cluster with UUID '" + clusterUuid + "' to task-mode"); + LOG_ERROR(error); + return false; + } + + return true; +} + +/** + * @brief switch cluster to direct-mode + * + * @param result reference for response-message + * @param clusterUuid uuid of the cluster to swtich + * @param error reference for error-output + * + * @return true, if successful, else false + */ +WebsocketClient* +switchToDirectMode(std::string &result, + const std::string &clusterUuid, + Kitsunemimi::ErrorContainer &error) +{ + // init websocket-client + WebsocketClient* wsClient = new WebsocketClient(); + std::string websocketUuid = ""; + const bool ret = wsClient->initClient(websocketUuid, + HanamiRequest::getInstance()->getToken(), + "kyouko", + HanamiRequest::getInstance()->getHost(), + HanamiRequest::getInstance()->getPort(), + error); + if(ret == false) + { + error.addMeesage("Failed to init websocket to kyouko"); + LOG_ERROR(error); + delete wsClient; + return nullptr; + } + + // create request + HanamiRequest* request = HanamiRequest::getInstance(); + const std::string path = "/control/kyouko/v1/cluster/set_mode"; + const std::string vars = ""; + const std::string jsonBody = "{\"connection_uuid\":\"" + + websocketUuid + + "\",\"new_state\":\"DIRECT\"" + ",\"uuid\":\"" + + clusterUuid + + "\"}"; + + // send request + if(request->sendPutRequest(result, path, vars, jsonBody, error) == false) + { + error.addMeesage("Failed to swith cluster with UUID '" + clusterUuid + "' to direct-mode"); + LOG_ERROR(error); + delete wsClient; + return nullptr; + } + + return wsClient; +} + +} // namespace HanamiAI diff --git a/src/frontend/Hanami-AI-Dashboard/src/libHanamiAiSdk/cpp/src/common/http_client.cpp b/src/frontend/Hanami-AI-Dashboard/src/libHanamiAiSdk/cpp/src/common/http_client.cpp new file mode 100644 index 00000000..9042a663 --- /dev/null +++ b/src/frontend/Hanami-AI-Dashboard/src/libHanamiAiSdk/cpp/src/common/http_client.cpp @@ -0,0 +1,483 @@ +/** + * @file request.cpp + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2021 Tobias Anker + * + * 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 + +#include + +#include + +namespace HanamiAI +{ + +HanamiAI::HanamiRequest* HanamiRequest::m_instance = nullptr; + +/** + * @brief constructor + */ +HanamiRequest::HanamiRequest() {} + +const std::string& +HanamiRequest::getHost() const +{ + return m_host; +} + +const std::string& +HanamiRequest::getPort() const +{ + return m_port; +} + +/** + * @brief HanamiRequest::token + * @return + */ +const std::string& +HanamiRequest::getToken() const +{ + return m_token; +} + +void +HanamiRequest::updateToken(const std::string &newToken) +{ + m_token = newToken; +} + +/** + * @brief static methode to get instance of the interface + * + * @return pointer to the static instance + */ +HanamiRequest* +HanamiRequest::getInstance() +{ + if(m_instance == nullptr) { + m_instance = new HanamiRequest(); + } + + return m_instance; +} + +/** + * @brief destructor + */ +HanamiRequest::~HanamiRequest() +{ +} + +/** + * @brief init request-object + * + * @param host target-host-address + * @param port port of the server + * @param user user-name for token-request + * @param password password for token-request + * @param cacert cacert for connection-validation + * + * @return false, if host or port ar missing in variables and venv, else true + */ +bool +HanamiRequest::init(const std::string &host, + const std::string &port, + const std::string &user, + const std::string &password, + const std::string &cacert) +{ + m_host = host; + m_port = port; + m_cacert = cacert; + m_userId = user; + m_password = password; + + // get host-address + if(m_host == "" + && getEnvVar(m_host, "HANAMI_ADDRESS") == false) + { + return false; + } + + // get target-port + if(m_port == "" + && getEnvVar(m_port, "HANAMI_PORT") == false) + { + return false; + } + + // ge ca-cert + if(m_cacert == "") { + getEnvVar(m_cacert, "HANAMI_CACERT"); + } + + // get token if there already one exist + getEnvVar(m_token, "HANAMI_TOKEN"); + + return true; +} + +/** + * @brief Request::sendGetRequest + * + * @param response reference for response-output + * @param path path to call + * @param vars variables as string for the request-path + * @param error reference for error-output + * + * @return false, if something went wrong while sending or token-request failed, else true + */ +bool +HanamiRequest::sendGetRequest(std::string &response, + const std::string &path, + const std::string &vars, + Kitsunemimi::ErrorContainer &error) +{ + return makeRequest(response, http::verb::get, path, vars, "", error); +} + +/** + * @brief Request::sendPostRequest + * + * @param response reference for response-output + * @param path path to call + * @param vars variables as string for the request-path + * @param body json-body as string + * @param error reference for error-output + * + * @return false, if something went wrong while sending or token-request failed, else true + */ +bool +HanamiRequest::sendPostRequest(std::string &response, + const std::string &path, + const std::string &vars, + const std::string &body, + Kitsunemimi::ErrorContainer &error) +{ + return makeRequest(response, http::verb::post, path, vars, body, error); +} + +/** + * @brief Request::sendPutRequest + * + * @param response reference for response-output + * @param path path to call + * @param vars variables as string for the request-path + * @param body json-body as string + * @param error reference for error-output + * + * @return false, if something went wrong while sending or token-request failed, else true + */ +bool +HanamiRequest::sendPutRequest(std::string &response, + const std::string &path, + const std::string &vars, + const std::string &body, + Kitsunemimi::ErrorContainer &error) +{ + return makeRequest(response, http::verb::put, path, vars, body, error); +} + +/** + * @brief Request::sendDeleteRequest + * + * @param response reference for response-output + * @param path path to call + * @param vars variables as string for the request-path + * @param error reference for error-output + * + * @return false, if something went wrong while sending or token-request failed, else true + */ +bool +HanamiRequest::sendDeleteRequest(std::string &response, + const std::string &path, + const std::string &vars, + Kitsunemimi::ErrorContainer &error) +{ + return makeRequest(response, http::verb::delete_, path, vars, "", error); +} + +/** + * @brief get content of an environment-variable + * + * @param content reference for output + * @param key name of the environment-variable + * + * @return false, if varibale is not set, else true + */ +bool +HanamiRequest::getEnvVar(std::string &content, + const std::string &key) const +{ + const char* val = getenv(key.c_str()); + if(val == NULL) { + return false; + } + + content = std::string(val); + return true; +} + +/** + * @brief Request::requestToken + * + * @param error reference for error-output + * + * @return false, if something failed, else true + */ +bool +HanamiRequest::requestToken(Kitsunemimi::ErrorContainer &error) +{ + // get user for access + if(m_userId == "" + && getEnvVar(m_userId, "HANAMI_USER_ID") == false) + { + error.addMeesage("Failed to request token, because no user-id was provided"); + LOG_ERROR(error); + return false; + } + + // get password for access + if(m_password == "" + && getEnvVar(m_password, "HANAMI_USER_PW") == false) + { + error.addMeesage("Failed to request token, because no password was provided"); + LOG_ERROR(error); + return false; + } + + // build request-path and body + const std::string path = "/control/misaki/v1/token"; + const std::string jsonBody = "{\"id\":\"" + + m_userId + + "\",\"password\":\"" + + m_password + + "\"}"; + + // make token-request + std::string response; + if(makeSingleRequest(response, http::verb::post, path, jsonBody, error) != 200) + { + error.addMeesage("Failed to request token"); + LOG_ERROR(error); + return false; + } + + // try to parse response + Kitsunemimi::JsonItem item; + if(item.parse(response, error) == false) + { + error.addMeesage("Failed to parse token-response"); + LOG_ERROR(error); + return false; + } + + // get token from parsed item + m_token = item["token"].getString(); + if(m_token == "") + { + error.addMeesage("Can not find token in token-response"); + LOG_ERROR(error); + return false; + } + + return true; +} + +/** + * @brief make a request against the backend + * + * @param response reference for response-output + * @param type request-type + * @param path path to call + * @param vars variables as string for the request-path + * @param jsonBody json-body as string + * @param error reference for error-output + * + * @return false, if something went wrong while sending or token-request failed, else true + */ +bool +HanamiRequest::makeRequest(std::string &response, + const http::verb type, + const std::string &path, + const std::string &vars, + const std::string &jsonBody, + Kitsunemimi::ErrorContainer &error) +{ + // get token if necessary + if(m_token == "") + { + if(requestToken(error) == false) { + return false; + } + } + + // build real request-path with the ntoken + std::string target = path; + if(vars != "") { + target.append("?" + vars); + } + + // send request + if(makeSingleRequest(response, type, target, jsonBody, error) != 200) { + return false; + } + + // handle expired token + if(response == "Token is expired") + { + // request new token + if(requestToken(error) == false) { + return false; + } + + // build new request-path with the new token + target = path; + if(m_token != "") { + target += "?token=" + m_token; + } + if(vars != "") { + target.append("&" + vars); + } + + // try request again + if(makeSingleRequest(response, type, target, jsonBody, error) != 200) { + return false; + } + } + + return true; +} + +/** + * @brief make a generic request against the backend + * + * @param response reference for response-output + * @param type type of the request + * @param target target-path as string + * @param jsonBody json-body as string + * @param error reference for error-output + * + * @return false, if something went wrong while sending, else true + */ +uint16_t +HanamiRequest::makeSingleRequest(std::string &response, + const http::verb type, + const std::string &target, + const std::string &jsonBody, + Kitsunemimi::ErrorContainer &error) +{ + u_int16_t statusCode = 0; + + try + { + int version = 11; + + // init ssl + ssl::context ctx(ssl::context::tlsv13_client); + //load_root_certificates(ctx); + //ctx.set_verify_mode(ssl::verify_peer); + + // These objects perform our I/O + net::io_context ioc; + tcp::resolver resolver(ioc); + beast::ssl_stream stream(ioc, ctx); + + // Set SNI Hostname (many hosts need this to handshake successfully) + if(! SSL_set_tlsext_host_name(stream.native_handle(), m_host.c_str())) + { + beast::error_code ec{ + static_cast(::ERR_get_error()), + net::error::get_ssl_category() + }; + throw beast::system_error{ec}; + } + + // init connection + const auto results = resolver.resolve(m_host, m_port); + beast::get_lowest_layer(stream).connect(results); + stream.handshake(ssl::stream_base::client); + + LOG_DEBUG("send http-request to '" + target + "'"); + + // create request + http::request req{type, target, version}; + req.set(http::field::host, m_host); + req.set(http::field::user_agent, BOOST_BEAST_VERSION_STRING); + + // add token + if(m_token != "") { + req.set("X-Auth-Token", m_token); + } + + // add body + if(jsonBody.size() > 0) + { + req.body() = jsonBody; + req.set(http::field::content_type, "application/json"); + req.content_length(jsonBody.size()); + req.prepare_payload(); + } + + // run request + http::write(stream, req); + + // receive response + beast::flat_buffer buffer; + http::response res; + http::read(stream, buffer, res); + response = res.body().c_str(); + statusCode = res.result_int(); + if(statusCode != 200) + { + if(statusCode == 500) { + response = "Internal error"; + } + error.addMeesage("ERROR " + std::to_string(statusCode) + ": " + response); + } + + // Gracefully close the stream + beast::error_code ec; + stream.shutdown(ec); + if(ec == net::error::eof) + { + // Rationale: + // http://stackoverflow.com/questions/25587403/ + // boost-asio-ssl-async-shutdown-always-finishes-with-an-error + ec = {}; + } + if(ec) + { + error.addMeesage("failed to close http-connection"); + return statusCode; + } + } + catch(std::exception const& e) + { + error.addMeesage("Error while making http-request: " + + std::string(e.what())); + return statusCode; + } + + return statusCode; +} + +} // namespace HanamiAI diff --git a/src/frontend/Hanami-AI-Dashboard/src/libHanamiAiSdk/cpp/src/common/http_client.h b/src/frontend/Hanami-AI-Dashboard/src/libHanamiAiSdk/cpp/src/common/http_client.h new file mode 100644 index 00000000..880378e8 --- /dev/null +++ b/src/frontend/Hanami-AI-Dashboard/src/libHanamiAiSdk/cpp/src/common/http_client.h @@ -0,0 +1,120 @@ +/** + * @file request.h + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2021 Tobias Anker + * + * 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. + */ + +#ifndef KITSUNEMIMI_HANAMISDK_HANAMI_REQUEST_H +#define KITSUNEMIMI_HANAMISDK_HANAMI_REQUEST_H + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace beast = boost::beast; // from +namespace http = beast::http; // from + +namespace net = boost::asio; // from +namespace ssl = net::ssl; // from +using tcp = net::ip::tcp; // from + +#include + +namespace HanamiAI +{ + +class HanamiRequest +{ +public: + static HanamiRequest* getInstance(); + ~HanamiRequest(); + + bool init(const std::string &host = "", + const std::string &port = "", + const std::string &user = "", + const std::string &password = "", + const std::string &cacert = ""); + + bool sendGetRequest(std::string &response, + const std::string &path, + const std::string &vars, + Kitsunemimi::ErrorContainer &error); + + bool sendPostRequest(std::string &response, + const std::string &path, + const std::string &vars, + const std::string &body, + Kitsunemimi::ErrorContainer &error); + + bool sendPutRequest(std::string &response, + const std::string &path, + const std::string &vars, + const std::string &body, + Kitsunemimi::ErrorContainer &error); + + bool sendDeleteRequest(std::string &response, + const std::string &path, + const std::string &vars, + Kitsunemimi::ErrorContainer &error); + + const std::string& getToken() const; + const std::string& getPort() const; + const std::string& getHost() const; + + void updateToken(const std::string &newToken); + +private: + HanamiRequest(); + static HanamiRequest* m_instance; + + std::string m_host = ""; + std::string m_port = ""; + std::string m_cacert = ""; + std::string m_token = ""; + std::string m_userId = ""; + std::string m_password = ""; + + bool requestToken(Kitsunemimi::ErrorContainer &error); + bool makeRequest(std::string &response, + const http::verb type, + const std::string &path, + const std::string &vars, + const std::string &jsonBody, + Kitsunemimi::ErrorContainer &error); + uint16_t makeSingleRequest(std::string &response, + const http::verb type, + const std::string &target, + const std::string &jsonBody, + Kitsunemimi::ErrorContainer &error); + bool getEnvVar(std::string &content, + const std::string &key) const; +}; + +} // namespace HanamiAI + +#endif // KITSUNEMIMI_HANAMISDK_HANAMI_REQUEST_H diff --git a/src/frontend/Hanami-AI-Dashboard/src/libHanamiAiSdk/cpp/src/common/websocket_client.cpp b/src/frontend/Hanami-AI-Dashboard/src/libHanamiAiSdk/cpp/src/common/websocket_client.cpp new file mode 100644 index 00000000..68c7949b --- /dev/null +++ b/src/frontend/Hanami-AI-Dashboard/src/libHanamiAiSdk/cpp/src/common/websocket_client.cpp @@ -0,0 +1,250 @@ +/** + * @file websocket_client.cpp + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2021 Tobias Anker + * + * 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 + +#include + +namespace HanamiAI +{ + +/** + * @brief constructor + */ +WebsocketClient::WebsocketClient() {} + +/** + * @brief destructor + */ +WebsocketClient::~WebsocketClient() +{ + m_websocket->close(websocket::close_code::normal); + // a 'delete' on the m_websocket-pointer breaks the program, + // because of bad programming in the websocket-class of the boost beast library + // TODO: fix the memory-leak +} + +/** + * @brief initialize new websocket-connection to a torii + * + * @param socketUuid reference for the + * @param token token to authenticate socket on the torii + * @param target name of the target on server-side behind the torii + * @param host address of the torii + * @param port port where the server is listen on target-side + * @param error reference for error-output + * + * @return true, if successful, else false + */ +bool +WebsocketClient::initClient(std::string &socketUuid, + const std::string &token, + const std::string &target, + const std::string &host, + const std::string &port, + Kitsunemimi::ErrorContainer &error) +{ + try + { + // init ssl + ssl::context ctx{ssl::context::tlsv13_client}; + /*if(loadCertificates(ctx) == false) + { + error.addMeesage("Failed to load certificates for creating Websocket-Client"); + LOG_ERROR(error); + return false; + }*/ + + net::io_context ioc; + tcp::resolver resolver{ioc}; + m_websocket = new websocket::stream>{ioc, ctx}; + + // Look up the domain name + const auto results = resolver.resolve(host, port); + auto ep = net::connect(get_lowest_layer(*m_websocket), results); + + // Set SNI Hostname (many hosts need this to handshake successfully) + if(! SSL_set_tlsext_host_name(m_websocket->next_layer().native_handle(), host.c_str())) + throw beast::system_error( + beast::error_code( + static_cast(::ERR_get_error()), + net::error::get_ssl_category()), + "Failed to set SNI Hostname"); + + const std::string address = host + ':' + std::to_string(ep.port()); + + m_websocket->next_layer().handshake(ssl::stream_base::client); + m_websocket->set_option(websocket::stream_base::decorator( + [](websocket::response_type& res) + { + res.set(http::field::server, + std::string(BOOST_BEAST_VERSION_STRING) + + " client-websocket-ssl"); + })); + + // Perform the websocket handshake + m_websocket->handshake(address, "/"); + + std::string initialMsg = ""; + initialMsg.append("{\"token\":\""); + initialMsg.append(token); + initialMsg.append("\",\"target\":\""); + initialMsg.append(target); + initialMsg.append("\"}"); + + // Send the message + m_websocket->binary(true); + m_websocket->write(net::buffer(initialMsg, initialMsg.size())); + + // Read a message into our buffer + beast::flat_buffer buffer; + m_websocket->read(buffer); + + const std::string responseMsg(static_cast(buffer.data().data()), + buffer.data().size()); + + // parse response + Kitsunemimi::JsonItem response; + if(response.parse(responseMsg, error) == false) + { + error.addMeesage("Failed to parse response-message from Websocket-init"); + LOG_ERROR(error); + } + + socketUuid = response.get("uuid").getString(); + return response.get("success").getInt(); + } + catch(std::exception const& e) + { + const std::string msg(e.what()); + error.addMeesage("Error-Message while initilializing Websocket-Client: '" + msg + "'"); + LOG_ERROR(error); + return false; + } + + return false; +} + +/** + * @brief send data over websocket-client + * + * @param data pointer to data to send + * @param dataSize number of bytes to send + * @param error reference for error-output + * + * @return true, if successful, else false + */ +bool +WebsocketClient::sendMessage(const void* data, + const uint64_t dataSize, + Kitsunemimi::ErrorContainer &error) +{ + try + { + // Send the message + m_websocket->binary(true); + m_websocket->write(net::buffer(data, dataSize)); + } + catch(const std::exception &e) + { + const std::string msg(e.what()); + error.addMeesage("Error-Message while send Websocket-Data: '" + msg + "'"); + LOG_ERROR(error); + return false; + } + + return true; +} + +/** + * @brief WebsocketClient::readMessage + * + * @param numberOfByes reference for output of number of read bytes + * @param error reference for error-output + * + * @return nullptr if failed, else pointer + */ +uint8_t* +WebsocketClient::readMessage(uint64_t &numberOfByes, + Kitsunemimi::ErrorContainer &error) +{ + try + { + // Read a message into our buffer + beast::flat_buffer buffer; + m_websocket->read(buffer); + + numberOfByes = buffer.data().size(); + if(numberOfByes == 0) { + return nullptr; + } + uint8_t* data = new uint8_t[numberOfByes]; + memcpy(data, buffer.data().data(), numberOfByes); + + return data; + } + catch(const std::exception &e) + { + numberOfByes = 0; + const std::string msg(e.what()); + error.addMeesage("Error-Message while read Websocket-Data: '" + msg + "'"); + LOG_ERROR(error); + return nullptr; + } + + numberOfByes = 0; + return nullptr; +} + +/** + * @brief load ssl-certificates for ssl-encryption of websocket (not used at the moment) + * + * @param ctx reference to ssl-context + * + * @return true, if successful, else false + */ +bool +WebsocketClient::loadCertificates(boost::asio::ssl::context& ctx) +{ + // TODO: use this functions to load specific certificates from file + std::string errorMessage = ""; + std::string cert = "..."; + std::string key = "..."; + + const std::string dh = "..."; + + + ctx.set_options(boost::asio::ssl::context::default_workarounds | + boost::asio::ssl::context::no_sslv2 | + boost::asio::ssl::context::single_dh_use); + + ctx.use_certificate_chain(boost::asio::buffer(cert.data(), cert.size())); + + ctx.use_private_key(boost::asio::buffer(key.data(), key.size()), + boost::asio::ssl::context::file_format::pem); + + ctx.use_tmp_dh(boost::asio::buffer(dh.data(), dh.size())); + + return true; +} + +} // namespace HanamiAI diff --git a/src/frontend/Hanami-AI-Dashboard/src/libHanamiAiSdk/cpp/src/data_set.cpp b/src/frontend/Hanami-AI-Dashboard/src/libHanamiAiSdk/cpp/src/data_set.cpp new file mode 100644 index 00000000..11b2fd4e --- /dev/null +++ b/src/frontend/Hanami-AI-Dashboard/src/libHanamiAiSdk/cpp/src/data_set.cpp @@ -0,0 +1,615 @@ +/** + * @file data_set.cpp + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2021 Tobias Anker + * + * 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 +#include +#include + +#include +#include +#include +#include + +#include <../../libKitsunemimiHanamiMessages/protobuffers/shiori_messages.proto3.pb.h> + +namespace HanamiAI +{ + +/** + * @brief get size of a local file + * + * @param filePath path the file to check + * + * @return size of file if successful, or -1 if failed + */ +long +getFileSize(const std::string &filePath) +{ + std::ifstream in(filePath, std::ifstream::ate | std::ifstream::binary); + return in.tellg(); +} + +/** + * @brief initialize a new csv-dataset in shiori + * + * @param result reference for response-message + * @param dataSetName name for the new data-set + * @param inputDataSize size of the file with the input-data + * @param error reference for error-output + * + * @return true, if successful, else false + */ +bool +createCsvDataSet(std::string &result, + const std::string &dataSetName, + const uint64_t inputDataSize, + Kitsunemimi::ErrorContainer &error) +{ + // create request + HanamiRequest* request = HanamiRequest::getInstance(); + const std::string path = "/control/shiori/v1/csv/data_set"; + const std::string vars = ""; + const std::string jsonBody = "{\"name\":\"" + dataSetName + "\"" + ",\"input_data_size\":" + std::to_string(inputDataSize) + "}"; + + // send request + if(request->sendPostRequest(result, path, vars, jsonBody, error) == false) { + return false; + } + + return true; +} + +/** + * @brief finalize data-set + * + * @param result reference for response-message + * @param uuid uuid to identify the data-set + * @param inputUuid uuid to identify the temporary file with the input-data on server-side + * @param error reference for error-output + * + * @return true, if successful, else false + */ +bool +finalizeCsvDataSet(std::string &result, + const std::string &uuid, + const std::string &inputUuid, + Kitsunemimi::ErrorContainer &error) +{ + // create request + HanamiRequest* request = HanamiRequest::getInstance(); + const std::string path = "/control/shiori/v1/csv/data_set"; + const std::string vars = ""; + const std::string jsonBody = "{\"uuid\":\"" + uuid + "\"" + ",\"uuid_input_file\":\"" + inputUuid + "\"}"; + + // send request + if(request->sendPutRequest(result, path, vars, jsonBody, error) == false) { + return false; + } + + return true; +} + +/** + * @brief initialize a new mnist-dataset in shiori + * + * @param result reference for response-message + * @param dataSetName name for the new data-set + * @param inputDataSize size of the file with the input-data + * @param labelDataSize size of the file with the label-data + * @param error reference for error-output + * + * @return true, if successful, else false + */ +bool +createMnistDataSet(std::string &result, + const std::string &dataSetName, + const uint64_t inputDataSize, + const uint64_t labelDataSize, + Kitsunemimi::ErrorContainer &error) +{ + // create request + HanamiRequest* request = HanamiRequest::getInstance(); + const std::string path = "/control/shiori/v1/mnist/data_set"; + const std::string vars = ""; + const std::string jsonBody = "{\"name\":\"" + dataSetName + "\"" + ",\"input_data_size\":" + std::to_string(inputDataSize) + + ",\"label_data_size\":" + std::to_string(labelDataSize) + "}"; + + // send request + if(request->sendPostRequest(result, path, vars, jsonBody, error) == false) { + return false; + } + + return true; +} + +/** + * @brief finalize data-set + * + * @param result reference for response-message + * @param uuid uuid to identify the data-set + * @param inputUuid uuid to identify the temporary file with the input-data on server-side + * @param labelUuid uuid to identify the temporary file with the label-data on server-side + * @param error reference for error-output + * + * @return true, if successful, else false + */ +bool +finalizeMnistDataSet(std::string &result, + const std::string &uuid, + const std::string &inputUuid, + const std::string &labelUuid, + Kitsunemimi::ErrorContainer &error) +{ + // create request + HanamiRequest* request = HanamiRequest::getInstance(); + const std::string path = "/control/shiori/v1/mnist/data_set"; + const std::string vars = ""; + const std::string jsonBody = "{\"uuid\":\"" + uuid + "\"" + ",\"uuid_input_file\":\"" + inputUuid + "\"" + ",\"uuid_label_file\":\"" + labelUuid + "\"}"; + + // send request + if(request->sendPutRequest(result, path, vars, jsonBody, error) == false) { + return false; + } + + return true; +} + +/** + * @brief send data to shiori + * + * @param session session over which the data should be send + * @param datasetUuid uuid of the dataset where the file belongs to + * @param fileUuid uuid of the file for identification in shiori + * @param filePath path to file, which should be send + * @param error reference for error-output + * + * @return true, if successful, else false + */ +bool +sendFile(WebsocketClient* client, + const std::string &datasetUuid, + const std::string &fileUuid, + const std::string &filePath, + Kitsunemimi::ErrorContainer &error) +{ + // get data, which should be send + const uint64_t dataSize = getFileSize(filePath); + Kitsunemimi::BinaryFile sourceFile(filePath); + + bool success = true; + uint64_t pos = 0; + + // prepare buffer + uint64_t segmentSize = 96 * 1024; + + uint8_t readBuffer[96*1024]; + uint8_t sendBuffer[128*1024]; + + do + { + // check the size for the last segment + segmentSize = 96 * 1024; + if(dataSize - pos < segmentSize) { + segmentSize = dataSize - pos; + } + + FileUpload_Message message; + message.set_fileuuid(fileUuid); + message.set_datasetuuid(datasetUuid); + message.set_type(UploadDataType::DATASET_TYPE); + message.set_islast(false); + if(pos + segmentSize >= dataSize) { + message.set_islast(true); + } + + // read segment of the local file + if(sourceFile.readDataFromFile(&readBuffer[0], pos, segmentSize, error) == false) + { + success = false; + error.addMeesage("Failed to read file '" + filePath + "'"); + break; + } + + message.set_position(pos); + message.set_data(readBuffer, segmentSize); + + const uint64_t msgSize = message.ByteSizeLong(); + if(message.SerializeToArray(sendBuffer, msgSize) == false) + { + error.addMeesage("Failed to serialize learn-message"); + return false; + } + + // send segment + if(client->sendMessage(sendBuffer, msgSize, error) == false) + { + LOG_ERROR(error); + success = false; + break; + } + + pos += segmentSize; + } + while(pos < dataSize); + + return success; +} + +/** + * @brief wait until the upload of all tempfiles are complete + * + * @param uuid uuid of the dataset + * @param error reference for error-output + * + * @return true, if successful, else false + */ +bool +waitUntilFullyUploaded(const std::string &uuid, + Kitsunemimi::ErrorContainer &error) +{ + // TODO: add timeout-timer + bool completeUploaded = false; + while(completeUploaded == false) + { + sleep(1); + + std::string progressStr = ""; + if(getDatasetProgress(progressStr, uuid, error) == false) + { + LOG_ERROR(error); + return false; + } + + Kitsunemimi::JsonItem progress; + if(progress.parse(progressStr, error) == false) + { + LOG_ERROR(error); + return false; + } + + completeUploaded = progress.get("complete").getBool(); + } + + return true; +} + +/** + * @brief upload new csv-data-set to shiori + * + * @param result reference for response-message + * @param dataSetName name for the new data-set + * @param inputFilePath path to file with the inputs + * @param error reference for error-output + * + * @return true, if successful, else false + */ +bool +uploadCsvData(std::string &result, + const std::string &dataSetName, + const std::string &inputFilePath, + Kitsunemimi::ErrorContainer &error) +{ + // init new mnist-data-set + if(createCsvDataSet(result, + dataSetName, + getFileSize(inputFilePath), + error) == false) + { + return false; + } + + // parse output to get the uuid + Kitsunemimi::JsonItem jsonItem; + if(jsonItem.parse(result, error) == false) + { + LOG_ERROR(error); + return false; + } + + // get ids from inital reponse to identify the file-transfer + const std::string uuid = jsonItem.get("uuid").getString(); + const std::string inputUuid = jsonItem.get("uuid_input_file").getString(); + + // init websocket to shiori + WebsocketClient wsClient; + std::string websocketUuid = ""; + const bool ret = wsClient.initClient(websocketUuid, + HanamiRequest::getInstance()->getToken(), + "shiori", + HanamiRequest::getInstance()->getHost(), + HanamiRequest::getInstance()->getPort(), + error); + if(ret == false) + { + error.addMeesage("Failed to init websocket to shiori"); + LOG_ERROR(error); + return false; + } + + // send file + if(sendFile(&wsClient, uuid, inputUuid, inputFilePath, error) == false) + { + LOG_ERROR(error); + return false; + } + + // wait until all data-transfers to shiori are completed + if(waitUntilFullyUploaded(uuid, error) == false) + { + LOG_ERROR(error); + return false; + } + + if(finalizeCsvDataSet(result, uuid, inputUuid, error) == false) + { + LOG_ERROR(error); + return false; + } + + return true; +} + +/** + * @brief upload new mnist-data-set to shiori + * + * @param result reference for response-message + * @param dataSetName name for the new data-set + * @param inputFilePath path to file with the inputs + * @param labelFilePath path to file with the labels + * @param error reference for error-output + * + * @return true, if successful, else false + */ +bool +uploadMnistData(std::string &result, + const std::string &dataSetName, + const std::string &inputFilePath, + const std::string &labelFilePath, + Kitsunemimi::ErrorContainer &error) +{ + // init new mnist-data-set + if(createMnistDataSet(result, + dataSetName, + getFileSize(inputFilePath), + getFileSize(labelFilePath), + error) == false) + { + return false; + } + + // parse output to get the uuid + Kitsunemimi::JsonItem jsonItem; + if(jsonItem.parse(result, error) == false) + { + LOG_ERROR(error); + return false; + } + + // get ids from inital reponse to identify the file-transfer + const std::string uuid = jsonItem.get("uuid").getString(); + const std::string inputUuid = jsonItem.get("uuid_input_file").getString(); + const std::string labelUuid = jsonItem.get("uuid_label_file").getString(); + + // init websocket to shiori + WebsocketClient wsClient; + std::string websocketUuid = ""; + const bool ret = wsClient.initClient(websocketUuid, + HanamiRequest::getInstance()->getToken(), + "shiori", + HanamiRequest::getInstance()->getHost(), + HanamiRequest::getInstance()->getPort(), + error); + if(ret == false) + { + error.addMeesage("Failed to init websocket to shiori"); + LOG_ERROR(error); + return false; + } + + // send file with inputs + if(sendFile(&wsClient, uuid, inputUuid, inputFilePath, error) == false) + { + error.addMeesage("Failed to send file with input-values"); + LOG_ERROR(error); + return false; + } + + // send file with labels + if(sendFile(&wsClient, uuid, labelUuid, labelFilePath, error) == false) + { + error.addMeesage("Failed to send file with labes"); + LOG_ERROR(error); + return false; + } + + // wait until all data-transfers to shiori are completed + if(waitUntilFullyUploaded(uuid, error) == false) + { + error.addMeesage("Failed to wait for fully uploaded files"); + LOG_ERROR(error); + return false; + } + + if(finalizeMnistDataSet(result, uuid, inputUuid, labelUuid, error) == false) + { + error.addMeesage("Failed to finalize MNIST-dataset"); + LOG_ERROR(error); + return false; + } + + return true; +} + +/** + * @brief check values against a data-set to get correctness + * + * @param result reference for response-message + * @param dataUuid uuid of the data-set to compare to + * @param resultUuid uuid of the result-set to compare + * @param error reference for error-output + * + * @return true, if successful, else false + */ +bool +checkDataset(std::string &result, + const std::string &dataUuid, + const std::string &resultUuid, + Kitsunemimi::ErrorContainer &error) +{ + // create request + HanamiRequest* request = HanamiRequest::getInstance(); + const std::string path = "/control/shiori/v1/data_set/check"; + const std::string vars = ""; + const std::string jsonBody = "{\"data_set_uuid\":\"" + dataUuid + "\"" + ",\"result_uuid\":\"" + resultUuid + "\"}"; + + // send request + if(request->sendPostRequest(result, path, vars, jsonBody, error) == false) { + return false; + } + + return true; +} + +/** + * @brief get metadata of a specific data-set + * + * @param result reference for response-message + * @param dataUuid uuid of the requested data-set + * @param error reference for error-output + * + * @return true, if successful, else false + */ +bool +getDataset(std::string &result, + const std::string &dataUuid, + Kitsunemimi::ErrorContainer &error) +{ + // create request + HanamiRequest* request = HanamiRequest::getInstance(); + const std::string path = "/control/shiori/v1/data_set"; + const std::string vars = "uuid=" + dataUuid; + + // send request + if(request->sendGetRequest(result, path, vars, error) == false) + { + error.addMeesage("Failed to get dataset with UUID '" + dataUuid + "'"); + LOG_ERROR(error); + return false; + } + + return true; +} + +/** + * @brief list all data-sets of the user, which are available on shiori + * + * @param result reference for response-message + * @param error reference for error-output + * + * @return true, if successful, else false + */ +bool +listDatasets(std::string &result, + Kitsunemimi::ErrorContainer &error) +{ + // create request + HanamiRequest* request = HanamiRequest::getInstance(); + const std::string path = "/control/shiori/v1/data_set/all"; + + // send request + if(request->sendGetRequest(result, path, "", error) == false) + { + error.addMeesage("Failed to list datasets"); + LOG_ERROR(error); + return false; + } + + return true; +} + +/** + * @brief delete a data-set from shiori + * + * @param result reference for response-message + * @param dataUuid uuid of the data-set to delete + * @param error reference for error-output + * + * @return true, if successful, else false + */ +bool +deleteDataset(std::string &result, + const std::string &dataUuid, + Kitsunemimi::ErrorContainer &error) +{ + // create request + HanamiRequest* request = HanamiRequest::getInstance(); + const std::string path = "/control/shiori/v1/data_set"; + const std::string vars = "uuid=" + dataUuid; + + // send request + if(request->sendDeleteRequest(result, path, vars, error) == false) + { + error.addMeesage("Failed to delete dataset with UUID '" + dataUuid + "'"); + LOG_ERROR(error); + return false; + } + + return true; +} + +/** + * @brief check progress of file-upload + * + * @param result reference for response-message + * @param dataUuid uuid of the data-set to get + * @param error reference for error-output + * + * @return true, if successful, else false + */ +bool +getDatasetProgress(std::string &result, + const std::string &dataUuid, + Kitsunemimi::ErrorContainer &error) +{ + // create request + HanamiRequest* request = HanamiRequest::getInstance(); + const std::string path = "/control/shiori/v1/data_set/progress"; + const std::string vars = "uuid=" + dataUuid; + + // send request + if(request->sendGetRequest(result, path, vars, error) == false) + { + error.addMeesage("Failed to check upload-state of dataset with UUID '" + dataUuid + "'"); + LOG_ERROR(error); + return false; + } + + return true; +} + +} // namespace HanamiAI diff --git a/src/frontend/Hanami-AI-Dashboard/src/libHanamiAiSdk/cpp/src/init.cpp b/src/frontend/Hanami-AI-Dashboard/src/libHanamiAiSdk/cpp/src/init.cpp new file mode 100644 index 00000000..b8afa06a --- /dev/null +++ b/src/frontend/Hanami-AI-Dashboard/src/libHanamiAiSdk/cpp/src/init.cpp @@ -0,0 +1,58 @@ +/** + * @file init.cpp + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2021 Tobias Anker + * + * 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 +#include + +namespace HanamiAI +{ + +/** + * @brief initialize new client-session by requesting a access-token from the server + * + * @param host target-address to connect + * @param port target-port to connect + * @param user name of the user + * @param password password of the user + * @param error reference for error-output + * + * @return true, if successful, else false + */ +bool +initClient(const std::string &host, + const std::string &port, + const std::string &user, + const std::string &password, + Kitsunemimi::ErrorContainer &error) +{ + HanamiRequest* request = HanamiRequest::getInstance(); + if(request->init(host, port, user, password) == false) + { + error.addMeesage("Failed to initialize hanami-client"); + LOG_ERROR(error); + return false; + } + + return true; +} + +} // namespace HanamiAI diff --git a/src/frontend/Hanami-AI-Dashboard/src/libHanamiAiSdk/cpp/src/io.cpp b/src/frontend/Hanami-AI-Dashboard/src/libHanamiAiSdk/cpp/src/io.cpp new file mode 100644 index 00000000..ab006246 --- /dev/null +++ b/src/frontend/Hanami-AI-Dashboard/src/libHanamiAiSdk/cpp/src/io.cpp @@ -0,0 +1,281 @@ +/** + * @file io.cpp + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2021 Tobias Anker + * + * 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 + +#include + +#include <../../libKitsunemimiHanamiMessages/protobuffers/kyouko_messages.proto3.pb.h> + +namespace HanamiAI +{ + +/** + * @brief learn single value + * + * @param wsClient pointer to websocket-client for data-transfer + * @param inputValues vector with all input-values + * @param shouldValues vector with all should-values + * @param error reference for error-output + * + * @return true, if successful, else false + */ +bool +learn(WebsocketClient* wsClient, + std::vector &inputValues, + std::vector &shouldValues, + Kitsunemimi::ErrorContainer &error) +{ + return learn(wsClient, + &inputValues[0], + inputValues.size(), + &shouldValues[0], + shouldValues.size(), + error); +} + +/** + * @brief request single value + * + * @param wsClient pointer to websocket-client for data-transfer + * @param inputValues vector with all input-values + * @param numberOfOutputValues reference for returning number of output-values + * @param error reference for error-output + * + * @return true, if successful, else false + */ +float* +request(WebsocketClient* wsClient, + std::vector &inputValues, + uint64_t &numberOfOutputValues, + Kitsunemimi::ErrorContainer &error) +{ + return request(wsClient, + &inputValues[0], + inputValues.size(), + numberOfOutputValues, + error); +} + +/** + * @brief learn single value + * + * @param wsClient pointer to websocket-client for data-transfer + * @param inputValues float-pointer to array with input-values for input-segment + * @param numberOfInputValues number of input-values for input-segment + * @param shouldValues float-pointer to array with should-values for output-segment + * @param numberOfShouldValues number of should-values for output-segment + * @param error reference for error-output + * + * @return true, if successful, else false + */ +bool +learn(WebsocketClient* wsClient, + float* inputValues, + const uint64_t numberOfInputValues, + float* shouldValues, + const uint64_t numberOfShouldValues, + Kitsunemimi::ErrorContainer &error) +{ + uint8_t buffer[96*1024]; + + // build input-message + ClusterIO_Message inputMsg; + inputMsg.set_segmentname("input"); + inputMsg.set_islast(false); + inputMsg.set_processtype(ClusterProcessType::LEARN_TYPE); + inputMsg.set_datatype(ClusterDataType::INPUT_TYPE); + inputMsg.set_numberofvalues(numberOfInputValues); + + for(uint64_t i = 0; i < numberOfInputValues; i++) { + inputMsg.add_values(inputValues[i]); + } + + // add input-values to message + const uint64_t inputMsgSize = inputMsg.ByteSizeLong(); + if(inputMsg.SerializeToArray(buffer, inputMsgSize) == false) + { + Kitsunemimi::ErrorContainer error; + error.addMeesage("Failed to serialize learn-message"); + return false; + } + + // send input + if(wsClient->sendMessage(buffer, inputMsgSize, error) == false) + { + error.addMeesage("Failed to send input-values"); + LOG_ERROR(error); + return false; + } + + // receive bogus-response, which is not further processed and exist only because of issue: + // https://github.com/kitsudaiki/KyoukoMind/issues/27 + uint64_t numberOfBytes = 0; + uint8_t* recvData = wsClient->readMessage(numberOfBytes, error); + if(recvData == nullptr + || numberOfBytes == 0) + { + error.addMeesage("Got no valid response"); + LOG_ERROR(error); + return false; + } + delete[] recvData; + + // build should-message + ClusterIO_Message shouldMsg; + shouldMsg.set_segmentname("output"); + shouldMsg.set_islast(true); + shouldMsg.set_processtype(ClusterProcessType::LEARN_TYPE); + shouldMsg.set_datatype(ClusterDataType::SHOULD_TYPE); + shouldMsg.set_numberofvalues(numberOfShouldValues); + + for(uint64_t i = 0; i < numberOfShouldValues; i++) { + shouldMsg.add_values(shouldValues[i]); + } + + // add should-values to message + const uint64_t shouldMsgSize = shouldMsg.ByteSizeLong(); + if(shouldMsg.SerializeToArray(buffer, shouldMsgSize) == false) + { + Kitsunemimi::ErrorContainer error; + error.addMeesage("Failed to serialize learn-message"); + return false; + } + + // send should + if(wsClient->sendMessage(buffer, shouldMsgSize, error) == false) + { + error.addMeesage("Failed to send should-values"); + LOG_ERROR(error); + return false; + } + + // receive response + numberOfBytes = 0; + recvData = wsClient->readMessage(numberOfBytes, error); + if(recvData == nullptr + || numberOfBytes == 0) + { + error.addMeesage("Got no valid response"); + LOG_ERROR(error); + return false; + } + + // check end-message + bool success = true; + ClusterIO_Message response; + if(response.ParseFromArray(recvData, numberOfBytes) == false) + { + success = false; + error.addMeesage("Got no valid learn-end-message"); + LOG_ERROR(error); + } + + delete[] recvData; + + return success; +} + +/** + * @brief request single value + * + * @param wsClient pointer to websocket-client for data-transfer + * @param inputValues float-pointer to array with input-values for input-segment + * @param numberOfInputValues number of input-values for input-segment + * @param numberOfOutputValues reference for returning number of output-values + * @param error reference for error-output + * + * @return true, if successful, else false + */ +float* +request(WebsocketClient* wsClient, + float* inputData, + const uint64_t numberOfInputValues, + uint64_t &numberOfOutputValues, + Kitsunemimi::ErrorContainer &error) +{ + uint8_t buffer[96*1024]; + + // build message + ClusterIO_Message inputMsg; + inputMsg.set_segmentname("input"); + inputMsg.set_islast(true); + inputMsg.set_processtype(ClusterProcessType::REQUEST_TYPE); + inputMsg.set_datatype(ClusterDataType::INPUT_TYPE); + inputMsg.set_numberofvalues(numberOfInputValues); + + for(uint64_t i = 0; i < numberOfInputValues; i++) { + inputMsg.add_values(inputData[i]); + } + + // add input-values to message + const uint64_t inputMsgSize = inputMsg.ByteSizeLong(); + if(inputMsg.SerializeToArray(buffer, inputMsgSize) == false) + { + Kitsunemimi::ErrorContainer error; + error.addMeesage("Failed to serialize request-message"); + LOG_ERROR(error); + return nullptr; + } + + // send message + if(wsClient->sendMessage(buffer, inputMsgSize, error) == false) + { + error.addMeesage("Failed to send input-values"); + LOG_ERROR(error); + return nullptr; + } + + // receive response + uint64_t numberOfBytes = 0; + uint8_t* recvData = wsClient->readMessage(numberOfBytes, error); + if(recvData == nullptr + || numberOfBytes == 0) + { + error.addMeesage("Got no valid request response"); + LOG_ERROR(error); + return nullptr; + } + + // read message from response + ClusterIO_Message response; + if(response.ParseFromArray(recvData, numberOfBytes) == false) + { + delete[] recvData; + error.addMeesage("Got no valid request response"); + LOG_ERROR(error); + return nullptr; + } + + // convert output + numberOfOutputValues = response.values_size(); + float* result = new float[numberOfOutputValues]; + for(uint64_t i = 0; i < numberOfOutputValues; i++) { + result[i] = response.values(i); + } + + delete[] recvData; + + return result; +} + +} // namespace HanamiAI diff --git a/src/frontend/Hanami-AI-Dashboard/src/libHanamiAiSdk/cpp/src/project.cpp b/src/frontend/Hanami-AI-Dashboard/src/libHanamiAiSdk/cpp/src/project.cpp new file mode 100644 index 00000000..67d73f6d --- /dev/null +++ b/src/frontend/Hanami-AI-Dashboard/src/libHanamiAiSdk/cpp/src/project.cpp @@ -0,0 +1,152 @@ +/** + * @file project.cpp + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2021 Tobias Anker + * + * 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 +#include + +namespace HanamiAI +{ + +/** + * @brief create a new user in misaki + * + * @param result reference for response-message + * @param projectId id of the new project + * @param projectName name of the new project + * @param error reference for error-output + * + * @return true, if successful, else false + */ +bool +createProject(std::string &result, + const std::string &projectId, + const std::string &projectName, + Kitsunemimi::ErrorContainer &error) +{ + // create request + HanamiRequest* request = HanamiRequest::getInstance(); + const std::string path = "/control/misaki/v1/project"; + const std::string vars = ""; + const std::string jsonBody = "{\"id\":\"" + + projectId + + "\",\"name\":\"" + + projectName + + "\"}"; + + // send request + if(request->sendPostRequest(result, path, vars, jsonBody, error) == false) + { + error.addMeesage("Failed to create project with id '" + projectId + "'"); + LOG_ERROR(error); + return false; + } + + return true; +} + +/** + * @brief get information of a user from misaki + * + * @param result reference for response-message + * @param projectId id of the requested project + * @param error reference for error-output + * + * @return true, if successful, else false + */ +bool +getProject(std::string &result, + const std::string &projectId, + Kitsunemimi::ErrorContainer &error) +{ + // create request + HanamiRequest* request = HanamiRequest::getInstance(); + const std::string path = "/control/misaki/v1/project"; + const std::string vars = "id=" + projectId; + + if(request->sendGetRequest(result, path, vars, error) == false) + { + error.addMeesage("Failed to get project with name '" + projectId + "'"); + LOG_ERROR(error); + return false; + } + + return true; +} + +/** + * @brief list all visible users on misaki + * + * @param result reference for response-message + * @param error reference for error-output + * + * @return true, if successful, else false + */ +bool +listProject(std::string &result, + Kitsunemimi::ErrorContainer &error) +{ + // create request + HanamiRequest* request = HanamiRequest::getInstance(); + const std::string path = "/control/misaki/v1/project/all"; + + // send request + if(request->sendGetRequest(result, path, "", error) == false) + { + error.addMeesage("Failed to list projects"); + LOG_ERROR(error); + return false; + } + + return true; +} + +/** + * @brief delete a project from misaki + * + * @param result reference for response-message + * @param projectId id of the project, which should be deleted + * @param error reference for error-output + * + * @return true, if successful, else false + */ +bool +deleteProject(std::string &result, + const std::string &projectId, + Kitsunemimi::ErrorContainer &error) +{ + // create request + HanamiRequest* request = HanamiRequest::getInstance(); + const std::string path = "/control/misaki/v1/project"; + const std::string vars = "id=" + projectId; + + // send request + if(request->sendDeleteRequest(result, path, vars, error) == false) + { + error.addMeesage("Failed to delete project with id '" + projectId + "'"); + LOG_ERROR(error); + return false; + } + + return true; +} + +} // namespace HanamiAI diff --git a/src/frontend/Hanami-AI-Dashboard/src/libHanamiAiSdk/cpp/src/request_result.cpp b/src/frontend/Hanami-AI-Dashboard/src/libHanamiAiSdk/cpp/src/request_result.cpp new file mode 100644 index 00000000..0945ae5a --- /dev/null +++ b/src/frontend/Hanami-AI-Dashboard/src/libHanamiAiSdk/cpp/src/request_result.cpp @@ -0,0 +1,115 @@ +/** + * @file request_result.cpp + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2021 Tobias Anker + * + * 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 +#include + +namespace HanamiAI +{ + +/** + * @brief get results of a request from shiori + * + * @param result reference for response-message + * @param requestResultUuid uuid of the requested result + * @param error reference for error-output + * + * @return true, if successful, else false + */ +bool +getRequestResult(std::string &result, + const std::string &requestResultUuid, + Kitsunemimi::ErrorContainer &error) +{ + // create request + HanamiRequest* request = HanamiRequest::getInstance(); + const std::string path = "/control/shiori/v1/request_result"; + const std::string vars = "uuid=" + requestResultUuid; + + if(request->sendGetRequest(result, path, vars, error) == false) + { + error.addMeesage("Failed to get request-result with uuid '" + requestResultUuid + "'"); + LOG_ERROR(error); + return false; + } + + return true; +} + +/** + * @brief list all visible request-results + * + * @param result reference for response-message + * @param error reference for error-output + * + * @return true, if successful, else false + */ +bool +listRequestResult(std::string &result, + Kitsunemimi::ErrorContainer &error) +{ + // create request + HanamiRequest* request = HanamiRequest::getInstance(); + const std::string path = "/control/shiori/v1/request_result/all"; + + // send request + if(request->sendGetRequest(result, path, "", error) == false) + { + error.addMeesage("Failed to list request-results"); + LOG_ERROR(error); + return false; + } + + return true; +} + +/** + * @brief delete a user from misaki + * + * @param result reference for response-message + * @param requestResultUuid uuid of the request-result, which should be deleted + * @param error reference for error-output + * + * @return true, if successful, else false + */ +bool +deleteRequestResult(std::string &result, + const std::string &requestResultUuid, + Kitsunemimi::ErrorContainer &error) +{ + // create request + HanamiRequest* request = HanamiRequest::getInstance(); + const std::string path = "/control/shiori/v1/request_result"; + const std::string vars = "uuid=" + requestResultUuid; + + // send request + if(request->sendDeleteRequest(result, path, vars, error) == false) + { + error.addMeesage("Failed to delete request-result with uuid '" + requestResultUuid + "'"); + LOG_ERROR(error); + return false; + } + + return true; +} + +} // namespace HanamiAI diff --git a/src/frontend/Hanami-AI-Dashboard/src/libHanamiAiSdk/cpp/src/snapshot.cpp b/src/frontend/Hanami-AI-Dashboard/src/libHanamiAiSdk/cpp/src/snapshot.cpp new file mode 100644 index 00000000..5732414d --- /dev/null +++ b/src/frontend/Hanami-AI-Dashboard/src/libHanamiAiSdk/cpp/src/snapshot.cpp @@ -0,0 +1,116 @@ +/** + * @file snapshot.cpp + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2021 Tobias Anker + * + * 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 +#include + +namespace HanamiAI +{ + +/** + * @brief get information of a snapshot from shiori + * + * @param result reference for response-message + * @param snapshotUuid uuid of the snapshot to get + * @param error reference for error-output + * + * @return true, if successful, else false + */ +bool +getSnapshot(std::string &result, + const std::string &snapshotUuid, + Kitsunemimi::ErrorContainer &error) +{ + // create request + HanamiRequest* request = HanamiRequest::getInstance(); + const std::string path = "/control/shiori/v1/cluster_snapshot"; + const std::string vars = "uuid=" + snapshotUuid; + + // send request + if(request->sendGetRequest(result, path, vars, error) == false) + { + error.addMeesage("Failed to get snapshot with UUID '" + snapshotUuid + "'"); + LOG_ERROR(error); + return false; + } + + return true; +} + +/** + * @brief list all visible snapshot on shiori + * + * @param result reference for response-message + * @param error reference for error-output + * + * @return true, if successful, else false + */ +bool +listSnapshot(std::string &result, + Kitsunemimi::ErrorContainer &error) +{ + // create request + HanamiRequest* request = HanamiRequest::getInstance(); + const std::string path = "/control/shiori/v1/cluster_snapshot/all"; + + // send request + if(request->sendGetRequest(result, path, "", error) == false) + { + error.addMeesage("Failed to list snapshots"); + LOG_ERROR(error); + return false; + } + + return true; +} + +/** + * @brief delete a snapshot + * + * @param result reference for response-message + * @param snapshotUuid uuid of the snapshot to delete + * @param error reference for error-output + * + * @return true, if successful, else false + */ +bool +deleteSnapshot(std::string &result, + const std::string &snapshotUuid, + Kitsunemimi::ErrorContainer &error) +{ + // create request + HanamiRequest* request = HanamiRequest::getInstance(); + const std::string path = "/control/shiori/v1/cluster_snapshot"; + const std::string vars = "uuid=" + snapshotUuid; + + // send request + if(request->sendDeleteRequest(result, path, vars, error) == false) + { + error.addMeesage("Failed to delete snapshot with UUID '" + snapshotUuid + "'"); + LOG_ERROR(error); + return false; + } + + return true; +} + +} // namespace HanamiAI diff --git a/src/frontend/Hanami-AI-Dashboard/src/libHanamiAiSdk/cpp/src/src.pro b/src/frontend/Hanami-AI-Dashboard/src/libHanamiAiSdk/cpp/src/src.pro new file mode 100644 index 00000000..d605f6a9 --- /dev/null +++ b/src/frontend/Hanami-AI-Dashboard/src/libHanamiAiSdk/cpp/src/src.pro @@ -0,0 +1,98 @@ +QT -= qt core gui + +TARGET = HanamiAiSdk +CONFIG += c++17 +TEMPLATE = lib +VERSION = 0.3.1 + +LIBS += -L../../libKitsunemimiCommon/src -lKitsunemimiCommon +LIBS += -L../../libKitsunemimiCommon/src/debug -lKitsunemimiCommon +LIBS += -L../../libKitsunemimiCommon/src/release -lKitsunemimiCommon +INCLUDEPATH += ../../../libKitsunemimiCommon/include + +LIBS += -L../../libKitsunemimiJson/src -lKitsunemimiJson +LIBS += -L../../libKitsunemimiJson/src/debug -lKitsunemimiJson +LIBS += -L../../libKitsunemimiJson/src/release -lKitsunemimiJson +INCLUDEPATH += ../../../libKitsunemimiJson/include + +LIBS += -L../../libKitsunemimiCrypto/src -lKitsunemimiCrypto +LIBS += -L../../libKitsunemimiCrypto/src/debug -lKitsunemimiCrypto +LIBS += -L../../libKitsunemimiCrypto/src/release -lKitsunemimiCrypto +INCLUDEPATH += ../../../libKitsunemimiCrypto/include + +LIBS += -L../../libKitsunemimiHanamiCommon/src -lKitsunemimiHanamiCommon +LIBS += -L../../libKitsunemimiHanamiCommon/src/debug -lKitsunemimiHanamiCommon +LIBS += -L../../libKitsunemimiHanamiCommon/src/release -lKitsunemimiHanamiCommon +INCLUDEPATH += ../../../libKitsunemimiHanamiCommon/include + +LIBS += -lssl -lcryptopp -lcrypt + +INCLUDEPATH += $$PWD \ + $$PWD/../include + +HEADERS += \ + ../include/libHanamiAiSdk/cluster.h \ + ../include/libHanamiAiSdk/data_set.h \ + ../include/libHanamiAiSdk/init.h \ + ../include/libHanamiAiSdk/project.h \ + ../include/libHanamiAiSdk/request_result.h \ + ../include/libHanamiAiSdk/task.h \ + ../include/libHanamiAiSdk/template.h \ + ../include/libHanamiAiSdk/user.h \ + ../include/libHanamiAiSdk/snapshot.h \ + ../include/libHanamiAiSdk/io.h \ + common/http_client.h \ + ../include/libHanamiAiSdk/common/websocket_client.h + +SOURCES += \ + cluster.cpp \ + data_set.cpp \ + init.cpp \ + io.cpp \ + project.cpp \ + request_result.cpp \ + task.cpp \ + template.cpp \ + user.cpp \ + snapshot.cpp \ + common/http_client.cpp \ + common/websocket_client.cpp + + +SHIORI_PROTO_BUFFER = ../../../libKitsunemimiHanamiMessages/protobuffers/shiori_messages.proto3 +KYOUKO_PROTO_BUFFER = ../../../libKitsunemimiHanamiMessages/protobuffers/kyouko_messages.proto3 + +OTHER_FILES += $$KYOUKO_PROTO_BUFFER +OTHER_FILES += $$SHIORI_PROTO_BUFFER + +protobuf_decl_shiori.name = protobuf headers +protobuf_decl_shiori.input = SHIORI_PROTO_BUFFER +protobuf_decl_shiori.output = ${QMAKE_FILE_IN_PATH}/${QMAKE_FILE_BASE}.proto3.pb.h +protobuf_decl_shiori.commands = protoc --cpp_out=${QMAKE_FILE_IN_PATH} --proto_path=${QMAKE_FILE_IN_PATH} ${QMAKE_FILE_NAME} +protobuf_decl_shiori.variable_out = HEADERS +QMAKE_EXTRA_COMPILERS += protobuf_decl_shiori + +protobuf_impl_shiori.name = protobuf sources +protobuf_impl_shiori.input = SHIORI_PROTO_BUFFER +protobuf_impl_shiori.output = ${QMAKE_FILE_IN_PATH}/${QMAKE_FILE_BASE}.proto3.pb.cc +protobuf_impl_shiori.depends = ${QMAKE_FILE_IN_PATH}/${QMAKE_FILE_BASE}.proto3.pb.h +protobuf_impl_shiori.commands = $$escape_expand(\n) +protobuf_impl_shiori.variable_out = SOURCES +QMAKE_EXTRA_COMPILERS += protobuf_impl_shiori + + +protobuf_decl_kyouko.name = protobuf headers +protobuf_decl_kyouko.name = protobuf headers +protobuf_decl_kyouko.input = KYOUKO_PROTO_BUFFER +protobuf_decl_kyouko.output = ${QMAKE_FILE_IN_PATH}/${QMAKE_FILE_BASE}.proto3.pb.h +protobuf_decl_kyouko.commands = protoc --cpp_out=${QMAKE_FILE_IN_PATH} --proto_path=${QMAKE_FILE_IN_PATH} ${QMAKE_FILE_NAME} +protobuf_decl_kyouko.variable_out = HEADERS +QMAKE_EXTRA_COMPILERS += protobuf_decl_kyouko + +protobuf_impl_kyouko.name = protobuf sources +protobuf_impl_kyouko.input = KYOUKO_PROTO_BUFFER +protobuf_impl_kyouko.output = ${QMAKE_FILE_IN_PATH}/${QMAKE_FILE_BASE}.proto3.pb.cc +protobuf_impl_kyouko.depends = ${QMAKE_FILE_IN_PATH}/${QMAKE_FILE_BASE}.proto3.pb.h +protobuf_impl_kyouko.commands = $$escape_expand(\n) +protobuf_impl_kyouko.variable_out = SOURCES +QMAKE_EXTRA_COMPILERS += protobuf_impl_kyouko diff --git a/src/frontend/Hanami-AI-Dashboard/src/libHanamiAiSdk/cpp/src/task.cpp b/src/frontend/Hanami-AI-Dashboard/src/libHanamiAiSdk/cpp/src/task.cpp new file mode 100644 index 00000000..9c16dfe0 --- /dev/null +++ b/src/frontend/Hanami-AI-Dashboard/src/libHanamiAiSdk/cpp/src/task.cpp @@ -0,0 +1,183 @@ +/** + * @file task.cpp + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2021 Tobias Anker + * + * 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 +#include + +namespace HanamiAI +{ + +/** + * @brief create a new learn-task + * + * @param result reference for response-message + * @param name name of the new task + * @param type type of the new task (learn or request) + * @param clusterUuid uuid of the cluster, which should execute the task + * @param dataSetUuid uuid of the data-set-file on server + * @param error reference for error-output + * + * @return true, if successful, else false + */ +bool +createTask(std::string &result, + const std::string &name, + const std::string &type, + const std::string &clusterUuid, + const std::string &dataSetUuid, + Kitsunemimi::ErrorContainer &error) +{ + // precheck task-type + if(type != "learn" + && type != "request") + { + error.addMeesage("Unknow task-type '" + type + "'"); + return false; + } + + // create request + HanamiRequest* request = HanamiRequest::getInstance(); + const std::string path = "/control/kyouko/v1/task"; + const std::string vars = ""; + const std::string jsonBody = "{\"name\":\"" + + name + + "\",\"type\":\"" + + type + + "\",\"cluster_uuid\":\"" + + clusterUuid + + "\",\"data_set_uuid\":\"" + + dataSetUuid + + "\"}"; + + // send request + if(request->sendPostRequest(result, path, vars, jsonBody, error) == false) + { + error.addMeesage("Failed to start task on cluster with UUID '" + + clusterUuid + + "' and dataset with UUID '" + + dataSetUuid + + "'"); + LOG_ERROR(error); + return false; + } + + return true; +} + +/** + * @brief get task-information + * + * @param result reference for response-message + * @param taskUuid uuid of the requested task + * @param clusterUuid uuid of the cluster, where the task belongs to + * @param error reference for error-output + * + * @return true, if successful, else false + */ +bool +getTask(std::string &result, + const std::string &taskUuid, + const std::string &clusterUuid, + Kitsunemimi::ErrorContainer &error) +{ + // create request + HanamiRequest* request = HanamiRequest::getInstance(); + const std::string path = "/control/kyouko/v1/task"; + std::string vars = "uuid=" + taskUuid + "&cluster_uuid=" + clusterUuid; + + // send request + if(request->sendGetRequest(result, path, vars, error) == false) + { + error.addMeesage("Failed to get task with UUID '" + + taskUuid + + "' of cluster with UUID '" + + clusterUuid + + "'"); + LOG_ERROR(error); + return false; + } + + return true; +} + +/** + * @brief list all visible tasks on kyouko + * + * @param result reference for response-message + * @param clusterUuid uuid of the cluster, which tasks should be listed + * @param error reference for error-output + * + * @return true, if successful, else false + */ +bool +listTask(std::string &result, + const std::string &clusterUuid, + Kitsunemimi::ErrorContainer &error) +{ + // create request + HanamiRequest* request = HanamiRequest::getInstance(); + const std::string path = "/control/kyouko/v1/task/all?cluster_uuid=" + clusterUuid; + + // send request + if(request->sendGetRequest(result, path, "", error) == false) + { + error.addMeesage("Failed to list tasks"); + LOG_ERROR(error); + return false; + } + + return true; +} + +/** + * @brief delete or abort a task from kyouko + * + * @param result reference for response-message + * @param taskUuid uuid of the task, which should be deleted + * @param clusterUuid uuid of the cluster, where the task belongs to + * @param error reference for error-output + * + * @return true, if successful, else false + */ +bool +deleteTask(std::string &result, + const std::string &taskUuid, + const std::string &clusterUuid, + Kitsunemimi::ErrorContainer &error) +{ + // create request + HanamiRequest* request = HanamiRequest::getInstance(); + const std::string path = "/control/kyouko/v1/task"; + const std::string vars = "uuid=" + taskUuid + "&clusterUuid=" + clusterUuid; + + // send request + if(request->sendDeleteRequest(result, path, vars, error) == false) + { + error.addMeesage("Failed to delete task with UUID '" + taskUuid + "'"); + LOG_ERROR(error); + return false; + } + + return true; +} + +} // namespace HanamiAI diff --git a/src/frontend/Hanami-AI-Dashboard/src/libHanamiAiSdk/cpp/src/template.cpp b/src/frontend/Hanami-AI-Dashboard/src/libHanamiAiSdk/cpp/src/template.cpp new file mode 100644 index 00000000..a00d4f04 --- /dev/null +++ b/src/frontend/Hanami-AI-Dashboard/src/libHanamiAiSdk/cpp/src/template.cpp @@ -0,0 +1,162 @@ +/** + * @file template.cpp + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2021 Tobias Anker + * + * 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 +#include +#include + +namespace HanamiAI +{ + +/** + * @brief upload a template to the kyouko + * + * @param result reference for response-message + * @param templateName name of the new template + * @param type type of the new template (cluster or segment) + * @param segmentTemplate template to upload. + * @param error reference for error-output + * + * @return true, if successful, else false + */ +bool +uploadTemplate(std::string &result, + const std::string &templateName, + const std::string &segmentTemplate, + Kitsunemimi::ErrorContainer &error) +{ + HanamiRequest* request = HanamiRequest::getInstance(); + + // convert template into base64-string + std::string segmentTemplateB64; + Kitsunemimi::encodeBase64(segmentTemplateB64, + segmentTemplate.c_str(), + segmentTemplate.size()); + + // create request + const std::string path = "/control/kyouko/v1/template/upload"; + const std::string vars = ""; + const std::string jsonBody = "{\"name\":\"" + + templateName + + "\",\"template\":\"" + + segmentTemplateB64 + + "\"}"; + + // send request + if(request->sendPostRequest(result, path, vars, jsonBody, error) == false) + { + error.addMeesage("Failed to upload new template"); + LOG_ERROR(error); + return false; + } + + return true; +} + +/** + * @brief get a specific template from kyouko + * + * @param result reference for response-message + * @param templateUuid uuid of the template to get + * @param error reference for error-output + * + * @return true, if successful, else false + */ +bool +getTemplate(std::string &result, + const std::string &templateUuid, + Kitsunemimi::ErrorContainer &error) +{ + // create request + HanamiRequest* request = HanamiRequest::getInstance(); + const std::string path = "/control/kyouko/v1/template"; + const std::string vars = "uuid=" + templateUuid; + + // send request + if(request->sendGetRequest(result, path, vars, error) == false) + { + error.addMeesage("Failed to get template with UUID '" + templateUuid + "'"); + LOG_ERROR(error); + return false; + } + + return true; +} + +/** + * @brief list all visible templates on kyouko + * + * @param result reference for response-message + * @param error reference for error-output + * + * @return true, if successful, else false + */ +bool +listTemplate(std::string &result, + Kitsunemimi::ErrorContainer &error) +{ + // create request + HanamiRequest* request = HanamiRequest::getInstance(); + const std::string path = "/control/kyouko/v1/template/all"; + + // send request + if(request->sendGetRequest(result, path, "", error) == false) + { + error.addMeesage("Failed to list templates"); + LOG_ERROR(error); + return false; + } + + return true; +} + +/** + * @brief delete a template form kyouko + * + * @param result reference for response-message + * @param templateUuid uuid of the template to delete + * @param error reference for error-output + * + * @return true, if successful, else false + */ +bool +deleteTemplate(std::string &result, + const std::string &templateUuid, + Kitsunemimi::ErrorContainer &error) +{ + // create request + HanamiRequest* request = HanamiRequest::getInstance(); + const std::string path = "/control/kyouko/v1/template"; + const std::string vars = "uuid=" + templateUuid; + + // send request + if(request->sendDeleteRequest(result, path, vars, error) == false) + { + error.addMeesage("Failed to delete template with UUID '" + templateUuid + "'"); + LOG_ERROR(error); + return false; + } + + return true; +} + +} // namespace HanamiAI diff --git a/src/frontend/Hanami-AI-Dashboard/src/libHanamiAiSdk/cpp/src/user.cpp b/src/frontend/Hanami-AI-Dashboard/src/libHanamiAiSdk/cpp/src/user.cpp new file mode 100644 index 00000000..ebc39ee0 --- /dev/null +++ b/src/frontend/Hanami-AI-Dashboard/src/libHanamiAiSdk/cpp/src/user.cpp @@ -0,0 +1,321 @@ +/** + * @file user.cpp + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2021 Tobias Anker + * + * 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 +#include +#include + +namespace HanamiAI +{ + +/** + * @brief create a new user in misaki + * + * @param result reference for response-message + * @param id id of the new user + * @param userName name of the new user + * @param password password of the new user + * @param isAdmin true to make new user to an admin + * @param error reference for error-output + * + * @return true, if successful, else false + */ +bool +createUser(std::string &result, + const std::string &userId, + const std::string &userName, + const std::string &password, + const bool isAdmin, + Kitsunemimi::ErrorContainer &error) +{ + // create request + HanamiRequest* request = HanamiRequest::getInstance(); + const std::string path = "/control/misaki/v1/user"; + const std::string vars = ""; + const std::string jsonBody = "{\"id\":\"" + + userId + + "\",\"name\":\"" + + userName + + "\",\"password\":\"" + + password + + "\",\"is_admin\":" + + (isAdmin ? "true" : "false") + + + "}"; + + // send request + if(request->sendPostRequest(result, path, vars, jsonBody, error) == false) + { + error.addMeesage("Failed to create user with name '" + userName + "'"); + LOG_ERROR(error); + return false; + } + + return true; +} + +/** + * @brief get information of a user from misaki + * + * @param result reference for response-message + * @param userId id of the requested user + * @param error reference for error-output + * + * @return true, if successful, else false + */ +bool +getUser(std::string &result, + const std::string &userId, + Kitsunemimi::ErrorContainer &error) +{ + // create request + HanamiRequest* request = HanamiRequest::getInstance(); + const std::string path = "/control/misaki/v1/user"; + const std::string vars = "id=" + userId; + + if(request->sendGetRequest(result, path, vars, error) == false) + { + error.addMeesage("Failed to get user with id '" + userId + "'"); + LOG_ERROR(error); + return false; + } + + return true; +} + +/** + * @brief list all visible users on misaki + * + * @param result reference for response-message + * @param error reference for error-output + * + * @return true, if successful, else false + */ +bool +listUser(std::string &result, + Kitsunemimi::ErrorContainer &error) +{ + // create request + HanamiRequest* request = HanamiRequest::getInstance(); + const std::string path = "/control/misaki/v1/user/all"; + + // send request + if(request->sendGetRequest(result, path, "", error) == false) + { + error.addMeesage("Failed to list users"); + LOG_ERROR(error); + return false; + } + + return true; +} + +/** + * @brief delete a user from misaki + * + * @param result reference for response-message + * @param userId id of the user, which should be deleted + * @param error reference for error-output + * + * @return true, if successful, else false + */ +bool +deleteUser(std::string &result, + const std::string &userId, + Kitsunemimi::ErrorContainer &error) +{ + // create request + HanamiRequest* request = HanamiRequest::getInstance(); + const std::string path = "/control/misaki/v1/user"; + const std::string vars = "id=" + userId; + + // send request + if(request->sendDeleteRequest(result, path, vars, error) == false) + { + error.addMeesage("Failed to delete user with id '" + userId + "'"); + LOG_ERROR(error); + return false; + } + + return true; +} + +/** + * @brief assign an already existing project to a user. + * + * @param result reference for response-message + * @param userId id of the user, who should be assigned to another projects + * @param projectId id of the project, which should be assigned to the user + * @param role role of the user, while signed-in in the project + * @param isProjectAdmin true, if user should be admin within the new added project + * @param error reference for error-output + * + * @return true, if successful, else false + */ +bool +addProjectToUser(std::string &result, + const std::string &userId, + const std::string &projectId, + const std::string &role, + const bool isProjectAdmin, + Kitsunemimi::ErrorContainer &error) +{ + // create request + HanamiRequest* request = HanamiRequest::getInstance(); + const std::string path = "/control/misaki/v1/user/project"; + const std::string vars = ""; + const std::string jsonBody = "{id:\"" + userId; + + "\",project_id:\"" + projectId + + "\",role:\"" + role + + "\",is_project_admin:" + (isProjectAdmin ? "true" : "false") + + "}"; + + // send request + if(request->sendPostRequest(result, path, vars, jsonBody, error) == false) + { + error.addMeesage("Failed to add project with id '" + + projectId + + "' to user with id '" + + userId + + "'"); + LOG_ERROR(error); + return false; + } + + return true; +} + +/** + * @brief unassign project from a user + * + * @param result reference for response-message + * @param userId id of the user, who should be unassigned + * @param projectId id of the project, which should be removed from the user + * @param error reference for error-output + * + * @return true, if successful, else false + */ +bool +removeProjectFromUser(std::string &result, + const std::string &userId, + const std::string &projectId, + Kitsunemimi::ErrorContainer &error) +{ + // create request + HanamiRequest* request = HanamiRequest::getInstance(); + const std::string path = "/control/misaki/v1/user/project"; + const std::string vars = "id=" + userId + "&project_id=" + projectId; + + // send request + if(request->sendDeleteRequest(result, path, vars, error) == false) + { + error.addMeesage("Failed to remove project with id '" + + projectId + + "' from user with id '" + + userId + + "'"); + LOG_ERROR(error); + return false; + } + + return true; +} + +/** + * @brief list all projects where the current user is assigned to + * + * @param result reference for response-message + * @param error reference for error-output + * + * @return true, if successful, else false + */ +bool +listProjectsOfUser(std::string &result, + Kitsunemimi::ErrorContainer &error) +{ + // create request + HanamiRequest* request = HanamiRequest::getInstance(); + const std::string path = "/control/misaki/v1/user/project"; + + // send request + if(request->sendGetRequest(result, path, "", error) == false) + { + error.addMeesage("Failed to list project of user"); + LOG_ERROR(error); + return false; + } + + return true; +} + +/** + * @brief switch to another project by requesting a new token for the selected project. The project + * must be in the list of assigned projects of the user. + * + * @param result reference for response-message + * @param projectId id of the project, where to switch to + * @param error reference for error-output + * + * @return true, if successful, else false + */ +bool +switchProject(std::string &result, + const std::string &projectId, + Kitsunemimi::ErrorContainer &error) +{ + // create request + HanamiRequest* request = HanamiRequest::getInstance(); + const std::string path = "/control/misaki/v1/user/project"; + const std::string vars = ""; + const std::string jsonBody = "{\"project_id\":\"" + projectId + "\"}"; + + // send request + if(request->sendPutRequest(result, path, vars, jsonBody, error) == false) + { + error.addMeesage("Failed to swtich to project with id '" + projectId + "'"); + LOG_ERROR(error); + return false; + } + + // try to parse response + Kitsunemimi::JsonItem item; + if(item.parse(result, error) == false) + { + error.addMeesage("Failed to parse token-response"); + LOG_ERROR(error); + return false; + } + + // get token from parsed item + const std::string newToken = item["token"].getString(); + if(newToken == "") + { + error.addMeesage("Can not find token in token-response"); + LOG_ERROR(error); + return false; + } + + request->updateToken(newToken); + + return true; +} + +} // namespace HanamiAI diff --git a/src/frontend/Hanami-AI-Dashboard/src/libHanamiAiSdk/cpp/tests/tests.pro b/src/frontend/Hanami-AI-Dashboard/src/libHanamiAiSdk/cpp/tests/tests.pro new file mode 100644 index 00000000..9c043732 --- /dev/null +++ b/src/frontend/Hanami-AI-Dashboard/src/libHanamiAiSdk/cpp/tests/tests.pro @@ -0,0 +1,7 @@ +TEMPLATE = subdirs +CONFIG += ordered +QT -= qt core gui +CONFIG += c++17 + + +tests.depends = src diff --git a/src/frontend/Hanami-AI-Dashboard/src/libHanamiAiSdk/go/README.md b/src/frontend/Hanami-AI-Dashboard/src/libHanamiAiSdk/go/README.md new file mode 100644 index 00000000..85c24036 --- /dev/null +++ b/src/frontend/Hanami-AI-Dashboard/src/libHanamiAiSdk/go/README.md @@ -0,0 +1 @@ +# libKitsumiAiSdk \ No newline at end of file diff --git a/src/frontend/Hanami-AI-Dashboard/src/libHanamiAiSdk/go/kitsumi_ai_sdk/cluster_commands.go b/src/frontend/Hanami-AI-Dashboard/src/libHanamiAiSdk/go/kitsumi_ai_sdk/cluster_commands.go new file mode 100644 index 00000000..d02d0998 --- /dev/null +++ b/src/frontend/Hanami-AI-Dashboard/src/libHanamiAiSdk/go/kitsumi_ai_sdk/cluster_commands.go @@ -0,0 +1,34 @@ +/** + * @file cluster_commands.go + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2021 Tobias Anker + * + * 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. + */ + + package http_request + +import ( + "fmt" +) + +func CreateCluster_Request(data string) (bool, string) { + jsonBody := fmt.Sprintf("{\"data\":\"%s\"}", data) + path := "control/kyouko/v1/cluster" + vars := "" + return SendPost_Request(path, vars, jsonBody) +} diff --git a/src/frontend/Hanami-AI-Dashboard/src/libHanamiAiSdk/go/kitsumi_ai_sdk/documentation_commands.go b/src/frontend/Hanami-AI-Dashboard/src/libHanamiAiSdk/go/kitsumi_ai_sdk/documentation_commands.go new file mode 100644 index 00000000..c99758a5 --- /dev/null +++ b/src/frontend/Hanami-AI-Dashboard/src/libHanamiAiSdk/go/kitsumi_ai_sdk/documentation_commands.go @@ -0,0 +1,30 @@ +/** + * @file user_commands.go + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2021 Tobias Anker + * + * 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. + */ + +package http_request + +func GetRestApiDocumentation_Request() (bool, string) { + path := "control/misaka/v1/documentation/api/rest" + vars := "" + return SendGet_Request(path, vars) +} + \ No newline at end of file diff --git a/src/frontend/Hanami-AI-Dashboard/src/libHanamiAiSdk/go/kitsumi_ai_sdk/go.mod b/src/frontend/Hanami-AI-Dashboard/src/libHanamiAiSdk/go/kitsumi_ai_sdk/go.mod new file mode 100644 index 00000000..d50db501 --- /dev/null +++ b/src/frontend/Hanami-AI-Dashboard/src/libHanamiAiSdk/go/kitsumi_ai_sdk/go.mod @@ -0,0 +1,5 @@ +module kitsunemimi.moe/hanami_sdk + +go 1.13 + +replace kitsunemimi.moe/hanami_sdk => ../hanami_sdk diff --git a/src/frontend/Hanami-AI-Dashboard/src/libHanamiAiSdk/go/kitsumi_ai_sdk/http_request.go b/src/frontend/Hanami-AI-Dashboard/src/libHanamiAiSdk/go/kitsumi_ai_sdk/http_request.go new file mode 100644 index 00000000..1d81d9eb --- /dev/null +++ b/src/frontend/Hanami-AI-Dashboard/src/libHanamiAiSdk/go/kitsumi_ai_sdk/http_request.go @@ -0,0 +1,170 @@ +/** + * @file http_request.go + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2021 Tobias Anker + * + * 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. + */ + + package http_request + +import ( + "fmt" + "net/http" + "io/ioutil" + "crypto/tls" + "strings" + "strconv" + "os" + "encoding/json" +) + +func SendGet_Request(path string, vars string) (bool, string) { + return sendHanamiRequest("GET", path, vars, "") +} + +func SendPost_Request(path string, vars string, jsonBody string) (bool, string) { + return sendHanamiRequest("POST", path, vars, jsonBody) +} + +func SendPut_Request(path string, vars string, jsonBody string) (bool, string) { + return sendHanamiRequest("PUT", path, vars, jsonBody) +} + +func SendDelete_Request(path string, vars string) (bool, string) { + return sendHanamiRequest("DELETE", path, vars, "") +} + + +func sendHanamiRequest(requestType string, path string, vars string, jsonBody string) (bool, string){ + var token = os.Getenv("HANAMI_TOKEN") + + // request token, if no one exist within the environment-variables + if token == "" { + success := requestToken() + if success == false { + return false, "ACCESS DENIED!" + } + } + + // make request + token = os.Getenv("HANAMI_TOKEN") + success, content := sendRequest(requestType, token, path, vars, jsonBody) + + // hande expired token + if success && content == "Token is expired" { + success := requestToken() + if success == false { + return false, "ACCESS DENIED!" + } + + // make new request with new token + token = os.Getenv("HANAMI_TOKEN") + + return sendRequest(requestType, token, path, vars, jsonBody) + } + + return success, content +} + + +func parseJson(input string) map[string]interface{} { + // parse json and fill into map + outputMap := map[string]interface{}{} + err := json.Unmarshal([]byte(input), &outputMap) + if err != nil { + panic(err) + } + + return outputMap +} + + +func requestToken() bool { + var user = os.Getenv("HANAMI_USER") + var pw = os.Getenv("HANAMI_PW") + + path := fmt.Sprintf("control/misaka/v1/token?name=%s&pw=%s", user, pw) + + success, content := sendGenericRequest("GET", "", path, "") + if success == false { + return false + } + + outputMap := parseJson(content) + token := outputMap["token"].(string) + os.Setenv("HANAMI_TOKEN", token) + + return true +} + +func sendRequest(requestType string, token string, path string, vars string, jsonBody string) (bool, string) { + completePath := path + if vars != "" { + completePath += fmt.Sprintf("?%s", vars) + } + + return sendGenericRequest(requestType, token, completePath, jsonBody) +} + + +func sendGenericRequest(requestType string, token string, path string, jsonBody string) (bool, string) { + // read environment-variables + var address = os.Getenv("HANAMI_ADDRESS") + port, err := strconv.Atoi(os.Getenv("HANAMI_PORT")) + if err != nil { + return false, "err" + } + + // check if https or not + if strings.Contains(address, "https") { + http.DefaultTransport.(*http.Transport).TLSClientConfig = &tls.Config{InsecureSkipVerify: true} + } + + // build uri + var reqBody = strings.NewReader(jsonBody) + // fmt.Printf("completePath: "+ completePath) + completePath := fmt.Sprintf("%s:%d/%s", address, port, path) + req, err := http.NewRequest(requestType, completePath, reqBody) + if err != nil { + panic(err) + } + + // add token to header + if token != "" { + req.Header.Set("X-Auth-Token", token) + } + + // run request + resp, err := http.DefaultClient.Do(req) + if err != nil { + panic(err) + } + defer resp.Body.Close() + + // read data from response and convert into string + bodyBytes, err := ioutil.ReadAll(resp.Body) + if err != nil { + return false, "" + } + bodyString := string(bodyBytes) + + // fmt.Printf("bodyString: " + bodyString + "\n") + + var ok = resp.StatusCode == http.StatusOK + return ok, bodyString +} diff --git a/src/frontend/Hanami-AI-Dashboard/src/libHanamiAiSdk/go/kitsumi_ai_sdk/run_commands.go b/src/frontend/Hanami-AI-Dashboard/src/libHanamiAiSdk/go/kitsumi_ai_sdk/run_commands.go new file mode 100644 index 00000000..9a013c7b --- /dev/null +++ b/src/frontend/Hanami-AI-Dashboard/src/libHanamiAiSdk/go/kitsumi_ai_sdk/run_commands.go @@ -0,0 +1,51 @@ +/** + * @file run_commands.go + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2021 Tobias Anker + * + * 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. + */ + + package http_request + +import ( + "fmt" +) + +func RunLearn_Request(number_of_inputs_per_cycle string, number_of_outputs_per_cycle string, number_of_cycles string, cluster_uuid string, inputs string, label string) (bool, string) { + path := "control/kyouko/v1/io" + vars := "" + jsonBody := fmt.Sprintf("{\"number_of_inputs_per_cycle\":%s, \"number_of_outputs_per_cycle\":%s, \"number_of_cycles\":%s, \"cluster_uuid\":\"%s\", \"inputs\":\"%s\", \"label\":\"%s\"}", + number_of_inputs_per_cycle, + number_of_outputs_per_cycle, + number_of_cycles, + cluster_uuid, + inputs, + label) + return SendPost_Request(path, vars, jsonBody) +} + +func RunAsk_Request(number_of_inputs_per_cycle string, number_of_cycles string, cluster_uuid string, inputs string) (bool, string) { + path := "control/kyouko/v1/io" + vars := "" + jsonBody := fmt.Sprintf("{\"number_of_inputs_per_cycle\":%s, \"number_of_cycles\":%s, \"cluster_uuid\":\"%s\", \"inputs\":\"%s\"}", + number_of_inputs_per_cycle, + number_of_cycles, + cluster_uuid, + inputs) + return SendPost_Request(path, vars, jsonBody) +} diff --git a/src/frontend/Hanami-AI-Dashboard/src/libHanamiAiSdk/go/kitsumi_ai_sdk/threading_commands.go b/src/frontend/Hanami-AI-Dashboard/src/libHanamiAiSdk/go/kitsumi_ai_sdk/threading_commands.go new file mode 100644 index 00000000..23b013d0 --- /dev/null +++ b/src/frontend/Hanami-AI-Dashboard/src/libHanamiAiSdk/go/kitsumi_ai_sdk/threading_commands.go @@ -0,0 +1,32 @@ +/** + * @file train_data_commands.go + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2021 Tobias Anker + * + * 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. + */ + + package http_request + +import ( +) + +func GetThreadMapping_Request() (bool, string) { + path := "control/azuki/v1/threading" + vars := "" + return SendGet_Request(path, vars) +} diff --git a/src/frontend/Hanami-AI-Dashboard/src/libHanamiAiSdk/go/kitsumi_ai_sdk/train_data_commands.go b/src/frontend/Hanami-AI-Dashboard/src/libHanamiAiSdk/go/kitsumi_ai_sdk/train_data_commands.go new file mode 100644 index 00000000..7e7a4796 --- /dev/null +++ b/src/frontend/Hanami-AI-Dashboard/src/libHanamiAiSdk/go/kitsumi_ai_sdk/train_data_commands.go @@ -0,0 +1,51 @@ +/** + * @file train_data_commands.go + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2021 Tobias Anker + * + * 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. + */ + + package http_request + +import ( + "fmt" +) + +func UploadTrainData_Request(name string, dataType string, data string) (bool, string) { + path := "control/sagiri/train_data" + vars := "" + jsonBody := fmt.Sprintf("{\"name\":%s,\"type\":%s,\"data\":%s}", name, dataType, data) + return SendPost_Request(path, vars, jsonBody) +} + +func GetTrainData_Request(data_uuid string, with_data bool) (bool, string) { + path := "control/sagiri/train_data" + vars := fmt.Sprintf("uuid=%s", data_uuid) + if with_data { + vars += "&with_data=true" + } else { + vars += "&with_data=false" + } + return SendGet_Request(path, vars) +} + +func ListTrainData_Request() (bool, string) { + path := fmt.Sprintf("control/sagiri/train_datas") + vars := "" + return SendGet_Request(path, vars) +} diff --git a/src/frontend/Hanami-AI-Dashboard/src/libHanamiAiSdk/go/kitsumi_ai_sdk/user_commands.go b/src/frontend/Hanami-AI-Dashboard/src/libHanamiAiSdk/go/kitsumi_ai_sdk/user_commands.go new file mode 100644 index 00000000..90f132c6 --- /dev/null +++ b/src/frontend/Hanami-AI-Dashboard/src/libHanamiAiSdk/go/kitsumi_ai_sdk/user_commands.go @@ -0,0 +1,53 @@ +/** + * @file user_commands.go + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2021 Tobias Anker + * + * 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. + */ + +package http_request + +import ( + "fmt" +) + +func CreateUser_Request(user_name string, pw string, is_admin string, groups string) (bool, string) { + path := "control/misaka/v1/create_user" + vars := "" + jsonBody := fmt.Sprintf("{\"user_name\":\"%s\",\"pw\":\"%s\",\"is_admin\":%s,\"groups\":\"%s\"}", + user_name, pw, is_admin, groups) + return SendPost_Request(path, vars, jsonBody) +} + +func GetUser_Request(user_name string) (bool, string) { + path := "control/misaka/v1/user" + vars := fmt.Sprintf("user_name=%s", user_name) + return SendGet_Request(path, vars) +} + +func ListUser_Request() (bool, string) { + path := fmt.Sprintf("control/misaka/v1/user/all") + vars := "" + return SendGet_Request(path, vars) +} + +func DeleteUser_Request(user_name string) (bool, string) { + path := "control/misaka/v1/user" + vars := fmt.Sprintf("user_name=%s", user_name) + return SendDelete_Request(path, vars) +} \ No newline at end of file diff --git a/src/frontend/Hanami-AI-Dashboard/src/libHanamiAiSdk/javascript/cluster.js b/src/frontend/Hanami-AI-Dashboard/src/libHanamiAiSdk/javascript/cluster.js new file mode 100644 index 00000000..375fe0b0 --- /dev/null +++ b/src/frontend/Hanami-AI-Dashboard/src/libHanamiAiSdk/javascript/cluster.js @@ -0,0 +1,51 @@ +// Apache License Version 2.0 + +// Copyright 2020 Tobias Anker + +// 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. + +function createCluster_request(outputFunc, name, templateStr, token) +{ + const path = "/control/kyouko/v1/cluster"; + let reqContent = "{\"name\":\"" + name; + reqContent += "\",\"template\":\"" + btoa(templateStr) + "\"}"; + createObject_request(outputFunc, path, reqContent, token); +} + +function saveCluster_request(outputFunc, name, clusterUuid, token) +{ + const path = "/control/kyouko/v1/cluster/save"; + let reqContent = "{\"name\":\"" + name; + reqContent += "\",\"cluster_uuid\":" + clusterUuid + "}"; + createObject_request(outputFunc, path, reqContent, token); +} + +function restoreCluster_request(outputFunc, snapshotUuid, clusterUuid, token) +{ + const path = "/control/kyouko/v1/cluster/load"; + let reqContent = "{\"snapshot_uuid\":\"" + snapshotUuid; + reqContent += "\",\"cluster_uuid\":" + clusterUuid + "}"; + createObject_request(outputFunc, path, reqContent, token); +} + +function listClusters_request(outputFunc, token) +{ + listObjects_request(outputFunc, "/control/kyouko/v1/cluster/all", token); +} + +function deleteCluster_request(postProcessFunc, clusterUuid, token) +{ + const request = "/control/kyouko/v1/cluster?uuid=" + clusterUuid; + deleteObject_request(postProcessFunc, request, token); +} + diff --git a/src/frontend/Hanami-AI-Dashboard/src/libHanamiAiSdk/javascript/common.js b/src/frontend/Hanami-AI-Dashboard/src/libHanamiAiSdk/javascript/common.js new file mode 100644 index 00000000..d3cb6992 --- /dev/null +++ b/src/frontend/Hanami-AI-Dashboard/src/libHanamiAiSdk/javascript/common.js @@ -0,0 +1,67 @@ +// Apache License Version 2.0 + +// Copyright 2020 Tobias Anker + +// 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. + +function makeHttpRequest(outputFunc, path, type, payload, token) +{ + // create request + let requestConnection = new XMLHttpRequest(); + requestConnection.open(type, path, true); + requestConnection.setRequestHeader("X-Auth-Token", token); + + // create requeset + requestConnection.onload = function(e) + { + if(requestConnection.status != 200) + { + console.log("HTTP-request failed: " + requestConnection.status); + } + + outputFunc(requestConnection.status, requestConnection.responseText); + }; + + // callback for fail + requestConnection.onerror = function(e) + { + console.log("Failed to request request-results from shiori."); + }; + + requestConnection.send(payload); +} + +function createObject_request(outputFunc, path, payload, token) +{ + makeHttpRequest(outputFunc, path, "POST", payload, token); +} + +function updateObject_request(outputFunc, path, payload, token) +{ + makeHttpRequest(outputFunc, path, "PUT", payload, token); +} + +function getObject_request(outputFunc, path, token) +{ + makeHttpRequest(outputFunc, path, "GET", null, token); +} + +function listObjects_request(outputFunc, path, token) +{ + makeHttpRequest(outputFunc, path, "GET", null, token); +} + +function deleteObject_request(postProcessFunc, path, token) +{ + makeHttpRequest(postProcessFunc, path, "DELETE", null, token); +} diff --git a/src/frontend/Hanami-AI-Dashboard/src/libHanamiAiSdk/javascript/data_set.js b/src/frontend/Hanami-AI-Dashboard/src/libHanamiAiSdk/javascript/data_set.js new file mode 100644 index 00000000..cd0ff0b5 --- /dev/null +++ b/src/frontend/Hanami-AI-Dashboard/src/libHanamiAiSdk/javascript/data_set.js @@ -0,0 +1,319 @@ +// Apache License Version 2.0 + +// Copyright 2020 Tobias Anker + +// 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. + +function sendFile(websocket, file, uuid, fileUuid) +{ + protobuf.load("/libKitsunemimiHanamiMessages/protobuffers/shiori_messages.proto3", function(err, root) + { + if(err) { + throw err; + } + + // Obtain a message type + let FileUpload_Message = root.lookupType("FileUpload_Message"); + + let segmentSize = 96 * 1024; + let fileSize = file.size; + + for(let start = 0; start < fileSize; start += segmentSize) + { + let reader = new FileReader(); + if(start + segmentSize > fileSize) { + segmentSize = fileSize - start; + } + let blob = file.slice(start, segmentSize + start); + let isLast = start + segmentSize >= fileSize; + + // read part of the file and send id as new message + reader.onload = function(e) + { + var payload = { + datasetUuid: uuid, + fileUuid: fileUuid, + isLast: isLast, + type: 0, + position: start, + // get reeader-result and cut unnecessary header of the resulting string + data: e.target.result.split(',')[1] + }; + var errMsg = FileUpload_Message.verify(payload); + if(errMsg) { + throw Error(errMsg); + } + + // Create a new message and ncode a message to an Uint8Array (browser) + let message = FileUpload_Message.create(payload); + let buffer = FileUpload_Message.encode(message).finish(); + + websocket.send(buffer); + }; + + // read as DataURL instead of array-buffer, + // because the javascript websocket seems to have problems with plain raw-data + reader.readAsDataURL(blob); + } + }); + + return true; +} + +/** + * wait for a specific amount of time + * + * @param {ms} number of milliseconds to sleep + */ +function sleep(ms) { + var start = new Date().getTime(), expire = start + ms; + while (new Date().getTime() < expire) { } + return; +} + +/** + * wait until a data-set is fully uploaded + * + * @param {uuid} uuid of the dataset to check + * @param {token} access jwt-token + */ +function waitUntilUploadComplete(uuid, token) +{ + // wait until upload completed + var completeUploaded = false; + while(completeUploaded == false) + { + // wait 500ms + sleep(500); + var request = new XMLHttpRequest(); + // `false` makes the request synchronous + request.open('GET', '/control/shiori/v1/data_set/progress?uuid=' + uuid, false); + request.setRequestHeader("X-Auth-Token", token); + request.send(null); + + if(request.status !== 200) { + return false; + } + + const jsonContent = JSON.parse(request.responseText); + completeUploaded = jsonContent.complete; + } + + return true; +} + +/** + * wait until a data-set is fully uploaded + * + * @param {websocket} uuid of the dataset to check + * @param {uuid} access jwt-token + * @param {file} uuid of the dataset to check + * @param {fileUuid} access jwt-token + */ +function sendCsvFiles(websocket, uuid, file, fileUuid) +{ + const token = getAndCheckToken(); + if(token == "") { + return false; + } + + websocket.onopen = function () { + console.log("WebSocket open") + const initialMsg = "{\"token\":\"" + token + "\",\"target\":\"shiori\"}"; + websocket.send(initialMsg); + }; + + websocket.onerror = function () { + console.log("WebSocket failed!"); + }; + + websocket.onmessage = function(event) { + var reader = new FileReader(); + reader.onload = function() { + console.log("Data received from server: " + reader.result); + } + reader.readAsText(event.data); + + if(sendFile(websocket, file, uuid, fileUuid) == false) { + return false; + } + }; + + if(waitUntilUploadComplete(uuid, token) == false) { + return false; + } + + return true; +} + +/** + * upload finalize the upload of the csv-dataset + * + * @param {uuid} uuid of the data-set + * @param {inputUuid} uuid of the temporary file for the input-data + * @param {token} access jwt-token + */ +function finishCsvUpload(uuid, inputUuid, token) +{ + // finish upload + var request = new XMLHttpRequest(); + // `false` makes the request synchronous + request.open('PUT', '/control/shiori/v1/csv/data_set', false); + request.setRequestHeader("X-Auth-Token", token); + var jsonBody = "{\"uuid\":\"" + uuid + + "\",\"uuid_input_file\":\"" + inputUuid + "\"}"; + request.send(jsonBody); + + if(request.status !== 200) { + return false; + } + + return true; +} + +/** + * send mnist-files over a websocket + * + * @param {websocket} websocket where the data should be send + * @param {uuid} uuid of the data-set + * @param {inputFile} file with input-data + * @param {labelFile} file with label-data + * @param {inputFileUuid} uuid of the temporary file for the input-data + * @param {labelFileUuid} uuid of the temporary file for the label-data + */ +function sendMnistFiles(websocket, uuid, inputFile, labelFile, inputFileUuid, labelFileUuid) +{ + const token = getAndCheckToken(); + if(token == "") { + return false; + } + + websocket.onopen = function () { + console.log("WebSocket open") + const initialMsg = "{\"token\":\"" + token + "\",\"target\":\"shiori\"}"; + websocket.send(initialMsg); + }; + + websocket.onerror = function () { + console.log("WebSocket failed!"); + }; + + websocket.onmessage = function(event) { + var reader = new FileReader(); + reader.onload = function() { + console.log("Data received from server: " + reader.result); + } + reader.readAsText(event.data); + + if(sendFile(websocket, inputFile, uuid, inputFileUuid) == false) { + return false; + } + if(sendFile(websocket, labelFile, uuid, labelFileUuid) == false) { + return false; + } + }; + + if(waitUntilUploadComplete(uuid, token) == false) { + return false; + } + + return true; +} + +/** + * upload finalize the upload of the mnist-dataset + * + * @param {uuid} uuid of the data-set + * @param {inputUuid} uuid of the temporary file for the input-data + * @param {labelUuid} uuid of the temporary file for the label-data + * @param {token} access jwt-token + */ +function finishMnistUpload(uuid, inputUuid, labelUuid, token) +{ + // finish upload + var request = new XMLHttpRequest(); + // `false` makes the request synchronous + request.open('PUT', '/control/shiori/v1/mnist/data_set', false); + request.setRequestHeader("X-Auth-Token", token); + var jsonBody = "{\"uuid\":\"" + uuid + + "\",\"uuid_input_file\":\"" + inputUuid + + "\",\"uuid_label_file\":\"" + labelUuid + "\"}"; + request.send(jsonBody); + + if(request.status !== 200) { + return false; + } + + return true; +} + +function uploadMnistFiles(jsonContent, inputFile, labelFile, token) +{ + const uuid = jsonContent.uuid; + const inputFileUuid = jsonContent.uuid_input_file; + const labelFileUuid = jsonContent.uuid_label_file; + + var websocket = new WebSocket('wss://' + location.host); + if(sendMnistFiles(websocket, uuid, inputFile, labelFile, inputFileUuid, labelFileUuid) == false) { + return; + } + + if(finishMnistUpload(uuid, inputFileUuid, labelFileUuid, token) == false) { + return; + } +} + +function uploadCsvFile(jsonContent, inputFile, token) +{ + const uuid = jsonContent.uuid; + const inputFileUuid = jsonContent.uuid_input_file; + + var websocket = new WebSocket('wss://' + location.host); + if(sendCsvFiles(websocket, uuid, inputFile, inputFileUuid) == false) { + return; + } + + if(finishCsvUpload(uuid, inputFileUuid, token) == false) { + return; + } +} + + +function createMnistDataSet_request(outputFunc, name, inputFile, labelFile, token) +{ + const path = "/control/shiori/v1/mnist/data_set"; + let reqContent = "{\"name\":\"" + name; + reqContent += "\",\"input_data_size\":" + inputFile.size + reqContent += ",\"label_data_size\":" + labelFile.size + "}"; + createObject_request(outputFunc, path, reqContent, token); +} + +function createCsvDataSet_request(outputFunc, name, inputFile, token) +{ + const path = "/control/shiori/v1/csv/data_set"; + let reqContent = "{name:\"" + name; + reqContent += "\",input_data_size:" + inputFile.size + "}"; + createObject_request(outputFunc, path, reqContent, token); +} + +function listDataSet_request(outputFunc, token) +{ + listObjects_request(outputFunc, "/control/shiori/v1/data_set/all", token); +} + +function deleteDataSet_request(postProcessFunc, dataSetUuid, token) +{ + const request = "/control/shiori/v1/data_set?uuid=" + dataSetUuid; + deleteObject_request(postProcessFunc, request, token); +} + diff --git a/src/frontend/Hanami-AI-Dashboard/src/libHanamiAiSdk/javascript/init.js b/src/frontend/Hanami-AI-Dashboard/src/libHanamiAiSdk/javascript/init.js new file mode 100644 index 00000000..87dae677 --- /dev/null +++ b/src/frontend/Hanami-AI-Dashboard/src/libHanamiAiSdk/javascript/init.js @@ -0,0 +1,72 @@ +// Apache License Version 2.0 + +// Copyright 2020 Tobias Anker + +// 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. + +function login_request(loginFunc, user, pw) +{ + const path = "/control/misaki/v1/token"; + const reqContent = "{\"id\":\"" + user + "\",\"password\":\"" + pw + "\"}"; + + let loginConnection = new XMLHttpRequest(); + loginConnection.open("POST", path, true); + + // create requeset + loginConnection.onload = function(e) + { + if(loginConnection.status != 200) + { + // TODO: error-popup + showErrorInModal("login", loginConnection.responseText); + return; + } + + loginFunc(JSON.parse(loginConnection.responseText)); + }; + + // callback for fail + loginConnection.onerror = function(e) + { + console.log("Failed to request request-results from shiori."); + }; + + loginConnection.send(reqContent); +} + +function checkTokenRequest(errorFunc, token) +{ + // create request + const request = "/control/misaki/v1/auth?token=" + token; + let authConnection = new XMLHttpRequest(); + authConnection.open("GET", request, true); + authConnection.setRequestHeader("X-Auth-Token", token); + + // callback for success + authConnection.onload = function(e) + { + if(authConnection.status != 200) + { + console.log("token-check failed"); + errorFunc(); + } + }; + + // callback for fail + authConnection.onerror = function(e) + { + errorFunc(); + }; + + authConnection.send(null); +} diff --git a/src/frontend/Hanami-AI-Dashboard/src/libHanamiAiSdk/javascript/io.js b/src/frontend/Hanami-AI-Dashboard/src/libHanamiAiSdk/javascript/io.js new file mode 100644 index 00000000..e69de29b diff --git a/src/frontend/Hanami-AI-Dashboard/src/libHanamiAiSdk/javascript/logs.js b/src/frontend/Hanami-AI-Dashboard/src/libHanamiAiSdk/javascript/logs.js new file mode 100644 index 00000000..d132b63c --- /dev/null +++ b/src/frontend/Hanami-AI-Dashboard/src/libHanamiAiSdk/javascript/logs.js @@ -0,0 +1,31 @@ +// Apache License Version 2.0 + +// Copyright 2020 Tobias Anker + +// 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. + +function listAuditLogs_request(outputFunc, userId, page, token) +{ + let request = "/control/shiori/v1/audit_log?"; + if(userId !== "") { + request += "user_id=" + userId + "&"; + } + request += "page=" + page; + getObject_request(outputFunc, request, token); +} + +function listErrorLogs_request(outputFunc, userId, page, token) +{ + const request = "/control/shiori/v1/error_log?user_id=" + userId + "&page=" + page; + getObject_request(outputFunc, request, token); +} diff --git a/src/frontend/Hanami-AI-Dashboard/src/libHanamiAiSdk/javascript/measurements.js b/src/frontend/Hanami-AI-Dashboard/src/libHanamiAiSdk/javascript/measurements.js new file mode 100644 index 00000000..ceac9ce5 --- /dev/null +++ b/src/frontend/Hanami-AI-Dashboard/src/libHanamiAiSdk/javascript/measurements.js @@ -0,0 +1,33 @@ +// Apache License Version 2.0 + +// Copyright 2020 Tobias Anker + +// 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. + +function getPowerData_request(outputFunc, token) +{ + let request = "/control/azuki/v1/power_consumption"; + getObject_request(outputFunc, request, token); +} + +function getTemperatureData_request(outputFunc, token) +{ + let request = "/control/azuki/v1/temperature_production"; + getObject_request(outputFunc, request, token); +} + +function getSpeedData_request(outputFunc, token) +{ + let request = "/control/azuki/v1/speed"; + getObject_request(outputFunc, request, token); +} diff --git a/src/frontend/Hanami-AI-Dashboard/src/libHanamiAiSdk/javascript/project.js b/src/frontend/Hanami-AI-Dashboard/src/libHanamiAiSdk/javascript/project.js new file mode 100644 index 00000000..488e8fe8 --- /dev/null +++ b/src/frontend/Hanami-AI-Dashboard/src/libHanamiAiSdk/javascript/project.js @@ -0,0 +1,34 @@ +// Apache License Version 2.0 + +// Copyright 2020 Tobias Anker + +// 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. + +function createProject_request(outputFunc, projectId, name, token) +{ + const path = "/control/misaki/v1/project"; + const reqContent = "{\"id\":\"" + projectId + "\",\"name\":\"" + name + "\"}"; + createObject_request(outputFunc, path, reqContent, token); +} + +function listProjects_request(outputFunc, token) +{ + listObjects_request(outputFunc, "/control/misaki/v1/project/all", token); +} + +function deleteProject_request(postProcessFunc, projectId, token) +{ + const request = "/control/misaki/v1/project?id=" + projectId; + deleteObject_request(postProcessFunc, request, token); +} + diff --git a/src/frontend/Hanami-AI-Dashboard/src/libHanamiAiSdk/javascript/request_result.js b/src/frontend/Hanami-AI-Dashboard/src/libHanamiAiSdk/javascript/request_result.js new file mode 100644 index 00000000..62aaf779 --- /dev/null +++ b/src/frontend/Hanami-AI-Dashboard/src/libHanamiAiSdk/javascript/request_result.js @@ -0,0 +1,33 @@ +// Apache License Version 2.0 + +// Copyright 2020 Tobias Anker + +// 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. + +function getRequestResultData_request(outputFunc, resultUuid, token) +{ + const request = "/control/shiori/v1/request_result?uuid=" + resultUuid; + getObject_request(outputFunc, request, token); +} + +function listResults_request(outputFunc, token) +{ + listObjects_request(outputFunc, "/control/shiori/v1/request_result/all", token); +} + +function deleteResult_request(postProcessFunc, resultUuid, token) +{ + const request = "/control/shiori/v1/request_result?uuid=" + resultUuid; + deleteObject_request(postProcessFunc, request, token); +} + diff --git a/src/frontend/Hanami-AI-Dashboard/src/libHanamiAiSdk/javascript/snapshot.js b/src/frontend/Hanami-AI-Dashboard/src/libHanamiAiSdk/javascript/snapshot.js new file mode 100644 index 00000000..f1abaf46 --- /dev/null +++ b/src/frontend/Hanami-AI-Dashboard/src/libHanamiAiSdk/javascript/snapshot.js @@ -0,0 +1,27 @@ +// Apache License Version 2.0 + +// Copyright 2020 Tobias Anker + +// 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. + +function listClusterSnapshots_request(outputFunc, token) +{ + listObjects_request(outputFunc, "/control/shiori/v1/cluster_snapshot/all", token); +} + +function deleteClusterSnapshot_request(postProcessFunc, resultUuid, token) +{ + const request = "/control/shiori/v1/cluster_snapshot?uuid=" + resultUuid; + deleteObject_request(postProcessFunc, request, token); +} + diff --git a/src/frontend/Hanami-AI-Dashboard/src/libHanamiAiSdk/javascript/system_info.js b/src/frontend/Hanami-AI-Dashboard/src/libHanamiAiSdk/javascript/system_info.js new file mode 100644 index 00000000..69bdf2bb --- /dev/null +++ b/src/frontend/Hanami-AI-Dashboard/src/libHanamiAiSdk/javascript/system_info.js @@ -0,0 +1,28 @@ +// Apache License Version 2.0 + +// Copyright 2020 Tobias Anker + +// 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. + +function getSystemInfo_request(outputFunc, token) +{ + const request = "/control/azuki/v1/system_info"; + getObject_request(outputFunc, request, token); +} + +function getThreadMapping_request(outputFunc, token) +{ + const request = "/control/azuki/v1/threading"; + getObject_request(outputFunc, request, token); +} + diff --git a/src/frontend/Hanami-AI-Dashboard/src/libHanamiAiSdk/javascript/task.js b/src/frontend/Hanami-AI-Dashboard/src/libHanamiAiSdk/javascript/task.js new file mode 100644 index 00000000..d28b5cee --- /dev/null +++ b/src/frontend/Hanami-AI-Dashboard/src/libHanamiAiSdk/javascript/task.js @@ -0,0 +1,40 @@ +// Apache License Version 2.0 + +// Copyright 2020 Tobias Anker + +// 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. + +function createTask_request(outputFunc, name, type, clusterUuid, datasetUuid, token) +{ + let path = "/control/kyouko/v1/task"; + + // create request-content + var reqContent = "{\"name\":\"" + name + + "\",\"type\":\"" + type + + "\",\"cluster_uuid\":\"" + clusterUuid + + "\",\"data_set_uuid\":\"" + datasetUuid + "\"}"; + + createObject_request(outputFunc, path, reqContent, token); +} + +function listTasks_request(outputFunc, clusterUuid, token) +{ + const path = "/control/kyouko/v1/task/all?cluster_uuid=" + clusterUuid + listObjects_request(outputFunc, path, token); +} + +function deleteTask_request(outputFunc, taskUuid, clusterUuid, token) +{ + const request = "/control/kyouko/v1/task?cluster_uuid=" + clusterUuid + "&uuid=" + taskUuid; + deleteObject_request(outputFunc, request, token); +} diff --git a/src/frontend/Hanami-AI-Dashboard/src/libHanamiAiSdk/javascript/template.js b/src/frontend/Hanami-AI-Dashboard/src/libHanamiAiSdk/javascript/template.js new file mode 100644 index 00000000..3f89b461 --- /dev/null +++ b/src/frontend/Hanami-AI-Dashboard/src/libHanamiAiSdk/javascript/template.js @@ -0,0 +1,34 @@ +// Apache License Version 2.0 + +// Copyright 2020 Tobias Anker + +// 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. + +function createSegmentTemplaete_request(outputFunc, name, templateContent, token) +{ + const path = "/control/kyouko/v1/template/upload"; + const reqContent = "{\"name\":\"" + name + "\",\"template\":\"" + btoa(templateContent) + "\"}"; + createObject_request(outputFunc, path, reqContent, token); +} + +function listSegmentTemplaetes_request(outputFunc, token) +{ + listObjects_request(outputFunc, "/control/kyouko/v1/template/all", token); +} + +function deleteSegmentTemplaete_request(postProcessFunc, templateId, token) +{ + const request = "/control/kyouko/v1/template?uuid=" + templateId; + deleteObject_request(postProcessFunc, request, token); +} + diff --git a/src/frontend/Hanami-AI-Dashboard/src/libHanamiAiSdk/javascript/user.js b/src/frontend/Hanami-AI-Dashboard/src/libHanamiAiSdk/javascript/user.js new file mode 100644 index 00000000..0cf18a83 --- /dev/null +++ b/src/frontend/Hanami-AI-Dashboard/src/libHanamiAiSdk/javascript/user.js @@ -0,0 +1,55 @@ +// Apache License Version 2.0 + +// Copyright 2020 Tobias Anker + +// 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. + +function createUser_request(outputFunc, userId, name, password, isAdmin, token) +{ + const path = "/control/misaki/v1/user"; + let reqContent = "{id:\"" + userId; + reqContent += "\",name:\"" + name; + reqContent += "\",password:\"" + password; + reqContent += "\",is_admin:" + isAdmin + "}"; + createObject_request(outputFunc, path, reqContent, token); +} + +function addProjectToUser_request(outputFunc, userId, projectId, role, isProjectAdmin, token) +{ + const path = "/control/misaki/v1/user/project"; + let reqContent = "{id:\"" + userId; + reqContent += "\",project_id:\"" + projectId; + reqContent += "\",role:\"" + role; + reqContent += "\",is_project_admin:" + isProjectAdmin + "}"; + createObject_request(outputFunc, path, reqContent, token); +} + +function removeProjectFromUser_request(outputFunc, userId, projectId, token) +{ + let path = "/control/misaki/v1/user/project"; + path += "?project_id=" + projectId; + path += "&id=" + userId; + deleteObject_request(outputFunc, path, token); +} + +function listUsers_request(outputFunc, token) +{ + listObjects_request(outputFunc, "/control/misaki/v1/user/all", token); +} + +function deleteUser_request(postProcessFunc, userId, token) +{ + const request = "/control/misaki/v1/user?id=" + userId; + deleteObject_request(postProcessFunc, request, token); +} + diff --git a/src/frontend/Hanami-AI-Dashboard/src/libKitsunemimiHanamiMessages/LICENSE b/src/frontend/Hanami-AI-Dashboard/src/libKitsunemimiHanamiMessages/LICENSE new file mode 100644 index 00000000..fabcefe9 --- /dev/null +++ b/src/frontend/Hanami-AI-Dashboard/src/libKitsunemimiHanamiMessages/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright 2022 Tobias Anker + + 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. diff --git a/src/frontend/Hanami-AI-Dashboard/src/libKitsunemimiHanamiMessages/README.md b/src/frontend/Hanami-AI-Dashboard/src/libKitsunemimiHanamiMessages/README.md new file mode 100644 index 00000000..24adf25d --- /dev/null +++ b/src/frontend/Hanami-AI-Dashboard/src/libKitsunemimiHanamiMessages/README.md @@ -0,0 +1 @@ +# libKitsunemimiHanamiMessages diff --git a/src/frontend/Hanami-AI-Dashboard/src/libKitsunemimiHanamiMessages/hanami_messages/kyouko_messages.h b/src/frontend/Hanami-AI-Dashboard/src/libKitsunemimiHanamiMessages/hanami_messages/kyouko_messages.h new file mode 100644 index 00000000..a1c29d54 --- /dev/null +++ b/src/frontend/Hanami-AI-Dashboard/src/libKitsunemimiHanamiMessages/hanami_messages/kyouko_messages.h @@ -0,0 +1,148 @@ +/** + * @file hanami_messages.h + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2021 Tobias Anker + * + * 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. + */ + +#ifndef KYOUKO_HANAMIMESSAGES_H +#define KYOUKO_HANAMIMESSAGES_H + +#include +#include + +namespace Kitsunemimi +{ +namespace Hanami +{ + +//================================================================================================== + +class ClusterIO_Message + : public HanamiMessage +{ +public: + /** + * @brief constructor + */ + ClusterIO_Message() + { + m_type = 1; + } + + /** + * @brief destructor + */ + ~ClusterIO_Message() {} + + enum ProcessType + { + REQUEST_TYPE = 0, + LEARN_TYPE = 1, + }; + + enum DataType + { + INPUT_TYPE = 0, + SHOULD_TYPE = 1, + OUTPUT_TYPE = 2, + }; + + std::string segmentName = ""; + uint64_t processType = REQUEST_TYPE; + uint64_t dataType = INPUT_TYPE; + bool isLast = false; + float* values = nullptr; + uint64_t numberOfValues = 0; + + /** + * @brief read message from byte-array + * + * @param data pointer to byte-buffer + * @param dataSize number of bytes + * + * @return true, if message was valid, else false + */ + bool + read(void* data, const uint64_t dataSize) + { + if(initRead(data, dataSize) == false) { + return false; + } + + if(readString(data, segmentName) == false) { + return false; + } + if(readUint(data, processType) == false) { + return false; + } + if(readUint(data, dataType) == false) { + return false; + } + if(readBool(data, isLast) == false) { + return false; + } + if(readFloatList(data, &values, numberOfValues) == false) { + return false; + } + + return true; + } + + /** + * @brief convert message to byte-array + * + * @param result reference to the data-buffer to returning the final byte-array + * + * @return 0, if data are too big for the provided buffer, else number of used bytes + */ + uint64_t + createBlob(uint8_t* result, const uint64_t bufferSize) + { + const uint64_t totalMsgSize = sizeof(MessageHeader) + + 5 * sizeof(Entry) + + segmentName.size() + + sizeof(uint64_t) + + sizeof(uint64_t) + + sizeof(bool) + + numberOfValues * sizeof(float); + + if(bufferSize < totalMsgSize) { + return 0; + } + + uint64_t pos = 0; + pos += initBlob(&result[pos], totalMsgSize); + pos += appendString(&result[pos], segmentName); + pos += appendUint(&result[pos], processType); + pos += appendUint(&result[pos], dataType); + pos += appendBool(&result[pos], isLast); + pos += appendFloatList(&result[pos], values, numberOfValues); + + assert(pos == totalMsgSize); + + return pos; + } +}; + +//================================================================================================== + +} // namespace Hanami +} // namespace Kitsunemimi + +#endif // KYOUKO_HANAMIMESSAGES_H diff --git a/src/frontend/Hanami-AI-Dashboard/src/libKitsunemimiHanamiMessages/hanami_messages/shiori_messages.h b/src/frontend/Hanami-AI-Dashboard/src/libKitsunemimiHanamiMessages/hanami_messages/shiori_messages.h new file mode 100644 index 00000000..4a50114f --- /dev/null +++ b/src/frontend/Hanami-AI-Dashboard/src/libKitsunemimiHanamiMessages/hanami_messages/shiori_messages.h @@ -0,0 +1,147 @@ +/** + * @file hanami_messages.h + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2021 Tobias Anker + * + * 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. + */ + +#ifndef SHIORI_HANAMIMESSAGES_H +#define SHIORI_HANAMIMESSAGES_H + +#include +#include + +namespace Kitsunemimi +{ +namespace Hanami +{ + +//================================================================================================== + +class FileUpload_Message + : public HanamiMessage +{ +public: + /** + * @brief constructor + */ + FileUpload_Message() + { + m_type = 1; + } + + /** + * @brief destructor + */ + ~FileUpload_Message() {} + + enum UploadDataType + { + DATASET_TYPE = 0, + CLUSTER_SNAPSHOT_TYPE = 1 + }; + + std::string datasetUuid = ""; + std::string fileUuid = ""; + uint64_t type = DATASET_TYPE; + bool isLast = false; + uint64_t position = 0; + void* payload = nullptr; + uint64_t numberOfBytes = 0; + + /** + * @brief read message from byte-array + * + * @param data pointer to byte-buffer + * @param dataSize number of bytes + * + * @return true, if message was valid, else false + */ + bool + read(void* data, const uint64_t dataSize) + { + if(initRead(data, dataSize) == false) { + return false; + } + + if(readString(data, datasetUuid) == false) { + return false; + } + if(readString(data, fileUuid) == false) { + return false; + } + if(readUint(data, type) == false) { + return false; + } + if(readBool(data, isLast) == false) { + return false; + } + if(readUint(data, position) == false) { + return false; + } + if(readBinary(data, &payload, numberOfBytes) == false) { + return false; + } + + return true; + } + + /** + * @brief convert message to byte-array + * + * @param result reference to the data-buffer to returning the final byte-array + * + * @return 0, if data are too big for the provided buffer, else number of used bytes + */ + uint64_t + createBlob(uint8_t* result, const uint64_t bufferSize) + { + const uint64_t totalMsgSize = sizeof(MessageHeader) + + 6 * sizeof(Entry) + + datasetUuid.size() + + fileUuid.size() + + sizeof(uint64_t) + + sizeof(bool) + + sizeof(uint64_t) + + numberOfBytes; + + if(bufferSize < totalMsgSize) { + return 0; + } + + uint64_t pos = 0; + pos += initBlob(&result[pos], totalMsgSize); + pos += appendString(&result[pos], datasetUuid); + pos += appendString(&result[pos], fileUuid); + pos += appendUint(&result[pos], type); + pos += appendBool(&result[pos], isLast); + pos += appendUint(&result[pos], position); + pos += appendData(&result[pos], payload, numberOfBytes); + + assert(pos == totalMsgSize); + + return pos; + } +}; + +//================================================================================================== + +} // namespace Hanami +} // namespace Kitsunemimi + +#endif // SHIORI_HANAMIMESSAGES_H diff --git a/src/frontend/Hanami-AI-Dashboard/src/libKitsunemimiHanamiMessages/protobuffers/kyouko_messages.proto3 b/src/frontend/Hanami-AI-Dashboard/src/libKitsunemimiHanamiMessages/protobuffers/kyouko_messages.proto3 new file mode 100644 index 00000000..cefcb8cb --- /dev/null +++ b/src/frontend/Hanami-AI-Dashboard/src/libKitsunemimiHanamiMessages/protobuffers/kyouko_messages.proto3 @@ -0,0 +1,22 @@ +syntax = "proto3"; + +enum ClusterProcessType { + REQUEST_TYPE = 0; + LEARN_TYPE = 1; +} + +enum ClusterDataType { + INPUT_TYPE = 0; + SHOULD_TYPE = 1; + OUTPUT_TYPE = 2; +} + +message ClusterIO_Message { + string segmentName = 1; + bool isLast = 2; + ClusterProcessType processType = 3; + ClusterDataType dataType = 4; + uint64 numberOfValues = 5; + repeated float values = 6 [packed=true]; +} + diff --git a/src/frontend/Hanami-AI-Dashboard/src/libKitsunemimiHanamiMessages/protobuffers/kyouko_messages.proto3.pb.cc b/src/frontend/Hanami-AI-Dashboard/src/libKitsunemimiHanamiMessages/protobuffers/kyouko_messages.proto3.pb.cc new file mode 100644 index 00000000..d6dea819 --- /dev/null +++ b/src/frontend/Hanami-AI-Dashboard/src/libKitsunemimiHanamiMessages/protobuffers/kyouko_messages.proto3.pb.cc @@ -0,0 +1,475 @@ +// Generated by the protocol buffer compiler. DO NOT EDIT! +// source: kyouko_messages.proto3 + +#include "kyouko_messages.proto3.pb.h" + +#include + +#include +#include +#include +#include +#include +#include +#include +// @@protoc_insertion_point(includes) +#include +class ClusterIO_MessageDefaultTypeInternal { + public: + ::PROTOBUF_NAMESPACE_ID::internal::ExplicitlyConstructed _instance; +} _ClusterIO_Message_default_instance_; +static void InitDefaultsscc_info_ClusterIO_Message_kyouko_5fmessages_2eproto3() { + GOOGLE_PROTOBUF_VERIFY_VERSION; + + { + void* ptr = &::_ClusterIO_Message_default_instance_; + new (ptr) ::ClusterIO_Message(); + ::PROTOBUF_NAMESPACE_ID::internal::OnShutdownDestroyMessage(ptr); + } + ::ClusterIO_Message::InitAsDefaultInstance(); +} + +::PROTOBUF_NAMESPACE_ID::internal::SCCInfo<0> scc_info_ClusterIO_Message_kyouko_5fmessages_2eproto3 = + {{ATOMIC_VAR_INIT(::PROTOBUF_NAMESPACE_ID::internal::SCCInfoBase::kUninitialized), 0, 0, InitDefaultsscc_info_ClusterIO_Message_kyouko_5fmessages_2eproto3}, {}}; + +static ::PROTOBUF_NAMESPACE_ID::Metadata file_level_metadata_kyouko_5fmessages_2eproto3[1]; +static const ::PROTOBUF_NAMESPACE_ID::EnumDescriptor* file_level_enum_descriptors_kyouko_5fmessages_2eproto3[2]; +static constexpr ::PROTOBUF_NAMESPACE_ID::ServiceDescriptor const** file_level_service_descriptors_kyouko_5fmessages_2eproto3 = nullptr; + +const ::PROTOBUF_NAMESPACE_ID::uint32 TableStruct_kyouko_5fmessages_2eproto3::offsets[] PROTOBUF_SECTION_VARIABLE(protodesc_cold) = { + ~0u, // no _has_bits_ + PROTOBUF_FIELD_OFFSET(::ClusterIO_Message, _internal_metadata_), + ~0u, // no _extensions_ + ~0u, // no _oneof_case_ + ~0u, // no _weak_field_map_ + PROTOBUF_FIELD_OFFSET(::ClusterIO_Message, segmentname_), + PROTOBUF_FIELD_OFFSET(::ClusterIO_Message, islast_), + PROTOBUF_FIELD_OFFSET(::ClusterIO_Message, processtype_), + PROTOBUF_FIELD_OFFSET(::ClusterIO_Message, datatype_), + PROTOBUF_FIELD_OFFSET(::ClusterIO_Message, numberofvalues_), + PROTOBUF_FIELD_OFFSET(::ClusterIO_Message, values_), +}; +static const ::PROTOBUF_NAMESPACE_ID::internal::MigrationSchema schemas[] PROTOBUF_SECTION_VARIABLE(protodesc_cold) = { + { 0, -1, sizeof(::ClusterIO_Message)}, +}; + +static ::PROTOBUF_NAMESPACE_ID::Message const * const file_default_instances[] = { + reinterpret_cast(&::_ClusterIO_Message_default_instance_), +}; + +const char descriptor_table_protodef_kyouko_5fmessages_2eproto3[] PROTOBUF_SECTION_VARIABLE(protodesc_cold) = + "\n\026kyouko_messages.proto3\"\262\001\n\021ClusterIO_M" + "essage\022\023\n\013segmentName\030\001 \001(\t\022\016\n\006isLast\030\002 " + "\001(\010\022(\n\013processType\030\003 \001(\0162\023.ClusterProces" + "sType\022\"\n\010dataType\030\004 \001(\0162\020.ClusterDataTyp" + "e\022\026\n\016numberOfValues\030\005 \001(\004\022\022\n\006values\030\006 \003(" + "\002B\002\020\001*6\n\022ClusterProcessType\022\020\n\014REQUEST_T" + "YPE\020\000\022\016\n\nLEARN_TYPE\020\001*C\n\017ClusterDataType" + "\022\016\n\nINPUT_TYPE\020\000\022\017\n\013SHOULD_TYPE\020\001\022\017\n\013OUT" + "PUT_TYPE\020\002b\006proto3" + ; +static const ::PROTOBUF_NAMESPACE_ID::internal::DescriptorTable*const descriptor_table_kyouko_5fmessages_2eproto3_deps[1] = { +}; +static ::PROTOBUF_NAMESPACE_ID::internal::SCCInfoBase*const descriptor_table_kyouko_5fmessages_2eproto3_sccs[1] = { + &scc_info_ClusterIO_Message_kyouko_5fmessages_2eproto3.base, +}; +static ::PROTOBUF_NAMESPACE_ID::internal::once_flag descriptor_table_kyouko_5fmessages_2eproto3_once; +const ::PROTOBUF_NAMESPACE_ID::internal::DescriptorTable descriptor_table_kyouko_5fmessages_2eproto3 = { + false, false, descriptor_table_protodef_kyouko_5fmessages_2eproto3, "kyouko_messages.proto3", 338, + &descriptor_table_kyouko_5fmessages_2eproto3_once, descriptor_table_kyouko_5fmessages_2eproto3_sccs, descriptor_table_kyouko_5fmessages_2eproto3_deps, 1, 0, + schemas, file_default_instances, TableStruct_kyouko_5fmessages_2eproto3::offsets, + file_level_metadata_kyouko_5fmessages_2eproto3, 1, file_level_enum_descriptors_kyouko_5fmessages_2eproto3, file_level_service_descriptors_kyouko_5fmessages_2eproto3, +}; + +// Force running AddDescriptors() at dynamic initialization time. +static bool dynamic_init_dummy_kyouko_5fmessages_2eproto3 = (static_cast(::PROTOBUF_NAMESPACE_ID::internal::AddDescriptors(&descriptor_table_kyouko_5fmessages_2eproto3)), true); +const ::PROTOBUF_NAMESPACE_ID::EnumDescriptor* ClusterProcessType_descriptor() { + ::PROTOBUF_NAMESPACE_ID::internal::AssignDescriptors(&descriptor_table_kyouko_5fmessages_2eproto3); + return file_level_enum_descriptors_kyouko_5fmessages_2eproto3[0]; +} +bool ClusterProcessType_IsValid(int value) { + switch (value) { + case 0: + case 1: + return true; + default: + return false; + } +} + +const ::PROTOBUF_NAMESPACE_ID::EnumDescriptor* ClusterDataType_descriptor() { + ::PROTOBUF_NAMESPACE_ID::internal::AssignDescriptors(&descriptor_table_kyouko_5fmessages_2eproto3); + return file_level_enum_descriptors_kyouko_5fmessages_2eproto3[1]; +} +bool ClusterDataType_IsValid(int value) { + switch (value) { + case 0: + case 1: + case 2: + return true; + default: + return false; + } +} + + +// =================================================================== + +void ClusterIO_Message::InitAsDefaultInstance() { +} +class ClusterIO_Message::_Internal { + public: +}; + +ClusterIO_Message::ClusterIO_Message(::PROTOBUF_NAMESPACE_ID::Arena* arena) + : ::PROTOBUF_NAMESPACE_ID::Message(arena), + values_(arena) { + SharedCtor(); + RegisterArenaDtor(arena); + // @@protoc_insertion_point(arena_constructor:ClusterIO_Message) +} +ClusterIO_Message::ClusterIO_Message(const ClusterIO_Message& from) + : ::PROTOBUF_NAMESPACE_ID::Message(), + values_(from.values_) { + _internal_metadata_.MergeFrom<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(from._internal_metadata_); + segmentname_.UnsafeSetDefault(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); + if (!from._internal_segmentname().empty()) { + segmentname_.Set(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), from._internal_segmentname(), + GetArena()); + } + ::memcpy(&islast_, &from.islast_, + static_cast(reinterpret_cast(&datatype_) - + reinterpret_cast(&islast_)) + sizeof(datatype_)); + // @@protoc_insertion_point(copy_constructor:ClusterIO_Message) +} + +void ClusterIO_Message::SharedCtor() { + ::PROTOBUF_NAMESPACE_ID::internal::InitSCC(&scc_info_ClusterIO_Message_kyouko_5fmessages_2eproto3.base); + segmentname_.UnsafeSetDefault(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); + ::memset(&islast_, 0, static_cast( + reinterpret_cast(&datatype_) - + reinterpret_cast(&islast_)) + sizeof(datatype_)); +} + +ClusterIO_Message::~ClusterIO_Message() { + // @@protoc_insertion_point(destructor:ClusterIO_Message) + SharedDtor(); + _internal_metadata_.Delete<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(); +} + +void ClusterIO_Message::SharedDtor() { + GOOGLE_DCHECK(GetArena() == nullptr); + segmentname_.DestroyNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); +} + +void ClusterIO_Message::ArenaDtor(void* object) { + ClusterIO_Message* _this = reinterpret_cast< ClusterIO_Message* >(object); + (void)_this; +} +void ClusterIO_Message::RegisterArenaDtor(::PROTOBUF_NAMESPACE_ID::Arena*) { +} +void ClusterIO_Message::SetCachedSize(int size) const { + _cached_size_.Set(size); +} +const ClusterIO_Message& ClusterIO_Message::default_instance() { + ::PROTOBUF_NAMESPACE_ID::internal::InitSCC(&::scc_info_ClusterIO_Message_kyouko_5fmessages_2eproto3.base); + return *internal_default_instance(); +} + + +void ClusterIO_Message::Clear() { +// @@protoc_insertion_point(message_clear_start:ClusterIO_Message) + ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0; + // Prevent compiler warnings about cached_has_bits being unused + (void) cached_has_bits; + + values_.Clear(); + segmentname_.ClearToEmpty(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), GetArena()); + ::memset(&islast_, 0, static_cast( + reinterpret_cast(&datatype_) - + reinterpret_cast(&islast_)) + sizeof(datatype_)); + _internal_metadata_.Clear<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(); +} + +const char* ClusterIO_Message::_InternalParse(const char* ptr, ::PROTOBUF_NAMESPACE_ID::internal::ParseContext* ctx) { +#define CHK_(x) if (PROTOBUF_PREDICT_FALSE(!(x))) goto failure + ::PROTOBUF_NAMESPACE_ID::Arena* arena = GetArena(); (void)arena; + while (!ctx->Done(&ptr)) { + ::PROTOBUF_NAMESPACE_ID::uint32 tag; + ptr = ::PROTOBUF_NAMESPACE_ID::internal::ReadTag(ptr, &tag); + CHK_(ptr); + switch (tag >> 3) { + // string segmentName = 1; + case 1: + if (PROTOBUF_PREDICT_TRUE(static_cast<::PROTOBUF_NAMESPACE_ID::uint8>(tag) == 10)) { + auto str = _internal_mutable_segmentname(); + ptr = ::PROTOBUF_NAMESPACE_ID::internal::InlineGreedyStringParser(str, ptr, ctx); + CHK_(::PROTOBUF_NAMESPACE_ID::internal::VerifyUTF8(str, "ClusterIO_Message.segmentName")); + CHK_(ptr); + } else goto handle_unusual; + continue; + // bool isLast = 2; + case 2: + if (PROTOBUF_PREDICT_TRUE(static_cast<::PROTOBUF_NAMESPACE_ID::uint8>(tag) == 16)) { + islast_ = ::PROTOBUF_NAMESPACE_ID::internal::ReadVarint64(&ptr); + CHK_(ptr); + } else goto handle_unusual; + continue; + // .ClusterProcessType processType = 3; + case 3: + if (PROTOBUF_PREDICT_TRUE(static_cast<::PROTOBUF_NAMESPACE_ID::uint8>(tag) == 24)) { + ::PROTOBUF_NAMESPACE_ID::uint64 val = ::PROTOBUF_NAMESPACE_ID::internal::ReadVarint64(&ptr); + CHK_(ptr); + _internal_set_processtype(static_cast<::ClusterProcessType>(val)); + } else goto handle_unusual; + continue; + // .ClusterDataType dataType = 4; + case 4: + if (PROTOBUF_PREDICT_TRUE(static_cast<::PROTOBUF_NAMESPACE_ID::uint8>(tag) == 32)) { + ::PROTOBUF_NAMESPACE_ID::uint64 val = ::PROTOBUF_NAMESPACE_ID::internal::ReadVarint64(&ptr); + CHK_(ptr); + _internal_set_datatype(static_cast<::ClusterDataType>(val)); + } else goto handle_unusual; + continue; + // uint64 numberOfValues = 5; + case 5: + if (PROTOBUF_PREDICT_TRUE(static_cast<::PROTOBUF_NAMESPACE_ID::uint8>(tag) == 40)) { + numberofvalues_ = ::PROTOBUF_NAMESPACE_ID::internal::ReadVarint64(&ptr); + CHK_(ptr); + } else goto handle_unusual; + continue; + // repeated float values = 6 [packed = true]; + case 6: + if (PROTOBUF_PREDICT_TRUE(static_cast<::PROTOBUF_NAMESPACE_ID::uint8>(tag) == 50)) { + ptr = ::PROTOBUF_NAMESPACE_ID::internal::PackedFloatParser(_internal_mutable_values(), ptr, ctx); + CHK_(ptr); + } else if (static_cast<::PROTOBUF_NAMESPACE_ID::uint8>(tag) == 53) { + _internal_add_values(::PROTOBUF_NAMESPACE_ID::internal::UnalignedLoad(ptr)); + ptr += sizeof(float); + } else goto handle_unusual; + continue; + default: { + handle_unusual: + if ((tag & 7) == 4 || tag == 0) { + ctx->SetLastTag(tag); + goto success; + } + ptr = UnknownFieldParse(tag, + _internal_metadata_.mutable_unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(), + ptr, ctx); + CHK_(ptr != nullptr); + continue; + } + } // switch + } // while +success: + return ptr; +failure: + ptr = nullptr; + goto success; +#undef CHK_ +} + +::PROTOBUF_NAMESPACE_ID::uint8* ClusterIO_Message::_InternalSerialize( + ::PROTOBUF_NAMESPACE_ID::uint8* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const { + // @@protoc_insertion_point(serialize_to_array_start:ClusterIO_Message) + ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0; + (void) cached_has_bits; + + // string segmentName = 1; + if (this->segmentname().size() > 0) { + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::VerifyUtf8String( + this->_internal_segmentname().data(), static_cast(this->_internal_segmentname().length()), + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::SERIALIZE, + "ClusterIO_Message.segmentName"); + target = stream->WriteStringMaybeAliased( + 1, this->_internal_segmentname(), target); + } + + // bool isLast = 2; + if (this->islast() != 0) { + target = stream->EnsureSpace(target); + target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteBoolToArray(2, this->_internal_islast(), target); + } + + // .ClusterProcessType processType = 3; + if (this->processtype() != 0) { + target = stream->EnsureSpace(target); + target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteEnumToArray( + 3, this->_internal_processtype(), target); + } + + // .ClusterDataType dataType = 4; + if (this->datatype() != 0) { + target = stream->EnsureSpace(target); + target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteEnumToArray( + 4, this->_internal_datatype(), target); + } + + // uint64 numberOfValues = 5; + if (this->numberofvalues() != 0) { + target = stream->EnsureSpace(target); + target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteUInt64ToArray(5, this->_internal_numberofvalues(), target); + } + + // repeated float values = 6 [packed = true]; + if (this->_internal_values_size() > 0) { + target = stream->WriteFixedPacked(6, _internal_values(), target); + } + + if (PROTOBUF_PREDICT_FALSE(_internal_metadata_.have_unknown_fields())) { + target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormat::InternalSerializeUnknownFieldsToArray( + _internal_metadata_.unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(::PROTOBUF_NAMESPACE_ID::UnknownFieldSet::default_instance), target, stream); + } + // @@protoc_insertion_point(serialize_to_array_end:ClusterIO_Message) + return target; +} + +size_t ClusterIO_Message::ByteSizeLong() const { +// @@protoc_insertion_point(message_byte_size_start:ClusterIO_Message) + size_t total_size = 0; + + ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0; + // Prevent compiler warnings about cached_has_bits being unused + (void) cached_has_bits; + + // repeated float values = 6 [packed = true]; + { + unsigned int count = static_cast(this->_internal_values_size()); + size_t data_size = 4UL * count; + if (data_size > 0) { + total_size += 1 + + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::Int32Size( + static_cast<::PROTOBUF_NAMESPACE_ID::int32>(data_size)); + } + int cached_size = ::PROTOBUF_NAMESPACE_ID::internal::ToCachedSize(data_size); + _values_cached_byte_size_.store(cached_size, + std::memory_order_relaxed); + total_size += data_size; + } + + // string segmentName = 1; + if (this->segmentname().size() > 0) { + total_size += 1 + + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::StringSize( + this->_internal_segmentname()); + } + + // bool isLast = 2; + if (this->islast() != 0) { + total_size += 1 + 1; + } + + // .ClusterProcessType processType = 3; + if (this->processtype() != 0) { + total_size += 1 + + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::EnumSize(this->_internal_processtype()); + } + + // uint64 numberOfValues = 5; + if (this->numberofvalues() != 0) { + total_size += 1 + + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::UInt64Size( + this->_internal_numberofvalues()); + } + + // .ClusterDataType dataType = 4; + if (this->datatype() != 0) { + total_size += 1 + + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::EnumSize(this->_internal_datatype()); + } + + if (PROTOBUF_PREDICT_FALSE(_internal_metadata_.have_unknown_fields())) { + return ::PROTOBUF_NAMESPACE_ID::internal::ComputeUnknownFieldsSize( + _internal_metadata_, total_size, &_cached_size_); + } + int cached_size = ::PROTOBUF_NAMESPACE_ID::internal::ToCachedSize(total_size); + SetCachedSize(cached_size); + return total_size; +} + +void ClusterIO_Message::MergeFrom(const ::PROTOBUF_NAMESPACE_ID::Message& from) { +// @@protoc_insertion_point(generalized_merge_from_start:ClusterIO_Message) + GOOGLE_DCHECK_NE(&from, this); + const ClusterIO_Message* source = + ::PROTOBUF_NAMESPACE_ID::DynamicCastToGenerated( + &from); + if (source == nullptr) { + // @@protoc_insertion_point(generalized_merge_from_cast_fail:ClusterIO_Message) + ::PROTOBUF_NAMESPACE_ID::internal::ReflectionOps::Merge(from, this); + } else { + // @@protoc_insertion_point(generalized_merge_from_cast_success:ClusterIO_Message) + MergeFrom(*source); + } +} + +void ClusterIO_Message::MergeFrom(const ClusterIO_Message& from) { +// @@protoc_insertion_point(class_specific_merge_from_start:ClusterIO_Message) + GOOGLE_DCHECK_NE(&from, this); + _internal_metadata_.MergeFrom<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(from._internal_metadata_); + ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0; + (void) cached_has_bits; + + values_.MergeFrom(from.values_); + if (from.segmentname().size() > 0) { + _internal_set_segmentname(from._internal_segmentname()); + } + if (from.islast() != 0) { + _internal_set_islast(from._internal_islast()); + } + if (from.processtype() != 0) { + _internal_set_processtype(from._internal_processtype()); + } + if (from.numberofvalues() != 0) { + _internal_set_numberofvalues(from._internal_numberofvalues()); + } + if (from.datatype() != 0) { + _internal_set_datatype(from._internal_datatype()); + } +} + +void ClusterIO_Message::CopyFrom(const ::PROTOBUF_NAMESPACE_ID::Message& from) { +// @@protoc_insertion_point(generalized_copy_from_start:ClusterIO_Message) + if (&from == this) return; + Clear(); + MergeFrom(from); +} + +void ClusterIO_Message::CopyFrom(const ClusterIO_Message& from) { +// @@protoc_insertion_point(class_specific_copy_from_start:ClusterIO_Message) + if (&from == this) return; + Clear(); + MergeFrom(from); +} + +bool ClusterIO_Message::IsInitialized() const { + return true; +} + +void ClusterIO_Message::InternalSwap(ClusterIO_Message* other) { + using std::swap; + _internal_metadata_.Swap<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(&other->_internal_metadata_); + values_.InternalSwap(&other->values_); + segmentname_.Swap(&other->segmentname_, &::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), GetArena()); + ::PROTOBUF_NAMESPACE_ID::internal::memswap< + PROTOBUF_FIELD_OFFSET(ClusterIO_Message, datatype_) + + sizeof(ClusterIO_Message::datatype_) + - PROTOBUF_FIELD_OFFSET(ClusterIO_Message, islast_)>( + reinterpret_cast(&islast_), + reinterpret_cast(&other->islast_)); +} + +::PROTOBUF_NAMESPACE_ID::Metadata ClusterIO_Message::GetMetadata() const { + return GetMetadataStatic(); +} + + +// @@protoc_insertion_point(namespace_scope) +PROTOBUF_NAMESPACE_OPEN +template<> PROTOBUF_NOINLINE ::ClusterIO_Message* Arena::CreateMaybeMessage< ::ClusterIO_Message >(Arena* arena) { + return Arena::CreateMessageInternal< ::ClusterIO_Message >(arena); +} +PROTOBUF_NAMESPACE_CLOSE + +// @@protoc_insertion_point(global_scope) +#include diff --git a/src/frontend/Hanami-AI-Dashboard/src/libKitsunemimiHanamiMessages/protobuffers/kyouko_messages.proto3.pb.h b/src/frontend/Hanami-AI-Dashboard/src/libKitsunemimiHanamiMessages/protobuffers/kyouko_messages.proto3.pb.h new file mode 100644 index 00000000..7f96ed62 --- /dev/null +++ b/src/frontend/Hanami-AI-Dashboard/src/libKitsunemimiHanamiMessages/protobuffers/kyouko_messages.proto3.pb.h @@ -0,0 +1,582 @@ +// Generated by the protocol buffer compiler. DO NOT EDIT! +// source: kyouko_messages.proto3 + +#ifndef GOOGLE_PROTOBUF_INCLUDED_kyouko_5fmessages_2eproto3 +#define GOOGLE_PROTOBUF_INCLUDED_kyouko_5fmessages_2eproto3 + +#include +#include + +#include +#if PROTOBUF_VERSION < 3012000 +#error This file was generated by a newer version of protoc which is +#error incompatible with your Protocol Buffer headers. Please update +#error your headers. +#endif +#if 3012004 < PROTOBUF_MIN_PROTOC_VERSION +#error This file was generated by an older version of protoc which is +#error incompatible with your Protocol Buffer headers. Please +#error regenerate this file with a newer version of protoc. +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include // IWYU pragma: export +#include // IWYU pragma: export +#include +#include +// @@protoc_insertion_point(includes) +#include +#define PROTOBUF_INTERNAL_EXPORT_kyouko_5fmessages_2eproto3 +PROTOBUF_NAMESPACE_OPEN +namespace internal { +class AnyMetadata; +} // namespace internal +PROTOBUF_NAMESPACE_CLOSE + +// Internal implementation detail -- do not use these members. +struct TableStruct_kyouko_5fmessages_2eproto3 { + static const ::PROTOBUF_NAMESPACE_ID::internal::ParseTableField entries[] + PROTOBUF_SECTION_VARIABLE(protodesc_cold); + static const ::PROTOBUF_NAMESPACE_ID::internal::AuxillaryParseTableField aux[] + PROTOBUF_SECTION_VARIABLE(protodesc_cold); + static const ::PROTOBUF_NAMESPACE_ID::internal::ParseTable schema[1] + PROTOBUF_SECTION_VARIABLE(protodesc_cold); + static const ::PROTOBUF_NAMESPACE_ID::internal::FieldMetadata field_metadata[]; + static const ::PROTOBUF_NAMESPACE_ID::internal::SerializationTable serialization_table[]; + static const ::PROTOBUF_NAMESPACE_ID::uint32 offsets[]; +}; +extern const ::PROTOBUF_NAMESPACE_ID::internal::DescriptorTable descriptor_table_kyouko_5fmessages_2eproto3; +class ClusterIO_Message; +class ClusterIO_MessageDefaultTypeInternal; +extern ClusterIO_MessageDefaultTypeInternal _ClusterIO_Message_default_instance_; +PROTOBUF_NAMESPACE_OPEN +template<> ::ClusterIO_Message* Arena::CreateMaybeMessage<::ClusterIO_Message>(Arena*); +PROTOBUF_NAMESPACE_CLOSE + +enum ClusterProcessType : int { + REQUEST_TYPE = 0, + LEARN_TYPE = 1, + ClusterProcessType_INT_MIN_SENTINEL_DO_NOT_USE_ = std::numeric_limits<::PROTOBUF_NAMESPACE_ID::int32>::min(), + ClusterProcessType_INT_MAX_SENTINEL_DO_NOT_USE_ = std::numeric_limits<::PROTOBUF_NAMESPACE_ID::int32>::max() +}; +bool ClusterProcessType_IsValid(int value); +constexpr ClusterProcessType ClusterProcessType_MIN = REQUEST_TYPE; +constexpr ClusterProcessType ClusterProcessType_MAX = LEARN_TYPE; +constexpr int ClusterProcessType_ARRAYSIZE = ClusterProcessType_MAX + 1; + +const ::PROTOBUF_NAMESPACE_ID::EnumDescriptor* ClusterProcessType_descriptor(); +template +inline const std::string& ClusterProcessType_Name(T enum_t_value) { + static_assert(::std::is_same::value || + ::std::is_integral::value, + "Incorrect type passed to function ClusterProcessType_Name."); + return ::PROTOBUF_NAMESPACE_ID::internal::NameOfEnum( + ClusterProcessType_descriptor(), enum_t_value); +} +inline bool ClusterProcessType_Parse( + const std::string& name, ClusterProcessType* value) { + return ::PROTOBUF_NAMESPACE_ID::internal::ParseNamedEnum( + ClusterProcessType_descriptor(), name, value); +} +enum ClusterDataType : int { + INPUT_TYPE = 0, + SHOULD_TYPE = 1, + OUTPUT_TYPE = 2, + ClusterDataType_INT_MIN_SENTINEL_DO_NOT_USE_ = std::numeric_limits<::PROTOBUF_NAMESPACE_ID::int32>::min(), + ClusterDataType_INT_MAX_SENTINEL_DO_NOT_USE_ = std::numeric_limits<::PROTOBUF_NAMESPACE_ID::int32>::max() +}; +bool ClusterDataType_IsValid(int value); +constexpr ClusterDataType ClusterDataType_MIN = INPUT_TYPE; +constexpr ClusterDataType ClusterDataType_MAX = OUTPUT_TYPE; +constexpr int ClusterDataType_ARRAYSIZE = ClusterDataType_MAX + 1; + +const ::PROTOBUF_NAMESPACE_ID::EnumDescriptor* ClusterDataType_descriptor(); +template +inline const std::string& ClusterDataType_Name(T enum_t_value) { + static_assert(::std::is_same::value || + ::std::is_integral::value, + "Incorrect type passed to function ClusterDataType_Name."); + return ::PROTOBUF_NAMESPACE_ID::internal::NameOfEnum( + ClusterDataType_descriptor(), enum_t_value); +} +inline bool ClusterDataType_Parse( + const std::string& name, ClusterDataType* value) { + return ::PROTOBUF_NAMESPACE_ID::internal::ParseNamedEnum( + ClusterDataType_descriptor(), name, value); +} +// =================================================================== + +class ClusterIO_Message PROTOBUF_FINAL : + public ::PROTOBUF_NAMESPACE_ID::Message /* @@protoc_insertion_point(class_definition:ClusterIO_Message) */ { + public: + inline ClusterIO_Message() : ClusterIO_Message(nullptr) {}; + virtual ~ClusterIO_Message(); + + ClusterIO_Message(const ClusterIO_Message& from); + ClusterIO_Message(ClusterIO_Message&& from) noexcept + : ClusterIO_Message() { + *this = ::std::move(from); + } + + inline ClusterIO_Message& operator=(const ClusterIO_Message& from) { + CopyFrom(from); + return *this; + } + inline ClusterIO_Message& operator=(ClusterIO_Message&& from) noexcept { + if (GetArena() == from.GetArena()) { + if (this != &from) InternalSwap(&from); + } else { + CopyFrom(from); + } + return *this; + } + + static const ::PROTOBUF_NAMESPACE_ID::Descriptor* descriptor() { + return GetDescriptor(); + } + static const ::PROTOBUF_NAMESPACE_ID::Descriptor* GetDescriptor() { + return GetMetadataStatic().descriptor; + } + static const ::PROTOBUF_NAMESPACE_ID::Reflection* GetReflection() { + return GetMetadataStatic().reflection; + } + static const ClusterIO_Message& default_instance(); + + static void InitAsDefaultInstance(); // FOR INTERNAL USE ONLY + static inline const ClusterIO_Message* internal_default_instance() { + return reinterpret_cast( + &_ClusterIO_Message_default_instance_); + } + static constexpr int kIndexInFileMessages = + 0; + + friend void swap(ClusterIO_Message& a, ClusterIO_Message& b) { + a.Swap(&b); + } + inline void Swap(ClusterIO_Message* other) { + if (other == this) return; + if (GetArena() == other->GetArena()) { + InternalSwap(other); + } else { + ::PROTOBUF_NAMESPACE_ID::internal::GenericSwap(this, other); + } + } + void UnsafeArenaSwap(ClusterIO_Message* other) { + if (other == this) return; + GOOGLE_DCHECK(GetArena() == other->GetArena()); + InternalSwap(other); + } + + // implements Message ---------------------------------------------- + + inline ClusterIO_Message* New() const final { + return CreateMaybeMessage(nullptr); + } + + ClusterIO_Message* New(::PROTOBUF_NAMESPACE_ID::Arena* arena) const final { + return CreateMaybeMessage(arena); + } + void CopyFrom(const ::PROTOBUF_NAMESPACE_ID::Message& from) final; + void MergeFrom(const ::PROTOBUF_NAMESPACE_ID::Message& from) final; + void CopyFrom(const ClusterIO_Message& from); + void MergeFrom(const ClusterIO_Message& from); + PROTOBUF_ATTRIBUTE_REINITIALIZES void Clear() final; + bool IsInitialized() const final; + + size_t ByteSizeLong() const final; + const char* _InternalParse(const char* ptr, ::PROTOBUF_NAMESPACE_ID::internal::ParseContext* ctx) final; + ::PROTOBUF_NAMESPACE_ID::uint8* _InternalSerialize( + ::PROTOBUF_NAMESPACE_ID::uint8* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const final; + int GetCachedSize() const final { return _cached_size_.Get(); } + + private: + inline void SharedCtor(); + inline void SharedDtor(); + void SetCachedSize(int size) const final; + void InternalSwap(ClusterIO_Message* other); + friend class ::PROTOBUF_NAMESPACE_ID::internal::AnyMetadata; + static ::PROTOBUF_NAMESPACE_ID::StringPiece FullMessageName() { + return "ClusterIO_Message"; + } + protected: + explicit ClusterIO_Message(::PROTOBUF_NAMESPACE_ID::Arena* arena); + private: + static void ArenaDtor(void* object); + inline void RegisterArenaDtor(::PROTOBUF_NAMESPACE_ID::Arena* arena); + public: + + ::PROTOBUF_NAMESPACE_ID::Metadata GetMetadata() const final; + private: + static ::PROTOBUF_NAMESPACE_ID::Metadata GetMetadataStatic() { + ::PROTOBUF_NAMESPACE_ID::internal::AssignDescriptors(&::descriptor_table_kyouko_5fmessages_2eproto3); + return ::descriptor_table_kyouko_5fmessages_2eproto3.file_level_metadata[kIndexInFileMessages]; + } + + public: + + // nested types ---------------------------------------------------- + + // accessors ------------------------------------------------------- + + enum : int { + kValuesFieldNumber = 6, + kSegmentNameFieldNumber = 1, + kIsLastFieldNumber = 2, + kProcessTypeFieldNumber = 3, + kNumberOfValuesFieldNumber = 5, + kDataTypeFieldNumber = 4, + }; + // repeated float values = 6 [packed = true]; + int values_size() const; + private: + int _internal_values_size() const; + public: + void clear_values(); + private: + float _internal_values(int index) const; + const ::PROTOBUF_NAMESPACE_ID::RepeatedField< float >& + _internal_values() const; + void _internal_add_values(float value); + ::PROTOBUF_NAMESPACE_ID::RepeatedField< float >* + _internal_mutable_values(); + public: + float values(int index) const; + void set_values(int index, float value); + void add_values(float value); + const ::PROTOBUF_NAMESPACE_ID::RepeatedField< float >& + values() const; + ::PROTOBUF_NAMESPACE_ID::RepeatedField< float >* + mutable_values(); + + // string segmentName = 1; + void clear_segmentname(); + const std::string& segmentname() const; + void set_segmentname(const std::string& value); + void set_segmentname(std::string&& value); + void set_segmentname(const char* value); + void set_segmentname(const char* value, size_t size); + std::string* mutable_segmentname(); + std::string* release_segmentname(); + void set_allocated_segmentname(std::string* segmentname); + GOOGLE_PROTOBUF_RUNTIME_DEPRECATED("The unsafe_arena_ accessors for" + " string fields are deprecated and will be removed in a" + " future release.") + std::string* unsafe_arena_release_segmentname(); + GOOGLE_PROTOBUF_RUNTIME_DEPRECATED("The unsafe_arena_ accessors for" + " string fields are deprecated and will be removed in a" + " future release.") + void unsafe_arena_set_allocated_segmentname( + std::string* segmentname); + private: + const std::string& _internal_segmentname() const; + void _internal_set_segmentname(const std::string& value); + std::string* _internal_mutable_segmentname(); + public: + + // bool isLast = 2; + void clear_islast(); + bool islast() const; + void set_islast(bool value); + private: + bool _internal_islast() const; + void _internal_set_islast(bool value); + public: + + // .ClusterProcessType processType = 3; + void clear_processtype(); + ::ClusterProcessType processtype() const; + void set_processtype(::ClusterProcessType value); + private: + ::ClusterProcessType _internal_processtype() const; + void _internal_set_processtype(::ClusterProcessType value); + public: + + // uint64 numberOfValues = 5; + void clear_numberofvalues(); + ::PROTOBUF_NAMESPACE_ID::uint64 numberofvalues() const; + void set_numberofvalues(::PROTOBUF_NAMESPACE_ID::uint64 value); + private: + ::PROTOBUF_NAMESPACE_ID::uint64 _internal_numberofvalues() const; + void _internal_set_numberofvalues(::PROTOBUF_NAMESPACE_ID::uint64 value); + public: + + // .ClusterDataType dataType = 4; + void clear_datatype(); + ::ClusterDataType datatype() const; + void set_datatype(::ClusterDataType value); + private: + ::ClusterDataType _internal_datatype() const; + void _internal_set_datatype(::ClusterDataType value); + public: + + // @@protoc_insertion_point(class_scope:ClusterIO_Message) + private: + class _Internal; + + template friend class ::PROTOBUF_NAMESPACE_ID::Arena::InternalHelper; + typedef void InternalArenaConstructable_; + typedef void DestructorSkippable_; + ::PROTOBUF_NAMESPACE_ID::RepeatedField< float > values_; + mutable std::atomic _values_cached_byte_size_; + ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr segmentname_; + bool islast_; + int processtype_; + ::PROTOBUF_NAMESPACE_ID::uint64 numberofvalues_; + int datatype_; + mutable ::PROTOBUF_NAMESPACE_ID::internal::CachedSize _cached_size_; + friend struct ::TableStruct_kyouko_5fmessages_2eproto3; +}; +// =================================================================== + + +// =================================================================== + +#ifdef __GNUC__ + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Wstrict-aliasing" +#endif // __GNUC__ +// ClusterIO_Message + +// string segmentName = 1; +inline void ClusterIO_Message::clear_segmentname() { + segmentname_.ClearToEmpty(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), GetArena()); +} +inline const std::string& ClusterIO_Message::segmentname() const { + // @@protoc_insertion_point(field_get:ClusterIO_Message.segmentName) + return _internal_segmentname(); +} +inline void ClusterIO_Message::set_segmentname(const std::string& value) { + _internal_set_segmentname(value); + // @@protoc_insertion_point(field_set:ClusterIO_Message.segmentName) +} +inline std::string* ClusterIO_Message::mutable_segmentname() { + // @@protoc_insertion_point(field_mutable:ClusterIO_Message.segmentName) + return _internal_mutable_segmentname(); +} +inline const std::string& ClusterIO_Message::_internal_segmentname() const { + return segmentname_.Get(); +} +inline void ClusterIO_Message::_internal_set_segmentname(const std::string& value) { + + segmentname_.Set(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), value, GetArena()); +} +inline void ClusterIO_Message::set_segmentname(std::string&& value) { + + segmentname_.Set( + &::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), ::std::move(value), GetArena()); + // @@protoc_insertion_point(field_set_rvalue:ClusterIO_Message.segmentName) +} +inline void ClusterIO_Message::set_segmentname(const char* value) { + GOOGLE_DCHECK(value != nullptr); + + segmentname_.Set(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), ::std::string(value), + GetArena()); + // @@protoc_insertion_point(field_set_char:ClusterIO_Message.segmentName) +} +inline void ClusterIO_Message::set_segmentname(const char* value, + size_t size) { + + segmentname_.Set(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), ::std::string( + reinterpret_cast(value), size), GetArena()); + // @@protoc_insertion_point(field_set_pointer:ClusterIO_Message.segmentName) +} +inline std::string* ClusterIO_Message::_internal_mutable_segmentname() { + + return segmentname_.Mutable(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), GetArena()); +} +inline std::string* ClusterIO_Message::release_segmentname() { + // @@protoc_insertion_point(field_release:ClusterIO_Message.segmentName) + return segmentname_.Release(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), GetArena()); +} +inline void ClusterIO_Message::set_allocated_segmentname(std::string* segmentname) { + if (segmentname != nullptr) { + + } else { + + } + segmentname_.SetAllocated(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), segmentname, + GetArena()); + // @@protoc_insertion_point(field_set_allocated:ClusterIO_Message.segmentName) +} +inline std::string* ClusterIO_Message::unsafe_arena_release_segmentname() { + // @@protoc_insertion_point(field_unsafe_arena_release:ClusterIO_Message.segmentName) + GOOGLE_DCHECK(GetArena() != nullptr); + + return segmentname_.UnsafeArenaRelease(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), + GetArena()); +} +inline void ClusterIO_Message::unsafe_arena_set_allocated_segmentname( + std::string* segmentname) { + GOOGLE_DCHECK(GetArena() != nullptr); + if (segmentname != nullptr) { + + } else { + + } + segmentname_.UnsafeArenaSetAllocated(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), + segmentname, GetArena()); + // @@protoc_insertion_point(field_unsafe_arena_set_allocated:ClusterIO_Message.segmentName) +} + +// bool isLast = 2; +inline void ClusterIO_Message::clear_islast() { + islast_ = false; +} +inline bool ClusterIO_Message::_internal_islast() const { + return islast_; +} +inline bool ClusterIO_Message::islast() const { + // @@protoc_insertion_point(field_get:ClusterIO_Message.isLast) + return _internal_islast(); +} +inline void ClusterIO_Message::_internal_set_islast(bool value) { + + islast_ = value; +} +inline void ClusterIO_Message::set_islast(bool value) { + _internal_set_islast(value); + // @@protoc_insertion_point(field_set:ClusterIO_Message.isLast) +} + +// .ClusterProcessType processType = 3; +inline void ClusterIO_Message::clear_processtype() { + processtype_ = 0; +} +inline ::ClusterProcessType ClusterIO_Message::_internal_processtype() const { + return static_cast< ::ClusterProcessType >(processtype_); +} +inline ::ClusterProcessType ClusterIO_Message::processtype() const { + // @@protoc_insertion_point(field_get:ClusterIO_Message.processType) + return _internal_processtype(); +} +inline void ClusterIO_Message::_internal_set_processtype(::ClusterProcessType value) { + + processtype_ = value; +} +inline void ClusterIO_Message::set_processtype(::ClusterProcessType value) { + _internal_set_processtype(value); + // @@protoc_insertion_point(field_set:ClusterIO_Message.processType) +} + +// .ClusterDataType dataType = 4; +inline void ClusterIO_Message::clear_datatype() { + datatype_ = 0; +} +inline ::ClusterDataType ClusterIO_Message::_internal_datatype() const { + return static_cast< ::ClusterDataType >(datatype_); +} +inline ::ClusterDataType ClusterIO_Message::datatype() const { + // @@protoc_insertion_point(field_get:ClusterIO_Message.dataType) + return _internal_datatype(); +} +inline void ClusterIO_Message::_internal_set_datatype(::ClusterDataType value) { + + datatype_ = value; +} +inline void ClusterIO_Message::set_datatype(::ClusterDataType value) { + _internal_set_datatype(value); + // @@protoc_insertion_point(field_set:ClusterIO_Message.dataType) +} + +// uint64 numberOfValues = 5; +inline void ClusterIO_Message::clear_numberofvalues() { + numberofvalues_ = PROTOBUF_ULONGLONG(0); +} +inline ::PROTOBUF_NAMESPACE_ID::uint64 ClusterIO_Message::_internal_numberofvalues() const { + return numberofvalues_; +} +inline ::PROTOBUF_NAMESPACE_ID::uint64 ClusterIO_Message::numberofvalues() const { + // @@protoc_insertion_point(field_get:ClusterIO_Message.numberOfValues) + return _internal_numberofvalues(); +} +inline void ClusterIO_Message::_internal_set_numberofvalues(::PROTOBUF_NAMESPACE_ID::uint64 value) { + + numberofvalues_ = value; +} +inline void ClusterIO_Message::set_numberofvalues(::PROTOBUF_NAMESPACE_ID::uint64 value) { + _internal_set_numberofvalues(value); + // @@protoc_insertion_point(field_set:ClusterIO_Message.numberOfValues) +} + +// repeated float values = 6 [packed = true]; +inline int ClusterIO_Message::_internal_values_size() const { + return values_.size(); +} +inline int ClusterIO_Message::values_size() const { + return _internal_values_size(); +} +inline void ClusterIO_Message::clear_values() { + values_.Clear(); +} +inline float ClusterIO_Message::_internal_values(int index) const { + return values_.Get(index); +} +inline float ClusterIO_Message::values(int index) const { + // @@protoc_insertion_point(field_get:ClusterIO_Message.values) + return _internal_values(index); +} +inline void ClusterIO_Message::set_values(int index, float value) { + values_.Set(index, value); + // @@protoc_insertion_point(field_set:ClusterIO_Message.values) +} +inline void ClusterIO_Message::_internal_add_values(float value) { + values_.Add(value); +} +inline void ClusterIO_Message::add_values(float value) { + _internal_add_values(value); + // @@protoc_insertion_point(field_add:ClusterIO_Message.values) +} +inline const ::PROTOBUF_NAMESPACE_ID::RepeatedField< float >& +ClusterIO_Message::_internal_values() const { + return values_; +} +inline const ::PROTOBUF_NAMESPACE_ID::RepeatedField< float >& +ClusterIO_Message::values() const { + // @@protoc_insertion_point(field_list:ClusterIO_Message.values) + return _internal_values(); +} +inline ::PROTOBUF_NAMESPACE_ID::RepeatedField< float >* +ClusterIO_Message::_internal_mutable_values() { + return &values_; +} +inline ::PROTOBUF_NAMESPACE_ID::RepeatedField< float >* +ClusterIO_Message::mutable_values() { + // @@protoc_insertion_point(field_mutable_list:ClusterIO_Message.values) + return _internal_mutable_values(); +} + +#ifdef __GNUC__ + #pragma GCC diagnostic pop +#endif // __GNUC__ + +// @@protoc_insertion_point(namespace_scope) + + +PROTOBUF_NAMESPACE_OPEN + +template <> struct is_proto_enum< ::ClusterProcessType> : ::std::true_type {}; +template <> +inline const EnumDescriptor* GetEnumDescriptor< ::ClusterProcessType>() { + return ::ClusterProcessType_descriptor(); +} +template <> struct is_proto_enum< ::ClusterDataType> : ::std::true_type {}; +template <> +inline const EnumDescriptor* GetEnumDescriptor< ::ClusterDataType>() { + return ::ClusterDataType_descriptor(); +} + +PROTOBUF_NAMESPACE_CLOSE + +// @@protoc_insertion_point(global_scope) + +#include +#endif // GOOGLE_PROTOBUF_INCLUDED_GOOGLE_PROTOBUF_INCLUDED_kyouko_5fmessages_2eproto3 diff --git a/src/frontend/Hanami-AI-Dashboard/src/libKitsunemimiHanamiMessages/protobuffers/shiori_messages.proto3 b/src/frontend/Hanami-AI-Dashboard/src/libKitsunemimiHanamiMessages/protobuffers/shiori_messages.proto3 new file mode 100644 index 00000000..3883c793 --- /dev/null +++ b/src/frontend/Hanami-AI-Dashboard/src/libKitsunemimiHanamiMessages/protobuffers/shiori_messages.proto3 @@ -0,0 +1,16 @@ +syntax = "proto3"; + +enum UploadDataType { + DATASET_TYPE = 0; + CLUSTER_SNAPSHOT_TYPE = 1; +} + +message FileUpload_Message { + string datasetUuid = 1; + string fileUuid = 2; + bool isLast = 3; + UploadDataType type = 4; + uint64 position = 5; + bytes data = 6; +} + diff --git a/src/frontend/Hanami-AI-Dashboard/src/libKitsunemimiHanamiMessages/protobuffers/shiori_messages.proto3.pb.cc b/src/frontend/Hanami-AI-Dashboard/src/libKitsunemimiHanamiMessages/protobuffers/shiori_messages.proto3.pb.cc new file mode 100644 index 00000000..830f47f6 --- /dev/null +++ b/src/frontend/Hanami-AI-Dashboard/src/libKitsunemimiHanamiMessages/protobuffers/shiori_messages.proto3.pb.cc @@ -0,0 +1,469 @@ +// Generated by the protocol buffer compiler. DO NOT EDIT! +// source: shiori_messages.proto3 + +#include "shiori_messages.proto3.pb.h" + +#include + +#include +#include +#include +#include +#include +#include +#include +// @@protoc_insertion_point(includes) +#include +class FileUpload_MessageDefaultTypeInternal { + public: + ::PROTOBUF_NAMESPACE_ID::internal::ExplicitlyConstructed _instance; +} _FileUpload_Message_default_instance_; +static void InitDefaultsscc_info_FileUpload_Message_shiori_5fmessages_2eproto3() { + GOOGLE_PROTOBUF_VERIFY_VERSION; + + { + void* ptr = &::_FileUpload_Message_default_instance_; + new (ptr) ::FileUpload_Message(); + ::PROTOBUF_NAMESPACE_ID::internal::OnShutdownDestroyMessage(ptr); + } + ::FileUpload_Message::InitAsDefaultInstance(); +} + +::PROTOBUF_NAMESPACE_ID::internal::SCCInfo<0> scc_info_FileUpload_Message_shiori_5fmessages_2eproto3 = + {{ATOMIC_VAR_INIT(::PROTOBUF_NAMESPACE_ID::internal::SCCInfoBase::kUninitialized), 0, 0, InitDefaultsscc_info_FileUpload_Message_shiori_5fmessages_2eproto3}, {}}; + +static ::PROTOBUF_NAMESPACE_ID::Metadata file_level_metadata_shiori_5fmessages_2eproto3[1]; +static const ::PROTOBUF_NAMESPACE_ID::EnumDescriptor* file_level_enum_descriptors_shiori_5fmessages_2eproto3[1]; +static constexpr ::PROTOBUF_NAMESPACE_ID::ServiceDescriptor const** file_level_service_descriptors_shiori_5fmessages_2eproto3 = nullptr; + +const ::PROTOBUF_NAMESPACE_ID::uint32 TableStruct_shiori_5fmessages_2eproto3::offsets[] PROTOBUF_SECTION_VARIABLE(protodesc_cold) = { + ~0u, // no _has_bits_ + PROTOBUF_FIELD_OFFSET(::FileUpload_Message, _internal_metadata_), + ~0u, // no _extensions_ + ~0u, // no _oneof_case_ + ~0u, // no _weak_field_map_ + PROTOBUF_FIELD_OFFSET(::FileUpload_Message, datasetuuid_), + PROTOBUF_FIELD_OFFSET(::FileUpload_Message, fileuuid_), + PROTOBUF_FIELD_OFFSET(::FileUpload_Message, islast_), + PROTOBUF_FIELD_OFFSET(::FileUpload_Message, type_), + PROTOBUF_FIELD_OFFSET(::FileUpload_Message, position_), + PROTOBUF_FIELD_OFFSET(::FileUpload_Message, data_), +}; +static const ::PROTOBUF_NAMESPACE_ID::internal::MigrationSchema schemas[] PROTOBUF_SECTION_VARIABLE(protodesc_cold) = { + { 0, -1, sizeof(::FileUpload_Message)}, +}; + +static ::PROTOBUF_NAMESPACE_ID::Message const * const file_default_instances[] = { + reinterpret_cast(&::_FileUpload_Message_default_instance_), +}; + +const char descriptor_table_protodef_shiori_5fmessages_2eproto3[] PROTOBUF_SECTION_VARIABLE(protodesc_cold) = + "\n\026shiori_messages.proto3\"\212\001\n\022FileUpload_" + "Message\022\023\n\013datasetUuid\030\001 \001(\t\022\020\n\010fileUuid" + "\030\002 \001(\t\022\016\n\006isLast\030\003 \001(\010\022\035\n\004type\030\004 \001(\0162\017.U" + "ploadDataType\022\020\n\010position\030\005 \001(\004\022\014\n\004data\030" + "\006 \001(\014*=\n\016UploadDataType\022\020\n\014DATASET_TYPE\020" + "\000\022\031\n\025CLUSTER_SNAPSHOT_TYPE\020\001b\006proto3" + ; +static const ::PROTOBUF_NAMESPACE_ID::internal::DescriptorTable*const descriptor_table_shiori_5fmessages_2eproto3_deps[1] = { +}; +static ::PROTOBUF_NAMESPACE_ID::internal::SCCInfoBase*const descriptor_table_shiori_5fmessages_2eproto3_sccs[1] = { + &scc_info_FileUpload_Message_shiori_5fmessages_2eproto3.base, +}; +static ::PROTOBUF_NAMESPACE_ID::internal::once_flag descriptor_table_shiori_5fmessages_2eproto3_once; +const ::PROTOBUF_NAMESPACE_ID::internal::DescriptorTable descriptor_table_shiori_5fmessages_2eproto3 = { + false, false, descriptor_table_protodef_shiori_5fmessages_2eproto3, "shiori_messages.proto3", 236, + &descriptor_table_shiori_5fmessages_2eproto3_once, descriptor_table_shiori_5fmessages_2eproto3_sccs, descriptor_table_shiori_5fmessages_2eproto3_deps, 1, 0, + schemas, file_default_instances, TableStruct_shiori_5fmessages_2eproto3::offsets, + file_level_metadata_shiori_5fmessages_2eproto3, 1, file_level_enum_descriptors_shiori_5fmessages_2eproto3, file_level_service_descriptors_shiori_5fmessages_2eproto3, +}; + +// Force running AddDescriptors() at dynamic initialization time. +static bool dynamic_init_dummy_shiori_5fmessages_2eproto3 = (static_cast(::PROTOBUF_NAMESPACE_ID::internal::AddDescriptors(&descriptor_table_shiori_5fmessages_2eproto3)), true); +const ::PROTOBUF_NAMESPACE_ID::EnumDescriptor* UploadDataType_descriptor() { + ::PROTOBUF_NAMESPACE_ID::internal::AssignDescriptors(&descriptor_table_shiori_5fmessages_2eproto3); + return file_level_enum_descriptors_shiori_5fmessages_2eproto3[0]; +} +bool UploadDataType_IsValid(int value) { + switch (value) { + case 0: + case 1: + return true; + default: + return false; + } +} + + +// =================================================================== + +void FileUpload_Message::InitAsDefaultInstance() { +} +class FileUpload_Message::_Internal { + public: +}; + +FileUpload_Message::FileUpload_Message(::PROTOBUF_NAMESPACE_ID::Arena* arena) + : ::PROTOBUF_NAMESPACE_ID::Message(arena) { + SharedCtor(); + RegisterArenaDtor(arena); + // @@protoc_insertion_point(arena_constructor:FileUpload_Message) +} +FileUpload_Message::FileUpload_Message(const FileUpload_Message& from) + : ::PROTOBUF_NAMESPACE_ID::Message() { + _internal_metadata_.MergeFrom<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(from._internal_metadata_); + datasetuuid_.UnsafeSetDefault(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); + if (!from._internal_datasetuuid().empty()) { + datasetuuid_.Set(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), from._internal_datasetuuid(), + GetArena()); + } + fileuuid_.UnsafeSetDefault(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); + if (!from._internal_fileuuid().empty()) { + fileuuid_.Set(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), from._internal_fileuuid(), + GetArena()); + } + data_.UnsafeSetDefault(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); + if (!from._internal_data().empty()) { + data_.Set(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), from._internal_data(), + GetArena()); + } + ::memcpy(&islast_, &from.islast_, + static_cast(reinterpret_cast(&position_) - + reinterpret_cast(&islast_)) + sizeof(position_)); + // @@protoc_insertion_point(copy_constructor:FileUpload_Message) +} + +void FileUpload_Message::SharedCtor() { + ::PROTOBUF_NAMESPACE_ID::internal::InitSCC(&scc_info_FileUpload_Message_shiori_5fmessages_2eproto3.base); + datasetuuid_.UnsafeSetDefault(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); + fileuuid_.UnsafeSetDefault(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); + data_.UnsafeSetDefault(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); + ::memset(&islast_, 0, static_cast( + reinterpret_cast(&position_) - + reinterpret_cast(&islast_)) + sizeof(position_)); +} + +FileUpload_Message::~FileUpload_Message() { + // @@protoc_insertion_point(destructor:FileUpload_Message) + SharedDtor(); + _internal_metadata_.Delete<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(); +} + +void FileUpload_Message::SharedDtor() { + GOOGLE_DCHECK(GetArena() == nullptr); + datasetuuid_.DestroyNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); + fileuuid_.DestroyNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); + data_.DestroyNoArena(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited()); +} + +void FileUpload_Message::ArenaDtor(void* object) { + FileUpload_Message* _this = reinterpret_cast< FileUpload_Message* >(object); + (void)_this; +} +void FileUpload_Message::RegisterArenaDtor(::PROTOBUF_NAMESPACE_ID::Arena*) { +} +void FileUpload_Message::SetCachedSize(int size) const { + _cached_size_.Set(size); +} +const FileUpload_Message& FileUpload_Message::default_instance() { + ::PROTOBUF_NAMESPACE_ID::internal::InitSCC(&::scc_info_FileUpload_Message_shiori_5fmessages_2eproto3.base); + return *internal_default_instance(); +} + + +void FileUpload_Message::Clear() { +// @@protoc_insertion_point(message_clear_start:FileUpload_Message) + ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0; + // Prevent compiler warnings about cached_has_bits being unused + (void) cached_has_bits; + + datasetuuid_.ClearToEmpty(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), GetArena()); + fileuuid_.ClearToEmpty(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), GetArena()); + data_.ClearToEmpty(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), GetArena()); + ::memset(&islast_, 0, static_cast( + reinterpret_cast(&position_) - + reinterpret_cast(&islast_)) + sizeof(position_)); + _internal_metadata_.Clear<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(); +} + +const char* FileUpload_Message::_InternalParse(const char* ptr, ::PROTOBUF_NAMESPACE_ID::internal::ParseContext* ctx) { +#define CHK_(x) if (PROTOBUF_PREDICT_FALSE(!(x))) goto failure + ::PROTOBUF_NAMESPACE_ID::Arena* arena = GetArena(); (void)arena; + while (!ctx->Done(&ptr)) { + ::PROTOBUF_NAMESPACE_ID::uint32 tag; + ptr = ::PROTOBUF_NAMESPACE_ID::internal::ReadTag(ptr, &tag); + CHK_(ptr); + switch (tag >> 3) { + // string datasetUuid = 1; + case 1: + if (PROTOBUF_PREDICT_TRUE(static_cast<::PROTOBUF_NAMESPACE_ID::uint8>(tag) == 10)) { + auto str = _internal_mutable_datasetuuid(); + ptr = ::PROTOBUF_NAMESPACE_ID::internal::InlineGreedyStringParser(str, ptr, ctx); + CHK_(::PROTOBUF_NAMESPACE_ID::internal::VerifyUTF8(str, "FileUpload_Message.datasetUuid")); + CHK_(ptr); + } else goto handle_unusual; + continue; + // string fileUuid = 2; + case 2: + if (PROTOBUF_PREDICT_TRUE(static_cast<::PROTOBUF_NAMESPACE_ID::uint8>(tag) == 18)) { + auto str = _internal_mutable_fileuuid(); + ptr = ::PROTOBUF_NAMESPACE_ID::internal::InlineGreedyStringParser(str, ptr, ctx); + CHK_(::PROTOBUF_NAMESPACE_ID::internal::VerifyUTF8(str, "FileUpload_Message.fileUuid")); + CHK_(ptr); + } else goto handle_unusual; + continue; + // bool isLast = 3; + case 3: + if (PROTOBUF_PREDICT_TRUE(static_cast<::PROTOBUF_NAMESPACE_ID::uint8>(tag) == 24)) { + islast_ = ::PROTOBUF_NAMESPACE_ID::internal::ReadVarint64(&ptr); + CHK_(ptr); + } else goto handle_unusual; + continue; + // .UploadDataType type = 4; + case 4: + if (PROTOBUF_PREDICT_TRUE(static_cast<::PROTOBUF_NAMESPACE_ID::uint8>(tag) == 32)) { + ::PROTOBUF_NAMESPACE_ID::uint64 val = ::PROTOBUF_NAMESPACE_ID::internal::ReadVarint64(&ptr); + CHK_(ptr); + _internal_set_type(static_cast<::UploadDataType>(val)); + } else goto handle_unusual; + continue; + // uint64 position = 5; + case 5: + if (PROTOBUF_PREDICT_TRUE(static_cast<::PROTOBUF_NAMESPACE_ID::uint8>(tag) == 40)) { + position_ = ::PROTOBUF_NAMESPACE_ID::internal::ReadVarint64(&ptr); + CHK_(ptr); + } else goto handle_unusual; + continue; + // bytes data = 6; + case 6: + if (PROTOBUF_PREDICT_TRUE(static_cast<::PROTOBUF_NAMESPACE_ID::uint8>(tag) == 50)) { + auto str = _internal_mutable_data(); + ptr = ::PROTOBUF_NAMESPACE_ID::internal::InlineGreedyStringParser(str, ptr, ctx); + CHK_(ptr); + } else goto handle_unusual; + continue; + default: { + handle_unusual: + if ((tag & 7) == 4 || tag == 0) { + ctx->SetLastTag(tag); + goto success; + } + ptr = UnknownFieldParse(tag, + _internal_metadata_.mutable_unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(), + ptr, ctx); + CHK_(ptr != nullptr); + continue; + } + } // switch + } // while +success: + return ptr; +failure: + ptr = nullptr; + goto success; +#undef CHK_ +} + +::PROTOBUF_NAMESPACE_ID::uint8* FileUpload_Message::_InternalSerialize( + ::PROTOBUF_NAMESPACE_ID::uint8* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const { + // @@protoc_insertion_point(serialize_to_array_start:FileUpload_Message) + ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0; + (void) cached_has_bits; + + // string datasetUuid = 1; + if (this->datasetuuid().size() > 0) { + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::VerifyUtf8String( + this->_internal_datasetuuid().data(), static_cast(this->_internal_datasetuuid().length()), + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::SERIALIZE, + "FileUpload_Message.datasetUuid"); + target = stream->WriteStringMaybeAliased( + 1, this->_internal_datasetuuid(), target); + } + + // string fileUuid = 2; + if (this->fileuuid().size() > 0) { + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::VerifyUtf8String( + this->_internal_fileuuid().data(), static_cast(this->_internal_fileuuid().length()), + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::SERIALIZE, + "FileUpload_Message.fileUuid"); + target = stream->WriteStringMaybeAliased( + 2, this->_internal_fileuuid(), target); + } + + // bool isLast = 3; + if (this->islast() != 0) { + target = stream->EnsureSpace(target); + target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteBoolToArray(3, this->_internal_islast(), target); + } + + // .UploadDataType type = 4; + if (this->type() != 0) { + target = stream->EnsureSpace(target); + target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteEnumToArray( + 4, this->_internal_type(), target); + } + + // uint64 position = 5; + if (this->position() != 0) { + target = stream->EnsureSpace(target); + target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::WriteUInt64ToArray(5, this->_internal_position(), target); + } + + // bytes data = 6; + if (this->data().size() > 0) { + target = stream->WriteBytesMaybeAliased( + 6, this->_internal_data(), target); + } + + if (PROTOBUF_PREDICT_FALSE(_internal_metadata_.have_unknown_fields())) { + target = ::PROTOBUF_NAMESPACE_ID::internal::WireFormat::InternalSerializeUnknownFieldsToArray( + _internal_metadata_.unknown_fields<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(::PROTOBUF_NAMESPACE_ID::UnknownFieldSet::default_instance), target, stream); + } + // @@protoc_insertion_point(serialize_to_array_end:FileUpload_Message) + return target; +} + +size_t FileUpload_Message::ByteSizeLong() const { +// @@protoc_insertion_point(message_byte_size_start:FileUpload_Message) + size_t total_size = 0; + + ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0; + // Prevent compiler warnings about cached_has_bits being unused + (void) cached_has_bits; + + // string datasetUuid = 1; + if (this->datasetuuid().size() > 0) { + total_size += 1 + + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::StringSize( + this->_internal_datasetuuid()); + } + + // string fileUuid = 2; + if (this->fileuuid().size() > 0) { + total_size += 1 + + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::StringSize( + this->_internal_fileuuid()); + } + + // bytes data = 6; + if (this->data().size() > 0) { + total_size += 1 + + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::BytesSize( + this->_internal_data()); + } + + // bool isLast = 3; + if (this->islast() != 0) { + total_size += 1 + 1; + } + + // .UploadDataType type = 4; + if (this->type() != 0) { + total_size += 1 + + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::EnumSize(this->_internal_type()); + } + + // uint64 position = 5; + if (this->position() != 0) { + total_size += 1 + + ::PROTOBUF_NAMESPACE_ID::internal::WireFormatLite::UInt64Size( + this->_internal_position()); + } + + if (PROTOBUF_PREDICT_FALSE(_internal_metadata_.have_unknown_fields())) { + return ::PROTOBUF_NAMESPACE_ID::internal::ComputeUnknownFieldsSize( + _internal_metadata_, total_size, &_cached_size_); + } + int cached_size = ::PROTOBUF_NAMESPACE_ID::internal::ToCachedSize(total_size); + SetCachedSize(cached_size); + return total_size; +} + +void FileUpload_Message::MergeFrom(const ::PROTOBUF_NAMESPACE_ID::Message& from) { +// @@protoc_insertion_point(generalized_merge_from_start:FileUpload_Message) + GOOGLE_DCHECK_NE(&from, this); + const FileUpload_Message* source = + ::PROTOBUF_NAMESPACE_ID::DynamicCastToGenerated( + &from); + if (source == nullptr) { + // @@protoc_insertion_point(generalized_merge_from_cast_fail:FileUpload_Message) + ::PROTOBUF_NAMESPACE_ID::internal::ReflectionOps::Merge(from, this); + } else { + // @@protoc_insertion_point(generalized_merge_from_cast_success:FileUpload_Message) + MergeFrom(*source); + } +} + +void FileUpload_Message::MergeFrom(const FileUpload_Message& from) { +// @@protoc_insertion_point(class_specific_merge_from_start:FileUpload_Message) + GOOGLE_DCHECK_NE(&from, this); + _internal_metadata_.MergeFrom<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(from._internal_metadata_); + ::PROTOBUF_NAMESPACE_ID::uint32 cached_has_bits = 0; + (void) cached_has_bits; + + if (from.datasetuuid().size() > 0) { + _internal_set_datasetuuid(from._internal_datasetuuid()); + } + if (from.fileuuid().size() > 0) { + _internal_set_fileuuid(from._internal_fileuuid()); + } + if (from.data().size() > 0) { + _internal_set_data(from._internal_data()); + } + if (from.islast() != 0) { + _internal_set_islast(from._internal_islast()); + } + if (from.type() != 0) { + _internal_set_type(from._internal_type()); + } + if (from.position() != 0) { + _internal_set_position(from._internal_position()); + } +} + +void FileUpload_Message::CopyFrom(const ::PROTOBUF_NAMESPACE_ID::Message& from) { +// @@protoc_insertion_point(generalized_copy_from_start:FileUpload_Message) + if (&from == this) return; + Clear(); + MergeFrom(from); +} + +void FileUpload_Message::CopyFrom(const FileUpload_Message& from) { +// @@protoc_insertion_point(class_specific_copy_from_start:FileUpload_Message) + if (&from == this) return; + Clear(); + MergeFrom(from); +} + +bool FileUpload_Message::IsInitialized() const { + return true; +} + +void FileUpload_Message::InternalSwap(FileUpload_Message* other) { + using std::swap; + _internal_metadata_.Swap<::PROTOBUF_NAMESPACE_ID::UnknownFieldSet>(&other->_internal_metadata_); + datasetuuid_.Swap(&other->datasetuuid_, &::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), GetArena()); + fileuuid_.Swap(&other->fileuuid_, &::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), GetArena()); + data_.Swap(&other->data_, &::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), GetArena()); + ::PROTOBUF_NAMESPACE_ID::internal::memswap< + PROTOBUF_FIELD_OFFSET(FileUpload_Message, position_) + + sizeof(FileUpload_Message::position_) + - PROTOBUF_FIELD_OFFSET(FileUpload_Message, islast_)>( + reinterpret_cast(&islast_), + reinterpret_cast(&other->islast_)); +} + +::PROTOBUF_NAMESPACE_ID::Metadata FileUpload_Message::GetMetadata() const { + return GetMetadataStatic(); +} + + +// @@protoc_insertion_point(namespace_scope) +PROTOBUF_NAMESPACE_OPEN +template<> PROTOBUF_NOINLINE ::FileUpload_Message* Arena::CreateMaybeMessage< ::FileUpload_Message >(Arena* arena) { + return Arena::CreateMessageInternal< ::FileUpload_Message >(arena); +} +PROTOBUF_NAMESPACE_CLOSE + +// @@protoc_insertion_point(global_scope) +#include diff --git a/src/frontend/Hanami-AI-Dashboard/src/libKitsunemimiHanamiMessages/protobuffers/shiori_messages.proto3.pb.h b/src/frontend/Hanami-AI-Dashboard/src/libKitsunemimiHanamiMessages/protobuffers/shiori_messages.proto3.pb.h new file mode 100644 index 00000000..8ecc41b1 --- /dev/null +++ b/src/frontend/Hanami-AI-Dashboard/src/libKitsunemimiHanamiMessages/protobuffers/shiori_messages.proto3.pb.h @@ -0,0 +1,664 @@ +// Generated by the protocol buffer compiler. DO NOT EDIT! +// source: shiori_messages.proto3 + +#ifndef GOOGLE_PROTOBUF_INCLUDED_shiori_5fmessages_2eproto3 +#define GOOGLE_PROTOBUF_INCLUDED_shiori_5fmessages_2eproto3 + +#include +#include + +#include +#if PROTOBUF_VERSION < 3012000 +#error This file was generated by a newer version of protoc which is +#error incompatible with your Protocol Buffer headers. Please update +#error your headers. +#endif +#if 3012004 < PROTOBUF_MIN_PROTOC_VERSION +#error This file was generated by an older version of protoc which is +#error incompatible with your Protocol Buffer headers. Please +#error regenerate this file with a newer version of protoc. +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include // IWYU pragma: export +#include // IWYU pragma: export +#include +#include +// @@protoc_insertion_point(includes) +#include +#define PROTOBUF_INTERNAL_EXPORT_shiori_5fmessages_2eproto3 +PROTOBUF_NAMESPACE_OPEN +namespace internal { +class AnyMetadata; +} // namespace internal +PROTOBUF_NAMESPACE_CLOSE + +// Internal implementation detail -- do not use these members. +struct TableStruct_shiori_5fmessages_2eproto3 { + static const ::PROTOBUF_NAMESPACE_ID::internal::ParseTableField entries[] + PROTOBUF_SECTION_VARIABLE(protodesc_cold); + static const ::PROTOBUF_NAMESPACE_ID::internal::AuxillaryParseTableField aux[] + PROTOBUF_SECTION_VARIABLE(protodesc_cold); + static const ::PROTOBUF_NAMESPACE_ID::internal::ParseTable schema[1] + PROTOBUF_SECTION_VARIABLE(protodesc_cold); + static const ::PROTOBUF_NAMESPACE_ID::internal::FieldMetadata field_metadata[]; + static const ::PROTOBUF_NAMESPACE_ID::internal::SerializationTable serialization_table[]; + static const ::PROTOBUF_NAMESPACE_ID::uint32 offsets[]; +}; +extern const ::PROTOBUF_NAMESPACE_ID::internal::DescriptorTable descriptor_table_shiori_5fmessages_2eproto3; +class FileUpload_Message; +class FileUpload_MessageDefaultTypeInternal; +extern FileUpload_MessageDefaultTypeInternal _FileUpload_Message_default_instance_; +PROTOBUF_NAMESPACE_OPEN +template<> ::FileUpload_Message* Arena::CreateMaybeMessage<::FileUpload_Message>(Arena*); +PROTOBUF_NAMESPACE_CLOSE + +enum UploadDataType : int { + DATASET_TYPE = 0, + CLUSTER_SNAPSHOT_TYPE = 1, + UploadDataType_INT_MIN_SENTINEL_DO_NOT_USE_ = std::numeric_limits<::PROTOBUF_NAMESPACE_ID::int32>::min(), + UploadDataType_INT_MAX_SENTINEL_DO_NOT_USE_ = std::numeric_limits<::PROTOBUF_NAMESPACE_ID::int32>::max() +}; +bool UploadDataType_IsValid(int value); +constexpr UploadDataType UploadDataType_MIN = DATASET_TYPE; +constexpr UploadDataType UploadDataType_MAX = CLUSTER_SNAPSHOT_TYPE; +constexpr int UploadDataType_ARRAYSIZE = UploadDataType_MAX + 1; + +const ::PROTOBUF_NAMESPACE_ID::EnumDescriptor* UploadDataType_descriptor(); +template +inline const std::string& UploadDataType_Name(T enum_t_value) { + static_assert(::std::is_same::value || + ::std::is_integral::value, + "Incorrect type passed to function UploadDataType_Name."); + return ::PROTOBUF_NAMESPACE_ID::internal::NameOfEnum( + UploadDataType_descriptor(), enum_t_value); +} +inline bool UploadDataType_Parse( + const std::string& name, UploadDataType* value) { + return ::PROTOBUF_NAMESPACE_ID::internal::ParseNamedEnum( + UploadDataType_descriptor(), name, value); +} +// =================================================================== + +class FileUpload_Message PROTOBUF_FINAL : + public ::PROTOBUF_NAMESPACE_ID::Message /* @@protoc_insertion_point(class_definition:FileUpload_Message) */ { + public: + inline FileUpload_Message() : FileUpload_Message(nullptr) {}; + virtual ~FileUpload_Message(); + + FileUpload_Message(const FileUpload_Message& from); + FileUpload_Message(FileUpload_Message&& from) noexcept + : FileUpload_Message() { + *this = ::std::move(from); + } + + inline FileUpload_Message& operator=(const FileUpload_Message& from) { + CopyFrom(from); + return *this; + } + inline FileUpload_Message& operator=(FileUpload_Message&& from) noexcept { + if (GetArena() == from.GetArena()) { + if (this != &from) InternalSwap(&from); + } else { + CopyFrom(from); + } + return *this; + } + + static const ::PROTOBUF_NAMESPACE_ID::Descriptor* descriptor() { + return GetDescriptor(); + } + static const ::PROTOBUF_NAMESPACE_ID::Descriptor* GetDescriptor() { + return GetMetadataStatic().descriptor; + } + static const ::PROTOBUF_NAMESPACE_ID::Reflection* GetReflection() { + return GetMetadataStatic().reflection; + } + static const FileUpload_Message& default_instance(); + + static void InitAsDefaultInstance(); // FOR INTERNAL USE ONLY + static inline const FileUpload_Message* internal_default_instance() { + return reinterpret_cast( + &_FileUpload_Message_default_instance_); + } + static constexpr int kIndexInFileMessages = + 0; + + friend void swap(FileUpload_Message& a, FileUpload_Message& b) { + a.Swap(&b); + } + inline void Swap(FileUpload_Message* other) { + if (other == this) return; + if (GetArena() == other->GetArena()) { + InternalSwap(other); + } else { + ::PROTOBUF_NAMESPACE_ID::internal::GenericSwap(this, other); + } + } + void UnsafeArenaSwap(FileUpload_Message* other) { + if (other == this) return; + GOOGLE_DCHECK(GetArena() == other->GetArena()); + InternalSwap(other); + } + + // implements Message ---------------------------------------------- + + inline FileUpload_Message* New() const final { + return CreateMaybeMessage(nullptr); + } + + FileUpload_Message* New(::PROTOBUF_NAMESPACE_ID::Arena* arena) const final { + return CreateMaybeMessage(arena); + } + void CopyFrom(const ::PROTOBUF_NAMESPACE_ID::Message& from) final; + void MergeFrom(const ::PROTOBUF_NAMESPACE_ID::Message& from) final; + void CopyFrom(const FileUpload_Message& from); + void MergeFrom(const FileUpload_Message& from); + PROTOBUF_ATTRIBUTE_REINITIALIZES void Clear() final; + bool IsInitialized() const final; + + size_t ByteSizeLong() const final; + const char* _InternalParse(const char* ptr, ::PROTOBUF_NAMESPACE_ID::internal::ParseContext* ctx) final; + ::PROTOBUF_NAMESPACE_ID::uint8* _InternalSerialize( + ::PROTOBUF_NAMESPACE_ID::uint8* target, ::PROTOBUF_NAMESPACE_ID::io::EpsCopyOutputStream* stream) const final; + int GetCachedSize() const final { return _cached_size_.Get(); } + + private: + inline void SharedCtor(); + inline void SharedDtor(); + void SetCachedSize(int size) const final; + void InternalSwap(FileUpload_Message* other); + friend class ::PROTOBUF_NAMESPACE_ID::internal::AnyMetadata; + static ::PROTOBUF_NAMESPACE_ID::StringPiece FullMessageName() { + return "FileUpload_Message"; + } + protected: + explicit FileUpload_Message(::PROTOBUF_NAMESPACE_ID::Arena* arena); + private: + static void ArenaDtor(void* object); + inline void RegisterArenaDtor(::PROTOBUF_NAMESPACE_ID::Arena* arena); + public: + + ::PROTOBUF_NAMESPACE_ID::Metadata GetMetadata() const final; + private: + static ::PROTOBUF_NAMESPACE_ID::Metadata GetMetadataStatic() { + ::PROTOBUF_NAMESPACE_ID::internal::AssignDescriptors(&::descriptor_table_shiori_5fmessages_2eproto3); + return ::descriptor_table_shiori_5fmessages_2eproto3.file_level_metadata[kIndexInFileMessages]; + } + + public: + + // nested types ---------------------------------------------------- + + // accessors ------------------------------------------------------- + + enum : int { + kDatasetUuidFieldNumber = 1, + kFileUuidFieldNumber = 2, + kDataFieldNumber = 6, + kIsLastFieldNumber = 3, + kTypeFieldNumber = 4, + kPositionFieldNumber = 5, + }; + // string datasetUuid = 1; + void clear_datasetuuid(); + const std::string& datasetuuid() const; + void set_datasetuuid(const std::string& value); + void set_datasetuuid(std::string&& value); + void set_datasetuuid(const char* value); + void set_datasetuuid(const char* value, size_t size); + std::string* mutable_datasetuuid(); + std::string* release_datasetuuid(); + void set_allocated_datasetuuid(std::string* datasetuuid); + GOOGLE_PROTOBUF_RUNTIME_DEPRECATED("The unsafe_arena_ accessors for" + " string fields are deprecated and will be removed in a" + " future release.") + std::string* unsafe_arena_release_datasetuuid(); + GOOGLE_PROTOBUF_RUNTIME_DEPRECATED("The unsafe_arena_ accessors for" + " string fields are deprecated and will be removed in a" + " future release.") + void unsafe_arena_set_allocated_datasetuuid( + std::string* datasetuuid); + private: + const std::string& _internal_datasetuuid() const; + void _internal_set_datasetuuid(const std::string& value); + std::string* _internal_mutable_datasetuuid(); + public: + + // string fileUuid = 2; + void clear_fileuuid(); + const std::string& fileuuid() const; + void set_fileuuid(const std::string& value); + void set_fileuuid(std::string&& value); + void set_fileuuid(const char* value); + void set_fileuuid(const char* value, size_t size); + std::string* mutable_fileuuid(); + std::string* release_fileuuid(); + void set_allocated_fileuuid(std::string* fileuuid); + GOOGLE_PROTOBUF_RUNTIME_DEPRECATED("The unsafe_arena_ accessors for" + " string fields are deprecated and will be removed in a" + " future release.") + std::string* unsafe_arena_release_fileuuid(); + GOOGLE_PROTOBUF_RUNTIME_DEPRECATED("The unsafe_arena_ accessors for" + " string fields are deprecated and will be removed in a" + " future release.") + void unsafe_arena_set_allocated_fileuuid( + std::string* fileuuid); + private: + const std::string& _internal_fileuuid() const; + void _internal_set_fileuuid(const std::string& value); + std::string* _internal_mutable_fileuuid(); + public: + + // bytes data = 6; + void clear_data(); + const std::string& data() const; + void set_data(const std::string& value); + void set_data(std::string&& value); + void set_data(const char* value); + void set_data(const void* value, size_t size); + std::string* mutable_data(); + std::string* release_data(); + void set_allocated_data(std::string* data); + GOOGLE_PROTOBUF_RUNTIME_DEPRECATED("The unsafe_arena_ accessors for" + " string fields are deprecated and will be removed in a" + " future release.") + std::string* unsafe_arena_release_data(); + GOOGLE_PROTOBUF_RUNTIME_DEPRECATED("The unsafe_arena_ accessors for" + " string fields are deprecated and will be removed in a" + " future release.") + void unsafe_arena_set_allocated_data( + std::string* data); + private: + const std::string& _internal_data() const; + void _internal_set_data(const std::string& value); + std::string* _internal_mutable_data(); + public: + + // bool isLast = 3; + void clear_islast(); + bool islast() const; + void set_islast(bool value); + private: + bool _internal_islast() const; + void _internal_set_islast(bool value); + public: + + // .UploadDataType type = 4; + void clear_type(); + ::UploadDataType type() const; + void set_type(::UploadDataType value); + private: + ::UploadDataType _internal_type() const; + void _internal_set_type(::UploadDataType value); + public: + + // uint64 position = 5; + void clear_position(); + ::PROTOBUF_NAMESPACE_ID::uint64 position() const; + void set_position(::PROTOBUF_NAMESPACE_ID::uint64 value); + private: + ::PROTOBUF_NAMESPACE_ID::uint64 _internal_position() const; + void _internal_set_position(::PROTOBUF_NAMESPACE_ID::uint64 value); + public: + + // @@protoc_insertion_point(class_scope:FileUpload_Message) + private: + class _Internal; + + template friend class ::PROTOBUF_NAMESPACE_ID::Arena::InternalHelper; + typedef void InternalArenaConstructable_; + typedef void DestructorSkippable_; + ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr datasetuuid_; + ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr fileuuid_; + ::PROTOBUF_NAMESPACE_ID::internal::ArenaStringPtr data_; + bool islast_; + int type_; + ::PROTOBUF_NAMESPACE_ID::uint64 position_; + mutable ::PROTOBUF_NAMESPACE_ID::internal::CachedSize _cached_size_; + friend struct ::TableStruct_shiori_5fmessages_2eproto3; +}; +// =================================================================== + + +// =================================================================== + +#ifdef __GNUC__ + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Wstrict-aliasing" +#endif // __GNUC__ +// FileUpload_Message + +// string datasetUuid = 1; +inline void FileUpload_Message::clear_datasetuuid() { + datasetuuid_.ClearToEmpty(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), GetArena()); +} +inline const std::string& FileUpload_Message::datasetuuid() const { + // @@protoc_insertion_point(field_get:FileUpload_Message.datasetUuid) + return _internal_datasetuuid(); +} +inline void FileUpload_Message::set_datasetuuid(const std::string& value) { + _internal_set_datasetuuid(value); + // @@protoc_insertion_point(field_set:FileUpload_Message.datasetUuid) +} +inline std::string* FileUpload_Message::mutable_datasetuuid() { + // @@protoc_insertion_point(field_mutable:FileUpload_Message.datasetUuid) + return _internal_mutable_datasetuuid(); +} +inline const std::string& FileUpload_Message::_internal_datasetuuid() const { + return datasetuuid_.Get(); +} +inline void FileUpload_Message::_internal_set_datasetuuid(const std::string& value) { + + datasetuuid_.Set(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), value, GetArena()); +} +inline void FileUpload_Message::set_datasetuuid(std::string&& value) { + + datasetuuid_.Set( + &::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), ::std::move(value), GetArena()); + // @@protoc_insertion_point(field_set_rvalue:FileUpload_Message.datasetUuid) +} +inline void FileUpload_Message::set_datasetuuid(const char* value) { + GOOGLE_DCHECK(value != nullptr); + + datasetuuid_.Set(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), ::std::string(value), + GetArena()); + // @@protoc_insertion_point(field_set_char:FileUpload_Message.datasetUuid) +} +inline void FileUpload_Message::set_datasetuuid(const char* value, + size_t size) { + + datasetuuid_.Set(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), ::std::string( + reinterpret_cast(value), size), GetArena()); + // @@protoc_insertion_point(field_set_pointer:FileUpload_Message.datasetUuid) +} +inline std::string* FileUpload_Message::_internal_mutable_datasetuuid() { + + return datasetuuid_.Mutable(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), GetArena()); +} +inline std::string* FileUpload_Message::release_datasetuuid() { + // @@protoc_insertion_point(field_release:FileUpload_Message.datasetUuid) + return datasetuuid_.Release(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), GetArena()); +} +inline void FileUpload_Message::set_allocated_datasetuuid(std::string* datasetuuid) { + if (datasetuuid != nullptr) { + + } else { + + } + datasetuuid_.SetAllocated(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), datasetuuid, + GetArena()); + // @@protoc_insertion_point(field_set_allocated:FileUpload_Message.datasetUuid) +} +inline std::string* FileUpload_Message::unsafe_arena_release_datasetuuid() { + // @@protoc_insertion_point(field_unsafe_arena_release:FileUpload_Message.datasetUuid) + GOOGLE_DCHECK(GetArena() != nullptr); + + return datasetuuid_.UnsafeArenaRelease(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), + GetArena()); +} +inline void FileUpload_Message::unsafe_arena_set_allocated_datasetuuid( + std::string* datasetuuid) { + GOOGLE_DCHECK(GetArena() != nullptr); + if (datasetuuid != nullptr) { + + } else { + + } + datasetuuid_.UnsafeArenaSetAllocated(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), + datasetuuid, GetArena()); + // @@protoc_insertion_point(field_unsafe_arena_set_allocated:FileUpload_Message.datasetUuid) +} + +// string fileUuid = 2; +inline void FileUpload_Message::clear_fileuuid() { + fileuuid_.ClearToEmpty(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), GetArena()); +} +inline const std::string& FileUpload_Message::fileuuid() const { + // @@protoc_insertion_point(field_get:FileUpload_Message.fileUuid) + return _internal_fileuuid(); +} +inline void FileUpload_Message::set_fileuuid(const std::string& value) { + _internal_set_fileuuid(value); + // @@protoc_insertion_point(field_set:FileUpload_Message.fileUuid) +} +inline std::string* FileUpload_Message::mutable_fileuuid() { + // @@protoc_insertion_point(field_mutable:FileUpload_Message.fileUuid) + return _internal_mutable_fileuuid(); +} +inline const std::string& FileUpload_Message::_internal_fileuuid() const { + return fileuuid_.Get(); +} +inline void FileUpload_Message::_internal_set_fileuuid(const std::string& value) { + + fileuuid_.Set(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), value, GetArena()); +} +inline void FileUpload_Message::set_fileuuid(std::string&& value) { + + fileuuid_.Set( + &::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), ::std::move(value), GetArena()); + // @@protoc_insertion_point(field_set_rvalue:FileUpload_Message.fileUuid) +} +inline void FileUpload_Message::set_fileuuid(const char* value) { + GOOGLE_DCHECK(value != nullptr); + + fileuuid_.Set(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), ::std::string(value), + GetArena()); + // @@protoc_insertion_point(field_set_char:FileUpload_Message.fileUuid) +} +inline void FileUpload_Message::set_fileuuid(const char* value, + size_t size) { + + fileuuid_.Set(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), ::std::string( + reinterpret_cast(value), size), GetArena()); + // @@protoc_insertion_point(field_set_pointer:FileUpload_Message.fileUuid) +} +inline std::string* FileUpload_Message::_internal_mutable_fileuuid() { + + return fileuuid_.Mutable(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), GetArena()); +} +inline std::string* FileUpload_Message::release_fileuuid() { + // @@protoc_insertion_point(field_release:FileUpload_Message.fileUuid) + return fileuuid_.Release(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), GetArena()); +} +inline void FileUpload_Message::set_allocated_fileuuid(std::string* fileuuid) { + if (fileuuid != nullptr) { + + } else { + + } + fileuuid_.SetAllocated(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), fileuuid, + GetArena()); + // @@protoc_insertion_point(field_set_allocated:FileUpload_Message.fileUuid) +} +inline std::string* FileUpload_Message::unsafe_arena_release_fileuuid() { + // @@protoc_insertion_point(field_unsafe_arena_release:FileUpload_Message.fileUuid) + GOOGLE_DCHECK(GetArena() != nullptr); + + return fileuuid_.UnsafeArenaRelease(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), + GetArena()); +} +inline void FileUpload_Message::unsafe_arena_set_allocated_fileuuid( + std::string* fileuuid) { + GOOGLE_DCHECK(GetArena() != nullptr); + if (fileuuid != nullptr) { + + } else { + + } + fileuuid_.UnsafeArenaSetAllocated(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), + fileuuid, GetArena()); + // @@protoc_insertion_point(field_unsafe_arena_set_allocated:FileUpload_Message.fileUuid) +} + +// bool isLast = 3; +inline void FileUpload_Message::clear_islast() { + islast_ = false; +} +inline bool FileUpload_Message::_internal_islast() const { + return islast_; +} +inline bool FileUpload_Message::islast() const { + // @@protoc_insertion_point(field_get:FileUpload_Message.isLast) + return _internal_islast(); +} +inline void FileUpload_Message::_internal_set_islast(bool value) { + + islast_ = value; +} +inline void FileUpload_Message::set_islast(bool value) { + _internal_set_islast(value); + // @@protoc_insertion_point(field_set:FileUpload_Message.isLast) +} + +// .UploadDataType type = 4; +inline void FileUpload_Message::clear_type() { + type_ = 0; +} +inline ::UploadDataType FileUpload_Message::_internal_type() const { + return static_cast< ::UploadDataType >(type_); +} +inline ::UploadDataType FileUpload_Message::type() const { + // @@protoc_insertion_point(field_get:FileUpload_Message.type) + return _internal_type(); +} +inline void FileUpload_Message::_internal_set_type(::UploadDataType value) { + + type_ = value; +} +inline void FileUpload_Message::set_type(::UploadDataType value) { + _internal_set_type(value); + // @@protoc_insertion_point(field_set:FileUpload_Message.type) +} + +// uint64 position = 5; +inline void FileUpload_Message::clear_position() { + position_ = PROTOBUF_ULONGLONG(0); +} +inline ::PROTOBUF_NAMESPACE_ID::uint64 FileUpload_Message::_internal_position() const { + return position_; +} +inline ::PROTOBUF_NAMESPACE_ID::uint64 FileUpload_Message::position() const { + // @@protoc_insertion_point(field_get:FileUpload_Message.position) + return _internal_position(); +} +inline void FileUpload_Message::_internal_set_position(::PROTOBUF_NAMESPACE_ID::uint64 value) { + + position_ = value; +} +inline void FileUpload_Message::set_position(::PROTOBUF_NAMESPACE_ID::uint64 value) { + _internal_set_position(value); + // @@protoc_insertion_point(field_set:FileUpload_Message.position) +} + +// bytes data = 6; +inline void FileUpload_Message::clear_data() { + data_.ClearToEmpty(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), GetArena()); +} +inline const std::string& FileUpload_Message::data() const { + // @@protoc_insertion_point(field_get:FileUpload_Message.data) + return _internal_data(); +} +inline void FileUpload_Message::set_data(const std::string& value) { + _internal_set_data(value); + // @@protoc_insertion_point(field_set:FileUpload_Message.data) +} +inline std::string* FileUpload_Message::mutable_data() { + // @@protoc_insertion_point(field_mutable:FileUpload_Message.data) + return _internal_mutable_data(); +} +inline const std::string& FileUpload_Message::_internal_data() const { + return data_.Get(); +} +inline void FileUpload_Message::_internal_set_data(const std::string& value) { + + data_.Set(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), value, GetArena()); +} +inline void FileUpload_Message::set_data(std::string&& value) { + + data_.Set( + &::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), ::std::move(value), GetArena()); + // @@protoc_insertion_point(field_set_rvalue:FileUpload_Message.data) +} +inline void FileUpload_Message::set_data(const char* value) { + GOOGLE_DCHECK(value != nullptr); + + data_.Set(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), ::std::string(value), + GetArena()); + // @@protoc_insertion_point(field_set_char:FileUpload_Message.data) +} +inline void FileUpload_Message::set_data(const void* value, + size_t size) { + + data_.Set(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), ::std::string( + reinterpret_cast(value), size), GetArena()); + // @@protoc_insertion_point(field_set_pointer:FileUpload_Message.data) +} +inline std::string* FileUpload_Message::_internal_mutable_data() { + + return data_.Mutable(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), GetArena()); +} +inline std::string* FileUpload_Message::release_data() { + // @@protoc_insertion_point(field_release:FileUpload_Message.data) + return data_.Release(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), GetArena()); +} +inline void FileUpload_Message::set_allocated_data(std::string* data) { + if (data != nullptr) { + + } else { + + } + data_.SetAllocated(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), data, + GetArena()); + // @@protoc_insertion_point(field_set_allocated:FileUpload_Message.data) +} +inline std::string* FileUpload_Message::unsafe_arena_release_data() { + // @@protoc_insertion_point(field_unsafe_arena_release:FileUpload_Message.data) + GOOGLE_DCHECK(GetArena() != nullptr); + + return data_.UnsafeArenaRelease(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), + GetArena()); +} +inline void FileUpload_Message::unsafe_arena_set_allocated_data( + std::string* data) { + GOOGLE_DCHECK(GetArena() != nullptr); + if (data != nullptr) { + + } else { + + } + data_.UnsafeArenaSetAllocated(&::PROTOBUF_NAMESPACE_ID::internal::GetEmptyStringAlreadyInited(), + data, GetArena()); + // @@protoc_insertion_point(field_unsafe_arena_set_allocated:FileUpload_Message.data) +} + +#ifdef __GNUC__ + #pragma GCC diagnostic pop +#endif // __GNUC__ + +// @@protoc_insertion_point(namespace_scope) + + +PROTOBUF_NAMESPACE_OPEN + +template <> struct is_proto_enum< ::UploadDataType> : ::std::true_type {}; +template <> +inline const EnumDescriptor* GetEnumDescriptor< ::UploadDataType>() { + return ::UploadDataType_descriptor(); +} + +PROTOBUF_NAMESPACE_CLOSE + +// @@protoc_insertion_point(global_scope) + +#include +#endif // GOOGLE_PROTOBUF_INCLUDED_GOOGLE_PROTOBUF_INCLUDED_shiori_5fmessages_2eproto3 diff --git a/src/frontend/Hanami-AI-Dashboard/src/login.html b/src/frontend/Hanami-AI-Dashboard/src/login.html new file mode 100644 index 00000000..d7ac5b59 --- /dev/null +++ b/src/frontend/Hanami-AI-Dashboard/src/login.html @@ -0,0 +1,97 @@ + + + + + + + + +
+
+ + + +
+ + + + + + + + + + + + + + + + + + + + + diff --git a/src/frontend/Hanami-AI-Dashboard/src/styles/base.css b/src/frontend/Hanami-AI-Dashboard/src/styles/base.css new file mode 100644 index 00000000..8aeb94e1 --- /dev/null +++ b/src/frontend/Hanami-AI-Dashboard/src/styles/base.css @@ -0,0 +1,392 @@ +/* Apache License Version 2.0 + +Copyright 2020 Tobias Anker + +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. +*/ + +:root { + --color-background: #2a2f33; + --color-tile: #3b3f42; + --color-higlight-field: #45494d; + + --color-shadow: #292929; + --color-text: #ffffff; + --color-text-light: #8e9396; + --color-table: #ffffff; + --color-highlight: rgb(247, 181, 225); + + --text-font-family: Verdana, sans-serif, Arial, Helvetica; + --text-font-size: 1.0rem; + + --box-shadow: 0 0.5rem 0.5rem var(--color-shadow); + --box-shadow-footer: 0 -0.5rem 0.5rem var(--color-shadow); + --box-shadow-header: 0 0.5rem 0.5rem var(--color-shadow); +} + +/*================================================= +Generic objects +=================================================*/ + +* { + margin: 0; + padding: 0; + outline: 0; + border: 0; + box-sizing: border-box; +} + +body { + background-color: var(--color-background); + width: 100%; + height: 100%; + font-family: var(--text-font-family); + font-size: var(--text-font-size); + user-select: none; + overflow-x: hidden; + line-height: 1.5; + + /* scalse entire page */ + /*transform: scale(1.4); + transform-origin: 0 0; */ +} + +a { + color: var(--color-text); +} + +button { + color: var(--color-text); +} + +button:hover { + filter: brightness(85%); +} + +button:active { + filter: brightness(70%); +} + +h1 { + font-size: 2.0rem; +} + +input[type="text"] { + border: 0; + outline: 0; + background: transparent; + color: var(--color-text); + border-bottom: 1px solid var(--color-text-light); + padding: 8px; + width: 100%; +} + +input[type="password"] { + border: 0; + outline: 0; + background: transparent; + color: var(--color-text); + border-bottom: 1px solid var(--color-text-light); + padding: 8px; + width: 100%; +} + +input[type="file"] { + border: 0; + outline: 0; + background: transparent; + color: invert(var(--color-text)); + padding: 8px; + width: 100%; +} + +/*button for file-selector*/ +input::file-selector-button { + width: 10rem; + height: 2rem; + margin-bottom: 0.5rem; + border: thin solid grey; +} + +textarea { + background: transparent; + color: var(--color-text); + border: 1px solid var(--color-text-light); + padding: 8px; + width: 100%; +} + +label { + display: inline-block; + width: 100%; + text-align: left; +} + +/*================================================= +Base site +=================================================*/ + +#avatar { + width: 2.0rem; + height: 2.0rem; + border-radius: 50%; + overflow: hidden; /* hide scrollbar */ +} + +#kitsunemimi_top { + background-color: var(--color-tile); + overflow: auto; + height: 4.2rem; + width: 100%; + left: 0rem; + top: 0rem; + position: absolute; + box-shadow: var(--box-shadow-header); + min-height: 150; +} + +#header_user_name { + color: var(--color-text); + font-size: 1.5rem; + font-style: italic; + text-align: right; + padding-top: 0.75rem; + padding-right: 2rem; + margin: auto; +} + +#sidebar_div { + background-color: var(--color-tile); + height: 40rem; + width: 15rem; + left: 0.8rem; + top: 5.5rem; + position: absolute; +} + +#content_div { + height: 50rem; + width: calc(100% - 17.4rem); + left: 16.6rem; + top: 5.5rem; + position: absolute; +} + +.create_button { + color: invert(var(--color-text)); + width: 10.0rem; + height: 3.0rem; + padding-top: 0.5rem; + padding-bottom: 0.5rem; + padding-left: 0.5rem; + padding-right: 0.5rem; + margin-bottom: 1.5rem; + margin-left: 0.5rem; + margin-right: 0.5rem; + box-shadow: var(--box-shadow); +} + +.generic_button { + color: invert(var(--color-text)); + width: 10.0rem; + height: 3.0rem; + box-shadow: var(--box-shadow); +} + +.generic_svg_button { + background-color: var(--color-tile); + margin-top: 0.5rem; + width: 3rem; + height: 3rem; +} + +.generic_svg_on_button { + filter: invert(1); + width: 3rem; + height: 3rem; +} + +.table_side_button { + color: rgb(0, 0, 0); + width: auto; + height: 1.5rem; + box-shadow: var(--box-shadow); + padding-left: 0.3rem; + padding-right: 0.3rem; + margin: 0.5rem 0.5rem 0.5rem 0.5rem; +} + +.tile_content_div { + background-color: var(--color-tile); + color: var(--color-text); + padding-top: 2.5rem; + padding-bottom: 2.5rem; + padding-left: 0.5rem; + padding-right: 0.5rem; + margin-bottom: 1.0rem; + margin-left: 0.5rem; + margin-right: 0.5rem; + margin-top: -53px; + margin-bottom: -20px; + align-items: center; + box-shadow: var(--box-shadow); +} + +.plain_text_div { + overflow-wrap: break-word; + white-space: pre-wrap; +} + +.tile_div { + margin-bottom: 3rem; +} + +.hexagon_field { + color: rgb(0, 0, 0); + fill: white; + font-weight: bold; + filter: drop-shadow(var(--box-shadow-header)); +} + +.header_label_position_long { + transform: translate(calc(50% - 200px), 0%); +} + +.header_label_position_mid { + transform: translate(calc(50% - 150px), 0%); +} + +.header_label_position_short { + transform: translate(calc(50% - 100px), 0%); +} + +.add_button { + background-color: var(--color-highlight); + color: rgb(0, 0, 0); + width: 2.0rem; + height: 2.0rem; + margin-top: -2rem; + float: right; + position: relative; /* necessary for the z-index */ + z-index: 1; + box-shadow: var(--box-shadow); +} + +.delete_label_text { + vertical-align:middle; + text-align:center; + width: 30rem; +} + +/*================================================= +ON-OFF-Slider of checkboxes +=================================================*/ + +.switch { + display: inline-block; + height: 34px; + position: relative; + width: 60px; +} + +.switch input { + display:none; +} + +.slider { + background-color: #ccc; + bottom: 0; + cursor: pointer; + left: 0; + position: absolute; + right: 0; + top: 0; + transition: .4s; +} + +.slider:before { + background-color: #fff; + bottom: 4px; + content: ""; + height: 26px; + left: 4px; + position: absolute; + transition: .4s; + width: 26px; +} + +input:checked + .slider { + background-color: #66bb6a; +} + +input:checked + .slider:before { + transform: translateX(26px); +} + +select { + width: 100%; + height: 2.5rem; + margin-bottom: 0.5rem; + border: thin solid grey; +} + +/*================================================= +ALERTS +=================================================*/ + +.alert { + padding: 20px; + background-color: #f44336; + color: white; +} + +.alertCloseBtn { + margin-left: 15px; + color: white; + font-weight: bold; + float: right; + font-size: 22px; + line-height: 20px; + cursor: pointer; + transition: 0.3s; +} + +.alertCloseBtn:hover { + color: black; +} + +/*================================================= +Diagramms +=================================================*/ + +.diagram_axis{ + color: var(--color-text); +} + +.diagram_axis_text{ + fill: var(--color-text); + font-family: var(--text-font-family); +} + +.diagram_title{ + fill: var(--color-text); +} + +.diagram_path { + stroke: var(--color-highlight); +} + +.diagram_grid { + stroke: 1px; + color: var(--color-text-light); +} \ No newline at end of file diff --git a/src/frontend/Hanami-AI-Dashboard/src/styles/modal.css b/src/frontend/Hanami-AI-Dashboard/src/styles/modal.css new file mode 100644 index 00000000..6ed0302a --- /dev/null +++ b/src/frontend/Hanami-AI-Dashboard/src/styles/modal.css @@ -0,0 +1,123 @@ +/* Apache License Version 2.0 + +Copyright 2020 Tobias Anker + +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. +*/ + +.modal { + display: none; + position: fixed; + z-index: 2; + left: 0; + top: 0; + width: 100%; + height: 100%; + overflow: auto; + background-color: rgba(0,0,0,0.6); +} + +@-webkit-keyframes modal-animation { + from { + top: -100px; + opacity: 0; + } + to { + top: 0px; + opacity: 1; + } +} + +@keyframes modal-animation { + from { + top: -100px; + opacity: 0; + } + to { + top: 0px; + opacity: 1; + } +} + +.modal_header_position { + transform: translate(0, -35px); +} + +.modal_footer_position { + transform: translate(0, 44px); +} + +.modal_header_part { + background-color: var(--color-tile); + height: 4rem; + padding-top: 1rem; + text-align: center; + box-shadow: var(--box-shadow-header); +} + +.modal_content_part { + box-shadow: var(--box-shadow-header); + height: calc(100% - 6rem); +} + +.modal_input_fields { + width: auto; + margin-top: 2rem; + margin-bottom: 2rem; + margin-left: 2rem; + margin-right: 2rem; +} + +.modal_footer_part { + background-color: var(--color-tile); + height: 4rem; + box-shadow: var(--box-shadow-footer); +} + +.modal_content { + background-color: #31363b; + margin: 5% auto; + height: 30rem; + width: 40rem; + + -webkit-animation-name: modal-animation; + -webkit-animation-duration: 0.5s; + animation-name: modal-animation; + animation-duration: 0.5s; +} + +.modal_label { + white-space: pre-wrap; /* to allow multi-line lables */ + display: inline-block; + text-align: left; + margin-bottom: 10px; + color: var(--color-text); +} + +.modal_accept_button { + margin-right: 10rem; +} + +.close { + color: #aaa; + float: right; + font-size: 28px; + font-weight: bold; +} + +.close:hover, +.close:focus { + color: black; + text-decoration: none; + cursor: pointer; +} diff --git a/src/frontend/Hanami-AI-Dashboard/src/styles/sidebar.css b/src/frontend/Hanami-AI-Dashboard/src/styles/sidebar.css new file mode 100644 index 00000000..7659298a --- /dev/null +++ b/src/frontend/Hanami-AI-Dashboard/src/styles/sidebar.css @@ -0,0 +1,78 @@ +/* Apache License Version 2.0 + +Copyright 2020 Tobias Anker + +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. +*/ + +aside { + height: 100%; + width: 100%; + box-shadow: var(--box-shadow); +} + +aside .sidebar { + display: flex; + flex-direction: column; + position: relative; /* necessary for the top-value below */ + top: 1rem; +} + +aside .sidebar button { + display: flex; + align-items: center; + position: relative; + height: 3rem; + padding-left: 1rem; + background-color: var(--color-tile); +} + +aside .sidebar a { + display: flex; + margin-left: 1rem; + gap: 1rem; + align-items: center; + position: relative; + height: 2rem; +} + + +aside .sidebar button.active { + background-color: var(--color-higlight-field); + color: var(--color-text) +} + +aside .sidebar a.active { + background-color: var(--color-highlight); + color: invert(var(--color-text)) +} + +.sidebar_drop_content { + background-color: var(--color-shadow); + position:relative; + overflow: hidden; /* avoid content-overlapping when max-height is set to 0 */ + padding-left: 0.5rem; + -webkit-transition: max-height .5s; + -o-transition: max-height .5s; + transition: max-height .5s; +} + +.sidebar_dropdown_entry:hover { + background-color: var(--color-highlight); + color: black; + transition: all 200ms; +} + +.sidebar_dropdown_entry { + text-indent: 0.5em; +} \ No newline at end of file diff --git a/src/frontend/Hanami-AI-Dashboard/src/styles/table.css b/src/frontend/Hanami-AI-Dashboard/src/styles/table.css new file mode 100644 index 00000000..c5e4d05d --- /dev/null +++ b/src/frontend/Hanami-AI-Dashboard/src/styles/table.css @@ -0,0 +1,51 @@ +/* Apache License Version 2.0 + +Copyright 2020 Tobias Anker + +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. +*/ + +table { + text-align: left; + color: var(--color-text); + border-collapse: collapse; + table-layout: auto; + width: 95%; + margin-left: auto; + margin-right: auto; +} + +table tr { + height: 38px; + border-bottom: 1px solid var(--color-table); +} + +/* make first column mono-space, because it contains the UUID, which is easier to read this way */ +td:nth-child(1) { + font-family: Dejavu Sans Mono; + width: 25rem; +} + +table tr th { + white-space: nowrap; + padding-left: 0.5rem; + padding-right: 0.5rem; +} + +table tr td { + padding-left: 0.5rem; + padding-right: 0.5rem; + + word-wrap: break-word; + overflow-wrap: break-word; +} diff --git a/src/frontend/Hanami-AI-Dashboard/src/styles/tabs.css b/src/frontend/Hanami-AI-Dashboard/src/styles/tabs.css new file mode 100644 index 00000000..a0f710b5 --- /dev/null +++ b/src/frontend/Hanami-AI-Dashboard/src/styles/tabs.css @@ -0,0 +1,50 @@ +/* Apache License Version 2.0 + +Copyright 2020 Tobias Anker + +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. +*/ + +.tab { + overflow: hidden; + background-color: var(--color-tile); + box-shadow: var(--box-shadow-header); + padding-left: 1rem; + padding-right: 1rem; + margin-left: -1rem; + margin-right: -1rem; +} + +.tab button { + background-color: inherit; + float: left; + border: none; + outline: none; + cursor: pointer; + padding: 14px 16px; + transition: 0.3s; + font-family: var(--text-font-family); + color: white; + font-size: large; +} + +/* Change background color of buttons on hover */ +.tab button:hover { + background-color: var(--color-background); +} + +/* Create an active/current tablink class */ +.tab button.active { + background-color: var(--color-text-light); + color: black; +} diff --git a/src/frontend/Hanami-AI-Dashboard/src/subsites/azuki/power.html b/src/frontend/Hanami-AI-Dashboard/src/subsites/azuki/power.html new file mode 100644 index 00000000..927006ac --- /dev/null +++ b/src/frontend/Hanami-AI-Dashboard/src/subsites/azuki/power.html @@ -0,0 +1,88 @@ + + + + + + +
+ + + CPU-Power - last 60 seconds + +
+
+ +
+ + + CPU-Power - last 60 minutes + +
+
+ +
+ + + CPU-Power - last 24 hours + +
+
+ +
+ + + CPU-Power - last 365 days + +
+
+ + + + + + + + + diff --git a/src/frontend/Hanami-AI-Dashboard/src/subsites/azuki/speed.html b/src/frontend/Hanami-AI-Dashboard/src/subsites/azuki/speed.html new file mode 100644 index 00000000..d2438cce --- /dev/null +++ b/src/frontend/Hanami-AI-Dashboard/src/subsites/azuki/speed.html @@ -0,0 +1,88 @@ + + + + + + +
+ + + CPU-Speed - last 60 seconds + +
+
+ +
+ + + CPU-Speed - last 60 minutes + +
+
+ +
+ + + CPU-Speed - last 24 hours + +
+
+ +
+ + + CPU-Speed - last 365 days + +
+
+ + + + + + + + + diff --git a/src/frontend/Hanami-AI-Dashboard/src/subsites/azuki/system_info.html b/src/frontend/Hanami-AI-Dashboard/src/subsites/azuki/system_info.html new file mode 100644 index 00000000..d0a0e869 --- /dev/null +++ b/src/frontend/Hanami-AI-Dashboard/src/subsites/azuki/system_info.html @@ -0,0 +1,52 @@ + + + + + + + + + Show Sytem-Info + + +
+ + + + + + + + diff --git a/src/frontend/Hanami-AI-Dashboard/src/subsites/azuki/temperature.html b/src/frontend/Hanami-AI-Dashboard/src/subsites/azuki/temperature.html new file mode 100644 index 00000000..416b2901 --- /dev/null +++ b/src/frontend/Hanami-AI-Dashboard/src/subsites/azuki/temperature.html @@ -0,0 +1,88 @@ + + + + + + +
+ + + CPU-Temperature - last 60 seconds + +
+
+ +
+ + + CPU-Temperature - last 60 minutes + +
+
+ +
+ + + CPU-Temperature - last 24 hours + +
+
+ +
+ + + CPU-Temperature - last 365 days + +
+
+ + + + + + + + + diff --git a/src/frontend/Hanami-AI-Dashboard/src/subsites/azuki/thread_mapping.html b/src/frontend/Hanami-AI-Dashboard/src/subsites/azuki/thread_mapping.html new file mode 100644 index 00000000..a011f40d --- /dev/null +++ b/src/frontend/Hanami-AI-Dashboard/src/subsites/azuki/thread_mapping.html @@ -0,0 +1,52 @@ + + + + + + + + + Thread-Mapping + + +
+ + + + + + + + diff --git a/src/frontend/Hanami-AI-Dashboard/src/subsites/kyouko/cluster.html b/src/frontend/Hanami-AI-Dashboard/src/subsites/kyouko/cluster.html new file mode 100644 index 00000000..763ca17b --- /dev/null +++ b/src/frontend/Hanami-AI-Dashboard/src/subsites/kyouko/cluster.html @@ -0,0 +1,415 @@ + + + + + + + + + Cluster + + +
+ +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/frontend/Hanami-AI-Dashboard/src/subsites/kyouko/segment_template.html b/src/frontend/Hanami-AI-Dashboard/src/subsites/kyouko/segment_template.html new file mode 100644 index 00000000..ef3f998b --- /dev/null +++ b/src/frontend/Hanami-AI-Dashboard/src/subsites/kyouko/segment_template.html @@ -0,0 +1,247 @@ + + + + + + + + + Segment Template + + +
+ +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/frontend/Hanami-AI-Dashboard/src/subsites/kyouko/task.html b/src/frontend/Hanami-AI-Dashboard/src/subsites/kyouko/task.html new file mode 100644 index 00000000..850382c9 --- /dev/null +++ b/src/frontend/Hanami-AI-Dashboard/src/subsites/kyouko/task.html @@ -0,0 +1,248 @@ + + + + + + + + + Task + + +
+ +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/frontend/Hanami-AI-Dashboard/src/subsites/misaki/project.html b/src/frontend/Hanami-AI-Dashboard/src/subsites/misaki/project.html new file mode 100644 index 00000000..d1de1e29 --- /dev/null +++ b/src/frontend/Hanami-AI-Dashboard/src/subsites/misaki/project.html @@ -0,0 +1,243 @@ + + + + + + + + + Project + + +
+ +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/frontend/Hanami-AI-Dashboard/src/subsites/misaki/user.html b/src/frontend/Hanami-AI-Dashboard/src/subsites/misaki/user.html new file mode 100644 index 00000000..42832b1a --- /dev/null +++ b/src/frontend/Hanami-AI-Dashboard/src/subsites/misaki/user.html @@ -0,0 +1,441 @@ + + + + + + + + + User + + +
+ +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/frontend/Hanami-AI-Dashboard/src/subsites/shiori/audit_log.html b/src/frontend/Hanami-AI-Dashboard/src/subsites/shiori/audit_log.html new file mode 100644 index 00000000..4c2a7c49 --- /dev/null +++ b/src/frontend/Hanami-AI-Dashboard/src/subsites/shiori/audit_log.html @@ -0,0 +1,95 @@ + + + + + + + + + Audit Log + + +
+
+
+ + + + + + + + + + + + + + + + diff --git a/src/frontend/Hanami-AI-Dashboard/src/subsites/shiori/cluster_snapshot.html b/src/frontend/Hanami-AI-Dashboard/src/subsites/shiori/cluster_snapshot.html new file mode 100644 index 00000000..f71c43ad --- /dev/null +++ b/src/frontend/Hanami-AI-Dashboard/src/subsites/shiori/cluster_snapshot.html @@ -0,0 +1,149 @@ + + + + + + + + + Cluster Snapshots + + +
+
+
+ + + + + + + + + + + + + + + + + + + + + + diff --git a/src/frontend/Hanami-AI-Dashboard/src/subsites/shiori/dataset.html b/src/frontend/Hanami-AI-Dashboard/src/subsites/shiori/dataset.html new file mode 100644 index 00000000..273b3d20 --- /dev/null +++ b/src/frontend/Hanami-AI-Dashboard/src/subsites/shiori/dataset.html @@ -0,0 +1,341 @@ + + + + + + + + + Data-sets + + +
+ +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/frontend/Hanami-AI-Dashboard/src/subsites/shiori/graphical_result.html b/src/frontend/Hanami-AI-Dashboard/src/subsites/shiori/graphical_result.html new file mode 100644 index 00000000..2bf33777 --- /dev/null +++ b/src/frontend/Hanami-AI-Dashboard/src/subsites/shiori/graphical_result.html @@ -0,0 +1,74 @@ + + + + + + +
+ + + Request result + +
+
+ +
+ + + Request result (rounded) + +
+
+ + + + + + + + diff --git a/src/frontend/Hanami-AI-Dashboard/src/subsites/shiori/request_result.html b/src/frontend/Hanami-AI-Dashboard/src/subsites/shiori/request_result.html new file mode 100644 index 00000000..1975dff7 --- /dev/null +++ b/src/frontend/Hanami-AI-Dashboard/src/subsites/shiori/request_result.html @@ -0,0 +1,182 @@ + + + + + + + + + Results + + +
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/libraries/libAzukiHeart/CHANGELOG_old b/src/libraries/libAzukiHeart/CHANGELOG_old new file mode 100644 index 00000000..72acd9f2 --- /dev/null +++ b/src/libraries/libAzukiHeart/CHANGELOG_old @@ -0,0 +1,13 @@ +# Changelog + +## [0.2.0] - 2022-07-02 + +### Added +- allow binding to multiple cpu-threads at the same time + + +## [0.1.0] - 2022-02-13 + +### Added +- funtions to request and set thread-bindings + diff --git a/src/libraries/libAzukiHeart/README.md b/src/libraries/libAzukiHeart/README.md new file mode 100644 index 00000000..989cc650 --- /dev/null +++ b/src/libraries/libAzukiHeart/README.md @@ -0,0 +1,2 @@ +# libAzukiHeart + diff --git a/src/libraries/libAzukiHeart/build.sh b/src/libraries/libAzukiHeart/build.sh new file mode 100755 index 00000000..db26c2d8 --- /dev/null +++ b/src/libraries/libAzukiHeart/build.sh @@ -0,0 +1,126 @@ +#!/bin/bash + +# get current directory-path and the path of the parent-directory +DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )" +PARENT_DIR="$(dirname "$DIR")" +CURRENT_BRANCH=$(git rev-parse --abbrev-ref HEAD) + +# create build-directory +BUILD_DIR="$PARENT_DIR/build" +mkdir -p $BUILD_DIR + +# create directory for the final result +RESULT_DIR="$PARENT_DIR/result" +mkdir -p $RESULT_DIR + +#----------------------------------------------------------------------------------------------------------------- + +function build_kitsune_lib_repo () { + REPO_NAME=$1 + NUMBER_OF_THREADS=$2 + ADDITIONAL_CONFIGS=$3 + + # create build directory for repo and go into this directory + REPO_DIR="$BUILD_DIR/$REPO_NAME" + mkdir -p $REPO_DIR + cd $REPO_DIR + + # build repo library with qmake + /usr/lib/x86_64-linux-gnu/qt5/bin/qmake "$PARENT_DIR/$REPO_NAME/$REPO_NAME.pro" -spec linux-g++ "CONFIG += optimize_full staticlib $ADDITIONAL_CONFIGS" + /usr/bin/make -j$NUMBER_OF_THREADS + + # copy build-result and include-files into the result-directory + echo "----------------------------------------------------------------------" + echo $RESULT_DIR + cp $REPO_DIR/src/$REPO_NAME.a $RESULT_DIR/ + cp -r $PARENT_DIR/$REPO_NAME/include $RESULT_DIR/ + ls -l $RESULT_DIR/include/ + ls -l $RESULT_DIR +} + +function download_repo_github () { + REPO_NAME=$1 + TAG_OR_BRANCH=$2 + + echo "" + echo "" + echo "++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++" + echo "$REPO_NAME" + echo "++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++" + echo "Branch/Tag: $TAG_OR_BRANCH" + + # clone repo + git clone https://github.com/kitsudaiki/$REPO_NAME.git "$PARENT_DIR/$REPO_NAME" + cd "$PARENT_DIR/$REPO_NAME" + + # checkout branch + if [[ $CURRENT_BRANCH =~ ^tag.* ]] || [[ $CURRENT_BRANCH =~ ^hotfix.* ]] || [[ $CURRENT_BRANCH =~ ^v.* ]] || [[ $CURRENT_BRANCH =~ ^rolling$ ]] || [[ $CURRENT_BRANCH =~ ^staging$ ]]; then + # if a stable branch, then use the defined tag of branch + # check if defined branch even exist + BRANCH_EXIST=$(git ls-remote --heads origin $TAG_OR_BRANCH) + if [[ -z "$BRANCH_EXIST" ]]; then + echo "" + echo "-------------------------------------------------------------------------------------" + echo "Branch or tag '$TAG_OR_BRANCH' does not exist for the repository '$REPO_NAME'" + echo "-------------------------------------------------------------------------------------" + echo "" + exit 1 + fi + git checkout $TAG_OR_BRANCH + else + # if develop or feature branch, then try to checkout the feature-branch in the other repo as well + # or otherwise use the develop-branch as default + BRANCH_EXIST=$(git ls-remote --heads origin $CURRENT_BRANCH) + if [[ -n "$BRANCH_EXIST" ]]; then + git checkout $CURRENT_BRANCH + else + git checkout develop + fi + fi +} + +function get_required_kitsune_lib_repo () { + REPO_NAME=$1 + TAG_OR_BRANCH=$2 + NUMBER_OF_THREADS=$3 + + download_repo_github $REPO_NAME $TAG_OR_BRANCH + build_kitsune_lib_repo $REPO_NAME $NUMBER_OF_THREADS +} + +#----------------------------------------------------------------------------------------------------------------- + +echo "" +echo "###########################################################################################################" +echo "" +get_required_kitsune_lib_repo "libKitsunemimiCommon" "develop" 8 +get_required_kitsune_lib_repo "libKitsunemimiJson" "develop" 1 +get_required_kitsune_lib_repo "libKitsunemimiIni" "develop" 1 +get_required_kitsune_lib_repo "libKitsunemimiNetwork" "develop" 8 +get_required_kitsune_lib_repo "libKitsunemimiArgs" "develop" 8 +get_required_kitsune_lib_repo "libKitsunemimiConfig" "develop" 8 +get_required_kitsune_lib_repo "libKitsunemimiCrypto" "develop" 8 +get_required_kitsune_lib_repo "libKitsunemimiJwt" "develop" 8 +echo "" +echo "###########################################################################################################" +echo "" +get_required_kitsune_lib_repo "libKitsunemimiSakuraNetwork" "develop" 8 +echo "" +echo "###########################################################################################################" +echo "" +get_required_kitsune_lib_repo "libKitsunemimiHanamiCommon" "develop" 8 +get_required_kitsune_lib_repo "libKitsunemimiHanamiNetwork" "develop" 8 +download_repo_github "libKitsunemimiHanamiMessages" "develop" +echo "" +echo "###########################################################################################################" + +#----------------------------------------------------------------------------------------------------------------- + +if [ $1 = "test" ]; then + build_kitsune_lib_repo "libAzukiHeart" 8 "run_tests" +else + build_kitsune_lib_repo "libAzukiHeart" 8 +fi + +#----------------------------------------------------------------------------------------------------------------- + diff --git a/src/libraries/libAzukiHeart/defaults.pri b/src/libraries/libAzukiHeart/defaults.pri new file mode 100644 index 00000000..6f76e98b --- /dev/null +++ b/src/libraries/libAzukiHeart/defaults.pri @@ -0,0 +1,2 @@ +INCLUDEPATH += $$PWD/src \ + $$PWD/include diff --git a/src/libraries/libAzukiHeart/include/libAzukiHeart/azuki_input.h b/src/libraries/libAzukiHeart/include/libAzukiHeart/azuki_input.h new file mode 100644 index 00000000..9649da3b --- /dev/null +++ b/src/libraries/libAzukiHeart/include/libAzukiHeart/azuki_input.h @@ -0,0 +1,37 @@ +/** + * @file azuki_input.h + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2022 Tobias Anker + * + * 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. + */ + +#ifndef KITSUNEMIMI_HANAMI_AZUKI_INPUT_H +#define KITSUNEMIMI_HANAMI_AZUKI_INPUT_H + +#include + +#include + +namespace Azuki +{ + +bool initAzukiBlossoms(); + +} + +#endif // KITSUNEMIMI_HANAMI_AZUKI_INPUT_H diff --git a/src/libraries/libAzukiHeart/include/libAzukiHeart/azuki_send.h b/src/libraries/libAzukiHeart/include/libAzukiHeart/azuki_send.h new file mode 100644 index 00000000..9bcf2ad0 --- /dev/null +++ b/src/libraries/libAzukiHeart/include/libAzukiHeart/azuki_send.h @@ -0,0 +1,39 @@ + /** + * @file azuki_send.h + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2022 Tobias Anker + * + * 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. + */ + +#ifndef KITSUNEMIMI_HANAMI_AZUKI_SEND_H +#define KITSUNEMIMI_HANAMI_AZUKI_SEND_H + +#include + +#include + +namespace Azuki +{ + +bool setSpeedToMinimum(Kitsunemimi::ErrorContainer &error); +bool setSpeedToAutomatic(Kitsunemimi::ErrorContainer &error); +bool setSpeedToMaximum(Kitsunemimi::ErrorContainer &error); + +} + +#endif // KITSUNEMIMI_HANAMI_AZUKI_SEND_H diff --git a/src/libraries/libAzukiHeart/libAzukiHeart.pro b/src/libraries/libAzukiHeart/libAzukiHeart.pro new file mode 100644 index 00000000..762524b0 --- /dev/null +++ b/src/libraries/libAzukiHeart/libAzukiHeart.pro @@ -0,0 +1,11 @@ +TEMPLATE = subdirs +CONFIG += ordered + +SUBDIRS = src + +run_tests { + SUBDIRS += tests + + tests.depends = src +} + diff --git a/src/libraries/libAzukiHeart/src/azuki_input.cpp b/src/libraries/libAzukiHeart/src/azuki_input.cpp new file mode 100644 index 00000000..d926198d --- /dev/null +++ b/src/libraries/libAzukiHeart/src/azuki_input.cpp @@ -0,0 +1,75 @@ +/** + * @file azuki_input.cpp + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2022 Tobias Anker + * + * 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 +#include +#include + +#include + +namespace Azuki +{ + +/** + * @brief HanamiMessaging::initAzukiBlossoms + * @return + */ +bool +initAzukiBlossoms() +{ + // init predefined blossoms + Kitsunemimi::Hanami::HanamiMessaging * interface = + Kitsunemimi::Hanami::HanamiMessaging::getInstance(); + const std::string group = "-"; + + if(interface->addBlossom(group, "get_thread_mapping", new GetThreadMapping()) == false) { + return false; + } + + // add new endpoints + if(interface->addEndpoint("v1/get_thread_mapping", + Kitsunemimi::Hanami::GET_TYPE, + Kitsunemimi::Hanami::BLOSSOM_TYPE, + "-", + "get_thread_mapping") == false) + { + return false; + } + + if(interface->addBlossom(group, "bind_thread_to_core", new BindThreadToCore()) == false) { + return false; + } + + // add new endpoints + if(interface->addEndpoint("v1/bind_thread_to_core", + Kitsunemimi::Hanami::POST_TYPE, + Kitsunemimi::Hanami::BLOSSOM_TYPE, + "-", + "bind_thread_to_core") == false) + { + return false; + } + + return true; +} + +} diff --git a/src/libraries/libAzukiHeart/src/azuki_send.cpp b/src/libraries/libAzukiHeart/src/azuki_send.cpp new file mode 100644 index 00000000..c628ee00 --- /dev/null +++ b/src/libraries/libAzukiHeart/src/azuki_send.cpp @@ -0,0 +1,131 @@ + /** + * @file azuki_send.cpp + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2022 Tobias Anker + * + * 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 + +#include +#include +#include +#include + +#include <../../libKitsunemimiHanamiMessages/protobuffers/azuki_messages.proto3.pb.h> +#include <../../libKitsunemimiHanamiMessages/message_sub_types.h> + +using Kitsunemimi::Hanami::HanamiMessaging; +using Kitsunemimi::Hanami::HanamiMessagingClient; +using Kitsunemimi::Hanami::SupportedComponents; + +namespace Azuki +{ + +/** + * @brief send speed-setter-message zu azuki + * + * @param client pointer to client, which is connected to azuki + * @param msg message to send + * @param error reference for error-output + * + * @return true, if successful, else false + */ +bool +sendSpeedSetMesage(HanamiMessagingClient* client, + const SetCpuSpeed_Message &msg, + Kitsunemimi::ErrorContainer &error) +{ + uint8_t buffer[1024]; + const uint64_t msgSize = msg.ByteSizeLong(); + if(msg.SerializeToArray(buffer, msgSize) == false) + { + error.addMeesage("Failed to serialize speed-set-message for Azuki"); + return false; + } + + return client->sendGenericMessage(AZUKI_SPEED_SET_MESSAGE_TYPE, buffer, msgSize, error); +} + +/** + * @brief send message to azuki to set cpu-speed to minimum + * + * @param error reference for error-output + * + * @return true, if successful, else false + */ +bool +setSpeedToMinimum(Kitsunemimi::ErrorContainer &error) +{ + // get client + HanamiMessagingClient* client = HanamiMessaging::getInstance()->azukiClient; + if(client == nullptr) { + return false; + } + + // send message + SetCpuSpeed_Message msg; + msg.set_type(SpeedState::MINIMUM_SPEED); + return sendSpeedSetMesage(client, msg, error); +} + +/** + * @brief send message to azuki to set cpu-speed to automatic + * + * @param error reference for error-output + * + * @return true, if successful, else false + */ +bool +setSpeedToAutomatic(Kitsunemimi::ErrorContainer &error) +{ + // get client + HanamiMessagingClient* client = HanamiMessaging::getInstance()->azukiClient; + if(client == nullptr) { + return false; + } + + // send message + SetCpuSpeed_Message msg; + msg.set_type(SpeedState::AUTOMATIC_SPEED); + return sendSpeedSetMesage(client, msg, error); +} + +/** + * @brief send message to azuki to set cpu-speed to maximum + * + * @param error reference for error-output + * + * @return true, if successful, else false + */ +bool +setSpeedToMaximum(Kitsunemimi::ErrorContainer &error) +{ + // get client + HanamiMessagingClient* client = HanamiMessaging::getInstance()->azukiClient; + if(client == nullptr) { + return false; + } + + // send message + SetCpuSpeed_Message msg; + msg.set_type(SpeedState::MAXIMUM_SPEED); + return sendSpeedSetMesage(client, msg, error); +} + +} diff --git a/src/libraries/libAzukiHeart/src/bind_thread_to_core.cpp b/src/libraries/libAzukiHeart/src/bind_thread_to_core.cpp new file mode 100644 index 00000000..7bbf6d31 --- /dev/null +++ b/src/libraries/libAzukiHeart/src/bind_thread_to_core.cpp @@ -0,0 +1,104 @@ +/** + * @file bind_thread_to_core.cpp + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2022 Tobias Anker + * + * 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 "bind_thread_to_core.h" + +#include +#include + +#include + +using namespace Kitsunemimi; + +namespace Azuki +{ + +BindThreadToCore::BindThreadToCore() + : Hanami::Blossom("Bind threads of a specific thead-type-name to a specific core.") +{ + //---------------------------------------------------------------------------------------------- + // input + //---------------------------------------------------------------------------------------------- + + registerInputField("thread_name", + Hanami::SAKURA_STRING_TYPE, + true, + "Thread-type-name of the threads, which should be bound to the core."); + assert(addFieldBorder("thread_name", 3, 256)); + assert(addFieldRegex("thread_name", "[a-zA-Z][a-zA-Z_0-9\\-]*")); + + registerInputField("core_ids", + Hanami::SAKURA_ARRAY_TYPE, + true, + "Core-ids to bind to."); + + //---------------------------------------------------------------------------------------------- + // + //---------------------------------------------------------------------------------------------- +} + +/** + * @brief runTask + */ +bool +BindThreadToCore::runTask(Hanami::BlossomIO &blossomIO, + const DataMap &, + Hanami::BlossomStatus &status, + ErrorContainer &error) +{ + const std::string threadName = blossomIO.input.get("thread_name").getString(); + const DataArray* coreIdsArray = blossomIO.input.get("core_ids").getItemContent()->toArray(); + + // convert core-ids + std::vector coreIds; + for(uint64_t i = 0; i < coreIdsArray->size(); i++) { + coreIds.push_back(coreIdsArray->get(i)->toValue()->getLong()); + } + + ThreadHandler* threadHandler = ThreadHandler::getInstance(); + + // get threads + const std::vector threads = threadHandler->getThreads(threadName); + if(threads.size() == 0) + { + status.statusCode = Hanami::NOT_FOUND_RTYPE; + status.errorMessage = "No threads found for thread-name '" + threadName + "'"; + error.addMeesage(status.errorMessage); + return false; + } + + // bind threads to core-ids + for(Thread* thread : threads) + { + if(thread->bindThreadToCores(coreIds) == false) + { + status.statusCode = Hanami::BAD_REQUEST_RTYPE; + status.errorMessage = "Core-id '" + coreIdsArray->toString() + "' is out of range."; + error.addMeesage(status.errorMessage); + return false; + } + } + + return true; +} + +} // namespace Azuki diff --git a/src/libraries/libAzukiHeart/src/bind_thread_to_core.h b/src/libraries/libAzukiHeart/src/bind_thread_to_core.h new file mode 100644 index 00000000..9605795b --- /dev/null +++ b/src/libraries/libAzukiHeart/src/bind_thread_to_core.h @@ -0,0 +1,46 @@ +/** + * @file bind_thread_to_core.h + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2022 Tobias Anker + * + * 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. + */ + +#ifndef KITSUNEMIMI_HANAMI_AZUKI_BINDTHREADTOCORE_H +#define KITSUNEMIMI_HANAMI_AZUKI_BINDTHREADTOCORE_H + +#include + +namespace Azuki +{ + +class BindThreadToCore + : public Kitsunemimi::Hanami::Blossom +{ +public: + BindThreadToCore(); + +protected: + bool runTask(Kitsunemimi::Hanami::BlossomIO &blossomIO, + const Kitsunemimi::DataMap &, + Kitsunemimi::Hanami::BlossomStatus &status, + Kitsunemimi::ErrorContainer &error); +}; + +} // namespace Azuki + +#endif // KITSUNEMIMI_HANAMI_AZUKI_BINDTHREADTOCORE_H diff --git a/src/libraries/libAzukiHeart/src/get_thread_mapping.cpp b/src/libraries/libAzukiHeart/src/get_thread_mapping.cpp new file mode 100644 index 00000000..5cf3a414 --- /dev/null +++ b/src/libraries/libAzukiHeart/src/get_thread_mapping.cpp @@ -0,0 +1,85 @@ +/** + * @file get_thread_mapping.cpp + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2022 Tobias Anker + * + * 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 "get_thread_mapping.h" + +#include +#include + +using namespace Kitsunemimi; + +namespace Azuki +{ + +GetThreadMapping::GetThreadMapping() + : Hanami::Blossom("Collect all thread-names with its acutal mapped core-id's") +{ + //---------------------------------------------------------------------------------------------- + // output + //---------------------------------------------------------------------------------------------- + + registerOutputField("thread_map", + Hanami::SAKURA_MAP_TYPE, + "Map with all thread-names and its core-id as json-string."); + + //---------------------------------------------------------------------------------------------- + // + //---------------------------------------------------------------------------------------------- +} + +/** + * @brief runTask + */ +bool +GetThreadMapping::runTask(Hanami::BlossomIO &blossomIO, + const DataMap &, + Hanami::BlossomStatus &, + ErrorContainer &) +{ + ThreadHandler* threadHandler = ThreadHandler::getInstance(); + + const std::vector names = threadHandler->getRegisteredNames(); + + DataMap* result = new DataMap(); + + for(const std::string &name : names) + { + const std::vector threads = threadHandler->getThreads(name); + DataArray* threadArray = new DataArray(); + for(Thread* thread : threads) + { + const std::vector coreIds = thread->getCoreIds(); + DataArray* cores = new DataArray(); + for(const uint64_t coreId : coreIds) { + cores->append(new DataValue(static_cast(coreId))); + } + threadArray->append(cores); + } + result->insert(name, threadArray); + } + + blossomIO.output.insert("thread_map", result); + + return true; +} + +} // namespace Azuki diff --git a/src/libraries/libAzukiHeart/src/get_thread_mapping.h b/src/libraries/libAzukiHeart/src/get_thread_mapping.h new file mode 100644 index 00000000..8f287894 --- /dev/null +++ b/src/libraries/libAzukiHeart/src/get_thread_mapping.h @@ -0,0 +1,46 @@ +/** + * @file get_thread_mapping.h + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2022 Tobias Anker + * + * 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. + */ + +#ifndef KITSUNEMIMI_HANAMI_AZUKI_GETTHREADMAPPING_H +#define KITSUNEMIMI_HANAMI_AZUKI_GETTHREADMAPPING_H + +#include + +namespace Azuki +{ + +class GetThreadMapping + : public Kitsunemimi::Hanami::Blossom +{ +public: + GetThreadMapping(); + +protected: + bool runTask(Kitsunemimi::Hanami::BlossomIO &blossomIO, + const Kitsunemimi::DataMap &, + Kitsunemimi::Hanami::BlossomStatus &, + Kitsunemimi::ErrorContainer &); +}; + +} // namespace Azuki + +#endif // KITSUNEMIMI_HANAMI_AZUKI_GETTHREADMAPPING_H diff --git a/src/libraries/libAzukiHeart/src/src.pro b/src/libraries/libAzukiHeart/src/src.pro new file mode 100644 index 00000000..9e7a1d90 --- /dev/null +++ b/src/libraries/libAzukiHeart/src/src.pro @@ -0,0 +1,92 @@ +QT -= qt core gui + +TARGET = AzukiHeart +CONFIG += c++17 +TEMPLATE = lib +VERSION = 0.2.0 + +LIBS += -L../../libKitsunemimiCommon/src -lKitsunemimiCommon +LIBS += -L../../libKitsunemimiCommon/src/debug -lKitsunemimiCommon +LIBS += -L../../libKitsunemimiCommon/src/release -lKitsunemimiCommon +INCLUDEPATH += ../../libKitsunemimiCommon/include + +LIBS += -L../../libKitsunemimiJwt/src -lKitsunemimiJwt +LIBS += -L../../libKitsunemimiJwt/src/debug -lKitsunemimiJwt +LIBS += -L../../libKitsunemimiJwt/src/release -lKitsunemimiJwt +INCLUDEPATH += ../../libKitsunemimiJwt/include + +LIBS += -L../../libKitsunemimiCrypto/src -lKitsunemimiCrypto +LIBS += -L../../libKitsunemimiCrypto/src/debug -lKitsunemimiCrypto +LIBS += -L../../libKitsunemimiCrypto/src/release -lKitsunemimiCrypto +INCLUDEPATH += ../../libKitsunemimiCrypto/include + +LIBS += -L../../libKitsunemimiJson/src -lKitsunemimiJson +LIBS += -L../../libKitsunemimiJson/src/debug -lKitsunemimiJson +LIBS += -L../../libKitsunemimiJson/src/release -lKitsunemimiJson +INCLUDEPATH += ../../libKitsunemimiJson/include + +LIBS += -L../../libKitsunemimiIni/src -lKitsunemimiIni +LIBS += -L../../libKitsunemimiIni/src/debug -lKitsunemimiIni +LIBS += -L../../libKitsunemimiIni/src/release -lKitsunemimiIni +INCLUDEPATH += ../../libKitsunemimiIni/include + +LIBS += -L../../libKitsunemimiConfig/src -lKitsunemimiConfig +LIBS += -L../../libKitsunemimiConfig/src/debug -lKitsunemimiConfig +LIBS += -L../../libKitsunemimiConfig/src/release -lKitsunemimiConfig +INCLUDEPATH += ../../libKitsunemimiConfig/include + +LIBS += -L../../libKitsunemimiNetwork/src -lKitsunemimiNetwork +LIBS += -L../../libKitsunemimiNetwork/src/debug -lKitsunemimiNetwork +LIBS += -L../../libKitsunemimiNetwork/src/release -lKitsunemimiNetwork +INCLUDEPATH += ../../libKitsunemimiNetwork/include + +LIBS += -L../../libKitsunemimiSakuraNetwork/src -lKitsunemimiSakuraNetwork +LIBS += -L../../libKitsunemimiSakuraNetwork/src/debug -lKitsunemimiSakuraNetwork +LIBS += -L../../libKitsunemimiSakuraNetwork/src/release -lKitsunemimiSakuraNetwork +INCLUDEPATH += ../../libKitsunemimiSakuraNetwork/include + +LIBS += -L../../libKitsunemimiHanamiCommon/src -lKitsunemimiHanamiCommon +LIBS += -L../../libKitsunemimiHanamiCommon/src/debug -lKitsunemimiHanamiCommon +LIBS += -L../../libKitsunemimiHanamiCommon/src/release -lKitsunemimiHanamiCommon +INCLUDEPATH += ../../libKitsunemimiHanamiCommon/include + +LIBS += -L../../libKitsunemimiHanamiNetwork/src -lKitsunemimiHanamiNetwork +LIBS += -L../../libKitsunemimiHanamiNetwork/src/debug -lKitsunemimiHanamiNetwork +LIBS += -L../../libKitsunemimiHanamiNetwork/src/release -lKitsunemimiHanamiNetwork +INCLUDEPATH += ../../libKitsunemimiHanamiNetwork/include + +LIBS += -lssl -lcryptopp -lcrypto + +INCLUDEPATH += $$PWD \ + $$PWD/../include + +HEADERS += \ + ../include/libAzukiHeart/azuki_send.h \ + ../include/libAzukiHeart/azuki_input.h \ + bind_thread_to_core.h \ + get_thread_mapping.h + +SOURCES += \ + azuki_send.cpp \ + azuki_input.cpp \ + bind_thread_to_core.cpp \ + get_thread_mapping.cpp + +AZUKI_PROTO_BUFFER = ../../libKitsunemimiHanamiMessages/protobuffers/azuki_messages.proto3 + +OTHER_FILES += $$AZUKI_PROTO_BUFFER + +protobuf_decl.name = protobuf headers +protobuf_decl.input = AZUKI_PROTO_BUFFER +protobuf_decl.output = ${QMAKE_FILE_IN_PATH}/${QMAKE_FILE_BASE}.proto3.pb.h +protobuf_decl.commands = protoc --cpp_out=${QMAKE_FILE_IN_PATH} --proto_path=${QMAKE_FILE_IN_PATH} ${QMAKE_FILE_NAME} +protobuf_decl.variable_out = HEADERS +QMAKE_EXTRA_COMPILERS += protobuf_decl + +protobuf_impl.name = protobuf sources +protobuf_impl.input = AZUKI_PROTO_BUFFER +protobuf_impl.output = ${QMAKE_FILE_IN_PATH}/${QMAKE_FILE_BASE}.proto3.pb.cc +protobuf_impl.depends = ${QMAKE_FILE_IN_PATH}/${QMAKE_FILE_BASE}.proto3.pb.h +protobuf_impl.commands = $$escape_expand(\n) +protobuf_impl.variable_out = SOURCES +QMAKE_EXTRA_COMPILERS += protobuf_impl diff --git a/src/libraries/libKitsunemimiArgs/CHANGELOG_old b/src/libraries/libKitsunemimiArgs/CHANGELOG_old new file mode 100644 index 00000000..97968cc5 --- /dev/null +++ b/src/libraries/libKitsunemimiArgs/CHANGELOG_old @@ -0,0 +1,69 @@ +# Changelog + +## [0.4.0] - 2021-11-17 + +### Changed +- use error-container of libKitsunemimiCommon for error-output + + +## [0.3.1] - 2021-10-28 + +### Changed +- updated requirements + + +## [0.3.0] - 2021-10-03 + +### Removed +- subcommands were removed, because the are not used + +### Changed +- moved from c++14 to c++17 + + +## [0.2.2] - 2021-07-28 + +### Added +- some more validation + +### Fixed +- now possible to define only flaged values + + +## [0.2.1] - 2020-09-26 + +### Added +- optional --version, -v flags for version-output + + +## [0.2.0] - 2020-08-23 + +### Added +- subcommands + + +## [0.1.3] - 2020-03-30 + +### Changes +- updated requirements + +### Fixed +- the help-flag now terminates the program after the printing the output + + +## [0.1.2] - 2020-03-30 + +### Changes +- updated requirements + + +## [0.1.1] - 2020-03-29 + +### Fixed +- fixed broken length-valiation while parsing + + +## [0.1.0] - 2020-03-28 + +### Added +- first implementation of the Argument-Parser diff --git a/src/libraries/libKitsunemimiArgs/README.md b/src/libraries/libKitsunemimiArgs/README.md new file mode 100644 index 00000000..812bbdd3 --- /dev/null +++ b/src/libraries/libKitsunemimiArgs/README.md @@ -0,0 +1,193 @@ +# libKitsunemimiArgs + +![Github workfloat status](https://img.shields.io/github/actions/workflow/status/kitsudaiki/libKitsunemimiArgs/build_test.yml?branch=develop&style=flat-square&label=build%20and%20test) +![GitHub tag (latest SemVer)](https://img.shields.io/github/v/tag/kitsudaiki/libKitsunemimiArgs?label=version&style=flat-square) +![GitHub](https://img.shields.io/github/license/kitsudaiki/libKitsunemimiArgs?style=flat-square) +![C++Version](https://img.shields.io/badge/c%2B%2B-17-blue?style=flat-square) +![Platform](https://img.shields.io/badge/platform-Linux--x64-lightgrey?style=flat-square) + +## Description + +Small and easy to use parser for CLI-arguments. + +## Build + +### Requirements + +name | repository | version | task +--- | --- | --- | --- +g++ | g++ | >= 8.0 | Compiler for the C++ code. +make | make | >= 4.0 | process the make-file, which is created by qmake to build the programm with g++ +qmake | qt5-qmake | >= 5.0 | This package provides the tool qmake, which is similar to cmake and create the make-file for compilation. + +Installation on Ubuntu/Debian: + +```bash +sudo apt-get install g++ make qt5-qmake +``` + +IMPORTANT: All my projects are only tested on Linux. + +### Kitsunemimi-repositories + +Repository-Name | Version-Tag | Download-Path +--- | --- | --- +libKitsunemimiCommon | develop | https://github.com/kitsudaiki/libKitsunemimiCommon.git + +HINT: These Kitsunemimi-Libraries will be downloaded and build automatically with the build-script below. + +### build library + +In all of my repositories you will find a `build.sh`. You only have to run this script. It doesn't required sudo, because you have to install required tool via apt, for example, by yourself. But if other projects from me are required, it download them from github and build them in the correct version too. This script is also use by the ci-pipeline, so its tested with every commit. + + +Run the following commands: + +``` +git clone https://github.com/kitsudaiki/libKitsunemimiArgs.git +cd libKitsunemimiArgs +./build.sh +cd ../result +``` + +It create automatic a `build` and `result` directory in the directory, where you have cloned the project. At first it build all into the `build`-directory and after all build-steps are finished, it copy the include directory from the cloned repository and the build library into the `result`-directory. So you have all in one single place. + +Tested on Debian and Ubuntu. If you use Centos, Arch, etc and the build-script fails on your machine, then please write me a mail and I will try to fix the script. + +## Usage by example + +This example should show, how the parser is used and what is possible. + +HINT: The flags `--help` and `-h` for the help-output are hard coded and don't have to be set. + + +```cpp +#include +#include +#include + +int main(int argc, char *argv[]) +{ + // error messages of the parser are printed via logger + Kitsunemimi::initConsoleLogger(true); + // with "initFileLogger" the error-message of the argument-parser can also be written into a file + + Kitsunemimi::ArgParser parser("0.1.0"); + Kitsunemimi::ErrorContainer error; + + // register flags without value + parser.registerPlain("debug,d", + "debug-flag to enable addtional debug output", + error); + // "registerPlain" allows it to register flags without any value, which says only true or flase + // if they were set or not set + + // register flags + parser.registerString("source", + "source-path", + error, + true); + parser.registerInteger("input,i", + "additional parameter"); + + // register other values + parser.registerString("mode", + "modus for converting", + error, + true, // true to make it requried + true); // true to register this without a "--"-flag + parser.registerString("destination", + "destination path for output", + error, + true, + true); + // register types: + // registerString + // registerInteger + // registerFloat + // registerBoolean + + // parse incoming arguments + bool ret = parser.parse(argc, argv, error); + if(ret == false) + { + LOG_ERROR(error); + return 1; + } + // ret say, if the converting was successful or not. Error-message are written in the logger + + // check if flags without values were set. In this case check if the debug-flag was set + bool debug = parser.wasSet("debug"); + + // get values with or without flag as list of value for the case, that a flag was + // used multiple times within one cli-call: + const std::vector testValues = parser.getStringValues("source"); + const std::vector numbers = parser.getIntValues("input"); + // get types: + // getStringValues + // getIntValues + // getFloatValues + // getBoolValues + + // get values without flag: + const std::string mode = parser.getStringValue("mode"); + const std::string destination = parser.getStringValue("destination"); + // get types: + // getStringValue + // getIntValue + // getFloatValue + // getBoolValue + + //... + + return 0; +} + +``` + +If the tool would called `cli_test` the command `cli_test --help` would produce the following output: + +``` +command: cli_test [options] --source ... + +Options: ++-----------+-------+--------+-------------+---------------------------------------------+ +| long | short | type | is required | help-text | ++===========+=======+========+=============+=============================================+ +| --help | -h | | | print help ouput | ++-----------+-------+--------+-------------+---------------------------------------------+ +| --version | -v | | | print program version | ++-----------+-------+--------+-------------+---------------------------------------------+ +| --debug | -d | | | debug-flag to enable addtional debug output | ++-----------+-------+--------+-------------+---------------------------------------------+ +| --source | | string | x | source-path | ++-----------+-------+--------+-------------+---------------------------------------------+ +| --input | -i | number | | additional parameter | ++-----------+-------+--------+-------------+---------------------------------------------+ + +Required: ++---------------+--------+-----------------------------+ +| name | type | text | ++===============+========+=============================+ +| | string | modus for converting | ++---------------+--------+-----------------------------+ +| | number | destination path for output | ++---------------+--------+-----------------------------+ +``` + +If this example is called with a string `asdf` for the flag `-i`, the error-message looks like this: + +``` +ERROR: argument has the false type: + required type: number + identifier: -i + given value: asdf +``` + +## Contributing + +Please give me as many inputs as possible: Bugs, bad code style, bad documentation and so on. + +## License + +This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details diff --git a/src/libraries/libKitsunemimiArgs/build.sh b/src/libraries/libKitsunemimiArgs/build.sh new file mode 100755 index 00000000..639e5afa --- /dev/null +++ b/src/libraries/libKitsunemimiArgs/build.sh @@ -0,0 +1,103 @@ +#!/bin/bash + +# get current directory-path and the path of the parent-directory +DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )" +PARENT_DIR="$(dirname "$DIR")" +CURRENT_BRANCH=$(git rev-parse --abbrev-ref HEAD) + +# create build-directory +BUILD_DIR="$PARENT_DIR/build" +mkdir -p $BUILD_DIR + +# create directory for the final result +RESULT_DIR="$PARENT_DIR/result" +mkdir -p $RESULT_DIR + +#----------------------------------------------------------------------------------------------------------------- + +function build_kitsune_lib_repo () { + REPO_NAME=$1 + NUMBER_OF_THREADS=$2 + ADDITIONAL_CONFIGS=$3 + + # create build directory for repo and go into this directory + REPO_DIR="$BUILD_DIR/$REPO_NAME" + mkdir -p $REPO_DIR + cd $REPO_DIR + + # build repo library with qmake + /usr/lib/x86_64-linux-gnu/qt5/bin/qmake "$PARENT_DIR/$REPO_NAME/$REPO_NAME.pro" -spec linux-g++ "CONFIG += optimize_full staticlib $ADDITIONAL_CONFIGS" + /usr/bin/make -j$NUMBER_OF_THREADS + + # copy build-result and include-files into the result-directory + echo "----------------------------------------------------------------------" + echo $RESULT_DIR + cp $REPO_DIR/src/$REPO_NAME.a $RESULT_DIR/ + cp -r $PARENT_DIR/$REPO_NAME/include $RESULT_DIR/ + ls -l $RESULT_DIR/include/ + ls -l $RESULT_DIR +} + +function download_repo_github () { + REPO_NAME=$1 + TAG_OR_BRANCH=$2 + + echo "" + echo "" + echo "++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++" + echo "$REPO_NAME" + echo "++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++" + echo "Branch/Tag: $TAG_OR_BRANCH" + + # clone repo + git clone https://github.com/kitsudaiki/$REPO_NAME.git "$PARENT_DIR/$REPO_NAME" + cd "$PARENT_DIR/$REPO_NAME" + + # checkout branch + if [[ $CURRENT_BRANCH =~ ^tag.* ]] || [[ $CURRENT_BRANCH =~ ^hotfix.* ]] || [[ $CURRENT_BRANCH =~ ^v.* ]]; then + # if a stable branch, then use the defined tag of branch + # check if defined branch even exist + BRANCH_EXIST=$(git ls-remote --heads origin $TAG_OR_BRANCH) + if [[ -z "$BRANCH_EXIST" ]]; then + echo "" + echo "-------------------------------------------------------------------------------------" + echo "Branch or tag '$TAG_OR_BRANCH' does not exist for the repository '$REPO_NAME'" + echo "-------------------------------------------------------------------------------------" + echo "" + exit 1 + fi + git checkout $TAG_OR_BRANCH + else + # if develop or feature branch, then try to checkout the feature-branch in the other repo as well + # or otherwise use the develop-branch as default + BRANCH_EXIST=$(git ls-remote --heads origin $CURRENT_BRANCH) + if [[ -n "$BRANCH_EXIST" ]]; then + git checkout $CURRENT_BRANCH + else + git checkout develop + fi + fi +} + +function get_required_kitsune_lib_repo () { + REPO_NAME=$1 + TAG_OR_BRANCH=$2 + NUMBER_OF_THREADS=$3 + + download_repo_github $REPO_NAME $TAG_OR_BRANCH + build_kitsune_lib_repo $REPO_NAME $NUMBER_OF_THREADS +} + +#----------------------------------------------------------------------------------------------------------------- + +get_required_kitsune_lib_repo "libKitsunemimiCommon" "develop" 8 + +#----------------------------------------------------------------------------------------------------------------- + +if [ $1 = "test" ]; then + build_kitsune_lib_repo "libKitsunemimiArgs" 4 "run_tests" +else + build_kitsune_lib_repo "libKitsunemimiArgs" 4 "" +fi + +#----------------------------------------------------------------------------------------------------------------- diff --git a/src/libraries/libKitsunemimiArgs/defaults.pri b/src/libraries/libKitsunemimiArgs/defaults.pri new file mode 100644 index 00000000..6f76e98b --- /dev/null +++ b/src/libraries/libKitsunemimiArgs/defaults.pri @@ -0,0 +1,2 @@ +INCLUDEPATH += $$PWD/src \ + $$PWD/include diff --git a/src/libraries/libKitsunemimiArgs/include/libKitsunemimiArgs/arg_parser.h b/src/libraries/libKitsunemimiArgs/include/libKitsunemimiArgs/arg_parser.h new file mode 100644 index 00000000..0feccc35 --- /dev/null +++ b/src/libraries/libKitsunemimiArgs/include/libKitsunemimiArgs/arg_parser.h @@ -0,0 +1,141 @@ +/** + * @file arg_parser.h + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2022 Tobias Anker + * + * 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. + */ + +#ifndef ARG_PARSER_H +#define ARG_PARSER_H + +#include +#include +#include +#include +#include + +namespace Kitsunemimi +{ +class DataItem; +class DataArray; +class ArgParser_Test; +class SubCommand; + +class ArgParser +{ +public: + ArgParser(const std::string &version = ""); + ~ArgParser(); + + // register + bool registerPlain(const std::string &identifier, + const std::string &helpText, + ErrorContainer &error); + bool registerString(const std::string &identifier, + const std::string &helpText, + ErrorContainer &error, + bool required = false, + bool withoutFlag = false); + bool registerInteger(const std::string &identifier, + const std::string &helpText, + ErrorContainer &error, + bool required = false, + bool withoutFlag = false); + bool registerFloat(const std::string &identifier, + const std::string &helpText, + ErrorContainer &error, + bool required = false, + bool withoutFlag = false); + bool registerBoolean(const std::string &identifier, + const std::string &helpTex, + ErrorContainer &errort, + bool required = false, + bool withoutFlag = false); + + // parse + bool parse(const int argc, + char *argv[], + ErrorContainer &error); + bool parse(const int argc, + const char* argv[], + ErrorContainer &error); + + // getter + uint64_t getNumberOfValues(const std::string &identifier); + bool wasSet(const std::string &identifier); + const std::vector getStringValues(const std::string &identifier); + const std::vector getIntValues(const std::string &identifier); + const std::vector getFloatValues(const std::string &identifier); + const std::vector getBoolValues(const std::string &identifier); + + const std::string getStringValue(const std::string &identifier); + long getIntValue(const std::string &identifier); + double getFloatValue(const std::string &identifier); + bool getBoolValue(const std::string &identifier); + +private: + friend ArgParser_Test; + friend SubCommand; + + enum ArgType + { + NO_TYPE, + STRING_TYPE, + INT_TYPE, + FLOAT_TYPE, + BOOL_TYPE + }; + + struct ArgDefinition + { + bool withoutFlag = false; + bool required = false; + bool hasValue = false; + bool wasSet = false; + std::string longIdentifier = ""; + std::string shortIdentifier = ""; + ArgType type = STRING_TYPE; + std::string helpText = ""; + + DataArray* results = nullptr; + }; + + uint32_t m_positionCounter = 0; + std::string m_version = ""; + std::vector m_argumentList; + + const std::string convertType(ArgType type); + void print(const std::string &commandName); + bool precheckFlags(const int argc, const char* argv[]); + + ArgDefinition* getArgument(const std::string &identifier); + bool registerArgument(const std::string &identifier, + const std::string &helpText, + const ArgType type, + bool required, + bool withoutFlag, + bool hasValue, + ErrorContainer &error); + + DataItem* convertValue(const std::string &value, + const ArgType requiredType); +}; + +} + +#endif // ARG_PARSER_H diff --git a/src/libraries/libKitsunemimiArgs/libKitsunemimiArgs.pro b/src/libraries/libKitsunemimiArgs/libKitsunemimiArgs.pro new file mode 100644 index 00000000..762524b0 --- /dev/null +++ b/src/libraries/libKitsunemimiArgs/libKitsunemimiArgs.pro @@ -0,0 +1,11 @@ +TEMPLATE = subdirs +CONFIG += ordered + +SUBDIRS = src + +run_tests { + SUBDIRS += tests + + tests.depends = src +} + diff --git a/src/libraries/libKitsunemimiArgs/src/arg_parser.cpp b/src/libraries/libKitsunemimiArgs/src/arg_parser.cpp new file mode 100644 index 00000000..c0cd3121 --- /dev/null +++ b/src/libraries/libKitsunemimiArgs/src/arg_parser.cpp @@ -0,0 +1,996 @@ +/** + * @file arg_parser.cpp + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2022 Tobias Anker + * + * 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 + +#include +#include +#include + +#include + +namespace Kitsunemimi +{ + +/** + * @brief constructor + */ +ArgParser::ArgParser(const std::string &version) +{ + ErrorContainer error; + // register the help-output as special case + registerPlain("help,h", "print help ouput", error); + + if(version != "") + { + m_version = version; + registerPlain("version,v", "print program version", error); + } +} + +/** + * @brief destructor + */ +ArgParser::~ArgParser() +{ + for(uint32_t i = 0; i < m_argumentList.size(); i++) + { + delete m_argumentList[i].results; + } +} + +/** + * @brief register argument without value + * + * @param identifier Identifier for the new argument. Its a single word like "flag" for defining + * only a long identifier like "--flag" or a comma-separated pair like "flag,f" + * to define a long identifier like "--flag" together with a short identifier + * like "-f" + * @param helpText help-text for the argument for user-output + * + * @return false, if identifier is already registered or broken, else true + */ +bool +ArgParser::registerPlain(const std::string &identifier, + const std::string &helpText, + ErrorContainer &error) +{ + return registerArgument(identifier, + helpText, + ArgType::NO_TYPE, + false, + false, + false, + error); +} + +/** + * @brief register string-value + * + * @param identifier Identifier for the new argument. Its a single word like "flag" for defining + * only a long identifier like "--flag" or a comma-separated pair like "flag,f" + * to define a long identifier like "--flag" together with a short identifier + * like "-f" + * @param helpText help-text for the argument for user-output + * @param required true, to make the argument required, fals to make it optional + * @param withoutFlag true, the handle the identifier as flag for the value + * + * @return false, if identifier is already registered or broken, else true + */ +bool +ArgParser::registerString(const std::string &identifier, + const std::string &helpText, + ErrorContainer &error, + bool required, + bool withoutFlag) +{ + return registerArgument(identifier, + helpText, + ArgType::STRING_TYPE, + required, + withoutFlag, + true, + error); +} + +/** + * @brief register int/long value + * + * @param identifier Identifier for the new argument. Its a single word like "flag" for defining + * only a long identifier like "--flag" or a comma-separated pair like "flag,f" + * to define a long identifier like "--flag" together with a short identifier + * like "-f" + * @param helpText help-text for the argument for user-output + * @param required true, to make the argument required, fals to make it optional + * @param withoutFlag true, the handle the identifier as flag for the value. If this value is true + * the required-value is set to true too. + * + * @return false, if identifier is already registered or broken, else true + */ +bool +ArgParser::registerInteger(const std::string &identifier, + const std::string &helpText, + ErrorContainer &error, + bool required, + bool withoutFlag) +{ + return registerArgument(identifier, + helpText, + ArgType::INT_TYPE, + required, + withoutFlag, + true, + error); +} + + +/** + * @brief register float/double value + * + * @param identifier Identifier for the new argument. Its a single word like "flag" for defining + * only a long identifier like "--flag" or a comma-separated pair like "flag,f" + * to define a long identifier like "--flag" together with a short identifier + * like "-f" + * @param helpText help-text for the argument for user-output + * @param required true, to make the argument required, fals to make it optional + * @param withoutFlag true, the handle the identifier as flag for the value. If this value is true + * the required-value is set to true too. + * + * @return false, if identifier is already registered or broken, else true + */ +bool +ArgParser::registerFloat(const std::string &identifier, + const std::string &helpText, + ErrorContainer &error, + bool required, + bool withoutFlag) +{ + return registerArgument(identifier, + helpText, + ArgType::FLOAT_TYPE, + required, + withoutFlag, + true, + error); +} + +/** + * @brief register bool value + * + * @param identifier Identifier for the new argument. Its a single word like "flag" for defining + * only a long identifier like "--flag" or a comma-separated pair like "flag,f" + * to define a long identifier like "--flag" together with a short identifier + * like "-f" + * @param helpText help-text for the argument for user-output + * @param required true, to make the argument required, fals to make it optional + * @param withoutFlag true, the handle the identifier as flag for the value. If this value is true + * the required-value is set to true too. + * + * @return false, if identifier is already registered or broken, else true + */ +bool +ArgParser::registerBoolean(const std::string &identifier, + const std::string &helpText, + ErrorContainer &error, + bool required, + bool withoutFlag) +{ + return registerArgument(identifier, + helpText, + ArgType::BOOL_TYPE, + required, + withoutFlag, + true, + error); +} + +/** + * @brief register ne argument + * + * @param identifier Identifier for the new argument. Its a single word like "flag" for defining + * only a long identifier like "--flag" or a comma-separated pair like "flag,f" + * to define a long identifier like "--flag" together with a short identifier + * like "-f" + * @param helpText help-text for the argument for user-output + * @param type type identifier + * @param required true, to make the argument required, fals to make it optional + * @param withoutFlag true, the handle the identifier as flag for the value. If this value is true + * the required-value is set to true too. + * + * @return false, if identifier is already registered or broken, else true + */ +bool +ArgParser::registerArgument(const std::string &identifier, + const std::string &helpText, + const ArgType type, + bool required, + bool withoutFlag, + bool hasValue, + ErrorContainer &error) +{ + // precheck + if(identifier.size() == 0 + || identifier.at(0) == ',') + { + error.addMeesage("No argument identifier was set"); + LOG_ERROR(error); + return false; + } + + ArgDefinition newArgument; + + // split identifier-string + std::vector identifierList; + splitStringByDelimiter(identifierList, identifier, ','); + + // check splitting-result + if(identifierList.size() > 2) + { + ErrorContainer error; + error.addMeesage("argument identifier name is too long: " + identifier); + LOG_ERROR(error); + return false; + } + + // prepare long identifier + if(identifierList.at(0).size() == 0) + { + ErrorContainer error; + error.addMeesage("argument identifier is invalid: " + identifier); + LOG_ERROR(error); + return false; + } + if(withoutFlag == false) { + newArgument.longIdentifier = "--"; + } + + newArgument.longIdentifier += identifierList.at(0); + + // check if already used + ArgParser::ArgDefinition* findLong = getArgument(newArgument.longIdentifier); + if(findLong != nullptr) + { + error.addMeesage("argument already in use: " + newArgument.longIdentifier); + LOG_ERROR(error); + return false; + } + + // prepare short identifier + if(identifierList.size() == 2) + { + // check length + if(identifierList.at(1).size() != 1) + { + error.addMeesage("Argument identifier is invalid: " + identifier); + LOG_ERROR(error); + return false; + } + + newArgument.shortIdentifier = "-" + identifierList.at(1); + + // check if already used + ArgParser::ArgDefinition* findShort = getArgument(newArgument.shortIdentifier); + if(findShort != nullptr) + { + error.addMeesage("argument already in use: " + newArgument.shortIdentifier); + LOG_ERROR(error); + return false; + } + } + + // set other values + newArgument.required = required; + newArgument.hasValue = hasValue; + newArgument.withoutFlag = withoutFlag; + if(withoutFlag) { + newArgument.required = true; + } + newArgument.type = type; + newArgument.helpText = helpText; + newArgument.results = new DataArray(); + + m_argumentList.push_back(newArgument); + + return true; +} + +/** + * @brief convert argument-values + * + * @param value string-value + * @param requiredType required type for the argument + * + * @return nullptr, if converting failed, else data-item with the converted value + */ +DataItem* +ArgParser::convertValue(const std::string &value, + const ArgParser::ArgType requiredType) +{ + // string value + if(requiredType == ArgType::STRING_TYPE) { + return new DataValue(value); + } + + // long/int value + if(requiredType == ArgType::INT_TYPE) + { + char* err = nullptr; + const char* charValue = value.c_str(); + + // convert to long-value + const long longValue = std::strtol(charValue, &err, 10); + if(std::string(err).size() != 0) { + return nullptr; + } + + return new DataValue(longValue); + } + + // double/floag value + if(requiredType == ArgType::FLOAT_TYPE) + { + char* err = nullptr; + const char* charValue = value.c_str(); + + // convert to double-value + const double doubleValue = std::strtod(charValue, &err); + if(std::string(err).size() != 0) { + return nullptr; + } + + return new DataValue(doubleValue); + } + + // bool value + if(requiredType == ArgType::BOOL_TYPE) + { + // convert true + if(value == "true" + || value == "True" + || value == "1") + { + return new DataValue(true); + } + + // convert false + if(value == "false" + || value == "False" + || value == "0") + { + return new DataValue(false); + } + + // if nothing match, it is no boolean + return nullptr; + } + + return nullptr; +} + +/** + * @brief precheck for the flags to handle --help and --version + * + * @param argc number of arguments + * @param argv arguments + * + * @return true, if help or version flag was set, else true + */ +bool +ArgParser::precheckFlags(const int argc, + const char* argv[]) +{ + const std::string programmPath(argv[0]); + std::vector pathParts; + splitStringByDelimiter(pathParts, programmPath, '/'); + + for(int i = 1; i < argc; i++) + { + const std::string currentArgument(argv[i]); + + // check for help-flag + if(currentArgument == "-h" + || currentArgument == "--help") + { + print(pathParts.at(pathParts.size()-1)); + return true; + } + + if(m_version != "") + { + // check for version-flag + if(currentArgument == "-v" + || currentArgument == "--version") + { + std::cout<<"version: "<hasValue) + { + // check if there is a value for the identifier + if(i+1 == argc) + { + error.addMeesage("flag has no value: " + currentArgument); + LOG_ERROR(error); + return false; + } + + // get value + const std::string currentValue(argv[i+1]); + + // convert value + DataItem* convertedValue = convertValue(currentValue, argIdent->type); + if(convertedValue == nullptr) + { + const std::string errMsg = "argument has the false type: " + "\n required type: " + + convertType(argIdent->type) + + "\n identifier: " + + currentArgument + + "\n given value: " + + currentValue; + + error.addMeesage(errMsg); + LOG_ERROR(error); + return false; + } + + // add converted value to results + argIdent->results->append(convertedValue); + + i += 2; + } + else + { + i += 1; + } + + argIdent->wasSet = true; + } + else + { + uint32_t counter = 0; + for(uint64_t j = 0; j < m_argumentList.size(); j++) + { + if(m_argumentList[j].withoutFlag == true) + { + ArgParser::ArgDefinition* argIdent = &m_argumentList[j]; + if(m_positionCounter == counter) + { + // convert value + DataItem* convertedValue = convertValue(currentArgument, argIdent->type); + if(convertedValue == nullptr) + { + const std::string errMsg = "argument has the false type: " + "\n required type: " + + convertType(argIdent->type) + + "\n identifier: " + + m_argumentList[j].longIdentifier + + "\n given value: " + + currentArgument; + + error.addMeesage(errMsg); + LOG_ERROR(error); + return false; + } + + // add converted value to results + argIdent->results->append(convertedValue); + + // update counter + m_positionCounter++; + j = m_argumentList.size(); + } + + counter++; + } + } + + i += 1; + } + } + + // check if all requirements are satisfied + for(uint32_t i = 0; i < m_argumentList.size(); i++) + { + if(m_argumentList[i].results->size() == 0 + && m_argumentList[i].required) + { + error.addMeesage("argument is required but was not set: " + + m_argumentList[i].longIdentifier); + LOG_ERROR(error); + return false; + } + } + + return true; +} + +/** + * @brief check how often a flag was set + * + * @param identifier identifier-name of the flag + * + * @return 0, if the flag was not set, else the counter of how often the flag was set + * + */ +uint64_t +ArgParser::getNumberOfValues(const std::string &identifier) +{ + ArgParser::ArgDefinition* arg = getArgument(identifier); + if(arg == nullptr) { + return 0; + } + + return arg->results->size(); +} + +/** + * @brief check if a flag was set + * + * @param identifier identifier-name of the flag + * + * @return true, if flag was set, else false + */ +bool +ArgParser::wasSet(const std::string &identifier) +{ + ArgParser::ArgDefinition* arg = getArgument(identifier); + if(arg == nullptr) { + return false; + } + + return arg->wasSet; +} + +/** + * @brief get parsed string values of an identifier + * + * @param identifier regested identifier without "--" or "-" + * + * @return list of parsed string values + */ +const std::vector +ArgParser::getStringValues(const std::string &identifier) +{ + std::vector result; + + // get registered argument + ArgParser::ArgDefinition* arg = getArgument(identifier); + if(arg == nullptr) { + return result; + } + + // check argument-type + if(arg->type != ArgType::STRING_TYPE) { + return result; + } + + // build list with all results + for(uint32_t i = 0; i < arg->results->size(); i++) { + result.push_back(arg->results->get(i)->getString()); + } + + return result; +} + +/** + * @brief get parsed long values of an identifier + * + * @param identifier regested identifier without "--" or "-" + * + * @return list of parsed long values + */ +const std::vector +ArgParser::getIntValues(const std::string &identifier) +{ + std::vector result; + + // get registered argument + ArgParser::ArgDefinition* arg = getArgument(identifier); + if(arg == nullptr) { + return result; + } + + // check argument-type + if(arg->type != ArgType::INT_TYPE) { + return result; + } + + // build list with all results + for(uint32_t i = 0; i < arg->results->size(); i++) { + result.push_back(arg->results->get(i)->getLong()); + } + + return result; +} + +/** + * @brief get parsed double values of an identifier + * + * @param identifier regested identifier without "--" or "-" + * + * @return list of parsed double values + */ +const std::vector +ArgParser::getFloatValues(const std::string &identifier) +{ + std::vector result; + + // get registered argument + ArgParser::ArgDefinition* arg = getArgument(identifier); + if(arg == nullptr) { + return result; + } + + // check argument-type + if(arg->type != ArgType::FLOAT_TYPE) { + return result; + } + + // build list with all results + for(uint32_t i = 0; i < arg->results->size(); i++) { + result.push_back(arg->results->get(i)->getDouble()); + } + + return result; +} + +/** + * @brief get parsed bool values of an identifier + * + * @param identifier regested identifier without "--" or "-" + * + * @return list of parsed bool values + */ +const std::vector +ArgParser::getBoolValues(const std::string &identifier) +{ + std::vector result; + + // get registered argument + ArgParser::ArgDefinition* arg = getArgument(identifier); + if(arg == nullptr) { + return result; + } + + // check argument-type + if(arg->type != ArgType::BOOL_TYPE) { + return result; + } + + // build list with all results + for(uint32_t i = 0; i < arg->results->size(); i++) { + result.push_back(arg->results->get(i)->getBool()); + } + + return result; +} + +/** + * @brief get parsed string value of an required identifier + * + * @param identifier regested identifier + * + * @return parsed string value + */ +const std::string +ArgParser::getStringValue(const std::string &identifier) +{ + std::vector result; + + // get registered argument + ArgParser::ArgDefinition* arg = getArgument(identifier); + if(arg == nullptr) { + return ""; + } + + // check argument-type + if(arg->type != ArgType::STRING_TYPE) { + return ""; + } + + // check result not empty + if(arg->results->size() == 0) { + return ""; + } + + return arg->results->get(0)->getString(); +} + +/** + * @brief get parsed long value of an required identifier + * + * @param identifier regested identifier + * + * @return parsed long value + */ +long +ArgParser::getIntValue(const std::string &identifier) +{ + std::vector result; + + // get registered argument + ArgParser::ArgDefinition* arg = getArgument(identifier); + if(arg == nullptr) { + return 0l; + } + + // check argument-type + if(arg->type != ArgType::INT_TYPE) { + return 0l; + } + + // check result not empty + if(arg->results->size() == 0) { + return 0l; + } + + return arg->results->get(0)->getLong(); +} + +/** + * @brief get parsed double value of an required identifier + * + * @param identifier regested identifier + * + * @return parsed double value + */ +double +ArgParser::getFloatValue(const std::string &identifier) +{ + std::vector result; + + // get registered argument + ArgParser::ArgDefinition* arg = getArgument(identifier); + if(arg == nullptr) { + return 0.0; + } + + // check argument-type + if(arg->type != ArgType::FLOAT_TYPE) { + return 0.0; + } + + // check result not empty + if(arg->results->size() == 0) { + return 0.0; + } + + return arg->results->get(0)->getDouble(); +} + +/** + * @brief get parsed bool value of an required identifier + * + * @param identifier regested identifier + * + * @return parsed bool value + */ +bool +ArgParser::getBoolValue(const std::string &identifier) +{ + std::vector result; + + // get registered argument + ArgParser::ArgDefinition* arg = getArgument(identifier); + if(arg == nullptr) { + return false; + } + + // check argument-type + if(arg->type != ArgType::BOOL_TYPE) { + return false; + } + + // check result not empty + if(arg->results->size() == 0) { + return false; + } + + return arg->results->get(0)->getBool(); +} + +/** + * @brief convert type-value into string + * + * @param type type-value + * + * @return converted string + */ +const std::string +ArgParser::convertType(ArgParser::ArgType type) +{ + if(type == ArgType::STRING_TYPE) { + return "string"; + } + if(type == ArgType::INT_TYPE) { + return "number"; + } + if(type == ArgType::FLOAT_TYPE) { + return "floating point"; + } + if(type == ArgType::BOOL_TYPE) { + return "boolean"; + } + + return ""; +} + +/** + * @brief print arguments on cli + */ +void +ArgParser::print(const std::string &commandName) +{ + std::string commandString = commandName + " [options]"; + + // prepare table for arguments with flags + TableItem withFlags; + withFlags.addColumn("long"); + withFlags.addColumn("short"); + withFlags.addColumn("type"); + withFlags.addColumn("is required"); + withFlags.addColumn("help-text"); + + for(uint32_t i = 0; i < m_argumentList.size(); i++) + { + if(m_argumentList.at(i).withoutFlag == false) + { + // get type + const std::string type = convertType(m_argumentList.at(i).type); + + // required flag + std::string required = ""; + if(m_argumentList.at(i).required) + { + required = "x"; + commandString += " " + m_argumentList.at(i).longIdentifier + " ..."; + } + + // set row of table + withFlags.addRow(std::vector{ + m_argumentList.at(i).longIdentifier, + m_argumentList.at(i).shortIdentifier, + type, + required, + m_argumentList.at(i).helpText + }); + } + } + + // prepare table for arguments without flags + TableItem withoutFlags; + withoutFlags.addColumn("name"); + withoutFlags.addColumn("type"); + withoutFlags.addColumn("text"); + + for(uint32_t i = 0; i < m_argumentList.size(); i++) + { + if(m_argumentList.at(i).withoutFlag == true) + { + // get type + const std::string type = convertType(m_argumentList.at(i).type); + + // set row of table + withoutFlags.addRow(std::vector{ + "<" + m_argumentList.at(i).longIdentifier + ">", + type, + m_argumentList.at(i).helpText + }); + + commandString += " <" + m_argumentList.at(i).longIdentifier + ">"; + } + } + + std::cout<<"command: "< + * + * @copyright Apache License Version 2.0 + * + * Copyright 2022 Tobias Anker + * + * 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 +#include + +int main(int argc, char *argv[]) +{ + // error messages of the parser are printed via logger + Kitsunemimi::initConsoleLogger(true); + + Kitsunemimi::ArgParser parser("0.1.0"); + + Kitsunemimi::ErrorContainer error; + + // register flags without value + parser.registerPlain("debug,d", + "debug-flag to enable addtional debug output", + error); + // "registerPlain" allows it to register flags without any value, which says only true or flase + // if they were set or not set + + // register flags + parser.registerString("source", + "source-path", + error, + true); + parser.registerInteger("input,i", + "additional parameter", + error); + + // register other values + parser.registerString("mode", + "modus for converting", + error, + true, // true to make it requried + true); // true to register this without a "--"-flag + parser.registerString("destination", + "destination path for output", + error, + true, + true); + // register types: + // registerString + // registerInteger + // registerFloat + // registerBoolean + + bool ret = parser.parse(argc, argv, error); + if(ret == false) { + return 1; + } + // ret say, if the converting was successful or not. Error-message are written in the logger + + // check if flags without values were set. In this case check if the debug-flag was set + + // get values with or without flag as list of value for the case, that a flag was + // used multiple times within one cli-call: + const std::vector testValues = parser.getStringValues("source"); + const std::vector numbers = parser.getIntValues("input"); + // get types: + // getStringValues + // getIntValues + // getFloatValues + // getBoolValues + + // get values without flag: + const std::string mode = parser.getStringValue("mode"); + const std::string destination = parser.getStringValue("destination"); + // get types: + // getStringValue + // getIntValue + // getFloatValue + // getBoolValue + + //... + + return 0; +} diff --git a/src/libraries/libKitsunemimiArgs/tests/tests.pro b/src/libraries/libKitsunemimiArgs/tests/tests.pro new file mode 100644 index 00000000..ae9740b3 --- /dev/null +++ b/src/libraries/libKitsunemimiArgs/tests/tests.pro @@ -0,0 +1,10 @@ +TEMPLATE = subdirs +CONFIG += ordered +QT -= qt core gui +CONFIG += c++17 + +SUBDIRS = \ + unit_tests \ + cli_tests + +tests.depends = src diff --git a/src/libraries/libKitsunemimiArgs/tests/unit_tests/arg_parser_test.cpp b/src/libraries/libKitsunemimiArgs/tests/unit_tests/arg_parser_test.cpp new file mode 100644 index 00000000..80d8512b --- /dev/null +++ b/src/libraries/libKitsunemimiArgs/tests/unit_tests/arg_parser_test.cpp @@ -0,0 +1,506 @@ +/** + * @file arg_parser_test.cpp + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2022 Tobias Anker + * + * 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 "arg_parser_test.h" + +#include +#include + +namespace Kitsunemimi +{ + +ArgParser_Test::ArgParser_Test() + : Kitsunemimi::CompareTestHelper("ArgParser_Test") +{ + registerArgument_test(); + getArgument_test(); + convertValue_test(); + + registerString_test(); + registerInteger_test(); + registerFloat_test(); + registerBoolean_test(); + + parse_test(); + + getNumberOfValues_test(); + getStringValues_test(); + getIntValues_test(); + getFloatValues_test(); + getBoolValues_test(); + + getStringValue_test(); + getIntValue_test(); + getFloatValue_test(); + getBoolValue_test(); +} + +/** + * @brief registerArgument_test + */ +void +ArgParser_Test::registerArgument_test() +{ + ArgParser parser; + ErrorContainer error; + + TEST_EQUAL(parser.registerArgument("", + "this is an example", + ArgParser::ArgType::INT_TYPE, + true, + false, + true, + error) + , false); + TEST_EQUAL(parser.registerArgument("xyz,", + "this is an example", + ArgParser::ArgType::INT_TYPE, + true, + false, + true, + error) + , true); + TEST_EQUAL(parser.registerArgument(",a", + "this is an example", + ArgParser::ArgType::INT_TYPE, + true, + false, + true, + error) + , false); + TEST_EQUAL(parser.registerArgument("asdf,asdf", + "this is an example", + ArgParser::ArgType::INT_TYPE, + true, + false, + true, + error) + , false); + + TEST_EQUAL(parser.registerArgument("asdf,a", + "this is an example", + ArgParser::ArgType::INT_TYPE, + true, + false, + true, + error) + , true); + TEST_EQUAL(parser.registerArgument("asdf,a", + "this is an example", + ArgParser::ArgType::INT_TYPE, + true, + false, + true, + error) + , false); +} + +/** + * @brief getArgument_test + */ +void +ArgParser_Test::getArgument_test() +{ + ArgParser parser; + bool isNullptr = false; + ErrorContainer error; + + parser.registerArgument("asdf,a", + "this is an example", + ArgParser::ArgType::INT_TYPE, + true, + false, + true, + error); + + isNullptr = parser.getArgument("xyz") == nullptr; + TEST_EQUAL(isNullptr, true); + + isNullptr = parser.getArgument("--asdf") == nullptr; + TEST_EQUAL(isNullptr, false); + + isNullptr = parser.getArgument("-a") == nullptr; + TEST_EQUAL(isNullptr, false); + + ArgParser::ArgDefinition* ret = parser.getArgument("-a"); + TEST_EQUAL(ret->type, ArgParser::ArgType::INT_TYPE); + TEST_EQUAL(ret->helpText, std::string("this is an example")); + TEST_EQUAL(ret->required, true); + TEST_EQUAL(ret->withoutFlag, false); + TEST_EQUAL(ret->longIdentifier, "--asdf"); + TEST_EQUAL(ret->shortIdentifier, "-a"); +} + +/** + * @brief convertValue_test + */ +void +ArgParser_Test::convertValue_test() +{ + ArgParser parser; + DataItem* result = nullptr; + bool isNullptr = false; + ErrorContainer error; + + // string-type + // check if result is nullptr + isNullptr = parser.convertValue("asdf", ArgParser::ArgType::STRING_TYPE) == nullptr; + TEST_EQUAL(isNullptr, false); + isNullptr = parser.convertValue("1", ArgParser::ArgType::STRING_TYPE) == nullptr; + TEST_EQUAL(isNullptr, false); + + // check result value + result = parser.convertValue("asdf", ArgParser::ArgType::STRING_TYPE); + TEST_EQUAL(result->toValue()->getValueType(), DataValue::STRING_TYPE); + TEST_EQUAL(result->getString(), "asdf"); + + // int-type + // check if result is nullptr + isNullptr = parser.convertValue("1", ArgParser::ArgType::INT_TYPE) == nullptr; + TEST_EQUAL(isNullptr, false); + isNullptr = parser.convertValue("asdf", ArgParser::ArgType::INT_TYPE) == nullptr; + TEST_EQUAL(isNullptr, true); + + // check result value + result = parser.convertValue("42", ArgParser::ArgType::INT_TYPE); + TEST_EQUAL(result->toValue()->getValueType(), DataValue::INT_TYPE); + TEST_EQUAL(result->getInt(), 42); + + // float-type + // check if result is nullptr + isNullptr = parser.convertValue("1.0", ArgParser::ArgType::FLOAT_TYPE) == nullptr; + TEST_EQUAL(isNullptr, false); + isNullptr = parser.convertValue("1", ArgParser::ArgType::FLOAT_TYPE) == nullptr; + TEST_EQUAL(isNullptr, false); + isNullptr = parser.convertValue("asdf", ArgParser::ArgType::FLOAT_TYPE) == nullptr; + TEST_EQUAL(isNullptr, true); + + // check result value + result = parser.convertValue("42.25", ArgParser::ArgType::FLOAT_TYPE); + TEST_EQUAL(result->toValue()->getValueType(), DataValue::FLOAT_TYPE); + TEST_EQUAL(result->getDouble(), 42.25); + + // bool-type + // check if result is nullptr + isNullptr = parser.convertValue("1", ArgParser::ArgType::BOOL_TYPE) == nullptr; + TEST_EQUAL(isNullptr, false); + isNullptr = parser.convertValue("true", ArgParser::ArgType::BOOL_TYPE) == nullptr; + TEST_EQUAL(isNullptr, false); + isNullptr = parser.convertValue("True", ArgParser::ArgType::BOOL_TYPE) == nullptr; + TEST_EQUAL(isNullptr, false); + isNullptr = parser.convertValue("0", ArgParser::ArgType::BOOL_TYPE) == nullptr; + TEST_EQUAL(isNullptr, false); + isNullptr = parser.convertValue("false", ArgParser::ArgType::BOOL_TYPE) == nullptr; + TEST_EQUAL(isNullptr, false); + isNullptr = parser.convertValue("False", ArgParser::ArgType::BOOL_TYPE) == nullptr; + TEST_EQUAL(isNullptr, false); + + // check result value + result = parser.convertValue("true", ArgParser::ArgType::BOOL_TYPE); + TEST_EQUAL(result->toValue()->getValueType(), DataValue::BOOL_TYPE); + TEST_EQUAL(result->getBool(), true); +} + +/** + * @brief registerString_test + */ +void +ArgParser_Test::registerString_test() +{ + ArgParser parser; + ErrorContainer error; + + TEST_EQUAL(parser.registerString("", "this is an example", error, true, false), false); + TEST_EQUAL(parser.registerString("asdf", "this is an example", error, true, false), true); +} + +/** + * @brief registerInteger_test + */ +void +ArgParser_Test::registerInteger_test() +{ + ArgParser parser; + ErrorContainer error; + + TEST_EQUAL(parser.registerInteger("", "this is an example", error, true, false), false); + TEST_EQUAL(parser.registerInteger("asdf", "this is an example", error, true, false), true); +} + +/** + * @brief registerFloat_test + */ +void +ArgParser_Test::registerFloat_test() +{ + ArgParser parser; + ErrorContainer error; + + TEST_EQUAL(parser.registerFloat("", "this is an example", error, true, false), false); + TEST_EQUAL(parser.registerFloat("asdf", "this is an example", error, true, false), true); +} + +/** + * @brief registerBoolean_test + */ +void +ArgParser_Test::registerBoolean_test() +{ + ArgParser parser; + ErrorContainer error; + + TEST_EQUAL(parser.registerBoolean("", "this is an example", error, true, false), false); + TEST_EQUAL(parser.registerBoolean("asdf", "this is an example", error, true, false), true); +} + +/** + * @brief parse_test + */ +void +ArgParser_Test::parse_test() +{ + ArgParser parser; + ErrorContainer error; + + int argc = 15; + const char* argv[15]; + argv[0] = "-"; + argv[1] = "--test"; + argv[2] = "test1"; + argv[3] = "--test"; + argv[4] = "test2"; + argv[5] = "--integer"; + argv[6] = "1337"; + argv[7] = "-f"; + argv[8] = "123.5"; + argv[9] = "-b"; + argv[10] = "true"; + argv[11] = "poi"; + argv[12] = "42"; + argv[13] = "42.25"; + argv[14] = "false"; + + parser.registerString("test", " ", error); + parser.registerInteger("integer,i", " ", error); + parser.registerFloat("float,f", " ", error); + parser.registerBoolean("bool,b", " ", error); + parser.registerString("first_arg", "first argument", error, true, true); + parser.registerInteger("secondArg", "second argument", error, true, true); + parser.registerFloat("thirdArg", "third argument", error, true, true); + parser.registerBoolean("lastArg", "last argument", error, true, true); + + TEST_EQUAL(parser.parse(argc, argv, error), true); + + // negative test: set argument `bool` to a non-bool value + argv[6] = "asdf"; + TEST_EQUAL(parser.parse(argc, argv, error), false); + argv[6] = "true"; + + // negative test: set a value without flag to a false type + argv[12] = "asdf"; + TEST_EQUAL(parser.parse(argc, argv, error), false); + argv[12] = "42"; + + // negative test: register a required value, which is not given in the arguments + parser.registerBoolean("fail", "this is a boolean", error, true); + TEST_EQUAL(parser.parse(argc, argv, error), false); +} + +/** + * @brief getNumberOfValues_test + */ +void +ArgParser_Test::getNumberOfValues_test() +{ + ArgParser parser; + prepareTest(&parser); + + TEST_EQUAL(parser.getNumberOfValues("test"), 2); + TEST_EQUAL(parser.getNumberOfValues("bool"), 1); + TEST_EQUAL(parser.getNumberOfValues("first_arg"), 1); +} + +/** + * @brief getStringValues_test + */ +void +ArgParser_Test::getStringValues_test() +{ + ArgParser parser; + prepareTest(&parser); + + const std::vector ret = parser.getStringValues("test"); + TEST_EQUAL(ret.size(), 2); + TEST_EQUAL(ret.at(0), "test1"); + TEST_EQUAL(ret.at(1), "test2"); + + const std::vector ret2 = parser.getStringValues("first_arg"); + TEST_EQUAL(ret2.size(), 1); + TEST_EQUAL(ret2.at(0), "poi"); +} + +/** + * @brief getIntValues_test + */ +void +ArgParser_Test::getIntValues_test() +{ + ArgParser parser; + prepareTest(&parser); + + const std::vector ret = parser.getIntValues("integer"); + TEST_EQUAL(ret.size(), 1); + TEST_EQUAL(ret.at(0), 1337); + + const std::vector ret2 = parser.getIntValues("secondArg"); + TEST_EQUAL(ret2.size(), 1); + TEST_EQUAL(ret2.at(0), 42); +} + +/** + * @brief getFloatValues_test + */ +void +ArgParser_Test::getFloatValues_test() +{ + ArgParser parser; + prepareTest(&parser); + + const std::vector ret = parser.getFloatValues("f"); + TEST_EQUAL(ret.size(), 1); + TEST_EQUAL(ret.at(0), 123.5); + + const std::vector ret2 = parser.getFloatValues("thirdArg"); + TEST_EQUAL(ret2.size(), 1); + TEST_EQUAL(ret2.at(0), 42.25); +} + +/** + * @brief getBoolValues_test + */ +void +ArgParser_Test::getBoolValues_test() +{ + ArgParser parser; + prepareTest(&parser); + + const std::vector ret = parser.getBoolValues("bool"); + TEST_EQUAL(ret.size(), 1); + TEST_EQUAL(ret.at(0), true); + + const std::vector ret2 = parser.getBoolValues("lastArg"); + TEST_EQUAL(ret2.size(), 1); + TEST_EQUAL(ret2.at(0), false); +} + +/** + * @brief getStringValue_test + */ +void +ArgParser_Test::getStringValue_test() +{ + ArgParser parser; + prepareTest(&parser); + + const std::string ret2 = parser.getStringValue("first_arg"); + TEST_EQUAL(ret2, "poi"); +} + +/** + * @brief getIntValue_test + */ +void +ArgParser_Test::getIntValue_test() +{ + ArgParser parser; + prepareTest(&parser); + + const long ret2 = parser.getIntValue("secondArg"); + TEST_EQUAL(ret2, 42); +} + +/** + * @brief getFloatValue_test + */ +void +ArgParser_Test::getFloatValue_test() +{ + ArgParser parser; + prepareTest(&parser); + + const double ret2 = parser.getFloatValue("thirdArg"); + TEST_EQUAL(ret2, 42.25); +} + +/** + * @brief getBoolValue_test + */ +void +ArgParser_Test::getBoolValue_test() +{ + ArgParser parser; + prepareTest(&parser); + + const bool ret2 = parser.getBoolValue("lastArg"); + TEST_EQUAL(ret2, false); +} + +/** + * @brief ArgParser_Test::prepareTest + * @param parser + */ +void +ArgParser_Test::prepareTest(ArgParser *parser) +{ + ErrorContainer error; + + int argc = 15; + const char* argv[15]; + argv[0] = "-"; + argv[1] = "--test"; + argv[2] = "test1"; + argv[3] = "--test"; + argv[4] = "test2"; + argv[5] = "--integer"; + argv[6] = "1337"; + argv[7] = "-f"; + argv[8] = "123.5"; + argv[9] = "-b"; + argv[10] = "true"; + argv[11] = "poi"; + argv[12] = "42"; + argv[13] = "42.25"; + argv[14] = "false"; + + parser->registerString("test", " ", error); + parser->registerInteger("integer,i", " ", error); + parser->registerFloat("float,f", " ", error); + parser->registerBoolean("bool,b", " ", error); + parser->registerString("first_arg", "first argument", error, true, true); + parser->registerInteger("secondArg", "second argument", error, true, true); + parser->registerFloat("thirdArg", "third argument", error, true, true); + parser->registerBoolean("lastArg", "last argument", error, true, true); + + assert(parser->parse(argc, argv, error)); +} + +} diff --git a/src/libraries/libKitsunemimiArgs/tests/unit_tests/arg_parser_test.h b/src/libraries/libKitsunemimiArgs/tests/unit_tests/arg_parser_test.h new file mode 100644 index 00000000..649810df --- /dev/null +++ b/src/libraries/libKitsunemimiArgs/tests/unit_tests/arg_parser_test.h @@ -0,0 +1,67 @@ +/** + * @file arg_parser_test.h + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2022 Tobias Anker + * + * 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. + */ + +#ifndef ARG_PARSER_TEST_H +#define ARG_PARSER_TEST_H + +#include +#include + +namespace Kitsunemimi +{ +class ArgParser; + +class ArgParser_Test + : public Kitsunemimi::CompareTestHelper +{ +public: + ArgParser_Test(); + + void registerArgument_test(); + void getArgument_test(); + void convertValue_test(); + + void registerString_test(); + void registerInteger_test(); + void registerFloat_test(); + void registerBoolean_test(); + + void parse_test(); + + void getNumberOfValues_test(); + void getStringValues_test(); + void getIntValues_test(); + void getFloatValues_test(); + void getBoolValues_test(); + + void getStringValue_test(); + void getIntValue_test(); + void getFloatValue_test(); + void getBoolValue_test(); + +private: + void prepareTest(ArgParser* parser); +}; + +} + +#endif // ARG_PARSER_TEST_H diff --git a/src/libraries/libKitsunemimiArgs/tests/unit_tests/main.cpp b/src/libraries/libKitsunemimiArgs/tests/unit_tests/main.cpp new file mode 100644 index 00000000..d1db7a3e --- /dev/null +++ b/src/libraries/libKitsunemimiArgs/tests/unit_tests/main.cpp @@ -0,0 +1,30 @@ +/** + * @file main.cpp + * + * @author Tobias Anker + * + * @copyright Apache License Version 2.0 + * + * Copyright 2022 Tobias Anker + * + * 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 + +int main() +{ + Kitsunemimi::ArgParser_Test argParserTest; + + return 0; +} diff --git a/src/libraries/libKitsunemimiArgs/tests/unit_tests/unit_tests.pro b/src/libraries/libKitsunemimiArgs/tests/unit_tests/unit_tests.pro new file mode 100644 index 00000000..fbfbd8a7 --- /dev/null +++ b/src/libraries/libKitsunemimiArgs/tests/unit_tests/unit_tests.pro @@ -0,0 +1,21 @@ +include(../../defaults.pri) + +QT -= qt core gui + +CONFIG -= app_bundle +CONFIG += c++14 console + +LIBS += -L../../src -lKitsunemimiArgs +INCLUDEPATH += $$PWD + +LIBS += -L../../../libKitsunemimiCommon/src -lKitsunemimiCommon +LIBS += -L../../../libKitsunemimiCommon/src/debug -lKitsunemimiCommon +LIBS += -L../../../libKitsunemimiCommon/src/release -lKitsunemimiCommon +INCLUDEPATH += ../../../libKitsunemimiCommon/include + +SOURCES += \ + main.cpp \ + arg_parser_test.cpp + +HEADERS += \ + arg_parser_test.h diff --git a/src/libraries/libKitsunemimiCommon/CHANGELOG_old b/src/libraries/libKitsunemimiCommon/CHANGELOG_old new file mode 100644 index 00000000..10327d24 --- /dev/null +++ b/src/libraries/libKitsunemimiCommon/CHANGELOG_old @@ -0,0 +1,458 @@ +# Changelog + +## [0.27.0] - 2022-08-08 + +### Added +- added error-output to binary-files classes + +### Changed +- split bianary-files class into one which use direct-IO and one which doesn't +- remove `common_` from items- and method-directory names +- use github-actions as ci-pipeline + +### Fixed +- add missing default-callback to console-logging +- added some missing comments in binary-files class + + +## [0.26.1] - 2022-07-02 + +### Fixed +- removed broken precheck of thread-binding-functions + + +## [0.26.0] - 2022-06-26 + +### Changed +- Header of item-buffer was restructured to be stored in the buffer itself too + +### Added +- States of statemachine can now be connected to events, which are triggered, when entering the state +- Event-Queue to be also able to execute the Events of states by a separate thread +- item-buffer can now be completely backuped and restored + +### Fixed +- added missing include in state-class + + +## [0.25.3] - 2022-02-13 + +### Added +- callback to logger, which is called for each error-message + + +## [0.25.2] - 2022-01-30 + +### Added +- new read- and write-function for binary-files, which use a normal pointer instead of data-buffer + +### Fixed +- compile-warnings in data-items + + +## [0.25.1] - 2022-01-09 + +### Fixed +- fixed valgrind memory warning in data-items + + +## [0.25.0] - 2021-12-30 + +### Added +- support other types in table beside string-objects +- support list of core-ids instead of only a single code-id for thread-binding + + +## [0.24.0] - 2021-11-26 + +### Added +- add getter for inner table-header + +### Changed +- inner rework of the table-item +- improve speed of string-split-operations + +### Fixed +- fix writeCompleteFile-function to write the correct size +- made some more getter in data-items const + + +## [0.23.0] - 2021-11-17 + +### Added +- new getter in table-items to get a copy of the header and the body separately +- add new json-formated string-output to table-items +- add function to steal content of table-item to save unnecessary copy-operations +- add getter to table to request a single row of the table +- add function to convert string to upper and lower case +- add optional define-flag to make the logger-output completely while +- add function to data-buffer to calculate necessary number of blocks for a number of bytes +- add usage of error-container for file-operations +- tables can now be printed without header, if new flag is active + +### Changed +- improved the error-container to handle multiple messages in one container + +### Fixed +- added missing validation to read and write of text-files + +### Removed +- the show-one option for table-items was removed, because its is not used and was also broken + + +## [0.22.0] - 2021-10-26 + +### Changed +- the bool-flag while printing a table-item now doesn't show a single item verticall was changed to print the table without head-line +- error-messages for the logger are now handle by a struct, which also has a field for a possible solution +- error-messages are now printed as table +- the name of threads now doesn't have to be unique anymore, which makes automatic created threads in case of clients much easier + +### Fixed +- logging a table now add a line-break at the beginning to not break the output of the first line + + +## [0.21.0] - 2021-10-23 + +### Added +- give threads names for identification in thread-manager + +### Changed +- make thrad-binding to cpu-threads more flexible +- access to table-cells is now by row/column instead of x/y +- renamed public accessilbe variables in data-items +- code-cleanup in many parts of the libray + +### Fixed +- fixes to remove compile-warnings in clang + + +## [0.20.0] - 2021-10-02 + +### Added +- added parts of libKitsunemimiPersistence + - logger + - functions for binary-files + - functions for text-files + +### Changed +- moved from c++14 to c++17 + +### Removed +- moved common object-methods to other libraries + + +## [0.19.0] - 2021-09-26 + +### Added +- progress-bar for terminal output +- optional static part at the beginning of item-buffers + +### Changed +- changed minimal block-size of data-buffer from 512 to 8 byte + + +## [0.18.0] - 2021-03-29 + +### Added +- Item-Buffer for fast add and remove of items in a preallocated buffer + +### Fixed +- fix possible race-condition in barrier (not totally complete fixed) + + +## [0.17.1] - 2021-01-17 + +### Fixed +- hotfix for the broken hexlify + + +## [0.17.0] - 2021-01-17 + +### Added +- Base64 encode and decode to object-methods + +### Changed +- moved new and delete override into memory-leak-test to avoid possible conflics in other projects + +### Fixed +- removed compiler-warnings +- fixed possible memory-leak in thread class + + +## [0.16.1] - 2020-11-29 + +### Fixed +- the new cleanup-thread will now be started automatically + +### Changed +- changed some variables in the thread-class from protected to private. This is basically a API-breaking change, but the affected variables should have never been called by any other object than the thread-class itself. So its basically more a fix. + + +## [0.16.0] - 2020-11-25 + +### Added +- Cleanup-Thread, to delete threads in the backgroud, which are scheduled for deletion +- missing memory-leak tests +- some missing comments + +### Removed +- Removed some functions to stop a thread + + +## [0.15.2] - 2020-10-18 + +### Fixed +- fixed events for the event-queue + + +## [0.15.1] - 2020-08-16 + +### Changed +- memory-counter doesn't counter bytes allocated by new-delete-calls anymore, because there were crazy conflics with opencl + + +## [0.15.0] - 2020-08-16 + +### Added +- memory-leak-test-helper +- memory-leak-tests +- event-queue for threads + +### Changed +- update gitlab-ci + + +## [0.14.1] - 2020-08-05 + +### Changed +- build tests only in the ci-pipeline, but not everytime when running the build-script + +### Fixed +- smaller style-fixes + + +## [0.14.0] - 2020-08-03 + +### Added +- method to remove whitespaces from a string +- copy-assignment operator to DataBuffer +- trim-function for strings +- simple memory-counter to count new- and delete-calls for memory-leak-tests +- hexlify-function to convert objects into hex-represenation + +### Changed +- vector-method changed to use object-reference instead of pointer + +### Fixed +- manually trigger in barrier-class to reset counter again +- some smaller naming fixes + + +## [0.13.0] - 2020-04.13 + +### Changed +- renamed the methods of the data-, ring- and stack-buffer, to have unique names, because there were conflicts + +### Fixed +- bugfix for the allocateBlocks-method of the data-buffer, because there could be a dataloss if the new allocated block-number was smaller than the actual number +- bugfix for teh reset-method of the stack-buffer + + +## [0.12.1] - 2020-03-29 + +### Changed +- getObjectFromBuffer and getDataPointer in the ring-buffer returning not a const-pointer anymore + +### Fixed +- Destructor of the ring-buffer was broken + + +## [0.12.0] - 2020-03-14 + +### Added +- add ring-buffer +- add stack-buffer and stack-buffer-reserve + + +## [0.11.0] - 2020-02-25 + +### Added +- added `SpeedTestHelper`-class for performance-tests + +### Fixed +- fixed broken reset-function in the DataBuffer + +### Changed +- renamed generic `Test`-class to `CompareTestHelper` and moved the class to a subdirectory + + +## [0.10.1] - 2020-01-18 + +### Fixed +- fixed spin-lock to be not removed while optimizing by the compiler + + +## [0.10.0] - 2020-01-15 + +### Added +- clear-method to data-items + +### Changed +- methods of databuffer + +### Removed +- namespace `Common` + +### Fixed +- memory-leaks in data-items + + +## [0.9.0] - 2019-12-30 + +### Added +- string-method to replace substring within a string +- function to execute other executables + +### Changed +- changed style of split-string functions +- made some iterators in the data-items to const_iterator + +### Fixed +- fixed some segmentaion-faults + + +## [0.8.0] - 2019-11-22 + +### Added +- new Barrier-class + +### Changed +- data-items now support long and double +- moved Thread-class in subdirectory + +### Fixed +- little fixes in the data-items to avoid segmentation-faults + + +## [0.7.0] - 2019-11-02 + +### Changed +- renamed repo from `libKitsuneCommon` to `libKitsunemimiCommon` +- statemachine now uses primary int-values as identifier instead of strings +- renamed unit-test-class, because its also used for functional tests and not only for unit-tests +- renamed start- and stop-methods in thread to startThread and stopThread + +### Added +- parent-child-states to statemachine +- spin-locks to thread-class + +### Fixed +- statemachine now thread-save + + +## [0.6.0] - 2019-10-07 + +### Changed +- file-structure +- include paths + +### Fixed +- Maximum thead number for thead-bindung is not hardcoded anymore + + +## [0.5.4] - 2019-09-29 + +### Added +- Doxyfile for generating code-documentation +- copy-assingment-operator to DataMap, DataArray and DataValue +- copy-assingment-constructor to DataMap, DataArray and DataValue + + +## [0.5.3] - 2019-09-18 + +### Fixed +- the output of the toString-method of the data-items was not complete json-format + + +## [0.5.2] - 2019-09-15 + +### Changed +- quotes around string in toString are now only added, the its calld by the DataMap or DataArray + (this is basically an API-break, but its necessary for libKitsuneJson and to few for a 0.6 tag) + + +## [0.5.1] - 2019-09-08 + +### Added +- clear-function for table-items +- optional block-size option in constructor of the data-buffer +- support for bool-values in the DataValue-class + +### Changed +- union for internal data-handling in the DataValue-class + + +## [0.5.0] - 2019-08-31 + +### Added +- build-script for the library +- add show-one-options for tables to make it better readable for tables with one row +- column-width-limitation in tables +- rows in the table can now have multiple lines within one cell of the table +- new common method to split strings into part with a maximum size + +### Changed +- renamed in data-items: + - toString -> getString + - getString(...) -> getStringByKey(...) + - print -> toString +- renamed in table-items + - print -> toString +- renamed DataObject to DataMap include all relevant getter and setter + + +## [0.4.1] - 2019-08-26 + +### Fixed +- data-buffer was missing in the readme-file, which was fixed + + +## [0.4.0] - 2019-08-25 + +### Added +- table-items with unit-tests +- short description to all files +- content for readme-file + +### Changed +- in data-items: getSize() to size() +- in data-items: made print-method better usable +- renamed Test-class to UnitTest-class +- renamed data_structure-directory to common_items + + +## [0.3.0] - 2019-08-17 + +### Changed +- changed file-names for better readable + +### Fixed +- fixed memory-leak in DataArray and DataObject + + +## [0.2.0] - 2019-08-10 + +### Added +- data-items, which were originally json-items from libKitsuneJson +- simple statemachine + + +## [0.1.0] - 2019-08-03 + +### Added +- simple thread-class with some common used stuff +- simple buffer-class which use aligned-memory +- UNITTEST- and UNITTEST_NEG-makro +- method to split string +- method to remove empty entries for a vector of strings diff --git a/src/libraries/libKitsunemimiCommon/Doxyfile b/src/libraries/libKitsunemimiCommon/Doxyfile new file mode 100644 index 00000000..0f8cfd58 --- /dev/null +++ b/src/libraries/libKitsunemimiCommon/Doxyfile @@ -0,0 +1,2494 @@ +# Doxyfile 1.8.13 + +# This file describes the settings to be used by the documentation system +# doxygen (www.doxygen.org) for a project. +# +# All text after a double hash (##) is considered a comment and is placed in +# front of the TAG it is preceding. +# +# All text after a single hash (#) is considered a comment and will be ignored. +# The format is: +# TAG = value [value, ...] +# For lists, items can also be appended using: +# TAG += value [value, ...] +# Values that contain spaces should be placed between quotes (\" \"). + +#--------------------------------------------------------------------------- +# Project related configuration options +#--------------------------------------------------------------------------- + +# This tag specifies the encoding used for all characters in the config file +# that follow. The default is UTF-8 which is also the encoding used for all text +# before the first occurrence of this tag. Doxygen uses libiconv (or the iconv +# built into libc) for the transcoding. See http://www.gnu.org/software/libiconv +# for the list of possible encodings. +# The default value is: UTF-8. + +DOXYFILE_ENCODING = UTF-8 + +# The PROJECT_NAME tag is a single word (or a sequence of words surrounded by +# double-quotes, unless you are using Doxywizard) that should identify the +# project for which the documentation is generated. This name is used in the +# title of most generated pages and in a few other places. +# The default value is: My Project. + +PROJECT_NAME = "libKitsunemimiCommon" + +# The PROJECT_NUMBER tag can be used to enter a project or revision number. This +# could be handy for archiving the generated documentation or if some version +# control system is used. + +PROJECT_NUMBER = + +# Using the PROJECT_BRIEF tag one can provide an optional one line description +# for a project that appears at the top of each page and should give viewer a +# quick idea about the purpose of the project. Keep the description short. + +PROJECT_BRIEF = "Simple C++ library with commenly used functions for memory-handling, thread-handling, data representation and testing." + +# With the PROJECT_LOGO tag one can specify a logo or an icon that is included +# in the documentation. The maximum height of the logo should not exceed 55 +# pixels and the maximum width should not exceed 200 pixels. Doxygen will copy +# the logo to the output directory. + +PROJECT_LOGO = + +# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) path +# into which the generated documentation will be written. If a relative path is +# entered, it will be relative to the location where doxygen was started. If +# left blank the current directory will be used. + +OUTPUT_DIRECTORY = ./docs + +# If the CREATE_SUBDIRS tag is set to YES then doxygen will create 4096 sub- +# directories (in 2 levels) under the output directory of each output format and +# will distribute the generated files over these directories. Enabling this +# option can be useful when feeding doxygen a huge amount of source files, where +# putting all generated files in the same directory would otherwise causes +# performance problems for the file system. +# The default value is: NO. + +CREATE_SUBDIRS = NO + +# If the ALLOW_UNICODE_NAMES tag is set to YES, doxygen will allow non-ASCII +# characters to appear in the names of generated files. If set to NO, non-ASCII +# characters will be escaped, for example _xE3_x81_x84 will be used for Unicode +# U+3044. +# The default value is: NO. + +ALLOW_UNICODE_NAMES = NO + +# The OUTPUT_LANGUAGE tag is used to specify the language in which all +# documentation generated by doxygen is written. Doxygen will use this +# information to generate all constant output in the proper language. +# Possible values are: Afrikaans, Arabic, Armenian, Brazilian, Catalan, Chinese, +# Chinese-Traditional, Croatian, Czech, Danish, Dutch, English (United States), +# Esperanto, Farsi (Persian), Finnish, French, German, Greek, Hungarian, +# Indonesian, Italian, Japanese, Japanese-en (Japanese with English messages), +# Korean, Korean-en (Korean with English messages), Latvian, Lithuanian, +# Macedonian, Norwegian, Persian (Farsi), Polish, Portuguese, Romanian, Russian, +# Serbian, Serbian-Cyrillic, Slovak, Slovene, Spanish, Swedish, Turkish, +# Ukrainian and Vietnamese. +# The default value is: English. + +OUTPUT_LANGUAGE = English + +# If the BRIEF_MEMBER_DESC tag is set to YES, doxygen will include brief member +# descriptions after the members that are listed in the file and class +# documentation (similar to Javadoc). Set to NO to disable this. +# The default value is: YES. + +BRIEF_MEMBER_DESC = YES + +# If the REPEAT_BRIEF tag is set to YES, doxygen will prepend the brief +# description of a member or function before the detailed description +# +# Note: If both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the +# brief descriptions will be completely suppressed. +# The default value is: YES. + +REPEAT_BRIEF = YES + +# This tag implements a quasi-intelligent brief description abbreviator that is +# used to form the text in various listings. Each string in this list, if found +# as the leading text of the brief description, will be stripped from the text +# and the result, after processing the whole list, is used as the annotated +# text. Otherwise, the brief description is used as-is. If left blank, the +# following values are used ($name is automatically replaced with the name of +# the entity):The $name class, The $name widget, The $name file, is, provides, +# specifies, contains, represents, a, an and the. + +ABBREVIATE_BRIEF = "The $name class" \ + "The $name widget" \ + "The $name file" \ + is \ + provides \ + specifies \ + contains \ + represents \ + a \ + an \ + the + +# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then +# doxygen will generate a detailed section even if there is only a brief +# description. +# The default value is: NO. + +ALWAYS_DETAILED_SEC = NO + +# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all +# inherited members of a class in the documentation of that class as if those +# members were ordinary class members. Constructors, destructors and assignment +# operators of the base classes will not be shown. +# The default value is: NO. + +INLINE_INHERITED_MEMB = NO + +# If the FULL_PATH_NAMES tag is set to YES, doxygen will prepend the full path +# before files name in the file list and in the header files. If set to NO the +# shortest path that makes the file name unique will be used +# The default value is: YES. + +FULL_PATH_NAMES = YES + +# The STRIP_FROM_PATH tag can be used to strip a user-defined part of the path. +# Stripping is only done if one of the specified strings matches the left-hand +# part of the path. The tag can be used to show relative paths in the file list. +# If left blank the directory from which doxygen is run is used as the path to +# strip. +# +# Note that you can specify absolute paths here, but also relative paths, which +# will be relative from the directory where doxygen is started. +# This tag requires that the tag FULL_PATH_NAMES is set to YES. + +STRIP_FROM_PATH = + +# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of the +# path mentioned in the documentation of a class, which tells the reader which +# header file to include in order to use a class. If left blank only the name of +# the header file containing the class definition is used. Otherwise one should +# specify the list of include paths that are normally passed to the compiler +# using the -I flag. + +STRIP_FROM_INC_PATH = + +# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter (but +# less readable) file names. This can be useful is your file systems doesn't +# support long names like on DOS, Mac, or CD-ROM. +# The default value is: NO. + +SHORT_NAMES = NO + +# If the JAVADOC_AUTOBRIEF tag is set to YES then doxygen will interpret the +# first line (until the first dot) of a Javadoc-style comment as the brief +# description. If set to NO, the Javadoc-style will behave just like regular Qt- +# style comments (thus requiring an explicit @brief command for a brief +# description.) +# The default value is: NO. + +JAVADOC_AUTOBRIEF = NO + +# If the QT_AUTOBRIEF tag is set to YES then doxygen will interpret the first +# line (until the first dot) of a Qt-style comment as the brief description. If +# set to NO, the Qt-style will behave just like regular Qt-style comments (thus +# requiring an explicit \brief command for a brief description.) +# The default value is: NO. + +QT_AUTOBRIEF = YES + +# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make doxygen treat a +# multi-line C++ special comment block (i.e. a block of //! or /// comments) as +# a brief description. This used to be the default behavior. The new default is +# to treat a multi-line C++ comment block as a detailed description. Set this +# tag to YES if you prefer the old behavior instead. +# +# Note that setting this tag to YES also means that rational rose comments are +# not recognized any more. +# The default value is: NO. + +MULTILINE_CPP_IS_BRIEF = NO + +# If the INHERIT_DOCS tag is set to YES then an undocumented member inherits the +# documentation from any documented member that it re-implements. +# The default value is: YES. + +INHERIT_DOCS = YES + +# If the SEPARATE_MEMBER_PAGES tag is set to YES then doxygen will produce a new +# page for each member. If set to NO, the documentation of a member will be part +# of the file/class/namespace that contains it. +# The default value is: NO. + +SEPARATE_MEMBER_PAGES = NO + +# The TAB_SIZE tag can be used to set the number of spaces in a tab. Doxygen +# uses this value to replace tabs by spaces in code fragments. +# Minimum value: 1, maximum value: 16, default value: 4. + +TAB_SIZE = 4 + +# This tag can be used to specify a number of aliases that act as commands in +# the documentation. An alias has the form: +# name=value +# For example adding +# "sideeffect=@par Side Effects:\n" +# will allow you to put the command \sideeffect (or @sideeffect) in the +# documentation, which will result in a user-defined paragraph with heading +# "Side Effects:". You can put \n's in the value part of an alias to insert +# newlines. + +ALIASES = + +# This tag can be used to specify a number of word-keyword mappings (TCL only). +# A mapping has the form "name=value". For example adding "class=itcl::class" +# will allow you to use the command class in the itcl::class meaning. + +TCL_SUBST = + +# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C sources +# only. Doxygen will then generate output that is more tailored for C. For +# instance, some of the names that are used will be different. The list of all +# members will be omitted, etc. +# The default value is: NO. + +OPTIMIZE_OUTPUT_FOR_C = NO + +# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java or +# Python sources only. Doxygen will then generate output that is more tailored +# for that language. For instance, namespaces will be presented as packages, +# qualified scopes will look different, etc. +# The default value is: NO. + +OPTIMIZE_OUTPUT_JAVA = NO + +# Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran +# sources. Doxygen will then generate output that is tailored for Fortran. +# The default value is: NO. + +OPTIMIZE_FOR_FORTRAN = NO + +# Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL +# sources. Doxygen will then generate output that is tailored for VHDL. +# The default value is: NO. + +OPTIMIZE_OUTPUT_VHDL = NO + +# Doxygen selects the parser to use depending on the extension of the files it +# parses. With this tag you can assign which parser to use for a given +# extension. Doxygen has a built-in mapping, but you can override or extend it +# using this tag. The format is ext=language, where ext is a file extension, and +# language is one of the parsers supported by doxygen: IDL, Java, Javascript, +# C#, C, C++, D, PHP, Objective-C, Python, Fortran (fixed format Fortran: +# FortranFixed, free formatted Fortran: FortranFree, unknown formatted Fortran: +# Fortran. In the later case the parser tries to guess whether the code is fixed +# or free formatted code, this is the default for Fortran type files), VHDL. For +# instance to make doxygen treat .inc files as Fortran files (default is PHP), +# and .f files as C (default is Fortran), use: inc=Fortran f=C. +# +# Note: For files without extension you can use no_extension as a placeholder. +# +# Note that for custom extensions you also need to set FILE_PATTERNS otherwise +# the files are not read by doxygen. + +EXTENSION_MAPPING = + +# If the MARKDOWN_SUPPORT tag is enabled then doxygen pre-processes all comments +# according to the Markdown format, which allows for more readable +# documentation. See http://daringfireball.net/projects/markdown/ for details. +# The output of markdown processing is further processed by doxygen, so you can +# mix doxygen, HTML, and XML commands with Markdown formatting. Disable only in +# case of backward compatibilities issues. +# The default value is: YES. + +MARKDOWN_SUPPORT = YES + +# When the TOC_INCLUDE_HEADINGS tag is set to a non-zero value, all headings up +# to that level are automatically included in the table of contents, even if +# they do not have an id attribute. +# Note: This feature currently applies only to Markdown headings. +# Minimum value: 0, maximum value: 99, default value: 0. +# This tag requires that the tag MARKDOWN_SUPPORT is set to YES. + +TOC_INCLUDE_HEADINGS = 0 + +# When enabled doxygen tries to link words that correspond to documented +# classes, or namespaces to their corresponding documentation. Such a link can +# be prevented in individual cases by putting a % sign in front of the word or +# globally by setting AUTOLINK_SUPPORT to NO. +# The default value is: YES. + +AUTOLINK_SUPPORT = YES + +# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want +# to include (a tag file for) the STL sources as input, then you should set this +# tag to YES in order to let doxygen match functions declarations and +# definitions whose arguments contain STL classes (e.g. func(std::string); +# versus func(std::string) {}). This also make the inheritance and collaboration +# diagrams that involve STL classes more complete and accurate. +# The default value is: NO. + +BUILTIN_STL_SUPPORT = NO + +# If you use Microsoft's C++/CLI language, you should set this option to YES to +# enable parsing support. +# The default value is: NO. + +CPP_CLI_SUPPORT = NO + +# Set the SIP_SUPPORT tag to YES if your project consists of sip (see: +# http://www.riverbankcomputing.co.uk/software/sip/intro) sources only. Doxygen +# will parse them like normal C++ but will assume all classes use public instead +# of private inheritance when no explicit protection keyword is present. +# The default value is: NO. + +SIP_SUPPORT = NO + +# For Microsoft's IDL there are propget and propput attributes to indicate +# getter and setter methods for a property. Setting this option to YES will make +# doxygen to replace the get and set methods by a property in the documentation. +# This will only work if the methods are indeed getting or setting a simple +# type. If this is not the case, or you want to show the methods anyway, you +# should set this option to NO. +# The default value is: YES. + +IDL_PROPERTY_SUPPORT = YES + +# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC +# tag is set to YES then doxygen will reuse the documentation of the first +# member in the group (if any) for the other members of the group. By default +# all members of a group must be documented explicitly. +# The default value is: NO. + +DISTRIBUTE_GROUP_DOC = NO + +# If one adds a struct or class to a group and this option is enabled, then also +# any nested class or struct is added to the same group. By default this option +# is disabled and one has to add nested compounds explicitly via \ingroup. +# The default value is: NO. + +GROUP_NESTED_COMPOUNDS = NO + +# Set the SUBGROUPING tag to YES to allow class member groups of the same type +# (for instance a group of public functions) to be put as a subgroup of that +# type (e.g. under the Public Functions section). Set it to NO to prevent +# subgrouping. Alternatively, this can be done per class using the +# \nosubgrouping command. +# The default value is: YES. + +SUBGROUPING = YES + +# When the INLINE_GROUPED_CLASSES tag is set to YES, classes, structs and unions +# are shown inside the group in which they are included (e.g. using \ingroup) +# instead of on a separate page (for HTML and Man pages) or section (for LaTeX +# and RTF). +# +# Note that this feature does not work in combination with +# SEPARATE_MEMBER_PAGES. +# The default value is: NO. + +INLINE_GROUPED_CLASSES = NO + +# When the INLINE_SIMPLE_STRUCTS tag is set to YES, structs, classes, and unions +# with only public data fields or simple typedef fields will be shown inline in +# the documentation of the scope in which they are defined (i.e. file, +# namespace, or group documentation), provided this scope is documented. If set +# to NO, structs, classes, and unions are shown on a separate page (for HTML and +# Man pages) or section (for LaTeX and RTF). +# The default value is: NO. + +INLINE_SIMPLE_STRUCTS = NO + +# When TYPEDEF_HIDES_STRUCT tag is enabled, a typedef of a struct, union, or +# enum is documented as struct, union, or enum with the name of the typedef. So +# typedef struct TypeS {} TypeT, will appear in the documentation as a struct +# with name TypeT. When disabled the typedef will appear as a member of a file, +# namespace, or class. And the struct will be named TypeS. This can typically be +# useful for C code in case the coding convention dictates that all compound +# types are typedef'ed and only the typedef is referenced, never the tag name. +# The default value is: NO. + +TYPEDEF_HIDES_STRUCT = NO + +# The size of the symbol lookup cache can be set using LOOKUP_CACHE_SIZE. This +# cache is used to resolve symbols given their name and scope. Since this can be +# an expensive process and often the same symbol appears multiple times in the +# code, doxygen keeps a cache of pre-resolved symbols. If the cache is too small +# doxygen will become slower. If the cache is too large, memory is wasted. The +# cache size is given by this formula: 2^(16+LOOKUP_CACHE_SIZE). The valid range +# is 0..9, the default is 0, corresponding to a cache size of 2^16=65536 +# symbols. At the end of a run doxygen will report the cache usage and suggest +# the optimal cache size from a speed point of view. +# Minimum value: 0, maximum value: 9, default value: 0. + +LOOKUP_CACHE_SIZE = 0 + +#--------------------------------------------------------------------------- +# Build related configuration options +#--------------------------------------------------------------------------- + +# If the EXTRACT_ALL tag is set to YES, doxygen will assume all entities in +# documentation are documented, even if no documentation was available. Private +# class members and static file members will be hidden unless the +# EXTRACT_PRIVATE respectively EXTRACT_STATIC tags are set to YES. +# Note: This will also disable the warnings about undocumented members that are +# normally produced when WARNINGS is set to YES. +# The default value is: NO. + +EXTRACT_ALL = YES + +# If the EXTRACT_PRIVATE tag is set to YES, all private members of a class will +# be included in the documentation. +# The default value is: NO. + +EXTRACT_PRIVATE = NO + +# If the EXTRACT_PACKAGE tag is set to YES, all members with package or internal +# scope will be included in the documentation. +# The default value is: NO. + +EXTRACT_PACKAGE = NO + +# If the EXTRACT_STATIC tag is set to YES, all static members of a file will be +# included in the documentation. +# The default value is: NO. + +EXTRACT_STATIC = NO + +# If the EXTRACT_LOCAL_CLASSES tag is set to YES, classes (and structs) defined +# locally in source files will be included in the documentation. If set to NO, +# only classes defined in header files are included. Does not have any effect +# for Java sources. +# The default value is: YES. + +EXTRACT_LOCAL_CLASSES = YES + +# This flag is only useful for Objective-C code. If set to YES, local methods, +# which are defined in the implementation section but not in the interface are +# included in the documentation. If set to NO, only methods in the interface are +# included. +# The default value is: NO. + +EXTRACT_LOCAL_METHODS = NO + +# If this flag is set to YES, the members of anonymous namespaces will be +# extracted and appear in the documentation as a namespace called +# 'anonymous_namespace{file}', where file will be replaced with the base name of +# the file that contains the anonymous namespace. By default anonymous namespace +# are hidden. +# The default value is: NO. + +EXTRACT_ANON_NSPACES = NO + +# If the HIDE_UNDOC_MEMBERS tag is set to YES, doxygen will hide all +# undocumented members inside documented classes or files. If set to NO these +# members will be included in the various overviews, but no documentation +# section is generated. This option has no effect if EXTRACT_ALL is enabled. +# The default value is: NO. + +HIDE_UNDOC_MEMBERS = NO + +# If the HIDE_UNDOC_CLASSES tag is set to YES, doxygen will hide all +# undocumented classes that are normally visible in the class hierarchy. If set +# to NO, these classes will be included in the various overviews. This option +# has no effect if EXTRACT_ALL is enabled. +# The default value is: NO. + +HIDE_UNDOC_CLASSES = NO + +# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, doxygen will hide all friend +# (class|struct|union) declarations. If set to NO, these declarations will be +# included in the documentation. +# The default value is: NO. + +HIDE_FRIEND_COMPOUNDS = NO + +# If the HIDE_IN_BODY_DOCS tag is set to YES, doxygen will hide any +# documentation blocks found inside the body of a function. If set to NO, these +# blocks will be appended to the function's detailed documentation block. +# The default value is: NO. + +HIDE_IN_BODY_DOCS = NO + +# The INTERNAL_DOCS tag determines if documentation that is typed after a +# \internal command is included. If the tag is set to NO then the documentation +# will be excluded. Set it to YES to include the internal documentation. +# The default value is: NO. + +INTERNAL_DOCS = NO + +# If the CASE_SENSE_NAMES tag is set to NO then doxygen will only generate file +# names in lower-case letters. If set to YES, upper-case letters are also +# allowed. This is useful if you have classes or files whose names only differ +# in case and if your file system supports case sensitive file names. Windows +# and Mac users are advised to set this option to NO. +# The default value is: system dependent. + +CASE_SENSE_NAMES = YES + +# If the HIDE_SCOPE_NAMES tag is set to NO then doxygen will show members with +# their full class and namespace scopes in the documentation. If set to YES, the +# scope will be hidden. +# The default value is: NO. + +HIDE_SCOPE_NAMES = NO + +# If the HIDE_COMPOUND_REFERENCE tag is set to NO (default) then doxygen will +# append additional text to a page's title, such as Class Reference. If set to +# YES the compound reference will be hidden. +# The default value is: NO. + +HIDE_COMPOUND_REFERENCE= NO + +# If the SHOW_INCLUDE_FILES tag is set to YES then doxygen will put a list of +# the files that are included by a file in the documentation of that file. +# The default value is: YES. + +SHOW_INCLUDE_FILES = YES + +# If the SHOW_GROUPED_MEMB_INC tag is set to YES then Doxygen will add for each +# grouped member an include statement to the documentation, telling the reader +# which file to include in order to use the member. +# The default value is: NO. + +SHOW_GROUPED_MEMB_INC = NO + +# If the FORCE_LOCAL_INCLUDES tag is set to YES then doxygen will list include +# files with double quotes in the documentation rather than with sharp brackets. +# The default value is: NO. + +FORCE_LOCAL_INCLUDES = NO + +# If the INLINE_INFO tag is set to YES then a tag [inline] is inserted in the +# documentation for inline members. +# The default value is: YES. + +INLINE_INFO = YES + +# If the SORT_MEMBER_DOCS tag is set to YES then doxygen will sort the +# (detailed) documentation of file and class members alphabetically by member +# name. If set to NO, the members will appear in declaration order. +# The default value is: YES. + +SORT_MEMBER_DOCS = YES + +# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the brief +# descriptions of file, namespace and class members alphabetically by member +# name. If set to NO, the members will appear in declaration order. Note that +# this will also influence the order of the classes in the class list. +# The default value is: NO. + +SORT_BRIEF_DOCS = NO + +# If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen will sort the +# (brief and detailed) documentation of class members so that constructors and +# destructors are listed first. If set to NO the constructors will appear in the +# respective orders defined by SORT_BRIEF_DOCS and SORT_MEMBER_DOCS. +# Note: If SORT_BRIEF_DOCS is set to NO this option is ignored for sorting brief +# member documentation. +# Note: If SORT_MEMBER_DOCS is set to NO this option is ignored for sorting +# detailed member documentation. +# The default value is: NO. + +SORT_MEMBERS_CTORS_1ST = NO + +# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the hierarchy +# of group names into alphabetical order. If set to NO the group names will +# appear in their defined order. +# The default value is: NO. + +SORT_GROUP_NAMES = NO + +# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be sorted by +# fully-qualified names, including namespaces. If set to NO, the class list will +# be sorted only by class name, not including the namespace part. +# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES. +# Note: This option applies only to the class list, not to the alphabetical +# list. +# The default value is: NO. + +SORT_BY_SCOPE_NAME = NO + +# If the STRICT_PROTO_MATCHING option is enabled and doxygen fails to do proper +# type resolution of all parameters of a function it will reject a match between +# the prototype and the implementation of a member function even if there is +# only one candidate or it is obvious which candidate to choose by doing a +# simple string match. By disabling STRICT_PROTO_MATCHING doxygen will still +# accept a match between prototype and implementation in such cases. +# The default value is: NO. + +STRICT_PROTO_MATCHING = NO + +# The GENERATE_TODOLIST tag can be used to enable (YES) or disable (NO) the todo +# list. This list is created by putting \todo commands in the documentation. +# The default value is: YES. + +GENERATE_TODOLIST = YES + +# The GENERATE_TESTLIST tag can be used to enable (YES) or disable (NO) the test +# list. This list is created by putting \test commands in the documentation. +# The default value is: YES. + +GENERATE_TESTLIST = YES + +# The GENERATE_BUGLIST tag can be used to enable (YES) or disable (NO) the bug +# list. This list is created by putting \bug commands in the documentation. +# The default value is: YES. + +GENERATE_BUGLIST = YES + +# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or disable (NO) +# the deprecated list. This list is created by putting \deprecated commands in +# the documentation. +# The default value is: YES. + +GENERATE_DEPRECATEDLIST= YES + +# The ENABLED_SECTIONS tag can be used to enable conditional documentation +# sections, marked by \if ... \endif and \cond +# ... \endcond blocks. + +ENABLED_SECTIONS = + +# The MAX_INITIALIZER_LINES tag determines the maximum number of lines that the +# initial value of a variable or macro / define can have for it to appear in the +# documentation. If the initializer consists of more lines than specified here +# it will be hidden. Use a value of 0 to hide initializers completely. The +# appearance of the value of individual variables and macros / defines can be +# controlled using \showinitializer or \hideinitializer command in the +# documentation regardless of this setting. +# Minimum value: 0, maximum value: 10000, default value: 30. + +MAX_INITIALIZER_LINES = 30 + +# Set the SHOW_USED_FILES tag to NO to disable the list of files generated at +# the bottom of the documentation of classes and structs. If set to YES, the +# list will mention the files that were used to generate the documentation. +# The default value is: YES. + +SHOW_USED_FILES = YES + +# Set the SHOW_FILES tag to NO to disable the generation of the Files page. This +# will remove the Files entry from the Quick Index and from the Folder Tree View +# (if specified). +# The default value is: YES. + +SHOW_FILES = YES + +# Set the SHOW_NAMESPACES tag to NO to disable the generation of the Namespaces +# page. This will remove the Namespaces entry from the Quick Index and from the +# Folder Tree View (if specified). +# The default value is: YES. + +SHOW_NAMESPACES = YES + +# The FILE_VERSION_FILTER tag can be used to specify a program or script that +# doxygen should invoke to get the current version for each file (typically from +# the version control system). Doxygen will invoke the program by executing (via +# popen()) the command command input-file, where command is the value of the +# FILE_VERSION_FILTER tag, and input-file is the name of an input file provided +# by doxygen. Whatever the program writes to standard output is used as the file +# version. For an example see the documentation. + +FILE_VERSION_FILTER = + +# The LAYOUT_FILE tag can be used to specify a layout file which will be parsed +# by doxygen. The layout file controls the global structure of the generated +# output files in an output format independent way. To create the layout file +# that represents doxygen's defaults, run doxygen with the -l option. You can +# optionally specify a file name after the option, if omitted DoxygenLayout.xml +# will be used as the name of the layout file. +# +# Note that if you run doxygen from a directory containing a file called +# DoxygenLayout.xml, doxygen will parse it automatically even if the LAYOUT_FILE +# tag is left empty. + +LAYOUT_FILE = + +# The CITE_BIB_FILES tag can be used to specify one or more bib files containing +# the reference definitions. This must be a list of .bib files. The .bib +# extension is automatically appended if omitted. This requires the bibtex tool +# to be installed. See also http://en.wikipedia.org/wiki/BibTeX for more info. +# For LaTeX the style of the bibliography can be controlled using +# LATEX_BIB_STYLE. To use this feature you need bibtex and perl available in the +# search path. See also \cite for info how to create references. + +CITE_BIB_FILES = + +#--------------------------------------------------------------------------- +# Configuration options related to warning and progress messages +#--------------------------------------------------------------------------- + +# The QUIET tag can be used to turn on/off the messages that are generated to +# standard output by doxygen. If QUIET is set to YES this implies that the +# messages are off. +# The default value is: NO. + +QUIET = NO + +# The WARNINGS tag can be used to turn on/off the warning messages that are +# generated to standard error (stderr) by doxygen. If WARNINGS is set to YES +# this implies that the warnings are on. +# +# Tip: Turn warnings on while writing the documentation. +# The default value is: YES. + +WARNINGS = YES + +# If the WARN_IF_UNDOCUMENTED tag is set to YES then doxygen will generate +# warnings for undocumented members. If EXTRACT_ALL is set to YES then this flag +# will automatically be disabled. +# The default value is: YES. + +WARN_IF_UNDOCUMENTED = YES + +# If the WARN_IF_DOC_ERROR tag is set to YES, doxygen will generate warnings for +# potential errors in the documentation, such as not documenting some parameters +# in a documented function, or documenting parameters that don't exist or using +# markup commands wrongly. +# The default value is: YES. + +WARN_IF_DOC_ERROR = YES + +# This WARN_NO_PARAMDOC option can be enabled to get warnings for functions that +# are documented, but have no documentation for their parameters or return +# value. If set to NO, doxygen will only warn about wrong or incomplete +# parameter documentation, but not about the absence of documentation. +# The default value is: NO. + +WARN_NO_PARAMDOC = NO + +# If the WARN_AS_ERROR tag is set to YES then doxygen will immediately stop when +# a warning is encountered. +# The default value is: NO. + +WARN_AS_ERROR = NO + +# The WARN_FORMAT tag determines the format of the warning messages that doxygen +# can produce. The string should contain the $file, $line, and $text tags, which +# will be replaced by the file and line number from which the warning originated +# and the warning text. Optionally the format may contain $version, which will +# be replaced by the version of the file (if it could be obtained via +# FILE_VERSION_FILTER) +# The default value is: $file:$line: $text. + +WARN_FORMAT = "$file:$line: $text" + +# The WARN_LOGFILE tag can be used to specify a file to which warning and error +# messages should be written. If left blank the output is written to standard +# error (stderr). + +WARN_LOGFILE = + +#--------------------------------------------------------------------------- +# Configuration options related to the input files +#--------------------------------------------------------------------------- + +# The INPUT tag is used to specify the files and/or directories that contain +# documented source files. You may enter file names like myfile.cpp or +# directories like /usr/src/myproject. Separate the files or directories with +# spaces. See also FILE_PATTERNS and EXTENSION_MAPPING +# Note: If this tag is empty the current directory is searched. + +INPUT = ./src ./include + +# This tag can be used to specify the character encoding of the source files +# that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses +# libiconv (or the iconv built into libc) for the transcoding. See the libiconv +# documentation (see: http://www.gnu.org/software/libiconv) for the list of +# possible encodings. +# The default value is: UTF-8. + +INPUT_ENCODING = UTF-8 + +# If the value of the INPUT tag contains directories, you can use the +# FILE_PATTERNS tag to specify one or more wildcard patterns (like *.cpp and +# *.h) to filter out the source-files in the directories. +# +# Note that for custom extensions or not directly supported extensions you also +# need to set EXTENSION_MAPPING for the extension otherwise the files are not +# read by doxygen. +# +# If left blank the following patterns are tested:*.c, *.cc, *.cxx, *.cpp, +# *.c++, *.java, *.ii, *.ixx, *.ipp, *.i++, *.inl, *.idl, *.ddl, *.odl, *.h, +# *.hh, *.hxx, *.hpp, *.h++, *.cs, *.d, *.php, *.php4, *.php5, *.phtml, *.inc, +# *.m, *.markdown, *.md, *.mm, *.dox, *.py, *.pyw, *.f90, *.f95, *.f03, *.f08, +# *.f, *.for, *.tcl, *.vhd, *.vhdl, *.ucf and *.qsf. + +FILE_PATTERNS = *.c \ + *.cc \ + *.cxx \ + *.cpp \ + *.c++ \ + *.java \ + *.ii \ + *.ixx \ + *.ipp \ + *.i++ \ + *.inl \ + *.idl \ + *.ddl \ + *.odl \ + *.h \ + *.hh \ + *.hxx \ + *.hpp \ + *.h++ \ + *.cs \ + *.d \ + *.php \ + *.php4 \ + *.php5 \ + *.phtml \ + *.inc \ + *.m \ + *.markdown \ + *.md \ + *.mm \ + *.dox \ + *.py \ + *.pyw \ + *.f90 \ + *.f95 \ + *.f03 \ + *.f08 \ + *.f \ + *.for \ + *.tcl \ + *.vhd \ + *.vhdl \ + *.ucf \ + *.qsf + +# The RECURSIVE tag can be used to specify whether or not subdirectories should +# be searched for input files as well. +# The default value is: NO. + +RECURSIVE = YES + +# The EXCLUDE tag can be used to specify files and/or directories that should be +# excluded from the INPUT source files. This way you can easily exclude a +# subdirectory from a directory tree whose root is specified with the INPUT tag. +# +# Note that relative paths are relative to the directory from which doxygen is +# run. + +EXCLUDE = + +# The EXCLUDE_SYMLINKS tag can be used to select whether or not files or +# directories that are symbolic links (a Unix file system feature) are excluded +# from the input. +# The default value is: NO. + +EXCLUDE_SYMLINKS = NO + +# If the value of the INPUT tag contains directories, you can use the +# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude +# certain files from those directories. +# +# Note that the wildcards are matched against the file with absolute path, so to +# exclude all test directories for example use the pattern */test/* + +EXCLUDE_PATTERNS = + +# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names +# (namespaces, classes, functions, etc.) that should be excluded from the +# output. The symbol name can be a fully qualified name, a word, or if the +# wildcard * is used, a substring. Examples: ANamespace, AClass, +# AClass::ANamespace, ANamespace::*Test +# +# Note that the wildcards are matched against the file with absolute path, so to +# exclude all test directories use the pattern */test/* + +EXCLUDE_SYMBOLS = + +# The EXAMPLE_PATH tag can be used to specify one or more files or directories +# that contain example code fragments that are included (see the \include +# command). + +EXAMPLE_PATH = + +# If the value of the EXAMPLE_PATH tag contains directories, you can use the +# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp and +# *.h) to filter out the source-files in the directories. If left blank all +# files are included. + +EXAMPLE_PATTERNS = * + +# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be +# searched for input files to be used with the \include or \dontinclude commands +# irrespective of the value of the RECURSIVE tag. +# The default value is: NO. + +EXAMPLE_RECURSIVE = NO + +# The IMAGE_PATH tag can be used to specify one or more files or directories +# that contain images that are to be included in the documentation (see the +# \image command). + +IMAGE_PATH = + +# The INPUT_FILTER tag can be used to specify a program that doxygen should +# invoke to filter for each input file. Doxygen will invoke the filter program +# by executing (via popen()) the command: +# +# +# +# where is the value of the INPUT_FILTER tag, and is the +# name of an input file. Doxygen will then use the output that the filter +# program writes to standard output. If FILTER_PATTERNS is specified, this tag +# will be ignored. +# +# Note that the filter must not add or remove lines; it is applied before the +# code is scanned, but not when the output code is generated. If lines are added +# or removed, the anchors will not be placed correctly. +# +# Note that for custom extensions or not directly supported extensions you also +# need to set EXTENSION_MAPPING for the extension otherwise the files are not +# properly processed by doxygen. + +INPUT_FILTER = + +# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern +# basis. Doxygen will compare the file name with each pattern and apply the +# filter if there is a match. The filters are a list of the form: pattern=filter +# (like *.cpp=my_cpp_filter). See INPUT_FILTER for further information on how +# filters are used. If the FILTER_PATTERNS tag is empty or if none of the +# patterns match the file name, INPUT_FILTER is applied. +# +# Note that for custom extensions or not directly supported extensions you also +# need to set EXTENSION_MAPPING for the extension otherwise the files are not +# properly processed by doxygen. + +FILTER_PATTERNS = + +# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using +# INPUT_FILTER) will also be used to filter the input files that are used for +# producing the source files to browse (i.e. when SOURCE_BROWSER is set to YES). +# The default value is: NO. + +FILTER_SOURCE_FILES = NO + +# The FILTER_SOURCE_PATTERNS tag can be used to specify source filters per file +# pattern. A pattern will override the setting for FILTER_PATTERN (if any) and +# it is also possible to disable source filtering for a specific pattern using +# *.ext= (so without naming a filter). +# This tag requires that the tag FILTER_SOURCE_FILES is set to YES. + +FILTER_SOURCE_PATTERNS = + +# If the USE_MDFILE_AS_MAINPAGE tag refers to the name of a markdown file that +# is part of the input, its contents will be placed on the main page +# (index.html). This can be useful if you have a project on for instance GitHub +# and want to reuse the introduction page also for the doxygen output. + +USE_MDFILE_AS_MAINPAGE = + +#--------------------------------------------------------------------------- +# Configuration options related to source browsing +#--------------------------------------------------------------------------- + +# If the SOURCE_BROWSER tag is set to YES then a list of source files will be +# generated. Documented entities will be cross-referenced with these sources. +# +# Note: To get rid of all source code in the generated output, make sure that +# also VERBATIM_HEADERS is set to NO. +# The default value is: NO. + +SOURCE_BROWSER = YES + +# Setting the INLINE_SOURCES tag to YES will include the body of functions, +# classes and enums directly into the documentation. +# The default value is: NO. + +INLINE_SOURCES = YES + +# Setting the STRIP_CODE_COMMENTS tag to YES will instruct doxygen to hide any +# special comment blocks from generated source code fragments. Normal C, C++ and +# Fortran comments will always remain visible. +# The default value is: YES. + +STRIP_CODE_COMMENTS = YES + +# If the REFERENCED_BY_RELATION tag is set to YES then for each documented +# function all documented functions referencing it will be listed. +# The default value is: NO. + +REFERENCED_BY_RELATION = NO + +# If the REFERENCES_RELATION tag is set to YES then for each documented function +# all documented entities called/used by that function will be listed. +# The default value is: NO. + +REFERENCES_RELATION = NO + +# If the REFERENCES_LINK_SOURCE tag is set to YES and SOURCE_BROWSER tag is set +# to YES then the hyperlinks from functions in REFERENCES_RELATION and +# REFERENCED_BY_RELATION lists will link to the source code. Otherwise they will +# link to the documentation. +# The default value is: YES. + +REFERENCES_LINK_SOURCE = YES + +# If SOURCE_TOOLTIPS is enabled (the default) then hovering a hyperlink in the +# source code will show a tooltip with additional information such as prototype, +# brief description and links to the definition and documentation. Since this +# will make the HTML file larger and loading of large files a bit slower, you +# can opt to disable this feature. +# The default value is: YES. +# This tag requires that the tag SOURCE_BROWSER is set to YES. + +SOURCE_TOOLTIPS = YES + +# If the USE_HTAGS tag is set to YES then the references to source code will +# point to the HTML generated by the htags(1) tool instead of doxygen built-in +# source browser. The htags tool is part of GNU's global source tagging system +# (see http://www.gnu.org/software/global/global.html). You will need version +# 4.8.6 or higher. +# +# To use it do the following: +# - Install the latest version of global +# - Enable SOURCE_BROWSER and USE_HTAGS in the config file +# - Make sure the INPUT points to the root of the source tree +# - Run doxygen as normal +# +# Doxygen will invoke htags (and that will in turn invoke gtags), so these +# tools must be available from the command line (i.e. in the search path). +# +# The result: instead of the source browser generated by doxygen, the links to +# source code will now point to the output of htags. +# The default value is: NO. +# This tag requires that the tag SOURCE_BROWSER is set to YES. + +USE_HTAGS = NO + +# If the VERBATIM_HEADERS tag is set the YES then doxygen will generate a +# verbatim copy of the header file for each class for which an include is +# specified. Set to NO to disable this. +# See also: Section \class. +# The default value is: YES. + +VERBATIM_HEADERS = YES + +# If the CLANG_ASSISTED_PARSING tag is set to YES then doxygen will use the +# clang parser (see: http://clang.llvm.org/) for more accurate parsing at the +# cost of reduced performance. This can be particularly helpful with template +# rich C++ code for which doxygen's built-in parser lacks the necessary type +# information. +# Note: The availability of this option depends on whether or not doxygen was +# generated with the -Duse-libclang=ON option for CMake. +# The default value is: NO. + +CLANG_ASSISTED_PARSING = NO + +# If clang assisted parsing is enabled you can provide the compiler with command +# line options that you would normally use when invoking the compiler. Note that +# the include paths will already be set by doxygen for the files and directories +# specified with INPUT and INCLUDE_PATH. +# This tag requires that the tag CLANG_ASSISTED_PARSING is set to YES. + +CLANG_OPTIONS = + +#--------------------------------------------------------------------------- +# Configuration options related to the alphabetical class index +#--------------------------------------------------------------------------- + +# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index of all +# compounds will be generated. Enable this if the project contains a lot of +# classes, structs, unions or interfaces. +# The default value is: YES. + +ALPHABETICAL_INDEX = YES + +# The COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns in +# which the alphabetical index list will be split. +# Minimum value: 1, maximum value: 20, default value: 5. +# This tag requires that the tag ALPHABETICAL_INDEX is set to YES. + +COLS_IN_ALPHA_INDEX = 5 + +# In case all classes in a project start with a common prefix, all classes will +# be put under the same header in the alphabetical index. The IGNORE_PREFIX tag +# can be used to specify a prefix (or a list of prefixes) that should be ignored +# while generating the index headers. +# This tag requires that the tag ALPHABETICAL_INDEX is set to YES. + +IGNORE_PREFIX = + +#--------------------------------------------------------------------------- +# Configuration options related to the HTML output +#--------------------------------------------------------------------------- + +# If the GENERATE_HTML tag is set to YES, doxygen will generate HTML output +# The default value is: YES. + +GENERATE_HTML = YES + +# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. If a +# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of +# it. +# The default directory is: html. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_OUTPUT = html + +# The HTML_FILE_EXTENSION tag can be used to specify the file extension for each +# generated HTML page (for example: .htm, .php, .asp). +# The default value is: .html. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_FILE_EXTENSION = .html + +# The HTML_HEADER tag can be used to specify a user-defined HTML header file for +# each generated HTML page. If the tag is left blank doxygen will generate a +# standard header. +# +# To get valid HTML the header file that includes any scripts and style sheets +# that doxygen needs, which is dependent on the configuration options used (e.g. +# the setting GENERATE_TREEVIEW). It is highly recommended to start with a +# default header using +# doxygen -w html new_header.html new_footer.html new_stylesheet.css +# YourConfigFile +# and then modify the file new_header.html. See also section "Doxygen usage" +# for information on how to generate the default header that doxygen normally +# uses. +# Note: The header is subject to change so you typically have to regenerate the +# default header when upgrading to a newer version of doxygen. For a description +# of the possible markers and block names see the documentation. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_HEADER = + +# The HTML_FOOTER tag can be used to specify a user-defined HTML footer for each +# generated HTML page. If the tag is left blank doxygen will generate a standard +# footer. See HTML_HEADER for more information on how to generate a default +# footer and what special commands can be used inside the footer. See also +# section "Doxygen usage" for information on how to generate the default footer +# that doxygen normally uses. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_FOOTER = + +# The HTML_STYLESHEET tag can be used to specify a user-defined cascading style +# sheet that is used by each HTML page. It can be used to fine-tune the look of +# the HTML output. If left blank doxygen will generate a default style sheet. +# See also section "Doxygen usage" for information on how to generate the style +# sheet that doxygen normally uses. +# Note: It is recommended to use HTML_EXTRA_STYLESHEET instead of this tag, as +# it is more robust and this tag (HTML_STYLESHEET) will in the future become +# obsolete. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_STYLESHEET = + +# The HTML_EXTRA_STYLESHEET tag can be used to specify additional user-defined +# cascading style sheets that are included after the standard style sheets +# created by doxygen. Using this option one can overrule certain style aspects. +# This is preferred over using HTML_STYLESHEET since it does not replace the +# standard style sheet and is therefore more robust against future updates. +# Doxygen will copy the style sheet files to the output directory. +# Note: The order of the extra style sheet files is of importance (e.g. the last +# style sheet in the list overrules the setting of the previous ones in the +# list). For an example see the documentation. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_EXTRA_STYLESHEET = + +# The HTML_EXTRA_FILES tag can be used to specify one or more extra images or +# other source files which should be copied to the HTML output directory. Note +# that these files will be copied to the base HTML output directory. Use the +# $relpath^ marker in the HTML_HEADER and/or HTML_FOOTER files to load these +# files. In the HTML_STYLESHEET file, use the file name only. Also note that the +# files will be copied as-is; there are no commands or markers available. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_EXTRA_FILES = + +# The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. Doxygen +# will adjust the colors in the style sheet and background images according to +# this color. Hue is specified as an angle on a colorwheel, see +# http://en.wikipedia.org/wiki/Hue for more information. For instance the value +# 0 represents red, 60 is yellow, 120 is green, 180 is cyan, 240 is blue, 300 +# purple, and 360 is red again. +# Minimum value: 0, maximum value: 359, default value: 220. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_COLORSTYLE_HUE = 220 + +# The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of the colors +# in the HTML output. For a value of 0 the output will use grayscales only. A +# value of 255 will produce the most vivid colors. +# Minimum value: 0, maximum value: 255, default value: 100. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_COLORSTYLE_SAT = 100 + +# The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to the +# luminance component of the colors in the HTML output. Values below 100 +# gradually make the output lighter, whereas values above 100 make the output +# darker. The value divided by 100 is the actual gamma applied, so 80 represents +# a gamma of 0.8, The value 220 represents a gamma of 2.2, and 100 does not +# change the gamma. +# Minimum value: 40, maximum value: 240, default value: 80. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_COLORSTYLE_GAMMA = 80 + +# If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML +# page will contain the date and time when the page was generated. Setting this +# to YES can help to show when doxygen was last run and thus if the +# documentation is up to date. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_TIMESTAMP = NO + +# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML +# documentation will contain sections that can be hidden and shown after the +# page has loaded. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_DYNAMIC_SECTIONS = NO + +# With HTML_INDEX_NUM_ENTRIES one can control the preferred number of entries +# shown in the various tree structured indices initially; the user can expand +# and collapse entries dynamically later on. Doxygen will expand the tree to +# such a level that at most the specified number of entries are visible (unless +# a fully collapsed tree already exceeds this amount). So setting the number of +# entries 1 will produce a full collapsed tree by default. 0 is a special value +# representing an infinite number of entries and will result in a full expanded +# tree by default. +# Minimum value: 0, maximum value: 9999, default value: 100. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_INDEX_NUM_ENTRIES = 100 + +# If the GENERATE_DOCSET tag is set to YES, additional index files will be +# generated that can be used as input for Apple's Xcode 3 integrated development +# environment (see: http://developer.apple.com/tools/xcode/), introduced with +# OSX 10.5 (Leopard). To create a documentation set, doxygen will generate a +# Makefile in the HTML output directory. Running make will produce the docset in +# that directory and running make install will install the docset in +# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find it at +# startup. See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html +# for more information. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +GENERATE_DOCSET = NO + +# This tag determines the name of the docset feed. A documentation feed provides +# an umbrella under which multiple documentation sets from a single provider +# (such as a company or product suite) can be grouped. +# The default value is: Doxygen generated docs. +# This tag requires that the tag GENERATE_DOCSET is set to YES. + +DOCSET_FEEDNAME = "Doxygen generated docs" + +# This tag specifies a string that should uniquely identify the documentation +# set bundle. This should be a reverse domain-name style string, e.g. +# com.mycompany.MyDocSet. Doxygen will append .docset to the name. +# The default value is: org.doxygen.Project. +# This tag requires that the tag GENERATE_DOCSET is set to YES. + +DOCSET_BUNDLE_ID = org.doxygen.Project + +# The DOCSET_PUBLISHER_ID tag specifies a string that should uniquely identify +# the documentation publisher. This should be a reverse domain-name style +# string, e.g. com.mycompany.MyDocSet.documentation. +# The default value is: org.doxygen.Publisher. +# This tag requires that the tag GENERATE_DOCSET is set to YES. + +DOCSET_PUBLISHER_ID = org.doxygen.Publisher + +# The DOCSET_PUBLISHER_NAME tag identifies the documentation publisher. +# The default value is: Publisher. +# This tag requires that the tag GENERATE_DOCSET is set to YES. + +DOCSET_PUBLISHER_NAME = Publisher + +# If the GENERATE_HTMLHELP tag is set to YES then doxygen generates three +# additional HTML index files: index.hhp, index.hhc, and index.hhk. The +# index.hhp is a project file that can be read by Microsoft's HTML Help Workshop +# (see: http://www.microsoft.com/en-us/download/details.aspx?id=21138) on +# Windows. +# +# The HTML Help Workshop contains a compiler that can convert all HTML output +# generated by doxygen into a single compiled HTML file (.chm). Compiled HTML +# files are now used as the Windows 98 help format, and will replace the old +# Windows help format (.hlp) on all Windows platforms in the future. Compressed +# HTML files also contain an index, a table of contents, and you can search for +# words in the documentation. The HTML workshop also contains a viewer for +# compressed HTML files. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +GENERATE_HTMLHELP = NO + +# The CHM_FILE tag can be used to specify the file name of the resulting .chm +# file. You can add a path in front of the file if the result should not be +# written to the html output directory. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +CHM_FILE = + +# The HHC_LOCATION tag can be used to specify the location (absolute path +# including file name) of the HTML help compiler (hhc.exe). If non-empty, +# doxygen will try to run the HTML help compiler on the generated index.hhp. +# The file has to be specified with full path. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +HHC_LOCATION = + +# The GENERATE_CHI flag controls if a separate .chi index file is generated +# (YES) or that it should be included in the master .chm file (NO). +# The default value is: NO. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +GENERATE_CHI = NO + +# The CHM_INDEX_ENCODING is used to encode HtmlHelp index (hhk), content (hhc) +# and project file content. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +CHM_INDEX_ENCODING = + +# The BINARY_TOC flag controls whether a binary table of contents is generated +# (YES) or a normal table of contents (NO) in the .chm file. Furthermore it +# enables the Previous and Next buttons. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +BINARY_TOC = NO + +# The TOC_EXPAND flag can be set to YES to add extra items for group members to +# the table of contents of the HTML help documentation and to the tree view. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +TOC_EXPAND = NO + +# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and +# QHP_VIRTUAL_FOLDER are set, an additional index file will be generated that +# can be used as input for Qt's qhelpgenerator to generate a Qt Compressed Help +# (.qch) of the generated HTML documentation. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +GENERATE_QHP = NO + +# If the QHG_LOCATION tag is specified, the QCH_FILE tag can be used to specify +# the file name of the resulting .qch file. The path specified is relative to +# the HTML output folder. +# This tag requires that the tag GENERATE_QHP is set to YES. + +QCH_FILE = + +# The QHP_NAMESPACE tag specifies the namespace to use when generating Qt Help +# Project output. For more information please see Qt Help Project / Namespace +# (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#namespace). +# The default value is: org.doxygen.Project. +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHP_NAMESPACE = org.doxygen.Project + +# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating Qt +# Help Project output. For more information please see Qt Help Project / Virtual +# Folders (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#virtual- +# folders). +# The default value is: doc. +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHP_VIRTUAL_FOLDER = doc + +# If the QHP_CUST_FILTER_NAME tag is set, it specifies the name of a custom +# filter to add. For more information please see Qt Help Project / Custom +# Filters (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#custom- +# filters). +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHP_CUST_FILTER_NAME = + +# The QHP_CUST_FILTER_ATTRS tag specifies the list of the attributes of the +# custom filter to add. For more information please see Qt Help Project / Custom +# Filters (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#custom- +# filters). +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHP_CUST_FILTER_ATTRS = + +# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this +# project's filter section matches. Qt Help Project / Filter Attributes (see: +# http://qt-project.org/doc/qt-4.8/qthelpproject.html#filter-attributes). +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHP_SECT_FILTER_ATTRS = + +# The QHG_LOCATION tag can be used to specify the location of Qt's +# qhelpgenerator. If non-empty doxygen will try to run qhelpgenerator on the +# generated .qhp file. +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHG_LOCATION = + +# If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files will be +# generated, together with the HTML files, they form an Eclipse help plugin. To +# install this plugin and make it available under the help contents menu in +# Eclipse, the contents of the directory containing the HTML and XML files needs +# to be copied into the plugins directory of eclipse. The name of the directory +# within the plugins directory should be the same as the ECLIPSE_DOC_ID value. +# After copying Eclipse needs to be restarted before the help appears. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +GENERATE_ECLIPSEHELP = NO + +# A unique identifier for the Eclipse help plugin. When installing the plugin +# the directory name containing the HTML and XML files should also have this +# name. Each documentation set should have its own identifier. +# The default value is: org.doxygen.Project. +# This tag requires that the tag GENERATE_ECLIPSEHELP is set to YES. + +ECLIPSE_DOC_ID = org.doxygen.Project + +# If you want full control over the layout of the generated HTML pages it might +# be necessary to disable the index and replace it with your own. The +# DISABLE_INDEX tag can be used to turn on/off the condensed index (tabs) at top +# of each HTML page. A value of NO enables the index and the value YES disables +# it. Since the tabs in the index contain the same information as the navigation +# tree, you can set this option to YES if you also set GENERATE_TREEVIEW to YES. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +DISABLE_INDEX = NO + +# The GENERATE_TREEVIEW tag is used to specify whether a tree-like index +# structure should be generated to display hierarchical information. If the tag +# value is set to YES, a side panel will be generated containing a tree-like +# index structure (just like the one that is generated for HTML Help). For this +# to work a browser that supports JavaScript, DHTML, CSS and frames is required +# (i.e. any modern browser). Windows users are probably better off using the +# HTML help feature. Via custom style sheets (see HTML_EXTRA_STYLESHEET) one can +# further fine-tune the look of the index. As an example, the default style +# sheet generated by doxygen has an example that shows how to put an image at +# the root of the tree instead of the PROJECT_NAME. Since the tree basically has +# the same information as the tab index, you could consider setting +# DISABLE_INDEX to YES when enabling this option. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +GENERATE_TREEVIEW = NO + +# The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values that +# doxygen will group on one line in the generated HTML documentation. +# +# Note that a value of 0 will completely suppress the enum values from appearing +# in the overview section. +# Minimum value: 0, maximum value: 20, default value: 4. +# This tag requires that the tag GENERATE_HTML is set to YES. + +ENUM_VALUES_PER_LINE = 4 + +# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be used +# to set the initial width (in pixels) of the frame in which the tree is shown. +# Minimum value: 0, maximum value: 1500, default value: 250. +# This tag requires that the tag GENERATE_HTML is set to YES. + +TREEVIEW_WIDTH = 250 + +# If the EXT_LINKS_IN_WINDOW option is set to YES, doxygen will open links to +# external symbols imported via tag files in a separate window. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +EXT_LINKS_IN_WINDOW = NO + +# Use this tag to change the font size of LaTeX formulas included as images in +# the HTML documentation. When you change the font size after a successful +# doxygen run you need to manually remove any form_*.png images from the HTML +# output directory to force them to be regenerated. +# Minimum value: 8, maximum value: 50, default value: 10. +# This tag requires that the tag GENERATE_HTML is set to YES. + +FORMULA_FONTSIZE = 10 + +# Use the FORMULA_TRANPARENT tag to determine whether or not the images +# generated for formulas are transparent PNGs. Transparent PNGs are not +# supported properly for IE 6.0, but are supported on all modern browsers. +# +# Note that when changing this option you need to delete any form_*.png files in +# the HTML output directory before the changes have effect. +# The default value is: YES. +# This tag requires that the tag GENERATE_HTML is set to YES. + +FORMULA_TRANSPARENT = YES + +# Enable the USE_MATHJAX option to render LaTeX formulas using MathJax (see +# http://www.mathjax.org) which uses client side Javascript for the rendering +# instead of using pre-rendered bitmaps. Use this if you do not have LaTeX +# installed or if you want to formulas look prettier in the HTML output. When +# enabled you may also need to install MathJax separately and configure the path +# to it using the MATHJAX_RELPATH option. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +USE_MATHJAX = NO + +# When MathJax is enabled you can set the default output format to be used for +# the MathJax output. See the MathJax site (see: +# http://docs.mathjax.org/en/latest/output.html) for more details. +# Possible values are: HTML-CSS (which is slower, but has the best +# compatibility), NativeMML (i.e. MathML) and SVG. +# The default value is: HTML-CSS. +# This tag requires that the tag USE_MATHJAX is set to YES. + +MATHJAX_FORMAT = HTML-CSS + +# When MathJax is enabled you need to specify the location relative to the HTML +# output directory using the MATHJAX_RELPATH option. The destination directory +# should contain the MathJax.js script. For instance, if the mathjax directory +# is located at the same level as the HTML output directory, then +# MATHJAX_RELPATH should be ../mathjax. The default value points to the MathJax +# Content Delivery Network so you can quickly see the result without installing +# MathJax. However, it is strongly recommended to install a local copy of +# MathJax from http://www.mathjax.org before deployment. +# The default value is: http://cdn.mathjax.org/mathjax/latest. +# This tag requires that the tag USE_MATHJAX is set to YES. + +MATHJAX_RELPATH = http://cdn.mathjax.org/mathjax/latest + +# The MATHJAX_EXTENSIONS tag can be used to specify one or more MathJax +# extension names that should be enabled during MathJax rendering. For example +# MATHJAX_EXTENSIONS = TeX/AMSmath TeX/AMSsymbols +# This tag requires that the tag USE_MATHJAX is set to YES. + +MATHJAX_EXTENSIONS = + +# The MATHJAX_CODEFILE tag can be used to specify a file with javascript pieces +# of code that will be used on startup of the MathJax code. See the MathJax site +# (see: http://docs.mathjax.org/en/latest/output.html) for more details. For an +# example see the documentation. +# This tag requires that the tag USE_MATHJAX is set to YES. + +MATHJAX_CODEFILE = + +# When the SEARCHENGINE tag is enabled doxygen will generate a search box for +# the HTML output. The underlying search engine uses javascript and DHTML and +# should work on any modern browser. Note that when using HTML help +# (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets (GENERATE_DOCSET) +# there is already a search function so this one should typically be disabled. +# For large projects the javascript based search engine can be slow, then +# enabling SERVER_BASED_SEARCH may provide a better solution. It is possible to +# search using the keyboard; to jump to the search box use + S +# (what the is depends on the OS and browser, but it is typically +# , /