diff --git a/CMakeLists.txt b/CMakeLists.txt index 3ab57140..26a57ec5 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -12,21 +12,21 @@ # See the License for the specific language governing permissions and # limitations under the License. cmake_minimum_required (VERSION 2.8.0) -project(mujincontrollerclient) +project(mujinwebstackclient) set( CMAKE_ALLOW_LOOSE_LOOP_CONSTRUCTS TRUE ) # Define here the needed parameters # make sure to change the version in docs/Makefile -set (MUJINCLIENT_VERSION_MAJOR 0) -set (MUJINCLIENT_VERSION_MINOR 65) -set (MUJINCLIENT_VERSION_PATCH 0) -set (MUJINCLIENT_VERSION ${MUJINCLIENT_VERSION_MAJOR}.${MUJINCLIENT_VERSION_MINOR}.${MUJINCLIENT_VERSION_PATCH}) -set (MUJINCLIENT_SOVERSION ${MUJINCLIENT_VERSION_MAJOR}.${MUJINCLIENT_VERSION_MINOR}) -set (CLIENT_SOVERSION ${MUJINCLIENT_VERSION_MAJOR}.${MUJINCLIENT_VERSION_MINOR}) -message(STATUS "Compiling MUJIN Controller Client C++ Version ${MUJINCLIENT_VERSION}, soversion=${CLIENT_SOVERSION}") +set (MUJINWEBSTACKCLIENT_VERSION_MAJOR 0) +set (MUJINWEBSTACKCLIENT_VERSION_MINOR 65) +set (MUJINWEBSTACKCLIENT_VERSION_PATCH 0) +set (MUJINWEBSTACKCLIENT_VERSION ${MUJINWEBSTACKCLIENT_VERSION_MAJOR}.${MUJINWEBSTACKCLIENT_VERSION_MINOR}.${MUJINWEBSTACKCLIENT_VERSION_PATCH}) +set (MUJINWEBSTACKCLIENT_SOVERSION ${MUJINWEBSTACKCLIENT_VERSION_MAJOR}.${MUJINWEBSTACKCLIENT_VERSION_MINOR}) +set (CLIENT_SOVERSION ${MUJINWEBSTACKCLIENT_VERSION_MAJOR}.${MUJINWEBSTACKCLIENT_VERSION_MINOR}) +message(STATUS "Compiling MUJIN Controller Client C++ Version ${MUJINWEBSTACKCLIENT_VERSION}, soversion=${CLIENT_SOVERSION}") -set(MUJINCLIENT_CMAKE_INSTALL_DIR "mujincontrollerclient-${MUJINCLIENT_VERSION_MAJOR}.${MUJINCLIENT_VERSION_MINOR}" CACHE STRING "Directory to install the cmake config files.") -set(MUJINCLIENT_TARGET_PROCESSOR ${CMAKE_SYSTEM_PROCESSOR} CACHE STRING "The target processor architecture to build for, this is combined with the generator toolchain") +set(MUJINWEBSTACKCLIENT_CMAKE_INSTALL_DIR "mujinwebstackclient-${MUJINWEBSTACKCLIENT_VERSION_MAJOR}.${MUJINWEBSTACKCLIENT_VERSION_MINOR}" CACHE STRING "Directory to install the cmake config files.") +set(MUJINWEBSTACKCLIENT_TARGET_PROCESSOR ${CMAKE_SYSTEM_PROCESSOR} CACHE STRING "The target processor architecture to build for, this is combined with the generator toolchain") message(STATUS "Using cmake version ${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION}.${CMAKE_PATCH_VERSION}" ) # http://www.cmake.org/cmake/help/cmake-2.6.html#policy:CMP0002 @@ -34,7 +34,7 @@ cmake_policy(SET CMP0002 NEW) # http://www.cmake.org/cmake/help/cmake-2.6.html#policy:CMP0003 cmake_policy(SET CMP0003 NEW) -option(OPT_SAMPLES "Build the samples" ON) +option(OPT_SAMPLES "Build the samples" OFF) option(OPT_BUILD_TESTS "Build the tests" OFF) option(OPT_BUILD_STATIC "Build static libraries for the client" ON) option(OPT_LOG4CXX "Use log4cxx for logging" ON) @@ -66,9 +66,12 @@ include(CheckCXXCompilerFlag) add_definitions("-DBOOST_SPIRIT_THREADSAFE") # for json parsing # have to include before boost since the boost headers can be located in a previous installed version of this library -set(MUJINCLIENT_INCLUDE_LOCAL_DIRS ${CMAKE_CURRENT_SOURCE_DIR}/include) -include_directories(${CMAKE_CURRENT_BINARY_DIR}/include) -include_directories(${MUJINCLIENT_INCLUDE_LOCAL_DIRS}) +set(MUJINWEBSTACKCLIENT_INCLUDE_LOCAL_DIRS ${CMAKE_CURRENT_SOURCE_DIR}/src/include) +include_directories( + ${CMAKE_CURRENT_SOURCE_DIR}/src/include + ${CMAKE_CURRENT_BINARY_DIR}/include +) # have to do this before every other include! +include_directories(${MUJINWEBSTACKCLIENT_INCLUDE_LOCAL_DIRS}) if( MSVC ) add_definitions(-D_CRT_SECURE_NO_WARNINGS -D_CRT_SECURE_NO_DEPRECATE) @@ -102,7 +105,7 @@ if( MSVC ) check_include_file(stdint.h HAVE_STDINT_H) if( NOT HAVE_STDINT_H ) - #install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/msvc_include/stdint.h DESTINATION include/mujinclient-${MUJINCLIENT_VERSION_MAJOR}.${MUJINCLIENT_VERSION_MINOR} COMPONENT ${COMPONENT_PREFIX}dev) + #install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/msvc_include/stdint.h DESTINATION include/mujinwebstackclient-${MUJINWEBSTACKCLIENT_VERSION_MAJOR}.${MUJINWEBSTACKCLIENT_VERSION_MINOR} COMPONENT ${COMPONENT_PREFIX}dev) #include_directories(${CMAKE_CURRENT_SOURCE_DIR}/msvc_include) endif() @@ -120,10 +123,10 @@ if( MSVC ) set(MSVC_PREFIX "vc100") set(MSVC_PREFIX2 "v100") endif() - set(MUJINCLIENT_LIBRARY_SUFFIX "${MUJINCLIENT_SOVERSION}-${MSVC_PREFIX}-mt" CACHE STRING "Suffix to append to library names") + set(MUJINWEBSTACKCLIENT_LIBRARY_SUFFIX "${MUJINWEBSTACKCLIENT_SOVERSION}-${MSVC_PREFIX}-mt" CACHE STRING "Suffix to append to library names") # install all DLLs - install(DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/msvc_binaries/${MUJINCLIENT_TARGET_PROCESSOR}/${MSVC_PREFIX}/bin/" DESTINATION bin FILES_MATCHING PATTERN "*.dll") + install(DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/msvc_binaries/${MUJINWEBSTACKCLIENT_TARGET_PROCESSOR}/${MSVC_PREFIX}/bin/" DESTINATION bin FILES_MATCHING PATTERN "*.dll") # force multi-threaded DLL boost set(Boost_USE_MULTITHREAD ON) @@ -131,27 +134,27 @@ if( MSVC ) set(Boost_USE_STATIC_RUNTIME OFF) set(Boost_CFLAGS "-DBOOST_ALL_DYN_LINK -DBOOST_ALL_NO_LIB") else() - set(MUJINCLIENT_LIBRARY_SUFFIX "${MUJINCLIENT_SOVERSION}" CACHE STRING "Suffix to append to library names") + set(MUJINWEBSTACKCLIENT_LIBRARY_SUFFIX "${MUJINWEBSTACKCLIENT_SOVERSION}" CACHE STRING "Suffix to append to library names") endif() -set(MUJINCLIENT_LINK_DIRS) +set(MUJINWEBSTACKCLIENT_LINK_DIRS) find_package(PkgConfig) -set(MUJINCLIENT_LOG4CXX 0) +set(MUJINWEBSTACKCLIENT_LOG4CXX 0) if (OPT_LOG4CXX) pkg_check_modules(LOG4CXX liblog4cxx) if (LOG4CXX_FOUND) if( MSVC ) - set(MUJINCLIENT_LOG4CXX_INCLUDE_DIRS "/I\"${LOG4CXX_INCLUDEDIR}\"") - set(MUJINCLIENT_LOG4CXX_LIB_DIRS "/LIBPATH:\"${LOG4CXX_LIBDIR}\"") + set(MUJINWEBSTACKCLIENT_LOG4CXX_INCLUDE_DIRS "/I\"${LOG4CXX_INCLUDEDIR}\"") + set(MUJINWEBSTACKCLIENT_LOG4CXX_LIB_DIRS "/LIBPATH:\"${LOG4CXX_LIBDIR}\"") else() - set(MUJINCLIENT_LOG4CXX_INCLUDE_DIRS "-I${LOG4CXX_INCLUDEDIR}") - set(MUJINCLIENT_LOG4CXX_LIB_DIRS "-L${LOG4CXX_LIBDIR}") + set(MUJINWEBSTACKCLIENT_LOG4CXX_INCLUDE_DIRS "-I${LOG4CXX_INCLUDEDIR}") + set(MUJINWEBSTACKCLIENT_LOG4CXX_LIB_DIRS "-L${LOG4CXX_LIBDIR}") endif() - set(MUJINCLIENT_LOG4CXX_LIBRARY "-l${LOG4CXX_LIBRARIES}") + set(MUJINWEBSTACKCLIENT_LOG4CXX_LIBRARY "-l${LOG4CXX_LIBRARIES}") - set(MUJINCLIENT_LINK_DIRS ${MUJINCLIENT_LINK_DIRS} ${LOG4CXX_LIBDIR}) - set(MUJINCLIENT_LOG4CXX 1) + set(MUJINWEBSTACKCLIENT_LINK_DIRS ${MUJINWEBSTACKCLIENT_LINK_DIRS} ${LOG4CXX_LIBDIR}) + set(MUJINWEBSTACKCLIENT_LOG4CXX 1) endif() endif() @@ -182,11 +185,11 @@ endif() if( Boost_FOUND ) include_directories(${Boost_INCLUDE_DIRS}) - set(MUJINCLIENT_LINK_DIRS ${MUJINCLIENT_LINK_DIRS} ${Boost_LIBRARY_DIRS}) + set(MUJINWEBSTACKCLIENT_LINK_DIRS ${MUJINWEBSTACKCLIENT_LINK_DIRS} ${Boost_LIBRARY_DIRS}) set(EXTRA_MSVC_DEPEND) # reset extra depend elseif(Boost_VERSION AND NOT "${Boost_VERSION}" STREQUAL "0") include_directories(${Boost_INCLUDE_DIRS}) - set(MUJINCLIENT_LINK_DIRS ${MUJINCLIENT_LINK_DIRS} ${Boost_LIBRARY_DIRS}) + set(MUJINWEBSTACKCLIENT_LINK_DIRS ${MUJINWEBSTACKCLIENT_LINK_DIRS} ${Boost_LIBRARY_DIRS}) set(EXTRA_MSVC_DEPEND msvc_boost) # reset extra depend elseif( MSVC ) # to facilitate compilation, visual studio libraries are included locally @@ -195,7 +198,7 @@ elseif( MSVC ) set(Boost_FOUND 1) set(Boost_INCLUDE_DIRS "${BOOST_ROOT}") set(Boost_INCLUDE_DIR "${BOOST_ROOT}") - set(Boost_LIBRARY_DIRS "${BOOST_ROOT}/${MUJINCLIENT_TARGET_PROCESSOR}/lib") + set(Boost_LIBRARY_DIRS "${BOOST_ROOT}/${MUJINWEBSTACKCLIENT_TARGET_PROCESSOR}/lib") set(Boost_REGEX_FOUND 0) set(Boost_FILESYSTEM_FOUND 1) set(Boost_IOSTREAMS_FOUND 1) @@ -227,7 +230,7 @@ elseif( MSVC ) install(FILES ${Boost_DATE_TIME_LIBRARY_DLL} ${Boost_THREAD_LIBRARY_DLL} ${Boost_SYSTEM_LIBRARY_DLL} ${Boost_FILESYSTEM_LIBRARY_DLL} ${Boost_PROGRAM_OPTIONS_LIBRARY_DLL} DESTINATION bin COMPONENT ${COMPONENT_PREFIX}dev) install(DIRECTORY "${Boost_INCLUDE_DIR}/boost" DESTINATION include COMPONENT ${COMPONENT_PREFIX}dev) include_directories(${Boost_INCLUDE_DIRS}) - set(MUJINCLIENT_LINK_DIRS ${MUJINCLIENT_LINK_DIRS} ${Boost_LIBRARY_DIRS}) + set(MUJINWEBSTACKCLIENT_LINK_DIRS ${MUJINWEBSTACKCLIENT_LINK_DIRS} ${Boost_LIBRARY_DIRS}) else() message(FATAL_ERROR "Could not find boost libraries!") endif() @@ -256,14 +259,14 @@ if( CMAKE_COMPILER_IS_GNUCC OR CMAKE_COMPILER_IS_GNUCXX ) add_definitions("-fno-strict-aliasing -Wall -Werror=shadow") endif( CMAKE_COMPILER_IS_GNUCC OR CMAKE_COMPILER_IS_GNUCXX ) -configure_file(${CMAKE_CURRENT_SOURCE_DIR}/config.h.in ${CMAKE_CURRENT_BINARY_DIR}/include/mujincontrollerclient/config.h IMMEDIATE @ONLY) +configure_file(${CMAKE_CURRENT_SOURCE_DIR}/config.h.in ${CMAKE_CURRENT_BINARY_DIR}/include/mujinwebstackclientcpp/config.h IMMEDIATE @ONLY) find_package(CURL) if( NOT CURL_FOUND ) if( MSVC ) - message(STATUS "setting local curl library from msvc_binaries/${MUJINCLIENT_TARGET_PROCESSOR}/${MSVC_PREFIX}") - set(CURL_INCLUDE_DIRS "${CMAKE_CURRENT_SOURCE_DIR}/msvc_binaries/${MUJINCLIENT_TARGET_PROCESSOR}/${MSVC_PREFIX}/include") - set(CURL_LIBRARIES "${CMAKE_CURRENT_SOURCE_DIR}/msvc_binaries/${MUJINCLIENT_TARGET_PROCESSOR}/${MSVC_PREFIX}/lib/libcurl-${MSVC_PREFIX}-mt_imp.lib") + message(STATUS "setting local curl library from msvc_binaries/${MUJINWEBSTACKCLIENT_TARGET_PROCESSOR}/${MSVC_PREFIX}") + set(CURL_INCLUDE_DIRS "${CMAKE_CURRENT_SOURCE_DIR}/msvc_binaries/${MUJINWEBSTACKCLIENT_TARGET_PROCESSOR}/${MSVC_PREFIX}/include") + set(CURL_LIBRARIES "${CMAKE_CURRENT_SOURCE_DIR}/msvc_binaries/${MUJINWEBSTACKCLIENT_TARGET_PROCESSOR}/${MSVC_PREFIX}/lib/libcurl-${MSVC_PREFIX}-mt_imp.lib") else() message(FATAL_ERROR "could not find CURL library") endif() @@ -297,10 +300,10 @@ else() endif() if (NOT libzmq_FOUND) if( MSVC ) - set(libzmq_INCLUDE_DIRS "${CMAKE_CURRENT_SOURCE_DIR}/msvc_binaries/${MUJINCLIENT_TARGET_PROCESSOR}/${MSVC_PREFIX}/include") - set(libzmq_LIBRARIES "${CMAKE_CURRENT_SOURCE_DIR}/msvc_binaries/${MUJINCLIENT_TARGET_PROCESSOR}/${MSVC_PREFIX}/lib/libzmq-${MSVC_PREFIX2}-mt-4_0_4.lib") + set(libzmq_INCLUDE_DIRS "${CMAKE_CURRENT_SOURCE_DIR}/msvc_binaries/${MUJINWEBSTACKCLIENT_TARGET_PROCESSOR}/${MSVC_PREFIX}/include") + set(libzmq_LIBRARIES "${CMAKE_CURRENT_SOURCE_DIR}/msvc_binaries/${MUJINWEBSTACKCLIENT_TARGET_PROCESSOR}/${MSVC_PREFIX}/lib/libzmq-${MSVC_PREFIX2}-mt-4_0_4.lib") if(EXISTS ${libzmq_LIBRARIES} ) - message(STATUS "setting local zmq library from msvc_binaries/${MUJINCLIENT_TARGET_PROCESSOR}/${MSVC_PREFIX}") + message(STATUS "setting local zmq library from msvc_binaries/${MUJINWEBSTACKCLIENT_TARGET_PROCESSOR}/${MSVC_PREFIX}") set(libzmq_FOUND 1) else() message(STATUS "could not find msvc zmq library ${libzmq_LIBRARIES}") @@ -311,21 +314,22 @@ endif() if (libzmq_FOUND) include_directories(${libzmq_INCLUDE_DIRS}) - set(MUJINCLIENT_LINK_DIRS ${MUJINCLIENT_LINK_DIRS} ${libzmq_LIBRARY_DIRS}) + set(MUJINWEBSTACKCLIENT_LINK_DIRS ${MUJINWEBSTACKCLIENT_LINK_DIRS} ${libzmq_LIBRARY_DIRS}) else() message(WARNING "compiling without libzmq library") endif() -file(GLOB mujin_header_files ${CMAKE_CURRENT_SOURCE_DIR}/include/mujincontrollerclient/*.h ${CMAKE_CURRENT_SOURCE_DIR}/include/mujincontrollerclient/*.hpp) -install(FILES ${mujin_header_files} ${CMAKE_CURRENT_BINARY_DIR}/include/mujincontrollerclient/config.h DESTINATION include/mujincontrollerclient) +file(GLOB mujin_header_files ${CMAKE_CURRENT_SOURCE_DIR}/src/include/mujinwebstackclientcpp/*.h ${CMAKE_CURRENT_SOURCE_DIR}/src/include/mujinwebstackclientcpp/*.hpp) +install(FILES ${mujin_header_files} DESTINATION include/mujinwebstackclientcpp) +install(FILES ${CMAKE_CURRENT_BINARY_DIR}/include/mujinwebstackclientcpp/config.h DESTINATION include/mujinwebstackclientcpp) macro(build_sample name) include_directories(${LOG4CXX_INCLUDEDIR} ${libzmq_INCLUDE_DIRS}) link_directories(${LOG4CXX_LIBDIR}) add_executable(${name} ${name}.cpp) set_target_properties(${name} PROPERTIES COMPILE_FLAGS "${Boost_CFLAGS}" LINK_FLAGS "") - add_dependencies(${name} libmujincontrollerclient) - target_link_libraries (${name} libmujincontrollerclient ${Boost_THREAD_LIBRARY} ${Boost_DATE_TIME_LIBRARY} ${Boost_PROGRAM_OPTIONS_LIBRARY} ${EXTRA_LIBRARIES} ${LOG4CXX_LIBRARIES}) + add_dependencies(${name} libmujinwebstackclient) + target_link_libraries (${name} libmujinwebstackclient ${Boost_THREAD_LIBRARY} ${Boost_DATE_TIME_LIBRARY} ${Boost_PROGRAM_OPTIONS_LIBRARY} ${EXTRA_LIBRARIES} ${LOG4CXX_LIBRARIES}) install(TARGETS ${name} DESTINATION bin) endmacro(build_sample) @@ -337,15 +341,13 @@ if( OPT_BUILD_TESTS ) add_subdirectory(test) endif() -install(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/share/mujincontrollerclient DESTINATION share) - # add make uninstall capability configure_file("${CMAKE_CURRENT_SOURCE_DIR}/cmake_uninstall.cmake.in" "${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake" IMMEDIATE @ONLY) add_custom_target(uninstall "${CMAKE_COMMAND}" -P "${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake") -set(MujinControllerClient_INCLUDE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/include") -configure_file("${CMAKE_CURRENT_SOURCE_DIR}/mujincontrollerclient-config.cmake.in" "${CMAKE_CURRENT_BINARY_DIR}/mujincontrollerclient-config.cmake" IMMEDIATE @ONLY) -configure_file("${CMAKE_CURRENT_SOURCE_DIR}/mujincontrollerclient-config-version.cmake.in" "${CMAKE_CURRENT_BINARY_DIR}/mujincontrollerclient-config-version.cmake" IMMEDIATE @ONLY) +set(Mujinwebstackclient_INCLUDE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/include") +configure_file("${CMAKE_CURRENT_SOURCE_DIR}/mujinwebstackclient-config.cmake.in" "${CMAKE_CURRENT_BINARY_DIR}/mujinwebstackclient-config.cmake" IMMEDIATE @ONLY) +configure_file("${CMAKE_CURRENT_SOURCE_DIR}/mujinwebstackclient-config-version.cmake.in" "${CMAKE_CURRENT_BINARY_DIR}/mujinwebstackclient-config-version.cmake" IMMEDIATE @ONLY) -install(FILES "${CMAKE_CURRENT_BINARY_DIR}/mujincontrollerclient-config.cmake" "${CMAKE_CURRENT_BINARY_DIR}/mujincontrollerclient-config-version.cmake" DESTINATION "lib${LIB_SUFFIX}/cmake/${MUJINCLIENT_CMAKE_INSTALL_DIR}") +install(FILES "${CMAKE_CURRENT_BINARY_DIR}/mujinwebstackclient-config.cmake" "${CMAKE_CURRENT_BINARY_DIR}/mujinwebstackclient-config-version.cmake" DESTINATION "lib${LIB_SUFFIX}/cmake/${MUJINWEBSTACKCLIENT_CMAKE_INSTALL_DIR}") diff --git a/README.md b/README.md new file mode 100644 index 00000000..749cb93c --- /dev/null +++ b/README.md @@ -0,0 +1,9 @@ +# Mujin Webstack Client Cpp + +This is the client for webstack, in C++. + +We also have a [python version](https://github.com/mujin/mujinwebstackclientpy). + +## Building? + +Assuming you have rapid json and curl, you can simply `cmake . && make` in this directory. \ No newline at end of file diff --git a/config.h.in b/config.h.in index d6da334a..c2504f2b 100644 --- a/config.h.in +++ b/config.h.in @@ -1,56 +1,56 @@ /** \file config.h \brief Defines MUJIN Controller Client installation-specific information. */ -#ifndef MUJINCLIENT_DEFINITIONS_H -#define MUJINCLIENT_DEFINITIONS_H +#ifndef MUJINWEBSTACKCLIENT_DEFINITIONS_H +#define MUJINWEBSTACKCLIENT_DEFINITIONS_H #if defined(_WIN32) || defined(__CYGWIN__) || defined(_MSC_VER) - #define MUJINCLIENT_HELPER_DLL_IMPORT __declspec(dllimport) - #define MUJINCLIENT_HELPER_DLL_EXPORT __declspec(dllexport) - #define MUJINCLIENT_HELPER_DLL_LOCAL + #define MUJINWEBSTACKCLIENT_HELPER_DLL_IMPORT __declspec(dllimport) + #define MUJINWEBSTACKCLIENT_HELPER_DLL_EXPORT __declspec(dllexport) + #define MUJINWEBSTACKCLIENT_HELPER_DLL_LOCAL #else #if __GNUC__ >= 4 - #define MUJINCLIENT_HELPER_DLL_IMPORT __attribute__ ((visibility("default"))) - #define MUJINCLIENT_HELPER_DLL_EXPORT __attribute__ ((visibility("default"))) - #define MUJINCLIENT_HELPER_DLL_LOCAL __attribute__ ((visibility("hidden"))) + #define MUJINWEBSTACKCLIENT_HELPER_DLL_IMPORT __attribute__ ((visibility("default"))) + #define MUJINWEBSTACKCLIENT_HELPER_DLL_EXPORT __attribute__ ((visibility("default"))) + #define MUJINWEBSTACKCLIENT_HELPER_DLL_LOCAL __attribute__ ((visibility("hidden"))) #else - #define MUJINCLIENT_HELPER_DLL_IMPORT - #define MUJINCLIENT_HELPER_DLL_EXPORT - #define MUJINCLIENT_HELPER_DLL_LOCAL + #define MUJINWEBSTACKCLIENT_HELPER_DLL_IMPORT + #define MUJINWEBSTACKCLIENT_HELPER_DLL_EXPORT + #define MUJINWEBSTACKCLIENT_HELPER_DLL_LOCAL #endif #endif -// Now we use the generic helper definitions above to define MUJINCLIENT_API and MUJINCLIENT_LOCAL. -// MUJINCLIENT_API is used for the public API symbols. It either DLL imports or DLL exports (or does nothing for static build) -// MUJINCLIENT_LOCAL is used for non-api symbols. -#if defined(MUJINCLIENT_DLL) || defined(MUJINCLIENT_CORE_DLL) // defined if OpenRAVE is compiled as a DLL - #ifdef MUJINCLIENT_DLL_EXPORTS // defined if we are building the OpenRAVE DLL (instead of using it) - #define MUJINCLIENT_API MUJINCLIENT_HELPER_DLL_EXPORT +// Now we use the generic helper definitions above to define MUJINWEBSTACKCLIENT_API and MUJINWEBSTACKCLIENT_LOCAL. +// MUJINWEBSTACKCLIENT_API is used for the public API symbols. It either DLL imports or DLL exports (or does nothing for static build) +// MUJINWEBSTACKCLIENT_LOCAL is used for non-api symbols. +#if defined(MUJINWEBSTACKCLIENT_DLL) || defined(MUJINWEBSTACKCLIENT_CORE_DLL) // defined if OpenRAVE is compiled as a DLL + #ifdef MUJINWEBSTACKCLIENT_DLL_EXPORTS // defined if we are building the OpenRAVE DLL (instead of using it) + #define MUJINWEBSTACKCLIENT_API MUJINWEBSTACKCLIENT_HELPER_DLL_EXPORT #else - #define MUJINCLIENT_API MUJINCLIENT_HELPER_DLL_IMPORT - #endif // MUJINCLIENT_DLL_EXPORTS - #define MUJINCLIENT_LOCAL MUJINCLIENT_HELPER_DLL_LOCAL -#else // MUJINCLIENT_DLL is not defined: this means OpenRAVE is a static lib. - #define MUJINCLIENT_API - #define MUJINCLIENT_LOCAL -#endif // MUJINCLIENT_DLL + #define MUJINWEBSTACKCLIENT_API MUJINWEBSTACKCLIENT_HELPER_DLL_IMPORT + #endif // MUJINWEBSTACKCLIENT_DLL_EXPORTS + #define MUJINWEBSTACKCLIENT_LOCAL MUJINWEBSTACKCLIENT_HELPER_DLL_LOCAL +#else // MUJINWEBSTACKCLIENT_DLL is not defined: this means OpenRAVE is a static lib. + #define MUJINWEBSTACKCLIENT_API + #define MUJINWEBSTACKCLIENT_LOCAL +#endif // MUJINWEBSTACKCLIENT_DLL -#define MUJINCLIENT_VERSION_MAJOR @MUJINCLIENT_VERSION_MAJOR@ -#define MUJINCLIENT_VERSION_MINOR @MUJINCLIENT_VERSION_MINOR@ -#define MUJINCLIENT_VERSION_PATCH @MUJINCLIENT_VERSION_PATCH@ -#define MUJINCLIENT_VERSION_COMBINED(major, minor, patch) (((major) << 16) | ((minor) << 8) | (patch)) -#define MUJINCLIENT_VERSION MUJINCLIENT_VERSION_COMBINED(MUJINCLIENT_VERSION_MAJOR, MUJINCLIENT_VERSION_MINOR, MUJINCLIENT_VERSION_PATCH) -#define MUJINCLIENT_VERSION_EXTRACT_MAJOR(version) (((version)>>16)&0xff) -#define MUJINCLIENT_VERSION_EXTRACT_MINOR(version) (((version)>>8)&0xff) -#define MUJINCLIENT_VERSION_EXTRACT_PATCH(version) (((version))&0xff) -#define MUJINCLIENT_VERSION_STRING "@MUJINCLIENT_VERSION_MAJOR@.@MUJINCLIENT_VERSION_MINOR@.@MUJINCLIENT_VERSION_PATCH@" -#define MUJINCLIENT_VERSION_STRING_FORMAT(version) boost::str(boost::format("%s.%s.%s")%(MUJINCLIENT_VERSION_EXTRACT_MAJOR(version))%(MUJINCLIENT_VERSION_EXTRACT_MINOR(version))%(MUJINCLIENT_VERSION_EXTRACT_PATCH(version))) +#define MUJINWEBSTACKCLIENT_VERSION_MAJOR @MUJINWEBSTACKCLIENT_VERSION_MAJOR@ +#define MUJINWEBSTACKCLIENT_VERSION_MINOR @MUJINWEBSTACKCLIENT_VERSION_MINOR@ +#define MUJINWEBSTACKCLIENT_VERSION_PATCH @MUJINWEBSTACKCLIENT_VERSION_PATCH@ +#define MUJINWEBSTACKCLIENT_VERSION_COMBINED(major, minor, patch) (((major) << 16) | ((minor) << 8) | (patch)) +#define MUJINWEBSTACKCLIENT_VERSION MUJINWEBSTACKCLIENT_VERSION_COMBINED(MUJINWEBSTACKCLIENT_VERSION_MAJOR, MUJINWEBSTACKCLIENT_VERSION_MINOR, MUJINWEBSTACKCLIENT_VERSION_PATCH) +#define MUJINWEBSTACKCLIENT_VERSION_EXTRACT_MAJOR(version) (((version)>>16)&0xff) +#define MUJINWEBSTACKCLIENT_VERSION_EXTRACT_MINOR(version) (((version)>>8)&0xff) +#define MUJINWEBSTACKCLIENT_VERSION_EXTRACT_PATCH(version) (((version))&0xff) +#define MUJINWEBSTACKCLIENT_VERSION_STRING "@MUJINWEBSTACKCLIENT_VERSION_MAJOR@.@MUJINWEBSTACKCLIENT_VERSION_MINOR@.@MUJINWEBSTACKCLIENT_VERSION_PATCH@" +#define MUJINWEBSTACKCLIENT_VERSION_STRING_FORMAT(version) boost::str(boost::format("%s.%s.%s")%(MUJINWEBSTACKCLIENT_VERSION_EXTRACT_MAJOR(version))%(MUJINWEBSTACKCLIENT_VERSION_EXTRACT_MINOR(version))%(MUJINWEBSTACKCLIENT_VERSION_EXTRACT_PATCH(version))) -#define MUJINCLIENT_VERSION_GE(major1, minor1, patch1, major2, minor2, patch2) (MUJINCLIENT_VERSION_COMBINED(major1, minor1, patch1) >= MUJINCLIENT_VERSION_COMBINED(major2, minor2, patch2)) -#define MUJINCLIENT_VERSION_MINIMUM(major, minor, patch) MUJINCLIENT_VERSION_GE(MUJINCLIENT_VERSION_MAJOR, MUJINCLIENT_VERSION_MINOR, MUJINCLIENT_VERSION_PATCH, major, minor, patch) +#define MUJINWEBSTACKCLIENT_VERSION_GE(major1, minor1, patch1, major2, minor2, patch2) (MUJINWEBSTACKCLIENT_VERSION_COMBINED(major1, minor1, patch1) >= MUJINWEBSTACKCLIENT_VERSION_COMBINED(major2, minor2, patch2)) +#define MUJINWEBSTACKCLIENT_VERSION_MINIMUM(major, minor, patch) MUJINWEBSTACKCLIENT_VERSION_GE(MUJINWEBSTACKCLIENT_VERSION_MAJOR, MUJINWEBSTACKCLIENT_VERSION_MINOR, MUJINWEBSTACKCLIENT_VERSION_PATCH, major, minor, patch) // whether log4cxx is to be used -#define MUJINCLIENT_LOG4CXX @MUJINCLIENT_LOG4CXX@ +#define MUJINWEBSTACKCLIENT_LOG4CXX @MUJINWEBSTACKCLIENT_LOG4CXX@ #define MUJIN_USEZMQ @MUJIN_USEZMQ@ #endif diff --git a/mujincontrollerclient-config-version.cmake.in b/mujincontrollerclient-config-version.cmake.in deleted file mode 100644 index cfd29d6a..00000000 --- a/mujincontrollerclient-config-version.cmake.in +++ /dev/null @@ -1,9 +0,0 @@ -set( PACKAGE_VERSION "@MUJINCLIENT_VERSION@" ) -if( "${PACKAGE_FIND_VERSION}" VERSION_EQUAL "@MUJINCLIENT_VERSION@") - set(PACKAGE_VERSION_EXACT 1) - set(PACKAGE_VERSION_COMPATIBLE 1) -endif() - -if( "${PACKAGE_FIND_VERSION}" VERSION_LESS "@MUJINCLIENT_VERSION@") - set(PACKAGE_VERSION_COMPATIBLE 1) -endif() diff --git a/mujincontrollerclient-config.cmake.in b/mujincontrollerclient-config.cmake.in deleted file mode 100644 index 46a0bfc1..00000000 --- a/mujincontrollerclient-config.cmake.in +++ /dev/null @@ -1,89 +0,0 @@ -# - Find Mujin Controller Client C++ Library -# -# Users can set the following variables before calling the module: -# MujinControllerClient_DIR - The preferred installation prefix for searching for MujinControllerClient. Set by the user. -# -# MujinControllerClient_ROOT_DIR - the root directory where the installation can be found -# MujinControllerClient_CXX_FLAGS - extra flags for compilation -# MujinControllerClient_LINK_FLAGS - extra flags for linking -# MujinControllerClient_INCLUDE_DIRS - include directories -# MujinControllerClient_LIBRARY_DIRS - link directories -# MujinControllerClient_LIBRARIES - libraries to link plugins with -# MujinControllerClient_Boost_VERSION - the boost version was compiled with - -#================================================================================== -# Copyright (C) 2009-2011 Rosen Diankov -# -# Distributed under the OSI-approved BSD License (the "License"); -# see accompanying file Copyright.txt for details. -# -# This software is distributed WITHOUT ANY WARRANTY; without even the -# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -# See the License for more information. -#============================================================================= -# (To distributed this file outside of CMake, substitute the full -# License text for the above reference.) -#================================================================================== -if( MSVC ) -get_filename_component(MujinControllerClient_ROOT_DIR "${CMAKE_CURRENT_LIST_FILE}" PATH) -set( MujinControllerClient_INCLUDE_DIRS "@MujinControllerClient_INCLUDE_DIR@;${MujinControllerClient_ROOT_DIR}/include") -else() -get_filename_component(_PREFIX "${CMAKE_CURRENT_LIST_FILE}" PATH) -get_filename_component(_PREFIX "${_PREFIX}" PATH) -get_filename_component(_PREFIX "${_PREFIX}" PATH) -get_filename_component(MujinControllerClient_ROOT_DIR "${_PREFIX}" PATH) -set( MujinControllerClient_INCLUDE_DIRS "${MujinControllerClient_ROOT_DIR}/include") -endif() - -if( MSVC ) - # in order to prevent DLL hell, each of the DLLs have to be suffixed with the major version and msvc prefix - if( MSVC70 OR MSVC71 ) - set(MSVC_PREFIX "vc70") - elseif( MSVC80 ) - set(MSVC_PREFIX "vc80") - elseif( MSVC90 ) - set(MSVC_PREFIX "vc90") - else() - set(MSVC_PREFIX "vc100") - endif() - set(MujinControllerClient_LIBRARY_SUFFIX "@MUJINCLIENT_VERSION_MAJOR@.@MUJINCLIENT_VERSION_MINOR@-${MSVC_PREFIX}-mt" CACHE STRING "the suffix for the mujin client libraries" FORCE) -else() - set(MujinControllerClient_LIBRARY_SUFFIX "@MUJINCLIENT_LIBRARY_SUFFIX@" CACHE STRING "the suffix for the mujin client libraries" FORCE) -endif() - -set( MujinControllerClient_CXX_FLAGS "-DMUJINCLIENT_DLL @MUJINCLIENT_EXPORT_CXXFLAGS@ @MUJINCLIENT_LOG4CXX_INCLUDE_DIRS@" ) -if( WIN32 ) - set( MujinControllerClient_CXX_FLAGS "${MujinControllerClient_CXX_FLAGS} -DBOOST_ALL_DYN_LINK -DBOOST_ALL_NO_LIB") -endif() -if( MSVC ) - set( MujinControllerClient_CXX_FLAGS "${MujinControllerClient_CXX_FLAGS} /EHc-") -endif() -set( MujinControllerClient_LINK_FLAGS "@MUJINCLIENT_LOG4CXX_LIB_DIRS@" ) -set( MujinControllerClient_LIBRARY_DIRS "${MujinControllerClient_ROOT_DIR}/lib@LIB_SUFFIX@") -set( MujinControllerClient_LIBRARIES mujincontrollerclient${MujinControllerClient_LIBRARY_SUFFIX} @LOG4CXX_LIBRARIES@) - -set( MujinControllerClient_Boost_VERSION "@Boost_MAJOR_VERSION@.@Boost_MINOR_VERSION@") - -if( WIN32 ) - # search for the boost version was compiled with - set(Boost_USE_MULTITHREAD ON) - set(Boost_USE_STATIC_LIBS OFF) - set(Boost_USE_STATIC_RUNTIME OFF) - find_package(Boost ${MujinControllerClient_Boost_VERSION} EXACT COMPONENTS thread date_time) - if(Boost_VERSION AND NOT "${Boost_VERSION}" STREQUAL "0") - set( MujinControllerClient_INCLUDE_DIRS "${MujinControllerClient_INCLUDE_DIRS}" ${Boost_INCLUDE_DIRS}) - set( MujinControllerClient_LIBRARY_DIRS "${MujinControllerClient_LIBRARY_DIRS}" ${Boost_LIBRARY_DIRS}) - else(Boost_VERSION AND NOT "${Boost_VERSION}" STREQUAL "0") - message(WARNING "Failed to find Boost ${MujinControllerClient_Boost_VERSION} necessary MujinControllerClient") - endif(Boost_VERSION AND NOT "${Boost_VERSION}" STREQUAL "0") -endif( WIN32 ) - -mark_as_advanced( - MujinControllerClient_ROOT_DIR - MujinControllerClient_CXX_FLAGS - MujinControllerClient_LINK_FLAGS - MujinControllerClient_INCLUDE_DIRS - MujinControllerClient_LIBRARY_DIRS - MujinControllerClient_LIBRARIES - MujinControllerClient_Boost_VERSION -) diff --git a/mujinwebstackclient-config-version.cmake.in b/mujinwebstackclient-config-version.cmake.in new file mode 100644 index 00000000..d47b1791 --- /dev/null +++ b/mujinwebstackclient-config-version.cmake.in @@ -0,0 +1,9 @@ +set( PACKAGE_VERSION "@MUJINWEBSTACKCLIENT_VERSION@" ) +if( "${PACKAGE_FIND_VERSION}" VERSION_EQUAL "@MUJINWEBSTACKCLIENT_VERSION@") + set(PACKAGE_VERSION_EXACT 1) + set(PACKAGE_VERSION_COMPATIBLE 1) +endif() + +if( "${PACKAGE_FIND_VERSION}" VERSION_LESS "@MUJINWEBSTACKCLIENT_VERSION@") + set(PACKAGE_VERSION_COMPATIBLE 1) +endif() diff --git a/mujinwebstackclient-config.cmake.in b/mujinwebstackclient-config.cmake.in new file mode 100644 index 00000000..01e4a352 --- /dev/null +++ b/mujinwebstackclient-config.cmake.in @@ -0,0 +1,89 @@ +# - Find Mujin Controller Client C++ Library +# +# Users can set the following variables before calling the module: +# MujinWebstackClient_DIR - The preferred installation prefix for searching for MujinWebstackClient. Set by the user. +# +# MujinWebstackClient_ROOT_DIR - the root directory where the installation can be found +# MujinWebstackClient_CXX_FLAGS - extra flags for compilation +# MujinWebstackClient_LINK_FLAGS - extra flags for linking +# MujinWebstackClient_INCLUDE_DIRS - include directories +# MujinWebstackClient_LIBRARY_DIRS - link directories +# MujinWebstackClient_LIBRARIES - libraries to link plugins with +# MujinWebstackClient_Boost_VERSION - the boost version was compiled with + +#================================================================================== +# Copyright (C) 2009-2011 Rosen Diankov +# +# Distributed under the OSI-approved BSD License (the "License"); +# see accompanying file Copyright.txt for details. +# +# This software is distributed WITHOUT ANY WARRANTY; without even the +# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the License for more information. +#============================================================================= +# (To distributed this file outside of CMake, substitute the full +# License text for the above reference.) +#================================================================================== +if( MSVC ) +get_filename_component(MujinWebstackClient_ROOT_DIR "${CMAKE_CURRENT_LIST_FILE}" PATH) +set( MujinWebstackClient_INCLUDE_DIRS "@MujinWebstackClient_INCLUDE_DIR@;${MujinWebstackClient_ROOT_DIR}/include") +else() +get_filename_component(_PREFIX "${CMAKE_CURRENT_LIST_FILE}" PATH) +get_filename_component(_PREFIX "${_PREFIX}" PATH) +get_filename_component(_PREFIX "${_PREFIX}" PATH) +get_filename_component(MujinWebstackClient_ROOT_DIR "${_PREFIX}" PATH) +set( MujinWebstackClient_INCLUDE_DIRS "${MujinWebstackClient_ROOT_DIR}/include") +endif() + +if( MSVC ) + # in order to prevent DLL hell, each of the DLLs have to be suffixed with the major version and msvc prefix + if( MSVC70 OR MSVC71 ) + set(MSVC_PREFIX "vc70") + elseif( MSVC80 ) + set(MSVC_PREFIX "vc80") + elseif( MSVC90 ) + set(MSVC_PREFIX "vc90") + else() + set(MSVC_PREFIX "vc100") + endif() + set(MujinWebstackClient_LIBRARY_SUFFIX "@MUJINWEBSTACKCLIENT_VERSION_MAJOR@.@MUJINWEBSTACKCLIENT_VERSION_MINOR@-${MSVC_PREFIX}-mt" CACHE STRING "the suffix for the mujin client libraries" FORCE) +else() + set(MujinWebstackClient_LIBRARY_SUFFIX "@MUJINWEBSTACKCLIENT_LIBRARY_SUFFIX@" CACHE STRING "the suffix for the mujin client libraries" FORCE) +endif() + +set( MujinWebstackClient_CXX_FLAGS "-DMUJINWEBSTACKCLIENT_DLL @MUJINWEBSTACKCLIENT_EXPORT_CXXFLAGS@ @MUJINWEBSTACKCLIENT_LOG4CXX_INCLUDE_DIRS@" ) +if( WIN32 ) + set( MujinWebstackClient_CXX_FLAGS "${MujinWebstackClient_CXX_FLAGS} -DBOOST_ALL_DYN_LINK -DBOOST_ALL_NO_LIB") +endif() +if( MSVC ) + set( MujinWebstackClient_CXX_FLAGS "${MujinWebstackClient_CXX_FLAGS} /EHc-") +endif() +set( MujinWebstackClient_LINK_FLAGS "@MUJINWEBSTACKCLIENT_LOG4CXX_LIB_DIRS@" ) +set( MujinWebstackClient_LIBRARY_DIRS "${MujinWebstackClient_ROOT_DIR}/lib@LIB_SUFFIX@") +set( MujinWebstackClient_LIBRARIES mujinwebstackclient${MujinWebstackClient_LIBRARY_SUFFIX} @LOG4CXX_LIBRARIES@) + +set( MujinWebstackClient_Boost_VERSION "@Boost_MAJOR_VERSION@.@Boost_MINOR_VERSION@") + +if( WIN32 ) + # search for the boost version was compiled with + set(Boost_USE_MULTITHREAD ON) + set(Boost_USE_STATIC_LIBS OFF) + set(Boost_USE_STATIC_RUNTIME OFF) + find_package(Boost ${MujinWebstackClient_Boost_VERSION} EXACT COMPONENTS thread date_time) + if(Boost_VERSION AND NOT "${Boost_VERSION}" STREQUAL "0") + set( MujinWebstackClient_INCLUDE_DIRS "${MujinWebstackClient_INCLUDE_DIRS}" ${Boost_INCLUDE_DIRS}) + set( MujinWebstackClient_LIBRARY_DIRS "${MujinWebstackClient_LIBRARY_DIRS}" ${Boost_LIBRARY_DIRS}) + else(Boost_VERSION AND NOT "${Boost_VERSION}" STREQUAL "0") + message(WARNING "Failed to find Boost ${MujinWebstackClient_Boost_VERSION} necessary MujinWebstackClient") + endif(Boost_VERSION AND NOT "${Boost_VERSION}" STREQUAL "0") +endif( WIN32 ) + +mark_as_advanced( + MujinWebstackClient_ROOT_DIR + MujinWebstackClient_CXX_FLAGS + MujinWebstackClient_LINK_FLAGS + MujinWebstackClient_INCLUDE_DIRS + MujinWebstackClient_LIBRARY_DIRS + MujinWebstackClient_LIBRARIES + MujinWebstackClient_Boost_VERSION +) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index d975da3f..672733ab 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -13,64 +13,58 @@ # limitations under the License. include_directories(${CURL_INCLUDE_DIRS} ${LOG4CXX_INCLUDEDIR}) -if (libzmq_FOUND) - include_directories(${libzmq_INCLUDE_DIRS}) -endif() - -link_directories(${MUJINCLIENT_LINK_DIRS}) +link_directories(${MUJINWEBSTACKCLIENT_LINK_DIRS}) set(SOURCE_FILES boost_assertion_failed.cpp - binpickingtask.cpp - common.cpp - common.h - controllerclientimpl.cpp - controllerclientimpl.h - mujincontrollerclient.cpp - mujindefinitions.cpp + createwebstackclient.cpp mujinjson.cpp - utf8.h ) - -if (libzmq_FOUND) - set(SOURCE_FILES ${SOURCE_FILES} binpickingtaskzmq.h binpickingtaskzmq.cpp mujinzmq.cpp) -endif() + utf8.h + common.h + common.cpp + logging.h + webstackclient.cpp ) -add_library(libmujincontrollerclient SHARED ${SOURCE_FILES}) +add_library(libmujinwebstackclient SHARED ${SOURCE_FILES}) if( EXTRA_MSVC_DEPEND ) message(STATUS "adding msvc_boost dependency") - add_dependencies(libmujincontrollerclient ${EXTRA_MSVC_DEPEND}) + add_dependencies(libmujinwebstackclient ${EXTRA_MSVC_DEPEND}) endif() -set_target_properties(libmujincontrollerclient PROPERTIES COMPILE_FLAGS "${EXTRA_COMPILE_FLAGS} ${Boost_CFLAGS} -DMUJINCLIENT_DLL_EXPORTS -DMUJINCLIENT_DLL" LINK_FLAGS "" - OUTPUT_NAME mujincontrollerclient${MUJINCLIENT_LIBRARY_SUFFIX} + +set_target_properties(libmujinwebstackclient PROPERTIES + COMPILE_FLAGS "${EXTRA_COMPILE_FLAGS} ${Boost_CFLAGS} -DMUJINWEBSTACKCLIENT_DLL_EXPORTS -DMUJINWEBSTACKCLIENT_DLL" + LINK_FLAGS "" + OUTPUT_NAME mujinwebstackclient${MUJINWEBSTACKCLIENT_LIBRARY_SUFFIX} SOVERSION 0 # always have it 0 since we're including the soversion as part of the library name - VERSION ${MUJINCLIENT_VERSION} + VERSION ${MUJINWEBSTACKCLIENT_VERSION} CLEAN_DIRECT_OUTPUT 1 ) -target_link_libraries(libmujincontrollerclient ${CURL_LIBRARIES} ${Boost_THREAD_LIBRARY} ${Boost_DATE_TIME_LIBRARY} ${EXTRA_LIBRARIES} ${libzmq_LIBRARIES} ${LOG4CXX_LIBRARIES}) +target_link_libraries(libmujinwebstackclient ${CURL_LIBRARIES} ${Boost_THREAD_LIBRARY} ${Boost_DATE_TIME_LIBRARY} ${EXTRA_LIBRARIES} ${libzmq_LIBRARIES} ${LOG4CXX_LIBRARIES}) if( MSVC ) - install(TARGETS libmujincontrollerclient RUNTIME DESTINATION bin LIBRARY DESTINATION bin ARCHIVE DESTINATION lib${LIB_SUFFIX}) + install(TARGETS libmujinwebstackclient RUNTIME DESTINATION bin LIBRARY DESTINATION bin ARCHIVE DESTINATION lib${LIB_SUFFIX}) else() - install(TARGETS libmujincontrollerclient DESTINATION lib${LIB_SUFFIX}) + install(TARGETS libmujinwebstackclient DESTINATION lib${LIB_SUFFIX}) endif() if( OPT_BUILD_STATIC ) # visual studio needs static lib built if( MSVC ) # static version needs to have different name - set(LIBMUJINCONTROLLERCLIENT_NAME libmujincontrollerclient${MUJINCLIENT_LIBRARY_SUFFIX}) + set(LIBMUJINWEBSTACKCLIENT_NAME libmujinwebstackclient${MUJINWEBSTACKCLIENT_LIBRARY_SUFFIX}) else() - set(LIBMUJINCONTROLLERCLIENT_NAME mujincontrollerclient${MUJINCLIENT_LIBRARY_SUFFIX}) + set(LIBMUJINWEBSTACKCLIENT_NAME mujinwebstackclient${MUJINWEBSTACKCLIENT_LIBRARY_SUFFIX}) endif() - add_library(libmujincontrollerclient_static STATIC ${SOURCE_FILES}) - set_target_properties(libmujincontrollerclient_static PROPERTIES OUTPUT_NAME ${LIBMUJINCONTROLLERCLIENT_NAME} - SOVERSION 0 # always have it 0 since we're including the soversion as part of the library name - VERSION ${MUJINCLIENT_VERSION} - CLEAN_DIRECT_OUTPUT 1 - COMPILE_FLAGS "${EXTRA_COMPILE_FLAGS} ${Boost_CFLAGS} -DMUJINCLIENT_DLL_EXPORTS -DMUJINCLIENT_DLL" - LINK_FLAGS "") + add_library(libmujinwebstackclient_static STATIC ${SOURCE_FILES}) + set_target_properties(libmujinwebstackclient_static PROPERTIES + OUTPUT_NAME ${LIBMUJINWEBSTACKCLIENT_NAME} + SOVERSION 0 # always have it 0 since we're including the soversion as part of the library name + VERSION ${MUJINWEBSTACKCLIENT_VERSION} + CLEAN_DIRECT_OUTPUT 1 + COMPILE_FLAGS "${EXTRA_COMPILE_FLAGS} ${Boost_CFLAGS} -DMUJINWEBSTACKCLIENT_DLL_EXPORTS -DMUJINWEBSTACKCLIENT_DLL" + LINK_FLAGS "") - target_link_libraries(libmujincontrollerclient_static ${CURL_LIBRARIES} ${EXTRA_LIBRARIES} ${libzmq_LIBRARIES} ${libzmq_LIBRARIES} ${LOG4CXX_LIBRARIES}) - install(TARGETS libmujincontrollerclient_static DESTINATION lib${LIB_SUFFIX}) + target_link_libraries(libmujinwebstackclient_static ${CURL_LIBRARIES} ${EXTRA_LIBRARIES} ${libzmq_LIBRARIES} ${libzmq_LIBRARIES} ${LOG4CXX_LIBRARIES}) + install(TARGETS libmujinwebstackclient_static DESTINATION lib${LIB_SUFFIX}) endif() diff --git a/src/binpickingtask.cpp b/src/binpickingtask.cpp deleted file mode 100644 index cadd052b..00000000 --- a/src/binpickingtask.cpp +++ /dev/null @@ -1,2008 +0,0 @@ -// -*- coding: utf-8 -*- -// Copyright (C) 2012-2016 MUJIN Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT 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 "common.h" -#include "controllerclientimpl.h" -#if BOOST_VERSION > 104800 -#include -#endif -#include // for sleep -#include "mujincontrollerclient/binpickingtask.h" - -#ifdef MUJIN_USEZMQ -#include "mujincontrollerclient/zmq.hpp" -#endif - -#ifdef _WIN32 -#include -#define isnan _isnan -#endif - -#include - -#include "logging.h" - -#include -#include -#include -#include "mujincontrollerclient/mujinjson.h" - -MUJIN_LOGGER("mujin.controllerclientcpp.binpickingtask"); - -namespace mujinclient { -using namespace utils; -using namespace mujinjson; - -static void LoadAABBFromJsonValue(const rapidjson::Value& rAABB, mujin::AABB& aabb) -{ - BOOST_ASSERT(rAABB.IsObject()); - BOOST_ASSERT(rAABB.HasMember("pos")); - BOOST_ASSERT(rAABB.HasMember("extents")); - const rapidjson::Value& rPos = rAABB["pos"]; - BOOST_ASSERT(rPos.IsArray()); - mujinjson::LoadJsonValue(rPos[0], aabb.pos[0]); - mujinjson::LoadJsonValue(rPos[1], aabb.pos[1]); - mujinjson::LoadJsonValue(rPos[2], aabb.pos[2]); - const rapidjson::Value& rExtents = rAABB["extents"]; - BOOST_ASSERT(rExtents.IsArray()); - mujinjson::LoadJsonValue(rExtents[0], aabb.extents[0]); - mujinjson::LoadJsonValue(rExtents[1], aabb.extents[1]); - mujinjson::LoadJsonValue(rExtents[2], aabb.extents[2]); -} - -BinPickingResultResource::BinPickingResultResource(ControllerClientPtr controller, const std::string& pk) : PlanningResultResource(controller,"binpickingresult", pk) -{ -} - -BinPickingResultResource::~BinPickingResultResource() -{ -} - -BinPickingTaskResource::BinPickingTaskResource(ControllerClientPtr pcontroller, const std::string& pk, const std::string& scenepk, const std::string& tasktype) : TaskResource(pcontroller,pk), _zmqPort(-1), _heartbeatPort(-1), _tasktype(tasktype), _bIsInitialized(false) -{ - _callerid = str(boost::format("controllerclientcpp%s_web")%MUJINCLIENT_VERSION_STRING); - _scenepk = scenepk; - // get hostname from uri - GETCONTROLLERIMPL(); - const std::string baseuri = controller->GetBaseUri(); - std::string::const_iterator uriend = baseuri.end(); - // query start - std::string::const_iterator querystart = std::find(baseuri.begin(), uriend, '?'); - // protocol - std::string protocol; - std::string::const_iterator protocolstart = baseuri.begin(); - std::string::const_iterator protocolend = std::find(protocolstart, uriend, ':'); - if (protocolend != uriend) { - std::string p = &*(protocolend); - if ((p.length() > 3) & (p.substr(0,3) == "://")) { - protocol = std::string(protocolstart, protocolend); - protocolend +=3; - } else { - protocolend = baseuri.begin(); - } - } else { - protocolend = baseuri.begin(); - } - // host - std::string::const_iterator hoststart = protocolend; - std::string::const_iterator pathstart = std::find(hoststart, uriend, '/'); - std::string::const_iterator hostend = std::find(protocolend, (pathstart != uriend) ? pathstart : querystart, ':'); - _mujinControllerIp = std::string(hoststart, hostend); - - { - /// HACK until can think of proper way to send sceneparams - std::string scenebasename = pcontroller->GetNameFromPrimaryKey_UTF8(scenepk); - - _rSceneParams.SetObject(); - mujinjson::SetJsonValueByKey(_rSceneParams, "scenetype", "mujin"); - mujinjson::SetJsonValueByKey(_rSceneParams, "sceneuri", std::string("mujin:/")+scenebasename); - - // should stop sending scenefilename since it is a hack! - std::string MUJIN_MEDIA_ROOT_DIR = "/var/www/media/u"; - char* pMUJIN_MEDIA_ROOT_DIR = getenv("MUJIN_MEDIA_ROOT_DIR"); - if( !!pMUJIN_MEDIA_ROOT_DIR ) { - MUJIN_MEDIA_ROOT_DIR = pMUJIN_MEDIA_ROOT_DIR; - } - - std::string scenefilename = MUJIN_MEDIA_ROOT_DIR + std::string("/") + pcontroller->GetUserName() + std::string("/") + scenebasename; - mujinjson::SetJsonValueByKey(_rSceneParams, "scenefilename", scenefilename); - _sceneparams_json = mujinjson::DumpJson(_rSceneParams); - } -} - -BinPickingTaskResource::~BinPickingTaskResource() -{ - _bShutdownHeartbeatMonitor = true; - if (!!_pHeartbeatMonitorThread) { - _pHeartbeatMonitorThread->join(); - } -} - -void BinPickingTaskResource::Initialize(const std::string& defaultTaskParameters, const double timeout, const std::string& userinfo, const std::string& slaverequestid) -{ - if( defaultTaskParameters.size() > 0 ) { - _mapTaskParameters.clear(); - rapidjson::Document d; - d.Parse(defaultTaskParameters.c_str()); - for (rapidjson::Value::ConstMemberIterator it = d.MemberBegin(); it != d.MemberEnd(); ++it) { - rapidjson::StringBuffer stringbuffer; - rapidjson::Writer writer(stringbuffer); - it->value.Accept(writer); - _mapTaskParameters[it->name.GetString()] = std::string(stringbuffer.GetString(), stringbuffer.GetSize()); - } - } - - _bIsInitialized = true; - ParseJson(_rUserInfo, userinfo); - _userinfo_json = userinfo; - _slaverequestid = slaverequestid; -} - -void BinPickingTaskResource::SetCallerId(const std::string& callerid) -{ - _callerid = callerid; -} - -const std::string& BinPickingTaskResource::_GetCallerId() const -{ - return _callerid; -} - -#ifdef MUJIN_USEZMQ -void BinPickingTaskResource::Initialize(const std::string& defaultTaskParameters, const int zmqPort, const int heartbeatPort, boost::shared_ptr zmqcontext, const bool initializezmq, const double reinitializetimeout, const double timeout, const std::string& userinfo, const std::string& slaverequestid) -{ - - if( defaultTaskParameters.size() > 0 ) { - _mapTaskParameters.clear(); - rapidjson::Document d; - d.Parse(defaultTaskParameters.c_str()); - for (rapidjson::Value::ConstMemberIterator it = d.MemberBegin(); it != d.MemberEnd(); ++it) { - rapidjson::StringBuffer stringbuffer; - rapidjson::Writer writer(stringbuffer); - it->value.Accept(writer); - _mapTaskParameters[it->name.GetString()] = std::string(stringbuffer.GetString(), stringbuffer.GetSize()); - } - } - - _zmqPort = zmqPort; - _heartbeatPort = heartbeatPort; - _bIsInitialized = true; - _zmqcontext = zmqcontext; - ParseJson(_rUserInfo, userinfo); - _userinfo_json = userinfo; - _slaverequestid = slaverequestid; -} -#endif - -void BinPickingResultResource::GetResultJson(rapidjson::Document& pt) const -{ - GETCONTROLLERIMPL(); - //rapidjson::Document d(rapidjson::kObjectType); - controller->CallGet(boost::str(boost::format("%s/%s/?format=json&limit=1")%GetResourceName()%GetPrimaryKey()), pt); - // in this way we don't copy - rapidjson::Value v; - v.Swap(pt["output"]); - v.Swap(pt); -} - -std::string utils::GetJsonString(const std::string& str) -{ - std::string newstr = str; -#if BOOST_VERSION > 104800 - boost::replace_all(newstr, "\"", "\\\""); -#else - std::vector< std::pair > serachpairs(1); - serachpairs[0].first = "\""; serachpairs[0].second = "\\\""; - SearchAndReplace(newstr, str, serachpairs); -#endif - return "\""+newstr+"\""; -} - -std::string utils::GetJsonString (const std::vector& vec) -{ - std::stringstream ss; - ss << std::setprecision(std::numeric_limits::digits10+1); - ss << "["; - for (unsigned int i = 0; i < vec.size(); ++i) { - ss << vec[i]; - if (i != vec.size() - 1) { - ss << ", "; - } - } - ss << "]"; - return ss.str(); -} - -std::string utils::GetJsonString (const std::vector& vec) -{ - std::stringstream ss; - ss << std::setprecision(std::numeric_limits::digits10+1); - ss << "["; - for (unsigned int i = 0; i < vec.size(); ++i) { - ss << vec[i]; - if (i != vec.size() - 1) { - ss << ", "; - } - } - ss << "]"; - return ss.str(); -} - -std::string utils::GetJsonString (const std::vector& vec) -{ - std::stringstream ss; - ss << "["; - for (unsigned int i = 0; i < vec.size(); ++i) { - ss << vec[i]; - if (i != vec.size() - 1) { - ss << ", "; - } - } - ss << "]"; - return ss.str(); -} - -std::string utils::GetJsonString(const std::vector& vec) -{ - std::stringstream ss; - ss << "["; - for (unsigned int i = 0; i < vec.size(); ++i) { - ss << GetJsonString(vec[i]); - if (i != vec.size() - 1) { - ss << ", "; - } - } - ss << "]"; - return ss.str(); -} - -std::string utils::GetJsonString(const Transform& transform) -{ - std::stringstream ss; - ss << std::setprecision(std::numeric_limits::digits10+1); - // \"translation\":[%.15f, %.15f, %.15f], \"quaternion\":[%.15f, %.15f, %.15f, %.15f] - ss << GetJsonString("translation") << ": ["; - for (unsigned int i=0; i<3; i++) { - ss << transform.translate[i]; - if (i!=3-1) { - ss << ", "; - } - } - ss << "], "; - ss << GetJsonString("quaternion") << ": ["; - for (unsigned int i=0; i<4; i++) { - ss << transform.quaternion[i]; - if (i!=4-1) { - ss << ", "; - } - } - ss << "]"; - return ss.str(); -} - -std::string utils::GetJsonString(const BinPickingTaskResource::DetectedObject& obj) -{ - std::stringstream ss; - ss << std::setprecision(std::numeric_limits::digits10+1); - //"{\"name\": \"obj\",\"translation_\":[100,200,300],\"quat_\":[1,0,0,0],\"confidence\":0.5}" - ss << "{"; - ss << GetJsonString("name") << ": " << GetJsonString(obj.name) << ", "; - ss << GetJsonString("object_uri") << ": " << GetJsonString(obj.object_uri) << ", "; - ss << GetJsonString("translation_") << ": ["; - for (unsigned int i=0; i<3; i++) { - ss << obj.transform.translate[i]; - if (i!=3-1) { - ss << ", "; - } - } - ss << "], "; - ss << GetJsonString("quat_") << ": ["; - for (unsigned int i=0; i<4; i++) { - ss << obj.transform.quaternion[i]; - if (i!=4-1) { - ss << ", "; - } - } - ss << "], "; - ss << GetJsonString("confidence") << ": " << obj.confidence; - ss << ", " << GetJsonString("sensortimestamp") << ": " << obj.timestamp; - ss << ", " << GetJsonString("isPickable") << ": " << obj.isPickable; - if( obj.extra.size() > 0 ) { - ss << ", " << GetJsonString("extra") << ": " << obj.extra; - } - ss << "}"; - return ss.str(); -} - -std::string utils::GetJsonString(const BinPickingTaskResource::PointCloudObstacle& obj) -{ - std::stringstream ss; - ss << std::setprecision(std::numeric_limits::digits10+1); // want to control the size of the JSON file outputted - // "\"name\": __dynamicobstacle__, \"pointsize\": 0.005, \"points\": [] - ss << GetJsonString("pointcloudid") << ": " << GetJsonString(obj.name) << ", "; - ss << GetJsonString("pointsize") << ": " << obj.pointsize <<", "; - - ss << GetJsonString("points") << ": " << "["; - bool bwrite = false; - for (unsigned int i = 0; i < obj.points.size(); i+=3) { - if( !isnan(obj.points[i]) && !isnan(obj.points[i+1]) && !isnan(obj.points[i+2]) ) { // sometimes point clouds can have NaNs, although it's a bug on detectors sending bad point clouds, these points can usually be ignored. - if( bwrite ) { - ss << ","; - } - ss << obj.points[i] << "," << obj.points[i+1] << "," << obj.points[i+2]; - bwrite = true; - } - } - ss << "]"; - return ss.str(); -} - -std::string utils::GetJsonString(const BinPickingTaskResource::SensorOcclusionCheck& check) -{ - std::stringstream ss; - ss << GetJsonString("bodyname") << ": " << GetJsonString(check.bodyname) << ", "; - ss << GetJsonString("cameraname") << ": " << GetJsonString(check.cameraname) << ", "; - ss << GetJsonString("starttime") << ": " << check.starttime <<", "; - ss << GetJsonString("endtime") << ": " << check.endtime; - return ss.str(); -} - -std::string utils::GetJsonString(const std::string& key, const std::string& value) -{ - std::stringstream ss; - ss << GetJsonString(key) << ": " << GetJsonString(value); - return ss.str(); -} - -std::string utils::GetJsonString(const std::string& key, const int value) -{ - std::stringstream ss; - ss << GetJsonString(key) << ": " << value; - return ss.str(); -} - -std::string utils::GetJsonString(const std::string& key, const unsigned long long value) -{ - std::stringstream ss; - ss << GetJsonString(key) << ": " << value; - return ss.str(); -} - -std::string utils::GetJsonString(const std::string& key, const Real value) -{ - std::stringstream ss; - ss << GetJsonString(key) << ": " << value; - return ss.str(); -} - -BinPickingTaskResource::ResultGetJointValues::~ResultGetJointValues() -{ -} - -void BinPickingTaskResource::ResultGetJointValues::Parse(const rapidjson::Value& pt) -{ - BOOST_ASSERT(pt.IsObject() && pt.HasMember("output")); - const rapidjson::Value& v = pt["output"]; - - LoadJsonValueByKey(v, "robottype", robottype); - LoadJsonValueByKey(v, "jointnames", jointnames); - LoadJsonValueByKey(v, "currentjointvalues", currentjointvalues); - tools.clear(); - if (v.HasMember("tools")) { - const rapidjson::Value &toolsjson = v["tools"]; - for (rapidjson::Document::ConstMemberIterator it = toolsjson.MemberBegin(); it != toolsjson.MemberEnd(); it++) { - Transform transform; - LoadJsonValueByKey(it->value, "translate", transform.translate); - LoadJsonValueByKey(it->value, "quaternion", transform.quaternion); - tools[it->name.GetString()] = transform; - } - } -} - -BinPickingTaskResource::ResultMoveJoints::~ResultMoveJoints() -{ -} - -void BinPickingTaskResource::ResultMoveJoints::Parse(const rapidjson::Value& pt) -{ - BOOST_ASSERT(pt.IsObject() && pt.HasMember("output")); - const rapidjson::Value& v = pt["output"]; - LoadJsonValueByKey(v, "robottype", robottype); - LoadJsonValueByKey(v, "timedjointvalues", timedjointvalues); - LoadJsonValueByKey(v, "numpoints", numpoints); -} - -BinPickingTaskResource::ResultTransform::~ResultTransform() -{ -} - -void BinPickingTaskResource::ResultTransform::Parse(const rapidjson::Value& pt) -{ - BOOST_ASSERT(pt.IsObject() && pt.HasMember("output")); - const rapidjson::Value& v = pt["output"]; - - LoadJsonValueByKey(v, "translation", transform.translate); - LoadJsonValueByKey(v, "quaternion", transform.quaternion); -} - -BinPickingTaskResource::ResultInstObjectInfo::~ResultInstObjectInfo() -{ -} - -void BinPickingTaskResource::ResultInstObjectInfo::Parse(const rapidjson::Value& pt) -{ - BOOST_ASSERT(pt.IsObject() && pt.HasMember("output")); - const rapidjson::Value& rOutput = pt["output"]; - - LoadJsonValueByKey(rOutput, "translation", instobjecttransform.translate); - LoadJsonValueByKey(rOutput, "quaternion", instobjecttransform.quaternion); - instobjectobb.Parse(rOutput["obb"]); - instobjectinnerobb.Parse(rOutput["innerobb"]); - - if( rOutput.HasMember("geometryInfos") ) { - mujinjson::SaveJsonValue(rGeometryInfos, rOutput["geometryInfos"]); - } - - if( rOutput.HasMember("ikparams") ) { - mujinjson::SaveJsonValue(rIkParams, rOutput["ikparams"]); - } -} - -BinPickingTaskResource::ResultGetInstObjectAndSensorInfo::~ResultGetInstObjectAndSensorInfo() -{ -} - -void BinPickingTaskResource::ResultGetInstObjectAndSensorInfo::Parse(const rapidjson::Value& pt) -{ - BOOST_ASSERT(pt.IsObject() && pt.HasMember("output")); - const rapidjson::Value& output = pt["output"]; - - mrGeometryInfos.clear(); - - const rapidjson::Value& instobjects = output["instobjects"]; - for (rapidjson::Document::ConstMemberIterator it = instobjects.MemberBegin(); it != instobjects.MemberEnd(); it++) { - std::string objname = it->name.GetString(); - Transform transform; - ResultOBB resultobb, resultinnerobb; - LoadJsonValueByKey(it->value, "translation", transform.translate); - LoadJsonValueByKey(it->value, "quaternion", transform.quaternion); - resultobb.Parse(it->value["obb"]); - resultinnerobb.Parse(it->value["innerobb"]); - - minstobjecttransform[objname] = transform; - minstobjectobb[objname] = resultobb; - minstobjectinnerobb[objname] = resultinnerobb; - - if( it->value.HasMember("geometryInfos") ) { - boost::shared_ptr pr(new rapidjson::Document());; - mujinjson::SaveJsonValue(*pr, it->value["geometryInfos"]); - mrGeometryInfos[objname] = pr; - } - - LoadJsonValueByKey(it->value, "uri", muri[objname]); - } - - const rapidjson::Value& sensors = output["sensors"]; - for (rapidjson::Document::ConstMemberIterator it = sensors.MemberBegin(); it != sensors.MemberEnd(); it++) { - mujin::SensorSelectionInfo sensorSelectionInfo; - LoadJsonValue(it->name, sensorSelectionInfo.sensorName); - for (rapidjson::Document::ConstMemberIterator itlink = it->value.MemberBegin(); itlink != it->value.MemberEnd(); itlink++) { - LoadJsonValue(itlink->name, sensorSelectionInfo.sensorLinkName); - Transform transform; - RobotResource::AttachedSensorResource::SensorData sensordata; - LoadJsonValueByKey(itlink->value, "translation", transform.translate); - LoadJsonValueByKey(itlink->value, "quaternion", transform.quaternion); - - const rapidjson::Value &sensor = itlink->value["sensordata"]; - LoadJsonValueByKey(sensor, "distortion_coeffs", sensordata.distortion_coeffs); - LoadJsonValueByKey(sensor, "intrinsic", sensordata.intrinsic); - - std::vector imagedimensions; - LoadJsonValueByKey(sensor, "image_dimensions", imagedimensions); - if (imagedimensions.size() == 2) { - imagedimensions.push_back(1); - } - if (imagedimensions.size() != 3) { - throw MujinException("the length of image_dimensions is invalid", MEC_Failed); - } - for (int i = 0; i < 3; i++) { - sensordata.image_dimensions[i] = imagedimensions[i]; - } - - LoadJsonValueByKey(sensor, "extra_parameters", sensordata.extra_parameters); - LoadJsonValueByKey(sensor, "distortion_model", sensordata.distortion_model); - LoadJsonValueByKey(sensor, "focal_length", sensordata.focal_length); - LoadJsonValueByKey(sensor, "measurement_time", sensordata.measurement_time); - - msensortransform[sensorSelectionInfo] = transform; - msensordata[sensorSelectionInfo] = sensordata; - } - } -} - -BinPickingTaskResource::ResultGetBinpickingState::ResultGetBinpickingState() : - statusPickPlace(""), - statusDescPickPlace(""), - statusPhysics(""), - isDynamicEnvironmentStateEmpty(false), - pickAttemptFromSourceId(-1), - timestamp(0), - lastGrabbedTargetTimeStamp(0), - isGrabbingTarget(true), - isGrabbingLastTarget(true), - hasRobotExecutionStarted(false), - orderNumber(-1), - numLeftInOrder(-1), - numLeftInSupply(-1), - placedInDest(-1) -{ -} - -BinPickingTaskResource::ResultGetBinpickingState::~ResultGetBinpickingState() -{ -} - -void BinPickingTaskResource::ResultGetBinpickingState::Parse(const rapidjson::Value& pt) -{ - BOOST_ASSERT(pt.IsObject() && pt.HasMember("output")); - const rapidjson::Value& v = pt["output"]; - - statusPickPlace = GetJsonValueByKey(v, "statusPickPlace", "unknown"); - statusDescPickPlace = GetJsonValueByKey(v, "statusDescPickPlace", "unknown"); - cycleIndex = GetJsonValueByKey(v, "statusPickPlaceCycleIndex", ""); - statusPhysics = GetJsonValueByKey(v, "statusPhysics", "unknown"); - pickAttemptFromSourceId = GetJsonValueByKey(v, "pickAttemptFromSourceId", -1); - //isContainerEmptyMap.clear(); - //LoadJsonValueByKey(v, "isContainerEmptyMap", isContainerEmptyMap); - //lastInsideSourceTimeStamp = (unsigned long long)(GetJsonValueByKey(v, "lastInsideSourceTimeStamp", 0) * 1000.0); // s -> ms - //lastInsideDestTimeStamp = (unsigned long long)(GetJsonValueByKey(v, "lastInsideDestTimeStamp", 0) * 1000.0); // s -> ms - timestamp = (unsigned long long)(GetJsonValueByKey(v, "timestamp", 0) * 1000.0); // s -> ms - lastGrabbedTargetTimeStamp = (unsigned long long)(GetJsonValueByKey(v, "lastGrabbedTargetTimeStamp", 0) * 1000.0); // s -> ms - - vOcclusionResults.clear(); - const rapidjson::Value::ConstMemberIterator itOcclusionResults = v.FindMember("occlusionResults"); - if( itOcclusionResults != v.MemberEnd() && itOcclusionResults->value.IsArray() ) { - vOcclusionResults.resize(itOcclusionResults->value.Size()); - for(int iocc = 0; iocc < (int)vOcclusionResults.size(); ++iocc) { - const rapidjson::Value& result = itOcclusionResults->value[iocc]; - vOcclusionResults[iocc].sensorSelectionInfo = GetJsonValueByKey(result, "sensorSelectionInfo", mujin::SensorSelectionInfo()); - vOcclusionResults[iocc].bodyname = GetJsonValueByKey(result, "bodyname", std::string()); - vOcclusionResults[iocc].isocclusion = GetJsonValueByKey(result, "isocclusion", -1); - } - } - - isGrabbingTarget = GetJsonValueByKey(v, "isGrabbingTarget", true); - isGrabbingLastTarget = GetJsonValueByKey(v, "isGrabbingLastTarget", true); - hasRobotExecutionStarted = GetJsonValueByKey(v, "hasRobotExecutionStarted", false); - orderNumber = GetJsonValueByPath(v, "/orderstate/orderNumber", -1); - numLeftInOrder = GetJsonValueByPath(v, "/orderstate/numLeftInOrder", -1); - numLeftInSupply = GetJsonValueByPath(v, "/orderstate/numLeftInSupply", -1); - placedInDest = GetJsonValueByPath(v, "/orderstate/placedInDest", -1); - - registerMinViableRegionInfo.locationName = GetJsonValueByPath(v, "/registerMinViableRegionInfo/locationName", std::string()); - LoadJsonValueByPath(v, "/registerMinViableRegionInfo/translation_", registerMinViableRegionInfo.translation_); - LoadJsonValueByPath(v, "/registerMinViableRegionInfo/quat_", registerMinViableRegionInfo.quat_); - registerMinViableRegionInfo.objectWeight = GetJsonValueByPath(v, "/registerMinViableRegionInfo/objectWeight", 0); - registerMinViableRegionInfo.sensorTimeStampMS = GetJsonValueByPath(v, "/registerMinViableRegionInfo/sensorTimeStampMS", 0); - registerMinViableRegionInfo.robotDepartStopTimestamp = GetJsonValueByPath(v, "/registerMinViableRegionInfo/robotDepartStopTimestamp", 0); - registerMinViableRegionInfo.transferSpeedPostMult = GetJsonValueByPath(v, "/registerMinViableRegionInfo/transferSpeedPostMult", 1.0); - { - registerMinViableRegionInfo.graspModelInfo.SetNull(); - registerMinViableRegionInfo.graspModelInfo.GetAllocator().Clear(); - const rapidjson::Value* graspModelInfoJson = rapidjson::Pointer("/registerMinViableRegionInfo/graspModelInfo").Get(v); - if( !!graspModelInfoJson && graspModelInfoJson->IsObject() ) { - registerMinViableRegionInfo.graspModelInfo.CopyFrom(*graspModelInfoJson, registerMinViableRegionInfo.graspModelInfo.GetAllocator()); - } - } - registerMinViableRegionInfo.minCornerVisibleDist = GetJsonValueByPath(v, "/registerMinViableRegionInfo/minCornerVisibleDist", 30); - registerMinViableRegionInfo.minCornerVisibleInsideDist = GetJsonValueByPath(v, "/registerMinViableRegionInfo/minCornerVisibleInsideDist", 0); - registerMinViableRegionInfo.maxCornerAngleDeviation = GetJsonValueByPath(v, "/registerMinViableRegionInfo/maxCornerAngleDeviation", 0); - LoadJsonValueByPath(v, "/registerMinViableRegionInfo/minViableRegion/size2D", registerMinViableRegionInfo.minViableRegion.size2D); - LoadJsonValueByPath(v, "/registerMinViableRegionInfo/minViableRegion/maxPossibleSize", registerMinViableRegionInfo.minViableRegion.maxPossibleSize); - LoadJsonValueByPath(v, "/registerMinViableRegionInfo/minViableRegion/maxPossibleSizeOriginal", registerMinViableRegionInfo.minViableRegion.maxPossibleSizeOriginal, registerMinViableRegionInfo.minViableRegion.maxPossibleSize); - registerMinViableRegionInfo.minViableRegion.cornerMask = GetJsonValueByPath(v, "/registerMinViableRegionInfo/minViableRegion/cornerMask", 0); - registerMinViableRegionInfo.minViableRegion.cornerMaskOriginal = GetJsonValueByPath(v, "/registerMinViableRegionInfo/minViableRegion/cornerMaskOriginal", 0); - registerMinViableRegionInfo.occlusionFreeCornerMask = GetJsonValueByPath(v, "/registerMinViableRegionInfo/occlusionFreeCornerMask", 0); - const uint8_t registrationMode = GetJsonValueByPath(v, "/registerMinViableRegionInfo/registrationMode", MVRRM_Drag); - if( registrationMode > MVRRM_PerpendicularDrag ) { - throw MujinException(str(boost::format("got unexpected value %d when receiving /registerMinViableRegionInfo/registrationMode. assuming 'drag'")%(int)registrationMode), MEC_InvalidArguments); - } - else { - registerMinViableRegionInfo.registrationMode = static_cast(registrationMode); - } - registerMinViableRegionInfo.maxPossibleSizePadding = GetJsonValueByPath(v, "/registerMinViableRegionInfo/maxPossibleSizePadding", 30); - registerMinViableRegionInfo.skipAppendingToObjectSet = GetJsonValueByPath(v, "/registerMinViableRegionInfo/skipAppendingToObjectSet", false); - LoadJsonValueByPath(v, "/registerMinViableRegionInfo/liftedWorldOffset", registerMinViableRegionInfo.liftedWorldOffset); - LoadJsonValueByPath(v, "/registerMinViableRegionInfo/minCandidateSize", registerMinViableRegionInfo.minCandidateSize); - LoadJsonValueByPath(v, "/registerMinViableRegionInfo/maxCandidateSize", registerMinViableRegionInfo.maxCandidateSize); - LoadJsonValueByPath(v, "/registerMinViableRegionInfo/fullDofValues", registerMinViableRegionInfo.fullDofValues); - LoadJsonValueByPath(v, "/registerMinViableRegionInfo/connectedBodyActiveStates", registerMinViableRegionInfo.connectedBodyActiveStates); - - removeObjectFromObjectListInfos.clear(); - if( v.HasMember("removeObjectsFromObjectList") && v["removeObjectsFromObjectList"].IsArray()) { - const rapidjson::Value& rRemoveObjectFromObjectList = v["removeObjectsFromObjectList"]; - removeObjectFromObjectListInfos.resize(rRemoveObjectFromObjectList.Size()); - for(int iitem = 0; iitem < (int)removeObjectFromObjectListInfos.size(); ++iitem) { - const rapidjson::Value& rInfo = rRemoveObjectFromObjectList[iitem]; - removeObjectFromObjectListInfos[iitem].timestamp = GetJsonValueByKey(rInfo, "timestamp", 0); - removeObjectFromObjectListInfos[iitem].objectPk = GetJsonValueByKey(rInfo, "objectPk", std::string()); - } - } - - triggerDetectionCaptureInfo.timestamp = GetJsonValueByPath(v, "/triggerDetectionCaptureInfo/timestamp", 0); - triggerDetectionCaptureInfo.triggerType = GetJsonValueByPath(v, "/triggerDetectionCaptureInfo/triggerType", ""); - triggerDetectionCaptureInfo.locationName = GetJsonValueByPath(v, "/triggerDetectionCaptureInfo/locationName", ""); - triggerDetectionCaptureInfo.targetupdatename = GetJsonValueByPath(v, "/triggerDetectionCaptureInfo/targetupdatename", ""); - - activeLocationTrackingInfos.clear(); - if( v.HasMember("activeLocationTrackingInfos") && v["activeLocationTrackingInfos"].IsArray()) { - const rapidjson::Value& rLocationTrackingInfo = v["activeLocationTrackingInfos"]; - activeLocationTrackingInfos.resize(rLocationTrackingInfo.Size()); - for(int iitem = 0; iitem < (int)rLocationTrackingInfo.Size(); ++iitem) { - const rapidjson::Value& rInfo = rLocationTrackingInfo[iitem]; - activeLocationTrackingInfos[iitem].locationName = GetJsonValueByKey(rInfo, "locationName", std::string()); - activeLocationTrackingInfos[iitem].containerId = GetJsonValueByKey(rInfo, "containerId", std::string()); - activeLocationTrackingInfos[iitem].containerName = GetJsonValueByKey(rInfo, "containerName", std::string()); - activeLocationTrackingInfos[iitem].containerUsage = GetJsonValueByKey(rInfo, "containerUsage", std::string()); - activeLocationTrackingInfos[iitem].cycleIndex = GetJsonValueByKey(rInfo, "cycleIndex", std::string()); - } - } - - locationExecutionInfos.clear(); - if( v.HasMember("locationExecutionInfos") && v["locationExecutionInfos"].IsArray()) { - const rapidjson::Value& rLocationTrackingInfo = v["locationExecutionInfos"]; - locationExecutionInfos.resize(rLocationTrackingInfo.Size()); - for(int iitem = 0; iitem < (int)rLocationTrackingInfo.Size(); ++iitem) { - const rapidjson::Value& rInfo = rLocationTrackingInfo[iitem]; - locationExecutionInfos[iitem].locationName = GetJsonValueByKey(rInfo, "locationName", std::string()); - locationExecutionInfos[iitem].containerId = GetJsonValueByKey(rInfo, "containerId", std::string()); - locationExecutionInfos[iitem].forceRequestStampMS = GetJsonValueByKey(rInfo, "forceRequestStampMS", 0); - locationExecutionInfos[iitem].lastInsideContainerStampMS = GetJsonValueByKey(rInfo, "lastInsideContainerStampMS", 0); - locationExecutionInfos[iitem].needContainerState = GetJsonValueByKey(rInfo, "needContainerState", std::string()); - } - } - - pickPlaceHistoryItems.clear(); - if( v.HasMember("pickPlaceHistoryItems") && v["pickPlaceHistoryItems"].IsArray() ) { - pickPlaceHistoryItems.resize(v["pickPlaceHistoryItems"].Size()); - for(int iitem = 0; iitem < (int)pickPlaceHistoryItems.size(); ++iitem) { - const rapidjson::Value& rItem = v["pickPlaceHistoryItems"][iitem]; - pickPlaceHistoryItems[iitem].pickPlaceType = GetJsonValueByKey(rItem, "pickPlaceType", std::string()); - pickPlaceHistoryItems[iitem].locationName = GetJsonValueByKey(rItem, "locationName", std::string()); - pickPlaceHistoryItems[iitem].eventTimeStampUS = GetJsonValueByKey(rItem, "eventTimeStampUS", 0); - pickPlaceHistoryItems[iitem].object_uri = GetJsonValueByKey(rItem, "object_uri", std::string()); - - pickPlaceHistoryItems[iitem].objectpose = Transform(); - const rapidjson::Value::ConstMemberIterator itPose = rItem.FindMember("objectpose"); - if( itPose != rItem.MemberEnd() ) { - const rapidjson::Value& rObjectPose = itPose->value;; - if( rObjectPose.IsArray() && rObjectPose.Size() == 7 ) { - LoadJsonValue(rObjectPose[0], pickPlaceHistoryItems[iitem].objectpose.quaternion[0]); - LoadJsonValue(rObjectPose[1], pickPlaceHistoryItems[iitem].objectpose.quaternion[1]); - LoadJsonValue(rObjectPose[2], pickPlaceHistoryItems[iitem].objectpose.quaternion[2]); - LoadJsonValue(rObjectPose[3], pickPlaceHistoryItems[iitem].objectpose.quaternion[3]); - LoadJsonValue(rObjectPose[4], pickPlaceHistoryItems[iitem].objectpose.translate[0]); - LoadJsonValue(rObjectPose[5], pickPlaceHistoryItems[iitem].objectpose.translate[1]); - LoadJsonValue(rObjectPose[6], pickPlaceHistoryItems[iitem].objectpose.translate[2]); - } - } - - pickPlaceHistoryItems[iitem].localaabb = mujin::AABB(); - const rapidjson::Value::ConstMemberIterator itLocalAABB = rItem.FindMember("localaabb"); - if( itLocalAABB != rItem.MemberEnd() ) { - const rapidjson::Value& rLocalAABB = itLocalAABB->value; - LoadAABBFromJsonValue(rLocalAABB, pickPlaceHistoryItems[iitem].localaabb); - } - - pickPlaceHistoryItems[iitem].sensorTimeStampUS = GetJsonValueByKey(rItem, "sensorTimeStampUS", 0); - } - } -} - -BinPickingTaskResource::ResultGetBinpickingState::RegisterMinViableRegionInfo::RegisterMinViableRegionInfo() : - objectWeight(0.0), - sensorTimeStampMS(0), - robotDepartStopTimestamp(0), - transferSpeedPostMult(1.0), - minCornerVisibleDist(30), - minCornerVisibleInsideDist(0), - occlusionFreeCornerMask(0), - skipAppendingToObjectSet(false), - maxPossibleSizePadding(30) -{ - translation_.fill(0); - quat_.fill(0); - liftedWorldOffset.fill(0); - maxCandidateSize.fill(0); - minCandidateSize.fill(0); -} - -BinPickingTaskResource::ResultGetBinpickingState::RegisterMinViableRegionInfo::MinViableRegionInfo::MinViableRegionInfo() : - cornerMask(0) -{ - size2D.fill(0); - maxPossibleSize.fill(0); - maxPossibleSizeOriginal.fill(0); -} - -BinPickingTaskResource::ResultGetBinpickingState::RemoveObjectFromObjectListInfo::RemoveObjectFromObjectListInfo() : - timestamp(0) -{ -} - -BinPickingTaskResource::ResultGetBinpickingState::TriggerDetectionCaptureInfo::TriggerDetectionCaptureInfo() : - timestamp(0) -{ -} - -BinPickingTaskResource::ResultIsRobotOccludingBody::~ResultIsRobotOccludingBody() -{ -} - -void BinPickingTaskResource::ResultIsRobotOccludingBody::Parse(const rapidjson::Value& pt) -{ - BOOST_ASSERT(pt.IsObject() && pt.HasMember("output")); - const rapidjson::Value& v = pt["output"]; - if (!v.IsObject() || !v.HasMember("occluded")) { - throw MujinException("Output does not have \"occluded\" attribute!", MEC_Failed); - } - result = GetJsonValueByKey(v, "occluded", 1) == 1; -} - -void BinPickingTaskResource::ResultGetPickedPositions::Parse(const rapidjson::Value& pt) -{ - BOOST_ASSERT(pt.IsObject() && pt.HasMember("output")); - const rapidjson::Value& v = pt["output"]; - for (rapidjson::Document::ConstMemberIterator value = v.MemberBegin(); value != v.MemberEnd(); ++value) { - if (std::string(value->name.GetString()) == "positions" && value->value.IsArray()) { - for (rapidjson::Document::ConstValueIterator it = value->value.Begin(); it != value->value.End(); ++it) { - if (it->IsArray() && it->Size() >= 8) { - Transform transform; - for (int i = 0; i < 4; i++) { - transform.quaternion[i] = (*it)[i].GetDouble(); - } - for (int i = 0; i < 3; i++) { - transform.translate[i] = (*it)[i + 4].GetDouble(); - } - transforms.push_back(transform); - timestamps.push_back((*it)[7].GetInt64()); - } - } - } - } -} - -void BinPickingTaskResource::ResultAABB::Parse(const rapidjson::Value& pt) -{ - BOOST_ASSERT(pt.IsObject() && pt.HasMember("output")); - const rapidjson::Value& v = pt["output"]; - LoadJsonValueByKey(v, "pos", pos); - LoadJsonValueByKey(v, "extents", extents); - if (pos.size() != 3) { - throw MujinException("The length of pos is invalid.", MEC_Failed); - } - if (extents.size() != 3) { - throw MujinException("The length of extents is invalid.", MEC_Failed); - } -} - -void BinPickingTaskResource::ResultOBB::Parse(const rapidjson::Value& pt) -{ - const rapidjson::Value& v = (pt.IsObject()&&pt.HasMember("output") ? pt["output"] : pt); - - LoadJsonValueByKey(v, "translation", translation); - LoadJsonValueByKey(v, "extents", extents); - std::vector > rotationmatrix2d; - LoadJsonValueByKey(v, "rotationmat", rotationmatrix2d); - if (translation.size() != 3) { - throw MujinException("The length of translation is invalid.", MEC_Failed); - } - if (extents.size() != 3) { - throw MujinException("The length of extents is invalid.", MEC_Failed); - } - if (rotationmatrix2d.size() != 3 || rotationmatrix2d[0].size() != 3 || rotationmatrix2d[1].size() != 3 || rotationmatrix2d[2].size() != 3) { - throw MujinException("The row number of rotationmat is invalid.", MEC_Failed); - } - - rotationmat.resize(9); - for (int i = 0; i < 3; i++) { - for (int j = 0; j < 3; j++) { - rotationmat[i*3+j] = rotationmatrix2d[i][j]; - } - } - - LoadJsonValueByKey(v, "quaternion", quaternion); -} - -void BinPickingTaskResource::ResultComputeIkParamPosition::Parse(const rapidjson::Value& pt) -{ - BOOST_ASSERT(pt.IsObject() && pt.HasMember("output")); - const rapidjson::Value& v = pt["output"]; - LoadJsonValueByKey(v, "translation", translation); - LoadJsonValueByKey(v, "quaternion", quaternion); - LoadJsonValueByKey(v, "direction", direction); - LoadJsonValueByKey(v, "angleXZ", angleXZ); - LoadJsonValueByKey(v, "angleYX", angleYX); - LoadJsonValueByKey(v, "angleZY", angleZY); - LoadJsonValueByKey(v, "angleX", angleX); - LoadJsonValueByKey(v, "angleY", angleY); - LoadJsonValueByKey(v, "angleZ", angleZ); - - if (translation.size() != 3) { - throw MujinException("The length of translation is invalid.", MEC_Failed); - } - if (quaternion.size() != 4) { - throw MujinException("The length of quaternion is invalid.", MEC_Failed); - } - if (direction.size() != 3) { - throw MujinException("The length of direction is invalid.", MEC_Failed); - } -} - -void BinPickingTaskResource::ResultComputeIKFromParameters::Parse(const rapidjson::Value& pt) -{ - BOOST_ASSERT(pt.IsObject() && pt.HasMember("output")); - const rapidjson::Value& v_ = pt["output"]; - BOOST_ASSERT(v_.IsObject() && v_.HasMember("solutions")); - const rapidjson::Value& v = v_["solutions"]; - for (rapidjson::Document::ConstValueIterator it = v.Begin(); it != v.End(); ++it) { - std::vector dofvalues_; - std::string extra_; - LoadJsonValueByKey(*it, "dofvalues", dofvalues_); - extra_ = DumpJson((*it)["extra"]); - dofvalues.push_back(dofvalues_); - extra.push_back(extra_); - } -} - -BinPickingTaskResource::ResultHeartBeat::~ResultHeartBeat() -{ -} - -void BinPickingTaskResource::ResultHeartBeat::Parse(const rapidjson::Value& pt) -{ - status = ""; - msg = ""; - timestamp = 0; - LoadJsonValueByKey(pt, "status", status); - LoadJsonValueByKey(pt, "message",msg); - LoadJsonValueByKey(pt, "timestamp", timestamp); - if (pt.HasMember("slavestates") && _slaverequestid.size() > 0) { - rapidjson::Document d(rapidjson::kObjectType), taskstatejson; - std::string key = "slaverequestid-" + _slaverequestid; - //try { - if (pt["slavestates"].HasMember(key.c_str()) && pt["slavestates"][key.c_str()].HasMember("taskstate")) { - mujinjson::SaveJsonValue(taskstatejson, pt["slavestates"][key.c_str()]["taskstate"]); - SetJsonValueByKey(d, "output", taskstatejson); - taskstate.Parse(d); - } -// } -// catch (const std::exception& ex){ -// MUJIN_LOG_WARN("parsing heartbeat at " << timestamp ": " << ex.what()); -// } - } -} - -namespace { -void SetMapTaskParameters(std::stringstream &ss, const std::map ¶ms) -{ - ss << std::setprecision(std::numeric_limits::digits10+1); - ss.str(""); - ss.clear(); - ss << "{"; - FOREACHC(it, params) { - ss << "\"" << it->first << "\":" << it->second << ", "; - } -} - -void SerializeGetStateCommand(std::stringstream &ss, const std::map ¶ms, - const std::string &functionname, const std::string &tasktype, - const std::string &robotname, const std::string &unit, const double timeout) { - SetMapTaskParameters(ss, params); - - ss << GetJsonString("command", functionname) << ", "; - ss << GetJsonString("tasktype", tasktype) << ", "; - if (!robotname.empty()) { - ss << GetJsonString("robotname", robotname) << ", "; - } - ss << GetJsonString("unit", unit); - ss << "}"; -} - -void -GenerateMoveToolCommand(const std::string &movetype, const std::string &goaltype, const std::vector &goals, - const std::string &robotname, const std::string &toolname, const double robotspeed, - Real envclearance, std::stringstream &ss, - const std::map ¶ms) { - SetMapTaskParameters(ss, params); - ss << GetJsonString("command", movetype) << ", "; - ss << GetJsonString("goaltype", goaltype) << ", "; - if (!robotname.empty()) { - ss << GetJsonString("robotname", robotname) << ", "; - } - if (!toolname.empty()) { - ss << GetJsonString("toolname", toolname) << ", "; - } - if (robotspeed >= 0) { - ss << GetJsonString("robotspeed") << ": " << robotspeed << ", "; - } - if (envclearance >= 0) { - ss << GetJsonString("envclearance") << ": " << envclearance << ", "; - } - ss << GetJsonString("goals") << ": " << GetJsonString(goals); - ss << "}"; - -} - -void -GenerateMoveToolByIkParamCommand(const std::string &movetype, const std::string &instobjectname, const std::string &ikparamname, - const std::string &robotname, const std::string &toolname, const double robotspeed, - Real envclearance, std::stringstream &ss, - const std::map ¶ms) { - SetMapTaskParameters(ss, params); - ss << GetJsonString("command", movetype) << ", "; - ss << "\"goaltype\": null,"; - ss << GetJsonString("instobjectname", instobjectname) << ", "; - if (!robotname.empty()) { - ss << GetJsonString("robotname", robotname) << ", "; - } - if (!toolname.empty()) { - ss << GetJsonString("toolname", toolname) << ", "; - } - if (robotspeed >= 0) { - ss << GetJsonString("robotspeed") << ": " << robotspeed << ", "; - } - if (envclearance >= 0) { - ss << GetJsonString("envclearance") << ": " << envclearance << ", "; - } - ss << GetJsonString("ikparamname", ikparamname); - ss << "}"; - -} - -void SetTrajectory(const rapidjson::Value &pt, - std::string *pTraj) { - if (!(pt.IsObject() && pt.HasMember("output") && pt["output"].HasMember("trajectory"))) { - throw MujinException("trajectory is not available in output", MEC_Failed); - } - *pTraj = GetJsonValueByPath(pt, "/output/trajectory"); -} -} - -void BinPickingTaskResource::GetJointValues(ResultGetJointValues& result, const std::string& unit, const double timeout) -{ - SetMapTaskParameters(_ss, _mapTaskParameters); - std::string command = "GetJointValues"; - std::string robottype = "densowave"; - _ss << GetJsonString("command", command) << ", "; - _ss << GetJsonString("robottype", robottype) << ", "; - _ss << GetJsonString("unit", unit) << ", "; - _ss << GetJsonString("tasktype", _tasktype); - _ss << "}"; - - rapidjson::Document pt(rapidjson::kObjectType); - ExecuteCommand(_ss.str(), pt, timeout); - result.Parse(pt); -} - -void BinPickingTaskResource::SetInstantaneousJointValues(const std::vector& jointvalues, const std::string& unit, const double timeout) -{ - rapidjson::Document pt(rapidjson::kObjectType); - { - for(std::map::iterator it=_mapTaskParameters.begin(); it!=_mapTaskParameters.end(); ++it) { - rapidjson::Document t; - ParseJson(t,it->second); - SetJsonValueByKey(pt,it->first,t); - } - } - SetJsonValueByKey(pt,"command","SetInstantaneousJointValues"); - SetJsonValueByKey(pt,"tasktype",_tasktype); - SetJsonValueByKey(pt,"jointvalues",jointvalues); - SetJsonValueByKey(pt,"unit",unit); - rapidjson::Document d; - ExecuteCommand(DumpJson(pt), d, timeout); // need to check return code -} - -void BinPickingTaskResource::ComputeIkParamPosition(ResultComputeIkParamPosition& result, const std::string& name, const std::string& unit, const double timeout) -{ - rapidjson::Document pt(rapidjson::kObjectType); - { - for(std::map::iterator it=_mapTaskParameters.begin(); it!=_mapTaskParameters.end(); ++it) { - rapidjson::Document t; - ParseJson(t,it->second); - SetJsonValueByKey(pt,it->first,t); - } - } - SetJsonValueByKey(pt,"command","ComputeIkParamPosition"); - SetJsonValueByKey(pt,"tasktype",_tasktype); - SetJsonValueByKey(pt,"name",name); - SetJsonValueByKey(pt,"unit",unit); - rapidjson::Document d; - ExecuteCommand(DumpJson(pt), d, timeout); - result.Parse(d); -} - -void BinPickingTaskResource::ComputeIKFromParameters(ResultComputeIKFromParameters& result, const std::string& targetname, const std::vector& ikparamnames, const int filteroptions, const int limit, const double timeout) -{ - rapidjson::Document pt(rapidjson::kObjectType); - { - for(std::map::iterator it=_mapTaskParameters.begin(); it!=_mapTaskParameters.end(); ++it) { - rapidjson::Document t; - ParseJson(t,it->second); - SetJsonValueByKey(pt,it->first,t); - } - } - SetJsonValueByKey(pt,"command","ComputeIKFromParameters"); - SetJsonValueByKey(pt,"tasktype",_tasktype); - SetJsonValueByKey(pt,"targetname",targetname); - SetJsonValueByKey(pt,"ikparamnames",ikparamnames); - SetJsonValueByKey(pt,"filteroptions",filteroptions); // 0 - SetJsonValueByKey(pt,"limit",limit); // 0 - rapidjson::Document d; - ExecuteCommand(DumpJson(pt), d, timeout); - result.Parse(d); -} - -void BinPickingTaskResource::MoveJoints(const std::vector& goaljoints, const std::vector& jointindices, const Real envclearance, const Real speed, ResultMoveJoints& result, const double timeout, std::string* pTraj) -{ - _mapTaskParameters["execute"] = !!pTraj ? "0" : "1"; - SetMapTaskParameters(_ss, _mapTaskParameters); - - std::string command = "MoveJoints"; - std::string robottype = "densowave"; - _ss << GetJsonString("command", command) << ", "; - _ss << GetJsonString("robottype", robottype) << ", "; - _ss << GetJsonString("tasktype", _tasktype) << ", "; - _ss << GetJsonString("goaljoints") << ": " << GetJsonString(goaljoints) << ", "; - _ss << GetJsonString("jointindices") << ": " << GetJsonString(jointindices) << ", "; - _ss << GetJsonString("envclearance",envclearance ) << ", "; - _ss << GetJsonString("speed", speed); - _ss << "}"; - - rapidjson::Document pt(rapidjson::kObjectType); - ExecuteCommand(_ss.str(), pt, timeout); - result.Parse(pt); - if (!!pTraj) { - SetTrajectory(pt, pTraj); - } -} - -void BinPickingTaskResource::GetTransform(const std::string& targetname, Transform& transform, const std::string& unit, const double timeout) -{ - SetMapTaskParameters(_ss, _mapTaskParameters); - std::string command = "GetTransform"; - _ss << GetJsonString("command", command) << ", "; - _ss << GetJsonString("targetname", targetname) << ", "; - _ss << GetJsonString("tasktype", _tasktype) << ", "; - _ss << GetJsonString("unit", unit); - _ss << "}"; - ResultTransform result; - rapidjson::Document pt(rapidjson::kObjectType); - ExecuteCommand(_ss.str(), pt, timeout); - result.Parse(pt); - transform = result.transform; -} - -void BinPickingTaskResource::SendMVRRegistrationResult( - const rapidjson::Document &mvrResultInfo, - double timeout) -{ - SetMapTaskParameters(_ss, _mapTaskParameters); - _ss << GetJsonString("command", "SendMVRRegistrationResult") << ", "; - _ss << GetJsonString("mvrResultInfo", DumpJson(mvrResultInfo)) << ", "; - _ss << "}"; - rapidjson::Document pt(rapidjson::kObjectType); - ExecuteCommand(_ss.str(), pt, timeout); - -} - -void BinPickingTaskResource::SendRemoveObjectsFromObjectListResult( - const std::vector& removeObjectFromObjectListInfos, - const bool success, - const double timeout) -{ - SetMapTaskParameters(_ss, _mapTaskParameters); - _ss << GetJsonString("command", "SendRemoveObjectsFromObjectListResult") << ", "; - _ss << GetJsonString("objectPks") << ": ["; - for (size_t iInfo = 0; iInfo < removeObjectFromObjectListInfos.size(); ++iInfo) { - _ss << GetJsonString(removeObjectFromObjectListInfos[iInfo].objectPk); - if (iInfo != removeObjectFromObjectListInfos.size() - 1) { - _ss << ", "; - } - } - _ss << "], "; - _ss << GetJsonString("success", success) << ", "; - _ss << "}"; - rapidjson::Document pt(rapidjson::kObjectType); - ExecuteCommand(_ss.str(), pt, timeout); -} - - -void BinPickingTaskResource::SendTriggerDetectionCaptureResult(const std::string& triggerType, const std::string& returnCode, double timeout) -{ - SetMapTaskParameters(_ss, _mapTaskParameters); - _ss << GetJsonString("command", "SendTriggerDetectionCaptureResult") << ", "; - _ss << GetJsonString("triggerType", triggerType) << ", "; - _ss << GetJsonString("returnCode", returnCode) << ", "; - _ss << "}"; - rapidjson::Document pt(rapidjson::kObjectType); - ExecuteCommand(_ss.str(), pt, timeout); -} - -void BinPickingTaskResource::SetTransform(const std::string& targetname, const Transform &transform, const std::string& unit, const double timeout) -{ - SetMapTaskParameters(_ss, _mapTaskParameters); - std::string command = "SetTransform"; - _ss << GetJsonString("command", command) << ", "; - _ss << GetJsonString("targetname", targetname) << ", "; - _ss << GetJsonString("tasktype", _tasktype) << ", "; - _ss << GetJsonString(transform) << ", "; - _ss << GetJsonString("unit", unit); - _ss << "}"; - rapidjson::Document d; - ExecuteCommand(_ss.str(), d, timeout); // need to check return code -} - -void BinPickingTaskResource::GetManipTransformToRobot(Transform& transform, const std::string& unit, const double timeout) -{ - SetMapTaskParameters(_ss, _mapTaskParameters); - std::string command = "GetManipTransformToRobot"; - _ss << GetJsonString("command", command) << ", "; - _ss << GetJsonString("tasktype", _tasktype) << ", "; - _ss << GetJsonString("unit", unit); - _ss << "}"; - ResultTransform result; - rapidjson::Document pt(rapidjson::kObjectType); - ExecuteCommand(_ss.str(), pt, timeout); - result.Parse(pt); - transform = result.transform; -} - -void BinPickingTaskResource::GetManipTransform(Transform& transform, const std::string& unit, const double timeout) -{ - SetMapTaskParameters(_ss, _mapTaskParameters); - std::string command = "GetManipTransform"; - _ss << GetJsonString("command", command) << ", "; - _ss << GetJsonString("tasktype", _tasktype) << ", "; - _ss << GetJsonString("unit", unit); - _ss << "}"; - ResultTransform result; - rapidjson::Document pt(rapidjson::kObjectType); - ExecuteCommand(_ss.str(), pt, timeout); - result.Parse(pt); - transform = result.transform; -} - -void BinPickingTaskResource::GetAABB(const std::string& targetname, ResultAABB& result, const std::string& unit, const double timeout) -{ - SetMapTaskParameters(_ss, _mapTaskParameters); - std::string command = "GetAABB"; - _ss << GetJsonString("command", command) << ", "; - _ss << GetJsonString("targetname", targetname) << ", "; - _ss << GetJsonString("tasktype", _tasktype) << ", "; - _ss << GetJsonString("unit", unit); - _ss << "}"; - rapidjson::Document pt(rapidjson::kObjectType); - ExecuteCommand(_ss.str(), pt, timeout); - result.Parse(pt); -} - -void BinPickingTaskResource::GetInnerEmptyRegionOBB(ResultOBB& result, const std::string& targetname, const std::string& linkname, const std::string& unit, const double timeout) -{ - SetMapTaskParameters(_ss, _mapTaskParameters); - std::string command = "GetInnerEmptyRegionOBB"; - _ss << GetJsonString("command", command) << ", "; - _ss << GetJsonString("targetname", targetname) << ", "; - if (linkname != "") { - _ss << GetJsonString("linkname", linkname) << ", "; - } - _ss << GetJsonString("tasktype", _tasktype) << ", "; - _ss << GetJsonString("unit", unit); - _ss << "}"; - rapidjson::Document pt(rapidjson::kObjectType); - ExecuteCommand(_ss.str(), pt, timeout); - result.Parse(pt); -} - -void BinPickingTaskResource::GetOBB(ResultOBB& result, const std::string& targetname, const std::string& linkname, const std::string& unit, const double timeout) -{ - SetMapTaskParameters(_ss, _mapTaskParameters); - std::string command = "GetOBB"; - _ss << GetJsonString("command", command) << ", "; - _ss << GetJsonString("targetname", targetname) << ", "; - if (linkname != "") { - _ss << GetJsonString("linkname", linkname) << ", "; - } - _ss << GetJsonString("tasktype", _tasktype) << ", "; - _ss << GetJsonString("unit", unit); - _ss << "}"; - rapidjson::Document pt(rapidjson::kObjectType); - ExecuteCommand(_ss.str(), pt, timeout); - result.Parse(pt); -} - -void BinPickingTaskResource::InitializeZMQ(const double reinitializetimeout, const double timeout) -{ -#ifdef MUJIN_USEZMQ - if (!_pHeartbeatMonitorThread) { - _bShutdownHeartbeatMonitor = false; - if ( reinitializetimeout > 0) { - _pHeartbeatMonitorThread.reset(new boost::thread(boost::bind(&BinPickingTaskResource::_HeartbeatMonitorThread, this, reinitializetimeout, timeout))); - } - } -#endif -} - -void BinPickingTaskResource::UpdateObjects(const std::string& objectname, const std::vector& detectedobjects, const std::string& resultstate, const std::string& unit, const double timeout) -{ - SetMapTaskParameters(_ss, _mapTaskParameters); - static const std::string command = "UpdateObjects"; - _ss << GetJsonString("command", command) << ", "; - _ss << GetJsonString("tasktype", _tasktype) << ", "; - _ss << GetJsonString("objectname", objectname) << ", "; - _ss << GetJsonString("envstate") << ": ["; - for (unsigned int i=0; i&vpoints, const Real pointsize, const std::string& name, const unsigned long long starttimestamp, const unsigned long long endtimestamp, const bool executionverification, const std::string& unit, int isoccluded, const std::string& locationName, const double timeout, bool clampToContainer, CropContainerMarginsXYZXYZPtr pCropContainerMargins, AddPointOffsetInfoPtr pAddPointOffsetInfo) -{ - SetMapTaskParameters(_ss, _mapTaskParameters); - std::string command = "AddPointCloudObstacle"; - _ss << GetJsonString("command", command) << ", "; - _ss << GetJsonString("tasktype", _tasktype) << ", "; - _ss << GetJsonString("isoccluded", isoccluded) << ", "; - _ss << GetJsonString("locationName", locationName) << ", "; - _ss << GetJsonString("clampToContainer", clampToContainer) << ", "; - if( !!pCropContainerMargins ) { - _ss << "\"cropContainerMarginsXYZXYZ\":[" << pCropContainerMargins->minMargins[0] << ", " << pCropContainerMargins->minMargins[1] << ", " << pCropContainerMargins->minMargins[2] << ", " << pCropContainerMargins->maxMargins[0] << ", " << pCropContainerMargins->maxMargins[1] << ", " << pCropContainerMargins->maxMargins[2] << "], "; - } - if( !!pAddPointOffsetInfo ) { - _ss << "\"addPointOffsetInfo\":{"; - _ss << "\"zOffsetAtBottom\": " << pAddPointOffsetInfo->zOffsetAtBottom << ", "; - _ss << "\"zOffsetAtTop\": " << pAddPointOffsetInfo->zOffsetAtTop << ", "; - _ss << "\"use\": true"; - _ss << "}, "; - } - PointCloudObstacle pointcloudobstacle; - pointcloudobstacle.name = name; - pointcloudobstacle.pointsize = pointsize; - pointcloudobstacle.points = vpoints; - _ss << GetJsonString(pointcloudobstacle); - - // send timestamp regardless of the pointcloud definition - _ss << ", \"starttimestamp\": " << starttimestamp; - _ss << ", \"endtimestamp\": " << endtimestamp; - _ss << ", \"executionverification\": " << (int) executionverification; - - _ss << ", " << GetJsonString("unit", unit); - _ss << "}"; - rapidjson::Document rResult; - ExecuteCommand(_ss.str(), rResult, timeout); // need to check return code -} - -void BinPickingTaskResource::UpdateEnvironmentState(const std::string& objectname, const std::vector& detectedobjects, const std::vector& vpoints, const std::string& state, const Real pointsize, const std::string& pointcloudobstaclename, const std::string& unit, const double timeout, const std::string& locationName, const std::vector& cameranames, CropContainerMarginsXYZXYZPtr pCropContainerMargins) -{ - SetMapTaskParameters(_ss, _mapTaskParameters); - std::string command = "UpdateEnvironmentState"; - _ss << GetJsonString("command", command) << ", "; - _ss << GetJsonString("tasktype", _tasktype) << ", "; - _ss << GetJsonString("objectname", objectname) << ", "; - _ss << GetJsonString("locationName", locationName) << ", "; - _ss << "\"cameranames\":" << GetJsonString(cameranames) << ", "; - _ss << GetJsonString("envstate") << ": ["; - for (unsigned int i=0; iminMargins[0] << ", " << pCropContainerMargins->minMargins[1] << ", " << pCropContainerMargins->minMargins[2] << ", " << pCropContainerMargins->maxMargins[0] << ", " << pCropContainerMargins->maxMargins[1] << ", " << pCropContainerMargins->maxMargins[2] << "], "; - } - PointCloudObstacle pointcloudobstacle; - pointcloudobstacle.name = pointcloudobstaclename; - pointcloudobstacle.pointsize = pointsize; - pointcloudobstacle.points = vpoints; - _ss << GetJsonString(pointcloudobstacle); - - _ss << "}"; - rapidjson::Document d; - ExecuteCommand(_ss.str(),d, timeout); // need to check return code -} - -void BinPickingTaskResource::RemoveObjectsWithPrefix(const std::string& prefix, double timeout) -{ - SetMapTaskParameters(_ss, _mapTaskParameters); - std::string command = "RemoveObjectsWithPrefix"; - _ss << GetJsonString("command", command) << ", "; - _ss << GetJsonString("prefix", prefix); - _ss << "}"; - rapidjson::Document d; - ExecuteCommand(_ss.str(),d, timeout); - -} -void BinPickingTaskResource::VisualizePointCloud(const std::vector >&pointslist, const Real pointsize, const std::vector&names, const std::string& unit, const double timeout) -{ - SetMapTaskParameters(_ss, _mapTaskParameters); - std::string command = "VisualizePointCloud"; - _ss << GetJsonString("command", command) << ", "; - _ss << GetJsonString("tasktype", _tasktype) << ", "; - _ss << GetJsonString("pointslist") << ": ["; - for (unsigned int i=0; i< pointslist.size(); i++) { - _ss << GetJsonString(pointslist[i]); - if (iCallGet(str(boost::format("task/%s/result/?format=json&limit=1&optimization=None&fields=pk,errormessage")%GetPrimaryKey()), pt); - BinPickingResultResourcePtr result; - if(pt.IsObject() && pt.HasMember("objects") && pt["objects"].IsArray() && pt["objects"].Size() > 0) { - rapidjson::Value& objects = pt["objects"]; - std::string pk = GetJsonValueByKey(objects[0], "pk", pk); - result.reset(new BinPickingResultResource(controller, pk)); - std::string erroptional = GetJsonValueByKey(objects[0], "errormessage"); - if (erroptional.size() > 0 && erroptional != "succeed") { - throw MujinException(erroptional, MEC_BinPickingError); - } - } - return result; -} - -void BinPickingTaskResource::GetInstObjectAndSensorInfo(const std::vector& instobjectnames, const std::vector& sensorSelectionInfos, ResultGetInstObjectAndSensorInfo& result, const std::string& unit, const double timeout) -{ - SetMapTaskParameters(_ss, _mapTaskParameters); - std::string command = "GetInstObjectAndSensorInfo"; - _ss << GetJsonString("command", command) << ", "; - _ss << GetJsonString("tasktype", _tasktype) << ", "; - _ss << GetJsonString("instobjectnames") << ": " << GetJsonString(instobjectnames) << ", "; - _ss << GetJsonString("sensorSelectionInfos") << ": " << GetJsonString(sensorSelectionInfos) << ", "; - _ss << GetJsonString("unit", unit); - _ss << "}"; - rapidjson::Document pt(rapidjson::kObjectType); - ExecuteCommand(_ss.str(), pt, timeout); - try { - result.Parse(pt); - } - catch(const std::exception& ex) { - MUJIN_LOG_ERROR(str(boost::format("got error when parsing result. exception: %s result: %s")%ex.what()%DumpJson(pt))); - throw; - } -} - -void BinPickingTaskResource::GetInstObjectInfoFromURI(const std::string& objecturi, const Transform& instobjecttransform, ResultInstObjectInfo& result, const std::string& unit, const double timeout) -{ - SetMapTaskParameters(_ss, _mapTaskParameters); - std::string command = "GetInstObjectInfoFromURI"; - _ss << GetJsonString("command", command) << ", "; - _ss << GetJsonString("unit", unit) << ", "; - _ss << "\"objecturi\":" << GetJsonString(objecturi) << ", "; - _ss << "\"instobjectpose\":["; - _ss << instobjecttransform.quaternion[0] << ", " << instobjecttransform.quaternion[1] << ", " << instobjecttransform.quaternion[2] << ", " << instobjecttransform.quaternion[3] << ", "; - _ss << instobjecttransform.translate[0] << ", " << instobjecttransform.translate[1] << ", " << instobjecttransform.translate[2]; - _ss << "]"; - _ss << "}"; - rapidjson::Document pt(rapidjson::kObjectType); - ExecuteCommand(_ss.str(), pt, timeout); - try { - result.Parse(pt); - } - catch(const std::exception& ex) { - MUJIN_LOG_ERROR(str(boost::format("got error when parsing result: %s result: %s")%ex.what()%DumpJson(pt))); - throw; - } -} - -void BinPickingTaskResource::GetPublishedTaskState(ResultGetBinpickingState& result, const std::string& robotname, const std::string& unit, const double timeout) -{ - ResultGetBinpickingState taskstate; - { - boost::mutex::scoped_lock lock(_mutexTaskState); - taskstate =_taskstate; - } - - if (taskstate.timestamp == 0) { - if (_tasktype == "binpicking") { - MUJIN_LOG_INFO("Have not received published message yet, getting published state from GetBinpickingState()"); - GetBinpickingState(result, robotname, unit, timeout); - } - else { - MUJIN_LOG_INFO("Have not received published message yet, getting published state from GetITLState()"); - GetITLState(result, robotname, unit, timeout); - } - { - boost::mutex::scoped_lock lock(_mutexTaskState); - _taskstate = result; - } - } else { - result = taskstate; - } -} - -void BinPickingTaskResource::GetBinpickingState(ResultGetBinpickingState& result, const std::string& robotname, const std::string& unit, const double timeout) -{ - SerializeGetStateCommand(_ss, _mapTaskParameters, "GetState", _tasktype, robotname, unit, timeout); - rapidjson::Document pt(rapidjson::kObjectType); - ExecuteCommand(_ss.str(), pt, timeout); - result.Parse(pt); -} - -void BinPickingTaskResource::GetITLState(ResultGetBinpickingState& result, const std::string& robotname, const std::string& unit, const double timeout) -{ - SerializeGetStateCommand(_ss, _mapTaskParameters, "GetState", _tasktype, robotname, unit, timeout); - rapidjson::Document pt(rapidjson::kObjectType); - ExecuteCommand(_ss.str(), pt, timeout); - result.Parse(pt); -} - -void BinPickingTaskResource::SetJogModeVelocities(const std::string& jogtype, const std::vector& movejointsigns, const std::string& robotname, const std::string& toolname, const double robotspeed, const double robotaccelmult, const double timeout) -{ - SetMapTaskParameters(_ss, _mapTaskParameters); - _ss << GetJsonString("command", std::string("SetJogModeVelocities")) << ", "; - _ss << GetJsonString("jogtype", jogtype) << ", "; - - if (!robotname.empty()) { - _ss << GetJsonString("robotname", robotname) << ", "; - } - if (!toolname.empty()) { - _ss << GetJsonString("toolname", toolname) << ", "; - } - if (robotspeed >= 0) { - _ss << GetJsonString("robotspeed") << ": " << robotspeed << ", "; - } - if (robotaccelmult >= 0) { - _ss << GetJsonString("robotaccelmult") << ": " << robotaccelmult << ", "; - } - _ss << GetJsonString("movejointsigns") << ": " << GetJsonString(movejointsigns); - _ss << "}"; - //std::cout << "Sending\n" << _ss.str() << " from " << __func__ << std::endl; - rapidjson::Document d; - ExecuteCommand(_ss.str(), d, timeout); -} - -void BinPickingTaskResource::MoveToolLinear(const std::string& goaltype, const std::vector& goals, const std::string& robotname, const std::string& toolname, const double workspeedlin, const double workspeedrot, bool checkEndeffectorCollision, const double timeout, std::string* pTraj) -{ - _mapTaskParameters["execute"] = !!pTraj ? "0" : "1"; - - const std::string ignorethresh = checkEndeffectorCollision ? "0.0" : "1000.0"; // zero to not ignore collision at any time, large number (1000) to ignore collision of end effector for first and last part of trajectory as well as ignore collision of robot at initial part of trajectory - _mapTaskParameters["workignorefirstcollisionee"] = ignorethresh; - _mapTaskParameters["workignorelastcollisionee"] = ignorethresh; - _mapTaskParameters["workignorefirstcollision"] = ignorethresh; - if (workspeedlin > 0 && workspeedrot > 0) { - std::stringstream ss; - ss << "[" << workspeedrot << ", " << workspeedlin << "]"; - _mapTaskParameters["workspeed"] = ss.str(); - } - GenerateMoveToolCommand("MoveToolLinear", goaltype, goals, robotname, toolname, -1, -1, _ss, _mapTaskParameters); - - rapidjson::Document pt(rapidjson::kObjectType); - ExecuteCommand(_ss.str(), pt, timeout); - if (!!pTraj) { - SetTrajectory(pt, pTraj); - } -} - -void BinPickingTaskResource::MoveToolLinear(const std::string& instobjectname, const std::string& ikparamname, const std::string& robotname, const std::string& toolname, const double workspeedlin, const double workspeedrot, bool checkEndeffectorCollision, const double timeout, std::string* pTraj) -{ - _mapTaskParameters["execute"] = !!pTraj ? "0" : "1"; - - const std::string ignorethresh = checkEndeffectorCollision ? "0.0" : "1000.0"; // zero to not ignore collision at any time, large number (1000) to ignore collision of end effector for first and last part of trajectory as well as ignore collision of robot at initial part of trajectory - _mapTaskParameters["workignorefirstcollisionee"] = ignorethresh; - _mapTaskParameters["workignorelastcollisionee"] = ignorethresh; - _mapTaskParameters["workignorefirstcollision"] = ignorethresh; - if (workspeedlin > 0 && workspeedrot > 0) { - std::stringstream ss; - ss << "[" << workspeedrot << ", " << workspeedlin << "]"; - _mapTaskParameters["workspeed"] = ss.str(); - } - GenerateMoveToolByIkParamCommand("MoveToolLinear", instobjectname, ikparamname, robotname, toolname, -1, -1, _ss, _mapTaskParameters); - - rapidjson::Document pt(rapidjson::kObjectType); - ExecuteCommand(_ss.str(), pt, timeout); - if (!!pTraj) { - SetTrajectory(pt, pTraj); - } -} - -void BinPickingTaskResource::MoveToHandPosition(const std::string& goaltype, const std::vector& goals, const std::string& robotname, const std::string& toolname, const double robotspeed, const double timeout, Real envclearance, std::string* pTraj) -{ - _mapTaskParameters["execute"] = !!pTraj ? "0" : "1"; - - GenerateMoveToolCommand("MoveToHandPosition", goaltype, goals, robotname, toolname, robotspeed, envclearance, _ss, _mapTaskParameters); - - rapidjson::Document pt(rapidjson::kObjectType); - ExecuteCommand(_ss.str(), pt, timeout); - if (!!pTraj) { - SetTrajectory(pt, pTraj); - } -} - -void BinPickingTaskResource::MoveToHandPosition(const std::string& instobjectname, const std::string& ikparamname, const std::string& robotname, const std::string& toolname, const double robotspeed, const double timeout, Real envclearance, std::string* pTraj) -{ - _mapTaskParameters["execute"] = !!pTraj ? "0" : "1"; - - GenerateMoveToolByIkParamCommand("MoveToHandPosition", instobjectname, ikparamname, robotname, toolname, robotspeed, envclearance, _ss, _mapTaskParameters); - - rapidjson::Document pt(rapidjson::kObjectType); - ExecuteCommand(_ss.str(), pt, timeout); - if (!!pTraj) { - SetTrajectory(pt, pTraj); - } -} - -void BinPickingTaskResource::GetGrabbed(std::vector& grabbed, const std::string& robotname, const double timeout) -{ - grabbed.clear(); - rapidjson::Document pt(rapidjson::kObjectType); - { - for(std::map::iterator it=_mapTaskParameters.begin(); it!=_mapTaskParameters.end(); ++it) { - rapidjson::Document t; - ParseJson(t,it->second); - SetJsonValueByKey(pt,it->first,t); - } - } - SetJsonValueByKey(pt,"command","GetGrabbed"); - SetJsonValueByKey(pt,"tasktype",_tasktype); - if (!robotname.empty()) { - SetJsonValueByKey(pt, "robotname", robotname); - } - rapidjson::Document d; - ExecuteCommand(DumpJson(pt), d, timeout); // need to check return code - BOOST_ASSERT(d.IsObject() && d.HasMember("output")); - const rapidjson::Value& v = d["output"]; - if(v.HasMember("names") && !v["names"].IsNull()) { - LoadJsonValueByKey(v, "names", grabbed); - } -} - -void BinPickingTaskResource::ExecuteSingleXMLTrajectory(const std::string& trajectory, bool filterTraj, const double timeout) -{ - _ss.str(""); _ss.clear(); - SetMapTaskParameters(_ss, _mapTaskParameters); - _ss << GetJsonString("command", "ExecuteSingleXMLTrajectory") << ", " - << GetJsonString("filtertraj", filterTraj ? 1 : 0) << ", " - << GetJsonString("trajectory", trajectory) << "}"; - rapidjson::Document d; - ExecuteCommand(_ss.str(), d, timeout); -} - -void BinPickingTaskResource::Grab(const std::string& targetname, const std::string& robotname, const std::string& toolname, const double timeout) -{ - SetMapTaskParameters(_ss, _mapTaskParameters); - _ss << GetJsonString("command", "Grab") << ", "; - _ss << GetJsonString("targetname", targetname) << ", "; - if (!robotname.empty()) { - _ss << GetJsonString("robotname", robotname) << ", "; - } - if (!toolname.empty()) { - _ss << GetJsonString("toolname", toolname) << ", "; - } - _ss << "}"; - rapidjson::Document d; - ExecuteCommand(_ss.str(), d, timeout); -} - -void BinPickingTaskResource::Release(const std::string& targetname, const std::string& robotname, const std::string& toolname, const double timeout) -{ - SetMapTaskParameters(_ss, _mapTaskParameters); - _ss << GetJsonString("command", "Release") << ", "; - _ss << GetJsonString("targetname", targetname) << ", "; - if (!robotname.empty()) { - _ss << GetJsonString("robotname", robotname) << ", "; - } - if (!toolname.empty()) { - _ss << GetJsonString("toolname", toolname) << ", "; - } - _ss << "}"; - rapidjson::Document d; - ExecuteCommand(_ss.str(), d, timeout); -} - -void BinPickingTaskResource::GetRobotBridgeIOVariableString(const std::vector& ionames, std::vector& iovalues, const double timeout) -{ - SetMapTaskParameters(_ss, _mapTaskParameters); - _ss << GetJsonString("command", "GetRobotBridgeIOVariableString") << ", "; - _ss << "\"ionames\":" << GetJsonString(ionames); - _ss << "}"; - rapidjson::Document d; - ExecuteCommand(_ss.str(), d, timeout); - BOOST_ASSERT(d.IsObject() && d.HasMember("output")); - const rapidjson::Value& rIOOutputs = d["output"]; - - // process - iovalues.resize(ionames.size()); - for(size_t index = 0; index < ionames.size(); ++index) { - if( rIOOutputs.HasMember(ionames[index].c_str()) ) { - iovalues[index] = mujinjson::GetStringJsonValueByKey(rIOOutputs, ionames[index].c_str(), std::string()); - } - else { - MUJIN_LOG_DEBUG(str(boost::format("could not read ioname %s")%ionames[index])); - } - } -} - -const std::string& BinPickingTaskResource::GetSlaveRequestId() const -{ - return _slaverequestid; -} - -void BinPickingTaskResource::ExecuteCommand(const std::string& taskparameters, rapidjson::Document& rResult, const double timeout, const bool getresult) -{ - if (!_bIsInitialized) { - throw MujinException("BinPicking task is not initialized, please call Initialzie() first.", MEC_Failed); - } - - GETCONTROLLERIMPL(); - - std::stringstream ss; ss << std::setprecision(std::numeric_limits::digits10+1); - ss << "{\"tasktype\": \"" << _tasktype << "\", \"taskparameters\": " << taskparameters << ", "; - ss << "\"sceneparams\": " << _sceneparams_json << ", "; - ss << "\"userinfo\": " << _userinfo_json << ", "; - if (_slaverequestid != "") { - ss << GetJsonString("slaverequestid", _slaverequestid) << ", "; - } - ss << "\"stamp\": " << (GetMilliTime()*1e-3) << ", "; - ss << "\"callerid\": \"" << _GetCallerId() << "\""; - ss << "}"; - rapidjson::Document pt(rapidjson::kObjectType); - controller->CallPutJSON(str(boost::format("task/%s/?format=json")%GetPrimaryKey()), ss.str(), pt); - Execute(); - - double secondspassed = 0; - while (1) { - BinPickingResultResourcePtr resultresource; - resultresource = boost::dynamic_pointer_cast(GetResult()); - if( !!resultresource ) { - if (getresult) { - resultresource->GetResultJson(rResult); - } - return; - } - boost::this_thread::sleep(boost::posix_time::milliseconds(100)); - secondspassed+=0.1; - if( timeout != 0 && secondspassed > timeout ) { - controller->CancelAllJobs(); - std::stringstream sss; sss << std::setprecision(std::numeric_limits::digits10+1); - sss << secondspassed; - throw MujinException("operation timed out after " +sss.str() + " seconds, cancelling all jobs and quitting", MEC_Timeout); - } - } -} - -void BinPickingTaskResource::ExecuteCommand(rapidjson::Value& rTaskParameters, rapidjson::Document& rOutput, const double timeout) -{ - BOOST_ASSERT(0); // not supported -} - -void utils::GetAttachedSensors(SceneResource& scene, const std::string& bodyname, std::vector& attachedsensors) -{ - SceneResource::InstObjectPtr sensorinstobject; - if (!scene.FindInstObject(bodyname, sensorinstobject)) { - throw MujinException("Could not find instobject with name: " + bodyname+".", MEC_Failed); - } - - RobotResourcePtr sensorrobot; - sensorrobot.reset(new RobotResource(scene.GetController(),sensorinstobject->object_pk)); - sensorrobot->GetAttachedSensors(attachedsensors); - if (attachedsensors.size() == 0) { - throw MujinException("Could not find attached sensor. Is calibration done for sensor: " + bodyname + "?", MEC_Failed); - } -} - -void utils::GetSensorData(SceneResource& scene, const std::string& bodyname, const std::string& sensorname, RobotResource::AttachedSensorResource::SensorData& result) -{ - std::vector attachedsensors; - utils::GetAttachedSensors(scene, bodyname, attachedsensors); - for (size_t i=0; iname == sensorname) { - result = attachedsensors.at(i)->sensordata; - return; - } - } - throw MujinException("Could not find attached sensor " + sensorname + " on " + bodyname + ".", MEC_Failed); -} - -void utils::GetSensorTransform(SceneResource& scene, const std::string& bodyname, const std::string& sensorname, Transform& result, const std::string& unit) -{ - std::vector attachedsensors; - utils::GetAttachedSensors(scene, bodyname, attachedsensors); - for (size_t i=0; iname == sensorname) { - Transform transform; - std::copy(attachedsensors.at(i)->quaternion, attachedsensors.at(i)->quaternion+4, transform.quaternion); - std::copy(attachedsensors.at(i)->translate, attachedsensors.at(i)->translate+3, transform.translate); - if (unit == "m") { //?! - transform.translate[0] *= 0.001; - transform.translate[1] *= 0.001; - transform.translate[2] *= 0.001; - } - - result = transform; - return; - } - } - throw MujinException("Could not find attached sensor " + sensorname + " on " + bodyname + ".", MEC_Failed); -} - -void utils::DeleteObject(SceneResource& scene, const std::string& name) -{ - //TODO needs to robot.Release(name) - std::vector instobjects; - scene.GetInstObjects(instobjects); - - for(unsigned int i = 0; i < instobjects.size(); ++i) { - const std::size_t found_at = instobjects[i]->name.find(name); - if (found_at != std::string::npos) { - instobjects[i]->Delete(); - break; - } - } -} - -void BinPickingTaskResource::_HeartbeatMonitorThread(const double reinitializetimeout, const double commandtimeout) -{ -#ifdef MUJIN_USEZMQ - boost::shared_ptr socket; - - while (!_bShutdownHeartbeatMonitor) { - if (!!socket) { - socket->close(); - socket.reset(); - } - socket.reset(new zmq::socket_t((*_zmqcontext.get()),ZMQ_SUB)); - socket->setsockopt(ZMQ_TCP_KEEPALIVE, 1); // turn on tcp keepalive, do these configuration before connect - socket->setsockopt(ZMQ_TCP_KEEPALIVE_IDLE, 2); // the interval between the last data packet sent (simple ACKs are not considered data) and the first keepalive probe; after the connection is marked to need keepalive, this counter is not used any further - socket->setsockopt(ZMQ_TCP_KEEPALIVE_INTVL, 2); // the interval between subsequential keepalive probes, regardless of what the connection has exchanged in the meantime - socket->setsockopt(ZMQ_TCP_KEEPALIVE_CNT, 2); // the number of unacknowledged probes to send before considering the connection dead and notifying the application layer - std::stringstream ss; ss << std::setprecision(std::numeric_limits::digits10+1); - ss << _heartbeatPort; - socket->connect(("tcp://"+ _mujinControllerIp+":"+ss.str()).c_str()); - socket->setsockopt(ZMQ_SUBSCRIBE, "", 0); - - zmq::pollitem_t pollitem; - memset(&pollitem, 0, sizeof(zmq::pollitem_t)); - pollitem.socket = socket->operator void*(); - pollitem.events = ZMQ_POLLIN; - - unsigned long long lastheartbeat = GetMilliTime(); - while (!_bShutdownHeartbeatMonitor && (GetMilliTime() - lastheartbeat) / 1000.0f < reinitializetimeout) { - zmq::poll(&pollitem,1, 50); // wait 50 ms for message - if (pollitem.revents & ZMQ_POLLIN) { - zmq::message_t reply; - socket->recv(&reply); - std::string replystring((char *)reply.data (), (size_t)reply.size()); - //if ((size_t)reply.size() == 1 && ((char *)reply.data())[0]==255) { - if (replystring == "255") { - lastheartbeat = GetMilliTime(); - } - } - } - if (!_bShutdownHeartbeatMonitor) { - std::stringstream sss; sss << std::setprecision(std::numeric_limits::digits10+1); - sss << (double)((GetMilliTime() - lastheartbeat)/1000.0f) << " seconds passed since last heartbeat signal, re-intializing ZMQ server."; - MUJIN_LOG_INFO(sss.str()); - } - } -#else - MUJIN_LOG_ERROR("cannot create heartbeat monitor since not compiled with libzmq"); -#endif -} - -#ifdef MUJIN_USEZMQ -std::string utils::GetHeartbeat(const std::string& endpoint) { - zmq::context_t zmqcontext(1); - zmq::socket_t socket(zmqcontext, ZMQ_SUB); - socket.connect(endpoint.c_str()); - socket.setsockopt(ZMQ_SUBSCRIBE, "", 0); - - zmq::pollitem_t pollitem; - memset(&pollitem, 0, sizeof(zmq::pollitem_t)); - pollitem.socket = socket; - pollitem.events = ZMQ_POLLIN; - - zmq::poll(&pollitem,1, 50); // wait 50 ms for message - if (!(pollitem.revents & ZMQ_POLLIN)) { - return ""; - } - - zmq::message_t reply; - socket.recv(&reply); - const std::string received((char *)reply.data (), (size_t)reply.size()); -#ifndef _WIN32 - return received; -#else - // sometimes buffer can container \n or \\, which windows does not like - std::string newbuffer; - std::vector< std::pair > serachpairs(2); - serachpairs[0].first = "\n"; serachpairs[0].second = ""; - serachpairs[1].first = "\\"; serachpairs[1].second = ""; - SearchAndReplace(newbuffer, received, serachpairs); - return newbuffer; -#endif -} - - -namespace { -std::string FindSmallestSlaveRequestId(const rapidjson::Value& pt) { - // get all slave request ids - std::vector slavereqids; - if (pt.IsObject() && pt.HasMember("slavestates")) { - const rapidjson::Value& slavestates = pt["slavestates"]; - for (rapidjson::Document::ConstMemberIterator it = slavestates.MemberBegin(); it != slavestates.MemberEnd(); ++it) { - static const std::string prefix("slaverequestid-"); - if (std::string(it->name.GetString()).substr(0,prefix.length()) == prefix) { - // GetValueForSmallestSlaveRequestId uses slavereqid directly, so cannot substr here - slavereqids.push_back(it->name.GetString()); - } - } - } - - // find numerically smallest suffix (find one with smallest ### in slave request id of form hostname_slave###) - int smallest_suffix_index = -1; - int smallest_suffix = INT_MAX; - static const char searchstr('_'); - for (std::vector::const_iterator it = slavereqids.begin(); - it != slavereqids.end(); ++it) { - const size_t foundindex = it->rfind(searchstr); - if (foundindex == std::string::npos) { - continue; - } - - if ((*it)[it->length()-1] < '0' || '9' < (*it)[it->length()-1]) { - continue; - } - int suffix = 0; - int po = 1; - for (int i = it->length()-1; i >= 0 && '0' <= (*it)[i] && (*it)[i] <= '9'; i--, po *= 10) { - suffix = suffix + ((*it)[i] - '0')*po; - } - - if (smallest_suffix > suffix) { - smallest_suffix = suffix; - smallest_suffix_index = std::distance::const_iterator>(slavereqids.begin(), it); - } - } - - if (smallest_suffix_index == -1) { - throw MujinException("Failed to find slave request id like hostname_slave### where ### is a number"); - } - return slavereqids[smallest_suffix_index]; -} - -std::string GetValueForSmallestSlaveRequestId(const std::string& heartbeat, - const std::string& key) -{ - - rapidjson::Document pt(rapidjson::kObjectType); - std::stringstream ss(heartbeat); - ParseJson(pt, ss.str()); - try { - const std::string slavereqid = FindSmallestSlaveRequestId(pt); - std::string result; - LoadJsonValue(pt["slavestates"][slavereqid.c_str()][key.c_str()], result); - return result; - } - catch (const MujinException& ex) { - throw MujinException(boost::str(boost::format("%s from heartbeat:\n%s")%ex.what()%heartbeat)); - } - -} -} - - -std::string mujinclient::utils::GetScenePkFromHeartbeat(const std::string& heartbeat) { - static const std::string prefix("mujin:/"); - return GetValueForSmallestSlaveRequestId(heartbeat, "currentsceneuri").substr(prefix.length()); -} - -std::string utils::GetSlaveRequestIdFromHeartbeat(const std::string& heartbeat) { - rapidjson::Document pt; - std::stringstream ss(heartbeat); - ParseJson(pt, ss.str()); - try { - static const std::string prefix("slaverequestid-"); - return FindSmallestSlaveRequestId(pt).substr(prefix.length()); - } - catch (const MujinException& ex) { - throw MujinException(boost::str(boost::format("%s from heartbeat:\n%s")%ex.what()%heartbeat)); - } -} -#endif - -} // end namespace mujinclient diff --git a/src/binpickingtaskzmq.cpp b/src/binpickingtaskzmq.cpp deleted file mode 100644 index a62aa88e..00000000 --- a/src/binpickingtaskzmq.cpp +++ /dev/null @@ -1,297 +0,0 @@ -// -*- coding: utf-8 -*- -// Copyright (C) 2012-2014 MUJIN Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT 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 "common.h" -#include "controllerclientimpl.h" -#include "binpickingtaskzmq.h" -#include "mujincontrollerclient/mujinzmq.h" - -#include // find - -#include "logging.h" -#include "mujincontrollerclient/mujinjson.h" - -MUJIN_LOGGER("mujin.controllerclientcpp.binpickingtask.zmq"); - -using namespace mujinzmq; - -namespace mujinclient { -using namespace utils; -using namespace mujinjson; - -class ZmqMujinControllerClient : public mujinzmq::ZmqClient -{ - -public: - ZmqMujinControllerClient(boost::shared_ptr context, const std::string& host, const int port); - - virtual ~ZmqMujinControllerClient(); - -}; - -ZmqMujinControllerClient::ZmqMujinControllerClient(boost::shared_ptr context, const std::string& host, const int port) : ZmqClient(host, port) -{ - _InitializeSocket(context); -} - -ZmqMujinControllerClient::~ZmqMujinControllerClient() -{ - // _DestroySocket() is called in ~ZmqClient() -} - -BinPickingTaskZmqResource::BinPickingTaskZmqResource(ControllerClientPtr c, const std::string& pk, const std::string& scenepk, const std::string& tasktype) : BinPickingTaskResource(c, pk, scenepk, tasktype) -{ - _callerid = str(boost::format("controllerclientcpp%s_zmq")%MUJINCLIENT_VERSION_STRING); -} - -BinPickingTaskZmqResource::~BinPickingTaskZmqResource() -{ -} - -void BinPickingTaskZmqResource::Initialize(const std::string& defaultTaskParameters, const int zmqPort, const int heartbeatPort, boost::shared_ptr zmqcontext, const bool initializezmq, const double reinitializetimeout, const double timeout, const std::string& userinfo, const std::string& slaverequestid) -{ - BinPickingTaskResource::Initialize(defaultTaskParameters, zmqPort, heartbeatPort, zmqcontext, initializezmq, reinitializetimeout, timeout, userinfo, slaverequestid); - - if (initializezmq) { - InitializeZMQ(reinitializetimeout, timeout); - } - - _zmqmujincontrollerclient.reset(new ZmqMujinControllerClient(_zmqcontext, _mujinControllerIp, _zmqPort)); - if (!_zmqmujincontrollerclient) { - throw MujinException(boost::str(boost::format("Failed to establish ZMQ connection to mujin controller at %s:%d")%_mujinControllerIp%_zmqPort), MEC_Failed); - } - if (!_pHeartbeatMonitorThread) { - _bShutdownHeartbeatMonitor = false; - if (reinitializetimeout > 0 ) { - _pHeartbeatMonitorThread.reset(new boost::thread(boost::bind(&BinPickingTaskZmqResource::_HeartbeatMonitorThread, this, reinitializetimeout, timeout))); - } - } -} - -void _LogTaskParametersAndThrow(const std::string& taskparameters) { - std::string errstr; - if (taskparameters.size()>1000) { - errstr = taskparameters.substr(0, 1000); - } else { - errstr = taskparameters; - } - throw MujinException(boost::str(boost::format("Timed out receiving response of command with taskparameters=%s...")%errstr)); -} - -void BinPickingTaskZmqResource::ExecuteCommand(const std::string& taskparameters, rapidjson::Document &pt, const double timeout /* [sec] */, const bool getresult) -{ - std::stringstream ss; ss << std::setprecision(std::numeric_limits::digits10+1); - ss << "{\"fnname\": \""; - ss << (_tasktype == "binpicking" ? "binpicking.RunCommand\", " : "RunCommand\", "); - - ss << "\"stamp\": " << (GetMilliTime()*1e-3) << ", "; - ss << "\"callerid\": \"" << _GetCallerId() << "\", "; - ss << "\"taskparams\": {\"tasktype\": \"" << _tasktype << "\", "; - - ss << "\"taskparameters\": " << taskparameters << ", "; - ss << "\"sceneparams\": " << _sceneparams_json << "}, "; - ss << "\"userinfo\": " << _userinfo_json; - if (_slaverequestid != "") { - ss << ", " << GetJsonString("slaverequestid", _slaverequestid); - } - ss << "}"; - std::string result_ss; - - try{ - _ExecuteCommandZMQ(ss.str(), pt, timeout, getresult); - } - catch (const MujinException& e) { - MUJIN_LOG_ERROR(e.what()); - if (e.GetCode() == MEC_Timeout) { - _LogTaskParametersAndThrow(taskparameters); - } - else { - throw; - } - } -} - -void BinPickingTaskZmqResource::ExecuteCommand(rapidjson::Value& rTaskParameters, rapidjson::Document& rOutput, const double timeout) -{ - rapidjson::Document rCommand; rCommand.SetObject(); - mujinjson::SetJsonValueByKey(rCommand, "fnname", _tasktype == "binpicking" ? "binpicking.RunCommand" : "RunCommand"); - mujinjson::SetJsonValueByKey(rCommand, "stamp", GetMilliTime()*1e-3); - mujinjson::SetJsonValueByKey(rCommand, "callerid", _GetCallerId()); - - { - rapidjson::Value rTaskParams; rTaskParams.SetObject(); - mujinjson::SetJsonValueByKey(rTaskParams, "tasktype", _tasktype, rCommand.GetAllocator()); - rTaskParams.AddMember(rapidjson::Document::StringRefType("taskparameters"), rTaskParameters, rCommand.GetAllocator()); - - { - rapidjson::Value rSceneParams; - rSceneParams.CopyFrom(_rSceneParams, rCommand.GetAllocator()); - rTaskParams.AddMember(rapidjson::Document::StringRefType("sceneparams"), rSceneParams, rCommand.GetAllocator()); - } - rCommand.AddMember(rapidjson::Document::StringRefType("taskparams"), rTaskParams, rCommand.GetAllocator()); - } - - { - rapidjson::Value rUserInfo; - rUserInfo.CopyFrom(_rUserInfo, rCommand.GetAllocator()); - rCommand.AddMember(rapidjson::Document::StringRefType("userinfo"), rUserInfo, rCommand.GetAllocator()); - } - - if (!_slaverequestid.empty()) { - mujinjson::SetJsonValueByKey(rCommand, "slaverequestid", _slaverequestid); - } - - try { - _ExecuteCommandZMQ(mujinjson::DumpJson(rCommand), rOutput, timeout); - } - catch (const MujinException& e) { - MUJIN_LOG_ERROR(e.what()); - if (e.GetCode() == MEC_Timeout) { - _LogTaskParametersAndThrow(mujinjson::DumpJson(rCommand["taskparams"])); - } - else { - throw; - } - } -} - -void BinPickingTaskZmqResource::_ExecuteCommandZMQ(const std::string& command, rapidjson::Document& rOutput, const double timeout, const bool getresult) -{ - if (!_bIsInitialized) { - throw MujinException("BinPicking task is not initialized, please call Initialzie() first.", MEC_Failed); - } - - if (!_zmqmujincontrollerclient) { - MUJIN_LOG_ERROR("zmqcontrollerclient is not initialized! initialize"); - _zmqmujincontrollerclient.reset(new ZmqMujinControllerClient(_zmqcontext, _mujinControllerIp, _zmqPort)); - } - - std::string result_ss; - try { - result_ss = _zmqmujincontrollerclient->Call(command, timeout); - } - catch (const MujinException& e) { - MUJIN_LOG_ERROR(e.what()); - if (e.GetCode() == MEC_ZMQNoResponse) { - MUJIN_LOG_INFO("reinitializing zmq connection with the slave"); - _zmqmujincontrollerclient.reset(new ZmqMujinControllerClient(_zmqcontext, _mujinControllerIp, _zmqPort)); - if (!_zmqmujincontrollerclient) { - throw MujinException(boost::str(boost::format("Failed to establish ZMQ connection to mujin controller at %s:%d")%_mujinControllerIp%_zmqPort), MEC_Failed); - } - } else if (e.GetCode() == MEC_Timeout) { - throw MujinException(""); // Filled by `ExecuteCommand` callers who can access taskparameters more easily - } - else { - throw; - } - } - - try { - ParseJson(rOutput, result_ss); - } - catch(const std::exception& ex) { - MUJIN_LOG_ERROR(str(boost::format("Could not parse result %s")%result_ss)); - throw; - } - if( rOutput.IsObject() && rOutput.HasMember("error")) { - std::string error = GetJsonValueByKey(rOutput["error"], "errorcode"); - std::string description = GetJsonValueByKey(rOutput["error"], "description"); - if ( error.size() > 0 ) { - std::string serror; - if ( description.size() > 0 ) { - serror = description; - } - else { - serror = error; - } - if( serror.size() > 1000 ) { - MUJIN_LOG_ERROR(str(boost::format("truncated original error message from %d")%serror.size())); - serror = serror.substr(0,1000); - } - throw MujinException(str(boost::format("Error when calling binpicking.RunCommand: %s")%serror), MEC_BinPickingError); - } - } -} - -void BinPickingTaskZmqResource::InitializeZMQ(const double reinitializetimeout, const double timeout) -{ -} - -void BinPickingTaskZmqResource::_HeartbeatMonitorThread(const double reinitializetimeout, const double commandtimeout) -{ - MUJIN_LOG_DEBUG(str(boost::format("starting controller %s monitoring thread on port %d for slaverequestid=%s.")%_mujinControllerIp%_heartbeatPort%_slaverequestid)); - boost::shared_ptr socket; - BinPickingTaskResource::ResultHeartBeat heartbeat; - heartbeat._slaverequestid = _slaverequestid; - while (!_bShutdownHeartbeatMonitor) { - if (!!socket) { - socket->close(); - socket.reset(); - } - socket.reset(new zmq::socket_t((*_zmqcontext.get()),ZMQ_SUB)); - socket->setsockopt(ZMQ_TCP_KEEPALIVE, 1); // turn on tcp keepalive, do these configuration before connect - socket->setsockopt(ZMQ_TCP_KEEPALIVE_IDLE, 2); // the interval between the last data packet sent (simple ACKs are not considered data) and the first keepalive probe; after the connection is marked to need keepalive, this counter is not used any further - socket->setsockopt(ZMQ_TCP_KEEPALIVE_INTVL, 2); // the interval between subsequential keepalive probes, regardless of what the connection has exchanged in the meantime - socket->setsockopt(ZMQ_TCP_KEEPALIVE_CNT, 2); // the number of unacknowledged probes to send before considering the connection dead and notifying the application layer - std::stringstream ss; ss << std::setprecision(std::numeric_limits::digits10+1); - ss << _heartbeatPort; - socket->connect (("tcp://"+ _mujinControllerIp+":"+ss.str()).c_str()); - socket->setsockopt(ZMQ_SUBSCRIBE, "", 0); - - zmq::pollitem_t pollitem; - memset(&pollitem, 0, sizeof(zmq::pollitem_t)); - pollitem.socket = socket->operator void*(); - pollitem.events = ZMQ_POLLIN; - unsigned long long lastheartbeat = GetMilliTime(); - while (!_bShutdownHeartbeatMonitor && (GetMilliTime() - lastheartbeat) / 1000.0f < reinitializetimeout) { - zmq::poll(&pollitem,1, 50); // wait 50 ms for message - if (pollitem.revents & ZMQ_POLLIN) { - zmq::message_t reply; - socket->recv(&reply); - std::string replystring((char *)reply.data (), (size_t)reply.size()); - rapidjson::Document pt(rapidjson::kObjectType); - try{ - std::stringstream replystring_ss(replystring); - ParseJson(pt, replystring_ss.str()); - heartbeat.Parse(pt); - { - boost::mutex::scoped_lock lock(_mutexTaskState); - _taskstate = heartbeat.taskstate; - } - //BINPICKING_LOG_ERROR(replystring); - - if (heartbeat.status != std::string("lost") && heartbeat.status.size() > 1) { - lastheartbeat = GetMilliTime(); - } - } - catch (std::exception const &e) { - MUJIN_LOG_ERROR("HeartBeat reply is not JSON"); - MUJIN_LOG_ERROR(replystring); - MUJIN_LOG_ERROR(e.what()); - continue; - } - } - } - if (!_bShutdownHeartbeatMonitor) { - std::stringstream sss; sss << std::setprecision(std::numeric_limits::digits10+1); - sss << (double)((GetMilliTime() - lastheartbeat)/1000.0f) << " seconds passed since last heartbeat signal, re-intializing ZMQ server."; - MUJIN_LOG_INFO(sss.str()); - } - } - MUJIN_LOG_DEBUG(str(boost::format("Stopped controller %s monitoring thread on port %d for slaverequestid=%s.")%_mujinControllerIp%_heartbeatPort%_slaverequestid)); -} - - - -} // end namespace mujinclient diff --git a/src/binpickingtaskzmq.h b/src/binpickingtaskzmq.h deleted file mode 100644 index 4a22ff90..00000000 --- a/src/binpickingtaskzmq.h +++ /dev/null @@ -1,53 +0,0 @@ -// -*- coding: utf-8 -*- -// Copyright (C) 2012-2014 MUJIN Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -/** \file mujincontrollerclient.h - \brief Defines the public headers of the MUJIN Controller Client - */ -#ifndef MUJIN_CONTROLLERCLIENT_BINPICKINGTASK_ZMQ_H -#define MUJIN_CONTROLLERCLIENT_BINPICKINGTASK_ZMQ_H - -#include "mujincontrollerclient/binpickingtask.h" -#include "mujincontrollerclient/mujinzmq.h" - -namespace mujinclient { - -/** \brief client to mujin controller via zmq socket connection - */ -class ZmqMujinControllerClient; -typedef boost::shared_ptr ZmqMujinControllerClientPtr; -typedef boost::weak_ptr ZmqMujinControllerClientWeakPtr; - -class MUJINCLIENT_API BinPickingTaskZmqResource : public BinPickingTaskResource -{ -public: - BinPickingTaskZmqResource(ControllerClientPtr controller, const std::string& pk, const std::string& scenepk, const std::string& tasktype = "binpicking"); - - ~BinPickingTaskZmqResource(); - - void ExecuteCommand(const std::string& taskparameters, rapidjson::Document &pt, const double timeout /* [sec] */=0.0, const bool getresult=true) override; - - virtual void ExecuteCommand(rapidjson::Value& rTaskParameters, rapidjson::Document& rOutput, const double timeout /* second */=5.0) override; - void _ExecuteCommandZMQ(const std::string& command, rapidjson::Document& rOutput, const double timeout /* second */=5.0, const bool getresult=true); - - void Initialize(const std::string& defaultTaskParameters, const int zmqPort, const int heartbeatPort, boost::shared_ptr zmqcontext, const bool initializezmq=false, const double reinitializetimeout=10, const double timeout=0, const std::string& userinfo="{}", const std::string& slaverequestid="") override; - - void InitializeZMQ(const double reinitializetimeout = 5, const double timeout /* second */=0); - void _HeartbeatMonitorThread(const double reinitializetimeout, const double commandtimeout); - -private: - ZmqMujinControllerClientPtr _zmqmujincontrollerclient; -}; - -} // namespace mujinclient -#endif diff --git a/src/boost_assertion_failed.cpp b/src/boost_assertion_failed.cpp index 573e77cb..ea93d857 100644 --- a/src/boost_assertion_failed.cpp +++ b/src/boost_assertion_failed.cpp @@ -1,7 +1,7 @@ #ifdef BOOST_ENABLE_ASSERT_HANDLER #include -#include +#include // Derived from https://gcc.gnu.org/wiki/Visibility #if !(defined _WIN32 || defined __CYGWIN__) && __GNUC__ >= 4 @@ -15,13 +15,13 @@ namespace boost { HIDDEN void assertion_failed(char const * expr, char const * function, char const * file, long line) { - throw mujinclient::MujinException((boost::format("[%s:%d] -> %s, expr: %s")%file%line%function%expr).str(),mujinclient::MEC_Assert); + throw mujinwebstackclient::MujinException((boost::format("[%s:%d] -> %s, expr: %s")%file%line%function%expr).str(),mujinwebstackclient::MEC_Assert); } #if BOOST_VERSION>104600 HIDDEN void assertion_failed_msg(char const * expr, char const * msg, char const * function, char const * file, long line) { - throw mujinclient::MujinException((boost::format("[%s:%d] -> %s, expr: %s, msg: %s")%file%line%function%expr%msg).str(),mujinclient::MEC_Assert); + throw mujinwebstackclient::MujinException((boost::format("[%s:%d] -> %s, expr: %s, msg: %s")%file%line%function%expr%msg).str(),mujinwebstackclient::MEC_Assert); } #endif } // namespace boost diff --git a/src/common.cpp b/src/common.cpp index 8e8daef7..113d9510 100644 --- a/src/common.cpp +++ b/src/common.cpp @@ -13,44 +13,13 @@ // limitations under the License. #include "common.h" -namespace mujinclient { +namespace mujinwebstackclient { bool PairStringLengthCompare(const std::pair&p0, const std::pair&p1) { return p0.first.size() > p1.first.size(); } -std::string& SearchAndReplace(std::string& out, const std::string& in, const std::vector< std::pair >&_pairs) -{ - BOOST_ASSERT(&out != &in); - std::vector< std::pair >::const_iterator itp, itbestp; - for(itp = _pairs.begin(); itp != _pairs.end(); ++itp) { - BOOST_ASSERT(itp->first.size()>0); - } - std::vector< std::pair > pairs = _pairs; - stable_sort(pairs.begin(),pairs.end(),PairStringLengthCompare); - out.resize(0); - size_t startindex = 0; - while(startindex < in.size()) { - size_t nextindex=std::string::npos; - for(itp = pairs.begin(); itp != pairs.end(); ++itp) { - size_t index = in.find(itp->first,startindex); - if((nextindex == std::string::npos)|| ((index != std::string::npos)&&(index < nextindex)) ) { - nextindex = index; - itbestp = itp; - } - } - if( nextindex == std::string::npos ) { - out += in.substr(startindex); - break; - } - out += in.substr(startindex,nextindex-startindex); - out += itbestp->second; - startindex = nextindex+itbestp->first.size(); - } - return out; -} - namespace encoding { #if defined(_WIN32) || defined(_WIN64) @@ -113,11 +82,11 @@ void ConvertTimestampToFloat(const std::string& in, const std::size_t closingCurly = in.substr(timestampbegin, len).find("}"); if (comma == std::string::npos && closingCurly == std::string::npos) { - throw mujinclient::MujinException(boost::str(boost::format("error while converting timestamp value format for %s")%in), mujinclient::MEC_Failed); + throw mujinwebstackclient::MujinException(boost::str(boost::format("error while converting timestamp value format for %s")%in), mujinwebstackclient::MEC_Failed); } const std::size_t timestampend = timestampbegin + (comma < closingCurly ? comma : closingCurly); if (timestampend == std::string::npos) { - throw mujinclient::MujinException(boost::str(boost::format("error while converting timestamp value format for %s")%in), mujinclient::MEC_Failed); + throw mujinwebstackclient::MujinException(boost::str(boost::format("error while converting timestamp value format for %s")%in), mujinwebstackclient::MEC_Failed); } const std::size_t period = in.substr(timestampbegin, len).find("."); @@ -133,4 +102,4 @@ void ConvertTimestampToFloat(const std::string& in, } } -} // namespace mujinclient +} // namespace mujinwebstackclient diff --git a/src/common.h b/src/common.h index df48a950..59467ba6 100644 --- a/src/common.h +++ b/src/common.h @@ -12,13 +12,16 @@ // See the License for the specific language governing permissions and // limitations under the License. /** \file common.h - \brief Private common definitions for mujin controller client. + \brief Private common definitions for mujin webstack client. */ -#ifndef MUJIN_CONTROLLERCLIENT_COMMON_H -#define MUJIN_CONTROLLERCLIENT_COMMON_H +#ifndef MUJIN_WEBSTACKCLIENT_COMMON_H +#define MUJIN_WEBSTACKCLIENT_COMMON_H #define WIN32_LEAN_AND_MEAN -#include + +#include +#include +#include #include #include @@ -28,12 +31,10 @@ #include #include #include - -#include -#include -#include #include +#include + #if defined(_WIN32) || defined(_WIN64) #include #undef GetUserName // classes with ControllerClient::GetUserName @@ -186,7 +187,6 @@ inline static unsigned long long GetNanoPerformanceTime() #endif #endif -#define GETCONTROLLERIMPL() ControllerClientImplPtr controller = boost::dynamic_pointer_cast(GetController()); #define CHECKCURLCODE(code, msg) if (code != CURLE_OK) { \ throw MujinException(boost::str(boost::format("[%s:%d] curl function %s with error '%s': %s")%(__PRETTY_FUNCTION__)%(__LINE__)%(msg)%curl_easy_strerror(code)%_errormessage), MEC_HTTPClient); \ } @@ -200,18 +200,14 @@ inline static unsigned long long GetNanoPerformanceTime() } #endif -#define MUJIN_EXCEPTION_FORMAT0(s, errorcode) mujinclient::MujinException(boost::str(boost::format("[%s:%d] " s)%(__PRETTY_FUNCTION__)%(__LINE__)),errorcode) +#define MUJIN_EXCEPTION_FORMAT0(s, errorcode) mujinwebstackclient::MujinException(boost::str(boost::format("[%s:%d] " s)%(__PRETTY_FUNCTION__)%(__LINE__)),errorcode) /// adds the function name and line number to an exception -#define MUJIN_EXCEPTION_FORMAT(s, args,errorcode) mujinclient::MujinException(boost::str(boost::format("[%s:%d] " s)%(__PRETTY_FUNCTION__)%(__LINE__)%args),errorcode) +#define MUJIN_EXCEPTION_FORMAT(s, args,errorcode) mujinwebstackclient::MujinException(boost::str(boost::format("[%s:%d] " s)%(__PRETTY_FUNCTION__)%(__LINE__)%args),errorcode) BOOST_STATIC_ASSERT(sizeof(unsigned short) == 2); // need this for utf-16 reading -namespace mujinclient { - -class BinPickingTaskZmqResource; -typedef boost::shared_ptr BinPickingTaskZmqResourcePtr; -typedef boost::weak_ptr BinPickingTaskZmqResourceWeakPtr; +namespace mujinwebstackclient { class FileHandler { @@ -233,7 +229,6 @@ class FileHandler }; bool PairStringLengthCompare(const std::pair&p0, const std::pair&p1); -std::string& SearchAndReplace(std::string& out, const std::string& in, const std::vector< std::pair >&_pairs); namespace encoding { @@ -361,6 +356,6 @@ const char s_filesep = '/'; const wchar_t s_wfilesep = L'/'; #endif -} // end namespace mujinclient +} // end namespace mujinwebstackclient #endif diff --git a/src/controllerclientimpl.cpp b/src/controllerclientimpl.cpp deleted file mode 100644 index 2628bd48..00000000 --- a/src/controllerclientimpl.cpp +++ /dev/null @@ -1,1995 +0,0 @@ -// -*- coding: utf-8 -*- -// Copyright (C) 2012-2013 MUJIN Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT 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 "common.h" -#include "controllerclientimpl.h" - -#include -#include -#include -#include - -#define SKIP_PEER_VERIFICATION // temporary -//#define SKIP_HOSTNAME_VERIFICATION - -#include "logging.h" - -MUJIN_LOGGER("mujin.controllerclientcpp"); - -#define CURL_OPTION_SAVER(curl, curlopt, curvalue) boost::shared_ptr __curloptionsaver ## curlopt((void*)0, boost::bind(boost::function(curl_easy_setopt), curl, curlopt, curvalue)) -#define CURL_OPTION_SETTER(curl, curlopt, newvalue) CHECKCURLCODE(curl_easy_setopt(curl, curlopt, newvalue), "curl_easy_setopt " # curlopt) -#define CURL_OPTION_SAVE_SETTER(curl, curlopt, curvalue, newvalue) CURL_OPTION_SAVER(curl, curlopt, curvalue); CURL_OPTION_SETTER(curl, curlopt, newvalue) -#define CURL_INFO_GETTER(curl, curlinfo, outvalue) CHECKCURLCODE(curl_easy_getinfo(curl, curlinfo, outvalue), "curl_easy_getinfo " # curlinfo) -#define CURL_PERFORM(curl) CHECKCURLCODE(curl_easy_perform(curl), "curl_easy_perform") -#define CURL_FORM_RELEASER(form) boost::shared_ptr __curlformreleaser ## form((void*)0, boost::bind(boost::function(curl_formfree), form)) - -namespace mujinclient { - -using namespace mujinjson; - -template -std::wstring ParseWincapsWCNPath(const T& sourcefilename, const boost::function& ConvertToFileSystemEncoding) -{ - // scenefilenames is the WPJ file, so have to open it up to see what directory it points to - // note that the encoding is utf-16 - // - // .\threegoaltouch\threegoaltouch.WCN; - // - // first have to get the raw utf-16 data -#if defined(_WIN32) || defined(_WIN64) - std::ifstream wpjfilestream(sourcefilename.c_str(), std::ios::binary|std::ios::in); -#else - // linux doesn't mix ifstream and wstring - std::ifstream wpjfilestream(ConvertToFileSystemEncoding(sourcefilename).c_str(), std::ios::binary|std::ios::in); -#endif - if( !wpjfilestream ) { - throw MUJIN_EXCEPTION_FORMAT("failed to open file %s", ConvertToFileSystemEncoding(sourcefilename), MEC_InvalidArguments); - } - std::wstringstream utf16stream; - bool readbom = false; - while(!wpjfilestream.eof() ) { - unsigned short c; - wpjfilestream.read(reinterpret_cast(&c),sizeof(c)); - if( !wpjfilestream ) { - break; - } - // skip the first character (BOM) due to a bug in boost property_tree (should be fixed in 1.49) - if( readbom || c != 0xfeff ) { - utf16stream << static_cast(c); - } - else { - readbom = true; - } - } - boost::property_tree::wptree wpj; - boost::property_tree::read_xml(utf16stream, wpj); - boost::property_tree::wptree& clsProject = wpj.get_child(L"clsProject"); - boost::property_tree::wptree& WCNPath = clsProject.get_child(L"WCNPath"); - std::wstring strWCNPath = WCNPath.data(); - if( strWCNPath.size() > 0 ) { - // post process the string to get the real filesystem directory - if( strWCNPath.at(strWCNPath.size()-1) == L';') { - strWCNPath.resize(strWCNPath.size()-1); - } - - if( strWCNPath.size() >= 2 && (strWCNPath[0] == L'.' && strWCNPath[1] == L'\\') ) { - // don't need the prefix - strWCNPath = strWCNPath.substr(2); - } - } - - return strWCNPath; -} - -ControllerClientImpl::ControllerClientImpl(const std::string& usernamepassword, const std::string& baseuri, const std::string& proxyserverport, const std::string& proxyuserpw, int options, double timeout) -{ - BOOST_ASSERT( !baseuri.empty() ); - size_t usernameindex = 0; - usernameindex = usernamepassword.find_first_of(':'); - BOOST_ASSERT(usernameindex != std::string::npos ); - _username = usernamepassword.substr(0,usernameindex); - std::string password = usernamepassword.substr(usernameindex+1); - - _httpheadersjson = NULL; - _httpheadersstl = NULL; - _httpheadersmultipartformdata = NULL; - _baseuri = baseuri; - // ensure trailing slash - if( _baseuri[_baseuri.size()-1] != '/' ) { - _baseuri.push_back('/'); - } - _baseapiuri = _baseuri + std::string("api/v1/"); - // hack for now since webdav server and api server could be running on different ports - if( boost::algorithm::ends_with(_baseuri, ":8000/") || (options&0x80000000) ) { - // testing on localhost, however the webdav server is running on port 80... - _basewebdavuri = str(boost::format("%s/u/%s/")%_baseuri.substr(0,_baseuri.size()-6)%_username); - } - else { - _basewebdavuri = str(boost::format("%su/%s/")%_baseuri%_username); - } - - //CURLcode code = curl_global_init(CURL_GLOBAL_SSL|CURL_GLOBAL_WIN32); - _curl = curl_easy_init(); - BOOST_ASSERT(!!_curl); - -#ifdef _DEBUG - // CURL_OPTION_SETTER(_curl, CURLOPT_VERBOSE, 1L); -#endif - _errormessage.resize(CURL_ERROR_SIZE); - CURL_OPTION_SETTER(_curl, CURLOPT_ERRORBUFFER, &_errormessage[0]); - -#ifdef SKIP_PEER_VERIFICATION - /* - * if you want to connect to a site who isn't using a certificate that is - * signed by one of the certs in the ca bundle you have, you can skip the - * verification of the server's certificate. this makes the connection - * a lot less secure. - * - * if you have a ca cert for the server stored someplace else than in the - * default bundle, then the curlopt_capath option might come handy for - * you. - */ - CURL_OPTION_SETTER(_curl, CURLOPT_SSL_VERIFYPEER, 0L); -#endif - -#ifdef SKIP_HOSTNAME_VERIFICATION - /* - * If the site you're connecting to uses a different host name that what - * they have mentioned in their server certificate's commonName (or - * subjectAltName) fields, libcurl will refuse to connect. You can skip - * this check, but this will make the connection less secure. - */ - CURL_OPTION_SETTER(_curl, CURLOPT_SSL_VERIFYHOST, 0L); -#endif - - if( proxyserverport.size() > 0 ) { - SetProxy(proxyserverport, proxyuserpw); - } - - CURL_OPTION_SETTER(_curl, CURLOPT_HTTPAUTH, CURLAUTH_BASIC); - CURL_OPTION_SETTER(_curl, CURLOPT_USERPWD, usernamepassword.c_str()); - - // need to set the following? - //CURLOPT_USERAGENT - //CURLOPT_TCP_KEEPIDLE - //CURLOPT_TCP_KEEPALIVE - //CURLOPT_TCP_KEEPINTVL - - CURL_OPTION_SETTER(_curl, CURLOPT_COOKIEFILE, ""); // just to start the cookie engine - - // save everything to _buffer, neceesary to do it before first POST/GET calls or data will be output to stdout - // these should be set on individual calls - // CURL_OPTION_SETTER(_curl, CURLOPT_WRITEFUNCTION, _WriteStringStreamCallback); // just to start the cookie engine - // CURL_OPTION_SETTER(_curl, CURLOPT_WRITEDATA, &_buffer); - - std::string useragent = std::string("controllerclientcpp/")+MUJINCLIENT_VERSION_STRING; - CURL_OPTION_SETTER(_curl, CURLOPT_USERAGENT, useragent.c_str()); - - CURL_OPTION_SETTER(_curl, CURLOPT_FOLLOWLOCATION, 1L); // we can always follow redirect now, we don't need to detect login page - CURL_OPTION_SETTER(_curl, CURLOPT_MAXREDIRS, 10L); - CURL_OPTION_SETTER(_curl, CURLOPT_NOSIGNAL, 1L); - - CURL_OPTION_SETTER(_curl, CURLOPT_POSTFIELDSIZE, 0L); - CURL_OPTION_SETTER(_curl, CURLOPT_POSTFIELDS, NULL); - - // csrftoken can be any non-empty string - _csrfmiddlewaretoken = "csrftoken"; - std::string cookie = "Set-Cookie: csrftoken=" + _csrfmiddlewaretoken; -#if CURL_AT_LEAST_VERSION(7,60,0) - // with https://github.com/curl/curl/commit/b8d5036ec9b702d6392c97a6fc2e141d6c7cce1f, setting domain param to cookie is required. - if(_baseuri.find('/') == _baseuri.size()-1) { - // _baseuri should be hostname with trailing slash - cookie += "; domain="; - cookie += _baseuri.substr(0,_baseuri.size()-1); - } else { - CURLU *url = curl_url(); - BOOST_SCOPE_EXIT_ALL(&url) { - curl_url_cleanup(url); - }; - CHECKCURLUCODE(curl_url_set(url, CURLUPART_URL, _baseuri.c_str(), 0), "cannot parse url"); - char *host = NULL; - BOOST_SCOPE_EXIT_ALL(&host) { - if(host) { - curl_free(host); - } - }; - CHECKCURLUCODE(curl_url_get(url, CURLUPART_HOST, &host, 0), "cannot determine hostname from url"); - cookie += "; domain="; - cookie += host; - } -#endif - CURL_OPTION_SETTER(_curl, CURLOPT_COOKIELIST, cookie.c_str()); - - _charset = "utf-8"; - _language = "en-us"; -#if defined(_WIN32) || defined(_WIN64) - UINT codepage = GetACP(); - std::map::const_iterator itcodepage = encoding::GetCodePageMap().find(codepage); - if( itcodepage != encoding::GetCodePageMap().end() ) { - _charset = itcodepage->second; - } -#endif - MUJIN_LOG_INFO("setting character set to " << _charset); - _SetupHTTPHeadersJSON(); - _SetupHTTPHeadersSTL(); - _SetupHTTPHeadersMultipartFormData(); -} - -ControllerClientImpl::~ControllerClientImpl() -{ - if( !!_httpheadersjson ) { - curl_slist_free_all(_httpheadersjson); - } - if( !!_httpheadersstl ) { - curl_slist_free_all(_httpheadersstl); - } - if( !!_httpheadersmultipartformdata ) { - curl_slist_free_all(_httpheadersmultipartformdata); - } - curl_easy_cleanup(_curl); -} - -std::string ControllerClientImpl::GetVersion() -{ - if (!_profile.IsObject()) { - _profile.SetObject(); - CallGet("profile/", _profile); - } - return GetJsonValueByKey(_profile, "version"); -} - -const std::string& ControllerClientImpl::GetUserName() const -{ - return _username; -} - -const std::string& ControllerClientImpl::GetBaseURI() const -{ - return _baseuri; -} - -void ControllerClientImpl::SetCharacterEncoding(const std::string& newencoding) -{ - boost::mutex::scoped_lock lock(_mutex); - _charset = newencoding; - _SetupHTTPHeadersJSON(); - // the following two format does not need charset - // _SetupHTTPHeadersSTL(); - // _SetupHTTPHeadersMultipartFormData(); -} - -void ControllerClientImpl::SetProxy(const std::string& serverport, const std::string& userpw) -{ - // mutally exclusive with unix endpoint settings - CURL_OPTION_SETTER(_curl, CURLOPT_UNIX_SOCKET_PATH, NULL); - CURL_OPTION_SETTER(_curl, CURLOPT_PROXY, serverport.c_str()); - CURL_OPTION_SETTER(_curl, CURLOPT_PROXYUSERPWD, userpw.c_str()); -} - -void ControllerClientImpl::SetUnixEndpoint(const std::string& unixendpoint) -{ - // mutually exclusive with proxy settings - CURL_OPTION_SETTER(_curl, CURLOPT_PROXY, NULL); - CURL_OPTION_SETTER(_curl, CURLOPT_PROXYUSERPWD, NULL); - CURL_OPTION_SETTER(_curl, CURLOPT_UNIX_SOCKET_PATH, unixendpoint.c_str()); -} - -void ControllerClientImpl::SetLanguage(const std::string& language) -{ - boost::mutex::scoped_lock lock(_mutex); - if (language!= "") { - _language = language; - } - _SetupHTTPHeadersJSON(); - // the following two format does not need language - // _SetupHTTPHeadersSTL(); - // _SetupHTTPHeadersMultipartFormData(); -} - -void ControllerClientImpl::SetUserAgent(const std::string& userAgent) -{ - CURL_OPTION_SETTER(_curl, CURLOPT_USERAGENT, userAgent.c_str()); -} - -void ControllerClientImpl::SetAdditionalHeaders(const std::vector& additionalHeaders) -{ - boost::mutex::scoped_lock lock(_mutex); - _additionalHeaders = additionalHeaders; - _SetupHTTPHeadersJSON(); - _SetupHTTPHeadersSTL(); - _SetupHTTPHeadersMultipartFormData(); -} - -void ControllerClientImpl::_ExecuteGraphQuery(const char* operationName, const char* query, const rapidjson::Value& rVariables, rapidjson::Value& rResult, rapidjson::Document::AllocatorType& rAlloc, double timeout, bool checkForErrors, bool returnRawResponse) -{ - rResult.SetNull(); // zero output - - rapidjson::Document rResultDoc(&rAlloc); - - { - boost::mutex::scoped_lock lock(_mutex); - - rapidjson::StringBuffer& rRequestStringBuffer = _rRequestStringBufferCache; - rRequestStringBuffer.Clear(); - - { - // use the callers allocator to construct the request body - rapidjson::Value rRequest, rValue; - rRequest.SetObject(); - rValue.SetString(operationName, rAlloc); - rRequest.AddMember(rapidjson::Document::StringRefType("operationName"), rValue, rAlloc); - rValue.SetString(query, rAlloc); - rRequest.AddMember(rapidjson::Document::StringRefType("query"), rValue, rAlloc); - rValue.CopyFrom(rVariables, rAlloc); - rRequest.AddMember(rapidjson::Document::StringRefType("variables"), rValue, rAlloc); - - rapidjson::Writer writer(rRequestStringBuffer); - rRequest.Accept(writer); - } - - _uri = _baseuri + "api/v2/graphql"; - _CallPost(_uri, rRequestStringBuffer.GetString(), rResultDoc, 200, timeout); - } - - // parse response - if (!rResultDoc.IsObject()) { - throw MUJIN_EXCEPTION_FORMAT("Execute graph query does not return valid response \"%s\", invalid response: %s", operationName%mujinjson::DumpJson(rResultDoc), MEC_HTTPServer); - } - - if (checkForErrors) { - // look for errors in response - const rapidjson::Value::ConstMemberIterator itErrors = rResultDoc.FindMember("errors"); - if (itErrors != rResultDoc.MemberEnd() && itErrors->value.IsArray() && itErrors->value.Size() > 0) { - MUJIN_LOG_VERBOSE(str(boost::format("graph query has errors \"%s\": %s")%operationName%mujinjson::DumpJson(rResultDoc))); - for (rapidjson::Value::ConstValueIterator itError = itErrors->value.Begin(); itError != itErrors->value.End(); ++itError) { - const rapidjson::Value& rError = *itError; - if (rError.IsObject() && rError.HasMember("message") && rError["message"].IsString()) { - const char* errorCode = "unknown"; - const rapidjson::Value::ConstMemberIterator itExtensions = rError.FindMember("extensions"); - if (itExtensions != rError.MemberEnd() && itExtensions->value.IsObject() && itExtensions->value.HasMember("errorCode") && itExtensions->value["errorCode"].IsString()) { - errorCode = itExtensions->value["errorCode"].GetString(); - } - throw mujinclient::MujinGraphQueryError(boost::str(boost::format("[%s:%d] graph query has errors \"%s\": %s")%(__PRETTY_FUNCTION__)%(__LINE__)%operationName%rError["message"].GetString()), errorCode); - } - } - throw MUJIN_EXCEPTION_FORMAT("graph query has undefined errors \"%s\": %s", operationName%mujinjson::DumpJson(rResultDoc), MEC_HTTPServer); - } - } - - // should have data member - if (!rResultDoc.HasMember("data")) { - throw MUJIN_EXCEPTION_FORMAT("Execute graph query does not have 'data' field in \"%s\", invalid response: %s", operationName%mujinjson::DumpJson(rResultDoc), MEC_HTTPServer); - } - - // set output - if (returnRawResponse) { - rResult.Swap(rResultDoc); - } else { - rResult = rResultDoc["data"]; - } -} - -void ControllerClientImpl::ExecuteGraphQuery(const char* operationName, const char* query, const rapidjson::Value& rVariables, rapidjson::Value& rResult, rapidjson::Document::AllocatorType& rAlloc, double timeout) -{ - _ExecuteGraphQuery(operationName, query, rVariables, rResult, rAlloc, timeout, true, false); -} - -void ControllerClientImpl::ExecuteGraphQueryRaw(const char* operationName, const char* query, const rapidjson::Value& rVariables, rapidjson::Value& rResult, rapidjson::Document::AllocatorType& rAlloc, double timeout) -{ - _ExecuteGraphQuery(operationName, query, rVariables, rResult, rAlloc, timeout, false, true); -} - -void ControllerClientImpl::RestartServer(double timeout) -{ - boost::mutex::scoped_lock lock(_mutex); - CURL_OPTION_SAVE_SETTER(_curl, CURLOPT_TIMEOUT_MS, 0L, (long)(timeout * 1000L)); - _uri = _baseuri + std::string("restartserver/"); - CURL_OPTION_SAVE_SETTER(_curl, CURLOPT_HTTPHEADER, NULL, _httpheadersjson); - CURL_OPTION_SAVE_SETTER(_curl, CURLOPT_URL, NULL, _uri.c_str()); - CURL_OPTION_SAVE_SETTER(_curl, CURLOPT_POST, 0L, 1L); - _buffer.clear(); - _buffer.str(""); - CURL_OPTION_SAVE_SETTER(_curl, CURLOPT_WRITEFUNCTION, NULL, _WriteStringStreamCallback); - CURL_OPTION_SAVE_SETTER(_curl, CURLOPT_WRITEDATA, NULL, &_buffer); - CURL_PERFORM(_curl); - long http_code = 0; - CURL_INFO_GETTER(_curl, CURLINFO_RESPONSE_CODE, &http_code); - if( http_code != 200 ) { - throw MUJIN_EXCEPTION_FORMAT0("Failed to restart server, please try again or contact MUJIN support", MEC_HTTPServer); - } -} - -void ControllerClientImpl::CancelAllJobs() -{ - CallDelete("job/?format=json", 204); -} - -void ControllerClientImpl::GetRunTimeStatuses(std::vector& statuses, int options) -{ - rapidjson::Document pt(rapidjson::kObjectType); - std::string url = "job/?format=json&fields=pk,status,fnname,elapsedtime"; - if( options & 1 ) { - url += std::string(",status_text"); - } - CallGet(url, pt); - rapidjson::Value& objects = pt["objects"]; - size_t i = 0; - statuses.resize(objects.Size()); - for (rapidjson::Document::ValueIterator it=objects.Begin(); it != objects.End(); ++it) { - - statuses[i].pk = GetJsonValueByKey(*it, "pk"); - statuses[i].code = GetStatusCode(GetJsonValueByKey(*it, "status")); - statuses[i].type = GetJsonValueByKey(*it, "fnname"); - statuses[i].elapsedtime = GetJsonValueByKey(*it, "elapsedtime"); - if( options & 1 ) { - statuses[i].message = GetJsonValueByKey(*it, "status_text"); - } - i++; - } -} - -void ControllerClientImpl::GetScenePrimaryKeys(std::vector& scenekeys) -{ - rapidjson::Document pt(rapidjson::kObjectType); - CallGet("scene/?format=json&limit=0&fields=pk", pt); - rapidjson::Value& objects = pt["objects"]; - scenekeys.resize(objects.Size()); - size_t i = 0; - for (rapidjson::Document::ValueIterator it=objects.Begin(); it != objects.End(); ++it) { - scenekeys[i++] = GetJsonValueByKey(*it, "pk"); - } -} - -SceneResourcePtr ControllerClientImpl::RegisterScene_UTF8(const std::string& uri, const std::string& scenetype) -{ - BOOST_ASSERT(scenetype.size()>0); - rapidjson::Document pt(rapidjson::kObjectType); - CallPost_UTF8("scene/?format=json&fields=pk", str(boost::format("{\"uri\":\"%s\", \"scenetype\":\"%s\"}")%uri%scenetype), pt); - std::string pk = GetJsonValueByKey(pt, "pk"); - SceneResourcePtr scene(new SceneResource(shared_from_this(), pk)); - return scene; -} - -SceneResourcePtr ControllerClientImpl::RegisterScene_UTF16(const std::wstring& uri, const std::string& scenetype) -{ - BOOST_ASSERT(scenetype.size()>0); - rapidjson::Document pt(rapidjson::kObjectType); - CallPost_UTF16("scene/?format=json&fields=pk", str(boost::wformat(L"{\"uri\":\"%s\", \"scenetype\":\"%s\"}")%uri%scenetype.c_str()), pt); - std::string pk = GetJsonValueByKey(pt, "pk"); - SceneResourcePtr scene(new SceneResource(shared_from_this(), pk)); - return scene; -} - -SceneResourcePtr ControllerClientImpl::ImportSceneToCOLLADA_UTF8(const std::string& importuri, const std::string& importformat, const std::string& newuri, bool overwrite) -{ - BOOST_ASSERT(importformat.size()>0); - rapidjson::Document pt(rapidjson::kObjectType); - CallPost_UTF8(str(boost::format("scene/?format=json&fields=pk&overwrite=%d")%overwrite), str(boost::format("{\"reference_uri\":\"%s\", \"reference_scenetype\":\"%s\", \"uri\":\"%s\"}")%importuri%importformat%newuri), pt); - std::string pk = GetJsonValueByKey(pt, "pk"); - SceneResourcePtr scene(new SceneResource(shared_from_this(), pk)); - return scene; -} - -SceneResourcePtr ControllerClientImpl::ImportSceneToCOLLADA_UTF16(const std::wstring& importuri, const std::string& importformat, const std::wstring& newuri, bool overwrite) -{ - BOOST_ASSERT(importformat.size()>0); - rapidjson::Document pt(rapidjson::kObjectType); - CallPost_UTF16(str(boost::format("scene/?format=json&fields=pk&overwrite=%d")%overwrite), str(boost::wformat(L"{\"reference_uri\":\"%s\", \"reference_scenetype\":\"%s\", \"uri\":\"%s\"}")%importuri%importformat.c_str()%newuri), pt); - std::string pk = GetJsonValueByKey(pt, "pk"); - SceneResourcePtr scene(new SceneResource(shared_from_this(), pk)); - return scene; -} - -void ControllerClientImpl::SyncUpload_UTF8(const std::string& _sourcefilename, const std::string& destinationdir, const std::string& scenetype) -{ - // TODO use curl_multi_perform to allow uploading of multiple files simultaneously - // TODO should LOCK with WebDAV repository? - boost::mutex::scoped_lock lock(_mutex); - std::string baseuploaduri; - if( destinationdir.size() >= 7 && destinationdir.substr(0,7) == "mujin:/" ) { - baseuploaduri = _basewebdavuri; - baseuploaduri += _EncodeWithoutSeparator(destinationdir.substr(7)); - _EnsureWebDAVDirectories(destinationdir.substr(7)); - } - else { - baseuploaduri = destinationdir; - } - // ensure trailing slash - if( baseuploaduri[baseuploaduri.size()-1] != '/' ) { - baseuploaduri.push_back('/'); - } - - std::string sourcefilename = _sourcefilename; -#if defined(_WIN32) || defined(_WIN64) - // check if / is used anywhere and send a warning if it is - if( sourcefilename.find_first_of('/') != std::string::npos ) { - std::stringstream ss; - ss << "scene filename '" << sourcefilename << "' is using /, so replacing this with \\"; - MUJIN_LOG_INFO(ss.str()); - for(size_t i = 0; i < sourcefilename.size(); ++i) { - if( sourcefilename[i] == '/' ) { - sourcefilename[i] = '\\'; - } - } - } -#endif - - size_t nBaseFilenameStartIndex = sourcefilename.find_last_of(s_filesep); - if( nBaseFilenameStartIndex == std::string::npos ) { - // there's no path? - nBaseFilenameStartIndex = 0; - } - else { - nBaseFilenameStartIndex++; - } - - if( scenetype == "wincaps" ) { - std::wstring strWCNPath_utf16 = ParseWincapsWCNPath(sourcefilename, encoding::ConvertUTF8ToFileSystemEncoding); - if( strWCNPath_utf16.size() > 0 ) { - std::string strWCNPath; - utf8::utf16to8(strWCNPath_utf16.begin(), strWCNPath_utf16.end(), std::back_inserter(strWCNPath)); - std::string strWCNURI = strWCNPath; - size_t lastindex = 0; - for(size_t i = 0; i < strWCNURI.size(); ++i) { - if( strWCNURI[i] == '\\' ) { - strWCNURI[i] = '/'; - strWCNPath[i] = s_filesep; - lastindex = i; - } - } - std::string sCopyDir = sourcefilename.substr(0,nBaseFilenameStartIndex) + strWCNPath.substr(0,lastindex); - _UploadDirectoryToController_UTF8(sCopyDir, baseuploaduri+_EncodeWithoutSeparator(strWCNURI.substr(0,lastindex))); - } - } - else if( scenetype == "rttoolbox" || scenetype == "cecrobodiaxml" ) { - if( nBaseFilenameStartIndex > 0 ) { - // sourcefilename[nBaseFilenameStartIndex] should be == s_filesep - _UploadDirectoryToController_UTF8(sourcefilename.substr(0,nBaseFilenameStartIndex), baseuploaduri); - } - return; - } - - // sourcefilenamebase is utf-8 - std::string uploadfileuri = baseuploaduri + EscapeString(sourcefilename.substr(nBaseFilenameStartIndex)); - _UploadFileToController_UTF8(sourcefilename, uploadfileuri); - - /* webdav operations - const char *postdata = - "" - "SELECT \"http://schemas.microsoft.com/repl/contenttag\"" - " from SCOPE ('deep traversal of \"/exchange/adb/Calendar/\"') " - "WHERE \"DAV:isfolder\" = True\r\n"; - */ -} - -void ControllerClientImpl::SyncUpload_UTF16(const std::wstring& _sourcefilename_utf16, const std::wstring& destinationdir_utf16, const std::string& scenetype) -{ - // TODO use curl_multi_perform to allow uploading of multiple files simultaneously - // TODO should LOCK with WebDAV repository? - boost::mutex::scoped_lock lock(_mutex); - std::string baseuploaduri; - std::string destinationdir_utf8; - utf8::utf16to8(destinationdir_utf16.begin(), destinationdir_utf16.end(), std::back_inserter(destinationdir_utf8)); - - if( destinationdir_utf8.size() >= 7 && destinationdir_utf8.substr(0,7) == "mujin:/" ) { - baseuploaduri = _basewebdavuri; - std::string s = destinationdir_utf8.substr(7); - baseuploaduri += _EncodeWithoutSeparator(s); - _EnsureWebDAVDirectories(s); - } - else { - baseuploaduri = destinationdir_utf8; - } - // ensure trailing slash - if( baseuploaduri[baseuploaduri.size()-1] != '/' ) { - baseuploaduri.push_back('/'); - } - - std::wstring sourcefilename_utf16 = _sourcefilename_utf16; -#if defined(_WIN32) || defined(_WIN64) - // check if / is used anywhere and send a warning if it is - if( sourcefilename_utf16.find_first_of(L'/') != std::wstring::npos ) { - std::stringstream ss; - ss << "scene filename '" << encoding::ConvertUTF16ToFileSystemEncoding(sourcefilename_utf16) << "' is using /, so replacing this with \\"; - MUJIN_LOG_INFO(ss.str()); - for(size_t i = 0; i < sourcefilename_utf16.size(); ++i) { - if( sourcefilename_utf16[i] == L'/' ) { - sourcefilename_utf16[i] = L'\\'; - } - } - - boost::replace_all(sourcefilename_utf16, L"/", L"\\"); - } -#endif - - size_t nBaseFilenameStartIndex = sourcefilename_utf16.find_last_of(s_wfilesep); - if( nBaseFilenameStartIndex == std::string::npos ) { - // there's no path? - nBaseFilenameStartIndex = 0; - } - else { - nBaseFilenameStartIndex++; - } - - if( scenetype == "wincaps" ) { - std::wstring strWCNPath_utf16 = ParseWincapsWCNPath(sourcefilename_utf16, encoding::ConvertUTF16ToFileSystemEncoding); - if( strWCNPath_utf16.size() > 0 ) { - std::string strWCNURI; - utf8::utf16to8(strWCNPath_utf16.begin(), strWCNPath_utf16.end(), std::back_inserter(strWCNURI)); - size_t lastindex_utf8 = 0; - for(size_t i = 0; i < strWCNURI.size(); ++i) { - if( strWCNURI[i] == '\\' ) { - strWCNURI[i] = '/'; - lastindex_utf8 = i; - } - } - size_t lastindex_utf16 = 0; - for(size_t i = 0; i < strWCNPath_utf16.size(); ++i) { - if( strWCNPath_utf16[i] == '\\' ) { - strWCNPath_utf16[i] = s_wfilesep; - lastindex_utf16 = i; - } - } - std::wstring sCopyDir_utf16 = sourcefilename_utf16.substr(0,nBaseFilenameStartIndex) + strWCNPath_utf16.substr(0,lastindex_utf16); - _UploadDirectoryToController_UTF16(sCopyDir_utf16, baseuploaduri+_EncodeWithoutSeparator(strWCNURI.substr(0,lastindex_utf8))); - } - } - else if( scenetype == "rttoolbox" || scenetype == "cecrobodiaxml" ) { - if( nBaseFilenameStartIndex > 0 ) { - // sourcefilename_utf16[nBaseFilenameStartIndex] should be == s_filesep - _UploadDirectoryToController_UTF16(sourcefilename_utf16.substr(0,nBaseFilenameStartIndex), baseuploaduri); - } - return; - } - - // sourcefilenamebase is utf-8 - std::string sourcefilenamedir_utf8; - utf8::utf16to8(sourcefilename_utf16.begin()+nBaseFilenameStartIndex, sourcefilename_utf16.end(), std::back_inserter(sourcefilenamedir_utf8)); - std::string uploadfileuri = baseuploaduri + EscapeString(sourcefilenamedir_utf8); - _UploadFileToController_UTF16(sourcefilename_utf16, uploadfileuri); -} - -/// \brief expectedhttpcode is not 0, then will check with the returned http code and if not equal will throw an exception -int ControllerClientImpl::CallGet(const std::string& relativeuri, rapidjson::Document& pt, int expectedhttpcode, double timeout) -{ - boost::mutex::scoped_lock lock(_mutex); - _uri = _baseapiuri; - _uri += relativeuri; - return _CallGet(_uri, pt, expectedhttpcode, timeout); -} - -int ControllerClientImpl::_CallGet(const std::string& desturi, rapidjson::Document& pt, int expectedhttpcode, double timeout) -{ - MUJIN_LOG_INFO(str(boost::format("GET %s")%desturi)); - CURL_OPTION_SAVE_SETTER(_curl, CURLOPT_TIMEOUT_MS, 0L, (long)(timeout * 1000L)); - CURL_OPTION_SAVE_SETTER(_curl, CURLOPT_HTTPHEADER, NULL, _httpheadersjson); - CURL_OPTION_SAVE_SETTER(_curl, CURLOPT_URL, NULL, desturi.c_str()); - _buffer.clear(); - _buffer.str(""); - CURL_OPTION_SAVE_SETTER(_curl, CURLOPT_WRITEFUNCTION, NULL, _WriteStringStreamCallback); - CURL_OPTION_SAVE_SETTER(_curl, CURLOPT_WRITEDATA, NULL, &_buffer); - CURL_OPTION_SAVE_SETTER(_curl, CURLOPT_HTTPGET, 0L, 1L); - CURL_PERFORM(_curl); - long http_code = 0; - CURL_INFO_GETTER(_curl, CURLINFO_RESPONSE_CODE, &http_code); - if( _buffer.rdbuf()->in_avail() > 0 ) { - mujinjson::ParseJson(pt, _buffer.str()); - } - if( expectedhttpcode != 0 && http_code != expectedhttpcode ) { - std::string error_message = GetJsonValueByKey(pt, "error_message"); - std::string traceback = GetJsonValueByKey(pt, "traceback"); - throw MUJIN_EXCEPTION_FORMAT("HTTP GET to '%s' returned HTTP status %s: %s", desturi%http_code%error_message, MEC_HTTPServer); - } - return http_code; -} - -int ControllerClientImpl::CallGet(const std::string& relativeuri, std::string& outputdata, int expectedhttpcode, double timeout) -{ - boost::mutex::scoped_lock lock(_mutex); - _uri = _baseapiuri; - _uri += relativeuri; - return _CallGet(_uri, outputdata, expectedhttpcode, timeout); -} - -int ControllerClientImpl::_CallGet(const std::string& desturi, std::string& outputdata, int expectedhttpcode, double timeout) -{ - MUJIN_LOG_VERBOSE(str(boost::format("GET %s")%desturi)); - CURL_OPTION_SAVE_SETTER(_curl, CURLOPT_TIMEOUT_MS, 0L, (long)(timeout * 1000L)); - CURL_OPTION_SAVE_SETTER(_curl, CURLOPT_HTTPHEADER, NULL, _httpheadersjson); - CURL_OPTION_SAVE_SETTER(_curl, CURLOPT_URL, NULL, desturi.c_str()); - _buffer.clear(); - _buffer.str(""); - CURL_OPTION_SAVE_SETTER(_curl, CURLOPT_WRITEFUNCTION, NULL, _WriteStringStreamCallback); - CURL_OPTION_SAVE_SETTER(_curl, CURLOPT_WRITEDATA, NULL, &_buffer); - CURL_OPTION_SAVE_SETTER(_curl, CURLOPT_HTTPGET, 0L, 1L); - CURL_PERFORM(_curl); - long http_code = 0; - CURL_INFO_GETTER(_curl, CURLINFO_RESPONSE_CODE, &http_code); - outputdata = _buffer.str(); - if( expectedhttpcode != 0 && http_code != expectedhttpcode ) { - if( outputdata.size() > 0 ) { - rapidjson::Document d; - ParseJson(d, _buffer.str()); - std::string error_message = GetJsonValueByKey(d, "error_message"); - std::string traceback = GetJsonValueByKey(d, "traceback"); - throw MUJIN_EXCEPTION_FORMAT("HTTP GET to '%s' returned HTTP status %s: %s", desturi%http_code%error_message, MEC_HTTPServer); - } - throw MUJIN_EXCEPTION_FORMAT("HTTP GET to '%s' returned HTTP status %s", desturi%http_code, MEC_HTTPServer); - } - return http_code; -} - -int ControllerClientImpl::CallGet(const std::string& relativeuri, std::ostream& outputStream, int expectedhttpcode, double timeout) -{ - boost::mutex::scoped_lock lock(_mutex); - _uri = _baseapiuri; - _uri += relativeuri; - return _CallGet(_uri, outputStream, expectedhttpcode, timeout); -} - -int ControllerClientImpl::_CallGet(const std::string& desturi, std::ostream& outputStream, int expectedhttpcode, double timeout) -{ - MUJIN_LOG_VERBOSE(str(boost::format("GET %s")%desturi)); - CURL_OPTION_SAVE_SETTER(_curl, CURLOPT_TIMEOUT_MS, 0L, (long)(timeout * 1000L)); - CURL_OPTION_SAVE_SETTER(_curl, CURLOPT_HTTPHEADER, NULL, _httpheadersjson); - CURL_OPTION_SAVE_SETTER(_curl, CURLOPT_URL, NULL, desturi.c_str()); - CURL_OPTION_SAVE_SETTER(_curl, CURLOPT_WRITEFUNCTION, NULL, _WriteOStreamCallback); - CURL_OPTION_SAVE_SETTER(_curl, CURLOPT_WRITEDATA, NULL, &outputStream); - CURL_OPTION_SAVE_SETTER(_curl, CURLOPT_HTTPGET, 0L, 1L); - CURL_PERFORM(_curl); - long http_code = 0; - CURL_INFO_GETTER(_curl, CURLINFO_RESPONSE_CODE, &http_code); - if( expectedhttpcode != 0 && http_code != expectedhttpcode ) { - // outputStream is not always seekable; ignore any error message. - throw MUJIN_EXCEPTION_FORMAT("HTTP GET to '%s' returned HTTP status %s (outputStream might have information)", desturi%http_code, MEC_HTTPServer); - } - return http_code; -} - -int ControllerClientImpl::CallGet(const std::string& relativeuri, std::vector& outputdata, int expectedhttpcode, double timeout) -{ - boost::mutex::scoped_lock lock(_mutex); - _uri = _baseapiuri; - _uri += relativeuri; - return _CallGet(_uri, outputdata, expectedhttpcode, timeout); -} - -int ControllerClientImpl::_CallGet(const std::string& desturi, std::vector& outputdata, int expectedhttpcode, double timeout) -{ - MUJIN_LOG_VERBOSE(str(boost::format("GET %s")%desturi)); - CURL_OPTION_SAVE_SETTER(_curl, CURLOPT_TIMEOUT_MS, 0L, (long)(timeout * 1000L)); - CURL_OPTION_SAVE_SETTER(_curl, CURLOPT_HTTPHEADER, NULL, _httpheadersjson); - CURL_OPTION_SAVE_SETTER(_curl, CURLOPT_URL, NULL, desturi.c_str()); - - CURL_OPTION_SAVE_SETTER(_curl, CURLOPT_WRITEFUNCTION, NULL, _WriteVectorCallback); - outputdata.resize(0); - CURL_OPTION_SAVE_SETTER(_curl, CURLOPT_WRITEDATA, NULL, &outputdata); - - CURL_OPTION_SAVE_SETTER(_curl, CURLOPT_HTTPGET, 0L, 1L); - CURL_PERFORM(_curl); - long http_code = 0; - CURL_INFO_GETTER(_curl, CURLINFO_RESPONSE_CODE, &http_code); - if( expectedhttpcode != 0 && http_code != expectedhttpcode ) { - if( outputdata.size() > 0 ) { - rapidjson::Document d; - std::stringstream ss; - ss.write((const char*)&outputdata[0], outputdata.size()); - ParseJson(d, ss.str()); - std::string error_message = GetJsonValueByKey(d, "error_message"); - std::string traceback = GetJsonValueByKey(d, "traceback"); - throw MUJIN_EXCEPTION_FORMAT("HTTP GET to '%s' returned HTTP status %s: %s", desturi%http_code%error_message, MEC_HTTPServer); - } - throw MUJIN_EXCEPTION_FORMAT("HTTP GET to '%s' returned HTTP status %s", desturi%http_code, MEC_HTTPServer); - } - return http_code; -} - -/// \brief expectedhttpcode is not 0, then will check with the returned http code and if not equal will throw an exception -int ControllerClientImpl::CallPost(const std::string& relativeuri, const std::string& data, rapidjson::Document& pt, int expectedhttpcode, double timeout) -{ - MUJIN_LOG_DEBUG(str(boost::format("POST %s%s")%_baseapiuri%relativeuri)); - boost::mutex::scoped_lock lock(_mutex); - _uri = _baseapiuri; - _uri += relativeuri; - return _CallPost(_uri, data, pt, expectedhttpcode, timeout); -} - -/// \brief expectedhttpcode is not 0, then will check with the returned http code and if not equal will throw an exception -int ControllerClientImpl::_CallPost(const std::string& desturi, const std::string& data, rapidjson::Document& pt, int expectedhttpcode, double timeout) -{ - MUJIN_LOG_VERBOSE(str(boost::format("POST %s")%desturi)); - CURL_OPTION_SAVE_SETTER(_curl, CURLOPT_TIMEOUT_MS, 0L, (long)(timeout * 1000L)); - CURL_OPTION_SAVE_SETTER(_curl, CURLOPT_HTTPHEADER, NULL, _httpheadersjson); - CURL_OPTION_SAVE_SETTER(_curl, CURLOPT_URL, NULL, desturi.c_str()); - _buffer.clear(); - _buffer.str(""); - CURL_OPTION_SAVE_SETTER(_curl, CURLOPT_WRITEFUNCTION, NULL, _WriteStringStreamCallback); - CURL_OPTION_SAVE_SETTER(_curl, CURLOPT_WRITEDATA, NULL, &_buffer); - CURL_OPTION_SAVE_SETTER(_curl, CURLOPT_POST, 0L, 1L); - CURL_OPTION_SAVE_SETTER(_curl, CURLOPT_POSTFIELDSIZE, 0, data.size()); - CURL_OPTION_SAVE_SETTER(_curl, CURLOPT_POSTFIELDS, NULL, data.size() > 0 ? data.c_str() : NULL); - CURL_PERFORM(_curl); - long http_code = 0; - CURL_INFO_GETTER(_curl, CURLINFO_RESPONSE_CODE, &http_code); - if( _buffer.rdbuf()->in_avail() > 0 ) { - ParseJson(pt, _buffer.str()); - } else { - pt.SetObject(); - } - if( expectedhttpcode != 0 && http_code != expectedhttpcode ) { - std::string error_message = GetJsonValueByKey(pt, "error_message"); - std::string traceback = GetJsonValueByKey(pt, "traceback"); - throw MUJIN_EXCEPTION_FORMAT("HTTP POST to '%s' returned HTTP status %s: %s", desturi%http_code%error_message, MEC_HTTPServer); - } - return http_code; -} - -int ControllerClientImpl::CallPost_UTF8(const std::string& relativeuri, const std::string& data, rapidjson::Document& pt, int expectedhttpcode, double timeout) -{ - return CallPost(relativeuri, encoding::ConvertUTF8ToFileSystemEncoding(data), pt, expectedhttpcode, timeout); -} - -int ControllerClientImpl::CallPost_UTF16(const std::string& relativeuri, const std::wstring& data, rapidjson::Document& pt, int expectedhttpcode, double timeout) -{ - return CallPost(relativeuri, encoding::ConvertUTF16ToFileSystemEncoding(data), pt, expectedhttpcode, timeout); -} - -int ControllerClientImpl::_CallPut(const std::string& relativeuri, const void* pdata, size_t nDataSize, rapidjson::Document& pt, curl_slist* headers, int expectedhttpcode, double timeout) -{ - MUJIN_LOG_DEBUG(str(boost::format("PUT %s%s")%_baseapiuri%relativeuri)); - boost::mutex::scoped_lock lock(_mutex); - CURL_OPTION_SAVE_SETTER(_curl, CURLOPT_HTTPHEADER, NULL, headers); - CURL_OPTION_SAVE_SETTER(_curl, CURLOPT_TIMEOUT_MS, 0L, (long)(timeout * 1000L)); - _uri = _baseapiuri; - _uri += relativeuri; - CURL_OPTION_SAVE_SETTER(_curl, CURLOPT_URL, NULL, _uri.c_str()); - _buffer.clear(); - _buffer.str(""); - CURL_OPTION_SAVE_SETTER(_curl, CURLOPT_WRITEFUNCTION, NULL, _WriteStringStreamCallback); - CURL_OPTION_SAVE_SETTER(_curl, CURLOPT_WRITEDATA, NULL, &_buffer); - CURL_OPTION_SAVE_SETTER(_curl, CURLOPT_CUSTOMREQUEST, NULL, "PUT"); - CURL_OPTION_SAVE_SETTER(_curl, CURLOPT_POSTFIELDSIZE, 0, nDataSize); - CURL_OPTION_SAVE_SETTER(_curl, CURLOPT_POSTFIELDS, NULL, pdata); - CURL_PERFORM(_curl); - long http_code = 0; - CURL_INFO_GETTER(_curl, CURLINFO_RESPONSE_CODE, &http_code); - if( _buffer.rdbuf()->in_avail() > 0 ) { - ParseJson(pt, _buffer.str()); - } else { - pt.SetObject(); - } - if( expectedhttpcode != 0 && http_code != expectedhttpcode ) { - std::string error_message = GetJsonValueByKey(pt, "error_message"); - std::string traceback = GetJsonValueByKey(pt, "traceback"); - throw MUJIN_EXCEPTION_FORMAT("HTTP PUT to '%s' returned HTTP status %s: %s", relativeuri%http_code%error_message, MEC_HTTPServer); - } - return http_code; -} - -int ControllerClientImpl::CallPutSTL(const std::string& relativeuri, const std::vector& data, rapidjson::Document& pt, int expectedhttpcode, double timeout) -{ - return _CallPut(relativeuri, static_cast (&data[0]), data.size(), pt, _httpheadersstl, expectedhttpcode, timeout); -} - -int ControllerClientImpl::CallPutJSON(const std::string& relativeuri, const std::string& data, rapidjson::Document& pt, int expectedhttpcode, double timeout) -{ - return _CallPut(relativeuri, static_cast(&data[0]), data.size(), pt, _httpheadersjson, expectedhttpcode, timeout); -} - -void ControllerClientImpl::CallDelete(const std::string& relativeuri, int expectedhttpcode, double timeout) -{ - MUJIN_LOG_DEBUG(str(boost::format("DELETE %s%s")%_baseapiuri%relativeuri)); - boost::mutex::scoped_lock lock(_mutex); - CURL_OPTION_SAVE_SETTER(_curl, CURLOPT_TIMEOUT_MS, 0L, (long)(timeout * 1000L)); - _uri = _baseapiuri; - _uri += relativeuri; - CURL_OPTION_SAVE_SETTER(_curl, CURLOPT_HTTPHEADER, NULL, _httpheadersjson); - CURL_OPTION_SAVE_SETTER(_curl, CURLOPT_URL, NULL, _uri.c_str()); - CURL_OPTION_SAVE_SETTER(_curl, CURLOPT_CUSTOMREQUEST, NULL, "DELETE"); - _buffer.clear(); - _buffer.str(""); - CURL_OPTION_SAVE_SETTER(_curl, CURLOPT_WRITEFUNCTION, NULL, _WriteStringStreamCallback); - CURL_OPTION_SAVE_SETTER(_curl, CURLOPT_WRITEDATA, NULL, &_buffer); - CURL_PERFORM(_curl); - long http_code = 0; - CURL_INFO_GETTER(_curl, CURLINFO_RESPONSE_CODE, &http_code); - if( http_code != expectedhttpcode ) { - rapidjson::Document d; - ParseJson(d, _buffer.str()); - std::string error_message = GetJsonValueByKey(d, "error_message"); - std::string traceback = GetJsonValueByKey(d, "traceback"); - throw MUJIN_EXCEPTION_FORMAT("HTTP DELETE to '%s' returned HTTP status %s: %s", relativeuri%http_code%error_message, MEC_HTTPServer); - } -} - -std::stringstream& ControllerClientImpl::GetBuffer() -{ - return _buffer; -} - -void ControllerClientImpl::SetDefaultSceneType(const std::string& scenetype) -{ - _defaultscenetype = scenetype; -} - -const std::string& ControllerClientImpl::GetDefaultSceneType() -{ - return _defaultscenetype; -} - -void ControllerClientImpl::SetDefaultTaskType(const std::string& tasktype) -{ - _defaulttasktype = tasktype; -} - -const std::string& ControllerClientImpl::GetDefaultTaskType() -{ - return _defaulttasktype; -} - -std::string ControllerClientImpl::GetScenePrimaryKeyFromURI_UTF8(const std::string& uri) -{ - size_t index = uri.find(":/"); - if (index == std::string::npos) { - throw MUJIN_EXCEPTION_FORMAT("bad URI: %s", uri, MEC_InvalidArguments); - } - return EscapeString(uri.substr(index+2)); -} - -std::string ControllerClientImpl::GetScenePrimaryKeyFromURI_UTF16(const std::wstring& uri) -{ - std::string utf8line; - utf8::utf16to8(uri.begin(), uri.end(), std::back_inserter(utf8line)); - return GetScenePrimaryKeyFromURI_UTF8(utf8line); -} - -std::string ControllerClientImpl::GetPrimaryKeyFromName_UTF8(const std::string& name) -{ - return EscapeString(name); -} - -std::string ControllerClientImpl::GetPrimaryKeyFromName_UTF16(const std::wstring& name) -{ - std::string name_utf8; - utf8::utf16to8(name.begin(), name.end(), std::back_inserter(name_utf8)); - return GetPrimaryKeyFromName_UTF8(name_utf8); -} - -std::string ControllerClientImpl::GetNameFromPrimaryKey_UTF8(const std::string& pk) -{ - return UnescapeString(pk); -} - -std::wstring ControllerClientImpl::GetNameFromPrimaryKey_UTF16(const std::string& pk) -{ - std::string utf8 = GetNameFromPrimaryKey_UTF8(pk); - std::wstring utf16; - utf8::utf8to16(utf8.begin(), utf8.end(), std::back_inserter(utf16)); - return utf16; -} - -std::string ControllerClientImpl::CreateObjectGeometry(const std::string& objectPk, const std::string& geometryName, const std::string& linkPk, const std::string& geomtype, double timeout) -{ - rapidjson::Document pt(rapidjson::kObjectType); - const std::string geometryData("{\"name\":\"" + geometryName + "\", \"linkpk\":\"" + linkPk + "\", \"geomtype\": \"" + geomtype + "\"}"); - const std::string uri(str(boost::format("object/%s/geometry/") % objectPk)); - - CallPost(uri, geometryData, pt, 201, timeout); - return GetJsonValueByKey(pt, "pk"); -} - -std::string ControllerClientImpl::CreateIkParam(const std::string& objectPk, const std::string& name, const std::string& iktype, double timeout) -{ - rapidjson::Document pt(rapidjson::kObjectType); - const std::string ikparamData("{\"name\":\"" + name + "\", \"iktype\":\"" + iktype + "\"}"); - const std::string uri(str(boost::format("object/%s/ikparam/") % objectPk)); - - CallPost(uri, ikparamData, pt, 201, timeout); - return GetJsonValueByKey(pt, "pk"); -} - -std::string ControllerClientImpl::CreateLink(const std::string& objectPk, const std::string& parentlinkPk, const std::string& name, const Real quaternion[4], const Real translate[3], double timeout) -{ - rapidjson::Document pt(rapidjson::kObjectType); - std::string data(str(boost::format("{\"name\":\"%s\", \"quaternion\":[%.15f,%.15f,%.15f,%.15f], \"translate\":[%.15f,%.15f,%.15f]")%name%quaternion[0]%quaternion[1]%quaternion[2]%quaternion[3]%translate[0]%translate[1]%translate[2])); - if (!parentlinkPk.empty()) { - data += ", \"parentlinkpk\": \"" + parentlinkPk + "\""; - } - data += "}"; - const std::string uri(str(boost::format("object/%s/link/") % objectPk)); - - CallPost(uri, data, pt, 201, timeout); - return GetJsonValueByKey(pt, "pk"); -} - -std::string ControllerClientImpl::SetObjectGeometryMesh(const std::string& objectPk, const std::string& geometryPk, const std::vector& meshData, const std::string& unit, double timeout) -{ - rapidjson::Document pt(rapidjson::kObjectType); - const std::string uri(str(boost::format("object/%s/geometry/%s/?unit=%s")%objectPk%geometryPk%unit)); - CallPutSTL(uri, meshData, pt, 202, timeout); - return GetJsonValueByKey(pt, "pk"); -} - -int ControllerClientImpl::_WriteStringStreamCallback(char *data, size_t size, size_t nmemb, std::stringstream *writerData) -{ - if (writerData == NULL) { - return 0; - } - writerData->write(data, size*nmemb); - return size * nmemb; -} - -int ControllerClientImpl::_WriteOStreamCallback(char *data, size_t size, size_t nmemb, std::ostream *writerData) -{ - if (writerData == NULL) { - return 0; - } - writerData->write(data, size*nmemb); - return size * nmemb; -} - -int ControllerClientImpl::_WriteVectorCallback(char *data, size_t size, size_t nmemb, std::vector *writerData) -{ - if (writerData == NULL) { - return 0; - } - writerData->insert(writerData->end(), data, data+size*nmemb); - return size * nmemb; -} - -int ControllerClientImpl::_ReadIStreamCallback(char *data, size_t size, size_t nmemb, std::istream *readerData) -{ - if (readerData == NULL) { - return 0; - } - return readerData->read(data, size*nmemb).gcount(); -} - -void ControllerClientImpl::_SetupHTTPHeadersJSON() -{ - // set the header to only send json - std::string s = std::string("Content-Type: application/json; charset=") + _charset; - if( !!_httpheadersjson ) { - curl_slist_free_all(_httpheadersjson); - } - _httpheadersjson = curl_slist_append(NULL, s.c_str()); - s = str(boost::format("Accept-Language: %s,en-us")%_language); - _httpheadersjson = curl_slist_append(_httpheadersjson, s.c_str()); //,en;q=0.7,ja;q=0.3',") - s = str(boost::format("Accept-Charset: %s")%_charset); - _httpheadersjson = curl_slist_append(_httpheadersjson, s.c_str()); - //_httpheadersjson = curl_slist_append(_httpheadersjson, "Accept:"); // necessary? - s = std::string("X-CSRFToken: ")+_csrfmiddlewaretoken; - _httpheadersjson = curl_slist_append(_httpheadersjson, s.c_str()); - _httpheadersjson = curl_slist_append(_httpheadersjson, "Connection: Keep-Alive"); - _httpheadersjson = curl_slist_append(_httpheadersjson, "Keep-Alive: 20"); // keep alive for 20s? - // test on windows first - //_httpheadersjson = curl_slist_append(_httpheadersjson, "Accept-Encoding: gzip, deflate"); - for (const std::string& additionalHeader : _additionalHeaders) { - _httpheadersjson = curl_slist_append(_httpheadersjson, additionalHeader.c_str()); - } -} - -void ControllerClientImpl::_SetupHTTPHeadersSTL() -{ - // set the header to only send stl - std::string s = std::string("Content-Type: application/sla"); - if( !!_httpheadersstl ) { - curl_slist_free_all(_httpheadersstl); - } - _httpheadersstl = curl_slist_append(NULL, s.c_str()); - //_httpheadersstl = curl_slist_append(_httpheadersstl, "Accept:"); // necessary? - s = std::string("X-CSRFToken: ")+_csrfmiddlewaretoken; - _httpheadersstl = curl_slist_append(_httpheadersstl, s.c_str()); - _httpheadersstl = curl_slist_append(_httpheadersstl, "Connection: Keep-Alive"); - _httpheadersstl = curl_slist_append(_httpheadersstl, "Keep-Alive: 20"); // keep alive for 20s? - // test on windows first - //_httpheadersstl = curl_slist_append(_httpheadersstl, "Accept-Encoding: gzip, deflate"); - for (const std::string& additionalHeader : _additionalHeaders) { - _httpheadersstl = curl_slist_append(_httpheadersstl, additionalHeader.c_str()); - } -} - -void ControllerClientImpl::_SetupHTTPHeadersMultipartFormData() -{ - // set the header to only send stl - std::string s = std::string("Content-Type: multipart/form-data"); - if( !!_httpheadersmultipartformdata ) { - curl_slist_free_all(_httpheadersmultipartformdata); - } - _httpheadersmultipartformdata = curl_slist_append(NULL, s.c_str()); - //_httpheadersmultipartformdata = curl_slist_append(_httpheadersmultipartformdata, "Accept:"); // necessary? - s = std::string("X-CSRFToken: ")+_csrfmiddlewaretoken; - _httpheadersmultipartformdata = curl_slist_append(_httpheadersmultipartformdata, s.c_str()); - _httpheadersmultipartformdata = curl_slist_append(_httpheadersmultipartformdata, "Connection: Keep-Alive"); - _httpheadersmultipartformdata = curl_slist_append(_httpheadersmultipartformdata, "Keep-Alive: 20"); // keep alive for 20s? - // test on windows first - //_httpheadersmultipartformdata = curl_slist_append(_httpheadersmultipartformdata, "Accept-Encoding: gzip, deflate"); - for (const std::string& additionalHeader : _additionalHeaders) { - _httpheadersmultipartformdata = curl_slist_append(_httpheadersmultipartformdata, additionalHeader.c_str()); - } -} - -std::string ControllerClientImpl::_EncodeWithoutSeparator(const std::string& raw) -{ - std::string output; - size_t startindex = 0; - for(size_t i = 0; i < raw.size(); ++i) { - if( raw[i] == '/' ) { - if( startindex != i ) { - output += EscapeString(raw.substr(startindex, i-startindex)); - startindex = i+1; - } - output += '/'; - } - } - if( startindex != raw.size() ) { - output += EscapeString(raw.substr(startindex)); - } - return output; -} - -void ControllerClientImpl::_EnsureWebDAVDirectories(const std::string& relativeuri, double timeout) -{ - if (relativeuri.empty()) { - return; - } - - CURL_OPTION_SAVE_SETTER(_curl, CURLOPT_TIMEOUT_MS, 0L, (long)(timeout * 1000L)); - std::list listCreateDirs; - std::string output; - size_t startindex = 0; - for(size_t i = 0; i < relativeuri.size(); ++i) { - if( relativeuri[i] == '/' ) { - if( startindex != i ) { - listCreateDirs.push_back(EscapeString(relativeuri.substr(startindex, i-startindex))); - startindex = i+1; - } - } - } - if( startindex != relativeuri.size() ) { - listCreateDirs.push_back(EscapeString(relativeuri.substr(startindex))); - } - - CURL_OPTION_SAVE_SETTER(_curl, CURLOPT_WRITEFUNCTION, NULL, _WriteStringStreamCallback); - CURL_OPTION_SAVE_SETTER(_curl, CURLOPT_WRITEDATA, NULL, &_buffer); - CURL_OPTION_SAVE_SETTER(_curl, CURLOPT_CUSTOMREQUEST, NULL, "MKCOL"); - - std::string totaluri = ""; - for(std::list::iterator itdir = listCreateDirs.begin(); itdir != listCreateDirs.end(); ++itdir) { - // first have to create the directory structure up to destinationdir - if( totaluri.size() > 0 ) { - totaluri += '/'; - } - totaluri += *itdir; - _uri = _basewebdavuri + totaluri; - CURL_OPTION_SAVE_SETTER(_curl, CURLOPT_HTTPHEADER, NULL, _httpheadersjson); - CURL_OPTION_SAVE_SETTER(_curl, CURLOPT_URL, NULL, _uri.c_str()); - _buffer.clear(); - _buffer.str(""); - CURL_PERFORM(_curl); - long http_code = 0; - CURL_INFO_GETTER(_curl, CURLINFO_RESPONSE_CODE, &http_code); - /* creating directories - - Responses from a MKCOL request MUST NOT be cached as MKCOL has non-idempotent semantics. - - 201 (Created) - The collection or structured resource was created in its entirety. - - 403 (Forbidden) - This indicates at least one of two conditions: 1) the server does not allow the creation of collections at the given location in its namespace, or 2) the parent collection of the Request-URI exists but cannot accept members. - - 405 (Method Not Allowed) - MKCOL can only be executed on a deleted/non-existent resource. - - 409 (Conflict) - A collection cannot be made at the Request-URI until one or more intermediate collections have been created. - - 415 (Unsupported Media Type)- The server does not support the request type of the body. - - 507 (Insufficient Storage) - The resource does not have sufficient space to record the state of the resource after the execution of this method. - - */ - if( http_code != 201 && http_code != 301 ) { - throw MUJIN_EXCEPTION_FORMAT("HTTP MKCOL failed with HTTP status %d: %s", http_code%_errormessage, MEC_HTTPServer); - } - } -} - -std::string ControllerClientImpl::_PrepareDestinationURI_UTF8(const std::string& rawuri, bool bEnsurePath, bool bEnsureSlash, bool bIsDirectory) -{ - std::string baseuploaduri; - if( rawuri.size() >= 7 && rawuri.substr(0,7) == "mujin:/" ) { - baseuploaduri = _basewebdavuri; - std::string s = rawuri.substr(7); - baseuploaduri += _EncodeWithoutSeparator(s); - if( bEnsurePath ) { - if( !bIsDirectory ) { - size_t nBaseFilenameStartIndex = s.find_last_of(s_filesep); - if( nBaseFilenameStartIndex != std::string::npos ) { - s = s.substr(0, nBaseFilenameStartIndex); - } else { - s = ""; - } - } - _EnsureWebDAVDirectories(s); - } - } - else { - if( !bEnsureSlash ) { - return rawuri; - } - baseuploaduri = rawuri; - } - if( bEnsureSlash ) { - // ensure trailing slash - if( baseuploaduri[baseuploaduri.size()-1] != '/' ) { - baseuploaduri.push_back('/'); - } - } - return baseuploaduri; -} - -std::string ControllerClientImpl::_PrepareDestinationURI_UTF16(const std::wstring& rawuri_utf16, bool bEnsurePath, bool bEnsureSlash, bool bIsDirectory) -{ - std::string baseuploaduri; - std::string desturi_utf8; - utf8::utf16to8(rawuri_utf16.begin(), rawuri_utf16.end(), std::back_inserter(desturi_utf8)); - - if( desturi_utf8.size() >= 7 && desturi_utf8.substr(0,7) == "mujin:/" ) { - baseuploaduri = _basewebdavuri; - std::string s = desturi_utf8.substr(7); - baseuploaduri += _EncodeWithoutSeparator(s); - if( bEnsurePath ) { - if( !bIsDirectory ) { - size_t nBaseFilenameStartIndex = s.find_last_of(s_filesep); - if( nBaseFilenameStartIndex != std::string::npos ) { - s = s.substr(0, nBaseFilenameStartIndex); - } else { - s = ""; - } - } - _EnsureWebDAVDirectories(s); - } - } - else { - if( !bEnsureSlash ) { - return desturi_utf8; - } - baseuploaduri = desturi_utf8; - } - if( bEnsureSlash ) { - // ensure trailing slash - if( baseuploaduri[baseuploaduri.size()-1] != '/' ) { - baseuploaduri.push_back('/'); - } - } - return baseuploaduri; -} - -void ControllerClientImpl::UploadFileToController_UTF8(const std::string& filename, const std::string& desturi) -{ - boost::mutex::scoped_lock lock(_mutex); - _UploadFileToController_UTF8(filename, _PrepareDestinationURI_UTF8(desturi, false)); -} - -void ControllerClientImpl::UploadFileToController_UTF16(const std::wstring& filename_utf16, const std::wstring& desturi_utf16) -{ - boost::mutex::scoped_lock lock(_mutex); - _UploadFileToController_UTF16(filename_utf16, _PrepareDestinationURI_UTF16(desturi_utf16, false)); -} - -void ControllerClientImpl::UploadDataToController_UTF8(const void* data, size_t size, const std::string& desturi) -{ - boost::mutex::scoped_lock lock(_mutex); - const std::string filename = _PrepareDestinationURI_UTF8(desturi, false).substr(_basewebdavuri.size()); - _UploadDataToControllerViaForm(data, size, filename, _baseuri + "fileupload"); -} - -void ControllerClientImpl::UploadDataToController_UTF16(const void* data, size_t size, const std::wstring& desturi) -{ - boost::mutex::scoped_lock lock(_mutex); - const std::string filename = _PrepareDestinationURI_UTF16(desturi, false).substr(_basewebdavuri.size()); - _UploadDataToControllerViaForm(data, size, filename, _baseuri + "fileupload"); -} - -void ControllerClientImpl::UploadDirectoryToController_UTF8(const std::string& copydir, const std::string& desturi) -{ - boost::mutex::scoped_lock lock(_mutex); - _UploadDirectoryToController_UTF8(copydir, _PrepareDestinationURI_UTF8(desturi, false, false, true)); -} - -void ControllerClientImpl::UploadDirectoryToController_UTF16(const std::wstring& copydir, const std::wstring& desturi) -{ - boost::mutex::scoped_lock lock(_mutex); - _UploadDirectoryToController_UTF16(copydir, _PrepareDestinationURI_UTF16(desturi, false, false, true)); -} - -void ControllerClientImpl::DownloadFileFromController_UTF8(const std::string& desturi, std::vector& vdata) -{ - boost::mutex::scoped_lock lock(_mutex); - _CallGet(_PrepareDestinationURI_UTF8(desturi, false), vdata); -} - -void ControllerClientImpl::DownloadFileFromController_UTF16(const std::wstring& desturi, std::vector& vdata) -{ - boost::mutex::scoped_lock lock(_mutex); - _CallGet(_PrepareDestinationURI_UTF16(desturi, false), vdata); -} - -void ControllerClientImpl::DownloadFileFromControllerIfModifiedSince_UTF8(const std::string& desturi, long localtimeval, long& remotetimeval, std::vector& vdata, double timeout) -{ - boost::mutex::scoped_lock lock(_mutex); - _DownloadFileFromController(_PrepareDestinationURI_UTF8(desturi, false), localtimeval, remotetimeval, vdata, timeout); -} - -void ControllerClientImpl::DownloadFileFromControllerIfModifiedSince_UTF16(const std::wstring& desturi, long localtimeval, long& remotetimeval, std::vector& vdata, double timeout) -{ - boost::mutex::scoped_lock lock(_mutex); - _DownloadFileFromController(_PrepareDestinationURI_UTF16(desturi, false), localtimeval, remotetimeval, vdata, timeout); -} - -long ControllerClientImpl::GetModifiedTime(const std::string& uri, double timeout) -{ - boost::mutex::scoped_lock lock(_mutex); - - // Copied from https://curl.haxx.se/libcurl/c/CURLINFO_FILETIME.html - CURL_OPTION_SAVE_SETTER(_curl, CURLOPT_HTTPHEADER, NULL, _httpheadersjson); - - // in order to resolve cache correctly, need to go thorugh file/download endpoint - std::string apiendpoint = _baseuri + "file/download/?filename="; - if( uri.size() >= 7 && uri.substr(0,7) == "mujin:/" ) { - apiendpoint += _EncodeWithoutSeparator(uri.substr(7)); - } - - CURL_OPTION_SAVE_SETTER(_curl, CURLOPT_URL, NULL, apiendpoint.c_str()); - CURL_OPTION_SAVE_SETTER(_curl, CURLOPT_FILETIME, 0L, 1L); - CURL_OPTION_SAVE_SETTER(_curl, CURLOPT_NOBODY, 0L, 1L); - CURL_PERFORM(_curl); - - long http_code = 0; - CURL_INFO_GETTER(_curl, CURLINFO_RESPONSE_CODE, &http_code); - if( http_code != 200 ) { - throw MUJIN_EXCEPTION_FORMAT("Cannot get modified date of %s for HTTP HEAD call: return is %s", uri%http_code, MEC_HTTPServer); - } - - long filetime=-1; - CURL_INFO_GETTER(_curl, CURLINFO_FILETIME, &filetime); - return filetime; -} - -void ControllerClientImpl::_DownloadFileFromController(const std::string& desturi, long localtimeval, long &remotetimeval, std::vector& outputdata, double timeout) -{ - remotetimeval = 0; - - // ask for remote file time - CURL_OPTION_SAVE_SETTER(_curl, CURLOPT_FILETIME, 0L, 1L); - - // use if modified since if local file time is provided - CURL_OPTION_SAVE_SETTER(_curl, CURLOPT_TIMECONDITION, CURL_TIMECOND_NONE, localtimeval > 0 ? CURL_TIMECOND_IFMODSINCE : CURL_TIMECOND_NONE); - CURL_OPTION_SAVE_SETTER(_curl, CURLOPT_TIMEVALUE, 0L, localtimeval > 0 ? localtimeval : 0L); - - // do the get call - long http_code = _CallGet(desturi, outputdata, 0, timeout); - if ((http_code != 200 && http_code != 304)) { - if (outputdata.size() > 0) { - std::stringstream ss; - ss.write((const char*)&outputdata[0], outputdata.size()); - throw MUJIN_EXCEPTION_FORMAT("HTTP GET to '%s' returned HTTP status %s: %s", desturi%http_code%ss.str(), MEC_HTTPServer); - } - throw MUJIN_EXCEPTION_FORMAT("HTTP GET to '%s' returned HTTP status %s", desturi%http_code, MEC_HTTPServer); - } - - // retrieve remote file time - if (http_code != 304) { - // got the entire file so fill in the timestamp of that file - CURL_INFO_GETTER(_curl, CURLINFO_FILETIME, &remotetimeval); - } -} - -void ControllerClientImpl::SaveBackup(std::ostream& outputStream, bool config, bool media, const std::string& backupscenepks, double timeout) -{ - boost::mutex::scoped_lock lock(_mutex); - std::string query=std::string("?config=")+(config ? "true" : "false")+"&media="+(media ? "true" : "false")+"&backupscenepks="+backupscenepks; - _CallGet(_baseuri+"backup/"+query, outputStream, 200, timeout); -} - -void ControllerClientImpl::RestoreBackup(std::istream& inputStream, bool config, bool media, double timeout) -{ - boost::mutex::scoped_lock lock(_mutex); - std::string query=std::string("?config=")+(config ? "true" : "false")+"&media="+(media ? "true" : "false"); - _UploadFileToControllerViaForm(inputStream, "", _baseuri+"backup/"+query, timeout); -} - -void ControllerClientImpl::Upgrade(std::istream& inputStream, bool autorestart, bool uploadonly, double timeout) -{ - boost::mutex::scoped_lock lock(_mutex); - std::string query=std::string("?autorestart=")+(autorestart ? "1" : "0")+("&uploadonly=")+(uploadonly ? "1" : "0"); - - std::streampos originalPos = inputStream.tellg(); - inputStream.seekg(0, std::ios::end); - if(inputStream.fail()) { - throw MUJIN_EXCEPTION_FORMAT0("failed to seek inputStream to get the length", MEC_InvalidArguments); - } - std::streampos contentLength = inputStream.tellg() - originalPos; - if(inputStream.fail()) { - throw MUJIN_EXCEPTION_FORMAT0("failed to tell the length of inputStream", MEC_InvalidArguments); - } - inputStream.seekg(originalPos, std::ios::beg); - if(inputStream.fail()) { - throw MUJIN_EXCEPTION_FORMAT0("failed to rewind inputStream", MEC_InvalidArguments); - } - - if(contentLength) { - _UploadFileToControllerViaForm(inputStream, "", _baseuri+"upgrade/"+query, timeout); - } else { - rapidjson::Document pt(rapidjson::kObjectType); - _CallPost(_baseuri+"upgrade/"+query, "", pt, 200, timeout); - } -} - -bool ControllerClientImpl::GetUpgradeStatus(std::string& status, double &progress, double timeout) -{ - boost::mutex::scoped_lock lock(_mutex); - rapidjson::Document pt(rapidjson::kObjectType); - _CallGet(_baseuri+"upgrade/", pt, 200, timeout); - if(pt.IsNull()) { - return false; - } - status = GetJsonValueByKey(pt, "status"); - progress = GetJsonValueByKey(pt, "progress"); - return true; -} - -void ControllerClientImpl::CancelUpgrade(double timeout) -{ - CallDelete(_baseuri+"upgrade/", 200, timeout); -} - -void ControllerClientImpl::Reboot(double timeout) -{ - boost::mutex::scoped_lock lock(_mutex); - rapidjson::Document pt(rapidjson::kObjectType); - _CallPost(_baseuri+"reboot/", "", pt, 200, timeout); -} - -void ControllerClientImpl::DeleteAllScenes(double timeout) -{ - boost::mutex::scoped_lock lock(_mutex); - rapidjson::Document pt(rapidjson::kObjectType); - CallDelete("scene/", 204, timeout); -} - -void ControllerClientImpl::DeleteAllITLPrograms(double timeout) -{ - CallDelete("itl/", 204, timeout); -} - -void ControllerClientImpl::DeleteFileOnController_UTF8(const std::string& desturi) -{ - boost::mutex::scoped_lock lock(_mutex); - _DeleteFileOnController(_PrepareDestinationURI_UTF8(desturi, false)); -} - -void ControllerClientImpl::DeleteFileOnController_UTF16(const std::wstring& desturi) -{ - boost::mutex::scoped_lock lock(_mutex); - _DeleteFileOnController(_PrepareDestinationURI_UTF16(desturi, false)); -} - -void ControllerClientImpl::DeleteDirectoryOnController_UTF8(const std::string& desturi) -{ - boost::mutex::scoped_lock lock(_mutex); - _DeleteDirectoryOnController(_PrepareDestinationURI_UTF8(desturi, false, false, true)); -} - -void ControllerClientImpl::DeleteDirectoryOnController_UTF16(const std::wstring& desturi) -{ - boost::mutex::scoped_lock lock(_mutex); - _DeleteDirectoryOnController(_PrepareDestinationURI_UTF16(desturi, false, false, true)); -} - -void ControllerClientImpl::ModifySceneAddReferenceObjectPK(const std::string &scenepk, const std::string &referenceobjectpk, double timeout) -{ - rapidjson::Document pt, pt2; - rapidjson::Value value; - - pt.SetObject(); - - value.SetString(scenepk.c_str(), pt.GetAllocator()); - pt.AddMember("scenepk", value, pt.GetAllocator()); - - value.SetString(referenceobjectpk.c_str(), pt.GetAllocator()); - pt.AddMember("referenceobjectpk", value, pt.GetAllocator()); - - boost::mutex::scoped_lock lock(_mutex); - _CallPost(_baseuri + "referenceobjectpks/add/", DumpJson(pt), pt2, 200, timeout); -} - -void ControllerClientImpl::ModifySceneRemoveReferenceObjectPK(const std::string &scenepk, const std::string &referenceobjectpk, double timeout) -{ - rapidjson::Document pt, pt2; - rapidjson::Value value; - - pt.SetObject(); - - value.SetString(scenepk.c_str(), pt.GetAllocator()); - pt.AddMember("scenepk", value, pt.GetAllocator()); - - value.SetString(referenceobjectpk.c_str(), pt.GetAllocator()); - pt.AddMember("referenceobjectpk", value, pt.GetAllocator()); - - boost::mutex::scoped_lock lock(_mutex); - _CallPost(_baseuri + "referenceobjectpks/remove/", DumpJson(pt), pt2, 200, timeout); -} - -void ControllerClientImpl::_UploadDirectoryToController_UTF8(const std::string& copydir_utf8, const std::string& rawuri) -{ - BOOST_ASSERT(rawuri.size()>0 && copydir_utf8.size()>0); - - // if there's a trailing slash, have to get rid of it - std::string uri; - if( rawuri.at(rawuri.size()-1) == '/' ) { - if( copydir_utf8.at(copydir_utf8.size()-1) != s_filesep ) { - // append the copydir_utf8 name to rawuri - size_t nBaseFilenameStartIndex = copydir_utf8.find_last_of(s_filesep); - if( nBaseFilenameStartIndex == std::string::npos ) { - // there's no path? - nBaseFilenameStartIndex = 0; - } - else { - nBaseFilenameStartIndex++; - } - uri = rawuri + EscapeString(copydir_utf8.substr(nBaseFilenameStartIndex)); - } - else { - // copydir also ends in a fileseparator, so remove the file separator from rawuri - uri = rawuri.substr(0, rawuri.size()-1); - } - } - else { - if (copydir_utf8.at(copydir_utf8.size()-1) == s_filesep) { - throw MUJIN_EXCEPTION_FORMAT("copydir '%s' cannot end in slash '%s'", copydir_utf8%s_filesep, MEC_InvalidArguments); - } - uri = rawuri; - } - - CURL_OPTION_SAVE_SETTER(_curl, CURLOPT_WRITEFUNCTION, NULL, _WriteStringStreamCallback); - CURL_OPTION_SAVE_SETTER(_curl, CURLOPT_WRITEDATA, NULL, &_buffer); - - { - // make sure the directory is created - CURL_OPTION_SAVE_SETTER(_curl, CURLOPT_CUSTOMREQUEST, NULL, "MKCOL"); - CURL_OPTION_SAVE_SETTER(_curl, CURLOPT_HTTPHEADER, NULL, _httpheadersjson); - CURL_OPTION_SAVE_SETTER(_curl, CURLOPT_URL, NULL, uri.c_str()); - CURL_PERFORM(_curl); - long http_code = 0; - CURL_INFO_GETTER(_curl, CURLINFO_RESPONSE_CODE, &http_code); - if( http_code != 201 && http_code != 301 ) { - throw MUJIN_EXCEPTION_FORMAT("HTTP MKCOL failed for %s with HTTP status %d: %s", uri%http_code%_errormessage, MEC_HTTPServer); - } - } - - std::string sCopyDir_FS = encoding::ConvertUTF8ToFileSystemEncoding(copydir_utf8); - // remove the fileseparator if it exists - std::stringstream ss; - ss << "uploading " << sCopyDir_FS << " -> " << uri; - MUJIN_LOG_INFO(ss.str()); - -#if defined(_WIN32) || defined(_WIN64) - bool bhasseparator = false; - if( sCopyDir_FS.size() > 0 && sCopyDir_FS.at(sCopyDir_FS.size()-1) == s_filesep ) { - sCopyDir_FS.resize(sCopyDir_FS.size()-1); - bhasseparator = true; - } - - WIN32_FIND_DATAA ffd; - std::string searchstr = sCopyDir_FS + std::string("\\*"); - HANDLE hFind = FindFirstFileA(searchstr.c_str(), &ffd); - if (hFind == INVALID_HANDLE_VALUE) { - throw MUJIN_EXCEPTION_FORMAT("could not retrieve file data for %s", sCopyDir_FS, MEC_Assert); - } - - do { - std::string filename = std::string(ffd.cFileName); - if( filename != "." && filename != ".." ) { - std::string filename_utf8 = encoding::ConvertMBStoUTF8(filename); - std::string newcopydir_utf8; - if( bhasseparator ) { - newcopydir_utf8 = copydir_utf8 + filename_utf8; - } - else { - newcopydir_utf8 = str(boost::format("%s%c%s")%copydir_utf8%s_filesep%filename_utf8); - } - std::string newuri = str(boost::format("%s/%s")%uri%EscapeString(filename_utf8)); - - if( ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY ) { - _UploadDirectoryToController_UTF8(newcopydir_utf8, newuri); - } - else if( ffd.dwFileAttributes == 0 || ffd.dwFileAttributes == FILE_ATTRIBUTE_READONLY || ffd.dwFileAttributes == FILE_ATTRIBUTE_NORMAL || ffd.dwFileAttributes == FILE_ATTRIBUTE_ARCHIVE ) { - _UploadFileToController_UTF8(newcopydir_utf8, newuri); - } - } - } while(FindNextFileA(hFind,&ffd) != 0); - - DWORD err = GetLastError(); - FindClose(hFind); - if( err != ERROR_NO_MORE_FILES ) { - throw MUJIN_EXCEPTION_FORMAT("system error 0x%x when recursing through %s", err%sCopyDir_FS, MEC_HTTPServer); - } - -#else - boost::filesystem::path bfpcopydir(copydir_utf8); - for(boost::filesystem::directory_iterator itdir(bfpcopydir); itdir != boost::filesystem::directory_iterator(); ++itdir) { -#if defined(BOOST_FILESYSTEM_VERSION) && BOOST_FILESYSTEM_VERSION >= 3 - std::string dirfilename = encoding::ConvertFileSystemEncodingToUTF8(itdir->path().filename().string()); -#else - std::string dirfilename = encoding::ConvertFileSystemEncodingToUTF8(itdir->path().filename()); -#endif - std::string newuri = str(boost::format("%s/%s")%uri%EscapeString(dirfilename)); - if( boost::filesystem::is_directory(itdir->status()) ) { - _UploadDirectoryToController_UTF8(itdir->path().string(), newuri); - } - else if( boost::filesystem::is_regular_file(itdir->status()) ) { - _UploadFileToController_UTF8(itdir->path().string(), newuri); - } - } -#endif // defined(_WIN32) || defined(_WIN64) -} - -void ControllerClientImpl::_UploadDirectoryToController_UTF16(const std::wstring& copydir_utf16, const std::string& rawuri) -{ - BOOST_ASSERT(rawuri.size()>0 && copydir_utf16.size()>0); - - // if there's a trailing slash, have to get rid of it - std::string uri; - if( rawuri.at(rawuri.size()-1) == '/' ) { - if( copydir_utf16.at(copydir_utf16.size()-1) != s_wfilesep ) { - // append the copydir_utf16 name to rawuri - size_t nBaseFilenameStartIndex = copydir_utf16.find_last_of(s_wfilesep); - if( nBaseFilenameStartIndex == std::string::npos ) { - // there's no path? - nBaseFilenameStartIndex = 0; - } - else { - nBaseFilenameStartIndex++; - } - std::string name_utf8; - utf8::utf16to8(copydir_utf16.begin()+nBaseFilenameStartIndex, copydir_utf16.end(), std::back_inserter(name_utf8)); - uri = rawuri + EscapeString(name_utf8); - } - else { - // copydir also ends in a fileseparator, so remove the file separator from rawuri - uri = rawuri.substr(0, rawuri.size()-1); - } - } - else { - if (copydir_utf16.at(copydir_utf16.size()-1) == s_wfilesep) { - throw MUJIN_EXCEPTION_FORMAT("copydir '%s' cannot end in slash '%s'", encoding::ConvertUTF16ToFileSystemEncoding(copydir_utf16)%s_filesep, MEC_InvalidArguments); - } - uri = rawuri; - } - - CURL_OPTION_SAVE_SETTER(_curl, CURLOPT_WRITEFUNCTION, NULL, _WriteStringStreamCallback); - CURL_OPTION_SAVE_SETTER(_curl, CURLOPT_WRITEDATA, NULL, &_buffer); - - { - // make sure the directory is created - CURL_OPTION_SAVE_SETTER(_curl, CURLOPT_CUSTOMREQUEST, NULL, "MKCOL"); - CURL_OPTION_SAVE_SETTER(_curl, CURLOPT_HTTPHEADER, NULL, _httpheadersjson); - CURL_OPTION_SAVE_SETTER(_curl, CURLOPT_URL, NULL, uri.c_str()); - CURL_PERFORM(_curl); - long http_code = 0; - CURL_INFO_GETTER(_curl, CURLINFO_RESPONSE_CODE, &http_code); - if( http_code != 201 && http_code != 301 ) { - throw MUJIN_EXCEPTION_FORMAT("HTTP MKCOL failed for %s with HTTP status %d: %s", uri%http_code%_errormessage, MEC_HTTPServer); - } - } - - std::wstring sCopyDir_FS; - // remove the fileseparator if it exists - if( copydir_utf16.size() > 0 && copydir_utf16.at(copydir_utf16.size()-1) == s_wfilesep ) { - sCopyDir_FS = copydir_utf16.substr(0,copydir_utf16.size()-1); - } - else { - sCopyDir_FS = copydir_utf16; - } - std::stringstream ss; - ss << "uploading " << encoding::ConvertUTF16ToFileSystemEncoding(copydir_utf16) << " -> " << uri; - MUJIN_LOG_INFO(ss.str()); - -#if defined(_WIN32) || defined(_WIN64) - WIN32_FIND_DATAW ffd; - std::wstring searchstr = sCopyDir_FS + std::wstring(L"\\*"); - HANDLE hFind = FindFirstFileW(searchstr.c_str(), &ffd); - if (hFind == INVALID_HANDLE_VALUE) { - throw MUJIN_EXCEPTION_FORMAT("could not retrieve file data for %s", encoding::ConvertUTF16ToFileSystemEncoding(copydir_utf16), MEC_Assert); - } - - do { - std::wstring filename = std::wstring(ffd.cFileName); - if( filename != L"." && filename != L".." ) { - std::string filename_utf8; - utf8::utf16to8(filename.begin(), filename.end(), std::back_inserter(filename_utf8)); - std::wstring newcopydir = str(boost::wformat(L"%s\\%s")%copydir_utf16%filename); - std::string newuri = str(boost::format("%s/%s")%uri%EscapeString(filename_utf8)); - - if( ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY ) { - _UploadDirectoryToController_UTF16(newcopydir, newuri); - } - else if( ffd.dwFileAttributes == 0 || ffd.dwFileAttributes == FILE_ATTRIBUTE_READONLY || ffd.dwFileAttributes == FILE_ATTRIBUTE_NORMAL || ffd.dwFileAttributes == FILE_ATTRIBUTE_ARCHIVE ) { - _UploadFileToController_UTF16(newcopydir, newuri); - } - } - } while(FindNextFileW(hFind,&ffd) != 0); - - DWORD err = GetLastError(); - FindClose(hFind); - if( err != ERROR_NO_MORE_FILES ) { - throw MUJIN_EXCEPTION_FORMAT("system error 0x%x when recursing through %s", err%encoding::ConvertUTF16ToFileSystemEncoding(copydir_utf16), MEC_HTTPServer); - } - -#elif defined(BOOST_FILESYSTEM_VERSION) && BOOST_FILESYSTEM_VERSION >= 3 - boost::filesystem::path bfpcopydir(copydir_utf16); - for(boost::filesystem::directory_iterator itdir(bfpcopydir); itdir != boost::filesystem::directory_iterator(); ++itdir) { - std::wstring dirfilename_utf16 = itdir->path().filename().wstring(); - std::string dirfilename; - utf8::utf16to8(dirfilename_utf16.begin(), dirfilename_utf16.end(), std::back_inserter(dirfilename)); - std::string newuri = str(boost::format("%s/%s")%uri%EscapeString(dirfilename)); - if( boost::filesystem::is_directory(itdir->status()) ) { - _UploadDirectoryToController_UTF16(itdir->path().wstring(), newuri); - } - else if( boost::filesystem::is_regular_file(itdir->status()) ) { - _UploadFileToController_UTF16(itdir->path().wstring(), newuri); - } - } -#else - // boost filesystem v2 - boost::filesystem::wpath bfpcopydir(copydir_utf16); - for(boost::filesystem::wdirectory_iterator itdir(bfpcopydir); itdir != boost::filesystem::wdirectory_iterator(); ++itdir) { - std::wstring dirfilename_utf16 = itdir->path().filename(); - std::string dirfilename; - utf8::utf16to8(dirfilename_utf16.begin(), dirfilename_utf16.end(), std::back_inserter(dirfilename)); - std::string newuri = str(boost::format("%s/%s")%uri%EscapeString(dirfilename)); - if( boost::filesystem::is_directory(itdir->status()) ) { - _UploadDirectoryToController_UTF16(itdir->path().string(), newuri); - } - else if( boost::filesystem::is_regular_file(itdir->status()) ) { - _UploadFileToController_UTF16(itdir->path().string(), newuri); - } - } -#endif // defined(_WIN32) || defined(_WIN64) -} - -void ControllerClientImpl::_UploadFileToController_UTF8(const std::string& filename, const std::string& uri) -{ - // the dest filename of the upload is determined by stripping the leading _basewebdavuri - if( uri.size() < _basewebdavuri.size() || uri.substr(0,_basewebdavuri.size()) != _basewebdavuri ) { - throw MUJIN_EXCEPTION_FORMAT("trying to upload a file outside of the webdav endpoint is not allowed: %s", uri, MEC_HTTPServer); - } - std::string filenameoncontroller = uri.substr(_basewebdavuri.size()); - - std::string sFilename_FS = encoding::ConvertUTF8ToFileSystemEncoding(filename); - std::ifstream fin(sFilename_FS.c_str(), std::ios::in | std::ios::binary); - if(!fin.good()) { - throw MUJIN_EXCEPTION_FORMAT("failed to open filename %s for uploading", sFilename_FS, MEC_InvalidArguments); - } - - MUJIN_LOG_DEBUG(str(boost::format("upload %s")%uri)) - _UploadFileToControllerViaForm(fin, filenameoncontroller, _baseuri + "fileupload"); -} - -void ControllerClientImpl::_UploadFileToController_UTF16(const std::wstring& filename, const std::string& uri) -{ - // the dest filename of the upload is determined by stripping the leading _basewebdavuri - if( uri.size() < _basewebdavuri.size() || uri.substr(0,_basewebdavuri.size()) != _basewebdavuri ) { - throw MUJIN_EXCEPTION_FORMAT("trying to upload a file outside of the webdav endpoint is not allowed: %s", uri, MEC_HTTPServer); - } - std::string filenameoncontroller = uri.substr(_basewebdavuri.size()); - - std::string sFilename_FS = encoding::ConvertUTF16ToFileSystemEncoding(filename); - std::vectorcontent; - std::ifstream fin(sFilename_FS.c_str(), std::ios::in | std::ios::binary); - if(!fin.good()) { - throw MUJIN_EXCEPTION_FORMAT("failed to open filename %s for uploading", sFilename_FS, MEC_InvalidArguments); - } - - MUJIN_LOG_DEBUG(str(boost::format("upload %s")%uri)) - _UploadFileToControllerViaForm(fin, filenameoncontroller, _baseuri + "fileupload"); -} - -void ControllerClientImpl::_UploadFileToControllerViaForm(std::istream& inputStream, const std::string& filename, const std::string& endpoint, double timeout) -{ - CURL_OPTION_SAVE_SETTER(_curl, CURLOPT_URL, NULL, endpoint.c_str()); - _buffer.clear(); - _buffer.str(""); - CURL_OPTION_SAVE_SETTER(_curl, CURLOPT_WRITEFUNCTION, NULL, _WriteStringStreamCallback); - CURL_OPTION_SAVE_SETTER(_curl, CURLOPT_WRITEDATA, NULL, &_buffer); - //timeout is default to 0 (never) - CURL_OPTION_SAVE_SETTER(_curl, CURLOPT_TIMEOUT_MS, 0L, (long)(timeout * 1000L)); - - std::streampos originalPos = inputStream.tellg(); - inputStream.seekg(0, std::ios::end); - if(inputStream.fail()) { - throw MUJIN_EXCEPTION_FORMAT0("failed to seek inputStream to get the length", MEC_InvalidArguments); - } - std::streampos contentLength = inputStream.tellg() - originalPos; - if(inputStream.fail()) { - throw MUJIN_EXCEPTION_FORMAT0("failed to tell the length of inputStream", MEC_InvalidArguments); - } - inputStream.seekg(originalPos, std::ios::beg); - if(inputStream.fail()) { - throw MUJIN_EXCEPTION_FORMAT0("failed to rewind inputStream", MEC_InvalidArguments); - } - - CURL_OPTION_SAVE_SETTER(_curl, CURLOPT_READFUNCTION, NULL, _ReadIStreamCallback); - // prepare form - struct curl_httppost *formpost = NULL; - struct curl_httppost *lastptr = NULL; - CURL_FORM_RELEASER(formpost); - curl_formadd(&formpost, &lastptr, - CURLFORM_COPYNAME, "files[]", - CURLFORM_FILENAME, filename.empty() ? "unused" : filename.c_str(), - CURLFORM_STREAM, &inputStream, -#if !CURL_AT_LEAST_VERSION(7,46,0) - // According to curl/lib/formdata.c, CURLFORM_CONTENTSLENGTH argument type is long. - // Also, as va_list is used in curl_formadd, the bit length needs to match exactly. - // streampos can be directly converted to streamoff, but it does not correspond on 32bit machines. - CURLFORM_CONTENTSLENGTH, (long)contentLength, -#else - // Actually we should use CURLFORM_CONTENTLEN, whose argument type is curl_off_t, which is 64bit. - // However, it was added in curl 7.46 and cannot be used in official Windows build. - CURLFORM_CONTENTLEN, (curl_off_t)contentLength, -#endif - CURLFORM_END); - if(!filename.empty()) { - curl_formadd(&formpost, &lastptr, - CURLFORM_COPYNAME, "filename", - CURLFORM_COPYCONTENTS, filename.c_str(), - CURLFORM_END); - } - CURL_OPTION_SAVE_SETTER(_curl, CURLOPT_HTTPPOST, NULL, formpost); - CURL_OPTION_SAVE_SETTER(_curl, CURLOPT_HTTPHEADER, NULL, _httpheadersmultipartformdata); - CURL_PERFORM(_curl); - // get http status - long http_code = 0; - CURL_INFO_GETTER(_curl, CURLINFO_RESPONSE_CODE, &http_code); - - // 204 is when it overwrites the file? - if( http_code != 200 ) { - throw MUJIN_EXCEPTION_FORMAT("upload of %s to %s failed with HTTP status %s", filename%endpoint%http_code, MEC_HTTPServer); - } -} - -void ControllerClientImpl::_UploadDataToControllerViaForm(const void* data, size_t size, const std::string& filename, const std::string& endpoint, double timeout) -{ - CURL_OPTION_SAVE_SETTER(_curl, CURLOPT_URL, NULL, endpoint.c_str()); - _buffer.clear(); - _buffer.str(""); - CURL_OPTION_SAVE_SETTER(_curl, CURLOPT_WRITEFUNCTION, NULL, _WriteStringStreamCallback); - CURL_OPTION_SAVE_SETTER(_curl, CURLOPT_WRITEDATA, NULL, &_buffer); - //timeout is default to 0 (never) - CURL_OPTION_SAVE_SETTER(_curl, CURLOPT_TIMEOUT_MS, 0L, (long)(timeout * 1000L)); - - // prepare form - struct curl_httppost *formpost = NULL; - struct curl_httppost *lastptr = NULL; - CURL_FORM_RELEASER(formpost); - curl_formadd(&formpost, &lastptr, - CURLFORM_PTRNAME, "files[]", - CURLFORM_BUFFER, filename.empty() ? "unused" : filename.c_str(), - CURLFORM_BUFFERPTR, data, - CURLFORM_END); - if(!filename.empty()) { - curl_formadd(&formpost, &lastptr, - CURLFORM_PTRNAME, "filename", - CURLFORM_PTRCONTENTS, filename.c_str(), - CURLFORM_END); - } - CURL_OPTION_SAVE_SETTER(_curl, CURLOPT_HTTPPOST, NULL, formpost); - CURL_OPTION_SAVE_SETTER(_curl, CURLOPT_HTTPHEADER, NULL, _httpheadersmultipartformdata); - CURL_PERFORM(_curl); - // get http status - long http_code = 0; - CURL_INFO_GETTER(_curl, CURLINFO_RESPONSE_CODE, &http_code); - - // 204 is when it overwrites the file? - if( http_code != 200 ) { - throw MUJIN_EXCEPTION_FORMAT("upload of %s to %s failed with HTTP status %s", filename%endpoint%http_code, MEC_HTTPServer); - } -} - -void ControllerClientImpl::_DeleteFileOnController(const std::string& desturi) -{ - MUJIN_LOG_DEBUG(str(boost::format("delete %s")%desturi)) - - // the dest filename of the upload is determined by stripping the leading _basewebdavuri - if( desturi.size() < _basewebdavuri.size() || desturi.substr(0,_basewebdavuri.size()) != _basewebdavuri ) { - throw MUJIN_EXCEPTION_FORMAT("trying to upload a file outside of the webdav endpoint is not allowed: %s", desturi, MEC_HTTPServer); - } - std::string filename = desturi.substr(_basewebdavuri.size()); - - rapidjson::Document pt(rapidjson::kObjectType); - _CallPost(_baseuri+"file/delete/?filename="+filename, "", pt, 200, 5.0); -} - -void ControllerClientImpl::_DeleteDirectoryOnController(const std::string& desturi) -{ - CURL_OPTION_SAVE_SETTER(_curl, CURLOPT_CUSTOMREQUEST, NULL, "DELETE"); - CURL_OPTION_SAVE_SETTER(_curl, CURLOPT_HTTPHEADER, NULL, _httpheadersjson); - CURL_OPTION_SAVE_SETTER(_curl, CURLOPT_URL, NULL, desturi.c_str()); - CURL_PERFORM(_curl); - long http_code = 0; - CURL_INFO_GETTER(_curl, CURLINFO_RESPONSE_CODE, &http_code); - MUJIN_LOG_INFO("response code: " << http_code); -} - -size_t ControllerClientImpl::_ReadUploadCallback(void *ptr, size_t size, size_t nmemb, void *stream) -{ - // in real-world cases, this would probably get this data differently as this fread() stuff is exactly what the library already would do by default internally - size_t nread = fread(ptr, size, nmemb, (FILE*)stream); - //fprintf(stderr, "*** We read %" CURL_FORMAT_CURL_OFF_T " bytes from file\n", nread); - return nread; -} - -size_t ControllerClientImpl::_ReadInMemoryUploadCallback(void *ptr, size_t size, size_t nmemb, void *stream) -{ - std::pair::const_iterator, size_t>* pstreamdata = static_cast::const_iterator, size_t>*>(stream); - size_t nBytesToRead = size*nmemb; - if( nBytesToRead > pstreamdata->second ) { - nBytesToRead = pstreamdata->second; - } - if( nBytesToRead > 0 ) { - std::copy(pstreamdata->first, pstreamdata->first+nBytesToRead, static_cast(ptr)); - pstreamdata->first += nBytesToRead; - pstreamdata->second -= nBytesToRead; - } - return nBytesToRead; -} - -void ControllerClientImpl::GetDebugInfos(std::vector& debuginfos, double timeout) -{ - rapidjson::Document pt(rapidjson::kObjectType); - CallGet(str(boost::format("debug/?format=json&limit=0")), pt, 200, timeout); - rapidjson::Value& objects = pt["objects"]; - - debuginfos.resize(objects.Size()); - size_t iobj = 0; - for (rapidjson::Document::ValueIterator it = objects.Begin(); it != objects.End(); ++it) { - DebugResourcePtr debuginfo(new DebugResource(shared_from_this(), GetJsonValueByKey(*it, "pk"))); - - //LoadJsonValueByKey(*it, "datemodified", debuginfo->datemodified); - LoadJsonValueByKey(*it, "description", debuginfo->description); - //LoadJsonValueByKey(*it, "downloadUri", debuginfo->downloadUri); - LoadJsonValueByKey(*it, "name", debuginfo->name); - //LoadJsonValueByKey(*it, "resource_uri", debuginfo->resource_uri); - LoadJsonValueByKey(*it, "size", debuginfo->size); - - debuginfos.at(iobj++) = debuginfo; - } -} - -void ControllerClientImpl::ListFilesInController(std::vector& fileentries, const std::string &dirname, double timeout) -{ - rapidjson::Document pt(rapidjson::kObjectType); - _CallGet(_baseuri+"file/list/?dirname="+dirname, pt, 200, timeout); - fileentries.resize(pt.MemberCount()); - size_t iobj = 0; - for (rapidjson::Document::MemberIterator it = pt.MemberBegin(); it != pt.MemberEnd(); ++it) { - FileEntry &fileentry = fileentries.at(iobj); - - fileentry.filename = it->name.GetString(); - LoadJsonValueByKey(it->value, "modified", fileentry.modified); - LoadJsonValueByKey(it->value, "size", fileentry.size); - - iobj++; - } -} - -} // end namespace mujinclient diff --git a/src/controllerclientimpl.h b/src/controllerclientimpl.h deleted file mode 100644 index 963f0187..00000000 --- a/src/controllerclientimpl.h +++ /dev/null @@ -1,293 +0,0 @@ -// -*- coding: utf-8 -*- -// Copyright (C) 2012-2013 MUJIN Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -/** \file controllerclientimpl.h - \brief Private header file implementing the ControllerClient interface - */ -#ifndef MUJIN_CONTROLLERCLIENT_IMPL_H -#define MUJIN_CONTROLLERCLIENT_IMPL_H - -#include - -#include - -namespace mujinclient { - -class ControllerClientImpl : public ControllerClient, public boost::enable_shared_from_this -{ -public: - ControllerClientImpl(const std::string& usernamepassword, const std::string& baseuri, const std::string& proxyserverport, const std::string& proxyuserpw, int options, double timeout); - virtual ~ControllerClientImpl(); - - virtual const std::string& GetUserName() const; - virtual const std::string& GetBaseURI() const; - - virtual std::string GetVersion(); - virtual void SetCharacterEncoding(const std::string& newencoding); - virtual void SetProxy(const std::string& serverport, const std::string& userpw); - virtual void SetUnixEndpoint(const std::string& unixendpoint) override; - virtual void SetLanguage(const std::string& language); - virtual void SetUserAgent(const std::string& userAgent); - virtual void SetAdditionalHeaders(const std::vector& additionalHeaders); - virtual void RestartServer(double timeout); - virtual void _ExecuteGraphQuery(const char* operationName, const char* query, const rapidjson::Value& rVariables, rapidjson::Value& rResult, rapidjson::Document::AllocatorType& rAlloc, double timeout, bool checkForErrors, bool returnRawResponse); - virtual void ExecuteGraphQuery(const char* operationName, const char* query, const rapidjson::Value& rVariables, rapidjson::Value& rResult, rapidjson::Document::AllocatorType& rAlloc, double timeout); - virtual void ExecuteGraphQueryRaw(const char* operationName, const char* query, const rapidjson::Value& rVariables, rapidjson::Value& rResult, rapidjson::Document::AllocatorType& rAlloc, double timeout); - virtual void CancelAllJobs(); - virtual void GetRunTimeStatuses(std::vector& statuses, int options); - virtual void GetScenePrimaryKeys(std::vector& scenekeys); - virtual SceneResourcePtr RegisterScene_UTF8(const std::string& uri, const std::string& scenetype); - virtual SceneResourcePtr RegisterScene_UTF16(const std::wstring& uri, const std::string& scenetype); - virtual SceneResourcePtr ImportSceneToCOLLADA_UTF8(const std::string& importuri, const std::string& importformat, const std::string& newuri, bool overwrite=false); - virtual SceneResourcePtr ImportSceneToCOLLADA_UTF16(const std::wstring& importuri, const std::string& importformat, const std::wstring& newuri, bool overwrite=false); - - virtual void SyncUpload_UTF8(const std::string& sourcefilename, const std::string& destinationdir, const std::string& scenetype); - virtual void SyncUpload_UTF16(const std::wstring& sourcefilename_utf16, const std::wstring& destinationdir_utf16, const std::string& scenetype); - - virtual void UploadFileToController_UTF8(const std::string& filename, const std::string& desturi); - virtual void UploadFileToController_UTF16(const std::wstring& filename, const std::wstring& desturi); - virtual void UploadDataToController_UTF8(const void* data, size_t size, const std::string& desturi); - virtual void UploadDataToController_UTF16(const void* data, size_t size, const std::wstring& desturi); - virtual void UploadDirectoryToController_UTF8(const std::string& copydir, const std::string& desturi); - virtual void UploadDirectoryToController_UTF16(const std::wstring& copydir, const std::wstring& desturi); - virtual void DownloadFileFromController_UTF8(const std::string& desturi, std::vector& vdata); - virtual void DownloadFileFromController_UTF16(const std::wstring& desturi, std::vector& vdata); - virtual void DownloadFileFromControllerIfModifiedSince_UTF8(const std::string& desturi, long localtimeval, long &remotetimeval, std::vector& vdata, double timeout); - virtual void DownloadFileFromControllerIfModifiedSince_UTF16(const std::wstring& desturi, long localtimeval, long &remotetimeval, std::vector& vdata, double timeout); - virtual long GetModifiedTime(const std::string& uri, double timeout); - virtual void DeleteFileOnController_UTF8(const std::string& desturi); - virtual void DeleteFileOnController_UTF16(const std::wstring& desturi); - virtual void DeleteDirectoryOnController_UTF8(const std::string& desturi); - virtual void DeleteDirectoryOnController_UTF16(const std::wstring& desturi); - virtual void ListFilesInController(std::vector& fileentries, const std::string &dirname, double timeout); - - virtual void SaveBackup(std::ostream& outputStream, bool config, bool media, const std::string& backupscenepks, double timeout); - virtual void RestoreBackup(std::istream& inputStream, bool config, bool media, double timeout); - virtual void Upgrade(std::istream& inputStream, bool autorestart, bool uploadonly, double timeout); - virtual bool GetUpgradeStatus(std::string& status, double &progress, double timeout); - virtual void CancelUpgrade(double timeout); - virtual void Reboot(double timeout); - virtual void DeleteAllScenes(double timeout); - virtual void DeleteAllITLPrograms(double timeout); - - virtual void ModifySceneAddReferenceObjectPK(const std::string &scenepk, const std::string &referenceobjectpk, double timeout = 5.0); - virtual void ModifySceneRemoveReferenceObjectPK(const std::string &scenepk, const std::string &referenceobjectpk, double timeout = 5.0); - - /// \brief expectedhttpcode is not 0, then will check with the returned http code and if not equal will throw an exception - int CallGet(const std::string& relativeuri, rapidjson::Document& pt, int expectedhttpcode=200, double timeout = 5.0); - - /// \brief expectedhttpcode is not 0, then will check with the returned http code and if not equal will throw an exception - int CallGet(const std::string& relativeuri, std::string& outputdata, int expectedhttpcode=200, double timeout = 5.0); - - /// \brief expectedhttpcode is not 0, then will check with the returned http code and if not equal will throw an exception - int CallGet(const std::string& relativeuri, std::ostream& outputStream, int expectedhttpcode=200, double timeout = 5.0); - - /// \brief expectedhttpcode is not 0, then will check with the returned http code and if not equal will throw an exception - int CallGet(const std::string& relativeuri, std::vector& outputdata, int expectedhttpcode=200, double timeout = 5.0); - - /// \brief expectedhttpcode is not 0, then will check with the returned http code and if not equal will throw an exception - /// - /// \param relativeuri URL-encoded UTF-8 encoded - /// \param data encoded depending on the character encoding set on the system - int CallPost(const std::string& relativeuri, const std::string& data, rapidjson::Document& pt, int expectedhttpcode=201, double timeout = 5.0); - - /// \param data utf-8 encoded - int CallPost_UTF8(const std::string& relativeuri, const std::string& data, rapidjson::Document& pt, int expectedhttpcode=201, double timeout = 5.0); - /// \param data utf-16 encoded - int CallPost_UTF16(const std::string& relativeuri, const std::wstring& data, rapidjson::Document& pt, int expectedhttpcode=201, double timeout = 5.0); - - /// \brief puts json string - /// \param relativeuri relative uri to put at - /// \param data json string to put - /// \param pt reply is stored - /// \param expectedhttpcode expected http code - /// \param timeout timeout of puts - /// \return http code returned - int CallPutJSON(const std::string& relativeuri, const std::string& data, rapidjson::Document& pt, int expectedhttpcode=202, double timeout = 5.0); - - /// \brief puts stl data - /// \param relativeuri relative uri to put at - /// \param data stl raw data - /// \param pt reply is stored - /// \param expectedhttpcode expected http code - /// \param timeout timeout of puts - /// \return http code returned - int CallPutSTL(const std::string& relativeuri, const std::vector& data, rapidjson::Document& pt, int expectedhttpcode=202, double timeout = 5.0); - - - void CallDelete(const std::string& relativeuri, int expectedhttpcode, double timeout = 5.0); - - std::stringstream& GetBuffer(); - - virtual void SetDefaultSceneType(const std::string& scenetype); - virtual const std::string& GetDefaultSceneType(); - virtual void SetDefaultTaskType(const std::string& tasktype); - virtual const std::string& GetDefaultTaskType(); - - std::string GetScenePrimaryKeyFromURI_UTF8(const std::string& uri); - std::string GetScenePrimaryKeyFromURI_UTF16(const std::wstring& uri); - std::string GetPrimaryKeyFromName_UTF8(const std::string& name); - std::string GetPrimaryKeyFromName_UTF16(const std::wstring& name); - std::string GetNameFromPrimaryKey_UTF8(const std::string& pk); - std::wstring GetNameFromPrimaryKey_UTF16(const std::string& pk); - - /// \brief create geometry for a link of an object - /// \param objectPk primary key for the object - /// \param geometryName name of the geometry - /// \param linkPk primary key for the link - /// \param timeout timeout of creating object geometry - /// \return primary key for the geometry created - std::string CreateObjectGeometry(const std::string& objectPk, const std::string& geometryName, const std::string& linkPk, const std::string& geomtype, double timeout = 5); - - std::string CreateIkParam(const std::string& objectPk, const std::string& name, const std::string& iktype, double timeout = 5); - std::string CreateLink(const std::string& objectPk, const std::string& parentlinkPk, const std::string& name, const Real quaternion[4], const Real translate[3], double timeout = 5); - - /// \brief set geometry for an object - /// \param objectPk primary key for the object - /// \param scenePk primary key for the scene - /// \param meshData mesh data to be set to the object - /// \param timeout timeout of creating object geometry - /// \return primary key for the geometry created - std::string SetObjectGeometryMesh(const std::string& objectPk, const std::string& scenePk, const std::vector& meshData, const std::string& unit = "mm", double timeout = 5); - - void GetDebugInfos(std::vector& debuginfos, double timeout = 5); - - inline std::string GetBaseUri() const - { - return _baseuri; - } - - /// \brief URL-encode raw string - inline std::string EscapeString(const std::string& raw) const - { - boost::shared_ptr pstr(curl_easy_escape(_curl, raw.c_str(), raw.size()), curl_free); - return std::string(pstr.get()); - } - - /// \brief decode URL-encoded raw string - inline std::string UnescapeString(const std::string& raw) const - { - int outlength = 0; - boost::shared_ptr pstr(curl_easy_unescape(_curl, raw.c_str(), raw.size(), &outlength), curl_free); - return std::string(pstr.get(), outlength); - } - - -protected: - - int _CallPut(const std::string& relativeuri, const void* pdata, size_t nDataSize, rapidjson::Document& pt, curl_slist* headers, int expectedhttpcode=202, double timeout = 5.0); - - static int _WriteStringStreamCallback(char *data, size_t size, size_t nmemb, std::stringstream *writerData); - static int _WriteVectorCallback(char *data, size_t size, size_t nmemb, std::vector *writerData); - static int _WriteOStreamCallback(char *data, size_t size, size_t nmemb, std::ostream *writerData); - static int _ReadIStreamCallback(char *data, size_t size, size_t nmemb, std::istream *writerData); - - /// \brief sets up http header for doing http operation with json data - void _SetupHTTPHeadersJSON(); - - /// \brief sets up http header for doing http operation with stl data - void _SetupHTTPHeadersSTL(); - - /// \brief sets up http header for doing http operation with multipart/form-data data - void _SetupHTTPHeadersMultipartFormData(); - - /// \brief given a raw uri with "mujin:/", return the real network uri - /// - /// mutex should be locked - /// \param bEnsurePath if true, will make sure the directories on the server side are created - /// \param bEnsureSlash if true, will ensure returned uri ends with slash / - std::string _PrepareDestinationURI_UTF8(const std::string& rawuri, bool bEnsurePath=true, bool bEnsureSlash=false, bool bIsDirectory=false); - std::string _PrepareDestinationURI_UTF16(const std::wstring& rawuri, bool bEnsurePath=true, bool bEnsureSlash=false, bool bIsDirectory=false); - - // encode a URL without the / separator - std::string _EncodeWithoutSeparator(const std::string& raw); - - /// \param relativeuri utf-8 encoded directory inside the user webdav folder. has a trailing slash. relative to real uri - void _EnsureWebDAVDirectories(const std::string& relativeuri, double timeout = 3.0); - - /// For all webdav internal functions: mutex is already locked, desturi directories are already created - //@{ - - /// \param desturi expects the fully resolved URI to pass to curl - int _CallGet(const std::string& desturi, rapidjson::Document& pt, int expectedhttpcode=200, double timeout = 5.0); - int _CallGet(const std::string& desturi, std::string& outputdata, int expectedhttpcode=200, double timeout = 5.0); - int _CallGet(const std::string& desturi, std::vector& outputdata, int expectedhttpcode=200, double timeout = 5.0); - int _CallGet(const std::string& desturi, std::ostream& outputStream, int expectedhttpcode=200, double timeout = 5.0); - int _CallPost(const std::string& desturi, const std::string& data, rapidjson::Document& pt, int expectedhttpcode=201, double timeout = 5.0); - - /// \brief desturi is URL-encoded. Also assume _mutex is locked. - virtual void _UploadFileToController_UTF8(const std::string& filename, const std::string& desturi); - /// \brief desturi is URL-encoded. Also assume _mutex is locked. - virtual void _UploadFileToController_UTF16(const std::wstring& filename, const std::string& desturi); - - /// \brief uploads a single file, to dest location specified by filename - /// - /// overwrites the file if it already exists. - /// \param inputStream the stream represententing the backup. It needs to be seekable to get the size (ifstream subclass is applicable to files). - virtual void _UploadFileToControllerViaForm(std::istream& inputStream, const std::string& filename, const std::string& endpoint, double timeout = 0); - - /// \brief uploads a single file, to dest location specified by filename - /// - /// overwrites the file if it already exists. - /// \param data the buffer represententing the file - /// \param size the length of the buffer represententing the file - virtual void _UploadDataToControllerViaForm(const void* data, size_t size, const std::string& filename, const std::string& endpoint, double timeout = 0); - - /// \brief desturi is URL-encoded. Also assume _mutex is locked. - virtual void _UploadDirectoryToController_UTF8(const std::string& copydir, const std::string& desturi); - /// \brief desturi is URL-encoded. Also assume _mutex is locked. - virtual void _UploadDirectoryToController_UTF16(const std::wstring& wcopydir, const std::string& desturi); - - /// \brief desturi is URL-encoded. Also assume _mutex is locked. - virtual void _DeleteFileOnController(const std::string& desturi); - /// \brief desturi is URL-encoded. Also assume _mutex is locked. - virtual void _DeleteDirectoryOnController(const std::string& desturi); - - /// \brief desturi is URL-encoded. Also assume _mutex is locked. - virtual void _DownloadFileFromController(const std::string& desturi, long localtimeval, long &remotetimeval, std::vector& vdata, double timeout = 5); - - //@} - - /// \brief read upload function for win32. - /// MUST also provide this read callback using CURLOPT_READFUNCTION. Failing to do so will give you a crash since a DLL may not use the variable's memory when passed in to it from an app like this. */ - static size_t _ReadUploadCallback(void *ptr, size_t size, size_t nmemb, void *stream); - - /// \param stream is std::pair::const_iterator, size_t>*, which gets incremented everytime this function is called. - static size_t _ReadInMemoryUploadCallback(void *ptr, size_t size, size_t nmemb, void *stream); - - int _lastmode; - CURL *_curl; - boost::mutex _mutex; - std::stringstream _buffer; - std::string _baseuri, _baseapiuri, _basewebdavuri, _uri, _username; - - curl_slist *_httpheadersjson; - curl_slist *_httpheadersstl; - curl_slist *_httpheadersmultipartformdata; - std::string _charset, _language; - std::string _csrfmiddlewaretoken; - std::vector _additionalHeaders; ///< list of "Header-Name: header-value" additional http headers - - rapidjson::Document _profile; ///< user profile and versioning - std::string _errormessage; ///< set when an error occurs in libcurl - - std::string _defaultscenetype, _defaulttasktype; - - rapidjson::StringBuffer _rRequestStringBufferCache; ///< cache for request string, protected by _mutex -}; - -typedef boost::shared_ptr ControllerClientImplPtr; - -} // end namespace mujinclient - -#endif diff --git a/src/createwebstackclient.cpp b/src/createwebstackclient.cpp new file mode 100644 index 00000000..56a9f952 --- /dev/null +++ b/src/createwebstackclient.cpp @@ -0,0 +1,92 @@ +#include +#include "logging.h" + +MUJIN_LOGGER("mujin.mujinwebstackclientcpp.createwebstackclient"); + +namespace mujinwebstackclient { + +namespace { + +// Versions of the below implemented in terms of the WebstackClientInfo, +// to avoid re-parsing the URL. + +bool IsWebstackLocal(const WebstackClientInfo& clientInfo) +{ + if (clientInfo.httpPort != 0 && clientInfo.httpPort != 80) { + return false; + } + return IsHostnameLocal(clientInfo.host.c_str(), clientInfo.host.length()); +} + +const char* GetUnixEndpointForLocalWebstack(const WebstackClientInfo& clientInfo) +{ + if (IsWebstackLocal(clientInfo)) { + const char* unixendpoint = std::getenv("MUJIN_WEBSTACK_UNIX_ENDPOINT"); + if (unixendpoint != nullptr && unixendpoint[0] != '\0') { + MUJIN_LOG_DEBUG(boost::str(boost::format("forcing webstack client to use unix endpoint \"%s\" since url is \"%s\"")%unixendpoint%clientInfo.GetURL(true))); + return unixendpoint; + } + MUJIN_LOG_DEBUG(boost::str(boost::format("No unix endpoint -- url is \"%s\"")%clientInfo.GetURL(true))); + } + MUJIN_LOG_DEBUG(boost::str(boost::format("Not using unix endpoint since url is \"%s\"")%clientInfo.GetURL(true))); + return ""; +} + +} + +/// \brief determine if hostname is local, if len is not given, then use strlen to determine string length +bool IsHostnameLocal(const char* hostname, ssize_t len) +{ + if (len == -1) { + len = std::strlen(hostname); + } + if (len == sizeof("127.0.0.1") - 1 && strncmp(hostname, "127.0.0.1", len) == 0) { + return true; + } + if (len == sizeof("localhost") - 1 && strncmp(hostname, "localhost", len) == 0) { + return true; + } + char localHostname[HOST_NAME_MAX + 1] = {}; + if (gethostname(localHostname, sizeof(localHostname)) != 0) { + return false; + } + // If the null-terminated hostname is too large to fit, then the name is truncated, and no error is returned (but see NOTES below). + // POSIX.1 says that if such truncation occurs, then it is unspecified whether the returned buffer includes a terminating null byte. + localHostname[HOST_NAME_MAX] = '\0'; + const size_t localHostnameLen = std::strlen(localHostname); + if ((size_t)len == localHostnameLen && strncmp(hostname, localHostname, len) == 0) { + return true; + } + return false; +} + +/// \brief determine if the url is pointing to local webstack +bool IsWebstackLocal(const char* url) +{ + return IsWebstackLocal(WebstackClientInfo::FromUrl(url)); +} + +/// \brief determine the unix endpoint if webstack is local +const char* GetUnixEndpointForLocalWebstack(const char* url) +{ + return GetUnixEndpointForLocalWebstack(WebstackClientInfo::FromUrl(url)); +} + +/// \brief Transparently diverge to private webstack if url is localhost +mujinwebstackclient::WebstackClientPtr CreateWebstackClient( + const std::string& usernamepassword, + const std::string& url, + const std::string& proxyserverport, + const std::string& proxyuserpw, + int options, + double timeout) +{ + WebstackClientPtr pClient = WebstackClient::CreateWebstackClient(usernamepassword, url, proxyserverport, proxyuserpw, options, timeout); + std::string unixendpoint = GetUnixEndpointForLocalWebstack(pClient->GetClientInfo()); + if (!unixendpoint.empty()) { + pClient->SetUnixEndpoint(unixendpoint); + } + return pClient; +} + +} // namespace mujinwebstackclient diff --git a/src/include/mujinwebstackclientcpp/createwebstackclient.h b/src/include/mujinwebstackclientcpp/createwebstackclient.h new file mode 100644 index 00000000..9e23ecf0 --- /dev/null +++ b/src/include/mujinwebstackclientcpp/createwebstackclient.h @@ -0,0 +1,51 @@ +// -*- coding: utf-8 -*- +// Copyright (C) MUJIN Inc. +// Do not distribute this file outside of Mujin + +#ifndef __MUJIN_CONTROLLERCOMMON_CREATEWEBSTACKCLIENT__ +#define __MUJIN_CONTROLLERCOMMON_CREATEWEBSTACKCLIENT__ + +#include +#include + +namespace mujinwebstackclient { + +/// \brief determine if hostname is local, if len is not given, then use strlen to determine string length +MUJINWEBSTACKCLIENT_API bool IsHostnameLocal(const char* hostname, ssize_t len = -1); + +/// \brief determine if the url is pointing to local webstack +MUJINWEBSTACKCLIENT_API bool IsWebstackLocal(const char* url); + +/// \brief determine the unix endpoint if webstack is local +MUJINWEBSTACKCLIENT_API const char* GetUnixEndpointForLocalWebstack(const char* url); + +/** \en \brief creates the controller with an account. This function is not thread safe. + + You must not call it when any other thread in the program (i.e. a thread sharing the same memory) is running. + \param usernamepassword user:password + \param url the URI-encoded URL of controller server, it needs to have a trailing slash. It can also be in the form of https://username@server/ in order to force login of a particular user. + \param proxyserverport Specify proxy server to use. To specify port number in this string, append :[port] to the end of the host name. The proxy string may be prefixed with [protocol]:// since any such prefix will be ignored. The proxy's port number may optionally be specified with the separate option. If not specified, will default to using port 1080 for proxies. Setting to empty string will disable the proxy. + \param proxyuserpw If non-empty, [user name]:[password] to use for the connection to the HTTP proxy. + \param options extra options for connecting to the controller. If 0x1 is set, the client will optimize usage to only allow GET calls. Set 0x80000000 if using a development server. + + \ja \brief MUJINコントローラのクライアントを作成する。この関数はスレッドセーフではない。 + + この関数はスレッドセーフではないため、呼び出す時に他のスレッドが走っていないようにご注意ください。 + \param usernamepassword ユーザ:パスワード + \param url コントローラにアクセスするためのURLです。スラッシュ「/」で終わる必要があります。強制的にユーザも指定出来ます、例えばhttps://username@server/。 + \param proxyserverport Specify proxy server to use. To specify port number in this string, append :[port] to the end of the host name. The proxy string may be prefixed with [protocol]:// since any such prefix will be ignored. The proxy's port number may optionally be specified with the separate option. If not specified, will default to using port 1080 for proxies. Setting to empty string will disable the proxy. + \param proxyuserpw If non-empty, [user name]:[password] to use for the connection to the HTTP proxy. + \param options 1が指定されたら、クライアントがGETのみを呼び出し出来ます。それで初期化がもっと速くなれます。 + \param timeout set timeout in seconds for the initial login requests + */ +MUJINWEBSTACKCLIENT_API WebstackClientPtr CreateWebstackClient( + const std::string& usernamepassword, + const std::string& url, + const std::string& proxyserverport = std::string(), + const std::string& proxyuserpw = std::string(), + int options = 0, + double timeout = 3.0); + +} // namespace mujinwebstackclient + +#endif // __MUJIN_CONTROLLERCOMMON_CREATEWEBSTACKCLIENT__ diff --git a/src/include/mujinwebstackclientcpp/mujinexceptions.h b/src/include/mujinwebstackclientcpp/mujinexceptions.h new file mode 100644 index 00000000..6052ae34 --- /dev/null +++ b/src/include/mujinwebstackclientcpp/mujinexceptions.h @@ -0,0 +1,113 @@ +// -*- coding: utf-8 -*- +// Copyright (C) 2016 MUJIN Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +/** \file mujinexceptions.h + \brief Exception definitions. + */ +#ifndef MUJIN_WEBSTACK_EXCEPTIONS_H +#define MUJIN_WEBSTACK_EXCEPTIONS_H + +namespace mujinwebstackclient { + +#include + +enum MujinErrorCode { + MEC_Failed=0, + MEC_InvalidArguments=1, ///< passed in input arguments are not valid + MEC_CommandNotSupported=3, ///< string command could not be parsed or is not supported + MEC_Assert=4, + MEC_NotInitialized=9, ///< when object is used without it getting fully initialized + MEC_InvalidState=10, ///< the state of the object is not consistent with its parameters, or cannot be used. This is usually due to a programming error where a vector is not the correct length, etc. + MEC_Timeout=11, ///< process timed out + MEC_HTTPClient=12, ///< HTTP client error + MEC_HTTPServer=13, ///< HTTP server error + MEC_UserAuthentication=14, ///< authentication failed + MEC_AlreadyExists=15, ///< the resource already exists and overwriting terminated + MEC_BinPickingError=16, ///< BinPicking failed + MEC_HandEyeCalibrationError=17, ///< HandEye Calibration failed + MEC_ZMQNoResponse=20, ///< No response from the zmq server, using REQ-REP + MEC_GraphQueryError=21, ///< GraphQL failed to execute +}; + +inline const char* GetErrorCodeString(MujinErrorCode error) +{ + switch(error) { + case MEC_Failed: return "Failed"; + case MEC_InvalidArguments: return "InvalidArguments"; + case MEC_CommandNotSupported: return "CommandNotSupported"; + case MEC_Assert: return "Assert"; + case MEC_NotInitialized: return "NotInitialized"; + case MEC_InvalidState: return "InvalidState"; + case MEC_Timeout: return "Timeout"; + case MEC_HTTPClient: return "HTTPClient"; + case MEC_HTTPServer: return "HTTPServer"; + case MEC_UserAuthentication: return "UserAuthentication"; + case MEC_AlreadyExists: return "AlreadyExists"; + case MEC_BinPickingError: return "BinPickingError"; + case MEC_HandEyeCalibrationError: return "HandEyeCalibrationError"; + case MEC_ZMQNoResponse: return "NoResponse"; + case MEC_GraphQueryError: return "GraphQueryError"; + } + // should throw an exception? + return ""; +} + +/// \brief Exception that all Mujin internal methods throw; the error codes are held in \ref MujinErrorCode. +class MUJINWEBSTACKCLIENT_API MujinException : public std::exception +{ +public: + MujinException() : std::exception(), _s("unknown exception"), _error(MEC_Failed) { + } + MujinException(const std::string& s, MujinErrorCode error=MEC_Failed) : std::exception() { + _error = error; + _s = "mujin ("; + _s += GetErrorCodeString(_error); + _s += "): "; + _s += s; + } + virtual ~MujinException() throw() { + } + char const* what() const throw() { + return _s.c_str(); + } + const std::string& message() const { + return _s; + } + MujinErrorCode GetCode() const { + return _error; + } +private: + std::string _s; + MujinErrorCode _error; +}; + +/// \brief Error that can be thrown by ExecuteGraphQuery API, use GetGraphQueryErrorCode to get detailed error code +class MUJINWEBSTACKCLIENT_API MujinGraphQueryError : public MujinException +{ +public: + MujinGraphQueryError(const std::string& s, const std::string& graphQueryErrorCode) : MujinGraphQueryError(s, graphQueryErrorCode.c_str()) {} + MujinGraphQueryError(const std::string& s, const char* graphQueryErrorCode) : MujinException(s, MEC_GraphQueryError) { + _graphQueryErrorCode = graphQueryErrorCode; + } + + /// \brief GraphQL error code, such as "not-found" + const std::string& GetGraphQueryErrorCode() const { + return _graphQueryErrorCode; + } + +private: + std::string _graphQueryErrorCode; +}; + +} // namespace mujinwebstackclient +#endif diff --git a/src/include/mujinwebstackclientcpp/mujinjson.h b/src/include/mujinwebstackclientcpp/mujinjson.h new file mode 100644 index 00000000..1c252406 --- /dev/null +++ b/src/include/mujinwebstackclientcpp/mujinjson.h @@ -0,0 +1,940 @@ +// -*- coding: utf-8 -*- +// Copyright (C) 2012-2017 MUJIN Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +/** \file mujinjson.h + \brief Wrapper for rapidjson. + */ +#ifndef MUJIN_WEBSTACK_JSON_H +#define MUJIN_WEBSTACK_JSON_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include + +#ifndef MUJINJSONWEBSTACK_LOAD_REQUIRED_JSON_VALUE_BY_KEY +#define MUJINJSONWEBSTACK_LOAD_REQUIRED_JSON_VALUE_BY_KEY(rValue, key, param) \ + { \ + if (!mujinjsonwebstack::LoadJsonValueByKey(rValue, key, param))) \ + { \ + throw mujinjsonwebstack::MujinJSONWebstackException(boost::str(boost::format(("[%s, %u] assertmujinjsonwebstack::LoadJsonValueByKey(%s, %s, %s))"))%__FILE__%__LINE__%# rValue%key%# param)); \ + } \ + } +#endif // MUJINJSONWEBSTACK_LOAD_REQUIRED_JSON_VALUE_BY_KEY + +namespace mujinjsonwebstack { + +#ifndef MujinJSONException + +enum MujinJSONErrorCode +{ + MJE_Failed=0, +}; + +// define a MujinJSONException class +inline const char* GetErrorCodeString(MujinJSONErrorCode error) +{ + switch(error) { + case MJE_Failed: return "Failed"; + } + // throw an exception? + return ""; +} + +/// \brief Exception thatmujinjsonwebstack internal methods throw; the error codes are held in \ref MujinJSONErrorCode. +class MujinJSONException : public std::exception +{ +public: + MujinJSONException() : std::exception(), _s(""), _error(MJE_Failed) { + } + MujinJSONException(const std::string& s, MujinJSONErrorCode error=MJE_Failed ) : std::exception() { + _error = error; + _s = "mujin ("; + _s += "): "; + _s += s; + _ssub = s; + } + virtual ~MujinJSONException() throw() { + } + + /// \brief outputs everything + char const* what() const throw() { + return _s.c_str(); + } + + /// \brief outputs everything + const std::string& message() const { + return _s; + } + + /// \briefs just the sub-message + const std::string& GetSubMessage() const { + return _ssub; + } + + MujinJSONErrorCode GetCode() const { + return _error; + } + +private: + std::string _s, _ssub; + MujinJSONErrorCode _error; +}; + +typedef MujinJSONException MujinJSONWebstackException; + +#else +typedef mujinjson::MujinJSONException MujinJSONWebstackException; +#endif + +template inline std::string GetJsonString(const T& t); + +/// \brief gets a string of the Value type for debugging purposes +inline std::string GetJsonTypeName(const rapidjson::Value& v) { + int type = v.GetType(); + switch (type) { + case 0: + return "Null"; + case 1: + return "False"; + case 2: + return "True"; + case 3: + return "Object"; + case 4: + return "Array"; + case 5: + return "String"; + case 6: + return "Number"; + default: + return "Unknown"; + } +} + +template inline std::string GetJsonString(const T& t); + +inline std::string DumpJson(const rapidjson::Value& value, const unsigned int indent=0) { + rapidjson::StringBuffer stringbuffer; + if (indent == 0) { + rapidjson::Writer writer(stringbuffer); + value.Accept(writer); + } else { + rapidjson::PrettyWriter writer(stringbuffer); + writer.SetIndent(' ', indent); + value.Accept(writer); + } + return std::string(stringbuffer.GetString(), stringbuffer.GetSize()); +} + +inline void DumpJson(const rapidjson::Value& value, std::ostream& os, const unsigned int indent=0) { + rapidjson::OStreamWrapper osw(os); + if (indent == 0) { + rapidjson::Writer writer(osw); + value.Accept(writer); + } else { + rapidjson::PrettyWriter writer(osw); + writer.SetIndent(' ', indent); + value.Accept(writer); + } +} + +/// \brief This clears the d's allocator! +inline void ParseJson(rapidjson::Document& d, const char* str, size_t length) { + // repeatedly calling Parse on the same rapidjson::Document will not release previsouly allocated memory, memory will accumulate until the object is destroyed + // we use a new temporary Document to parse, and swap content with the original one, so that memory in original Document will be released when this function ends + // see: https://github.com/Tencent/rapidjson/issues/1333 + // a newer solution that allows reuse of allocated memory is to clear the previous document first + d.SetNull(); + d.GetAllocator().Clear(); + d.Parse(str, length); // parse float in full precision mode + if (d.HasParseError()) { + const std::string substr(str, length < 200 ? length : 200); + + throw MujinJSONWebstackException(boost::str(boost::format("Json string is invalid (offset %u) %s data is '%s'.")%((unsigned)d.GetErrorOffset())%GetParseError_En(d.GetParseError())%substr)); + } +} + +/// \brief This clears the d's allocator! +inline void ParseJson(rapidjson::Document& d, const std::string& str) { + ParseJson(d, str.c_str(), str.size()); +} + +/// \brief This clears the d's allocator! +inline void ParseJson(rapidjson::Document& d, std::istream& is) { + rapidjson::IStreamWrapper isw(is); + // see note in: void ParseJson(rapidjson::Document& d, const std::string& str) + d.SetNull(); + d.GetAllocator().Clear(); + d.ParseStream(isw); // parse float in full precision mode + if (d.HasParseError()) { + throw MujinJSONWebstackException(boost::str(boost::format("Json stream is invalid (offset %u) %s")%((unsigned)d.GetErrorOffset())%GetParseError_En(d.GetParseError()))); + } +} + +inline void ParseJson(rapidjson::Value& r, rapidjson::Document::AllocatorType& alloc, std::istream& is) +{ + rapidjson::IStreamWrapper isw(is); + rapidjson::GenericReader reader(&alloc); + //ClearStackOnExit scope(*this); + + size_t kDefaultStackCapacity = 1024; + rapidjson::Document rTemp(&alloc, kDefaultStackCapacity); // needed by Parse to be a document + rTemp.ParseStream(isw); // parse float in full precision mode + if (rTemp.HasParseError()) { + throw MujinJSONWebstackException(boost::str(boost::format("Json stream is invalid (offset %u) %s")%((unsigned)rTemp.GetErrorOffset())%GetParseError_En(rTemp.GetParseError()))); + } + r.Swap(rTemp); + +// rapidjson::ParseResult parseResult = reader.template Parse(isw, rTemp); +// if( parseResult.IsError() ) { +// *stack_.template Pop(1) +// throw MujinJSONException(boost::str(boost::format("Json stream is invalid (offset %u) %s")%((unsigned)parseResult.Offset())%GetParseError_En(parseResult.Code()))); +// } +} + + +template +MUJINWEBSTACKCLIENT_API void ParseJsonFile(rapidjson::Document& d, const char* filename, Container& buffer); + +inline void ParseJsonFile(rapidjson::Document& d, const char* filename) +{ + std::vector buffer; + + return ParseJsonFile(d, filename, buffer); +} + +template +void ParseJsonFile(rapidjson::Document& d, const std::string& filename, Container& buffer) +{ + return ParseJsonFile(d, filename.c_str(), buffer); +} + +inline void ParseJsonFile(rapidjson::Document& d, const std::string& filename) +{ + std::vector buffer; + + return ParseJsonFile(d, filename.c_str(), buffer); +} + + +class JsonSerializable { +public: + virtual void LoadFromJson(const rapidjson::Value& v) = 0; + virtual void SaveToJson(rapidjson::Value& v, rapidjson::Document::AllocatorType& alloc) const = 0; + virtual void SaveToJson(rapidjson::Document& d) const { + SaveToJson(d, d.GetAllocator()); + } +}; + +template inline T LexicalCast(const S& v, const std::string& typeName) { + try { + T t = boost::lexical_cast(v); + return t; + } + catch (const boost::bad_lexical_cast& ex) { + std::stringstream ss; + ss << v; + throw MujinJSONWebstackException("Cannot convert \"" + ss.str() + "\" to type " + typeName); + } +} + +//store a json value to local data structures +//for compatibility with ptree, type conversion is made. will remove them in the future +inline void LoadJsonValue(const rapidjson::Value& v, JsonSerializable& t) { + t.LoadFromJson(v); +} + +inline void LoadJsonValue(const rapidjson::Value& v, std::string& t) { + if (v.IsString()) { + t.assign(v.GetString(), v.GetStringLength()); // specify size to allow null characters + } else if (v.IsInt64()) { + //TODO: add warnings on all usages of lexical_cast + t = LexicalCast(v.GetInt64(), "String"); + } else { + throw MujinJSONWebstackException("Cannot convert json type " + GetJsonString(v) + " to String"); + } + +} + +inline void LoadJsonValue(const rapidjson::Value& v, int& t) { + if (v.IsInt()) { + t = v.GetInt(); + } else if (v.IsUint()) { + t = v.GetUint(); + } else if (v.IsString()) { + t = LexicalCast(v.GetString(), "Int"); + } else if (v.IsBool()) { + t = v.GetBool() ? 1 : 0; + } else { + throw MujinJSONWebstackException("Cannot convert json type " + GetJsonString(v) + " to Int"); + } +} + +inline void LoadJsonValue(const rapidjson::Value& v, int8_t& t) { + if (v.IsInt()) { + t = v.GetInt(); + } + else if (v.IsUint()) { + t = v.GetUint(); + } + else if (v.IsString()) { + t = LexicalCast(v.GetString(), "Int8"); + } + else if (v.IsBool()) { + t = v.GetBool() ? 1 : 0; + } else { + throw MujinJSONWebstackException("Cannot convert json type " + GetJsonString(v) + " to Int8"); + } +} + +inline void LoadJsonValue(const rapidjson::Value& v, uint8_t& t) { + if (v.IsUint()) { + t = v.GetUint(); + } else if (v.IsString()) { + t = LexicalCast(v.GetString(), "UInt8"); + } else if (v.IsBool()) { + t = v.GetBool() ? 1 : 0; + } else { + throw MujinJSONWebstackException("Cannot convert json type " + GetJsonString(v) + " to UInt8"); + } +} + +inline void LoadJsonValue(const rapidjson::Value& v, uint16_t& t) { + if (v.IsUint()) { + t = v.GetUint(); + } else if (v.IsString()) { + t = LexicalCast(v.GetString(), "UInt"); + } else if (v.IsBool()) { + t = v.GetBool() ? 1 : 0; + } else { + throw MujinJSONWebstackException("Cannot convert json type " + GetJsonString(v) + " to UInt"); + } +} + +inline void LoadJsonValue(const rapidjson::Value& v, unsigned int& t) { + if (v.IsUint()) { + t = v.GetUint(); + } else if (v.IsString()) { + t = LexicalCast(v.GetString(), "UInt"); + } else if (v.IsBool()) { + t = v.GetBool() ? 1 : 0; + } else { + throw MujinJSONWebstackException("Cannot convert json type " + GetJsonString(v) + " to UInt"); + } +} + +inline void LoadJsonValue(const rapidjson::Value& v, unsigned long long& t) { + if (v.IsUint64()) { + t = v.GetUint64(); + } else if (v.IsString()) { + t = LexicalCast(v.GetString(), "ULongLong"); + } else if (v.IsBool()) { + t = v.GetBool() ? 1 : 0; + } else { + throw MujinJSONWebstackException("Cannot convert json type " + GetJsonString(v) + " to ULongLong"); + } +} + +inline void LoadJsonValue(const rapidjson::Value& v, uint64_t& t) { + if (v.IsUint64()) { + t = v.GetUint64(); + } else if (v.IsString()) { + t = LexicalCast(v.GetString(), "UInt64"); + } else if (v.IsBool()) { + t = v.GetBool() ? 1 : 0; + } else { + throw MujinJSONWebstackException("Cannot convert json type " + GetJsonString(v) + " to UInt64"); + } +} + +inline void LoadJsonValue(const rapidjson::Value& v, int64_t& t) { + if (v.IsInt64()) { + t = v.GetInt64(); + } else if (v.IsString()) { + t = LexicalCast(v.GetString(), "Int64"); + } else if (v.IsBool()) { + t = v.GetBool() ? 1 : 0; + } else { + throw MujinJSONWebstackException("Cannot convert json type " + GetJsonString(v) + " to Int64"); + } +} + +inline void LoadJsonValue(const rapidjson::Value& v, double& t) { + if (v.IsNumber()) { + t = v.GetDouble(); + } else if (v.IsString()) { + t = LexicalCast(v.GetString(), "Double"); + } else { + throw MujinJSONWebstackException("Cannot convert json type " + GetJsonString(v) + " to Double"); + } +} + +inline void LoadJsonValue(const rapidjson::Value& v, float& t) { + if (v.IsNumber()) { + t = v.GetDouble(); + } else if (v.IsString()) { + t = LexicalCast(v.GetString(), "Float"); + } else { + throw MujinJSONWebstackException("Cannot convert json type " + GetJsonString(v) + " to Float"); + } +} + +inline void LoadJsonValue(const rapidjson::Value& v, bool& t) { + if (v.IsInt()) t = v.GetInt(); + else if (v.IsBool()) t = v.GetBool(); + else if (v.IsString()) { + t = LexicalCast(v.GetString(), "Bool"); + } else { + throw MujinJSONWebstackException("Cannot convert json type " + GetJsonString(v) + " to Bool"); + } +} + +template > inline void LoadJsonValue(const rapidjson::Value& v, std::vector& t); + +template inline void LoadJsonValue(const rapidjson::Value& v, std::array& t); + +template inline void LoadJsonValue(const rapidjson::Value& v, boost::shared_ptr& ptr) { + static_assert(std::is_default_constructible::value, "Shared pointer of type must be default-constructible."); + ptr = boost::make_shared(); + LoadJsonValue(v, *ptr); +} + +template inline void LoadJsonValue(const rapidjson::Value& v, std::pair& t) { + if (v.IsArray()) { + if (v.GetArray().Size() == 2) { + LoadJsonValue(v[0], t.first); + LoadJsonValue(v[1], t.second); + } else { + throw MujinJSONWebstackException("List-based map has entry with size != 2"); + } + } else { + throw MujinJSONWebstackException("Cannot convert json type " + GetJsonTypeName(v) + " to Pair"); + } +} + +template inline void LoadJsonValue(const rapidjson::Value& v, T (&p)[N]) { + if (v.IsArray()) { + if (v.GetArray().Size() != N) { + throw MujinJSONWebstackException("Json array size doesn't match"); + } + size_t i = 0; + for (rapidjson::Value::ConstValueIterator it = v.Begin(); it != v.End(); ++it) { + LoadJsonValue(*it, p[i]); + i++; + } + } else { + throw MujinJSONWebstackException("Cannot convert json type " + GetJsonTypeName(v) + " to Array"); + } +} + +template inline void LoadJsonValue(const rapidjson::Value& v, std::vector& t) { + if (v.IsArray()) { + t.clear(); + t.resize(v.GetArray().Size()); + size_t i = 0; + for (rapidjson::Value::ConstValueIterator it = v.Begin(); it != v.End(); ++it) { + LoadJsonValue(*it, t[i]); + i++; + } + } else { + throw MujinJSONWebstackException("Cannot convert json type " + GetJsonTypeName(v) + " to Vector"); + } +} + +template inline void LoadJsonValue(const rapidjson::Value& v, std::array& t) { + if (v.IsArray()) { + if (v.GetArray().Size() != N) { + throw MujinJSONWebstackException( + (boost::format("Cannot convert json type " + GetJsonTypeName(v) + " to Array. " + "Array length does not match (%d != %d)") % N % v.GetArray().Size()).str()); + } + size_t i = 0; + for (rapidjson::Value::ConstValueIterator it = v.Begin(); it != v.End(); ++it) { + LoadJsonValue(*it, t[i]); + i++; + } + } else { + throw MujinJSONWebstackException("Cannot convert json type " + GetJsonTypeName(v) + " to Array"); + } +} + +template inline void LoadJsonValue(const rapidjson::Value& v, std::map& t) { + if (v.IsArray()) { + // list based map + // TODO: is it dangerous? + for (rapidjson::Value::ConstValueIterator itr = v.Begin(); itr != v.End(); ++itr) { + std::pair value; + LoadJsonValue((*itr), value); + t[value.first] = value.second; + } + } else if (v.IsObject()) { + t.clear(); + U value; + for (rapidjson::Value::ConstMemberIterator it = v.MemberBegin(); + it != v.MemberEnd(); ++it) { + LoadJsonValue(it->value, value); + t[std::string(it->name.GetString(), it->name.GetStringLength())] = value; // string can contain null character + } + } else { + throw MujinJSONWebstackException("Cannot convert json type " + GetJsonTypeName(v) + " to Map"); + } +} + +//Save a data structure to rapidjson::Value format + +/*template inline void SaveJsonValue(rapidjson::Value& v, const T& t, rapidjson::Document::AllocatorType& alloc) {*/ +/*JsonWrapper::SaveToJson(v, t, alloc);*/ +/*}*/ + +inline void SaveJsonValue(rapidjson::Value& v, const JsonSerializable& t, rapidjson::Document::AllocatorType& alloc) { + t.SaveToJson(v, alloc); +} + +inline void SaveJsonValue(rapidjson::Value& v, const std::string& t, rapidjson::Document::AllocatorType& alloc) { + v.SetString(t.c_str(), alloc); +} + +inline void SaveJsonValue(rapidjson::Value& v, const char* t, rapidjson::Document::AllocatorType& alloc) { + v.SetString(t, alloc); +} + +inline void SaveJsonValue(rapidjson::Value& v, int t, rapidjson::Document::AllocatorType& alloc) { + v.SetInt(t); +} + +inline void SaveJsonValue(rapidjson::Value& v, unsigned int t, rapidjson::Document::AllocatorType& alloc) { + v.SetUint(t); +} + +inline void SaveJsonValue(rapidjson::Value& v, long long t, rapidjson::Document::AllocatorType& alloc) { + v.SetInt64(t); +} + +inline void SaveJsonValue(rapidjson::Value& v, int64_t t, rapidjson::Document::AllocatorType& alloc) { + v.SetInt64(t); +} + +inline void SaveJsonValue(rapidjson::Value& v, unsigned long long t, rapidjson::Document::AllocatorType& alloc) { + v.SetUint64(t); +} + +#ifndef _MSC_VER +inline void SaveJsonValue(rapidjson::Value& v, uint64_t t, rapidjson::Document::AllocatorType& alloc) { + v.SetUint64(t); +} +#endif + +inline void SaveJsonValue(rapidjson::Value& v, bool t, rapidjson::Document::AllocatorType& alloc) { + v.SetBool(t); +} + +inline void SaveJsonValue(rapidjson::Value& v, int8_t t, rapidjson::Document::AllocatorType& alloc) { + v.SetInt(t); +} + +inline void SaveJsonValue(rapidjson::Value& v, double t, rapidjson::Document::AllocatorType& alloc) { + v.SetDouble(t); +} + +inline void SaveJsonValue(rapidjson::Value& v, float t, rapidjson::Document::AllocatorType& alloc) { + v.SetDouble(t); +} + +inline void SaveJsonValue(rapidjson::Value& v, const rapidjson::Value& t, rapidjson::Document::AllocatorType& alloc) { + v.CopyFrom(t, alloc); +} + +template > inline void SaveJsonValue(rapidjson::Value& v, const std::vector& t, rapidjson::Document::AllocatorType& alloc); + +template inline void SaveJsonValue(rapidjson::Value& v, const std::array& t, rapidjson::Document::AllocatorType& alloc); + +/** do not remove: otherwise boost::shared_ptr could be treated as bool + */ +template inline void SaveJsonValue(rapidjson::Value& v, const boost::shared_ptr& ptr, rapidjson::Document::AllocatorType& alloc) { + SaveJsonValue(v, *ptr, alloc); +} + +template inline void SaveJsonValue(rapidjson::Value& v, const std::pair& t, rapidjson::Document::AllocatorType& alloc) { + v.SetArray(); + v.Reserve(2, alloc); + rapidjson::Value rFirst; + SaveJsonValue(rFirst, t.first, alloc); + v.PushBack(rFirst, alloc); + rapidjson::Value rSecond; + SaveJsonValue(rSecond, t.second, alloc); + v.PushBack(rSecond, alloc); +} + +template inline void SaveJsonValue(rapidjson::Value& v, const std::vector& t, rapidjson::Document::AllocatorType& alloc) { + v.SetArray(); + v.Reserve(t.size(), alloc); + for (size_t i = 0; i < t.size(); ++i) { + rapidjson::Value tmpv; + SaveJsonValue(tmpv, t[i], alloc); + v.PushBack(tmpv, alloc); + } +} + +template inline void SaveJsonValue(rapidjson::Value& v, const std::array& t, rapidjson::Document::AllocatorType& alloc) { + v.SetArray(); + v.Reserve(N, alloc); + for (size_t i = 0; i < N; ++i) { + rapidjson::Value tmpv; + SaveJsonValue(tmpv, t[i], alloc); + v.PushBack(tmpv, alloc); + } +} + +template > inline void SaveJsonValue(rapidjson::Value& v, const std::vector& t, rapidjson::Document::AllocatorType& alloc) { + v.SetArray(); + v.Reserve(t.size(), alloc); + for (size_t i = 0; i < t.size(); ++i) { + v.PushBack(t[i], alloc); + } +} + +template inline void SaveJsonValue(rapidjson::Value& v, const T (&t)[N], rapidjson::Document::AllocatorType& alloc) { + v.SetArray(); + v.Reserve(N, alloc); + for (size_t i = 0; i < N; ++i) { + rapidjson::Value tmpv; + SaveJsonValue(tmpv, t[i], alloc); + v.PushBack(tmpv, alloc); + } +} + +template inline void SaveJsonValue(rapidjson::Value& v, const double (&t)[N], rapidjson::Document::AllocatorType& alloc) { + v.SetArray(); + v.Reserve(N, alloc); + for (size_t i = 0; i < N; ++i) { + v.PushBack(t[i], alloc); + } +} + +template inline void SaveJsonValue(rapidjson::Value& v, const std::array& t, rapidjson::Document::AllocatorType& alloc) { + v.SetArray(); + v.Reserve(N, alloc); + for (size_t i = 0; i < N; ++i) { + v.PushBack(t[i], alloc); + } +} + +template inline void SaveJsonValue(rapidjson::Value& v, const std::map& t, rapidjson::Document::AllocatorType& alloc) { + v.SetObject(); + for (typename std::map::const_iterator it = t.begin(); it != t.end(); ++it) { + rapidjson::Value name, value; + SaveJsonValue(name, it->first, alloc); + SaveJsonValue(value, it->second, alloc); + v.AddMember(name, value, alloc); + } +} + +template inline void SaveJsonValue(rapidjson::Document& v, const T& t) { + // rapidjson::Value::CopyFrom also doesn't free up memory, need to clear memory + // see note in: void ParseJson(rapidjson::Document& d, const std::string& str) + v.SetNull(); + v.GetAllocator().Clear(); + SaveJsonValue(v, t, v.GetAllocator()); +} + +template inline void SetJsonValueByKey(rapidjson::Value& v, const U& key, const T& t, rapidjson::Document::AllocatorType& alloc); + +//get one json value by key, and store it in local data structures +//return true if key is present. Will return false if the key is not present or the member is Null +template bool inline LoadJsonValueByKey(const rapidjson::Value& v, const char* key, T& t) { + if (!v.IsObject()) { + throw MujinJSONWebstackException("Cannot load value of non-object."); + } + rapidjson::Value::ConstMemberIterator itMember = v.FindMember(key); + if( itMember != v.MemberEnd() ) { + const rapidjson::Value& rMember = itMember->value; + if( !rMember.IsNull() ) { + try { + LoadJsonValue(rMember, t); + return true; + } + catch (const MujinJSONWebstackException& ex) { + throw MujinJSONWebstackException("Got \"" + ex.message() + "\" while parsing the value of \"" + key + "\""); + } + } + } + // Return false if member is null or non-existent + return false; +} +template inline void LoadJsonValueByKey(const rapidjson::Value& v, const char* key, T& t, const U& d) { + if (!v.IsObject()) { + throw MujinJSONWebstackException("Cannot load value of non-object."); + } + + rapidjson::Value::ConstMemberIterator itMember = v.FindMember(key); + if( itMember != v.MemberEnd() && !itMember->value.IsNull() ) { + try { + LoadJsonValue(itMember->value, t); + } + catch (const MujinJSONWebstackException& ex) { + throw MujinJSONWebstackException("Got \"" + ex.message() + "\" while parsing the value of \"" + key + "\""); + } + } + else { + t = d; + } +} + +template inline void LoadJsonValueByPath(const rapidjson::Value& v, const char* key, T& t) { + const rapidjson::Value *child = rapidjson::Pointer(key).Get(v); + if (child && !child->IsNull()) { + LoadJsonValue(*child, t); + } +} +template inline void LoadJsonValueByPath(const rapidjson::Value& v, const char* key, T& t, const U& d) { + const rapidjson::Value *child = rapidjson::Pointer(key).Get(v); + if (child && !child->IsNull()) { + LoadJsonValue(*child, t); + } + else { + t = d; + } +} + + +//work the same as LoadJsonValueByKey, but the value is returned instead of being passed as reference +template T GetJsonValueByKey(const rapidjson::Value& v, const char* key, const U& t) { + if (!v.IsObject()) { + throw MujinJSONWebstackException("Cannot get value of non-object."); + } + + rapidjson::Value::ConstMemberIterator itMember = v.FindMember(key); + if (itMember != v.MemberEnd() ) { + const rapidjson::Value& child = itMember->value; + if (!child.IsNull()) { + T r; + try { + LoadJsonValue(v[key], r); + } + catch (const MujinJSONWebstackException& ex) { + throw MujinJSONWebstackException("Got \"" + ex.message() + "\" while parsing the value of \"" + key + "\""); + } + return r; + } + } + return T(t); +} +template inline T GetJsonValueByKey(const rapidjson::Value& v, const char* key) { + if (!v.IsObject()) { + throw MujinJSONWebstackException("Cannot load value of non-object."); + } + T r = T(); + rapidjson::Value::ConstMemberIterator itMember = v.FindMember(key); + if (itMember != v.MemberEnd() ) { + const rapidjson::Value& child = itMember->value; + if (!child.IsNull()) { + try { + LoadJsonValue(v[key], r); + } + catch (const MujinJSONWebstackException& ex) { + throw MujinJSONWebstackException("Got \"" + ex.message() + "\" while parsing the value of \"" + key + "\""); + } + } + } + return r; +} + +inline std::string GetStringJsonValueByKey(const rapidjson::Value& v, const char* key, const std::string& defaultValue=std::string()) { + return GetJsonValueByKey(v, key, defaultValue); +} + +/// \brief default value is returned when there is no key or value is null +inline const char* GetCStringJsonValueByKey(const rapidjson::Value& v, const char* key, const char* pDefaultValue=nullptr) { + if (!v.IsObject()) { + throw MujinJSONWebstackException("Cannot load value of non-object."); + } + rapidjson::Value::ConstMemberIterator itMember = v.FindMember(key); + if (itMember != v.MemberEnd() ) { + const rapidjson::Value& child = itMember->value; + if (!child.IsNull()) { + if( child.IsString() ) { + return child.GetString(); + } + else { + throw MujinJSONWebstackException("In GetCStringJsonValueByKey, expecting a String, but got a different object type"); + } + } + } + return pDefaultValue; // not present +} + +template inline T GetJsonValueByPath(const rapidjson::Value& v, const char* key) { + T r; + const rapidjson::Value *child = rapidjson::Pointer(key).Get(v); + if (child && !child->IsNull()) { + LoadJsonValue(*child, r); + } + return r; +} + +#if __cplusplus >= 201103 +template +#else +template +#endif +T GetJsonValueByPath(const rapidjson::Value& v, const char* key, const U& t) { + const rapidjson::Value *child = rapidjson::Pointer(key).Get(v); + if (child && !child->IsNull()) { + T r; + LoadJsonValue(*child, r); + return r; + } + else { + return T(t); + } +} + +inline const rapidjson::Value* GetJsonPointerByPath(const rapidjson::Value& value) +{ + return &value; +} + +template const rapidjson::Value* GetJsonPointerByPath(const rapidjson::Value& value, const char (& key)[N], Ps&&... ps) +{ + const rapidjson::Value::ConstMemberIterator it = value.FindMember(rapidjson::Value(key, N - 1)); + if (it != value.MemberEnd()) { + return GetJsonPointerByPath(it->value, static_cast(ps)...); + } else { + return nullptr; + } +} + +template inline void SetJsonValueByPath(rapidjson::Value& d, const char* path, const T& t, rapidjson::Document::AllocatorType& alloc) { + rapidjson::Value v; + SaveJsonValue(v, t, alloc); + rapidjson::Pointer(path).Swap(d, v, alloc); +} + +template inline void SetJsonValueByPath(rapidjson::Document& d, const char* path, const T& t) { + SetJsonValueByPath(d, path, t, d.GetAllocator()); +} + +template inline void SetJsonValueByKey(rapidjson::Value& v, const U& key, const T& t, rapidjson::Document::AllocatorType& alloc) +{ + if (!v.IsObject()) { + throw MujinJSONWebstackException("Cannot set value for non-object."); + } + if (v.HasMember(key)) { + SaveJsonValue(v[key], t, alloc); + } + else { + rapidjson::Value value, name; + SaveJsonValue(name, key, alloc); + SaveJsonValue(value, t, alloc); + v.AddMember(name, value, alloc); + } +} + +template +inline void SetJsonValueByKey(rapidjson::Document& d, const char* key, const T& t) +{ + SetJsonValueByKey(d, key, t, d.GetAllocator()); +} + +template +inline void SetJsonValueByKey(rapidjson::Document& d, const std::string& key, const T& t) +{ + SetJsonValueByKey(d, key.c_str(), t, d.GetAllocator()); +} + +inline void ValidateJsonString(const std::string& str) { + rapidjson::Document d; + if (d.Parse(str.c_str()).HasParseError()) { + throw MujinJSONWebstackException("json string " + str + " is invalid." + GetParseError_En(d.GetParseError())); + } +} + +template inline std::string GetJsonString(const T& t) { + rapidjson::Document d; + SaveJsonValue(d, t); + return DumpJson(d); +} + +template inline std::string GetJsonStringByKey(const U& key, const T& t) { + rapidjson::Document d(rapidjson::kObjectType); + SetJsonValueByKey(d, key, t); + return DumpJson(d); +} + +/** update a json object with another one, new key-value pair will be added, existing ones will be overwritten + */ +inline void UpdateJson(rapidjson::Document& a, const rapidjson::Value& b) { + if (!a.IsObject()) { + throw MujinJSONWebstackException("json object should be a dict to be updated: " + GetJsonString(a)); + } + if (!b.IsObject()) { + throw MujinJSONWebstackException("json object should be a dict to update another dict: " + GetJsonString(b)); + } + for (rapidjson::Value::ConstMemberIterator it = b.MemberBegin(); it != b.MemberEnd(); ++it) { + SetJsonValueByKey(a, it->name.GetString(), it->value); + } +} + +/// \brief One way merge overrideValue into sourceValue. sourceValue will be overwritten. When both sourceValue and overrideValue are objects and a member of overrideValue is a rapidjson object and the corresponding member of sourceValue is also a rapidjson object, call this function recursivelly inside this function. +/// \param sourceValue json value to be updated +/// \param overrideValue read-only json value used to update contents of sourceValue +/// \return true if sourceValue has changed +inline bool UpdateJsonRecursively(rapidjson::Value& sourceValue, const rapidjson::Value& overrideValue, rapidjson::Document::AllocatorType& alloc) { + if (sourceValue == overrideValue) { + return false; + } + + if (!sourceValue.IsObject() || !overrideValue.IsObject()) { + sourceValue.CopyFrom(overrideValue, alloc, true); + return true; + } + + bool hasChanged = false; + for (rapidjson::Value::ConstMemberIterator itOverrideMember = overrideValue.MemberBegin(); itOverrideMember != overrideValue.MemberEnd(); ++itOverrideMember) { + const rapidjson::Value& overrideMemberValue = itOverrideMember->value; + rapidjson::Value::MemberIterator itSourceMember = sourceValue.FindMember(itOverrideMember->name); + if (itSourceMember != sourceValue.MemberEnd()) { // found corresponding member in sourceObject + hasChanged |= UpdateJsonRecursively(itSourceMember->value, overrideMemberValue, alloc); + } + else { // not found corresponding member in sourceObject, so just copy it + rapidjson::Value key(itOverrideMember->name, alloc); + rapidjson::Value val; + val.CopyFrom(overrideMemberValue, alloc, true); + sourceValue.AddMember(key, val, alloc); + hasChanged = true; + } + } + return hasChanged; +} + +} // namespace mujinjsonwebstack + +#endif diff --git a/src/include/mujinwebstackclientcpp/webstackclient.h b/src/include/mujinwebstackclientcpp/webstackclient.h new file mode 100644 index 00000000..5939757d --- /dev/null +++ b/src/include/mujinwebstackclientcpp/webstackclient.h @@ -0,0 +1,409 @@ +// -*- coding: utf-8 -*- +// +// Copyright (C) 2012-2013 MUJIN Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +/** \file mujinwebstackclient.h + \brief Defines the public headers of the MUJIN Controller Client + */ +#ifndef MUJIN_WEBSTACKCLIENT_H +#define MUJIN_WEBSTACKCLIENT_H + +#ifdef _MSC_VER + +#pragma warning(disable:4251)// needs to have dll-interface to be used by clients of class +#pragma warning(disable:4190)// C-linkage specified, but returns UDT 'boost::shared_ptr' which is incompatible with C +#pragma warning(disable:4819)//The file contains a character that cannot be represented in the current code page (932). Save the file in Unicode format to prevent data loss using native typeof + +#ifndef __PRETTY_FUNCTION__ +#define __PRETTY_FUNCTION__ __FUNCDNAME__ +#endif + +#else +#endif + +#if defined(__GNUC__) +#define MUJINWEBSTACKCLIENT_DEPRECATED __attribute__((deprecated)) +#else +#define MUJINWEBSTACKCLIENT_DEPRECATED +#endif + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +//#include + + +namespace mujinwebstackclient { + +/// \brief (scene) file entry in mujin controller +struct FileEntry +{ + std::string filename; + double modified; // in epoch seconds + size_t size; // file size in bytes +}; + +typedef double Real; + +/// \brief connecting to a controller's webstack +class MUJINWEBSTACKCLIENT_API WebstackClientInfo : public mujinjsonwebstack::JsonSerializable +{ +public: + /// \brief given a url "http[s]://[username[:password]@]hostname[:port][/path]", parse WebstackClientInfo + static WebstackClientInfo FromUrl(const char* url); + + virtual void Reset(); + + void LoadFromJson(const rapidjson::Value& rClientInfo) override; + void SaveToJson(rapidjson::Value& rClientInfo, rapidjson::Document::AllocatorType& alloc) const override; + + bool operator==(const WebstackClientInfo &rhs) const; + bool operator!=(const WebstackClientInfo &rhs) const { + return !operator==(rhs); + } + std::string GetURL(bool bIncludeNamePassword) const; + + inline bool IsEnabled() const { + return !host.empty(); + } + + std::string host; + uint16_t httpPort = 0; ///< Post to communicate with the webstack. If 0, then use the default port + std::string username; + std::string password; + std::vector additionalHeaders; ///< expect each value to be in the format of "Header-Name: header-value" + std::string unixEndpoint; ///< unix socket endpoint for communicating with HTTP server over unix socket +}; + +/// \brief Creates on MUJIN Controller instance. +/// +/// Only one call can be made at a time. In order to make multiple calls simultaneously, create another instance. +class MUJINWEBSTACKCLIENT_API WebstackClient +{ +public: + WebstackClient(const std::string& usernamepassword, const std::string& baseuri, const std::string& proxyserverport, const std::string& proxyuserpw, int options, double timeout); + virtual ~WebstackClient(); + + /** \en \brief creates the controller with an account. This function is not thread safe. + + You must not call it when any other thread in the program (i.e. a thread sharing the same memory) is running. + \param usernamepassword user:password + \param url the URI-encoded URL of controller server, it needs to have a trailing slash. It can also be in the form of https://username@server/ in order to force login of a particular user. + \param proxyserverport Specify proxy server to use. To specify port number in this string, append :[port] to the end of the host name. The proxy string may be prefixed with [protocol]:// since any such prefix will be ignored. The proxy's port number may optionally be specified with the separate option. If not specified, will default to using port 1080 for proxies. Setting to empty string will disable the proxy. + \param proxyuserpw If non-empty, [user name]:[password] to use for the connection to the HTTP proxy. + \param options extra options for connecting to the controller. If 0x1 is set, the client will optimize usage to only allow GET calls. Set 0x80000000 if using a development server. + + \ja \brief MUJINコントローラのクライアントを作成する。この関数はスレッドセーフではない。 + + この関数はスレッドセーフではないため、呼び出す時に他のスレッドが走っていないようにご注意ください。 + \param usernamepassword ユーザ:パスワード + \param url コントローラにアクセスするためのURLです。スラッシュ「/」で終わる必要があります。強制的にユーザも指定出来ます、例えばhttps://username@server/。 + \param proxyserverport Specify proxy server to use. To specify port number in this string, append :[port] to the end of the host name. The proxy string may be prefixed with [protocol]:// since any such prefix will be ignored. The proxy's port number may optionally be specified with the separate option. If not specified, will default to using port 1080 for proxies. Setting to empty string will disable the proxy. + \param proxyuserpw If non-empty, [user name]:[password] to use for the connection to the HTTP proxy. + \param options 1が指定されたら、クライアントがGETのみを呼び出し出来ます。それで初期化がもっと速くなれます。 + \param timeout set timeout in seconds for the initial login requests + */ + MUJINWEBSTACKCLIENT_API static boost::shared_ptr CreateWebstackClient(const std::string& usernamepassword, const std::string& url, const std::string& proxyserverport=std::string(), const std::string& proxyuserpw=std::string(), int options=0, double timeout=3.0); + + /// \brief sets the character encoding for all strings that are being input and output from the resources + /// + /// The default character encoding is \b utf-8, can also set it to \b Shift_JIS for windows japanese unicode, \b iso-2022-jp + /// List of possible sets: http://www.iana.org/assignments/character-sets/character-sets.xml + void SetCharacterEncoding(const std::string& newencoding); + + /// \brief sets the language code for all output + /// + /// Check out http://en.wikipedia.org/wiki/List_of_ISO_639-1_codes + void SetLanguage(const std::string& language); + + /// \brief sets the user agent to be sent with each http request + void SetUserAgent(const std::string& userAgent); + + /// \brief sets additional http headers to be included on all requests + /// + /// \param additionalHeaders expect each value to be in the format of "Header-Name: header-value" + void SetAdditionalHeaders(const std::vector& additionalHeaders); + + /// \brief returns the username logged into this controller + const std::string& GetUserName() const; + + /// \brief returns the URI used to setup the connection + const std::string& GetBaseURI() const; + + std::string GetURIWithUsernamePassword() const { + return GetClientInfo().GetURL(true); + } + + const WebstackClientInfo& GetClientInfo() const; + + /// \brief If necessary, changes the proxy to communicate to the controller server. Setting proxy disables previously set unix endpoint. + /// + /// \param serverport Specify proxy server to use. To specify port number in this string, append :[port] to the end of the host name. The proxy string may be prefixed with [protocol]:// since any such prefix will be ignored. The proxy's port number may optionally be specified with the separate option. If not specified, will default to using port 1080 for proxies. Setting to empty string will disable the proxy. + /// \param userpw If non-empty, [user name]:[password] to use for the connection to the HTTP proxy. + void SetProxy(const std::string& serverport, const std::string& userpw); + + /// \brief If necessary, changes the unix domain socket to be used to communicate to the controller server. Setting unix endpoint disables previously set proxy. + /// + /// \param unixendpoint Specify the file path to the unix domain socket to connect to. + void SetUnixEndpoint(const std::string& unixendpoint); + + /// \brief Restarts the MUJIN Controller Server and destroys any optimizaiton jobs. + /// + /// If the server is not responding, call this method to clear the server state and initialize everything. + /// The method is blocking, when it returns the MUJIN Controller would have been restarted. + void RestartServer(double timeout = 5.0); + + /// \brief Execute GraphQL query or mutation against Mujin Controller. + /// + /// Throws an exception if there are any errors + /// \param rResultData The "data" field of the result if the query returns without problems + void ExecuteGraphQuery(const char* operationName, const char* query, const rapidjson::Value& rVariables, rapidjson::Value& rResultData, rapidjson::Document::AllocatorType& rAlloc, double timeout = 60.0); + + /// \brief Execute the GraphQL query or mutation against Mujin Controller and return any output as-is without doing any error processing + /// + /// \param rResult The entire result field of the query. Should have keys "data" and "errors". Each error should have keys: "message", "locations", "path", "extensions". And "extensions" has keys "errorCode". + void ExecuteGraphQueryRaw(const char* operationName, const char* query, const rapidjson::Value& rVariables, rapidjson::Value& rResult, rapidjson::Document::AllocatorType& rAlloc, double timeout = 60.0); + + /// \brief returns the mujin controller version + std::string GetVersion(); + + /// \brief sends the cancel message to all jobs. + /// + /// The method is non-blocking + void CancelAllJobs(); + + /// \param localtimeval seconds since epoch, will use input as If-Modified-Since header + /// \param remotetimeval will output the modified date in response + /// \param vdata filled with the contents of the file on the controller filesystem + void DownloadFileFromControllerIfModifiedSince_UTF8(const std::string& desturi, long localtimeval, long &remotetimeval, std::vector& vdata, double timeout = 5.0); + + /// \param localtimeval seconds since epoch, will use input as If-Modified-Since header + /// \param remotetimeval will output the modified date in response + /// \param vdata filled with the contents of the file on the controller filesystem + void DownloadFileFromControllerIfModifiedSince_UTF16(const std::wstring& desturi, long localtimeval, long &remotetimeval, std::vector& vdata, double timeout = 5.0); + + void ModifySceneAddReferenceObjectPK(const std::string &scenepk, const std::string &referenceobjectpk, double timeout = 5.0); + + void ModifySceneRemoveReferenceObjectPK(const std::string &scenepk, const std::string &referenceobjectpk, double timeout = 5.0); + + const std::string& GetDefaultTaskType(); + + /** \brief Get the url-encoded primary key of a scene from a scene uri (utf-8 encoded) + + For example, the URI + + mujin:/検証動作_121122.mujin.dae + + is represented as: + + "mujin:/\xe6\xa4\x9c\xe8\xa8\xbc\xe5\x8b\x95\xe4\xbd\x9c_121122.mujin.dae" + + Return value will be: "%E6%A4%9C%E8%A8%BC%E5%8B%95%E4%BD%9C_121122" + \param uri utf-8 encoded URI + */ + std::string GetScenePrimaryKeyFromURI_UTF8(const std::string& uri); + + /** \brief Get the url-encoded primary key of a scene from a scene uri (utf-16 encoded) + + If input URL is L"mujin:/\u691c\u8a3c\u52d5\u4f5c_121122.mujin.dae" + Return value will be: "%E6%A4%9C%E8%A8%BC%E5%8B%95%E4%BD%9C_121122" + + \param uri utf-16 encoded URI + */ + std::string GetScenePrimaryKeyFromURI_UTF16(const std::wstring& uri); + + /// \brief returns the uncoded name from a primary key + /// + /// \return utf-8 encoded name + std::string GetNameFromPrimaryKey_UTF8(const std::string& pk); + + /// \brief returns the uncoded name from a primary key + /// + /// \return utf-16 encoded name + std::wstring GetNameFromPrimaryKey_UTF16(const std::string& pk); + + std::string CreateObjectGeometry(const std::string& objectPk, const std::string& name, const std::string& linkPk, const std::string& geomtype, double timeout = 5); + + /// \brief set geometry mesh to an object + /// \param objectPk primary key for the object to set mesh data to + /// \param geometryPk primary key for the geometry + /// \param data stl format binary mesh data + /// \param unit length unit of mesh + /// \param timeout timeout of uploading mesh + /// + std::string SetObjectGeometryMesh(const std::string& objectPk, const std::string& geometryPk, const std::vector& data, const std::string& unit = "mm", double timeout = 5); + + /// \brief expectedhttpcode is not 0, then will check with the returned http code and if not equal will throw an exception + int CallGet(const std::string& relativeuri, rapidjson::Document& pt, int expectedhttpcode=200, double timeout = 5.0); + + /// \brief expectedhttpcode is not 0, then will check with the returned http code and if not equal will throw an exception + int CallGet(const std::string& relativeuri, std::string& outputdata, int expectedhttpcode=200, double timeout = 5.0); + + /// \brief expectedhttpcode is not 0, then will check with the returned http code and if not equal will throw an exception + int CallGet(const std::string& relativeuri, std::ostream& outputStream, int expectedhttpcode=200, double timeout = 5.0); + + /// \brief expectedhttpcode is not 0, then will check with the returned http code and if not equal will throw an exception + int CallGet(const std::string& relativeuri, std::vector& outputdata, int expectedhttpcode=200, double timeout = 5.0); + + /// \brief expectedhttpcode is not 0, then will check with the returned http code and if not equal will throw an exception + /// + /// \param relativeuri URL-encoded UTF-8 encoded + /// \param data encoded depending on the character encoding set on the system + int CallPost(const std::string& relativeuri, const std::string& data, rapidjson::Document& pt, int expectedhttpcode=201, double timeout = 5.0); + + /// \param data utf-8 encoded + int CallPost_UTF8(const std::string& relativeuri, const std::string& data, rapidjson::Document& pt, int expectedhttpcode=201, double timeout = 5.0); + /// \param data utf-16 encoded + int CallPost_UTF16(const std::string& relativeuri, const std::wstring& data, rapidjson::Document& pt, int expectedhttpcode=201, double timeout = 5.0); + + /// \brief puts json string + /// \param relativeuri relative uri to put at + /// \param data json string to put + /// \param pt reply is stored + /// \param expectedhttpcode expected http code + /// \param timeout timeout of puts + /// \return http code returned + int CallPutJSON(const std::string& relativeuri, const std::string& data, rapidjson::Document& pt, int expectedhttpcode=202, double timeout = 5.0); + + /// \brief puts stl data + /// \param relativeuri relative uri to put at + /// \param data stl raw data + /// \param pt reply is stored + /// \param expectedhttpcode expected http code + /// \param timeout timeout of puts + /// \return http code returned + int CallPutSTL(const std::string& relativeuri, const std::vector& data, rapidjson::Document& pt, int expectedhttpcode=202, double timeout = 5.0); + + + void CallDelete(const std::string& relativeuri, int expectedhttpcode, double timeout = 5.0); + + std::stringstream& GetBuffer(); + + /// \brief URL-encode raw string + inline std::string EscapeString(const std::string& raw) const + { + boost::shared_ptr pstr(curl_easy_escape(_curl, raw.c_str(), raw.size()), curl_free); + return std::string(pstr.get()); + } + + /// \brief decode URL-encoded raw string + inline std::string UnescapeString(const std::string& raw) const + { + int outlength = 0; + boost::shared_ptr pstr(curl_easy_unescape(_curl, raw.c_str(), raw.size(), &outlength), curl_free); + return std::string(pstr.get(), outlength); + } + +private: + int _CallPut(const std::string& relativeuri, const void* pdata, size_t nDataSize, rapidjson::Document& pt, curl_slist* headers, int expectedhttpcode=202, double timeout = 5.0); + + /// For all webdav internal functions: mutex is already locked, desturi directories are already created + //@{ + + /// \param desturi expects the fully resolved URI to pass to curl + int _CallGet(const std::string& desturi, rapidjson::Value& rResult, rapidjson::Document::AllocatorType& alloc, int expectedhttpcode=200, double timeout = 5.0); + int _CallGet(const std::string& desturi, std::string& outputdata, int expectedhttpcode=200, double timeout = 5.0); + int _CallGet(const std::string& desturi, std::vector& outputdata, int expectedhttpcode=200, double timeout = 5.0); + int _CallGet(const std::string& desturi, std::ostream& outputStream, int expectedhttpcode=200, double timeout = 5.0); + + int _CallPost(const std::string& desturi, const std::string& data, rapidjson::Value& rResult, rapidjson::Document::AllocatorType& alloc, int expectedhttpcode=201, double timeout = 5.0); + + static int _WriteStringStreamCallback(char *data, size_t size, size_t nmemb, std::stringstream *writerData); + static int _WriteVectorCallback(char *data, size_t size, size_t nmemb, std::vector *writerData); + static int _WriteOStreamCallback(char *data, size_t size, size_t nmemb, std::ostream *writerData); + static int _ReadIStreamCallback(char *data, size_t size, size_t nmemb, std::istream *writerData); + + /// \brief sets up http header for doing http operation with json data + void _SetupHTTPHeadersJSON(); + + /// \brief sets up http header for doing http operation with stl data + void _SetupHTTPHeadersSTL(); + + /// \brief sets up http header for doing http operation with multipart/form-data data + void _SetupHTTPHeadersMultipartFormData(); + + void _DownloadFileFromController(const std::string& desturi, long localtimeval, long &remotetimeval, std::vector& vdata, double timeout = 5); + + /// \brief given a raw uri with "mujin:/", return the real network uri + /// + /// mutex should be locked + /// \param bEnsurePath if true, will make sure the directories on the server side are created + /// \param bEnsureSlash if true, will ensure returned uri ends with slash / + std::string _PrepareDestinationURI_UTF8(const std::string& rawuri, bool bEnsurePath=true, bool bEnsureSlash=false, bool bIsDirectory=false); + std::string _PrepareDestinationURI_UTF16(const std::wstring& rawuri, bool bEnsurePath=true, bool bEnsureSlash=false, bool bIsDirectory=false); + + // encode a URL without the / separator + std::string _EncodeWithoutSeparator(const std::string& raw); + + /// \param relativeuri utf-8 encoded directory inside the user webdav folder. has a trailing slash. relative to real uri + void _EnsureWebDAVDirectories(const std::string& relativeuri, double timeout = 3.0); + + + void _ExecuteGraphQuery(const char* operationName, const char* query, const rapidjson::Value& rVariables, rapidjson::Value& rResult, rapidjson::Document::AllocatorType& rAlloc, double timeout, bool checkForErrors, bool returnRawResponse); + + // INSTANCE FIELDS + int _lastmode; + CURL *_curl; + boost::mutex _mutex; + std::stringstream _buffer; + std::string _baseuri, _baseapiuri, _basewebdavuri, _uri; + + WebstackClientInfo _clientInfo; + + curl_slist *_httpheadersjson; + curl_slist *_httpheadersstl; + curl_slist *_httpheadersmultipartformdata; + std::string _charset, _language; + std::string _csrfmiddlewaretoken; + + rapidjson::Document _profile; ///< user profile and versioning + std::string _errormessage; ///< set when an error occurs in libcurl + + std::string _defaultscenetype, _defaulttasktype; + + rapidjson::StringBuffer _rRequestStringBufferCache; ///< cache for request string, protected by _mutex + +}; // End class WebstackClient + +typedef boost::shared_ptr WebstackClientPtr; +typedef boost::weak_ptr WebstackClientWeakPtr; + + +} // namespace mujinwebstackclient + +BOOST_STATIC_ASSERT(MUJINWEBSTACKCLIENT_VERSION_MAJOR>=0&&MUJINWEBSTACKCLIENT_VERSION_MAJOR<=255); +BOOST_STATIC_ASSERT(MUJINWEBSTACKCLIENT_VERSION_MINOR>=0&&MUJINWEBSTACKCLIENT_VERSION_MINOR<=255); +BOOST_STATIC_ASSERT(MUJINWEBSTACKCLIENT_VERSION_PATCH>=0&&MUJINWEBSTACKCLIENT_VERSION_PATCH<=255); + +#endif diff --git a/src/logging.h b/src/logging.h index 662d8c6d..b30a1818 100644 --- a/src/logging.h +++ b/src/logging.h @@ -1,6 +1,6 @@ #pragma once -#if MUJINCLIENT_LOG4CXX +#if MUJINWEBSTACKCLIENT_LOG4CXX #include diff --git a/src/mujincontrollerclient.cpp b/src/mujincontrollerclient.cpp deleted file mode 100644 index c2ad77ff..00000000 --- a/src/mujincontrollerclient.cpp +++ /dev/null @@ -1,1385 +0,0 @@ -// -*- coding: utf-8 -*- -// Copyright (C) 2012-2013 MUJIN Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT 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 "common.h" -#include "controllerclientimpl.h" -#include // for sleep -#include - -#include - -#ifdef MUJIN_USEZMQ -#include "binpickingtaskzmq.h" -#endif - -#include "logging.h" -#include "mujincontrollerclient/mujinjson.h" - -MUJIN_LOGGER("mujin.controllerclientcpp"); - -namespace mujinclient { - -using namespace mujinjson; - -void ExtractEnvironmentStateFromPTree(const rapidjson::Value& envstatejson, EnvironmentState& envstate) -{ - // FIXME: is this a dict or array? - envstate.clear(); - for (rapidjson::Document::ConstValueIterator it = envstatejson.Begin(); it != envstatejson.End(); ++it) { - InstanceObjectState objstate; - std::string name = GetJsonValueByKey(*it, "name"); - std::vector quat = GetJsonValueByKey >(*it, "quat_"); - BOOST_ASSERT(quat.size() == 4); - Real dist2 = 0; - for (int i = 0; i < 4; i++ ) { - Real f = quat[i] * quat[i]; - dist2 += f; - objstate.transform.quaternion[i] = f; - } - // normalize the quaternion - if( dist2 > 0 ) { - Real fnorm =1/std::sqrt(dist2); - objstate.transform.quaternion[0] *= fnorm; - objstate.transform.quaternion[1] *= fnorm; - objstate.transform.quaternion[2] *= fnorm; - objstate.transform.quaternion[3] *= fnorm; - } - LoadJsonValueByKey(*it, "translation_", objstate.transform.translate); - LoadJsonValueByKey(*it, "dofvalues", objstate.dofvalues); - envstate[name] = objstate; - } -} - -void SerializeEnvironmentStateToJSON(const EnvironmentState& envstate, std::ostream& os) -{ - os << "["; - bool bhaswritten = false; - FOREACHC(itstate, envstate) { - if( itstate->first.size() > 0 ) { - if( bhaswritten ) { - os << ","; - } - os << "{ \"name\":\"" << itstate->first << "\", \"translation_\":["; - for(int i = 0; i < 3; ++i) { - if( i > 0 ) { - os << ","; - } - os << itstate->second.transform.translate[i]; - } - os << "], \"quat_\":["; - for(int i = 0; i < 4; ++i) { - if( i > 0 ) { - os << ","; - } - os << itstate->second.transform.quaternion[i]; - } - os << "], \"dofvalues\":["; - for(size_t i = 0; i < itstate->second.dofvalues.size(); ++i) { - if( i > 0 ) { - os << ","; - } - os << itstate->second.dofvalues.at(i); - } - os << "] }"; - bhaswritten = true; - } - } - os << "]"; -} - -WebResource::WebResource(ControllerClientPtr controller, const std::string& resourcename, const std::string& pk) : __controller(controller), __resourcename(resourcename), __pk(pk) -{ - BOOST_ASSERT(__pk.size()>0); -} - -void WebResource::GetWrap(rapidjson::Document& pt, const std::string& field, double timeout) -{ - GETCONTROLLERIMPL(); - controller->CallGet(str(boost::format("%s/%s/?format=json&fields=%s")%GetResourceName()%GetPrimaryKey()%field), pt, 200, timeout); -} - -void WebResource::Set(const std::string& field, const std::string& newvalue, double timeout) -{ - throw MujinException("not implemented"); -} - -void WebResource::SetJSON(const std::string& json, double timeout) -{ - GETCONTROLLERIMPL(); - rapidjson::Document pt(rapidjson::kObjectType); - controller->CallPutJSON(str(boost::format("%s/%s/?format=json")%GetResourceName()%GetPrimaryKey()), json, pt, 202, timeout); -} - -void WebResource::Delete(double timeout) -{ - GETCONTROLLERIMPL(); - controller->CallDelete(str(boost::format("%s/%s/")%GetResourceName()%GetPrimaryKey()), 204, timeout); -} - -void WebResource::Copy(const std::string& newname, int options, double timeout) -{ - throw MujinException("not implemented yet"); -} - -ObjectResource::ObjectResource(ControllerClientPtr controller, const std::string& pk_) : WebResource(controller, "object", pk_), pk(pk_) -{ -} - -ObjectResource::ObjectResource(ControllerClientPtr controller, const std::string& resource, const std::string& pk_) : WebResource(controller, resource, pk_), pk(pk_) -{ -} - -ObjectResource::LinkResource::LinkResource(ControllerClientPtr controller, const std::string& objectpk_, const std::string& pk_) : WebResource(controller, str(boost::format("object/%s/link")%objectpk_), pk_), pk(pk_), objectpk(objectpk_) -{ -} - -ObjectResource::GeometryResource::GeometryResource(ControllerClientPtr controller, const std::string& objectpk_, const std::string& pk_) : WebResource(controller, str(boost::format("object/%s/geometry")%objectpk_), pk_), pk(pk_), objectpk(objectpk_) -{ -} - -ObjectResource::IkParamResource::IkParamResource(ControllerClientPtr controller, const std::string& objectpk_, const std::string& pk_) : WebResource(controller, str(boost::format("object/%s/ikparam")%objectpk_), pk_), pk(pk_) -{ -} - -void ObjectResource::GeometryResource::GetMesh(std::string& primitive, std::vector >& indices, std::vector >& vertices) -{ - GETCONTROLLERIMPL(); - rapidjson::Document pt(rapidjson::kObjectType); - const std::string relativeuri(str(boost::format("%s/%s/?format=json&limit=0&mesh=true")%GetResourceName()%GetPrimaryKey())); - controller->CallGet(relativeuri, pt); - rapidjson::Value& objects = pt["mesh"]; - LoadJsonValueByKey(objects,"primitive",primitive); - LoadJsonValueByKey(objects,"indices",indices); - LoadJsonValueByKey(objects,"vertices",vertices); -} - -void ObjectResource::GeometryResource::SetGeometryFromRawSTL(const std::vector& rawstldata, const std::string& unit, double timeout) -{ - GETCONTROLLERIMPL(); - if (this->geomtype != "mesh") { - throw MUJIN_EXCEPTION_FORMAT("geomtype is not mesh: %s", this->geomtype, MEC_InvalidArguments); - } - controller->SetObjectGeometryMesh(this->objectpk, this->pk, rawstldata, unit, timeout); -} - -ObjectResource::GeometryResourcePtr ObjectResource::LinkResource::AddGeometryFromRawSTL(const std::vector& rawstldata, const std::string& geomname, const std::string& unit, double timeout) -{ - GETCONTROLLERIMPL(); - const std::string& linkpk = GetPrimaryKey(); - const std::string geometryPk = controller->CreateObjectGeometry(this->objectpk, geomname, linkpk, "mesh", timeout); - - ObjectResource::GeometryResourcePtr geometry(new GeometryResource(controller, this->objectpk, geometryPk)); - geometry->name = geomname; - geometry->geomtype = "mesh"; - geometry->linkpk = linkpk; - geometry->SetGeometryFromRawSTL(rawstldata, unit, timeout); - return geometry; -} - -ObjectResource::GeometryResourcePtr ObjectResource::LinkResource::AddPrimitiveGeometry(const std::string& geomname, const std::string& geomtype, double timeout) -{ - GETCONTROLLERIMPL(); - const std::string& linkpk = GetPrimaryKey(); - const std::string geometryPk = controller->CreateObjectGeometry(this->objectpk, geomname, linkpk, geomtype, timeout); - - ObjectResource::GeometryResourcePtr geometry(new GeometryResource(controller, this->objectpk, geometryPk)); - geometry->name = geomname; - geometry->geomtype = geomtype; - geometry->linkpk = linkpk; - return geometry; -} - -ObjectResource::GeometryResourcePtr ObjectResource::LinkResource::GetGeometryFromName(const std::string& geometryName) -{ - GETCONTROLLERIMPL(); - rapidjson::Document pt(rapidjson::kObjectType); - const std::string relativeuri(str(boost::format("object/%s/geometry/?format=json&limit=0&fields=geometries")%this->objectpk)); - controller->CallGet(relativeuri, pt); - if (pt.IsObject() && pt.HasMember("geometries") && pt["geometries"].IsArray()) { - rapidjson::Value& objects = pt["geometries"]; - for (rapidjson::Document::ConstValueIterator it = objects.Begin(); it != objects.End(); ++it) { - const std::string geomname = it->HasMember("name") ? GetJsonValueByKey(*it, "name") : GetJsonValueByKey(*it, "pk"); - if (geomname == geometryName && (*it)["linkpk"].GetString() == this->pk) { - ObjectResource::GeometryResourcePtr geometry(new GeometryResource(controller, this->objectpk, GetJsonValueByKey(*it, "pk"))); - geometry->name = geomname; - LoadJsonValueByKey(*it,"linkpk",geometry->linkpk); - LoadJsonValueByKey(*it,"visible",geometry->visible); - LoadJsonValueByKey(*it,"geomtype",geometry->geomtype); - LoadJsonValueByKey(*it,"transparency",geometry->transparency); - LoadJsonValueByKey(*it,"quaternion",geometry->quaternion); - LoadJsonValueByKey(*it,"translate",geometry->translate); - LoadJsonValueByKey(*it,"diffusecolor",geometry->diffusecolor); - - /// geomtype /// - // mesh - // box: half_extents - // cylinder: height, radius - // sphere: radius - LoadJsonValueByKey(*it,"half_extents",geometry->half_extents); - LoadJsonValueByKey(*it,"height",geometry->height); - LoadJsonValueByKey(*it,"radius",geometry->radius); - return geometry; - } - } - } - throw MUJIN_EXCEPTION_FORMAT("link %s does not have geometry named %s", this->name%geometryName, MEC_InvalidArguments); -} - -void ObjectResource::LinkResource::GetGeometries(std::vector& geometries) -{ - GETCONTROLLERIMPL(); - rapidjson::Document pt(rapidjson::kObjectType); - const std::string relativeuri(str(boost::format("object/%s/geometry/?format=json&limit=0&fields=geometries")%this->objectpk)); - controller->CallGet(relativeuri, pt); - if (pt.IsObject() && pt.HasMember("geometries") && pt["geometries"].IsArray()) { - rapidjson::Value& objects = pt["geometries"]; - geometries.clear(); - for (rapidjson::Document::ConstValueIterator it = objects.Begin(); it != objects.End(); ++it) { - const std::string linkpk = GetJsonValueByKey(*it, "linkpk"); - if (linkpk == this->pk) { - ObjectResource::GeometryResourcePtr geometry(new GeometryResource(controller, this->objectpk, GetJsonValueByKey(*it, "pk"))); - geometry->linkpk = linkpk; - LoadJsonValueByKey(*it,"name",geometry->name,geometry->pk); - LoadJsonValueByKey(*it,"visible",geometry->visible); - LoadJsonValueByKey(*it,"geomtype",geometry->geomtype); - LoadJsonValueByKey(*it,"transparency",geometry->transparency); - LoadJsonValueByKey(*it,"quaternion",geometry->quaternion); - LoadJsonValueByKey(*it,"translate",geometry->translate); - LoadJsonValueByKey(*it,"diffusecolor",geometry->diffusecolor); - - LoadJsonValueByKey(*it,"half_extents",geometry->half_extents); - LoadJsonValueByKey(*it,"height",geometry->height); - LoadJsonValueByKey(*it,"radius",geometry->radius); - geometries.push_back(geometry); - } - } - } -} - -void ObjectResource::LinkResource::SetCollision(bool hasCollision) -{ - this->SetJSON(mujinjson::GetJsonStringByKey("collision", hasCollision)); - this->collision = hasCollision; -} -void ObjectResource::SetCollision(bool collision) -{ - std::vector links; - this->GetLinks(links); - BOOST_FOREACH(ObjectResource::LinkResourcePtr &link, links){ - link->SetCollision(collision); - } -} -int ObjectResource::LinkResource::GetCollision() -{ - return this->collision; -} -int ObjectResource::GetCollision() -{ - std::vector links; - this->GetLinks(links); - int ret=0; - BOOST_FOREACH(ObjectResource::LinkResourcePtr &link, links){ - ret|=link->GetCollision()+1; - } - return ret-1; -} - -void ObjectResource::GeometryResource::SetVisible(bool isVisible) -{ - this->SetJSON(mujinjson::GetJsonStringByKey("visible",isVisible)); - this->visible = isVisible; -} -void ObjectResource::LinkResource::SetVisible(bool visible) -{ - std::vector geometries; - this->GetGeometries(geometries); - BOOST_FOREACH(ObjectResource::GeometryResourcePtr &geometry, geometries){ - geometry->SetVisible(visible); - } -} -void ObjectResource::SetVisible(bool visible) -{ - std::vector links; - this->GetLinks(links); - BOOST_FOREACH(ObjectResource::LinkResourcePtr &link, links){ - link->SetVisible(visible); - } -} -int ObjectResource::GeometryResource::GetVisible() -{ - return this->visible; -} -int ObjectResource::LinkResource::GetVisible() -{ - std::vector geometries; - this->GetGeometries(geometries); - int ret=0; - BOOST_FOREACH(ObjectResource::GeometryResourcePtr &geometry, geometries){ - ret|=geometry->GetVisible()+1; - } - return ret-1; -} -int ObjectResource::GetVisible() -{ - std::vector links; - this->GetLinks(links); - int ret=0; - BOOST_FOREACH(ObjectResource::LinkResourcePtr &link, links){ - ret|=link->GetVisible()+1; - } - return ret-1; -} - -void ObjectResource::GetLinks(std::vector& links) -{ - GETCONTROLLERIMPL(); - rapidjson::Document pt(rapidjson::kObjectType); - controller->CallGet(str(boost::format("object/%s/link/?format=json&limit=0&fields=links")%GetPrimaryKey()), pt); - rapidjson::Value& objects = pt["links"]; - links.resize(objects.Size()); - size_t i = 0; - for (rapidjson::Document::ValueIterator it = objects.Begin(); it != objects.End(); ++it) { - LinkResourcePtr link(new LinkResource(controller, GetPrimaryKey(), GetJsonValueByKey(*it, "pk"))); - LoadJsonValueByKey(*it,"parentlinkpk",link->parentlinkpk); - LoadJsonValueByKey(*it,"name",link->name); - LoadJsonValueByKey(*it,"collision",link->collision); - LoadJsonValueByKey(*it,"attachmentpks",link->attachmentpks); - LoadJsonValueByKey(*it,"quaternion",link->quaternion); - LoadJsonValueByKey(*it,"translate",link->translate); - links[i++] = link; - } -} - -ObjectResource::LinkResourcePtr ObjectResource::AddLink(const std::string& objname, const Real quaternion_[4], const Real translate_[3]) -{ - GETCONTROLLERIMPL(); - const std::string linkPk = controller->CreateLink(this->pk, "", objname, quaternion_, translate_); - - ObjectResource::LinkResourcePtr link(new LinkResource(controller, this->pk, linkPk)); - link->name = objname; - link->parentlinkpk = ""; - return link; -} - -ObjectResource::LinkResourcePtr ObjectResource::LinkResource::AddChildLink(const std::string& objname, const Real quaternion_[4], const Real translate_[3]) -{ - GETCONTROLLERIMPL(); - const std::string linkPk = controller->CreateLink(this->objectpk, this->pk, objname, quaternion_, translate_); - - ObjectResource::LinkResourcePtr link(new LinkResource(controller, this->objectpk, linkPk)); - link->name = objname; - link->parentlinkpk = this->pk; - return link; -} - -ObjectResource::IkParamResourcePtr ObjectResource::AddIkParam(const std::string& objname, const std::string& iktype) -{ - GETCONTROLLERIMPL(); - const std::string ikparamPk = controller->CreateIkParam(this->pk, objname, iktype); - - return ObjectResource::IkParamResourcePtr(new IkParamResource(controller, this->pk, ikparamPk)); -} - -void ObjectResource::GetIkParams(std::vector& ikparams) -{ - GETCONTROLLERIMPL(); - rapidjson::Document pt(rapidjson::kObjectType); - controller->CallGet(str(boost::format("object/%s/ikparam/?format=json&limit=0&fields=ikparams")%GetPrimaryKey()), pt); - rapidjson::Value& objects = pt["ikparams"]; - ikparams.resize(objects.Size()); - size_t i = 0; - for (rapidjson::Document::ValueIterator it = objects.Begin(); it != objects.End(); ++it) { - IkParamResourcePtr ikparam(new IkParamResource(controller, GetPrimaryKey(), GetJsonValueByKey(*it, "pk"))); - LoadJsonValueByKey(*it,"name",ikparam->name); - LoadJsonValueByKey(*it,"iktype",ikparam->iktype); - LoadJsonValueByKey(*it,"quaternion",ikparam->quaternion); - LoadJsonValueByKey(*it,"direction",ikparam->direction); - LoadJsonValueByKey(*it,"translation",ikparam->translation); - LoadJsonValueByKey(*it,"angle",ikparam->angle); - ikparams[i++] = ikparam; - } -} - -RobotResource::RobotResource(ControllerClientPtr controller, const std::string& pk_) : ObjectResource(controller, "robot", pk_) -{ -} - -RobotResource::ToolResource::ToolResource(ControllerClientPtr controller, const std::string& robotobjectpk, const std::string& pk_) : WebResource(controller, str(boost::format("robot/%s/tool")%robotobjectpk), pk_), pk(pk_) -{ -} - -void RobotResource::GetTools(std::vector& tools) -{ - GETCONTROLLERIMPL(); - rapidjson::Document pt(rapidjson::kObjectType); - controller->CallGet(str(boost::format("robot/%s/tool/?format=json&limit=0&fields=tools")%GetPrimaryKey()), pt); - rapidjson::Value& objects = pt["tools"]; - tools.resize(objects.Size()); - size_t i = 0; -// FOREACH(v, objects) { - for (rapidjson::Document::ValueIterator it = objects.Begin(); it != objects.End(); ++it) { - ToolResourcePtr tool(new ToolResource(controller, GetPrimaryKey(), GetJsonValueByKey(*it, "pk"))); - - LoadJsonValueByKey(*it, "name", tool->name); - LoadJsonValueByKey(*it, "frame_orgin", tool->frame_origin); - LoadJsonValueByKey(*it, "frame_tip", tool->frame_tip); - LoadJsonValueByKey(*it, "direction", tool->direction); - LoadJsonValueByKey(*it, "quaternion", tool->quaternion); - LoadJsonValueByKey(*it, "translate", tool->translate); - - tools[i++] = tool; - } -} - -RobotResource::AttachedSensorResource::AttachedSensorResource(ControllerClientPtr controller, const std::string& robotobjectpk, const std::string& pk_) : WebResource(controller, str(boost::format("robot/%s/attachedsensor")%robotobjectpk), pk_), pk(pk_) -{ -} - -void RobotResource::GetAttachedSensors(std::vector& attachedsensors, bool useConnectedBodies) -{ - GETCONTROLLERIMPL(); - rapidjson::Document pt(rapidjson::kObjectType); - controller->CallGet(str(boost::format("robot/%s/attachedsensor/?format=json&limit=0&fields=attachedsensors")%GetPrimaryKey()), pt); - - rapidjson::Value& rAttachedSensors = pt["attachedsensors"]; - attachedsensors.resize(rAttachedSensors.Size()); - size_t attachedSensorIdx = 0; - for (rapidjson::Document::ValueIterator itAttachedSensor = rAttachedSensors.Begin(); itAttachedSensor != rAttachedSensors.End(); ++itAttachedSensor) { - AttachedSensorResourcePtr attachedsensor(new AttachedSensorResource(controller, GetPrimaryKey(), GetJsonValueByKey(*itAttachedSensor, "pk"))); - - LoadJsonValueByKey(*itAttachedSensor, "name", attachedsensor->name); - LoadJsonValueByKey(*itAttachedSensor, "frame_origin", attachedsensor->frame_origin); - LoadJsonValueByKey(*itAttachedSensor, "sensortype", attachedsensor->sensortype); - LoadJsonValueByKey(*itAttachedSensor, "quaternion", attachedsensor->quaternion); - LoadJsonValueByKey(*itAttachedSensor, "translate", attachedsensor->translate); - std::vector distortionCoeffs = GetJsonValueByPath > (*itAttachedSensor, "/sensordata/distortion_coeffs"); - - BOOST_ASSERT(distortionCoeffs.size() <= 5); - for (size_t i = 0; i < distortionCoeffs.size(); i++) { - attachedsensor->sensordata.distortion_coeffs[i] = distortionCoeffs[i]; - } - attachedsensor->sensordata.distortion_model = GetJsonValueByPath(*itAttachedSensor, "/sensordata/distortion_model"); - attachedsensor->sensordata.focal_length = GetJsonValueByPath(*itAttachedSensor, "/sensordata/focal_length"); - attachedsensor->sensordata.measurement_time= GetJsonValueByPath(*itAttachedSensor, "/sensordata/measurement_time"); - std::vector intrinsics = GetJsonValueByPath >(*itAttachedSensor, "/sensordata/intrinsic"); - BOOST_ASSERT(intrinsics.size() <= 6); - for (size_t i = 0; i < intrinsics.size(); i++) { - attachedsensor->sensordata.intrinsic[i] = intrinsics[i]; - } - std::vector imgdim = GetJsonValueByPath >(*itAttachedSensor, "/sensordata/image_dimensions"); - BOOST_ASSERT(imgdim.size() <= 3); - for (size_t i = 0; i < imgdim.size(); i++) { - attachedsensor->sensordata.image_dimensions[i] = imgdim[i]; - } - - if (rapidjson::Pointer("/sensordata/extra_parameters").Get(*itAttachedSensor)) { - std::string parameters_string = GetJsonValueByPath(*itAttachedSensor, "/sensordata/extra_parameters"); - //std::cout << "extra param " << parameters_string << std::endl; - std::list results; - boost::split(results, parameters_string, boost::is_any_of(" ")); - results.remove(""); - attachedsensor->sensordata.extra_parameters.resize(results.size()); - size_t iparam = 0; - BOOST_FOREACH(std::string p, results) { - //std::cout << "'"<< p << "'"<< std::endl; - try { - attachedsensor->sensordata.extra_parameters[iparam++] = boost::lexical_cast(p); - } catch (...) { - //lexical_cast fails... - } - } - } else { - //std::cout << "no asus param" << std::endl; - } - - attachedsensors[attachedSensorIdx++] = attachedsensor; - } - - rapidjson::Document rRobotConnectedBodies(rapidjson::kObjectType); - controller->CallGet(str(boost::format("robot/%s/connectedBody/?format=json")%GetPrimaryKey()), rRobotConnectedBodies); - rapidjson::Value& rConnectedBodies = rRobotConnectedBodies["connectedBodies"]; - if (useConnectedBodies && rConnectedBodies.IsArray() && rConnectedBodies.Size() > 0) { - for (rapidjson::Document::ConstValueIterator itConnectedBody = rConnectedBodies.Begin(); itConnectedBody != rConnectedBodies.End(); ++itConnectedBody) { - std::string connectedBodyScenePk = controller->GetScenePrimaryKeyFromURI_UTF8(GetJsonValueByKey(*itConnectedBody, "url")); - std::string connectedBodyName = GetJsonValueByKey(*itConnectedBody, "name"); - rapidjson::Document rConnectedBodyInstObjects(rapidjson::kObjectType); - controller->CallGet(str(boost::format("scene/%s/instobject/?format=json&limit=0&fields=attachedsensors,object_pk,name")%connectedBodyScenePk), rConnectedBodyInstObjects); - for (rapidjson::Document::ConstValueIterator itConnectedBodyInstObject = rConnectedBodyInstObjects["objects"].Begin(); itConnectedBodyInstObject != rConnectedBodyInstObjects["objects"].End(); ++itConnectedBodyInstObject) { - if (!itConnectedBodyInstObject->HasMember("attachedsensors") || !(*itConnectedBodyInstObject)["attachedsensors"].IsArray() || (*itConnectedBodyInstObject)["attachedsensors"].Size() == 0) { - continue; - } - std::string connectedBodyObjectPk = GetJsonValueByKey(*itConnectedBodyInstObject, "object_pk"); - RobotResourcePtr connectedbodyrobot(new RobotResource(controller,connectedBodyObjectPk)); - std::vector connectedbodyattachedsensors; - - connectedbodyrobot->GetAttachedSensors(connectedbodyattachedsensors, false); - - for (size_t i = 0; i < connectedbodyattachedsensors.size(); i++) { - std::string resolvedSensorName = str(boost::format("%s_%s")%connectedBodyName%connectedbodyattachedsensors[i]->name); - connectedbodyattachedsensors[i]->name = resolvedSensorName; - } - - attachedsensors.reserve(attachedsensors.size() + connectedbodyattachedsensors.size()); - attachedsensors.insert(attachedsensors.end(), connectedbodyattachedsensors.begin(), connectedbodyattachedsensors.end()); - } - } - } -} - -SceneResource::InstObject::InstObject(ControllerClientPtr controller, const std::string& scenepk, const std::string& pk_) : WebResource(controller, str(boost::format("scene/%s/instobject")%scenepk), pk_), pk(pk_) -{ -} - -void SceneResource::InstObject::SetTransform(const Transform& t) -{ - GETCONTROLLERIMPL(); - rapidjson::Document pt; - std::string data = str(boost::format("{\"quaternion\":[%.15f, %.15f, %.15f, %.15f], \"translate\":[%.15f, %.15f, %.15f]}")%t.quaternion[0]%t.quaternion[1]%t.quaternion[2]%t.quaternion[3]%t.translate[0]%t.translate[1]%t.translate[2]); - controller->CallPutJSON(str(boost::format("%s/%s/?format=json")%GetResourceName()%GetPrimaryKey()), data, pt); -} - -void SceneResource::InstObject::SetDOFValues() -{ - GETCONTROLLERIMPL(); - std::stringstream ss; - ss << "{\"dofvalues\":"; - ss << "["; - if( this->dofvalues.size() > 0 ) { - for (unsigned int i = 0; i < this->dofvalues.size(); i++) { - ss << this->dofvalues[i]; - if( i != this->dofvalues.size()-1) { - ss << ", "; - } - } - } - ss << "]}"; - rapidjson::Document pt; - controller->CallPutJSON(str(boost::format("%s/%s/?format=json")%GetResourceName()%GetPrimaryKey()), ss.str(), pt); -} - - -void SceneResource::InstObject::GrabObject(InstObjectPtr grabbedobject, std::string& grabbedobjectlinkpk, std::string& grabbinglinkpk) -{ - SceneResource::InstObject::Grab grab; - grab.instobjectpk = grabbedobject->pk; - grab.grabbed_linkpk = grabbedobjectlinkpk; - grab.grabbing_linkpk = grabbinglinkpk; - //TODO do not use this->grabs. this is the cached information - for (size_t igrab = 0; igrab < this->grabs.size(); igrab++) { - if (this->grabs[igrab] == grab) { - std::stringstream ss; - ss << grabbedobject->name << "is already grabbed"; - MUJIN_LOG_ERROR(ss.str()); - return; - } - } - std::stringstream ss; - ss << "{\"grabs\":"; - ss << "["; - if( this->grabs.size() > 0 ) { - for (unsigned int i = 0; i < this->grabs.size(); i++) { - ss << this->grabs[i].Serialize() << ", "; - } - } - ss << grab.Serialize(); - ss << "]}"; - GETCONTROLLERIMPL(); - rapidjson::Document pt; - controller->CallPutJSON(str(boost::format("%s/%s/?format=json")%GetResourceName()%GetPrimaryKey()), ss.str(), pt); -} - -void SceneResource::InstObject::ReleaseObject(InstObjectPtr grabbedobject, std::string& grabbedobjectlinkpk, std::string& grabbinglinkpk) -{ - SceneResource::InstObject::Grab grab; - grab.instobjectpk = grabbedobject->pk; - grab.grabbed_linkpk = grabbedobjectlinkpk; - grab.grabbing_linkpk = grabbinglinkpk; - for (size_t igrab = 0; igrab < this->grabs.size(); igrab++) { - if (this->grabs[igrab] == grab) { - this->grabs.erase(std::remove(this->grabs.begin(), this->grabs.end(), this->grabs[igrab]), this->grabs.end()); - std::stringstream ss; - ss << "{\"grabs\":"; - ss << "["; - if( this->grabs.size() > 0 ) { - for (unsigned int i = 0; i < this->grabs.size(); i++) { - ss << this->grabs[i].Serialize() << ", "; - } - if( igrab != this->grabs.size()-1) { - ss << ", "; - } - } - ss << "]}"; - GETCONTROLLERIMPL(); - rapidjson::Document pt; - controller->CallPutJSON(str(boost::format("%s/%s/?format=json")%GetResourceName()%GetPrimaryKey()), ss.str(), pt); - } - } - std::stringstream ss; - ss << grabbedobject->name << "is not grabbed"; - MUJIN_LOG_ERROR(ss.str()); - -} - -SceneResource::SceneResource(ControllerClientPtr controller, const std::string& pk) : WebResource(controller, "scene", pk) -{ - // get something from the scene? - //this->Get(""); -} - -TaskResourcePtr SceneResource::GetOrCreateTaskFromName_UTF8(const std::string& taskname, const std::string& tasktype, int options) -{ - GETCONTROLLERIMPL(); - rapidjson::Document pt(rapidjson::kObjectType); - controller->CallGet(str(boost::format("scene/%s/task/?format=json&limit=1&name=%s&fields=pk,tasktype")%GetPrimaryKey()%controller->EscapeString(taskname)), pt); - // task exists - std::string pk; - - std::string tasktype_internal = tasktype; - if( tasktype == "realtimeitlplanning" ) { - tasktype_internal = "realtimeitlplanning3"; - } - - if (pt.IsObject() && pt.HasMember("objects") && pt["objects"].IsArray() && pt["objects"].Size() > 0) { - rapidjson::Value& objects = pt["objects"]; - pk = GetJsonValueByKey(objects[0], "pk"); - std::string currenttasktype = GetJsonValueByKey(objects[0], "tasktype"); - if( currenttasktype != tasktype_internal && (currenttasktype != "realtimeitlplanning" || tasktype_internal != "realtimeitlplanning3")) { - throw MUJIN_EXCEPTION_FORMAT("task pk %s exists and has type %s, expected is %s", pk%currenttasktype%tasktype_internal, MEC_InvalidState); - } - } - else { - pt.SetObject(); - controller->CallPost(str(boost::format("scene/%s/task/?format=json&fields=pk")%GetPrimaryKey()), str(boost::format("{\"name\":\"%s\", \"tasktype\":\"%s\", \"scenepk\":\"%s\"}")%taskname%tasktype_internal%GetPrimaryKey()), pt); - LoadJsonValueByKey(pt, "pk", pk); - } - - if( pk.size() == 0 ) { - return TaskResourcePtr(); - } - - if( tasktype_internal == "binpicking" || tasktype_internal == "realtimeitlplanning3") { - BinPickingTaskResourcePtr task; - if( options & 1 ) { -#ifdef MUJIN_USEZMQ - task.reset(new BinPickingTaskZmqResource(GetController(), pk, GetPrimaryKey(), tasktype_internal)); -#else - throw MujinException("cannot create binpicking zmq task since not compiled with zeromq library", MEC_Failed); -#endif - } - else { - task.reset(new BinPickingTaskResource(GetController(), pk, GetPrimaryKey())); - } - return task; - } - else if( tasktype_internal == "cablepicking" ) { // TODO create CablePickingTaskResource - BinPickingTaskResourcePtr task; - if( options & 1 ) { -#ifdef MUJIN_USEZMQ - task.reset(new BinPickingTaskZmqResource(GetController(), pk, GetPrimaryKey())); -#else - throw MujinException("cannot create binpicking zmq task since not compiled with zeromq library", MEC_Failed); -#endif - } - else { - task.reset(new BinPickingTaskResource(GetController(), pk, GetPrimaryKey())); - } - return task; - } - else { - TaskResourcePtr task(new TaskResource(GetController(), pk)); - return task; - } -} - -void SceneResource::SetInstObjectsState(const std::vector& instobjects, const std::vector& states) -{ - GETCONTROLLERIMPL(); - if (instobjects.size() != states.size()) { - throw MUJIN_EXCEPTION_FORMAT("the size of instobjects (%d) and the one of states (%d) must be the same",instobjects.size()%states.size(),MEC_InvalidArguments); - } - boost::format transformtemplate("{\"pk\":\"%s\",\"quaternion\":[%.15f, %.15f, %.15f, %.15f], \"translate\":[%.15f, %.15f, %.15f] %s}"); - std::string datastring, sdofvalues; - for(size_t i = 0; i < instobjects.size(); ++i) { - const Transform& transform = states[i].transform; - if( states[i].dofvalues.size() > 0 ) { - sdofvalues = str(boost::format(", \"dofvalues\":[%.15f")%states[i].dofvalues.at(0)); - for(size_t j = 1; j < states[i].dofvalues.size(); ++j) { - sdofvalues += str(boost::format(", %.15f")%states[i].dofvalues.at(j)); - } - sdofvalues += "]"; - } - else { - sdofvalues = ""; - } - datastring += str(transformtemplate%instobjects[i]->pk%transform.quaternion[0]%transform.quaternion[1]%transform.quaternion[2]%transform.quaternion[3]%transform.translate[0]%transform.translate[1]%transform.translate[2]%sdofvalues); - if ( i != instobjects.size()-1) { - datastring += ","; - } - } - std::string data = str(boost::format("{\"objects\": [%s]}")%datastring); - rapidjson::Document pt; - controller->CallPutJSON(str(boost::format("%s/%s/instobject/?format=json")%GetResourceName()%GetPrimaryKey()), data, pt); -} - -TaskResourcePtr SceneResource::GetTaskFromName_UTF8(const std::string& taskname, int options) -{ - GETCONTROLLERIMPL(); - rapidjson::Document pt(rapidjson::kObjectType); - controller->CallGet(str(boost::format("scene/%s/task/?format=json&limit=1&name=%s&fields=pk,tasktype")%GetPrimaryKey()%controller->EscapeString(taskname)), pt); - // task exists - - if (!(pt.IsObject() && pt.HasMember("objects") && pt["objects"].IsArray() && pt["objects"].Size() > 0)) { - throw MUJIN_EXCEPTION_FORMAT("could not find task with name %s", taskname, MEC_InvalidState); - } - - std::string pk = GetJsonValueByKey(pt["objects"][0], "pk"); - TaskResourcePtr task(new TaskResource(GetController(), pk)); - return task; -} - -TaskResourcePtr SceneResource::GetOrCreateTaskFromName_UTF16(const std::wstring& taskname, const std::string& tasktype, int options) -{ - std::string taskname_utf8; - utf8::utf16to8(taskname.begin(), taskname.end(), std::back_inserter(taskname_utf8)); - return GetOrCreateTaskFromName_UTF8(taskname_utf8, tasktype, options); -} - -TaskResourcePtr SceneResource::GetTaskFromName_UTF16(const std::wstring& taskname, int options) -{ - std::string taskname_utf8; - utf8::utf16to8(taskname.begin(), taskname.end(), std::back_inserter(taskname_utf8)); - return GetTaskFromName_UTF8(taskname_utf8, options); -} - -BinPickingTaskResourcePtr SceneResource::GetOrCreateBinPickingTaskFromName_UTF8(const std::string& taskname, const std::string& tasktype, int options) -{ - return boost::dynamic_pointer_cast(GetOrCreateTaskFromName_UTF8(taskname, tasktype, options)); -} - -BinPickingTaskResourcePtr SceneResource::GetOrCreateBinPickingTaskFromName_UTF16(const std::wstring& taskname, const std::string& tasktype, int options) -{ - return boost::dynamic_pointer_cast(GetOrCreateTaskFromName_UTF16(taskname, tasktype, options)); -} - -void SceneResource::GetTaskPrimaryKeys(std::vector& taskkeys) -{ - GETCONTROLLERIMPL(); - rapidjson::Document pt(rapidjson::kObjectType); - controller->CallGet(str(boost::format("scene/%s/task/?format=json&limit=0&fields=pk")%GetPrimaryKey()), pt); - rapidjson::Value& objects = pt["objects"]; - taskkeys.resize(objects.Size()); - size_t i = 0; - for (rapidjson::Document::ValueIterator it = objects.Begin(); it != objects.End(); ++it) { - taskkeys[i++] = GetJsonValueByKey(*it, "pk"); - } -} - -void SceneResource::GetTaskNames(std::vector& taskkeys) -{ - GETCONTROLLERIMPL(); - rapidjson::Document pt(rapidjson::kObjectType); - controller->CallGet(str(boost::format("scene/%s/task/?format=json&limit=0&fields=name")%GetPrimaryKey()), pt); - rapidjson::Value& objects = pt["objects"]; - taskkeys.resize(objects.Size()); - size_t i = 0; - for (rapidjson::Document::ValueIterator it = objects.Begin(); it != objects.End(); ++it) { - taskkeys[i++] = GetJsonValueByKey(*it, "name"); - } -} - -void SceneResource::GetAllSensorSelectionInfos(std::vector& allSensorSelectionInfos) -{ - GETCONTROLLERIMPL(); - allSensorSelectionInfos.clear(); - rapidjson::Document rInstObjects(rapidjson::kObjectType); - controller->CallGet(str(boost::format("scene/%s/instobject/?format=json&limit=0&fields=attachedsensors,connectedBodies,object_pk,name")%GetPrimaryKey()), rInstObjects); - for (rapidjson::Document::ConstValueIterator itInstObject = rInstObjects["objects"].Begin(); itInstObject != rInstObjects["objects"].End(); ++itInstObject) { - const std::string sensorName = GetJsonValueByKey(*itInstObject, "name"); - const std::string objectPk = GetJsonValueByKey(*itInstObject, "object_pk"); - if ( itInstObject->HasMember("attachedsensors") && (*itInstObject)["attachedsensors"].IsArray() && (*itInstObject)["attachedsensors"].Size() > 0) { - rapidjson::Document rRobotAttachedSensors(rapidjson::kObjectType); - controller->CallGet(str(boost::format("robot/%s/attachedsensor/?format=json")%objectPk), rRobotAttachedSensors); - const rapidjson::Value& rAttachedSensors = rRobotAttachedSensors["attachedsensors"]; - for (rapidjson::Document::ConstValueIterator itAttachedSensor = rAttachedSensors.Begin(); itAttachedSensor != rAttachedSensors.End(); ++itAttachedSensor) { - const std::string sensorLinkName = GetJsonValueByKey(*itAttachedSensor, "linkName"); - allSensorSelectionInfos.emplace_back(sensorName, sensorLinkName); - } - } - if ( itInstObject->HasMember("connectedBodies") && (*itInstObject)["connectedBodies"].IsArray() && (*itInstObject)["connectedBodies"].Size() > 0 ) { - rapidjson::Document rRobotConnectedBodies(rapidjson::kObjectType); - controller->CallGet(str(boost::format("robot/%s/connectedBody/?format=json")%objectPk), rRobotConnectedBodies); - rapidjson::Value& rConnectedBodies = rRobotConnectedBodies["connectedBodies"]; - for (rapidjson::Document::ConstValueIterator itConnectedBody = rConnectedBodies.Begin(); itConnectedBody != rConnectedBodies.End(); ++itConnectedBody) { - const std::string connectedBodyScenePk = controller->GetScenePrimaryKeyFromURI_UTF8(GetJsonValueByKey(*itConnectedBody, "url")); - const std::string connectedBodyName = GetJsonValueByKey(*itConnectedBody, "name"); - rapidjson::Document rConnectedBodyInstObjects(rapidjson::kObjectType); - controller->CallGet(str(boost::format("scene/%s/instobject/?format=json&limit=0&fields=attachedsensors,object_pk,name")%connectedBodyScenePk), rConnectedBodyInstObjects); - for (rapidjson::Document::ConstValueIterator itConnectedBodyInstObject = rConnectedBodyInstObjects["objects"].Begin(); itConnectedBodyInstObject != rConnectedBodyInstObjects["objects"].End(); ++itConnectedBodyInstObject) { - if (!itConnectedBodyInstObject->HasMember("attachedsensors") || !(*itConnectedBodyInstObject)["attachedsensors"].IsArray() || (*itConnectedBodyInstObject)["attachedsensors"].Size() == 0) { - continue; - } - std::string connectedBodyObjectPk = GetJsonValueByKey(*itConnectedBodyInstObject, "object_pk"); - rapidjson::Document rConnectedBodyRobotAttachedSensors(rapidjson::kObjectType); - controller->CallGet(str(boost::format("robot/%s/attachedsensor/?format=json")%connectedBodyObjectPk), rConnectedBodyRobotAttachedSensors); - rapidjson::Value& rConnectedBodyAttachedSensors = rConnectedBodyRobotAttachedSensors["attachedsensors"]; - for (rapidjson::Document::ConstValueIterator itConnectedBodyAttachedSensor = rConnectedBodyAttachedSensors.Begin(); itConnectedBodyAttachedSensor != rConnectedBodyAttachedSensors.End(); ++itConnectedBodyAttachedSensor) { - const std::string sensorLinkName = GetJsonValueByKey(*itConnectedBodyAttachedSensor, "linkName"); - allSensorSelectionInfos.emplace_back(sensorName, connectedBodyName+"_"+sensorLinkName); - } - } - } - } - } -} - -void SceneResource::GetInstObjects(std::vector& instobjects) -{ - GETCONTROLLERIMPL(); - rapidjson::Document pt(rapidjson::kObjectType); - controller->CallGet(str(boost::format("scene/%s/instobject/?format=json&limit=0")%GetPrimaryKey()), pt); - rapidjson::Value& objects = pt["objects"]; - - instobjects.resize(objects.Size()); - size_t iobj = 0; - for (rapidjson::Document::ValueIterator it = objects.Begin(); it != objects.End(); ++it) { - InstObjectPtr instobject(new InstObject(controller, GetPrimaryKey(), GetJsonValueByKey(*it, "pk"))); - - LoadJsonValueByKey(*it, "name", instobject->name); - LoadJsonValueByKey(*it, "object_pk", instobject->object_pk); - LoadJsonValueByKey(*it, "reference_object_pk", instobject->reference_object_pk, std::string()); - LoadJsonValueByKey(*it, "reference_uri", instobject->reference_uri); - LoadJsonValueByKey(*it, "dofvalues", instobject->dofvalues); - LoadJsonValueByKey(*it, "quaternion", instobject->quaternion); - LoadJsonValueByKey(*it, "translate", instobject->translate); - - if (it->HasMember("links")) { - rapidjson::Value& jsonlinks = (*it)["links"]; - instobject->links.resize(jsonlinks.Size()); - size_t ilink = 0; - for (rapidjson::Document::ValueIterator itlink = jsonlinks.Begin(); itlink != jsonlinks.End(); ++itlink) { - InstObject::Link& link = instobject->links[ilink]; - LoadJsonValueByKey(*itlink, "name", link.name); - LoadJsonValueByKey(*itlink, "quaternion", link.quaternion); - LoadJsonValueByKey(*itlink, "translate", link.translate); - ilink++; - } - } - - if (it->HasMember("tools")) { - rapidjson::Value& jsontools = (*it)["tools"]; - instobject->tools.resize(jsontools.Size()); - size_t itool = 0; - for (rapidjson::Document::ValueIterator ittool = jsontools.Begin(); ittool != jsontools.End(); ++ittool) { - InstObject::Tool &tool = instobject->tools[itool]; - LoadJsonValueByKey(*ittool, "name", tool.name); - LoadJsonValueByKey(*ittool, "quaternion", tool.quaternion); - LoadJsonValueByKey(*ittool, "translate", tool.translate); - LoadJsonValueByKey(*ittool, "direction", tool.direction); - itool++; - } - } - - if (it->HasMember("grabs")) { - rapidjson::Value& jsongrabs = (*it)["grabs"]; - instobject->grabs.resize(jsongrabs.Size()); - size_t igrab = 0; - for (rapidjson::Document::ValueIterator itgrab = jsongrabs.Begin(); itgrab != jsongrabs.End(); ++itgrab) { - InstObject::Grab &grab = instobject->grabs[igrab]; - LoadJsonValueByKey(*itgrab, "instobjectpk", grab.instobjectpk); - LoadJsonValueByKey(*itgrab, "grabbed_linkpk", grab.grabbed_linkpk); - LoadJsonValueByKey(*itgrab, "grabbing_linkpk", grab.grabbing_linkpk); - igrab++; - } - } - - if (it->HasMember("attachedsensors")) { - rapidjson::Value& jsonattachedsensors = (*it)["attachedsensors"]; - instobject->attachedsensors.resize(jsonattachedsensors.Size()); - size_t iattchedsensor = 0; - for (rapidjson::Document::ValueIterator itsensor = jsonattachedsensors.Begin(); - itsensor != jsonattachedsensors.End(); ++itsensor) { - InstObject::AttachedSensor& sensor = instobject->attachedsensors[iattchedsensor]; - LoadJsonValueByKey(*itsensor, "name", sensor.name); - LoadJsonValueByKey(*itsensor, "quaternion", sensor.quaternion); - LoadJsonValueByKey(*itsensor, "translate", sensor.translate); - iattchedsensor++; - } - } - instobjects.at(iobj++) = instobject; - } -} - -bool SceneResource::FindInstObject(const std::string& name, SceneResource::InstObjectPtr& instobject) -{ - - std::vector instobjects; - this->GetInstObjects(instobjects); - for(size_t i = 0; i < instobjects.size(); ++i) { - if (instobjects[i]->name == name) { - instobject = instobjects[i]; - return true; - } - } - return false; -} - -SceneResource::InstObjectPtr SceneResource::CreateInstObject(const std::string& name, const std::string& referenceUri, const Real quaternion[4], const Real translate[3], double timeout) -{ - GETCONTROLLERIMPL(); - const std::string uri(str(boost::format("scene/%s/instobject/?format=json&fields=pk,object_pk,reference_object_pk,reference_uri,dofvalues,quaternion,translate")%GetPrimaryKey())); - std::string data(str(boost::format("{\"name\":\"%s\", \"quaternion\":[%.15f,%.15f,%.15f,%.15f], \"translate\":[%.15f,%.15f,%.15f]")%name%quaternion[0]%quaternion[1]%quaternion[2]%quaternion[3]%translate[0]%translate[1]%translate[2])); - if (!referenceUri.empty()) { - data += ", \"reference_uri\": \"" + referenceUri + "\""; - } - data += "}"; - - rapidjson::Document pt(rapidjson::kObjectType); - controller->CallPost(uri, data, pt, 201, timeout); - std::string inst_pk = GetJsonValueByKey(pt, "pk"); - SceneResource::InstObjectPtr instobject(new SceneResource::InstObject(GetController(), GetPrimaryKey(), inst_pk)); - LoadJsonValueByKey(pt, "object_pk", instobject->object_pk); - LoadJsonValueByKey(pt, "reference_object_pk", instobject->reference_object_pk, std::string()); - LoadJsonValueByKey(pt, "reference_uri", instobject->reference_uri); - LoadJsonValueByKey(pt, "dofvalues", instobject->dofvalues); - LoadJsonValueByKey(pt, "quaternion", instobject->quaternion); - LoadJsonValueByKey(pt, "translate", instobject->translate); - return instobject; -} - -void SceneResource::DeleteInstObject(const std::string& pk) -{ - GETCONTROLLERIMPL(); - controller->CallDelete(str(boost::format("scene/%s/instobject/%s/")%GetPrimaryKey()%pk), 204); -} - -SceneResourcePtr SceneResource::Copy(const std::string& name) -{ - GETCONTROLLERIMPL(); - rapidjson::Document pt(rapidjson::kObjectType); - controller->CallPost("scene/?format=json", str(boost::format("{\"name\":\"%s\", \"reference_pk\":\"%s\", \"overwrite\": \"1\"}")%name%GetPrimaryKey()), pt); - std::string pk = GetJsonValueByKey(pt, "pk"); - SceneResourcePtr scene(new SceneResource(GetController(), pk)); - return scene; -} - -TaskResource::TaskResource(ControllerClientPtr controller, const std::string& pk) : WebResource(controller,"task",pk) -{ -} - -bool TaskResource::Execute() -{ - GETCONTROLLERIMPL(); - rapidjson::Document pt(rapidjson::kObjectType); - controller->CallPost("job/", str(boost::format("{\"resource_type\":\"task\", \"target_pk\":%s}")%GetPrimaryKey()), pt, 200); - _jobpk = GetJsonValueByKey(pt, "jobpk"); - return true; -} - -void TaskResource::Cancel() -{ - // have to look through all jobs for the task - BOOST_ASSERT(0); -} - -JobStatusCode GetStatusCode(const std::string& str) -{ - MUJIN_LOG_INFO(str); - if (str == "pending") return JSC_Pending; - if (str == "active") return JSC_Active; - if (str == "preempted") return JSC_Preempted; - if (str == "succeeded") return JSC_Succeeded; - if (str == "aborted") return JSC_Aborted; - if (str == "rejected") return JSC_Rejected; - if (str == "preempting") return JSC_Preempting; - if (str == "recalling") return JSC_Recalling; - if (str == "recalled") return JSC_Recalled; - if (str == "lost") return JSC_Lost; - if (str == "unknown") return JSC_Unknown; - throw MUJIN_EXCEPTION_FORMAT("unknown staus %s", str, MEC_InvalidArguments); -} - -void TaskResource::GetRunTimeStatus(JobStatus& status, int options) -{ - status.code = JSC_Unknown; - if( _jobpk.size() > 0 ) { - GETCONTROLLERIMPL(); - rapidjson::Document pt(rapidjson::kObjectType); - std::string url = str(boost::format("job/%s/?format=json&fields=pk,status,fnname,elapsedtime")%_jobpk); - if( options & 1 ) { - url += std::string(",status_text"); - } - controller->CallGet(url, pt); - //pt.get("error_message") - LoadJsonValueByKey(pt, "pk", status.pk); - //LoadJsonValueByKey(pt, "status", status.code); - status.code = GetStatusCode(GetJsonValueByKey(pt, "status")); - LoadJsonValueByKey(pt, "elapsedtime", status.elapsedtime); - LoadJsonValueByKey(pt, "fname", status.type); - if( options & 1 ) { - LoadJsonValueByKey(pt, "status_text", status.message); - } - } -} - -OptimizationResourcePtr TaskResource::GetOrCreateOptimizationFromName_UTF8(const std::string& optimizationname, const std::string& optimizationtype) -{ - GETCONTROLLERIMPL(); - rapidjson::Document pt(rapidjson::kObjectType); - controller->CallGet(str(boost::format("task/%s/optimization/?format=json&limit=1&name=%s&fields=pk,optimizationtype")%GetPrimaryKey()%controller->EscapeString(optimizationname)), pt); - // optimization exists - if (pt.IsObject() && pt.HasMember("objects") && pt["objects"].IsArray() && pt["objects"].Size() > 0) { - rapidjson::Value& object = pt["objects"][0]; - std::string pk = GetJsonValueByKey(object, "pk"); - std::string currentoptimizationtype = GetJsonValueByKey(object, "optimizationtype"); - if( currentoptimizationtype != optimizationtype ) { - throw MUJIN_EXCEPTION_FORMAT("optimization pk %s exists and has type %s, expected is %s", pk%currentoptimizationtype%optimizationtype, MEC_InvalidState); - } - OptimizationResourcePtr optimization(new OptimizationResource(GetController(), pk)); - return optimization; - } - - pt.SetObject(); - controller->CallPost(str(boost::format("task/%s/optimization/?format=json&fields=pk")%GetPrimaryKey()), str(boost::format("{\"name\":\"%s\", \"optimizationtype\":\"%s\", \"taskpk\":\"%s\"}")%optimizationname%optimizationtype%GetPrimaryKey()), pt); - std::string pk = GetJsonValueByKey(pt, "pk"); - OptimizationResourcePtr optimization(new OptimizationResource(GetController(), pk)); - return optimization; -} - -OptimizationResourcePtr TaskResource::GetOrCreateOptimizationFromName_UTF16(const std::wstring& optimizationname, const std::string& optimizationtype) -{ - std::string optimizationname_utf8; - utf8::utf16to8(optimizationname.begin(), optimizationname.end(), std::back_inserter(optimizationname_utf8)); - return GetOrCreateOptimizationFromName_UTF8(optimizationname_utf8, optimizationtype); -} - -void TaskResource::GetOptimizationPrimaryKeys(std::vector& optimizationkeys) -{ - GETCONTROLLERIMPL(); - rapidjson::Document pt(rapidjson::kObjectType); - controller->CallGet(str(boost::format("task/%s/optimization/?format=json&limit=0&fields=pk")%GetPrimaryKey()), pt); - rapidjson::Value& objects = pt["objects"]; - - optimizationkeys.resize(objects.Size()); - size_t i = 0; - for (rapidjson::Document::ValueIterator it = objects.Begin(); it != objects.End(); ++it) { - LoadJsonValueByKey(*it, "pk", optimizationkeys[i++]); - } -} - -void TaskResource::GetTaskParameters(ITLPlanningTaskParameters& taskparameters) -{ - GETCONTROLLERIMPL(); - rapidjson::Document pt(rapidjson::kObjectType); - controller->CallGet(str(boost::format("task/%s/?format=json&fields=taskparameters,tasktype")%GetPrimaryKey()), pt); - std::string tasktype = GetJsonValueByKey(pt, "tasktype"); - if( tasktype != "itlplanning" ) { - throw MUJIN_EXCEPTION_FORMAT("task %s is type %s, expected itlplanning", GetPrimaryKey()%tasktype, MEC_InvalidArguments); - } - rapidjson::Value& taskparametersjson = pt["taskparameters"]; - taskparameters.SetDefaults(); - bool bhasreturnmode = false, bhasreturntostart = false, breturntostart = false; - for (rapidjson::Document::MemberIterator v = taskparametersjson.MemberBegin(); v != taskparametersjson.MemberEnd(); ++v) { - if( std::string(v->name.GetString()) == "startfromcurrent" ) { - taskparameters.startfromcurrent = std::string("True") == v->value.GetString(); - } - else if(std::string(v->name.GetString()) == "returntostart" ) { - bhasreturntostart = true; - breturntostart = std::string("True") == v->value.GetString(); - } - else if( std::string(v->name.GetString()) == "returnmode" ) { - taskparameters.returnmode = v->value.GetString(); - bhasreturnmode = true; - } - else if( std::string(v->name.GetString()) == "ignorefigure" ) { - taskparameters.ignorefigure = std::string("True") == v->value.GetString(); - } - else if( std::string(v->name.GetString()) == "vrcruns" ) { - //taskparameters.vrcruns = boost::lexical_cast(v->value); - LoadJsonValueByKey(taskparametersjson, "vrcruns", taskparameters.vrcruns); - } - else if( std::string(v->name.GetString()) == "unit" ) { - taskparameters.unit = v->value.GetString(); - } - else if( std::string(v->name.GetString()) == "optimizationvalue" ) { - LoadJsonValueByKey(taskparametersjson, "optimizationvalue", taskparameters.optimizationvalue); - //taskparameters.optimizationvalue = boost::lexical_cast(v->second.data()); - } - else if( std::string(v->name.GetString()) == "program" ) { - taskparameters.program = v->value.GetString(); - } - else if( std::string(v->name.GetString()) == "parameters" ) { - taskparameters.parameters = DumpJson(v->value,2); - } - else if( std::string(v->name.GetString()) == "initial_envstate" ) { - ExtractEnvironmentStateFromPTree(v->value, taskparameters.initial_envstate); - } - else if( std::string(v->name.GetString()) == "final_envstate" ) { - ExtractEnvironmentStateFromPTree(v->value, taskparameters.final_envstate); - } - else { - std::stringstream ss; - ss << "unsupported ITL task parameter " << v->name.GetString(); - MUJIN_LOG_ERROR(ss.str()); - } - } - // for back compat - if( !bhasreturnmode && bhasreturntostart ) { - taskparameters.returnmode = breturntostart ? "start" : ""; - } -} - -void TaskResource::SetTaskParameters(const ITLPlanningTaskParameters& taskparameters) -{ - GETCONTROLLERIMPL(); - std::string startfromcurrent = taskparameters.startfromcurrent ? "True" : "False"; - std::string ignorefigure = taskparameters.ignorefigure ? "True" : "False"; - std::string vrcruns = boost::lexical_cast(taskparameters.vrcruns); - - std::stringstream ssinitial_envstate; - if( taskparameters.initial_envstate.size() > 0 ) { - ssinitial_envstate << std::setprecision(std::numeric_limits::digits10+1); - ssinitial_envstate << ", \"initial_envstate\":"; - SerializeEnvironmentStateToJSON(taskparameters.initial_envstate, ssinitial_envstate); - } - std::stringstream ssfinal_envstate; - if( taskparameters.final_envstate.size() > 0 ) { - ssfinal_envstate << std::setprecision(std::numeric_limits::digits10+1); - ssfinal_envstate << ", \"final_envstate\":"; - SerializeEnvironmentStateToJSON(taskparameters.final_envstate, ssfinal_envstate); - } - - // because program will inside string, encode newlines - std::string program; - std::vector< std::pair > serachpairs(3); - serachpairs[0].first = "\""; serachpairs[0].second = "\\\""; - serachpairs[1].first = "\n"; serachpairs[1].second = "\\n"; - serachpairs[2].first = "\r\n"; serachpairs[2].second = "\\n"; - SearchAndReplace(program, taskparameters.program, serachpairs); - std::string taskgoalput = str(boost::format("{\"tasktype\": \"itlplanning\", \"taskparameters\":{\"optimizationvalue\":%f, \"program\":\"%s\", \"parameters\":%s, \"unit\":\"%s\", \"returnmode\":\"%s\", \"startfromcurrent\":\"%s\", \"ignorefigure\":\"%s\", \"vrcruns\":%d %s %s } }")%taskparameters.optimizationvalue%program%taskparameters.parameters%taskparameters.unit%taskparameters.returnmode%startfromcurrent%ignorefigure%vrcruns%ssinitial_envstate.str()%ssfinal_envstate.str()); - rapidjson::Document pt; - controller->CallPutJSON(str(boost::format("task/%s/?format=json&fields=")%GetPrimaryKey()), taskgoalput, pt); -} - -PlanningResultResourcePtr TaskResource::GetResult() -{ - GETCONTROLLERIMPL(); - rapidjson::Document pt(rapidjson::kObjectType); - controller->CallGet(str(boost::format("task/%s/result/?format=json&limit=1&optimization=None&fields=pk")%GetPrimaryKey()), pt); - if (!(pt.IsObject() && pt.HasMember("objects") && pt["objects"].IsArray() && pt["objects"].Size() > 0) ) { - return PlanningResultResourcePtr(); - } - std::string pk = GetJsonValueByKey(pt["objects"][0], "pk"); - PlanningResultResourcePtr result(new PlanningResultResource(GetController(), pk)); - return result; -} - -OptimizationResource::OptimizationResource(ControllerClientPtr controller, const std::string& pk) : WebResource(controller,"optimization",pk) -{ -} - -void OptimizationResource::Execute(bool bClearOldResults) -{ - GETCONTROLLERIMPL(); - rapidjson::Document pt(rapidjson::kObjectType); - controller->CallPost(str(boost::format("optimization/%s/")%GetPrimaryKey()), str(boost::format("{\"clear\":%d}")%bClearOldResults), pt, 200); - _jobpk = GetJsonValueByKey(pt, "jobpk"); -} - -void OptimizationResource::Cancel() -{ - BOOST_ASSERT(0); -} - -void OptimizationResource::GetRunTimeStatus(JobStatus& status, int options) -{ - status.code = JSC_Unknown; - if( _jobpk.size() > 0 ) { - GETCONTROLLERIMPL(); - rapidjson::Document pt(rapidjson::kObjectType); - std::string url = str(boost::format("job/%s/?format=json&fields=pk,status,fnname,elapsedtime")%_jobpk); - if( options & 1 ) { - url += std::string(",status_text"); - } - controller->CallGet(url, pt); - //pt.get("error_message") - LoadJsonValueByKey(pt, "pk", status.pk); - // LoadJsonValueByKey(pt, "status", status.code); - status.code = GetStatusCode(GetJsonValueByKey(pt, "status")); - LoadJsonValueByKey(pt, "fname", status.type); - LoadJsonValueByKey(pt, "elpasedtime", status.elapsedtime); - if( options & 1 ) { - LoadJsonValueByKey(pt, "status-text", status.message); - } - } -} - -void OptimizationResource::SetOptimizationParameters(const RobotPlacementOptimizationParameters& optparams) -{ - GETCONTROLLERIMPL(); - std::string ignorebasecollision = optparams.ignorebasecollision ? "True" : "False"; - std::string optimizationgoalput = str(boost::format("{\"optimizationtype\":\"robotplacement\", \"optimizationparameters\":{\"targetname\":\"%s\", \"frame\":\"%s\", \"ignorebasecollision\":\"%s\", \"unit\":\"%s\", \"maxrange_\":[ %.15f, %.15f, %.15f, %.15f], \"minrange_\":[ %.15f, %.15f, %.15f, %.15f], \"stepsize_\":[ %.15f, %.15f, %.15f, %.15f], \"topstorecandidates\":%d} }")%optparams.targetname%optparams.framename%ignorebasecollision%optparams.unit%optparams.maxrange[0]%optparams.maxrange[1]%optparams.maxrange[2]%optparams.maxrange[3]%optparams.minrange[0]%optparams.minrange[1]%optparams.minrange[2]%optparams.minrange[3]%optparams.stepsize[0]%optparams.stepsize[1]%optparams.stepsize[2]%optparams.stepsize[3]%optparams.topstorecandidates); - rapidjson::Document pt; - controller->CallPutJSON(str(boost::format("optimization/%s/?format=json&fields=")%GetPrimaryKey()), optimizationgoalput, pt); -} - -void OptimizationResource::SetOptimizationParameters(const PlacementsOptimizationParameters& optparams) -{ - GETCONTROLLERIMPL(); - std::stringstream optimizationgoalput; - optimizationgoalput << str(boost::format("{\"optimizationtype\":\"placements\", \"optimizationparameters\":{ \"unit\":\"%s\", \"topstorecandidates\":%d")%optparams.unit%optparams.topstorecandidates); - for(size_t itarget = 0; itarget < optparams.targetnames.size(); ++itarget) { - std::string ignorebasecollision = optparams.ignorebasecollisions[itarget] ? "True" : "False"; - std::string suffix; - if( itarget > 0 ) { - suffix = boost::lexical_cast(itarget+1); - } - optimizationgoalput << str(boost::format(", \"targetname%s\":\"%s\", \"frame%s\":\"%s\", \"ignorebasecollision%s\":\"%s\", , \"maxrange%s_\":[ %.15f, %.15f, %.15f, %.15f], \"minrange%s_\":[ %.15f, %.15f, %.15f, %.15f], \"stepsize%s_\":[ %.15f, %.15f, %.15f, %.15f]")%suffix%optparams.targetnames[itarget]%suffix%optparams.framenames[itarget]%suffix%ignorebasecollision%suffix%optparams.maxranges[itarget][0]%optparams.maxranges[itarget][1]%optparams.maxranges[itarget][2]%optparams.maxranges[itarget][3]%suffix%optparams.minranges[itarget][0]%optparams.minranges[itarget][1]%optparams.minranges[itarget][2]%optparams.minranges[itarget][3]%suffix%optparams.stepsizes[itarget][0]%optparams.stepsizes[itarget][1]%optparams.stepsizes[itarget][2]%optparams.stepsizes[itarget][3]); - } - optimizationgoalput << "} }"; - rapidjson::Document pt; - controller->CallPutJSON(str(boost::format("optimization/%s/?format=json&fields=")%GetPrimaryKey()), optimizationgoalput.str(), pt); -} - -void OptimizationResource::GetResults(std::vector& results, int startoffset, int num) -{ - GETCONTROLLERIMPL(); - std::string querystring = str(boost::format("optimization/%s/result/?format=json&fields=pk&order_by=task_time&offset=%d&limit=%d")%GetPrimaryKey()%startoffset%num); - rapidjson::Document pt(rapidjson::kObjectType); - controller->CallGet(querystring, pt); - if (!(pt.IsObject() && pt.HasMember("objects") && pt["objects"].IsArray() && pt["objects"].Size() > 0)) { - return; - } - rapidjson::Value& objects = pt["objects"]; - results.resize(objects.Size()); - size_t i = 0; - for (rapidjson::Document::ValueIterator it = objects.Begin(); it != objects.End(); ++it) { - results[i++].reset(new PlanningResultResource(controller, GetJsonValueByKey(*it, "pk"))); - } -} - -PlanningResultResource::PlanningResultResource(ControllerClientPtr controller, const std::string& resulttype, const std::string& pk) : WebResource(controller,resulttype,pk) -{ -} - -PlanningResultResource::PlanningResultResource(ControllerClientPtr controller, const std::string& pk) : WebResource(controller,"planningresult",pk) -{ -} - -void PlanningResultResource::GetEnvironmentState(EnvironmentState& envstate) -{ - GETCONTROLLERIMPL(); - rapidjson::Document pt(rapidjson::kObjectType); - controller->CallGet(str(boost::format("%s/%s/?format=json&fields=envstate")%GetResourceName()%GetPrimaryKey()), pt); - ExtractEnvironmentStateFromPTree(pt["envstate"], envstate); -} - -void PlanningResultResource::GetAllRawProgramData(std::string& outputdata, const std::string& programtype) -{ - GETCONTROLLERIMPL(); - controller->CallGet(str(boost::format("%s/%s/program/?type=%s")%GetResourceName()%GetPrimaryKey()%programtype), outputdata); -} - -void PlanningResultResource::GetRobotRawProgramData(std::string& outputdata, const std::string& robotpk, const std::string& programtype) -{ - GETCONTROLLERIMPL(); - controller->CallGet(str(boost::format("%s/%s/program/%s/?type=%s")%GetResourceName()%GetPrimaryKey()%robotpk%programtype), outputdata); -} - -void PlanningResultResource::GetPrograms(RobotControllerPrograms& programs, const std::string& programtype) -{ - GETCONTROLLERIMPL(); - rapidjson::Document pt(rapidjson::kObjectType); - programs.programs.clear(); - controller->CallGet(str(boost::format("%s/%s/program/?format=json&type=%s")%GetResourceName()%GetPrimaryKey()%programtype), pt); - for (rapidjson::Document::MemberIterator it = pt.MemberBegin(); it != pt.MemberEnd(); ++it) { - std::string robotpk = it->name.GetString(); - std::string program = GetJsonValueByKey(it->value, "program"); - std::string currenttype = GetJsonValueByKey(it->value, "type"); - programs.programs[robotpk] = RobotProgramData(program, currenttype); - } -} - -DebugResource::DebugResource(ControllerClientPtr controller, const std::string& pk_) : WebResource(controller, "debug", pk_), pk(pk_) -{ -} - -DebugResource::DebugResource(ControllerClientPtr controller, const std::string& resource, const std::string& pk_) : WebResource(controller, resource, pk_), pk(pk_) -{ -} - -void DebugResource::Download(std::ostream& outputStream, double timeout) -{ - GETCONTROLLERIMPL(); - controller->CallGet(str(boost::format("%s/%s/download/")%GetResourceName()%GetPrimaryKey()), outputStream, 200, timeout); -} - -ControllerClientPtr CreateControllerClient(const std::string& usernamepassword, const std::string& baseurl, const std::string& proxyserverport, const std::string& proxyuserpw, int options, double timeout) -{ - return ControllerClientPtr(new ControllerClientImpl(usernamepassword, baseurl, proxyserverport, proxyuserpw, options, timeout)); -} - -void ControllerClientDestroy() -{ - DestroyControllerClient(); -} - -void DestroyControllerClient() -{ -} - -void ComputeMatrixFromTransform(Real matrix[12], const Transform &transform) -{ - throw MujinException("not implemented yet"); -// length2 = numpy.sum(quat**2) -// ilength2 = 2.0/length2 -// qq1 = ilength2*quat[1]*quat[1] -// qq2 = ilength2*quat[2]*quat[2] -// qq3 = ilength2*quat[3]*quat[3] -// T = numpy.eye(4) -// T[0,0] = 1 - qq2 - qq3 -// T[0,1] = ilength2*(quat[1]*quat[2] - quat[0]*quat[3]) -// T[0,2] = ilength2*(quat[1]*quat[3] + quat[0]*quat[2]) -// T[1,0] = ilength2*(quat[1]*quat[2] + quat[0]*quat[3]) -// T[1,1] = 1 - qq1 - qq3 -// T[1,2] = ilength2*(quat[2]*quat[3] - quat[0]*quat[1]) -// T[2,0] = ilength2*(quat[1]*quat[3] - quat[0]*quat[2]) -// T[2,1] = ilength2*(quat[2]*quat[3] + quat[0]*quat[1]) -// T[2,2] = 1 - qq1 - qq2 -} - -void ComputeZXYFromMatrix(Real ZXY[3], Real matrix[12]) -{ - throw MujinException("not implemented yet"); -// if abs(T[2][0]) < 1e-10 and abs(T[2][2]) < 1e-10: -// sinx = T[2][1] -// x = numpy.pi/2 if sinx > 0 else -numpy.pi/2 -// z = 0.0 -// y = numpy.arctan2(sinx*T[1][0],T[0][0]) -// else: -// y = numpy.arctan2(-T[2][0],T[2][2]) -// siny = numpy.sin(y) -// cosy = numpy.cos(y) -// Ryinv = numpy.array([[cosy,0,-siny],[0,1,0],[siny,0,cosy]]) -// Rzx = numpy.dot(T[0:3,0:3],Ryinv) -// x = numpy.arctan2(Rzx[2][1],Rzx[2][2]) -// z = numpy.arctan2(Rzx[1][0],Rzx[0][0]) -// return numpy.array([x,y,z]) -} - -void ComputeZXYFromTransform(Real ZXY[3], const Transform& transform) -{ - throw MujinException("not implemented yet"); - //zxyFromMatrix(matrixFromTransform()) -} - -} diff --git a/src/mujindefinitions.cpp b/src/mujindefinitions.cpp deleted file mode 100644 index 27256694..00000000 --- a/src/mujindefinitions.cpp +++ /dev/null @@ -1,21 +0,0 @@ -// -*- coding: utf-8 -*- -// Copyright (C) 2012-2023 MUJIN Inc. -#include - -namespace mujin { - - -void SensorSelectionInfo::LoadFromJson(const rapidjson::Value& rSensorSelectionInfo) -{ - mujinjson::LoadJsonValueByKey(rSensorSelectionInfo, "sensorName", sensorName); - mujinjson::LoadJsonValueByKey(rSensorSelectionInfo, "sensorLinkName", sensorLinkName); -} - -void SensorSelectionInfo::SaveToJson(rapidjson::Value& rSensorSelectionInfo, rapidjson::Document::AllocatorType& alloc) const -{ - rSensorSelectionInfo.SetObject(); - mujinjson::SetJsonValueByKey(rSensorSelectionInfo, "sensorName", sensorName, alloc); - mujinjson::SetJsonValueByKey(rSensorSelectionInfo, "sensorLinkName", sensorLinkName, alloc); -} - -} // end namespace mujin diff --git a/src/mujinjson.cpp b/src/mujinjson.cpp index 2654a946..84e37d74 100644 --- a/src/mujinjson.cpp +++ b/src/mujinjson.cpp @@ -1,12 +1,12 @@ // -*- coding: utf-8 -*- // Copyright (C) 2012-2022 MUJIN Inc. -#include +#include #include #include #include -namespace mujinjson { +namespace mujinjsonwebstack { template void ParseJsonFile(rapidjson::Document& d, const char* filename, Container& buffer) @@ -15,13 +15,13 @@ void ParseJsonFile(rapidjson::Document& d, const char* filename, Container& buff const int fd = ::open(filename, O_RDONLY); if (fd < 0) { - throw MujinJSONException(boost::str(boost::format("Could not open Json file %s") % filename)); + throw MujinJSONWebstackException(boost::str(boost::format("Could not open Json file %s") % filename)); } struct stat fs; if (::fstat(fd, &fs) != 0) { ::close(fd); - throw MujinJSONException(boost::str(boost::format("Could not get file stats of Json file %s") % filename)); + throw MujinJSONWebstackException(boost::str(boost::format("Could not get file stats of Json file %s") % filename)); } if( fs.st_size == 0 ) { @@ -30,7 +30,7 @@ void ParseJsonFile(rapidjson::Document& d, const char* filename, Container& buff size_t nBufferSize = nChunkSize; char* pbuffer = (char*)::malloc(nBufferSize); if( !pbuffer ) { - throw MujinJSONException("Could not allocate memory"); + throw MujinJSONWebstackException("Could not allocate memory"); } size_t nFileSize = 0; @@ -40,7 +40,7 @@ void ParseJsonFile(rapidjson::Document& d, const char* filename, Container& buff nBufferSize += nChunkSize; pbuffer = (char*)::realloc(pbuffer, nBufferSize); if( !pbuffer ) { - throw MujinJSONException("Could not allocate memory"); + throw MujinJSONWebstackException("Could not allocate memory"); } nTotalToRead = nBufferSize - nFileSize; } @@ -51,7 +51,7 @@ void ParseJsonFile(rapidjson::Document& d, const char* filename, Container& buff } ::close(fd); ::free(pbuffer); - throw MujinJSONException(boost::str(boost::format("Could not read file data from Json file '%s'") % filename)); + throw MujinJSONWebstackException(boost::str(boost::format("Could not read file data from Json file '%s'") % filename)); } if( count == 0 ) { break; // EOF @@ -63,7 +63,7 @@ void ParseJsonFile(rapidjson::Document& d, const char* filename, Container& buff if( nFileSize == 0 ) { ::free(pbuffer); - throw MujinJSONException(boost::str(boost::format("JSON file '%s' is empty") % filename)); + throw MujinJSONWebstackException(boost::str(boost::format("JSON file '%s' is empty") % filename)); } try { @@ -87,7 +87,7 @@ void ParseJsonFile(rapidjson::Document& d, const char* filename, Container& buff continue; // retry if interrupted } ::close(fd); - throw MujinJSONException(boost::str(boost::format("Could not read file data from Json file '%s'") % filename)); + throw MujinJSONWebstackException(boost::str(boost::format("Could not read file data from Json file '%s'") % filename)); } if( count == 0 ) { break; // EOF @@ -97,7 +97,7 @@ void ParseJsonFile(rapidjson::Document& d, const char* filename, Container& buff ::close(fd); if( offset == 0 ) { - throw MujinJSONException(boost::str(boost::format("JSON file '%s' is empty") % filename)); + throw MujinJSONWebstackException(boost::str(boost::format("JSON file '%s' is empty") % filename)); } ParseJson(d, reinterpret_cast(buffer.data()), offset); @@ -110,4 +110,4 @@ void __InternalParseJsonFile(rapidjson::Document& d, const char* filename) return ParseJsonFile(d, filename, buffer); } -} // end namespace mujinjson +} // end namespace mujinjsonwebstack diff --git a/src/mujinzmq.cpp b/src/mujinzmq.cpp deleted file mode 100644 index fcfb814e..00000000 --- a/src/mujinzmq.cpp +++ /dev/null @@ -1,507 +0,0 @@ -#include "mujincontrollerclient/mujinzmq.h" - -#include -#if BOOST_VERSION > 104800 -#include -#endif -#include -#include - -#include "common.h" -#include "logging.h" - -MUJIN_LOGGER("mujin.controllerclientcpp.mujinzmq"); - - -#if defined(_MSC_VER) && _MSC_VER < 1600 -typedef __int64 int64_t; -typedef unsigned __int64 uint64_t; -#endif - - -#ifndef MUJIN_TIME -#define MUJIN_TIME -#include - -#ifndef _WIN32 -#if !(defined(CLOCK_GETTIME_FOUND) && (POSIX_TIMERS > 0 || _POSIX_TIMERS > 0)) -#include -#endif -#else -#define WIN32_LEAN_AND_MEAN -#include -#include // ftime(), struct timeb -inline void usleep(unsigned long microseconds) { - Sleep((microseconds+999)/1000); -} -#endif - -#ifdef _WIN32 -inline uint64_t GetMilliTime() -{ - LARGE_INTEGER count, freq; - QueryPerformanceCounter(&count); - QueryPerformanceFrequency(&freq); - return (uint64_t)((count.QuadPart * 1000) / freq.QuadPart); -} - -inline uint64_t GetMicroTime() -{ - LARGE_INTEGER count, freq; - QueryPerformanceCounter(&count); - QueryPerformanceFrequency(&freq); - return (count.QuadPart * 1000000) / freq.QuadPart; -} - -inline uint64_t GetNanoTime() -{ - LARGE_INTEGER count, freq; - QueryPerformanceCounter(&count); - QueryPerformanceFrequency(&freq); - return (count.QuadPart * 1000000000) / freq.QuadPart; -} - -inline static uint64_t GetNanoPerformanceTime() { - return GetNanoTime(); -} - -#else - -inline void GetWallTime(uint32_t& sec, uint32_t& nsec) -{ -#if defined(CLOCK_GETTIME_FOUND) && (POSIX_TIMERS > 0 || _POSIX_TIMERS > 0) - struct timespec start; - clock_gettime(CLOCK_REALTIME, &start); - sec = start.tv_sec; - nsec = start.tv_nsec; -#else - struct timeval timeofday; - gettimeofday(&timeofday,NULL); - sec = timeofday.tv_sec; - nsec = timeofday.tv_usec * 1000; -#endif -} - -inline uint64_t GetMilliTimeOfDay() -{ - struct timeval timeofday; - gettimeofday(&timeofday,NULL); - return (uint64_t)timeofday.tv_sec*1000+(uint64_t)timeofday.tv_usec/1000; -} - -inline uint64_t GetNanoTime() -{ - uint32_t sec,nsec; - GetWallTime(sec,nsec); - return (uint64_t)sec*1000000000 + (uint64_t)nsec; -} - -inline uint64_t GetMicroTime() -{ - uint32_t sec,nsec; - GetWallTime(sec,nsec); - return (uint64_t)sec*1000000 + (uint64_t)nsec/1000; -} - -inline uint64_t GetMilliTime() -{ - uint32_t sec,nsec; - GetWallTime(sec,nsec); - return (uint64_t)sec*1000 + (uint64_t)nsec/1000000; -} - -inline static uint64_t GetNanoPerformanceTime() -{ -#if defined(CLOCK_GETTIME_FOUND) && (POSIX_TIMERS > 0 || _POSIX_TIMERS > 0) && defined(_POSIX_MONOTONIC_CLOCK) - struct timespec start; - uint32_t sec, nsec; - clock_gettime(CLOCK_MONOTONIC, &start); - sec = start.tv_sec; - nsec = start.tv_nsec; - return (uint64_t)sec*1000000000 + (uint64_t)nsec; -#else - return GetNanoTime(); -#endif -} -#endif -#endif - - -using namespace mujinzmq; -using namespace mujinclient; - -ZmqSubscriber::ZmqSubscriber(const std::string& host, const unsigned int port) -{ - _host = host; - _port = port; -} - -ZmqSubscriber::~ZmqSubscriber() -{ - _DestroySocket(); -} - -void ZmqSubscriber::_InitializeSocket(boost::shared_ptr context) -{ - if (!!context) { - _context = context; - _sharedcontext = true; - } else { - _context.reset(new zmq::context_t(1)); - _sharedcontext = false; - } - _socket.reset(new zmq::socket_t ((*_context.get()), ZMQ_SUB)); - _socket->setsockopt(ZMQ_TCP_KEEPALIVE, 1); // turn on tcp keepalive, do these configuration before connect - _socket->setsockopt(ZMQ_TCP_KEEPALIVE_IDLE, 2); // the interval between the last data packet sent (simple ACKs are not considered data) and the first keepalive probe; after the connection is marked to need keepalive, this counter is not used any further - _socket->setsockopt(ZMQ_TCP_KEEPALIVE_INTVL, 2); // the interval between subsequential keepalive probes, regardless of what the connection has exchanged in the meantime - _socket->setsockopt(ZMQ_TCP_KEEPALIVE_CNT, 2); // the number of unacknowledged probes to send before considering the connection dead and notifying the application layer - _socket->setsockopt(ZMQ_SNDHWM, 2); - _socket->setsockopt(ZMQ_LINGER, 100); // ms - std::ostringstream port_stream; - port_stream << _port; - _socket->connect (("tcp://" + _host + ":" + port_stream.str()).c_str()); - _socket->setsockopt(ZMQ_SUBSCRIBE, "", 0); -} - -void ZmqSubscriber::_DestroySocket() -{ - if (!!_socket) { - _socket->close(); - _socket.reset(); - } - if (!!_context && !_sharedcontext) { - _context->close(); - _context.reset(); - } -} - -ZmqPublisher::ZmqPublisher(const unsigned int port) -{ - _port = port; -} - -ZmqPublisher::~ZmqPublisher() -{ - _DestroySocket(); -} - -bool ZmqPublisher::Publish(const std::string& messagestr) -{ - zmq::message_t message(messagestr.size()); - memcpy(message.data(), messagestr.data(), messagestr.size()); - return _socket->send(message); -} - -void ZmqPublisher::_InitializeSocket(boost::shared_ptr context) -{ - if (!!context) { - _context = context; - _sharedcontext = true; - } - else { - _context.reset(new zmq::context_t(1)); - _sharedcontext = false; - } - _socket.reset(new zmq::socket_t ((*(zmq::context_t*)_context.get()), ZMQ_PUB)); - _socket->setsockopt(ZMQ_TCP_KEEPALIVE, 1); // turn on tcp keepalive, do these configuration before connect - _socket->setsockopt(ZMQ_TCP_KEEPALIVE_IDLE, 2); // the interval between the last data packet sent (simple ACKs are not considered data) and the first keepalive probe; after the connection is marked to need keepalive, this counter is not used any further - _socket->setsockopt(ZMQ_TCP_KEEPALIVE_INTVL, 2); // the interval between subsequential keepalive probes, regardless of what the connection has exchanged in the meantime - _socket->setsockopt(ZMQ_TCP_KEEPALIVE_CNT, 2); // the number of unacknowledged probes to send before considering the connection dead and notifying the application layer - _socket->setsockopt(ZMQ_SNDHWM, 2); - _socket->setsockopt(ZMQ_LINGER, 100); // ms - std::ostringstream port_stream; - port_stream << _port; - _socket->bind (("tcp://*:" + port_stream.str()).c_str()); -} - -void ZmqPublisher::_DestroySocket() -{ - if (!!_socket) { - _socket->close(); - _socket.reset(); - } - if (!!_context && !_sharedcontext) { - _context->close(); - _context.reset(); - } -} - - -ZmqClient::ZmqClient(const std::string& host, const unsigned int port, const CheckPreemptFn& preemptfn) -{ - _host = host; - _port = port; - _preemptfn = preemptfn; -} - -ZmqClient::~ZmqClient() -{ - _DestroySocket(); -} - -std::string ZmqClient::Call(const std::string& msg, const double timeout, const unsigned int checkpreemptbits) -{ - //send - zmq::message_t request (msg.size()); - // std::cout << msg.size() << std::endl; - // std::cout << msg << std::endl; - memcpy ((void *) request.data (), msg.c_str(), msg.size()); - - uint64_t starttime = GetMilliTime(); - bool recreatedonce = false; - while (GetMilliTime() - starttime < timeout*1000.0) { - try { - _socket->send(request); - break; - } catch (const zmq::error_t& e) { - if (e.num() == EAGAIN) { - MUJIN_LOG_ERROR("failed to send request, try again"); - boost::this_thread::sleep(boost::posix_time::milliseconds(100)); - continue; - } else { - std::stringstream errss; - errss << "failed to send msg: "; - if (msg.length() > 1000) { - errss << msg.substr(0, 1000) << "..."; - } else { - errss << msg; - } - MUJIN_LOG_ERROR(errss.str()); - } - if (!recreatedonce) { - MUJIN_LOG_INFO("re-creating zmq socket and trying again"); - if (!!_socket) { - _socket->close(); - _socket.reset(); - } - _InitializeSocket(_context); - recreatedonce = true; - } else{ - std::stringstream ss; - ss << "Failed to send request after re-creating socket."; - MUJIN_LOG_ERROR(ss.str()); - throw MujinException(ss.str(), MEC_Failed); - } - } - if( !!_preemptfn ) { - _preemptfn(checkpreemptbits); - } - - } - if (GetMilliTime() - starttime > timeout*1000.0) { - std::stringstream ss; - ss << "Timed out trying to send request."; - MUJIN_LOG_ERROR(ss.str()); - if (msg.length() > 1000) { - MUJIN_LOG_INFO(msg.substr(0,1000) << "..."); - } else { - MUJIN_LOG_INFO(msg); - } - throw MujinException(ss.str(), MEC_Timeout); - } - //recv - recreatedonce = false; - zmq::message_t reply; - bool receivedonce = false; // receive at least once - while (!receivedonce || (GetMilliTime() - starttime < timeout * 1000.0)) { - if( !!_preemptfn ) { - _preemptfn(checkpreemptbits); - } - - try { - - zmq::pollitem_t pollitem; - memset(&pollitem, 0, sizeof(zmq::pollitem_t)); - pollitem.socket = _socket->operator void*(); - pollitem.events = ZMQ_POLLIN; - - // if timeout param is 0, caller means infinite - long timeoutms = -1; - if (timeout > 0) { - timeoutms = timeout * 1000.0; - } - - zmq::poll(&pollitem, 1, timeoutms); - receivedonce = true; - if (pollitem.revents & ZMQ_POLLIN) { - _socket->recv(&reply); - std::string replystring((char *) reply.data (), (size_t) reply.size()); - return replystring; - } else{ - std::stringstream ss; - if (msg.length() > 1000) { - ss << "Timed out receiving response of command " << msg.substr(0, 1000) << "... after " << timeout << " seconds"; - } else { - ss << "Timed out receiving response of command " << msg << " after " << timeout << " seconds"; - } - MUJIN_LOG_ERROR(ss.str()); -#if BOOST_VERSION > 104800 - std::string errstr = ss.str(); - boost::replace_all(errstr, "\"", ""); // need to remove " in the message so that json parser works - boost::replace_all(errstr, "\\", ""); // need to remove \ in the message so that json parser works -#else - std::vector< std::pair > serachpairs(2); - serachpairs[0].first = "\""; serachpairs[0].second = ""; - serachpairs[1].first = "\\"; serachpairs[1].second = ""; - std::string errstr; - mujinclient::SearchAndReplace(errstr, ss.str(), serachpairs); -#endif - throw MujinException(errstr, MEC_Timeout); - } - - } catch (const zmq::error_t& e) { - if (e.num() == EAGAIN) { - MUJIN_LOG_ERROR("failed to receive reply, zmq::EAGAIN"); - boost::this_thread::sleep(boost::posix_time::milliseconds(100)); - continue; - } else { - MUJIN_LOG_INFO("failed to send"); - if (msg.length() > 1000) { - MUJIN_LOG_INFO(msg.substr(0,1000) << "..."); - } else { - MUJIN_LOG_INFO(msg); - } - } - if (!recreatedonce) { - MUJIN_LOG_INFO("re-creating zmq socket and trying again"); - if (!!_socket) { - _socket->close(); - _socket.reset(); - } - _InitializeSocket(_context); - recreatedonce = true; - } else{ - std::string errstr = "Failed to receive response after re-creating socket."; - MUJIN_LOG_ERROR(errstr); - throw MujinException(errstr, MEC_Failed); - } - } - } - if (GetMilliTime() - starttime > timeout*1000.0) { - std::stringstream ss; - ss << "timed out trying to receive request"; - MUJIN_LOG_ERROR(ss.str()); - if (msg.length() > 1000) { - MUJIN_LOG_INFO(msg.substr(0,1000) << "..."); - } else { - MUJIN_LOG_INFO(msg); - } - throw MujinException(ss.str(), MEC_Failed); - } - - return ""; -} - -void ZmqClient::_InitializeSocket(boost::shared_ptr context) -{ - if (!!context) { - _context = context; - _sharedcontext = true; - } else { - _context.reset(new zmq::context_t(1)); - _sharedcontext = false; - } - _socket.reset(new zmq::socket_t ((*(zmq::context_t*)_context.get()), ZMQ_REQ)); - _socket->setsockopt(ZMQ_TCP_KEEPALIVE, 1); // turn on tcp keepalive, do these configuration before connect - _socket->setsockopt(ZMQ_TCP_KEEPALIVE_IDLE, 2); // the interval between the last data packet sent (simple ACKs are not considered data) and the first keepalive probe; after the connection is marked to need keepalive, this counter is not used any further - _socket->setsockopt(ZMQ_TCP_KEEPALIVE_INTVL, 2); // the interval between subsequential keepalive probes, regardless of what the connection has exchanged in the meantime - _socket->setsockopt(ZMQ_TCP_KEEPALIVE_CNT, 2); // the number of unacknowledged probes to send before considering the connection dead and notifying the application layer - std::ostringstream port_stream; - port_stream << _port; - std::stringstream ss; - ss << "connecting to socket at " << _host << ":" << _port; - MUJIN_LOG_INFO(ss.str()); - _socket->connect (("tcp://" + _host + ":" + port_stream.str()).c_str()); -} - -void ZmqClient::_DestroySocket() -{ - if (!!_socket) { - _socket->setsockopt(ZMQ_LINGER, 0); - _socket->close(); - _socket.reset(); - } - if (!!_context && !_sharedcontext) { - _context->close(); - _context.reset(); - } -} - -ZmqServer::ZmqServer(const unsigned int port) : _sharedcontext(false), _port(port) { -} - -ZmqServer::~ZmqServer() { - _DestroySocket(); -} - -unsigned int ZmqServer::Recv(std::string& data, long timeout) -{ - // wait timeout in millisecond for message - if (timeout > 0) { - zmq::poll(&_pollitem, 1, timeout); - if ((_pollitem.revents & ZMQ_POLLIN) == 0) - { - // did not receive anything - return 0; - } - } - - const bool ret = _socket->recv(&_reply, ZMQ_NOBLOCK); - if (ret && _reply.size() > 0) { - data.resize(_reply.size()); - std::copy((uint8_t*)_reply.data(), (uint8_t*)_reply.data() + _reply.size(), data.begin()); - return _reply.size(); - } else { - return 0; - } -} - -void ZmqServer::Send(const std::string& message) -{ - zmq::message_t request(message.size()); - memcpy((void *)request.data(), message.c_str(), message.size()); - _socket->send(request); -} - -void ZmqServer::_InitializeSocket(boost::shared_ptr context) -{ - if (!!context) { - _context = context; - _sharedcontext = true; - } else { - _context.reset(new zmq::context_t(1)); - _sharedcontext = false; - } - - _socket.reset(new zmq::socket_t((*(zmq::context_t*)_context.get()), ZMQ_REP)); - _socket->setsockopt(ZMQ_TCP_KEEPALIVE, 1); // turn on tcp keepalive, do these configuration before connect - _socket->setsockopt(ZMQ_TCP_KEEPALIVE_IDLE, 2); // the interval between the last data packet sent (simple ACKs are not considered data) and the first keepalive probe; after the connection is marked to need keepalive, this counter is not used any further - _socket->setsockopt(ZMQ_TCP_KEEPALIVE_INTVL, 2); // the interval between subsequential keepalive probes, regardless of what the connection has exchanged in the meantime - _socket->setsockopt(ZMQ_TCP_KEEPALIVE_CNT, 2); // the number of unacknowledged probes to send before considering the connection dead and notifying the application layer - - // setup the pollitem - memset(&_pollitem, 0, sizeof(_pollitem)); - _pollitem.socket = _socket->operator void*(); - _pollitem.events = ZMQ_POLLIN; - - std::ostringstream endpoint; - endpoint << "tcp://*:" << _port; - _socket->bind(endpoint.str().c_str()); - std::stringstream ss; - ss << "binded to " << endpoint.str(); - MUJIN_LOG_INFO(ss.str()); -} - -void ZmqServer::_DestroySocket() -{ - if (!!_socket) { - _socket->setsockopt(ZMQ_LINGER, 0); - _socket->close(); - _socket.reset(); - } - if (!!_context && !_sharedcontext) { - _context->close(); - _context.reset(); - } - memset(&_pollitem, 0, sizeof(_pollitem)); -} diff --git a/src/webstackclient.cpp b/src/webstackclient.cpp new file mode 100644 index 00000000..566c795f --- /dev/null +++ b/src/webstackclient.cpp @@ -0,0 +1,1210 @@ +// -*- coding: utf-8 -*- +// Copyright (C) 2012-2013 MUJIN Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include + +#include +#include +#include +#include +#include + +#include "common.h" +#include "logging.h" + +#define SKIP_PEER_VERIFICATION // temporary +//#define SKIP_HOSTNAME_VERIFICATION + +MUJIN_LOGGER("mujin.webstackcpp"); + +#define CURL_OPTION_SAVER(curl, curlopt, curvalue) boost::shared_ptr __curloptionsaver ## curlopt((void*)0, boost::bind(boost::function(curl_easy_setopt), curl, curlopt, curvalue)) +#define CURL_OPTION_SETTER(curl, curlopt, newvalue) CHECKCURLCODE(curl_easy_setopt(curl, curlopt, newvalue), "curl_easy_setopt " # curlopt) +#define CURL_OPTION_SAVE_SETTER(curl, curlopt, curvalue, newvalue) CURL_OPTION_SAVER(curl, curlopt, curvalue); CURL_OPTION_SETTER(curl, curlopt, newvalue) +#define CURL_INFO_GETTER(curl, curlinfo, outvalue) CHECKCURLCODE(curl_easy_getinfo(curl, curlinfo, outvalue), "curl_easy_getinfo " # curlinfo) +#define CURL_PERFORM(curl) CHECKCURLCODE(curl_easy_perform(curl), "curl_easy_perform") +#define CURL_FORM_RELEASER(form) boost::shared_ptr __curlformreleaser ## form((void*)0, boost::bind(boost::function(curl_formfree), form)) + +namespace mujinwebstackclient { + +using namespace mujinjsonwebstack; + +namespace { + +template +std::wstring ParseWincapsWCNPath(const T& sourcefilename, const boost::function& ConvertToFileSystemEncoding) +{ + // scenefilenames is the WPJ file, so have to open it up to see what directory it points to + // note that the encoding is utf-16 + // + // .\threegoaltouch\threegoaltouch.WCN; + // + // first have to get the raw utf-16 data +#if defined(_WIN32) || defined(_WIN64) + std::ifstream wpjfilestream(sourcefilename.c_str(), std::ios::binary|std::ios::in); +#else + // linux doesn't mix ifstream and wstring + std::ifstream wpjfilestream(ConvertToFileSystemEncoding(sourcefilename).c_str(), std::ios::binary|std::ios::in); +#endif + if( !wpjfilestream ) { + throw MUJIN_EXCEPTION_FORMAT("failed to open file %s", ConvertToFileSystemEncoding(sourcefilename), MEC_InvalidArguments); + } + std::wstringstream utf16stream; + bool readbom = false; + while(!wpjfilestream.eof() ) { + unsigned short c; + wpjfilestream.read(reinterpret_cast(&c),sizeof(c)); + if( !wpjfilestream ) { + break; + } + // skip the first character (BOM) due to a bug in boost property_tree (should be fixed in 1.49) + if( readbom || c != 0xfeff ) { + utf16stream << static_cast(c); + } + else { + readbom = true; + } + } + boost::property_tree::wptree wpj; + boost::property_tree::read_xml(utf16stream, wpj); + boost::property_tree::wptree& clsProject = wpj.get_child(L"clsProject"); + boost::property_tree::wptree& WCNPath = clsProject.get_child(L"WCNPath"); + std::wstring strWCNPath = WCNPath.data(); + if( strWCNPath.size() > 0 ) { + // post process the string to get the real filesystem directory + if( strWCNPath.at(strWCNPath.size()-1) == L';') { + strWCNPath.resize(strWCNPath.size()-1); + } + + if( strWCNPath.size() >= 2 && (strWCNPath[0] == L'.' && strWCNPath[1] == L'\\') ) { + // don't need the prefix + strWCNPath = strWCNPath.substr(2); + } + } + + return strWCNPath; +} + +/// \brief given a port string "80", fill WebstackClientInfo httpPort +void _ParseClientInfoPort(const char* port, size_t length, WebstackClientInfo& clientInfo) +{ + clientInfo.httpPort = 0; + for (; length > 0; ++port, --length) { + clientInfo.httpPort = clientInfo.httpPort * 10 + (*port - '0'); + } +} + +void _ParseUsernamePassword(const char* usernamePassword, const char* usernamePasswordEnd, WebstackClientInfo& clientInfo) +{ + const char* colon = strstr(usernamePassword, ":"); // not found is ok + if (colon != nullptr) { + const char* password = colon + sizeof(":") - 1; + clientInfo.username = std::string(usernamePassword, colon - usernamePassword); + clientInfo.password = std::string(password, usernamePasswordEnd - password); + } else { + clientInfo.username = std::string(usernamePassword, usernamePasswordEnd - usernamePassword); + } +} + +} // end namespace + + +WebstackClientInfo WebstackClientInfo::FromUrl(const char* url) +{ + WebstackClientInfo clientInfo; + const char* colonSlashSlash = strstr(url, "://"); + if (colonSlashSlash == nullptr) { + return clientInfo; + } + const char* hostname = colonSlashSlash + sizeof("://") - 1; + const char* at = strstr(hostname, "@"); // not found is ok + const char* slash = strstr(hostname, "/"); // not found is ok + if (at != nullptr && (slash == nullptr || at < slash)) { + // if the at is before the slash, i.e. for the username:password + const char* usernamePassword = hostname; + hostname = at + sizeof("@") - 1; + _ParseUsernamePassword(usernamePassword, at, clientInfo); + } + const char* port = strstr(hostname, ":"); // not found is ok + if (slash == nullptr) { + if (port == nullptr) { + // no port, no slash + clientInfo.host = hostname; + } else { + // has port, no slash + const char* portStart = port + sizeof(":") - 1; + _ParseClientInfoPort(portStart, strlen(portStart), clientInfo); + clientInfo.host = std::string(hostname, port - hostname); + } + } else { + if (port != nullptr && port < slash) { + // has port before slash + const char* portStart = port + sizeof(":") - 1; + _ParseClientInfoPort(portStart, slash - portStart, clientInfo); + clientInfo.host = std::string(hostname, port - hostname); + } else { + // no port, but has slash + clientInfo.host = std::string(hostname, slash - hostname); + } + } + return clientInfo; +} + +void WebstackClientInfo::Reset() +{ + host.clear(); + httpPort = 0; + username.clear(); + password.clear(); + additionalHeaders.clear(); + unixEndpoint.clear(); +} + +void WebstackClientInfo::LoadFromJson(const rapidjson::Value& rClientInfo) +{ + mujinjsonwebstack::LoadJsonValueByKey(rClientInfo, "host", host); + mujinjsonwebstack::LoadJsonValueByKey(rClientInfo, "httpPort", httpPort); + mujinjsonwebstack::LoadJsonValueByKey(rClientInfo, "username", username); + mujinjsonwebstack::LoadJsonValueByKey(rClientInfo, "password", password); + mujinjsonwebstack::LoadJsonValueByKey(rClientInfo, "additionalHeaders", additionalHeaders); + mujinjsonwebstack::LoadJsonValueByKey(rClientInfo, "unixEndpoint", unixEndpoint); +} + +void WebstackClientInfo::SaveToJson(rapidjson::Value& rClientInfo, rapidjson::Document::AllocatorType& alloc) const +{ + rClientInfo.SetObject(); + if( !host.empty() ) { + mujinjsonwebstack::SetJsonValueByKey(rClientInfo, "host", host, alloc); + } + if( httpPort != 0 ) { + mujinjsonwebstack::SetJsonValueByKey(rClientInfo, "httpPort", httpPort, alloc); + } + if( !username.empty() ) { + mujinjsonwebstack::SetJsonValueByKey(rClientInfo, "username", username, alloc); + } + if( !password.empty() ) { + mujinjsonwebstack::SetJsonValueByKey(rClientInfo, "password", password, alloc); + } + if( !additionalHeaders.empty() ) { + mujinjsonwebstack::SetJsonValueByKey(rClientInfo, "additionalHeaders", additionalHeaders, alloc); + } + if( !unixEndpoint.empty() ) { + mujinjsonwebstack::SetJsonValueByKey(rClientInfo, "unixEndpoint", unixEndpoint, alloc); + } +} + +bool WebstackClientInfo::operator==(const WebstackClientInfo &rhs) const +{ + return host == rhs.host && + httpPort == rhs.httpPort && + username == rhs.username && + password == rhs.password && + additionalHeaders == rhs.additionalHeaders && + unixEndpoint == rhs.unixEndpoint; +} + +std::string WebstackClientInfo::GetURL(bool bIncludeNamePassword) const +{ + std::string url; + if( host.empty() ) { + return url; + } + url += "http://"; + if( bIncludeNamePassword ) { + url += username; + url += ":"; + url += password; + url += "@"; + } + + url += host; + if( httpPort != 0 ) { + url += ":"; + url += std::to_string(httpPort); + } + return url; +} + +WebstackClient::WebstackClient(const std::string& usernamepassword, const std::string& baseuri, const std::string& proxyserverport, const std::string& proxyuserpw, int options, double timeout) +{ + BOOST_ASSERT( !baseuri.empty() ); + _clientInfo = WebstackClientInfo::FromUrl(baseuri.c_str()); + if (!usernamepassword.empty()) { + const char* usernamePasswordPtr = usernamepassword.c_str(); + _ParseUsernamePassword(usernamePasswordPtr, usernamePasswordPtr + usernamepassword.length(), _clientInfo); + } + + _httpheadersjson = NULL; + _httpheadersstl = NULL; + _httpheadersmultipartformdata = NULL; + _baseuri = baseuri; + // ensure trailing slash + if( _baseuri[_baseuri.size()-1] != '/' ) { + _baseuri.push_back('/'); + } + _baseapiuri = _baseuri + std::string("api/v1/"); + // hack for now since webdav server and api server could be running on different ports + if( boost::algorithm::ends_with(_baseuri, ":8000/") || (options&0x80000000) ) { + // testing on localhost, however the webdav server is running on port 80... + _basewebdavuri = str(boost::format("%s/u/%s/")%_baseuri.substr(0,_baseuri.size()-6)%_clientInfo.username); + } + else { + _basewebdavuri = str(boost::format("%su/%s/")%_baseuri%_clientInfo.username); + } + + //CURLcode code = curl_global_init(CURL_GLOBAL_SSL|CURL_GLOBAL_WIN32); + _curl = curl_easy_init(); + BOOST_ASSERT(!!_curl); + +#ifdef _DEBUG + // CURL_OPTION_SETTER(_curl, CURLOPT_VERBOSE, 1L); +#endif + _errormessage.resize(CURL_ERROR_SIZE); + CURL_OPTION_SETTER(_curl, CURLOPT_ERRORBUFFER, &_errormessage[0]); + +#ifdef SKIP_PEER_VERIFICATION + /* + * if you want to connect to a site who isn't using a certificate that is + * signed by one of the certs in the ca bundle you have, you can skip the + * verification of the server's certificate. this makes the connection + * a lot less secure. + * + * if you have a ca cert for the server stored someplace else than in the + * default bundle, then the curlopt_capath option might come handy for + * you. + */ + CURL_OPTION_SETTER(_curl, CURLOPT_SSL_VERIFYPEER, 0L); +#endif + +#ifdef SKIP_HOSTNAME_VERIFICATION + /* + * If the site you're connecting to uses a different host name that what + * they have mentioned in their server certificate's commonName (or + * subjectAltName) fields, libcurl will refuse to connect. You can skip + * this check, but this will make the connection less secure. + */ + CURL_OPTION_SETTER(_curl, CURLOPT_SSL_VERIFYHOST, 0L); +#endif + + if( proxyserverport.size() > 0 ) { + SetProxy(proxyserverport, proxyuserpw); + } + + CURL_OPTION_SETTER(_curl, CURLOPT_HTTPAUTH, CURLAUTH_BASIC); + CURL_OPTION_SETTER(_curl, CURLOPT_USERPWD, usernamepassword.c_str()); + + // need to set the following? + //CURLOPT_USERAGENT + //CURLOPT_TCP_KEEPIDLE + //CURLOPT_TCP_KEEPALIVE + //CURLOPT_TCP_KEEPINTVL + + CURL_OPTION_SETTER(_curl, CURLOPT_COOKIEFILE, ""); // just to start the cookie engine + + // save everything to _buffer, neceesary to do it before first POST/GET calls or data will be output to stdout + // these should be set on individual calls + // CURL_OPTION_SETTER(_curl, CURLOPT_WRITEFUNCTION, _WriteStringStreamCallback); // just to start the cookie engine + // CURL_OPTION_SETTER(_curl, CURLOPT_WRITEDATA, &_buffer); + + std::string useragent = std::string("controllerclientcpp/")+MUJINWEBSTACKCLIENT_VERSION_STRING; + CURL_OPTION_SETTER(_curl, CURLOPT_USERAGENT, useragent.c_str()); + + CURL_OPTION_SETTER(_curl, CURLOPT_FOLLOWLOCATION, 1L); // we can always follow redirect now, we don't need to detect login page + CURL_OPTION_SETTER(_curl, CURLOPT_MAXREDIRS, 10L); + CURL_OPTION_SETTER(_curl, CURLOPT_NOSIGNAL, 1L); + + CURL_OPTION_SETTER(_curl, CURLOPT_POSTFIELDSIZE, 0L); + CURL_OPTION_SETTER(_curl, CURLOPT_POSTFIELDS, NULL); + + // csrftoken can be any non-empty string + _csrfmiddlewaretoken = "csrftoken"; + std::string cookie = "Set-Cookie: csrftoken=" + _csrfmiddlewaretoken; +#if CURL_AT_LEAST_VERSION(7,60,0) + // with https://github.com/curl/curl/commit/b8d5036ec9b702d6392c97a6fc2e141d6c7cce1f, setting domain param to cookie is required. + if(_baseuri.find('/') == _baseuri.size()-1) { + // _baseuri should be hostname with trailing slash + cookie += "; domain="; + cookie += _baseuri.substr(0,_baseuri.size()-1); + } else { + CURLU *url = curl_url(); + BOOST_SCOPE_EXIT_ALL(&url) { + curl_url_cleanup(url); + }; + CHECKCURLUCODE(curl_url_set(url, CURLUPART_URL, _baseuri.c_str(), 0), "cannot parse url"); + char *host = NULL; + BOOST_SCOPE_EXIT_ALL(&host) { + if(host) { + curl_free(host); + } + }; + CHECKCURLUCODE(curl_url_get(url, CURLUPART_HOST, &host, 0), "cannot determine hostname from url"); + cookie += "; domain="; + cookie += host; + } +#endif + CURL_OPTION_SETTER(_curl, CURLOPT_COOKIELIST, cookie.c_str()); + + _charset = "utf-8"; + _language = "en-us"; +#if defined(_WIN32) || defined(_WIN64) + UINT codepage = GetACP(); + std::map::const_iterator itcodepage = encoding::GetCodePageMap().find(codepage); + if( itcodepage != encoding::GetCodePageMap().end() ) { + _charset = itcodepage->second; + } +#endif + MUJIN_LOG_INFO("setting character set to " << _charset); + _SetupHTTPHeadersJSON(); + _SetupHTTPHeadersSTL(); + _SetupHTTPHeadersMultipartFormData(); +} + +WebstackClient::~WebstackClient() +{ + if( !!_httpheadersjson ) { + curl_slist_free_all(_httpheadersjson); + } + if( !!_httpheadersstl ) { + curl_slist_free_all(_httpheadersstl); + } + if( !!_httpheadersmultipartformdata ) { + curl_slist_free_all(_httpheadersmultipartformdata); + } + curl_easy_cleanup(_curl); +} + +void WebstackClient::SetCharacterEncoding(const std::string& newencoding) +{ + boost::mutex::scoped_lock lock(_mutex); + _charset = newencoding; + _SetupHTTPHeadersJSON(); + // the following two format does not need charset + // _SetupHTTPHeadersSTL(); + // _SetupHTTPHeadersMultipartFormData(); +} + +void WebstackClient::SetLanguage(const std::string& language) +{ + boost::mutex::scoped_lock lock(_mutex); + if (language!= "") { + _language = language; + } + _SetupHTTPHeadersJSON(); + // the following two format does not need language + // _SetupHTTPHeadersSTL(); + // _SetupHTTPHeadersMultipartFormData(); +} + +void WebstackClient::SetUserAgent(const std::string& userAgent) +{ + CURL_OPTION_SETTER(_curl, CURLOPT_USERAGENT, userAgent.c_str()); +} + +void WebstackClient::SetAdditionalHeaders(const std::vector& additionalHeaders) +{ + boost::mutex::scoped_lock lock(_mutex); + _clientInfo.additionalHeaders = additionalHeaders; + _SetupHTTPHeadersJSON(); + _SetupHTTPHeadersSTL(); + _SetupHTTPHeadersMultipartFormData(); +} + +const std::string& WebstackClient::GetUserName() const +{ + return _clientInfo.username; +} + +const std::string& WebstackClient::GetBaseURI() const +{ + return _baseuri; +} + +const WebstackClientInfo& WebstackClient::GetClientInfo() const +{ + return _clientInfo; +} + +void WebstackClient::SetProxy(const std::string& serverport, const std::string& userpw) +{ + // mutally exclusive with unix endpoint settings + CURL_OPTION_SETTER(_curl, CURLOPT_UNIX_SOCKET_PATH, NULL); + CURL_OPTION_SETTER(_curl, CURLOPT_PROXY, serverport.c_str()); + CURL_OPTION_SETTER(_curl, CURLOPT_PROXYUSERPWD, userpw.c_str()); + _clientInfo.unixEndpoint.clear(); +} + +void WebstackClient::SetUnixEndpoint(const std::string& unixendpoint) +{ + // mutually exclusive with proxy settings + CURL_OPTION_SETTER(_curl, CURLOPT_PROXY, NULL); + CURL_OPTION_SETTER(_curl, CURLOPT_PROXYUSERPWD, NULL); + CURL_OPTION_SETTER(_curl, CURLOPT_UNIX_SOCKET_PATH, unixendpoint.c_str()); + _clientInfo.unixEndpoint = unixendpoint; +} + +void WebstackClient::RestartServer(double timeout) +{ + boost::mutex::scoped_lock lock(_mutex); + CURL_OPTION_SAVE_SETTER(_curl, CURLOPT_TIMEOUT_MS, 0L, (long)(timeout * 1000L)); + _uri = _baseuri + std::string("restartserver/"); + CURL_OPTION_SAVE_SETTER(_curl, CURLOPT_HTTPHEADER, NULL, _httpheadersjson); + CURL_OPTION_SAVE_SETTER(_curl, CURLOPT_URL, NULL, _uri.c_str()); + CURL_OPTION_SAVE_SETTER(_curl, CURLOPT_POST, 0L, 1L); + _buffer.clear(); + _buffer.str(""); + CURL_OPTION_SAVE_SETTER(_curl, CURLOPT_WRITEFUNCTION, NULL, _WriteStringStreamCallback); + CURL_OPTION_SAVE_SETTER(_curl, CURLOPT_WRITEDATA, NULL, &_buffer); + CURL_PERFORM(_curl); + long http_code = 0; + CURL_INFO_GETTER(_curl, CURLINFO_RESPONSE_CODE, &http_code); + if( http_code != 200 ) { + throw MUJIN_EXCEPTION_FORMAT0("Failed to restart server, please try again or contact MUJIN support", MEC_HTTPServer); + } +} + +void WebstackClient::ExecuteGraphQuery(const char* operationName, const char* query, const rapidjson::Value& rVariables, rapidjson::Value& rResult, rapidjson::Document::AllocatorType& rAlloc, double timeout) +{ + _ExecuteGraphQuery(operationName, query, rVariables, rResult, rAlloc, timeout, true, false); +} + +void WebstackClient::ExecuteGraphQueryRaw(const char* operationName, const char* query, const rapidjson::Value& rVariables, rapidjson::Value& rResult, rapidjson::Document::AllocatorType& rAlloc, double timeout) +{ + _ExecuteGraphQuery(operationName, query, rVariables, rResult, rAlloc, timeout, false, true); +} + +void WebstackClient::_ExecuteGraphQuery(const char* operationName, const char* query, const rapidjson::Value& rVariables, rapidjson::Value& rResult, rapidjson::Document::AllocatorType& rAlloc, double timeout, bool checkForErrors, bool returnRawResponse) +{ + rResult.SetNull(); // zero output + + rapidjson::Document rResultDoc(&rAlloc); + + { + boost::mutex::scoped_lock lock(_mutex); + + rapidjson::StringBuffer& rRequestStringBuffer = _rRequestStringBufferCache; + rRequestStringBuffer.Clear(); + + { + // use the callers allocator to construct the request body + rapidjson::Value rRequest, rValue; + rRequest.SetObject(); + rValue.SetString(operationName, rAlloc); + rRequest.AddMember(rapidjson::Document::StringRefType("operationName"), rValue, rAlloc); + rValue.SetString(query, rAlloc); + rRequest.AddMember(rapidjson::Document::StringRefType("query"), rValue, rAlloc); + rValue.CopyFrom(rVariables, rAlloc); + rRequest.AddMember(rapidjson::Document::StringRefType("variables"), rValue, rAlloc); + + rapidjson::Writer writer(rRequestStringBuffer); + rRequest.Accept(writer); + } + + _uri = _baseuri + "api/v2/graphql"; + _CallPost(_uri, rRequestStringBuffer.GetString(), rResultDoc, rResultDoc.GetAllocator(), 200, timeout); + } + + // parse response + if (!rResultDoc.IsObject()) { + throw MUJIN_EXCEPTION_FORMAT("Execute graph query does not return valid response \"%s\", invalid response: %s", operationName%mujinjsonwebstack::DumpJson(rResultDoc), MEC_HTTPServer); + } + + if (checkForErrors) { + // look for errors in response + const rapidjson::Value::ConstMemberIterator itErrors = rResultDoc.FindMember("errors"); + if (itErrors != rResultDoc.MemberEnd() && itErrors->value.IsArray() && itErrors->value.Size() > 0) { + MUJIN_LOG_VERBOSE(str(boost::format("graph query has errors \"%s\": %s")%operationName%mujinjsonwebstack::DumpJson(rResultDoc))); + for (rapidjson::Value::ConstValueIterator itError = itErrors->value.Begin(); itError != itErrors->value.End(); ++itError) { + const rapidjson::Value& rError = *itError; + if (rError.IsObject() && rError.HasMember("message") && rError["message"].IsString()) { + const char* errorCode = "unknown"; + const rapidjson::Value::ConstMemberIterator itExtensions = rError.FindMember("extensions"); + if (itExtensions != rError.MemberEnd() && itExtensions->value.IsObject() && itExtensions->value.HasMember("errorCode") && itExtensions->value["errorCode"].IsString()) { + errorCode = itExtensions->value["errorCode"].GetString(); + } + throw mujinwebstackclient::MujinGraphQueryError(boost::str(boost::format("[%s:%d] graph query has errors \"%s\": %s")%(__PRETTY_FUNCTION__)%(__LINE__)%operationName%rError["message"].GetString()), errorCode); + } + } + throw MUJIN_EXCEPTION_FORMAT("graph query has undefined errors \"%s\": %s", operationName%mujinjsonwebstack::DumpJson(rResultDoc), MEC_HTTPServer); + } + } + + // should have data member + if (!rResultDoc.HasMember("data")) { + throw MUJIN_EXCEPTION_FORMAT("Execute graph query does not have 'data' field in \"%s\", invalid response: %s", operationName%mujinjsonwebstack::DumpJson(rResultDoc), MEC_HTTPServer); + } + + // set output + if (returnRawResponse) { + rResult.Swap(rResultDoc); + } else { + rResult = rResultDoc["data"]; + } +} + +std::string WebstackClient::GetVersion() +{ + if (!_profile.IsObject()) { + _profile.SetObject(); + CallGet("profile/", _profile); + } + return GetJsonValueByKey(_profile, "version"); +} + +void WebstackClient::CancelAllJobs() +{ + CallDelete("job/?format=json", 204); +} + +void WebstackClient::DownloadFileFromControllerIfModifiedSince_UTF8(const std::string& desturi, long localtimeval, long& remotetimeval, std::vector& vdata, double timeout) +{ + boost::mutex::scoped_lock lock(_mutex); + _DownloadFileFromController(_PrepareDestinationURI_UTF8(desturi, false), localtimeval, remotetimeval, vdata, timeout); +} + +void WebstackClient::DownloadFileFromControllerIfModifiedSince_UTF16(const std::wstring& desturi, long localtimeval, long& remotetimeval, std::vector& vdata, double timeout) +{ + boost::mutex::scoped_lock lock(_mutex); + _DownloadFileFromController(_PrepareDestinationURI_UTF16(desturi, false), localtimeval, remotetimeval, vdata, timeout); +} + +void WebstackClient::_DownloadFileFromController(const std::string& desturi, long localtimeval, long &remotetimeval, std::vector& outputdata, double timeout) +{ + remotetimeval = 0; + + // ask for remote file time + CURL_OPTION_SAVE_SETTER(_curl, CURLOPT_FILETIME, 0L, 1L); + + // use if modified since if local file time is provided + CURL_OPTION_SAVE_SETTER(_curl, CURLOPT_TIMECONDITION, CURL_TIMECOND_NONE, localtimeval > 0 ? CURL_TIMECOND_IFMODSINCE : CURL_TIMECOND_NONE); + CURL_OPTION_SAVE_SETTER(_curl, CURLOPT_TIMEVALUE, 0L, localtimeval > 0 ? localtimeval : 0L); + + // do the get call + long http_code = _CallGet(desturi, outputdata, 0, timeout); + if ((http_code != 200 && http_code != 304)) { + if (outputdata.size() > 0) { + std::stringstream ss; + ss.write((const char*)&outputdata[0], outputdata.size()); + throw MUJIN_EXCEPTION_FORMAT("HTTP GET to '%s' returned HTTP status %s: %s", desturi%http_code%ss.str(), MEC_HTTPServer); + } + throw MUJIN_EXCEPTION_FORMAT("HTTP GET to '%s' returned HTTP status %s", desturi%http_code, MEC_HTTPServer); + } + + // retrieve remote file time + if (http_code != 304) { + // got the entire file so fill in the timestamp of that file + CURL_INFO_GETTER(_curl, CURLINFO_FILETIME, &remotetimeval); + } +} + +void WebstackClient::ModifySceneAddReferenceObjectPK(const std::string &scenepk, const std::string &referenceobjectpk, double timeout) +{ + rapidjson::Document pt, pt2; + rapidjson::Value value; + + pt.SetObject(); + + value.SetString(scenepk.c_str(), pt.GetAllocator()); + pt.AddMember("scenepk", value, pt.GetAllocator()); + + value.SetString(referenceobjectpk.c_str(), pt.GetAllocator()); + pt.AddMember("referenceobjectpk", value, pt.GetAllocator()); + + boost::mutex::scoped_lock lock(_mutex); + _CallPost(_baseuri + "referenceobjectpks/add/", DumpJson(pt), pt2, pt2.GetAllocator(), 200, timeout); +} + +void WebstackClient::ModifySceneRemoveReferenceObjectPK(const std::string &scenepk, const std::string &referenceobjectpk, double timeout) +{ + rapidjson::Document pt, pt2; + rapidjson::Value value; + + pt.SetObject(); + + value.SetString(scenepk.c_str(), pt.GetAllocator()); + pt.AddMember("scenepk", value, pt.GetAllocator()); + + value.SetString(referenceobjectpk.c_str(), pt.GetAllocator()); + pt.AddMember("referenceobjectpk", value, pt.GetAllocator()); + + boost::mutex::scoped_lock lock(_mutex); + _CallPost(_baseuri + "referenceobjectpks/remove/", DumpJson(pt), pt2, pt2.GetAllocator(), 200, timeout); +} + +const std::string& WebstackClient::GetDefaultTaskType() +{ + return _defaulttasktype; +} + +std::string WebstackClient::GetScenePrimaryKeyFromURI_UTF8(const std::string& uri) +{ + size_t index = uri.find(":/"); + if (index == std::string::npos) { + throw MUJIN_EXCEPTION_FORMAT("bad URI: %s", uri, MEC_InvalidArguments); + } + return EscapeString(uri.substr(index+2)); +} + +std::string WebstackClient::GetScenePrimaryKeyFromURI_UTF16(const std::wstring& uri) +{ + std::string utf8line; + utf8::utf16to8(uri.begin(), uri.end(), std::back_inserter(utf8line)); + return GetScenePrimaryKeyFromURI_UTF8(utf8line); +} + +std::string WebstackClient::GetNameFromPrimaryKey_UTF8(const std::string& pk) +{ + return UnescapeString(pk); +} + +std::wstring WebstackClient::GetNameFromPrimaryKey_UTF16(const std::string& pk) +{ + std::string utf8 = GetNameFromPrimaryKey_UTF8(pk); + std::wstring utf16; + utf8::utf8to16(utf8.begin(), utf8.end(), std::back_inserter(utf16)); + return utf16; +} + +std::string WebstackClient::CreateObjectGeometry(const std::string& objectPk, const std::string& geometryName, const std::string& linkPk, const std::string& geomtype, double timeout) +{ + rapidjson::Document pt(rapidjson::kObjectType); + const std::string geometryData("{\"name\":\"" + geometryName + "\", \"linkpk\":\"" + linkPk + "\", \"geomtype\": \"" + geomtype + "\"}"); + const std::string uri(str(boost::format("object/%s/geometry/") % objectPk)); + + CallPost(uri, geometryData, pt, 201, timeout); + return GetJsonValueByKey(pt, "pk"); +} + +std::string WebstackClient::SetObjectGeometryMesh(const std::string& objectPk, const std::string& geometryPk, const std::vector& meshData, const std::string& unit, double timeout) +{ + rapidjson::Document pt(rapidjson::kObjectType); + const std::string uri(str(boost::format("object/%s/geometry/%s/?unit=%s")%objectPk%geometryPk%unit)); + CallPutSTL(uri, meshData, pt, 202, timeout); + return GetJsonValueByKey(pt, "pk"); +} + +int WebstackClient::CallGet(const std::string& relativeuri, rapidjson::Document& pt, int expectedhttpcode, double timeout) +{ + boost::mutex::scoped_lock lock(_mutex); + _uri = _baseapiuri; + _uri += relativeuri; + return _CallGet(_uri, pt, pt.GetAllocator(), expectedhttpcode, timeout); +} + +int WebstackClient::_CallGet(const std::string& desturi, rapidjson::Value& rResult, rapidjson::Document::AllocatorType& alloc, int expectedhttpcode, double timeout) +{ + MUJIN_LOG_INFO(str(boost::format("GET %s")%desturi)); + CURL_OPTION_SAVE_SETTER(_curl, CURLOPT_TIMEOUT_MS, 0L, (long)(timeout * 1000L)); + CURL_OPTION_SAVE_SETTER(_curl, CURLOPT_HTTPHEADER, NULL, _httpheadersjson); + CURL_OPTION_SAVE_SETTER(_curl, CURLOPT_URL, NULL, desturi.c_str()); + _buffer.clear(); + _buffer.str(""); + CURL_OPTION_SAVE_SETTER(_curl, CURLOPT_WRITEFUNCTION, NULL, _WriteStringStreamCallback); + CURL_OPTION_SAVE_SETTER(_curl, CURLOPT_WRITEDATA, NULL, &_buffer); + CURL_OPTION_SAVE_SETTER(_curl, CURLOPT_HTTPGET, 0L, 1L); + CURL_PERFORM(_curl); + long http_code = 0; + CURL_INFO_GETTER(_curl, CURLINFO_RESPONSE_CODE, &http_code); + if( _buffer.rdbuf()->in_avail() > 0 ) { + mujinjsonwebstack::ParseJson(rResult, alloc, _buffer); + } + if( expectedhttpcode != 0 && http_code != expectedhttpcode ) { + std::string error_message = GetJsonValueByKey(rResult, "error_message"); + std::string traceback = GetJsonValueByKey(rResult, "traceback"); + throw MUJIN_EXCEPTION_FORMAT("HTTP GET to '%s' returned HTTP status %s: %s", desturi%http_code%error_message, MEC_HTTPServer); + } + return http_code; +} + +int WebstackClient::CallGet(const std::string& relativeuri, std::string& outputdata, int expectedhttpcode, double timeout) +{ + boost::mutex::scoped_lock lock(_mutex); + _uri = _baseapiuri; + _uri += relativeuri; + return _CallGet(_uri, outputdata, expectedhttpcode, timeout); +} + +int WebstackClient::_CallGet(const std::string& desturi, std::string& outputdata, int expectedhttpcode, double timeout) +{ + MUJIN_LOG_VERBOSE(str(boost::format("GET %s")%desturi)); + CURL_OPTION_SAVE_SETTER(_curl, CURLOPT_TIMEOUT_MS, 0L, (long)(timeout * 1000L)); + CURL_OPTION_SAVE_SETTER(_curl, CURLOPT_HTTPHEADER, NULL, _httpheadersjson); + CURL_OPTION_SAVE_SETTER(_curl, CURLOPT_URL, NULL, desturi.c_str()); + _buffer.clear(); + _buffer.str(""); + CURL_OPTION_SAVE_SETTER(_curl, CURLOPT_WRITEFUNCTION, NULL, _WriteStringStreamCallback); + CURL_OPTION_SAVE_SETTER(_curl, CURLOPT_WRITEDATA, NULL, &_buffer); + CURL_OPTION_SAVE_SETTER(_curl, CURLOPT_HTTPGET, 0L, 1L); + CURL_PERFORM(_curl); + long http_code = 0; + CURL_INFO_GETTER(_curl, CURLINFO_RESPONSE_CODE, &http_code); + outputdata = _buffer.str(); + if( expectedhttpcode != 0 && http_code != expectedhttpcode ) { + if( outputdata.size() > 0 ) { + rapidjson::Document d; + ParseJson(d, _buffer.str()); + std::string error_message = GetJsonValueByKey(d, "error_message"); + std::string traceback = GetJsonValueByKey(d, "traceback"); + throw MUJIN_EXCEPTION_FORMAT("HTTP GET to '%s' returned HTTP status %s: %s", desturi%http_code%error_message, MEC_HTTPServer); + } + throw MUJIN_EXCEPTION_FORMAT("HTTP GET to '%s' returned HTTP status %s", desturi%http_code, MEC_HTTPServer); + } + return http_code; +} + +int WebstackClient::CallGet(const std::string& relativeuri, std::ostream& outputStream, int expectedhttpcode, double timeout) +{ + boost::mutex::scoped_lock lock(_mutex); + _uri = _baseapiuri; + _uri += relativeuri; + return _CallGet(_uri, outputStream, expectedhttpcode, timeout); +} + +int WebstackClient::_CallGet(const std::string& desturi, std::ostream& outputStream, int expectedhttpcode, double timeout) +{ + MUJIN_LOG_VERBOSE(str(boost::format("GET %s")%desturi)); + CURL_OPTION_SAVE_SETTER(_curl, CURLOPT_TIMEOUT_MS, 0L, (long)(timeout * 1000L)); + CURL_OPTION_SAVE_SETTER(_curl, CURLOPT_HTTPHEADER, NULL, _httpheadersjson); + CURL_OPTION_SAVE_SETTER(_curl, CURLOPT_URL, NULL, desturi.c_str()); + CURL_OPTION_SAVE_SETTER(_curl, CURLOPT_WRITEFUNCTION, NULL, _WriteOStreamCallback); + CURL_OPTION_SAVE_SETTER(_curl, CURLOPT_WRITEDATA, NULL, &outputStream); + CURL_OPTION_SAVE_SETTER(_curl, CURLOPT_HTTPGET, 0L, 1L); + CURL_PERFORM(_curl); + long http_code = 0; + CURL_INFO_GETTER(_curl, CURLINFO_RESPONSE_CODE, &http_code); + if( expectedhttpcode != 0 && http_code != expectedhttpcode ) { + // outputStream is not always seekable; ignore any error message. + throw MUJIN_EXCEPTION_FORMAT("HTTP GET to '%s' returned HTTP status %s (outputStream might have information)", desturi%http_code, MEC_HTTPServer); + } + return http_code; +} + +int WebstackClient::CallGet(const std::string& relativeuri, std::vector& outputdata, int expectedhttpcode, double timeout) +{ + boost::mutex::scoped_lock lock(_mutex); + _uri = _baseapiuri; + _uri += relativeuri; + return _CallGet(_uri, outputdata, expectedhttpcode, timeout); +} + +int WebstackClient::_CallGet(const std::string& desturi, std::vector& outputdata, int expectedhttpcode, double timeout) +{ + MUJIN_LOG_VERBOSE(str(boost::format("GET %s")%desturi)); + CURL_OPTION_SAVE_SETTER(_curl, CURLOPT_TIMEOUT_MS, 0L, (long)(timeout * 1000L)); + CURL_OPTION_SAVE_SETTER(_curl, CURLOPT_HTTPHEADER, NULL, _httpheadersjson); + CURL_OPTION_SAVE_SETTER(_curl, CURLOPT_URL, NULL, desturi.c_str()); + + CURL_OPTION_SAVE_SETTER(_curl, CURLOPT_WRITEFUNCTION, NULL, _WriteVectorCallback); + outputdata.resize(0); + CURL_OPTION_SAVE_SETTER(_curl, CURLOPT_WRITEDATA, NULL, &outputdata); + + CURL_OPTION_SAVE_SETTER(_curl, CURLOPT_HTTPGET, 0L, 1L); + CURL_PERFORM(_curl); + long http_code = 0; + CURL_INFO_GETTER(_curl, CURLINFO_RESPONSE_CODE, &http_code); + if( expectedhttpcode != 0 && http_code != expectedhttpcode ) { + if( outputdata.size() > 0 ) { + rapidjson::Document d; + std::stringstream ss; + ss.write((const char*)&outputdata[0], outputdata.size()); + ParseJson(d, ss.str()); + std::string error_message = GetJsonValueByKey(d, "error_message"); + std::string traceback = GetJsonValueByKey(d, "traceback"); + throw MUJIN_EXCEPTION_FORMAT("HTTP GET to '%s' returned HTTP status %s: %s", desturi%http_code%error_message, MEC_HTTPServer); + } + throw MUJIN_EXCEPTION_FORMAT("HTTP GET to '%s' returned HTTP status %s", desturi%http_code, MEC_HTTPServer); + } + return http_code; +} + +int WebstackClient::CallPost(const std::string& relativeuri, const std::string& data, rapidjson::Document& pt, int expectedhttpcode, double timeout) +{ + MUJIN_LOG_DEBUG(str(boost::format("POST %s%s")%_baseapiuri%relativeuri)); + boost::mutex::scoped_lock lock(_mutex); + _uri = _baseapiuri; + _uri += relativeuri; + return _CallPost(_uri, data, pt, pt.GetAllocator(), expectedhttpcode, timeout); +} + +/// \brief expectedhttpcode is not 0, then will check with the returned http code and if not equal will throw an exception +int WebstackClient::_CallPost(const std::string& desturi, const std::string& data, rapidjson::Value& rResult, rapidjson::Document::AllocatorType& alloc, int expectedhttpcode, double timeout) +{ + MUJIN_LOG_VERBOSE(str(boost::format("POST %s")%desturi)); + CURL_OPTION_SAVE_SETTER(_curl, CURLOPT_TIMEOUT_MS, 0L, (long)(timeout * 1000L)); + CURL_OPTION_SAVE_SETTER(_curl, CURLOPT_HTTPHEADER, NULL, _httpheadersjson); + CURL_OPTION_SAVE_SETTER(_curl, CURLOPT_URL, NULL, desturi.c_str()); + _buffer.clear(); + _buffer.str(""); + CURL_OPTION_SAVE_SETTER(_curl, CURLOPT_WRITEFUNCTION, NULL, _WriteStringStreamCallback); + CURL_OPTION_SAVE_SETTER(_curl, CURLOPT_WRITEDATA, NULL, &_buffer); + CURL_OPTION_SAVE_SETTER(_curl, CURLOPT_POST, 0L, 1L); + CURL_OPTION_SAVE_SETTER(_curl, CURLOPT_POSTFIELDSIZE, 0, data.size()); + CURL_OPTION_SAVE_SETTER(_curl, CURLOPT_POSTFIELDS, NULL, data.size() > 0 ? data.c_str() : NULL); + CURL_PERFORM(_curl); + long http_code = 0; + CURL_INFO_GETTER(_curl, CURLINFO_RESPONSE_CODE, &http_code); + if( _buffer.rdbuf()->in_avail() > 0 ) { + ParseJson(rResult, alloc, _buffer); + } else { + rResult.SetObject(); + } + if( expectedhttpcode != 0 && http_code != expectedhttpcode ) { + std::string error_message = GetJsonValueByKey(rResult, "error_message"); + std::string traceback = GetJsonValueByKey(rResult, "traceback"); + throw MUJIN_EXCEPTION_FORMAT("HTTP POST to '%s' returned HTTP status %s: %s", desturi%http_code%error_message, MEC_HTTPServer); + } + return http_code; +} + +int WebstackClient::CallPost_UTF8(const std::string& relativeuri, const std::string& data, rapidjson::Document& pt, int expectedhttpcode, double timeout) +{ + return CallPost(relativeuri, encoding::ConvertUTF8ToFileSystemEncoding(data), pt, expectedhttpcode, timeout); +} + +int WebstackClient::CallPost_UTF16(const std::string& relativeuri, const std::wstring& data, rapidjson::Document& pt, int expectedhttpcode, double timeout) +{ + return CallPost(relativeuri, encoding::ConvertUTF16ToFileSystemEncoding(data), pt, expectedhttpcode, timeout); +} + +int WebstackClient::CallPutJSON(const std::string& relativeuri, const std::string& data, rapidjson::Document& pt, int expectedhttpcode, double timeout) +{ + return _CallPut(relativeuri, static_cast(&data[0]), data.size(), pt, _httpheadersjson, expectedhttpcode, timeout); +} + +int WebstackClient::CallPutSTL(const std::string& relativeuri, const std::vector& data, rapidjson::Document& pt, int expectedhttpcode, double timeout) +{ + return _CallPut(relativeuri, static_cast (&data[0]), data.size(), pt, _httpheadersstl, expectedhttpcode, timeout); +} + +int WebstackClient::_CallPut(const std::string& relativeuri, const void* pdata, size_t nDataSize, rapidjson::Document& pt, curl_slist* headers, int expectedhttpcode, double timeout) +{ + MUJIN_LOG_DEBUG(str(boost::format("PUT %s%s")%_baseapiuri%relativeuri)); + boost::mutex::scoped_lock lock(_mutex); + CURL_OPTION_SAVE_SETTER(_curl, CURLOPT_HTTPHEADER, NULL, headers); + CURL_OPTION_SAVE_SETTER(_curl, CURLOPT_TIMEOUT_MS, 0L, (long)(timeout * 1000L)); + _uri = _baseapiuri; + _uri += relativeuri; + CURL_OPTION_SAVE_SETTER(_curl, CURLOPT_URL, NULL, _uri.c_str()); + _buffer.clear(); + _buffer.str(""); + CURL_OPTION_SAVE_SETTER(_curl, CURLOPT_WRITEFUNCTION, NULL, _WriteStringStreamCallback); + CURL_OPTION_SAVE_SETTER(_curl, CURLOPT_WRITEDATA, NULL, &_buffer); + CURL_OPTION_SAVE_SETTER(_curl, CURLOPT_CUSTOMREQUEST, NULL, "PUT"); + CURL_OPTION_SAVE_SETTER(_curl, CURLOPT_POSTFIELDSIZE, 0, nDataSize); + CURL_OPTION_SAVE_SETTER(_curl, CURLOPT_POSTFIELDS, NULL, pdata); + CURL_PERFORM(_curl); + long http_code = 0; + CURL_INFO_GETTER(_curl, CURLINFO_RESPONSE_CODE, &http_code); + if( _buffer.rdbuf()->in_avail() > 0 ) { + ParseJson(pt, _buffer.str()); + } else { + pt.SetObject(); + } + if( expectedhttpcode != 0 && http_code != expectedhttpcode ) { + std::string error_message = GetJsonValueByKey(pt, "error_message"); + std::string traceback = GetJsonValueByKey(pt, "traceback"); + throw MUJIN_EXCEPTION_FORMAT("HTTP PUT to '%s' returned HTTP status %s: %s", relativeuri%http_code%error_message, MEC_HTTPServer); + } + return http_code; +} + +void WebstackClient::CallDelete(const std::string& relativeuri, int expectedhttpcode, double timeout) +{ + MUJIN_LOG_DEBUG(str(boost::format("DELETE %s%s")%_baseapiuri%relativeuri)); + boost::mutex::scoped_lock lock(_mutex); + CURL_OPTION_SAVE_SETTER(_curl, CURLOPT_TIMEOUT_MS, 0L, (long)(timeout * 1000L)); + _uri = _baseapiuri; + _uri += relativeuri; + CURL_OPTION_SAVE_SETTER(_curl, CURLOPT_HTTPHEADER, NULL, _httpheadersjson); + CURL_OPTION_SAVE_SETTER(_curl, CURLOPT_URL, NULL, _uri.c_str()); + CURL_OPTION_SAVE_SETTER(_curl, CURLOPT_CUSTOMREQUEST, NULL, "DELETE"); + _buffer.clear(); + _buffer.str(""); + CURL_OPTION_SAVE_SETTER(_curl, CURLOPT_WRITEFUNCTION, NULL, _WriteStringStreamCallback); + CURL_OPTION_SAVE_SETTER(_curl, CURLOPT_WRITEDATA, NULL, &_buffer); + CURL_PERFORM(_curl); + long http_code = 0; + CURL_INFO_GETTER(_curl, CURLINFO_RESPONSE_CODE, &http_code); + if( http_code != expectedhttpcode ) { + rapidjson::Document d; + ParseJson(d, _buffer.str()); + std::string error_message = GetJsonValueByKey(d, "error_message"); + std::string traceback = GetJsonValueByKey(d, "traceback"); + throw MUJIN_EXCEPTION_FORMAT("HTTP DELETE to '%s' returned HTTP status %s: %s", relativeuri%http_code%error_message, MEC_HTTPServer); + } +} + +std::stringstream& WebstackClient::GetBuffer() +{ + return _buffer; +} + +int WebstackClient::_WriteStringStreamCallback(char *data, size_t size, size_t nmemb, std::stringstream *writerData) +{ + if (writerData == NULL) { + return 0; + } + writerData->write(data, size*nmemb); + return size * nmemb; +} + +int WebstackClient::_WriteOStreamCallback(char *data, size_t size, size_t nmemb, std::ostream *writerData) +{ + if (writerData == NULL) { + return 0; + } + writerData->write(data, size*nmemb); + return size * nmemb; +} + +int WebstackClient::_WriteVectorCallback(char *data, size_t size, size_t nmemb, std::vector *writerData) +{ + if (writerData == NULL) { + return 0; + } + writerData->insert(writerData->end(), data, data+size*nmemb); + return size * nmemb; +} + +int WebstackClient::_ReadIStreamCallback(char *data, size_t size, size_t nmemb, std::istream *readerData) +{ + if (readerData == NULL) { + return 0; + } + return readerData->read(data, size*nmemb).gcount(); +} + +void WebstackClient::_SetupHTTPHeadersJSON() +{ + // set the header to only send json + std::string s = std::string("Content-Type: application/json; charset=") + _charset; + if( !!_httpheadersjson ) { + curl_slist_free_all(_httpheadersjson); + } + _httpheadersjson = curl_slist_append(NULL, s.c_str()); + s = str(boost::format("Accept-Language: %s,en-us")%_language); + _httpheadersjson = curl_slist_append(_httpheadersjson, s.c_str()); //,en;q=0.7,ja;q=0.3',") + s = str(boost::format("Accept-Charset: %s")%_charset); + _httpheadersjson = curl_slist_append(_httpheadersjson, s.c_str()); + //_httpheadersjson = curl_slist_append(_httpheadersjson, "Accept:"); // necessary? + s = std::string("X-CSRFToken: ")+_csrfmiddlewaretoken; + _httpheadersjson = curl_slist_append(_httpheadersjson, s.c_str()); + _httpheadersjson = curl_slist_append(_httpheadersjson, "Connection: Keep-Alive"); + _httpheadersjson = curl_slist_append(_httpheadersjson, "Keep-Alive: 20"); // keep alive for 20s? + // test on windows first + //_httpheadersjson = curl_slist_append(_httpheadersjson, "Accept-Encoding: gzip, deflate"); + for (const std::string& additionalHeader : _clientInfo.additionalHeaders) { + _httpheadersjson = curl_slist_append(_httpheadersjson, additionalHeader.c_str()); + } +} + +void WebstackClient::_SetupHTTPHeadersSTL() +{ + // set the header to only send stl + std::string s = std::string("Content-Type: application/sla"); + if( !!_httpheadersstl ) { + curl_slist_free_all(_httpheadersstl); + } + _httpheadersstl = curl_slist_append(NULL, s.c_str()); + //_httpheadersstl = curl_slist_append(_httpheadersstl, "Accept:"); // necessary? + s = std::string("X-CSRFToken: ")+_csrfmiddlewaretoken; + _httpheadersstl = curl_slist_append(_httpheadersstl, s.c_str()); + _httpheadersstl = curl_slist_append(_httpheadersstl, "Connection: Keep-Alive"); + _httpheadersstl = curl_slist_append(_httpheadersstl, "Keep-Alive: 20"); // keep alive for 20s? + // test on windows first + //_httpheadersstl = curl_slist_append(_httpheadersstl, "Accept-Encoding: gzip, deflate"); + for (const std::string& additionalHeader : _clientInfo.additionalHeaders) { + _httpheadersstl = curl_slist_append(_httpheadersstl, additionalHeader.c_str()); + } +} + +void WebstackClient::_SetupHTTPHeadersMultipartFormData() +{ + // set the header to only send stl + std::string s = std::string("Content-Type: multipart/form-data"); + if( !!_httpheadersmultipartformdata ) { + curl_slist_free_all(_httpheadersmultipartformdata); + } + _httpheadersmultipartformdata = curl_slist_append(NULL, s.c_str()); + //_httpheadersmultipartformdata = curl_slist_append(_httpheadersmultipartformdata, "Accept:"); // necessary? + s = std::string("X-CSRFToken: ")+_csrfmiddlewaretoken; + _httpheadersmultipartformdata = curl_slist_append(_httpheadersmultipartformdata, s.c_str()); + _httpheadersmultipartformdata = curl_slist_append(_httpheadersmultipartformdata, "Connection: Keep-Alive"); + _httpheadersmultipartformdata = curl_slist_append(_httpheadersmultipartformdata, "Keep-Alive: 20"); // keep alive for 20s? + // test on windows first + //_httpheadersmultipartformdata = curl_slist_append(_httpheadersmultipartformdata, "Accept-Encoding: gzip, deflate"); + for (const std::string& additionalHeader : _clientInfo.additionalHeaders) { + _httpheadersmultipartformdata = curl_slist_append(_httpheadersmultipartformdata, additionalHeader.c_str()); + } +} + +std::string WebstackClient::_PrepareDestinationURI_UTF8(const std::string& rawuri, bool bEnsurePath, bool bEnsureSlash, bool bIsDirectory) +{ + std::string baseuploaduri; + if( rawuri.size() >= 7 && rawuri.substr(0,7) == "mujin:/" ) { + baseuploaduri = _basewebdavuri; + std::string s = rawuri.substr(7); + baseuploaduri += _EncodeWithoutSeparator(s); + if( bEnsurePath ) { + if( !bIsDirectory ) { + size_t nBaseFilenameStartIndex = s.find_last_of(s_filesep); + if( nBaseFilenameStartIndex != std::string::npos ) { + s = s.substr(0, nBaseFilenameStartIndex); + } else { + s = ""; + } + } + _EnsureWebDAVDirectories(s); + } + } + else { + if( !bEnsureSlash ) { + return rawuri; + } + baseuploaduri = rawuri; + } + if( bEnsureSlash ) { + // ensure trailing slash + if( baseuploaduri[baseuploaduri.size()-1] != '/' ) { + baseuploaduri.push_back('/'); + } + } + return baseuploaduri; +} + +std::string WebstackClient::_PrepareDestinationURI_UTF16(const std::wstring& rawuri_utf16, bool bEnsurePath, bool bEnsureSlash, bool bIsDirectory) +{ + std::string baseuploaduri; + std::string desturi_utf8; + utf8::utf16to8(rawuri_utf16.begin(), rawuri_utf16.end(), std::back_inserter(desturi_utf8)); + + if( desturi_utf8.size() >= 7 && desturi_utf8.substr(0,7) == "mujin:/" ) { + baseuploaduri = _basewebdavuri; + std::string s = desturi_utf8.substr(7); + baseuploaduri += _EncodeWithoutSeparator(s); + if( bEnsurePath ) { + if( !bIsDirectory ) { + size_t nBaseFilenameStartIndex = s.find_last_of(s_filesep); + if( nBaseFilenameStartIndex != std::string::npos ) { + s = s.substr(0, nBaseFilenameStartIndex); + } else { + s = ""; + } + } + _EnsureWebDAVDirectories(s); + } + } + else { + if( !bEnsureSlash ) { + return desturi_utf8; + } + baseuploaduri = desturi_utf8; + } + if( bEnsureSlash ) { + // ensure trailing slash + if( baseuploaduri[baseuploaduri.size()-1] != '/' ) { + baseuploaduri.push_back('/'); + } + } + return baseuploaduri; +} + +std::string WebstackClient::_EncodeWithoutSeparator(const std::string& raw) +{ + std::string output; + size_t startindex = 0; + for(size_t i = 0; i < raw.size(); ++i) { + if( raw[i] == '/' ) { + if( startindex != i ) { + output += EscapeString(raw.substr(startindex, i-startindex)); + startindex = i+1; + } + output += '/'; + } + } + if( startindex != raw.size() ) { + output += EscapeString(raw.substr(startindex)); + } + return output; +} + +void WebstackClient::_EnsureWebDAVDirectories(const std::string& relativeuri, double timeout) +{ + if (relativeuri.empty()) { + return; + } + + CURL_OPTION_SAVE_SETTER(_curl, CURLOPT_TIMEOUT_MS, 0L, (long)(timeout * 1000L)); + std::list listCreateDirs; + std::string output; + size_t startindex = 0; + for(size_t i = 0; i < relativeuri.size(); ++i) { + if( relativeuri[i] == '/' ) { + if( startindex != i ) { + listCreateDirs.push_back(EscapeString(relativeuri.substr(startindex, i-startindex))); + startindex = i+1; + } + } + } + if( startindex != relativeuri.size() ) { + listCreateDirs.push_back(EscapeString(relativeuri.substr(startindex))); + } + + CURL_OPTION_SAVE_SETTER(_curl, CURLOPT_WRITEFUNCTION, NULL, _WriteStringStreamCallback); + CURL_OPTION_SAVE_SETTER(_curl, CURLOPT_WRITEDATA, NULL, &_buffer); + CURL_OPTION_SAVE_SETTER(_curl, CURLOPT_CUSTOMREQUEST, NULL, "MKCOL"); + + std::string totaluri = ""; + for(std::list::iterator itdir = listCreateDirs.begin(); itdir != listCreateDirs.end(); ++itdir) { + // first have to create the directory structure up to destinationdir + if( totaluri.size() > 0 ) { + totaluri += '/'; + } + totaluri += *itdir; + _uri = _basewebdavuri + totaluri; + CURL_OPTION_SAVE_SETTER(_curl, CURLOPT_HTTPHEADER, NULL, _httpheadersjson); + CURL_OPTION_SAVE_SETTER(_curl, CURLOPT_URL, NULL, _uri.c_str()); + _buffer.clear(); + _buffer.str(""); + CURL_PERFORM(_curl); + long http_code = 0; + CURL_INFO_GETTER(_curl, CURLINFO_RESPONSE_CODE, &http_code); + /* creating directories + + Responses from a MKCOL request MUST NOT be cached as MKCOL has non-idempotent semantics. + + 201 (Created) - The collection or structured resource was created in its entirety. + + 403 (Forbidden) - This indicates at least one of two conditions: 1) the server does not allow the creation of collections at the given location in its namespace, or 2) the parent collection of the Request-URI exists but cannot accept members. + + 405 (Method Not Allowed) - MKCOL can only be executed on a deleted/non-existent resource. + + 409 (Conflict) - A collection cannot be made at the Request-URI until one or more intermediate collections have been created. + + 415 (Unsupported Media Type)- The server does not support the request type of the body. + + 507 (Insufficient Storage) - The resource does not have sufficient space to record the state of the resource after the execution of this method. + + */ + if( http_code != 201 && http_code != 301 ) { + throw MUJIN_EXCEPTION_FORMAT("HTTP MKCOL failed with HTTP status %d: %s", http_code%_errormessage, MEC_HTTPServer); + } + } +} + +// static +MUJINWEBSTACKCLIENT_API WebstackClientPtr WebstackClient::CreateWebstackClient(const std::string& usernamepassword, const std::string& url, const std::string& proxyserverport, const std::string& proxyuserpw, int options, double timeout) +{ + return boost::make_shared(usernamepassword, url, proxyserverport, proxyuserpw, options, timeout); +} + +} // end namespace mujinwebstackclient