From 3ff5b60b50da5bea01b9a3917b9a5456c78215da Mon Sep 17 00:00:00 2001 From: Ji-hoon Lee Date: Fri, 11 Aug 2023 21:05:33 +0900 Subject: [PATCH] Update MMI Framework 2.0 Change-Id: I332cdda776346435880da0dc0f26859e77506feb --- CODEOWNERS | 2 +- README.md | 75 +- capi/meson.build | 17 + capi/mmi-attribute.h | 76 ++ capi/mmi-data.h | 222 ++++++ src/mmi-ipc.h => capi/mmi-defines.h | 45 +- capi/mmi-error.h | 68 ++ capi/mmi-node-controller.h | 71 ++ capi/mmi-node-logic.h | 166 ++++ capi/mmi-node-processor.h | 160 ++++ capi/mmi-node-source.h | 105 +++ capi/mmi-node.h | 140 ++++ capi/mmi-plugin-module.h | 62 ++ capi/mmi-plugin-storage.h | 124 +++ capi/mmi-port.h | 129 +++ capi/mmi-primitive-value.h | 99 +++ capi/mmi-signal.h | 94 +++ capi/mmi-workflow.h | 194 +++++ capi/mmi.h | 79 ++ external/adishavit/LICENSE.txt | 26 + external/adishavit/argh.h | 434 ++++++++++ external/cli/LICENSE | 23 + external/cli/boostasiocliasyncsession.h | 40 + external/cli/boostasioremotecli.h | 39 + external/cli/boostasioscheduler.h | 38 + external/cli/cli.h | 877 +++++++++++++++++++++ external/cli/clifilesession.h | 85 ++ external/cli/clilocalsession.h | 81 ++ external/cli/colorprofile.h | 76 ++ external/cli/detail/boostasiolib.h | 49 ++ external/cli/detail/commonprefix.h | 70 ++ external/cli/detail/fromstring.h | 282 +++++++ external/cli/detail/genericasioremotecli.h | 590 ++++++++++++++ external/cli/detail/genericasioscheduler.h | 113 +++ external/cli/detail/genericcliasyncsession.h | 100 +++ external/cli/detail/history.h | 165 ++++ external/cli/detail/inputdevice.h | 72 ++ external/cli/detail/inputhandler.h | 133 ++++ external/cli/detail/keyboard.h | 58 ++ external/cli/detail/linuxkeyboard.h | 218 +++++ external/cli/detail/newboostasiolib.h | 89 +++ external/cli/detail/newstandaloneasiolib.h | 89 +++ external/cli/detail/oldboostasiolib.h | 82 ++ external/cli/detail/oldstandaloneasiolib.h | 85 ++ external/cli/detail/rang.h | 303 +++++++ external/cli/detail/server.h | 157 ++++ external/cli/detail/split.h | 239 ++++++ external/cli/detail/standaloneasiolib.h | 49 ++ external/cli/detail/terminal.h | 213 +++++ external/cli/detail/winkeyboard.h | 182 +++++ external/cli/filehistorystorage.h | 86 ++ external/cli/historystorage.h | 54 ++ external/cli/loopscheduler.h | 128 +++ external/cli/scheduler.h | 55 ++ external/cli/standaloneasiocliasyncsession.h | 40 + external/cli/standaloneasioremotecli.h | 40 + external/cli/standaloneasioscheduler.h | 38 + external/cli/volatilehistorystorage.h | 68 ++ meson.build | 10 +- meson_options.txt | 0 packaging/mmi.spec | 69 +- plugins/meson.build | 2 + plugins/nodes/camera/meson.build | 27 + plugins/nodes/camera/mmi-module-camera-preview.cpp | 148 ++++ plugins/nodes/camera/mmi-module-camera-preview.h | 59 ++ plugins/nodes/camera/mmi-module-camera.cpp | 185 +++++ .../face-recognition/data/recognizerInfo.json | 15 + plugins/nodes/face-recognition/meson.build | 37 + .../mmi-module-fr-data-manager.cpp | 234 ++++++ .../face-recognition/mmi-module-fr-data-manager.h | 56 ++ plugins/nodes/face-recognition/mmi-module-fr.cpp | 595 ++++++++++++++ .../mv_face_recognition_internal.h | 85 ++ plugins/nodes/match/meson.build | 27 + .../match/mmi-module-match-text-candidates.cpp | 77 ++ .../nodes/match/mmi-module-match-text-candidates.h | 40 + plugins/nodes/match/mmi-module-match.cpp | 302 +++++++ plugins/nodes/meson.build | 6 + plugins/nodes/mic/meson.build | 31 + plugins/nodes/mic/mmi-module-mic-recorder.cpp | 161 ++++ plugins/nodes/mic/mmi-module-mic-recorder.h | 52 ++ plugins/nodes/mic/mmi-module-mic.cpp | 210 +++++ plugins/nodes/regex-match/meson.build | 27 + .../mmi-module-regex-match-regex-candidates.cpp | 84 ++ .../mmi-module-regex-match-regex-candidates.h | 41 + .../nodes/regex-match/mmi-module-regex-match.cpp | 308 ++++++++ plugins/nodes/voice-touch/meson.build | 36 + .../mmi-module-voice-touch-candidates.cpp | 299 +++++++ .../mmi-module-voice-touch-candidates.h | 52 ++ .../voice-touch/mmi-module-voice-touch-common.h | 60 ++ .../mmi-module-voice-touch-data-utility.cpp | 246 ++++++ .../mmi-module-voice-touch-data-utility.h | 60 ++ .../mmi-module-voice-touch-primitive-utility.cpp | 102 +++ .../mmi-module-voice-touch-primitive-utility.h | 47 ++ .../mmi-module-voice-touch-screen-info.cpp | 343 ++++++++ .../mmi-module-voice-touch-screen-info.h | 73 ++ .../mmi-module-voice-touch-string-utility.cpp | 45 +- .../mmi-module-voice-touch-string-utility.h | 31 + .../nodes/voice-touch/mmi-module-voice-touch.cpp | 617 +++++++++++++++ plugins/workflows/meson.build | 4 + plugins/workflows/script-parser/meson.build | 25 + .../script-parser/mmi-module-script-parser.cpp | 33 + plugins/workflows/user-recognition/meson.build | 1 + .../user-recognition/user-recognition.mws | 21 + plugins/workflows/voice-touch/meson.build | 25 + .../voice-touch/mmi-module-voice-touch.cpp | 150 ++++ plugins/workflows/voice-touch/voice-touch.mws | 19 + plugins/workflows/wakeupless-command/meson.build | 25 + .../mmi-module-wakeupless-command.cpp | 69 ++ src/common/mmi-communication-channel.h | 75 ++ src/common/mmi-communication-message.h | 93 +++ src/common/mmi-event-observer.h | 106 +++ src/common/mmi-plugin-module-event.h | 57 ++ src/common/mmi-workflow-event.h | 42 + src/common/mmi-workflow-output-event.h | 48 ++ src/meson.build | 62 +- src/mmi-cli/cliecoresession.h | 106 +++ src/mmi-cli/meson.build | 56 ++ src/mmi-cli/mmi-cli-node-tester.cpp | 822 +++++++++++++++++++ src/mmi-cli/mmi-cli.cpp | 172 ++++ src/mmi-client.c | 140 ---- src/mmi-ipc.c | 252 ------ src/mmi-manager/main.cpp | 83 ++ src/mmi-manager/meson.build | 78 ++ src/mmi-manager/mmi-client.cpp | 168 ++++ src/mmi-manager/mmi-client.h | 103 +++ src/mmi-manager/mmi-common.cpp | 25 + src/mmi-manager/mmi-common.h | 34 + src/mmi-manager/mmi-data-gateway.cpp | 71 ++ src/mmi-manager/mmi-data-gateway.h | 56 ++ src/mmi-manager/mmi-ipc-tidl.cpp | 340 ++++++++ src/mmi-manager/mmi-ipc-tidl.h | 81 ++ src/mmi-manager/mmi-manager-log.h | 50 ++ src/mmi-manager/mmi-manager.cpp | 136 ++++ src/mmi-manager/mmi-manager.h | 75 ++ src/mmi-manager/mmi-manager.xml | 15 + src/mmi-manager/mmi-node-instance-manager.cpp | 173 ++++ src/mmi-manager/mmi-node-instance-manager.h | 101 +++ src/mmi-manager/mmi-node-instance.cpp | 300 +++++++ src/mmi-manager/mmi-node-instance.h | 91 +++ src/mmi-manager/mmi-node-prototype-manager.cpp | 77 ++ src/mmi-manager/mmi-node-prototype-manager.h | 60 ++ src/mmi-manager/mmi-node-prototype.cpp | 151 ++++ src/mmi-manager/mmi-node-prototype.h | 117 +++ src/mmi-manager/mmi-plugin-module-proxy.cpp | 213 +++++ src/mmi-manager/mmi-plugin-module-proxy.h | 127 +++ src/mmi-manager/mmi-plugin-module-registry.cpp | 324 ++++++++ src/mmi-manager/mmi-plugin-module-registry.h | 72 ++ src/mmi-manager/mmi-port-instance-manager.cpp | 62 ++ src/mmi-manager/mmi-port-instance-manager.h | 70 ++ src/mmi-manager/mmi-port-instance.cpp | 73 ++ src/mmi-manager/mmi-port-instance.h | 64 ++ src/mmi-manager/mmi-self-container.cpp | 370 +++++++++ src/mmi-manager/mmi-self-container.h | 132 ++++ src/mmi-manager/mmi-workflow-instance-manager.cpp | 261 ++++++ src/mmi-manager/mmi-workflow-instance-manager.h | 88 +++ src/mmi-manager/mmi-workflow-instance.cpp | 358 +++++++++ src/mmi-manager/mmi-workflow-instance.h | 100 +++ src/mmi-manager/mmi-workflow-prototype-manager.cpp | 76 ++ src/mmi-manager/mmi-workflow-prototype-manager.h | 58 ++ src/mmi-manager/mmi-workflow-prototype.cpp | 107 +++ src/mmi-manager/mmi-workflow-prototype.h | 108 +++ src/mmi.c | 135 ---- src/mmi.h | 67 -- src/mmi/meson.build | 69 ++ src/mmi/mmi-attribute.cpp | 301 +++++++ src/mmi/mmi-client-manager.h | 99 +++ src/mmi/mmi-data.cpp | 863 ++++++++++++++++++++ src/mmi/mmi-ipc-tidl.cpp | 418 ++++++++++ src/mmi/mmi-ipc-tidl.h | 67 ++ src/{mmi-dbg.h => mmi/mmi-log.h} | 6 +- src/mmi/mmi-node-logic.cpp | 51 ++ src/mmi/mmi-node-processor.cpp | 51 ++ src/mmi/mmi-node-source.cpp | 44 ++ src/mmi/mmi-node.cpp | 307 ++++++++ src/mmi/mmi-plugin-storage-impl.h | 123 +++ src/mmi/mmi-plugin-storage.cpp | 85 ++ src/mmi/mmi-port.cpp | 205 +++++ src/mmi/mmi-primitive-value.cpp | 549 +++++++++++++ src/mmi/mmi-signal.cpp | 147 ++++ src/mmi/mmi-workflow-instance-manager.h | 119 +++ src/mmi/mmi-workflow-instance.cpp | 186 +++++ src/mmi/mmi-workflow-script-parser.h | 482 +++++++++++ src/mmi/mmi-workflow-script.cpp | 55 ++ src/mmi/mmi-workflow.cpp | 351 +++++++++ src/mmi/mmi.cpp | 112 +++ tests/meson.build | 19 +- tests/mmi-ipc-test.cpp | 266 ------- tests/mmi-main-test.cpp | 328 -------- tests/mmi-manager/ecore-event-dispatcher.cpp | 20 + tests/mmi-manager/meson.build | 34 + tests/mmi-manager/mmi-client-tests.cpp | 148 ++++ tests/mmi-manager/mmi-manager-main-tests.cpp | 82 ++ tests/mmi-manager/mmi-manager-tests.cpp | 52 ++ tests/mmi-manager/mmi-manager-tests.h | 43 + .../mmi-node-instance-manager-tests.cpp | 101 +++ .../mmi-node-prototype-manager-tests.cpp | 85 ++ .../node-prototype/mmi-node-prototype-tests.cpp | 147 ++++ .../mmi-plugin-module-registry-tests.cpp | 279 +++++++ .../mmi-port-instance-manager-tests.cpp | 53 ++ .../mmi-workflow-instance-manager-tests.cpp | 451 +++++++++++ .../mmi-workflow-instance-tests.cpp | 184 +++++ .../mmi-workflow-prototype-tests.cpp | 68 ++ tests/mmi/attribute/mmi-attribute-test.cpp | 268 +++++++ tests/mmi/meson.build | 22 + tests/mmi/mmi-data-test.cpp | 524 ++++++++++++ tests/mmi/mmi-ipc-test.cpp | 129 +++ tests/mmi/mmi-main-test.cpp | 56 ++ tests/{ => mmi}/mmi-tests.cpp | 43 +- tests/{ => mmi}/mmi-tests.h | 1 - .../primitive-value/mmi-primitive-value-test.cpp | 629 +++++++++++++++ tests/mmi/workflow/mmi-workflow-test.cpp | 299 +++++++ tests/wait-helper.cpp | 15 - tidl/mmi.tidl | 12 +- 213 files changed, 27703 insertions(+), 1391 deletions(-) create mode 100644 capi/meson.build create mode 100644 capi/mmi-attribute.h create mode 100644 capi/mmi-data.h rename src/mmi-ipc.h => capi/mmi-defines.h (52%) create mode 100644 capi/mmi-error.h create mode 100644 capi/mmi-node-controller.h create mode 100644 capi/mmi-node-logic.h create mode 100644 capi/mmi-node-processor.h create mode 100644 capi/mmi-node-source.h create mode 100644 capi/mmi-node.h create mode 100644 capi/mmi-plugin-module.h create mode 100644 capi/mmi-plugin-storage.h create mode 100644 capi/mmi-port.h create mode 100644 capi/mmi-primitive-value.h create mode 100644 capi/mmi-signal.h create mode 100644 capi/mmi-workflow.h create mode 100644 capi/mmi.h create mode 100644 external/adishavit/LICENSE.txt create mode 100644 external/adishavit/argh.h create mode 100644 external/cli/LICENSE create mode 100644 external/cli/boostasiocliasyncsession.h create mode 100644 external/cli/boostasioremotecli.h create mode 100644 external/cli/boostasioscheduler.h create mode 100644 external/cli/cli.h create mode 100644 external/cli/clifilesession.h create mode 100644 external/cli/clilocalsession.h create mode 100644 external/cli/colorprofile.h create mode 100644 external/cli/detail/boostasiolib.h create mode 100644 external/cli/detail/commonprefix.h create mode 100644 external/cli/detail/fromstring.h create mode 100644 external/cli/detail/genericasioremotecli.h create mode 100644 external/cli/detail/genericasioscheduler.h create mode 100644 external/cli/detail/genericcliasyncsession.h create mode 100644 external/cli/detail/history.h create mode 100644 external/cli/detail/inputdevice.h create mode 100644 external/cli/detail/inputhandler.h create mode 100644 external/cli/detail/keyboard.h create mode 100644 external/cli/detail/linuxkeyboard.h create mode 100644 external/cli/detail/newboostasiolib.h create mode 100644 external/cli/detail/newstandaloneasiolib.h create mode 100644 external/cli/detail/oldboostasiolib.h create mode 100644 external/cli/detail/oldstandaloneasiolib.h create mode 100644 external/cli/detail/rang.h create mode 100644 external/cli/detail/server.h create mode 100644 external/cli/detail/split.h create mode 100644 external/cli/detail/standaloneasiolib.h create mode 100644 external/cli/detail/terminal.h create mode 100644 external/cli/detail/winkeyboard.h create mode 100644 external/cli/filehistorystorage.h create mode 100644 external/cli/historystorage.h create mode 100644 external/cli/loopscheduler.h create mode 100644 external/cli/scheduler.h create mode 100644 external/cli/standaloneasiocliasyncsession.h create mode 100644 external/cli/standaloneasioremotecli.h create mode 100644 external/cli/standaloneasioscheduler.h create mode 100644 external/cli/volatilehistorystorage.h delete mode 100644 meson_options.txt create mode 100644 plugins/meson.build create mode 100644 plugins/nodes/camera/meson.build create mode 100644 plugins/nodes/camera/mmi-module-camera-preview.cpp create mode 100644 plugins/nodes/camera/mmi-module-camera-preview.h create mode 100644 plugins/nodes/camera/mmi-module-camera.cpp create mode 100644 plugins/nodes/face-recognition/data/recognizerInfo.json create mode 100644 plugins/nodes/face-recognition/meson.build create mode 100644 plugins/nodes/face-recognition/mmi-module-fr-data-manager.cpp create mode 100644 plugins/nodes/face-recognition/mmi-module-fr-data-manager.h create mode 100644 plugins/nodes/face-recognition/mmi-module-fr.cpp create mode 100644 plugins/nodes/face-recognition/mv_face_recognition_internal.h create mode 100644 plugins/nodes/match/meson.build create mode 100644 plugins/nodes/match/mmi-module-match-text-candidates.cpp create mode 100644 plugins/nodes/match/mmi-module-match-text-candidates.h create mode 100644 plugins/nodes/match/mmi-module-match.cpp create mode 100755 plugins/nodes/meson.build create mode 100644 plugins/nodes/mic/meson.build create mode 100644 plugins/nodes/mic/mmi-module-mic-recorder.cpp create mode 100644 plugins/nodes/mic/mmi-module-mic-recorder.h create mode 100644 plugins/nodes/mic/mmi-module-mic.cpp create mode 100644 plugins/nodes/regex-match/meson.build create mode 100644 plugins/nodes/regex-match/mmi-module-regex-match-regex-candidates.cpp create mode 100644 plugins/nodes/regex-match/mmi-module-regex-match-regex-candidates.h create mode 100644 plugins/nodes/regex-match/mmi-module-regex-match.cpp create mode 100644 plugins/nodes/voice-touch/meson.build create mode 100644 plugins/nodes/voice-touch/mmi-module-voice-touch-candidates.cpp create mode 100644 plugins/nodes/voice-touch/mmi-module-voice-touch-candidates.h create mode 100644 plugins/nodes/voice-touch/mmi-module-voice-touch-common.h create mode 100644 plugins/nodes/voice-touch/mmi-module-voice-touch-data-utility.cpp create mode 100644 plugins/nodes/voice-touch/mmi-module-voice-touch-data-utility.h create mode 100644 plugins/nodes/voice-touch/mmi-module-voice-touch-primitive-utility.cpp create mode 100644 plugins/nodes/voice-touch/mmi-module-voice-touch-primitive-utility.h create mode 100644 plugins/nodes/voice-touch/mmi-module-voice-touch-screen-info.cpp create mode 100644 plugins/nodes/voice-touch/mmi-module-voice-touch-screen-info.h rename src/mmi-client.h => plugins/nodes/voice-touch/mmi-module-voice-touch-string-utility.cpp (51%) create mode 100644 plugins/nodes/voice-touch/mmi-module-voice-touch-string-utility.h create mode 100644 plugins/nodes/voice-touch/mmi-module-voice-touch.cpp create mode 100644 plugins/workflows/meson.build create mode 100644 plugins/workflows/script-parser/meson.build create mode 100644 plugins/workflows/script-parser/mmi-module-script-parser.cpp create mode 100644 plugins/workflows/user-recognition/meson.build create mode 100644 plugins/workflows/user-recognition/user-recognition.mws create mode 100644 plugins/workflows/voice-touch/meson.build create mode 100644 plugins/workflows/voice-touch/mmi-module-voice-touch.cpp create mode 100644 plugins/workflows/voice-touch/voice-touch.mws create mode 100644 plugins/workflows/wakeupless-command/meson.build create mode 100644 plugins/workflows/wakeupless-command/mmi-module-wakeupless-command.cpp create mode 100644 src/common/mmi-communication-channel.h create mode 100644 src/common/mmi-communication-message.h create mode 100644 src/common/mmi-event-observer.h create mode 100644 src/common/mmi-plugin-module-event.h create mode 100644 src/common/mmi-workflow-event.h create mode 100644 src/common/mmi-workflow-output-event.h create mode 100644 src/mmi-cli/cliecoresession.h create mode 100644 src/mmi-cli/meson.build create mode 100644 src/mmi-cli/mmi-cli-node-tester.cpp create mode 100644 src/mmi-cli/mmi-cli.cpp delete mode 100644 src/mmi-client.c delete mode 100644 src/mmi-ipc.c create mode 100644 src/mmi-manager/main.cpp create mode 100644 src/mmi-manager/meson.build create mode 100644 src/mmi-manager/mmi-client.cpp create mode 100644 src/mmi-manager/mmi-client.h create mode 100644 src/mmi-manager/mmi-common.cpp create mode 100644 src/mmi-manager/mmi-common.h create mode 100644 src/mmi-manager/mmi-data-gateway.cpp create mode 100644 src/mmi-manager/mmi-data-gateway.h create mode 100644 src/mmi-manager/mmi-ipc-tidl.cpp create mode 100644 src/mmi-manager/mmi-ipc-tidl.h create mode 100644 src/mmi-manager/mmi-manager-log.h create mode 100644 src/mmi-manager/mmi-manager.cpp create mode 100644 src/mmi-manager/mmi-manager.h create mode 100644 src/mmi-manager/mmi-manager.xml create mode 100644 src/mmi-manager/mmi-node-instance-manager.cpp create mode 100644 src/mmi-manager/mmi-node-instance-manager.h create mode 100644 src/mmi-manager/mmi-node-instance.cpp create mode 100644 src/mmi-manager/mmi-node-instance.h create mode 100644 src/mmi-manager/mmi-node-prototype-manager.cpp create mode 100644 src/mmi-manager/mmi-node-prototype-manager.h create mode 100644 src/mmi-manager/mmi-node-prototype.cpp create mode 100644 src/mmi-manager/mmi-node-prototype.h create mode 100644 src/mmi-manager/mmi-plugin-module-proxy.cpp create mode 100644 src/mmi-manager/mmi-plugin-module-proxy.h create mode 100644 src/mmi-manager/mmi-plugin-module-registry.cpp create mode 100644 src/mmi-manager/mmi-plugin-module-registry.h create mode 100644 src/mmi-manager/mmi-port-instance-manager.cpp create mode 100644 src/mmi-manager/mmi-port-instance-manager.h create mode 100644 src/mmi-manager/mmi-port-instance.cpp create mode 100644 src/mmi-manager/mmi-port-instance.h create mode 100644 src/mmi-manager/mmi-self-container.cpp create mode 100644 src/mmi-manager/mmi-self-container.h create mode 100644 src/mmi-manager/mmi-workflow-instance-manager.cpp create mode 100644 src/mmi-manager/mmi-workflow-instance-manager.h create mode 100644 src/mmi-manager/mmi-workflow-instance.cpp create mode 100644 src/mmi-manager/mmi-workflow-instance.h create mode 100644 src/mmi-manager/mmi-workflow-prototype-manager.cpp create mode 100644 src/mmi-manager/mmi-workflow-prototype-manager.h create mode 100644 src/mmi-manager/mmi-workflow-prototype.cpp create mode 100644 src/mmi-manager/mmi-workflow-prototype.h delete mode 100644 src/mmi.c delete mode 100644 src/mmi.h create mode 100644 src/mmi/meson.build create mode 100644 src/mmi/mmi-attribute.cpp create mode 100644 src/mmi/mmi-client-manager.h create mode 100644 src/mmi/mmi-data.cpp create mode 100644 src/mmi/mmi-ipc-tidl.cpp create mode 100644 src/mmi/mmi-ipc-tidl.h rename src/{mmi-dbg.h => mmi/mmi-log.h} (92%) create mode 100644 src/mmi/mmi-node-logic.cpp create mode 100644 src/mmi/mmi-node-processor.cpp create mode 100644 src/mmi/mmi-node-source.cpp create mode 100644 src/mmi/mmi-node.cpp create mode 100644 src/mmi/mmi-plugin-storage-impl.h create mode 100644 src/mmi/mmi-plugin-storage.cpp create mode 100644 src/mmi/mmi-port.cpp create mode 100644 src/mmi/mmi-primitive-value.cpp create mode 100644 src/mmi/mmi-signal.cpp create mode 100644 src/mmi/mmi-workflow-instance-manager.h create mode 100644 src/mmi/mmi-workflow-instance.cpp create mode 100644 src/mmi/mmi-workflow-script-parser.h create mode 100644 src/mmi/mmi-workflow-script.cpp create mode 100644 src/mmi/mmi-workflow.cpp create mode 100644 src/mmi/mmi.cpp delete mode 100644 tests/mmi-ipc-test.cpp delete mode 100644 tests/mmi-main-test.cpp create mode 100644 tests/mmi-manager/ecore-event-dispatcher.cpp create mode 100644 tests/mmi-manager/meson.build create mode 100644 tests/mmi-manager/mmi-client-tests.cpp create mode 100644 tests/mmi-manager/mmi-manager-main-tests.cpp create mode 100644 tests/mmi-manager/mmi-manager-tests.cpp create mode 100644 tests/mmi-manager/mmi-manager-tests.h create mode 100644 tests/mmi-manager/node-instance-manager/mmi-node-instance-manager-tests.cpp create mode 100644 tests/mmi-manager/node-prototype-manager/mmi-node-prototype-manager-tests.cpp create mode 100644 tests/mmi-manager/node-prototype/mmi-node-prototype-tests.cpp create mode 100644 tests/mmi-manager/plugin-module-registry/mmi-plugin-module-registry-tests.cpp create mode 100644 tests/mmi-manager/port-instance-manager/mmi-port-instance-manager-tests.cpp create mode 100644 tests/mmi-manager/workflow-instance-manager/mmi-workflow-instance-manager-tests.cpp create mode 100644 tests/mmi-manager/workflow-instance/mmi-workflow-instance-tests.cpp create mode 100644 tests/mmi-manager/workflow-prototype/mmi-workflow-prototype-tests.cpp create mode 100644 tests/mmi/attribute/mmi-attribute-test.cpp create mode 100644 tests/mmi/meson.build create mode 100644 tests/mmi/mmi-data-test.cpp create mode 100644 tests/mmi/mmi-ipc-test.cpp create mode 100644 tests/mmi/mmi-main-test.cpp rename tests/{ => mmi}/mmi-tests.cpp (53%) rename tests/{ => mmi}/mmi-tests.h (96%) create mode 100644 tests/mmi/primitive-value/mmi-primitive-value-test.cpp create mode 100644 tests/mmi/workflow/mmi-workflow-test.cpp delete mode 100644 tests/wait-helper.cpp diff --git a/CODEOWNERS b/CODEOWNERS index e20cb34..9df4ed0 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -2,4 +2,4 @@ # the repo. Unless a later match takes precedence, # @global-owner1 and @global-owner2 will be requested for # review when someone opens a pull request. -* @TizenWS/mmi-iu-and-voice +* @Tizen-Interaction/MMI_FW diff --git a/README.md b/README.md index 4579c8a..fa599b0 100644 --- a/README.md +++ b/README.md @@ -1,26 +1,61 @@ -# MMI(Multi-modal Interaction) Framework Library - -[Source Tree] +# MMI(Multi-modal Interaction) Framework Library + +``` +[Source Tree] . +├── capi +│   ├── meson.build +│   ├── mmi-attribute.h +│   ├── mmi-data.h +│   ├── mmi-error.h +│   ├── mmi.h +│   ├── mmi-node-gate.h +│   ├── mmi-node.h +│   ├── mmi-node-processor.h +│   ├── mmi-node-source.h +│   ├── mmi-port.h +│   ├── mmi-primitive-value.h +│   ├── mmi-signal.h +│   └── mmi-workflow.h ├── CODEOWNERS ├── COPYING ├── meson.build ├── meson_options.txt ├── packaging -│   ├── mmi.manifest -│   └── mmi.spec -├── README.md -├── src -│   ├── interface -│   │   ├── mmi_proxy.c -│   │   └── mmi_proxy.h -│   ├── meson.build -│   ├── mmi.c -│   └── mmi.h -├── tests -│   ├── meson.build -│   ├── mmi-tests.cpp -│   └── mmi-tests.h -└── tidl - └── mmi.tidl - +│   ├── mmi.manifest +│   └── mmi.spec +├── README.md +├── src +│   ├── common +│   │   ├── mmi-communication-channel.h +│   │   ├── mmi-communication-message.h +│   │   └── mmi-event-observer.h +│   ├── meson.build +│   ├── mmi-attribute.cpp +│   ├── mmi-client-manager.h +│   ├── mmi.cpp +│   ├── mmi-data.cpp +│   ├── mmi-ipc-tidl.cpp +│   ├── mmi-ipc-tidl.h +│   ├── mmi-log.h +│   ├── mmi-node.cpp +│   ├── mmi-node-gate.cpp +│   ├── mmi-node-processor.cpp +│   ├── mmi-node-source.cpp +│   ├── mmi-port.cpp +│   ├── mmi-primitive-value.cpp +│   ├── mmi-signal.cpp +│   ├── mmi-workflow.cpp +│   └── mmi-workflow-instance-manager.h +├── tests +│   ├── meson.build +│   ├── mmi-attribute-test.cpp +│   ├── mmi-data-test.cpp +│   ├── mmi-ipc-test.cpp +│   ├── mmi-main-test.cpp +│   ├── mmi-primitive-value-test.cpp +│   ├── mmi-tests.cpp +│   └── mmi-tests.h +└── tidl + └── mmi.tidl +``` diff --git a/capi/meson.build b/capi/meson.build new file mode 100644 index 0000000..a3a3de2 --- /dev/null +++ b/capi/meson.build @@ -0,0 +1,17 @@ +install_headers( + 'mmi-primitive-value.h', + 'mmi-data.h', + 'mmi-attribute.h', + 'mmi-error.h', + 'mmi-workflow.h', + 'mmi-node.h', + 'mmi-node-source.h', + 'mmi-node-processor.h', + 'mmi-node-logic.h', + 'mmi-port.h', + 'mmi-signal.h', + 'mmi-defines.h', + 'mmi-plugin-module.h', + 'mmi-plugin-storage.h', + 'mmi.h', + ) diff --git a/capi/mmi-attribute.h b/capi/mmi-attribute.h new file mode 100644 index 0000000..f09d947 --- /dev/null +++ b/capi/mmi-attribute.h @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2023 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 __TIZEN_UIX_MMI_ATTRIBUTE_H__ +#define __TIZEN_UIX_MMI_ATTRIBUTE_H__ + + +#include +#include +#include + + +/** +* @file mmi-attribute.h +*/ + + +/** +* @addtogroup CAPI_UIX_MMI_MODULE +* @{ +*/ + + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct mmi_attribute_s* mmi_attribute_h; + + +int mmi_attribute_create(mmi_primitive_value_h value, const char *name, mmi_attribute_h *attribute); + +int mmi_attribute_set_name(mmi_attribute_h attribute, const char *name); + +int mmi_attribute_get_name(mmi_attribute_h attribute, char **name); + +int mmi_attribute_get_value(mmi_attribute_h attribute, mmi_primitive_value_h *value); + +int mmi_attribute_clone(mmi_attribute_h attribute, mmi_attribute_h *cloned); + +int mmi_attribute_destroy(mmi_attribute_h attribute); + +int mmi_attribute_to_bytes(mmi_attribute_h attribute, unsigned char **bytes, size_t *length); + +int mmi_attribute_from_bytes(const unsigned char *bytes, size_t length, mmi_attribute_h *attribute); + +/* Utility functions */ +int mmi_attribute_create_string_array(const char *name, const char *strings[], size_t count, mmi_attribute_h *attribute); + + +#ifdef __cplusplus +} +#endif + + +/** + * @} + */ + + +#endif /* __TIZEN_UIX_MMI_ATTRIBUTE_H__ */ diff --git a/capi/mmi-data.h b/capi/mmi-data.h new file mode 100644 index 0000000..e1a8b5a --- /dev/null +++ b/capi/mmi-data.h @@ -0,0 +1,222 @@ +/* + * Copyright (c) 2023 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 __TIZEN_UIX_MMI_DATA_H__ +#define __TIZEN_UIX_MMI_DATA_H__ + + +#include +#include + + +/** +* @file mmi-data.h +*/ + + +/** +* @addtogroup CAPI_UIX_MMI_MODULE +* @{ +*/ + + +#ifdef __cplusplus +extern "C" { +#endif + + +typedef enum { + MMI_DATA_TYPE_BOOLEAN, + MMI_DATA_TYPE_INTEGER, + MMI_DATA_TYPE_FLOAT, + MMI_DATA_TYPE_TEXT, + MMI_DATA_TYPE_JSON, + MMI_DATA_TYPE_USER_IDENTIFICATION, + MMI_DATA_TYPE_AUDIO, + MMI_DATA_TYPE_VIDEO, + MMI_DATA_TYPE_BOUNDING_BOX, + MMI_DATA_TYPE_ARRAY, + MMI_DATA_TYPE_STRUCT, + MMI_DATA_TYPE_ANY, +} mmi_data_type_e; + +/* Data Format */ +/* +- MMI_DATA_BOOLEAN + data : pointer to a byte + datalen : 1 byte + description : false if all bits are 0, true otherwise + +- MMI_DATA_INTEGER + data : pointer to a signed integer type + datalen : 4 byte + description : cast 'data' to a 32-bit signed integer (e.g. int) + +- MMI_DATA_FLOAT + data : pointer to a floating-point type + datalen : 4 byte + description : cast 'data' to a 32-bit floating-point type (e.g. float) + +- MMI_DATA_TEXT + data : pointer to a null-terminated string + datalen : length of the text + description : cast 'data' to a char* + +- MMI_DATA_JSON + data : pointer to a null-terminated string + datalen : length of the json string + description : cast 'data' to a char* + +- MMI_DATA_USER_IDENTIFICATION + data : pointer to a platform-dependent id for a user + datalen : platform-dependent + description : this value can be used to acquire platform-dependent user information + +- MMI_DATA_AUDIO + data : pointer to bytes + datalen : size of 'data' in bytes + format: + { + "bitrate" : 48000, + "format" : { + "signed" : true, + "bits" : 16 + }, + "channels" : 1 + } + description : audio frame data + +- MMI_DATA_VIDEO + data : pointer to bytes + datalen : size of 'data' in bytes + format: + { + "width":1920, + "height":1080, + "encoding":"mp4" + } + description : video frame data + +- MMI_DATA_BOUNDING_BOX + data : pointer to json string + datalen : length of the json string + description : data is composed in below json format + { + "x":300, + "y":150, + "w":200, + "h":200 + } + +- MMI_DATA_ARRAY + data : pointer to an array of mmi_data_h struct + datalen : size of a pointer * number of items + description : A container that can hold multiple mmi_data_h with the same type. + +- MMI_DATA_STRUCT + data : pointer to an array of mmi_data_h struct + datalen : size of a pointer * number of items * 2 (one for key, one for value) + description : A container that can hold multiple mmi_data_h with different types. + A key is a MMI_DATA_TEXT that identifies the value. +- MMI_DATA_ANY + data : pointer to an mmi_data_h struct + datalen : size of a pointer + description : Represents a data type that can be any of the above types. +*/ + + + + +typedef struct mmi_data_timestamp_s *mmi_data_timestamp_h; + +typedef struct mmi_data_s *mmi_data_h; + +int mmi_data_create_bool(bool value, mmi_data_h *data); + +int mmi_data_create_int(int value, mmi_data_h *data); + +int mmi_data_create_float(float value, mmi_data_h *data); + +int mmi_data_create_text(const char *value, mmi_data_h *data); + +int mmi_data_create_audio(const void *ptr, size_t len, mmi_data_h *data); + +int mmi_data_create_video(const void *ptr, size_t len, mmi_data_h *data); + +int mmi_data_create_user_identification(const void *ptr, size_t len, mmi_data_h *data); + +int mmi_data_create_array(mmi_data_h *array_handle); + +int mmi_data_add_array_element(mmi_data_h array_handle, mmi_data_h element); + +int mmi_data_create_struct(mmi_data_h *struct_handle); + +int mmi_data_set_struct_element(mmi_data_h struct_handle, const char *name, mmi_data_h element); + +int mmi_data_get_type(mmi_data_h data, mmi_data_type_e *type); + +int mmi_data_get_bool(mmi_data_h data, bool *value); + +int mmi_data_get_int(mmi_data_h data, int *value); + +int mmi_data_get_float(mmi_data_h data, float *value); + +int mmi_data_get_text(mmi_data_h data, const char **string); + +int mmi_data_get_audio(mmi_data_h data, const void **ptr, size_t *len); + +int mmi_data_get_video(mmi_data_h data, const void **ptr, size_t *len); + +int mmi_data_get_user_identification(mmi_data_h data, const void **ptr, size_t *len); + +int mmi_data_get_array_count(mmi_data_h array, size_t *count); + +int mmi_data_get_array_element(mmi_data_h array, size_t index, mmi_data_h *element); + +int mmi_data_get_struct_element(mmi_data_h struct_handle, const char *name, mmi_data_h *element); + +int mmi_data_get_struct_count(mmi_data_h struct_handle, size_t *count); + +int mmi_data_get_struct_element_name(mmi_data_h struct_handle, size_t index, const char **string); + +int mmi_data_get_struct_element_value(mmi_data_h struct_handle, size_t index, mmi_data_h *element); + +int mmi_data_get_node_timestamp(mmi_data_h data, mmi_data_timestamp_h *timestamp); + +int mmi_data_get_source_timestamp(mmi_data_h data, mmi_data_timestamp_h *timestamp); + +int mmi_data_set_source_timestamp(mmi_data_h data, mmi_data_timestamp_h timestamp); + +int mmi_data_to_bytes(mmi_data_h data, unsigned char **bytes, size_t *length); + +int mmi_data_from_bytes(unsigned char *bytes, size_t length, mmi_data_h *data); + +int mmi_data_destroy(mmi_data_h data); + + +#ifdef __cplusplus +} +#endif + + +/** + * @} + */ + + +#endif /* __TIZEN_UIX_MMI_DATA_H__ */ diff --git a/src/mmi-ipc.h b/capi/mmi-defines.h similarity index 52% rename from src/mmi-ipc.h rename to capi/mmi-defines.h index 8c4367f..e8d26ad 100644 --- a/src/mmi-ipc.h +++ b/capi/mmi-defines.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022 Samsung Electronics Co., Ltd All Rights Reserved + * Copyright (c) 2023 Samsung Electronics Co., Ltd All Rights Reserved * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,29 +15,44 @@ * */ -#ifndef __MMI_IPC_H__ -#define __MMI_IPC_H__ -#include "mmi_proxy.h" -#include "mmi.h" +#ifndef __TIZEN_UIX_MMI_DEFINES_H__ +#define __TIZEN_UIX_MMI_DEFINES_H__ + + +/** +* @file mmi-defines.h +*/ + + +/** +* @addtogroup CAPI_UIX_MMI_MODULE +* @{ +*/ + #ifdef __cplusplus extern "C" { #endif -int mmi_ipc_initialize(void); -void mmi_ipc_deinitialize(void); -int mmi_ipc_register_input_event_result_cb(int input_event_type, rpc_port_proxy_mmi_result_cb_cb callback); -int mmi_ipc_activate_input_event(int input_event_type); -int mmi_ipc_deactivate_input_event(int input_event_type); -rpc_port_proxy_mmi_h mmi_ipc_get_rpc_h(void); -int mmi_ipc_get_uid(void); -bool mmi_ipc_is_connected(void); -const char *mmi_ipc_get_stub_appid(void); +#define MMI_NAME_MAX_LENGTH 32 +#define MMI_PARAMETER_MAX_COUNT 32 +#define MMI_PORT_MAX_COUNT 32 +#define MMI_NODE_MAX_COUNT 128 +#define MMI_LINK_MAX_COUNT 128 +#define MMI_OUTPUT_MAX_COUNT 32 +#define MMI_WORKFLOW_MAX_COUNT 64 + #ifdef __cplusplus } #endif -#endif //__MMI_IPC_H__ + +/** + * @} + */ + + +#endif /* __TIZEN_UIX_MMI_DEFINES_H__ */ diff --git a/capi/mmi-error.h b/capi/mmi-error.h new file mode 100644 index 0000000..edbf780 --- /dev/null +++ b/capi/mmi-error.h @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2023 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 __TIZEN_UIX_MMI_ERROR_H__ +#define __TIZEN_UIX_MMI_ERROR_H__ + + +#include + + +/** +* @file mmi-error.h +*/ + + +/** +* @addtogroup CAPI_UIX_MMI_MODULE +* @{ +*/ + + +#ifdef __cplusplus +extern "C" { +#endif + + +#define TIZEN_ERROR_MMI -0x030F0000 + +typedef enum { + MMI_ERROR_NONE = TIZEN_ERROR_NONE, /**< Successful */ + MMI_ERROR_OUT_OF_MEMORY = TIZEN_ERROR_OUT_OF_MEMORY, /**< Out of Memory */ + MMI_ERROR_IO_ERROR = TIZEN_ERROR_IO_ERROR, /**< I/O error */ + MMI_ERROR_INVALID_PARAMETER = TIZEN_ERROR_INVALID_PARAMETER, /**< Invalid parameter */ + MMI_ERROR_OUT_OF_NETWORK = TIZEN_ERROR_NETWORK_DOWN, /**< Network is down */ + MMI_ERROR_TIMED_OUT = TIZEN_ERROR_TIMED_OUT, /**< No answer from the daemon */ + MMI_ERROR_PERMISSION_DENIED = TIZEN_ERROR_PERMISSION_DENIED, /**< Permission denied */ + MMI_ERROR_NOT_SUPPORTED = TIZEN_ERROR_NOT_SUPPORTED, /**< MMI NOT supported */ + MMI_ERROR_OPERATION_FAILED = TIZEN_ERROR_MMI | 0x01, /**< Operation failed */ + MMI_ERROR_RESOURCE_BUSY = TIZEN_ERROR_MMI | 0x02, /**< Resource busy */ +} mmi_error_e; + + +#ifdef __cplusplus +} +#endif + + +/** + * @} + */ + + +#endif /* __TIZEN_UIX_MMI_ERROR_H__ */ diff --git a/capi/mmi-node-controller.h b/capi/mmi-node-controller.h new file mode 100644 index 0000000..b92a4a1 --- /dev/null +++ b/capi/mmi-node-controller.h @@ -0,0 +1,71 @@ +/* + * Copyright (c) 2023 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 __TIZEN_UIX_MMI_NODE_CONTROLLER_H__ +#define __TIZEN_UIX_MMI_NODE_CONTROLLER_H__ + + + +#include + +/** +* @file mmi-node-controller.h +*/ + + +/** +* @addtogroup CAPI_UIX_MMI_MODULE +* @{ +*/ + + +#ifdef __cplusplus +extern "C" { +#endif + + +enum mmi_standard_node_controller_type_e { + MMI_STANDARD_NODE_CONTROLLER_TYPE_NONE, + + /* - Description : Turns the node with a name specified in the attribute "TARGET" on or off + - Attributes : + "TARGET" : string + - Input Port 1 + Name : "VALUE" + Data Type : MMI_DATA_TYPE_BOOLEAN + */ + MMI_STANDARD_NODE_CONTROLLER_TYPE_SWITCH, +}; + +int mmi_standard_node_create_controller(mmi_standard_node_controller_type_e type, mmi_node_h *node); + +int mmi_standard_node_get_controller_type(mmi_node_h node, mmi_standard_node_controller_type_e *type); + +int mmi_standard_node_register_controller(mmi_standard_node_controller_type_e type, mmi_node_callbacks *callbacks, mmi_node_h node); + +#ifdef __cplusplus +} +#endif + + +/** + * @} + */ + + +#endif /* __TIZEN_UIX_MMI_NODE_LOGIC_H__ */ diff --git a/capi/mmi-node-logic.h b/capi/mmi-node-logic.h new file mode 100644 index 0000000..39f2c60 --- /dev/null +++ b/capi/mmi-node-logic.h @@ -0,0 +1,166 @@ +/* + * Copyright (c) 2023 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 __TIZEN_UIX_MMI_NODE_LOGIC_H__ +#define __TIZEN_UIX_MMI_NODE_LOGIC_H__ + + + +#include + +/** +* @file mmi-node-logic.h +*/ + + +/** +* @addtogroup CAPI_UIX_MMI_MODULE +* @{ +*/ + + +#ifdef __cplusplus +extern "C" { +#endif + + +enum mmi_standard_node_logic_type_e { + MMI_STANDARD_NODE_LOGIC_TYPE_NONE, + + /* + - Description : Compares the text received from the input port 1 ("TEXT") with + 1) the fixed strings specified in the attribute "CANDIDATES" + 2) the array of texts received from input port 2 ("TEXT_ARRAY") + 3) the array of structs received from input port 3 ("STRUCT_ARRAY"), + for the element specified in the attribute "COMPARAND_STRUCT_ELEMENT" + ex) if the struct has two elements named "label" and "text", and + "COMPARAND_STRUCT_ELEMENT" is "label", then the value of + "label" will be compared with the text received from input port 1 + And outputs the mmi_data_t to the corresponding OUT port when matched. + ex1) if the text received from input port 1 is "Hello", and the fixed strings + specified in the attribute "CANDIDATES" is ["Hello", "World"], then the + text "Hello" will be sent to output port 1. + ex2) if the text received from input port 1 is "Hello", and the array of texts + received from input port 2 is ["Hello", "World", "Goodbye"], then the + text "Hello" will be sent to output port 2. + ex3) if the text received from input port 1 is "Hello", with the attribute + "COMPARAND_STRUCT_ELEMENT" set to "label", and the array of structs + received from input port 3 is like below, + [ + {"label":"Hello"}, + {"label":"World"}, + {"label":"Goodbye"} + ] + then the struct {"label":"Hello"} will be sent to output port 3. + - Attributes : + "CANDIDATES" : array [ string ] + "COMPARAND_STRUCT_ELEMENT" : string + - Input Port 1 + Name : "TEXT" + Data Type : MMI_DATA_TYPE_TEXT + - Input Port 2 + Name : "TEXT_ARRAY" + Data Type : MMI_DATA_TYPE_ARRAY [ MMI_DATA_TYPE_TEXT ] + - Input Port 3 + Name : "STRUCT_ARRAY" + Data Type : MMI_DATA_TYPE_ARRAY [ MMI_DATA_TYPE_STRUCT ] + - Output Port 1 + Name : "MATCHED_CANDIDATE" + Data Type : MMI_DATA_TYPE_TEXT + - Output Port 2 + Name : "MATCHED_TEXT_ARRAY" + Data Type : MMI_DATA_TYPE_TEXT + - Output Port 3 + Name : "MATCHED_STRUCT_ARRAY" + Data Type : MMI_DATA_TYPE_STRUCT + */ + MMI_STANDARD_NODE_LOGIC_TYPE_FIXED_STRING_MATCH, + + /* + - Description : Tests if the regex strings specified in the attribute "REGEX" with + 1) the text received from the input port 1 ("TEXT") matches + 2) the array of texts received from input port 2 ("TEXT_ARRAY") + 3) the array of structs received from input port 3 ("STRUCT_ARRAY"), + for the element specified in the attribute "COMPARAND_STRUCT_ELEMENT" + - Attributes : + "REGEX" : array [ string ] + "COMPARAND_STRUCT_ELEMENT" : string + - Input Port 1 + Name : "TEXT" + Data Type : MMI_DATA_TYPE_TEXT + - Input Port 2 + Name : "TEXT_ARRAY" + Data Type : MMI_DATA_TYPE_ARRAY [ MMI_DATA_TYPE_TEXT ] + - Input Port 3 + Name : "STRUCT_ARRAY" + Data Type : MMI_DATA_TYPE_ARRAY [ MMI_DATA_TYPE_STRUCT ] + - Output Port 1 + Name : "MATCHED_TEXT" + Data Type : MMI_DATA_TYPE_TEXT + - Output Port 2 + Name : "MATCHED_TEXT_ARRAY" + Data Type : MMI_DATA_TYPE_TEXT + - Output Port 3 + Name : "MATCHED_STRUCT_ARRAY" + Data Type : MMI_DATA_TYPE_STRUCT + */ + MMI_STANDARD_NODE_LOGIC_TYPE_REGEX_STRING_MATCH, + + + /* + - Description : Compares the User Recognition Struct from each input ports + and selects the one with the highest score. + - Input Port 1 + Name : "Port1" + Data Type : MMI_DATA_TYPE_ARRAY of MMI_DATA_TYPE_STRUCT + Format of MMI_DATA_TYPE_STRUCT : + - "USER" : MMI_DATA_TYPE_USER_IDENTIFICATION + - "SCORE" : MMI_DATA_TYPE_FLOAT + - Input Port 2 + Name : "Port2" + Data Type : MMI_DATA_TYPE_ARRAY of MMI_DATA_TYPE_STRUCT + Format of MMI_DATA_TYPE_STRUCT : + - "USER" : MMI_DATA_TYPE_USER_IDENTIFICATION + - "SCORE" : MMI_DATA_TYPE_FLOAT + - Output Port 1 + Name : "BYPASS_RESULT" + Data Type : MMI_DATA_TYPE_USER_IDENTIFICATION + - Output Port 2 + Name : "COMPARISON_RESULT" + Data Type : MMI_DATA_TYPE_USER_IDENTIFICATION + */ + MMI_STANDARD_NODE_LOGIC_TYPE_USER_COMPARISON, +}; + +int mmi_standard_node_create_logic(mmi_standard_node_logic_type_e type, mmi_node_h *node); + +int mmi_standard_node_get_logic_type(mmi_node_h node, mmi_standard_node_logic_type_e *type); + +int mmi_standard_node_register_logic(mmi_standard_node_logic_type_e type, mmi_node_callbacks *callbacks, mmi_node_h node); + +#ifdef __cplusplus +} +#endif + + +/** + * @} + */ + + +#endif /* __TIZEN_UIX_MMI_NODE_LOGIC_H__ */ diff --git a/capi/mmi-node-processor.h b/capi/mmi-node-processor.h new file mode 100644 index 0000000..3cd7551 --- /dev/null +++ b/capi/mmi-node-processor.h @@ -0,0 +1,160 @@ +/* + * Copyright (c) 2023 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 __TIZEN_UIX_MMI_NODE_PROCESSOR_H__ +#define __TIZEN_UIX_MMI_NODE_PROCESSOR_H__ + + + +#include + +/** +* @file mmi-node-processor.h +*/ + + +/** +* @addtogroup CAPI_UIX_MMI_MODULE +* @{ +*/ + + +#ifdef __cplusplus +extern "C" { +#endif + + +enum mmi_standard_node_processor_type_e { + MMI_STANDARD_NODE_PROCESSOR_TYPE_NONE, + + /* + - Attributes : None + - Input Port 1 + Name : "AUDIO" + Data Type : MMI_DATA_TYPE_AUDIO + - Output Port 1 + Name : "PARTIAL" + Data Type : MMI_DATA_TYPE_TEXT + - Output Port 2 + Name : "FINAL" + Data Type : MMI_DATA_TYPE_TEXT + */ + MMI_STANDARD_NODE_PROCESSOR_TYPE_ASR, + + /* + - Attributes : None + - Input Port 1 + Name : "VIDEO" + Data Type : MMI_DATA_TYPE_VIDEO + - Output Port 1 + Name : "MATCHED_FACES" + Data Type : MMI_DATA_TYPE_ARRAY of MMI_DATA_TYPE_STRUCT + Format of MMI_DATA_TYPE_STRUCT : + - "USER" : MMI_DATA_TYPE_USER_IDENTIFICATION + - "SCORE" : MMI_DATA_TYPE_FLOAT + */ + MMI_STANDARD_NODE_PROCESSOR_TYPE_FACE_RECOGNITION, + + /* + - Attributes : None + - Input Port 1 + Name : "AUDIO" + Data Type : MMI_DATA_TYPE_AUDIO + - Output Port 1 + Name : "MATCHED_SPEAKERS" + Data Type : MMI_DATA_TYPE_ARRAY of MMI_DATA_TYPE_STRUCT + Format of MMI_DATA_TYPE_STRUCT : + - "USER" : MMI_DATA_TYPE_USER_IDENTIFICATION + - "SCORE" : MMI_DATA_TYPE_FLOAT + */ + MMI_STANDARD_NODE_PROCESSOR_TYPE_SPEAKER_RECOGNITION, + + /* + - Attributes : + "GRID_CONFIGURATION" : array [ integer ] + - Input Port 1 + Name : "SCREEN_INFO" + Data Type : MMI_DATA_TYPE_ARRAY of MMI_DATA_TYPE_STRUCT + MMI_DATA_TYPE STRUCT format: { + "coord_x": MMI_DATA_TYPE_INT, + "coord_y": MMI_DATA_TYPE_INT, + "width": MMI_DATA_TYPE_INT, + "height": MMI_DATA_TYPE_INT, + "object_id": MMI_DATA_TYPE_TEXT, + "label": MMI_DATA_TYPE_TEXT, + "role": MMI_DATA_TYPE_TEXT, + } + - Input Port 2 + Name : "MODE_COMMANDS" + Data Type : MMI_DATA_TYPE_TEXT + - Input Port 3 + Name : "UTTERANCE" + Data Type : MMI_DATA_TYPE_TEXT + - Output Port 1 + Name : "CANDIDATES" + Data Type : MMI_DATA_TYPE_ARRAY of MMI_DATA_TYPE_STRUCT + MMI_DATA_TYPE STRUCT format: { + "mode": MMI_DATA_TYPE_INT, + "coord_x": MMI_DATA_TYPE_INT, + "coord_y": MMI_DATA_TYPE_INT, + "width": MMI_DATA_TYPE_INT, + "height": MMI_DATA_TYPE_INT, + "label": MMI_DATA_TYPE_TEXT, + } + - Output Port 2 + Name : "MATCHED_RESULT" + Data Type : MMI_DATA_TYPE_STRUCT + MMI_DATA_TYPE STRUCT format: { + "candidate_info": MMI_DATA_TYPE_STRUCT { + "mode": MMI_DATA_TYPE_INT, + "coord_x": MMI_DATA_TYPE_INT, + "coord_y": MMI_DATA_TYPE_INT, + "width": MMI_DATA_TYPE_INT, + "height": MMI_DATA_TYPE_INT, + "label": MMI_DATA_TYPE_TEXT, + }, + "click_info": MMI_DATA_TYPE_STRUCT { + "x": MMI_DATA_TYPE_INT, + "y": MMI_DATA_TYPE_INT, + "object_id": MMI_DATA_TYPE_TEXT, + } + } + - Output Port 3 + Name : "REJECTED" + Data Type : MMI_DATA_TYPE_TEXT + */ + MMI_STANDARD_NODE_PROCESSOR_TYPE_VOICE_TOUCH, +}; + +int mmi_standard_node_create_processor(mmi_standard_node_processor_type_e type, mmi_node_h *node); + +int mmi_standard_node_get_processor_type(mmi_node_h node, mmi_standard_node_processor_type_e *type); + +int mmi_standard_node_register_processor(mmi_standard_node_processor_type_e type, mmi_node_callbacks *callbacks, mmi_node_h node); + +#ifdef __cplusplus +} +#endif + + +/** + * @} + */ + + +#endif /* __TIZEN_UIX_MMI_NODE_PROCESSOR_H__ */ diff --git a/capi/mmi-node-source.h b/capi/mmi-node-source.h new file mode 100644 index 0000000..dbb445f --- /dev/null +++ b/capi/mmi-node-source.h @@ -0,0 +1,105 @@ +/* + * Copyright (c) 2023 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 __TIZEN_UIX_MMI_NODE_SOURCE_H__ +#define __TIZEN_UIX_MMI_NODE_SOURCE_H__ + + + +#include + +/** +* @file mmi-node-source.h +*/ + + +/** +* @addtogroup CAPI_UIX_MMI_MODULE +* @{ +*/ + + +#ifdef __cplusplus +extern "C" { +#endif + +enum mmi_standard_node_source_type_e { + MMI_STANDARD_NODE_SOURCE_TYPE_NONE, + + /* + - Description : A microphone that captures sound all the time. + - Attributes : + - "UNFOCUSED_ONLY" : bool + Default : false + Description : If true, the node will be activated only when there is + no other application currently acquired sound focus. + - Output Port 1 + Name : "AUDIO" + Type : OUT + Data Type : MMI_DATA_TYPE_AUDIO + */ + MMI_STANDARD_NODE_SOURCE_TYPE_MIC_AMBIENT, + + /* + - Description : A camera that captures video. + - Output Port 1 + Name : "VIDEO" + Type : OUT + Data Type : MMI_DATA_TYPE_VIDEO + */ + MMI_STANDARD_NODE_SOURCE_TYPE_CAMERA, + + /* + - Description : A screen analyzer that gets screen info when rescanning occur. + // FIXME: temporary attribute for replacing signal + - Attributes : + - "RESCANNING" : bool + Default : false + Description : If true, the node will rescan the screen and make screen info + - Output Port 1 + Name : "SCREEN_INFO" + Type : OUT + Data Type : MMI_DATA_TYPE_ARRAY of MMI_DATA_TYPE_STRUCT + MMI_DATA_TYPE STRUCT format: { + "coord_x": MMI_DATA_TYPE_INT, + "coord_y": MMI_DATA_TYPE_INT, + "width": MMI_DATA_TYPE_INT, + "height": MMI_DATA_TYPE_INT, + "object_id": MMI_DATA_TYPE_TEXT, + "label": MMI_DATA_TYPE_TEXT, + "role": MMI_DATA_TYPE_TEXT, + } + */ + MMI_STANDARD_NODE_SOURCE_TYPE_SCREEN_ANALYZER, +}; + +int mmi_standard_node_create_source(mmi_standard_node_source_type_e type, mmi_node_h *node); + +int mmi_standard_node_get_source_type(mmi_node_h node, mmi_standard_node_source_type_e *type); + +#ifdef __cplusplus +} +#endif + + +/** + * @} + */ + + +#endif /* __TIZEN_UIX_MMI_NODE_SOURCE_H__ */ diff --git a/capi/mmi-node.h b/capi/mmi-node.h new file mode 100644 index 0000000..124ecca --- /dev/null +++ b/capi/mmi-node.h @@ -0,0 +1,140 @@ +/* + * Copyright (c) 2023 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 __TIZEN_UIX_MMI_NODE_H__ +#define __TIZEN_UIX_MMI_NODE_H__ + + +#include + +#include +#include +#include +#include +#include + +/** +* @file mmi-node.h +*/ + + +/** +* @addtogroup CAPI_UIX_MMI_MODULE +* @{ +*/ + + +#ifdef __cplusplus +extern "C" { +#endif + +enum mmi_standard_node_type_e { + MMI_STANDARD_NODE_TYPE_NONE, + MMI_STANDARD_NODE_TYPE_SOURCE, + MMI_STANDARD_NODE_TYPE_PROCESSOR, + MMI_STANDARD_NODE_TYPE_LOGIC, + MMI_STANDARD_NODE_TYPE_CONTROLLER, +}; + +typedef void* mmi_node_instance_h; + +typedef int (*mmi_node_initialized_cb)(mmi_node_instance_h instance); +typedef int (*mmi_node_deinitialized_cb)(mmi_node_instance_h instance); +typedef int (*mmi_node_attribute_set_cb)(mmi_node_instance_h instance, mmi_attribute_h attribute); +typedef int (*mmi_node_activated_cb)(mmi_node_instance_h instance); +typedef int (*mmi_node_deactivated_cb)(mmi_node_instance_h instance); +typedef int (*mmi_node_signal_received_cb)(mmi_node_instance_h instance, mmi_signal_h signal); + +typedef struct { + mmi_node_initialized_cb initialized_cb; + mmi_node_deinitialized_cb deinitialized_cb; + mmi_node_attribute_set_cb attribute_set_cb; + mmi_node_activated_cb activated_cb; + mmi_node_deactivated_cb deactivated_cb; + mmi_node_signal_received_cb signal_received_cb; +} mmi_node_callbacks; + +typedef struct { + mmi_standard_node_type_e type; + int sub_type; + mmi_port_h *ports; + size_t port_count; + mmi_node_callbacks callbacks; +} mmi_node_s; + +typedef mmi_node_s* mmi_node_h; + +/* Adds a static port to a static node. The port has to be already created before calling this function. */ +int mmi_node_add_port(mmi_node_h node, mmi_port_h port); + +/* Finds a port in a node matching the given name and type. */ +int mmi_node_find_port(mmi_node_h node, mmi_port_type_e port_type, const char *port_name, mmi_port_h *port); + +/* Retrieves the type of a node. */ +int mmi_node_get_type(mmi_node_h node, mmi_standard_node_type_e *type); + +/* Retrieves the number of ports in a node. */ +int mmi_node_get_port_count(mmi_node_h node, size_t *port_count); + +/* Retrieves the port at the given index. */ +int mmi_node_get_port(mmi_node_h node, size_t index, mmi_port_h *port); + +/* Get the callbacks of a node */ +int mmi_node_get_callbacks(mmi_node_h node, mmi_node_callbacks *callbacks); + +/* Set the callbacks of a node */ +int mmi_node_set_callbacks(mmi_node_h node, mmi_node_callbacks callbacks); + +/* Registers a node. */ +int mmi_node_register(mmi_node_h node); + +/* Clones a node. */ +int mmi_node_clone(mmi_node_h node, mmi_node_h *cloned); + +/* Destroys a node. */ +int mmi_node_destroy(mmi_node_h node); + +/* Provide a attribute to a node instance. */ +int mmi_node_instance_set_attribute(mmi_node_instance_h instance, mmi_attribute_h attribute); + +/* Provide a attribute bundle to a node instance. */ +//int mmi_node_instance_attribute_bundle_set(mmi_node_instance_h instance, mmi_attribute_bundle_h bundle); + +/* Retrieve a port instance of a node instance. */ +int mmi_node_instance_find_port(mmi_node_instance_h instance, mmi_port_type_e port_type, const char *port_name, mmi_port_instance_h *port); + +/* Retrieve a sibiling port instance of a port instance in a node, that matches the given name. */ +int mmi_node_instance_find_sibling_port(mmi_port_instance_h instance, const char *sibling_name, mmi_port_instance_h *sibling); + +/* Emits a signal from a node instance. */ +int mmi_node_instance_emit_signal(mmi_node_instance_h instance, mmi_signal_h signal); + +/* If the correct result could not be returned on last activate_cb() call, it needs to be updated afterwards using this function. */ +int mmi_node_instance_update_pending_activation_result(mmi_error_e result); + +#ifdef __cplusplus +} +#endif + + +/** + * @} + */ + + +#endif /* __TIZEN_UIX_MMI_NODE_H__ */ diff --git a/capi/mmi-plugin-module.h b/capi/mmi-plugin-module.h new file mode 100644 index 0000000..7581a15 --- /dev/null +++ b/capi/mmi-plugin-module.h @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2023 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 __TIZEN_UIX_MMI_PLUGIN_MODULE_H__ +#define __TIZEN_UIX_MMI_PLUGIN_MODULE_H__ + + +#include + +/** +* @file mmi-plugin-module.h +*/ + + +/** +* @addtogroup CAPI_UIX_MMI_MODULE +* @{ +*/ + + +#ifdef __cplusplus +extern "C" { +#endif + +/* If a plugin module wants to provide Nodes to mmi framework, + * it should implement a function named "mmi_plugin_module_get_node_list", + * with the following prototype. */ +#define MMI_PLUGIN_MODULE_GET_NODE_LIST_FUNC_NAME "mmi_plugin_module_get_node_list" +typedef void (*mmi_plugin_module_get_node_list_func)(); + +/* If a plugin module wants to provide Workflows to mmi framework, + * it should implement a function named "mmi_plugin_module_get_workflow_list", + * with the following prototype. */ +#define MMI_PLUGIN_MODULE_GET_WORKFLOW_LIST_FUNC_NAME "mmi_plugin_module_get_workflow_list" +typedef void (*mmi_plugin_module_get_workflow_list_func)(); + +#ifdef __cplusplus +} +#endif + + +/** + * @} + */ + + +#endif /* __TIZEN_UIX_MMI_PLUGIN_MODULE_H__ */ diff --git a/capi/mmi-plugin-storage.h b/capi/mmi-plugin-storage.h new file mode 100644 index 0000000..b68e22c --- /dev/null +++ b/capi/mmi-plugin-storage.h @@ -0,0 +1,124 @@ +/* + * Copyright (c) 2023 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 __TIZEN_UIX_MMI_PLUGIN_STORAGE_H__ +#define __TIZEN_UIX_MMI_PLUGIN_STORAGE_H__ + + +#include +#include +#include +#include +#include +#include +#include +#include + +/** +* @file mmi-plugin-storage.h +*/ + + +/** +* @addtogroup CAPI_UIX_MMI_MODULE +* @{ +*/ + + +#ifdef __cplusplus +extern "C" { +#endif + +/* The functions and structs below are used by MMI framework */ + +typedef struct { + mmi_node_h *nodes; + size_t node_count; +} mmi_plugin_module_node_list_s; + +typedef struct { + mmi_workflow_h *workflows; + size_t workflow_count; +} mmi_plugin_module_workflow_list_s; + + +typedef struct { + char name[MMI_NAME_MAX_LENGTH]; + mmi_port_instance_h port; +} mmi_plugin_module_port_instance_info_s; + +typedef struct { + mmi_node_instance_h node; + mmi_plugin_module_port_instance_info_s port_infos[MMI_PORT_MAX_COUNT]; + size_t port_info_count; +} mmi_plugin_module_node_instance_info_s; + +typedef void (*mmi_plugin_module_port_instance_output_handler_func)( + mmi_port_instance_h port, mmi_data_h data, void *user_data); + +typedef void (*mmi_plugin_module_switch_node_event_handler_func)( + mmi_node_instance_h node, const char *controllee, bool state, void *user_data); + +typedef struct { + mmi_plugin_module_port_instance_output_handler_func port_instance_output_handler; + mmi_plugin_module_switch_node_event_handler_func switch_node_event_handler; +} mmi_plugin_module_event_handler_info_s; + +/* MMI Manager will invoke this function to set the current identifier of the plugin module. */ +void mmi_plugin_storage_set_current_module_identifier(const char *identifier); + +/* Each plugin modules will call this functino to get the current identifier of the plugin module. */ +const char* mmi_plugin_storage_get_current_module_identifier(); + +/* Each plugin modules will call these functions to register their nodes and workflows. */ +void mmi_plugin_storage_set_node_list(mmi_plugin_module_node_list_s node_list); +void mmi_plugin_storage_set_workflow_list(mmi_plugin_module_workflow_list_s workflow_list); + +/* MMI Manager will invoke these functions to get the node list of the plugin module. */ +mmi_plugin_module_node_list_s mmi_plugin_storage_get_node_list(const char *identifier); +mmi_plugin_module_workflow_list_s mmi_plugin_storage_get_workflow_list(const char *identifier); + +/* MMI Manager will invoke these function to notify the node instance addition and removal. */ +void mmi_plugin_storage_add_node_instance_info(mmi_plugin_module_node_instance_info_s *info); +void mmi_plugin_storage_remove_node_instance_info(mmi_node_instance_h node_instance); + +/* Each plugin modules will call this function to find appropriate port instance */ +mmi_port_instance_h mmi_plugin_storage_find_port_instance( + mmi_node_instance_h node_instance, const char *port_name); +mmi_node_instance_h mmi_plugin_storage_find_node_instance_by_port_instance(mmi_port_instance_h port_instance); + +/* MMI Manager will invoke these function to register / unregister the event handlers */ +void mmi_plugin_storage_set_plugin_module_event_handler( + mmi_plugin_module_event_handler_info_s handler_info, void *user_data); +void mmi_plugin_storage_unset_plugin_module_event_handler(); + +/* Each plugin modules will call this function to notify the plugin module events */ +mmi_plugin_module_event_handler_info_s mmi_plugin_storage_get_plugin_module_event_handler(); +void* mmi_plugin_storage_get_plugin_module_event_handler_user_data(); + +#ifdef __cplusplus +} +#endif + + +/** + * @} + */ + + +#endif /* __TIZEN_UIX_MMI_PLUGIN_STORAGE_H__ */ diff --git a/capi/mmi-port.h b/capi/mmi-port.h new file mode 100644 index 0000000..2efc3de --- /dev/null +++ b/capi/mmi-port.h @@ -0,0 +1,129 @@ +/* + * Copyright (c) 2023 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 __TIZEN_UIX_MMI_PORT_H__ +#define __TIZEN_UIX_MMI_PORT_H__ + + +#include + +#include +#include +#include + +/** +* @file mmi-port.h +*/ + + +/** +* @addtogroup CAPI_UIX_MMI_MODULE +* @{ +*/ + + +#ifdef __cplusplus +extern "C" { +#endif + +enum mmi_port_type_e { + MMI_PORT_TYPE_IN, + MMI_PORT_TYPE_OUT +}; + +typedef void* mmi_port_instance_h; + +typedef int (*mmi_port_output_format_requested_cb)(mmi_port_instance_h instance, const char *format); +typedef int (*mmi_port_input_data_received_cb)(mmi_port_instance_h instance, mmi_data_h data); + +typedef struct { + mmi_port_output_format_requested_cb output_format_requested_cb; + mmi_port_input_data_received_cb input_data_received_cb; +} mmi_port_callbacks; + +typedef struct { + char name[MMI_NAME_MAX_LENGTH]; + mmi_port_type_e type; + mmi_data_type_e data_type; + mmi_port_callbacks callbacks; +} mmi_port_s; + +typedef mmi_port_s* mmi_port_h; + +/* Create a port */ +int mmi_port_create(mmi_port_h *port); + +/* Get the name of a port */ +int mmi_port_get_name(mmi_port_h port, char **name, size_t *length); + +/* Get the type of a port */ +int mmi_port_get_type(mmi_port_h port, mmi_port_type_e *type); + +/* Get the data type of a port */ +int mmi_port_get_data_type(mmi_port_h port, mmi_data_type_e *data_type); + +/* Get the callbacks of a port */ +int mmi_port_get_callbacks(mmi_port_h port, mmi_port_callbacks *callbacks); + +/* Set the name of a port */ +int mmi_port_set_name(mmi_port_h port, const char *name); + +/* Set the type of a port */ +int mmi_port_set_type(mmi_port_h port, mmi_port_type_e type); + +/* Set the data type of a port */ +int mmi_port_set_data_type(mmi_port_h port, mmi_data_type_e data_type); + +/* Set the callbacks of a port */ +int mmi_port_set_callbacks(mmi_port_h port, mmi_port_callbacks callbacks); + +/* Clones a port */ +int mmi_port_clone(mmi_port_h port, mmi_port_h *cloned); + +/* Destroy a port */ +int mmi_port_destroy(mmi_port_h port); + +/* Provide a link information between two static ports. */ +// int mmi_port_link(mmi_port_h out, mmi_port_h in); + +/* Registers a port with the given type, data type, and callbacks. */ +// int mmi_port_register(mmi_port_type_e port_type, mmi_data_type_e data_type, mmi_port_callbacks *callbacks, mmi_port_h port); + +/* Generates data to the port with the given port instance. */ +int mmi_port_instance_generate_output(mmi_port_instance_h instance, mmi_data_h data); + +/* Requests the port instance to transform the data to the given format. */ +int mmi_port_instance_request_auto_transform(mmi_port_instance_h instance, const char *from_format, const char *to_format); + +/* Requests the port instance to retrieve the data with the given format. */ +int mmi_port_instance_request_input_data_format(mmi_port_instance_h instance, const char *format); + +/* Announce the data format to the port instance. */ +int mmi_port_instance_announce_output_data_format(mmi_port_instance_h instance, const char *format); + +#ifdef __cplusplus +} +#endif + + +/** + * @} + */ + + +#endif /* __TIZEN_UIX_MMI_PORT_H__ */ diff --git a/capi/mmi-primitive-value.h b/capi/mmi-primitive-value.h new file mode 100644 index 0000000..b8b25e8 --- /dev/null +++ b/capi/mmi-primitive-value.h @@ -0,0 +1,99 @@ +/* + * Copyright (c) 2023 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 __TIZEN_UIX_MMI_PRIMITIVE_VALUE_H__ +#define __TIZEN_UIX_MMI_PRIMITIVE_VALUE_H__ + + +#include +#include + + +/** +* @file mmi-primitive-value.h +*/ + + +/** +* @addtogroup CAPI_UIX_MMI_MODULE +* @{ +*/ + + +#ifdef __cplusplus +extern "C" { +#endif + + +typedef enum { + MMI_PRIMITIVE_VALUE_TYPE_INT, + MMI_PRIMITIVE_VALUE_TYPE_FLOAT, + MMI_PRIMITIVE_VALUE_TYPE_STRING, + MMI_PRIMITIVE_VALUE_TYPE_BOOL, + MMI_PRIMITIVE_VALUE_TYPE_ARRAY, +} mmi_primitive_value_type_e; + + +typedef struct mmi_primitive_value_s* mmi_primitive_value_h; + + +int mmi_primitive_value_create_int(int data, mmi_primitive_value_h *handle); + +int mmi_primitive_value_create_float(float data, mmi_primitive_value_h *handle); + +int mmi_primitive_value_create_string(const char *data, mmi_primitive_value_h *handle); + +int mmi_primitive_value_create_bool(bool data, mmi_primitive_value_h *handle); + +int mmi_primitive_value_create_array(mmi_primitive_value_h *handle); + +int mmi_primitive_value_add_array_element(mmi_primitive_value_h array, mmi_primitive_value_h element); + +int mmi_primitive_value_get_type(mmi_primitive_value_h handle, mmi_primitive_value_type_e *type); + +int mmi_primitive_value_get_int(mmi_primitive_value_h handle, int *value); + +int mmi_primitive_value_get_float(mmi_primitive_value_h handle, float *value); + +int mmi_primitive_value_get_string(mmi_primitive_value_h handle, const char **string); + +int mmi_primitive_value_get_bool(mmi_primitive_value_h handle, bool *value); + +int mmi_primitive_value_get_array_count(mmi_primitive_value_h array, size_t *count); + +int mmi_primitive_value_get_array_element(mmi_primitive_value_h array, size_t index, mmi_primitive_value_h *element); + +int mmi_primitive_value_clone(mmi_primitive_value_h handle, mmi_primitive_value_h *cloned); + +int mmi_primitive_value_destroy(mmi_primitive_value_h handle); + +int mmi_primitive_value_to_bytes(mmi_primitive_value_h handle, unsigned char **bytes, size_t *size); + +int mmi_primitive_value_from_bytes(unsigned char *bytes, size_t size, mmi_primitive_value_h *handle); + +#ifdef __cplusplus +} +#endif + + +/** + * @} + */ + + +#endif /* __TIZEN_UIX_MMI_PRIMITIVE_VALUE_H__ */ diff --git a/capi/mmi-signal.h b/capi/mmi-signal.h new file mode 100644 index 0000000..3d989ec --- /dev/null +++ b/capi/mmi-signal.h @@ -0,0 +1,94 @@ +/* + * Copyright (c) 2023 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 __TIZEN_UIX_MMI_SIGNAL_H__ +#define __TIZEN_UIX_MMI_SIGNAL_H__ + + +#include + +#include +#include +#include + +#include +#include + +/** +* @file mmi-port.h +*/ + + +/** +* @addtogroup CAPI_UIX_MMI_MODULE +* @{ +*/ + + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct { + char name[MMI_NAME_MAX_LENGTH]; + mmi_primitive_value_h value; +} mmi_signal_parameter_s; + +typedef mmi_signal_parameter_s* mmi_signal_parameter_h; + +typedef struct { + char name[MMI_NAME_MAX_LENGTH]; + mmi_signal_parameter_h parameters[MMI_PARAMETER_MAX_COUNT]; + int parameter_count; +} mmi_signal_s; + +typedef mmi_signal_s* mmi_signal_h; + +int mmi_signal_parameter_create(mmi_primitive_value_h value, const char *name, mmi_signal_parameter_h *parameter); + +int mmi_signal_parameter_get_name(mmi_signal_parameter_h parameter, char **name); + +int mmi_signal_parameter_get_value(mmi_signal_parameter_h parameter, mmi_primitive_value_h *value); + +int mmi_signal_parameter_clone(mmi_signal_parameter_h parameter, mmi_signal_parameter_h *cloned); + +int mmi_signal_parameter_destroy(mmi_signal_parameter_h parameter); + +int mmi_signal_create(const char *name, mmi_signal_h *handle); + +int mmi_signal_add_parameter(mmi_signal_h handle, mmi_signal_parameter_h parameter); + +int mmi_signal_get_name(mmi_signal_h handle, char **name); + +int mmi_signal_get_parameter_count(mmi_signal_h handle, int *count); + +int mmi_signal_get_parameter(mmi_signal_h handle, int index, mmi_signal_parameter_h *parameter); + +int mmi_signal_destroy(mmi_signal_h handle); + +#ifdef __cplusplus +} +#endif + + +/** + * @} + */ + + +#endif /* __TIZEN_UIX_MMI_SIGNAL_H__ */ diff --git a/capi/mmi-workflow.h b/capi/mmi-workflow.h new file mode 100644 index 0000000..148dd3e --- /dev/null +++ b/capi/mmi-workflow.h @@ -0,0 +1,194 @@ +/* + * Copyright (c) 2023 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 __TIZEN_UIX_MMI_WORKFLOW_H__ +#define __TIZEN_UIX_MMI_WORKFLOW_H__ + + +#include + +#include +#include +#include + + +/** +* @file mmi-workflow.h +*/ + + +/** +* @addtogroup CAPI_UIX_MMI_MODULE +* @{ +*/ + + +#ifdef __cplusplus +extern "C" { +#endif + +typedef enum { + MMI_STANDARD_WORKFLOW_NONE, + MMI_STANDARD_WORKFLOW_WAKEUPLESS_COMMAND, + MMI_STANDARD_WORKFLOW_VOICE_TOUCH, + MMI_STANDARD_WORKFLOW_USER_RECOGNITION, +} mmi_standard_workflow_type_e; + + +/* Functions to define a workflow prototype. Mostly used by workflow developers. */ + +typedef struct { + char name[MMI_NAME_MAX_LENGTH]; + mmi_node_h node; +} mmi_workflow_node_info_s; + +typedef struct { + char from_node_name[MMI_NAME_MAX_LENGTH]; + char from_port_name[MMI_NAME_MAX_LENGTH]; + char to_node_name[MMI_NAME_MAX_LENGTH]; + char to_port_name[MMI_NAME_MAX_LENGTH]; +} mmi_workflow_link_info_s; + +typedef struct { + char attribute_name[MMI_NAME_MAX_LENGTH]; + char target_node_name[MMI_NAME_MAX_LENGTH]; + char target_attribute_name[MMI_NAME_MAX_LENGTH]; +} mmi_workflow_attribute_assignment_info_s; + +typedef struct { + size_t serialized_default_value_size; + unsigned char *serialized_default_value; +} mmi_workflow_attribute_default_value_info_s; + +typedef struct { + char output_name[MMI_NAME_MAX_LENGTH]; + char from_node_name[MMI_NAME_MAX_LENGTH]; + char from_port_name[MMI_NAME_MAX_LENGTH]; +} mmi_workflow_output_assignment_info_s; + +typedef struct { + mmi_standard_workflow_type_e type; + mmi_workflow_node_info_s *node_infos; + size_t node_info_count; + mmi_workflow_link_info_s *link_infos; + size_t link_info_count; + mmi_workflow_attribute_assignment_info_s *attribute_assignment_infos; + size_t attribute_assignment_info_count; + mmi_workflow_attribute_default_value_info_s *attribute_default_value_infos; + size_t attribute_default_value_info_count; + mmi_workflow_output_assignment_info_s *output_assignment_infos; + size_t output_assignment_info_count; +} mmi_workflow_s; + +typedef mmi_workflow_s* mmi_workflow_h; + +/* Create a workflow prototype */ +int mmi_workflow_create(mmi_workflow_h *workflow); + +/* Set the type of a workflow prototype */ +int mmi_workflow_set_type(mmi_workflow_h workflow, mmi_standard_workflow_type_e type); + +/* Get the type of a workflow prototype */ +int mmi_workflow_get_type(mmi_workflow_h workflow, mmi_standard_workflow_type_e *type); + +/* Add a node to a workflow prototype */ +int mmi_workflow_node_add(mmi_workflow_h workflow, const char *node_name, mmi_node_h node); + +/* A convinience function to link two nodes by automatically finding the appropriate ports in the nodes. */ +/* The linking succeeds only if there is exactly one pair of compatible input and output ports in the nodes. */ +/* int mmi_workflow_link_nodes(mmi_node_h from_node, mmi_node_h to_node); */ + +/* Link two nodes with specifying the port names. */ +/* FIXME: It would be better to use handles instead of names. */ +int mmi_workflow_link_nodes_by_names(mmi_workflow_h workflow, const char *from_node_name, const char *from_port_name, const char *to_node_name, const char *to_port_name); + +/* Assign an attribute of a workflow to an attribute of a specific node in a workflow prototype */ +/* FIXME: It would be better to use handles instead of names. */ +int mmi_workflow_attribute_assign(mmi_workflow_h workflow, const char *attribute_name, const char *target_node_name, const char *target_attribute_name); + +/* Set the default value of an attribute of a workflow */ +int mmi_workflow_attribute_set_default_value(mmi_workflow_h workflow, mmi_attribute_h default_value); + +/* Assign a signal of a workflow to a signal of a specific node in a workflow prototype */ +int mmi_workflow_signal_assign(mmi_workflow_h workflow, const char *workflow_signal, mmi_node_h node, const char *node_signal); + +/* Assign an output of a workflow to a OUT port of a specific node in a workflow prototype */ +/* FIXME: It would be better to use handles instead of names. */ +int mmi_workflow_output_assign(mmi_workflow_h workflow, const char *workflow_output, const char *out_node_name, const char *node_out_port_name); + +/* Assign an output of a workflow to a OUT port of a specific node in a workflow prototype, with a port handle */ +int mmi_workflow_output_assign_by_port(mmi_workflow_h workflow, const char *workflow_output, mmi_port_h port); + +/* Register a standard workflow prototype to the workflow manager */ +int mmi_standard_workflow_register(mmi_workflow_h workflow); + +/* Clones a workflow. */ +int mmi_workflow_clone(mmi_workflow_h workflow, mmi_workflow_h *cloned); + +/* Create a workflow prototype from a script file */ +int mmi_workflow_create_from_script(const char *script_path, mmi_workflow_h *workflow); + +/* Destroy a workflow prototype */ +int mmi_workflow_destroy(mmi_workflow_h workflow); + +/* Functions to create a workflow instance. Mostly used by application developers. */ + +typedef void* mmi_workflow_instance_h; + +/* Callback function for workflow output */ +typedef void (*mmi_workflow_output_cb)(mmi_workflow_instance_h instance, const char *name, mmi_data_h data, void *user_data); + +/* Callback function for workflow signal */ +// typedef void (*mmi_workflow_signal_cb)(const char *name, mmi_signal_parameter_h parameter, void *user_data); + +/* Instantiate a workflow from a workflow prototype */ +int mmi_standard_workflow_instance_create(mmi_standard_workflow_type_e type, mmi_workflow_instance_h *instance); + +/* Destroy a workflow instance */ +int mmi_workflow_instance_destroy(mmi_workflow_instance_h instance); + +/* Start a workflow instance */ +int mmi_workflow_instance_activate(mmi_workflow_instance_h instance); + +/* Stop a workflow instance */ +int mmi_workflow_instance_deactivate(mmi_workflow_instance_h instance); + +/* Set an attribute of a workflow instance */ +int mmi_workflow_instance_set_attribute(mmi_workflow_instance_h instance, mmi_attribute_h attribute); + +/* Emit a signal to a workflow instance */ +// int mmi_workflow_instance_signal_emit(mmi_workflow_instance_h instance, mmi_signal_h signal); + +/* Set a callback function to receive workflow output */ +int mmi_workflow_instance_set_output_callback(mmi_workflow_instance_h instance, const char *name, mmi_workflow_output_cb callback, void *user_data); + +/* Set a callback function to receive workflow signal */ +// int mmi_workflow_instance_set_signal_callback(mmi_workflow_instance_h instance, const char *name, mmi_workflow_signal_cb callback, void *user_data); + + +#ifdef __cplusplus +} +#endif + + +/** + * @} + */ + + +#endif /* __TIZEN_UIX_MMI_WORKFLOW_H__ */ diff --git a/capi/mmi.h b/capi/mmi.h new file mode 100644 index 0000000..e3df8d8 --- /dev/null +++ b/capi/mmi.h @@ -0,0 +1,79 @@ +/* + * Copyright (c) 2022 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 __MMI_H__ +#define __MMI_H__ + +#include +#define MMI_API __attribute__ ((visibility("default"))) + +#include +#include +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + + +typedef enum { + MMI_STATE_NONE = 0, + MMI_STATE_READY, +} mmi_state_e; + +typedef int (*mmi_state_changed_cb)(mmi_state_e state, void *user_data); + +/** @brief Initialize MMI framework + * + * @return 0 on success, otherwise a negative error value + */ +int mmi_initialize(); + +/** @brief Deinitialize MMI framework + * + * @return 0 on success, otherwise a negative error value + */ +int mmi_deinitialize(); + +/** @brief Set callback for state change + * + * @param[in] callback The callback function to be called when state is changed + * @param[in] user_data The user data to be passed to the callback function + * + * @return 0 on success, otherwise a negative error value + * @see mmi_state_changed_cb + */ +int mmi_set_state_changed_cb(mmi_state_changed_cb callback, void *user_data); + +/** @brief Unset callback for state change + * + * @param[in] callback The callback function to be unset + * + * @return 0 on success, otherwise a negative error value + */ +int mmi_unset_state_changed_cb(mmi_state_changed_cb callback); + +#ifdef __cplusplus +} +#endif + + + +#endif //__MMI_H__ diff --git a/external/adishavit/LICENSE.txt b/external/adishavit/LICENSE.txt new file mode 100644 index 0000000..7cf4796 --- /dev/null +++ b/external/adishavit/LICENSE.txt @@ -0,0 +1,26 @@ +Copyright (c) 2016, Adi Shavit +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of nor the names of its contributors may be used to + endorse or promote products derived from this software without specific + prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. diff --git a/external/adishavit/argh.h b/external/adishavit/argh.h new file mode 100644 index 0000000..fb38dea --- /dev/null +++ b/external/adishavit/argh.h @@ -0,0 +1,434 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace argh +{ + // Terminology: + // A command line is composed of 2 types of args: + // 1. Positional args, i.e. free standing values + // 2. Options: args beginning with '-'. We identify two kinds: + // 2.1: Flags: boolean options => (exist ? true : false) + // 2.2: Parameters: a name followed by a non-option value + +#if !defined(__GNUC__) || (__GNUC__ >= 5) + using string_stream = std::istringstream; +#else + // Until GCC 5, istringstream did not have a move constructor. + // stringstream_proxy is used instead, as a workaround. + class stringstream_proxy + { + public: + stringstream_proxy() = default; + + // Construct with a value. + stringstream_proxy(std::string const& value) : + stream_(value) + {} + + // Copy constructor. + stringstream_proxy(const stringstream_proxy& other) : + stream_(other.stream_.str()) + { + stream_.setstate(other.stream_.rdstate()); + } + + void setstate(std::ios_base::iostate state) { stream_.setstate(state); } + + // Stream out the value of the parameter. + // If the conversion was not possible, the stream will enter the fail state, + // and operator bool will return false. + template + stringstream_proxy& operator >> (T& thing) + { + stream_ >> thing; + return *this; + } + + + // Get the string value. + std::string str() const { return stream_.str(); } + + std::stringbuf* rdbuf() const { return stream_.rdbuf(); } + + // Check the state of the stream. + // False when the most recent stream operation failed + explicit operator bool() const { return !!stream_; } + + ~stringstream_proxy() = default; + private: + std::istringstream stream_; + }; + using string_stream = stringstream_proxy; +#endif + + class parser + { + public: + enum Mode { PREFER_FLAG_FOR_UNREG_OPTION = 1 << 0, + PREFER_PARAM_FOR_UNREG_OPTION = 1 << 1, + NO_SPLIT_ON_EQUALSIGN = 1 << 2, + SINGLE_DASH_IS_MULTIFLAG = 1 << 3, + }; + + parser() = default; + + parser(std::initializer_list pre_reg_names) + { add_params(pre_reg_names); } + + parser(const char* const argv[], int mode = PREFER_FLAG_FOR_UNREG_OPTION) + { parse(argv, mode); } + + parser(int argc, const char* const argv[], int mode = PREFER_FLAG_FOR_UNREG_OPTION) + { parse(argc, argv, mode); } + + void add_param(std::string const& name); + void add_params(std::initializer_list init_list); + + void parse(const char* const argv[], int mode = PREFER_FLAG_FOR_UNREG_OPTION); + void parse(int argc, const char* const argv[], int mode = PREFER_FLAG_FOR_UNREG_OPTION); + + std::multiset const& flags() const { return flags_; } + std::map const& params() const { return params_; } + std::vector const& pos_args() const { return pos_args_; } + + // begin() and end() for using range-for over positional args. + std::vector::const_iterator begin() const { return pos_args_.cbegin(); } + std::vector::const_iterator end() const { return pos_args_.cend(); } + size_t size() const { return pos_args_.size(); } + + ////////////////////////////////////////////////////////////////////////// + // Accessors + + // flag (boolean) accessors: return true if the flag appeared, otherwise false. + bool operator[](std::string const& name) const; + + // multiple flag (boolean) accessors: return true if at least one of the flag appeared, otherwise false. + bool operator[](std::initializer_list init_list) const; + + // returns positional arg string by order. Like argv[] but without the options + std::string const& operator[](size_t ind) const; + + // returns a std::istream that can be used to convert a positional arg to a typed value. + string_stream operator()(size_t ind) const; + + // same as above, but with a default value in case the arg is missing (index out of range). + template + string_stream operator()(size_t ind, T&& def_val) const; + + // parameter accessors, give a name get an std::istream that can be used to convert to a typed value. + // call .str() on result to get as string + string_stream operator()(std::string const& name) const; + + // accessor for a parameter with multiple names, give a list of names, get an std::istream that can be used to convert to a typed value. + // call .str() on result to get as string + // returns the first value in the list to be found. + string_stream operator()(std::initializer_list init_list) const; + + // same as above, but with a default value in case the param was missing. + // Non-string def_val types must have an operator<<() (output stream operator) + // If T only has an input stream operator, pass the string version of the type as in "3" instead of 3. + template + string_stream operator()(std::string const& name, T&& def_val) const; + + // same as above but for a list of names. returns the first value to be found. + template + string_stream operator()(std::initializer_list init_list, T&& def_val) const; + + private: + string_stream bad_stream() const; + std::string trim_leading_dashes(std::string const& name) const; + bool is_number(std::string const& arg) const; + bool is_option(std::string const& arg) const; + bool got_flag(std::string const& name) const; + bool is_param(std::string const& name) const; + + private: + std::vector args_; + std::map params_; + std::vector pos_args_; + std::multiset flags_; + std::set registeredParams_; + std::string empty_; + }; + + + ////////////////////////////////////////////////////////////////////////// + + inline void parser::parse(const char * const argv[], int mode) + { + int argc = 0; + for (auto argvp = argv; *argvp; ++argc, ++argvp); + parse(argc, argv, mode); + } + + ////////////////////////////////////////////////////////////////////////// + + inline void parser::parse(int argc, const char* const argv[], int mode /*= PREFER_FLAG_FOR_UNREG_OPTION*/) + { + // convert to strings + args_.resize(static_cast(argc)); + std::transform(argv, argv + argc, args_.begin(), [](const char* const arg) { return arg; }); + + // parse line + for (auto i = 0u; i < args_.size(); ++i) + { + if (!is_option(args_[i])) + { + pos_args_.emplace_back(args_[i]); + continue; + } + + auto name = trim_leading_dashes(args_[i]); + + if (!(mode & NO_SPLIT_ON_EQUALSIGN)) + { + auto equalPos = name.find('='); + if (equalPos != std::string::npos) + { + params_.insert({ name.substr(0, equalPos), name.substr(equalPos + 1) }); + continue; + } + } + + // if the option is unregistered and should be a multi-flag + if (1 == (args_[i].size() - name.size()) && // single dash + argh::parser::SINGLE_DASH_IS_MULTIFLAG & mode && // multi-flag mode + !is_param(name)) // unregistered + { + std::string keep_param; + + if (!name.empty() && is_param(std::string(1ul, name.back()))) // last char is param + { + keep_param += name.back(); + name.resize(name.size() - 1); + } + + for (auto const& c : name) + { + flags_.emplace(std::string{ c }); + } + + if (!keep_param.empty()) + { + name = keep_param; + } + else + { + continue; // do not consider other options for this arg + } + } + + // any potential option will get as its value the next arg, unless that arg is an option too + // in that case it will be determined a flag. + if (i == args_.size() - 1 || is_option(args_[i + 1])) + { + flags_.emplace(name); + continue; + } + + // if 'name' is a pre-registered option, then the next arg cannot be a free parameter to it is skipped + // otherwise we have 2 modes: + // PREFER_FLAG_FOR_UNREG_OPTION: a non-registered 'name' is determined a flag. + // The following value (the next arg) will be a free parameter. + // + // PREFER_PARAM_FOR_UNREG_OPTION: a non-registered 'name' is determined a parameter, the next arg + // will be the value of that option. + + assert(!(mode & argh::parser::PREFER_FLAG_FOR_UNREG_OPTION) + || !(mode & argh::parser::PREFER_PARAM_FOR_UNREG_OPTION)); + + bool preferParam = mode & argh::parser::PREFER_PARAM_FOR_UNREG_OPTION; + + if (is_param(name) || preferParam) + { + params_.insert({ name, args_[i + 1] }); + ++i; // skip next value, it is not a free parameter + continue; + } + else + { + flags_.emplace(name); + } + }; + } + + ////////////////////////////////////////////////////////////////////////// + + inline string_stream parser::bad_stream() const + { + string_stream bad; + bad.setstate(std::ios_base::failbit); + return bad; + } + + ////////////////////////////////////////////////////////////////////////// + + inline bool parser::is_number(std::string const& arg) const + { + // inefficient but simple way to determine if a string is a number (which can start with a '-') + std::istringstream istr(arg); + double number; + istr >> number; + return !(istr.fail() || istr.bad()); + } + + ////////////////////////////////////////////////////////////////////////// + + inline bool parser::is_option(std::string const& arg) const + { + assert(0 != arg.size()); + if (is_number(arg)) + return false; + return '-' == arg[0]; + } + + ////////////////////////////////////////////////////////////////////////// + + inline std::string parser::trim_leading_dashes(std::string const& name) const + { + auto pos = name.find_first_not_of('-'); + return std::string::npos != pos ? name.substr(pos) : name; + } + + ////////////////////////////////////////////////////////////////////////// + + inline bool argh::parser::got_flag(std::string const& name) const + { + return flags_.end() != flags_.find(trim_leading_dashes(name)); + } + + ////////////////////////////////////////////////////////////////////////// + + inline bool argh::parser::is_param(std::string const& name) const + { + return registeredParams_.count(name); + } + + ////////////////////////////////////////////////////////////////////////// + + inline bool parser::operator[](std::string const& name) const + { + return got_flag(name); + } + + ////////////////////////////////////////////////////////////////////////// + + inline bool parser::operator[](std::initializer_list init_list) const + { + return std::any_of(init_list.begin(), init_list.end(), [&](char const* const name) { return got_flag(name); }); + } + + ////////////////////////////////////////////////////////////////////////// + + inline std::string const& parser::operator[](size_t ind) const + { + if (ind < pos_args_.size()) + return pos_args_[ind]; + return empty_; + } + + ////////////////////////////////////////////////////////////////////////// + + inline string_stream parser::operator()(std::string const& name) const + { + auto optIt = params_.find(trim_leading_dashes(name)); + if (params_.end() != optIt) + return string_stream(optIt->second); + return bad_stream(); + } + + ////////////////////////////////////////////////////////////////////////// + + inline string_stream parser::operator()(std::initializer_list init_list) const + { + for (auto& name : init_list) + { + auto optIt = params_.find(trim_leading_dashes(name)); + if (params_.end() != optIt) + return string_stream(optIt->second); + } + return bad_stream(); + } + + ////////////////////////////////////////////////////////////////////////// + + template + string_stream parser::operator()(std::string const& name, T&& def_val) const + { + auto optIt = params_.find(trim_leading_dashes(name)); + if (params_.end() != optIt) + return string_stream(optIt->second); + + std::ostringstream ostr; + ostr.precision(std::numeric_limits::max_digits10); + ostr << def_val; + return string_stream(ostr.str()); // use default + } + + ////////////////////////////////////////////////////////////////////////// + + // same as above but for a list of names. returns the first value to be found. + template + string_stream parser::operator()(std::initializer_list init_list, T&& def_val) const + { + for (auto& name : init_list) + { + auto optIt = params_.find(trim_leading_dashes(name)); + if (params_.end() != optIt) + return string_stream(optIt->second); + } + std::ostringstream ostr; + ostr.precision(std::numeric_limits::max_digits10); + ostr << def_val; + return string_stream(ostr.str()); // use default + } + + ////////////////////////////////////////////////////////////////////////// + + inline string_stream parser::operator()(size_t ind) const + { + if (pos_args_.size() <= ind) + return bad_stream(); + + return string_stream(pos_args_[ind]); + } + + ////////////////////////////////////////////////////////////////////////// + + template + string_stream parser::operator()(size_t ind, T&& def_val) const + { + if (pos_args_.size() <= ind) + { + std::ostringstream ostr; + ostr.precision(std::numeric_limits::max_digits10); + ostr << def_val; + return string_stream(ostr.str()); + } + + return string_stream(pos_args_[ind]); + } + + ////////////////////////////////////////////////////////////////////////// + + inline void parser::add_param(std::string const& name) + { + registeredParams_.insert(trim_leading_dashes(name)); + } + + ////////////////////////////////////////////////////////////////////////// + + inline void parser::add_params(std::initializer_list init_list) + { + for (auto& name : init_list) + registeredParams_.insert(trim_leading_dashes(name)); + } +} diff --git a/external/cli/LICENSE b/external/cli/LICENSE new file mode 100644 index 0000000..127a5bc --- /dev/null +++ b/external/cli/LICENSE @@ -0,0 +1,23 @@ +Boost Software License - Version 1.0 - August 17th, 2003 + +Permission is hereby granted, free of charge, to any person or organization +obtaining a copy of the software and accompanying documentation covered by +this license (the "Software") to use, reproduce, display, distribute, +execute, and transmit the Software, and to prepare derivative works of the +Software, and to permit third-parties to whom the Software is furnished to +do so, all subject to the following: + +The copyright notices in the Software and this entire statement, including +the above license grant, this restriction and the following disclaimer, +must be included in all copies of the Software, in whole or in part, and +all derivative works of the Software, unless such copies or derivative +works are solely in the form of machine-executable object code generated by +a source language processor. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT +SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE +FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, +ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. \ No newline at end of file diff --git a/external/cli/boostasiocliasyncsession.h b/external/cli/boostasiocliasyncsession.h new file mode 100644 index 0000000..e2dc607 --- /dev/null +++ b/external/cli/boostasiocliasyncsession.h @@ -0,0 +1,40 @@ +/******************************************************************************* + * CLI - A simple command line interface. + * Copyright (C) 2016-2021 Daniele Pallastrelli + * + * Boost Software License - Version 1.0 - August 17th, 2003 + * + * Permission is hereby granted, free of charge, to any person or organization + * obtaining a copy of the software and accompanying documentation covered by + * this license (the "Software") to use, reproduce, display, distribute, + * execute, and transmit the Software, and to prepare derivative works of the + * Software, and to permit third-parties to whom the Software is furnished to + * do so, all subject to the following: + * + * The copyright notices in the Software and this entire statement, including + * the above license grant, this restriction and the following disclaimer, + * must be included in all copies of the Software, in whole or in part, and + * all derivative works of the Software, unless such copies or derivative + * works are solely in the form of machine-executable object code generated by + * a source language processor. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT + * SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE + * FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ******************************************************************************/ + +#ifndef CLI_BOOSTASIOCLIASYNCSESSION_H_ +#define CLI_BOOSTASIOCLIASYNCSESSION_H_ + +#include "detail/genericcliasyncsession.h" +#include "detail/boostasiolib.h" + + +namespace cli { using BoostAsioCliAsyncSession = detail::GenericCliAsyncSession; } + +#endif // CLI_BOOSTASIOCLIASYNCSESSION_H_ + diff --git a/external/cli/boostasioremotecli.h b/external/cli/boostasioremotecli.h new file mode 100644 index 0000000..4f25749 --- /dev/null +++ b/external/cli/boostasioremotecli.h @@ -0,0 +1,39 @@ +/******************************************************************************* + * CLI - A simple command line interface. + * Copyright (C) 2016-2021 Daniele Pallastrelli + * + * Boost Software License - Version 1.0 - August 17th, 2003 + * + * Permission is hereby granted, free of charge, to any person or organization + * obtaining a copy of the software and accompanying documentation covered by + * this license (the "Software") to use, reproduce, display, distribute, + * execute, and transmit the Software, and to prepare derivative works of the + * Software, and to permit third-parties to whom the Software is furnished to + * do so, all subject to the following: + * + * The copyright notices in the Software and this entire statement, including + * the above license grant, this restriction and the following disclaimer, + * must be included in all copies of the Software, in whole or in part, and + * all derivative works of the Software, unless such copies or derivative + * works are solely in the form of machine-executable object code generated by + * a source language processor. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT + * SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE + * FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ******************************************************************************/ + +#ifndef CLI_BOOSTASIOREMOTECLI_H_ +#define CLI_BOOSTASIOREMOTECLI_H_ + +#include "detail/boostasiolib.h" +#include "detail/genericasioremotecli.h" + +namespace cli { using BoostAsioCliTelnetServer = detail::CliGenericTelnetServer; } + +#endif // CLI_BOOSTASIOREMOTECLI_H_ + diff --git a/external/cli/boostasioscheduler.h b/external/cli/boostasioscheduler.h new file mode 100644 index 0000000..f4a872a --- /dev/null +++ b/external/cli/boostasioscheduler.h @@ -0,0 +1,38 @@ +/******************************************************************************* + * CLI - A simple command line interface. + * Copyright (C) 2016-2021 Daniele Pallastrelli + * + * Boost Software License - Version 1.0 - August 17th, 2003 + * + * Permission is hereby granted, free of charge, to any person or organization + * obtaining a copy of the software and accompanying documentation covered by + * this license (the "Software") to use, reproduce, display, distribute, + * execute, and transmit the Software, and to prepare derivative works of the + * Software, and to permit third-parties to whom the Software is furnished to + * do so, all subject to the following: + * + * The copyright notices in the Software and this entire statement, including + * the above license grant, this restriction and the following disclaimer, + * must be included in all copies of the Software, in whole or in part, and + * all derivative works of the Software, unless such copies or derivative + * works are solely in the form of machine-executable object code generated by + * a source language processor. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT + * SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE + * FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ******************************************************************************/ + +#ifndef CLI_BOOSTASIOSCHEDULER_H_ +#define CLI_BOOSTASIOSCHEDULER_H_ + +#include "detail/genericasioscheduler.h" +#include "detail/boostasiolib.h" + +namespace cli { using BoostAsioScheduler = detail::GenericAsioScheduler; } + +#endif // CLI_BOOSTASIOSCHEDULER_H_ diff --git a/external/cli/cli.h b/external/cli/cli.h new file mode 100644 index 0000000..8d61911 --- /dev/null +++ b/external/cli/cli.h @@ -0,0 +1,877 @@ +/******************************************************************************* + * CLI - A simple command line interface. + * Copyright (C) 2016-2021 Daniele Pallastrelli + * + * Boost Software License - Version 1.0 - August 17th, 2003 + * + * Permission is hereby granted, free of charge, to any person or organization + * obtaining a copy of the software and accompanying documentation covered by + * this license (the "Software") to use, reproduce, display, distribute, + * execute, and transmit the Software, and to prepare derivative works of the + * Software, and to permit third-parties to whom the Software is furnished to + * do so, all subject to the following: + * + * The copyright notices in the Software and this entire statement, including + * the above license grant, this restriction and the following disclaimer, + * must be included in all copies of the Software, in whole or in part, and + * all derivative works of the Software, unless such copies or derivative + * works are solely in the form of machine-executable object code generated by + * a source language processor. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT + * SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE + * FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ******************************************************************************/ + +#ifndef CLI_CLI_H +#define CLI_CLI_H + +#include +#include +#include +#include +#include +#include // std::isspace +#include +#include "colorprofile.h" +#include "detail/history.h" +#include "detail/split.h" +#include "detail/fromstring.h" +#include "historystorage.h" +#include "volatilehistorystorage.h" +#include +#include + +namespace cli +{ + + // ******************************************************************** + + template < typename T > struct TypeDesc { static const char* Name() { return ""; } }; + template <> struct TypeDesc< char > { static const char* Name() { return ""; } }; + template <> struct TypeDesc< unsigned char > { static const char* Name() { return ""; } }; + template <> struct TypeDesc< signed char > { static const char* Name() { return ""; } }; + template <> struct TypeDesc< short > { static const char* Name() { return ""; } }; + template <> struct TypeDesc< unsigned short > { static const char* Name() { return ""; } }; + template <> struct TypeDesc< int > { static const char* Name() { return ""; } }; + template <> struct TypeDesc< unsigned int > { static const char* Name() { return ""; } }; + template <> struct TypeDesc< long > { static const char* Name() { return ""; } }; + template <> struct TypeDesc< unsigned long > { static const char* Name() { return ""; } }; + template <> struct TypeDesc< long long > { static const char* Name() { return ""; } }; + template <> struct TypeDesc< unsigned long long > { static const char* Name() { return ""; } }; + template <> struct TypeDesc< float > { static const char* Name() { return ""; } }; + template <> struct TypeDesc< double > { static const char* Name() { return ""; } }; + template <> struct TypeDesc< long double > { static const char* Name() { return ""; } }; + template <> struct TypeDesc< bool > { static const char* Name() { return ""; } }; + template <> struct TypeDesc< std::string > { static const char* Name() { return ""; } }; + template <> struct TypeDesc< std::vector > { static const char* Name() { return ""; } }; + + // ******************************************************************** + + // this class provides a global output stream + class OutStream : public std::basic_ostream, public std::streambuf + { + public: + OutStream() : std::basic_ostream(this) + { + } + + // std::streambuf overrides + std::streamsize xsputn(const char* s, std::streamsize n) override + { + for (auto os: ostreams) + os->rdbuf()->sputn(s, n); + return n; + } + int overflow(int c) override + { + for (auto os: ostreams) + *os << static_cast(c); + return c; + } + + void Register(std::ostream& o) + { + ostreams.push_back(&o); + } + void UnRegister(std::ostream& o) + { + ostreams.erase(std::remove(ostreams.begin(), ostreams.end(), &o), ostreams.end()); + } + + private: + + std::vector ostreams; + }; + + // forward declarations + class Menu; + class CliSession; + + class Cli + { + + public: + ~Cli() = default; + // disable value semantics + Cli(const Cli&) = delete; + Cli& operator = (const Cli&) = delete; + // enable move semantics + Cli(Cli&&) = default; + Cli& operator = (Cli&&) = default; + + /** + * @brief Construct a new Cli object having a given root menu that contains the first level commands available. + * + * @param _rootMenu is the @c Menu containing the first level commands available to the user. + * @param historyStorage is the policy for the storage of the cli commands history. You must pass an instance of + * a class derived from @c HistoryStorage. The library provides these policies: + * - @c VolatileHistoryStorage + * - @c FileHistoryStorage it's a persistent history. I.e., the command history is preserved after your application + * is restarted. + * + * However, you can develop your own, just derive a class from @c HistoryStorage . + */ + Cli(std::unique_ptr _rootMenu, std::unique_ptr historyStorage = std::make_unique()) : + globalHistoryStorage(std::move(historyStorage)), + rootMenu(std::move(_rootMenu)), + enterAction{}, + exitAction{} + { + } + + /** + * @brief Add a global enter action that is called every time a session (local or remote) is established. + * + * @param action the function to be called when a session exits, taking a @c std::ostream& parameter to write on that session console. + */ + void EnterAction(const std::function< void(std::ostream&)>& action) { enterAction = action; } + + /** + * @brief Add a global exit action that is called every time a session (local or remote) gets the "exit" command. + * + * @param action the function to be called when a session exits, taking a @c std::ostream& parameter to write on that session console. + */ + void ExitAction(const std::function< void(std::ostream&)>& action) { exitAction = action; } + + /** + * @brief Add an handler that will be called when a @c std::exception (or derived) is thrown inside a command handler. + * If an exception handler is not set, the exception will be logget on the session output stream. + * + * @param handler the function to be called when an exception is thrown, taking a @c std::ostream& parameter to write on that session console + * and the exception thrown. + */ + void StdExceptionHandler(const std::function< void(std::ostream&, const std::string& cmd, const std::exception&) >& handler) + { + exceptionHandler = handler; + } + + /** + * @brief Get a global out stream object that can be used to print on every session currently connected (local and remote) + * + * @return OutStream& the reference to the global out stream writing on every session console. + */ + static OutStream& cout() + { + return *CoutPtr(); + } + + private: + friend class CliSession; + + static std::shared_ptr CoutPtr() + { + static std::shared_ptr s = std::make_shared(); + return s; + } + + Menu* RootMenu() { return rootMenu.get(); } + + void EnterAction(std::ostream& out) + { + if (enterAction) + enterAction(out); + } + + void ExitAction(std::ostream& out) + { + if (exitAction) + exitAction(out); + } + + void StdExceptionHandler(std::ostream& out, const std::string& cmd, const std::exception& e) + { + if (exceptionHandler) + exceptionHandler(out, cmd, e); + else + out << e.what() << '\n'; + } + + void StoreCommands(const std::vector& cmds) + { + globalHistoryStorage->Store(cmds); + } + + std::vector GetCommands() const + { + return globalHistoryStorage->Commands(); + } + + private: + std::unique_ptr globalHistoryStorage; + std::unique_ptr rootMenu; // just to keep it alive + std::function enterAction; + std::function exitAction; + std::function exceptionHandler; + }; + + // ******************************************************************** + + class Command + { + public: + explicit Command(std::string _name) : name(std::move(_name)), enabled(true) {} + virtual ~Command() noexcept = default; + + // disable copy and move semantics + Command(const Command&) = delete; + Command(Command&&) = delete; + Command& operator=(const Command&) = delete; + Command& operator=(Command&&) = delete; + + virtual void Enable() { enabled = true; } + virtual void Disable() { enabled = false; } + virtual bool Exec(const std::vector& cmdLine, CliSession& session) = 0; + virtual void Help(std::ostream& out) const = 0; + // Returns the collection of completions relatives to this command. + // For simple commands, provides a base implementation that use the name of the command + // for aggregate commands (i.e., Menu), the function is redefined to give the menu command + // and the subcommand recursively + virtual std::vector GetCompletionRecursive(const std::string& line) const + { + if (!enabled) return {}; + if (name.rfind(line, 0) == 0) return {name}; // name starts_with line + return {}; + } + protected: + const std::string& Name() const { return name; } + bool IsEnabled() const { return enabled; } + private: + const std::string name; + bool enabled; + }; + + // ******************************************************************** + + // free utility function to get completions from a list of commands and the current line + inline std::vector GetCompletions( + const std::shared_ptr>>& cmds, + const std::string& currentLine) + { + std::vector result; + std::for_each(cmds->begin(), cmds->end(), + [¤tLine,&result](const auto& cmd) + { + auto c = cmd->GetCompletionRecursive(currentLine); + result.insert( + result.end(), + std::make_move_iterator(c.begin()), + std::make_move_iterator(c.end()) + ); + } + ); + return result; + } + + // ******************************************************************** + + class CliSession + { + public: + CliSession(Cli& _cli, std::ostream& _out, std::size_t historySize = 100); + virtual ~CliSession() noexcept { coutPtr->UnRegister(out); } + + // disable value semantics + CliSession(const CliSession&) = delete; + CliSession& operator = (const CliSession&) = delete; + // disable move semantics + CliSession(CliSession&&) = delete; + CliSession& operator = (CliSession&&) = delete; + + void Feed(const std::string& cmd); + + void Prompt(); + + void Current(Menu* menu) { current = menu; } + + std::ostream& OutStream() { return out; } + + void Help() const; + + void Enter() + { + cli.EnterAction(out); + + if (enterAction) + enterAction(out); + } + + void Exit() + { + exitAction(out); + cli.ExitAction(out); + + auto cmds = history.GetCommands(); + cli.StoreCommands(cmds); + + exit = true; // prevent the prompt to be shown + } + + void EnterAction(const std::function& action) + { + enterAction = action; + } + + void ExitAction(const std::function& action) + { + exitAction = action; + } + + void ShowHistory() const { history.Show(out); } + + std::string PreviousCmd(const std::string& line) + { + return history.Previous(line); + } + + std::string NextCmd() + { + return history.Next(); + } + + std::vector GetCompletions(std::string currentLine) const; + + private: + + Cli& cli; + std::shared_ptr coutPtr; + Menu* current; + std::unique_ptr globalScopeMenu; + std::ostream& out; + std::function< void(std::ostream&)> enterAction = []( std::ostream& ) noexcept {}; + std::function< void(std::ostream&)> exitAction = []( std::ostream& ) noexcept {}; + detail::History history; + bool exit{ false }; // to prevent the prompt after exit command + }; + + // ******************************************************************** + + class CmdHandler + { + public: + using CmdVec = std::vector>; + CmdHandler() : descriptor(std::make_shared()) {} + CmdHandler(std::weak_ptr c, std::weak_ptr v) : + descriptor(std::make_shared(c, v)) + {} + void Enable() { if (descriptor) descriptor->Enable(); } + void Disable() { if (descriptor) descriptor->Disable(); } + void Remove() { if (descriptor) descriptor->Remove(); } + private: + struct Descriptor + { + Descriptor() = default; + Descriptor(std::weak_ptr c, std::weak_ptr v) : + cmd(std::move(c)), cmds(std::move(v)) + {} + void Enable() + { + if (auto c = cmd.lock()) + c->Enable(); + } + void Disable() + { + if(auto c = cmd.lock()) + c->Disable(); + } + void Remove() + { + auto scmd = cmd.lock(); + auto scmds = cmds.lock(); + if (scmd && scmds) + { + auto i = std::find_if( + scmds->begin(), + scmds->end(), + [&](const auto& c){ return c.get() == scmd.get(); } + ); + if (i != scmds->end()) + scmds->erase(i); + } + } + std::weak_ptr cmd; + std::weak_ptr cmds; + }; + std::shared_ptr descriptor; + }; + + // ******************************************************************** + + class Menu : public Command + { + public: + // disable value and move semantics + Menu(const Menu&) = delete; + Menu& operator = (const Menu&) = delete; + Menu(Menu&&) = delete; + Menu& operator = (Menu&&) = delete; + + Menu() : Command({}), parent(nullptr), description(), cmds(std::make_shared()) {} + + explicit Menu(const std::string& _name, std::string desc = "(menu)", const std::string& _prompt="") : + Command(_name), + parent(nullptr), + description(std::move(desc)), + prompt(_prompt.empty() ? _name : _prompt), + cmds(std::make_shared()) + {} + + template + CmdHandler Insert(const std::string& cmdName, R (*f)(std::ostream&, Args...), const std::string& help, const std::vector& parDesc={}); + + template + CmdHandler Insert(const std::string& cmdName, F f, const std::string& help = "", const std::vector& parDesc={}) + { + // dispatch to private Insert methods + return Insert(cmdName, help, parDesc, f, &F::operator()); + } + + template + CmdHandler Insert(const std::string& cmdName, const std::vector& parDesc, F f, const std::string& help = "") + { + // dispatch to private Insert methods + return Insert(cmdName, help, parDesc, f, &F::operator()); + } + + CmdHandler Insert(std::unique_ptr&& cmd) + { + std::shared_ptr scmd(std::move(cmd)); + CmdHandler c(scmd, cmds); + cmds->push_back(scmd); + return c; + } + + CmdHandler Insert(std::unique_ptr&& menu) + { + std::shared_ptr smenu(std::move(menu)); + CmdHandler c(smenu, cmds); + smenu->parent = this; + cmds->push_back(smenu); + return c; + } + + bool Exec(const std::vector& cmdLine, CliSession& session) override + { + if (!IsEnabled()) + return false; + if (cmdLine[0] == Name()) + { + if (cmdLine.size() == 1) + { + session.Current(this); + return true; + } + else + { + // check also for subcommands + std::vector subCmdLine(cmdLine.begin()+1, cmdLine.end()); + for (auto& cmd: *cmds) + if (cmd->Exec( subCmdLine, session )) return true; + } + } + return false; + } + + bool ScanCmds(const std::vector& cmdLine, CliSession& session) + { + if (!IsEnabled()) + return false; + for (auto& cmd: *cmds) + if (cmd->Exec(cmdLine, session)) + return true; + return (parent && parent->Exec(cmdLine, session)); + } + + std::string Prompt() const + { + return prompt; + } + + void MainHelp(std::ostream& out) + { + if (!IsEnabled()) return; + for (const auto& cmd: *cmds) + cmd->Help(out); + if (parent != nullptr) + parent->Help(out); + } + + void Help(std::ostream& out) const override + { + if (!IsEnabled()) return; + out << " - " << Name() << "\n\t" << description << "\n"; + } + + // returns: + // - the completions of this menu command + // - the recursive completions of subcommands + // - the recursive completions of parent menu + std::vector GetCompletions(const std::string& currentLine) const + { + auto result = cli::GetCompletions(cmds, currentLine); + if (parent != nullptr) + { + auto c = parent->GetCompletionRecursive(currentLine); + result.insert(result.end(), std::make_move_iterator(c.begin()), std::make_move_iterator(c.end())); + } + return result; + } + + // returns: + // - the completion of this menu command + // - the recursive completions of the subcommands + std::vector GetCompletionRecursive(const std::string& line) const override + { + if (line.rfind(Name(), 0) == 0) // line starts_with Name() + { + auto rest = line; + rest.erase(0, Name().size()); + // trim_left(rest); + rest.erase(rest.begin(), std::find_if(rest.begin(), rest.end(), [](int ch) { return !std::isspace(ch); })); + std::vector result; + for (const auto& cmd: *cmds) + { + auto cs = cmd->GetCompletionRecursive(rest); + for (const auto& c: cs) + result.push_back(Name() + ' ' + c); // concat submenu with command + } + return result; + } + return Command::GetCompletionRecursive(line); + } + + private: + + template + CmdHandler Insert(const std::string& name, const std::string& help, const std::vector& parDesc, F& f, R (F::*)(std::ostream& out, Args...) const); + + template + CmdHandler Insert(const std::string& name, const std::string& help, const std::vector& parDesc, F& f, R (F::*)(std::ostream& out, const std::vector&) const); + + template + CmdHandler Insert(const std::string& name, const std::string& help, const std::vector& parDesc, F& f, R (F::*)(std::ostream& out, std::vector) const); + + Menu* parent{ nullptr }; + const std::string description; + const std::string prompt; + // using shared_ptr instead of unique_ptr to get a weak_ptr + // for the CmdHandler::Descriptor + using Cmds = std::vector>; + std::shared_ptr cmds; + }; + + // ******************************************************************** + + template + struct Select; + + template + struct Select + { + template + static void Exec(const F& f, InputIt first, InputIt last) + { + assert( first != last ); + assert( std::distance(first, last) == 1+sizeof...(Args) ); + const P p = detail::from_string::type>(*first); + auto g = [&](auto ... pars){ f(p, pars...); }; + Select::Exec(g, std::next(first), last); + } + }; + + template <> + struct Select<> + { + template + static void Exec(const F& f, InputIt first, InputIt last) + { + // silence the unused warning in release mode when assert is disabled + static_cast(first); + static_cast(last); + + assert(first == last); + + f(); + } + }; + + template + struct PrintDesc; + + template + struct PrintDesc + { + static void Dump(std::ostream& out) + { + out << " " << TypeDesc< typename std::decay

::type >::Name(); + PrintDesc::Dump(out); + } + }; + + template <> + struct PrintDesc<> + { + static void Dump(std::ostream& /*out*/) {} + }; + + // ******************************************* + + template + class VariadicFunctionCommand : public Command + { + public: + // disable value semantics + VariadicFunctionCommand(const VariadicFunctionCommand&) = delete; + VariadicFunctionCommand& operator = (const VariadicFunctionCommand&) = delete; + + VariadicFunctionCommand( + const std::string& _name, + F fun, + std::string desc, + std::vector parDesc + ) + : Command(_name), func(std::move(fun)), description(std::move(desc)), parameterDesc(std::move(parDesc)) + { + } + + bool Exec(const std::vector< std::string >& cmdLine, CliSession& session) override + { + if (!IsEnabled()) return false; + const std::size_t paramSize = sizeof...(Args); + if (cmdLine.size() != paramSize+1) return false; + if (Name() == cmdLine[0]) + { + try + { + auto g = [&](auto ... pars){ func( session.OutStream(), pars... ); }; + Select::Exec(g, std::next(cmdLine.begin()), cmdLine.end()); + } + catch (std::bad_cast&) + { + return false; + } + return true; + } + return false; + } + + void Help(std::ostream& out) const override + { + if (!IsEnabled()) return; + out << " - " << Name(); + if (parameterDesc.empty()) + PrintDesc::Dump(out); + for (auto& s: parameterDesc) + out << " <" << s << '>'; + out << "\n\t" << description << "\n"; + } + + private: + + const F func; + const std::string description; + const std::vector parameterDesc; + }; + + + template + class FreeformCommand : public Command + { + public: + // disable value semantics + FreeformCommand(const FreeformCommand&) = delete; + FreeformCommand& operator = (const FreeformCommand&) = delete; + + FreeformCommand( + const std::string& _name, + F fun, + std::string desc, + std::vector parDesc + ) + : Command(_name), func(std::move(fun)), description(std::move(desc)), parameterDesc(std::move(parDesc)) + { + } + + bool Exec(const std::vector< std::string >& cmdLine, CliSession& session) override + { + if (!IsEnabled()) return false; + assert(!cmdLine.empty()); + if (Name() == cmdLine[0]) + { + func(session.OutStream(), std::vector(std::next(cmdLine.begin()), cmdLine.end())); + return true; + } + return false; + } + void Help(std::ostream& out) const override + { + if (!IsEnabled()) return; + out << " - " << Name(); + if (parameterDesc.empty()) + PrintDesc>::Dump(out); + for (auto& s: parameterDesc) + out << " <" << s << '>'; + out << "\n\t" << description << "\n"; + } + + private: + + const F func; + const std::string description; + const std::vector parameterDesc; + }; + + + // ******************************************************************** + + // CliSession implementation + + inline CliSession::CliSession(Cli& _cli, std::ostream& _out, std::size_t historySize) : + cli(_cli), + coutPtr(Cli::CoutPtr()), + current(cli.RootMenu()), + globalScopeMenu(std::make_unique< Menu >()), + out(_out), + history(historySize) + { + history.LoadCommands(cli.GetCommands()); + + coutPtr->Register(out); + globalScopeMenu->Insert( + "help", + [this](std::ostream&){ Help(); }, + "This help message" + ); + globalScopeMenu->Insert( + "exit", + [this](std::ostream&){ Exit(); }, + "Quit the session" + ); +#ifdef CLI_HISTORY_CMD + globalScopeMenu->Insert( + "history", + [this](std::ostream&){ ShowHistory(); }, + "Show the history" + ); +#endif + } + + inline void CliSession::Feed(const std::string& cmd) + { + std::vector strs; + detail::split(strs, cmd); + if (strs.empty()) return; // just hit enter + + history.NewCommand(cmd); // add anyway to history + + try + { + + // global cmds check + bool found = globalScopeMenu->ScanCmds(strs, *this); + + // root menu recursive cmds check + if (!found) found = current->ScanCmds(strs, *this); + + if (!found) // error msg if not found + out << "wrong command: " << cmd << '\n'; + } + catch(const std::exception& e) + { + cli.StdExceptionHandler(out, cmd, e); + } + catch(...) + { + out << "Cli. Unknown exception caught handling command line \"" + << cmd + << "\"\n"; + } + } + + inline void CliSession::Prompt() + { + if (exit) return; + out << beforePrompt + << current->Prompt() + << afterPrompt + << "> " + << std::flush; + } + + inline void CliSession::Help() const + { + out << "Commands available:\n"; + globalScopeMenu->MainHelp(out); + current -> MainHelp( out ); + } + + inline std::vector CliSession::GetCompletions(std::string currentLine) const + { + // trim_left(currentLine); + currentLine.erase(currentLine.begin(), std::find_if(currentLine.begin(), currentLine.end(), [](int ch) { return !std::isspace(ch); })); + auto v1 = globalScopeMenu->GetCompletions(currentLine); + auto v3 = current->GetCompletions(currentLine); + v1.insert(v1.end(), std::make_move_iterator(v3.begin()), std::make_move_iterator(v3.end())); + + // removes duplicates (std::unique requires a sorted container) + std::sort(v1.begin(), v1.end()); + auto ip = std::unique(v1.begin(), v1.end()); + v1.resize(static_cast(std::distance(v1.begin(), ip))); + + return v1; + } + + // Menu implementation + + template + CmdHandler Menu::Insert(const std::string& cmdName, R (*f)(std::ostream&, Args...), const std::string& help, const std::vector& parDesc) + { + using F = R (*)(std::ostream&, Args...); + return Insert(std::make_unique>(cmdName, f, help, parDesc)); + } + + template + CmdHandler Menu::Insert(const std::string& cmdName, const std::string& help, const std::vector& parDesc, F& f, R (F::*)(std::ostream& out, Args...) const ) + { + return Insert(std::make_unique>(cmdName, f, help, parDesc)); + } + + template + CmdHandler Menu::Insert(const std::string& cmdName, const std::string& help, const std::vector& parDesc, F& f, R (F::*)(std::ostream& out, const std::vector& args) const ) + { + return Insert(std::make_unique>(cmdName, f, help, parDesc)); + } + + template + CmdHandler Menu::Insert(const std::string& cmdName, const std::string& help, const std::vector& parDesc, F& f, R (F::*)(std::ostream& out, std::vector args) const ) + { + return Insert(std::make_unique>(cmdName, f, help, parDesc)); + } + +} // namespace cli + +#endif // CLI_CLI_H diff --git a/external/cli/clifilesession.h b/external/cli/clifilesession.h new file mode 100644 index 0000000..68177e5 --- /dev/null +++ b/external/cli/clifilesession.h @@ -0,0 +1,85 @@ +/******************************************************************************* + * CLI - A simple command line interface. + * Copyright (C) 2016-2021 Daniele Pallastrelli + * + * Boost Software License - Version 1.0 - August 17th, 2003 + * + * Permission is hereby granted, free of charge, to any person or organization + * obtaining a copy of the software and accompanying documentation covered by + * this license (the "Software") to use, reproduce, display, distribute, + * execute, and transmit the Software, and to prepare derivative works of the + * Software, and to permit third-parties to whom the Software is furnished to + * do so, all subject to the following: + * + * The copyright notices in the Software and this entire statement, including + * the above license grant, this restriction and the following disclaimer, + * must be included in all copies of the Software, in whole or in part, and + * all derivative works of the Software, unless such copies or derivative + * works are solely in the form of machine-executable object code generated by + * a source language processor. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT + * SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE + * FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ******************************************************************************/ + +#ifndef CLI_CLIFILESESSION_H +#define CLI_CLIFILESESSION_H + +#include +#include +#include // std::invalid_argument +#include "cli.h" // CliSession + +namespace cli +{ + +class CliFileSession : public CliSession +{ +public: + /// @throw std::invalid_argument if @c _in or @c out are invalid streams + explicit CliFileSession(Cli& _cli, std::istream& _in=std::cin, std::ostream& _out=std::cout) : + CliSession(_cli, _out, 1), + exit(false), + in(_in) + { + if (!_in.good()) throw std::invalid_argument("istream invalid"); + if (!_out.good()) throw std::invalid_argument("ostream invalid"); + ExitAction( + [this](std::ostream&) noexcept + { + exit = true; + } + ); + } + void Start() + { + Enter(); + + while(!exit) + { + Prompt(); + std::string line; + if (!in.good()) + Exit(); + std::getline(in, line); + if (in.eof()) + Exit(); + else + Feed(line); + } + } + +private: + bool exit; + std::istream& in; +}; + +} // namespace cli + +#endif // CLI_CLIFILESESSION_H + diff --git a/external/cli/clilocalsession.h b/external/cli/clilocalsession.h new file mode 100644 index 0000000..5528350 --- /dev/null +++ b/external/cli/clilocalsession.h @@ -0,0 +1,81 @@ +/******************************************************************************* + * CLI - A simple command line interface. + * Copyright (C) 2016-2021 Daniele Pallastrelli + * + * Boost Software License - Version 1.0 - August 17th, 2003 + * + * Permission is hereby granted, free of charge, to any person or organization + * obtaining a copy of the software and accompanying documentation covered by + * this license (the "Software") to use, reproduce, display, distribute, + * execute, and transmit the Software, and to prepare derivative works of the + * Software, and to permit third-parties to whom the Software is furnished to + * do so, all subject to the following: + * + * The copyright notices in the Software and this entire statement, including + * the above license grant, this restriction and the following disclaimer, + * must be included in all copies of the Software, in whole or in part, and + * all derivative works of the Software, unless such copies or derivative + * works are solely in the form of machine-executable object code generated by + * a source language processor. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT + * SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE + * FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ******************************************************************************/ + +#ifndef CLI_CLILOCALSESSION_H +#define CLI_CLILOCALSESSION_H + +#include // std::ostream +#include "detail/inputhandler.h" +#include "cli.h" // CliSession +#include "detail/keyboard.h" + +namespace cli +{ + +class Scheduler; // forward declaration + +/** + * @brief CliLocalTerminalSession represents a local session. + * You should instantiate it to start an interactive prompt on the standard + * input/output of your application. + * The handlers of the commands will be invoked in the same thread the @c Scheduler runs. + */ +class CliLocalTerminalSession : public CliSession +{ +public: + + /** + * @brief Construct a new Cli Local Terminal Session object that uses the specified @c std::ostream + * for output. You can also specify a size for the command history. + * + * @param _cli The cli object that defines the menu hierarchy for this session + * @param scheduler The scheduler that will process the command handlers + * @param _out the output stream where command output will be printed + * @param historySize the size of the command history + */ + CliLocalTerminalSession(Cli& _cli, Scheduler& scheduler, std::ostream& _out, std::size_t historySize = 100) : + CliSession(_cli, _out, historySize), + kb(scheduler), + ih(*this, kb) + { + Enter(); + Prompt(); + } + +private: + detail::Keyboard kb; + detail::InputHandler ih; +}; + +using CliLocalSession = CliLocalTerminalSession; + +} // namespace cli + +#endif // CLI_CLILOCALSESSION_H + diff --git a/external/cli/colorprofile.h b/external/cli/colorprofile.h new file mode 100644 index 0000000..518ce57 --- /dev/null +++ b/external/cli/colorprofile.h @@ -0,0 +1,76 @@ +/******************************************************************************* + * CLI - A simple command line interface. + * Copyright (C) 2016-2021 Daniele Pallastrelli + * + * Boost Software License - Version 1.0 - August 17th, 2003 + * + * Permission is hereby granted, free of charge, to any person or organization + * obtaining a copy of the software and accompanying documentation covered by + * this license (the "Software") to use, reproduce, display, distribute, + * execute, and transmit the Software, and to prepare derivative works of the + * Software, and to permit third-parties to whom the Software is furnished to + * do so, all subject to the following: + * + * The copyright notices in the Software and this entire statement, including + * the above license grant, this restriction and the following disclaimer, + * must be included in all copies of the Software, in whole or in part, and + * all derivative works of the Software, unless such copies or derivative + * works are solely in the form of machine-executable object code generated by + * a source language processor. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT + * SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE + * FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ******************************************************************************/ + +#ifndef CLI_COLORPROFILE_H_ +#define CLI_COLORPROFILE_H_ + +#include "detail/rang.h" + +namespace cli +{ + +inline bool& Color() { static bool color; return color; } + +inline void SetColor() { Color() = true; } +inline void SetNoColor() { Color() = false; } + +enum BeforePrompt { beforePrompt }; +enum AfterPrompt { afterPrompt }; +enum BeforeInput { beforeInput }; +enum AfterInput { afterInput }; + +inline std::ostream& operator<<(std::ostream& os, BeforePrompt) +{ + if ( Color() ) { os << detail::rang::control::forceColor << detail::rang::fg::green << detail::rang::style::bold; } + return os; +} + +inline std::ostream& operator<<(std::ostream& os, AfterPrompt) +{ + os << detail::rang::style::reset; + return os; +} + +inline std::ostream& operator<<(std::ostream& os, BeforeInput) +{ + if ( Color() ) { os << detail::rang::control::forceColor << detail::rang::fgB::gray; } + return os; +} + +inline std::ostream& operator<<(std::ostream& os, AfterInput) +{ + os << detail::rang::style::reset; + return os; +} + +} // namespace cli + +#endif // CLI_COLORPROFILE_H_ + + diff --git a/external/cli/detail/boostasiolib.h b/external/cli/detail/boostasiolib.h new file mode 100644 index 0000000..a82c53c --- /dev/null +++ b/external/cli/detail/boostasiolib.h @@ -0,0 +1,49 @@ +/******************************************************************************* + * CLI - A simple command line interface. + * Copyright (C) 2016-2021 Daniele Pallastrelli + * + * Boost Software License - Version 1.0 - August 17th, 2003 + * + * Permission is hereby granted, free of charge, to any person or organization + * obtaining a copy of the software and accompanying documentation covered by + * this license (the "Software") to use, reproduce, display, distribute, + * execute, and transmit the Software, and to prepare derivative works of the + * Software, and to permit third-parties to whom the Software is furnished to + * do so, all subject to the following: + * + * The copyright notices in the Software and this entire statement, including + * the above license grant, this restriction and the following disclaimer, + * must be included in all copies of the Software, in whole or in part, and + * all derivative works of the Software, unless such copies or derivative + * works are solely in the form of machine-executable object code generated by + * a source language processor. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT + * SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE + * FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ******************************************************************************/ + +#ifndef CLI_DETAIL_BOOSTASIOLIB_H_ +#define CLI_DETAIL_BOOSTASIOLIB_H_ + +/** + * This header file provides the class `cli::BoostAsioLib`, using the right + * implementation according to the version of boost libraries included. + */ + +#include + +#if BOOST_VERSION < 106600 + #include "oldboostasiolib.h" + namespace cli { namespace detail { using BoostAsioLib = OldBoostAsioLib; } } +#else + #include "newboostasiolib.h" + namespace cli { namespace detail { using BoostAsioLib = NewBoostAsioLib; } } +#endif + +#endif // CLI_DETAIL_BOOSTASIOLIB_H_ + diff --git a/external/cli/detail/commonprefix.h b/external/cli/detail/commonprefix.h new file mode 100644 index 0000000..626e089 --- /dev/null +++ b/external/cli/detail/commonprefix.h @@ -0,0 +1,70 @@ +/******************************************************************************* + * CLI - A simple command line interface. + * Copyright (C) 2016-2021 Daniele Pallastrelli + * + * Boost Software License - Version 1.0 - August 17th, 2003 + * + * Permission is hereby granted, free of charge, to any person or organization + * obtaining a copy of the software and accompanying documentation covered by + * this license (the "Software") to use, reproduce, display, distribute, + * execute, and transmit the Software, and to prepare derivative works of the + * Software, and to permit third-parties to whom the Software is furnished to + * do so, all subject to the following: + * + * The copyright notices in the Software and this entire statement, including + * the above license grant, this restriction and the following disclaimer, + * must be included in all copies of the Software, in whole or in part, and + * all derivative works of the Software, unless such copies or derivative + * works are solely in the form of machine-executable object code generated by + * a source language processor. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT + * SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE + * FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ******************************************************************************/ + +#ifndef CLI_DETAIL_COMMONPREFIX_H_ +#define CLI_DETAIL_COMMONPREFIX_H_ + +#include +#include +#include +#include + +namespace cli +{ +namespace detail +{ + +inline std::string CommonPrefix(const std::vector& v) +{ + assert(!v.empty()); + std::string prefix; + + // find the shorter string + auto smin = std::min_element(v.begin(), v.end(), + [] (const std::string& s1, const std::string& s2) + { + return s1.size() < s2.size(); + }); + + for (std::size_t i = 0; i < smin->size(); ++i) + { + // check if i-th element is equal in each input string + const char c = (*smin)[i]; + for (auto& x: v) + if (x[i] != c) return prefix; + prefix += c; + } + + return prefix; +} + +} // namespace detail +} // namespace cli + +#endif // CLI_DETAIL_COMMONPREFIX_H_ diff --git a/external/cli/detail/fromstring.h b/external/cli/detail/fromstring.h new file mode 100644 index 0000000..e66fd85 --- /dev/null +++ b/external/cli/detail/fromstring.h @@ -0,0 +1,282 @@ +/******************************************************************************* + * CLI - A simple command line interface. + * Copyright (C) 2016-2021 Daniele Pallastrelli + * + * Boost Software License - Version 1.0 - August 17th, 2003 + * + * Permission is hereby granted, free of charge, to any person or organization + * obtaining a copy of the software and accompanying documentation covered by + * this license (the "Software") to use, reproduce, display, distribute, + * execute, and transmit the Software, and to prepare derivative works of the + * Software, and to permit third-parties to whom the Software is furnished to + * do so, all subject to the following: + * + * The copyright notices in the Software and this entire statement, including + * the above license grant, this restriction and the following disclaimer, + * must be included in all copies of the Software, in whole or in part, and + * all derivative works of the Software, unless such copies or derivative + * works are solely in the form of machine-executable object code generated by + * a source language processor. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT + * SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE + * FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ******************************************************************************/ + +#ifndef CLI_DETAIL_FROMSTRING_H_ +#define CLI_DETAIL_FROMSTRING_H_ + +// #define CLI_FROMSTRING_USE_BOOST + +#ifdef CLI_FROMSTRING_USE_BOOST + +#include + +namespace cli +{ +namespace detail +{ + +template +inline +T from_string(const std::string& s) +{ + return boost::lexical_cast(s); +} + +} // namespace detail +} // namespace cli + +#else + +#include +#include +#include +#include + +namespace cli +{ + + namespace detail + { + class bad_conversion : public std::bad_cast + { + public: + const char* what() const noexcept override { + return "bad from_string conversion: " + "source string value could not be interpreted as target"; + } + }; + +template +inline T from_string(const std::string& s); + +template <> +inline std::string from_string(const std::string& s) +{ + return s; +} + +template <> +inline std::nullptr_t from_string(const std::string& /*s*/) +{ + return nullptr; +} + +namespace detail +{ + +template +inline T unsigned_digits_from_string(const std::string& s) +{ + if (s.empty()) + throw bad_conversion(); + T result = 0; + for (char c: s) + { + if (!std::isdigit(c)) + throw bad_conversion(); + const T digit = static_cast( c - '0' ); + const T tmp = (result * 10) + digit; + if (result != ((tmp-digit)/10) || (tmp < result)) + throw bad_conversion(); + result = tmp; + } + return result; +} + +template +inline T unsigned_from_string(std::string s) +{ + if (s.empty()) + throw bad_conversion(); + if (s[0] == '+') + { + s = s.substr(1); + } + return unsigned_digits_from_string(s); +} + +template +inline T signed_from_string(std::string s) +{ + if (s.empty()) + throw bad_conversion(); + using U = std::make_unsigned_t; + if (s[0] == '-') + { + s = s.substr(1); + const U val = unsigned_digits_from_string(s); + if ( val > static_cast( - std::numeric_limits::min() ) ) + throw bad_conversion(); + return (- static_cast(val)); + } + else if (s[0] == '+') + { + s = s.substr(1); + } + const U val = unsigned_digits_from_string(s); + if (val > static_cast( std::numeric_limits::max() )) + throw bad_conversion(); + return static_cast(val); +} + +} // namespace detail + +// signed + +template <> inline signed char +from_string(const std::string& s) { return detail::signed_from_string(s); } + +template <> inline short int +from_string(const std::string& s) { return detail::signed_from_string(s); } + +template <> inline int +from_string(const std::string& s) { return detail::signed_from_string(s); } + +template <> inline long int +from_string(const std::string& s) { return detail::signed_from_string(s); } + +template <> inline long long int +from_string(const std::string& s) { return detail::signed_from_string(s); } + +// unsigned + +template <> inline unsigned char +from_string(const std::string& s) { return detail::unsigned_from_string(s); } + +template <> inline unsigned short int +from_string(const std::string& s) { return detail::unsigned_from_string(s); } + +template <> inline unsigned int +from_string(const std::string& s) { return detail::unsigned_from_string(s); } + +template <> inline unsigned long int +from_string(const std::string& s) { return detail::unsigned_from_string(s); } + +template <> inline unsigned long long int +from_string(const std::string& s) { return detail::unsigned_from_string(s); } + +// bool + +template <> +inline bool from_string(const std::string& s) +{ + if (s == "true") return true; + else if (s == "false") return false; + const auto value = detail::signed_from_string(s); + if (value == 1) return true; + else if (value == 0) return false; + throw bad_conversion(); +} + +// chars + +template <> +inline char from_string(const std::string& s) +{ + if (s.size() != 1) throw bad_conversion(); + return s[0]; +} + +// floating points + +template <> +inline float from_string(const std::string& s) +{ + if ( std::any_of(s.begin(), s.end(), [](char c){return std::isspace(c);} ) ) + throw bad_conversion(); + std::string::size_type sz; + float result = {}; + try { + result = std::stof(s, &sz); + } catch (const std::exception&) { + throw bad_conversion(); + } + if (sz != s.size()) + throw bad_conversion(); + return result; +} + +template <> +inline double from_string(const std::string& s) +{ + if ( std::any_of(s.begin(), s.end(), [](char c){return std::isspace(c);} ) ) + throw bad_conversion(); + std::string::size_type sz; + double result = {}; + try { + result = std::stod(s, &sz); + } catch (const std::exception&) { + throw bad_conversion(); + } + if (sz != s.size()) + throw bad_conversion(); + return result; +} + +template <> +inline long double from_string(const std::string& s) +{ + if ( std::any_of(s.begin(), s.end(), [](char c){return std::isspace(c);} ) ) + throw bad_conversion(); + std::string::size_type sz; + long double result = {}; + try { + result = std::stold(s, &sz); + } catch (const std::exception&) { + throw bad_conversion(); + } + if (sz != s.size()) + throw bad_conversion(); + return result; +} + +// fallback: operator << + +template +inline T from_string(const std::string& s) +{ + std::stringstream interpreter; + T result; + + if(!(interpreter << s) || + !(interpreter >> result) || + !(interpreter >> std::ws).eof()) + throw bad_conversion(); + + return result; +} + + } // namespace detail + +} // namespace cli + + +#endif // CLI_FROMSTRING_USE_BOOST + +#endif // CLI_DETAIL_FROMSTRING_H_ diff --git a/external/cli/detail/genericasioremotecli.h b/external/cli/detail/genericasioremotecli.h new file mode 100644 index 0000000..576520a --- /dev/null +++ b/external/cli/detail/genericasioremotecli.h @@ -0,0 +1,590 @@ +/******************************************************************************* + * CLI - A simple command line interface. + * Copyright (C) 2016-2021 Daniele Pallastrelli + * + * Boost Software License - Version 1.0 - August 17th, 2003 + * + * Permission is hereby granted, free of charge, to any person or organization + * obtaining a copy of the software and accompanying documentation covered by + * this license (the "Software") to use, reproduce, display, distribute, + * execute, and transmit the Software, and to prepare derivative works of the + * Software, and to permit third-parties to whom the Software is furnished to + * do so, all subject to the following: + * + * The copyright notices in the Software and this entire statement, including + * the above license grant, this restriction and the following disclaimer, + * must be included in all copies of the Software, in whole or in part, and + * all derivative works of the Software, unless such copies or derivative + * works are solely in the form of machine-executable object code generated by + * a source language processor. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT + * SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE + * FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ******************************************************************************/ + +#ifndef CLI_DETAIL_GENERICASIOREMOTECLI_H_ +#define CLI_DETAIL_GENERICASIOREMOTECLI_H_ + +#include +#include "../cli.h" +#include "inputhandler.h" +#include "server.h" +#include "inputdevice.h" +#include "genericasioscheduler.h" + +namespace cli +{ +namespace detail +{ + +// ******************************************************************************* + +class TelnetSession : public Session +{ +public: + explicit TelnetSession(asiolib::ip::tcp::socket _socket) : + Session(std::move(_socket)) + {} + +protected: + + std::string Encode(const std::string& _data) const override + { + std::string result; + for (char c: _data) + { + if (c == '\n') result += '\r'; + result += c; + } + return result; + } + + void OnConnect() override + { + // to specify hexadecimal value as chars we use + // the syntax \xVVV + // and the std::string ctor that takes the size, + // so that it's not null-terminated + //std::string msg{ "\x0FF\x0FD\x027", 3 }; + //waitAck = true; + //std::string iacDoSuppressGoAhead{ "\x0FF\x0FD\x003", 3 }; + //this -> OutStream() << iacDoSuppressGoAhead << std::flush; + + // https://www.ibm.com/support/knowledgecenter/SSLTBW_1.13.0/com.ibm.zos.r13.hald001/telcmds.htm + + static const std::string iacDoLineMode{ "\x0FF\x0FD\x022", 3 }; + this -> OutStream() << iacDoLineMode << std::flush; + + static const std::string iacSbLineMode0IacSe{ "\x0FF\x0FA\x022\x001\x000\x0FF\x0F0", 7 }; + this -> OutStream() << iacSbLineMode0IacSe << std::flush; + + static const std::string iacWillEcho{ "\x0FF\x0FB\x001", 3 }; + this -> OutStream() << iacWillEcho << std::flush; + +/* + constexpr char IAC = '\x0FF'; // 255 + constexpr char DO = '\x0FD'; // 253 + constexpr char VT100 = '\x030'; // 48 + + this -> OutStream() << IAC << DO << VT100 << std::flush; +*/ + //cliSession.Prompt(); + } + void OnDisconnect() override {} + void OnError() override {} +#if 0 + void OnDataReceived(const std::string& data) override + { + if (waitAck) + { + if ( data[0] == '\x0FF' ) + { + // TODO + for (size_t i = 0; i < data.size(); ++i) + std::cout << static_cast( data[i] & 0xFF ) << ' '; + std::cout << std::endl; + + for (size_t i = 0; i < data.size(); ++i) + std::cout << "0x" << std::hex << static_cast( data[i] & 0xFF ) << std::dec << ' '; + std::cout << std::endl; + + for (size_t i = 0; i < data.size(); ++i) + switch (static_cast( data[i] & 0xFF )) + { + case 0xFF: std::cout << "IAC "; break; + case 0xFE: std::cout << "DONT "; break; + case 0xFD: std::cout << "DO "; break; + case 0xFC: std::cout << "WONT "; break; + case 0xFB: std::cout << "WILL "; break; + case 0xFA: std::cout << "SB "; break; + case 0xF9: std::cout << "GoAhead "; break; + case 0xF8: std::cout << "EraseLine "; break; + case 0xF7: std::cout << "EraseCharacter "; break; + case 0xF6: std::cout << "AreYouThere "; break; + case 0xF5: std::cout << "AbortOutput "; break; + case 0xF4: std::cout << "InterruptProcess "; break; + case 0xF3: std::cout << "Break "; break; + case 0xF2: std::cout << "DataMark "; break; + case 0xF1: std::cout << "NOP "; break; + case 0xF0: std::cout << "SE "; break; + default: std::cout << (static_cast( data[i] & 0xFF )) << ' '; + } + std::cout << std::endl; + + } + waitAck = false; +/* + std::string iacWillSuppressGoAhead{ "\x0FF\x0FB\x003", 3 }; + if ( data == iacWillSuppressGoAhead ) + { + waitAck = false; + cliSession.Prompt(); + } + + else + Disconnect(); +*/ + } + else + { + for (size_t i = 0; i < data.size(); ++i) + Feed(data[i]); +/* + auto str = data; + // trim trailing spaces + std::size_t endpos = str.find_last_not_of(" \t\r\n"); + if( std::string::npos != endpos ) str = str.substr( 0, endpos+1 ); + + if ( cliSession.Feed( str ) ) cliSession.Prompt(); + else Disconnect(); +*/ + } + } +#else + + /* + See + https://www.iana.org/assignments/telnet-options/telnet-options.xhtml + for a list of telnet options + */ + + enum + { + SE = '\x0F0', // End of subnegotiation parameters. + NOP = '\x0F1', // No operation. + DataMark = '\x0F2', // The data stream portion of a Synch. + // This should always be accompanied + // by a TCP Urgent notification. + Break = '\x0F3', // NVT character BRK. + InterruptProcess = '\x0F4', // The function IP. + AbortOutput = '\x0F5', // The function AO. + AreYouThere = '\x0F6', // The function AYT. + EraseCharacter = '\x0F7', // The function EC. + EraseLine = '\x0F8', // The function EL. + GoAhead = '\x0F9', // The GA signal. + SB = '\x0FA', // Indicates that what follows is + // subnegotiation of the indicated + // option. + WILL = '\x0FB', // Indicates the desire to begin + // performing, or confirmation that + // you are now performing, the + // indicated option. + WONT = '\x0FC', // Indicates the refusal to perform, + // or continue performing, the + // indicated option. + DO = '\x0FD', // Indicates the request that the + // other party perform, or + // confirmation that you are expecting + // the other party to perform, the + // indicated option. + DONT = '\x0FE', // Indicates the demand that the + // other party stop performing, + // or confirmation that you are no + // longer expecting the other party + // to perform, the indicated option. + IAC = '\x0FF', // Data Byte 255. + + _ECHO = '\x001', + SUPPRESS_GO_AHEAD = '\x003', + TERMINAL_TYPE = '\x018', + NEGOTIATE_ABOUT_WIN_SIZE = '\x01F', + TERMINAL_SPEED = '\x020', + NEW_ENV_OPTION = '\x027' + }; + + void OnDataReceived(const std::string& _data) override + { + for (auto c: _data) + Consume(c); + } + +private: + + void Consume(signed char c) + { + if (escape) + { + if (c == IAC) + Data(c); + else + Command(c); + escape = false; + } + else + { + if (c == IAC) + escape = true; + else + Data(c); + } + } + + void Data(signed char c) + { + switch(state) + { + case State::data: + Output(c); + break; + case State::sub: + RxSub(c); + break; + case State::wait_will: + RxWill(c); + state = State::data; + break; + case State::wait_wont: + RxWont(c); + state = State::data; + break; + case State::wait_do: + RxDo(c); + state = State::data; + break; + case State::wait_dont: + RxDont(c); + state = State::data; + break; + } + } + + void Command(char c) + { +/* + switch (static_cast( c & 0xFF )) + { + case 0xFF: std::cout << "IAC" << std::endl; break; + case 0xFE: std::cout << "DONT" << std::endl; break; + case 0xFD: std::cout << "DO" << std::endl; break; + case 0xFC: std::cout << "WONT" << std::endl; break; + case 0xFB: std::cout << "WILL" << std::endl; break; + case 0xFA: std::cout << "SB" << std::endl; break; + case 0xF9: std::cout << "GoAhead" << std::endl; break; + case 0xF8: std::cout << "EraseLine" << std::endl; break; + case 0xF7: std::cout << "EraseCharacter" << std::endl; break; + case 0xF6: std::cout << "AreYouThere" << std::endl; break; + case 0xF5: std::cout << "AbortOutput" << std::endl; break; + case 0xF4: std::cout << "InterruptProcess" << std::endl; break; + case 0xF3: std::cout << "Break" << std::endl; break; + case 0xF2: std::cout << "DataMark" << std::endl; break; + case 0xF1: std::cout << "NOP" << std::endl; break; + case 0xF0: std::cout << "SE" << std::endl; break; + default: std::cout << (static_cast( c & 0xFF )) << std::endl;; + } +*/ + switch(c) + { + case SE: + if (state == State::sub) + state = State::data; + else + std::cerr << "ERROR: received SE when not in sub state\n"; + break; + case DataMark: // ? + case Break: // ? + case InterruptProcess: + case AbortOutput: + case AreYouThere: + case EraseCharacter: + case EraseLine: + case GoAhead: + case NOP: + state = State::data; + break; + case SB: + if (state != State::sub) + state = State::sub; + else + std::cout << "ERROR: received SB when already in sub state" << std::endl; + break; + case WILL: + state = State::wait_will; + break; + case WONT: + state = State::wait_wont; + break; + case DO: + state = State::wait_do; + break; + case DONT: + state = State::wait_dont; + break; + case IAC: + assert(false); // can't be here + state = State::data; + break; + } + } + + void RxWill(char c) + { + #ifdef CLI_TELNET_TRACE + std::cout << "will " << static_cast(c) << std::endl; + #endif + switch(c) + { + case SUPPRESS_GO_AHEAD: + SendIacCmd(WILL, SUPPRESS_GO_AHEAD); + break; + case NEGOTIATE_ABOUT_WIN_SIZE: + SendIacCmd(DO, NEGOTIATE_ABOUT_WIN_SIZE); + break; + default: + SendIacCmd(DONT, c); + }; + } + void RxWont(char c) + { + #ifdef CLI_TELNET_TRACE + std::cout << "wont " << static_cast(c) << std::endl; + #else + (void)c; + #endif + } + void RxDo(char c) + { + #ifdef CLI_TELNET_TRACE + std::cout << "do " << static_cast(c) << std::endl; + #endif + switch (c) + { + case _ECHO: + SendIacCmd(DO, _ECHO); + break; + case SUPPRESS_GO_AHEAD: + SendIacCmd(WILL, SUPPRESS_GO_AHEAD); + break; + default: + SendIacCmd(WONT, c); + }; + } + void RxDont(char c) + { + #ifdef CLI_TELNET_TRACE + std::cout << "dont " << static_cast(c) << std::endl; + #else + (void)c; + #endif + } + void RxSub(char c) + { + #ifdef CLI_TELNET_TRACE + std::cout << "sub: " << static_cast(c) << std::endl; + #else + (void)c; + #endif + } + void SendIacCmd(char action, char op) + { + std::string answer("\x0FF\x000\x000", 3); + answer[1] = action; + answer[2] = op; + this -> OutStream() << answer << std::flush; + } +protected: + virtual void Output(signed char c) + { + #ifdef CLI_TELNET_TRACE + std::cout << "data: " << static_cast(c) << std::endl; + #else + (void)c; + #endif + } +private: + enum class State { data, sub, wait_will, wait_wont, wait_do, wait_dont }; + State state = State::data; + bool escape = false; + +#endif + +private: + void Feed(char c) + { + if (std::isprint(c)) std::cout << c << std::endl; + else std::cout << "0x" << std::hex << static_cast(c) << std::dec << std::endl; + } + std::string buffer; +}; + +template +class TelnetServer : public Server +{ +public: + TelnetServer(typename ASIOLIB::ContextType& ios, unsigned short port) : + Server(ios, port) + {} + std::shared_ptr CreateSession(asiolib::ip::tcp::socket _socket) override + { + return std::make_shared(std::move(_socket)); + } +}; + +////////////// + +class CliTelnetSession : public InputDevice, public TelnetSession, public CliSession +{ +public: + + CliTelnetSession(Scheduler& _scheduler, asiolib::ip::tcp::socket _socket, Cli& _cli, const std::function< void(std::ostream&)>& _exitAction, std::size_t historySize ) : + InputDevice(_scheduler), + TelnetSession(std::move(_socket)), + CliSession(_cli, TelnetSession::OutStream(), historySize), + poll(*this, *this) + { + ExitAction([this, _exitAction](std::ostream& _out){ _exitAction(_out), Disconnect(); } ); + } +protected: + + void OnConnect() override + { + TelnetSession::OnConnect(); + Enter(); + Prompt(); + } + + void Output(signed char c) override // NB: C++ does not specify wether char is signed or unsigned + { + switch(step) + { + case Step::_1: + switch( c ) + { + case EOF: + case 4: // EOT + Notify(std::make_pair(KeyType::eof,' ')); break; + case 8: // Backspace + case 127: // Backspace or Delete + Notify(std::make_pair(KeyType::backspace, ' ')); break; + //case 10: Notify(std::make_pair(KeyType::ret,' ')); break; + case 27: step = Step::_2; break; // symbol + case 13: step = Step::wait_0; break; // wait for 0 (ENTER key) + default: // ascii + { + const char ch = static_cast(c); + Notify(std::make_pair(KeyType::ascii,ch)); + } + } + break; + + case Step::_2: // got 27 last time + if ( c == 91 ) + { + step = Step::_3; + break; // arrow keys + } + else + { + step = Step::_1; + Notify(std::make_pair(KeyType::ignored,' ')); + break; // unknown + } + break; + + case Step::_3: // got 27 and 91 + switch( c ) + { + case 65: step = Step::_1; Notify(std::make_pair(KeyType::up,' ')); break; + case 66: step = Step::_1; Notify(std::make_pair(KeyType::down,' ')); break; + case 68: step = Step::_1; Notify(std::make_pair(KeyType::left,' ')); break; + case 67: step = Step::_1; Notify(std::make_pair(KeyType::right,' ')); break; + case 70: step = Step::_1; Notify(std::make_pair(KeyType::end,' ')); break; + case 72: step = Step::_1; Notify(std::make_pair(KeyType::home,' ')); break; + default: step = Step::_4; break; // not arrow keys + } + break; + + case Step::_4: + if ( c == 126 ) Notify(std::make_pair(KeyType::canc,' ')); + else Notify(std::make_pair(KeyType::ignored,' ')); + + step = Step::_1; + + break; + + case Step::wait_0: + if ( c == 0 /* linux */ || c == 10 /* win */ ) Notify(std::make_pair(KeyType::ret,' ')); + else Notify(std::make_pair(KeyType::ignored,' ')); + + step = Step::_1; + + break; + + } + } + +private: + + enum class Step { _1, _2, _3, _4, wait_0 }; + Step step = Step::_1; + InputHandler poll; +}; + +template +class CliGenericTelnetServer : public Server +{ +public: + CliGenericTelnetServer(Cli& _cli, GenericAsioScheduler& _scheduler, unsigned short port, std::size_t _historySize=100 ) : + Server(_scheduler.AsioContext(), port), + scheduler(_scheduler), + cli(_cli), + historySize(_historySize) + {} + CliGenericTelnetServer(Cli& _cli, GenericAsioScheduler& _scheduler, std::string address, unsigned short port, std::size_t _historySize=100 ) : + Server(_scheduler.AsioContext(), address, port), + scheduler(_scheduler), + cli(_cli), + historySize(_historySize) + {} + + void EnterAction(std::function< void(std::ostream&)> action) + { + enterAction = action; + } + + void ExitAction( std::function< void(std::ostream&)> action ) + { + exitAction = action; + } + std::shared_ptr CreateSession(asiolib::ip::tcp::socket _socket) override + { + return std::make_shared(scheduler, std::move(_socket), cli, exitAction, historySize); + } +private: + Scheduler& scheduler; + Cli& cli; + std::function< void(std::ostream&)> enterAction; + std::function< void(std::ostream&)> exitAction; + std::size_t historySize; +}; + + +} // namespace detail +} // namespace cli + +#endif // CLI_DETAIL_GENERICASIOREMOTECLI_H_ + diff --git a/external/cli/detail/genericasioscheduler.h b/external/cli/detail/genericasioscheduler.h new file mode 100644 index 0000000..7fb2979 --- /dev/null +++ b/external/cli/detail/genericasioscheduler.h @@ -0,0 +1,113 @@ +/******************************************************************************* + * CLI - A simple command line interface. + * Copyright (C) 2016-2021 Daniele Pallastrelli + * + * Boost Software License - Version 1.0 - August 17th, 2003 + * + * Permission is hereby granted, free of charge, to any person or organization + * obtaining a copy of the software and accompanying documentation covered by + * this license (the "Software") to use, reproduce, display, distribute, + * execute, and transmit the Software, and to prepare derivative works of the + * Software, and to permit third-parties to whom the Software is furnished to + * do so, all subject to the following: + * + * The copyright notices in the Software and this entire statement, including + * the above license grant, this restriction and the following disclaimer, + * must be included in all copies of the Software, in whole or in part, and + * all derivative works of the Software, unless such copies or derivative + * works are solely in the form of machine-executable object code generated by + * a source language processor. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT + * SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE + * FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ******************************************************************************/ + +#ifndef CLI_DETAIL_GENERICASIOSCHEDULER_H_ +#define CLI_DETAIL_GENERICASIOSCHEDULER_H_ + +#include "../scheduler.h" +#include // unique_ptr + +namespace cli +{ +namespace detail +{ + +template +class GenericAsioScheduler : public Scheduler +{ +public: + + using ContextType = typename ASIOLIB::ContextType; + using WorkGuard = typename ASIOLIB::WorkGuard; + + GenericAsioScheduler() : + owned{true}, + context{new ContextType()}, + executor{*context}, + work{std::make_unique(ASIOLIB::MakeWorkGuard(*context))} + {} + + explicit GenericAsioScheduler(ContextType& _context) : context{&_context}, executor{*context} {} + + ~GenericAsioScheduler() override + { + if (owned) + { + work.reset(); // work uses context, so it must be deleted before context + delete context; + } + } + + // non copyable + GenericAsioScheduler(const GenericAsioScheduler&) = delete; + GenericAsioScheduler& operator=(const GenericAsioScheduler&) = delete; + + void Stop() + { + if (work) + ASIOLIB::Reset(*work); + context->stop(); + } + + void Run() + { + context->run(); + } + + bool Stopped() const + { + return context->stopped(); + } + + void ExecOne() { context->run_one(); } + + void PollOne() { context->poll_one(); } + + void Post(const std::function& f) override + { + executor.Post(f); + } + + ContextType& AsioContext() { return *context; } + +private: + + using ExecutorType = typename ASIOLIB::Executor; + + bool owned = false; + ContextType* context; + ExecutorType executor; + std::unique_ptr work; +}; + + +} // namespace detail +} // namespace cli + +#endif // CLI_DETAIL_GENERICASIOSCHEDULER_H_ diff --git a/external/cli/detail/genericcliasyncsession.h b/external/cli/detail/genericcliasyncsession.h new file mode 100644 index 0000000..2712276 --- /dev/null +++ b/external/cli/detail/genericcliasyncsession.h @@ -0,0 +1,100 @@ +/******************************************************************************* + * CLI - A simple command line interface. + * Copyright (C) 2016-2021 Daniele Pallastrelli + * + * Boost Software License - Version 1.0 - August 17th, 2003 + * + * Permission is hereby granted, free of charge, to any person or organization + * obtaining a copy of the software and accompanying documentation covered by + * this license (the "Software") to use, reproduce, display, distribute, + * execute, and transmit the Software, and to prepare derivative works of the + * Software, and to permit third-parties to whom the Software is furnished to + * do so, all subject to the following: + * + * The copyright notices in the Software and this entire statement, including + * the above license grant, this restriction and the following disclaimer, + * must be included in all copies of the Software, in whole or in part, and + * all derivative works of the Software, unless such copies or derivative + * works are solely in the form of machine-executable object code generated by + * a source language processor. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT + * SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE + * FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ******************************************************************************/ + +#ifndef CLI_DETAIL_GENERICCLIASYNCSESSION_H_ +#define CLI_DETAIL_GENERICCLIASYNCSESSION_H_ + +#include +#include "../cli.h" // CliSession +#include "genericasioscheduler.h" + +namespace cli +{ +namespace detail +{ + +template +class GenericCliAsyncSession : public CliSession +{ +public: + GenericCliAsyncSession(GenericAsioScheduler& _scheduler, Cli& _cli) : + CliSession(_cli, std::cout, 1), + input(_scheduler.AsioContext(), ::dup(STDIN_FILENO)) + { + Read(); + } + ~GenericCliAsyncSession() noexcept override + { + try { input.close(); } catch (const std::exception&) { /* do nothing */ } + } + +private: + + void Read() + { + Prompt(); + // Read a line of input entered by the user. + asiolib::async_read_until( + input, + inputBuffer, + '\n', + std::bind( &GenericCliAsyncSession::NewLine, this, + std::placeholders::_1, + std::placeholders::_2 ) + ); + } + + void NewLine(const asiolibec::error_code& error, std::size_t length ) + { + if ( !error || error == asiolib::error::not_found ) + { + auto bufs = inputBuffer.data(); + auto size = static_cast(length); + if ( !error ) --size; // remove \n + std::string s(asiolib::buffers_begin( bufs ), asiolib::buffers_begin( bufs ) + size); + inputBuffer.consume( length ); + + Feed( s ); + Read(); + } + else + { + input.close(); + } + } + + asiolib::streambuf inputBuffer; + asiolib::posix::stream_descriptor input; +}; + +} // namespace detail +} // namespace cli + +#endif // CLI_DETAIL_GENERICCLIASYNCSESSION_H_ + diff --git a/external/cli/detail/history.h b/external/cli/detail/history.h new file mode 100644 index 0000000..528109b --- /dev/null +++ b/external/cli/detail/history.h @@ -0,0 +1,165 @@ +/******************************************************************************* + * CLI - A simple command line interface. + * Copyright (C) 2016-2021 Daniele Pallastrelli + * + * Boost Software License - Version 1.0 - August 17th, 2003 + * + * Permission is hereby granted, free of charge, to any person or organization + * obtaining a copy of the software and accompanying documentation covered by + * this license (the "Software") to use, reproduce, display, distribute, + * execute, and transmit the Software, and to prepare derivative works of the + * Software, and to permit third-parties to whom the Software is furnished to + * do so, all subject to the following: + * + * The copyright notices in the Software and this entire statement, including + * the above license grant, this restriction and the following disclaimer, + * must be included in all copies of the Software, in whole or in part, and + * all derivative works of the Software, unless such copies or derivative + * works are solely in the form of machine-executable object code generated by + * a source language processor. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT + * SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE + * FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ******************************************************************************/ + +#ifndef CLI_DETAIL_HISTORY_H_ +#define CLI_DETAIL_HISTORY_H_ + +#include +#include +#include +#include +#include +#include + +namespace cli +{ +namespace detail +{ + +class History +{ +public: + + explicit History(std::size_t size) : maxSize(size) {} + + // Insert a new item in the buffer, changing the current state to "inserting" + // If we're browsing the history (eg with arrow keys) the new item overwrites + // the current one. + // Otherwise, the item is added to the front of the container + void NewCommand(const std::string& item) + { + ++commands; + current = 0; + if (mode == Mode::browsing) + { + assert(!buffer.empty()); + if (buffer.size() > 1 && buffer[1] == item) // try to insert an element identical to last one + buffer.pop_front(); + else // the item was not identical + buffer[current] = item; + } + else // Mode::inserting + { + if (buffer.empty() || buffer[0] != item) // insert an element not equal to last one + Insert(item); + } + mode = Mode::inserting; + } + + // Return the previous item of the history, updating the current item and + // changing the current state to "browsing" + // If we're already browsing the history (eg with arrow keys) the edit line is inserted + // to the front of the container. + // Otherwise, the line overwrites the current item. + std::string Previous(const std::string& line) + { + if (mode == Mode::inserting) + { + Insert(line); + mode = Mode::browsing; + current = (buffer.size() > 1) ? 1 : 0; + } + else // Mode::browsing + { + assert(!buffer.empty()); + buffer[current] = line; + if (current != buffer.size()-1) + ++current; + } + assert(mode == Mode::browsing); + assert(current < buffer.size()); + return buffer[current]; + } + + // Return the next item of the history, updating the current item. + std::string Next() + { + if (buffer.empty() || current == 0) + return {}; + assert(current != 0); + --current; + assert(current < buffer.size()); + return buffer[current]; + } + + // Show the whole history on the given ostream + void Show(std::ostream& out) const + { + out << '\n'; + for (auto& item: buffer) + out << item << '\n'; + out << '\n' << std::flush; + } + + // cmds[0] is the oldest command, cmds[size-1] the newer + void LoadCommands(const std::vector& cmds) + { + for (const auto& c: cmds) + Insert(c); + } + + // result[0] is the oldest command, result[size-1] the newer + std::vector GetCommands() const + { + auto numCmdsToReturn = std::min(commands, buffer.size()); + auto start = buffer.begin(); + if (mode == Mode::browsing) + { + numCmdsToReturn = std::min(commands, buffer.size()-1); + start = buffer.begin()+1; + } + std::vector result(numCmdsToReturn); + assert(std::distance(start, buffer.end()) >= 0); + assert(numCmdsToReturn <= static_cast(std::distance(buffer.end(), start))); + assert(numCmdsToReturn <= static_cast(std::numeric_limits::max())); + std::reverse_copy(start, start+static_cast(numCmdsToReturn), result.begin()); + return result; + } + +private: + + void Insert(const std::string& item) + { + buffer.push_front(item); + if (buffer.size() > maxSize) + buffer.pop_back(); + } + + const std::size_t maxSize; + std::deque buffer; + std::size_t current = 0; + std::size_t commands = 0; // number of commands issued + enum class Mode { inserting, browsing }; + Mode mode = Mode::inserting; +}; + +} // namespace detail +} // namespace cli + +#endif // CLI_DETAIL_HISTORY_H_ diff --git a/external/cli/detail/inputdevice.h b/external/cli/detail/inputdevice.h new file mode 100644 index 0000000..5655ed8 --- /dev/null +++ b/external/cli/detail/inputdevice.h @@ -0,0 +1,72 @@ +/******************************************************************************* + * CLI - A simple command line interface. + * Copyright (C) 2016-2021 Daniele Pallastrelli + * + * Boost Software License - Version 1.0 - August 17th, 2003 + * + * Permission is hereby granted, free of charge, to any person or organization + * obtaining a copy of the software and accompanying documentation covered by + * this license (the "Software") to use, reproduce, display, distribute, + * execute, and transmit the Software, and to prepare derivative works of the + * Software, and to permit third-parties to whom the Software is furnished to + * do so, all subject to the following: + * + * The copyright notices in the Software and this entire statement, including + * the above license grant, this restriction and the following disclaimer, + * must be included in all copies of the Software, in whole or in part, and + * all derivative works of the Software, unless such copies or derivative + * works are solely in the form of machine-executable object code generated by + * a source language processor. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT + * SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE + * FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ******************************************************************************/ + +#ifndef CLI_DETAIL_INPUTDEVICE_H_ +#define CLI_DETAIL_INPUTDEVICE_H_ + +#include +#include +#include "../scheduler.h" + +namespace cli +{ +namespace detail +{ + +enum class KeyType { ascii, up, down, left, right, backspace, canc, home, end, ret, eof, ignored }; + +class InputDevice +{ +public: + using Handler = std::function< void( std::pair ) >; + + explicit InputDevice(Scheduler& _scheduler) : scheduler(_scheduler) {} + virtual ~InputDevice() = default; + + template + void Register(H&& h) { handler = std::forward(h); } + +protected: + + void Notify(std::pair k) + { + scheduler.Post([this,k](){ if (handler) handler(k); }); + } + +private: + + Scheduler& scheduler; + Handler handler; +}; + +} // namespace detail +} // namespace cli + +#endif // CLI_DETAIL_INPUTDEVICE_H_ + diff --git a/external/cli/detail/inputhandler.h b/external/cli/detail/inputhandler.h new file mode 100644 index 0000000..ac9170e --- /dev/null +++ b/external/cli/detail/inputhandler.h @@ -0,0 +1,133 @@ +/******************************************************************************* + * CLI - A simple command line interface. + * Copyright (C) 2016-2021 Daniele Pallastrelli + * + * Boost Software License - Version 1.0 - August 17th, 2003 + * + * Permission is hereby granted, free of charge, to any person or organization + * obtaining a copy of the software and accompanying documentation covered by + * this license (the "Software") to use, reproduce, display, distribute, + * execute, and transmit the Software, and to prepare derivative works of the + * Software, and to permit third-parties to whom the Software is furnished to + * do so, all subject to the following: + * + * The copyright notices in the Software and this entire statement, including + * the above license grant, this restriction and the following disclaimer, + * must be included in all copies of the Software, in whole or in part, and + * all derivative works of the Software, unless such copies or derivative + * works are solely in the form of machine-executable object code generated by + * a source language processor. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT + * SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE + * FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ******************************************************************************/ + +#ifndef CLI_DETAIL_INPUTHANDLER_H_ +#define CLI_DETAIL_INPUTHANDLER_H_ + +#include +#include +#include "terminal.h" +#include "inputdevice.h" +#include "../cli.h" // CliSession +#include "commonprefix.h" + +namespace cli +{ +namespace detail +{ + +class InputHandler +{ +public: + InputHandler(CliSession& _session, InputDevice& kb) : + session(_session), + terminal(session.OutStream()) + { + kb.Register( [this](auto key){ this->Keypressed(key); } ); + } + +private: + + void Keypressed(std::pair k) + { + const std::pair s = terminal.Keypressed(k); + NewCommand(s); + } + + void NewCommand(const std::pair& s) + { + switch (s.first) + { + case Symbol::nothing: + { + break; + } + case Symbol::eof: + { + session.Exit(); + break; + } + case Symbol::command: + { + session.Feed(s.second); + session.Prompt(); + break; + } + case Symbol::down: + { + terminal.SetLine(session.NextCmd()); + break; + } + case Symbol::up: + { + auto line = terminal.GetLine(); + terminal.SetLine(session.PreviousCmd(line)); + break; + } + case Symbol::tab: + { + auto line = terminal.GetLine(); + auto completions = session.GetCompletions(line); + + if (completions.empty()) + break; + if (completions.size() == 1) + { + terminal.SetLine(completions[0]+' '); + break; + } + + auto commonPrefix = CommonPrefix(completions); + if (commonPrefix.size() > line.size()) + { + terminal.SetLine(commonPrefix); + break; + } + session.OutStream() << '\n'; + std::string items; + std::for_each( completions.begin(), completions.end(), [&items](auto& cmd){ items += '\t' + cmd; } ); + session.OutStream() << items << '\n'; + session.Prompt(); + terminal.ResetCursor(); + terminal.SetLine( line ); + break; + } + } + + } + + CliSession& session; + Terminal terminal; +}; + +} // namespace detail +} // namespace cli + +#endif // CLI_DETAIL_INPUTHANDLER_H_ + diff --git a/external/cli/detail/keyboard.h b/external/cli/detail/keyboard.h new file mode 100644 index 0000000..e300d25 --- /dev/null +++ b/external/cli/detail/keyboard.h @@ -0,0 +1,58 @@ +/******************************************************************************* + * CLI - A simple command line interface. + * Copyright (C) 2016-2021 Daniele Pallastrelli + * + * Boost Software License - Version 1.0 - August 17th, 2003 + * + * Permission is hereby granted, free of charge, to any person or organization + * obtaining a copy of the software and accompanying documentation covered by + * this license (the "Software") to use, reproduce, display, distribute, + * execute, and transmit the Software, and to prepare derivative works of the + * Software, and to permit third-parties to whom the Software is furnished to + * do so, all subject to the following: + * + * The copyright notices in the Software and this entire statement, including + * the above license grant, this restriction and the following disclaimer, + * must be included in all copies of the Software, in whole or in part, and + * all derivative works of the Software, unless such copies or derivative + * works are solely in the form of machine-executable object code generated by + * a source language processor. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT + * SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE + * FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ******************************************************************************/ + +#ifndef CLI_DETAIL_KEYBOARD_H_ +#define CLI_DETAIL_KEYBOARD_H_ + +#if defined(__unix__) || defined(__unix) || defined(__linux__) + #define OS_LINUX +#elif defined(WIN32) || defined(_WIN32) || defined(_WIN64) + #define OS_WIN +#elif defined(__APPLE__) || defined(__MACH__) + #define OS_MAC +#else + #error "Platform not supported (yet)." +#endif + +#if defined(OS_LINUX) || defined(OS_MAC) + #include "linuxkeyboard.h" + namespace cli { namespace detail { using Keyboard = LinuxKeyboard; } } +#elif defined(OS_WIN) + #include "winkeyboard.h" + namespace cli { namespace detail { using Keyboard = WinKeyboard; } } +#else + #error "Platform not supported (yet)." +#endif + +#undef OS_LINUX +#undef OS_WIN +#undef OS_MAC + +#endif // CLI_DETAIL_KEYBOARD_H_ + diff --git a/external/cli/detail/linuxkeyboard.h b/external/cli/detail/linuxkeyboard.h new file mode 100644 index 0000000..72ec901 --- /dev/null +++ b/external/cli/detail/linuxkeyboard.h @@ -0,0 +1,218 @@ +/******************************************************************************* + * CLI - A simple command line interface. + * Copyright (C) 2016-2021 Daniele Pallastrelli + * + * Boost Software License - Version 1.0 - August 17th, 2003 + * + * Permission is hereby granted, free of charge, to any person or organization + * obtaining a copy of the software and accompanying documentation covered by + * this license (the "Software") to use, reproduce, display, distribute, + * execute, and transmit the Software, and to prepare derivative works of the + * Software, and to permit third-parties to whom the Software is furnished to + * do so, all subject to the following: + * + * The copyright notices in the Software and this entire statement, including + * the above license grant, this restriction and the following disclaimer, + * must be included in all copies of the Software, in whole or in part, and + * all derivative works of the Software, unless such copies or derivative + * works are solely in the form of machine-executable object code generated by + * a source language processor. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT + * SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE + * FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ******************************************************************************/ + +#ifndef CLI_DETAIL_LINUXKEYBOARD_H_ +#define CLI_DETAIL_LINUXKEYBOARD_H_ + +#include +#include + +#include +#include +#include +#include +#include + +#include "inputdevice.h" + + +namespace cli +{ +namespace detail +{ + +class InputSource +{ +public: + + InputSource() + { + int pipes[2]; + if (pipe(pipes) == 0) + { + shutdownPipe = pipes[1]; // we store the write end + readPipe = pipes[0]; // ... and the read end + } + } + + void WaitKbHit() + { + fd_set rfds; + FD_ZERO(&rfds); + FD_SET(STDIN_FILENO, &rfds); + FD_SET(readPipe, &rfds); + + while (select(readPipe + 1, &rfds, nullptr, nullptr, nullptr) == 0); + + if (FD_ISSET(readPipe, &rfds)) // stop called + { + close(readPipe); + throw std::runtime_error("InputSource stop"); + } + + if (FD_ISSET(STDIN_FILENO, &rfds)) // char from stdinput + { + return; + } + + // cannot reach this point + assert(false); + } + + void Stop() + { + auto unused = write(shutdownPipe, " ", 1); + unused = close(shutdownPipe); + static_cast(unused); // silence unused warn + shutdownPipe = -1; + } + +private: + int shutdownPipe; + int readPipe; +}; + +// + + +class LinuxKeyboard : public InputDevice +{ +public: + explicit LinuxKeyboard(Scheduler& _scheduler) : + InputDevice(_scheduler), + servant( [this]() noexcept { Read(); } ) + { + ToManualMode(); + } + ~LinuxKeyboard() override + { + ToStandardMode(); + is.Stop(); + servant.join(); + } + +private: + + void Read() noexcept + { + try + { + while (true) + { + auto k = Get(); + Notify(k); + } + } + catch(const std::exception&) + { + // nothing to do: just exit + } + } + + char GetChar() + { + char buffer = 0; + auto unused = read(0, &buffer, 1); + static_cast(unused); // silence unused warn + return buffer; + } + + std::pair Get() + { + is.WaitKbHit(); + + auto ch = GetChar(); + switch(ch) + { + case EOF: + case 4: // EOT + return std::make_pair(KeyType::eof,' '); + break; + case 127: + case 8: + return std::make_pair(KeyType::backspace,' '); break; + case 10: return std::make_pair(KeyType::ret,' '); break; + case 27: // symbol + ch = GetChar(); + if ( ch == 91 ) // arrow keys + { + ch = GetChar(); + switch( ch ) + { + case 51: + ch = GetChar(); + if ( ch == 126 ) return std::make_pair(KeyType::canc,' '); + else return std::make_pair(KeyType::ignored,' '); + break; + case 65: return std::make_pair(KeyType::up,' '); + case 66: return std::make_pair(KeyType::down,' '); + case 68: return std::make_pair(KeyType::left,' '); + case 67: return std::make_pair(KeyType::right,' '); + case 70: return std::make_pair(KeyType::end,' '); + case 72: return std::make_pair(KeyType::home,' '); + default: return std::make_pair(KeyType::ignored,' '); + } + } + break; + default: // ascii + { + const char c = static_cast(ch); + return std::make_pair(KeyType::ascii,c); + } + } + return std::make_pair(KeyType::ignored,' '); + } + + void ToManualMode() + { + constexpr tcflag_t ICANON_FLAG = ICANON; + constexpr tcflag_t ECHO_FLAG = ECHO; + + tcgetattr(STDIN_FILENO, &oldt); + newt = oldt; + newt.c_lflag &= ~( ICANON_FLAG | ECHO_FLAG ); + tcsetattr(STDIN_FILENO, TCSANOW, &newt); + } + + void ToStandardMode() + { + tcsetattr(STDIN_FILENO, TCSANOW, &oldt); + } + + termios oldt; + termios newt; + InputSource is; + std::thread servant; +}; + +} // namespace detail +} // namespace cli + +#endif // CLI_DETAIL_LINUXKEYBOARD_H_ + diff --git a/external/cli/detail/newboostasiolib.h b/external/cli/detail/newboostasiolib.h new file mode 100644 index 0000000..2d7b4e6 --- /dev/null +++ b/external/cli/detail/newboostasiolib.h @@ -0,0 +1,89 @@ +/******************************************************************************* + * CLI - A simple command line interface. + * Copyright (C) 2016-2021 Daniele Pallastrelli + * + * Boost Software License - Version 1.0 - August 17th, 2003 + * + * Permission is hereby granted, free of charge, to any person or organization + * obtaining a copy of the software and accompanying documentation covered by + * this license (the "Software") to use, reproduce, display, distribute, + * execute, and transmit the Software, and to prepare derivative works of the + * Software, and to permit third-parties to whom the Software is furnished to + * do so, all subject to the following: + * + * The copyright notices in the Software and this entire statement, including + * the above license grant, this restriction and the following disclaimer, + * must be included in all copies of the Software, in whole or in part, and + * all derivative works of the Software, unless such copies or derivative + * works are solely in the form of machine-executable object code generated by + * a source language processor. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT + * SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE + * FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ******************************************************************************/ + +#ifndef CLI_DETAIL_NEWBOOSTASIOLIB_H_ +#define CLI_DETAIL_NEWBOOSTASIOLIB_H_ + +#include +#include + +namespace cli +{ +namespace detail +{ + +namespace asiolib = boost::asio; +namespace asiolibec = boost::system; + +class NewBoostAsioLib +{ +public: + + using ContextType = boost::asio::io_context; + using WorkGuard = boost::asio::executor_work_guard; + + class Executor + { + public: + explicit Executor(ContextType& ios) : + executor(ios.get_executor()) {} + explicit Executor(boost::asio::ip::tcp::socket& socket) : + executor(socket.get_executor()) {} + template void Post(T&& t) { boost::asio::post(executor, std::forward(t)); } + private: +#if BOOST_VERSION >= 107400 + using AsioExecutor = boost::asio::any_io_executor; +#else + using AsioExecutor = boost::asio::executor; +#endif + AsioExecutor executor; + }; + + static boost::asio::ip::address IpAddressFromString(const std::string& address) + { + return boost::asio::ip::make_address(address); + } + + static auto MakeWorkGuard(ContextType& context) + { + return boost::asio::make_work_guard(context); + } + + static void Reset(WorkGuard& wg) + { + wg.reset(); + } + +}; + +} // namespace detail +} // namespace cli + +#endif // CLI_DETAIL_NEWBOOSTASIOLIB_H_ + diff --git a/external/cli/detail/newstandaloneasiolib.h b/external/cli/detail/newstandaloneasiolib.h new file mode 100644 index 0000000..54d5aa7 --- /dev/null +++ b/external/cli/detail/newstandaloneasiolib.h @@ -0,0 +1,89 @@ +/******************************************************************************* + * CLI - A simple command line interface. + * Copyright (C) 2016-2021 Daniele Pallastrelli + * + * Boost Software License - Version 1.0 - August 17th, 2003 + * + * Permission is hereby granted, free of charge, to any person or organization + * obtaining a copy of the software and accompanying documentation covered by + * this license (the "Software") to use, reproduce, display, distribute, + * execute, and transmit the Software, and to prepare derivative works of the + * Software, and to permit third-parties to whom the Software is furnished to + * do so, all subject to the following: + * + * The copyright notices in the Software and this entire statement, including + * the above license grant, this restriction and the following disclaimer, + * must be included in all copies of the Software, in whole or in part, and + * all derivative works of the Software, unless such copies or derivative + * works are solely in the form of machine-executable object code generated by + * a source language processor. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT + * SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE + * FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ******************************************************************************/ + +#ifndef CLI_DETAIL_NEWSTANDALONEASIOLIB_H_ +#define CLI_DETAIL_NEWSTANDALONEASIOLIB_H_ + +#include +#include + +namespace cli +{ +namespace detail +{ + +namespace asiolib = asio; +namespace asiolibec = asio; + +class NewStandaloneAsioLib +{ +public: + + using ContextType = asio::io_context; + using WorkGuard = asio::executor_work_guard; + + class Executor + { + public: + explicit Executor(ContextType& ios) : + executor(ios.get_executor()) {} + explicit Executor(asio::ip::tcp::socket& socket) : + executor(socket.get_executor()) {} + template void Post(T&& t) { asio::post(executor, std::forward(t)); } + private: +#if ASIO_VERSION >= 101700 + using AsioExecutor = asio::any_io_executor; +#else + using AsioExecutor = asio::executor; +#endif + AsioExecutor executor; + }; + + static asio::ip::address IpAddressFromString(const std::string& address) + { + return asio::ip::make_address(address); + } + + static auto MakeWorkGuard(ContextType& context) + { + return asio::make_work_guard(context); + } + + static void Reset(WorkGuard& wg) + { + wg.reset(); + } + +}; + +} // namespace detail +} // namespace cli + +#endif // CLI_DETAIL_NEWSTANDALONEASIOLIB_H_ + diff --git a/external/cli/detail/oldboostasiolib.h b/external/cli/detail/oldboostasiolib.h new file mode 100644 index 0000000..a24173d --- /dev/null +++ b/external/cli/detail/oldboostasiolib.h @@ -0,0 +1,82 @@ +/******************************************************************************* + * CLI - A simple command line interface. + * Copyright (C) 2016-2021 Daniele Pallastrelli + * + * Boost Software License - Version 1.0 - August 17th, 2003 + * + * Permission is hereby granted, free of charge, to any person or organization + * obtaining a copy of the software and accompanying documentation covered by + * this license (the "Software") to use, reproduce, display, distribute, + * execute, and transmit the Software, and to prepare derivative works of the + * Software, and to permit third-parties to whom the Software is furnished to + * do so, all subject to the following: + * + * The copyright notices in the Software and this entire statement, including + * the above license grant, this restriction and the following disclaimer, + * must be included in all copies of the Software, in whole or in part, and + * all derivative works of the Software, unless such copies or derivative + * works are solely in the form of machine-executable object code generated by + * a source language processor. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT + * SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE + * FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ******************************************************************************/ + +#ifndef CLI_DETAIL_OLDBOOSTASIOLIB_H_ +#define CLI_DETAIL_OLDBOOSTASIOLIB_H_ + +#include + +namespace cli +{ +namespace detail +{ + +namespace asiolib = boost::asio; +namespace asiolibec = boost::system; + +class OldBoostAsioLib +{ +public: + using ContextType = boost::asio::io_service; + using WorkGuard = boost::asio::io_service::work; + + class Executor + { + public: + explicit Executor(ContextType& _ios) : + ios(_ios) {} + explicit Executor(boost::asio::ip::tcp::socket& socket) : + ios(socket.get_io_service()) {} + template void Post(T&& t) { ios.post(std::forward(t)); } + private: + ContextType& ios; + }; + + static boost::asio::ip::address IpAddressFromString(const std::string& address) + { + return boost::asio::ip::address::from_string(address); + } + + static auto MakeWorkGuard(ContextType& context) + { + boost::asio::io_service::work work(context); + return work; + } + + static void Reset(WorkGuard& /*wg*/) + { + } + +}; + +} // namespace detail +} // namespace cli + +#endif // CLI_DETAIL_OLDBOOSTASIOLIB_H_ + diff --git a/external/cli/detail/oldstandaloneasiolib.h b/external/cli/detail/oldstandaloneasiolib.h new file mode 100644 index 0000000..6c582e6 --- /dev/null +++ b/external/cli/detail/oldstandaloneasiolib.h @@ -0,0 +1,85 @@ +/******************************************************************************* + * CLI - A simple command line interface. + * Copyright (C) 2016-2021 Daniele Pallastrelli + * + * Boost Software License - Version 1.0 - August 17th, 2003 + * + * Permission is hereby granted, free of charge, to any person or organization + * obtaining a copy of the software and accompanying documentation covered by + * this license (the "Software") to use, reproduce, display, distribute, + * execute, and transmit the Software, and to prepare derivative works of the + * Software, and to permit third-parties to whom the Software is furnished to + * do so, all subject to the following: + * + * The copyright notices in the Software and this entire statement, including + * the above license grant, this restriction and the following disclaimer, + * must be included in all copies of the Software, in whole or in part, and + * all derivative works of the Software, unless such copies or derivative + * works are solely in the form of machine-executable object code generated by + * a source language processor. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT + * SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE + * FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ******************************************************************************/ + +#ifndef CLI_DETAIL_OLDSTANDALONEASIOLIB_H_ +#define CLI_DETAIL_OLDSTANDALONEASIOLIB_H_ + +#define ASIO_STANDALONE 1 + +#include + +namespace cli +{ +namespace detail +{ + +namespace asiolib = asio; +namespace asiolibec = asio; + +class OldStandaloneAsioLib +{ +public: + + using ContextType = asio::io_service; + using WorkGuard = asio::io_service::work; + + class Executor + { + public: + explicit Executor(ContextType& _ios) : + ios(_ios) {} + explicit Executor(asio::ip::tcp::socket& socket) : + ios(socket.get_io_service()) {} + template void Post(T&& t) { ios.post(std::forward(t)); } + private: + ContextType& ios; + }; + + static asio::ip::address IpAddressFromString(const std::string& address) + { + return asio::ip::address::from_string(address); + } + + static auto MakeWorkGuard(ContextType& context) + { + asio::io_service::work work(context); + return work; + } + + static void Reset(WorkGuard& /*wg*/) + { + } + +}; + +} // namespace detail +} // namespace cli + +#endif // CLI_DETAIL_OLDSTANDALONEASIOLIB_H_ + diff --git a/external/cli/detail/rang.h b/external/cli/detail/rang.h new file mode 100644 index 0000000..79b5ae6 --- /dev/null +++ b/external/cli/detail/rang.h @@ -0,0 +1,303 @@ +#ifndef CLI_DETAIL_RANG_H +#define CLI_DETAIL_RANG_H + +#if defined(__unix__) || defined(__unix) || defined(__linux__) +#define OS_LINUX +#elif defined(WIN32) || defined(_WIN32) || defined(_WIN64) +#define OS_WIN +#elif defined(__APPLE__) || defined(__MACH__) +#define OS_MAC +#else +#error Unknown Platform +#endif + +#if defined(OS_LINUX) || defined(OS_MAC) + #include + #include +#elif defined(OS_WIN) + #if !defined(NOMINMAX) + #define NOMINMAX 1 + #endif // !defined(NOMINMAX) + #include + #include + #include +#endif + +#include +#include +#include +#include +#include +#include + +namespace cli { +namespace detail { +namespace rang { + +enum class style { + reset = 0, + bold = 1, + dim = 2, + italic = 3, + underline = 4, + blink = 5, + rblink = 6, + reversed = 7, + conceal = 8, + crossed = 9 +}; + +enum class fg { + black = 30, + red = 31, + green = 32, + yellow = 33, + blue = 34, + magenta = 35, + cyan = 36, + gray = 37, + reset = 39 +}; + +enum class bg { + black = 40, + red = 41, + green = 42, + yellow = 43, + blue = 44, + magenta = 45, + cyan = 46, + gray = 47, + reset = 49 +}; + +enum class fgB { + black = 90, + red = 91, + green = 92, + yellow = 93, + blue = 94, + magenta = 95, + cyan = 96, + gray = 97 +}; + +enum class bgB { + black = 100, + red = 101, + green = 102, + yellow = 103, + blue = 104, + magenta = 105, + cyan = 106, + gray = 107 +}; + +enum class control { autoColor = 0, forceColor = 1 }; + + +namespace rang_implementation { + + inline std::streambuf const *&RANG_coutbuf() + { + static std::streambuf const *pOutbuff = std::cout.rdbuf(); + return pOutbuff; + } + + inline std::streambuf const *&RANG_cerrbuf() + { + static std::streambuf const *pErrbuff = std::cerr.rdbuf(); + return pErrbuff; + } + + inline std::streambuf const *&RANG_clogbuf() + { + static std::streambuf const *pLogbuff = std::clog.rdbuf(); + return pLogbuff; + } + + inline int getIword() + { + static int i = std::ios_base::xalloc(); + return i; + } + + + inline bool supportsColor() + { +#if defined(OS_LINUX) || defined(OS_MAC) + static constexpr const char* Terms[] = { + "ansi", "color", "console", "cygwin", "gnome", "konsole", "kterm", + "linux", "msys", "putty", "rxvt", "screen", "vt100", "xterm" + }; + + const char *env_p = std::getenv("TERM"); + if (env_p == nullptr) { + return false; + } + + static const bool result = std::any_of( + std::begin(Terms), std::end(Terms), [&](const char* term) { + return std::strstr(env_p, term) != nullptr; + }); + +#elif defined(OS_WIN) + static constexpr bool result = true; +#endif + return result; + } + + + inline bool isTerminal(const std::streambuf *osbuf) + { + if (osbuf == RANG_coutbuf()) { +#if defined(OS_LINUX) || defined(OS_MAC) + return isatty(fileno(stdout)) ? true : false; +#elif defined(OS_WIN) + return _isatty(_fileno(stdout)) ? true : false; +#endif + } + + if (osbuf == RANG_cerrbuf() || osbuf == RANG_clogbuf()) { +#if defined(OS_LINUX) || defined(OS_MAC) + return isatty(fileno(stderr)) ? true : false; +#elif defined(OS_WIN) + return _isatty(_fileno(stderr)) ? true : false; +#endif + } + return false; + } + + + template + using enableStd = + typename std::enable_if::value + || std::is_same::value + || std::is_same::value + || std::is_same::value + || std::is_same::value, + std::ostream &>::type; + + +#ifdef OS_WIN + inline HANDLE getVersionDependentHandle() + { + if (IsWindowsVersionOrGreater(10, 0, 0)) return nullptr; + return GetStdHandle(STD_OUTPUT_HANDLE); + } + + inline HANDLE getConsoleHandle() + { + static HANDLE h = getVersionDependentHandle(); + return h; + } + + inline WORD reverseRGB(WORD rgb) + { + static const WORD rev[8] = { 0, 4, 2, 6, 1, 5, 3, 7 }; + return rev[rgb]; + } + + inline void setWinAttribute(rang::bg col, WORD &state) + { + state &= 0xFF0F; + state |= reverseRGB(static_cast(col) - 40) << 4; + } + + inline void setWinAttribute(rang::fg col, WORD &state) + { + state &= 0xFFF0; + state |= reverseRGB(static_cast(col) - 30); + } + + inline void setWinAttribute(rang::bgB col, WORD &state) + { + state &= 0xFF0F; + state |= (0x8 | reverseRGB(static_cast(col) - 100)) << 4; + } + + inline void setWinAttribute(rang::fgB col, WORD &state) + { + state &= 0xFFF0; + state |= (0x8 | reverseRGB(static_cast(col) - 90)); + } + + inline void setWinAttribute(rang::style style, WORD &state) + { + if (style == rang::style::reset) { + state = (FOREGROUND_BLUE | FOREGROUND_GREEN | FOREGROUND_RED); + } + } + + inline WORD ¤t_state() + { + static WORD state + = (FOREGROUND_BLUE | FOREGROUND_GREEN | FOREGROUND_RED); + return state; + } + + template + inline enableStd setColor(std::ostream &os, T const value) + { + HANDLE h = getConsoleHandle(); + if (h && isTerminal(os.rdbuf())) { + setWinAttribute(value, current_state()); + SetConsoleTextAttribute(h, current_state()); + return os; + } + return os << "\033[" << static_cast(value) << "m"; + } +#else + template + inline enableStd setColor(std::ostream &os, T const value) + { + return os << "\033[" << static_cast(value) << "m"; + } +#endif + + template + using enableControl = + typename std::enable_if::value, + std::ostream &>::type; +} // namespace rang_implementation + +inline void init() +{ + rang_implementation::RANG_coutbuf(); + rang_implementation::RANG_cerrbuf(); + rang_implementation::RANG_clogbuf(); +} + +template +inline rang_implementation::enableStd operator<<( + std::ostream &os, T const value) +{ + std::streambuf const *osbuf = os.rdbuf(); + return (os.iword(rang_implementation::getIword()) + || ((rang_implementation::supportsColor()) + && (rang_implementation::isTerminal(osbuf)))) + ? rang_implementation::setColor(os, value) + : os; +} + +template +inline rang_implementation::enableControl operator<<( + std::ostream &os, T const value) +{ + if (value == rang::control::forceColor) { + os.iword(rang_implementation::getIword()) = 1; + } else if (value == rang::control::autoColor) { + os.iword(rang_implementation::getIword()) = 0; + } + return os; +} + +} // namespace rang +} // namespace detail +} // namespace cli + +#undef OS_LINUX +#undef OS_WIN +#undef OS_MAC + +#endif // CLI_DETAIL_RANG_H diff --git a/external/cli/detail/server.h b/external/cli/detail/server.h new file mode 100644 index 0000000..8013057 --- /dev/null +++ b/external/cli/detail/server.h @@ -0,0 +1,157 @@ +/******************************************************************************* + * CLI - A simple command line interface. + * Copyright (C) 2016-2021 Daniele Pallastrelli + * + * Boost Software License - Version 1.0 - August 17th, 2003 + * + * Permission is hereby granted, free of charge, to any person or organization + * obtaining a copy of the software and accompanying documentation covered by + * this license (the "Software") to use, reproduce, display, distribute, + * execute, and transmit the Software, and to prepare derivative works of the + * Software, and to permit third-parties to whom the Software is furnished to + * do so, all subject to the following: + * + * The copyright notices in the Software and this entire statement, including + * the above license grant, this restriction and the following disclaimer, + * must be included in all copies of the Software, in whole or in part, and + * all derivative works of the Software, unless such copies or derivative + * works are solely in the form of machine-executable object code generated by + * a source language processor. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT + * SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE + * FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ******************************************************************************/ + +#ifndef CLI_DETAIL_SERVER_H_ +#define CLI_DETAIL_SERVER_H_ + +#include +#include + +namespace cli +{ +namespace detail +{ + +class Session : public std::enable_shared_from_this, public std::streambuf +{ +public: + ~Session() override = default; + virtual void Start() + { + OnConnect(); + Read(); + } + +protected: + + explicit Session(asiolib::ip::tcp::socket _socket) : socket(std::move(_socket)), outStream( this ) {} + + virtual void Disconnect() + { + socket.shutdown(asiolib::ip::tcp::socket::shutdown_both); + socket.close(); + } + + virtual void Read() + { + auto self( shared_from_this() ); + socket.async_read_some(asiolib::buffer( data, max_length ), + [ this, self ]( asiolibec::error_code ec, std::size_t length ) + { + if ( !socket.is_open() || ( ec == asiolib::error::eof ) || ( ec == asiolib::error::connection_reset ) ) + OnDisconnect(); + else if ( ec ) + OnError(); + else + { + OnDataReceived( std::string( data, length )); + Read(); + } + }); + } + + virtual void Send(const std::string& msg) + { + asiolibec::error_code ec; + asiolib::write(socket, asiolib::buffer(msg), ec); + if ((ec == asiolib::error::eof) || (ec == asiolib::error::connection_reset)) + OnDisconnect(); + else if (ec) + OnError(); + } + + virtual std::ostream& OutStream() { return outStream; } + + virtual void OnConnect() = 0; + virtual void OnDisconnect() = 0; + virtual void OnError() = 0; + virtual void OnDataReceived(const std::string& _data) = 0; + + virtual std::string Encode(const std::string& _data) const { return _data; } + +private: + + // std::streambuf + std::streamsize xsputn( const char* s, std::streamsize n ) override + { + Send(Encode(std::string(s, s+n))); + return n; + } + int overflow( int c ) override + { + Send(Encode(std::string(1, static_cast< char >(c)))); + return c; + } + + asiolib::ip::tcp::socket socket; + enum { max_length = 1024 }; + char data[ max_length ]; + std::ostream outStream; +}; + + +template +class Server +{ +public: + // disable value semantics + Server( const Server& ) = delete; + Server& operator = ( const Server& ) = delete; + + Server(typename ASIOLIB::ContextType& ios, unsigned short port) : + acceptor(ios, asiolib::ip::tcp::endpoint(asiolib::ip::tcp::v4(), port)) + { + Accept(); + } + Server(typename ASIOLIB::ContextType& ios, std::string address, unsigned short port) : + acceptor(ios, asiolib::ip::tcp::endpoint(ASIOLIB::IpAddressFromString(address), port)) + { + Accept(); + } + virtual ~Server() = default; + // returns shared_ptr instead of unique_ptr because Session needs to use enable_shared_from_this + virtual std::shared_ptr CreateSession(asiolib::ip::tcp::socket socket) = 0; +private: + void Accept() + { + acceptor.async_accept([this](asiolibec::error_code ec, asiolib::ip::tcp::socket socket) + { + if (!ec) CreateSession(std::move(socket))->Start(); + Accept(); + }); + } + asiolib::ip::tcp::acceptor acceptor; +}; + + +} // namespace detail +} // namespace cli + +#endif // CLI_DETAIL_SERVER_H_ + diff --git a/external/cli/detail/split.h b/external/cli/detail/split.h new file mode 100644 index 0000000..fbd8686 --- /dev/null +++ b/external/cli/detail/split.h @@ -0,0 +1,239 @@ +/******************************************************************************* + * CLI - A simple command line interface. + * Copyright (C) 2016-2021 Daniele Pallastrelli + * + * Boost Software License - Version 1.0 - August 17th, 2003 + * + * Permission is hereby granted, free of charge, to any person or organization + * obtaining a copy of the software and accompanying documentation covered by + * this license (the "Software") to use, reproduce, display, distribute, + * execute, and transmit the Software, and to prepare derivative works of the + * Software, and to permit third-parties to whom the Software is furnished to + * do so, all subject to the following: + * + * The copyright notices in the Software and this entire statement, including + * the above license grant, this restriction and the following disclaimer, + * must be included in all copies of the Software, in whole or in part, and + * all derivative works of the Software, unless such copies or derivative + * works are solely in the form of machine-executable object code generated by + * a source language processor. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT + * SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE + * FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ******************************************************************************/ + +#ifndef CLI_DETAIL_SPLIT_H_ +#define CLI_DETAIL_SPLIT_H_ + +#include +#include +#include +#include +#include + +namespace cli +{ +namespace detail +{ + +class Text +{ +public: + explicit Text(std::string _input) : input(std::move(_input)) + { + } + void SplitInto(std::vector& strs) + { + Reset(); + for (char c: input) + Eval(c); + RemoveEmptyEntries(); + splitResult.swap(strs); // puts the result back in strs + } +private: + void Reset() + { + state = State::space; + prev_state = State::space; + sentence_type = SentenceType::double_quote; + splitResult.clear(); + } + + void Eval(char c) + { + switch(state) + { + case State::space: + EvalSpace(c); + break; + case State::word: + EvalWord(c); + break; + case State::sentence: + EvalSentence(c); + break; + case State::escape: + EvalEscape(c); + break; + } + } + + void EvalSpace(char c) + { + if (c == ' ' || c == '\t' || c == '\n') + { + // do nothing + } + else if (c == '"' || c == '\'') + { + NewSentence(c); + } + else if (c == '\\') + { + // This is the case where the first character of a word is escaped. + // Should come back into the word state after this. + prev_state = State::word; + state = State::escape; + splitResult.emplace_back(""); + } + else + { + state = State::word; + splitResult.emplace_back(1, c); + } + } + + void EvalWord(char c) + { + if (c == ' ' || c == '\t' || c == '\n') + { + state = State::space; + } + else if (c == '"' || c == '\'') + { + NewSentence(c); + } + else if (c == '\\') + { + prev_state = state; + state = State::escape; + } + else + { + assert(!splitResult.empty()); + splitResult.back() += c; + } + } + + void EvalSentence(char c) + { + if (c == '"' || c == '\'') + { + auto new_type = c == '"' ? SentenceType::double_quote : SentenceType::quote; + if (new_type == sentence_type) + state = State::space; + else + { + assert(!splitResult.empty()); + splitResult.back() += c; + } + } + else if (c == '\\') + { + prev_state = state; + state = State::escape; + } + else + { + assert(!splitResult.empty()); + splitResult.back() += c; + } + } + + void EvalEscape(char c) + { + assert(!splitResult.empty()); + if (c != '"' && c != '\'' && c != '\\') + splitResult.back() += "\\"; + splitResult.back() += c; + state = prev_state; + } + + void NewSentence(char c) + { + state = State::sentence; + sentence_type = ( c == '"' ? SentenceType::double_quote : SentenceType::quote); + splitResult.emplace_back(""); + } + + void RemoveEmptyEntries() + { + // remove null entries from the vector: + splitResult.erase( + std::remove_if( + splitResult.begin(), + splitResult.end(), + [](const std::string& s){ return s.empty(); } + ), + splitResult.end() + ); + } + + enum class State { space, word, sentence, escape }; + enum class SentenceType { quote, double_quote }; + State state = State::space; + State prev_state = State::space; + SentenceType sentence_type = SentenceType::double_quote; + const std::string input; + std::vector splitResult; +}; + +// Split the string input into a vector of strings. +// The original string is split where there are spaces. +// Quotes and double quotes can be used to indicate a substring that should not be splitted +// (even if it contains spaces) + +// split(strs, ""); => empty vector +// split(strs, " "); => empty vector +// split(strs, " "); => empty vector +// split(strs, "\t"); => empty vector +// split(strs, " \t \t "); => empty vector + +// split(strs, "1234567890"); => <"1234567890"> +// split(strs, " foo "); => <"foo"> +// split(strs, " foo \t \t bar \t"); => <"foo","bar"> + +// split(strs, "\"\""); => empty vector +// split(strs, "\"foo bar\""); => <"foo bar"> +// split(strs, " \t\t \"foo \tbar\" \t"); => <"foo \tbar"> +// split(strs, " first \t\t \"foo \tbar\" \t last"); => <"first","foo \tbar","last"> +// split(strs, "first\"foo \tbar\""); => <"first","foo \tbar"> +// split(strs, "first \"'second' 'thirdh'\""); => <"first","'second' 'thirdh'"> + +// split(strs, "''"); => empty vector +// split(strs, "'foo bar'"); => <"foo bar"> +// split(strs, " \t\t 'foo \tbar' \t"); => <"foo \tbar"> +// split(strs, " first \t\t 'foo \tbar' \t last"); => <"first","foo \tbar","last"> +// split(strs, "first'foo \tbar'"); => <"first","foo \tbar"> +// split(strs, "first '\"second\" \"thirdh\"'"); => <"first","\"second\" \"thirdh\""> + +// split(strs, R"("foo\"bar")"); // "foo\"bar" => <"foo"bar"> +// split(strs, R"('foo\'bar')"); // 'foo\'bar' => <"foo'bar"> +// split(strs, R"("foo\bar")"); // "foo\bar" => <"foo\bar"> +// split(strs, R"("foo\\"bar")"); // "foo\\"bar" => <"foo\"bar"> + +inline void split(std::vector& strs, const std::string& input) +{ + Text sentence(input); + sentence.SplitInto(strs); +} + +} // namespace detail +} // namespace cli + +#endif // CLI_DETAIL_SPLIT_H_ diff --git a/external/cli/detail/standaloneasiolib.h b/external/cli/detail/standaloneasiolib.h new file mode 100644 index 0000000..adb747e --- /dev/null +++ b/external/cli/detail/standaloneasiolib.h @@ -0,0 +1,49 @@ +/******************************************************************************* + * CLI - A simple command line interface. + * Copyright (C) 2016-2021 Daniele Pallastrelli + * + * Boost Software License - Version 1.0 - August 17th, 2003 + * + * Permission is hereby granted, free of charge, to any person or organization + * obtaining a copy of the software and accompanying documentation covered by + * this license (the "Software") to use, reproduce, display, distribute, + * execute, and transmit the Software, and to prepare derivative works of the + * Software, and to permit third-parties to whom the Software is furnished to + * do so, all subject to the following: + * + * The copyright notices in the Software and this entire statement, including + * the above license grant, this restriction and the following disclaimer, + * must be included in all copies of the Software, in whole or in part, and + * all derivative works of the Software, unless such copies or derivative + * works are solely in the form of machine-executable object code generated by + * a source language processor. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT + * SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE + * FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ******************************************************************************/ + +#ifndef CLI_DETAIL_STANDALONEASIOLIB_H_ +#define CLI_DETAIL_STANDALONEASIOLIB_H_ + +/** + * This header file provides the class `cli::StandaloneAsioLib`, using the right + * implementation according to the version of asio libraries included. + */ + +#include + +#if ASIO_VERSION < 101300 + #include "oldstandaloneasiolib.h" + namespace cli { namespace detail { using StandaloneAsioLib = OldStandaloneAsioLib; } } +#else + #include "newstandaloneasiolib.h" + namespace cli { namespace detail { using StandaloneAsioLib = NewStandaloneAsioLib; } } +#endif + +#endif // CLI_DETAIL_STANDALONEASIOLIB_H_ + diff --git a/external/cli/detail/terminal.h b/external/cli/detail/terminal.h new file mode 100644 index 0000000..e9a4f6d --- /dev/null +++ b/external/cli/detail/terminal.h @@ -0,0 +1,213 @@ +/******************************************************************************* + * CLI - A simple command line interface. + * Copyright (C) 2016-2021 Daniele Pallastrelli + * + * Boost Software License - Version 1.0 - August 17th, 2003 + * + * Permission is hereby granted, free of charge, to any person or organization + * obtaining a copy of the software and accompanying documentation covered by + * this license (the "Software") to use, reproduce, display, distribute, + * execute, and transmit the Software, and to prepare derivative works of the + * Software, and to permit third-parties to whom the Software is furnished to + * do so, all subject to the following: + * + * The copyright notices in the Software and this entire statement, including + * the above license grant, this restriction and the following disclaimer, + * must be included in all copies of the Software, in whole or in part, and + * all derivative works of the Software, unless such copies or derivative + * works are solely in the form of machine-executable object code generated by + * a source language processor. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT + * SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE + * FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ******************************************************************************/ + +#ifndef CLI_DETAIL_TERMINAL_H_ +#define CLI_DETAIL_TERMINAL_H_ + +#include +#include "../colorprofile.h" +#include "inputdevice.h" + +namespace cli +{ +namespace detail +{ + +enum class Symbol +{ + nothing, + command, + up, + down, + tab, + eof +}; + +class Terminal +{ + public: + explicit Terminal(std::ostream &_out) : out(_out) {} + + void ResetCursor() { position = 0; } + + void SetLine(const std::string &newLine) + { + out << beforeInput + << std::string(position, '\b') << newLine + << afterInput << std::flush; + + // if newLine is shorter than currentLine, we have + // to clear the rest of the string + if (newLine.size() < currentLine.size()) + { + out << std::string(currentLine.size() - newLine.size(), ' '); + // and go back + out << std::string(currentLine.size() - newLine.size(), '\b') << std::flush; + } + + currentLine = newLine; + position = currentLine.size(); + } + + std::string GetLine() const { return currentLine; } + + std::pair Keypressed(std::pair k) + { + switch (k.first) + { + case KeyType::eof: + return std::make_pair(Symbol::eof, std::string{}); + break; + case KeyType::backspace: + { + if (position == 0) + break; + + --position; + + const auto pos = static_cast(position); + // remove the char from buffer + currentLine.erase(currentLine.begin() + pos); + // go back to the previous char + out << '\b'; + // output the rest of the line + out << std::string(currentLine.begin() + pos, currentLine.end()); + // remove last char + out << ' '; + // go back to the original position + out << std::string(currentLine.size() - position + 1, '\b') << std::flush; + break; + } + case KeyType::up: + return std::make_pair(Symbol::up, std::string{}); + break; + case KeyType::down: + return std::make_pair(Symbol::down, std::string{}); + break; + case KeyType::left: + if (position > 0) + { + out << '\b' << std::flush; + --position; + } + break; + case KeyType::right: + if (position < currentLine.size()) + { + out << beforeInput + << currentLine[position] + << afterInput << std::flush; + ++position; + } + break; + case KeyType::ret: + { + out << "\r\n"; + auto cmd = currentLine; + currentLine.clear(); + position = 0; + return std::make_pair(Symbol::command, cmd); + } + break; + case KeyType::ascii: + { + const char c = static_cast(k.second); + if (c == '\t') + return std::make_pair(Symbol::tab, std::string()); + else + { + const auto pos = static_cast(position); + + // output the new char: + out << beforeInput << c; + // and the rest of the string: + out << std::string(currentLine.begin() + pos, currentLine.end()) + << afterInput; + + // go back to the original position + out << std::string(currentLine.size() - position, '\b') << std::flush; + + // update the buffer and cursor position: + currentLine.insert(currentLine.begin() + pos, c); + ++position; + } + + break; + } + case KeyType::canc: + { + if (position == currentLine.size()) + break; + + const auto pos = static_cast(position); + + // output the rest of the line + out << std::string(currentLine.begin() + pos + 1, currentLine.end()); + // remove last char + out << ' '; + // go back to the original position + out << std::string(currentLine.size() - position, '\b') << std::flush; + // remove the char from buffer + currentLine.erase(currentLine.begin() + pos); + break; + } + case KeyType::end: + { + const auto pos = static_cast(position); + + out << beforeInput + << std::string(currentLine.begin() + pos, currentLine.end()) + << afterInput << std::flush; + position = currentLine.size(); + break; + } + case KeyType::home: + { + out << std::string(position, '\b') << std::flush; + position = 0; + break; + } + case KeyType::ignored: + // TODO + break; + } + + return std::make_pair(Symbol::nothing, std::string()); + } + + private: + std::string currentLine; + std::size_t position = 0; // next writing position in currentLine + std::ostream &out; +}; + +} // namespace detail +} // namespace cli + +#endif // CLI_DETAIL_TERMINAL_H_ diff --git a/external/cli/detail/winkeyboard.h b/external/cli/detail/winkeyboard.h new file mode 100644 index 0000000..0ed159d --- /dev/null +++ b/external/cli/detail/winkeyboard.h @@ -0,0 +1,182 @@ +/******************************************************************************* + * CLI - A simple command line interface. + * Copyright (C) 2016-2021 Daniele Pallastrelli + * + * Boost Software License - Version 1.0 - August 17th, 2003 + * + * Permission is hereby granted, free of charge, to any person or organization + * obtaining a copy of the software and accompanying documentation covered by + * this license (the "Software") to use, reproduce, display, distribute, + * execute, and transmit the Software, and to prepare derivative works of the + * Software, and to permit third-parties to whom the Software is furnished to + * do so, all subject to the following: + * + * The copyright notices in the Software and this entire statement, including + * the above license grant, this restriction and the following disclaimer, + * must be included in all copies of the Software, in whole or in part, and + * all derivative works of the Software, unless such copies or derivative + * works are solely in the form of machine-executable object code generated by + * a source language processor. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT + * SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE + * FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ******************************************************************************/ + +#ifndef CLI_DETAIL_WINKEYBOARD_H_ +#define CLI_DETAIL_WINKEYBOARD_H_ + +#include +#include +#include +#include +#include +#include + +#include "inputdevice.h" + +#if !defined(NOMINMAX) +#define NOMINMAX 1 // prevent windows from defining min and max macros +#endif // !defined(NOMINMAX) +#include + +namespace cli +{ +namespace detail +{ + +class InputSource +{ +public: + + InputSource() + { + events[0] = CreateEvent(nullptr, FALSE, FALSE, nullptr); // Obtain a Windows handle to use to stop + events[1] = GetStdHandle(STD_INPUT_HANDLE); // Get a Windows handle to the keyboard input + } + + void WaitKbHit() + { + // Wait for either the timer to expire or a key press event + DWORD dwResult = WaitForMultipleObjects(2, events, false, INFINITE); + + if (dwResult == WAIT_FAILED) + { + // TODO + assert(false); + } + else + { + if (dwResult == WAIT_OBJECT_0) // WAIT_OBJECT_0 corresponds to the stop event + { + throw std::runtime_error("InputSource stop"); + } + else + { + return; + } + } + + // we can't reach this point + assert(false); + } + + void Stop() + { + SetEvent(events[0]); + } + +private: + HANDLE events[2]; +}; + +// + +class WinKeyboard : public InputDevice +{ +public: + explicit WinKeyboard(Scheduler& _scheduler) : + InputDevice(_scheduler), + servant([this]() noexcept { Read(); }) + { + } + ~WinKeyboard() override + { + is.Stop(); + servant.join(); + } + +private: + + void Read() noexcept + { + try + { + while (true) + { + auto k = Get(); + Notify(k); + } + } + catch (const std::exception&) + { + // nothing to do: just exit + } + } + + std::pair Get() + { + is.WaitKbHit(); + + int c = _getch(); + switch (c) + { + case EOF: + case 4: // EOT ie CTRL-D + case 26: // CTRL-Z + case 3: // CTRL-C + return std::make_pair(KeyType::eof, ' '); + break; + + case 224: // symbol + { + c = _getch(); + switch (c) + { + case 72: return std::make_pair(KeyType::up, ' '); + case 80: return std::make_pair(KeyType::down, ' '); + case 75: return std::make_pair(KeyType::left, ' '); + case 77: return std::make_pair(KeyType::right, ' '); + case 71: return std::make_pair(KeyType::home, ' '); + case 79: return std::make_pair(KeyType::end, ' '); + case 83: return std::make_pair(KeyType::canc, ' '); + default: return std::make_pair(KeyType::ignored, ' '); + } + } + case 8: + return std::make_pair(KeyType::backspace, c); + break; + case 13: + return std::make_pair(KeyType::ret, c); + break; + default: // hopefully ascii + { + const char ch = static_cast(c); + return std::make_pair(KeyType::ascii, ch); + } + } + return std::make_pair(KeyType::ignored, ' '); + } + + InputSource is; + std::thread servant; +}; + +} // namespace detail +} // namespace cli + +#endif // CLI_DETAIL_WINKEYBOARD_H_ diff --git a/external/cli/filehistorystorage.h b/external/cli/filehistorystorage.h new file mode 100644 index 0000000..1a59f95 --- /dev/null +++ b/external/cli/filehistorystorage.h @@ -0,0 +1,86 @@ +/******************************************************************************* + * CLI - A simple command line interface. + * Copyright (C) 2016-2021 Daniele Pallastrelli + * + * Boost Software License - Version 1.0 - August 17th, 2003 + * + * Permission is hereby granted, free of charge, to any person or organization + * obtaining a copy of the software and accompanying documentation covered by + * this license (the "Software") to use, reproduce, display, distribute, + * execute, and transmit the Software, and to prepare derivative works of the + * Software, and to permit third-parties to whom the Software is furnished to + * do so, all subject to the following: + * + * The copyright notices in the Software and this entire statement, including + * the above license grant, this restriction and the following disclaimer, + * must be included in all copies of the Software, in whole or in part, and + * all derivative works of the Software, unless such copies or derivative + * works are solely in the form of machine-executable object code generated by + * a source language processor. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT + * SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE + * FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ******************************************************************************/ + +#ifndef CLI_FILEHISTORYSTORAGE_H_ +#define CLI_FILEHISTORYSTORAGE_H_ + +#include "historystorage.h" +#include +#include + +namespace cli +{ + +class FileHistoryStorage : public HistoryStorage +{ +public: + explicit FileHistoryStorage(std::string _fileName, std::size_t size = 1000) : + maxSize(size), + fileName(std::move(_fileName)) + { + } + void Store(const std::vector& cmds) override + { + using dt = std::vector::difference_type; + auto commands = Commands(); + commands.insert(commands.end(), cmds.begin(), cmds.end()); + if (commands.size() > maxSize) + commands.erase( + commands.begin(), + commands.begin() + static_cast

(commands.size() - maxSize) + ); + std::ofstream f(fileName, std::ios_base::out); + for (const auto& line: commands) + f << line << '\n'; + } + std::vector Commands() const override + { + std::vector commands; + std::ifstream in(fileName); + if (in) + { + std::string line; + while (std::getline(in, line)) + commands.push_back(line); + } + return commands; + } + void Clear() override + { + std::ofstream f(fileName, std::ios_base::out | std::ios_base::trunc); + } + +private: + const std::size_t maxSize; + const std::string fileName; +}; + +} // namespace cli + +#endif // CLI_FILEHISTORYSTORAGE_H_ diff --git a/external/cli/historystorage.h b/external/cli/historystorage.h new file mode 100644 index 0000000..f5af2b1 --- /dev/null +++ b/external/cli/historystorage.h @@ -0,0 +1,54 @@ +/******************************************************************************* + * CLI - A simple command line interface. + * Copyright (C) 2016-2021 Daniele Pallastrelli + * + * Boost Software License - Version 1.0 - August 17th, 2003 + * + * Permission is hereby granted, free of charge, to any person or organization + * obtaining a copy of the software and accompanying documentation covered by + * this license (the "Software") to use, reproduce, display, distribute, + * execute, and transmit the Software, and to prepare derivative works of the + * Software, and to permit third-parties to whom the Software is furnished to + * do so, all subject to the following: + * + * The copyright notices in the Software and this entire statement, including + * the above license grant, this restriction and the following disclaimer, + * must be included in all copies of the Software, in whole or in part, and + * all derivative works of the Software, unless such copies or derivative + * works are solely in the form of machine-executable object code generated by + * a source language processor. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT + * SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE + * FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ******************************************************************************/ + +#ifndef CLI_HISTORYSTORAGE_H_ +#define CLI_HISTORYSTORAGE_H_ + +#include +#include + +namespace cli +{ + +class HistoryStorage +{ +public: + virtual ~HistoryStorage() = default; + // Store a vector of commands in the history storage + virtual void Store(const std::vector& commands) = 0; + // Returns all the commands stored + virtual std::vector Commands() const = 0; + // Clear the whole content of the storage + // After calling this method, Commands() returns the empty vector + virtual void Clear() = 0; +}; + +} // namespace cli + +#endif // CLI_HISTORYSTORAGE_H_ diff --git a/external/cli/loopscheduler.h b/external/cli/loopscheduler.h new file mode 100644 index 0000000..7bc3169 --- /dev/null +++ b/external/cli/loopscheduler.h @@ -0,0 +1,128 @@ +/******************************************************************************* + * CLI - A simple command line interface. + * Copyright (C) 2016-2021 Daniele Pallastrelli + * + * Boost Software License - Version 1.0 - August 17th, 2003 + * + * Permission is hereby granted, free of charge, to any person or organization + * obtaining a copy of the software and accompanying documentation covered by + * this license (the "Software") to use, reproduce, display, distribute, + * execute, and transmit the Software, and to prepare derivative works of the + * Software, and to permit third-parties to whom the Software is furnished to + * do so, all subject to the following: + * + * The copyright notices in the Software and this entire statement, including + * the above license grant, this restriction and the following disclaimer, + * must be included in all copies of the Software, in whole or in part, and + * all derivative works of the Software, unless such copies or derivative + * works are solely in the form of machine-executable object code generated by + * a source language processor. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT + * SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE + * FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ******************************************************************************/ + +#ifndef CLI_LOOPSCHEDULER_H_ +#define CLI_LOOPSCHEDULER_H_ + +#include +#include +#include +#include +#include "scheduler.h" + +namespace cli +{ + +/** + * @brief The LoopScheduler is a simple thread-safe scheduler + * + */ +class LoopScheduler : public Scheduler +{ +public: + LoopScheduler() = default; + ~LoopScheduler() override + { + Stop(); + } + + // non copyable + LoopScheduler(const LoopScheduler&) = delete; + LoopScheduler& operator=(const LoopScheduler&) = delete; + + void Stop() + { + std::lock_guard lck (mtx); + running = false; + cv.notify_all(); + } + + void Run() + { + while( ExecOne() ) {}; + } + + bool Stopped() const + { + std::lock_guard lck (mtx); + return !running; + } + + void Post(const std::function& f) override + { + std::lock_guard lck (mtx); + tasks.push(f); + cv.notify_all(); + } + + bool ExecOne() + { + std::function task; + { + std::unique_lock lck(mtx); + cv.wait(lck, [this](){ return !running || !tasks.empty(); }); + if (!running) + return false; + task = tasks.front(); + tasks.pop(); + } + + if (task) + task(); + + return true; + } + + bool PollOne() + { + std::function task; + { + std::lock_guard lck(mtx); + if (!running || tasks.empty()) + return false; + task = tasks.front(); + tasks.pop(); + } + + if (task) + task(); + + return true; + } + +private: + std::queue> tasks; + bool running{ true }; + mutable std::mutex mtx; + std::condition_variable cv; +}; + +} // namespace cli + +#endif // CLI_LOOPSCHEDULER_H_ diff --git a/external/cli/scheduler.h b/external/cli/scheduler.h new file mode 100644 index 0000000..6c74ec8 --- /dev/null +++ b/external/cli/scheduler.h @@ -0,0 +1,55 @@ +/******************************************************************************* + * CLI - A simple command line interface. + * Copyright (C) 2016-2021 Daniele Pallastrelli + * + * Boost Software License - Version 1.0 - August 17th, 2003 + * + * Permission is hereby granted, free of charge, to any person or organization + * obtaining a copy of the software and accompanying documentation covered by + * this license (the "Software") to use, reproduce, display, distribute, + * execute, and transmit the Software, and to prepare derivative works of the + * Software, and to permit third-parties to whom the Software is furnished to + * do so, all subject to the following: + * + * The copyright notices in the Software and this entire statement, including + * the above license grant, this restriction and the following disclaimer, + * must be included in all copies of the Software, in whole or in part, and + * all derivative works of the Software, unless such copies or derivative + * works are solely in the form of machine-executable object code generated by + * a source language processor. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT + * SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE + * FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ******************************************************************************/ + +#ifndef CLI_SCHEDULER_H_ +#define CLI_SCHEDULER_H_ + +#include + +namespace cli +{ + +/** + * A `Scheduler` represents an engine capable of running a task. + * Its method `Post` can be safely called from any thread to submit the task + * that will execute in an unspecified thread of execution as soon as possible + * (but in any case after the call to `Post` is terminated). + */ +class Scheduler +{ +public: + virtual ~Scheduler() = default; + + /// Submits a completion token or function object for execution. + virtual void Post(const std::function& f) = 0; +}; + +} // namespace cli + +#endif // CLI_SCHEDULER_H_ diff --git a/external/cli/standaloneasiocliasyncsession.h b/external/cli/standaloneasiocliasyncsession.h new file mode 100644 index 0000000..c5eb680 --- /dev/null +++ b/external/cli/standaloneasiocliasyncsession.h @@ -0,0 +1,40 @@ +/******************************************************************************* + * CLI - A simple command line interface. + * Copyright (C) 2016-2021 Daniele Pallastrelli + * + * Boost Software License - Version 1.0 - August 17th, 2003 + * + * Permission is hereby granted, free of charge, to any person or organization + * obtaining a copy of the software and accompanying documentation covered by + * this license (the "Software") to use, reproduce, display, distribute, + * execute, and transmit the Software, and to prepare derivative works of the + * Software, and to permit third-parties to whom the Software is furnished to + * do so, all subject to the following: + * + * The copyright notices in the Software and this entire statement, including + * the above license grant, this restriction and the following disclaimer, + * must be included in all copies of the Software, in whole or in part, and + * all derivative works of the Software, unless such copies or derivative + * works are solely in the form of machine-executable object code generated by + * a source language processor. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT + * SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE + * FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ******************************************************************************/ + +#ifndef CLI_STANDALONEASIOCLIASYNCSESSION_H_ +#define CLI_STANDALONEASIOCLIASYNCSESSION_H_ + +#include "detail/genericcliasyncsession.h" +#include "detail/standaloneasiolib.h" + + +namespace cli { using StandaloneAsioCliAsyncSession = detail::GenericCliAsyncSession; } + +#endif // CLI_STANDALONEASIOCLIASYNCSESSION_H_ + diff --git a/external/cli/standaloneasioremotecli.h b/external/cli/standaloneasioremotecli.h new file mode 100644 index 0000000..2ac50d9 --- /dev/null +++ b/external/cli/standaloneasioremotecli.h @@ -0,0 +1,40 @@ +/******************************************************************************* + * CLI - A simple command line interface. + * Copyright (C) 2016-2021 Daniele Pallastrelli + * + * Boost Software License - Version 1.0 - August 17th, 2003 + * + * Permission is hereby granted, free of charge, to any person or organization + * obtaining a copy of the software and accompanying documentation covered by + * this license (the "Software") to use, reproduce, display, distribute, + * execute, and transmit the Software, and to prepare derivative works of the + * Software, and to permit third-parties to whom the Software is furnished to + * do so, all subject to the following: + * + * The copyright notices in the Software and this entire statement, including + * the above license grant, this restriction and the following disclaimer, + * must be included in all copies of the Software, in whole or in part, and + * all derivative works of the Software, unless such copies or derivative + * works are solely in the form of machine-executable object code generated by + * a source language processor. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT + * SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE + * FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ******************************************************************************/ + +#ifndef CLI_STANDALONEASIOREMOTECLI_H_ +#define CLI_STANDALONEASIOREMOTECLI_H_ + +#include "detail/standaloneasiolib.h" +#include "detail/genericasioremotecli.h" + +namespace cli { using StandaloneAsioCliTelnetServer = detail::CliGenericTelnetServer; } + + +#endif // CLI_STANDALONEASIOREMOTECLI_H_ + diff --git a/external/cli/standaloneasioscheduler.h b/external/cli/standaloneasioscheduler.h new file mode 100644 index 0000000..fade453 --- /dev/null +++ b/external/cli/standaloneasioscheduler.h @@ -0,0 +1,38 @@ +/******************************************************************************* + * CLI - A simple command line interface. + * Copyright (C) 2016-2021 Daniele Pallastrelli + * + * Boost Software License - Version 1.0 - August 17th, 2003 + * + * Permission is hereby granted, free of charge, to any person or organization + * obtaining a copy of the software and accompanying documentation covered by + * this license (the "Software") to use, reproduce, display, distribute, + * execute, and transmit the Software, and to prepare derivative works of the + * Software, and to permit third-parties to whom the Software is furnished to + * do so, all subject to the following: + * + * The copyright notices in the Software and this entire statement, including + * the above license grant, this restriction and the following disclaimer, + * must be included in all copies of the Software, in whole or in part, and + * all derivative works of the Software, unless such copies or derivative + * works are solely in the form of machine-executable object code generated by + * a source language processor. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT + * SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE + * FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ******************************************************************************/ + +#ifndef CLI_STANDALONEASIOSCHEDULER_H_ +#define CLI_STANDALONEASIOSCHEDULER_H_ + +#include "detail/genericasioscheduler.h" +#include "detail/standaloneasiolib.h" + +namespace cli { using StandaloneAsioScheduler = detail::GenericAsioScheduler; } + +#endif // CLI_STANDALONEASIOSCHEDULER_H_ diff --git a/external/cli/volatilehistorystorage.h b/external/cli/volatilehistorystorage.h new file mode 100644 index 0000000..d357c01 --- /dev/null +++ b/external/cli/volatilehistorystorage.h @@ -0,0 +1,68 @@ +/******************************************************************************* + * CLI - A simple command line interface. + * Copyright (C) 2016-2021 Daniele Pallastrelli + * + * Boost Software License - Version 1.0 - August 17th, 2003 + * + * Permission is hereby granted, free of charge, to any person or organization + * obtaining a copy of the software and accompanying documentation covered by + * this license (the "Software") to use, reproduce, display, distribute, + * execute, and transmit the Software, and to prepare derivative works of the + * Software, and to permit third-parties to whom the Software is furnished to + * do so, all subject to the following: + * + * The copyright notices in the Software and this entire statement, including + * the above license grant, this restriction and the following disclaimer, + * must be included in all copies of the Software, in whole or in part, and + * all derivative works of the Software, unless such copies or derivative + * works are solely in the form of machine-executable object code generated by + * a source language processor. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT + * SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE + * FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + ******************************************************************************/ + +#ifndef CLI_VOLATILEHISTORYSTORAGE_H_ +#define CLI_VOLATILEHISTORYSTORAGE_H_ + +#include "historystorage.h" +#include + +namespace cli +{ + +class VolatileHistoryStorage : public HistoryStorage +{ + public: + explicit VolatileHistoryStorage(std::size_t size = 1000) : maxSize(size) {} + void Store(const std::vector& cmds) override + { + using dt = std::deque::difference_type; + commands.insert(commands.end(), cmds.begin(), cmds.end()); + if (commands.size() > maxSize) + commands.erase( + commands.begin(), + commands.begin()+static_cast
(commands.size()-maxSize) + ); + } + std::vector Commands() const override + { + return std::vector(commands.begin(), commands.end()); + } + void Clear() override + { + commands.clear(); + } + private: + const std::size_t maxSize; + std::deque commands; +}; + +} // namespace cli + +#endif // CLI_VOLATILEHISTORYSTORAGE_H_ diff --git a/meson.build b/meson.build index 54ac780..7818836 100644 --- a/meson.build +++ b/meson.build @@ -1,17 +1,21 @@ project( 'mmi', ['c', 'cpp'], - version : '1.0.2', - license : 'MIT', - default_options : ['b_pie=true'] + version : '2.0.0', + license : 'Apache-2.0', + default_options : ['b_pie=true', 'cpp_std=c++17'] ) mmi_version = meson.project_version().split('.') mmi_prefix = get_option('prefix') mmi_prefix_bindir = join_paths(mmi_prefix, get_option('bindir')) mmi_prefix_libdir = join_paths(mmi_prefix, get_option('libdir')) +mmi_prefix_plugindir = join_paths(mmi_prefix, get_option('datadir'), 'mmi', 'plugins') +mmi_prefix_scriptdir = join_paths(mmi_prefix, get_option('datadir'), 'mmi', 'scripts') pkgconfig = import('pkgconfig') +subdir('capi') subdir('src') +subdir('plugins') subdir('tests') diff --git a/meson_options.txt b/meson_options.txt deleted file mode 100644 index e69de29..0000000 diff --git a/packaging/mmi.spec b/packaging/mmi.spec index e00ddf9..84f31e4 100644 --- a/packaging/mmi.spec +++ b/packaging/mmi.spec @@ -1,9 +1,9 @@ %define USE_GCOV 0 Name: mmi -Version: 1.0.2 +Version: 2.0.1 Release: 0 -Summary: Multi-modal Interaction Framework Library +Summary: Multi-modal Interaction Framework License: MIT URL: http://www.tizen.org Source: %{name}-%{version}.tar.xz @@ -11,30 +11,50 @@ Source1004: %{name}.manifest BuildRequires: meson BuildRequires: tidl -BuildRequires: pkgconfig(libtzplatform-config) -BuildRequires: pkgconfig(bundle) -BuildRequires: pkgconfig(gio-2.0) +BuildRequires: pkgconfig(libtzplatform-config) +BuildRequires: pkgconfig(bundle) +BuildRequires: pkgconfig(gio-2.0) BuildRequires: pkgconfig(glib-2.0) BuildRequires: pkgconfig(dlog) BuildRequires: pkgconfig(rpc-port) BuildRequires: pkgconfig(ecore) - +BuildRequires: pkgconfig(capi-media-audio-io) +BuildRequires: pkgconfig(capi-media-sound-manager) +BuildRequires: pkgconfig(capi-media-camera) +BuildRequires: pkgconfig(libaurum) +BuildRequires: pkgconfig(vconf) +BuildRequires: pkgconfig(json-glib-1.0) +BuildRequires: pkgconfig(capi-media-vision) +BuildRequires: pkgconfig(capi-media-image-util) +BuildRequires: pkgconfig(opencv) #Build dependencies for tests BuildRequires: pkgconfig(gmock) %description -MMI(Multi-modal Interaction) Framework Library +MMI(Multi-modal Interaction) Framework + +%package plugins +Summary: Plugins for MMI Framework +Group: System/Libraries +%description plugins +Plugins for MMI Framework + +%package cli +Summary: CLI for MMI Framework +Group: System/Libraries +%description cli +CLI for MMI Framework %package devel -Summary: Development package for MMI Framework Library +Summary: Development package for MMI Framework Group: Development/Libraries Requires: %{name} = %{version}-%{release} Requires: pkgconfig(rpc-port) %description devel -Development package for MMI Framework Library +Development package for MMI Framework %package tests -Summary: Testcases for MMI Framework Library +Summary: Testcases for MMI Framework Group: System/Libraries %description tests Testcases for testing MMI Framework APIs @@ -45,7 +65,9 @@ cp %{SOURCE1004} . #generate mmi-proxy using TIDL Compiler tidlc -p -l C -i tidl/mmi.tidl -o mmi_proxy -mv mmi_proxy.* src +mv mmi_proxy.* src/mmi +tidlc -s -l C -i tidl/mmi.tidl -o mmi_stub +mv mmi_stub.* src/mmi-manager %build %if "%{USE_GCOV}" == "1" @@ -53,15 +75,22 @@ CFLAGS+=" -fprofile-arcs -ftest-coverage -DTIZEN_TEST_GCOV" CXXFLAGS+=" -fprofile-arcs -ftest-coverage -DTIZEN_TEST_GCOV" LDFLAGS+=" -lgcov" %endif + meson setup --prefix=/usr \ --bindir %{_bindir} \ --libdir %{_libdir} \ + --datadir %{_datadir} \ builddir ninja -C builddir all %install DESTDIR=%{buildroot} ninja -C builddir install +%check +%if "%{_mmi_test_enable}" == "true" +ninja -C builddir test +%endif + %post -p /sbin/ldconfig %postun -p /sbin/ldconfig @@ -70,6 +99,23 @@ DESTDIR=%{buildroot} ninja -C builddir install %defattr(-,root,root,-) %license COPYING %{_libdir}/*.so* +%{_bindir}/mmi-manager +%{_datadir}/packages/mmi-manager.xml + +%files plugins +%manifest %{name}.manifest +%defattr(-,root,root,-) +%license COPYING +%{_datadir}/mmi/plugins/*.so* +%{_datadir}/mmi/scripts/*.mws +%{_datadir}/face_recognition/* + +%files cli +%manifest %{name}.manifest +%defattr(-,root,root,-) +%license COPYING +%{_bindir}/mmi-cli +%{_bindir}/mmi-cli-node-tester %files devel %manifest %{name}.manifest @@ -82,4 +128,5 @@ DESTDIR=%{buildroot} ninja -C builddir install %defattr(-,root,root,-) %license COPYING %{_bindir}/mmi-tests +%{_bindir}/mmi-manager-tests diff --git a/plugins/meson.build b/plugins/meson.build new file mode 100644 index 0000000..c7ea24c --- /dev/null +++ b/plugins/meson.build @@ -0,0 +1,2 @@ +subdir('nodes') +subdir('workflows') diff --git a/plugins/nodes/camera/meson.build b/plugins/nodes/camera/meson.build new file mode 100644 index 0000000..add475c --- /dev/null +++ b/plugins/nodes/camera/meson.build @@ -0,0 +1,27 @@ +mmi_module_camera_library_srcs = [ + 'mmi-module-camera.cpp', + 'mmi-module-camera-preview.cpp', + 'mmi-module-camera-preview.h', + ] + +dlog_dep = dependency('dlog', method : 'pkg-config') +camera_dep = dependency('capi-media-camera', method : 'pkg-config') + +mmi_module_camera_deps = [ + mmi_declared_dep, + dlog_dep, + camera_dep, + ] + +mmi_module_camera_include_dirs = include_directories( + '.', + '../../../capi/', + ) + +mmi_module_camera_library = library('mmi_module_camera', + mmi_module_camera_library_srcs, + include_directories : [ mmi_module_camera_include_dirs ], + dependencies : [mmi_module_camera_deps], + install_dir : mmi_prefix_plugindir, + install : true + ) diff --git a/plugins/nodes/camera/mmi-module-camera-preview.cpp b/plugins/nodes/camera/mmi-module-camera-preview.cpp new file mode 100644 index 0000000..bdc4a30 --- /dev/null +++ b/plugins/nodes/camera/mmi-module-camera-preview.cpp @@ -0,0 +1,148 @@ +/* + * Copyright (c) 2023 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 "mmi-module-camera-preview.h" + +#ifdef LOG_TAG +#undef LOG_TAG +#endif +#define LOG_TAG "MMI-MODULE-CAMERA" + +int Camera::frame_num = 0; + +Camera::Camera() +{ +} + +Camera::~Camera() +{ +} + +int Camera::initialize() +{ + if (CAMERA_ERROR_NONE != camera_create(CAMERA_DEVICE_CAMERA0, &m_camera)) { + _E("[Camera] Fail to create camera"); + return MMI_ERROR_OPERATION_FAILED; + } + + if (CAMERA_ERROR_NONE != camera_set_display(m_camera, CAMERA_DISPLAY_TYPE_NONE, NULL)){ + _E("[Camera] Fail to set display"); + return MMI_ERROR_OPERATION_FAILED; + } + + camera_set_preview_format(m_camera, CAMERA_PIXEL_FORMAT_I420); + camera_set_preview_resolution(m_camera, 640, 480); + camera_attr_set_preview_fps(m_camera, CAMERA_ATTR_FPS_15); + camera_set_state_changed_cb(m_camera, _camera_state_changed_cb, this); + camera_set_preview_cb(m_camera, _camera_preview_cb, this); + + return MMI_ERROR_NONE; +} + +int Camera::deinitialize() +{ + + camera_unset_preview_cb(m_camera); + + if (CAMERA_ERROR_NONE != camera_destroy(m_camera)) { + _E("[Camera] Fail to distroy camera"); + return MMI_ERROR_OPERATION_FAILED; + } + return MMI_ERROR_NONE; +} + +int Camera::start() +{ + int width = 0; + int height = 0; + camera_attr_fps_e fps; + + _D("Camera::start()"); + + if (CAMERA_ERROR_NONE != camera_start_preview(m_camera)) { + _E("[Camera] Fail to start preview"); + return MMI_ERROR_OPERATION_FAILED; + } + + camera_attr_get_preview_fps(m_camera, &fps); + camera_get_preview_resolution(m_camera, &width, &height); + + _I("camera info -> fps :%d, width : %d, height : %d", fps, width, height); + + return MMI_ERROR_NONE; +} + +int Camera::stop() +{ + _D("Camera::stop()"); + + if (CAMERA_ERROR_NONE != camera_stop_preview(m_camera)) { + _E("[Camera] Fail to stop preview"); + return MMI_ERROR_OPERATION_FAILED; + } + + return MMI_ERROR_NONE; +} + +void Camera::set_preview_callback(camera_data_callback callback) +{ + m_camera_callback = callback; +} + +void Camera::set_node_instance(mmi_node_instance_h node_instance) +{ + m_node_instance = node_instance; +} + +void Camera::_camera_preview_cb(camera_preview_data_s *frame, void *user_data) +{ + int size = 0; + unsigned long timestamp = 0; + unsigned char *pData = nullptr; + + Camera *camera = static_cast(user_data); + camera_attr_get_preview_frame_timestamp(camera->m_camera, ×tamp); + + switch (frame->format) { + case CAMERA_PIXEL_FORMAT_NV12: + _D("NV12 p[%p,%p], size[%u,%u]", frame->data.double_plane.y, frame->data.double_plane.uv, + frame->data.double_plane.y_size, frame->data.double_plane.uv_size); + pData = frame->data.double_plane.y; + size = frame->data.double_plane.y_size + frame->data.double_plane.uv_size; + + break; + case CAMERA_PIXEL_FORMAT_I420: + _D("I420 p[%p,%p,%p], size[%u,%u,%u]", frame->data.triple_plane.y, frame->data.triple_plane.u, frame->data.triple_plane.v, + frame->data.triple_plane.y_size, frame->data.triple_plane.u_size, frame->data.triple_plane.v_size); + pData = frame->data.triple_plane.y; + size = frame->data.triple_plane.y_size + frame->data.triple_plane.u_size + frame->data.triple_plane.v_size; + + break; + default: + _E("invalid format : %u", frame->format); + break; + + } + Camera::frame_num++; + + if (camera->m_camera_callback && (Camera::frame_num % 3 == 0) && pData) { + camera->m_camera_callback(pData, size, timestamp, camera->m_node_instance); + } +} + diff --git a/plugins/nodes/camera/mmi-module-camera-preview.h b/plugins/nodes/camera/mmi-module-camera-preview.h new file mode 100644 index 0000000..17ba9d1 --- /dev/null +++ b/plugins/nodes/camera/mmi-module-camera-preview.h @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2023 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#pragma once + +#include +#include + +#include +#include + +using camera_data_callback = std::function; + +class Camera { +public: + static int frame_num; + + Camera(); + ~Camera(); + + int initialize(); + int deinitialize(); + int start(); + int stop(); + + void set_preview_callback(camera_data_callback callback); + void set_node_instance(mmi_node_instance_h node_instance); + + static void _camera_state_changed_cb(camera_state_e previous, camera_state_e current, bool by_policy, void *user_data) + { + _D("camera state changed %d -> %d", previous, current); + } + + static void _camera_interrupted_cb(camera_policy_e policy, camera_state_e previous, camera_state_e current, + void *user_data) + { + _D("camera interrupted callback called[state %d -> %d, policy %d]", previous, current, policy); + } + +private: + camera_h m_camera{nullptr}; + camera_data_callback m_camera_callback{nullptr}; + mmi_node_instance_h m_node_instance{nullptr}; + static void _camera_preview_cb(camera_preview_data_s *frame, void *user_data); +}; diff --git a/plugins/nodes/camera/mmi-module-camera.cpp b/plugins/nodes/camera/mmi-module-camera.cpp new file mode 100644 index 0000000..9ecea3a --- /dev/null +++ b/plugins/nodes/camera/mmi-module-camera.cpp @@ -0,0 +1,185 @@ +#include +#include +#include +#include + +#include +#include +#include +#include "mmi-module-camera-preview.h" + +#ifdef LOG_TAG +#undef LOG_TAG +#endif +#define LOG_TAG "MMI-MODULE-CAMERA" + +static std::map g_camera_map; + +void feed_preview_callback(void *data, size_t size, unsigned long timestamp, void *user_data) +{ + mmi_node_instance_h instance = (mmi_node_instance_h)user_data; + mmi_port_instance_h port_instance = nullptr; + mmi_data_h output_data = nullptr; + + mmi_node_instance_find_port(instance, MMI_PORT_TYPE_OUT, "VIDEO", &port_instance); + mmi_data_create_video(data, size, &output_data); + mmi_port_instance_generate_output(port_instance, output_data); + mmi_data_destroy(output_data); + _D("frame num : %d, timestamp : %lu", Camera::frame_num, timestamp); +} + +static int node_initialized_cb(mmi_node_instance_h instance) +{ + _D("[Camera] Node initialize callback is called for %p", instance); + + if (nullptr == instance) { + _E("[Camera] node instance is null"); + return MMI_ERROR_INVALID_PARAMETER; + } + Camera *camera = new(std::nothrow) Camera(); + if (nullptr == camera) { + _E("[ERROR] Failed to create instance"); + return MMI_ERROR_OUT_OF_MEMORY; + } + + camera->initialize(); + camera->set_node_instance(instance); + camera->set_preview_callback(feed_preview_callback); + g_camera_map.insert(std::pair(instance, camera)); + + return MMI_ERROR_NONE; +} + +static int node_deinitialized_cb(mmi_node_instance_h instance) +{ + _D("[Camera] Node deinitialize callback is called for %p", instance); + if (nullptr == instance) { + _E("[Camera] node instance is null"); + return MMI_ERROR_INVALID_PARAMETER; + } + + try { + Camera *camera = g_camera_map[instance]; + camera->deinitialize(); + g_camera_map.erase(instance); + delete camera; + } catch (std::out_of_range &e) { + _E("[Camera] failed to find recorder instance"); + return MMI_ERROR_INVALID_PARAMETER; + } + + return MMI_ERROR_NONE; +} + +static int node_attribute_set_cb(mmi_node_instance_h instance, mmi_attribute_h attribute) +{ + _D("Node attribute set callback is called for %p", instance); + + if (nullptr == instance) { + _E("[CAMERA] node instance is null"); + return MMI_ERROR_INVALID_PARAMETER; + } + + if (nullptr == attribute) { + _E("[Camera] attribute is null"); + return MMI_ERROR_INVALID_PARAMETER; + } + + // TODO: set attribute "UNFOCUSED_ONLY" + + return MMI_ERROR_NONE; +} + +static int node_activated_cb(mmi_node_instance_h instance) +{ + _D("[Camera] Node activate callback is called for %p", instance); + + if (nullptr == instance) { + _E("[Camera] node instance is null"); + return MMI_ERROR_INVALID_PARAMETER; + } + + try { + Camera *camera = g_camera_map[instance]; + camera->start(); + } catch (std::out_of_range &e) { + _E("[Camera] failed to find recorder instance"); + return MMI_ERROR_INVALID_PARAMETER; + } + + return MMI_ERROR_NONE; +} + +static int node_deactivated_cb(mmi_node_instance_h instance) +{ + _D("[Camera] Node deactivate callback is called for %p", instance); + + if (nullptr == instance) { + _E("[Camera] node instance is null"); + return MMI_ERROR_INVALID_PARAMETER; + } + + try { + Camera *camera = g_camera_map[instance]; + camera->stop(); + } catch (std::out_of_range &e) { + _E("[Camera] failed to find recorder instance"); + return MMI_ERROR_INVALID_PARAMETER; + } + + return MMI_ERROR_NONE; +} + +static int node_signal_received_cb(mmi_node_instance_h instance, mmi_signal_h signal) +{ + _D("Node signal received callback is called for %p", instance); + return MMI_ERROR_NONE; +} + +static int port_output_format_requested_cb(mmi_port_instance_h instance, const char *format) { + _D("Port output format request callback is called for %p", instance); + return MMI_ERROR_NONE; +} + +// TODO: It must be determined how to receive input data from the client +static int port_input_data_received_cb(mmi_port_instance_h instance, mmi_data_h data) { + _D("Port input data callback is called for %p", instance); + return MMI_ERROR_NONE; +} + +extern "C" { + + EXPORT_API void mmi_plugin_module_get_node_list() + { + mmi_node_callbacks node_callbacks { + node_initialized_cb, + node_deinitialized_cb, + node_attribute_set_cb, + node_activated_cb, + node_deactivated_cb, + node_signal_received_cb + }; + + mmi_port_callbacks camera_port_callbacks { + port_output_format_requested_cb, + port_input_data_received_cb + }; + + mmi_port_h camera_port = nullptr; + mmi_port_create(&camera_port); + mmi_port_set_name(camera_port, "VIDEO"); + mmi_port_set_type(camera_port, MMI_PORT_TYPE_OUT); + mmi_port_set_data_type(camera_port, MMI_DATA_TYPE_VIDEO); + mmi_port_set_callbacks(camera_port, camera_port_callbacks); + + mmi_node_h camera_node = nullptr; + mmi_standard_node_create_source(MMI_STANDARD_NODE_SOURCE_TYPE_CAMERA, &camera_node); + mmi_node_add_port(camera_node, camera_port); + mmi_node_set_callbacks(camera_node, node_callbacks); + + mmi_node_register(camera_node); + + mmi_node_destroy(camera_node); + } + +} // extern "C" diff --git a/plugins/nodes/face-recognition/data/recognizerInfo.json b/plugins/nodes/face-recognition/data/recognizerInfo.json new file mode 100644 index 0000000..dc7ba76 --- /dev/null +++ b/plugins/nodes/face-recognition/data/recognizerInfo.json @@ -0,0 +1,15 @@ +{ + "maxFaceCount": 2, + "faceInfo": [ + { + "faceId": "test4", + "faceName": "test4", + "enrollFilePath": "/usr/share/face_recognition/enroll/soo_1.jpg" + }, + { + "faceId": "test2", + "faceName": "test2", + "enrollFilePath": "/usr/share/face_recognition/enroll/soo_4.jpg" + } + ] +} \ No newline at end of file diff --git a/plugins/nodes/face-recognition/meson.build b/plugins/nodes/face-recognition/meson.build new file mode 100644 index 0000000..6e48678 --- /dev/null +++ b/plugins/nodes/face-recognition/meson.build @@ -0,0 +1,37 @@ +mmi_module_fr_library_srcs = [ + 'mmi-module-fr.cpp', + 'mmi-module-fr-data-manager.h', + 'mmi-module-fr-data-manager.cpp' + ] + +dlog_dep = dependency('dlog', method : 'pkg-config') +ecore_dep = dependency('ecore', method : 'pkg-config') +json_dep = dependency('json-glib-1.0', method : 'pkg-config') +media_vision_dep = dependency('capi-media-vision', method : 'pkg-config') +image_util_dep = dependency('capi-media-image-util', method : 'pkg-config') +opencv_dep = dependency('opencv', method : 'pkg-config') + +mmi_module_fr_deps = [ + mmi_declared_dep, + dlog_dep, + ecore_dep, + json_dep, + media_vision_dep, + image_util_dep, + opencv_dep, + ] + +mmi_module_fr_include_dirs = include_directories( + '.', + '../../../capi/', + ) + +mmi_module_fr_library = library('mmi_module_fr', + mmi_module_fr_library_srcs, + include_directories : [ mmi_module_fr_include_dirs ], + dependencies : [mmi_module_fr_deps], + install_dir : mmi_prefix_plugindir, + install : true + ) + +install_data('data/recognizerInfo.json', install_dir : '/usr/share/face_recognition/') diff --git a/plugins/nodes/face-recognition/mmi-module-fr-data-manager.cpp b/plugins/nodes/face-recognition/mmi-module-fr-data-manager.cpp new file mode 100644 index 0000000..10b5f8c --- /dev/null +++ b/plugins/nodes/face-recognition/mmi-module-fr-data-manager.cpp @@ -0,0 +1,234 @@ +/* + * Copyright (c) 2023 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +// FIXME: temporary module for testing +// FIXME: remove this module after register/recognize fw/app is ready +#include +#include + +#include "mmi-module-fr-data-manager.h" + +#ifdef LOG_TAG +#undef LOG_TAG +#endif +#define LOG_TAG "MMI-MODULE-FACE-RECOGNITION" + +DataManager::DataManager() +{ + m_info_file_path = std::string("/usr/share/face_recognition/recognizerInfo.json"); + m_mutex = new(std::nothrow) std::mutex(); + if (nullptr == m_mutex) { + _E("[FR DATAMGR] Failed to create mutex"); + return; + } + load_info(); +} + +DataManager::~DataManager() +{ + delete m_mutex; + m_mutex = nullptr; +} + +void DataManager::load_info() +{ + _D("[FR DATAMGR] load info "); + std::lock_guard lock(*m_mutex); + + JsonParser *parser = nullptr; + parser = json_parser_new(); + if (nullptr == parser) { + _E("[FR DATAMGR] Failed to create json parser"); + return; + } + + GError *error = nullptr; + if (!json_parser_load_from_file(parser, m_info_file_path.c_str(), &error)) { + _E("[FR DATAMGR] Failed to load json file: %s", error->message); + g_error_free(error); + return; + } + + JsonObject *recognizer_info = json_node_get_object(json_parser_get_root(parser)); + if (nullptr == recognizer_info) { + _E("[FR DATAMGR] Failed to get root object"); + g_object_unref(parser); + parser = nullptr; + return; + } + + m_max_face_count = json_object_get_int_member(recognizer_info, "maxFaceCount"); + _D("[FR DATAMGR] max face count is %d", m_max_face_count); + + JsonArray *face_info = json_object_get_array_member(recognizer_info, "faceInfo"); + if (nullptr == face_info) { + _E("[FR DATAMGR] Failed to get faceInfo object"); + g_object_unref(parser); + parser = nullptr; + return; + } + + for (int i = 0; i < json_array_get_length(face_info); i++) { + JsonObject *face = json_array_get_object_element(face_info, i); + if (nullptr == face) { + _E("[FR DATAMGR] Failed to get face object"); + g_object_unref(parser); + parser = nullptr; + return; + } + + const gchar *id = json_object_get_string_member(face, "faceId"); + const gchar *name = json_object_get_string_member(face, "faceName"); + const gchar *file_path = json_object_get_string_member(face, "enrollFilePath"); + + FaceInfo info; + info.id = std::string(id); + info.name = std::string(name); + info.file_path = std::string(file_path); + _D("[FR DATAMGR] face info: id: %s, name: %s, file_path: %s", info.id.c_str(), info.name.c_str(), info.file_path.c_str()); + m_face_list.push_back(info); + } + + g_object_unref(parser); + parser = nullptr; +} + +void DataManager::save_info() +{ + _D("[FR DATAMGR] save info"); + std::lock_guard lock(*m_mutex); + JsonBuilder *builder = json_builder_new(); + + json_builder_begin_object(builder); + + json_builder_set_member_name(builder, "maxFaceCount"); + json_builder_add_int_value(builder, m_max_face_count); + + json_builder_set_member_name(builder, "faceInfo"); + + json_builder_begin_array(builder); + for (int i = 0; i < m_face_list.size(); i++) { + json_builder_begin_object(builder); + json_builder_set_member_name(builder, "faceId"); + json_builder_add_string_value(builder, m_face_list[i].id.c_str()); + json_builder_set_member_name(builder, "faceName"); + json_builder_add_string_value(builder, m_face_list[i].name.c_str()); + json_builder_set_member_name(builder, "enrollFilePath"); + json_builder_add_string_value(builder, m_face_list[i].file_path.c_str()); + json_builder_end_object(builder); + } + json_builder_end_array(builder); + + json_builder_end_object(builder); + + JsonGenerator *generator = json_generator_new(); + json_generator_set_root(generator, json_builder_get_root(builder)); + json_generator_set_pretty(generator, TRUE); + + // save file and check return value + gboolean ret = json_generator_to_file(generator, m_info_file_path.c_str(), NULL); + if (FALSE == ret) { + _E("[FR DATAMGR] Failed to save json file"); + } + + if (builder) { + g_object_unref(builder); + builder = nullptr; + } + if (generator) { + g_object_unref(generator); + generator = nullptr; + } +} + +void DataManager::add_face(FaceInfo info) +{ + _D("[FR DATAMGR] add face info"); + { + std::lock_guard lock(*m_mutex); + m_face_list.push_back(info); + for (auto &i : m_face_list) { + _D("[FR DATAMGR] face info: id: %s, name: %s, model: %s", i.id.c_str(), i.name.c_str(), i.file_path.c_str()); + } + } + + save_info(); +} + +void DataManager::delete_face(std::string id) +{ + _D("[FR DATAMGR] delete face info"); + { + std::lock_guard lock(*m_mutex); + for (int i = 0; i < m_face_list.size(); i++) { + if (m_face_list[i].id == id) { + m_face_list.erase(m_face_list.begin() + i); + break; + } + } + } + save_info(); +} + +void DataManager::update_face(FaceInfo info) +{ + _D("[FR DATAMGR] update face info"); + { + std::lock_guard lock(*m_mutex); + for (int i = 0; i < m_face_list.size(); i++) { + if (m_face_list[i].id == info.id) { + m_face_list[i].name = info.name; + m_face_list[i].file_path = info.file_path; + break; + } + } + } + + save_info(); +} + +int DataManager::get_face_count() +{ + _D("[FR DATAMGR] get face count"); + std::lock_guard lock(*m_mutex); + int count = m_face_list.size(); + return count; +} + +FaceInfo DataManager::get_face_info(std::string id) +{ + _D("[FR DATAMGR] get face info"); + std::lock_guard lock(*m_mutex); + FaceInfo info; + for (int i = 0; i < m_face_list.size(); i++) { + if (m_face_list[i].id == id) { + info = m_face_list[i]; + break; + } + } + + return info; +} + +std::vector DataManager::get_face_list() +{ + _D("[FR DATAMGR] get face list"); + std::lock_guard lock(*m_mutex); + + std::vector list = m_face_list; + return list; +} diff --git a/plugins/nodes/face-recognition/mmi-module-fr-data-manager.h b/plugins/nodes/face-recognition/mmi-module-fr-data-manager.h new file mode 100644 index 0000000..000de19 --- /dev/null +++ b/plugins/nodes/face-recognition/mmi-module-fr-data-manager.h @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2023 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#pragma once + +#include +#include +#include + +struct FaceInfo { + std::string id; + std::string name; + std::string file_path; +}; + +class DataManager { +private: + void load_info(); + void save_info(); + +public: + DataManager(); + ~DataManager(); + static DataManager& get_instance() { + static DataManager instance; + return instance; + } + + std::vector get_face_list(); + FaceInfo get_face_info(std::string id); + int get_face_count(); + void add_face(FaceInfo info); + void delete_face(std::string id); + void update_face(FaceInfo info); + +private: + static DataManager m_instance; + std::vector m_face_list; + int m_max_face_count{0}; + std::string m_info_file_path{}; + std::mutex* m_mutex{nullptr}; +}; diff --git a/plugins/nodes/face-recognition/mmi-module-fr.cpp b/plugins/nodes/face-recognition/mmi-module-fr.cpp new file mode 100644 index 0000000..0266f6a --- /dev/null +++ b/plugins/nodes/face-recognition/mmi-module-fr.cpp @@ -0,0 +1,595 @@ +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include "mv_face_recognition_internal.h" +#include + +#include +#include + +#include +#include +#include +#include + +#include "mmi-module-fr-data-manager.h" + +#ifdef LOG_TAG +#undef LOG_TAG +#endif +#define LOG_TAG "MMI-MODULE-FACE-RECOGNITION" + +#define FRAME_WIDTH 640 +#define FRAME_HEIGHT 480 + +static const char *INPUT_PORT_NAME_VIDEO = "VIDEO"; +static const char *OUTPUT_PORT_NAME_MATCHED_FACES = "MATCHED_FACES"; +static const char *OUTPUT_RESULT_ELEMENT_NODE = "fromNode"; +static const char *OUTPUT_RESULT_NODE_NAME = "FACE_RECOGNITION"; +static const char *OUTPUT_RESULT_ELEMENT_CANDIDATE = "recognizedCandidates"; + +static mv_face_recognition_h g_face_recog_h = nullptr; +static DataManager *g_data_manager = nullptr; + +struct _facedata_s +{ + unsigned char *id; + unsigned int width; + unsigned int height; + unsigned int channel; + unsigned int size; + unsigned char *data; +}; + +typedef struct _facedata_s facedata_s; +static std::map g_registered_faces_map; +static std::map g_input_port_to_instance_map; + +int register_face_from_file(const char *file_path, const char *file_name) +{ + unsigned char *data_buffer = NULL; + size_t buffer_size = 0; + unsigned int width = FRAME_WIDTH; + unsigned int height = FRAME_HEIGHT; + image_util_decode_h img_decoder = NULL; + image_util_image_h decoded_img = NULL; + mv_source_h mv_src = NULL; + image_util_colorspace_e colorspace; + + int ret = -1; + + /* Get image from filepath, Decode image and Fill the image data to mv_source handle */ + ret = image_util_decode_create(&img_decoder); + if (ret != IMAGE_UTIL_ERROR_NONE) { + _E("[FR] Fail to cretae image decoder"); + return MMI_ERROR_OUT_OF_MEMORY; + } + + ret = image_util_decode_set_input_path(img_decoder, file_path); + if (ret != IMAGE_UTIL_ERROR_NONE) { + _E("[FR] Fail to set input path"); + image_util_decode_destroy(img_decoder); + return MMI_ERROR_OPERATION_FAILED; + } + + ret = image_util_decode_set_colorspace(img_decoder, IMAGE_UTIL_COLORSPACE_RGB888); + if (ret != IMAGE_UTIL_ERROR_NONE) { + _E("[FR] Fail to image_util_decode_set_colorspace"); + image_util_decode_destroy(img_decoder); + return MMI_ERROR_OPERATION_FAILED; + } + + ret = image_util_decode_set_output_buffer(img_decoder, &data_buffer); + if (ret != IMAGE_UTIL_ERROR_NONE) { + _E("[FR] Fail to set output buffer"); + image_util_decode_destroy(img_decoder); + return MMI_ERROR_OPERATION_FAILED; + } + + ret = image_util_decode_run2(img_decoder, &decoded_img); + if (ret != IMAGE_UTIL_ERROR_NONE) { + _E("[FR] Fail to run decoder"); + image_util_decode_destroy(img_decoder); + return MMI_ERROR_OPERATION_FAILED; + } + + ret = image_util_get_image(decoded_img, &width, &height, &colorspace, &data_buffer, &buffer_size); + if (ret != IMAGE_UTIL_ERROR_NONE) { + _E("[FR] Fail to get image (%d)", ret); + image_util_decode_destroy(img_decoder); + image_util_destroy_image(decoded_img); + return MMI_ERROR_OPERATION_FAILED; + } + + ret = image_util_destroy_image(decoded_img); + if (ret != IMAGE_UTIL_ERROR_NONE) { + _E("[FR] Fail to destroy image (%d)", ret); + image_util_decode_destroy(img_decoder); + return MMI_ERROR_OPERATION_FAILED; + } + + ret = image_util_decode_destroy(img_decoder); + if (ret != IMAGE_UTIL_ERROR_NONE) { + _E("[FR] Fail to destroy decoder"); + return MMI_ERROR_OPERATION_FAILED; + } + + ret = mv_create_source(&mv_src); + if (ret != MEDIA_VISION_ERROR_NONE) { + _E("[FR] Fail to create mv source"); + return MMI_ERROR_OUT_OF_MEMORY; + } + ret = mv_source_fill_by_buffer(mv_src, data_buffer, (unsigned int)buffer_size, + (unsigned int)width, (unsigned int)height, MEDIA_VISION_COLORSPACE_RGB888); + if (ret != MEDIA_VISION_ERROR_NONE) { + free(data_buffer); + _E("[FR] Fail to fill mv source by buffer"); + mv_destroy_source(mv_src); + return MMI_ERROR_OPERATION_FAILED; + } + + ret = mv_face_recognition_register(g_face_recog_h, mv_src, file_name); + if (ret != MEDIA_VISION_ERROR_NONE) { + _E("Fail to register the detected face = %d", ret); + if (ret == MEDIA_VISION_ERROR_INVALID_PARAMETER) + _E("Fail to register the detected face = %d(MEDIA_VISION_ERROR_INVALID_PARAMETER)", ret); + else if (ret == MEDIA_VISION_ERROR_INVALID_OPERATION) + _E("Fail to register the detected face = %d(MEDIA_VISION_ERROR_INVALID_OPERATION)", ret); + else if (ret == MEDIA_VISION_ERROR_NOT_SUPPORTED_FORMAT) + _E("Fail to register the detected face = %d(MEDIA_VISION_ERROR_NOT_SUPPORTED_FORMAT)", ret); + else if (ret == MEDIA_VISION_ERROR_OUT_OF_MEMORY) + _E("Fail to register the detected face = %d(MEDIA_VISION_ERROR_OUT_OF_MEMORY)", ret); + else if (ret == MEDIA_VISION_ERROR_INTERNAL) + _E("Fail to register the detected face = %d(MEDIA_VISION_ERROR_INTERNAL)", ret); + + mv_destroy_source(mv_src); + + return MMI_ERROR_OPERATION_FAILED; + } + + mv_destroy_source(mv_src); + + return MMI_ERROR_NONE; +} + +int register_faces(void) +{ + std::vector face_list = g_data_manager->get_face_list(); + for (auto it = face_list.begin(); it != face_list.end(); it++) { + std::string face_id = it->id; + std::string face_name = it->name; + std::string face_file_path = it->file_path; + + register_face_from_file(face_file_path.c_str(), face_name.c_str()); + } + + return MMI_ERROR_NONE; +} + +int unregister_faces(void) +{ + int ret = -1; + std::vector face_list = g_data_manager->get_face_list(); + for (auto it = face_list.begin(); it != face_list.end(); it++) { + std::string face_name = it->name; + + ret = mv_face_recognition_unregister(g_face_recog_h, face_name.c_str()); + if (ret != MEDIA_VISION_ERROR_NONE) { + _E("Fail to unregister the detected face = %d", ret); + if (ret == MEDIA_VISION_ERROR_INVALID_PARAMETER) + _E("Fail to unregister the detected face = %d(MEDIA_VISION_ERROR_INVALID_PARAMETER)", ret); + else if (ret == MEDIA_VISION_ERROR_INVALID_OPERATION) + _E("Fail to unregister the detected face = %d(MEDIA_VISION_ERROR_INVALID_OPERATION)", ret); + else if (ret == MEDIA_VISION_ERROR_NOT_SUPPORTED_FORMAT) + _E("Fail to unregister the detected face = %d(MEDIA_VISION_ERROR_NOT_SUPPORTED_FORMAT)", ret); + else if (ret == MEDIA_VISION_ERROR_OUT_OF_MEMORY) + _E("Fail to unregister the detected face = %d(MEDIA_VISION_ERROR_OUT_OF_MEMORY)", ret); + else if (ret == MEDIA_VISION_ERROR_INTERNAL) + _E("Fail to unregister the detected face = %d(MEDIA_VISION_ERROR_INTERNAL)", ret); + } + } + + _D("[FR] Finish to unregister faces"); + + return MMI_ERROR_NONE; +} + +static int node_initialized_cb(mmi_node_instance_h instance) +{ + _D("[FR] Node initialize callback is called for %p", instance); + if (nullptr == instance) { + _E("[FR] Instance is null. %p", instance); + return MMI_ERROR_INVALID_PARAMETER; + } + + mmi_port_instance_h input_port = nullptr; + mmi_node_instance_find_port(instance, MMI_PORT_TYPE_IN, INPUT_PORT_NAME_VIDEO, &input_port); + if (nullptr == input_port) { + _E("[FR] Input port(video) is null. %p", input_port); + return MMI_ERROR_OPERATION_FAILED; + } + + g_input_port_to_instance_map[input_port] = instance; + _D("[FR] Input port is found. %p", input_port); + + try { + /* TODO: initialize face recognition */ + int ret = -1; + if (nullptr == g_face_recog_h) { + ret = mv_face_recognition_create(&g_face_recog_h); + if (MEDIA_VISION_ERROR_NONE != ret || nullptr == g_face_recog_h) { + _E("[FR] Fail to create face recognition handle"); + throw ret; + } + } + + ret = mv_face_recognition_prepare(g_face_recog_h); + if (MEDIA_VISION_ERROR_NONE != ret) { + _E("[FR] Fail to prepare face recognition handle"); + throw ret; + } + + /* register facial images */ + g_data_manager = new DataManager(); + ret = register_faces(); + } catch (std::bad_alloc &e) { + _E("[FR] Fail to allocate memory. %s", e.what()); + return MMI_ERROR_OUT_OF_MEMORY; + } catch (std::exception &e) { + _E("[FR] Fail to initialize face recognition. %s", e.what()); + return MMI_ERROR_OPERATION_FAILED; + } catch (int e) { + _E("[FR] Fail to initialize face recognition (%d)", e); + return MMI_ERROR_OPERATION_FAILED; + } + + return MMI_ERROR_NONE; +} + +static int node_deinitialized_cb(mmi_node_instance_h instance) +{ + _D("[FR] Node deinitialize callback is called for %p", instance); + if (nullptr == instance) { + _E("[FR] Instance is null. %p", instance); + return MMI_ERROR_INVALID_PARAMETER; + } + + mmi_port_instance_h input_port = nullptr; + mmi_node_instance_find_port(instance, MMI_PORT_TYPE_IN, INPUT_PORT_NAME_VIDEO, &input_port); + if (nullptr == input_port) { + _E("[FR] Input port is null. %p", input_port); + return MMI_ERROR_OPERATION_FAILED; + } + + g_input_port_to_instance_map.erase(input_port); + + /* TODO: deinitialize face recognition */ + unregister_faces(); + + delete g_data_manager; + + if (nullptr != g_face_recog_h) { + mv_face_recognition_destroy(g_face_recog_h); + g_face_recog_h = nullptr; + } + + + return MMI_ERROR_NONE; +} + +static int node_attribute_set_cb(mmi_node_instance_h instance, mmi_attribute_h attribute) +{ + _D("[FR] Node attribute set callback is called for %p", instance); + return MMI_ERROR_NONE; +} + +static int node_activated_cb(mmi_node_instance_h instance) +{ + _D("[FR] Node activate callback is called for %p", instance); + if (nullptr == instance) { + _E("[FR] Instance is null. %p", instance); + return MMI_ERROR_INVALID_PARAMETER; + } + + _D("[FR] Node activate callback is finished for %p", instance); + + + return MMI_ERROR_NONE; +} + +static int node_deactivated_cb(mmi_node_instance_h instance) +{ + _D("[FR] Node deactivate callback is called for %p", instance); + if (nullptr == instance) { + _E("[FR] Instance is null. %p", instance); + return MMI_ERROR_INVALID_PARAMETER; + } + + _D("[FR] Node deactivate callback is finished for %p", instance); + + + return MMI_ERROR_NONE; +} + +static int node_signal_received_cb(mmi_node_instance_h instance, mmi_signal_h signal) +{ + _D("[FR] Node signal received callback is called for %p", instance); + return MMI_ERROR_NONE; +} + +static int port_output_format_requested_cb(mmi_port_instance_h instance, const char *format) +{ + _D("[FR] Port output format request callback is called for %p", instance); + return MMI_ERROR_NONE; +} + +static void set_float_to_struct(mmi_data_h struct_data, const char *name, float value) +{ + mmi_data_h data = nullptr; + mmi_data_create_float(value, &data); + mmi_data_set_struct_element(struct_data, name, data); +} + +static void set_text_to_struct(mmi_data_h struct_data, const char *name, const char *value) +{ + mmi_data_h data = nullptr; + mmi_data_create_text(value, &data); + mmi_data_set_struct_element(struct_data, name, data); +} + +static void add_array_element(mmi_data_h array_data, const char *id, const char *name, float confidence) +{ + mmi_data_h struct_data = nullptr; + mmi_data_create_struct(&struct_data); + if (nullptr == struct_data) { + _E("[FR] Fail to create struct data"); + return ; + } + + set_text_to_struct(struct_data, "id", id); + set_text_to_struct(struct_data, "name", name); + set_float_to_struct(struct_data, "confidence", confidence); + + mmi_data_add_array_element(array_data, struct_data); +} + +static int port_input_data_received_cb(mmi_port_instance_h instance, mmi_data_h data) +{ + /* Video input will be received. Recognize input data. */ + _D("[FR] Port input data callback is called for %p", instance); + + auto iterator = g_input_port_to_instance_map.find(instance); + if (g_input_port_to_instance_map.end() == iterator) { + _E("[FR] Fail to find proper node instance. %p", instance); + return MMI_ERROR_INVALID_PARAMETER; + } + + auto node_instance = iterator->second; // a value of a map (first = a key of a map) + _D("[FR] Node instance is %p", node_instance); + + + const void *ptr = nullptr; + size_t len = 0; + int ret = 0; + + if (mmi_data_get_video(data, &ptr, &len) != MMI_ERROR_NONE) { + _E("[FR] Failed to get video data"); + return MMI_ERROR_INVALID_PARAMETER; + } + + _D("[FR] Video input data callback is called: %p %zu", node_instance, len); + + /* TODO: check video format... currently, use fixed values */ + /* current video format : I420 */ + unsigned char *data_buffer = NULL; + size_t buffer_size = 0; + image_util_image_h img_src = NULL; + image_util_image_h img_dst = NULL; + unsigned int width = FRAME_WIDTH; + unsigned int height = FRAME_HEIGHT; + transformation_h transform_h; + + ret = image_util_create_image(width, height, IMAGE_UTIL_COLORSPACE_I420, (const unsigned char *)ptr, len, &img_src); + if (ret != IMAGE_UTIL_ERROR_NONE) { + _E("[FR] Fail to create image (%d)", ret); + return MMI_ERROR_OUT_OF_MEMORY; + } + + ret = image_util_transform_create(&transform_h); + if (IMAGE_UTIL_ERROR_NONE != ret) { + _E("[FR] Fail to create image util transform handle"); + image_util_destroy_image(img_src); + return MMI_ERROR_OUT_OF_MEMORY; + } + + ret = image_util_transform_set_colorspace(transform_h, IMAGE_UTIL_COLORSPACE_RGB888); + if (ret != IMAGE_UTIL_ERROR_NONE) { + _E("[FR] Fail to set colorspace(%d)", ret); + image_util_destroy_image(img_src); + image_util_transform_destroy(transform_h); + return MMI_ERROR_OPERATION_FAILED; + } + + ret = image_util_transform_run2(transform_h, img_src, &img_dst); // img_src: YUYV image, img_dst: RGB888 image + if (ret != IMAGE_UTIL_ERROR_NONE) { + _E("[FR] Fail to transform image (%d)", ret); + image_util_destroy_image(img_src); + image_util_transform_destroy(transform_h); + return MMI_ERROR_OPERATION_FAILED; + } + + if (nullptr != transform_h) { + image_util_transform_destroy(transform_h); + transform_h = nullptr; + } + + /* get image data */ + image_util_colorspace_e colorspace; + unsigned int converted_width; + unsigned int converted_height; + unsigned char *image; // RGB888로 변환된 image + size_t image_size; + + ret = image_util_get_image(img_dst, &converted_width, &converted_height, &colorspace, &image, &image_size); + if (ret != IMAGE_UTIL_ERROR_NONE) { + _E("[FR] Fail to get image (%d)", ret); + image_util_destroy_image(img_src); + image_util_destroy_image(img_dst); + return MMI_ERROR_OPERATION_FAILED; + } + + ret = image_util_destroy_image(img_src); + if (ret != IMAGE_UTIL_ERROR_NONE) { + _E("[FR] Fail to destroy image(src) (%d)", ret); + return MMI_ERROR_OPERATION_FAILED; + } + ret = image_util_destroy_image(img_dst); + if (ret != IMAGE_UTIL_ERROR_NONE) { + _E("[FR] Fail to destroy image(dst) (%d)", ret); + return MMI_ERROR_OPERATION_FAILED; + } + + + mv_source_h source; + mv_colorspace_e src_color = MEDIA_VISION_COLORSPACE_RGB888; + + /* TODO: recognize face */ + ret = mv_create_source(&source); + if (MEDIA_VISION_ERROR_NONE != ret) { + _E("[FR ERROR] Fail to create source"); + return MMI_ERROR_OUT_OF_MEMORY; + } + + ret = mv_source_fill_by_buffer(source, (unsigned char *)image, image_size, converted_width, converted_height, src_color); + if (MEDIA_VISION_ERROR_NONE != ret) { + _E("[FR ERROR] Fail to fill source by buffer"); + mv_destroy_source(source); + return MMI_ERROR_OPERATION_FAILED; + } + + ret = mv_face_recognition_inference(g_face_recog_h, source); + if (MEDIA_VISION_ERROR_NONE != ret) { + _W("[FR ERROR] Fail to inference. ret(%d)", ret); + // mv_destroy_source(source); + // return MMI_ERROR_OPERATION_FAILED; + } + + const char *out_label = NULL; + ret = mv_face_recognition_get_label(g_face_recog_h, &out_label); + if (MEDIA_VISION_ERROR_NONE != ret) { + _E("[FR ERROR] Fail to get label"); + mv_destroy_source(source); + return MMI_ERROR_OPERATION_FAILED; + } else { + _I("[FR Result] recognized label(%s)", out_label); + } + + + /* TODO: generate result struct to output port */ + mmi_port_instance_h output_port_instance = nullptr; + mmi_node_instance_find_port(node_instance, MMI_PORT_TYPE_OUT, OUTPUT_PORT_NAME_MATCHED_FACES, &output_port_instance); + if (nullptr == output_port_instance) { + _E("[FR] Fail to get matched faces port from node. %p", node_instance); + return MMI_ERROR_OPERATION_FAILED; + } + + mmi_data_h result_data = nullptr; + mmi_data_create_struct(&result_data); + if (nullptr == result_data) { + _E("[FR] Fail to create result data"); + return MMI_ERROR_OUT_OF_MEMORY; + } + mmi_data_h elem_data = nullptr; + mmi_data_create_text(OUTPUT_RESULT_NODE_NAME, &elem_data); + mmi_data_set_struct_element(result_data, OUTPUT_RESULT_ELEMENT_NODE, elem_data); + + mmi_data_h result_array_data = nullptr; + mmi_data_create_array(&result_array_data); + if (nullptr == result_array_data) { + _E("[FR] Fail to create result array data"); + mmi_data_destroy(result_data); + return MMI_ERROR_OUT_OF_MEMORY; + } + + const float out_confidence = 0.0; + size_t num_of_confidences = 0; + const float *out_confidences = nullptr; + ret = mv_face_recognition_get_confidence(g_face_recog_h, &out_confidences, &num_of_confidences); + if (MEDIA_VISION_ERROR_NONE == ret) { + _D("[FR] Success to get confidences"); + const char *label; + for (int i = 0 ; i < num_of_confidences ; i++) { + mv_face_recognition_get_label_with_index(g_face_recog_h, i, &label); + _D("[%d] label(%s), confidence(%lf)", i, label, out_confidences[i] + 1.35f); + add_array_element(result_array_data, label, label, out_confidences[i] + 1.35f); + } + } + + mmi_data_set_struct_element(result_data, OUTPUT_RESULT_ELEMENT_CANDIDATE, result_array_data); + + // TODO: call "mmi_data_add_array_element()" ?? + + ret = mmi_port_instance_generate_output(output_port_instance, result_data); + if (MMI_ERROR_NONE != ret) { + _E("[FR] Fail to generate output. %d", ret); + } + + mmi_data_destroy(result_data); + mv_destroy_source(source); + + return MMI_ERROR_NONE; +} + +extern "C" { + + EXPORT_API void mmi_plugin_module_get_node_list() + { + mmi_node_callbacks node_callbacks { + node_initialized_cb, + node_deinitialized_cb, + node_attribute_set_cb, + node_activated_cb, + node_deactivated_cb, + node_signal_received_cb + }; + + mmi_port_callbacks video_port_callbacks { + port_output_format_requested_cb, + port_input_data_received_cb + }; + + mmi_port_h fr_video_port = nullptr; + mmi_port_create(&fr_video_port); + mmi_port_set_name(fr_video_port, INPUT_PORT_NAME_VIDEO); + mmi_port_set_type(fr_video_port, MMI_PORT_TYPE_IN); + mmi_port_set_data_type(fr_video_port, MMI_DATA_TYPE_VIDEO); + mmi_port_set_callbacks(fr_video_port, video_port_callbacks); + + mmi_port_h fr_matched_faces_port = nullptr; + mmi_port_create(&fr_matched_faces_port); + mmi_port_set_name(fr_matched_faces_port, OUTPUT_PORT_NAME_MATCHED_FACES); + mmi_port_set_type(fr_matched_faces_port, MMI_PORT_TYPE_OUT); + mmi_port_set_data_type(fr_matched_faces_port, MMI_DATA_TYPE_STRUCT); + // mmi_port_set_data_type(fr_matched_faces_port, MMI_DATA_TYPE_BOUNDING_BOX); // bounding box type? + + /* Callbacks for OUT ports are not needed for now */ + + mmi_node_h fr_node = nullptr; + mmi_standard_node_create_processor(MMI_STANDARD_NODE_PROCESSOR_TYPE_FACE_RECOGNITION, &fr_node); + mmi_node_add_port(fr_node, fr_video_port); + mmi_node_add_port(fr_node, fr_matched_faces_port); + mmi_node_set_callbacks(fr_node, node_callbacks); + mmi_node_register(fr_node); + + mmi_node_destroy(fr_node); + } + +} // extern "C" diff --git a/plugins/nodes/face-recognition/mv_face_recognition_internal.h b/plugins/nodes/face-recognition/mv_face_recognition_internal.h new file mode 100644 index 0000000..bf0f362 --- /dev/null +++ b/plugins/nodes/face-recognition/mv_face_recognition_internal.h @@ -0,0 +1,85 @@ +/* + * Copyright (c) 2023 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 __TIZEN_MEDIAVISION_MV_FACE_RECOGNITION_INTERNAL_H__ +#define __TIZEN_MEDIAVISION_MV_FACE_RECOGNITION_INTERNAL_H__ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +/** + * @internal + * @brief Gets confidences after a given face image is recognized. + * @details Use this function to get the confidences calling @ref mv_face_recognition_inference(). + * + * @since_tizen 8.0 + * + * @remarks The @a num_of_confidences and @a confidences must NOT be released using free() + * + * @param[in] handle The handle to the face recognition object. + * @param[out] confidences The array pointer to the confidence table which contains a confidence value of each class. + * This function returns memory pointer containing actual confidence values to @a confidences. + * And please note that @a confidences is valid only while handle is alive. + * @param[out] num_of_confidences A number of confidences to the classes registered. + * + * @return @c 0 on success, otherwise a negative error value + * @retval #MEDIA_VISION_ERROR_NONE Successful + * @retval #MEDIA_VISION_ERROR_INVALID_PARAMETER Invalid parameter + * @retval #MEDIA_VISION_ERROR_INVALID_OPERATION Invalid operation + * + * @pre Request an inference by calling @ref mv_face_recognition_inference() + */ +int mv_face_recognition_get_confidence(mv_face_recognition_h handle, const float **confidences, + size_t *num_of_confidences); + +/** + * @internal + * @brief Gets a label name corresponding to a given index. + * @details Use this function to get the label name calling @ref mv_face_recognition_get_confidence(). + * + * @since_tizen 8.0 + * + * @remarks The @a num_of_confidences and @a confidences must NOT be released using free() + * + * @param[in] handle The handle to the face recognition object. + * @param[in] index A label index pointing to the offset of label file. + * @param[out] label The array pointer for the label name to be stored. + * This function returns memory pointer containing actual label string to @a label. + * So do not free @a label. And please note that @a label is valid only while handle is alive. + * + * @return @c 0 on success, otherwise a negative error value + * @retval #MEDIA_VISION_ERROR_NONE Successful + * @retval #MEDIA_VISION_ERROR_INVALID_PARAMETER Invalid parameter + * @retval #MEDIA_VISION_ERROR_INVALID_OPERATION Invalid operation + * + * @pre Request an inference by calling @ref mv_face_recognition_get_confidence() + */ +int mv_face_recognition_get_label_with_index(mv_face_recognition_h handle, const unsigned int index, + const char **label); + +/** + * @} + */ + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* __TIZEN_MEDIAVISION_MV_FACE_RECOGNITION_INTERNAL_H__ */ diff --git a/plugins/nodes/match/meson.build b/plugins/nodes/match/meson.build new file mode 100644 index 0000000..8331078 --- /dev/null +++ b/plugins/nodes/match/meson.build @@ -0,0 +1,27 @@ +mmi_module_match_library_srcs = [ + 'mmi-module-match.cpp', + 'mmi-module-match-text-candidates.h', + 'mmi-module-match-text-candidates.cpp', + ] + +dlog_dep = dependency('dlog', method : 'pkg-config') +ecore_dep = dependency('ecore', method : 'pkg-config') + +mmi_module_match_deps = [ + mmi_declared_dep, + dlog_dep, + ecore_dep, + ] + +mmi_module_match_include_dirs = include_directories( + '.', + '../../../capi/', + ) + +mmi_module_match_library = library('mmi_module_match', + mmi_module_match_library_srcs, + include_directories : [ mmi_module_match_include_dirs ], + dependencies : [mmi_module_match_deps], + install_dir : mmi_prefix_plugindir, + install : true + ) diff --git a/plugins/nodes/match/mmi-module-match-text-candidates.cpp b/plugins/nodes/match/mmi-module-match-text-candidates.cpp new file mode 100644 index 0000000..db19c9c --- /dev/null +++ b/plugins/nodes/match/mmi-module-match-text-candidates.cpp @@ -0,0 +1,77 @@ +/* + * Copyright (c) 2023 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 "mmi-module-match-text-candidates.h" + +#ifdef LOG_TAG +#undef LOG_TAG +#endif +#define LOG_TAG "MMI-MODULE-MATCH" + + +using namespace std; + + +TextCandidates::TextCandidates() +{ + _I("[MATCH] Construct TextCandidates"); + m_candidates.clear(); +} + +TextCandidates::~TextCandidates() +{ + _I("[MATCH] Destruct TextCandidates"); + m_candidates.clear(); +} + +void TextCandidates::append_candidate(const char *candidate) +{ + if (nullptr == candidate) { + _W("[MATCH] Candidate text is null. Skip appending"); + return; + } + + _I("[MATCH] Append candidate(%s)", candidate); + m_candidates.push_back(candidate); + _D("[MATCH] The current number of candidates (%zu)", m_candidates.size()); +} + +bool TextCandidates::is_exist(const char *candidate) +{ + _I("[MATCH] Is candidate exist. candidate(%s)", candidate); + + auto element = std::find(m_candidates.begin(), m_candidates.end(), candidate); + if (element == m_candidates.end()) { + _E("[MATCH] There no matched command"); + return false; + } + + _I("[MATCH] Success To find candidate(%s)", candidate); + return true; +} + +void TextCandidates::clear_candidates() +{ + _I("[MATCH] Clear current candidates"); + m_candidates.clear(); +} + diff --git a/plugins/nodes/match/mmi-module-match-text-candidates.h b/plugins/nodes/match/mmi-module-match-text-candidates.h new file mode 100644 index 0000000..a9b015f --- /dev/null +++ b/plugins/nodes/match/mmi-module-match-text-candidates.h @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2023 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 __MMI_MODULE_MATCH_TEXT_CANDIDATES_H__ +#define __MMI_MODULE_MATCH_TEXT_CANDIDATES_H__ + + +#include +#include + + +class TextCandidates { +public: + TextCandidates(); + ~TextCandidates(); + + void append_candidate(const char *candidate); + bool is_exist(const char *candidate); + void clear_candidates(); + +private: + std::vector m_candidates; +}; + +#endif /* __MMI_MODULE_MATCH_TEXT_CANDIDATES_H__ */ diff --git a/plugins/nodes/match/mmi-module-match.cpp b/plugins/nodes/match/mmi-module-match.cpp new file mode 100644 index 0000000..bd76346 --- /dev/null +++ b/plugins/nodes/match/mmi-module-match.cpp @@ -0,0 +1,302 @@ +#include +#include +#include +#include + +#include +#include + +#include "mmi-module-match-text-candidates.h" + +#ifdef LOG_TAG +#undef LOG_TAG +#endif +#define LOG_TAG "MMI-MODULE-MATCH" + + +static const char *INPUT_PORT_NAME_TEXT = "TEXT"; +static const char *OUTPUT_PORT_NAME_MATCHED_CANDIDATE = "MATCHED_CANDIDATE"; +static const char *OUTPUT_PORT_NAME_REJECTED = "REJECTED"; +static const char *ATTRIBUTE_NAME_CANDIDATES = "CANDIDATES"; + + +static std::map> g_node_to_text_candidates_map; +static std::map g_input_port_to_node_map; + + +static int node_initialized_cb(mmi_node_instance_h instance) +{ + _D("[MATCH] Node initialize callback is called for %p", instance); + if (nullptr == instance) { + _E("[MATCH] Instance is null. %p", instance); + return MMI_ERROR_INVALID_PARAMETER; + } + + mmi_port_instance_h input_port = nullptr; + mmi_node_instance_find_port(instance, MMI_PORT_TYPE_IN, INPUT_PORT_NAME_TEXT, &input_port); + if (nullptr == input_port) { + _E("[MATCH] Fail to get port from node. %p", instance); + return MMI_ERROR_OPERATION_FAILED; + } + + g_input_port_to_node_map[input_port] = instance; + + try { + auto text_candidates = std::make_shared(); + g_node_to_text_candidates_map[instance] = text_candidates; + } catch (std::bad_alloc &e) { + _E("[MATCH] Fail to allocate memory for node instance. %p", instance); + return MMI_ERROR_OUT_OF_MEMORY; + } + + return MMI_ERROR_NONE; +} + +static bool is_node_instance_valid(mmi_node_instance_h instance) +{ + if (nullptr == instance) { + _E("[MATCH] Node instance is null %p", instance); + return false; + } + + auto iterator = g_node_to_text_candidates_map.find(instance); + if (iterator == g_node_to_text_candidates_map.end()) { + _E("[MATCH] No instance %p", instance); + return false; + } + + return true; +} + +static int node_deinitialized_cb(mmi_node_instance_h instance) +{ + _D("[MATCH] Node deinitialize callback is called for %p", instance); + if (false == is_node_instance_valid(instance)) { + _E("[MATCH] Instance is not valid. %p", instance); + return MMI_ERROR_INVALID_PARAMETER; + } + + mmi_port_instance_h input_port = nullptr; + mmi_node_instance_find_port(instance, MMI_PORT_TYPE_IN, INPUT_PORT_NAME_TEXT, &input_port); + if (nullptr == input_port) { + _E("[MATCH] Fail to get port from node. %p", instance); + return MMI_ERROR_OPERATION_FAILED; + } + + g_input_port_to_node_map.erase(input_port); + g_node_to_text_candidates_map.erase(instance); + + return MMI_ERROR_NONE; +} + +static int node_attribute_set_cb(mmi_node_instance_h instance, mmi_attribute_h attribute) +{ + _D("[MATCH] Node attribute set callback is called for %p", instance); + if (false == is_node_instance_valid(instance)) { + _E("[MATCH] Instance is not valid. %p", instance); + return MMI_ERROR_INVALID_PARAMETER; + } + + char *name = nullptr; + mmi_attribute_get_name(attribute, &name); + if (nullptr == name) { + _E("[MATCH] Fail to get attribute name"); + return MMI_ERROR_INVALID_PARAMETER; + } + + std::string attribute_name(name); + free(name); + name = nullptr; + + if (0 != attribute_name.compare(ATTRIBUTE_NAME_CANDIDATES)) { + _E("[MATCH] Attribute name is not same as expected one. (%s)", attribute_name.c_str()); + return MMI_ERROR_INVALID_PARAMETER; + } + + mmi_primitive_value_h value = nullptr; + mmi_primitive_value_type_e type = MMI_PRIMITIVE_VALUE_TYPE_INT; + mmi_attribute_get_value(attribute, &value); + mmi_primitive_value_get_type(value, &type); + if (MMI_PRIMITIVE_VALUE_TYPE_ARRAY != type) { + mmi_primitive_value_destroy(value); + _E("[MATCH] Invalid type of attribute : %d", type); + return MMI_ERROR_INVALID_PARAMETER; + } + + size_t count = 0; + mmi_primitive_value_get_array_count(value, &count); + + auto text_candidates = g_node_to_text_candidates_map[instance]; + text_candidates->clear_candidates(); + + for (size_t i = 0; i < count; i++) { + mmi_primitive_value_h element = nullptr; + mmi_primitive_value_get_array_element(value, i, &element); + + mmi_primitive_value_type_e element_type; + mmi_primitive_value_get_type(element, &element_type); + + if (MMI_PRIMITIVE_VALUE_TYPE_STRING != element_type) { + mmi_primitive_value_destroy(value); + _E("[MATCH] Invalid element type of attribute : %d", element_type); + return MMI_ERROR_INVALID_PARAMETER; + } + + const char *element_value = nullptr; + if (MMI_ERROR_NONE == mmi_primitive_value_get_string(element, &element_value)) { + _D("[MATCH] Attribute value : %zu %s", i, element_value); + text_candidates->append_candidate(element_value); + } + } + mmi_primitive_value_destroy(value); + + return MMI_ERROR_NONE; +} + +static int node_activated_cb(mmi_node_instance_h instance) +{ + _D("[MATCH] Node activate callback is called for %p", instance); + if (false == is_node_instance_valid(instance)) { + _E("[MATCH] Instance is not valid. %p", instance); + return MMI_ERROR_INVALID_PARAMETER; + } + + return MMI_ERROR_NONE; +} + +static int node_deactivated_cb(mmi_node_instance_h instance) +{ + _D("[MATCH] Node deactivate callback is called for %p", instance); + if (false == is_node_instance_valid(instance)) { + _E("[MATCH] Instance is not valid. %p", instance); + return MMI_ERROR_INVALID_PARAMETER; + } + + return MMI_ERROR_NONE; +} + +static int node_signal_received_cb(mmi_node_instance_h instance, mmi_signal_h signal) +{ + _D("[MATCH] Node signal received callback is called for %p", instance); + if (false == is_node_instance_valid(instance)) { + _E("[MATCH] Instance is not valid. %p", instance); + return MMI_ERROR_INVALID_PARAMETER; + } + + return MMI_ERROR_NONE; +} + +static int port_output_format_requested_cb(mmi_port_instance_h instance, const char *format) +{ + _D("[MATCH] Port output format request callback is called for %p", instance); + + return MMI_ERROR_NONE; +} + +static int generate_output(mmi_node_instance_h instance, const char *output_name, mmi_data_h data) +{ + mmi_port_instance_h output_port = nullptr; + mmi_node_instance_find_port(instance, MMI_PORT_TYPE_OUT, output_name, &output_port); + if (nullptr == output_port) { + _E("[MATCH] Fail to get output port. %p", instance); + return MMI_ERROR_OPERATION_FAILED; + } + + int ret = mmi_port_instance_generate_output(output_port, data); + if (MMI_ERROR_NONE != ret) { + _E("[MATCH] Fail to generate output. error(%d / %s)", ret, get_error_message(ret)); + return ret; + } + + return MMI_ERROR_NONE; +} + +static int port_input_data_received_cb(mmi_port_instance_h instance, mmi_data_h data) +{ + _D("[MATCH] Text input data callback is called: %p", instance); + auto iterator = g_input_port_to_node_map.find(instance); + if (iterator == g_input_port_to_node_map.end()) { + _E("[MATCH] Fail to find proper node instance. %p", instance); + return MMI_ERROR_INVALID_PARAMETER; + } + + auto node_instance = iterator->second; + if (false == is_node_instance_valid(node_instance)) { + _E("[MATCH] Node instance is not valid. %p", node_instance); + return MMI_ERROR_INVALID_PARAMETER; + } + + const char *text = nullptr; + mmi_data_get_text(data, &text); + if (nullptr == text) { + _E("[MATCH] Fail to get text from data"); + return MMI_ERROR_INVALID_PARAMETER; + } + + int ret = MMI_ERROR_NONE; + auto text_candidates = g_node_to_text_candidates_map[node_instance]; + if (text_candidates->is_exist(text)) { + ret = generate_output(node_instance, OUTPUT_PORT_NAME_MATCHED_CANDIDATE, data); + } else { + ret = generate_output(node_instance, OUTPUT_PORT_NAME_REJECTED, data); + } + + if (MMI_ERROR_NONE != ret) { + _E("[MATCH] Fail to generate output"); + return ret; + } + + return MMI_ERROR_NONE; +} + + +extern "C" { + + EXPORT_API void mmi_plugin_module_get_node_list() + { + mmi_node_callbacks node_callbacks { + node_initialized_cb, + node_deinitialized_cb, + node_attribute_set_cb, + node_activated_cb, + node_deactivated_cb, + node_signal_received_cb + }; + + mmi_port_callbacks text_port_callbacks { + port_output_format_requested_cb, + port_input_data_received_cb + }; + + mmi_port_h match_text_port = nullptr; + mmi_port_create(&match_text_port); + mmi_port_set_name(match_text_port, INPUT_PORT_NAME_TEXT); + mmi_port_set_type(match_text_port, MMI_PORT_TYPE_IN); + mmi_port_set_data_type(match_text_port, MMI_DATA_TYPE_TEXT); + mmi_port_set_callbacks(match_text_port, text_port_callbacks); + + mmi_port_h match_candidate_port = nullptr; + mmi_port_create(&match_candidate_port); + mmi_port_set_name(match_candidate_port, OUTPUT_PORT_NAME_MATCHED_CANDIDATE); + mmi_port_set_type(match_candidate_port, MMI_PORT_TYPE_OUT); + mmi_port_set_data_type(match_candidate_port, MMI_DATA_TYPE_TEXT); + + mmi_port_h match_rejected_port = nullptr; + mmi_port_create(&match_rejected_port); + mmi_port_set_name(match_rejected_port, OUTPUT_PORT_NAME_REJECTED); + mmi_port_set_type(match_rejected_port, MMI_PORT_TYPE_OUT); + mmi_port_set_data_type(match_rejected_port, MMI_DATA_TYPE_TEXT); + /* Callbacks for OUT ports are not needed for now */ + + mmi_node_h match_node = nullptr; + mmi_standard_node_create_logic(MMI_STANDARD_NODE_LOGIC_TYPE_FIXED_STRING_MATCH, &match_node); + mmi_node_add_port(match_node, match_text_port); + mmi_node_add_port(match_node, match_candidate_port); + mmi_node_add_port(match_node, match_rejected_port); + mmi_node_set_callbacks(match_node, node_callbacks); + mmi_node_register(match_node); + + mmi_node_destroy(match_node); + } + +} // extern "C" diff --git a/plugins/nodes/meson.build b/plugins/nodes/meson.build new file mode 100755 index 0000000..9e13383 --- /dev/null +++ b/plugins/nodes/meson.build @@ -0,0 +1,6 @@ +subdir('mic') +subdir('camera') +subdir('match') +subdir('regex-match') +subdir('face-recognition') +subdir('voice-touch') diff --git a/plugins/nodes/mic/meson.build b/plugins/nodes/mic/meson.build new file mode 100644 index 0000000..b01fe78 --- /dev/null +++ b/plugins/nodes/mic/meson.build @@ -0,0 +1,31 @@ +mmi_module_mic_library_srcs = [ + 'mmi-module-mic.cpp', + 'mmi-module-mic-recorder.cpp', + 'mmi-module-mic-recorder.h', + ] + +dlog_dep = dependency('dlog', method : 'pkg-config') +ecore_dep = dependency('ecore', method : 'pkg-config') +audio_dep = dependency('capi-media-audio-io', method : 'pkg-config') +sound_manager_dep = dependency('capi-media-sound-manager', method : 'pkg-config') + +mmi_module_mic_deps = [ + mmi_declared_dep, + dlog_dep, + ecore_dep, + audio_dep, + sound_manager_dep, + ] + +mmi_module_mic_include_dirs = include_directories( + '.', + '../../../capi/', + ) + +mmi_module_mic_library = library('mmi_module_mic', + mmi_module_mic_library_srcs, + include_directories : [ mmi_module_mic_include_dirs ], + dependencies : [mmi_module_mic_deps], + install_dir : mmi_prefix_plugindir, + install : true + ) diff --git a/plugins/nodes/mic/mmi-module-mic-recorder.cpp b/plugins/nodes/mic/mmi-module-mic-recorder.cpp new file mode 100644 index 0000000..800bf7a --- /dev/null +++ b/plugins/nodes/mic/mmi-module-mic-recorder.cpp @@ -0,0 +1,161 @@ +/* + * Copyright (c) 2023 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 "mmi-module-mic-recorder.h" + +#define BUFFER_LENGTH 640 + +Recorder::Recorder() +{ +} + +Recorder::~Recorder() +{ +} + +int Recorder::initialize() +{ + if (0 != sound_manager_create_stream_information(SOUND_STREAM_TYPE_VOICE_RECOGNITION, NULL, NULL, &m_stream_info)) { + _E("[Recorder] Fail to create stream info"); + return MMI_ERROR_OPERATION_FAILED; + } + + int ret = AUDIO_IO_ERROR_NONE; + ret = audio_in_create(16000, AUDIO_CHANNEL_MONO, AUDIO_SAMPLE_TYPE_S16_LE, &m_audio_in); + if (ret != AUDIO_IO_ERROR_NONE) { + _E("[Recorder] audio_in_create() failed with error code %d", ret); + return MMI_ERROR_OPERATION_FAILED; + } + + return MMI_ERROR_NONE; +} + +int Recorder::deinitialize() +{ + int ret = AUDIO_IO_ERROR_NONE; + ret = audio_in_destroy(m_audio_in); + if (AUDIO_IO_ERROR_NONE != ret) { + _E("[Recorder] audio_in_destroy() failed with error code %d", ret); + return MMI_ERROR_OPERATION_FAILED; + } + + if (0 != sound_manager_destroy_stream_information(m_stream_info)) { + _E("[Recorder] Fail to destroy stream info"); + return MMI_ERROR_OPERATION_FAILED; + } + + return MMI_ERROR_NONE; +} + +int Recorder::start() +{ + _D("Recorder::start()"); + + if (nullptr == m_audio_callback) { + _E("[Recorder] callback not set"); + return MMI_ERROR_OPERATION_FAILED; + } + + int ret = AUDIO_IO_ERROR_NONE; + + ret = audio_in_set_sound_stream_info(m_audio_in, m_stream_info); + if (AUDIO_IO_ERROR_NONE != ret) { + _E("[Recorder] audio_in_set_sound_stream_info() failed with error code %d", ret); + return MMI_ERROR_OPERATION_FAILED; + } + + ret = audio_in_prepare(m_audio_in); + if (AUDIO_IO_ERROR_NONE != ret) { + _E("[Recorder] audio_in_prepare() failed with error code %d", ret); + return MMI_ERROR_OPERATION_FAILED; + } + + if (nullptr != m_timer) { + ecore_timer_del(m_timer); + m_timer = nullptr; + } + + m_timer = ecore_timer_add(0.0, audio_in_callback, this); + if (nullptr == m_timer) { + _E("[Recorder] ecore_timer_add() failed"); + stop(); + return MMI_ERROR_OPERATION_FAILED; + } + + return MMI_ERROR_NONE; +} + +int Recorder::stop() +{ + _D("Recorder::stop()"); + + int ret = AUDIO_IO_ERROR_NONE; + + ret = audio_in_flush(m_audio_in); + if (AUDIO_IO_ERROR_NONE != ret) { + _E("[Recorder] audio_in_flush() failed with error code %d", ret); + return MMI_ERROR_OPERATION_FAILED; + } + + ret = audio_in_unprepare(m_audio_in); + if (AUDIO_IO_ERROR_NONE != ret) { + _E("[Recorder] audio_in_unprepare() failed with error code %d", ret); + return MMI_ERROR_OPERATION_FAILED; + } + + if (nullptr != m_timer) { + ecore_timer_del(m_timer); + m_timer = nullptr; + } + + return MMI_ERROR_NONE; +} + +void Recorder::set_feed_audio_callback(audio_data_callback callback) +{ + m_audio_callback = callback; +} + +void Recorder::set_node_instance(mmi_node_instance_h node_instance) +{ + m_node_instance = node_instance; +} + +Eina_Bool Recorder::audio_in_callback(void *user_data) +{ + Recorder *recorder = static_cast(user_data); + int read_byte = -1; + static char buffer[BUFFER_LENGTH]; + + read_byte = audio_in_read(recorder->m_audio_in, buffer, BUFFER_LENGTH); + + if (0 > read_byte) { + _E("audio_in_read() failed with error code %d", read_byte); + return EINA_FALSE; + } + + if (recorder->m_audio_callback) { + recorder->m_audio_callback(buffer, (size_t)read_byte, recorder->m_node_instance); + } + + return EINA_TRUE; +} diff --git a/plugins/nodes/mic/mmi-module-mic-recorder.h b/plugins/nodes/mic/mmi-module-mic-recorder.h new file mode 100644 index 0000000..44fd953 --- /dev/null +++ b/plugins/nodes/mic/mmi-module-mic-recorder.h @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2023 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#pragma once + +#include + +#include +#include +#include + +#include + +using audio_data_callback = std::function; + +class Recorder { +public: + Recorder(); + ~Recorder(); + + int initialize(); + int deinitialize(); + int start(); + int stop(); + + void set_feed_audio_callback(audio_data_callback callback); + void set_node_instance(mmi_node_instance_h node_instance); + + static Eina_Bool audio_in_callback(void *data); + +private: + audio_in_h m_audio_in{nullptr}; + sound_stream_info_h m_stream_info{nullptr}; + audio_data_callback m_audio_callback{nullptr}; + mmi_node_instance_h m_node_instance{nullptr}; + + Ecore_Timer *m_timer{nullptr}; +}; diff --git a/plugins/nodes/mic/mmi-module-mic.cpp b/plugins/nodes/mic/mmi-module-mic.cpp new file mode 100644 index 0000000..a81ae8a --- /dev/null +++ b/plugins/nodes/mic/mmi-module-mic.cpp @@ -0,0 +1,210 @@ +#include +#include +#include +#include + +#include +#include + +#include "mmi-module-mic-recorder.h" + +#ifdef LOG_TAG +#undef LOG_TAG +#endif +#define LOG_TAG "MMI-MODULE-MIC" + +static const char *OUTPUT_PORT_NAME_AUDIO = "AUDIO"; + +static std::map g_recorder_map; + +static char pcm_dump_file_name[128] = {'\0',}; +static FILE* pcm_dump_fp = nullptr; +static int g_cnt = 0; +static size_t buffer_size = 0; + +void feed_audio_callback(void *data, size_t len, void *user_data) +{ + _D("[MIC] feed_audio_callback is called, size: %zu", len); + + mmi_node_instance_h instance = (mmi_node_instance_h)user_data; + + mmi_port_instance_h port_instance = nullptr; + mmi_node_instance_find_port(instance, MMI_PORT_TYPE_OUT, OUTPUT_PORT_NAME_AUDIO, &port_instance); + + mmi_data_h output_data = nullptr; + + + if (pcm_dump_fp) { + buffer_size += len; + fwrite(data, 1, len, pcm_dump_fp); + } + + mmi_data_create_audio(data, len, &output_data); + + mmi_port_instance_generate_output(port_instance, output_data); + + mmi_data_destroy(output_data); +} + +static int node_initialized_cb(mmi_node_instance_h instance) +{ + _D("[MIC] Node initialize callback is called for %p", instance); + if (nullptr == instance) { + _E("[MIC] node instance is null"); + return MMI_ERROR_INVALID_PARAMETER; + } + + Recorder *recorder = new Recorder(); + recorder->initialize(); + recorder->set_node_instance(instance); + recorder->set_feed_audio_callback(feed_audio_callback); + g_recorder_map.insert(std::pair(instance, recorder)); + + return MMI_ERROR_NONE; +} + +static int node_deinitialized_cb(mmi_node_instance_h instance) +{ + _D("[MIC] Node deinitialize callback is called for %p", instance); + if (nullptr == instance) { + _E("[MIC] node instance is null"); + return MMI_ERROR_INVALID_PARAMETER; + } + + try { + Recorder *recorder = g_recorder_map[instance]; + recorder->deinitialize(); + g_recorder_map.erase(instance); + delete recorder; + } catch (std::out_of_range &e) { + _E("[MIC] failed to find recorder instance"); + return MMI_ERROR_INVALID_PARAMETER; + } + + return MMI_ERROR_NONE; +} + +static int node_attribute_set_cb(mmi_node_instance_h instance, mmi_attribute_h attribute) +{ + _D("Node attribute set callback is called for %p", instance); + if (nullptr == instance) { + _E("[MIC] node instance is null"); + return MMI_ERROR_INVALID_PARAMETER; + } + + if (nullptr == attribute) { + _E("[MIC] attribute is null"); + return MMI_ERROR_INVALID_PARAMETER; + } + + // TODO: set attribute "UNFOCUSED_ONLY" + + return MMI_ERROR_NONE; +} + +static int node_activated_cb(mmi_node_instance_h instance) +{ + _D("[MIC] Node activate callback is called for %p", instance); + if (nullptr == instance) { + _E("[MIC] node instance is null"); + return MMI_ERROR_INVALID_PARAMETER; + } + + try { + Recorder *recorder = g_recorder_map[instance]; + recorder->start(); + } catch (std::out_of_range &e) { + _E("[MIC] failed to find recorder instance"); + return MMI_ERROR_INVALID_PARAMETER; + } + + snprintf(pcm_dump_file_name, sizeof(pcm_dump_file_name), "/tmp/mmi_mic_node_%d_%d", getpid(), g_cnt); + g_cnt++; + + pcm_dump_fp = fopen(pcm_dump_file_name, "wb+x"); + if (!pcm_dump_fp) { + _E("[SPEAKER RECOGNITION] File not found!"); + return -1; + } + buffer_size = 0; + + return MMI_ERROR_NONE; +} + +static int node_deactivated_cb(mmi_node_instance_h instance) +{ + _D("[MIC] Node deactivate callback is called for %p", instance); + if (nullptr == instance) { + _E("[MIC] node instance is null"); + return MMI_ERROR_INVALID_PARAMETER; + } + + try { + Recorder *recorder = g_recorder_map[instance]; + recorder->stop(); + } catch (std::out_of_range &e) { + _E("[MIC] failed to find recorder instance"); + return MMI_ERROR_INVALID_PARAMETER; + } + + if (pcm_dump_fp) + fclose(pcm_dump_fp); + pcm_dump_fp = nullptr; + buffer_size = 0; + + return MMI_ERROR_NONE; +} + +static int node_signal_received_cb(mmi_node_instance_h instance, mmi_signal_h signal) +{ + _D("Node signal received callback is called for %p", instance); + return MMI_ERROR_NONE; +} + +static int port_output_format_requested_cb(mmi_port_instance_h instance, const char *format) { + _D("Port output format request callback is called for %p", instance); + return MMI_ERROR_NONE; +} + +// TODO: It must be determined how to receive input data from the client +static int port_input_data_received_cb(mmi_port_instance_h instance, mmi_data_h data) { + _D("Port input data callback is called for %p", instance); + return MMI_ERROR_NONE; +} + +extern "C" { + + EXPORT_API void mmi_plugin_module_get_node_list() + { + mmi_node_callbacks node_callbacks { + node_initialized_cb, + node_deinitialized_cb, + node_attribute_set_cb, + node_activated_cb, + node_deactivated_cb, + node_signal_received_cb + }; + + mmi_port_callbacks audio_port_callbacks { + port_output_format_requested_cb, + port_input_data_received_cb + }; + + mmi_port_h mic_audio_port = nullptr; + mmi_port_create(&mic_audio_port); + mmi_port_set_name(mic_audio_port, "AUDIO"); + mmi_port_set_type(mic_audio_port, MMI_PORT_TYPE_OUT); + mmi_port_set_data_type(mic_audio_port, MMI_DATA_TYPE_AUDIO); + mmi_port_set_callbacks(mic_audio_port, audio_port_callbacks); + + mmi_node_h mic_node = nullptr; + mmi_standard_node_create_source(MMI_STANDARD_NODE_SOURCE_TYPE_MIC_AMBIENT, &mic_node); + mmi_node_add_port(mic_node, mic_audio_port); + mmi_node_set_callbacks(mic_node, node_callbacks); + + mmi_node_register(mic_node); + + mmi_node_destroy(mic_node); + } + +} // extern "C" diff --git a/plugins/nodes/regex-match/meson.build b/plugins/nodes/regex-match/meson.build new file mode 100644 index 0000000..f214608 --- /dev/null +++ b/plugins/nodes/regex-match/meson.build @@ -0,0 +1,27 @@ +mmi_module_regex_match_library_srcs = [ + 'mmi-module-regex-match.cpp', + 'mmi-module-regex-match-regex-candidates.h', + 'mmi-module-regex-match-regex-candidates.cpp', + ] + +dlog_dep = dependency('dlog', method : 'pkg-config') +ecore_dep = dependency('ecore', method : 'pkg-config') + +mmi_module_regex_match_deps = [ + mmi_declared_dep, + dlog_dep, + ecore_dep, + ] + +mmi_module_regex_match_include_dirs = include_directories( + '.', + '../../../capi/', + ) + +mmi_module_regex_match_library = library('mmi_module_regex_match', + mmi_module_regex_match_library_srcs, + include_directories : [ mmi_module_regex_match_include_dirs ], + dependencies : [mmi_module_regex_match_deps], + install_dir : mmi_prefix_plugindir, + install : true + ) diff --git a/plugins/nodes/regex-match/mmi-module-regex-match-regex-candidates.cpp b/plugins/nodes/regex-match/mmi-module-regex-match-regex-candidates.cpp new file mode 100644 index 0000000..ada0376 --- /dev/null +++ b/plugins/nodes/regex-match/mmi-module-regex-match-regex-candidates.cpp @@ -0,0 +1,84 @@ +/* + * Copyright (c) 2023 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 "mmi-module-regex-match-regex-candidates.h" + +#ifdef LOG_TAG +#undef LOG_TAG +#endif +#define LOG_TAG "MMI-MODULE-REGEX-MATCH" + + +RegExCandidates::RegExCandidates() +{ + _I("[REGEX MATCH] Construct RegExCandidates"); + m_candidates.clear(); +} + +RegExCandidates::~RegExCandidates() +{ + _I("[REGEX MATCH] Destruct RegExCandidates"); + m_candidates.clear(); +} + +void RegExCandidates::append_candidate(const char *regex_name, const char *regex_pattern) +{ + if (nullptr == regex_name ||nullptr == regex_pattern) { + _W("[REGEX MATCH] Regex name or pattern is null. Skip appending"); + return; + } + + _I("[REGEX MATCH] Append candidate(%s/%s)", regex_pattern, regex_name); + m_candidates.push_back(std::make_pair(std::regex(regex_pattern), std::string(regex_name))); + _D("[REGEX MATCH] The current number of candidates (%zu)", m_candidates.size()); +} + +std::optional RegExCandidates::find_matched_candidate(const char *text) +{ + if (nullptr == text) { + _E("[REGEX MATCH] text is null."); + return std::nullopt; + } + + _I("[REGEX MATCH] Find matched candidate. text(%s)", text); + + try { + for (auto &candidate : m_candidates) { + auto ®ex = candidate.first; + if (std::regex_match(text, regex)) { + _I("Result : text(%s), result(%s)", text, candidate.second.c_str()); + return candidate.second; + } + } + } catch (std::exception &e) { + _E("Exception occurs. (%s)", e.what()); + return std::nullopt; + } + + _E("[REGEX MATCH] There no matched candidate"); + return std::nullopt; +} + +void RegExCandidates::clear_candidates() +{ + _I("[REGEX MATCH] Clear current candidates"); + m_candidates.clear(); +} + diff --git a/plugins/nodes/regex-match/mmi-module-regex-match-regex-candidates.h b/plugins/nodes/regex-match/mmi-module-regex-match-regex-candidates.h new file mode 100644 index 0000000..9eed260 --- /dev/null +++ b/plugins/nodes/regex-match/mmi-module-regex-match-regex-candidates.h @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2023 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 __MMI_MODULE_REGEX_MATCH_REGEX_CANDIDATES_H__ +#define __MMI_MODULE_REGEX_MATCH_REGEX_CANDIDATES_H__ + + +#include +#include +#include +#include + +class RegExCandidates { +public: + RegExCandidates(); + ~RegExCandidates(); + + void append_candidate(const char *regex_name, const char *regex_pattern); + std::optional find_matched_candidate(const char *text); + void clear_candidates(); + +private: + std::vector> m_candidates; +}; + +#endif /* __MMI_MODULE_REGEX_MATCH_REGEX_CANDIDATES_H__ */ diff --git a/plugins/nodes/regex-match/mmi-module-regex-match.cpp b/plugins/nodes/regex-match/mmi-module-regex-match.cpp new file mode 100644 index 0000000..e110148 --- /dev/null +++ b/plugins/nodes/regex-match/mmi-module-regex-match.cpp @@ -0,0 +1,308 @@ +#include +#include +#include +#include + +#include +#include + +#include "mmi-module-regex-match-regex-candidates.h" + +#ifdef LOG_TAG +#undef LOG_TAG +#endif +#define LOG_TAG "MMI-MODULE-REGEX-MATCH" + + +static const char *INPUT_PORT_NAME_TEXT = "TEXT"; +static const char *OUTPUT_PORT_NAME_MATCHED_CANDIDATE = "MATCHED_CANDIDATE"; +static const char *OUTPUT_PORT_NAME_REJECTED = "REJECTED"; +static const char *ATTRIBUTE_NAME_CANDIDATES = "CANDIDATES"; + + +static std::map> g_node_to_regex_candidates_map; +static std::map g_input_port_to_node_map; + + +static int node_initialized_cb(mmi_node_instance_h instance) +{ + _D("[REGEX MATCH] Node initialize callback is called for %p", instance); + if (nullptr == instance) { + _E("[REGEX MATCH] Instance is null. %p", instance); + return MMI_ERROR_INVALID_PARAMETER; + } + + mmi_port_instance_h input_port = nullptr; + mmi_node_instance_find_port(instance, MMI_PORT_TYPE_IN, INPUT_PORT_NAME_TEXT, &input_port); + if (nullptr == input_port) { + _E("[REGEX MATCH] Fail to get port from node. %p", instance); + return MMI_ERROR_OPERATION_FAILED; + } + + g_input_port_to_node_map[input_port] = instance; + + try { + auto text_candidates = std::make_shared(); + g_node_to_regex_candidates_map[instance] = text_candidates; + } catch (std::bad_alloc &e) { + _E("[REGEX MATCH] Fail to allocate memory for node instance. %p", instance); + return MMI_ERROR_OUT_OF_MEMORY; + } + + return MMI_ERROR_NONE; +} + +static bool is_node_instance_valid(mmi_node_instance_h instance) +{ + if (nullptr == instance) { + _E("[REGEX MATCH] Node instance is null %p", instance); + return false; + } + + auto iterator = g_node_to_regex_candidates_map.find(instance); + if (iterator == g_node_to_regex_candidates_map.end()) { + _E("[REGEX MATCH] No instance %p", instance); + return false; + } + + return true; +} + +static int node_deinitialized_cb(mmi_node_instance_h instance) +{ + _D("[REGEX MATCH] Node deinitialize callback is called for %p", instance); + if (false == is_node_instance_valid(instance)) { + _E("[REGEX MATCH] Instance is not valid. %p", instance); + return MMI_ERROR_INVALID_PARAMETER; + } + + mmi_port_instance_h input_port = nullptr; + mmi_node_instance_find_port(instance, MMI_PORT_TYPE_IN, INPUT_PORT_NAME_TEXT, &input_port); + if (nullptr == input_port) { + _E("[REGEX MATCH] Fail to get port from node. %p", instance); + return MMI_ERROR_OPERATION_FAILED; + } + + g_input_port_to_node_map.erase(input_port); + g_node_to_regex_candidates_map.erase(instance); + + return MMI_ERROR_NONE; +} + +static int node_attribute_set_cb(mmi_node_instance_h instance, mmi_attribute_h attribute) +{ + _D("[REGEX MATCH] Node attribute set callback is called for %p", instance); + if (false == is_node_instance_valid(instance)) { + _E("[REGEX MATCH] Instance is not valid. %p", instance); + return MMI_ERROR_INVALID_PARAMETER; + } + + char *name = nullptr; + mmi_attribute_get_name(attribute, &name); + if (nullptr == name) { + _E("[REGEX MATCH] Fail to get attribute name"); + return MMI_ERROR_INVALID_PARAMETER; + } + + std::string attribute_name(name); + free(name); + name = nullptr; + + if (0 != attribute_name.compare(ATTRIBUTE_NAME_CANDIDATES)) { + _E("[REGEX MATCH] Attribute name is not same as expected one. (%s)", attribute_name.c_str()); + return MMI_ERROR_INVALID_PARAMETER; + } + + mmi_primitive_value_h value = nullptr; + mmi_primitive_value_type_e type = MMI_PRIMITIVE_VALUE_TYPE_INT; + mmi_attribute_get_value(attribute, &value); + mmi_primitive_value_get_type(value, &type); + if (MMI_PRIMITIVE_VALUE_TYPE_STRING != type) { + mmi_primitive_value_destroy(value); + _E("[REGEX MATCH] Invalid type of attribute : %d", type); + return MMI_ERROR_INVALID_PARAMETER; + } + + // TODO: Check the efficient way to set attribute for regex candidates. + size_t count = 0; + mmi_primitive_value_get_array_count(value, &count); + + auto regex_candidates = g_node_to_regex_candidates_map[instance]; + regex_candidates->clear_candidates(); + + for (size_t i = 0; i < count; i += 2) { + mmi_primitive_value_h regex_name = nullptr; + mmi_primitive_value_get_array_element(value, i, ®ex_name); + + mmi_primitive_value_h regex_pattern = nullptr; + mmi_primitive_value_get_array_element(value, i + 1, ®ex_pattern); + + const char *regex_name_value = nullptr; + mmi_primitive_value_get_string(regex_name, ®ex_name_value); + + const char *regex_pattern_value = nullptr; + mmi_primitive_value_get_string(regex_pattern, ®ex_pattern_value); + + if (nullptr != regex_name_value && nullptr != regex_pattern_value) { + _D("[REGEX MATCH] Attribute value : %zu, %s, %s", i, regex_name_value, regex_pattern_value); + regex_candidates->append_candidate(regex_name_value, regex_pattern_value); + } else { + _W("[REGEX MATCH] Fail to get string. Please check error. index(%zu)", i); + } + } + mmi_primitive_value_destroy(value); + + return MMI_ERROR_NONE; +} + +static int node_activated_cb(mmi_node_instance_h instance) +{ + _D("[REGEX MATCH] Node activate callback is called for %p", instance); + if (false == is_node_instance_valid(instance)) { + _E("[REGEX MATCH] Instance is not valid. %p", instance); + return MMI_ERROR_INVALID_PARAMETER; + } + + return MMI_ERROR_NONE; +} + +static int node_deactivated_cb(mmi_node_instance_h instance) +{ + _D("[REGEX MATCH] Node deactivate callback is called for %p", instance); + if (false == is_node_instance_valid(instance)) { + _E("[REGEX MATCH] Instance is not valid. %p", instance); + return MMI_ERROR_INVALID_PARAMETER; + } + + return MMI_ERROR_NONE; +} + +static int node_signal_received_cb(mmi_node_instance_h instance, mmi_signal_h signal) +{ + _D("[REGEX MATCH] Node signal received callback is called for %p", instance); + if (false == is_node_instance_valid(instance)) { + _E("[REGEX MATCH] Instance is not valid. %p", instance); + return MMI_ERROR_INVALID_PARAMETER; + } + + return MMI_ERROR_NONE; +} + +static int port_output_format_requested_cb(mmi_port_instance_h instance, const char *format) +{ + _D("[REGEX MATCH] Port output format request callback is called for %p", instance); + + return MMI_ERROR_NONE; +} + +static int generate_output(mmi_node_instance_h instance, const char *output_name, mmi_data_h data) +{ + mmi_port_instance_h output_port = nullptr; + mmi_node_instance_find_port(instance, MMI_PORT_TYPE_OUT, output_name, &output_port); + if (nullptr == output_port) { + _E("[REGEX MATCH] Fail to get output port. %p", instance); + return MMI_ERROR_OPERATION_FAILED; + } + + int ret = mmi_port_instance_generate_output(output_port, data); + if (MMI_ERROR_NONE != ret) { + _E("[REGEX MATCH] Fail to generate output. error(%d / %s)", ret, get_error_message(ret)); + return ret; + } + + return MMI_ERROR_NONE; +} + +static int port_input_data_received_cb(mmi_port_instance_h instance, mmi_data_h data) +{ + _D("[REGEX MATCH] Text input data callback is called: %p", instance); + auto iterator = g_input_port_to_node_map.find(instance); + if (iterator == g_input_port_to_node_map.end()) { + _E("[REGEX MATCH] Fail to find proper node instance. %p", instance); + return MMI_ERROR_INVALID_PARAMETER; + } + + auto node_instance = iterator->second; + if (false == is_node_instance_valid(node_instance)) { + _E("[REGEX MATCH] Node instance is not valid. %p", node_instance); + return MMI_ERROR_INVALID_PARAMETER; + } + + const char *text = nullptr; + mmi_data_get_text(data, &text); + if (nullptr == text) { + _E("[REGEX MATCH] Fail to get text from data"); + return MMI_ERROR_INVALID_PARAMETER; + } + + int ret = MMI_ERROR_NONE; + auto text_candidates = g_node_to_regex_candidates_map[node_instance]; + auto result = text_candidates->find_matched_candidate(text); + if (result) { + mmi_data_h matched_candidate = nullptr; + mmi_data_create_text(result->c_str(), &matched_candidate); + ret = generate_output(node_instance, OUTPUT_PORT_NAME_MATCHED_CANDIDATE, matched_candidate); + mmi_data_destroy(matched_candidate); + } else { + ret = generate_output(node_instance, OUTPUT_PORT_NAME_REJECTED, data); + } + + if (MMI_ERROR_NONE != ret) { + _E("[REGEX MATCH] Fail to generate output"); + return ret; + } + + return MMI_ERROR_NONE; +} + + +extern "C" { + + EXPORT_API void mmi_plugin_module_get_node_list() + { + mmi_node_callbacks node_callbacks { + node_initialized_cb, + node_deinitialized_cb, + node_attribute_set_cb, + node_activated_cb, + node_deactivated_cb, + node_signal_received_cb + }; + + mmi_port_callbacks text_port_callbacks { + port_output_format_requested_cb, + port_input_data_received_cb + }; + + mmi_port_h match_text_port = nullptr; + mmi_port_create(&match_text_port); + mmi_port_set_name(match_text_port, INPUT_PORT_NAME_TEXT); + mmi_port_set_type(match_text_port, MMI_PORT_TYPE_IN); + mmi_port_set_data_type(match_text_port, MMI_DATA_TYPE_TEXT); + mmi_port_set_callbacks(match_text_port, text_port_callbacks); + + mmi_port_h match_candidate_port = nullptr; + mmi_port_create(&match_candidate_port); + mmi_port_set_name(match_candidate_port, OUTPUT_PORT_NAME_MATCHED_CANDIDATE); + mmi_port_set_type(match_candidate_port, MMI_PORT_TYPE_OUT); + mmi_port_set_data_type(match_candidate_port, MMI_DATA_TYPE_TEXT); + + mmi_port_h match_rejected_port = nullptr; + mmi_port_create(&match_rejected_port); + mmi_port_set_name(match_rejected_port, OUTPUT_PORT_NAME_REJECTED); + mmi_port_set_type(match_rejected_port, MMI_PORT_TYPE_OUT); + mmi_port_set_data_type(match_rejected_port, MMI_DATA_TYPE_TEXT); + /* Callbacks for OUT ports are not needed for now */ + + mmi_node_h match_node = nullptr; + mmi_standard_node_create_logic(MMI_STANDARD_NODE_LOGIC_TYPE_REGEX_STRING_MATCH, &match_node); + mmi_node_add_port(match_node, match_text_port); + mmi_node_add_port(match_node, match_candidate_port); + mmi_node_add_port(match_node, match_rejected_port); + mmi_node_set_callbacks(match_node, node_callbacks); + mmi_node_register(match_node); + + mmi_node_destroy(match_node); + } + +} // extern "C" diff --git a/plugins/nodes/voice-touch/meson.build b/plugins/nodes/voice-touch/meson.build new file mode 100644 index 0000000..1564037 --- /dev/null +++ b/plugins/nodes/voice-touch/meson.build @@ -0,0 +1,36 @@ +mmi_module_node_voice_touch_library_srcs = [ + 'mmi-module-voice-touch.cpp', + 'mmi-module-voice-touch-common.h', + 'mmi-module-voice-touch-candidates.h', + 'mmi-module-voice-touch-candidates.cpp', + 'mmi-module-voice-touch-string-utility.h', + 'mmi-module-voice-touch-string-utility.cpp', + 'mmi-module-voice-touch-screen-info.h', + 'mmi-module-voice-touch-screen-info.cpp', + 'mmi-module-voice-touch-data-utility.h', + 'mmi-module-voice-touch-data-utility.cpp', + 'mmi-module-voice-touch-primitive-utility.h', + 'mmi-module-voice-touch-primitive-utility.cpp', + ] + +dlog_dep = dependency('dlog', method : 'pkg-config') +ecore_dep = dependency('ecore', method : 'pkg-config') + +mmi_module_node_voice_touch_deps = [ + mmi_declared_dep, + dlog_dep, + ecore_dep, + ] + +mmi_module_node_voice_touch_include_dirs = include_directories( + '.', + '../../../capi/', + ) + +mmi_module_node_voice_touch_library = library('mmi_module_node_voice_touch', + mmi_module_node_voice_touch_library_srcs, + include_directories : [ mmi_module_node_voice_touch_include_dirs ], + dependencies : [mmi_module_node_voice_touch_deps], + install_dir : mmi_prefix_plugindir, + install : true + ) diff --git a/plugins/nodes/voice-touch/mmi-module-voice-touch-candidates.cpp b/plugins/nodes/voice-touch/mmi-module-voice-touch-candidates.cpp new file mode 100644 index 0000000..07e08bc --- /dev/null +++ b/plugins/nodes/voice-touch/mmi-module-voice-touch-candidates.cpp @@ -0,0 +1,299 @@ +/* + * Copyright (c) 2023 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 "mmi-module-voice-touch-candidates.h" +#include "mmi-module-voice-touch-string-utility.h" + +#ifdef LOG_TAG +#undef LOG_TAG +#endif +#define LOG_TAG "MMI-MODULE-VOICE-TOUCH" + + +static constexpr double WORD_MATCHING_RATE = 0.3; +static constexpr double CHAR_MATCHING_RATE = 0.35; +static constexpr size_t MIN_WORD_SIZE = 3; + + +VoiceTouchCandidates::VoiceTouchCandidates() +{ + _I("[VOICE TOUCH] Construct VoiceTouchCandidates"); + m_candidates.clear(); +} + +VoiceTouchCandidates::~VoiceTouchCandidates() +{ + _I("[VOICE TOUCH] Destruct VoiceTouchCandidates"); + m_candidates.clear(); +} + +void VoiceTouchCandidates::set_candidates(const std::vector &candidates) +{ + _I("[VOICE TOUCH] Set candidates(%zu)", candidates.size()); + m_candidates.clear(); + m_candidates = candidates; + _D("[VOICE TOUCH] The current number of candidates (%zu)", m_candidates.size()); +} + +std::optional VoiceTouchCandidates::find_matched_candidate(size_t index) +{ + _I("[VOICE TOUCH] Find matched candidate. index(%zu)", index); + std::optional result = find_matched_candidate_by_index(index); + if (result) { + _I("[VOICE TOUCH] Success To find voice touch candidate(%zu)", index); + return result; + } + + return std::nullopt; +} + +std::optional VoiceTouchCandidates::find_matched_candidate_by_index(size_t index) +{ + _I("[VOICE TOUCH] Find matched candidate by index (%zu)", index); + + if (index > m_candidates.size()) { + _E("[VOICE TOUCH] Invalid index. %zu", index); + return std::nullopt; + } + + for (auto &candidate : m_candidates) { + if (candidate.index == index) { + _I("[VOICE TOUCH] result candidate(%s)", candidate.label.c_str()); + return candidate; + } + } + + return std::nullopt; +} + +std::optional VoiceTouchCandidates::find_matched_candidate(const char *text) +{ + _I("[VOICE TOUCH] Find matched candidate. text(%s)", text); + auto lower_cased_text = StringUtility::make_lower_case(std::string(text)); + auto splited_text = StringUtility::split_text(lower_cased_text, ' '); + + std::optional result; + result = find_matched_candidate_by_text(splited_text); + if (result) { + _I("[VOICE TOUCH] Success To find voice touch candidate(%s)", text); + return result; + } + + result = find_matched_candidate_by_word(splited_text); + if (result) { + _I("[VOICE TOUCH] Success To find voice touch candidate(%s)", text); + return result; + } + + result = find_matched_candidate_by_char(splited_text); + if (result) { + _I("[VOICE TOUCH] Success To find voice touch candidate(%s)", text); + return result; + } + + _I("[VOICE TOUCH] There is no matched candidate by text. Try to find by index. text(%s)", text); + size_t index = 0; + try { + index = std::stoull(lower_cased_text); + } catch (std::exception &e) { + _E("[VOICE TOUCH] Fail string to int conversion. (%s)", e.what()); + return std::nullopt; + } + + result = find_matched_candidate_by_index(index); + if (result) { + _I("[VOICE TOUCH] Success To find voice touch candidate(%zu)", index); + return result; + } + + return std::nullopt; +} + +std::optional VoiceTouchCandidates::find_matched_candidate_by_text(const std::vector &splited_text) +{ + _I("[VOICE TOUCH] Find matched candidate by full text."); + + for (auto &candidate : m_candidates) { + std::string label = StringUtility::make_lower_case(candidate.label); + + bool is_valid = true; + for (auto &word : splited_text) { + if (label.find(word) == std::string::npos) { + is_valid = false; + break; + } + } + + if (is_valid) { + _I("[VOICE TOUCH] Success To find voice touch candidate(%s)", candidate.label.c_str()); + return candidate; + } + } + + return std::nullopt; +} + +std::optional VoiceTouchCandidates::find_matched_candidate_by_word(const std::vector &splited_text) +{ + _I("[VOICE TOUCH] Find matched candidate by word."); + + size_t max_matching_word_num = static_cast(splited_text.size() * WORD_MATCHING_RATE); + if (0 == max_matching_word_num) { + max_matching_word_num = 1; + } + _I("[VOICE TOUCH] Threshold of matching word number (%zu)", max_matching_word_num); + + std::optional result; + for (auto &candidate : m_candidates) { + std::string label = StringUtility::make_lower_case(candidate.label); + + size_t matching_word_num = 0; + for (auto &word : splited_text) { + if (label.find(word) != std::string::npos) { + matching_word_num++; + } + } + + if (matching_word_num >= max_matching_word_num) { + max_matching_word_num = matching_word_num; + result = candidate; + } + } + + if (result) { + _I("[VOICE TOUCH] Success To find voice touch candidate which has highest matching rate(%s)", result->label.c_str()); + return result; + } + + return std::nullopt; +} + +std::optional VoiceTouchCandidates::find_matched_candidate_by_char(const std::vector &splited_text) +{ + _I("[VOICE TOUCH] Find matched candidate by character."); + + auto filter_splited_text = [](std::string x) -> bool { + return x.size() >= MIN_WORD_SIZE; + }; + std::vector filtered_words; + std::copy_if(splited_text.begin(), splited_text.end(), std::back_inserter(filtered_words), filter_splited_text); + + // TODO: Current algorithm doesn't consider the original order of the words. It can affect the total score. + std::optional result; + size_t result_score = std::numeric_limits::max(); + for (auto &candidate : m_candidates) { + auto candidate_label = StringUtility::make_lower_case(candidate.label); + auto candidate_words = StringUtility::split_text(candidate_label, ' '); + + std::vector filtered_candidate_words; + std::copy_if(candidate_words.begin(), candidate_words.end(), std::back_inserter(filtered_candidate_words), filter_splited_text); + _D("[VOICE TOUCH] candidate(%s)", candidate_label.c_str()); + + size_t score = 0; + for (auto &word : filtered_words) { + const size_t MIN_THRESHOLD = static_cast(word.size() * CHAR_MATCHING_RATE) + 1; + size_t min_score = MIN_THRESHOLD; + _D("[VOICE TOUCH] Minimum matching character number (%zu)", MIN_THRESHOLD); + + for (auto &candidate_word : filtered_candidate_words) { + size_t edit_distance = calcualte_edit_distance(candidate_word, word); + + if (min_score > edit_distance) { + min_score = edit_distance; + } + } + + score += (min_score < MIN_THRESHOLD ? min_score : word.size()); + _D("[VOICE TOUCH] word(%s), label word(%s), minscore(%zu), score(%zu)", word.c_str(), candidate_label.c_str(), min_score, score); + } + + _D("[VOICE TOUCH] result score(%zu), score(%zu)", result_score, score); + if (result_score > score) { + result_score = score; + result = candidate; + } + } + + auto accumulate_size = [](size_t sum, std::string word) -> size_t + { + return sum + word.size(); + }; + size_t threshold = std::accumulate(filtered_words.begin(), filtered_words.end(), static_cast(0), accumulate_size); + if (threshold > result_score) { + _I("[VOICE TOUCH] result candidate(%s)", result->label.c_str()); + return result; + } + + return std::nullopt; +} + +size_t VoiceTouchCandidates::calcualte_edit_distance(const std::string &text1, const std::string &text2) +{ + // INFO: This is for calculating Levenshtein distance(a.k.a edit distance) as similarity matric. + std::string long_str; + std::string short_str; + if (text1.size() > text2.size()) { + long_str = text1; + short_str = text2; + } else { + long_str = text2; + short_str = text1; + } + + size_t long_str_size = long_str.size(); + size_t short_str_size = short_str.size(); + + size_t cost_array1[long_str_size + 1]{}; + size_t cost_array2[long_str_size + 1]{}; + + size_t *cost = cost_array1; + size_t *ncost = cost_array2; + + for (size_t l = 0; l <= long_str_size; l++) { + cost[l] = l; + } + + for (size_t s = 1; s <= short_str_size; s++) { + ncost[0] = s; + + for (size_t l = 1; l <= long_str_size; l++) { + size_t match = (short_str[s - 1] == long_str[l - 1] ? 0 : 1); + + size_t rep = cost[l - 1] + match; + size_t ins = cost[l] + 1; + size_t del = ncost[l - 1] + 1; + + ncost[l] = std::min({rep, ins, del}); + } + + std::swap(ncost, cost); + } + + return cost[long_str_size]; +} + +void VoiceTouchCandidates::clear_candidates() +{ + _I("[VOICE TOUCH] Clear current candidates"); + m_candidates.clear(); +} diff --git a/plugins/nodes/voice-touch/mmi-module-voice-touch-candidates.h b/plugins/nodes/voice-touch/mmi-module-voice-touch-candidates.h new file mode 100644 index 0000000..29b6f6a --- /dev/null +++ b/plugins/nodes/voice-touch/mmi-module-voice-touch-candidates.h @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2023 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 __MMI_MODULE_VOICE_TOUCH_CANDIDATES_H__ +#define __MMI_MODULE_VOICE_TOUCH_CANDIDATES_H__ + + +#include +#include +#include +#include + +#include "mmi-module-voice-touch-common.h" + + +class VoiceTouchCandidates { +public: + VoiceTouchCandidates(); + ~VoiceTouchCandidates(); + + void set_candidates(const std::vector &candidates); + std::optional find_matched_candidate(size_t index); + std::optional find_matched_candidate(const char *text); + void clear_candidates(); + +private: + std::optional find_matched_candidate_by_index(size_t index); + std::optional find_matched_candidate_by_text(const std::vector &splited_text); + std::optional find_matched_candidate_by_word(const std::vector &splited_text); + std::optional find_matched_candidate_by_char(const std::vector &splited_text); + size_t calcualte_edit_distance(const std::string &text1, const std::string &text2); + +private: + std::vector m_candidates; +}; + +#endif /* __MMI_MODULE_VOICE_TOUCH_CANDIDATES_H__ */ diff --git a/plugins/nodes/voice-touch/mmi-module-voice-touch-common.h b/plugins/nodes/voice-touch/mmi-module-voice-touch-common.h new file mode 100644 index 0000000..18e0a7e --- /dev/null +++ b/plugins/nodes/voice-touch/mmi-module-voice-touch-common.h @@ -0,0 +1,60 @@ + +/* + * Copyright (c) 2023 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 __MMI_MODULE_VOICE_TOUCH_COMMON_H__ +#define __MMI_MODULE_VOICE_TOUCH_COMMON_H__ + + +#include + + +enum voice_touch_mode_e { + VOICE_TOUCH_MODE_TEXT, + VOICE_TOUCH_MODE_INDEX, + VOICE_TOUCH_MODE_GRID +}; + +struct area_coordinates_s { + int x{}; + int y{}; + int w{}; + int h{}; +}; + +struct voice_touch_candidate_s { + voice_touch_mode_e mode{VOICE_TOUCH_MODE_TEXT}; + area_coordinates_s candidate_area{}; + size_t index{}; + std::string object_id{}; + std::string label{}; +}; + +struct screen_element_s { + area_coordinates_s element_area{}; + std::string object_id{}; + std::string label{}; + std::string role{}; +}; + +struct grid_information_s { + size_t col_count{}; + size_t row_count{}; +}; + + +#endif /* __MMI_MODULE_VOICE_TOUCH_COMMON_H__ */ diff --git a/plugins/nodes/voice-touch/mmi-module-voice-touch-data-utility.cpp b/plugins/nodes/voice-touch/mmi-module-voice-touch-data-utility.cpp new file mode 100644 index 0000000..24c43a7 --- /dev/null +++ b/plugins/nodes/voice-touch/mmi-module-voice-touch-data-utility.cpp @@ -0,0 +1,246 @@ +/* + * Copyright (c) 2022 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 "mmi-module-voice-touch-data-utility.h" + + +#ifdef LOG_TAG +#undef LOG_TAG +#endif +#define LOG_TAG "MMI-MODULE-VOICE-TOUCH" + +#define NAME_OF_VARIABLE(x) (#x) + + +MmiDataHelper::MmiDataHelper(mmi_data_h data, bool ownership) noexcept +{ + _I("[VOICE TOUCH] Construct helper class for wrapping existing data(%p). ownership(%s)", data, ownership ? "True" : "False"); + m_handle = data; + m_ownership = ownership; +} + +MmiDataHelper::MmiDataHelper(const std::vector &candidates) noexcept +{ + _I("[VOICE TOUCH] Construct helper class for candidates output data"); + m_handle = make_internal_mmi_data(candidates); + m_ownership = true; +} + +MmiDataHelper::MmiDataHelper(const voice_touch_candidate_s &candidate, const screen_element_s &element) noexcept +{ + _I("[VOICE TOUCH] Construct helper class for matched result output data"); + m_handle = make_internal_mmi_data(candidate, element); + m_ownership = true; +} + +MmiDataHelper::MmiDataHelper(const char *text) noexcept +{ + _I("[VOICE TOUCH] Construct helper class for rejected output data"); + m_handle = make_internal_mmi_data(text); + m_ownership = true; +} + +MmiDataHelper::~MmiDataHelper() +{ + if (m_ownership) { + mmi_data_destroy(m_handle); + } +} + +mmi_data_h MmiDataHelper::get_handle() const +{ + return m_handle; +} + +mmi_data_type_e MmiDataHelper::get_type() const +{ + mmi_data_type_e type = MMI_DATA_TYPE_ANY; + int ret = mmi_data_get_type(m_handle, &type); + if (MMI_ERROR_NONE != ret) { + _E("[VOICE TOUCH] Fail to get type. ret(%d)", ret); + throw ret; + } + + return type; +} + +int MmiDataHelper::get_integer_struct_member(const char *name) const +{ + mmi_data_h member_data = nullptr; + mmi_data_get_struct_element(m_handle, name, &member_data); + + int value = 0; + int ret = mmi_data_get_int(member_data, &value); + if (MMI_ERROR_NONE != ret) { + throw ret; + } + + return value; +} + +const char *MmiDataHelper::get_text_struct_member(const char *name) const +{ + mmi_data_h member_data = nullptr; + mmi_data_get_struct_element(m_handle, name, &member_data); + + const char *value = nullptr; + int ret = mmi_data_get_text(member_data, &value); + if (MMI_ERROR_NONE != ret) { + throw ret; + } + + return value; +} + +size_t MmiDataHelper::get_array_count() const +{ + _I("[VOICE TOUCH] Get integer value from handle."); + size_t value = 0; + int ret = mmi_data_get_array_count(m_handle, &value); + if (MMI_ERROR_NONE != ret) { + throw ret; + } + + return value; +} + +MmiDataHelper MmiDataHelper::get_array_element(size_t index) const +{ + _I("[VOICE TOUCH] Get array element on index(%zu) from handle.", index); + mmi_data_h value = nullptr; + int ret = mmi_data_get_array_element(m_handle, index, &value); + if (MMI_ERROR_NONE != ret) { + throw ret; + } + + if (nullptr == value) { + throw MMI_ERROR_OPERATION_FAILED; + } + + return MmiDataHelper(value); +} + +mmi_data_h MmiDataHelper::make_internal_mmi_data(const std::vector &candidates) +{ + mmi_data_h output_data = nullptr; + mmi_data_create_array(&output_data); + if (nullptr == output_data) { + _E("[VOICE TOUCH] Fail to allocate memory for output"); + return nullptr; + } + + for (auto &candidate : candidates) { + mmi_data_h candidate_info = nullptr; + mmi_data_create_struct(&candidate_info); + if (nullptr == output_data) { + _E("[VOICE TOUCH] Fail to allocate memory for output"); + mmi_data_destroy(output_data); + return nullptr; + } + + set_integer_struct_member(candidate_info, "mode", candidate.mode); + set_integer_struct_member(candidate_info, "coord_x", candidate.candidate_area.x); + set_integer_struct_member(candidate_info, "coord_y", candidate.candidate_area.y); + set_integer_struct_member(candidate_info, "width", candidate.candidate_area.w); + set_integer_struct_member(candidate_info, "height", candidate.candidate_area.h); + set_text_struct_member(candidate_info, "label", candidate.label.c_str()); + + mmi_data_add_array_element(output_data, candidate_info); + } + + return output_data; +} + +mmi_data_h MmiDataHelper::make_internal_mmi_data(const voice_touch_candidate_s &candidate, const screen_element_s &element) +{ + mmi_data_h output_data = nullptr; + mmi_data_h candidate_info = nullptr; + mmi_data_h click_info = nullptr; + mmi_data_create_struct(&output_data); + mmi_data_create_struct(&candidate_info); + mmi_data_create_struct(&click_info); + if (nullptr == output_data || nullptr == candidate_info || nullptr == click_info) { + mmi_data_destroy(output_data); + mmi_data_destroy(candidate_info); + mmi_data_destroy(click_info); + _E("[VOICE TOUCH] Fail to allocate memory for data."); + return nullptr; + } + + set_integer_struct_member(candidate_info, "mode", candidate.mode); + set_integer_struct_member(candidate_info, "coord_x", candidate.candidate_area.x); + set_integer_struct_member(candidate_info, "coord_y", candidate.candidate_area.y); + set_integer_struct_member(candidate_info, "width", candidate.candidate_area.w); + set_integer_struct_member(candidate_info, "height", candidate.candidate_area.h); + set_text_struct_member(candidate_info, "label", candidate.label.c_str()); + + auto &element_area = element.element_area; + set_integer_struct_member(click_info, "x", element_area.x + element_area.w / 2); + set_integer_struct_member(click_info, "y", element_area.y + element_area.h / 2); + set_text_struct_member(click_info, "object_id", element.object_id.c_str()); + + mmi_data_set_struct_element(output_data, NAME_OF_VARIABLE(candidate_info), candidate_info); + mmi_data_set_struct_element(output_data, NAME_OF_VARIABLE(click_info), click_info); + + return output_data; +} + +mmi_data_h MmiDataHelper::make_internal_mmi_data(const char *text) +{ + mmi_data_h output_data = nullptr; + mmi_data_create_text(text, &output_data); + if (nullptr == output_data) { + _E("[VOICE TOUCH] Fail to allocate memory for output"); + return nullptr; + } + + return output_data; +} + +void MmiDataHelper::set_integer_struct_member(mmi_data_h struct_data, const char *member_name, int value) +{ + mmi_data_h member_data = nullptr; + mmi_data_create_int(value, &member_data); + if (nullptr == member_data) { + _E("[VOICE TOUCH] Fail to allocate memory for data."); + return; + } + + if (MMI_ERROR_NONE != mmi_data_set_struct_element(struct_data, member_name, member_data)) { + _E("[VOICE TOUCH] Fail to set struct integer type member. name(%s). value(%d)", member_name, value); + mmi_data_destroy(member_data); + } +} + +void MmiDataHelper::set_text_struct_member(mmi_data_h struct_data, const char *member_name, const char *value) +{ + mmi_data_h member_data = nullptr; + mmi_data_create_text(value, &member_data); + if (nullptr == member_data) { + _E("[VOICE TOUCH] Fail to allocate memory for data."); + return; + } + + if (MMI_ERROR_NONE != mmi_data_set_struct_element(struct_data, member_name, member_data)) { + _E("[VOICE TOUCH] Fail to set struct text type member. name(%s). value(%s)", member_name, value); + mmi_data_destroy(member_data); + } +} diff --git a/plugins/nodes/voice-touch/mmi-module-voice-touch-data-utility.h b/plugins/nodes/voice-touch/mmi-module-voice-touch-data-utility.h new file mode 100644 index 0000000..db0cc42 --- /dev/null +++ b/plugins/nodes/voice-touch/mmi-module-voice-touch-data-utility.h @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2023 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 __MMI_MODULE_VOICE_TOUCH_DATA_UTILITY_H__ +#define __MMI_MODULE_VOICE_TOUCH_DATA_UTILITY_H__ + + +#include + +#include +#include "mmi-module-voice-touch-common.h" + +class MmiDataHelper { +public: + MmiDataHelper(mmi_data_h data, bool ownership = false) noexcept; + MmiDataHelper(const std::vector &candidates) noexcept; + MmiDataHelper(const voice_touch_candidate_s &candidate, const screen_element_s &element) noexcept; + MmiDataHelper(const char *text) noexcept; + ~MmiDataHelper(); + + // For common + mmi_data_h get_handle() const; + mmi_data_type_e get_type() const; + + // For struct type + int get_integer_struct_member(const char *name) const; + const char *get_text_struct_member(const char *name) const; + + // For array type + size_t get_array_count() const; + MmiDataHelper get_array_element(size_t index) const; + +private: + mmi_data_h make_internal_mmi_data(const std::vector &candidates); + mmi_data_h make_internal_mmi_data(const voice_touch_candidate_s &candidate, const screen_element_s &element); + mmi_data_h make_internal_mmi_data(const char *text); + + void set_integer_struct_member(mmi_data_h struct_data, const char *member_name, int value); + void set_text_struct_member(mmi_data_h struct_data, const char *member_name, const char *value); + +private: + mmi_data_h m_handle; + bool m_ownership; +}; + +#endif /* __MMI_MODULE_VOICE_TOUCH_DATA_UTILITY_H__ */ diff --git a/plugins/nodes/voice-touch/mmi-module-voice-touch-primitive-utility.cpp b/plugins/nodes/voice-touch/mmi-module-voice-touch-primitive-utility.cpp new file mode 100644 index 0000000..6c39a1f --- /dev/null +++ b/plugins/nodes/voice-touch/mmi-module-voice-touch-primitive-utility.cpp @@ -0,0 +1,102 @@ +/* + * Copyright (c) 2022 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 "mmi-module-voice-touch-primitive-utility.h" + + +#ifdef LOG_TAG +#undef LOG_TAG +#endif +#define LOG_TAG "MMI-MODULE-VOICE-TOUCH" + + +MmiPrimitiveHelper::MmiPrimitiveHelper(mmi_primitive_value_h handle, bool ownership) noexcept +{ + _I("[VOICE TOUCH] Construct helper class for primitive value handle"); + m_handle = handle; + m_ownership = ownership; +} + +MmiPrimitiveHelper::~MmiPrimitiveHelper() +{ + _I("[VOICE TOUCH] Destruct helper class for primitive value handle"); + if (m_ownership) { + mmi_primitive_value_destroy(m_handle); + } +} + +mmi_primitive_value_h MmiPrimitiveHelper::get_handle() const +{ + _I("[VOICE TOUCH] Get raw primitive value handle. (%p)", m_handle); + return m_handle; +} + +mmi_primitive_value_type_e MmiPrimitiveHelper::get_type() const +{ + _I("[VOICE TOUCH] Get type from handle."); + mmi_primitive_value_type_e type = MMI_PRIMITIVE_VALUE_TYPE_BOOL; + int ret = mmi_primitive_value_get_type(m_handle, &type); + if (MMI_ERROR_NONE != ret) { + throw ret; + } + + return type; +} + +int MmiPrimitiveHelper::get_integer_value() const +{ + _I("[VOICE TOUCH] Get integer value from handle."); + int value = -1; + int ret = mmi_primitive_value_get_int(m_handle, &value); + if (MMI_ERROR_NONE != ret) { + throw ret; + } + + return value; +} + +size_t MmiPrimitiveHelper::get_array_count() const +{ + _I("[VOICE TOUCH] Get integer value from handle."); + size_t value = 0; + int ret = mmi_primitive_value_get_array_count(m_handle, &value); + if (MMI_ERROR_NONE != ret) { + throw ret; + } + + return value; +} + +MmiPrimitiveHelper MmiPrimitiveHelper::get_array_element(size_t index) const +{ + _I("[VOICE TOUCH] Get array element on index(%zu) from handle.", index); + mmi_primitive_value_h value = nullptr; + int ret = mmi_primitive_value_get_array_element(m_handle, index, &value); + if (MMI_ERROR_NONE != ret) { + throw ret; + } + + if (nullptr == value) { + throw MMI_ERROR_OPERATION_FAILED; + } + + return MmiPrimitiveHelper(value); +} diff --git a/plugins/nodes/voice-touch/mmi-module-voice-touch-primitive-utility.h b/plugins/nodes/voice-touch/mmi-module-voice-touch-primitive-utility.h new file mode 100644 index 0000000..32c70a4 --- /dev/null +++ b/plugins/nodes/voice-touch/mmi-module-voice-touch-primitive-utility.h @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2023 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 __MMI_MODULE_VOICE_TOUCH_PRIMITIVE_UTILITY_H__ +#define __MMI_MODULE_VOICE_TOUCH_PRIMITIVE_UTILITY_H__ + + +#include + +class MmiPrimitiveHelper { +public: + MmiPrimitiveHelper(mmi_primitive_value_h handle, bool ownership = false) noexcept; + ~MmiPrimitiveHelper(); + + // For common + mmi_primitive_value_h get_handle() const; + mmi_primitive_value_type_e get_type() const; + + // For integer value + int get_integer_value() const; + + // For array value + size_t get_array_count() const; + MmiPrimitiveHelper get_array_element(size_t index) const; + +private: + +private: + mmi_primitive_value_h m_handle; + bool m_ownership; +}; + +#endif /* __MMI_MODULE_VOICE_TOUCH_PRIMITIVE_UTILITY_H__ */ diff --git a/plugins/nodes/voice-touch/mmi-module-voice-touch-screen-info.cpp b/plugins/nodes/voice-touch/mmi-module-voice-touch-screen-info.cpp new file mode 100644 index 0000000..7aa456e --- /dev/null +++ b/plugins/nodes/voice-touch/mmi-module-voice-touch-screen-info.cpp @@ -0,0 +1,343 @@ +/* + * Copyright (c) 2023 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 "mmi-module-voice-touch-screen-info.h" +#include "mmi-module-voice-touch-data-utility.h" + + +#ifdef LOG_TAG +#undef LOG_TAG +#endif +#define LOG_TAG "MMI-MODULE-VOICE-TOUCH" + + +static const double WORD_MATCHING_RATE = 0.3; +static const double CHAR_MATCHING_RATE = 0.35; +static const size_t MIN_WORD_SIZE = 3; + + +ScreenElements::ScreenElements() +{ + _I("[VOICE TOUCH] Construct ScreenElements"); + m_screen_width = 1920; + m_screen_height = 1080; + m_grid_depth = 0; + m_parent_grid_area = { + .x = 0, + .y = 0, + .w = m_screen_width, + .h = m_screen_height + }; + + m_grid_informations.clear(); + m_grid_informations.push_back(grid_information_s{ + .col_count = 7, + .row_count = 5 + }); + m_grid_informations.push_back(grid_information_s{ + .col_count = 4, + .row_count = 3 + }); + + m_screen_elements.clear(); +} + +ScreenElements::~ScreenElements() +{ + _I("[VOICE TOUCH] Destruct ScreenElements"); + m_screen_width = 1920; + m_screen_height = 1080; + m_grid_depth = 0; + m_parent_grid_area = { + .x = 0, + .y = 0, + .w = m_screen_width, + .h = m_screen_height + }; + m_grid_informations.clear(); + m_screen_elements.clear(); +} + +void ScreenElements::set_screen_size(int width, int height) +{ + _I("[VOICE TOUCH] Set screen size information. w(%d), h(%d)", width, height); + m_screen_width = width; + m_screen_height = height; +} + +void ScreenElements::set_grid_informations(const std::vector &grid_informations) +{ + _I("[VOICE TOUCH] Set grid information according to each depth. maximum depth(%zu)", grid_informations.size()); + m_grid_informations = grid_informations; +} + +void ScreenElements::set_screen_elements(const MmiDataHelper &screen_elements) +{ + _I("[VOICE TOUCH] Set screen elements from mmi data"); + try { + size_t counts = screen_elements.get_array_count(); + + m_screen_elements.clear(); + for (size_t i = 0; i < counts; i++) { + auto element = screen_elements.get_array_element(i); + m_screen_elements.push_back(make_screen_element(element)); + } + } catch (mmi_error_e &error) { + _E("[VOICE TOUCH] Fail to make screen element. (%d)", error); + return; + } + + _D("[VOICE TOUCH] The current number of elements (%zu)", m_screen_elements.size()); +} + +screen_element_s ScreenElements::make_screen_element(MmiDataHelper &data) +{ + area_coordinates_s element_area{ + .x = data.get_integer_struct_member("coord_x"), + .y = data.get_integer_struct_member("coord_y"), + .w = data.get_integer_struct_member("width"), + .h = data.get_integer_struct_member("height") + }; + + screen_element_s screen_element{ + .element_area = element_area, + .object_id = std::string(data.get_text_struct_member("object_id")), + .label = std::string(data.get_text_struct_member("label")), + .role = std::string(data.get_text_struct_member("role")) + }; + + return screen_element; +} + +void ScreenElements::set_grid_area(const area_coordinates_s &grid_area) +{ + _I("[VOICE TOUCH] Set screen area information x(%d) / y(%d) / w(%d)/ h(%d)", + grid_area.x, grid_area.y, grid_area.w, grid_area.h); + m_parent_grid_area = grid_area; +} + +void ScreenElements::increase_grid_depth() +{ + m_grid_depth++; + _I("[VOICE TOUCH] Increase current grid depth. (%zu)", m_grid_depth); +} + +void ScreenElements::reset_grid_depth() +{ + _I("[VOICE TOUCH] Reset current grid depth."); + m_grid_depth = 0; + m_parent_grid_area = area_coordinates_s{ + .x = 0, + .y = 0, + .w = m_screen_width, + .h = m_screen_height + }; +} + +bool ScreenElements::is_maximum_grid_depth() +{ + bool result = (m_grid_depth == m_grid_informations.size()); + _I("[VOICE TOUCH] Check current grid depth reaches to maximum. current(%zu) / max(%zu). result(%s)", + m_grid_depth, m_grid_informations.size(), result ? "True" : "False"); + + return result; +} + +std::vector ScreenElements::make_voice_touch_candidates(voice_touch_mode_e mode) +{ + _I("[VOICE TOUCH] Make voice touch candidates according to the mode. (%d)", mode); + std::vector candidates; + + switch (mode) + { + case VOICE_TOUCH_MODE_TEXT: + fill_text_label_candidates(candidates); + break; + + case VOICE_TOUCH_MODE_INDEX: + fill_index_label_candidates(candidates); + break; + + case VOICE_TOUCH_MODE_GRID: + fill_grid_label_candidates(candidates); + break; + + default: + _E("[VOICE TOUCH] Invalid voice touch mode (%d)", mode); + break; + } + + return candidates; +} + +void ScreenElements::fill_grid_label_candidates(std::vector &candidates) +{ + if (m_grid_depth >= m_grid_informations.size()) { + _E("[VOICE TOUCH] Current grid depth is out of range (%zu) / (%zu)", m_grid_depth, m_grid_informations.size()); + return; + } + + size_t col_count = m_grid_informations.at(m_grid_depth).col_count; + size_t row_count = m_grid_informations.at(m_grid_depth).row_count; + + size_t grid_width = m_parent_grid_area.w / col_count; + size_t grid_height = m_parent_grid_area.h / row_count; + + size_t remain_width = m_parent_grid_area.w % col_count; + size_t remain_height = m_parent_grid_area.h % row_count; + + // TODO: Enhance the calculation to uniformly seperate the grid + size_t index = 1; + for (size_t y = 0; y < row_count; y++) { + for (size_t x = 0; x < col_count; x++) { + area_coordinates_s candidate_area{ + .x = static_cast(m_parent_grid_area.x + (x * grid_width)), + .y = static_cast(m_parent_grid_area.y + (y * grid_height)), + .w = static_cast((x == col_count - 1 ? grid_width + remain_width : grid_width)), + .h = static_cast((y == row_count - 1 ? grid_height + remain_height : grid_height)) + }; + + voice_touch_candidate_s candidate{ + .mode = VOICE_TOUCH_MODE_GRID, + .candidate_area = candidate_area, + .index = index, + .object_id = std::string(), + .label = std::to_string(index) + }; + + candidates.push_back(candidate); + index++; + } + } +} + +void ScreenElements::fill_index_label_candidates(std::vector &candidates) +{ + size_t index = 1; + for (auto &element : m_screen_elements) { + voice_touch_candidate_s candidate{ + .mode = VOICE_TOUCH_MODE_INDEX, + .candidate_area = element.element_area, + .index = index, + .object_id = element.object_id, + .label = std::to_string(index) + }; + + candidates.push_back(candidate); + index++; + } +} + +void ScreenElements::fill_text_label_candidates(std::vector &candidates) +{ + size_t index = 1; + for (auto &element : m_screen_elements) { + voice_touch_candidate_s candidate{ + .mode = VOICE_TOUCH_MODE_TEXT, + .candidate_area = element.element_area, + .index = index, + .object_id = element.object_id, + .label = (element.label.empty() ? std::to_string(index) : element.label) + }; + + candidates.push_back(candidate); + index++; + } +} + +std::optional ScreenElements::find_related_screen_element(const voice_touch_candidate_s &candidate) +{ + switch (candidate.mode) + { + case VOICE_TOUCH_MODE_TEXT: + case VOICE_TOUCH_MODE_INDEX: + return find_exact_element_by_object_id(candidate.object_id); + + case VOICE_TOUCH_MODE_GRID: + return find_best_element_in_grid(candidate.candidate_area); + + default: + _E("[VOICE TOUCH] Invalid voice touch mode (%d)", candidate.mode); + break; + } + + return std::nullopt; +} + +std::optional ScreenElements::find_exact_element_by_object_id(const std::string &object_id) +{ + auto is_same_element = [object_id](const screen_element_s &element) -> bool { + return element.object_id == object_id; + }; + + auto result = std::find_if(m_screen_elements.begin(), m_screen_elements.end(), is_same_element); + if (result == m_screen_elements.end()) { + _E("[VOICE TOUCH] No candidates which has object id(%s)", object_id.c_str()); + return std::nullopt; + } + + _I("[VOICE TOUCH] Result. object Id(%s)", object_id.c_str()); + return *result; +} + +std::optional ScreenElements::find_best_element_in_grid(const area_coordinates_s &grid_area) +{ + int grid_x1 = grid_area.x; + int grid_x2 = grid_area.x + grid_area.w; + int grid_y1 = grid_area.y; + int grid_y2 = grid_area.y + grid_area.h; + + auto is_in_area = [grid_x1, grid_x2, grid_y1, grid_y2](const screen_element_s &element) -> bool { + auto &area = element.element_area; + if (area.x >= grid_x2 || area.x + area.w <= grid_x1 || area.y > grid_y2 || area.y + area.h <= grid_y1) { + return false; + } + + return true; + }; + + std::vector filtered_elements; + std::copy_if(m_screen_elements.begin(), m_screen_elements.end(), std::back_inserter(filtered_elements), is_in_area); + if (filtered_elements.empty()) { + _E("[VOICE TOUCH] No candidates in grid (%d, %d / %d, %d)", grid_x1, grid_y1, grid_x2, grid_y2); + return std::nullopt; + } + + std::optional result; + int maximum_area_size = -1; + for (auto &element : filtered_elements) { + auto &area = element.element_area; + int dx = std::min(grid_x2, area.x + area.w) - std::max(grid_x1, area.x); + int dy = std::min(grid_y2, area.y + area.h) - std::max(grid_y1, area.y); + + int area_size = dx * dy; + if (maximum_area_size < area_size) { + maximum_area_size = area_size; + result = element; + } + } + + _I("[VOICE TOUCH] Result. object Id(%s)", result->object_id.c_str()); + return result; +} diff --git a/plugins/nodes/voice-touch/mmi-module-voice-touch-screen-info.h b/plugins/nodes/voice-touch/mmi-module-voice-touch-screen-info.h new file mode 100644 index 0000000..057c901 --- /dev/null +++ b/plugins/nodes/voice-touch/mmi-module-voice-touch-screen-info.h @@ -0,0 +1,73 @@ +/* + * Copyright (c) 2023 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 __MMI_MODULE_VOICE_TOUCH_SCREEN_INFO_H__ +#define __MMI_MODULE_VOICE_TOUCH_SCREEN_INFO_H__ + + +#include +#include +#include +#include + +#include "mmi-module-voice-touch-common.h" +#include "mmi-module-voice-touch-data-utility.h" + + +class ScreenElements { +public: + ScreenElements(); + ~ScreenElements(); + + void set_screen_size(int width, int height); + void set_grid_informations(const std::vector &grid_informations); + void set_screen_elements(const MmiDataHelper &screen_elements); + void set_grid_area(const area_coordinates_s &grid_area); + + void increase_grid_depth(); + void reset_grid_depth(); + bool is_maximum_grid_depth(); + + std::vector make_voice_touch_candidates(voice_touch_mode_e mode); + std::optional find_related_screen_element(const voice_touch_candidate_s &candidate); + +private: + screen_element_s make_screen_element(MmiDataHelper &data); + + void fill_grid_label_candidates(std::vector &candidates); + void fill_index_label_candidates(std::vector &candidates); + void fill_text_label_candidates(std::vector &candidates); + + std::optional find_exact_element_by_object_id(const std::string &object_id); + std::optional find_best_element_in_grid(const area_coordinates_s &grid_area); + +private: + // Screen information + int m_screen_width; + int m_screen_height; + + // Grid information + std::vector m_grid_informations; + area_coordinates_s m_parent_grid_area; + size_t m_grid_depth; + + // Current screen elements + std::vector m_screen_elements; +}; + +#endif /* __MMI_MODULE_VOICE_TOUCH_SCREEN_INFO_H__ */ diff --git a/src/mmi-client.h b/plugins/nodes/voice-touch/mmi-module-voice-touch-string-utility.cpp similarity index 51% rename from src/mmi-client.h rename to plugins/nodes/voice-touch/mmi-module-voice-touch-string-utility.cpp index 6f5cc81..7a1134c 100644 --- a/src/mmi-client.h +++ b/plugins/nodes/voice-touch/mmi-module-voice-touch-string-utility.cpp @@ -15,39 +15,28 @@ * */ -#ifndef __MMI_CLIENT_H__ -#define __MMI_CLIENT_H__ -#include -#include "mmi.h" +#include +#include -typedef void* mmi_rpc_h; +#include "mmi-module-voice-touch-string-utility.h" -typedef struct { - mmi_rpc_h rpc_h; - const char *stub_appid; - int state; - GList* result_cb_list; -} mmi_struct_s; -typedef mmi_struct_s* mmi_handle; - -typedef struct { - int input_event_type; - mmi_result_cb result_callback; -} mmi_result_cb_s; +std::string StringUtility::make_lower_case(std::string text) +{ + transform(text.begin(), text.end(), text.begin(), ::tolower); + return text; +} -#ifdef __cplusplus -extern "C" { -#endif +std::vector StringUtility::split_text(std::string text, char delimiter) +{ + std::vector result; + std::istringstream ss{text}; + std::string temp; -int mmi_client_create(void); -int mmi_client_destroy(void); -int mmi_client_set_result_cb(int input_event_type, mmi_result_cb callback, void* user_data); -mmi_handle mmi_client_get(void); + while (getline(ss, temp, delimiter)) { + result.push_back(temp); + } -#ifdef __cplusplus + return result; } -#endif - -#endif //__MMI_CLIENT_H__ \ No newline at end of file diff --git a/plugins/nodes/voice-touch/mmi-module-voice-touch-string-utility.h b/plugins/nodes/voice-touch/mmi-module-voice-touch-string-utility.h new file mode 100644 index 0000000..12c71b2 --- /dev/null +++ b/plugins/nodes/voice-touch/mmi-module-voice-touch-string-utility.h @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2023 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 __MMI_MODULE_VOICE_TOUCH_STRING_UTILITY_H__ +#define __MMI_MODULE_VOICE_TOUCH_STRING_UTILITY_H__ + + +#include +#include + +class StringUtility { +public: + static std::string make_lower_case(std::string text); + static std::vector split_text(std::string text, char delimeter); +}; + +#endif /* __MMI_MODULE_VOICE_TOUCH_STRING_UTILITY_H__ */ diff --git a/plugins/nodes/voice-touch/mmi-module-voice-touch.cpp b/plugins/nodes/voice-touch/mmi-module-voice-touch.cpp new file mode 100644 index 0000000..76d72fc --- /dev/null +++ b/plugins/nodes/voice-touch/mmi-module-voice-touch.cpp @@ -0,0 +1,617 @@ +#include +#include +#include +#include + +#include +#include + +#include "mmi-module-voice-touch-candidates.h" +#include "mmi-module-voice-touch-screen-info.h" +#include "mmi-module-voice-touch-data-utility.h" +#include "mmi-module-voice-touch-primitive-utility.h" + + +#ifdef LOG_TAG +#undef LOG_TAG +#endif +#define LOG_TAG "MMI-MODULE-VOICE-TOUCH" + + +struct voice_touch_helper_s +{ + VoiceTouchCandidates voice_touch_candidates; + ScreenElements screen_elements; + voice_touch_mode_e mode{VOICE_TOUCH_MODE_TEXT}; +}; + + +static const char *INPUT_PORT_NAME_SCREEN_INFO = "SCREEN_INFO"; +static const char *INPUT_PORT_NAME_MODE_COMMANDS = "MODE_COMMANDS"; +static const char *INPUT_PORT_NAME_UTTERANCE = "UTTERANCE"; + +static const char *OUTPUT_PORT_NAME_MATCHED_RESULT = "MATCHED_RESULT"; +static const char *OUTPUT_PORT_NAME_REJECTED = "REJECTED"; +static const char *OUTPUT_PORT_NAME_CANDIDATES = "CANDIDATES"; + +static const char *ATTRIBUTE_NAME_GRID_CONFIGURATION = "GRID_CONFIGURATION"; + +static const char *MODE_COMMAND_SHOW_INDEX = "숫자 보여줘"; +static const char *MODE_COMMAND_SHOW_TEXT = "텍스트 보여줘"; +static const char *MODE_COMMAND_SHOW_GRID = "그리드 보여줘"; + + +static std::map> g_node_to_helper_map; + + +static int node_initialized_cb(mmi_node_instance_h instance) +{ + _D("[VOICE TOUCH] Node initialize callback is called for %p", instance); + if (nullptr == instance) { + _E("[VOICE TOUCH] Instance is null. %p", instance); + return MMI_ERROR_INVALID_PARAMETER; + } + + try { + auto helper_instance = std::make_shared(); + g_node_to_helper_map[instance] = helper_instance; + } catch (std::bad_alloc &e) { + _E("[VOICE TOUCH] Fail to allocate memory for node instance. %p", instance); + return MMI_ERROR_OUT_OF_MEMORY; + } + + return MMI_ERROR_NONE; +} + +static bool is_node_instance_valid(mmi_node_instance_h instance) +{ + if (nullptr == instance) { + _E("[VOICE TOUCH] Node instance is null %p", instance); + return false; + } + + auto iterator = g_node_to_helper_map.find(instance); + if (iterator == g_node_to_helper_map.end()) { + _E("[VOICE TOUCH] No instance %p", instance); + return false; + } + + return true; +} + +static int node_deinitialized_cb(mmi_node_instance_h instance) +{ + _D("[VOICE TOUCH] Node deinitialize callback is called for %p", instance); + if (false == is_node_instance_valid(instance)) { + _E("[VOICE TOUCH] Instance is not valid. %p", instance); + return MMI_ERROR_INVALID_PARAMETER; + } + + g_node_to_helper_map.erase(instance); + return MMI_ERROR_NONE; +} + +static int node_attribute_set_cb(mmi_node_instance_h instance, mmi_attribute_h attribute) +{ + _D("[VOICE TOUCH] Node attribute set callback is called for %p", instance); + if (false == is_node_instance_valid(instance)) { + _E("[VOICE TOUCH] Instance is not valid. %p", instance); + return MMI_ERROR_INVALID_PARAMETER; + } + + char *name = nullptr; + mmi_attribute_get_name(attribute, &name); + if (nullptr == name) { + _E("[VOICE TOUCH] Fail to get name from attribute. %p", attribute); + return MMI_ERROR_INVALID_PARAMETER; + } + + std::string attribute_name(name); + free(name); + name = nullptr; + + if (0 != attribute_name.compare(ATTRIBUTE_NAME_GRID_CONFIGURATION)) { + _E("[VOICE TOUCH] Attribute name is not same as expected one. (%s)", attribute_name.c_str()); + return MMI_ERROR_INVALID_PARAMETER; + } + + mmi_primitive_value_h value = nullptr; + mmi_attribute_get_value(attribute, &value); + if (nullptr == value) { + _E("[VOICE TOUCH] Fail to get primitive value from attribute. %p", attribute); + return MMI_ERROR_INVALID_PARAMETER; + } + + MmiPrimitiveHelper attribute_value(value, true); + value = nullptr; + if (MMI_PRIMITIVE_VALUE_TYPE_ARRAY != attribute_value.get_type()) { + _E("[VOICE TOUCH] Fail to get primitive value from attribute. %p", attribute); + return MMI_ERROR_INVALID_PARAMETER; + } + + auto &screen_elements = g_node_to_helper_map[instance]->screen_elements; + + // TODO: Check the efficient way to set attribute for voice touch. + try { + auto screen_w = attribute_value.get_array_element(0).get_integer_value(); + auto screen_h = attribute_value.get_array_element(1).get_integer_value(); + + _D("[VOICE TOUCH] Screen size : %d, %d", screen_w, screen_h); + screen_elements.set_screen_size(screen_w, screen_h); + } catch (mmi_error_e &error) { + _E("[VOICE TOUCH] Fail to get screen size. %p", attribute); + return error; + } + + size_t count = attribute_value.get_array_count(); + std::vector grid_informations; + for (size_t i = 2; i < count; i += 2) { + try { + grid_information_s grid_information{ + .col_count = static_cast(attribute_value.get_array_element(i).get_integer_value()), + .row_count = static_cast(attribute_value.get_array_element(i + 1).get_integer_value()) + }; + + _D("[VOICE TOUCH] Grid information : col(%zu), row(%zu)", grid_information.col_count, grid_information.row_count); + grid_informations.push_back(grid_information); + } catch (mmi_error_e &error) { + _E("[VOICE TOUCH] Fail to get data. Please check error. index(%zu). error(%d)", i, error); + return error; + } + } + + screen_elements.set_grid_informations(grid_informations); + return MMI_ERROR_NONE; +} + +static int node_activated_cb(mmi_node_instance_h instance) +{ + _D("[VOICE TOUCH] Node activate callback is called for %p", instance); + if (false == is_node_instance_valid(instance)) { + _E("[VOICE TOUCH] Instance is not valid. %p", instance); + return MMI_ERROR_INVALID_PARAMETER; + } + + return MMI_ERROR_NONE; +} + +static int node_deactivated_cb(mmi_node_instance_h instance) +{ + _D("[VOICE TOUCH] Node deactivate callback is called for %p", instance); + if (false == is_node_instance_valid(instance)) { + _E("[VOICE TOUCH] Instance is not valid. %p", instance); + return MMI_ERROR_INVALID_PARAMETER; + } + + return MMI_ERROR_NONE; +} + +static int utterance_port_output_format_requested_cb(mmi_port_instance_h instance, const char *format) +{ + _D("[VOICE TOUCH] Port output format request callback is called for %p", instance); + if (false == is_node_instance_valid(instance)) { + _E("[VOICE TOUCH] Instance is not valid. %p", instance); + return MMI_ERROR_INVALID_PARAMETER; + } + + return MMI_ERROR_NONE; +} + +static void generate_output_for_rejected_port(mmi_node_instance_h instance, const char *text) +{ + mmi_port_instance_h rejected_port = nullptr; + mmi_node_instance_find_port(instance, MMI_PORT_TYPE_OUT, OUTPUT_PORT_NAME_REJECTED, &rejected_port); + if (nullptr == rejected_port) { + _E("[VOICE TOUCH] Fail to get output port. name(%s). node instance(%p)", OUTPUT_PORT_NAME_REJECTED, instance); + return; + } + + MmiDataHelper output_data(text); + if (nullptr == output_data.get_handle()) { + _E("[VOICE TOUCH] Fail to allocate memory for output data. name(%s). node instance(%p)", OUTPUT_PORT_NAME_MATCHED_RESULT, instance); + return; + } + + mmi_port_instance_generate_output(rejected_port, output_data.get_handle()); +} + +static void generate_output_for_matched_result_port(mmi_node_instance_h instance, const voice_touch_candidate_s &candidate, const screen_element_s &element) +{ + mmi_port_instance_h matched_result_port = nullptr; + mmi_node_instance_find_port(instance, MMI_PORT_TYPE_OUT, OUTPUT_PORT_NAME_MATCHED_RESULT, &matched_result_port); + if (nullptr == matched_result_port) { + _E("[VOICE TOUCH] Fail to get output port. name(%s). node instance(%p)", OUTPUT_PORT_NAME_MATCHED_RESULT, instance); + return; + } + + MmiDataHelper output_data(candidate, element); + if (nullptr == output_data.get_handle()) { + _E("[VOICE TOUCH] Fail to allocate memory for output data. name(%s). node instance(%p)", OUTPUT_PORT_NAME_MATCHED_RESULT, instance); + return; + } + + mmi_port_instance_generate_output(matched_result_port, output_data.get_handle()); +} + +static void generate_output_for_candidates_port(mmi_node_instance_h instance, const std::vector &candidates) +{ + mmi_port_instance_h candidates_port = nullptr; + mmi_node_instance_find_port(instance, MMI_PORT_TYPE_OUT, OUTPUT_PORT_NAME_CANDIDATES, &candidates_port); + if (nullptr == candidates_port) { + _E("[VOICE TOUCH] Fail to get output port. name(%s). node instance(%p)", OUTPUT_PORT_NAME_CANDIDATES, instance); + return; + } + + MmiDataHelper output_data(candidates); + if (nullptr == output_data.get_handle()) { + _E("[VOICE TOUCH] Fail to allocate memory for output data. name(%s). node instance(%p)", OUTPUT_PORT_NAME_CANDIDATES, instance); + return; + } + + mmi_port_instance_generate_output(candidates_port, output_data.get_handle()); +} + +static mmi_error_e handle_utterance_input_as_text(mmi_node_instance_h instance, const char *utterance) +{ + auto &voice_touch_candidates = g_node_to_helper_map[instance]->voice_touch_candidates; + auto &screen_elements = g_node_to_helper_map[instance]->screen_elements; + + auto matched_candidate = voice_touch_candidates.find_matched_candidate(utterance); + if (matched_candidate.has_value() == false) { + _I("[VOICE TOUCH] There is no proper candidate in the screen. utterance(%s)", utterance); + return MMI_ERROR_OPERATION_FAILED; + } + + auto matched_element = screen_elements.find_related_screen_element(*matched_candidate); + if (matched_element.has_value() == false) { + _I("[VOICE TOUCH] There is no proper screen element in the screen. utterance(%s)", utterance); + return MMI_ERROR_OPERATION_FAILED; + } + + _I("[VOICE TOUCH] Success to find the candidate. utterance(%s)", utterance); + generate_output_for_matched_result_port(instance, *matched_candidate, *matched_element); + + return MMI_ERROR_NONE; +} + +static mmi_error_e handle_utterance_input_as_index(mmi_node_instance_h instance, const char *utterance) +{ + auto &voice_touch_candidates = g_node_to_helper_map[instance]->voice_touch_candidates; + auto &screen_elements = g_node_to_helper_map[instance]->screen_elements; + + int index = -1; + try { + index = std::stoi(utterance); + } catch (std::exception &e) { + _I("[VOICE TOUCH] There is no number in utterance. utterance(%s). exception(%s)", utterance, e.what()); + return MMI_ERROR_OPERATION_FAILED; + } + + auto matched_candidate = voice_touch_candidates.find_matched_candidate(index); + if (matched_candidate.has_value() == false) { + _I("[VOICE TOUCH] There is no proper candidate in the screen. utterance(%s)", utterance); + return MMI_ERROR_OPERATION_FAILED; + } + + auto matched_element = screen_elements.find_related_screen_element(*matched_candidate); + if (matched_element.has_value() == false) { + _I("[VOICE TOUCH] There is no proper screen element in the screen. utterance(%s)", utterance); + return MMI_ERROR_OPERATION_FAILED; + } + + _I("[VOICE TOUCH] Success to find the candidate. utterance(%s)", utterance); + generate_output_for_matched_result_port(instance, *matched_candidate, *matched_element); + + return MMI_ERROR_NONE; +} + +static mmi_error_e handle_utterance_input_as_grid(mmi_node_instance_h instance, const char *utterance) +{ + auto &voice_touch_candidates = g_node_to_helper_map[instance]->voice_touch_candidates; + auto &screen_elements = g_node_to_helper_map[instance]->screen_elements; + + int index = -1; + try { + index = std::stoi(utterance); + } catch (std::exception &e) { + _I("[VOICE TOUCH] There is no number in utterance. utterance(%s). exception(%s)", utterance, e.what()); + return MMI_ERROR_OPERATION_FAILED; + } + + auto matched_candidate = voice_touch_candidates.find_matched_candidate(index); + if (matched_candidate.has_value() == false) { + _I("[VOICE TOUCH] There is no proper candidate in the screen. utterance(%s)", utterance); + return MMI_ERROR_OPERATION_FAILED; + } + + screen_elements.increase_grid_depth(); + if (screen_elements.is_maximum_grid_depth() == false) { + _I("[VOICE TOUCH] Make candidate for next grid depth."); + screen_elements.set_grid_area(matched_candidate->candidate_area); + + auto candidates = screen_elements.make_voice_touch_candidates(VOICE_TOUCH_MODE_GRID); + voice_touch_candidates.set_candidates(candidates); + generate_output_for_candidates_port(instance, candidates); + return MMI_ERROR_NONE; + } + + auto matched_element = screen_elements.find_related_screen_element(*matched_candidate); + if (matched_element.has_value() == false) { + _I("[VOICE TOUCH] There is no proper screen element in the screen. utterance(%s)", utterance); + _I("[VOICE TOUCH] Make output for center of grid"); + matched_element = screen_element_s{ + .element_area = matched_candidate->candidate_area, + .object_id = std::string(), + .label = matched_candidate->label, + .role = std::string() + }; + } else { + _I("[VOICE TOUCH] Success to find the candidate. utterance(%s)", utterance); + } + + screen_elements.reset_grid_depth(); + generate_output_for_matched_result_port(instance, *matched_candidate, *matched_element); + + return MMI_ERROR_NONE; +} + +static std::optional find_node_instance(mmi_port_instance_h port_instance, const char *port_name) +{ + if (nullptr == port_instance || nullptr == port_name) { + return std::nullopt; + } + + for (auto &element : g_node_to_helper_map) { + mmi_port_instance_h candidate_port = nullptr; + mmi_node_instance_find_port(element.first, MMI_PORT_TYPE_IN, port_name, &candidate_port); + + if (candidate_port == port_instance) { + return element.first; + } + } + + return std::nullopt; +} + +static int utterance_port_input_data_received_cb(mmi_port_instance_h instance, mmi_data_h data) +{ + _D("[VOICE TOUCH] Text input data callback is called: %p", instance); + auto node_instance = find_node_instance(instance, INPUT_PORT_NAME_UTTERANCE); + if (node_instance.has_value() == false) { + _E("[VOICE TOUCH] There is no proper node instance: %p", instance); + return MMI_ERROR_INVALID_PARAMETER; + } + + const char *utterance = nullptr; + mmi_data_get_text(data, &utterance); + if (nullptr == utterance) { + _E("[VOICE TOUCH] Fail to get text from the data: %p", data); + return MMI_ERROR_INVALID_PARAMETER; + } + + voice_touch_mode_e mode = g_node_to_helper_map[*node_instance]->mode; + mmi_error_e ret = MMI_ERROR_NONE; + switch (mode) { + case VOICE_TOUCH_MODE_TEXT: + ret = handle_utterance_input_as_text(*node_instance, utterance); + break; + + case VOICE_TOUCH_MODE_INDEX: + ret = handle_utterance_input_as_index(*node_instance, utterance); + break; + + case VOICE_TOUCH_MODE_GRID: + ret = handle_utterance_input_as_grid(*node_instance, utterance); + break; + } + + if (MMI_ERROR_NONE != ret) { + _I("[VOICE TOUCH] Generate reject output. utterance(%s)", utterance); + generate_output_for_rejected_port(*node_instance, utterance); + } + + return MMI_ERROR_NONE; +} + +static int mode_commands_port_output_format_requested_cb(mmi_port_instance_h instance, const char *format) +{ + _D("[VOICE TOUCH] Port output format request callback is called for %p", instance); + + return MMI_ERROR_NONE; +} + +static int mode_commands_port_input_data_received_cb(mmi_port_instance_h instance, mmi_data_h data) +{ + _D("[VOICE TOUCH] Text input data callback is called: %p", instance); + auto node_instance = find_node_instance(instance, INPUT_PORT_NAME_MODE_COMMANDS); + if (node_instance.has_value() == false) { + _E("[VOICE TOUCH] There is no proper node instance: %p", instance); + return MMI_ERROR_INVALID_PARAMETER; + } + + const char *text = nullptr; + mmi_data_get_text(data, &text); + if (nullptr == text) { + _E("[VOICE TOUCH] Fail to get text from the data: %p", data); + return MMI_ERROR_INVALID_PARAMETER; + } + + std::string mode_str = text; + text = nullptr; + + voice_touch_mode_e mode = VOICE_TOUCH_MODE_TEXT; + if (mode_str.compare(MODE_COMMAND_SHOW_TEXT) == 0) { + mode = VOICE_TOUCH_MODE_TEXT; + } else if (mode_str.compare(MODE_COMMAND_SHOW_INDEX) == 0) { + mode = VOICE_TOUCH_MODE_INDEX; + } else if (mode_str.compare(MODE_COMMAND_SHOW_GRID) == 0) { + mode = VOICE_TOUCH_MODE_GRID; + } else { + _E("[VOICE TOUCH] Invalid mode command: %s", mode_str.c_str()); + return MMI_ERROR_INVALID_PARAMETER; + } + + auto &node_helper = g_node_to_helper_map[*node_instance]; + auto &voice_touch_candidates = node_helper->voice_touch_candidates; + auto &screen_elements = node_helper->screen_elements; + + node_helper->mode = mode; + auto candidates = screen_elements.make_voice_touch_candidates(node_helper->mode); + voice_touch_candidates.set_candidates(candidates); + + generate_output_for_candidates_port(*node_instance, candidates); + + return MMI_ERROR_NONE; +} + +static int screen_info_port_output_format_requested_cb(mmi_port_instance_h instance, const char *format) +{ + _D("[VOICE TOUCH] Port output format request callback is called for %p", instance); + if (false == is_node_instance_valid(instance)) { + _E("[VOICE TOUCH] Instance is not valid. %p", instance); + return MMI_ERROR_INVALID_PARAMETER; + } + + return MMI_ERROR_NONE; +} + +static int screen_info_port_input_data_received_cb(mmi_port_instance_h instance, mmi_data_h data) +{ + _D("[VOICE TOUCH] Text input data callback is called: %p", instance); + auto node_instance = find_node_instance(instance, INPUT_PORT_NAME_SCREEN_INFO); + if (node_instance.has_value() == false) { + _E("[VOICE TOUCH] There is no proper node instance: %p", instance); + return MMI_ERROR_INVALID_PARAMETER; + } + + const MmiDataHelper data_helper(data); + try { + mmi_data_type_e type = data_helper.get_type(); + if (MMI_DATA_TYPE_ARRAY != type) { + _E("[VOICE TOUCH] Type is not valid. data(%p). type(%d)", data, type); + return MMI_ERROR_INVALID_PARAMETER; + } + } catch (mmi_error_e &error) { + return error; + } + + auto &node_helper = g_node_to_helper_map[*node_instance]; + auto &voice_touch_candidates = node_helper->voice_touch_candidates; + auto &screen_elements = node_helper->screen_elements; + + screen_elements.set_screen_elements(data_helper); + auto candidates = screen_elements.make_voice_touch_candidates(node_helper->mode); + voice_touch_candidates.set_candidates(candidates); + generate_output_for_candidates_port(*node_instance, candidates); + + return MMI_ERROR_NONE; +} + +static mmi_port_h create_input_port(const char *name, mmi_data_type_e type, mmi_port_callbacks callbacks) +{ + mmi_port_h input_port = nullptr; + mmi_port_create(&input_port); + if (nullptr == input_port) { + return nullptr; + } + + mmi_port_set_name(input_port, name); + mmi_port_set_type(input_port, MMI_PORT_TYPE_IN); + mmi_port_set_data_type(input_port, type); + mmi_port_set_callbacks(input_port, callbacks); + + return input_port; +} + +static mmi_port_h create_output_port(const char *name, mmi_data_type_e type) +{ + mmi_port_h output_port = nullptr; + mmi_port_create(&output_port); + if (nullptr == output_port) { + return nullptr; + } + + mmi_port_set_name(output_port, name); + mmi_port_set_type(output_port, MMI_PORT_TYPE_OUT); + mmi_port_set_data_type(output_port, type); + + return output_port; +} + + +extern "C" { + + EXPORT_API void mmi_plugin_module_get_node_list() + { + mmi_node_callbacks node_callbacks { + node_initialized_cb, + node_deinitialized_cb, + node_attribute_set_cb, + node_activated_cb, + node_deactivated_cb, + nullptr + }; + + mmi_port_callbacks utterance_port_callbacks { + utterance_port_output_format_requested_cb, + utterance_port_input_data_received_cb + }; + + mmi_port_callbacks mode_commands_port_callbacks { + mode_commands_port_output_format_requested_cb, + mode_commands_port_input_data_received_cb + }; + + mmi_port_callbacks screen_info_port_callbacks { + screen_info_port_output_format_requested_cb, + screen_info_port_input_data_received_cb + }; + + mmi_port_h utterance_input_port = create_input_port( + INPUT_PORT_NAME_UTTERANCE, + MMI_DATA_TYPE_TEXT, + utterance_port_callbacks); + + mmi_port_h mode_commands_input_port = create_input_port( + INPUT_PORT_NAME_MODE_COMMANDS, + MMI_DATA_TYPE_TEXT, + mode_commands_port_callbacks); + + // TODO: check the type of input + mmi_port_h screen_info_input_port = create_input_port( + INPUT_PORT_NAME_SCREEN_INFO, + MMI_DATA_TYPE_ARRAY, + screen_info_port_callbacks); + + mmi_port_h candidates_output_port = create_output_port( + OUTPUT_PORT_NAME_CANDIDATES, + MMI_DATA_TYPE_ARRAY); + + mmi_port_h matched_result_output_port = create_output_port( + OUTPUT_PORT_NAME_MATCHED_RESULT, + MMI_DATA_TYPE_STRUCT); + + mmi_port_h rejected_output_port = create_output_port( + OUTPUT_PORT_NAME_REJECTED, + MMI_DATA_TYPE_TEXT); + + mmi_node_h voice_touch_node = nullptr; + mmi_standard_node_create_processor(MMI_STANDARD_NODE_PROCESSOR_TYPE_VOICE_TOUCH, &voice_touch_node); + + // Add input ports + mmi_node_add_port(voice_touch_node, utterance_input_port); + mmi_node_add_port(voice_touch_node, mode_commands_input_port); + mmi_node_add_port(voice_touch_node, screen_info_input_port); + + // Add output ports + mmi_node_add_port(voice_touch_node, candidates_output_port); + mmi_node_add_port(voice_touch_node, matched_result_output_port); + mmi_node_add_port(voice_touch_node, rejected_output_port); + + mmi_node_set_callbacks(voice_touch_node, node_callbacks); + mmi_node_register(voice_touch_node); + + mmi_node_destroy(voice_touch_node); + } + +} // extern "C" diff --git a/plugins/workflows/meson.build b/plugins/workflows/meson.build new file mode 100644 index 0000000..952a1b0 --- /dev/null +++ b/plugins/workflows/meson.build @@ -0,0 +1,4 @@ +subdir('script-parser') +subdir('wakeupless-command') +subdir('voice-touch') +subdir('user-recognition') diff --git a/plugins/workflows/script-parser/meson.build b/plugins/workflows/script-parser/meson.build new file mode 100644 index 0000000..dd3564c --- /dev/null +++ b/plugins/workflows/script-parser/meson.build @@ -0,0 +1,25 @@ +mmi_module_script_parser_library_srcs = [ + 'mmi-module-script-parser.cpp', + ] + +dlog_dep = dependency('dlog', method : 'pkg-config') +ecore_dep = dependency('ecore', method : 'pkg-config') + +mmi_module_script_parser_deps = [ + mmi_declared_dep, + dlog_dep, + ecore_dep, + ] + +mmi_module_script_parser_include_dirs = include_directories( + '.', + '../../../capi/', + ) + +mmi_module_script_parser_library = library('mmi_module_script_parser', + mmi_module_script_parser_library_srcs, + include_directories : [ mmi_module_script_parser_include_dirs ], + dependencies : [mmi_module_script_parser_deps], + install_dir : mmi_prefix_plugindir, + install : true + ) diff --git a/plugins/workflows/script-parser/mmi-module-script-parser.cpp b/plugins/workflows/script-parser/mmi-module-script-parser.cpp new file mode 100644 index 0000000..b708256 --- /dev/null +++ b/plugins/workflows/script-parser/mmi-module-script-parser.cpp @@ -0,0 +1,33 @@ +#include +#include +#include + +#include "mmi-log.h" + +#include +#include +#include + +#include +#include + +extern "C" { + + EXPORT_API void mmi_plugin_module_get_workflow_list() + { + const std::string plugin_module_path{"/usr/share/mmi/scripts"}; + for (const auto & entry : std::filesystem::directory_iterator(plugin_module_path)) { + auto const pos = entry.path().string().find_last_of("."); + if (pos != std::string::npos) { + auto const ext = entry.path().string().substr(pos + 1); + if (ext == "mws") { + mmi_workflow_h workflow = nullptr; + mmi_workflow_create_from_script(entry.path().string().c_str(), &workflow); + mmi_standard_workflow_register(workflow); + mmi_workflow_destroy(workflow); + } + } + } + } + +} // extern "C" diff --git a/plugins/workflows/user-recognition/meson.build b/plugins/workflows/user-recognition/meson.build new file mode 100644 index 0000000..85328db --- /dev/null +++ b/plugins/workflows/user-recognition/meson.build @@ -0,0 +1 @@ +install_data('user-recognition.mws', install_dir : mmi_prefix_scriptdir) diff --git a/plugins/workflows/user-recognition/user-recognition.mws b/plugins/workflows/user-recognition/user-recognition.mws new file mode 100644 index 0000000..49148b8 --- /dev/null +++ b/plugins/workflows/user-recognition/user-recognition.mws @@ -0,0 +1,21 @@ +@workflow +name : USER_RECOGNITION + +@node-list +[Source] MIC_AMBIENT as MIC +[Source] CAMERA as CAMERA +[Processor] FACE_RECOGNITION as FACE_RECOGNITION +[Processor] SPEAKER_RECOGNITION as SPEAKER_RECOGNITION +[Logic] USER_COMPARISON as USER_COMPARISON + +@link-list +CAMERA.VIDEO -> FACE_RECOGNITION.VIDEO +FACE_RECOGNITION.MATCHED_FACES -> USER_COMPARISON.PORT1 +MIC.AUDIO -> SPEAKER_RECOGNITION.AUDIO +SPEAKER_RECOGNITION.MATCHED_SPEAKERS -> USER_COMPARISON.PORT2 + +@attribute-list + +@output-list +USER_COMPARISON.BYPASS_RESULT as SINGLE_MODAL_USER_ID +USER_COMPARISON.COMPARISON_RESULT as MULTI_MODAL_USER_ID diff --git a/plugins/workflows/voice-touch/meson.build b/plugins/workflows/voice-touch/meson.build new file mode 100644 index 0000000..628d533 --- /dev/null +++ b/plugins/workflows/voice-touch/meson.build @@ -0,0 +1,25 @@ +mmi_module_voice_touch_library_srcs = [ + 'mmi-module-voice-touch.cpp', + ] + +dlog_dep = dependency('dlog', method : 'pkg-config') +ecore_dep = dependency('ecore', method : 'pkg-config') + +mmi_module_voice_touch_deps = [ + mmi_declared_dep, + dlog_dep, + ecore_dep, + ] + +mmi_module_voice_touch_include_dirs = include_directories( + '.', + '../../../capi/', + ) + +mmi_module_voice_touch_library = library('mmi_module_voice_touch', + mmi_module_voice_touch_library_srcs, + include_directories : [ mmi_module_voice_touch_include_dirs ], + dependencies : [mmi_module_voice_touch_deps], + install_dir : mmi_prefix_plugindir, + install : true + ) diff --git a/plugins/workflows/voice-touch/mmi-module-voice-touch.cpp b/plugins/workflows/voice-touch/mmi-module-voice-touch.cpp new file mode 100644 index 0000000..f8baebd --- /dev/null +++ b/plugins/workflows/voice-touch/mmi-module-voice-touch.cpp @@ -0,0 +1,150 @@ +#include +#include +#include + +#include "mmi-log.h" + +#include +#include + +#include +#include + +#ifdef LOG_TAG +#undef LOG_TAG +#endif +#define LOG_TAG "MMI-MODULE-VOICE-TOUCH" + +#define ENABLE_TEMP_RESCAN + +extern "C" { + + EXPORT_API void mmi_plugin_module_get_workflow_list() + { + const char *NODE_NAME_MIC = "MIC"; + const char *NODE_NAME_ASR = "ASR"; + const char *NODE_NAME_MATCH_MODE_COMMANDS = "MATCH_MODE_COMMANDS"; + const char *NODE_NAME_MATCH_PREDEFINED_COMMANDS = "MATCH_PREDEFINED_COMMANDS"; + const char *NODE_NAME_VOICE_TOUCH_PROCESSOR = "VOICE_TOUCH_PROCESSOR"; + const char *NODE_NAME_SCREEN_ANALYZER = "SCREEN_ANALYZER"; + + mmi_workflow_h workflow = nullptr; + mmi_workflow_create(&workflow); + + mmi_workflow_set_type(workflow, MMI_STANDARD_WORKFLOW_VOICE_TOUCH); + + mmi_node_h node_mic = nullptr; + mmi_standard_node_create_source(MMI_STANDARD_NODE_SOURCE_TYPE_MIC_AMBIENT, &node_mic); + mmi_workflow_node_add(workflow, NODE_NAME_MIC, node_mic); + + mmi_node_h node_asr = nullptr; + mmi_standard_node_create_processor(MMI_STANDARD_NODE_PROCESSOR_TYPE_ASR, &node_asr); + mmi_workflow_node_add(workflow, NODE_NAME_ASR, node_asr); + + mmi_node_h node_match_mode_commands = nullptr; + mmi_standard_node_create_logic(MMI_STANDARD_NODE_LOGIC_TYPE_FIXED_STRING_MATCH, &node_match_mode_commands); + mmi_workflow_node_add(workflow, NODE_NAME_MATCH_MODE_COMMANDS, node_match_mode_commands); + + mmi_node_h node_match_predefined_commands = nullptr; + mmi_standard_node_create_logic(MMI_STANDARD_NODE_LOGIC_TYPE_FIXED_STRING_MATCH, &node_match_predefined_commands); + mmi_workflow_node_add(workflow, NODE_NAME_MATCH_PREDEFINED_COMMANDS, node_match_predefined_commands); + + mmi_node_h node_voice_touch = nullptr; + mmi_standard_node_create_processor(MMI_STANDARD_NODE_PROCESSOR_TYPE_VOICE_TOUCH, &node_voice_touch); + mmi_workflow_node_add(workflow, NODE_NAME_VOICE_TOUCH_PROCESSOR, node_voice_touch); + + mmi_node_h node_screen_analyzer = nullptr; + mmi_standard_node_create_source(MMI_STANDARD_NODE_SOURCE_TYPE_SCREEN_ANALYZER, &node_screen_analyzer); + mmi_workflow_node_add(workflow, NODE_NAME_SCREEN_ANALYZER, node_screen_analyzer); + +#ifdef ENABLE_TEMP_RESCAN + /* This is a temporary node for rescanning the screen, it will be removed when the signal mechanism is ready */ + mmi_node_h temp_node_match_rescan_command = nullptr; + mmi_standard_node_create_logic(MMI_STANDARD_NODE_LOGIC_TYPE_FIXED_STRING_MATCH, &temp_node_match_rescan_command); + mmi_workflow_node_add(workflow, "TEMP_RESCAN", temp_node_match_rescan_command); +#endif + + mmi_workflow_link_nodes_by_names(workflow, NODE_NAME_MIC, "AUDIO", NODE_NAME_ASR, "AUDIO"); + + mmi_workflow_link_nodes_by_names(workflow, NODE_NAME_ASR, "FINAL_ASR", NODE_NAME_MATCH_MODE_COMMANDS, "TEXT"); + + mmi_workflow_link_nodes_by_names(workflow, NODE_NAME_MATCH_MODE_COMMANDS, "MATCHED_CANDIDATE", NODE_NAME_VOICE_TOUCH_PROCESSOR, "MODE_COMMANDS"); + mmi_workflow_link_nodes_by_names(workflow, NODE_NAME_MATCH_MODE_COMMANDS, "REJECTED", NODE_NAME_MATCH_PREDEFINED_COMMANDS, "TEXT"); + + mmi_workflow_link_nodes_by_names(workflow, NODE_NAME_MATCH_PREDEFINED_COMMANDS, "REJECTED", NODE_NAME_VOICE_TOUCH_PROCESSOR, "UTTERANCE"); + + mmi_workflow_link_nodes_by_names(workflow, NODE_NAME_SCREEN_ANALYZER, "SCREEN_INFO", NODE_NAME_VOICE_TOUCH_PROCESSOR, "SCREEN_INFO"); + +#ifdef ENABLE_TEMP_RESCAN + /* These are temporary links for rescanning the screen, it will be removed when the signal mechanism is ready */ + mmi_workflow_link_nodes_by_names(workflow, NODE_NAME_ASR, "FINAL_ASR", "TEMP_RESCAN", "TEXT"); + mmi_workflow_link_nodes_by_names(workflow, "TEMP_RESCAN", "MATCHED_CANDIDATE", NODE_NAME_SCREEN_ANALYZER, "RESCAN"); +#endif + + mmi_workflow_attribute_assign(workflow, "PREDEFINED_COMMANDS", NODE_NAME_MATCH_PREDEFINED_COMMANDS, "CANDIDATES"); + + mmi_workflow_attribute_assign(workflow, "GRID_CONFIGURATION", NODE_NAME_VOICE_TOUCH_PROCESSOR, "GRID_CONFIGURATION"); + +#ifdef ENABLE_TEMP_RESCAN + /* These are temporary attributes for rescanning the screen, it will be removed when the signal mechanism is ready */ + mmi_workflow_attribute_assign(workflow, "REFRESH", NODE_NAME_SCREEN_ANALYZER, "RESCANNING"); + mmi_workflow_attribute_assign(workflow, "TEMP_RESCAN_COMMAND", "TEMP_RESCAN", "CANDIDATES"); +#endif + + mmi_workflow_output_assign(workflow, "PARTIAL_RESULT", NODE_NAME_ASR, "PARTIAL_ASR"); + mmi_workflow_output_assign(workflow, "FINAL_RESULT", NODE_NAME_ASR, "FINAL_ASR"); + + mmi_workflow_output_assign(workflow, "MATCHED_CANDIDATE", NODE_NAME_MATCH_PREDEFINED_COMMANDS, "MATCHED_COMMAND"); + + mmi_workflow_output_assign(workflow, "REJECTED", NODE_NAME_VOICE_TOUCH_PROCESSOR, "REJECTED"); + mmi_workflow_output_assign(workflow, "CANDIDATES", NODE_NAME_VOICE_TOUCH_PROCESSOR, "CANDIDATES"); + mmi_workflow_output_assign(workflow, "MATCHED_RESULT", NODE_NAME_VOICE_TOUCH_PROCESSOR, "MATCHED_RESULT"); + + mmi_primitive_value_h primitive_array = nullptr; + mmi_primitive_value_create_array(&primitive_array); + + mmi_primitive_value_h predefined_command_grid = nullptr; + mmi_primitive_value_create_string("Grid", &predefined_command_grid); + mmi_primitive_value_add_array_element(primitive_array, predefined_command_grid); + + mmi_primitive_value_h predefined_command_number = nullptr; + mmi_primitive_value_create_string("Number", &predefined_command_number); + mmi_primitive_value_add_array_element(primitive_array, predefined_command_number); + + mmi_attribute_h attribute = nullptr; + mmi_attribute_create(primitive_array, "PREDEFINED_COMMANDS", &attribute); + + mmi_workflow_attribute_set_default_value(workflow, attribute); + + mmi_attribute_destroy(attribute); + mmi_primitive_value_destroy(primitive_array); + +#ifdef ENABLE_TEMP_RESCAN + /* This is a temporary attribute for rescanning the screen, it will be removed when the signal mechanism is ready */ + mmi_primitive_value_h temp_rescan_command = nullptr; + mmi_primitive_value_create_string("Rescan", &temp_rescan_command); + mmi_attribute_h temp_attribute = nullptr; + mmi_attribute_create(temp_rescan_command, "TEMP_RESCAN_COMMAND", &temp_attribute); + mmi_workflow_attribute_set_default_value(workflow, temp_attribute); + mmi_attribute_destroy(temp_attribute); + mmi_primitive_value_destroy(temp_rescan_command); +#endif + + mmi_standard_workflow_register(workflow); + +#ifdef ENABLE_TEMP_RESCAN + /* This is a temporary node for rescanning the screen, it will be removed when the signal mechanism is ready */ + mmi_node_destroy(temp_node_match_rescan_command); +#endif + + mmi_node_destroy(node_mic); + mmi_node_destroy(node_asr); + mmi_node_destroy(node_match_mode_commands); + mmi_node_destroy(node_match_predefined_commands); + mmi_node_destroy(node_voice_touch); + mmi_node_destroy(node_screen_analyzer); + + mmi_workflow_destroy(workflow); + } + +} // extern "C" diff --git a/plugins/workflows/voice-touch/voice-touch.mws b/plugins/workflows/voice-touch/voice-touch.mws new file mode 100644 index 0000000..c4daa81 --- /dev/null +++ b/plugins/workflows/voice-touch/voice-touch.mws @@ -0,0 +1,19 @@ +@workflow +name : VoiceTouch + +@node-list +[Source] MIC_AMBIENT as MIC +[Processor] ASR as ASR +[Logic] FIXED_STRING_MATCH as MATCH +[Logic] FIXED_STRING_MATCH as DUMMY + +@link-list +MIC.AUDIO -> ASR.AUDIO +ASR.FINAL_RESULT -> MATCH.TEXT +ASR.FINAL_RESULT -> DUMMY.TEXT + +@attribute-list +MATCH.CANDIDATES as COMMANDS + +@output-list +MATCH.MATCHED_CANDIDATE as COMMAND diff --git a/plugins/workflows/wakeupless-command/meson.build b/plugins/workflows/wakeupless-command/meson.build new file mode 100644 index 0000000..55c517b --- /dev/null +++ b/plugins/workflows/wakeupless-command/meson.build @@ -0,0 +1,25 @@ +mmi_module_wakeupless_command_library_srcs = [ + 'mmi-module-wakeupless-command.cpp', + ] + +dlog_dep = dependency('dlog', method : 'pkg-config') +ecore_dep = dependency('ecore', method : 'pkg-config') + +mmi_module_wakeupless_command_deps = [ + mmi_declared_dep, + dlog_dep, + ecore_dep, + ] + +mmi_module_wakeupless_command_include_dirs = include_directories( + '.', + '../../../capi/', + ) + +mmi_module_wakeupless_command_library = library('mmi_module_wakeupless_command', + mmi_module_wakeupless_command_library_srcs, + include_directories : [ mmi_module_wakeupless_command_include_dirs ], + dependencies : [mmi_module_wakeupless_command_deps], + install_dir : mmi_prefix_plugindir, + install : true + ) diff --git a/plugins/workflows/wakeupless-command/mmi-module-wakeupless-command.cpp b/plugins/workflows/wakeupless-command/mmi-module-wakeupless-command.cpp new file mode 100644 index 0000000..62d0070 --- /dev/null +++ b/plugins/workflows/wakeupless-command/mmi-module-wakeupless-command.cpp @@ -0,0 +1,69 @@ +#include +#include +#include + +#include +#include + +#include +#include + +#ifdef LOG_TAG +#undef LOG_TAG +#endif +#define LOG_TAG "MMI-MODULE-WAKEUPLESS-COMMAND" + +#define MMI_MODULE_LOG_(prio, tag, fmt, arg...) \ + ({ do { \ + dlog_print(prio, tag, "%s: %s(%d) > " fmt, __MODULE__, __func__, __LINE__, ##arg); \ + } while (0); }) + +#define _D(fmt, args...) MMI_MODULE_LOG_(DLOG_DEBUG, LOG_TAG, fmt, ##args) +#define _I(fmt, args...) MMI_MODULE_LOG_(DLOG_INFO, LOG_TAG, fmt, ##args) +#define _W(fmt, args...) MMI_MODULE_LOG_(DLOG_WARN, LOG_TAG, fmt, ##args) +#define _E(fmt, args...) MMI_MODULE_LOG_(DLOG_ERROR, LOG_TAG, fmt, ##args) + +extern "C" { + + EXPORT_API void mmi_plugin_module_get_workflow_list() + { + mmi_workflow_h workflow = nullptr; + mmi_workflow_create(&workflow); + + mmi_workflow_set_type(workflow, MMI_STANDARD_WORKFLOW_WAKEUPLESS_COMMAND); + + mmi_node_h node_mic = nullptr; + /* FIXME: node should be 'found' instead of 'created' */ + mmi_standard_node_create_source(MMI_STANDARD_NODE_SOURCE_TYPE_MIC_AMBIENT, &node_mic); + mmi_workflow_node_add(workflow, "MIC", node_mic); + + mmi_node_h node_asr = nullptr; + mmi_standard_node_create_processor(MMI_STANDARD_NODE_PROCESSOR_TYPE_ASR, &node_asr); + mmi_workflow_node_add(workflow, "ASR", node_asr); + + mmi_node_h node_match = nullptr; + mmi_standard_node_create_logic(MMI_STANDARD_NODE_LOGIC_TYPE_FIXED_STRING_MATCH, &node_match); + mmi_workflow_node_add(workflow, "MATCH", node_match); + + mmi_workflow_link_nodes_by_names(workflow, "MIC", "AUDIO", "ASR", "AUDIO"); + mmi_workflow_link_nodes_by_names(workflow, "ASR", "FINAL_ASR", "MATCH", "TEXT"); + + mmi_workflow_attribute_assign(workflow, "COMMANDS", "MATCH", "CANDIDATES"); + mmi_workflow_attribute_assign(workflow, "LANGUAGE", "ASR", "LANGUAGE"); + mmi_workflow_attribute_assign(workflow, "STOP_METHOD", "ASR", "STOP_METHOD"); + mmi_workflow_attribute_assign(workflow, "ASR_START", "ASR", "ASR_START"); + + mmi_workflow_output_assign(workflow, "COMMAND", "MATCH", "MATCHED_CANDIDATE"); + mmi_workflow_output_assign(workflow, "UTTERANCE", "ASR", "PARTIAL_ASR"); + mmi_workflow_output_assign(workflow, "UTTERANCE", "ASR", "FINAL_ASR"); + + mmi_standard_workflow_register(workflow); + + mmi_node_destroy(node_mic); + mmi_node_destroy(node_asr); + mmi_node_destroy(node_match); + + mmi_workflow_destroy(workflow); + } + +} // extern "C" diff --git a/src/common/mmi-communication-channel.h b/src/common/mmi-communication-channel.h new file mode 100644 index 0000000..c09948d --- /dev/null +++ b/src/common/mmi-communication-channel.h @@ -0,0 +1,75 @@ +/* + * Copyright (c) 2023 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#pragma once + +#include "mmi-event-observer.h" +#include "mmi-communication-message.h" +#include "mmi-workflow-output-event.h" + +#include + +namespace mmi { + +namespace communication { + +enum class COMMUNICATION_CHANNEL_EVENT_TYPE { + CONNECTED, + DISCONNECTED, + MESSAGE_RECEIVED, +}; + +class CommunicationChannel : public SimpleEventObservable { +public: + CommunicationChannel() = default; + virtual ~CommunicationChannel() = default; + + virtual int connect() = 0; + virtual int disconnect() = 0; + virtual int send(Message *message) = 0; +}; + +class CommunicationChannelManager : public CommunicationChannel, + public SimpleEventObserver { +public: + CommunicationChannelManager() = default; + virtual ~CommunicationChannelManager() = default; +}; + +class CommunicationChannelClient : public CommunicationChannel { +public: + CommunicationChannelClient() = default; + virtual ~CommunicationChannelClient() = default; +}; + +struct CommunicationChannelManagerFactory { + CommunicationChannelManagerFactory() = default; + virtual ~CommunicationChannelManagerFactory() = default; + + virtual std::shared_ptr create_channel() = 0; +}; + +struct CommunicationChannelClientFactory { + CommunicationChannelClientFactory() = default; + virtual ~CommunicationChannelClientFactory() = default; + + virtual std::shared_ptr create_channel() = 0; +}; + +}; // namespace communication + +}; // namespace mmi diff --git a/src/common/mmi-communication-message.h b/src/common/mmi-communication-message.h new file mode 100644 index 0000000..4fc7ca0 --- /dev/null +++ b/src/common/mmi-communication-message.h @@ -0,0 +1,93 @@ +/* + * Copyright (c) 2023 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#pragma once + +#include +#include +#include +#include + +#include + +#include "mmi.h" + +namespace mmi { + +namespace communication { + +enum class MESSAGE_GROUP { + CLIENT, + WORKFLOW, + NODE, +}; + +/* Client Messages */ +enum class CLIENT_MESSAGE_TYPE { + /* Client to Manager */ + WORKFLOW_INSTANCE_CREATE, + WORKFLOW_INSTANCE_DESTROY, + WORKFLOW_INSTANCE_SET_ATTRIBUTE, + WORKFLOW_INSTANCE_ACTIVATE, + WORKFLOW_INSTANCE_DEACTIVATE, + /* Manager to Client */ +}; + +struct Message { + Message(MESSAGE_GROUP group) : message_group{group} {} + virtual ~Message() {} + MESSAGE_GROUP message_group; + std::string sender; +}; + +struct ClientMessage : public Message { + ClientMessage(CLIENT_MESSAGE_TYPE type) : Message(MESSAGE_GROUP::CLIENT), message_type{type} {} + virtual ~ClientMessage() {} + CLIENT_MESSAGE_TYPE message_type; +}; + +struct ClientMessageWorkflowInstanceCreate : public ClientMessage { + ClientMessageWorkflowInstanceCreate() : ClientMessage{CLIENT_MESSAGE_TYPE::WORKFLOW_INSTANCE_CREATE} {} + mmi_standard_workflow_type_e workflow_type{MMI_STANDARD_WORKFLOW_NONE}; + int local_workflow_instance_id{0}; + void *user_data{nullptr}; +}; + +struct ClientMessageWorkflowInstanceDestroy : public ClientMessage { + ClientMessageWorkflowInstanceDestroy() : ClientMessage{CLIENT_MESSAGE_TYPE::WORKFLOW_INSTANCE_DESTROY} {} + int local_workflow_instance_id{0}; +}; + +struct ClientMessageWorkflowInstanceSetAttribute : public ClientMessage { + ClientMessageWorkflowInstanceSetAttribute() : ClientMessage{CLIENT_MESSAGE_TYPE::WORKFLOW_INSTANCE_SET_ATTRIBUTE} {} + int local_workflow_instance_id{0}; + mmi_attribute_h attribute{nullptr}; +}; + +struct ClientMessageWorkflowInstanceActivate : public ClientMessage { + ClientMessageWorkflowInstanceActivate() : ClientMessage{CLIENT_MESSAGE_TYPE::WORKFLOW_INSTANCE_ACTIVATE} {} + int local_workflow_instance_id{0}; +}; + +struct ClientMessageWorkflowInstanceDeactivate : public ClientMessage { + ClientMessageWorkflowInstanceDeactivate() : ClientMessage{CLIENT_MESSAGE_TYPE::WORKFLOW_INSTANCE_DEACTIVATE} {} + int local_workflow_instance_id{0}; +}; + +}; // namespace communication + +}; // namespace mmi diff --git a/src/common/mmi-event-observer.h b/src/common/mmi-event-observer.h new file mode 100644 index 0000000..84e5058 --- /dev/null +++ b/src/common/mmi-event-observer.h @@ -0,0 +1,106 @@ +/* + * Copyright (c) 2023 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#pragma once + +#include +#include +#include +#include + +namespace mmi { + +/* SimpleEventObserver / SimpleEventObservable do not require EventSource to be specified. */ + +template +class SimpleEventObserver { +public: + SimpleEventObserver() = default; + virtual ~SimpleEventObserver() = default; + + virtual void on_observer_event(const EventType &event, std::any data = std::any(nullptr)) = 0; +}; + +template +class SimpleEventObservable { +public: + SimpleEventObservable() = default; + virtual ~SimpleEventObservable() = default; + + void notify_observers(const EventType &event, std::any data = std::any(nullptr)) { + for (auto observer : m_observers) { + if (observer) { + observer->on_observer_event(event, data); + } + } + } + void add_observer(SimpleEventObserver *observer) { + if (observer) { + m_observers.push_back(observer); + } + } + void remove_observer(SimpleEventObserver *observer) { + m_observers.erase( + std::remove(m_observers.begin(), m_observers.end(), observer), + m_observers.end()); + }; + +private: + std::vector*> m_observers; +}; + +/* EventObserver / EventObservable require EventSource to be specified. + * This is useful when you want to know which object triggered the event. */ + +template +class EventObserver { +public: + EventObserver() = default; + virtual ~EventObserver() = default; + + virtual void on_observer_event(const ObservableType &source, const EventType &event, std::any data = std::any(nullptr)) = 0; +}; + +template +class EventObservable { +public: + EventObservable() = default; + virtual ~EventObservable() = default; + + void notify_observers(const ObservableType &source, const EventType &event, std::any data = std::any(nullptr)) { + for (auto observer : m_observers) { + if (observer) { + observer->on_observer_event(source, event, data); + } + } + } + void add_observer(EventObserver *observer) { + if (observer) { + m_observers.push_back(observer); + } + } + void remove_observer(EventObserver *observer) { + m_observers.erase( + std::remove(m_observers.begin(), m_observers.end(), observer), + m_observers.end()); + }; + +private: + std::vector*> m_observers; +}; + +} diff --git a/src/common/mmi-plugin-module-event.h b/src/common/mmi-plugin-module-event.h new file mode 100644 index 0000000..5d784c5 --- /dev/null +++ b/src/common/mmi-plugin-module-event.h @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2023 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#pragma once + +#include "mmi-event-observer.h" +#include "mmi-node.h" +#include "mmi-port.h" + +#include + +namespace mmi { + +enum class PLUGIN_MODULE_EVENT_TYPE { + OUTPUT_GENERATED, + SWITCH_EVENT_EMITTED, +}; + +struct PluginModuleEvent { + PluginModuleEvent(PLUGIN_MODULE_EVENT_TYPE type) : type(type) {} + virtual ~PluginModuleEvent() = default; + + PLUGIN_MODULE_EVENT_TYPE type; +}; + +struct PluginModuleEventOutputGenerated : public PluginModuleEvent { + PluginModuleEventOutputGenerated() : PluginModuleEvent(PLUGIN_MODULE_EVENT_TYPE::OUTPUT_GENERATED) {} + virtual ~PluginModuleEventOutputGenerated() {} + + mmi_port_instance_h port_instance{nullptr}; + mmi_data_h data{nullptr}; +}; + +struct PluginModuleEventSwitchEventEmitted : public PluginModuleEvent { + PluginModuleEventSwitchEventEmitted() : PluginModuleEvent(PLUGIN_MODULE_EVENT_TYPE::SWITCH_EVENT_EMITTED) {} + virtual ~PluginModuleEventSwitchEventEmitted() {} + + mmi_node_instance_h node_instance{nullptr}; + std::string controllee; + bool state{false}; +}; + +}; // namespace mmi diff --git a/src/common/mmi-workflow-event.h b/src/common/mmi-workflow-event.h new file mode 100644 index 0000000..e9d6a55 --- /dev/null +++ b/src/common/mmi-workflow-event.h @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2023 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#pragma once + +#include "mmi-event-observer.h" + +namespace mmi { + +enum class WORKFLOW_EVENT_TYPE { + WORKFLOW_CREATED, +}; + +struct WorkflowEvent { + WorkflowEvent(WORKFLOW_EVENT_TYPE type) : type(type) {} + virtual ~WorkflowEvent() = default; + + WORKFLOW_EVENT_TYPE type; +}; + +struct WorkflowEventWorkflowCreated : public WorkflowEvent { + WorkflowEventWorkflowCreated() : WorkflowEvent(WORKFLOW_EVENT_TYPE::WORKFLOW_CREATED) {} + virtual ~WorkflowEventWorkflowCreated() {} + + std::any workflow_instance{nullptr}; +}; + +}; // namespace mmi diff --git a/src/common/mmi-workflow-output-event.h b/src/common/mmi-workflow-output-event.h new file mode 100644 index 0000000..818b3e5 --- /dev/null +++ b/src/common/mmi-workflow-output-event.h @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2023 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#pragma once + +#include "mmi-event-observer.h" + +#include +#include + +namespace mmi { + +enum class WORKFLOW_OUTPUT_EVENT_TYPE { + OUTPUT_GENERATED, +}; + +struct WorkflowOutputEvent { + WorkflowOutputEvent(WORKFLOW_OUTPUT_EVENT_TYPE type) : type(type) {} + virtual ~WorkflowOutputEvent() = default; + + WORKFLOW_OUTPUT_EVENT_TYPE type; +}; + +struct WorkflowOutputEventOutputGenerated : public WorkflowOutputEvent { + WorkflowOutputEventOutputGenerated() : WorkflowOutputEvent(WORKFLOW_OUTPUT_EVENT_TYPE::OUTPUT_GENERATED) {} + virtual ~WorkflowOutputEventOutputGenerated() {} + + bundle *data{nullptr}; + std::string source_name; + std::string client_id; + int local_workflow_instance_id{0}; +}; + +}; // namespace mmi diff --git a/src/meson.build b/src/meson.build index 7803d2e..e9bae44 100644 --- a/src/meson.build +++ b/src/meson.build @@ -1,59 +1,3 @@ -mmi_srcs = [ - 'mmi.h', - 'mmi.c', - 'mmi-ipc.h', - 'mmi-ipc.c', - 'mmi-dbg.h', - 'mmi_proxy.h', - 'mmi_proxy.c', - 'mmi-client.h', - 'mmi-client.c', - ] - -install_headers( - 'mmi.h', - ) - -glib_dep = dependency('glib-2.0', method : 'pkg-config') -gio_dep = dependency('gio-2.0', method : 'pkg-config') -bundle_dep = dependency('bundle', method : 'pkg-config') -dlog_dep = dependency('dlog', method : 'pkg-config') -rpc_port_dep = dependency('rpc-port', method : 'pkg-config') -libtzplatform_config_dep = dependency('libtzplatform-config') -ecore_dep = dependency('ecore', method : 'pkg-config') - -mmi_deps = [ - ecore_dep, - glib_dep, - gio_dep, - bundle_dep, - dlog_dep, - rpc_port_dep, - libtzplatform_config_dep] - -mmi_include_dirs = include_directories( - '.' - ) - -mmi_lib = shared_library( - 'mmi', - mmi_srcs, - dependencies : [mmi_deps], - include_directories : [mmi_include_dirs], - version : meson.project_version(), - install : true - ) - -pkgconfig.generate( - filebase : 'mmi', - name : 'mmi', - description : 'Multi-modal Interaction Framework Library', - version : meson.project_version(), - libraries : mmi_lib - ) - -mmi_declared_dep = declare_dependency( - link_with : mmi_lib, - dependencies : [mmi_deps], - include_directories : [mmi_include_dirs] - ) +subdir('mmi') +subdir('mmi-manager') +subdir('mmi-cli') diff --git a/src/mmi-cli/cliecoresession.h b/src/mmi-cli/cliecoresession.h new file mode 100644 index 0000000..fb03f33 --- /dev/null +++ b/src/mmi-cli/cliecoresession.h @@ -0,0 +1,106 @@ +#include "cli/cli.h" + +#include +#include +#include // std::invalid_argument + +#include + +namespace cli +{ + +class CliEcoreSession : public CliSession +{ +public: + /// @throw std::invalid_argument if @c _in or @c out are invalid streams + explicit CliEcoreSession(Cli& _cli, std::istream& _in=std::cin, std::ostream& _out=std::cout) : + CliSession(_cli, _out, 1), + exit(false), + loop(false), + in(_in), + handler(nullptr) + { + if (!_in.good()) throw std::invalid_argument("istream invalid"); + if (!_out.good()) throw std::invalid_argument("ostream invalid"); + ExitAction( + [this](std::ostream&) noexcept + { + exit = true; + ecore_main_loop_quit(); + } + ); + } + std::istream& GetInputStream() { + return in; + } + static Eina_Bool Handler(void *data, int type, void *event) + { + CliEcoreSession* session = static_cast(data); + if (session->exit) return ECORE_CALLBACK_CANCEL; + + if (type == ECORE_EVENT_SIGNAL_EXIT) + { + session->Prompt(); + + std::string line; + std::istream& in = session->GetInputStream(); + if (!in.good()) + session->Exit(); + std::getline(in, line); + if (in.eof()) + session->Exit(); + else + session->Feed(line); + } + + return ECORE_CALLBACK_RENEW; + } + void Start() + { + Enter(); + + ecore_init(); + + handler = ecore_event_handler_add( + ECORE_EVENT_SIGNAL_EXIT, Handler, this); + + while(!exit && !loop) { + Prompt(); + + std::string line; + if (!in.good()) + Exit(); + std::getline(in, line); + if (in.eof()) + Exit(); + else + Feed(line); + } + } + void Loop() + { + if (loop) { + std::cout << "Already looping" << std::endl; + return; + } + + std::cout << "Running ecore main loop, please press to enter commands" << std::endl; + + loop = true; + ecore_main_loop_begin(); + } + void Finish() + { + if (handler) ecore_event_handler_del(handler); + + ecore_shutdown(); + } + +private: + bool exit; + bool loop; + std::istream& in; + Ecore_Event_Handler *handler; +}; + +} // namespace cli diff --git a/src/mmi-cli/meson.build b/src/mmi-cli/meson.build new file mode 100644 index 0000000..0512065 --- /dev/null +++ b/src/mmi-cli/meson.build @@ -0,0 +1,56 @@ +dlog_dep = dependency('dlog', method : 'pkg-config') +ecore_dep = dependency('ecore', method : 'pkg-config') +rpc_port_dep = dependency('rpc-port') + +mmi_cli_deps = [ + mmi_declared_dep, + dlog_dep, + ecore_dep, + rpc_port_dep, + ] + +mmi_cli_include_dirs = include_directories( + '.', + '../common/', + '../../capi/', + '../../external/', + ) + +mmi_cli_declared_dep = declare_dependency( + dependencies : [mmi_cli_deps], + include_directories : [mmi_cli_include_dirs] + ) + +mmi_cli_program_srcs = [ + 'mmi-cli.cpp' + ] + +mmi_cli_program_deps = [ + mmi_cli_declared_dep + ] + +executable('mmi-cli', + mmi_cli_program_srcs, + include_directories : [ mmi_cli_include_dirs ], + dependencies : [mmi_cli_program_deps], + install_dir : mmi_prefix_bindir, + install : true, + pie : true + ) + +mmi_cli_node_tester_program_srcs = [ + 'mmi-cli-node-tester.cpp' + ] + +mmi_cli_node_tester_program_deps = [ + mmi_cli_declared_dep + ] + +executable('mmi-cli-node-tester', + mmi_cli_node_tester_program_srcs, + include_directories : [ mmi_cli_include_dirs ], + dependencies : [mmi_cli_program_deps], + install_dir : mmi_prefix_bindir, + install : true, + pie : true + ) diff --git a/src/mmi-cli/mmi-cli-node-tester.cpp b/src/mmi-cli/mmi-cli-node-tester.cpp new file mode 100644 index 0000000..3a4a9c4 --- /dev/null +++ b/src/mmi-cli/mmi-cli-node-tester.cpp @@ -0,0 +1,822 @@ +#include +#include +#include +#include +#include + +#include + +#include "cliecoresession.h" + +#include "cli/cli.h" + +#include "mmi.h" +#include "mmi-plugin-module.h" +#include "mmi-plugin-storage.h" + +#define MMI_CLI_LOG_(fmt, arg...) \ + ({ do { \ + printf("%s(%d) > " fmt "\n", __func__, __LINE__, ##arg); \ + } while (0); }) + +#define _D(fmt, args...) MMI_CLI_LOG_(fmt, ##args) +#define _I(fmt, args...) MMI_CLI_LOG_(fmt, ##args) +#define _W(fmt, args...) MMI_CLI_LOG_(fmt, ##args) +#define _E(fmt, args...) MMI_CLI_LOG_(fmt, ##args) + +class Program; + +struct PortInstance +{ + std::string m_name; + mmi_port_type_e m_port_type; + mmi_data_type_e m_data_type; + mmi_port_callbacks m_callbacks{nullptr,}; +}; + +struct NodeInstance +{ + mmi_standard_node_type_e m_node_type; + + mmi_node_callbacks m_callbacks{nullptr,}; + + std::vector m_ports; +}; + +enum class AttributeTestType { + Int, + Text, + Bool, + TextArray, + + MaxCount, +}; + +typedef struct { + std::string name; + AttributeTestType type; + std::string description; + std::function attribute_generator; +} AttributeTestEntry; + +enum class DataTestType { + Int, + Text, + Bool, + TextArray, + ScreenInfo, + UserRecognition, + Audio, + Video, + + MaxCount, +}; + +typedef struct { + std::string name; + DataTestType type; + std::string description; + std::function data_generator; +} DataTestEntry; + +class Program +{ +public: + Program(); + ~Program(); + + static void log_mmi_data(PortInstance *port_instance, mmi_data_h data); + static void port_instance_output_handler(mmi_port_instance_h port, mmi_data_h data, void *user_data); + void load_module(std::string path); + void unload_module(); + std::string read_string_value(std::string prompt); + int read_int_value(std::string prompt); + void create_node_instance(); + NodeInstance* get_node_instance(); + void destroy_node_instance(); + void activate_node_instance(); + void deactivate_node_instance(); + void set_attribute(AttributeTestType type); + void push_data(DataTestType type); + void set_ecore_session(cli::CliEcoreSession *session); + +private: + cli::CliEcoreSession *m_session{nullptr}; + void* m_handle{nullptr}; + mmi_plugin_module_node_list_s m_node_list; + + std::map m_node_instance_map; + int m_node_instance_index{0}; +}; + +static Program g_program; + +AttributeTestEntry g_attribute_test_entries[] = { + {"int", AttributeTestType::Int, "Set an integer attribute", + [](Program *program, mmi_attribute_h *attribute) { + if (!program || !attribute) { + return; + } + std::string name = program->read_string_value("Enter attribute name: "); + std::string input = program->read_string_value("Enter attribute value: "); + int value = 0; + try { + value = std::stoi(input); + } catch (std::invalid_argument& e) { + _E("Invalid argument: %s", e.what()); + return; + } catch (std::out_of_range& e) { + _E("Out of range: %s", e.what()); + return; + } + mmi_primitive_value_h primitive_value = nullptr; + + mmi_primitive_value_create_int(value, &primitive_value); + mmi_attribute_create(primitive_value, name.c_str(), attribute); + + mmi_primitive_value_destroy(primitive_value); + } + }, + {"text", AttributeTestType::Text, "Set a text attribute", + [](Program *program, mmi_attribute_h *attribute) { + if (!program || !attribute) { + return; + } + std::string name = program->read_string_value("Enter attribute name: "); + std::string value = program->read_string_value("Enter attribute value: "); + mmi_primitive_value_h primitive_value = nullptr; + + mmi_primitive_value_create_string(value.c_str(), &primitive_value); + mmi_attribute_create(primitive_value, name.c_str(), attribute); + + mmi_primitive_value_destroy(primitive_value); + } + }, + {"bool", AttributeTestType::Bool, "Set a boolean attribute", + [](Program *program, mmi_attribute_h *attribute) { + if (!program || !attribute) { + return; + } + std::string name = program->read_string_value("Enter attribute name: "); + std::string input = program->read_string_value("Enter attribute value: "); + int value = 0; + try { + value = std::stoi(input); + } catch (std::invalid_argument& e) { + _E("Invalid argument: %s", e.what()); + return; + } catch (std::out_of_range& e) { + _E("Out of range: %s", e.what()); + return; + } + mmi_primitive_value_h primitive_value = nullptr; + + mmi_primitive_value_create_bool((value != 0), &primitive_value); + mmi_attribute_create(primitive_value, name.c_str(), attribute); + + mmi_primitive_value_destroy(primitive_value); + } + }, + {"text_array", AttributeTestType::TextArray, "Set a text array attribute", + [](Program *program, mmi_attribute_h *attribute) { + if (!program || !attribute) { + return; + } + std::string name = program->read_string_value("Enter attribute name: "); + /* Currently we only support predefined array with string type */ + constexpr size_t COMMAND_NUM = 2; + const char *commands[] = {"Open", "Close"}; + mmi_attribute_create_string_array(name.c_str(), commands, COMMAND_NUM, attribute); + } + }, +}; + +DataTestEntry g_data_test_entries[] = { + {"int", DataTestType::Int, "Push an integer data", + [](Program *program, PortInstance *port_instance) { + if (!program || !port_instance) { + return; + } + mmi_data_h data = nullptr; + int value = program->read_int_value("Enter data value: "); + mmi_data_create_int(value, &data); + if (data) { + port_instance->m_callbacks.input_data_received_cb(static_cast(port_instance), data); + mmi_data_destroy(data); + } + } + }, + {"text", DataTestType::Text, "Push a text data", + [](Program *program, PortInstance *port_instance) { + if (!program || !port_instance) { + return; + } + mmi_data_h data = nullptr; + std::string value = program->read_string_value("Enter data value: "); + mmi_data_create_text(value.c_str(), &data); + if (data) { + port_instance->m_callbacks.input_data_received_cb(static_cast(port_instance), data); + mmi_data_destroy(data); + } + } + }, + {"screen_info", DataTestType::ScreenInfo, "Push a screen info data", + [](Program *program, PortInstance *port_instance) { + if (!program || !port_instance) { + return; + } + mmi_data_h data = nullptr; + /* Currently we only support predefined screen info */ + mmi_data_create_array(&data); + for (size_t loop = 0;loop < 5;loop++) { + mmi_data_h struct_data; + mmi_data_create_struct(&struct_data); + + mmi_data_h coord_x; + mmi_data_create_int(10 * loop, &coord_x); + mmi_data_set_struct_element(struct_data, "coord_x", coord_x); + + mmi_data_h coord_y; + mmi_data_create_int(10 * loop, &coord_y); + mmi_data_set_struct_element(struct_data, "coord_y", coord_y); + + mmi_data_h width; + mmi_data_create_int(100, &width); + mmi_data_set_struct_element(struct_data, "width", width); + + mmi_data_h height; + mmi_data_create_int(100, &height); + mmi_data_set_struct_element(struct_data, "height", height); + + mmi_data_h object_id; + mmi_data_create_text( + (std::string("id_") + std::to_string(loop)).c_str(), &object_id); + mmi_data_set_struct_element(struct_data, "object_id", object_id); + + mmi_data_h label; + mmi_data_create_text( + (std::string("label") + std::to_string(loop)).c_str(), &label); + mmi_data_set_struct_element(struct_data, "label", label); + + mmi_data_h role; + mmi_data_create_text( + (std::string("role") + std::to_string(loop)).c_str(), &role); + mmi_data_set_struct_element(struct_data, "role", role); + + mmi_data_add_array_element(data, struct_data); + } + if (data) { + port_instance->m_callbacks.input_data_received_cb(static_cast(port_instance), data); + mmi_data_destroy(data); + } + } + }, + {"user_recognition", DataTestType::UserRecognition, "Push a user recognition info data", + [](Program *program, PortInstance *port_instance) { + if (!program || !port_instance) { + return; + } + mmi_data_h data = nullptr; + mmi_data_create_struct(&data); + + std::string value = program->read_string_value("Enter fromNode name: "); + mmi_data_h from_node = nullptr; + mmi_data_create_text(value.c_str(), &from_node); + mmi_data_set_struct_element(data, "fromNode", from_node); + + /* Currently we only support predefined user recognition info */ + mmi_data_h candidates = nullptr; + mmi_data_create_array(&candidates); + for (size_t loop = 0;loop < 2;loop++) { + mmi_data_h struct_data; + mmi_data_create_struct(&struct_data); + + mmi_data_h user_id; + mmi_data_create_text( + (std::string("user") + std::to_string(loop)).c_str(), &user_id); + mmi_data_set_struct_element(struct_data, "id", user_id); + + mmi_data_h user_name; + mmi_data_create_text( + (std::string("name") + std::to_string(loop)).c_str(), &user_name); + mmi_data_set_struct_element(struct_data, "name", user_name); + + mmi_data_h score; + mmi_data_create_float(91.5f + loop, &score); + mmi_data_set_struct_element(struct_data, "confidence", score); + + mmi_data_add_array_element(candidates, struct_data); + } + mmi_data_set_struct_element(data, "recognizedCandidates", candidates); + + if (data) { + port_instance->m_callbacks.input_data_received_cb(static_cast(port_instance), data); + mmi_data_destroy(data); + } + } + }, + {"audio", DataTestType::Audio, "Push audio data from file", + [](Program *program, PortInstance *port_instance) { + if (!program || !port_instance) { + return; + } + std::string value = program->read_string_value("Enter audio file path : "); + const size_t read_size = 640; + char frame_buf[read_size]; + FILE *fp = fopen(value.c_str(), "rb"); + if (fp) { + size_t size = 0; + while ((size = fread(frame_buf, sizeof(char), read_size, fp)) > 0) { + mmi_data_h data = nullptr; + mmi_data_create_audio(frame_buf, size, &data); + if (data) { + port_instance->m_callbacks.input_data_received_cb(static_cast(port_instance), data); + mmi_data_destroy(data); + } + } + fclose(fp); + } else { + _E("Failed to open file %s", value.c_str()); + } + } + }, + {"video", DataTestType::Video, "Push video data from file", + [](Program *program, PortInstance *port_instance) { + if (!program || !port_instance) { + return; + } + std::string value = program->read_string_value("Enter video file path : "); + const size_t read_size = 921600; // 640*480*3 + char frame_buf[read_size]; + FILE *fp = fopen(value.c_str(), "rb"); + if (fp) { + size_t size; + while ((size = fread(frame_buf, sizeof(char), read_size, fp)) > 0) { + mmi_data_h data = nullptr; + mmi_data_create_video(frame_buf, size, &data); + if (data) { + port_instance->m_callbacks.input_data_received_cb(static_cast(port_instance), data); + mmi_data_destroy(data); + } + } + fclose(fp); + } else { + _E("Failed to open file %s", value.c_str()); + } + } + }, +}; + +Program::Program() { + memset(&m_node_list, 0, sizeof(mmi_plugin_module_node_list_s)); +} +Program::~Program() { + unload_module(); +} + +void Program::log_mmi_data(PortInstance *port_instance, mmi_data_h data) { + mmi_data_type_e type; + mmi_data_get_type(data, &type); + switch(type) { + case MMI_DATA_TYPE_BOOLEAN: + { + bool value; + mmi_data_get_bool(data, &value); + _D("Port %s generated data type %d (BOOLEAN), value %d", port_instance->m_name.c_str(), type, value); + } + break; + case MMI_DATA_TYPE_INTEGER: + { + int value; + mmi_data_get_int(data, &value); + _D("Port %s generated data type %d (INTEGER), value %d", port_instance->m_name.c_str(), type, value); + } + break; + case MMI_DATA_TYPE_FLOAT: + { + float value; + mmi_data_get_float(data, &value); + _D("Port %s generated data type %d (FLOAT), value %f", port_instance->m_name.c_str(), type, value); + } + break; + case MMI_DATA_TYPE_TEXT: + { + const char *value = nullptr; + mmi_data_get_text(data, &value); + _D("Port %s generated data type %d (TEXT), value %s", port_instance->m_name.c_str(), type, value); + } + break; + case MMI_DATA_TYPE_USER_IDENTIFICATION: + { + const void *ptr = nullptr; + size_t len = 0; + mmi_data_get_user_identification(data, &ptr, &len); + std::string hex_string; + for (size_t i = 0; i < len; i++) { + char buf[3] = {0, }; + snprintf(buf, sizeof(buf), "%02x", ((unsigned char*)ptr)[i]); + hex_string += buf; + } + _D("Port %s generated data type %d (USER_IDENTIFICATION), size %zu, hex %s", + port_instance->m_name.c_str(), type, len, hex_string.c_str()); + } + break; + case MMI_DATA_TYPE_ARRAY: + { + size_t count = 0; + mmi_data_get_array_count(data, &count); + + for (size_t i = 0; i < count; i++) { + mmi_data_h element = nullptr; + mmi_data_get_array_element(data, i, &element); + _D("Array Element %zu", i); + log_mmi_data(port_instance, element); + } + } + break; + case MMI_DATA_TYPE_STRUCT: + { + size_t count; + mmi_data_get_struct_count(data, &count); + + for (size_t i = 0; i < count; i++) { + const char *element_name = nullptr; + mmi_data_get_struct_element_name(data, i, &element_name); + _D("Struct Element %zu : %s", i, element_name); + mmi_data_h element_value = nullptr; + mmi_data_get_struct_element_value(data, i, &element_value); + log_mmi_data(port_instance, element_value); + } + } + break; + default: + _D("Port %s generated data type %d", port_instance->m_name.c_str(), type); + break; + } +} + +void Program::port_instance_output_handler(mmi_port_instance_h port, mmi_data_h data, void *user_data) { + if (port == nullptr || data == nullptr) { + _E("Invalid parameter : port(%p), data(%p)", port, data); + return; + } + + PortInstance *port_instance = static_cast(port); + log_mmi_data(port_instance, data); +} + +void Program::load_module(std::string path) { + mmi_plugin_module_event_handler_info_s module_event_handler_info{nullptr, }; + module_event_handler_info.port_instance_output_handler = port_instance_output_handler; + + mmi_plugin_storage_set_plugin_module_event_handler(module_event_handler_info, nullptr); + + m_handle = dlopen(path.c_str(), RTLD_NOW); + if (!m_handle) { + _E("Failed to load plugin module: %s", dlerror()); + return; + } + + mmi_plugin_storage_set_current_module_identifier(path.c_str()); + auto prepare_node_list = reinterpret_cast( + dlsym(m_handle, MMI_PLUGIN_MODULE_GET_NODE_LIST_FUNC_NAME)); + if (!prepare_node_list) { + _E("Failed to find symbol: %s", dlerror()); + return; + } + + prepare_node_list(); + + m_node_list = mmi_plugin_storage_get_node_list(path.c_str()); + + /* Dump node_list */ + _D("node_list.count: %zu", m_node_list.node_count); + for (size_t i = 0;i < m_node_list.node_count;i++) { + mmi_node_h node = m_node_list.nodes[i]; + mmi_standard_node_type_e type; + mmi_node_get_type(node, &type); + mmi_node_callbacks callbacks; + mmi_node_get_callbacks(node, &callbacks); + size_t port_count; + _D("node_info[%zu].type: %d", i, type); + _D("node_info[%zu].callbacks : %p %p %p %p %p %p", i, + callbacks.initialized_cb, + callbacks.deinitialized_cb, + callbacks.attribute_set_cb, + callbacks.activated_cb, + callbacks.deactivated_cb, + callbacks.signal_received_cb); + mmi_node_get_port_count(node, &port_count); + _D("node_info[%zu].port_list.count: %zu", i, port_count); + for (size_t j = 0;j < port_count;j++) { + mmi_port_h port; + mmi_node_get_port(node, j, &port); + char *name = nullptr; + size_t length = 0; + mmi_port_get_name(port, &name, &length); + mmi_port_type_e port_type; + mmi_port_get_type(port, &port_type); + mmi_data_type_e data_type; + mmi_port_get_data_type(port, &data_type); + mmi_port_callbacks port_callbacks; + mmi_port_get_callbacks(port, &port_callbacks); + _D("node_info[%zu].port_info[%zu].name: %s", i, j, name); + _D("node_info[%zu].port_info[%zu].type: %d", i, j, port_type); + _D("node_info[%zu].port_info[%zu].data_type: %d", i, j, data_type); + _D("node_info[%zu].port_info[%zu].callbacks: %p %p", i, j, + port_callbacks.output_format_requested_cb, + port_callbacks.input_data_received_cb); + } + } +} + +void Program::unload_module() { + if (m_handle) dlclose(m_handle); + m_handle = nullptr; +} + +std::string Program::read_string_value(std::string prompt) { + if (!m_session) { + _E("Session is not created"); + return ""; + } + std::cout << prompt; + std::string value; + std::getline(m_session->GetInputStream(), value); + return value; +} + +int Program::read_int_value(std::string prompt) { + int value = -1; + std::string string_value = read_string_value(prompt); + try { + value = std::stoi(string_value); + } catch (std::invalid_argument& e) { + _E("Invalid argument: %s", e.what()); + return -1; + } catch (std::out_of_range& e) { + _E("Out of range: %s", e.what()); + return -1; + } + return value; +} + +void Program::create_node_instance() { + NodeInstance *node_instance = new NodeInstance(); + + /* Currently only one node is supported */ + mmi_node_h node = m_node_list.nodes[0]; + mmi_standard_node_type_e type; + mmi_node_get_type(node, &type); + mmi_node_callbacks callbacks; + mmi_node_get_callbacks(node, &callbacks); + + node_instance->m_node_type = type; + node_instance->m_callbacks = callbacks; + + mmi_plugin_module_node_instance_info_s node_instance_info; + node_instance_info.node = static_cast(node_instance); + node_instance_info.port_info_count = 0; + + size_t port_count; + mmi_node_get_port_count(node, &port_count); + for (size_t j = 0;j < port_count;j++) { + mmi_port_h port; + mmi_node_get_port(node, j, &port); + char *name = nullptr; + size_t length = 0; + mmi_port_get_name(port, &name, &length); + mmi_port_type_e port_type; + mmi_port_get_type(port, &port_type); + mmi_data_type_e data_type; + mmi_port_get_data_type(port, &data_type); + mmi_port_callbacks port_callbacks; + mmi_port_get_callbacks(port, &port_callbacks); + + PortInstance *port_instance = new PortInstance(); + port_instance->m_name = name; + port_instance->m_port_type = port_type; + port_instance->m_data_type = data_type; + port_instance->m_callbacks = port_callbacks; + + node_instance->m_ports.push_back(port_instance); + + node_instance_info.port_infos[node_instance_info.port_info_count].port = + static_cast(port_instance); + snprintf(node_instance_info.port_infos[node_instance_info.port_info_count].name, MMI_NAME_MAX_LENGTH, "%s", name); + + node_instance_info.port_info_count++; + } + + mmi_plugin_storage_add_node_instance_info(&node_instance_info); + + m_node_instance_map[m_node_instance_index] = node_instance; + node_instance->m_callbacks.initialized_cb(static_cast(node_instance)); + + _D("Created node instance: %zu", m_node_instance_index); + + m_node_instance_index++; +} + +NodeInstance* Program::get_node_instance() { + NodeInstance *node_instance = nullptr; + if (m_node_instance_map.size() == 1) { + node_instance = m_node_instance_map.begin()->second; + } else { + int index = read_int_value("Enter instance id: "); + auto it = m_node_instance_map.find(index); + if (it == m_node_instance_map.end()) { + _E("Failed to find node instance: %d", index); + return nullptr; + } + node_instance = it->second; + } + return node_instance; +} + +void Program::destroy_node_instance() { + NodeInstance *node_instance = get_node_instance(); + if (!node_instance) return; + + node_instance->m_callbacks.deinitialized_cb(static_cast(node_instance)); + mmi_plugin_storage_remove_node_instance_info(static_cast(node_instance)); + for (auto port_instance : node_instance->m_ports) { + delete port_instance; + } + delete node_instance; + + /* Erase node_instance from map */ + for (auto it = m_node_instance_map.begin();it != m_node_instance_map.end();it++) { + if (it->second == node_instance) { + m_node_instance_map.erase(it); + break; + } + } +} + +void Program::activate_node_instance() { + NodeInstance *node_instance = get_node_instance(); + if (!node_instance) return; + + node_instance->m_callbacks.activated_cb(static_cast(node_instance)); +} + +void Program::deactivate_node_instance() { + NodeInstance *node_instance = get_node_instance(); + if (!node_instance) return; + + node_instance->m_callbacks.deactivated_cb(static_cast(node_instance)); +} + +void Program::set_attribute(AttributeTestType type) { + NodeInstance *node_instance = get_node_instance(); + if (!node_instance) return; + + mmi_attribute_h attribute = nullptr; + + if (type >= AttributeTestType::MaxCount) { + _E("Invalid attribute type"); + return; + } + + size_t entry_index = static_cast(AttributeTestType::MaxCount); + for (size_t i = 0;i < sizeof(g_attribute_test_entries) / sizeof(g_attribute_test_entries[0]);i++) { + if (g_attribute_test_entries[i].type == type) { + entry_index = i; + break; + } + } + if (entry_index == static_cast(AttributeTestType::MaxCount)) { + _E("Failed to find attribute type: %d", type); + return; + } + g_attribute_test_entries[entry_index].attribute_generator(this, &attribute); + + node_instance->m_callbacks.attribute_set_cb(static_cast(node_instance), attribute); + mmi_attribute_destroy(attribute); +} + +void Program::push_data(DataTestType type) { + NodeInstance *node_instance = get_node_instance(); + if (!node_instance) return; + + if (type >= DataTestType::MaxCount) { + _E("Invalid data type"); + return; + } + + std::string name = read_string_value("Enter port name: "); + for (auto port_instance : node_instance->m_ports) { + if (port_instance->m_name == name) { + mmi_data_h data = nullptr; + + size_t entry_index = static_cast(DataTestType::MaxCount); + for (size_t i = 0;i < sizeof(g_data_test_entries) / sizeof(g_data_test_entries[0]);i++) { + if (g_data_test_entries[i].type == type) { + entry_index = i; + break; + } + } + if (entry_index == static_cast(DataTestType::MaxCount)) { + _E("Failed to find data type"); + return; + } + g_data_test_entries[entry_index].data_generator(this, port_instance); + + break; + } + } +} + +void Program::set_ecore_session(cli::CliEcoreSession *session) { + m_session = session; +} + +int main(int argc, char **argv) +{ + if (argc < 2) { + std::cout << "Usage: " << argv[0] << " " << std::endl; + return 0; + } + std::string path = argv[1]; + try { + cli::SetColor(); + + cli::CliEcoreSession *ref = nullptr; + auto rootMenu = std::make_unique( "tester" ); + + rootMenu->Insert("loop", [&ref](std::ostream& out) { + if (ref) { + ref->Loop(); + } + }, + "Start ecore main loop"); + + rootMenu->Insert("load", [path](std::ostream& out) { + g_program.load_module(path); + }, + "Load the node module that needs to be tested"); + + rootMenu->Insert("unload", [](std::ostream& out) { + g_program.unload_module(); + }, + "Unload the currently loaded node module"); + + rootMenu->Insert("create", [](std::ostream& out) { + g_program.create_node_instance(); + }, + "Create a new node instance"); + + rootMenu->Insert("destroy", [](std::ostream& out) { + g_program.destroy_node_instance(); + }, + "Destroy the node instance with the given index"); + + rootMenu->Insert("activate", [](std::ostream& out) { + g_program.activate_node_instance(); + }, + "Activate the node instance with the given index"); + + rootMenu->Insert("deactivate", [](std::ostream& out) { + g_program.deactivate_node_instance(); + }, + "Dectivate the node instance with the given index"); + + auto attributeMenu = std::make_unique("set_attribute"); + for (auto &entry : g_attribute_test_entries) { + attributeMenu->Insert(entry.name, [entry](std::ostream& out) { + g_program.set_attribute(entry.type); + }, + entry.description); + } + + rootMenu->Insert(std::move(attributeMenu)); + + auto dataMenu = std::make_unique("push_data"); + for (auto &entry : g_data_test_entries) { + dataMenu->Insert(entry.name, [entry](std::ostream& out) { + g_program.push_data(entry.type); + }, + entry.description); + } + + rootMenu->Insert(std::move(dataMenu)); + + cli::Cli cli(std::move(rootMenu)); + + cli::CliEcoreSession session(cli); + ref = &session; + + g_program.set_ecore_session(&session); + + session.Start(); + session.Finish(); + + g_program.set_ecore_session(nullptr); + } catch (const std::exception& e) { + std::cout << "Exception: " << e.what() << std::endl; + } + + return 0; +} + diff --git a/src/mmi-cli/mmi-cli.cpp b/src/mmi-cli/mmi-cli.cpp new file mode 100644 index 0000000..d3e6963 --- /dev/null +++ b/src/mmi-cli/mmi-cli.cpp @@ -0,0 +1,172 @@ +#include +#include + +#include "cliecoresession.h" + +#include "cli/cli.h" + +#include "mmi.h" + +class Program +{ +public: + Program() { + mmi_initialize(); + mmi_set_state_changed_cb(state_changed_cb, this); + } + ~Program() { + mmi_unset_state_changed_cb(state_changed_cb); + mmi_deinitialize(); + } + + void create_instance(int type) { + if (m_instance) { + std::cout << "Instance already created" << std::endl; + return; + } + + std::cout << "Creating instance of type " << type << std::endl; + mmi_standard_workflow_instance_create( + static_cast(type), &m_instance); + mmi_workflow_instance_set_output_callback(m_instance, "COMMAND", + [](mmi_workflow_instance_h instance, const char *name, mmi_data_h data, void *user_data) { + std::cout << "OUTPUT data received : " << std::string(name ? name : "") << std::endl; + }, + nullptr); + mmi_workflow_instance_set_output_callback(m_instance, "UTTERANCE", + [](mmi_workflow_instance_h instance, const char *name, mmi_data_h data, void *user_data) { + const char *value = nullptr; + mmi_data_get_text(data, &value); + std::cout << "OUTPUT data received : " << std::string(name ? name : "") << " : " << value << std::endl; + }, + nullptr); + } + void destroy_instance() { + if (!m_instance) { + std::cout << "No instance to destroy" << std::endl; + return; + } + + std::cout << "Destroying instance" << std::endl; + mmi_workflow_instance_destroy(m_instance); + m_instance = nullptr; + } + void activate_instance() { + if (!m_instance) { + std::cout << "No instance to activate" << std::endl; + return; + } + + std::cout << "Activating instance" << std::endl; + mmi_workflow_instance_activate(m_instance); + } + void deactivate_instance() { + if (!m_instance) { + std::cout << "No instance to deactivate" << std::endl; + return; + } + + std::cout << "Deactivating instance" << std::endl; + mmi_workflow_instance_deactivate(m_instance); + } + void set_attribute() { + if (!m_instance) { + std::cout << "No instance to set attribute" << std::endl; + return; + } + + std::cout << "Setting attribute " << std::endl; + + mmi_attribute_h attribute = nullptr; + constexpr size_t COMMAND_NUM = 2; + const char *commands[] = {"Open", "Close"}; + mmi_attribute_create_string_array("COMMANDS", commands, COMMAND_NUM, &attribute); + + mmi_workflow_instance_set_attribute(m_instance, attribute); + + mmi_attribute_destroy(attribute); + + attribute = nullptr; + mmi_primitive_value_h lang_value = nullptr; + mmi_primitive_value_create_string("ko-KR", &lang_value); + mmi_attribute_create(lang_value, "LANGUAGE", &attribute); + mmi_workflow_instance_set_attribute(m_instance, attribute); + mmi_attribute_destroy(attribute); + mmi_primitive_value_destroy(lang_value); + + attribute = nullptr; + mmi_primitive_value_h stop_method_value = nullptr; + mmi_primitive_value_create_int(1, &stop_method_value); // 1 : silence detection + mmi_attribute_create(stop_method_value, "STOP_METHOD", &attribute); + mmi_workflow_instance_set_attribute(m_instance, attribute); + mmi_attribute_destroy(attribute); + mmi_primitive_value_destroy(stop_method_value); + } + + static int state_changed_cb(mmi_state_e state, void *user_data) { + Program *program = static_cast(user_data); + program->m_state = state; + std::cout << "State changed to " << state << std::endl; + return 0; + } +private: + mmi_state_e m_state{MMI_STATE_NONE}; + mmi_workflow_instance_h m_instance{nullptr}; +}; + +static Program g_program; + +int main(void) +{ + try { + cli::SetColor(); + + cli::CliEcoreSession *ref = nullptr; + auto rootMenu = std::make_unique( "cli" ); + + rootMenu->Insert("loop", [&ref](std::ostream& out) { + if (ref) { + ref->Loop(); + } + }, + "Start ecore main loop"); + + rootMenu->Insert("create", [](std::ostream& out, int type) { + g_program.create_instance(type); + }, + "Creates a new workflow instance"); + + rootMenu->Insert("destroy", [](std::ostream& out) { + g_program.destroy_instance(); + }, + "Destroys the current workflow instance"); + + rootMenu->Insert("set_attribute", [](std::ostream& out) { + g_program.set_attribute(); + }, + "Set attributes for the current workflow instance"); + + rootMenu->Insert("activate", [](std::ostream& out) { + g_program.activate_instance(); + }, + "Activates the current workflow instance"); + + rootMenu->Insert("deactivate", [](std::ostream& out) { + g_program.deactivate_instance(); + }, + "Deactivates the current workflow instance"); + + cli::Cli cli(std::move(rootMenu)); + + cli::CliEcoreSession session(cli); + ref = &session; + + session.Start(); + session.Finish(); + } catch (const std::exception& e) { + std::cout << "Exception: " << e.what() << std::endl; + } + + return 0; +} + diff --git a/src/mmi-client.c b/src/mmi-client.c deleted file mode 100644 index 2d047a9..0000000 --- a/src/mmi-client.c +++ /dev/null @@ -1,140 +0,0 @@ -/* - * Copyright (c) 2022 Samsung Electronics Co., Ltd All Rights Reserved - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT 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 "mmi-ipc.h" -#include "mmi-dbg.h" -#include "mmi-client.h" - -static mmi_handle mmi_h = NULL; - -int mmi_client_create(void) -{ - mmi_h = (mmi_handle)calloc(1, sizeof(mmi_struct_s)); - - if (mmi_h == NULL) { - LOGE("Failed to allocate memory for mmi_h !\n"); - return MMI_ERROR_OUT_OF_MEMORY; - } - - if (mmi_ipc_initialize()) { - LOGE("Failed to init mmi ipc !\n"); - if(mmi_h != NULL) { - free(mmi_h); - mmi_h = NULL; - } - return MMI_ERROR_NOT_SUPPORTED; - } - - mmi_h->rpc_h = mmi_ipc_get_rpc_h(); - mmi_h->stub_appid = mmi_ipc_get_stub_appid(); - mmi_h->result_cb_list = NULL; - - return MMI_ERROR_NONE; -} - -int mmi_client_destroy(void) -{ - if (mmi_h == NULL) { - LOGE("A mmi_h is already NULL"); - return MMI_ERROR_NONE; - } - - mmi_ipc_deinitialize(); - mmi_h->rpc_h = NULL; - - GList *iter = NULL; - mmi_result_cb_s *data = NULL; - - if (g_list_length(mmi_h->result_cb_list) > 0) { - iter = g_list_first(mmi_h->result_cb_list); - while (iter != NULL) { - data = iter->data; - if (data != NULL) { - data->input_event_type = 0; - data->result_callback = NULL; - free(data); - data = NULL; - - GList *temp = iter; - iter = g_list_next(iter); - mmi_h->result_cb_list = g_list_remove_link(mmi_h->result_cb_list, temp); - } - } - } - - free(mmi_h); - mmi_h = NULL; - - return MMI_ERROR_NONE; -} - -static const char* __mmi_client_convert_error_code(int err) -{ - switch (err) { - case MMI_ERROR_NONE: return "MMI_ERROR_NONE"; - case MMI_ERROR_OUT_OF_MEMORY: return "MMI_ERROR_OUT_OF_MEMORY"; - case MMI_ERROR_IO_ERROR: return "MMI_ERROR_IO_ERROR"; - case MMI_ERROR_INVALID_PARAMETER: return "MMI_ERROR_INVALID_PARAMETER"; - case MMI_ERROR_OUT_OF_NETWORK: return "MMI_ERROR_OUT_OF_NETWORK"; - case MMI_ERROR_TIMED_OUT: return "MMI_ERROR_TIMED_OUT"; - case MMI_ERROR_PERMISSION_DENIED: return "MMI_ERROR_PERMISSION_DENIED"; - case MMI_ERROR_NOT_SUPPORTED: return "MMI_ERROR_NOT_SUPPORTED"; - case MMI_ERROR_OPERATION_FAILED: return "MMI_ERROR_OPERATION_FAILED"; - default: - return "Invalid error code"; - } - return NULL; -} - -int mmi_client_set_result_cb(int input_event_type, mmi_result_cb callback, void* user_data) -{ - LOGI("Set result cb about input event type(%d) to client", input_event_type); - - if (callback == NULL) { - LOGE("Callback is invalid"); - return MMI_ERROR_INVALID_PARAMETER; - } - - if (mmi_h == NULL) { - LOGE("Fail to get client"); - return MMI_ERROR_OPERATION_FAILED; - } - - mmi_result_cb_s* input_result_callback = (mmi_result_cb_s*)calloc(1, sizeof(mmi_result_cb_s)); - if (input_result_callback == NULL) { - LOGE("[ERROR] Fail to allocate memory"); - return MMI_ERROR_OUT_OF_MEMORY; - } - - input_result_callback->input_event_type = input_event_type; - input_result_callback->result_callback = callback; - mmi_h->result_cb_list = g_list_append(mmi_h->result_cb_list, input_result_callback); - - int ret = mmi_ipc_register_input_event_result_cb(input_event_type, callback); - if (ret != MMI_ERROR_NONE) { - LOGE("Fail to register input event's result callback(%d), reason(%s)", ret, __mmi_client_convert_error_code(ret)); - callback(input_event_type, __mmi_client_convert_error_code(ret), NULL); - return ret; - } - - return MMI_ERROR_NONE; -} - -mmi_handle mmi_client_get(void) -{ - return mmi_h; -} diff --git a/src/mmi-ipc.c b/src/mmi-ipc.c deleted file mode 100644 index 8f66c5e..0000000 --- a/src/mmi-ipc.c +++ /dev/null @@ -1,252 +0,0 @@ -/* - * Copyright (c) 2022 Samsung Electronics Co., Ltd All Rights Reserved - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT 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 "mmi-ipc.h" -#include "mmi-dbg.h" -#include "mmi-client.h" - -#include -#include -#include -#include - -static rpc_port_proxy_mmi_h _rpc_h; -static const char* _stub_appid = "mmi-manager"; -static int _connected = 0; - -rpc_port_proxy_mmi_h mmi_ipc_get_rpc_h(void) -{ - return _rpc_h; -} - -bool mmi_ipc_is_connected(void) -{ - return _connected ? true : false; -} - -const char * mmi_ipc_get_stub_appid(void) -{ - return _stub_appid; -} - -static void _on_connected(rpc_port_proxy_mmi_h h, void *user_data) -{ - int r; - _connected = 1; - - LOGI("..."); - return; -} - -static void _on_disconnected(rpc_port_proxy_mmi_h h, void *user_data) -{ - _connected = 0; - - LOGI("..."); -} -static void _on_rejected(rpc_port_proxy_mmi_h h, void *user_data) -{ - LOGI("..."); - _connected = 0; -} - -int mmi_ipc_retry_connection(rpc_port_proxy_mmi_h h) -{ - LOGI("Retry connection"); - - int ret = rpc_port_proxy_mmi_connect_sync(h); - if (ret != RPC_PORT_ERROR_NONE) { - LOGE("Fail to retry connection(%d)", ret); - return MMI_ERROR_OPERATION_FAILED; - } - - return MMI_ERROR_NONE; -} - -int mmi_ipc_initialize(void) -{ - /* initialize handles */ - _rpc_h = NULL; - int ret; - - rpc_port_proxy_mmi_callback_s callback = { - .connected = _on_connected, - .disconnected = _on_disconnected, - .rejected = _on_rejected - }; - - ret = rpc_port_proxy_mmi_create(_stub_appid, &callback, NULL, &_rpc_h); - if (ret != RPC_PORT_ERROR_NONE) { - LOGE("Failed to create mmi proxy handle ! (error:%d)\n", ret); - return MMI_ERROR_OPERATION_FAILED; - } - - ret = rpc_port_proxy_mmi_connect_sync(_rpc_h); - if (ret != RPC_PORT_ERROR_NONE) { - LOGE("Try to connect again"); - //Retry - ret = mmi_ipc_retry_connection(_rpc_h); - if (ret != RPC_PORT_ERROR_NONE) { - LOGE("Fail to retry connection(%d)", ret); - return MMI_ERROR_OPERATION_FAILED; - } - } - - LOGI("connect mmi ipc"); - - return MMI_ERROR_NONE; -} - -void mmi_ipc_deinitialize(void) -{ - if (!_rpc_h) - return; - - LOGI("disconnect mmi ipc"); - - rpc_port_proxy_mmi_destroy(_rpc_h); - rpc_port_deregister_proc_info(); - _rpc_h = NULL; - - _connected = 0; -} - -int mmi_ipc_register_input_event_result_cb(int input_event_type, rpc_port_proxy_mmi_result_cb_cb callback) -{ - LOGI("Register input event(%d) result cb to MMI Service", input_event_type); - int ret; - - if (callback == NULL) { - LOGE("Parameter callback is NULL"); - return MMI_ERROR_INVALID_PARAMETER; - } - - if (input_event_type == MMI_INPUT_EVENT_TYPE_NONE) { - LOGE("Parameter input event type is NULL"); - return MMI_ERROR_INVALID_PARAMETER; - } - - mmi_handle client = mmi_client_get(); - if (client == NULL) { - LOGE("Fail to get client"); - return MMI_ERROR_OPERATION_FAILED; - } - - rpc_port_proxy_mmi_h rpc_h = client->rpc_h; - if (rpc_h == NULL) { - LOGE("Fail to get tidl rpc info"); - return MMI_ERROR_OPERATION_FAILED; - } - - if (mmi_ipc_is_connected() == false) { - LOGE("Try to connect again"); - ret = mmi_ipc_retry_connection(rpc_h); - if (ret != RPC_PORT_ERROR_NONE) { - LOGE("Fail to retry connection(%d)", ret); - return MMI_ERROR_OPERATION_FAILED; - } - } - - rpc_port_proxy_mmi_result_cb_h result_cb_h; - ret = rpc_port_proxy_mmi_result_cb_create(&result_cb_h); - if (ret != RPC_PORT_ERROR_NONE) { - LOGE("Failed to create result callback handle (error:%d)\n", ret); - return MMI_ERROR_OPERATION_FAILED; - } - rpc_port_proxy_mmi_result_cb_set_callback(result_cb_h, callback, NULL); - rpc_port_proxy_mmi_result_cb_set_once(result_cb_h, false); - if (result_cb_h == NULL) { - LOGE("Failed to create event callbacks"); - return MMI_ERROR_OPERATION_FAILED; - } - - ret = rpc_port_proxy_mmi_invoke_register_input_event(rpc_h, input_event_type, result_cb_h); - if (ret != RPC_PORT_ERROR_NONE) { - LOGE("Failed to register event callbacks(%d)\n", ret); - return MMI_ERROR_OPERATION_FAILED; - } - - return MMI_ERROR_NONE; -} - -int mmi_ipc_activate_input_event(int input_event_type) -{ - LOGI("Activate about input event type(%d)", input_event_type); - - int ret; - - if (input_event_type == MMI_INPUT_EVENT_TYPE_NONE) { - LOGE("Parameter input event type is NULL"); - return MMI_ERROR_INVALID_PARAMETER; - } - - mmi_handle client = mmi_client_get(); - if (client == NULL) { - LOGE("Fail to get client"); - return MMI_ERROR_OPERATION_FAILED; - } - - rpc_port_proxy_mmi_h rpc_h = client->rpc_h; - if (rpc_h == NULL) { - LOGE("Fail to get tidl rpc info"); - return MMI_ERROR_OPERATION_FAILED; - } - - if (mmi_ipc_is_connected() == false) { - LOGE("Try to connect again"); - ret = mmi_ipc_retry_connection(_rpc_h); - if (ret != RPC_PORT_ERROR_NONE) { - LOGE("Fail to retry connection(%d)", ret); - return MMI_ERROR_OPERATION_FAILED; - } - } - - ret = rpc_port_proxy_mmi_invoke_activate_input_event(rpc_h, input_event_type); - if (ret != RPC_PORT_ERROR_NONE) { - LOGE("Fail to invoke activate input event"); - return MMI_ERROR_OPERATION_FAILED; - } - - return MMI_ERROR_NONE; -} - -int mmi_ipc_deactivate_input_event(int input_event_type) -{ - LOGE("Dectivate about input event type(%d)", input_event_type); - - int ret; - rpc_port_proxy_mmi_h rpc_h = NULL; - - if (input_event_type == MMI_INPUT_EVENT_TYPE_NONE) { - LOGE("Parameter input event type is NULL"); - return MMI_ERROR_INVALID_PARAMETER; - } - - rpc_h = mmi_ipc_get_rpc_h(); - if (rpc_h == NULL) { - LOGE("Fail to get tidl rpc info"); - return MMI_ERROR_OPERATION_FAILED; - } - - ret = rpc_port_proxy_mmi_invoke_deactivate_input_event(rpc_h, input_event_type); - if (ret != RPC_PORT_ERROR_NONE) { - LOGE("Fail to invoke activate input event"); - return MMI_ERROR_OPERATION_FAILED; - } - - return MMI_ERROR_NONE; -} diff --git a/src/mmi-manager/main.cpp b/src/mmi-manager/main.cpp new file mode 100644 index 0000000..97c95c5 --- /dev/null +++ b/src/mmi-manager/main.cpp @@ -0,0 +1,83 @@ +/* +* Copyright © 2021 Samsung Electronics co., Ltd. All Rights Reserved. +* +* Permission is hereby granted, free of charge, to any person obtaining a +* copy of this software and associated documentation files (the "Software"), +* to deal in the Software without restriction, including without limitation +* the rights to use, copy, modify, merge, publish, distribute, sublicense, +* and/or sell copies of the Software, and to permit persons to whom the +* Software is furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice (including the next +* paragraph) shall be included in all copies or substantial portions of the +* Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +* DEALINGS IN THE SOFTWARE. +*/ + +#include + +#include "mmi-manager.h" +#include "mmi-ipc-tidl.h" +#include "mmi-self-container.h" + +#include "adishavit/argh.h" + +struct CommunicationChannelFactoryTIDL : + public mmi::communication::CommunicationChannelManagerFactory { + std::shared_ptr create_channel() override { + return std::make_shared(); + } +}; + +static Ecore_Event_Handler *exit_signal_event_handler = nullptr; + +static Eina_Bool +exit_signal_cb(void *data EINA_UNUSED, int type, void *event) { + ecore_main_loop_quit(); + + return ECORE_CALLBACK_PASS_ON; +} + +int main(int argc, char *argv[]) { + ecore_init(); + + std::shared_ptr communication_channel_factory; + std::shared_ptr plugin_module_proxy_factory; + + argh::parser cmdl(argv); + + if (cmdl["--self-container-test"]) { + plugin_module_proxy_factory = std::make_shared(); + } else { + plugin_module_proxy_factory = std::make_shared(); + } + + communication_channel_factory = std::make_shared(); + + mmi::Manager manager(communication_channel_factory, plugin_module_proxy_factory); + + manager.initialize(); + + exit_signal_event_handler = ecore_event_handler_add( + ECORE_EVENT_SIGNAL_EXIT, exit_signal_cb, nullptr); + + ecore_main_loop_begin(); + + ecore_main_loop_thread_safe_call_wait(1.0f); + + ecore_event_handler_del(exit_signal_event_handler); + exit_signal_event_handler = nullptr; + + manager.deinitialize(); + + ecore_shutdown(); + return 0; +} + diff --git a/src/mmi-manager/meson.build b/src/mmi-manager/meson.build new file mode 100644 index 0000000..ecec178 --- /dev/null +++ b/src/mmi-manager/meson.build @@ -0,0 +1,78 @@ +mmi_manager_library_srcs = [ + 'mmi-manager.cpp', + 'mmi-ipc-tidl.cpp', + 'mmi-client.cpp', + 'mmi-common.cpp', + 'mmi-data-gateway.cpp', + 'mmi-port-instance.cpp', + 'mmi-port-instance-manager.cpp', + 'mmi-node-prototype.cpp', + 'mmi-node-prototype-manager.cpp', + 'mmi-node-instance.cpp', + 'mmi-node-instance-manager.cpp', + 'mmi-workflow-prototype.cpp', + 'mmi-workflow-prototype-manager.cpp', + 'mmi-workflow-instance.cpp', + 'mmi-workflow-instance-manager.cpp', + 'mmi-plugin-module-proxy.cpp', + 'mmi-plugin-module-registry.cpp', + 'mmi-self-container.cpp', + 'mmi_stub.c', + ] + +bundle_dep = dependency('bundle', method : 'pkg-config') +dlog_dep = dependency('dlog', method : 'pkg-config') +ecore_dep = dependency('ecore', method : 'pkg-config') +glib_dep = dependency('glib-2.0', method : 'pkg-config') +rpc_port_dep = dependency('rpc-port') +libtzplatform_config_dep = dependency('libtzplatform-config') + +mmi_manager_deps = [ + mmi_declared_dep, + bundle_dep, + dlog_dep, + ecore_dep, + glib_dep, + rpc_port_dep, + libtzplatform_config_dep, + ] + +mmi_manager_include_dirs = include_directories( + '.', + '../common/', + '../../capi/', + '../../external/', + ) + +mmi_manager_library = library('mmi_manager', + mmi_manager_library_srcs, + include_directories : [ mmi_manager_include_dirs ], + dependencies : [mmi_manager_deps], + install_dir : mmi_prefix_libdir, + install : true + ) + +mmi_manager_declared_dep = declare_dependency( + link_with : mmi_manager_library, + dependencies : [mmi_manager_deps], + include_directories : [mmi_manager_include_dirs] + ) + +mmi_manager_program_srcs = [ + 'main.cpp' + ] + +mmi_manager_program_deps = [ + mmi_manager_declared_dep + ] + +executable('mmi-manager', + mmi_manager_program_srcs, + include_directories : [ mmi_manager_include_dirs ], + dependencies : [mmi_manager_program_deps], + install_dir : mmi_prefix_bindir, + install : true, + pie : true + ) + +install_data('mmi-manager.xml', install_dir:'/usr/share/packages/') diff --git a/src/mmi-manager/mmi-client.cpp b/src/mmi-manager/mmi-client.cpp new file mode 100644 index 0000000..3135075 --- /dev/null +++ b/src/mmi-manager/mmi-client.cpp @@ -0,0 +1,168 @@ +/* +* Copyright © 2021 Samsung Electronics co., Ltd. All Rights Reserved. +* +* Permission is hereby granted, free of charge, to any person obtaining a +* copy of this software and associated documentation files (the "Software"), +* to deal in the Software without restriction, including without limitation +* the rights to use, copy, modify, merge, publish, distribute, sublicense, +* and/or sell copies of the Software, and to permit persons to whom the +* Software is furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice (including the next +* paragraph) shall be included in all copies or substantial portions of the +* Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +* DEALINGS IN THE SOFTWARE. +*/ + +#include "mmi-client.h" +#include "mmi-common.h" +#include "mmi-manager-log.h" +#include "mmi_stub.h" + +namespace mmi { + +ClientManager::ClientManager() { +} + +ClientManager::~ClientManager() { +} + +void ClientManager::on_observer_event( + const COMMUNICATION_CHANNEL_EVENT_TYPE &event, std::any data) { + try { + if (event == COMMUNICATION_CHANNEL_EVENT_TYPE::CONNECTED) { + std::string sender = std::any_cast(data); + add_client(sender); + } else if (event == COMMUNICATION_CHANNEL_EVENT_TYPE::DISCONNECTED) { + std::string sender = std::any_cast(data); + remove_client(sender); + } + } catch (const std::bad_any_cast &e) { + LOGE("Failed to cast message data: %s", e.what()); + } +} + +int ClientManager::initialize() { + return MMI_ERROR_NONE; +} + +int ClientManager::deinitialize() { + for (auto it = m_clients.begin(); it != m_clients.end(); it++) { + delete it->second; + } + m_clients.clear(); + + return MMI_ERROR_NONE; +} + +Client* ClientManager::add_client(std::string sender) { + LOGI("Add client(%s)\n", sender.c_str()); + + Client *client = new Client(sender); + if (!client) { + LOGE("Failed to allocate memory for client !\n"); + return nullptr; + } + + m_clients.insert(std::pair(sender, client)); + + return client; +} + +Client* ClientManager::get_client(std::string sender) { + auto it = m_clients.find(sender); + + if (it == m_clients.end()) { + LOGE("Failed to find client info from sender(%s)\n", sender.c_str()); + return nullptr; + } + + return it->second; +} + +int ClientManager::remove_client(std::string sender) { + LOGI("Remove client(%s)\n", sender.c_str()); + + auto it = m_clients.find(sender); + + if (it == m_clients.end()) { + LOGE("Failed to find client info from sender(%s)\n", sender.c_str()); + return MMI_ERROR_INVALID_PARAMETER; + } + + delete it->second; + m_clients.erase(it); + + return MMI_ERROR_NONE; +} + + +int ClientManager::set_client_uid(Client *client, uid_t uid) { + if (!client) { + LOGE("client is NULL\n"); + return MMI_ERROR_INVALID_PARAMETER; + } + + client->set_uid(uid); + + return MMI_ERROR_NONE; +} + +uid_t ClientManager::get_client_uid(Client *client) { + uid_t uid = 0; + + if (!client) { + LOGE("client is NULL\n"); + return uid; + } + + uid = client->get_uid(); + + return uid; +} + +int ClientManager::set_client_pid(Client *client, pid_t pid) { + if (!client) { + LOGE("client is NULL\n"); + return MMI_ERROR_INVALID_PARAMETER;; + } + + client->set_pid(pid); + + return MMI_ERROR_NONE; +} + +pid_t ClientManager::get_client_pid(Client *client) { + pid_t pid = 0; + + if (!client) { + LOGE("client is NULL\n"); + return pid; + } + + pid = client->get_pid(); + + return pid; +} + +std::string ClientManager::get_client_sender(Client *client) { + std::string sender; + + if (!client) { + LOGE("client is NULL\n"); + return sender; + } + + sender = client->get_sender(); + + return sender; +} + +} // namespace mmi diff --git a/src/mmi-manager/mmi-client.h b/src/mmi-manager/mmi-client.h new file mode 100644 index 0000000..50e1c1e --- /dev/null +++ b/src/mmi-manager/mmi-client.h @@ -0,0 +1,103 @@ +/* +* Copyright © 2021 Samsung Electronics co., Ltd. All Rights Reserved. +* +* Permission is hereby granted, free of charge, to any person obtaining a +* copy of this software and associated documentation files (the "Software"), +* to deal in the Software without restriction, including without limitation +* the rights to use, copy, modify, merge, publish, distribute, sublicense, +* and/or sell copies of the Software, and to permit persons to whom the +* Software is furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice (including the next +* paragraph) shall be included in all copies or substantial portions of the +* Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +* DEALINGS IN THE SOFTWARE. +*/ + +#ifndef __MMI_CLIENT_H__ +#define __MMI_CLIENT_H__ + +#include + +#include +#include + +#include + +#include "mmi-common.h" +#include "mmi-communication-channel.h" +#include "mmi_stub.h" + +namespace mmi { + +using namespace communication; + +class Client { +public: + Client(std::string sender) : m_sender(sender) { + } + virtual ~Client() { + } + + std::string get_sender() { + return m_sender; + } + + uid_t get_uid() { + return m_uid; + } + pid_t get_pid() { + return m_pid; + } + + void set_uid(uid_t uid) { + m_uid = uid; + } + void set_pid(pid_t pid) { + m_pid = pid; + } + +private: + std::string m_sender; + uid_t m_uid{0}; + pid_t m_pid{0}; +}; + +class ClientManager : + public SimpleEventObserver { +public: + ClientManager(); + virtual ~ClientManager(); + + void on_observer_event( + const COMMUNICATION_CHANNEL_EVENT_TYPE &event, std::any data) override; + + int initialize(); + int deinitialize(); + + Client* add_client(std::string sender); + Client* get_client(std::string sender); + int remove_client(std::string sender); + + int set_client_uid(Client *client, uid_t uid); + uid_t get_client_uid(Client *client); + + int set_client_pid(Client *client, pid_t pid); + pid_t get_client_pid(Client *client); + + std::string get_client_sender(Client *client); + +private: + std::map m_clients; +}; + +} // namespace mmi + +#endif //__MMI_CLIENT_H__ diff --git a/src/mmi-manager/mmi-common.cpp b/src/mmi-manager/mmi-common.cpp new file mode 100644 index 0000000..139d4b9 --- /dev/null +++ b/src/mmi-manager/mmi-common.cpp @@ -0,0 +1,25 @@ +/* +* Copyright © 2021 Samsung Electronics co., Ltd. All Rights Reserved. +* +* Permission is hereby granted, free of charge, to any person obtaining a +* copy of this software and associated documentation files (the "Software"), +* to deal in the Software without restriction, including without limitation +* the rights to use, copy, modify, merge, publish, distribute, sublicense, +* and/or sell copies of the Software, and to permit persons to whom the +* Software is furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice (including the next +* paragraph) shall be included in all copies or substantial portions of the +* Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +* DEALINGS IN THE SOFTWARE. +*/ + +#include "mmi-common.h" + diff --git a/src/mmi-manager/mmi-common.h b/src/mmi-manager/mmi-common.h new file mode 100644 index 0000000..3fc6ad1 --- /dev/null +++ b/src/mmi-manager/mmi-common.h @@ -0,0 +1,34 @@ +/* +* Copyright © 2021 Samsung Electronics co., Ltd. All Rights Reserved. +* +* Permission is hereby granted, free of charge, to any person obtaining a +* copy of this software and associated documentation files (the "Software"), +* to deal in the Software without restriction, including without limitation +* the rights to use, copy, modify, merge, publish, distribute, sublicense, +* and/or sell copies of the Software, and to permit persons to whom the +* Software is furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice (including the next +* paragraph) shall be included in all copies or substantial portions of the +* Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +* DEALINGS IN THE SOFTWARE. +*/ + +#ifndef __MMI_COMMON_H__ +#define __MMI_COMMON_H__ + +#include +#include + +#ifndef MMI_API +#define MMI_API __attribute__ ((visibility("default"))) +#endif + +#endif //__MMI_COMMON_H__ diff --git a/src/mmi-manager/mmi-data-gateway.cpp b/src/mmi-manager/mmi-data-gateway.cpp new file mode 100644 index 0000000..dc3eccf --- /dev/null +++ b/src/mmi-manager/mmi-data-gateway.cpp @@ -0,0 +1,71 @@ +/* + * Copyright © 2023 Samsung Electronics co., Ltd. All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +#include "mmi-data-gateway.h" + +#include "mmi-manager-log.h" + +namespace mmi { + +DataGateway::DataGateway(std::string name) + : m_name(name) { + _D("Data Gateway is created: %s", m_name.c_str()); +} + +DataGateway::~DataGateway() { + _D("Data Gateway is destroyed: %s", m_name.c_str()); +} + +bool DataGateway::initialize() { + _D("Data Gateway is initialized: %s", m_name.c_str()); + return true; +} + +bool DataGateway::deinitialize() { + _D("Data Gateway is deinitialized: %s", m_name.c_str()); + return true; +} + +bool DataGateway::set_name(std::string name) { + m_name = name; + return true; +} + +bool DataGateway::set_callback(mmi_data_transfer_callback callback) { + _D("[DataGateway] set data transfer callback"); + m_data_transfer_callback = callback; + return true; +} + +void DataGateway::send_data(mmi_data_h data) { + _D("[DataGateway] Send Data: %s", m_name.c_str()); + + if (m_data_transfer_callback) { + m_data_transfer_callback(data, m_name); + } + else { + _E("Data Gateway is not set callback: %s", m_name.c_str()); + } +} + +} // namespace mmi \ No newline at end of file diff --git a/src/mmi-manager/mmi-data-gateway.h b/src/mmi-manager/mmi-data-gateway.h new file mode 100644 index 0000000..ffc0f5d --- /dev/null +++ b/src/mmi-manager/mmi-data-gateway.h @@ -0,0 +1,56 @@ +/* +* Copyright © 2023 Samsung Electronics co., Ltd. All Rights Reserved. +* +* Permission is hereby granted, free of charge, to any person obtaining a +* copy of this software and associated documentation files (the "Software"), +* to deal in the Software without restriction, including without limitation +* the rights to use, copy, modify, merge, publish, distribute, sublicense, +* and/or sell copies of the Software, and to permit persons to whom the +* Software is furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice (including the next +* paragraph) shall be included in all copies or substantial portions of the +* Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +* DEALINGS IN THE SOFTWARE. +*/ + +#ifndef __MMI_DATA_GATEWAY_H__ +#define __MMI_DATA_GATEWAY_H__ + +#include "mmi-data.h" + +#include +#include + +namespace mmi { + +using mmi_data_transfer_callback = std::function; + +class DataGateway { +public: + DataGateway(std::string name); + virtual ~DataGateway(); + + bool initialize(); + bool deinitialize(); + + bool set_name(std::string name); + bool set_callback(mmi_data_transfer_callback callback); + + void send_data(mmi_data_h data); + +private: + std::string m_name; + mmi_data_transfer_callback m_data_transfer_callback; +}; + +} // namesapce mmi + +#endif // __MMI_DATA_GATEWAY_H__ \ No newline at end of file diff --git a/src/mmi-manager/mmi-ipc-tidl.cpp b/src/mmi-manager/mmi-ipc-tidl.cpp new file mode 100644 index 0000000..a3f2325 --- /dev/null +++ b/src/mmi-manager/mmi-ipc-tidl.cpp @@ -0,0 +1,340 @@ +/* +* Copyright © 2021 Samsung Electronics co., Ltd. All Rights Reserved. +* +* Permission is hereby granted, free of charge, to any person obtaining a +* copy of this software and associated documentation files (the "Software"), +* to deal in the Software without restriction, including without limitation +* the rights to use, copy, modify, merge, publish, distribute, sublicense, +* and/or sell copies of the Software, and to permit persons to whom the +* Software is furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice (including the next +* paragraph) shall be included in all copies or substantial portions of the +* Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +* DEALINGS IN THE SOFTWARE. +*/ + +#include "mmi-ipc-tidl.h" + +#include "mmi-manager-log.h" +#include "mmi-client.h" +#include "mmi-common.h" + +#include + +#include +#include +#include + +namespace mmi { + +namespace communication { + +CommunicationChannelTIDL::CommunicationChannelTIDL() { +} + +CommunicationChannelTIDL::~CommunicationChannelTIDL() { +} + +int CommunicationChannelTIDL::connect() { + int r; + LOGD("mmi_api_handler_initialize"); + + if (m_initialized) { + LOGE("Already initialized"); + return MMI_ERROR_NONE; + } + + r = rpc_port_register_proc_info(m_stub_appid.c_str(), nullptr); + if (r != RPC_PORT_ERROR_NONE) { + LOGE("Failed to register proc info ! (error:%d)", r); + return MMI_ERROR_OPERATION_FAILED; + } + + rpc_port_stub_mmi_callback_s callback = { + on_create, + on_terminate, + workflow_instance_create, + workflow_instance_destroy, + workflow_instance_set_attribute, + workflow_instance_activate, + workflow_instance_deactivate, + workflow_instance_register_result_callback, + }; + + r = rpc_port_stub_mmi_register(&callback, this); + if (r != RPC_PORT_ERROR_NONE) { + LOGE("Failed to register callbacks (error:%d)", r); + return MMI_ERROR_OPERATION_FAILED; + } + + m_initialized = true; + return MMI_ERROR_NONE; +} + +int CommunicationChannelTIDL::disconnect() { + if (!m_initialized) + return MMI_ERROR_OPERATION_FAILED; + + clear_invoke_callbacks(); + + rpc_port_stub_mmi_unregister(); + rpc_port_deregister_proc_info(); + + m_initialized = false; + + return MMI_ERROR_NONE; +} + +int CommunicationChannelTIDL::send(communication::Message *message) { + return 0; +} + +static bool get_sender_string(rpc_port_stub_mmi_context_h context, + CommunicationChannelTIDL *channel, std::string &sender) { + char *buf = nullptr; + + //int res = rpc_port_stub_mmi_context_get_sender(context, &buf); + int res = rpc_port_stub_mmi_context_get_instance(context, &buf); + if (res != MMI_ERROR_NONE || !buf) { + LOGE("Failed to get sender from context ! (error:%d)", res); + free(buf); + return false; + } + + sender = std::string{buf}; + free(buf); + buf = nullptr; + + return true; +} + +static int notify_connection_changes(CommunicationChannelTIDL *channel, bool connected, std::string sender) { + if (nullptr == channel) { + LOGE("Failed to get channel information !"); + return MMI_ERROR_INVALID_PARAMETER; + } + + channel->notify_observers( + connected ? + COMMUNICATION_CHANNEL_EVENT_TYPE::CONNECTED : + COMMUNICATION_CHANNEL_EVENT_TYPE::DISCONNECTED, + sender); + + return MMI_ERROR_NONE; +} + +static int notify_message_received(CommunicationChannelTIDL *channel, Message *message) { + if (nullptr == channel) { + LOGE("Failed to get channel information !"); + return MMI_ERROR_INVALID_PARAMETER; + } + + channel->notify_observers( + COMMUNICATION_CHANNEL_EVENT_TYPE::MESSAGE_RECEIVED, message); + + return MMI_ERROR_NONE; +} + +void CommunicationChannelTIDL::on_create(rpc_port_stub_mmi_context_h context, void *user_data) { + CommunicationChannelTIDL *channel = static_cast(user_data); + + std::string sender; + if (!get_sender_string(context, channel, sender)) + return; + + LOGI("Client(%s) has been connected !", sender.c_str()); + + //TODO: set additional information of client + notify_connection_changes(channel, true, sender); +} + +void CommunicationChannelTIDL::on_terminate(rpc_port_stub_mmi_context_h context, void *user_data) { + CommunicationChannelTIDL *channel = static_cast(user_data); + + std::string sender; + if (!get_sender_string(context, channel, sender)) + return; + + LOGI("Client(%s) has been disconnected !", sender.c_str()); + + channel->clear_invoke_callbacks(); + + notify_connection_changes(channel, false, sender); +} + +int CommunicationChannelTIDL::workflow_instance_create(rpc_port_stub_mmi_context_h context, + int local_workflow_instance_id, int workflow_type, void *user_data) { + CommunicationChannelTIDL *channel = static_cast(user_data); + + std::string sender; + if (!get_sender_string(context, channel, sender)) + return MMI_ERROR_INVALID_PARAMETER; + + ClientMessageWorkflowInstanceCreate msg; + msg.sender = sender; + msg.local_workflow_instance_id = local_workflow_instance_id; + msg.workflow_type = static_cast(workflow_type); + + return notify_message_received(channel, static_cast(&msg)); +} + +int CommunicationChannelTIDL::workflow_instance_destroy(rpc_port_stub_mmi_context_h context, + int local_workflow_instance_id, void *user_data) { + CommunicationChannelTIDL *channel = static_cast(user_data); + + std::string sender; + if (!get_sender_string(context, channel, sender)) + return MMI_ERROR_INVALID_PARAMETER; + + ClientMessageWorkflowInstanceDestroy msg; + msg.sender = sender; + msg.local_workflow_instance_id = local_workflow_instance_id; + + channel->remove_invoke_callbacks(sender, local_workflow_instance_id); + + return notify_message_received(channel, static_cast(&msg)); +} + +int CommunicationChannelTIDL::workflow_instance_set_attribute(rpc_port_stub_mmi_context_h context, + int local_workflow_instance_id, bundle *attribute, void *user_data) { + CommunicationChannelTIDL *channel = static_cast(user_data); + + std::string sender; + if (!get_sender_string(context, channel, sender)) + return MMI_ERROR_INVALID_PARAMETER; + + mmi_attribute_h restored_attribute; + + unsigned char *bytes = nullptr; + size_t returned_size = 0; + + if (BUNDLE_ERROR_NONE != bundle_get_byte(attribute, "data", (void**)&bytes, &returned_size)) { + _E("Bundle does not contain 'data' field"); + return MMI_ERROR_OPERATION_FAILED; + } + if (nullptr == bytes || 0 == returned_size) { + _E("Bundle does not contain 'data' field"); + return MMI_ERROR_OPERATION_FAILED; + } + + if (MMI_ERROR_NONE != mmi_attribute_from_bytes(bytes, returned_size, &restored_attribute)) { + _E("Failed to restore attribute from bytes"); + return MMI_ERROR_OPERATION_FAILED; + } + + ClientMessageWorkflowInstanceSetAttribute msg; + msg.sender = sender; + msg.local_workflow_instance_id = local_workflow_instance_id; + msg.attribute = restored_attribute; + + notify_message_received(channel, static_cast(&msg)); + + mmi_attribute_destroy(restored_attribute); + + return MMI_ERROR_NONE; +} + +int CommunicationChannelTIDL::workflow_instance_activate(rpc_port_stub_mmi_context_h context, + int local_workflow_instance_id, void *user_data) { + CommunicationChannelTIDL *channel = static_cast(user_data); + + std::string sender; + if (!get_sender_string(context, channel, sender)) + return MMI_ERROR_INVALID_PARAMETER; + + ClientMessageWorkflowInstanceActivate msg; + msg.sender = sender; + msg.local_workflow_instance_id = local_workflow_instance_id; + + return notify_message_received(channel, static_cast(&msg)); +} + +int CommunicationChannelTIDL::workflow_instance_deactivate(rpc_port_stub_mmi_context_h context, + int local_workflow_instance_id, void *user_data) { + CommunicationChannelTIDL *channel = static_cast(user_data); + + std::string sender; + if (!get_sender_string(context, channel, sender)) + return MMI_ERROR_INVALID_PARAMETER; + + ClientMessageWorkflowInstanceDeactivate msg; + msg.sender = sender; + msg.local_workflow_instance_id = local_workflow_instance_id; + + return notify_message_received(channel, static_cast(&msg)); +} + +int CommunicationChannelTIDL::workflow_instance_register_result_callback(rpc_port_stub_mmi_context_h context, + int local_workflow_instance_id, rpc_port_stub_mmi_result_cb_h callback, void *user_data) { + CommunicationChannelTIDL *channel = static_cast(user_data); + + std::string sender; + if (!get_sender_string(context, channel, sender)) + return MMI_ERROR_INVALID_PARAMETER; + + rpc_port_stub_mmi_result_cb_h result_cb_h = nullptr; + int ret = rpc_port_stub_mmi_result_cb_clone(callback, &result_cb_h); + + if (RPC_PORT_ERROR_NONE != ret) { + LOGE("Fail to clone invoke callback(%d)", ret); + rpc_port_stub_mmi_result_cb_destroy(result_cb_h); + return MMI_ERROR_OPERATION_FAILED; + } + channel->set_invoke_callbacks(sender, local_workflow_instance_id, result_cb_h); + + return MMI_ERROR_NONE; +} + +void CommunicationChannelTIDL::on_observer_event( + const WORKFLOW_OUTPUT_EVENT_TYPE &event, std::any data) { + try { + WorkflowOutputEventOutputGenerated *output_generated = std::any_cast(data); + + rpc_port_stub_mmi_result_cb_h callback = get_invoke_callbacks(output_generated->client_id, output_generated->local_workflow_instance_id); + + std::string source_name = output_generated->source_name; + bundle *data_b = reinterpret_cast(output_generated->data); + + int ret = rpc_port_stub_mmi_result_cb_invoke(callback, source_name.c_str(), data_b); + if (ret != RPC_PORT_ERROR_NONE) { + LOGE("Failed to send result to client !"); + } + } catch (const std::bad_any_cast &e) { + LOGE("Failed to cast message data: %s", e.what()); + } +} + +void CommunicationChannelTIDL::set_invoke_callbacks(std::string client_id, int local_workflow_instance_id, rpc_port_stub_mmi_result_cb_h callback) { + std::string key = client_id + "_" + std::to_string(local_workflow_instance_id); + m_invoke_callbacks[key] = callback; +} + +void CommunicationChannelTIDL::remove_invoke_callbacks(std::string client_id, int local_workflow_instance_id) { + std::string key = client_id + "_" + std::to_string(local_workflow_instance_id); + rpc_port_stub_mmi_result_cb_destroy(m_invoke_callbacks[key]); + m_invoke_callbacks.erase(key); +} + +rpc_port_stub_mmi_result_cb_h CommunicationChannelTIDL::get_invoke_callbacks(std::string client_id, int local_workflow_instance_id) { + std::string key = client_id + "_" + std::to_string(local_workflow_instance_id); + return m_invoke_callbacks[key]; +} + +void CommunicationChannelTIDL::clear_invoke_callbacks() { + for (auto &it : m_invoke_callbacks) { + rpc_port_stub_mmi_result_cb_destroy(it.second); + } + m_invoke_callbacks.clear(); +} + +} // namespace communication +// +} // namespace mmi diff --git a/src/mmi-manager/mmi-ipc-tidl.h b/src/mmi-manager/mmi-ipc-tidl.h new file mode 100644 index 0000000..9117ece --- /dev/null +++ b/src/mmi-manager/mmi-ipc-tidl.h @@ -0,0 +1,81 @@ +/* +* Copyright © 2021 Samsung Electronics co., Ltd. All Rights Reserved. +* +* Permission is hereby granted, free of charge, to any person obtaining a +* copy of this software and associated documentation files (the "Software"), +* to deal in the Software without restriction, including without limitation +* the rights to use, copy, modify, merge, publish, distribute, sublicense, +* and/or sell copies of the Software, and to permit persons to whom the +* Software is furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice (including the next +* paragraph) shall be included in all copies or substantial portions of the +* Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +* DEALINGS IN THE SOFTWARE. +*/ + +#ifndef __MMI_IPC_TIDL_H__ +#define __MMI_IPC_TIDL_H__ + +#include "mmi_stub.h" +#include "mmi-communication-channel.h" +#include "mmi-workflow-output-event.h" + +#include +namespace mmi { + +namespace communication { + +class CommunicationChannelTIDL : public CommunicationChannelManager { +public: + CommunicationChannelTIDL(); + ~CommunicationChannelTIDL(); + + int connect() override; + int disconnect() override; + int send(communication::Message *message) override; + + void on_observer_event( + const WORKFLOW_OUTPUT_EVENT_TYPE &event, std::any data) override; + +private: + static void on_create(rpc_port_stub_mmi_context_h context, void *user_data); + static void on_terminate(rpc_port_stub_mmi_context_h context, void *user_data); + + static int workflow_instance_create(rpc_port_stub_mmi_context_h context, + int local_workflow_instance_id, int workflow_type, void *user_data); + static int workflow_instance_destroy(rpc_port_stub_mmi_context_h context, + int local_workflow_instance_id, void *user_data); + static int workflow_instance_set_attribute(rpc_port_stub_mmi_context_h context, + int workflow_instance, bundle *attribute, void *user_data); + static int workflow_instance_activate(rpc_port_stub_mmi_context_h context, + int local_workflow_instance_id, void *user_data); + static int workflow_instance_deactivate(rpc_port_stub_mmi_context_h context, + int local_workflow_instance_id, void *user_data); + static int workflow_instance_register_result_callback(rpc_port_stub_mmi_context_h context, + int local_workflow_instance_id, rpc_port_stub_mmi_result_cb_h callback, void *user_data); + + void set_invoke_callbacks(std::string appid, int local_workflow_instance_id, + rpc_port_stub_mmi_result_cb_h callback); + rpc_port_stub_mmi_result_cb_h get_invoke_callbacks(std::string appid, int local_workflow_instance_id); + void remove_invoke_callbacks(std::string appid, int local_workflow_instance_id); + void clear_invoke_callbacks(); + +private: + bool m_initialized{false}; + const std::string m_stub_appid{"mmi-manager"}; + std::map m_invoke_callbacks; +}; + +} // namespace communication + +} // namespace mmi + +#endif //__MMI_IPC_TIDL_H__ diff --git a/src/mmi-manager/mmi-manager-log.h b/src/mmi-manager/mmi-manager-log.h new file mode 100644 index 0000000..51afe9c --- /dev/null +++ b/src/mmi-manager/mmi-manager-log.h @@ -0,0 +1,50 @@ +/* +* Copyright © 2021 Samsung Electronics co., Ltd. All Rights Reserved. +* +* Permission is hereby granted, free of charge, to any person obtaining a +* copy of this software and associated documentation files (the "Software"), +* to deal in the Software without restriction, including without limitation +* the rights to use, copy, modify, merge, publish, distribute, sublicense, +* and/or sell copies of the Software, and to permit persons to whom the +* Software is furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice (including the next +* paragraph) shall be included in all copies or substantial portions of the +* Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +* DEALINGS IN THE SOFTWARE. +*/ + +#ifndef __MMI_MANAGER_LOG_H__ +#define __MMI_MANAGER_LOG_H__ + +#include + +#ifdef LOG_TAG +#undef LOG_TAG +#endif +#define LOG_TAG "MMIMGR" + +#ifndef _E +#define _E LOGE +#endif + +#ifndef _D +#define _D LOGD +#endif + +#ifndef _I +#define _I LOGI +#endif + +#ifndef _W +#define _W LOGW +#endif + +#endif // __MMI_MANAGER_LOG_H__ diff --git a/src/mmi-manager/mmi-manager.cpp b/src/mmi-manager/mmi-manager.cpp new file mode 100644 index 0000000..f23c043 --- /dev/null +++ b/src/mmi-manager/mmi-manager.cpp @@ -0,0 +1,136 @@ +/* +* Copyright © 2021 Samsung Electronics co., Ltd. All Rights Reserved. +* +* Permission is hereby granted, free of charge, to any person obtaining a +* copy of this software and associated documentation files (the "Software"), +* to deal in the Software without restriction, including without limitation +* the rights to use, copy, modify, merge, publish, distribute, sublicense, +* and/or sell copies of the Software, and to permit persons to whom the +* Software is furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice (including the next +* paragraph) shall be included in all copies or substantial portions of the +* Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +* DEALINGS IN THE SOFTWARE. +*/ + +#include "mmi-manager.h" + +namespace mmi { + +Manager::Manager( + std::shared_ptr communication_channel_factory, + std::shared_ptr plugin_module_proxy_factory) : + m_communication_channel_factory(communication_channel_factory), + m_plugin_module_proxy_factory(plugin_module_proxy_factory) { +} + +Manager::~Manager() { +} + +int Manager::initialize() { + if (nullptr == m_communication_channel_factory) { + return MMI_ERROR_OPERATION_FAILED; + } + + m_plugin_module_registry = std::make_shared(); + if (nullptr == m_plugin_module_registry) { + return MMI_ERROR_OPERATION_FAILED; + } + + m_client_manager = std::make_shared(); + + m_port_instance_manager = std::make_shared(); + + m_node_prototype_manager = std::make_shared(); + m_node_instance_manager = std::make_shared(); + if (nullptr == m_node_instance_manager) { + return MMI_ERROR_OPERATION_FAILED; + } + + m_node_instance_manager->set_plugin_module_proxy_provider(m_plugin_module_registry); + m_node_instance_manager->set_node_prototype_store(m_node_prototype_manager); + m_node_instance_manager->set_port_instance_manager(m_port_instance_manager); + + m_workflow_prototype_manager = std::make_shared(); + m_workflow_instance_manager = std::make_shared(); + if (nullptr == m_workflow_instance_manager) { + return MMI_ERROR_OPERATION_FAILED; + } + + m_workflow_instance_manager->set_workflow_prototype_store(m_workflow_prototype_manager); + m_workflow_instance_manager->set_plugin_module_proxy_provider(m_plugin_module_registry); + m_workflow_instance_manager->set_node_instance_manager(m_node_instance_manager); + m_workflow_instance_manager->add_observer(this); + + m_communication_channel = m_communication_channel_factory->create_channel(); + if (m_communication_channel) { + m_communication_channel->add_observer(m_client_manager.get()); + m_communication_channel->add_observer(m_workflow_instance_manager.get()); + + m_communication_channel->connect(); + } else { + return MMI_ERROR_OPERATION_FAILED; + } + + m_plugin_module_proxy_factory->add_observer(m_node_instance_manager.get()); + + m_plugin_module_registry->set_plugin_module_proxy_factory(m_plugin_module_proxy_factory); + m_plugin_module_registry->set_node_prototype_store(m_node_prototype_manager); + m_plugin_module_registry->set_workflow_prototype_store(m_workflow_prototype_manager); + + m_plugin_module_registry->initialize(); + + return MMI_ERROR_NONE; +} + +int Manager::deinitialize() { + if (m_communication_channel) { + m_communication_channel->disconnect(); + + m_communication_channel->remove_observer(m_client_manager.get()); + m_communication_channel->remove_observer(m_workflow_instance_manager.get()); + } + + m_communication_channel.reset(); + m_communication_channel_factory.reset(); + + m_plugin_module_proxy_factory.reset(); + + m_workflow_instance_manager.reset(); + m_workflow_prototype_manager.reset(); + + m_node_instance_manager.reset(); + + m_port_instance_manager.reset(); + + m_client_manager.reset(); + + if (m_plugin_module_registry) { + m_plugin_module_registry->deinitialize(); + } + m_plugin_module_registry.reset(); + + return MMI_ERROR_NONE; +} + +void Manager::on_observer_event( + const WORKFLOW_EVENT_TYPE &event, std::any data) { + try { + WorkflowEventWorkflowCreated *workflow_created = std::any_cast(data); + std::shared_ptr workflow_instance = std::any_cast>(workflow_created->workflow_instance); + workflow_instance->add_observer(m_communication_channel.get()); + } catch (const std::bad_any_cast &e) { + LOGE("Failed to cast message data: %s", e.what()); + } +} + + +} // namespace mmi diff --git a/src/mmi-manager/mmi-manager.h b/src/mmi-manager/mmi-manager.h new file mode 100644 index 0000000..610159a --- /dev/null +++ b/src/mmi-manager/mmi-manager.h @@ -0,0 +1,75 @@ +/* +* Copyright © 2021 Samsung Electronics co., Ltd. All Rights Reserved. +* +* Permission is hereby granted, free of charge, to any person obtaining a +* copy of this software and associated documentation files (the "Software"), +* to deal in the Software without restriction, including without limitation +* the rights to use, copy, modify, merge, publish, distribute, sublicense, +* and/or sell copies of the Software, and to permit persons to whom the +* Software is furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice (including the next +* paragraph) shall be included in all copies or substantial portions of the +* Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +* DEALINGS IN THE SOFTWARE. +*/ + +#ifndef __MMI_MANAGER_H__ +#define __MMI_MANAGER_H__ + +#include + +#include "mmi-client.h" +#include "mmi-communication-channel.h" +#include "mmi-plugin-module-registry.h" +#include "mmi-node-instance-manager.h" +#include "mmi-node-prototype-manager.h" +#include "mmi-port-instance-manager.h" +#include "mmi-workflow-instance-manager.h" +#include "mmi-workflow-prototype-manager.h" +#include "mmi-workflow-event.h" + +namespace mmi { + +using namespace mmi::communication; + +class Manager : SimpleEventObserver { +public: + Manager(std::shared_ptr communication_channel_factory, + std::shared_ptr plugin_module_proxy_factory); + virtual ~Manager(); + + int initialize(); + int deinitialize(); + + void on_observer_event( + const WORKFLOW_EVENT_TYPE &event, std::any data) override; + +private: + std::shared_ptr m_communication_channel_factory; + std::shared_ptr m_communication_channel; + + std::shared_ptr m_plugin_module_proxy_factory; + std::shared_ptr m_plugin_module_registry; + + std::shared_ptr m_client_manager; + + std::shared_ptr m_port_instance_manager; + + std::shared_ptr m_node_prototype_manager; + std::shared_ptr m_node_instance_manager; + + std::shared_ptr m_workflow_prototype_manager; + std::shared_ptr m_workflow_instance_manager; +}; + +} // namespace mmi + +#endif //__MMI_MANAGER_H__ diff --git a/src/mmi-manager/mmi-manager.xml b/src/mmi-manager/mmi-manager.xml new file mode 100644 index 0000000..9ff40f9 --- /dev/null +++ b/src/mmi-manager/mmi-manager.xml @@ -0,0 +1,15 @@ + + + + DaYe Lee + autofilld + + + + + http://tizen.org/privilege/inputgenerator + http://tizen.org/privilege/datasharing + http://tizen.org/privilege/externalstroage + http://tizen.org/privilege/mediastorage + + diff --git a/src/mmi-manager/mmi-node-instance-manager.cpp b/src/mmi-manager/mmi-node-instance-manager.cpp new file mode 100644 index 0000000..ab07751 --- /dev/null +++ b/src/mmi-manager/mmi-node-instance-manager.cpp @@ -0,0 +1,173 @@ +/* + * Copyright © 2021 Samsung Electronics co., Ltd. All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +#include "mmi-node-instance-manager.h" + +#include "mmi-manager-log.h" + +#include + +namespace mmi { + +NodeInstanceManager::NodeInstanceManager() { +} + +NodeInstanceManager::~NodeInstanceManager() { +} + +std::shared_ptr NodeInstanceManager::create_node_instance( + mmi_standard_node_type_e node_type, + mmi_standard_node_sub_type_e node_sub_type) { + std::shared_ptr ret; + + ret = std::make_shared(node_type, node_sub_type); + if (ret == nullptr) { + _E("Failed to create node instance"); + return ret; + } + + ret->set_node_prototype_store(m_prototype_store); + ret->set_plugin_module_proxy_provider(m_plugin_module_proxy_provider); + ret->set_port_instance_manager(m_port_instance_manager); + + ret->initialize(); + + m_node_instances.push_back(ret); + + return ret; +} + +bool NodeInstanceManager::destroy_node_instance( + std::shared_ptr node_instance) { + if (node_instance) { + node_instance->deinitialize(); + } + + auto &instances = m_node_instances; + + auto it = std::find(instances.begin(), instances.end(), node_instance); + if (it == instances.end()) { + _E("Node instance is not found"); + return false; + } + + instances.erase(it); + + return true; +} + +bool NodeInstanceManager::link_node_instances( + std::shared_ptr from_node_instance, + std::shared_ptr to_node_instance, + std::optional from_port_name, + std::optional to_port_name) { + if (nullptr == from_node_instance || nullptr == to_node_instance) { + LOGE("Failed to find node instances : from(%p) to(%p)", + from_node_instance.get(), to_node_instance.get()); + return false; + } + + if (false == from_port_name.has_value() || false == to_port_name.has_value() ) { + LOGE("Failed to find port names : from(%s) to(%s)", + from_port_name.has_value() ? from_port_name.value().c_str() : "null", + to_port_name.has_value() ? to_port_name.value().c_str() : "null"); + return false; + } + + std::shared_ptr from_port_instance = + from_node_instance->get_port_instance(from_port_name.value()); + std::shared_ptr to_port_instance = + to_node_instance->get_port_instance(to_port_name.value()); + + if (nullptr == from_port_instance || nullptr == to_port_instance) { + LOGE("Failed to find port instance : %s(%p) %s(%p)", + from_port_name.value().c_str(), from_port_instance.get(), + to_port_name.value().c_str(), to_port_instance.get()); + return false; + } + + LOGD("Linking port instances : [%p:%s] - [%p:%s]", + from_node_instance.get(), from_port_name.value().c_str(), + to_node_instance.get(), to_port_name.value().c_str()); + + m_port_instance_manager->link_port_instances(from_port_instance, to_port_instance); + + return true; +} + +void NodeInstanceManager::initialize() { +} + +void NodeInstanceManager::deinitialize() { +} + +void NodeInstanceManager::set_node_prototype_store( + std::shared_ptr prototype_store) { + m_prototype_store = prototype_store; +} + +void NodeInstanceManager::set_plugin_module_proxy_provider( + std::shared_ptr plugin_module_proxy_provider) { + m_plugin_module_proxy_provider = plugin_module_proxy_provider; +} + +void NodeInstanceManager::set_port_instance_manager( + std::shared_ptr port_instance_manager) { + m_port_instance_manager = port_instance_manager; +} + +void NodeInstanceManager::on_observer_event( + const PLUGIN_MODULE_EVENT_TYPE &event, std::any data) { + try { + if (event == PLUGIN_MODULE_EVENT_TYPE::OUTPUT_GENERATED) { + PluginModuleEventOutputGenerated *output_generated = std::any_cast(data); + if (output_generated) { + PortInstance *port_instance = reinterpret_cast(output_generated->port_instance); + if (port_instance) { + _D("Output generated for %p : %p", port_instance, output_generated->data); + port_instance->on_output_data_generated(output_generated->data); + } + } + } + } catch (const std::bad_any_cast &e) { + LOGE("Failed to cast message data: %s", e.what()); + } +} + +void NodeInstanceManager::on_observer_event( + const PROXY_FACTORY_EVENT_TYPE &event, std::any data) { + try { + if (event == PROXY_FACTORY_EVENT_TYPE::PROXY_CREATED) { + IPluginModuleProxy *proxy = std::any_cast(data); + if (proxy) { + /* Add 'this' observer as a SimpleEventObserver */ + proxy->add_observer(this); + } + } + } catch (const std::bad_any_cast &e) { + LOGE("Failed to cast message data: %s", e.what()); + } +} + +} // namespace mmi + diff --git a/src/mmi-manager/mmi-node-instance-manager.h b/src/mmi-manager/mmi-node-instance-manager.h new file mode 100644 index 0000000..3a6f613 --- /dev/null +++ b/src/mmi-manager/mmi-node-instance-manager.h @@ -0,0 +1,101 @@ +/* +* Copyright © 2023 Samsung Electronics co., Ltd. All Rights Reserved. +* +* Permission is hereby granted, free of charge, to any person obtaining a +* copy of this software and associated documentation files (the "Software"), +* to deal in the Software without restriction, including without limitation +* the rights to use, copy, modify, merge, publish, distribute, sublicense, +* and/or sell copies of the Software, and to permit persons to whom the +* Software is furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice (including the next +* paragraph) shall be included in all copies or substantial portions of the +* Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +* DEALINGS IN THE SOFTWARE. +*/ + +#ifndef __MMI_NODE_INSTANCE_MANAGER_H__ +#define __MMI_NODE_INSTANCE_MANAGER_H__ + +#include "mmi-plugin-module-event.h" +#include "mmi-plugin-module-proxy.h" +#include "mmi-node-instance.h" +#include "mmi-node-prototype-manager.h" +#include "mmi-port-instance-manager.h" + +#include +#include + +namespace mmi { + +class INodeInstanceManager { +public: + INodeInstanceManager() = default; + virtual ~INodeInstanceManager() = default; + + virtual std::shared_ptr create_node_instance( + mmi_standard_node_type_e node_type, + mmi_standard_node_sub_type_e node_sub_type) = 0; + virtual bool destroy_node_instance( + std::shared_ptr node_instance) = 0; + virtual bool link_node_instances( + std::shared_ptr from_node_instance, + std::shared_ptr to_node_instance, + std::optional from_port_name, + std::optional to_port_name) = 0; +}; + +class NodeInstanceManager : + public INodeInstanceManager, + public SimpleEventObserver, + public SimpleEventObserver { +public: + NodeInstanceManager(); + virtual ~NodeInstanceManager(); + + virtual std::shared_ptr create_node_instance( + mmi_standard_node_type_e node_type, + mmi_standard_node_sub_type_e node_sub_type) override; + virtual bool destroy_node_instance( + std::shared_ptr node_instance) override; + virtual bool link_node_instances( + std::shared_ptr from_node_instance, + std::shared_ptr to_node_instance, + std::optional from_port_name, + std::optional to_port_name) override; + + void set_node_prototype_store( + std::shared_ptr prototype_store); + void set_plugin_module_proxy_provider( + std::shared_ptr plugin_module_proxy_provider); + void set_port_instance_manager( + std::shared_ptr port_instance_manager); + + void initialize(); + void deinitialize(); + + void on_observer_event( + const PLUGIN_MODULE_EVENT_TYPE &event, std::any data) override; + void on_observer_event( + const PROXY_FACTORY_EVENT_TYPE &event, std::any data) override; + +protected: + std::shared_ptr m_prototype_store; + std::shared_ptr m_plugin_module_proxy_provider; + std::shared_ptr m_port_instance_manager; + + std::vector> m_node_instances; +}; + +} // namespace mmi + +#endif //__MMI_NODE_INSTANCE_MANAGER_H__ + + diff --git a/src/mmi-manager/mmi-node-instance.cpp b/src/mmi-manager/mmi-node-instance.cpp new file mode 100644 index 0000000..8c4c6db --- /dev/null +++ b/src/mmi-manager/mmi-node-instance.cpp @@ -0,0 +1,300 @@ +/* + * Copyright © 2021 Samsung Electronics co., Ltd. All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +#include "mmi-node-instance.h" + +#include "mmi-manager-log.h" + +namespace mmi { + +NodeInstance::NodeInstance( + mmi_standard_node_type_e node_type, + mmi_standard_node_sub_type_e node_sub_type) + : m_node_type(node_type) + , m_node_sub_type(node_sub_type) { + _D("Node instance is created : %d %d", m_node_type, to_int(m_node_sub_type)); +} + +NodeInstance::~NodeInstance() { + _D("Node instance is destroyed : %d %d", m_node_type, to_int(m_node_sub_type)); +} + +static bool node_type_and_subtype_matches( + mmi_node_h node, + mmi_standard_node_type_e node_type, + mmi_standard_node_sub_type_e node_sub_type) { + mmi_standard_node_type_e type; + mmi_node_get_type(node, &type); + + if (type != node_type) { + return false; + } + + try { + if (node_type == MMI_STANDARD_NODE_TYPE_SOURCE) { + mmi_standard_node_source_type_e source_type; + mmi_standard_node_get_source_type(node, &source_type); + return source_type == std::get(node_sub_type); + } else if (node_type == MMI_STANDARD_NODE_TYPE_PROCESSOR) { + mmi_standard_node_processor_type_e processor_type; + mmi_standard_node_get_processor_type(node, &processor_type); + return processor_type == std::get(node_sub_type); + } else if (node_type == MMI_STANDARD_NODE_TYPE_LOGIC) { + mmi_standard_node_logic_type_e logic_type; + mmi_standard_node_get_logic_type(node, &logic_type); + return logic_type == std::get(node_sub_type); + } else if (node_type == MMI_STANDARD_NODE_TYPE_CONTROLLER) { + mmi_standard_node_controller_type_e controller_type; + mmi_standard_node_get_controller_type(node, &controller_type); + return controller_type == std::get(node_sub_type); + } + } catch (const std::bad_variant_access &e) { + _E("Failed to get node sub type : %s", e.what()); + } + + return false; +} + + + +bool NodeInstance::initialize() { + if (m_prototype_store == nullptr) { + _E("Node prototype store is not set"); + return false; + } + + m_prototype = m_prototype_store->get_node_prototype(m_node_type, m_node_sub_type); + if (m_prototype == nullptr) { + _E("Failed to get node prototype"); + return false; + } + + if (m_plugin_module_proxy_provider == nullptr) { + _E("Plugin module proxy provider is not set"); + return false; + } + + /* Owning this shared pointer guarantees that the callback functions are + * valid until the node instance is destroyed. */ + m_plugin_module_proxy = m_plugin_module_proxy_provider->get_plugin_module_proxy( + m_prototype->get_plugin_module_info()); + + if (m_plugin_module_proxy == nullptr) { + _E("Failed to get plugin module proxy"); + return false; + } + + std::vector ports; + size_t port_count = 0; + + mmi_plugin_module_node_list_s node_list_s = m_plugin_module_proxy->load_node_prototypes(); + for (int i = 0; i < node_list_s.node_count; i++) { + mmi_node_h node = node_list_s.nodes[i]; + if (node_type_and_subtype_matches(node, m_node_type, m_node_sub_type)) { + mmi_node_get_callbacks(node, &m_callbacks); + mmi_node_get_port_count(node, &port_count); + for (size_t i = 0; i < port_count; i++) { + mmi_port_h port; + mmi_node_get_port(node, i, &port); + ports.push_back(port); + } + break; + } + } + + _D("Node instance is initialized for %d %d", + m_node_type, to_int(m_node_sub_type)); + + if (m_port_instance_manager == nullptr) { + _E("Port instance manager is not set"); + return false; + } + + mmi_plugin_module_node_instance_info_s plugin_module_node_instance_info; + plugin_module_node_instance_info.node = static_cast(this); + plugin_module_node_instance_info.port_info_count = 0; + + for (auto &port_info : m_prototype->get_port_infos()) { + std::shared_ptr port_instance = m_port_instance_manager->create_port_instance(); + if (port_instance == nullptr) { + _E("Failed to create port instance"); + return false; + } + for (auto &port : ports) { + char *name = nullptr; + size_t name_length = 0; + mmi_port_get_name(port, &name, &name_length); + + mmi_port_callbacks callbacks; + mmi_port_get_callbacks(port, &callbacks); + + if (strncmp(name, port_info.name.c_str(), name_length) == 0) { + _D("Port instance is initialized for %s, %p %p", port_info.name.c_str(), + callbacks.output_format_requested_cb, + callbacks.input_data_received_cb); + port_instance->set_port_callbacks(callbacks); + break; + } + } + + m_port_instances[port_info.name] = port_instance; + + size_t index = plugin_module_node_instance_info.port_info_count; + plugin_module_node_instance_info.port_infos[index].port = + static_cast(port_instance.get()); + strncpy(plugin_module_node_instance_info.port_infos[index].name, + port_info.name.c_str(), MMI_NAME_MAX_LENGTH - 1); + plugin_module_node_instance_info.port_infos[index].name[MMI_NAME_MAX_LENGTH - 1] = '\0'; + + plugin_module_node_instance_info.port_info_count++; + } + + m_plugin_module_proxy->add_node_instance_info(&plugin_module_node_instance_info); + + if (m_callbacks.initialized_cb != nullptr) { + m_callbacks.initialized_cb(static_cast(this)); + } + + return true; +} + +bool NodeInstance::deinitialize() { + if (m_callbacks.deinitialized_cb != nullptr) { + m_callbacks.deinitialized_cb(static_cast(this)); + } + + m_plugin_module_proxy->remove_node_instance_info(static_cast(this)); + return true; +} + +void NodeInstance::set_node_prototype_store( + std::shared_ptr prototype_store) { + m_prototype_store = prototype_store; +} + +void NodeInstance::set_plugin_module_proxy_provider( + std::shared_ptr plugin_module_proxy_provider) { + m_plugin_module_proxy_provider = plugin_module_proxy_provider; +} + +void NodeInstance::set_port_instance_manager( + std::shared_ptr port_instance_manager) { + m_port_instance_manager = port_instance_manager; +} + +void NodeInstance::add_data_gateway(std::string port_name, + std::shared_ptr gateway) { + try { + auto port = m_port_instances.at(port_name); + port->add_data_gateway(gateway); + } catch (const std::out_of_range &e) { + _E("Port instance is not found for %s, %s", port_name.c_str(), e.what()); + } +} + +mmi_standard_node_type_e NodeInstance::get_node_type() { + return m_node_type; +} + +mmi_standard_node_sub_type_e NodeInstance::get_node_sub_type() { + return m_node_sub_type; +} + +bool NodeInstance::activate() { + m_activated = true; + if (m_suspended) { + _D("Node instance is suspended, so the activated_cb is not called"); + } + if (m_callbacks.activated_cb == nullptr) { + _E("Activate callback is not set"); + return false; + } + m_callbacks.activated_cb(static_cast(this)); + return true; +} + +bool NodeInstance::deactivate() { + m_activated = false; + if (m_suspended) { + _D("Node instance is suspended, so the deactivated_cb is not called"); + } + if (m_callbacks.deactivated_cb == nullptr) { + _E("Deactivate callback is not set"); + return false; + } + m_callbacks.deactivated_cb(static_cast(this)); + return true; +} + +bool NodeInstance::suspend() { + m_suspended = true; + if (m_activated) { + if (m_callbacks.deactivated_cb != nullptr) { + m_callbacks.deactivated_cb(static_cast(this)); + } + } + return true; +} + +bool NodeInstance::resume() { + m_suspended = false; + if (m_activated) { + if (m_callbacks.activated_cb != nullptr) { + m_callbacks.activated_cb(static_cast(this)); + } + } + return true; +} + +std::shared_ptr NodeInstance::get_port_instance(std::string name) { + auto it = m_port_instances.find(name); + if (it == m_port_instances.end()) { + _E("Port instance is not found : %s", name.c_str()); + return nullptr; + } + + return it->second; +} + +std::shared_ptr NodeInstance::get_plugin_module_proxy() { + return m_plugin_module_proxy; +} + +bool NodeInstance::set_attribute(mmi_attribute_h attribute) +{ + char *name = nullptr; + mmi_attribute_get_name(attribute, &name); + _D("Set attribute : %s [%p]", name, attribute); + free(name); + + if (m_callbacks.attribute_set_cb == nullptr) { + _E("Set attribute callback is not set"); + return false; + } + m_callbacks.attribute_set_cb(static_cast(this), attribute); + return true; +} + +} // namespace mmi + + diff --git a/src/mmi-manager/mmi-node-instance.h b/src/mmi-manager/mmi-node-instance.h new file mode 100644 index 0000000..5e7e9bf --- /dev/null +++ b/src/mmi-manager/mmi-node-instance.h @@ -0,0 +1,91 @@ +/* +* Copyright © 2023 Samsung Electronics co., Ltd. All Rights Reserved. +* +* Permission is hereby granted, free of charge, to any person obtaining a +* copy of this software and associated documentation files (the "Software"), +* to deal in the Software without restriction, including without limitation +* the rights to use, copy, modify, merge, publish, distribute, sublicense, +* and/or sell copies of the Software, and to permit persons to whom the +* Software is furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice (including the next +* paragraph) shall be included in all copies or substantial portions of the +* Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +* DEALINGS IN THE SOFTWARE. +*/ + +#ifndef __MMI_NODE_INSTANCE_H__ +#define __MMI_NODE_INSTANCE_H__ + +#include "mmi-node.h" +#include "mmi-node-prototype-manager.h" +#include "mmi-plugin-module-proxy.h" +#include "mmi-port-instance-manager.h" + +#include +#include + +namespace mmi { + +class NodeInstance { +public: + NodeInstance(mmi_standard_node_type_e node_type, + mmi_standard_node_sub_type_e node_sub_type); + virtual ~NodeInstance(); + + bool initialize(); + bool deinitialize(); + + void set_node_prototype_store( + std::shared_ptr prototype_store); + void set_plugin_module_proxy_provider( + std::shared_ptr plugin_module_proxy_provider); + void set_port_instance_manager( + std::shared_ptr port_instance_manager); + + void add_data_gateway(std::string port_name, + std::shared_ptr gateway); + + mmi_standard_node_type_e get_node_type(); + mmi_standard_node_sub_type_e get_node_sub_type(); + + bool activate(); + bool deactivate(); + + bool suspend(); + bool resume(); + + std::shared_ptr get_port_instance(std::string name); + std::shared_ptr get_plugin_module_proxy(); + + bool set_attribute(mmi_attribute_h attribute); +protected: + std::shared_ptr m_prototype_store; + std::shared_ptr m_plugin_module_proxy_provider; + std::shared_ptr m_port_instance_manager; + + std::shared_ptr m_prototype; + std::shared_ptr m_plugin_module_proxy; + std::map> m_port_instances; + + mmi_standard_node_type_e m_node_type; + mmi_standard_node_sub_type_e m_node_sub_type; + + mmi_node_callbacks m_callbacks{nullptr,}; + + bool m_suspended{false}; + bool m_activated{false}; +}; + +} // namespace mmi + +#endif //__MMI_NODE_INSTANCE_H__ + + diff --git a/src/mmi-manager/mmi-node-prototype-manager.cpp b/src/mmi-manager/mmi-node-prototype-manager.cpp new file mode 100644 index 0000000..f86eec9 --- /dev/null +++ b/src/mmi-manager/mmi-node-prototype-manager.cpp @@ -0,0 +1,77 @@ +/* + * Copyright © 2021 Samsung Electronics co., Ltd. All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +#include "mmi-node-prototype-manager.h" + +#include "mmi-manager-log.h" + +namespace mmi { + +NodePrototypeManager::NodePrototypeManager() { +} + +NodePrototypeManager::~NodePrototypeManager() { +} + +bool NodePrototypeManager::add_node_prototype(std::shared_ptr prototype) { + if (!prototype->is_valid()) { + _E("ERROR: invalid node prototype"); + return false; + } + + /* check if we already have a prototype with the same type and sub type */ + for (auto& elem : m_prototypes) { + if (elem->get_type() == prototype->get_type() && + elem->get_sub_type() == prototype->get_sub_type()) { + _E("ERROR: prototype with the same type / sub type already exists"); + return false; + } + } + + m_prototypes.push_back(prototype); + + return true; +} + +std::shared_ptr NodePrototypeManager::get_node_prototype( + mmi_standard_node_type_e type, mmi_standard_node_sub_type_e sub_type) { + std::shared_ptr ret; + + for (auto& elem : m_prototypes) { + if (elem->get_type() == type && elem->get_sub_type() == sub_type) { + ret = elem; + break; + } + } + + return ret; +} + +void NodePrototypeManager::initialize() { +} + +void NodePrototypeManager::deinitialize() { +} + +} // namespace mmi + diff --git a/src/mmi-manager/mmi-node-prototype-manager.h b/src/mmi-manager/mmi-node-prototype-manager.h new file mode 100644 index 0000000..4794c90 --- /dev/null +++ b/src/mmi-manager/mmi-node-prototype-manager.h @@ -0,0 +1,60 @@ +/* +* Copyright © 2023 Samsung Electronics co., Ltd. All Rights Reserved. +* +* Permission is hereby granted, free of charge, to any person obtaining a +* copy of this software and associated documentation files (the "Software"), +* to deal in the Software without restriction, including without limitation +* the rights to use, copy, modify, merge, publish, distribute, sublicense, +* and/or sell copies of the Software, and to permit persons to whom the +* Software is furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice (including the next +* paragraph) shall be included in all copies or substantial portions of the +* Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +* DEALINGS IN THE SOFTWARE. +*/ + +#ifndef __MMI_NODE_PROTOTYPE_MANAGER_H__ +#define __MMI_NODE_PROTOTYPE_MANAGER_H__ + +#include "mmi-node-prototype.h" + +#include +#include + +namespace mmi { + +struct INodePrototypeStore { + virtual bool add_node_prototype(std::shared_ptr prototype) = 0; + virtual std::shared_ptr get_node_prototype( + mmi_standard_node_type_e type, mmi_standard_node_sub_type_e sub_type) = 0; +}; + +class NodePrototypeManager : public INodePrototypeStore { +public: + NodePrototypeManager(); + virtual ~NodePrototypeManager(); + + virtual bool add_node_prototype(std::shared_ptr prototype) override; + virtual std::shared_ptr get_node_prototype( + mmi_standard_node_type_e type, mmi_standard_node_sub_type_e sub_type) override; + + void initialize(); + void deinitialize(); + +protected: + std::vector> m_prototypes; +}; + +} // namespace mmi + +#endif //__MMI_NODE_PROTOTYPE_MANAGER_H__ + + diff --git a/src/mmi-manager/mmi-node-prototype.cpp b/src/mmi-manager/mmi-node-prototype.cpp new file mode 100644 index 0000000..2cb9a77 --- /dev/null +++ b/src/mmi-manager/mmi-node-prototype.cpp @@ -0,0 +1,151 @@ +/* + * Copyright © 2021 Samsung Electronics co., Ltd. All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +#include "mmi-node-prototype.h" + +#include "mmi-manager-log.h" + +namespace mmi { + +NodePrototype::NodePrototype() { +} + +NodePrototype::~NodePrototype() { +} + +bool NodePrototype::is_valid() { + if (!m_type.has_value() || !m_sub_type.has_value()) { + _E("Failed to validate node prototype: type or sub type is empty"); + return false; + } + + bool valid = false; + /* Make sure that the m_sub_type is a valid match for m_type */ + try { + switch (m_type.value()) { + case mmi_standard_node_type_e::MMI_STANDARD_NODE_TYPE_SOURCE: { + auto subclass = + std::get(m_sub_type.value()); + valid = true; + } + break; + case mmi_standard_node_type_e::MMI_STANDARD_NODE_TYPE_PROCESSOR: { + auto subclass = + std::get(m_sub_type.value()); + valid = true; + } + break; + case mmi_standard_node_type_e::MMI_STANDARD_NODE_TYPE_LOGIC: { + auto subclass = + std::get(m_sub_type.value()); + valid = true; + } + break; + case mmi_standard_node_type_e::MMI_STANDARD_NODE_TYPE_CONTROLLER: { + auto subclass = + std::get(m_sub_type.value()); + valid = true; + } + } + } catch (const std::bad_variant_access& e) { + _E("[std::bad_variant_access] Failed to create node prototype: %s", e.what()); + } catch (const std::bad_optional_access& e) { + _E("[std::bad_optional_access] Failed to create node prototype: %s", e.what()); + } + + if (get_plugin_module_info().plugin_module_identifier.empty()) { + _E("Failed to validate node prototype: plugin module info is empty"); + valid = false; + } + + return valid; +} + +void NodePrototype::set_type(mmi_standard_node_type_e type) { + m_type = type; +} + +void NodePrototype::set_sub_type(mmi_standard_node_sub_type_e sub_type) { + m_sub_type = sub_type; +} + +bool NodePrototype::add_port_info(PortInfo &port_info) { + for (auto& port : m_port_infos) { + if (port.name == port_info.name) { + _E("Failed to add port info: port with the same name already exists"); + return false; + } + } + + m_port_infos.push_back(port_info); + return true; +} + +bool NodePrototype::add_attribute_info(AttributeInfo &attribute_info) { + /* check if we already have an attribute with the same name */ + for (auto& attribute : m_attribute_infos) { + if (attribute.name == attribute_info.name) { + _E("Failed to add attribute info: attribute with the same name already exists"); + return false; + } + } + + m_attribute_infos.push_back(attribute_info); + return true; +} + +mmi_standard_node_type_e NodePrototype::get_type() { + mmi_standard_node_type_e ret = MMI_STANDARD_NODE_TYPE_NONE; + if (m_type.has_value()) { + ret = m_type.value(); + } + return ret; +} + +mmi_standard_node_sub_type_e NodePrototype::get_sub_type() { + mmi_standard_node_sub_type_e ret = std::monostate{}; + if (m_sub_type.has_value()) { + ret = m_sub_type.value(); + } + return ret; +} + +std::vector NodePrototype::get_port_infos() { + return m_port_infos; +} + +std::vector NodePrototype::get_attribute_infos() { + return m_attribute_infos; +} + +bool NodePrototype::has_port_with_name(std::string name) { + for (auto& port : m_port_infos) { + if (port.name == name) { + return true; + } + } + return false; +} + +} // namespace mmi + diff --git a/src/mmi-manager/mmi-node-prototype.h b/src/mmi-manager/mmi-node-prototype.h new file mode 100644 index 0000000..9273134 --- /dev/null +++ b/src/mmi-manager/mmi-node-prototype.h @@ -0,0 +1,117 @@ +/* +* Copyright © 2023 Samsung Electronics co., Ltd. All Rights Reserved. +* +* Permission is hereby granted, free of charge, to any person obtaining a +* copy of this software and associated documentation files (the "Software"), +* to deal in the Software without restriction, including without limitation +* the rights to use, copy, modify, merge, publish, distribute, sublicense, +* and/or sell copies of the Software, and to permit persons to whom the +* Software is furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice (including the next +* paragraph) shall be included in all copies or substantial portions of the +* Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +* DEALINGS IN THE SOFTWARE. +*/ + +#ifndef __MMI_NODE_PROTOTYPE_H__ +#define __MMI_NODE_PROTOTYPE_H__ + +#include "mmi-common.h" +#include "mmi-node.h" +#include "mmi-node-source.h" +#include "mmi-node-processor.h" +#include "mmi-node-logic.h" +#include "mmi-node-controller.h" +#include "mmi-plugin-module-proxy.h" + +#include +#include +#include +#include + +namespace mmi { + +typedef std::variant< + std::monostate, + mmi_standard_node_source_type_e, + mmi_standard_node_processor_type_e, + mmi_standard_node_logic_type_e, + mmi_standard_node_controller_type_e + > mmi_standard_node_sub_type_e; + +static int to_int(mmi_standard_node_sub_type_e sub_type) { + try { + return std::visit([](auto&& arg) -> int { + using T = std::decay_t; + if constexpr (std::is_same_v) + return -1; + else + return static_cast(arg); + }, sub_type); + } catch (std::bad_variant_access&) { + return -1; + } +} + +typedef struct { + std::string name; + mmi_port_type_e port_type; + mmi_data_type_e data_type; + std::string data_description; +} PortInfo; + +typedef struct { + std::string name; + mmi_primitive_value_type_e value_type; +} AttributeInfo; + +class NodePrototype { +public: + NodePrototype(); + virtual ~NodePrototype(); + + bool is_valid(); + + void set_plugin_module_info(PluginModuleInfo plugin_module_info) { + m_plugin_module_info = plugin_module_info; + } + PluginModuleInfo get_plugin_module_info() { + return m_plugin_module_info; + } + + void set_type(mmi_standard_node_type_e type); + void set_sub_type(mmi_standard_node_sub_type_e sub_type); + + bool add_port_info(PortInfo &port_info); + bool add_attribute_info(AttributeInfo &attribute_info); + + mmi_standard_node_type_e get_type(); + mmi_standard_node_sub_type_e get_sub_type(); + + std::vector get_port_infos(); + std::vector get_attribute_infos(); + + bool has_port_with_name(std::string name); +private: + PluginModuleInfo m_plugin_module_info; + + std::optional m_type; + std::optional m_sub_type; + + std::vector m_port_infos; + std::vector m_attribute_infos; +}; + +} // namespace mmi + +#endif //__MMI_NODE_PROTOTYPE_H__ + + diff --git a/src/mmi-manager/mmi-plugin-module-proxy.cpp b/src/mmi-manager/mmi-plugin-module-proxy.cpp new file mode 100644 index 0000000..b9682dc --- /dev/null +++ b/src/mmi-manager/mmi-plugin-module-proxy.cpp @@ -0,0 +1,213 @@ +/* + * Copyright © 2023 Samsung Electronics co., Ltd. All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +#include "mmi-plugin-module-proxy.h" +#include "mmi-manager-log.h" + +#include + +namespace mmi { + +static void port_instance_output_handler(mmi_port_instance_h port, mmi_data_h data, void *user_data) { + PluginModuleProxySharedLibrary* proxy = static_cast(user_data); + if (proxy == nullptr) { + _E("Invalid proxy"); + return; + } + + PluginModuleEventOutputGenerated event; + event.port_instance = port; + event.data = data; + proxy->notify_observers(PLUGIN_MODULE_EVENT_TYPE::OUTPUT_GENERATED, &event); +} + +static void switch_node_event_handler(mmi_node_instance_h node, const char *controllee, bool state, void *user_data) { + PluginModuleProxySharedLibrary* proxy = static_cast(user_data); + if (proxy == nullptr) { + _E("Invalid proxy"); + return; + } + if (controllee == nullptr) { + _E("Controllee is null"); + return; + } + + PluginModuleEventSwitchEventEmitted event; + event.node_instance = node; + event.controllee = controllee; + event.state = state; + proxy->notify_observers(PLUGIN_MODULE_EVENT_TYPE::SWITCH_EVENT_EMITTED, &event); +} + +PluginModuleProxySharedLibrary::PluginModuleProxySharedLibrary( + const mmi_plugin_module_identifier plugin_module_identifier) : + m_plugin_module_identifier(plugin_module_identifier) { + mmi_plugin_module_event_handler_info_s module_event_handler_info{nullptr, }; + module_event_handler_info.port_instance_output_handler = port_instance_output_handler; + module_event_handler_info.switch_node_event_handler = switch_node_event_handler; + + /* FIXME : When there are more than one plugin module, the logic should be changed + * since the global event handler will be overwritten by the last plugin module. + * But the overwrite would not cause any problem for now, since the event handler + * will always have the pointer to the static function port_instance_output_handler(). */ + mmi_plugin_storage_set_plugin_module_event_handler(module_event_handler_info, this); + + m_shared_object_handle = dlopen(m_plugin_module_identifier.c_str(), RTLD_NOW); + if (!m_shared_object_handle) { + _E("Failed to load plugin module: %s", dlerror()); + } +} + +PluginModuleProxySharedLibrary::~PluginModuleProxySharedLibrary() { + if (m_shared_object_handle) dlclose(m_shared_object_handle); + m_shared_object_handle = nullptr; +} + +function_pointer PluginModuleProxySharedLibrary::get_function(const std::string& function_name) { + return dlsym(m_shared_object_handle, function_name.c_str()); +} + +mmi_plugin_module_node_list_s PluginModuleProxySharedLibrary::load_node_prototypes() { + mmi_plugin_module_node_list_s node_list; + memset(&node_list, 0, sizeof(mmi_plugin_module_node_list_s)); + + mmi_plugin_storage_set_current_module_identifier(m_plugin_module_identifier.c_str()); + + function_pointer load_node_prototypes = get_function(MMI_PLUGIN_MODULE_GET_NODE_LIST_FUNC_NAME); + if (load_node_prototypes) { + auto func = (mmi_plugin_module_get_node_list_func)load_node_prototypes; + func(); + } else { + _E("No prototype provider function exists for node prototypes : %s", m_plugin_module_identifier.c_str()); + return node_list; + } + + node_list = mmi_plugin_storage_get_node_list(m_plugin_module_identifier.c_str()); + + /* Dump node_list */ + _D("node_list.count: %zu", node_list.node_count); + for (size_t i = 0;i < node_list.node_count;i++) { + mmi_node_h node = node_list.nodes[i]; + mmi_standard_node_type_e type; + mmi_node_get_type(node, &type); + mmi_node_callbacks callbacks; + mmi_node_get_callbacks(node, &callbacks); + size_t port_count; + _D("node_info[%zu].type: %d", i, type); + _D("node_info[%zu].callbacks : %p %p %p %p %p %p", i, + callbacks.initialized_cb, + callbacks.deinitialized_cb, + callbacks.attribute_set_cb, + callbacks.activated_cb, + callbacks.deactivated_cb, + callbacks.signal_received_cb); + mmi_node_get_port_count(node, &port_count); + _D("node_info[%zu].port_list.count: %zu", i, port_count); + for (size_t j = 0;j < port_count;j++) { + mmi_port_h port; + mmi_node_get_port(node, j, &port); + char *name = nullptr; + size_t length = 0; + mmi_port_get_name(port, &name, &length); + mmi_port_type_e port_type; + mmi_port_get_type(port, &port_type); + mmi_data_type_e data_type; + mmi_port_get_data_type(port, &data_type); + mmi_port_callbacks port_callbacks; + mmi_port_get_callbacks(port, &port_callbacks); + _D("node_info[%zu].port_info[%zu].name: %s", i, j, name); + _D("node_info[%zu].port_info[%zu].type: %d", i, j, port_type); + _D("node_info[%zu].port_info[%zu].data_type: %d", i, j, data_type); + _D("node_info[%zu].port_info[%zu].callbacks: %p %p", i, j, + port_callbacks.output_format_requested_cb, + port_callbacks.input_data_received_cb); + } + } + return node_list; +} + +mmi_plugin_module_workflow_list_s PluginModuleProxySharedLibrary::load_workflow_prototypes() { + mmi_plugin_module_workflow_list_s workflow_list; + memset(&workflow_list, 0, sizeof(mmi_plugin_module_workflow_list_s)); + + mmi_plugin_storage_set_current_module_identifier(m_plugin_module_identifier.c_str()); + + function_pointer load_workflow_prototypes = get_function(MMI_PLUGIN_MODULE_GET_WORKFLOW_LIST_FUNC_NAME); + if (load_workflow_prototypes) { + auto func = (mmi_plugin_module_get_workflow_list_func)load_workflow_prototypes; + func(); + } else { + _E("No prototype provider function exists for workflow prototypes : %s", m_plugin_module_identifier.c_str()); + return workflow_list; + } + + workflow_list = mmi_plugin_storage_get_workflow_list(m_plugin_module_identifier.c_str()); + + /* Dump workflow_list */ + _D("workflow_list.count: %zu", workflow_list.workflow_count); + for (size_t i = 0;i < workflow_list.workflow_count;i++) { + mmi_workflow_h workflow = workflow_list.workflows[i]; + mmi_workflow_s *workflow_info = static_cast(workflow); + mmi_standard_workflow_type_e type; + mmi_workflow_get_type(workflow, &type); + _D("workflow_info[%zu].type: %d", i, type); + + _D("workflow_info[%zu].node_info_count: %zu", i, workflow_info->node_info_count); + for (size_t j = 0;j < workflow_info->node_info_count;j++) { + mmi_workflow_node_info_s *node_info = &(workflow_info->node_infos[j]); + mmi_standard_node_type_e type; + mmi_node_get_type(workflow_info->node_infos[j].node, &type); + _D("workflow_info[%zu].node_info[%zu].name: %s", i, j, node_info->name); + _D("workflow_info[%zu].node_info[%zu].type: %d", i, j, type); + } + + _D("workflow_info[%zu].link_info_count: %zu", i, workflow_info->link_info_count); + for (size_t j = 0;j < workflow_info->link_info_count;j++) { + mmi_workflow_link_info_s *link_info = &(workflow_info->link_infos[j]); + _D("workflow_info[%zu].link_info[%zu].from_node_name: %s", i, j, link_info->from_node_name); + _D("workflow_info[%zu].link_info[%zu].from_port_name: %s", i, j, link_info->from_port_name); + _D("workflow_info[%zu].link_info[%zu].to_node_name: %s", i, j, link_info->to_node_name); + _D("workflow_info[%zu].link_info[%zu].to_port_name: %s", i, j, link_info->to_port_name); + } + + _D("workflow_info[%zu].output_assignment_info_count: %zu", i, workflow_info->output_assignment_info_count); + for (size_t j = 0;j < workflow_info->output_assignment_info_count;j++) { + mmi_workflow_output_assignment_info_s *output_assignment_info = &(workflow_info->output_assignment_infos[j]); + _D("workflow_info[%zu].output_assignment_info[%zu].output_name: %s", i, j, output_assignment_info->output_name); + _D("workflow_info[%zu].output_assignment_info[%zu].from_node_name: %s", i, j, output_assignment_info->from_node_name); + _D("workflow_info[%zu].output_assignment_info[%zu].from_port_name: %s", i, j, output_assignment_info->from_port_name); + } + } + return workflow_list; +} + +void PluginModuleProxySharedLibrary::add_node_instance_info(mmi_plugin_module_node_instance_info_s *node_instance_info) { + mmi_plugin_storage_add_node_instance_info(node_instance_info); +} + +void PluginModuleProxySharedLibrary::remove_node_instance_info(mmi_node_instance_h node_instance) { + mmi_plugin_storage_remove_node_instance_info(node_instance); +} + +} // namespace mmi + diff --git a/src/mmi-manager/mmi-plugin-module-proxy.h b/src/mmi-manager/mmi-plugin-module-proxy.h new file mode 100644 index 0000000..f02002e --- /dev/null +++ b/src/mmi-manager/mmi-plugin-module-proxy.h @@ -0,0 +1,127 @@ +/* +* Copyright © 2023 Samsung Electronics co., Ltd. All Rights Reserved. +* +* Permission is hereby granted, free of charge, to any person obtaining a +* copy of this software and associated documentation files (the "Software"), +* to deal in the Software without restriction, including without limitation +* the rights to use, copy, modify, merge, publish, distribute, sublicense, +* and/or sell copies of the Software, and to permit persons to whom the +* Software is furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice (including the next +* paragraph) shall be included in all copies or substantial portions of the +* Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +* DEALINGS IN THE SOFTWARE. +*/ + +#ifndef __MMI_PLUGIN_MODULE_PROXY_H__ +#define __MMI_PLUGIN_MODULE_PROXY_H__ + +#include +#include +#include +#include + +#include "mmi-common.h" +#include "mmi-plugin-module.h" +#include "mmi-plugin-module-event.h" +#include "mmi-plugin-storage.h" + +#include "mmi.h" + +namespace mmi { + +typedef void* function_pointer; + +typedef std::string mmi_plugin_module_identifier; + +enum class mmi_plugin_module_type_e { + SELF_CONTAINED = 0, + SHARED_LIBRARY, +}; + +typedef struct { + mmi_plugin_module_type_e plugin_module_type; + mmi_plugin_module_identifier plugin_module_identifier; +} PluginModuleInfo; + +struct IPluginModuleProxy : public SimpleEventObservable { + IPluginModuleProxy() = default; + virtual ~IPluginModuleProxy() = default; + virtual function_pointer get_function(const std::string& function_name) = 0; + + virtual mmi_plugin_module_node_list_s load_node_prototypes() = 0; + virtual mmi_plugin_module_workflow_list_s load_workflow_prototypes() = 0; + + virtual void add_node_instance_info(mmi_plugin_module_node_instance_info_s *node_instance_info) = 0; + virtual void remove_node_instance_info(mmi_node_instance_h node_instance) = 0; +}; + +struct IPluginModuleProxyProvider { + virtual std::shared_ptr get_plugin_module_proxy( + const PluginModuleInfo plugin_module_info) = 0; +}; + +class PluginModuleProxySharedLibrary : public IPluginModuleProxy { +public: + PluginModuleProxySharedLibrary(const mmi_plugin_module_identifier plugin_module_identifier); + virtual ~PluginModuleProxySharedLibrary(); + + virtual function_pointer get_function(const std::string& function_name) override; + + virtual mmi_plugin_module_node_list_s load_node_prototypes() override; + virtual mmi_plugin_module_workflow_list_s load_workflow_prototypes() override; + + virtual void add_node_instance_info(mmi_plugin_module_node_instance_info_s *node_instance_info) override; + virtual void remove_node_instance_info(mmi_node_instance_h node_instance) override; +private: + const mmi_plugin_module_identifier m_plugin_module_identifier; + + void* m_shared_object_handle{nullptr}; +}; + +enum class PROXY_FACTORY_EVENT_TYPE { + PROXY_CREATED, +}; + +struct IPluginModuleProxyFactory : public SimpleEventObservable { + virtual std::vector get_plugin_module_list() = 0; + virtual std::shared_ptr create( + PluginModuleInfo plugin_module_info) = 0; +}; + +class PluginModuleProxyFactoryDefault : public IPluginModuleProxyFactory { +public: + virtual std::vector get_plugin_module_list() override { + const std::string plugin_module_path{"/usr/share/mmi/plugins"}; + std::vector plugin_module_list; + for (const auto & entry : std::filesystem::directory_iterator(plugin_module_path)) { + PluginModuleInfo info{mmi_plugin_module_type_e::SHARED_LIBRARY, entry.path().string()}; + plugin_module_list.push_back(info); + } + return plugin_module_list; + } + virtual std::shared_ptr create( + PluginModuleInfo plugin_module_info) override { + if (plugin_module_info.plugin_module_type == mmi_plugin_module_type_e::SHARED_LIBRARY) { + auto proxy = std::make_shared( + plugin_module_info.plugin_module_identifier); + notify_observers(PROXY_FACTORY_EVENT_TYPE::PROXY_CREATED, static_cast(proxy.get())); + return proxy; + } + return std::shared_ptr(); + } +}; + +} // namespace mmi + +#endif //__MMI_PLUGIN_MODULE_PROXY_H__ + + diff --git a/src/mmi-manager/mmi-plugin-module-registry.cpp b/src/mmi-manager/mmi-plugin-module-registry.cpp new file mode 100644 index 0000000..0f19279 --- /dev/null +++ b/src/mmi-manager/mmi-plugin-module-registry.cpp @@ -0,0 +1,324 @@ +/* + * Copyright © 2021 Samsung Electronics co., Ltd. All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +#include "mmi-plugin-module-registry.h" +#include "mmi-manager-log.h" + +namespace mmi { + +PluginModuleRegistry::PluginModuleRegistry() { + _D("PluginModuleRegistry is created"); +} + +PluginModuleRegistry::~PluginModuleRegistry() { + _D("PluginModuleRegistry is destroyed"); +} + +void PluginModuleRegistry::initialize() { + if (!m_factory) { + _E("Plugin module proxy factory is not set"); + return; + } + + load_node_prototypes(); + load_workflow_prototypes(); +} + +void PluginModuleRegistry::deinitialize() { +} + +void PluginModuleRegistry::set_plugin_module_proxy_factory(std::shared_ptr factory) { + m_factory = factory; +} + +void PluginModuleRegistry::set_node_prototype_store(std::shared_ptr node_prototype_store) { + m_node_prototype_store = node_prototype_store; +} + +void PluginModuleRegistry::set_workflow_prototype_store(std::shared_ptr workflow_prototype_store) { + m_workflow_prototype_store = workflow_prototype_store; +} + +/* FIXME : Check if this function needs to be moved to INodePrototypeStore */ +static std::shared_ptr create_node_prototype(mmi_node_h node) { + if (!node) { + _E("Invalid argument"); + return nullptr; + } + + auto prototype = std::make_shared(); + + if (prototype) { + mmi_standard_node_type_e type; + mmi_node_get_type(node, &type); + + prototype->set_type(type); + switch (type) { + case MMI_STANDARD_NODE_TYPE_SOURCE: { + mmi_standard_node_source_type_e source_type; + mmi_standard_node_get_source_type(node, &source_type); + prototype->set_sub_type(source_type); + break; + } + case MMI_STANDARD_NODE_TYPE_PROCESSOR: { + mmi_standard_node_processor_type_e processor_type; + mmi_standard_node_get_processor_type(node, &processor_type); + prototype->set_sub_type(processor_type); + break; + } + case MMI_STANDARD_NODE_TYPE_LOGIC: { + mmi_standard_node_logic_type_e logic_type; + mmi_standard_node_get_logic_type(node, &logic_type); + prototype->set_sub_type(logic_type); + break; + } + case MMI_STANDARD_NODE_TYPE_CONTROLLER: { + mmi_standard_node_controller_type_e controller_type; + mmi_standard_node_get_controller_type(node, &controller_type); + prototype->set_sub_type(controller_type); + break; + } + } + + size_t port_count; + mmi_node_get_port_count(node, &port_count); + for (size_t i = 0; i < port_count; i++) { + mmi_port_h port; + mmi_node_get_port(node, i, &port); + + char *name = nullptr; + size_t name_length = 0; + mmi_port_get_name(port, &name, &name_length); + + mmi_port_type_e type; + mmi_port_get_type(port, &type); + + mmi_data_type_e data_type; + mmi_port_get_data_type(port, &data_type); + + PortInfo port_info; + port_info.name = name; + port_info.port_type = type; + port_info.data_type = data_type; + prototype->add_port_info(port_info); + } + } else { + _E("Failed to create node prototype"); + } + + return prototype; +} + +bool PluginModuleRegistry::load_node_prototypes() { + if (!m_node_prototype_store) { + _E("Node prototype store is not set"); + return false; + } + + std::vector plugin_module_list = m_factory->get_plugin_module_list(); + for (auto& plugin_module_info : plugin_module_list) { + std::shared_ptr ptr = + get_plugin_module_proxy(plugin_module_info); + if (ptr) { + mmi_plugin_module_node_list_s node_list = ptr->load_node_prototypes(); + for (size_t i = 0; i < node_list.node_count; i++) { + auto prototype = create_node_prototype(node_list.nodes[i]); + if (prototype) { + prototype->set_plugin_module_info(plugin_module_info); + bool ret = m_node_prototype_store->add_node_prototype(prototype); + _D("Add Node Prototype ret(%d): %d %d %s", ret, prototype->get_type(), to_int(prototype->get_sub_type()), plugin_module_info.plugin_module_identifier.c_str()); + } + } + } else { + _E("Failed to get plugin module: %s", plugin_module_info.plugin_module_identifier.c_str()); + } + } + + return true; +} + +/* FIXME : Check if this function needs to be moved to IWorkflowPrototypeStore */ +static std::shared_ptr create_workflow_prototype(mmi_workflow_h workflow) { + if (!workflow) { + _E("Invalid argument"); + return nullptr; + } + + auto prototype = std::make_shared(); + + if (prototype) { + mmi_standard_workflow_type_e type; + mmi_workflow_get_type(workflow, &type); + + prototype->set_type(type); + + mmi_workflow_s *workflow_info = static_cast(workflow); + for (size_t i = 0; i < workflow_info->node_info_count; i++) { + NodeInfo node_info; + node_info.name = workflow_info->node_infos[i].name; + mmi_node_get_type(workflow_info->node_infos[i].node, &node_info.node_type); + switch (node_info.node_type) { + case MMI_STANDARD_NODE_TYPE_SOURCE: { + mmi_standard_node_source_type_e source_type; + mmi_standard_node_get_source_type(workflow_info->node_infos[i].node, &source_type); + node_info.node_sub_type = source_type; + } + break; + case MMI_STANDARD_NODE_TYPE_PROCESSOR: { + mmi_standard_node_processor_type_e processor_type; + mmi_standard_node_get_processor_type(workflow_info->node_infos[i].node, &processor_type); + node_info.node_sub_type = processor_type; + } + break; + case MMI_STANDARD_NODE_TYPE_LOGIC: { + mmi_standard_node_logic_type_e logic_type; + mmi_standard_node_get_logic_type(workflow_info->node_infos[i].node, &logic_type); + node_info.node_sub_type = logic_type; + } + break; + case MMI_STANDARD_NODE_TYPE_CONTROLLER: { + mmi_standard_node_controller_type_e controller_type; + mmi_standard_node_get_controller_type(workflow_info->node_infos[i].node, &controller_type); + node_info.node_sub_type = controller_type; + } + break; + default: { + _E("Invalid node type"); + return nullptr; + } + } + + prototype->add_node_info(node_info); + } + + for (size_t i = 0; i < workflow_info->link_info_count; i++) { + LinkInfo link_info; + link_info.from_node_name = workflow_info->link_infos[i].from_node_name; + link_info.from_port_name = workflow_info->link_infos[i].from_port_name; + link_info.to_node_name = workflow_info->link_infos[i].to_node_name; + link_info.to_port_name = workflow_info->link_infos[i].to_port_name; + + prototype->add_link_info(link_info); + } + + for (size_t i = 0; i < workflow_info->attribute_assignment_info_count; i++) { + AttributeAssignmentInfo attribute_assignment_info; + attribute_assignment_info.attribute_name = workflow_info->attribute_assignment_infos[i].attribute_name; + attribute_assignment_info.target_node_name = workflow_info->attribute_assignment_infos[i].target_node_name; + attribute_assignment_info.target_attribute_name = workflow_info->attribute_assignment_infos[i].target_attribute_name; + + prototype->add_attribute_assignment_info(attribute_assignment_info); + } + + for (size_t i = 0; i < workflow_info->attribute_default_value_info_count; i++) { + AttributeDefaultValueInfo attribute_default_value_info; + mmi_attribute_h default_value = nullptr; + int ret = mmi_attribute_from_bytes( + workflow_info->attribute_default_value_infos[i].serialized_default_value, + workflow_info->attribute_default_value_infos[i].serialized_default_value_size, + &default_value); + if (MMI_ERROR_NONE != ret) { + _E("Failed to create attribute from bytes"); + } + attribute_default_value_info.default_value = default_value; + + prototype->add_attribute_default_value_info(attribute_default_value_info); + } + + for (size_t i = 0; i < workflow_info->output_assignment_info_count; i++) { + OutputAssignmentInfo output_assignment_info; + output_assignment_info.output_name = workflow_info->output_assignment_infos[i].output_name; + output_assignment_info.from_node_name = workflow_info->output_assignment_infos[i].from_node_name; + output_assignment_info.from_port_name = workflow_info->output_assignment_infos[i].from_port_name; + + prototype->add_output_assignment_info(output_assignment_info); + } + } else { + _E("Failed to create workflow prototype"); + } + + return prototype; +} + +bool PluginModuleRegistry::load_workflow_prototypes() { + if (!m_workflow_prototype_store) { + _E("Workflow prototype store is not set"); + return false; + } + + std::vector plugin_module_list = m_factory->get_plugin_module_list(); + for (auto& plugin_module_info : plugin_module_list) { + std::shared_ptr ptr = + get_plugin_module_proxy(plugin_module_info); + if (ptr) { + mmi_plugin_module_workflow_list_s workflow_list = ptr->load_workflow_prototypes(); + for (size_t i = 0; i < workflow_list.workflow_count; i++) { + auto prototype = create_workflow_prototype(workflow_list.workflows[i]); + if (prototype) { + prototype->set_plugin_module_info(plugin_module_info); + m_workflow_prototype_store->add_workflow_prototype(prototype); + } + } + } else { + _E("Failed to get plugin module: %s", plugin_module_info.plugin_module_identifier.c_str()); + } + } + + return true; +} + +std::shared_ptr PluginModuleRegistry::get_plugin_module_proxy( + const PluginModuleInfo plugin_module_info) { + plugin_module_map_key key( + plugin_module_info.plugin_module_type, plugin_module_info.plugin_module_identifier); + auto it = m_plugin_modules.find(key); + if (it != m_plugin_modules.end()) { + auto plugin_module = it->second.lock(); + if (plugin_module) { + return plugin_module; + } else { + _D("Plugin module proxy is expired: %d %s", + static_cast(plugin_module_info.plugin_module_type), + plugin_module_info.plugin_module_identifier.c_str()); + } + } + + if (!m_factory) { + _E("Plugin module proxy factory is not set"); + return nullptr; + } + + _D("Create plugin module proxy: %d %s", + static_cast(plugin_module_info.plugin_module_type), + plugin_module_info.plugin_module_identifier.c_str()); + + auto plugin_module = m_factory->create(plugin_module_info); + if (plugin_module) { + m_plugin_modules[key] = plugin_module; + } + + return plugin_module; +} + +} // namespace mmi + diff --git a/src/mmi-manager/mmi-plugin-module-registry.h b/src/mmi-manager/mmi-plugin-module-registry.h new file mode 100644 index 0000000..f5b6fae --- /dev/null +++ b/src/mmi-manager/mmi-plugin-module-registry.h @@ -0,0 +1,72 @@ +/* +* Copyright © 2023 Samsung Electronics co., Ltd. All Rights Reserved. +* +* Permission is hereby granted, free of charge, to any person obtaining a +* copy of this software and associated documentation files (the "Software"), +* to deal in the Software without restriction, including without limitation +* the rights to use, copy, modify, merge, publish, distribute, sublicense, +* and/or sell copies of the Software, and to permit persons to whom the +* Software is furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice (including the next +* paragraph) shall be included in all copies or substantial portions of the +* Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +* DEALINGS IN THE SOFTWARE. +*/ + +#ifndef __MMI_PLUGIN_MODULE_REGISTRY_H__ +#define __MMI_PLUGIN_MODULE_REGISTRY_H__ + +#include "mmi.h" +#include "mmi-common.h" +#include "mmi-plugin-module-proxy.h" +#include "mmi-manager-log.h" +#include "mmi-plugin-module.h" +#include "mmi-node-prototype-manager.h" +#include "mmi-workflow-prototype-manager.h" + +#include +#include +#include + +namespace mmi { + +class PluginModuleRegistry : public IPluginModuleProxyProvider { +public: + PluginModuleRegistry(); + virtual ~PluginModuleRegistry(); + + void initialize(); + void deinitialize(); + + void set_plugin_module_proxy_factory(std::shared_ptr factory); + void set_node_prototype_store(std::shared_ptr node_prototype_store); + void set_workflow_prototype_store(std::shared_ptr workflow_prototype_store); + + bool load_node_prototypes(); + bool load_workflow_prototypes(); + + std::shared_ptr get_plugin_module_proxy( + const PluginModuleInfo plugin_module_info) override; +protected: + std::shared_ptr m_factory; + + std::shared_ptr m_node_prototype_store; + std::shared_ptr m_workflow_prototype_store; + + typedef std::pair plugin_module_map_key; + std::map> m_plugin_modules; +}; + +} // namespace mmi + +#endif //__MMI_PLUGIN_MODULE_REGISTRY_H__ + + diff --git a/src/mmi-manager/mmi-port-instance-manager.cpp b/src/mmi-manager/mmi-port-instance-manager.cpp new file mode 100644 index 0000000..d5bd9d1 --- /dev/null +++ b/src/mmi-manager/mmi-port-instance-manager.cpp @@ -0,0 +1,62 @@ +/* + * Copyright © 2021 Samsung Electronics co., Ltd. All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +#include "mmi-port-instance-manager.h" + +#include "mmi-manager-log.h" + +namespace mmi { + +PortInstanceManager::PortInstanceManager() { +} + +PortInstanceManager::~PortInstanceManager() { +} + +std::shared_ptr PortInstanceManager::create_port_instance() { + std::shared_ptr port_instance = std::make_shared(); + m_port_instances.push_back(port_instance); + return port_instance; +} + +bool PortInstanceManager::destroy_port_instance( + std::shared_ptr port_instance) { + for (auto it = m_port_instances.begin(); it != m_port_instances.end(); ++it) { + if (*it == port_instance) { + m_port_instances.erase(it); + return true; + } + } + return false; +} + +bool PortInstanceManager::link_port_instances( + std::shared_ptr out_port, + std::shared_ptr in_port) { + out_port->add_linked_port_instance(in_port); + in_port->add_linked_port_instance(out_port); + return true; +} + +} // namespace mmi + diff --git a/src/mmi-manager/mmi-port-instance-manager.h b/src/mmi-manager/mmi-port-instance-manager.h new file mode 100644 index 0000000..cce6b52 --- /dev/null +++ b/src/mmi-manager/mmi-port-instance-manager.h @@ -0,0 +1,70 @@ +/* +* Copyright © 2023 Samsung Electronics co., Ltd. All Rights Reserved. +* +* Permission is hereby granted, free of charge, to any person obtaining a +* copy of this software and associated documentation files (the "Software"), +* to deal in the Software without restriction, including without limitation +* the rights to use, copy, modify, merge, publish, distribute, sublicense, +* and/or sell copies of the Software, and to permit persons to whom the +* Software is furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice (including the next +* paragraph) shall be included in all copies or substantial portions of the +* Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +* DEALINGS IN THE SOFTWARE. +*/ + +#ifndef __MMI_PORT_INSTANCE_MANAGER_H__ +#define __MMI_PORT_INSTANCE_MANAGER_H__ + +#include "mmi-port-instance.h" + +#include +#include + +namespace mmi { + +class IPortInstanceManager { +public: + IPortInstanceManager() = default; + virtual ~IPortInstanceManager() = default; + + virtual std::shared_ptr create_port_instance() = 0; + virtual bool destroy_port_instance( + std::shared_ptr port_instance) = 0; + virtual bool link_port_instances( + std::shared_ptr out_port, + std::shared_ptr in_port) = 0; +}; + +class PortInstanceManager : public IPortInstanceManager { +public: + PortInstanceManager(); + virtual ~PortInstanceManager(); + + virtual std::shared_ptr create_port_instance() override; + virtual bool destroy_port_instance( + std::shared_ptr port_instance) override; + virtual bool link_port_instances( + std::shared_ptr out_port, + std::shared_ptr in_port) override; + + void initialize(); + void deinitialize(); + +protected: + std::vector> m_port_instances; +}; + +} // namespace mmi + +#endif //__MMI_PORT_INSTANCE_MANAGER_H__ + + diff --git a/src/mmi-manager/mmi-port-instance.cpp b/src/mmi-manager/mmi-port-instance.cpp new file mode 100644 index 0000000..d74e0b4 --- /dev/null +++ b/src/mmi-manager/mmi-port-instance.cpp @@ -0,0 +1,73 @@ +/* + * Copyright © 2021 Samsung Electronics co., Ltd. All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +#include "mmi-port-instance.h" + +#include "mmi-manager-log.h" + +namespace mmi { + +PortInstance::PortInstance() { + _D("PortInstance created : %p", this); +} + +PortInstance::~PortInstance() { +} + +void PortInstance::set_port_callbacks(mmi_port_callbacks callbacks) { + _D("PortInstance[%p] set_port_callbacks : %p %p", this, + callbacks.output_format_requested_cb, callbacks.input_data_received_cb); + m_callbacks = callbacks; +} + +void PortInstance::add_data_gateway(std::shared_ptr gateway) { + _D("PortInstance[%p] add_data_gateway : %p", this, gateway.get()); + m_data_gateways.push_back(gateway); +} + +void PortInstance::add_linked_port_instance(std::shared_ptr port_instance) { + m_linked_port_instances.push_back(port_instance); +} + +void PortInstance::on_output_data_generated(mmi_data_h data) { + _D("Data generated : this[%p] data[%p]", this, data); + + for (auto it = m_data_gateways.begin(); it != m_data_gateways.end(); ++it) { + (*it)->send_data(data); + } + + for (auto it = m_linked_port_instances.begin(); it != m_linked_port_instances.end(); ++it) { + (*it)->on_output_data_received(data); + } +} + +void PortInstance::on_output_data_received(mmi_data_h data) { + _D("Data received : this[%p] data[%p] input_data_received_cb[%p]", + this, data, m_callbacks.input_data_received_cb); + if (m_callbacks.input_data_received_cb) { + m_callbacks.input_data_received_cb(static_cast(this), data); + } +} + +} // namespace mmi + diff --git a/src/mmi-manager/mmi-port-instance.h b/src/mmi-manager/mmi-port-instance.h new file mode 100644 index 0000000..e508a6b --- /dev/null +++ b/src/mmi-manager/mmi-port-instance.h @@ -0,0 +1,64 @@ +/* +* Copyright © 2023 Samsung Electronics co., Ltd. All Rights Reserved. +* +* Permission is hereby granted, free of charge, to any person obtaining a +* copy of this software and associated documentation files (the "Software"), +* to deal in the Software without restriction, including without limitation +* the rights to use, copy, modify, merge, publish, distribute, sublicense, +* and/or sell copies of the Software, and to permit persons to whom the +* Software is furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice (including the next +* paragraph) shall be included in all copies or substantial portions of the +* Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +* DEALINGS IN THE SOFTWARE. +*/ + +#ifndef __MMI_PORT_INSTANCE_H__ +#define __MMI_PORT_INSTANCE_H__ + +#include "mmi.h" +#include "mmi-port.h" +#include "mmi-data-gateway.h" + +#include +#include +#include + +namespace mmi { + +class PortInstance { +public: + PortInstance(); + virtual ~PortInstance(); + + void set_port_callbacks(mmi_port_callbacks callbacks); + + void add_linked_port_instance(std::shared_ptr port_instance); + void add_data_gateway(std::shared_ptr gateway); + + /* Output data was generated by a Node module */ + void on_output_data_generated(mmi_data_h data); + + /* An output data generated by a different Node module was received through a link */ + void on_output_data_received(mmi_data_h data); +private: + std::string m_name; + mmi_port_callbacks m_callbacks{nullptr,}; + + std::vector> m_linked_port_instances; + std::vector> m_data_gateways; +}; + +} // namespace mmi + +#endif //__MMI_PORT_INSTANCE_H__ + + diff --git a/src/mmi-manager/mmi-self-container.cpp b/src/mmi-manager/mmi-self-container.cpp new file mode 100644 index 0000000..daffaf9 --- /dev/null +++ b/src/mmi-manager/mmi-self-container.cpp @@ -0,0 +1,370 @@ +/* + * Copyright © 2023 Samsung Electronics co., Ltd. All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +#include "mmi-self-container.h" +#include "mmi-port-instance-manager.h" + +#include + +namespace mmi { + +std::map g_node_instance_info_map; + +TestNodePluginModule::TestNodePluginModule() { + default_node_callbacks.initialized_cb = node_initialized_cb; + default_node_callbacks.deinitialized_cb = node_deinitialized_cb; + default_node_callbacks.attribute_set_cb = node_attribute_set_cb; + default_node_callbacks.activated_cb = node_activated_cb; + default_node_callbacks.deactivated_cb = node_deactivated_cb; + default_node_callbacks.signal_received_cb = node_signal_received_cb; + + default_port_callbacks.output_format_requested_cb = port_output_format_requested_cb; + default_port_callbacks.input_data_received_cb = port_input_data_received_cb; +} + +TestNodePluginModule::~TestNodePluginModule() { +} + +int TestNodePluginModule::node_initialized_cb(mmi_node_instance_h instance) { + _D("Node initialize callback is called for %p", instance); + return MMI_ERROR_NONE; +} + +int TestNodePluginModule::node_deinitialized_cb(mmi_node_instance_h instance) { + _D("Node deinitialize callback is called for %p", instance); + return MMI_ERROR_NONE; +} + +int TestNodePluginModule::node_attribute_set_cb(mmi_node_instance_h instance, mmi_attribute_h attribute) { + _D("Node attribute set callback is called for %p", instance); + return MMI_ERROR_NONE; +} + +int TestNodePluginModule::node_activated_cb(mmi_node_instance_h instance) { + _D("Node activate callback is called for %p", instance); + return MMI_ERROR_NONE; +} + +int TestNodePluginModule::node_deactivated_cb(mmi_node_instance_h instance) { + _D("Node deactivate callback is called for %p", instance); + return MMI_ERROR_NONE; +} + +int TestNodePluginModule::node_signal_received_cb(mmi_node_instance_h instance, mmi_signal_h signal) { + _D("Node signal received callback is called for %p", instance); + return MMI_ERROR_NONE; +} + +int TestNodePluginModule::port_output_format_requested_cb(mmi_port_instance_h instance, const char *format) { + _D("Port output format request callback is called for %p", instance); + return MMI_ERROR_NONE; +} +int TestNodePluginModule::port_input_data_received_cb(mmi_port_instance_h instance, mmi_data_h data) { + _D("Port input data callback is called for %p", instance); + return MMI_ERROR_NONE; +} + +TestNodePluginModuleMIC::TestNodePluginModuleMIC() { + m_node_callbacks = default_node_callbacks; + m_node_callbacks.activated_cb = node_activated_cb; + m_audio_port_callbacks = default_port_callbacks; +} + +int TestNodePluginModuleMIC::node_activated_cb(mmi_node_instance_h instance) { + _D("[MIC] Node activate callback is called for %p", instance); + + /* In shared object plugin module, the output data can be generated using the API + mmi_port_instance_output_generate() function, after finding the port using + the API mmi_node_instance_port_find. However, in this test plugin module, calling + mmi library API is not allowed. So, we have to find the output port using + the port instance map. */ + + std::string output_port_name{"AUDIO"}; + mmi_port_instance_h output_port = nullptr; + for (const auto &elem : g_node_instance_info_map) { + const mmi_plugin_module_node_instance_info_s &node_instance_info = elem.second; + if (node_instance_info.node == instance) { + for (size_t i = 0; i < node_instance_info.port_info_count; ++i) { + if (output_port_name.compare(std::string(node_instance_info.port_infos[i].name)) == 0) { + _D("[MIC] Found output port %p", node_instance_info.port_infos[i].port); + output_port = node_instance_info.port_infos[i].port; + break; + } + } + } + } + + auto plugin_module = reinterpret_cast(instance); + + int value = 100; + mmi_data_h output_data = reinterpret_cast(&value); + + PortInstance *port_instance = reinterpret_cast(output_port); + if (port_instance) { + _D("[MIC] Generating an output %d to %p", value, port_instance); + port_instance->on_output_data_generated(output_data); + } + + return MMI_ERROR_NONE; +} + +TestNodePluginModuleASR::TestNodePluginModuleASR() { + m_node_callbacks = default_node_callbacks; + m_audio_port_callbacks = default_port_callbacks; + m_audio_port_callbacks.input_data_received_cb = audio_port_input_data_received_cb; +} + +int TestNodePluginModuleASR::audio_port_input_data_received_cb(mmi_port_instance_h instance, mmi_data_h data) { + /* In shared object plugin module, the output port can be found using the API + mmi_node_instance_sibling_port_find() function. However, in this test + plugin module, calling mmi library API is not allowed. So, we have to find the + output port using the node instance info map. */ + std::string output_port_name{"FINAL_RESULT"}; + mmi_port_instance_h output_port = nullptr; + const mmi_plugin_module_node_instance_info_s &node_instance_info = g_node_instance_info_map[instance]; + for (size_t i = 0; i < node_instance_info.port_info_count; ++i) { + if (output_port_name.compare(std::string(node_instance_info.port_infos[i].name)) == 0) { + output_port = node_instance_info.port_infos[i].port; + break; + } + } + + int value = -1; + int *ptr = reinterpret_cast(data); + if (ptr) value = *ptr; + + _D("[ASR] Audio input data callback is called: %p %d", instance, value); + + value++; + ptr = &value; + + mmi_data_h output_data = reinterpret_cast(ptr); + + /* In shared object plugin module, the output data can be generated using the API + mmi_port_instance_output_generate() function. However, in this test plugin module, + calling mmi library API is not allowed. So, we have to generate the output + using the port instance handle directly. */ + + PortInstance *port_instance = reinterpret_cast(output_port); + if (port_instance) { + _D("[ASR] Generating an output %d to %p", value, port_instance); + port_instance->on_output_data_generated(output_data); + } + + return MMI_ERROR_NONE; +} + +int TestNodePluginModuleMatch::text_port_input_data_received_cb(mmi_port_instance_h instance, mmi_data_h data) { + /* In shared object plugin module, the output port can be found using the API + mmi_node_instance_sibling_port_find() function. However, in this test + plugin module, calling mmi library API is not allowed. So, we have to find the + output port using the node instance info map. */ + + std::string output_port_name{"MATCHED_CANDIDATE"}; + mmi_port_instance_h output_port = nullptr; + const mmi_plugin_module_node_instance_info_s &node_instance_info = g_node_instance_info_map[instance]; + for (size_t i = 0; i < node_instance_info.port_info_count; ++i) { + if (output_port_name.compare(std::string(node_instance_info.port_infos[i].name)) == 0) { + output_port = node_instance_info.port_infos[i].port; + break; + } + } + + int value = -1; + int *ptr = reinterpret_cast(data); + if (ptr) value = *ptr; + + _D("[Match] Text input data callback is called: %p %d", instance, value); + + value++; + ptr = &value; + + mmi_data_h output_data = reinterpret_cast(ptr); + + /* In shared object plugin module, the output data can be generated using the API + mmi_port_instance_output_generate() function. However, in this test plugin module, + calling mmi library API is not allowed. So, we have to generate the output + using the port instance handle directly. */ + + PortInstance *port_instance = reinterpret_cast(output_port); + if (port_instance) { + _D("[Match] Generating an output %d to %p", value, port_instance); + port_instance->on_output_data_generated(output_data); + } + + return MMI_ERROR_NONE; +} + +TestNodePluginModuleMatch::TestNodePluginModuleMatch() { + m_node_callbacks = default_node_callbacks; + m_text_port_callbacks = default_port_callbacks; + m_text_port_callbacks.input_data_received_cb = text_port_input_data_received_cb; +} + +PluginModuleProxySelfContainerTest::PluginModuleProxySelfContainerTest() { +} + +PluginModuleProxySelfContainerTest::~PluginModuleProxySelfContainerTest() { +} + +function_pointer PluginModuleProxySelfContainerTest::get_function(const std::string& function_name) { + return nullptr; +} + +mmi_plugin_module_node_list_s PluginModuleProxySelfContainerTest::load_node_prototypes() { + _D("Loading node prototypes from self-contained test plugin module"); + + /* Self-contained port information */ + + mmi_port_h mic_audio_port = nullptr; + mmi_port_create(&mic_audio_port); + mmi_port_set_name(mic_audio_port, "AUDIO"); + mmi_port_set_type(mic_audio_port, MMI_PORT_TYPE_OUT); + mmi_port_set_data_type(mic_audio_port, MMI_DATA_TYPE_AUDIO); + mmi_port_set_callbacks(mic_audio_port, m_node_mic.m_audio_port_callbacks); + + mmi_node_h mic_node = nullptr; + mmi_standard_node_create_source(MMI_STANDARD_NODE_SOURCE_TYPE_MIC_AMBIENT, &mic_node); + mmi_node_add_port(mic_node, mic_audio_port); + mmi_node_set_callbacks(mic_node, m_node_mic.m_node_callbacks); + mmi_node_register(mic_node); + + mmi_node_destroy(mic_node); + + mmi_port_h asr_audio_port = nullptr; + mmi_port_create(&asr_audio_port); + mmi_port_set_name(asr_audio_port, "AUDIO"); + mmi_port_set_type(asr_audio_port, MMI_PORT_TYPE_IN); + mmi_port_set_data_type(asr_audio_port, MMI_DATA_TYPE_AUDIO); + mmi_port_set_callbacks(asr_audio_port, m_node_asr.m_audio_port_callbacks); + + mmi_port_h asr_partial_result_port = nullptr; + mmi_port_create(&asr_partial_result_port); + mmi_port_set_name(asr_partial_result_port, "PARTIAL_RESULT"); + mmi_port_set_type(asr_partial_result_port, MMI_PORT_TYPE_OUT); + mmi_port_set_data_type(asr_partial_result_port, MMI_DATA_TYPE_TEXT); + mmi_port_set_callbacks(asr_partial_result_port, m_node_asr.m_audio_port_callbacks); + + mmi_port_h asr_final_result_port = nullptr; + mmi_port_create(&asr_final_result_port); + mmi_port_set_name(asr_final_result_port, "FINAL_RESULT"); + mmi_port_set_type(asr_final_result_port, MMI_PORT_TYPE_OUT); + mmi_port_set_data_type(asr_final_result_port, MMI_DATA_TYPE_TEXT); + mmi_port_set_callbacks(asr_final_result_port, m_node_asr.m_audio_port_callbacks); + + mmi_node_h asr_node = nullptr; + mmi_standard_node_create_processor(MMI_STANDARD_NODE_PROCESSOR_TYPE_ASR, &asr_node); + mmi_node_add_port(asr_node, asr_audio_port); + mmi_node_add_port(asr_node, asr_partial_result_port); + mmi_node_add_port(asr_node, asr_final_result_port); + mmi_node_set_callbacks(asr_node, m_node_asr.m_node_callbacks); + mmi_node_register(asr_node); + + mmi_node_destroy(asr_node); + + mmi_port_h match_text_port = nullptr; + mmi_port_create(&match_text_port); + mmi_port_set_name(match_text_port, "TEXT"); + mmi_port_set_type(match_text_port, MMI_PORT_TYPE_IN); + mmi_port_set_data_type(match_text_port, MMI_DATA_TYPE_TEXT); + mmi_port_set_callbacks(match_text_port, m_node_match.m_text_port_callbacks); + + mmi_port_h match_candidate_port = nullptr; + mmi_port_create(&match_candidate_port); + mmi_port_set_name(match_candidate_port, "MATCHED_CANDIDATE"); + mmi_port_set_type(match_candidate_port, MMI_PORT_TYPE_OUT); + mmi_port_set_data_type(match_candidate_port, MMI_DATA_TYPE_TEXT); + mmi_port_set_callbacks(match_candidate_port, m_node_match.m_text_port_callbacks); + + mmi_node_h match_node = nullptr; + mmi_standard_node_create_logic(MMI_STANDARD_NODE_LOGIC_TYPE_FIXED_STRING_MATCH, &match_node); + mmi_node_add_port(match_node, match_text_port); + mmi_node_add_port(match_node, match_candidate_port); + mmi_node_set_callbacks(match_node, m_node_match.m_node_callbacks); + mmi_node_register(match_node); + + mmi_node_destroy(match_node); + + const char *identifier = mmi_plugin_storage_get_current_module_identifier(); + mmi_plugin_module_node_list_s list = mmi_plugin_storage_get_node_list(identifier); + + return list; +} + +mmi_plugin_module_workflow_list_s PluginModuleProxySelfContainerTest::load_workflow_prototypes() { + _D("Loading workflow prototypes from self-contained test plugin module"); + + mmi_workflow_h workflow = nullptr; + mmi_workflow_create(&workflow); + + mmi_workflow_set_type(workflow, MMI_STANDARD_WORKFLOW_WAKEUPLESS_COMMAND); + + mmi_node_h node_mic = nullptr; + mmi_standard_node_create_source(MMI_STANDARD_NODE_SOURCE_TYPE_MIC_AMBIENT, &node_mic); + mmi_workflow_node_add(workflow, "MIC", node_mic); + + mmi_node_h node_asr = nullptr; + mmi_standard_node_create_processor(MMI_STANDARD_NODE_PROCESSOR_TYPE_ASR, &node_asr); + mmi_workflow_node_add(workflow, "ASR", node_asr); + + mmi_node_h node_match = nullptr; + mmi_standard_node_create_logic(MMI_STANDARD_NODE_LOGIC_TYPE_FIXED_STRING_MATCH, &node_match); + mmi_workflow_node_add(workflow, "MATCH", node_match); + + mmi_workflow_link_nodes_by_names(workflow, "MIC", "AUDIO", "ASR", "AUDIO"); + mmi_workflow_link_nodes_by_names(workflow, "ASR", "FINAL_RESULT", "MATCH", "TEXT"); + + mmi_workflow_output_assign(workflow, "OUTPUT", "MATCH", "MATCHED_CANDIDATE"); + + mmi_standard_workflow_register(workflow); + + mmi_node_destroy(node_match); + mmi_node_destroy(node_asr); + mmi_node_destroy(node_mic); + + mmi_workflow_destroy(workflow); + + const char *identifier = mmi_plugin_storage_get_current_module_identifier(); + mmi_plugin_module_workflow_list_s list = mmi_plugin_storage_get_workflow_list(identifier); + + return list; +} + +void PluginModuleProxySelfContainerTest::add_node_instance_info(mmi_plugin_module_node_instance_info_s *node_instance_info) { + for (size_t i = 0; i < node_instance_info->port_info_count; i++) { + g_node_instance_info_map[node_instance_info->port_infos[i].port] = *node_instance_info; + } +} + +void PluginModuleProxySelfContainerTest::remove_node_instance_info(mmi_node_instance_h node_instance) { + for (auto it = g_node_instance_info_map.begin(); it != g_node_instance_info_map.end();) { + if (it->second.node == node_instance) { + it = g_node_instance_info_map.erase(it); + } else { + it++; + } + } +} + +} // namespace mmi + diff --git a/src/mmi-manager/mmi-self-container.h b/src/mmi-manager/mmi-self-container.h new file mode 100644 index 0000000..6daa1d1 --- /dev/null +++ b/src/mmi-manager/mmi-self-container.h @@ -0,0 +1,132 @@ +/* +* Copyright © 2023 Samsung Electronics co., Ltd. All Rights Reserved. +* +* Permission is hereby granted, free of charge, to any person obtaining a +* copy of this software and associated documentation files (the "Software"), +* to deal in the Software without restriction, including without limitation +* the rights to use, copy, modify, merge, publish, distribute, sublicense, +* and/or sell copies of the Software, and to permit persons to whom the +* Software is furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice (including the next +* paragraph) shall be included in all copies or substantial portions of the +* Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +* DEALINGS IN THE SOFTWARE. +*/ + +#ifndef __MMI_SELF_CONTAINER__ +#define __MMI_SELF_CONTAINER__ + +#include "mmi-plugin-module-proxy.h" +#include "mmi-manager-log.h" + +#include +#include +#include + +/* This file is for testing purpose only */ + +namespace mmi { + +class PluginModuleProxySelfContainerTest; + +struct TestWorkflowPluginModule { +}; + +class TestNodePluginModule { +public: + TestNodePluginModule(); + virtual ~TestNodePluginModule(); + + static int node_initialized_cb(mmi_node_instance_h instance); + static int node_deinitialized_cb(mmi_node_instance_h instance); + static int node_attribute_set_cb(mmi_node_instance_h instance, mmi_attribute_h attribute); + static int node_activated_cb(mmi_node_instance_h instance); + static int node_deactivated_cb(mmi_node_instance_h instance); + static int node_signal_received_cb(mmi_node_instance_h instance, mmi_signal_h signal); + static int port_output_format_requested_cb(mmi_port_instance_h instance, const char *format); + static int port_input_data_received_cb(mmi_port_instance_h instance, mmi_data_h data); + + mmi_node_callbacks default_node_callbacks; + mmi_port_callbacks default_port_callbacks; +}; + +class TestWorkflowPluginModuleWakeuplessCommand : TestWorkflowPluginModule { + +}; + +class TestNodePluginModuleMIC : public TestNodePluginModule { +public: + TestNodePluginModuleMIC(); + static int node_activated_cb(mmi_node_instance_h instance); + + mmi_node_callbacks m_node_callbacks; + mmi_port_callbacks m_audio_port_callbacks; +}; + +class TestNodePluginModuleASR : public TestNodePluginModule { +public: + TestNodePluginModuleASR(); + + static int audio_port_input_data_received_cb(mmi_port_instance_h instance, mmi_data_h data); + + mmi_node_callbacks m_node_callbacks; + mmi_port_callbacks m_audio_port_callbacks; +}; + +class TestNodePluginModuleMatch : public TestNodePluginModule { +public: + TestNodePluginModuleMatch(); + + static int text_port_input_data_received_cb(mmi_port_instance_h instance, mmi_data_h data); + + mmi_node_callbacks m_node_callbacks; + mmi_port_callbacks m_text_port_callbacks; +}; + +class PluginModuleProxySelfContainerTest : public IPluginModuleProxy { +public: + PluginModuleProxySelfContainerTest(); + virtual ~PluginModuleProxySelfContainerTest(); + + virtual function_pointer get_function(const std::string& function_name) override; + + virtual mmi_plugin_module_node_list_s load_node_prototypes() override; + virtual mmi_plugin_module_workflow_list_s load_workflow_prototypes() override; + + virtual void add_node_instance_info(mmi_plugin_module_node_instance_info_s *node_instance_info) override; + virtual void remove_node_instance_info(mmi_node_instance_h node_instance) override; + +private: + TestWorkflowPluginModuleWakeuplessCommand m_workflow_wakeupless_command; + TestNodePluginModuleMIC m_node_mic; + TestNodePluginModuleASR m_node_asr; + TestNodePluginModuleMatch m_node_match; +}; + +class PluginModuleProxyFactorySelfContainerTest : public IPluginModuleProxyFactory { +public: + virtual std::vector get_plugin_module_list() override { + std::vector plugin_module_list; + PluginModuleInfo item{mmi_plugin_module_type_e::SELF_CONTAINED, "self-container-test"}; + plugin_module_list.push_back(item); + return plugin_module_list; + } + virtual std::shared_ptr create( + PluginModuleInfo plugin_module_info) override { + return std::make_shared(); + } +}; + +} // namespace mmi + +#endif //__MMI_SELF_CONTAINER__ + + diff --git a/src/mmi-manager/mmi-workflow-instance-manager.cpp b/src/mmi-manager/mmi-workflow-instance-manager.cpp new file mode 100644 index 0000000..bfbd1fe --- /dev/null +++ b/src/mmi-manager/mmi-workflow-instance-manager.cpp @@ -0,0 +1,261 @@ +/* + * Copyright © 2021 Samsung Electronics co., Ltd. All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +#include "mmi-workflow-instance-manager.h" + +#include "mmi-manager-log.h" + +namespace mmi { + +WorkflowInstanceManager::WorkflowInstanceManager() { +} + +WorkflowInstanceManager::~WorkflowInstanceManager() { + deinitialize(); +} + +void WorkflowInstanceManager::initialize() { +} + +void WorkflowInstanceManager::deinitialize() { + m_workflow_instances.clear(); + + m_node_instance_manager.reset(); + m_prototype_store.reset(); +} + +void WorkflowInstanceManager::set_workflow_prototype_store( + std::shared_ptr prototype_store) { + m_prototype_store = prototype_store; +} + +void WorkflowInstanceManager::set_plugin_module_proxy_provider( + std::shared_ptr plugin_module_proxy_provider) { + m_plugin_module_proxy_provider = plugin_module_proxy_provider; +} + +void WorkflowInstanceManager::set_node_instance_manager( + std::shared_ptr node_instance_manager) { + m_node_instance_manager = node_instance_manager; +} + +std::shared_ptr WorkflowInstanceManager::get_workflow_instance( + std::string client_id, int local_workflow_instance_id) { + for (auto &instance : m_workflow_instances) { + if (instance->get_client_id() == client_id && + instance->get_local_workflow_instance_id() == local_workflow_instance_id) { + return instance; + } + } + + return nullptr; +} + +void WorkflowInstanceManager::on_observer_event( + const COMMUNICATION_CHANNEL_EVENT_TYPE &event, std::any data) { + try { + if (event == COMMUNICATION_CHANNEL_EVENT_TYPE::CONNECTED) { + std::string client_id = std::any_cast(data); + handle_client_connected(client_id); + } else if (event == COMMUNICATION_CHANNEL_EVENT_TYPE::DISCONNECTED) { + std::string client_id = std::any_cast(data); + handle_client_disconnected(client_id); + } else if (event == COMMUNICATION_CHANNEL_EVENT_TYPE::MESSAGE_RECEIVED) { + Message *message = std::any_cast(data); + if (message->message_group == MESSAGE_GROUP::CLIENT) { + handle_client_message(static_cast(message)); + } + } + } catch (const std::bad_any_cast &e) { + LOGE("Failed to cast message data: %s", e.what()); + } +} + +void WorkflowInstanceManager::handle_client_message(ClientMessage *message) { + if (nullptr == message) { + LOGE("Message is null"); + return; + } + + switch(message->message_type) { + case CLIENT_MESSAGE_TYPE::WORKFLOW_INSTANCE_CREATE: { + auto *msg = static_cast(message); + handle_create(msg); + } + break; + case CLIENT_MESSAGE_TYPE::WORKFLOW_INSTANCE_DESTROY: { + auto *msg = static_cast(message); + handle_destroy(msg); + } + break; + case CLIENT_MESSAGE_TYPE::WORKFLOW_INSTANCE_SET_ATTRIBUTE: { + auto *msg = static_cast(message); + handle_set_attribute(msg); + } + break; + case CLIENT_MESSAGE_TYPE::WORKFLOW_INSTANCE_ACTIVATE: { + auto *msg = static_cast(message); + handle_activate(msg); + } + break; + case CLIENT_MESSAGE_TYPE::WORKFLOW_INSTANCE_DEACTIVATE: { + auto *msg = static_cast(message); + handle_deactivate(msg); + } + break; + } +} + +void WorkflowInstanceManager::handle_client_connected(std::string client_id) { + LOGD("%s connected", client_id.c_str()); +} + +void WorkflowInstanceManager::handle_client_disconnected(std::string client_id) { + LOGD("%s disconnected", client_id.c_str()); + + auto it = m_workflow_instances.begin(); + while (it != m_workflow_instances.end()) { + if ((*it)->get_client_id() == client_id) { + (*it)->deinitialize(); + it = m_workflow_instances.erase(it); + } else { + ++it; + } + } +} + + +void WorkflowInstanceManager::handle_create( + ClientMessageWorkflowInstanceCreate *message) { + if (nullptr == message) { + LOGE("Message is null"); + return; + } + + std::shared_ptr instance = + get_workflow_instance(message->sender, message->local_workflow_instance_id); + if (instance) { + LOGE("Workflow instance already exists"); + return; + } + + if (nullptr == m_prototype_store) { + LOGE("Workflow prototype store is not set"); + return; + } + + if (nullptr == m_node_instance_manager) { + LOGE("Node instance manager is not set"); + return; + } + + instance = std::make_shared(); + if (instance) { + instance->set_client_id(message->sender); + instance->set_local_workflow_instance_id(message->local_workflow_instance_id); + instance->set_type(message->workflow_type); + + instance->set_workflow_prototype_store(m_prototype_store); + instance->set_plugin_module_proxy_provider(m_plugin_module_proxy_provider); + instance->set_node_instance_manager(m_node_instance_manager); + + instance->initialize(); + + m_workflow_instances.push_back(instance); + + WorkflowEventWorkflowCreated event; + event.workflow_instance = instance; + notify_observers(WORKFLOW_EVENT_TYPE::WORKFLOW_CREATED, &event); + } else { + LOGE("Failed to create workflow instance"); + } +} + +void WorkflowInstanceManager::handle_destroy( + ClientMessageWorkflowInstanceDestroy *message) { + if (nullptr == message) { + LOGE("Message is null"); + return; + } + + std::shared_ptr instance = + get_workflow_instance(message->sender, message->local_workflow_instance_id); + if (instance) { + instance->deinitialize(); + } + + auto it = m_workflow_instances.begin(); + while (it != m_workflow_instances.end()) { + if ((*it)->get_client_id() == message->sender && + (*it)->get_local_workflow_instance_id() == message->local_workflow_instance_id) { + it = m_workflow_instances.erase(it); + } else { + ++it; + } + } +} + +void WorkflowInstanceManager::handle_set_attribute( + ClientMessageWorkflowInstanceSetAttribute *message) { + if (nullptr == message) { + LOGE("Message is null"); + return; + } + + std::shared_ptr instance = + get_workflow_instance(message->sender, message->local_workflow_instance_id); + if (instance) { + instance->set_attribute(message->attribute); + } +} + +void WorkflowInstanceManager::handle_activate( + ClientMessageWorkflowInstanceActivate *message) { + if (nullptr == message) { + LOGE("Message is null"); + return; + } + + std::shared_ptr instance = + get_workflow_instance(message->sender, message->local_workflow_instance_id); + if (instance) { + instance->activate(); + } +} + +void WorkflowInstanceManager::handle_deactivate( + ClientMessageWorkflowInstanceDeactivate *message) { + if (nullptr == message) { + LOGE("Message is null"); + return; + } + + std::shared_ptr instance = + get_workflow_instance(message->sender, message->local_workflow_instance_id); + if (instance) { + instance->deactivate(); + } +} + +} // namespace mmi + diff --git a/src/mmi-manager/mmi-workflow-instance-manager.h b/src/mmi-manager/mmi-workflow-instance-manager.h new file mode 100644 index 0000000..5e32912 --- /dev/null +++ b/src/mmi-manager/mmi-workflow-instance-manager.h @@ -0,0 +1,88 @@ +/* +* Copyright © 2023 Samsung Electronics co., Ltd. All Rights Reserved. +* +* Permission is hereby granted, free of charge, to any person obtaining a +* copy of this software and associated documentation files (the "Software"), +* to deal in the Software without restriction, including without limitation +* the rights to use, copy, modify, merge, publish, distribute, sublicense, +* and/or sell copies of the Software, and to permit persons to whom the +* Software is furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice (including the next +* paragraph) shall be included in all copies or substantial portions of the +* Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +* DEALINGS IN THE SOFTWARE. +*/ + +#ifndef __MMI_WORKFLOW_INSTANCE_MANAGER_H__ +#define __MMI_WORKFLOW_INSTANCE_MANAGER_H__ + +#include +#include +#include + +#include "mmi-communication-channel.h" +#include "mmi-event-observer.h" +#include "mmi-node-instance-manager.h" +#include "mmi-plugin-module-proxy.h" +#include "mmi-workflow-instance.h" +#include "mmi-workflow-prototype-manager.h" +#include "mmi-workflow-event.h" + +namespace mmi { + +using namespace communication; + +class WorkflowInstanceManager : + public SimpleEventObserver, + public SimpleEventObservable { +public: + WorkflowInstanceManager(); + virtual ~WorkflowInstanceManager(); + + void initialize(); + void deinitialize(); + + void set_workflow_prototype_store( + std::shared_ptr prototype_store); + void set_plugin_module_proxy_provider( + std::shared_ptr plugin_module_proxy_provider); + void set_node_instance_manager( + std::shared_ptr node_instance_manager); + + std::shared_ptr get_workflow_instance( + std::string client_id, int local_workflow_instance_id); + + void on_observer_event( + const COMMUNICATION_CHANNEL_EVENT_TYPE &event, std::any data) override; + +private: + void handle_client_message(ClientMessage *message); + + void handle_client_connected(std::string client_id); + void handle_client_disconnected(std::string client_id); + + void handle_create(ClientMessageWorkflowInstanceCreate *message); + void handle_destroy(ClientMessageWorkflowInstanceDestroy *message); + void handle_set_attribute(ClientMessageWorkflowInstanceSetAttribute *message); + void handle_activate(ClientMessageWorkflowInstanceActivate *message); + void handle_deactivate(ClientMessageWorkflowInstanceDeactivate *message); +protected: + std::shared_ptr m_prototype_store; + std::shared_ptr m_plugin_module_proxy_provider; + std::shared_ptr m_node_instance_manager; + + std::vector> m_workflow_instances; +}; + +} // namespace mmi + +#endif //__MMI_WORKFLOW_INSTANCE_MANAGER_H__ + diff --git a/src/mmi-manager/mmi-workflow-instance.cpp b/src/mmi-manager/mmi-workflow-instance.cpp new file mode 100644 index 0000000..e26b78f --- /dev/null +++ b/src/mmi-manager/mmi-workflow-instance.cpp @@ -0,0 +1,358 @@ +/* + * Copyright © 2023 Samsung Electronics co., Ltd. All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +#include "mmi-workflow-instance.h" +#include "mmi-manager-log.h" +#include "mmi-workflow-output-event.h" + +#include +namespace mmi { + +WorkflowInstance::WorkflowInstance() { +} + +WorkflowInstance::~WorkflowInstance() { +} + +void WorkflowInstance::set_client_id(std::string client_id) { + m_client_id = client_id; +} + +std::string WorkflowInstance::get_client_id() { + return m_client_id; +} + +void WorkflowInstance::set_local_workflow_instance_id(int local_workflow_instance_id) { + m_local_workflow_instance_id = local_workflow_instance_id; +} + +int WorkflowInstance::get_local_workflow_instance_id() { + return m_local_workflow_instance_id; +} + +void WorkflowInstance::set_type(mmi_standard_workflow_type_e type) { + m_type = type; +} + +mmi_standard_workflow_type_e WorkflowInstance::get_type() { + return m_type; +} + +void WorkflowInstance::set_workflow_prototype_store( + std::shared_ptr prototype_store) { + m_prototype_store = prototype_store; +} + +void WorkflowInstance::set_plugin_module_proxy_provider( + std::shared_ptr plugin_module_proxy_provider) { + m_plugin_module_proxy_provider = plugin_module_proxy_provider; +} + +void WorkflowInstance::set_node_instance_manager( + std::shared_ptr node_instance_manager) { + m_node_instance_manager = node_instance_manager; +} + + +void WorkflowInstance::initialize() { + if (WORKFLOW_INSTANCE_STATE::NONE != m_state) { + _E("Workflow instance state is not NONE"); + return; + } + + if (nullptr == m_prototype_store) { + _E("Workflow prototype store is not set"); + return; + } + + if (nullptr == m_node_instance_manager) { + _E("Node instance manager is not set"); + return; + } + + if (nullptr == m_plugin_module_proxy_provider) { + _E("Plugin module proxy provider is not set"); + return; + } + + std::shared_ptr prototype = m_prototype_store->get_workflow_prototype(m_type); + if (nullptr == prototype) { + _E("Could not find prototype information from store"); + return; + } + + /* Owning this shared pointer guarantees that the callback functions are + * valid until the node instance is destroyed. */ + m_plugin_module_proxy = m_plugin_module_proxy_provider->get_plugin_module_proxy( + prototype->get_plugin_module_info()); + + /* Create node instances based on prototype information */ + std::vector node_infos = prototype->get_node_infos(); + for (auto &node_info : node_infos) { + std::shared_ptr node_instance = + m_node_instance_manager->create_node_instance( + node_info.node_type, node_info.node_sub_type); + if (nullptr == node_instance) { + _E("Failed to create node instance : %s", node_info.name.c_str()); + } else { + m_node_instances[node_info.name] = node_instance; + _D("Created node instance : %s(%p)", + node_info.name.c_str(), node_instance.get()); + /* Listen to plugin module events */ + std::shared_ptr proxy = node_instance->get_plugin_module_proxy(); + if (proxy) { + proxy->add_observer(this); + } + } + } + + std::vector link_infos = prototype->get_link_infos(); + for (auto &link_info : link_infos) { + try { + std::shared_ptr from_node_instance = + m_node_instances.at(link_info.from_node_name); + std::shared_ptr to_node_instance = + m_node_instances.at(link_info.to_node_name); + + _D("Linking two nodes : %s(%p) -> %s(%p)", + link_info.from_node_name.c_str(), from_node_instance.get(), + link_info.to_node_name.c_str(), to_node_instance.get()); + m_node_instance_manager->link_node_instances( + from_node_instance, to_node_instance, + link_info.from_port_name, link_info.to_port_name); + } catch (std::out_of_range& e) { + _E("Failed to find node instance : %s -> %s", + link_info.from_node_name.c_str(), link_info.to_node_name.c_str()); + } + } + + std::vector output_assignment_infos = + prototype->get_output_assignment_infos(); + + for (auto &output_assignment_info : output_assignment_infos) { + try { + std::shared_ptr from_node_instance = + m_node_instances.at(output_assignment_info.from_node_name); + + std::shared_ptr output_data_gateway = + std::make_shared(output_assignment_info.output_name); + + + _D("Adding Gateway : node(%s), port(%s) -> output name(%s)", + output_assignment_info.from_node_name.c_str(), + output_assignment_info.from_port_name.c_str(), + output_assignment_info.output_name.c_str()); + + mmi_data_transfer_callback callback = [this](mmi_data_h data, std::string name) { + _D("Data received from Data Gateway"); + unsigned char *data_bytes = nullptr; + size_t data_byte_size = 0; + mmi_data_to_bytes(data, &data_bytes, &data_byte_size); + + bundle *encoded = bundle_create(); + bundle_add_byte(encoded, "data", data_bytes, data_byte_size); + + WorkflowOutputEventOutputGenerated event; + event.source_name = name; + event.data = encoded; + event.client_id = get_client_id(); + event.local_workflow_instance_id = get_local_workflow_instance_id(); + this->notify_observers(WORKFLOW_OUTPUT_EVENT_TYPE::OUTPUT_GENERATED, &event); + }; + output_data_gateway->set_callback(callback); + + m_data_gateways.push_back(output_data_gateway); + + from_node_instance->add_data_gateway( + output_assignment_info.from_port_name, + output_data_gateway); + } catch (std::out_of_range& e) { + _E("Failed to find node instance : %s", + output_assignment_info.from_node_name.c_str()); + } + } + + for (auto &node_instance : m_node_instances) { + _D("Node instance in m_node_instances : %s(%p)", + node_instance.first.c_str(), node_instance.second.get()); + } + + set_attribute_default_values(); + + m_state = WORKFLOW_INSTANCE_STATE::INITIALIZED; +} + +void WorkflowInstance::deinitialize() { + /* If the workflow instance is running, stop it first */ + if (WORKFLOW_INSTANCE_STATE::ACTIVATED == m_state) { + deactivate(); + } + if (WORKFLOW_INSTANCE_STATE::INITIALIZED != m_state) { + _E("Workflow instance state is not INITIALIZED"); + return; + } + + for (auto &node_instance : m_node_instances) { + std::shared_ptr proxy = (node_instance.second)->get_plugin_module_proxy(); + if (proxy) { + proxy->remove_observer(this); + } + m_node_instance_manager->destroy_node_instance(node_instance.second); + } + m_node_instances.clear(); +} + +void WorkflowInstance::activate() { + if (WORKFLOW_INSTANCE_STATE::INITIALIZED != m_state) { + _E("Workflow instance state is not INITIALIZED"); + return; + } + + _D("Activating workflow instance"); + + for (auto &node_instance : m_node_instances) { + _D("Activating node instance : %s(%p)", + node_instance.first.c_str(), node_instance.second.get()); + (node_instance.second)->activate(); + } + + m_state = WORKFLOW_INSTANCE_STATE::ACTIVATED; +} + +void WorkflowInstance::deactivate() { + if (WORKFLOW_INSTANCE_STATE::ACTIVATED != m_state) { + _E("Workflow instance state is not ACTIVATED"); + return; + } + + _D("Deactivating workflow instance"); + + for (auto &node_instance : m_node_instances) { + (node_instance.second)->deactivate(); + } + + m_state = WORKFLOW_INSTANCE_STATE::INITIALIZED; +} + +bool WorkflowInstance::set_attribute(mmi_attribute_h attribute) { + _D("Setting attribute : %p", attribute); + + std::shared_ptr prototype = m_prototype_store->get_workflow_prototype(m_type); + if (nullptr == prototype) { + _E("Could not find prototype information from store"); + return false; + } + + char *name = nullptr; + if (MMI_ERROR_NONE == mmi_attribute_get_name(attribute, &name)) { + std::vector attribute_assignment_infos = + prototype->get_attribute_assignment_infos(); + + if (!name) { + _E("Attribute name is null"); + return false; + } + auto it = std::find_if(attribute_assignment_infos.begin(), attribute_assignment_infos.end(), + [name](const AttributeAssignmentInfo& info) { + return (info.attribute_name.compare(name) == 0); + }); + if (it != attribute_assignment_infos.end()) { + _D("Found attribute assignment info : %s", name); + try { + std::shared_ptr target_node_instance = + m_node_instances.at(it->target_node_name); + if (target_node_instance) { + mmi_attribute_set_name(attribute, it->target_attribute_name.c_str()); + target_node_instance->set_attribute(attribute); + } else { + _E("Could not find target node instance : %s", it->target_node_name.c_str()); + } + } catch (std::out_of_range& e) { + _E("Failed to find node instance : %s", it->target_node_name.c_str()); + } + } else { + _E("Could not find attribute assignment info : %s", name); + } + free(name); + name = nullptr; + } + + return true; +} + +void WorkflowInstance::on_observer_event( + const PLUGIN_MODULE_EVENT_TYPE &event, std::any data) { + try { + if (event == PLUGIN_MODULE_EVENT_TYPE::SWITCH_EVENT_EMITTED) { + PluginModuleEventSwitchEventEmitted *switch_event = std::any_cast(data); + if (switch_event != nullptr) { + bool found = false; + for (auto &node_instance : m_node_instances) { + if (switch_event->node_instance == node_instance.second.get()) { + found = true; + break; + } + } + if (!found) { + /* This event is not for this workflow instance */ + return; + } + _D("Switch Event emitted for %s %d", switch_event->controllee.c_str(), switch_event->state); + std::shared_ptr node_instance = m_node_instances.at(switch_event->controllee); + if (node_instance) { + if (switch_event->state) { + node_instance->resume(); + } else { + node_instance->suspend(); + } + } else { + _E("Could not find node instance : %s", switch_event->controllee.c_str()); + } + } + } + } catch (const std::bad_any_cast &e) { + LOGE("Failed to cast message data: %s", e.what()); + } +} + +bool WorkflowInstance::set_attribute_default_values() { + _D("Setting attribute default value"); + + std::shared_ptr prototype = m_prototype_store->get_workflow_prototype(m_type); + if (nullptr == prototype) { + _E("Could not find prototype information from store"); + return false; + } + + std::vector attribute_default_value_infos = + prototype->get_attribute_default_value_infos(); + + for (auto &default_value_info : attribute_default_value_infos) { + set_attribute(default_value_info.default_value); + } + + return true; +} + +} // namespace mmi + diff --git a/src/mmi-manager/mmi-workflow-instance.h b/src/mmi-manager/mmi-workflow-instance.h new file mode 100644 index 0000000..42ef79c --- /dev/null +++ b/src/mmi-manager/mmi-workflow-instance.h @@ -0,0 +1,100 @@ +/* +* Copyright © 2023 Samsung Electronics co., Ltd. All Rights Reserved. +* +* Permission is hereby granted, free of charge, to any person obtaining a +* copy of this software and associated documentation files (the "Software"), +* to deal in the Software without restriction, including without limitation +* the rights to use, copy, modify, merge, publish, distribute, sublicense, +* and/or sell copies of the Software, and to permit persons to whom the +* Software is furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice (including the next +* paragraph) shall be included in all copies or substantial portions of the +* Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +* DEALINGS IN THE SOFTWARE. +*/ + +#ifndef __MMI_WORKFLOW_INSTANCE_H__ +#define __MMI_WORKFLOW_INSTANCE_H__ + +#include +#include +#include + +#include "mmi-plugin-module-event.h" +#include "mmi-plugin-module-proxy.h" +#include "mmi-manager-log.h" +#include "mmi-node-instance-manager.h" +#include "mmi-workflow-prototype-manager.h" +#include "mmi-workflow-output-event.h" +namespace mmi { + +enum class WORKFLOW_INSTANCE_STATE { + NONE, + INITIALIZED, + ACTIVATED, +}; + +class WorkflowInstance : + public SimpleEventObserver, + public SimpleEventObservable { +public: + WorkflowInstance(); + virtual ~WorkflowInstance(); + + void set_client_id(std::string client_id); + std::string get_client_id(); + + void set_local_workflow_instance_id(int local_workflow_instance_id); + int get_local_workflow_instance_id(); + + void set_type(mmi_standard_workflow_type_e type); + mmi_standard_workflow_type_e get_type(); + + void set_workflow_prototype_store( + std::shared_ptr prototype_store); + void set_plugin_module_proxy_provider( + std::shared_ptr plugin_module_proxy_provider); + void set_node_instance_manager( + std::shared_ptr node_instance_manager); + + void initialize(); + void deinitialize(); + + void activate(); + void deactivate(); + + bool set_attribute(mmi_attribute_h attribute); + bool set_attribute_default_values(); + + void on_observer_event( + const PLUGIN_MODULE_EVENT_TYPE &event, std::any data) override; +private: + std::shared_ptr m_prototype_store; + std::shared_ptr m_plugin_module_proxy_provider; + std::shared_ptr m_node_instance_manager; + + std::shared_ptr m_plugin_module_proxy; + + std::string m_client_id; + int m_local_workflow_instance_id{0}; + + mmi_standard_workflow_type_e m_type{MMI_STANDARD_WORKFLOW_NONE}; + WORKFLOW_INSTANCE_STATE m_state{WORKFLOW_INSTANCE_STATE::NONE}; + + std::map> m_node_instances; + + std::vector> m_data_gateways; +}; + +} // namespace mmi + +#endif //__MMI_WORKFLOW_INSTANCE_H__ + diff --git a/src/mmi-manager/mmi-workflow-prototype-manager.cpp b/src/mmi-manager/mmi-workflow-prototype-manager.cpp new file mode 100644 index 0000000..5aa3977 --- /dev/null +++ b/src/mmi-manager/mmi-workflow-prototype-manager.cpp @@ -0,0 +1,76 @@ +/* + * Copyright © 2021 Samsung Electronics co., Ltd. All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +#include "mmi-workflow-prototype-manager.h" + +#include "mmi-manager-log.h" + +namespace mmi { + +WorkflowPrototypeManager::WorkflowPrototypeManager() { +} + +WorkflowPrototypeManager::~WorkflowPrototypeManager() { +} + +bool WorkflowPrototypeManager::add_workflow_prototype(std::shared_ptr prototype) { + if (!prototype->is_valid()) { + _E("ERROR: invalid workflow prototype"); + return false; + } + + /* check if we already have a prototype with the same type */ + for (auto& elem : m_prototypes) { + if (elem->get_type() == prototype->get_type()) { + _E("ERROR: prototype with the same type already exists : %d", prototype->get_type()); + return false; + } + } + + m_prototypes.push_back(prototype); + + return true; +} + +std::shared_ptr WorkflowPrototypeManager::get_workflow_prototype( + mmi_standard_workflow_type_e type) { + std::shared_ptr ret; + + for (auto& elem : m_prototypes) { + if (elem->get_type() == type) { + ret = elem; + break; + } + } + + return ret; +} + +void WorkflowPrototypeManager::initialize() { +} + +void WorkflowPrototypeManager::deinitialize() { +} + +} // namespace mmi + diff --git a/src/mmi-manager/mmi-workflow-prototype-manager.h b/src/mmi-manager/mmi-workflow-prototype-manager.h new file mode 100644 index 0000000..270ed4c --- /dev/null +++ b/src/mmi-manager/mmi-workflow-prototype-manager.h @@ -0,0 +1,58 @@ +/* +* Copyright © 2023 Samsung Electronics co., Ltd. All Rights Reserved. +* +* Permission is hereby granted, free of charge, to any person obtaining a +* copy of this software and associated documentation files (the "Software"), +* to deal in the Software without restriction, including without limitation +* the rights to use, copy, modify, merge, publish, distribute, sublicense, +* and/or sell copies of the Software, and to permit persons to whom the +* Software is furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice (including the next +* paragraph) shall be included in all copies or substantial portions of the +* Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +* DEALINGS IN THE SOFTWARE. +*/ + +#ifndef __MMI_WORKFLOW_PROTOTYPE_MANAGER_H__ +#define __MMI_WORKFLOW_PROTOTYPE_MANAGER_H__ + +#include "mmi-workflow.h" +#include "mmi-workflow-prototype.h" + +#include + +namespace mmi { + +struct IWorkflowPrototypeStore { + virtual bool add_workflow_prototype(std::shared_ptr prototype) = 0; + virtual std::shared_ptr get_workflow_prototype(mmi_standard_workflow_type_e type) = 0; +}; + +class WorkflowPrototypeManager : public IWorkflowPrototypeStore { +public: + WorkflowPrototypeManager(); + virtual ~WorkflowPrototypeManager(); + + virtual bool add_workflow_prototype(std::shared_ptr prototype) override; + virtual std::shared_ptr get_workflow_prototype(mmi_standard_workflow_type_e type) override; + + void initialize(); + void deinitialize(); + +protected: + std::vector> m_prototypes; +}; + +} // namespace mmi + +#endif //__MMI_WORKFLOW_PROTOTYPE_MANAGER_H__ + + diff --git a/src/mmi-manager/mmi-workflow-prototype.cpp b/src/mmi-manager/mmi-workflow-prototype.cpp new file mode 100644 index 0000000..ca66168 --- /dev/null +++ b/src/mmi-manager/mmi-workflow-prototype.cpp @@ -0,0 +1,107 @@ +/* + * Copyright © 2021 Samsung Electronics co., Ltd. All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +#include "mmi-workflow-prototype.h" + +#include "mmi-manager-log.h" + +namespace mmi { + +WorkflowPrototype::WorkflowPrototype() { +} + +WorkflowPrototype::~WorkflowPrototype() { + for (auto& attribute_default_value_info : m_attribute_default_value_infos) { + mmi_attribute_destroy(attribute_default_value_info.default_value); + } +} + +bool WorkflowPrototype::is_valid() { + if (!m_type.has_value()) { + _E("ERROR: type is not set"); + return false; + } + + if (m_plugin_module_info.plugin_module_identifier.empty()) { + _E("ERROR: plugin module identifier is not set"); + return false; + } + + return true; +} + +void WorkflowPrototype::set_type(mmi_standard_workflow_type_e type) { + m_type = type; +} + +bool WorkflowPrototype::add_node_info(NodeInfo node_info) { + m_node_infos.push_back(node_info); + return true; +} + +bool WorkflowPrototype::add_link_info(LinkInfo link_info) { + m_link_infos.push_back(link_info); + return true; +} + +bool WorkflowPrototype::add_attribute_assignment_info(AttributeAssignmentInfo attribute_assignment_info) { + m_attribute_assignment_infos.push_back(attribute_assignment_info); + return true; +} + +bool WorkflowPrototype::add_attribute_default_value_info(AttributeDefaultValueInfo attribute_default_value_info) { + m_attribute_default_value_infos.push_back(attribute_default_value_info); + return true; +} + +bool WorkflowPrototype::add_output_assignment_info(OutputAssignmentInfo output_assignment_info) { + m_output_assignment_infos.push_back(output_assignment_info); + return true; +} + +mmi_standard_workflow_type_e WorkflowPrototype::get_type() { + return m_type.value(); +} + +std::vector WorkflowPrototype::get_node_infos() { + return m_node_infos; +} + +std::vector WorkflowPrototype::get_link_infos() { + return m_link_infos; +} + +std::vector WorkflowPrototype::get_attribute_assignment_infos() { + return m_attribute_assignment_infos; +} + +std::vector WorkflowPrototype::get_attribute_default_value_infos() { + return m_attribute_default_value_infos; +} + +std::vector WorkflowPrototype::get_output_assignment_infos() { + return m_output_assignment_infos; +} + +} // namespace mmi + diff --git a/src/mmi-manager/mmi-workflow-prototype.h b/src/mmi-manager/mmi-workflow-prototype.h new file mode 100644 index 0000000..85058b1 --- /dev/null +++ b/src/mmi-manager/mmi-workflow-prototype.h @@ -0,0 +1,108 @@ +/* +* Copyright © 2023 Samsung Electronics co., Ltd. All Rights Reserved. +* +* Permission is hereby granted, free of charge, to any person obtaining a +* copy of this software and associated documentation files (the "Software"), +* to deal in the Software without restriction, including without limitation +* the rights to use, copy, modify, merge, publish, distribute, sublicense, +* and/or sell copies of the Software, and to permit persons to whom the +* Software is furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice (including the next +* paragraph) shall be included in all copies or substantial portions of the +* Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +* DEALINGS IN THE SOFTWARE. +*/ + +#ifndef __MMI_WORKFLOW_PROTOTYPE_H__ +#define __MMI_WORKFLOW_PROTOTYPE_H__ + +#include "mmi-workflow.h" +#include "mmi-node-prototype.h" + +#include + +namespace mmi { + +typedef struct { + std::string name; + mmi_standard_node_type_e node_type; + mmi_standard_node_sub_type_e node_sub_type; +} NodeInfo; + +typedef struct { + std::string from_node_name; + std::string from_port_name; + std::string to_node_name; + std::string to_port_name; +} LinkInfo; + +typedef struct { + std::string attribute_name; + std::string target_node_name; + std::string target_attribute_name; +} AttributeAssignmentInfo; + +typedef struct { + mmi_attribute_h default_value; +} AttributeDefaultValueInfo; + +typedef struct { + std::string output_name; + std::string from_node_name; + std::string from_port_name; +} OutputAssignmentInfo; + +class WorkflowPrototype { +public: + WorkflowPrototype(); + virtual ~WorkflowPrototype(); + + bool is_valid(); + + void set_plugin_module_info(PluginModuleInfo plugin_module_info) { + m_plugin_module_info = plugin_module_info; + } + PluginModuleInfo get_plugin_module_info() { + return m_plugin_module_info; + } + + void set_type(mmi_standard_workflow_type_e type); + + bool add_node_info(NodeInfo node_info); + bool add_link_info(LinkInfo link_info); + bool add_attribute_assignment_info(AttributeAssignmentInfo attribute_assignment_info); + bool add_attribute_default_value_info(AttributeDefaultValueInfo attribute_default_value_info); + bool add_output_assignment_info(OutputAssignmentInfo output_assignment_info); + + mmi_standard_workflow_type_e get_type(); + + std::vector get_node_infos(); + std::vector get_link_infos(); + std::vector get_attribute_assignment_infos(); + std::vector get_attribute_default_value_infos(); + std::vector get_output_assignment_infos(); +private: + PluginModuleInfo m_plugin_module_info; + + std::optional m_type; + + std::vector m_node_infos; + std::vector m_link_infos; + std::vector m_attribute_assignment_infos; + std::vector m_attribute_default_value_infos; + std::vector m_output_assignment_infos; +}; + +} // namespace mmi + +#endif //__MMI_WORKFLOW_PROTOTYPE_H__ + + diff --git a/src/mmi.c b/src/mmi.c deleted file mode 100644 index 9824c00..0000000 --- a/src/mmi.c +++ /dev/null @@ -1,135 +0,0 @@ -/* - * Copyright (c) 2022 Samsung Electronics Co., Ltd All Rights Reserved - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT 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 "mmi.h" -#include "mmi-client.h" -#include "mmi-dbg.h" -#include "mmi-ipc.h" - -MMI_API int mmi_initialize(void) -{ - int ret = mmi_client_create(); - if(ret != MMI_ERROR_NONE) { - LOGE("Fail to create mmi client(%d)", ret); - return MMI_ERROR_NOT_SUPPORTED; - } - - return MMI_ERROR_NONE; -} - -MMI_API int mmi_deinitialize(void) -{ - int ret = mmi_client_destroy(); - if(ret != MMI_ERROR_NONE) { - LOGE("Fail to destroy mmi client(%d)", ret); - return MMI_ERROR_NOT_SUPPORTED; - } - - return MMI_ERROR_NONE; -} - -MMI_API int mmi_set_result_cb(int input_event_type, mmi_result_cb callback, void* user_data) -{ - LOGI("Set result cb about input event type(%d)", input_event_type); - - int ret = mmi_client_set_result_cb(input_event_type, callback, user_data); - if(ret != MMI_ERROR_NONE) { - LOGE("Fail to set result cb(%d)", ret); - return ret; - } - - return MMI_ERROR_NONE; -} - -MMI_API int mmi_activate_input_event(int input_event_type) -{ - LOGI("Activate input event(%d)", input_event_type); - - int ret; - GList* iter = NULL; - mmi_result_cb_s *data = NULL; - mmi_handle client = mmi_client_get(); - - if (g_list_length(client->result_cb_list) > 0) { - LOGD("Check length of callback lists = %d", g_list_length(client->result_cb_list)); - - iter = g_list_first(client->result_cb_list); - - while (iter != NULL) { - data = iter->data; - - if (data != NULL) { - int type = data->input_event_type; - - if (type == input_event_type) { - ret = mmi_ipc_activate_input_event(input_event_type); - if(ret != MMI_ERROR_NONE) { - LOGE("Fail to activate input event(%d)", ret); - return ret; - } - - LOGD("Activate input event(%d)", type); - return MMI_ERROR_NONE; - } - } - - iter = g_list_next(iter); - } - } - - LOGE("No match callback about input event type(%d)", input_event_type); - return MMI_ERROR_INVALID_PARAMETER; -} - -MMI_API int mmi_deactivate_input_event(int input_event_type) -{ - LOGI("Deactivate input event(%d)", input_event_type); - - int ret; - GList* iter = NULL; - mmi_result_cb_s *data = NULL; - mmi_handle client = mmi_client_get(); - - if (g_list_length(client->result_cb_list) > 0) { - LOGD("Check length of callback lists = %d", g_list_length(client->result_cb_list)); - - iter = g_list_first(client->result_cb_list); - - while (NULL != iter) { - data = iter->data; - - if (NULL != data) { - int type = data->input_event_type; - - if (type == input_event_type) { - ret = mmi_ipc_deactivate_input_event(input_event_type); - if(ret != MMI_ERROR_NONE) { - LOGE("Fail to deactivate input event(%d)", ret); - return ret; - } - - return MMI_ERROR_NONE; - } - } - - iter = g_list_next(iter); - } - } - - LOGE("Fail to deactivate input event type(%d)", input_event_type); - return MMI_ERROR_INVALID_PARAMETER; -} diff --git a/src/mmi.h b/src/mmi.h deleted file mode 100644 index 0a8683b..0000000 --- a/src/mmi.h +++ /dev/null @@ -1,67 +0,0 @@ -/* - * Copyright (c) 2022 Samsung Electronics Co., Ltd All Rights Reserved - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT 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 __MMI_H__ -#define __MMI_H__ - -#include - -#define MMI_API __attribute__ ((visibility("default"))) - -typedef enum { - MMI_RESULT_NONE, - MMI_RESULT_FAIL, - MMI_RESULT_SUCCESS -} mmi_result_e; - -#define TIZEN_ERROR_MMI -0x030F0000 - -typedef enum { - MMI_ERROR_NONE = TIZEN_ERROR_NONE, /**< Successful */ - MMI_ERROR_OUT_OF_MEMORY = TIZEN_ERROR_OUT_OF_MEMORY, /**< Out of Memory */ - MMI_ERROR_IO_ERROR = TIZEN_ERROR_IO_ERROR, /**< I/O error */ - MMI_ERROR_INVALID_PARAMETER = TIZEN_ERROR_INVALID_PARAMETER, /**< Invalid parameter */ - MMI_ERROR_OUT_OF_NETWORK = TIZEN_ERROR_NETWORK_DOWN, /**< Network is down */ - MMI_ERROR_TIMED_OUT = TIZEN_ERROR_TIMED_OUT, /**< No answer from the daemon */ - MMI_ERROR_PERMISSION_DENIED = TIZEN_ERROR_PERMISSION_DENIED, /**< Permission denied */ - MMI_ERROR_NOT_SUPPORTED = TIZEN_ERROR_NOT_SUPPORTED, /**< MMI NOT supported */ - MMI_ERROR_OPERATION_FAILED = TIZEN_ERROR_MMI | 0x01, /**< Operation failed */ -} mmi_error_e; - -typedef void (*mmi_result_cb)(int input_event_type, const char *result_out, void *user_data); - -typedef enum { - MMI_INPUT_EVENT_TYPE_NONE, - MMI_INPUT_EVENT_TYPE_VOICE_TOUCH, - MMI_INPUT_EVENT_TYPE_VOICE_RECOGNITION, -} mmi_input_event_type_e; - -#ifdef __cplusplus -extern "C" { -#endif - -MMI_API int mmi_initialize(void); -MMI_API int mmi_deinitialize(void); -MMI_API int mmi_set_result_cb(int input_event_type, mmi_result_cb callback, void* user_data); -MMI_API int mmi_activate_input_event(int input_event_type); -MMI_API int mmi_deactivate_input_event(int input_event_type); - -#ifdef __cplusplus -} -#endif - -#endif //__MMI_H__ diff --git a/src/mmi/meson.build b/src/mmi/meson.build new file mode 100644 index 0000000..4260c90 --- /dev/null +++ b/src/mmi/meson.build @@ -0,0 +1,69 @@ +mmi_srcs = [ + 'mmi.cpp', + 'mmi-workflow.cpp', + 'mmi-workflow-instance.cpp', + 'mmi-workflow-script.cpp', + 'mmi-ipc-tidl.h', + 'mmi-ipc-tidl.cpp', + 'mmi-log.h', + 'mmi_proxy.h', + 'mmi_proxy.c', + 'mmi-primitive-value.cpp', + 'mmi-data.cpp', + 'mmi-attribute.cpp', + 'mmi-node.cpp', + 'mmi-node-source.cpp', + 'mmi-node-processor.cpp', + 'mmi-node-logic.cpp', + 'mmi-node-controller.cpp', + 'mmi-port.cpp', + 'mmi-signal.cpp', + 'mmi-plugin-storage.cpp', + ] + +glib_dep = dependency('glib-2.0', method : 'pkg-config') +gio_dep = dependency('gio-2.0', method : 'pkg-config') +bundle_dep = dependency('bundle', method : 'pkg-config') +dlog_dep = dependency('dlog', method : 'pkg-config') +rpc_port_dep = dependency('rpc-port', method : 'pkg-config') +libtzplatform_config_dep = dependency('libtzplatform-config') +ecore_dep = dependency('ecore', method : 'pkg-config') + +mmi_deps = [ + ecore_dep, + glib_dep, + gio_dep, + bundle_dep, + dlog_dep, + rpc_port_dep, + libtzplatform_config_dep] + +mmi_include_dirs = include_directories( + '.', + '../common', + '../../capi' + ) + +mmi_lib = shared_library( + 'mmi', + mmi_srcs, + dependencies : [mmi_deps], + include_directories : [mmi_include_dirs], + version : meson.project_version(), + install : true + ) + +pkgconfig = import('pkgconfig') +pkgconfig.generate( + filebase : 'mmi', + name : 'mmi', + description : 'Multi-modal Interaction Framework Library', + version : meson.project_version(), + libraries : mmi_lib + ) + +mmi_declared_dep = declare_dependency( + link_with : mmi_lib, + dependencies : [mmi_deps], + include_directories : [mmi_include_dirs] + ) diff --git a/src/mmi/mmi-attribute.cpp b/src/mmi/mmi-attribute.cpp new file mode 100644 index 0000000..f49bc0c --- /dev/null +++ b/src/mmi/mmi-attribute.cpp @@ -0,0 +1,301 @@ +/* + * Copyright (c) 2023 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 "mmi-log.h" + +#include + +constexpr size_t MAX_ATTRIBUTE_NAME_SIZE = 256; + +struct mmi_attribute_s { + char name[MAX_ATTRIBUTE_NAME_SIZE]; + mmi_primitive_value_h value; +}; + + +static constexpr size_t g_unit_size = sizeof(mmi_primitive_value_h); + + +int mmi_attribute_create(mmi_primitive_value_h value, const char *name, mmi_attribute_h *attribute) { + if (nullptr == value || nullptr == name || nullptr == attribute) { + _E("[ERROR] Some parameters are null"); + return MMI_ERROR_INVALID_PARAMETER; + } + + const size_t length = strlen(name); + if (0 == length || MAX_ATTRIBUTE_NAME_SIZE <= length) { + _E("[ERROR] Length of name is invalid. length(%zu)", length); + return MMI_ERROR_INVALID_PARAMETER; + } + + mmi_attribute_h new_attribute = reinterpret_cast(calloc(1, sizeof(mmi_attribute_s))); + if (nullptr == new_attribute) { + _E("[ERROR] Fail to allocate memory for attribute"); + return MMI_ERROR_OUT_OF_MEMORY; + } + + snprintf(new_attribute->name, MAX_ATTRIBUTE_NAME_SIZE, "%s", name); + int ret = mmi_primitive_value_clone(value, &(new_attribute->value)); + if (MMI_ERROR_NONE != ret) { + free(new_attribute); + _E("[ERROR] Fail to clone primitve value"); + return MMI_ERROR_OUT_OF_MEMORY; + } + + *attribute = new_attribute; + + _D("[DEBUG] Success to create attribute"); + return MMI_ERROR_NONE; +} + +int mmi_attribute_set_name(mmi_attribute_h attribute, const char *name) { + if (nullptr == attribute || nullptr == name) { + _E("[ERROR] Some parameters are null"); + return MMI_ERROR_INVALID_PARAMETER; + } + + auto attribute_ptr = static_cast(attribute); + snprintf(attribute_ptr->name, MAX_ATTRIBUTE_NAME_SIZE, "%s", name); + + _D("[DEBUG] Success to set name (%s)", attribute_ptr->name); + return MMI_ERROR_NONE; +} + +int mmi_attribute_get_name(mmi_attribute_h attribute, char **name) { + if (nullptr == attribute || nullptr == name) { + _E("[ERROR] Some parameters are null"); + return MMI_ERROR_INVALID_PARAMETER; + } + + *name = strdup(attribute->name); + if (nullptr == *name) { + _E("[ERROR] Fail to allocate memory for name string"); + return MMI_ERROR_OUT_OF_MEMORY; + } + + _D("[DEBUG] Success to get name (%s)", *name); + return MMI_ERROR_NONE; +} + +int mmi_attribute_get_value(mmi_attribute_h attribute, mmi_primitive_value_h *value) { + if (nullptr == attribute || nullptr == value) { + _E("[ERROR] Some parameters are null"); + return MMI_ERROR_INVALID_PARAMETER; + } + + int ret = mmi_primitive_value_clone(attribute->value, value); + if (MMI_ERROR_NONE != ret) { + _E("[ERROR] Fail to clone primitve value"); + return MMI_ERROR_OUT_OF_MEMORY; + } + + _D("[DEBUG] Success to get primitive value"); + return MMI_ERROR_NONE; +} + +int mmi_attribute_clone(mmi_attribute_h attribute, mmi_attribute_h *cloned) { + if (nullptr == attribute || nullptr == cloned) { + _E("[ERROR] Some parameters are null"); + return MMI_ERROR_INVALID_PARAMETER; + } + + mmi_attribute_h new_attribute = reinterpret_cast(calloc(1, sizeof(mmi_attribute_s))); + if (nullptr == new_attribute) { + _E("[ERROR] Fail to allocate memory for attribute"); + return MMI_ERROR_OUT_OF_MEMORY; + } + + snprintf(new_attribute->name, MAX_ATTRIBUTE_NAME_SIZE, "%s", attribute->name); + int ret = mmi_primitive_value_clone(attribute->value, &(new_attribute->value)); + if (MMI_ERROR_NONE != ret) { + free(new_attribute); + _E("[ERROR] Fail to clone primitve value"); + return MMI_ERROR_OUT_OF_MEMORY; + } + + *cloned = new_attribute; + + _D("[DEBUG] Success to clone attribute"); + return MMI_ERROR_NONE; +} + +int mmi_attribute_destroy(mmi_attribute_h attribute) { + if (nullptr == attribute) { + _E("[ERROR] Parameter is null"); + return MMI_ERROR_INVALID_PARAMETER; + } + + int ret = mmi_primitive_value_destroy(attribute->value); + if (MMI_ERROR_NONE != ret) { + _W("[WARN] Fail to destroy primitive value"); + } + + free(attribute); + + _D("[DEBUG] Success to destroy attribute"); + return MMI_ERROR_NONE; +} + +int mmi_attribute_to_bytes(mmi_attribute_h attribute, unsigned char **bytes, size_t *length) { + if (nullptr == attribute || nullptr == bytes || nullptr == length) { + _E("[ERROR] Some parameters are null"); + return MMI_ERROR_INVALID_PARAMETER; + } + + size_t name_length = strlen(attribute->name); + size_t value_length = 0; + unsigned char *value_bytes = nullptr; + int ret = mmi_primitive_value_to_bytes(attribute->value, &value_bytes, &value_length); + if (MMI_ERROR_NONE != ret) { + _E("[ERROR] Fail to convert primitive value to bytes"); + free(value_bytes); + return MMI_ERROR_OUT_OF_MEMORY; + } + + *length = sizeof(name_length) + name_length + sizeof(value_length) + value_length; + *bytes = reinterpret_cast(calloc(1, *length)); + if (nullptr == *bytes) { + _E("[ERROR] Fail to allocate memory for bytes"); + free(value_bytes); + return MMI_ERROR_OUT_OF_MEMORY; + } + + unsigned char *pos = *bytes; + memcpy(pos, &name_length, sizeof(name_length)); + pos += sizeof(name_length); + memcpy(pos, attribute->name, name_length); + pos += name_length; + memcpy(pos, &value_length, sizeof(value_length)); + pos += sizeof(value_length); + memcpy(pos, value_bytes, value_length); + + free(value_bytes); + + _D("[DEBUG] Success to convert attribute to bytes"); + return MMI_ERROR_NONE; +} + +int mmi_attribute_from_bytes(const unsigned char *bytes, size_t length, mmi_attribute_h *attribute) { + if (nullptr == bytes || 0 == length || nullptr == attribute) { + _E("[ERROR] Some parameters are null"); + return MMI_ERROR_INVALID_PARAMETER; + } + + size_t name_length = 0; + char *name = nullptr; + unsigned char *pos = const_cast(bytes); + memcpy(&name_length, pos, sizeof(name_length)); + pos += sizeof(name_length); + name = reinterpret_cast(calloc(1, name_length + 1)); + if (nullptr == name) { + _E("[ERROR] Fail to allocate memory for name string"); + return MMI_ERROR_OUT_OF_MEMORY; + } + memcpy(name, pos, name_length); + pos += name_length; + + size_t value_length = 0; + unsigned char *value_bytes = nullptr; + memcpy(&value_length, pos, sizeof(value_length)); + pos += sizeof(value_length); + value_bytes = reinterpret_cast(calloc(1, value_length)); + if (nullptr == value_bytes) { + _E("[ERROR] Fail to allocate memory for value bytes"); + free(name); + return MMI_ERROR_OUT_OF_MEMORY; + } + memcpy(value_bytes, pos, value_length); + pos += value_length; + + mmi_primitive_value_h value = nullptr; + int ret = mmi_primitive_value_from_bytes(value_bytes, value_length, &value); + if (MMI_ERROR_NONE != ret) { + _E("[ERROR] Fail to convert bytes to primitive value"); + free(name); + free(value_bytes); + return MMI_ERROR_OUT_OF_MEMORY; + } + + free(value_bytes); + + ret = mmi_attribute_create(value, name, attribute); + if (MMI_ERROR_NONE != ret) { + _E("[ERROR] Fail to create attribute"); + free(name); + mmi_primitive_value_destroy(value); + return MMI_ERROR_OUT_OF_MEMORY; + } + + mmi_primitive_value_destroy(value); + + free(name); + + _D("[DEBUG] Success to convert bytes to attribute"); + return MMI_ERROR_NONE; +} + +int mmi_attribute_create_string_array(const char *name, const char *strings[], size_t count, mmi_attribute_h *attribute) { + if (nullptr == attribute || nullptr == name || nullptr == strings || 0 == count) { + _E("[ERROR] Some parameters are invalid"); + return MMI_ERROR_INVALID_PARAMETER; + } + + mmi_primitive_value_h array = nullptr; + int ret = mmi_primitive_value_create_array(&array); + if (MMI_ERROR_NONE != ret) { + _E("[ERROR] Fail to allocate memory for array primitive"); + return MMI_ERROR_OUT_OF_MEMORY; + } + + for (size_t i = 0; i < count; i++) { + mmi_primitive_value_h element = nullptr; + int ret = mmi_primitive_value_create_string(strings[i], &element); + if (MMI_ERROR_NONE != ret) { + _E("[ERROR] Fail to allocate memory for array attribute"); + mmi_primitive_value_destroy(array); + return MMI_ERROR_OUT_OF_MEMORY; + } + + ret = mmi_primitive_value_add_array_element(array, element); + if (MMI_ERROR_NONE != ret) { + _E("[ERROR] Fail to allocate memory for array attribute"); + mmi_primitive_value_destroy(array); + mmi_primitive_value_destroy(element); + return MMI_ERROR_OUT_OF_MEMORY; + } + } + + ret = mmi_attribute_create(array, name, attribute); + if (MMI_ERROR_NONE != ret) { + mmi_primitive_value_destroy(array); + _E("[ERROR] Fail to allocate memory for array attribute"); + return MMI_ERROR_OUT_OF_MEMORY; + } + + ret = mmi_primitive_value_destroy(array); + if (MMI_ERROR_NONE != ret) { + _W("[WARN] Fail to destroy temporary primitive value"); + } + + _D("[DEBUG] Success to create attribute"); + return MMI_ERROR_NONE; +} diff --git a/src/mmi/mmi-client-manager.h b/src/mmi/mmi-client-manager.h new file mode 100644 index 0000000..c8b921b --- /dev/null +++ b/src/mmi/mmi-client-manager.h @@ -0,0 +1,99 @@ +/* + * Copyright (c) 2023 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#pragma once + +#include "mmi.h" + +#include "mmi-event-observer.h" +#include "mmi-communication-channel.h" +#include "mmi-workflow-instance-manager.h" + +using namespace mmi; +using namespace mmi::communication; + +struct ICommunicationChannelFactory { + virtual CommunicationChannel* create_channel() = 0; + virtual void destroy_channel(CommunicationChannel* channel) = 0; +}; + +class ClientManager : public SimpleEventObserver { +public: + ClientManager( + ICommunicationChannelFactory *factory, + SimpleEventObserver *observer) + : m_communication_channel_factory(factory), + m_communication_channel_observer(observer) { + } + virtual ~ClientManager() { + } + + bool initialize() { + if (m_reference_count == 0) { + m_communication_channel = + m_communication_channel_factory->create_channel(); + if (nullptr == m_communication_channel) { + return false; + } else { + m_communication_channel->add_observer(m_communication_channel_observer); + m_communication_channel->add_observer(this); + m_communication_channel->connect(); + } + + m_workflow_instance_manager.initialize(); + } + m_reference_count++; + return true; + } + bool deinitialize() { + if (m_reference_count == 0) { + return false; + } + m_reference_count--; + if (m_reference_count == 0) { + if (m_communication_channel) { + m_communication_channel->disconnect(); + m_communication_channel_factory->destroy_channel(m_communication_channel); + } + m_communication_channel = nullptr; + + m_workflow_instance_manager.deinitialize(); + } + return true; + } + + CommunicationChannel* get_communication_channel() { + return m_communication_channel; + } + WorkflowInstanceManager* get_workflow_instance_manager() { + return &m_workflow_instance_manager; + } + + void on_observer_event( + const COMMUNICATION_CHANNEL_EVENT_TYPE &event, std::any data) override { + } + +private: + ICommunicationChannelFactory* m_communication_channel_factory{nullptr}; + CommunicationChannel *m_communication_channel{nullptr}; + SimpleEventObserver + *m_communication_channel_observer; + WorkflowInstanceManager m_workflow_instance_manager; + + int m_reference_count{0}; +}; + diff --git a/src/mmi/mmi-data.cpp b/src/mmi/mmi-data.cpp new file mode 100644 index 0000000..7119506 --- /dev/null +++ b/src/mmi/mmi-data.cpp @@ -0,0 +1,863 @@ +/* + * Copyright (c) 2023 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 "mmi-log.h" + +#include +#include + + +struct mmi_data_timestamp_s { + bool valid; + void *data; +}; + +struct mmi_data_s { + mmi_data_type_e type; + void *data; + size_t datalen; +}; + + +static constexpr size_t g_unit_size = sizeof(mmi_data_h); + + +static constexpr size_t get_count_of_elements(mmi_data_h array) { + return array->datalen / g_unit_size; +} + +static constexpr size_t get_count_of_struct_elements(mmi_data_h struct_handle) { + return get_count_of_elements(struct_handle) / 2; +} + +static inline mmi_data_h *get_element_pointer_in_array(mmi_data_h array, size_t index) { + return reinterpret_cast(array->data) + index; +} + +static inline mmi_data_h *get_element_name_pointer_in_struct(mmi_data_h struct_handle, size_t index) { + return get_element_pointer_in_array(struct_handle, index * 2); +} + +static inline mmi_data_h *get_element_value_pointer_in_struct(mmi_data_h struct_handle, size_t index) { + return get_element_pointer_in_array(struct_handle, index * 2 + 1); +} + + +int mmi_data_create_bool(bool value, mmi_data_h *data) { + if (nullptr == data) { + _E("[ERROR] Some parameters are invalid"); + return MMI_ERROR_INVALID_PARAMETER; + } + + auto new_data = reinterpret_cast(malloc(sizeof(mmi_data_s))); + if (nullptr == new_data) { + _E("[ERROR] Fail to allocate memory for mmi data"); + return MMI_ERROR_OUT_OF_MEMORY; + } + + new_data->type = MMI_DATA_TYPE_BOOLEAN; + new_data->data = malloc(sizeof(bool)); + if (nullptr == new_data->data) { + _E("[ERROR] Fail to allocate memory for raw data"); + free(new_data); + return MMI_ERROR_OUT_OF_MEMORY; + } + + *(reinterpret_cast(new_data->data)) = value; + new_data->datalen = sizeof(bool); + + *data = new_data; + + _D("[DEBUG] Success to create for bool(%d)", value); + return MMI_ERROR_NONE; +} + +int mmi_data_create_int(int value, mmi_data_h *data) { + if (nullptr == data) { + _E("[ERROR] Some parameters are invalid"); + return MMI_ERROR_INVALID_PARAMETER; + } + + auto new_data = reinterpret_cast(malloc(sizeof(mmi_data_s))); + if (nullptr == new_data) { + _E("[ERROR] Fail to allocate memory for mmi data"); + return MMI_ERROR_OUT_OF_MEMORY; + } + + new_data->type = MMI_DATA_TYPE_INTEGER; + new_data->data = malloc(sizeof(int)); + if (nullptr == new_data->data) { + _E("[ERROR] Fail to allocate memory for raw data"); + free(new_data); + return MMI_ERROR_OUT_OF_MEMORY; + } + + *(reinterpret_cast(new_data->data)) = value; + new_data->datalen = sizeof(int); + + *data = new_data; + + _D("[DEBUG] Success to create for int(%d)", value); + return MMI_ERROR_NONE; +} + +int mmi_data_create_float(float value, mmi_data_h *data) { + if (nullptr == data) { + _E("[ERROR] Some parameters are invalid"); + return MMI_ERROR_INVALID_PARAMETER; + } + + auto new_data = reinterpret_cast(malloc(sizeof(mmi_data_s))); + if (nullptr == new_data) { + _E("[ERROR] Fail to allocate memory for mmi data"); + return MMI_ERROR_OUT_OF_MEMORY; + } + + new_data->type = MMI_DATA_TYPE_FLOAT; + new_data->data = malloc(sizeof(float)); + if (nullptr == new_data->data) { + _E("[ERROR] Fail to allocate memory for raw data"); + free(new_data); + return MMI_ERROR_OUT_OF_MEMORY; + } + + *(reinterpret_cast(new_data->data)) = value; + new_data->datalen = sizeof(float); + + *data = new_data; + + _D("[DEBUG] Success to create for float(%f)", value); + return MMI_ERROR_NONE; +} + +int mmi_data_create_text(const char *value, mmi_data_h *data) { + if (nullptr == data || nullptr == value) { + _E("[ERROR] Some parameters are invalid"); + return MMI_ERROR_INVALID_PARAMETER; + } + + auto new_data = reinterpret_cast(malloc(sizeof(mmi_data_s))); + if (nullptr == new_data) { + _E("[ERROR] Fail to allocate memory for primitive value"); + return MMI_ERROR_OUT_OF_MEMORY; + } + + new_data->type = MMI_DATA_TYPE_TEXT; + new_data->data = reinterpret_cast(strdup(value)); + if (nullptr == new_data->data) { + _E("[ERROR] Fail to allocate memory for raw data"); + free(new_data); + return MMI_ERROR_OUT_OF_MEMORY; + } + + new_data->datalen = strlen(value); + + *data = new_data; + + _D("[DEBUG] Success to create for string(%s)", value); + return MMI_ERROR_NONE; +} + +int mmi_data_create_audio(const void *ptr, size_t len, mmi_data_h *data) { + if (nullptr == data || nullptr == ptr || 0 == len) { + _E("[ERROR] Some parameters are invalid"); + return MMI_ERROR_INVALID_PARAMETER; + } + + auto new_data = reinterpret_cast(malloc(sizeof(mmi_data_s))); + if (nullptr == new_data) { + _E("[ERROR] Fail to allocate memory for mmi data"); + return MMI_ERROR_OUT_OF_MEMORY; + } + + new_data->type = MMI_DATA_TYPE_AUDIO; + new_data->data = malloc(len); + if (nullptr == new_data->data) { + _E("[ERROR] Fail to allocate memory for raw data"); + free(new_data); + return MMI_ERROR_OUT_OF_MEMORY; + } + + memcpy(new_data->data, ptr, len); + new_data->datalen = len; + + *data = new_data; + + _D("[DEBUG] Success to create for audio data. len(%zu)", len); + return MMI_ERROR_NONE; +} + +int mmi_data_create_video(const void *ptr, size_t len, mmi_data_h *data) { + if (nullptr == data || nullptr == ptr || 0 == len) { + _E("[ERROR] Some parameters are invalid"); + return MMI_ERROR_INVALID_PARAMETER; + } + + auto new_data = reinterpret_cast(malloc(sizeof(mmi_data_s))); + if (nullptr == new_data) { + _E("[ERROR] Fail to allocate memory for mmi data"); + return MMI_ERROR_OUT_OF_MEMORY; + } + + new_data->type = MMI_DATA_TYPE_VIDEO; + new_data->data = malloc(len); + if (nullptr == new_data->data) { + _E("[ERROR] Fail to allocate memory for raw data"); + free(new_data); + return MMI_ERROR_OUT_OF_MEMORY; + } + + memcpy(new_data->data, ptr, len); + new_data->datalen = len; + + *data = new_data; + + _D("[DEBUG] Success to create for video data. len(%zu)", len); + return MMI_ERROR_NONE; +} + +int mmi_data_create_user_identification(const void *ptr, size_t len, mmi_data_h *data) { + if (nullptr == data || nullptr == ptr || 0 == len) { + _E("[ERROR] Some parameters are invalid"); + return MMI_ERROR_INVALID_PARAMETER; + } + + auto new_data = reinterpret_cast(malloc(sizeof(mmi_data_s))); + if (nullptr == new_data) { + _E("[ERROR] Fail to allocate memory for mmi data"); + return MMI_ERROR_OUT_OF_MEMORY; + } + + new_data->type = MMI_DATA_TYPE_USER_IDENTIFICATION; + new_data->data = malloc(len); + if (nullptr == new_data->data) { + _E("[ERROR] Fail to allocate memory for raw data"); + free(new_data); + return MMI_ERROR_OUT_OF_MEMORY; + } + + memcpy(new_data->data, ptr, len); + new_data->datalen = len; + + *data = new_data; + + _D("[DEBUG] Success to create for user identification data. len(%zu)", len); + return MMI_ERROR_NONE; +} + +int mmi_data_create_array(mmi_data_h *data) { + if (nullptr == data) { + _E("[ERROR] Some parameters are invalid"); + return MMI_ERROR_INVALID_PARAMETER; + } + + auto new_data = reinterpret_cast(malloc(sizeof(mmi_data_s))); + if (nullptr == new_data) { + _E("[ERROR] Fail to allocate memory for primitive value"); + return MMI_ERROR_OUT_OF_MEMORY; + } + + new_data->type = MMI_DATA_TYPE_ARRAY; + new_data->data = nullptr; + new_data->datalen = 0; + + *data = new_data; + + _D("[DEBUG] Success to create empty array"); + return MMI_ERROR_NONE; +} + +int mmi_data_add_array_element(mmi_data_h array, mmi_data_h element) { + if (nullptr == array || nullptr == element) { + _E("[ERROR] Some parameters are invalid"); + return MMI_ERROR_INVALID_PARAMETER; + } + + if (array->type != MMI_DATA_TYPE_ARRAY) { + _E("[ERROR] Data type is not matched"); + return MMI_ERROR_INVALID_PARAMETER; + } + + size_t count = get_count_of_elements(array); + if (count > 0) { + mmi_data_h first = *(get_element_pointer_in_array(array, 0)); + if (first->type != element->type) { + _E("[ERROR] Element type is not matched"); + return MMI_ERROR_INVALID_PARAMETER; + } + } + + auto array_data = reinterpret_cast(array->data); + array_data = reinterpret_cast(realloc(array_data, g_unit_size * (count + 1))); + if (array_data) { + array_data[count] = element; + + array->data = array_data; + array->datalen = array->datalen + g_unit_size; + } else { + _E("[ERROR] Fail to allocate memory for array data"); + return MMI_ERROR_OUT_OF_MEMORY; + } + + _D("[DEBUG] Success to add element(%zu)", get_count_of_elements(array)); + return MMI_ERROR_NONE; +} + +int mmi_data_create_struct(mmi_data_h *struct_handle) { + if (nullptr == struct_handle) { + _E("[ERROR] Some parameters are invalid"); + return MMI_ERROR_INVALID_PARAMETER; + } + + auto new_data = reinterpret_cast(malloc(sizeof(mmi_data_s))); + if (nullptr == new_data) { + _E("[ERROR] Fail to allocate memory for primitive value"); + return MMI_ERROR_OUT_OF_MEMORY; + } + + new_data->type = MMI_DATA_TYPE_STRUCT; + new_data->data = nullptr; + new_data->datalen = 0; + + *struct_handle = new_data; + + _D("[DEBUG] Success to create empty array"); + return MMI_ERROR_NONE; +} + +int mmi_data_set_struct_element(mmi_data_h struct_handle, const char *name, mmi_data_h element) { + if (nullptr == struct_handle || nullptr == name || nullptr == element) { + _E("[ERROR] Some parameters are invalid"); + return MMI_ERROR_INVALID_PARAMETER; + } + + if (struct_handle->type != MMI_DATA_TYPE_STRUCT) { + _E("[ERROR] Data type is not matched"); + return MMI_ERROR_INVALID_PARAMETER; + } + + mmi_data_h name_element = nullptr; + int ret = mmi_data_create_text(name, &name_element); + if (MMI_ERROR_NONE != ret) { + _E("[ERROR] Fail to allocate memory for name element"); + return MMI_ERROR_OUT_OF_MEMORY; + } + + size_t count = get_count_of_elements(struct_handle); + auto struct_data = reinterpret_cast(struct_handle->data); + struct_data = reinterpret_cast(realloc(struct_data, g_unit_size * (count + 2))); + if (struct_data) { + struct_data[count] = name_element; + struct_data[count + 1] = element; + + struct_handle->data = struct_data; + struct_handle->datalen = struct_handle->datalen + g_unit_size * 2; + } else { + mmi_data_destroy(name_element); + _E("[ERROR] Fail to allocate memory for struct data"); + return MMI_ERROR_OUT_OF_MEMORY; + } + + _D("[DEBUG] Success to add element with name(%s)", name); + return MMI_ERROR_NONE; +} + +int mmi_data_get_type(mmi_data_h data, mmi_data_type_e *type) { + if (nullptr == data || nullptr == type) { + _E("[ERROR] Some parameters are invalid"); + return MMI_ERROR_INVALID_PARAMETER; + } + + *type = data->type; + + _D("[DEBUG] Success to get type(%d)", *type); + return MMI_ERROR_NONE; +} + +int mmi_data_get_bool(mmi_data_h data, bool *value) { + if (nullptr == data || nullptr == value) { + _E("[ERROR] Some parameters are invalid"); + return MMI_ERROR_INVALID_PARAMETER; + } + + if (data->type != MMI_DATA_TYPE_BOOLEAN) { + _E("[ERROR] Data type is not matched"); + return MMI_ERROR_INVALID_PARAMETER; + } + + *value = *(reinterpret_cast(data->data)); + + _D("[DEBUG] Success to get value: bool(%d)", *value); + return MMI_ERROR_NONE; +} + +int mmi_data_get_int(mmi_data_h data, int *value) { + if (nullptr == data || nullptr == value) { + _E("[ERROR] Some parameters are invalid"); + return MMI_ERROR_INVALID_PARAMETER; + } + + if (data->type != MMI_DATA_TYPE_INTEGER) { + _E("[ERROR] Data type is not matched"); + return MMI_ERROR_INVALID_PARAMETER; + } + + *value = *(reinterpret_cast(data->data)); + + _D("[DEBUG] Success to get value: int(%d)", *value); + return MMI_ERROR_NONE; +} + +int mmi_data_get_float(mmi_data_h data, float *value) { + if (nullptr == data || nullptr == value) { + _E("[ERROR] Some parameters are invalid"); + return MMI_ERROR_INVALID_PARAMETER; + } + + if (data->type != MMI_DATA_TYPE_FLOAT) { + _E("[ERROR] Data type is not matched"); + return MMI_ERROR_INVALID_PARAMETER; + } + + *value = *(reinterpret_cast(data->data)); + + _D("[DEBUG] Success to get value: float(%f)", *value); + return MMI_ERROR_NONE; +} + +int mmi_data_get_text(mmi_data_h data, const char **string) { + if (nullptr == data || nullptr == string) { + _E("[ERROR] Some parameters are invalid"); + return MMI_ERROR_INVALID_PARAMETER; + } + + if (data->type != MMI_DATA_TYPE_TEXT) { + _E("[ERROR] Data type is not matched"); + return MMI_ERROR_INVALID_PARAMETER; + } + + *string = reinterpret_cast(data->data); + + _D("[DEBUG] Success to get value: string(%s)", *string); + return MMI_ERROR_NONE; +} + +int mmi_data_get_audio(mmi_data_h data, const void **ptr, size_t *len) { + if (nullptr == data || nullptr == ptr || nullptr == len) { + _E("[ERROR] Some parameters are invalid"); + return MMI_ERROR_INVALID_PARAMETER; + } + + if (data->type != MMI_DATA_TYPE_AUDIO) { + _E("[ERROR] Data type is not matched"); + return MMI_ERROR_INVALID_PARAMETER; + } + + *len = data->datalen; + *ptr = data->data; + + _D("[DEBUG] Success to get value: audio(%zu)", *len); + return MMI_ERROR_NONE; +} + +int mmi_data_get_video(mmi_data_h data, const void **ptr, size_t *len) { + if (nullptr == data || nullptr == ptr || nullptr == len) { + _E("[ERROR] Some parameters are invalid"); + return MMI_ERROR_INVALID_PARAMETER; + } + + if (data->type != MMI_DATA_TYPE_VIDEO) { + _E("[ERROR] Data type is not matched"); + return MMI_ERROR_INVALID_PARAMETER; + } + + *len = data->datalen; + *ptr = data->data; + + _D("[DEBUG] Success to get value: video(%zu)", *len); + return MMI_ERROR_NONE; +} + +int mmi_data_get_user_identification(mmi_data_h data, const void **ptr, size_t *len) { + if (nullptr == data || nullptr == ptr || nullptr == len) { + _E("[ERROR] Some parameters are invalid"); + return MMI_ERROR_INVALID_PARAMETER; + } + + if (data->type != MMI_DATA_TYPE_USER_IDENTIFICATION) { + _E("[ERROR] Data type is not matched"); + return MMI_ERROR_INVALID_PARAMETER; + } + + *len = data->datalen; + *ptr = data->data; + + _D("[DEBUG] Success to get value: user_identification(%zu)", *len); + return MMI_ERROR_NONE; +} + +int mmi_data_get_array_count(mmi_data_h array, size_t *count) { + if (nullptr == array || nullptr == count) { + _E("[ERROR] Some parameters are invalid"); + return MMI_ERROR_INVALID_PARAMETER; + } + + if (array->type != MMI_DATA_TYPE_ARRAY) { + _E("[ERROR] Data type is not matched"); + return MMI_ERROR_INVALID_PARAMETER; + } + + *count = get_count_of_elements(array); + + _D("[DEBUG] Success to get array count(%zu)", *count); + return MMI_ERROR_NONE; +} + +int mmi_data_get_array_element(mmi_data_h array, size_t index, mmi_data_h *element) { + if (nullptr == array || nullptr == element) { + _E("[ERROR] Some parameters are invalid"); + return MMI_ERROR_INVALID_PARAMETER; + } + + if (array->type != MMI_DATA_TYPE_ARRAY) { + _E("[ERROR] Data type is not matched"); + return MMI_ERROR_INVALID_PARAMETER; + } + + size_t count = get_count_of_elements(array); + if (index >= count) { + _E("[ERROR] index(%zu) is out of range. count(%zu)", index, count); + return MMI_ERROR_INVALID_PARAMETER; + } + + *element = *(get_element_pointer_in_array(array, index)); + + _D("[DEBUG] Success to get array element on index(%zu)", index); + return MMI_ERROR_NONE; +} + +int mmi_data_get_struct_element(mmi_data_h struct_handle, const char *name, mmi_data_h *element) { + if (nullptr == struct_handle || nullptr == name || nullptr == element) { + _E("[ERROR] Some parameters are invalid"); + return MMI_ERROR_INVALID_PARAMETER; + } + + if (struct_handle->type != MMI_DATA_TYPE_STRUCT) { + _E("[ERROR] Data type is not matched"); + return MMI_ERROR_INVALID_PARAMETER; + } + + size_t count = get_count_of_elements(struct_handle); + if (count % 2 != 0) { + _E("[ERROR] Element size is invalid"); + return MMI_ERROR_INVALID_PARAMETER; + } + + mmi_data_h target_element = nullptr; + auto struct_data = reinterpret_cast(struct_handle->data); + for (size_t i = 0; i < count; i += 2) { + const char *element_name = nullptr; + mmi_data_get_text(struct_data[i], &element_name); + if (0 == strcmp(name, element_name)) { + target_element = struct_data[i + 1]; + break; + } + } + + if (nullptr == target_element) { + _E("[ERROR] No matched element with name (%s)", name); + return MMI_ERROR_INVALID_PARAMETER; + } + + *element = target_element; + + _D("[DEBUG] Success to get element on name(%s)", name); + return MMI_ERROR_NONE; +} + +int mmi_data_get_struct_count(mmi_data_h struct_handle, size_t *count) { + if (nullptr == struct_handle || nullptr == count) { + _E("[ERROR] Some parameters are invalid"); + return MMI_ERROR_INVALID_PARAMETER; + } + + if (struct_handle->type != MMI_DATA_TYPE_STRUCT) { + _E("[ERROR] Data type is not matched"); + return MMI_ERROR_INVALID_PARAMETER; + } + + *count = get_count_of_struct_elements(struct_handle); + + _D("[DEBUG] Success to get struct count(%zu)", *count); + return MMI_ERROR_NONE; +} + +int mmi_data_get_struct_element_name(mmi_data_h struct_handle, size_t index, const char **string) { + if (nullptr == struct_handle || nullptr == string) { + _E("[ERROR] Some parameters are invalid"); + return MMI_ERROR_INVALID_PARAMETER; + } + + if (struct_handle->type != MMI_DATA_TYPE_STRUCT) { + _E("[ERROR] Data type is not matched"); + return MMI_ERROR_INVALID_PARAMETER; + } + + size_t count = get_count_of_struct_elements(struct_handle); + if (index >= count) { + _E("[ERROR] index(%zu) is out of range. count(%zu)", index, count); + return MMI_ERROR_INVALID_PARAMETER; + } + + mmi_data_h *element = get_element_name_pointer_in_struct(struct_handle, index); + mmi_data_get_text(*element, string); + + _D("[DEBUG] Success to get struct element name on index(%zu)", index); + return MMI_ERROR_NONE; +} + + +int mmi_data_get_struct_element_value(mmi_data_h struct_handle, size_t index, mmi_data_h *element) { + if (nullptr == struct_handle || nullptr == element) { + _E("[ERROR] Some parameters are invalid"); + return MMI_ERROR_INVALID_PARAMETER; + } + + if (struct_handle->type != MMI_DATA_TYPE_STRUCT) { + _E("[ERROR] Data type is not matched"); + return MMI_ERROR_INVALID_PARAMETER; + } + + size_t count = get_count_of_struct_elements(struct_handle); + if (index >= count) { + _E("[ERROR] index(%zu) is out of range. count(%zu)", index, count); + return MMI_ERROR_INVALID_PARAMETER; + } + + *element = *(get_element_value_pointer_in_struct(struct_handle, index)); + + _D("[DEBUG] Success to get struct element value on index(%zu)", index); + return MMI_ERROR_NONE; +} + +int mmi_data_destroy(mmi_data_h data) { + if (nullptr == data) { + _E("[ERROR] Some parameters are invalid"); + return MMI_ERROR_INVALID_PARAMETER; + } + + if (data->type == MMI_DATA_TYPE_ARRAY || data->type == MMI_DATA_TYPE_STRUCT) { + size_t count = get_count_of_elements(data); + for (size_t i = 0; i < count; i++) { + auto element = get_element_pointer_in_array(data, i); + mmi_data_destroy(*element); + } + } + + free(data->data); + data->data = nullptr; + data->datalen = 0; + data->type = MMI_DATA_TYPE_ANY; + free(data); + + _D("[DEBUG] Success to destroy"); + return MMI_ERROR_NONE; +} + +int mmi_data_get_node_timestamp(mmi_data_h data, mmi_data_timestamp_h *timestamp) { + if (nullptr == data || nullptr == timestamp) { + return MMI_ERROR_INVALID_PARAMETER; + } + + *timestamp = (mmi_data_timestamp_s*)(data->data); + return MMI_ERROR_NONE; +} + +int mmi_data_get_source_timestamp(mmi_data_h data, mmi_data_timestamp_h *timestamp) { + if (nullptr == data || nullptr == timestamp) { + return MMI_ERROR_INVALID_PARAMETER; + } + + *timestamp = (mmi_data_timestamp_s*)(data->data); + return MMI_ERROR_NONE; +} + +int mmi_data_set_source_timestamp(mmi_data_h data, mmi_data_timestamp_h timestamp) { + if (nullptr == data || nullptr == timestamp) { + return MMI_ERROR_INVALID_PARAMETER; + } + + data->data = timestamp; + return MMI_ERROR_NONE; +} + +int mmi_data_to_bytes(mmi_data_h data, unsigned char **bytes, size_t *length) { + if (nullptr == data || nullptr == bytes || nullptr == length) { + _E("[ERROR] Some parameters are invalid"); + return MMI_ERROR_INVALID_PARAMETER; + } + + mmi_data_type_e type; + mmi_data_get_type(data, &type); + // TODO:: add data type, separate to function + + if (type == MMI_DATA_TYPE_ARRAY || type == MMI_DATA_TYPE_STRUCT) { + size_t current_size = sizeof(mmi_data_type_e) + sizeof(size_t); + unsigned char *buffer = reinterpret_cast(malloc(current_size)); + + size_t count = get_count_of_elements(data); + unsigned char *current = buffer; + memcpy(current, &(data->type), sizeof(mmi_data_type_e)); + current += sizeof(mmi_data_type_e); + /* This size will be updated after getting element bytes */ + memcpy(current, &count, sizeof(size_t)); + current += sizeof(size_t); + for (size_t i = 0; i < count; i++) { + auto element = get_element_pointer_in_array(data, i); + unsigned char *element_bytes = nullptr; + size_t element_size = 0; + mmi_data_to_bytes(*element, &element_bytes, &element_size); + buffer = reinterpret_cast(realloc(buffer, current_size + element_size + sizeof(size_t))); + if (nullptr == buffer) { + _E("[ERROR] Failed to allocate memory"); + return MMI_ERROR_OUT_OF_MEMORY; + } + current = buffer + current_size; + memcpy(current, &element_size, sizeof(size_t)); + current += sizeof(size_t); + current_size += sizeof(size_t); + memcpy(current, element_bytes, element_size); + current_size += element_size; + free(element_bytes); + } + size_t total_element_size = current_size - sizeof(mmi_data_type_e) - sizeof(size_t); + memcpy(buffer + sizeof(mmi_data_type_e), &total_element_size, sizeof(size_t)); + + *bytes = buffer; + *length = current_size; + } else { + size_t total_size = sizeof(mmi_data_type_e) + sizeof(size_t) + data->datalen; + unsigned char *buffer = reinterpret_cast(malloc(total_size)); + if (nullptr == buffer) { + _E("[ERROR] Failed to allocate memory"); + return MMI_ERROR_OUT_OF_MEMORY; + } + unsigned char *current = buffer; + memcpy(current, &(data->type), sizeof(mmi_data_type_e)); + current += sizeof(mmi_data_type_e); + memcpy(current, &(data->datalen), sizeof(size_t)); + current += sizeof(size_t); + memcpy(current, data->data, data->datalen); + + *bytes = buffer; + *length = total_size; + } + + _D("[DEBUG] Success to convert data to bytes"); + + return MMI_ERROR_NONE; +} + +int mmi_data_from_bytes(unsigned char *bytes, size_t length, mmi_data_h *data) { + if (nullptr == data || nullptr == bytes || 0 == length) { + _E("[ERROR] Some parameters are invalid"); + return MMI_ERROR_INVALID_PARAMETER; + } + + unsigned char *current = bytes; + mmi_data_type_e type = MMI_DATA_TYPE_ANY; + memcpy(&type, current, sizeof(mmi_data_type_e)); + current += sizeof(mmi_data_type_e); + if (type == MMI_DATA_TYPE_ARRAY || type == MMI_DATA_TYPE_STRUCT) { + size_t datalen = 0; + memcpy(&datalen, current, sizeof(size_t)); + current += sizeof(size_t); + void *buf = nullptr; + auto value = reinterpret_cast(malloc(sizeof(mmi_data_s))); + value->type = type; + value->datalen = 0; + value->data = nullptr; + + size_t num_elements = 0; + while (current < bytes + length) { + size_t element_size; + memcpy(&element_size, current, sizeof(size_t)); + current += sizeof(size_t); + unsigned char *element_bytes = reinterpret_cast(malloc(element_size)); + if (nullptr == element_bytes) { + _E("[ERROR] Failed to allocate memory"); + mmi_data_destroy(value); + return MMI_ERROR_OUT_OF_MEMORY; + } + + memcpy(element_bytes, current, element_size); + current += element_size; + mmi_data_h element = nullptr; + mmi_data_from_bytes(element_bytes, element_size, &element); + free(element_bytes); + if (nullptr == element) { + _E("[ERROR] Failed to convert bytes to data"); + mmi_data_destroy(value); + return MMI_ERROR_OUT_OF_MEMORY; + } + + auto array_data = reinterpret_cast(value->data); + array_data = reinterpret_cast(realloc(array_data, g_unit_size * (num_elements + 1))); + if (array_data) { + array_data[num_elements] = element; + + value->data = array_data; + value->datalen += g_unit_size; + } else { + _E("[ERROR] Fail to allocate memory for array data"); + mmi_data_destroy(element); + mmi_data_destroy(value); + return MMI_ERROR_OUT_OF_MEMORY; + } + + num_elements++; + } + + *data = value; + } else { + size_t datalen; + memcpy(&datalen, current, sizeof(size_t)); + current += sizeof(size_t); + void *buf = calloc(1, datalen); + if (nullptr == buf) { + _E("[ERROR] Failed to allocate memory"); + return MMI_ERROR_OUT_OF_MEMORY; + } + memcpy(buf, current, datalen); + + auto value = reinterpret_cast(malloc(sizeof(mmi_data_s))); + value->type = type; + value->datalen = datalen; + value->data = buf; + + *data = value; + } + + _D("[DEBUG] Success to convert from bytes. type(%d), size(%zu)", type, length); + + return MMI_ERROR_NONE; +} diff --git a/src/mmi/mmi-ipc-tidl.cpp b/src/mmi/mmi-ipc-tidl.cpp new file mode 100644 index 0000000..a58fc8f --- /dev/null +++ b/src/mmi/mmi-ipc-tidl.cpp @@ -0,0 +1,418 @@ +/* + * Copyright (c) 2022 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 "mmi-ipc-tidl.h" +#include "mmi-log.h" +#include "mmi-workflow-instance-manager.h" + +#include +#include +#include +#include + +namespace mmi { + +namespace communication { + +CommunicationChannelTIDL::CommunicationChannelTIDL() { +} + +CommunicationChannelTIDL::~CommunicationChannelTIDL() { +} + +static void result_cb(void *user_data, const char *source_name, bundle *data) { + mmi_data_h restored_data = nullptr; + + if (nullptr == user_data) { + LOGE("[ERROR] user_data is null"); + return; + } + + WorkflowInstance *workflow_instance = static_cast(user_data); + unsigned char *bytes = nullptr; + size_t returned_size = 0; + + if (BUNDLE_ERROR_NONE != bundle_get_byte(data, "data", (void**)&bytes, &returned_size)) { + LOGE("[ERROR] Failed to get bytes from bundle"); + return; + } + + if (nullptr == bytes || 0 == returned_size) { + LOGE("[ERROR] bytes is null or returned_size is 0"); + return; + } + + if (MMI_ERROR_NONE != mmi_data_from_bytes(bytes, returned_size, &restored_data)) { + LOGE("[ERROR] Failed to restore data from bytes"); + return; + } + + workflow_instance->call_output_callback(source_name, restored_data); +} + +void CommunicationChannelTIDL::on_connected(rpc_port_proxy_mmi_h h, void *user_data) { + LOGI("[ENTER]"); + CommunicationChannelTIDL *channel = static_cast(user_data); + if (channel) { + channel->m_connected = true; + channel->notify_observers( + COMMUNICATION_CHANNEL_EVENT_TYPE::CONNECTED); + } +} + +void CommunicationChannelTIDL::on_disconnected(rpc_port_proxy_mmi_h h, void *user_data) { + LOGI("[ENTER]"); + CommunicationChannelTIDL *channel = static_cast(user_data); + if (channel) { + channel->m_connected = false; + channel->notify_observers( + COMMUNICATION_CHANNEL_EVENT_TYPE::DISCONNECTED); + } +} + +void CommunicationChannelTIDL::on_rejected(rpc_port_proxy_mmi_h h, void *user_data) { + LOGI("[ENTER]"); + CommunicationChannelTIDL *channel = static_cast(user_data); + if (channel) { + channel->m_connected = false; + channel->notify_observers( + COMMUNICATION_CHANNEL_EVENT_TYPE::DISCONNECTED); + } +} + +int CommunicationChannelTIDL::connect() { + LOGI("[ENTER]"); + + if (m_rpc_h) { + LOGI("RPC Handle already exists"); + return MMI_ERROR_OPERATION_FAILED; + } + + rpc_port_proxy_mmi_callback_s callback; + callback.connected = on_connected; + callback.disconnected = on_disconnected; + callback.rejected = on_rejected; + + int ret = rpc_port_proxy_mmi_create(m_stub_appid.c_str(), &callback, this, &m_rpc_h); + if (ret != RPC_PORT_ERROR_NONE) { + LOGE("Failed to create mmi proxy handle ! (error:%d)\n", ret); + return MMI_ERROR_OPERATION_FAILED; + } + + ret = rpc_port_proxy_mmi_connect_sync(m_rpc_h); + if (ret != RPC_PORT_ERROR_NONE) { + LOGE("Try to connect again"); + //Retry + ret = retry_connection(m_rpc_h); + if (ret != RPC_PORT_ERROR_NONE) { + LOGE("Fail to retry connection(%d)", ret); + return MMI_ERROR_OPERATION_FAILED; + } + } + + LOGI("Connected to mmi manager"); + + return MMI_ERROR_NONE; +} + +int CommunicationChannelTIDL::disconnect(void) { + if (!m_rpc_h) { + LOGE("RPC Handle does not exist"); + return MMI_ERROR_OPERATION_FAILED; + } + + rpc_port_proxy_mmi_destroy(m_rpc_h); + rpc_port_deregister_proc_info(); + m_rpc_h = nullptr; + + m_connected = false; + + LOGI("disconnected from mmi manager"); + + return MMI_ERROR_NONE; +} + +int CommunicationChannelTIDL::send(Message *message) { + auto clientMessage = static_cast(message); + CLIENT_MESSAGE_TYPE type = clientMessage->message_type; + + switch(type) { + case CLIENT_MESSAGE_TYPE::WORKFLOW_INSTANCE_CREATE: { + auto subclass = static_cast(message); + workflow_instance_create(subclass->local_workflow_instance_id, subclass->workflow_type, subclass->user_data); + } + break; + case CLIENT_MESSAGE_TYPE::WORKFLOW_INSTANCE_DESTROY: { + auto subclass = static_cast(message); + workflow_instance_destroy(subclass->local_workflow_instance_id); + } + break; + case CLIENT_MESSAGE_TYPE::WORKFLOW_INSTANCE_SET_ATTRIBUTE: { + auto subclass = static_cast(message); + workflow_instance_set_attribute(subclass->local_workflow_instance_id, subclass->attribute); + } + break; + case CLIENT_MESSAGE_TYPE::WORKFLOW_INSTANCE_ACTIVATE: { + auto subclass = static_cast(message); + workflow_instance_activate(subclass->local_workflow_instance_id); + } + break; + case CLIENT_MESSAGE_TYPE::WORKFLOW_INSTANCE_DEACTIVATE: { + auto subclass = static_cast(message); + workflow_instance_deactivate(subclass->local_workflow_instance_id); + } + break; + }; + + return MMI_ERROR_NONE; +} + +bool CommunicationChannelTIDL::is_connected(void) { + return m_connected ? true : false; +} + +int CommunicationChannelTIDL::retry_connection(rpc_port_proxy_mmi_h h) { + LOGI("Retry connection"); + + int ret = rpc_port_proxy_mmi_connect_sync(h); + if (ret != RPC_PORT_ERROR_NONE) { + LOGE("Fail to retry connection(%d)", ret); + return MMI_ERROR_OPERATION_FAILED; + } + + return MMI_ERROR_NONE; +} + +int CommunicationChannelTIDL::workflow_instance_create(int local_workflow_instance_id, mmi_standard_workflow_type_e workflow_type, void *user_data) { + LOGI("Creating workflow instance of type (%d) for id (%d)", + workflow_type, local_workflow_instance_id); + + rpc_port_proxy_mmi_h rpc_h = static_cast(m_rpc_h); + if (NULL == rpc_h) { + LOGE("Fail to get tidl rpc info"); + return MMI_ERROR_OPERATION_FAILED; + } + + if (false == is_connected()) { + LOGE("Try to connect again"); + int ret = retry_connection(rpc_h); + if (RPC_PORT_ERROR_NONE != ret) { + LOGE("Fail to retry connection(%d)", ret); + return MMI_ERROR_OPERATION_FAILED; + } + } + + int ret = rpc_port_proxy_mmi_invoke_workflow_instance_create( + rpc_h, local_workflow_instance_id, workflow_type); + if (RPC_PORT_ERROR_NONE != ret) { + LOGE("Failed to create workflow instance(%d)\n", ret); + return MMI_ERROR_OPERATION_FAILED; + } + + ret = workflow_instance_register_result_callback(local_workflow_instance_id, user_data); + if (MMI_ERROR_NONE != ret) { + LOGE("Failed to register result callback(%d)\n", ret); + return MMI_ERROR_OPERATION_FAILED; + } + + return MMI_ERROR_NONE; +} + +int CommunicationChannelTIDL::workflow_instance_destroy(int local_workflow_instance_id) { + LOGI("Destroying workflow instance of id (%d)", local_workflow_instance_id); + + rpc_port_proxy_mmi_h rpc_h = static_cast(m_rpc_h); + if (NULL == rpc_h) { + LOGE("Fail to get tidl rpc info"); + return MMI_ERROR_OPERATION_FAILED; + } + + if (false == is_connected()) { + LOGE("Try to connect again"); + int ret = retry_connection(rpc_h); + if (RPC_PORT_ERROR_NONE != ret) { + LOGE("Fail to retry connection(%d)", ret); + return MMI_ERROR_OPERATION_FAILED; + } + } + + int ret = rpc_port_proxy_mmi_invoke_workflow_instance_destroy( + rpc_h, local_workflow_instance_id); + if (RPC_PORT_ERROR_NONE != ret) { + LOGE("Failed to destroy workflow instance(%d)\n", ret); + return MMI_ERROR_OPERATION_FAILED; + } + + return MMI_ERROR_NONE; +} + +int CommunicationChannelTIDL::workflow_instance_set_attribute(int local_workflow_instance_id, mmi_attribute_h attribute) { + LOGI("Setting attribute for workflow instance (%d)", local_workflow_instance_id); + int ret = MMI_ERROR_NONE; + + if (NULL == attribute) { + LOGE("Parameter is NULL or empty"); + return MMI_ERROR_INVALID_PARAMETER; + } + + rpc_port_proxy_mmi_h rpc_h = static_cast(m_rpc_h); + if (NULL == rpc_h) { + LOGE("Fail to get tidl rpc info"); + return MMI_ERROR_OPERATION_FAILED; + } + + if (false == is_connected()) { + LOGE("Try to connect again"); + int ret = retry_connection(rpc_h); + if (RPC_PORT_ERROR_NONE != ret) { + LOGE("Fail to retry connection(%d)", ret); + return MMI_ERROR_OPERATION_FAILED; + } + } + + unsigned char *attribute_bytes = nullptr; + size_t attribute_byte_size = 0; + mmi_attribute_to_bytes(attribute, &attribute_bytes, &attribute_byte_size); + + bundle *encoded = bundle_create(); + bundle_add_byte(encoded, "data", attribute_bytes, attribute_byte_size); + + if (encoded) { + int ret = rpc_port_proxy_mmi_invoke_workflow_instance_set_attribute(rpc_h, local_workflow_instance_id, encoded); + bundle_free(encoded); + + if (RPC_PORT_ERROR_NONE != ret) { + LOGE("Failed to set workflow attribute(%d)\n", ret); + ret = MMI_ERROR_OPERATION_FAILED; + } + } + + return ret; +} + +int CommunicationChannelTIDL::workflow_instance_activate(int local_workflow_instance_id) { + LOGI("Activating workflow instance of id (%d)", local_workflow_instance_id); + + rpc_port_proxy_mmi_h rpc_h = static_cast(m_rpc_h); + if (NULL == rpc_h) { + LOGE("Fail to get tidl rpc info"); + return MMI_ERROR_OPERATION_FAILED; + } + + if (false == is_connected()) { + LOGE("Try to connect again"); + int ret = retry_connection(rpc_h); + if (RPC_PORT_ERROR_NONE != ret) { + LOGE("Fail to retry connection(%d)", ret); + return MMI_ERROR_OPERATION_FAILED; + } + } + + int ret = rpc_port_proxy_mmi_invoke_workflow_instance_activate( + rpc_h, local_workflow_instance_id); + if (RPC_PORT_ERROR_NONE != ret) { + LOGE("Failed to activate workflow instance(%d)\n", ret); + return MMI_ERROR_OPERATION_FAILED; + } + + return MMI_ERROR_NONE; +} + +int CommunicationChannelTIDL::workflow_instance_deactivate(int local_workflow_instance_id) { + LOGI("Deactivating workflow instance of id (%d)", local_workflow_instance_id); + + rpc_port_proxy_mmi_h rpc_h = static_cast(m_rpc_h); + if (NULL == rpc_h) { + LOGE("Fail to get tidl rpc info"); + return MMI_ERROR_OPERATION_FAILED; + } + + if (false == is_connected()) { + LOGE("Try to connect again"); + int ret = retry_connection(rpc_h); + if (RPC_PORT_ERROR_NONE != ret) { + LOGE("Fail to retry connection(%d)", ret); + return MMI_ERROR_OPERATION_FAILED; + } + } + + int ret = rpc_port_proxy_mmi_invoke_workflow_instance_deactivate( + rpc_h, local_workflow_instance_id); + if (RPC_PORT_ERROR_NONE != ret) { + LOGE("Failed to deactivate workflow instance(%d)\n", ret); + return MMI_ERROR_OPERATION_FAILED; + } + + return MMI_ERROR_NONE; +} + +int CommunicationChannelTIDL::workflow_instance_register_result_callback(int local_workflow_instance_id, void *user_data) { + LOGI("Registering result callback for workflow instance of id (%d)", local_workflow_instance_id); + + if (nullptr == user_data) { + LOGE("Parameter is NULL or empty"); + return MMI_ERROR_INVALID_PARAMETER; + } + + rpc_port_proxy_mmi_h rpc_h = static_cast(m_rpc_h); + if (NULL == rpc_h) { + LOGE("Fail to get tidl rpc info"); + return MMI_ERROR_OPERATION_FAILED; + } + + if (false == is_connected()) { + LOGE("Try to connect again"); + int ret = retry_connection(rpc_h); + if (RPC_PORT_ERROR_NONE != ret) { + LOGE("Fail to retry connection(%d)", ret); + return MMI_ERROR_OPERATION_FAILED; + } + } + + rpc_port_proxy_mmi_result_cb_h result_cb_h = make_callback_handle(result_cb, user_data); + + int ret = rpc_port_proxy_mmi_invoke_workflow_instance_register_result_callback( + rpc_h, local_workflow_instance_id, result_cb_h); + if (RPC_PORT_ERROR_NONE != ret) { + LOGE("Failed to register result callback(%d)\n", ret); + rpc_port_proxy_mmi_result_cb_destroy(result_cb_h); + return MMI_ERROR_OPERATION_FAILED; + } + + LOGI("register result callback done"); + return MMI_ERROR_NONE; +} + +rpc_port_proxy_mmi_result_cb_h CommunicationChannelTIDL::make_callback_handle(rpc_port_proxy_mmi_result_cb_cb callback, void *user_data) { + rpc_port_proxy_mmi_result_cb_h result_cb_h; + int ret = rpc_port_proxy_mmi_result_cb_create(&result_cb_h); + if (RPC_PORT_ERROR_NONE != ret) { + LOGE("Failed to create result callback handle (error:%d)\n", ret); + return nullptr; + } + rpc_port_proxy_mmi_result_cb_set_callback(result_cb_h, callback, user_data); + rpc_port_proxy_mmi_result_cb_set_once(result_cb_h, false); + if (NULL == result_cb_h) { + LOGE("Failed to set result callback handle\n"); + return nullptr; + } + return result_cb_h; +} + +}; // namespace communication + +}; // namespace mmi diff --git a/src/mmi/mmi-ipc-tidl.h b/src/mmi/mmi-ipc-tidl.h new file mode 100644 index 0000000..8f794c7 --- /dev/null +++ b/src/mmi/mmi-ipc-tidl.h @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2022 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 __MMI_IPC_TIDL_H__ +#define __MMI_IPC_TIDL_H__ + +#include "mmi.h" + +#include "mmi-communication-channel.h" + +#include "mmi_proxy.h" + +namespace mmi { + +namespace communication { + +class CommunicationChannelTIDL : public CommunicationChannelClient { +public: + CommunicationChannelTIDL(); + ~CommunicationChannelTIDL(); + + int connect() override; + int disconnect(void) override; + int send(communication::Message *message) override; + +private: + int workflow_instance_create(int local_workflow_instance_id, mmi_standard_workflow_type_e workflow_type, void *user_data); + int workflow_instance_destroy(int local_workflow_instance_id); + int workflow_instance_set_attribute(int local_workflow_instance_id, mmi_attribute_h attribute); + int workflow_instance_activate(int local_workflow_instance_id); + int workflow_instance_deactivate(int local_workflow_instance_id); + int workflow_instance_register_result_callback(int local_workflow_instance_id, void *user_data); + + bool is_connected(void); + int retry_connection(rpc_port_proxy_mmi_h h); + + static void on_connected(rpc_port_proxy_mmi_h h, void *user_data); + static void on_disconnected(rpc_port_proxy_mmi_h h, void *user_data); + static void on_rejected(rpc_port_proxy_mmi_h h, void *user_data); + + static rpc_port_proxy_mmi_result_cb_h make_callback_handle(rpc_port_proxy_mmi_result_cb_cb callback, void *user_data); + +private: + rpc_port_proxy_mmi_h m_rpc_h{nullptr}; + const std::string m_stub_appid{"mmi-manager"}; + bool m_connected{false}; +}; + +}; // namespace communication + +}; // namespace mmi + +#endif //__MMI_IPC_TIDL_H__ diff --git a/src/mmi-dbg.h b/src/mmi/mmi-log.h similarity index 92% rename from src/mmi-dbg.h rename to src/mmi/mmi-log.h index 398875e..7718fcd 100644 --- a/src/mmi-dbg.h +++ b/src/mmi/mmi-log.h @@ -15,8 +15,8 @@ * */ -#ifndef __MMI_DBG_H__ -#define __MMI_DBG_H__ +#ifndef __MMI_LOG_H__ +#define __MMI_LOG_H__ #include @@ -41,4 +41,4 @@ #define _W LOGW #endif -#endif // __MMI_DBG_H__ +#endif // __MMI_LOG_H__ diff --git a/src/mmi/mmi-node-logic.cpp b/src/mmi/mmi-node-logic.cpp new file mode 100644 index 0000000..36e003d --- /dev/null +++ b/src/mmi/mmi-node-logic.cpp @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2023 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 "mmi.h" +#include "mmi-node-logic.h" + +#include "mmi-log.h" + +int mmi_standard_node_create_logic(mmi_standard_node_logic_type_e type, mmi_node_h *node) { + if (nullptr == node) { + return MMI_ERROR_INVALID_PARAMETER; + } + *node = new mmi_node_s; + (*node)->type = MMI_STANDARD_NODE_TYPE_LOGIC; + (*node)->sub_type = (int)type; + (*node)->ports = nullptr; + (*node)->port_count = 0; + memset(&(*node)->callbacks, 0, sizeof(mmi_node_callbacks)); + return MMI_ERROR_NONE; +} + +int mmi_standard_node_get_logic_type(mmi_node_h node, mmi_standard_node_logic_type_e *type) { + if (nullptr == node) { + return MMI_ERROR_INVALID_PARAMETER; + } + *type = (mmi_standard_node_logic_type_e)node->sub_type; + return MMI_ERROR_NONE; +} + +int mmi_standard_node_register_logic(mmi_standard_node_logic_type_e type, mmi_node_callbacks *callbacks, mmi_node_h node) { + if (nullptr == callbacks || nullptr == node) { + return MMI_ERROR_INVALID_PARAMETER; + } + return MMI_ERROR_NONE; +} + diff --git a/src/mmi/mmi-node-processor.cpp b/src/mmi/mmi-node-processor.cpp new file mode 100644 index 0000000..8634a81 --- /dev/null +++ b/src/mmi/mmi-node-processor.cpp @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2023 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 "mmi.h" +#include "mmi-node-processor.h" + +#include "mmi-log.h" + +int mmi_standard_node_create_processor(mmi_standard_node_processor_type_e type, mmi_node_h *node) { + if (nullptr == node) { + return MMI_ERROR_INVALID_PARAMETER; + } + *node = new mmi_node_s; + (*node)->type = MMI_STANDARD_NODE_TYPE_PROCESSOR; + (*node)->sub_type = (int)type; + (*node)->ports = nullptr; + (*node)->port_count = 0; + memset(&(*node)->callbacks, 0, sizeof(mmi_node_callbacks)); + return MMI_ERROR_NONE; +} + +int mmi_standard_node_get_processor_type(mmi_node_h node, mmi_standard_node_processor_type_e *type) { + if (nullptr == node || nullptr == type) { + return MMI_ERROR_INVALID_PARAMETER; + } + *type = (mmi_standard_node_processor_type_e)node->sub_type; + return MMI_ERROR_NONE; +} + +int mmi_standard_node_register_processor(mmi_standard_node_processor_type_e type, mmi_node_callbacks *callbacks, mmi_node_h node) { + if (nullptr == callbacks || nullptr == node) { + return MMI_ERROR_INVALID_PARAMETER; + } + return MMI_ERROR_NONE; +} + diff --git a/src/mmi/mmi-node-source.cpp b/src/mmi/mmi-node-source.cpp new file mode 100644 index 0000000..30abd0d --- /dev/null +++ b/src/mmi/mmi-node-source.cpp @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2023 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 "mmi.h" +#include "mmi-node-source.h" + +#include "mmi-log.h" + +int mmi_standard_node_create_source(mmi_standard_node_source_type_e type, mmi_node_h *node) { + if (nullptr == node) { + return MMI_ERROR_INVALID_PARAMETER; + } + *node = new mmi_node_s; + (*node)->type = MMI_STANDARD_NODE_TYPE_SOURCE; + (*node)->sub_type = (int)type; + (*node)->ports = nullptr; + (*node)->port_count = 0; + memset(&(*node)->callbacks, 0, sizeof(mmi_node_callbacks)); + return MMI_ERROR_NONE; +} + +int mmi_standard_node_get_source_type(mmi_node_h node, mmi_standard_node_source_type_e *type) { + if (nullptr == type || nullptr == node) { + return MMI_ERROR_INVALID_PARAMETER; + } + *type = (mmi_standard_node_source_type_e)node->sub_type; + return MMI_ERROR_NONE; +} + diff --git a/src/mmi/mmi-node.cpp b/src/mmi/mmi-node.cpp new file mode 100644 index 0000000..1faffa9 --- /dev/null +++ b/src/mmi/mmi-node.cpp @@ -0,0 +1,307 @@ +/* + * Copyright (c) 2023 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 "mmi.h" +#include "mmi-node.h" +#include "mmi-plugin-storage.h" + +#include "mmi-log.h" + +#include + +int mmi_node_add_port(mmi_node_h node, mmi_port_h port) { + if (nullptr == node || nullptr == port) { + LOGE("[ERROR] parameter is null"); + return MMI_ERROR_INVALID_PARAMETER; + } + + mmi_node_s *node_ptr = static_cast(node); + + mmi_port_h *ports = node_ptr->ports; + size_t port_count = node_ptr->port_count; + + node_ptr->ports = new(std::nothrow) mmi_port_h[port_count + 1]; + if (nullptr == node_ptr->ports) { + node_ptr->ports = ports; + LOGE("[ERROR] failed to allocate memory"); + return MMI_ERROR_OUT_OF_MEMORY; + } + if (port_count > 0) { + memcpy(node_ptr->ports, ports, sizeof(mmi_port_h) * port_count); + delete [] ports; + } + + node_ptr->ports[port_count] = port; + node_ptr->port_count = port_count + 1; + + return MMI_ERROR_NONE; +} + +int mmi_node_find_port(mmi_node_h node, mmi_port_type_e port_type, const char *port_name, mmi_port_h *port) { + if (nullptr == node || nullptr == port_name || nullptr == port) { + LOGE("[ERROR] parameter is null"); + return MMI_ERROR_INVALID_PARAMETER; + } + return MMI_ERROR_NONE; +} + +int mmi_node_link(mmi_node_h from_node, mmi_node_h to_node) { + if (nullptr == from_node || nullptr == to_node) { + LOGE("[ERROR] parameter is null"); + return MMI_ERROR_INVALID_PARAMETER; + } + return MMI_ERROR_NONE; +} + +int mmi_node_link_by_name(mmi_node_h from_node, const char *from_port_name, mmi_node_h to_node, const char *to_port_name) { + if (nullptr == from_node || nullptr == from_port_name || nullptr == to_node || nullptr == to_port_name) { + LOGE("[ERROR] parameter is null"); + return MMI_ERROR_INVALID_PARAMETER; + } + return MMI_ERROR_NONE; +} + +int mmi_node_get_type(mmi_node_h node, mmi_standard_node_type_e *type) { + if (nullptr == node || nullptr == type) { + LOGE("[ERROR] parameter is null"); + return MMI_ERROR_INVALID_PARAMETER; + } + *type = (mmi_standard_node_type_e)node->type; + return MMI_ERROR_NONE; +} + +int mmi_node_get_port_count(mmi_node_h node, size_t *port_count) { + if (nullptr == node || nullptr == port_count) { + LOGE("[ERROR] parameter is null"); + return MMI_ERROR_INVALID_PARAMETER; + } + *port_count = node->port_count; + return MMI_ERROR_NONE; +} + +int mmi_node_get_port(mmi_node_h node, size_t index, mmi_port_h *port) { + if (nullptr == node || nullptr == port) { + LOGE("[ERROR] parameter is null"); + return MMI_ERROR_INVALID_PARAMETER; + } + if (index >= node->port_count) { + LOGE("[ERROR] index is out of range"); + return MMI_ERROR_OPERATION_FAILED; + } + *port = node->ports[index]; + return MMI_ERROR_NONE; +} + +int mmi_node_get_callbacks(mmi_node_h node, mmi_node_callbacks *callbacks) { + if (nullptr == node || nullptr == callbacks) { + LOGE("[ERROR] parameter is null"); + return MMI_ERROR_INVALID_PARAMETER; + } + *callbacks = node->callbacks; + return MMI_ERROR_NONE; +} + +int mmi_node_set_callbacks(mmi_node_h node, mmi_node_callbacks callbacks) { + if (nullptr == node) { + LOGE("[ERROR] parameter is null"); + return MMI_ERROR_INVALID_PARAMETER; + } + node->callbacks = callbacks; + return MMI_ERROR_NONE; +} + +int mmi_node_register(mmi_node_h node) { + if (nullptr == node) { + LOGE("[ERROR] parameter is null"); + return MMI_ERROR_INVALID_PARAMETER; + } + + mmi_standard_node_type_e node_type; + mmi_node_get_type(node, &node_type); + + const char *identifier = mmi_plugin_storage_get_current_module_identifier(); + if (nullptr == identifier) { + LOGE("[ERROR] identifier is null"); + return MMI_ERROR_INVALID_PARAMETER; + } + + mmi_plugin_module_node_list_s list = mmi_plugin_storage_get_node_list(identifier); + + mmi_node_h *nodes = list.nodes; + size_t node_count = list.node_count; + + bool duplicated = false; + for (size_t i = 0; i < node_count; i++) { + mmi_standard_node_type_e type; + mmi_node_get_type(nodes[i], &type); + + if (type == node_type) { + switch (type) { + case MMI_STANDARD_NODE_TYPE_SOURCE: { + mmi_standard_node_source_type_e node_source_type; + mmi_standard_node_get_source_type(node, &node_source_type); + mmi_standard_node_source_type_e source_type; + mmi_standard_node_get_source_type(nodes[i], &source_type); + if (node_source_type == source_type) { + duplicated = true; + } + break; + } + case MMI_STANDARD_NODE_TYPE_PROCESSOR: { + mmi_standard_node_processor_type_e node_processor_type; + mmi_standard_node_get_processor_type(node, &node_processor_type); + mmi_standard_node_processor_type_e processor_type; + mmi_standard_node_get_processor_type(nodes[i], &processor_type); + if (node_processor_type == processor_type) { + duplicated = true; + } + break; + } + case MMI_STANDARD_NODE_TYPE_LOGIC: { + mmi_standard_node_logic_type_e node_logic_type; + mmi_standard_node_get_logic_type(node, &node_logic_type); + mmi_standard_node_logic_type_e logic_type; + mmi_standard_node_get_logic_type(nodes[i], &logic_type); + if (node_logic_type == logic_type) { + duplicated = true; + } + break; + } + case MMI_STANDARD_NODE_TYPE_CONTROLLER: { + mmi_standard_node_controller_type_e node_controller_type; + mmi_standard_node_get_controller_type(node, &node_controller_type); + mmi_standard_node_controller_type_e controller_type; + mmi_standard_node_get_controller_type(nodes[i], &controller_type); + if (node_controller_type == controller_type) { + duplicated = true; + } + break; + } + } + } + if (duplicated) { + mmi_node_h cloned; + mmi_node_clone(node, &cloned); + mmi_node_destroy(nodes[i]); + nodes[i] = cloned; + break; + } + } + + if (!duplicated) { + list.nodes = new(std::nothrow) mmi_node_h[node_count + 1]; + if (nullptr == list.nodes) { + LOGE("[ERROR] failed to allocate memory"); + return MMI_ERROR_OUT_OF_MEMORY; + } + if (node_count > 0) { + memcpy(list.nodes, nodes, sizeof(mmi_node_h) * node_count); + delete [] nodes; + } + mmi_node_h cloned; + mmi_node_clone(node, &cloned); + list.nodes[node_count] = cloned; + list.node_count = node_count + 1; + } + + mmi_plugin_storage_set_node_list(list); + + return MMI_ERROR_NONE; +} + +int mmi_node_clone(mmi_node_h node, mmi_node_h *cloned) { + if (nullptr == node || nullptr == cloned) { + LOGE("[ERROR] parameter is null"); + return MMI_ERROR_INVALID_PARAMETER; + } + + mmi_node_s *node_ptr = static_cast(node); + + mmi_node_s *cloned_ptr = new(std::nothrow) mmi_node_s; + if (nullptr == cloned_ptr) { + LOGE("[ERROR] failed to allocate memory"); + return MMI_ERROR_OUT_OF_MEMORY; + } + + cloned_ptr->type = node_ptr->type; + cloned_ptr->sub_type = node_ptr->sub_type; + cloned_ptr->port_count = node_ptr->port_count; + cloned_ptr->callbacks = node_ptr->callbacks; + + cloned_ptr->ports = new(std::nothrow) mmi_port_h[node_ptr->port_count]; + if (nullptr == cloned_ptr->ports) { + LOGE("[ERROR] failed to allocate memory"); + delete cloned_ptr; + return MMI_ERROR_OUT_OF_MEMORY; + } + + for (int i = 0; i < node_ptr->port_count; i++) { + mmi_port_clone(node_ptr->ports[i], &cloned_ptr->ports[i]); + } + + *cloned = cloned_ptr; + + return MMI_ERROR_NONE; +} + +int mmi_node_destroy(mmi_node_h node) { + if (nullptr == node) { + LOGE("[ERROR] parameter is null"); + return MMI_ERROR_INVALID_PARAMETER; + } + + for (int i = 0; i < node->port_count; i++) { + mmi_port_destroy(node->ports[i]); + } + delete [] node->ports; + delete node; + + return MMI_ERROR_NONE; +} + +int mmi_node_instance_set_attribute(mmi_node_instance_h instance, mmi_attribute_h attribute) { + return MMI_ERROR_NONE; +} + +int mmi_node_instance_find_port(mmi_node_instance_h instance, mmi_port_type_e port_type, const char *port_name, mmi_port_instance_h *port) { + if (instance == NULL || port_name == NULL || port == NULL) { + return MMI_ERROR_INVALID_PARAMETER; + } + *port = mmi_plugin_storage_find_port_instance(instance, port_name); + return MMI_ERROR_NONE; +} + +int mmi_node_instance_find_sibling_port(mmi_port_instance_h instance, const char *port_name, mmi_port_instance_h *sibling) { + if (instance == NULL || port_name == NULL || sibling == NULL) { + return MMI_ERROR_INVALID_PARAMETER; + } + mmi_node_instance_h node_instance = mmi_plugin_storage_find_node_instance_by_port_instance(instance); + if (node_instance == NULL) { + return MMI_ERROR_INVALID_PARAMETER; + } + *sibling = mmi_plugin_storage_find_port_instance(node_instance, port_name); + return MMI_ERROR_NONE; +} + +int mmi_node_instance_emit_signal(mmi_node_instance_h instance, mmi_signal_h signal) { + return MMI_ERROR_NONE; +} + +int mmi_node_instance_update_pending_activation_result(mmi_error_e result) { + return MMI_ERROR_NONE; +} diff --git a/src/mmi/mmi-plugin-storage-impl.h b/src/mmi/mmi-plugin-storage-impl.h new file mode 100644 index 0000000..da08c77 --- /dev/null +++ b/src/mmi/mmi-plugin-storage-impl.h @@ -0,0 +1,123 @@ +/* + * Copyright (c) 2023 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 __MMI_PLUGIN_STORAGE_IMPL_H__ +#define __MMI_PLUGIN_STORAGE_IMPL_H__ + +#include +#include +#include + +#include "mmi-log.h" + +class PluginStorageImpl { +public: + PluginStorageImpl() { + } + + ~PluginStorageImpl() { + for (auto &it : m_node_lists) { + for (int loop = 0; loop < it.second.node_count; loop++) { + mmi_node_destroy(it.second.nodes[loop]); + } + delete [] it.second.nodes; + } + for (auto &it : m_workflow_lists) { + for (int loop = 0; loop < it.second.workflow_count; loop++) { + mmi_workflow_destroy(it.second.workflows[loop]); + } + delete [] it.second.workflows; + } + } + + void set_current_identifier(const char *identifier) { + if (identifier == nullptr) { + LOGE("Identifier is null"); + return; + } + m_current_identifier = identifier; + } + + const char* get_current_identifier() { + return m_current_identifier.c_str(); + } + + void set_node_list(mmi_plugin_module_node_list_s node_list) { + m_node_lists[m_current_identifier] = node_list; + } + + void set_workflow_list(mmi_plugin_module_workflow_list_s workflow_list) { + m_workflow_lists[m_current_identifier] = workflow_list; + } + + mmi_plugin_module_node_list_s get_node_list(const char *identifier) { + return m_node_lists[identifier]; + } + + mmi_plugin_module_workflow_list_s get_workflow_list(const char *identifier) { + return m_workflow_lists[identifier]; + } + + void add_node_instance_info(mmi_plugin_module_node_instance_info_s *info) { + m_node_instance_infos.push_back(*info); + } + + void remove_node_instance_info(mmi_node_instance_h node_instance) { + for (auto it = m_node_instance_infos.begin(); it != m_node_instance_infos.end(); ++it) { + if (it->node == node_instance) { + m_node_instance_infos.erase(it); + break; + } + } + } + + mmi_port_instance_h find_port_instance(mmi_node_instance_h node_instance, const char *port_name) { + for (auto &info : m_node_instance_infos) { + if (info.node == node_instance) { + for (int loop = 0; loop < info.port_info_count; loop++) { + if (strncmp(info.port_infos[loop].name, port_name, MMI_NAME_MAX_LENGTH) == 0) { + return info.port_infos[loop].port; + } + } + } + } + return nullptr; + } + + mmi_node_instance_h find_node_instance_by_port_instance(mmi_port_instance_h port_instance) { + for (auto &info : m_node_instance_infos) { + for (int loop = 0; loop < info.port_info_count; loop++) { + if (info.port_infos[loop].port == port_instance) { + return info.node; + } + } + } + return nullptr; + } +private: + std::string m_current_identifier; + + std::map m_node_lists; + std::map m_workflow_lists; + + /* Better to use std::map since find_port_instance() would be called frequently */ + std::vector m_node_instance_infos; +}; + + +#endif /* __MMI_PLUGIN_STORAGE_IMPL_H__ */ diff --git a/src/mmi/mmi-plugin-storage.cpp b/src/mmi/mmi-plugin-storage.cpp new file mode 100644 index 0000000..b9aa92f --- /dev/null +++ b/src/mmi/mmi-plugin-storage.cpp @@ -0,0 +1,85 @@ +/* + * Copyright (c) 2023 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 "mmi.h" +#include "mmi-plugin-storage.h" +#include "mmi-plugin-storage-impl.h" + +PluginStorageImpl g_plugin_storage_impl; + +void mmi_plugin_storage_set_current_module_identifier(const char *identifier) { + g_plugin_storage_impl.set_current_identifier(identifier); +} + +const char* mmi_plugin_storage_get_current_module_identifier() { + return g_plugin_storage_impl.get_current_identifier(); +} + +void mmi_plugin_storage_set_node_list(mmi_plugin_module_node_list_s node_list) { + g_plugin_storage_impl.set_node_list(node_list); +} + +void mmi_plugin_storage_set_workflow_list(mmi_plugin_module_workflow_list_s workflow_list) { + g_plugin_storage_impl.set_workflow_list(workflow_list); +} + +mmi_plugin_module_node_list_s mmi_plugin_storage_get_node_list(const char *identifier) { + return g_plugin_storage_impl.get_node_list(identifier); +} + +mmi_plugin_module_workflow_list_s mmi_plugin_storage_get_workflow_list(const char *identifier) { + return g_plugin_storage_impl.get_workflow_list(identifier); +} + +void mmi_plugin_storage_add_node_instance_info(mmi_plugin_module_node_instance_info_s *info) { + g_plugin_storage_impl.add_node_instance_info(info); +} + +void mmi_plugin_storage_remove_node_instance_info(mmi_node_instance_h node_instance) { + g_plugin_storage_impl.remove_node_instance_info(node_instance); +} + +mmi_port_instance_h mmi_plugin_storage_find_port_instance( + mmi_node_instance_h node_instance, const char *port_name) { + return g_plugin_storage_impl.find_port_instance(node_instance, port_name); +} + +mmi_node_instance_h mmi_plugin_storage_find_node_instance_by_port_instance(mmi_port_instance_h port_instance) { + return g_plugin_storage_impl.find_node_instance_by_port_instance(port_instance); +} + +static mmi_plugin_module_event_handler_info_s g_event_handler_info{nullptr, }; +static void* g_event_handler_user_data = nullptr; + +void mmi_plugin_storage_set_plugin_module_event_handler( + mmi_plugin_module_event_handler_info_s handler_info, void *user_data) { + g_event_handler_info = handler_info; + g_event_handler_user_data = user_data; +} + +void mmi_plugin_storage_unset_plugin_module_event_handler() { + memset(&g_event_handler_info, 0x00, sizeof(g_event_handler_info)); + g_event_handler_user_data = nullptr; +} + +mmi_plugin_module_event_handler_info_s mmi_plugin_storage_get_plugin_module_event_handler() { + return g_event_handler_info; +} + +void* mmi_plugin_storage_get_plugin_module_event_handler_user_data() { + return g_event_handler_user_data; +} diff --git a/src/mmi/mmi-port.cpp b/src/mmi/mmi-port.cpp new file mode 100644 index 0000000..2358a9e --- /dev/null +++ b/src/mmi/mmi-port.cpp @@ -0,0 +1,205 @@ +/* + * Copyright (c) 2023 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 "mmi.h" +#include "mmi-log.h" +#include "mmi-port.h" +#include "mmi-plugin-storage.h" + +#include +#include + +MMI_API int mmi_port_create(mmi_port_h *port) { + if (nullptr == port) { + LOGE("[ERROR] parameter is null"); + return MMI_ERROR_INVALID_PARAMETER; + } + + mmi_port_s *port_s = new (std::nothrow) mmi_port_s; + if (nullptr == port_s) { + LOGE("[ERROR] failed to create port"); + return MMI_ERROR_OUT_OF_MEMORY; + } + memset(port_s, 0, sizeof(mmi_port_s)); + + *port = port_s; + + return MMI_ERROR_NONE; +} + +MMI_API int mmi_port_get_name(mmi_port_h port, char **name, size_t *length) { + if (nullptr == port || nullptr == name || nullptr == length) { + LOGE("[ERROR] parameter is null"); + return MMI_ERROR_INVALID_PARAMETER; + } + + mmi_port_s *port_s = static_cast(port); + + *name = port_s->name; + *length = strlen(*name); + + return MMI_ERROR_NONE; +} + +MMI_API int mmi_port_get_type(mmi_port_h port, mmi_port_type_e *type) { + if (nullptr == port || nullptr == type) { + LOGE("[ERROR] parameter is null"); + return MMI_ERROR_INVALID_PARAMETER; + } + + mmi_port_s *port_s = static_cast(port); + + *type = port_s->type; + + return MMI_ERROR_NONE; +} + +MMI_API int mmi_port_get_data_type(mmi_port_h port, mmi_data_type_e *data_type) { + if (nullptr == port || nullptr == data_type) { + LOGE("[ERROR] parameter is null"); + return MMI_ERROR_INVALID_PARAMETER; + } + + mmi_port_s *port_s = static_cast(port); + + *data_type = port_s->data_type; + + return MMI_ERROR_NONE; +} + +MMI_API int mmi_port_get_callbacks(mmi_port_h port, mmi_port_callbacks *callbacks) { + if (nullptr == port || nullptr == callbacks) { + LOGE("[ERROR] parameter is null"); + return MMI_ERROR_INVALID_PARAMETER; + } + + mmi_port_s *port_s = static_cast(port); + + *callbacks = port_s->callbacks; + + return MMI_ERROR_NONE; +} + +MMI_API int mmi_port_set_name(mmi_port_h port, const char *name) { + if (nullptr == port || nullptr == name) { + LOGE("[ERROR] parameter is null"); + return MMI_ERROR_INVALID_PARAMETER; + } + + mmi_port_s *port_s = static_cast(port); + + std::snprintf(port_s->name, MMI_NAME_MAX_LENGTH, "%s", name); + + return MMI_ERROR_NONE; +} + +MMI_API int mmi_port_set_type(mmi_port_h port, mmi_port_type_e type) { + if (nullptr == port) { + LOGE("[ERROR] parameter is null"); + return MMI_ERROR_INVALID_PARAMETER; + } + + mmi_port_s *port_s = static_cast(port); + + port_s->type = type; + + return MMI_ERROR_NONE; +} + +MMI_API int mmi_port_set_data_type(mmi_port_h port, mmi_data_type_e data_type) { + if (nullptr == port) { + LOGE("[ERROR] parameter is null"); + return MMI_ERROR_INVALID_PARAMETER; + } + + mmi_port_s *port_s = static_cast(port); + + port_s->data_type = data_type; + + return MMI_ERROR_NONE; +} + +MMI_API int mmi_port_set_callbacks(mmi_port_h port, mmi_port_callbacks callbacks) { + if (nullptr == port) { + LOGE("[ERROR] parameter is null"); + return MMI_ERROR_INVALID_PARAMETER; + } + + mmi_port_s *port_s = static_cast(port); + + port_s->callbacks = callbacks; + + return MMI_ERROR_NONE; +} + +MMI_API int mmi_port_clone(mmi_port_h port, mmi_port_h *cloned) { + if (nullptr == port || nullptr == cloned) { + LOGE("[ERROR] parameter is null"); + return MMI_ERROR_INVALID_PARAMETER; + } + + mmi_port_s *port_s = static_cast(port); + + *cloned = new (std::nothrow) mmi_port_s; + if (nullptr == *cloned) { + LOGE("[ERROR] failed to clone port"); + return MMI_ERROR_OUT_OF_MEMORY; + } + + strncpy((*cloned)->name, port_s->name, MMI_NAME_MAX_LENGTH); + (*cloned)->type = port_s->type; + (*cloned)->data_type = port_s->data_type; + (*cloned)->callbacks = port_s->callbacks; + + return MMI_ERROR_NONE; +} + +MMI_API int mmi_port_destroy(mmi_port_h port) { + if (nullptr == port) { + LOGE("[ERROR] parameter is null"); + return MMI_ERROR_INVALID_PARAMETER; + } + + delete port; + + return MMI_ERROR_NONE; +} + +MMI_API int mmi_port_instance_generate_output(mmi_port_instance_h instance, mmi_data_h data) { + mmi_plugin_module_event_handler_info_s event_handler_info = mmi_plugin_storage_get_plugin_module_event_handler(); + void *user_data = mmi_plugin_storage_get_plugin_module_event_handler_user_data(); + + mmi_plugin_module_port_instance_output_handler_func output_handler = event_handler_info.port_instance_output_handler; + if (output_handler) { + output_handler(instance, data, user_data); + } + + return MMI_ERROR_NONE; +} + +MMI_API int mmi_port_instance_request_auto_transform(mmi_port_instance_h instance, const char *from_format, const char *to_format) { + return MMI_ERROR_NONE; +} + +MMI_API int mmi_port_instance_request_input_data_format(mmi_port_instance_h instance, const char *format) { + return MMI_ERROR_NONE; +} + +MMI_API int mmi_port_instance_announce_output_data_format(mmi_port_instance_h instance, const char *format) { + return MMI_ERROR_NONE; +} diff --git a/src/mmi/mmi-primitive-value.cpp b/src/mmi/mmi-primitive-value.cpp new file mode 100644 index 0000000..529086b --- /dev/null +++ b/src/mmi/mmi-primitive-value.cpp @@ -0,0 +1,549 @@ +/* + * Copyright (c) 2023 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 "mmi-log.h" + + +struct mmi_primitive_value_s { + mmi_primitive_value_type_e type; + void *data; + size_t datalen; +}; + +static const size_t g_unit_size = sizeof(mmi_primitive_value_h); + + +static inline size_t get_array_count(mmi_primitive_value_h array) { + if (nullptr != array) { + return array->datalen / g_unit_size; + } + + return 0; +} + +static inline mmi_primitive_value_h *get_element_pointer_in_array(mmi_primitive_value_h array, size_t index) { + return reinterpret_cast(array->data) + index; +} + +int mmi_primitive_value_create_int(int data, mmi_primitive_value_h *handle) { + if (nullptr == handle) { + _E("[ERROR] Some parameters are invalid"); + return MMI_ERROR_INVALID_PARAMETER; + } + + auto primitive_value = reinterpret_cast(malloc(sizeof(mmi_primitive_value_s))); + if (nullptr == primitive_value) { + _E("[ERROR] Fail to allocate memory for primitive value"); + return MMI_ERROR_OUT_OF_MEMORY; + } + + primitive_value->type = MMI_PRIMITIVE_VALUE_TYPE_INT; + primitive_value->data = reinterpret_cast(malloc(sizeof(int))); + if (nullptr == primitive_value->data) { + _E("[ERROR] Fail to allocate memory for raw data"); + free(primitive_value); + return MMI_ERROR_OUT_OF_MEMORY; + } + + *(reinterpret_cast(primitive_value->data)) = data; + primitive_value->datalen = sizeof(int); + + *handle = primitive_value; + + _D("[DEBUG] Success to create for int(%d)", data); + return MMI_ERROR_NONE; +} + +int mmi_primitive_value_create_float(float data, mmi_primitive_value_h *handle) { + if (nullptr == handle) { + _E("[ERROR] Some parameters are invalid"); + return MMI_ERROR_INVALID_PARAMETER; + } + + auto primitive_value = reinterpret_cast(malloc(sizeof(mmi_primitive_value_s))); + if (nullptr == primitive_value) { + _E("[ERROR] Fail to allocate memory for primitive value"); + return MMI_ERROR_OUT_OF_MEMORY; + } + + primitive_value->type = MMI_PRIMITIVE_VALUE_TYPE_FLOAT; + primitive_value->data = reinterpret_cast(malloc(sizeof(float))); + if (nullptr == primitive_value->data) { + _E("[ERROR] Fail to allocate memory for raw data"); + free(primitive_value); + return MMI_ERROR_OUT_OF_MEMORY; + } + + *(reinterpret_cast(primitive_value->data)) = data; + primitive_value->datalen = sizeof(float); + + *handle = primitive_value; + + _D("[DEBUG] Success to create for float(%f)", data); + return MMI_ERROR_NONE; +} + +int mmi_primitive_value_create_string(const char *data, mmi_primitive_value_h *handle) { + if (nullptr == handle || nullptr == data) { + _E("[ERROR] Some parameters are invalid"); + return MMI_ERROR_INVALID_PARAMETER; + } + + auto primitive_value = reinterpret_cast(malloc(sizeof(mmi_primitive_value_s))); + if (nullptr == primitive_value) { + _E("[ERROR] Fail to allocate memory for primitive value"); + return MMI_ERROR_OUT_OF_MEMORY; + } + + primitive_value->type = MMI_PRIMITIVE_VALUE_TYPE_STRING; + primitive_value->data = reinterpret_cast(strdup(data)); + primitive_value->datalen = strlen(data) + 1; /* including null character */ + + *handle = primitive_value; + + _D("[DEBUG] Success to create for string(%s)", data); + return MMI_ERROR_NONE; +} + +int mmi_primitive_value_create_bool(bool data, mmi_primitive_value_h *handle) { + if (nullptr == handle) { + _E("[ERROR] Some parameters are invalid"); + return MMI_ERROR_INVALID_PARAMETER; + } + + auto primitive_value = reinterpret_cast(malloc(sizeof(mmi_primitive_value_s))); + if (nullptr == primitive_value) { + _E("[ERROR] Fail to allocate memory for primitive value"); + return MMI_ERROR_OUT_OF_MEMORY; + } + + primitive_value->type = MMI_PRIMITIVE_VALUE_TYPE_BOOL; + primitive_value->data = reinterpret_cast(malloc(sizeof(int))); + if (nullptr == primitive_value->data) { + _E("[ERROR] Fail to allocate memory for raw data"); + free(primitive_value); + return MMI_ERROR_OUT_OF_MEMORY; + } + + *(reinterpret_cast(primitive_value->data)) = static_cast(data); + primitive_value->datalen = sizeof(int); + + *handle = primitive_value; + + _D("[DEBUG] Success to create for boolean(%s)", data ? "true" : "false"); + return MMI_ERROR_NONE; +} + +int mmi_primitive_value_create_array(mmi_primitive_value_h *handle) { + if (nullptr == handle) { + _E("[ERROR] Some parameters are invalid"); + return MMI_ERROR_INVALID_PARAMETER; + } + + auto primitive_value = reinterpret_cast(malloc(sizeof(mmi_primitive_value_s))); + if (nullptr == primitive_value) { + _E("[ERROR] Fail to allocate memory for primitive value"); + return MMI_ERROR_OUT_OF_MEMORY; + } + + primitive_value->type = MMI_PRIMITIVE_VALUE_TYPE_ARRAY; + primitive_value->data = nullptr; + primitive_value->datalen = 0; + + *handle = primitive_value; + + _D("[DEBUG] Success to create empty array"); + return MMI_ERROR_NONE; +} + +int mmi_primitive_value_add_array_element(mmi_primitive_value_h array, mmi_primitive_value_h element) { + if (nullptr == array || nullptr == element) { + _E("[ERROR] Some parameters are invalid"); + return MMI_ERROR_INVALID_PARAMETER; + } + + if (array->type != MMI_PRIMITIVE_VALUE_TYPE_ARRAY) { + _E("[ERROR] Data type is not matched"); + return MMI_ERROR_INVALID_PARAMETER; + } + + size_t count = get_array_count(array); + if (count > 0) { + mmi_primitive_value_h first = nullptr; + mmi_primitive_value_get_array_element(array, 0, &first); + + if (first->type != element->type) { + _E("[ERROR] Element type is not matched"); + return MMI_ERROR_INVALID_PARAMETER; + } + } + + auto array_data = reinterpret_cast(array->data); + array_data = reinterpret_cast(realloc(array_data, g_unit_size * (count + 1))); + if (array_data) { + array_data[count] = element; + + array->data = array_data; + array->datalen = array->datalen + g_unit_size; + } else { + _E("[ERROR] Fail to allocate memory for array element"); + return MMI_ERROR_OUT_OF_MEMORY; + } + + _D("[DEBUG] Success to add element(%zu)", get_array_count(array)); + return MMI_ERROR_NONE; +} + +int mmi_primitive_value_get_type(mmi_primitive_value_h handle, mmi_primitive_value_type_e *type) { + if (nullptr == handle || nullptr == type) { + _E("[ERROR] Some parameters are invalid"); + return MMI_ERROR_INVALID_PARAMETER; + } + + *type = handle->type; + + _D("[DEBUG] Success to get type(%d)", *type); + return MMI_ERROR_NONE; +} + +int mmi_primitive_value_get_int(mmi_primitive_value_h handle, int *value) { + if (nullptr == handle || nullptr == value) { + _E("[ERROR] Some parameters are invalid"); + return MMI_ERROR_INVALID_PARAMETER; + } + + if (handle->type != MMI_PRIMITIVE_VALUE_TYPE_INT) { + _E("[ERROR] Data type is not matched"); + return MMI_ERROR_INVALID_PARAMETER; + } + + *value = *(reinterpret_cast(handle->data)); + + _D("[DEBUG] Success to get value: int(%d)", *value); + return MMI_ERROR_NONE; +} + +int mmi_primitive_value_get_float(mmi_primitive_value_h handle, float *value) { + if (nullptr == handle || nullptr == value) { + _E("[ERROR] Some parameters are invalid"); + return MMI_ERROR_INVALID_PARAMETER; + } + + if (handle->type != MMI_PRIMITIVE_VALUE_TYPE_FLOAT) { + _E("[ERROR] Data type is not matched"); + return MMI_ERROR_INVALID_PARAMETER; + } + + *value = *(reinterpret_cast(handle->data)); + + _D("[DEBUG] Success to get value: float(%f)", *value); + return MMI_ERROR_NONE; +} + +int mmi_primitive_value_get_string(mmi_primitive_value_h handle, const char **string) { + if (nullptr == handle || nullptr == string) { + _E("[ERROR] Some parameters are invalid"); + return MMI_ERROR_INVALID_PARAMETER; + } + + if (handle->type != MMI_PRIMITIVE_VALUE_TYPE_STRING) { + _E("[ERROR] Data type is not matched"); + return MMI_ERROR_INVALID_PARAMETER; + } + + *string = reinterpret_cast(handle->data); + + _D("[DEBUG] Success to get value: string(%s)", *string); + return MMI_ERROR_NONE; +} + +int mmi_primitive_value_get_bool(mmi_primitive_value_h handle, bool *value) { + if (nullptr == handle || nullptr == value) { + _E("[ERROR] Some parameters are invalid"); + return MMI_ERROR_INVALID_PARAMETER; + } + + if (handle->type != MMI_PRIMITIVE_VALUE_TYPE_BOOL) { + _E("[ERROR] Data type is not matched"); + return MMI_ERROR_INVALID_PARAMETER; + } + + int data = *(reinterpret_cast(handle->data)); + *value = (data != 0); + + _D("[DEBUG] Success to get value: boolean(%s)", *value ? "true" : "false"); + return MMI_ERROR_NONE; +} + +int mmi_primitive_value_get_array_count(mmi_primitive_value_h array, size_t *count) { + if (nullptr == array || nullptr == count) { + _E("[ERROR] Some parameters are invalid"); + return MMI_ERROR_INVALID_PARAMETER; + } + + if (array->type != MMI_PRIMITIVE_VALUE_TYPE_ARRAY) { + _E("[ERROR] Data type is not matched"); + return MMI_ERROR_INVALID_PARAMETER; + } + + *count = get_array_count(array); + + _D("[DEBUG] Success to get array count(%zu)", *count); + return MMI_ERROR_NONE; +} + +int mmi_primitive_value_get_array_element(mmi_primitive_value_h array, size_t index, mmi_primitive_value_h *element) { + if (nullptr == array || nullptr == element) { + _E("[ERROR] Some parameters are invalid"); + return MMI_ERROR_INVALID_PARAMETER; + } + + if (array->type != MMI_PRIMITIVE_VALUE_TYPE_ARRAY) { + _E("[ERROR] Data type is not matched"); + return MMI_ERROR_INVALID_PARAMETER; + } + + size_t count = get_array_count(array); + if (index >= count) { + _E("[ERROR] index(%zu) is out of range. count(%zu)", index, count); + return MMI_ERROR_INVALID_PARAMETER; + } + + *element = *(get_element_pointer_in_array(array, index)); + + _D("[DEBUG] Success to get array element on index(%zu)", index); + return MMI_ERROR_NONE; +} + +int mmi_primitive_value_clone(mmi_primitive_value_h handle, mmi_primitive_value_h *cloned) { + if (nullptr == handle || nullptr == cloned) { + _E("[ERROR] Some parameters are invalid"); + return MMI_ERROR_INVALID_PARAMETER; + } + + auto cloned_value = reinterpret_cast(malloc(sizeof(mmi_primitive_value_s))); + cloned_value->type = handle->type; + cloned_value->datalen = handle->datalen; + cloned_value->data = calloc(1, handle->datalen); + if (nullptr == cloned_value->data) { + _E("[ERROR] Fail to allocate memory for data"); + free(cloned_value); + return MMI_ERROR_OUT_OF_MEMORY; + } + + if (handle->type == MMI_PRIMITIVE_VALUE_TYPE_ARRAY) { + const size_t count = get_array_count(handle); + for (size_t i = 0; i < count; i++) { + auto element = get_element_pointer_in_array(handle, i); + if (nullptr == element) { + _E("[ERROR] Fail to get element"); + mmi_primitive_value_destroy(cloned_value); + return MMI_ERROR_OPERATION_FAILED; + } + + mmi_primitive_value_clone(*element, get_element_pointer_in_array(cloned_value, i)); + } + } else { + memcpy(cloned_value->data, handle->data, handle->datalen); + } + + *cloned = cloned_value; + + _D("[DEBUG] Success to clone data. type(%d)", cloned_value->type); + return MMI_ERROR_NONE; +} + +int mmi_primitive_value_destroy(mmi_primitive_value_h handle) { + if (nullptr == handle) { + _E("[ERROR] Some parameters are invalid"); + return MMI_ERROR_INVALID_PARAMETER; + } + + if (handle->type == MMI_PRIMITIVE_VALUE_TYPE_ARRAY) { + size_t count = get_array_count(handle); + for (size_t i = 0; i < count; i++) { + auto element = get_element_pointer_in_array(handle, i); + mmi_primitive_value_destroy(*element); + } + } + + if (handle->data != NULL) { + free(handle->data); + } + + free(handle); + + _D("[DEBUG] Success to destroy"); + return MMI_ERROR_NONE; +} + +int mmi_primitive_value_to_bytes(mmi_primitive_value_h handle, unsigned char **bytes, size_t *size) +{ + if (nullptr == handle || nullptr == bytes || nullptr == size) { + _E("[ERROR] Some parameters are invalid"); + return MMI_ERROR_INVALID_PARAMETER; + } + + mmi_primitive_value_type_e type; + mmi_primitive_value_get_type(handle, &type); + if (type == MMI_PRIMITIVE_VALUE_TYPE_ARRAY) { + size_t current_size = sizeof(mmi_primitive_value_type_e) + sizeof(size_t); + unsigned char *buffer = reinterpret_cast(malloc(current_size)); + size_t count = get_array_count(handle); + unsigned char *current = buffer; + memcpy(current, &type, sizeof(mmi_primitive_value_type_e)); + current += sizeof(mmi_primitive_value_type_e); + /* This size will be updated after getting element bytes */ + memcpy(current, &count, sizeof(size_t)); + current += sizeof(size_t); + for (size_t i = 0; i < count; i++) { + auto element = get_element_pointer_in_array(handle, i); + unsigned char *element_bytes = nullptr; + size_t element_size = 0; + mmi_primitive_value_to_bytes(*element, &element_bytes, &element_size); + buffer = reinterpret_cast(realloc(buffer, current_size + element_size + sizeof(size_t))); + if (nullptr == buffer) { + _E("[ERROR] Fail to allocate memory for buffer"); + return MMI_ERROR_OUT_OF_MEMORY; + } + current = buffer + current_size; + memcpy(current, &element_size, sizeof(size_t)); + current += sizeof(size_t); + current_size += sizeof(size_t); + memcpy(current, element_bytes, element_size); + current_size += element_size; + free(element_bytes); + } + size_t total_element_size = current_size - sizeof(mmi_primitive_value_type_e) - sizeof(size_t); + memcpy(buffer + sizeof(mmi_primitive_value_type_e), &total_element_size, sizeof(size_t)); + + *bytes = buffer; + *size = current_size; + } else { + size_t total_size = sizeof(mmi_primitive_value_type_e) + sizeof(size_t) + handle->datalen; + unsigned char *buffer = reinterpret_cast(malloc(total_size)); + if (nullptr == buffer) { + _E("[ERROR] Fail to allocate memory for buffer"); + return MMI_ERROR_OUT_OF_MEMORY; + } + unsigned char *current = buffer; + memcpy(current, &(handle->type), sizeof(mmi_primitive_value_type_e)); + current += sizeof(mmi_primitive_value_type_e); + memcpy(current, &(handle->datalen), sizeof(size_t)); + current += sizeof(size_t); + memcpy(current, handle->data, handle->datalen); + + *bytes = buffer; + *size = total_size; + } + + _D("[DEBUG] Success to convert to bytes"); + + return MMI_ERROR_NONE; +} + +int mmi_primitive_value_from_bytes(unsigned char *bytes, size_t size, mmi_primitive_value_h *handle) +{ + if (nullptr == bytes || nullptr == handle) { + _E("[ERROR] Some parameters are invalid"); + return MMI_ERROR_INVALID_PARAMETER; + } + + unsigned char *current = bytes; + mmi_primitive_value_type_e type; + memcpy(&type, current, sizeof(mmi_primitive_value_type_e)); + current += sizeof(mmi_primitive_value_type_e); + if (type == MMI_PRIMITIVE_VALUE_TYPE_ARRAY) { + size_t datalen; + /* This datalen variable will not be used for array type */ + memcpy(&datalen, current, sizeof(size_t)); + current += sizeof(size_t); + void *data = nullptr; + auto value = reinterpret_cast(malloc(sizeof(mmi_primitive_value_s))); + value->type = type; + value->datalen = 0; + value->data = nullptr; + + size_t num_elements = 0; + while (current < bytes + size) { + bool failed = false; + mmi_primitive_value_h element = nullptr; + + size_t element_size; + memcpy(&element_size, current, sizeof(size_t)); + current += sizeof(size_t); + + unsigned char *element_bytes = reinterpret_cast(malloc(element_size)); + if (element_bytes) { + memcpy(element_bytes, current, element_size); + current += element_size; + mmi_primitive_value_from_bytes(element_bytes, element_size, &element); + free(element_bytes); + } else { + _E("[ERROR] Fail to allocate memory for element_bytes"); + failed = true; + } + if (nullptr == element) { + _E("[ERROR] Fail to convert element from bytes"); + failed = true; + } + + if (failed) { + /* Since we are in a while loop, value->data should also be freed */ + size_t num_elements = value->datalen / sizeof(mmi_primitive_value_h); + for (size_t i = 0; i < num_elements; i++) { + auto element = get_element_pointer_in_array(value, i); + mmi_primitive_value_destroy(*element); + } + free(value->data); + free(value); + return MMI_ERROR_OUT_OF_MEMORY; + } + + mmi_primitive_value_add_array_element(value, element); + num_elements++; + } + + *handle = value; + } else { + size_t datalen; + memcpy(&datalen, current, sizeof(size_t)); + current += sizeof(size_t); + void *data = calloc(1, datalen); + if (nullptr == data) { + _E("[ERROR] Fail to allocate memory for data"); + return MMI_ERROR_OUT_OF_MEMORY; + } + memcpy(data, current, datalen); + + auto value = reinterpret_cast(malloc(sizeof(mmi_primitive_value_s))); + value->type = type; + value->datalen = datalen; + value->data = data; + + *handle = value; + } + + _D("[DEBUG] Success to convert from bytes. type(%d), size(%zu)", type, size); + + return MMI_ERROR_NONE; +} + diff --git a/src/mmi/mmi-signal.cpp b/src/mmi/mmi-signal.cpp new file mode 100644 index 0000000..1d2c97c --- /dev/null +++ b/src/mmi/mmi-signal.cpp @@ -0,0 +1,147 @@ +/* + * Copyright (c) 2023 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 "mmi.h" +#include "mmi-signal.h" + +MMI_API int mmi_signal_parameter_create(mmi_primitive_value_h value, const char *name, mmi_signal_parameter_h *signal_parameter) { + if (signal_parameter == NULL) { + return -1; + } + + *signal_parameter = (mmi_signal_parameter_s*)calloc(1, sizeof(mmi_signal_parameter_s)); + if (*signal_parameter == NULL) return -1; + + strncpy((*signal_parameter)->name, name, MMI_NAME_MAX_LENGTH - 1); + (*signal_parameter)->name[MMI_NAME_MAX_LENGTH - 1] = '\0'; + mmi_primitive_value_clone(value, &((*signal_parameter)->value)); + + return 0; +} + +MMI_API int mmi_signal_parameter_get_name(mmi_signal_parameter_h signal_parameter, char **name) { + if (signal_parameter == NULL || name == NULL) { + return -1; + } + *name = strdup(signal_parameter->name); + return 0; +} + +MMI_API int mmi_signal_parameter_get_value(mmi_signal_parameter_h signal_parameter, mmi_primitive_value_h *value) { + if (signal_parameter == NULL || value == NULL) { + return -1; + } + mmi_primitive_value_clone(signal_parameter->value, value); + return 0; +} + +MMI_API int mmi_signal_parameter_clone(mmi_signal_parameter_h signal_parameter, mmi_signal_parameter_h *cloned) { + if (cloned == NULL) { + return -1; + } + + *cloned = (mmi_signal_parameter_s*)calloc(1, sizeof(mmi_signal_parameter_s)); + if (*cloned == NULL) return -1; + + strncpy((*cloned)->name, signal_parameter->name, MMI_NAME_MAX_LENGTH - 1); + (*cloned)->name[MMI_NAME_MAX_LENGTH - 1] = '\0'; + mmi_primitive_value_clone(signal_parameter->value, &((*cloned)->value)); + + return 0; +} + +MMI_API int mmi_signal_parameter_destroy(mmi_signal_parameter_h signal_parameter) { + if (signal_parameter == NULL) { + return -1; + } + mmi_primitive_value_destroy(signal_parameter->value); + free(signal_parameter); + return 0; +} + +MMI_API int mmi_signal_create(const char *name, mmi_signal_h *handle) { + if (handle == NULL) { + return -1; + } + + *handle = (mmi_signal_s*)calloc(1, sizeof(mmi_signal_s)); + if (*handle == NULL) return -1; + + strncpy((*handle)->name, name, MMI_NAME_MAX_LENGTH - 1); + (*handle)->name[MMI_NAME_MAX_LENGTH - 1] = '\0'; + + return 0; +} + +MMI_API int mmi_signal_add_parameter(mmi_signal_h handle, mmi_signal_parameter_h parameter) { + if (handle == NULL || parameter == NULL) { + return -1; + } + + if (handle->parameter_count >= MMI_PARAMETER_MAX_COUNT) { + return -1; + } + + mmi_signal_parameter_clone(parameter, &handle->parameters[handle->parameter_count]); + handle->parameter_count++; + + return 0; +} + +MMI_API int mmi_signal_get_name(mmi_signal_h handle, char **name) { + if (handle == NULL || name == NULL) { + return -1; + } + *name = handle->name; + return 0; +} + +MMI_API int mmi_signal_get_parameter_count(mmi_signal_h handle, int *count) { + if (handle == NULL || count == NULL) { + return -1; + } + *count = handle->parameter_count; + return 0; +} + +MMI_API int mmi_signal_get_parameter(mmi_signal_h handle, int index, mmi_signal_parameter_h *parameter) { + if (handle == NULL || parameter == NULL) { + return -1; + } + + if (index < 0 || index >= handle->parameter_count) { + return -1; + } + + mmi_signal_parameter_clone(handle->parameters[index], parameter); + + return 0; +} + +MMI_API int mmi_signal_destroy(mmi_signal_h handle) { + if (handle == NULL) { + return -1; + } + + for (int i = 0; i < handle->parameter_count; i++) { + mmi_signal_parameter_destroy(handle->parameters[i]); + } + + free(handle); + return 0; +} diff --git a/src/mmi/mmi-workflow-instance-manager.h b/src/mmi/mmi-workflow-instance-manager.h new file mode 100644 index 0000000..ae6ff6e --- /dev/null +++ b/src/mmi/mmi-workflow-instance-manager.h @@ -0,0 +1,119 @@ +/* + * Copyright (c) 2023 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 __MMI_WORKFLOW_INSTANCE_MANAGER_H__ +#define __MMI_WORKFLOW_INSTANCE_MANAGER_H__ + +#include "mmi.h" + +#include +#include + +namespace mmi { + +class WorkflowInstance { +public: + WorkflowInstance(int local_workflow_instance_id, mmi_standard_workflow_type_e workflow_type) + : m_local_workflow_instance_id(local_workflow_instance_id), m_workflow_type(workflow_type) { + } + virtual ~WorkflowInstance() {} + + bool initialize() { + return true; + } + + bool deinitialize() { + return true; + } + + int get_local_workflow_instance_id() { + return m_local_workflow_instance_id; + } + + mmi_standard_workflow_type_e get_workflow_type() { + return m_workflow_type; + } + + bool add_output_callback(std::string key, mmi_workflow_output_cb callback, void *user_data) { + bool ret = false; + if (callback) { + callbacks[key] = std::make_pair(callback, user_data); + ret = true; + } + return ret; + } + + bool call_output_callback(std::string key, mmi_data_h data) { + bool ret = false; + if (callbacks.find(key) != callbacks.end()) { + auto callback = callbacks[key]; + callback.first(static_cast(this), key.c_str(), data, callback.second); + ret = true; + } + return ret; + } + +private: + int m_local_workflow_instance_id; + mmi_standard_workflow_type_e m_workflow_type; + // TODO: make duplicate possible + std::map> callbacks; +}; + +class WorkflowInstanceManager { +public: + WorkflowInstanceManager() {} + virtual ~WorkflowInstanceManager() {} + + bool initialize() { + return true; + } + bool deinitialize() { + for (auto instance : m_instances) { + instance->deinitialize(); + delete instance; + } + m_instances.clear(); + return true; + } + + WorkflowInstance* create(mmi_standard_workflow_type_e type) { + WorkflowInstance *newInstance = new(std::nothrow) WorkflowInstance(m_last_local_workflow_instance_id++, type); + m_instances.push_back(newInstance); + return newInstance; + } + + bool destroy(WorkflowInstance *instance) { + bool ret = false; + auto it = std::find(m_instances.begin(), m_instances.end(), instance); + if (it != m_instances.end()) { + m_instances.erase(it); + instance->deinitialize(); + delete instance; + ret = true; + } + return ret; + } + +private: + int m_last_local_workflow_instance_id{0}; + std::vector m_instances; +}; + +} // namespace mmi +// +#endif //__MMI_WORKFLOW_INSTANCE_MANAGER_H__ diff --git a/src/mmi/mmi-workflow-instance.cpp b/src/mmi/mmi-workflow-instance.cpp new file mode 100644 index 0000000..74d5844 --- /dev/null +++ b/src/mmi/mmi-workflow-instance.cpp @@ -0,0 +1,186 @@ +/* + * Copyright (c) 2023 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 "mmi.h" +#include "mmi-plugin-storage.h" + +#include "mmi-client-manager.h" +#include "mmi-log.h" +#include "mmi-workflow-instance-manager.h" +#include "mmi_proxy.h" + +using namespace mmi; +using namespace mmi::communication; + +extern ClientManager g_mmi_client_manager; + +MMI_API int mmi_standard_workflow_instance_create(mmi_standard_workflow_type_e type, mmi_workflow_instance_h *instance) { + if (nullptr == instance) { + LOGE("[ERROR] parameter is null"); + return MMI_ERROR_INVALID_PARAMETER; + } + + WorkflowInstanceManager *workflow_instance_manager = g_mmi_client_manager.get_workflow_instance_manager(); + if (nullptr == workflow_instance_manager) { + LOGE("[ERROR] Failed to get workflow instance manager"); + return MMI_ERROR_OPERATION_FAILED; + } + + WorkflowInstance *workflow_instance = workflow_instance_manager->create(type); + if (workflow_instance) { + *instance = static_cast(workflow_instance); + + CommunicationChannel *channel = g_mmi_client_manager.get_communication_channel(); + if (channel) { + ClientMessageWorkflowInstanceCreate msg; + msg.local_workflow_instance_id = + workflow_instance->get_local_workflow_instance_id(); + msg.workflow_type = type; + msg.user_data = workflow_instance; + + channel->send(&msg); + } + } else { + return MMI_ERROR_OUT_OF_MEMORY; + } + + return MMI_ERROR_NONE; +} + +MMI_API int mmi_workflow_instance_destroy(mmi_workflow_instance_h instance) { + if (nullptr == instance) { + LOGE("[ERROR] parameter is null"); + return MMI_ERROR_INVALID_PARAMETER; + } + + WorkflowInstanceManager *workflow_instance_manager = g_mmi_client_manager.get_workflow_instance_manager(); + if (nullptr == workflow_instance_manager) { + LOGE("[ERROR] Failed to get workflow instance manager"); + return MMI_ERROR_OPERATION_FAILED; + } + + CommunicationChannel *channel = g_mmi_client_manager.get_communication_channel(); + if (channel) { + ClientMessageWorkflowInstanceDestroy msg; + WorkflowInstance *workflow_instance = static_cast(instance); + msg.local_workflow_instance_id = workflow_instance->get_local_workflow_instance_id(); + + channel->send(&msg); + + workflow_instance_manager->destroy(workflow_instance); + } + + return MMI_ERROR_NONE; +} + +MMI_API int mmi_workflow_instance_activate(mmi_workflow_instance_h instance) { + if (nullptr == instance) { + LOGE("[ERROR] parameter is null"); + return MMI_ERROR_INVALID_PARAMETER; + } + + CommunicationChannel *channel = g_mmi_client_manager.get_communication_channel(); + if (nullptr == channel) { + LOGE("[ERROR] Failed to get channel"); + return MMI_ERROR_OPERATION_FAILED; + } + + WorkflowInstance *workflow_instance = static_cast(instance); + + ClientMessageWorkflowInstanceActivate msg; + msg.local_workflow_instance_id = workflow_instance->get_local_workflow_instance_id(); + + channel->send(&msg); + + return MMI_ERROR_NONE; +} + +MMI_API int mmi_workflow_instance_deactivate(mmi_workflow_instance_h instance) { + if (nullptr == instance) { + LOGE("[ERROR] parameter is null"); + return MMI_ERROR_INVALID_PARAMETER; + } + + CommunicationChannel *channel = g_mmi_client_manager.get_communication_channel(); + if (nullptr == channel) { + LOGE("[ERROR] Failed to get channel"); + return MMI_ERROR_OPERATION_FAILED; + } + + WorkflowInstance *workflow_instance = static_cast(instance); + + ClientMessageWorkflowInstanceDeactivate msg; + msg.local_workflow_instance_id = workflow_instance->get_local_workflow_instance_id(); + + channel->send(&msg); + + return MMI_ERROR_NONE; +} + +MMI_API int mmi_workflow_instance_set_attribute(mmi_workflow_instance_h instance, mmi_attribute_h attribute) { + if (nullptr == instance) { + LOGE("[ERROR] parameter is null"); + return MMI_ERROR_INVALID_PARAMETER; + } + + CommunicationChannel *channel = g_mmi_client_manager.get_communication_channel(); + if (nullptr == channel) { + LOGE("[ERROR] Failed to get channel"); + return MMI_ERROR_OPERATION_FAILED; + } + + WorkflowInstance *workflow_instance = static_cast(instance); + + ClientMessageWorkflowInstanceSetAttribute msg; + msg.local_workflow_instance_id = workflow_instance->get_local_workflow_instance_id(); + msg.attribute = attribute; + + channel->send(&msg); + + return MMI_ERROR_NONE; +} + + +MMI_API int mmi_workflow_instance_set_output_callback(mmi_workflow_instance_h instance, const char *name, mmi_workflow_output_cb callback, void *user_data) { + if (nullptr == instance) { + LOGE("[ERROR] parameter is null"); + return MMI_ERROR_INVALID_PARAMETER; + } + + if (nullptr == name) { + LOGE("[ERROR] parameter is null"); + return MMI_ERROR_INVALID_PARAMETER; + } + + if (nullptr == callback) { + LOGE("[ERROR] parameter is null"); + return MMI_ERROR_INVALID_PARAMETER; + } + + WorkflowInstance *workflow_instance = static_cast(instance); + workflow_instance->add_output_callback(std::string{name}, callback, user_data); + + return MMI_ERROR_NONE; +} diff --git a/src/mmi/mmi-workflow-script-parser.h b/src/mmi/mmi-workflow-script-parser.h new file mode 100644 index 0000000..d2e035d --- /dev/null +++ b/src/mmi/mmi-workflow-script-parser.h @@ -0,0 +1,482 @@ +/* + * Copyright (c) 2023 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 __MMI_WORKFLOW_SCRIPT_PARSER_H__ +#define __MMI_WORKFLOW_SCRIPT_PARSER_H__ + +#include +#include +#include + +#include "mmi-log.h" +#include "mmi-node-source.h" +#include "mmi-node-processor.h" +#include "mmi-node-logic.h" +#include "mmi-node-controller.h" + +enum class WorkflowScriptSection { + NONE, + WORKFLOW_INFO, + NODE_LIST, + LINK_LIST, + ATTRIBUTE_LIST, + OUTPUT_LIST, +}; + +typedef struct { + WorkflowScriptSection section; + std::regex pattern; +} WorkflowScriptSectionPattern; + +typedef struct { + WorkflowScriptSection section; + std::string name; +} WorkflowScriptSectionName; + +typedef struct { + mmi_standard_workflow_type_e type; + std::string name; +} StandardWorkflowName; + +typedef struct { + mmi_standard_node_source_type_e type; + std::string name; +} StandardNodeSourceName; + +typedef struct { + mmi_standard_node_processor_type_e type; + std::string name; +} StandardNodeProcessorName; + +typedef struct { + mmi_standard_node_logic_type_e type; + std::string name; +} StandardNodeLogicName; + +typedef struct { + mmi_standard_node_controller_type_e type; + std::string name; +} StandardNodeControllerName; + +// trim from start (in place) +static inline void ltrim(std::string &s) { + s.erase(s.begin(), std::find_if(s.begin(), s.end(), [](unsigned char ch) { + return !std::isspace(ch); + })); +} + +// trim from end (in place) +static inline void rtrim(std::string &s) { + s.erase(std::find_if(s.rbegin(), s.rend(), [](unsigned char ch) { + return !std::isspace(ch); + }).base(), s.end()); +} + +// trim from both ends (in place) +static inline void trim(std::string &s) { + rtrim(s); + ltrim(s); +} + +class WorkflowScriptParser { +public: + WorkflowScriptParser() { + m_section_names.push_back({WorkflowScriptSection::WORKFLOW_INFO, "@workflow"}); + m_section_names.push_back({WorkflowScriptSection::NODE_LIST, "@node-list"}); + m_section_names.push_back({WorkflowScriptSection::LINK_LIST, "@link-list"}); + m_section_names.push_back({WorkflowScriptSection::ATTRIBUTE_LIST, "@attribute-list"}); + m_section_names.push_back({WorkflowScriptSection::OUTPUT_LIST, "@output-list"}); + + m_section_patterns.push_back({WorkflowScriptSection::WORKFLOW_INFO, std::regex("^name : ([a-zA-Z_]+)$")}); + m_section_patterns.push_back({WorkflowScriptSection::NODE_LIST, std::regex("^\\[([a-zA-Z]+)\\] ([a-zA-Z_]+) as ([a-zA-Z0-9_]+)$")}); + m_section_patterns.push_back({WorkflowScriptSection::LINK_LIST, std::regex("^([a-zA-Z0-9_.]+) -> ([a-zA-Z0-9_.]+)$")}); + m_section_patterns.push_back({WorkflowScriptSection::ATTRIBUTE_LIST, std::regex("^([a-zA-Z0-9_.]+) as ([a-zA-Z0-9_]+)$")}); + m_section_patterns.push_back({WorkflowScriptSection::OUTPUT_LIST, std::regex("^([a-zA-Z0-9_.]+) as ([a-zA-Z0-9_]+)$")}); + + m_standard_workflow_names.push_back({MMI_STANDARD_WORKFLOW_WAKEUPLESS_COMMAND, "WAKEUPLESS_COMMAND"}); + m_standard_workflow_names.push_back({MMI_STANDARD_WORKFLOW_VOICE_TOUCH, "VOICE_TOUCH"}); + m_standard_workflow_names.push_back({MMI_STANDARD_WORKFLOW_USER_RECOGNITION, "USER_RECOGNITION"}); + + m_standard_node_source_names.push_back({MMI_STANDARD_NODE_SOURCE_TYPE_MIC_AMBIENT, "MIC_AMBIENT"}); + m_standard_node_source_names.push_back({MMI_STANDARD_NODE_SOURCE_TYPE_CAMERA, "CAMERA"}); + + m_standard_node_processor_names.push_back({MMI_STANDARD_NODE_PROCESSOR_TYPE_ASR, "ASR"}); + m_standard_node_processor_names.push_back({MMI_STANDARD_NODE_PROCESSOR_TYPE_FACE_RECOGNITION, "FACE_RECOGNITION"}); + m_standard_node_processor_names.push_back({MMI_STANDARD_NODE_PROCESSOR_TYPE_SPEAKER_RECOGNITION, "SPEAKER_RECOGNITION"}); + + m_standard_node_logic_names.push_back({MMI_STANDARD_NODE_LOGIC_TYPE_FIXED_STRING_MATCH, "FIXED_STRING_MATCH"}); + m_standard_node_logic_names.push_back({MMI_STANDARD_NODE_LOGIC_TYPE_USER_COMPARISON, "USER_COMPARISON"}); + + m_standard_node_controller_names.push_back({MMI_STANDARD_NODE_CONTROLLER_TYPE_SWITCH, "SWITCH"}); + } + virtual ~WorkflowScriptParser() { + } + + bool process_section_declaration(const std::string &line) { + for (auto §ion_name : m_section_names) { + if (line == section_name.name) { + m_current_section = section_name.section; + LOGD("section changed to %d, [%s]", static_cast(m_current_section), line.c_str()); + return true; + } + } + return false; + } + + bool process_workflow_info(std::smatch matches, mmi_workflow_h workflow) { + if (matches.size() == 2) { + std::ssub_match sub_match = matches[1]; + std::string name = sub_match.str(); + for (auto &standard_workflow_name : m_standard_workflow_names) { + if (name == standard_workflow_name.name) { + LOGD("standard workflow name matched: %s", name.c_str()); + mmi_standard_workflow_type_e type = standard_workflow_name.type; + if (mmi_workflow_set_type(workflow, type) != MMI_ERROR_NONE) { + LOGE("[ERROR] Failed to set standard workflow type"); + return false; + } + break; + } + } + } + return true; + } + + bool process_node_section(std::smatch matches, mmi_workflow_h workflow) { + if (matches.size() == 4) { + std::ssub_match sub_match_type = matches[1]; + std::string type = sub_match_type.str(); + + if (type.compare("Source") == 0) { + std::ssub_match sub_match_name = matches[2]; + std::string name = sub_match_name.str(); + + std::ssub_match sub_match_alias = matches[3]; + std::string alias = sub_match_alias.str(); + + for (auto &standard_node_source_name : m_standard_node_source_names) { + if (name == standard_node_source_name.name) { + LOGD("standard node source name matched: %s", name.c_str()); + mmi_standard_node_source_type_e sub_type = standard_node_source_name.type; + mmi_node_h node = nullptr; + if (mmi_standard_node_create_source(sub_type, &node) != MMI_ERROR_NONE) { + LOGE("[ERROR] Failed to create standard node source"); + return false; + } + LOGD("standard node source created: %p", node); + mmi_workflow_node_add(workflow, alias.c_str(), node); + break; + } + } + } else if (type.compare("Processor") == 0) { + std::ssub_match sub_match_name = matches[2]; + std::string name = sub_match_name.str(); + + std::ssub_match sub_match_alias = matches[3]; + std::string alias = sub_match_alias.str(); + + for (auto &standard_node_processor_name : m_standard_node_processor_names) { + if (name == standard_node_processor_name.name) { + LOGD("standard node processor name matched: %s", name.c_str()); + mmi_standard_node_processor_type_e sub_type = standard_node_processor_name.type; + mmi_node_h node = nullptr; + if (mmi_standard_node_create_processor(sub_type, &node) != MMI_ERROR_NONE) { + LOGE("[ERROR] Failed to create standard node processor"); + return false; + } + LOGD("Adding processor node %s as %s", name.c_str(), alias.c_str()); + mmi_workflow_node_add(workflow, alias.c_str(), node); + mmi_node_destroy(node); + break; + } + } + } else if (type.compare("Logic") == 0) { + std::ssub_match sub_match_name = matches[2]; + std::string name = sub_match_name.str(); + + std::ssub_match sub_match_alias = matches[3]; + std::string alias = sub_match_alias.str(); + + for (auto &standard_node_logic_name : m_standard_node_logic_names) { + if (name == standard_node_logic_name.name) { + LOGD("standard node logic name matched: %s", name.c_str()); + mmi_standard_node_logic_type_e sub_type = standard_node_logic_name.type; + mmi_node_h node = nullptr; + if (mmi_standard_node_create_logic(sub_type, &node) != MMI_ERROR_NONE) { + LOGE("[ERROR] Failed to create standard node logic"); + return false; + } + LOGD("Adding logic node %s as %s", name.c_str(), alias.c_str()); + mmi_workflow_node_add(workflow, alias.c_str(), node); + mmi_node_destroy(node); + break; + } + } + } else if (type.compare("Controller") == 0) { + std::ssub_match sub_match_name = matches[2]; + std::string name = sub_match_name.str(); + + std::ssub_match sub_match_alias = matches[3]; + std::string alias = sub_match_alias.str(); + + for (auto &standard_node_controller_name : m_standard_node_controller_names) { + if (name == standard_node_controller_name.name) { + LOGD("standard node controller name matched: %s", name.c_str()); + mmi_standard_node_controller_type_e sub_type = standard_node_controller_name.type; + mmi_node_h node = nullptr; + if (mmi_standard_node_create_controller(sub_type, &node) != MMI_ERROR_NONE) { + LOGE("[ERROR] Failed to create standard node controller"); + return false; + } + LOGD("Adding controller node %s as %s", name.c_str(), alias.c_str()); + mmi_workflow_node_add(workflow, alias.c_str(), node); + mmi_node_destroy(node); + break; + } + } + } + } + return true; + } + + bool process_link_section(std::smatch matches, mmi_workflow_h workflow) { + if (matches.size() == 3) { + std::ssub_match sub_match_from = matches[1]; + std::string from = sub_match_from.str(); + + std::ssub_match sub_match_to = matches[2]; + std::string to = sub_match_to.str(); + + std::string from_node_name; + std::string from_port_name; + + std::string to_node_name; + std::string to_port_name; + + size_t from_pos = from.find("."); + if (from_pos != std::string::npos) { + from_node_name = from.substr(0, from_pos); + from_port_name = from.substr(from_pos + 1); + } else { + LOGE("[ERROR] Invalid 'from' format: %s", from.c_str()); + return false; + } + + size_t to_pos = to.find("."); + if (to_pos != std::string::npos) { + to_node_name = to.substr(0, to_pos); + to_port_name = to.substr(to_pos + 1); + } else { + LOGE("[ERROR] Invalid 'to' format: %s", to.c_str()); + return false; + } + + LOGD("from_node_name: %s, from_port_name: %s, to_node_name: %s, to_port_name: %s", + from_node_name.c_str(), from_port_name.c_str(), to_node_name.c_str(), to_port_name.c_str()); + + if (mmi_workflow_link_nodes_by_names(workflow, + from_node_name.c_str(), from_port_name.c_str(), + to_node_name.c_str(), to_port_name.c_str()) != MMI_ERROR_NONE) { + LOGE("[ERROR] Failed to link nodes by names"); + return false; + } + } + return true; + } + + bool process_attribute_section(std::smatch matches, mmi_workflow_h workflow) { + if (matches.size() == 3) { + std::ssub_match sub_match_name = matches[1]; + std::string identifier = sub_match_name.str(); + + std::ssub_match sub_match_value = matches[2]; + std::string alias = sub_match_value.str(); + + LOGD("identifier: %s, alias: %s", identifier.c_str(), alias.c_str()); + + size_t pos = identifier.find("."); + if (pos != std::string::npos) { + std::string node_name = identifier.substr(0, pos); + std::string attribute_name = identifier.substr(pos + 1); + + LOGD("node_name: %s, attribute_name: %s", node_name.c_str(), attribute_name.c_str()); + + mmi_workflow_attribute_assign(workflow, alias.c_str(), node_name.c_str(), attribute_name.c_str()); + } + } + return true; + } + + bool process_output_section(std::smatch matches, mmi_workflow_h workflow) { + if (matches.size() == 3) { + std::ssub_match sub_match_name = matches[1]; + std::string identifier = sub_match_name.str(); + + std::ssub_match sub_match_value = matches[2]; + std::string alias = sub_match_value.str(); + + LOGD("identifier: %s, alias: %s", identifier.c_str(), alias.c_str()); + + size_t pos = identifier.find("."); + if (pos != std::string::npos) { + std::string node_name = identifier.substr(0, pos); + std::string port_name = identifier.substr(pos + 1); + + LOGD("node_name: %s, port_name: %s", node_name.c_str(), port_name.c_str()); + + mmi_workflow_output_assign(workflow, alias.c_str(), node_name.c_str(), port_name.c_str()); + } + } + return true; + } + + bool process_section_line(const std::string &line, mmi_workflow_h workflow) { + for (auto §ion_pattern : m_section_patterns) { + if (m_current_section == section_pattern.section) { + if (std::regex_match(line, section_pattern.pattern)) { + LOGD("section line matched: %s", line.c_str()); + std::smatch matches; + if (std::regex_search(line, matches, section_pattern.pattern)) { + switch (m_current_section) { + case WorkflowScriptSection::WORKFLOW_INFO: + if (process_workflow_info(matches, workflow) == false) { + LOGE("[ERROR] Failed to process workflow info"); + return false; + } + break; + case WorkflowScriptSection::NODE_LIST: + if (process_node_section(matches, workflow) == false) { + LOGE("[ERROR] Failed to process node section"); + return false; + } + break; + case WorkflowScriptSection::LINK_LIST: + if (process_link_section(matches, workflow) == false) { + LOGE("[ERROR] Failed to process link section"); + return false; + } + break; + case WorkflowScriptSection::ATTRIBUTE_LIST: + if (process_attribute_section(matches, workflow) == false) { + LOGE("[ERROR] Failed to process attribute section"); + return false; + } + break; + case WorkflowScriptSection::OUTPUT_LIST: + if (process_output_section(matches, workflow) == false) { + LOGE("[ERROR] Failed to process output section"); + return false; + } + break; + default: + break; + } + } + return true; + } + } + } + return false; + } + + bool parse(std::vector &lines, mmi_workflow_h *workflow) { + if (nullptr == workflow) { + LOGE("[ERROR] parameter is null"); + return false; + } + create_workflow(workflow); + if (nullptr == *workflow) { + LOGE("[ERROR] Failed to create workflow"); + return false; + } + + for (auto &line : lines) { + trim(line); + + if (line.empty()) { + continue; + } + + if (std::regex_match(line, m_section_declaration_regex)) { + if (process_section_declaration(line) == false) { + LOGE("invalid section name: %s", line.c_str()); + return false; + } + } else { + if (process_section_line(line, *workflow) == false) { + LOGE("invalid section line: %s", line.c_str()); + return false; + } + } + } + return true; + } +private: + void create_workflow(mmi_workflow_h *workflow) { + if (nullptr == workflow) { + LOGE("[ERROR] parameter is null"); + return; + } + + mmi_workflow_s *workflow_s = new(std::nothrow) mmi_workflow_s; + if (nullptr == workflow_s) { + LOGE("[ERROR] Failed to create workflow"); + return; + } + + workflow_s->type = MMI_STANDARD_WORKFLOW_NONE; + workflow_s->node_infos = nullptr; + workflow_s->node_info_count = 0; + workflow_s->link_infos = nullptr; + workflow_s->link_info_count = 0; + workflow_s->attribute_assignment_infos = nullptr; + workflow_s->attribute_assignment_info_count = 0; + workflow_s->attribute_default_value_infos = nullptr; + workflow_s->attribute_default_value_info_count = 0; + workflow_s->output_assignment_infos = nullptr; + workflow_s->output_assignment_info_count = 0; + + *workflow = static_cast(workflow_s); + } + void destroy_workflow(mmi_workflow_h workflow) { + if (nullptr == workflow) { + LOGE("[ERROR] parameter is null"); + return; + } + + mmi_workflow_s *workflow_s = static_cast(workflow); + delete workflow_s; + } + + WorkflowScriptSection m_current_section{WorkflowScriptSection::NONE}; + + std::regex m_section_declaration_regex{"^@[a-zA-Z0-9_-]+$"}; + + std::vector m_section_names; + std::vector m_section_patterns; + + std::vector m_standard_workflow_names; + + std::vector m_standard_node_source_names; + std::vector m_standard_node_processor_names; + std::vector m_standard_node_logic_names; + std::vector m_standard_node_controller_names; + +}; + +#endif /* __MMI_WORKFLOW_SCRIPT_PARSER_H__ */ diff --git a/src/mmi/mmi-workflow-script.cpp b/src/mmi/mmi-workflow-script.cpp new file mode 100644 index 0000000..9be277a --- /dev/null +++ b/src/mmi/mmi-workflow-script.cpp @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2023 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 "mmi.h" +#include "mmi-log.h" +#include "mmi-workflow.h" + +#include "mmi-workflow-script-parser.h" + +#include + +MMI_API int mmi_workflow_create_from_script(const char *script_path, mmi_workflow_h *workflow) { + if (script_path == nullptr || workflow == nullptr) { + LOGE("[ERROR] parameter is null"); + return MMI_ERROR_INVALID_PARAMETER; + } + + std::ifstream script_file(script_path); + if (!script_file.is_open()) { + LOGE("[ERROR] Failed to open script file"); + return MMI_ERROR_INVALID_PARAMETER; + } + + std::string line; + std::vector lines; + while (std::getline(script_file, line)) { + lines.push_back(line); + } + + WorkflowScriptParser parser; + *workflow = nullptr; + bool result = parser.parse(lines, workflow); + if (!result || *workflow == nullptr) { + LOGE("[ERROR] Failed to create workflow from data"); + return MMI_ERROR_INVALID_PARAMETER; + } + + return MMI_ERROR_NONE; +} + diff --git a/src/mmi/mmi-workflow.cpp b/src/mmi/mmi-workflow.cpp new file mode 100644 index 0000000..dcc7260 --- /dev/null +++ b/src/mmi/mmi-workflow.cpp @@ -0,0 +1,351 @@ +/* + * Copyright (c) 2023 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 "mmi.h" +#include "mmi-plugin-storage.h" + +#include "mmi-log.h" + +MMI_API int mmi_workflow_create(mmi_workflow_h *workflow) { + if (nullptr == workflow) { + LOGE("[ERROR] parameter is null"); + return MMI_ERROR_INVALID_PARAMETER; + } + + mmi_workflow_s *workflow_s = new(std::nothrow) mmi_workflow_s; + if (nullptr == workflow_s) { + LOGE("[ERROR] Failed to create workflow"); + return MMI_ERROR_OUT_OF_MEMORY; + } + + workflow_s->type = MMI_STANDARD_WORKFLOW_NONE; + workflow_s->node_infos = nullptr; + workflow_s->node_info_count = 0; + workflow_s->link_infos = nullptr; + workflow_s->link_info_count = 0; + workflow_s->attribute_assignment_infos = nullptr; + workflow_s->attribute_assignment_info_count = 0; + workflow_s->attribute_default_value_infos = nullptr; + workflow_s->attribute_default_value_info_count = 0; + workflow_s->output_assignment_infos = nullptr; + workflow_s->output_assignment_info_count = 0; + + *workflow = static_cast(workflow_s); + + return MMI_ERROR_NONE; +} + +MMI_API int mmi_workflow_set_type(mmi_workflow_h workflow, mmi_standard_workflow_type_e type) { + if (nullptr == workflow) { + LOGE("[ERROR] parameter is null"); + return MMI_ERROR_INVALID_PARAMETER; + } + + mmi_workflow_s *workflow_s = static_cast(workflow); + workflow_s->type = type; + + return MMI_ERROR_NONE; +} + +MMI_API int mmi_workflow_get_type(mmi_workflow_h workflow, mmi_standard_workflow_type_e *type) { + if (nullptr == workflow || nullptr == type) { + LOGE("[ERROR] parameter is null"); + return MMI_ERROR_INVALID_PARAMETER; + } + + mmi_workflow_s *workflow_s = static_cast(workflow); + *type = workflow_s->type; + + return MMI_ERROR_NONE; +} + +MMI_API int mmi_workflow_node_add(mmi_workflow_h workflow, const char *node_name, mmi_node_h node) { + if (nullptr == workflow || nullptr == node_name || nullptr == node) { + LOGE("[ERROR] parameter is null"); + return MMI_ERROR_INVALID_PARAMETER; + } + + mmi_workflow_s *workflow_s = static_cast(workflow); + mmi_workflow_node_info_s *node_infos = workflow_s->node_infos; + size_t node_info_count = workflow_s->node_info_count; + workflow_s->node_infos = new(std::nothrow) mmi_workflow_node_info_s[node_info_count + 1]; + if (node_info_count > 0) { + memcpy(workflow_s->node_infos, node_infos, sizeof(mmi_workflow_node_info_s) * node_info_count); + delete [] node_infos; + } + + snprintf(workflow_s->node_infos[node_info_count].name, MMI_NAME_MAX_LENGTH, "%s", node_name); + mmi_node_clone(node, &(workflow_s->node_infos[node_info_count].node)); + workflow_s->node_info_count++; + + return MMI_ERROR_NONE; +} + +MMI_API int mmi_workflow_link_nodes_by_names(mmi_workflow_h workflow, + const char *from_node_name, const char *from_port_name, const char *to_node_name, const char *to_port_name) { + if (nullptr == from_node_name || nullptr == from_port_name || nullptr == to_node_name || nullptr == to_port_name) { + LOGE("[ERROR] parameter is null"); + return MMI_ERROR_INVALID_PARAMETER; + } + + mmi_workflow_s *workflow_s = static_cast(workflow); + mmi_workflow_link_info_s *link_infos = workflow_s->link_infos; + size_t link_info_count = workflow_s->link_info_count; + workflow_s->link_infos = new(std::nothrow) mmi_workflow_link_info_s[link_info_count + 1]; + if (link_info_count > 0) { + memcpy(workflow_s->link_infos, link_infos, sizeof(mmi_workflow_link_info_s) * link_info_count); + delete [] link_infos; + } + + snprintf(workflow_s->link_infos[link_info_count].from_node_name, MMI_NAME_MAX_LENGTH, "%s", from_node_name); + snprintf(workflow_s->link_infos[link_info_count].from_port_name, MMI_NAME_MAX_LENGTH, "%s", from_port_name); + snprintf(workflow_s->link_infos[link_info_count].to_node_name, MMI_NAME_MAX_LENGTH, "%s", to_node_name); + snprintf(workflow_s->link_infos[link_info_count].to_port_name, MMI_NAME_MAX_LENGTH, "%s", to_port_name); + + workflow_s->link_info_count = link_info_count + 1; + + return MMI_ERROR_NONE; +} + +MMI_API int mmi_workflow_attribute_assign(mmi_workflow_h workflow, const char *attribute_name, const char *target_node_name, const char *target_attribute_name) { + if (nullptr == workflow || nullptr == attribute_name || nullptr == target_node_name || nullptr == target_attribute_name) { + LOGE("[ERROR] parameter is null"); + return MMI_ERROR_INVALID_PARAMETER; + } + + mmi_workflow_s *workflow_s = static_cast(workflow); + mmi_workflow_attribute_assignment_info_s *attribute_assignment_infos = workflow_s->attribute_assignment_infos; + size_t attribute_assignment_info_count = workflow_s->attribute_assignment_info_count; + workflow_s->attribute_assignment_infos = new(std::nothrow) mmi_workflow_attribute_assignment_info_s[attribute_assignment_info_count + 1]; + if (attribute_assignment_info_count > 0) { + memcpy(workflow_s->attribute_assignment_infos, attribute_assignment_infos, sizeof(mmi_workflow_attribute_assignment_info_s) * attribute_assignment_info_count); + delete [] attribute_assignment_infos; + } + + snprintf(workflow_s->attribute_assignment_infos[attribute_assignment_info_count].attribute_name, MMI_NAME_MAX_LENGTH, "%s", attribute_name); + snprintf(workflow_s->attribute_assignment_infos[attribute_assignment_info_count].target_node_name, MMI_NAME_MAX_LENGTH, "%s", target_node_name); + snprintf(workflow_s->attribute_assignment_infos[attribute_assignment_info_count].target_attribute_name, MMI_NAME_MAX_LENGTH, "%s", target_attribute_name); + + workflow_s->attribute_assignment_info_count = attribute_assignment_info_count + 1; + + return MMI_ERROR_NONE; +} + +MMI_API int mmi_workflow_attribute_set_default_value(mmi_workflow_h workflow, mmi_attribute_h default_value) { + if (nullptr == workflow || nullptr == default_value) { + LOGE("[ERROR] parameter is null"); + return MMI_ERROR_INVALID_PARAMETER; + } + + mmi_workflow_s *workflow_s = static_cast(workflow); + mmi_workflow_attribute_default_value_info_s *attribute_default_value_infos = workflow_s->attribute_default_value_infos; + size_t attribute_default_value_info_count = workflow_s->attribute_default_value_info_count; + workflow_s->attribute_default_value_infos = new(std::nothrow) mmi_workflow_attribute_default_value_info_s[attribute_default_value_info_count + 1]; + if (attribute_default_value_info_count > 0) { + memcpy(workflow_s->attribute_default_value_infos, attribute_default_value_infos, sizeof(mmi_workflow_attribute_default_value_info_s) * attribute_default_value_info_count); + delete [] attribute_default_value_infos; + } + + mmi_attribute_to_bytes(default_value, + &(workflow_s->attribute_default_value_infos[attribute_default_value_info_count].serialized_default_value), + &(workflow_s->attribute_default_value_infos[attribute_default_value_info_count].serialized_default_value_size)); + + workflow_s->attribute_default_value_info_count = attribute_default_value_info_count + 1; + + return MMI_ERROR_NONE; +} + +MMI_API int mmi_workflow_signal_assign(mmi_workflow_h workflow, const char *workflow_signal, mmi_node_h node, const char *node_signal) { + if (nullptr == workflow || nullptr == workflow_signal || nullptr == node || nullptr == node_signal) { + LOGE("[ERROR] parameter is null"); + return MMI_ERROR_INVALID_PARAMETER; + } + return MMI_ERROR_NONE; +} + +MMI_API int mmi_workflow_output_assign(mmi_workflow_h workflow, const char *workflow_output, const char *out_node_name, const char *node_out_port_name) { + if (nullptr == workflow || nullptr == workflow_output || nullptr == out_node_name || nullptr == node_out_port_name) { + LOGE("[ERROR] parameter is null"); + return MMI_ERROR_INVALID_PARAMETER; + } + + mmi_workflow_s *workflow_s = static_cast(workflow); + mmi_workflow_output_assignment_info_s *output_assignment_infos = workflow_s->output_assignment_infos; + size_t output_assignment_info_count = workflow_s->output_assignment_info_count; + workflow_s->output_assignment_infos = new(std::nothrow) mmi_workflow_output_assignment_info_s[output_assignment_info_count + 1]; + if (output_assignment_info_count > 0) { + memcpy(workflow_s->output_assignment_infos, output_assignment_infos, sizeof(mmi_workflow_output_assignment_info_s) * output_assignment_info_count); + delete [] output_assignment_infos; + } + + snprintf(workflow_s->output_assignment_infos[output_assignment_info_count].output_name,MMI_NAME_MAX_LENGTH, "%s", workflow_output); + snprintf(workflow_s->output_assignment_infos[output_assignment_info_count].from_node_name, MMI_NAME_MAX_LENGTH, "%s", out_node_name); + snprintf(workflow_s->output_assignment_infos[output_assignment_info_count].from_port_name, MMI_NAME_MAX_LENGTH, "%s", node_out_port_name); + + workflow_s->output_assignment_info_count = output_assignment_info_count + 1; + + return MMI_ERROR_NONE; +} + +MMI_API int mmi_workflow_output_assign_by_port(mmi_workflow_h workflow, const char *workflow_output, mmi_port_h port) { + if (nullptr == workflow || nullptr == workflow_output || nullptr == port) { + LOGE("[ERROR] parameter is null"); + return MMI_ERROR_INVALID_PARAMETER; + } + return MMI_ERROR_NONE; +} + +MMI_API int mmi_standard_workflow_register(mmi_workflow_h workflow) { + if (nullptr == workflow) { + LOGE("[ERROR] parameter is null"); + return MMI_ERROR_INVALID_PARAMETER; + } + + mmi_standard_workflow_type_e workflow_type; + mmi_workflow_get_type(workflow, &workflow_type); + + const char *identifier = mmi_plugin_storage_get_current_module_identifier(); + if (nullptr == identifier) { + LOGE("[ERROR] identifier is null"); + return MMI_ERROR_INVALID_PARAMETER; + } + + mmi_plugin_module_workflow_list_s list = mmi_plugin_storage_get_workflow_list(identifier); + + mmi_workflow_h *workflows = list.workflows; + size_t workflow_count = list.workflow_count; + + bool duplicated = false; + + for (size_t i = 0; i < workflow_count; i++) { + mmi_standard_workflow_type_e type; + mmi_workflow_get_type(workflows[i], &type); + if (type == workflow_type) { + duplicated = true; + mmi_workflow_destroy(workflows[i]); + + mmi_workflow_h cloned; + mmi_workflow_clone(workflow, &cloned); + workflows[i] = cloned; + } + } + + if (!duplicated) { + list.workflows = new(std::nothrow) mmi_workflow_h[workflow_count + 1]; + if (nullptr == list.workflows) { + LOGE("[ERROR] failed to allocate memory"); + return MMI_ERROR_OUT_OF_MEMORY; + } + if (workflow_count > 0) { + memcpy(list.workflows, workflows, sizeof(mmi_workflow_h) * workflow_count); + delete [] workflows; + } + + mmi_workflow_h cloned; + mmi_workflow_clone(workflow, &cloned); + list.workflows[workflow_count] = cloned; + list.workflow_count = workflow_count + 1; + } + + mmi_plugin_storage_set_workflow_list(list); + + return MMI_ERROR_NONE; +} + +MMI_API int mmi_workflow_clone(mmi_workflow_h workflow, mmi_workflow_h *cloned) { + if (nullptr == workflow || nullptr == cloned) { + LOGE("[ERROR] parameter is null"); + return MMI_ERROR_INVALID_PARAMETER; + } + + mmi_workflow_s *cloned_s = new(std::nothrow) mmi_workflow_s; + if (nullptr == cloned_s) { + LOGE("[ERROR] new mmi_workflow_s failed"); + return MMI_ERROR_OUT_OF_MEMORY; + } + + mmi_workflow_s *workflow_s = static_cast(workflow); + memcpy(cloned_s, workflow_s, sizeof(mmi_workflow_s)); + + cloned_s->node_infos = new(std::nothrow) mmi_workflow_node_info_s[workflow_s->node_info_count]; + memcpy(cloned_s->node_infos, workflow_s->node_infos, sizeof(mmi_workflow_node_info_s) * workflow_s->node_info_count); + for (int i = 0; i < workflow_s->node_info_count; i++) { + mmi_node_clone(workflow_s->node_infos[i].node, &(cloned_s->node_infos[i].node)); + } + + if (workflow_s->link_info_count > 0) { + cloned_s->link_infos = new(std::nothrow) mmi_workflow_link_info_s[workflow_s->link_info_count]; + memcpy(cloned_s->link_infos, workflow_s->link_infos, sizeof(mmi_workflow_link_info_s) * workflow_s->link_info_count); + } + + if (workflow_s->attribute_assignment_info_count > 0) { + cloned_s->attribute_assignment_infos = new(std::nothrow) mmi_workflow_attribute_assignment_info_s[workflow_s->attribute_assignment_info_count]; + memcpy(cloned_s->attribute_assignment_infos, workflow_s->attribute_assignment_infos, + sizeof(mmi_workflow_attribute_assignment_info_s) * workflow_s->attribute_assignment_info_count); + } + + if (workflow_s->attribute_default_value_info_count > 0) { + cloned_s->attribute_default_value_infos = new(std::nothrow) mmi_workflow_attribute_default_value_info_s[workflow_s->attribute_default_value_info_count]; + for (int i = 0; i < workflow_s->attribute_default_value_info_count; i++) { + cloned_s->attribute_default_value_infos[i].serialized_default_value = + new(std::nothrow) unsigned char[workflow_s->attribute_default_value_infos[i].serialized_default_value_size]; + memcpy(cloned_s->attribute_default_value_infos[i].serialized_default_value, + workflow_s->attribute_default_value_infos[i].serialized_default_value, + workflow_s->attribute_default_value_infos[i].serialized_default_value_size); + } + } + + if (workflow_s->output_assignment_info_count > 0) { + cloned_s->output_assignment_infos = new(std::nothrow) mmi_workflow_output_assignment_info_s[workflow_s->output_assignment_info_count]; + memcpy(cloned_s->output_assignment_infos, workflow_s->output_assignment_infos, + sizeof(mmi_workflow_output_assignment_info_s) * workflow_s->output_assignment_info_count); + } + + *cloned = cloned_s; + + return MMI_ERROR_NONE; +} + +MMI_API int mmi_workflow_destroy(mmi_workflow_h workflow) { + if (nullptr == workflow) { + LOGE("[ERROR] parameter is null"); + return MMI_ERROR_INVALID_PARAMETER; + } + + mmi_workflow_s *workflow_s = static_cast(workflow); + + for (int i = 0; i < workflow_s->node_info_count; i++) { + mmi_node_destroy(workflow_s->node_infos[i].node); + } + delete [] workflow_s->node_infos; + delete [] workflow_s->link_infos; + delete [] workflow_s->attribute_assignment_infos; + delete [] workflow_s->attribute_default_value_infos; + delete [] workflow_s->output_assignment_infos; + + delete workflow_s; + + return MMI_ERROR_NONE; +} diff --git a/src/mmi/mmi.cpp b/src/mmi/mmi.cpp new file mode 100644 index 0000000..e49ec6e --- /dev/null +++ b/src/mmi/mmi.cpp @@ -0,0 +1,112 @@ +/* + * Copyright (c) 2023 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 "mmi-log.h" +#include "mmi-ipc-tidl.h" + +#include "mmi-client-manager.h" + +#include "mmi.h" + +using namespace mmi; +using namespace mmi::communication; + +static mmi_state_e g_mmi_state{MMI_STATE_NONE}; +static std::vector> g_state_changed_cb_list; + +struct CommunicationChannelFactoryTIDL : public ICommunicationChannelFactory { + CommunicationChannel* create_channel() override { + return new(std::nothrow) CommunicationChannelTIDL(); + } + void destroy_channel(CommunicationChannel* channel) override { + delete channel; + } +}; +static CommunicationChannelFactoryTIDL g_communication_channel_factory_tidl; + +class CommunicationChannelObserver : + public SimpleEventObserver { +public: + void on_observer_event( + const COMMUNICATION_CHANNEL_EVENT_TYPE &event, std::any data) override { + mmi_state_e previous_state = g_mmi_state; + + if (event == COMMUNICATION_CHANNEL_EVENT_TYPE::CONNECTED) { + g_mmi_state = MMI_STATE_READY; + } else if (event == COMMUNICATION_CHANNEL_EVENT_TYPE::DISCONNECTED) { + g_mmi_state = MMI_STATE_NONE; + } + + if (previous_state != g_mmi_state) { + for (auto& item : g_state_changed_cb_list) { + item.first(g_mmi_state, item.second); + } + } + } +}; +static CommunicationChannelObserver g_communication_channel_observer; + +ClientManager g_mmi_client_manager{ + &g_communication_channel_factory_tidl, + &g_communication_channel_observer +}; + +MMI_API int mmi_initialize() { + LOGE("[SUCCESS] Succeeded to create client"); + g_mmi_client_manager.initialize(); + + return MMI_ERROR_NONE; +} + +MMI_API int mmi_deinitialize() { + g_mmi_client_manager.deinitialize(); + + return MMI_ERROR_NONE; +} + +MMI_API int mmi_set_state_changed_cb(mmi_state_changed_cb callback, void *user_data) { + if (callback == nullptr) { + return MMI_ERROR_INVALID_PARAMETER; + } + + callback(g_mmi_state, user_data); + + g_state_changed_cb_list.push_back(std::make_pair(callback, user_data)); + + return MMI_ERROR_NONE; +} + +MMI_API int mmi_unset_state_changed_cb(mmi_state_changed_cb callback) { + auto it = std::find_if(g_state_changed_cb_list.begin(), g_state_changed_cb_list.end(), + [callback](const std::pair& item) { + return item.first == callback; + }); + + if (it != g_state_changed_cb_list.end()) { + g_state_changed_cb_list.erase(it); + } + + return MMI_ERROR_NONE; +} diff --git a/tests/meson.build b/tests/meson.build index 4cc2d49..d8c6f3a 100644 --- a/tests/meson.build +++ b/tests/meson.build @@ -1,17 +1,2 @@ -mmi_tests_srcs = [ - 'mmi-tests.cpp', - 'mmi-main-test.cpp', - 'mmi-ipc-test.cpp', - 'wait-helper.cpp' - ] - -gmock_dep = dependency('gmock', method : 'pkg-config') -ecore_dep = dependency('ecore', method : 'pkg-config') - -executable( - 'mmi-tests', - mmi_tests_srcs, - dependencies : [mmi_declared_dep, gmock_dep, ecore_dep], - install_dir : mmi_prefix_bindir, - install : true - ) +subdir('mmi') +subdir('mmi-manager') diff --git a/tests/mmi-ipc-test.cpp b/tests/mmi-ipc-test.cpp deleted file mode 100644 index 21c5514..0000000 --- a/tests/mmi-ipc-test.cpp +++ /dev/null @@ -1,266 +0,0 @@ -/* - * Copyright (c) 2022 Samsung Electronics Co., Ltd All Rights Reserved - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT 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 "mmi.h" -#include "mmi-tests.h" -#include "mmi-ipc.h" -#include "mmi-client.h" - -#include -#include - -class MMIIpcTest : public ::testing::Test -{ -public: - void SetUp(void) override - { - ecore_init(); - } - - void TearDown(void) override - { - ecore_shutdown(); - } -}; - -TEST_F(MMIIpcTest, MMIFWIpcInitSuccess) -{ - int res = mmi_initialize(); - - EXPECT_EQ(res, MMI_ERROR_NONE); - - mmi_deinitialize(); -} - -TEST_F(MMIIpcTest, MMIIpcInitFail) -{ - int res = mmi_initialize(); - - EXPECT_EQ(res, MMI_ERROR_NONE); - - mmi_deinitialize(); -} - -static void voice_touch_callback(int input_event_type, const char *result_out, void *user_data) -{ -} - -TEST_F(MMIIpcTest, MMIFWIpcRegisterSuccess) -{ - int res = mmi_initialize(); - mmi_input_event_type_e input_event_type = MMI_INPUT_EVENT_TYPE_VOICE_TOUCH; - - EXPECT_EQ(res, MMI_ERROR_NONE); - - res = mmi_ipc_register_input_event_result_cb(input_event_type, (rpc_port_proxy_mmi_result_cb_cb)voice_touch_callback); - EXPECT_EQ(res, MMI_ERROR_NONE); - - mmi_deinitialize(); -} - -TEST_F(MMIIpcTest, MMIFWIpcRegisterFailNoEvent) -{ - int res = mmi_initialize(); - mmi_input_event_type_e input_event_type = MMI_INPUT_EVENT_TYPE_NONE; - - EXPECT_EQ(res, MMI_ERROR_NONE); - - res = mmi_ipc_register_input_event_result_cb(MMI_INPUT_EVENT_TYPE_NONE, (rpc_port_proxy_mmi_result_cb_cb)voice_touch_callback); - EXPECT_EQ(res, MMI_ERROR_INVALID_PARAMETER); - - mmi_deinitialize(); -} - -TEST_F(MMIIpcTest, MMIFWIpcRegisterFailNoCallback) -{ - int res = mmi_initialize(); - mmi_input_event_type_e input_event_type = MMI_INPUT_EVENT_TYPE_VOICE_TOUCH; - - EXPECT_EQ(res, MMI_ERROR_NONE); - - res = mmi_ipc_register_input_event_result_cb(input_event_type, NULL); - EXPECT_EQ(res, MMI_ERROR_INVALID_PARAMETER); - - mmi_deinitialize(); -} - -TEST_F(MMIIpcTest, MMIFWIpcRegisterFailNoClient) -{ - int res = mmi_initialize(); - mmi_input_event_type_e input_event_type = MMI_INPUT_EVENT_TYPE_VOICE_TOUCH; - - EXPECT_EQ(res, MMI_ERROR_NONE); - - mmi_client_destroy(); - - res = mmi_ipc_register_input_event_result_cb(input_event_type, (rpc_port_proxy_mmi_result_cb_cb)voice_touch_callback); - EXPECT_EQ(res, MMI_ERROR_OPERATION_FAILED); - - mmi_deinitialize(); -} - -TEST_F(MMIIpcTest, MMIFWIpcRegisterFailNoRpcHandle) -{ - int res = mmi_initialize(); - mmi_input_event_type_e input_event_type = MMI_INPUT_EVENT_TYPE_VOICE_TOUCH; - - EXPECT_EQ(res, MMI_ERROR_NONE); - - mmi_ipc_deinitialize(); - - mmi_handle mmi_client = mmi_client_get(); - mmi_client->rpc_h = NULL; - - res = mmi_ipc_register_input_event_result_cb(input_event_type, (rpc_port_proxy_mmi_result_cb_cb)voice_touch_callback); - EXPECT_EQ(res, MMI_ERROR_OPERATION_FAILED); - - mmi_deinitialize(); -} - -TEST_F(MMIIpcTest, MMIFWIpcActivateSuccess) -{ - int res = mmi_initialize(); - mmi_input_event_type_e input_event_type = MMI_INPUT_EVENT_TYPE_VOICE_TOUCH; - - EXPECT_EQ(res, MMI_ERROR_NONE); - - res = mmi_ipc_register_input_event_result_cb(input_event_type, (rpc_port_proxy_mmi_result_cb_cb)voice_touch_callback); - EXPECT_EQ(res, MMI_ERROR_NONE); - - res = mmi_ipc_activate_input_event(input_event_type); - EXPECT_EQ(res, MMI_ERROR_NONE); - - mmi_deinitialize(); -} - -TEST_F(MMIIpcTest, MMIFWIpcActivateFailNoEvent) -{ - int res = mmi_initialize(); - mmi_input_event_type_e input_event_type = MMI_INPUT_EVENT_TYPE_VOICE_TOUCH; - - EXPECT_EQ(res, MMI_ERROR_NONE); - - res = mmi_ipc_register_input_event_result_cb(input_event_type, (rpc_port_proxy_mmi_result_cb_cb)voice_touch_callback); - EXPECT_EQ(res, MMI_ERROR_NONE); - - res = mmi_ipc_activate_input_event(MMI_INPUT_EVENT_TYPE_NONE); - EXPECT_EQ(res, MMI_ERROR_INVALID_PARAMETER); - - mmi_deinitialize(); -} - -TEST_F(MMIIpcTest, MMIFWIpcActivateFailNoRpcHandle) -{ - int res = mmi_initialize(); - mmi_input_event_type_e input_event_type = MMI_INPUT_EVENT_TYPE_VOICE_TOUCH; - - EXPECT_EQ(res, MMI_ERROR_NONE); - - res = mmi_ipc_register_input_event_result_cb(input_event_type, (rpc_port_proxy_mmi_result_cb_cb)voice_touch_callback); - EXPECT_EQ(res, MMI_ERROR_NONE); - - mmi_ipc_deinitialize(); - - mmi_handle mmi_client = mmi_client_get(); - mmi_client->rpc_h = NULL; - - res = mmi_ipc_activate_input_event(input_event_type); - EXPECT_EQ(res, MMI_ERROR_OPERATION_FAILED); - - mmi_deinitialize(); -} - -TEST_F(MMIIpcTest, MMIFWIpcActivateFailAlreadyDone) -{ - int res = mmi_initialize(); - mmi_input_event_type_e input_event_type = MMI_INPUT_EVENT_TYPE_VOICE_TOUCH; - - EXPECT_EQ(res, MMI_ERROR_NONE); - - res = mmi_ipc_register_input_event_result_cb(input_event_type, (rpc_port_proxy_mmi_result_cb_cb)voice_touch_callback); - EXPECT_EQ(res, MMI_ERROR_NONE); - - res = mmi_ipc_activate_input_event(input_event_type); - EXPECT_EQ(res, MMI_ERROR_NONE); - - res = mmi_ipc_activate_input_event(input_event_type); - EXPECT_EQ(res, MMI_ERROR_OPERATION_FAILED); - - mmi_deinitialize(); -} - -TEST_F(MMIIpcTest, MMIFWIpcDeactivateSuccess) -{ - int res = mmi_initialize(); - mmi_input_event_type_e input_event_type = MMI_INPUT_EVENT_TYPE_VOICE_TOUCH; - - EXPECT_EQ(res, MMI_ERROR_NONE); - - res = mmi_ipc_register_input_event_result_cb(input_event_type, (rpc_port_proxy_mmi_result_cb_cb)voice_touch_callback); - EXPECT_EQ(res, MMI_ERROR_NONE); - - res = mmi_ipc_activate_input_event(input_event_type); - EXPECT_EQ(res, MMI_ERROR_NONE); - - res = mmi_ipc_deactivate_input_event(input_event_type); - EXPECT_EQ(res, MMI_ERROR_NONE); - - mmi_deinitialize(); -} - -TEST_F(MMIIpcTest, MMIFWIpcDeactivateFailNoEvent) -{ - int res = mmi_initialize(); - mmi_input_event_type_e input_event_type = MMI_INPUT_EVENT_TYPE_VOICE_TOUCH; - - EXPECT_EQ(res, MMI_ERROR_NONE); - - res = mmi_ipc_register_input_event_result_cb(input_event_type, (rpc_port_proxy_mmi_result_cb_cb)voice_touch_callback); - EXPECT_EQ(res, MMI_ERROR_NONE); - - res = mmi_ipc_activate_input_event(input_event_type); - EXPECT_EQ(res, MMI_ERROR_NONE); - - res = mmi_ipc_deactivate_input_event(MMI_INPUT_EVENT_TYPE_NONE); - EXPECT_EQ(res, MMI_ERROR_INVALID_PARAMETER); - - mmi_deinitialize(); -} - -TEST_F(MMIIpcTest, MMIFWIpcDeactivateFailNoRpcHandle) -{ - int res = mmi_initialize(); - mmi_input_event_type_e input_event_type = MMI_INPUT_EVENT_TYPE_VOICE_TOUCH; - - EXPECT_EQ(res, MMI_ERROR_NONE); - - res = mmi_ipc_register_input_event_result_cb(input_event_type, (rpc_port_proxy_mmi_result_cb_cb)voice_touch_callback); - EXPECT_EQ(res, MMI_ERROR_NONE); - - res = mmi_ipc_activate_input_event(input_event_type); - EXPECT_EQ(res, MMI_ERROR_NONE); - - mmi_ipc_deinitialize(); - - mmi_handle mmi_client = mmi_client_get(); - mmi_client->rpc_h = NULL; - - res = mmi_ipc_deactivate_input_event(input_event_type); - EXPECT_EQ(res, MMI_ERROR_OPERATION_FAILED); - - mmi_deinitialize(); -} diff --git a/tests/mmi-main-test.cpp b/tests/mmi-main-test.cpp deleted file mode 100644 index a437ddb..0000000 --- a/tests/mmi-main-test.cpp +++ /dev/null @@ -1,328 +0,0 @@ -/* - * Copyright (c) 2022 Samsung Electronics Co., Ltd All Rights Reserved - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT 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 "mmi.h" -#include "mmi-client.h" -#include "mmi-tests.h" -#include "mmi-ipc.h" - -#include -#include - -class MMIMainTest : public ::testing::Test -{ -public: - void SetUp(void) override - { - ecore_init(); - } - - void TearDown(void) override - { - ecore_shutdown(); - } -}; - -TEST_F(MMIMainTest, MMIMainInit) -{ - int res = mmi_initialize(); - - EXPECT_EQ(res, MMI_ERROR_NONE); - - mmi_deinitialize(); -} - -TEST_F(MMIMainTest, MmiClientCreateSuccess) -{ - int res = mmi_client_create(); - - EXPECT_EQ(res, MMI_ERROR_NONE); - - mmi_client_destroy(); -} - -static void voice_touch_callback(int input_event_type, const char *result_out, void *user_data) -{ -} - -static void voice_recognition_callback(int input_event_type, const char *result_out, void *user_data) -{ -} - -TEST_F(MMIMainTest, MMIClientSetResultCbSuccess) -{ - int res = mmi_initialize(); - mmi_input_event_type_e input_event_type = MMI_INPUT_EVENT_TYPE_VOICE_TOUCH; - - EXPECT_EQ(res, MMI_ERROR_NONE); - - res = mmi_client_set_result_cb(input_event_type, voice_touch_callback, NULL); - EXPECT_EQ(res, MMI_ERROR_NONE); - - mmi_handle mmi_client = mmi_client_get(); - - GList* iter = NULL; - mmi_result_cb_s *data = NULL; - - iter = g_list_first(mmi_client->result_cb_list); - if(NULL != iter) { - data = (mmi_result_cb_s*)iter->data; - EXPECT_NE(data->result_callback, nullptr); - EXPECT_EQ(data->result_callback, voice_touch_callback); - EXPECT_EQ(data->input_event_type, input_event_type); - } - else { - EXPECT_TRUE(false); - } - - mmi_deinitialize(); -} - -TEST_F(MMIMainTest, MMIClientSetResultCbFail) -{ - int res = mmi_initialize(); - mmi_input_event_type_e input_event_type = MMI_INPUT_EVENT_TYPE_VOICE_TOUCH; - - EXPECT_EQ(res, MMI_ERROR_NONE); - - res = mmi_client_set_result_cb(input_event_type, NULL, NULL); - EXPECT_EQ(res, MMI_ERROR_INVALID_PARAMETER); - - mmi_deinitialize(); -} - -TEST_F(MMIMainTest, MMISetResultCbSuccess) -{ - int res = mmi_initialize(); - mmi_input_event_type_e input_event_type = MMI_INPUT_EVENT_TYPE_VOICE_TOUCH; - - EXPECT_EQ(res, MMI_ERROR_NONE); - - res = mmi_set_result_cb(input_event_type, voice_touch_callback, NULL); - EXPECT_EQ(res, MMI_ERROR_NONE); - - mmi_deinitialize(); -} - -TEST_F(MMIMainTest, MMISetResultCbFail) -{ - int res = mmi_initialize(); - mmi_input_event_type_e input_event_type = MMI_INPUT_EVENT_TYPE_VOICE_TOUCH; - - EXPECT_EQ(res, MMI_ERROR_NONE); - - res = mmi_set_result_cb(input_event_type, NULL, NULL); - EXPECT_EQ(res, MMI_ERROR_INVALID_PARAMETER); - - mmi_deinitialize(); -} - -TEST_F(MMIMainTest, MMIClientDestroyRemoveGList) -{ - int res = mmi_initialize(); - mmi_input_event_type_e input_event_type = MMI_INPUT_EVENT_TYPE_VOICE_TOUCH; - - EXPECT_EQ(res, MMI_ERROR_NONE); - - res = mmi_set_result_cb(input_event_type, voice_touch_callback, NULL); - EXPECT_EQ(res, MMI_ERROR_NONE); - - res = mmi_set_result_cb(MMI_INPUT_EVENT_TYPE_VOICE_RECOGNITION, voice_recognition_callback, NULL); - EXPECT_EQ(res, MMI_ERROR_NONE); - - mmi_client_destroy(); -} - -TEST_F(MMIMainTest, MMISetResultCbFailNoClient) -{ - int res = mmi_initialize(); - mmi_input_event_type_e input_event_type = MMI_INPUT_EVENT_TYPE_VOICE_TOUCH; - - EXPECT_EQ(res, MMI_ERROR_NONE); - - mmi_client_destroy(); - - res = mmi_set_result_cb(input_event_type, voice_touch_callback, NULL); - EXPECT_EQ(res, MMI_ERROR_OPERATION_FAILED); - - mmi_deinitialize(); -} - -TEST_F(MMIMainTest, MMISetResultCbFailNoRpcHandle) -{ - int res = mmi_initialize(); - mmi_input_event_type_e input_event_type = MMI_INPUT_EVENT_TYPE_VOICE_TOUCH; - - EXPECT_EQ(res, MMI_ERROR_NONE); - - mmi_ipc_deinitialize(); - - mmi_handle mmi_client = mmi_client_get(); - mmi_client->rpc_h = NULL; - - res = mmi_set_result_cb(input_event_type, voice_touch_callback, NULL); - EXPECT_EQ(res, MMI_ERROR_OPERATION_FAILED); - - mmi_deinitialize(); -} - -TEST_F(MMIMainTest, MMIActivateInputEventSuccess) -{ - int res = mmi_initialize(); - mmi_input_event_type_e input_event_type = MMI_INPUT_EVENT_TYPE_VOICE_TOUCH; - - EXPECT_EQ(res, MMI_ERROR_NONE); - - res = mmi_set_result_cb(input_event_type, voice_touch_callback, NULL); - EXPECT_EQ(res, MMI_ERROR_NONE); - - res = mmi_activate_input_event(input_event_type); - EXPECT_EQ(res, MMI_ERROR_NONE); - - mmi_deinitialize(); -} - -TEST_F(MMIMainTest, MMIActivateInputEventFailNoEvent) -{ - int res = mmi_initialize(); - mmi_input_event_type_e input_event_type = MMI_INPUT_EVENT_TYPE_VOICE_TOUCH; - - EXPECT_EQ(res, MMI_ERROR_NONE); - - res = mmi_set_result_cb(input_event_type, voice_touch_callback, NULL); - EXPECT_EQ(res, MMI_ERROR_NONE); - - res = mmi_activate_input_event(MMI_INPUT_EVENT_TYPE_VOICE_RECOGNITION); - EXPECT_EQ(res, MMI_ERROR_INVALID_PARAMETER); - - mmi_deinitialize(); -} - -TEST_F(MMIMainTest, MMIActivateInputEventFailNoCallback) -{ - int res = mmi_initialize(); - mmi_input_event_type_e input_event_type = MMI_INPUT_EVENT_TYPE_VOICE_TOUCH; - - EXPECT_EQ(res, MMI_ERROR_NONE); - - res = mmi_activate_input_event(input_event_type); - EXPECT_EQ(res, MMI_ERROR_INVALID_PARAMETER); - - mmi_deinitialize(); -} - -TEST_F(MMIMainTest, MMIActivateInputEventFailNoRpcHandle) -{ - int res = mmi_initialize(); - mmi_input_event_type_e input_event_type = MMI_INPUT_EVENT_TYPE_VOICE_TOUCH; - - EXPECT_EQ(res, MMI_ERROR_NONE); - - res = mmi_set_result_cb(input_event_type, voice_touch_callback, NULL); - EXPECT_EQ(res, MMI_ERROR_NONE); - - mmi_ipc_deinitialize(); - - mmi_handle mmi_client = mmi_client_get(); - mmi_client->rpc_h = NULL; - - res = mmi_activate_input_event(input_event_type); - EXPECT_EQ(res, MMI_ERROR_OPERATION_FAILED); - - mmi_deinitialize(); -} - -TEST_F(MMIMainTest, MMIActivateInputEventFailAlreadyDone) -{ - int res = mmi_initialize(); - mmi_input_event_type_e input_event_type = MMI_INPUT_EVENT_TYPE_VOICE_TOUCH; - - EXPECT_EQ(res, MMI_ERROR_NONE); - - res = mmi_set_result_cb(input_event_type, voice_touch_callback, NULL); - EXPECT_EQ(res, MMI_ERROR_NONE); - - res = mmi_activate_input_event(input_event_type); - EXPECT_EQ(res, MMI_ERROR_NONE); - - res = mmi_activate_input_event(input_event_type); - EXPECT_EQ(res, MMI_ERROR_OPERATION_FAILED); - - mmi_deinitialize(); -} - -TEST_F(MMIMainTest, MMIDeactivateInputEventSuccess) -{ - int res = mmi_initialize(); - mmi_input_event_type_e input_event_type = MMI_INPUT_EVENT_TYPE_VOICE_TOUCH; - - EXPECT_EQ(res, MMI_ERROR_NONE); - - res = mmi_set_result_cb(input_event_type, voice_touch_callback, NULL); - EXPECT_EQ(res, MMI_ERROR_NONE); - - res = mmi_activate_input_event(input_event_type); - EXPECT_EQ(res, MMI_ERROR_NONE); - - res = mmi_deactivate_input_event(input_event_type); - EXPECT_EQ(res, MMI_ERROR_NONE); - - mmi_deinitialize(); -} - -TEST_F(MMIMainTest, MMIDeactivateInputEventFailNoEvent) -{ - int res = mmi_initialize(); - mmi_input_event_type_e input_event_type = MMI_INPUT_EVENT_TYPE_VOICE_TOUCH; - - EXPECT_EQ(res, MMI_ERROR_NONE); - - res = mmi_set_result_cb(input_event_type, voice_touch_callback, NULL); - EXPECT_EQ(res, MMI_ERROR_NONE); - - res = mmi_activate_input_event(input_event_type); - EXPECT_EQ(res, MMI_ERROR_NONE); - - res = mmi_deactivate_input_event(MMI_INPUT_EVENT_TYPE_VOICE_RECOGNITION); - EXPECT_EQ(res, MMI_ERROR_INVALID_PARAMETER); - - mmi_deinitialize(); -} - -TEST_F(MMIMainTest, MMIDeactivateInputEventFailNoRpcHandle) -{ - int res = mmi_initialize(); - mmi_input_event_type_e input_event_type = MMI_INPUT_EVENT_TYPE_VOICE_TOUCH; - - EXPECT_EQ(res, MMI_ERROR_NONE); - - res = mmi_set_result_cb(input_event_type, voice_touch_callback, NULL); - EXPECT_EQ(res, MMI_ERROR_NONE); - - res = mmi_activate_input_event(input_event_type); - EXPECT_EQ(res, MMI_ERROR_NONE); - - mmi_ipc_deinitialize(); - - mmi_handle mmi_client = mmi_client_get(); - mmi_client->rpc_h = NULL; - - res = mmi_deactivate_input_event(input_event_type); - EXPECT_EQ(res, MMI_ERROR_OPERATION_FAILED); - - mmi_deinitialize(); -} diff --git a/tests/mmi-manager/ecore-event-dispatcher.cpp b/tests/mmi-manager/ecore-event-dispatcher.cpp new file mode 100644 index 0000000..768860f --- /dev/null +++ b/tests/mmi-manager/ecore-event-dispatcher.cpp @@ -0,0 +1,20 @@ +#include + +Ecore_Idle_Enterer *_idle_before = nullptr; +bool g_flag_wait_done = false; + +static Eina_Bool _e_cb_idle_before(void *data) { + (void) data; + + g_flag_wait_done = true; + return ECORE_CALLBACK_RENEW; +} + +void wait_for_dispatch() { + g_flag_wait_done = false; + _idle_before = ecore_idle_enterer_before_add(_e_cb_idle_before, NULL); + while (!g_flag_wait_done) { + ecore_main_loop_iterate(); + } + ecore_idle_enterer_del(_idle_before); +} \ No newline at end of file diff --git a/tests/mmi-manager/meson.build b/tests/mmi-manager/meson.build new file mode 100644 index 0000000..7c0bb17 --- /dev/null +++ b/tests/mmi-manager/meson.build @@ -0,0 +1,34 @@ +mmi_manager_tests_srcs = [ + 'mmi-manager-tests.cpp', + 'mmi-client-tests.cpp', + 'mmi-manager-main-tests.cpp', + 'ecore-event-dispatcher.cpp', + 'port-instance-manager/mmi-port-instance-manager-tests.cpp', + 'node-prototype/mmi-node-prototype-tests.cpp', + 'node-prototype-manager/mmi-node-prototype-manager-tests.cpp', + 'node-instance-manager/mmi-node-instance-manager-tests.cpp', + 'workflow-prototype/mmi-workflow-prototype-tests.cpp', + 'workflow-instance/mmi-workflow-instance-tests.cpp', + 'workflow-instance-manager/mmi-workflow-instance-manager-tests.cpp', + 'plugin-module-registry/mmi-plugin-module-registry-tests.cpp', + ] + +gmock_dep = dependency('gmock', method : 'pkg-config') +ecore_dep = dependency('ecore', method : 'pkg-config') + +mmi_manager_tests_include_dirs = include_directories( + '.', + '../../src/common/', + '../../src/mmi-manager/', + ) + +tc = executable( + 'mmi-manager-tests', + mmi_manager_tests_srcs, + include_directories : [ mmi_manager_tests_include_dirs ], + dependencies : [mmi_manager_declared_dep, gmock_dep, ecore_dep], + install_dir : mmi_prefix_bindir, + install : true + ) + +test('mmi-manager-tests', tc, args : ['--gtest_output=xml:./mmi-manager-tests.xml']) diff --git a/tests/mmi-manager/mmi-client-tests.cpp b/tests/mmi-manager/mmi-client-tests.cpp new file mode 100644 index 0000000..ddb4216 --- /dev/null +++ b/tests/mmi-manager/mmi-client-tests.cpp @@ -0,0 +1,148 @@ +/* +* Copyright © 2021 Samsung Electronics co., Ltd. All Rights Reserved. +* +* Permission is hereby granted, free of charge, to any person obtaining a +* copy of this software and associated documentation files (the "Software"), +* to deal in the Software without restriction, including without limitation +* the rights to use, copy, modify, merge, publish, distribute, sublicense, +* and/or sell copies of the Software, and to permit persons to whom the +* Software is furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice (including the next +* paragraph) shall be included in all copies or substantial portions of the +* Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +* DEALINGS IN THE SOFTWARE. +*/ + +#include "mmi-client.h" +#include "mmi-manager.h" +#include "mmi-manager-tests.h" + +using namespace mmi; + +class MMIClientTest : public ::testing::Test { +public: + void SetUp(void) override { + } + + void TearDown(void) override { + } + + ClientManager client_manager; +}; + + +TEST_F(MMIClientTest, MMIClientInitShutdown) { + int res = client_manager.initialize(); + EXPECT_EQ(res, MMI_ERROR_NONE); + + client_manager.deinitialize(); + EXPECT_EQ(res, MMI_ERROR_NONE); +} + +TEST_F(MMIClientTest, MMIClientAddGetClient) { + std::string app_id{"org.tizen.mmi-system-ux-test"}; + Client *client = nullptr, *client_retrieved = nullptr; + + int res = client_manager.initialize(); + EXPECT_EQ(res, MMI_ERROR_NONE); + + client = client_manager.add_client(app_id); + EXPECT_NE(client, nullptr); + + client_retrieved = client_manager.get_client(app_id); + EXPECT_EQ(client_retrieved, client); + + res = client_manager.deinitialize(); + EXPECT_EQ(res, MMI_ERROR_NONE); +} + +TEST_F(MMIClientTest, MMIClientAddRemoveClient) { + std::string app_id{"org.tizen.mmi-system-ux-test"}; + Client *client = nullptr; + + int res = client_manager.initialize(); + EXPECT_EQ(res, MMI_ERROR_NONE); + + client = client_manager.add_client(app_id); + EXPECT_NE(client, nullptr); + + res = client_manager.remove_client(app_id); + EXPECT_EQ(res, MMI_ERROR_NONE); + + res = client_manager.deinitialize(); + EXPECT_EQ(res, MMI_ERROR_NONE); +} + +TEST_F(MMIClientTest, MMIClientSetGetClientUID) { + std::string app_id{"org.tizen.mmi-system-ux-test"}; + Client *client = nullptr; + uid_t uid = 1; + + int res = client_manager.initialize(); + EXPECT_EQ(res, MMI_ERROR_NONE); + + client = client_manager.add_client(app_id); + + res = client_manager.set_client_uid(client, uid); + EXPECT_EQ(res, MMI_ERROR_NONE); + + uid_t retrieved = client_manager.get_client_uid(client); + EXPECT_EQ(uid, retrieved); + + res = client_manager.remove_client(app_id); + EXPECT_EQ(res, MMI_ERROR_NONE); + + res = client_manager.deinitialize(); + EXPECT_EQ(res, MMI_ERROR_NONE); +} + +TEST_F(MMIClientTest, MMIClientSetGetClientPID) { + std::string app_id{"org.tizen.mmi-system-ux-test"}; + Client *client = nullptr; + pid_t pid = 1; + + int res = client_manager.initialize(); + EXPECT_EQ(res, MMI_ERROR_NONE); + + client = client_manager.add_client(app_id); + + res = client_manager.set_client_pid(client, pid); + EXPECT_EQ(res, MMI_ERROR_NONE); + + pid_t retrieved = client_manager.get_client_pid(client); + EXPECT_EQ(pid, retrieved); + + res = client_manager.remove_client(app_id); + + res = client_manager.deinitialize(); + EXPECT_EQ(res, MMI_ERROR_NONE); +} + +TEST_F(MMIClientTest, MMIClientGetClientSender) { + std::string app_id{"org.tizen.mmi-system-ux-test"}; + std::string sender; + Client *client = nullptr; + + int res = client_manager.initialize(); + EXPECT_EQ(res, MMI_ERROR_NONE); + + client = client_manager.add_client(app_id); + + sender = client_manager.get_client_sender(client); + EXPECT_EQ(sender, app_id); + + res = client_manager.remove_client(app_id); + EXPECT_EQ(res, 0); + + res = client_manager.deinitialize(); + EXPECT_EQ(res, MMI_ERROR_NONE); +} + diff --git a/tests/mmi-manager/mmi-manager-main-tests.cpp b/tests/mmi-manager/mmi-manager-main-tests.cpp new file mode 100644 index 0000000..863b439 --- /dev/null +++ b/tests/mmi-manager/mmi-manager-main-tests.cpp @@ -0,0 +1,82 @@ +/* +* Copyright © 2021 Samsung Electronics co., Ltd. All Rights Reserved. +* +* Permission is hereby granted, free of charge, to any person obtaining a +* copy of this software and associated documentation files (the "Software"), +* to deal in the Software without restriction, including without limitation +* the rights to use, copy, modify, merge, publish, distribute, sublicense, +* and/or sell copies of the Software, and to permit persons to whom the +* Software is furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice (including the next +* paragraph) shall be included in all copies or substantial portions of the +* Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +* DEALINGS IN THE SOFTWARE. +*/ + +#include "mmi-manager.h" +#include "mmi-manager-tests.h" +#include "mmi-self-container.h" + +namespace { + +using namespace mmi; +using namespace mmi::communication; + +class CommunicationChannelManagerDummy : public CommunicationChannelManager { +public: + int connect() override { + return MMI_ERROR_NONE; + } + int disconnect() override { + return MMI_ERROR_NONE; + } + int send(communication::Message *message) override { + return MMI_ERROR_NONE; + } + + void on_observer_event( + const WORKFLOW_OUTPUT_EVENT_TYPE &event, std::any data) override { + return; + } +}; + +struct CommunicationChannelFactoryDummy : public CommunicationChannelManagerFactory { + std::shared_ptr create_channel() override { + return std::make_shared(); + } +}; + +class MMIMANAGERMainTest : public ::testing::Test { +public: + void SetUp(void) override { + } + + void TearDown(void) override { + } + +}; + + +TEST_F(MMIMANAGERMainTest, MMIMANAGERMainInit) { + int res = 0; + + mmi::Manager manager( + std::make_shared(), + std::make_shared()); + res = manager.initialize(); + + EXPECT_EQ(res, MMI_ERROR_NONE); + + res = manager.deinitialize(); + EXPECT_EQ(res, MMI_ERROR_NONE); +} + +} // namespace diff --git a/tests/mmi-manager/mmi-manager-tests.cpp b/tests/mmi-manager/mmi-manager-tests.cpp new file mode 100644 index 0000000..98b0c6c --- /dev/null +++ b/tests/mmi-manager/mmi-manager-tests.cpp @@ -0,0 +1,52 @@ +/* +* Copyright © 2021 Samsung Electronics co., Ltd. All Rights Reserved. +* +* Permission is hereby granted, free of charge, to any person obtaining a +* copy of this software and associated documentation files (the "Software"), +* to deal in the Software without restriction, including without limitation +* the rights to use, copy, modify, merge, publish, distribute, sublicense, +* and/or sell copies of the Software, and to permit persons to whom the +* Software is furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice (including the next +* paragraph) shall be included in all copies or substantial portions of the +* Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +* DEALINGS IN THE SOFTWARE. +*/ + +#include "mmi-manager-tests.h" + +int main(int argc, char **argv) { + auto testResults = false; + +#ifdef TIZEN_TEST_GCOV + setenv("GCOV_PREFIX", "/tmp", 1); +#endif + try { + ::testing::InitGoogleMock(&argc, argv); + ::testing::FLAGS_gtest_death_test_style = "fast"; + } catch ( ... ) { + PRINT("Error occurred while trying to initialize GoogleTest.\n"); + exit(EXIT_FAILURE); + } + + try { + testResults = (RUN_ALL_TESTS() == 0) ? true : false; + } catch (const ::testing::internal::GoogleTestFailureException &e) { + testResults = false; + PRINT("GoogleTestFailureException has been thrown: %s\n", e.what()); + } + +#ifdef TIZEN_TEST_GCOV + __gcov_flush(); +#endif + return (testResults ? 0 : 1); +} + diff --git a/tests/mmi-manager/mmi-manager-tests.h b/tests/mmi-manager/mmi-manager-tests.h new file mode 100644 index 0000000..4034144 --- /dev/null +++ b/tests/mmi-manager/mmi-manager-tests.h @@ -0,0 +1,43 @@ +/* +* Copyright © 2021 Samsung Electronics co., Ltd. All Rights Reserved. +* +* Permission is hereby granted, free of charge, to any person obtaining a +* copy of this software and associated documentation files (the "Software"), +* to deal in the Software without restriction, including without limitation +* the rights to use, copy, modify, merge, publish, distribute, sublicense, +* and/or sell copies of the Software, and to permit persons to whom the +* Software is furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice (including the next +* paragraph) shall be included in all copies or substantial portions of the +* Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +* DEALINGS IN THE SOFTWARE. +*/ + +#ifndef __MMI_MANAGER_TESTS_H__ +#define __MMI_MANAGER_TESTS_H__ + +#include +#include +#include + +#define PRINT printf + +#ifdef TIZEN_TEST_GCOV +extern "C" void __gcov_flush(void); +#endif +extern void wait_for_dispatch(); + +using ::testing::TestWithParam; +using ::testing::Bool; +using ::testing::Values; +using ::testing::Combine; + +#endif //__MMI_MANAGER_TESTS_H__ diff --git a/tests/mmi-manager/node-instance-manager/mmi-node-instance-manager-tests.cpp b/tests/mmi-manager/node-instance-manager/mmi-node-instance-manager-tests.cpp new file mode 100644 index 0000000..28cfeee --- /dev/null +++ b/tests/mmi-manager/node-instance-manager/mmi-node-instance-manager-tests.cpp @@ -0,0 +1,101 @@ +#include +#include +#include + +#include "mmi-node-instance-manager.h" +#include "mmi-self-container.h" + +using namespace mmi; + +extern "C" { + /* Add wrapper functions here with __wrap_ and __real prefix + Example : + + int __real_vconf_get_int(const char *in_key, int *intval); + int __wrap_vconf_get_int(const char *in_key, int *intval) + { + if (strcmp(in_key, "TestKey") == 0) + return 0; + + return __real_vconf_get_int(in_key, intval); + } + */ +} + +namespace { + +class NodePrototypeStoreDummy : public INodePrototypeStore { +public: + virtual bool add_node_prototype(std::shared_ptr prototype) override { + return true; + } + virtual std::shared_ptr get_node_prototype( + mmi_standard_node_type_e type, mmi_standard_node_sub_type_e sub_type) override { + last_query = std::make_pair(type, sub_type); + return nullptr; + } + + std::pair last_query; +}; + +class PluginModuleProxyProviderDummy : public IPluginModuleProxyProvider { +public: + PluginModuleProxyProviderDummy() { + proxy = factory.create( + PluginModuleInfo{mmi_plugin_module_type_e::SELF_CONTAINED, "dummy"}); + } + virtual std::shared_ptr get_plugin_module_proxy( + PluginModuleInfo plugin_module_info) override { + return proxy; + } + + PluginModuleProxyFactorySelfContainerTest factory; + std::shared_ptr proxy; +}; + +class NodeInstanceManagerTest : public testing::Test { +public: + NodeInstanceManagerTest() { + } + virtual ~NodeInstanceManagerTest() { + } + void SetUp() override { + prototype_store = std::make_shared(); + plugin_module_proxy_provider = std::make_shared(); + + manager.set_node_prototype_store(prototype_store); + manager.set_plugin_module_proxy_provider(plugin_module_proxy_provider); + + ASSERT_NE(prototype_store, nullptr); + ASSERT_NE(plugin_module_proxy_provider, nullptr); + } + void TearDown() override { + } + + NodeInstanceManager manager; + + std::shared_ptr prototype_store; + std::shared_ptr plugin_module_proxy_provider; +}; + +TEST_F(NodeInstanceManagerTest, ProperNodePrototypeQueriedWhenCreatingNodeInstance) { + prototype_store->last_query = std::make_pair( + MMI_STANDARD_NODE_TYPE_NONE, std::monostate{}); + + std::shared_ptr instance = manager.create_node_instance( + MMI_STANDARD_NODE_TYPE_PROCESSOR, MMI_STANDARD_NODE_PROCESSOR_TYPE_ASR ); + + ASSERT_EQ(prototype_store->last_query.first, MMI_STANDARD_NODE_TYPE_PROCESSOR); + ASSERT_EQ(prototype_store->last_query.second, mmi_standard_node_sub_type_e{MMI_STANDARD_NODE_PROCESSOR_TYPE_ASR}); +} + +} // namespace + +#ifdef TEST_MAIN_REQUIRED +int main(int argc, char** argv) { + std::cout << "Starting tests" << std::endl; + testing::InitGoogleTest(&argc, argv); + int ret = RUN_ALL_TESTS(); + return ret; +} +#endif diff --git a/tests/mmi-manager/node-prototype-manager/mmi-node-prototype-manager-tests.cpp b/tests/mmi-manager/node-prototype-manager/mmi-node-prototype-manager-tests.cpp new file mode 100644 index 0000000..80a930e --- /dev/null +++ b/tests/mmi-manager/node-prototype-manager/mmi-node-prototype-manager-tests.cpp @@ -0,0 +1,85 @@ +#include +#include +#include + +#include "mmi-node-prototype-manager.h" + +using namespace mmi; + +extern "C" { + /* Add wrapper functions here with __wrap_ and __real prefix + Example : + + int __real_vconf_get_int(const char *in_key, int *intval); + int __wrap_vconf_get_int(const char *in_key, int *intval) + { + if (strcmp(in_key, "TestKey") == 0) + return 0; + + return __real_vconf_get_int(in_key, intval); + } + */ +} + +namespace { + +class NodePrototypeManagerTest : public testing::Test { +public: + NodePrototypeManagerTest() { + } + virtual ~NodePrototypeManagerTest() { + } + void SetUp() override { + valid_prototype = std::make_shared(); + ASSERT_TRUE(valid_prototype != nullptr); + + valid_prototype->set_plugin_module_info( + PluginModuleInfo{mmi_plugin_module_type_e::SHARED_LIBRARY, "test.so"}); + + valid_prototype->set_type(MMI_STANDARD_NODE_TYPE_SOURCE); + valid_prototype->set_sub_type(MMI_STANDARD_NODE_SOURCE_TYPE_MIC_AMBIENT); + + ASSERT_TRUE(valid_prototype->is_valid()); + } + void TearDown() override { + } + + NodePrototypeManager manager; + std::shared_ptr valid_prototype; +}; + +TEST_F(NodePrototypeManagerTest, NodePrototypeManagerProperlyAddedAndRetrieved_p) { + manager.add_node_prototype(valid_prototype); + + auto prototype = manager.get_node_prototype( + valid_prototype->get_type(), + valid_prototype->get_sub_type()); + + ASSERT_TRUE(prototype != nullptr); +} + +TEST_F(NodePrototypeManagerTest, NodePrototypeManagerRefusesInvalidPrototype_n) { + auto invalid_prototype = std::make_shared(); + + invalid_prototype->set_type(MMI_STANDARD_NODE_TYPE_SOURCE); + invalid_prototype->set_sub_type(MMI_STANDARD_NODE_PROCESSOR_TYPE_ASR); + + manager.add_node_prototype(invalid_prototype); + + auto prototype = manager.get_node_prototype( + invalid_prototype->get_type(), + invalid_prototype->get_sub_type()); + + ASSERT_TRUE(prototype == nullptr); +} + +} // namespace + +#ifdef TEST_MAIN_REQUIRED +int main(int argc, char** argv) { + std::cout << "Starting tests" << std::endl; + testing::InitGoogleTest(&argc, argv); + int ret = RUN_ALL_TESTS(); + return ret; +} +#endif diff --git a/tests/mmi-manager/node-prototype/mmi-node-prototype-tests.cpp b/tests/mmi-manager/node-prototype/mmi-node-prototype-tests.cpp new file mode 100644 index 0000000..bc90e8f --- /dev/null +++ b/tests/mmi-manager/node-prototype/mmi-node-prototype-tests.cpp @@ -0,0 +1,147 @@ +#include +#include +#include + +#include "mmi-node-prototype.h" + +using namespace mmi; + +extern "C" { + /* Add wrapper functions here with __wrap_ and __real prefix + Example : + + int __real_vconf_get_int(const char *in_key, int *intval); + int __wrap_vconf_get_int(const char *in_key, int *intval) + { + if (strcmp(in_key, "TestKey") == 0) + return 0; + + return __real_vconf_get_int(in_key, intval); + } + */ +} + +namespace { + +class NodePrototypeTest : public testing::Test { +public: + NodePrototypeTest() { + } + virtual ~NodePrototypeTest() { + } + void SetUp() override { + prototype = std::make_shared(); + ASSERT_TRUE(prototype != nullptr); + } + void TearDown() override { + } + std::shared_ptr prototype; +}; + +TEST_F(NodePrototypeTest, NodePrototypeValidationSucceedsForDifferentNodeTypes_p) { + prototype->set_plugin_module_info( + PluginModuleInfo{mmi_plugin_module_type_e::SHARED_LIBRARY, "test.so"}); + + prototype->set_type(MMI_STANDARD_NODE_TYPE_SOURCE); + prototype->set_sub_type(MMI_STANDARD_NODE_SOURCE_TYPE_MIC_AMBIENT); + ASSERT_TRUE(prototype->is_valid()); + + prototype->set_type(MMI_STANDARD_NODE_TYPE_PROCESSOR); + prototype->set_sub_type(MMI_STANDARD_NODE_PROCESSOR_TYPE_ASR); + ASSERT_TRUE(prototype->is_valid()); + + prototype->set_type(MMI_STANDARD_NODE_TYPE_LOGIC); + prototype->set_sub_type(MMI_STANDARD_NODE_LOGIC_TYPE_FIXED_STRING_MATCH); + ASSERT_TRUE(prototype->is_valid()); +} + +TEST_F(NodePrototypeTest, NodePrototypeValidationFailsWithNoSubtype_n) { + prototype->set_plugin_module_info( + PluginModuleInfo{mmi_plugin_module_type_e::SHARED_LIBRARY, "test.so"}); + + prototype->set_type(MMI_STANDARD_NODE_TYPE_SOURCE); + /* No sub type provided */ + + ASSERT_FALSE(prototype->is_valid()); +} + +TEST_F(NodePrototypeTest, NodePrototypeValidationFailsWithIncorrectSubtype_n) { + prototype->set_plugin_module_info( + PluginModuleInfo{mmi_plugin_module_type_e::SHARED_LIBRARY, "test.so"}); + + prototype->set_type(MMI_STANDARD_NODE_TYPE_SOURCE); + /* PROCESSOR sub type for SOURCE type */ + prototype->set_sub_type(MMI_STANDARD_NODE_PROCESSOR_TYPE_ASR); + + ASSERT_FALSE(prototype->is_valid()); +} + +TEST_F(NodePrototypeTest, NodePrototypeValidationFailsWithNoPluginModuleInfo_n) { + prototype->set_plugin_module_info( + PluginModuleInfo{mmi_plugin_module_type_e::SHARED_LIBRARY, ""}); + /* No plugin module identifier provided */ + + prototype->set_type(MMI_STANDARD_NODE_TYPE_SOURCE); + prototype->set_sub_type(MMI_STANDARD_NODE_SOURCE_TYPE_MIC_AMBIENT); + + ASSERT_FALSE(prototype->is_valid()); +} + +TEST_F(NodePrototypeTest, PortInfoProperlyAdded_p) { + ASSERT_TRUE(prototype->get_port_infos().size() == 0); + + PortInfo port_info_1 = {"test_port_1", MMI_PORT_TYPE_IN, MMI_DATA_TYPE_TEXT, "test_description"}; + prototype->add_port_info(port_info_1); + + PortInfo port_info_2 = {"test_port_2", MMI_PORT_TYPE_OUT, MMI_DATA_TYPE_BOOLEAN, "test_description"}; + prototype->add_port_info(port_info_2); + + ASSERT_TRUE(prototype->get_port_infos().size() == 2); +} + +TEST_F(NodePrototypeTest, PortInfoWithSameNameNotAdded_n) { + ASSERT_TRUE(prototype->get_port_infos().size() == 0); + + PortInfo port_info_1 = {"test_port_1", MMI_PORT_TYPE_IN, MMI_DATA_TYPE_TEXT, "test_description"}; + prototype->add_port_info(port_info_1); + + PortInfo port_info_2 = {"test_port_1", MMI_PORT_TYPE_OUT, MMI_DATA_TYPE_BOOLEAN, "test_description"}; + prototype->add_port_info(port_info_2); + + ASSERT_TRUE(prototype->get_port_infos().size() == 1); +} + +TEST_F(NodePrototypeTest, AttributeProperlyAdded_p) { + ASSERT_TRUE(prototype->get_attribute_infos().size() == 0); + + AttributeInfo attribute_info_1 = {"test_attribute_1", MMI_PRIMITIVE_VALUE_TYPE_INT}; + prototype->add_attribute_info(attribute_info_1); + + AttributeInfo attribute_info_2 = {"test_attribute_2", MMI_PRIMITIVE_VALUE_TYPE_STRING}; + prototype->add_attribute_info(attribute_info_2); + + ASSERT_TRUE(prototype->get_attribute_infos().size() == 2); +} + +TEST_F(NodePrototypeTest, AttributeWithSameNameNotAdded_n) { + ASSERT_TRUE(prototype->get_attribute_infos().size() == 0); + + AttributeInfo attribute_info_1 = {"test_attribute_1", MMI_PRIMITIVE_VALUE_TYPE_INT}; + prototype->add_attribute_info(attribute_info_1); + + AttributeInfo attribute_info_2 = {"test_attribute_1", MMI_PRIMITIVE_VALUE_TYPE_STRING}; + prototype->add_attribute_info(attribute_info_2); + + ASSERT_TRUE(prototype->get_attribute_infos().size() == 1); +} + +} // namespace + +#ifdef TEST_MAIN_REQUIRED +int main(int argc, char** argv) { + std::cout << "Starting tests" << std::endl; + testing::InitGoogleTest(&argc, argv); + int ret = RUN_ALL_TESTS(); + return ret; +} +#endif diff --git a/tests/mmi-manager/plugin-module-registry/mmi-plugin-module-registry-tests.cpp b/tests/mmi-manager/plugin-module-registry/mmi-plugin-module-registry-tests.cpp new file mode 100644 index 0000000..d60dcde --- /dev/null +++ b/tests/mmi-manager/plugin-module-registry/mmi-plugin-module-registry-tests.cpp @@ -0,0 +1,279 @@ +#include +#include +#include + +#include "mmi-plugin-module-registry.h" +#include "mmi-self-container.h" + +using namespace mmi; + +extern "C" { + /* Add wrapper functions here with __wrap_ and __real prefix + Example : + + int __real_vconf_get_int(const char *in_key, int *intval); + int __wrap_vconf_get_int(const char *in_key, int *intval) + { + if (strcmp(in_key, "TestKey") == 0) + return 0; + + return __real_vconf_get_int(in_key, intval); + } + */ +} + +namespace { + +class NodePrototypeStoreDummy : public INodePrototypeStore { +public: + virtual bool add_node_prototype(std::shared_ptr prototype) override { + _D("Adding prototype : %d", prototype->is_valid()); + m_prototypes.push_back(prototype); + + _D("Node prototype added: type=%d, sub_type=%d, plugin_module_type=%d, plugin_module_identifier=%s", + prototype->get_type(), to_int(prototype->get_sub_type()), + static_cast(prototype->get_plugin_module_info().plugin_module_type), + prototype->get_plugin_module_info().plugin_module_identifier.c_str()); + + for (auto& port : prototype->get_port_infos()) { + _D("Port info: name=%s, port_type=%d, data_type=%d, data_description=%s", + port.name.c_str(), port.port_type, port.data_type, port.data_description.c_str()); + } + + for (auto& attribute : prototype->get_attribute_infos()) { + _D("Attribute info: name=%s, value_type=%d", + attribute.name.c_str(), attribute.value_type); + } + + return true; + } + virtual std::shared_ptr get_node_prototype( + mmi_standard_node_type_e type, mmi_standard_node_sub_type_e sub_type) override { + for (auto &prototype : m_prototypes) { + if (prototype->get_type() == type && prototype->get_sub_type() == sub_type) { + return prototype; + } + } + return nullptr; + } + size_t get_prototype_count() { + return m_prototypes.size(); + } + std::vector> m_prototypes; +}; + +class WorkflowPrototypeStoreDummy : public IWorkflowPrototypeStore { +public: + virtual bool add_workflow_prototype(std::shared_ptr prototype) override { + _D("Adding prototype : %d", prototype->is_valid()); + m_prototypes.push_back(prototype); + + _D("Workflow prototype added: type=%d, plugin_module_type=%d, plugin_module_identifier=%s", + prototype->get_type(), + static_cast(prototype->get_plugin_module_info().plugin_module_type), + prototype->get_plugin_module_info().plugin_module_identifier.c_str()); + _D(" node_infos:"); + for (auto& elem : prototype->get_node_infos()) { + _D(" name=%s, node_type=%d, node_sub_type=%d", + elem.name.c_str(), elem.node_type, to_int(elem.node_sub_type)); + } + _D(" link_infos:"); + for (auto& elem : prototype->get_link_infos()) { + _D(" from_node_name=%s, from_port_name=%s, to_node_name=%s, to_port_name=%s", + elem.from_node_name.c_str(), elem.from_port_name.c_str(), + elem.to_node_name.c_str(), elem.to_port_name.c_str()); + } + _D(" output_assignment_infos:"); + for (auto& elem : prototype->get_output_assignment_infos()) { + _D(" output_name=%s, from_node_name=%s, from_port_name=%s", + elem.output_name.c_str(), elem.from_node_name.c_str(), elem.from_port_name.c_str()); + } + + return true; + } + virtual std::shared_ptr get_workflow_prototype( + mmi_standard_workflow_type_e type) override { + for (auto &prototype : m_prototypes) { + if (prototype->get_type() == type) { + return prototype; + } + } + return nullptr; + } + size_t get_prototype_count() { + return m_prototypes.size(); + } + std::vector> m_prototypes; +}; + +/* Derived class to access protected members */ +class PluginModuleRegistry_Derived : public PluginModuleRegistry { +public: + typedef std::pair plugin_module_map_key; + std::map>& get_plugin_modules() { + return m_plugin_modules; + } +}; + +class PluginModuleRegistryTest : public testing::Test { +public: + PluginModuleRegistryTest() { + } + virtual ~PluginModuleRegistryTest() { + } + void SetUp() override { + factory = std::make_shared(); + node_prototype_store = std::make_shared(); + workflow_prototype_store = std::make_shared(); + registry.set_plugin_module_proxy_factory(factory); + registry.set_node_prototype_store(node_prototype_store); + registry.set_workflow_prototype_store(workflow_prototype_store); + } + void TearDown() override { + } + + std::shared_ptr factory; + std::shared_ptr node_prototype_store; + std::shared_ptr workflow_prototype_store; + PluginModuleRegistry_Derived registry; +}; + +TEST_F(PluginModuleRegistryTest, PluginModuleProxyProperlyCreated_p) { + registry.initialize(); + + std::map>& plugin_modules = registry.get_plugin_modules(); + + auto plugin_module = registry.get_plugin_module_proxy( + PluginModuleInfo{mmi_plugin_module_type_e::SHARED_LIBRARY, "dummy.so"}); + ASSERT_NE(nullptr, plugin_module); + + auto it = plugin_modules.find(std::make_pair( + mmi_plugin_module_type_e::SHARED_LIBRARY, "dummy.so")); + + ASSERT_NE(plugin_modules.end(), it); + + /* Reference count should be 1 */ + ASSERT_EQ(1, it->second.use_count()); + + registry.deinitialize(); +} + +TEST_F(PluginModuleRegistryTest, PluginModuleProxyProperlyDestroyedWhenGoesOutOfScope_p) { + registry.initialize(); + + std::map>& plugin_modules = registry.get_plugin_modules(); + + { + auto plugin_module = registry.get_plugin_module_proxy( + PluginModuleInfo{mmi_plugin_module_type_e::SHARED_LIBRARY, "dummy.so"}); + ASSERT_NE(nullptr, plugin_module); + + std::map>& plugin_modules = registry.get_plugin_modules(); + auto it = plugin_modules.find(std::make_pair(mmi_plugin_module_type_e::SHARED_LIBRARY, "dummy.so")); + + ASSERT_NE(plugin_modules.end(), it); + ASSERT_EQ(1, it->second.use_count()); + } + + auto it = plugin_modules.find(std::make_pair(mmi_plugin_module_type_e::SHARED_LIBRARY, "dummy.so")); + + ASSERT_NE(plugin_modules.end(), it); + + /* Reference count should be 0, since the 'plugin_module' variable went out of scope */ + ASSERT_EQ(0, it->second.use_count()); + + registry.deinitialize(); +} + +TEST_F(PluginModuleRegistryTest, PluginModuleProxyNotCreatedAgainWhenAlreadyExists_n) { + registry.initialize(); + + std::map>& plugin_modules = registry.get_plugin_modules(); + + auto plugin_module_1 = registry.get_plugin_module_proxy( + PluginModuleInfo{mmi_plugin_module_type_e::SHARED_LIBRARY, "dummy.so"}); + ASSERT_NE(nullptr, plugin_module_1); + + /* There should be 2 plugin modules by now : + * 1. "default.so", which is created in the 'initialize' function + * 2. "dummy.so", which is created in the previous line + * */ + ASSERT_EQ(plugin_modules.size(), 2); + + auto plugin_module_2 = registry.get_plugin_module_proxy( + PluginModuleInfo{mmi_plugin_module_type_e::SHARED_LIBRARY, "dummy.so"}); + ASSERT_NE(nullptr, plugin_module_2); + + /* Size should not be incremented */ + ASSERT_EQ(plugin_modules.size(), 2); + + auto it = plugin_modules.find(std::make_pair(mmi_plugin_module_type_e::SHARED_LIBRARY, "dummy.so")); + ASSERT_NE(plugin_modules.end(), it); + + /* Reference count should be 2, since both 'plugin_module_1' and 'plugin_module_2' variables are pointing to the same object */ + ASSERT_EQ(2, it->second.use_count()); + + registry.deinitialize(); +} + +TEST_F(PluginModuleRegistryTest, DummyNodeInfoProperlyCreatedAndRetrieved_p) { + registry.initialize(); + + bool ret = registry.load_node_prototypes(); + ASSERT_TRUE(ret); + + auto asr_node = node_prototype_store->get_node_prototype(MMI_STANDARD_NODE_TYPE_PROCESSOR, MMI_STANDARD_NODE_PROCESSOR_TYPE_ASR); + ASSERT_NE(nullptr, asr_node); + + auto match_node = node_prototype_store->get_node_prototype(MMI_STANDARD_NODE_TYPE_LOGIC, MMI_STANDARD_NODE_LOGIC_TYPE_FIXED_STRING_MATCH); + ASSERT_NE(nullptr, match_node); + + registry.deinitialize(); +} + +TEST_F(PluginModuleRegistryTest, NonExistingNodeNotRetrieved_n) { + registry.initialize(); + + bool ret = registry.load_node_prototypes(); + ASSERT_TRUE(ret); + + auto node = node_prototype_store->get_node_prototype(MMI_STANDARD_NODE_TYPE_PROCESSOR, MMI_STANDARD_NODE_PROCESSOR_TYPE_NONE); + ASSERT_EQ(nullptr, node); + + registry.deinitialize(); +} + +TEST_F(PluginModuleRegistryTest, DummyWorkflowInfoProperlyCreatedAndRetrieved_p) { + registry.initialize(); + + bool ret = registry.load_workflow_prototypes(); + ASSERT_TRUE(ret); + + auto workflow = workflow_prototype_store->get_workflow_prototype(MMI_STANDARD_WORKFLOW_WAKEUPLESS_COMMAND); + ASSERT_NE(nullptr, workflow); + + registry.deinitialize(); +} + +TEST_F(PluginModuleRegistryTest, NonExistingWorkflowNotRetrieved_n) { + registry.initialize(); + + bool ret = registry.load_workflow_prototypes(); + ASSERT_TRUE(ret); + + auto workflow = workflow_prototype_store->get_workflow_prototype(MMI_STANDARD_WORKFLOW_VOICE_TOUCH); + ASSERT_EQ(nullptr, workflow); + + registry.deinitialize(); +} + +} // namespace + +#ifdef TEST_MAIN_REQUIRED +int main(int argc, char** argv) { + std::cout << "Starting tests" << std::endl; + testing::InitGoogleTest(&argc, argv); + int ret = RUN_ALL_TESTS(); + return ret; +} +#endif diff --git a/tests/mmi-manager/port-instance-manager/mmi-port-instance-manager-tests.cpp b/tests/mmi-manager/port-instance-manager/mmi-port-instance-manager-tests.cpp new file mode 100644 index 0000000..3cc5d6c --- /dev/null +++ b/tests/mmi-manager/port-instance-manager/mmi-port-instance-manager-tests.cpp @@ -0,0 +1,53 @@ +#include +#include +#include + +#include "mmi-port-instance-manager.h" + +using namespace mmi; + +extern "C" { + /* Add wrapper functions here with __wrap_ and __real prefix + Example : + + int __real_vconf_get_int(const char *in_key, int *intval); + int __wrap_vconf_get_int(const char *in_key, int *intval) + { + if (strcmp(in_key, "TestKey") == 0) + return 0; + + return __real_vconf_get_int(in_key, intval); + } + */ +} + +namespace { + +class PortInstanceManagerTest : public testing::Test { +public: + PortInstanceManagerTest() { + } + virtual ~PortInstanceManagerTest() { + } + void SetUp() override { + } + void TearDown() override { + } + + PortInstanceManager manager; +}; + +TEST_F(PortInstanceManagerTest, func1) { + ASSERT_TRUE(true); +} + +} // namespace + +#ifdef TEST_MAIN_REQUIRED +int main(int argc, char** argv) { + std::cout << "Starting tests" << std::endl; + testing::InitGoogleTest(&argc, argv); + int ret = RUN_ALL_TESTS(); + return ret; +} +#endif diff --git a/tests/mmi-manager/workflow-instance-manager/mmi-workflow-instance-manager-tests.cpp b/tests/mmi-manager/workflow-instance-manager/mmi-workflow-instance-manager-tests.cpp new file mode 100644 index 0000000..957c537 --- /dev/null +++ b/tests/mmi-manager/workflow-instance-manager/mmi-workflow-instance-manager-tests.cpp @@ -0,0 +1,451 @@ +#include +#include +#include + +#include "mmi-self-container.h" +#include "mmi-workflow-instance-manager.h" + +using namespace mmi; +using namespace mmi::communication; + +extern "C" { + /* Add wrapper functions here with __wrap_ and __real prefix + Example : + + int __real_vconf_get_int(const char *in_key, int *intval); + int __wrap_vconf_get_int(const char *in_key, int *intval) + { + if (strcmp(in_key, "TestKey") == 0) + return 0; + + return __real_vconf_get_int(in_key, intval); + } + */ +} + +namespace { + +class CommunicationChannelDummy : public CommunicationChannel { +public: + int connect() override { + return MMI_ERROR_NONE; + } + int disconnect() override { + return MMI_ERROR_NONE; + } + int send(communication::Message *message) override { + return MMI_ERROR_NONE; + } + +}; + +class NodePrototypeStoreDummy : public INodePrototypeStore { +public: + virtual bool add_node_prototype(std::shared_ptr prototype) override { + node_prototypes.push_back(prototype); + return true; + } + virtual std::shared_ptr get_node_prototype( + mmi_standard_node_type_e type, mmi_standard_node_sub_type_e sub_type) override { + for (auto& prototype : node_prototypes) { + if (prototype->get_type() == type && + prototype->get_sub_type() == sub_type) { + return prototype; + } + } + return nullptr; + } + std::vector> node_prototypes; +}; + +class NodeInstanceManagerDummy : public INodeInstanceManager { +public: + NodeInstanceManagerDummy() { + node_prototype_store = std::make_shared(); + + auto mic_prototype = std::make_shared(); + mic_prototype->set_type(MMI_STANDARD_NODE_TYPE_SOURCE); + mic_prototype->set_sub_type(MMI_STANDARD_NODE_SOURCE_TYPE_MIC_AMBIENT); + PortInfo mic_audio{"AUDIO", MMI_PORT_TYPE_OUT, MMI_DATA_TYPE_AUDIO, "audio data"}; + mic_prototype->add_port_info(mic_audio); + node_prototype_store->add_node_prototype(mic_prototype); + + auto asr_prototype = std::make_shared(); + asr_prototype->set_type(MMI_STANDARD_NODE_TYPE_PROCESSOR); + asr_prototype->set_sub_type(MMI_STANDARD_NODE_PROCESSOR_TYPE_ASR); + PortInfo asr_audio{"AUDIO", MMI_PORT_TYPE_IN, MMI_DATA_TYPE_AUDIO, "audio data"}; + asr_prototype->add_port_info(asr_audio); + node_prototype_store->add_node_prototype(asr_prototype); + } + + virtual std::shared_ptr create_node_instance( + mmi_standard_node_type_e node_type, + mmi_standard_node_sub_type_e node_sub_type) override { + create_requests.push_back(std::make_pair(node_type, node_sub_type)); + auto node_instance = std::make_shared(node_type, node_sub_type); + if (node_instance) { + node_instance->set_node_prototype_store(node_prototype_store); + node_instance->set_plugin_module_proxy_provider(plugin_module_proxy_provider); + node_instance->initialize(); + } + return node_instance; + } + virtual bool destroy_node_instance( + std::shared_ptr node_instance) override { + destroy_requests++; + return true; + } + virtual bool link_node_instances( + std::shared_ptr from_node_instance, + std::shared_ptr to_node_instance, + std::optional from_port_name, + std::optional to_port_name) override { + if (from_node_instance && to_node_instance) { + std::shared_ptr from_prototype = + node_prototype_store->get_node_prototype( + from_node_instance->get_node_type(), from_node_instance->get_node_sub_type()); + std::shared_ptr to_prototype = + node_prototype_store->get_node_prototype( + to_node_instance->get_node_type(), to_node_instance->get_node_sub_type()); + if (from_prototype->has_port_with_name(from_port_name.value()) && + to_prototype->has_port_with_name(to_port_name.value())) { + valid_link_requests++; + } + } + return true; + } + void set_plugin_module_proxy_provider(std::shared_ptr provider) { + plugin_module_proxy_provider = std::move(provider); + } + std::shared_ptr node_prototype_store; + std::shared_ptr plugin_module_proxy_provider; + std::vector> create_requests; + size_t destroy_requests{0}; + size_t valid_link_requests{0}; +}; + +class WorkflowPrototypeStoreDummy : public IWorkflowPrototypeStore { +public: + bool add_workflow_prototype(std::shared_ptr prototype) override { + last_added_workflow_prototype = prototype; + return true; + } + std::shared_ptr get_workflow_prototype(mmi_standard_workflow_type_e type) override { + last_requested_workflow_type = type; + return last_added_workflow_prototype; + } + mmi_standard_workflow_type_e last_requested_workflow_type; + std::shared_ptr last_added_workflow_prototype; +}; +class PluginModuleProxyProviderDummy : public IPluginModuleProxyProvider { +public: + PluginModuleProxyProviderDummy() { + proxy = factory.create( + PluginModuleInfo{mmi_plugin_module_type_e::SELF_CONTAINED, "dummy"}); + } + virtual std::shared_ptr get_plugin_module_proxy( + const PluginModuleInfo plugin_module_info) override { + return proxy; + } + PluginModuleProxyFactorySelfContainerTest factory; + std::shared_ptr proxy; +}; + +/* Derived class to access protected members */ +class WorkflowInstanceManager_Derived : public WorkflowInstanceManager { +public: + std::vector> get_workflow_instances() { + return m_workflow_instances; + } +}; + +class WorkflowInstanceManagerTest : public testing::Test { +public: + WorkflowInstanceManagerTest() { + plugin_module_proxy_provider = std::make_shared(); + + node_instance_manager = std::make_shared(); + node_instance_manager->set_plugin_module_proxy_provider(plugin_module_proxy_provider); + + workflow_prototype_store = std::make_shared(); + + workflow_instance_manager = std::make_shared(); + if (workflow_instance_manager) { + workflow_instance_manager->set_workflow_prototype_store(workflow_prototype_store); + workflow_instance_manager->set_plugin_module_proxy_provider(plugin_module_proxy_provider); + workflow_instance_manager->set_node_instance_manager(node_instance_manager); + } + } + virtual ~WorkflowInstanceManagerTest() { + } + void SetUp() override { + workflow_instance_manager->initialize(); + channel.add_observer(workflow_instance_manager.get()); + } + void TearDown() override { + workflow_instance_manager->deinitialize(); + channel.remove_observer(workflow_instance_manager.get()); + } + + CommunicationChannelDummy channel; + + std::shared_ptr node_instance_manager; + + std::shared_ptr workflow_prototype_store; + std::shared_ptr plugin_module_proxy_provider; + + std::shared_ptr workflow_instance_manager; +}; + +TEST_F(WorkflowInstanceManagerTest, WorkflowInstanceCreatedProperly_p) { + ClientMessageWorkflowInstanceCreate msg; + msg.sender = std::string{"client-1"}; + msg.local_workflow_instance_id = 0; + msg.workflow_type = MMI_STANDARD_WORKFLOW_WAKEUPLESS_COMMAND; + + channel.notify_observers( + COMMUNICATION_CHANNEL_EVENT_TYPE::MESSAGE_RECEIVED, static_cast(&msg)); + + std::shared_ptr instance = workflow_instance_manager->get_workflow_instance( + msg.sender, msg.local_workflow_instance_id); + ASSERT_NE(instance, nullptr); +} + +TEST_F(WorkflowInstanceManagerTest, WorkflowInstanceNotCreatedProperlyWhenDuplicated_n) { + ClientMessageWorkflowInstanceCreate msg; + msg.sender = std::string{"client-1"}; + msg.local_workflow_instance_id = 0; + msg.workflow_type = MMI_STANDARD_WORKFLOW_WAKEUPLESS_COMMAND; + + channel.notify_observers( + COMMUNICATION_CHANNEL_EVENT_TYPE::MESSAGE_RECEIVED, static_cast(&msg)); + + std::shared_ptr instance = workflow_instance_manager->get_workflow_instance( + msg.sender, msg.local_workflow_instance_id); + ASSERT_NE(instance, nullptr); + ASSERT_EQ(workflow_instance_manager->get_workflow_instances().size(), 1); + + channel.notify_observers( + COMMUNICATION_CHANNEL_EVENT_TYPE::MESSAGE_RECEIVED, static_cast(&msg)); + + ASSERT_EQ(workflow_instance_manager->get_workflow_instances().size(), 1); +} + +TEST_F(WorkflowInstanceManagerTest, ProperWorkflowTypeRequestedForInstanceCreation_p) { + workflow_prototype_store->last_requested_workflow_type = MMI_STANDARD_WORKFLOW_NONE; + + ClientMessageWorkflowInstanceCreate msg; + msg.sender = std::string{"client-1"}; + msg.local_workflow_instance_id = 0; + msg.workflow_type = MMI_STANDARD_WORKFLOW_VOICE_TOUCH; + + channel.notify_observers( + COMMUNICATION_CHANNEL_EVENT_TYPE::MESSAGE_RECEIVED, static_cast(&msg)); + + std::shared_ptr instance = workflow_instance_manager->get_workflow_instance( + msg.sender, msg.local_workflow_instance_id); + ASSERT_NE(instance, nullptr); + + ASSERT_EQ(workflow_prototype_store->last_requested_workflow_type, MMI_STANDARD_WORKFLOW_VOICE_TOUCH); +} + +TEST_F(WorkflowInstanceManagerTest, AllWorkflowInstanceDeletedOnDeinitialize_p) { + ClientMessageWorkflowInstanceCreate msg; + msg.sender = std::string{"client-1"}; + msg.local_workflow_instance_id = 0; + msg.workflow_type = MMI_STANDARD_WORKFLOW_WAKEUPLESS_COMMAND; + + channel.notify_observers( + COMMUNICATION_CHANNEL_EVENT_TYPE::MESSAGE_RECEIVED, static_cast(&msg)); + + ASSERT_EQ(workflow_instance_manager->get_workflow_instances().size(), 1); + + workflow_instance_manager->deinitialize(); + ASSERT_EQ(workflow_instance_manager->get_workflow_instances().size(), 0); +} + +TEST_F(WorkflowInstanceManagerTest, NodeInstanceCreationRequestedForWorkflowInstanceCreation_p) { + ASSERT_EQ(node_instance_manager->create_requests.size(), 0); + + auto prototype = std::make_shared(); + prototype->set_type(MMI_STANDARD_WORKFLOW_WAKEUPLESS_COMMAND); + + /* Add 2 nodes to the workflow prototype */ + prototype->add_node_info(mmi::NodeInfo{ + "MIC", MMI_STANDARD_NODE_TYPE_SOURCE, MMI_STANDARD_NODE_SOURCE_TYPE_MIC_AMBIENT}); + prototype->add_node_info(mmi::NodeInfo{ + "ASR", MMI_STANDARD_NODE_TYPE_PROCESSOR, MMI_STANDARD_NODE_PROCESSOR_TYPE_ASR}); + workflow_prototype_store->add_workflow_prototype(prototype); + + ClientMessageWorkflowInstanceCreate msg; + msg.sender = std::string{"client-1"}; + msg.local_workflow_instance_id = 0; + msg.workflow_type = MMI_STANDARD_WORKFLOW_WAKEUPLESS_COMMAND; + + channel.notify_observers( + COMMUNICATION_CHANNEL_EVENT_TYPE::MESSAGE_RECEIVED, static_cast(&msg)); + + ASSERT_EQ(node_instance_manager->create_requests.size(), 2); +} + +TEST_F(WorkflowInstanceManagerTest, NodeInstanceDestroyRequestedOnWorkflowDestruction_p) { + auto prototype = std::make_shared(); + prototype->set_type(MMI_STANDARD_WORKFLOW_WAKEUPLESS_COMMAND); + + /* Add 2 nodes to the workflow prototype */ + prototype->add_node_info(mmi::NodeInfo{ + "MIC", MMI_STANDARD_NODE_TYPE_SOURCE, MMI_STANDARD_NODE_SOURCE_TYPE_MIC_AMBIENT}); + prototype->add_node_info(mmi::NodeInfo{ + "ASR", MMI_STANDARD_NODE_TYPE_PROCESSOR, MMI_STANDARD_NODE_PROCESSOR_TYPE_ASR}); + workflow_prototype_store->add_workflow_prototype(prototype); + + ClientMessageWorkflowInstanceCreate create_msg; + create_msg.sender = std::string{"client-1"}; + create_msg.local_workflow_instance_id = 0; + create_msg.workflow_type = MMI_STANDARD_WORKFLOW_WAKEUPLESS_COMMAND; + + channel.notify_observers( + COMMUNICATION_CHANNEL_EVENT_TYPE::MESSAGE_RECEIVED, static_cast(&create_msg)); + + ASSERT_EQ(node_instance_manager->destroy_requests, 0); + + ClientMessageWorkflowInstanceDestroy destroy_msg; + destroy_msg.sender = std::string{"client-1"}; + destroy_msg.local_workflow_instance_id = 0; + + channel.notify_observers( + COMMUNICATION_CHANNEL_EVENT_TYPE::MESSAGE_RECEIVED, static_cast(&destroy_msg)); + ASSERT_EQ(node_instance_manager->destroy_requests, 2); +} + +TEST_F(WorkflowInstanceManagerTest, NodeInstanceDestroyRequestedOnClientDisconnection_p) { + auto prototype = std::make_shared(); + prototype->set_type(MMI_STANDARD_WORKFLOW_WAKEUPLESS_COMMAND); + + /* Add 2 nodes to the workflow prototype */ + prototype->add_node_info(mmi::NodeInfo{ + "MIC", MMI_STANDARD_NODE_TYPE_SOURCE, MMI_STANDARD_NODE_SOURCE_TYPE_MIC_AMBIENT}); + prototype->add_node_info(mmi::NodeInfo{ + "ASR", MMI_STANDARD_NODE_TYPE_PROCESSOR, MMI_STANDARD_NODE_PROCESSOR_TYPE_ASR}); + workflow_prototype_store->add_workflow_prototype(prototype); + + ClientMessageWorkflowInstanceCreate create_msg; + create_msg.sender = std::string{"client-1"}; + create_msg.local_workflow_instance_id = 0; + create_msg.workflow_type = MMI_STANDARD_WORKFLOW_WAKEUPLESS_COMMAND; + + channel.notify_observers( + COMMUNICATION_CHANNEL_EVENT_TYPE::MESSAGE_RECEIVED, static_cast(&create_msg)); + + ASSERT_EQ(node_instance_manager->destroy_requests, 0); + + channel.notify_observers( + COMMUNICATION_CHANNEL_EVENT_TYPE::DISCONNECTED, std::string{"client-1"}); + + ASSERT_EQ(node_instance_manager->destroy_requests, 2); +} + +TEST_F(WorkflowInstanceManagerTest, LinkCreatedIfLinkInfoIsProvided_p) { + auto prototype = std::make_shared(); + prototype->set_type(MMI_STANDARD_WORKFLOW_WAKEUPLESS_COMMAND); + + /* Add 2 nodes to the workflow prototype */ + prototype->add_node_info(mmi::NodeInfo{ + "MIC", MMI_STANDARD_NODE_TYPE_SOURCE, MMI_STANDARD_NODE_SOURCE_TYPE_MIC_AMBIENT}); + prototype->add_node_info(mmi::NodeInfo{ + "ASR", MMI_STANDARD_NODE_TYPE_PROCESSOR, MMI_STANDARD_NODE_PROCESSOR_TYPE_ASR}); + prototype->add_link_info(mmi::LinkInfo{ + "MIC", "AUDIO", "ASR", "AUDIO"}); + workflow_prototype_store->add_workflow_prototype(prototype); + + ClientMessageWorkflowInstanceCreate create_msg; + create_msg.sender = std::string{"client-1"}; + create_msg.local_workflow_instance_id = 0; + create_msg.workflow_type = MMI_STANDARD_WORKFLOW_WAKEUPLESS_COMMAND; + + channel.notify_observers( + COMMUNICATION_CHANNEL_EVENT_TYPE::MESSAGE_RECEIVED, static_cast(&create_msg)); + + ASSERT_NE(node_instance_manager->valid_link_requests, 0); +} + +TEST_F(WorkflowInstanceManagerTest, LinkNotCreatedIfLinkInfoIsNotProvided_n) { + auto prototype = std::make_shared(); + prototype->set_type(MMI_STANDARD_WORKFLOW_WAKEUPLESS_COMMAND); + + /* Add 2 nodes to the workflow prototype */ + prototype->add_node_info(mmi::NodeInfo{ + "MIC", MMI_STANDARD_NODE_TYPE_SOURCE, MMI_STANDARD_NODE_SOURCE_TYPE_MIC_AMBIENT}); + prototype->add_node_info(mmi::NodeInfo{ + "ASR", MMI_STANDARD_NODE_TYPE_PROCESSOR, MMI_STANDARD_NODE_PROCESSOR_TYPE_ASR}); + workflow_prototype_store->add_workflow_prototype(prototype); + + ClientMessageWorkflowInstanceCreate create_msg; + create_msg.sender = std::string{"client-1"}; + create_msg.local_workflow_instance_id = 0; + create_msg.workflow_type = MMI_STANDARD_WORKFLOW_WAKEUPLESS_COMMAND; + + channel.notify_observers( + COMMUNICATION_CHANNEL_EVENT_TYPE::MESSAGE_RECEIVED, static_cast(&create_msg)); + + ASSERT_EQ(node_instance_manager->valid_link_requests, 0); +} + +TEST_F(WorkflowInstanceManagerTest, LinkNotCreatedIfPortNameIsInvalid_n) { + auto prototype = std::make_shared(); + prototype->set_type(MMI_STANDARD_WORKFLOW_WAKEUPLESS_COMMAND); + + /* Add 2 nodes to the workflow prototype */ + prototype->add_node_info(mmi::NodeInfo{ + "MIC", MMI_STANDARD_NODE_TYPE_SOURCE, MMI_STANDARD_NODE_SOURCE_TYPE_MIC_AMBIENT}); + prototype->add_node_info(mmi::NodeInfo{ + "ASR", MMI_STANDARD_NODE_TYPE_PROCESSOR, MMI_STANDARD_NODE_PROCESSOR_TYPE_ASR}); + prototype->add_link_info(mmi::LinkInfo{ + "MIC", "UNKNOWN_PORT", "ASR", "AUDIO"}); + workflow_prototype_store->add_workflow_prototype(prototype); + + ClientMessageWorkflowInstanceCreate create_msg; + create_msg.sender = std::string{"client-1"}; + create_msg.local_workflow_instance_id = 0; + create_msg.workflow_type = MMI_STANDARD_WORKFLOW_WAKEUPLESS_COMMAND; + + channel.notify_observers( + COMMUNICATION_CHANNEL_EVENT_TYPE::MESSAGE_RECEIVED, static_cast(&create_msg)); + + ASSERT_EQ(node_instance_manager->valid_link_requests, 0); +} + +TEST_F(WorkflowInstanceManagerTest, LinkNotCreatedIfNodeNameIsInvalid_n) { + auto prototype = std::make_shared(); + prototype->set_type(MMI_STANDARD_WORKFLOW_WAKEUPLESS_COMMAND); + + /* Add 2 nodes to the workflow prototype */ + prototype->add_node_info(mmi::NodeInfo{ + "MIC", MMI_STANDARD_NODE_TYPE_SOURCE, MMI_STANDARD_NODE_SOURCE_TYPE_MIC_AMBIENT}); + prototype->add_node_info(mmi::NodeInfo{ + "ASR", MMI_STANDARD_NODE_TYPE_PROCESSOR, MMI_STANDARD_NODE_PROCESSOR_TYPE_ASR}); + prototype->add_link_info(mmi::LinkInfo{ + "UNKNOWN_NODE", "AUDIO", "ASR", "AUDIO"}); + workflow_prototype_store->add_workflow_prototype(prototype); + + ClientMessageWorkflowInstanceCreate create_msg; + create_msg.sender = std::string{"client-1"}; + create_msg.local_workflow_instance_id = 0; + create_msg.workflow_type = MMI_STANDARD_WORKFLOW_WAKEUPLESS_COMMAND; + + channel.notify_observers( + COMMUNICATION_CHANNEL_EVENT_TYPE::MESSAGE_RECEIVED, static_cast(&create_msg)); + + ASSERT_EQ(node_instance_manager->valid_link_requests, 0); +} + +} // namespace + +#ifdef TEST_MAIN_REQUIRED +int main(int argc, char** argv) { + std::cout << "Starting tests" << std::endl; + testing::InitGoogleTest(&argc, argv); + int ret = RUN_ALL_TESTS(); + return ret; +} +#endif diff --git a/tests/mmi-manager/workflow-instance/mmi-workflow-instance-tests.cpp b/tests/mmi-manager/workflow-instance/mmi-workflow-instance-tests.cpp new file mode 100644 index 0000000..96d13b8 --- /dev/null +++ b/tests/mmi-manager/workflow-instance/mmi-workflow-instance-tests.cpp @@ -0,0 +1,184 @@ +#include +#include +#include + +#include "mmi-self-container.h" +#include "mmi-workflow-instance.h" +#include "mmi-workflow-prototype.h" +#include "mmi-workflow-prototype-manager.h" + +using namespace mmi; + +extern "C" { +/* Add wrapper functions here with __wrap_ and __real prefix +Example : + +int __real_vconf_get_int(const char *in_key, int *intval); +int __wrap_vconf_get_int(const char *in_key, int *intval) +{ + if (strcmp(in_key, "TestKey") == 0) + return 0; + + return __real_vconf_get_int(in_key, intval); +} +*/ +} + +class WorkflowPrototypeStoreDummy : public IWorkflowPrototypeStore { +public: + bool add_workflow_prototype(std::shared_ptr prototype) override { + return true; + } + std::shared_ptr get_workflow_prototype(mmi_standard_workflow_type_e type) override { + auto prototype = std::make_shared(); + prototype->set_type(MMI_STANDARD_WORKFLOW_WAKEUPLESS_COMMAND); + + /* Add 2 nodes to the workflow prototype */ + prototype->add_node_info(mmi::NodeInfo{ + "MIC", MMI_STANDARD_NODE_TYPE_SOURCE, MMI_STANDARD_NODE_SOURCE_TYPE_MIC_AMBIENT}); + prototype->add_node_info(mmi::NodeInfo{ + "ASR", MMI_STANDARD_NODE_TYPE_PROCESSOR, MMI_STANDARD_NODE_PROCESSOR_TYPE_ASR}); + AttributeAssignmentInfo attribute_assignment_info; + attribute_assignment_info.attribute_name = "CANDIDATE"; + attribute_assignment_info.target_node_name = "ASR"; + attribute_assignment_info.target_attribute_name = "CANDIDATE"; + prototype->add_attribute_assignment_info(attribute_assignment_info); + return prototype; + } +}; + +class NodePrototypeStoreDummy : public INodePrototypeStore { +public: + virtual bool add_node_prototype(std::shared_ptr prototype) override { + node_prototypes.push_back(prototype); + return true; + } + virtual std::shared_ptr get_node_prototype( + mmi_standard_node_type_e type, mmi_standard_node_sub_type_e sub_type) override { + for (auto& prototype : node_prototypes) { + if (prototype->get_type() == type && + prototype->get_sub_type() == sub_type) { + return prototype; + } + } + return nullptr; + } + std::vector> node_prototypes; +}; + +class NodeInstanceManagerDummy : public INodeInstanceManager { +public: + NodeInstanceManagerDummy() { + node_prototype_store = std::make_shared(); + + auto mic_prototype = std::make_shared(); + mic_prototype->set_type(MMI_STANDARD_NODE_TYPE_SOURCE); + mic_prototype->set_sub_type(MMI_STANDARD_NODE_SOURCE_TYPE_MIC_AMBIENT); + PortInfo mic_audio{"AUDIO", MMI_PORT_TYPE_OUT, MMI_DATA_TYPE_AUDIO, "audio data"}; + mic_prototype->add_port_info(mic_audio); + node_prototype_store->add_node_prototype(mic_prototype); + + auto asr_prototype = std::make_shared(); + asr_prototype->set_type(MMI_STANDARD_NODE_TYPE_PROCESSOR); + asr_prototype->set_sub_type(MMI_STANDARD_NODE_PROCESSOR_TYPE_ASR); + PortInfo asr_audio{"AUDIO", MMI_PORT_TYPE_IN, MMI_DATA_TYPE_AUDIO, "audio data"}; + asr_prototype->add_port_info(asr_audio); + node_prototype_store->add_node_prototype(asr_prototype); + } + + virtual std::shared_ptr create_node_instance( + mmi_standard_node_type_e node_type, + mmi_standard_node_sub_type_e node_sub_type) override { + auto node_instance = std::make_shared(node_type, node_sub_type); + if (node_instance) { + node_instance->set_node_prototype_store(node_prototype_store); + node_instance->set_plugin_module_proxy_provider(plugin_module_proxy_provider); + node_instance->initialize(); + } + return node_instance; + } + virtual bool destroy_node_instance( + std::shared_ptr node_instance) override { + return true; + } + virtual bool link_node_instances( + std::shared_ptr from_node_instance, + std::shared_ptr to_node_instance, + std::optional from_port_name, + std::optional to_port_name) override { + return true; + } + void set_plugin_module_proxy_provider(std::shared_ptr provider) { + plugin_module_proxy_provider = provider; + } + std::shared_ptr node_prototype_store; + std::shared_ptr plugin_module_proxy_provider; +}; + +class PluginModuleProxyProviderDummy : public IPluginModuleProxyProvider { +public: + PluginModuleProxyProviderDummy() { + proxy = factory.create( + PluginModuleInfo{mmi_plugin_module_type_e::SELF_CONTAINED, "dummy"}); + } + virtual std::shared_ptr get_plugin_module_proxy( + const PluginModuleInfo plugin_module_info) override { + return proxy; + } + PluginModuleProxyFactorySelfContainerTest factory; + std::shared_ptr proxy; +}; + +class WorkflowInstanceTest : public testing::Test +{ +public: + WorkflowInstanceTest() { + workflow_prototype_store = std::make_shared(); + node_instance_manager = std::make_shared(); + plugin_module_proxy_provider = std::make_shared(); + } + virtual ~WorkflowInstanceTest() { + } + void SetUp() override { + workflow_instance = std::make_shared(); + if (workflow_instance) { + workflow_instance->set_type(MMI_STANDARD_WORKFLOW_WAKEUPLESS_COMMAND); + + workflow_instance->set_workflow_prototype_store(workflow_prototype_store); + workflow_instance->set_plugin_module_proxy_provider(plugin_module_proxy_provider); + workflow_instance->set_node_instance_manager(node_instance_manager); + } + if (node_instance_manager) { + node_instance_manager->set_plugin_module_proxy_provider(plugin_module_proxy_provider); + } + } + void TearDown() override { + workflow_instance.reset(); + } + std::shared_ptr workflow_instance; + std::shared_ptr plugin_module_proxy_provider; + std::shared_ptr workflow_prototype_store; + std::shared_ptr node_instance_manager; +}; + +TEST_F(WorkflowInstanceTest, WorkflowAttributePassedToNodeAttribute_p) { + ASSERT_NE(workflow_instance, nullptr); + workflow_instance->initialize(); + mmi_primitive_value_h value = nullptr; + mmi_primitive_value_create_int(5, &value); + mmi_attribute_h attribute = nullptr; + mmi_attribute_create(value, "CANDIDATE", &attribute); + ASSERT_TRUE(workflow_instance->set_attribute(attribute)); + workflow_instance->deinitialize(); + mmi_attribute_destroy(attribute); + mmi_primitive_value_destroy(value); +} + +#ifdef TEST_MAIN_REQUIRED +int main(int argc, char** argv) { + std::cout << "Starting tests" << std::endl; + testing::InitGoogleTest(&argc, argv); + int ret = RUN_ALL_TESTS(); + return ret; +} +#endif diff --git a/tests/mmi-manager/workflow-prototype/mmi-workflow-prototype-tests.cpp b/tests/mmi-manager/workflow-prototype/mmi-workflow-prototype-tests.cpp new file mode 100644 index 0000000..89c1d2d --- /dev/null +++ b/tests/mmi-manager/workflow-prototype/mmi-workflow-prototype-tests.cpp @@ -0,0 +1,68 @@ +#include +#include +#include + +#include "mmi-workflow-prototype.h" + +using namespace mmi; + +extern "C" { + /* Add wrapper functions here with __wrap_ and __real prefix + Example : + + int __real_vconf_get_int(const char *in_key, int *intval); + int __wrap_vconf_get_int(const char *in_key, int *intval) + { + if (strcmp(in_key, "TestKey") == 0) + return 0; + + return __real_vconf_get_int(in_key, intval); + } + */ +} + +namespace { + +class WorkflowPrototypeTest : public testing::Test { +public: + WorkflowPrototypeTest() { + } + virtual ~WorkflowPrototypeTest() { + } + void SetUp() override { + prototype = std::make_shared(); + ASSERT_TRUE(prototype != nullptr); + } + void TearDown() override { + } + + std::shared_ptr prototype; +}; + +TEST_F(WorkflowPrototypeTest, WorkflowPrototypeValidationSucceeds_p) { + prototype->set_plugin_module_info( + PluginModuleInfo{mmi_plugin_module_type_e::SHARED_LIBRARY, "test.so"}); + + prototype->set_type(MMI_STANDARD_WORKFLOW_WAKEUPLESS_COMMAND); + ASSERT_TRUE(prototype->is_valid()); +} + +TEST_F(WorkflowPrototypeTest, WorkflowPrototypeValidationFailsWithNoType_n) { + prototype->set_plugin_module_info( + PluginModuleInfo{mmi_plugin_module_type_e::SHARED_LIBRARY, "test.so"}); + + /* No type provided */ + + ASSERT_FALSE(prototype->is_valid()); +} + +} // namespace + +#ifdef TEST_MAIN_REQUIRED +int main(int argc, char** argv) { + std::cout << "Starting tests" << std::endl; + testing::InitGoogleTest(&argc, argv); + int ret = RUN_ALL_TESTS(); + return ret; +} +#endif diff --git a/tests/mmi/attribute/mmi-attribute-test.cpp b/tests/mmi/attribute/mmi-attribute-test.cpp new file mode 100644 index 0000000..0f85233 --- /dev/null +++ b/tests/mmi/attribute/mmi-attribute-test.cpp @@ -0,0 +1,268 @@ +/* + * Copyright (c) 2023 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 MMIAttributeTest : public ::testing::Test { +public: + void SetUp(void) override { + mmiAttribute = nullptr; + cloned_attribute = nullptr; + primitiveValue = nullptr; + } + + void TearDown(void) override { + if (nullptr != mmiAttribute) { + mmi_attribute_destroy(mmiAttribute); + mmiAttribute = nullptr; + } + + if (nullptr != cloned_attribute) { + mmi_attribute_destroy(cloned_attribute); + cloned_attribute = nullptr; + } + + if (nullptr != primitiveValue) { + mmi_primitive_value_destroy(primitiveValue); + primitiveValue = nullptr; + } + } + + mmi_attribute_h mmiAttribute = nullptr; + mmi_attribute_h cloned_attribute = nullptr; + mmi_primitive_value_h primitiveValue = nullptr; +}; + +TEST_F(MMIAttributeTest, MMIAttributeCreate_p) { + constexpr int sourceInt = 0; + ASSERT_EQ(mmi_primitive_value_create_int(sourceInt, &primitiveValue), MMI_ERROR_NONE); + ASSERT_NE(primitiveValue, nullptr); + + constexpr const char *name = "Attribute"; + ASSERT_EQ(mmi_attribute_create(primitiveValue, name, &mmiAttribute), MMI_ERROR_NONE); + ASSERT_NE(mmiAttribute, nullptr); +} + +TEST_F(MMIAttributeTest, MMIAttributeCreate_n1) { + constexpr int sourceInt = 0; + ASSERT_EQ(mmi_primitive_value_create_int(sourceInt, &primitiveValue), MMI_ERROR_NONE); + ASSERT_NE(primitiveValue, nullptr); + + constexpr const char *name = "Attribute"; + EXPECT_EQ(mmi_attribute_create(nullptr, name, &mmiAttribute), MMI_ERROR_INVALID_PARAMETER); + EXPECT_EQ(mmi_attribute_create(primitiveValue, nullptr, &mmiAttribute), MMI_ERROR_INVALID_PARAMETER); + EXPECT_EQ(mmi_attribute_create(primitiveValue, name, nullptr), MMI_ERROR_INVALID_PARAMETER); +} + +TEST_F(MMIAttributeTest, MMIAttributeCreate_n2) { + constexpr int sourceInt = 0; + ASSERT_EQ(mmi_primitive_value_create_int(sourceInt, &primitiveValue), MMI_ERROR_NONE); + ASSERT_NE(primitiveValue, nullptr); + + EXPECT_EQ(mmi_attribute_create(primitiveValue, "", &mmiAttribute), MMI_ERROR_INVALID_PARAMETER); + + constexpr size_t NAME_LENGTH = 300; + char name[NAME_LENGTH]; + for (auto &c : name) { + c = 'a'; + } + name[NAME_LENGTH - 1] = 0; + EXPECT_EQ(mmi_attribute_create(primitiveValue, name, &mmiAttribute), MMI_ERROR_INVALID_PARAMETER); +} + +TEST_F(MMIAttributeTest, MMIAttributeGetName_p) { + constexpr int sourceInt = 0; + ASSERT_EQ(mmi_primitive_value_create_int(sourceInt, &primitiveValue), MMI_ERROR_NONE); + ASSERT_NE(primitiveValue, nullptr); + + constexpr const char *name = "Attribute"; + ASSERT_EQ(mmi_attribute_create(primitiveValue, name, &mmiAttribute), MMI_ERROR_NONE); + ASSERT_NE(mmiAttribute, nullptr); + + char *temp_name = nullptr; + ASSERT_EQ(mmi_attribute_get_name(mmiAttribute, &temp_name), MMI_ERROR_NONE); + ASSERT_NE(temp_name, nullptr); + EXPECT_STREQ(temp_name, name); + free(temp_name); +} + +TEST_F(MMIAttributeTest, MMIAttributeGetName_n) { + constexpr int sourceInt = 0; + ASSERT_EQ(mmi_primitive_value_create_int(sourceInt, &primitiveValue), MMI_ERROR_NONE); + ASSERT_NE(primitiveValue, nullptr); + + constexpr const char *name = "Attribute"; + ASSERT_EQ(mmi_attribute_create(primitiveValue, name, &mmiAttribute), MMI_ERROR_NONE); + ASSERT_NE(mmiAttribute, nullptr); + + char *temp_name = nullptr; + EXPECT_EQ(mmi_attribute_get_name(nullptr, &temp_name), MMI_ERROR_INVALID_PARAMETER); + EXPECT_EQ(mmi_attribute_get_name(mmiAttribute, nullptr), MMI_ERROR_INVALID_PARAMETER); +} + +TEST_F(MMIAttributeTest, MMIAttributeGetValue_p) { + constexpr int sourceInt = 0; + ASSERT_EQ(mmi_primitive_value_create_int(sourceInt, &primitiveValue), MMI_ERROR_NONE); + ASSERT_NE(primitiveValue, nullptr); + + constexpr const char *name = "Attribute"; + ASSERT_EQ(mmi_attribute_create(primitiveValue, name, &mmiAttribute), MMI_ERROR_NONE); + ASSERT_NE(mmiAttribute, nullptr); + + mmi_primitive_value_h temp_value = nullptr; + ASSERT_EQ(mmi_attribute_get_value(mmiAttribute, &temp_value), MMI_ERROR_NONE); + ASSERT_NE(temp_value, nullptr); + + mmi_primitive_value_type_e type = MMI_PRIMITIVE_VALUE_TYPE_ARRAY; + EXPECT_EQ(mmi_primitive_value_get_type(temp_value, &type), MMI_ERROR_NONE); + EXPECT_EQ(type, MMI_PRIMITIVE_VALUE_TYPE_INT); + + int value = -1; + EXPECT_EQ(mmi_primitive_value_get_int(temp_value, &value), MMI_ERROR_NONE); + EXPECT_EQ(value, sourceInt); + + EXPECT_EQ(mmi_primitive_value_destroy(temp_value), MMI_ERROR_NONE); +} + +TEST_F(MMIAttributeTest, MMIAttributeGetValue_n) { + constexpr int sourceInt = 0; + ASSERT_EQ(mmi_primitive_value_create_int(sourceInt, &primitiveValue), MMI_ERROR_NONE); + ASSERT_NE(primitiveValue, nullptr); + + constexpr const char *name = "Attribute"; + ASSERT_EQ(mmi_attribute_create(primitiveValue, name, &mmiAttribute), MMI_ERROR_NONE); + ASSERT_NE(mmiAttribute, nullptr); + + mmi_primitive_value_h temp_value = nullptr; + EXPECT_EQ(mmi_attribute_get_value(nullptr, &temp_value), MMI_ERROR_INVALID_PARAMETER); + EXPECT_EQ(mmi_attribute_get_value(mmiAttribute, nullptr), MMI_ERROR_INVALID_PARAMETER); +} + +TEST_F(MMIAttributeTest, MMIAttributeClone_p) { + constexpr int sourceInt = 0; + ASSERT_EQ(mmi_primitive_value_create_int(sourceInt, &primitiveValue), MMI_ERROR_NONE); + ASSERT_NE(primitiveValue, nullptr); + + constexpr const char *name = "Attribute"; + ASSERT_EQ(mmi_attribute_create(primitiveValue, name, &mmiAttribute), MMI_ERROR_NONE); + ASSERT_NE(mmiAttribute, nullptr); + + ASSERT_EQ(mmi_attribute_clone(mmiAttribute, &cloned_attribute), MMI_ERROR_NONE); + ASSERT_NE(cloned_attribute, nullptr); + + char *temp_name = nullptr; + ASSERT_EQ(mmi_attribute_get_name(cloned_attribute, &temp_name), MMI_ERROR_NONE); + ASSERT_NE(temp_name, nullptr); + EXPECT_STREQ(temp_name, name); + free(temp_name); + + mmi_primitive_value_h temp_value = nullptr; + ASSERT_EQ(mmi_attribute_get_value(mmiAttribute, &temp_value), MMI_ERROR_NONE); + ASSERT_NE(temp_value, nullptr); + + mmi_primitive_value_type_e type = MMI_PRIMITIVE_VALUE_TYPE_ARRAY; + EXPECT_EQ(mmi_primitive_value_get_type(temp_value, &type), MMI_ERROR_NONE); + EXPECT_EQ(type, MMI_PRIMITIVE_VALUE_TYPE_INT); + + int value = -1; + EXPECT_EQ(mmi_primitive_value_get_int(temp_value, &value), MMI_ERROR_NONE); + EXPECT_EQ(value, sourceInt); + + EXPECT_EQ(mmi_primitive_value_destroy(temp_value), MMI_ERROR_NONE); +} + +TEST_F(MMIAttributeTest, MMIAttributeClone_n) { + constexpr int sourceInt = 0; + ASSERT_EQ(mmi_primitive_value_create_int(sourceInt, &primitiveValue), MMI_ERROR_NONE); + ASSERT_NE(primitiveValue, nullptr); + + constexpr const char *name = "Attribute"; + ASSERT_EQ(mmi_attribute_create(primitiveValue, name, &mmiAttribute), MMI_ERROR_NONE); + ASSERT_NE(mmiAttribute, nullptr); + + EXPECT_EQ(mmi_attribute_clone(nullptr, &cloned_attribute), MMI_ERROR_INVALID_PARAMETER); + EXPECT_EQ(mmi_attribute_clone(mmiAttribute, nullptr), MMI_ERROR_INVALID_PARAMETER); +} + +TEST_F(MMIAttributeTest, MMIDataDestroy_p1) { + constexpr int sourceInt = 0; + ASSERT_EQ(mmi_primitive_value_create_int(sourceInt, &primitiveValue), MMI_ERROR_NONE); + ASSERT_NE(primitiveValue, nullptr); + + constexpr const char *name = "Attribute"; + ASSERT_EQ(mmi_attribute_create(primitiveValue, name, &mmiAttribute), MMI_ERROR_NONE); + ASSERT_NE(mmiAttribute, nullptr); + + ASSERT_EQ(mmi_attribute_destroy(mmiAttribute), MMI_ERROR_NONE); + mmiAttribute = nullptr; +} + +TEST_F(MMIAttributeTest, MMIDataDestroy_n) { + ASSERT_EQ(mmi_attribute_destroy(nullptr), MMI_ERROR_INVALID_PARAMETER); +} + +TEST_F(MMIAttributeTest, MMIAttributeBytesConversion_p) { + constexpr int sourceInt = 5; + ASSERT_EQ(mmi_primitive_value_create_int(sourceInt, &primitiveValue), MMI_ERROR_NONE); + ASSERT_NE(primitiveValue, nullptr); + + constexpr const char *name = "Attribute"; + ASSERT_EQ(mmi_attribute_create(primitiveValue, name, &mmiAttribute), MMI_ERROR_NONE); + ASSERT_NE(mmiAttribute, nullptr); + + unsigned char *bytes = nullptr; + size_t size = 0; + ASSERT_EQ(mmi_attribute_to_bytes(mmiAttribute, &bytes, &size), MMI_ERROR_NONE); + ASSERT_NE(bytes, nullptr); + + ASSERT_EQ(mmi_attribute_destroy(mmiAttribute), MMI_ERROR_NONE); + mmiAttribute = nullptr; + + mmi_attribute_h restoredAttribute = nullptr; + ASSERT_EQ(mmi_attribute_from_bytes(bytes, size, &restoredAttribute), MMI_ERROR_NONE); + ASSERT_NE(restoredAttribute, nullptr); + + free(bytes); + + char *temp_name = nullptr; + ASSERT_EQ(mmi_attribute_get_name(restoredAttribute, &temp_name), MMI_ERROR_NONE); + ASSERT_NE(temp_name, nullptr); + EXPECT_STREQ(temp_name, name); + free(temp_name); + + mmi_primitive_value_h temp_value = nullptr; + ASSERT_EQ(mmi_attribute_get_value(restoredAttribute, &temp_value), MMI_ERROR_NONE); + ASSERT_NE(temp_value, nullptr); + + mmi_primitive_value_type_e type = MMI_PRIMITIVE_VALUE_TYPE_BOOL; + EXPECT_EQ(mmi_primitive_value_get_type(temp_value, &type), MMI_ERROR_NONE); + EXPECT_EQ(type, MMI_PRIMITIVE_VALUE_TYPE_INT); + + int value = -1; + EXPECT_EQ(mmi_primitive_value_get_int(temp_value, &value), MMI_ERROR_NONE); + EXPECT_EQ(value, sourceInt); + + mmi_primitive_value_destroy(temp_value); + temp_value = nullptr; + + mmi_attribute_destroy(restoredAttribute); + restoredAttribute = nullptr; +} diff --git a/tests/mmi/meson.build b/tests/mmi/meson.build new file mode 100644 index 0000000..4834a64 --- /dev/null +++ b/tests/mmi/meson.build @@ -0,0 +1,22 @@ +mmi_tests_srcs = [ + 'mmi-tests.cpp', + 'mmi-main-test.cpp', + 'mmi-ipc-test.cpp', + 'mmi-data-test.cpp', + 'attribute/mmi-attribute-test.cpp', + 'primitive-value/mmi-primitive-value-test.cpp', + 'workflow/mmi-workflow-test.cpp', + ] + +gmock_dep = dependency('gmock', method : 'pkg-config') +ecore_dep = dependency('ecore', method : 'pkg-config') + +tc = executable( + 'mmi-tests', + mmi_tests_srcs, + dependencies : [mmi_declared_dep, gmock_dep, ecore_dep], + install_dir : mmi_prefix_bindir, + install : true + ) + +test('mmi-tests', tc, args : ['--gtest_output=xml:./mmi-tests.xml']) diff --git a/tests/mmi/mmi-data-test.cpp b/tests/mmi/mmi-data-test.cpp new file mode 100644 index 0000000..393acb0 --- /dev/null +++ b/tests/mmi/mmi-data-test.cpp @@ -0,0 +1,524 @@ +/* + * Copyright (c) 2023 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 "mmi-tests.h" +#include +#include + + +class MMIDataTest : public ::testing::Test { +public: + void SetUp(void) override { + mmiData = nullptr; + for (size_t i = 0; i < MAX_ARRAY_LENGTH; i++) { + arrayElement[i] = nullptr; + } + } + + void TearDown(void) override { + if (nullptr != mmiData) { + mmi_data_destroy(mmiData); + mmiData = nullptr; + } + + for (size_t i = 0; i < MAX_ARRAY_LENGTH; i++) { + if (nullptr != arrayElement[i]) { + mmi_data_destroy(arrayElement[i]); + arrayElement[i] = nullptr; + } + } + } + + static constexpr size_t MAX_ARRAY_LENGTH = 5; + + mmi_data_h mmiData = nullptr; + mmi_data_h arrayElement[MAX_ARRAY_LENGTH] = {nullptr, }; +}; + +TEST_F(MMIDataTest, MMIDataCreateDataByInt_p) { + constexpr int sourceValue = 0; + ASSERT_EQ(mmi_data_create_int(sourceValue, &mmiData), MMI_ERROR_NONE); + ASSERT_NE(mmiData, nullptr); +} + +TEST_F(MMIDataTest, MMIDataCreateDataByInt_n) { + constexpr int sourceValue = 0; + EXPECT_EQ(mmi_data_create_int(sourceValue, nullptr), MMI_ERROR_INVALID_PARAMETER); +} + +TEST_F(MMIDataTest, MMIDataCreateDataByText_p) { + constexpr const char *sourceValue = "Hello"; + ASSERT_EQ(mmi_data_create_text(sourceValue, &mmiData), MMI_ERROR_NONE); + ASSERT_NE(mmiData, nullptr); +} + +TEST_F(MMIDataTest, MMIDataCreateDataByText_n) { + constexpr const char *sourceValue = "Hello"; + EXPECT_EQ(mmi_data_create_text(sourceValue, nullptr), MMI_ERROR_INVALID_PARAMETER); + EXPECT_EQ(mmi_data_create_text(nullptr, &mmiData), MMI_ERROR_INVALID_PARAMETER); +} + +TEST_F(MMIDataTest, MMIDataCreateDataByAudio_p) { + constexpr size_t DATA_LENGTH = 10; + constexpr char sourceValue[DATA_LENGTH] = {1, }; + ASSERT_EQ(mmi_data_create_audio(sourceValue, DATA_LENGTH, &mmiData), MMI_ERROR_NONE); + ASSERT_NE(mmiData, nullptr); +} + +TEST_F(MMIDataTest, MMIDataCreateDataByAudio_n) { + constexpr size_t DATA_LENGTH = 10; + constexpr char sourceValue[DATA_LENGTH] = {1, }; + EXPECT_EQ(mmi_data_create_audio(nullptr, DATA_LENGTH, &mmiData), MMI_ERROR_INVALID_PARAMETER); + EXPECT_EQ(mmi_data_create_audio(sourceValue, 0, &mmiData), MMI_ERROR_INVALID_PARAMETER); + EXPECT_EQ(mmi_data_create_audio(sourceValue, DATA_LENGTH, nullptr), MMI_ERROR_INVALID_PARAMETER); +} + +TEST_F(MMIDataTest, MMIDataCreateDataByArray_p) { + EXPECT_EQ(mmi_data_create_array(&mmiData), MMI_ERROR_NONE); + ASSERT_NE(mmiData, nullptr); +} + +TEST_F(MMIDataTest, MMIDataCreateDataByArray_n) { + EXPECT_EQ(mmi_data_create_array(nullptr), MMI_ERROR_INVALID_PARAMETER); +} + +TEST_F(MMIDataTest, MMIDataAddElementIntoArray_p) { + ASSERT_EQ(mmi_data_create_array(&mmiData), MMI_ERROR_NONE); + ASSERT_NE(mmiData, nullptr); + + constexpr int COUNT_ELEMENTS = 2; + constexpr int source[COUNT_ELEMENTS] = {1, 2}; + for (size_t i = 0; i < COUNT_ELEMENTS; i++) { + ASSERT_EQ(mmi_data_create_int(source[i], &arrayElement[i]), MMI_ERROR_NONE); + ASSERT_EQ(mmi_data_add_array_element(mmiData, arrayElement[i]), MMI_ERROR_NONE); + arrayElement[i] = nullptr; + } +} + +TEST_F(MMIDataTest, MMIDataAddElementIntoArray_n1) { + ASSERT_EQ(mmi_data_create_array(&mmiData), MMI_ERROR_NONE); + ASSERT_NE(mmiData, nullptr); + + constexpr int sourceInt = 1; + constexpr const char *sourceText = "Hello"; + ASSERT_EQ(mmi_data_create_int(sourceInt, &arrayElement[0]), MMI_ERROR_NONE); + ASSERT_EQ(mmi_data_create_text(sourceText, &arrayElement[1]), MMI_ERROR_NONE); + + EXPECT_EQ(mmi_data_add_array_element(nullptr, arrayElement[0]), MMI_ERROR_INVALID_PARAMETER); + EXPECT_EQ(mmi_data_add_array_element(mmiData, nullptr), MMI_ERROR_INVALID_PARAMETER); + + ASSERT_EQ(mmi_data_add_array_element(mmiData, arrayElement[0]), MMI_ERROR_NONE); + arrayElement[0] = nullptr; + EXPECT_EQ(mmi_data_add_array_element(mmiData, arrayElement[1]), MMI_ERROR_INVALID_PARAMETER); +} + +TEST_F(MMIDataTest, MMIDataAddElementIntoArray_n2) { + constexpr int sourceInt = 1; + EXPECT_EQ(mmi_data_create_int(sourceInt, &mmiData), MMI_ERROR_NONE); + ASSERT_NE(mmiData, nullptr); + + ASSERT_EQ(mmi_data_create_int(sourceInt, &arrayElement[0]), MMI_ERROR_NONE); + ASSERT_NE(arrayElement, nullptr); + + EXPECT_EQ(mmi_data_add_array_element(mmiData, arrayElement[0]), MMI_ERROR_INVALID_PARAMETER); +} + +TEST_F(MMIDataTest, MMIDataCreateDataByStruct_p) { + EXPECT_EQ(mmi_data_create_struct(&mmiData), MMI_ERROR_NONE); + ASSERT_NE(mmiData, nullptr); +} + +TEST_F(MMIDataTest, MMIDataCreateDataByStruct_n) { + EXPECT_EQ(mmi_data_create_struct(nullptr), MMI_ERROR_INVALID_PARAMETER); +} + +TEST_F(MMIDataTest, MMIDataAddElementIntoStruct_p) { + ASSERT_EQ(mmi_data_create_struct(&mmiData), MMI_ERROR_NONE); + ASSERT_NE(mmiData, nullptr); + + constexpr int sourceInt = 1; + constexpr const char *sourceText = "Hello"; + ASSERT_EQ(mmi_data_create_int(sourceInt, &arrayElement[0]), MMI_ERROR_NONE); + ASSERT_EQ(mmi_data_create_text(sourceText, &arrayElement[1]), MMI_ERROR_NONE); + + constexpr int COUNT_ELEMENTS = 2; + constexpr const char *name[COUNT_ELEMENTS] = {"Element1", "Element2"}; + for (size_t i = 0; i < COUNT_ELEMENTS; i++) { + ASSERT_EQ(mmi_data_set_struct_element(mmiData, name[i], arrayElement[i]), MMI_ERROR_NONE); + arrayElement[i] = nullptr; + } +} + +TEST_F(MMIDataTest, MMIDataAddElementIntoStruct_n1) { + ASSERT_EQ(mmi_data_create_array(&mmiData), MMI_ERROR_NONE); + ASSERT_NE(mmiData, nullptr); + + constexpr int sourceInt = 1; + ASSERT_EQ(mmi_data_create_int(sourceInt, &arrayElement[0]), MMI_ERROR_NONE); + + constexpr const char *name = "Element"; + ASSERT_EQ(mmi_data_set_struct_element(nullptr, name, arrayElement[0]), MMI_ERROR_INVALID_PARAMETER); + ASSERT_EQ(mmi_data_set_struct_element(mmiData, nullptr, arrayElement[0]), MMI_ERROR_INVALID_PARAMETER); + ASSERT_EQ(mmi_data_set_struct_element(mmiData, name, nullptr), MMI_ERROR_INVALID_PARAMETER); +} + +TEST_F(MMIDataTest, MMIDataGetElementFromStruct_p) { + ASSERT_EQ(mmi_data_create_struct(&mmiData), MMI_ERROR_NONE); + ASSERT_NE(mmiData, nullptr); + + constexpr int sourceInt = 1; + constexpr const char *sourceText = "Hello"; + ASSERT_EQ(mmi_data_create_int(sourceInt, &arrayElement[0]), MMI_ERROR_NONE); + ASSERT_EQ(mmi_data_create_text(sourceText, &arrayElement[1]), MMI_ERROR_NONE); + + constexpr int COUNT_ELEMENTS = 2; + constexpr const char *name[COUNT_ELEMENTS] = {"Element1", "Element2"}; + for (size_t i = 0; i < COUNT_ELEMENTS; i++) { + ASSERT_EQ(mmi_data_set_struct_element(mmiData, name[i], arrayElement[i]), MMI_ERROR_NONE); + arrayElement[i] = nullptr; + } + + size_t count = 0; + ASSERT_EQ(mmi_data_get_struct_count(mmiData, &count), MMI_ERROR_NONE); + ASSERT_EQ(count, COUNT_ELEMENTS); + + const char *element_name = nullptr; + mmi_data_h element_value = nullptr; + + EXPECT_EQ(mmi_data_get_struct_element_name(mmiData, 0, &element_name), MMI_ERROR_NONE); + EXPECT_STREQ(element_name, "Element1"); + EXPECT_EQ(mmi_data_get_struct_element_value(mmiData, 0, &element_value), MMI_ERROR_NONE); + int intValue = 0; + EXPECT_EQ(mmi_data_get_int(element_value, &intValue), MMI_ERROR_NONE); + EXPECT_EQ(intValue, sourceInt); + + EXPECT_EQ(mmi_data_get_struct_element_name(mmiData, 1, &element_name), MMI_ERROR_NONE); + EXPECT_STREQ(element_name, "Element2"); + EXPECT_EQ(mmi_data_get_struct_element_value(mmiData, 1, &element_value), MMI_ERROR_NONE); + const char *textValue = nullptr; + EXPECT_EQ(mmi_data_get_text(element_value, &textValue), MMI_ERROR_NONE); + EXPECT_STREQ(textValue, sourceText); +} + +TEST_F(MMIDataTest, MMIDataGetType_p) { + mmi_data_h tempValue = nullptr; + mmi_data_type_e type = MMI_DATA_TYPE_ARRAY; + + EXPECT_EQ(mmi_data_create_int(0, &tempValue), MMI_ERROR_NONE); + EXPECT_EQ(mmi_data_get_type(tempValue, &type), MMI_ERROR_NONE); + EXPECT_EQ(type, MMI_DATA_TYPE_INTEGER); + EXPECT_EQ(mmi_data_destroy(tempValue), MMI_ERROR_NONE); + tempValue = nullptr; + + EXPECT_EQ(mmi_data_create_text("Hello", &tempValue), MMI_ERROR_NONE); + EXPECT_EQ(mmi_data_get_type(tempValue, &type), MMI_ERROR_NONE); + EXPECT_EQ(type, MMI_DATA_TYPE_TEXT); + EXPECT_EQ(mmi_data_destroy(tempValue), MMI_ERROR_NONE); + tempValue = nullptr; + + constexpr size_t length = 10; + constexpr char sourceValue[length] = {1, }; + EXPECT_EQ(mmi_data_create_audio(sourceValue, length, &tempValue), MMI_ERROR_NONE); + EXPECT_EQ(mmi_data_get_type(tempValue, &type), MMI_ERROR_NONE); + EXPECT_EQ(type, MMI_DATA_TYPE_AUDIO); + EXPECT_EQ(mmi_data_destroy(tempValue), MMI_ERROR_NONE); + tempValue = nullptr; + + EXPECT_EQ(mmi_data_create_array(&tempValue), MMI_ERROR_NONE); + EXPECT_EQ(mmi_data_get_type(tempValue, &type), MMI_ERROR_NONE); + EXPECT_EQ(type, MMI_DATA_TYPE_ARRAY); + EXPECT_EQ(mmi_data_destroy(tempValue), MMI_ERROR_NONE); + tempValue = nullptr; + + EXPECT_EQ(mmi_data_create_struct(&tempValue), MMI_ERROR_NONE); + EXPECT_EQ(mmi_data_get_type(tempValue, &type), MMI_ERROR_NONE); + EXPECT_EQ(type, MMI_DATA_TYPE_STRUCT); + EXPECT_EQ(mmi_data_destroy(tempValue), MMI_ERROR_NONE); + tempValue = nullptr; +} + +TEST_F(MMIDataTest, MMIDataGetType_n) { + ASSERT_EQ(mmi_data_create_array(&mmiData), MMI_ERROR_NONE); + ASSERT_NE(mmiData, nullptr); + + mmi_data_type_e type = MMI_DATA_TYPE_ARRAY; + EXPECT_EQ(mmi_data_get_type(nullptr, &type), MMI_ERROR_INVALID_PARAMETER); + EXPECT_EQ(mmi_data_get_type(mmiData, nullptr), MMI_ERROR_INVALID_PARAMETER); +} + +TEST_F(MMIDataTest, MMIDataGetIntValue_p) { + constexpr int sourceInt = 1; + ASSERT_EQ(mmi_data_create_int(sourceInt, &mmiData), MMI_ERROR_NONE); + ASSERT_NE(mmiData, nullptr); + + int targetValue = -1; + EXPECT_EQ(mmi_data_get_int(mmiData, &targetValue), MMI_ERROR_NONE); + EXPECT_EQ(targetValue, sourceInt); +} + +TEST_F(MMIDataTest, MMIDataGetIntValue_n1) { + constexpr int sourceInt = 1; + ASSERT_EQ(mmi_data_create_int(sourceInt, &mmiData), MMI_ERROR_NONE); + ASSERT_NE(mmiData, nullptr); + + int targetValue = -1; + EXPECT_EQ(mmi_data_get_int(nullptr, &targetValue), MMI_ERROR_INVALID_PARAMETER); + EXPECT_EQ(mmi_data_get_int(mmiData, nullptr), MMI_ERROR_INVALID_PARAMETER); +} + +TEST_F(MMIDataTest, MMIDataGetIntValue_n2) { + constexpr const char *sourceText = "Hello"; + ASSERT_EQ(mmi_data_create_text(sourceText, &mmiData), MMI_ERROR_NONE); + ASSERT_NE(mmiData, nullptr); + + int targetValue = -1; + EXPECT_EQ(mmi_data_get_int(mmiData, &targetValue), MMI_ERROR_INVALID_PARAMETER); +} + +TEST_F(MMIDataTest, MMIDataGetTextValue_p) { + constexpr const char *sourceText = "Hello"; + ASSERT_EQ(mmi_data_create_text(sourceText, &mmiData), MMI_ERROR_NONE); + ASSERT_NE(mmiData, nullptr); + + const char *textInData = nullptr; + EXPECT_EQ(mmi_data_get_text(mmiData, &textInData), MMI_ERROR_NONE); + EXPECT_STREQ(textInData, sourceText); +} + +TEST_F(MMIDataTest, MMIDataGetTextValue_n1) { + constexpr const char *sourceText = "Hello"; + ASSERT_EQ(mmi_data_create_text(sourceText, &mmiData), MMI_ERROR_NONE); + ASSERT_NE(mmiData, nullptr); + + const char *textInData = nullptr; + EXPECT_EQ(mmi_data_get_text(nullptr, &textInData), MMI_ERROR_INVALID_PARAMETER); + EXPECT_EQ(mmi_data_get_text(mmiData, nullptr), MMI_ERROR_INVALID_PARAMETER); +} + +TEST_F(MMIDataTest, MMIDataGetTextValue_n2) { + constexpr int sourceInt = 1; + ASSERT_EQ(mmi_data_create_int(sourceInt, &mmiData), MMI_ERROR_NONE); + ASSERT_NE(mmiData, nullptr); + + const char *textInData = nullptr; + EXPECT_EQ(mmi_data_get_text(mmiData, &textInData), MMI_ERROR_INVALID_PARAMETER); +} + +TEST_F(MMIDataTest, MMIDataGetArrayCount_p) { + ASSERT_EQ(mmi_data_create_array(&mmiData), MMI_ERROR_NONE); + ASSERT_NE(mmiData, nullptr); + + constexpr int COUNT_ELEMENTS = 2; + constexpr int source[COUNT_ELEMENTS] = {1, 2}; + for (size_t i = 0; i < COUNT_ELEMENTS; i++) { + size_t count = 10; + EXPECT_EQ(mmi_data_get_array_count(mmiData, &count), MMI_ERROR_NONE); + EXPECT_EQ(count, i); + + ASSERT_EQ(mmi_data_create_int(source[i], &arrayElement[i]), MMI_ERROR_NONE); + ASSERT_EQ(mmi_data_add_array_element(mmiData, arrayElement[i]), MMI_ERROR_NONE); + arrayElement[i] = nullptr; + } + + size_t count = 10; + EXPECT_EQ(mmi_data_get_array_count(mmiData, &count), MMI_ERROR_NONE); + EXPECT_EQ(count, COUNT_ELEMENTS); +} + +TEST_F(MMIDataTest, MMIDataGetArrayCount_n1) { + ASSERT_EQ(mmi_data_create_array(&mmiData), MMI_ERROR_NONE); + ASSERT_NE(mmiData, nullptr); + + size_t count = 0; + EXPECT_EQ(mmi_data_get_array_count(nullptr, &count), MMI_ERROR_INVALID_PARAMETER); + EXPECT_EQ(mmi_data_get_array_count(mmiData, nullptr), MMI_ERROR_INVALID_PARAMETER); +} + +TEST_F(MMIDataTest, MMIDataGetArrayCount_n2) { + int sourceValue = 0; + ASSERT_EQ(mmi_data_create_int(sourceValue, &mmiData), MMI_ERROR_NONE); + ASSERT_NE(mmiData, nullptr); + + size_t count = 0; + EXPECT_EQ(mmi_data_get_array_count(mmiData, &count), MMI_ERROR_INVALID_PARAMETER); +} + +TEST_F(MMIDataTest, MMIDataGetArrayElement_p) { + ASSERT_EQ(mmi_data_create_array(&mmiData), MMI_ERROR_NONE); + ASSERT_NE(mmiData, nullptr); + + constexpr int COUNT_ELEMENTS = 2; + constexpr int source[COUNT_ELEMENTS] = {1, 2}; + for (size_t i = 0; i < COUNT_ELEMENTS; i++) { + ASSERT_EQ(mmi_data_create_int(source[i], &arrayElement[i]), MMI_ERROR_NONE); + ASSERT_EQ(mmi_data_add_array_element(mmiData, arrayElement[i]), MMI_ERROR_NONE); + arrayElement[i] = nullptr; + } + + size_t count = 0; + EXPECT_EQ(mmi_data_get_array_count(mmiData, &count), MMI_ERROR_NONE); + EXPECT_EQ(count, COUNT_ELEMENTS); + + for (size_t i = 0; i < count; i++) { + mmi_data_h element = nullptr; + EXPECT_EQ(mmi_data_get_array_element(mmiData, i, &element), MMI_ERROR_NONE); + + mmi_data_type_e type = MMI_DATA_TYPE_ANY; + EXPECT_EQ(mmi_data_get_type(element, &type), MMI_ERROR_NONE); + EXPECT_EQ(type, MMI_DATA_TYPE_INTEGER); + + int data = 0; + EXPECT_EQ(mmi_data_get_int(element, &data), MMI_ERROR_NONE); + EXPECT_EQ(data, source[i]); + } +} + +TEST_F(MMIDataTest, MMIDataGetArrayElement_n1) { + ASSERT_EQ(mmi_data_create_array(&mmiData), MMI_ERROR_NONE); + ASSERT_NE(mmiData, nullptr); + + mmi_data_h element = nullptr; + EXPECT_EQ(mmi_data_get_array_element(mmiData, 0, &element), MMI_ERROR_INVALID_PARAMETER); + + constexpr int COUNT_ELEMENTS = 2; + constexpr int source[COUNT_ELEMENTS] = {1, 2}; + for (size_t i = 0; i < COUNT_ELEMENTS; i++) { + ASSERT_EQ(mmi_data_create_int(source[i], &arrayElement[i]), MMI_ERROR_NONE); + ASSERT_EQ(mmi_data_add_array_element(mmiData, arrayElement[i]), MMI_ERROR_NONE); + arrayElement[i] = nullptr; + } + + size_t count = 0; + EXPECT_EQ(mmi_data_get_array_count(mmiData, &count), MMI_ERROR_NONE); + EXPECT_EQ(count, COUNT_ELEMENTS); + + EXPECT_EQ(mmi_data_get_array_element(nullptr, 0, &element), MMI_ERROR_INVALID_PARAMETER); + EXPECT_EQ(mmi_data_get_array_element(mmiData, count, &element), MMI_ERROR_INVALID_PARAMETER); + EXPECT_EQ(mmi_data_get_array_element(mmiData, 0, nullptr), MMI_ERROR_INVALID_PARAMETER); +} + +TEST_F(MMIDataTest, MMIDataGetArrayElement_n2) { + int sourceValue = 0; + ASSERT_EQ(mmi_data_create_int(sourceValue, &mmiData), MMI_ERROR_NONE); + ASSERT_NE(mmiData, nullptr); + + mmi_data_h element = nullptr; + EXPECT_EQ(mmi_data_get_array_element(mmiData, 0, &element), MMI_ERROR_INVALID_PARAMETER); +} + +TEST_F(MMIDataTest, MMIDataGetStructElement_p) { + ASSERT_EQ(mmi_data_create_struct(&mmiData), MMI_ERROR_NONE); + ASSERT_NE(mmiData, nullptr); + + constexpr int sourceInt = 1; + ASSERT_EQ(mmi_data_create_int(sourceInt, &arrayElement[0]), MMI_ERROR_NONE); + + constexpr const char *name = "Element"; + EXPECT_EQ(mmi_data_set_struct_element(mmiData, name, arrayElement[0]), MMI_ERROR_NONE); + arrayElement[0] = nullptr; + + mmi_data_h element = nullptr; + EXPECT_EQ(mmi_data_get_struct_element(mmiData, name, &element), MMI_ERROR_NONE); + EXPECT_NE(element, nullptr); +} + +TEST_F(MMIDataTest, MMIDataGetStructElement_n1) { + ASSERT_EQ(mmi_data_create_struct(&mmiData), MMI_ERROR_NONE); + ASSERT_NE(mmiData, nullptr); + + constexpr const char *name = "Element"; + mmi_data_h element = nullptr; + EXPECT_EQ(mmi_data_get_struct_element(mmiData, name, &element), MMI_ERROR_INVALID_PARAMETER); + + constexpr int sourceInt = 1; + ASSERT_EQ(mmi_data_create_int(sourceInt, &arrayElement[0]), MMI_ERROR_NONE); + EXPECT_EQ(mmi_data_set_struct_element(mmiData, name, arrayElement[0]), MMI_ERROR_NONE); + arrayElement[0] = nullptr; + + EXPECT_EQ(mmi_data_get_struct_element(nullptr, name, &element), MMI_ERROR_INVALID_PARAMETER); + EXPECT_EQ(mmi_data_get_struct_element(mmiData, nullptr, &element), MMI_ERROR_INVALID_PARAMETER); + EXPECT_EQ(mmi_data_get_struct_element(mmiData, name, nullptr), MMI_ERROR_INVALID_PARAMETER); +} + +TEST_F(MMIDataTest, MMIDataGetStructElement_n2) { + int sourceValue = 0; + ASSERT_EQ(mmi_data_create_int(sourceValue, &mmiData), MMI_ERROR_NONE); + ASSERT_NE(mmiData, nullptr); + + constexpr const char *name = "Element"; + mmi_data_h element = nullptr; + EXPECT_EQ(mmi_data_get_struct_element(mmiData, name, &element), MMI_ERROR_INVALID_PARAMETER); +} + +TEST_F(MMIDataTest, MMIDataDestroy_p1) { + int sourceValue = 0; + ASSERT_EQ(mmi_data_create_int(sourceValue, &mmiData), MMI_ERROR_NONE); + ASSERT_NE(mmiData, nullptr); + + ASSERT_EQ(mmi_data_destroy(mmiData), MMI_ERROR_NONE); + mmiData = nullptr; +} + +TEST_F(MMIDataTest, MMIDataDestroy_p2) { + const char *sourceValue = "Hello"; + EXPECT_EQ(mmi_data_create_text(sourceValue, &mmiData), MMI_ERROR_NONE); + ASSERT_NE(mmiData, nullptr); + + ASSERT_EQ(mmi_data_destroy(mmiData), MMI_ERROR_NONE); + mmiData = nullptr; +} + +TEST_F(MMIDataTest, MMIDataDestroy_p3) { + ASSERT_EQ(mmi_data_create_array(&mmiData), MMI_ERROR_NONE); + ASSERT_NE(mmiData, nullptr); + + constexpr int COUNT_ELEMENTS = 2; + constexpr int source[COUNT_ELEMENTS] = {1, 2}; + for (size_t i = 0; i < COUNT_ELEMENTS; i++) { + ASSERT_EQ(mmi_data_create_int(source[i], &arrayElement[i]), MMI_ERROR_NONE); + ASSERT_EQ(mmi_data_add_array_element(mmiData, arrayElement[i]), MMI_ERROR_NONE); + arrayElement[i] = nullptr; + } + + ASSERT_EQ(mmi_data_destroy(mmiData), MMI_ERROR_NONE); + mmiData = nullptr; +} + +TEST_F(MMIDataTest, MMIDataDestroy_p4) { + ASSERT_EQ(mmi_data_create_struct(&mmiData), MMI_ERROR_NONE); + ASSERT_NE(mmiData, nullptr); + + constexpr int sourceInt = 1; + constexpr const char *sourceText = "Hello"; + ASSERT_EQ(mmi_data_create_int(sourceInt, &arrayElement[0]), MMI_ERROR_NONE); + ASSERT_EQ(mmi_data_create_text(sourceText, &arrayElement[1]), MMI_ERROR_NONE); + + constexpr int COUNT_ELEMENTS = 2; + constexpr const char *name[COUNT_ELEMENTS] = {"Element1", "Element2"}; + for (size_t i = 0; i < COUNT_ELEMENTS; i++) { + ASSERT_EQ(mmi_data_set_struct_element(mmiData, name[i], arrayElement[i]), MMI_ERROR_NONE); + arrayElement[i] = nullptr; + } + + ASSERT_EQ(mmi_data_destroy(mmiData), MMI_ERROR_NONE); + mmiData = nullptr; +} + +TEST_F(MMIDataTest, MMIDataDestroy_n) { + ASSERT_EQ(mmi_data_destroy(nullptr), MMI_ERROR_INVALID_PARAMETER); +} diff --git a/tests/mmi/mmi-ipc-test.cpp b/tests/mmi/mmi-ipc-test.cpp new file mode 100644 index 0000000..2574314 --- /dev/null +++ b/tests/mmi/mmi-ipc-test.cpp @@ -0,0 +1,129 @@ +/* + * Copyright (c) 2022 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 "mmi.h" +#include "mmi-tests.h" +#include "mmi-ipc-tidl.h" + +#include +#include + +static mmi_state_e g_state = MMI_STATE_NONE; +static int state_changed_cb(mmi_state_e state, void *user_data) { + g_state = state; + return MMI_ERROR_NONE; +} + +static void wait_until_connected(int msec) { + int i = 0; + while (g_state != MMI_STATE_READY && i < msec) { + ecore_main_loop_iterate(); + usleep(1000); + i++; + } +} + +class MMIIpcTest : public ::testing::Test { +public: + void SetUp(void) override { + ecore_init(); + } + + void TearDown(void) override { + ecore_shutdown(); + } +}; + +TEST_F(MMIIpcTest, MMIFWIpcInitSuccess_p) { + int res = mmi_initialize(); + + EXPECT_EQ(res, MMI_ERROR_NONE); + + mmi_deinitialize(); +} + +TEST_F(MMIIpcTest, MMIFWIpcWorkflowInstanceCreationSuccess_p) { + int res = mmi_initialize(); + EXPECT_EQ(res, MMI_ERROR_NONE); + + res = mmi_set_state_changed_cb(state_changed_cb, nullptr); + EXPECT_EQ(res, MMI_ERROR_NONE); + + wait_until_connected(1000); + + mmi_workflow_instance_h instance = nullptr; + res = mmi_standard_workflow_instance_create(MMI_STANDARD_WORKFLOW_WAKEUPLESS_COMMAND, &instance); + EXPECT_EQ(res, MMI_ERROR_NONE); + + res = mmi_unset_state_changed_cb(state_changed_cb); + EXPECT_EQ(res, MMI_ERROR_NONE); + + res = mmi_deinitialize(); + EXPECT_EQ(res, MMI_ERROR_NONE); +} + +TEST_F(MMIIpcTest, MMIFWIpcWorkflowInstanceSetAttributeSuccess_p) { + int res = mmi_initialize(); + EXPECT_EQ(res, MMI_ERROR_NONE); + + res = mmi_set_state_changed_cb(state_changed_cb, nullptr); + EXPECT_EQ(res, MMI_ERROR_NONE); + + wait_until_connected(1000); + + mmi_workflow_instance_h instance = nullptr; + res = mmi_standard_workflow_instance_create(MMI_STANDARD_WORKFLOW_WAKEUPLESS_COMMAND, &instance); + EXPECT_EQ(res, MMI_ERROR_NONE); + + mmi_attribute_h attribute = nullptr; + constexpr size_t COMMAND_NUM = 2; + const char *commands[] = {"Open", "Close"}; + res = mmi_attribute_create_string_array("COMMANDS", commands, COMMAND_NUM, &attribute); + EXPECT_EQ(res, MMI_ERROR_NONE); + + res = mmi_workflow_instance_set_attribute(instance, attribute); + EXPECT_EQ(res, MMI_ERROR_NONE); + + res = mmi_unset_state_changed_cb(state_changed_cb); + EXPECT_EQ(res, MMI_ERROR_NONE); + + res = mmi_deinitialize(); + EXPECT_EQ(res, MMI_ERROR_NONE); +} + +TEST_F(MMIIpcTest, MMIFWIpcWorkflowInstanceActivateSuccess_p) { + int res = mmi_initialize(); + EXPECT_EQ(res, MMI_ERROR_NONE); + + res = mmi_set_state_changed_cb(state_changed_cb, nullptr); + EXPECT_EQ(res, MMI_ERROR_NONE); + + wait_until_connected(1000); + + mmi_workflow_instance_h instance = nullptr; + res = mmi_standard_workflow_instance_create(MMI_STANDARD_WORKFLOW_WAKEUPLESS_COMMAND, &instance); + EXPECT_EQ(res, MMI_ERROR_NONE); + + res = mmi_workflow_instance_activate(instance); + EXPECT_EQ(res, MMI_ERROR_NONE); + + res = mmi_unset_state_changed_cb(state_changed_cb); + EXPECT_EQ(res, MMI_ERROR_NONE); + + res = mmi_deinitialize(); + EXPECT_EQ(res, MMI_ERROR_NONE); +} diff --git a/tests/mmi/mmi-main-test.cpp b/tests/mmi/mmi-main-test.cpp new file mode 100644 index 0000000..1abb86a --- /dev/null +++ b/tests/mmi/mmi-main-test.cpp @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2022 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 "mmi.h" +#include "mmi-tests.h" +#include "mmi-ipc-tidl.h" + +#include +#include + +static int state_changed_cb(mmi_state_e state, void *user_data) { + return MMI_ERROR_NONE; +} + +class MMIMainTest : public ::testing::Test { +public: + void SetUp(void) override { + ecore_init(); + } + + void TearDown(void) override { + ecore_shutdown(); + } +}; + +TEST_F(MMIMainTest, MMIMainInit) { + int res = mmi_initialize(); + + EXPECT_EQ(res, MMI_ERROR_NONE); + + mmi_deinitialize(); +} + +TEST_F(MMIMainTest, MmiClientCreateSuccess) { + int res = mmi_initialize(); + + EXPECT_EQ(res, MMI_ERROR_NONE); + + mmi_deinitialize(); +} + + diff --git a/tests/mmi-tests.cpp b/tests/mmi/mmi-tests.cpp similarity index 53% rename from tests/mmi-tests.cpp rename to tests/mmi/mmi-tests.cpp index 1db650c..3ad4905 100644 --- a/tests/mmi-tests.cpp +++ b/tests/mmi/mmi-tests.cpp @@ -17,37 +17,30 @@ #include "mmi-tests.h" -int main(int argc, char **argv) -{ - auto testResults = false; +int main(int argc, char **argv) { + auto testResults = false; #ifdef TIZEN_TEST_GCOV - setenv("GCOV_PREFIX", "/tmp", 1); + setenv("GCOV_PREFIX", "/tmp", 1); #endif - try - { - ::testing::InitGoogleMock(&argc, argv); - ::testing::FLAGS_gtest_death_test_style = "fast"; - } - catch ( ... ) - { - PRINT("Error occurred while trying to initialize GoogleTest.\n"); - exit(EXIT_FAILURE); - } + try { + ::testing::InitGoogleMock(&argc, argv); + ::testing::FLAGS_gtest_death_test_style = "fast"; + } catch ( ... ) { + PRINT("Error occurred while trying to initialize GoogleTest.\n"); + exit(EXIT_FAILURE); + } - try - { - testResults = (RUN_ALL_TESTS() == 0) ? true : false; - } - catch (const ::testing::internal::GoogleTestFailureException &e) - { - testResults = false; - PRINT("GoogleTestFailureException has been thrown: %s\n", e.what()); - } + try { + testResults = (RUN_ALL_TESTS() == 0) ? true : false; + } catch (const ::testing::internal::GoogleTestFailureException &e) { + testResults = false; + PRINT("GoogleTestFailureException has been thrown: %s\n", e.what()); + } #ifdef TIZEN_TEST_GCOV - __gcov_flush(); + __gcov_flush(); #endif - return testResults; + return (testResults ? 0 : 1); } diff --git a/tests/mmi-tests.h b/tests/mmi/mmi-tests.h similarity index 96% rename from tests/mmi-tests.h rename to tests/mmi/mmi-tests.h index 862d1f3..d775edf 100644 --- a/tests/mmi-tests.h +++ b/tests/mmi/mmi-tests.h @@ -29,7 +29,6 @@ #ifdef TIZEN_TEST_GCOV extern "C" void __gcov_flush(void); #endif -extern void wait_for_connect(); using ::testing::TestWithParam; using ::testing::Bool; diff --git a/tests/mmi/primitive-value/mmi-primitive-value-test.cpp b/tests/mmi/primitive-value/mmi-primitive-value-test.cpp new file mode 100644 index 0000000..bb9725c --- /dev/null +++ b/tests/mmi/primitive-value/mmi-primitive-value-test.cpp @@ -0,0 +1,629 @@ +/* + * Copyright (c) 2023 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT 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 MMIPrimitiveValueTest : public ::testing::Test { +public: + void SetUp(void) override { + primitiveValue = nullptr; + arrayElement1 = nullptr; + arrayElement2 = nullptr; + } + + void TearDown(void) override { + if (nullptr != primitiveValue) { + mmi_primitive_value_destroy(primitiveValue); + } + + if (nullptr != arrayElement1) { + mmi_primitive_value_destroy(arrayElement1); + } + + if (nullptr != arrayElement2) { + mmi_primitive_value_destroy(arrayElement2); + } + + primitiveValue = nullptr; + arrayElement1 = nullptr; + arrayElement2 = nullptr; + } + + mmi_primitive_value_h primitiveValue = nullptr; + mmi_primitive_value_h arrayElement1 = nullptr; + mmi_primitive_value_h arrayElement2 = nullptr; +}; + +TEST_F(MMIPrimitiveValueTest, MMIPrimitiveCreateValueByInt_p) { + int sourceValue = 0; + EXPECT_EQ(mmi_primitive_value_create_int(sourceValue, &primitiveValue), MMI_ERROR_NONE); + EXPECT_NE(primitiveValue, nullptr); +} + +TEST_F(MMIPrimitiveValueTest, MMIPrimitiveCreateValueByInt_n) { + int sourceValue = 0; + EXPECT_EQ(mmi_primitive_value_create_int(sourceValue, nullptr), MMI_ERROR_INVALID_PARAMETER); +} + +TEST_F(MMIPrimitiveValueTest, MMIPrimitiveCreateValueByFloat_p) { + float sourceValue = 0.0; + EXPECT_EQ(mmi_primitive_value_create_float(sourceValue, &primitiveValue), MMI_ERROR_NONE); + EXPECT_NE(primitiveValue, nullptr); +} + +TEST_F(MMIPrimitiveValueTest, MMIPrimitiveCreateValueByFloat_n) { + float sourceValue = 0.0; + EXPECT_EQ(mmi_primitive_value_create_float(sourceValue, nullptr), MMI_ERROR_INVALID_PARAMETER); +} + +TEST_F(MMIPrimitiveValueTest, MMIPrimitiveCreateValueByString_p) { + const char *sourceValue = "Hello"; + EXPECT_EQ(mmi_primitive_value_create_string(sourceValue, &primitiveValue), MMI_ERROR_NONE); + EXPECT_NE(primitiveValue, nullptr); +} + +TEST_F(MMIPrimitiveValueTest, MMIPrimitiveCreateValueByString_n) { + const char *sourceValue = "Hello"; + EXPECT_EQ(mmi_primitive_value_create_string(sourceValue, nullptr), MMI_ERROR_INVALID_PARAMETER); + EXPECT_EQ(mmi_primitive_value_create_string(nullptr, &primitiveValue), MMI_ERROR_INVALID_PARAMETER); +} + +TEST_F(MMIPrimitiveValueTest, MMIPrimitiveCreateValueByBool_p) { + bool sourceValue = true; + EXPECT_EQ(mmi_primitive_value_create_bool(sourceValue, &primitiveValue), MMI_ERROR_NONE); + EXPECT_NE(primitiveValue, nullptr); +} + +TEST_F(MMIPrimitiveValueTest, MMIPrimitiveCreateValueByBool_n) { + bool sourceValue = true; + EXPECT_EQ(mmi_primitive_value_create_bool(sourceValue, nullptr), MMI_ERROR_INVALID_PARAMETER); +} + +TEST_F(MMIPrimitiveValueTest, MMIPrimitiveCreateValueByArray_p) { + EXPECT_EQ(mmi_primitive_value_create_array(&primitiveValue), MMI_ERROR_NONE); + EXPECT_NE(primitiveValue, nullptr); +} + +TEST_F(MMIPrimitiveValueTest, MMIPrimitiveCreateValueByArray_n) { + EXPECT_EQ(mmi_primitive_value_create_array(nullptr), MMI_ERROR_INVALID_PARAMETER); +} + +TEST_F(MMIPrimitiveValueTest, MMIPrimitiveAddElementIntoArray_p) { + ASSERT_EQ(mmi_primitive_value_create_array(&primitiveValue), MMI_ERROR_NONE); + EXPECT_NE(primitiveValue, nullptr); + + int source1 = 1, source2 = 2; + ASSERT_EQ(mmi_primitive_value_create_int(source1, &arrayElement1), MMI_ERROR_NONE); + ASSERT_EQ(mmi_primitive_value_create_int(source2, &arrayElement2), MMI_ERROR_NONE); + + ASSERT_EQ(mmi_primitive_value_add_array_element(primitiveValue, arrayElement1), MMI_ERROR_NONE); + arrayElement1 = nullptr; + ASSERT_EQ(mmi_primitive_value_add_array_element(primitiveValue, arrayElement2), MMI_ERROR_NONE); + arrayElement2 = nullptr; +} + +TEST_F(MMIPrimitiveValueTest, MMIPrimitiveAddElementIntoArray_n1) { + ASSERT_EQ(mmi_primitive_value_create_array(&primitiveValue), MMI_ERROR_NONE); + EXPECT_NE(primitiveValue, nullptr); + + int source1 = 1; + float source2 = 2.0; + ASSERT_EQ(mmi_primitive_value_create_int(source1, &arrayElement1), MMI_ERROR_NONE); + ASSERT_EQ(mmi_primitive_value_create_float(source2, &arrayElement2), MMI_ERROR_NONE); + + EXPECT_EQ(mmi_primitive_value_add_array_element(nullptr, arrayElement1), MMI_ERROR_INVALID_PARAMETER); + EXPECT_EQ(mmi_primitive_value_add_array_element(primitiveValue, nullptr), MMI_ERROR_INVALID_PARAMETER); + + ASSERT_EQ(mmi_primitive_value_add_array_element(primitiveValue, arrayElement1), MMI_ERROR_NONE); + arrayElement1 = nullptr; + EXPECT_EQ(mmi_primitive_value_add_array_element(primitiveValue, arrayElement2), MMI_ERROR_INVALID_PARAMETER); +} + +TEST_F(MMIPrimitiveValueTest, MMIPrimitiveAddElementIntoArray_n2) { + int sourceValue = 0; + EXPECT_EQ(mmi_primitive_value_create_int(sourceValue, &primitiveValue), MMI_ERROR_NONE); + EXPECT_NE(primitiveValue, nullptr); + + int source1 = 1; + ASSERT_EQ(mmi_primitive_value_create_int(source1, &arrayElement1), MMI_ERROR_NONE); + EXPECT_NE(arrayElement1, nullptr); + + EXPECT_EQ(mmi_primitive_value_add_array_element(primitiveValue, arrayElement1), MMI_ERROR_INVALID_PARAMETER); +} + +TEST_F(MMIPrimitiveValueTest, MMIPrimitiveGetType_p) { + mmi_primitive_value_h tempIntValue = nullptr; + mmi_primitive_value_type_e tempIntType = MMI_PRIMITIVE_VALUE_TYPE_ARRAY; + + EXPECT_EQ(mmi_primitive_value_create_int(0, &tempIntValue), MMI_ERROR_NONE); + EXPECT_EQ(mmi_primitive_value_get_type(tempIntValue, &tempIntType), MMI_ERROR_NONE); + EXPECT_EQ(tempIntType, MMI_PRIMITIVE_VALUE_TYPE_INT); + EXPECT_EQ(mmi_primitive_value_destroy(tempIntValue), MMI_ERROR_NONE); + + mmi_primitive_value_h tempFloatValue = nullptr; + mmi_primitive_value_type_e tempFloatType = MMI_PRIMITIVE_VALUE_TYPE_ARRAY; + + EXPECT_EQ(mmi_primitive_value_create_float(0.0, &tempFloatValue), MMI_ERROR_NONE); + EXPECT_EQ(mmi_primitive_value_get_type(tempFloatValue, &tempFloatType), MMI_ERROR_NONE); + EXPECT_EQ(tempFloatType, MMI_PRIMITIVE_VALUE_TYPE_FLOAT); + EXPECT_EQ(mmi_primitive_value_destroy(tempFloatValue), MMI_ERROR_NONE); + + mmi_primitive_value_h tempStringValue = nullptr; + mmi_primitive_value_type_e tempStringType = MMI_PRIMITIVE_VALUE_TYPE_ARRAY; + + EXPECT_EQ(mmi_primitive_value_create_string("Hello", &tempStringValue), MMI_ERROR_NONE); + EXPECT_EQ(mmi_primitive_value_get_type(tempStringValue, &tempStringType), MMI_ERROR_NONE); + EXPECT_EQ(tempStringType, MMI_PRIMITIVE_VALUE_TYPE_STRING); + EXPECT_EQ(mmi_primitive_value_destroy(tempStringValue), MMI_ERROR_NONE); + + mmi_primitive_value_h tempBoolValue = nullptr; + mmi_primitive_value_type_e tempBoolType = MMI_PRIMITIVE_VALUE_TYPE_ARRAY; + + EXPECT_EQ(mmi_primitive_value_create_bool(true, &tempBoolValue), MMI_ERROR_NONE); + EXPECT_EQ(mmi_primitive_value_get_type(tempBoolValue, &tempBoolType), MMI_ERROR_NONE); + EXPECT_EQ(tempBoolType, MMI_PRIMITIVE_VALUE_TYPE_BOOL); + EXPECT_EQ(mmi_primitive_value_destroy(tempBoolValue), MMI_ERROR_NONE); + + mmi_primitive_value_h tempArrayValue = nullptr; + mmi_primitive_value_type_e tempArrayType = MMI_PRIMITIVE_VALUE_TYPE_INT; + + EXPECT_EQ(mmi_primitive_value_create_array(&tempArrayValue), MMI_ERROR_NONE); + EXPECT_EQ(mmi_primitive_value_get_type(tempArrayValue, &tempArrayType), MMI_ERROR_NONE); + EXPECT_EQ(tempArrayType, MMI_PRIMITIVE_VALUE_TYPE_ARRAY); + EXPECT_EQ(mmi_primitive_value_destroy(tempArrayValue), MMI_ERROR_NONE); +} + +TEST_F(MMIPrimitiveValueTest, MMIPrimitiveGetType_n) { + float sourceValue = 0.0; + ASSERT_EQ(mmi_primitive_value_create_float(sourceValue, &primitiveValue), MMI_ERROR_NONE); + EXPECT_NE(primitiveValue, nullptr); + + mmi_primitive_value_type_e type = MMI_PRIMITIVE_VALUE_TYPE_ARRAY; + EXPECT_EQ(mmi_primitive_value_get_type(nullptr, &type), MMI_ERROR_INVALID_PARAMETER); + EXPECT_EQ(mmi_primitive_value_get_type(primitiveValue, nullptr), MMI_ERROR_INVALID_PARAMETER); +} + +TEST_F(MMIPrimitiveValueTest, MMIPrimitiveGetIntValue_p) { + int sourceValue = 0; + ASSERT_EQ(mmi_primitive_value_create_int(sourceValue, &primitiveValue), MMI_ERROR_NONE); + EXPECT_NE(primitiveValue, nullptr); + + int targetValue = -1; + EXPECT_EQ(mmi_primitive_value_get_int(primitiveValue, &targetValue), MMI_ERROR_NONE); + EXPECT_EQ(targetValue, sourceValue); +} + +TEST_F(MMIPrimitiveValueTest, MMIPrimitiveGetIntValue_n1) { + int sourceValue = 0; + ASSERT_EQ(mmi_primitive_value_create_int(sourceValue, &primitiveValue), MMI_ERROR_NONE); + EXPECT_NE(primitiveValue, nullptr); + + int targetValue = -1; + EXPECT_EQ(mmi_primitive_value_get_int(nullptr, &targetValue), MMI_ERROR_INVALID_PARAMETER); + EXPECT_EQ(mmi_primitive_value_get_int(primitiveValue, nullptr), MMI_ERROR_INVALID_PARAMETER); +} + +TEST_F(MMIPrimitiveValueTest, MMIPrimitiveGetIntValue_n2) { + float sourceValue = 0.0; + ASSERT_EQ(mmi_primitive_value_create_float(sourceValue, &primitiveValue), MMI_ERROR_NONE); + EXPECT_NE(primitiveValue, nullptr); + + int targetValue = -1; + EXPECT_EQ(mmi_primitive_value_get_int(primitiveValue, &targetValue), MMI_ERROR_INVALID_PARAMETER); +} + +TEST_F(MMIPrimitiveValueTest, MMIPrimitiveGetFloatValue_p) { + float sourceValue = 0; + ASSERT_EQ(mmi_primitive_value_create_float(sourceValue, &primitiveValue), MMI_ERROR_NONE); + EXPECT_NE(primitiveValue, nullptr); + + float targetValue = -1.0; + EXPECT_EQ(mmi_primitive_value_get_float(primitiveValue, &targetValue), MMI_ERROR_NONE); + EXPECT_EQ(targetValue, sourceValue); +} + +TEST_F(MMIPrimitiveValueTest, MMIPrimitiveGetFloatValue_n1) { + float sourceValue = 0; + ASSERT_EQ(mmi_primitive_value_create_float(sourceValue, &primitiveValue), MMI_ERROR_NONE); + EXPECT_NE(primitiveValue, nullptr); + + float targetValue = -1.0; + EXPECT_EQ(mmi_primitive_value_get_float(nullptr, &targetValue), MMI_ERROR_INVALID_PARAMETER); + EXPECT_EQ(mmi_primitive_value_get_float(primitiveValue, nullptr), MMI_ERROR_INVALID_PARAMETER); +} + +TEST_F(MMIPrimitiveValueTest, MMIPrimitiveGetFloatValue_n2) { + int sourceValue = 0; + ASSERT_EQ(mmi_primitive_value_create_int(sourceValue, &primitiveValue), MMI_ERROR_NONE); + EXPECT_NE(primitiveValue, nullptr); + + float targetValue = -1.0; + EXPECT_EQ(mmi_primitive_value_get_float(primitiveValue, &targetValue), MMI_ERROR_INVALID_PARAMETER); +} + +TEST_F(MMIPrimitiveValueTest, MMIPrimitiveGetStringValue_p) { + const char *sourceValue = "Hello"; + ASSERT_EQ(mmi_primitive_value_create_string(sourceValue, &primitiveValue), MMI_ERROR_NONE); + EXPECT_NE(primitiveValue, nullptr); + + const char *targetValue = nullptr; + EXPECT_EQ(mmi_primitive_value_get_string(primitiveValue, &targetValue), MMI_ERROR_NONE); + EXPECT_STREQ(targetValue, sourceValue); +} + +TEST_F(MMIPrimitiveValueTest, MMIPrimitiveGetStringValue_n1) { + const char *sourceValue = "Hello"; + ASSERT_EQ(mmi_primitive_value_create_string(sourceValue, &primitiveValue), MMI_ERROR_NONE); + EXPECT_NE(primitiveValue, nullptr); + + const char *targetValue = nullptr; + EXPECT_EQ(mmi_primitive_value_get_string(nullptr, &targetValue), MMI_ERROR_INVALID_PARAMETER); + EXPECT_EQ(mmi_primitive_value_get_string(primitiveValue, nullptr), MMI_ERROR_INVALID_PARAMETER); +} + +TEST_F(MMIPrimitiveValueTest, MMIPrimitiveGetStringValue_n2) { + int sourceValue = 0; + ASSERT_EQ(mmi_primitive_value_create_int(sourceValue, &primitiveValue), MMI_ERROR_NONE); + EXPECT_NE(primitiveValue, nullptr); + + const char *targetValue = nullptr; + EXPECT_EQ(mmi_primitive_value_get_string(primitiveValue, &targetValue), MMI_ERROR_INVALID_PARAMETER); +} + +TEST_F(MMIPrimitiveValueTest, MMIPrimitiveGetArrayCount_p) { + ASSERT_EQ(mmi_primitive_value_create_array(&primitiveValue), MMI_ERROR_NONE); + EXPECT_NE(primitiveValue, nullptr); + + size_t count = 10; + EXPECT_EQ(mmi_primitive_value_get_array_count(primitiveValue, &count), MMI_ERROR_NONE); + EXPECT_EQ(count, 0); + + int source1 = 1, source2 = 2; + ASSERT_EQ(mmi_primitive_value_create_int(source1, &arrayElement1), MMI_ERROR_NONE); + ASSERT_EQ(mmi_primitive_value_create_int(source2, &arrayElement2), MMI_ERROR_NONE); + + ASSERT_EQ(mmi_primitive_value_add_array_element(primitiveValue, arrayElement1), MMI_ERROR_NONE); + arrayElement1 = nullptr; + ASSERT_EQ(mmi_primitive_value_add_array_element(primitiveValue, arrayElement2), MMI_ERROR_NONE); + arrayElement2 = nullptr; + + EXPECT_EQ(mmi_primitive_value_get_array_count(primitiveValue, &count), MMI_ERROR_NONE); + EXPECT_EQ(count, 2); +} + +TEST_F(MMIPrimitiveValueTest, MMIPrimitiveGetArrayCount_n1) { + ASSERT_EQ(mmi_primitive_value_create_array(&primitiveValue), MMI_ERROR_NONE); + EXPECT_NE(primitiveValue, nullptr); + + size_t count = 0; + EXPECT_EQ(mmi_primitive_value_get_array_count(nullptr, &count), MMI_ERROR_INVALID_PARAMETER); + EXPECT_EQ(mmi_primitive_value_get_array_count(primitiveValue, nullptr), MMI_ERROR_INVALID_PARAMETER); +} + +TEST_F(MMIPrimitiveValueTest, MMIPrimitiveGetArrayCount_n2) { + int sourceValue = 0; + ASSERT_EQ(mmi_primitive_value_create_int(sourceValue, &primitiveValue), MMI_ERROR_NONE); + EXPECT_NE(primitiveValue, nullptr); + + size_t count = 0; + EXPECT_EQ(mmi_primitive_value_get_array_count(primitiveValue, &count), MMI_ERROR_INVALID_PARAMETER); +} + +TEST_F(MMIPrimitiveValueTest, MMIPrimitiveGetArrayElement_p) { + ASSERT_EQ(mmi_primitive_value_create_array(&primitiveValue), MMI_ERROR_NONE); + EXPECT_NE(primitiveValue, nullptr); + + const int source[] = {1, 2}; + ASSERT_EQ(mmi_primitive_value_create_int(source[0], &arrayElement1), MMI_ERROR_NONE); + ASSERT_EQ(mmi_primitive_value_create_int(source[1], &arrayElement2), MMI_ERROR_NONE); + + EXPECT_EQ(mmi_primitive_value_add_array_element(primitiveValue, arrayElement1), MMI_ERROR_NONE); + arrayElement1 = nullptr; + EXPECT_EQ(mmi_primitive_value_add_array_element(primitiveValue, arrayElement2), MMI_ERROR_NONE); + arrayElement2 = nullptr; + + size_t count = 0; + EXPECT_EQ(mmi_primitive_value_get_array_count(primitiveValue, &count), MMI_ERROR_NONE); + EXPECT_EQ(count, 2); + + for (size_t i = 0; i < count; i++) { + mmi_primitive_value_h element = nullptr; + EXPECT_EQ(mmi_primitive_value_get_array_element(primitiveValue, i, &element), MMI_ERROR_NONE); + + mmi_primitive_value_type_e type = MMI_PRIMITIVE_VALUE_TYPE_BOOL; + EXPECT_EQ(mmi_primitive_value_get_type(element, &type), MMI_ERROR_NONE); + EXPECT_EQ(type, MMI_PRIMITIVE_VALUE_TYPE_INT); + + int data = 0; + EXPECT_EQ(mmi_primitive_value_get_int(element, &data), MMI_ERROR_NONE); + EXPECT_EQ(data, source[i]); + } +} + +TEST_F(MMIPrimitiveValueTest, MMIPrimitiveGetArrayElement_n1) { + ASSERT_EQ(mmi_primitive_value_create_array(&primitiveValue), MMI_ERROR_NONE); + EXPECT_NE(primitiveValue, nullptr); + + size_t count = 0; + EXPECT_EQ(mmi_primitive_value_get_array_count(nullptr, &count), MMI_ERROR_INVALID_PARAMETER); + EXPECT_EQ(mmi_primitive_value_get_array_count(primitiveValue, nullptr), MMI_ERROR_INVALID_PARAMETER); +} + +TEST_F(MMIPrimitiveValueTest, MMIPrimitiveGetArrayElement_n2) { + int sourceValue = 0; + ASSERT_EQ(mmi_primitive_value_create_int(sourceValue, &primitiveValue), MMI_ERROR_NONE); + EXPECT_NE(primitiveValue, nullptr); + + size_t count = 0; + EXPECT_EQ(mmi_primitive_value_get_array_count(primitiveValue, &count), MMI_ERROR_INVALID_PARAMETER); +} + +TEST_F(MMIPrimitiveValueTest, MMIPrimitiveCloneValue_p1) { + int sourceValue = 0; + ASSERT_EQ(mmi_primitive_value_create_int(sourceValue, &primitiveValue), MMI_ERROR_NONE); + EXPECT_NE(primitiveValue, nullptr); + + mmi_primitive_value_h clonedValue = nullptr; + EXPECT_EQ(mmi_primitive_value_clone(primitiveValue, &clonedValue), MMI_ERROR_NONE); + + mmi_primitive_value_type_e type = MMI_PRIMITIVE_VALUE_TYPE_BOOL; + EXPECT_EQ(mmi_primitive_value_get_type(clonedValue, &type), MMI_ERROR_NONE); + EXPECT_EQ(type, MMI_PRIMITIVE_VALUE_TYPE_INT); + + int data = 0; + EXPECT_EQ(mmi_primitive_value_get_int(clonedValue, &data), MMI_ERROR_NONE); + EXPECT_EQ(data, sourceValue); + + EXPECT_EQ(mmi_primitive_value_destroy(clonedValue), MMI_ERROR_NONE); +} + +TEST_F(MMIPrimitiveValueTest, MMIPrimitiveCloneValue_p2) { + const char *sourceValue = "Hello"; + EXPECT_EQ(mmi_primitive_value_create_string(sourceValue, &primitiveValue), MMI_ERROR_NONE); + EXPECT_NE(primitiveValue, nullptr); + + mmi_primitive_value_h clonedValue = nullptr; + EXPECT_EQ(mmi_primitive_value_clone(primitiveValue, &clonedValue), MMI_ERROR_NONE); + + mmi_primitive_value_type_e type = MMI_PRIMITIVE_VALUE_TYPE_BOOL; + EXPECT_EQ(mmi_primitive_value_get_type(clonedValue, &type), MMI_ERROR_NONE); + EXPECT_EQ(type, MMI_PRIMITIVE_VALUE_TYPE_STRING); + + const char *data = nullptr; + EXPECT_EQ(mmi_primitive_value_get_string(clonedValue, &data), MMI_ERROR_NONE); + EXPECT_STREQ(data, sourceValue); + + EXPECT_EQ(mmi_primitive_value_destroy(clonedValue), MMI_ERROR_NONE); +} + +TEST_F(MMIPrimitiveValueTest, MMIPrimitiveCloneValue_p3) { + ASSERT_EQ(mmi_primitive_value_create_array(&primitiveValue), MMI_ERROR_NONE); + EXPECT_NE(primitiveValue, nullptr); + + const int source[] = {1, 2}; + ASSERT_EQ(mmi_primitive_value_create_int(source[0], &arrayElement1), MMI_ERROR_NONE); + ASSERT_EQ(mmi_primitive_value_create_int(source[1], &arrayElement2), MMI_ERROR_NONE); + + ASSERT_EQ(mmi_primitive_value_add_array_element(primitiveValue, arrayElement1), MMI_ERROR_NONE); + arrayElement1 = nullptr; + ASSERT_EQ(mmi_primitive_value_add_array_element(primitiveValue, arrayElement2), MMI_ERROR_NONE); + arrayElement2 = nullptr; + + mmi_primitive_value_h clonedValue = nullptr; + EXPECT_EQ(mmi_primitive_value_clone(primitiveValue, &clonedValue), MMI_ERROR_NONE); + + size_t count = 0; + EXPECT_EQ(mmi_primitive_value_get_array_count(clonedValue, &count), MMI_ERROR_NONE); + EXPECT_EQ(count, 2); + + for (size_t i = 0; i < count; i++) { + mmi_primitive_value_h element = nullptr; + EXPECT_EQ(mmi_primitive_value_get_array_element(clonedValue, i, &element), MMI_ERROR_NONE); + + mmi_primitive_value_type_e type = MMI_PRIMITIVE_VALUE_TYPE_BOOL; + EXPECT_EQ(mmi_primitive_value_get_type(element, &type), MMI_ERROR_NONE); + EXPECT_EQ(type, MMI_PRIMITIVE_VALUE_TYPE_INT); + + int data = 0; + EXPECT_EQ(mmi_primitive_value_get_int(element, &data), MMI_ERROR_NONE); + EXPECT_EQ(data, source[i]); + } + + EXPECT_EQ(mmi_primitive_value_destroy(clonedValue), MMI_ERROR_NONE); +} + +TEST_F(MMIPrimitiveValueTest, MMIPrimitiveCloneValue_n) { + int sourceValue = 0; + ASSERT_EQ(mmi_primitive_value_create_int(sourceValue, &primitiveValue), MMI_ERROR_NONE); + EXPECT_NE(primitiveValue, nullptr); + + mmi_primitive_value_h clonedValue = nullptr; + EXPECT_EQ(mmi_primitive_value_clone(nullptr, &clonedValue), MMI_ERROR_INVALID_PARAMETER); + EXPECT_EQ(mmi_primitive_value_clone(primitiveValue, nullptr), MMI_ERROR_INVALID_PARAMETER); +} + +TEST_F(MMIPrimitiveValueTest, MMIPrimitiveDestroy_p1) { + int sourceValue = 0; + ASSERT_EQ(mmi_primitive_value_create_int(sourceValue, &primitiveValue), MMI_ERROR_NONE); + EXPECT_NE(primitiveValue, nullptr); + + ASSERT_EQ(mmi_primitive_value_destroy(primitiveValue), MMI_ERROR_NONE); + primitiveValue = nullptr; +} + +TEST_F(MMIPrimitiveValueTest, MMIPrimitiveDestroy_p2) { + const char *sourceValue = "Hello"; + EXPECT_EQ(mmi_primitive_value_create_string(sourceValue, &primitiveValue), MMI_ERROR_NONE); + EXPECT_NE(primitiveValue, nullptr); + + ASSERT_EQ(mmi_primitive_value_destroy(primitiveValue), MMI_ERROR_NONE); + primitiveValue = nullptr; +} + +TEST_F(MMIPrimitiveValueTest, MMIPrimitiveDestroy_p3) { + ASSERT_EQ(mmi_primitive_value_create_array(&primitiveValue), MMI_ERROR_NONE); + EXPECT_NE(primitiveValue, nullptr); + + const int source[] = {1, 2}; + ASSERT_EQ(mmi_primitive_value_create_int(source[0], &arrayElement1), MMI_ERROR_NONE); + ASSERT_EQ(mmi_primitive_value_create_int(source[1], &arrayElement2), MMI_ERROR_NONE); + + EXPECT_EQ(mmi_primitive_value_add_array_element(primitiveValue, arrayElement1), MMI_ERROR_NONE); + arrayElement1 = nullptr; + EXPECT_EQ(mmi_primitive_value_add_array_element(primitiveValue, arrayElement2), MMI_ERROR_NONE); + arrayElement2 = nullptr; + + ASSERT_EQ(mmi_primitive_value_destroy(primitiveValue), MMI_ERROR_NONE); + primitiveValue = nullptr; +} + +TEST_F(MMIPrimitiveValueTest, MMIPrimitiveDestroy_n) { + ASSERT_EQ(mmi_primitive_value_destroy(nullptr), MMI_ERROR_INVALID_PARAMETER); +} + +TEST_F(MMIPrimitiveValueTest, MMIPrimitiveBytesConversionIntValue_p) { + int sourceValue = 3; + ASSERT_EQ(mmi_primitive_value_create_int(sourceValue, &primitiveValue), MMI_ERROR_NONE); + EXPECT_NE(primitiveValue, nullptr); + + unsigned char *bytes = nullptr; + size_t length = 0; + ASSERT_EQ(mmi_primitive_value_to_bytes(primitiveValue, &bytes, &length), MMI_ERROR_NONE); + + mmi_primitive_value_destroy(primitiveValue); + primitiveValue = nullptr; + + mmi_primitive_value_h restoredValue = nullptr; + ASSERT_EQ(mmi_primitive_value_from_bytes(bytes, length, &restoredValue), MMI_ERROR_NONE); + + free(bytes); + + mmi_primitive_value_type_e type = MMI_PRIMITIVE_VALUE_TYPE_BOOL; + ASSERT_EQ(mmi_primitive_value_get_type(restoredValue, &type), MMI_ERROR_NONE); + ASSERT_EQ(type, MMI_PRIMITIVE_VALUE_TYPE_INT); + + int data = 0; + ASSERT_EQ(mmi_primitive_value_get_int(restoredValue, &data), MMI_ERROR_NONE); + ASSERT_EQ(data, sourceValue); + + ASSERT_EQ(mmi_primitive_value_destroy(restoredValue), MMI_ERROR_NONE); +} + +TEST_F(MMIPrimitiveValueTest, MMIPrimitiveBytesConversionIntArray_p) { + ASSERT_EQ(mmi_primitive_value_create_array(&primitiveValue), MMI_ERROR_NONE); + EXPECT_NE(primitiveValue, nullptr); + + constexpr size_t SOURCE_COUNT = 2; + const int source[SOURCE_COUNT] = {1, 2}; + ASSERT_EQ(mmi_primitive_value_create_int(source[0], &arrayElement1), MMI_ERROR_NONE); + ASSERT_EQ(mmi_primitive_value_create_int(source[1], &arrayElement2), MMI_ERROR_NONE); + + ASSERT_EQ(mmi_primitive_value_add_array_element(primitiveValue, arrayElement1), MMI_ERROR_NONE); + arrayElement1 = nullptr; + ASSERT_EQ(mmi_primitive_value_add_array_element(primitiveValue, arrayElement2), MMI_ERROR_NONE); + arrayElement2 = nullptr; + + unsigned char *bytes = nullptr; + size_t length = 0; + ASSERT_EQ(mmi_primitive_value_to_bytes(primitiveValue, &bytes, &length), MMI_ERROR_NONE); + + mmi_primitive_value_destroy(primitiveValue); + primitiveValue = nullptr; + mmi_primitive_value_destroy(arrayElement1); + arrayElement1 = nullptr; + mmi_primitive_value_destroy(arrayElement2); + arrayElement2 = nullptr; + + mmi_primitive_value_h restoredValue = nullptr; + ASSERT_EQ(mmi_primitive_value_from_bytes(bytes, length, &restoredValue), MMI_ERROR_NONE); + + free(bytes); + + mmi_primitive_value_type_e type = MMI_PRIMITIVE_VALUE_TYPE_BOOL; + ASSERT_EQ(mmi_primitive_value_get_type(restoredValue, &type), MMI_ERROR_NONE); + ASSERT_EQ(type, MMI_PRIMITIVE_VALUE_TYPE_ARRAY); + + size_t count = 0; + ASSERT_EQ(mmi_primitive_value_get_array_count(restoredValue, &count), MMI_ERROR_NONE); + ASSERT_EQ(count, SOURCE_COUNT); + + for (size_t i = 0; i < count; ++i) { + int data = 0; + mmi_primitive_value_h element = nullptr; + ASSERT_EQ(mmi_primitive_value_get_array_element(restoredValue, i, &element), MMI_ERROR_NONE); + ASSERT_EQ(mmi_primitive_value_get_type(element, &type), MMI_ERROR_NONE); + ASSERT_EQ(type, MMI_PRIMITIVE_VALUE_TYPE_INT); + ASSERT_EQ(mmi_primitive_value_get_int(element, &data), MMI_ERROR_NONE); + ASSERT_EQ(data, source[i]); + } + ASSERT_EQ(mmi_primitive_value_destroy(restoredValue), MMI_ERROR_NONE); +} + +TEST_F(MMIPrimitiveValueTest, MMIPrimitiveBytesConversionStringArray_p) { + ASSERT_EQ(mmi_primitive_value_create_array(&primitiveValue), MMI_ERROR_NONE); + EXPECT_NE(primitiveValue, nullptr); + + constexpr size_t SOURCE_COUNT = 2; + const char *source[SOURCE_COUNT] = {"1", "Number 2"}; + ASSERT_EQ(mmi_primitive_value_create_string(source[0], &arrayElement1), MMI_ERROR_NONE); + ASSERT_EQ(mmi_primitive_value_create_string(source[1], &arrayElement2), MMI_ERROR_NONE); + + ASSERT_EQ(mmi_primitive_value_add_array_element(primitiveValue, arrayElement1), MMI_ERROR_NONE); + arrayElement1 = nullptr; + ASSERT_EQ(mmi_primitive_value_add_array_element(primitiveValue, arrayElement2), MMI_ERROR_NONE); + arrayElement2 = nullptr; + + unsigned char *bytes = nullptr; + size_t length = 0; + ASSERT_EQ(mmi_primitive_value_to_bytes(primitiveValue, &bytes, &length), MMI_ERROR_NONE); + + mmi_primitive_value_destroy(primitiveValue); + primitiveValue = nullptr; + mmi_primitive_value_destroy(arrayElement1); + arrayElement1 = nullptr; + mmi_primitive_value_destroy(arrayElement2); + arrayElement2 = nullptr; + + mmi_primitive_value_h restoredValue = nullptr; + ASSERT_EQ(mmi_primitive_value_from_bytes(bytes, length, &restoredValue), MMI_ERROR_NONE); + + free(bytes); + + mmi_primitive_value_type_e type = MMI_PRIMITIVE_VALUE_TYPE_BOOL; + ASSERT_EQ(mmi_primitive_value_get_type(restoredValue, &type), MMI_ERROR_NONE); + ASSERT_EQ(type, MMI_PRIMITIVE_VALUE_TYPE_ARRAY); + + size_t count = 0; + ASSERT_EQ(mmi_primitive_value_get_array_count(restoredValue, &count), MMI_ERROR_NONE); + ASSERT_EQ(count, SOURCE_COUNT); + + for (size_t i = 0; i < count; ++i) { + mmi_primitive_value_h element = nullptr; + ASSERT_EQ(mmi_primitive_value_get_array_element(restoredValue, i, &element), MMI_ERROR_NONE); + ASSERT_EQ(mmi_primitive_value_get_type(element, &type), MMI_ERROR_NONE); + ASSERT_EQ(type, MMI_PRIMITIVE_VALUE_TYPE_STRING); + const char *data = nullptr; + EXPECT_EQ(mmi_primitive_value_get_string(element, &data), MMI_ERROR_NONE); + ASSERT_STREQ(data, source[i]); + } + ASSERT_EQ(mmi_primitive_value_destroy(restoredValue), MMI_ERROR_NONE); +} diff --git a/tests/mmi/workflow/mmi-workflow-test.cpp b/tests/mmi/workflow/mmi-workflow-test.cpp new file mode 100644 index 0000000..04ecc94 --- /dev/null +++ b/tests/mmi/workflow/mmi-workflow-test.cpp @@ -0,0 +1,299 @@ +#include +#include +#include + +#include +#include +#include +#include + +#include "mmi-workflow-script-parser.h" + +#include +#include +#include + +extern "C" { +/* Add wrapper functions here with __wrap_ and __real prefix +Example : + +int __real_vconf_get_int(const char *in_key, int *intval); +int __wrap_vconf_get_int(const char *in_key, int *intval) +{ + if (strcmp(in_key, "TestKey") == 0) + return 0; + + return __real_vconf_get_int(in_key, intval); +} +*/ +} + +class MMIWorkflowTest : public ::testing::Test { +public: + void SetUp(void) override { + mmi_workflow_create(&m_workflow); + + mmi_workflow_set_type(m_workflow, MMI_STANDARD_WORKFLOW_WAKEUPLESS_COMMAND); + + m_node_mic = nullptr; + /* FIXME: node should be 'found' instead of 'created' */ + mmi_standard_node_create_source(MMI_STANDARD_NODE_SOURCE_TYPE_MIC_AMBIENT, &m_node_mic); + mmi_workflow_node_add(m_workflow, "MIC", m_node_mic); + + m_node_asr = nullptr; + mmi_standard_node_create_processor(MMI_STANDARD_NODE_PROCESSOR_TYPE_ASR, &m_node_asr); + mmi_workflow_node_add(m_workflow, "ASR", m_node_asr); + + m_node_match = nullptr; + mmi_standard_node_create_logic(MMI_STANDARD_NODE_LOGIC_TYPE_FIXED_STRING_MATCH, &m_node_match); + mmi_workflow_node_add(m_workflow, "MATCH", m_node_match); + + mmi_workflow_link_nodes_by_names(m_workflow, "MIC", "AUDIO", "ASR", "AUDIO"); + mmi_workflow_link_nodes_by_names(m_workflow, "ASR", "FINAL_RESULT", "MATCH", "TEXT"); + + mmi_workflow_attribute_assign(m_workflow, "COMMANDS", "MATCH", "CANDIDATES"); + + mmi_workflow_output_assign(m_workflow, "COMMAND", "MATCH", "MATCHED_CANDIDATE"); + } + + void TearDown(void) override { + mmi_node_destroy(m_node_match); + mmi_node_destroy(m_node_asr); + mmi_node_destroy(m_node_mic); + + mmi_workflow_destroy(m_workflow); + } + + mmi_workflow_h m_workflow{nullptr}; + mmi_node_h m_node_mic{nullptr}; + mmi_node_h m_node_asr{nullptr}; + mmi_node_h m_node_match{nullptr}; +}; + +static bool compare_workflow_prototype(mmi_workflow_h first, mmi_workflow_h second) { + if (first == nullptr || second == nullptr) { + return false; + } + mmi_workflow_s *first_workflow = (mmi_workflow_s *)first; + mmi_workflow_s *second_workflow = (mmi_workflow_s *)second; + + if (first_workflow->type != second_workflow->type) { + return false; + } + + if (first_workflow->node_info_count != second_workflow->node_info_count) { + return false; + } + + for (size_t i = 0; i < first_workflow->node_info_count; i++) { + if (strcmp(first_workflow->node_infos[i].name, second_workflow->node_infos[i].name) != 0) { + return false; + } + + mmi_standard_node_type_e first_type; + mmi_standard_node_type_e second_type; + mmi_node_get_type(first_workflow->node_infos[i].node, &first_type); + mmi_node_get_type(second_workflow->node_infos[i].node, &second_type); + + if (first_type != second_type) { + return false; + } + + switch(first_type) { + case MMI_STANDARD_NODE_TYPE_SOURCE: + { + mmi_standard_node_source_type_e first_source_type; + mmi_standard_node_source_type_e second_source_type; + mmi_standard_node_get_source_type(first_workflow->node_infos[i].node, &first_source_type); + mmi_standard_node_get_source_type(second_workflow->node_infos[i].node, &second_source_type); + + if (first_source_type != second_source_type) { + return false; + } + } + break; + case MMI_STANDARD_NODE_TYPE_PROCESSOR: + { + mmi_standard_node_processor_type_e first_processor_type; + mmi_standard_node_processor_type_e second_processor_type; + mmi_standard_node_get_processor_type(first_workflow->node_infos[i].node, &first_processor_type); + mmi_standard_node_get_processor_type(second_workflow->node_infos[i].node, &second_processor_type); + + if (first_processor_type != second_processor_type) { + return false; + } + } + break; + case MMI_STANDARD_NODE_TYPE_LOGIC: + { + mmi_standard_node_logic_type_e first_logic_type; + mmi_standard_node_logic_type_e second_logic_type; + mmi_standard_node_get_logic_type(first_workflow->node_infos[i].node, &first_logic_type); + mmi_standard_node_get_logic_type(second_workflow->node_infos[i].node, &second_logic_type); + + if (first_logic_type != second_logic_type) { + return false; + } + } + break; + default: + return false; + } + } + + if (first_workflow->link_info_count != second_workflow->link_info_count) { + return false; + } + + for (size_t i = 0; i < first_workflow->link_info_count; i++) { + if (strcmp(first_workflow->link_infos[i].from_node_name, second_workflow->link_infos[i].from_node_name) != 0) { + return false; + } + if (strcmp(first_workflow->link_infos[i].from_port_name, second_workflow->link_infos[i].from_port_name) != 0) { + return false; + } + if (strcmp(first_workflow->link_infos[i].to_node_name, second_workflow->link_infos[i].to_node_name) != 0) { + return false; + } + if (strcmp(first_workflow->link_infos[i].to_port_name, second_workflow->link_infos[i].to_port_name) != 0) { + return false; + } + } + + if (first_workflow->attribute_assignment_info_count != second_workflow->attribute_assignment_info_count) { + return false; + } + + for (size_t i = 0; i < first_workflow->attribute_assignment_info_count; i++) { + if (strcmp(first_workflow->attribute_assignment_infos[i].attribute_name, second_workflow->attribute_assignment_infos[i].attribute_name) != 0) { + return false; + } + if (strcmp(first_workflow->attribute_assignment_infos[i].target_node_name, second_workflow->attribute_assignment_infos[i].target_node_name) != 0) { + return false; + } + if (strcmp(first_workflow->attribute_assignment_infos[i].target_attribute_name, second_workflow->attribute_assignment_infos[i].target_attribute_name) != 0) { + return false; + } + } + + if (first_workflow->output_assignment_info_count != second_workflow->output_assignment_info_count) { + return false; + } + + for (size_t i = 0; i < first_workflow->output_assignment_info_count; i++) { + if (strcmp(first_workflow->output_assignment_infos[i].output_name, second_workflow->output_assignment_infos[i].output_name) != 0) { + return false; + } + if (strcmp(first_workflow->output_assignment_infos[i].from_node_name, second_workflow->output_assignment_infos[i].from_node_name) != 0) { + return false; + } + if (strcmp(first_workflow->output_assignment_infos[i].from_port_name, second_workflow->output_assignment_infos[i].from_port_name) != 0) { + return false; + } + } + + return true; +} + +TEST_F(MMIWorkflowTest, TestCompareFunction_p1) { + mmi_workflow_h workflow = nullptr; + + mmi_workflow_create(&workflow); + + mmi_workflow_set_type(workflow, MMI_STANDARD_WORKFLOW_WAKEUPLESS_COMMAND); + + mmi_node_h node_mic = nullptr; + /* FIXME: node should be 'found' instead of 'created' */ + mmi_standard_node_create_source(MMI_STANDARD_NODE_SOURCE_TYPE_MIC_AMBIENT, &node_mic); + mmi_workflow_node_add(workflow, "MIC", node_mic); + + mmi_node_h node_asr = nullptr; + mmi_standard_node_create_processor(MMI_STANDARD_NODE_PROCESSOR_TYPE_ASR, &node_asr); + mmi_workflow_node_add(workflow, "ASR", node_asr); + + mmi_node_h node_match = nullptr; + mmi_standard_node_create_logic(MMI_STANDARD_NODE_LOGIC_TYPE_FIXED_STRING_MATCH, &node_match); + mmi_workflow_node_add(workflow, "MATCH", node_match); + + mmi_workflow_link_nodes_by_names(workflow, "MIC", "AUDIO", "ASR", "AUDIO"); + mmi_workflow_link_nodes_by_names(workflow, "ASR", "FINAL_RESULT", "MATCH", "TEXT"); + + mmi_workflow_attribute_assign(workflow, "COMMANDS", "MATCH", "CANDIDATES"); + + mmi_workflow_output_assign(workflow, "COMMAND", "MATCH", "MATCHED_CANDIDATE"); + + EXPECT_TRUE(compare_workflow_prototype(m_workflow, workflow)); + + mmi_node_destroy(node_match); + mmi_node_destroy(node_asr); + mmi_node_destroy(node_mic); + + mmi_workflow_destroy(workflow); +} + +TEST_F(MMIWorkflowTest, TestCompareFunction_p2) { + mmi_workflow_h workflow = nullptr; + + mmi_workflow_clone(m_workflow, &workflow); + + EXPECT_TRUE(compare_workflow_prototype(m_workflow, workflow)); + + mmi_workflow_destroy(workflow); +} + +TEST_F(MMIWorkflowTest, TestCompareFunction_n1) { + mmi_workflow_h workflow = nullptr; + + mmi_workflow_clone(m_workflow, &workflow); + + mmi_node_h node_regex_match = nullptr; + mmi_standard_node_create_logic(MMI_STANDARD_NODE_LOGIC_TYPE_REGEX_STRING_MATCH, &node_regex_match); + mmi_workflow_node_add(workflow, "REGEX_MATCH", node_regex_match); + + EXPECT_FALSE(compare_workflow_prototype(m_workflow, workflow)); + + mmi_node_destroy(node_regex_match); + + mmi_workflow_destroy(workflow); +} + +TEST_F(MMIWorkflowTest, MMIWorkflowCreateFromScript_p1) { + mmi_workflow_h workflow = NULL; + const char *script = NULL; + int ret = MMI_ERROR_NONE; + + std::string script_data = R"( +@workflow +name : WAKEUPLESS_COMMAND + +@node-list +[Source] MIC_AMBIENT as MIC +[Processor] ASR as ASR +[Logic] FIXED_STRING_MATCH as MATCH + +@link-list +MIC.AUDIO -> ASR.AUDIO +ASR.FINAL_RESULT -> MATCH.TEXT + +@attribute-list +MATCH.CANDIDATES as COMMANDS + +@output-list +MATCH.MATCHED_CANDIDATE as COMMAND + )"; + + std::vector lines; + std::stringstream ss(script_data); + std::string line; + while (std::getline(ss, line)) { + lines.push_back(line); + } + + WorkflowScriptParser parser; + parser.parse(lines, &workflow); + + EXPECT_TRUE(compare_workflow_prototype(m_workflow, workflow)); + + mmi_workflow_destroy(workflow); +} + diff --git a/tests/wait-helper.cpp b/tests/wait-helper.cpp deleted file mode 100644 index d5a387b..0000000 --- a/tests/wait-helper.cpp +++ /dev/null @@ -1,15 +0,0 @@ -#include "mmi.h" -#include "mmi-tests.h" -#include "mmi-ipc.h" - -#include - -void wait_for_connect() -{ - int timer = 0; - while (!mmi_ipc_is_connected() && timer < MAX_WAIT_TIME) - { - ecore_main_loop_iterate(); - timer++; - } -} \ No newline at end of file diff --git a/tidl/mmi.tidl b/tidl/mmi.tidl index ed51938..3d5f6f0 100644 --- a/tidl/mmi.tidl +++ b/tidl/mmi.tidl @@ -1,8 +1,10 @@ interface mmi { - void result_cb(int input_event_type, string result_out) delegate; + void result_cb(string source_name, bundle data) delegate; - int register_input_event(int input_event_type, result_cb callback); - - int activate_input_event(int input_event_type); - int deactivate_input_event(int input_event_type); + int workflow_instance_create(in int local_workflow_instance_id, in int workflow_type); + int workflow_instance_destroy(in int local_workflow_instance_id); + int workflow_instance_set_attribute(in int local_workflow_instance_id, in bundle attribute); + int workflow_instance_activate(in int local_workflow_instance_id); + int workflow_instance_deactivate(in int local_workflow_instance_id); + int workflow_instance_register_result_callback(in int local_workflow_instance_id, result_cb callback); } -- 2.7.4