diff options
77 files changed, 6007 insertions, 0 deletions
diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..91b4a80 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,6 @@ +[submodule "app-controller"] + path = app-controller + url = https://gerrit.automotivelinux.org/gerrit/apps/app-controller-submodule +[submodule "afb-helpers"] + path = afb-helpers + url = https://gerrit.automotivelinux.org/gerrit/apps/app-afb-helpers-submodule diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..f39acc4 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,21 @@ +########################################################################### +# Copyright 2018 IoT.bzh +# +# author: Romain Forlot <romain.forlot@iot.bzh> +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +########################################################################### + +CMAKE_MINIMUM_REQUIRED(VERSION 3.5.1) + +include(${CMAKE_CURRENT_SOURCE_DIR}/conf.d/cmake/config.cmake) diff --git a/License.txt b/License.txt new file mode 100644 index 0000000..d645695 --- /dev/null +++ b/License.txt @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/README.md b/README.md new file mode 100644 index 0000000..2a4a265 --- /dev/null +++ b/README.md @@ -0,0 +1,95 @@ +# 1. High Level Voice Service Capabilities (VSHL-CAPABILITIES) +This repository hosts the code for the AGL's high level voice service's capabilities publish/subscribe binding also known as VSHL-CAPABILITIES. +Please refer to the [architecture](https://confluence.automotivelinux.org/display/SPE/Speech+EG+Architecture) for more information. + +# 2. Build Dependencies and License Information + +During the build time, the following dependencies are fetched and run by the build system. Please refer to each of the individual entities for the particular licenses. +* [Google Test v1.8.0](https://github.com/google/googletest) when compiled with ENABLE_UNIT_TESTS option. + +# 3. Getting the Source Code +``` +export MY_PROJECTS_DIR = <Your Project Directory> +pushd $MY_PROJECTS_DIR +git clone --recursive https://gerrit.automotivelinux.org/gerrit/apps/agl-service-voice-high-capabilities +``` + +# 4. Renesas R-Car M3 board +## 4.1 Building VSHL Capabilities + +``` +pushd agl-service-voice-high-capabilities +mkdir build +pushd build +source /opt/agl-sdk/6.0.1-aarch64/environment-setup-aarch64-agl-linux +cmake .. +make autobuild +popd +./conf.d/autobuild/agl/autobuild package +``` +* The build output will be located at $MY_PROJECTS_DIR/agl-service-voice-high-capabilities/build/vshl-capabilities.wgt + +## 4.2 Running VSHL Capabilities +``` +# afm-util install vshl-capabilities.wgt +# afm-util start vshl-capabilities@1.0 +``` + +# 5. Ubuntu 16.04 +## 5.1 Building VSHL Capabilities + +``` +pushd agl-service-voice-high--capabilities +mkdir build +pushd build +cmake .. +make autobuild +popd +./conf.d/autobuild/linux/autobuild package +``` +To build the included unit tests modify the cmake step as following: +cmake .. -DENABLE_UNIT_TESTS=ON + +## 5.2 Running VSHL Capabilities +``` +afb-daemon --port=1111 --name=afb-vshl-capabilities --workdir=$MY_PROJECTS_DIR/agl-service-voice-high-capabilities/build/package --ldpaths=lib --roothttp=htdocs --token= -vvv +``` + +# 6. Running the Unit Tests +## 6.1 Ubuntu 16.04 +``` +pushd agl-service-voice-high-capabilities/ +./build/src/plugins/vshl-capabilities-api_Test +popd +``` + +# 7. Testing VSHL Capabilities +* The binding can be tested by launching the HTML5 sample application that is bundled with the package in a browser. + +``` +http://localhost:1111 +``` + +# 8. Contributing code +Before contributing the source, its recommended to format the code with clang-format. This is done automatically during commit step if following instructions are followed. +**Prerequisite**: Install clang-format-6.0 or greater. +There are following 2 options. + +* Before commit, manually run clang-format on the file (It will use local .clang-format file for checking the rules) +``` +clang-format -i <path to source file> +``` + +* Setup clang-format as pre-commit git hook. This is one time step after you clone the repo +``` +cd ${VSHL_CAPABILITIES_ROOT} +cp tools/pre-commit .git/hooks/pre-commit +``` + +* With the hook in place, everytime you try to commit, it will check the format and disallow commit if format doesn't abide by the .clang-format rules. +It will also give you the option to apply a patch (it creates a patch in /tmp folder) to make the source abide by the .clang-format rules. Apply the patch and proceed to commit +``` +git apply /tmp/<patch> +git add <source files> +git commit +```
\ No newline at end of file diff --git a/afb-helpers b/afb-helpers new file mode 160000 +Subproject fd4fe7525aaf737597e004b0a2b7d91bf18c9f1 diff --git a/app-controller b/app-controller new file mode 160000 +Subproject f8531f98503e72238253d4faff634293ad4d194 diff --git a/autobuild/agl/autobuild b/autobuild/agl/autobuild new file mode 100755 index 0000000..a832db4 --- /dev/null +++ b/autobuild/agl/autobuild @@ -0,0 +1,75 @@ +#!/usr/bin/make -f +# Copyright (C) 2015 - 2018 "IoT.bzh" +# Author "Romain Forlot" <romain.forlot@iot.bzh> +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +THISFILE := $(lastword $(MAKEFILE_LIST)) +BUILD_DIR := $(abspath $(dir $(THISFILE))/../../build) +DEST := ${BUILD_DIR} + +.PHONY: all clean distclean configure build package help update + +all: help + +help: + @echo "List of targets available:" + @echo "" + @echo "- all" + @echo "- clean" + @echo "- distclean" + @echo "- configure" + @echo "- build: compilation, link and prepare files for package into a widget" + @echo "- package: output a widget file '*.wgt'" + @echo "- install: install in your ${CMAKE_INSTALL_DIR} directory" + @echo "" + @echo "Usage: ./autobuild/agl/autobuild package DEST=${HOME}/opt" + @echo "Don't use your build dir as DEST as wgt file is generated at this location" + +update: configure + @cmake --build ${BUILD_DIR} --target autobuild + +clean: + @([ -d ${BUILD_DIR} ] && make -C ${BUILD_DIR} ${CLEAN_ARGS} clean) || echo Nothing to clean + +distclean: + @rm -rf ${BUILD_DIR} + +configure: + @[ -d ${BUILD_DIR} ] || mkdir -p ${BUILD_DIR} + @[ -f ${BUILD_DIR}/Makefile ] || (cd ${BUILD_DIR} && cmake ${CONFIGURE_ARGS} ..) + +build: configure + @cmake --build ${BUILD_DIR} ${BUILD_ARGS} --target all + +package: build + @mkdir -p ${BUILD_DIR}/$@/bin + @mkdir -p ${BUILD_DIR}/$@/etc + @mkdir -p ${BUILD_DIR}/$@/lib + @mkdir -p ${BUILD_DIR}/$@/htdocs + @mkdir -p ${BUILD_DIR}/$@/var + @cmake --build ${BUILD_DIR} ${PACKAGE_ARGS} --target widget + @mkdir -p ${DEST} && cp ${BUILD_DIR}/*.wgt ${DEST} + +package-test: build + @mkdir -p ${BUILD_DIR}/$@/bin + @mkdir -p ${BUILD_DIR}/$@/etc + @mkdir -p ${BUILD_DIR}/$@/lib + @mkdir -p ${BUILD_DIR}/$@/htdocs + @mkdir -p ${BUILD_DIR}/$@/var + @cmake --build ${BUILD_DIR} ${PACKAGE_ARGS} --target widget + @cmake --build ${BUILD_DIR} ${PACKAGE_ARGS} --target test_widget + @mkdir -p ${DEST} && cp ${BUILD_DIR}/*.wgt ${DEST} + +install: build + @cmake --build ${BUILD_DIR} ${INSTALL_ARGS} --target install diff --git a/autobuild/linux/autobuild b/autobuild/linux/autobuild new file mode 100755 index 0000000..a832db4 --- /dev/null +++ b/autobuild/linux/autobuild @@ -0,0 +1,75 @@ +#!/usr/bin/make -f +# Copyright (C) 2015 - 2018 "IoT.bzh" +# Author "Romain Forlot" <romain.forlot@iot.bzh> +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +THISFILE := $(lastword $(MAKEFILE_LIST)) +BUILD_DIR := $(abspath $(dir $(THISFILE))/../../build) +DEST := ${BUILD_DIR} + +.PHONY: all clean distclean configure build package help update + +all: help + +help: + @echo "List of targets available:" + @echo "" + @echo "- all" + @echo "- clean" + @echo "- distclean" + @echo "- configure" + @echo "- build: compilation, link and prepare files for package into a widget" + @echo "- package: output a widget file '*.wgt'" + @echo "- install: install in your ${CMAKE_INSTALL_DIR} directory" + @echo "" + @echo "Usage: ./autobuild/agl/autobuild package DEST=${HOME}/opt" + @echo "Don't use your build dir as DEST as wgt file is generated at this location" + +update: configure + @cmake --build ${BUILD_DIR} --target autobuild + +clean: + @([ -d ${BUILD_DIR} ] && make -C ${BUILD_DIR} ${CLEAN_ARGS} clean) || echo Nothing to clean + +distclean: + @rm -rf ${BUILD_DIR} + +configure: + @[ -d ${BUILD_DIR} ] || mkdir -p ${BUILD_DIR} + @[ -f ${BUILD_DIR}/Makefile ] || (cd ${BUILD_DIR} && cmake ${CONFIGURE_ARGS} ..) + +build: configure + @cmake --build ${BUILD_DIR} ${BUILD_ARGS} --target all + +package: build + @mkdir -p ${BUILD_DIR}/$@/bin + @mkdir -p ${BUILD_DIR}/$@/etc + @mkdir -p ${BUILD_DIR}/$@/lib + @mkdir -p ${BUILD_DIR}/$@/htdocs + @mkdir -p ${BUILD_DIR}/$@/var + @cmake --build ${BUILD_DIR} ${PACKAGE_ARGS} --target widget + @mkdir -p ${DEST} && cp ${BUILD_DIR}/*.wgt ${DEST} + +package-test: build + @mkdir -p ${BUILD_DIR}/$@/bin + @mkdir -p ${BUILD_DIR}/$@/etc + @mkdir -p ${BUILD_DIR}/$@/lib + @mkdir -p ${BUILD_DIR}/$@/htdocs + @mkdir -p ${BUILD_DIR}/$@/var + @cmake --build ${BUILD_DIR} ${PACKAGE_ARGS} --target widget + @cmake --build ${BUILD_DIR} ${PACKAGE_ARGS} --target test_widget + @mkdir -p ${DEST} && cp ${BUILD_DIR}/*.wgt ${DEST} + +install: build + @cmake --build ${BUILD_DIR} ${INSTALL_ARGS} --target install diff --git a/conf.d/CMakeLists.txt b/conf.d/CMakeLists.txt new file mode 100644 index 0000000..3beb009 --- /dev/null +++ b/conf.d/CMakeLists.txt @@ -0,0 +1,20 @@ +########################################################################### +# Copyright 2015, 2016, 2017 IoT.bzh +# +# author: Fulup Ar Foll <rfulup@iot.bzh> +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +########################################################################### + +# This component should be included as a submodule and wont compile as standalone +project_subdirs_add() diff --git a/conf.d/cmake/00-debian-osconfig.cmake b/conf.d/cmake/00-debian-osconfig.cmake new file mode 100644 index 0000000..2ce0ad3 --- /dev/null +++ b/conf.d/cmake/00-debian-osconfig.cmake @@ -0,0 +1 @@ +list(APPEND PKG_REQUIRED_LIST lua-5.3>=5.3) diff --git a/conf.d/cmake/00-default-osconfig.cmake b/conf.d/cmake/00-default-osconfig.cmake new file mode 100644 index 0000000..a2b9325 --- /dev/null +++ b/conf.d/cmake/00-default-osconfig.cmake @@ -0,0 +1 @@ +list(APPEND PKG_REQUIRED_LIST lua>=5.3) diff --git a/conf.d/cmake/config.cmake b/conf.d/cmake/config.cmake new file mode 100644 index 0000000..da2ce59 --- /dev/null +++ b/conf.d/cmake/config.cmake @@ -0,0 +1,221 @@ +########################################################################### +# Copyright 2018 IoT.bzh +# +# author: Sebastien Douheret <sebastien@iot.bzh> +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +########################################################################### + +# Project Info +# ------------------ +set(PROJECT_NAME vshl-capabilities) +set(PROJECT_VERSION "0.1") +set(PROJECT_PRETTY_NAME "High Level Voice Service capabilities APIs") +set(PROJECT_DESCRIPTION "Binding that provides high level voice service capabilities subscribe and publish APIs to AGL apps.") +set(PROJECT_ICON "icon.png") +set(PROJECT_AUTHOR "Naveen Bobbili") +set(PROJECT_AUTHOR_MAIL "nbobbili@amazon.com") +set(PROJECT_LICENSE "APL2.0") +set(PROJECT_LANGUAGES "CXX") + +# Where are stored the project configuration files +# relative to the root project directory +set(PROJECT_CMAKE_CONF_DIR "conf.d") + +# Where are stored your external libraries for your project. This is 3rd party library that you don't maintain +# but used and must be built and linked. +# set(PROJECT_LIBDIR "libs") + +# Which directories inspect to find CMakeLists.txt target files +# set(PROJECT_SRC_DIR_PATTERN "*") + +# Compilation Mode (DEBUG, RELEASE) +# ---------------------------------- +set(CMAKE_BUILD_TYPE "RELEASE") +#set(USE_EFENCE 1) + +# Helpers Submodule parameters +# set(AFB_HELPERS_QTWSCLIENT OFF CACHE BOOL "Adds QT5 WebSocket helpers from submodule") + +# Kernel selection if needed. You can choose between a +# mandatory version to impose a minimal version. +# Or check Kernel minimal version and just print a Warning +# about missing features and define a preprocessor variable +# to be used as preprocessor condition in code to disable +# incompatibles features. Preprocessor define is named +# KERNEL_MINIMAL_VERSION_OK. +# +# NOTE*** FOR NOW IT CHECKS KERNEL Yocto environment and +# Yocto SDK Kernel version. +# ----------------------------------------------- +#set (kernel_mandatory_version 4.8) +#set (kernel_minimal_version 4.8) + +# Compiler selection if needed. Impose a minimal version. +# ----------------------------------------------- +set (gcc_minimal_version 4.9) + +# PKG_CONFIG required packages +# ----------------------------- +set (PKG_REQUIRED_LIST + json-c + afb-daemon +) + +# Prefix path where will be installed the files +# Default: /usr/local (need root permission to write in) +# ------------------------------------------------------ +#set(CMAKE_INSTALL_PREFIX $ENV{HOME}/opt) + +# Customize link option +# ----------------------------- +#list(APPEND link_libraries -an-option) + +# Compilation options definition +# Use CMake generator expressions to specify only for a specific language +# Values are prefilled with default options that is currently used. +# Either separate options with ";", or each options must be quoted separately +# DO NOT PUT ALL OPTION QUOTED AT ONCE , COMPILATION COULD FAILED ! +# ---------------------------------------------------------------------------- +set(COMPILE_OPTIONS + -Wno-missing-field-initializers + -Wno-format-security +# -Wall +# -Wextra +# -Wconversion +# -Wno-unused-parameter +# -Wno-sign-compare +# -Wno-sign-conversion +# -Werror=maybe-uninitialized +# -Werror=implicit-function-declaration +# -ffunction-sections +# -fdata-sections +# -fPIC + CACHE STRING "Compilation flags") +#set(C_COMPILE_OPTIONS "" CACHE STRING "Compilation flags for C language.") +#set(CXX_COMPILE_OPTIONS "" CACHE STRING "Compilation flags for C++ language.") +#set(PROFILING_COMPILE_OPTIONS +# -g +# -O0 +# -pg +# -Wp,-U_FORTIFY_SOURCE +# CACHE STRING "Compilation flags for PROFILING build type.") +#set(DEBUG_COMPILE_OPTIONS +# -g +# -ggdb +# -Wp,-U_FORTIFY_SOURCE +# CACHE STRING "Compilation flags for DEBUG build type.") +#set(CCOV_COMPILE_OPTIONS +# -g +# -O2 +# --coverage +# CACHE STRING "Compilation flags for CCOV build type.") +#set(RELEASE_COMPILE_OPTIONS +# -g +# -O2 +# CACHE STRING "Compilation flags for RELEASE build type.") + +set(CONTROL_SUPPORT_LUA 1) +# Search Paths: Build directory -> CMake installation path -> AFM installation path +add_definitions(-DCONTROL_PLUGIN_PATH="${CMAKE_BINARY_DIR}/package/lib/plugins:${CMAKE_INSTALL_PREFIX}/${PROJECT_NAME}/lib/plugins:/var/local/lib/afm/applications/${PROJECT_NAME}/${PROJECT_VERSION}/lib/plugins") +add_definitions(-DCONTROL_CONFIG_PATH="${CMAKE_BINARY_DIR}/package/etc:${CMAKE_INSTALL_PREFIX}/${PROJECT_NAME}:/var/local/lib/afm/applications/${PROJECT_NAME}") +#add_definitions(-DCONTROL_LUA_PATH="${CMAKE_SOURCE_DIR}/conf.d/project/lua.d:${CMAKE_INSTALL_PREFIX}/${PROJECT_NAME}/var:${CMAKE_INSTALL_PREFIX}/${PROJECT_NAME}") +add_definitions(-DCTL_PLUGIN_MAGIC=1286576532) +add_definitions(-DUSE_API_DYN=1 -DAFB_BINDING_VERSION=3 -DAFB_BINDING_WANT_DYNAPI) + +# (BUG!!!) as PKG_CONFIG_PATH does not work [should be an env variable] +# --------------------------------------------------------------------- +#set(CMAKE_PREFIX_PATH ${CMAKE_INSTALL_PREFIX}/lib64/pkgconfig ${CMAKE_INSTALL_PREFIX}/lib/pkgconfig) +#set(LD_LIBRARY_PATH ${CMAKE_INSTALL_PREFIX}/lib64 ${CMAKE_INSTALL_PREFIX}/lib) + +# Optional location for config.xml.in +# ----------------------------------- +#set(WIDGET_ICON "\"conf.d/wgt/${PROJECT_ICON}\"" CACHE PATH "Path to the widget icon") +set(WIDGET_CONFIG_TEMPLATE "${CMAKE_SOURCE_DIR}/conf.d/wgt/config.xml.in" CACHE PATH "Path to widget config file template (config.xml.in)") + +# Mandatory widget Mimetype specification of the main unit +# -------------------------------------------------------------------------- +# Choose between : +#- text/html : HTML application, +# content.src designates the home page of the application +# +#- application/vnd.agl.native : AGL compatible native, +# content.src designates the relative path of the binary. +# +# - application/vnd.agl.service: AGL service, content.src is not used. +# +#- ***application/x-executable***: Native application, +# content.src designates the relative path of the binary. +# For such application, only security setup is made. +# +set(WIDGET_TYPE application/vnd.agl.service) + +# Mandatory Widget entry point file of the main unit +# -------------------------------------------------------------- +# This is the file that will be executed, loaded, +# at launch time by the application framework. +# +set(WIDGET_ENTRY_POINT lib/afb-vshl-capabilities.so) + +# Optional dependencies order +# --------------------------- +#set(EXTRA_DEPENDENCIES_ORDER) + +# Optional Extra global include path +# ----------------------------------- +#set(EXTRA_INCLUDE_DIRS) + +# Optional extra libraries +# ------------------------- +#set(EXTRA_LINK_LIBRARIES) + +# Optional force binding installation +# ------------------------------------ +# set(BINDINGS_INSTALL_PREFIX PrefixPath ) + +# Optional force binding Linking flag +# ------------------------------------ +# set(BINDINGS_LINK_FLAG LinkOptions ) + +# Optional force package prefix generation, like widget +# ----------------------------------------------------- +# set(PKG_PREFIX DestinationPath) + +# Optional Application Framework security token +# and port use for remote debugging. +#------------------------------------------------------------ +set(AFB_TOKEN "" CACHE PATH "Default binder security token") +set(AFB_REMPORT "1111" CACHE PATH "Default binder listening port") + +# Print a helper message when every thing is finished +# ---------------------------------------------------- +set(CLOSING_MESSAGE "Typical binding launch: \ +afb-daemon --port=${AFB_REMPORT} --name=afb-speech --workdir=${CMAKE_BINARY_DIR}/package \ +--ldpaths=lib --roothttp=htdocs --token=\"${AFB_TOKEN}\" -vvv") + +set(PACKAGE_MESSAGE "Install widget file using in the target : afm-util install ${PROJECT_NAME}.wgt") + +# Optional schema validator about now only XML, LUA and JSON +# are supported +#------------------------------------------------------------ +#set(LUA_CHECKER "luac" "-p" CACHE STRING "LUA compiler") +#set(XML_CHECKER "xmllint" CACHE STRING "XML linter") +#set(JSON_CHECKER "json_verify" CACHE STRING "JSON linter") + +# This include is mandatory and MUST happens at the end +# of this file, else you expose you to unexpected behavior +# +# This CMake module could be found at the following url: +# https://gerrit.automotivelinux.org/gerrit/#/admin/projects/src/cmake-apps-module +# ----------------------------------------------------------- +include(CMakeAfbTemplates)
\ No newline at end of file diff --git a/conf.d/project/CMakeLists.txt b/conf.d/project/CMakeLists.txt new file mode 100644 index 0000000..3beb009 --- /dev/null +++ b/conf.d/project/CMakeLists.txt @@ -0,0 +1,20 @@ +########################################################################### +# Copyright 2015, 2016, 2017 IoT.bzh +# +# author: Fulup Ar Foll <rfulup@iot.bzh> +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +########################################################################### + +# This component should be included as a submodule and wont compile as standalone +project_subdirs_add() diff --git a/conf.d/project/etc/CMakeLists.txt b/conf.d/project/etc/CMakeLists.txt new file mode 100644 index 0000000..fc44330 --- /dev/null +++ b/conf.d/project/etc/CMakeLists.txt @@ -0,0 +1,32 @@ +########################################################################### +# Copyright 2017 IoT.bzh +# +# author: Fulup Ar Foll <fulup@iot.bzh> +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +########################################################################### + +################################################## +# Control Policy Config file +################################################## +PROJECT_TARGET_ADD(vshl-capabilities-api-config) + + file(GLOB CONF_FILES "*.json") + + add_input_files("${CONF_FILES}") + + SET_TARGET_PROPERTIES( + ${TARGET_NAME} PROPERTIES + LABELS "BINDING-CONFIG" + OUTPUT_NAME ${TARGET_NAME} + )
\ No newline at end of file diff --git a/conf.d/project/etc/vshl-capabilities-api.json b/conf.d/project/etc/vshl-capabilities-api.json new file mode 100644 index 0000000..04706b0 --- /dev/null +++ b/conf.d/project/etc/vshl-capabilities-api.json @@ -0,0 +1,52 @@ +{ + "$schema": "http://iot.bzh/download/public/schema/json/ctl-schema.json", + "metadata": { + "uid": "vshl-capabilities", + "version": "0.1", + "api": "vshl-capabilities", + "info": "High Level Voice Service Capabilities APIs" + }, + + "plugins": [{ + "uid": "vshl-capabilities", + "info": "Plugin to handle high level voice service capabilities API implementation", + "libs": [ + "vshl-capabilities-api.ctlso" + ] + }], + + + "controls": [{ + "uid": "guiMetadata/publish", + "privileges": "urn:AGL:permission:vshl-capabilities:guiMetadata:public", + "action": "plugin://vshl-capabilities#guiMetadataPublish" + }, { + "uid": "guiMetadata/subscribe", + "privileges": "urn:AGL:permission:vshl-capabilities:guiMetadata:public", + "action": "plugin://vshl-capabilities#guiMetadataSubscribe" + }, { + "uid": "phonecontrol/publish", + "privileges": "urn:AGL:permission:vshl-capabilities:phonecontrol:public", + "action": "plugin://vshl-capabilities#phonecontrolPublish" + }, { + "uid": "phonecontrol/subscribe", + "privileges": "urn:AGL:permission:vshl-capabilities:phonecontrol:public", + "action": "plugin://vshl-capabilities#phonecontrolSubscribe" + }, { + "uid": "navigation/publish", + "privileges": "urn:AGL:permission:vshl-capabilities:navigation:public", + "action": "plugin://vshl-capabilities#navigationPublish" + }, { + "uid": "navigation/subscribe", + "privileges": "urn:AGL:permission:vshl-capabilities:navigation:public", + "action": "plugin://vshl-capabilities#navigationSubscribe" + }, { + "uid": "playbackcontroller/publish", + "privileges": "urn:AGL:permission:vshl-capabilities:playbackcontroller:public", + "action": "plugin://vshl-capabilities#playbackControllerPublish" + }, { + "uid": "playbackcontroller/subscribe", + "privileges": "urn:AGL:permission:vshl-capabilities:playbackcontroller:public", + "action": "plugin://vshl-capabilities#playbackControllerSubscribe" + }] +}
\ No newline at end of file diff --git a/conf.d/wgt/config.xml.in b/conf.d/wgt/config.xml.in new file mode 100644 index 0000000..fed5bdf --- /dev/null +++ b/conf.d/wgt/config.xml.in @@ -0,0 +1,23 @@ +<?xml version="1.0" encoding="UTF-8"?> +<widget xmlns="http://www.w3.org/ns/widgets" id="@PROJECT_NAME@" version="0.1"> + <name>@PROJECT_NAME@</name> + <icon src="@PROJECT_ICON@"/> + <content src="@WIDGET_ENTRY_POINT@" type="@WIDGET_TYPE@"/> + <description>@PROJECT_DESCRIPTION@</description> + <author>@PROJECT_AUTHOR@ <@PROJECT_AUTHOR_MAIL@></author> + <license>@PROJECT_LICENSE@</license> + <feature name="urn:AGL:widget:provided-api"> + <param name="vshl-capabilities" value="ws" /> + </feature> + <feature name="urn:AGL:widget:required-binding"> + <param name="@WIDGET_ENTRY_POINT@" value="local" /> + </feature> + <feature name="urn:AGL:widget:required-api"> + <param name="afm-main" value="ws" /> + <param name="homescreen" value="ws" /> + </feature> + <feature name="urn:AGL:widget:required-permission"> + <param name="urn:AGL:permission::public:hidden" value="required" /> + </feature> + +</widget>
\ No newline at end of file diff --git a/htdocs/AFB.js b/htdocs/AFB.js new file mode 100644 index 0000000..2cf3aec --- /dev/null +++ b/htdocs/AFB.js @@ -0,0 +1,217 @@ +/* + * Copyright (C) 2017, 2018 "IoT.bzh" + * Author: José Bollo <jose.bollo@iot.bzh> + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +AFB = function(base, initialtoken){ + +if (typeof base != "object") + base = { base: base, token: initialtoken }; + +var initial = { + base: base.base || "api", + token: base.token || initialtoken || "HELLO", + host: base.host || window.location.host, + url: base.url || undefined +}; + +var urlws = initial.url || "ws://"+initial.host+"/"+initial.base; + +/*********************************************/ +/**** ****/ +/**** AFB_context ****/ +/**** ****/ +/*********************************************/ +var AFB_context; +{ + var UUID = undefined; + var TOKEN = initial.token; + + var context = function(token, uuid) { + this.token = token; + this.uuid = uuid; + } + + context.prototype = { + get token() {return TOKEN;}, + set token(tok) {if(tok) TOKEN=tok;}, + get uuid() {return UUID;}, + set uuid(id) {if(id) UUID=id;} + }; + + AFB_context = new context(); +} +/*********************************************/ +/**** ****/ +/**** AFB_websocket ****/ +/**** ****/ +/*********************************************/ +var AFB_websocket; +{ + var CALL = 2; + var RETOK = 3; + var RETERR = 4; + var EVENT = 5; + + var PROTO1 = "x-afb-ws-json1"; + + AFB_websocket = function(on_open, on_abort) { + var u = urlws, p = '?'; + if (AFB_context.token) { + u = u + '?x-afb-token=' + AFB_context.token; + p = '&'; + } + if (AFB_context.uuid) + u = u + p + 'x-afb-uuid=' + AFB_context.uuid; + this.ws = new WebSocket(u, [ PROTO1 ]); + this.url = u; + this.pendings = {}; + this.awaitens = {}; + this.counter = 0; + this.ws.onopen = onopen.bind(this); + this.ws.onerror = onerror.bind(this); + this.ws.onclose = onclose.bind(this); + this.ws.onmessage = onmessage.bind(this); + this.onopen = on_open; + this.onabort = on_abort; + } + + function onerror(event) { + var f = this.onabort; + if (f) { + delete this.onopen; + delete this.onabort; + f(this); + } + this.onerror && this.onerror(this); + } + + function onopen(event) { + var f = this.onopen; + delete this.onopen; + delete this.onabort; + f && f(this); + } + + function onclose(event) { + var err = { + jtype: 'afb-reply', + request: { + status: 'disconnected', + info: 'server hung up' + } + }; + for (var id in this.pendings) { + try { this.pendings[id][1](err); } catch (x) {/*NOTHING*/} + } + this.pendings = {}; + this.onclose && this.onclose(); + } + + function fire(awaitens, name, data) { + var a = awaitens[name]; + if (a) + a.forEach(function(handler){handler(data);}); + var i = name.indexOf("/"); + if (i >= 0) { + a = awaitens[name.substring(0,i)]; + if (a) + a.forEach(function(handler){handler(data);}); + } + a = awaitens["*"]; + if (a) + a.forEach(function(handler){handler(data);}); + } + + function reply(pendings, id, ans, offset) { + if (id in pendings) { + var p = pendings[id]; + delete pendings[id]; + try { p[offset](ans); } catch (x) {/*TODO?*/} + } + } + + function onmessage(event) { + var obj = JSON.parse(event.data); + var code = obj[0]; + var id = obj[1]; + var ans = obj[2]; + AFB_context.token = obj[3]; + switch (code) { + case RETOK: + reply(this.pendings, id, ans, 0); + break; + case RETERR: + reply(this.pendings, id, ans, 1); + break; + case EVENT: + default: + fire(this.awaitens, id, ans); + break; + } + } + + function close() { + this.ws.close(); + this.ws.onopen = + this.ws.onerror = + this.ws.onclose = + this.ws.onmessage = + this.onopen = + this.onabort = function(){}; + } + + function call(method, request, callid) { + return new Promise((function(resolve, reject){ + var id, arr; + if (callid) { + id = String(callid); + if (id in this.pendings) + throw new Error("pending callid("+id+") exists"); + } else { + do { + id = String(this.counter = 4095 & (this.counter + 1)); + } while (id in this.pendings); + } + this.pendings[id] = [ resolve, reject ]; + arr = [CALL, id, method, request ]; + if (AFB_context.token) arr.push(AFB_context.token); + this.ws.send(JSON.stringify(arr)); + }).bind(this)); + } + + function onevent(name, handler) { + var id = name; + var list = this.awaitens[id] || (this.awaitens[id] = []); + list.push(handler); + } + + AFB_websocket.prototype = { + close: close, + call: call, + onevent: onevent + }; +} +/*********************************************/ +/**** ****/ +/**** ****/ +/**** ****/ +/*********************************************/ +return { + context: AFB_context, + ws: AFB_websocket, + url: urlws +}; +}; + diff --git a/htdocs/binding.css b/htdocs/binding.css new file mode 100644 index 0000000..9ee1303 --- /dev/null +++ b/htdocs/binding.css @@ -0,0 +1,99 @@ +body.page-content { + height: 100%; + width: auto; + margin-top: 20px; + background-size: cover; + background-position: center; +} + +img { + float: right; +} + +ol { + display: flex; + flex-direction: column; +} + +#question, +#output, +#outevt { + white-space: pre-wrap; +} + +div.row { + display: flex; + flex-direction: row; +} + +div.col1 { + flex-basis: 0; + flex-grow: 1; + width: 100%; +} + +div.col2 { + flex-basis: 0; + flex-grow: 1; + width: 30ch; +} + +button { + margin-right: 10px; + padding: 6px 8px; + font-size: large; +} + +pre { + outline: 1px solid #ccc; + padding: 5px; + margin: 5px; + background-color: white; + opacity: 0.85; + min-height: 5pc; + max-height: 5pc; + overflow: auto; +} + +.string { + color: green; +} + +.number { + color: darkorange; +} + +.boolean { + color: blue; +} + +.null { + color: magenta; +} + +.key { + color: red; +} + +dialog { + padding: 0; + border: 0; + border-radius: 0.6rem; + box-shadow: 0 0 1em black; +} + +dialog::backdrop { + /* make the backdrop a semi-transparent black */ + background-color: rgba(0, 0, 0, 0.4); +} + +h3.dialogheader { + background-color: lightgreen; + padding: 1ch; +} + +footer { + display: flex; + align-items: center; + justify-content: center; +}
\ No newline at end of file diff --git a/htdocs/binding.js b/htdocs/binding.js new file mode 100644 index 0000000..9de9608 --- /dev/null +++ b/htdocs/binding.js @@ -0,0 +1,276 @@ +var afbVshlCapabilities; +var ws; +var evtIdx = 0; +var count = 0; + +//********************************************** +// Logger +//********************************************** +var log = { + command: function (url, api, verb, query) { + console.log("subscribe api=" + api + " verb=" + verb + " query=", query); + var question = url + "/" + api + "/" + verb + "?query=" + JSON.stringify(query); + log._write("question", count + ": " + log.syntaxHighlight(question)); + }, + + event: function (obj) { + console.log("gotevent:" + JSON.stringify(obj)); + log._write("outevt", (evtIdx++) + ": " + JSON.stringify(obj)); + }, + + reply: function (obj) { + console.log("replyok:" + JSON.stringify(obj)); + log._write("output", count + ": OK: " + log.syntaxHighlight(obj)); + }, + + error: function (obj) { + console.log("replyerr:" + JSON.stringify(obj)); + log._write("output", count + ": ERROR: " + log.syntaxHighlight(obj)); + }, + + _write: function (element, msg) { + var el = document.getElementById(element); + el.innerHTML += msg + '\n'; + + // auto scroll down + setTimeout(function () { + el.scrollTop = el.scrollHeight; + }, 100); + }, + + syntaxHighlight: function (json) { + if (typeof json !== 'string') { + json = JSON.stringify(json, undefined, 2); + } + json = json.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>'); + return json.replace(/("(\\u[a-zA-Z0-9]{4}|\\[^u]|[^\\"])*"(\s*:)?|\b(true|false|null)\b|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?)/g, function (match) { + var cls = 'number'; + if (/^"/.test(match)) { + if (/:$/.test(match)) { + cls = 'key'; + } else { + cls = 'string'; + } + } else if (/true|false/.test(match)) { + cls = 'boolean'; + } else if (/null/.test(match)) { + cls = 'null'; + } + return '<span class="' + cls + '">' + match + '</span>'; + }); + }, +}; + +//********************************************** +// Generic function to call binder +//*********************************************** +function callbinder(url, api, verb, query) { + log.command(url, api, verb, query); + + // ws.call return a Promise + return ws.call(api + '/' + verb, query) + .then(function (res) { + log.reply(res); + count++; + return res; + }) + .catch(function (err) { + log.reply(err); + count++; + throw err; + }); +}; + + +//********************************************** +// connect - establish Websocket connection +//********************************************** + +function connect(elemID, api, verb, query) { + connectVshlCapabilities(elemID, api, verb, query); +} + +//********************************************** + +// +var lastCallId; + +function onCapabilityEvent(eventDataObj) { + log.event(eventDataObj); + if (eventDataObj.event == "vshl-capabilities/dial") { + lastCallId = JSON.parse(eventDataObj.data).callId; + console.log("New Dial Directive received. Sending ringing state back"); + // send ringing state back. + triggerCallStateChangedAction("OUTBOUND_RINGING"); + } +} + +function triggerPhoneConnectionStateChanged(newState) { + var paramsJson = { + "action" : "connection_state_changed", + "payload": { + "state" : newState + } + } + callbinder(afbVshlCapabilities.url, 'vshl-capabilities', 'phonecontrol/publish', paramsJson); +} + +function triggerCallStateChangedAction(newCallstate) { + var query = { + "action": "call_state_changed", + "payload": { + "callId": lastCallId, + "state": newCallstate + } + } + callbinder(afbVshlCapabilities.url, 'vshl-capabilities', 'phonecontrol/publish', query); +} + +function guid() { + function s4() { + return Math.floor((1 + Math.random()) * 0x10000) + .toString(16) + .substring(1); + } + return s4() + s4() + '-' + s4() + '-' + s4() + '-' + s4() + '-' + s4() + s4() + s4(); +} + +function triggerCallStateInBoundRingingAction() { + var callId = guid(); + var state = "INBOUND_RINGING"; + var query = { + "action": "call_state_changed", + "payload": { + "callId": callId, + "state": state + } + } + callbinder(afbVshlCapabilities.url, 'vshl-capabilities', 'phonecontrol/publish', query); +} + +function connectVshlCapabilities(elemID, api, verb, query) { + + function onopen() { + document.getElementById("main").style.visibility = "visible"; + document.getElementById("connected").innerHTML = "VSHL Capabilities Binder WS Active"; + document.getElementById("connected").style.background = "lightgreen"; + ws.onevent("*", onCapabilityEvent); + } + + function onabort() { + document.getElementById("main").style.visibility = "hidden"; + document.getElementById("connected").innerHTML = "Connected Closed"; + document.getElementById("connected").style.background = "red"; + } + + var urlparams = { + base: "api", + token: "HELLO", + }; + const vshlCapabilitiesAddressInput = document.getElementById('vshl-capabilities-address'); + urlparams.host = vshlCapabilitiesAddressInput.value; + + afbVshlCapabilities = new AFB(urlparams, "HELLO"); + ws = new afbVshlCapabilities.ws(onopen, onabort); +} + +function clearPre(preId) { + const pre = document.getElementById(preId); + while (pre && pre.firstChild) { + pre.removeChild(pre.firstChild); + } +} + +function showTemplateUIEventChooserDialog() { + const modal = document.getElementById('templateui-event-chooser'); + const subscribeBtn = document.getElementById('templateui-subscribe-btn'); + + subscribeBtn.addEventListener('click', (evt) => { + const renderTemplate = document.getElementById('render_template').checked; + const clearTemplate = document.getElementById('clear_template').checked; + const renderPlayerInfo = document.getElementById('render_player_info').checked; + const clearPlayerInfo = document.getElementById('clear_player_info').checked; + + const query = {"actions":[]}; + + if (renderTemplate) + query.actions.push('render_template'); + if (clearTemplate) + query.actions.push('clear_template'); + if (renderPlayerInfo) + query.actions.push('render_player_info'); + if (clearPlayerInfo) + query.actions.push('clear_player_info'); + + callbinder(afbVshlCapabilities.url, 'vshl-capabilities', 'guiMetadata/subscribe', query); + modal.close(); + }); + + // makes modal appear (adds `open` attribute) + modal.showModal(); +} + +function showPhoneControlEventChooserDialog() { + const modal = document.getElementById('phonecontrol-event-chooser'); + const subscribeBtn = document.getElementById('phonecontrol-subscribe-btn'); + + subscribeBtn.addEventListener('click', (evt) => { + const dial = document.getElementById('phonecontrol-dial').checked; + const redial = document.getElementById('phonecontrol-redial').checked; + const answer = document.getElementById('phonecontrol-answer').checked; + const stop = document.getElementById('phonecontrol-stop').checked; + const sendDtmf = document.getElementById('phonecontrol-send_dtmf').checked; + + const query = {"actions":[]}; + + if (dial) + query.actions.push('dial'); + if (redial) + query.actions.push('redial'); + if (answer) + query.actions.push('answer'); + if (stop) + query.actions.push('stop'); + if (sendDtmf) + query.actions.push('send_dtmf'); + + callbinder(afbVshlCapabilities.url, 'vshl-capabilities', 'phonecontrol/subscribe', query); + modal.close(); + }); + + // makes modal appear (adds `open` attribute) + modal.showModal(); +} + +function showNavigationEventCHooserDialod() { + const modal = document.getElementById('navigation-event-chooser'); + const subscribeBtn = document.getElementById('navigation-subscribe-btn'); + + subscribeBtn.addEventListener('click', (evt) => { + const setDestination = document.getElementById('set_destination').checked; + const cancelNavigation = document.getElementById('cancel_navigation').checked; + + const query = {"actions":[]}; + + if (setDestination) + query.actions.push('set_destination'); + if (cancelNavigation) + query.actions.push('cancel_navigation'); + + callbinder(afbVshlCapabilities.url, 'vshl-capabilities', 'navigation/subscribe', query); + modal.close(); + }); + + // makes modal appear (adds `open` attribute) + modal.showModal(); +} + +function triggerButtonPressedAction(button) { + var paramsJson = { + "action" : "button_pressed", + "payload": { + "button" : button + } + } + callbinder(afbVshlCapabilities.url, 'vshl-capabilities', 'playbackcontroller/publish', paramsJson); +}
\ No newline at end of file diff --git a/htdocs/index.html b/htdocs/index.html new file mode 100644 index 0000000..f322881 --- /dev/null +++ b/htdocs/index.html @@ -0,0 +1,142 @@ +<html> + +<head> + <title>VSHL CAPABILITIES API Test</title> + <link rel="stylesheet" href="binding.css"> + <script type="text/javascript" src="AFB.js"></script> + <script type="text/javascript" src="binding.js"></script> +</head> + +<body class="page-content" onload="connect()"> + + <h1>Voice Service High Level Support API Tester</h1> + + <button id="connected" onclick="init()">Binder WS Fail</button> + <button id="monitoring" onclick="window.open('/monitoring/monitor.html','_monitor_ctl')">Debug/Monitoring</a> + </button> + <button onclick="clearPre('question'); clearPre('output'); clearPre('outevt');">Clear</button> <br><br> + VSHL CAPABILITIES URL: <input type="text" id="vshl-capabilities-address" value="localhost:1111" onchange="connectVshlCapabilities()"> + <br> + <br> + + <div> + <P>Phone Connection Status UI</p> + <button onclick="triggerPhoneConnectionStateChanged('CONNECTED')">Connected</button> + <button onclick="triggerPhoneConnectionStateChanged('DISCONNECTED')">Disconnected</button> + <P>Phone Call Control Inbound Ringing UI</p> + <button onclick="triggerCallStateInBoundRingingAction()">Simulate Inbound Ringing</button> + <P>Phone Call Control UI</p> + <button onclick="triggerCallStateChangedAction('ACTIVE')">Accept</button> + <button onclick="triggerCallStateChangedAction('IDLE')">Reject</button> + </div> + <div> + <p>Playback Controller UI</p> + <button onclick="triggerButtonPressedAction('play')">play</button> + <button onclick="triggerButtonPressedAction('pause')">pause</button> + <button onclick="triggerButtonPressedAction('next')">next</button> + <button onclick="triggerButtonPressedAction('previous')">previous</button> + <button onclick="triggerButtonPressedAction('skip-forward')">skip-forward</button> + <button onclick="triggerButtonPressedAction('skip-backward')">skip-backward</button> + </div> + + <dialog id="templateui-event-chooser"> + <h3 class="dialogheader">Subscribe to the following GUI Metadata Messages</h3> + <div> + <ol> + <li> + <input type="checkbox" id="render_template" checked> + <label>render_template</label> + </li> + <li> + <input type="checkbox" id="clear_template" checked> + <label>clear_template</label> + </li> + <li> + <input type="checkbox" id="render_player_info" checked> + <label>render_player_info</label> + </li> + <li> + <input type="checkbox" id="clear_player_info" checked> + <label>clear_player_info</label> + </li> + </ol> + </div> + <footer> + <button id="templateui-subscribe-btn" type="button" style="margin: 10px">Subscribe</button> + </footer> + </dialog> + + <dialog id="phonecontrol-event-chooser"> + <h3 class="dialogheader">Subscribe to the following phone control messages</h3> + <div> + <ol> + <li> + <input type="checkbox" id="phonecontrol-dial" checked> + <label>phonecontrol/dial</label> + </li> + <li> + <input type="checkbox" id="phonecontrol-redial" checked> + <label>phonecontrol/redial</label> + </li> + <li> + <input type="checkbox" id="phonecontrol-answer" checked> + <label>phonecontrol/answer</label> + </li> + <li> + <input type="checkbox" id="phonecontrol-stop" checked> + <label>phonecontrol/stop</label> + </li> + <li> + <input type="checkbox" id="phonecontrol-send_dtmf" checked> + <label>phonecontrol/send_dtmf</label> + </li> + </ol> + </div> + <footer> + <button id="phonecontrol-subscribe-btn" type="button" style="margin: 10px">Subscribe</button> + </footer> + </dialog> + + <dialog id="navigation-event-chooser"> + <h3 class="dialogheader">Subscribe to the following navigation messages</h3> + <div> + <ol> + <li> + <input type="checkbox" id="set_destination" checked> + <label>set_destination</label> + </li> + <li> + <input type="checkbox" id="cancel_navigation" checked> + <label>cancel_navigation</label> + </li> + </ol> + </div> + <footer> + <button id="navigation-subscribe-btn" type="button" style="margin: 10px">Subscribe</button> + </footer> + </dialog> + + <div id="top" class="row"> + <div id='actions' class="col1"> + <div> + <h2>VSHL CAPABILITIES APIs</h2> + <p>Speech framework's VSHL Capabilities APIs</p> + <button onclick="showTemplateUIEventChooserDialog();">Subscribe to GUI Metadata</button> + <button onclick="showPhoneControlEventChooserDialog();">Subscribe to Phonecontrol messages</button> + <button onclick="showNavigationEventCHooserDialod();">Subscribe to Navigation messages</button> + </div> + + <div id="agentsDiv"> + </div> + </div> + + <div id="main" style="visibility:hidden" class="col2"> + <ol> + <li>Question <pre id="question"></pre> + <li>Response <pre id="output"></pre> + <li>Events: <pre id="outevt"></pre> + </ol> + </div> + </div> + +</body> diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt new file mode 100644 index 0000000..6bc208f --- /dev/null +++ b/src/CMakeLists.txt @@ -0,0 +1,44 @@ +########################################################################### +# Copyright 2017-2018 Amazon.com, Inc. or its affiliates. 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. +########################################################################### + +# Add target to project dependency list +PROJECT_TARGET_ADD(vshl-capabilities) + set(CMAKE_CXX_STANDARD 11) + set(CMAKE_CXX_STANDARD_REQUIRED ON) + set(CMAKE_CXX_EXTENSIONS OFF) + + # Define project Targets + add_library(${TARGET_NAME} MODULE + ${TARGET_NAME}-binding.c + ) + + set(OPENAPI_DEF "${TARGET_NAME}-apidef" CACHE STRING "name and path to the JSON API definition without extension") + + # Binder exposes a unique public entry point + SET_TARGET_PROPERTIES(${TARGET_NAME} PROPERTIES + PREFIX "afb-" + LABELS "BINDINGV3" + LINK_FLAGS ${BINDINGS_LINK_FLAG} + OUTPUT_NAME ${TARGET_NAME} + ) + + # Library dependencies (include updates automatically) + TARGET_LINK_LIBRARIES(${TARGET_NAME} + afb-helpers + ctl-utilities + ${link_libraries}) + +add_subdirectory("plugins")
\ No newline at end of file diff --git a/src/plugins/CMakeLists.txt b/src/plugins/CMakeLists.txt new file mode 100644 index 0000000..705b13d --- /dev/null +++ b/src/plugins/CMakeLists.txt @@ -0,0 +1,153 @@ +########################################################################### +# Copyright 2017-2018 Amazon.com, Inc. or its affiliates. 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. +########################################################################### + +PROJECT_TARGET_ADD(vshl-capabilities-api) + + set(CMAKE_CXX_STANDARD 11) + set(CMAKE_CXX_STANDARD_REQUIRED ON) + set(CMAKE_CXX_EXTENSIONS OFF) + + set(VSHL_CAPABILITIES_LIB_SRC + ${CMAKE_CURRENT_SOURCE_DIR}/VshlCapabilitiesApi.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/VshlCapabilitiesApi.h + + # Interfaces + ${CMAKE_CURRENT_SOURCE_DIR}/interfaces/afb/IAFBApi.h + ${CMAKE_CURRENT_SOURCE_DIR}/interfaces/capabilities/ICapability.h + ${CMAKE_CURRENT_SOURCE_DIR}/interfaces/utilities/events/IEventFilter.h + ${CMAKE_CURRENT_SOURCE_DIR}/interfaces/utilities/logging/ILogger.h + + # AFB + ${CMAKE_CURRENT_SOURCE_DIR}/afb/AFBApiImpl.h + ${CMAKE_CURRENT_SOURCE_DIR}/afb/AFBApiImpl.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/afb/AFBRequestImpl.h + ${CMAKE_CURRENT_SOURCE_DIR}/afb/AFBRequestImpl.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/afb/include/AFBEventImpl.h + ${CMAKE_CURRENT_SOURCE_DIR}/afb/src/AFBEventImpl.cpp + + #Capabilities + ${CMAKE_CURRENT_SOURCE_DIR}/capabilities/CapabilitiesFactory.h + ${CMAKE_CURRENT_SOURCE_DIR}/capabilities/CapabilitiesFactory.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/capabilities/CapabilityMessagingService.h + ${CMAKE_CURRENT_SOURCE_DIR}/capabilities/CapabilityMessagingService.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/capabilities/core/include/MessageChannel.h + ${CMAKE_CURRENT_SOURCE_DIR}/capabilities/core/src/MessageChannel.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/capabilities/core/include/PublisherForwarder.h + ${CMAKE_CURRENT_SOURCE_DIR}/capabilities/core/src/PublisherForwarder.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/capabilities/core/include/SubscriberForwarder.h + ${CMAKE_CURRENT_SOURCE_DIR}/capabilities/core/src/SubscriberForwarder.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/capabilities/communication/include/PhoneControlMessages.h + ${CMAKE_CURRENT_SOURCE_DIR}/capabilities/communication/include/PhoneControlCapability.h + ${CMAKE_CURRENT_SOURCE_DIR}/capabilities/communication/src/PhoneControlCapability.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/capabilities/playbackcontroller/include/PlaybackControllerMessages.h + ${CMAKE_CURRENT_SOURCE_DIR}/capabilities/playbackcontroller/include/PlaybackControllerCapability.h + ${CMAKE_CURRENT_SOURCE_DIR}/capabilities/playbackcontroller/src/PlaybackControllerCapability.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/capabilities/navigation/include/NavigationMessages.h + ${CMAKE_CURRENT_SOURCE_DIR}/capabilities/navigation/include/NavigationCapability.h + ${CMAKE_CURRENT_SOURCE_DIR}/capabilities/navigation/src/NavigationCapability.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/capabilities/guimetadata/include/GuiMetadataMessages.h + ${CMAKE_CURRENT_SOURCE_DIR}/capabilities/guimetadata/include/GuiMetadataCapability.h + ${CMAKE_CURRENT_SOURCE_DIR}/capabilities/guimetadata/src/GuiMetadataCapability.cpp + + #Utilities + ${CMAKE_CURRENT_SOURCE_DIR}/utilities/events/EventRouter.h + ${CMAKE_CURRENT_SOURCE_DIR}/utilities/events/EventRouter.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/utilities/logging/Logger.h + ${CMAKE_CURRENT_SOURCE_DIR}/utilities/logging/Logger.cpp + ) + + # Define targets + ADD_LIBRARY(${TARGET_NAME} MODULE + ${VSHL_CAPABILITIES_LIB_SRC} + ) + + # VSHL Capabilities plugin properties + SET_TARGET_PROPERTIES(${TARGET_NAME} PROPERTIES + LABELS "PLUGIN" + PREFIX "" + SUFFIX ".ctlso" + OUTPUT_NAME ${TARGET_NAME} + ) + + # Define target includes + TARGET_INCLUDE_DIRECTORIES(${TARGET_NAME} + PUBLIC ${GLIB_PKG_INCLUDE_DIRS} + PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}" + PRIVATE "${CMAKE_SOURCE_DIR}/app-controller/ctl-lib" + ) + + # Library dependencies (include updates automatically) + TARGET_LINK_LIBRARIES(${TARGET_NAME} + afb-helpers + ${GLIB_PKG_LIBRARIES} + ${link_libraries} + ) + + option(ENABLE_UNIT_TESTS "Build unit tests or not" OFF) + if (ENABLE_UNIT_TESTS) + execute_process( + COMMAND "${CMAKE_CURRENT_SOURCE_DIR}/agreement.sh" + RESULT_VARIABLE AGREEMENT_RESULT + ) + message(STATUS "Agreement Result: ${AGREEMENT_RESULT}") + if (${AGREEMENT_RESULT} MATCHES "1") + message(FATAL_ERROR "User agreement not accepted. Quitting") + endif() + + include(cmake/gtest.cmake) + + set(VSHL_CAPABILITIES_TEST_SRC ${VSHL_CAPABILITIES_LIB_SRC}) + list(APPEND VSHL_CAPABILITIES_TEST_SRC + # Main + ${CMAKE_CURRENT_SOURCE_DIR}/TestMain.cpp + + # Test common + ${CMAKE_CURRENT_SOURCE_DIR}/test/common/ConsoleLogger.h + ${CMAKE_CURRENT_SOURCE_DIR}/test/common/ConsoleLogger.cpp + + # Test Mocks + ${CMAKE_CURRENT_SOURCE_DIR}/test/mocks/AFBApiMock.h + ${CMAKE_CURRENT_SOURCE_DIR}/test/mocks/AFBEventMock.h + ${CMAKE_CURRENT_SOURCE_DIR}/test/mocks/AFBRequestMock.h + ${CMAKE_CURRENT_SOURCE_DIR}/test/mocks/CapabilityMock.h + + # Capabilities + ${CMAKE_CURRENT_SOURCE_DIR}/capabilities/test/CapabilityMessagingServiceTest.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/capabilities/test/PublisherForwarderTest.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/capabilities/test/SubscriberForwarderTest.cpp + ) + + ADD_EXECUTABLE(${TARGET_NAME}_Test + ${VSHL_CAPABILITIES_TEST_SRC} + ) + + TARGET_INCLUDE_DIRECTORIES(${TARGET_NAME}_Test + PUBLIC ${GLIB_PKG_INCLUDE_DIRS} + PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}" + PRIVATE "${CMAKE_SOURCE_DIR}/app-controller/ctl-lib" + ) + + TARGET_LINK_LIBRARIES(${TARGET_NAME}_Test + afb-helpers + libgtest + libgmock + ${GLIB_PKG_LIBRARIES} + ${link_libraries} + ) + + ENABLE_TESTING() + ADD_TEST(VshlCapabilitiesTest ${TARGET_NAME}_Test) + endif()
\ No newline at end of file diff --git a/src/plugins/TestMain.cpp b/src/plugins/TestMain.cpp new file mode 100644 index 0000000..d4fcbec --- /dev/null +++ b/src/plugins/TestMain.cpp @@ -0,0 +1,20 @@ +/* + * Copyright 2018-2019 Amazon.com, Inc. or its affiliates. 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. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0/ + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT 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 "gtest/gtest.h" + +int main(int argc, char** argv) { + ::testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +}
\ No newline at end of file diff --git a/src/plugins/VshlCapabilitiesApi.cpp b/src/plugins/VshlCapabilitiesApi.cpp new file mode 100644 index 0000000..c02fb8f --- /dev/null +++ b/src/plugins/VshlCapabilitiesApi.cpp @@ -0,0 +1,411 @@ +/* + * Copyright 2018-2019 Amazon.com, Inc. or its affiliates. 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. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0/ + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT 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 "VshlCapabilitiesApi.h" + +#include <list> + +#include "afb/AFBApiImpl.h" +#include "afb/AFBRequestImpl.h" +#include "capabilities/CapabilitiesFactory.h" +#include "capabilities/CapabilityMessagingService.h" +#include "utilities/events/EventRouter.h" +#include "utilities/logging/Logger.h" + +#include "json.hpp" + +using namespace std; + +CTLP_CAPI_REGISTER("vshlsupport-api"); + +static std::string TAG = "vshlcapabilities::plugins::VshlCapabilitiesApi"; + +static std::string EVENTS_JSON_ATTR_VA_ID = "va_id"; +static std::string EVENTS_JSON_ATTR_EVENTS = "events"; + +static std::string CAPABILITIES_JSON_ATTR_ACTION = "action"; +static std::string CAPABILITIES_JSON_ATTR_ACTIONS = "actions"; +static std::string CAPABILITIES_JSON_ATTR_PAYLOAD = "payload"; + +static std::shared_ptr<vshlcapabilities::utilities::logging::Logger> sLogger; +static std::shared_ptr<vshlcapabilities::common::interfaces::IAFBApi> sAfbApi; +static std::unique_ptr<vshlcapabilities::capabilities::CapabilitiesFactory> sCapabilitiesFactory; +static std::unique_ptr<vshlcapabilities::capabilities::CapabilityMessagingService> sCapabilityMessagingService; +static std::unique_ptr<vshlcapabilities::utilities::events::EventRouter> sEventRouter; + +using json = nlohmann::json; +using Level = vshlcapabilities::utilities::logging::Logger::Level; + +CTLP_ONLOAD(plugin, ret) { + if (plugin->api == nullptr) { + return -1; + } + + // Logger + sLogger = vshlcapabilities::utilities::logging::Logger::create(plugin->api); + + // AFB Wrapper + sAfbApi = vshlcapabilities::afb::AFBApiImpl::create(plugin->api); + + // EventRouter + sEventRouter = vshlcapabilities::utilities::events::EventRouter::create(sLogger); + if (!sEventRouter) { + sLogger->log(Level::ERROR, TAG, "Failed to create EventRouter"); + return -1; + } + + sCapabilitiesFactory = vshlcapabilities::capabilities::CapabilitiesFactory::create(sLogger); + if (!sCapabilitiesFactory) { + sLogger->log(Level::ERROR, TAG, "Failed to create CapabilitiesFactory"); + return -1; + } + + sCapabilityMessagingService = vshlcapabilities::capabilities::CapabilityMessagingService::create(sLogger, sAfbApi); + if (!sCapabilityMessagingService) { + sLogger->log(Level::ERROR, TAG, "Failed to create CapabilityMessagingService"); + return -1; + } + + return 0; +} + + +CTLP_CAPI(guiMetadataSubscribe, source, argsJ, eventJ) { + if (sCapabilitiesFactory == nullptr || sCapabilityMessagingService == nullptr) { + return -1; + } + + shared_ptr<vshlcapabilities::common::interfaces::ICapability> guMetadataCapability = sCapabilitiesFactory->getGuiMetadata(); + if (!guMetadataCapability) { + sLogger->log( + Level::WARNING, + TAG, + "guimetadataSubscribe: Failed to " + "fetch guimetadata capability " + "object."); + return -1; + } + + if (eventJ == nullptr) { + sLogger->log(Level::WARNING, TAG, "guimetadataSubscribe: No arguments supplied."); + return -1; + } + + json subscribeJson = json::parse(json_object_to_json_string(eventJ)); + if (subscribeJson.find(CAPABILITIES_JSON_ATTR_ACTIONS) == subscribeJson.end()) { + sLogger->log(Level::ERROR, TAG, "guimetadataSubscribe: No events array found in subscribe json"); + return -1; + } + list<string> events(subscribeJson[CAPABILITIES_JSON_ATTR_ACTIONS].get<list<string>>()); + + // SUbscribe this client for the guimetadata events. + auto request = vshlcapabilities::afb::AFBRequestImpl::create(source->request); + for (auto event : events) { + if (!sCapabilityMessagingService->subscribe(*request, guMetadataCapability, event)) { + sLogger->log(Level::ERROR, TAG, "guimetadataSubscribe: Failed to subscribe to event: " + event); + return -1; + } + } + + AFB_ReqSuccess( + source->request, json_object_new_string("Subscription to guimetadata events successfully completed."), NULL); + return 0; +} + +CTLP_CAPI(guiMetadataPublish, source, argsJ, eventJ) { + if (sCapabilitiesFactory == nullptr || sCapabilityMessagingService == nullptr) { + return -1; + } + + shared_ptr<vshlcapabilities::common::interfaces::ICapability> guMetadataCapability = sCapabilitiesFactory->getGuiMetadata(); + if (!guMetadataCapability) { + sLogger->log( + Level::WARNING, + TAG, + "guimetadataPublish: Failed to fetch " + "guimetadata capability object."); + return -1; + } + + if (eventJ == nullptr) { + sLogger->log(Level::WARNING, TAG, "guimetadataPublish: No arguments supplied."); + return -1; + } + + json_object* actionJ = json_object_object_get(eventJ , CAPABILITIES_JSON_ATTR_ACTION.c_str()); + if (actionJ == nullptr) { + sLogger->log(Level::ERROR, TAG, "guimetadataPublish: No action found in publish json"); + return -1; + } + + std::string action = std::string(json_object_get_string(actionJ)); + if (action.empty()) { + sLogger->log(Level::ERROR, TAG, "guimetadataPublish: Invalid action input found in publish json"); + return -1; + } + + json_object* payloadJ = json_object_object_get(eventJ , CAPABILITIES_JSON_ATTR_PAYLOAD.c_str()); + if (payloadJ == nullptr) { + sLogger->log(Level::ERROR, TAG, "guimetadataPublish: No playload found in publish json"); + return -1; + } + + if (!sCapabilityMessagingService->publish(guMetadataCapability, action, payloadJ)) { + sLogger->log(Level::ERROR, TAG, "guimetadataPublish: Failed to publish message: " + action); + return -1; + } + + AFB_ReqSuccess(source->request, json_object_new_string("Successfully published guimetadata messages."), NULL); + return 0; +} + +CTLP_CAPI(phonecontrolSubscribe, source, argsJ, eventJ) { + if (sCapabilitiesFactory == nullptr || sCapabilityMessagingService == nullptr) { + return -1; + } + + shared_ptr<vshlcapabilities::common::interfaces::ICapability> phoneControlCapability = sCapabilitiesFactory->getPhoneControl(); + if (!phoneControlCapability) { + sLogger->log(Level::WARNING, TAG, "phoneControlSubscribe: Failed to fetch phone control capability object."); + return -1; + } + + if (eventJ == nullptr) { + sLogger->log(Level::WARNING, TAG, "phoneControlSubscribe: No arguments supplied."); + return -1; + } + + json subscribeJson = json::parse(json_object_to_json_string(eventJ)); + if (subscribeJson.find(CAPABILITIES_JSON_ATTR_ACTIONS) == subscribeJson.end()) { + sLogger->log(Level::ERROR, TAG, "phoneControlSubscribe: No events array found in subscribe json"); + return -1; + } + list<string> events(subscribeJson[CAPABILITIES_JSON_ATTR_ACTIONS].get<list<string>>()); + + // SUbscribe this client for the phone call control events. + auto request = vshlcapabilities::afb::AFBRequestImpl::create(source->request); + for (auto event : events) { + if (!sCapabilityMessagingService->subscribe(*request, phoneControlCapability, event)) { + sLogger->log(Level::ERROR, TAG, "phoneControlSubscribe: Failed to subscribe to event: " + event); + return -1; + } + } + + AFB_ReqSuccess( + source->request, json_object_new_string("Subscription to phone control events successfully completed."), NULL); + return 0; +} + +CTLP_CAPI(phonecontrolPublish, source, argsJ, eventJ) { + if (sCapabilitiesFactory == nullptr || sCapabilityMessagingService == nullptr) { + return -1; + } + + shared_ptr<vshlcapabilities::common::interfaces::ICapability> phoneControlCapability = sCapabilitiesFactory->getPhoneControl(); + if (!phoneControlCapability) { + sLogger->log(Level::WARNING, TAG, "phoneControlPublish: Failed to fetch navigation capability object."); + return -1; + } + + if (eventJ == nullptr) { + sLogger->log(Level::WARNING, TAG, "phoneControlPublish: No arguments supplied."); + return -1; + } + + json_object* actionJ = json_object_object_get(eventJ , CAPABILITIES_JSON_ATTR_ACTION.c_str()); + if (actionJ == nullptr) { + sLogger->log(Level::ERROR, TAG, "phoneControlPublish: No action found in publish json"); + return -1; + } + + std::string action = std::string(json_object_get_string(actionJ)); + if (action.empty()) { + sLogger->log(Level::ERROR, TAG, "phoneControlPublish: Invalid action input found in publish json"); + return -1; + } + + json_object* payloadJ = json_object_object_get(eventJ , CAPABILITIES_JSON_ATTR_PAYLOAD.c_str()); + if (payloadJ == nullptr) { + sLogger->log(Level::ERROR, TAG, "phoneControlPublish: No playload found in publish json"); + return -1; + } + + if (!sCapabilityMessagingService->publish(phoneControlCapability, action, payloadJ)) { + sLogger->log(Level::ERROR, TAG, "phoneControlPublish: Failed to publish message: " + action); + return -1; + } + + AFB_ReqSuccess(source->request, json_object_new_string("Successfully published phone control messages."), NULL); + return 0; +} + +CTLP_CAPI(navigationSubscribe, source, argsJ, eventJ) { + if (sCapabilitiesFactory == nullptr || sCapabilityMessagingService == nullptr) { + return -1; + } + + shared_ptr<vshlcapabilities::common::interfaces::ICapability> navigationCapability = sCapabilitiesFactory->getNavigation(); + if (!navigationCapability) { + sLogger->log(Level::WARNING, TAG, "navigationSubscribe: Failed to fetch navigation capability object."); + return -1; + } + + if (eventJ == nullptr) { + sLogger->log(Level::WARNING, TAG, "navigationSubscribe: No arguments supplied."); + return -1; + } + + json subscribeJson = json::parse(json_object_to_json_string(eventJ)); + if (subscribeJson.find(CAPABILITIES_JSON_ATTR_ACTIONS) == subscribeJson.end()) { + sLogger->log(Level::ERROR, TAG, "navigationSubscribe: No events array found in subscribe json"); + return -1; + } + list<string> events(subscribeJson[CAPABILITIES_JSON_ATTR_ACTIONS].get<list<string>>()); + + // SUbscribe this client for the navigation events. + auto request = vshlcapabilities::afb::AFBRequestImpl::create(source->request); + for (auto event : events) { + if (!sCapabilityMessagingService->subscribe(*request, navigationCapability, event)) { + sLogger->log(Level::ERROR, TAG, "navigationSubscribe: Failed to subscribe to event: " + event); + return -1; + } + } + + AFB_ReqSuccess( + source->request, json_object_new_string("Subscription to navigation events successfully completed."), NULL); + return 0; +} + +CTLP_CAPI(navigationPublish, source, argsJ, eventJ) { + if (sCapabilitiesFactory == nullptr || sCapabilityMessagingService == nullptr) { + return -1; + } + + shared_ptr<vshlcapabilities::common::interfaces::ICapability> navigationCapability = sCapabilitiesFactory->getNavigation(); + if (!navigationCapability) { + sLogger->log(Level::WARNING, TAG, "navigationPublish: Failed to fetch navigation capability object."); + return -1; + } + + if (eventJ == nullptr) { + sLogger->log(Level::WARNING, TAG, "navigationPublish: No arguments supplied."); + return -1; + } + + json_object* actionJ = json_object_object_get(eventJ , CAPABILITIES_JSON_ATTR_ACTION.c_str()); + if (actionJ == nullptr) { + sLogger->log(Level::ERROR, TAG, "navigationPublish: No action found in publish json"); + return -1; + } + + std::string action = std::string(json_object_get_string(actionJ)); + if (action.empty()) { + sLogger->log(Level::ERROR, TAG, "navigationPublish: Invalid action input found in publish json"); + return -1; + } + + json_object* payloadJ = json_object_object_get(eventJ , CAPABILITIES_JSON_ATTR_PAYLOAD.c_str()); + if (payloadJ == nullptr) { + sLogger->log(Level::ERROR, TAG, "navigationPublish: No playload found in publish json"); + return -1; + } + + if (!sCapabilityMessagingService->publish(navigationCapability, action, payloadJ)) { + sLogger->log(Level::ERROR, TAG, "navigationPublish: Failed to publish message: " + action); + return -1; + } + + AFB_ReqSuccess(source->request, json_object_new_string("Successfully published navigation messages."), NULL); + return 0; +} + +CTLP_CAPI(playbackControllerSubscribe, source, argsJ, eventJ) { + if (sCapabilitiesFactory == nullptr || sCapabilityMessagingService == nullptr) { + return -1; + } + + shared_ptr<vshlcapabilities::common::interfaces::ICapability> playbackcontrollerCapability = sCapabilitiesFactory->getPlaybackController(); + if (!playbackcontrollerCapability) { + sLogger->log(Level::WARNING, TAG, "playbackControllerSubscribe: Failed to fetch playbackcontroller capability object."); + return -1; + } + + if (eventJ == nullptr) { + sLogger->log(Level::WARNING, TAG, "playbackControllerSubscribe: No arguments supplied."); + return -1; + } + + json subscribeJson = json::parse(json_object_to_json_string(eventJ)); + if (subscribeJson.find(CAPABILITIES_JSON_ATTR_ACTIONS) == subscribeJson.end()) { + sLogger->log(Level::ERROR, TAG, "playbackControllerSubscribe: No events array found in subscribe json"); + return -1; + } + list<string> events(subscribeJson[CAPABILITIES_JSON_ATTR_ACTIONS].get<list<string>>()); + + // SUbscribe this client for the navigation events. + auto request = vshlcapabilities::afb::AFBRequestImpl::create(source->request); + for (auto event : events) { + if (!sCapabilityMessagingService->subscribe(*request, playbackcontrollerCapability, event)) { + sLogger->log(Level::ERROR, TAG, "playbackControllerSubscribe: Failed to subscribe to event: " + event); + return -1; + } + } + + AFB_ReqSuccess( + source->request, json_object_new_string("Subscription to playbackcontroller events successfully completed."), NULL); + return 0; +} + +CTLP_CAPI(playbackControllerPublish, source, argsJ, eventJ) { + if (sCapabilitiesFactory == nullptr || sCapabilityMessagingService == nullptr) { + return -1; + } + + shared_ptr<vshlcapabilities::common::interfaces::ICapability> playbackcontrollerCapability = sCapabilitiesFactory->getPlaybackController(); + if (!playbackcontrollerCapability) { + sLogger->log(Level::WARNING, TAG, "playbackControllerPublish: Failed to fetch playbackcontroller capability object."); + return -1; + } + + if (eventJ == nullptr) { + sLogger->log(Level::WARNING, TAG, "playbackControllerPublish: No arguments supplied."); + return -1; + } + + json_object* actionJ = json_object_object_get(eventJ , CAPABILITIES_JSON_ATTR_ACTION.c_str()); + if (actionJ == nullptr) { + sLogger->log(Level::ERROR, TAG, "playbackControllerPublish: No action found in publish json"); + return -1; + } + + std::string action = std::string(json_object_get_string(actionJ)); + if (action.empty()) { + sLogger->log(Level::ERROR, TAG, "playbackControllerPublish: Invalid action input found in publish json"); + return -1; + } + + json_object* payloadJ = json_object_object_get(eventJ , CAPABILITIES_JSON_ATTR_PAYLOAD.c_str()); + if (payloadJ == nullptr) { + sLogger->log(Level::ERROR, TAG, "playbackControllerPublish: No playload found in publish json"); + return -1; + } + + if (!sCapabilityMessagingService->publish(playbackcontrollerCapability, action, payloadJ)) { + sLogger->log(Level::ERROR, TAG, "playbackControllerPublish: Failed to publish message: " + action); + return -1; + } + + AFB_ReqSuccess(source->request, json_object_new_string("Successfully published playbackcontroller messages."), NULL); + return 0; +}
\ No newline at end of file diff --git a/src/plugins/VshlCapabilitiesApi.h b/src/plugins/VshlCapabilitiesApi.h new file mode 100644 index 0000000..1ef1d71 --- /dev/null +++ b/src/plugins/VshlCapabilitiesApi.h @@ -0,0 +1,39 @@ +/* + * Copyright 2017-2018 Amazon.com, Inc. or its affiliates. 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. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0/ + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT 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 VSHL_CAPABILITIES_API_INCLUDE +#define VSHL_CAPABILITIES_API_INCLUDE + +#include "ctl-plugin.h" + +#ifdef __cplusplus +extern "C" { +#endif + +CTLP_ONLOAD(plugin, ret); +CTLP_INIT(plugin, ret); +int guiMetadataSubscribe(CtlSourceT* source, json_object* argsJ, json_object* queryJ); +int guiMetadataPublish(CtlSourceT* source, json_object* argsJ, json_object* queryJ); +int phonecontrolSubscribe(CtlSourceT* source, json_object* argsJ, json_object* queryJ); +int phonecontrolPublish(CtlSourceT* source, json_object* argsJ, json_object* queryJ); +int navigationSubscribe(CtlSourceT* source, json_object* argsJ, json_object* queryJ); +int navigationPublish(CtlSourceT* source, json_object* argsJ, json_object* queryJ); +int playbackControllerSubscribe(CtlSourceT* source, json_object* argsJ, json_object* queryJ); +int playbackControllerPublish(CtlSourceT* source, json_object* argsJ, json_object* queryJ); + +#ifdef __cplusplus +} +#endif + +#endif // VSHL_CAPABILITIES_API_INCLUDE diff --git a/src/plugins/afb/AFBApiImpl.cpp b/src/plugins/afb/AFBApiImpl.cpp new file mode 100644 index 0000000..443a412 --- /dev/null +++ b/src/plugins/afb/AFBApiImpl.cpp @@ -0,0 +1,78 @@ +/* + * Copyright 2018-2019 Amazon.com, Inc. or its affiliates. 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. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0/ + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT 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 "afb/AFBApiImpl.h" + +#include "afb/include/AFBEventImpl.h" +#include "utilities/logging/Logger.h" + +extern "C" { +#define AFB_BINDING_VERSION 3 + +#include "afb-definitions.h" +} + +static std::string TAG = "vshlcapabilities::afb::AFBApiImpl"; + +/** + * Specifies the severity level of a log message + */ +using Level = vshlcapabilities::common::interfaces::ILogger::Level; +using namespace vshlcapabilities::common::interfaces; +using namespace vshlcapabilities::utilities::logging; + +namespace vshlcapabilities { +namespace afb { + +std::unique_ptr<AFBApiImpl> AFBApiImpl::create(AFB_ApiT api) { + return std::unique_ptr<AFBApiImpl>(new AFBApiImpl(api)); +} + +AFBApiImpl::AFBApiImpl(AFB_ApiT api) : mApi(api), mLogger(Logger::create(api)) { +} + +AFBApiImpl::~AFBApiImpl() { +} + +std::shared_ptr<IAFBApi::IAFBEvent> AFBApiImpl::createEvent(const std::string& eventName) { + return AFBEventImpl::create(mLogger, mApi, eventName); +} + +int AFBApiImpl::callSync( + const std::string& api, + const std::string& verb, + struct json_object* request, + struct json_object** result, + std::string& error, + std::string& info) { + char* errorStr = NULL; + char* infoStr = NULL; + int rc = AFB_ApiSync(mApi, api.c_str(), verb.c_str(), request, result, &errorStr, &infoStr); + + if (errorStr) { + error = errorStr; + free(errorStr); + } + + if (infoStr) { + info = infoStr; + free(infoStr); + } + + return rc; +} + +} // namespace afb +} // namespace vshl diff --git a/src/plugins/afb/AFBApiImpl.h b/src/plugins/afb/AFBApiImpl.h new file mode 100644 index 0000000..a8d8e08 --- /dev/null +++ b/src/plugins/afb/AFBApiImpl.h @@ -0,0 +1,61 @@ +/* + * Copyright 2018-2019 Amazon.com, Inc. or its affiliates. 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. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0/ + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT 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 VSHL_AFB_AFBAPIIMPL_H_ +#define VSHL_AFB_AFBAPIIMPL_H_ + +#include <memory> + +extern "C" { +#include "ctl-plugin.h" +} + +#include "interfaces/afb/IAFBApi.h" +#include "interfaces/utilities/logging/ILogger.h" + +using namespace std; + +namespace vshlcapabilities { +namespace afb { + +class AFBApiImpl : public vshlcapabilities::common::interfaces::IAFBApi { +public: + static std::unique_ptr<AFBApiImpl> create(AFB_ApiT api); + + ~AFBApiImpl(); + + std::shared_ptr<IAFBEvent> createEvent(const std::string& eventName) override; + + int callSync( + const std::string& api, + const std::string& verb, + struct json_object* request, + struct json_object** result, + std::string& error, + std::string& info) override; + +private: + AFBApiImpl(AFB_ApiT api); + + // AFB API Binding + AFB_ApiT mApi; + + // Logger + std::shared_ptr<vshlcapabilities::common::interfaces::ILogger> mLogger; +}; + +} // namespace afb +} // namespace vshl + +#endif // VSHL_AFB_AFBAPIIMPL_H_ diff --git a/src/plugins/afb/AFBRequestImpl.cpp b/src/plugins/afb/AFBRequestImpl.cpp new file mode 100644 index 0000000..3e20172 --- /dev/null +++ b/src/plugins/afb/AFBRequestImpl.cpp @@ -0,0 +1,37 @@ +/* + * Copyright 2018-2019 Amazon.com, Inc. or its affiliates. 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. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0/ + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT 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 "afb/AFBRequestImpl.h" + +extern "C" { +#include "afb-definitions.h" +} + +namespace vshlcapabilities { +namespace afb { + +std::unique_ptr<AFBRequestImpl> AFBRequestImpl::create(AFB_ReqT afbRequest) { + return std::unique_ptr<AFBRequestImpl>(new AFBRequestImpl(afbRequest)); +} + +AFBRequestImpl::AFBRequestImpl(AFB_ReqT afbRequest) : mAfbRequest(afbRequest) { +} + +void* AFBRequestImpl::getNativeRequest() { + return mAfbRequest; +} + +} // namespace afb +} // namespace vshl diff --git a/src/plugins/afb/AFBRequestImpl.h b/src/plugins/afb/AFBRequestImpl.h new file mode 100644 index 0000000..c155f31 --- /dev/null +++ b/src/plugins/afb/AFBRequestImpl.h @@ -0,0 +1,49 @@ +/* + * Copyright 2018-2019 Amazon.com, Inc. or its affiliates. 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. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0/ + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT 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 VSHL_AFB_AFBREQUESTIMPL_H_ +#define VSHL_AFB_AFBREQUESTIMPL_H_ + +#include <memory> + +extern "C" { +#include "ctl-plugin.h" +} + +#include "interfaces/afb/IAFBApi.h" + +namespace vshlcapabilities { +namespace afb { + +/** + * AFB Request impl + */ +class AFBRequestImpl : public vshlcapabilities::common::interfaces::IAFBRequest { +public: + static std::unique_ptr<AFBRequestImpl> create(AFB_ReqT afbRequest); + + // {@c IAFBRequest Implementation + void *getNativeRequest() override; + // @c IAFBRequest Implementation } + +private: + AFBRequestImpl(AFB_ReqT afbRequest); + + AFB_ReqT mAfbRequest; +}; + +} // namespace afb +} // namespace vshl + +#endif // VSHL_AFB_AFBREQUESTIMPL_H_ diff --git a/src/plugins/afb/include/AFBEventImpl.h b/src/plugins/afb/include/AFBEventImpl.h new file mode 100644 index 0000000..2dda513 --- /dev/null +++ b/src/plugins/afb/include/AFBEventImpl.h @@ -0,0 +1,77 @@ +/* + * Copyright 2018-2019 Amazon.com, Inc. or its affiliates. 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. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0/ + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT 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 VSHL_AFB_EVENT_H_ +#define VSHL_AFB_EVENT_H_ + +#include <memory> +#include <string> + +#include "interfaces/afb/IAFBApi.h" +#include "interfaces/utilities/logging/ILogger.h" + +extern "C" { +#include "ctl-plugin.h" +#include <json-c/json.h> +} + +using namespace std; + +namespace vshlcapabilities { +namespace afb { +/* + * This class encapsulates AFB Event. + */ +class AFBEventImpl : public vshlcapabilities::common::interfaces::IAFBApi::IAFBEvent { +public: + static unique_ptr<AFBEventImpl> + create(shared_ptr<vshlcapabilities::common::interfaces::ILogger> logger, AFB_ApiT api, + const string &eventName); + + // Destructor + ~AFBEventImpl(); + + /// { @c IAFBEvent implementation + string getName() const override; + bool isValid() override; + int publishEvent(struct json_object *payload) override; + bool subscribe(vshlcapabilities::common::interfaces::IAFBRequest &request) override; + bool unsubscribe(vshlcapabilities::common::interfaces::IAFBRequest &request) override; + /// @c IAFBEvent implementation } + +private: + AFBEventImpl(shared_ptr<vshlcapabilities::common::interfaces::ILogger> logger, + AFB_ApiT api, const string &eventName); + + // Make the event. This is a lazy make that happens + // usually during the subscribe stage. + void makeEventIfNeccessary(); + + // Binding API reference + AFB_ApiT mAfbApi; + + // AFB Event + afb_event_t mAfbEvent; + + // Event Name + string mEventName; + + // Logger + shared_ptr<vshlcapabilities::common::interfaces::ILogger> mLogger; +}; + +} // namespace afb +} // namespace vshl + +#endif // VSHL_AFB_EVENT_H_ diff --git a/src/plugins/afb/src/AFBEventImpl.cpp b/src/plugins/afb/src/AFBEventImpl.cpp new file mode 100644 index 0000000..7ddac36 --- /dev/null +++ b/src/plugins/afb/src/AFBEventImpl.cpp @@ -0,0 +1,89 @@ +/* + * Copyright 2018-2019 Amazon.com, Inc. or its affiliates. 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. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0/ + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT 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 "afb/include/AFBEventImpl.h" + +static string TAG = "vshlcapabilities::afb::Event"; + +using Level = vshlcapabilities::common::interfaces::ILogger::Level; +using namespace vshlcapabilities::common::interfaces; + +namespace vshlcapabilities { +namespace afb { + +unique_ptr<AFBEventImpl> AFBEventImpl::create( + shared_ptr<vshlcapabilities::common::interfaces::ILogger> logger, + AFB_ApiT api, + const string& eventName) { + return unique_ptr<AFBEventImpl>(new AFBEventImpl(logger, api, eventName)); +} + +AFBEventImpl::AFBEventImpl( + shared_ptr<vshlcapabilities::common::interfaces::ILogger> logger, + AFB_ApiT api, + const string& eventName) : + mLogger(logger), + mAfbApi(api), + mEventName(eventName), + mAfbEvent(nullptr) { +} + +AFBEventImpl::~AFBEventImpl() { +} + +string AFBEventImpl::getName() const { + return mEventName; +} + +bool AFBEventImpl::isValid() { + makeEventIfNeccessary(); + return afb_event_is_valid(mAfbEvent) == 1 ? true : false; +} + +bool AFBEventImpl::subscribe(IAFBRequest& requestInterface) { + makeEventIfNeccessary(); + auto request = static_cast<AFB_ReqT>(requestInterface.getNativeRequest()); + if (isValid() && afb_req_subscribe(request, mAfbEvent) == 0) { + return true; + } + + return false; +} + +bool AFBEventImpl::unsubscribe(IAFBRequest& requestInterface) { + makeEventIfNeccessary(); + auto request = static_cast<AFB_ReqT>(requestInterface.getNativeRequest()); + if (isValid() && afb_req_unsubscribe(request, mAfbEvent) == 0) { + return true; + } + + return false; +} + +int AFBEventImpl::publishEvent(struct json_object* payload) { + makeEventIfNeccessary(); + return afb_event_push(mAfbEvent, payload); +} + +void AFBEventImpl::makeEventIfNeccessary() { + if (mAfbEvent) { + return; + } + + mLogger->log(Level::NOTICE, TAG, "Creating VSHL event: " + mEventName); + mAfbEvent = afb_api_make_event(mAfbApi, mEventName.c_str()); +} + +} // namespace afb +} // namespace vshl diff --git a/src/plugins/agreement.sh b/src/plugins/agreement.sh new file mode 100755 index 0000000..2a8eb71 --- /dev/null +++ b/src/plugins/agreement.sh @@ -0,0 +1,43 @@ +#******************************************************************************** +# Copyright 2018-2019 Amazon.com, Inc. or its affiliates. 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. +# A copy of the License is located at +# +# http://aws.amazon.com/apache2.0/ +# +# or in the "license" file accompanying this file. This file is distributed +# on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either +# express or implied. See the License for the specific language governing +# permissions and limitations under the License. +#********************************************************************************* + +agreement_check() { + cat << EOF + +******************************************************************************* +The scripts provided herein will retrieve several third-party libraries, +environments, and/or other software packages at build-time +("External Dependencies") from third-party sources. These are terms and +conditions that you need to agree to abide by if you choose to build the +External Dependencies. Licenses for the External Dependencies may be found at +README.md. If you do not agree with every term and condition +associated with the External Dependencies, enter “QUIT” in the command line +when prompted by the script. +******************************************************************************* + +EOF + + answer="dummy" + while [ ! -z $answer ]; do + read -p "Type \"QUIT\" to exit the script now, press ENTER to continue: " -r answer + if [ "$answer" = "QUIT" ]; then + exit 1 + fi + echo "" + done +} + +agreement_check +exit 0
\ No newline at end of file diff --git a/src/plugins/capabilities/CapabilitiesFactory.cpp b/src/plugins/capabilities/CapabilitiesFactory.cpp new file mode 100644 index 0000000..7a65842 --- /dev/null +++ b/src/plugins/capabilities/CapabilitiesFactory.cpp @@ -0,0 +1,71 @@ +/* + * Copyright 2018-2019 Amazon.com, Inc. or its affiliates. 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. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0/ + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT 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 "capabilities/CapabilitiesFactory.h" + +#include "capabilities/communication/include/PhoneControlCapability.h" +#include "capabilities/guimetadata/include/GuiMetadataCapability.h" +#include "capabilities/navigation/include/NavigationCapability.h" +#include "capabilities/playbackcontroller/include/PlaybackControllerCapability.h" + +static string TAG = "vshlcapabilities::core::CapabilitiesFactory"; + +namespace vshlcapabilities { +namespace capabilities { + +// Create CapabilitiesFactory +std::unique_ptr<CapabilitiesFactory> CapabilitiesFactory::create( + shared_ptr<vshlcapabilities::common::interfaces::ILogger> logger) { + auto capabilitiesFactory = std::unique_ptr<CapabilitiesFactory>(new CapabilitiesFactory(logger)); + return capabilitiesFactory; +} + +CapabilitiesFactory::CapabilitiesFactory( + shared_ptr<vshlcapabilities::common::interfaces::ILogger> logger) { + mLogger = logger; +} + +std::shared_ptr<common::interfaces::ICapability> CapabilitiesFactory::getGuiMetadata() { + if (!mGuiMetadata) { + mGuiMetadata = vshlcapabilities::capabilities::guimetadata::GuiMetadata::create(mLogger); + } + return mGuiMetadata; +} + +std::shared_ptr<common::interfaces::ICapability> CapabilitiesFactory::getPhoneControl() { + if (!mPhoneControl) { + mPhoneControl = vshlcapabilities::capabilities::phonecontrol::PhoneControl::create(mLogger); + } + return mPhoneControl; +} + +std::shared_ptr<common::interfaces::ICapability> CapabilitiesFactory::getNavigation( + ) { + if (!mNavigation) { + mNavigation = vshlcapabilities::capabilities::navigation::Navigation::create(mLogger); + } + return mNavigation; +} + +std::shared_ptr<common::interfaces::ICapability> CapabilitiesFactory::getPlaybackController( + ) { + if (!mPlaybackController) { + mPlaybackController = vshlcapabilities::capabilities::playbackcontroller::PlaybackController::create(mLogger); + } + return mPlaybackController; +} + +} // namespace capabilities +} // namespace vshl
\ No newline at end of file diff --git a/src/plugins/capabilities/CapabilitiesFactory.h b/src/plugins/capabilities/CapabilitiesFactory.h new file mode 100644 index 0000000..b97fcfe --- /dev/null +++ b/src/plugins/capabilities/CapabilitiesFactory.h @@ -0,0 +1,69 @@ +/* + * Copyright 2018-2019 Amazon.com, Inc. or its affiliates. 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. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0/ + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT 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 VSHL_CAPABILITIES_CAPABILITIESFACTORY_H_ +#define VSHL_CAPABILITIES_CAPABILITIESFACTORY_H_ + +#include <memory> + +#include "interfaces/capabilities/ICapability.h" +#include "interfaces/utilities/logging/ILogger.h" + +using namespace std; + +namespace vshlcapabilities { +namespace capabilities { +/* + * Factory for creating different capability objects. + */ +class CapabilitiesFactory { +public: + // Create CapabilitiesFactory + static std::unique_ptr<CapabilitiesFactory> create( + shared_ptr<vshlcapabilities::common::interfaces::ILogger> logger); + + // GUI Metadata capability + std::shared_ptr<common::interfaces::ICapability> getGuiMetadata(); + + // Phone call control capability + std::shared_ptr<common::interfaces::ICapability> getPhoneControl(); + + // Navigation capability + std::shared_ptr<common::interfaces::ICapability> getNavigation(); + + // PLaybackcontroller capability + std::shared_ptr<common::interfaces::ICapability> getPlaybackController(); + + // Destructor + ~CapabilitiesFactory() = default; + +private: + // Constructor + CapabilitiesFactory( + shared_ptr<vshlcapabilities::common::interfaces::ILogger> logger); + + // Capabilities + shared_ptr<vshlcapabilities::common::interfaces::ICapability> mGuiMetadata; + shared_ptr<vshlcapabilities::common::interfaces::ICapability> mPhoneControl; + shared_ptr<vshlcapabilities::common::interfaces::ICapability> mNavigation; + shared_ptr<vshlcapabilities::common::interfaces::ICapability> mPlaybackController; + + // Logger + shared_ptr<vshlcapabilities::common::interfaces::ILogger> mLogger; +}; + +} // namespace capabilities +} // namespace vshl + +#endif // VSHL_CAPABILITIES_CAPABILITIESFACTORY_H_ diff --git a/src/plugins/capabilities/CapabilityMessagingService.cpp b/src/plugins/capabilities/CapabilityMessagingService.cpp new file mode 100644 index 0000000..8dd6e26 --- /dev/null +++ b/src/plugins/capabilities/CapabilityMessagingService.cpp @@ -0,0 +1,117 @@ +/* + * Copyright 2018-2019 Amazon.com, Inc. or its affiliates. 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. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0/ + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT 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 "capabilities/CapabilityMessagingService.h" + +#include "capabilities/core/include/PublisherForwarder.h" +#include "capabilities/core/include/SubscriberForwarder.h" + +static string TAG = "vshlcapabilities::capabilities::CapabilityMessagingService"; + +using Level = vshlcapabilities::common::interfaces::ILogger::Level; + +namespace vshlcapabilities { +namespace capabilities { + +// Create a CapabilityMessagingService. +unique_ptr<CapabilityMessagingService> CapabilityMessagingService::create( + shared_ptr<vshlcapabilities::common::interfaces::ILogger> logger, + shared_ptr<vshlcapabilities::common::interfaces::IAFBApi> afbApi) { + if (logger == nullptr) { + return nullptr; + } + + if (afbApi == nullptr) { + logger->log(Level::ERROR, TAG, "Failed to create CapabilityMessagingService: AFB API null"); + return nullptr; + } + + auto capabilityMessageService = + std::unique_ptr<CapabilityMessagingService>(new CapabilityMessagingService(logger, afbApi)); + return capabilityMessageService; +} + +CapabilityMessagingService::~CapabilityMessagingService() { + mMessageChannelsMap.clear(); +} + +CapabilityMessagingService::CapabilityMessagingService( + shared_ptr<vshlcapabilities::common::interfaces::ILogger> logger, + shared_ptr<vshlcapabilities::common::interfaces::IAFBApi> afbApi) : + mAfbApi(afbApi), + mLogger(logger) { +} + +// Subscribe to capability specific messages. +bool CapabilityMessagingService::subscribe( + vshlcapabilities::common::interfaces::IAFBRequest& request, + shared_ptr<common::interfaces::ICapability> capability, + const string action) { + auto capabilityName = capability->getName(); + + if (capabilityName.empty()) { + mLogger->log(Level::ERROR, TAG, "Failed to subscribe to message. Invalid input."); + return false; + } + + auto messageChannel = getMessageChannel(capability); + return messageChannel->subscribe(request, action); +} + +// Publish capability messages. +bool CapabilityMessagingService::publish( + shared_ptr<common::interfaces::ICapability> capability, + const string action, + json_object* payload) { + auto capabilityName = capability->getName(); + + if (capabilityName.empty()) { + mLogger->log(Level::ERROR, TAG, "Failed to publish message. Invalid input."); + return false; + } + + auto messageChannelIt = mMessageChannelsMap.find(capabilityName); + if (messageChannelIt == mMessageChannelsMap.end()) { + mLogger->log( + Level::ERROR, + TAG, + "No one subscribed for a message of capability. " + capabilityName); + return false; + } + + return messageChannelIt->second->publish(action, payload); +} + +shared_ptr<vshlcapabilities::capabilities::core::MessageChannel> CapabilityMessagingService::getMessageChannel( + shared_ptr<common::interfaces::ICapability> capability) { + auto capabilityName = capability->getName(); + + if (capabilityName.empty()) { + mLogger->log(Level::ERROR, TAG, "Failed to create message channel. Invalid input."); + return nullptr; + } + + auto messageChannelIt = mMessageChannelsMap.find(capabilityName); + if (messageChannelIt == mMessageChannelsMap.end()) { + mLogger->log(Level::INFO, TAG, "Creating new message channel for capability: " + capabilityName); + auto messageChannel = vshlcapabilities::capabilities::core::MessageChannel::create(mLogger, mAfbApi, capability); + mMessageChannelsMap.insert(make_pair(capabilityName, messageChannel)); + return messageChannel; + } + + return messageChannelIt->second; +} + +} // namespace capabilities +} // namespace vshl diff --git a/src/plugins/capabilities/CapabilityMessagingService.h b/src/plugins/capabilities/CapabilityMessagingService.h new file mode 100644 index 0000000..79edfa8 --- /dev/null +++ b/src/plugins/capabilities/CapabilityMessagingService.h @@ -0,0 +1,82 @@ +/* + * Copyright 2018-2019 Amazon.com, Inc. or its affiliates. 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. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0/ + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT 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 VSHL_CAPABILITIES_CAPABILITYMESSAGINGSERVICE_H_ +#define VSHL_CAPABILITIES_CAPABILITYMESSAGINGSERVICE_H_ + +#include <memory> +#include <string> +#include <unordered_map> + +#include "capabilities/core/include/MessageChannel.h" +#include "interfaces/afb/IAFBApi.h" +#include "interfaces/capabilities/ICapability.h" +#include "interfaces/utilities/logging/ILogger.h" + +using namespace std; + +namespace vshlcapabilities { +namespace capabilities { +/* + * This hosts service APIs that clients can use to subscribe and + * forward capability messages. Each capability has a name and + * direction (upstream/downstream). Upstream messages are from + * voiceagents to Apps and downstream messages are Apps to voiceagents. + * This class will use a factory to create publisher and subcribers for + * each capability and create assiociations between them. + */ +class CapabilityMessagingService { +public: + // Create a CapabilityMessagingService. + static std::unique_ptr<CapabilityMessagingService> + create(shared_ptr<vshlcapabilities::common::interfaces::ILogger> logger, + shared_ptr<vshlcapabilities::common::interfaces::IAFBApi> afbApi); + + // Subscribe to capability specific messages. + bool subscribe(vshlcapabilities::common::interfaces::IAFBRequest &request, + shared_ptr<common::interfaces::ICapability> capability, + const string action); + + // Publish capability messages. + bool publish(shared_ptr<common::interfaces::ICapability> capability, + const string action, json_object* payload); + + // Destructor + ~CapabilityMessagingService(); + +private: + // Constructor + CapabilityMessagingService( + shared_ptr<vshlcapabilities::common::interfaces::ILogger> logger, + shared_ptr<vshlcapabilities::common::interfaces::IAFBApi> afbApi); + + // Binding API reference + shared_ptr<vshlcapabilities::common::interfaces::IAFBApi> mAfbApi; + + // Create a message channel for the capability. + shared_ptr<vshlcapabilities::capabilities::core::MessageChannel> + getMessageChannel(shared_ptr<common::interfaces::ICapability> capability); + + // Map of capabilities to message channels. + unordered_map<string, shared_ptr<vshlcapabilities::capabilities::core::MessageChannel>> + mMessageChannelsMap; + + // Logger + shared_ptr<vshlcapabilities::common::interfaces::ILogger> mLogger; +}; + +} // namespace capabilities +} // namespace vshl + +#endif // VSHL_CAPABILITIES_CAPABILITYMESSAGINGSERVICE_H_ diff --git a/src/plugins/capabilities/communication/include/PhoneControlCapability.h b/src/plugins/capabilities/communication/include/PhoneControlCapability.h new file mode 100644 index 0000000..87dc339 --- /dev/null +++ b/src/plugins/capabilities/communication/include/PhoneControlCapability.h @@ -0,0 +1,57 @@ +/* + * Copyright 2018-2019 Amazon.com, Inc. or its affiliates. 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. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0/ + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT 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 VSHL_CAPABILITIES_PHONECONTROL_CAPABILITY_H_ +#define VSHL_CAPABILITIES_PHONECONTROL_CAPABILITY_H_ + +#include <memory> + +#include "interfaces/capabilities/ICapability.h" +#include "interfaces/utilities/logging/ILogger.h" + +namespace vshlcapabilities { +namespace capabilities { +namespace phonecontrol { + +/* + * PhoneControl capability. Calls are initiated in the endpoint. + */ +class PhoneControl : public common::interfaces::ICapability { +public: + // Create a PhoneControl. + static std::shared_ptr<PhoneControl> create( + shared_ptr<vshlcapabilities::common::interfaces::ILogger> logger); + + ~PhoneControl() = default; + +protected: + string getName() const override; + + list<string> getUpstreamMessages() const override; + + list<string> getDownstreamMessages() const override; + +private: + PhoneControl( + shared_ptr<vshlcapabilities::common::interfaces::ILogger> logger); + + shared_ptr<vshlcapabilities::common::interfaces::ILogger> mLogger; +}; + +} // namespace phonecontrol +} // namespace capabilities +} // namespace vshl + +#endif // VSHL_CAPABILITIES_PHONECONTROL_CAPABILITY_H_ diff --git a/src/plugins/capabilities/communication/include/PhoneControlMessages.h b/src/plugins/capabilities/communication/include/PhoneControlMessages.h new file mode 100644 index 0000000..23d2fae --- /dev/null +++ b/src/plugins/capabilities/communication/include/PhoneControlMessages.h @@ -0,0 +1,208 @@ +/* + * Copyright 2018-2019 Amazon.com, Inc. or its affiliates. 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. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0/ + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT 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 VSHL_CAPABILITIES_PHONECONTROL_MESSAGES_H_ +#define VSHL_CAPABILITIES_PHONECONTROL_MESSAGES_H_ + +#include <list> +#include <string> + +using namespace std; + +namespace vshlcapabilities { +namespace capabilities { +namespace phonecontrol { + +static string NAME = "phonecontrol"; + +/* + ****************************************************************************************************** + * Supported actions from VA -> APPS + ****************************************************************************************************** + */ + +/* Dial message sent from VA to app handling the calling. + * + * payload: + * { + * "callId": "{{STRING}}", + * "callee": { + * "details": "{{STRING}}", + * "defaultAddress": { + * "protocol": "{{STRING}}", + * "format": "{{STRING}}", + * "value": "{{STRING}}" + * }, + * "alternativeAddresses": [{ + * "protocol": "{{STRING}}", + * "format": "{{STRING}}", + * "value": {{STRING}} + * }] + * } + * } + * } + * + * callId (required): A unique identifier for the call + * callee (required): The destination of the outgoing call + * callee.details (optional): Descriptive information about the callee + * callee.defaultAddress (required): The default address to use for calling the callee + * callee.alternativeAddresses (optional): An array of alternate addresses for the existing callee + * address.protocol (required): The protocol for this address of the callee (e.g. PSTN, SIP, H323, etc.) + * address.format (optional): The format for this address of the callee (e.g. E.164, E.163, E.123, DIN5008, etc.) + * address.value (required): The address of the callee. + * + */ +static string PHONECONTROL_DIAL = "dial"; +/** + * Notifies the platform implementation to redial the last called phone number. + * + * After returning @c true, if no stored number is available to be redialed, @c PhoneCallController::callFailed with + * @c CallError::NO_NUMBER_FOR_REDIAL should be called. + * + * @param [in] payload Details of the redial request in structured JSON format. See the following + * payload: + * { + * "callId": "{{STRING}}" + * } + * @li callId (required): A unique identifier for the call + * + * @return @c true if the platform implementation successfully handled the call + */ +static string PHONECONTROL_REDIAL = "redial"; +/** + * Notifies the platform implementation to answer an inbound call + * + * @param [in] payload Details of the answer request in structured JSON format. See the following + * payload: + * { + * "callId": "{{STRING}}", + * } + * @li callId (required): The unique identifier for the call to answer + */ +static string PHONECONTROL_ANSWER = "answer"; +/** + * Notifies the platform implementation to end an ongoing call or stop inbound or outbound call setup + * + * @param [in] payload Details of the stop request in structured JSON format. See the following + * payload: + * { + * "callId": "{{STRING}}" + * } + * @li callId (required): The unique identifier for the call to be stopped + */ +static string PHONECONTROL_STOP = "stop"; +/** + * Notifies the platform implementation to send a DTMF signal to the calling device + * + * @param [in] payload Details of the DTMF request in structured JSON format. See the following + * payload: + * { + * "callId": "{{STRING}}", + * "signal": "{{STRING}}" + * } + * @li callId (required): The unique identifier for the call + * @li signal (required): The DTMF string to be sent to the calling device associated with the callId + */ +static string PHONECONTROL_SEND_DTMF = "send_dtmf"; + +/* + ****************************************************************************************************** + * Supported actions from Apps -> VA + ****************************************************************************************************** + */ + +/* + * App notifies the voiceagents of a change in connection state of a calling device. + * + * payload: + * { + * "state" : "{{STRING}}" // CONNECTED or DISCONNECTED + * } + */ +static string PHONECONTROL_CONNECTIONSTATE_CHANGED = "connection_state_changed"; +/* + * App notifies the voiceagents that call is activated + * + * callId must match the one that is sent by VA with DIAL message above. + * + * payload: + * { + * "callId" : "{{STRING}}" + * "state" : "{{STRING}}" // IDLE, ACTIVE, TRYING, INBOUND_RINGING, OUTBOUND_RINGING, INVITED + * } + */ +static string PHONECONTROL_CALL_STATE_CHANGED = "call_state_changed"; +/* + * App notifies the voiceagents of an error in initiating or maintaining a + * call on a calling device + * + * callId must match the one that is sent by VA with DIAL message above. + * error: below status codes. + * 4xx: Validation failure for the input from the DIAL message + * 500: Internal error on the platform unrelated to the cellular network + * 503: Error on the platform related to the cellular network + * + * payload : + * { + * "callId" : "{{STRING}}" + * "error" : "{{STRING}}" + * } + */ +static string PHONECONTROL_CALL_FAILED = "call_failed"; +/** + * App notifies the voiceagents that a caller id was received for an inbound call + * payload : + * { + * "callId" : "{{STRING}}" + * "callerId" : "{{STRING}}" + * } + * + * @param [in] callId The unique identifier for the call associated with the callId + * @param [in] callerId The caller's identifier or phone number + */ +static string PHONECONTROL_CALLERID_RECEIVED = "caller_id_received"; + +/** + * Notifies the Engine that sending the DTMF signal succeeded. + * payload : + * { + * "callId" : "{{STRING}}" + * } + * @param [in] callId The unique identifier for the associated call + * + * PhoneCallController::sendDTMF + */ +static string PHONECONTROL_SEND_DTMF_SUCCEEDED = "send_dtmf_succeeded"; + +// List of actions that are delivered from VA -> Apps +static list<string> PHONECONTROL_UPSTREAM_ACTIONS = { + PHONECONTROL_DIAL, + PHONECONTROL_REDIAL, + PHONECONTROL_ANSWER, + PHONECONTROL_STOP, + PHONECONTROL_SEND_DTMF +}; + +// List of actions that are delivered from Apps -> VA +static list<string> PHONECONTROL_DOWNSTREAM_ACTIONS = {PHONECONTROL_CONNECTIONSTATE_CHANGED, + PHONECONTROL_CALL_STATE_CHANGED, + PHONECONTROL_CALL_FAILED, + PHONECONTROL_CALLERID_RECEIVED, + PHONECONTROL_SEND_DTMF_SUCCEEDED}; + +} // namespace phonecontrol +} // namespace capabilities +} // namespace vshl + +#endif // VSHL_CAPABILITIES_PHONECONTROL_MESSAGES_H_ diff --git a/src/plugins/capabilities/communication/src/PhoneControlCapability.cpp b/src/plugins/capabilities/communication/src/PhoneControlCapability.cpp new file mode 100644 index 0000000..75296bf --- /dev/null +++ b/src/plugins/capabilities/communication/src/PhoneControlCapability.cpp @@ -0,0 +1,52 @@ +/* + * Copyright 2018-2019 Amazon.com, Inc. or its affiliates. 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. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0/ + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT 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 "capabilities/communication/include/PhoneControlCapability.h" +#include "capabilities/communication/include/PhoneControlMessages.h" + +const string TAG = "vshlcapabilities::capabilities::phonecontrol"; + +using Level = vshlcapabilities::common::interfaces::ILogger::Level; + +namespace vshlcapabilities { +namespace capabilities { +namespace phonecontrol { + +// Create a phonecontrol. +shared_ptr<PhoneControl> PhoneControl::create( + shared_ptr<vshlcapabilities::common::interfaces::ILogger> logger) { + auto phonecontrol = std::shared_ptr<PhoneControl>(new PhoneControl(logger)); + return phonecontrol; +} + +PhoneControl::PhoneControl( + shared_ptr<vshlcapabilities::common::interfaces::ILogger> logger) { + mLogger = logger; +} + +string PhoneControl::getName() const { + return NAME; +} + +list<string> PhoneControl::getUpstreamMessages() const { + return PHONECONTROL_UPSTREAM_ACTIONS; +} + +list<string> PhoneControl::getDownstreamMessages() const { + return PHONECONTROL_DOWNSTREAM_ACTIONS; +} + +} // namespace phonecontrol +} // namespace capabilities +} // namespace vshl diff --git a/src/plugins/capabilities/core/include/MessageChannel.h b/src/plugins/capabilities/core/include/MessageChannel.h new file mode 100644 index 0000000..79e4eab --- /dev/null +++ b/src/plugins/capabilities/core/include/MessageChannel.h @@ -0,0 +1,68 @@ +/* + * Copyright 2018-2019 Amazon.com, Inc. or its affiliates. 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. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0/ + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT 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 VSHL_CAPABILITIES_CORE_MESSAGECHANNEL_H_ +#define VSHL_CAPABILITIES_CORE_MESSAGECHANNEL_H_ + +#include <memory> + +#include "capabilities/core/include/PublisherForwarder.h" +#include "capabilities/core/include/SubscriberForwarder.h" +#include "interfaces/afb/IAFBApi.h" +#include "interfaces/capabilities/ICapability.h" +#include "interfaces/utilities/logging/ILogger.h" + +using namespace std; + +namespace vshlcapabilities { +namespace capabilities { +namespace core { +/* + * MessageChannel has one end as publisher forwarder and the other end + * as subscriber forwarder. + */ +class MessageChannel { +public: + // Create a MessageChannel. + static std::shared_ptr<MessageChannel> + create(shared_ptr<vshlcapabilities::common::interfaces::ILogger> logger, + shared_ptr<vshlcapabilities::common::interfaces::IAFBApi> afbApi, + shared_ptr<vshlcapabilities::common::interfaces::ICapability> capability); + + // Sends the message + bool publish(const string action, json_object* payload); + + // Subscribe + bool subscribe(vshlcapabilities::common::interfaces::IAFBRequest &request, + const string action); + + // Destructor + virtual ~MessageChannel() = default; + +private: + // Constructor + MessageChannel(shared_ptr<vshlcapabilities::common::interfaces::ILogger> logger, + shared_ptr<vshlcapabilities::common::interfaces::IAFBApi> afbApi, + shared_ptr<vshlcapabilities::common::interfaces::ICapability> capability); + + // Forwarders + shared_ptr<PublisherForwarder> mPublisherForwarder; + shared_ptr<SubscriberForwarder> mSubscriberForwarder; +}; + +} // namespace core +} // namespace capabilities +} // namespace vshl + +#endif // VSHL_CAPABILITIES_CORE_MESSAGECHANNEL_H_ diff --git a/src/plugins/capabilities/core/include/PublisherForwarder.h b/src/plugins/capabilities/core/include/PublisherForwarder.h new file mode 100644 index 0000000..01e9523 --- /dev/null +++ b/src/plugins/capabilities/core/include/PublisherForwarder.h @@ -0,0 +1,73 @@ +/* + * Copyright 2018-2019 Amazon.com, Inc. or its affiliates. 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. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0/ + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT 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 VSHL_CAPABILITIES_CORE_PUBLISHERFORWARDER_H_ +#define VSHL_CAPABILITIES_CORE_PUBLISHERFORWARDER_H_ + +#include <memory> + +#include "capabilities/core/include/SubscriberForwarder.h" + +#include "interfaces/afb/IAFBApi.h" +#include "interfaces/capabilities/ICapability.h" +#include "interfaces/utilities/logging/ILogger.h" + +using namespace std; + +namespace vshlcapabilities { +namespace capabilities { +namespace core { +/* + * This class is responsible for forwarding the messages to be published + * to subscriber forwarder. Subscriber forwarder will deliver the messages + * as AFB Events to all the subscribed clients. + * There is one PublisherForwarder and one SubscriberForwarder per capability. + */ +class PublisherForwarder { +public: + // Create a PublisherForwarder. + static std::shared_ptr<PublisherForwarder> create( + shared_ptr<vshlcapabilities::common::interfaces::ILogger> logger, + shared_ptr<vshlcapabilities::common::interfaces::ICapability> capability); + + // Connect a subscriber forwarder to this publisher forwarder + void setSubscriberForwarder(shared_ptr<SubscriberForwarder> subscriberForwarder); + + // Forward message to the subscriber forwarder + bool forwardMessage(const string action, json_object* payload); + + // Destructor + ~PublisherForwarder(); + +private: + // Constructor + PublisherForwarder( + shared_ptr<vshlcapabilities::common::interfaces::ILogger> logger, + shared_ptr<vshlcapabilities::common::interfaces::ICapability> capability); + + // Subscriber forwarder connected to this publisher forwarder. + shared_ptr<SubscriberForwarder> mSubscriberForwarder; + + // Capability + shared_ptr<vshlcapabilities::common::interfaces::ICapability> mCapability; + + // Logger + shared_ptr<vshlcapabilities::common::interfaces::ILogger> mLogger; +}; + +} // namespace core +} // namespace capabilities +} // namespace vshl + +#endif // VSHL_CAPABILITIES_CORE_PUBLISHERFORWARDER_H_ diff --git a/src/plugins/capabilities/core/include/SubscriberForwarder.h b/src/plugins/capabilities/core/include/SubscriberForwarder.h new file mode 100644 index 0000000..ac469b3 --- /dev/null +++ b/src/plugins/capabilities/core/include/SubscriberForwarder.h @@ -0,0 +1,84 @@ +/* + * Copyright 2018-2019 Amazon.com, Inc. or its affiliates. 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. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0/ + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT 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 VSHL_CAPABILITIES_CORE_SUBSCRIBERFORWARDER_H_ +#define VSHL_CAPABILITIES_CORE_SUBSCRIBERFORWARDER_H_ + +#include <memory> +#include <string> +#include <unordered_map> + +#include "interfaces/afb/IAFBApi.h" +#include "interfaces/capabilities/ICapability.h" +#include "interfaces/utilities/logging/ILogger.h" + +using namespace std; + +namespace vshlcapabilities { +namespace capabilities { +namespace core { +/* + * This class is responsible for forwarding the messages publishing + * to the actual clients using AFB. + */ +class SubscriberForwarder { +public: + // Create a SubscriberForwarder. + static std::shared_ptr<SubscriberForwarder> + create(shared_ptr<vshlcapabilities::common::interfaces::ILogger> logger, + shared_ptr<vshlcapabilities::common::interfaces::IAFBApi> afbApi, + shared_ptr<vshlcapabilities::common::interfaces::ICapability> capability); + + // Publish a capability message to the actual client. + bool forwardMessage(const string action, json_object* payload); + + // Subscribe + bool subscribe(vshlcapabilities::common::interfaces::IAFBRequest &request, + const string action); + + // Destructor + ~SubscriberForwarder(); + +private: + // Constructor + SubscriberForwarder( + shared_ptr<vshlcapabilities::common::interfaces::ILogger> logger, + shared_ptr<vshlcapabilities::common::interfaces::IAFBApi> afbApi, + shared_ptr<vshlcapabilities::common::interfaces::ICapability> capability); + + // Creates both upstream and downstream events + void createEvents(); + + // Binding API reference + shared_ptr<vshlcapabilities::common::interfaces::IAFBApi> mAfbApi; + + // Capability + shared_ptr<vshlcapabilities::common::interfaces::ICapability> mCapability; + + // Maps of capability action events to its corresponding Event object. + // Event name maps to Action Name + unordered_map<string, shared_ptr<common::interfaces::IAFBApi::IAFBEvent>> + mUpstreamEventsMap; + unordered_map<string, shared_ptr<common::interfaces::IAFBApi::IAFBEvent>> + mDownstreamEventsMap; + + // Logger + shared_ptr<vshlcapabilities::common::interfaces::ILogger> mLogger; +}; + +} // namespace core +} // namespace capabilities +} // namespace vshl + +#endif // VSHL_CAPABILITIES_CORE_SUBSCRIBERFORWARDER_H_ diff --git a/src/plugins/capabilities/core/src/MessageChannel.cpp b/src/plugins/capabilities/core/src/MessageChannel.cpp new file mode 100644 index 0000000..f4e55dd --- /dev/null +++ b/src/plugins/capabilities/core/src/MessageChannel.cpp @@ -0,0 +1,51 @@ +/* + * Copyright 2018-2019 Amazon.com, Inc. or its affiliates. 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. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0/ + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT 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 "capabilities/core/include/MessageChannel.h" + +namespace vshlcapabilities { +namespace capabilities { +namespace core { + +// Create a MessageChannel. +std::shared_ptr<MessageChannel> MessageChannel::create( + shared_ptr<vshlcapabilities::common::interfaces::ILogger> logger, + shared_ptr<vshlcapabilities::common::interfaces::IAFBApi> api, + shared_ptr<vshlcapabilities::common::interfaces::ICapability> capability) { + auto messageChannel = std::shared_ptr<MessageChannel>(new MessageChannel(logger, api, capability)); + return messageChannel; +} + +MessageChannel::MessageChannel( + shared_ptr<vshlcapabilities::common::interfaces::ILogger> logger, + shared_ptr<vshlcapabilities::common::interfaces::IAFBApi> api, + shared_ptr<vshlcapabilities::common::interfaces::ICapability> capability) { + // Subscriber forwarder + mSubscriberForwarder = SubscriberForwarder::create(logger, api, capability); + // Publisher forwarder + mPublisherForwarder = PublisherForwarder::create(logger, capability); + mPublisherForwarder->setSubscriberForwarder(mSubscriberForwarder); +} + +bool MessageChannel::publish(const string action, json_object* payload) { + return mPublisherForwarder->forwardMessage(action, payload); +} + +bool MessageChannel::subscribe(vshlcapabilities::common::interfaces::IAFBRequest& request, const string action) { + return mSubscriberForwarder->subscribe(request, action); +} + +} // namespace core +} // namespace capabilities +} // namespace vshl diff --git a/src/plugins/capabilities/core/src/PublisherForwarder.cpp b/src/plugins/capabilities/core/src/PublisherForwarder.cpp new file mode 100644 index 0000000..8f8f542 --- /dev/null +++ b/src/plugins/capabilities/core/src/PublisherForwarder.cpp @@ -0,0 +1,69 @@ +/* + * Copyright 2018-2019 Amazon.com, Inc. or its affiliates. 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. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0/ + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT 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 "capabilities/core/include/PublisherForwarder.h" + +static string TAG = "vshlcapabilities::capabilities::PublisherForwarder"; + +using Level = vshlcapabilities::common::interfaces::ILogger::Level; + +namespace vshlcapabilities { +namespace capabilities { +namespace core { + +// Create a PublisherForwarder. +std::shared_ptr<PublisherForwarder> PublisherForwarder::create( + shared_ptr<vshlcapabilities::common::interfaces::ILogger> logger, + shared_ptr<vshlcapabilities::common::interfaces::ICapability> capability) { + if (logger == nullptr) { + return nullptr; + } + + if (capability == nullptr) { + logger->log(Level::ERROR, TAG, "Failed to create PublisherForwarder: Capability null"); + return nullptr; + } + + auto publisherForwarder = std::shared_ptr<PublisherForwarder>(new PublisherForwarder(logger, capability)); + return publisherForwarder; +} + +// Constructor +PublisherForwarder::PublisherForwarder( + shared_ptr<vshlcapabilities::common::interfaces::ILogger> logger, + shared_ptr<vshlcapabilities::common::interfaces::ICapability> capability) { + mCapability = capability; + mLogger = logger; +} + +// Destructor +PublisherForwarder::~PublisherForwarder() { +} + +void PublisherForwarder::setSubscriberForwarder(shared_ptr<SubscriberForwarder> subscriberForwarder) { + mSubscriberForwarder = subscriberForwarder; +} + +bool PublisherForwarder::forwardMessage(const string action, json_object* payload) { + if (!mSubscriberForwarder) { + mLogger->log(Level::ERROR, TAG, "Failed to forward message for capability: " + mCapability->getName()); + return false; + } + + return mSubscriberForwarder->forwardMessage(action, payload); +} + +} // namespace core +} // namespace capabilities +} // namespace vshl diff --git a/src/plugins/capabilities/core/src/SubscriberForwarder.cpp b/src/plugins/capabilities/core/src/SubscriberForwarder.cpp new file mode 100644 index 0000000..970dbf0 --- /dev/null +++ b/src/plugins/capabilities/core/src/SubscriberForwarder.cpp @@ -0,0 +1,139 @@ +/* + * Copyright 2018-2019 Amazon.com, Inc. or its affiliates. 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. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0/ + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT 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 "capabilities/core/include/SubscriberForwarder.h" + +static string TAG = "vshlcapabilities::capabilities::SubscriberForwarder"; + +using Level = vshlcapabilities::common::interfaces::ILogger::Level; + +namespace vshlcapabilities { +namespace capabilities { +namespace core { + +// Create a SubscriberForwarder. +std::shared_ptr<SubscriberForwarder> SubscriberForwarder::create( + shared_ptr<vshlcapabilities::common::interfaces::ILogger> logger, + shared_ptr<vshlcapabilities::common::interfaces::IAFBApi> afbApi, + shared_ptr<vshlcapabilities::common::interfaces::ICapability> capability) { + if (logger == nullptr) { + return nullptr; + } + + if (afbApi == nullptr) { + logger->log(Level::ERROR, TAG, "Failed to create SubscriberForwarder: AFB API null"); + return nullptr; + } + + if (capability == nullptr) { + logger->log(Level::ERROR, TAG, "Failed to create SubscriberForwarder: Capability null"); + return nullptr; + } + + auto subscriberForwarder = + std::shared_ptr<SubscriberForwarder>(new SubscriberForwarder(logger, afbApi, capability)); + return subscriberForwarder; +} + +SubscriberForwarder::SubscriberForwarder( + shared_ptr<vshlcapabilities::common::interfaces::ILogger> logger, + shared_ptr<vshlcapabilities::common::interfaces::IAFBApi> afbApi, + shared_ptr<vshlcapabilities::common::interfaces::ICapability> capability) : + mAfbApi(afbApi), + mLogger(logger), + mCapability(capability) { + createEvents(); +} + +SubscriberForwarder::~SubscriberForwarder() { + mUpstreamEventsMap.clear(); + mDownstreamEventsMap.clear(); +} + +void SubscriberForwarder::createEvents() { + if (!mCapability) { + mLogger->log(Level::NOTICE, TAG, "Create Events failed. No capability assigned."); + return; + } + + // Upstream events + auto upstreamEvents = mCapability->getUpstreamMessages(); + for (auto upstreamEventName : upstreamEvents) { + auto it = mUpstreamEventsMap.find(upstreamEventName); + if (it == mUpstreamEventsMap.end() && mAfbApi) { + // create a new event and add it to the map. + shared_ptr<common::interfaces::IAFBApi::IAFBEvent> event = mAfbApi->createEvent(upstreamEventName); + if (event == nullptr) { + mLogger->log(Level::ERROR, TAG, "Failed to create upstream event: " + upstreamEventName); + } else { + mUpstreamEventsMap.insert(make_pair(upstreamEventName, event)); + } + } + } + + // Downstream events + auto downstreamEvents = mCapability->getDownstreamMessages(); + for (auto downstreamEventName : downstreamEvents) { + auto it = mDownstreamEventsMap.find(downstreamEventName); + if (it == mDownstreamEventsMap.end() && mAfbApi) { + // create a new event and add it to the map. + shared_ptr<common::interfaces::IAFBApi::IAFBEvent> event = mAfbApi->createEvent(downstreamEventName); + if (event == nullptr) { + mLogger->log(Level::ERROR, TAG, "Failed to create downstream event: " + downstreamEventName); + } else { + mDownstreamEventsMap.insert(make_pair(downstreamEventName, event)); + } + } + } +} + +bool SubscriberForwarder::forwardMessage(const string action, json_object* payload) { + auto upstreamEventIt = mUpstreamEventsMap.find(action); + if (upstreamEventIt != mUpstreamEventsMap.end()) { + mLogger->log(Level::NOTICE, TAG, "Publishing upstream event: " + action); + upstreamEventIt->second->publishEvent(payload); + return true; + } + + auto downstreamEventIt = mDownstreamEventsMap.find(action); + if (downstreamEventIt != mDownstreamEventsMap.end()) { + mLogger->log(Level::NOTICE, TAG, "Publishing downstream event: " + action); + downstreamEventIt->second->publishEvent(payload); + return true; + } + + mLogger->log(Level::NOTICE, TAG, "Failed to publish upstream event: " + action); + return false; +} + +bool SubscriberForwarder::subscribe(vshlcapabilities::common::interfaces::IAFBRequest& request, const string action) { + auto upstreamEventIt = mUpstreamEventsMap.find(action); + if (upstreamEventIt != mUpstreamEventsMap.end()) { + mLogger->log(Level::NOTICE, TAG, "Subscribing to upstream event: " + action); + return upstreamEventIt->second->subscribe(request); + } + + auto downstreamEventIt = mDownstreamEventsMap.find(action); + if (downstreamEventIt != mDownstreamEventsMap.end()) { + mLogger->log(Level::NOTICE, TAG, "Subscribing to downstream event: " + action); + return downstreamEventIt->second->subscribe(request); + } + + mLogger->log(Level::NOTICE, TAG, "Failed to subscribe to upstream event: " + action); + return false; +} + +} // namespace core +} // namespace capabilities +} // namespace vshl diff --git a/src/plugins/capabilities/guimetadata/include/GuiMetadataCapability.h b/src/plugins/capabilities/guimetadata/include/GuiMetadataCapability.h new file mode 100644 index 0000000..ecaf09f --- /dev/null +++ b/src/plugins/capabilities/guimetadata/include/GuiMetadataCapability.h @@ -0,0 +1,57 @@ +/* + * Copyright 2018-2019 Amazon.com, Inc. or its affiliates. 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. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0/ + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT 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 VSHL_CAPABILITIES_GUIMETADATA_CAPABILITY_H_ +#define VSHL_CAPABILITIES_GUIMETADATA_CAPABILITY_H_ + +#include <memory> + +#include "interfaces/capabilities/ICapability.h" +#include "interfaces/utilities/logging/ILogger.h" + +namespace vshlcapabilities { +namespace capabilities { +namespace guimetadata { + +/* + * GuiMetadata capability + */ +class GuiMetadata : public common::interfaces::ICapability { +public: + // Create a GuiMetadata. + static std::shared_ptr<GuiMetadata> create( + shared_ptr<vshlcapabilities::common::interfaces::ILogger> logger); + + ~GuiMetadata() = default; + +protected: + string getName() const override; + + list<string> getUpstreamMessages() const override; + + list<string> getDownstreamMessages() const override; + +private: + GuiMetadata( + shared_ptr<vshlcapabilities::common::interfaces::ILogger> logger); + + shared_ptr<vshlcapabilities::common::interfaces::ILogger> mLogger; +}; + +} // namespace guimetadata +} // namespace capabilities +} // namespace vshl + +#endif // VSHL_CAPABILITIES_GUIMETADATA_CAPABILITY_H_ diff --git a/src/plugins/capabilities/guimetadata/include/GuiMetadataMessages.h b/src/plugins/capabilities/guimetadata/include/GuiMetadataMessages.h new file mode 100644 index 0000000..07f4725 --- /dev/null +++ b/src/plugins/capabilities/guimetadata/include/GuiMetadataMessages.h @@ -0,0 +1,59 @@ +/* + * Copyright 2018-2019 Amazon.com, Inc. or its affiliates. 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. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0/ + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT 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 VSHL_CAPABILITIES_GUIMETADATA_ACTIONS_H_ +#define VSHL_CAPABILITIES_GUIMETADATA_ACTIONS_H_ + +#include <list> +#include <string> + +using namespace std; + +namespace vshlcapabilities { +namespace capabilities { +namespace guimetadata { + +static string NAME = "guimetadata"; + +/* + ****************************************************************************************************** + * Supported actions from VA -> APPS + ****************************************************************************************************** + */ +static string GUIMETADATA_RENDER_TEMPLATE = "render_template"; +static string GUIMETADATA_CLEAR_TEMPLATE = "clear_template"; +static string GUIMETADATA_RENDER_PLAYER_INFO = "render_player_info"; +static string GUIMETADATA_CLEAR_PLAYER_INFO = "clear_player_info"; + + +// List of actions that are delivered from VA -> Apps +static list<string> GUIMETADATA_UPSTREAM_ACTIONS = {GUIMETADATA_RENDER_TEMPLATE, + GUIMETADATA_CLEAR_TEMPLATE, + GUIMETADATA_RENDER_PLAYER_INFO, + GUIMETADATA_CLEAR_PLAYER_INFO}; + +/* + ****************************************************************************************************** + * Supported actions from APPS -> VA + ****************************************************************************************************** + */ + +// List of actions that are delivered from Apps -> VA +static list<string> GUIMETADATA_DOWNSTREAM_ACTIONS = {}; + +} // namespace guimetadata +} // namespace capabilities +} // namespace vshl + +#endif // VSHL_CAPABILITIES_GUIMETADATA_ACTIONS_H_ diff --git a/src/plugins/capabilities/guimetadata/src/GuiMetadataCapability.cpp b/src/plugins/capabilities/guimetadata/src/GuiMetadataCapability.cpp new file mode 100644 index 0000000..df90c91 --- /dev/null +++ b/src/plugins/capabilities/guimetadata/src/GuiMetadataCapability.cpp @@ -0,0 +1,52 @@ +/* + * Copyright 2018-2019 Amazon.com, Inc. or its affiliates. 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. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0/ + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT 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 "capabilities/guimetadata/include/GuiMetadataCapability.h" +#include "capabilities/guimetadata/include/GuiMetadataMessages.h" + +const string TAG = "vshlcapabilities::capabilities::guimetadata"; + +using Level = vshlcapabilities::common::interfaces::ILogger::Level; + +namespace vshlcapabilities { +namespace capabilities { +namespace guimetadata { + +// Create a GuiMetadata. +shared_ptr<GuiMetadata> GuiMetadata::create( + shared_ptr<vshlcapabilities::common::interfaces::ILogger> logger) { + auto guiMetadata = std::shared_ptr<GuiMetadata>(new GuiMetadata(logger)); + return guiMetadata; +} + +GuiMetadata::GuiMetadata( + shared_ptr<vshlcapabilities::common::interfaces::ILogger> logger) { + mLogger = logger; +} + +string GuiMetadata::getName() const { + return NAME; +} + +list<string> GuiMetadata::getUpstreamMessages() const { + return GUIMETADATA_UPSTREAM_ACTIONS; +} + +list<string> GuiMetadata::getDownstreamMessages() const { + return GUIMETADATA_DOWNSTREAM_ACTIONS; +} + +} // namespace guimetadata +} // namespace capabilities +} // namespace vshl diff --git a/src/plugins/capabilities/navigation/include/NavigationCapability.h b/src/plugins/capabilities/navigation/include/NavigationCapability.h new file mode 100644 index 0000000..bb055a8 --- /dev/null +++ b/src/plugins/capabilities/navigation/include/NavigationCapability.h @@ -0,0 +1,57 @@ +/* + * Copyright 2018-2019 Amazon.com, Inc. or its affiliates. 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. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0/ + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT 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 VSHL_CAPABILITIES_NAVIGATION_CAPABILITY_H_ +#define VSHL_CAPABILITIES_NAVIGATION_CAPABILITY_H_ + +#include <memory> + +#include "interfaces/capabilities/ICapability.h" +#include "interfaces/utilities/logging/ILogger.h" + +namespace vshlcapabilities { +namespace capabilities { +namespace navigation { + +/* + * Navigation capability + */ +class Navigation : public common::interfaces::ICapability { +public: + // Create a Navigation. + static std::shared_ptr<Navigation> create( + shared_ptr<vshlcapabilities::common::interfaces::ILogger> logger); + + ~Navigation() = default; + +protected: + string getName() const override; + + list<string> getUpstreamMessages() const override; + + list<string> getDownstreamMessages() const override; + +private: + Navigation( + shared_ptr<vshlcapabilities::common::interfaces::ILogger> logger); + + shared_ptr<vshlcapabilities::common::interfaces::ILogger> mLogger; +}; + +} // namespace navigation +} // namespace capabilities +} // namespace vshl + +#endif // VSHL_CAPABILITIES_NAVIGATION_CAPABILITY_H_ diff --git a/src/plugins/capabilities/navigation/include/NavigationMessages.h b/src/plugins/capabilities/navigation/include/NavigationMessages.h new file mode 100644 index 0000000..00c0c65 --- /dev/null +++ b/src/plugins/capabilities/navigation/include/NavigationMessages.h @@ -0,0 +1,56 @@ +/* + * Copyright 2018-2019 Amazon.com, Inc. or its affiliates. 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. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0/ + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT 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 VSHL_CAPABILITIES_NAVIGATION_ACTIONS_H_ +#define VSHL_CAPABILITIES_NAVIGATION_ACTIONS_H_ + +#include <list> +#include <string> + +using namespace std; + +namespace vshlcapabilities { +namespace capabilities { +namespace navigation { + +static string NAME = "navigation"; + +/* + ****************************************************************************************************** + * Supported actions from VA -> APPS + ****************************************************************************************************** + */ +static string NAVIGATION_SET_DESTINATION = "set_destination"; +static string NAVIGATION_CANCEL = "cancel_navigation"; + +// Supported actions from Apps -> VA + +// List of actions that are delivered from VA -> Apps +static list<string> NAVIGATION_UPSTREAM_ACTIONS = { + NAVIGATION_SET_DESTINATION, + NAVIGATION_CANCEL, +}; + +/* + ****************************************************************************************************** + * Supported actions from APPS -> VA + ****************************************************************************************************** + */ +static list<string> NAVIGATION_DOWNSTREAM_ACTIONS = {}; + +} // namespace navigation +} // namespace capabilities +} // namespace vshl + +#endif // VSHL_CAPABILITIES_NAVIGATION_ACTIONS_H_ diff --git a/src/plugins/capabilities/navigation/src/NavigationCapability.cpp b/src/plugins/capabilities/navigation/src/NavigationCapability.cpp new file mode 100644 index 0000000..e1c6cda --- /dev/null +++ b/src/plugins/capabilities/navigation/src/NavigationCapability.cpp @@ -0,0 +1,52 @@ +/* + * Copyright 2018-2019 Amazon.com, Inc. or its affiliates. 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. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0/ + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT 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 "capabilities/navigation/include/NavigationCapability.h" +#include "capabilities/navigation/include/NavigationMessages.h" + +const string TAG = "vshlcapabilities::capabilities::navigation"; + +using Level = vshlcapabilities::common::interfaces::ILogger::Level; + +namespace vshlcapabilities { +namespace capabilities { +namespace navigation { + +// Create a Navigation. +shared_ptr<Navigation> Navigation::create( + shared_ptr<vshlcapabilities::common::interfaces::ILogger> logger) { + auto navigation = std::shared_ptr<Navigation>(new Navigation(logger)); + return navigation; +} + +Navigation::Navigation( + shared_ptr<vshlcapabilities::common::interfaces::ILogger> logger) { + mLogger = logger; +} + +string Navigation::getName() const { + return NAME; +} + +list<string> Navigation::getUpstreamMessages() const { + return NAVIGATION_UPSTREAM_ACTIONS; +} + +list<string> Navigation::getDownstreamMessages() const { + return NAVIGATION_DOWNSTREAM_ACTIONS; +} + +} // namespace navigation +} // namespace capabilities +} // namespace vshl diff --git a/src/plugins/capabilities/playbackcontroller/include/PlaybackControllerCapability.h b/src/plugins/capabilities/playbackcontroller/include/PlaybackControllerCapability.h new file mode 100644 index 0000000..e80b213 --- /dev/null +++ b/src/plugins/capabilities/playbackcontroller/include/PlaybackControllerCapability.h @@ -0,0 +1,57 @@ +/* + * Copyright 2018-2019 Amazon.com, Inc. or its affiliates. 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. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0/ + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT 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 VSHL_CAPABILITIES_PLAYBACKCONTROLLER_CAPABILITY_H_ +#define VSHL_CAPABILITIES_PLAYBACKCONTROLLER_CAPABILITY_H_ + +#include <memory> + +#include "interfaces/capabilities/ICapability.h" +#include "interfaces/utilities/logging/ILogger.h" + +namespace vshlcapabilities { +namespace capabilities { +namespace playbackcontroller { + +/* + * PlaybackController capability. Calls are initiated in the endpoint. + */ +class PlaybackController : public common::interfaces::ICapability { +public: + // Create a PlaybackController. + static std::shared_ptr<PlaybackController> create( + shared_ptr<vshlcapabilities::common::interfaces::ILogger> logger); + + ~PlaybackController() = default; + +protected: + string getName() const override; + + list<string> getUpstreamMessages() const override; + + list<string> getDownstreamMessages() const override; + +private: + PlaybackController( + shared_ptr<vshlcapabilities::common::interfaces::ILogger> logger); + + shared_ptr<vshlcapabilities::common::interfaces::ILogger> mLogger; +}; + +} // namespace playbackcontroller +} // namespace capabilities +} // namespace vshl + +#endif // VSHL_CAPABILITIES_PLAYBACKCONTROLLER_CAPABILITY_H_ diff --git a/src/plugins/capabilities/playbackcontroller/include/PlaybackControllerMessages.h b/src/plugins/capabilities/playbackcontroller/include/PlaybackControllerMessages.h new file mode 100644 index 0000000..224b8dd --- /dev/null +++ b/src/plugins/capabilities/playbackcontroller/include/PlaybackControllerMessages.h @@ -0,0 +1,64 @@ +/* + * Copyright 2018-2019 Amazon.com, Inc. or its affiliates. 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. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0/ + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT 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 VSHL_CAPABILITIES_PLAYBACKCONTROLLER_MESSAGES_H_ +#define VSHL_CAPABILITIES_PLAYBACKCONTROLLER_MESSAGES_H_ + +#include <list> +#include <string> + +using namespace std; + +namespace vshlcapabilities { +namespace capabilities { +namespace playbackcontroller { + +static string NAME = "playbackcontroller"; + +/* + ****************************************************************************************************** + * Supported actions from APPS -> VA + ****************************************************************************************************** + */ +// List of actions that are delivered from VA -> Apps +static list<string> PLAYBACKCONTROLLER_UPSTREAM_ACTIONS = { + +}; + + +/* + ****************************************************************************************************** + * Supported actions from VA -> APPS + ****************************************************************************************************** + */ +// List of actions that are delivered from Apps -> VA +/* + * payload : + * { + * "button" : "{{STRING}}" // play, pause, next, previous, skip-forward, skip-backward + * } + * @param [in] button Button pressed by the user. + * + */ +static string PLAYBACKCONTROLLER_BUTTONPRESSED = "button_pressed"; + +static list<string> PLAYBACKCONTROLLER_DOWNSTREAM_ACTIONS = { + PLAYBACKCONTROLLER_BUTTONPRESSED +}; + +} // namespace playbackcontroller +} // namespace capabilities +} // namespace vshl + +#endif // VSHL_CAPABILITIES_PLAYBACKCONTROLLER_MESSAGES_H_ diff --git a/src/plugins/capabilities/playbackcontroller/src/PlaybackControllerCapability.cpp b/src/plugins/capabilities/playbackcontroller/src/PlaybackControllerCapability.cpp new file mode 100644 index 0000000..a5abbed --- /dev/null +++ b/src/plugins/capabilities/playbackcontroller/src/PlaybackControllerCapability.cpp @@ -0,0 +1,52 @@ +/* + * Copyright 2018-2019 Amazon.com, Inc. or its affiliates. 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. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0/ + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT 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 "capabilities/playbackcontroller/include/PlaybackControllerCapability.h" +#include "capabilities/playbackcontroller/include/PlaybackControllerMessages.h" + +const string TAG = "vshlcapabilities::capabilities::playbackcontroller"; + +using Level = vshlcapabilities::common::interfaces::ILogger::Level; + +namespace vshlcapabilities { +namespace capabilities { +namespace playbackcontroller { + +// Create a playbackcontroller. +shared_ptr<PlaybackController> PlaybackController::create( + shared_ptr<vshlcapabilities::common::interfaces::ILogger> logger) { + auto playbackcontroller = std::shared_ptr<PlaybackController>(new PlaybackController(logger)); + return playbackcontroller; +} + +PlaybackController::PlaybackController( + shared_ptr<vshlcapabilities::common::interfaces::ILogger> logger) { + mLogger = logger; +} + +string PlaybackController::getName() const { + return NAME; +} + +list<string> PlaybackController::getUpstreamMessages() const { + return PLAYBACKCONTROLLER_UPSTREAM_ACTIONS; +} + +list<string> PlaybackController::getDownstreamMessages() const { + return PLAYBACKCONTROLLER_DOWNSTREAM_ACTIONS; +} + +} // namespace playbackcontroller +} // namespace capabilities +} // namespace vshl diff --git a/src/plugins/capabilities/test/CapabilityMessagingServiceTest.cpp b/src/plugins/capabilities/test/CapabilityMessagingServiceTest.cpp new file mode 100644 index 0000000..5d3971d --- /dev/null +++ b/src/plugins/capabilities/test/CapabilityMessagingServiceTest.cpp @@ -0,0 +1,96 @@ +/* + * Copyright 2018-2019 Amazon.com, Inc. or its affiliates. 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. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0/ + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT 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 <gtest/gtest.h> + +#include "capabilities/CapabilityMessagingService.h" + +#include "test/common/ConsoleLogger.h" +#include "test/mocks/AFBApiMock.h" +#include "test/mocks/AFBEventMock.h" +#include "test/mocks/AFBRequestMock.h" +#include "test/mocks/CapabilityMock.h" + +using namespace vshlcapabilities::common::interfaces; +using namespace vshlcapabilities::capabilities; +using namespace vshlcapabilities::test::common; + +namespace vshlcapabilities { +namespace test { + +class CapabilityMessagingServiceTest : public ::testing::Test { +protected: + void SetUp() override { + mConsoleLogger = std::make_shared<ConsoleLogger>(); + mAfbApi = std::make_shared<::testing::NiceMock<AFBApiMock>>(); + } + + std::shared_ptr<::testing::NiceMock<AFBApiMock>> mAfbApi; + std::shared_ptr<ConsoleLogger> mConsoleLogger; +}; + +TEST_F(CapabilityMessagingServiceTest, failsInitializationOnInvalidParams) { + auto service = CapabilityMessagingService::create(mConsoleLogger, nullptr); + ASSERT_EQ(service, nullptr); + + service = CapabilityMessagingService::create(nullptr, mAfbApi); + ASSERT_EQ(service, nullptr); +} + +TEST_F(CapabilityMessagingServiceTest, initializesSuccessfully) { + auto service = CapabilityMessagingService::create(mConsoleLogger, mAfbApi); + ASSERT_NE(service, nullptr); +} + +TEST_F(CapabilityMessagingServiceTest, canSubscribeAndPublishCapabilityEvents) { + auto service = CapabilityMessagingService::create(mConsoleLogger, mAfbApi); + + auto capability = std::make_shared<::testing::NiceMock<CapabilityMock>>(); + std::list<std::string> upstreamEvents({"up-ev1", "up-ev2"}); + std::list<std::string> downstreamEvents({"down-ev1", "down-ev2"}); + std::string capabilityName = "weather"; + + ON_CALL(*capability, getName()).WillByDefault(::testing::Return(capabilityName)); + ON_CALL(*capability, getUpstreamMessages()).WillByDefault(::testing::Return(upstreamEvents)); + ON_CALL(*capability, getDownstreamMessages()).WillByDefault(::testing::Return(downstreamEvents)); + + // We don't care if and how many times subscribe method is called on event. + std::shared_ptr<AFBEventMock> mockEvent(new ::testing::NiceMock<AFBEventMock>()); + ON_CALL(*mockEvent, subscribe(::testing::_)).WillByDefault(::testing::Return(true)); + ON_CALL(*mockEvent, publishEvent(::testing::_)).WillByDefault(::testing::Return(true)); + auto eventCreator = [mockEvent](const std::string& eventName) -> std::shared_ptr<IAFBApi::IAFBEvent> { + mockEvent->setName(eventName); + return mockEvent; + }; + + ON_CALL(*mAfbApi, createEvent(::testing::_)).WillByDefault(::testing::Invoke(eventCreator)); + + auto request = std::make_shared<::testing::StrictMock<AFBRequestMock>>(); + std::string payload = "The answer to life the universe and everything = 42"; + + bool result = service->publish(capability, *upstreamEvents.begin(), payload); + ASSERT_FALSE(result); // Without subscribing to the event. Publish should fail. + + result = service->subscribe(*request, capability, *upstreamEvents.begin()); + ASSERT_TRUE(result); + + result = service->subscribe(*request, capability, *downstreamEvents.begin()); + ASSERT_TRUE(result); + + result = service->publish(capability, *downstreamEvents.begin(), payload); + ASSERT_TRUE(result); +} + +} // namespace test +} // namespace vshl
\ No newline at end of file diff --git a/src/plugins/capabilities/test/PublisherForwarderTest.cpp b/src/plugins/capabilities/test/PublisherForwarderTest.cpp new file mode 100644 index 0000000..8fb499c --- /dev/null +++ b/src/plugins/capabilities/test/PublisherForwarderTest.cpp @@ -0,0 +1,116 @@ +/* + * Copyright 2018-2019 Amazon.com, Inc. or its affiliates. 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. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0/ + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT 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 <gtest/gtest.h> + +#include "capabilities/core/include/PublisherForwarder.h" +#include "capabilities/core/include/SubscriberForwarder.h" + +#include "test/common/ConsoleLogger.h" +#include "test/mocks/AFBApiMock.h" +#include "test/mocks/AFBEventMock.h" +#include "test/mocks/CapabilityMock.h" + +using namespace vshlcapabilities::common::interfaces; +using namespace vshlcapabilities::capabilities::core; +using namespace vshlcapabilities::test::common; + +namespace vshlcapabilities { +namespace test { + +class PublisherForwarderTest : public ::testing::Test { +protected: + void SetUp() override { + mConsoleLogger = std::make_shared<ConsoleLogger>(); + mAfbApi = std::make_shared<::testing::StrictMock<AFBApiMock>>(); + + mEventCreatorFn = [](const std::string& eventName) -> std::shared_ptr<IAFBApi::IAFBEvent> { + std::shared_ptr<AFBEventMock> mockEvent(new ::testing::StrictMock<AFBEventMock>()); + mockEvent->setName(eventName); + return mockEvent; + }; + } + + std::shared_ptr<SubscriberForwarder> createSubscriberForwarder( + std::shared_ptr<::testing::StrictMock<CapabilityMock>> capability) { + EXPECT_CALL(*capability, getUpstreamMessages()).Times(1); + EXPECT_CALL(*capability, getDownstreamMessages()).Times(1); + + return SubscriberForwarder::create(mConsoleLogger, mAfbApi, capability); + } + + std::shared_ptr<PublisherForwarder> createPublisherForwarder( + std::shared_ptr<::testing::StrictMock<CapabilityMock>> capability) { + return PublisherForwarder::create(mConsoleLogger, capability); + } + + std::shared_ptr<::testing::StrictMock<AFBApiMock>> mAfbApi; + std::shared_ptr<ConsoleLogger> mConsoleLogger; + std::function<std::shared_ptr<IAFBApi::IAFBEvent>(const std::string&)> mEventCreatorFn; + std::shared_ptr<::testing::StrictMock<AFBEventMock>> mAfbEventMock; +}; + +TEST_F(PublisherForwarderTest, failsInitializationOnInvalidParams) { + auto capability = std::make_shared<::testing::StrictMock<CapabilityMock>>(); + + auto forwarder = PublisherForwarder::create(mConsoleLogger, nullptr); + ASSERT_EQ(forwarder, nullptr); + + forwarder = PublisherForwarder::create(nullptr, capability); + ASSERT_EQ(forwarder, nullptr); +} + +TEST_F(PublisherForwarderTest, initializesCorrectly) { + auto capability = std::make_shared<::testing::StrictMock<CapabilityMock>>(); + auto forwarder = createPublisherForwarder(capability); + + ASSERT_NE(forwarder, nullptr); +} + +TEST_F(PublisherForwarderTest, canForwardMessages) { + std::shared_ptr<AFBEventMock> mockEvent(new ::testing::StrictMock<AFBEventMock>()); + ON_CALL(*mockEvent, publishEvent(::testing::_)).WillByDefault(::testing::Return(true)); + EXPECT_CALL(*mockEvent, publishEvent(::testing::_)).Times(4); + auto eventCreator = [mockEvent](const std::string& eventName) -> std::shared_ptr<IAFBApi::IAFBEvent> { + return mockEvent; + }; + + ON_CALL(*mAfbApi, createEvent(::testing::_)).WillByDefault(::testing::Invoke(eventCreator)); + EXPECT_CALL(*mAfbApi, createEvent(::testing::_)).Times(4); + + auto capability = std::make_shared<::testing::StrictMock<CapabilityMock>>(); + std::list<std::string> upstreamEvents({"up-ev1", "up-ev2"}); + std::list<std::string> downstreamEvents({"down-ev1", "down-ev2"}); + ON_CALL(*capability, getUpstreamMessages()).WillByDefault(::testing::Return(upstreamEvents)); + ON_CALL(*capability, getDownstreamMessages()).WillByDefault(::testing::Return(downstreamEvents)); + + auto publisherForwarder = createPublisherForwarder(capability); + ASSERT_NE(publisherForwarder, nullptr); + + auto subscriberForwarder = createSubscriberForwarder(capability); + ASSERT_NE(subscriberForwarder, nullptr); + + std::string payload = "The answer to life the universe and everything = 42"; + publisherForwarder->setSubscriberForwarder(subscriberForwarder); + + auto itCapability = downstreamEvents.begin(); + ASSERT_TRUE(publisherForwarder->forwardMessage(*itCapability++, payload)); + ASSERT_TRUE(publisherForwarder->forwardMessage(*itCapability++, payload)); + itCapability = upstreamEvents.begin(); + ASSERT_TRUE(publisherForwarder->forwardMessage(*itCapability++, payload)); + ASSERT_TRUE(publisherForwarder->forwardMessage(*itCapability++, payload)); +} + +} // namespace test +} // namespace vshl
\ No newline at end of file diff --git a/src/plugins/capabilities/test/SubscriberForwarderTest.cpp b/src/plugins/capabilities/test/SubscriberForwarderTest.cpp new file mode 100644 index 0000000..c3d72de --- /dev/null +++ b/src/plugins/capabilities/test/SubscriberForwarderTest.cpp @@ -0,0 +1,262 @@ +/* + * Copyright 2018-2019 Amazon.com, Inc. or its affiliates. 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. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0/ + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT 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 <gtest/gtest.h> + +#include "capabilities/core/include/SubscriberForwarder.h" + +#include "test/common/ConsoleLogger.h" +#include "test/mocks/AFBApiMock.h" +#include "test/mocks/AFBEventMock.h" +#include "test/mocks/AFBRequestMock.h" +#include "test/mocks/CapabilityMock.h" + +using namespace vshlcapabilities::common::interfaces; +using namespace vshlcapabilities::capabilities::core; +using namespace vshlcapabilities::test::common; + +namespace vshlcapabilities { +namespace test { + +class SubscriberForwarderTest : public ::testing::Test { +protected: + void SetUp() override { + mConsoleLogger = std::make_shared<ConsoleLogger>(); + mAfbApi = std::make_shared<::testing::StrictMock<AFBApiMock>>(); + + mEventCreatorFn = [](const std::string& eventName) -> std::shared_ptr<IAFBApi::IAFBEvent> { + std::shared_ptr<AFBEventMock> mockEvent(new ::testing::StrictMock<AFBEventMock>()); + mockEvent->setName(eventName); + return mockEvent; + }; + } + + std::shared_ptr<SubscriberForwarder> createSubscriberForwarder( + std::shared_ptr<::testing::StrictMock<CapabilityMock>> capability) { + EXPECT_CALL(*capability, getUpstreamMessages()).Times(1); + EXPECT_CALL(*capability, getDownstreamMessages()).Times(1); + + return SubscriberForwarder::create(mConsoleLogger, mAfbApi, capability); + } + + std::shared_ptr<::testing::StrictMock<AFBApiMock>> mAfbApi; + std::shared_ptr<ConsoleLogger> mConsoleLogger; + std::function<std::shared_ptr<IAFBApi::IAFBEvent>(const std::string&)> mEventCreatorFn; + std::shared_ptr<::testing::StrictMock<AFBEventMock>> mAfbEventMock; +}; + +TEST_F(SubscriberForwarderTest, failsInitializationOnInvalidParams) { + auto capability = std::make_shared<::testing::StrictMock<CapabilityMock>>(); + + auto forwarder = SubscriberForwarder::create(mConsoleLogger, mAfbApi, nullptr); + ASSERT_EQ(forwarder, nullptr); + + forwarder = SubscriberForwarder::create(mConsoleLogger, nullptr, capability); + ASSERT_EQ(forwarder, nullptr); + + forwarder = SubscriberForwarder::create(nullptr, mAfbApi, capability); + ASSERT_EQ(forwarder, nullptr); +} + +TEST_F(SubscriberForwarderTest, initializesCorrectly) { + auto capability = std::make_shared<::testing::StrictMock<CapabilityMock>>(); + auto forwarder = createSubscriberForwarder(capability); + + ASSERT_NE(forwarder, nullptr); +} + +TEST_F(SubscriberForwarderTest, createsEventsOnInitialization) { + ON_CALL(*mAfbApi, createEvent(::testing::_)).WillByDefault(::testing::Invoke(mEventCreatorFn)); + + std::list<std::string> upstreamEvents({"up-ev1", "up-ev2"}); + std::list<std::string> downstreamEvents({"down-ev1", "down-ev2"}); + + auto capability = std::make_shared<::testing::StrictMock<CapabilityMock>>(); + ON_CALL(*capability, getUpstreamMessages()).WillByDefault(::testing::Return(upstreamEvents)); + ON_CALL(*capability, getDownstreamMessages()).WillByDefault(::testing::Return(downstreamEvents)); + + auto itCapability = upstreamEvents.begin(); + EXPECT_CALL(*mAfbApi, createEvent(*itCapability)).Times(1); + itCapability++; + EXPECT_CALL(*mAfbApi, createEvent(*itCapability)).Times(1); + + itCapability = downstreamEvents.begin(); + EXPECT_CALL(*mAfbApi, createEvent(*itCapability)).Times(1); + itCapability++; + EXPECT_CALL(*mAfbApi, createEvent(*itCapability)).Times(1); + + auto forwarder = createSubscriberForwarder(capability); + ASSERT_NE(forwarder, nullptr); +} + +TEST_F(SubscriberForwarderTest, canNotSubscribeToNonExistentEvents) { + ON_CALL(*mAfbApi, createEvent(::testing::_)).WillByDefault(::testing::Invoke(mEventCreatorFn)); + EXPECT_CALL(*mAfbApi, createEvent(::testing::_)).Times(4); + + std::list<std::string> upstreamEvents({"up-ev1", "up-ev2"}); + std::list<std::string> downstreamEvents({"down-ev1", "down-ev2"}); + + auto capability = std::make_shared<::testing::StrictMock<CapabilityMock>>(); + ON_CALL(*capability, getUpstreamMessages()).WillByDefault(::testing::Return(upstreamEvents)); + ON_CALL(*capability, getDownstreamMessages()).WillByDefault(::testing::Return(downstreamEvents)); + + auto forwarder = createSubscriberForwarder(capability); + ASSERT_NE(forwarder, nullptr); + + auto nonExistentEvents = std::list<std::string>({"non", "existent", "events"}); + auto itCapability = nonExistentEvents.begin(); + auto request = std::make_shared<::testing::StrictMock<AFBRequestMock>>(); + ASSERT_FALSE(forwarder->subscribe(*request, *itCapability++)); + ASSERT_FALSE(forwarder->subscribe(*request, *itCapability++)); + ASSERT_FALSE(forwarder->subscribe(*request, *itCapability++)); +} + +TEST_F(SubscriberForwarderTest, canNotSubscribeOnEventCreationFailure) { + // Fail the event creation and return null event. + ON_CALL(*mAfbApi, createEvent(::testing::_)).WillByDefault(::testing::Return(nullptr)); + EXPECT_CALL(*mAfbApi, createEvent(::testing::_)).Times(4); + + std::list<std::string> upstreamEvents({"up-ev1", "up-ev2"}); + std::list<std::string> downstreamEvents({"down-ev1", "down-ev2"}); + + auto capability = std::make_shared<::testing::StrictMock<CapabilityMock>>(); + ON_CALL(*capability, getUpstreamMessages()).WillByDefault(::testing::Return(upstreamEvents)); + ON_CALL(*capability, getDownstreamMessages()).WillByDefault(::testing::Return(downstreamEvents)); + + auto forwarder = createSubscriberForwarder(capability); + ASSERT_NE(forwarder, nullptr); + + auto itCapability = downstreamEvents.begin(); + auto request = std::make_shared<::testing::StrictMock<AFBRequestMock>>(); + std::string payload = "The answer to life the universe and everything = 42"; + ASSERT_FALSE(forwarder->subscribe(*request, *itCapability++)); + ASSERT_FALSE(forwarder->subscribe(*request, *itCapability++)); + itCapability = upstreamEvents.begin(); + ASSERT_FALSE(forwarder->subscribe(*request, *itCapability++)); + ASSERT_FALSE(forwarder->subscribe(*request, *itCapability++)); +} + +TEST_F(SubscriberForwarderTest, canSubscribeToEvents) { + std::shared_ptr<AFBEventMock> mockEvent(new ::testing::StrictMock<AFBEventMock>()); + ON_CALL(*mockEvent, subscribe(::testing::_)).WillByDefault(::testing::Return(true)); + EXPECT_CALL(*mockEvent, subscribe(::testing::_)).Times(4); + auto eventCreator = [mockEvent](const std::string& eventName) -> std::shared_ptr<IAFBApi::IAFBEvent> { + return mockEvent; + }; + + ON_CALL(*mAfbApi, createEvent(::testing::_)).WillByDefault(::testing::Invoke(eventCreator)); + EXPECT_CALL(*mAfbApi, createEvent(::testing::_)).Times(4); + + std::list<std::string> upstreamEvents({"up-ev1", "up-ev2"}); + std::list<std::string> downstreamEvents({"down-ev1", "down-ev2"}); + + auto capability = std::make_shared<::testing::StrictMock<CapabilityMock>>(); + ON_CALL(*capability, getUpstreamMessages()).WillByDefault(::testing::Return(upstreamEvents)); + ON_CALL(*capability, getDownstreamMessages()).WillByDefault(::testing::Return(downstreamEvents)); + + auto forwarder = createSubscriberForwarder(capability); + ASSERT_NE(forwarder, nullptr); + + auto itCapability = downstreamEvents.begin(); + auto request = std::make_shared<::testing::StrictMock<AFBRequestMock>>(); + ASSERT_TRUE(forwarder->subscribe(*request, *itCapability++)); + ASSERT_TRUE(forwarder->subscribe(*request, *itCapability)); + itCapability = upstreamEvents.begin(); + ASSERT_TRUE(forwarder->subscribe(*request, *itCapability++)); + ASSERT_TRUE(forwarder->subscribe(*request, *itCapability)); +} + +TEST_F(SubscriberForwarderTest, canNotPublishNonExistentEvents) { + std::shared_ptr<AFBEventMock> mockEvent(new ::testing::StrictMock<AFBEventMock>()); + EXPECT_CALL(*mockEvent, publishEvent(::testing::_)).Times(0); + auto eventCreator = [mockEvent](const std::string& eventName) -> std::shared_ptr<IAFBApi::IAFBEvent> { + return mockEvent; + }; + + ON_CALL(*mAfbApi, createEvent(::testing::_)).WillByDefault(::testing::Invoke(eventCreator)); + EXPECT_CALL(*mAfbApi, createEvent(::testing::_)).Times(4); + + std::list<std::string> upstreamEvents({"up-ev1", "up-ev2"}); + std::list<std::string> downstreamEvents({"down-ev1", "down-ev2"}); + + auto capability = std::make_shared<::testing::StrictMock<CapabilityMock>>(); + ON_CALL(*capability, getUpstreamMessages()).WillByDefault(::testing::Return(upstreamEvents)); + ON_CALL(*capability, getDownstreamMessages()).WillByDefault(::testing::Return(downstreamEvents)); + + auto forwarder = createSubscriberForwarder(capability); + ASSERT_NE(forwarder, nullptr); + + auto nonExistentEvents = std::list<std::string>({"non", "existent", "events"}); + auto itCapability = nonExistentEvents.begin(); + ASSERT_FALSE(forwarder->forwardMessage(*itCapability++, "My-Payload")); + ASSERT_FALSE(forwarder->forwardMessage(*itCapability++, "My-Payload")); + ASSERT_FALSE(forwarder->forwardMessage(*itCapability++, "My-Payload")); +} + +TEST_F(SubscriberForwarderTest, canNotPublishOnEventCreationFailure) { + // Fail the event creation and return null event. + ON_CALL(*mAfbApi, createEvent(::testing::_)).WillByDefault(::testing::Return(nullptr)); + EXPECT_CALL(*mAfbApi, createEvent(::testing::_)).Times(4); + + std::list<std::string> upstreamEvents({"up-ev1", "up-ev2"}); + std::list<std::string> downstreamEvents({"down-ev1", "down-ev2"}); + + auto capability = std::make_shared<::testing::StrictMock<CapabilityMock>>(); + ON_CALL(*capability, getUpstreamMessages()).WillByDefault(::testing::Return(upstreamEvents)); + ON_CALL(*capability, getDownstreamMessages()).WillByDefault(::testing::Return(downstreamEvents)); + + auto forwarder = createSubscriberForwarder(capability); + ASSERT_NE(forwarder, nullptr); + + auto itCapability = downstreamEvents.begin(); + std::string payload = "The answer to life the universe and everything = 42"; + ASSERT_FALSE(forwarder->forwardMessage(*itCapability++, payload)); + ASSERT_FALSE(forwarder->forwardMessage(*itCapability++, payload)); + itCapability = upstreamEvents.begin(); + ASSERT_FALSE(forwarder->forwardMessage(*itCapability++, payload)); + ASSERT_FALSE(forwarder->forwardMessage(*itCapability++, payload)); +} + +TEST_F(SubscriberForwarderTest, canPublishEvents) { + std::shared_ptr<AFBEventMock> mockEvent(new ::testing::StrictMock<AFBEventMock>()); + ON_CALL(*mockEvent, publishEvent(::testing::_)).WillByDefault(::testing::Return(true)); + EXPECT_CALL(*mockEvent, publishEvent(::testing::_)).Times(4); + auto eventCreator = [mockEvent](const std::string& eventName) -> std::shared_ptr<IAFBApi::IAFBEvent> { + return mockEvent; + }; + + ON_CALL(*mAfbApi, createEvent(::testing::_)).WillByDefault(::testing::Invoke(eventCreator)); + EXPECT_CALL(*mAfbApi, createEvent(::testing::_)).Times(4); + + std::list<std::string> upstreamEvents({"up-ev1", "up-ev2"}); + std::list<std::string> downstreamEvents({"down-ev1", "down-ev2"}); + + auto capability = std::make_shared<::testing::StrictMock<CapabilityMock>>(); + ON_CALL(*capability, getUpstreamMessages()).WillByDefault(::testing::Return(upstreamEvents)); + ON_CALL(*capability, getDownstreamMessages()).WillByDefault(::testing::Return(downstreamEvents)); + + auto forwarder = createSubscriberForwarder(capability); + ASSERT_NE(forwarder, nullptr); + + auto itCapability = downstreamEvents.begin(); + std::string payload = "The answer to life the universe and everything = 42"; + ASSERT_TRUE(forwarder->forwardMessage(*itCapability++, payload)); + ASSERT_TRUE(forwarder->forwardMessage(*itCapability++, payload)); + itCapability = upstreamEvents.begin(); + ASSERT_TRUE(forwarder->forwardMessage(*itCapability++, payload)); + ASSERT_TRUE(forwarder->forwardMessage(*itCapability++, payload)); +} + +} // namespace test +} // namespace vshl
\ No newline at end of file diff --git a/src/plugins/cmake/gtest.cmake b/src/plugins/cmake/gtest.cmake new file mode 100644 index 0000000..def6559 --- /dev/null +++ b/src/plugins/cmake/gtest.cmake @@ -0,0 +1,44 @@ +# gtest + +find_package(Threads REQUIRED) + +# Enable ExternalProject CMake module +INCLUDE(ExternalProject) + +ExternalProject_Add( + gtest + URL https://github.com/google/googletest/archive/release-1.8.1.zip + SOURCE_DIR "${CMAKE_BINARY_DIR}/gtest-src" + BINARY_DIR "${CMAKE_BINARY_DIR}/gtest-build" + INSTALL_COMMAND "" + LOG_DOWNLOAD ON + LOG_CONFIGURE ON + LOG_BUILD ON +) + +# Get GTest source and binary directories from CMake project +ExternalProject_Get_Property(gtest source_dir binary_dir) + +# Create a libgtest target to be used as a dependency by test programs +ADD_LIBRARY(libgtest INTERFACE) +TARGET_INCLUDE_DIRECTORIES(libgtest + INTERFACE + ${source_dir}/googletest/include +) +TARGET_LINK_LIBRARIES(libgtest + INTERFACE + ${binary_dir}/googlemock/gtest/libgtest.a + ${CMAKE_THREAD_LIBS_INIT} +) + +# Create a libgmock target to be used as a dependency by test programs +ADD_LIBRARY(libgmock INTERFACE) +TARGET_INCLUDE_DIRECTORIES(libgmock + INTERFACE + ${source_dir}/googlemock/include +) +TARGET_LINK_LIBRARIES(libgmock + INTERFACE + ${binary_dir}/googlemock/libgmock.a + ${CMAKE_THREAD_LIBS_INIT} +)
\ No newline at end of file diff --git a/src/plugins/interfaces/afb/IAFBApi.h b/src/plugins/interfaces/afb/IAFBApi.h new file mode 100644 index 0000000..b607fd8 --- /dev/null +++ b/src/plugins/interfaces/afb/IAFBApi.h @@ -0,0 +1,98 @@ +/* + * Copyright 2018-2019 Amazon.com, Inc. or its affiliates. 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. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0/ + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT 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 VSHL_COMMON_INTERFACES_AFBAPI_H_ +#define VSHL_COMMON_INTERFACES_AFBAPI_H_ + +#include <memory> +#include <string> + +#include <json-c/json_object.h> + +using namespace std; + +namespace vshlcapabilities { +namespace common { +namespace interfaces { + +/** + * Interface to represent AFB Request. + */ +class IAFBRequest { +public: + /** + * Gets the native request object. + */ + virtual void* getNativeRequest() = 0; +}; + +/** + * Interface to encapsulate all AFB (AGL Application Framework Binding) + * functions. + */ +class IAFBApi { +public: + /** + * Interface to represent AFB Event + */ + class IAFBEvent { + public: + /** + * Gets human readable name of the event. + */ + virtual std::string getName() const = 0; + + /** + * Returns true if event is valid. False otherwise. + */ + virtual bool isValid() = 0; + + /** + * Publish event to all observers. + * + * @return The number of observers that received the event. + */ + virtual int publishEvent(struct json_object* payload) = 0; + + /** + * Subscribe to the event + * + * @c request Party interested in the event. + */ + virtual bool subscribe(IAFBRequest& request) = 0; + + /** + * Unsubscribe to the event + * + * @c request Party no longer interested in the event. + */ + virtual bool unsubscribe(IAFBRequest& request) = 0; + }; + + virtual std::shared_ptr<IAFBEvent> createEvent(const std::string& eventName) = 0; + + virtual int callSync( + const std::string& api, + const std::string& verb, + struct json_object* request, + struct json_object** result, + std::string& error, + std::string& info) = 0; +}; + +} // namespace interfaces +} // namespace common +} // namespace vshl + +#endif // VSHL_COMMON_INTERFACES_AFBAPI_H_ diff --git a/src/plugins/interfaces/capabilities/ICapability.h b/src/plugins/interfaces/capabilities/ICapability.h new file mode 100644 index 0000000..ebdd4e8 --- /dev/null +++ b/src/plugins/interfaces/capabilities/ICapability.h @@ -0,0 +1,58 @@ +/* + * Copyright 2018-2019 Amazon.com, Inc. or its affiliates. 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. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0/ + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT 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 VSHL_COMMON_INTERFACES_ICAPABILITY_H_ +#define VSHL_COMMON_INTERFACES_ICAPABILITY_H_ + +#include <list> +#include <string> + +using namespace std; + +namespace vshlcapabilities { +namespace common { +namespace interfaces { + +/* + * This interface defines the structure for a specific voiceagent capability. + */ +class ICapability { +public: + /* + * Returns the capability's name. + */ + virtual string getName() const = 0; + + /* + * Returns the list of upstream messages. + */ + virtual list<string> getUpstreamMessages() const = 0; + + /* + * Returns the list of downstream messages + */ + virtual list<string> getDownstreamMessages() const = 0; + + /** + * Virtual destructor to assure proper cleanup of derived types. + */ + virtual ~ICapability() = default; +}; + +} // namespace interfaces +} // namespace common +} // namespace vshl + +#endif // VSHL_COMMON_INTERFACES_ICAPABILITY_H_ diff --git a/src/plugins/interfaces/utilities/events/IEventFilter.h b/src/plugins/interfaces/utilities/events/IEventFilter.h new file mode 100644 index 0000000..a4e3c64 --- /dev/null +++ b/src/plugins/interfaces/utilities/events/IEventFilter.h @@ -0,0 +1,46 @@ +/* + * Copyright 2018-2019 Amazon.com, Inc. or its affiliates. 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. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0/ + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT 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 VSHL_COMMON_INTERFACES_IEVENTFILTER_H_ +#define VSHL_COMMON_INTERFACES_IEVENTFILTER_H_ + +#include <string> + +using namespace std; + +namespace vshlcapabilities { +namespace common { +namespace interfaces { +/* + * This is an abstract class that is responsible for filtering the events + * that are delivered to the high level voice service from apps or voiceagents. + */ +class IEventFilter { +public: + // Name of the event filter. + virtual string getName() = 0; + + // Every event filter needs to implement this method and + // return true if consuming the event or false otherwise. + virtual bool onIncomingEvent(const string eventName, const string voiceAgentId, const string payload) = 0; + + // Destructor + virtual ~IEventFilter() = default; +}; + +} // namespace interfaces +} // namespace common +} // namespace vshl + +#endif // VSHL_COMMON_INTERFACES_IEVENTFILTER_H_ diff --git a/src/plugins/interfaces/utilities/logging/ILogger.h b/src/plugins/interfaces/utilities/logging/ILogger.h new file mode 100644 index 0000000..4cf25aa --- /dev/null +++ b/src/plugins/interfaces/utilities/logging/ILogger.h @@ -0,0 +1,42 @@ +/* + * Copyright 2018-2019 Amazon.com, Inc. or its affiliates. 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. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0/ + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT 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 VSHL_COMMON_INTERFACES_LOGGER_H_ +#define VSHL_COMMON_INTERFACES_LOGGER_H_ + +#include <string> + +namespace vshlcapabilities { +namespace common { +namespace interfaces { + +class ILogger { +public: + enum Level { + DEBUG, + INFO, + WARNING, + ERROR, + NOTICE, + }; + + virtual void log(Level level, const std::string &tag, + const std::string &message) = 0; +}; + +} // namespace interfaces +} // namespace common +} // namespace vshl + +#endif // VSHL_COMMON_INTERFACES_LOGGER_H_ diff --git a/src/plugins/test/common/ConsoleLogger.cpp b/src/plugins/test/common/ConsoleLogger.cpp new file mode 100644 index 0000000..a2a2598 --- /dev/null +++ b/src/plugins/test/common/ConsoleLogger.cpp @@ -0,0 +1,31 @@ +/* + * Copyright 2018-2019 Amazon.com, Inc. or its affiliates. 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. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0/ + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT 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 <iostream> + +#include "test/common/ConsoleLogger.h" + +namespace vshlcapabilities { +namespace test { +namespace common { + +void ConsoleLogger::log(Level level, const std::string& tag, const std::string& message) { + string format_msg = "Tag: " + tag + ", message: " + message; + std::cout << format_msg << std::endl; +} + +} // namespace common +} // namespace test +} // namespace vshl diff --git a/src/plugins/test/common/ConsoleLogger.h b/src/plugins/test/common/ConsoleLogger.h new file mode 100644 index 0000000..2f81086 --- /dev/null +++ b/src/plugins/test/common/ConsoleLogger.h @@ -0,0 +1,37 @@ +/* + * Copyright 2018-2019 Amazon.com, Inc. or its affiliates. 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. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0/ + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT 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 VSHL_TEST_COMMON_CONSOLE_LOGGER_H_ +#define VSHL_TEST_COMMON_CONSOLE_LOGGER_H_ + +#include "interfaces/utilities/logging/ILogger.h" + +using namespace std; + +namespace vshlcapabilities { +namespace test { +namespace common { + +class ConsoleLogger : public vshlcapabilities::common::interfaces::ILogger { +public: + // ILogger interface + void log(Level level, const std::string &tag, + const std::string &message) override; +}; + +} // namespace common +} // namespace test +} // namespace vshl + +#endif // VSHL_TEST_COMMON_CONSOLE_LOGGER_H_ diff --git a/src/plugins/test/mocks/AFBApiMock.h b/src/plugins/test/mocks/AFBApiMock.h new file mode 100644 index 0000000..8cefa39 --- /dev/null +++ b/src/plugins/test/mocks/AFBApiMock.h @@ -0,0 +1,41 @@ +/* + * Copyright 2018-2019 Amazon.com, Inc. or its affiliates. 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. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0/ + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT 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 VSHL_TEST_MOCKS_AFBAPIMOCK_H_ +#define VSHL_TEST_MOCKS_AFBAPIMOCK_H_ + +#include <gmock/gmock.h> + +#include "interfaces/afb/IAFBApi.h" + +namespace vshlcapabilities { +namespace test { + +class AFBApiMock : public vshlcapabilities::common::interfaces::IAFBApi { +public: + MOCK_METHOD1(createEvent, std::shared_ptr<IAFBEvent>(const std::string& eventName)); + MOCK_METHOD6( + callSync, + int(const std::string& api, + const std::string& verb, + struct json_object* request, + struct json_object** result, + std::string& error, + std::string& info)); +}; + +} // namespace test +} // namespace vshl + +#endif // VSHL_TEST_MOCKS_AFBAPIMOCK_H_
\ No newline at end of file diff --git a/src/plugins/test/mocks/AFBEventMock.h b/src/plugins/test/mocks/AFBEventMock.h new file mode 100644 index 0000000..f4e2b67 --- /dev/null +++ b/src/plugins/test/mocks/AFBEventMock.h @@ -0,0 +1,47 @@ +/* + * Copyright 2018-2019 Amazon.com, Inc. or its affiliates. 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. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0/ + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT 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 VSHL_TEST_MOCKS_AFBEVENTMOCK_H_ +#define VSHL_TEST_MOCKS_AFBEVENTMOCK_H_ + +#include <gmock/gmock.h> + +#include "interfaces/afb/IAFBApi.h" + +namespace vshlcapabilities { +namespace test { + +class AFBEventMock : public vshlcapabilities::common::interfaces::IAFBApi::IAFBEvent { +public: + void setName(const std::string& name) { + mName = name; + } + + std::string getName() const override { + return mName; + } + + MOCK_METHOD0(isValid, bool()); + MOCK_METHOD1(publishEvent, int(struct json_object* payload)); + MOCK_METHOD1(subscribe, bool(vshlcapabilities::common::interfaces::IAFBRequest& request)); + MOCK_METHOD1(unsubscribe, bool(vshlcapabilities::common::interfaces::IAFBRequest& request)); + +private: + std::string mName; +}; + +} // namespace test +} // namespace vshl + +#endif // VSHL_TEST_MOCKS_AFBEVENTMOCK_H_
\ No newline at end of file diff --git a/src/plugins/test/mocks/AFBRequestMock.h b/src/plugins/test/mocks/AFBRequestMock.h new file mode 100644 index 0000000..0aaeede --- /dev/null +++ b/src/plugins/test/mocks/AFBRequestMock.h @@ -0,0 +1,33 @@ +/* + * Copyright 2018-2019 Amazon.com, Inc. or its affiliates. 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. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0/ + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT 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 VSHL_TEST_MOCKS_AFBREQUESTMOCK_H_ +#define VSHL_TEST_MOCKS_AFBREQUESTMOCK_H_ + +#include <gmock/gmock.h> + +#include "interfaces/afb/IAFBApi.h" + +namespace vshlcapabilities { +namespace test { + +class AFBRequestMock : public vshlcapabilities::common::interfaces::IAFBRequest { +public: + MOCK_METHOD0(getNativeRequest, void*()); +}; + +} // namespace test +} // namespace vshl + +#endif // VSHL_TEST_MOCKS_AFBREQUESTMOCK_H_
\ No newline at end of file diff --git a/src/plugins/test/mocks/CapabilityMock.h b/src/plugins/test/mocks/CapabilityMock.h new file mode 100644 index 0000000..aaa0bd5 --- /dev/null +++ b/src/plugins/test/mocks/CapabilityMock.h @@ -0,0 +1,35 @@ +/* + * Copyright 2018-2019 Amazon.com, Inc. or its affiliates. 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. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0/ + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT 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 VSHL_TEST_MOCKS_CAPABILITYMOCK_H_ +#define VSHL_TEST_MOCKS_CAPABILITYMOCK_H_ + +#include <gmock/gmock.h> + +#include "interfaces/capabilities/ICapability.h" + +namespace vshlcapabilities { +namespace test { + +class CapabilityMock : public vshlcapabilities::common::interfaces::ICapability { +public: + MOCK_CONST_METHOD0(getName, std::string()); + MOCK_CONST_METHOD0(getUpstreamMessages, std::list<std::string>()); + MOCK_CONST_METHOD0(getDownstreamMessages, std::list<std::string>()); +}; + +} // namespace test +} // namespace vshl + +#endif // VSHL_TEST_MOCKS_CAPABILITYMOCK_H_
\ No newline at end of file diff --git a/src/plugins/utilities/events/EventRouter.cpp b/src/plugins/utilities/events/EventRouter.cpp new file mode 100644 index 0000000..e715ca8 --- /dev/null +++ b/src/plugins/utilities/events/EventRouter.cpp @@ -0,0 +1,68 @@ +/* + * Copyright 2018-2019 Amazon.com, Inc. or its affiliates. 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. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0/ + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT 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 "utilities/events/EventRouter.h" + +static string TAG = "vshlcapabilities::utilities::events::EventRouter"; + +using Level = vshlcapabilities::common::interfaces::ILogger::Level; + +namespace vshlcapabilities { +namespace utilities { +namespace events { + +unique_ptr<EventRouter> EventRouter::create(shared_ptr<vshlcapabilities::common::interfaces::ILogger> logger) { + return std::unique_ptr<EventRouter>(new EventRouter(logger)); +} + +EventRouter::EventRouter(shared_ptr<vshlcapabilities::common::interfaces::ILogger> logger) : mLogger(logger) { +} + +EventRouter::~EventRouter() { + mEventFilters.clear(); +} + +bool EventRouter::handleIncomingEvent(const string eventName, const string voiceAgentId, const string payload) { + for (auto eventFilter : mEventFilters) { + if (eventFilter->onIncomingEvent(eventName, voiceAgentId, payload)) { + return true; + } + } + + return false; +} + +bool EventRouter::addEventFilter(shared_ptr<vshlcapabilities::common::interfaces::IEventFilter> filter) { + if (!filter) { + mLogger->log(Level::ERROR, TAG, "Failed to add event filter. Invalid arguments."); + return false; + } + + mEventFilters.insert(filter); + return true; +} + +bool EventRouter::removeEventFilter(shared_ptr<vshlcapabilities::common::interfaces::IEventFilter> filter) { + if (!filter) { + mLogger->log(Level::ERROR, TAG, "Failed to add remove filter. Invalid arguments."); + return false; + } + + mEventFilters.erase(filter); + return true; +} + +} // namespace events +} // namespace utilities +} // namespace vshl diff --git a/src/plugins/utilities/events/EventRouter.h b/src/plugins/utilities/events/EventRouter.h new file mode 100644 index 0000000..1286128 --- /dev/null +++ b/src/plugins/utilities/events/EventRouter.h @@ -0,0 +1,66 @@ +/* + * Copyright 2018-2019 Amazon.com, Inc. or its affiliates. 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. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0/ + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT 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 VSHL_UTILITIES_EVENTS_EVENTMANAGER_H_ +#define VSHL_UTILITIES_EVENTS_EVENTMANAGER_H_ + +#include <memory> +#include <string> +#include <unordered_set> + +#include "interfaces/utilities/events/IEventFilter.h" +#include "interfaces/utilities/logging/ILogger.h" + +using namespace std; + +namespace vshlcapabilities { +namespace utilities { +namespace events { +/* + * This class is responsible for routing incoming events to + * the appropriate event listener for consumption. + * Note: The listeners should implement the IEventFilter class. + */ +class EventRouter { +public: + static unique_ptr<EventRouter> create(shared_ptr<vshlcapabilities::common::interfaces::ILogger> logger); + + // Destructor + ~EventRouter(); + + // Add event filter as listerner. + bool addEventFilter(shared_ptr<vshlcapabilities::common::interfaces::IEventFilter> filter); + + // Remove event filter as listerner. + bool removeEventFilter(shared_ptr<vshlcapabilities::common::interfaces::IEventFilter> filter); + + // This method is called by the controller for routing + // the event to appropriate listener. + bool handleIncomingEvent(const string eventName, const string voiceAgentId, const string payload); + +private: + EventRouter(shared_ptr<vshlcapabilities::common::interfaces::ILogger> logger); + + // set of event filters. + unordered_set<shared_ptr<vshlcapabilities::common::interfaces::IEventFilter>> mEventFilters; + + // Logger + shared_ptr<vshlcapabilities::common::interfaces::ILogger> mLogger; +}; + +} // namespace events +} // namespace utilities +} // namespace vshl + +#endif // VSHL_UTILITIES_EVENTS_EVENTROUTER_H_ diff --git a/src/plugins/utilities/logging/Logger.cpp b/src/plugins/utilities/logging/Logger.cpp new file mode 100644 index 0000000..c121556 --- /dev/null +++ b/src/plugins/utilities/logging/Logger.cpp @@ -0,0 +1,56 @@ +/* + * Copyright 2018-2019 Amazon.com, Inc. or its affiliates. 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. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0/ + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT 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 "utilities/logging/Logger.h" + +namespace vshlcapabilities { +namespace utilities { +namespace logging { + +// Constructor +Logger::Logger(AFB_ApiT api) { + mApi = api; +} + +unique_ptr<Logger> Logger::create(AFB_ApiT api) { + auto logger = std::unique_ptr<Logger>(new Logger(api)); + return logger; +} + +void Logger::log(Level level, const std::string& tag, const std::string& message) { + string format_msg = "Tag: " + tag + ", message: " + message; + switch (level) { + case Level::NOTICE: + AFB_ApiNotice(mApi, format_msg.c_str()); + break; + case Level::WARNING: + AFB_ApiWarning(mApi, format_msg.c_str()); + break; + case Level::DEBUG: + AFB_ApiDebug(mApi, format_msg.c_str()); + break; + case Level::ERROR: + AFB_ApiError(mApi, format_msg.c_str()); + break; + case Level::INFO: + AFB_ApiInfo(mApi, format_msg.c_str()); + break; + default: + break; + } +} + +} // namespace logging +} // namespace utilities +} // namespace vshl diff --git a/src/plugins/utilities/logging/Logger.h b/src/plugins/utilities/logging/Logger.h new file mode 100644 index 0000000..56f810d --- /dev/null +++ b/src/plugins/utilities/logging/Logger.h @@ -0,0 +1,53 @@ +/* + * Copyright 2018-2019 Amazon.com, Inc. or its affiliates. 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. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0/ + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT 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 VSHL_UTILITIES_LOGGING_LOGGER_H_ +#define VSHL_UTILITIES_LOGGING_LOGGER_H_ + +#include <memory> + +extern "C" { +#define AFB_BINDING_VERSION 3 +#include "afb-definitions.h" +#include "ctl-plugin.h" +}; + +#include "interfaces/utilities/logging/ILogger.h" + +using namespace std; + +namespace vshlcapabilities { +namespace utilities { +namespace logging { + +class Logger : public vshlcapabilities::common::interfaces::ILogger { +public: + static std::unique_ptr<Logger> create(AFB_ApiT api); + + // ILogger interface + void log(Level level, const std::string &tag, + const std::string &message) override; + +private: + Logger(AFB_ApiT api); + + // Binding API reference + AFB_ApiT mApi; +}; + +} // namespace logging +} // namespace utilities +} // namespace vshl + +#endif // VSHL_UTILITIES_LOGGING_LOGGER_H_ diff --git a/src/vshl-capabilities-apidef.h b/src/vshl-capabilities-apidef.h new file mode 100644 index 0000000..6ce4cb3 --- /dev/null +++ b/src/vshl-capabilities-apidef.h @@ -0,0 +1,53 @@ + +static const char _afb_description_vshl_capabilities[] = + "{\"openapi\":\"3.0.0\",\"$schema\":\"http://iot.bzh/download/openapi/sch" + "ema-3.0/default-schema.json\",\"info\":{\"description\":\"\",\"title\":\"" + "High Level Voice Service Capabilities APIs\",\"version\":\"0.1\",\"x-bin" + "ding-c-generator\":{\"api\":\"vshl-capabilities\",\"version\":3,\"prefix" + "\":\"afv_\",\"postfix\":\"\",\"start\":null,\"onevent\":null,\"init\":\"" + "init\",\"scope\":\"\",\"private\":false,\"noconcurrency\":true}},\"serve" + "rs\":[{\"url\":\"ws://{host}:{port}/api/monitor\",\"description\":\"TS c" + "aching binding\",\"variables\":{\"host\":{\"default\":\"localhost\"},\"p" + "ort\":{\"default\":\"1234\"}},\"x-afb-events\":[{\"$ref\":\"#/components" + "/schemas/afb-event\"}]}],\"components\":{\"schemas\":{\"afb-reply\":{\"$" + "ref\":\"#/components/schemas/afb-reply-v3\"},\"afb-event\":{\"$ref\":\"#" + "/components/schemas/afb-event-v3\"},\"afb-reply-v3\":{\"title\":\"Generi" + "c response.\",\"type\":\"object\",\"required\":[\"jtype\",\"request\"],\"" + "properties\":{\"jtype\":{\"type\":\"string\",\"const\":\"afb-reply\"},\"" + "request\":{\"type\":\"object\",\"required\":[\"status\"],\"properties\":" + "{\"status\":{\"type\":\"string\"},\"info\":{\"type\":\"string\"},\"token" + "\":{\"type\":\"string\"},\"uuid\":{\"type\":\"string\"},\"reqid\":{\"typ" + "e\":\"string\"}}},\"response\":{\"type\":\"object\"}}},\"afb-event-v3\":" + "{\"type\":\"object\",\"required\":[\"jtype\",\"event\"],\"properties\":{" + "\"jtype\":{\"type\":\"string\",\"const\":\"afb-event\"},\"event\":{\"typ" + "e\":\"string\"},\"data\":{\"type\":\"object\"}}}},\"responses\":{\"200\"" + ":{\"description\":\"A complex object array response\",\"content\":{\"app" + "lication/json\":{\"schema\":{\"$ref\":\"#/components/schemas/afb-reply\"" + "}}}}}}}" +; + + +static const struct afb_verb_v3 _afb_verbs_vshl_capabilities[] = { + { + .verb = NULL, + .callback = NULL, + .auth = NULL, + .info = NULL, + .session = 0, + .vcbdata = NULL, + .glob = 0 + } +}; + +const struct afb_binding_v3 afbBindingV3 = { + .api = "vshl-capabilities", + .specification = _afb_description_vshl_capabilities, + .info = "", + .verbs = _afb_verbs_vshl_capabilities, + .preinit = NULL, + .init = init, + .onevent = NULL, + .userdata = NULL, + .noconcurrency = 1 +}; + diff --git a/src/vshl-capabilities-apidef.json b/src/vshl-capabilities-apidef.json new file mode 100644 index 0000000..158bf5a --- /dev/null +++ b/src/vshl-capabilities-apidef.json @@ -0,0 +1,109 @@ +{ + "openapi": "3.0.0", + "$schema": "http://iot.bzh/download/openapi/schema-3.0/default-schema.json", + "info": { + "description": "", + "title": "High Level Voice Service Capabilities APIs", + "version": "0.1", + "x-binding-c-generator": { + "api": "vshl-capabilities", + "version": 3, + "prefix": "afv_", + "postfix": "", + "start": null, + "onevent": null, + "init": "init", + "scope": "", + "private": false, + "noconcurrency": true + } + }, + "servers": [{ + "url": "ws://{host}:{port}/api/monitor", + "description": "TS caching binding", + "variables": { + "host": { + "default": "localhost" + }, + "port": { + "default": "1234" + } + }, + "x-afb-events": [{ + "$ref": "#/components/schemas/afb-event" + }] + }], + "components": { + "schemas": { + "afb-reply": { + "$ref": "#/components/schemas/afb-reply-v3" + }, + "afb-event": { + "$ref": "#/components/schemas/afb-event-v3" + }, + "afb-reply-v3": { + "title": "Generic response.", + "type": "object", + "required": ["jtype", "request"], + "properties": { + "jtype": { + "type": "string", + "const": "afb-reply" + }, + "request": { + "type": "object", + "required": ["status"], + "properties": { + "status": { + "type": "string" + }, + "info": { + "type": "string" + }, + "token": { + "type": "string" + }, + "uuid": { + "type": "string" + }, + "reqid": { + "type": "string" + } + } + }, + "response": { + "type": "object" + } + } + }, + "afb-event-v3": { + "type": "object", + "required": ["jtype", "event"], + "properties": { + "jtype": { + "type": "string", + "const": "afb-event" + }, + "event": { + "type": "string" + }, + "data": { + "type": "object" + } + } + } + }, + "responses": { + "200": { + "description": "A complex object array response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/afb-reply" + } + } + } + } + } + } +}
\ No newline at end of file diff --git a/src/vshl-capabilities-binding.c b/src/vshl-capabilities-binding.c new file mode 100644 index 0000000..b38210a --- /dev/null +++ b/src/vshl-capabilities-binding.c @@ -0,0 +1,126 @@ +/* + * Copyright 2018-2019 Amazon.com, Inc. or its affiliates. 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. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0/ + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ +#define _GNU_SOURCE +#include "vshl-capabilities-binding.h" + +afb_dynapi* AFB_default; + +// Config Section definition (note: controls section index should match handle +// retrieval in HalConfigExec) +static CtlSectionT ctrlSections[] = {{.key = "plugins", .loadCB = PluginConfig}, + {.key = "controls", .loadCB = ControlConfig}, + {.key = NULL}}; + +static AFB_ApiVerbs ctrlApiVerbs[] = { + {.verb = NULL} /* marker for end of the array */ +}; + +static int ctrlLoadStaticVerbs(afb_dynapi* apiHandle, AFB_ApiVerbs* verbs) { + int errcount = 0; + + for (int idx = 0; verbs[idx].verb; idx++) { + errcount += afb_dynapi_add_verb( + apiHandle, + ctrlApiVerbs[idx].verb, + NULL, + ctrlApiVerbs[idx].callback, + (void*)&ctrlApiVerbs[idx], + ctrlApiVerbs[idx].auth, + 0); + } + + return errcount; +}; + +// next generation dynamic API-V3 mode +#include <signal.h> + +static int CtrlInitOneApi(AFB_ApiT apiHandle) +{ + CtlConfigT *ctrlConfig; + + if(!apiHandle) + return -1; + + // Retrieve section config from api handle + ctrlConfig = (CtlConfigT *) AFB_ApiGetUserData(apiHandle); + if(!ctrlConfig) + return -2; + + return CtlConfigExec(apiHandle, ctrlConfig); +} + +static int ctrlLoadOneApi(void* cbdata, AFB_ApiT apiHandle) { + CtlConfigT* ctrlConfig = (CtlConfigT*)cbdata; + + // save closure as api's data context + afb_dynapi_set_userdata(apiHandle, ctrlConfig); + + // add static controls verbs + int err = ctrlLoadStaticVerbs(apiHandle, ctrlApiVerbs); + if (err) { + AFB_ApiError(apiHandle, "ctrlLoadStaticVerbs fail to register static V2 verbs"); + return ERROR; + } + + // load section for corresponding API + err = CtlLoadSections(apiHandle, ctrlConfig, ctrlSections); + if (err) { + AFB_ApiError(apiHandle, "CtlLoadSections fail to load the sections"); + return ERROR; + } + + // declare an event event manager for this API; + afb_dynapi_on_event(apiHandle, CtrlDispatchApiEvent); + + // init API function (does not receive user closure ??? + afb_dynapi_on_init(apiHandle, CtrlInitOneApi); + + afb_dynapi_seal(apiHandle); + return err; +} + +int afbBindingEntry(afb_dynapi* apiHandle) { + AFB_default = apiHandle; + AFB_ApiNotice(apiHandle, "Controller in afbBindingEntry"); + + const char* dirList = getenv("CONTROL_CONFIG_PATH"); + if (!dirList) dirList = CONTROL_CONFIG_PATH; + + const char* configPath = CtlConfigSearch(apiHandle, dirList, ""); + if (!configPath) { + AFB_ApiError(apiHandle, "CtlPreInit: No %s* config found in %s ", GetBinderName(), dirList); + return ERROR; + } + + // load config file and create API + CtlConfigT* ctrlConfig = CtlLoadMetaData(apiHandle, configPath); + if (!ctrlConfig) { + AFB_ApiError(apiHandle, "CtrlBindingDyn No valid control config file in:\n-- %s", configPath); + return ERROR; + } + + if (!ctrlConfig->api) { + AFB_ApiError(apiHandle, "CtrlBindingDyn API Missing from metadata in:\n-- %s", configPath); + return ERROR; + } + + AFB_ApiNotice(apiHandle, "Controller API='%s' info='%s'", ctrlConfig->api, ctrlConfig->info); + + // create one API per config file (Pre-V3 return code ToBeChanged) + int status = afb_dynapi_new_api(apiHandle, ctrlConfig->api, ctrlConfig->info, 1, ctrlLoadOneApi, ctrlConfig); + + return status; +} diff --git a/src/vshl-capabilities-binding.h b/src/vshl-capabilities-binding.h new file mode 100644 index 0000000..75e7c91 --- /dev/null +++ b/src/vshl-capabilities-binding.h @@ -0,0 +1,25 @@ +/* + * Copyright 2018-2019 Amazon.com, Inc. or its affiliates. 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. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0/ + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT 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 _CTL_BINDING_INCLUDE_ +#define _CTL_BINDING_INCLUDE_ + +#define AFB_BINDING_VERSION 3 +#include <ctl-config.h> + +#ifndef ERROR +#define ERROR -1 +#endif + +#endif /* _CTL_BINDING_INCLUDE_ */
\ No newline at end of file diff --git a/tools/pre-commit b/tools/pre-commit new file mode 100755 index 0000000..87a9fc3 --- /dev/null +++ b/tools/pre-commit @@ -0,0 +1,140 @@ +#!/bin/bash + +# git pre-commit hook that runs an clang-format stylecheck. +# Features: +# - abort commit when commit does not comply with the style guidelines +# - create a patch of the proposed style changes + +# modifications for clang-format by rene.milk@wwu.de +# This file is part of a set of unofficial pre-commit hooks available +# at github. +# Link: https://gist.github.com/wangkuiyi/7379a242f0d4089eaa75 + + +################################################################## +# SETTINGS +# set path to clang-format binary +CLANG_FORMAT="`which clang-format`" + +# remove any older patches from previous commits. Set to true or false. +# DELETE_OLD_PATCHES=false +DELETE_OLD_PATCHES=false + +# only parse files with the extensions in FILE_EXTS. Set to true or false. +# if false every changed file in the commit will be parsed with clang-format. +# if true only files matching one of the extensions are parsed with clang-format. +# PARSE_EXTS=true +PARSE_EXTS=true + +# file types to parse. Only effective when PARSE_EXTS is true. +# FILE_EXTS=".c .h .cpp .hpp" +FILE_EXTS=".c .h .cpp .hpp .cc .hh .cxx .m" + +################################################################## +# There should be no need to change anything below this line. + +# Reference: http://stackoverflow.com/questions/1055671/how-can-i-get-the-behavior-of-gnus-readlink-f-on-a-mac +canonicalize_filename () { + local target_file=$1 + local physical_directory="" + local result="" + + # Need to restore the working directory after work. + pushd `pwd` > /dev/null + + cd "$(dirname "$target_file")" + target_file=`basename $target_file` + + # Iterate down a (possible) chain of symlinks + while [ -L "$target_file" ] + do + target_file=$(readlink "$target_file") + cd "$(dirname "$target_file")" + target_file=$(basename "$target_file") + done + + # Compute the canonicalized name by finding the physical path + # for the directory we're in and appending the target file. + physical_directory=`pwd -P` + result="$physical_directory"/"$target_file" + + # restore the working directory after work. + popd > /dev/null + + echo "$result" +} + +# exit on error +set -e + +# check whether the given file matches any of the set extensions +matches_extension() { + local filename=$(basename "$1") + local extension=".${filename##*.}" + local ext + + for ext in $FILE_EXTS; do [[ "$ext" == "$extension" ]] && return 0; done + + return 1 +} + +# necessary check for initial commit +if git rev-parse --verify HEAD >/dev/null 2>&1 ; then + against=HEAD +else + # Initial commit: diff against an empty tree object + against=4b825dc642cb6eb9a060e54bf8d69288fbee4904 +fi + +if [ ! -x "$CLANG_FORMAT" ] ; then + printf "Error: clang-format executable not found.\n" + printf "Set the correct path in $(canonicalize_filename "$0").\n" + exit 1 +fi + +# create a random filename to store our generated patch +prefix="pre-commit-clang-format" +suffix="$(date +%s)" +patch="/tmp/$prefix-$suffix.patch" + +# clean up any older clang-format patches +$DELETE_OLD_PATCHES && rm -f /tmp/$prefix*.patch + +# create one patch containing all changes to the files +git diff-index --cached --diff-filter=ACMR --name-only $against -- | while read file; +do + # ignore file if we do check for file extensions and the file + # does not match any of the extensions specified in $FILE_EXTS + if $PARSE_EXTS && ! matches_extension "$file"; then + continue; + fi + + # clang-format our sourcefile, create a patch with diff and append it to our $patch + # The sed call is necessary to transform the patch from + # --- $file timestamp + # +++ - timestamp + # to both lines working on the same file and having a a/ and b/ prefix. + # Else it can not be applied with 'git apply'. + "$CLANG_FORMAT" -style=file "$file" | \ + diff -u "$file" - | \ + sed -e "1s|--- |--- a/|" -e "2s|+++ -|+++ b/$file|" >> "$patch" +done + +# if no patch has been generated all is ok, clean up the file stub and exit +if [ ! -s "$patch" ] ; then + printf "Files in this commit comply with the clang-format rules.\n" + rm -f "$patch" + exit 0 +fi + +# a patch has been created, notify the user and exit +printf "\nThe following differences were found between the code to commit " +printf "and the clang-format rules:\n\n" +cat "$patch" + +printf "\nYou can apply these changes with:\n git apply $patch\n" +printf "(may need to be called from the root directory of your repository)\n" +printf "Aborting commit. Apply changes and commit again or skip checking with" +printf " --no-verify (not recommended).\n" + +exit 1 |