mirror of
https://github.com/wheremyfoodat/Panda3DS.git
synced 2025-04-15 18:39:47 +12:00
Add external libraries
This commit is contained in:
parent
70f443b06e
commit
e8dc11cc31
17 changed files with 2010 additions and 0 deletions
63
third_party/open-bp-cpp/.gitattributes
vendored
Normal file
63
third_party/open-bp-cpp/.gitattributes
vendored
Normal file
|
@ -0,0 +1,63 @@
|
|||
###############################################################################
|
||||
# Set default behavior to automatically normalize line endings.
|
||||
###############################################################################
|
||||
* text=auto
|
||||
|
||||
###############################################################################
|
||||
# Set default behavior for command prompt diff.
|
||||
#
|
||||
# This is need for earlier builds of msysgit that does not have it on by
|
||||
# default for csharp files.
|
||||
# Note: This is only used by command line
|
||||
###############################################################################
|
||||
#*.cs diff=csharp
|
||||
|
||||
###############################################################################
|
||||
# Set the merge driver for project and solution files
|
||||
#
|
||||
# Merging from the command prompt will add diff markers to the files if there
|
||||
# are conflicts (Merging from VS is not affected by the settings below, in VS
|
||||
# the diff markers are never inserted). Diff markers may cause the following
|
||||
# file extensions to fail to load in VS. An alternative would be to treat
|
||||
# these files as binary and thus will always conflict and require user
|
||||
# intervention with every merge. To do so, just uncomment the entries below
|
||||
###############################################################################
|
||||
#*.sln merge=binary
|
||||
#*.csproj merge=binary
|
||||
#*.vbproj merge=binary
|
||||
#*.vcxproj merge=binary
|
||||
#*.vcproj merge=binary
|
||||
#*.dbproj merge=binary
|
||||
#*.fsproj merge=binary
|
||||
#*.lsproj merge=binary
|
||||
#*.wixproj merge=binary
|
||||
#*.modelproj merge=binary
|
||||
#*.sqlproj merge=binary
|
||||
#*.wwaproj merge=binary
|
||||
|
||||
###############################################################################
|
||||
# behavior for image files
|
||||
#
|
||||
# image files are treated as binary by default.
|
||||
###############################################################################
|
||||
#*.jpg binary
|
||||
#*.png binary
|
||||
#*.gif binary
|
||||
|
||||
###############################################################################
|
||||
# diff behavior for common document formats
|
||||
#
|
||||
# Convert binary document formats to text before diffing them. This feature
|
||||
# is only available from the command line. Turn it on by uncommenting the
|
||||
# entries below.
|
||||
###############################################################################
|
||||
#*.doc diff=astextplain
|
||||
#*.DOC diff=astextplain
|
||||
#*.docx diff=astextplain
|
||||
#*.DOCX diff=astextplain
|
||||
#*.dot diff=astextplain
|
||||
#*.DOT diff=astextplain
|
||||
#*.pdf diff=astextplain
|
||||
#*.PDF diff=astextplain
|
||||
#*.rtf diff=astextplain
|
||||
#*.RTF diff=astextplain
|
40
third_party/open-bp-cpp/.gitignore
vendored
Normal file
40
third_party/open-bp-cpp/.gitignore
vendored
Normal file
|
@ -0,0 +1,40 @@
|
|||
# Prerequisites
|
||||
*.d
|
||||
|
||||
# Compiled Object files
|
||||
*.slo
|
||||
*.lo
|
||||
*.o
|
||||
*.obj
|
||||
|
||||
# Precompiled Headers
|
||||
*.gch
|
||||
*.pch
|
||||
|
||||
# Compiled Dynamic libraries
|
||||
*.so
|
||||
*.dylib
|
||||
*.dll
|
||||
|
||||
# Fortran module files
|
||||
*.mod
|
||||
*.smod
|
||||
|
||||
# Compiled Static libraries
|
||||
*.lai
|
||||
*.la
|
||||
*.a
|
||||
*.lib
|
||||
|
||||
# Executables
|
||||
*.exe
|
||||
*.out
|
||||
*.app
|
||||
/.vs
|
||||
/out/build/x64-debug
|
||||
/CMakeFiles
|
||||
/_3rdParty
|
||||
/CMakeCache.txt
|
||||
/cmake_install.cmake
|
||||
/Makefile
|
||||
/buttplugCpp
|
43
third_party/open-bp-cpp/CMakeLists.txt
vendored
Normal file
43
third_party/open-bp-cpp/CMakeLists.txt
vendored
Normal file
|
@ -0,0 +1,43 @@
|
|||
# CMakeList.txt : CMake project for buttplugCpp, include source and define
|
||||
# project specific logic here.
|
||||
#
|
||||
cmake_minimum_required (VERSION 3.8)
|
||||
include("cmake/HunterGate.cmake")
|
||||
|
||||
HunterGate(
|
||||
URL "https://github.com/cpp-pm/hunter/archive/v0.24.18.tar.gz"
|
||||
SHA1 "1292e4d661e1770d6d6ca08c12c07cf34a0bf718"
|
||||
)
|
||||
|
||||
# Enable Hot Reload for MSVC compilers if supported.
|
||||
if (POLICY CMP0141)
|
||||
cmake_policy(SET CMP0141 NEW)
|
||||
set(CMAKE_MSVC_DEBUG_INFORMATION_FORMAT "$<IF:$<AND:$<C_COMPILER_ID:MSVC>,$<CXX_COMPILER_ID:MSVC>>,$<$<CONFIG:Debug,RelWithDebInfo>:EditAndContinue>,$<$<CONFIG:Debug,RelWithDebInfo>:ProgramDatabase>>")
|
||||
endif()
|
||||
|
||||
project ("buttplugCpp")
|
||||
|
||||
# Add source to this project's executable.
|
||||
add_executable (buttplugCpp "example/buttplugCpp.cpp" "example/buttplugCpp.h")
|
||||
|
||||
file(GLOB SRC_FILES
|
||||
"src/*.cpp"
|
||||
"include/*.h"
|
||||
)
|
||||
|
||||
target_sources(buttplugCpp PUBLIC
|
||||
${SRC_FILES}
|
||||
)
|
||||
|
||||
hunter_add_package(nlohmann_json)
|
||||
hunter_add_package(ZLIB)
|
||||
find_package(ixwebsocket CONFIG REQUIRED)
|
||||
find_package(nlohmann_json 3.2.0 REQUIRED)
|
||||
find_package(ZLIB REQUIRED)
|
||||
find_package(Threads REQUIRED)
|
||||
target_link_libraries(buttplugCpp PUBLIC ixwebsocket::ixwebsocket nlohmann_json::nlohmann_json Threads::Threads)
|
||||
|
||||
if (CMAKE_VERSION VERSION_GREATER 3.12)
|
||||
set_property(TARGET buttplugCpp PROPERTY CXX_STANDARD 11)
|
||||
endif()
|
||||
# TODO: Add tests and install targets if needed.
|
61
third_party/open-bp-cpp/CMakePresets.json
vendored
Normal file
61
third_party/open-bp-cpp/CMakePresets.json
vendored
Normal file
|
@ -0,0 +1,61 @@
|
|||
{
|
||||
"version": 3,
|
||||
"configurePresets": [
|
||||
{
|
||||
"name": "windows-base",
|
||||
"hidden": true,
|
||||
"generator": "Ninja",
|
||||
"binaryDir": "${sourceDir}/out/build/${presetName}",
|
||||
"installDir": "${sourceDir}/out/install/${presetName}",
|
||||
"cacheVariables": {
|
||||
"CMAKE_C_COMPILER": "cl.exe",
|
||||
"CMAKE_CXX_COMPILER": "cl.exe"
|
||||
},
|
||||
"condition": {
|
||||
"type": "equals",
|
||||
"lhs": "${hostSystemName}",
|
||||
"rhs": "Windows"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "x64-debug",
|
||||
"displayName": "x64 Debug",
|
||||
"inherits": "windows-base",
|
||||
"architecture": {
|
||||
"value": "x64",
|
||||
"strategy": "external"
|
||||
},
|
||||
"cacheVariables": {
|
||||
"CMAKE_BUILD_TYPE": "Debug"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "x64-release",
|
||||
"displayName": "x64 Release",
|
||||
"inherits": "x64-debug",
|
||||
"cacheVariables": {
|
||||
"CMAKE_BUILD_TYPE": "Release"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "x86-debug",
|
||||
"displayName": "x86 Debug",
|
||||
"inherits": "windows-base",
|
||||
"architecture": {
|
||||
"value": "x86",
|
||||
"strategy": "external"
|
||||
},
|
||||
"cacheVariables": {
|
||||
"CMAKE_BUILD_TYPE": "Debug"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "x86-release",
|
||||
"displayName": "x86 Release",
|
||||
"inherits": "x86-debug",
|
||||
"cacheVariables": {
|
||||
"CMAKE_BUILD_TYPE": "Release"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
17
third_party/open-bp-cpp/README.md
vendored
Normal file
17
third_party/open-bp-cpp/README.md
vendored
Normal file
|
@ -0,0 +1,17 @@
|
|||
# buttplugCpp
|
||||
## How to use
|
||||
I made this project in Visual Studio 2022, but it uses CMake. I currently am struggling to get a proper library CMake going, so simply include the src and include directories in your project and add them with:
|
||||
```
|
||||
file(GLOB SRC_FILES
|
||||
"src/*.cpp"
|
||||
"include/*.h"
|
||||
)
|
||||
|
||||
target_sources(buttplugCpp PUBLIC
|
||||
${SRC_FILES}
|
||||
)
|
||||
```
|
||||
in your CMake file. Also make sure to build IXWebSockets (without zlib, unless you want to deal with getting it working with CMake). Nlohmann Json dependency is handled by Hunter, so make sure you have Hunter in your project too. I provide an example CMake file which makes the example program in example directory.
|
||||
|
||||
## More info
|
||||
Currently this library was tested (not very extensively) with Linux and Windows. The C++ version is C++11. This library and its documentation is still WIP.
|
537
third_party/open-bp-cpp/cmake/HunterGate.cmake
vendored
Normal file
537
third_party/open-bp-cpp/cmake/HunterGate.cmake
vendored
Normal file
|
@ -0,0 +1,537 @@
|
|||
# Copyright (c) 2013-2019, Ruslan Baratov
|
||||
# All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted provided that the following conditions are met:
|
||||
#
|
||||
# * Redistributions of source code must retain the above copyright notice, this
|
||||
# list of conditions and the following disclaimer.
|
||||
#
|
||||
# * Redistributions in binary form must reproduce the above copyright notice,
|
||||
# this list of conditions and the following disclaimer in the documentation
|
||||
# and/or other materials provided with the distribution.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
# This is a gate file to Hunter package manager.
|
||||
# Include this file using `include` command and add package you need, example:
|
||||
#
|
||||
# cmake_minimum_required(VERSION 3.2)
|
||||
#
|
||||
# include("cmake/HunterGate.cmake")
|
||||
# HunterGate(
|
||||
# URL "https://github.com/path/to/hunter/archive.tar.gz"
|
||||
# SHA1 "798501e983f14b28b10cda16afa4de69eee1da1d"
|
||||
# )
|
||||
#
|
||||
# project(MyProject)
|
||||
#
|
||||
# hunter_add_package(Foo)
|
||||
# hunter_add_package(Boo COMPONENTS Bar Baz)
|
||||
#
|
||||
# Projects:
|
||||
# * https://github.com/hunter-packages/gate/
|
||||
# * https://github.com/ruslo/hunter
|
||||
|
||||
option(HUNTER_ENABLED "Enable Hunter package manager support" ON)
|
||||
|
||||
if(HUNTER_ENABLED)
|
||||
if(CMAKE_VERSION VERSION_LESS "3.2")
|
||||
message(
|
||||
FATAL_ERROR
|
||||
"At least CMake version 3.2 required for Hunter dependency management."
|
||||
" Update CMake or set HUNTER_ENABLED to OFF."
|
||||
)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
include(CMakeParseArguments) # cmake_parse_arguments
|
||||
|
||||
option(HUNTER_STATUS_PRINT "Print working status" ON)
|
||||
option(HUNTER_STATUS_DEBUG "Print a lot info" OFF)
|
||||
option(HUNTER_TLS_VERIFY "Enable/disable TLS certificate checking on downloads" ON)
|
||||
set(HUNTER_ROOT "" CACHE FILEPATH "Override the HUNTER_ROOT.")
|
||||
|
||||
set(HUNTER_ERROR_PAGE "https://hunter.readthedocs.io/en/latest/reference/errors")
|
||||
|
||||
function(hunter_gate_status_print)
|
||||
if(HUNTER_STATUS_PRINT OR HUNTER_STATUS_DEBUG)
|
||||
foreach(print_message ${ARGV})
|
||||
message(STATUS "[hunter] ${print_message}")
|
||||
endforeach()
|
||||
endif()
|
||||
endfunction()
|
||||
|
||||
function(hunter_gate_status_debug)
|
||||
if(HUNTER_STATUS_DEBUG)
|
||||
foreach(print_message ${ARGV})
|
||||
string(TIMESTAMP timestamp)
|
||||
message(STATUS "[hunter *** DEBUG *** ${timestamp}] ${print_message}")
|
||||
endforeach()
|
||||
endif()
|
||||
endfunction()
|
||||
|
||||
function(hunter_gate_error_page error_page)
|
||||
message("------------------------------ ERROR ------------------------------")
|
||||
message(" ${HUNTER_ERROR_PAGE}/${error_page}.html")
|
||||
message("-------------------------------------------------------------------")
|
||||
message("")
|
||||
message(FATAL_ERROR "")
|
||||
endfunction()
|
||||
|
||||
function(hunter_gate_internal_error)
|
||||
message("")
|
||||
foreach(print_message ${ARGV})
|
||||
message("[hunter ** INTERNAL **] ${print_message}")
|
||||
endforeach()
|
||||
message("[hunter ** INTERNAL **] [Directory:${CMAKE_CURRENT_LIST_DIR}]")
|
||||
message("")
|
||||
hunter_gate_error_page("error.internal")
|
||||
endfunction()
|
||||
|
||||
function(hunter_gate_fatal_error)
|
||||
cmake_parse_arguments(hunter "" "ERROR_PAGE" "" "${ARGV}")
|
||||
if("${hunter_ERROR_PAGE}" STREQUAL "")
|
||||
hunter_gate_internal_error("Expected ERROR_PAGE")
|
||||
endif()
|
||||
message("")
|
||||
foreach(x ${hunter_UNPARSED_ARGUMENTS})
|
||||
message("[hunter ** FATAL ERROR **] ${x}")
|
||||
endforeach()
|
||||
message("[hunter ** FATAL ERROR **] [Directory:${CMAKE_CURRENT_LIST_DIR}]")
|
||||
message("")
|
||||
hunter_gate_error_page("${hunter_ERROR_PAGE}")
|
||||
endfunction()
|
||||
|
||||
function(hunter_gate_user_error)
|
||||
hunter_gate_fatal_error(${ARGV} ERROR_PAGE "error.incorrect.input.data")
|
||||
endfunction()
|
||||
|
||||
function(hunter_gate_self root version sha1 result)
|
||||
string(COMPARE EQUAL "${root}" "" is_bad)
|
||||
if(is_bad)
|
||||
hunter_gate_internal_error("root is empty")
|
||||
endif()
|
||||
|
||||
string(COMPARE EQUAL "${version}" "" is_bad)
|
||||
if(is_bad)
|
||||
hunter_gate_internal_error("version is empty")
|
||||
endif()
|
||||
|
||||
string(COMPARE EQUAL "${sha1}" "" is_bad)
|
||||
if(is_bad)
|
||||
hunter_gate_internal_error("sha1 is empty")
|
||||
endif()
|
||||
|
||||
string(SUBSTRING "${sha1}" 0 7 archive_id)
|
||||
|
||||
if(EXISTS "${root}/cmake/Hunter")
|
||||
set(hunter_self "${root}")
|
||||
else()
|
||||
set(
|
||||
hunter_self
|
||||
"${root}/_Base/Download/Hunter/${version}/${archive_id}/Unpacked"
|
||||
)
|
||||
endif()
|
||||
|
||||
set("${result}" "${hunter_self}" PARENT_SCOPE)
|
||||
endfunction()
|
||||
|
||||
# Set HUNTER_GATE_ROOT cmake variable to suitable value.
|
||||
function(hunter_gate_detect_root)
|
||||
# Check CMake variable
|
||||
if(HUNTER_ROOT)
|
||||
set(HUNTER_GATE_ROOT "${HUNTER_ROOT}" PARENT_SCOPE)
|
||||
hunter_gate_status_debug("HUNTER_ROOT detected by cmake variable")
|
||||
return()
|
||||
endif()
|
||||
|
||||
# Check environment variable
|
||||
if(DEFINED ENV{HUNTER_ROOT})
|
||||
set(HUNTER_GATE_ROOT "$ENV{HUNTER_ROOT}" PARENT_SCOPE)
|
||||
hunter_gate_status_debug("HUNTER_ROOT detected by environment variable")
|
||||
return()
|
||||
endif()
|
||||
|
||||
# Check HOME environment variable
|
||||
if(DEFINED ENV{HOME})
|
||||
set(HUNTER_GATE_ROOT "$ENV{HOME}/.hunter" PARENT_SCOPE)
|
||||
hunter_gate_status_debug("HUNTER_ROOT set using HOME environment variable")
|
||||
return()
|
||||
endif()
|
||||
|
||||
# Check SYSTEMDRIVE and USERPROFILE environment variable (windows only)
|
||||
if(WIN32)
|
||||
if(DEFINED ENV{SYSTEMDRIVE})
|
||||
set(HUNTER_GATE_ROOT "$ENV{SYSTEMDRIVE}/.hunter" PARENT_SCOPE)
|
||||
hunter_gate_status_debug(
|
||||
"HUNTER_ROOT set using SYSTEMDRIVE environment variable"
|
||||
)
|
||||
return()
|
||||
endif()
|
||||
|
||||
if(DEFINED ENV{USERPROFILE})
|
||||
set(HUNTER_GATE_ROOT "$ENV{USERPROFILE}/.hunter" PARENT_SCOPE)
|
||||
hunter_gate_status_debug(
|
||||
"HUNTER_ROOT set using USERPROFILE environment variable"
|
||||
)
|
||||
return()
|
||||
endif()
|
||||
endif()
|
||||
|
||||
hunter_gate_fatal_error(
|
||||
"Can't detect HUNTER_ROOT"
|
||||
ERROR_PAGE "error.detect.hunter.root"
|
||||
)
|
||||
endfunction()
|
||||
|
||||
function(hunter_gate_download dir)
|
||||
string(
|
||||
COMPARE
|
||||
NOTEQUAL
|
||||
"$ENV{HUNTER_DISABLE_AUTOINSTALL}"
|
||||
""
|
||||
disable_autoinstall
|
||||
)
|
||||
if(disable_autoinstall AND NOT HUNTER_RUN_INSTALL)
|
||||
hunter_gate_fatal_error(
|
||||
"Hunter not found in '${dir}'"
|
||||
"Set HUNTER_RUN_INSTALL=ON to auto-install it from '${HUNTER_GATE_URL}'"
|
||||
"Settings:"
|
||||
" HUNTER_ROOT: ${HUNTER_GATE_ROOT}"
|
||||
" HUNTER_SHA1: ${HUNTER_GATE_SHA1}"
|
||||
ERROR_PAGE "error.run.install"
|
||||
)
|
||||
endif()
|
||||
string(COMPARE EQUAL "${dir}" "" is_bad)
|
||||
if(is_bad)
|
||||
hunter_gate_internal_error("Empty 'dir' argument")
|
||||
endif()
|
||||
|
||||
string(COMPARE EQUAL "${HUNTER_GATE_SHA1}" "" is_bad)
|
||||
if(is_bad)
|
||||
hunter_gate_internal_error("HUNTER_GATE_SHA1 empty")
|
||||
endif()
|
||||
|
||||
string(COMPARE EQUAL "${HUNTER_GATE_URL}" "" is_bad)
|
||||
if(is_bad)
|
||||
hunter_gate_internal_error("HUNTER_GATE_URL empty")
|
||||
endif()
|
||||
|
||||
set(done_location "${dir}/DONE")
|
||||
set(sha1_location "${dir}/SHA1")
|
||||
|
||||
set(build_dir "${dir}/Build")
|
||||
set(cmakelists "${dir}/CMakeLists.txt")
|
||||
|
||||
hunter_gate_status_debug("Locking directory: ${dir}")
|
||||
file(LOCK "${dir}" DIRECTORY GUARD FUNCTION)
|
||||
hunter_gate_status_debug("Lock done")
|
||||
|
||||
if(EXISTS "${done_location}")
|
||||
# while waiting for lock other instance can do all the job
|
||||
hunter_gate_status_debug("File '${done_location}' found, skip install")
|
||||
return()
|
||||
endif()
|
||||
|
||||
file(REMOVE_RECURSE "${build_dir}")
|
||||
file(REMOVE_RECURSE "${cmakelists}")
|
||||
|
||||
file(MAKE_DIRECTORY "${build_dir}") # check directory permissions
|
||||
|
||||
# Disabling languages speeds up a little bit, reduces noise in the output
|
||||
# and avoids path too long windows error
|
||||
file(
|
||||
WRITE
|
||||
"${cmakelists}"
|
||||
"cmake_minimum_required(VERSION 3.2)\n"
|
||||
"project(HunterDownload LANGUAGES NONE)\n"
|
||||
"include(ExternalProject)\n"
|
||||
"ExternalProject_Add(\n"
|
||||
" Hunter\n"
|
||||
" URL\n"
|
||||
" \"${HUNTER_GATE_URL}\"\n"
|
||||
" URL_HASH\n"
|
||||
" SHA1=${HUNTER_GATE_SHA1}\n"
|
||||
" DOWNLOAD_DIR\n"
|
||||
" \"${dir}\"\n"
|
||||
" TLS_VERIFY\n"
|
||||
" ${HUNTER_TLS_VERIFY}\n"
|
||||
" SOURCE_DIR\n"
|
||||
" \"${dir}/Unpacked\"\n"
|
||||
" CONFIGURE_COMMAND\n"
|
||||
" \"\"\n"
|
||||
" BUILD_COMMAND\n"
|
||||
" \"\"\n"
|
||||
" INSTALL_COMMAND\n"
|
||||
" \"\"\n"
|
||||
")\n"
|
||||
)
|
||||
|
||||
if(HUNTER_STATUS_DEBUG)
|
||||
set(logging_params "")
|
||||
else()
|
||||
set(logging_params OUTPUT_QUIET)
|
||||
endif()
|
||||
|
||||
hunter_gate_status_debug("Run generate")
|
||||
|
||||
# Need to add toolchain file too.
|
||||
# Otherwise on Visual Studio + MDD this will fail with error:
|
||||
# "Could not find an appropriate version of the Windows 10 SDK installed on this machine"
|
||||
if(EXISTS "${CMAKE_TOOLCHAIN_FILE}")
|
||||
get_filename_component(absolute_CMAKE_TOOLCHAIN_FILE "${CMAKE_TOOLCHAIN_FILE}" ABSOLUTE)
|
||||
set(toolchain_arg "-DCMAKE_TOOLCHAIN_FILE=${absolute_CMAKE_TOOLCHAIN_FILE}")
|
||||
else()
|
||||
# 'toolchain_arg' can't be empty
|
||||
set(toolchain_arg "-DCMAKE_TOOLCHAIN_FILE=")
|
||||
endif()
|
||||
|
||||
string(COMPARE EQUAL "${CMAKE_MAKE_PROGRAM}" "" no_make)
|
||||
if(no_make)
|
||||
set(make_arg "")
|
||||
else()
|
||||
# Test case: remove Ninja from PATH but set it via CMAKE_MAKE_PROGRAM
|
||||
set(make_arg "-DCMAKE_MAKE_PROGRAM=${CMAKE_MAKE_PROGRAM}")
|
||||
endif()
|
||||
|
||||
execute_process(
|
||||
COMMAND
|
||||
"${CMAKE_COMMAND}"
|
||||
"-H${dir}"
|
||||
"-B${build_dir}"
|
||||
"-G${CMAKE_GENERATOR}"
|
||||
"${toolchain_arg}"
|
||||
${make_arg}
|
||||
WORKING_DIRECTORY "${dir}"
|
||||
RESULT_VARIABLE download_result
|
||||
${logging_params}
|
||||
)
|
||||
|
||||
if(NOT download_result EQUAL 0)
|
||||
hunter_gate_internal_error(
|
||||
"Configure project failed."
|
||||
"To reproduce the error run: ${CMAKE_COMMAND} -H${dir} -B${build_dir} -G${CMAKE_GENERATOR} ${toolchain_arg} ${make_arg}"
|
||||
"In directory ${dir}"
|
||||
)
|
||||
endif()
|
||||
|
||||
hunter_gate_status_print(
|
||||
"Initializing Hunter workspace (${HUNTER_GATE_SHA1})"
|
||||
" ${HUNTER_GATE_URL}"
|
||||
" -> ${dir}"
|
||||
)
|
||||
execute_process(
|
||||
COMMAND "${CMAKE_COMMAND}" --build "${build_dir}"
|
||||
WORKING_DIRECTORY "${dir}"
|
||||
RESULT_VARIABLE download_result
|
||||
${logging_params}
|
||||
)
|
||||
|
||||
if(NOT download_result EQUAL 0)
|
||||
hunter_gate_internal_error("Build project failed")
|
||||
endif()
|
||||
|
||||
file(REMOVE_RECURSE "${build_dir}")
|
||||
file(REMOVE_RECURSE "${cmakelists}")
|
||||
|
||||
file(WRITE "${sha1_location}" "${HUNTER_GATE_SHA1}")
|
||||
file(WRITE "${done_location}" "DONE")
|
||||
|
||||
hunter_gate_status_debug("Finished")
|
||||
endfunction()
|
||||
|
||||
# Must be a macro so master file 'cmake/Hunter' can
|
||||
# apply all variables easily just by 'include' command
|
||||
# (otherwise PARENT_SCOPE magic needed)
|
||||
macro(HunterGate)
|
||||
if(HUNTER_GATE_DONE)
|
||||
# variable HUNTER_GATE_DONE set explicitly for external project
|
||||
# (see `hunter_download`)
|
||||
set_property(GLOBAL PROPERTY HUNTER_GATE_DONE YES)
|
||||
endif()
|
||||
|
||||
# First HunterGate command will init Hunter, others will be ignored
|
||||
get_property(_hunter_gate_done GLOBAL PROPERTY HUNTER_GATE_DONE SET)
|
||||
|
||||
if(NOT HUNTER_ENABLED)
|
||||
# Empty function to avoid error "unknown function"
|
||||
function(hunter_add_package)
|
||||
endfunction()
|
||||
|
||||
set(
|
||||
_hunter_gate_disabled_mode_dir
|
||||
"${CMAKE_CURRENT_LIST_DIR}/cmake/Hunter/disabled-mode"
|
||||
)
|
||||
if(EXISTS "${_hunter_gate_disabled_mode_dir}")
|
||||
hunter_gate_status_debug(
|
||||
"Adding \"disabled-mode\" modules: ${_hunter_gate_disabled_mode_dir}"
|
||||
)
|
||||
list(APPEND CMAKE_PREFIX_PATH "${_hunter_gate_disabled_mode_dir}")
|
||||
endif()
|
||||
elseif(_hunter_gate_done)
|
||||
hunter_gate_status_debug("Secondary HunterGate (use old settings)")
|
||||
hunter_gate_self(
|
||||
"${HUNTER_CACHED_ROOT}"
|
||||
"${HUNTER_VERSION}"
|
||||
"${HUNTER_SHA1}"
|
||||
_hunter_self
|
||||
)
|
||||
include("${_hunter_self}/cmake/Hunter")
|
||||
else()
|
||||
set(HUNTER_GATE_LOCATION "${CMAKE_CURRENT_SOURCE_DIR}")
|
||||
|
||||
string(COMPARE NOTEQUAL "${PROJECT_NAME}" "" _have_project_name)
|
||||
if(_have_project_name)
|
||||
hunter_gate_fatal_error(
|
||||
"Please set HunterGate *before* 'project' command. "
|
||||
"Detected project: ${PROJECT_NAME}"
|
||||
ERROR_PAGE "error.huntergate.before.project"
|
||||
)
|
||||
endif()
|
||||
|
||||
cmake_parse_arguments(
|
||||
HUNTER_GATE "LOCAL" "URL;SHA1;GLOBAL;FILEPATH" "" ${ARGV}
|
||||
)
|
||||
|
||||
string(COMPARE EQUAL "${HUNTER_GATE_SHA1}" "" _empty_sha1)
|
||||
string(COMPARE EQUAL "${HUNTER_GATE_URL}" "" _empty_url)
|
||||
string(
|
||||
COMPARE
|
||||
NOTEQUAL
|
||||
"${HUNTER_GATE_UNPARSED_ARGUMENTS}"
|
||||
""
|
||||
_have_unparsed
|
||||
)
|
||||
string(COMPARE NOTEQUAL "${HUNTER_GATE_GLOBAL}" "" _have_global)
|
||||
string(COMPARE NOTEQUAL "${HUNTER_GATE_FILEPATH}" "" _have_filepath)
|
||||
|
||||
if(_have_unparsed)
|
||||
hunter_gate_user_error(
|
||||
"HunterGate unparsed arguments: ${HUNTER_GATE_UNPARSED_ARGUMENTS}"
|
||||
)
|
||||
endif()
|
||||
if(_empty_sha1)
|
||||
hunter_gate_user_error("SHA1 suboption of HunterGate is mandatory")
|
||||
endif()
|
||||
if(_empty_url)
|
||||
hunter_gate_user_error("URL suboption of HunterGate is mandatory")
|
||||
endif()
|
||||
if(_have_global)
|
||||
if(HUNTER_GATE_LOCAL)
|
||||
hunter_gate_user_error("Unexpected LOCAL (already has GLOBAL)")
|
||||
endif()
|
||||
if(_have_filepath)
|
||||
hunter_gate_user_error("Unexpected FILEPATH (already has GLOBAL)")
|
||||
endif()
|
||||
endif()
|
||||
if(HUNTER_GATE_LOCAL)
|
||||
if(_have_global)
|
||||
hunter_gate_user_error("Unexpected GLOBAL (already has LOCAL)")
|
||||
endif()
|
||||
if(_have_filepath)
|
||||
hunter_gate_user_error("Unexpected FILEPATH (already has LOCAL)")
|
||||
endif()
|
||||
endif()
|
||||
if(_have_filepath)
|
||||
if(_have_global)
|
||||
hunter_gate_user_error("Unexpected GLOBAL (already has FILEPATH)")
|
||||
endif()
|
||||
if(HUNTER_GATE_LOCAL)
|
||||
hunter_gate_user_error("Unexpected LOCAL (already has FILEPATH)")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
hunter_gate_detect_root() # set HUNTER_GATE_ROOT
|
||||
|
||||
# Beautify path, fix probable problems with windows path slashes
|
||||
get_filename_component(
|
||||
HUNTER_GATE_ROOT "${HUNTER_GATE_ROOT}" ABSOLUTE
|
||||
)
|
||||
hunter_gate_status_debug("HUNTER_ROOT: ${HUNTER_GATE_ROOT}")
|
||||
if(NOT HUNTER_ALLOW_SPACES_IN_PATH)
|
||||
string(FIND "${HUNTER_GATE_ROOT}" " " _contain_spaces)
|
||||
if(NOT _contain_spaces EQUAL -1)
|
||||
hunter_gate_fatal_error(
|
||||
"HUNTER_ROOT (${HUNTER_GATE_ROOT}) contains spaces."
|
||||
"Set HUNTER_ALLOW_SPACES_IN_PATH=ON to skip this error"
|
||||
"(Use at your own risk!)"
|
||||
ERROR_PAGE "error.spaces.in.hunter.root"
|
||||
)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
string(
|
||||
REGEX
|
||||
MATCH
|
||||
"[0-9]+\\.[0-9]+\\.[0-9]+[-_a-z0-9]*"
|
||||
HUNTER_GATE_VERSION
|
||||
"${HUNTER_GATE_URL}"
|
||||
)
|
||||
string(COMPARE EQUAL "${HUNTER_GATE_VERSION}" "" _is_empty)
|
||||
if(_is_empty)
|
||||
set(HUNTER_GATE_VERSION "unknown")
|
||||
endif()
|
||||
|
||||
hunter_gate_self(
|
||||
"${HUNTER_GATE_ROOT}"
|
||||
"${HUNTER_GATE_VERSION}"
|
||||
"${HUNTER_GATE_SHA1}"
|
||||
_hunter_self
|
||||
)
|
||||
|
||||
set(_master_location "${_hunter_self}/cmake/Hunter")
|
||||
if(EXISTS "${HUNTER_GATE_ROOT}/cmake/Hunter")
|
||||
# Hunter downloaded manually (e.g. by 'git clone')
|
||||
set(_unused "xxxxxxxxxx")
|
||||
set(HUNTER_GATE_SHA1 "${_unused}")
|
||||
set(HUNTER_GATE_VERSION "${_unused}")
|
||||
else()
|
||||
get_filename_component(_archive_id_location "${_hunter_self}/.." ABSOLUTE)
|
||||
set(_done_location "${_archive_id_location}/DONE")
|
||||
set(_sha1_location "${_archive_id_location}/SHA1")
|
||||
|
||||
# Check Hunter already downloaded by HunterGate
|
||||
if(NOT EXISTS "${_done_location}")
|
||||
hunter_gate_download("${_archive_id_location}")
|
||||
endif()
|
||||
|
||||
if(NOT EXISTS "${_done_location}")
|
||||
hunter_gate_internal_error("hunter_gate_download failed")
|
||||
endif()
|
||||
|
||||
if(NOT EXISTS "${_sha1_location}")
|
||||
hunter_gate_internal_error("${_sha1_location} not found")
|
||||
endif()
|
||||
file(READ "${_sha1_location}" _sha1_value)
|
||||
string(TOLOWER "${_sha1_value}" _sha1_value_lower)
|
||||
string(TOLOWER "${HUNTER_GATE_SHA1}" _HUNTER_GATE_SHA1_lower)
|
||||
string(COMPARE EQUAL "${_sha1_value_lower}" "${_HUNTER_GATE_SHA1_lower}" _is_equal)
|
||||
if(NOT _is_equal)
|
||||
hunter_gate_internal_error(
|
||||
"Short SHA1 collision:"
|
||||
" ${_sha1_value} (from ${_sha1_location})"
|
||||
" ${HUNTER_GATE_SHA1} (HunterGate)"
|
||||
)
|
||||
endif()
|
||||
if(NOT EXISTS "${_master_location}")
|
||||
hunter_gate_user_error(
|
||||
"Master file not found:"
|
||||
" ${_master_location}"
|
||||
"try to update Hunter/HunterGate"
|
||||
)
|
||||
endif()
|
||||
endif()
|
||||
include("${_master_location}")
|
||||
set_property(GLOBAL PROPERTY HUNTER_GATE_DONE YES)
|
||||
endif()
|
||||
endmacro()
|
51
third_party/open-bp-cpp/example/buttplugCpp.cpp
vendored
Normal file
51
third_party/open-bp-cpp/example/buttplugCpp.cpp
vendored
Normal file
|
@ -0,0 +1,51 @@
|
|||
// buttplugCpp.cpp : Defines the entry point for the application.
|
||||
//
|
||||
|
||||
#include "buttplugCpp.h"
|
||||
|
||||
|
||||
using namespace std;
|
||||
|
||||
void callbackFunction(const mhl::Messages msg) {
|
||||
if (msg.messageType == mhl::MessageTypes::DeviceList) {
|
||||
cout << "Device List callback" << endl;
|
||||
}
|
||||
if (msg.messageType == mhl::MessageTypes::DeviceAdded) {
|
||||
cout << "Device Added callback" << endl;
|
||||
}
|
||||
if (msg.messageType == mhl::MessageTypes::ServerInfo) {
|
||||
cout << "Server Info callback" << endl;
|
||||
}
|
||||
if (msg.messageType == mhl::MessageTypes::DeviceRemoved) {
|
||||
cout << "Device Removed callback" << endl;
|
||||
}
|
||||
if (msg.messageType == mhl::MessageTypes::SensorReading) {
|
||||
cout << "Sensor Reading callback" << endl;
|
||||
}
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
std::string url = "ws://127.0.0.1";
|
||||
std::cout << "\n";
|
||||
Client client(url, 12345, "test.txt");
|
||||
client.connect(callbackFunction);
|
||||
client.requestDeviceList();
|
||||
client.startScan();
|
||||
while (1) {
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(2000));
|
||||
client.stopScan();
|
||||
break;
|
||||
}
|
||||
//std::vector<DeviceClass> myDevices = client.getDevices();
|
||||
//client.sendScalar(myDevices[0], 0.5);
|
||||
//client.sendScalar(myDevices[1], 0.5);
|
||||
//client.sensorSubscribe(myDevices[0], 0);
|
||||
//std::this_thread::sleep_for(std::chrono::milliseconds(20000));
|
||||
//client.sensorUnsubscribe(myDevices[0], 0);
|
||||
//client.stopAllDevices();
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(2000));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
9
third_party/open-bp-cpp/example/buttplugCpp.h
vendored
Normal file
9
third_party/open-bp-cpp/example/buttplugCpp.h
vendored
Normal file
|
@ -0,0 +1,9 @@
|
|||
// buttplugCpp.h : Include file for standard system include files,
|
||||
// or project specific include files.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <iostream>
|
||||
#include "../include/buttplugclient.h"
|
||||
|
||||
// TODO: Reference additional headers your program requires here.
|
110
third_party/open-bp-cpp/include/buttplugclient.h
vendored
Normal file
110
third_party/open-bp-cpp/include/buttplugclient.h
vendored
Normal file
|
@ -0,0 +1,110 @@
|
|||
#include <string>
|
||||
#include <functional>
|
||||
#include <iostream>
|
||||
#include <atomic>
|
||||
#include <sstream>
|
||||
#include <queue>
|
||||
|
||||
#include <IXWebSocket.h>
|
||||
#ifdef _WIN32
|
||||
#include <IXNetSystem.h>
|
||||
#endif
|
||||
#include "messageHandler.h"
|
||||
#include "log.h"
|
||||
|
||||
typedef msg::SensorReading SensorClass;
|
||||
|
||||
// Helper class to store devices and access them outside of the library.
|
||||
class DeviceClass {
|
||||
public:
|
||||
std::string deviceName;
|
||||
std::string displayName;
|
||||
std::vector<std::string> commandTypes;
|
||||
std::vector<std::string> sensorTypes;
|
||||
unsigned int deviceID;
|
||||
};
|
||||
|
||||
// Main client class
|
||||
class Client {
|
||||
public:
|
||||
// Constructor which initialized websockets for Windows. Add an IFDEF depending on compilation OS for portability.
|
||||
Client(std::string url, unsigned int port) {
|
||||
#ifdef _WIN32
|
||||
ix::initNetSystem();
|
||||
#endif
|
||||
lUrl = url;
|
||||
lPort = port;
|
||||
}
|
||||
Client(std::string url, unsigned int port, std::string logfile) {
|
||||
#ifdef _WIN32
|
||||
ix::initNetSystem();
|
||||
#endif
|
||||
lUrl = url;
|
||||
lPort = port;
|
||||
logging = 1;
|
||||
if (!logfile.empty()) logInfo.init(logfile);
|
||||
else logInfo.init("log.txt");
|
||||
}
|
||||
~Client() {
|
||||
#ifdef _WIN32
|
||||
ix::uninitNetSystem();
|
||||
#endif
|
||||
}
|
||||
|
||||
int connect(void (*callFunc)(const mhl::Messages));
|
||||
// Atomic variables to store connection status. Can be accessed outside library too since atomic.
|
||||
std::atomic<int> wsConnected{0};
|
||||
std::atomic<int> isConnecting{0};
|
||||
std::atomic<int> clientConnected{0};
|
||||
// Condition variables for the atomics, we want C++11 support
|
||||
std::condition_variable condClient;
|
||||
std::condition_variable condWs;
|
||||
|
||||
// Public functions that send requests to server.
|
||||
void startScan();
|
||||
void stopScan();
|
||||
void requestDeviceList();
|
||||
void stopDevice(DeviceClass dev);
|
||||
void stopAllDevices();
|
||||
void sendScalar(DeviceClass dev, double str);
|
||||
void sensorRead(DeviceClass dev, int senIndex);
|
||||
void sensorSubscribe(DeviceClass dev, int senIndex);
|
||||
void sensorUnsubscribe(DeviceClass dev, int senIndex);
|
||||
|
||||
// Mutex blocked function which grabs the currently connected devices and sensor reads.
|
||||
std::vector<DeviceClass> getDevices();
|
||||
SensorClass getSensors();
|
||||
private:
|
||||
// URL variables for the websocket.
|
||||
std::string FullUrl;
|
||||
std::string lUrl;
|
||||
unsigned int lPort;
|
||||
|
||||
int logging = 0;
|
||||
Logger logInfo;
|
||||
|
||||
ix::WebSocket webSocket;
|
||||
|
||||
// Message handler class, which takes messages, parses them and makes them to classes.
|
||||
mhl::Messages messageHandler;
|
||||
|
||||
// Queue variable for passing received messages from server.
|
||||
std::queue<std::string> q;
|
||||
// Condition variabel to wait for received messages in the queue.
|
||||
std::condition_variable cond;
|
||||
// Mutex to ensure no race conditions.
|
||||
std::mutex msgMx;
|
||||
// Callback function for when a message is received and handled.
|
||||
std::function<void(const mhl::Messages&)> messageCallback;
|
||||
|
||||
// Device and sensor class vector which is grabbed outside of the library.
|
||||
std::vector<DeviceClass> devices;
|
||||
SensorClass sensorData;
|
||||
|
||||
void connectServer();
|
||||
void callbackFunction(const ix::WebSocketMessagePtr& msg);
|
||||
void messageHandling();
|
||||
void sendMessage(json msg, mhl::MessageTypes mType);
|
||||
void updateDevices();
|
||||
int findDevice(DeviceClass dev);
|
||||
};
|
42
third_party/open-bp-cpp/include/helperClasses.h
vendored
Normal file
42
third_party/open-bp-cpp/include/helperClasses.h
vendored
Normal file
|
@ -0,0 +1,42 @@
|
|||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#ifdef DEBUG
|
||||
#define DEBUG_MSG(str) do { std::cout << str << std::endl; } while( false )
|
||||
#else
|
||||
#define DEBUG_MSG(str) do { } while ( false )
|
||||
#endif
|
||||
|
||||
|
||||
class DeviceCmdAttr {
|
||||
public:
|
||||
std::string FeatureDescriptor = "";
|
||||
unsigned int StepCount = 0;
|
||||
std::string ActuatorType = "";
|
||||
std::string SensorType = "";
|
||||
std::vector<int> SensorRange; // every two steps
|
||||
//std::vector<std::string> Endpoints;
|
||||
};
|
||||
|
||||
class DeviceCmd {
|
||||
public:
|
||||
std::string CmdType = "";
|
||||
std::string StopDeviceCmd = "";
|
||||
std::vector<DeviceCmdAttr> DeviceCmdAttributes;
|
||||
};
|
||||
|
||||
class Device {
|
||||
public:
|
||||
std::string DeviceName;
|
||||
unsigned int DeviceIndex;
|
||||
unsigned int DeviceMessageTimingGap = 0;
|
||||
std::string DeviceDisplayName = "";
|
||||
std::vector<DeviceCmd> DeviceMessages;
|
||||
};
|
||||
|
||||
class Scalar {
|
||||
public:
|
||||
unsigned int Index;
|
||||
double ScalarVal;
|
||||
std::string ActuatorType;
|
||||
};
|
31
third_party/open-bp-cpp/include/log.h
vendored
Normal file
31
third_party/open-bp-cpp/include/log.h
vendored
Normal file
|
@ -0,0 +1,31 @@
|
|||
#include <fstream>
|
||||
#include <vector>
|
||||
#include <chrono>
|
||||
|
||||
class RequestQueue {
|
||||
public:
|
||||
unsigned int id = 1;
|
||||
std::string requestType;
|
||||
};
|
||||
|
||||
class Logger {
|
||||
public:
|
||||
Logger() {
|
||||
start = std::chrono::system_clock::now();
|
||||
}
|
||||
|
||||
~Logger() {
|
||||
logFile.close();
|
||||
}
|
||||
|
||||
void init(std::string filename);
|
||||
void logSentMessage(std::string rqType, unsigned int id);
|
||||
void logReceivedMessage(std::string repType, unsigned int id);
|
||||
|
||||
private:
|
||||
std::fstream logFile;
|
||||
std::vector<RequestQueue> rQueue;
|
||||
|
||||
// Using time point and system_clock
|
||||
std::chrono::time_point<std::chrono::system_clock> start, end;
|
||||
};
|
95
third_party/open-bp-cpp/include/messageHandler.h
vendored
Normal file
95
third_party/open-bp-cpp/include/messageHandler.h
vendored
Normal file
|
@ -0,0 +1,95 @@
|
|||
#include<string>
|
||||
#include<map>
|
||||
|
||||
#include "messages.h"
|
||||
|
||||
namespace mhl {
|
||||
// Enum class for message types for convenience.
|
||||
enum class MessageTypes {
|
||||
Ok,
|
||||
Error,
|
||||
Ping,
|
||||
RequestServerInfo,
|
||||
ServerInfo,
|
||||
StartScanning,
|
||||
StopScanning,
|
||||
ScanningFinished,
|
||||
RequestDeviceList,
|
||||
DeviceList,
|
||||
DeviceAdded,
|
||||
DeviceRemoved,
|
||||
StopDeviceCmd,
|
||||
StopAllDevices,
|
||||
ScalarCmd,
|
||||
LinearCmd,
|
||||
RotateCmd,
|
||||
SensorReadCmd,
|
||||
SensorReading,
|
||||
SensorSubscribeCmd,
|
||||
SensorUnsubscribeCmd
|
||||
};
|
||||
|
||||
typedef std::map<MessageTypes, std::string> MessageMap_t;
|
||||
|
||||
// Class for request messages.
|
||||
class Requests {
|
||||
public:
|
||||
msg::RequestServerInfo requestServerInfo;
|
||||
msg::StartScanning startScanning;
|
||||
msg::StopScanning stopScanning;
|
||||
msg::ScanningFinished scanningFinished;
|
||||
msg::RequestDeviceList requestDeviceList;
|
||||
msg::DeviceRemoved deviceRemoved;
|
||||
msg::StopDeviceCmd stopDeviceCmd;
|
||||
msg::StopAllDevices stopAllDevices;
|
||||
msg::ScalarCmd scalarCmd;
|
||||
msg::SensorReadCmd sensorReadCmd;
|
||||
msg::SensorSubscribeCmd sensorSubscribeCmd;
|
||||
msg::SensorUnsubscribeCmd sensorUnsubscribeCmd;
|
||||
};
|
||||
|
||||
// Class for messages received and for handling all types of messages.
|
||||
class Messages {
|
||||
public:
|
||||
MessageTypes messageType = MessageTypes::Ok;
|
||||
unsigned int Id;
|
||||
|
||||
// A map for message strings corresponding to enum. This is in this class since it is more convenient.
|
||||
MessageMap_t messageMap = {
|
||||
{MessageTypes::Ok, "Ok"},
|
||||
{MessageTypes::Error, "Error"},
|
||||
{MessageTypes::Ping, "Ping"},
|
||||
{MessageTypes::RequestServerInfo, "RequestServerInfo"},
|
||||
{MessageTypes::ServerInfo, "ServerInfo"},
|
||||
{MessageTypes::StartScanning, "StartScanning"},
|
||||
{MessageTypes::StopScanning, "StopScanning"},
|
||||
{MessageTypes::ScanningFinished, "ScanningFinished"},
|
||||
{MessageTypes::RequestDeviceList, "RequestDeviceList"},
|
||||
{MessageTypes::DeviceList, "DeviceList"},
|
||||
{MessageTypes::DeviceAdded, "DeviceAdded"},
|
||||
{MessageTypes::DeviceRemoved, "DeviceRemoved"},
|
||||
{MessageTypes::StopDeviceCmd, "StopDeviceCmd"},
|
||||
{MessageTypes::StopAllDevices, "StopAllDevices"},
|
||||
{MessageTypes::ScalarCmd, "ScalarCmd"},
|
||||
{MessageTypes::LinearCmd, "LinearCmd"},
|
||||
{MessageTypes::RotateCmd, "RotateCmd"},
|
||||
{MessageTypes::SensorReadCmd, "SensorReadCmd"},
|
||||
{MessageTypes::SensorReading, "SensorReading"},
|
||||
{MessageTypes::SensorSubscribeCmd, "SensorSubscribeCmd"},
|
||||
{MessageTypes::SensorUnsubscribeCmd, "SensorUnsubscribeCmd"}
|
||||
};
|
||||
|
||||
msg::Ok ok;
|
||||
msg::Error error;
|
||||
msg::ServerInfo serverInfo;
|
||||
msg::DeviceList deviceList;
|
||||
msg::DeviceAdded deviceAdded;
|
||||
msg::DeviceRemoved deviceRemoved;
|
||||
msg::SensorReading sensorReading;
|
||||
|
||||
// Both server message and requests are handled in this class.
|
||||
void handleServerMessage(json& msg);
|
||||
json handleClientRequest(Requests req);
|
||||
private:
|
||||
};
|
||||
}
|
150
third_party/open-bp-cpp/include/messages.h
vendored
Normal file
150
third_party/open-bp-cpp/include/messages.h
vendored
Normal file
|
@ -0,0 +1,150 @@
|
|||
#include <nlohmann/json.hpp>
|
||||
#include "helperClasses.h"
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <iostream>
|
||||
|
||||
using json = nlohmann::json;
|
||||
|
||||
// Classes for the various message types and functions to convert to/from json.
|
||||
namespace msg {
|
||||
class Ok {
|
||||
public:
|
||||
unsigned int Id = 1;
|
||||
//NLOHMANN_DEFINE_TYPE_INTRUSIVE(Ok, Id);
|
||||
};
|
||||
|
||||
class Error {
|
||||
public:
|
||||
unsigned int Id = 1;
|
||||
std::string ErrorMessage = "";
|
||||
int ErrorCode = 0;
|
||||
|
||||
//NLOHMANN_DEFINE_TYPE_INTRUSIVE(Error, Id, ErrorMessage, ErrorCode);
|
||||
};
|
||||
|
||||
class RequestServerInfo {
|
||||
public:
|
||||
unsigned int Id = 1;
|
||||
std::string ClientName = "Default Client";
|
||||
unsigned int MessageVersion = 3;
|
||||
};
|
||||
|
||||
class ServerInfo {
|
||||
public:
|
||||
unsigned int Id;
|
||||
std::string ServerName;
|
||||
unsigned int MessageVersion;
|
||||
unsigned int MaxPingTime;
|
||||
|
||||
//NLOHMANN_DEFINE_TYPE_INTRUSIVE(ServerInfo, Id, ServerName, MessageVersion, MaxPingTime);
|
||||
};
|
||||
|
||||
class StartScanning {
|
||||
public:
|
||||
unsigned int Id = 1;
|
||||
};
|
||||
|
||||
class StopScanning {
|
||||
public:
|
||||
unsigned int Id = 1;
|
||||
};
|
||||
|
||||
class ScanningFinished {
|
||||
public:
|
||||
unsigned int Id = 1;
|
||||
//NLOHMANN_DEFINE_TYPE_INTRUSIVE(ScanningFinished, Id);
|
||||
};
|
||||
|
||||
class RequestDeviceList {
|
||||
public:
|
||||
unsigned int Id = 1;
|
||||
};
|
||||
|
||||
class DeviceList {
|
||||
public:
|
||||
unsigned int Id = 1;
|
||||
std::vector<Device> Devices;
|
||||
};
|
||||
|
||||
class DeviceAdded {
|
||||
public:
|
||||
unsigned int Id = 1;
|
||||
Device device;
|
||||
};
|
||||
|
||||
class DeviceRemoved {
|
||||
public:
|
||||
unsigned int Id = 1;
|
||||
unsigned int DeviceIndex;
|
||||
};
|
||||
|
||||
class StopDeviceCmd {
|
||||
public:
|
||||
unsigned int Id = 1;
|
||||
unsigned int DeviceIndex;
|
||||
};
|
||||
|
||||
class StopAllDevices {
|
||||
public:
|
||||
unsigned int Id = 1;
|
||||
};
|
||||
|
||||
class ScalarCmd {
|
||||
public:
|
||||
unsigned int Id = 1;
|
||||
unsigned int DeviceIndex;
|
||||
std::vector<Scalar> Scalars;
|
||||
};
|
||||
|
||||
class SensorReadCmd {
|
||||
public:
|
||||
unsigned int Id = 1;
|
||||
unsigned int DeviceIndex;
|
||||
unsigned int SensorIndex;
|
||||
std::string SensorType;
|
||||
};
|
||||
|
||||
class SensorReading {
|
||||
public:
|
||||
unsigned int Id = 1;
|
||||
unsigned int DeviceIndex;
|
||||
unsigned int SensorIndex;
|
||||
std::string SensorType;
|
||||
std::vector<int> Data;
|
||||
};
|
||||
|
||||
class SensorSubscribeCmd {
|
||||
public:
|
||||
unsigned int Id = 1;
|
||||
unsigned int DeviceIndex;
|
||||
unsigned int SensorIndex;
|
||||
std::string SensorType;
|
||||
};
|
||||
|
||||
class SensorUnsubscribeCmd {
|
||||
public:
|
||||
unsigned int Id = 1;
|
||||
unsigned int DeviceIndex;
|
||||
unsigned int SensorIndex;
|
||||
std::string SensorType;
|
||||
};
|
||||
|
||||
extern void to_json(json& j, const RequestServerInfo& k);
|
||||
extern void to_json(json& j, const StartScanning& k);
|
||||
extern void to_json(json& j, const StopScanning& k);
|
||||
extern void to_json(json& j, const RequestDeviceList& k);
|
||||
extern void to_json(json& j, const StopDeviceCmd& k);
|
||||
extern void to_json(json& j, const StopAllDevices& k);
|
||||
extern void to_json(json& j, const ScalarCmd& k);
|
||||
extern void to_json(json& j, const SensorReadCmd& k);
|
||||
extern void to_json(json& j, const SensorSubscribeCmd& k);
|
||||
extern void to_json(json& j, const SensorUnsubscribeCmd& k);
|
||||
extern void from_json(const json& j, Ok& k);
|
||||
extern void from_json(const json& j, Error& k);
|
||||
extern void from_json(const json& j, ServerInfo& k);
|
||||
extern void from_json(const json& j, DeviceList& k);
|
||||
extern void from_json(const json& j, DeviceAdded& k);
|
||||
extern void from_json(const json& j, DeviceRemoved& k);
|
||||
extern void from_json(const json& j, SensorReading& k);
|
||||
}
|
423
third_party/open-bp-cpp/source/buttplugclient.cpp
vendored
Normal file
423
third_party/open-bp-cpp/source/buttplugclient.cpp
vendored
Normal file
|
@ -0,0 +1,423 @@
|
|||
#include "../include/buttplugclient.h"
|
||||
|
||||
/*
|
||||
TODO: Let the user access the devices in more details, that is, how many scalar cmds,
|
||||
how many sensor cmds and their types. Implement some kind of logging to track whether
|
||||
commands are successful. Implement Linear and Rotation cmds. Port to Linux.
|
||||
Investigate whether push back won't ruin sending scalar and sensor cmds, since the
|
||||
device list does not provide scalar or sensor indices (don't think it will).
|
||||
*/
|
||||
|
||||
|
||||
// Connection function with a function parameter which acts as a callback.
|
||||
int Client::connect(void (*callFunc)(const mhl::Messages)) {
|
||||
FullUrl = lUrl + ":" + std::to_string(lPort);
|
||||
|
||||
webSocket.setUrl(FullUrl);
|
||||
|
||||
// Ping interval option.
|
||||
webSocket.setPingInterval(10);
|
||||
|
||||
// Per message deflate connection is enabled by default. You can tweak its parameters or disable it
|
||||
webSocket.disablePerMessageDeflate();
|
||||
|
||||
// Set the callback function of the websocket
|
||||
std::function<void(const ix::WebSocketMessagePtr&)> callback = std::bind(&Client::callbackFunction, this, std::placeholders::_1);
|
||||
messageCallback = callFunc;
|
||||
|
||||
webSocket.setOnMessageCallback(callback);
|
||||
|
||||
// Start websocket and indicate that it is connecting (non blocking function, other functions must wait for it to connect).
|
||||
webSocket.start();
|
||||
isConnecting = 1;
|
||||
|
||||
// Start a message handler thread and detach it.
|
||||
std::thread messageHandler(&Client::messageHandling, this);
|
||||
messageHandler.detach();
|
||||
|
||||
// Connect to server, specifically send a RequestServerInfo
|
||||
connectServer();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Websocket callback function.
|
||||
void Client::callbackFunction(const ix::WebSocketMessagePtr& msg) {
|
||||
// If a message is received to the websocket, pass it to the message handler and notify to stop waiting.
|
||||
if (msg->type == ix::WebSocketMessageType::Message)
|
||||
{
|
||||
// Mutex lock this scope.
|
||||
std::lock_guard<std::mutex> lock{msgMx};
|
||||
// Push message.
|
||||
q.push(msg->str);
|
||||
// Notify conditional variable to stop waiting.
|
||||
cond.notify_one();
|
||||
}
|
||||
|
||||
// Handle websocket errors.
|
||||
if (msg->type == ix::WebSocketMessageType::Error)
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss << "Error: " << msg->errorInfo.reason << std::endl;
|
||||
ss << "#retries: " << msg->errorInfo.retries << std::endl;
|
||||
ss << "Wait time(ms): " << msg->errorInfo.wait_time << std::endl;
|
||||
ss << "HTTP Status: " << msg->errorInfo.http_status << std::endl;
|
||||
std::cout << ss.str() << std::endl;
|
||||
}
|
||||
|
||||
// Set atomic variable that websocket is connected once it is open.
|
||||
if (msg->type == ix::WebSocketMessageType::Open) {
|
||||
wsConnected = 1;
|
||||
condWs.notify_all();
|
||||
}
|
||||
|
||||
// Set atomic variable that websocket is not connected if socket closes.
|
||||
if (msg->type == ix::WebSocketMessageType::Close) {
|
||||
wsConnected = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// Function to start scanning in the server.
|
||||
void Client::startScan() {
|
||||
// Mutex lock scope.
|
||||
std::lock_guard<std::mutex> lock{msgMx};
|
||||
|
||||
// Get a request class from message handling header.
|
||||
mhl::Requests req;
|
||||
// Set the ID of message according to the message enum in message handling header.
|
||||
req.startScanning.Id = static_cast<unsigned int>(mhl::MessageTypes::StartScanning);
|
||||
// Set message type for the handler to recognize what message it is.
|
||||
messageHandler.messageType = mhl::MessageTypes::StartScanning;
|
||||
|
||||
// Convert the returned handled request message class to json.
|
||||
json j = json::array({ messageHandler.handleClientRequest(req) });
|
||||
std::cout << j << std::endl;
|
||||
|
||||
// Start a thread that sends the message.
|
||||
std::thread sendHandler(&Client::sendMessage, this, j, messageHandler.messageType);
|
||||
sendHandler.detach();
|
||||
}
|
||||
|
||||
// Function to stop scanning, same as before but different type.
|
||||
void Client::stopScan() {
|
||||
std::lock_guard<std::mutex> lock{msgMx};
|
||||
|
||||
mhl::Requests req;
|
||||
req.stopScanning.Id = static_cast<unsigned int>(mhl::MessageTypes::StopScanning);
|
||||
messageHandler.messageType = mhl::MessageTypes::StopScanning;
|
||||
|
||||
json j = json::array({ messageHandler.handleClientRequest(req) });
|
||||
std::cout << j << std::endl;
|
||||
|
||||
std::thread sendHandler(&Client::sendMessage, this, j, messageHandler.messageType);
|
||||
sendHandler.detach();
|
||||
}
|
||||
|
||||
// Function to get device list, same as before but different type.
|
||||
void Client::requestDeviceList() {
|
||||
std::lock_guard<std::mutex> lock{msgMx};
|
||||
|
||||
mhl::Requests req;
|
||||
req.stopScanning.Id = static_cast<unsigned int>(mhl::MessageTypes::RequestDeviceList);
|
||||
messageHandler.messageType = mhl::MessageTypes::RequestDeviceList;
|
||||
|
||||
json j = json::array({ messageHandler.handleClientRequest(req) });
|
||||
std::cout << j << std::endl;
|
||||
|
||||
std::thread sendHandler(&Client::sendMessage, this, j, messageHandler.messageType);
|
||||
sendHandler.detach();
|
||||
}
|
||||
|
||||
// Function to send RequestServerInfo, same as before but different type.
|
||||
void Client::connectServer() {
|
||||
std::lock_guard<std::mutex> lock{msgMx};
|
||||
|
||||
mhl::Requests req;
|
||||
req.requestServerInfo.Id = static_cast<unsigned int>(mhl::MessageTypes::RequestServerInfo);
|
||||
req.requestServerInfo.ClientName = "Testing";
|
||||
req.requestServerInfo.MessageVersion = 3;
|
||||
messageHandler.messageType = mhl::MessageTypes::RequestServerInfo;
|
||||
|
||||
json j = json::array({ messageHandler.handleClientRequest(req) });
|
||||
std::cout << j << std::endl;
|
||||
|
||||
std::thread sendHandler(&Client::sendMessage, this, j, messageHandler.messageType);
|
||||
sendHandler.detach();
|
||||
}
|
||||
|
||||
// Function that actually sends the message.
|
||||
void Client::sendMessage(json msg, mhl::MessageTypes mType) {
|
||||
// First check whether a connection process is started.
|
||||
if (!isConnecting && !wsConnected) {
|
||||
std::cout << "Client is not connected and not started, start before sending a message" << std::endl;
|
||||
return;
|
||||
}
|
||||
// If started, wait for the socket to connect first.
|
||||
if (!wsConnected && isConnecting) {
|
||||
std::unique_lock<std::mutex> lock{msgMx};
|
||||
DEBUG_MSG("Waiting for socket to connect");
|
||||
auto wsConnStatus = [this]() {return wsConnected == 1; };
|
||||
condWs.wait(lock, wsConnStatus);
|
||||
std::cout << "Connected to socket" << std::endl;
|
||||
//webSocket.send(msg.dump());
|
||||
}
|
||||
// Once socket is connected, either wait for client to connect, or send a message if the message type
|
||||
// is a request server info, since this is our client connection message.
|
||||
if (!clientConnected && isConnecting) {
|
||||
std::cout << "Waiting for client to connect" << std::endl;
|
||||
if (mType == mhl::MessageTypes::RequestServerInfo) {
|
||||
webSocket.send(msg.dump());
|
||||
if (logging) logInfo.logSentMessage("RequestServerInfo", static_cast<unsigned int>(mType));
|
||||
std::cout << "Started connection to client" << std::endl;
|
||||
return;
|
||||
}
|
||||
std::unique_lock<std::mutex> lock{msgMx};
|
||||
auto clientConnStatus = [this]() {return clientConnected == 1; };
|
||||
condClient.wait(lock, clientConnStatus);
|
||||
std::cout << "Connected to client" << std::endl;
|
||||
webSocket.send(msg.dump());
|
||||
}
|
||||
// If everything is connected, simply send message and log request if enabled.
|
||||
else if (wsConnected && clientConnected) webSocket.send(msg.dump());
|
||||
if (logging) {
|
||||
auto result = std::find_if(
|
||||
messageHandler.messageMap.begin(),
|
||||
messageHandler.messageMap.end(),
|
||||
[mType](std::pair<const mhl::MessageTypes, std::basic_string<char> > mo) {return mo.first == mType; });
|
||||
std::string msgTypeText = result->second;
|
||||
logInfo.logSentMessage(msgTypeText, static_cast<unsigned int>(mType));
|
||||
}
|
||||
}
|
||||
|
||||
// This takes the device data from message handler and puts it in to main device class
|
||||
// for user access.
|
||||
void Client::updateDevices() {
|
||||
std::vector<DeviceClass> tempDeviceVec;
|
||||
// Iterate through available devices.
|
||||
for (auto& el : messageHandler.deviceList.Devices) {
|
||||
DeviceClass tempDevice;
|
||||
// Set the appropriate class variables.
|
||||
tempDevice.deviceID = el.DeviceIndex;
|
||||
tempDevice.deviceName = el.DeviceName;
|
||||
tempDevice.displayName = el.DeviceDisplayName;
|
||||
if (el.DeviceMessages.size() > 0) {
|
||||
for (auto& el2 : el.DeviceMessages) tempDevice.commandTypes.push_back(el2.CmdType);
|
||||
}
|
||||
// Push back the device in vector.
|
||||
tempDeviceVec.push_back(tempDevice);
|
||||
}
|
||||
devices = tempDeviceVec;
|
||||
}
|
||||
|
||||
// Mutex locked function to provide the user with available devices.
|
||||
std::vector<DeviceClass> Client::getDevices() {
|
||||
std::lock_guard<std::mutex> lock{msgMx};
|
||||
return devices;
|
||||
}
|
||||
|
||||
SensorClass Client::getSensors() {
|
||||
std::lock_guard<std::mutex> lock{msgMx};
|
||||
return sensorData;
|
||||
}
|
||||
|
||||
int Client::findDevice(DeviceClass dev) {
|
||||
for (int i = 0; i < messageHandler.deviceList.Devices.size(); i++)
|
||||
if (messageHandler.deviceList.Devices[i].DeviceIndex == dev.deviceID)
|
||||
return i;
|
||||
return -1;
|
||||
}
|
||||
|
||||
void Client::stopDevice(DeviceClass dev) {
|
||||
std::lock_guard<std::mutex> lock{msgMx};
|
||||
|
||||
mhl::Requests req;
|
||||
req.stopDeviceCmd.Id = static_cast<unsigned int>(mhl::MessageTypes::StopDeviceCmd);
|
||||
req.stopDeviceCmd.DeviceIndex = dev.deviceID;
|
||||
|
||||
messageHandler.messageType = mhl::MessageTypes::StopDeviceCmd;
|
||||
|
||||
json j = json::array({ messageHandler.handleClientRequest(req) });
|
||||
std::cout << j << std::endl;
|
||||
|
||||
std::thread sendHandler(&Client::sendMessage, this, j, messageHandler.messageType);
|
||||
sendHandler.detach();
|
||||
}
|
||||
|
||||
void Client::stopAllDevices() {
|
||||
std::lock_guard<std::mutex> lock{msgMx};
|
||||
|
||||
mhl::Requests req;
|
||||
req.stopDeviceCmd.Id = static_cast<unsigned int>(mhl::MessageTypes::StopAllDevices);
|
||||
|
||||
messageHandler.messageType = mhl::MessageTypes::StopAllDevices;
|
||||
|
||||
json j = json::array({ messageHandler.handleClientRequest(req) });
|
||||
std::cout << j << std::endl;
|
||||
|
||||
std::thread sendHandler(&Client::sendMessage, this, j, messageHandler.messageType);
|
||||
sendHandler.detach();
|
||||
}
|
||||
|
||||
void Client::sendScalar(DeviceClass dev, double str) {
|
||||
std::lock_guard<std::mutex> lock{msgMx};
|
||||
int idx = findDevice(dev);
|
||||
if (idx > -1) {
|
||||
mhl::Requests req;
|
||||
|
||||
for (auto& el1 : messageHandler.deviceList.Devices[idx].DeviceMessages) {
|
||||
std::string testScalar = "ScalarCmd";
|
||||
if (!el1.CmdType.compare(testScalar)) {
|
||||
req.scalarCmd.DeviceIndex = messageHandler.deviceList.Devices[idx].DeviceIndex;
|
||||
req.scalarCmd.Id = static_cast<unsigned int>(mhl::MessageTypes::ScalarCmd);
|
||||
int i = 0;
|
||||
for (auto& el2: el1.DeviceCmdAttributes) {
|
||||
Scalar sc;
|
||||
sc.ActuatorType = el2.ActuatorType;
|
||||
sc.ScalarVal = str;
|
||||
sc.Index = i;
|
||||
req.scalarCmd.Scalars.push_back(sc);
|
||||
i++;
|
||||
}
|
||||
messageHandler.messageType = mhl::MessageTypes::ScalarCmd;
|
||||
|
||||
json j = json::array({ messageHandler.handleClientRequest(req) });
|
||||
std::cout << j << std::endl;
|
||||
|
||||
std::thread sendHandler(&Client::sendMessage, this, j, messageHandler.messageType);
|
||||
sendHandler.detach();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Client::sensorRead(DeviceClass dev, int senIndex) {
|
||||
std::lock_guard<std::mutex> lock{msgMx};
|
||||
int idx = findDevice(dev);
|
||||
if (idx > -1) {
|
||||
mhl::Requests req;
|
||||
|
||||
for (auto& el1 : messageHandler.deviceList.Devices[idx].DeviceMessages) {
|
||||
std::string testSensor = "SensorReadCmd";
|
||||
if (!el1.CmdType.compare(testSensor)) {
|
||||
req.sensorReadCmd.DeviceIndex = messageHandler.deviceList.Devices[idx].DeviceIndex;
|
||||
req.sensorReadCmd.Id = static_cast<unsigned int>(mhl::MessageTypes::SensorReadCmd);
|
||||
req.sensorReadCmd.SensorIndex = senIndex;
|
||||
req.sensorReadCmd.SensorType = el1.DeviceCmdAttributes[senIndex].SensorType;
|
||||
int i = 0;
|
||||
messageHandler.messageType = mhl::MessageTypes::SensorReadCmd;
|
||||
|
||||
json j = json::array({ messageHandler.handleClientRequest(req) });
|
||||
std::cout << j << std::endl;
|
||||
|
||||
std::thread sendHandler(&Client::sendMessage, this, j, messageHandler.messageType);
|
||||
sendHandler.detach();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Client::sensorSubscribe(DeviceClass dev, int senIndex) {
|
||||
std::lock_guard<std::mutex> lock{msgMx};
|
||||
int idx = findDevice(dev);
|
||||
if (idx > -1) {
|
||||
mhl::Requests req;
|
||||
|
||||
for (auto& el1 : messageHandler.deviceList.Devices[idx].DeviceMessages) {
|
||||
std::string testSensor = "SensorReadCmd";
|
||||
if (!el1.CmdType.compare(testSensor)) {
|
||||
req.sensorSubscribeCmd.DeviceIndex = messageHandler.deviceList.Devices[idx].DeviceIndex;
|
||||
req.sensorSubscribeCmd.Id = static_cast<unsigned int>(mhl::MessageTypes::SensorSubscribeCmd);
|
||||
req.sensorSubscribeCmd.SensorIndex = senIndex;
|
||||
req.sensorSubscribeCmd.SensorType = el1.DeviceCmdAttributes[senIndex].SensorType;
|
||||
int i = 0;
|
||||
messageHandler.messageType = mhl::MessageTypes::SensorSubscribeCmd;
|
||||
|
||||
json j = json::array({ messageHandler.handleClientRequest(req) });
|
||||
std::cout << j << std::endl;
|
||||
|
||||
std::thread sendHandler(&Client::sendMessage, this, j, messageHandler.messageType);
|
||||
sendHandler.detach();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Client::sensorUnsubscribe(DeviceClass dev, int senIndex) {
|
||||
std::lock_guard<std::mutex> lock{msgMx};
|
||||
int idx = findDevice(dev);
|
||||
if (idx > -1) {
|
||||
mhl::Requests req;
|
||||
|
||||
for (auto& el1 : messageHandler.deviceList.Devices[idx].DeviceMessages) {
|
||||
std::string testSensor = "SensorReadCmd";
|
||||
if (!el1.CmdType.compare(testSensor)) {
|
||||
req.sensorUnsubscribeCmd.DeviceIndex = messageHandler.deviceList.Devices[idx].DeviceIndex;
|
||||
req.sensorUnsubscribeCmd.Id = static_cast<unsigned int>(mhl::MessageTypes::SensorUnsubscribeCmd);
|
||||
req.sensorUnsubscribeCmd.SensorIndex = senIndex;
|
||||
req.sensorUnsubscribeCmd.SensorType = el1.DeviceCmdAttributes[senIndex].SensorType;
|
||||
int i = 0;
|
||||
messageHandler.messageType = mhl::MessageTypes::SensorUnsubscribeCmd;
|
||||
|
||||
json j = json::array({ messageHandler.handleClientRequest(req) });
|
||||
std::cout << j << std::endl;
|
||||
|
||||
std::thread sendHandler(&Client::sendMessage, this, j, messageHandler.messageType);
|
||||
sendHandler.detach();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Message handling function.
|
||||
// TODO: add client disconnect which stops this thread too.
|
||||
void Client::messageHandling() {
|
||||
// Start infinite loop.
|
||||
while (1) {
|
||||
std::unique_lock<std::mutex> lock{msgMx};
|
||||
|
||||
// A lambda that waits to receive messages in the queue.
|
||||
cond.wait(
|
||||
lock,
|
||||
[this] {
|
||||
return !q.empty();
|
||||
}
|
||||
);
|
||||
|
||||
// If received, grab the message and pop it out.
|
||||
std::string value = q.front();
|
||||
q.pop();
|
||||
|
||||
// Handle the message.
|
||||
json j = json::parse(value);
|
||||
// Iterate through messages since server can send array.
|
||||
for (auto& el : j.items()) {
|
||||
// Pass the message to actual handler.
|
||||
messageHandler.handleServerMessage(el.value());
|
||||
|
||||
// If server info received, it means client is connected so set the connection atomic variables
|
||||
// and notify all send threads that they are good to go.
|
||||
if (messageHandler.messageType == mhl::MessageTypes::ServerInfo) {
|
||||
isConnecting = 0;
|
||||
clientConnected = 1;
|
||||
condClient.notify_all();
|
||||
}
|
||||
// If a device updated message, make sure to update the devices for the user.
|
||||
if (messageHandler.messageType == mhl::MessageTypes::DeviceAdded ||
|
||||
messageHandler.messageType == mhl::MessageTypes::DeviceList ||
|
||||
messageHandler.messageType == mhl::MessageTypes::DeviceRemoved) updateDevices();
|
||||
|
||||
if (messageHandler.messageType == mhl::MessageTypes::SensorReading) sensorData = messageHandler.sensorReading;
|
||||
|
||||
// Log if logging is enabled.
|
||||
if (logging)
|
||||
logInfo.logReceivedMessage(el.value().begin().key(), static_cast<unsigned int>(messageHandler.messageType));
|
||||
|
||||
// Callback function for the user.
|
||||
messageCallback(messageHandler);
|
||||
}
|
||||
|
||||
lock.unlock();
|
||||
|
||||
std::cout << "[subscriber] Received " << value << std::endl;
|
||||
}
|
||||
}
|
20
third_party/open-bp-cpp/source/log.cpp
vendored
Normal file
20
third_party/open-bp-cpp/source/log.cpp
vendored
Normal file
|
@ -0,0 +1,20 @@
|
|||
#include "../include/log.h"
|
||||
|
||||
void Logger::init(std::string filename) {
|
||||
logFile.open(filename, std::fstream::out);
|
||||
}
|
||||
|
||||
void Logger::logSentMessage(std::string rqType, unsigned int id) {
|
||||
end = std::chrono::system_clock::now();
|
||||
std::chrono::duration<double> elapsed_seconds = end - start;
|
||||
RequestQueue tempQ;
|
||||
tempQ.id = id;
|
||||
tempQ.requestType = rqType;
|
||||
rQueue.push_back(tempQ);
|
||||
|
||||
logFile << elapsed_seconds.count() << " s, Request type sent: " << rqType << ", ID: " << id << std::endl;
|
||||
}
|
||||
|
||||
void Logger::logReceivedMessage(std::string repType, unsigned int id) {
|
||||
//end = std::chrono::system_clock::now();
|
||||
}
|
100
third_party/open-bp-cpp/source/messageHandler.cpp
vendored
Normal file
100
third_party/open-bp-cpp/source/messageHandler.cpp
vendored
Normal file
|
@ -0,0 +1,100 @@
|
|||
#include "../include/messageHandler.h"
|
||||
|
||||
namespace mhl {
|
||||
// Function that handles messages received from server.
|
||||
void Messages::handleServerMessage(json& msg) {
|
||||
// Grab the string of message type and find it in the map.
|
||||
auto msgType = msg.begin().key();
|
||||
auto result = std::find_if(
|
||||
messageMap.begin(),
|
||||
messageMap.end(),
|
||||
[msgType](std::pair<const mhl::MessageTypes, std::basic_string<char> > mo) {return mo.second == msgType; });
|
||||
auto msgEnumType = result->first;
|
||||
|
||||
int i = 0;
|
||||
// Switch that converts message to class.
|
||||
switch (msgEnumType) {
|
||||
case mhl::MessageTypes::Ok:
|
||||
messageType = mhl::MessageTypes::Ok;
|
||||
ok = msg.get<msg::Ok>();
|
||||
break;
|
||||
case mhl::MessageTypes::Error:
|
||||
messageType = mhl::MessageTypes::Error;
|
||||
error = msg.get<msg::Error>();
|
||||
break;
|
||||
case mhl::MessageTypes::ServerInfo:
|
||||
// Set message type and convert to class from json.
|
||||
std::cout << "Server info!" << std::endl;
|
||||
messageType = mhl::MessageTypes::ServerInfo;
|
||||
serverInfo = msg.get<msg::ServerInfo>();
|
||||
break;
|
||||
case mhl::MessageTypes::ScanningFinished:
|
||||
break;
|
||||
case mhl::MessageTypes::DeviceList:
|
||||
std::cout << "Device list!" << std::endl;
|
||||
messageType = mhl::MessageTypes::DeviceList;
|
||||
deviceList = msg.get<msg::DeviceList>();
|
||||
break;
|
||||
case mhl::MessageTypes::DeviceAdded:
|
||||
deviceAdded = msg.get<msg::DeviceAdded>();
|
||||
messageType = mhl::MessageTypes::DeviceAdded;
|
||||
// Push back to message handler class device list the newly added device.
|
||||
deviceList.Devices.push_back(deviceAdded.device);
|
||||
break;
|
||||
case mhl::MessageTypes::DeviceRemoved:
|
||||
deviceRemoved = msg.get<msg::DeviceRemoved>();
|
||||
messageType = mhl::MessageTypes::DeviceRemoved;
|
||||
// Erase device from message handler class device list.
|
||||
for (auto& el : deviceList.Devices) {
|
||||
if (deviceRemoved.DeviceIndex == el.DeviceIndex) {
|
||||
deviceList.Devices.erase(deviceList.Devices.begin() + i);
|
||||
break;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
break;
|
||||
case mhl::MessageTypes::SensorReading:
|
||||
sensorReading = msg.get<msg::SensorReading>();
|
||||
messageType = mhl::MessageTypes::SensorReading;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Convert client request classes to json.
|
||||
json Messages::handleClientRequest(Requests req) {
|
||||
json j;
|
||||
switch (messageType) {
|
||||
case mhl::MessageTypes::RequestServerInfo:
|
||||
j = req.requestServerInfo;
|
||||
break;
|
||||
case mhl::MessageTypes::RequestDeviceList:
|
||||
j = req.requestDeviceList;
|
||||
break;
|
||||
case mhl::MessageTypes::StartScanning:
|
||||
j = req.startScanning;
|
||||
break;
|
||||
case mhl::MessageTypes::StopScanning:
|
||||
j = req.stopScanning;
|
||||
break;
|
||||
case mhl::MessageTypes::StopDeviceCmd:
|
||||
j = req.stopDeviceCmd;
|
||||
break;
|
||||
case mhl::MessageTypes::StopAllDevices:
|
||||
j = req.stopAllDevices;
|
||||
break;
|
||||
case mhl::MessageTypes::ScalarCmd:
|
||||
j = req.scalarCmd;
|
||||
break;
|
||||
case mhl::MessageTypes::SensorReadCmd:
|
||||
j = req.sensorReadCmd;
|
||||
break;
|
||||
case mhl::MessageTypes::SensorSubscribeCmd:
|
||||
j = req.sensorSubscribeCmd;
|
||||
break;
|
||||
case mhl::MessageTypes::SensorUnsubscribeCmd:
|
||||
j = req.sensorUnsubscribeCmd;
|
||||
break;
|
||||
}
|
||||
return j;
|
||||
}
|
||||
}
|
218
third_party/open-bp-cpp/source/messages.cpp
vendored
Normal file
218
third_party/open-bp-cpp/source/messages.cpp
vendored
Normal file
|
@ -0,0 +1,218 @@
|
|||
#include "../include/messages.h"
|
||||
|
||||
// Function definitions for json conversions.
|
||||
namespace msg {
|
||||
void to_json(json& j, const RequestServerInfo& k) {
|
||||
j["RequestServerInfo"] = { {"Id", k.Id}, {"ClientName", k.ClientName}, {"MessageVersion", k.MessageVersion} };
|
||||
}
|
||||
|
||||
void to_json(json& j, const StartScanning& k) {
|
||||
j["StartScanning"] = { {"Id", k.Id} };
|
||||
}
|
||||
|
||||
void to_json(json& j, const StopScanning& k) {
|
||||
j["StopScanning"] = { {"Id", k.Id} };
|
||||
}
|
||||
|
||||
void to_json(json& j, const RequestDeviceList& k) {
|
||||
j["RequestDeviceList"] = { {"Id", k.Id} };
|
||||
}
|
||||
|
||||
void to_json(json& j, const StopDeviceCmd& k) {
|
||||
j["StopDeviceCmd"] = { {"Id", k.Id}, {"DeviceIndex", k.DeviceIndex} };
|
||||
}
|
||||
|
||||
void to_json(json& j, const StopAllDevices& k) {
|
||||
j["StopAllDevices"] = { {"Id", k.Id} };
|
||||
}
|
||||
|
||||
void to_json(json& j, const ScalarCmd& k) {
|
||||
j["ScalarCmd"] = { {"Id", k.Id}, {"DeviceIndex", k.DeviceIndex} };
|
||||
j["ScalarCmd"]["Scalars"] = json::array();
|
||||
|
||||
for (auto& vec : k.Scalars) {
|
||||
json jTemp = { { "Index", vec.Index }, { "Scalar", vec.ScalarVal }, { "ActuatorType", vec.ActuatorType } };
|
||||
j["ScalarCmd"]["Scalars"].insert(j["ScalarCmd"]["Scalars"].end(), jTemp);
|
||||
}
|
||||
}
|
||||
|
||||
void to_json(json& j, const SensorReadCmd& k) {
|
||||
j["SensorReadCmd"] = { {"Id", k.Id}, {"DeviceIndex", k.DeviceIndex}, {"SensorIndex", k.SensorIndex}, {"SensorType", k.SensorType}};
|
||||
}
|
||||
|
||||
void to_json(json& j, const SensorSubscribeCmd& k) {
|
||||
j["SensorSubscribeCmd"] = { {"Id", k.Id}, {"DeviceIndex", k.DeviceIndex}, {"SensorIndex", k.SensorIndex}, {"SensorType", k.SensorType} };
|
||||
}
|
||||
|
||||
void to_json(json& j, const SensorUnsubscribeCmd& k) {
|
||||
j["SensorUnsubscribeCmd"] = { {"Id", k.Id}, {"DeviceIndex", k.DeviceIndex}, {"SensorIndex", k.SensorIndex}, {"SensorType", k.SensorType} };
|
||||
}
|
||||
|
||||
void from_json(const json& j, ServerInfo& k) {
|
||||
json jTemp;
|
||||
j.at("ServerInfo").get_to(jTemp);
|
||||
jTemp.at("Id").get_to(k.Id);
|
||||
jTemp.at("ServerName").get_to(k.ServerName);
|
||||
jTemp.at("MessageVersion").get_to(k.MessageVersion);
|
||||
jTemp.at("MaxPingTime").get_to(k.MaxPingTime);
|
||||
}
|
||||
|
||||
void from_json(const json& j, Ok& k) {
|
||||
json jTemp;
|
||||
j.at("Ok").get_to(jTemp);
|
||||
jTemp.at("Id").get_to(k.Id);
|
||||
}
|
||||
|
||||
void from_json(const json& j, Error& k) {
|
||||
json jTemp;
|
||||
j.at("Error").get_to(jTemp);
|
||||
jTemp.at("Id").get_to(k.Id);
|
||||
jTemp.at("ErrorCode").get_to(k.ErrorCode);
|
||||
jTemp.at("ErrorMessage").get_to(k.ErrorMessage);
|
||||
}
|
||||
|
||||
void from_json(const json& j, DeviceRemoved& k) {
|
||||
json jTemp;
|
||||
j.at("DeviceRemoved").get_to(jTemp);
|
||||
jTemp.at("Id").get_to(k.Id);
|
||||
jTemp.at("DeviceIndex").get_to(k.DeviceIndex);
|
||||
}
|
||||
|
||||
// The device conversion functions are slightly more complicated since they have some
|
||||
// nested objects and arrays, but otherwise it is simply parsing, just a lot of it.
|
||||
void from_json(const json& j, DeviceList& k) {
|
||||
json jTemp;
|
||||
j.at("DeviceList").get_to(jTemp);
|
||||
jTemp.at("Id").get_to(k.Id);
|
||||
|
||||
if (jTemp["Devices"].size() > 0) {
|
||||
for (auto& el : jTemp["Devices"].items()) {
|
||||
Device tempD;
|
||||
//std::cout << el.value() << std::endl;
|
||||
auto test = el.value().contains("DeviceMessageTimingGap");
|
||||
if (el.value().contains("DeviceName")) tempD.DeviceName = el.value()["DeviceName"];
|
||||
|
||||
if (el.value().contains("DeviceIndex")) tempD.DeviceIndex = el.value()["DeviceIndex"];
|
||||
|
||||
if (el.value().contains("DeviceMessageTimingGap")) tempD.DeviceMessageTimingGap = el.value()["DeviceMessageTimingGap"];
|
||||
|
||||
if (el.value().contains("DeviceDisplayName")) tempD.DeviceName = el.value()["DeviceDisplayName"];
|
||||
|
||||
if (el.value().contains("DeviceMessages")) {
|
||||
json jTemp2;
|
||||
jTemp2 = el.value()["DeviceMessages"];
|
||||
|
||||
for (auto& el2 : jTemp2.items()) {
|
||||
DeviceCmd tempCmd;
|
||||
tempCmd.CmdType = el2.key();
|
||||
|
||||
if (!el2.key().compare("StopDeviceCmd")) {
|
||||
// Do something, not sure what yet
|
||||
continue;
|
||||
}
|
||||
|
||||
for (auto& el3 : el2.value().items()) {
|
||||
DeviceCmdAttr tempAttr;
|
||||
//std::cout << el3.value() << std::endl;
|
||||
|
||||
if (el3.value().contains("FeatureDescriptor")) tempAttr.FeatureDescriptor = el3.value()["FeatureDescriptor"];
|
||||
|
||||
if (el3.value().contains("StepCount")) tempAttr.StepCount = el3.value()["StepCount"];
|
||||
|
||||
if (el3.value().contains("ActuatorType")) tempAttr.ActuatorType = el3.value()["ActuatorType"];
|
||||
|
||||
if (el3.value().contains("SensorType")) tempAttr.SensorType = el3.value()["SensorType"];
|
||||
|
||||
if (el3.value().contains("SensorRange")) {
|
||||
//std::cout << el3.value()["SensorRange"] << std::endl;
|
||||
for (auto& el4 : el3.value()["SensorRange"].items()) {
|
||||
tempAttr.SensorRange.push_back(el4.value()[0]);
|
||||
tempAttr.SensorRange.push_back(el4.value()[1]);
|
||||
}
|
||||
//tempCmd.SensorRange.push_back(el2.value()["SensorRange"]);
|
||||
}
|
||||
|
||||
//if (el2.value().contains("Endpoints")) tempCmd.Endpoints = el2.value()["Endpoints"];
|
||||
//std::cout << el2.key() << std::endl;
|
||||
//std::cout << el2.value() << std::endl;
|
||||
tempCmd.DeviceCmdAttributes.push_back(tempAttr);
|
||||
}
|
||||
|
||||
tempD.DeviceMessages.push_back(tempCmd);
|
||||
}
|
||||
}
|
||||
k.Devices.push_back(tempD);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void from_json(const json& j, DeviceAdded& k) {
|
||||
json jTemp;
|
||||
j.at("DeviceAdded").get_to(jTemp);
|
||||
jTemp.at("Id").get_to(k.Id);
|
||||
|
||||
Device tempD;
|
||||
|
||||
if (jTemp.contains("DeviceName")) k.device.DeviceName = jTemp["DeviceName"];
|
||||
|
||||
if (jTemp.contains("DeviceIndex")) k.device.DeviceIndex = jTemp["DeviceIndex"];
|
||||
|
||||
if (jTemp.contains("DeviceMessageTimingGap")) k.device.DeviceMessageTimingGap = jTemp["DeviceMessageTimingGap"];
|
||||
|
||||
if (jTemp.contains("DeviceDisplayName")) k.device.DeviceName = jTemp["DeviceDisplayName"];
|
||||
|
||||
if (jTemp.contains("DeviceMessages")) {
|
||||
json jTemp2;
|
||||
jTemp2 = jTemp["DeviceMessages"];
|
||||
|
||||
for (auto& el2 : jTemp2.items()) {
|
||||
DeviceCmd tempCmd;
|
||||
tempCmd.CmdType = el2.key();
|
||||
|
||||
if (!el2.key().compare("StopDeviceCmd")) {
|
||||
// Do something, not sure what yet
|
||||
continue;
|
||||
}
|
||||
|
||||
for (auto& el3 : el2.value().items()) {
|
||||
DeviceCmdAttr tempAttr;
|
||||
//std::cout << el3.value() << std::endl;
|
||||
|
||||
if (el3.value().contains("FeatureDescriptor")) tempAttr.FeatureDescriptor = el3.value()["FeatureDescriptor"];
|
||||
|
||||
if (el3.value().contains("StepCount")) tempAttr.StepCount = el3.value()["StepCount"];
|
||||
|
||||
if (el3.value().contains("ActuatorType")) tempAttr.ActuatorType = el3.value()["ActuatorType"];
|
||||
|
||||
if (el3.value().contains("SensorType")) tempAttr.SensorType = el3.value()["SensorType"];
|
||||
|
||||
if (el3.value().contains("SensorRange")) {
|
||||
//std::cout << el3.value()["SensorRange"] << std::endl;
|
||||
for (auto& el4 : el3.value()["SensorRange"].items()) {
|
||||
tempAttr.SensorRange.push_back(el4.value()[0]);
|
||||
tempAttr.SensorRange.push_back(el4.value()[1]);
|
||||
}
|
||||
}
|
||||
|
||||
//if (el2.value().contains("Endpoints")) tempCmd.Endpoints = el2.value()["Endpoints"];
|
||||
//std::cout << el2.key() << std::endl;
|
||||
//std::cout << el2.value() << std::endl;
|
||||
tempCmd.DeviceCmdAttributes.push_back(tempAttr);
|
||||
}
|
||||
|
||||
k.device.DeviceMessages.push_back(tempCmd);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void from_json(const json& j, SensorReading& k) {
|
||||
json jTemp;
|
||||
j.at("SensorReading").get_to(jTemp);
|
||||
jTemp.at("Id").get_to(k.Id);
|
||||
jTemp.at("DeviceIndex").get_to(k.DeviceIndex);
|
||||
jTemp.at("SensorIndex").get_to(k.SensorIndex);
|
||||
jTemp.at("SensorType").get_to(k.SensorType);
|
||||
for (auto& el : jTemp["Data"].items())
|
||||
k.Data.push_back(el.value());
|
||||
}
|
||||
}
|
Loading…
Add table
Reference in a new issue