Merge: Snapmaker Orca 2.1.2

This commit is contained in:
xiaoyeliu
2025-11-17 10:04:25 +08:00
parent 737948be1f
commit e89263e51a
1147 changed files with 668188 additions and 15290 deletions
+63 -56
View File
@@ -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 ()
+25 -25
View File
@@ -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());
}
}
@@ -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"
@@ -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
View File
@@ -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;
@@ -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
View File
@@ -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
+51 -1
View File
@@ -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
-1
View File
@@ -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);
+62 -7
View File
@@ -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"));
+2 -2
View File
@@ -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 };
+16 -16
View File
@@ -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
View File
@@ -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;
+4
View File
@@ -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 -1
View File
@@ -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 -1
View File
@@ -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.
+15 -11
View File
@@ -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
+7 -1
View File
@@ -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.
+28
View File
@@ -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);
+1
View File
@@ -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();
+11 -10
View File
@@ -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);
+77 -27
View File
@@ -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,
+4 -1
View File
@@ -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
View File
@@ -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);
}
+5 -1
View File
@@ -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.
+1 -1
View File
@@ -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
View File
@@ -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;
}
+8
View File
@@ -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);
+63 -8
View File
@@ -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;
+14 -7
View File
@@ -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;
+8
View File
@@ -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;
}
+6 -1
View File
@@ -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);
+1 -1
View File
@@ -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
View File
@@ -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\", "
+14 -1
View File
@@ -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))
+34 -4
View File
@@ -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;
+4 -2
View File
@@ -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);
+1 -1
View File
@@ -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"); }
+4 -4
View File
@@ -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>
+2 -1
View File
@@ -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
+3 -3
View File
@@ -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()
+173
View File
@@ -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)
+46
View File
@@ -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}
)
+10
View File
@@ -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()
+19
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
+22
View File
@@ -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
View File
@@ -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, &current) != 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, &current) != NULL)
printf("List element: %d\n", *((int*)(current->content)));
printf("List contents in reverse order:\n");
current = NULL;
while (ListPrevElement(l, &current) != 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, &current) != 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, &current) != NULL)
printf("List element: %d\n", *((int*)(current->content)));
ListFree(l);
printf("List freed\n");
}
#endif
+105
View File
@@ -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
View File
@@ -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
View File
@@ -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
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+187
View File
@@ -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_ */
File diff suppressed because it is too large Load Diff
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
File diff suppressed because it is too large Load Diff
+271
View File
@@ -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
View File
@@ -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;
}
+39
View File
@@ -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
+921
View File
@@ -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, &current) != 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, &current);
while(ListNextElement(client->outboundMsgs, &current) != 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, &current) != 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
+99
View File
@@ -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
View File
@@ -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
View File
@@ -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 */
+46
View File
@@ -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
File diff suppressed because it is too large Load Diff
+62
View File
@@ -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
+479
View File
@@ -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;
}
+66
View File
@@ -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
+101
View File
@@ -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;
}
+79
View File
@@ -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
+46
View File
@@ -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
View File
@@ -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
View File
@@ -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