mirror of
https://github.com/OrcaSlicer/OrcaSlicer.git
synced 2026-09-17 05:52:39 +00:00
Merge: Snapmaker Orca 2.1.2
This commit is contained in:
+63
-56
@@ -1,5 +1,5 @@
|
||||
cmake_minimum_required(VERSION 3.13)
|
||||
project(OrcaSlicer-native)
|
||||
project(Snapmaker_Orca-native)
|
||||
|
||||
set(CMAKE_CXX_STANDARD 17)
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||
@@ -9,6 +9,13 @@ add_subdirectory(dev-utils)
|
||||
# add_subdirectory(avrdude)
|
||||
# Note: semver and hints are now included from deps_src/CMakeLists.txt
|
||||
|
||||
# Add MQTT library
|
||||
add_subdirectory(mqtt)
|
||||
|
||||
# Set MQTT paths for use in slic3r GUI
|
||||
set(PAHO_MQTT_CPP_PATH "${CMAKE_CURRENT_SOURCE_DIR}/mqtt" CACHE PATH "Paho MQTT C++ path")
|
||||
set(PAHO_MQTT_C_PATH "${CMAKE_CURRENT_SOURCE_DIR}/mqtt/externals/paho-mqtt-c" CACHE PATH "Paho MQTT C path")
|
||||
|
||||
add_subdirectory(libslic3r)
|
||||
|
||||
if (SLIC3R_GUI)
|
||||
@@ -80,84 +87,84 @@ if (SLIC3R_GUI)
|
||||
endif()
|
||||
|
||||
if(ORCA_TOOLS)
|
||||
# OrcaSlicer_profile_validator
|
||||
# Snapmaker_Orca_profile_validator
|
||||
if(APPLE)
|
||||
add_executable(OrcaSlicer_profile_validator MACOSX_BUNDLE dev-utils/OrcaSlicer_profile_validator.cpp)
|
||||
set_target_properties(OrcaSlicer_profile_validator PROPERTIES
|
||||
add_executable(Snapmaker_Orca_profile_validator MACOSX_BUNDLE dev-utils/Snapmaker_Orca_profile_validator.cpp)
|
||||
set_target_properties(Snapmaker_Orca_profile_validator PROPERTIES
|
||||
MACOSX_BUNDLE TRUE
|
||||
MACOSX_BUNDLE_BUNDLE_NAME "OrcaSlicer Profile Validator"
|
||||
MACOSX_BUNDLE_BUNDLE_NAME "Snapmaker_Orca Profile Validator"
|
||||
MACOSX_BUNDLE_BUNDLE_VERSION "${SLIC3R_VERSION}"
|
||||
MACOSX_BUNDLE_SHORT_VERSION_STRING "${SLIC3R_VERSION}"
|
||||
MACOSX_BUNDLE_IDENTIFIER "com.softfever.orcaslicer.profile-validator"
|
||||
MACOSX_BUNDLE_IDENTIFIER "com.softfever.Snapmaker_Orca.profile-validator"
|
||||
MACOSX_BUNDLE_COPYRIGHT "© 2024 SoftFever"
|
||||
MACOSX_BUNDLE_GUI_IDENTIFIER "com.softfever.orcaslicer.profile-validator"
|
||||
MACOSX_BUNDLE_GUI_IDENTIFIER "com.softfever.Snapmaker_Orca.profile-validator"
|
||||
)
|
||||
else()
|
||||
add_executable(OrcaSlicer_profile_validator dev-utils/OrcaSlicer_profile_validator.cpp)
|
||||
add_executable(Snapmaker_Orca_profile_validator dev-utils/Snapmaker_Orca_profile_validator.cpp)
|
||||
endif()
|
||||
target_link_libraries(OrcaSlicer_profile_validator libslic3r boost_headeronly libcurl OpenSSL::SSL OpenSSL::Crypto)
|
||||
target_compile_definitions(OrcaSlicer_profile_validator PRIVATE -DBOOST_ALL_NO_LIB -DBOOST_USE_WINAPI_VERSION=0x602 -DBOOST_SYSTEM_USE_UTF8)
|
||||
target_link_libraries(Snapmaker_Orca_profile_validator libslic3r boost_headeronly libcurl OpenSSL::SSL OpenSSL::Crypto)
|
||||
target_compile_definitions(Snapmaker_Orca_profile_validator PRIVATE -DBOOST_ALL_NO_LIB -DBOOST_USE_WINAPI_VERSION=0x602 -DBOOST_SYSTEM_USE_UTF8)
|
||||
if(WIN32)
|
||||
target_link_libraries(OrcaSlicer_profile_validator bcrypt.lib)
|
||||
target_link_libraries(Snapmaker_Orca_profile_validator bcrypt.lib)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# Create a slic3r executable
|
||||
# Process mainfests for various platforms.
|
||||
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/dev-utils/platform/msw/OrcaSlicer.rc.in ${CMAKE_CURRENT_BINARY_DIR}/OrcaSlicer.rc @ONLY)
|
||||
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/dev-utils/platform/msw/OrcaSlicer.manifest.in ${CMAKE_CURRENT_BINARY_DIR}/OrcaSlicer.manifest @ONLY)
|
||||
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/dev-utils/platform/msw/Snapmaker_Orca.rc.in ${CMAKE_CURRENT_BINARY_DIR}/Snapmaker_Orca.rc @ONLY)
|
||||
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/dev-utils/platform/msw/Snapmaker_Orca.manifest.in ${CMAKE_CURRENT_BINARY_DIR}/Snapmaker_Orca.manifest @ONLY)
|
||||
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/dev-utils/platform/osx/Info.plist.in ${CMAKE_CURRENT_BINARY_DIR}/Info.plist @ONLY)
|
||||
if (WIN32)
|
||||
add_library(OrcaSlicer SHARED OrcaSlicer.cpp OrcaSlicer.hpp dev-utils/BaseException.cpp dev-utils/BaseException.h dev-utils/StackWalker.cpp dev-utils/StackWalker.h)
|
||||
add_library(Snapmaker_Orca SHARED Snapmaker_Orca.cpp Snapmaker_Orca.hpp dev-utils/BaseException.cpp dev-utils/BaseException.h dev-utils/StackWalker.cpp dev-utils/StackWalker.h)
|
||||
else ()
|
||||
add_executable(OrcaSlicer OrcaSlicer.cpp OrcaSlicer.hpp)
|
||||
add_executable(Snapmaker_Orca Snapmaker_Orca.cpp Snapmaker_Orca.hpp)
|
||||
endif ()
|
||||
|
||||
if (MINGW)
|
||||
target_link_options(OrcaSlicer PUBLIC "-Wl,-allow-multiple-definition")
|
||||
set_target_properties(OrcaSlicer PROPERTIES PREFIX "")
|
||||
target_link_options(Snapmaker_Orca PUBLIC "-Wl,-allow-multiple-definition")
|
||||
set_target_properties(Snapmaker_Orca PROPERTIES PREFIX "")
|
||||
endif (MINGW)
|
||||
|
||||
if (NOT WIN32 AND NOT APPLE)
|
||||
# Binary name on unix like systems (Linux, Unix)
|
||||
set_target_properties(OrcaSlicer PROPERTIES OUTPUT_NAME "orca-slicer")
|
||||
set(SLIC3R_APP_CMD "orca-slicer")
|
||||
set_target_properties(Snapmaker_Orca PROPERTIES OUTPUT_NAME "snapmaker-orca")
|
||||
set(SLIC3R_APP_CMD "snapmaker-orca")
|
||||
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/dev-utils/platform/unix/build_linux_image.sh.in ${CMAKE_CURRENT_BINARY_DIR}/build_linux_image.sh USE_SOURCE_PERMISSIONS @ONLY)
|
||||
endif ()
|
||||
|
||||
target_link_libraries(OrcaSlicer libslic3r cereal::cereal)
|
||||
target_link_libraries(Snapmaker_Orca libslic3r cereal::cereal)
|
||||
# target_include_directories(OrcaSlicer PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/slic3r)
|
||||
if (APPLE)
|
||||
# add_compile_options(-stdlib=libc++)
|
||||
# add_definitions(-DBOOST_THREAD_DONT_USE_CHRONO -DBOOST_NO_CXX11_RVALUE_REFERENCES -DBOOST_THREAD_USES_MOVE)
|
||||
# -liconv: boost links to libiconv by default
|
||||
target_link_libraries(OrcaSlicer "-liconv -framework IOKit" "-framework CoreFoundation" "-framework AVFoundation" "-framework AVKit" "-framework CoreMedia" "-framework VideoToolbox" -lc++)
|
||||
target_link_libraries(Snapmaker_Orca "-liconv -framework IOKit" "-framework CoreFoundation" "-framework AVFoundation" "-framework AVKit" "-framework CoreMedia" "-framework VideoToolbox" -lc++)
|
||||
elseif (MSVC)
|
||||
# Manifest is provided through OrcaSlicer.rc, don't generate your own.
|
||||
# Manifest is provided through Snapmaker_Orca.rc, don't generate your own.
|
||||
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} /MANIFEST:NO")
|
||||
else ()
|
||||
# Boost on Raspberry-Pi does not link to pthreads explicitely.
|
||||
target_link_libraries(OrcaSlicer ${CMAKE_DL_LIBS} -lstdc++ Threads::Threads pangoft2-1.0)
|
||||
target_link_libraries(Snapmaker_Orca ${CMAKE_DL_LIBS} -lstdc++ Threads::Threads pangoft2-1.0)
|
||||
endif ()
|
||||
|
||||
# Add the Slic3r GUI library, libcurl, OpenGL and GLU libraries.
|
||||
if (SLIC3R_GUI)
|
||||
# target_link_libraries(OrcaSlicer ws2_32 uxtheme setupapi libslic3r_gui ${wxWidgets_LIBRARIES})
|
||||
target_link_libraries(OrcaSlicer libslic3r_gui)
|
||||
# target_link_libraries(Snapmaker_Orca ws2_32 uxtheme setupapi libslic3r_gui ${wxWidgets_LIBRARIES})
|
||||
target_link_libraries(Snapmaker_Orca libslic3r_gui)
|
||||
if (MSVC)
|
||||
# Generate debug symbols even in release mode.
|
||||
target_link_options(OrcaSlicer PUBLIC "$<$<CONFIG:RELEASE>:/DEBUG>")
|
||||
target_link_libraries(OrcaSlicer user32.lib Setupapi.lib)
|
||||
target_link_options(Snapmaker_Orca PUBLIC "$<$<CONFIG:RELEASE>:/DEBUG>")
|
||||
target_link_libraries(Snapmaker_Orca user32.lib Setupapi.lib)
|
||||
elseif (MINGW)
|
||||
target_link_libraries(OrcaSlicer ws2_32 uxtheme setupapi)
|
||||
target_link_libraries(Snapmaker_Orca ws2_32 uxtheme setupapi)
|
||||
elseif (APPLE)
|
||||
target_link_libraries(OrcaSlicer "-framework OpenGL")
|
||||
target_link_libraries(Snapmaker_Orca "-framework OpenGL")
|
||||
else ()
|
||||
target_link_libraries(OrcaSlicer -ldl)
|
||||
target_link_libraries(Snapmaker_Orca -ldl)
|
||||
endif ()
|
||||
#if (WIN32)
|
||||
# find_library(PSAPI_LIB NAMES Psapi)
|
||||
# target_link_libraries(OrcaSlicer ${PSAPI_LIB})
|
||||
# target_link_libraries(Snapmaker_Orca ${PSAPI_LIB})
|
||||
#endif ()
|
||||
endif ()
|
||||
|
||||
@@ -168,15 +175,15 @@ if (WIN32)
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -municode")
|
||||
endif()
|
||||
|
||||
add_executable(OrcaSlicer_app_gui WIN32 OrcaSlicer_app_msvc.cpp ${CMAKE_CURRENT_BINARY_DIR}/OrcaSlicer.rc)
|
||||
add_executable(Snapmaker_Orca_app_gui WIN32 Snapmaker_Orca_app_msvc.cpp ${CMAKE_CURRENT_BINARY_DIR}/Snapmaker_Orca.rc)
|
||||
# Generate debug symbols even in release mode.
|
||||
if(MSVC)
|
||||
target_link_options(OrcaSlicer_app_gui PUBLIC "$<$<CONFIG:RELEASE>:/DEBUG>")
|
||||
target_link_options(Snapmaker_Orca_app_gui PUBLIC "$<$<CONFIG:RELEASE>:/DEBUG>")
|
||||
endif()
|
||||
target_compile_definitions(OrcaSlicer_app_gui PRIVATE -DSLIC3R_WRAPPER_NOCONSOLE)
|
||||
add_dependencies(OrcaSlicer_app_gui OrcaSlicer)
|
||||
set_target_properties(OrcaSlicer_app_gui PROPERTIES OUTPUT_NAME "orca-slicer")
|
||||
target_link_libraries(OrcaSlicer_app_gui PRIVATE boost_headeronly)
|
||||
target_compile_definitions(Snapmaker_Orca_app_gui PRIVATE -DSLIC3R_WRAPPER_NOCONSOLE)
|
||||
add_dependencies(Snapmaker_Orca_app_gui Snapmaker_Orca)
|
||||
set_target_properties(Snapmaker_Orca_app_gui PROPERTIES OUTPUT_NAME "snapmaker-orca")
|
||||
target_link_libraries(Snapmaker_Orca_app_gui PRIVATE boost_headeronly)
|
||||
endif ()
|
||||
|
||||
# Link the resources dir to where Slic3r GUI expects it
|
||||
@@ -185,13 +192,13 @@ set(output_dlls_Debug "")
|
||||
set(output_dlls_RelWithDebInfo "")
|
||||
if (WIN32)
|
||||
# This has to be a separate target due to the windows command line lenght limits
|
||||
add_custom_target(COPY_DLLS ALL DEPENDS OrcaSlicer)
|
||||
add_custom_target(COPY_DLLS ALL DEPENDS Snapmaker_Orca)
|
||||
|
||||
if (CMAKE_CONFIGURATION_TYPES)
|
||||
foreach (CONF ${CMAKE_CONFIGURATION_TYPES})
|
||||
file(TO_NATIVE_PATH "${CMAKE_CURRENT_BINARY_DIR}/${CONF}" WIN_CONF_OUTPUT_DIR)
|
||||
file(TO_NATIVE_PATH "${CMAKE_CURRENT_BINARY_DIR}/${CONF}/resources" WIN_RESOURCES_SYMLINK)
|
||||
add_custom_command(TARGET OrcaSlicer POST_BUILD
|
||||
add_custom_command(TARGET Snapmaker_Orca POST_BUILD
|
||||
COMMAND if exist "${WIN_CONF_OUTPUT_DIR}" "("
|
||||
if not exist "${WIN_RESOURCES_SYMLINK}" "("
|
||||
mklink /J "${WIN_RESOURCES_SYMLINK}" "${SLIC3R_RESOURCES_DIR_WIN}"
|
||||
@@ -203,15 +210,15 @@ if (WIN32)
|
||||
endforeach ()
|
||||
|
||||
if ("${CMAKE_BUILD_TYPE}" STREQUAL "Debug")
|
||||
orcaslicer_copy_dlls(COPY_DLLS "Debug" "d" output_dlls_Debug)
|
||||
Snapmaker_Orca_copy_dlls(COPY_DLLS "Debug" "d" output_dlls_Debug)
|
||||
elseif("${CMAKE_BUILD_TYPE}" STREQUAL "RelWithDebInfo")
|
||||
orcaslicer_copy_dlls(COPY_DLLS "RelWithDebInfo" "" output_dlls_Release)
|
||||
Snapmaker_Orca_copy_dlls(COPY_DLLS "RelWithDebInfo" "" output_dlls_Release)
|
||||
else()
|
||||
orcaslicer_copy_dlls(COPY_DLLS "Release" "" output_dlls_Release)
|
||||
Snapmaker_Orca_copy_dlls(COPY_DLLS "Release" "" output_dlls_Release)
|
||||
endif()
|
||||
else ()
|
||||
file(TO_NATIVE_PATH "${CMAKE_CURRENT_BINARY_DIR}/resources" WIN_RESOURCES_SYMLINK)
|
||||
add_custom_command(TARGET OrcaSlicer POST_BUILD
|
||||
add_custom_command(TARGET Snapmaker_Orca POST_BUILD
|
||||
COMMAND if not exist "${WIN_RESOURCES_SYMLINK}" "(" mklink /J "${WIN_RESOURCES_SYMLINK}" "${SLIC3R_RESOURCES_DIR_WIN}" ")"
|
||||
COMMENT "Symlinking the resources directory into the build tree"
|
||||
VERBATIM
|
||||
@@ -222,13 +229,13 @@ if (WIN32)
|
||||
else ()
|
||||
if (APPLE AND NOT CMAKE_MACOSX_BUNDLE)
|
||||
# On OSX, the name of the binary matches the name of the Application.
|
||||
add_custom_command(TARGET OrcaSlicer POST_BUILD
|
||||
COMMAND ln -sf OrcaSlicer orca-slicer
|
||||
WORKING_DIRECTORY "$<TARGET_FILE_DIR:OrcaSlicer>"
|
||||
add_custom_command(TARGET Snapmaker_Orca POST_BUILD
|
||||
COMMAND ln -sf Snapmaker_Orca snapmaker-orca
|
||||
WORKING_DIRECTORY "$<TARGET_FILE_DIR:Snapmaker_Orca>"
|
||||
VERBATIM)
|
||||
else ()
|
||||
add_custom_command(TARGET OrcaSlicer POST_BUILD
|
||||
WORKING_DIRECTORY "$<TARGET_FILE_DIR:OrcaSlicer>"
|
||||
add_custom_command(TARGET Snapmaker_Orca POST_BUILD
|
||||
WORKING_DIRECTORY "$<TARGET_FILE_DIR:Snapmaker_Orca>"
|
||||
VERBATIM)
|
||||
endif ()
|
||||
if (XCODE)
|
||||
@@ -240,16 +247,16 @@ else ()
|
||||
endif ()
|
||||
if (CMAKE_MACOSX_BUNDLE)
|
||||
if (CMAKE_CONFIGURATION_TYPES)
|
||||
set(BIN_RESOURCES_DIR "${CMAKE_CURRENT_BINARY_DIR}/$<CONFIG>/OrcaSlicer.app/Contents/Resources")
|
||||
set(BIN_RESOURCES_DIR "${CMAKE_CURRENT_BINARY_DIR}/$<CONFIG>/Snapmaker Orca.app/Contents/Resources")
|
||||
else()
|
||||
set(BIN_RESOURCES_DIR "${CMAKE_CURRENT_BINARY_DIR}/OrcaSlicer.app/Contents/Resources")
|
||||
set(BIN_RESOURCES_DIR "${CMAKE_CURRENT_BINARY_DIR}/Snapmaker Orca.app/Contents/Resources")
|
||||
endif()
|
||||
set(MACOSX_BUNDLE_ICON_FILE Icon.icns)
|
||||
set(MACOSX_BUNDLE_BUNDLE_NAME "OrcaSlicer")
|
||||
set(MACOSX_BUNDLE_SHORT_VERSION_STRING ${SoftFever_VERSION})
|
||||
set(MACOSX_BUNDLE_BUNDLE_NAME "Snapmaker Orca")
|
||||
set(MACOSX_BUNDLE_SHORT_VERSION_STRING ${Snapmaker_VERSION})
|
||||
set(MACOSX_BUNDLE_COPYRIGHT "Copyright(C) 2022-2024 Li Jiang All Rights Reserved")
|
||||
endif()
|
||||
add_custom_command(TARGET OrcaSlicer POST_BUILD
|
||||
add_custom_command(TARGET Snapmaker_Orca POST_BUILD
|
||||
COMMAND ln -sfn "${SLIC3R_RESOURCES_DIR}" "${BIN_RESOURCES_DIR}"
|
||||
COMMENT "Symlinking the resources directory into the build tree"
|
||||
VERBATIM)
|
||||
@@ -269,11 +276,11 @@ endif()
|
||||
message(STATUS "libslic3r-CMAKE_BUILD_TYPE: ${build_type}")
|
||||
message(STATUS "CMAKE_CURRENT_BINARY_DIR: ${CMAKE_CURRENT_BINARY_DIR}")
|
||||
if (WIN32)
|
||||
install(TARGETS OrcaSlicer RUNTIME DESTINATION ".")
|
||||
install(TARGETS Snapmaker_Orca RUNTIME DESTINATION ".")
|
||||
if (MSVC)
|
||||
install(TARGETS OrcaSlicer_app_gui RUNTIME DESTINATION ".")
|
||||
install(TARGETS Snapmaker_Orca_app_gui RUNTIME DESTINATION ".")
|
||||
endif ()
|
||||
install(FILES ${output_dlls_${build_type}} DESTINATION ".")
|
||||
else ()
|
||||
install(TARGETS OrcaSlicer RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}" BUNDLE DESTINATION ${CMAKE_INSTALL_BINDIR})
|
||||
install(TARGETS Snapmaker_Orca RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}" BUNDLE DESTINATION ${CMAKE_INSTALL_BINDIR})
|
||||
endif ()
|
||||
|
||||
@@ -69,7 +69,7 @@ using namespace nlohmann;
|
||||
#include "libslic3r/Orient.hpp"
|
||||
#include "libslic3r/PNGReadWrite.hpp"
|
||||
|
||||
#include "OrcaSlicer.hpp"
|
||||
#include "Snapmaker_Orca.hpp"
|
||||
//BBS: add exception handler for win32
|
||||
#include <wx/stdpaths.h>
|
||||
#ifdef WIN32
|
||||
@@ -126,8 +126,8 @@ std::map<int, std::string> cli_errors = {
|
||||
{CLI_OBJECT_ORIENT_FAILED, "An error occurred when auto-orienting object(s)."},
|
||||
{CLI_MODIFIED_PARAMS_TO_PRINTER, "Found modified parameter in printer preset in the 3mf file, which should not be changed."},
|
||||
{CLI_FILE_VERSION_NOT_SUPPORTED, "Unsupported 3MF version. Please make sure the 3MF file was created with the official version of Bambu Studio, not a beta version."},
|
||||
{CLI_NO_SUITABLE_OBJECTS, "One of the plate is empty or has no object fully inside it. Please check that the 3mf contains no empty plate in Orca Slicer before uploading."},
|
||||
{CLI_VALIDATE_ERROR, "There are some incorrect slicing parameters in the 3mf. Please verify the slicing of all plates in Orca Slicer before uploading."},
|
||||
{CLI_NO_SUITABLE_OBJECTS, "One of the plate is empty or has no object fully inside it. Please check that the 3mf contains no empty plate in Snapmaker Orca before uploading."},
|
||||
{CLI_VALIDATE_ERROR, "There are some incorrect slicing parameters in the 3mf. Please verify the slicing of all plates in Snapmaker Orca before uploading."},
|
||||
{CLI_OBJECTS_PARTLY_INSIDE, "Some objects are located over the boundary of the heated bed."},
|
||||
{CLI_EXPORT_CACHE_DIRECTORY_CREATE_FAILED, "Failed creating directory when exporting cache data."},
|
||||
{CLI_EXPORT_CACHE_WRITE_FAILED, "Failed exporting cache data."},
|
||||
@@ -137,13 +137,13 @@ std::map<int, std::string> cli_errors = {
|
||||
{CLI_SLICING_TIME_EXCEEDS_LIMIT, "Slicing time of a certain plate exceeds the limit. Please simplify the model or use a larger slicing layer height."},
|
||||
{CLI_TRIANGLE_COUNT_EXCEEDS_LIMIT, "Triangle count of single plate exceeds the limit. Please simplify the model and try to upload again."},
|
||||
{CLI_NO_SUITABLE_OBJECTS_AFTER_SKIP, "No printable objects to slice after skipping."},
|
||||
{CLI_FILAMENT_NOT_MATCH_BED_TYPE, "Filaments are not compatible with the plate type. Please verify the slicing of all plates in Orca Slicer before uploading."},
|
||||
{CLI_FILAMENTS_DIFFERENT_TEMP, "The temperature difference of the filaments used is too large. Please verify the slicing of all plates in Orca Slicer before uploading."},
|
||||
{CLI_OBJECT_COLLISION_IN_SEQ_PRINT, "Object conflicts were detected when using print-by-object mode. Please verify the slicing of all plates in Orca Slicer before uploading."},
|
||||
{CLI_OBJECT_COLLISION_IN_LAYER_PRINT, "Object conflicts were detected. Please verify the slicing of all plates in Orca Slicer before uploading."},
|
||||
{CLI_SPIRAL_MODE_INVALID_PARAMS, "Some slicing parameters cannot work with Spiral Vase mode. Please solve the issue in Orca Slicer before uploading."},
|
||||
{CLI_SLICING_ERROR, "Failed slicing the model. Please verify the slicing of all plates on Orca Slicer before uploading."},
|
||||
{CLI_GCODE_PATH_CONFLICTS, " G-code conflicts detected after slicing. Please make sure the 3mf file can be successfully sliced in the latest Orca Slicer."}
|
||||
{CLI_FILAMENT_NOT_MATCH_BED_TYPE, "Filaments are not compatible with the plate type. Please verify the slicing of all plates in Snapmaker Orca before uploading."},
|
||||
{CLI_FILAMENTS_DIFFERENT_TEMP, "The temperature difference of the filaments used is too large. Please verify the slicing of all plates in Snapmaker Orca before uploading."},
|
||||
{CLI_OBJECT_COLLISION_IN_SEQ_PRINT, "Object conflicts were detected when using print-by-object mode. Please verify the slicing of all plates in Snapmaker Orca before uploading."},
|
||||
{CLI_OBJECT_COLLISION_IN_LAYER_PRINT, "Object conflicts were detected. Please verify the slicing of all plates in Snapmaker Orca before uploading."},
|
||||
{CLI_SPIRAL_MODE_INVALID_PARAMS, "Some slicing parameters cannot work with Spiral Vase mode. Please solve the issue in Snapmaker Orca before uploading."},
|
||||
{CLI_SLICING_ERROR, "Failed slicing the model. Please verify the slicing of all plates on Snapmaker Orca before uploading."},
|
||||
{CLI_GCODE_PATH_CONFLICTS, " G-code conflicts detected after slicing. Please make sure the 3mf file can be successfully sliced in the latest Snapmaker Orca."}
|
||||
};
|
||||
|
||||
typedef struct _sliced_plate_info{
|
||||
@@ -1038,7 +1038,7 @@ static void load_downward_settings_list_from_config(std::string config_file, std
|
||||
int CLI::run(int argc, char **argv)
|
||||
{
|
||||
// Mark the main thread for the debugger and for runtime checks.
|
||||
set_current_thread_name("orcaslicer_main");
|
||||
set_current_thread_name("Snapmaker_Orca_main");
|
||||
// Save the thread ID of the main thread.
|
||||
save_main_thread_id();
|
||||
|
||||
@@ -1164,7 +1164,7 @@ int CLI::run(int argc, char **argv)
|
||||
//BBS: remove GCodeViewer as seperate APP logic
|
||||
//params.start_as_gcodeviewer = start_as_gcodeviewer;
|
||||
|
||||
BOOST_LOG_TRIVIAL(info) << "begin to launch OrcaSlicer GUI soon";
|
||||
BOOST_LOG_TRIVIAL(info) << "begin to launch Snapmaker_Orca GUI soon";
|
||||
return Slic3r::GUI::GUI_Run(params);
|
||||
#else // SLIC3R_GUI
|
||||
// No GUI support. Just print out a help.
|
||||
@@ -1184,7 +1184,7 @@ int CLI::run(int argc, char **argv)
|
||||
}
|
||||
|
||||
global_begin_time = (long long)Slic3r::Utils::get_current_time_utc();
|
||||
BOOST_LOG_TRIVIAL(warning) << boost::format("cli mode, Current OrcaSlicer Version %1%")%SoftFever_VERSION;
|
||||
BOOST_LOG_TRIVIAL(warning) << boost::format("cli mode, Current Snapmaker_Orca Version %1%")%SLIC3R_VERSION;
|
||||
|
||||
//BBS: add plate data related logic
|
||||
PlateDataPtrs plate_data_src;
|
||||
@@ -1403,9 +1403,9 @@ int CLI::run(int argc, char **argv)
|
||||
BOOST_LOG_TRIVIAL(info) << "object "<<o->name <<", id :" << o->id().id << ", from bbl 3mf\n";
|
||||
}*/
|
||||
|
||||
Semver cli_ver = *Semver::parse(SoftFever_VERSION);
|
||||
Semver cli_ver = *Semver::parse(SLIC3R_VERSION);
|
||||
if (!allow_newer_file && ((cli_ver.maj() != file_version.maj()) || (cli_ver.min() < file_version.min()))){
|
||||
BOOST_LOG_TRIVIAL(error) << boost::format("Version Check: File Version %1% not supported by current cli version %2%")%file_version.to_string() %SoftFever_VERSION;
|
||||
BOOST_LOG_TRIVIAL(error) << boost::format("Version Check: File Version %1% not supported by current cli version %2%")%file_version.to_string() %SLIC3R_VERSION;
|
||||
record_exit_reson(outfile_dir, CLI_FILE_VERSION_NOT_SUPPORTED, 0, cli_errors[CLI_FILE_VERSION_NOT_SUPPORTED], sliced_info);
|
||||
flush_and_exit(CLI_FILE_VERSION_NOT_SUPPORTED);
|
||||
}
|
||||
@@ -4660,7 +4660,7 @@ int CLI::run(int argc, char **argv)
|
||||
//FIXME check for mixing the FFF / SLA parameters.
|
||||
// or better save fff_print_config vs. sla_print_config
|
||||
//m_print_config.save(m_config.opt_string("save"));
|
||||
m_print_config.save_to_json(m_config.opt_string(opt_key), std::string("project_settings"), std::string("project"), std::string(SoftFever_VERSION));
|
||||
m_print_config.save_to_json(m_config.opt_string(opt_key), std::string("project_settings"), std::string("project"), std::string(Snapmaker_VERSION));
|
||||
} else if (opt_key == "info") {
|
||||
// --info works on unrepaired model
|
||||
for (Model &model : m_models) {
|
||||
@@ -5947,17 +5947,17 @@ bool CLI::setup(int argc, char **argv)
|
||||
detect_platform();
|
||||
|
||||
#ifdef WIN32
|
||||
// Notify user that a blacklisted DLL was injected into OrcaSlicer process (for example Nahimic, see GH #5573).
|
||||
// We hope that if a DLL is being injected into a OrcaSlicer process, it happens at the very start of the application,
|
||||
// Notify user that a blacklisted DLL was injected into Snapmaker_Orca process (for example Nahimic, see GH #5573).
|
||||
// We hope that if a DLL is being injected into a Snapmaker_Orca process, it happens at the very start of the application,
|
||||
// thus we shall detect them now.
|
||||
if (BlacklistedLibraryCheck::get_instance().perform_check()) {
|
||||
std::wstring text = L"Following DLLs have been injected into the OrcaSlicer process:\n\n";
|
||||
std::wstring text = L"Following DLLs have been injected into the Snapmaker Orca process:\n\n";
|
||||
text += BlacklistedLibraryCheck::get_instance().get_blacklisted_string();
|
||||
text += L"\n\n"
|
||||
L"OrcaSlicer is known to not run correctly with these DLLs injected. "
|
||||
L"Snapmaker Orca is known to not run correctly with these DLLs injected. "
|
||||
L"We suggest stopping or uninstalling these services if you experience "
|
||||
L"crashes or unexpected behaviour while using OrcaSlicer.\n"
|
||||
L"For example, ASUS Sonic Studio injects a Nahimic driver, which makes OrcaSlicer "
|
||||
L"crashes or unexpected behaviour while using Snapmaker Orca.\n"
|
||||
L"For example, ASUS Sonic Studio injects a Nahimic driver, which makes Snapmaker Orca "
|
||||
L"to crash on a secondary monitor";
|
||||
MessageBoxW(NULL, text.c_str(), L"Warning"/*L"Incopatible library found"*/, MB_OK);
|
||||
}
|
||||
@@ -6085,9 +6085,9 @@ void CLI::print_help(bool include_print_options, PrinterTechnology printer_techn
|
||||
attach_console_on_demand();
|
||||
|
||||
boost::nowide::cout
|
||||
<< SLIC3R_APP_KEY <<"-"<< SoftFever_VERSION << ":"
|
||||
<< SLIC3R_APP_KEY <<"-"<< SLIC3R_VERSION << ":"
|
||||
<< std::endl
|
||||
<< "Usage: orca-slicer [ OPTIONS ] [ file.3mf/file.stl ... ]" << std::endl
|
||||
<< "Usage: snapmaker-orca [ OPTIONS ] [ file.3mf/file.stl ... ]" << std::endl
|
||||
<< std::endl
|
||||
<< "OPTIONS:" << std::endl;
|
||||
cli_misc_config_def.print_cli_help(boost::nowide::cout, false);
|
||||
@@ -6327,7 +6327,7 @@ LONG WINAPI VectoredExceptionHandler(PEXCEPTION_POINTERS pExceptionInfo)
|
||||
|
||||
#if defined(_MSC_VER) || defined(__MINGW32__)
|
||||
extern "C" {
|
||||
__declspec(dllexport) int __stdcall orcaslicer_main(int argc, wchar_t **argv)
|
||||
__declspec(dllexport) int __stdcall Snapmaker_Orca_main(int argc, wchar_t **argv)
|
||||
{
|
||||
// Convert wchar_t arguments to UTF8.
|
||||
std::vector<std::string> argv_narrow;
|
||||
@@ -53,10 +53,10 @@ public:
|
||||
wc.lpfnWndProc = OpenGLVersionCheck::supports_opengl2_wndproc;
|
||||
wc.hInstance = (HINSTANCE)GetModuleHandle(nullptr);
|
||||
wc.hbrBackground = (HBRUSH)(COLOR_BACKGROUND);
|
||||
wc.lpszClassName = L"OrcaSlicer_opengl_version_check";
|
||||
wc.lpszClassName = L"Snapmaker_Orca_opengl_version_check";
|
||||
wc.style = CS_OWNDC;
|
||||
if (RegisterClass(&wc)) {
|
||||
HWND hwnd = CreateWindowW(wc.lpszClassName, L"OrcaSlicer_opengl_version_check", WS_OVERLAPPEDWINDOW, 0, 0, 640, 480, 0, 0, wc.hInstance, (LPVOID)this);
|
||||
HWND hwnd = CreateWindowW(wc.lpszClassName, L"Snapmaker_Orca_opengl_version_check", WS_OVERLAPPEDWINDOW, 0, 0, 640, 480, 0, 0, wc.hInstance, (LPVOID)this);
|
||||
if (hwnd) {
|
||||
message_pump_exit = false;
|
||||
while (GetMessage(&msg, NULL, 0, 0 ) > 0 && ! message_pump_exit)
|
||||
@@ -204,7 +204,7 @@ bool OpenGLVersionCheck::message_pump_exit = false;
|
||||
|
||||
extern "C" {
|
||||
typedef int (__stdcall *Slic3rMainFunc)(int argc, wchar_t **argv);
|
||||
Slic3rMainFunc orcaslicer_main = nullptr;
|
||||
Slic3rMainFunc Snapmaker_Orca_main = nullptr;
|
||||
}
|
||||
|
||||
extern "C" {
|
||||
@@ -283,28 +283,28 @@ int wmain(int argc, wchar_t **argv)
|
||||
|
||||
wchar_t path_to_slic3r[MAX_PATH + 1] = { 0 };
|
||||
wcscpy(path_to_slic3r, path_to_exe);
|
||||
wcscat(path_to_slic3r, L"OrcaSlicer.dll");
|
||||
wcscat(path_to_slic3r, L"Snapmaker_Orca.dll");
|
||||
// printf("Loading Slic3r library: %S\n", path_to_slic3r);
|
||||
HINSTANCE hInstance_Slic3r = LoadLibraryExW(path_to_slic3r, nullptr, 0);
|
||||
if (hInstance_Slic3r == nullptr) {
|
||||
printf("OrcaSlicer.dll was not loaded, error=%d\n", GetLastError());
|
||||
printf("Snapmaker_Orca.dll was not loaded, error=%d\n", GetLastError());
|
||||
return -1;
|
||||
}
|
||||
|
||||
// resolve function address here
|
||||
orcaslicer_main = (Slic3rMainFunc)GetProcAddress(hInstance_Slic3r,
|
||||
Snapmaker_Orca_main = (Slic3rMainFunc)GetProcAddress(hInstance_Slic3r,
|
||||
#ifdef _WIN64
|
||||
// there is just a single calling conversion, therefore no mangling of the function name.
|
||||
"orcaslicer_main"
|
||||
"Snapmaker_Orca_main"
|
||||
#else // stdcall calling convention declaration
|
||||
"_bambustu_main@8"
|
||||
#endif
|
||||
);
|
||||
if (orcaslicer_main == nullptr) {
|
||||
printf("could not locate the function orcaslicer_main in OrcaSlicer.dll\n");
|
||||
if (Snapmaker_Orca_main == nullptr) {
|
||||
printf("could not locate the function Snapmaker_Orca_main in Snapmaker_Orca.dll\n");
|
||||
return -1;
|
||||
}
|
||||
// argc minus the trailing nullptr of the argv
|
||||
return orcaslicer_main((int)argv_extended.size() - 1, argv_extended.data());
|
||||
return Snapmaker_Orca_main((int)argv_extended.size() - 1, argv_extended.data());
|
||||
}
|
||||
}
|
||||
+3
-3
@@ -6,7 +6,7 @@ PRODUCTVERSION @SLIC3R_VERSION@
|
||||
{
|
||||
BLOCK "040904E4"
|
||||
{
|
||||
VALUE "CompanyName", "SoftFever"
|
||||
VALUE "CompanyName", "Snapmaker"
|
||||
VALUE "FileDescription", "@SLIC3R_APP_NAME@ G-code Viewer"
|
||||
VALUE "FileVersion", "@SLIC3R_BUILD_ID@"
|
||||
VALUE "ProductName", "@SLIC3R_APP_NAME@ G-code Viewer"
|
||||
@@ -21,5 +21,5 @@ PRODUCTVERSION @SLIC3R_VERSION@
|
||||
VALUE "Translation", 0x409, 1252
|
||||
}
|
||||
}
|
||||
2 ICON "@SLIC3R_RESOURCES_DIR@/images/OrcaSlicer-gcodeviewer.ico"
|
||||
1 24 "OrcaSlicer.manifest"
|
||||
2 ICON "@SLIC3R_RESOURCES_DIR@/images/Snapmaker_Orca-gcodeviewer.ico"
|
||||
1 24 "Snapmaker_Orca.manifest"
|
||||
+4
-4
@@ -6,14 +6,14 @@ PRODUCTVERSION @SLIC3R_VERSION@
|
||||
{
|
||||
BLOCK "040904E4"
|
||||
{
|
||||
VALUE "CompanyName", "SoftFever"
|
||||
VALUE "CompanyName", "Snapmaker"
|
||||
VALUE "FileDescription", "@SLIC3R_APP_NAME@"
|
||||
VALUE "FileVersion", "@SLIC3R_BUILD_ID@"
|
||||
VALUE "ProductName", "@SLIC3R_APP_NAME@"
|
||||
VALUE "ProductVersion", "@SLIC3R_BUILD_ID@"
|
||||
VALUE "InternalName", "@SLIC3R_APP_NAME@"
|
||||
VALUE "LegalCopyright", ""
|
||||
VALUE "OriginalFilename", "orca-slicer.exe"
|
||||
VALUE "OriginalFilename", "snapmaker-orca.exe"
|
||||
}
|
||||
}
|
||||
BLOCK "VarFileInfo"
|
||||
@@ -21,5 +21,5 @@ PRODUCTVERSION @SLIC3R_VERSION@
|
||||
VALUE "Translation", 0x409, 1252
|
||||
}
|
||||
}
|
||||
2 ICON "@SLIC3R_RESOURCES_DIR@/images/OrcaSlicer.ico"
|
||||
1 24 "OrcaSlicer.manifest"
|
||||
2 ICON "@SLIC3R_RESOURCES_DIR@/images/Snapmaker_Orca.ico"
|
||||
1 24 "Snapmaker_Orca.manifest"
|
||||
@@ -7,7 +7,7 @@
|
||||
<key>CFBundleGetInfoString</key>
|
||||
<string>@SLIC3R_APP_NAME@ Copyright(C) 2021-2023 Lunkuo All Rights Reserved</string>
|
||||
<key>CFBundleIconFile</key>
|
||||
<string>images/OrcaSlicer.icns</string>
|
||||
<string>images/Snapmaker_Orca.icns</string>
|
||||
<key>CFBundleName</key>
|
||||
<string>@SLIC3R_APP_KEY@</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
@@ -28,11 +28,11 @@
|
||||
<array>
|
||||
<dict>
|
||||
<key>CFBundleURLName</key>
|
||||
<string>orcasliceropen url</string>
|
||||
<string>Snapmaker_Orcaopen url</string>
|
||||
<key>CFBundleURLSchemes</key>
|
||||
<array>
|
||||
<string>orcasliceropen</string>
|
||||
<string>orcaslicer</string>
|
||||
<string>Snapmaker_Orcaopen</string>
|
||||
<string>Snapmaker_Orca</string>
|
||||
</array>
|
||||
</dict>
|
||||
</array>
|
||||
@@ -62,7 +62,7 @@
|
||||
<string>OBJ</string>
|
||||
</array>
|
||||
<key>CFBundleTypeIconFile</key>
|
||||
<string>images/OrcaSlicer.icns</string>
|
||||
<string>images/Snapmaker_Orca.icns</string>
|
||||
<key>CFBundleTypeName</key>
|
||||
<string>STL</string>
|
||||
<key>CFBundleTypeRole</key>
|
||||
@@ -79,7 +79,7 @@
|
||||
<string>AMF</string>
|
||||
</array>
|
||||
<key>CFBundleTypeIconFile</key>
|
||||
<string>images/OrcaSlicer.icns</string>
|
||||
<string>images/Snapmaker_Orca.icns</string>
|
||||
<key>CFBundleTypeName</key>
|
||||
<string>AMF</string>
|
||||
<key>CFBundleTypeRole</key>
|
||||
@@ -96,7 +96,7 @@
|
||||
<string>3MF</string>
|
||||
</array>
|
||||
<key>CFBundleTypeIconFile</key>
|
||||
<string>images/OrcaSlicer.icns</string>
|
||||
<string>images/Snapmaker_Orca.icns</string>
|
||||
<key>CFBundleTypeName</key>
|
||||
<string>3MF</string>
|
||||
<key>CFBundleTypeRole</key>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
[Desktop Entry]
|
||||
Name=Bambu GCode viewer
|
||||
Exec=orca-slicer --gcodeviewer %F
|
||||
Icon=OrcaSlicer-gcodeviewer
|
||||
Exec=snapmaker-orca --gcodeviewer %F
|
||||
Icon=Snapmaker_Orca-gcodeviewer
|
||||
Terminal=false
|
||||
Type=Application
|
||||
MimeType=text/x.gcode;
|
||||
|
||||
+5
-5
@@ -1,12 +1,12 @@
|
||||
[Desktop Entry]
|
||||
Name=OrcaSlicer
|
||||
Name=Snapmaker Orca
|
||||
GenericName=3D Printing Software
|
||||
Icon=OrcaSlicer
|
||||
Exec=orca-slicer %U
|
||||
Icon=Snapmaker_Orca
|
||||
Exec=snapmaker-orca %U
|
||||
Terminal=false
|
||||
Type=Application
|
||||
MimeType=model/stl;model/3mf;application/vnd.ms-3mfdocument;application/prs.wavefront-obj;application/x-amf;x-scheme-handler/orcaslicer;
|
||||
MimeType=model/stl;model/3mf;application/vnd.ms-3mfdocument;application/prs.wavefront-obj;application/x-amf;
|
||||
Categories=Graphics;3DGraphics;Engineering;
|
||||
Keywords=3D;Printing;Slicer;slice;3D;printer;convert;gcode;stl;obj;amf;SLA
|
||||
StartupNotify=false
|
||||
StartupWMClass=orca-slicer
|
||||
StartupWMClass=snapmaker-orca
|
||||
@@ -2,7 +2,7 @@
|
||||
APPIMAGETOOLURL="https://github.com/AppImage/appimagetool/releases/download/continuous/appimagetool-$(uname -m).AppImage"
|
||||
|
||||
|
||||
APP_IMAGE="@SLIC3R_APP_KEY@_Linux_V@SoftFever_VERSION@.AppImage"
|
||||
APP_IMAGE="@SLIC3R_APP_KEY@_Linux_V@Snapmaker_VERSION@.AppImage"
|
||||
|
||||
wget ${APPIMAGETOOLURL} -O ../appimagetool.AppImage
|
||||
chmod +x ../appimagetool.AppImage
|
||||
@@ -29,8 +29,6 @@ MimeType=model/stl;application/vnd.ms-3mfdocument;application/prs.wavefront-obj;
|
||||
EOF
|
||||
|
||||
|
||||
export ARCH=$(uname -m)
|
||||
|
||||
if [ -f /.dockerenv ] ; then # Only run if inside of a Docker Container
|
||||
../appimagetool.AppImage --appimage-extract-and-run . $([ ! -z "${container}" ] && echo '--appimage-extract-and-run')
|
||||
else
|
||||
|
||||
+226
-4
@@ -37,8 +37,9 @@ using namespace nlohmann;
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
static const std::string VERSION_CHECK_URL = "https://check-version.orcaslicer.com/latest";
|
||||
static const std::string PROFILE_UPDATE_URL = "https://api.github.com/repos/OrcaSlicer/orcaslicer-profiles/releases/tags";
|
||||
static const std::string VERSION_CHECK_URL_STABLE = "https://api.github.com/repos/Snapmaker/OrcaSlicer/releases/latest";
|
||||
static const std::string VERSION_CHECK_URL = "https://api.github.com/repos/Snapmaker/OrcaSlicer/releases";
|
||||
static const std::string PROFILE_UPDATE_URL = "https://api.github.com/repos/Snapmaker/Orca_Presets/releases/latest";
|
||||
static const std::string MODELS_STR = "models";
|
||||
|
||||
const std::string AppConfig::SECTION_FILAMENTS = "filaments";
|
||||
@@ -263,6 +264,9 @@ void AppConfig::set_defaults()
|
||||
if(get("check_stable_update_only").empty()) {
|
||||
set_bool("check_stable_update_only", false);
|
||||
}
|
||||
// SM prerelease does not update
|
||||
set_bool("check_stable_update_only", true);
|
||||
|
||||
|
||||
// Orca
|
||||
if(get("show_splash_screen").empty()) {
|
||||
@@ -522,6 +526,8 @@ std::string AppConfig::load()
|
||||
#else
|
||||
ifs >> j;
|
||||
#endif
|
||||
// SM orca
|
||||
update_filament_names(j);
|
||||
}
|
||||
catch(nlohmann::detail::parse_error &err) {
|
||||
#ifdef WIN32
|
||||
@@ -669,6 +675,11 @@ std::string AppConfig::load()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// SM Orca
|
||||
if (j.contains("devices")) {
|
||||
m_device_list = j["devices"].get<std::vector<DeviceInfo>>();
|
||||
}
|
||||
} catch(std::exception err) {
|
||||
BOOST_LOG_TRIVIAL(info) << format("parse app config \"%1%\", error: %2%", AppConfig::loading_path(), err.what());
|
||||
|
||||
@@ -785,6 +796,11 @@ void AppConfig::save()
|
||||
continue;
|
||||
}
|
||||
for (const auto& kvp : category.second) {
|
||||
// SM Orca
|
||||
if (kvp.first == "use_new_connect") {
|
||||
j[category.first][kvp.first] = false;
|
||||
continue;
|
||||
}
|
||||
if (kvp.second == "true") {
|
||||
j[category.first][kvp.first] = true;
|
||||
continue;
|
||||
@@ -820,6 +836,20 @@ void AppConfig::save()
|
||||
for (const auto& preset : m_printer_settings) {
|
||||
j["orca_presets"].push_back(preset.second);
|
||||
}
|
||||
|
||||
j["devices"] = json::array();
|
||||
for (size_t i = 0; i < m_device_list.size(); ++i) {
|
||||
if (m_device_list[i].link_mode != "wan") {
|
||||
j["devices"].push_back(m_device_list[i]);
|
||||
}
|
||||
}
|
||||
|
||||
// j["devices"] = m_device_list;
|
||||
|
||||
|
||||
for (size_t i = 0; i < j["devices"].size(); ++i) {
|
||||
j["devices"][i]["connected"] = false;
|
||||
}
|
||||
for (const auto& local_machine : m_local_machines) {
|
||||
json m_json;
|
||||
m_json["dev_name"] = local_machine.second.dev_name;
|
||||
@@ -1382,10 +1412,10 @@ std::string AppConfig::config_path()
|
||||
return path;
|
||||
}
|
||||
|
||||
std::string AppConfig::version_check_url() const
|
||||
std::string AppConfig::version_check_url(bool stable_only/* = false*/) const
|
||||
{
|
||||
auto from_settings = get("version_check_url");
|
||||
return from_settings.empty() ? VERSION_CHECK_URL : from_settings;
|
||||
return from_settings.empty() ? stable_only ? VERSION_CHECK_URL_STABLE : VERSION_CHECK_URL : from_settings;
|
||||
}
|
||||
|
||||
std::string AppConfig::profile_update_url() const
|
||||
@@ -1398,4 +1428,196 @@ bool AppConfig::exists()
|
||||
return boost::filesystem::exists(config_path());
|
||||
}
|
||||
|
||||
void AppConfig::save_device_info(const DeviceInfo& device)
|
||||
{
|
||||
// 检查是否已存在该设备
|
||||
auto it = std::find_if(m_device_list.begin(), m_device_list.end(),
|
||||
[&device](const DeviceInfo& d) { return d.dev_id == device.dev_id; });
|
||||
|
||||
if (it != m_device_list.end()) {
|
||||
// 更新已存在的设备信息
|
||||
*it = device;
|
||||
} else {
|
||||
// 添加新设备
|
||||
m_device_list.push_back(device);
|
||||
}
|
||||
m_dirty = true;
|
||||
}
|
||||
|
||||
void AppConfig::clear_device_info()
|
||||
{
|
||||
m_device_list.clear();
|
||||
m_dirty = true;
|
||||
}
|
||||
|
||||
void AppConfig::remove_device_info(const std::string& dev_id)
|
||||
{
|
||||
auto it = std::find_if(m_device_list.begin(), m_device_list.end(),
|
||||
[&dev_id](const DeviceInfo& d) { return d.dev_id == dev_id; });
|
||||
|
||||
if (it != m_device_list.end()) {
|
||||
m_device_list.erase(it);
|
||||
m_dirty = true;
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<DeviceInfo> AppConfig::get_devices() const
|
||||
{
|
||||
return m_device_list;
|
||||
}
|
||||
|
||||
bool AppConfig::get_device_info(const std::string& dev_id, DeviceInfo& info) const
|
||||
{
|
||||
auto it = std::find_if(m_device_list.begin(), m_device_list.end(),
|
||||
[&dev_id](const DeviceInfo& d) { return d.dev_id == dev_id; });
|
||||
|
||||
if (it != m_device_list.end()) {
|
||||
info = *it;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void AppConfig::clear_filament_extruder_map()
|
||||
{
|
||||
filament_extruder_map.clear();
|
||||
}
|
||||
|
||||
std::unordered_map<int, int>& AppConfig::get_filament_extruder_map_ref()
|
||||
{
|
||||
return filament_extruder_map;
|
||||
}
|
||||
|
||||
const std::map<std::string, std::string> AppConfig::filament_name_map = {
|
||||
// J1相关映射
|
||||
{"PolyLite J1 PLA @0.2 nozzle", "PolyLite PLA @J1 0.2 nozzle"},
|
||||
{"PolyLite J1 PLA", "PolyLite PLA @J1"},
|
||||
{"PolyTerra J1 PLA @0.2 nozzle", "PolyTerra PLA @J1 0.2 nozzle"},
|
||||
{"PolyTerra J1 PLA", "PolyTerra PLA @J1"},
|
||||
{"Snapmaker J1 ABS @0.2 nozzle", "Snapmaker ABS @J1 0.2 nozzle"},
|
||||
{"Snapmaker J1 ABS @0.8 nozzle", "Snapmaker ABS @J1 0.8 nozzle"},
|
||||
{"Snapmaker J1 ABS", "Snapmaker ABS @J1"},
|
||||
{"Snapmaker J1 ABS Benchy", "Snapmaker ABS Benchy @J1"},
|
||||
{"Snapmaker J1 ASA @0.2 nozzle", "Snapmaker ASA @J1 0.2 nozzle"},
|
||||
{"Snapmaker J1 ASA", "Snapmaker ASA @J1"},
|
||||
{"Snapmaker J1 Breakaway Support", "Snapmaker Breakaway Support @J1"},
|
||||
{"Snapmaker J1 PA-CF", "Snapmaker PA-CF @J1"},
|
||||
{"Snapmaker J1 PET", "Snapmaker PET @J1"},
|
||||
{"Snapmaker J1 PETG @0.2 nozzle", "Snapmaker PETG @J1 0.2 nozzle"},
|
||||
{"Snapmaker J1 PETG @0.8 nozzle", "Snapmaker PETG @J1 0.8 nozzle"},
|
||||
{"Snapmaker J1 PETG", "Snapmaker PETG @J1"},
|
||||
{"Snapmaker J1 PETG-CF", "Snapmaker PETG-CF @J1"},
|
||||
{"Snapmaker J1 PLA", "Snapmaker PLA @J1"},
|
||||
{"Snapmaker J1 PLA Eco @0.2 nozzle", "Snapmaker PLA Eco @J1 0.2 nozzle"},
|
||||
{"Snapmaker J1 PLA Eco @0.8 nozzle", "Snapmaker PLA Eco @J1 0.8 nozzle"},
|
||||
{"Snapmaker J1 PLA Eco", "Snapmaker PLA Eco @J1"},
|
||||
{"Snapmaker J1 PLA Matte @0.2 nozzle", "Snapmaker PLA Matte @J1 0.2 nozzle"},
|
||||
{"Snapmaker J1 PLA Matte @0.8 nozzle", "Snapmaker PLA Matte @J1 0.8 nozzle"},
|
||||
{"Snapmaker J1 PLA Matte", "Snapmaker PLA Matte @J1"},
|
||||
{"Snapmaker J1 PLA Metal @0.2 nozzle", "Snapmaker PLA Metal @J1 0.2 nozzle"},
|
||||
{"Snapmaker J1 PLA Metal", "Snapmaker PLA Metal @J1"},
|
||||
{"Snapmaker J1 PLA Silk @0.2 nozzle", "Snapmaker PLA Silk @J1 0.2 nozzle"},
|
||||
{"Snapmaker J1 PLA Silk", "Snapmaker PLA Silk @J1"},
|
||||
{"Snapmaker J1 PLA-CF", "Snapmaker PLA-CF @J1"},
|
||||
{"Snapmaker J1 PVA @0.2 nozzle", "Snapmaker PVA @J1 0.2 nozzle"},
|
||||
{"Snapmaker J1 PVA", "Snapmaker PVA @J1"},
|
||||
{"Snapmaker J1 TPE", "Snapmaker TPE @J1"},
|
||||
{"Snapmaker J1 TPU", "Snapmaker TPU @J1"},
|
||||
{"Snapmaker J1 TPU High-Flow", "Snapmaker TPU High-Flow @J1"},
|
||||
|
||||
// Dual相关映射
|
||||
{"PolyLite Dual PLA @0.2 nozzle", "PolyLite PLA @Dual 0.2 nozzle"},
|
||||
{"PolyLite Dual PLA", "PolyLite PLA @Dual"},
|
||||
{"PolyTerra Dual PLA @0.2 nozzle", "PolyTerra PLA @Dual 0.2 nozzle"},
|
||||
{"PolyTerra Dual PLA", "PolyTerra PLA @Dual"},
|
||||
{"Snapmaker Dual ABS @0.2 nozzle", "Snapmaker ABS @Dual 0.2 nozzle"},
|
||||
{"Snapmaker Dual ABS @0.8 nozzle", "Snapmaker ABS @Dual 0.8 nozzle"},
|
||||
{"Snapmaker Dual ABS", "Snapmaker ABS @Dual"},
|
||||
{"Snapmaker Dual ABS Benchy", "Snapmaker ABS Benchy @Dual"},
|
||||
{"Snapmaker Dual ASA @0.2 nozzle", "Snapmaker ASA @Dual 0.2 nozzle"},
|
||||
{"Snapmaker Dual ASA", "Snapmaker ASA @Dual"},
|
||||
{"Snapmaker Dual PA-CF", "Snapmaker PA-CF @Dual"},
|
||||
{"Snapmaker Dual PET @0.8 nozzle", "Snapmaker PET @Dual 0.8 nozzle"},
|
||||
{"Snapmaker Dual PET", "Snapmaker PET @Dual"},
|
||||
{"Snapmaker Dual PETG @0.2 nozzle", "Snapmaker PETG @Dual 0.2 nozzle"},
|
||||
{"Snapmaker Dual PETG @0.8 nozzle", "Snapmaker PETG @Dual 0.8 nozzle"},
|
||||
{"Snapmaker Dual PETG", "Snapmaker PETG @Dual"},
|
||||
{"Snapmaker Dual PETG-CF", "Snapmaker PETG-CF @Dual"},
|
||||
{"Snapmaker Dual PLA", "Snapmaker PLA @Dual"},
|
||||
{"Snapmaker Dual PLA Eco @0.2 nozzle", "Snapmaker PLA Eco @Dual 0.2 nozzle"},
|
||||
{"Snapmaker Dual PLA Eco @0.8 nozzle", "Snapmaker PLA Eco @Dual 0.8 nozzle"},
|
||||
{"Snapmaker Dual PLA Eco", "Snapmaker PLA Eco @Dual"},
|
||||
{"Snapmaker Dual PLA Matte @0.2 nozzle", "Snapmaker PLA Matte @Dual 0.2 nozzle"},
|
||||
{"Snapmaker Dual PLA Matte @0.8 nozzle", "Snapmaker PLA Matte @Dual 0.8 nozzle"},
|
||||
{"Snapmaker Dual PLA Matte", "Snapmaker PLA Matte @Dual"},
|
||||
{"Snapmaker Dual PLA Metal @0.2 nozzle", "Snapmaker PLA Metal @Dual 0.2 nozzle"},
|
||||
{"Snapmaker Dual PLA Metal", "Snapmaker PLA Metal @Dual"},
|
||||
{"Snapmaker Dual PLA Silk @0.2 nozzle", "Snapmaker PLA Silk @Dual 0.2 nozzle"},
|
||||
{"Snapmaker Dual PLA Silk", "Snapmaker PLA Silk @Dual"},
|
||||
{"Snapmaker Dual PLA-CF @0.8 nozzle", "Snapmaker PLA-CF @Dual 0.8 nozzle"},
|
||||
{"Snapmaker Dual PLA-CF", "Snapmaker PLA-CF @Dual"},
|
||||
{"Snapmaker Dual PVA @0.2 nozzle", "Snapmaker PVA @Dual 0.2 nozzle"},
|
||||
{"Snapmaker Dual PVA", "Snapmaker PVA @Dual"},
|
||||
{"Snapmaker Dual TPE", "Snapmaker TPE @Dual"},
|
||||
{"Snapmaker Dual TPU", "Snapmaker TPU @Dual"},
|
||||
{"Snapmaker Dual TPU High-Flow", "Snapmaker TPU High-Flow @Dual"}};
|
||||
|
||||
void AppConfig::update_filament_names(json& j)
|
||||
{
|
||||
bool need_save = false;
|
||||
|
||||
// 更新 filaments 数组中的耗材名称
|
||||
if (j.contains("filaments") && j["filaments"].is_array()) {
|
||||
auto& filaments = j["filaments"];
|
||||
for (auto& filament : filaments) {
|
||||
if (filament.is_string()) {
|
||||
std::string old_name = filament.get<std::string>();
|
||||
auto it = filament_name_map.find(old_name);
|
||||
if (it != filament_name_map.end()) {
|
||||
filament = it->second;
|
||||
need_save = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 更新 orca_presets 中的耗材名称
|
||||
if (j.contains("orca_presets") && j["orca_presets"].is_array()) {
|
||||
auto& presets = j["orca_presets"];
|
||||
for (auto& preset : presets) {
|
||||
// 更新主要耗材字段
|
||||
if (preset.contains("filament")) {
|
||||
std::string old_name = preset["filament"].get<std::string>();
|
||||
auto it = filament_name_map.find(old_name);
|
||||
if (it != filament_name_map.end()) {
|
||||
preset["filament"] = it->second;
|
||||
need_save = true;
|
||||
}
|
||||
}
|
||||
|
||||
// 更新 filament_XX 字段
|
||||
for (int i = 1; ; i++) {
|
||||
std::string key = std::string("filament_") + (i < 10 ? "0" : "") + std::to_string(i);
|
||||
if (!preset.contains(key)) {
|
||||
break;
|
||||
}
|
||||
|
||||
std::string old_name = preset[key].get<std::string>();
|
||||
auto it = filament_name_map.find(old_name);
|
||||
if (it != filament_name_map.end()) {
|
||||
preset[key] = it->second;
|
||||
need_save = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//// 如果有更新,保存文件
|
||||
//if (need_save) {
|
||||
// std::string conf_path = (fs::path(Slic3r::data_dir()) / "Snapmaker_Orca.conf").string();
|
||||
// boost::nowide::ofstream ofs(conf_path);
|
||||
// ofs << std::setw(4) << j << std::endl;
|
||||
//}
|
||||
}
|
||||
|
||||
}; // namespace Slic3r
|
||||
|
||||
@@ -30,6 +30,35 @@ using namespace nlohmann;
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
struct DeviceInfo {
|
||||
std::string ip;
|
||||
std::string dev_id;
|
||||
std::string dev_name;
|
||||
std::string model_name;
|
||||
std::string preset_name; // 关联的打印机预设名称
|
||||
bool connected;
|
||||
std::string img;
|
||||
std::vector<std::string> nozzle_sizes;
|
||||
std::string sn;
|
||||
int protocol;
|
||||
std::string api_key;
|
||||
std::string user;
|
||||
std::string password;
|
||||
std::string ca;
|
||||
std::string cert;
|
||||
std::string key;
|
||||
std::string clientId;
|
||||
int port;
|
||||
std::string link_mode;
|
||||
std::string userid;
|
||||
std::string id;
|
||||
|
||||
|
||||
|
||||
// 用于JSON序列化
|
||||
NLOHMANN_DEFINE_TYPE_INTRUSIVE(DeviceInfo, ip, dev_id, dev_name, model_name, preset_name, connected, img, nozzle_sizes, sn, protocol,api_key,
|
||||
user, password, ca, cert, key, clientId, port, link_mode, userid, id)
|
||||
};
|
||||
|
||||
// Connected LAN mode BambuLab printer
|
||||
struct BBLocalMachine
|
||||
@@ -294,7 +323,7 @@ public:
|
||||
|
||||
// Get the Slic3r version check url.
|
||||
// This returns a hardcoded string unless it is overriden by "version_check_url" in the ini file.
|
||||
std::string version_check_url() const;
|
||||
std::string version_check_url(bool stable_only = false) const;
|
||||
|
||||
// Get the Orca profile update url.
|
||||
std::string profile_update_url() const;
|
||||
@@ -339,10 +368,22 @@ public:
|
||||
bool get_mouse_device_invert_roll(const std::string& name, bool& invert) const
|
||||
{ return get_3dmouse_device_numeric_value(name, "invert_roll", invert); }
|
||||
|
||||
void update_filament_names(json& j);
|
||||
|
||||
static const std::string SECTION_FILAMENTS;
|
||||
static const std::string SECTION_MATERIALS;
|
||||
static const std::string SECTION_EMBOSS_STYLE;
|
||||
|
||||
// 添加设备相关的方法
|
||||
void save_device_info(const DeviceInfo& device);
|
||||
void remove_device_info(const std::string& dev_id);
|
||||
std::vector<DeviceInfo> get_devices() const;
|
||||
bool get_device_info(const std::string& dev_id, DeviceInfo& info) const;
|
||||
void clear_device_info();
|
||||
|
||||
void clear_filament_extruder_map();
|
||||
std::unordered_map<int, int>& get_filament_extruder_map_ref();
|
||||
|
||||
private:
|
||||
template<typename T>
|
||||
bool get_3dmouse_device_numeric_value(const std::string &device_name, const char *parameter_name, T &out) const
|
||||
@@ -383,6 +424,15 @@ private:
|
||||
std::vector<PrinterCaliInfo> m_printer_cali_infos;
|
||||
|
||||
std::map<std::string, BBLocalMachine> m_local_machines;
|
||||
|
||||
// 添加耗材名称映射表
|
||||
static const std::map<std::string, std::string> filament_name_map;
|
||||
|
||||
// 添加设备信息存储
|
||||
std::vector<DeviceInfo> m_device_list;
|
||||
|
||||
// 耗材喷嘴映射表
|
||||
std::unordered_map<int, int> filament_extruder_map;
|
||||
};
|
||||
|
||||
} // namespace Slic3r
|
||||
|
||||
@@ -14,7 +14,6 @@ BuildVolume::BuildVolume(const std::vector<Vec2d> &printable_area, const double
|
||||
assert(printable_height >= 0);
|
||||
|
||||
m_polygon = Polygon::new_scale(printable_area);
|
||||
assert(m_polygon.is_counter_clockwise());
|
||||
|
||||
// Calcuate various metrics of the input polygon.
|
||||
m_convex_hull = Geometry::convex_hull(m_polygon.points);
|
||||
|
||||
@@ -1276,16 +1276,16 @@ ConfigSubstitutions ConfigBase::load_from_gcode_file(const std::string &file, Fo
|
||||
// Read a 64k block from the end of the G-code.
|
||||
boost::nowide::ifstream ifs(file);
|
||||
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(": before parse_file %1%") % file.c_str();
|
||||
// Look for Slic3r or OrcaSlicer header.
|
||||
// Look for Slic3r or Snapmaker_Orca header.
|
||||
// Look for the header across the whole file as the G-code may have been extended at the start by a post-processing script or the user.
|
||||
//BBS
|
||||
bool has_delimiters = true;
|
||||
{
|
||||
//BBS
|
||||
std::string bambuslicer_gcode_header = "; OrcaSlicer";
|
||||
std::string bambuslicer_gcode_header = "; Snapmaker_Orca";
|
||||
|
||||
std::string orcaslicer_gcode_header = std::string("; generated by ");
|
||||
orcaslicer_gcode_header += SLIC3R_APP_NAME;
|
||||
std::string Snapmaker_Orca_gcode_header = std::string("; generated by ");
|
||||
Snapmaker_Orca_gcode_header += SLIC3R_APP_NAME;
|
||||
|
||||
std::string header;
|
||||
bool header_found = false;
|
||||
@@ -1297,7 +1297,7 @@ ConfigSubstitutions ConfigBase::load_from_gcode_file(const std::string &file, Fo
|
||||
line_c = skip_whitespaces(line_c);
|
||||
// BBS
|
||||
if (strncmp(bambuslicer_gcode_header.c_str(), line_c, strlen(bambuslicer_gcode_header.c_str())) == 0 ||
|
||||
strncmp(orcaslicer_gcode_header.c_str(), line_c, strlen(orcaslicer_gcode_header.c_str())) == 0) {
|
||||
strncmp(Snapmaker_Orca_gcode_header.c_str(), line_c, strlen(Snapmaker_Orca_gcode_header.c_str())) == 0) {
|
||||
header_found = true;
|
||||
break;
|
||||
}
|
||||
@@ -1324,11 +1324,66 @@ ConfigSubstitutions ConfigBase::load_from_gcode_file(const std::string &file, Fo
|
||||
bool begin_found = false;
|
||||
bool end_found = false;
|
||||
std::string line;
|
||||
while (std::getline(ifs, line))
|
||||
if (line.rfind("; CONFIG_BLOCK_START",0)==0) {
|
||||
|
||||
int thumbnail_id = 0;
|
||||
while (std::getline(ifs, line)) {
|
||||
// 找到缩略图开始标记
|
||||
if (line.find("; THUMBNAIL_BLOCK_START") != std::string::npos) {
|
||||
std::string thumb_content = "";
|
||||
int width = 0;
|
||||
int height = 0;
|
||||
int data_size = 0;
|
||||
|
||||
// 跳过空行
|
||||
std::getline(ifs, line);
|
||||
std::getline(ifs, line);
|
||||
|
||||
// 读取缩略图信息行
|
||||
std::getline(ifs, line);
|
||||
if (line.find("; thumbnail begin") != std::string::npos) {
|
||||
// 解析宽度、高度和数据大小
|
||||
// 格式: "; thumbnail begin 48x48 1144"
|
||||
sscanf(line.c_str(), "; thumbnail begin %dx%d %d", &width, &height, &data_size);
|
||||
|
||||
// 读取Base64编码的数据
|
||||
std::string base64_data;
|
||||
while (std::getline(ifs, line)) {
|
||||
if (line.find("; thumbnail end") != std::string::npos) {
|
||||
break;
|
||||
}
|
||||
// 移除行首的 "; "
|
||||
if (line.substr(0, 2) == "; ") {
|
||||
base64_data += line.substr(2);
|
||||
}
|
||||
}
|
||||
thumb_content = base64_data;
|
||||
|
||||
this->set_deserialize("thumb" + std::to_string(thumbnail_id++), thumb_content, substitutions_ctxt);
|
||||
|
||||
}
|
||||
|
||||
// 读取到块结束标记
|
||||
while (std::getline(ifs, line)) {
|
||||
if (line.find("; THUMBNAIL_BLOCK_END") != std::string::npos) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/*if (thumbnail_id == 2)
|
||||
break;*/
|
||||
}
|
||||
}
|
||||
|
||||
ifs.clear(); // 清除可能的 EOF 标志
|
||||
ifs.seekg(0);
|
||||
|
||||
while (std::getline(ifs, line)) {
|
||||
if (line.rfind("; CONFIG_BLOCK_START", 0) == 0) {
|
||||
begin_found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!begin_found) {
|
||||
//BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << format("Configuration block closing tag \"; CONFIG_BLOCK_START\" not found when reading %1%", file);
|
||||
throw Slic3r::RuntimeError(format("Config tag \"; CONFIG_BLOCK_START\" not found"));
|
||||
|
||||
@@ -231,8 +231,8 @@ class ConfigOptionDef;
|
||||
struct ConfigOptionDeleter { void operator()(ConfigOption* p); };
|
||||
using ConfigOptionUniquePtr = std::unique_ptr<ConfigOption, ConfigOptionDeleter>;
|
||||
|
||||
// When parsing a configuration value, if the old_value is not understood by this OrcaSlicer version,
|
||||
// it is being substituted with some default value that this OrcaSlicer could work with.
|
||||
// When parsing a configuration value, if the old_value is not understood by this Snapmaker_Orca version,
|
||||
// it is being substituted with some default value that this Snapmaker_Orca could work with.
|
||||
// This structure serves to inform the user about the substitutions having been done during file import.
|
||||
struct ConfigSubstitution {
|
||||
const ConfigOptionDef *opt_def { nullptr };
|
||||
|
||||
@@ -1729,7 +1729,7 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result)
|
||||
// Orca: skip version check
|
||||
bool dont_load_config = !m_load_config;
|
||||
// if (m_bambuslicer_generator_version) {
|
||||
// Semver app_version = *(Semver::parse(SoftFever_VERSION));
|
||||
// Semver app_version = *(Semver::parse(Snapmaker_VERSION));
|
||||
// Semver file_version = *m_bambuslicer_generator_version;
|
||||
// if (file_version.maj() != app_version.maj())
|
||||
// dont_load_config = true;
|
||||
@@ -1858,7 +1858,7 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result)
|
||||
lock.close();
|
||||
|
||||
if (!m_is_bbl_3mf) {
|
||||
// if the 3mf was not produced by OrcaSlicer and there is more than one instance,
|
||||
// if the 3mf was not produced by Snapmaker_Orca and there is more than one instance,
|
||||
// split the object in as many objects as instances
|
||||
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ":" << __LINE__ << boost::format(", found 3mf from other vendor, split as instance");
|
||||
for (const IdToModelObjectMap::value_type& object : m_objects) {
|
||||
@@ -2094,7 +2094,7 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result)
|
||||
extruder_id = extruder_opt->getInt();
|
||||
|
||||
if (extruder_id == 0 || extruder_id > max_filament_id)
|
||||
mo->config.set_key_value("extruder", new ConfigOptionInt(1));
|
||||
mo->config.set_key_value("extruder", new ConfigOptionInt(0));
|
||||
|
||||
if (mo->volumes.size() == 1) {
|
||||
mo->volumes[0]->config.erase("extruder");
|
||||
@@ -2108,7 +2108,7 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result)
|
||||
if (vol_extruder_opt->getInt() == 0)
|
||||
mv->config.erase("extruder");
|
||||
else if (vol_extruder_opt->getInt() > max_filament_id)
|
||||
mv->config.set_key_value("extruder", new ConfigOptionInt(1));
|
||||
mv->config.set_key_value("extruder", new ConfigOptionInt(0));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3328,7 +3328,7 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result)
|
||||
}
|
||||
|
||||
if (!m_is_bbl_3mf) {
|
||||
// if the 3mf was not produced by OrcaSlicer and there is only one object,
|
||||
// if the 3mf was not produced by Snapmaker_Orca and there is only one object,
|
||||
// set the object name to match the filename
|
||||
if (m_model->objects.size() == 1)
|
||||
m_model->objects.front()->name = m_name;
|
||||
@@ -3728,12 +3728,12 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result)
|
||||
}*/
|
||||
} else if (m_curr_metadata_name == BBL_APPLICATION_TAG) {
|
||||
// Generator application of the 3MF.
|
||||
// SLIC3R_APP_KEY - SoftFever_VERSION
|
||||
// SLIC3R_APP_KEY - Snapmaker_VERSION
|
||||
if (boost::starts_with(m_curr_characters, "BambuStudio-")) {
|
||||
m_is_bbl_3mf = true;
|
||||
m_bambuslicer_generator_version = Semver::parse(m_curr_characters.substr(12));
|
||||
}
|
||||
else if (boost::starts_with(m_curr_characters, "OrcaSlicer-")) {
|
||||
else if (boost::starts_with(m_curr_characters, "Snapmaker_Orca-")) {
|
||||
m_is_bbl_3mf = true;
|
||||
m_bambuslicer_generator_version = Semver::parse(m_curr_characters.substr(11));
|
||||
}
|
||||
@@ -3741,15 +3741,15 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result)
|
||||
/*} else if (m_curr_metadata_name == BBS_FDM_SUPPORTS_PAINTING_VERSION) {
|
||||
m_fdm_supports_painting_version = (unsigned int) atoi(m_curr_characters.c_str());
|
||||
check_painting_version(m_fdm_supports_painting_version, FDM_SUPPORTS_PAINTING_VERSION,
|
||||
_(L("The selected 3MF contains FDM supports painted object using a newer version of OrcaSlicer and is not compatible.")));
|
||||
_(L("The selected 3MF contains FDM supports painted object using a newer version of Snapmaker_Orca and is not compatible.")));
|
||||
} else if (m_curr_metadata_name == BBS_SEAM_PAINTING_VERSION) {
|
||||
m_seam_painting_version = (unsigned int) atoi(m_curr_characters.c_str());
|
||||
check_painting_version(m_seam_painting_version, SEAM_PAINTING_VERSION,
|
||||
_(L("The selected 3MF contains seam painted object using a newer version of OrcaSlicer and is not compatible.")));
|
||||
_(L("The selected 3MF contains seam painted object using a newer version of Snapmaker_Orca and is not compatible.")));
|
||||
} else if (m_curr_metadata_name == BBS_MM_PAINTING_VERSION) {
|
||||
m_mm_painting_version = (unsigned int) atoi(m_curr_characters.c_str());
|
||||
check_painting_version(m_mm_painting_version, MM_PAINTING_VERSION,
|
||||
_(L("The selected 3MF contains multi-material painted object using a newer version of OrcaSlicer and is not compatible.")));*/
|
||||
_(L("The selected 3MF contains multi-material painted object using a newer version of Snapmaker_Orca and is not compatible.")));*/
|
||||
} else if (m_curr_metadata_name == BBL_MODEL_ID_TAG) {
|
||||
m_model_id = xml_unescape(m_curr_characters);
|
||||
} else if (m_curr_metadata_name == BBL_MODEL_NAME_TAG) {
|
||||
@@ -4908,7 +4908,7 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result)
|
||||
TriangleMesh triangle_mesh(std::move(its), volume_data.mesh_stats);
|
||||
|
||||
if (!m_is_bbl_3mf) {
|
||||
// if the 3mf was not produced by OrcaSlicer and there is only one instance,
|
||||
// if the 3mf was not produced by Snapmaker_Orca and there is only one instance,
|
||||
// bake the transformation into the geometry to allow the reload from disk command
|
||||
// to work properly
|
||||
if (object.instances.size() == 1) {
|
||||
@@ -5791,7 +5791,7 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result)
|
||||
}
|
||||
|
||||
// Adds content types file ("[Content_Types].xml";).
|
||||
// The content of this file is the same for each OrcaSlicer 3mf.
|
||||
// The content of this file is the same for each Snapmaker_Orca 3mf.
|
||||
if (!_add_content_types_file_to_archive(archive)) {
|
||||
return false;
|
||||
}
|
||||
@@ -6174,7 +6174,7 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result)
|
||||
}
|
||||
|
||||
// Adds relationships file ("_rels/.rels").
|
||||
// The content of this file is the same for each OrcaSlicer 3mf.
|
||||
// The content of this file is the same for each Snapmaker_Orca 3mf.
|
||||
// The relationshis file contains a reference to the geometry file "3D/3dmodel.model", the name was chosen to be compatible with CURA.
|
||||
if (!_add_relationships_file_to_archive(archive, {}, {}, {}, temp_data, export_plate_idx)) {
|
||||
BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << ":" <<__LINE__ << boost::format(", _add_relationships_file_to_archive failed\n");
|
||||
@@ -6538,7 +6538,7 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result)
|
||||
metadata_item_map[BBL_CREATION_DATE_TAG] = "";
|
||||
metadata_item_map[BBL_MODIFICATION_TAG] = "";
|
||||
//SoftFever: write BambuStudio tag to keep it compatible
|
||||
metadata_item_map[BBL_APPLICATION_TAG] = (boost::format("%1%-%2%") % "BambuStudio" % SoftFever_VERSION).str();
|
||||
metadata_item_map[BBL_APPLICATION_TAG] = (boost::format("%1%-%2%") % "BambuStudio" % Snapmaker_VERSION).str();
|
||||
}
|
||||
metadata_item_map[BBS_3MF_VERSION] = std::to_string(VERSION_BBS_3MF);
|
||||
|
||||
@@ -7420,7 +7420,7 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result)
|
||||
{
|
||||
const std::string& temp_path = model.get_backup_path();
|
||||
std::string temp_file = temp_path + std::string("/") + "_temp_1.config";
|
||||
config.save_to_json(temp_file, std::string("project_settings"), std::string("project"), std::string(SoftFever_VERSION));
|
||||
config.save_to_json(temp_file, std::string("project_settings"), std::string("project"), std::string(Snapmaker_VERSION));
|
||||
return _add_file_to_archive(archive, BBS_PROJECT_CONFIG_FILE, temp_file);
|
||||
}
|
||||
|
||||
@@ -7798,7 +7798,7 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result)
|
||||
// save slice header for debug
|
||||
stream << " <" << SLICE_HEADER_TAG << ">\n";
|
||||
stream << " <" << SLICE_HEADER_ITEM_TAG << " " << KEY_ATTR << "=\"" << "X-BBL-Client-Type" << "\" " << VALUE_ATTR << "=\"" << "slicer" << "\"/>\n";
|
||||
stream << " <" << SLICE_HEADER_ITEM_TAG << " " << KEY_ATTR << "=\"" << "X-BBL-Client-Version" << "\" " << VALUE_ATTR << "=\"" << convert_to_full_version(SoftFever_VERSION) << "\"/>\n";
|
||||
stream << " <" << SLICE_HEADER_ITEM_TAG << " " << KEY_ATTR << "=\"" << "X-BBL-Client-Version" << "\" " << VALUE_ATTR << "=\"" << convert_to_full_version(Snapmaker_VERSION) << "\"/>\n";
|
||||
stream << " </" << SLICE_HEADER_TAG << ">\n";
|
||||
|
||||
for (unsigned int i = 0; i < (unsigned int)plate_data_list.size(); ++i)
|
||||
|
||||
+50
-9
@@ -281,7 +281,7 @@ static std::vector<Vec2d> get_path_of_change_filament(const Print& print)
|
||||
std::string OozePrevention::post_toolchange(GCode& gcodegen)
|
||||
{
|
||||
return (gcodegen.config().standby_temperature_delta.value != 0) ?
|
||||
gcodegen.writer().set_temperature(this->_get_temp(gcodegen), true, gcodegen.writer().extruder()->id()) :
|
||||
gcodegen.writer().set_temperature(this->_get_temp(gcodegen), gcodegen.config().tool_change_temprature_wait, gcodegen.writer().extruder()->id()) :
|
||||
std::string();
|
||||
}
|
||||
|
||||
@@ -723,6 +723,12 @@ static std::vector<Vec2d> get_path_of_change_filament(const Print& print)
|
||||
float wipe_tower_rotation = tcr.priming ? 0.f : alpha;
|
||||
Vec2f plate_origin_2d(m_plate_origin(0), m_plate_origin(1));
|
||||
|
||||
// For Snapmaker Artision
|
||||
gcodegen.m_next_wipe_x = 0;
|
||||
gcodegen.m_next_wipe_y = 0;
|
||||
auto transformed_pos = Eigen::Rotation2Df(wipe_tower_rotation) * tcr.start_pos + wipe_tower_offset;
|
||||
gcodegen.m_next_wipe_x = transformed_pos(0);
|
||||
gcodegen.m_next_wipe_y = transformed_pos(1);
|
||||
|
||||
std::string tcr_rotated_gcode = post_process_wipe_tower_moves(tcr, wipe_tower_offset, wipe_tower_rotation);
|
||||
|
||||
@@ -741,13 +747,21 @@ static std::vector<Vec2d> get_path_of_change_filament(const Print& print)
|
||||
gcodegen.config().filament_multitool_ramming.get_at(tcr.initial_tool));
|
||||
const bool should_travel_to_tower = !tcr.priming && (tcr.force_travel // wipe tower says so
|
||||
|| !needs_toolchange // this is just finishing the tower with no toolchange
|
||||
|| will_go_down // Make sure to move to prime tower before moving down
|
||||
|| is_ramming);
|
||||
|
||||
if (should_travel_to_tower || gcodegen.m_need_change_layer_lift_z) {
|
||||
// FIXME: It would be better if the wipe tower set the force_travel flag for all toolchanges,
|
||||
// then we could simplify the condition and make it more readable.
|
||||
gcode += gcodegen.retract();
|
||||
auto type = ZHopType(gcodegen.m_config.z_hop_types.get_at(gcodegen.m_writer.extruder()->id()));
|
||||
if (type == ZHopType::zhtAuto) {
|
||||
type = ZHopType::zhtSpiral;
|
||||
}
|
||||
auto lift_type = gcodegen.to_lift_type(type);
|
||||
|
||||
if (gcodegen.m_config.z_hop_when_prime.get_at(gcodegen.m_writer.extruder()->id())) {
|
||||
gcode += gcodegen.retract(false, false, lift_type);
|
||||
}
|
||||
|
||||
gcodegen.m_avoid_crossing_perimeters.use_external_mp_once();
|
||||
gcode += gcodegen.travel_to(wipe_tower_point_to_object_point(gcodegen, start_pos + plate_origin_2d), erMixed, "Travel to a Wipe Tower");
|
||||
gcode += gcodegen.unretract();
|
||||
@@ -839,6 +853,8 @@ static std::vector<Vec2d> get_path_of_change_filament(const Print& print)
|
||||
Vec2f transformed_pos = trans_pos(pos);
|
||||
Vec2f old_pos(-1000.1f, -1000.1f);
|
||||
|
||||
bool isFirstTransform = true;
|
||||
|
||||
while (gcode_str) {
|
||||
std::getline(gcode_str, line); // we read the gcode line by line
|
||||
|
||||
@@ -3206,16 +3222,29 @@ void GCode::_print_first_layer_extruder_temperatures(GCodeOutputStream &file, Pr
|
||||
file.write(m_writer.set_temperature(temp, wait, first_printing_extruder_id));
|
||||
} else {
|
||||
// Set temperatures of all the printing extruders.
|
||||
bool is_active = true;
|
||||
int target_temp = -1;
|
||||
int target_tool = -1;
|
||||
for (unsigned int tool_id : print.extruders()) {
|
||||
is_active = true;
|
||||
int temp = print.config().nozzle_temperature_initial_layer.get_at(tool_id);
|
||||
if (m_ooze_prevention.enable && tool_id != first_printing_extruder_id) {
|
||||
if (print.config().ooze_prevention.value && tool_id != first_printing_extruder_id) {
|
||||
is_active = false;
|
||||
if (print.config().idle_temperature.get_at(tool_id) == 0)
|
||||
temp += print.config().standby_temperature_delta.value;
|
||||
else
|
||||
temp = print.config().idle_temperature.get_at(tool_id);
|
||||
}
|
||||
if (temp > 0)
|
||||
file.write(m_writer.set_temperature(temp, wait, tool_id));
|
||||
if (temp > 0) {
|
||||
if (is_active) {
|
||||
target_temp = temp;
|
||||
target_tool = tool_id;
|
||||
}else
|
||||
file.write(m_writer.set_temperature(temp, wait, tool_id));
|
||||
}
|
||||
}
|
||||
if (target_temp != -1 && target_tool != -1) {
|
||||
file.write(m_writer.set_temperature(target_temp, wait, target_tool));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -6343,9 +6372,17 @@ std::string GCode::retract(bool toolchange, bool is_last_retraction, LiftType li
|
||||
(the extruder might be already retracted fully or partially). We call these
|
||||
methods even if we performed wipe, since this will ensure the entire retraction
|
||||
length is honored in case wipe path was too short. */
|
||||
if ((!this->on_first_layer() || this->config().bottom_surface_pattern != InfillPattern::ipHilbertCurve) &&
|
||||
(role != erTopSolidInfill || this->config().top_surface_pattern != InfillPattern::ipHilbertCurve))
|
||||
gcode += toolchange ? m_writer.retract_for_toolchange() : m_writer.retract();
|
||||
|
||||
// Snapmaker U1
|
||||
std::string printer_model = this->m_curr_print->m_config.printer_model.value;
|
||||
if (printer_model == "Snapmaker U1" && toolchange) {
|
||||
gcode += "M400\n";
|
||||
}
|
||||
if ((!this->on_first_layer() || this->config().bottom_surface_pattern != InfillPattern::ipHilbertCurve) &&
|
||||
(role != erTopSolidInfill || this->config().top_surface_pattern != InfillPattern::ipHilbertCurve)){
|
||||
gcode += toolchange ? m_writer.retract_for_toolchange() : m_writer.retract();
|
||||
}
|
||||
|
||||
|
||||
gcode += m_writer.reset_e();
|
||||
// Orca: check if should + can lift (roughly from SuperSlicer)
|
||||
@@ -6546,6 +6583,10 @@ std::string GCode::set_extruder(unsigned int extruder_id, double print_z, bool b
|
||||
dyn_config.set_key_value(key_value, new ConfigOptionFloat(0.f));
|
||||
}
|
||||
|
||||
// For Snapmaker Artisian
|
||||
dyn_config.set_key_value("next_wipe_x", new ConfigOptionFloat(m_next_wipe_x));
|
||||
dyn_config.set_key_value("next_wipe_y", new ConfigOptionFloat(m_next_wipe_y));
|
||||
|
||||
// Process the custom change_filament_gcode.
|
||||
const std::string& change_filament_gcode = m_config.change_filament_gcode.value;
|
||||
std::string toolchange_gcode_parsed;
|
||||
|
||||
@@ -544,6 +544,10 @@ private:
|
||||
float m_last_layer_z{ 0.0f };
|
||||
float m_max_layer_z{ 0.0f };
|
||||
float m_last_width{ 0.0f };
|
||||
|
||||
// SM_Orca
|
||||
float m_next_wipe_x {0.0f};
|
||||
float m_next_wipe_y {0.0f};
|
||||
#if ENABLE_GCODE_VIEWER_DATA_CHECKING
|
||||
double m_last_mm3_per_mm;
|
||||
#endif // ENABLE_GCODE_VIEWER_DATA_CHECKING
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// AdaptivePAInterpolator.cpp
|
||||
// OrcaSlicer
|
||||
// Snapmaker_Orca
|
||||
//
|
||||
// Implementation file for the AdaptivePAInterpolator class, providing methods to parse data and perform PA interpolation.
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// AdaptivePAInterpolator.hpp
|
||||
// OrcaSlicer
|
||||
// Snapmaker_Orca
|
||||
//
|
||||
// Header file for the AdaptivePAInterpolator class, responsible for interpolating pressure advance (PA) values based on flow rate and acceleration using PCHIP interpolation.
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// AdaptivePAProcessor.cpp
|
||||
// OrcaSlicer
|
||||
// Snapmaker_Orca
|
||||
//
|
||||
// Implementation of the AdaptivePAProcessor class, responsible for processing G-code layers with adaptive pressure advance.
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// AdaptivePAProcessor.hpp
|
||||
// OrcaSlicer
|
||||
// Snapmaker_Orca
|
||||
//
|
||||
// Header file for the AdaptivePAProcessor class, responsible for processing G-code layers for the purposes of applying adaptive pressure advance.
|
||||
|
||||
|
||||
@@ -616,12 +616,12 @@ void GCodeProcessorResult::reset() {
|
||||
#endif // ENABLE_GCODE_VIEWER_STATISTICS
|
||||
|
||||
const std::vector<std::pair<GCodeProcessor::EProducer, std::string>> GCodeProcessor::Producers = {
|
||||
//BBS: OrcaSlicer is also "bambu". Otherwise the time estimation didn't work.
|
||||
//BBS: Snapmaker_Orca is also "bambu". Otherwise the time estimation didn't work.
|
||||
//FIXME: Workaround and should be handled when do removing-bambu
|
||||
{ EProducer::OrcaSlicer, SLIC3R_APP_NAME },
|
||||
{ EProducer::OrcaSlicer, "generated by OrcaSlicer" },
|
||||
{ EProducer::OrcaSlicer, "generated by BambuStudio" },
|
||||
{ EProducer::OrcaSlicer, "BambuStudio" }
|
||||
{ EProducer::Snapmaker_Orca, SLIC3R_APP_NAME },
|
||||
{ EProducer::Snapmaker_Orca, "generated by Snapmaker_Orca" },
|
||||
{ EProducer::Snapmaker_Orca, "generated by BambuStudio" },
|
||||
{ EProducer::Snapmaker_Orca, "BambuStudio" }
|
||||
//{ EProducer::Slic3rPE, "generated by Slic3r Bambu Edition" },
|
||||
//{ EProducer::Slic3r, "generated by Slic3r" },
|
||||
//{ EProducer::SuperSlicer, "generated by SuperSlicer" },
|
||||
@@ -712,6 +712,7 @@ void GCodeProcessor::apply_config(const PrintConfig& config)
|
||||
// Orca:
|
||||
m_is_XL_printer = is_XL_printer(config);
|
||||
m_preheat_time = config.preheat_time;
|
||||
m_delta_temperature = config.delta_temperature;
|
||||
m_preheat_steps = config.preheat_steps;
|
||||
// sanity check
|
||||
if(m_preheat_steps < 1)
|
||||
@@ -811,6 +812,8 @@ void GCodeProcessor::apply_config(const PrintConfig& config)
|
||||
|
||||
void GCodeProcessor::apply_config(const DynamicPrintConfig& config)
|
||||
{
|
||||
m_current_config = config;
|
||||
|
||||
m_parser.apply_config(config);
|
||||
|
||||
//BBS
|
||||
@@ -1191,6 +1194,7 @@ void GCodeProcessor::reset()
|
||||
|
||||
m_seams_count = 0;
|
||||
m_preheat_time = 0.f;
|
||||
m_delta_temperature = 0;
|
||||
m_preheat_steps = 1;
|
||||
|
||||
#if ENABLE_GCODE_VIEWER_DATA_CHECKING
|
||||
@@ -1242,12 +1246,12 @@ void GCodeProcessor::process_file(const std::string& filename, std::function<voi
|
||||
});
|
||||
m_parser.reset();
|
||||
|
||||
// if the gcode was produced by OrcaSlicer,
|
||||
// if the gcode was produced by Snapmaker_Orca,
|
||||
// extract the config from it
|
||||
if (m_producer == EProducer::OrcaSlicer || m_producer == EProducer::Slic3rPE || m_producer == EProducer::Slic3r) {
|
||||
if (m_producer == EProducer::Snapmaker_Orca || m_producer == EProducer::Slic3rPE || m_producer == EProducer::Slic3r) {
|
||||
DynamicPrintConfig config;
|
||||
config.apply(FullPrintConfig::defaults());
|
||||
// Silently substitute unknown values by new ones for loading configurations from OrcaSlicer's own G-code.
|
||||
// Silently substitute unknown values by new ones for loading configurations from Snapmaker_Orca's own G-code.
|
||||
// Showing substitution log or errors may make sense, but we are not really reading many values from the G-code config,
|
||||
// thus a probability of incorrect substitution is low and the G-code viewer is a consumer-only anyways.
|
||||
config.load_from_gcode_file(filename, ForwardCompatibilitySubstitutionRule::EnableSilent);
|
||||
@@ -1959,7 +1963,7 @@ void GCodeProcessor::process_tags(const std::string_view comment, bool producers
|
||||
return;
|
||||
}
|
||||
|
||||
if (!producers_enabled || m_producer == EProducer::OrcaSlicer) {
|
||||
if (!producers_enabled || m_producer == EProducer::Snapmaker_Orca) {
|
||||
// height tag
|
||||
if (boost::starts_with(comment, reserved_tag(ETags::Height))) {
|
||||
if (!parse_number(comment.substr(reserved_tag(ETags::Height).size()), m_forced_height))
|
||||
@@ -2106,7 +2110,7 @@ bool GCodeProcessor::process_producers_tags(const std::string_view comment)
|
||||
case EProducer::Slic3rPE:
|
||||
case EProducer::Slic3r:
|
||||
case EProducer::SuperSlicer:
|
||||
case EProducer::OrcaSlicer: { return process_bambuslicer_tags(comment); }
|
||||
case EProducer::Snapmaker_Orca: { return process_bambuslicer_tags(comment); }
|
||||
case EProducer::Cura: { return process_cura_tags(comment); }
|
||||
case EProducer::Simplify3D: { return process_simplify3d_tags(comment); }
|
||||
case EProducer::CraftWare: { return process_craftware_tags(comment); }
|
||||
@@ -4640,7 +4644,7 @@ void GCodeProcessor::run_post_process()
|
||||
} else {
|
||||
std::string comment = "preheat T" + std::to_string(tool_number) +
|
||||
" time: " + std::to_string((int) std::round(time_diffs[0])) + "s";
|
||||
return GCodeWriter::set_temperature(temperature, this->m_flavor, false, tool_number, comment);
|
||||
return GCodeWriter::set_temperature(temperature + m_delta_temperature, this->m_flavor, false, tool_number, comment);
|
||||
}
|
||||
},
|
||||
// line replacer
|
||||
|
||||
@@ -731,6 +731,7 @@ class Print;
|
||||
int m_seams_count;
|
||||
bool m_single_extruder_multi_material;
|
||||
float m_preheat_time;
|
||||
int m_delta_temperature;
|
||||
int m_preheat_steps;
|
||||
bool m_disable_m73;
|
||||
#if ENABLE_GCODE_VIEWER_STATISTICS
|
||||
@@ -740,7 +741,7 @@ class Print;
|
||||
enum class EProducer
|
||||
{
|
||||
Unknown,
|
||||
OrcaSlicer,
|
||||
Snapmaker_Orca,
|
||||
Slic3rPE,
|
||||
Slic3r,
|
||||
SuperSlicer,
|
||||
@@ -754,6 +755,8 @@ class Print;
|
||||
static const std::vector<std::pair<GCodeProcessor::EProducer, std::string>> Producers;
|
||||
EProducer m_producer;
|
||||
|
||||
DynamicConfig m_current_config;
|
||||
|
||||
TimeProcessor m_time_processor;
|
||||
UsedFilaments m_used_filaments;
|
||||
|
||||
@@ -783,7 +786,10 @@ class Print;
|
||||
const GCodeProcessorResult& get_result() const { return m_result; }
|
||||
GCodeProcessorResult& result() { return m_result; }
|
||||
GCodeProcessorResult&& extract_result() { return std::move(m_result); }
|
||||
DynamicConfig& current_dynamic_config() { return m_current_config; }
|
||||
|
||||
|
||||
GCodeReader& parser() { return m_parser; }
|
||||
// Load a G-code into a stand-alone G-code viewer.
|
||||
// throws CanceledException through print->throw_if_canceled() (sent by the caller as callback).
|
||||
void process_file(const std::string& filename, std::function<void()> cancel_callback = nullptr);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// PchipInterpolatorHelper.cpp
|
||||
// OrcaSlicer
|
||||
// Snapmaker_Orca
|
||||
//
|
||||
// Implementation file for the PchipInterpolatorHelper class
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// PchipInterpolatorHelper.hpp
|
||||
// OrcaSlicer
|
||||
// Snapmaker_Orca
|
||||
//
|
||||
// Header file for the PchipInterpolatorHelper class, responsible for performing Piecewise Cubic Hermite Interpolating Polynomial (PCHIP) interpolation on given data points.
|
||||
|
||||
|
||||
@@ -246,6 +246,24 @@ ToolOrdering::ToolOrdering(const PrintObject &object, unsigned int first_extrude
|
||||
this->mark_skirt_layers(object.print()->config(), max_layer_height);
|
||||
}
|
||||
|
||||
bool ToolOrdering::insert_wipe_tower_extruder()
|
||||
{
|
||||
if(!m_print_config_ptr->enable_prime_tower)
|
||||
return false;
|
||||
// In case that wipe_tower_extruder is set to non-zero, we must make sure that the extruder will be in the list.
|
||||
bool changed = false;
|
||||
if (m_print_config_ptr->wipe_tower_filament != 0) {
|
||||
for (LayerTools& lt : m_layer_tools) {
|
||||
if (lt.wipe_tower_partitions > 0) {
|
||||
lt.extruders.emplace_back(m_print_config_ptr->wipe_tower_filament - 1);
|
||||
sort_remove_duplicates(lt.extruders);
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return changed;
|
||||
}
|
||||
|
||||
// For the use case when all objects are printed at once.
|
||||
// (print->config().print_sequence == PrintSequence::ByObject is false).
|
||||
ToolOrdering::ToolOrdering(const Print &print, unsigned int first_extruder, bool prime_multi_material)
|
||||
@@ -312,6 +330,16 @@ ToolOrdering::ToolOrdering(const Print &print, unsigned int first_extruder, bool
|
||||
|
||||
this->fill_wipe_tower_partitions(print.config(), object_bottom_z, max_layer_height);
|
||||
|
||||
if (this->insert_wipe_tower_extruder()) {
|
||||
// Now convert the 0-based list to 1-based again, because that is what reorder_extruder expects.
|
||||
for (LayerTools& lt : m_layer_tools) {
|
||||
for (auto& extruder : lt.extruders)
|
||||
++extruder;
|
||||
}
|
||||
this->reorder_extruders(first_extruder);
|
||||
this->fill_wipe_tower_partitions(print.config(), object_bottom_z, max_layer_height);
|
||||
}
|
||||
|
||||
this->collect_extruder_statistics(prime_multi_material);
|
||||
|
||||
this->mark_skirt_layers(print.config(), max_layer_height);
|
||||
|
||||
@@ -194,6 +194,7 @@ private:
|
||||
// BBS
|
||||
void reorder_extruders(std::vector<unsigned int> tool_order_layer0);
|
||||
void fill_wipe_tower_partitions(const PrintConfig &config, coordf_t object_bottom_z, coordf_t max_layer_height);
|
||||
bool insert_wipe_tower_extruder();
|
||||
void mark_skirt_layers(const PrintConfig &config, coordf_t max_layer_height);
|
||||
void collect_extruder_statistics(bool prime_multi_material);
|
||||
void reorder_extruders_for_minimum_flush_volume();
|
||||
|
||||
@@ -119,7 +119,7 @@ public:
|
||||
|
||||
WipeTowerWriter& set_initial_tool(size_t tool) { m_current_tool = tool; return *this; }
|
||||
|
||||
WipeTowerWriter& set_z(float z)
|
||||
WipeTowerWriter& set_z(float z)
|
||||
{ m_current_z = z; return *this; }
|
||||
|
||||
WipeTowerWriter& set_extrusion_flow(float flow)
|
||||
@@ -238,7 +238,7 @@ public:
|
||||
WipeTowerWriter& travel(float x, float y, float f = 0.f)
|
||||
{ return extrude_explicit(x, y, 0.f, f); }
|
||||
|
||||
WipeTowerWriter& travel(const Vec2f &dest, float f = 0.f)
|
||||
WipeTowerWriter& travel(const Vec2f &dest, float f = 0.f)
|
||||
{ return extrude_explicit(dest.x(), dest.y(), 0.f, f); }
|
||||
|
||||
// Extrude a line from current position to x, y with the extrusion amount given by m_extrusion_flow.
|
||||
@@ -249,7 +249,7 @@ public:
|
||||
return extrude_explicit(x, y, std::sqrt(dx*dx+dy*dy) * m_extrusion_flow, f, true);
|
||||
}
|
||||
|
||||
WipeTowerWriter& extrude(const Vec2f &dest, const float f = 0.f)
|
||||
WipeTowerWriter& extrude(const Vec2f &dest, const float f = 0.f)
|
||||
{ return extrude(dest.x(), dest.y(), f); }
|
||||
|
||||
WipeTowerWriter& rectangle(const Vec2f& ld,float width,float height,const float f = 0.f)
|
||||
@@ -300,7 +300,7 @@ public:
|
||||
do {
|
||||
++i;
|
||||
if (i == 4) i = 0;
|
||||
if (need_change_flow) {
|
||||
if (need_change_flow) {
|
||||
if (i == 1) {
|
||||
// using bridge flow in bridge area, and add notes for gcode-check when flow changed
|
||||
set_extrusion_flow(wipe_tower->extrusion_flow(0.2));
|
||||
@@ -359,7 +359,7 @@ public:
|
||||
|
||||
// Elevate the extruder head above the current print_z position.
|
||||
WipeTowerWriter& z_hop(float hop, float f = 0.f)
|
||||
{
|
||||
{
|
||||
m_gcode += std::string("G1") + set_format_Z(m_current_z + hop);
|
||||
if (f != 0 && f != m_current_feedrate)
|
||||
m_gcode += set_format_F(f);
|
||||
@@ -368,7 +368,7 @@ public:
|
||||
}
|
||||
|
||||
// Lower the extruder head back to the current print_z position.
|
||||
WipeTowerWriter& z_hop_reset(float f = 0.f)
|
||||
WipeTowerWriter& z_hop_reset(float f = 0.f)
|
||||
{ return z_hop(0, f); }
|
||||
|
||||
// Move to x1, +y_increment,
|
||||
@@ -456,14 +456,14 @@ public:
|
||||
}
|
||||
|
||||
WipeTowerWriter& flush_planner_queue()
|
||||
{
|
||||
m_gcode += "G4 S0\n";
|
||||
{
|
||||
m_gcode += "G4 S0\n";
|
||||
return *this;
|
||||
}
|
||||
|
||||
// Reset internal extruder counter.
|
||||
WipeTowerWriter& reset_extruder()
|
||||
{
|
||||
{
|
||||
m_gcode += "G92 E0\n";
|
||||
return *this;
|
||||
}
|
||||
@@ -674,7 +674,8 @@ void WipeTower::set_extruder(size_t idx, const PrintConfig& config)
|
||||
m_filpar.push_back(FilamentParameters());
|
||||
|
||||
m_filpar[idx].material = config.filament_type.get_at(idx);
|
||||
m_filpar[idx].is_soluble = config.filament_soluble.get_at(idx);
|
||||
// m_filpar[idx].is_soluble = config.filament_soluble.get_at(idx);
|
||||
m_filpar[idx].is_soluble = config.wipe_tower_filament == 0 ? config.filament_soluble.get_at(idx) : (idx != size_t(config.wipe_tower_filament - 1));
|
||||
// BBS
|
||||
m_filpar[idx].is_support = config.filament_is_support.get_at(idx);
|
||||
m_filpar[idx].nozzle_temperature = config.nozzle_temperature.get_at(idx);
|
||||
|
||||
@@ -565,7 +565,8 @@ public:
|
||||
float line_width,
|
||||
GCodeFlavor flavor,
|
||||
const std::vector<WipeTower2::FilamentParameters>& filament_parameters,
|
||||
bool enable_arc_fitting)
|
||||
bool enable_arc_fitting,
|
||||
const std::string& printer_model)
|
||||
:
|
||||
m_current_pos(std::numeric_limits<float>::max(), std::numeric_limits<float>::max()),
|
||||
m_current_z(0.f),
|
||||
@@ -574,8 +575,9 @@ public:
|
||||
m_extrusion_flow(0.f),
|
||||
m_preview_suppressed(false),
|
||||
m_elapsed_time(0.f),
|
||||
m_gcode_flavor(flavor), m_filpar(filament_parameters)
|
||||
//m_enable_arc_fitting(enable_arc_fitting)
|
||||
m_gcode_flavor(flavor),
|
||||
m_filpar(filament_parameters),
|
||||
m_printer_model(printer_model)
|
||||
{
|
||||
// ORCA: This class is only used by non BBL printers, so set the parameter appropriately.
|
||||
// This fixes an issue where the wipe tower was using BBL tags resulting in statistics for purging in the purge tower not being displayed.
|
||||
@@ -624,13 +626,28 @@ public:
|
||||
WipeTowerWriter2& disable_linear_advance() {
|
||||
if (m_gcode_flavor == gcfRepRapSprinter || m_gcode_flavor == gcfRepRapFirmware)
|
||||
m_gcode += (std::string("M572 D") + std::to_string(m_current_tool) + " S0\n");
|
||||
else if (m_gcode_flavor == gcfKlipper)
|
||||
m_gcode += "SET_PRESSURE_ADVANCE ADVANCE=0\n";
|
||||
else if (m_gcode_flavor == gcfKlipper){
|
||||
// m_gcode += "SET_PRESSURE_ADVANCE ADVANCE=0\n"; // Snapmaker U1
|
||||
|
||||
}
|
||||
|
||||
else
|
||||
m_gcode += "M900 K0\n";
|
||||
return *this;
|
||||
}
|
||||
|
||||
WipeTowerWriter2& disable_linear_advance_value(float value = 0.0) {
|
||||
if (m_gcode_flavor == gcfRepRapSprinter || m_gcode_flavor == gcfRepRapFirmware)
|
||||
m_gcode += (std::string("M572 D") + std::to_string(m_current_tool) + " S"+ std::to_string(value) + "\n");
|
||||
else if (m_gcode_flavor == gcfKlipper) {
|
||||
m_gcode += "SET_PRESSURE_ADVANCE ADVANCE=" + Slic3r::float_to_string_decimal_point(value, 4) + "\n"; // Snapmaker U1
|
||||
}
|
||||
|
||||
else
|
||||
m_gcode += "M900 K" + std::to_string(value) + "\n";
|
||||
return *this;
|
||||
}
|
||||
|
||||
WipeTowerWriter2& switch_filament_monitoring(bool enable) {
|
||||
m_gcode += std::string("G4 S0\n") + "M591 " + (enable ? "R" : "S0") + "\n";
|
||||
return *this;
|
||||
@@ -896,16 +913,20 @@ public:
|
||||
WipeTowerWriter2& speed_override_backup()
|
||||
{
|
||||
// This is only supported by Prusa at this point (https://github.com/prusa3d/PrusaSlicer/issues/3114)
|
||||
if (m_gcode_flavor == gcfMarlinLegacy || m_gcode_flavor == gcfMarlinFirmware)
|
||||
if (m_gcode_flavor == gcfMarlinLegacy || m_gcode_flavor == gcfMarlinFirmware || is_snapmaker_u1()) {
|
||||
// u1 特殊处理
|
||||
m_gcode += "M220 B\n";
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
// Let the firmware restore the active speed override value.
|
||||
WipeTowerWriter2& speed_override_restore()
|
||||
{
|
||||
if (m_gcode_flavor == gcfMarlinLegacy || m_gcode_flavor == gcfMarlinFirmware)
|
||||
if (m_gcode_flavor == gcfMarlinLegacy || m_gcode_flavor == gcfMarlinFirmware || is_snapmaker_u1()) {
|
||||
// u1 特殊处理
|
||||
m_gcode += "M220 R\n";
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
@@ -1151,6 +1172,13 @@ private:
|
||||
GCodeFlavor m_gcode_flavor;
|
||||
bool m_enable_arc_fitting = false;
|
||||
const std::vector<WipeTower2::FilamentParameters>& m_filpar;
|
||||
std::string m_printer_model;
|
||||
|
||||
// 判断是否是 Snapmaker U1 打印机
|
||||
bool is_snapmaker_u1() const {
|
||||
return boost::icontains(m_printer_model, "Snapmaker") &&
|
||||
boost::icontains(m_printer_model, "U1");
|
||||
}
|
||||
|
||||
std::string set_format_X(float x)
|
||||
{
|
||||
@@ -1240,7 +1268,11 @@ WipeTower2::WipeTower2(const PrintConfig& config, const PrintRegionConfig& defau
|
||||
m_infill_speed(default_region_config.sparse_infill_speed),
|
||||
m_perimeter_speed(default_region_config.inner_wall_speed),
|
||||
m_current_tool(initial_tool),
|
||||
wipe_volumes(wiping_matrix), m_wipe_tower_max_purge_speed(float(config.wipe_tower_max_purge_speed)),
|
||||
wipe_volumes(wiping_matrix),
|
||||
m_wipe_tower_max_purge_speed(float(config.wipe_tower_max_purge_speed)),
|
||||
m_change_pressure(config.enable_change_pressure_when_wiping),
|
||||
m_change_pressure_value(config.ramming_pressure_advance_value),
|
||||
m_ramming_width_ratio(config.ramming_line_width_ratio),
|
||||
m_enable_arc_fitting(config.enable_arc_fitting),
|
||||
m_used_fillet(config.wipe_tower_fillet_wall),
|
||||
m_rib_width(config.wipe_tower_rib_width),
|
||||
@@ -1274,6 +1306,9 @@ WipeTower2::WipeTower2(const PrintConfig& config, const PrintRegionConfig& defau
|
||||
|
||||
m_is_mk4mmu3 = boost::icontains(config.printer_notes.value, "PRINTER_MODEL_MK4") && boost::icontains(config.printer_notes.value, "MMU");
|
||||
|
||||
// 保存打印机型号信息
|
||||
m_printer_model = config.printer_model.value;
|
||||
|
||||
// Calculate where the priming lines should be - very naive test not detecting parallelograms etc.
|
||||
const std::vector<Vec2d>& bed_points = config.printable_area.values;
|
||||
BoundingBoxf bb(bed_points);
|
||||
@@ -1305,7 +1340,8 @@ void WipeTower2::set_extruder(size_t idx, const PrintConfig& config)
|
||||
m_filpar.push_back(FilamentParameters());
|
||||
|
||||
m_filpar[idx].material = config.filament_type.get_at(idx);
|
||||
m_filpar[idx].is_soluble = config.filament_soluble.get_at(idx);
|
||||
// m_filpar[idx].is_soluble = config.filament_soluble.get_at(idx);
|
||||
m_filpar[idx].is_soluble = config.wipe_tower_filament == 0 ? config.filament_soluble.get_at(idx) : (idx != size_t(config.wipe_tower_filament - 1));
|
||||
m_filpar[idx].temperature = config.nozzle_temperature.get_at(idx);
|
||||
m_filpar[idx].first_layer_temperature = config.nozzle_temperature_initial_layer.get_at(idx);
|
||||
m_filpar[idx].filament_minimal_purge_on_wipe_tower = config.filament_minimal_purge_on_wipe_tower.get_at(idx);
|
||||
@@ -1351,7 +1387,7 @@ void WipeTower2::set_extruder(size_t idx, const PrintConfig& config)
|
||||
float vol = config.filament_multitool_ramming_volume.get_at(idx);
|
||||
float flow = config.filament_multitool_ramming_flow.get_at(idx);
|
||||
m_filpar[idx].multitool_ramming = config.filament_multitool_ramming.get_at(idx) && vol > 0.f && flow > 0.f;
|
||||
m_filpar[idx].ramming_line_width_multiplicator = 2.;
|
||||
m_filpar[idx].ramming_line_width_multiplicator = m_ramming_width_ratio;
|
||||
m_filpar[idx].ramming_step_multiplicator = 1.;
|
||||
|
||||
// Now the ramming speed vector. In this case it contains just one value (flow).
|
||||
@@ -1405,7 +1441,7 @@ std::vector<WipeTower::ToolChangeResult> WipeTower2::prime(
|
||||
for (size_t idx_tool = 0; idx_tool < tools.size(); ++ idx_tool) {
|
||||
size_t old_tool = m_current_tool;
|
||||
|
||||
WipeTowerWriter2 writer(m_layer_height, m_perimeter_width, m_gcode_flavor, m_filpar, m_enable_arc_fitting);
|
||||
WipeTowerWriter2 writer(m_layer_height, m_perimeter_width, m_gcode_flavor, m_filpar, m_enable_arc_fitting, m_printer_model);
|
||||
writer.set_extrusion_flow(m_extrusion_flow)
|
||||
.set_z(m_z_pos)
|
||||
.set_initial_tool(m_current_tool);
|
||||
@@ -1432,7 +1468,10 @@ std::vector<WipeTower::ToolChangeResult> WipeTower2::prime(
|
||||
toolchange_Load(writer, cleaning_box); // Prime the tool.
|
||||
if (idx_tool + 1 == tools.size()) {
|
||||
// Last tool should not be unloaded, but it should be wiped enough to become of a pure color.
|
||||
toolchange_Wipe(writer, cleaning_box, wipe_volumes[tools[idx_tool-1]][tool]);
|
||||
if (idx_tool == 0)
|
||||
toolchange_Wipe(writer, cleaning_box, wipe_volumes[tools[idx_tool]][tool]);
|
||||
else
|
||||
toolchange_Wipe(writer, cleaning_box, wipe_volumes[tools[idx_tool - 1]][tool]);
|
||||
} else {
|
||||
// Ram the hot material out of the melt zone, retract the filament into the cooling tubes and let it cool.
|
||||
//writer.travel(writer.x(), writer.y() + m_perimeter_width, 7200);
|
||||
@@ -1500,7 +1539,7 @@ WipeTower::ToolChangeResult WipeTower2::tool_change(size_t tool)
|
||||
(tool != (unsigned int)(-1) ? wipe_area+m_depth_traversed-0.5f*m_perimeter_width
|
||||
: m_wipe_tower_depth-m_perimeter_width));
|
||||
|
||||
WipeTowerWriter2 writer(m_layer_height, m_perimeter_width, m_gcode_flavor, m_filpar, m_enable_arc_fitting);
|
||||
WipeTowerWriter2 writer(m_layer_height, m_perimeter_width, m_gcode_flavor, m_filpar, m_enable_arc_fitting, m_printer_model);
|
||||
writer.set_extrusion_flow(m_extrusion_flow)
|
||||
.set_z(m_z_pos)
|
||||
.set_initial_tool(m_current_tool)
|
||||
@@ -1590,8 +1629,12 @@ void WipeTower2::toolchange_Unload(
|
||||
|
||||
if (do_ramming) {
|
||||
writer.travel(ramming_start_pos); // move to starting position
|
||||
if (! m_is_mk4mmu3)
|
||||
writer.disable_linear_advance();
|
||||
if (!m_is_mk4mmu3) {
|
||||
if (m_change_pressure) {
|
||||
writer.disable_linear_advance_value(m_change_pressure_value);
|
||||
}
|
||||
}
|
||||
|
||||
if (cold_ramming)
|
||||
writer.set_extruder_temp(old_temperature - 20);
|
||||
}
|
||||
@@ -1634,8 +1677,13 @@ void WipeTower2::toolchange_Unload(
|
||||
}
|
||||
|
||||
|
||||
bool is_over_tower_height = false;
|
||||
if (m_plan.size() > 0 && m_num_layer_changes == m_plan.size()) {
|
||||
is_over_tower_height = true;
|
||||
}
|
||||
|
||||
// now the ramming itself:
|
||||
while (do_ramming && i < m_filpar[m_current_tool].ramming_speed.size())
|
||||
while (do_ramming && i < m_filpar[m_current_tool].ramming_speed.size() && !is_over_tower_height)
|
||||
{
|
||||
// The time step is different for SEMM ramming and the MM ramming. See comments in set_extruder() for details.
|
||||
const float time_step = m_semm ? 0.25f : m_filpar[m_current_tool].multitool_ramming_time;
|
||||
@@ -1705,8 +1753,12 @@ void WipeTower2::toolchange_Unload(
|
||||
|
||||
float speed_inc = (final_speed - initial_speed) / (2.f * number_of_cooling_moves - 1.f);
|
||||
|
||||
if (m_is_mk4mmu3)
|
||||
writer.disable_linear_advance();
|
||||
if (m_is_mk4mmu3) {
|
||||
if (m_change_pressure) {
|
||||
writer.disable_linear_advance_value(m_change_pressure_value);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
writer.suppress_preview()
|
||||
.travel(writer.x(), writer.y() + y_step);
|
||||
@@ -1928,7 +1980,7 @@ WipeTower::ToolChangeResult WipeTower2::finish_layer()
|
||||
|
||||
size_t old_tool = m_current_tool;
|
||||
|
||||
WipeTowerWriter2 writer(m_layer_height, m_perimeter_width, m_gcode_flavor, m_filpar, m_enable_arc_fitting);
|
||||
WipeTowerWriter2 writer(m_layer_height, m_perimeter_width, m_gcode_flavor, m_filpar, m_enable_arc_fitting, m_printer_model);
|
||||
writer.set_extrusion_flow(m_extrusion_flow)
|
||||
.set_z(m_z_pos)
|
||||
.set_initial_tool(m_current_tool)
|
||||
@@ -2216,14 +2268,12 @@ void WipeTower2::save_on_last_wipe()
|
||||
int WipeTower2::first_toolchange_to_nonsoluble(
|
||||
const std::vector<WipeTowerInfo::ToolChange>& tool_changes) const
|
||||
{
|
||||
// Orca: allow calculation of the required depth and wipe volume for soluable toolchanges as well
|
||||
// NOTE: it's not clear if this is the right way, technically we should disable wipe tower if soluble filament is used as it
|
||||
// will will make the wipe tower unstable. Need to revist this in the future.
|
||||
return tool_changes.empty() ? -1 : 0;
|
||||
//for (size_t idx=0; idx<tool_changes.size(); ++idx)
|
||||
// if (! m_filpar[tool_changes[idx].new_tool].is_soluble)
|
||||
// return idx;
|
||||
//return -1;
|
||||
// 使用 wipe_tower_filament 配置来决定哪个挤出机用于 wipe tower
|
||||
for (size_t idx=0; idx<tool_changes.size(); ++idx) {
|
||||
if (!m_filpar[tool_changes[idx].new_tool].is_soluble)
|
||||
return idx;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
static WipeTower::ToolChangeResult merge_tcr(WipeTower::ToolChangeResult& first,
|
||||
|
||||
@@ -175,10 +175,13 @@ private:
|
||||
return m_filpar[0].filament_area; // all extruders are assumed to have the same filament diameter at this point
|
||||
}
|
||||
|
||||
|
||||
bool m_change_pressure = true;
|
||||
float m_change_pressure_value = 0.0;
|
||||
float m_ramming_width_ratio = 2.0;
|
||||
bool m_semm = true; // Are we using a single extruder multimaterial printer?
|
||||
bool m_enable_filament_ramming = true;
|
||||
bool m_is_mk4mmu3 = false;
|
||||
std::string m_printer_model; // Printer model name (e.g., "Snapmaker U1")
|
||||
Vec2f m_wipe_tower_pos; // Left front corner of the wipe tower in mm.
|
||||
float m_wipe_tower_width; // Width of the wipe tower.
|
||||
float m_wipe_tower_depth = 0.f; // Depth of the wipe tower
|
||||
|
||||
+24
-6
@@ -458,7 +458,7 @@ ModelObject* Model::add_object(const char *name, const char *path, const Triangl
|
||||
new_volume->source.volume_idx = (int)new_object->volumes.size() - 1;
|
||||
// BBS: set extruder id to 1
|
||||
if (!new_object->config.has("extruder") || new_object->config.extruder() == 0)
|
||||
new_object->config.set_key_value("extruder", new ConfigOptionInt(1));
|
||||
new_object->config.set_key_value("extruder", new ConfigOptionInt(0));
|
||||
new_object->invalidate_bounding_box();
|
||||
return new_object;
|
||||
}
|
||||
@@ -476,7 +476,7 @@ ModelObject* Model::add_object(const char *name, const char *path, TriangleMesh
|
||||
new_volume->source.volume_idx = (int)new_object->volumes.size() - 1;
|
||||
// BBS: set default extruder id to 1
|
||||
if (!new_object->config.has("extruder") || new_object->config.extruder() == 0)
|
||||
new_object->config.set_key_value("extruder", new ConfigOptionInt(1));
|
||||
new_object->config.set_key_value("extruder", new ConfigOptionInt(0));
|
||||
new_object->invalidate_bounding_box();
|
||||
return new_object;
|
||||
}
|
||||
@@ -487,7 +487,7 @@ ModelObject* Model::add_object(const ModelObject &other)
|
||||
new_object->set_model(this);
|
||||
// BBS: set default extruder id to 1
|
||||
if (!new_object->config.has("extruder") || new_object->config.extruder() == 0)
|
||||
new_object->config.set_key_value("extruder", new ConfigOptionInt(1));
|
||||
new_object->config.set_key_value("extruder", new ConfigOptionInt(0));
|
||||
this->objects.push_back(new_object);
|
||||
// BBS: backup
|
||||
if (need_backup) {
|
||||
@@ -2424,7 +2424,7 @@ int ModelVolume::extruder_id() const
|
||||
const ConfigOption *opt = this->config.option("extruder");
|
||||
if ((opt == nullptr) || (opt->getInt() == 0))
|
||||
opt = this->object->config.option("extruder");
|
||||
extruder_id = (opt == nullptr) ? 1 : opt->getInt();
|
||||
extruder_id = (opt == nullptr) ? 0 : opt->getInt();
|
||||
}
|
||||
return extruder_id;
|
||||
}
|
||||
@@ -2466,6 +2466,8 @@ std::vector<int> ModelVolume::get_extruders() const
|
||||
int volume_extruder_id = this->extruder_id();
|
||||
if (volume_extruder_id > 0)
|
||||
volume_extruders.push_back(volume_extruder_id);
|
||||
else if (volume_extruder_id == 0)
|
||||
volume_extruders.push_back(volume_extruder_id + 1);
|
||||
|
||||
return volume_extruders;
|
||||
}
|
||||
@@ -2481,6 +2483,19 @@ void ModelVolume::update_extruder_count(size_t extruder_count)
|
||||
}
|
||||
}
|
||||
|
||||
void ModelVolume::update_extruder_count_when_delete_filament(size_t extruder_count, size_t filament_id, int replace_filament_id)
|
||||
{
|
||||
std::vector<int> used_extruders = get_extruders();
|
||||
for (int extruder_id : used_extruders) {
|
||||
if (extruder_id >= filament_id) {
|
||||
mmu_segmentation_facets.set_enforcer_block_type_limit(*this, (EnforcerBlockerType) (extruder_count),
|
||||
(EnforcerBlockerType) (filament_id),
|
||||
(EnforcerBlockerType) (replace_filament_id));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ModelVolume::center_geometry_after_creation(bool update_source_offset)
|
||||
{
|
||||
Vec3d shift = this->mesh().bounding_box().center();
|
||||
@@ -3375,10 +3390,13 @@ void FacetsAnnotation::get_facets(const ModelVolume& mv, std::vector<indexed_tri
|
||||
selector.get_facets(facets_per_type);
|
||||
}
|
||||
|
||||
void FacetsAnnotation::set_enforcer_block_type_limit(const ModelVolume& mv, EnforcerBlockerType max_type)
|
||||
void FacetsAnnotation::set_enforcer_block_type_limit(const ModelVolume& mv,
|
||||
EnforcerBlockerType max_type,
|
||||
EnforcerBlockerType to_delete_filament,
|
||||
EnforcerBlockerType replace_filament)
|
||||
{
|
||||
TriangleSelector selector(mv.mesh());
|
||||
selector.deserialize(m_data, false, max_type);
|
||||
selector.deserialize(m_data, false, max_type, to_delete_filament, replace_filament);
|
||||
this->set(selector);
|
||||
}
|
||||
|
||||
|
||||
@@ -734,7 +734,10 @@ public:
|
||||
indexed_triangle_set get_facets(const ModelVolume& mv, EnforcerBlockerType type) const;
|
||||
// BBS
|
||||
void get_facets(const ModelVolume& mv, std::vector<indexed_triangle_set>& facets_per_type) const;
|
||||
void set_enforcer_block_type_limit(const ModelVolume& mv, EnforcerBlockerType max_type);
|
||||
void set_enforcer_block_type_limit(const ModelVolume& mv,
|
||||
EnforcerBlockerType max_type,
|
||||
EnforcerBlockerType to_delete_filament = EnforcerBlockerType::NONE,
|
||||
EnforcerBlockerType replace_filament = EnforcerBlockerType::NONE);
|
||||
indexed_triangle_set get_facets_strict(const ModelVolume& mv, EnforcerBlockerType type) const;
|
||||
bool has_facets(const ModelVolume& mv, EnforcerBlockerType type) const;
|
||||
bool empty() const { return m_data.triangles_to_split.empty(); }
|
||||
@@ -915,6 +918,7 @@ public:
|
||||
// BBS
|
||||
std::vector<int> get_extruders() const;
|
||||
void update_extruder_count(size_t extruder_count);
|
||||
void update_extruder_count_when_delete_filament(size_t extruder_count, size_t filament_id, int replace_filament_id = -1);
|
||||
|
||||
// Split this volume, append the result to the object owning this volume.
|
||||
// Return the number of volumes created from this one.
|
||||
|
||||
@@ -69,7 +69,7 @@ namespace Slic3r {
|
||||
|
||||
PlaceholderParser::PlaceholderParser(const DynamicConfig *external_config) : m_external_config(external_config)
|
||||
{
|
||||
this->set("version", std::string(SoftFever_VERSION));
|
||||
this->set("version", std::string(Snapmaker_VERSION));
|
||||
this->apply_env_variables();
|
||||
this->update_timestamp();
|
||||
this->update_user_name();
|
||||
|
||||
+59
-16
@@ -52,6 +52,31 @@ using boost::property_tree::ptree;
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
Semver get_min_version_from_json(std::string file_path)
|
||||
{
|
||||
try {
|
||||
boost::nowide::ifstream ifs(file_path);
|
||||
json j;
|
||||
ifs >> j;
|
||||
if (!j.count(BBL_JSON_KEY_MIN_VERSION)) {
|
||||
return Semver();
|
||||
}
|
||||
std::string version_str = j.at(BBL_JSON_KEY_MIN_VERSION);
|
||||
|
||||
auto config_version = Semver::parse(version_str);
|
||||
if (!config_version) {
|
||||
return Semver();
|
||||
} else {
|
||||
return *config_version;
|
||||
}
|
||||
}
|
||||
catch (nlohmann::detail::parse_error& err) {
|
||||
BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << ": parse " << file_path
|
||||
<< " got a nlohmann::detail::parse_error, reason = " << err.what();
|
||||
return Semver();
|
||||
}
|
||||
}
|
||||
|
||||
//BBS: add a function to load the version from xxx.json
|
||||
Semver get_version_from_json(std::string file_path)
|
||||
{
|
||||
@@ -753,17 +778,10 @@ BedType Preset::get_default_bed_type(PresetBundle* preset_bundle)
|
||||
{
|
||||
if (config.has("default_bed_type") && !config.opt_string("default_bed_type").empty()) {
|
||||
try {
|
||||
std::string str_bed_type = config.opt_string("default_bed_type");
|
||||
|
||||
// Try parsing as integer first (legacy format)
|
||||
int bed_type_value = atoi(str_bed_type.c_str());
|
||||
if (bed_type_value > 0) {
|
||||
std::string str_bed_type = config.opt_string("default_bed_type");
|
||||
int bed_type_value = atoi(str_bed_type.c_str());
|
||||
if (bed_type_value != 0)
|
||||
return BedType(bed_type_value);
|
||||
}
|
||||
else {
|
||||
BOOST_LOG_TRIVIAL(error) << "default_bed_type: invalid bed type: " << str_bed_type;
|
||||
}
|
||||
return BedType::btPEI;
|
||||
|
||||
} catch(...) {
|
||||
;
|
||||
@@ -775,6 +793,8 @@ BedType Preset::get_default_bed_type(PresetBundle* preset_bundle)
|
||||
return BedType::btPC;
|
||||
} else if (model_id == "C11") {
|
||||
return BedType::btPEI;
|
||||
} else if (model_id == "SM_U1") {
|
||||
return BedType::btPTE;
|
||||
}
|
||||
return BedType::btPEI;
|
||||
}
|
||||
@@ -814,7 +834,7 @@ static std::vector<std::string> s_Preset_print_options {
|
||||
"support_top_z_distance", "support_on_build_plate_only","support_critical_regions_only", "bridge_no_support", "thick_bridges", "thick_internal_bridges","dont_filter_internal_bridges","enable_extra_bridge_layer", "max_bridge_length", "print_sequence", "print_order", "support_remove_small_overhang",
|
||||
"filename_format", "wall_filament", "support_bottom_z_distance",
|
||||
"sparse_infill_filament", "solid_infill_filament", "support_filament", "support_interface_filament","support_interface_not_for_body",
|
||||
"ooze_prevention", "standby_temperature_delta", "preheat_time","preheat_steps", "interface_shells", "line_width", "initial_layer_line_width", "inner_wall_line_width",
|
||||
"ooze_prevention", "standby_temperature_delta", "preheat_time","delta_temperature","preheat_steps", "interface_shells", "line_width", "initial_layer_line_width", "inner_wall_line_width",
|
||||
"outer_wall_line_width", "sparse_infill_line_width", "internal_solid_infill_line_width",
|
||||
"skin_infill_line_width","skeleton_infill_line_width",
|
||||
"top_surface_line_width", "support_line_width", "infill_wall_overlap","top_bottom_infill_wall_overlap", "bridge_flow", "internal_bridge_flow",
|
||||
@@ -866,7 +886,7 @@ static std::vector<std::string> s_Preset_filament_options {
|
||||
"activate_air_filtration","during_print_exhaust_fan_speed","complete_print_exhaust_fan_speed",
|
||||
// Retract overrides
|
||||
"filament_retraction_length", "filament_z_hop", "filament_z_hop_types", "filament_retract_lift_above", "filament_retract_lift_below", "filament_retract_lift_enforce", "filament_retraction_speed", "filament_deretraction_speed", "filament_retract_restart_extra", "filament_retraction_minimum_travel",
|
||||
"filament_retract_when_changing_layer", "filament_wipe", "filament_retract_before_wipe",
|
||||
"filament_retract_when_changing_layer", "filament_wipe", "filament_retract_before_wipe", "filament_retract_length_toolchange", "filament_retract_restart_extra_toolchange",
|
||||
// Profile compatibility
|
||||
"filament_vendor", "compatible_prints", "compatible_prints_condition", "compatible_printers", "compatible_printers_condition", "inherits",
|
||||
//BBS
|
||||
@@ -902,8 +922,8 @@ static std::vector<std::string> s_Preset_printer_options {
|
||||
"nozzle_height",
|
||||
"default_print_profile", "inherits",
|
||||
"silent_mode",
|
||||
"scan_first_layer", "machine_load_filament_time", "machine_unload_filament_time", "machine_tool_change_time", "time_cost", "machine_pause_gcode", "template_custom_gcode",
|
||||
"nozzle_type", "nozzle_hrc","auxiliary_fan", "nozzle_volume","upward_compatible_machine", "z_hop_types", "travel_slope", "retract_lift_enforce","support_chamber_temp_control","support_air_filtration","printer_structure",
|
||||
"scan_first_layer", "machine_load_filament_time", "machine_unload_filament_time", "machine_tool_change_time", "tool_change_temprature_wait", "time_cost", "machine_pause_gcode", "template_custom_gcode",
|
||||
"nozzle_type", "nozzle_hrc","auxiliary_fan", "nozzle_volume","upward_compatible_machine", "z_hop_types", "z_hop_when_prime", "travel_slope", "retract_lift_enforce","support_chamber_temp_control","support_air_filtration","printer_structure",
|
||||
"best_object_pos","head_wrap_detect_zone",
|
||||
"host_type", "print_host", "printhost_apikey", "bbl_use_printhost",
|
||||
"print_host_webui",
|
||||
@@ -911,7 +931,7 @@ static std::vector<std::string> s_Preset_printer_options {
|
||||
"printhost_user", "printhost_password", "printhost_ssl_ignore_revoke", "thumbnails", "thumbnails_format",
|
||||
"use_firmware_retraction", "use_relative_e_distances", "printer_notes",
|
||||
"cooling_tube_retraction",
|
||||
"cooling_tube_length", "high_current_on_filament_swap", "parking_pos_retraction", "extra_loading_move", "purge_in_prime_tower", "enable_filament_ramming",
|
||||
"cooling_tube_length", "high_current_on_filament_swap", "parking_pos_retraction", "extra_loading_move", "purge_in_prime_tower", "enable_filament_ramming", "ramming_line_width_ratio", "enable_change_pressure_when_wiping", "ramming_pressure_advance_value",
|
||||
"z_offset",
|
||||
"disable_m73", "preferred_orientation", "emit_machine_limits_to_gcode", "pellet_modded_printer", "support_multi_bed_types", "default_bed_type", "bed_mesh_min","bed_mesh_max","bed_mesh_probe_distance", "adaptive_bed_mesh_margin", "enable_long_retraction_when_cut","long_retractions_when_cut","retraction_distances_when_cut"
|
||||
};
|
||||
@@ -2615,6 +2635,17 @@ size_t PresetCollection::first_visible_idx() const
|
||||
return first_visible;
|
||||
}
|
||||
|
||||
std::vector<std::string> PresetCollection::diameters_of_selected_printer()
|
||||
{
|
||||
std::set<std::string> diameters;
|
||||
auto printer_model = m_edited_preset.config.opt_string("printer_model");
|
||||
for (auto &preset : m_presets) {
|
||||
if (preset.config.opt_string("printer_model") == printer_model)
|
||||
diameters.insert(preset.config.opt_string("printer_variant"));
|
||||
}
|
||||
return std::vector<std::string>{diameters.begin(), diameters.end()};
|
||||
}
|
||||
|
||||
void PresetCollection::set_default_suppressed(bool default_suppressed)
|
||||
{
|
||||
if (m_default_suppressed != default_suppressed) {
|
||||
@@ -2897,8 +2928,20 @@ std::vector<std::string> PresetCollection::merge_presets(PresetCollection &&othe
|
||||
preset.vendor = &it->second;
|
||||
}
|
||||
m_presets.emplace(it, std::move(preset));
|
||||
} else
|
||||
} else {
|
||||
std::string default_vendor = std::string(PresetBundle::SM_BUNDLE);
|
||||
if (preset.vendor->name == default_vendor) {
|
||||
if (preset.vendor != nullptr) {
|
||||
// Re-assign a pointer to the vendor structure in the new PresetBundle.
|
||||
auto it = new_vendors.find(preset.vendor->id);
|
||||
assert(it != new_vendors.end());
|
||||
preset.vendor = &it->second;
|
||||
}
|
||||
m_presets.emplace(it, std::move(preset));
|
||||
}
|
||||
duplicates.emplace_back(std::move(preset.name));
|
||||
}
|
||||
|
||||
}
|
||||
return duplicates;
|
||||
}
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
#include "ProjectTask.hpp"
|
||||
|
||||
//BBS: change system directories
|
||||
#define PRESET_WEB_DIR "web"
|
||||
#define PRESET_SYSTEM_DIR "system"
|
||||
#define PRESET_USER_DIR "user"
|
||||
#define PRESET_FILAMENT_NAME "filament"
|
||||
@@ -35,6 +36,7 @@
|
||||
|
||||
|
||||
//BBS: add json support
|
||||
#define BBL_JSON_KEY_MIN_VERSION "min_version"
|
||||
#define BBL_JSON_KEY_VERSION "version"
|
||||
#define BBL_JSON_KEY_IS_CUSTOM "is_custom_defined"
|
||||
#define BBL_JSON_KEY_URL "url"
|
||||
@@ -83,6 +85,9 @@ enum ConfigFileType
|
||||
|
||||
//BBS: add a function to load the version from xxx.json
|
||||
extern Semver get_version_from_json(std::string file_path);
|
||||
|
||||
extern Semver get_min_version_from_json(std::string file_path);
|
||||
|
||||
//BBS: add a function to load the key-values from xxx.json
|
||||
extern int get_values_from_json(std::string file_path, std::vector<std::string>& keys, std::map<std::string, std::string>& key_values);
|
||||
|
||||
@@ -612,6 +617,9 @@ public:
|
||||
// Orca: find preset, if not found, keep searching in the renamed history. This is function should only be used when find
|
||||
// system(parent) presets for custom preset.
|
||||
Preset* find_preset2(const std::string& name, bool auto_match = true);
|
||||
|
||||
std::vector<std::string> diameters_of_selected_printer();
|
||||
|
||||
const Preset* find_preset2(const std::string& name, bool auto_match = true) const
|
||||
{
|
||||
return const_cast<PresetCollection*>(this)->find_preset2(name, auto_match);
|
||||
|
||||
@@ -43,11 +43,11 @@ static std::vector<std::string> s_project_options {
|
||||
"flush_multiplier",
|
||||
};
|
||||
|
||||
//Orca: add custom as default
|
||||
const char *PresetBundle::ORCA_DEFAULT_BUNDLE = "Custom";
|
||||
const char *PresetBundle::ORCA_DEFAULT_PRINTER_MODEL = "MyKlipper 0.4 nozzle";
|
||||
const char *PresetBundle::ORCA_DEFAULT_PRINTER_VARIANT = "0.4";
|
||||
const char *PresetBundle::ORCA_DEFAULT_FILAMENT = "Generic PLA @System";
|
||||
// SM_FEATURE: add Snapmaker machine as default
|
||||
const char* PresetBundle::SM_BUNDLE = "Snapmaker";
|
||||
const char* PresetBundle::SM_DEFAULT_PRINTER_MODEL = "Snapmaker U1(0.4 nozzle)";
|
||||
const char* PresetBundle::SM_DEFAULT_PRINTER_VARIANT = "0.4";
|
||||
const char* PresetBundle::SM_DEFAULT_FILAMENT = "Snapmaker PLA SnapSpeed";
|
||||
const char *PresetBundle::ORCA_FILAMENT_LIBRARY = "OrcaFilamentLibrary";
|
||||
|
||||
PresetBundle::PresetBundle()
|
||||
@@ -172,6 +172,7 @@ void PresetBundle::setup_directories()
|
||||
data_dir / "ota",
|
||||
data_dir / PRESET_SYSTEM_DIR,
|
||||
data_dir / PRESET_USER_DIR,
|
||||
data_dir / PRESET_WEB_DIR,
|
||||
// Store the print/filament/printer presets at the same location as the upstream Slic3r.
|
||||
//data_dir / PRESET_SYSTEM_DIR / PRESET_PRINT_NAME,
|
||||
//data_dir / PRESET_SYSTEM_DIR / PRESET_FILAMENT_NAME,
|
||||
@@ -368,7 +369,7 @@ bool PresetBundle::use_bbl_device_tab() {
|
||||
|
||||
bool PresetBundle::backup_user_folder() const
|
||||
{
|
||||
const std::string backup_folderpath = data_dir() + "/" + (boost::format("user_backup-v%1%") % SoftFever_VERSION).str();
|
||||
const std::string backup_folderpath = data_dir() + "/" + (boost::format("user_backup-v%1%") % Snapmaker_VERSION).str();
|
||||
|
||||
// Check if backup file already exists
|
||||
if (boost::filesystem::exists(boost::filesystem::path(backup_folderpath)))
|
||||
@@ -1118,7 +1119,7 @@ void PresetBundle::remove_users_preset(AppConfig &config, std::map<std::string,
|
||||
}
|
||||
|
||||
if (need_reset_printer_preset) {
|
||||
std::string default_printer_model = ORCA_DEFAULT_PRINTER_MODEL;
|
||||
std::string default_printer_model = SM_DEFAULT_PRINTER_MODEL;
|
||||
std::string default_printer_name;
|
||||
for (auto it = printers.begin(); it != printers.end(); it++) {
|
||||
if (it->config.has("printer_model")) {
|
||||
@@ -1833,6 +1834,29 @@ void PresetBundle::export_selections(AppConfig &config)
|
||||
}
|
||||
|
||||
// BBS
|
||||
void PresetBundle::update_num_filaments(unsigned int to_del_filament_id)
|
||||
{
|
||||
unsigned old_filament_count = this->filament_presets.size();
|
||||
assert(to_del_flament_id < old_filament_count);
|
||||
filament_presets.erase(filament_presets.begin() + to_del_filament_id);
|
||||
|
||||
ConfigOptionStrings* filament_color = project_config.option<ConfigOptionStrings>("filament_colour");
|
||||
|
||||
if (filament_color->values.size() > to_del_filament_id) {
|
||||
filament_color->values.erase(filament_color->values.begin() + to_del_filament_id);
|
||||
} else {
|
||||
filament_color->values.resize(to_del_filament_id);
|
||||
}
|
||||
|
||||
if (ams_multi_color_filment.size() > to_del_filament_id) {
|
||||
ams_multi_color_filment.erase(ams_multi_color_filment.begin() + to_del_filament_id);
|
||||
} else {
|
||||
ams_multi_color_filment.resize(to_del_filament_id);
|
||||
}
|
||||
|
||||
update_multi_material_filament_presets(to_del_filament_id);
|
||||
}
|
||||
|
||||
void PresetBundle::set_num_filaments(unsigned int n, std::vector<std::string> new_colors) {
|
||||
int old_filament_count = this->filament_presets.size();
|
||||
if (n > old_filament_count && old_filament_count != 0)
|
||||
@@ -2053,6 +2077,37 @@ bool PresetBundle::check_filament_temp_equation_by_printer_type_and_nozzle_for_m
|
||||
return is_equation;
|
||||
}
|
||||
|
||||
Preset *PresetBundle::get_similar_printer_preset(std::string printer_model, std::string printer_variant)
|
||||
{
|
||||
if (printer_model.empty())
|
||||
printer_model = printers.get_selected_preset().config.opt_string("printer_model");
|
||||
auto printer_variant_old = printers.get_selected_preset().config.opt_string("printer_variant");
|
||||
std::map<std::string, Preset*> printer_presets;
|
||||
for (auto &preset : printers.m_presets) {
|
||||
if (printer_variant.empty() && !preset.is_system)
|
||||
continue;
|
||||
if (preset.config.opt_string("printer_model") == printer_model)
|
||||
printer_presets.insert({preset.name, &preset});
|
||||
}
|
||||
if (printer_presets.empty())
|
||||
return nullptr;
|
||||
auto prefer_printer = printers.get_selected_preset().name;
|
||||
if (!printer_variant.empty())
|
||||
boost::replace_all(prefer_printer, printer_variant_old, printer_variant);
|
||||
else if (auto n = prefer_printer.find(printer_variant_old); n != std::string::npos)
|
||||
prefer_printer = printer_model + " " + printer_variant_old + prefer_printer.substr(n + printer_variant_old.length());
|
||||
if (auto iter = printer_presets.find(prefer_printer); iter != printer_presets.end()) {
|
||||
return iter->second;
|
||||
}
|
||||
if (printer_variant.empty())
|
||||
printer_variant = printer_variant_old;
|
||||
for (auto& preset : printer_presets) {
|
||||
if (preset.second->config.opt_string("printer_variant") == printer_variant)
|
||||
return preset.second;
|
||||
}
|
||||
return printer_presets.begin()->second;
|
||||
}
|
||||
|
||||
//BBS: check whether this is the only edited filament
|
||||
bool PresetBundle::is_the_only_edited_filament(unsigned int filament_index)
|
||||
{
|
||||
@@ -3156,7 +3211,7 @@ std::pair<PresetsConfigSubstitutions, size_t> PresetBundle::load_vendor_configs_
|
||||
return std::make_pair(std::move(substitutions), presets_loaded);
|
||||
}
|
||||
|
||||
void PresetBundle::update_multi_material_filament_presets()
|
||||
void PresetBundle::update_multi_material_filament_presets(size_t to_delete_filament_id)
|
||||
{
|
||||
if (printers.get_edited_preset().printer_technology() != ptFFF)
|
||||
return;
|
||||
|
||||
@@ -113,8 +113,9 @@ public:
|
||||
void export_selections(AppConfig &config);
|
||||
|
||||
// BBS
|
||||
void set_num_filaments(unsigned int n, std::vector<std::string> new_colors);
|
||||
void set_num_filaments(unsigned int n, std::string new_col = "");
|
||||
void set_num_filaments(unsigned int n, std::vector<std::string> new_colors);
|
||||
void update_num_filaments(unsigned int to_del_filament_id);
|
||||
unsigned int sync_ams_list(unsigned int & unknowns);
|
||||
//BBS: check whether this is the only edited filament
|
||||
bool is_the_only_edited_filament(unsigned int filament_index);
|
||||
@@ -135,6 +136,8 @@ public:
|
||||
std::string & nozzle_temp_max,
|
||||
std::string & preset_setting_id);
|
||||
|
||||
Preset * get_similar_printer_preset(std::string printer_model, std::string printer_variant);
|
||||
|
||||
PresetCollection prints;
|
||||
PresetCollection sla_prints;
|
||||
PresetCollection filaments;
|
||||
@@ -149,6 +152,10 @@ public:
|
||||
// BBS: ams
|
||||
std::map<int, DynamicPrintConfig> filament_ams_list;
|
||||
std::vector<std::vector<std::string>> ams_multi_color_filment;
|
||||
|
||||
// Snapmaker
|
||||
std::map<int, std::pair<std::string, std::string>> machine_filaments;
|
||||
|
||||
// Calibrate
|
||||
Preset const * calibrate_printer = nullptr;
|
||||
std::set<Preset const *> calibrate_filaments;
|
||||
@@ -241,7 +248,7 @@ public:
|
||||
|
||||
// Read out the number of extruders from an active printer preset,
|
||||
// update size and content of filament_presets.
|
||||
void update_multi_material_filament_presets();
|
||||
void update_multi_material_filament_presets(size_t to_delete_filament_id = size_t(-1));
|
||||
|
||||
// Update the is_compatible flag of all print and filament presets depending on whether they are marked
|
||||
// as compatible with the currently selected printer (and print in case of filament presets).
|
||||
@@ -268,11 +275,11 @@ public:
|
||||
std::pair<PresetsConfigSubstitutions, std::string> load_system_filaments_json(ForwardCompatibilitySubstitutionRule compatibility_rule);
|
||||
VendorProfile get_custom_vendor_models() const;
|
||||
|
||||
//orca: add 'custom' as default
|
||||
static const char *ORCA_DEFAULT_BUNDLE;
|
||||
static const char *ORCA_DEFAULT_PRINTER_MODEL;
|
||||
static const char *ORCA_DEFAULT_PRINTER_VARIANT;
|
||||
static const char *ORCA_DEFAULT_FILAMENT;
|
||||
// SM_FEATURE: add Snapmaker machine as default
|
||||
static const char *SM_BUNDLE;
|
||||
static const char* SM_DEFAULT_PRINTER_MODEL;
|
||||
static const char* SM_DEFAULT_PRINTER_VARIANT;
|
||||
static const char* SM_DEFAULT_FILAMENT;
|
||||
static const char *ORCA_FILAMENT_LIBRARY;
|
||||
|
||||
|
||||
|
||||
@@ -164,6 +164,7 @@ bool Print::invalidate_state_by_config_options(const ConfigOptionResolver & /* n
|
||||
"slow_down_layer_time",
|
||||
"standby_temperature_delta",
|
||||
"preheat_time",
|
||||
"delta_temperature",
|
||||
"preheat_steps",
|
||||
"machine_start_gcode",
|
||||
"filament_start_gcode",
|
||||
@@ -481,6 +482,13 @@ std::vector<unsigned int> Print::extruders(bool conside_custom_gcode) const
|
||||
}
|
||||
}
|
||||
|
||||
// The wipe tower extruder can also be set. When the wipe tower is enabled and it will be generated,
|
||||
// append its extruder into the list too.
|
||||
if (has_wipe_tower() && config().wipe_tower_filament != 0 && extruders.size() > 1) {
|
||||
assert(config().wipe_tower_filament > 0 && config().wipe_tower_filament < int(config().nozzle_diameter.size()));
|
||||
extruders.emplace_back(config().wipe_tower_filament - 1); // the config value is 1-based
|
||||
}
|
||||
|
||||
sort_remove_duplicates(extruders);
|
||||
return extruders;
|
||||
}
|
||||
|
||||
@@ -235,7 +235,12 @@ static t_config_option_keys print_config_diffs(
|
||||
if (opt_new == nullptr)
|
||||
//FIXME This may happen when executing some test cases.
|
||||
continue;
|
||||
const ConfigOption *opt_new_filament = std::binary_search(extruder_retract_keys.begin(), extruder_retract_keys.end(), opt_key) ? new_full_config.option(filament_prefix + opt_key) : nullptr;
|
||||
|
||||
auto iter = std::find(extruder_retract_keys.begin(), extruder_retract_keys.end(), opt_key);
|
||||
|
||||
// const ConfigOption *opt_new_filament = std::binary_search(extruder_retract_keys.begin(), extruder_retract_keys.end(), opt_key) ? new_full_config.option(filament_prefix + opt_key) : nullptr;
|
||||
const ConfigOption* opt_new_filament = (iter == extruder_retract_keys.end()) ? nullptr :
|
||||
new_full_config.option(filament_prefix + opt_key);
|
||||
if (opt_new_filament != nullptr && ! opt_new_filament->is_nil()) {
|
||||
// An extruder retract override is available at some of the filament presets.
|
||||
bool overriden = opt_new->overriden_by(opt_new_filament);
|
||||
|
||||
@@ -67,7 +67,7 @@ std::string PrintBase::output_filename(const std::string &format, const std::str
|
||||
DynamicConfig cfg;
|
||||
if (config_override != nullptr)
|
||||
cfg = *config_override;
|
||||
cfg.set_key_value("version", new ConfigOptionString(std::string(SoftFever_VERSION)));
|
||||
cfg.set_key_value("version", new ConfigOptionString(std::string(Snapmaker_VERSION)));
|
||||
PlaceholderParser::update_timestamp(cfg);
|
||||
PlaceholderParser::update_user_name(cfg);
|
||||
this->update_object_placeholders(cfg, default_ext);
|
||||
|
||||
+109
-39
@@ -585,7 +585,7 @@ void PrintConfigDef::init_common_params()
|
||||
|
||||
def = this->add("print_host", coString);
|
||||
def->label = L("Hostname, IP or URL");
|
||||
def->tooltip = L("Orca Slicer can upload G-code files to a printer host. This field should contain "
|
||||
def->tooltip = L("Snapmaker Orca can upload G-code files to a printer host. This field should contain "
|
||||
"the hostname, IP address or URL of the printer host instance. "
|
||||
"Print host behind HAProxy with basic auth enabled can be accessed by putting the user name and password into the URL "
|
||||
"in the following format: https://username:password@your-octopi-address/");
|
||||
@@ -684,7 +684,7 @@ void PrintConfigDef::init_fff_params()
|
||||
def = this->add("reduce_crossing_wall", coBool);
|
||||
def->label = L("Avoid crossing walls");
|
||||
def->category = L("Quality");
|
||||
def->tooltip = L("Detour to avoid traveling across walls, which may cause blobs on the surface.");
|
||||
def->tooltip = L("Detour and avoid to travel across wall which may cause blob on surface");
|
||||
def->mode = comAdvanced;
|
||||
def->set_default_value(new ConfigOptionBool(false));
|
||||
|
||||
@@ -1764,6 +1764,7 @@ void PrintConfigDef::init_fff_params()
|
||||
def->enum_labels.push_back("4");
|
||||
def->enum_labels.push_back("5");
|
||||
def->mode = comAdvanced;
|
||||
def->set_default_value(new ConfigOptionInt{0});
|
||||
|
||||
def = this->add("extruder_clearance_height_to_rod", coFloat);
|
||||
def->label = L("Height to rod");
|
||||
@@ -1817,7 +1818,7 @@ void PrintConfigDef::init_fff_params()
|
||||
def->tooltip = L(
|
||||
"This option sets the max point for the allowed bed mesh area. Due to the probe's XY offset, most printers are unable to probe the "
|
||||
"entire bed. To ensure the probe point does not go outside the bed area, the minimum and maximum points of the bed mesh should be "
|
||||
"set appropriately. OrcaSlicer ensures that adaptive_bed_mesh_min/adaptive_bed_mesh_max values do not exceed these min/max "
|
||||
"set appropriately. Snapmaker_Orca ensures that adaptive_bed_mesh_min/adaptive_bed_mesh_max values do not exceed these min/max "
|
||||
"points. This information can usually be obtained from your printer manufacturer. The default setting is (99999, 99999), which "
|
||||
"means there are no limits, thus allowing probing across the entire bed.");
|
||||
def->sidetext = "mm"; // milimeters, don't need translation
|
||||
@@ -2002,6 +2003,18 @@ void PrintConfigDef::init_fff_params()
|
||||
def->mode = comAdvanced;
|
||||
def->set_default_value(new ConfigOptionStrings{ "#F2754E" });
|
||||
|
||||
def = this->add("thumb0", coStrings);
|
||||
def->label = L("small thumb");
|
||||
def->tooltip = L("first small thumb");
|
||||
def->mode = comSimple;
|
||||
def->set_default_value(new ConfigOptionString{""});
|
||||
|
||||
def = this->add("thumb1", coStrings);
|
||||
def->label = L("big thumb");
|
||||
def->tooltip = L("first big thumb");
|
||||
def->mode = comSimple;
|
||||
def->set_default_value(new ConfigOptionString{""});
|
||||
|
||||
// PS
|
||||
def = this->add("filament_notes", coStrings);
|
||||
def->label = L("Filament notes");
|
||||
@@ -2058,6 +2071,12 @@ void PrintConfigDef::init_fff_params()
|
||||
def->mode = comAdvanced;
|
||||
def->set_default_value(new ConfigOptionFloat { 0. });
|
||||
|
||||
def = this->add("tool_change_temprature_wait", coBool);
|
||||
def->label = L("Wait for the temperature when changing tools");
|
||||
def->tooltip = L("It will use the M109 instead of M104 T[target] after changing tools if this is set to true");
|
||||
def->mode = comAdvanced;
|
||||
def->set_default_value(new ConfigOptionBool(true));
|
||||
|
||||
|
||||
def = this->add("filament_diameter", coFloats);
|
||||
def->label = L("Diameter");
|
||||
@@ -2201,7 +2220,7 @@ void PrintConfigDef::init_fff_params()
|
||||
def->label = L("Minimal purge on wipe tower");
|
||||
def->tooltip = L("After a tool change, the exact position of the newly loaded filament inside "
|
||||
"the nozzle may not be known, and the filament pressure is likely not yet stable. "
|
||||
"Before purging the print head into an infill or a sacrificial object, Orca Slicer will always prime "
|
||||
"Before purging the print head into an infill or a sacrificial object, Snapmaker Orca will always prime "
|
||||
"this amount of material into the wipe tower to produce successive infill or sacrificial object extrusions reliably.");
|
||||
def->sidetext = u8"mm³"; // cubic milimeters, don't need translation
|
||||
def->min = 0;
|
||||
@@ -3889,7 +3908,7 @@ void PrintConfigDef::init_fff_params()
|
||||
|
||||
def = this->add("host_type", coEnum);
|
||||
def->label = L("Host Type");
|
||||
def->tooltip = L("Orca Slicer can upload G-code files to a printer host. This field must contain "
|
||||
def->tooltip = L("Snapmaker Orca can upload G-code files to a printer host. This field must contain "
|
||||
"the kind of the host.");
|
||||
def->enum_keys_map = &ConfigOptionEnum<PrintHostType>::get_enum_values();
|
||||
def->enum_values.push_back("prusalink");
|
||||
@@ -4094,7 +4113,7 @@ void PrintConfigDef::init_fff_params()
|
||||
def->tooltip = L("If you want to process the output G-code through custom scripts, "
|
||||
"just list their absolute paths here. Separate multiple scripts with a semicolon. "
|
||||
"Scripts will be passed the absolute path to the G-code file as the first argument, "
|
||||
"and they can access the Orca Slicer config settings by reading environment variables.");
|
||||
"and they can access the Snapmaker Orca config settings by reading environment variables.");
|
||||
def->gui_flags = "serialized";
|
||||
def->multiline = true;
|
||||
def->full_width = true;
|
||||
@@ -4240,7 +4259,7 @@ void PrintConfigDef::init_fff_params()
|
||||
def->set_default_value(new ConfigOptionFloats {18});
|
||||
|
||||
def = this->add("retract_length_toolchange", coFloats);
|
||||
def->label = L("Length");
|
||||
def->label = L("Retraction Length (Toolchange)");
|
||||
//def->full_label = L("Retraction Length (Toolchange)");
|
||||
def->full_label = "Retraction Length (Toolchange)";
|
||||
//def->tooltip = L("When retraction is triggered before changing tool, filament is pulled back "
|
||||
@@ -4292,6 +4311,11 @@ void PrintConfigDef::init_fff_params()
|
||||
def->mode = comAdvanced;
|
||||
def->set_default_value(new ConfigOptionEnumsGeneric{ ZHopType::zhtSlope });
|
||||
|
||||
def = this->add("z_hop_when_prime", coBools);
|
||||
def->label = L("Z hop when moving to tower");
|
||||
def->mode = comAdvanced;
|
||||
def->set_default_value(new ConfigOptionBools{true});
|
||||
|
||||
def = this->add("travel_slope", coFloats);
|
||||
def->label = L("Traveling angle");
|
||||
def->tooltip = L("Traveling angle for Slope and Spiral Z-hop type. Setting it to 90° results in Normal Lift.");
|
||||
@@ -4774,6 +4798,18 @@ void PrintConfigDef::init_fff_params()
|
||||
def->mode = comAdvanced;
|
||||
def->set_default_value(new ConfigOptionFloat(30.0));
|
||||
|
||||
|
||||
def = this->add("delta_temperature", coInt);
|
||||
def->label = L("Preheat delta temperature");
|
||||
def->tooltip = L("Allow user to set the Preheat temperature. If target temperature is 220 and Preheat delta temperature is -30, then "
|
||||
"the preheat temperature will be 190");
|
||||
def->sidetext = "∆°C";
|
||||
def->min = -50;
|
||||
def->max = 50;
|
||||
def->mode = comAdvanced;
|
||||
def->set_default_value(new ConfigOptionInt(0));
|
||||
|
||||
|
||||
def = this->add("preheat_steps", coInt);
|
||||
def->label = L("Preheat steps");
|
||||
def->tooltip = L("Insert multiple preheat commands (e.g. M104.1). Only useful for Prusa XL. For other printers, please set it to 1.");
|
||||
@@ -4828,6 +4864,24 @@ void PrintConfigDef::init_fff_params()
|
||||
def->mode = comAdvanced;
|
||||
def->set_default_value(new ConfigOptionBool(true));
|
||||
|
||||
def = this->add("ramming_line_width_ratio", coFloat);
|
||||
def->label = L("Ramming line width ratio");
|
||||
def->tooltip = L(
|
||||
"This is used to decide the line width of wipe tower when ramming, ramming line width = [this ratio] * extruder * 1.25");
|
||||
def->mode = comAdvanced;
|
||||
def->set_default_value(new ConfigOptionFloat(2.0));
|
||||
|
||||
def = this->add("enable_change_pressure_when_wiping", coBool);
|
||||
def->label = L("Enable change pressure advance when wiping");
|
||||
def->tooltip = L("If it's set to false, the pressure advance value will not be changed.");
|
||||
def->mode = comAdvanced;
|
||||
def->set_default_value(new ConfigOptionBool(true));
|
||||
|
||||
def = this->add("ramming_pressure_advance_value", coFloat);
|
||||
def->label = L("Pressure advance value when ramming");
|
||||
def->tooltip = L("Set_Pressure_advance [this value] when ramming on wipe tower");
|
||||
def->mode = comAdvanced;
|
||||
def->set_default_value(new ConfigOptionFloat(0.0));
|
||||
|
||||
def = this->add("wipe_tower_no_sparse_layers", coBool);
|
||||
def->label = L("No sparse layers (beta)");
|
||||
@@ -6030,7 +6084,9 @@ void PrintConfigDef::init_fff_params()
|
||||
// percents
|
||||
"retract_before_wipe",
|
||||
"long_retractions_when_cut",
|
||||
"retraction_distances_when_cut"
|
||||
"retraction_distances_when_cut",
|
||||
"retract_length_toolchange",
|
||||
"retract_restart_extra_toolchange"
|
||||
}) {
|
||||
auto it_opt = options.find(opt_key);
|
||||
assert(it_opt != options.end());
|
||||
@@ -6076,7 +6132,7 @@ void PrintConfigDef::init_extruder_option_keys()
|
||||
// ConfigOptionFloats, ConfigOptionPercents, ConfigOptionBools, ConfigOptionStrings
|
||||
m_extruder_option_keys = {
|
||||
"nozzle_diameter", "min_layer_height", "max_layer_height", "extruder_offset",
|
||||
"retraction_length", "z_hop", "z_hop_types", "travel_slope", "retract_lift_above", "retract_lift_below", "retract_lift_enforce", "retraction_speed", "deretraction_speed",
|
||||
"retraction_length", "z_hop", "z_hop_types", "z_hop_when_prime", "travel_slope", "retract_lift_above", "retract_lift_below", "retract_lift_enforce", "retraction_speed", "deretraction_speed",
|
||||
"retract_before_wipe", "retract_restart_extra", "retraction_minimum_travel", "wipe", "wipe_distance",
|
||||
"retract_when_changing_layer", "retract_length_toolchange", "retract_restart_extra_toolchange", "extruder_colour",
|
||||
"default_filament_profile","retraction_distances_when_cut","long_retractions_when_cut"
|
||||
@@ -6099,7 +6155,10 @@ void PrintConfigDef::init_extruder_option_keys()
|
||||
"wipe",
|
||||
"wipe_distance",
|
||||
"z_hop",
|
||||
"z_hop_types"
|
||||
"z_hop_types",
|
||||
"z_hop_when_prime",
|
||||
"retract_length_toolchange",
|
||||
"retract_restart_extra_toolchange"
|
||||
};
|
||||
assert(std::is_sorted(m_extruder_retract_keys.begin(), m_extruder_retract_keys.end()));
|
||||
}
|
||||
@@ -6130,7 +6189,9 @@ void PrintConfigDef::init_filament_option_keys()
|
||||
"wipe",
|
||||
"wipe_distance",
|
||||
"z_hop",
|
||||
"z_hop_types"
|
||||
"z_hop_types",
|
||||
"retract_length_toolchange",
|
||||
"retract_restart_extra_toolchange",
|
||||
};
|
||||
assert(std::is_sorted(m_filament_retract_keys.begin(), m_filament_retract_keys.end()));
|
||||
}
|
||||
@@ -6923,7 +6984,6 @@ void PrintConfigDef::handle_legacy(t_config_option_key &opt_key, std::string &va
|
||||
"extruder_type",
|
||||
"internal_bridge_support_thickness","extruder_clearance_max_radius", "top_area_threshold", "reduce_wall_solid_infill","filament_load_time","filament_unload_time",
|
||||
"smooth_coefficient", "overhang_totally_speed", "silent_mode",
|
||||
"overhang_speed_classic",
|
||||
};
|
||||
|
||||
if (ignore.find(opt_key) != ignore.end()) {
|
||||
@@ -7056,6 +7116,14 @@ void DynamicPrintConfig::normalize_fdm(int used_filaments)
|
||||
}
|
||||
}
|
||||
|
||||
if (this->has("wipe_tower_filament")) {
|
||||
// If invalid, replace with 0.
|
||||
int extruder = this->opt<ConfigOptionInt>("wipe_tower_filament")->value;
|
||||
int num_extruders = this->opt<ConfigOptionFloats>("nozzle_diameter")->size();
|
||||
if (extruder < 0 || extruder > num_extruders)
|
||||
this->option("wipe_tower_filament")->setInt(0);
|
||||
}
|
||||
|
||||
if (!this->has("solid_infill_filament") && this->has("sparse_infill_filament"))
|
||||
this->option("solid_infill_filament", true)->setInt(this->option("sparse_infill_filament")->getInt());
|
||||
|
||||
@@ -7894,8 +7962,8 @@ CLIMiscConfigDef::CLIMiscConfigDef()
|
||||
|
||||
def = this->add("config_compatibility", coEnum);
|
||||
def->label = L("Forward-compatibility rule when loading configurations from config files and project files (3MF, AMF).");
|
||||
def->tooltip = L("This version of OrcaSlicer may not understand configurations produced by the newest OrcaSlicer versions. "
|
||||
"For example, newer OrcaSlicer may extend the list of supported firmware flavors. One may decide to "
|
||||
def->tooltip = L("This version of Snapmaker_Orca may not understand configurations produced by the newest OrcaSlicer versions. "
|
||||
"For example, newer may extend the list of supported firmware flavors. One may decide to "
|
||||
"bail out or to substitute an unknown value with a default silently or verbosely.");
|
||||
def->enum_keys_map = &ConfigOptionEnum<ForwardCompatibilitySubstitutionRule>::get_enum_values();
|
||||
def->enum_values.push_back("disable");
|
||||
@@ -7971,7 +8039,7 @@ CLIMiscConfigDef::CLIMiscConfigDef()
|
||||
def = this->add("single_instance", coBool);
|
||||
def->label = L("Single instance mode");
|
||||
def->tooltip = L("If enabled, the command line arguments are sent to an existing instance of GUI OrcaSlicer, "
|
||||
"or an existing OrcaSlicer window is activated. "
|
||||
"or an existing Snapmaker_Orca window is activated. "
|
||||
"Overrides the \"single_instance\" configuration value from application preferences.");*/
|
||||
|
||||
/*
|
||||
@@ -8388,7 +8456,7 @@ static std::map<t_custom_gcode_key, t_config_option_keys> s_CustomGcodeSpecificP
|
||||
"new_retract_length_toolchange", "old_filament_e_feedrate", "old_filament_temp", "old_retract_length",
|
||||
"old_retract_length_toolchange", "relative_e_axis", "second_flush_volume", "toolchange_count", "toolchange_z",
|
||||
"travel_point_1_x", "travel_point_1_y", "travel_point_2_x", "travel_point_2_y", "travel_point_3_x",
|
||||
"travel_point_3_y", "x_after_toolchange", "y_after_toolchange", "z_after_toolchange"}},
|
||||
"travel_point_3_y", "x_after_toolchange", "y_after_toolchange", "z_after_toolchange", "next_wipe_x", "next_wipe_y"}},
|
||||
{"change_extrusion_role_gcode", {"layer_num", "layer_z", "extrusion_role", "last_extrusion_role"}},
|
||||
{"printing_by_object_gcode", {}},
|
||||
{"machine_pause_gcode", {}},
|
||||
@@ -8430,29 +8498,31 @@ CustomGcodeSpecificConfigDef::CustomGcodeSpecificConfigDef()
|
||||
new_def("relative_e_axis", coBool, "Relative e-axis", "Indicates if relative positioning is being used.");
|
||||
new_def("toolchange_count", coInt, "Toolchange count", "The number of toolchanges throught the print.");
|
||||
new_def("fan_speed", coNone, "", ""); //Option is no longer used and is zeroed by placeholder parser for compatability
|
||||
new_def("old_retract_length", coFloat, "Old retract length", "The retraction length of the previous filament.");
|
||||
new_def("new_retract_length", coFloat, "New retract length", "The retraction lenght of the new filament.");
|
||||
new_def("old_retract_length_toolchange", coFloat, "Old retract length toolchange", "The toolchange retraction length of the previous filament.");
|
||||
new_def("new_retract_length_toolchange", coFloat, "New retract length toolchange", "The toolchange retraction length of the new filament.");
|
||||
new_def("old_filament_temp", coInt, "Old filament temp", "The old filament temp.");
|
||||
new_def("new_filament_temp", coInt, "New filament temp", "The new filament temp.");
|
||||
new_def("x_after_toolchange", coFloat, "X after toolchange", "The X pos after toolchange.");
|
||||
new_def("y_after_toolchange", coFloat, "Y after toolchange", "The Y pos after toolchange.");
|
||||
new_def("z_after_toolchange", coFloat, "Z after toolchange", "The Z pos after toolchange.");
|
||||
new_def("first_flush_volume", coFloat, "First flush volume", "The first flush volume.");
|
||||
new_def("second_flush_volume", coFloat, "Second flush volume", "The second flush volume.");
|
||||
new_def("old_filament_e_feedrate", coInt, "Old filament e feedrate", "The old filament extruder feedrate.");
|
||||
new_def("new_filament_e_feedrate", coInt, "New filament e feedrate", "The new filament extruder feedrate.");
|
||||
new_def("travel_point_1_x", coFloat, "Travel point 1 X", "The travel point 1 X.");
|
||||
new_def("travel_point_1_y", coFloat, "Travel point 1 Y", "The travel point 1 Y.");
|
||||
new_def("travel_point_2_x", coFloat, "Travel point 2 X", "The travel point 2 X.");
|
||||
new_def("travel_point_2_y", coFloat, "Travel point 2 Y", "The travel point 2 Y.");
|
||||
new_def("travel_point_3_x", coFloat, "Travel point 3 X", "The travel point 3 X.");
|
||||
new_def("travel_point_3_y", coFloat, "Travel point 3 Y", "The travel point 3 Y.");
|
||||
new_def("flush_length_1", coFloat, "Flush Length 1", "The first flush length.");
|
||||
new_def("flush_length_2", coFloat, "Flush Length 2", "The second flush length.");
|
||||
new_def("flush_length_3", coFloat, "Flush Length 3", "The third flush length.");
|
||||
new_def("flush_length_4", coFloat, "Flush Length 4", "The fourth flush length.");
|
||||
new_def("old_retract_length", coFloat, "Old retract length", "The retraction length of the previous filament");
|
||||
new_def("new_retract_length", coFloat, "New retract length", "The retraction lenght of the new filament");
|
||||
new_def("old_retract_length_toolchange", coFloat, "Old retract length toolchange", "The toolchange retraction length of the previous filament");
|
||||
new_def("new_retract_length_toolchange", coFloat, "New retract length toolchange", "The toolchange retraction length of the new filament");
|
||||
new_def("old_filament_temp", coInt, "Old filament temp", "The old filament temp");
|
||||
new_def("new_filament_temp", coInt, "New filament temp", "The new filament temp");
|
||||
new_def("x_after_toolchange", coFloat, "X after toolchange", "The x pos after toolchange");
|
||||
new_def("y_after_toolchange", coFloat, "Y after toolchange", "The y pos after toolchange");
|
||||
new_def("z_after_toolchange", coFloat, "Z after toolchange", "The z pos after toolchange");
|
||||
new_def("first_flush_volume", coFloat, "First flush volume", "The first flush volume");
|
||||
new_def("second_flush_volume", coFloat, "Second flush volume", "The second flush volume");
|
||||
new_def("old_filament_e_feedrate", coInt, "Old filament e feedrate", "The old filament extruder feedrate");
|
||||
new_def("new_filament_e_feedrate", coInt, "New filament e feedrate", "The new filament extruder feedrate");
|
||||
new_def("travel_point_1_x", coFloat, "Travel point 1 x", "The travel point 1 x");
|
||||
new_def("travel_point_1_y", coFloat, "Travel point 1 y", "The travel point 1 y");
|
||||
new_def("travel_point_2_x", coFloat, "Travel point 2 x", "The travel point 2 x");
|
||||
new_def("travel_point_2_y", coFloat, "Travel point 2 y", "The travel point 2 y");
|
||||
new_def("travel_point_3_x", coFloat, "Travel point 3 x", "The travel point 3 x");
|
||||
new_def("travel_point_3_y", coFloat, "Travel point 3 y", "The travel point 3 y");
|
||||
new_def("flush_length_1", coFloat, "Flush Length 1", "The first flush length");
|
||||
new_def("flush_length_2", coFloat, "Flush Length 2", "The second flush length");
|
||||
new_def("flush_length_3", coFloat, "Flush Length 3", "The third flush length");
|
||||
new_def("flush_length_4", coFloat, "Flush Length 4", "The fourth flush length");
|
||||
new_def("next_wipe_x", coFloat, "Next Wipe X", "For Snapmaker Artision, next x after toolchange");
|
||||
new_def("next_wipe_y", coFloat, "Next Wipe Y", "For Snapmaker Artision, next y after toolchange");
|
||||
|
||||
// change_extrusion_role_gcode
|
||||
std::string extrusion_role_types = "Possible Values:\n[\"Perimeter\", \"ExternalPerimeter\", "
|
||||
|
||||
@@ -57,7 +57,7 @@ enum class NoiseType {
|
||||
};
|
||||
|
||||
enum PrintHostType {
|
||||
htPrusaLink, htPrusaConnect, htOctoPrint, htDuet, htFlashAir, htAstroBox, htRepetier, htMKS, htESP3D, htCrealityPrint, htObico, htFlashforge, htSimplyPrint, htElegooLink
|
||||
htPrusaLink, htPrusaConnect, htOctoPrint, htDuet, htFlashAir, htAstroBox, htRepetier, htMKS, htESP3D, htCrealityPrint, htObico, htFlashforge, htSimplyPrint, htElegooLink, htMoonRaker_mqtt, htMoonRaker,
|
||||
};
|
||||
|
||||
enum AuthorizationType {
|
||||
@@ -337,6 +337,13 @@ enum ZHopType {
|
||||
zhtCount
|
||||
};
|
||||
|
||||
enum FilamentMapMode {
|
||||
fmmAutoForFlush,
|
||||
fmmAutoForMatch,
|
||||
fmmManual,
|
||||
fmmDefault
|
||||
};
|
||||
|
||||
enum NozzleVolumeType {
|
||||
nvtNormal = 0,
|
||||
nvtBigTraffic,
|
||||
@@ -1193,6 +1200,7 @@ PRINT_CONFIG_CLASS_DEFINE(
|
||||
((ConfigOptionBools, long_retractions_when_cut))
|
||||
((ConfigOptionFloats, z_hop))
|
||||
// BBS
|
||||
((ConfigOptionBools, z_hop_when_prime))
|
||||
((ConfigOptionEnumsGeneric, z_hop_types))
|
||||
((ConfigOptionFloats, travel_slope))
|
||||
((ConfigOptionFloats, retract_lift_above))
|
||||
@@ -1240,6 +1248,7 @@ PRINT_CONFIG_CLASS_DEFINE(
|
||||
((ConfigOptionFloat, extra_loading_move))
|
||||
((ConfigOptionFloat, machine_load_filament_time))
|
||||
((ConfigOptionFloat, machine_tool_change_time))
|
||||
((ConfigOptionBool, tool_change_temprature_wait))
|
||||
((ConfigOptionFloat, machine_unload_filament_time))
|
||||
((ConfigOptionFloats, filament_loading_speed))
|
||||
((ConfigOptionFloats, filament_loading_speed_start))
|
||||
@@ -1258,6 +1267,9 @@ PRINT_CONFIG_CLASS_DEFINE(
|
||||
((ConfigOptionFloats, filament_stamping_distance))
|
||||
((ConfigOptionBool, purge_in_prime_tower))
|
||||
((ConfigOptionBool, enable_filament_ramming))
|
||||
((ConfigOptionFloat, ramming_line_width_ratio))
|
||||
((ConfigOptionBool, enable_change_pressure_when_wiping))
|
||||
((ConfigOptionFloat, ramming_pressure_advance_value))
|
||||
((ConfigOptionBool, support_multi_bed_types))
|
||||
|
||||
// Small Area Infill Flow Compensation
|
||||
@@ -1358,6 +1370,7 @@ PRINT_CONFIG_CLASS_DERIVED_DEFINE(
|
||||
((ConfigOptionFloat, spiral_starting_flow_ratio))
|
||||
((ConfigOptionInt, standby_temperature_delta))
|
||||
((ConfigOptionFloat, preheat_time))
|
||||
((ConfigOptionInt, delta_temperature))
|
||||
((ConfigOptionInt, preheat_steps))
|
||||
((ConfigOptionInts, nozzle_temperature))
|
||||
((ConfigOptionBools, wipe))
|
||||
|
||||
@@ -1720,7 +1720,11 @@ TriangleSelector::TriangleSplittingData TriangleSelector::serialize() const {
|
||||
return out.data;
|
||||
}
|
||||
|
||||
void TriangleSelector::deserialize(const TriangleSplittingData& data, bool needs_reset, EnforcerBlockerType max_ebt)
|
||||
void TriangleSelector::deserialize(const TriangleSplittingData& data,
|
||||
bool needs_reset,
|
||||
EnforcerBlockerType max_ebt,
|
||||
EnforcerBlockerType to_delete_filament,
|
||||
EnforcerBlockerType replace_filament)
|
||||
{
|
||||
if (needs_reset)
|
||||
reset(); // dump any current state
|
||||
@@ -1766,11 +1770,37 @@ void TriangleSelector::deserialize(const TriangleSplittingData& data, bool needs
|
||||
int num_of_children = num_of_split_sides == 0 ? 0 : num_of_split_sides + 1;
|
||||
bool is_split = num_of_children != 0;
|
||||
// Only valid if not is_split. Value of the second nibble was subtracted by 3, so it is added back.
|
||||
auto state = is_split ? EnforcerBlockerType::NONE : EnforcerBlockerType((code & 0b1100) == 0b1100 ? next_nibble() + 3 : code >> 2);
|
||||
// auto state = is_split ? EnforcerBlockerType::NONE : EnforcerBlockerType((code & 0b1100) == 0b1100 ? next_nibble() + 3 : code >> 2);
|
||||
auto state = EnforcerBlockerType::NONE;
|
||||
//// BBS
|
||||
//if (state > max_ebt)
|
||||
// state = EnforcerBlockerType::NONE;
|
||||
|
||||
// BBS
|
||||
if (state > max_ebt)
|
||||
if (!is_split) {
|
||||
if ((code & 0b1100) == 0b1100) {
|
||||
int next_code = next_nibble();
|
||||
int num = 0;
|
||||
while (next_code == 0b1111) {
|
||||
num++;
|
||||
next_code = next_nibble();
|
||||
}
|
||||
state = EnforcerBlockerType(next_code + 15 * num + 3); // old:next_nibble() + 3;
|
||||
} else {
|
||||
state = EnforcerBlockerType(code >> 2);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (state == to_delete_filament)
|
||||
state = replace_filament;
|
||||
else if (to_delete_filament != EnforcerBlockerType::NONE && state != EnforcerBlockerType::NONE) {
|
||||
state = state > to_delete_filament ? EnforcerBlockerType((int) state - 1) : state;
|
||||
}
|
||||
|
||||
if (state > max_ebt) {
|
||||
assert(false);
|
||||
state = EnforcerBlockerType::NONE;
|
||||
}
|
||||
|
||||
// Only valid if is_split.
|
||||
int special_side = code >> 2;
|
||||
|
||||
@@ -357,8 +357,10 @@ public:
|
||||
|
||||
// Load serialized data. Assumes that correct mesh is loaded.
|
||||
void deserialize(const TriangleSplittingData& data,
|
||||
bool needs_reset = true,
|
||||
EnforcerBlockerType max_ebt = EnforcerBlockerType::ExtruderMax);
|
||||
bool needs_reset = true,
|
||||
EnforcerBlockerType max_ebt = EnforcerBlockerType::ExtruderMax,
|
||||
EnforcerBlockerType to_delete_filament = EnforcerBlockerType::NONE,
|
||||
EnforcerBlockerType replace_filament = EnforcerBlockerType::NONE);
|
||||
|
||||
// Extract all used facet states from the given TriangleSplittingData.
|
||||
static std::vector<EnforcerBlockerType> extract_used_facet_states(const TriangleSplittingData &data);
|
||||
|
||||
@@ -217,7 +217,7 @@ extern bool is_shapes_dir(const std::string& dir);
|
||||
extern bool is_json_file(const std::string& path);
|
||||
|
||||
// Orca: custom protocal support utils
|
||||
inline bool is_orca_open(const std::string& url) { return boost::starts_with(url, "orcaslicer://open"); }
|
||||
inline bool is_orca_open(const std::string& url) { return boost::starts_with(url, "Snapmaker_Orca://open"); }
|
||||
inline bool is_prusaslicer_open(const std::string& url) { return boost::starts_with(url, "prusaslicer://open"); }
|
||||
inline bool is_bambustudio_open(const std::string& url) { return boost::starts_with(url, "bambustudio://open") || boost::starts_with(url, "bambustudioopen://"); }
|
||||
inline bool is_cura_open(const std::string& url) { return boost::starts_with(url, "cura://open"); }
|
||||
|
||||
@@ -2,10 +2,10 @@
|
||||
#define _libslic3r_h_
|
||||
|
||||
#include "libslic3r_version.h"
|
||||
#define SLIC3R_APP_FULL_NAME "Orca Slicer"
|
||||
#define GCODEVIEWER_APP_NAME "OrcaSlicer G-code Viewer"
|
||||
#define GCODEVIEWER_APP_KEY "OrcaSlicerGcodeViewer"
|
||||
#define GCODEVIEWER_BUILD_ID std::string("OrcaSlicer G-code Viewer-") + std::string(SLIC3R_VERSION) + std::string("-RC")
|
||||
#define SLIC3R_APP_FULL_NAME "Snapmaker Orca"
|
||||
#define GCODEVIEWER_APP_NAME "Snapmaker_Orca G-code Viewer"
|
||||
#define GCODEVIEWER_APP_KEY "Snapmaker_OrcaGcodeViewer"
|
||||
#define GCODEVIEWER_BUILD_ID std::string("Snapmaker_Orca G-code Viewer-") + std::string(SLIC3R_VERSION) + std::string("-RC")
|
||||
|
||||
// this needs to be included early for MSVC (listing it in Build.PL is not enough)
|
||||
#include <memory>
|
||||
|
||||
@@ -4,7 +4,8 @@
|
||||
#define SLIC3R_APP_NAME "@SLIC3R_APP_NAME@"
|
||||
#define SLIC3R_APP_KEY "@SLIC3R_APP_KEY@"
|
||||
#define SLIC3R_VERSION "@SLIC3R_VERSION@"
|
||||
#define SoftFever_VERSION "@SoftFever_VERSION@"
|
||||
#define Snapmaker_VERSION "@Snapmaker_VERSION@"
|
||||
#define MIN_FIRM_VER "@MIN_FIRM_VER@"
|
||||
#ifndef GIT_COMMIT_HASH
|
||||
#define GIT_COMMIT_HASH "0000000" // 0000000 means uninitialized
|
||||
#endif
|
||||
|
||||
@@ -337,7 +337,7 @@ void set_log_path_and_level(const std::string& file, unsigned int level)
|
||||
}
|
||||
#endif
|
||||
|
||||
//BBS log file at C:\\Users\\[yourname]\\AppData\\Roaming\\OrcaSlicer\\log\\[log_filename].log
|
||||
//BBS log file at C:\\Users\\[yourname]\\AppData\\Roaming\\Snapmaker_Orca\\log\\[log_filename].log
|
||||
auto log_folder = boost::filesystem::path(g_data_dir) / "log";
|
||||
if (!boost::filesystem::exists(log_folder)) {
|
||||
boost::filesystem::create_directory(log_folder);
|
||||
@@ -1162,12 +1162,12 @@ std::string string_printf(const char *format, ...)
|
||||
|
||||
std::string header_slic3r_generated()
|
||||
{
|
||||
return std::string(SLIC3R_APP_NAME " " SoftFever_VERSION);
|
||||
return std::string(SLIC3R_APP_NAME " " Snapmaker_VERSION);
|
||||
}
|
||||
|
||||
std::string header_gcodeviewer_generated()
|
||||
{
|
||||
return std::string(GCODEVIEWER_APP_NAME " " SoftFever_VERSION);
|
||||
return std::string(GCODEVIEWER_APP_NAME " " Snapmaker_VERSION);
|
||||
}
|
||||
|
||||
unsigned get_current_pid()
|
||||
|
||||
@@ -0,0 +1,173 @@
|
||||
# CMakeLists.txt
|
||||
#
|
||||
# Top-level CMake file for the Paho C++ library.
|
||||
#
|
||||
#*******************************************************************************
|
||||
# This is part of the Paho MQTT C++ client library.
|
||||
#
|
||||
# Copyright (c) 2016-2017, Guilherme Maciel Ferreira
|
||||
# Copyright (c) 2017-2023, Frank Pagliughi
|
||||
#
|
||||
# All rights reserved. This program and the accompanying materials
|
||||
# are made available under the terms of the Eclipse Public License v2.0
|
||||
# and Eclipse Distribution License v1.0 which accompany this distribution.
|
||||
#
|
||||
# The Eclipse Public License is available at
|
||||
# http://www.eclipse.org/legal/epl-v20.html
|
||||
# and the Eclipse Distribution License is available at
|
||||
# http://www.eclipse.org/org/documents/edl-v10.php.
|
||||
#
|
||||
# Contributors:
|
||||
# Guilherme Maciel Ferreira - initial version
|
||||
# Frank Pagliughi
|
||||
#*******************************************************************************/
|
||||
|
||||
cmake_minimum_required(VERSION 3.5)
|
||||
|
||||
project(PahoMqttCpp VERSION "1.4.0")
|
||||
|
||||
## --- Build options ---
|
||||
|
||||
if(WIN32)
|
||||
option(PAHO_BUILD_STATIC "Build static library" TRUE)
|
||||
option(PAHO_BUILD_SHARED "Build shared library (DLL)" FALSE)
|
||||
option(PAHO_WITH_SSL "Build SSL-enabled library" ON)
|
||||
else()
|
||||
option(PAHO_BUILD_STATIC "Build static library" TRUE)
|
||||
option(PAHO_BUILD_SHARED "Build shared library" FALSE)
|
||||
option(PAHO_WITH_SSL "Build SSL-enabled library" ON)
|
||||
option(PAHO_BUILD_DEB_PACKAGE "Build debian package" FALSE)
|
||||
endif()
|
||||
|
||||
option(PAHO_BUILD_SAMPLES "Build sample/example programs" FALSE)
|
||||
option(PAHO_BUILD_EXAMPLES "Build sample/example programs" FALSE)
|
||||
option(PAHO_BUILD_TESTS "Build tests (requires Catch2)" FALSE)
|
||||
option(PAHO_BUILD_DOCUMENTATION "Create and install the API documentation (requires Doxygen)" FALSE)
|
||||
option(PAHO_WITH_MQTT_C "Build Paho C from the internal GIT submodule." TRUE)
|
||||
|
||||
if(NOT PAHO_BUILD_SHARED AND NOT PAHO_BUILD_STATIC)
|
||||
message(FATAL_ERROR "You must set either PAHO_BUILD_SHARED, PAHO_BUILD_STATIC, or both")
|
||||
endif()
|
||||
|
||||
## --- Find Paho C or build it, if reqested ---
|
||||
|
||||
if(PAHO_WITH_SSL)
|
||||
find_package(OpenSSL REQUIRED)
|
||||
set(PAHO_MQTT_C_LIB paho-mqtt3as)
|
||||
else()
|
||||
set(PAHO_MQTT_C_LIB paho-mqtt3a)
|
||||
endif()
|
||||
|
||||
if(PAHO_WITH_MQTT_C)
|
||||
message(STATUS "Paho C: Bundled")
|
||||
|
||||
## Build the Paho C library from the submodule
|
||||
set(PAHO_ENABLE_TESTING FALSE CACHE BOOL "No Paho C tests")
|
||||
set(PAHO_HIGH_PERFORMANCE TRUE CACHE BOOL "Paho C high performance")
|
||||
|
||||
# TODO: Remove this when the next Paho C update is released.
|
||||
set(CMAKE_POLICY_DEFAULT_CMP0048 NEW)
|
||||
|
||||
add_subdirectory(${PROJECT_SOURCE_DIR}/externals/paho-mqtt-c)
|
||||
|
||||
## Alias namespace so that the full names can be used with the subdir.
|
||||
if(PAHO_BUILD_SHARED)
|
||||
add_library(eclipse-paho-mqtt-c::paho-mqtt3a ALIAS paho-mqtt3a)
|
||||
list(APPEND PAHO_MQTT_C_LIBS paho-mqtt3a)
|
||||
if(PAHO_WITH_SSL)
|
||||
add_library(eclipse-paho-mqtt-c::paho-mqtt3as ALIAS paho-mqtt3as)
|
||||
list(APPEND PAHO_MQTT_C_LIBS paho-mqtt3as)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(PAHO_BUILD_STATIC)
|
||||
add_library(eclipse-paho-mqtt-c::paho-mqtt3a-static ALIAS paho-mqtt3a-static)
|
||||
list(APPEND PAHO_MQTT_C_LIBS paho-mqtt3a-static)
|
||||
if(PAHO_WITH_SSL)
|
||||
add_library(eclipse-paho-mqtt-c::paho-mqtt3as-static ALIAS paho-mqtt3as-static)
|
||||
list(APPEND PAHO_MQTT_C_LIBS paho-mqtt3as-static)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
## install paho.mqtt.c library (appending to PahoMqttCpp export)
|
||||
install(TARGETS ${PAHO_MQTT_C_LIBS}
|
||||
EXPORT PahoMqttCpp
|
||||
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
|
||||
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
|
||||
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
|
||||
)
|
||||
else()
|
||||
find_package(eclipse-paho-mqtt-c REQUIRED)
|
||||
endif()
|
||||
|
||||
## --- C++11 build flags ---
|
||||
|
||||
set(CMAKE_CXX_STANDARD 11)
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||
set(CMAKE_CXX_EXTENSIONS OFF)
|
||||
|
||||
# Generate position-independent code (-fPIC on UNIX)
|
||||
set(CMAKE_POSITION_INDEPENDENT_CODE ON)
|
||||
|
||||
# --- System Details ---
|
||||
|
||||
include(GNUInstallDirs)
|
||||
|
||||
if(WIN32)
|
||||
add_definitions(-D_CRT_SECURE_NO_WARNINGS)
|
||||
set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS ON)
|
||||
set(LIBS_SYSTEM ws2_32)
|
||||
endif()
|
||||
|
||||
## --- Build directories ---
|
||||
|
||||
# --- The headers ---
|
||||
|
||||
add_subdirectory(include/mqtt)
|
||||
|
||||
# For the paho_mqtt_c module
|
||||
list(APPEND CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/cmake)
|
||||
add_subdirectory(src)
|
||||
|
||||
# --- Documentation ---
|
||||
|
||||
if(PAHO_BUILD_DOCUMENTATION)
|
||||
add_subdirectory(doc)
|
||||
endif()
|
||||
|
||||
# --- Default library for samples and unit tests ---
|
||||
|
||||
if(PAHO_BUILD_SHARED)
|
||||
set(PAHO_CPP_LIB paho-mqttpp3)
|
||||
else()
|
||||
set(PAHO_CPP_LIB paho-mqttpp3-static)
|
||||
endif()
|
||||
|
||||
# --- Example Apps ---
|
||||
|
||||
if(PAHO_BUILD_SAMPLES OR PAHO_BUILD_EXAMPLES)
|
||||
add_subdirectory(examples)
|
||||
endif()
|
||||
|
||||
# --- Unit Tests ---
|
||||
|
||||
if(PAHO_BUILD_TESTS)
|
||||
add_subdirectory(test/unit)
|
||||
endif()
|
||||
|
||||
## --- Packaging settings ---
|
||||
|
||||
if(WIN32)
|
||||
set(CPACK_GENERATOR "ZIP")
|
||||
elseif(UNIX)
|
||||
if(PAHO_BUILD_DEB_PACKAGE)
|
||||
set(CPACK_GENERATOR "DEB")
|
||||
include(cmake/CPackDebConfig.cmake)
|
||||
else()
|
||||
set(CPACK_GENERATOR "TGZ")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
include(CPack)
|
||||
|
||||
add_subdirectory(cmake)
|
||||
@@ -0,0 +1,46 @@
|
||||
# CMakeLists.txt
|
||||
#
|
||||
# CMake export file for the Paho C++ library.
|
||||
#
|
||||
#*******************************************************************************
|
||||
# This is part of the Paho MQTT C++ client library.
|
||||
#
|
||||
# Copyright (c) 2017-2023, Frank Pagliughi
|
||||
#
|
||||
# All rights reserved. This program and the accompanying materials
|
||||
# are made available under the terms of the Eclipse Public License v2.0
|
||||
# and Eclipse Distribution License v1.0 which accompany this distribution.
|
||||
#
|
||||
# The Eclipse Public License is available at
|
||||
# http://www.eclipse.org/legal/epl-v20.html
|
||||
# and the Eclipse Distribution License is available at
|
||||
# http://www.eclipse.org/org/documents/edl-v10.php.
|
||||
#*******************************************************************************/
|
||||
|
||||
set(package_name PahoMqttCpp)
|
||||
configure_file(${package_name}Config.cmake.in ${package_name}Config.cmake @ONLY)
|
||||
|
||||
include(CMakePackageConfigHelpers)
|
||||
|
||||
write_basic_package_version_file(
|
||||
"${CMAKE_CURRENT_BINARY_DIR}/${package_name}ConfigVersion.cmake"
|
||||
VERSION ${PROJECT_VERSION}
|
||||
COMPATIBILITY SameMajorVersion
|
||||
)
|
||||
|
||||
export(EXPORT ${package_name}
|
||||
FILE "${CMAKE_CURRENT_BINARY_DIR}/${package_name}Targets.cmake"
|
||||
NAMESPACE ${package_name}::
|
||||
)
|
||||
|
||||
install(EXPORT ${package_name}
|
||||
DESTINATION lib/cmake/${package_name}
|
||||
FILE ${package_name}Targets.cmake
|
||||
NAMESPACE ${package_name}::
|
||||
)
|
||||
|
||||
install(FILES
|
||||
"${CMAKE_CURRENT_BINARY_DIR}/${package_name}Config.cmake"
|
||||
"${CMAKE_CURRENT_BINARY_DIR}/${package_name}ConfigVersion.cmake"
|
||||
DESTINATION lib/cmake/${package_name}
|
||||
)
|
||||
@@ -0,0 +1,10 @@
|
||||
if(CPACK_GENERATOR MATCHES "DEB")
|
||||
set(CPACK_PACKAGE_NAME "libpaho-mqtt.cpp")
|
||||
set(CPACK_DEBIAN_PACKAGE_NAME ${CPACK_PACKAGE_NAME})
|
||||
set(CPACK_PACKAGE_CONTACT "Eclipse")
|
||||
set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "Eclipse Paho MQTT C++ client")
|
||||
set(CPACK_DEBIAN_PACKAGE_MAINTAINER " <>")
|
||||
set(CPACK_DEBIAN_PACKAGE_SHLIBDEPS ON)
|
||||
set(CPACK_DEBIAN_PACKAGE_VERSION ${PACKAGE_VERSION})
|
||||
set(CPACK_DEBIAN_PACKAGE_SECTION "net")
|
||||
endif()
|
||||
@@ -0,0 +1,19 @@
|
||||
# save build-time options
|
||||
set(PAHO_BUILD_STATIC @PAHO_BUILD_STATIC@)
|
||||
set(PAHO_BUILD_SHARED @PAHO_BUILD_SHARED@)
|
||||
set(PAHO_WITH_SSL @PAHO_WITH_SSL@)
|
||||
set(PAHO_WITH_MQTT_C @PAHO_WITH_MQTT_C@)
|
||||
|
||||
include(CMakeFindDependencyMacro)
|
||||
|
||||
find_dependency(Threads REQUIRED)
|
||||
|
||||
if (NOT PAHO_WITH_MQTT_C)
|
||||
find_dependency(eclipse-paho-mqtt-c REQUIRED)
|
||||
endif()
|
||||
|
||||
if (PAHO_WITH_SSL)
|
||||
find_dependency(OpenSSL REQUIRED)
|
||||
endif()
|
||||
|
||||
include("${CMAKE_CURRENT_LIST_DIR}/@package_name@Targets.cmake")
|
||||
+131
@@ -0,0 +1,131 @@
|
||||
#*******************************************************************************
|
||||
# Copyright (c) 2015, 2023 logi.cals GmbH and others
|
||||
#
|
||||
# All rights reserved. This program and the accompanying materials
|
||||
# are made available under the terms of the Eclipse Public License v2.0
|
||||
# and Eclipse Distribution License v1.0 which accompany this distribution.
|
||||
#
|
||||
# The Eclipse Public License is available at
|
||||
# https://www.eclipse.org/legal/epl-2.0/
|
||||
# and the Eclipse Distribution License is available at
|
||||
# http://www.eclipse.org/org/documents/edl-v10.php.
|
||||
#
|
||||
# Contributors:
|
||||
# Rainer Poisel - initial version
|
||||
# Genis Riera Perez - Add support for building debian package
|
||||
#*******************************************************************************/
|
||||
|
||||
# Note: on OS X you should install XCode and the associated command-line tools
|
||||
|
||||
CMAKE_MINIMUM_REQUIRED(VERSION 2.8.12)
|
||||
PROJECT("Eclipse Paho C" C)
|
||||
MESSAGE(STATUS "CMake version: " ${CMAKE_VERSION})
|
||||
MESSAGE(STATUS "CMake system name: " ${CMAKE_SYSTEM_NAME})
|
||||
|
||||
SET(CMAKE_SCRIPTS "${CMAKE_SOURCE_DIR}/cmake")
|
||||
SET(CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake/modules")
|
||||
|
||||
## build settings
|
||||
file(READ version.major PAHO_VERSION_MAJOR)
|
||||
file(READ version.minor PAHO_VERSION_MINOR)
|
||||
file(READ version.patch PAHO_VERSION_PATCH)
|
||||
SET(CLIENT_VERSION ${PAHO_VERSION_MAJOR}.${PAHO_VERSION_MINOR}.${PAHO_VERSION_PATCH})
|
||||
|
||||
INCLUDE(GNUInstallDirs)
|
||||
|
||||
STRING(TIMESTAMP BUILD_TIMESTAMP UTC)
|
||||
MESSAGE(STATUS "Timestamp is ${BUILD_TIMESTAMP}")
|
||||
|
||||
IF(WIN32)
|
||||
ADD_DEFINITIONS(-D_CRT_SECURE_NO_DEPRECATE -DWIN32_LEAN_AND_MEAN)
|
||||
ELSEIF(${CMAKE_SYSTEM_NAME} STREQUAL "Darwin")
|
||||
ADD_DEFINITIONS(-DOSX)
|
||||
ENDIF()
|
||||
|
||||
## build options
|
||||
SET(PAHO_WITH_SSL ON CACHE BOOL "Flag that defines whether to build ssl-enabled binaries too. " FORCE)
|
||||
SET(PAHO_WITH_LIBUUID FALSE CACHE BOOL "Flag that defines whether libuuid or a custom uuid implementation should be used")
|
||||
SET(PAHO_BUILD_SHARED TRUE CACHE BOOL "Build shared library")
|
||||
SET(PAHO_BUILD_STATIC FALSE CACHE BOOL "Build static library")
|
||||
SET(PAHO_BUILD_DOCUMENTATION FALSE CACHE BOOL "Create and install the HTML based API documentation (requires Doxygen)")
|
||||
SET(PAHO_BUILD_SAMPLES FALSE CACHE BOOL "Build sample programs")
|
||||
SET(PAHO_BUILD_DEB_PACKAGE FALSE CACHE BOOL "Build debian package")
|
||||
SET(PAHO_ENABLE_TESTING TRUE CACHE BOOL "Build tests and run")
|
||||
SET(PAHO_ENABLE_CPACK TRUE CACHE BOOL "Enable CPack")
|
||||
SET(PAHO_HIGH_PERFORMANCE FALSE CACHE BOOL "Disable tracing and heap tracking")
|
||||
SET(PAHO_USE_SELECT FALSE CACHE BOOL "Revert to select system call instead of poll")
|
||||
|
||||
IF (PAHO_HIGH_PERFORMANCE)
|
||||
ADD_DEFINITIONS(-DHIGH_PERFORMANCE=1)
|
||||
ENDIF()
|
||||
|
||||
IF (PAHO_USE_SELECT)
|
||||
ADD_DEFINITIONS(-DUSE_SELECT=1)
|
||||
ENDIF()
|
||||
|
||||
IF (PAHO_WITH_LIBUUID)
|
||||
ADD_DEFINITIONS(-DUSE_LIBUUID=1)
|
||||
ENDIF()
|
||||
|
||||
IF (NOT PAHO_BUILD_SHARED AND NOT PAHO_BUILD_STATIC)
|
||||
MESSAGE(FATAL_ERROR "You must set either PAHO_BUILD_SHARED, PAHO_BUILD_STATIC, or both")
|
||||
ENDIF()
|
||||
|
||||
IF (PAHO_BUILD_SAMPLES AND NOT PAHO_WITH_SSL)
|
||||
MESSAGE(FATAL_ERROR "You must build with SSL to build the samples")
|
||||
ENDIF()
|
||||
|
||||
IF(PAHO_BUILD_DEB_PACKAGE)
|
||||
set(CMAKE_INSTALL_DOCDIR share/doc/libpaho-mqtt)
|
||||
set(CPACK_DEBIAN_PACKAGE_GENERATE_SHLIBS ON)
|
||||
set(CPACK_DEBIAN_PACKAGE_GENERATE_SHLIBS_POLICY ">=")
|
||||
ENDIF()
|
||||
|
||||
ADD_SUBDIRECTORY(src)
|
||||
IF(PAHO_BUILD_SAMPLES)
|
||||
ADD_SUBDIRECTORY(src/samples)
|
||||
ENDIF()
|
||||
|
||||
IF(PAHO_BUILD_DOCUMENTATION)
|
||||
ADD_SUBDIRECTORY(doc)
|
||||
ENDIF()
|
||||
|
||||
IF (PAHO_ENABLE_CPACK)
|
||||
### packaging settings
|
||||
FILE(GLOB samples "src/samples/*.c")
|
||||
INSTALL(FILES ${samples} DESTINATION ${CMAKE_INSTALL_DOCDIR}/samples)
|
||||
|
||||
SET(CPACK_PACKAGE_VENDOR "Eclipse Paho")
|
||||
SET(CPACK_PACKAGE_NAME "Eclipse-Paho-MQTT-C")
|
||||
|
||||
IF (WIN32)
|
||||
SET(CPACK_GENERATOR "ZIP")
|
||||
ELSEIF(PAHO_BUILD_DEB_PACKAGE)
|
||||
|
||||
SET(CPACK_GENERATOR "DEB")
|
||||
CONFIGURE_FILE(${CMAKE_SCRIPTS}/CPackDebConfig.cmake.in
|
||||
${CMAKE_BINARY_DIR}/CPackDebConfig.cmake @ONLY)
|
||||
SET(CPACK_PROJECT_CONFIG_FILE ${CMAKE_BINARY_DIR}/CPackDebConfig.cmake)
|
||||
ELSE()
|
||||
SET(CPACK_GENERATOR "TGZ")
|
||||
ENDIF()
|
||||
ELSE()
|
||||
FILE(GLOB samples "src/samples/*.c")
|
||||
INSTALL(FILES ${samples} DESTINATION ${CMAKE_INSTALL_DOCDIR})
|
||||
ENDIF()
|
||||
|
||||
SET(CPACK_PACKAGE_VERSION_MAJOR ${PAHO_VERSION_MAJOR})
|
||||
SET(CPACK_PACKAGE_VERSION_MINOR ${PAHO_VERSION_MINOR})
|
||||
SET(CPACK_PACKAGE_VERSION_PATCH ${PAHO_VERSION_PATCH})
|
||||
INCLUDE(CPack)
|
||||
|
||||
IF(PAHO_ENABLE_TESTING)
|
||||
ENABLE_TESTING()
|
||||
INCLUDE_DIRECTORIES(test src)
|
||||
ADD_SUBDIRECTORY(test)
|
||||
ELSE()
|
||||
INCLUDE_DIRECTORIES(src)
|
||||
ENDIF()
|
||||
|
||||
# 添加调试输出
|
||||
MESSAGE(STATUS "Root CMake PAHO_WITH_SSL is set to: ${PAHO_WITH_SSL}")
|
||||
@@ -0,0 +1,88 @@
|
||||
IF (CPACK_GENERATOR MATCHES "DEB")
|
||||
FIND_PROGRAM(DPKG_PROGRAM dpkg DOC "dpkg program of Debian-based systems")
|
||||
IF (DPKG_PROGRAM)
|
||||
EXECUTE_PROCESS(
|
||||
COMMAND ${DPKG_PROGRAM} --print-architecture
|
||||
OUTPUT_VARIABLE CPACK_DEBIAN_PACKAGE_ARCHITECTURE
|
||||
OUTPUT_STRIP_TRAILING_WHITESPACE
|
||||
)
|
||||
ELSE (DPKG_PROGRAM)
|
||||
MESSAGE(FATAL_ERROR "Could not find an architecture for the package")
|
||||
ENDIF (DPKG_PROGRAM)
|
||||
|
||||
EXECUTE_PROCESS(
|
||||
COMMAND lsb_release -si
|
||||
OUTPUT_VARIABLE CPACK_DEBIAN_DIST_NAME
|
||||
RESULT_VARIABLE DIST_NAME_STATUS
|
||||
OUTPUT_STRIP_TRAILING_WHITESPACE
|
||||
)
|
||||
|
||||
IF (DIST_NAME_STATUS)
|
||||
MESSAGE(FATAL_ERROR "Could not find a GNU/Linux distribution name")
|
||||
ENDIF (DIST_NAME_STATUS)
|
||||
|
||||
IF (CPACK_DEBIAN_DIST_NAME STREQUAL "")
|
||||
MESSAGE(FATAL_ERROR "Could not find a GNU/Linux distribution name")
|
||||
ENDIF ()
|
||||
|
||||
EXECUTE_PROCESS(
|
||||
COMMAND lsb_release -sc
|
||||
OUTPUT_VARIABLE CPACK_DEBIAN_DIST_CODE
|
||||
RESULT_VARIABLE DIST_CODE_STATUS
|
||||
OUTPUT_STRIP_TRAILING_WHITESPACE
|
||||
)
|
||||
|
||||
IF (DIST_NAME_STATUS)
|
||||
MESSAGE(FATAL_ERROR "Could not find a GNU/Linux distribution codename")
|
||||
ENDIF (DIST_NAME_STATUS)
|
||||
|
||||
IF (CPACK_DEBIAN_DIST_CODE STREQUAL "")
|
||||
MESSAGE(FATAL_ERROR "Could not find a GNU/Linux distribution codename")
|
||||
ENDIF ()
|
||||
|
||||
SET(CPACK_PACKAGE_VERSION_MAJOR @PAHO_VERSION_MAJOR@)
|
||||
SET(CPACK_PACKAGE_VERSION_MINOR @PAHO_VERSION_MINOR@)
|
||||
SET(CPACK_PACKAGE_VERSION_PATCH @PAHO_VERSION_PATCH@)
|
||||
SET(PACKAGE_VERSION
|
||||
"${CPACK_PACKAGE_VERSION_MAJOR}.${CPACK_PACKAGE_VERSION_MINOR}.${CPACK_PACKAGE_VERSION_PATCH}")
|
||||
|
||||
IF (PACKAGE_VERSION STREQUAL "")
|
||||
MESSAGE(FATAL_ERROR "Could not find a version number for the package")
|
||||
ENDIF ()
|
||||
|
||||
SET(PAHO_WITH_SSL @PAHO_WITH_SSL@)
|
||||
|
||||
MESSAGE("Package version: ${PACKAGE_VERSION}")
|
||||
MESSAGE("Package built for: ${CPACK_DEBIAN_DIST_NAME} ${CPACK_DEBIAN_DIST_CODE}")
|
||||
IF(PAHO_WITH_SSL)
|
||||
MESSAGE("Package built with ssl-enabled binaries too")
|
||||
ENDIF()
|
||||
|
||||
# Additional lines to a paragraph should start with " "; paragraphs should
|
||||
# be separated with a " ." line
|
||||
SET(CPACK_PACKAGE_NAME "libpaho-mqtt")
|
||||
SET(CPACK_PACKAGE_CONTACT "Eclipse")
|
||||
SET(CPACK_PACKAGE_DESCRIPTION_SUMMARY "Eclipse Paho MQTT C client")
|
||||
SET(CPACK_DEBIAN_PACKAGE_NAME ${CPACK_PACKAGE_NAME})
|
||||
SET(CPACK_DEBIAN_PACKAGE_MAINTAINER
|
||||
"Genis Riera Perez <genis.riera.perez@gmail.com>")
|
||||
SET(CPACK_DEBIAN_PACKAGE_DESCRIPTION "Eclipse Paho MQTT C client library")
|
||||
SET(CPACK_DEBIAN_PACKAGE_SHLIBDEPS ON)
|
||||
SET(CPACK_DEBIAN_PACKAGE_VERSION ${PACKAGE_VERSION})
|
||||
SET(CPACK_DEBIAN_PACKAGE_SECTION "net")
|
||||
SET(CPACK_DEBIAN_PACKAGE_CONFLICTS ${CPACK_PACKAGE_NAME})
|
||||
SET(CPACK_PACKAGE_FILE_NAME
|
||||
"${CPACK_DEBIAN_PACKAGE_NAME}_${CPACK_DEBIAN_PACKAGE_VERSION}_${CPACK_DEBIAN_PACKAGE_ARCHITECTURE}")
|
||||
|
||||
UNSET(PACKAGE_VERSION CACHE)
|
||||
UNSET(CPACK_DEBIAN_PACKAGE_VERSION CACHE)
|
||||
|
||||
#
|
||||
# From CMakeDebHelper
|
||||
# See http://www.cmake.org/Wiki/CMake:CPackPackageGenerators#Overall_usage_.28common_to_all_generators.29
|
||||
#
|
||||
|
||||
# When the DEB-generator runs, we want him to run our install-script
|
||||
#set( CPACK_INSTALL_SCRIPT ${CPACK_DEBIAN_INSTALL_SCRIPT} )
|
||||
|
||||
ENDIF()
|
||||
@@ -0,0 +1,10 @@
|
||||
# path to compiler and utilities
|
||||
# specify the cross compiler
|
||||
SET(CMAKE_C_COMPILER arm-linux-gnueabihf-gcc)
|
||||
|
||||
# Name of the target platform
|
||||
SET(CMAKE_SYSTEM_NAME Linux)
|
||||
SET(CMAKE_SYSTEM_PROCESSOR arm)
|
||||
|
||||
# Version of the system
|
||||
SET(CMAKE_SYSTEM_VERSION 1)
|
||||
@@ -0,0 +1,15 @@
|
||||
# Name of the target platform
|
||||
SET(CMAKE_SYSTEM_NAME Windows)
|
||||
|
||||
# Version of the system
|
||||
SET(CMAKE_SYSTEM_VERSION 1)
|
||||
|
||||
# specify the cross compiler
|
||||
SET(CMAKE_C_COMPILER i686-w64-mingw32-gcc)
|
||||
SET(CMAKE_CXX_COMPILER i686-w64-mingw32-g++)
|
||||
SET(CMAKE_RC_COMPILER_ENV_VAR "RC")
|
||||
SET(CMAKE_RC_COMPILER "")
|
||||
SET(CMAKE_SHARED_LINKER_FLAGS
|
||||
"-fdata-sections -ffunction-sections -Wl,--enable-stdcall-fixup -static-libgcc -static -lpthread" CACHE STRING "" FORCE)
|
||||
SET(CMAKE_EXE_LINKER_FLAGS
|
||||
"-fdata-sections -ffunction-sections -Wl,--enable-stdcall-fixup -static-libgcc -static -lpthread" CACHE STRING "" FORCE)
|
||||
@@ -0,0 +1,15 @@
|
||||
# Name of the target platform
|
||||
SET(CMAKE_SYSTEM_NAME Windows)
|
||||
|
||||
# Version of the system
|
||||
SET(CMAKE_SYSTEM_VERSION 1)
|
||||
|
||||
# specify the cross compiler
|
||||
SET(CMAKE_C_COMPILER x86_64-w64-mingw32-gcc)
|
||||
SET(CMAKE_CXX_COMPILER x86_64-w64-mingw32-g++)
|
||||
SET(CMAKE_RC_COMPILER_ENV_VAR "RC")
|
||||
SET(CMAKE_RC_COMPILER "")
|
||||
SET(CMAKE_SHARED_LINKER_FLAGS
|
||||
"-fdata-sections -ffunction-sections -Wl,--enable-stdcall-fixup -static-libgcc -static -lpthread" CACHE STRING "" FORCE)
|
||||
SET(CMAKE_EXE_LINKER_FLAGS
|
||||
"-fdata-sections -ffunction-sections -Wl,--enable-stdcall-fixup -static-libgcc -static -lpthread" CACHE STRING "" FORCE)
|
||||
+352
@@ -0,0 +1,352 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2018, 2019 Wind River Systems, Inc. All Rights Reserved.
|
||||
*
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v2.0
|
||||
* and Eclipse Distribution License v1.0 which accompany this distribution.
|
||||
*
|
||||
* The Eclipse Public License is available at
|
||||
* https://www.eclipse.org/legal/epl-2.0/
|
||||
* and the Eclipse Distribution License is available at
|
||||
* http://www.eclipse.org/org/documents/edl-v10.php.
|
||||
*
|
||||
* Contributors:
|
||||
* Keith Holman - initial implementation and documentation
|
||||
*******************************************************************************/
|
||||
|
||||
#include "Base64.h"
|
||||
|
||||
#if defined(_WIN32) || defined(_WIN64)
|
||||
#pragma comment(lib, "crypt32.lib")
|
||||
#include <windows.h>
|
||||
#include <wincrypt.h>
|
||||
b64_size_t Base64_decode( b64_data_t *out, b64_size_t out_len, const char *in, b64_size_t in_len )
|
||||
{
|
||||
b64_size_t ret = 0u;
|
||||
DWORD dw_out_len = (DWORD)out_len;
|
||||
if ( CryptStringToBinaryA( in, in_len, CRYPT_STRING_BASE64, out, &dw_out_len, NULL, NULL ) )
|
||||
ret = (b64_size_t)dw_out_len;
|
||||
return ret;
|
||||
}
|
||||
|
||||
b64_size_t Base64_encode( char *out, b64_size_t out_len, const b64_data_t *in, b64_size_t in_len )
|
||||
{
|
||||
b64_size_t ret = 0u;
|
||||
DWORD dw_out_len = (DWORD)out_len;
|
||||
if ( CryptBinaryToStringA( in, in_len, CRYPT_STRING_BASE64 | CRYPT_STRING_NOCRLF, out, &dw_out_len ) )
|
||||
ret = (b64_size_t)dw_out_len;
|
||||
return ret;
|
||||
}
|
||||
#else /* if defined(_WIN32) || defined(_WIN64) */
|
||||
|
||||
#if defined(OPENSSL)
|
||||
#include <openssl/bio.h>
|
||||
#include <openssl/evp.h>
|
||||
static b64_size_t Base64_encodeDecode(
|
||||
char *out, b64_size_t out_len, const char *in, b64_size_t in_len, int encode )
|
||||
{
|
||||
b64_size_t ret = 0u;
|
||||
if ( in_len > 0u )
|
||||
{
|
||||
int rv;
|
||||
BIO *bio, *b64, *b_in, *b_out;
|
||||
|
||||
b64 = BIO_new(BIO_f_base64());
|
||||
bio = BIO_new(BIO_s_mem());
|
||||
b64 = BIO_push(b64, bio);
|
||||
BIO_set_flags(b64, BIO_FLAGS_BASE64_NO_NL); /* ignore new-lines */
|
||||
|
||||
if ( encode )
|
||||
{
|
||||
b_in = bio;
|
||||
b_out = b64;
|
||||
}
|
||||
else
|
||||
{
|
||||
b_in = b64;
|
||||
b_out = bio;
|
||||
}
|
||||
|
||||
rv = BIO_write(b_out, in, (int)in_len);
|
||||
BIO_flush(b_out); /* indicate end of encoding */
|
||||
|
||||
if ( rv > 0 )
|
||||
{
|
||||
rv = BIO_read(b_in, out, (int)out_len);
|
||||
if ( rv > 0 )
|
||||
{
|
||||
ret = (b64_size_t)rv;
|
||||
if ( out_len > ret )
|
||||
out[ret] = '\0';
|
||||
}
|
||||
}
|
||||
|
||||
BIO_free_all(b64); /* free all used memory */
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
b64_size_t Base64_decode( b64_data_t *out, b64_size_t out_len, const char *in, b64_size_t in_len )
|
||||
{
|
||||
return Base64_encodeDecode( (char*)out, out_len, in, in_len, 0 );
|
||||
}
|
||||
|
||||
b64_size_t Base64_encode( char *out, b64_size_t out_len, const b64_data_t *in, b64_size_t in_len )
|
||||
{
|
||||
return Base64_encodeDecode( out, out_len, (const char*)in, in_len, 1 );
|
||||
}
|
||||
|
||||
#else /* if defined(OPENSSL) */
|
||||
b64_size_t Base64_decode( b64_data_t *out, b64_size_t out_len, const char *in, b64_size_t in_len )
|
||||
{
|
||||
#define NV 64
|
||||
static const unsigned char BASE64_DECODE_TABLE[] =
|
||||
{
|
||||
NV, NV, NV, NV, NV, NV, NV, NV, NV, NV, NV, NV, NV, NV, NV, NV, /* 0-15 */
|
||||
NV, NV, NV, NV, NV, NV, NV, NV, NV, NV, NV, NV, NV, NV, NV, NV, /* 16-31 */
|
||||
NV, NV, NV, NV, NV, NV, NV, NV, NV, NV, NV, 62, NV, NV, NV, 63, /* 32-47 */
|
||||
52, 53, 54, 55, 56, 57, 58, 59, 60, 61, NV, NV, NV, NV, NV, NV, /* 48-63 */
|
||||
NV, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, /* 64-79 */
|
||||
15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, NV, NV, NV, NV, NV, /* 80-95 */
|
||||
NV, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, /* 96-111 */
|
||||
41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, NV, NV, NV, NV, NV, /* 112-127 */
|
||||
NV, NV, NV, NV, NV, NV, NV, NV, NV, NV, NV, NV, NV, NV, NV, NV, /* 128-143 */
|
||||
NV, NV, NV, NV, NV, NV, NV, NV, NV, NV, NV, NV, NV, NV, NV, NV, /* 144-159 */
|
||||
NV, NV, NV, NV, NV, NV, NV, NV, NV, NV, NV, NV, NV, NV, NV, NV, /* 160-175 */
|
||||
NV, NV, NV, NV, NV, NV, NV, NV, NV, NV, NV, NV, NV, NV, NV, NV, /* 176-191 */
|
||||
NV, NV, NV, NV, NV, NV, NV, NV, NV, NV, NV, NV, NV, NV, NV, NV, /* 192-207 */
|
||||
NV, NV, NV, NV, NV, NV, NV, NV, NV, NV, NV, NV, NV, NV, NV, NV, /* 208-223 */
|
||||
NV, NV, NV, NV, NV, NV, NV, NV, NV, NV, NV, NV, NV, NV, NV, NV, /* 224-239 */
|
||||
NV, NV, NV, NV, NV, NV, NV, NV, NV, NV, NV, NV, NV, NV, NV, NV /* 240-255 */
|
||||
};
|
||||
|
||||
b64_size_t ret = 0u;
|
||||
b64_size_t out_count = 0u;
|
||||
|
||||
/* in valid base64, length must be multiple of 4's: 0, 4, 8, 12, etc */
|
||||
while ( in_len > 3u && out_count < out_len )
|
||||
{
|
||||
int i;
|
||||
unsigned char c[4];
|
||||
for ( i = 0; i < 4; ++i, ++in )
|
||||
c[i] = BASE64_DECODE_TABLE[(int)(*in)];
|
||||
in_len -= 4u;
|
||||
|
||||
/* first byte */
|
||||
*out = c[0] << 2;
|
||||
*out |= (c[1] & ~0xF) >> 4;
|
||||
++out;
|
||||
++out_count;
|
||||
|
||||
if ( out_count < out_len )
|
||||
{
|
||||
/* second byte */
|
||||
*out = (c[1] & 0xF) << 4;
|
||||
if ( c[2] < NV )
|
||||
{
|
||||
*out |= (c[2] & ~0x3) >> 2;
|
||||
++out;
|
||||
++out_count;
|
||||
|
||||
if ( out_count < out_len )
|
||||
{
|
||||
/* third byte */
|
||||
*out = (c[2] & 0x3) << 6;
|
||||
if ( c[3] < NV )
|
||||
{
|
||||
*out |= c[3];
|
||||
++out;
|
||||
++out_count;
|
||||
}
|
||||
else
|
||||
in_len = 0u;
|
||||
}
|
||||
}
|
||||
else
|
||||
in_len = 0u;
|
||||
}
|
||||
}
|
||||
|
||||
if ( out_count <= out_len )
|
||||
{
|
||||
ret = out_count;
|
||||
if ( out_count < out_len )
|
||||
*out = '\0';
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
b64_size_t Base64_encode( char *out, b64_size_t out_len, const b64_data_t *in, b64_size_t in_len )
|
||||
{
|
||||
static const char BASE64_ENCODE_TABLE[] =
|
||||
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
||||
"abcdefghijklmnopqrstuvwxyz"
|
||||
"0123456789+/=";
|
||||
b64_size_t ret = 0u;
|
||||
b64_size_t out_count = 0u;
|
||||
|
||||
while ( in_len > 0u && out_count < out_len )
|
||||
{
|
||||
int i;
|
||||
unsigned char c[] = { 0, 0, 64, 64 }; /* index of '=' char */
|
||||
|
||||
/* first character */
|
||||
i = *in;
|
||||
c[0] = (i & ~0x3) >> 2;
|
||||
|
||||
/* second character */
|
||||
c[1] = (i & 0x3) << 4;
|
||||
--in_len;
|
||||
if ( in_len > 0u )
|
||||
{
|
||||
++in;
|
||||
i = *in;
|
||||
c[1] |= (i & ~0xF) >> 4;
|
||||
|
||||
/* third character */
|
||||
c[2] = (i & 0xF) << 2;
|
||||
--in_len;
|
||||
if ( in_len > 0u )
|
||||
{
|
||||
++in;
|
||||
i = *in;
|
||||
c[2] |= (i & ~0x3F) >> 6;
|
||||
|
||||
/* fourth character */
|
||||
c[3] = (i & 0x3F);
|
||||
--in_len;
|
||||
++in;
|
||||
}
|
||||
}
|
||||
|
||||
/* encode the characters */
|
||||
out_count += 4u;
|
||||
for ( i = 0; i < 4 && out_count <= out_len; ++i, ++out )
|
||||
*out = BASE64_ENCODE_TABLE[c[i]];
|
||||
}
|
||||
|
||||
if ( out_count <= out_len )
|
||||
{
|
||||
if ( out_count < out_len )
|
||||
*out = '\0';
|
||||
ret = out_count;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
#endif /* else if defined(OPENSSL) */
|
||||
#endif /* if else defined(_WIN32) || defined(_WIN64) */
|
||||
|
||||
b64_size_t Base64_decodeLength( const char *in, b64_size_t in_len )
|
||||
{
|
||||
b64_size_t pad = 0u;
|
||||
|
||||
if ( in && in_len > 1u )
|
||||
pad += ( in[in_len - 2u] == '=' ? 1u : 0u );
|
||||
if ( in && in_len > 0u )
|
||||
pad += ( in[in_len - 1u] == '=' ? 1u : 0u );
|
||||
return (in_len / 4u * 3u) - pad;
|
||||
}
|
||||
|
||||
b64_size_t Base64_encodeLength( const b64_data_t *in, b64_size_t in_len )
|
||||
{
|
||||
return ((4u * in_len / 3u) + 3u) & ~0x3;
|
||||
}
|
||||
|
||||
#if defined(BASE64_TEST)
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#define TEST_EXPECT(i,x) if (!(x)) {fprintf( stderr, "failed test: %s (for i == %d)\n", #x, i ); ++fails;}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
struct _td
|
||||
{
|
||||
const char *in;
|
||||
const char *out;
|
||||
};
|
||||
|
||||
int i;
|
||||
unsigned int fails = 0u;
|
||||
struct _td test_data[] = {
|
||||
{ "", "" },
|
||||
{ "p", "cA==" },
|
||||
{ "pa", "cGE=" },
|
||||
{ "pah", "cGFo" },
|
||||
{ "paho", "cGFobw==" },
|
||||
{ "paho ", "cGFobyA=" },
|
||||
{ "paho w", "cGFobyB3" },
|
||||
{ "paho wi", "cGFobyB3aQ==" },
|
||||
{ "paho wit", "cGFobyB3aXQ=" },
|
||||
{ "paho with", "cGFobyB3aXRo" },
|
||||
{ "paho with ", "cGFobyB3aXRoIA==" },
|
||||
{ "paho with w", "cGFobyB3aXRoIHc=" },
|
||||
{ "paho with we", "cGFobyB3aXRoIHdl" },
|
||||
{ "paho with web", "cGFobyB3aXRoIHdlYg==" },
|
||||
{ "paho with webs", "cGFobyB3aXRoIHdlYnM=" },
|
||||
{ "paho with webso", "cGFobyB3aXRoIHdlYnNv" },
|
||||
{ "paho with websoc", "cGFobyB3aXRoIHdlYnNvYw==" },
|
||||
{ "paho with websock", "cGFobyB3aXRoIHdlYnNvY2s=" },
|
||||
{ "paho with websocke", "cGFobyB3aXRoIHdlYnNvY2tl" },
|
||||
{ "paho with websocket", "cGFobyB3aXRoIHdlYnNvY2tldA==" },
|
||||
{ "paho with websockets", "cGFobyB3aXRoIHdlYnNvY2tldHM=" },
|
||||
{ "paho with websockets.", "cGFobyB3aXRoIHdlYnNvY2tldHMu" },
|
||||
{ "The quick brown fox jumps over the lazy dog",
|
||||
"VGhlIHF1aWNrIGJyb3duIGZveCBqdW1wcyBvdmVyIHRoZSBsYXp5IGRvZw==" },
|
||||
{ "Man is distinguished, not only by his reason, but by this singular passion from\n"
|
||||
"other animals, which is a lust of the mind, that by a perseverance of delight\n"
|
||||
"in the continued and indefatigable generation of knowledge, exceeds the short\n"
|
||||
"vehemence of any carnal pleasure.",
|
||||
"TWFuIGlzIGRpc3Rpbmd1aXNoZWQsIG5vdCBvbmx5IGJ5IGhpcyByZWFzb24sIGJ1dCBieSB0aGlz"
|
||||
"IHNpbmd1bGFyIHBhc3Npb24gZnJvbQpvdGhlciBhbmltYWxzLCB3aGljaCBpcyBhIGx1c3Qgb2Yg"
|
||||
"dGhlIG1pbmQsIHRoYXQgYnkgYSBwZXJzZXZlcmFuY2Ugb2YgZGVsaWdodAppbiB0aGUgY29udGlu"
|
||||
"dWVkIGFuZCBpbmRlZmF0aWdhYmxlIGdlbmVyYXRpb24gb2Yga25vd2xlZGdlLCBleGNlZWRzIHRo"
|
||||
"ZSBzaG9ydAp2ZWhlbWVuY2Ugb2YgYW55IGNhcm5hbCBwbGVhc3VyZS4=" },
|
||||
{ NULL, NULL }
|
||||
};
|
||||
|
||||
/* decode tests */
|
||||
i = 0;
|
||||
while ( test_data[i].in != NULL )
|
||||
{
|
||||
int r;
|
||||
char out[512u];
|
||||
r = Base64_decode( out, sizeof(out), test_data[i].out, strlen(test_data[i].out) );
|
||||
TEST_EXPECT( i, r == strlen(test_data[i].in) && strncmp(out, test_data[i].in, strlen(test_data[i].in)) == 0 );
|
||||
++i;
|
||||
}
|
||||
|
||||
/* decode length tests */
|
||||
i = 0;
|
||||
while ( test_data[i].in != NULL )
|
||||
{
|
||||
TEST_EXPECT( i, Base64_decodeLength(test_data[i].out, strlen(test_data[i].out)) == strlen(test_data[i].in));
|
||||
++i;
|
||||
}
|
||||
|
||||
/* encode tests */
|
||||
i = 0;
|
||||
while ( test_data[i].in != NULL )
|
||||
{
|
||||
int r;
|
||||
char out[512u];
|
||||
r = Base64_encode( out, sizeof(out), test_data[i].in, strlen(test_data[i].in) );
|
||||
TEST_EXPECT( i, r == strlen(test_data[i].out) && strncmp(out, test_data[i].out, strlen(test_data[i].out)) == 0 );
|
||||
++i;
|
||||
}
|
||||
|
||||
/* encode length tests */
|
||||
i = 0;
|
||||
while ( test_data[i].in != NULL )
|
||||
{
|
||||
TEST_EXPECT( i, Base64_encodeLength(test_data[i].in, strlen(test_data[i].in)) == strlen(test_data[i].out) );
|
||||
++i;
|
||||
}
|
||||
|
||||
if ( fails )
|
||||
printf( "%u test failed!\n", fails );
|
||||
else
|
||||
printf( "all tests passed\n" );
|
||||
return fails;
|
||||
}
|
||||
#endif /* if defined(BASE64_TEST) */
|
||||
+83
@@ -0,0 +1,83 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2018 Wind River Systems, Inc. All Rights Reserved.
|
||||
*
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v2.0
|
||||
* and Eclipse Distribution License v1.0 which accompany this distribution.
|
||||
*
|
||||
* The Eclipse Public License is available at
|
||||
* https://www.eclipse.org/legal/epl-2.0/
|
||||
* and the Eclipse Distribution License is available at
|
||||
* http://www.eclipse.org/org/documents/edl-v10.php.
|
||||
*
|
||||
* Contributors:
|
||||
* Keith Holman - initial implementation and documentation
|
||||
*******************************************************************************/
|
||||
|
||||
#if !defined(BASE64_H)
|
||||
#define BASE64_H
|
||||
|
||||
/** type for size of a buffer, it saves passing around @p size_t (unsigned long long or unsigned long int) */
|
||||
typedef unsigned int b64_size_t;
|
||||
/** type for raw base64 data */
|
||||
typedef unsigned char b64_data_t;
|
||||
|
||||
/**
|
||||
* Decodes base64 data
|
||||
*
|
||||
* @param[out] out decoded data
|
||||
* @param[in] out_len length of output buffer
|
||||
* @param[in] in base64 string to decode
|
||||
* @param[in] in_len length of input buffer
|
||||
*
|
||||
* @return the amount of data decoded
|
||||
*
|
||||
* @see Base64_decodeLength
|
||||
* @see Base64_encode
|
||||
*/
|
||||
b64_size_t Base64_decode( b64_data_t *out, b64_size_t out_len,
|
||||
const char *in, b64_size_t in_len );
|
||||
|
||||
/**
|
||||
* Size of buffer required to decode base64 data
|
||||
*
|
||||
* @param[in] in base64 string to decode
|
||||
* @param[in] in_len length of input buffer
|
||||
*
|
||||
* @return the size of buffer the decoded string would require
|
||||
*
|
||||
* @see Base64_decode
|
||||
* @see Base64_encodeLength
|
||||
*/
|
||||
b64_size_t Base64_decodeLength( const char *in, b64_size_t in_len );
|
||||
|
||||
/**
|
||||
* Encodes base64 data
|
||||
*
|
||||
* @param[out] out encode base64 string
|
||||
* @param[in] out_len length of output buffer
|
||||
* @param[in] in raw data to encode
|
||||
* @param[in] in_len length of input buffer
|
||||
*
|
||||
* @return the amount of data encoded
|
||||
*
|
||||
* @see Base64_decode
|
||||
* @see Base64_encodeLength
|
||||
*/
|
||||
b64_size_t Base64_encode( char *out, b64_size_t out_len,
|
||||
const b64_data_t *in, b64_size_t in_len );
|
||||
|
||||
/**
|
||||
* Size of buffer required to encode base64 data
|
||||
*
|
||||
* @param[in] in raw data to encode
|
||||
* @param[in] in_len length of input buffer
|
||||
*
|
||||
* @return the size of buffer the encoded string would require
|
||||
*
|
||||
* @see Base64_decodeLength
|
||||
* @see Base64_encode
|
||||
*/
|
||||
b64_size_t Base64_encodeLength( const b64_data_t *in, b64_size_t in_len );
|
||||
|
||||
#endif /* BASE64_H */
|
||||
+380
@@ -0,0 +1,380 @@
|
||||
#*******************************************************************************
|
||||
# Copyright (c) 2015, 2023 logi.cals GmbH and others
|
||||
#
|
||||
# All rights reserved. This program and the accompanying materials
|
||||
# are made available under the terms of the Eclipse Public License v2.0
|
||||
# and Eclipse Distribution License v1.0 which accompany this distribution.
|
||||
#
|
||||
# The Eclipse Public License is available at
|
||||
# https://www.eclipse.org/legal/epl-2.0/
|
||||
# and the Eclipse Distribution License is available at
|
||||
# http://www.eclipse.org/org/documents/edl-v10.php.
|
||||
#
|
||||
# Contributors:
|
||||
# Rainer Poisel - initial version
|
||||
# Ian Craggs (IBM Corp.) - merge master
|
||||
# Ian Craggs - update for MQTTV5 support
|
||||
#*******************************************************************************/
|
||||
|
||||
# Note: on OS X you should install XCode and the associated command-line tools
|
||||
|
||||
## compilation/linkage settings
|
||||
|
||||
CONFIGURE_FILE(VersionInfo.h.in
|
||||
${CMAKE_BINARY_DIR}/VersionInfo.h
|
||||
@ONLY
|
||||
)
|
||||
|
||||
SET(common_src
|
||||
MQTTTime.c
|
||||
MQTTProtocolClient.c
|
||||
Clients.c
|
||||
utf-8.c
|
||||
MQTTPacket.c
|
||||
MQTTPacketOut.c
|
||||
Messages.c
|
||||
Tree.c
|
||||
Socket.c
|
||||
Log.c
|
||||
MQTTPersistence.c
|
||||
Thread.c
|
||||
MQTTProtocolOut.c
|
||||
MQTTPersistenceDefault.c
|
||||
SocketBuffer.c
|
||||
LinkedList.c
|
||||
MQTTProperties.c
|
||||
MQTTReasonCodes.c
|
||||
Base64.c
|
||||
SHA1.c
|
||||
WebSocket.c
|
||||
Proxy.c
|
||||
)
|
||||
|
||||
IF (NOT PAHO_HIGH_PERFORMANCE)
|
||||
SET(common_src ${common_src}
|
||||
StackTrace.c
|
||||
Heap.c
|
||||
)
|
||||
ENDIF()
|
||||
|
||||
IF (WIN32)
|
||||
SET(LIBS_SYSTEM ws2_32 crypt32 RpcRT4)
|
||||
ELSEIF (UNIX)
|
||||
IF(CMAKE_SYSTEM_NAME MATCHES "Linux")
|
||||
SET(LIBS_SYSTEM c dl pthread rt)
|
||||
# anl is only available with glibc so check if it is found before using
|
||||
# it or build will fail on uclibc or musl
|
||||
FIND_LIBRARY(LIB_ANL anl)
|
||||
IF(LIB_ANL)
|
||||
SET(LIBS_SYSTEM "${LIBS_SYSTEM}" anl)
|
||||
ENDIF()
|
||||
IF(PAHO_WITH_LIBUUID)
|
||||
SET(LIBS_SYSTEM "${LIBS_SYSTEM}" uuid)
|
||||
ENDIF()
|
||||
ADD_DEFINITIONS(-D_GNU_SOURCE -fvisibility=hidden)
|
||||
ELSEIF (CMAKE_SYSTEM_NAME MATCHES "Android")
|
||||
SET(LIBS_SYSTEM c dl)
|
||||
ELSEIF (CMAKE_SYSTEM_NAME MATCHES "FreeBSD")
|
||||
SET(LIBS_SYSTEM compat pthread)
|
||||
ELSE()
|
||||
SET(LIBS_SYSTEM c pthread)
|
||||
ENDIF()
|
||||
ENDIF()
|
||||
|
||||
IF (PAHO_BUILD_SHARED)
|
||||
# common compilation for libpaho-mqtt3c and libpaho-mqtt3a
|
||||
ADD_LIBRARY(common_obj OBJECT ${common_src})
|
||||
SET_TARGET_PROPERTIES(common_obj PROPERTIES
|
||||
POSITION_INDEPENDENT_CODE ON
|
||||
COMPILE_DEFINITIONS "PAHO_MQTT_EXPORTS=1")
|
||||
ADD_EXECUTABLE(MQTTVersion MQTTVersion.c)
|
||||
SET_TARGET_PROPERTIES(MQTTVersion PROPERTIES
|
||||
POSITION_INDEPENDENT_CODE ON
|
||||
COMPILE_DEFINITIONS "PAHO_MQTT_IMPORTS=1")
|
||||
ENDIF()
|
||||
|
||||
IF (PAHO_BUILD_STATIC)
|
||||
ADD_LIBRARY(common_obj_static OBJECT ${common_src})
|
||||
SET_TARGET_PROPERTIES(common_obj_static PROPERTIES
|
||||
POSITION_INDEPENDENT_CODE ON
|
||||
COMPILE_DEFINITIONS "PAHO_MQTT_STATIC=1")
|
||||
ENDIF()
|
||||
|
||||
IF (PAHO_BUILD_SHARED)
|
||||
ADD_LIBRARY(paho-mqtt3c SHARED $<TARGET_OBJECTS:common_obj> MQTTClient.c)
|
||||
ADD_LIBRARY(paho-mqtt3a SHARED $<TARGET_OBJECTS:common_obj> MQTTAsync.c MQTTAsyncUtils.c)
|
||||
|
||||
TARGET_LINK_LIBRARIES(paho-mqtt3c ${LIBS_SYSTEM})
|
||||
TARGET_LINK_LIBRARIES(paho-mqtt3a ${LIBS_SYSTEM})
|
||||
TARGET_LINK_LIBRARIES(MQTTVersion paho-mqtt3a paho-mqtt3c ${LIBS_SYSTEM})
|
||||
|
||||
SET_TARGET_PROPERTIES(
|
||||
paho-mqtt3c paho-mqtt3a PROPERTIES
|
||||
VERSION ${CLIENT_VERSION}
|
||||
SOVERSION ${PAHO_VERSION_MAJOR}
|
||||
COMPILE_DEFINITIONS "PAHO_MQTT_EXPORTS=1")
|
||||
|
||||
IF(${CMAKE_SYSTEM_NAME} STREQUAL "Darwin")
|
||||
SET(MQTTCLIENT_ENTRY_POINT _MQTTClient_init)
|
||||
SET(MQTTASYNC_ENTRY_POINT _MQTTAsync_init)
|
||||
ELSEIF (NOT WIN32)
|
||||
SET(MQTTCLIENT_ENTRY_POINT MQTTClient_init)
|
||||
SET(MQTTASYNC_ENTRY_POINT MQTTAsync_init)
|
||||
ENDIF()
|
||||
|
||||
IF (NOT WIN32)
|
||||
SET_TARGET_PROPERTIES(
|
||||
paho-mqtt3c PROPERTIES
|
||||
LINK_FLAGS "-Wl,-init,${MQTTCLIENT_ENTRY_POINT}")
|
||||
SET_TARGET_PROPERTIES(
|
||||
paho-mqtt3a PROPERTIES
|
||||
LINK_FLAGS "-Wl,-init,${MQTTASYNC_ENTRY_POINT}")
|
||||
ENDIF()
|
||||
|
||||
FOREACH(TARGET paho-mqtt3c paho-mqtt3a)
|
||||
TARGET_INCLUDE_DIRECTORIES(${TARGET}
|
||||
PUBLIC
|
||||
$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>
|
||||
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>
|
||||
PRIVATE
|
||||
${CMAKE_BINARY_DIR})
|
||||
ENDFOREACH()
|
||||
|
||||
INSTALL(TARGETS paho-mqtt3c paho-mqtt3a
|
||||
EXPORT eclipse-paho-mqtt-cTargets
|
||||
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
|
||||
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
|
||||
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR})
|
||||
INSTALL(TARGETS MQTTVersion
|
||||
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR})
|
||||
ENDIF()
|
||||
|
||||
IF (PAHO_BUILD_STATIC)
|
||||
ADD_LIBRARY(paho-mqtt3c-static STATIC $<TARGET_OBJECTS:common_obj_static> MQTTClient.c)
|
||||
ADD_LIBRARY(paho-mqtt3a-static STATIC $<TARGET_OBJECTS:common_obj_static> MQTTAsync.c MQTTAsyncUtils.c)
|
||||
|
||||
TARGET_LINK_LIBRARIES(paho-mqtt3c-static ${LIBS_SYSTEM})
|
||||
TARGET_LINK_LIBRARIES(paho-mqtt3a-static ${LIBS_SYSTEM})
|
||||
IF (NOT WIN32)
|
||||
SET_TARGET_PROPERTIES(paho-mqtt3c-static PROPERTIES OUTPUT_NAME paho-mqtt3c)
|
||||
SET_TARGET_PROPERTIES(paho-mqtt3a-static PROPERTIES OUTPUT_NAME paho-mqtt3a)
|
||||
ENDIF()
|
||||
SET_TARGET_PROPERTIES(
|
||||
paho-mqtt3c-static paho-mqtt3a-static PROPERTIES
|
||||
VERSION ${CLIENT_VERSION}
|
||||
SOVERSION ${PAHO_VERSION_MAJOR}
|
||||
COMPILE_DEFINITIONS "PAHO_MQTT_STATIC=1")
|
||||
|
||||
FOREACH(TARGET paho-mqtt3c-static paho-mqtt3a-static)
|
||||
TARGET_INCLUDE_DIRECTORIES(${TARGET}
|
||||
PUBLIC
|
||||
$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>
|
||||
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>
|
||||
PRIVATE
|
||||
${CMAKE_BINARY_DIR})
|
||||
ENDFOREACH()
|
||||
|
||||
message("PAHO_WITH_SSL: ${PAHO_WITH_SSL}")
|
||||
if(PAHO_WITH_SSL)
|
||||
target_compile_definitions(paho-mqtt3a-static PRIVATE OPENSSL=1)
|
||||
message("paho-mqtt3a-static is compiled with SSL")
|
||||
endif()
|
||||
|
||||
IF (NOT PAHO_BUILD_SHARED)
|
||||
INSTALL(TARGETS paho-mqtt3c-static paho-mqtt3a-static
|
||||
EXPORT eclipse-paho-mqtt-cTargets
|
||||
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
|
||||
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
|
||||
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR})
|
||||
ELSE()
|
||||
INSTALL(TARGETS paho-mqtt3c-static paho-mqtt3a-static
|
||||
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR})
|
||||
ENDIF()
|
||||
ENDIF()
|
||||
|
||||
INSTALL(FILES MQTTAsync.h MQTTClient.h MQTTClientPersistence.h MQTTProperties.h MQTTReasonCodes.h MQTTSubscribeOpts.h MQTTExportDeclarations.h
|
||||
DESTINATION ${CMAKE_INSTALL_INCLUDEDIR})
|
||||
|
||||
IF (PAHO_WITH_SSL)
|
||||
find_package(OpenSSL REQUIRED)
|
||||
if(NOT OPENSSL_FOUND)
|
||||
message(FATAL_ERROR "OpenSSL not found")
|
||||
endif()
|
||||
message(STATUS "OpenSSL found: ${OPENSSL_INCLUDE_DIR}")
|
||||
|
||||
IF (PAHO_BUILD_SHARED)
|
||||
## common compilation for libpaho-mqtt3cs and libpaho-mqtt3as
|
||||
## Note: SSL libraries must be recompiled due ifdefs
|
||||
ADD_LIBRARY(common_ssl_obj OBJECT ${common_src})
|
||||
TARGET_INCLUDE_DIRECTORIES(common_ssl_obj PUBLIC ${OPENSSL_INCLUDE_DIR})
|
||||
SET_PROPERTY(TARGET common_ssl_obj PROPERTY POSITION_INDEPENDENT_CODE ON)
|
||||
SET_PROPERTY(TARGET common_ssl_obj PROPERTY COMPILE_DEFINITIONS "OPENSSL=1;PAHO_MQTT_EXPORTS=1")
|
||||
|
||||
ADD_LIBRARY(paho-mqtt3cs SHARED $<TARGET_OBJECTS:common_ssl_obj> MQTTClient.c SSLSocket.c)
|
||||
ADD_LIBRARY(paho-mqtt3as SHARED $<TARGET_OBJECTS:common_ssl_obj> MQTTAsync.c MQTTAsyncUtils.c SSLSocket.c)
|
||||
|
||||
SET_TARGET_PROPERTIES(
|
||||
paho-mqtt3cs paho-mqtt3as PROPERTIES
|
||||
VERSION ${CLIENT_VERSION}
|
||||
SOVERSION ${PAHO_VERSION_MAJOR}
|
||||
COMPILE_DEFINITIONS "OPENSSL=1;PAHO_MQTT_EXPORTS=1")
|
||||
|
||||
IF(${CMAKE_SYSTEM_NAME} STREQUAL "Darwin")
|
||||
SET(MQTTCLIENT_ENTRY_POINT _MQTTClient_init)
|
||||
SET(MQTTASYNC_ENTRY_POINT _MQTTAsync_init)
|
||||
ELSEIF (NOT WIN32)
|
||||
SET(MQTTCLIENT_ENTRY_POINT MQTTClient_init)
|
||||
SET(MQTTASYNC_ENTRY_POINT MQTTAsync_init)
|
||||
ENDIF()
|
||||
|
||||
IF (NOT WIN32)
|
||||
SET_TARGET_PROPERTIES(
|
||||
paho-mqtt3cs PROPERTIES
|
||||
LINK_FLAGS "-Wl,-init,${MQTTCLIENT_ENTRY_POINT}")
|
||||
SET_TARGET_PROPERTIES(
|
||||
paho-mqtt3as PROPERTIES
|
||||
LINK_FLAGS "-Wl,-init,${MQTTASYNC_ENTRY_POINT}")
|
||||
ENDIF()
|
||||
|
||||
FOREACH(TARGET paho-mqtt3cs paho-mqtt3as)
|
||||
TARGET_INCLUDE_DIRECTORIES(${TARGET}
|
||||
PUBLIC
|
||||
$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>
|
||||
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>
|
||||
PRIVATE
|
||||
${CMAKE_BINARY_DIR})
|
||||
TARGET_LINK_LIBRARIES(${TARGET}
|
||||
PUBLIC
|
||||
OpenSSL::SSL OpenSSL::Crypto ${LIBS_SYSTEM})
|
||||
ENDFOREACH()
|
||||
INSTALL(TARGETS paho-mqtt3cs paho-mqtt3as
|
||||
EXPORT eclipse-paho-mqtt-cTargets
|
||||
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
|
||||
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
|
||||
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR})
|
||||
ENDIF()
|
||||
|
||||
IF (PAHO_BUILD_STATIC)
|
||||
## common compilation for libpaho-mqtt3cs and libpaho-mqtt3as
|
||||
## Note: SSL libraries must be recompiled due ifdefs
|
||||
ADD_LIBRARY(common_ssl_obj_static OBJECT ${common_src})
|
||||
TARGET_INCLUDE_DIRECTORIES(common_ssl_obj_static PUBLIC ${OPENSSL_INCLUDE_DIR})
|
||||
SET_PROPERTY(TARGET common_ssl_obj_static PROPERTY POSITION_INDEPENDENT_CODE ON)
|
||||
SET_PROPERTY(TARGET common_ssl_obj_static PROPERTY COMPILE_DEFINITIONS "OPENSSL=1;PAHO_MQTT_STATIC=1")
|
||||
|
||||
ADD_LIBRARY(paho-mqtt3cs-static STATIC $<TARGET_OBJECTS:common_ssl_obj_static> MQTTClient.c SSLSocket.c)
|
||||
ADD_LIBRARY(paho-mqtt3as-static STATIC $<TARGET_OBJECTS:common_ssl_obj_static> MQTTAsync.c MQTTAsyncUtils.c SSLSocket.c)
|
||||
|
||||
SET_TARGET_PROPERTIES(
|
||||
paho-mqtt3cs-static paho-mqtt3as-static PROPERTIES
|
||||
VERSION ${CLIENT_VERSION}
|
||||
SOVERSION ${PAHO_VERSION_MAJOR}
|
||||
COMPILE_DEFINITIONS "OPENSSL=1;PAHO_MQTT_STATIC=1")
|
||||
IF (NOT WIN32)
|
||||
SET_TARGET_PROPERTIES(paho-mqtt3cs-static PROPERTIES OUTPUT_NAME paho-mqtt3cs)
|
||||
SET_TARGET_PROPERTIES(paho-mqtt3as-static PROPERTIES OUTPUT_NAME paho-mqtt3as)
|
||||
ENDIF()
|
||||
|
||||
IF(${CMAKE_SYSTEM_NAME} STREQUAL "Darwin")
|
||||
SET(MQTTCLIENT_ENTRY_POINT _MQTTClient_init)
|
||||
SET(MQTTASYNC_ENTRY_POINT _MQTTAsync_init)
|
||||
ELSEIF (NOT WIN32)
|
||||
SET(MQTTCLIENT_ENTRY_POINT MQTTClient_init)
|
||||
SET(MQTTASYNC_ENTRY_POINT MQTTAsync_init)
|
||||
ENDIF()
|
||||
|
||||
IF (NOT WIN32)
|
||||
SET_TARGET_PROPERTIES(
|
||||
paho-mqtt3cs-static PROPERTIES
|
||||
LINK_FLAGS "-Wl,-init,${MQTTCLIENT_ENTRY_POINT}")
|
||||
SET_TARGET_PROPERTIES(
|
||||
paho-mqtt3as-static PROPERTIES
|
||||
LINK_FLAGS "-Wl,-init,${MQTTASYNC_ENTRY_POINT}")
|
||||
ENDIF()
|
||||
|
||||
IF (NOT PAHO_BUILD_SHARED)
|
||||
INSTALL(TARGETS paho-mqtt3cs-static paho-mqtt3as-static
|
||||
EXPORT eclipse-paho-mqtt-cTargets
|
||||
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
|
||||
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
|
||||
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR})
|
||||
ELSE()
|
||||
INSTALL(TARGETS paho-mqtt3cs-static paho-mqtt3as-static
|
||||
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR})
|
||||
ENDIF()
|
||||
FOREACH(TARGET paho-mqtt3cs-static paho-mqtt3as-static)
|
||||
TARGET_INCLUDE_DIRECTORIES(${TARGET}
|
||||
PUBLIC
|
||||
$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>
|
||||
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>
|
||||
PRIVATE
|
||||
${CMAKE_BINARY_DIR})
|
||||
TARGET_LINK_LIBRARIES(${TARGET}
|
||||
PUBLIC
|
||||
OpenSSL::SSL OpenSSL::Crypto ${LIBS_SYSTEM})
|
||||
ENDFOREACH()
|
||||
|
||||
# 为所有静态库目标添加 OpenSSL 包含目录
|
||||
IF (PAHO_BUILD_STATIC)
|
||||
# 已有的 paho-mqtt3a-static
|
||||
target_include_directories(paho-mqtt3a-static
|
||||
PUBLIC $<BUILD_INTERFACE:${OPENSSL_INCLUDE_DIR}>)
|
||||
|
||||
# 为 paho-mqtt3cs-static 添加
|
||||
target_include_directories(paho-mqtt3cs-static
|
||||
PUBLIC $<BUILD_INTERFACE:${OPENSSL_INCLUDE_DIR}>)
|
||||
|
||||
# 为 paho-mqtt3c-static 添加
|
||||
target_include_directories(paho-mqtt3c-static
|
||||
PUBLIC $<BUILD_INTERFACE:${OPENSSL_INCLUDE_DIR}>)
|
||||
|
||||
# 确保所有静态库都定义了 OPENSSL 宏
|
||||
foreach(target paho-mqtt3a-static paho-mqtt3cs-static paho-mqtt3c-static)
|
||||
target_compile_definitions(${target} PRIVATE OPENSSL=1)
|
||||
message("${target} is compiled with SSL")
|
||||
endforeach()
|
||||
ENDIF()
|
||||
ENDIF()
|
||||
ENDIF()
|
||||
|
||||
INSTALL(EXPORT eclipse-paho-mqtt-cTargets
|
||||
FILE eclipse-paho-mqtt-cConfig.cmake
|
||||
NAMESPACE eclipse-paho-mqtt-c::
|
||||
DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/eclipse-paho-mqtt-c)
|
||||
|
||||
INCLUDE(CMakePackageConfigHelpers)
|
||||
WRITE_BASIC_PACKAGE_VERSION_FILE("eclipse-paho-mqtt-cConfigVersion.cmake"
|
||||
VERSION ${CLIENT_VERSION}
|
||||
COMPATIBILITY SameMajorVersion)
|
||||
INSTALL(FILES
|
||||
"${CMAKE_CURRENT_BINARY_DIR}/eclipse-paho-mqtt-cConfigVersion.cmake"
|
||||
DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/eclipse-paho-mqtt-c)
|
||||
|
||||
# Base64 test
|
||||
ADD_EXECUTABLE( Base64Test EXCLUDE_FROM_ALL Base64.c Base64.h )
|
||||
TARGET_COMPILE_DEFINITIONS( Base64Test PUBLIC "-DBASE64_TEST" )
|
||||
IF (PAHO_WITH_SSL)
|
||||
ADD_EXECUTABLE( Base64TestOpenSSL EXCLUDE_FROM_ALL Base64.c Base64.h )
|
||||
TARGET_LINK_LIBRARIES( Base64TestOpenSSL OpenSSL::SSL OpenSSL::Crypto)
|
||||
TARGET_COMPILE_DEFINITIONS( Base64TestOpenSSL PUBLIC "-DBASE64_TEST -DOPENSSL=1" )
|
||||
ENDIF (PAHO_WITH_SSL)
|
||||
|
||||
# SHA1 test
|
||||
ADD_EXECUTABLE( Sha1Test EXCLUDE_FROM_ALL SHA1.c SHA1.h )
|
||||
TARGET_COMPILE_DEFINITIONS( Sha1Test PUBLIC "-DSHA1_TEST" )
|
||||
IF (PAHO_WITH_SSL)
|
||||
ADD_EXECUTABLE( Sha1TestOpenSSL EXCLUDE_FROM_ALL SHA1.c SHA1.h )
|
||||
TARGET_LINK_LIBRARIES( Sha1TestOpenSSL OpenSSL::SSL OpenSSL::Crypto)
|
||||
TARGET_COMPILE_DEFINITIONS( Sha1TestOpenSSL PUBLIC "-DSHA1_TEST -DOPENSSL=1" )
|
||||
ENDIF (PAHO_WITH_SSL)
|
||||
|
||||
if(WIN32)
|
||||
option(PAHO_BUILD_STATIC "Build static library" TRUE)
|
||||
option(PAHO_BUILD_SHARED "Build shared library (DLL)" FALSE)
|
||||
option(PAHO_WITH_SSL "Build SSL-enabled library" FALSE)
|
||||
else()
|
||||
option(PAHO_BUILD_STATIC "Build static library" TRUE)
|
||||
option(PAHO_BUILD_SHARED "Build shared library" FALSE)
|
||||
option(PAHO_WITH_SSL "Build SSL-enabled library" TRUE)
|
||||
option(PAHO_BUILD_DEB_PACKAGE "Build debian package" FALSE)
|
||||
endif()
|
||||
+55
@@ -0,0 +1,55 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2009, 2022 IBM Corp.
|
||||
*
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v2.0
|
||||
* and Eclipse Distribution License v1.0 which accompany this distribution.
|
||||
*
|
||||
* The Eclipse Public License is available at
|
||||
* https://www.eclipse.org/legal/epl-2.0/
|
||||
* and the Eclipse Distribution License is available at
|
||||
* http://www.eclipse.org/org/documents/edl-v10.php.
|
||||
*
|
||||
* Contributors:
|
||||
* Ian Craggs - initial API and implementation and/or initial documentation
|
||||
* Ian Craggs - add SSL support
|
||||
*******************************************************************************/
|
||||
|
||||
/**
|
||||
* @file
|
||||
* \brief functions which apply to client structures
|
||||
* */
|
||||
|
||||
|
||||
#include "Clients.h"
|
||||
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
|
||||
|
||||
/**
|
||||
* List callback function for comparing clients by clientid
|
||||
* @param a first integer value
|
||||
* @param b second integer value
|
||||
* @return boolean indicating whether a and b are equal
|
||||
*/
|
||||
int clientIDCompare(void* a, void* b)
|
||||
{
|
||||
Clients* client = (Clients*)a;
|
||||
/*printf("comparing clientdIDs %s with %s\n", client->clientID, (char*)b);*/
|
||||
return strcmp(client->clientID, (char*)b) == 0;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* List callback function for comparing clients by socket
|
||||
* @param a first integer value
|
||||
* @param b second integer value
|
||||
* @return boolean indicating whether a and b are equal
|
||||
*/
|
||||
int clientSocketCompare(void* a, void* b)
|
||||
{
|
||||
Clients* client = (Clients*)a;
|
||||
/*printf("comparing %d with %d\n", (char*)a, (char*)b); */
|
||||
return client->net.socket == *(SOCKET*)b;
|
||||
}
|
||||
+174
@@ -0,0 +1,174 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2009, 2023 IBM Corp. and Ian Craggs
|
||||
*
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v2.0
|
||||
* and Eclipse Distribution License v1.0 which accompany this distribution.
|
||||
*
|
||||
* The Eclipse Public License is available at
|
||||
* https://www.eclipse.org/legal/epl-2.0/
|
||||
* and the Eclipse Distribution License is available at
|
||||
* http://www.eclipse.org/org/documents/edl-v10.php.
|
||||
*
|
||||
* Contributors:
|
||||
* Ian Craggs - initial API and implementation and/or initial documentation
|
||||
* Ian Craggs - add SSL support
|
||||
* Ian Craggs - fix for bug 413429 - connectionLost not called
|
||||
* Ian Craggs - change will payload to binary
|
||||
* Ian Craggs - password to binary
|
||||
* Ian Craggs - MQTT 5 support
|
||||
*******************************************************************************/
|
||||
|
||||
#if !defined(CLIENTS_H)
|
||||
#define CLIENTS_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include "MQTTTime.h"
|
||||
#if defined(_WIN32) || defined(_WIN64)
|
||||
#include <winsock2.h>
|
||||
#endif
|
||||
#if defined(OPENSSL)
|
||||
#include <openssl/ssl.h>
|
||||
#endif
|
||||
#include "MQTTClient.h"
|
||||
#include "LinkedList.h"
|
||||
#include "MQTTClientPersistence.h"
|
||||
#include "Socket.h"
|
||||
|
||||
/**
|
||||
* Stored publication data to minimize copying
|
||||
*/
|
||||
typedef struct
|
||||
{
|
||||
char *topic;
|
||||
int topiclen;
|
||||
char* payload;
|
||||
int payloadlen;
|
||||
int refcount;
|
||||
uint8_t mask[4];
|
||||
} Publications;
|
||||
|
||||
/**
|
||||
* Client publication message data
|
||||
*/
|
||||
typedef struct
|
||||
{
|
||||
int qos;
|
||||
int retain;
|
||||
int msgid;
|
||||
int MQTTVersion;
|
||||
MQTTProperties properties;
|
||||
Publications *publish;
|
||||
START_TIME_TYPE lastTouch; /**> used for retry and expiry */
|
||||
char nextMessageType; /**> PUBREC, PUBREL, PUBCOMP */
|
||||
int len; /**> length of the whole structure+data */
|
||||
} Messages;
|
||||
|
||||
/**
|
||||
* Client will message data
|
||||
*/
|
||||
typedef struct
|
||||
{
|
||||
char *topic;
|
||||
int payloadlen;
|
||||
void *payload;
|
||||
int retained;
|
||||
int qos;
|
||||
} willMessages;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
SOCKET socket;
|
||||
START_TIME_TYPE lastSent;
|
||||
START_TIME_TYPE lastReceived;
|
||||
START_TIME_TYPE lastPing;
|
||||
#if defined(OPENSSL)
|
||||
SSL* ssl;
|
||||
SSL_CTX* ctx;
|
||||
char *https_proxy;
|
||||
char *https_proxy_auth;
|
||||
#endif
|
||||
char *http_proxy;
|
||||
char *http_proxy_auth;
|
||||
int websocket; /**< socket has been upgraded to use web sockets */
|
||||
char *websocket_key;
|
||||
const MQTTClient_nameValue* httpHeaders;
|
||||
} networkHandles;
|
||||
|
||||
|
||||
/* connection states */
|
||||
/** no connection in progress, see connected value */
|
||||
#define NOT_IN_PROGRESS 0x0
|
||||
/** TCP connection in progress */
|
||||
#define TCP_IN_PROGRESS 0x1
|
||||
/** SSL connection in progress */
|
||||
#define SSL_IN_PROGRESS 0x2
|
||||
/** Websocket connection in progress */
|
||||
#define WEBSOCKET_IN_PROGRESS 0x3
|
||||
/** TCP completed, waiting for MQTT ACK */
|
||||
#define WAIT_FOR_CONNACK 0x4
|
||||
/** Proxy connection in progress */
|
||||
#define PROXY_CONNECT_IN_PROGRESS 0x5
|
||||
/** Disconnecting */
|
||||
#define DISCONNECTING -2
|
||||
|
||||
/**
|
||||
* Data related to one client
|
||||
*/
|
||||
typedef struct
|
||||
{
|
||||
char* clientID; /**< the string id of the client */
|
||||
const char* username; /**< MQTT v3.1 user name */
|
||||
int passwordlen; /**< MQTT password length */
|
||||
const void* password; /**< MQTT v3.1 binary password */
|
||||
unsigned int cleansession : 1; /**< MQTT V3 clean session flag */
|
||||
unsigned int cleanstart : 1; /**< MQTT V5 clean start flag */
|
||||
unsigned int connected : 1; /**< whether it is currently connected */
|
||||
unsigned int good : 1; /**< if we have an error on the socket we turn this off */
|
||||
unsigned int ping_outstanding : 1;
|
||||
unsigned int ping_due : 1; /**< we couldn't send a ping so we should send one when we can */
|
||||
signed int connect_state : 4;
|
||||
START_TIME_TYPE ping_due_time; /**< the time at which the ping should have been sent (ping_due) */
|
||||
networkHandles net; /**< network info for this client */
|
||||
int msgID; /**< the MQTT message id */
|
||||
int keepAliveInterval; /**< the MQTT keep alive interval */
|
||||
int retryInterval; /**< the MQTT retry interval for QoS > 0 */
|
||||
int maxInflightMessages; /**< the max number of inflight outbound messages we allow */
|
||||
willMessages* will; /**< the MQTT will message, if any */
|
||||
List* inboundMsgs; /**< inbound in flight messages */
|
||||
List* outboundMsgs; /**< outbound in flight messages */
|
||||
int connect_count; /**< the number of outbound messages on reconnect - to ensure we send them all */
|
||||
int connect_sent; /**< the current number of outbound messages on reconnect that we've sent */
|
||||
List* messageQueue; /**< inbound complete but undelivered messages */
|
||||
List* outboundQueue; /**< outbound queued messages */
|
||||
unsigned int qentry_seqno;
|
||||
void* phandle; /**< the persistence handle */
|
||||
MQTTClient_persistence* persistence; /**< a persistence implementation */
|
||||
MQTTPersistence_beforeWrite* beforeWrite; /**< persistence write callback */
|
||||
MQTTPersistence_afterRead* afterRead; /**< persistence read callback */
|
||||
void* beforeWrite_context; /**< context to be used with the persistence beforeWrite callbacks */
|
||||
void* afterRead_context; /**< context to be used with the persistence afterRead callback */
|
||||
void* context; /**< calling context - used when calling disconnect_internal */
|
||||
int MQTTVersion; /**< the version of MQTT being used, 3, 4 or 5 */
|
||||
unsigned int sessionExpiry; /**< MQTT 5 session expiry */
|
||||
char* httpProxy; /**< HTTP proxy */
|
||||
char* httpsProxy; /**< HTTPS proxy */
|
||||
#if defined(OPENSSL)
|
||||
MQTTClient_SSLOptions *sslopts; /**< the SSL/TLS connect options */
|
||||
SSL_SESSION* session; /**< SSL session pointer for fast handhake */
|
||||
#endif
|
||||
} Clients;
|
||||
|
||||
int clientIDCompare(void* a, void* b);
|
||||
int clientSocketCompare(void* a, void* b);
|
||||
|
||||
/**
|
||||
* Configuration data related to all clients
|
||||
*/
|
||||
typedef struct
|
||||
{
|
||||
const char* version;
|
||||
List* clients;
|
||||
} ClientStates;
|
||||
|
||||
#endif
|
||||
+530
@@ -0,0 +1,530 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2009, 2023 IBM Corp.
|
||||
*
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v2.0
|
||||
* and Eclipse Distribution License v1.0 which accompany this distribution.
|
||||
*
|
||||
* The Eclipse Public License is available at
|
||||
* https://www.eclipse.org/legal/epl-2.0/
|
||||
* and the Eclipse Distribution License is available at
|
||||
* http://www.eclipse.org/org/documents/edl-v10.php.
|
||||
*
|
||||
* Contributors:
|
||||
* Ian Craggs - initial API and implementation and/or initial documentation
|
||||
* Ian Craggs - use tree data structure instead of list
|
||||
* Ian Craggs - change roundup to Heap_roundup to avoid macro name clash on MacOSX
|
||||
*******************************************************************************/
|
||||
|
||||
/**
|
||||
* @file
|
||||
* \brief functions to manage the heap with the goal of eliminating memory leaks
|
||||
*
|
||||
* For any module to use these functions transparently, simply include the Heap.h
|
||||
* header file. Malloc and free will be redefined, but will behave in exactly the same
|
||||
* way as normal, so no recoding is necessary.
|
||||
*
|
||||
* */
|
||||
|
||||
#include "Tree.h"
|
||||
#include "Log.h"
|
||||
#include "StackTrace.h"
|
||||
#include "Thread.h"
|
||||
|
||||
#if defined(HEAP_UNIT_TESTS)
|
||||
char* Broker_recordFFDC(char* symptoms);
|
||||
#endif /* HEAP_UNIT_TESTS */
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <stddef.h>
|
||||
#include <inttypes.h>
|
||||
|
||||
#include "Heap.h"
|
||||
|
||||
#if !defined(NO_HEAP_TRACKING)
|
||||
|
||||
#undef malloc
|
||||
#undef realloc
|
||||
#undef free
|
||||
|
||||
#if defined(_WIN32) || defined(_WIN64)
|
||||
mutex_type heap_mutex;
|
||||
#else
|
||||
static pthread_mutex_t heap_mutex_store = PTHREAD_MUTEX_INITIALIZER;
|
||||
static mutex_type heap_mutex = &heap_mutex_store;
|
||||
#endif
|
||||
|
||||
static heap_info state = {0, 0}; /**< global heap state information */
|
||||
|
||||
typedef uint64_t eyecatcherType;
|
||||
static eyecatcherType eyecatcher = (eyecatcherType)0x8888888888888888;
|
||||
#define PRIeyecatcher PRIx64 /**< print eyecatcher in HEX notation */
|
||||
|
||||
/*#define HEAP_STACK 1 */
|
||||
|
||||
/**
|
||||
* Each item on the heap is recorded with this structure.
|
||||
*/
|
||||
typedef struct
|
||||
{
|
||||
char* file; /**< the name of the source file where the storage was allocated */
|
||||
int line; /**< the line no in the source file where it was allocated */
|
||||
void* ptr; /**< pointer to the allocated storage */
|
||||
size_t size; /**< size of the allocated storage */
|
||||
#if defined(HEAP_STACK)
|
||||
char* stack;
|
||||
#endif
|
||||
} storageElement;
|
||||
|
||||
static Tree heap; /**< Tree that holds the allocation records */
|
||||
static const char *errmsg = "Memory allocation error";
|
||||
|
||||
|
||||
static size_t Heap_roundup(size_t size);
|
||||
static int ptrCompare(void* a, void* b, int value);
|
||||
/*static void Heap_check(char* string, void* ptr);*/
|
||||
static void checkEyecatchers(char* file, int line, void* p, size_t size);
|
||||
static int Internal_heap_unlink(char* file, int line, void* p);
|
||||
static void HeapScan(enum LOG_LEVELS log_level);
|
||||
|
||||
|
||||
/**
|
||||
* Round allocation size up to a multiple of the size of an int. Apart from possibly reducing fragmentation,
|
||||
* on the old v3 gcc compilers I was hitting some weird behaviour, which might have been errors in
|
||||
* sizeof() used on structures and related to packing. In any case, this fixes that too.
|
||||
* @param size the size actually needed
|
||||
* @return the rounded up size
|
||||
*/
|
||||
static size_t Heap_roundup(size_t size)
|
||||
{
|
||||
static int multsize = 4*sizeof(int);
|
||||
|
||||
if (size % multsize != 0)
|
||||
size += multsize - (size % multsize);
|
||||
return size;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* List callback function for comparing storage elements
|
||||
* @param a pointer to the current content in the tree (storageElement*)
|
||||
* @param b pointer to the memory to free
|
||||
* @return boolean indicating whether a and b are equal
|
||||
*/
|
||||
static int ptrCompare(void* a, void* b, int value)
|
||||
{
|
||||
a = ((storageElement*)a)->ptr;
|
||||
if (value)
|
||||
b = ((storageElement*)b)->ptr;
|
||||
|
||||
return (a > b) ? -1 : (a == b) ? 0 : 1;
|
||||
}
|
||||
|
||||
/*
|
||||
static void Heap_check(char* string, void* ptr)
|
||||
{
|
||||
Node* curnode = NULL;
|
||||
storageElement* prev, *s = NULL;
|
||||
|
||||
printf("Heap_check start %p\n", ptr);
|
||||
while ((curnode = TreeNextElement(&heap, curnode)) != NULL)
|
||||
{
|
||||
prev = s;
|
||||
s = (storageElement*)(curnode->content);
|
||||
|
||||
if (prev)
|
||||
{
|
||||
if (ptrCompare(s, prev, 1) != -1)
|
||||
{
|
||||
printf("%s: heap order error %d %p %p\n", string, ptrCompare(s, prev, 1), prev->ptr, s->ptr);
|
||||
exit(99);
|
||||
}
|
||||
else
|
||||
printf("%s: heap order good %d %p %p\n", string, ptrCompare(s, prev, 1), prev->ptr, s->ptr);
|
||||
}
|
||||
}
|
||||
}*/
|
||||
|
||||
|
||||
/**
|
||||
* Allocates a block of memory. A direct replacement for malloc, but keeps track of items
|
||||
* allocated in a list, so that free can check that a item is being freed correctly and that
|
||||
* we can check that all memory is freed at shutdown.
|
||||
* @param file use the __FILE__ macro to indicate which file this item was allocated in
|
||||
* @param line use the __LINE__ macro to indicate which line this item was allocated at
|
||||
* @param size the size of the item to be allocated
|
||||
* @return pointer to the allocated item, or NULL if there was an error
|
||||
*/
|
||||
void* mymalloc(char* file, int line, size_t size)
|
||||
{
|
||||
storageElement* s = NULL;
|
||||
size_t space = sizeof(storageElement);
|
||||
size_t filenamelen = strlen(file)+1;
|
||||
void* rc = NULL;
|
||||
|
||||
Paho_thread_lock_mutex(heap_mutex);
|
||||
size = Heap_roundup(size);
|
||||
if ((s = malloc(sizeof(storageElement))) == NULL)
|
||||
{
|
||||
Log(LOG_ERROR, 13, errmsg);
|
||||
goto exit;
|
||||
}
|
||||
memset(s, 0, sizeof(storageElement));
|
||||
|
||||
s->size = size; /* size without eyecatchers */
|
||||
if ((s->file = malloc(filenamelen)) == NULL)
|
||||
{
|
||||
Log(LOG_ERROR, 13, errmsg);
|
||||
free(s);
|
||||
goto exit;
|
||||
}
|
||||
memset(s->file, 0, sizeof(filenamelen));
|
||||
|
||||
space += filenamelen;
|
||||
strcpy(s->file, file);
|
||||
#if defined(HEAP_STACK)
|
||||
#define STACK_LEN 300
|
||||
if ((s->stack = malloc(STACK_LEN)) == NULL)
|
||||
{
|
||||
Log(LOG_ERROR, 13, errmsg);
|
||||
free(s->file);
|
||||
free(s);
|
||||
goto exit;
|
||||
}
|
||||
memset(s->stack, 0, sizeof(filenamelen));
|
||||
StackTrace_get(Paho_thread_getid(), s->stack, STACK_LEN);
|
||||
#endif
|
||||
s->line = line;
|
||||
/* Add space for eyecatcher at each end */
|
||||
if ((s->ptr = malloc(size + 2*sizeof(eyecatcherType))) == NULL)
|
||||
{
|
||||
Log(LOG_ERROR, 13, errmsg);
|
||||
free(s->file);
|
||||
free(s);
|
||||
goto exit;
|
||||
}
|
||||
memset(s->ptr, 0, size + 2*sizeof(eyecatcherType));
|
||||
space += size + 2*sizeof(eyecatcherType);
|
||||
*(eyecatcherType*)(s->ptr) = eyecatcher; /* start eyecatcher */
|
||||
*(eyecatcherType*)(((char*)(s->ptr)) + (sizeof(eyecatcherType) + size)) = eyecatcher; /* end eyecatcher */
|
||||
Log(TRACE_MAX, -1, "Allocating %d bytes in heap at file %s line %d ptr %p\n", (int)size, file, line, s->ptr);
|
||||
TreeAdd(&heap, s, space);
|
||||
state.current_size += size;
|
||||
if (state.current_size > state.max_size)
|
||||
state.max_size = state.current_size;
|
||||
rc = ((eyecatcherType*)(s->ptr)) + 1; /* skip start eyecatcher */
|
||||
exit:
|
||||
Paho_thread_unlock_mutex(heap_mutex);
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
static void checkEyecatchers(char* file, int line, void* p, size_t size)
|
||||
{
|
||||
eyecatcherType *sp = (eyecatcherType*)p;
|
||||
char *cp = (char*)p;
|
||||
eyecatcherType us;
|
||||
static const char *msg = "Invalid %s eyecatcher %" PRIeyecatcher " in heap item at file %s line %d";
|
||||
|
||||
if ((us = *--sp) != eyecatcher)
|
||||
Log(LOG_ERROR, 13, msg, "start", us, file, line);
|
||||
|
||||
cp += size;
|
||||
if ((us = *(eyecatcherType*)cp) != eyecatcher)
|
||||
Log(LOG_ERROR, 13, msg, "end", us, file, line);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Remove an item from the recorded heap without actually freeing it.
|
||||
* Use sparingly!
|
||||
* @param file use the __FILE__ macro to indicate which file this item was allocated in
|
||||
* @param line use the __LINE__ macro to indicate which line this item was allocated at
|
||||
* @param p pointer to the item to be removed
|
||||
*/
|
||||
static int Internal_heap_unlink(char* file, int line, void* p)
|
||||
{
|
||||
Node* e = NULL;
|
||||
int rc = 0;
|
||||
|
||||
e = TreeFind(&heap, ((eyecatcherType*)p)-1);
|
||||
if (e == NULL)
|
||||
Log(LOG_ERROR, 13, "Failed to remove heap item at file %s line %d", file, line);
|
||||
else
|
||||
{
|
||||
storageElement* s = (storageElement*)(e->content);
|
||||
Log(TRACE_MAX, -1, "Freeing %d bytes in heap at file %s line %d, heap use now %d bytes\n",
|
||||
(int)s->size, file, line, (int)state.current_size);
|
||||
checkEyecatchers(file, line, p, s->size);
|
||||
/* free(s->ptr); */
|
||||
free(s->file);
|
||||
state.current_size -= s->size;
|
||||
TreeRemoveNodeIndex(&heap, e, 0);
|
||||
free(s);
|
||||
rc = 1;
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Frees a block of memory. A direct replacement for free, but checks that a item is in
|
||||
* the allocates list first.
|
||||
* @param file use the __FILE__ macro to indicate which file this item was allocated in
|
||||
* @param line use the __LINE__ macro to indicate which line this item was allocated at
|
||||
* @param p pointer to the item to be freed
|
||||
*/
|
||||
void myfree(char* file, int line, void* p)
|
||||
{
|
||||
if (p) /* it is legal und usual to call free(NULL) */
|
||||
{
|
||||
Paho_thread_lock_mutex(heap_mutex);
|
||||
if (Internal_heap_unlink(file, line, p))
|
||||
free(((eyecatcherType*)p)-1);
|
||||
Paho_thread_unlock_mutex(heap_mutex);
|
||||
}
|
||||
else
|
||||
{
|
||||
Log(LOG_ERROR, -1, "Call of free(NULL) in %s,%d",file,line);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Remove an item from the recorded heap without actually freeing it.
|
||||
* Use sparingly!
|
||||
* @param file use the __FILE__ macro to indicate which file this item was allocated in
|
||||
* @param line use the __LINE__ macro to indicate which line this item was allocated at
|
||||
* @param p pointer to the item to be removed
|
||||
*/
|
||||
void Heap_unlink(char* file, int line, void* p)
|
||||
{
|
||||
Paho_thread_lock_mutex(heap_mutex);
|
||||
Internal_heap_unlink(file, line, p);
|
||||
Paho_thread_unlock_mutex(heap_mutex);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Reallocates a block of memory. A direct replacement for realloc, but keeps track of items
|
||||
* allocated in a list, so that free can check that a item is being freed correctly and that
|
||||
* we can check that all memory is freed at shutdown.
|
||||
* We have to remove the item from the tree, as the memory is in order and so it needs to
|
||||
* be reinserted in the correct place.
|
||||
* @param file use the __FILE__ macro to indicate which file this item was reallocated in
|
||||
* @param line use the __LINE__ macro to indicate which line this item was reallocated at
|
||||
* @param p pointer to the item to be reallocated
|
||||
* @param size the new size of the item
|
||||
* @return pointer to the allocated item, or NULL if there was an error
|
||||
*/
|
||||
void *myrealloc(char* file, int line, void* p, size_t size)
|
||||
{
|
||||
void* rc = NULL;
|
||||
storageElement* s = NULL;
|
||||
|
||||
Paho_thread_lock_mutex(heap_mutex);
|
||||
s = TreeRemoveKey(&heap, ((eyecatcherType*)p)-1);
|
||||
if (s == NULL)
|
||||
Log(LOG_ERROR, 13, "Failed to reallocate heap item at file %s line %d", file, line);
|
||||
else
|
||||
{
|
||||
size_t space = sizeof(storageElement);
|
||||
size_t filenamelen = strlen(file)+1;
|
||||
|
||||
checkEyecatchers(file, line, p, s->size);
|
||||
size = Heap_roundup(size);
|
||||
state.current_size += size - s->size;
|
||||
if (state.current_size > state.max_size)
|
||||
state.max_size = state.current_size;
|
||||
if ((s->ptr = realloc(s->ptr, size + 2*sizeof(eyecatcherType))) == NULL)
|
||||
{
|
||||
Log(LOG_ERROR, 13, errmsg);
|
||||
goto exit;
|
||||
}
|
||||
space += size + 2*sizeof(eyecatcherType) - s->size;
|
||||
*(eyecatcherType*)(s->ptr) = eyecatcher; /* start eyecatcher */
|
||||
*(eyecatcherType*)(((char*)(s->ptr)) + (sizeof(eyecatcherType) + size)) = eyecatcher; /* end eyecatcher */
|
||||
s->size = size;
|
||||
space -= strlen(s->file);
|
||||
s->file = realloc(s->file, filenamelen);
|
||||
space += filenamelen;
|
||||
strcpy(s->file, file);
|
||||
s->line = line;
|
||||
rc = s->ptr;
|
||||
TreeAdd(&heap, s, space);
|
||||
}
|
||||
exit:
|
||||
Paho_thread_unlock_mutex(heap_mutex);
|
||||
return (rc == NULL) ? NULL : ((eyecatcherType*)(rc)) + 1; /* skip start eyecatcher */
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Utility to find an item in the heap. Lets you know if the heap already contains
|
||||
* the memory location in question.
|
||||
* @param p pointer to a memory location
|
||||
* @return pointer to the storage element if found, or NULL
|
||||
*/
|
||||
void* Heap_findItem(void* p)
|
||||
{
|
||||
Node* e = NULL;
|
||||
|
||||
Paho_thread_lock_mutex(heap_mutex);
|
||||
e = TreeFind(&heap, ((eyecatcherType*)p)-1);
|
||||
Paho_thread_unlock_mutex(heap_mutex);
|
||||
return (e == NULL) ? NULL : e->content;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Scans the heap and reports any items currently allocated.
|
||||
* To be used at shutdown if any heap items have not been freed.
|
||||
*/
|
||||
static void HeapScan(enum LOG_LEVELS log_level)
|
||||
{
|
||||
Node* current = NULL;
|
||||
|
||||
Paho_thread_lock_mutex(heap_mutex);
|
||||
Log(log_level, -1, "Heap scan start, total %d bytes", (int)state.current_size);
|
||||
while ((current = TreeNextElement(&heap, current)) != NULL)
|
||||
{
|
||||
storageElement* s = (storageElement*)(current->content);
|
||||
Log(log_level, -1, "Heap element size %d, line %d, file %s, ptr %p", (int)s->size, s->line, s->file, s->ptr);
|
||||
Log(log_level, -1, " Content %.*s", (10 > current->size) ? (int)s->size : 10, (char*)(((eyecatcherType*)s->ptr) + 1));
|
||||
#if defined(HEAP_STACK)
|
||||
Log(log_level, -1, " Stack:\n%s", s->stack);
|
||||
#endif
|
||||
}
|
||||
Log(log_level, -1, "Heap scan end");
|
||||
Paho_thread_unlock_mutex(heap_mutex);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Heap initialization.
|
||||
*/
|
||||
int Heap_initialize(void)
|
||||
{
|
||||
TreeInitializeNoMalloc(&heap, ptrCompare);
|
||||
heap.heap_tracking = 0; /* no recursive heap tracking! */
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Heap termination.
|
||||
*/
|
||||
void Heap_terminate(void)
|
||||
{
|
||||
Log(TRACE_MIN, -1, "Maximum heap use was %d bytes", (int)state.max_size);
|
||||
if (state.current_size > 20) /* One log list is freed after this function is called */
|
||||
{
|
||||
Log(LOG_ERROR, -1, "Some memory not freed at shutdown, possible memory leak");
|
||||
HeapScan(LOG_ERROR);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Access to heap state
|
||||
* @return pointer to the heap state structure
|
||||
*/
|
||||
heap_info* Heap_get_info(void)
|
||||
{
|
||||
return &state;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Dump a string from the heap so that it can be displayed conveniently
|
||||
* @param file file handle to dump the heap contents to
|
||||
* @param str the string to dump, could be NULL
|
||||
*/
|
||||
int HeapDumpString(FILE* file, char* str)
|
||||
{
|
||||
int rc = 0;
|
||||
size_t len = str ? strlen(str) + 1 : 0; /* include the trailing null */
|
||||
|
||||
if (fwrite(&(str), sizeof(char*), 1, file) != 1)
|
||||
rc = -1;
|
||||
else if (fwrite(&(len), sizeof(int), 1 ,file) != 1)
|
||||
rc = -1;
|
||||
else if (len > 0 && fwrite(str, len, 1, file) != 1)
|
||||
rc = -1;
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Dump the state of the heap
|
||||
* @param file file handle to dump the heap contents to
|
||||
*/
|
||||
int HeapDump(FILE* file)
|
||||
{
|
||||
int rc = 0;
|
||||
Node* current = NULL;
|
||||
|
||||
while (rc == 0 && (current = TreeNextElement(&heap, current)))
|
||||
{
|
||||
storageElement* s = (storageElement*)(current->content);
|
||||
|
||||
if (fwrite(&(s->ptr), sizeof(s->ptr), 1, file) != 1)
|
||||
rc = -1;
|
||||
else if (fwrite(&(current->size), sizeof(current->size), 1, file) != 1)
|
||||
rc = -1;
|
||||
else if (fwrite(s->ptr, current->size, 1, file) != 1)
|
||||
rc = -1;
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
#if defined(HEAP_UNIT_TESTS)
|
||||
|
||||
void Log(enum LOG_LEVELS log_level, int msgno, char* format, ...)
|
||||
{
|
||||
printf("Log %s", format);
|
||||
}
|
||||
|
||||
char* Broker_recordFFDC(char* symptoms)
|
||||
{
|
||||
printf("recordFFDC");
|
||||
return "";
|
||||
}
|
||||
|
||||
#define malloc(x) mymalloc(__FILE__, __LINE__, x)
|
||||
#define realloc(a, b) myrealloc(__FILE__, __LINE__, a, b)
|
||||
#define free(x) myfree(__FILE__, __LINE__, x)
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
char* h = NULL;
|
||||
Heap_initialize();
|
||||
|
||||
h = malloc(12);
|
||||
free(h);
|
||||
printf("freed h\n");
|
||||
|
||||
h = malloc(12);
|
||||
h = realloc(h, 14);
|
||||
h = realloc(h, 25);
|
||||
h = realloc(h, 255);
|
||||
h = realloc(h, 2225);
|
||||
h = realloc(h, 22225);
|
||||
printf("freeing h\n");
|
||||
free(h);
|
||||
Heap_terminate();
|
||||
printf("Finishing\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif /* HEAP_UNIT_TESTS */
|
||||
|
||||
/* Local Variables: */
|
||||
/* indent-tabs-mode: t */
|
||||
/* c-basic-offset: 8 */
|
||||
/* End: */
|
||||
+90
@@ -0,0 +1,90 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2009, 2020 IBM Corp. and others
|
||||
*
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v2.0
|
||||
* and Eclipse Distribution License v1.0 which accompany this distribution.
|
||||
*
|
||||
* The Eclipse Public License is available at
|
||||
* https://www.eclipse.org/legal/epl-2.0/
|
||||
* and the Eclipse Distribution License is available at
|
||||
* http://www.eclipse.org/org/documents/edl-v10.php.
|
||||
*
|
||||
* Contributors:
|
||||
* Ian Craggs - initial API and implementation and/or initial documentation
|
||||
* Ian Craggs - use tree data structure instead of list
|
||||
*******************************************************************************/
|
||||
|
||||
|
||||
#if !defined(HEAP_H)
|
||||
#define HEAP_H
|
||||
|
||||
#if defined(HIGH_PERFORMANCE)
|
||||
#define NO_HEAP_TRACKING 1
|
||||
#endif
|
||||
|
||||
#define PAHO_MEMORY_ERROR -99
|
||||
|
||||
#include "MQTTExportDeclarations.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#if !defined(NO_HEAP_TRACKING)
|
||||
|
||||
#if !defined(TREE_C)
|
||||
/**
|
||||
* redefines malloc to use "mymalloc" so that heap allocation can be tracked
|
||||
* @param x the size of the item to be allocated
|
||||
* @return the pointer to the item allocated, or NULL
|
||||
*/
|
||||
#define malloc(x) mymalloc(__FILE__, __LINE__, x)
|
||||
|
||||
/**
|
||||
* redefines realloc to use "myrealloc" so that heap allocation can be tracked
|
||||
* @param a the heap item to be reallocated
|
||||
* @param b the new size of the item
|
||||
* @return the new pointer to the heap item
|
||||
*/
|
||||
#define realloc(a, b) myrealloc(__FILE__, __LINE__, a, b)
|
||||
|
||||
/**
|
||||
* redefines free to use "myfree" so that heap allocation can be tracked
|
||||
* @param x the pointer to the item to be freed
|
||||
*/
|
||||
#define free(x) myfree(__FILE__, __LINE__, x)
|
||||
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Information about the state of the heap.
|
||||
*/
|
||||
typedef struct
|
||||
{
|
||||
size_t current_size; /**< current size of the heap in bytes */
|
||||
size_t max_size; /**< max size the heap has reached in bytes */
|
||||
} heap_info;
|
||||
|
||||
#if defined(__cplusplus)
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
void* mymalloc(char*, int, size_t size);
|
||||
void* myrealloc(char*, int, void* p, size_t size);
|
||||
void myfree(char*, int, void* p);
|
||||
|
||||
void Heap_scan(FILE* file);
|
||||
int Heap_initialize(void);
|
||||
void Heap_terminate(void);
|
||||
LIBMQTT_API heap_info* Heap_get_info(void);
|
||||
int HeapDump(FILE* file);
|
||||
int HeapDumpString(FILE* file, char* str);
|
||||
void* Heap_findItem(void* p);
|
||||
void Heap_unlink(char* file, int line, void* p);
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,22 @@
|
||||
#if WINVER <= _WIN32_WINNT_WIN8
|
||||
#define HTON(x) hton((uint64_t) (x), sizeof(x))
|
||||
uint64_t hton(uint64_t x, size_t n)
|
||||
{
|
||||
uint64_t y = 0;
|
||||
size_t i = 0;
|
||||
|
||||
for (i=0; i < n; ++i)
|
||||
{
|
||||
y = (y << 8) | (x & 0xff);
|
||||
x = (x >> 8);
|
||||
}
|
||||
return y;
|
||||
}
|
||||
#define htons(x) (uint16_t) HTON(x)
|
||||
#define htonl(x) (uint32_t) HTON(x)
|
||||
#define htonll(x) (uint64_t) HTON(x)
|
||||
|
||||
#define ntohs(x) htons(x)
|
||||
#define ntohl(x) htonl(x)
|
||||
#define ntohll(x) htonll(x)
|
||||
#endif
|
||||
+508
@@ -0,0 +1,508 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2009, 2020 IBM Corp.
|
||||
*
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v2.0
|
||||
* and Eclipse Distribution License v1.0 which accompany this distribution.
|
||||
*
|
||||
* The Eclipse Public License is available at
|
||||
* https://www.eclipse.org/legal/epl-2.0/
|
||||
* and the Eclipse Distribution License is available at
|
||||
* http://www.eclipse.org/org/documents/edl-v10.php.
|
||||
*
|
||||
* Contributors:
|
||||
* Ian Craggs - initial API and implementation and/or initial documentation
|
||||
* Ian Craggs - updates for the async client
|
||||
*******************************************************************************/
|
||||
|
||||
/**
|
||||
* @file
|
||||
* \brief functions which apply to linked list structures.
|
||||
*
|
||||
* These linked lists can hold data of any sort, pointed to by the content pointer of the
|
||||
* ListElement structure. ListElements hold the points to the next and previous items in the
|
||||
* list.
|
||||
* */
|
||||
|
||||
#include "LinkedList.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "Heap.h"
|
||||
|
||||
|
||||
static int ListUnlink(List* aList, void* content, int(*callback)(void*, void*), int freeContent);
|
||||
|
||||
|
||||
/**
|
||||
* Sets a list structure to empty - all null values. Does not remove any items from the list.
|
||||
* @param newl a pointer to the list structure to be initialized
|
||||
*/
|
||||
void ListZero(List* newl)
|
||||
{
|
||||
memset(newl, '\0', sizeof(List));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Allocates and initializes a new list structure.
|
||||
* @return a pointer to the new list structure
|
||||
*/
|
||||
List* ListInitialize(void)
|
||||
{
|
||||
List* newl = malloc(sizeof(List));
|
||||
if (newl)
|
||||
ListZero(newl);
|
||||
return newl;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Append an already allocated ListElement and content to a list. Can be used to move
|
||||
* an item from one list to another.
|
||||
* @param aList the list to which the item is to be added
|
||||
* @param content the list item content itself
|
||||
* @param newel the ListElement to be used in adding the new item
|
||||
* @param size the size of the element
|
||||
*/
|
||||
void ListAppendNoMalloc(List* aList, void* content, ListElement* newel, size_t size)
|
||||
{ /* for heap use */
|
||||
newel->content = content;
|
||||
newel->next = NULL;
|
||||
newel->prev = aList->last;
|
||||
if (aList->first == NULL)
|
||||
aList->first = newel;
|
||||
else
|
||||
aList->last->next = newel;
|
||||
aList->last = newel;
|
||||
++(aList->count);
|
||||
aList->size += size;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Append an item to a list.
|
||||
* @param aList the list to which the item is to be added
|
||||
* @param content the list item content itself
|
||||
* @param size the size of the element
|
||||
*/
|
||||
ListElement* ListAppend(List* aList, void* content, size_t size)
|
||||
{
|
||||
ListElement* newel = malloc(sizeof(ListElement));
|
||||
if (newel)
|
||||
ListAppendNoMalloc(aList, content, newel, size);
|
||||
return newel;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Insert an item to a list at a specific position.
|
||||
* @param aList the list to which the item is to be added
|
||||
* @param content the list item content itself
|
||||
* @param size the size of the element
|
||||
* @param index the position in the list. If NULL, this function is equivalent
|
||||
* to ListAppend.
|
||||
*/
|
||||
ListElement* ListInsert(List* aList, void* content, size_t size, ListElement* index)
|
||||
{
|
||||
ListElement* newel = malloc(sizeof(ListElement));
|
||||
|
||||
if (newel == NULL)
|
||||
return newel;
|
||||
if ( index == NULL )
|
||||
ListAppendNoMalloc(aList, content, newel, size);
|
||||
else
|
||||
{
|
||||
newel->content = content;
|
||||
newel->next = index;
|
||||
newel->prev = index->prev;
|
||||
|
||||
index->prev = newel;
|
||||
if ( newel->prev != NULL )
|
||||
newel->prev->next = newel;
|
||||
else
|
||||
aList->first = newel;
|
||||
|
||||
++(aList->count);
|
||||
aList->size += size;
|
||||
}
|
||||
return newel;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Finds an element in a list by comparing the content pointers, rather than the contents
|
||||
* @param aList the list in which the search is to be conducted
|
||||
* @param content pointer to the list item content itself
|
||||
* @return the list item found, or NULL
|
||||
*/
|
||||
ListElement* ListFind(List* aList, void* content)
|
||||
{
|
||||
return ListFindItem(aList, content, NULL);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Finds an element in a list by comparing the content or pointer to the content. A callback
|
||||
* function is used to define the method of comparison for each element.
|
||||
* @param aList the list in which the search is to be conducted
|
||||
* @param content pointer to the content to look for
|
||||
* @param callback pointer to a function which compares each element (NULL means compare by content pointer)
|
||||
* @return the list element found, or NULL
|
||||
*/
|
||||
ListElement* ListFindItem(List* aList, void* content, int(*callback)(void*, void*))
|
||||
{
|
||||
ListElement* rc = NULL;
|
||||
|
||||
if (aList->current != NULL && ((callback == NULL && aList->current->content == content) ||
|
||||
(callback != NULL && callback(aList->current->content, content))))
|
||||
rc = aList->current;
|
||||
else
|
||||
{
|
||||
ListElement* current = NULL;
|
||||
|
||||
/* find the content */
|
||||
while (ListNextElement(aList, ¤t) != NULL)
|
||||
{
|
||||
if (callback == NULL)
|
||||
{
|
||||
if (current->content == content)
|
||||
{
|
||||
rc = current;
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (callback(current->content, content))
|
||||
{
|
||||
rc = current;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (rc != NULL)
|
||||
aList->current = rc;
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Removes and optionally frees an element in a list by comparing the content.
|
||||
* A callback function is used to define the method of comparison for each element.
|
||||
* @param aList the list in which the search is to be conducted
|
||||
* @param content pointer to the content to look for
|
||||
* @param callback pointer to a function which compares each element
|
||||
* @param freeContent boolean value to indicate whether the item found is to be freed
|
||||
* @return 1=item removed, 0=item not removed
|
||||
*/
|
||||
static int ListUnlink(List* aList, void* content, int(*callback)(void*, void*), int freeContent)
|
||||
{
|
||||
ListElement* next = NULL;
|
||||
ListElement* saved = aList->current;
|
||||
int saveddeleted = 0;
|
||||
|
||||
if (!ListFindItem(aList, content, callback))
|
||||
return 0; /* false, did not remove item */
|
||||
|
||||
if (aList->current->prev == NULL)
|
||||
/* so this is the first element, and we have to update the "first" pointer */
|
||||
aList->first = aList->current->next;
|
||||
else
|
||||
aList->current->prev->next = aList->current->next;
|
||||
|
||||
if (aList->current->next == NULL)
|
||||
aList->last = aList->current->prev;
|
||||
else
|
||||
aList->current->next->prev = aList->current->prev;
|
||||
|
||||
next = aList->current->next;
|
||||
if (freeContent)
|
||||
{
|
||||
free(aList->current->content);
|
||||
aList->current->content = NULL;
|
||||
}
|
||||
if (saved == aList->current)
|
||||
saveddeleted = 1;
|
||||
free(aList->current);
|
||||
if (saveddeleted)
|
||||
aList->current = next;
|
||||
else
|
||||
aList->current = saved;
|
||||
--(aList->count);
|
||||
return 1; /* successfully removed item */
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Removes but does not free an item in a list by comparing the pointer to the content.
|
||||
* @param aList the list in which the search is to be conducted
|
||||
* @param content pointer to the content to look for
|
||||
* @return 1=item removed, 0=item not removed
|
||||
*/
|
||||
int ListDetach(List* aList, void* content)
|
||||
{
|
||||
return ListUnlink(aList, content, NULL, 0);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Removes and frees an item in a list by comparing the pointer to the content.
|
||||
* @param aList the list from which the item is to be removed
|
||||
* @param content pointer to the content to look for
|
||||
* @return 1=item removed, 0=item not removed
|
||||
*/
|
||||
int ListRemove(List* aList, void* content)
|
||||
{
|
||||
return ListUnlink(aList, content, NULL, 1);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Removes and frees an the first item in a list.
|
||||
* @param aList the list from which the item is to be removed
|
||||
* @return 1=item removed, 0=item not removed
|
||||
*/
|
||||
void* ListDetachHead(List* aList)
|
||||
{
|
||||
void *content = NULL;
|
||||
if (aList->count > 0)
|
||||
{
|
||||
ListElement* first = aList->first;
|
||||
if (aList->current == first)
|
||||
aList->current = first->next;
|
||||
if (aList->last == first) /* i.e. no of items in list == 1 */
|
||||
aList->last = NULL;
|
||||
content = first->content;
|
||||
aList->first = aList->first->next;
|
||||
if (aList->first)
|
||||
aList->first->prev = NULL;
|
||||
free(first);
|
||||
--(aList->count);
|
||||
}
|
||||
return content;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Removes and frees an the first item in a list.
|
||||
* @param aList the list from which the item is to be removed
|
||||
* @return 1=item removed, 0=item not removed
|
||||
*/
|
||||
int ListRemoveHead(List* aList)
|
||||
{
|
||||
free(ListDetachHead(aList));
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Removes but does not free the last item in a list.
|
||||
* @param aList the list from which the item is to be removed
|
||||
* @return the last item removed (or NULL if none was)
|
||||
*/
|
||||
void* ListPopTail(List* aList)
|
||||
{
|
||||
void* content = NULL;
|
||||
if (aList->count > 0)
|
||||
{
|
||||
ListElement* last = aList->last;
|
||||
if (aList->current == last)
|
||||
aList->current = last->prev;
|
||||
if (aList->first == last) /* i.e. no of items in list == 1 */
|
||||
aList->first = NULL;
|
||||
content = last->content;
|
||||
aList->last = aList->last->prev;
|
||||
if (aList->last)
|
||||
aList->last->next = NULL;
|
||||
free(last);
|
||||
--(aList->count);
|
||||
}
|
||||
return content;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Removes but does not free an element in a list by comparing the content.
|
||||
* A callback function is used to define the method of comparison for each element.
|
||||
* @param aList the list in which the search is to be conducted
|
||||
* @param content pointer to the content to look for
|
||||
* @param callback pointer to a function which compares each element
|
||||
* @return 1=item removed, 0=item not removed
|
||||
*/
|
||||
int ListDetachItem(List* aList, void* content, int(*callback)(void*, void*))
|
||||
{ /* do not free the content */
|
||||
return ListUnlink(aList, content, callback, 0);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Removes and frees an element in a list by comparing the content.
|
||||
* A callback function is used to define the method of comparison for each element
|
||||
* @param aList the list in which the search is to be conducted
|
||||
* @param content pointer to the content to look for
|
||||
* @param callback pointer to a function which compares each element
|
||||
* @return 1=item removed, 0=item not removed
|
||||
*/
|
||||
int ListRemoveItem(List* aList, void* content, int(*callback)(void*, void*))
|
||||
{ /* remove from list and free the content */
|
||||
return ListUnlink(aList, content, callback, 1);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Removes and frees all items in a list, leaving the list ready for new items.
|
||||
* @param aList the list to which the operation is to be applied
|
||||
*/
|
||||
void ListEmpty(List* aList)
|
||||
{
|
||||
while (aList->first != NULL)
|
||||
{
|
||||
ListElement* first = aList->first;
|
||||
if (first->content != NULL)
|
||||
{
|
||||
free(first->content);
|
||||
first->content = NULL;
|
||||
}
|
||||
aList->first = first->next;
|
||||
free(first);
|
||||
}
|
||||
aList->count = 0;
|
||||
aList->size = 0;
|
||||
aList->current = aList->first = aList->last = NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes and frees all items in a list, and frees the list itself
|
||||
* @param aList the list to which the operation is to be applied
|
||||
*/
|
||||
void ListFree(List* aList)
|
||||
{
|
||||
ListEmpty(aList);
|
||||
free(aList);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Removes and but does not free all items in a list, and frees the list itself
|
||||
* @param aList the list to which the operation is to be applied
|
||||
*/
|
||||
void ListFreeNoContent(List* aList)
|
||||
{
|
||||
while (aList->first != NULL)
|
||||
{
|
||||
ListElement* first = aList->first;
|
||||
aList->first = first->next;
|
||||
free(first);
|
||||
}
|
||||
free(aList);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Forward iteration through a list
|
||||
* @param aList the list to which the operation is to be applied
|
||||
* @param pos pointer to the current position in the list. NULL means start from the beginning of the list
|
||||
* This is updated on return to the same value as that returned from this function
|
||||
* @return pointer to the current list element
|
||||
*/
|
||||
ListElement* ListNextElement(List* aList, ListElement** pos)
|
||||
{
|
||||
return *pos = (*pos == NULL) ? aList->first : (*pos)->next;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Backward iteration through a list
|
||||
* @param aList the list to which the operation is to be applied
|
||||
* @param pos pointer to the current position in the list. NULL means start from the end of the list
|
||||
* This is updated on return to the same value as that returned from this function
|
||||
* @return pointer to the current list element
|
||||
*/
|
||||
ListElement* ListPrevElement(List* aList, ListElement** pos)
|
||||
{
|
||||
return *pos = (*pos == NULL) ? aList->last : (*pos)->prev;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* List callback function for comparing integers
|
||||
* @param a first integer value
|
||||
* @param b second integer value
|
||||
* @return boolean indicating whether a and b are equal
|
||||
*/
|
||||
int intcompare(void* a, void* b)
|
||||
{
|
||||
return *((int*)a) == *((int*)b);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* List callback function for comparing C strings
|
||||
* @param a first integer value
|
||||
* @param b second integer value
|
||||
* @return boolean indicating whether a and b are equal
|
||||
*/
|
||||
int stringcompare(void* a, void* b)
|
||||
{
|
||||
return strcmp((char*)a, (char*)b) == 0;
|
||||
}
|
||||
|
||||
|
||||
#if defined(UNIT_TESTS)
|
||||
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
int i, *ip, *todelete;
|
||||
ListElement* current = NULL;
|
||||
List* l = ListInitialize();
|
||||
printf("List initialized\n");
|
||||
|
||||
for (i = 0; i < 10; i++)
|
||||
{
|
||||
ip = malloc(sizeof(int));
|
||||
*ip = i;
|
||||
ListAppend(l, (void*)ip, sizeof(int));
|
||||
if (i==5)
|
||||
todelete = ip;
|
||||
printf("List element appended %d\n", *((int*)(l->last->content)));
|
||||
}
|
||||
|
||||
printf("List contents:\n");
|
||||
current = NULL;
|
||||
while (ListNextElement(l, ¤t) != NULL)
|
||||
printf("List element: %d\n", *((int*)(current->content)));
|
||||
|
||||
printf("List contents in reverse order:\n");
|
||||
current = NULL;
|
||||
while (ListPrevElement(l, ¤t) != NULL)
|
||||
printf("List element: %d\n", *((int*)(current->content)));
|
||||
|
||||
/* if ListFindItem(l, *ip, intcompare)->content */
|
||||
|
||||
printf("List contents having deleted element %d:\n", *todelete);
|
||||
ListRemove(l, todelete);
|
||||
current = NULL;
|
||||
while (ListNextElement(l, ¤t) != NULL)
|
||||
printf("List element: %d\n", *((int*)(current->content)));
|
||||
|
||||
i = 9;
|
||||
ListRemoveItem(l, &i, intcompare);
|
||||
printf("List contents having deleted another element, %d, size now %d:\n", i, l->size);
|
||||
current = NULL;
|
||||
while (ListNextElement(l, ¤t) != NULL)
|
||||
printf("List element: %d\n", *((int*)(current->content)));
|
||||
|
||||
ListFree(l);
|
||||
printf("List freed\n");
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
+105
@@ -0,0 +1,105 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2009, 2020 IBM Corp.
|
||||
*
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v2.0
|
||||
* and Eclipse Distribution License v1.0 which accompany this distribution.
|
||||
*
|
||||
* The Eclipse Public License is available at
|
||||
* https://www.eclipse.org/legal/epl-2.0/
|
||||
* and the Eclipse Distribution License is available at
|
||||
* http://www.eclipse.org/org/documents/edl-v10.php.
|
||||
*
|
||||
* Contributors:
|
||||
* Ian Craggs - initial API and implementation and/or initial documentation
|
||||
* Ian Craggs - updates for the async client
|
||||
* Ian Craggs - change size types from int to size_t
|
||||
*******************************************************************************/
|
||||
|
||||
#if !defined(LINKEDLIST_H)
|
||||
#define LINKEDLIST_H
|
||||
|
||||
#include <stdlib.h> /* for size_t definition */
|
||||
|
||||
/*BE
|
||||
defm defList(T)
|
||||
|
||||
def T concat Item
|
||||
{
|
||||
at 4
|
||||
n32 ptr T concat Item suppress "next"
|
||||
at 0
|
||||
n32 ptr T concat Item suppress "prev"
|
||||
at 8
|
||||
n32 ptr T id2str(T)
|
||||
}
|
||||
|
||||
def T concat List
|
||||
{
|
||||
n32 ptr T concat Item suppress "first"
|
||||
n32 ptr T concat Item suppress "last"
|
||||
n32 ptr T concat Item suppress "current"
|
||||
n32 dec "count"
|
||||
n32 suppress "size"
|
||||
}
|
||||
endm
|
||||
|
||||
defList(INT)
|
||||
defList(STRING)
|
||||
defList(TMP)
|
||||
|
||||
BE*/
|
||||
|
||||
/**
|
||||
* Structure to hold all data for one list element
|
||||
*/
|
||||
typedef struct ListElementStruct
|
||||
{
|
||||
struct ListElementStruct *prev, /**< pointer to previous list element */
|
||||
*next; /**< pointer to next list element */
|
||||
void* content; /**< pointer to element content */
|
||||
} ListElement;
|
||||
|
||||
|
||||
/**
|
||||
* Structure to hold all data for one list
|
||||
*/
|
||||
typedef struct
|
||||
{
|
||||
ListElement *first, /**< first element in the list */
|
||||
*last, /**< last element in the list */
|
||||
*current; /**< current element in the list, for iteration */
|
||||
int count; /**< no of items */
|
||||
size_t size; /**< heap storage used */
|
||||
} List;
|
||||
|
||||
void ListZero(List*);
|
||||
List* ListInitialize(void);
|
||||
|
||||
ListElement* ListAppend(List* aList, void* content, size_t size);
|
||||
void ListAppendNoMalloc(List* aList, void* content, ListElement* newel, size_t size);
|
||||
ListElement* ListInsert(List* aList, void* content, size_t size, ListElement* index);
|
||||
|
||||
int ListRemove(List* aList, void* content);
|
||||
int ListRemoveItem(List* aList, void* content, int(*callback)(void*, void*));
|
||||
void* ListDetachHead(List* aList);
|
||||
int ListRemoveHead(List* aList);
|
||||
void* ListPopTail(List* aList);
|
||||
|
||||
int ListDetach(List* aList, void* content);
|
||||
int ListDetachItem(List* aList, void* content, int(*callback)(void*, void*));
|
||||
|
||||
void ListFree(List* aList);
|
||||
void ListEmpty(List* aList);
|
||||
void ListFreeNoContent(List* aList);
|
||||
|
||||
ListElement* ListNextElement(List* aList, ListElement** pos);
|
||||
ListElement* ListPrevElement(List* aList, ListElement** pos);
|
||||
|
||||
ListElement* ListFind(List* aList, void* content);
|
||||
ListElement* ListFindItem(List* aList, void* content, int(*callback)(void*, void*));
|
||||
|
||||
int intcompare(void* a, void* b);
|
||||
int stringcompare(void* a, void* b);
|
||||
|
||||
#endif
|
||||
+585
@@ -0,0 +1,585 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2009, 2023 IBM Corp.
|
||||
*
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v2.0
|
||||
* and Eclipse Distribution License v1.0 which accompany this distribution.
|
||||
*
|
||||
* The Eclipse Public License is available at
|
||||
* https://www.eclipse.org/legal/epl-2.0/
|
||||
* and the Eclipse Distribution License is available at
|
||||
* http://www.eclipse.org/org/documents/edl-v10.php.
|
||||
*
|
||||
* Contributors:
|
||||
* Ian Craggs - initial API and implementation and/or initial documentation
|
||||
* Ian Craggs - updates for the async client
|
||||
* Ian Craggs - fix for bug #427028
|
||||
*******************************************************************************/
|
||||
|
||||
/**
|
||||
* @file
|
||||
* \brief Logging and tracing module
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
#include "Log.h"
|
||||
#include "MQTTPacket.h"
|
||||
#include "MQTTProtocol.h"
|
||||
#include "MQTTProtocolClient.h"
|
||||
#include "Messages.h"
|
||||
#include "LinkedList.h"
|
||||
#include "StackTrace.h"
|
||||
#include "Thread.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdarg.h>
|
||||
#include <time.h>
|
||||
#include <string.h>
|
||||
|
||||
#if !defined(_WIN32) && !defined(_WIN64)
|
||||
#include <syslog.h>
|
||||
#include <sys/stat.h>
|
||||
#define GETTIMEOFDAY 1
|
||||
#else
|
||||
#define snprintf _snprintf
|
||||
#endif
|
||||
|
||||
#if defined(GETTIMEOFDAY)
|
||||
#include <sys/time.h>
|
||||
#else
|
||||
#include <sys/timeb.h>
|
||||
#endif
|
||||
|
||||
#if !defined(_WIN32) && !defined(_WIN64)
|
||||
/**
|
||||
* _unlink mapping for linux
|
||||
*/
|
||||
#define _unlink unlink
|
||||
#endif
|
||||
|
||||
|
||||
#if !defined(min)
|
||||
#define min(A,B) ( (A) < (B) ? (A):(B))
|
||||
#endif
|
||||
|
||||
trace_settings_type trace_settings =
|
||||
{
|
||||
#if defined(HIGH_PERFORMANCE)
|
||||
LOG_ERROR,
|
||||
#else
|
||||
TRACE_MINIMUM,
|
||||
#endif
|
||||
400,
|
||||
INVALID_LEVEL
|
||||
};
|
||||
|
||||
#define MAX_FUNCTION_NAME_LENGTH 256
|
||||
|
||||
typedef struct
|
||||
{
|
||||
#if defined(GETTIMEOFDAY)
|
||||
struct timeval ts;
|
||||
#else
|
||||
struct timeb ts;
|
||||
#endif
|
||||
int sametime_count;
|
||||
int number;
|
||||
thread_id_type thread_id;
|
||||
int depth;
|
||||
char name[MAX_FUNCTION_NAME_LENGTH + 1];
|
||||
int line;
|
||||
int has_rc;
|
||||
int rc;
|
||||
enum LOG_LEVELS level;
|
||||
} traceEntry;
|
||||
|
||||
static int start_index = -1,
|
||||
next_index = 0;
|
||||
static traceEntry* trace_queue = NULL;
|
||||
static int trace_queue_size = 0;
|
||||
|
||||
static FILE* trace_destination = NULL; /**< flag to indicate if trace is to be sent to a stream */
|
||||
static char* trace_destination_name = NULL; /**< the name of the trace file */
|
||||
static char* trace_destination_backup_name = NULL; /**< the name of the backup trace file */
|
||||
static int lines_written = 0; /**< number of lines written to the current output file */
|
||||
static int max_lines_per_file = 1000; /**< maximum number of lines to write to one trace file */
|
||||
static enum LOG_LEVELS trace_output_level = INVALID_LEVEL;
|
||||
static Log_traceCallback* trace_callback = NULL;
|
||||
static traceEntry* Log_pretrace(void);
|
||||
static char* Log_formatTraceEntry(traceEntry* cur_entry);
|
||||
static void Log_output(enum LOG_LEVELS log_level, const char *msg);
|
||||
static void Log_posttrace(enum LOG_LEVELS log_level, traceEntry* cur_entry);
|
||||
static void Log_trace(enum LOG_LEVELS log_level, const char *buf);
|
||||
#if 0
|
||||
static FILE* Log_destToFile(const char *dest);
|
||||
static int Log_compareEntries(const char *entry1, const char *entry2);
|
||||
#endif
|
||||
|
||||
static int sametime_count = 0;
|
||||
#if defined(GETTIMEOFDAY)
|
||||
struct timeval now_ts, last_ts;
|
||||
#else
|
||||
struct timeb now_ts, last_ts;
|
||||
#endif
|
||||
static char msg_buf[512];
|
||||
|
||||
#if defined(_WIN32) || defined(_WIN64)
|
||||
mutex_type log_mutex;
|
||||
#else
|
||||
static pthread_mutex_t log_mutex_store = PTHREAD_MUTEX_INITIALIZER;
|
||||
static mutex_type log_mutex = &log_mutex_store;
|
||||
#endif
|
||||
|
||||
|
||||
int Log_initialize(Log_nameValue* info)
|
||||
{
|
||||
int rc = SOCKET_ERROR;
|
||||
char* envval = NULL;
|
||||
#if !defined(_WIN32) && !defined(_WIN64)
|
||||
struct stat buf;
|
||||
#endif
|
||||
|
||||
if ((trace_queue = malloc(sizeof(traceEntry) * trace_settings.max_trace_entries)) == NULL)
|
||||
goto exit;
|
||||
trace_queue_size = trace_settings.max_trace_entries;
|
||||
|
||||
if ((envval = getenv("MQTT_C_CLIENT_TRACE")) != NULL && strlen(envval) > 0)
|
||||
{
|
||||
if (strcmp(envval, "ON") == 0 || (trace_destination = fopen(envval, "w")) == NULL)
|
||||
trace_destination = stdout;
|
||||
else
|
||||
{
|
||||
size_t namelen = 0;
|
||||
|
||||
if ((trace_destination_name = malloc(strlen(envval) + 1)) == NULL)
|
||||
{
|
||||
free(trace_queue);
|
||||
goto exit;
|
||||
}
|
||||
strcpy(trace_destination_name, envval);
|
||||
namelen = strlen(envval) + 3;
|
||||
if ((trace_destination_backup_name = malloc(namelen)) == NULL)
|
||||
{
|
||||
free(trace_queue);
|
||||
free(trace_destination_name);
|
||||
goto exit;
|
||||
}
|
||||
if (snprintf(trace_destination_backup_name, namelen, "%s.0", trace_destination_name) >= namelen)
|
||||
trace_destination_backup_name[namelen-1] = '\0';
|
||||
}
|
||||
}
|
||||
if ((envval = getenv("MQTT_C_CLIENT_TRACE_MAX_LINES")) != NULL && strlen(envval) > 0)
|
||||
{
|
||||
max_lines_per_file = atoi(envval);
|
||||
if (max_lines_per_file <= 0)
|
||||
max_lines_per_file = 1000;
|
||||
}
|
||||
if ((envval = getenv("MQTT_C_CLIENT_TRACE_LEVEL")) != NULL && strlen(envval) > 0)
|
||||
{
|
||||
if (strcmp(envval, "MAXIMUM") == 0 || strcmp(envval, "TRACE_MAXIMUM") == 0)
|
||||
trace_settings.trace_level = TRACE_MAXIMUM;
|
||||
else if (strcmp(envval, "MEDIUM") == 0 || strcmp(envval, "TRACE_MEDIUM") == 0)
|
||||
trace_settings.trace_level = TRACE_MEDIUM;
|
||||
else if (strcmp(envval, "MINIMUM") == 0 || strcmp(envval, "TRACE_MINIMUM") == 0)
|
||||
trace_settings.trace_level = TRACE_MINIMUM;
|
||||
else if (strcmp(envval, "PROTOCOL") == 0 || strcmp(envval, "TRACE_PROTOCOL") == 0)
|
||||
trace_output_level = TRACE_PROTOCOL;
|
||||
else if (strcmp(envval, "ERROR") == 0 || strcmp(envval, "TRACE_ERROR") == 0)
|
||||
trace_output_level = LOG_ERROR;
|
||||
}
|
||||
Log_output(TRACE_MINIMUM, "=========================================================");
|
||||
Log_output(TRACE_MINIMUM, " Trace Output");
|
||||
if (info)
|
||||
{
|
||||
while (info->name)
|
||||
{
|
||||
snprintf(msg_buf, sizeof(msg_buf), "%s: %s", info->name, info->value);
|
||||
Log_output(TRACE_MINIMUM, msg_buf);
|
||||
info++;
|
||||
}
|
||||
}
|
||||
#if !defined(_WIN32) && !defined(_WIN64)
|
||||
if (stat("/proc/version", &buf) != -1)
|
||||
{
|
||||
FILE* vfile;
|
||||
|
||||
if ((vfile = fopen("/proc/version", "r")) != NULL)
|
||||
{
|
||||
int len;
|
||||
|
||||
strcpy(msg_buf, "/proc/version: ");
|
||||
len = strlen(msg_buf);
|
||||
if (fgets(&msg_buf[len], sizeof(msg_buf) - len, vfile))
|
||||
Log_output(TRACE_MINIMUM, msg_buf);
|
||||
fclose(vfile);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
Log_output(TRACE_MINIMUM, "=========================================================");
|
||||
exit:
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
void Log_setTraceCallback(Log_traceCallback* callback)
|
||||
{
|
||||
trace_callback = callback;
|
||||
}
|
||||
|
||||
|
||||
void Log_setTraceLevel(enum LOG_LEVELS level)
|
||||
{
|
||||
if (level <= LOG_ERROR) /* the lowest we can go is LOG_ERROR */
|
||||
trace_settings.trace_level = level;
|
||||
trace_output_level = level;
|
||||
}
|
||||
|
||||
|
||||
void Log_terminate(void)
|
||||
{
|
||||
free(trace_queue);
|
||||
trace_queue = NULL;
|
||||
trace_queue_size = 0;
|
||||
if (trace_destination)
|
||||
{
|
||||
if (trace_destination != stdout)
|
||||
fclose(trace_destination);
|
||||
trace_destination = NULL;
|
||||
}
|
||||
if (trace_destination_name) {
|
||||
free(trace_destination_name);
|
||||
trace_destination_name = NULL;
|
||||
}
|
||||
if (trace_destination_backup_name) {
|
||||
free(trace_destination_backup_name);
|
||||
trace_destination_backup_name = NULL;
|
||||
}
|
||||
start_index = -1;
|
||||
next_index = 0;
|
||||
trace_output_level = INVALID_LEVEL;
|
||||
sametime_count = 0;
|
||||
}
|
||||
|
||||
|
||||
static traceEntry* Log_pretrace(void)
|
||||
{
|
||||
traceEntry *cur_entry = NULL;
|
||||
|
||||
/* calling ftime/gettimeofday seems to be comparatively expensive, so we need to limit its use */
|
||||
if (++sametime_count % 20 == 0)
|
||||
{
|
||||
#if defined(GETTIMEOFDAY)
|
||||
gettimeofday(&now_ts, NULL);
|
||||
if (now_ts.tv_sec != last_ts.tv_sec || now_ts.tv_usec != last_ts.tv_usec)
|
||||
#else
|
||||
ftime(&now_ts);
|
||||
if (now_ts.time != last_ts.time || now_ts.millitm != last_ts.millitm)
|
||||
#endif
|
||||
{
|
||||
sametime_count = 0;
|
||||
last_ts = now_ts;
|
||||
}
|
||||
}
|
||||
|
||||
if (trace_queue_size != trace_settings.max_trace_entries)
|
||||
{
|
||||
traceEntry* new_trace_queue = malloc(sizeof(traceEntry) * trace_settings.max_trace_entries);
|
||||
|
||||
if (new_trace_queue == NULL)
|
||||
goto exit;
|
||||
memcpy(new_trace_queue, trace_queue, min(trace_queue_size, trace_settings.max_trace_entries) * sizeof(traceEntry));
|
||||
free(trace_queue);
|
||||
trace_queue = new_trace_queue;
|
||||
trace_queue_size = trace_settings.max_trace_entries;
|
||||
|
||||
if (start_index > trace_settings.max_trace_entries + 1 ||
|
||||
next_index > trace_settings.max_trace_entries + 1)
|
||||
{
|
||||
start_index = -1;
|
||||
next_index = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* add to trace buffer */
|
||||
cur_entry = &trace_queue[next_index];
|
||||
if (next_index == start_index) /* means the buffer is full */
|
||||
{
|
||||
if (++start_index == trace_settings.max_trace_entries)
|
||||
start_index = 0;
|
||||
} else if (start_index == -1)
|
||||
start_index = 0;
|
||||
if (++next_index == trace_settings.max_trace_entries)
|
||||
next_index = 0;
|
||||
exit:
|
||||
return cur_entry;
|
||||
}
|
||||
|
||||
static char* Log_formatTraceEntry(traceEntry* cur_entry)
|
||||
{
|
||||
struct tm *timeinfo;
|
||||
int buf_pos = 31;
|
||||
|
||||
#if defined(GETTIMEOFDAY)
|
||||
timeinfo = localtime((time_t *)&cur_entry->ts.tv_sec);
|
||||
#else
|
||||
timeinfo = localtime(&cur_entry->ts.time);
|
||||
#endif
|
||||
strftime(&msg_buf[7], 80, "%Y%m%d %H%M%S ", timeinfo);
|
||||
#if defined(GETTIMEOFDAY)
|
||||
snprintf(&msg_buf[22], sizeof(msg_buf)-22, ".%.3lu ", cur_entry->ts.tv_usec / 1000L);
|
||||
#else
|
||||
snprintf(&msg_buf[22], sizeof(msg_buf)-22, ".%.3hu ", cur_entry->ts.millitm);
|
||||
#endif
|
||||
buf_pos = 27;
|
||||
|
||||
snprintf(msg_buf, sizeof(msg_buf), "(%.4d)", cur_entry->sametime_count);
|
||||
msg_buf[6] = ' ';
|
||||
|
||||
if (cur_entry->has_rc == 2)
|
||||
strncpy(&msg_buf[buf_pos], cur_entry->name, sizeof(msg_buf)-buf_pos);
|
||||
else
|
||||
{
|
||||
const char *format = Messages_get(cur_entry->number, cur_entry->level);
|
||||
if (cur_entry->has_rc == 1)
|
||||
snprintf(&msg_buf[buf_pos], sizeof(msg_buf)-buf_pos, format, cur_entry->thread_id,
|
||||
cur_entry->depth, "", cur_entry->depth, cur_entry->name, cur_entry->line, cur_entry->rc);
|
||||
else
|
||||
snprintf(&msg_buf[buf_pos], sizeof(msg_buf)-buf_pos, format, cur_entry->thread_id,
|
||||
cur_entry->depth, "", cur_entry->depth, cur_entry->name, cur_entry->line);
|
||||
}
|
||||
return msg_buf;
|
||||
}
|
||||
|
||||
|
||||
static void Log_output(enum LOG_LEVELS log_level, const char *msg)
|
||||
{
|
||||
if (trace_destination)
|
||||
{
|
||||
fprintf(trace_destination, "%s\n", msg);
|
||||
|
||||
if (trace_destination != stdout && ++lines_written >= max_lines_per_file)
|
||||
{
|
||||
|
||||
fclose(trace_destination);
|
||||
_unlink(trace_destination_backup_name); /* remove any old backup trace file */
|
||||
rename(trace_destination_name, trace_destination_backup_name); /* rename recently closed to backup */
|
||||
trace_destination = fopen(trace_destination_name, "w"); /* open new trace file */
|
||||
if (trace_destination == NULL)
|
||||
trace_destination = stdout;
|
||||
lines_written = 0;
|
||||
}
|
||||
else
|
||||
fflush(trace_destination);
|
||||
}
|
||||
|
||||
if (trace_callback)
|
||||
(*trace_callback)(log_level, msg);
|
||||
}
|
||||
|
||||
|
||||
static void Log_posttrace(enum LOG_LEVELS log_level, traceEntry* cur_entry)
|
||||
{
|
||||
if (((trace_output_level == -1) ? log_level >= trace_settings.trace_level : log_level >= trace_output_level))
|
||||
{
|
||||
char* msg = NULL;
|
||||
|
||||
if (trace_destination || trace_callback)
|
||||
msg = &Log_formatTraceEntry(cur_entry)[7];
|
||||
|
||||
Log_output(log_level, msg);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void Log_trace(enum LOG_LEVELS log_level, const char *buf)
|
||||
{
|
||||
traceEntry *cur_entry = NULL;
|
||||
|
||||
if (trace_queue == NULL)
|
||||
return;
|
||||
|
||||
cur_entry = Log_pretrace();
|
||||
|
||||
memcpy(&(cur_entry->ts), &now_ts, sizeof(now_ts));
|
||||
cur_entry->sametime_count = sametime_count;
|
||||
|
||||
cur_entry->has_rc = 2;
|
||||
strncpy(cur_entry->name, buf, sizeof(cur_entry->name));
|
||||
cur_entry->name[MAX_FUNCTION_NAME_LENGTH] = '\0';
|
||||
|
||||
Log_posttrace(log_level, cur_entry);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Log a message. If possible, all messages should be indexed by message number, and
|
||||
* the use of the format string should be minimized or negated altogether. If format is
|
||||
* provided, the message number is only used as a message label.
|
||||
* @param log_level the log level of the message
|
||||
* @param msgno the id of the message to use if the format string is NULL
|
||||
* @param aFormat the printf format string to be used if the message id does not exist
|
||||
* @param ... the printf inserts
|
||||
*/
|
||||
void Log(enum LOG_LEVELS log_level, int msgno, const char *format, ...)
|
||||
{
|
||||
if (log_level >= trace_settings.trace_level)
|
||||
{
|
||||
const char *temp = NULL;
|
||||
va_list args;
|
||||
|
||||
/* we're using a static character buffer, so we need to make sure only one thread uses it at a time */
|
||||
Paho_thread_lock_mutex(log_mutex);
|
||||
if (format == NULL && (temp = Messages_get(msgno, log_level)) != NULL)
|
||||
format = temp;
|
||||
|
||||
va_start(args, format);
|
||||
vsnprintf(msg_buf, sizeof(msg_buf), format, args);
|
||||
|
||||
Log_trace(log_level, msg_buf);
|
||||
va_end(args);
|
||||
Paho_thread_unlock_mutex(log_mutex);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* The reason for this function is to make trace logging as fast as possible so that the
|
||||
* function exit/entry history can be captured by default without unduly impacting
|
||||
* performance. Therefore it must do as little as possible.
|
||||
* @param log_level the log level of the message
|
||||
* @param msgno the id of the message to use if the format string is NULL
|
||||
* @param aFormat the printf format string to be used if the message id does not exist
|
||||
* @param ... the printf inserts
|
||||
*/
|
||||
void Log_stackTrace(enum LOG_LEVELS log_level, int msgno, thread_id_type thread_id, int current_depth, const char* name, int line, int* rc)
|
||||
{
|
||||
traceEntry *cur_entry = NULL;
|
||||
|
||||
if (trace_queue == NULL)
|
||||
return;
|
||||
|
||||
if (log_level < trace_settings.trace_level)
|
||||
return;
|
||||
|
||||
Paho_thread_lock_mutex(log_mutex);
|
||||
cur_entry = Log_pretrace();
|
||||
|
||||
memcpy(&(cur_entry->ts), &now_ts, sizeof(now_ts));
|
||||
cur_entry->sametime_count = sametime_count;
|
||||
cur_entry->number = msgno;
|
||||
cur_entry->thread_id = thread_id;
|
||||
cur_entry->depth = current_depth;
|
||||
strcpy(cur_entry->name, name);
|
||||
cur_entry->level = log_level;
|
||||
cur_entry->line = line;
|
||||
if (rc == NULL)
|
||||
cur_entry->has_rc = 0;
|
||||
else
|
||||
{
|
||||
cur_entry->has_rc = 1;
|
||||
cur_entry->rc = *rc;
|
||||
}
|
||||
|
||||
Log_posttrace(log_level, cur_entry);
|
||||
Paho_thread_unlock_mutex(log_mutex);
|
||||
}
|
||||
|
||||
|
||||
#if 0
|
||||
static FILE* Log_destToFile(const char *dest)
|
||||
{
|
||||
FILE* file = NULL;
|
||||
|
||||
if (strcmp(dest, "stdout") == 0)
|
||||
file = stdout;
|
||||
else if (strcmp(dest, "stderr") == 0)
|
||||
file = stderr;
|
||||
else
|
||||
{
|
||||
if (strstr(dest, "FFDC"))
|
||||
file = fopen(dest, "ab");
|
||||
else
|
||||
file = fopen(dest, "wb");
|
||||
}
|
||||
return file;
|
||||
}
|
||||
|
||||
|
||||
static int Log_compareEntries(const char *entry1, const char *entry2)
|
||||
{
|
||||
int comp = strncmp(&entry1[7], &entry2[7], 19);
|
||||
|
||||
/* if timestamps are equal, use the sequence numbers */
|
||||
if (comp == 0)
|
||||
comp = strncmp(&entry1[1], &entry2[1], 4);
|
||||
|
||||
return comp;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Write the contents of the stored trace to a stream
|
||||
* @param dest string which contains a file name or the special strings stdout or stderr
|
||||
*/
|
||||
int Log_dumpTrace(char* dest)
|
||||
{
|
||||
FILE* file = NULL;
|
||||
ListElement* cur_trace_entry = NULL;
|
||||
const int msgstart = 7;
|
||||
int rc = -1;
|
||||
int trace_queue_index = 0;
|
||||
|
||||
if ((file = Log_destToFile(dest)) == NULL)
|
||||
{
|
||||
Log(LOG_ERROR, 9, NULL, "trace", dest, "trace entries");
|
||||
goto exit;
|
||||
}
|
||||
|
||||
fprintf(file, "=========== Start of trace dump ==========\n");
|
||||
/* Interleave the log and trace entries together appropriately */
|
||||
ListNextElement(trace_buffer, &cur_trace_entry);
|
||||
trace_queue_index = start_index;
|
||||
if (trace_queue_index == -1)
|
||||
trace_queue_index = next_index;
|
||||
else
|
||||
{
|
||||
Log_formatTraceEntry(&trace_queue[trace_queue_index++]);
|
||||
if (trace_queue_index == trace_settings.max_trace_entries)
|
||||
trace_queue_index = 0;
|
||||
}
|
||||
while (cur_trace_entry || trace_queue_index != next_index)
|
||||
{
|
||||
if (cur_trace_entry && trace_queue_index != -1)
|
||||
{ /* compare these timestamps */
|
||||
if (Log_compareEntries((char*)cur_trace_entry->content, msg_buf) > 0)
|
||||
cur_trace_entry = NULL;
|
||||
}
|
||||
|
||||
if (cur_trace_entry)
|
||||
{
|
||||
fprintf(file, "%s\n", &((char*)(cur_trace_entry->content))[msgstart]);
|
||||
ListNextElement(trace_buffer, &cur_trace_entry);
|
||||
}
|
||||
else
|
||||
{
|
||||
fprintf(file, "%s\n", &msg_buf[7]);
|
||||
if (trace_queue_index != next_index)
|
||||
{
|
||||
Log_formatTraceEntry(&trace_queue[trace_queue_index++]);
|
||||
if (trace_queue_index == trace_settings.max_trace_entries)
|
||||
trace_queue_index = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
fprintf(file, "========== End of trace dump ==========\n\n");
|
||||
if (file != stdout && file != stderr && file != NULL)
|
||||
fclose(file);
|
||||
rc = 0;
|
||||
exit:
|
||||
return rc;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
+93
@@ -0,0 +1,93 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2009, 2022 IBM Corp.
|
||||
*
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v2.0
|
||||
* and Eclipse Distribution License v1.0 which accompany this distribution.
|
||||
*
|
||||
* The Eclipse Public License is available at
|
||||
* https://www.eclipse.org/legal/epl-2.0/
|
||||
* and the Eclipse Distribution License is available at
|
||||
* http://www.eclipse.org/org/documents/edl-v10.php.
|
||||
*
|
||||
* Contributors:
|
||||
* Ian Craggs - initial API and implementation and/or initial documentation
|
||||
* Ian Craggs - updates for the async client
|
||||
*******************************************************************************/
|
||||
|
||||
#if !defined(LOG_H)
|
||||
#define LOG_H
|
||||
|
||||
#if defined(_WIN32) || defined(_WIN64)
|
||||
#include <windows.h>
|
||||
#define thread_id_type DWORD
|
||||
#else
|
||||
#include <pthread.h>
|
||||
#define thread_id_type pthread_t
|
||||
#endif
|
||||
|
||||
/*BE
|
||||
map LOG_LEVELS
|
||||
{
|
||||
"TRACE_MAXIMUM" 1
|
||||
"TRACE_MEDIUM" 2
|
||||
"TRACE_MINIMUM" 3
|
||||
"TRACE_PROTOCOL" 4
|
||||
|
||||
"ERROR" 5
|
||||
"SEVERE" 6
|
||||
"FATAL" 7
|
||||
}
|
||||
BE*/
|
||||
|
||||
enum LOG_LEVELS {
|
||||
INVALID_LEVEL = -1,
|
||||
TRACE_MAXIMUM = 1,
|
||||
TRACE_MEDIUM,
|
||||
TRACE_MINIMUM,
|
||||
TRACE_PROTOCOL,
|
||||
LOG_ERROR,
|
||||
LOG_SEVERE,
|
||||
LOG_FATAL,
|
||||
};
|
||||
|
||||
|
||||
/*BE
|
||||
def trace_settings_type
|
||||
{
|
||||
n32 map LOG_LEVELS "trace_level"
|
||||
n32 dec "max_trace_entries"
|
||||
n32 dec "trace_output_level"
|
||||
}
|
||||
BE*/
|
||||
typedef struct
|
||||
{
|
||||
enum LOG_LEVELS trace_level; /**< trace level */
|
||||
int max_trace_entries; /**< max no of entries in the trace buffer */
|
||||
enum LOG_LEVELS trace_output_level; /**< trace level to output to destination */
|
||||
} trace_settings_type;
|
||||
|
||||
extern trace_settings_type trace_settings;
|
||||
|
||||
#define LOG_PROTOCOL TRACE_PROTOCOL
|
||||
#define TRACE_MAX TRACE_MAXIMUM
|
||||
#define TRACE_MIN TRACE_MINIMUM
|
||||
#define TRACE_MED TRACE_MEDIUM
|
||||
|
||||
typedef struct
|
||||
{
|
||||
const char* name;
|
||||
const char* value;
|
||||
} Log_nameValue;
|
||||
|
||||
int Log_initialize(Log_nameValue*);
|
||||
void Log_terminate(void);
|
||||
|
||||
void Log(enum LOG_LEVELS, int, const char *, ...);
|
||||
void Log_stackTrace(enum LOG_LEVELS, int, thread_id_type, int, const char*, int, int*);
|
||||
|
||||
typedef void Log_traceCallback(enum LOG_LEVELS level, const char *message);
|
||||
void Log_setTraceCallback(Log_traceCallback* callback);
|
||||
void Log_setTraceLevel(enum LOG_LEVELS level);
|
||||
|
||||
#endif
|
||||
+1861
File diff suppressed because it is too large
Load Diff
+2383
File diff suppressed because it is too large
Load Diff
+3184
File diff suppressed because it is too large
Load Diff
+187
@@ -0,0 +1,187 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2009, 2022 IBM Corp. and others
|
||||
*
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v2.0
|
||||
* and Eclipse Distribution License v1.0 which accompany this distribution.
|
||||
*
|
||||
* The Eclipse Public License is available at
|
||||
* https://www.eclipse.org/legal/epl-2.0/
|
||||
* and the Eclipse Distribution License is available at
|
||||
* http://www.eclipse.org/org/documents/edl-v10.php.
|
||||
*
|
||||
* Contributors:
|
||||
* Ian Craggs - initial implementation and documentation
|
||||
*******************************************************************************/
|
||||
|
||||
#if !defined(MQTTASYNCUTILS_H_)
|
||||
#define MQTTASYNCUTILS_H_
|
||||
|
||||
#include "MQTTPacket.h"
|
||||
#include "Thread.h"
|
||||
|
||||
#define URI_TCP "tcp://"
|
||||
#define URI_MQTT "mqtt://"
|
||||
#define URI_WS "ws://"
|
||||
#define URI_WSS "wss://"
|
||||
|
||||
enum MQTTAsync_threadStates
|
||||
{
|
||||
STOPPED, STARTING, RUNNING, STOPPING
|
||||
};
|
||||
|
||||
typedef struct
|
||||
{
|
||||
MQTTAsync_message* msg;
|
||||
char* topicName;
|
||||
int topicLen;
|
||||
unsigned int seqno; /* only used on restore */
|
||||
} qEntry;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
int type;
|
||||
MQTTAsync_onSuccess* onSuccess;
|
||||
MQTTAsync_onFailure* onFailure;
|
||||
MQTTAsync_onSuccess5* onSuccess5;
|
||||
MQTTAsync_onFailure5* onFailure5;
|
||||
MQTTAsync_token token;
|
||||
void* context;
|
||||
START_TIME_TYPE start_time;
|
||||
MQTTProperties properties;
|
||||
union
|
||||
{
|
||||
struct
|
||||
{
|
||||
int count;
|
||||
char** topics;
|
||||
int* qoss;
|
||||
MQTTSubscribe_options opts;
|
||||
MQTTSubscribe_options* optlist;
|
||||
} sub;
|
||||
struct
|
||||
{
|
||||
int count;
|
||||
char** topics;
|
||||
} unsub;
|
||||
struct
|
||||
{
|
||||
char* destinationName;
|
||||
int payloadlen;
|
||||
void* payload;
|
||||
int qos;
|
||||
int retained;
|
||||
} pub;
|
||||
struct
|
||||
{
|
||||
int internal;
|
||||
int timeout;
|
||||
enum MQTTReasonCodes reasonCode;
|
||||
} dis;
|
||||
struct
|
||||
{
|
||||
int currentURI;
|
||||
int MQTTVersion; /**< current MQTT version being used to connect */
|
||||
} conn;
|
||||
} details;
|
||||
} MQTTAsync_command;
|
||||
|
||||
typedef struct MQTTAsync_struct
|
||||
{
|
||||
char* serverURI;
|
||||
int ssl;
|
||||
int websocket;
|
||||
Clients* c;
|
||||
|
||||
/* "Global", to the client, callback definitions */
|
||||
MQTTAsync_connectionLost* cl;
|
||||
MQTTAsync_messageArrived* ma;
|
||||
MQTTAsync_deliveryComplete* dc;
|
||||
void* clContext; /* the context to be associated with the conn lost callback*/
|
||||
void* maContext; /* the context to be associated with the msg arrived callback*/
|
||||
void* dcContext; /* the context to be associated with the deliv complete callback*/
|
||||
|
||||
MQTTAsync_connected* connected;
|
||||
void* connected_context; /* the context to be associated with the connected callback*/
|
||||
|
||||
MQTTAsync_disconnected* disconnected;
|
||||
void* disconnected_context; /* the context to be associated with the disconnected callback*/
|
||||
|
||||
MQTTAsync_updateConnectOptions* updateConnectOptions;
|
||||
void* updateConnectOptions_context;
|
||||
|
||||
/* Each time connect is called, we store the options that were used. These are reused in
|
||||
any call to reconnect, or an automatic reconnect attempt */
|
||||
MQTTAsync_command connect; /* Connect operation properties */
|
||||
MQTTAsync_command disconnect; /* Disconnect operation properties */
|
||||
MQTTAsync_command* pending_write; /* Is there a socket write pending? */
|
||||
|
||||
List* responses;
|
||||
unsigned int command_seqno;
|
||||
|
||||
MQTTPacket* pack;
|
||||
|
||||
/* added for offline buffering */
|
||||
MQTTAsync_createOptions* createOptions;
|
||||
int shouldBeConnected;
|
||||
int noBufferedMessages; /* the current number of buffered (publish) messages for this client */
|
||||
|
||||
/* added for automatic reconnect */
|
||||
int automaticReconnect;
|
||||
int minRetryInterval;
|
||||
int maxRetryInterval;
|
||||
int serverURIcount;
|
||||
char** serverURIs;
|
||||
int connectTimeout;
|
||||
|
||||
int currentInterval;
|
||||
int currentIntervalBase;
|
||||
START_TIME_TYPE lastConnectionFailedTime;
|
||||
int retrying;
|
||||
int reconnectNow;
|
||||
|
||||
/* MQTT V5 properties */
|
||||
MQTTProperties* connectProps;
|
||||
MQTTProperties* willProps;
|
||||
|
||||
} MQTTAsyncs;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
MQTTAsync_command command;
|
||||
MQTTAsyncs* client;
|
||||
unsigned int seqno; /* only used on restore */
|
||||
int not_restored;
|
||||
char* key; /* if not_restored, this holds the key */
|
||||
} MQTTAsync_queuedCommand;
|
||||
|
||||
void MQTTAsync_lock_mutex(mutex_type amutex);
|
||||
void MQTTAsync_unlock_mutex(mutex_type amutex);
|
||||
void MQTTAsync_terminate(void);
|
||||
#if !defined(NO_PERSISTENCE)
|
||||
int MQTTAsync_restoreCommands(MQTTAsyncs* client);
|
||||
#endif
|
||||
int MQTTAsync_addCommand(MQTTAsync_queuedCommand* command, int command_size);
|
||||
void MQTTAsync_emptyMessageQueue(Clients* client);
|
||||
void MQTTAsync_freeResponses(MQTTAsyncs* m);
|
||||
void MQTTAsync_freeCommands(MQTTAsyncs* m);
|
||||
int MQTTAsync_unpersistCommandsAndMessages(Clients* c);
|
||||
void MQTTAsync_closeSession(Clients* client, enum MQTTReasonCodes reasonCode, MQTTProperties* props);
|
||||
int MQTTAsync_disconnect1(MQTTAsync handle, const MQTTAsync_disconnectOptions* options, int internal);
|
||||
int MQTTAsync_assignMsgId(MQTTAsyncs* m);
|
||||
int MQTTAsync_getNoBufferedMessages(MQTTAsyncs* m);
|
||||
void MQTTAsync_writeContinue(SOCKET socket);
|
||||
void MQTTAsync_writeComplete(SOCKET socket, int rc);
|
||||
void setRetryLoopInterval(int keepalive);
|
||||
void MQTTAsync_NULLPublishResponses(MQTTAsyncs* m);
|
||||
void MQTTAsync_NULLPublishCommands(MQTTAsyncs* m);
|
||||
|
||||
#if defined(_WIN32) || defined(_WIN64)
|
||||
#else
|
||||
#define WINAPI
|
||||
#endif
|
||||
|
||||
thread_return_type WINAPI MQTTAsync_sendThread(void* n);
|
||||
thread_return_type WINAPI MQTTAsync_receiveThread(void* n);
|
||||
|
||||
#endif /* MQTTASYNCUTILS_H_ */
|
||||
+3118
File diff suppressed because it is too large
Load Diff
+1980
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,277 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2009, 2020 IBM Corp.
|
||||
*
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v2.0
|
||||
* and Eclipse Distribution License v1.0 which accompany this distribution.
|
||||
*
|
||||
* The Eclipse Public License is available at
|
||||
* https://www.eclipse.org/legal/epl-2.0/
|
||||
* and the Eclipse Distribution License is available at
|
||||
* http://www.eclipse.org/org/documents/edl-v10.php.
|
||||
*
|
||||
* Contributors:
|
||||
* Ian Craggs - initial API and implementation and/or initial documentation
|
||||
*******************************************************************************/
|
||||
|
||||
/**
|
||||
* @file
|
||||
* \brief This structure represents a persistent data store, used to store
|
||||
* outbound and inbound messages, in order to achieve reliable messaging.
|
||||
*
|
||||
* The MQTT Client persists QoS1 and QoS2 messages in order to meet the
|
||||
* assurances of delivery associated with these @ref qos levels. The messages
|
||||
* are saved in persistent storage
|
||||
* The type and context of the persistence implementation are specified when
|
||||
* the MQTT client is created (see MQTTClient_create()). The default
|
||||
* persistence type (::MQTTCLIENT_PERSISTENCE_DEFAULT) uses a file system-based
|
||||
* persistence mechanism. The <i>persistence_context</i> argument passed to
|
||||
* MQTTClient_create() when using the default peristence is a string
|
||||
* representing the location of the persistence directory. If the context
|
||||
* argument is NULL, the working directory will be used.
|
||||
*
|
||||
* To use memory-based persistence, an application passes
|
||||
* ::MQTTCLIENT_PERSISTENCE_NONE as the <i>persistence_type</i> to
|
||||
* MQTTClient_create(). This can lead to message loss in certain situations,
|
||||
* but can be appropriate in some cases (see @ref qos).
|
||||
*
|
||||
* Client applications can provide their own persistence mechanism by passing
|
||||
* ::MQTTCLIENT_PERSISTENCE_USER as the <i>persistence_type</i>. To implement a
|
||||
* custom persistence mechanism, the application must pass an initialized
|
||||
* ::MQTTClient_persistence structure as the <i>persistence_context</i>
|
||||
* argument to MQTTClient_create().
|
||||
*
|
||||
* If the functions defined return an ::MQTTCLIENT_PERSISTENCE_ERROR then the
|
||||
* state of the persisted data should remain as it was prior to the function
|
||||
* being called. For example, if Persistence_put() returns
|
||||
* ::MQTTCLIENT_PERSISTENCE_ERROR, then it is assumed tha tthe persistent store
|
||||
* does not contain the data that was passed to the function. Similarly, if
|
||||
* Persistence_remove() returns ::MQTTCLIENT_PERSISTENCE_ERROR then it is
|
||||
* assumed that the data to be removed is still held in the persistent store.
|
||||
*
|
||||
* It is up to the persistence implementation to log any error information that
|
||||
* may be required to diagnose a persistence mechanism failure.
|
||||
*/
|
||||
|
||||
/*
|
||||
/// @cond EXCLUDE
|
||||
*/
|
||||
#if !defined(MQTTCLIENTPERSISTENCE_H)
|
||||
#define MQTTCLIENTPERSISTENCE_H
|
||||
/*
|
||||
/// @endcond
|
||||
*/
|
||||
|
||||
/**
|
||||
* This <i>persistence_type</i> value specifies the default file system-based
|
||||
* persistence mechanism (see MQTTClient_create()).
|
||||
*/
|
||||
#define MQTTCLIENT_PERSISTENCE_DEFAULT 0
|
||||
/**
|
||||
* This <i>persistence_type</i> value specifies a memory-based
|
||||
* persistence mechanism (see MQTTClient_create()).
|
||||
*/
|
||||
#define MQTTCLIENT_PERSISTENCE_NONE 1
|
||||
/**
|
||||
* This <i>persistence_type</i> value specifies an application-specific
|
||||
* persistence mechanism (see MQTTClient_create()).
|
||||
*/
|
||||
#define MQTTCLIENT_PERSISTENCE_USER 2
|
||||
|
||||
/**
|
||||
* Application-specific persistence functions must return this error code if
|
||||
* there is a problem executing the function.
|
||||
*/
|
||||
#define MQTTCLIENT_PERSISTENCE_ERROR -2
|
||||
|
||||
/**
|
||||
* @brief Initialize the persistent store.
|
||||
*
|
||||
* Either open the existing persistent store for this client ID or create a new
|
||||
* one if one doesn't exist. If the persistent store is already open, return
|
||||
* without taking any action.
|
||||
*
|
||||
* An application can use the same client identifier to connect to many
|
||||
* different servers. The <i>clientid</i> in conjunction with the
|
||||
* <i>serverURI</i> uniquely identifies the persistence store required.
|
||||
*
|
||||
* @param handle The address of a pointer to a handle for this persistence
|
||||
* implementation. This function must set handle to a valid reference to the
|
||||
* persistence following a successful return.
|
||||
* The handle pointer is passed as an argument to all the other
|
||||
* persistence functions. It may include the context parameter and/or any other
|
||||
* data for use by the persistence functions.
|
||||
* @param clientID The client identifier for which the persistent store should
|
||||
* be opened.
|
||||
* @param serverURI The connection string specified when the MQTT client was
|
||||
* created (see MQTTClient_create()).
|
||||
* @param context A pointer to any data required to initialize the persistent
|
||||
* store (see ::MQTTClient_persistence).
|
||||
* @return Return 0 if the function completes successfully, otherwise return
|
||||
* ::MQTTCLIENT_PERSISTENCE_ERROR.
|
||||
*/
|
||||
typedef int (*Persistence_open)(void** handle, const char* clientID, const char* serverURI, void* context);
|
||||
|
||||
/**
|
||||
* @brief Close the persistent store referred to by the handle.
|
||||
*
|
||||
* @param handle The handle pointer from a successful call to
|
||||
* Persistence_open().
|
||||
* @return Return 0 if the function completes successfully, otherwise return
|
||||
* ::MQTTCLIENT_PERSISTENCE_ERROR.
|
||||
*/
|
||||
typedef int (*Persistence_close)(void* handle);
|
||||
|
||||
/**
|
||||
* @brief Put the specified data into the persistent store.
|
||||
*
|
||||
* @param handle The handle pointer from a successful call to
|
||||
* Persistence_open().
|
||||
* @param key A string used as the key for the data to be put in the store. The
|
||||
* key is later used to retrieve data from the store with Persistence_get().
|
||||
* @param bufcount The number of buffers to write to the persistence store.
|
||||
* @param buffers An array of pointers to the data buffers associated with
|
||||
* this <i>key</i>.
|
||||
* @param buflens An array of lengths of the data buffers. <i>buflen[n]</i>
|
||||
* gives the length of <i>buffer[n]</i>.
|
||||
* @return Return 0 if the function completes successfully, otherwise return
|
||||
* ::MQTTCLIENT_PERSISTENCE_ERROR.
|
||||
*/
|
||||
typedef int (*Persistence_put)(void* handle, char* key, int bufcount, char* buffers[], int buflens[]);
|
||||
|
||||
/**
|
||||
* @brief Retrieve the specified data from the persistent store.
|
||||
*
|
||||
* @param handle The handle pointer from a successful call to
|
||||
* Persistence_open().
|
||||
* @param key A string that is the key for the data to be retrieved. This is
|
||||
* the same key used to save the data to the store with Persistence_put().
|
||||
* @param buffer The address of a pointer to a buffer. This function sets the
|
||||
* pointer to point at the retrieved data, if successful.
|
||||
* @param buflen The address of an int that is set to the length of
|
||||
* <i>buffer</i> by this function if successful.
|
||||
* @return Return 0 if the function completes successfully, otherwise return
|
||||
* ::MQTTCLIENT_PERSISTENCE_ERROR.
|
||||
*/
|
||||
typedef int (*Persistence_get)(void* handle, char* key, char** buffer, int* buflen);
|
||||
|
||||
/**
|
||||
* @brief Remove the data for the specified key from the store.
|
||||
*
|
||||
* @param handle The handle pointer from a successful call to
|
||||
* Persistence_open().
|
||||
* @param key A string that is the key for the data to be removed from the
|
||||
* store. This is the same key used to save the data to the store with
|
||||
* Persistence_put().
|
||||
* @return Return 0 if the function completes successfully, otherwise return
|
||||
* ::MQTTCLIENT_PERSISTENCE_ERROR.
|
||||
*/
|
||||
typedef int (*Persistence_remove)(void* handle, char* key);
|
||||
|
||||
/**
|
||||
* @brief Returns the keys in this persistent data store.
|
||||
*
|
||||
* @param handle The handle pointer from a successful call to
|
||||
* Persistence_open().
|
||||
* @param keys The address of a pointer to pointers to strings. Assuming
|
||||
* successful execution, this function allocates memory to hold the returned
|
||||
* keys (strings used to store the data with Persistence_put()). It also
|
||||
* allocates memory to hold an array of pointers to these strings. <i>keys</i>
|
||||
* is set to point to the array of pointers to strings.
|
||||
* @param nkeys A pointer to the number of keys in this persistent data store.
|
||||
* This function sets the number of keys, if successful.
|
||||
* @return Return 0 if the function completes successfully, otherwise return
|
||||
* ::MQTTCLIENT_PERSISTENCE_ERROR.
|
||||
*/
|
||||
typedef int (*Persistence_keys)(void* handle, char*** keys, int* nkeys);
|
||||
|
||||
/**
|
||||
* @brief Clears the persistence store, so that it no longer contains any
|
||||
* persisted data.
|
||||
*
|
||||
* @param handle The handle pointer from a successful call to
|
||||
* Persistence_open().
|
||||
* @return Return 0 if the function completes successfully, otherwise return
|
||||
* ::MQTTCLIENT_PERSISTENCE_ERROR.
|
||||
*/
|
||||
typedef int (*Persistence_clear)(void* handle);
|
||||
|
||||
/**
|
||||
* @brief Returns whether any data has been persisted using the specified key.
|
||||
*
|
||||
* @param handle The handle pointer from a successful call to
|
||||
* Persistence_open().
|
||||
* @param key The string to be tested for existence in the store.
|
||||
* @return Return 0 if the key was found in the store, otherwise return
|
||||
* ::MQTTCLIENT_PERSISTENCE_ERROR.
|
||||
*/
|
||||
typedef int (*Persistence_containskey)(void* handle, char* key);
|
||||
|
||||
/**
|
||||
* @brief A structure containing the function pointers to a persistence
|
||||
* implementation and the context or state that will be shared across all
|
||||
* the persistence functions.
|
||||
*/
|
||||
typedef struct {
|
||||
/**
|
||||
* A pointer to any data required to initialize the persistent store.
|
||||
*/
|
||||
void* context;
|
||||
/**
|
||||
* A function pointer to an implementation of Persistence_open().
|
||||
*/
|
||||
Persistence_open popen;
|
||||
/**
|
||||
* A function pointer to an implementation of Persistence_close().
|
||||
*/
|
||||
Persistence_close pclose;
|
||||
/**
|
||||
* A function pointer to an implementation of Persistence_put().
|
||||
*/
|
||||
Persistence_put pput;
|
||||
/**
|
||||
* A function pointer to an implementation of Persistence_get().
|
||||
*/
|
||||
Persistence_get pget;
|
||||
/**
|
||||
* A function pointer to an implementation of Persistence_remove().
|
||||
*/
|
||||
Persistence_remove premove;
|
||||
/**
|
||||
* A function pointer to an implementation of Persistence_keys().
|
||||
*/
|
||||
Persistence_keys pkeys;
|
||||
/**
|
||||
* A function pointer to an implementation of Persistence_clear().
|
||||
*/
|
||||
Persistence_clear pclear;
|
||||
/**
|
||||
* A function pointer to an implementation of Persistence_containskey().
|
||||
*/
|
||||
Persistence_containskey pcontainskey;
|
||||
} MQTTClient_persistence;
|
||||
|
||||
|
||||
/**
|
||||
* A callback which is invoked just before a write to persistence. This can be
|
||||
* used to transform the data, for instance to encrypt it.
|
||||
* @param context The context as set in ::MQTTAsync_setBeforePersistenceWrite
|
||||
* @param bufcount The number of buffers to write to the persistence store.
|
||||
* @param buffers An array of pointers to the data buffers.
|
||||
* @param buflens An array of lengths of the data buffers.
|
||||
* @return Return 0 if the function completes successfully, otherwise non 0.
|
||||
*/
|
||||
typedef int MQTTPersistence_beforeWrite(void* context, int bufcount, char* buffers[], int buflens[]);
|
||||
|
||||
|
||||
/**
|
||||
* A callback which is invoked just after a read from persistence. This can be
|
||||
* used to transform the data, for instance to decrypt it.
|
||||
* @param context The context as set in ::MQTTAsync_setAfterPersistenceRead
|
||||
* @param buffer The address of a pointer to a buffer.
|
||||
* @param buflen The address of an int that is the length of the buffer.
|
||||
* @return Return 0 if the function completes successfully, otherwise non 0.
|
||||
*/
|
||||
typedef int MQTTPersistence_afterRead(void* context, char** buffer, int* buflen);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,36 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2020, 2020 Andreas Walter
|
||||
*
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v2.0
|
||||
* and Eclipse Distribution License v1.0 which accompany this distribution.
|
||||
*
|
||||
* The Eclipse Public License is available at
|
||||
* https://www.eclipse.org/legal/epl-2.0/
|
||||
* and the Eclipse Distribution License is available at
|
||||
* http://www.eclipse.org/org/documents/edl-v10.php.
|
||||
*
|
||||
* Contributors:
|
||||
* Andreas Walter - initially moved export declarations into separate fle
|
||||
*******************************************************************************/
|
||||
|
||||
#if !defined(EXPORTDECLARATIONS_H)
|
||||
#define EXPORTDECLARATIONS_H
|
||||
|
||||
#if defined(_WIN32) || defined(_WIN64)
|
||||
# if defined(PAHO_MQTT_EXPORTS)
|
||||
# define LIBMQTT_API __declspec(dllexport)
|
||||
# elif defined(PAHO_MQTT_IMPORTS)
|
||||
# define LIBMQTT_API __declspec(dllimport)
|
||||
# else
|
||||
# define LIBMQTT_API
|
||||
# endif
|
||||
#else
|
||||
# if defined(PAHO_MQTT_EXPORTS)
|
||||
# define LIBMQTT_API __attribute__ ((visibility ("default")))
|
||||
# else
|
||||
# define LIBMQTT_API extern
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#endif
|
||||
+1077
File diff suppressed because it is too large
Load Diff
+271
@@ -0,0 +1,271 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2009, 2023 IBM Corp.
|
||||
*
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v2.0
|
||||
* and Eclipse Distribution License v1.0 which accompany this distribution.
|
||||
*
|
||||
* The Eclipse Public License is available at
|
||||
* https://www.eclipse.org/legal/epl-2.0/
|
||||
* and the Eclipse Distribution License is available at
|
||||
* http://www.eclipse.org/org/documents/edl-v10.php.
|
||||
*
|
||||
* Contributors:
|
||||
* Ian Craggs - initial API and implementation and/or initial documentation
|
||||
* Ian Craggs, Allan Stockdill-Mander - SSL updates
|
||||
* Ian Craggs - MQTT 3.1.1 support
|
||||
* Ian Craggs - big endian Linux reversed definition
|
||||
* Ian Craggs - MQTT 5.0 support
|
||||
*******************************************************************************/
|
||||
|
||||
#if !defined(MQTTPACKET_H)
|
||||
#define MQTTPACKET_H
|
||||
|
||||
#include "Socket.h"
|
||||
#if defined(OPENSSL)
|
||||
#include "SSLSocket.h"
|
||||
#endif
|
||||
#include "LinkedList.h"
|
||||
#include "Clients.h"
|
||||
|
||||
typedef unsigned int bool;
|
||||
typedef void* (*pf)(int, unsigned char, char*, size_t);
|
||||
|
||||
#include "MQTTProperties.h"
|
||||
#include "MQTTReasonCodes.h"
|
||||
|
||||
enum errors
|
||||
{
|
||||
MQTTPACKET_BAD = -4,
|
||||
MQTTPACKET_BUFFER_TOO_SHORT = -2,
|
||||
MQTTPACKET_READ_ERROR = -1,
|
||||
MQTTPACKET_READ_COMPLETE
|
||||
};
|
||||
|
||||
|
||||
enum msgTypes
|
||||
{
|
||||
CONNECT = 1, CONNACK, PUBLISH, PUBACK, PUBREC, PUBREL,
|
||||
PUBCOMP, SUBSCRIBE, SUBACK, UNSUBSCRIBE, UNSUBACK,
|
||||
PINGREQ, PINGRESP, DISCONNECT, AUTH
|
||||
};
|
||||
|
||||
#if defined(__linux__)
|
||||
#include <endian.h>
|
||||
#if __BYTE_ORDER == __BIG_ENDIAN
|
||||
#define REVERSED 1
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Bitfields for the MQTT header byte.
|
||||
*/
|
||||
typedef union
|
||||
{
|
||||
/*unsigned*/ char byte; /**< the whole byte */
|
||||
#if defined(REVERSED)
|
||||
struct
|
||||
{
|
||||
unsigned int type : 4; /**< message type nibble */
|
||||
bool dup : 1; /**< DUP flag bit */
|
||||
unsigned int qos : 2; /**< QoS value, 0, 1 or 2 */
|
||||
bool retain : 1; /**< retained flag bit */
|
||||
} bits;
|
||||
#else
|
||||
struct
|
||||
{
|
||||
bool retain : 1; /**< retained flag bit */
|
||||
unsigned int qos : 2; /**< QoS value, 0, 1 or 2 */
|
||||
bool dup : 1; /**< DUP flag bit */
|
||||
unsigned int type : 4; /**< message type nibble */
|
||||
} bits;
|
||||
#endif
|
||||
} Header;
|
||||
|
||||
|
||||
/**
|
||||
* Data for a connect packet.
|
||||
*/
|
||||
typedef struct
|
||||
{
|
||||
Header header; /**< MQTT header byte */
|
||||
union
|
||||
{
|
||||
unsigned char all; /**< all connect flags */
|
||||
#if defined(REVERSED)
|
||||
struct
|
||||
{
|
||||
bool username : 1; /**< 3.1 user name */
|
||||
bool password : 1; /**< 3.1 password */
|
||||
bool willRetain : 1; /**< will retain setting */
|
||||
unsigned int willQoS : 2; /**< will QoS value */
|
||||
bool will : 1; /**< will flag */
|
||||
bool cleanstart : 1; /**< cleansession flag */
|
||||
int : 1; /**< unused */
|
||||
} bits;
|
||||
#else
|
||||
struct
|
||||
{
|
||||
int : 1; /**< unused */
|
||||
bool cleanstart : 1; /**< cleansession flag */
|
||||
bool will : 1; /**< will flag */
|
||||
unsigned int willQoS : 2; /**< will QoS value */
|
||||
bool willRetain : 1; /**< will retain setting */
|
||||
bool password : 1; /**< 3.1 password */
|
||||
bool username : 1; /**< 3.1 user name */
|
||||
} bits;
|
||||
#endif
|
||||
} flags; /**< connect flags byte */
|
||||
|
||||
char *Protocol, /**< MQTT protocol name */
|
||||
*clientID, /**< string client id */
|
||||
*willTopic, /**< will topic */
|
||||
*willMsg; /**< will payload */
|
||||
|
||||
int keepAliveTimer; /**< keepalive timeout value in seconds */
|
||||
unsigned char version; /**< MQTT version number */
|
||||
} Connect;
|
||||
|
||||
|
||||
/**
|
||||
* Data for a connack packet.
|
||||
*/
|
||||
typedef struct
|
||||
{
|
||||
Header header; /**< MQTT header byte */
|
||||
union
|
||||
{
|
||||
unsigned char all; /**< all connack flags */
|
||||
#if defined(REVERSED)
|
||||
struct
|
||||
{
|
||||
unsigned int reserved : 7; /**< message type nibble */
|
||||
bool sessionPresent : 1; /**< was a session found on the server? */
|
||||
} bits;
|
||||
#else
|
||||
struct
|
||||
{
|
||||
bool sessionPresent : 1; /**< was a session found on the server? */
|
||||
unsigned int reserved : 7; /**< message type nibble */
|
||||
} bits;
|
||||
#endif
|
||||
} flags; /**< connack flags byte */
|
||||
unsigned char rc; /**< connack reason code */
|
||||
unsigned int MQTTVersion; /**< the version of MQTT */
|
||||
MQTTProperties properties; /**< MQTT 5.0 properties. Not used for MQTT < 5.0 */
|
||||
} Connack;
|
||||
|
||||
|
||||
/**
|
||||
* Data for a packet with header only.
|
||||
*/
|
||||
typedef struct
|
||||
{
|
||||
Header header; /**< MQTT header byte */
|
||||
} MQTTPacket;
|
||||
|
||||
|
||||
/**
|
||||
* Data for a suback packet.
|
||||
*/
|
||||
typedef struct
|
||||
{
|
||||
Header header; /**< MQTT header byte */
|
||||
int msgId; /**< MQTT message id */
|
||||
int MQTTVersion; /**< the version of MQTT */
|
||||
MQTTProperties properties; /**< MQTT 5.0 properties. Not used for MQTT < 5.0 */
|
||||
List* qoss; /**< list of granted QoSs (MQTT 3/4) / reason codes (MQTT 5) */
|
||||
} Suback;
|
||||
|
||||
|
||||
/**
|
||||
* Data for an MQTT V5 unsuback packet.
|
||||
*/
|
||||
typedef struct
|
||||
{
|
||||
Header header; /**< MQTT header byte */
|
||||
int msgId; /**< MQTT message id */
|
||||
int MQTTVersion; /**< the version of MQTT */
|
||||
MQTTProperties properties; /**< MQTT 5.0 properties. Not used for MQTT < 5.0 */
|
||||
List* reasonCodes; /**< list of reason codes */
|
||||
} Unsuback;
|
||||
|
||||
|
||||
/**
|
||||
* Data for a publish packet.
|
||||
*/
|
||||
typedef struct
|
||||
{
|
||||
Header header; /**< MQTT header byte */
|
||||
char* topic; /**< topic string */
|
||||
int topiclen;
|
||||
int msgId; /**< MQTT message id */
|
||||
char* payload; /**< binary payload, length delimited */
|
||||
int payloadlen; /**< payload length */
|
||||
int MQTTVersion; /**< the version of MQTT */
|
||||
MQTTProperties properties; /**< MQTT 5.0 properties. Not used for MQTT < 5.0 */
|
||||
uint8_t mask[4]; /**< the websockets mask the payload is masked with, if any */
|
||||
} Publish;
|
||||
|
||||
|
||||
/**
|
||||
* Data for one of the ack packets.
|
||||
*/
|
||||
typedef struct
|
||||
{
|
||||
Header header; /**< MQTT header byte */
|
||||
int msgId; /**< MQTT message id */
|
||||
unsigned char rc; /**< MQTT 5 reason code */
|
||||
int MQTTVersion; /**< the version of MQTT */
|
||||
MQTTProperties properties; /**< MQTT 5.0 properties. Not used for MQTT < 5.0 */
|
||||
} Ack;
|
||||
|
||||
typedef Ack Puback;
|
||||
typedef Ack Pubrec;
|
||||
typedef Ack Pubrel;
|
||||
typedef Ack Pubcomp;
|
||||
|
||||
int MQTTPacket_encode(char* buf, size_t length);
|
||||
int MQTTPacket_decode(networkHandles* net, size_t* value);
|
||||
int readInt(char** pptr);
|
||||
char* readUTF(char** pptr, char* enddata);
|
||||
unsigned char readChar(char** pptr);
|
||||
void writeChar(char** pptr, char c);
|
||||
void writeInt(char** pptr, int anInt);
|
||||
void writeUTF(char** pptr, const char* string);
|
||||
void writeData(char** pptr, const void* data, int datalen);
|
||||
|
||||
const char* MQTTPacket_name(int ptype);
|
||||
|
||||
void* MQTTPacket_Factory(int MQTTVersion, networkHandles* net, int* error);
|
||||
int MQTTPacket_send(networkHandles* net, Header header, char* buffer, size_t buflen, int free, int MQTTVersion);
|
||||
int MQTTPacket_sends(networkHandles* net, Header header, PacketBuffers* buffers, int MQTTVersion);
|
||||
|
||||
void* MQTTPacket_header_only(int MQTTVersion, unsigned char aHeader, char* data, size_t datalen);
|
||||
int MQTTPacket_send_disconnect(Clients* client, enum MQTTReasonCodes reason, MQTTProperties* props);
|
||||
|
||||
void* MQTTPacket_publish(int MQTTVersion, unsigned char aHeader, char* data, size_t datalen);
|
||||
void MQTTPacket_freePublish(Publish* pack);
|
||||
int MQTTPacket_send_publish(Publish* pack, int dup, int qos, int retained, networkHandles* net, const char* clientID);
|
||||
int MQTTPacket_send_puback(int MQTTVersion, int msgid, networkHandles* net, const char* clientID);
|
||||
void* MQTTPacket_ack(int MQTTVersion, unsigned char aHeader, char* data, size_t datalen);
|
||||
|
||||
void MQTTPacket_freeAck(Ack* pack);
|
||||
void MQTTPacket_freeSuback(Suback* pack);
|
||||
void MQTTPacket_freeUnsuback(Unsuback* pack);
|
||||
int MQTTPacket_send_pubrec(int MQTTVersion, int msgid, networkHandles* net, const char* clientID);
|
||||
int MQTTPacket_send_pubrel(int MQTTVersion, int msgid, int dup, networkHandles* net, const char* clientID);
|
||||
int MQTTPacket_send_pubcomp(int MQTTVersion, int msgid, networkHandles* net, const char* clientID);
|
||||
|
||||
void MQTTPacket_free_packet(MQTTPacket* pack);
|
||||
|
||||
void writeInt4(char** pptr, unsigned int anInt);
|
||||
unsigned int readInt4(char** pptr);
|
||||
void writeMQTTLenString(char** pptr, MQTTLenString lenstring);
|
||||
int MQTTLenStringRead(MQTTLenString* lenstring, char** pptr, char* enddata);
|
||||
int MQTTPacket_VBIlen(int rem_len);
|
||||
int MQTTPacket_decodeBuf(char* buf, unsigned int* value);
|
||||
|
||||
#include "MQTTPacketOut.h"
|
||||
|
||||
#endif /* MQTTPACKET_H */
|
||||
+473
@@ -0,0 +1,473 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2009, 2021 IBM Corp. and Ian Craggs
|
||||
*
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v2.0
|
||||
* and Eclipse Distribution License v1.0 which accompany this distribution.
|
||||
*
|
||||
* The Eclipse Public License is available at
|
||||
* https://www.eclipse.org/legal/epl-2.0/
|
||||
* and the Eclipse Distribution License is available at
|
||||
* http://www.eclipse.org/org/documents/edl-v10.php.
|
||||
*
|
||||
* Contributors:
|
||||
* Ian Craggs - initial API and implementation and/or initial documentation
|
||||
* Ian Craggs, Allan Stockdill-Mander - SSL updates
|
||||
* Ian Craggs - MQTT 3.1.1 support
|
||||
* Rong Xiang, Ian Craggs - C++ compatibility
|
||||
* Ian Craggs - binary password and will payload
|
||||
* Ian Craggs - MQTT 5.0 support
|
||||
*******************************************************************************/
|
||||
|
||||
/**
|
||||
* @file
|
||||
* \brief functions to deal with reading and writing of MQTT packets from and to sockets
|
||||
*
|
||||
* Some other related functions are in the MQTTPacket module
|
||||
*/
|
||||
|
||||
|
||||
#include "MQTTPacketOut.h"
|
||||
#include "Log.h"
|
||||
#include "StackTrace.h"
|
||||
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "Heap.h"
|
||||
|
||||
|
||||
/**
|
||||
* Send an MQTT CONNECT packet down a socket for V5 or later
|
||||
* @param client a structure from which to get all the required values
|
||||
* @param MQTTVersion the MQTT version to connect with
|
||||
* @param connectProperties MQTT V5 properties for the connect packet
|
||||
* @param willProperties MQTT V5 properties for the will message, if any
|
||||
* @return the completion code (e.g. TCPSOCKET_COMPLETE)
|
||||
*/
|
||||
int MQTTPacket_send_connect(Clients* client, int MQTTVersion,
|
||||
MQTTProperties* connectProperties, MQTTProperties* willProperties)
|
||||
{
|
||||
char *buf, *ptr;
|
||||
Connect packet;
|
||||
int rc = SOCKET_ERROR, len;
|
||||
|
||||
FUNC_ENTRY;
|
||||
packet.header.byte = 0;
|
||||
packet.header.bits.type = CONNECT;
|
||||
|
||||
len = ((MQTTVersion == MQTTVERSION_3_1) ? 12 : 10) + (int)strlen(client->clientID)+2;
|
||||
if (client->will)
|
||||
len += (int)strlen(client->will->topic)+2 + client->will->payloadlen+2;
|
||||
if (client->username)
|
||||
len += (int)strlen(client->username)+2;
|
||||
if (client->password)
|
||||
len += client->passwordlen+2;
|
||||
if (MQTTVersion >= MQTTVERSION_5)
|
||||
{
|
||||
len += MQTTProperties_len(connectProperties);
|
||||
if (client->will)
|
||||
len += MQTTProperties_len(willProperties);
|
||||
}
|
||||
|
||||
ptr = buf = malloc(len);
|
||||
if (ptr == NULL)
|
||||
goto exit_nofree;
|
||||
if (MQTTVersion == MQTTVERSION_3_1)
|
||||
{
|
||||
writeUTF(&ptr, "MQIsdp");
|
||||
writeChar(&ptr, (char)MQTTVERSION_3_1);
|
||||
}
|
||||
else if (MQTTVersion == MQTTVERSION_3_1_1 || MQTTVersion == MQTTVERSION_5)
|
||||
{
|
||||
writeUTF(&ptr, "MQTT");
|
||||
writeChar(&ptr, (char)MQTTVersion);
|
||||
}
|
||||
else
|
||||
goto exit;
|
||||
|
||||
packet.flags.all = 0;
|
||||
if (MQTTVersion >= MQTTVERSION_5)
|
||||
packet.flags.bits.cleanstart = client->cleanstart;
|
||||
else
|
||||
packet.flags.bits.cleanstart = client->cleansession;
|
||||
packet.flags.bits.will = (client->will) ? 1 : 0;
|
||||
if (packet.flags.bits.will)
|
||||
{
|
||||
packet.flags.bits.willQoS = client->will->qos;
|
||||
packet.flags.bits.willRetain = client->will->retained;
|
||||
}
|
||||
if (client->username)
|
||||
packet.flags.bits.username = 1;
|
||||
if (client->password)
|
||||
packet.flags.bits.password = 1;
|
||||
|
||||
writeChar(&ptr, packet.flags.all);
|
||||
writeInt(&ptr, client->keepAliveInterval);
|
||||
if (MQTTVersion >= MQTTVERSION_5)
|
||||
MQTTProperties_write(&ptr, connectProperties);
|
||||
writeUTF(&ptr, client->clientID);
|
||||
if (client->will)
|
||||
{
|
||||
if (MQTTVersion >= MQTTVERSION_5)
|
||||
MQTTProperties_write(&ptr, willProperties);
|
||||
writeUTF(&ptr, client->will->topic);
|
||||
writeData(&ptr, client->will->payload, client->will->payloadlen);
|
||||
}
|
||||
if (client->username)
|
||||
writeUTF(&ptr, client->username);
|
||||
if (client->password)
|
||||
writeData(&ptr, client->password, client->passwordlen);
|
||||
|
||||
rc = MQTTPacket_send(&client->net, packet.header, buf, len, 1, MQTTVersion);
|
||||
Log(LOG_PROTOCOL, 0, NULL, client->net.socket, client->clientID,
|
||||
MQTTVersion, client->cleansession, rc);
|
||||
exit:
|
||||
if (rc != TCPSOCKET_INTERRUPTED)
|
||||
free(buf);
|
||||
exit_nofree:
|
||||
FUNC_EXIT_RC(rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Function used in the new packets table to create connack packets.
|
||||
* @param MQTTVersion MQTT 5 or less?
|
||||
* @param aHeader the MQTT header byte
|
||||
* @param data the rest of the packet
|
||||
* @param datalen the length of the rest of the packet
|
||||
* @return pointer to the packet structure
|
||||
*/
|
||||
void* MQTTPacket_connack(int MQTTVersion, unsigned char aHeader, char* data, size_t datalen)
|
||||
{
|
||||
Connack* pack = NULL;
|
||||
char* curdata = data;
|
||||
char* enddata = &data[datalen];
|
||||
|
||||
FUNC_ENTRY;
|
||||
if ((pack = malloc(sizeof(Connack))) == NULL)
|
||||
goto exit;
|
||||
pack->MQTTVersion = MQTTVersion;
|
||||
pack->header.byte = aHeader;
|
||||
if (datalen < 2) /* enough data for connect flags and reason code? */
|
||||
{
|
||||
free(pack);
|
||||
pack = NULL;
|
||||
goto exit;
|
||||
}
|
||||
pack->flags.all = readChar(&curdata); /* connect flags */
|
||||
pack->rc = readChar(&curdata); /* reason code */
|
||||
if (MQTTVersion >= MQTTVERSION_5 && datalen > 2)
|
||||
{
|
||||
MQTTProperties props = MQTTProperties_initializer;
|
||||
pack->properties = props;
|
||||
if (MQTTProperties_read(&pack->properties, &curdata, enddata) != 1)
|
||||
{
|
||||
if (pack->properties.array)
|
||||
free(pack->properties.array);
|
||||
if (pack)
|
||||
free(pack);
|
||||
pack = NULL; /* signal protocol error */
|
||||
goto exit;
|
||||
}
|
||||
}
|
||||
exit:
|
||||
FUNC_EXIT;
|
||||
return pack;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Free allocated storage for a connack packet.
|
||||
* @param pack pointer to the connack packet structure
|
||||
*/
|
||||
void MQTTPacket_freeConnack(Connack* pack)
|
||||
{
|
||||
FUNC_ENTRY;
|
||||
if (pack->MQTTVersion >= MQTTVERSION_5)
|
||||
MQTTProperties_free(&pack->properties);
|
||||
free(pack);
|
||||
FUNC_EXIT;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Send an MQTT PINGREQ packet down a socket.
|
||||
* @param socket the open socket to send the data to
|
||||
* @param clientID the string client identifier, only used for tracing
|
||||
* @return the completion code (e.g. TCPSOCKET_COMPLETE)
|
||||
*/
|
||||
int MQTTPacket_send_pingreq(networkHandles* net, const char* clientID)
|
||||
{
|
||||
Header header;
|
||||
int rc = 0;
|
||||
|
||||
FUNC_ENTRY;
|
||||
header.byte = 0;
|
||||
header.bits.type = PINGREQ;
|
||||
rc = MQTTPacket_send(net, header, NULL, 0, 0, MQTTVERSION_3_1_1);
|
||||
Log(LOG_PROTOCOL, 20, NULL, net->socket, clientID, rc);
|
||||
FUNC_EXIT_RC(rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Send an MQTT subscribe packet down a socket.
|
||||
* @param topics list of topics
|
||||
* @param qoss list of corresponding QoSs
|
||||
* @param msgid the MQTT message id to use
|
||||
* @param dup boolean - whether to set the MQTT DUP flag
|
||||
* @param socket the open socket to send the data to
|
||||
* @param clientID the string client identifier, only used for tracing
|
||||
* @return the completion code (e.g. TCPSOCKET_COMPLETE)
|
||||
*/
|
||||
int MQTTPacket_send_subscribe(List* topics, List* qoss, MQTTSubscribe_options* opts, MQTTProperties* props,
|
||||
int msgid, int dup, Clients* client)
|
||||
{
|
||||
Header header;
|
||||
char *data, *ptr;
|
||||
int rc = -1;
|
||||
ListElement *elem = NULL, *qosElem = NULL;
|
||||
int datalen, i = 0;
|
||||
|
||||
FUNC_ENTRY;
|
||||
header.bits.type = SUBSCRIBE;
|
||||
header.bits.dup = dup;
|
||||
header.bits.qos = 1;
|
||||
header.bits.retain = 0;
|
||||
|
||||
datalen = 2 + topics->count * 3; /* utf length + char qos == 3 */
|
||||
while (ListNextElement(topics, &elem))
|
||||
datalen += (int)strlen((char*)(elem->content));
|
||||
if (client->MQTTVersion >= MQTTVERSION_5)
|
||||
datalen += MQTTProperties_len(props);
|
||||
|
||||
ptr = data = malloc(datalen);
|
||||
if (ptr == NULL)
|
||||
goto exit;
|
||||
writeInt(&ptr, msgid);
|
||||
|
||||
if (client->MQTTVersion >= MQTTVERSION_5)
|
||||
MQTTProperties_write(&ptr, props);
|
||||
|
||||
elem = NULL;
|
||||
while (ListNextElement(topics, &elem))
|
||||
{
|
||||
char subopts = 0;
|
||||
|
||||
ListNextElement(qoss, &qosElem);
|
||||
writeUTF(&ptr, (char*)(elem->content));
|
||||
subopts = *(int*)(qosElem->content);
|
||||
if (client->MQTTVersion >= MQTTVERSION_5 && opts != NULL)
|
||||
{
|
||||
subopts |= (opts[i].noLocal << 2); /* 1 bit */
|
||||
subopts |= (opts[i].retainAsPublished << 3); /* 1 bit */
|
||||
subopts |= (opts[i].retainHandling << 4); /* 2 bits */
|
||||
}
|
||||
writeChar(&ptr, subopts);
|
||||
++i;
|
||||
}
|
||||
rc = MQTTPacket_send(&client->net, header, data, datalen, 1, client->MQTTVersion);
|
||||
Log(LOG_PROTOCOL, 22, NULL, client->net.socket, client->clientID, msgid, rc);
|
||||
if (rc != TCPSOCKET_INTERRUPTED)
|
||||
free(data);
|
||||
exit:
|
||||
FUNC_EXIT_RC(rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Function used in the new packets table to create suback packets.
|
||||
* @param MQTTVersion the version of MQTT
|
||||
* @param aHeader the MQTT header byte
|
||||
* @param data the rest of the packet
|
||||
* @param datalen the length of the rest of the packet
|
||||
* @return pointer to the packet structure
|
||||
*/
|
||||
void* MQTTPacket_suback(int MQTTVersion, unsigned char aHeader, char* data, size_t datalen)
|
||||
{
|
||||
Suback* pack = NULL;
|
||||
char* curdata = data;
|
||||
char* enddata = &data[datalen];
|
||||
|
||||
FUNC_ENTRY;
|
||||
if ((pack = malloc(sizeof(Suback))) == NULL)
|
||||
goto exit;
|
||||
pack->MQTTVersion = MQTTVersion;
|
||||
pack->header.byte = aHeader;
|
||||
if (enddata - curdata < 2) /* Is there enough data to read the msgid? */
|
||||
{
|
||||
free(pack);
|
||||
pack = NULL;
|
||||
goto exit;
|
||||
}
|
||||
pack->msgId = readInt(&curdata);
|
||||
if (MQTTVersion >= MQTTVERSION_5)
|
||||
{
|
||||
MQTTProperties props = MQTTProperties_initializer;
|
||||
pack->properties = props;
|
||||
if (MQTTProperties_read(&pack->properties, &curdata, enddata) != 1)
|
||||
{
|
||||
if (pack->properties.array)
|
||||
free(pack->properties.array);
|
||||
if (pack)
|
||||
free(pack);
|
||||
pack = NULL; /* signal protocol error */
|
||||
goto exit;
|
||||
}
|
||||
}
|
||||
pack->qoss = ListInitialize();
|
||||
while ((size_t)(curdata - data) < datalen)
|
||||
{
|
||||
unsigned int* newint;
|
||||
newint = malloc(sizeof(unsigned int));
|
||||
if (newint == NULL)
|
||||
{
|
||||
if (pack->properties.array)
|
||||
free(pack->properties.array);
|
||||
if (pack)
|
||||
free(pack);
|
||||
pack = NULL; /* signal protocol error */
|
||||
goto exit;
|
||||
}
|
||||
*newint = (unsigned int)readChar(&curdata);
|
||||
ListAppend(pack->qoss, newint, sizeof(unsigned int));
|
||||
}
|
||||
if (pack->qoss->count == 0)
|
||||
{
|
||||
if (pack->properties.array)
|
||||
free(pack->properties.array);
|
||||
ListFree(pack->qoss);
|
||||
free(pack);
|
||||
pack = NULL;
|
||||
}
|
||||
exit:
|
||||
FUNC_EXIT;
|
||||
return pack;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Send an MQTT unsubscribe packet down a socket.
|
||||
* @param topics list of topics
|
||||
* @param msgid the MQTT message id to use
|
||||
* @param dup boolean - whether to set the MQTT DUP flag
|
||||
* @param socket the open socket to send the data to
|
||||
* @param clientID the string client identifier, only used for tracing
|
||||
* @return the completion code (e.g. TCPSOCKET_COMPLETE)
|
||||
*/
|
||||
int MQTTPacket_send_unsubscribe(List* topics, MQTTProperties* props, int msgid, int dup, Clients* client)
|
||||
{
|
||||
Header header;
|
||||
char *data, *ptr;
|
||||
int rc = SOCKET_ERROR;
|
||||
ListElement *elem = NULL;
|
||||
int datalen;
|
||||
|
||||
FUNC_ENTRY;
|
||||
header.bits.type = UNSUBSCRIBE;
|
||||
header.bits.dup = dup;
|
||||
header.bits.qos = 1;
|
||||
header.bits.retain = 0;
|
||||
|
||||
datalen = 2 + topics->count * 2; /* utf length == 2 */
|
||||
while (ListNextElement(topics, &elem))
|
||||
datalen += (int)strlen((char*)(elem->content));
|
||||
if (client->MQTTVersion >= MQTTVERSION_5)
|
||||
datalen += MQTTProperties_len(props);
|
||||
ptr = data = malloc(datalen);
|
||||
if (ptr == NULL)
|
||||
goto exit;
|
||||
|
||||
writeInt(&ptr, msgid);
|
||||
|
||||
if (client->MQTTVersion >= MQTTVERSION_5)
|
||||
MQTTProperties_write(&ptr, props);
|
||||
|
||||
elem = NULL;
|
||||
while (ListNextElement(topics, &elem))
|
||||
writeUTF(&ptr, (char*)(elem->content));
|
||||
rc = MQTTPacket_send(&client->net, header, data, datalen, 1, client->MQTTVersion);
|
||||
Log(LOG_PROTOCOL, 25, NULL, client->net.socket, client->clientID, msgid, rc);
|
||||
if (rc != TCPSOCKET_INTERRUPTED)
|
||||
free(data);
|
||||
exit:
|
||||
FUNC_EXIT_RC(rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Function used in the new packets table to create unsuback packets.
|
||||
* @param MQTTVersion the version of MQTT
|
||||
* @param aHeader the MQTT header byte
|
||||
* @param data the rest of the packet
|
||||
* @param datalen the length of the rest of the packet
|
||||
* @return pointer to the packet structure
|
||||
*/
|
||||
void* MQTTPacket_unsuback(int MQTTVersion, unsigned char aHeader, char* data, size_t datalen)
|
||||
{
|
||||
Unsuback* pack = NULL;
|
||||
char* curdata = data;
|
||||
char* enddata = &data[datalen];
|
||||
|
||||
FUNC_ENTRY;
|
||||
if ((pack = malloc(sizeof(Unsuback))) == NULL)
|
||||
goto exit;
|
||||
pack->MQTTVersion = MQTTVersion;
|
||||
pack->header.byte = aHeader;
|
||||
if (enddata - curdata < 2) /* Is there enough data? */
|
||||
{
|
||||
free(pack);
|
||||
pack = NULL;
|
||||
goto exit;
|
||||
}
|
||||
pack->msgId = readInt(&curdata);
|
||||
pack->reasonCodes = NULL;
|
||||
if (MQTTVersion >= MQTTVERSION_5)
|
||||
{
|
||||
MQTTProperties props = MQTTProperties_initializer;
|
||||
pack->properties = props;
|
||||
if (MQTTProperties_read(&pack->properties, &curdata, enddata) != 1)
|
||||
{
|
||||
if (pack->properties.array)
|
||||
free(pack->properties.array);
|
||||
if (pack)
|
||||
free(pack);
|
||||
pack = NULL; /* signal protocol error */
|
||||
goto exit;
|
||||
}
|
||||
pack->reasonCodes = ListInitialize();
|
||||
while ((size_t)(curdata - data) < datalen)
|
||||
{
|
||||
enum MQTTReasonCodes* newrc;
|
||||
newrc = malloc(sizeof(enum MQTTReasonCodes));
|
||||
if (newrc == NULL)
|
||||
{
|
||||
if (pack->properties.array)
|
||||
free(pack->properties.array);
|
||||
if (pack)
|
||||
free(pack);
|
||||
pack = NULL; /* signal protocol error */
|
||||
goto exit;
|
||||
}
|
||||
*newrc = (enum MQTTReasonCodes)readChar(&curdata);
|
||||
ListAppend(pack->reasonCodes, newrc, sizeof(enum MQTTReasonCodes));
|
||||
}
|
||||
if (pack->reasonCodes->count == 0)
|
||||
{
|
||||
ListFree(pack->reasonCodes);
|
||||
if (pack->properties.array)
|
||||
free(pack->properties.array);
|
||||
if (pack)
|
||||
free(pack);
|
||||
pack = NULL;
|
||||
}
|
||||
}
|
||||
exit:
|
||||
FUNC_EXIT;
|
||||
return pack;
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2009, 2018 IBM Corp.
|
||||
*
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v2.0
|
||||
* and Eclipse Distribution License v1.0 which accompany this distribution.
|
||||
*
|
||||
* The Eclipse Public License is available at
|
||||
* https://www.eclipse.org/legal/epl-2.0/
|
||||
* and the Eclipse Distribution License is available at
|
||||
* http://www.eclipse.org/org/documents/edl-v10.php.
|
||||
*
|
||||
* Contributors:
|
||||
* Ian Craggs - initial API and implementation and/or initial documentation
|
||||
* Ian Craggs, Allan Stockdill-Mander - SSL updates
|
||||
* Ian Craggs - MQTT 3.1.1 support
|
||||
* Ian Craggs - MQTT 5.0 support
|
||||
*******************************************************************************/
|
||||
|
||||
#if !defined(MQTTPACKETOUT_H)
|
||||
#define MQTTPACKETOUT_H
|
||||
|
||||
#include "MQTTPacket.h"
|
||||
|
||||
int MQTTPacket_send_connect(Clients* client, int MQTTVersion,
|
||||
MQTTProperties* connectProperties, MQTTProperties* willProperties);
|
||||
void* MQTTPacket_connack(int MQTTVersion, unsigned char aHeader, char* data, size_t datalen);
|
||||
void MQTTPacket_freeConnack(Connack* pack);
|
||||
|
||||
int MQTTPacket_send_pingreq(networkHandles* net, const char* clientID);
|
||||
|
||||
int MQTTPacket_send_subscribe(List* topics, List* qoss, MQTTSubscribe_options* opts, MQTTProperties* props,
|
||||
int msgid, int dup, Clients* client);
|
||||
void* MQTTPacket_suback(int MQTTVersion, unsigned char aHeader, char* data, size_t datalen);
|
||||
|
||||
int MQTTPacket_send_unsubscribe(List* topics, MQTTProperties* props, int msgid, int dup, Clients* client);
|
||||
void* MQTTPacket_unsuback(int MQTTVersion, unsigned char aHeader, char* data, size_t datalen);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,921 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2009, 2023 IBM Corp.
|
||||
*
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v2.0
|
||||
* and Eclipse Distribution License v1.0 which accompany this distribution.
|
||||
*
|
||||
* The Eclipse Public License is available at
|
||||
* https://www.eclipse.org/legal/epl-2.0/
|
||||
* and the Eclipse Distribution License is available at
|
||||
* http://www.eclipse.org/org/documents/edl-v10.php.
|
||||
*
|
||||
* Contributors:
|
||||
* Ian Craggs - initial API and implementation and/or initial documentation
|
||||
* Ian Craggs - async client updates
|
||||
* Ian Craggs - fix for bug 432903 - queue persistence
|
||||
* Ian Craggs - MQTT V5 updates
|
||||
*******************************************************************************/
|
||||
|
||||
/**
|
||||
* @file
|
||||
* \brief Functions that apply to persistence operations.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "MQTTPersistence.h"
|
||||
#include "MQTTPersistenceDefault.h"
|
||||
#include "MQTTProtocolClient.h"
|
||||
#include "Heap.h"
|
||||
|
||||
#if defined(_WIN32) || defined(_WIN64)
|
||||
#define snprintf _snprintf
|
||||
#endif
|
||||
|
||||
|
||||
static MQTTPersistence_qEntry* MQTTPersistence_restoreQueueEntry(char* buffer, size_t buflen, int MQTTVersion);
|
||||
static void MQTTPersistence_insertInSeqOrder(List* list, MQTTPersistence_qEntry* qEntry, size_t size);
|
||||
|
||||
/**
|
||||
* Creates a ::MQTTClient_persistence structure representing a persistence implementation.
|
||||
* @param persistence the ::MQTTClient_persistence structure.
|
||||
* @param type the type of the persistence implementation. See ::MQTTClient_create.
|
||||
* @param pcontext the context for this persistence implementation. See ::MQTTClient_create.
|
||||
* @return 0 if success, #MQTTCLIENT_PERSISTENCE_ERROR otherwise.
|
||||
*/
|
||||
#include "StackTrace.h"
|
||||
|
||||
int MQTTPersistence_create(MQTTClient_persistence** persistence, int type, void* pcontext)
|
||||
{
|
||||
int rc = 0;
|
||||
MQTTClient_persistence* per = NULL;
|
||||
|
||||
FUNC_ENTRY;
|
||||
#if !defined(NO_PERSISTENCE)
|
||||
switch (type)
|
||||
{
|
||||
case MQTTCLIENT_PERSISTENCE_NONE :
|
||||
per = NULL;
|
||||
break;
|
||||
case MQTTCLIENT_PERSISTENCE_DEFAULT :
|
||||
per = malloc(sizeof(MQTTClient_persistence));
|
||||
if ( per != NULL )
|
||||
{
|
||||
if ( pcontext == NULL )
|
||||
pcontext = "."; /* working directory */
|
||||
if ((per->context = malloc(strlen(pcontext) + 1)) == NULL)
|
||||
{
|
||||
free(per);
|
||||
rc = PAHO_MEMORY_ERROR;
|
||||
goto exit;
|
||||
}
|
||||
strcpy(per->context, pcontext);
|
||||
/* file system functions */
|
||||
per->popen = pstopen;
|
||||
per->pclose = pstclose;
|
||||
per->pput = pstput;
|
||||
per->pget = pstget;
|
||||
per->premove = pstremove;
|
||||
per->pkeys = pstkeys;
|
||||
per->pclear = pstclear;
|
||||
per->pcontainskey = pstcontainskey;
|
||||
}
|
||||
else
|
||||
rc = PAHO_MEMORY_ERROR;
|
||||
break;
|
||||
case MQTTCLIENT_PERSISTENCE_USER :
|
||||
per = (MQTTClient_persistence *)pcontext;
|
||||
if ( per == NULL || (per != NULL && (per->context == NULL || per->pclear == NULL ||
|
||||
per->pclose == NULL || per->pcontainskey == NULL || per->pget == NULL || per->pkeys == NULL ||
|
||||
per->popen == NULL || per->pput == NULL || per->premove == NULL)) )
|
||||
rc = MQTTCLIENT_PERSISTENCE_ERROR;
|
||||
break;
|
||||
default:
|
||||
rc = MQTTCLIENT_PERSISTENCE_ERROR;
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
|
||||
*persistence = per;
|
||||
exit:
|
||||
FUNC_EXIT_RC(rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Open persistent store and restore any persisted messages.
|
||||
* @param client the client as ::Clients.
|
||||
* @param serverURI the URI of the remote end.
|
||||
* @return 0 if success, #MQTTCLIENT_PERSISTENCE_ERROR otherwise.
|
||||
*/
|
||||
int MQTTPersistence_initialize(Clients *c, const char *serverURI)
|
||||
{
|
||||
int rc = 0;
|
||||
|
||||
FUNC_ENTRY;
|
||||
if ( c->persistence != NULL )
|
||||
{
|
||||
rc = c->persistence->popen(&(c->phandle), c->clientID, serverURI, c->persistence->context);
|
||||
if ( rc == 0 )
|
||||
rc = MQTTPersistence_restorePackets(c);
|
||||
}
|
||||
|
||||
FUNC_EXIT_RC(rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Close persistent store.
|
||||
* @param client the client as ::Clients.
|
||||
* @return 0 if success, #MQTTCLIENT_PERSISTENCE_ERROR otherwise.
|
||||
*/
|
||||
int MQTTPersistence_close(Clients *c)
|
||||
{
|
||||
int rc = 0;
|
||||
|
||||
FUNC_ENTRY;
|
||||
#if !defined(NO_PERSISTENCE)
|
||||
if (c->persistence != NULL)
|
||||
{
|
||||
rc = c->persistence->pclose(c->phandle);
|
||||
|
||||
if (c->persistence->popen == pstopen) {
|
||||
if (c->persistence->context)
|
||||
free(c->persistence->context);
|
||||
free(c->persistence);
|
||||
}
|
||||
|
||||
c->phandle = NULL;
|
||||
c->persistence = NULL;
|
||||
}
|
||||
#endif
|
||||
FUNC_EXIT_RC(rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears the persistent store.
|
||||
* @param client the client as ::Clients.
|
||||
* @return 0 if success, #MQTTCLIENT_PERSISTENCE_ERROR otherwise.
|
||||
*/
|
||||
int MQTTPersistence_clear(Clients *c)
|
||||
{
|
||||
int rc = 0;
|
||||
|
||||
FUNC_ENTRY;
|
||||
if (c->persistence != NULL)
|
||||
rc = c->persistence->pclear(c->phandle);
|
||||
|
||||
FUNC_EXIT_RC(rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Restores the persisted records to the outbound and inbound message queues of the
|
||||
* client.
|
||||
* @param client the client as ::Clients.
|
||||
* @return 0 if success, #MQTTCLIENT_PERSISTENCE_ERROR otherwise.
|
||||
*/
|
||||
int MQTTPersistence_restorePackets(Clients *c)
|
||||
{
|
||||
int rc = 0;
|
||||
char **msgkeys = NULL,
|
||||
*buffer = NULL;
|
||||
int nkeys, buflen;
|
||||
int i = 0;
|
||||
int msgs_sent = 0;
|
||||
int msgs_rcvd = 0;
|
||||
|
||||
FUNC_ENTRY;
|
||||
if (c->persistence && (rc = c->persistence->pkeys(c->phandle, &msgkeys, &nkeys)) == 0)
|
||||
{
|
||||
while (rc == 0 && i < nkeys)
|
||||
{
|
||||
if (strncmp(msgkeys[i], PERSISTENCE_COMMAND_KEY, strlen(PERSISTENCE_COMMAND_KEY)) == 0 ||
|
||||
strncmp(msgkeys[i], PERSISTENCE_V5_COMMAND_KEY, strlen(PERSISTENCE_V5_COMMAND_KEY)) == 0)
|
||||
{
|
||||
;
|
||||
}
|
||||
else if (strncmp(msgkeys[i], PERSISTENCE_QUEUE_KEY, strlen(PERSISTENCE_QUEUE_KEY)) == 0 ||
|
||||
strncmp(msgkeys[i], PERSISTENCE_V5_QUEUE_KEY, strlen(PERSISTENCE_V5_QUEUE_KEY)) == 0)
|
||||
{
|
||||
;
|
||||
}
|
||||
else if ((rc = c->persistence->pget(c->phandle, msgkeys[i], &buffer, &buflen)) == 0 &&
|
||||
(c->afterRead == NULL || (rc = c->afterRead(c->afterRead_context, &buffer, &buflen)) == 0))
|
||||
{
|
||||
int data_MQTTVersion = MQTTVERSION_3_1_1;
|
||||
char* cur_key = msgkeys[i];
|
||||
MQTTPacket* pack = NULL;
|
||||
|
||||
if (strncmp(cur_key, PERSISTENCE_V5_PUBLISH_RECEIVED,
|
||||
strlen(PERSISTENCE_V5_PUBLISH_RECEIVED)) == 0)
|
||||
{
|
||||
data_MQTTVersion = MQTTVERSION_5;
|
||||
cur_key = PERSISTENCE_PUBLISH_RECEIVED;
|
||||
}
|
||||
else if (strncmp(cur_key, PERSISTENCE_V5_PUBLISH_SENT,
|
||||
strlen(PERSISTENCE_V5_PUBLISH_SENT)) == 0)
|
||||
{
|
||||
data_MQTTVersion = MQTTVERSION_5;
|
||||
cur_key = PERSISTENCE_PUBLISH_SENT;
|
||||
}
|
||||
else if (strncmp(cur_key, PERSISTENCE_V5_PUBREL,
|
||||
strlen(PERSISTENCE_V5_PUBREL)) == 0)
|
||||
{
|
||||
data_MQTTVersion = MQTTVERSION_5;
|
||||
cur_key = PERSISTENCE_PUBREL;
|
||||
}
|
||||
|
||||
if (data_MQTTVersion == MQTTVERSION_5 && c->MQTTVersion < MQTTVERSION_5)
|
||||
{
|
||||
rc = MQTTCLIENT_PERSISTENCE_ERROR; /* can't restore version 5 data with a version 3 client */
|
||||
goto exit;
|
||||
}
|
||||
|
||||
pack = MQTTPersistence_restorePacket(data_MQTTVersion, buffer, buflen);
|
||||
if ( pack != NULL )
|
||||
{
|
||||
if (strncmp(cur_key, PERSISTENCE_PUBLISH_RECEIVED,
|
||||
strlen(PERSISTENCE_PUBLISH_RECEIVED)) == 0)
|
||||
{
|
||||
Publish* publish = (Publish*)pack;
|
||||
Messages* msg = NULL;
|
||||
publish->MQTTVersion = c->MQTTVersion;
|
||||
msg = MQTTProtocol_createMessage(publish, &msg, publish->header.bits.qos, publish->header.bits.retain, 1);
|
||||
msg->nextMessageType = PUBREL;
|
||||
/* order does not matter for persisted received messages */
|
||||
ListAppend(c->inboundMsgs, msg, msg->len);
|
||||
if (c->MQTTVersion >= MQTTVERSION_5)
|
||||
{
|
||||
free(msg->publish->payload);
|
||||
free(msg->publish->topic);
|
||||
msg->publish->payload = msg->publish->topic = NULL;
|
||||
}
|
||||
publish->topic = NULL;
|
||||
MQTTPacket_freePublish(publish);
|
||||
msgs_rcvd++;
|
||||
}
|
||||
else if (strncmp(cur_key, PERSISTENCE_PUBLISH_SENT,
|
||||
strlen(PERSISTENCE_PUBLISH_SENT)) == 0)
|
||||
{
|
||||
Publish* publish = (Publish*)pack;
|
||||
Messages* msg = NULL;
|
||||
const size_t keysize = PERSISTENCE_MAX_KEY_LENGTH + 1;
|
||||
char *key = malloc(keysize);
|
||||
int chars = 0;
|
||||
|
||||
if (!key)
|
||||
{
|
||||
rc = PAHO_MEMORY_ERROR;
|
||||
goto exit;
|
||||
}
|
||||
publish->MQTTVersion = c->MQTTVersion;
|
||||
if (publish->MQTTVersion >= MQTTVERSION_5)
|
||||
chars = snprintf(key, keysize, "%s%d", PERSISTENCE_V5_PUBREL, publish->msgId);
|
||||
else
|
||||
chars = snprintf(key, keysize, "%s%d", PERSISTENCE_PUBREL, publish->msgId);
|
||||
if (chars >= keysize)
|
||||
{
|
||||
rc = MQTTCLIENT_PERSISTENCE_ERROR;
|
||||
Log(LOG_ERROR, 0, "Error writing %d chars with snprintf", chars);
|
||||
}
|
||||
else
|
||||
{
|
||||
msg = MQTTProtocol_createMessage(publish, &msg, publish->header.bits.qos, publish->header.bits.retain, 1);
|
||||
if (c->persistence->pcontainskey(c->phandle, key) == 0)
|
||||
/* PUBLISH Qo2 and PUBREL sent */
|
||||
msg->nextMessageType = PUBCOMP;
|
||||
/* else: PUBLISH QoS1, or PUBLISH QoS2 and PUBREL not sent */
|
||||
/* retry at the first opportunity */
|
||||
memset(&msg->lastTouch, '\0', sizeof(msg->lastTouch));
|
||||
MQTTPersistence_insertInOrder(c->outboundMsgs, msg, msg->len);
|
||||
publish->topic = NULL;
|
||||
MQTTPacket_freePublish(publish);
|
||||
msgs_sent++;
|
||||
}
|
||||
free(key);
|
||||
}
|
||||
else if (strncmp(cur_key, PERSISTENCE_PUBREL, strlen(PERSISTENCE_PUBREL)) == 0)
|
||||
{
|
||||
/* orphaned PUBRELs ? */
|
||||
Pubrel* pubrel = (Pubrel*)pack;
|
||||
const size_t keysize = PERSISTENCE_MAX_KEY_LENGTH + 1;
|
||||
char *key = malloc(keysize);
|
||||
int chars = 0;
|
||||
|
||||
if (!key)
|
||||
{
|
||||
rc = PAHO_MEMORY_ERROR;
|
||||
goto exit;
|
||||
}
|
||||
pubrel->MQTTVersion = c->MQTTVersion;
|
||||
if (pubrel->MQTTVersion >= MQTTVERSION_5)
|
||||
chars = snprintf(key, keysize, "%s%d", PERSISTENCE_V5_PUBLISH_SENT, pubrel->msgId);
|
||||
else
|
||||
chars = snprintf(key, keysize, "%s%d", PERSISTENCE_PUBLISH_SENT, pubrel->msgId);
|
||||
if (chars >= keysize)
|
||||
{
|
||||
rc = MQTTCLIENT_PERSISTENCE_ERROR;
|
||||
Log(LOG_ERROR, 0, "Error writing %d chars with snprintf", chars);
|
||||
}
|
||||
else if (c->persistence->pcontainskey(c->phandle, key) != 0)
|
||||
rc = c->persistence->premove(c->phandle, msgkeys[i]);
|
||||
free(pubrel);
|
||||
free(key);
|
||||
}
|
||||
}
|
||||
else /* pack == NULL -> bad persisted record */
|
||||
rc = c->persistence->premove(c->phandle, msgkeys[i]);
|
||||
}
|
||||
if (buffer)
|
||||
{
|
||||
free(buffer);
|
||||
buffer = NULL;
|
||||
}
|
||||
if (msgkeys[i])
|
||||
{
|
||||
free(msgkeys[i]);
|
||||
msgkeys[i] = NULL;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
}
|
||||
Log(TRACE_MINIMUM, -1, "%d sent messages and %d received messages restored for client %s\n",
|
||||
msgs_sent, msgs_rcvd, c->clientID);
|
||||
MQTTPersistence_wrapMsgID(c);
|
||||
exit:
|
||||
if (msgkeys)
|
||||
{
|
||||
int i = 0;
|
||||
for (i = 0; i < nkeys; ++i)
|
||||
{
|
||||
if (msgkeys[i])
|
||||
free(msgkeys[i]);
|
||||
}
|
||||
free(msgkeys);
|
||||
}
|
||||
if (buffer)
|
||||
free(buffer);
|
||||
FUNC_EXIT_RC(rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns a MQTT packet restored from persisted data.
|
||||
* @param buffer the persisted data.
|
||||
* @param buflen the number of bytes of the data buffer.
|
||||
*/
|
||||
void* MQTTPersistence_restorePacket(int MQTTVersion, char* buffer, size_t buflen)
|
||||
{
|
||||
void* pack = NULL;
|
||||
Header header;
|
||||
int fixed_header_length = 1, ptype, remaining_length = 0;
|
||||
char c;
|
||||
int multiplier = 1;
|
||||
extern pf new_packets[];
|
||||
|
||||
FUNC_ENTRY;
|
||||
header.byte = buffer[0];
|
||||
/* decode the message length according to the MQTT algorithm */
|
||||
do
|
||||
{
|
||||
c = *(++buffer);
|
||||
remaining_length += (c & 127) * multiplier;
|
||||
multiplier *= 128;
|
||||
fixed_header_length++;
|
||||
} while ((c & 128) != 0);
|
||||
|
||||
if ( (fixed_header_length + remaining_length) == buflen )
|
||||
{
|
||||
ptype = header.bits.type;
|
||||
if (ptype >= CONNECT && ptype <= DISCONNECT && new_packets[ptype] != NULL)
|
||||
pack = (*new_packets[ptype])(MQTTVersion, header.byte, ++buffer, remaining_length);
|
||||
}
|
||||
|
||||
FUNC_EXIT;
|
||||
return pack;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Inserts the specified message into the list, maintaining message ID order.
|
||||
* @param list the list to insert the message into.
|
||||
* @param content the message to add.
|
||||
* @param size size of the message.
|
||||
*/
|
||||
void MQTTPersistence_insertInOrder(List* list, void* content, size_t size)
|
||||
{
|
||||
ListElement* index = NULL;
|
||||
ListElement* current = NULL;
|
||||
|
||||
FUNC_ENTRY;
|
||||
while(ListNextElement(list, ¤t) != NULL && index == NULL)
|
||||
{
|
||||
if ( ((Messages*)content)->msgid < ((Messages*)current->content)->msgid )
|
||||
index = current;
|
||||
}
|
||||
|
||||
ListInsert(list, content, size, index);
|
||||
FUNC_EXIT;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Adds a record to the persistent store. This function must not be called for QoS0
|
||||
* messages.
|
||||
* @param socket the socket of the client.
|
||||
* @param buf0 fixed header.
|
||||
* @param buf0len length of the fixed header.
|
||||
* @param count number of buffers representing the variable header and/or the payload.
|
||||
* @param buffers the buffers representing the variable header and/or the payload.
|
||||
* @param buflens length of the buffers representing the variable header and/or the payload.
|
||||
* @param htype MQTT packet type - PUBLISH or PUBREL
|
||||
* @param msgId the message ID.
|
||||
* @param scr 0 indicates message in the sending direction; 1 indicates message in the
|
||||
* receiving direction.
|
||||
* @param the MQTT version being used (>= MQTTVERSION_5 means properties included)
|
||||
* @return 0 if success, #MQTTCLIENT_PERSISTENCE_ERROR otherwise.
|
||||
*/
|
||||
int MQTTPersistence_putPacket(SOCKET socket, char* buf0, size_t buf0len, int count,
|
||||
char** buffers, size_t* buflens, int htype, int msgId, int scr, int MQTTVersion)
|
||||
{
|
||||
int rc = 0;
|
||||
extern ClientStates* bstate;
|
||||
int nbufs, i;
|
||||
int* lens = NULL;
|
||||
char** bufs = NULL;
|
||||
char *key;
|
||||
Clients* client = NULL;
|
||||
|
||||
FUNC_ENTRY;
|
||||
client = (Clients*)(ListFindItem(bstate->clients, &socket, clientSocketCompare)->content);
|
||||
if (client->persistence != NULL)
|
||||
{
|
||||
const size_t keysize = PERSISTENCE_MAX_KEY_LENGTH + 1;
|
||||
if ((key = malloc(keysize)) == NULL)
|
||||
{
|
||||
rc = PAHO_MEMORY_ERROR;
|
||||
goto exit;
|
||||
}
|
||||
nbufs = 1 + count;
|
||||
if ((lens = (int *)malloc(nbufs * sizeof(int))) == NULL)
|
||||
{
|
||||
free(key);
|
||||
rc = PAHO_MEMORY_ERROR;
|
||||
goto exit;
|
||||
}
|
||||
if ((bufs = (char **)malloc(nbufs * sizeof(char *))) == NULL)
|
||||
{
|
||||
free(key);
|
||||
free(lens);
|
||||
rc = PAHO_MEMORY_ERROR;
|
||||
goto exit;
|
||||
}
|
||||
lens[0] = (int)buf0len;
|
||||
bufs[0] = buf0;
|
||||
for (i = 0; i < count; i++)
|
||||
{
|
||||
lens[i+1] = (int)buflens[i];
|
||||
bufs[i+1] = buffers[i];
|
||||
}
|
||||
|
||||
/* key */
|
||||
if (scr == 0)
|
||||
{ /* sending */
|
||||
char* key_id = PERSISTENCE_PUBLISH_SENT;
|
||||
|
||||
if (htype == PUBLISH) /* PUBLISH QoS1 and QoS2*/
|
||||
{
|
||||
if (MQTTVersion >= MQTTVERSION_5)
|
||||
key_id = PERSISTENCE_V5_PUBLISH_SENT;
|
||||
}
|
||||
else if (htype == PUBREL) /* PUBREL */
|
||||
{
|
||||
if (MQTTVersion >= MQTTVERSION_5)
|
||||
key_id = PERSISTENCE_V5_PUBREL;
|
||||
else
|
||||
key_id = PERSISTENCE_PUBREL;
|
||||
}
|
||||
if (snprintf(key, keysize, "%s%d", key_id, msgId) >= keysize)
|
||||
rc = MQTTCLIENT_PERSISTENCE_ERROR;
|
||||
}
|
||||
else if (scr == 1) /* receiving PUBLISH QoS2 */
|
||||
{
|
||||
char* key_id = PERSISTENCE_PUBLISH_RECEIVED;
|
||||
|
||||
if (MQTTVersion >= MQTTVERSION_5)
|
||||
key_id = PERSISTENCE_V5_PUBLISH_RECEIVED;
|
||||
if (snprintf(key, keysize, "%s%d", key_id, msgId) >= keysize)
|
||||
rc = MQTTCLIENT_PERSISTENCE_ERROR;
|
||||
}
|
||||
|
||||
if (rc == 0 && client->beforeWrite)
|
||||
rc = client->beforeWrite(client->beforeWrite_context, nbufs, bufs, lens);
|
||||
|
||||
if (rc == 0)
|
||||
rc = client->persistence->pput(client->phandle, key, nbufs, bufs, lens);
|
||||
|
||||
free(key);
|
||||
free(lens);
|
||||
free(bufs);
|
||||
}
|
||||
|
||||
exit:
|
||||
FUNC_EXIT_RC(rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Deletes a record from the persistent store.
|
||||
* @param client the client as ::Clients.
|
||||
* @param type the type of the persisted record: #PERSISTENCE_PUBLISH_SENT, #PERSISTENCE_PUBREL
|
||||
* or #PERSISTENCE_PUBLISH_RECEIVED.
|
||||
* @param qos the qos field of the message.
|
||||
* @param msgId the message ID.
|
||||
* @return 0 if success, #MQTTCLIENT_PERSISTENCE_ERROR otherwise.
|
||||
*/
|
||||
int MQTTPersistence_remove(Clients* c, char *type, int qos, int msgId)
|
||||
{
|
||||
int rc = 0;
|
||||
|
||||
FUNC_ENTRY;
|
||||
if (c->persistence != NULL)
|
||||
{
|
||||
const size_t keysize = PERSISTENCE_MAX_KEY_LENGTH + 1;
|
||||
char *key = malloc(keysize);
|
||||
int chars = 0;
|
||||
|
||||
if (!key)
|
||||
{
|
||||
rc = PAHO_MEMORY_ERROR;
|
||||
goto exit;
|
||||
}
|
||||
if (strcmp(type, PERSISTENCE_PUBLISH_SENT) == 0 ||
|
||||
strcmp(type, PERSISTENCE_V5_PUBLISH_SENT) == 0)
|
||||
{
|
||||
if ((chars = snprintf(key, keysize, "%s%d", PERSISTENCE_V5_PUBLISH_SENT, msgId)) >= keysize)
|
||||
rc = MQTTCLIENT_PERSISTENCE_ERROR;
|
||||
else
|
||||
{
|
||||
rc = c->persistence->premove(c->phandle, key);
|
||||
if ((chars = snprintf(key, keysize, "%s%d", PERSISTENCE_V5_PUBREL, msgId)) >= keysize)
|
||||
rc = MQTTCLIENT_PERSISTENCE_ERROR;
|
||||
else
|
||||
{
|
||||
rc += c->persistence->premove(c->phandle, key);
|
||||
if ((chars = snprintf(key, keysize, "%s%d", PERSISTENCE_PUBLISH_SENT, msgId)) >= keysize)
|
||||
rc = MQTTCLIENT_PERSISTENCE_ERROR;
|
||||
else
|
||||
{
|
||||
rc += c->persistence->premove(c->phandle, key);
|
||||
if ((chars = snprintf(key, keysize, "%s%d", PERSISTENCE_PUBREL, msgId)) >= keysize)
|
||||
rc = MQTTCLIENT_PERSISTENCE_ERROR;
|
||||
else
|
||||
rc += c->persistence->premove(c->phandle, key);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else /* PERSISTENCE_PUBLISH_SENT && qos == 1 */
|
||||
{ /* or PERSISTENCE_PUBLISH_RECEIVED */
|
||||
|
||||
if ((chars = snprintf(key, keysize, "%s%d", PERSISTENCE_V5_PUBLISH_RECEIVED, msgId)) >= keysize)
|
||||
rc = MQTTCLIENT_PERSISTENCE_ERROR;
|
||||
else
|
||||
{
|
||||
rc = c->persistence->premove(c->phandle, key);
|
||||
if ((chars = snprintf(key, keysize, "%s%d", PERSISTENCE_PUBLISH_RECEIVED, msgId)) >= keysize)
|
||||
rc = MQTTCLIENT_PERSISTENCE_ERROR;
|
||||
else
|
||||
rc += c->persistence->premove(c->phandle, key);
|
||||
}
|
||||
}
|
||||
if (rc == MQTTCLIENT_PERSISTENCE_ERROR)
|
||||
Log(LOG_ERROR, 0, "Error writing %d chars with snprintf", chars);
|
||||
free(key);
|
||||
}
|
||||
|
||||
exit:
|
||||
FUNC_EXIT_RC(rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Checks whether the message IDs wrapped by looking for the largest gap between two consecutive
|
||||
* message IDs in the outboundMsgs queue.
|
||||
* @param client the client as ::Clients.
|
||||
*/
|
||||
void MQTTPersistence_wrapMsgID(Clients *client)
|
||||
{
|
||||
ListElement* wrapel = NULL;
|
||||
ListElement* current = NULL;
|
||||
|
||||
FUNC_ENTRY;
|
||||
if ( client->outboundMsgs->count > 0 )
|
||||
{
|
||||
int firstMsgID = ((Messages*)client->outboundMsgs->first->content)->msgid;
|
||||
int lastMsgID = ((Messages*)client->outboundMsgs->last->content)->msgid;
|
||||
int gap = MAX_MSG_ID - lastMsgID + firstMsgID;
|
||||
current = ListNextElement(client->outboundMsgs, ¤t);
|
||||
|
||||
while(ListNextElement(client->outboundMsgs, ¤t) != NULL)
|
||||
{
|
||||
int curMsgID = ((Messages*)current->content)->msgid;
|
||||
int curPrevMsgID = ((Messages*)current->prev->content)->msgid;
|
||||
int curgap = curMsgID - curPrevMsgID;
|
||||
if ( curgap > gap )
|
||||
{
|
||||
gap = curgap;
|
||||
wrapel = current;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ( wrapel != NULL )
|
||||
{
|
||||
/* put wrapel at the beginning of the queue */
|
||||
client->outboundMsgs->first->prev = client->outboundMsgs->last;
|
||||
client->outboundMsgs->last->next = client->outboundMsgs->first;
|
||||
client->outboundMsgs->first = wrapel;
|
||||
client->outboundMsgs->last = wrapel->prev;
|
||||
client->outboundMsgs->first->prev = NULL;
|
||||
client->outboundMsgs->last->next = NULL;
|
||||
}
|
||||
FUNC_EXIT;
|
||||
}
|
||||
|
||||
|
||||
#if !defined(NO_PERSISTENCE)
|
||||
int MQTTPersistence_unpersistQueueEntry(Clients* client, MQTTPersistence_qEntry* qe)
|
||||
{
|
||||
int rc = 0;
|
||||
#if defined(_WIN32) || defined(_WIN64)
|
||||
#define KEYSIZE PERSISTENCE_MAX_KEY_LENGTH + 1
|
||||
#else
|
||||
const size_t KEYSIZE = PERSISTENCE_MAX_KEY_LENGTH + 1;
|
||||
#endif
|
||||
char key[KEYSIZE];
|
||||
int chars = 0;
|
||||
|
||||
FUNC_ENTRY;
|
||||
if (client->MQTTVersion >= MQTTVERSION_5)
|
||||
chars = snprintf(key, KEYSIZE, "%s%u", PERSISTENCE_V5_QUEUE_KEY, qe->seqno);
|
||||
else
|
||||
chars = snprintf(key, KEYSIZE, "%s%u", PERSISTENCE_QUEUE_KEY, qe->seqno);
|
||||
if (chars >= KEYSIZE)
|
||||
{
|
||||
Log(LOG_ERROR, 0, "Error writing %d chars with snprintf", chars);
|
||||
rc = MQTTCLIENT_PERSISTENCE_ERROR;
|
||||
}
|
||||
else if ((rc = client->persistence->premove(client->phandle, key)) != 0)
|
||||
Log(LOG_ERROR, 0, "Error %d removing qEntry from persistence", rc);
|
||||
FUNC_EXIT_RC(rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
#define MAX_NO_OF_BUFFERS 9
|
||||
int MQTTPersistence_persistQueueEntry(Clients* aclient, MQTTPersistence_qEntry* qe)
|
||||
{
|
||||
int rc = 0;
|
||||
int bufindex = 0;
|
||||
#if !defined(_WIN32) && !defined(_WIN64)
|
||||
const size_t KEYSIZE = PERSISTENCE_MAX_KEY_LENGTH + 1;
|
||||
#endif
|
||||
char key[KEYSIZE];
|
||||
int chars = 0;
|
||||
int lens[MAX_NO_OF_BUFFERS];
|
||||
void* bufs[MAX_NO_OF_BUFFERS];
|
||||
int props_allocated = 0;
|
||||
|
||||
FUNC_ENTRY;
|
||||
bufs[bufindex] = &qe->msg->payloadlen;
|
||||
lens[bufindex++] = sizeof(qe->msg->payloadlen);
|
||||
|
||||
bufs[bufindex] = qe->msg->payload;
|
||||
lens[bufindex++] = qe->msg->payloadlen;
|
||||
|
||||
bufs[bufindex] = &qe->msg->qos;
|
||||
lens[bufindex++] = sizeof(qe->msg->qos);
|
||||
|
||||
bufs[bufindex] = &qe->msg->retained;
|
||||
lens[bufindex++] = sizeof(qe->msg->retained);
|
||||
|
||||
bufs[bufindex] = &qe->msg->dup;
|
||||
lens[bufindex++] = sizeof(qe->msg->dup);
|
||||
|
||||
bufs[bufindex] = &qe->msg->msgid;
|
||||
lens[bufindex++] = sizeof(qe->msg->msgid);
|
||||
|
||||
bufs[bufindex] = qe->topicName;
|
||||
lens[bufindex++] = (int)strlen(qe->topicName) + 1;
|
||||
|
||||
bufs[bufindex] = &qe->topicLen;
|
||||
lens[bufindex++] = sizeof(qe->topicLen);
|
||||
|
||||
if (++aclient->qentry_seqno == PERSISTENCE_SEQNO_LIMIT)
|
||||
aclient->qentry_seqno = 0;
|
||||
|
||||
if (aclient->MQTTVersion >= MQTTVERSION_5) /* persist properties */
|
||||
{
|
||||
MQTTProperties no_props = MQTTProperties_initializer;
|
||||
MQTTProperties* props = &no_props;
|
||||
int temp_len = 0;
|
||||
char* ptr = NULL;
|
||||
|
||||
if (qe->msg->struct_version >= 1)
|
||||
props = &qe->msg->properties;
|
||||
|
||||
temp_len = MQTTProperties_len(props);
|
||||
ptr = bufs[bufindex] = malloc(temp_len);
|
||||
if (!ptr)
|
||||
{
|
||||
rc = PAHO_MEMORY_ERROR;
|
||||
goto exit;
|
||||
}
|
||||
props_allocated = bufindex;
|
||||
rc = MQTTProperties_write(&ptr, props);
|
||||
lens[bufindex++] = temp_len;
|
||||
|
||||
chars = snprintf(key, KEYSIZE, "%s%u", PERSISTENCE_V5_QUEUE_KEY, aclient->qentry_seqno);
|
||||
}
|
||||
else
|
||||
chars = snprintf(key, KEYSIZE, "%s%u", PERSISTENCE_QUEUE_KEY, aclient->qentry_seqno);
|
||||
|
||||
if (chars >= KEYSIZE)
|
||||
rc = MQTTCLIENT_PERSISTENCE_ERROR;
|
||||
else
|
||||
{
|
||||
qe->seqno = aclient->qentry_seqno;
|
||||
|
||||
if (aclient->beforeWrite)
|
||||
rc = aclient->beforeWrite(aclient->beforeWrite_context, bufindex, (char**)bufs, lens);
|
||||
|
||||
if (rc == 0 && (rc = aclient->persistence->pput(aclient->phandle, key, bufindex, (char**)bufs, lens)) != 0)
|
||||
Log(LOG_ERROR, 0, "Error persisting queue entry, rc %d", rc);
|
||||
}
|
||||
if (props_allocated != 0)
|
||||
free(bufs[props_allocated]);
|
||||
|
||||
exit:
|
||||
FUNC_EXIT_RC(rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
static MQTTPersistence_qEntry* MQTTPersistence_restoreQueueEntry(char* buffer, size_t buflen, int MQTTVersion)
|
||||
{
|
||||
MQTTPersistence_qEntry* qe = NULL;
|
||||
char* ptr = buffer;
|
||||
int data_size;
|
||||
|
||||
FUNC_ENTRY;
|
||||
if ((qe = malloc(sizeof(MQTTPersistence_qEntry))) == NULL)
|
||||
goto exit;
|
||||
memset(qe, '\0', sizeof(MQTTPersistence_qEntry));
|
||||
|
||||
if ((qe->msg = malloc(sizeof(MQTTPersistence_message))) == NULL)
|
||||
{
|
||||
free(qe);
|
||||
qe = NULL;
|
||||
goto exit;
|
||||
}
|
||||
memset(qe->msg, '\0', sizeof(MQTTPersistence_message));
|
||||
|
||||
qe->msg->struct_version = 1;
|
||||
|
||||
qe->msg->payloadlen = *(int*)ptr;
|
||||
ptr += sizeof(int);
|
||||
|
||||
data_size = qe->msg->payloadlen;
|
||||
if ((qe->msg->payload = malloc(data_size)) == NULL)
|
||||
{
|
||||
free(qe->msg);
|
||||
free(qe);
|
||||
qe = NULL;
|
||||
goto exit;
|
||||
}
|
||||
memcpy(qe->msg->payload, ptr, data_size);
|
||||
ptr += data_size;
|
||||
|
||||
qe->msg->qos = *(int*)ptr;
|
||||
ptr += sizeof(int);
|
||||
|
||||
qe->msg->retained = *(int*)ptr;
|
||||
ptr += sizeof(int);
|
||||
|
||||
qe->msg->dup = *(int*)ptr;
|
||||
ptr += sizeof(int);
|
||||
|
||||
qe->msg->msgid = *(int*)ptr;
|
||||
ptr += sizeof(int);
|
||||
|
||||
data_size = (int)strlen(ptr) + 1;
|
||||
if ((qe->topicName = malloc(data_size)) == NULL)
|
||||
{
|
||||
free(qe->msg->payload);
|
||||
free(qe->msg);
|
||||
free(qe);
|
||||
qe = NULL;
|
||||
goto exit;
|
||||
}
|
||||
strcpy(qe->topicName, ptr);
|
||||
ptr += data_size;
|
||||
|
||||
qe->topicLen = *(int*)ptr;
|
||||
ptr += sizeof(int);
|
||||
|
||||
if (MQTTVersion >= MQTTVERSION_5 &&
|
||||
MQTTProperties_read(&qe->msg->properties, &ptr, buffer + buflen) != 1)
|
||||
Log(LOG_ERROR, -1, "Error restoring properties from persistence");
|
||||
|
||||
exit:
|
||||
FUNC_EXIT;
|
||||
return qe;
|
||||
}
|
||||
|
||||
|
||||
static void MQTTPersistence_insertInSeqOrder(List* list, MQTTPersistence_qEntry* qEntry, size_t size)
|
||||
{
|
||||
ListElement* index = NULL;
|
||||
ListElement* current = NULL;
|
||||
|
||||
FUNC_ENTRY;
|
||||
while (ListNextElement(list, ¤t) != NULL && index == NULL)
|
||||
{
|
||||
if (qEntry->seqno < ((MQTTPersistence_qEntry*)current->content)->seqno)
|
||||
index = current;
|
||||
}
|
||||
ListInsert(list, qEntry, size, index);
|
||||
FUNC_EXIT;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Restores a queue of messages from persistence to memory
|
||||
* @param c the client as ::Clients - the client object to restore the messages to
|
||||
* @return return code, 0 if successful
|
||||
*/
|
||||
int MQTTPersistence_restoreMessageQueue(Clients* c)
|
||||
{
|
||||
int rc = 0;
|
||||
char **msgkeys;
|
||||
int nkeys;
|
||||
int i = 0;
|
||||
int entries_restored = 0;
|
||||
|
||||
FUNC_ENTRY;
|
||||
if (c->persistence && (rc = c->persistence->pkeys(c->phandle, &msgkeys, &nkeys)) == 0)
|
||||
{
|
||||
while (rc == 0 && i < nkeys)
|
||||
{
|
||||
char *buffer = NULL;
|
||||
int buflen;
|
||||
|
||||
if (strncmp(msgkeys[i], PERSISTENCE_QUEUE_KEY, strlen(PERSISTENCE_QUEUE_KEY)) != 0 &&
|
||||
strncmp(msgkeys[i], PERSISTENCE_V5_QUEUE_KEY, strlen(PERSISTENCE_V5_QUEUE_KEY)) != 0)
|
||||
{
|
||||
; /* ignore if not a queue entry key */
|
||||
}
|
||||
else if ((rc = c->persistence->pget(c->phandle, msgkeys[i], &buffer, &buflen)) == 0 &&
|
||||
(c->afterRead == NULL || (rc = c->afterRead(c->afterRead_context, &buffer, &buflen)) == 0))
|
||||
{
|
||||
int MQTTVersion =
|
||||
(strncmp(msgkeys[i], PERSISTENCE_V5_QUEUE_KEY, strlen(PERSISTENCE_V5_QUEUE_KEY)) == 0)
|
||||
? MQTTVERSION_5 : MQTTVERSION_3_1_1;
|
||||
MQTTPersistence_qEntry* qe = MQTTPersistence_restoreQueueEntry(buffer, buflen, MQTTVersion);
|
||||
|
||||
if (qe)
|
||||
{
|
||||
qe->seqno = atoi(strchr(msgkeys[i], '-')+1); /* key format is tag'-'seqno */
|
||||
MQTTPersistence_insertInSeqOrder(c->messageQueue, qe, sizeof(MQTTPersistence_qEntry));
|
||||
c->qentry_seqno = max(c->qentry_seqno, qe->seqno);
|
||||
entries_restored++;
|
||||
}
|
||||
if (buffer)
|
||||
free(buffer);
|
||||
}
|
||||
if (msgkeys[i])
|
||||
{
|
||||
free(msgkeys[i]);
|
||||
}
|
||||
i++;
|
||||
}
|
||||
if (msgkeys != NULL)
|
||||
free(msgkeys);
|
||||
}
|
||||
Log(TRACE_MINIMUM, -1, "%d queued messages restored for client %s", entries_restored, c->clientID);
|
||||
FUNC_EXIT_RC(rc);
|
||||
return rc;
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,99 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2009, 2022 IBM Corp.
|
||||
*
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v2.0
|
||||
* and Eclipse Distribution License v1.0 which accompany this distribution.
|
||||
*
|
||||
* The Eclipse Public License is available at
|
||||
* https://www.eclipse.org/legal/epl-2.0/
|
||||
* and the Eclipse Distribution License is available at
|
||||
* http://www.eclipse.org/org/documents/edl-v10.php.
|
||||
*
|
||||
* Contributors:
|
||||
* Ian Craggs - initial API and implementation and/or initial documentation
|
||||
* Ian Craggs - async client updates
|
||||
* Ian Craggs - fix for bug 432903 - queue persistence
|
||||
* Ian Craggs - MQTT V5 updates
|
||||
*******************************************************************************/
|
||||
|
||||
#if !defined(MQTTPERSISTENCE_H)
|
||||
#define MQTTPERSISTENCE_H
|
||||
|
||||
#if defined(__cplusplus)
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include "Clients.h"
|
||||
#include "MQTTProperties.h"
|
||||
|
||||
/** Stem of the key for a sent PUBLISH QoS1 or QoS2 */
|
||||
#define PERSISTENCE_PUBLISH_SENT "s-"
|
||||
/** Stem of the key for a sent PUBREL */
|
||||
#define PERSISTENCE_PUBREL "sc-"
|
||||
/** Stem of the key for a received PUBLISH QoS2 */
|
||||
#define PERSISTENCE_PUBLISH_RECEIVED "r-"
|
||||
|
||||
/** Stem of the key for a sent MQTT V5 PUBLISH QoS1 or QoS2 */
|
||||
#define PERSISTENCE_V5_PUBLISH_SENT "s5-"
|
||||
/** Stem of the key for a sent MQTT V5 PUBREL */
|
||||
#define PERSISTENCE_V5_PUBREL "sc5-"
|
||||
/** Stem of the key for a received MQTT V5 PUBLISH QoS2 */
|
||||
#define PERSISTENCE_V5_PUBLISH_RECEIVED "r5-"
|
||||
|
||||
/** Stem of the key for an async client command */
|
||||
#define PERSISTENCE_COMMAND_KEY "c-"
|
||||
/** Stem of the key for an MQTT V5 async client command */
|
||||
#define PERSISTENCE_V5_COMMAND_KEY "c5-"
|
||||
/** Stem of the key for an client incoming message queue */
|
||||
#define PERSISTENCE_QUEUE_KEY "q-"
|
||||
/** Stem of the key for an MQTT V5 incoming message queue */
|
||||
#define PERSISTENCE_V5_QUEUE_KEY "q5-"
|
||||
/** Maximum length of a stem for a persistence key */
|
||||
#define PERSISTENCE_MAX_STEM_LENGTH 4
|
||||
/** Maximum allowed length of a persistence key */
|
||||
#define PERSISTENCE_MAX_KEY_LENGTH 10
|
||||
/** Maximum size of an integer sequence number appended to a persistence key */
|
||||
#define PERSISTENCE_SEQNO_LIMIT 1000000 /*10^(PERSISTENCE_MAX_KEY_LENGTH - PERSISTENCE_MAX_STEM_LENGTH)*/
|
||||
|
||||
int MQTTPersistence_create(MQTTClient_persistence** per, int type, void* pcontext);
|
||||
int MQTTPersistence_initialize(Clients* c, const char* serverURI);
|
||||
int MQTTPersistence_close(Clients* c);
|
||||
int MQTTPersistence_clear(Clients* c);
|
||||
int MQTTPersistence_restorePackets(Clients* c);
|
||||
void* MQTTPersistence_restorePacket(int MQTTVersion, char* buffer, size_t buflen);
|
||||
void MQTTPersistence_insertInOrder(List* list, void* content, size_t size);
|
||||
int MQTTPersistence_putPacket(SOCKET socket, char* buf0, size_t buf0len, int count,
|
||||
char** buffers, size_t* buflens, int htype, int msgId, int scr, int MQTTVersion);
|
||||
int MQTTPersistence_remove(Clients* c, char* type, int qos, int msgId);
|
||||
void MQTTPersistence_wrapMsgID(Clients *c);
|
||||
|
||||
typedef struct
|
||||
{
|
||||
char struct_id[4];
|
||||
int struct_version;
|
||||
int payloadlen;
|
||||
void* payload;
|
||||
int qos;
|
||||
int retained;
|
||||
int dup;
|
||||
int msgid;
|
||||
MQTTProperties properties;
|
||||
} MQTTPersistence_message;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
MQTTPersistence_message* msg;
|
||||
char* topicName;
|
||||
int topicLen;
|
||||
unsigned int seqno; /* only used on restore */
|
||||
} MQTTPersistence_qEntry;
|
||||
|
||||
int MQTTPersistence_unpersistQueueEntry(Clients* client, MQTTPersistence_qEntry* qe);
|
||||
int MQTTPersistence_persistQueueEntry(Clients* aclient, MQTTPersistence_qEntry* qe);
|
||||
int MQTTPersistence_restoreMessageQueue(Clients* c);
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,36 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2009, 2018 IBM Corp.
|
||||
*
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v2.0
|
||||
* and Eclipse Distribution License v1.0 which accompany this distribution.
|
||||
*
|
||||
* The Eclipse Public License is available at
|
||||
* https://www.eclipse.org/legal/epl-2.0/
|
||||
* and the Eclipse Distribution License is available at
|
||||
* http://www.eclipse.org/org/documents/edl-v10.php.
|
||||
*
|
||||
* Contributors:
|
||||
* Ian Craggs - initial API and implementation and/or initial documentation
|
||||
*******************************************************************************/
|
||||
|
||||
#if !defined(MQTTPERSISTENCEDEFAULT_H)
|
||||
#define MQTTPERSISTENCEDEFAULT_H
|
||||
|
||||
/** Extension of the filename */
|
||||
#define MESSAGE_FILENAME_EXTENSION ".msg"
|
||||
|
||||
/* prototypes of the functions for the default file system persistence */
|
||||
int pstopen(void** handle, const char* clientID, const char* serverURI, void* context);
|
||||
int pstclose(void* handle);
|
||||
int pstput(void* handle, char* key, int bufcount, char* buffers[], int buflens[]);
|
||||
int pstget(void* handle, char* key, char** buffer, int* buflen);
|
||||
int pstremove(void* handle, char* key);
|
||||
int pstkeys(void* handle, char*** keys, int* nkeys);
|
||||
int pstclear(void* handle);
|
||||
int pstcontainskey(void* handle, char* key);
|
||||
|
||||
int pstmkdir(char *pPathname);
|
||||
|
||||
#endif
|
||||
|
||||
+552
@@ -0,0 +1,552 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2017, 2020 IBM Corp. and others
|
||||
*
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v2.0
|
||||
* and Eclipse Distribution License v1.0 which accompany this distribution.
|
||||
*
|
||||
* The Eclipse Public License is available at
|
||||
* https://www.eclipse.org/legal/epl-2.0/
|
||||
* and the Eclipse Distribution License is available at
|
||||
* http://www.eclipse.org/org/documents/edl-v10.php.
|
||||
*
|
||||
* Contributors:
|
||||
* Ian Craggs - initial API and implementation and/or initial documentation
|
||||
*******************************************************************************/
|
||||
|
||||
#include "MQTTProperties.h"
|
||||
|
||||
#include "MQTTPacket.h"
|
||||
#include "MQTTProtocolClient.h"
|
||||
#include "Heap.h"
|
||||
#include "StackTrace.h"
|
||||
|
||||
#include <memory.h>
|
||||
|
||||
#define ARRAY_SIZE(a) (sizeof(a) / sizeof(a[0]))
|
||||
|
||||
static struct nameToType
|
||||
{
|
||||
enum MQTTPropertyCodes name;
|
||||
enum MQTTPropertyTypes type;
|
||||
} namesToTypes[] =
|
||||
{
|
||||
{MQTTPROPERTY_CODE_PAYLOAD_FORMAT_INDICATOR, MQTTPROPERTY_TYPE_BYTE},
|
||||
{MQTTPROPERTY_CODE_MESSAGE_EXPIRY_INTERVAL, MQTTPROPERTY_TYPE_FOUR_BYTE_INTEGER},
|
||||
{MQTTPROPERTY_CODE_CONTENT_TYPE, MQTTPROPERTY_TYPE_UTF_8_ENCODED_STRING},
|
||||
{MQTTPROPERTY_CODE_RESPONSE_TOPIC, MQTTPROPERTY_TYPE_UTF_8_ENCODED_STRING},
|
||||
{MQTTPROPERTY_CODE_CORRELATION_DATA, MQTTPROPERTY_TYPE_BINARY_DATA},
|
||||
{MQTTPROPERTY_CODE_SUBSCRIPTION_IDENTIFIER, MQTTPROPERTY_TYPE_VARIABLE_BYTE_INTEGER},
|
||||
{MQTTPROPERTY_CODE_SESSION_EXPIRY_INTERVAL, MQTTPROPERTY_TYPE_FOUR_BYTE_INTEGER},
|
||||
{MQTTPROPERTY_CODE_ASSIGNED_CLIENT_IDENTIFER, MQTTPROPERTY_TYPE_UTF_8_ENCODED_STRING},
|
||||
{MQTTPROPERTY_CODE_SERVER_KEEP_ALIVE, MQTTPROPERTY_TYPE_TWO_BYTE_INTEGER},
|
||||
{MQTTPROPERTY_CODE_AUTHENTICATION_METHOD, MQTTPROPERTY_TYPE_UTF_8_ENCODED_STRING},
|
||||
{MQTTPROPERTY_CODE_AUTHENTICATION_DATA, MQTTPROPERTY_TYPE_BINARY_DATA},
|
||||
{MQTTPROPERTY_CODE_REQUEST_PROBLEM_INFORMATION, MQTTPROPERTY_TYPE_BYTE},
|
||||
{MQTTPROPERTY_CODE_WILL_DELAY_INTERVAL, MQTTPROPERTY_TYPE_FOUR_BYTE_INTEGER},
|
||||
{MQTTPROPERTY_CODE_REQUEST_RESPONSE_INFORMATION, MQTTPROPERTY_TYPE_BYTE},
|
||||
{MQTTPROPERTY_CODE_RESPONSE_INFORMATION, MQTTPROPERTY_TYPE_UTF_8_ENCODED_STRING},
|
||||
{MQTTPROPERTY_CODE_SERVER_REFERENCE, MQTTPROPERTY_TYPE_UTF_8_ENCODED_STRING},
|
||||
{MQTTPROPERTY_CODE_REASON_STRING, MQTTPROPERTY_TYPE_UTF_8_ENCODED_STRING},
|
||||
{MQTTPROPERTY_CODE_RECEIVE_MAXIMUM, MQTTPROPERTY_TYPE_TWO_BYTE_INTEGER},
|
||||
{MQTTPROPERTY_CODE_TOPIC_ALIAS_MAXIMUM, MQTTPROPERTY_TYPE_TWO_BYTE_INTEGER},
|
||||
{MQTTPROPERTY_CODE_TOPIC_ALIAS, MQTTPROPERTY_TYPE_TWO_BYTE_INTEGER},
|
||||
{MQTTPROPERTY_CODE_MAXIMUM_QOS, MQTTPROPERTY_TYPE_BYTE},
|
||||
{MQTTPROPERTY_CODE_RETAIN_AVAILABLE, MQTTPROPERTY_TYPE_BYTE},
|
||||
{MQTTPROPERTY_CODE_USER_PROPERTY, MQTTPROPERTY_TYPE_UTF_8_STRING_PAIR},
|
||||
{MQTTPROPERTY_CODE_MAXIMUM_PACKET_SIZE, MQTTPROPERTY_TYPE_FOUR_BYTE_INTEGER},
|
||||
{MQTTPROPERTY_CODE_WILDCARD_SUBSCRIPTION_AVAILABLE, MQTTPROPERTY_TYPE_BYTE},
|
||||
{MQTTPROPERTY_CODE_SUBSCRIPTION_IDENTIFIERS_AVAILABLE, MQTTPROPERTY_TYPE_BYTE},
|
||||
{MQTTPROPERTY_CODE_SHARED_SUBSCRIPTION_AVAILABLE, MQTTPROPERTY_TYPE_BYTE}
|
||||
};
|
||||
|
||||
|
||||
static char* datadup(const MQTTLenString* str)
|
||||
{
|
||||
char* temp = malloc(str->len);
|
||||
if (temp)
|
||||
memcpy(temp, str->data, str->len);
|
||||
return temp;
|
||||
}
|
||||
|
||||
|
||||
int MQTTProperty_getType(enum MQTTPropertyCodes value)
|
||||
{
|
||||
int i, rc = -1;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(namesToTypes); ++i)
|
||||
{
|
||||
if (namesToTypes[i].name == value)
|
||||
{
|
||||
rc = namesToTypes[i].type;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
int MQTTProperties_len(MQTTProperties* props)
|
||||
{
|
||||
/* properties length is an mbi */
|
||||
return (props == NULL) ? 1 : props->length + MQTTPacket_VBIlen(props->length);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Add a new property to a property list
|
||||
* @param props the property list
|
||||
* @param prop the new property
|
||||
* @return code 0 is success
|
||||
*/
|
||||
int MQTTProperties_add(MQTTProperties* props, const MQTTProperty* prop)
|
||||
{
|
||||
int rc = 0, type;
|
||||
|
||||
if ((type = MQTTProperty_getType(prop->identifier)) < 0)
|
||||
{
|
||||
/*StackTrace_printStack(stdout);*/
|
||||
rc = MQTT_INVALID_PROPERTY_ID;
|
||||
goto exit;
|
||||
}
|
||||
else if (props->array == NULL)
|
||||
{
|
||||
props->max_count = 10;
|
||||
props->array = malloc(sizeof(MQTTProperty) * props->max_count);
|
||||
}
|
||||
else if (props->count == props->max_count)
|
||||
{
|
||||
props->max_count += 10;
|
||||
props->array = realloc(props->array, sizeof(MQTTProperty) * props->max_count);
|
||||
}
|
||||
|
||||
if (props->array)
|
||||
{
|
||||
int len = 0;
|
||||
|
||||
props->array[props->count++] = *prop;
|
||||
/* calculate length */
|
||||
switch (type)
|
||||
{
|
||||
case MQTTPROPERTY_TYPE_BYTE:
|
||||
len = 1;
|
||||
break;
|
||||
case MQTTPROPERTY_TYPE_TWO_BYTE_INTEGER:
|
||||
len = 2;
|
||||
break;
|
||||
case MQTTPROPERTY_TYPE_FOUR_BYTE_INTEGER:
|
||||
len = 4;
|
||||
break;
|
||||
case MQTTPROPERTY_TYPE_VARIABLE_BYTE_INTEGER:
|
||||
len = MQTTPacket_VBIlen(prop->value.integer4);
|
||||
break;
|
||||
case MQTTPROPERTY_TYPE_BINARY_DATA:
|
||||
case MQTTPROPERTY_TYPE_UTF_8_ENCODED_STRING:
|
||||
case MQTTPROPERTY_TYPE_UTF_8_STRING_PAIR:
|
||||
len = 2 + prop->value.data.len;
|
||||
props->array[props->count-1].value.data.data = datadup(&prop->value.data);
|
||||
if (type == MQTTPROPERTY_TYPE_UTF_8_STRING_PAIR)
|
||||
{
|
||||
len += 2 + prop->value.value.len;
|
||||
props->array[props->count-1].value.value.data = datadup(&prop->value.value);
|
||||
}
|
||||
break;
|
||||
}
|
||||
props->length += len + 1; /* add identifier byte */
|
||||
}
|
||||
else
|
||||
rc = PAHO_MEMORY_ERROR;
|
||||
|
||||
exit:
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
int MQTTProperty_write(char** pptr, MQTTProperty* prop)
|
||||
{
|
||||
int rc = -1,
|
||||
type = -1;
|
||||
|
||||
type = MQTTProperty_getType(prop->identifier);
|
||||
if (type >= MQTTPROPERTY_TYPE_BYTE && type <= MQTTPROPERTY_TYPE_UTF_8_STRING_PAIR)
|
||||
{
|
||||
writeChar(pptr, prop->identifier);
|
||||
switch (type)
|
||||
{
|
||||
case MQTTPROPERTY_TYPE_BYTE:
|
||||
writeChar(pptr, prop->value.byte);
|
||||
rc = 1;
|
||||
break;
|
||||
case MQTTPROPERTY_TYPE_TWO_BYTE_INTEGER:
|
||||
writeInt(pptr, prop->value.integer2);
|
||||
rc = 2;
|
||||
break;
|
||||
case MQTTPROPERTY_TYPE_FOUR_BYTE_INTEGER:
|
||||
writeInt4(pptr, prop->value.integer4);
|
||||
rc = 4;
|
||||
break;
|
||||
case MQTTPROPERTY_TYPE_VARIABLE_BYTE_INTEGER:
|
||||
rc = MQTTPacket_encode(*pptr, prop->value.integer4);
|
||||
*pptr += rc;
|
||||
break;
|
||||
case MQTTPROPERTY_TYPE_BINARY_DATA:
|
||||
case MQTTPROPERTY_TYPE_UTF_8_ENCODED_STRING:
|
||||
writeMQTTLenString(pptr, prop->value.data);
|
||||
rc = prop->value.data.len + 2; /* include length field */
|
||||
break;
|
||||
case MQTTPROPERTY_TYPE_UTF_8_STRING_PAIR:
|
||||
writeMQTTLenString(pptr, prop->value.data);
|
||||
writeMQTTLenString(pptr, prop->value.value);
|
||||
rc = prop->value.data.len + prop->value.value.len + 4; /* include length fields */
|
||||
break;
|
||||
}
|
||||
}
|
||||
return rc + 1; /* include identifier byte */
|
||||
}
|
||||
|
||||
|
||||
int MQTTProperties_write(char** pptr, const MQTTProperties* properties)
|
||||
{
|
||||
int rc = -1;
|
||||
int i = 0, len = 0;
|
||||
|
||||
/* write the entire property list length first */
|
||||
if (properties == NULL)
|
||||
{
|
||||
*pptr += MQTTPacket_encode(*pptr, 0);
|
||||
rc = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
*pptr += MQTTPacket_encode(*pptr, properties->length);
|
||||
len = rc = 1;
|
||||
for (i = 0; i < properties->count; ++i)
|
||||
{
|
||||
rc = MQTTProperty_write(pptr, &properties->array[i]);
|
||||
if (rc < 0)
|
||||
break;
|
||||
else
|
||||
len += rc;
|
||||
}
|
||||
if (rc >= 0)
|
||||
rc = len;
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
int MQTTProperty_read(MQTTProperty* prop, char** pptr, char* enddata)
|
||||
{
|
||||
int type = -1,
|
||||
len = -1;
|
||||
|
||||
prop->identifier = readChar(pptr);
|
||||
type = MQTTProperty_getType(prop->identifier);
|
||||
if (type >= MQTTPROPERTY_TYPE_BYTE && type <= MQTTPROPERTY_TYPE_UTF_8_STRING_PAIR)
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case MQTTPROPERTY_TYPE_BYTE:
|
||||
prop->value.byte = readChar(pptr);
|
||||
len = 1;
|
||||
break;
|
||||
case MQTTPROPERTY_TYPE_TWO_BYTE_INTEGER:
|
||||
prop->value.integer2 = readInt(pptr);
|
||||
len = 2;
|
||||
break;
|
||||
case MQTTPROPERTY_TYPE_FOUR_BYTE_INTEGER:
|
||||
prop->value.integer4 = readInt4(pptr);
|
||||
len = 4;
|
||||
break;
|
||||
case MQTTPROPERTY_TYPE_VARIABLE_BYTE_INTEGER:
|
||||
len = MQTTPacket_decodeBuf(*pptr, &prop->value.integer4);
|
||||
*pptr += len;
|
||||
break;
|
||||
case MQTTPROPERTY_TYPE_BINARY_DATA:
|
||||
case MQTTPROPERTY_TYPE_UTF_8_ENCODED_STRING:
|
||||
case MQTTPROPERTY_TYPE_UTF_8_STRING_PAIR:
|
||||
if ((len = MQTTLenStringRead(&prop->value.data, pptr, enddata)) == -1)
|
||||
break; /* error */
|
||||
if ((prop->value.data.data = datadup(&prop->value.data)) == NULL)
|
||||
{
|
||||
len = -1;
|
||||
break; /* error */
|
||||
}
|
||||
if (type == MQTTPROPERTY_TYPE_UTF_8_STRING_PAIR)
|
||||
{
|
||||
int proplen = MQTTLenStringRead(&prop->value.value, pptr, enddata);
|
||||
|
||||
if (proplen == -1)
|
||||
{
|
||||
len = -1;
|
||||
free(prop->value.data.data);
|
||||
break;
|
||||
}
|
||||
len += proplen;
|
||||
if ((prop->value.value.data = datadup(&prop->value.value)) == NULL)
|
||||
{
|
||||
len = -1;
|
||||
free(prop->value.data.data);
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
return (len == -1) ? -1 : len + 1; /* 1 byte for identifier */
|
||||
}
|
||||
|
||||
|
||||
int MQTTProperties_read(MQTTProperties* properties, char** pptr, char* enddata)
|
||||
{
|
||||
int rc = 0;
|
||||
unsigned int remlength = 0;
|
||||
|
||||
FUNC_ENTRY;
|
||||
/* we assume an initialized properties structure */
|
||||
if (enddata - (*pptr) > 0) /* enough length to read the VBI? */
|
||||
{
|
||||
int proplen = 0;
|
||||
|
||||
*pptr += MQTTPacket_decodeBuf(*pptr, &remlength);
|
||||
properties->length = remlength;
|
||||
while (remlength > 0)
|
||||
{
|
||||
if (properties->count == properties->max_count)
|
||||
{
|
||||
properties->max_count += 10;
|
||||
if (properties->max_count == 10)
|
||||
properties->array = malloc(sizeof(MQTTProperty) * properties->max_count);
|
||||
else
|
||||
properties->array = realloc(properties->array, sizeof(MQTTProperty) * properties->max_count);
|
||||
}
|
||||
if (properties->array == NULL)
|
||||
{
|
||||
rc = PAHO_MEMORY_ERROR;
|
||||
goto exit;
|
||||
}
|
||||
if ((proplen = MQTTProperty_read(&properties->array[properties->count], pptr, enddata)) > 0)
|
||||
remlength -= proplen;
|
||||
else
|
||||
break;
|
||||
properties->count++;
|
||||
}
|
||||
if (remlength == 0)
|
||||
rc = 1; /* data read successfully */
|
||||
}
|
||||
|
||||
if (rc != 1 && properties->array != NULL)
|
||||
MQTTProperties_free(properties);
|
||||
|
||||
exit:
|
||||
FUNC_EXIT_RC(rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
struct {
|
||||
enum MQTTPropertyCodes value;
|
||||
const char* name;
|
||||
} nameToString[] =
|
||||
{
|
||||
{MQTTPROPERTY_CODE_PAYLOAD_FORMAT_INDICATOR, "PAYLOAD_FORMAT_INDICATOR"},
|
||||
{MQTTPROPERTY_CODE_MESSAGE_EXPIRY_INTERVAL, "MESSAGE_EXPIRY_INTERVAL"},
|
||||
{MQTTPROPERTY_CODE_CONTENT_TYPE, "CONTENT_TYPE"},
|
||||
{MQTTPROPERTY_CODE_RESPONSE_TOPIC, "RESPONSE_TOPIC"},
|
||||
{MQTTPROPERTY_CODE_CORRELATION_DATA, "CORRELATION_DATA"},
|
||||
{MQTTPROPERTY_CODE_SUBSCRIPTION_IDENTIFIER, "SUBSCRIPTION_IDENTIFIER"},
|
||||
{MQTTPROPERTY_CODE_SESSION_EXPIRY_INTERVAL, "SESSION_EXPIRY_INTERVAL"},
|
||||
{MQTTPROPERTY_CODE_ASSIGNED_CLIENT_IDENTIFER, "ASSIGNED_CLIENT_IDENTIFER"},
|
||||
{MQTTPROPERTY_CODE_SERVER_KEEP_ALIVE, "SERVER_KEEP_ALIVE"},
|
||||
{MQTTPROPERTY_CODE_AUTHENTICATION_METHOD, "AUTHENTICATION_METHOD"},
|
||||
{MQTTPROPERTY_CODE_AUTHENTICATION_DATA, "AUTHENTICATION_DATA"},
|
||||
{MQTTPROPERTY_CODE_REQUEST_PROBLEM_INFORMATION, "REQUEST_PROBLEM_INFORMATION"},
|
||||
{MQTTPROPERTY_CODE_WILL_DELAY_INTERVAL, "WILL_DELAY_INTERVAL"},
|
||||
{MQTTPROPERTY_CODE_REQUEST_RESPONSE_INFORMATION, "REQUEST_RESPONSE_INFORMATION"},
|
||||
{MQTTPROPERTY_CODE_RESPONSE_INFORMATION, "RESPONSE_INFORMATION"},
|
||||
{MQTTPROPERTY_CODE_SERVER_REFERENCE, "SERVER_REFERENCE"},
|
||||
{MQTTPROPERTY_CODE_REASON_STRING, "REASON_STRING"},
|
||||
{MQTTPROPERTY_CODE_RECEIVE_MAXIMUM, "RECEIVE_MAXIMUM"},
|
||||
{MQTTPROPERTY_CODE_TOPIC_ALIAS_MAXIMUM, "TOPIC_ALIAS_MAXIMUM"},
|
||||
{MQTTPROPERTY_CODE_TOPIC_ALIAS, "TOPIC_ALIAS"},
|
||||
{MQTTPROPERTY_CODE_MAXIMUM_QOS, "MAXIMUM_QOS"},
|
||||
{MQTTPROPERTY_CODE_RETAIN_AVAILABLE, "RETAIN_AVAILABLE"},
|
||||
{MQTTPROPERTY_CODE_USER_PROPERTY, "USER_PROPERTY"},
|
||||
{MQTTPROPERTY_CODE_MAXIMUM_PACKET_SIZE, "MAXIMUM_PACKET_SIZE"},
|
||||
{MQTTPROPERTY_CODE_WILDCARD_SUBSCRIPTION_AVAILABLE, "WILDCARD_SUBSCRIPTION_AVAILABLE"},
|
||||
{MQTTPROPERTY_CODE_SUBSCRIPTION_IDENTIFIERS_AVAILABLE, "SUBSCRIPTION_IDENTIFIERS_AVAILABLE"},
|
||||
{MQTTPROPERTY_CODE_SHARED_SUBSCRIPTION_AVAILABLE, "SHARED_SUBSCRIPTION_AVAILABLE"}
|
||||
};
|
||||
|
||||
const char* MQTTPropertyName(enum MQTTPropertyCodes value)
|
||||
{
|
||||
int i = 0;
|
||||
const char* result = NULL;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(nameToString); ++i)
|
||||
{
|
||||
if (nameToString[i].value == value)
|
||||
{
|
||||
result = nameToString[i].name;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
void MQTTProperties_free(MQTTProperties* props)
|
||||
{
|
||||
int i = 0;
|
||||
|
||||
FUNC_ENTRY;
|
||||
if (props == NULL)
|
||||
goto exit;
|
||||
for (i = 0; i < props->count; ++i)
|
||||
{
|
||||
int id = props->array[i].identifier;
|
||||
int type = MQTTProperty_getType(id);
|
||||
|
||||
switch (type)
|
||||
{
|
||||
case MQTTPROPERTY_TYPE_BINARY_DATA:
|
||||
case MQTTPROPERTY_TYPE_UTF_8_ENCODED_STRING:
|
||||
case MQTTPROPERTY_TYPE_UTF_8_STRING_PAIR:
|
||||
free(props->array[i].value.data.data);
|
||||
if (type == MQTTPROPERTY_TYPE_UTF_8_STRING_PAIR)
|
||||
free(props->array[i].value.value.data);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (props->array)
|
||||
free(props->array);
|
||||
memset(props, '\0', sizeof(MQTTProperties)); /* zero all fields */
|
||||
exit:
|
||||
FUNC_EXIT;
|
||||
}
|
||||
|
||||
|
||||
MQTTProperties MQTTProperties_copy(const MQTTProperties* props)
|
||||
{
|
||||
int i = 0;
|
||||
MQTTProperties result = MQTTProperties_initializer;
|
||||
|
||||
FUNC_ENTRY;
|
||||
for (i = 0; i < props->count; ++i)
|
||||
{
|
||||
int rc = 0;
|
||||
|
||||
if ((rc = MQTTProperties_add(&result, &props->array[i])) != 0)
|
||||
Log(LOG_ERROR, -1, "Error from MQTTProperties add %d", rc);
|
||||
}
|
||||
|
||||
FUNC_EXIT;
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
int MQTTProperties_hasProperty(MQTTProperties *props, enum MQTTPropertyCodes propid)
|
||||
{
|
||||
int i = 0;
|
||||
int found = 0;
|
||||
|
||||
for (i = 0; i < props->count; ++i)
|
||||
{
|
||||
if (propid == props->array[i].identifier)
|
||||
{
|
||||
found = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return found;
|
||||
}
|
||||
|
||||
|
||||
int MQTTProperties_propertyCount(MQTTProperties *props, enum MQTTPropertyCodes propid)
|
||||
{
|
||||
int i = 0;
|
||||
int count = 0;
|
||||
|
||||
for (i = 0; i < props->count; ++i)
|
||||
{
|
||||
if (propid == props->array[i].identifier)
|
||||
count++;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
|
||||
int MQTTProperties_getNumericValueAt(MQTTProperties *props, enum MQTTPropertyCodes propid, int index)
|
||||
{
|
||||
int i = 0;
|
||||
int rc = -9999999;
|
||||
int cur_index = 0;
|
||||
|
||||
for (i = 0; i < props->count; ++i)
|
||||
{
|
||||
int id = props->array[i].identifier;
|
||||
|
||||
if (id == propid)
|
||||
{
|
||||
if (cur_index < index)
|
||||
{
|
||||
cur_index++;
|
||||
continue;
|
||||
}
|
||||
switch (MQTTProperty_getType(id))
|
||||
{
|
||||
case MQTTPROPERTY_TYPE_BYTE:
|
||||
rc = props->array[i].value.byte;
|
||||
break;
|
||||
case MQTTPROPERTY_TYPE_TWO_BYTE_INTEGER:
|
||||
rc = props->array[i].value.integer2;
|
||||
break;
|
||||
case MQTTPROPERTY_TYPE_FOUR_BYTE_INTEGER:
|
||||
case MQTTPROPERTY_TYPE_VARIABLE_BYTE_INTEGER:
|
||||
rc = props->array[i].value.integer4;
|
||||
break;
|
||||
default:
|
||||
rc = -999999;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
int MQTTProperties_getNumericValue(MQTTProperties *props, enum MQTTPropertyCodes propid)
|
||||
{
|
||||
return MQTTProperties_getNumericValueAt(props, propid, 0);
|
||||
}
|
||||
|
||||
|
||||
MQTTProperty* MQTTProperties_getPropertyAt(MQTTProperties *props, enum MQTTPropertyCodes propid, int index)
|
||||
{
|
||||
int i = 0;
|
||||
MQTTProperty* result = NULL;
|
||||
int cur_index = 0;
|
||||
|
||||
for (i = 0; i < props->count; ++i)
|
||||
{
|
||||
int id = props->array[i].identifier;
|
||||
|
||||
if (id == propid)
|
||||
{
|
||||
if (cur_index == index)
|
||||
{
|
||||
result = &props->array[i];
|
||||
break;
|
||||
}
|
||||
else
|
||||
cur_index++;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
MQTTProperty* MQTTProperties_getProperty(MQTTProperties *props, enum MQTTPropertyCodes propid)
|
||||
{
|
||||
return MQTTProperties_getPropertyAt(props, propid, 0);
|
||||
}
|
||||
+222
@@ -0,0 +1,222 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2017, 2023 IBM Corp. and others
|
||||
*
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v2.0
|
||||
* and Eclipse Distribution License v1.0 which accompany this distribution.
|
||||
*
|
||||
* The Eclipse Public License is available at
|
||||
* https://www.eclipse.org/legal/epl-2.0/
|
||||
* and the Eclipse Distribution License is available at
|
||||
* http://www.eclipse.org/org/documents/edl-v10.php.
|
||||
*
|
||||
* Contributors:
|
||||
* Ian Craggs - initial API and implementation and/or initial documentation
|
||||
*******************************************************************************/
|
||||
|
||||
#if !defined(MQTTPROPERTIES_H)
|
||||
#define MQTTPROPERTIES_H
|
||||
|
||||
#include "MQTTExportDeclarations.h"
|
||||
|
||||
#define MQTT_INVALID_PROPERTY_ID -2
|
||||
|
||||
/** The one byte MQTT V5 property indicator */
|
||||
enum MQTTPropertyCodes {
|
||||
MQTTPROPERTY_CODE_PAYLOAD_FORMAT_INDICATOR = 1, /**< The value is 1 */
|
||||
MQTTPROPERTY_CODE_MESSAGE_EXPIRY_INTERVAL = 2, /**< The value is 2 */
|
||||
MQTTPROPERTY_CODE_CONTENT_TYPE = 3, /**< The value is 3 */
|
||||
MQTTPROPERTY_CODE_RESPONSE_TOPIC = 8, /**< The value is 8 */
|
||||
MQTTPROPERTY_CODE_CORRELATION_DATA = 9, /**< The value is 9 */
|
||||
MQTTPROPERTY_CODE_SUBSCRIPTION_IDENTIFIER = 11, /**< The value is 11 */
|
||||
MQTTPROPERTY_CODE_SESSION_EXPIRY_INTERVAL = 17, /**< The value is 17 */
|
||||
MQTTPROPERTY_CODE_ASSIGNED_CLIENT_IDENTIFER = 18,/**< The value is 18 */
|
||||
MQTTPROPERTY_CODE_SERVER_KEEP_ALIVE = 19, /**< The value is 19 */
|
||||
MQTTPROPERTY_CODE_AUTHENTICATION_METHOD = 21, /**< The value is 21 */
|
||||
MQTTPROPERTY_CODE_AUTHENTICATION_DATA = 22, /**< The value is 22 */
|
||||
MQTTPROPERTY_CODE_REQUEST_PROBLEM_INFORMATION = 23,/**< The value is 23 */
|
||||
MQTTPROPERTY_CODE_WILL_DELAY_INTERVAL = 24, /**< The value is 24 */
|
||||
MQTTPROPERTY_CODE_REQUEST_RESPONSE_INFORMATION = 25,/**< The value is 25 */
|
||||
MQTTPROPERTY_CODE_RESPONSE_INFORMATION = 26, /**< The value is 26 */
|
||||
MQTTPROPERTY_CODE_SERVER_REFERENCE = 28, /**< The value is 28 */
|
||||
MQTTPROPERTY_CODE_REASON_STRING = 31, /**< The value is 31 */
|
||||
MQTTPROPERTY_CODE_RECEIVE_MAXIMUM = 33, /**< The value is 33*/
|
||||
MQTTPROPERTY_CODE_TOPIC_ALIAS_MAXIMUM = 34, /**< The value is 34 */
|
||||
MQTTPROPERTY_CODE_TOPIC_ALIAS = 35, /**< The value is 35 */
|
||||
MQTTPROPERTY_CODE_MAXIMUM_QOS = 36, /**< The value is 36 */
|
||||
MQTTPROPERTY_CODE_RETAIN_AVAILABLE = 37, /**< The value is 37 */
|
||||
MQTTPROPERTY_CODE_USER_PROPERTY = 38, /**< The value is 38 */
|
||||
MQTTPROPERTY_CODE_MAXIMUM_PACKET_SIZE = 39, /**< The value is 39 */
|
||||
MQTTPROPERTY_CODE_WILDCARD_SUBSCRIPTION_AVAILABLE = 40,/**< The value is 40 */
|
||||
MQTTPROPERTY_CODE_SUBSCRIPTION_IDENTIFIERS_AVAILABLE = 41,/**< The value is 41 */
|
||||
MQTTPROPERTY_CODE_SHARED_SUBSCRIPTION_AVAILABLE = 42/**< The value is 241 */
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns a printable string description of an MQTT V5 property code.
|
||||
* @param value an MQTT V5 property code.
|
||||
* @return the printable string description of the input property code.
|
||||
* NULL if the code was not found.
|
||||
*/
|
||||
LIBMQTT_API const char* MQTTPropertyName(enum MQTTPropertyCodes value);
|
||||
|
||||
/** The one byte MQTT V5 property type */
|
||||
enum MQTTPropertyTypes {
|
||||
MQTTPROPERTY_TYPE_BYTE,
|
||||
MQTTPROPERTY_TYPE_TWO_BYTE_INTEGER,
|
||||
MQTTPROPERTY_TYPE_FOUR_BYTE_INTEGER,
|
||||
MQTTPROPERTY_TYPE_VARIABLE_BYTE_INTEGER,
|
||||
MQTTPROPERTY_TYPE_BINARY_DATA,
|
||||
MQTTPROPERTY_TYPE_UTF_8_ENCODED_STRING,
|
||||
MQTTPROPERTY_TYPE_UTF_8_STRING_PAIR
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns the MQTT V5 type code of an MQTT V5 property.
|
||||
* @param value an MQTT V5 property code.
|
||||
* @return the MQTT V5 type code of the input property. -1 if the code was not found.
|
||||
*/
|
||||
LIBMQTT_API int MQTTProperty_getType(enum MQTTPropertyCodes value);
|
||||
|
||||
/**
|
||||
* The data for a length delimited string
|
||||
*/
|
||||
typedef struct
|
||||
{
|
||||
int len; /**< the length of the string */
|
||||
char* data; /**< pointer to the string data */
|
||||
} MQTTLenString;
|
||||
|
||||
|
||||
/**
|
||||
* Structure to hold an MQTT version 5 property of any type
|
||||
*/
|
||||
typedef struct
|
||||
{
|
||||
enum MQTTPropertyCodes identifier; /**< The MQTT V5 property id. A multi-byte integer. */
|
||||
/** The value of the property, as a union of the different possible types. */
|
||||
union {
|
||||
unsigned char byte; /**< holds the value of a byte property type */
|
||||
unsigned short integer2; /**< holds the value of a 2 byte integer property type */
|
||||
unsigned int integer4; /**< holds the value of a 4 byte integer property type */
|
||||
struct {
|
||||
MQTTLenString data; /**< The value of a string property, or the name of a user property. */
|
||||
MQTTLenString value; /**< The value of a user property. */
|
||||
};
|
||||
} value;
|
||||
} MQTTProperty;
|
||||
|
||||
/**
|
||||
* MQTT version 5 property list
|
||||
*/
|
||||
typedef struct MQTTProperties
|
||||
{
|
||||
int count; /**< number of property entries in the array */
|
||||
int max_count; /**< max number of properties that the currently allocated array can store */
|
||||
int length; /**< mbi: byte length of all properties */
|
||||
MQTTProperty *array; /**< array of properties */
|
||||
} MQTTProperties;
|
||||
|
||||
#define MQTTProperties_initializer {0, 0, 0, NULL}
|
||||
|
||||
/**
|
||||
* Returns the length of the properties structure when serialized ready for network transmission.
|
||||
* @param props an MQTT V5 property structure.
|
||||
* @return the length in bytes of the properties when serialized.
|
||||
*/
|
||||
int MQTTProperties_len(MQTTProperties* props);
|
||||
|
||||
/**
|
||||
* Add a property pointer to the property array. Memory is allocated in this function,
|
||||
* so MQTTClient_create or MQTTAsync_create must be called first to initialize the
|
||||
* internal heap tracking. Alternatively MQTTAsync_global_init() can be called first
|
||||
* or build with the HIGH_PERFORMANCE option which disables the heap tracking.
|
||||
* @param props The property list to add the property to.
|
||||
* @param prop The property to add to the list.
|
||||
* @return 0 on success, -1 on failure.
|
||||
*/
|
||||
LIBMQTT_API int MQTTProperties_add(MQTTProperties* props, const MQTTProperty* prop);
|
||||
|
||||
/**
|
||||
* Serialize the given property list to a character buffer, e.g. for writing to the network.
|
||||
* @param pptr pointer to the buffer - move the pointer as we add data
|
||||
* @param properties pointer to the property list, can be NULL
|
||||
* @return whether the write succeeded or not: number of bytes written, or < 0 on failure.
|
||||
*/
|
||||
int MQTTProperties_write(char** pptr, const MQTTProperties* properties);
|
||||
|
||||
/**
|
||||
* Reads a property list from a character buffer into an array.
|
||||
* @param properties pointer to the property list to be filled. Should be initalized but empty.
|
||||
* @param pptr pointer to the character buffer.
|
||||
* @param enddata pointer to the end of the character buffer so we don't read beyond.
|
||||
* @return 1 if the properties were read successfully.
|
||||
*/
|
||||
int MQTTProperties_read(MQTTProperties* properties, char** pptr, char* enddata);
|
||||
|
||||
/**
|
||||
* Free all memory allocated to the property list, including any to individual properties.
|
||||
* @param properties pointer to the property list.
|
||||
*/
|
||||
LIBMQTT_API void MQTTProperties_free(MQTTProperties* properties);
|
||||
|
||||
/**
|
||||
* Copy the contents of a property list, allocating additional memory if needed.
|
||||
* @param props pointer to the property list.
|
||||
* @return the duplicated property list.
|
||||
*/
|
||||
LIBMQTT_API MQTTProperties MQTTProperties_copy(const MQTTProperties* props);
|
||||
|
||||
/**
|
||||
* Checks if property list contains a specific property.
|
||||
* @param props pointer to the property list.
|
||||
* @param propid the property id to check for.
|
||||
* @return 1 if found, 0 if not.
|
||||
*/
|
||||
LIBMQTT_API int MQTTProperties_hasProperty(MQTTProperties *props, enum MQTTPropertyCodes propid);
|
||||
|
||||
/**
|
||||
* Returns the number of instances of a property id. Most properties can exist only once.
|
||||
* User properties and subscription ids can exist more than once.
|
||||
* @param props pointer to the property list.
|
||||
* @param propid the property id to check for.
|
||||
* @return the number of times found. Can be 0.
|
||||
*/
|
||||
LIBMQTT_API int MQTTProperties_propertyCount(MQTTProperties *props, enum MQTTPropertyCodes propid);
|
||||
|
||||
/**
|
||||
* Returns the integer value of a specific property. The property given must be a numeric type.
|
||||
* @param props pointer to the property list.
|
||||
* @param propid the property id to check for.
|
||||
* @return the integer value of the property. -9999999 on failure.
|
||||
*/
|
||||
LIBMQTT_API int MQTTProperties_getNumericValue(MQTTProperties *props, enum MQTTPropertyCodes propid);
|
||||
|
||||
/**
|
||||
* Returns the integer value of a specific property when it's not the only instance.
|
||||
* The property given must be a numeric type.
|
||||
* @param props pointer to the property list.
|
||||
* @param propid the property id to check for.
|
||||
* @param index the instance number, starting at 0.
|
||||
* @return the integer value of the property. -9999999 on failure.
|
||||
*/
|
||||
LIBMQTT_API int MQTTProperties_getNumericValueAt(MQTTProperties *props, enum MQTTPropertyCodes propid, int index);
|
||||
|
||||
/**
|
||||
* Returns a pointer to the property structure for a specific property.
|
||||
* @param props pointer to the property list.
|
||||
* @param propid the property id to check for.
|
||||
* @return the pointer to the property structure if found. NULL if not found.
|
||||
*/
|
||||
LIBMQTT_API MQTTProperty* MQTTProperties_getProperty(MQTTProperties *props, enum MQTTPropertyCodes propid);
|
||||
|
||||
/**
|
||||
* Returns a pointer to the property structure for a specific property when it's not the only instance.
|
||||
* @param props pointer to the property list.
|
||||
* @param propid the property id to check for.
|
||||
* @param index the instance number, starting at 0.
|
||||
* @return the pointer to the property structure if found. NULL if not found.
|
||||
*/
|
||||
LIBMQTT_API MQTTProperty* MQTTProperties_getPropertyAt(MQTTProperties *props, enum MQTTPropertyCodes propid, int index);
|
||||
|
||||
#endif /* MQTTPROPERTIES_H */
|
||||
@@ -0,0 +1,46 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2009, 2022 IBM Corp., Ian Craggs
|
||||
*
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v2.0
|
||||
* and Eclipse Distribution License v1.0 which accompany this distribution.
|
||||
*
|
||||
* The Eclipse Public License is available at
|
||||
* https://www.eclipse.org/legal/epl-2.0/
|
||||
* and the Eclipse Distribution License is available at
|
||||
* http://www.eclipse.org/org/documents/edl-v10.php.
|
||||
*
|
||||
* Contributors:
|
||||
* Ian Craggs - initial API and implementation and/or initial documentation
|
||||
* Ian Craggs - MQTT 3.1.1 updates
|
||||
*******************************************************************************/
|
||||
|
||||
#if !defined(MQTTPROTOCOL_H)
|
||||
#define MQTTPROTOCOL_H
|
||||
|
||||
#include "LinkedList.h"
|
||||
#include "MQTTPacket.h"
|
||||
#include "Clients.h"
|
||||
|
||||
#define MAX_MSG_ID 65535
|
||||
#define MAX_CLIENTID_LEN 65535
|
||||
|
||||
typedef struct
|
||||
{
|
||||
SOCKET socket;
|
||||
Publications* p;
|
||||
} pending_write;
|
||||
|
||||
|
||||
typedef struct
|
||||
{
|
||||
List publications;
|
||||
unsigned int msgs_received;
|
||||
unsigned int msgs_sent;
|
||||
List pending_writes; /* for qos 0 writes not complete */
|
||||
} MQTTProtocol;
|
||||
|
||||
|
||||
#include "MQTTProtocolOut.h"
|
||||
|
||||
#endif
|
||||
+1114
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,62 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2009, 2022 IBM Corp.
|
||||
*
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v2.0
|
||||
* and Eclipse Distribution License v1.0 which accompany this distribution.
|
||||
*
|
||||
* The Eclipse Public License is available at
|
||||
* https://www.eclipse.org/legal/epl-2.0/
|
||||
* and the Eclipse Distribution License is available at
|
||||
* http://www.eclipse.org/org/documents/edl-v10.php.
|
||||
*
|
||||
* Contributors:
|
||||
* Ian Craggs - initial API and implementation and/or initial documentation
|
||||
* Ian Craggs, Allan Stockdill-Mander - SSL updates
|
||||
* Ian Craggs - MQTT 3.1.1 updates
|
||||
* Rong Xiang, Ian Craggs - C++ compatibility
|
||||
* Ian Craggs - add debug definition of MQTTStrdup for when needed
|
||||
*******************************************************************************/
|
||||
|
||||
#if !defined(MQTTPROTOCOLCLIENT_H)
|
||||
#define MQTTPROTOCOLCLIENT_H
|
||||
|
||||
#include "LinkedList.h"
|
||||
#include "MQTTPacket.h"
|
||||
#include "Log.h"
|
||||
#include "MQTTProtocol.h"
|
||||
#include "Messages.h"
|
||||
#include "MQTTProperties.h"
|
||||
|
||||
#define MAX_MSG_ID 65535
|
||||
#define MAX_CLIENTID_LEN 65535
|
||||
|
||||
int MQTTProtocol_startPublish(Clients* pubclient, Publish* publish, int qos, int retained, Messages** m);
|
||||
Messages* MQTTProtocol_createMessage(Publish* publish, Messages** mm, int qos, int retained, int allocatePayload);
|
||||
Publications* MQTTProtocol_storePublication(Publish* publish, int* len);
|
||||
int messageIDCompare(void* a, void* b);
|
||||
int MQTTProtocol_assignMsgId(Clients* client);
|
||||
void MQTTProtocol_removePublication(Publications* p);
|
||||
void Protocol_processPublication(Publish* publish, Clients* client, int allocatePayload);
|
||||
|
||||
int MQTTProtocol_handlePublishes(void* pack, SOCKET sock);
|
||||
int MQTTProtocol_handlePubacks(void* pack, SOCKET sock, Publications** pubToRemove);
|
||||
int MQTTProtocol_handlePubrecs(void* pack, SOCKET sock, Publications** pubToRemove);
|
||||
int MQTTProtocol_handlePubrels(void* pack, SOCKET sock);
|
||||
int MQTTProtocol_handlePubcomps(void* pack, SOCKET sock, Publications** pubToRemove);
|
||||
|
||||
void MQTTProtocol_closeSession(Clients* c, int sendwill);
|
||||
void MQTTProtocol_keepalive(START_TIME_TYPE);
|
||||
void MQTTProtocol_retry(START_TIME_TYPE, int, int);
|
||||
void MQTTProtocol_freeClient(Clients* client);
|
||||
void MQTTProtocol_emptyMessageList(List* msgList);
|
||||
void MQTTProtocol_freeMessageList(List* msgList);
|
||||
|
||||
char* MQTTStrncpy(char *dest, const char* src, size_t num);
|
||||
char* MQTTStrdup(const char* src);
|
||||
|
||||
void MQTTProtocol_writeAvailable(SOCKET socket);
|
||||
|
||||
//#define MQTTStrdup(src) MQTTStrncpy(malloc(strlen(src)+1), src, strlen(src)+1)
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,479 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2009, 2023 IBM Corp., Ian Craggs and others
|
||||
*
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v2.0
|
||||
* and Eclipse Distribution License v1.0 which accompany this distribution.
|
||||
*
|
||||
* The Eclipse Public License is available at
|
||||
* https://www.eclipse.org/legal/epl-2.0/
|
||||
* and the Eclipse Distribution License is available at
|
||||
* http://www.eclipse.org/org/documents/edl-v10.php.
|
||||
*
|
||||
* Contributors:
|
||||
* Ian Craggs - initial API and implementation and/or initial documentation
|
||||
* Ian Craggs, Allan Stockdill-Mander - SSL updates
|
||||
* Ian Craggs - fix for buffer overflow in addressPort bug #433290
|
||||
* Ian Craggs - MQTT 3.1.1 support
|
||||
* Rong Xiang, Ian Craggs - C++ compatibility
|
||||
* Ian Craggs - fix for bug 479376
|
||||
* Ian Craggs - SNI support
|
||||
* Ian Craggs - fix for issue #164
|
||||
* Ian Craggs - fix for issue #179
|
||||
* Ian Craggs - MQTT 5.0 support
|
||||
* Sven Gambel - add generic proxy support
|
||||
*******************************************************************************/
|
||||
|
||||
/**
|
||||
* @file
|
||||
* \brief Functions dealing with the MQTT protocol exchanges
|
||||
*
|
||||
* Some other related functions are in the MQTTProtocolClient module
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
|
||||
#include "MQTTProtocolOut.h"
|
||||
#include "StackTrace.h"
|
||||
#include "Heap.h"
|
||||
#include "WebSocket.h"
|
||||
#include "Proxy.h"
|
||||
#include "Base64.h"
|
||||
|
||||
extern ClientStates* bstate;
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Separates an address:port into two separate values
|
||||
* @param[in] uri the input string - hostname:port
|
||||
* @param[out] port the returned port integer
|
||||
* @param[out] topic optional topic portion of the address starting with '/'
|
||||
* @return the address string
|
||||
*/
|
||||
size_t MQTTProtocol_addressPort(const char* uri, int* port, const char **topic, int default_port)
|
||||
{
|
||||
char* buf = (char*)uri;
|
||||
char* colon_pos;
|
||||
size_t len;
|
||||
char* topic_pos;
|
||||
|
||||
FUNC_ENTRY;
|
||||
colon_pos = strrchr(uri, ':'); /* reverse find to allow for ':' in IPv6 addresses */
|
||||
|
||||
if (uri[0] == '[')
|
||||
{ /* ip v6 */
|
||||
if (colon_pos < strrchr(uri, ']'))
|
||||
colon_pos = NULL; /* means it was an IPv6 separator, not for host:port */
|
||||
}
|
||||
|
||||
if (colon_pos) /* have to strip off the port */
|
||||
{
|
||||
len = colon_pos - uri;
|
||||
*port = atoi(colon_pos + 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
len = strlen(buf);
|
||||
*port = default_port;
|
||||
}
|
||||
|
||||
/* find any topic portion */
|
||||
topic_pos = (char*)uri;
|
||||
if (colon_pos)
|
||||
topic_pos = colon_pos;
|
||||
topic_pos = strchr(topic_pos, '/');
|
||||
if (topic_pos)
|
||||
{
|
||||
if (topic)
|
||||
*topic = topic_pos;
|
||||
if (!colon_pos)
|
||||
len = topic_pos - uri;
|
||||
}
|
||||
|
||||
if (buf[len - 1] == ']')
|
||||
{
|
||||
/* we are stripping off the final ], so length is 1 shorter */
|
||||
--len;
|
||||
}
|
||||
FUNC_EXIT;
|
||||
return len;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Allow user or password characters to be expressed in the form of %XX, XX being the
|
||||
* hexadecimal value of the character. This will avoid problems when a user code or a password
|
||||
* contains a '@' or another special character ('%' included)
|
||||
* @param p0 output string
|
||||
* @param p1 input string
|
||||
* @param basic_auth_in_len
|
||||
*/
|
||||
void MQTTProtocol_specialChars(char* p0, char* p1, b64_size_t *basic_auth_in_len)
|
||||
{
|
||||
while (*p1 != '@')
|
||||
{
|
||||
if (*p1 != '%')
|
||||
{
|
||||
*p0++ = *p1++;
|
||||
}
|
||||
else if (isxdigit(*(p1 + 1)) && isxdigit(*(p1 + 2)))
|
||||
{
|
||||
/* next 2 characters are hexa digits */
|
||||
char hex[3];
|
||||
p1++;
|
||||
hex[0] = *p1++;
|
||||
hex[1] = *p1++;
|
||||
hex[2] = '\0';
|
||||
*p0++ = (char)strtol(hex, 0, 16);
|
||||
/* 3 input char => 1 output char */
|
||||
*basic_auth_in_len -= 2;
|
||||
}
|
||||
}
|
||||
*p0 = 0x0;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Examples of proxy settings:
|
||||
* http://your.proxy.server:8080/
|
||||
* http://user:pass@my.proxy.server:8080/
|
||||
*/
|
||||
int MQTTProtocol_setHTTPProxy(Clients* aClient, char* source, char** dest, char** auth_dest, char* prefix)
|
||||
{
|
||||
b64_size_t basic_auth_in_len, basic_auth_out_len;
|
||||
b64_data_t *basic_auth;
|
||||
char *p1;
|
||||
int rc = 0;
|
||||
|
||||
if (*auth_dest)
|
||||
{
|
||||
free(*auth_dest);
|
||||
*auth_dest = NULL;
|
||||
}
|
||||
|
||||
if (source)
|
||||
{
|
||||
if ((p1 = strstr(source, prefix)) != NULL) /* skip http:// prefix, if any */
|
||||
source += strlen(prefix);
|
||||
*dest = source;
|
||||
if ((p1 = strchr(source, '@')) != NULL) /* find user.pass separator */
|
||||
*dest = p1 + 1;
|
||||
|
||||
if (p1)
|
||||
{
|
||||
/* basic auth len is string between http:// and @ */
|
||||
basic_auth_in_len = (b64_size_t)(p1 - source);
|
||||
if (basic_auth_in_len > 0)
|
||||
{
|
||||
basic_auth = (b64_data_t *)malloc(sizeof(char)*(basic_auth_in_len+1));
|
||||
if (!basic_auth)
|
||||
{
|
||||
rc = PAHO_MEMORY_ERROR;
|
||||
goto exit;
|
||||
}
|
||||
MQTTProtocol_specialChars((char*)basic_auth, source, &basic_auth_in_len);
|
||||
basic_auth_out_len = Base64_encodeLength(basic_auth, basic_auth_in_len) + 1; /* add 1 for trailing NULL */
|
||||
if ((*auth_dest = (char *)malloc(sizeof(char)*basic_auth_out_len)) == NULL)
|
||||
{
|
||||
free(basic_auth);
|
||||
rc = PAHO_MEMORY_ERROR;
|
||||
goto exit;
|
||||
}
|
||||
Base64_encode(*auth_dest, basic_auth_out_len, basic_auth, basic_auth_in_len);
|
||||
free(basic_auth);
|
||||
}
|
||||
}
|
||||
}
|
||||
exit:
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* MQTT outgoing connect processing for a client
|
||||
* @param ip_address the TCP address:port to connect to
|
||||
* @param aClient a structure with all MQTT data needed
|
||||
* @param int ssl
|
||||
* @param int MQTTVersion the MQTT version to connect with (3 or 4)
|
||||
* @param long timeout how long to wait for a new socket to be created
|
||||
* @return return code
|
||||
*/
|
||||
#if defined(OPENSSL)
|
||||
#if defined(__GNUC__) && defined(__linux__)
|
||||
int MQTTProtocol_connect(const char* ip_address, Clients* aClient, int ssl, int websocket, int MQTTVersion,
|
||||
MQTTProperties* connectProperties, MQTTProperties* willProperties, long timeout)
|
||||
#else
|
||||
int MQTTProtocol_connect(const char* ip_address, Clients* aClient, int ssl, int websocket, int MQTTVersion,
|
||||
MQTTProperties* connectProperties, MQTTProperties* willProperties)
|
||||
#endif
|
||||
#else
|
||||
#if defined(__GNUC__) && defined(__linux__)
|
||||
int MQTTProtocol_connect(const char* ip_address, Clients* aClient, int websocket, int MQTTVersion,
|
||||
MQTTProperties* connectProperties, MQTTProperties* willProperties, long timeout)
|
||||
#else
|
||||
int MQTTProtocol_connect(const char* ip_address, Clients* aClient, int websocket, int MQTTVersion,
|
||||
MQTTProperties* connectProperties, MQTTProperties* willProperties)
|
||||
#endif
|
||||
#endif
|
||||
{
|
||||
int rc = 0,
|
||||
port;
|
||||
size_t addr_len;
|
||||
char* p0;
|
||||
|
||||
FUNC_ENTRY;
|
||||
aClient->good = 1;
|
||||
|
||||
if (aClient->httpProxy)
|
||||
p0 = aClient->httpProxy;
|
||||
else
|
||||
p0 = getenv("http_proxy");
|
||||
|
||||
if (p0)
|
||||
{
|
||||
if ((rc = MQTTProtocol_setHTTPProxy(aClient, p0, &aClient->net.http_proxy, &aClient->net.http_proxy_auth, "http://")) != 0)
|
||||
goto exit;
|
||||
Log(TRACE_PROTOCOL, -1, "Setting http proxy to %s", aClient->net.http_proxy);
|
||||
if (aClient->net.http_proxy_auth)
|
||||
Log(TRACE_PROTOCOL, -1, "Setting http proxy auth to %s", aClient->net.http_proxy_auth);
|
||||
}
|
||||
|
||||
#if defined(OPENSSL)
|
||||
if (aClient->httpsProxy)
|
||||
p0 = aClient->httpsProxy;
|
||||
else
|
||||
p0 = getenv("https_proxy");
|
||||
|
||||
if (p0)
|
||||
{
|
||||
if ((rc = MQTTProtocol_setHTTPProxy(aClient, p0, &aClient->net.https_proxy, &aClient->net.https_proxy_auth, "https://")) != 0)
|
||||
goto exit;
|
||||
Log(TRACE_PROTOCOL, -1, "Setting https proxy to %s", aClient->net.https_proxy);
|
||||
if (aClient->net.https_proxy_auth)
|
||||
Log(TRACE_PROTOCOL, -1, "Setting https proxy auth to %s", aClient->net.https_proxy_auth);
|
||||
}
|
||||
|
||||
if (!ssl && aClient->net.http_proxy) {
|
||||
#else
|
||||
if (aClient->net.http_proxy) {
|
||||
#endif
|
||||
addr_len = MQTTProtocol_addressPort(aClient->net.http_proxy, &port, NULL, PROXY_DEFAULT_PORT);
|
||||
#if defined(__GNUC__) && defined(__linux__)
|
||||
if (timeout < 0)
|
||||
rc = -1;
|
||||
else
|
||||
rc = Socket_new(aClient->net.http_proxy, addr_len, port, &(aClient->net.socket), timeout);
|
||||
#else
|
||||
rc = Socket_new(aClient->net.http_proxy, addr_len, port, &(aClient->net.socket));
|
||||
#endif
|
||||
}
|
||||
#if defined(OPENSSL)
|
||||
else if (ssl && aClient->net.https_proxy) {
|
||||
addr_len = MQTTProtocol_addressPort(aClient->net.https_proxy, &port, NULL, PROXY_DEFAULT_PORT);
|
||||
#if defined(__GNUC__) && defined(__linux__)
|
||||
if (timeout < 0)
|
||||
rc = -1;
|
||||
else
|
||||
rc = Socket_new(aClient->net.https_proxy, addr_len, port, &(aClient->net.socket), timeout);
|
||||
#else
|
||||
rc = Socket_new(aClient->net.https_proxy, addr_len, port, &(aClient->net.socket));
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
else {
|
||||
#if defined(OPENSSL)
|
||||
addr_len = MQTTProtocol_addressPort(ip_address, &port, NULL, ssl ?
|
||||
(websocket ? WSS_DEFAULT_PORT : SECURE_MQTT_DEFAULT_PORT) :
|
||||
(websocket ? WS_DEFAULT_PORT : MQTT_DEFAULT_PORT) );
|
||||
#else
|
||||
addr_len = MQTTProtocol_addressPort(ip_address, &port, NULL, websocket ? WS_DEFAULT_PORT : MQTT_DEFAULT_PORT);
|
||||
#endif
|
||||
#if defined(__GNUC__) && defined(__linux__)
|
||||
if (timeout < 0)
|
||||
rc = -1;
|
||||
else
|
||||
rc = Socket_new(ip_address, addr_len, port, &(aClient->net.socket), timeout);
|
||||
#else
|
||||
rc = Socket_new(ip_address, addr_len, port, &(aClient->net.socket));
|
||||
#endif
|
||||
}
|
||||
if (rc == EINPROGRESS || rc == EWOULDBLOCK)
|
||||
aClient->connect_state = TCP_IN_PROGRESS; /* TCP connect called - wait for connect completion */
|
||||
else if (rc == 0)
|
||||
{ /* TCP connect completed. If SSL, send SSL connect */
|
||||
#if defined(OPENSSL)
|
||||
if (ssl)
|
||||
{
|
||||
if (aClient->net.https_proxy) {
|
||||
aClient->connect_state = PROXY_CONNECT_IN_PROGRESS;
|
||||
rc = Proxy_connect( &aClient->net, 1, ip_address);
|
||||
}
|
||||
if (rc == 0 && SSLSocket_setSocketForSSL(&aClient->net, aClient->sslopts, ip_address, addr_len) == 1)
|
||||
{
|
||||
rc = aClient->sslopts->struct_version >= 3 ?
|
||||
SSLSocket_connect(aClient->net.ssl, aClient->net.socket, ip_address,
|
||||
aClient->sslopts->verify, aClient->sslopts->ssl_error_cb, aClient->sslopts->ssl_error_context) :
|
||||
SSLSocket_connect(aClient->net.ssl, aClient->net.socket, ip_address,
|
||||
aClient->sslopts->verify, NULL, NULL);
|
||||
if (rc == TCPSOCKET_INTERRUPTED)
|
||||
aClient->connect_state = SSL_IN_PROGRESS; /* SSL connect called - wait for completion */
|
||||
}
|
||||
else
|
||||
rc = SOCKET_ERROR;
|
||||
}
|
||||
else if (aClient->net.http_proxy) {
|
||||
#else
|
||||
if (aClient->net.http_proxy) {
|
||||
#endif
|
||||
aClient->connect_state = PROXY_CONNECT_IN_PROGRESS;
|
||||
rc = Proxy_connect( &aClient->net, 0, ip_address);
|
||||
}
|
||||
if ( websocket )
|
||||
{
|
||||
#if defined(OPENSSL)
|
||||
rc = WebSocket_connect(&aClient->net, ssl, ip_address);
|
||||
#else
|
||||
rc = WebSocket_connect(&aClient->net, 0, ip_address);
|
||||
#endif
|
||||
if ( rc == TCPSOCKET_INTERRUPTED )
|
||||
aClient->connect_state = WEBSOCKET_IN_PROGRESS; /* Websocket connect called - wait for completion */
|
||||
}
|
||||
if (rc == 0)
|
||||
{
|
||||
/* Now send the MQTT connect packet */
|
||||
if ((rc = MQTTPacket_send_connect(aClient, MQTTVersion, connectProperties, willProperties)) == 0)
|
||||
aClient->connect_state = WAIT_FOR_CONNACK; /* MQTT Connect sent - wait for CONNACK */
|
||||
else
|
||||
aClient->connect_state = NOT_IN_PROGRESS;
|
||||
}
|
||||
}
|
||||
|
||||
exit:
|
||||
FUNC_EXIT_RC(rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Process an incoming pingresp packet for a socket
|
||||
* @param pack pointer to the publish packet
|
||||
* @param sock the socket on which the packet was received
|
||||
* @return completion code
|
||||
*/
|
||||
int MQTTProtocol_handlePingresps(void* pack, SOCKET sock)
|
||||
{
|
||||
Clients* client = NULL;
|
||||
int rc = TCPSOCKET_COMPLETE;
|
||||
|
||||
FUNC_ENTRY;
|
||||
client = (Clients*)(ListFindItem(bstate->clients, &sock, clientSocketCompare)->content);
|
||||
Log(LOG_PROTOCOL, 21, NULL, sock, client->clientID);
|
||||
client->ping_outstanding = 0;
|
||||
FUNC_EXIT_RC(rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* MQTT outgoing subscribe processing for a client
|
||||
* @param client the client structure
|
||||
* @param topics list of topics
|
||||
* @param qoss corresponding list of QoSs
|
||||
* @param opts MQTT 5.0 subscribe options
|
||||
* @param props MQTT 5.0 subscribe properties
|
||||
* @return completion code
|
||||
*/
|
||||
int MQTTProtocol_subscribe(Clients* client, List* topics, List* qoss, int msgID,
|
||||
MQTTSubscribe_options* opts, MQTTProperties* props)
|
||||
{
|
||||
int rc = 0;
|
||||
|
||||
FUNC_ENTRY;
|
||||
rc = MQTTPacket_send_subscribe(topics, qoss, opts, props, msgID, 0, client);
|
||||
FUNC_EXIT_RC(rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Process an incoming suback packet for a socket
|
||||
* @param pack pointer to the publish packet
|
||||
* @param sock the socket on which the packet was received
|
||||
* @return completion code
|
||||
*/
|
||||
int MQTTProtocol_handleSubacks(void* pack, SOCKET sock)
|
||||
{
|
||||
Suback* suback = (Suback*)pack;
|
||||
Clients* client = NULL;
|
||||
int rc = TCPSOCKET_COMPLETE;
|
||||
|
||||
FUNC_ENTRY;
|
||||
client = (Clients*)(ListFindItem(bstate->clients, &sock, clientSocketCompare)->content);
|
||||
Log(LOG_PROTOCOL, 23, NULL, sock, client->clientID, suback->msgId);
|
||||
MQTTPacket_freeSuback(suback);
|
||||
FUNC_EXIT_RC(rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* MQTT outgoing unsubscribe processing for a client
|
||||
* @param client the client structure
|
||||
* @param topics list of topics
|
||||
* @return completion code
|
||||
*/
|
||||
int MQTTProtocol_unsubscribe(Clients* client, List* topics, int msgID, MQTTProperties* props)
|
||||
{
|
||||
int rc = 0;
|
||||
|
||||
FUNC_ENTRY;
|
||||
rc = MQTTPacket_send_unsubscribe(topics, props, msgID, 0, client);
|
||||
FUNC_EXIT_RC(rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Process an incoming unsuback packet for a socket
|
||||
* @param pack pointer to the publish packet
|
||||
* @param sock the socket on which the packet was received
|
||||
* @return completion code
|
||||
*/
|
||||
int MQTTProtocol_handleUnsubacks(void* pack, SOCKET sock)
|
||||
{
|
||||
Unsuback* unsuback = (Unsuback*)pack;
|
||||
Clients* client = NULL;
|
||||
int rc = TCPSOCKET_COMPLETE;
|
||||
|
||||
FUNC_ENTRY;
|
||||
client = (Clients*)(ListFindItem(bstate->clients, &sock, clientSocketCompare)->content);
|
||||
Log(LOG_PROTOCOL, 24, NULL, sock, client->clientID, unsuback->msgId);
|
||||
MQTTPacket_freeUnsuback(unsuback);
|
||||
FUNC_EXIT_RC(rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Process an incoming disconnect packet for a socket
|
||||
* @param pack pointer to the disconnect packet
|
||||
* @param sock the socket on which the packet was received
|
||||
* @return completion code
|
||||
*/
|
||||
int MQTTProtocol_handleDisconnects(void* pack, SOCKET sock)
|
||||
{
|
||||
Ack* disconnect = (Ack*)pack;
|
||||
Clients* client = NULL;
|
||||
int rc = TCPSOCKET_COMPLETE;
|
||||
|
||||
FUNC_ENTRY;
|
||||
client = (Clients*)(ListFindItem(bstate->clients, &sock, clientSocketCompare)->content);
|
||||
Log(LOG_PROTOCOL, 30, NULL, sock, client->clientID, disconnect->rc);
|
||||
MQTTPacket_freeAck(disconnect);
|
||||
FUNC_EXIT_RC(rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,66 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2009, 2022 IBM Corp., Ian Craggs, and others
|
||||
*
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v2.0
|
||||
* and Eclipse Distribution License v1.0 which accompany this distribution.
|
||||
*
|
||||
* The Eclipse Public License is available at
|
||||
* https://www.eclipse.org/legal/epl-2.0/
|
||||
* and the Eclipse Distribution License is available at
|
||||
* http://www.eclipse.org/org/documents/edl-v10.php.
|
||||
*
|
||||
* Contributors:
|
||||
* Ian Craggs - initial API and implementation and/or initial documentation
|
||||
* Ian Craggs, Allan Stockdill-Mander - SSL updates
|
||||
* Ian Craggs - MQTT 3.1.1 support
|
||||
* Ian Craggs - SNI support
|
||||
* Ian Craggs - MQTT 5.0 support
|
||||
* Sven Gambel - add generic proxy support
|
||||
*******************************************************************************/
|
||||
|
||||
#if !defined(MQTTPROTOCOLOUT_H)
|
||||
#define MQTTPROTOCOLOUT_H
|
||||
|
||||
#include "LinkedList.h"
|
||||
#include "MQTTPacket.h"
|
||||
#include "Clients.h"
|
||||
#include "Log.h"
|
||||
#include "Messages.h"
|
||||
#include "MQTTProtocol.h"
|
||||
#include "MQTTProtocolClient.h"
|
||||
|
||||
#define MQTT_DEFAULT_PORT 1883
|
||||
#define SECURE_MQTT_DEFAULT_PORT 8883
|
||||
#define WS_DEFAULT_PORT 80
|
||||
#define WSS_DEFAULT_PORT 443
|
||||
#define PROXY_DEFAULT_PORT 8080
|
||||
|
||||
size_t MQTTProtocol_addressPort(const char* uri, int* port, const char **topic, int default_port);
|
||||
void MQTTProtocol_reconnect(const char* ip_address, Clients* client);
|
||||
#if defined(OPENSSL)
|
||||
#if defined(__GNUC__) && defined(__linux__)
|
||||
int MQTTProtocol_connect(const char* ip_address, Clients* acClients, int ssl, int websocket, int MQTTVersion,
|
||||
MQTTProperties* connectProperties, MQTTProperties* willProperties, long timeout);
|
||||
#else
|
||||
int MQTTProtocol_connect(const char* ip_address, Clients* acClients, int ssl, int websocket, int MQTTVersion,
|
||||
MQTTProperties* connectProperties, MQTTProperties* willProperties);
|
||||
#endif
|
||||
#else
|
||||
#if defined(__GNUC__) && defined(__linux__)
|
||||
int MQTTProtocol_connect(const char* ip_address, Clients* acClients, int websocket, int MQTTVersion,
|
||||
MQTTProperties* connectProperties, MQTTProperties* willProperties, long timeout);
|
||||
#else
|
||||
int MQTTProtocol_connect(const char* ip_address, Clients* acClients, int websocket, int MQTTVersion,
|
||||
MQTTProperties* connectProperties, MQTTProperties* willProperties);
|
||||
#endif
|
||||
#endif
|
||||
int MQTTProtocol_handlePingresps(void* pack, SOCKET sock);
|
||||
int MQTTProtocol_subscribe(Clients* client, List* topics, List* qoss, int msgID,
|
||||
MQTTSubscribe_options* opts, MQTTProperties* props);
|
||||
int MQTTProtocol_handleSubacks(void* pack, SOCKET sock);
|
||||
int MQTTProtocol_unsubscribe(Clients* client, List* topics, int msgID, MQTTProperties* props);
|
||||
int MQTTProtocol_handleUnsubacks(void* pack, SOCKET sock);
|
||||
int MQTTProtocol_handleDisconnects(void* pack, SOCKET sock);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,101 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2017, 2018 IBM Corp.
|
||||
*
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v2.0
|
||||
* and Eclipse Distribution License v1.0 which accompany this distribution.
|
||||
*
|
||||
* The Eclipse Public License is available at
|
||||
* https://www.eclipse.org/legal/epl-2.0/
|
||||
* and the Eclipse Distribution License is available at
|
||||
* http://www.eclipse.org/org/documents/edl-v10.php.
|
||||
*
|
||||
* Contributors:
|
||||
* Ian Craggs - initial API and implementation and/or initial documentation
|
||||
*******************************************************************************/
|
||||
|
||||
#include "MQTTReasonCodes.h"
|
||||
|
||||
#include "MQTTPacket.h"
|
||||
#include "Heap.h"
|
||||
#include "StackTrace.h"
|
||||
|
||||
#include <memory.h>
|
||||
|
||||
#define ARRAY_SIZE(a) (sizeof(a) / sizeof(a[0]))
|
||||
|
||||
static struct {
|
||||
enum MQTTReasonCodes value;
|
||||
const char* name;
|
||||
} nameToString[] =
|
||||
{
|
||||
{MQTTREASONCODE_SUCCESS, "SUCCESS"},
|
||||
{MQTTREASONCODE_NORMAL_DISCONNECTION, "Normal disconnection"},
|
||||
{MQTTREASONCODE_GRANTED_QOS_0, "Granted QoS 0"},
|
||||
{MQTTREASONCODE_GRANTED_QOS_1, "Granted QoS 1"},
|
||||
{MQTTREASONCODE_GRANTED_QOS_2, "Granted QoS 2"},
|
||||
{MQTTREASONCODE_DISCONNECT_WITH_WILL_MESSAGE, "Disconnect with Will Message"},
|
||||
{MQTTREASONCODE_NO_MATCHING_SUBSCRIBERS, "No matching subscribers"},
|
||||
{MQTTREASONCODE_NO_SUBSCRIPTION_FOUND, "No subscription found"},
|
||||
{MQTTREASONCODE_CONTINUE_AUTHENTICATION, "Continue authentication"},
|
||||
{MQTTREASONCODE_RE_AUTHENTICATE, "Re-authenticate"},
|
||||
{MQTTREASONCODE_UNSPECIFIED_ERROR, "Unspecified error"},
|
||||
{MQTTREASONCODE_MALFORMED_PACKET, "Malformed Packet"},
|
||||
{MQTTREASONCODE_PROTOCOL_ERROR, "Protocol error"},
|
||||
{MQTTREASONCODE_IMPLEMENTATION_SPECIFIC_ERROR, "Implementation specific error"},
|
||||
{MQTTREASONCODE_UNSUPPORTED_PROTOCOL_VERSION, "Unsupported Protocol Version"},
|
||||
{MQTTREASONCODE_CLIENT_IDENTIFIER_NOT_VALID, "Client Identifier not valid"},
|
||||
{MQTTREASONCODE_BAD_USER_NAME_OR_PASSWORD, "Bad User Name or Password"},
|
||||
{MQTTREASONCODE_NOT_AUTHORIZED, "Not authorized"},
|
||||
{MQTTREASONCODE_SERVER_UNAVAILABLE, "Server unavailable"},
|
||||
{MQTTREASONCODE_SERVER_BUSY, "Server busy"},
|
||||
{MQTTREASONCODE_BANNED, "Banned"},
|
||||
{MQTTREASONCODE_SERVER_SHUTTING_DOWN, "Server shutting down"},
|
||||
{MQTTREASONCODE_BAD_AUTHENTICATION_METHOD, "Bad authentication method"},
|
||||
{MQTTREASONCODE_KEEP_ALIVE_TIMEOUT, "Keep Alive timeout"},
|
||||
{MQTTREASONCODE_SESSION_TAKEN_OVER, "Session taken over"},
|
||||
{MQTTREASONCODE_TOPIC_FILTER_INVALID, "Topic filter invalid"},
|
||||
{MQTTREASONCODE_TOPIC_NAME_INVALID, "Topic name invalid"},
|
||||
{MQTTREASONCODE_PACKET_IDENTIFIER_IN_USE, "Packet Identifier in use"},
|
||||
{MQTTREASONCODE_PACKET_IDENTIFIER_NOT_FOUND, "Packet Identifier not found"},
|
||||
{MQTTREASONCODE_RECEIVE_MAXIMUM_EXCEEDED, "Receive Maximum exceeded"},
|
||||
{MQTTREASONCODE_TOPIC_ALIAS_INVALID, "Topic Alias invalid"},
|
||||
{MQTTREASONCODE_PACKET_TOO_LARGE, "Packet too large"},
|
||||
{MQTTREASONCODE_MESSAGE_RATE_TOO_HIGH, "Message rate too high"},
|
||||
{MQTTREASONCODE_QUOTA_EXCEEDED, "Quota exceeded"},
|
||||
{MQTTREASONCODE_ADMINISTRATIVE_ACTION, "Administrative action"},
|
||||
{MQTTREASONCODE_PAYLOAD_FORMAT_INVALID, "Payload format invalid"},
|
||||
{MQTTREASONCODE_RETAIN_NOT_SUPPORTED, "Retain not supported"},
|
||||
{MQTTREASONCODE_QOS_NOT_SUPPORTED, "QoS not supported"},
|
||||
{MQTTREASONCODE_USE_ANOTHER_SERVER, "Use another server"},
|
||||
{MQTTREASONCODE_SERVER_MOVED, "Server moved"},
|
||||
{MQTTREASONCODE_SHARED_SUBSCRIPTIONS_NOT_SUPPORTED, "Shared subscriptions not supported"},
|
||||
{MQTTREASONCODE_CONNECTION_RATE_EXCEEDED, "Connection rate exceeded"},
|
||||
{MQTTREASONCODE_MAXIMUM_CONNECT_TIME, "Maximum connect time"},
|
||||
{MQTTREASONCODE_SUBSCRIPTION_IDENTIFIERS_NOT_SUPPORTED, "Subscription Identifiers not supported"},
|
||||
{MQTTREASONCODE_WILDCARD_SUBSCRIPTIONS_NOT_SUPPORTED, "Wildcard Subscriptions not supported"}
|
||||
};
|
||||
|
||||
const char* MQTTReasonCode_toString(enum MQTTReasonCodes value)
|
||||
{
|
||||
int i = 0;
|
||||
const char* result = NULL;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(nameToString); ++i)
|
||||
{
|
||||
if (nameToString[i].value == value)
|
||||
{
|
||||
result = nameToString[i].name;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,79 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2017, 2020 IBM Corp. and others
|
||||
*
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v2.0
|
||||
* and Eclipse Distribution License v1.0 which accompany this distribution.
|
||||
*
|
||||
* The Eclipse Public License is available at
|
||||
* https://www.eclipse.org/legal/epl-2.0/
|
||||
* and the Eclipse Distribution License is available at
|
||||
* http://www.eclipse.org/org/documents/edl-v10.php.
|
||||
*
|
||||
* Contributors:
|
||||
* Ian Craggs - initial API and implementation and/or initial documentation
|
||||
*******************************************************************************/
|
||||
|
||||
#if !defined(MQTTREASONCODES_H)
|
||||
#define MQTTREASONCODES_H
|
||||
|
||||
#include "MQTTExportDeclarations.h"
|
||||
|
||||
/** The MQTT V5 one byte reason code */
|
||||
enum MQTTReasonCodes {
|
||||
MQTTREASONCODE_SUCCESS = 0,
|
||||
MQTTREASONCODE_NORMAL_DISCONNECTION = 0,
|
||||
MQTTREASONCODE_GRANTED_QOS_0 = 0,
|
||||
MQTTREASONCODE_GRANTED_QOS_1 = 1,
|
||||
MQTTREASONCODE_GRANTED_QOS_2 = 2,
|
||||
MQTTREASONCODE_DISCONNECT_WITH_WILL_MESSAGE = 4,
|
||||
MQTTREASONCODE_NO_MATCHING_SUBSCRIBERS = 16,
|
||||
MQTTREASONCODE_NO_SUBSCRIPTION_FOUND = 17,
|
||||
MQTTREASONCODE_CONTINUE_AUTHENTICATION = 24,
|
||||
MQTTREASONCODE_RE_AUTHENTICATE = 25,
|
||||
MQTTREASONCODE_UNSPECIFIED_ERROR = 128,
|
||||
MQTTREASONCODE_MALFORMED_PACKET = 129,
|
||||
MQTTREASONCODE_PROTOCOL_ERROR = 130,
|
||||
MQTTREASONCODE_IMPLEMENTATION_SPECIFIC_ERROR = 131,
|
||||
MQTTREASONCODE_UNSUPPORTED_PROTOCOL_VERSION = 132,
|
||||
MQTTREASONCODE_CLIENT_IDENTIFIER_NOT_VALID = 133,
|
||||
MQTTREASONCODE_BAD_USER_NAME_OR_PASSWORD = 134,
|
||||
MQTTREASONCODE_NOT_AUTHORIZED = 135,
|
||||
MQTTREASONCODE_SERVER_UNAVAILABLE = 136,
|
||||
MQTTREASONCODE_SERVER_BUSY = 137,
|
||||
MQTTREASONCODE_BANNED = 138,
|
||||
MQTTREASONCODE_SERVER_SHUTTING_DOWN = 139,
|
||||
MQTTREASONCODE_BAD_AUTHENTICATION_METHOD = 140,
|
||||
MQTTREASONCODE_KEEP_ALIVE_TIMEOUT = 141,
|
||||
MQTTREASONCODE_SESSION_TAKEN_OVER = 142,
|
||||
MQTTREASONCODE_TOPIC_FILTER_INVALID = 143,
|
||||
MQTTREASONCODE_TOPIC_NAME_INVALID = 144,
|
||||
MQTTREASONCODE_PACKET_IDENTIFIER_IN_USE = 145,
|
||||
MQTTREASONCODE_PACKET_IDENTIFIER_NOT_FOUND = 146,
|
||||
MQTTREASONCODE_RECEIVE_MAXIMUM_EXCEEDED = 147,
|
||||
MQTTREASONCODE_TOPIC_ALIAS_INVALID = 148,
|
||||
MQTTREASONCODE_PACKET_TOO_LARGE = 149,
|
||||
MQTTREASONCODE_MESSAGE_RATE_TOO_HIGH = 150,
|
||||
MQTTREASONCODE_QUOTA_EXCEEDED = 151,
|
||||
MQTTREASONCODE_ADMINISTRATIVE_ACTION = 152,
|
||||
MQTTREASONCODE_PAYLOAD_FORMAT_INVALID = 153,
|
||||
MQTTREASONCODE_RETAIN_NOT_SUPPORTED = 154,
|
||||
MQTTREASONCODE_QOS_NOT_SUPPORTED = 155,
|
||||
MQTTREASONCODE_USE_ANOTHER_SERVER = 156,
|
||||
MQTTREASONCODE_SERVER_MOVED = 157,
|
||||
MQTTREASONCODE_SHARED_SUBSCRIPTIONS_NOT_SUPPORTED = 158,
|
||||
MQTTREASONCODE_CONNECTION_RATE_EXCEEDED = 159,
|
||||
MQTTREASONCODE_MAXIMUM_CONNECT_TIME = 160,
|
||||
MQTTREASONCODE_SUBSCRIPTION_IDENTIFIERS_NOT_SUPPORTED = 161,
|
||||
MQTTREASONCODE_WILDCARD_SUBSCRIPTIONS_NOT_SUPPORTED = 162
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns a printable string description of an MQTT V5 reason code.
|
||||
* @param value an MQTT V5 reason code.
|
||||
* @return the printable string description of the input reason code.
|
||||
* NULL if the code was not found.
|
||||
*/
|
||||
LIBMQTT_API const char* MQTTReasonCode_toString(enum MQTTReasonCodes value);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,46 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2018 IBM Corp.
|
||||
*
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v2.0
|
||||
* and Eclipse Distribution License v1.0 which accompany this distribution.
|
||||
*
|
||||
* The Eclipse Public License is available at
|
||||
* https://www.eclipse.org/legal/epl-2.0/
|
||||
* and the Eclipse Distribution License is available at
|
||||
* http://www.eclipse.org/org/documents/edl-v10.php.
|
||||
*
|
||||
* Contributors:
|
||||
* Ian Craggs - initial API and implementation and/or initial documentation
|
||||
*******************************************************************************/
|
||||
|
||||
#if !defined(SUBOPTS_H)
|
||||
#define SUBOPTS_H
|
||||
|
||||
/** The MQTT V5 subscribe options, apart from QoS which existed before V5. */
|
||||
typedef struct MQTTSubscribe_options
|
||||
{
|
||||
/** The eyecatcher for this structure. Must be MQSO. */
|
||||
char struct_id[4];
|
||||
/** The version number of this structure. Must be 0.
|
||||
*/
|
||||
int struct_version;
|
||||
/** To not receive our own publications, set to 1.
|
||||
* 0 is the original MQTT behaviour - all messages matching the subscription are received.
|
||||
*/
|
||||
unsigned char noLocal;
|
||||
/** To keep the retain flag as on the original publish message, set to 1.
|
||||
* If 0, defaults to the original MQTT behaviour where the retain flag is only set on
|
||||
* publications sent by a broker if in response to a subscribe request.
|
||||
*/
|
||||
unsigned char retainAsPublished;
|
||||
/** 0 - send retained messages at the time of the subscribe (original MQTT behaviour)
|
||||
* 1 - send retained messages on subscribe only if the subscription is new
|
||||
* 2 - do not send retained messages at all
|
||||
*/
|
||||
unsigned char retainHandling;
|
||||
} MQTTSubscribe_options;
|
||||
|
||||
#define MQTTSubscribe_options_initializer { {'M', 'Q', 'S', 'O'}, 0, 0, 0, 0 }
|
||||
|
||||
#endif
|
||||
+112
@@ -0,0 +1,112 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2020, 2021 IBM Corp. and Ian Craggs
|
||||
*
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v2.0
|
||||
* and Eclipse Distribution License v1.0 which accompany this distribution.
|
||||
*
|
||||
* The Eclipse Public License is available at
|
||||
* https://www.eclipse.org/legal/epl-2.0/
|
||||
* and the Eclipse Distribution License is available at
|
||||
* http://www.eclipse.org/org/documents/edl-v10.php.
|
||||
*
|
||||
* Contributors:
|
||||
* Ian Craggs - initial implementation
|
||||
*******************************************************************************/
|
||||
|
||||
#include "MQTTTime.h"
|
||||
#include "StackTrace.h"
|
||||
|
||||
#if defined(_WIN32) || defined(_WIN64)
|
||||
#include <windows.h>
|
||||
#else
|
||||
#include <unistd.h>
|
||||
#include <sys/time.h>
|
||||
#endif
|
||||
|
||||
void MQTTTime_sleep(ELAPSED_TIME_TYPE milliseconds)
|
||||
{
|
||||
FUNC_ENTRY;
|
||||
#if defined(_WIN32) || defined(_WIN64)
|
||||
Sleep((DWORD)milliseconds);
|
||||
#else
|
||||
usleep((useconds_t)(milliseconds*1000));
|
||||
#endif
|
||||
FUNC_EXIT;
|
||||
}
|
||||
|
||||
#if defined(_WIN32) || defined(_WIN64)
|
||||
START_TIME_TYPE MQTTTime_start_clock(void)
|
||||
{
|
||||
#if WINVER >= _WIN32_WINNT_VISTA
|
||||
return GetTickCount64();
|
||||
#else
|
||||
return GetTickCount();
|
||||
#endif
|
||||
}
|
||||
#elif defined(AIX)
|
||||
START_TIME_TYPE MQTTTime_start_clock(void)
|
||||
{
|
||||
struct timespec start;
|
||||
clock_gettime(CLOCK_MONOTONIC, &start);
|
||||
return start;
|
||||
}
|
||||
#else
|
||||
START_TIME_TYPE MQTTTime_start_clock(void)
|
||||
{
|
||||
struct timeval start;
|
||||
struct timespec start_ts;
|
||||
|
||||
clock_gettime(CLOCK_MONOTONIC, &start_ts);
|
||||
start.tv_sec = start_ts.tv_sec;
|
||||
start.tv_usec = start_ts.tv_nsec / 1000;
|
||||
return start;
|
||||
}
|
||||
#endif
|
||||
START_TIME_TYPE MQTTTime_now(void)
|
||||
{
|
||||
return MQTTTime_start_clock();
|
||||
}
|
||||
|
||||
|
||||
#if defined(_WIN32) || defined(_WIN64)
|
||||
/*
|
||||
* @param t_new most recent time in milliseconds from GetTickCount()
|
||||
* @param t_old older time in milliseconds from GetTickCount()
|
||||
* @return difference in milliseconds
|
||||
*/
|
||||
DIFF_TIME_TYPE MQTTTime_difftime(START_TIME_TYPE t_new, START_TIME_TYPE t_old)
|
||||
{
|
||||
#if WINVER >= _WIN32_WINNT_VISTA
|
||||
return (DIFF_TIME_TYPE)(t_new - t_old);
|
||||
#else
|
||||
if (t_old < t_new) /* check for wrap around condition in GetTickCount */
|
||||
return (DIFF_TIME_TYPE)(t_new - t_old);
|
||||
else
|
||||
return (DIFF_TIME_TYPE)((0xFFFFFFFFL - t_old) + 1 + t_new);
|
||||
#endif
|
||||
}
|
||||
#elif defined(AIX)
|
||||
#define assert(a)
|
||||
DIFF_TIME_TYPE MQTTTime_difftime(START_TIME_TYPE t_new, START_TIME_TYPE t_old)
|
||||
{
|
||||
struct timespec result;
|
||||
|
||||
ntimersub(t_new, t_old, result);
|
||||
return (DIFF_TIME_TYPE)((result.tv_sec)*1000L + (result.tv_nsec)/1000000L); /* convert to milliseconds */
|
||||
}
|
||||
#else
|
||||
DIFF_TIME_TYPE MQTTTime_difftime(START_TIME_TYPE t_new, START_TIME_TYPE t_old)
|
||||
{
|
||||
struct timeval result;
|
||||
|
||||
timersub(&t_new, &t_old, &result);
|
||||
return (DIFF_TIME_TYPE)(((DIFF_TIME_TYPE)result.tv_sec)*1000 + ((DIFF_TIME_TYPE)result.tv_usec)/1000); /* convert to milliseconds */
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
ELAPSED_TIME_TYPE MQTTTime_elapsed(START_TIME_TYPE milliseconds)
|
||||
{
|
||||
return (ELAPSED_TIME_TYPE)MQTTTime_difftime(MQTTTime_now(), milliseconds);
|
||||
}
|
||||
+49
@@ -0,0 +1,49 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2020 IBM Corp.
|
||||
*
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v2.0
|
||||
* and Eclipse Distribution License v1.0 which accompany this distribution.
|
||||
*
|
||||
* The Eclipse Public License is available at
|
||||
* https://www.eclipse.org/legal/epl-2.0/
|
||||
* and the Eclipse Distribution License is available at
|
||||
* http://www.eclipse.org/org/documents/edl-v10.php.
|
||||
*
|
||||
* Contributors:
|
||||
* Ian Craggs - initial implementation
|
||||
*******************************************************************************/
|
||||
|
||||
#if !defined(MQTTTIME_H)
|
||||
#define MQTTTIME_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#if defined(_WIN32) || defined(_WIN64)
|
||||
#include <windows.h>
|
||||
#if WINVER >= _WIN32_WINNT_VISTA
|
||||
#define START_TIME_TYPE ULONGLONG
|
||||
#define START_TIME_ZERO 0
|
||||
#else
|
||||
#define START_TIME_TYPE DWORD
|
||||
#define START_TIME_ZERO 0
|
||||
#endif
|
||||
#elif defined(AIX)
|
||||
#define START_TIME_TYPE struct timespec
|
||||
#define START_TIME_ZERO {0, 0}
|
||||
#else
|
||||
#include <sys/time.h>
|
||||
#define START_TIME_TYPE struct timeval
|
||||
#define START_TIME_ZERO {0, 0}
|
||||
#endif
|
||||
|
||||
#define ELAPSED_TIME_TYPE uint64_t
|
||||
#define DIFF_TIME_TYPE int64_t
|
||||
|
||||
void MQTTTime_sleep(ELAPSED_TIME_TYPE milliseconds);
|
||||
START_TIME_TYPE MQTTTime_start_clock(void);
|
||||
START_TIME_TYPE MQTTTime_now(void);
|
||||
ELAPSED_TIME_TYPE MQTTTime_elapsed(START_TIME_TYPE milliseconds);
|
||||
DIFF_TIME_TYPE MQTTTime_difftime(START_TIME_TYPE t_new, START_TIME_TYPE t_old);
|
||||
|
||||
#endif
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user