diff --git a/build/README.md b/build/README.md new file mode 100644 index 0000000..96979de --- /dev/null +++ b/build/README.md @@ -0,0 +1,3 @@ +# cmake编译配置 +## 目录 +./toolchain:配置编译工具链 \ No newline at end of file diff --git a/build/cmake/Makefile b/build/cmake/Makefile new file mode 100644 index 0000000..66328fe --- /dev/null +++ b/build/cmake/Makefile @@ -0,0 +1,8 @@ +all: + @mkdir -p ../../cmake-shell;\ + cd ../../cmake-shell;\ + pwd;\ + cmake -DCMAKE_TOOLCHAIN_FILE="./build/cmake/toolchain/linux.toolchain.cmake" ..;\ + cd .. +clean: + rm -rf ../../cmake-shell \ No newline at end of file diff --git a/build/cmake/README.md b/build/cmake/README.md new file mode 100644 index 0000000..97dfa00 --- /dev/null +++ b/build/cmake/README.md @@ -0,0 +1,2 @@ +# cmake + diff --git a/build/cmake/toolchain/linux.toolchain.cmake b/build/cmake/toolchain/linux.toolchain.cmake new file mode 100755 index 0000000..2f4b9a0 --- /dev/null +++ b/build/cmake/toolchain/linux.toolchain.cmake @@ -0,0 +1,89 @@ + +INCLUDE(CMakeForceCompiler) + +set(LINUX_TEST "true") +set(CROSS_COMPILE_PREFIX "") +set(CMAKE_C_COMPILER "${CROSS_COMPILE_PREFIX}gcc") +set(CMAKE_CXX_COMPILER "${CROSS_COMPILE_PREFIX}g++") + +# path to compiler and utilities +set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) +set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) +set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) + +# Name of the target platform +set(CMAKE_SYSTEM_NAME Linux) +set(CMAKE_SYSTEM_PROCESSOR arm) + +# Version of the system +set(CMAKE_SYSTEM_VERSION 1) +cmake_policy(SET CMP0011 NEW) +cmake_policy(SET CMP0005 NEW) + +add_definitions(-Wall -O2 -Os) +add_definitions(-Wno-unused-local-typedefs) +add_definitions(-Wstrict-aliasing -Wwrite-strings) + +set(TOOLCHAIN_NAME arm-linux-gnueabihf) + +set(TARGET_PLATFORM "linux") +set(SUBMODULE_PATH_OF_IPC_SDK "") +set(PLATFORM_PATH "${CMAKE_CURRENT_SOURCE_DIR}") +set(TEST_COVERAGE "true") +add_definitions(-DPLATFORM_PATH=\"${PLATFORM_PATH}\") +set(PROJECT_OUTPUT_FOLDER "output_files") +set(CMAKE_INSTALL_PREFIX "${CMAKE_CURRENT_SOURCE_DIR}/${PROJECT_OUTPUT_FOLDER}") + +# ------------ build curl + openssl ------------ # +set(CURL_OPENSSL_LIB_SHARED_ENABLE "false") +# ------------ build curl + openssl end ------------ # + +# ------------ build IpcConfig ------------ # +set(IPC_CONFIG_FILE_PATH "./ipc_config") +set(USERDATA_MOUNT_PATH "/userdata") +# ------------ build IpcConfig end ------------ # + +# ------------ build log ------------ # +set(LOG_SUPPORT "true") +# ------------ build log end ------------ # + +# ------------ build GoAhead ------------ # +set(GOAHEAD_DOCUMENTS_PATH "web") +set(GOAHEAD_UPLOAD_TMP_PATH "./goahead") +set(GOAHEAD_UPLOAD_PATH "${GOAHEAD_UPLOAD_TMP_PATH}") +set(GOAHEAD_LIMIT_POST "335544320") # If not defined means using default setting. See goahead-linux-static-fancy.mk +# GOAHEAD_CONFIG_FILE_PATH should be set when cross compile +set(GOAHEAD_CONFIG_FILE_PATH ".") +# ------------ build GoAhead end ------------ # + +# ------------ build McuManager ------------ # +set(MCU_UART_DEVICE "dev/s1") +# ------------ build McuManager end ------------ # + +# ------------ build curl + openssl ------------ start +set(CROSS_COMPILE_PREFIX "") +# set(CROSS_COMPILE_PREFIX "") +set(CURL_OPENSSL_LIB_SHARED_ENABLE "false") +set(CURL_SHARED_LIBS_PATH "/mnt/mmc") +# ------------ build curl + openssl ------------ end + +# ------------ build AppManager ------------ # +set(APP_MANAGER_DEVICE_IP "localhost") +# set(APP_MANAGER_DEVICE_IP "192.168.1.29") +set(APP_MANAGER_HTTP_SERVER_PORT "8080") +set(APP_MANAGER_TCP_SERVER_PORT "9876") +# ------------ build AppManager end ------------ # + +# ------------ build sd card ------------ # +set(SD_CARD_DEV "/dev/test") +set(SD_CARD_MOUNT_PATH "./sdcard") +# ------------ build sd card end ------------ # + +# ------------ build upgrade ------------ # +set(APPLICATION_CHECK_PATH "/application.bin") +set(APPLICATION_UPGRADE_PATH "./fastboot_server") +set(APPLICATION_VERSION_1 1) +set(APPLICATION_VERSION_2 0) +set(APPLICATION_VERSION_3 0) +set(APPLICATION_VERSION_4 0) +# ------------ build upgrade end ------------ # \ No newline at end of file diff --git a/build/global_config.cmake b/build/global_config.cmake new file mode 100755 index 0000000..7af1883 --- /dev/null +++ b/build/global_config.cmake @@ -0,0 +1,88 @@ +set(DEFINE_LINUX "linux") +# set(CMAKE_SOURCE_DIR_IPCSDK "${CMAKE_SOURCE_DIR}") +# set(CMAKE_SOURCE_DIR_IPCSDK "${CMAKE_SOURCE_DIR_IPCSDK}${SUBMODULE_PATH_OF_IPC_SDK}") +set(EXEC_OUTPUT_PATH "${PLATFORM_PATH}/${PROJECT_OUTPUT_FOLDER}/bin") +set(LIBS_OUTPUT_PATH "${PLATFORM_PATH}/${PROJECT_OUTPUT_FOLDER}/libs") +set(TEST_TOOLS_OUTPUT_PATH "${PLATFORM_PATH}/${PROJECT_OUTPUT_FOLDER}/libs/test_tools") +set(EXTERNAL_LIBS_OUTPUT_PATH "${PLATFORM_PATH}/${PROJECT_OUTPUT_FOLDER}/libs/external") +set(TEST_OUTPUT_PATH "${PLATFORM_PATH}/${PROJECT_OUTPUT_FOLDER}/test") + +set(PROJECT_ROOT_PATH "${PLATFORM_PATH}") +set(APPLICATION_SOURCE_PATH "${CMAKE_SOURCE_DIR_IPCSDK}/application") +set(MIDDLEWARE_SOURCE_PATH "${CMAKE_SOURCE_DIR_IPCSDK}/middleware") +set(UTILS_SOURCE_PATH "${CMAKE_SOURCE_DIR_IPCSDK}/utils") +set(HAL_SOURCE_PATH "${CMAKE_SOURCE_DIR_IPCSDK}/hal") +set(TEST_SOURCE_PATH "${CMAKE_SOURCE_DIR_IPCSDK}/test") +set(EXTERNAL_SOURCE_PATH "${CMAKE_SOURCE_DIR_IPCSDK}/external") + +# -------------------------- clang-tidy tools -------------------------- # +set(CLANG_TIDY_CHECKS "-*,\ +llvm-else-after-return,\ +-llvm-include-order,\ +llvm-namespace-comment,\ +llvm-prefer-isa-or-dyn-cast-in-conditionals,\ +llvm-prefer-register-over-unsigned,\ +llvm-qualified-auto,\ +llvm-twine-local,\ +misc-confusable-identifiers,\ +misc-definitions-in-headers,\ +misc-header-include-cycle,\ +-misc-include-cleaner,\ +misc-misleading-bidirectional,\ +misc-misleading-identifier,\ +misc-misplaced-const,\ +misc-new-delete-overloads,\ +misc-non-copyable-objects,\ +misc-redundant-expression,\ +misc-static-assert,\ +misc-throw-by-value-catch-by-reference,\ +misc-unconventional-assign-operator,\ +misc-uniqueptr-reset-release,\ +misc-unused-alias-decls,\ +misc-unused-using-decls,\ +readability-identifier-naming") + +# set(CLANG_TIDY_CHECKS "${CLANG_TIDY_CHECKS},llvm-header-guard") +set(CLANG_TIDY_CHECKS "${CLANG_TIDY_CHECKS},-clang-diagnostic-error") + +set(CLANG_TIDY_CONFIG "-header-filter=\'.*\'") +set(CLANG_TIDY_CONFIG "${CLANG_TIDY_CONFIG} -p ${CMAKE_SOURCE_DIR_IPCSDK}/cmake-shell") + +# set(CLANG_FORMAT_FILE "${CMAKE_SOURCE_DIR_IPCSDK}/tools/clang-format/.clang-format") +set(CLANG_FORMAT_FILE "LLVM ${CMAKE_SOURCE_DIR_IPCSDK}/tools/clang-format/.clang-format") +# -------------------------- clang-tidy tools end -------------------------- # +# ------------ build clang-tools ------------ # +if(${LINUX_TEST} MATCHES "true") + set(CLANG_TIDY_SUPPORT "true") + set(CLANG_FORMAT_SUPPORT "true") + set(COMPILE_IMPROVE_SUPPORT "false") # 开启后每次编译可能会很慢 + set(LLVM_PATH "$ENV{HOME}/llvm-project") +endif() +# ------------ build clang-tools end ------------ # + +# -------------------------- log setting -------------------------- # +function(define_file_name target) + get_target_property(source_files "${target}" SOURCES) + foreach(source_file ${source_files}) + get_property(defs SOURCE "${source_file}" + PROPERTY COMPILE_DEFINITIONS) + get_filename_component(file_name "${source_file}" NAME) + list(APPEND defs "__F_FILE__=\"${file_name}\"") + set_property( + SOURCE "${source_file}" + PROPERTY COMPILE_DEFINITIONS ${defs}) + endforeach() +endfunction() +function(log_disable target) + get_target_property(source_files "${target}" SOURCES) + foreach(source_file ${source_files}) + get_property(defs SOURCE "${source_file}" + PROPERTY COMPILE_DEFINITIONS) + get_filename_component(file_name "${source_file}" NAME) + list(APPEND defs "LOG_DISABLE") + set_property( + SOURCE "${source_file}" + PROPERTY COMPILE_DEFINITIONS ${defs}) + endforeach() +endfunction() +# -------------------------- log setting end -------------------------- # \ No newline at end of file diff --git a/build/independent_source.cmake b/build/independent_source.cmake new file mode 100644 index 0000000..53f1e88 --- /dev/null +++ b/build/independent_source.cmake @@ -0,0 +1,14 @@ + +# This config file is only for independent source files. + +# Never leave over any warning in independt project. +# set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -Werror -Wextra") +# set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Werror -Wextra") +set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -Werror") +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Werror") + +# For build coverage report in linux-x86 +if(${TARGET_PLATFORM} MATCHES ${DEFINE_LINUX}) + # SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fprofile-arcs -ftest-coverage") + # SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fprofile-arcs -ftest-coverage") +endif() \ No newline at end of file diff --git a/build/sdk_config.cmake b/build/sdk_config.cmake new file mode 100755 index 0000000..232b852 --- /dev/null +++ b/build/sdk_config.cmake @@ -0,0 +1,27 @@ +include(${CMAKE_SOURCE_DIR_IPCSDK}/build/global_config.cmake) + +set(CMAKE_EXPORT_COMPILE_COMMANDS ON) + +# Gdb debug +if(${TARGET_PLATFORM} MATCHES ${DEFINE_LINUX}) + message("---------------------------Debug mode.-----------------------") + SET(CMAKE_BUILD_TYPE "Debug") + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -O0 -Wall -g -ggdb") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -O0 -Wall -g -ggdb") + SET(CMAKE_CXX_FLAGS_DEBUG "$ENV{CXXFLAGS} -O0 -Wall -g -ggdb") + SET(CMAKE_CXX_FLAGS_RELEASE "$ENV{CXXFLAGS} -O3 -Wall") + # asan + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fsanitize=leak -fsanitize=address -fno-omit-frame-pointer -fsanitize=undefined") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=leak -fsanitize=address -fno-omit-frame-pointer -fsanitize=undefined") +else() + message("---------------------------Release mode.-----------------------") + SET(CMAKE_BUILD_TYPE "Release") + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Os") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Os") +endif() + +if(${TEST_COVERAGE} MATCHES "true") + message("you choose to build gcno file") + add_definitions("-fprofile-arcs") + add_definitions("-ftest-coverage") +endif() diff --git a/hal/CMakeLists.txt b/hal/CMakeLists.txt new file mode 100644 index 0000000..87358f3 --- /dev/null +++ b/hal/CMakeLists.txt @@ -0,0 +1,66 @@ + +include(${CMAKE_SOURCE_DIR_IPCSDK}/build/global_config.cmake) +include(${CMAKE_CURRENT_SOURCE_DIR}/build/hal.cmake) +set(EXECUTABLE_OUTPUT_PATH ${EXEC_OUTPUT_PATH}) +set(LIBRARY_OUTPUT_PATH ${LIBS_OUTPUT_PATH}) + +include_directories( + ./abstract + ./src + ./include + ${UTILS_SOURCE_PATH}/StatusCode/include + ${UTILS_SOURCE_PATH}/Log/include + ${UTILS_SOURCE_PATH}/LinuxApi/include + ${UTILS_SOURCE_PATH}/KeyControl/include +) +#do not rely on any other library +# link_directories( +# ${EXTERNAL_SOURCE_PATH}/curl/curl-8.1.2/build/lib +# ) + +aux_source_directory(./abstract ABSTRACT_SRC_FILES) +aux_source_directory(./src IMPL_SRC_FILES) + +set(ABSTRACT_TARGET HalAbstract) +set(IMPL_TARGET Hal) +add_library(${ABSTRACT_TARGET} STATIC ${ABSTRACT_SRC_FILES}) +target_link_libraries(${ABSTRACT_TARGET} LinuxApi KeyControl StatusCode Log) +add_library(${IMPL_TARGET} STATIC ${IMPL_SRC_FILES}) +target_link_libraries(${IMPL_TARGET} ${ABSTRACT_TARGET}) + + +if ("${COMPILE_IMPROVE_SUPPORT}" MATCHES "true") +add_custom_target( + Hal_code_check + COMMAND ${CLANG_TIDY_EXE} + -checks='${CLANG_TIDY_CHECKS}' + --header-filter=.* + --system-headers=false + ${ABSTRACT_SRC_FILES} + ${IMPL_SRC_FILES} + ${CLANG_TIDY_CONFIG} + -p ${PLATFORM_PATH}/cmake-shell + WORKING_DIRECTORY ${CMAKE_SOURCE_DIR_IPCSDK}/hal +) +file(GLOB_RECURSE HEADER_FILES *.h) +add_custom_target( + Hal_code_format + COMMAND ${CLANG_FORMAT_EXE} + -style=file + -i ${ABSTRACT_SRC_FILES} ${IMPL_SRC_FILES} ${HEADER_FILES} + WORKING_DIRECTORY ${HAL_SOURCE_PATH} +) +add_custom_command( + TARGET ${IMPL_TARGET} + PRE_BUILD + COMMAND make Hal_code_check + COMMAND make Hal_code_format + WORKING_DIRECTORY ${PLATFORM_PATH}/cmake-shell/ +) +endif() + +define_file_name(${IMPL_TARGET}) +define_file_name(${ABSTRACT_TARGET}) + +file(GLOB_RECURSE INSTALL_HEADER_FILES include/*.h) +install(FILES ${INSTALL_HEADER_FILES} DESTINATION include) \ No newline at end of file diff --git a/hal/abstract/IHal.cpp b/hal/abstract/IHal.cpp new file mode 100644 index 0000000..115c424 --- /dev/null +++ b/hal/abstract/IHal.cpp @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2023 Fancy Code. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "IHal.h" +#include "IHalCpp.h" +#include "StatusCode.h" +// #include +#include +static StatusCode IHalInit(IHal *object) +{ + return IHalCpp::GetInstance()->Init(); +} +static void IHalFree(void *object) +{ +} +static StatusCode IHalUnInit(IHal *object) +{ + return IHalCpp::GetInstance()->UnInit(); +} +static IHal default_hal = { + .init = IHalInit, + .un_init = IHalUnInit, + .free = IHalFree, +}; +static IHal *hal_instance = &default_hal; +IHal *GetHalIntance(void) +{ + return hal_instance; +} +StatusCode NewIHal(IHal **object) +{ + if (!object || !(*object)) { + return CreateStatusCode(STATUS_CODE_NOT_OK); + } + memcpy(*object, &default_hal, sizeof(IHal)); + return CreateStatusCode(STATUS_CODE_OK); +} +void ResetHalImpl(IHal *impl) +{ + hal_instance->free(hal_instance); + hal_instance = impl; +} \ No newline at end of file diff --git a/hal/abstract/IHalCpp.cpp b/hal/abstract/IHalCpp.cpp new file mode 100644 index 0000000..766bcdc --- /dev/null +++ b/hal/abstract/IHalCpp.cpp @@ -0,0 +1,146 @@ +/* + * Copyright (c) 2023 Fancy Code. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "IHalCpp.h" +#include "ILog.h" +#include +camera_report_event::camera_report_event(const std::string &fileName, const CameraType &cameraType) + : mFileName(fileName), mCameraType(cameraType) +{ +} +void VKeyHalMonitor::KeyEventHappened(const std::string &keyName, const VirtualKeyEvent &event, + const unsigned int &timeMs) +{ + LogWarning("STATUS_CODE_VIRTUAL_FUNCTION.\n"); +} +void VKeyHal::CheckKeyStatus(void) +{ + LogWarning("STATUS_CODE_VIRTUAL_FUNCTION.\n"); +} +void VKeyHal::GetHoldPressingTimeMs(long int &holdTimeMs, VirtualKeyEvent &event) +{ + LogWarning("STATUS_CODE_VIRTUAL_FUNCTION.\n"); +} +void VKeyHal::SetKeyMonitor(std::shared_ptr &monitor) +{ + LogWarning("STATUS_CODE_VIRTUAL_FUNCTION.\n"); +} +// std::string VKeyHal::GetKeyHalName(void) +// { +// return "undefined"; +// } +StatusCode VWifiHal::PowerOn(void) +{ + LogWarning("STATUS_CODE_VIRTUAL_FUNCTION.\n"); + return CreateStatusCode(STATUS_CODE_VIRTUAL_FUNCTION); +} +StatusCode VWifiHal::PowerOff(void) +{ + LogWarning("STATUS_CODE_VIRTUAL_FUNCTION.\n"); + return CreateStatusCode(STATUS_CODE_VIRTUAL_FUNCTION); +} +StatusCode VWifiHal::OpenApMode(void) +{ + LogWarning("STATUS_CODE_VIRTUAL_FUNCTION.\n"); + return CreateStatusCode(STATUS_CODE_VIRTUAL_FUNCTION); +} +StatusCode VWifiHal::CloseApMode(void) +{ + LogWarning("STATUS_CODE_VIRTUAL_FUNCTION.\n"); + return CreateStatusCode(STATUS_CODE_VIRTUAL_FUNCTION); +} +void VCameraHalMonitor::ReportEvent(const CameraReportEvent &event) +{ + LogWarning("STATUS_CODE_VIRTUAL_FUNCTION.\n"); +} +camera_task_param::camera_task_param(const CameraTaskType &cameraTask) : mCameraTask(cameraTask) +{ +} +void VCameraHal::SetCameraMonitor(std::shared_ptr &monitor) +{ + LogWarning("STATUS_CODE_VIRTUAL_FUNCTION.\n"); +} +StatusCode VCameraHal::StartSingleTask(const CameraTaskParam ¶m) +{ + LogWarning("STATUS_CODE_VIRTUAL_FUNCTION.\n"); + return CreateStatusCode(STATUS_CODE_VIRTUAL_FUNCTION); +} +void VSdCardHalMonitor::ReportEvent(const SdCardHalStatus &status) +{ + LogWarning("STATUS_CODE_VIRTUAL_FUNCTION.\n"); +} +std::shared_ptr &IHalCpp::GetInstance(std::shared_ptr *impl) +{ + static auto instance = std::make_shared(); + if (impl) { + if (instance.use_count() == 1) { + LogInfo("Instance changed succeed.\n"); + instance = *impl; + } + else { + LogError("Can't changing the instance becase of using by some one.\n"); + } + } + return instance; +} +void VSdCardHal::SetSdCardMonitor(std::shared_ptr &monitor) +{ + LogWarning("STATUS_CODE_VIRTUAL_FUNCTION.\n"); +} +SdCardHalStatus VSdCardHal::GetSdCardStatus(void) +{ + LogWarning("STATUS_CODE_VIRTUAL_FUNCTION.\n"); + return SdCardHalStatus::END; +} +StatusCode VSdCardHal::GetCapacity(unsigned long long &totalSizeMB, unsigned long long &freeSizeMB, + unsigned long long &usedSizeMB) +{ + LogWarning("STATUS_CODE_VIRTUAL_FUNCTION.\n"); + return CreateStatusCode(STATUS_CODE_VIRTUAL_FUNCTION); +} +StatusCode IHalCpp::Init(void) +{ + LogWarning("STATUS_CODE_VIRTUAL_FUNCTION.\n"); + return CreateStatusCode(STATUS_CODE_VIRTUAL_FUNCTION); +} +StatusCode IHalCpp::UnInit(void) +{ + LogWarning("STATUS_CODE_VIRTUAL_FUNCTION.\n"); + return CreateStatusCode(STATUS_CODE_VIRTUAL_FUNCTION); +} +StatusCode IHalCpp::GetAllLeds(std::map> &allLeds) +{ + LogWarning("STATUS_CODE_VIRTUAL_FUNCTION.\n"); + return CreateStatusCode(STATUS_CODE_VIRTUAL_FUNCTION); +} +StatusCode IHalCpp::GetAllKeys(std::map> &allKeys) +{ + LogWarning("STATUS_CODE_VIRTUAL_FUNCTION.\n"); + return CreateStatusCode(STATUS_CODE_VIRTUAL_FUNCTION); +} +StatusCode IHalCpp::GetWifiHal(std::shared_ptr &wifi) +{ + LogWarning("STATUS_CODE_VIRTUAL_FUNCTION.\n"); + return CreateStatusCode(STATUS_CODE_VIRTUAL_FUNCTION); +} +StatusCode IHalCpp::GetCameraHal(std::map> &allCameras) +{ + LogWarning("STATUS_CODE_VIRTUAL_FUNCTION.\n"); + return CreateStatusCode(STATUS_CODE_VIRTUAL_FUNCTION); +} +StatusCode IHalCpp::GetSdCardHal(std::shared_ptr &sdCard) +{ + LogWarning("STATUS_CODE_VIRTUAL_FUNCTION.\n"); + return CreateStatusCode(STATUS_CODE_VIRTUAL_FUNCTION); +} \ No newline at end of file diff --git a/hal/build/hal.cmake b/hal/build/hal.cmake new file mode 100644 index 0000000..e409500 --- /dev/null +++ b/hal/build/hal.cmake @@ -0,0 +1,2 @@ +add_definitions(-DSD_CARD_DEV=\"${SD_CARD_DEV}\") +add_definitions(-DSD_CARD_MOUNT_PATH=\"${SD_CARD_MOUNT_PATH}\") \ No newline at end of file diff --git a/hal/include/IHal.h b/hal/include/IHal.h new file mode 100644 index 0000000..1a3713a --- /dev/null +++ b/hal/include/IHal.h @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2023 Fancy Code. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef IHAL_H +#define IHAL_H +#include "StatusCode.h" +#ifdef __cplusplus +extern "C" { +#endif +// enum KEY_EVENT +// { +// KEY_EVENT_NOT_PRESSING = 0, +// KEY_EVENT_PRESSING, +// KEY_EVENT_END +// }; +// typedef struct v_key_owner VKeyOwner; +// typedef struct v_key_owner +// { +// // StatusCode (*init)(VKeyOwner *); +// // StatusCode (*un_init)(VKeyOwner *); +// void (*key_event_trigger)(VKeyOwner *, const enum KEY_EVENT); +// } VKeyOwner; +// typedef struct v_key_handle VKeyHandle; +// typedef struct v_key_handle +// { +// // StatusCode (*init)(VKeyHandle *); +// // StatusCode (*un_init)(VKeyHandle *); +// StatusCode (*set_owner)(VKeyHandle *, VKeyOwner *); +// } VKeyHandle; +typedef struct i_hal IHal; +typedef struct i_hal +{ + StatusCode (*init)(IHal *); + StatusCode (*un_init)(IHal *); + void (*free)(void *); +} IHal; +IHal *GetHalIntance(void); +StatusCode NewIHal(IHal **object); +void ResetHalImpl(IHal *impl); +static inline StatusCode IHalInit(void) +{ + return GetHalIntance()->init(GetHalIntance()); +} +static inline StatusCode IHalUnInit(void) +{ + return GetHalIntance()->un_init(GetHalIntance()); +} +StatusCode CreateHalModule(void); +StatusCode DestroyHalModule(void); +#ifdef __cplusplus +} +#endif +#endif \ No newline at end of file diff --git a/hal/include/IHalCpp.h b/hal/include/IHalCpp.h new file mode 100644 index 0000000..fab89b6 --- /dev/null +++ b/hal/include/IHalCpp.h @@ -0,0 +1,161 @@ +/* + * Copyright (c) 2023 Fancy Code. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef I_HAL_CPP_H +#define I_HAL_CPP_H +#include "StatusCode.h" +#include +#include +#include +#include +using VirtualLedState = unsigned char; +using VirtualKeyEvent = unsigned char; +constexpr int INVALID_PERIOD = -1; +constexpr int PERIPHERAL_CHECK_PERIOD_MS = 50; +constexpr int IMEI_LEN = 15; +enum class CameraType +{ + MAIN_CAMERA = 0, + END +}; +enum class SdCardHalStatus +{ + MOUNTED = 0, + UNMOUNTED, + INSERTED, + PULL_OUT_DEVICE_NOT_EXIST, + PULL_OUT_OPEN_FAILED, + PULL_OUT_NOT_BLOCK_DEVICE, + ERROR, + END +}; +enum class CameraTaskType +{ + PICTURE = 0, + VIDEO, + PICTURE_AND_VIDEO, + END +}; +typedef struct camera_report_event +{ + camera_report_event(const std::string &fileName, const CameraType &cameraType); + const std::string mFileName; + const CameraType mCameraType; +} CameraReportEvent; +void CreateHalCppModule(void); +void DestroyHalCppModule(void); +class VKeyHalMonitor +{ +public: + VKeyHalMonitor() = default; + virtual ~VKeyHalMonitor() = default; + virtual void KeyEventHappened(const std::string &keyName, const VirtualKeyEvent &event, const unsigned int &timeMs); +}; +class VKeyHal +{ +public: + VKeyHal() = default; + virtual ~VKeyHal() = default; + virtual void CheckKeyStatus(void); + virtual void GetHoldPressingTimeMs(long int &holdTimeMs, VirtualKeyEvent &event); + virtual void SetKeyMonitor(std::shared_ptr &monitor); + // virtual std::string GetKeyHalName(void); +}; +class VLedHal +{ +public: + VLedHal() = default; + virtual ~VLedHal() = default; +}; +class VWifiHal +{ +public: + VWifiHal() = default; + virtual ~VWifiHal() = default; + virtual StatusCode PowerOn(void); + virtual StatusCode PowerOff(void); + virtual StatusCode OpenApMode(void); + virtual StatusCode CloseApMode(void); +}; +class VCameraHalMonitor +{ +public: + VCameraHalMonitor() = default; + virtual ~VCameraHalMonitor() = default; + virtual void ReportEvent(const CameraReportEvent &event); +}; +class VCameraTaskContext +{ +public: + VCameraTaskContext() = default; + virtual ~VCameraTaskContext() = default; +}; +template +class CameraTaskContext : public VCameraTaskContext +{ +public: + CameraTaskContext(T &value) : mData(value) + { + } + virtual ~CameraTaskContext() = default; + +public: + T mData; +}; +typedef struct camera_task_param +{ + camera_task_param(const CameraTaskType &cameraTask); + const CameraTaskType mCameraTask; + std::shared_ptr mCtx; +} CameraTaskParam; +class VCameraHal +{ +public: + VCameraHal() = default; + virtual ~VCameraHal() = default; + virtual void SetCameraMonitor(std::shared_ptr &monitor); + virtual StatusCode StartSingleTask(const CameraTaskParam ¶m); +}; +class VSdCardHalMonitor +{ +public: + VSdCardHalMonitor() = default; + virtual ~VSdCardHalMonitor() = default; + virtual void ReportEvent(const SdCardHalStatus &status); +}; +class VSdCardHal +{ +public: + VSdCardHal() = default; + virtual ~VSdCardHal() = default; + virtual void SetSdCardMonitor(std::shared_ptr &monitor); + virtual SdCardHalStatus GetSdCardStatus(void); + virtual StatusCode GetCapacity(unsigned long long &totalSizeMB, unsigned long long &freeSizeMB, + unsigned long long &usedSizeMB); +}; +class IHalCpp +{ +public: + IHalCpp() = default; + virtual ~IHalCpp() = default; + static std::shared_ptr &GetInstance(std::shared_ptr *impl = nullptr); + virtual StatusCode Init(void); + virtual StatusCode UnInit(void); + virtual StatusCode GetAllLeds(std::map> &allLeds); + virtual StatusCode GetAllKeys(std::map> &allKeys); + virtual StatusCode GetWifiHal(std::shared_ptr &wifi); + virtual StatusCode GetCameraHal(std::map> &allCameras); + virtual StatusCode GetSdCardHal(std::shared_ptr &sdCard); +}; +#endif diff --git a/hal/src/Hal.c b/hal/src/Hal.c new file mode 100644 index 0000000..465db48 --- /dev/null +++ b/hal/src/Hal.c @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2023 Fancy Code. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "Hal.h" +#include "ILog.h" +#include +#include +StatusCode HalInit(IHal *hal) +{ + LogInfo("Hal init.\n"); + return CreateStatusCode(STATUS_CODE_OK); +} +static void HalFree(void *object) +{ + LogInfo("hal instance free.\n"); + if (object) { + free(object); + } +} +void HalImplInit(Hal *hal) +{ + LogInfo("HalImplInit\n"); + NewIHal((IHal **)&hal); + ((IHal *)hal)->init = HalInit; + ((IHal *)hal)->free = HalFree; +} +StatusCode NewHal(Hal **hal) +{ + if (!hal) { + LogError("STATUS_CODE_INVALID_PARAMENTER\n"); + return CreateStatusCode(STATUS_CODE_INVALID_PARAMENTER); + } + if (!(*hal)) { + *hal = (Hal *)malloc(sizeof(Hal)); + if (*hal) { + LogInfo("NewHal succeed.\n"); + HalImplInit(*hal); + return CreateStatusCode(STATUS_CODE_OK); + } + LogError("NewHal failed.\n"); + return CreateStatusCode(STATUS_CODE_NOT_OK); + } + HalImplInit(*hal); + return CreateStatusCode(STATUS_CODE_OK); +} \ No newline at end of file diff --git a/hal/src/Hal.h b/hal/src/Hal.h new file mode 100644 index 0000000..5f54385 --- /dev/null +++ b/hal/src/Hal.h @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2023 Fancy Code. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef HAL_H +#define HAL_H +#include "IHal.h" +#include "StatusCode.h" +#ifdef __cplusplus +extern "C" { +#endif +typedef struct hal Hal; +typedef struct hal +{ + IHal base; +} Hal; +StatusCode NewHal(Hal **hal); +void HalImplInit(Hal *hal); +StatusCode HalInit(IHal *hal); +#ifdef __cplusplus +} +#endif +#endif \ No newline at end of file diff --git a/hal/src/HalCpp.cpp b/hal/src/HalCpp.cpp new file mode 100644 index 0000000..088387f --- /dev/null +++ b/hal/src/HalCpp.cpp @@ -0,0 +1,87 @@ +/* + * Copyright (c) 2023 Fancy Code. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "HalCpp.h" +#include "HalMakePtr.h" +#include "ILog.h" +#include "SdCardHal.h" +#include "WifiHal.h" +StatusCode HalCpp::Init(void) +{ + LogInfo("HalCpp::Init\n"); + HalMakePtr::GetInstance()->CreateWifiHal(mWifiHal); + std::shared_ptr wifiImpl = std::dynamic_pointer_cast(mWifiHal); + if (nullptr != wifiImpl) { + wifiImpl->Init(); + } + HalMakePtr::GetInstance()->CreateSdCardHal(mSdCardHal); + std::shared_ptr sdCardImpl = std::dynamic_pointer_cast(mSdCardHal); + if (nullptr != sdCardImpl) { + sdCardImpl->Init(); + } + HalMakePtr::GetInstance()->CreateAllKeyHal(mKeys); + // auto checkPinValue = [](std::shared_ptr impl) { + // LogInfo("HalCpp::CheckAllPinVauleThread start\n"); + // impl->CheckAllPinVauleThread(); + // }; + // mCheckPinThread = std::thread(checkPinValue, shared_from_this()); + return CreateStatusCode(STATUS_CODE_OK); +} +StatusCode HalCpp::UnInit(void) +{ + LogInfo("HalCpp::UnInit\n"); + std::shared_ptr sdCardImpl = std::dynamic_pointer_cast(mSdCardHal); + if (nullptr != sdCardImpl) { + sdCardImpl->UnInit(); + } + mThreadRuning = false; + if (mCheckPinThread.joinable()) { + mCheckPinThread.join(); + } + mWifiHal.reset(); + mSdCardHal.reset(); + return CreateStatusCode(STATUS_CODE_OK); +} +StatusCode HalCpp::GetWifiHal(std::shared_ptr &wifi) +{ + wifi = mWifiHal; + return CreateStatusCode(STATUS_CODE_OK); +} +StatusCode HalCpp::GetSdCardHal(std::shared_ptr &sdCard) +{ + sdCard = mSdCardHal; + return CreateStatusCode(STATUS_CODE_OK); +} +StatusCode HalCpp::GetAllKeys(std::map> &allKeys) +{ + LogInfo("GetAllKeys\n"); + for (auto &key : mKeys) { + std::shared_ptr keyControl = std::dynamic_pointer_cast(key); + if (nullptr == keyControl) { + LogError("keyControl is nullptr\n"); + continue; + } + allKeys.insert(std::make_pair(key->GetKeyName(), keyControl)); + } + return CreateStatusCode(STATUS_CODE_OK); +} +void HalCpp::CheckAllPinVauleThread(void) +{ + // mThreadRuning = true; + // while (mThreadRuning) { + // for (auto &keyHalImpl : mKeys) { + // } + // std::this_thread::sleep_for(std::chrono::milliseconds(PERIPHERAL_CHECK_PERIOD_MS)); + // } +} diff --git a/hal/src/HalCpp.h b/hal/src/HalCpp.h new file mode 100644 index 0000000..87e627b --- /dev/null +++ b/hal/src/HalCpp.h @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2023 Fancy Code. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef HALCPP_H +#define HALCPP_H +#include "IHalCpp.h" +#include "KeyControl.h" +#include +#include +class HalCpp : public IHalCpp, public std::enable_shared_from_this +{ +public: + HalCpp() = default; + virtual ~HalCpp() = default; + StatusCode Init(void) override; + StatusCode UnInit(void) override; + StatusCode GetWifiHal(std::shared_ptr &wifi) override; + StatusCode GetSdCardHal(std::shared_ptr &sdCard) override; + StatusCode GetAllKeys(std::map> &allKeys) override; + +private: + void CheckAllPinVauleThread(void); + +private: + std::vector> mLedHals; + std::shared_ptr mWifiHal; + std::shared_ptr mSdCardHal; + std::vector> mKeys; + bool mThreadRuning = false; + std::thread mCheckPinThread; +}; +#endif \ No newline at end of file diff --git a/hal/src/HalMakePtr.cpp b/hal/src/HalMakePtr.cpp new file mode 100644 index 0000000..69b5bdb --- /dev/null +++ b/hal/src/HalMakePtr.cpp @@ -0,0 +1,100 @@ +/* + * Copyright (c) 2023 Fancy Code. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "HalMakePtr.h" +#include "Hal.h" +#include "HalCpp.h" +#include "ILog.h" +#include "SdCardHal.h" +#include "WifiHal.h" +StatusCode CreateHalModule(void) +{ + IHal *hal = NULL; + StatusCode code = HalMakePtr::GetInstance()->CreateHalPtr(&hal); + if (IsCodeOK(code) && NULL != hal) { + LogInfo("Create Hal instance ok.\n"); + ResetHalImpl((IHal *)hal); + } + else { + LogError("Create Hal failed.\n"); + return CreateStatusCode(STATUS_CODE_NOT_OK); + } + auto instance = std::make_shared(); + StatusCode code2 = HalMakePtr::GetInstance()->CreateHalSharePtr(instance); + if (IsCodeOK(code2)) { + LogInfo("Hal instance is ok.\n"); + IHalCpp::GetInstance(&instance); + } + return code2; +} +StatusCode DestroyHalModule(void) +{ + ResetHalImpl(nullptr); + return CreateStatusCode(STATUS_CODE_OK); +} +void CreateHalCppModule(void) +{ + auto instance = std::make_shared(); + StatusCode code2 = HalMakePtr::GetInstance()->CreateHalSharePtr(instance); + if (IsCodeOK(code2)) { + LogInfo("Hal instance is ok.\n"); + IHalCpp::GetInstance(&instance); + } +} +void DestroyHalCppModule(void) +{ + auto instance = std::make_shared(); + IHalCpp::GetInstance(&instance); +} +std::shared_ptr &HalMakePtr::GetInstance(std::shared_ptr *impl) +{ + static auto instance = std::make_shared(); + if (impl) { + instance = *impl; + } + return instance; +} +StatusCode HalMakePtr::CreateHalPtr(IHal **hal) +{ + LogWarning("Hal is default hal.\n"); + return NewHal((Hal **)hal); +} +StatusCode HalMakePtr::CreateHalSharePtr(std::shared_ptr &impl) +{ + LogWarning("IHalCpp is default.\n"); + impl = std::make_shared(); + return CreateStatusCode(STATUS_CODE_OK); +} +StatusCode HalMakePtr::CreateWifiHal(std::shared_ptr &impl) +{ + LogInfo("CreateWifiHal.\n"); + impl = std::make_shared(); + return CreateStatusCode(STATUS_CODE_OK); +} +StatusCode HalMakePtr::CreateCameraHal(std::shared_ptr &impl) +{ + LogWarning("CreateCameraHal.\n"); + return CreateStatusCode(STATUS_CODE_VIRTUAL_FUNCTION); +} +StatusCode HalMakePtr::CreateSdCardHal(std::shared_ptr &impl) +{ + LogInfo("CreateSdCardHal.\n"); + impl = std::make_shared(); + return CreateStatusCode(STATUS_CODE_OK); +} +StatusCode HalMakePtr::CreateAllKeyHal(std::vector> &keys) +{ + LogInfo("STATUS_CODE_VIRTUAL_FUNCTION.\n"); + return CreateStatusCode(STATUS_CODE_VIRTUAL_FUNCTION); +} \ No newline at end of file diff --git a/hal/src/HalMakePtr.h b/hal/src/HalMakePtr.h new file mode 100644 index 0000000..72dfbf5 --- /dev/null +++ b/hal/src/HalMakePtr.h @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2023 Fancy Code. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef HALMAKEPTR_H +#define HALMAKEPTR_H +#include "IHal.h" +#include "IHalCpp.h" +#include "StatusCode.h" +#include "KeyControl.h" +#include +class HalMakePtr +{ +public: + HalMakePtr() = default; + virtual ~HalMakePtr() = default; + static std::shared_ptr &GetInstance(std::shared_ptr *impl = nullptr); + virtual StatusCode Init() + { + return CreateStatusCode(STATUS_CODE_VIRTUAL_FUNCTION); + } + virtual StatusCode UnInit() + { + return CreateStatusCode(STATUS_CODE_VIRTUAL_FUNCTION); + } + virtual StatusCode CreateHalPtr(IHal **hal); + virtual StatusCode CreateHalSharePtr(std::shared_ptr &impl); + virtual StatusCode CreateWifiHal(std::shared_ptr &impl); + virtual StatusCode CreateCameraHal(std::shared_ptr &impl); + virtual StatusCode CreateSdCardHal(std::shared_ptr &impl); + virtual StatusCode CreateAllKeyHal(std::vector> &keys); +}; +#endif \ No newline at end of file diff --git a/hal/src/SdCardHal.cpp b/hal/src/SdCardHal.cpp new file mode 100644 index 0000000..9582de1 --- /dev/null +++ b/hal/src/SdCardHal.cpp @@ -0,0 +1,191 @@ +/* + * Copyright (c) 2023 Fancy Code. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "SdCardHal.h" +#include "ILog.h" +#include "LinuxApi.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +const char *SD_CARD_DEVICE = SD_CARD_DEV; +const char *SD_MOUNT_PATH = SD_CARD_MOUNT_PATH; +SdCardHal::SdCardHal() : mStatus(SdCardHalStatus::END), mThreadRuning(false), mDeviceFd(-1) +{ +} +void SdCardHal::SetSdCardMonitor(std::shared_ptr &monitor) +{ + LogInfo("SetSdCardMonitor.\n"); + mMonitor = monitor; + monitor->ReportEvent(mStatus); +} +SdCardHalStatus SdCardHal::GetSdCardStatus(void) +{ + return mStatus; +} +StatusCode SdCardHal::GetCapacity(unsigned long long &totalSizeMB, unsigned long long &freeSizeMB, + unsigned long long &usedSizeMB) +{ + using SD_SIZE = unsigned long long; + constexpr SD_SIZE ONE_MB = 1024ULL * 1024; + if (SdCardHalStatus::MOUNTED != mStatus) { + LogWarning("SdCardHalStatus::MOUNTED != mStatus\n"); + return CreateStatusCode(STATUS_CODE_NOT_OK); + } + struct statfs sd_fs; + if (statfs(SD_MOUNT_PATH, &sd_fs) != 0) { + LogError("statfs failed!\n"); + return CreateStatusCode(STATUS_CODE_NOT_OK); + } + + totalSizeMB = ((SD_SIZE)sd_fs.f_blocks * (SD_SIZE)sd_fs.f_bsize) / ONE_MB; + freeSizeMB = ((SD_SIZE)sd_fs.f_bavail * (SD_SIZE)sd_fs.f_bsize) / ONE_MB; + usedSizeMB = (((SD_SIZE)sd_fs.f_blocks - (SD_SIZE)sd_fs.f_bfree) * (SD_SIZE)sd_fs.f_bsize) / ONE_MB; + LogInfo("Total size: %llu MB\n", totalSizeMB); + LogInfo("Free size: %llu MB\n", freeSizeMB); + LogInfo("Used size: %llu MB\n", usedSizeMB); + return CreateStatusCode(STATUS_CODE_OK); +} +void SdCardHal::Init(void) +{ + auto detectThread = [](std::shared_ptr sdCardHal) { + LogInfo("sdCardHal DevDetectingThread started.\n"); + sdCardHal->DevDetectingThread(); + }; + mDevDetectingThread = std::thread(detectThread, shared_from_this()); +} +void SdCardHal::UnInit(void) +{ + mThreadRuning = false; + if (mDevDetectingThread.joinable()) { + mDevDetectingThread.join(); + } +} +void SdCardHal::DevDetectingThread(void) +{ + constexpr int SLEEP_TIME_MS = 100; + constexpr int DEVICE_EXIST = 0; + // SdCardHalStatus status = SdCardHalStatus::END; + // const char *SD_CARD_DEVICE = "/SD_CARD_DEVICE/mmcblk1p1"; + mThreadRuning = true; + while (mThreadRuning) { + if (fx_access(SD_CARD_DEVICE, F_OK) != DEVICE_EXIST) { + if (SdCardHalStatus::PULL_OUT_DEVICE_NOT_EXIST != mStatus) { + mStatus = SdCardHalStatus::PULL_OUT_DEVICE_NOT_EXIST; + ReportDetecedChangedResult(mStatus); + } + if (mDeviceFd > 0) { + close(mDeviceFd); + mDeviceFd = -1; + } + goto CONTINUE; + } + if (mDeviceFd < 0) { + mDeviceFd = fx_open(SD_CARD_DEVICE, O_RDONLY); + if (mDeviceFd < 0) { + // LogInfo("sdCardHal: %s open failed.\n", SD_CARD_DEVICE); + if (SdCardHalStatus::PULL_OUT_OPEN_FAILED != mStatus) { + mStatus = SdCardHalStatus::PULL_OUT_OPEN_FAILED; + ReportDetecedChangedResult(mStatus); + } + goto CONTINUE; + } + } + struct stat sdStat; + if (fx_fstat(mDeviceFd, &sdStat) < 0) { + // LogInfo("sdCardHal: %s fstat failed.\n", SD_CARD_DEVICE); + if (SdCardHalStatus::ERROR != mStatus) { + mStatus = SdCardHalStatus::ERROR; + ReportDetecedChangedResult(mStatus); + } + close(mDeviceFd); + mDeviceFd = -1; + goto CONTINUE; + } + if (!S_ISBLK(sdStat.st_mode)) { + // LogInfo("sdCardHal: %s is not block device.\n", SD_CARD_DEVICE); + if (SdCardHalStatus::PULL_OUT_NOT_BLOCK_DEVICE != mStatus) { + mStatus = SdCardHalStatus::PULL_OUT_NOT_BLOCK_DEVICE; + ReportDetecedChangedResult(mStatus); + } + close(mDeviceFd); + mDeviceFd = -1; + } + else { + // LogInfo("sdCardHal: %s is inserted.\n", SD_CARD_DEVICE); + if (SdCardHalStatus::INSERTED != mStatus && SdCardHalStatus::MOUNTED != mStatus && + SdCardHalStatus::UNMOUNTED != mStatus) { + mStatus = SdCardHalStatus::INSERTED; + ReportDetecedChangedResult(mStatus); + } + } + CONTINUE: + std::this_thread::sleep_for(std::chrono::milliseconds(SLEEP_TIME_MS)); + } + if (mDeviceFd > 0) { + close(mDeviceFd); + mDeviceFd = -1; + } +} +void SdCardHal::ReportDetecedChangedResult(const SdCardHalStatus &status) +{ + LogInfo("SdCardHalStatus changed: %s.\n", PrintfStatusString(status)); + SdCardHalStatus mountedStatus = SdCardHalStatus::END; + if (SdCardHalStatus::INSERTED == status) { + LogInfo("mount sd to %s.\n", SD_CARD_MOUNT_PATH); + constexpr int BUF_LENGTH = 128; + char cmd[BUF_LENGTH] = {0}; + snprintf(cmd, BUF_LENGTH, "mount %s %s", SD_CARD_DEV, SD_CARD_MOUNT_PATH); + fx_system(cmd); + mountedStatus = SdCardHalStatus::MOUNTED; + mStatus = SdCardHalStatus::MOUNTED; + } + auto monitor = mMonitor.lock(); + if (mMonitor.expired()) { + LogWarning("SdCardHal: monitor is expired.\n"); + return; + } + if (SdCardHalStatus::END != mountedStatus) { + monitor->ReportEvent(mountedStatus); + return; + } + monitor->ReportEvent(status); +} +const char *SdCardHal::PrintfStatusString(const SdCardHalStatus &status) +{ + switch (status) { + case SdCardHalStatus::MOUNTED: + return "MOUNTE\n"; + case SdCardHalStatus::UNMOUNTED: + return "UNMOUNTED."; + case SdCardHalStatus::INSERTED: + return "INSERTED."; + case SdCardHalStatus::PULL_OUT_DEVICE_NOT_EXIST: + return "PULL_OUT_DEVICE_NOT_EXIST."; + case SdCardHalStatus::PULL_OUT_OPEN_FAILED: + return "PULL_OUT_OPEN_FAILED."; + case SdCardHalStatus::PULL_OUT_NOT_BLOCK_DEVICE: + return "PULL_OUT_NOT_BLOCK_DEVICE."; + case SdCardHalStatus::END: + return "END."; + default: + return "UNDEFINE."; + } +} \ No newline at end of file diff --git a/hal/src/SdCardHal.h b/hal/src/SdCardHal.h new file mode 100644 index 0000000..5aabdb7 --- /dev/null +++ b/hal/src/SdCardHal.h @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2023 Fancy Code. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef SD_CARD_HAL_H +#define SD_CARD_HAL_H +#include "IHalCpp.h" +#include +class SdCardHal : public VSdCardHal, public std::enable_shared_from_this +{ +public: + SdCardHal(); + virtual ~SdCardHal() = default; + void SetSdCardMonitor(std::shared_ptr &monitor) override; + SdCardHalStatus GetSdCardStatus(void) override; + StatusCode GetCapacity(unsigned long long &totalSizeMB, unsigned long long &freeSizeMB, unsigned long long &usedSizeMB) override; + void Init(void); + void UnInit(void); + void DevDetectingThread(void); + +private: + void ReportDetecedChangedResult(const SdCardHalStatus &status); + const char *PrintfStatusString(const SdCardHalStatus &status); + +private: + SdCardHalStatus mStatus; + bool mThreadRuning; + std::thread mDevDetectingThread; + std::weak_ptr mMonitor; + int mDeviceFd; +}; +#endif \ No newline at end of file diff --git a/hal/src/WifiHal.cpp b/hal/src/WifiHal.cpp new file mode 100644 index 0000000..8e68435 --- /dev/null +++ b/hal/src/WifiHal.cpp @@ -0,0 +1,90 @@ +/* + * Copyright (c) 2023 Fancy Code. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "WifiHal.h" +#include "ILog.h" +#include "LinuxApi.h" +#include +#include +#include +#include +#include +WifiHal::WifiHal() : mInitRunning(false) +{ +} +StatusCode WifiHal::OpenApMode(void) +{ + LogInfo("OpenApMode. \n"); + constexpr int SLEEP_TIME_MS = 5; + constexpr int WAITING_TIME_MS = 1000 * 10; + unsigned int sleepingTime_ms = 0; + mInitRunning = true; + while (CheckWlan0IfExist() == false) { + std::this_thread::sleep_for(std::chrono::milliseconds(SLEEP_TIME_MS)); + sleepingTime_ms += SLEEP_TIME_MS; + if (sleepingTime_ms > WAITING_TIME_MS) { + LogError("wlan0 not found. \n"); + return CreateStatusCode(STATUS_CODE_NOT_OK); + } + if (false == mInitRunning) { + LogError("Open ap mode stop. \n"); + return CreateStatusCode(STATUS_CODE_NOT_OK); + } + } + LogInfo("wlan0 ok. \n"); + fx_system("ifconfig wlan0 192.168.169.1 netmask 255.255.255.0"); + fx_system("hostapd -d /etc/hostapd.conf &"); + fx_system("touch /var/lib/misc/udhcpd.leases"); + fx_system("udhcpd -f /etc/udhcpd.conf &"); + return CreateStatusCode(STATUS_CODE_OK); +} +StatusCode WifiHal::CloseApMode(void) +{ + mInitRunning = false; + return CreateStatusCode(STATUS_CODE_OK); +} +void WifiHal::Init(void) +{ + PowerOff(); +} +void WifiHal::UnInit(void) +{ +} +bool WifiHal::CheckWlan0IfExist(void) +{ + DIR *dir; + struct dirent *entry; + int wlan0_found = 0; + + dir = opendir("/sys/class/net"); + if (dir == NULL) { + perror("opendir"); + return false; + } + + while ((entry = readdir(dir)) != NULL) { + if (strcmp(entry->d_name, "wlan0") == 0) { + wlan0_found = 1; + break; + } + } + + closedir(dir); + if (wlan0_found) { + return true; + } + else { + return false; + } +} \ No newline at end of file diff --git a/hal/src/WifiHal.h b/hal/src/WifiHal.h new file mode 100644 index 0000000..0089e5a --- /dev/null +++ b/hal/src/WifiHal.h @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2023 Fancy Code. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef WIFI_HAL_H +#define WIFI_HAL_H +#include "IHalCpp.h" +class WifiHal : public VWifiHal +{ +public: + WifiHal(); + virtual ~WifiHal() = default; + StatusCode OpenApMode(void) override; + StatusCode CloseApMode(void) override; + virtual void Init(void); + virtual void UnInit(void); + +protected: + bool CheckWlan0IfExist(void); + +protected: + bool mInitRunning; +}; +#endif \ No newline at end of file diff --git a/utils/CMakeLists.txt b/utils/CMakeLists.txt new file mode 100644 index 0000000..b24fc13 --- /dev/null +++ b/utils/CMakeLists.txt @@ -0,0 +1,20 @@ + +# cmake_minimum_required(VERSION 2.8.0) +add_subdirectory(ConfigBase) +add_subdirectory(StatusCode) +add_subdirectory(Log) +add_subdirectory(SharedData) +add_subdirectory(UartDevice) +add_subdirectory(LinuxApi) +# add_subdirectory(MultiProcess) +add_subdirectory(WebServer) +add_subdirectory(McuProtocol) +add_subdirectory(ModBusCRC16) +add_subdirectory(LedControl) +add_subdirectory(KeyControl) +add_subdirectory(MediaAdapter) +add_subdirectory(FxHttpServer) +add_subdirectory(Servers) +add_subdirectory(TcpModule) +add_subdirectory(UpgradeBase) +add_subdirectory(UpgradeTool) \ No newline at end of file diff --git a/utils/Config/CMakeLists.txt b/utils/Config/CMakeLists.txt new file mode 100644 index 0000000..f6f1e99 --- /dev/null +++ b/utils/Config/CMakeLists.txt @@ -0,0 +1,98 @@ + +include(${CMAKE_SOURCE_DIR_IPCSDK}/build/global_config.cmake) +set(EXECUTABLE_OUTPUT_PATH ${EXEC_OUTPUT_PATH}) +set(LIBRARY_OUTPUT_PATH ${LIBS_OUTPUT_PATH}) + +include_directories( + ./src + ./include + ${UTILS_SOURCE_PATH}/StatusCode/include + ${UTILS_SOURCE_PATH}/Log/include + ${EXTERNAL_SOURCE_PATH}/libconfig/libconfig-1.7.3/lib +) +# link_directories( +# ${EXTERNAL_SOURCE_PATH}/libconfig/libconfig-1.7.3/lib/.libs +# ) + +aux_source_directory(./src SRC_FILES) + +set(TARGET_NAME ConfigBase) +add_library(${TARGET_NAME} STATIC ${SRC_FILES}) +target_link_libraries(${TARGET_NAME} StatusCode Log libconfig.a) + +if ("${COMPILE_IMPROVE_SUPPORT}" MATCHES "true") +add_custom_target( + Config_code_check + COMMAND ${CLANG_TIDY_EXE} + -checks='${CLANG_TIDY_CHECKS}' + --header-filter=.* + --system-headers=false + ${SRC_FILES} + ${CLANG_TIDY_CONFIG} + -p ${PLATFORM_PATH}/cmake-shell + WORKING_DIRECTORY ${UTILS_SOURCE_PATH}/Config +) +add_custom_command( + TARGET ${TARGET_NAME} + PRE_BUILD + COMMAND make Config_code_check + WORKING_DIRECTORY ${PLATFORM_PATH}/cmake-shell/ +) +file(GLOB_RECURSE HEADER_FILES *.h) +add_custom_target( + Config_code_format + COMMAND ${CLANG_FORMAT_EXE} + -style=file + -i ${SRC_FILES} ${HEADER_FILES} + WORKING_DIRECTORY ${UTILS_SOURCE_PATH}/Config +) +add_custom_command( + TARGET ${TARGET_NAME} + PRE_BUILD + COMMAND make Config_code_check + COMMAND make Config_code_format + WORKING_DIRECTORY ${PLATFORM_PATH}/cmake-shell/ +) +endif() + +# build libconfig before make libConfig.a +add_custom_command( + # OUTPUT ${EXTERNAL_SOURCE_PATH}/libconfig/libconfig-1.7.3/lib/.libs/libconfig.a + OUTPUT ${EXTERNAL_LIBS_OUTPUT_PATH}/libconfig.a + COMMAND echo "Build libconfig-1.7.3. COMPILE_HOST = ${COMPILE_HOST}" + # COMMAND tar zxvf libconfig-1.7.3.tar.gz + COMMAND sh build_libconfig.sh ${TARGET_PLATFORM} ${COMPILE_HOST} + COMMAND mv ${EXTERNAL_SOURCE_PATH}/libconfig/libconfig-1.7.3/lib/.libs/libconfig.a ${EXTERNAL_LIBS_OUTPUT_PATH}/libconfig.a + WORKING_DIRECTORY ${EXTERNAL_SOURCE_PATH}/libconfig/ +) +add_custom_target( + libconfig.a + # DEPENDS ${EXTERNAL_SOURCE_PATH}/libconfig/libconfig-1.7.3/lib/.libs/libconfig.a + DEPENDS ${EXTERNAL_LIBS_OUTPUT_PATH}/libconfig.a +) + +# add_custom_command( +# TARGET ${TARGET_NAME} +# PRE_BUILD +# COMMAND make compile_libconfig +# WORKING_DIRECTORY ${PROJECT_ROOT_PATH}/cmake-shell +# ) + +function(config_owner target) + get_target_property(source_files "${target}" SOURCES) + foreach(source_file ${source_files}) + get_property(defs SOURCE "${source_file}" + PROPERTY COMPILE_DEFINITIONS) + get_filename_component(file_name "${source_file}" NAME) + list(APPEND defs "CONFIG_OWNER") + set_property( + SOURCE "${source_file}" + PROPERTY COMPILE_DEFINITIONS ${defs}) + endforeach() +endfunction() + +define_file_name(${TARGET_NAME}) +config_owner(${TARGET_NAME}) + +file(GLOB_RECURSE INSTALL_HEADER_FILES include/*.h) +install(FILES ${INSTALL_HEADER_FILES} DESTINATION include) \ No newline at end of file diff --git a/utils/Config/README.md b/utils/Config/README.md new file mode 100644 index 0000000..e69de29 diff --git a/utils/Config/config.cmake b/utils/Config/config.cmake new file mode 100644 index 0000000..1f8d44d --- /dev/null +++ b/utils/Config/config.cmake @@ -0,0 +1,13 @@ + +function(config_owner target) + get_target_property(source_files "${target}" SOURCES) + foreach(source_file ${source_files}) + get_property(defs SOURCE "${source_file}" + PROPERTY COMPILE_DEFINITIONS) + get_filename_component(file_name "${source_file}" NAME) + list(APPEND defs "CONFIG_OWNER") + set_property( + SOURCE "${source_file}" + PROPERTY COMPILE_DEFINITIONS ${defs}) + endforeach() +endfunction() \ No newline at end of file diff --git a/utils/Config/include/Config.h b/utils/Config/include/Config.h new file mode 100644 index 0000000..81b64e8 --- /dev/null +++ b/utils/Config/include/Config.h @@ -0,0 +1,75 @@ +/* + * Copyright (c) 2023 Fancy Code. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef CONFIG_H +#define CONFIG_H +#include "StatusCode.h" +#ifdef __cplusplus +extern "C" { +#endif +enum CONFIG_CODE +{ + CONFIG_CODE_PARAM_NOT_EXIST = STATUS_CODE_END, + CONFIG_CODE_END +}; +typedef struct v_config VConfig; +typedef struct v_config +{ + const StatusCode (*get_int)(VConfig *, const char *, int *); + const StatusCode (*set_int)(VConfig *, const char *, const int); + const StatusCode (*get_short)(VConfig *, const char *, short *); + const StatusCode (*set_short)(VConfig *, const char *, const short); + const StatusCode (*get_long)(VConfig *, const char *, long *); + const StatusCode (*set_long)(VConfig *, const char *, const long); + const StatusCode (*get_llong)(VConfig *, const char *, long long *); + const StatusCode (*set_llong)(VConfig *, const char *, const long long); + const StatusCode (*get_char)(VConfig *, const char *, char *); + const StatusCode (*set_char)(VConfig *, const char *, const char); + const StatusCode (*get_bool)(VConfig *, const char *, bool *); + const StatusCode (*set_bool)(VConfig *, const char *, const bool); + const StatusCode (*get_float)(VConfig *, const char *, float *); + const StatusCode (*set_float)(VConfig *, const char *, const float); + const StatusCode (*get_double)(VConfig *, const char *, double *); + const StatusCode (*set_double)(VConfig *, const char *, const double); + const StatusCode (*get_string)(VConfig *, const char *, const char **); + const StatusCode (*set_string)(VConfig *, const char *, const char *); + const StatusCode (*save)(VConfig *); +} VConfig; +const StatusCode ConfigInit(void); +const StatusCode ConfigUnInit(void); +VConfig *OpenConfigFile(const char *fileName); +const StatusCode ConfigSaveFile(VConfig *cfg); +void CloseConfigFile(VConfig *cfg); +const StatusCode ConfigGetInt(VConfig *cfg, const char *name, int *value); +const StatusCode ConfigSetInt(VConfig *cfg, const char *name, const int value); +const StatusCode ConfigGetShort(VConfig *cfg, const char *name, short *value); +const StatusCode ConfigSetShort(VConfig *cfg, const char *name, const short value); +const StatusCode ConfigGetLong(VConfig *cfg, const char *name, long *value); +const StatusCode ConfigSetLong(VConfig *cfg, const char *name, const long value); +const StatusCode ConfigGetLLong(VConfig *cfg, const char *name, long long *value); +const StatusCode ConfigSetLLong(VConfig *cfg, const char *name, const long long value); +const StatusCode ConfigGetChar(VConfig *cfg, const char *name, char *value); +const StatusCode ConfigSetChar(VConfig *cfg, const char *name, const char value); +const StatusCode ConfigGetBool(VConfig *cfg, const char *name, bool *value); +const StatusCode ConfigSetBool(VConfig *cfg, const char *name, const bool value); +const StatusCode ConfigGetFloat(VConfig *cfg, const char *name, float *value); +const StatusCode ConfigSetFloat(VConfig *cfg, const char *name, const float value); +const StatusCode ConfigGetDouble(VConfig *cfg, const char *name, double *value); +const StatusCode ConfigSetDouble(VConfig *cfg, const char *name, const double value); +const StatusCode ConfigGetString(VConfig *cfg, const char *name, const char **value); +const StatusCode ConfigSetString(VConfig *cfg, const char *name, const char *value); +#ifdef __cplusplus +} +#endif +#endif \ No newline at end of file diff --git a/utils/Config/src/Config.c b/utils/Config/src/Config.c new file mode 100644 index 0000000..4d143b7 --- /dev/null +++ b/utils/Config/src/Config.c @@ -0,0 +1,171 @@ +/* + * Copyright (c) 2023 Fancy Code. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "Config.h" +#include "ConfigImpl.h" +#include "ILog.h" +#include +const StatusCode ConfigInit(void) +{ + return CreateStatusCode(STATUS_CODE_OK); +} +const StatusCode ConfigUnInit(void) +{ + return CreateStatusCode(STATUS_CODE_OK); +} +VConfig *OpenConfigFile(const char *fileName) +{ + return (VConfig *)NewConfig(fileName); +} +const StatusCode ConfigSaveFile(VConfig *cfg) +{ + if (NULL == cfg) { + return CreateStatusCode(STATUS_CODE_NOT_OK); + } + return cfg->save(cfg); +} +void CloseConfigFile(VConfig *cfg) +{ + if (NULL == cfg) { + LogError("NULL config poniter.\n"); + return; + } + ((Config *)cfg)->close(cfg); +} +const StatusCode ConfigGetInt(VConfig *cfg, const char *name, int *value) +{ + if (NULL == cfg) { + return CreateStatusCode(STATUS_CODE_NOT_OK); + } + return cfg->get_int(cfg, name, value); +} +const StatusCode ConfigSetInt(VConfig *cfg, const char *name, const int value) +{ + if (NULL == cfg) { + return CreateStatusCode(STATUS_CODE_NOT_OK); + } + return cfg->set_int(cfg, name, value); +} +const StatusCode ConfigGetShort(VConfig *cfg, const char *name, short *value) +{ + if (NULL == cfg) { + return CreateStatusCode(STATUS_CODE_NOT_OK); + } + return cfg->get_short(cfg, name, value); +} +const StatusCode ConfigSetShort(VConfig *cfg, const char *name, const short value) +{ + if (NULL == cfg) { + return CreateStatusCode(STATUS_CODE_NOT_OK); + } + return cfg->set_short(cfg, name, value); +} +const StatusCode ConfigGetLong(VConfig *cfg, const char *name, long *value) +{ + if (NULL == cfg) { + return CreateStatusCode(STATUS_CODE_NOT_OK); + } + return cfg->get_long(cfg, name, value); +} +const StatusCode ConfigSetLong(VConfig *cfg, const char *name, const long value) +{ + if (NULL == cfg) { + return CreateStatusCode(STATUS_CODE_NOT_OK); + } + return cfg->set_long(cfg, name, value); +} +const StatusCode ConfigGetLLong(VConfig *cfg, const char *name, long long *value) +{ + if (NULL == cfg) { + return CreateStatusCode(STATUS_CODE_NOT_OK); + } + return cfg->get_llong(cfg, name, value); +} +const StatusCode ConfigSetLLong(VConfig *cfg, const char *name, const long long value) +{ + if (NULL == cfg) { + return CreateStatusCode(STATUS_CODE_NOT_OK); + } + return cfg->set_llong(cfg, name, value); +} +const StatusCode ConfigGetChar(VConfig *cfg, const char *name, char *value) +{ + if (NULL == cfg) { + return CreateStatusCode(STATUS_CODE_NOT_OK); + } + return cfg->get_char(cfg, name, value); +} +const StatusCode ConfigSetChar(VConfig *cfg, const char *name, const char value) +{ + if (NULL == cfg) { + return CreateStatusCode(STATUS_CODE_NOT_OK); + } + return cfg->set_char(cfg, name, value); +} +const StatusCode ConfigGetBool(VConfig *cfg, const char *name, bool *value) +{ + if (NULL == cfg) { + return CreateStatusCode(STATUS_CODE_NOT_OK); + } + return cfg->get_bool(cfg, name, value); +} +const StatusCode ConfigSetBool(VConfig *cfg, const char *name, const bool value) +{ + if (NULL == cfg) { + return CreateStatusCode(STATUS_CODE_NOT_OK); + } + return cfg->set_bool(cfg, name, value); +} +const StatusCode ConfigGetFloat(VConfig *cfg, const char *name, float *value) +{ + if (NULL == cfg) { + return CreateStatusCode(STATUS_CODE_NOT_OK); + } + return cfg->get_float(cfg, name, value); +} +const StatusCode ConfigSetFloat(VConfig *cfg, const char *name, const float value) +{ + if (NULL == cfg) { + return CreateStatusCode(STATUS_CODE_NOT_OK); + } + return cfg->set_float(cfg, name, value); +} +const StatusCode ConfigGetDouble(VConfig *cfg, const char *name, double *value) +{ + if (NULL == cfg) { + return CreateStatusCode(STATUS_CODE_NOT_OK); + } + return cfg->get_double(cfg, name, value); +} +const StatusCode ConfigSetDouble(VConfig *cfg, const char *name, const double value) +{ + if (NULL == cfg) { + return CreateStatusCode(STATUS_CODE_NOT_OK); + } + return cfg->set_double(cfg, name, value); +} +const StatusCode ConfigGetString(VConfig *cfg, const char *name, const char **value) +{ + if (NULL == cfg) { + return CreateStatusCode(STATUS_CODE_NOT_OK); + } + return cfg->get_string(cfg, name, value); +} +const StatusCode ConfigSetString(VConfig *cfg, const char *name, const char *value) +{ + if (NULL == cfg) { + return CreateStatusCode(STATUS_CODE_NOT_OK); + } + return cfg->set_string(cfg, name, value); +} \ No newline at end of file diff --git a/utils/Config/src/ConfigCode.c b/utils/Config/src/ConfigCode.c new file mode 100644 index 0000000..ebb73ee --- /dev/null +++ b/utils/Config/src/ConfigCode.c @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2023 Fancy Code. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "ConfigCode.h" +#include "ILog.h" +#include +static const char *ConfigCodeString[CONFIG_CODE_END - STATUS_CODE_END + 1] = {"CONFIG_CODE_PARAM_NOT_EXIST", + "CONFIG_CODE_END"}; +static const char *PrintStringConfigCode(const StatusCode this) +{ + const int CODE_INDEX = this.mStatusCode - STATUS_CODE_END; + if (STATUS_CODE_END <= this.mStatusCode && this.mStatusCode <= CONFIG_CODE_END) { + LogInfo("Config code = [ %s ]\n", ConfigCodeString[CODE_INDEX]); + return ConfigCodeString[CODE_INDEX]; + } + LogError("Config code undefine.\n"); + return "Config code undefine.\n"; +} +static const bool CodeEqual(const StatusCode code, const char *value) +{ + if (memcmp(value, ConfigCodeString[code.mStatusCode - STATUS_CODE_END], strlen(value)) == 0) { + return true; + } + return false; +} +static StatusCode NewConfigCode(const long int code) +{ + StatusCode result = {PrintStringConfigCode, CodeEqual, code}; + return result; +} +const StatusCode CreateConfigCode(const long int code) +{ + // if (STATUS_CODE_OK <= code && code < STATUS_CODE_END) + // { + // return CreateStatusCode(code); + // } + if (STATUS_CODE_END <= code && code < CONFIG_CODE_END) { + return NewConfigCode(code); + } + LogError("undefined code.\n"); + return CreateStatusCode(STATUS_CODE_END); +} \ No newline at end of file diff --git a/utils/Config/src/ConfigCode.h b/utils/Config/src/ConfigCode.h new file mode 100644 index 0000000..2720e89 --- /dev/null +++ b/utils/Config/src/ConfigCode.h @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2023 Fancy Code. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef CONFIGCODE_H +#define CONFIGCODE_H +#include "Config.h" +#ifdef __cplusplus +extern "C" { +#endif +#ifndef CONFIG_OWNER + #error This is internal file, never include it. +#endif +const StatusCode CreateConfigCode(const long int code); +#ifdef __cplusplus +} +#endif +#endif \ No newline at end of file diff --git a/utils/Config/src/ConfigImpl.c b/utils/Config/src/ConfigImpl.c new file mode 100644 index 0000000..912828a --- /dev/null +++ b/utils/Config/src/ConfigImpl.c @@ -0,0 +1,309 @@ +/* + * Copyright (c) 2023 Fancy Code. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "ConfigImpl.h" +#include "ConfigCode.h" +#include "ILog.h" +#include +#include +#include +#include +#include + +#define CHECK_SHORT_LIMIT(value) (value > SHRT_MAX ? false : (value < SHRT_MIN ? false : true)) +#define CHECK_LONG_LIMIT(value) (value > LONG_MAX ? false : (value < LONG_MIN ? false : true)) +#define CHECK_CHAR_LIMIT(value) (value > CHAR_MAX ? false : (value < CHAR_MIN ? false : true)) +#define CHECK_FLOAT_LIMIT(value) (fabs(value - ((float)value)) < 0.000001 ? false : true) + +static const StatusCode ConfigSaveFileImpl(VConfig *cfg) +{ + /* Write out the new configuration. */ + LogInfo("Save file[%s].\n", ((Config *)cfg)->mFileName); + if (!config_write_file(&(((Config *)cfg)->cfg), ((Config *)cfg)->mFileName)) { + LogError("Save config failed.\n"); + fprintf(stderr, "Error while writing file.\n"); + return CreateStatusCode(STATUS_CODE_NOT_OK); + } + return CreateStatusCode(STATUS_CODE_OK); +} +static void ConfigClose(VConfig *cfg) +{ + if (NULL != cfg) { + config_destroy(&(((Config *)cfg)->cfg)); + if (NULL != ((Config *)cfg)->mFileName) { + free(((Config *)cfg)->mFileName); + ((Config *)cfg)->mFileName = NULL; + } + free(cfg); + } +} +static const StatusCode ConfigGetIntImpl(VConfig *cfg, const char *name, int *value) +{ + int result = 0; + // config_setting_t *root; + result = config_lookup_int(&(((Config *)cfg)->cfg), name, value); + if (CONFIG_FALSE == result) { + return CreateConfigCode(CONFIG_CODE_PARAM_NOT_EXIST); + } + return CreateStatusCode(STATUS_CODE_OK); +} +static const StatusCode ConfigSetIntImpl(VConfig *cfg, const char *name, const int value) +{ + config_setting_t *root, *setting; + root = config_root_setting(&(((Config *)cfg)->cfg)); + setting = config_setting_get_member(root, name); + if (!setting) { + setting = config_setting_add(root, name, CONFIG_TYPE_INT); + } + config_setting_set_int(setting, value); + return CreateStatusCode(STATUS_CODE_OK); +} +static const StatusCode ConfigGetShortImpl(VConfig *cfg, const char *name, short *value) +{ + int intValue = 0; + int result = 0; + result = config_lookup_int(&(((Config *)cfg)->cfg), name, &intValue); + if (CONFIG_FALSE == result || CHECK_SHORT_LIMIT(intValue)) { + return CreateConfigCode(CONFIG_CODE_PARAM_NOT_EXIST); + } + *value = (short)intValue; + return CreateStatusCode(STATUS_CODE_OK); +} +static const StatusCode ConfigSetShortImpl(VConfig *cfg, const char *name, const short value) +{ + config_setting_t *root, *setting; + root = config_root_setting(&(((Config *)cfg)->cfg)); + setting = config_setting_get_member(root, name); + if (!setting) { + setting = config_setting_add(root, name, CONFIG_TYPE_INT); + } + int intValue = value; + config_setting_set_int(setting, intValue); + return CreateStatusCode(STATUS_CODE_OK); +} +static const StatusCode ConfigGetLongImpl(VConfig *cfg, const char *name, long *value) +{ + long long llongValue = 0; + int result = 0; + result = config_lookup_int64(&(((Config *)cfg)->cfg), name, &llongValue); + if (CONFIG_FALSE == result || CHECK_LONG_LIMIT(llongValue)) { + return CreateConfigCode(CONFIG_CODE_PARAM_NOT_EXIST); + } + *value = (long)llongValue; + return CreateStatusCode(STATUS_CODE_OK); +} +static const StatusCode ConfigSetLongImpl(VConfig *cfg, const char *name, const long value) +{ + config_setting_t *root, *setting; + root = config_root_setting(&(((Config *)cfg)->cfg)); + setting = config_setting_get_member(root, name); + if (!setting) { + setting = config_setting_add(root, name, CONFIG_TYPE_INT64); + } + long long llongValue = value; + config_setting_set_int64(setting, llongValue); + return CreateStatusCode(STATUS_CODE_OK); +} +static const StatusCode ConfigGetLLongImpl(VConfig *cfg, const char *name, long long *value) +{ + int result = 0; + result = config_lookup_int64(&(((Config *)cfg)->cfg), name, value); + if (CONFIG_FALSE == result) { + return CreateConfigCode(CONFIG_CODE_PARAM_NOT_EXIST); + } + return CreateStatusCode(STATUS_CODE_OK); +} +static const StatusCode ConfigSetLLongImpl(VConfig *cfg, const char *name, const long long value) +{ + config_setting_t *root, *setting; + root = config_root_setting(&(((Config *)cfg)->cfg)); + setting = config_setting_get_member(root, name); + if (!setting) { + setting = config_setting_add(root, name, CONFIG_TYPE_INT64); + } + config_setting_set_int64(setting, value); + return CreateStatusCode(STATUS_CODE_OK); +} +static const StatusCode ConfigGetCharImpl(VConfig *cfg, const char *name, char *value) +{ + int charValue = 0; + int result = 0; + result = config_lookup_int(&(((Config *)cfg)->cfg), name, &charValue); + if (CONFIG_FALSE == result && CHECK_CHAR_LIMIT(charValue)) { + return CreateConfigCode(CONFIG_CODE_PARAM_NOT_EXIST); + } + *value = (char)charValue; + return CreateStatusCode(STATUS_CODE_OK); +} +static const StatusCode ConfigSetCharImpl(VConfig *cfg, const char *name, const char value) +{ + config_setting_t *root, *setting; + root = config_root_setting(&(((Config *)cfg)->cfg)); + setting = config_setting_get_member(root, name); + if (!setting) { + setting = config_setting_add(root, name, CONFIG_TYPE_INT); + } + int charValue = (int)value; + config_setting_set_int(setting, charValue); + return CreateStatusCode(STATUS_CODE_OK); +} +static const StatusCode ConfigGetBoolImpl(VConfig *cfg, const char *name, bool *value) +{ + int result = 0; + result = config_lookup_bool(&(((Config *)cfg)->cfg), name, (int *)value); + if (CONFIG_FALSE == result) { + return CreateConfigCode(CONFIG_CODE_PARAM_NOT_EXIST); + } + return CreateStatusCode(STATUS_CODE_OK); +} +static const StatusCode ConfigSetBoolImpl(VConfig *cfg, const char *name, const bool value) +{ + config_setting_t *root, *setting; + root = config_root_setting(&(((Config *)cfg)->cfg)); + setting = config_setting_get_member(root, name); + if (!setting) { + setting = config_setting_add(root, name, CONFIG_TYPE_BOOL); + } + config_setting_set_bool(setting, (int)value); + return CreateStatusCode(STATUS_CODE_OK); +} +static const StatusCode ConfigGetFloatImpl(VConfig *cfg, const char *name, float *value) +{ + double dValue = 0; + int result = 0; + result = config_lookup_float(&(((Config *)cfg)->cfg), name, &dValue); + if (CONFIG_FALSE == result || CHECK_FLOAT_LIMIT(dValue)) { + return CreateConfigCode(CONFIG_CODE_PARAM_NOT_EXIST); + } + *value = (float)dValue; + return CreateStatusCode(STATUS_CODE_OK); +} +static const StatusCode ConfigSetFloatImpl(VConfig *cfg, const char *name, const float value) +{ + config_setting_t *root, *setting; + root = config_root_setting(&(((Config *)cfg)->cfg)); + setting = config_setting_get_member(root, name); + if (!setting) { + setting = config_setting_add(root, name, CONFIG_TYPE_FLOAT); + } + double dValue = value; + config_setting_set_float(setting, dValue); + return CreateStatusCode(STATUS_CODE_OK); +} +static const StatusCode ConfigGetDoubleImpl(VConfig *cfg, const char *name, double *value) +{ + int result = 0; + result = config_lookup_float(&(((Config *)cfg)->cfg), name, value); + if (CONFIG_FALSE == result) { + return CreateConfigCode(CONFIG_CODE_PARAM_NOT_EXIST); + } + return CreateStatusCode(STATUS_CODE_OK); +} +static const StatusCode ConfigSetDoubleImpl(VConfig *cfg, const char *name, const double value) +{ + config_setting_t *root, *setting; + root = config_root_setting(&(((Config *)cfg)->cfg)); + setting = config_setting_get_member(root, name); + if (!setting) { + setting = config_setting_add(root, name, CONFIG_TYPE_FLOAT); + } + config_setting_set_float(setting, value); + return CreateStatusCode(STATUS_CODE_OK); +} +static const StatusCode ConfigGetStringImpl(VConfig *cfg, const char *name, const char **value) +{ + int result = 0; + // config_setting_t *root; + result = config_lookup_string(&(((Config *)cfg)->cfg), name, value); + if (CONFIG_FALSE == result) { + return CreateConfigCode(CONFIG_CODE_PARAM_NOT_EXIST); + } + return CreateStatusCode(STATUS_CODE_OK); +} +static const StatusCode ConfigSetStringImpl(VConfig *cfg, const char *name, const char *value) +{ + config_setting_t *root, *setting; + root = config_root_setting(&(((Config *)cfg)->cfg)); + setting = config_setting_get_member(root, name); + if (!setting) { + setting = config_setting_add(root, name, CONFIG_TYPE_STRING); + } + config_setting_set_string(setting, value); + return CreateStatusCode(STATUS_CODE_OK); +} +static void ConfigImplInit(Config *cfg) +{ + if (NULL == cfg) { + LogError("NULL pointer.\n"); + return; + } + cfg->mFileName = NULL; + cfg->close = ConfigClose; + cfg->base.get_int = ConfigGetIntImpl; + cfg->base.set_int = ConfigSetIntImpl; + cfg->base.get_short = ConfigGetShortImpl; + cfg->base.set_short = ConfigSetShortImpl; + cfg->base.get_long = ConfigGetLongImpl; + cfg->base.set_long = ConfigSetLongImpl; + cfg->base.get_llong = ConfigGetLLongImpl; + cfg->base.set_llong = ConfigSetLLongImpl; + cfg->base.get_char = ConfigGetCharImpl; + cfg->base.set_char = ConfigSetCharImpl; + cfg->base.get_bool = ConfigGetBoolImpl; + cfg->base.set_bool = ConfigSetBoolImpl; + cfg->base.get_float = ConfigGetFloatImpl; + cfg->base.set_float = ConfigSetFloatImpl; + cfg->base.get_double = ConfigGetDoubleImpl; + cfg->base.set_double = ConfigSetDoubleImpl; + cfg->base.get_string = ConfigGetStringImpl; + cfg->base.set_string = ConfigSetStringImpl; + cfg->base.save = ConfigSaveFileImpl; +} +Config *NewConfig(const char *fileName) +{ + LogInfo("Config file name = %s\n", fileName); + Config *cfg = (Config *)malloc(sizeof(Config)); + ConfigImplInit(cfg); + config_init(&(cfg->cfg)); + config_set_options(&(cfg->cfg), + (CONFIG_OPTION_FSYNC | CONFIG_OPTION_SEMICOLON_SEPARATORS | + CONFIG_OPTION_COLON_ASSIGNMENT_FOR_GROUPS | CONFIG_OPTION_OPEN_BRACE_ON_SEPARATE_LINE)); +#define FIEL_EXIST 0 + if (FIEL_EXIST == access(fileName, F_OK)) { + if (!config_read_file(&(cfg->cfg), fileName)) { + LogError("Read file failed[%s].\n", fileName); + fprintf(stderr, + "%s:%d - %s\n", + config_error_file(&(cfg->cfg)), + config_error_line(&(cfg->cfg)), + config_error_text(&(cfg->cfg))); + return NULL; + } + } + else { + LogInfo("Config file doesn't exist.\n"); + /* Write out the new configuration. */ + if (!config_write_file(&(cfg->cfg), fileName)) { + fprintf(stderr, "Error while writing file.\n"); + return NULL; + } + } + unsigned int fileLength = strlen(fileName) + 1; + cfg->mFileName = (char *)malloc(fileLength); + memset(cfg->mFileName, 0, fileLength); + if (NULL != cfg->mFileName) { + memcpy(cfg->mFileName, fileName, fileLength - 1); + } + return cfg; +} \ No newline at end of file diff --git a/utils/Config/src/ConfigImpl.h b/utils/Config/src/ConfigImpl.h new file mode 100644 index 0000000..2e3bbad --- /dev/null +++ b/utils/Config/src/ConfigImpl.h @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2023 Fancy Code. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef CONFIGIMPL_H +#define CONFIGIMPL_H +#include "Config.h" +#include +#ifdef __cplusplus +extern "C" { +#endif +#ifndef CONFIG_OWNER + #error This is internal file, never include it. +#endif +typedef struct config Config; +typedef struct config +{ + VConfig base; + void (*close)(VConfig *); + config_t cfg; + char *mFileName; +} Config; +Config *NewConfig(const char *fileName); +#ifdef __cplusplus +} +#endif +#endif \ No newline at end of file diff --git a/utils/ConfigBase/CMakeLists.txt b/utils/ConfigBase/CMakeLists.txt new file mode 100644 index 0000000..8fb7242 --- /dev/null +++ b/utils/ConfigBase/CMakeLists.txt @@ -0,0 +1,79 @@ + +include(${CMAKE_SOURCE_DIR_IPCSDK}/build/global_config.cmake) +include(build/config_base.cmake) +set(EXECUTABLE_OUTPUT_PATH ${EXEC_OUTPUT_PATH}) +set(LIBRARY_OUTPUT_PATH ${LIBS_OUTPUT_PATH}) + +include_directories( + ./src + ./include + ${UTILS_SOURCE_PATH}/StatusCode/include + ${UTILS_SOURCE_PATH}/Log/include + ${EXTERNAL_SOURCE_PATH}/libconfig/libconfig-1.7.3/lib +) +# link_directories( +# ${EXTERNAL_SOURCE_PATH}/libconfig/libconfig-1.7.3/lib/.libs +# ) + +aux_source_directory(./src SRC_FILES) + +set(TARGET_NAME ConfigBase) +add_library(${TARGET_NAME} STATIC ${SRC_FILES}) +target_link_libraries(${TARGET_NAME} StatusCode Log libconfig.a) + +if ("${COMPILE_IMPROVE_SUPPORT}" MATCHES "true") +add_custom_target( + ConfigBase_code_check + COMMAND ${CLANG_TIDY_EXE} + -checks='${CLANG_TIDY_CHECKS}' + --header-filter=.* + --system-headers=false + ${SRC_FILES} + ${CLANG_TIDY_CONFIG} + -p ${PLATFORM_PATH}/cmake-shell + WORKING_DIRECTORY ${UTILS_SOURCE_PATH}/ConfigBase +) +add_custom_command( + TARGET ${TARGET_NAME} + PRE_BUILD + COMMAND make ConfigBase_code_check + WORKING_DIRECTORY ${PLATFORM_PATH}/cmake-shell/ +) +file(GLOB_RECURSE HEADER_FILES *.h) +add_custom_target( + ConfigBase_code_format + COMMAND ${CLANG_FORMAT_EXE} + -style=file + -i ${SRC_FILES} ${HEADER_FILES} + WORKING_DIRECTORY ${UTILS_SOURCE_PATH}/ConfigBase +) +add_custom_command( + TARGET ${TARGET_NAME} + PRE_BUILD + COMMAND make ConfigBase_code_check + COMMAND make ConfigBase_code_format + WORKING_DIRECTORY ${PLATFORM_PATH}/cmake-shell/ +) +endif() + +# build libconfig before make libConfigBase.a +add_custom_command( + # OUTPUT ${EXTERNAL_SOURCE_PATH}/libconfig/libconfig-1.7.3/lib/.libs/libconfig.a + OUTPUT ${EXTERNAL_LIBS_OUTPUT_PATH}/libconfig.a + COMMAND echo "Build libconfig-1.7.3. COMPILE_HOST = ${COMPILE_HOST}" + # COMMAND tar zxvf libconfig-1.7.3.tar.gz + COMMAND sh build_libconfig.sh ${TARGET_PLATFORM} ${COMPILE_HOST} + COMMAND mv ${EXTERNAL_SOURCE_PATH}/libconfig/libconfig-1.7.3/lib/.libs/libconfig.a ${EXTERNAL_LIBS_OUTPUT_PATH}/libconfig.a + WORKING_DIRECTORY ${EXTERNAL_SOURCE_PATH}/libconfig/ +) +add_custom_target( + libconfig.a + # DEPENDS ${EXTERNAL_SOURCE_PATH}/libconfig/libconfig-1.7.3/lib/.libs/libconfig.a + DEPENDS ${EXTERNAL_LIBS_OUTPUT_PATH}/libconfig.a +) + +define_file_name(${TARGET_NAME}) +config_owner(${TARGET_NAME}) + +file(GLOB_RECURSE INSTALL_HEADER_FILES include/*.h) +install(FILES ${INSTALL_HEADER_FILES} DESTINATION include) \ No newline at end of file diff --git a/utils/ConfigBase/build/config_base.cmake b/utils/ConfigBase/build/config_base.cmake new file mode 100644 index 0000000..09434ee --- /dev/null +++ b/utils/ConfigBase/build/config_base.cmake @@ -0,0 +1,12 @@ +function(config_owner target) + get_target_property(source_files "${target}" SOURCES) + foreach(source_file ${source_files}) + get_property(defs SOURCE "${source_file}" + PROPERTY COMPILE_DEFINITIONS) + get_filename_component(file_name "${source_file}" NAME) + list(APPEND defs "CONFIG_OWNER") + set_property( + SOURCE "${source_file}" + PROPERTY COMPILE_DEFINITIONS ${defs}) + endforeach() +endfunction() \ No newline at end of file diff --git a/utils/ConfigBase/include/ConfigBase.h b/utils/ConfigBase/include/ConfigBase.h new file mode 100644 index 0000000..0f2a330 --- /dev/null +++ b/utils/ConfigBase/include/ConfigBase.h @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2023 Fancy Code. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef CONFIG_BASE_H +#define CONFIG_BASE_H +#include "StatusCode.h" +#ifdef __cplusplus +extern "C" { +#endif +enum CONFIG_CODE +{ + CONFIG_CODE_PARAM_NOT_EXIST = STATUS_CODE_END, + CONFIG_CODE_END +}; +// StatusCode ConfigInit(void); +// StatusCode ConfigUnInit(void); +void *OpenConfigFile(const char *fileName); +StatusCode ConfigSaveFile(void *object); +void CloseConfigFile(void *object); +StatusCode ConfigGetInt(void *object, const char *name, int *value); +StatusCode ConfigSetInt(void *object, const char *name, const int value); +StatusCode ConfigGetShort(void *object, const char *name, short *value); +StatusCode ConfigSetShort(void *object, const char *name, const short value); +StatusCode ConfigGetLong(void *object, const char *name, long *value); +StatusCode ConfigSetLong(void *object, const char *name, const long value); +StatusCode ConfigGetLLong(void *object, const char *name, long long *value); +StatusCode ConfigSetLLong(void *object, const char *name, const long long value); +StatusCode ConfigGetChar(void *object, const char *name, char *value); +StatusCode ConfigSetChar(void *object, const char *name, const char value); +StatusCode ConfigGetBool(void *object, const char *name, bool *value); +StatusCode ConfigSetBool(void *object, const char *name, const bool value); +StatusCode ConfigGetFloat(void *object, const char *name, float *value); +StatusCode ConfigSetFloat(void *object, const char *name, const float value); +StatusCode ConfigGetDouble(void *object, const char *name, double *value); +StatusCode ConfigSetDouble(void *object, const char *name, const double value); +StatusCode ConfigGetString(void *object, const char *name, const char **value); +StatusCode ConfigSetString(void *object, const char *name, const char *value); +#ifdef __cplusplus +} +#endif +#endif \ No newline at end of file diff --git a/utils/ConfigBase/src/ConfigBase.cpp b/utils/ConfigBase/src/ConfigBase.cpp new file mode 100644 index 0000000..4c1e54a --- /dev/null +++ b/utils/ConfigBase/src/ConfigBase.cpp @@ -0,0 +1,180 @@ +/* + * Copyright (c) 2023 Fancy Code. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "ConfigBase.h" +#include "IConfigBase.h" +#include "ILog.h" +static bool ObjectCheck(void *object) +{ + if (nullptr == object) { + LogError("nullptr object!\n"); + return false; + } + if (*((const char **)(((char *)object) - sizeof(IConfigBaseHeader))) != GetConfigBaseModuleName()) { + LogError("Illegal object!\n"); + return false; + } + return true; +} +void *OpenConfigFile(const char *fileName) +{ + std::shared_ptr *configObject = NewConfigBase(fileName); + if (nullptr != configObject) { + if ((*configObject)->OpenConfigFile() == false) { + return nullptr; + } + } + return configObject; +} +StatusCode ConfigSaveFile(void *object) +{ + if (ObjectCheck(object) == true) { + return (*(std::shared_ptr *)object)->ConfigSaveFile(); + } + return CreateStatusCode(STATUS_CODE_INVALID_PARAMENTER); +} +void CloseConfigFile(void *object) +{ + if (ObjectCheck(object) == true) { + (*(std::shared_ptr *)object)->CloseConfigFile(); + (*(std::shared_ptr *)object).reset(); + free(((char *)object) - sizeof(IConfigBaseHeader)); // TODO: bug? + } +} +StatusCode ConfigGetInt(void *object, const char *name, int *value) +{ + if (ObjectCheck(object) == true) { + return (*(std::shared_ptr *)object)->ConfigGetInt(name, value); + } + return CreateStatusCode(STATUS_CODE_INVALID_PARAMENTER); +} +StatusCode ConfigSetInt(void *object, const char *name, const int value) +{ + if (ObjectCheck(object) == true) { + return (*(std::shared_ptr *)object)->ConfigSetInt(name, value); + } + return CreateStatusCode(STATUS_CODE_INVALID_PARAMENTER); +} +StatusCode ConfigGetShort(void *object, const char *name, short *value) +{ + if (ObjectCheck(object) == true) { + return (*(std::shared_ptr *)object)->ConfigGetShort(name, value); + } + return CreateStatusCode(STATUS_CODE_INVALID_PARAMENTER); +} +StatusCode ConfigSetShort(void *object, const char *name, const short value) +{ + if (ObjectCheck(object) == true) { + return (*(std::shared_ptr *)object)->ConfigSetShort(name, value); + } + return CreateStatusCode(STATUS_CODE_INVALID_PARAMENTER); +} +StatusCode ConfigGetLong(void *object, const char *name, long *value) +{ + if (ObjectCheck(object) == true) { + return (*(std::shared_ptr *)object)->ConfigGetLong(name, value); + } + return CreateStatusCode(STATUS_CODE_INVALID_PARAMENTER); +} +StatusCode ConfigSetLong(void *object, const char *name, const long value) +{ + if (ObjectCheck(object) == true) { + return (*(std::shared_ptr *)object)->ConfigSetLong(name, value); + } + return CreateStatusCode(STATUS_CODE_INVALID_PARAMENTER); +} +StatusCode ConfigGetLLong(void *object, const char *name, long long *value) +{ + if (ObjectCheck(object) == true) { + return (*(std::shared_ptr *)object)->ConfigGetLLong(name, value); + } + return CreateStatusCode(STATUS_CODE_INVALID_PARAMENTER); +} +StatusCode ConfigSetLLong(void *object, const char *name, const long long value) +{ + if (ObjectCheck(object) == true) { + return (*(std::shared_ptr *)object)->ConfigSetLLong(name, value); + } + return CreateStatusCode(STATUS_CODE_INVALID_PARAMENTER); +} +StatusCode ConfigGetChar(void *object, const char *name, char *value) +{ + if (ObjectCheck(object) == true) { + return (*(std::shared_ptr *)object)->ConfigGetChar(name, value); + } + return CreateStatusCode(STATUS_CODE_INVALID_PARAMENTER); +} +StatusCode ConfigSetChar(void *object, const char *name, const char value) +{ + if (ObjectCheck(object) == true) { + return (*(std::shared_ptr *)object)->ConfigSetChar(name, value); + } + return CreateStatusCode(STATUS_CODE_INVALID_PARAMENTER); +} +StatusCode ConfigGetBool(void *object, const char *name, bool *value) +{ + if (ObjectCheck(object) == true) { + return (*(std::shared_ptr *)object)->ConfigGetBool(name, value); + } + return CreateStatusCode(STATUS_CODE_INVALID_PARAMENTER); +} +StatusCode ConfigSetBool(void *object, const char *name, const bool value) +{ + if (ObjectCheck(object) == true) { + return (*(std::shared_ptr *)object)->ConfigSetBool(name, value); + } + return CreateStatusCode(STATUS_CODE_INVALID_PARAMENTER); +} +StatusCode ConfigGetFloat(void *object, const char *name, float *value) +{ + if (ObjectCheck(object) == true) { + return (*(std::shared_ptr *)object)->ConfigGetFloat(name, value); + } + return CreateStatusCode(STATUS_CODE_INVALID_PARAMENTER); +} +StatusCode ConfigSetFloat(void *object, const char *name, const float value) +{ + if (ObjectCheck(object) == true) { + return (*(std::shared_ptr *)object)->ConfigSetFloat(name, value); + } + return CreateStatusCode(STATUS_CODE_INVALID_PARAMENTER); +} +StatusCode ConfigGetDouble(void *object, const char *name, double *value) +{ + if (ObjectCheck(object) == true) { + return (*(std::shared_ptr *)object)->ConfigGetDouble(name, value); + } + return CreateStatusCode(STATUS_CODE_INVALID_PARAMENTER); +} +StatusCode ConfigSetDouble(void *object, const char *name, const double value) +{ + if (ObjectCheck(object) == true) { + return (*(std::shared_ptr *)object)->ConfigSetDouble(name, value); + } + return CreateStatusCode(STATUS_CODE_INVALID_PARAMENTER); +} +StatusCode ConfigGetString(void *object, const char *name, const char **value) +{ + if (ObjectCheck(object) == true) { + return (*(std::shared_ptr *)object)->ConfigGetString(name, value); + } + return CreateStatusCode(STATUS_CODE_INVALID_PARAMENTER); +} +StatusCode ConfigSetString(void *object, const char *name, const char *value) +{ + if (ObjectCheck(object) == true) { + return (*(std::shared_ptr *)object)->ConfigSetString(name, value); + } + return CreateStatusCode(STATUS_CODE_INVALID_PARAMENTER); +} \ No newline at end of file diff --git a/utils/ConfigBase/src/ConfigBaseCode.c b/utils/ConfigBase/src/ConfigBaseCode.c new file mode 100644 index 0000000..c6dac5a --- /dev/null +++ b/utils/ConfigBase/src/ConfigBaseCode.c @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2023 Fancy Code. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "ConfigBaseCode.h" +#include "ILog.h" +#include +static const char *ConfigCodeString[CONFIG_CODE_END - STATUS_CODE_END + 1] = {"CONFIG_CODE_PARAM_NOT_EXIST", + "CONFIG_CODE_END"}; +static const char *PrintStringConfigCode(const StatusCode this) +{ + const int CODE_INDEX = this.mStatusCode - STATUS_CODE_END; + if (STATUS_CODE_END <= this.mStatusCode && this.mStatusCode <= CONFIG_CODE_END) { + LogInfo("Config code = [ %s ]\n", ConfigCodeString[CODE_INDEX]); + return ConfigCodeString[CODE_INDEX]; + } + LogError("Config code undefine.\n"); + return "Config code undefine.\n"; +} +static const bool CodeEqual(const StatusCode code, const char *value) +{ + if (memcmp(value, ConfigCodeString[code.mStatusCode - STATUS_CODE_END], strlen(value)) == 0) { + return true; + } + return false; +} +static StatusCode NewConfigCode(const long int code) +{ + StatusCode result = {PrintStringConfigCode, CodeEqual, code}; + return result; +} +const StatusCode CreateConfigCode(const long int code) +{ + // if (STATUS_CODE_OK <= code && code < STATUS_CODE_END) + // { + // return CreateStatusCode(code); + // } + if (STATUS_CODE_END <= code && code < CONFIG_CODE_END) { + return NewConfigCode(code); + } + LogError("undefined code.\n"); + return CreateStatusCode(STATUS_CODE_END); +} \ No newline at end of file diff --git a/utils/ConfigBase/src/ConfigBaseCode.h b/utils/ConfigBase/src/ConfigBaseCode.h new file mode 100644 index 0000000..4b070a1 --- /dev/null +++ b/utils/ConfigBase/src/ConfigBaseCode.h @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2023 Fancy Code. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef CONFIG_BASE_CODE_H +#define CONFIG_BASE_CODE_H +#include "ConfigBase.h" +#include "StatusCode.h" +#ifdef __cplusplus +extern "C" { +#endif +#ifndef CONFIG_OWNER + #error This is internal file, never include it. +#endif +const StatusCode CreateConfigCode(const long int code); +#ifdef __cplusplus +} +#endif +#endif \ No newline at end of file diff --git a/utils/ConfigBase/src/ConfigBaseImpl.cpp b/utils/ConfigBase/src/ConfigBaseImpl.cpp new file mode 100644 index 0000000..4fe9e29 --- /dev/null +++ b/utils/ConfigBase/src/ConfigBaseImpl.cpp @@ -0,0 +1,340 @@ +/* + * Copyright (c) 2023 Fancy Code. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "ConfigBaseImpl.h" +#include "ConfigBaseCode.h" +#include "ILog.h" +#include +#include +#include + +constexpr int INVALID_RESULT = -1; +#define CHECK_SHORT_LIMIT(value) (value > SHRT_MAX ? false : (value < SHRT_MIN ? false : true)) +#define CHECK_LONG_LIMIT(value) (value > LONG_MAX ? false : (value < LONG_MIN ? false : true)) +#define CHECK_CHAR_LIMIT(value) (value > CHAR_MAX ? false : (value < CHAR_MIN ? false : true)) +#define CHECK_FLOAT_LIMIT(value) (fabs(value - ((float)value)) < 0.000001 ? false : true) + +ConfigBaseImpl::ConfigBaseImpl(const std::string &fileName) : mFileName(fileName) +{ +} +bool ConfigBaseImpl::OpenConfigFile(void) +{ + config_init(&mCfg); + config_set_options(&mCfg, + (CONFIG_OPTION_FSYNC | CONFIG_OPTION_SEMICOLON_SEPARATORS | + CONFIG_OPTION_COLON_ASSIGNMENT_FOR_GROUPS | CONFIG_OPTION_OPEN_BRACE_ON_SEPARATE_LINE)); + constexpr int FIEL_EXIST = 0; + if (FIEL_EXIST == access(mFileName.c_str(), F_OK)) { + if (!config_read_file(&mCfg, mFileName.c_str())) { + LogError("Read file failed[%s].\n", mFileName.c_str()); + fprintf(stderr, "%s:%d - %s\n", config_error_file(&mCfg), config_error_line(&mCfg), config_error_text(&mCfg)); + return false; + } + } + else { + LogInfo("Config file doesn't exist.mFileName = %s\n", mFileName.c_str()); + /* Write out the new configuration. */ + if (!config_write_file(&mCfg, mFileName.c_str())) { + fprintf(stderr, "Error while writing file.\n"); + return false; + } + } + return true; +} +void ConfigBaseImpl::CloseConfigFile(void) +{ + config_destroy(&mCfg); +} +StatusCode ConfigBaseImpl::ConfigSaveFile(void) +{ + LogInfo("Save file[%s].\n", mFileName.c_str()); + if (!config_write_file(&mCfg, mFileName.c_str())) { + LogError("Save config failed.\n"); + return CreateStatusCode(STATUS_CODE_NOT_OK); + } + return CreateStatusCode(STATUS_CODE_OK); +} +StatusCode ConfigBaseImpl::ConfigGetInt(const char *name, int *value) +{ + int result = INVALID_RESULT; + result = config_lookup_int(&mCfg, name, value); + if (CONFIG_FALSE == result) { + return CreateConfigCode(CONFIG_CODE_PARAM_NOT_EXIST); + } + return CreateStatusCode(STATUS_CODE_OK); +} +StatusCode ConfigBaseImpl::ConfigSetInt(const char *name, const int value) +{ + config_setting_t *root = nullptr; + config_setting_t *setting = nullptr; + root = config_root_setting(&mCfg); + if (nullptr == root) { + LogError("Config function failed.\n"); + return CreateConfigCode(STATUS_CODE_NOT_OK); + } + setting = config_setting_get_member(root, name); + if (nullptr == setting) { + setting = config_setting_add(root, name, CONFIG_TYPE_INT); + } + if (nullptr == setting) { + LogError("Config function failed.\n"); + return CreateConfigCode(STATUS_CODE_NOT_OK); + } + config_setting_set_int(setting, value); + return CreateStatusCode(STATUS_CODE_OK); +} +StatusCode ConfigBaseImpl::ConfigGetShort(const char *name, short *value) +{ + int intValue = 0; + int result = 0; + result = config_lookup_int(&mCfg, name, &intValue); + if (CONFIG_FALSE == result || CHECK_SHORT_LIMIT(intValue)) { + return CreateConfigCode(CONFIG_CODE_PARAM_NOT_EXIST); + } + *value = (short)intValue; + return CreateStatusCode(STATUS_CODE_OK); +} +StatusCode ConfigBaseImpl::ConfigSetShort(const char *name, const short value) +{ + config_setting_t *root = nullptr; + config_setting_t *setting = nullptr; + root = config_root_setting(&mCfg); + if (nullptr == root) { + LogError("Config function failed.\n"); + return CreateConfigCode(STATUS_CODE_NOT_OK); + } + setting = config_setting_get_member(root, name); + if (nullptr == setting) { + setting = config_setting_add(root, name, CONFIG_TYPE_INT); + } + if (nullptr == setting) { + LogError("Config function failed.\n"); + return CreateConfigCode(STATUS_CODE_NOT_OK); + } + int intValue = value; + config_setting_set_int(setting, intValue); + return CreateStatusCode(STATUS_CODE_OK); +} +StatusCode ConfigBaseImpl::ConfigGetLong(const char *name, long *value) +{ + long long llongValue = 0; + int result = 0; + result = config_lookup_int64(&mCfg, name, &llongValue); + if (CONFIG_FALSE == result || CHECK_LONG_LIMIT(llongValue)) { + return CreateConfigCode(CONFIG_CODE_PARAM_NOT_EXIST); + } + *value = (long)llongValue; + return CreateStatusCode(STATUS_CODE_OK); +} +StatusCode ConfigBaseImpl::ConfigSetLong(const char *name, const long value) +{ + config_setting_t *root = nullptr; + config_setting_t *setting = nullptr; + root = config_root_setting(&mCfg); + if (nullptr == root) { + LogError("Config function failed.\n"); + return CreateConfigCode(STATUS_CODE_NOT_OK); + } + setting = config_setting_get_member(root, name); + if (nullptr == setting) { + setting = config_setting_add(root, name, CONFIG_TYPE_INT64); + } + if (nullptr == setting) { + LogError("Config function failed.\n"); + return CreateConfigCode(STATUS_CODE_NOT_OK); + } + long long llongValue = value; + config_setting_set_int64(setting, llongValue); + return CreateStatusCode(STATUS_CODE_OK); +} +StatusCode ConfigBaseImpl::ConfigGetLLong(const char *name, long long *value) +{ + int result = 0; + result = config_lookup_int64(&mCfg, name, value); + if (CONFIG_FALSE == result) { + return CreateConfigCode(CONFIG_CODE_PARAM_NOT_EXIST); + } + return CreateStatusCode(STATUS_CODE_OK); +} +StatusCode ConfigBaseImpl::ConfigSetLLong(const char *name, const long long value) +{ + config_setting_t *root = nullptr; + config_setting_t *setting = nullptr; + root = config_root_setting(&mCfg); + if (nullptr == root) { + LogError("Config function failed.\n"); + return CreateConfigCode(STATUS_CODE_NOT_OK); + } + setting = config_setting_get_member(root, name); + if (nullptr == setting) { + setting = config_setting_add(root, name, CONFIG_TYPE_INT64); + } + if (nullptr == setting) { + LogError("Config function failed.\n"); + return CreateConfigCode(STATUS_CODE_NOT_OK); + } + config_setting_set_int64(setting, value); + return CreateStatusCode(STATUS_CODE_OK); +} +StatusCode ConfigBaseImpl::ConfigGetChar(const char *name, char *value) +{ + int charValue = 0; + int result = 0; + result = config_lookup_int(&mCfg, name, &charValue); + if (CONFIG_FALSE == result && CHECK_CHAR_LIMIT(charValue)) { + return CreateConfigCode(CONFIG_CODE_PARAM_NOT_EXIST); + } + *value = (char)charValue; + return CreateStatusCode(STATUS_CODE_OK); +} +StatusCode ConfigBaseImpl::ConfigSetChar(const char *name, const char value) +{ + config_setting_t *root = nullptr; + config_setting_t *setting = nullptr; + root = config_root_setting(&mCfg); + if (nullptr == root) { + LogError("Config function failed.\n"); + return CreateConfigCode(STATUS_CODE_NOT_OK); + } + setting = config_setting_get_member(root, name); + if (nullptr == setting) { + setting = config_setting_add(root, name, CONFIG_TYPE_INT); + } + if (nullptr == setting) { + LogError("Config function failed.\n"); + return CreateConfigCode(STATUS_CODE_NOT_OK); + } + int charValue = (int)value; + config_setting_set_int(setting, charValue); + return CreateStatusCode(STATUS_CODE_OK); +} +StatusCode ConfigBaseImpl::ConfigGetBool(const char *name, bool *value) +{ + int result = 0; + result = config_lookup_bool(&mCfg, name, (int *)value); + if (CONFIG_FALSE == result) { + return CreateConfigCode(CONFIG_CODE_PARAM_NOT_EXIST); + } + return CreateStatusCode(STATUS_CODE_OK); +} +StatusCode ConfigBaseImpl::ConfigSetBool(const char *name, const bool value) +{ + config_setting_t *root = nullptr; + config_setting_t *setting = nullptr; + root = config_root_setting(&mCfg); + if (nullptr == root) { + LogError("Config function failed.\n"); + return CreateConfigCode(STATUS_CODE_NOT_OK); + } + setting = config_setting_get_member(root, name); + if (nullptr == setting) { + setting = config_setting_add(root, name, CONFIG_TYPE_BOOL); + } + if (nullptr == setting) { + LogError("Config function failed.\n"); + return CreateConfigCode(STATUS_CODE_NOT_OK); + } + config_setting_set_bool(setting, (int)value); + return CreateStatusCode(STATUS_CODE_OK); +} +StatusCode ConfigBaseImpl::ConfigGetFloat(const char *name, float *value) +{ + double dValue = 0; + int result = 0; + result = config_lookup_float(&mCfg, name, &dValue); + if (CONFIG_FALSE == result || CHECK_FLOAT_LIMIT(dValue)) { + return CreateConfigCode(CONFIG_CODE_PARAM_NOT_EXIST); + } + *value = (float)dValue; + return CreateStatusCode(STATUS_CODE_OK); +} +StatusCode ConfigBaseImpl::ConfigSetFloat(const char *name, const float value) +{ + config_setting_t *root = nullptr; + config_setting_t *setting = nullptr; + root = config_root_setting(&mCfg); + if (nullptr == root) { + LogError("Config function failed.\n"); + return CreateConfigCode(STATUS_CODE_NOT_OK); + } + setting = config_setting_get_member(root, name); + if (nullptr == setting) { + setting = config_setting_add(root, name, CONFIG_TYPE_FLOAT); + } + if (nullptr == setting) { + LogError("Config function failed.\n"); + return CreateConfigCode(STATUS_CODE_NOT_OK); + } + double dValue = value; + config_setting_set_float(setting, dValue); + return CreateStatusCode(STATUS_CODE_OK); +} +StatusCode ConfigBaseImpl::ConfigGetDouble(const char *name, double *value) +{ + int result = 0; + result = config_lookup_float(&mCfg, name, value); + if (CONFIG_FALSE == result) { + return CreateConfigCode(CONFIG_CODE_PARAM_NOT_EXIST); + } + return CreateStatusCode(STATUS_CODE_OK); +} +StatusCode ConfigBaseImpl::ConfigSetDouble(const char *name, const double value) +{ + config_setting_t *root = nullptr; + config_setting_t *setting = nullptr; + root = config_root_setting(&mCfg); + if (nullptr == root) { + LogError("Config function failed.\n"); + return CreateConfigCode(STATUS_CODE_NOT_OK); + } + setting = config_setting_get_member(root, name); + if (nullptr == setting) { + setting = config_setting_add(root, name, CONFIG_TYPE_FLOAT); + } + if (nullptr == setting) { + LogError("Config function failed.\n"); + return CreateConfigCode(STATUS_CODE_NOT_OK); + } + config_setting_set_float(setting, value); + return CreateStatusCode(STATUS_CODE_OK); +} +StatusCode ConfigBaseImpl::ConfigGetString(const char *name, const char **value) +{ + int result = 0; + result = config_lookup_string(&mCfg, name, value); + if (CONFIG_FALSE == result) { + return CreateConfigCode(CONFIG_CODE_PARAM_NOT_EXIST); + } + return CreateStatusCode(STATUS_CODE_OK); +} +StatusCode ConfigBaseImpl::ConfigSetString(const char *name, const char *value) +{ + config_setting_t *root = nullptr; + config_setting_t *setting = nullptr; + root = config_root_setting(&mCfg); + if (nullptr == root) { + LogError("Config function failed.\n"); + return CreateConfigCode(STATUS_CODE_NOT_OK); + } + setting = config_setting_get_member(root, name); + if (!setting) { + setting = config_setting_add(root, name, CONFIG_TYPE_STRING); + } + if (nullptr == setting) { + LogError("Config function failed.\n"); + return CreateConfigCode(STATUS_CODE_NOT_OK); + } + config_setting_set_string(setting, value); + return CreateStatusCode(STATUS_CODE_OK); +} \ No newline at end of file diff --git a/utils/ConfigBase/src/ConfigBaseImpl.h b/utils/ConfigBase/src/ConfigBaseImpl.h new file mode 100644 index 0000000..56979ac --- /dev/null +++ b/utils/ConfigBase/src/ConfigBaseImpl.h @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2023 Fancy Code. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef CONFIG_BASE_IMPL_H +#define CONFIG_BASE_IMPL_H +#include "IConfigBase.h" +#include +class ConfigBaseImpl : public IConfigBase +{ +public: + ConfigBaseImpl(const std::string &fileName); + virtual ~ConfigBaseImpl() = default; + bool OpenConfigFile(void) override; + void CloseConfigFile(void) override; + StatusCode ConfigSaveFile(void) override; + StatusCode ConfigGetInt(const char *name, int *value) override; + StatusCode ConfigSetInt(const char *name, const int value) override; + StatusCode ConfigGetShort(const char *name, short *value) override; + StatusCode ConfigSetShort(const char *name, const short value) override; + StatusCode ConfigGetLong(const char *name, long *value) override; + StatusCode ConfigSetLong(const char *name, const long value) override; + StatusCode ConfigGetLLong(const char *name, long long *value) override; + StatusCode ConfigSetLLong(const char *name, const long long value) override; + StatusCode ConfigGetChar(const char *name, char *value) override; + StatusCode ConfigSetChar(const char *name, const char value) override; + StatusCode ConfigGetBool(const char *name, bool *value) override; + StatusCode ConfigSetBool(const char *name, const bool value) override; + StatusCode ConfigGetFloat(const char *name, float *value) override; + StatusCode ConfigSetFloat(const char *name, const float value) override; + StatusCode ConfigGetDouble(const char *name, double *value) override; + StatusCode ConfigSetDouble(const char *name, const double value) override; + StatusCode ConfigGetString(const char *name, const char **value) override; + StatusCode ConfigSetString(const char *name, const char *value) override; + +private: + const std::string mFileName; + config_t mCfg; +}; +#endif \ No newline at end of file diff --git a/utils/ConfigBase/src/IConfigBase.cpp b/utils/ConfigBase/src/IConfigBase.cpp new file mode 100644 index 0000000..55086fb --- /dev/null +++ b/utils/ConfigBase/src/IConfigBase.cpp @@ -0,0 +1,120 @@ +/* + * Copyright (c) 2023 Fancy Code. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "IConfigBase.h" +#include "ConfigBaseImpl.h" +#include "ILog.h" +#include +bool IConfigBase::OpenConfigFile(void) +{ + return false; +} +void IConfigBase::CloseConfigFile(void) +{ +} +StatusCode IConfigBase::ConfigSaveFile(void) +{ + return CreateStatusCode(STATUS_CODE_VIRTUAL_FUNCTION); +} +StatusCode IConfigBase::ConfigGetInt(const char *name, int *value) +{ + return CreateStatusCode(STATUS_CODE_VIRTUAL_FUNCTION); +} +StatusCode IConfigBase::ConfigSetInt(const char *name, const int value) +{ + return CreateStatusCode(STATUS_CODE_VIRTUAL_FUNCTION); +} +StatusCode IConfigBase::ConfigGetShort(const char *name, short *value) +{ + return CreateStatusCode(STATUS_CODE_VIRTUAL_FUNCTION); +} +StatusCode IConfigBase::ConfigSetShort(const char *name, const short value) +{ + return CreateStatusCode(STATUS_CODE_VIRTUAL_FUNCTION); +} +StatusCode IConfigBase::ConfigGetLong(const char *name, long *value) +{ + return CreateStatusCode(STATUS_CODE_VIRTUAL_FUNCTION); +} +StatusCode IConfigBase::ConfigSetLong(const char *name, const long value) +{ + return CreateStatusCode(STATUS_CODE_VIRTUAL_FUNCTION); +} +StatusCode IConfigBase::ConfigGetLLong(const char *name, long long *value) +{ + return CreateStatusCode(STATUS_CODE_VIRTUAL_FUNCTION); +} +StatusCode IConfigBase::ConfigSetLLong(const char *name, const long long value) +{ + return CreateStatusCode(STATUS_CODE_VIRTUAL_FUNCTION); +} +StatusCode IConfigBase::ConfigGetChar(const char *name, char *value) +{ + return CreateStatusCode(STATUS_CODE_VIRTUAL_FUNCTION); +} +StatusCode IConfigBase::ConfigSetChar(const char *name, const char value) +{ + return CreateStatusCode(STATUS_CODE_VIRTUAL_FUNCTION); +} +StatusCode IConfigBase::ConfigGetBool(const char *name, bool *value) +{ + return CreateStatusCode(STATUS_CODE_VIRTUAL_FUNCTION); +} +StatusCode IConfigBase::ConfigSetBool(const char *name, const bool value) +{ + return CreateStatusCode(STATUS_CODE_VIRTUAL_FUNCTION); +} +StatusCode IConfigBase::ConfigGetFloat(const char *name, float *value) +{ + return CreateStatusCode(STATUS_CODE_VIRTUAL_FUNCTION); +} +StatusCode IConfigBase::ConfigSetFloat(const char *name, const float value) +{ + return CreateStatusCode(STATUS_CODE_VIRTUAL_FUNCTION); +} +StatusCode IConfigBase::ConfigGetDouble(const char *name, double *value) +{ + return CreateStatusCode(STATUS_CODE_VIRTUAL_FUNCTION); +} +StatusCode IConfigBase::ConfigSetDouble(const char *name, const double value) +{ + return CreateStatusCode(STATUS_CODE_VIRTUAL_FUNCTION); +} +StatusCode IConfigBase::ConfigGetString(const char *name, const char **value) +{ + return CreateStatusCode(STATUS_CODE_VIRTUAL_FUNCTION); +} +StatusCode IConfigBase::ConfigSetString(const char *name, const char *value) +{ + return CreateStatusCode(STATUS_CODE_VIRTUAL_FUNCTION); +} +static const char *CONFIG_BASE_NAME = "config_base"; +const char *GetConfigBaseModuleName(void) +{ + return CONFIG_BASE_NAME; +} +std::shared_ptr *NewConfigBase(const char *fileName) +{ + LogInfo("Create the config base object.\n"); + ConfigBase *impl = (ConfigBase *)malloc(sizeof(ConfigBase)); + if (nullptr == impl) { + LogError("NewConfigBase::malloc failed.\n"); + return nullptr; + } + ConfigBase tmp; + memcpy((void *)impl, (void *)&tmp, sizeof(ConfigBase)); + impl->mHeader.mCheckName = CONFIG_BASE_NAME; + impl->mIConfigBase = std::make_shared(fileName); + return (std::shared_ptr *)(((char *)impl) + sizeof(IConfigBaseHeader)); +} \ No newline at end of file diff --git a/utils/ConfigBase/src/IConfigBase.h b/utils/ConfigBase/src/IConfigBase.h new file mode 100644 index 0000000..0fec945 --- /dev/null +++ b/utils/ConfigBase/src/IConfigBase.h @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2023 Fancy Code. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef I_CONFIG_BASE_H +#define I_CONFIG_BASE_H +#include "StatusCode.h" +#include +class IConfigBase +{ +public: + IConfigBase() = default; + virtual ~IConfigBase() = default; + virtual bool OpenConfigFile(void); + virtual void CloseConfigFile(void); + virtual StatusCode ConfigSaveFile(void); + virtual StatusCode ConfigGetInt(const char *name, int *value); + virtual StatusCode ConfigSetInt(const char *name, const int value); + virtual StatusCode ConfigGetShort(const char *name, short *value); + virtual StatusCode ConfigSetShort(const char *name, const short value); + virtual StatusCode ConfigGetLong(const char *name, long *value); + virtual StatusCode ConfigSetLong(const char *name, const long value); + virtual StatusCode ConfigGetLLong(const char *name, long long *value); + virtual StatusCode ConfigSetLLong(const char *name, const long long value); + virtual StatusCode ConfigGetChar(const char *name, char *value); + virtual StatusCode ConfigSetChar(const char *name, const char value); + virtual StatusCode ConfigGetBool(const char *name, bool *value); + virtual StatusCode ConfigSetBool(const char *name, const bool value); + virtual StatusCode ConfigGetFloat(const char *name, float *value); + virtual StatusCode ConfigSetFloat(const char *name, const float value); + virtual StatusCode ConfigGetDouble(const char *name, double *value); + virtual StatusCode ConfigSetDouble(const char *name, const double value); + virtual StatusCode ConfigGetString(const char *name, const char **value); + virtual StatusCode ConfigSetString(const char *name, const char *value); +}; +typedef struct i_config_base_header +{ + const char *mCheckName; +} IConfigBaseHeader; +typedef struct config_base +{ + IConfigBaseHeader mHeader; + std::shared_ptr mIConfigBase; +} ConfigBase; +const char *GetConfigBaseModuleName(void); +std::shared_ptr *NewConfigBase(const char *fileName); +#endif \ No newline at end of file diff --git a/utils/FxHttpServer/CMakeLists.txt b/utils/FxHttpServer/CMakeLists.txt new file mode 100644 index 0000000..8fba0ba --- /dev/null +++ b/utils/FxHttpServer/CMakeLists.txt @@ -0,0 +1,68 @@ +include(${CMAKE_SOURCE_DIR_IPCSDK}/build/global_config.cmake) +include(${EXTERNAL_SOURCE_PATH}/goahead-5.2.0/goahead.cmake) +set(EXECUTABLE_OUTPUT_PATH ${EXEC_OUTPUT_PATH}) +set(LIBRARY_OUTPUT_PATH ${LIBS_OUTPUT_PATH}) + +include_directories( + ./src + ./include + ${UTILS_SOURCE_PATH}/StatusCode/include + ${UTILS_SOURCE_PATH}/Log/include + ${EXTERNAL_SOURCE_PATH}/httpserver.h-master + # ${UTILS_SOURCE_PATH}/LinuxApi/include +) +# link_directories( +# ${EXTERNAL_SOURCE_PATH}/libconfig/libconfig-1.7.3/lib/.libs +# ) + +include(CheckFunctionExists) +check_function_exists(epoll_wait EPOLL) +check_function_exists(kqueue KQUEUE) + +aux_source_directory(./src SRC_FILES) + +set(TARGET_NAME FxHttpServer) +add_library(${TARGET_NAME} STATIC ${SRC_FILES}) +target_link_libraries(${TARGET_NAME} StatusCode Log -Wl,--start-group httpsrv -Wl,--end-group) + +if ("${COMPILE_IMPROVE_SUPPORT}" MATCHES "true") +add_custom_target( + FxHttpServer_code_check + COMMAND ${CLANG_TIDY_EXE} + -checks='${CLANG_TIDY_CHECKS}' + --header-filter=.* + --system-headers=false + ${SRC_FILES} + ${CLANG_TIDY_CONFIG} + -p ${PLATFORM_PATH}/cmake-shell + WORKING_DIRECTORY ${UTILS_SOURCE_PATH}/FxHttpServer +) +file(GLOB_RECURSE HEADER_FILES *.h) +add_custom_target( + FxHttpServer_code_format + COMMAND ${CLANG_FORMAT_EXE} + -style=file + -i ${SRC_FILES} ${HEADER_FILES} + WORKING_DIRECTORY ${UTILS_SOURCE_PATH}/FxHttpServer +) +add_custom_command( + TARGET ${TARGET_NAME} + PRE_BUILD + COMMAND make FxHttpServer_code_check + COMMAND make FxHttpServer_code_format + WORKING_DIRECTORY ${PLATFORM_PATH}/cmake-shell/ +) +endif() + +if(KQUEUE) + target_compile_definitions(${TARGET_NAME} PRIVATE KQUEUE) +endif() + +if(EPOLL) + target_compile_definitions(${TARGET_NAME} PRIVATE EPOLL) +endif() + +define_file_name(${TARGET_NAME}) + +file(GLOB_RECURSE INSTALL_HEADER_FILES include/*.h) +install(FILES ${INSTALL_HEADER_FILES} DESTINATION include) \ No newline at end of file diff --git a/utils/FxHttpServer/README.md b/utils/FxHttpServer/README.md new file mode 100644 index 0000000..6cb96ce --- /dev/null +++ b/utils/FxHttpServer/README.md @@ -0,0 +1,62 @@ +# 1. httpserver组件库 + +   使用/external/httpserver.h-master/src/CMakeLists.txt文件编译的开源库,二次封装接口给到应用层使用。 + +## 1.1. 注意事项 +1. 编译libhttpsrv时,可能会提示 ragel 工具未安装,需要安装 ragel 工具: +``` +$ sudo apt install ragel +``` +2. 对开源库的CMakeLists.txt文件增加拷贝命令: +``` +message("${PLATFORM_PATH}/cmake-shell/external${SUBMODULE_PATH_OF_IPC_SDK}/httpserver.h-master/src/libhttpsrv.a") +add_custom_command( + TARGET httpsrv + POST_BUILD + COMMAND cp ${PLATFORM_PATH}/cmake-shell${SUBMODULE_PATH_OF_IPC_SDK}/external/httpserver.h-master/src/libhttpsrv.a ${EXTERNAL_LIBS_OUTPUT_PATH} + WORKING_DIRECTORY ${PLATFORM_PATH}/cmake-shell/ +) +``` +3. 取消开源库的CMakeLists.txt文件debug配置,未知会产生什么不良后果; +``` +PUBLIC $<$:-fsanitize=address -fsanitize=undefined -fno-sanitize-recover=all> +``` +4. 由于开源代码不支持安全退出,所以修改了开源代码; +``` +int gHttpServerRuning = 1; // 增加一个运行标识 +int hs_server_run_event_loop(http_server_t *serv, const char *ipaddr) { + hs_server_listen_on_addr(serv, ipaddr); + struct epoll_event ev_list[1]; + while (gHttpServerRuning) { // 运行标识赋值为0时,httpserver退出 + int nev = epoll_wait(serv->loop, ev_list, 1, -1); + for (int i = 0; i < nev; i++) { + ev_cb_t *ev_cb = (ev_cb_t *)ev_list[i].data.ptr; + ev_cb->handler(&ev_list[i]); + } + } + return 0; +} +``` +5. 修复一个内存安全漏洞; +``` +void _hs_accept_and_begin_request_cycle(http_server_t *server, + hs_io_cb_t on_client_connection_cb, + hs_io_cb_t on_timer_event_cb) { + http_request_t *request = NULL; + while ((request = hs_server_accept_connection(server, on_client_connection_cb, + on_timer_event_cb))) { + if (server->memused > HTTP_MAX_TOTAL_EST_MEM_USAGE) { + hs_request_respond_error(request, 503, "Service Unavailable", + hs_request_begin_write); + } else { + hs_request_begin_read(request); + } + // ================ added by xiao ================ // + if (request) { + hs_request_terminate_connection(request); // 此处应该释放内存 + } + // ================ added by xiao end ================ // + } +} +``` +6. \ No newline at end of file diff --git a/utils/FxHttpServer/include/FxHttpServer.h b/utils/FxHttpServer/include/FxHttpServer.h new file mode 100644 index 0000000..ae1647a --- /dev/null +++ b/utils/FxHttpServer/include/FxHttpServer.h @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2023 Fancy Code. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef FX_HTTP_SERVER_H +#define FX_HTTP_SERVER_H +#include "StatusCode.h" +#ifdef __cplusplus +extern "C" { +#endif +typedef void (*ResponseHandle)(const char *, void *); +typedef void (*HttpHandleCallback)(const char *, const unsigned int, ResponseHandle, void *); +StatusCode FxHttpServerInit(HttpHandleCallback httpHandle, const int port); +StatusCode FxHttpServerExit(void); +StatusCode FxHttpServerUnInit(void); +#ifdef __cplusplus +} +#endif +#endif \ No newline at end of file diff --git a/utils/FxHttpServer/src/FxHttpServer.c b/utils/FxHttpServer/src/FxHttpServer.c new file mode 100644 index 0000000..be3813c --- /dev/null +++ b/utils/FxHttpServer/src/FxHttpServer.c @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2023 Fancy Code. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "FxHttpServer.h" +#include "ILog.h" +#include "httpserver.h" +#include +extern int gHttpServerRuning; +static struct http_server_s *server = NULL; +static struct http_server_s *poll_server = NULL; +static HttpHandleCallback gHttpHandle = NULL; +static void response_handle(const char *responseStr, void *context) +{ + struct http_response_s *response = (struct http_response_s *)context; + if (NULL != responseStr) { + http_response_header(response, "Content-Type", "text/plain"); + http_response_body(response, responseStr, strlen(responseStr)); + } +} +static void handle_request(struct http_request_s *request) +{ + http_request_connection(request, HTTP_AUTOMATIC); + struct http_response_s *response = http_response_init(); + http_response_status(response, 200); + http_string_t url = http_request_target(request); + gHttpHandle(url.buf, url.len, response_handle, response); + http_respond(request, response); +} +StatusCode FxHttpServerInit(HttpHandleCallback httpHandle, const int port) +{ + // poll_server = http_server_init(port + 1, handle_request); + if (NULL == httpHandle) { + LogError("FxHttpServerInit failed. Callback function is nullptr.\n"); + return CreateStatusCode(STATUS_CODE_NOT_OK); + } + server = http_server_init(port, handle_request); + if (!server) { + LogError("http_server_init failed.\n"); + return CreateStatusCode(STATUS_CODE_NOT_OK); + } + gHttpHandle = httpHandle; + // http_server_listen_poll(poll_server); + http_server_listen(server); + return CreateStatusCode(STATUS_CODE_OK); +} +StatusCode FxHttpServerExit(void) +{ + gHttpServerRuning = 0; + return CreateStatusCode(STATUS_CODE_OK); +} +StatusCode FxHttpServerUnInit(void) +{ + free(server); + server = NULL; + free(poll_server); + poll_server = NULL; + return CreateStatusCode(STATUS_CODE_OK); +} \ No newline at end of file diff --git a/utils/KeyControl/CMakeLists.txt b/utils/KeyControl/CMakeLists.txt new file mode 100644 index 0000000..102e6a1 --- /dev/null +++ b/utils/KeyControl/CMakeLists.txt @@ -0,0 +1,57 @@ +include(${CMAKE_SOURCE_DIR_IPCSDK}/build/global_config.cmake) +set(EXECUTABLE_OUTPUT_PATH ${EXEC_OUTPUT_PATH}) +set(LIBRARY_OUTPUT_PATH ${LIBS_OUTPUT_PATH}) + +include_directories( + ./src + ./include + # ${UTILS_SOURCE_PATH}/LinuxApi/include + ${UTILS_SOURCE_PATH}/StatusCode/include + ${UTILS_SOURCE_PATH}/ModBusCRC16/include + ${UTILS_SOURCE_PATH}/Log/include +) +# link_directories( +# ${EXTERNAL_SOURCE_PATH}/libconfig/libconfig-1.7.3/lib/.libs +# ) + + + +aux_source_directory(./src SRC_FILES) + +set(TARGET_NAME KeyControl) +add_library(${TARGET_NAME} STATIC ${SRC_FILES}) +target_link_libraries(${TARGET_NAME} StatusCode Log) + +if ("${COMPILE_IMPROVE_SUPPORT}" MATCHES "true") +add_custom_target( + KeyControl_code_check + COMMAND ${CLANG_TIDY_EXE} + -checks='${CLANG_TIDY_CHECKS}' + --header-filter=.* + --system-headers=false + ${SRC_FILES} + ${CLANG_TIDY_CONFIG} + -p ${PLATFORM_PATH}/cmake-shell + WORKING_DIRECTORY ${UTILS_SOURCE_PATH}/KeyControl +) +file(GLOB_RECURSE HEADER_FILES *.h) +add_custom_target( + KeyControl_code_format + COMMAND ${CLANG_FORMAT_EXE} + -style=file + -i ${SRC_FILES} ${HEADER_FILES} + WORKING_DIRECTORY ${UTILS_SOURCE_PATH}/KeyControl +) +add_custom_command( + TARGET ${TARGET_NAME} + PRE_BUILD + COMMAND make KeyControl_code_check + COMMAND make KeyControl_code_format + WORKING_DIRECTORY ${PLATFORM_PATH}/cmake-shell/ +) +endif() + +define_file_name(${TARGET_NAME}) + +file(GLOB_RECURSE INSTALL_HEADER_FILES include/*.h) +install(FILES ${INSTALL_HEADER_FILES} DESTINATION include) \ No newline at end of file diff --git a/utils/KeyControl/include/KeyControl.h b/utils/KeyControl/include/KeyControl.h new file mode 100644 index 0000000..24c50f4 --- /dev/null +++ b/utils/KeyControl/include/KeyControl.h @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2023 Fancy Code. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef KEY_CONTROL_H +#define KEY_CONTROL_H +#include +#include +#include +constexpr int KEY_ACTION_SHORT_CLICK = 100; +constexpr int KEY_ACTION_HOLD_DWON = 500; +constexpr long int KEY_NOT_PRESSING = -1; +enum class KeyHalEvent +{ + PRESSING = 0, + NOT_PRESSING, + END +}; +enum class KeyEvent +{ + SHORT_CLICK = 0, + HOLD_DOWN, + HOLD_UP, + END +}; +class VKeyControl +{ +public: + VKeyControl() = default; + virtual ~VKeyControl() = default; + virtual const std::string GetKeyName(void); + virtual unsigned int GetStatusCheckPeriodMs(void); + virtual void KeyEventTrigger(const std::string &keyName, const KeyEvent &event, const unsigned int &timeMs); +}; +class KeyControl : public VKeyControl, public std::enable_shared_from_this +{ +public: + KeyControl(); + virtual ~KeyControl(); + virtual void Init(void); + virtual void UnInit(void); + void KeyHalEventHandle(const std::string &key, const KeyHalEvent &keyEvent); + void TimerKeyEventTrigger(const KeyHalEvent &event); + void KeyHalEventTrigger(const KeyHalEvent &event); + long int GetHoldPressingTimeMs(void); + +private: + void KeyPressingTrigger(const std::string &key); + void KeyNotPressingTrigger(const std::string &key); + bool IsKeyPressing(void); + +private: + std::mutex mMutex; + long int mPressingTime; + long int mLongClickTime; +}; +const char *PrintKeyEvent(const KeyEvent &event); +#endif \ No newline at end of file diff --git a/utils/KeyControl/src/KeyControl.cpp b/utils/KeyControl/src/KeyControl.cpp new file mode 100644 index 0000000..01ab7e0 --- /dev/null +++ b/utils/KeyControl/src/KeyControl.cpp @@ -0,0 +1,127 @@ +/* + * Copyright (c) 2023 Fancy Code. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "KeyControl.h" +#include "ILog.h" +constexpr long int KEY_PRESSING = 0; +constexpr unsigned int NOT_A_HOLD_KEY_ACTION = 0; +KeyControl::KeyControl() +{ + mPressingTime = KEY_NOT_PRESSING; + mLongClickTime = 0; +} +KeyControl::~KeyControl() +{ +} +bool KeyControl::IsKeyPressing(void) +{ + return mPressingTime >= KEY_PRESSING ? true : false; +} +const std::string VKeyControl::GetKeyName(void) +{ + return "undefine"; +} +unsigned int VKeyControl::GetStatusCheckPeriodMs(void) +{ + return 0; +} +void VKeyControl::KeyEventTrigger(const std::string &keyName, const KeyEvent &event, const unsigned int &timeMs) +{ +} +void KeyControl::Init(void) +{ +} +void KeyControl::UnInit(void) +{ +} +void KeyControl::KeyHalEventHandle(const std::string &key, const KeyHalEvent &keyEvent) +{ + if (KEY_PRESSING <= mPressingTime) { + mPressingTime += GetStatusCheckPeriodMs(); + } + switch (keyEvent) { + case KeyHalEvent::PRESSING: + KeyPressingTrigger(key); + break; + case KeyHalEvent::NOT_PRESSING: + KeyNotPressingTrigger(key); + break; + + default: + break; + } +} +void KeyControl::TimerKeyEventTrigger(const KeyHalEvent &event) +{ + std::lock_guard locker(mMutex); + if (IsKeyPressing()) { + KeyHalEventHandle(GetKeyName(), event); + } +} +void KeyControl::KeyHalEventTrigger(const KeyHalEvent &event) +{ + std::lock_guard locker(mMutex); + KeyHalEventHandle(GetKeyName(), event); +} +long int KeyControl::GetHoldPressingTimeMs(void) +{ + return mPressingTime; +} +void KeyControl::KeyPressingTrigger(const std::string &key) +{ + if (mLongClickTime <= mPressingTime) { + /** + * @brief Long press events are not currently supported here. Developers believe that it is better to handle + * long press events at the application level. + */ + } + // LogInfo("KeyPressingTrigger: %s, %d", key.c_str(), mPressingTime); + if (mPressingTime != KEY_NOT_PRESSING && mPressingTime % KEY_ACTION_HOLD_DWON == 0) { + KeyEventTrigger(key, KeyEvent::HOLD_DOWN, mPressingTime); + } + if (KEY_NOT_PRESSING == mPressingTime) { + mPressingTime = KEY_PRESSING; + } +} +void KeyControl::KeyNotPressingTrigger(const std::string &key) +{ + if (KEY_ACTION_SHORT_CLICK <= mPressingTime && mPressingTime < KEY_ACTION_HOLD_DWON) { + KeyEventTrigger(key, KeyEvent::SHORT_CLICK, mPressingTime); + } + if (KEY_ACTION_HOLD_DWON <= mPressingTime) { + KeyEventTrigger(key, KeyEvent::HOLD_UP, mPressingTime); + } + mPressingTime = KEY_NOT_PRESSING; +} +const char *PrintKeyEvent(const KeyEvent &event) +{ + switch (event) { + case KeyEvent::SHORT_CLICK: { + return "SHORT_CLICK"; + break; + } + case KeyEvent::HOLD_DOWN: { + return "HOLD_DOWN"; + break; + } + case KeyEvent::HOLD_UP: { + return "HOLD_UP"; + break; + } + + default: + return "unknown event"; + break; + } +} \ No newline at end of file diff --git a/utils/LedControl/CMakeLists.txt b/utils/LedControl/CMakeLists.txt new file mode 100644 index 0000000..6f212f6 --- /dev/null +++ b/utils/LedControl/CMakeLists.txt @@ -0,0 +1,57 @@ +include(${CMAKE_SOURCE_DIR_IPCSDK}/build/global_config.cmake) +set(EXECUTABLE_OUTPUT_PATH ${EXEC_OUTPUT_PATH}) +set(LIBRARY_OUTPUT_PATH ${LIBS_OUTPUT_PATH}) + +include_directories( + ./src + ./include + # ${UTILS_SOURCE_PATH}/LinuxApi/include + ${UTILS_SOURCE_PATH}/StatusCode/include + ${UTILS_SOURCE_PATH}/ModBusCRC16/include + ${UTILS_SOURCE_PATH}/Log/include +) +# link_directories( +# ${EXTERNAL_SOURCE_PATH}/libconfig/libconfig-1.7.3/lib/.libs +# ) + + + +aux_source_directory(./src SRC_FILES) + +set(TARGET_NAME LedControl) +add_library(${TARGET_NAME} STATIC ${SRC_FILES}) +target_link_libraries(${TARGET_NAME} StatusCode Log) + +if ("${COMPILE_IMPROVE_SUPPORT}" MATCHES "true") +add_custom_target( + LedControl_code_check + COMMAND ${CLANG_TIDY_EXE} + -checks='${CLANG_TIDY_CHECKS}' + --header-filter=.* + --system-headers=false + ${SRC_FILES} + ${CLANG_TIDY_CONFIG} + -p ${PLATFORM_PATH}/cmake-shell + WORKING_DIRECTORY ${UTILS_SOURCE_PATH}/LedControl +) +file(GLOB_RECURSE HEADER_FILES *.h) +add_custom_target( + LedControl_code_format + COMMAND ${CLANG_FORMAT_EXE} + -style=file + -i ${SRC_FILES} ${HEADER_FILES} + WORKING_DIRECTORY ${UTILS_SOURCE_PATH}/LedControl +) +add_custom_command( + TARGET ${TARGET_NAME} + PRE_BUILD + COMMAND make LedControl_code_check + COMMAND make LedControl_code_format + WORKING_DIRECTORY ${PLATFORM_PATH}/cmake-shell/ +) +endif() + +define_file_name(${TARGET_NAME}) + +file(GLOB_RECURSE INSTALL_HEADER_FILES include/*.h) +install(FILES ${INSTALL_HEADER_FILES} DESTINATION include) \ No newline at end of file diff --git a/utils/LedControl/include/LedControl.h b/utils/LedControl/include/LedControl.h new file mode 100644 index 0000000..af95786 --- /dev/null +++ b/utils/LedControl/include/LedControl.h @@ -0,0 +1,94 @@ +/* + * Copyright (c) 2023 Fancy Code. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef LED_CONTROL_H +#define LED_CONTROL_H +#include "StatusCode.h" +#include +#include +constexpr unsigned int NEW_TOP_LED_STATE = 0; +constexpr unsigned int LED_NOT_BLINK = 0; +constexpr unsigned int BLINKING_FAST_MS = 500; +constexpr unsigned int BLINKING_SLOW_MS = 1000; +constexpr long int KEEP_ALIVE_FOREVER = 0; +constexpr unsigned int DELETED_LED_STATE = -1; +constexpr unsigned int DO_NOT_KEEP_ALIVE = -2; +enum class LedState +{ + OFF = 0, + ON, + GREEN, + RED, + BLUE, + YELLOW, + GREEN_RED_MEANS_YELLOW, + GREEN_BLUE_MEANS_CYAN, + RED_BLUE_MEANS_PURPLE, + GREEN_RED_BLUE_MEANS_WHITE, + LEVEL_0, + LEVEL_1, + LEVEL_2, + LEVEL_3, + LEVEL_4, + LEVEL_END, + END +}; +class VSingleControl +{ +public: + VSingleControl() = default; + virtual ~VSingleControl() = default; + virtual StatusCode GetLedState(LedState &state); + virtual unsigned int GetKeepAliveTimeMs(void); + virtual unsigned int GetBlinkTimeMs(void); +}; +class VLedControl +{ +public: + VLedControl() = default; + virtual ~VLedControl() = default; + virtual StatusCode SetLedState(const LedState &state); + virtual void AddLedState(std::shared_ptr &control); + virtual void CheckState(const unsigned int &period); + virtual std::string GetLedName(void); + virtual void DeleteAllState(void); +}; +class LedControl : virtual public VLedControl +{ +public: + LedControl() = default; + virtual ~LedControl() = default; + void CheckState(const unsigned int &period) override; + void AddLedState(std::shared_ptr &state) override; + +private: + void DeleteAllState(void) override; + /** + * @brief Each time you control a light, check for invalid data to avoid wasting memory resources. + * + */ + void DeleteUselessState(void); + void NewLedStateStart(void); + void DeleteTopLedState(void); + void BlinkOff(std::shared_ptr &state); + void BlinkOn(std::shared_ptr &state, const LedState &onState); + +private: + std::vector> mStates; + LedState mCurrentState; + unsigned int mStateAliveTime; + unsigned int mBlinkPeriod; +}; +const char *PrintLedState(const LedState &state); +#endif \ No newline at end of file diff --git a/utils/LedControl/src/LedControl.cpp b/utils/LedControl/src/LedControl.cpp new file mode 100644 index 0000000..3191ae7 --- /dev/null +++ b/utils/LedControl/src/LedControl.cpp @@ -0,0 +1,179 @@ +/* + * Copyright (c) 2023 Fancy Code. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "LedControl.h" +#include "ILog.h" +#include +StatusCode VSingleControl::GetLedState(LedState &state) +{ + return CreateStatusCode(STATUS_CODE_VIRTUAL_FUNCTION); +} +unsigned int VSingleControl::GetKeepAliveTimeMs(void) +{ + return KEEP_ALIVE_FOREVER; +} +unsigned int VSingleControl::GetBlinkTimeMs(void) +{ + return LED_NOT_BLINK; +} +StatusCode VLedControl::SetLedState(const LedState &state) +{ + LogWarning("STATUS_CODE_VIRTUAL_FUNCTION.\n"); + return CreateStatusCode(STATUS_CODE_VIRTUAL_FUNCTION); +} +void VLedControl::AddLedState(std::shared_ptr &control) +{ + LogWarning("STATUS_CODE_VIRTUAL_FUNCTION.\n"); +} +void VLedControl::CheckState(const unsigned int &period) +{ + LogWarning("STATUS_CODE_VIRTUAL_FUNCTION.\n"); +} +std::string VLedControl::GetLedName(void) +{ + LogWarning("STATUS_CODE_VIRTUAL_FUNCTION.\n"); + return "undefine"; +} +void VLedControl::DeleteAllState(void) +{ + LogWarning("STATUS_CODE_VIRTUAL_FUNCTION.\n"); +} +void LedControl::AddLedState(std::shared_ptr &state) +{ + LogInfo("Add led state.\n"); + DeleteUselessState(); + NewLedStateStart(); + LedState ledState = LedState::END; + state->GetLedState(ledState); + SetLedState(ledState); + mCurrentState = ledState; + if (DO_NOT_KEEP_ALIVE == state->GetKeepAliveTimeMs()) { + return; + } + mStates.push_back(state); +} +void LedControl::DeleteAllState(void) +{ + mStates.clear(); + SetLedState(LedState::OFF); +} +void LedControl::DeleteUselessState(void) +{ + constexpr bool DELETE_STATE = true; + constexpr bool KEEP_STATE = false; + auto is_to_remove = [](const std::shared_ptr &state) { + if (DELETED_LED_STATE == state->GetKeepAliveTimeMs()) { + LogInfo(" Delete useless led state.\n"); + return DELETE_STATE; + } + return KEEP_STATE; + }; + mStates.erase(std::remove_if(mStates.begin(), mStates.end(), is_to_remove), mStates.end()); +} +void LedControl::CheckState(const unsigned int &period) +{ + const int TOP_STATE_SHOW = mStates.size() - 1; + mStateAliveTime += period; + mBlinkPeriod += period; + if (TOP_STATE_SHOW >= 0) { + // LogInfo("TOP_STATE_SHOW = %d.\n", TOP_STATE_SHOW); + std::shared_ptr state = mStates[TOP_STATE_SHOW]; + LedState ledState = LedState::END; + if (state->GetKeepAliveTimeMs() == DELETED_LED_STATE || + (state->GetKeepAliveTimeMs() != KEEP_ALIVE_FOREVER && state->GetKeepAliveTimeMs() <= mStateAliveTime)) { + DeleteTopLedState(); + return; + } + state->GetLedState(ledState); + if (mCurrentState != ledState && LED_NOT_BLINK == state->GetBlinkTimeMs()) { + SetLedState(ledState); + mCurrentState = ledState; + } + else if (mCurrentState == ledState && state->GetBlinkTimeMs() != LED_NOT_BLINK) { + BlinkOff(state); + } + else if (mCurrentState == LedState::OFF && state->GetBlinkTimeMs() != LED_NOT_BLINK) { + BlinkOn(state, ledState); + } + } +} +void LedControl::NewLedStateStart(void) +{ + mCurrentState = LedState::END; + mStateAliveTime = NEW_TOP_LED_STATE; + mBlinkPeriod = 0; +} +void LedControl::DeleteTopLedState(void) +{ + const int TOP_STATE_SHOW = mStates.size() - 1; + if (TOP_STATE_SHOW < 0) { + return; + } + LogInfo("Delete led state.\n"); + NewLedStateStart(); + mStates.erase(mStates.begin() + TOP_STATE_SHOW); + const int NEXT_LED_STATE = mStates.size() - 1; + if (NEXT_LED_STATE < 0) { + SetLedState(LedState::OFF); + return; + } + LogInfo("Top next led state.\n"); + std::shared_ptr state = mStates[NEXT_LED_STATE]; + LedState ledState = LedState::END; + state->GetLedState(ledState); + SetLedState(ledState); + mCurrentState = ledState; +} +void LedControl::BlinkOff(std::shared_ptr &state) +{ + if (state->GetBlinkTimeMs() < mBlinkPeriod) { + // LogInfo("blink off.\n"); + SetLedState(LedState::OFF); + mCurrentState = LedState::OFF; + mBlinkPeriod = 0; + } +} +void LedControl::BlinkOn(std::shared_ptr &state, const LedState &onState) +{ + if (state->GetBlinkTimeMs() < mBlinkPeriod) { + // LogInfo("blink on.\n"); + SetLedState(onState); + mCurrentState = onState; + mBlinkPeriod = 0; + } +} +const char *PrintLedState(const LedState &state) +{ + switch (state) { + case LedState::OFF: { + return "OFF"; + } + case LedState::ON: { + return "ON"; + } + case LedState::GREEN: { + return "GREEN"; + } + case LedState::RED: { + return "RED"; + } + case LedState::YELLOW: { + return "YELLOW"; + } + + default: { + return "Unknown LedState"; + } + } +} \ No newline at end of file diff --git a/utils/LinuxApi/CMakeLists.txt b/utils/LinuxApi/CMakeLists.txt new file mode 100644 index 0000000..ea8af24 --- /dev/null +++ b/utils/LinuxApi/CMakeLists.txt @@ -0,0 +1,54 @@ +include(${CMAKE_SOURCE_DIR_IPCSDK}/build/global_config.cmake) +set(EXECUTABLE_OUTPUT_PATH ${EXEC_OUTPUT_PATH}) +set(LIBRARY_OUTPUT_PATH ${LIBS_OUTPUT_PATH}) + +include_directories( + ./src + ./include + ${UTILS_SOURCE_PATH}/Log/include +) +# link_directories( +# ${EXTERNAL_SOURCE_PATH}/libconfig/libconfig-1.7.3/lib/.libs +# ) + + + +aux_source_directory(./src SRC_FILES) + +set(TARGET_NAME LinuxApi) +add_library(${TARGET_NAME} STATIC ${SRC_FILES}) +target_link_libraries(${TARGET_NAME} Log) + +if ("${COMPILE_IMPROVE_SUPPORT}" MATCHES "true") +add_custom_target( + LinuxApi_code_check + COMMAND ${CLANG_TIDY_EXE} + -checks='${CLANG_TIDY_CHECKS}' + --header-filter=.* + --system-headers=false + ${SRC_FILES} + ${CLANG_TIDY_CONFIG} + -p ${PLATFORM_PATH}/cmake-shell + WORKING_DIRECTORY ${UTILS_SOURCE_PATH}/LinuxApi +) +file(GLOB_RECURSE HEADER_FILES *.h) +add_custom_target( + LinuxApi_code_format + COMMAND ${CLANG_FORMAT_EXE} + -style=file + -i ${SRC_FILES} ${HEADER_FILES} + WORKING_DIRECTORY ${UTILS_SOURCE_PATH}/LinuxApi +) +add_custom_command( + TARGET ${TARGET_NAME} + PRE_BUILD + COMMAND make LinuxApi_code_check + COMMAND make LinuxApi_code_format + WORKING_DIRECTORY ${PLATFORM_PATH}/cmake-shell/ +) +endif() + +define_file_name(${TARGET_NAME}) + +file(GLOB_RECURSE INSTALL_HEADER_FILES include/*.h) +install(FILES ${INSTALL_HEADER_FILES} DESTINATION include) \ No newline at end of file diff --git a/utils/LinuxApi/include/LinuxApi.h b/utils/LinuxApi/include/LinuxApi.h new file mode 100644 index 0000000..96f4568 --- /dev/null +++ b/utils/LinuxApi/include/LinuxApi.h @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2023 Fancy Code. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef LINUX_API_H +#define LINUX_API_H +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef __cplusplus +extern "C" { +#endif +int fx_system(const char *command); +int fx_open(const char *pathname, int flags); +int fx_tcgetattr(int fd, struct termios *termios_p); +int fx_tcsetattr(int fd, int optional_actions, const struct termios *termios_p); +ssize_t fx_write(int fd, const void *buf, size_t count); +ssize_t fx_read(int fd, void *buf, size_t count); +int fx_select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout); +int fx_fstat(int fd, struct stat *statbuf); +int fx_access(const char *pathname, int mode); +FILE *fx_fopen(const char *pathname, const char *mode); +#ifdef __cplusplus +} +#endif +#endif \ No newline at end of file diff --git a/utils/LinuxApi/src/LinuxApi.c b/utils/LinuxApi/src/LinuxApi.c new file mode 100644 index 0000000..754cf9c --- /dev/null +++ b/utils/LinuxApi/src/LinuxApi.c @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2023 Fancy Code. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "LinuxApi.h" +#include +#include +#include +#include +#include +#include +#include +#include + +int fx_system(const char *command) +{ + return system(command); +} +int fx_open(const char *pathname, int flags) +{ + return open(pathname, flags); +} +int fx_tcgetattr(int fd, struct termios *termios_p) +{ + return tcgetattr(fd, termios_p); +} +int fx_tcsetattr(int fd, int optional_actions, const struct termios *termios_p) +{ + return tcsetattr(fd, optional_actions, termios_p); +} +ssize_t fx_write(int fd, const void *buf, size_t count) +{ + return write(fd, buf, count); +} +ssize_t fx_read(int fd, void *buf, size_t count) +{ + return read(fd, buf, count); +} +int fx_select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout) +{ + return select(nfds, readfds, writefds, exceptfds, timeout); +} +int fx_fstat(int fd, struct stat *statbuf) +{ + return fstat(fd, statbuf); +} +int fx_access(const char *pathname, int mode) +{ + return access(pathname, mode); +} +FILE *fx_fopen(const char *pathname, const char *mode) +{ + return fopen(pathname, mode); +} \ No newline at end of file diff --git a/utils/Log/CMakeLists.txt b/utils/Log/CMakeLists.txt new file mode 100644 index 0000000..b65eb64 --- /dev/null +++ b/utils/Log/CMakeLists.txt @@ -0,0 +1,52 @@ + +include(${CMAKE_SOURCE_DIR_IPCSDK}/build/global_config.cmake) +set(EXECUTABLE_OUTPUT_PATH ${EXEC_OUTPUT_PATH}) +set(LIBRARY_OUTPUT_PATH ${LIBS_OUTPUT_PATH}) + +include_directories( + ./abstract + ./src + ./src/easyloggingpp + ./include +) +#do not rely on any other library +# link_directories( +# ${EXTERNAL_SOURCE_PATH}/curl/curl-8.1.2/build/lib +# ) + + + +aux_source_directory(./abstract ABSTRACT_SRC_FILES) +aux_source_directory(./src IMPL_SRC_FILES) +aux_source_directory(./src/easyloggingpp EASYLOGGING_SRC_FILES) + +set(ABSTRACT_TARGET LogAbstract) +set(IMPL_TARGET Log) +add_library(${ABSTRACT_TARGET} STATIC ${ABSTRACT_SRC_FILES}) +# target_link_libraries(${ABSTRACT_TARGET} Log) +add_library(${IMPL_TARGET} STATIC ${IMPL_SRC_FILES} ${EASYLOGGING_SRC_FILES}) +target_link_libraries(${IMPL_TARGET} ${ABSTRACT_TARGET}) + +if ("${COMPILE_IMPROVE_SUPPORT}" MATCHES "true") +add_custom_target( + Log_code_check + COMMAND ${CLANG_TIDY_EXE} + -checks='${CLANG_TIDY_CHECKS}' + --header-filter=.* + --system-headers=false + ${IMPL_SRC_FILES} + ${ABSTRACT_SRC_FILES} + ${CLANG_TIDY_CONFIG} + -p ${PLATFORM_PATH}/cmake-shell + WORKING_DIRECTORY ${UTILS_SOURCE_PATH}/Log +) +add_custom_command( + TARGET ${IMPL_TARGET} + PRE_BUILD + COMMAND make Log_code_check + WORKING_DIRECTORY ${PLATFORM_PATH}/cmake-shell/ +) +endif() + +file(GLOB_RECURSE INSTALL_HEADER_FILES include/*.h) +install(FILES ${INSTALL_HEADER_FILES} DESTINATION include) \ No newline at end of file diff --git a/utils/Log/README.md b/utils/Log/README.md new file mode 100644 index 0000000..92cbe22 --- /dev/null +++ b/utils/Log/README.md @@ -0,0 +1,57 @@ +# 1. 日志库 + +  日志库主要辅助调试和测试。 + +## 1.1. 基本需求: + +1. 日志类型:info / error / warning / trace ;需要打印出运行时间,文件名和行号; +2. 不同日志类型显示不同的颜色; +3. 通过预编译选择开启 / 关闭; +4. 通过多态动态加载 / 卸载; +5. 通过预编译加载 / 卸载某个模块的日志; + +## 1.2. 接口描述 + +1. 开启日志保存功能,把运行日志保存到制定目录; +2. 开启日志保存功能,只保存error和trace的日志; + +接口示例: + +``` +示例代码 +``` + +## 1.3. 参考 + +* CMakeLists.txt脚本代码: + +//build/global_config.cmake +``` +// 文件名宏定义__F_FILE__,指定文件实现打印文件名 + 行号功能 +// 不使用系统宏__FILE__是因为系统宏会打印绝对路径,日志阅读性差 +function(define_file_name target) + get_target_property(source_files "${target}" SOURCES) + foreach(source_file ${source_files}) + get_property(defs SOURCE "${source_file}" + PROPERTY COMPILE_DEFINITIONS) + get_filename_component(file_name "${source_file}" NAME) + list(APPEND defs "__F_FILE__=/"${file_name}/"") + set_property( + SOURCE "${source_file}" + PROPERTY COMPILE_DEFINITIONS ${defs}) + endforeach() +endfunction() +// LOG_DISABLE日志使能宏,指定文件开启 / 关闭日志功能 +function(log_disable target) + get_target_property(source_files "${target}" SOURCES) + foreach(source_file ${source_files}) + get_property(defs SOURCE "${source_file}" + PROPERTY COMPILE_DEFINITIONS) + get_filename_component(file_name "${source_file}" NAME) + list(APPEND defs "LOG_DISABLE") + set_property( + SOURCE "${source_file}" + PROPERTY COMPILE_DEFINITIONS ${defs}) + endforeach() +endfunction() +``` \ No newline at end of file diff --git a/utils/Log/abstract/ILog.cpp b/utils/Log/abstract/ILog.cpp new file mode 100644 index 0000000..a07cb08 --- /dev/null +++ b/utils/Log/abstract/ILog.cpp @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2023 Fancy Code. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "ILog.h" +#include "ILogCpp.h" +#include +static void ILogInitCallBack(ILog *object, const enum LogInstance log) +{ + return ILogCpp::GetInstance()->Init(log); +} +static void ILogFree(ILog *object) +{ +} +static int ILogPrintf(ILog *object, const char *function, const int line, const enum LogType type, const char *format, + ...) +{ + return 0; +} +static void ILogUnInitCallBack(ILog *object) +{ + return ILogCpp::GetInstance()->UnInit(); +} +static ILog default_log = { + .init = ILogInitCallBack, + .free = ILogFree, + .printf = ILogPrintf, + .un_init = ILogUnInitCallBack, +}; +static ILog *log_instance = &default_log; +ILog *GetLogIntance(void) +{ + return log_instance; +} +void NewILog(ILog **object) +{ + if (!object || !(*object)) { + return; + } + memcpy(*object, &default_log, sizeof(ILog)); + return; +} +void ResetLogImpl(ILog *impl) +{ + if (log_instance) { + log_instance->free(log_instance); + } + log_instance = impl; +} \ No newline at end of file diff --git a/utils/Log/abstract/ILogCpp.cpp b/utils/Log/abstract/ILogCpp.cpp new file mode 100644 index 0000000..95437ba --- /dev/null +++ b/utils/Log/abstract/ILogCpp.cpp @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2023 Fancy Code. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "ILogCpp.h" +#include +std::shared_ptr &ILogCpp::GetInstance(std::shared_ptr *impl) +{ + static std::shared_ptr instance = std::make_shared(); + static bool instanceChanging = false; + if (impl && false == instanceChanging) + { + // Don't use std::mutex for runing faster. + // Sleep for difference thread to release instance. + instanceChanging = true; + // std::this_thread::sleep_for(std::chrono::milliseconds(100)); // Don't sleep and make sure that start fast. + if (instance.use_count() == 1) // bug? + { + // LogInfo("Instance change succeed.\n"); + // instance->UnInit(); + // (*impl)->Init(); + instance = *impl; + } + else + { + // LogError("[ error ] instance change failed, using by some one.\n"); + } + instanceChanging = false; + } + if (instanceChanging) + { + static std::shared_ptr tmporaryInstance = std::make_shared(); + return tmporaryInstance; + } + return instance; +} \ No newline at end of file diff --git a/utils/Log/include/ILog.h b/utils/Log/include/ILog.h new file mode 100644 index 0000000..9554c8b --- /dev/null +++ b/utils/Log/include/ILog.h @@ -0,0 +1,96 @@ +/* + * Copyright (c) 2023 Fancy Code. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef ILOG_H +#define ILOG_H +#ifdef __cplusplus +extern "C" +{ +#endif + enum LogType + { + LOG_TYPE_VERBOSE = 0, + LOG_TYPE_DEBUG, + LOG_TYPE_INFORMATION, + LOG_TYPE_WARNING, + LOG_TYPE_ERROR, + LOG_TYPE_TRACE, + LOG_TYPE_TEST_TIPS, + LOG_TYPE_END + }; + typedef struct LogSetting + { + const char *fileName; // File name of saving log. + const char *maxSize; // Max size of saving log. + } LogSetting; + enum LogInstance + { + LOG_SERIAL_PRINT = 0, // for serial print. + LOG_EASYLOGGING, // for easylogging++. + LOG_INSTANCE_TYPE_END + }; +#ifndef __F_FILE__ +#define __F_FILE__ "fancy" +#endif +#ifndef LOG_DISABLE +#define LogVerbose(...) GetLogIntance()->printf(GetLogIntance(), __F_FILE__, __LINE__, LOG_TYPE_VERBOSE, __VA_ARGS__) +#define LogDebug(...) GetLogIntance()->printf(GetLogIntance(), __F_FILE__, __LINE__, LOG_TYPE_DEBUG, __VA_ARGS__) +#define LogInfo(...) GetLogIntance()->printf(GetLogIntance(), __F_FILE__, __LINE__, LOG_TYPE_INFORMATION, __VA_ARGS__) +#define LogWarning(...) GetLogIntance()->printf(GetLogIntance(), __F_FILE__, __LINE__, LOG_TYPE_WARNING, __VA_ARGS__) +#define LogError(...) GetLogIntance()->printf(GetLogIntance(), __F_FILE__, __LINE__, LOG_TYPE_ERROR, __VA_ARGS__) +#define LogTrace(...) GetLogIntance()->printf(GetLogIntance(), __F_FILE__, __LINE__, LOG_TYPE_TRACE, __VA_ARGS__) +#else +#define LogVerbose(...) +#define LogDebug(...) +#define LogInfo(...) +#define LogWarning(...) +#define LogError(...) +#define LogTrace(...) +#endif +#if 1 // For OpenHarmony log, should delete finally.// TODO: +#define LOGD(...) +#define LOGI(...) +#define LOGW(...) +#define LOGE(...) +#define LOGF(...) +#endif + typedef struct i_log ILog; + typedef struct i_log + { + void (*init)(ILog *, const enum LogInstance); + void (*free)(ILog *); + int (*printf)(ILog *, const char *, const int, const enum LogType, const char *, ...); + void (*un_init)(ILog *); + } ILog; + ILog *GetLogIntance(void); + void NewILog(ILog **object); + void ResetLogImpl(ILog *impl); + static inline void ILogInit(const enum LogInstance log) + { + return GetLogIntance()->init(GetLogIntance(), log); + } + static inline void ILogUnInit(void) + { + return GetLogIntance()->un_init(GetLogIntance()); + } + static inline void ILogFree(void) + { + GetLogIntance()->free(GetLogIntance()); + } + void CreateLogModule(void); + void DestroyLogModule(void); +#ifdef __cplusplus +} +#endif +#endif \ No newline at end of file diff --git a/utils/Log/include/ILogCpp.h b/utils/Log/include/ILogCpp.h new file mode 100644 index 0000000..36739a9 --- /dev/null +++ b/utils/Log/include/ILogCpp.h @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2023 Fancy Code. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef ILOGCPP_H +#define ILOGCPP_H +#include "ILog.h" +#include +class ILogCpp +{ +public: + ILogCpp() = default; + virtual ~ILogCpp() = default; + static std::shared_ptr &GetInstance(std::shared_ptr *impl = nullptr); + virtual void Init(const enum LogInstance &log) {} + virtual void UnInit(void) {} + virtual int Log(const char *buff) { return 0; } + virtual int InFo(const char *buff) { return 0; } + virtual int Warning(const char *buff) { return 0; } + virtual int Error(const char *buff) { return 0; } + virtual int Trace(const char *buff) { return 0; } + virtual int Debug(const char *buff) { return 0; } +}; +#endif \ No newline at end of file diff --git a/utils/Log/src/ILogMakePtr.cpp b/utils/Log/src/ILogMakePtr.cpp new file mode 100644 index 0000000..7d19556 --- /dev/null +++ b/utils/Log/src/ILogMakePtr.cpp @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2023 Fancy Code. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "ILogMakePtr.h" +#include "ILog.h" +#include "ILogCpp.h" +#include "LogEasylogging.h" +#include "LogImpl.h" +#include "Log.h" +#include +void CreateLogModule(void) +{ + std::shared_ptr logImpl = ILogMakePtr::GetInstance()->MakeLogEasylogging(nullptr); + ILogCpp::GetInstance(&logImpl); + ILog *log = nullptr; + ILogMakePtr::GetInstance()->CreateLogPtr(&log); + ResetLogImpl((ILog *)log); +} +void DestroyLogModule(void) +{ + ILogFree(); +} +std::shared_ptr ILogMakePtr::MakeLogImplPtr(void) +{ + std::shared_ptr logImpl = std::make_shared(); + return logImpl; +} +std::shared_ptr ILogMakePtr::MakeLogEasylogging(const LogSetting *setting) +{ + std::shared_ptr logImpl = std::make_shared(setting); + return logImpl; +} +void ILogMakePtr::CreateLogPtr(ILog **log) +{ + NewLog((Log **)log); +} \ No newline at end of file diff --git a/utils/Log/src/ILogMakePtr.h b/utils/Log/src/ILogMakePtr.h new file mode 100644 index 0000000..07cb58d --- /dev/null +++ b/utils/Log/src/ILogMakePtr.h @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2023 Fancy Code. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef ILOG_MAKE_PTR_H +#define ILOG_MAKE_PTR_H +#include "ILogCpp.h" +#include "ILog.h" +#include +#include +class ILogMakePtr +{ +public: + static std::shared_ptr &GetInstance(std::shared_ptr *impl = nullptr) + { + static std::shared_ptr instance = std::make_shared(); + if (impl) + { + instance = *impl; + } + return instance; + } + ILogMakePtr() = default; + virtual ~ILogMakePtr() = default; + virtual std::shared_ptr MakeLogImplPtr(void); + virtual std::shared_ptr MakeLogEasylogging(const LogSetting *setting); + virtual void CreateLogPtr(ILog **log); +}; +#endif \ No newline at end of file diff --git a/utils/Log/src/Log.cpp b/utils/Log/src/Log.cpp new file mode 100644 index 0000000..0bc7938 --- /dev/null +++ b/utils/Log/src/Log.cpp @@ -0,0 +1,101 @@ +/* + * Copyright (c) 2023 Fancy Code. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "Log.h" +#include "ILogCpp.h" +#include +#include +#include +#include +#include +static void LogFree(ILog *object) +{ + printf("log instance free.\n"); + if (object) + { + free(object); + } +} +static int LogPrintf(ILog *object, const char *function, const int line, const enum LogType type, const char *format, ...) +{ + // TODO: + // LogTypeToString(type); + constexpr int SEND_TRACE_BUFF_SIZE = 2048; + char buff[SEND_TRACE_BUFF_SIZE] = {0}; + snprintf(buff, SEND_TRACE_BUFF_SIZE, "[%s:%d]:", function, line); + // ILog::GetInstance()->Log(buff); + const int headLen = strlen(buff); + va_list vargs; + va_start(vargs, format); + int len = vsnprintf(buff + headLen, SEND_TRACE_BUFF_SIZE - headLen, format, vargs); + va_end(vargs); + switch (type) + { + case LOG_TYPE_INFORMATION: + ILogCpp::GetInstance()->InFo(buff); + break; + + case LOG_TYPE_WARNING: + ILogCpp::GetInstance()->Warning(buff); + break; + + case LOG_TYPE_ERROR: + ILogCpp::GetInstance()->Error(buff); + break; + + case LOG_TYPE_DEBUG: + ILogCpp::GetInstance()->Debug(buff); + break; + + case LOG_TYPE_TRACE: + ILogCpp::GetInstance()->Trace(buff); + break; + + default: + break; + } + return len; +} +static void LogImplInit(Log *log) +{ + printf("LogImplInit\n"); + ((ILog *)log)->printf = LogPrintf; + ((ILog *)log)->free = LogFree; +} +void NewLog(Log **log) +{ + if (!log) + { + printf("STATUS_CODE_INVALID_PARAMENTER\n"); + return; + } + if (!(*log)) + { + *log = (Log *)malloc(sizeof(Log)); + if (*log) + { + printf("NewLog succeed.\n"); + NewILog((ILog **)log); + LogImplInit(*log); + return; + } + LogError("NewLog failed.\n"); + return; + } + // else + { + LogImplInit(*log); + return; + } +} \ No newline at end of file diff --git a/utils/Log/src/Log.h b/utils/Log/src/Log.h new file mode 100644 index 0000000..b1e5840 --- /dev/null +++ b/utils/Log/src/Log.h @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2023 Fancy Code. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef LOG_H +# define LOG_H +#include "ILog.h" +#ifdef __cplusplus +extern "C" +{ +#endif + typedef struct log Log; + typedef struct log + { + ILog base; + } Log; + void NewLog(Log **log); +#ifdef __cplusplus +} +#endif +#endif \ No newline at end of file diff --git a/utils/Log/src/LogEasylogging.cpp b/utils/Log/src/LogEasylogging.cpp new file mode 100644 index 0000000..4ac7269 --- /dev/null +++ b/utils/Log/src/LogEasylogging.cpp @@ -0,0 +1,102 @@ +/* + * Copyright (c) 2023 Fancy Code. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "ILog.h" +#include "LogEasylogging.h" +#include "easylogging++.h" +// #include +// #define ELPP_UNICODE // Define for easylogging +INITIALIZE_EASYLOGGINGPP // Init easylogging + // static bool initFlag = false; // Only used for init easyloggingpp + bool test = false; +LogEasylogging::LogEasylogging(const LogSetting *setting) +{ + if (!setting) + { + return; + } + if (setting->fileName) + { + mFileName = setting->fileName; + } + if (setting->maxSize) + { + mMaxSize = setting->maxSize; + } +} +void LogEasylogging::Init(const enum LogInstance &log) +{ +#if 0 + el::Configurations conf("/home/xiaojiazhu/project/OS/OSThings/test/output_files/bin/default-logger.conf"); + el::Loggers::reconfigureAllLoggers(conf); +#endif + // Set the log path. + if (mFileName.size() > 0) + { + el::Loggers::reconfigureAllLoggers(el::ConfigurationType::Filename, mFileName.c_str()); + } + + // Set the max size of log file. + // el::Loggers::reconfigureAllLoggers(el::ConfigurationType::MaxLogFileSize, "1048576"); + if (mMaxSize.size() > 0) + { + el::Loggers::reconfigureAllLoggers(el::ConfigurationType::MaxLogFileSize, mMaxSize.c_str()); + } + el::Loggers::reconfigureAllLoggers(el::ConfigurationType::Enabled, "true"); + el::Loggers::reconfigureAllLoggers(el::ConfigurationType::ToFile, "true"); +} +void LogEasylogging::UnInit(void) +{ + el::Loggers::reconfigureAllLoggers(el::ConfigurationType::ToFile, "false"); + el::Loggers::reconfigureAllLoggers(el::ConfigurationType::Enabled, "false"); +} +// bool LogEasylogging::IsWorking() +// { +// return true; +// } +int LogEasylogging::Log(const char *buff) +{ + // LOG(INFO) << buff; + return 0; +} + +int LogEasylogging::InFo(const char *buff) +{ + LOG(INFO) << buff; + return 0; +} + +int LogEasylogging::Warning(const char *buff) +{ + LOG(WARNING) << buff; + return 0; +} + +int LogEasylogging::Error(const char *buff) +{ + LOG(ERROR) << buff; + return 0; +} + +int LogEasylogging::Debug(const char *buff) +{ + LOG(DEBUG) << buff; + return 0; +} + +int LogEasylogging::Trace(const char *buff) +{ + LOG(TRACE) << buff; + return 0; +} \ No newline at end of file diff --git a/utils/Log/src/LogEasylogging.h b/utils/Log/src/LogEasylogging.h new file mode 100644 index 0000000..729979c --- /dev/null +++ b/utils/Log/src/LogEasylogging.h @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2023 Fancy Code. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef LOG_EASYLOGGING_H +#define LOG_EASYLOGGING_H +#include "ILogCpp.h" +#include +class LogEasylogging : public ILogCpp +{ +public: + LogEasylogging(const LogSetting *setting); + virtual ~LogEasylogging() = default; + void Init(const enum LogInstance &log) override; + void UnInit(void) override; + // bool IsWorking(); // override; + int Log(const char *buff); // override; + int InFo(const char *buff); // override; + int Warning(const char *buff); // override; + int Error(const char *buff); // override; + int Trace(const char *buff); // override; + int Debug(const char *buff); // override; + +private: + std::string mFileName; // File name of saving log. + std::string mMaxSize; // Max size of saving log. +}; +#endif \ No newline at end of file diff --git a/utils/Log/src/LogImpl.cpp b/utils/Log/src/LogImpl.cpp new file mode 100644 index 0000000..3bb58a8 --- /dev/null +++ b/utils/Log/src/LogImpl.cpp @@ -0,0 +1,19 @@ +/* + * Copyright (c) 2023 Fancy Code. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +// #include "LogImpl.h" +// int LogImpl::Log(const char *buff) +// { +// return printf("%s", buff); +// } \ No newline at end of file diff --git a/utils/Log/src/LogImpl.h b/utils/Log/src/LogImpl.h new file mode 100644 index 0000000..e29c48d --- /dev/null +++ b/utils/Log/src/LogImpl.h @@ -0,0 +1,24 @@ +/* + * Copyright (c) 2023 Fancy Code. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef LOG_IMPL_H +#define LOG_IMPL_H +#include "ILogCpp.h" +class LogImpl : public ILogCpp +{ +public: + LogImpl() = default; + virtual ~LogImpl() = default; +}; +#endif \ No newline at end of file diff --git a/utils/Log/src/easyloggingpp/README.txt b/utils/Log/src/easyloggingpp/README.txt new file mode 100644 index 0000000..de33e5b --- /dev/null +++ b/utils/Log/src/easyloggingpp/README.txt @@ -0,0 +1,9 @@ +这是一个开源库的源码。 +注意: +//1. 需要定义宏 +//#define ELPP_UNICODE // Define for easylogging +2. 初始化 +INITIALIZE_EASYLOGGINGPP // Init easylogging +//3. 支持多线程,在源码头文件"easyloggingpp.h"定义宏 +//#define ELPP_THREAD_SAFE // Working in threads. Added by xiaojiazhu +4. ELPP_NO_DEFAULT_LOG_FILE 此宏屏蔽生成默认的log文件 diff --git a/utils/Log/src/easyloggingpp/default-logger.conf b/utils/Log/src/easyloggingpp/default-logger.conf new file mode 100644 index 0000000..9e34afd --- /dev/null +++ b/utils/Log/src/easyloggingpp/default-logger.conf @@ -0,0 +1,25 @@ +* GLOBAL: + FORMAT = "%datetime | %level | %logger | %msg" + FILENAME = "/tmp/logs/myeasylog-configuration.cpp.log" + ENABLED = true + TO_FILE = true + TO_STANDARD_OUTPUT = true + SUBSECOND_PRECISION = 3 + PERFORMANCE_TRACKING = false + MAX_LOG_FILE_SIZE = 2097152 ## Throw log files away after 2MB +* DEBUG: + FILENAME = "/tmp/logs/myeasylog-configuration.cpp-debug.log" + TO_STANDARD_OUTPUT = true + ENABLED = true ## We will set it to false after development completed +* WARNING: + FILENAME = "/tmp/logs/filename-with-time-%datetime{%H:%m}" +* TRACE: + TO_FILE = true ## Unnecessary configuration cuz its already true in GLOBAL but doing it anyway! +* VERBOSE: + FORMAT = "%datetime{%d/%M/%y} | %level-%vlevel | %msg" +## Error logs +* ERROR: + ENABLED = false + FILENAME = "/tmp/logs/myeasylog-configuration.cpp-error.log" +* FATAL: + ENABLED = false diff --git a/utils/Log/src/easyloggingpp/easylogging++.cc b/utils/Log/src/easyloggingpp/easylogging++.cc new file mode 100644 index 0000000..889e5c2 --- /dev/null +++ b/utils/Log/src/easyloggingpp/easylogging++.cc @@ -0,0 +1,3122 @@ +// +// Bismillah ar-Rahmaan ar-Raheem +// +// Easylogging++ v9.96.7 +// Cross-platform logging library for C++ applications +// +// Copyright (c) 2012-2018 Amrayn Web Services +// Copyright (c) 2012-2018 @abumusamq +// +// This library is released under the MIT Licence. +// https://github.com/amrayn/easyloggingpp/blob/master/LICENSE +// +// https://amrayn.com +// http://muflihun.com +// + +#include "easylogging++.h" + +#define ELPP_NO_DEFAULT_LOG_FILE // Don't build the default log file. Added by xiaojiazhu + +#if defined(AUTO_INITIALIZE_EASYLOGGINGPP) +INITIALIZE_EASYLOGGINGPP +#endif + +namespace el { + +// el::base +namespace base { +// el::base::consts +namespace consts { + +// Level log values - These are values that are replaced in place of %level format specifier +// Extra spaces after format specifiers are only for readability purposes in log files +static const base::type::char_t* kInfoLevelLogValue = ELPP_LITERAL("INFO"); +static const base::type::char_t* kDebugLevelLogValue = ELPP_LITERAL("DEBUG"); +static const base::type::char_t* kWarningLevelLogValue = ELPP_LITERAL("WARNING"); +static const base::type::char_t* kErrorLevelLogValue = ELPP_LITERAL("ERROR"); +static const base::type::char_t* kFatalLevelLogValue = ELPP_LITERAL("FATAL"); +static const base::type::char_t* kVerboseLevelLogValue = + ELPP_LITERAL("VERBOSE"); // will become VERBOSE-x where x = verbose level +static const base::type::char_t* kTraceLevelLogValue = ELPP_LITERAL("TRACE"); +static const base::type::char_t* kInfoLevelShortLogValue = ELPP_LITERAL("I"); +static const base::type::char_t* kDebugLevelShortLogValue = ELPP_LITERAL("D"); +static const base::type::char_t* kWarningLevelShortLogValue = ELPP_LITERAL("W"); +static const base::type::char_t* kErrorLevelShortLogValue = ELPP_LITERAL("E"); +static const base::type::char_t* kFatalLevelShortLogValue = ELPP_LITERAL("F"); +static const base::type::char_t* kVerboseLevelShortLogValue = ELPP_LITERAL("V"); +static const base::type::char_t* kTraceLevelShortLogValue = ELPP_LITERAL("T"); +// Format specifiers - These are used to define log format +static const base::type::char_t* kAppNameFormatSpecifier = ELPP_LITERAL("%app"); +static const base::type::char_t* kLoggerIdFormatSpecifier = ELPP_LITERAL("%logger"); +static const base::type::char_t* kThreadIdFormatSpecifier = ELPP_LITERAL("%thread"); +static const base::type::char_t* kSeverityLevelFormatSpecifier = ELPP_LITERAL("%level"); +static const base::type::char_t* kSeverityLevelShortFormatSpecifier = ELPP_LITERAL("%levshort"); +static const base::type::char_t* kDateTimeFormatSpecifier = ELPP_LITERAL("%datetime"); +static const base::type::char_t* kLogFileFormatSpecifier = ELPP_LITERAL("%file"); +static const base::type::char_t* kLogFileBaseFormatSpecifier = ELPP_LITERAL("%fbase"); +static const base::type::char_t* kLogLineFormatSpecifier = ELPP_LITERAL("%line"); +static const base::type::char_t* kLogLocationFormatSpecifier = ELPP_LITERAL("%loc"); +static const base::type::char_t* kLogFunctionFormatSpecifier = ELPP_LITERAL("%func"); +static const base::type::char_t* kCurrentUserFormatSpecifier = ELPP_LITERAL("%user"); +static const base::type::char_t* kCurrentHostFormatSpecifier = ELPP_LITERAL("%host"); +static const base::type::char_t* kMessageFormatSpecifier = ELPP_LITERAL("%msg"); +static const base::type::char_t* kVerboseLevelFormatSpecifier = ELPP_LITERAL("%vlevel"); +static const char* kDateTimeFormatSpecifierForFilename = "%datetime"; +// Date/time +static const char* kDays[7] = { "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday" }; +static const char* kDaysAbbrev[7] = { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" }; +static const char* kMonths[12] = { "January", "February", "March", "April", "May", "June", "July", "August", + "September", "October", "November", "December" + }; +static const char* kMonthsAbbrev[12] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" }; +static const char* kDefaultDateTimeFormat = "%Y-%M-%d %H:%m:%s,%g"; +static const char* kDefaultDateTimeFormatInFilename = "%Y-%M-%d_%H-%m"; +static const int kYearBase = 1900; +static const char* kAm = "AM"; +static const char* kPm = "PM"; +// Miscellaneous constants + +static const char* kNullPointer = "nullptr"; +#if ELPP_VARIADIC_TEMPLATES_SUPPORTED +#endif // ELPP_VARIADIC_TEMPLATES_SUPPORTED +static const base::type::VerboseLevel kMaxVerboseLevel = 9; +static const char* kUnknownUser = "unknown-user"; +static const char* kUnknownHost = "unknown-host"; + + +//---------------- DEFAULT LOG FILE ----------------------- + +#if defined(ELPP_NO_DEFAULT_LOG_FILE) +# if ELPP_OS_UNIX +static const char* kDefaultLogFile = "/dev/null"; +# elif ELPP_OS_WINDOWS +static const char* kDefaultLogFile = "nul"; +# endif // ELPP_OS_UNIX +#elif defined(ELPP_DEFAULT_LOG_FILE) +static const char* kDefaultLogFile = ELPP_DEFAULT_LOG_FILE; +#else +static const char* kDefaultLogFile = "myeasylog.log"; +#endif // defined(ELPP_NO_DEFAULT_LOG_FILE) + + +#if !defined(ELPP_DISABLE_LOG_FILE_FROM_ARG) +static const char* kDefaultLogFileParam = "--default-log-file"; +#endif // !defined(ELPP_DISABLE_LOG_FILE_FROM_ARG) +#if defined(ELPP_LOGGING_FLAGS_FROM_ARG) +static const char* kLoggingFlagsParam = "--logging-flags"; +#endif // defined(ELPP_LOGGING_FLAGS_FROM_ARG) +static const char* kValidLoggerIdSymbols = + "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-._"; +static const char* kConfigurationComment = "##"; +static const char* kConfigurationLevel = "*"; +static const char* kConfigurationLoggerId = "--"; +} +// el::base::utils +namespace utils { + +/// @brief Aborts application due with user-defined status +static void abort(int status, const std::string& reason) { + // Both status and reason params are there for debugging with tools like gdb etc + ELPP_UNUSED(status); + ELPP_UNUSED(reason); +#if defined(ELPP_COMPILER_MSVC) && defined(_M_IX86) && defined(_DEBUG) + // Ignore msvc critical error dialog - break instead (on debug mode) + _asm int 3 +#else + ::abort(); +#endif // defined(ELPP_COMPILER_MSVC) && defined(_M_IX86) && defined(_DEBUG) +} + +} // namespace utils +} // namespace base + +// el + +// LevelHelper + +const char* LevelHelper::convertToString(Level level) { + // Do not use switch over strongly typed enums because Intel C++ compilers dont support them yet. + if (level == Level::Global) return "GLOBAL"; + if (level == Level::Debug) return "DEBUG"; + if (level == Level::Info) return "INFO"; + if (level == Level::Warning) return "WARNING"; + if (level == Level::Error) return "ERROR"; + if (level == Level::Fatal) return "FATAL"; + if (level == Level::Verbose) return "VERBOSE"; + if (level == Level::Trace) return "TRACE"; + return "UNKNOWN"; +} + +struct StringToLevelItem { + const char* levelString; + Level level; +}; + +static struct StringToLevelItem stringToLevelMap[] = { + { "global", Level::Global }, + { "debug", Level::Debug }, + { "info", Level::Info }, + { "warning", Level::Warning }, + { "error", Level::Error }, + { "fatal", Level::Fatal }, + { "verbose", Level::Verbose }, + { "trace", Level::Trace } +}; + +Level LevelHelper::convertFromString(const char* levelStr) { + for (auto& item : stringToLevelMap) { + if (base::utils::Str::cStringCaseEq(levelStr, item.levelString)) { + return item.level; + } + } + return Level::Unknown; +} + +void LevelHelper::forEachLevel(base::type::EnumType* startIndex, const std::function& fn) { + base::type::EnumType lIndexMax = LevelHelper::kMaxValid; + do { + if (fn()) { + break; + } + *startIndex = static_cast(*startIndex << 1); + } while (*startIndex <= lIndexMax); +} + +// ConfigurationTypeHelper + +const char* ConfigurationTypeHelper::convertToString(ConfigurationType configurationType) { + // Do not use switch over strongly typed enums because Intel C++ compilers dont support them yet. + if (configurationType == ConfigurationType::Enabled) return "ENABLED"; + if (configurationType == ConfigurationType::Filename) return "FILENAME"; + if (configurationType == ConfigurationType::Format) return "FORMAT"; + if (configurationType == ConfigurationType::ToFile) return "TO_FILE"; + if (configurationType == ConfigurationType::ToStandardOutput) return "TO_STANDARD_OUTPUT"; + if (configurationType == ConfigurationType::SubsecondPrecision) return "SUBSECOND_PRECISION"; + if (configurationType == ConfigurationType::PerformanceTracking) return "PERFORMANCE_TRACKING"; + if (configurationType == ConfigurationType::MaxLogFileSize) return "MAX_LOG_FILE_SIZE"; + if (configurationType == ConfigurationType::LogFlushThreshold) return "LOG_FLUSH_THRESHOLD"; + return "UNKNOWN"; +} + +struct ConfigurationStringToTypeItem { + const char* configString; + ConfigurationType configType; +}; + +static struct ConfigurationStringToTypeItem configStringToTypeMap[] = { + { "enabled", ConfigurationType::Enabled }, + { "to_file", ConfigurationType::ToFile }, + { "to_standard_output", ConfigurationType::ToStandardOutput }, + { "format", ConfigurationType::Format }, + { "filename", ConfigurationType::Filename }, + { "subsecond_precision", ConfigurationType::SubsecondPrecision }, + { "milliseconds_width", ConfigurationType::MillisecondsWidth }, + { "performance_tracking", ConfigurationType::PerformanceTracking }, + { "max_log_file_size", ConfigurationType::MaxLogFileSize }, + { "log_flush_threshold", ConfigurationType::LogFlushThreshold }, +}; + +ConfigurationType ConfigurationTypeHelper::convertFromString(const char* configStr) { + for (auto& item : configStringToTypeMap) { + if (base::utils::Str::cStringCaseEq(configStr, item.configString)) { + return item.configType; + } + } + return ConfigurationType::Unknown; +} + +void ConfigurationTypeHelper::forEachConfigType(base::type::EnumType* startIndex, const std::function& fn) { + base::type::EnumType cIndexMax = ConfigurationTypeHelper::kMaxValid; + do { + if (fn()) { + break; + } + *startIndex = static_cast(*startIndex << 1); + } while (*startIndex <= cIndexMax); +} + +// Configuration + +Configuration::Configuration(const Configuration& c) : + m_level(c.m_level), + m_configurationType(c.m_configurationType), + m_value(c.m_value) { +} + +Configuration& Configuration::operator=(const Configuration& c) { + if (&c != this) { + m_level = c.m_level; + m_configurationType = c.m_configurationType; + m_value = c.m_value; + } + return *this; +} + +/// @brief Full constructor used to sets value of configuration +Configuration::Configuration(Level level, ConfigurationType configurationType, const std::string& value) : + m_level(level), + m_configurationType(configurationType), + m_value(value) { +} + +void Configuration::log(el::base::type::ostream_t& os) const { + os << LevelHelper::convertToString(m_level) + << ELPP_LITERAL(" ") << ConfigurationTypeHelper::convertToString(m_configurationType) + << ELPP_LITERAL(" = ") << m_value.c_str(); +} + +/// @brief Used to find configuration from configuration (pointers) repository. Avoid using it. +Configuration::Predicate::Predicate(Level level, ConfigurationType configurationType) : + m_level(level), + m_configurationType(configurationType) { +} + +bool Configuration::Predicate::operator()(const Configuration* conf) const { + return ((conf != nullptr) && (conf->level() == m_level) && (conf->configurationType() == m_configurationType)); +} + +// Configurations + +Configurations::Configurations(void) : + m_configurationFile(std::string()), + m_isFromFile(false) { +} + +Configurations::Configurations(const std::string& configurationFile, bool useDefaultsForRemaining, + Configurations* base) : + m_configurationFile(configurationFile), + m_isFromFile(false) { + parseFromFile(configurationFile, base); + if (useDefaultsForRemaining) { + setRemainingToDefault(); + } +} + +bool Configurations::parseFromFile(const std::string& configurationFile, Configurations* base) { + // We initial assertion with true because if we have assertion disabled, we want to pass this + // check and if assertion is enabled we will have values re-assigned any way. + bool assertionPassed = true; + ELPP_ASSERT((assertionPassed = base::utils::File::pathExists(configurationFile.c_str(), true)) == true, + "Configuration file [" << configurationFile << "] does not exist!"); + if (!assertionPassed) { + return false; + } + bool success = Parser::parseFromFile(configurationFile, this, base); + m_isFromFile = success; + return success; +} + +bool Configurations::parseFromText(const std::string& configurationsString, Configurations* base) { + bool success = Parser::parseFromText(configurationsString, this, base); + if (success) { + m_isFromFile = false; + } + return success; +} + +void Configurations::setFromBase(Configurations* base) { + if (base == nullptr || base == this) { + return; + } + base::threading::ScopedLock scopedLock(base->lock()); + for (Configuration*& conf : base->list()) { + set(conf); + } +} + +bool Configurations::hasConfiguration(ConfigurationType configurationType) { + base::type::EnumType lIndex = LevelHelper::kMinValid; + bool result = false; + LevelHelper::forEachLevel(&lIndex, [&](void) -> bool { + if (hasConfiguration(LevelHelper::castFromInt(lIndex), configurationType)) { + result = true; + } + return result; + }); + return result; +} + +bool Configurations::hasConfiguration(Level level, ConfigurationType configurationType) { + base::threading::ScopedLock scopedLock(lock()); +#if ELPP_COMPILER_INTEL + // We cant specify template types here, Intel C++ throws compilation error + // "error: type name is not allowed" + return RegistryWithPred::get(level, configurationType) != nullptr; +#else + return RegistryWithPred::get(level, configurationType) != nullptr; +#endif // ELPP_COMPILER_INTEL +} + +void Configurations::set(Level level, ConfigurationType configurationType, const std::string& value) { + base::threading::ScopedLock scopedLock(lock()); + unsafeSet(level, configurationType, value); // This is not unsafe anymore as we have locked mutex + if (level == Level::Global) { + unsafeSetGlobally(configurationType, value, false); // Again this is not unsafe either + } +} + +void Configurations::set(Configuration* conf) { + if (conf == nullptr) { + return; + } + set(conf->level(), conf->configurationType(), conf->value()); +} + +void Configurations::setToDefault(void) { + setGlobally(ConfigurationType::Enabled, std::string("true"), true); + setGlobally(ConfigurationType::Filename, std::string(base::consts::kDefaultLogFile), true); +#if defined(ELPP_NO_LOG_TO_FILE) + setGlobally(ConfigurationType::ToFile, std::string("false"), true); +#else + setGlobally(ConfigurationType::ToFile, std::string("true"), true); +#endif // defined(ELPP_NO_LOG_TO_FILE) + setGlobally(ConfigurationType::ToStandardOutput, std::string("true"), true); + setGlobally(ConfigurationType::SubsecondPrecision, std::string("3"), true); + setGlobally(ConfigurationType::PerformanceTracking, std::string("true"), true); + setGlobally(ConfigurationType::MaxLogFileSize, std::string("0"), true); + setGlobally(ConfigurationType::LogFlushThreshold, std::string("0"), true); + + setGlobally(ConfigurationType::Format, std::string("%datetime %level [%logger] %msg"), true); + set(Level::Debug, ConfigurationType::Format, + std::string("%datetime %level [%logger] [%user@%host] [%func] [%loc] %msg")); + // INFO and WARNING are set to default by Level::Global + set(Level::Error, ConfigurationType::Format, std::string("%datetime %level [%logger] %msg")); + set(Level::Fatal, ConfigurationType::Format, std::string("%datetime %level [%logger] %msg")); + set(Level::Verbose, ConfigurationType::Format, std::string("%datetime %level-%vlevel [%logger] %msg")); + set(Level::Trace, ConfigurationType::Format, std::string("%datetime %level [%logger] [%func] [%loc] %msg")); +} + +void Configurations::setRemainingToDefault(void) { + base::threading::ScopedLock scopedLock(lock()); +#if defined(ELPP_NO_LOG_TO_FILE) + unsafeSetIfNotExist(Level::Global, ConfigurationType::Enabled, std::string("false")); +#else + unsafeSetIfNotExist(Level::Global, ConfigurationType::Enabled, std::string("true")); +#endif // defined(ELPP_NO_LOG_TO_FILE) + unsafeSetIfNotExist(Level::Global, ConfigurationType::Filename, std::string(base::consts::kDefaultLogFile)); + unsafeSetIfNotExist(Level::Global, ConfigurationType::ToStandardOutput, std::string("true")); + unsafeSetIfNotExist(Level::Global, ConfigurationType::SubsecondPrecision, std::string("3")); + unsafeSetIfNotExist(Level::Global, ConfigurationType::PerformanceTracking, std::string("true")); + unsafeSetIfNotExist(Level::Global, ConfigurationType::MaxLogFileSize, std::string("0")); + unsafeSetIfNotExist(Level::Global, ConfigurationType::Format, std::string("%datetime %level [%logger] %msg")); + unsafeSetIfNotExist(Level::Debug, ConfigurationType::Format, + std::string("%datetime %level [%logger] [%user@%host] [%func] [%loc] %msg")); + // INFO and WARNING are set to default by Level::Global + unsafeSetIfNotExist(Level::Error, ConfigurationType::Format, std::string("%datetime %level [%logger] %msg")); + unsafeSetIfNotExist(Level::Fatal, ConfigurationType::Format, std::string("%datetime %level [%logger] %msg")); + unsafeSetIfNotExist(Level::Verbose, ConfigurationType::Format, std::string("%datetime %level-%vlevel [%logger] %msg")); + unsafeSetIfNotExist(Level::Trace, ConfigurationType::Format, + std::string("%datetime %level [%logger] [%func] [%loc] %msg")); +} + +bool Configurations::Parser::parseFromFile(const std::string& configurationFile, Configurations* sender, + Configurations* base) { + sender->setFromBase(base); + std::ifstream fileStream_(configurationFile.c_str(), std::ifstream::in); + ELPP_ASSERT(fileStream_.is_open(), "Unable to open configuration file [" << configurationFile << "] for parsing."); + bool parsedSuccessfully = false; + std::string line = std::string(); + Level currLevel = Level::Unknown; + std::string currConfigStr = std::string(); + std::string currLevelStr = std::string(); + while (fileStream_.good()) { + std::getline(fileStream_, line); + parsedSuccessfully = parseLine(&line, &currConfigStr, &currLevelStr, &currLevel, sender); + ELPP_ASSERT(parsedSuccessfully, "Unable to parse configuration line: " << line); + } + return parsedSuccessfully; +} + +bool Configurations::Parser::parseFromText(const std::string& configurationsString, Configurations* sender, + Configurations* base) { + sender->setFromBase(base); + bool parsedSuccessfully = false; + std::stringstream ss(configurationsString); + std::string line = std::string(); + Level currLevel = Level::Unknown; + std::string currConfigStr = std::string(); + std::string currLevelStr = std::string(); + while (std::getline(ss, line)) { + parsedSuccessfully = parseLine(&line, &currConfigStr, &currLevelStr, &currLevel, sender); + ELPP_ASSERT(parsedSuccessfully, "Unable to parse configuration line: " << line); + } + return parsedSuccessfully; +} + +void Configurations::Parser::ignoreComments(std::string* line) { + std::size_t foundAt = 0; + std::size_t quotesStart = line->find("\""); + std::size_t quotesEnd = std::string::npos; + if (quotesStart != std::string::npos) { + quotesEnd = line->find("\"", quotesStart + 1); + while (quotesEnd != std::string::npos && line->at(quotesEnd - 1) == '\\') { + // Do not erase slash yet - we will erase it in parseLine(..) while loop + quotesEnd = line->find("\"", quotesEnd + 2); + } + } + if ((foundAt = line->find(base::consts::kConfigurationComment)) != std::string::npos) { + if (foundAt < quotesEnd) { + foundAt = line->find(base::consts::kConfigurationComment, quotesEnd + 1); + } + *line = line->substr(0, foundAt); + } +} + +bool Configurations::Parser::isLevel(const std::string& line) { + return base::utils::Str::startsWith(line, std::string(base::consts::kConfigurationLevel)); +} + +bool Configurations::Parser::isComment(const std::string& line) { + return base::utils::Str::startsWith(line, std::string(base::consts::kConfigurationComment)); +} + +bool Configurations::Parser::isConfig(const std::string& line) { + std::size_t assignment = line.find('='); + return line != "" && + ((line[0] >= 'A' && line[0] <= 'Z') || (line[0] >= 'a' && line[0] <= 'z')) && + (assignment != std::string::npos) && + (line.size() > assignment); +} + +bool Configurations::Parser::parseLine(std::string* line, std::string* currConfigStr, std::string* currLevelStr, + Level* currLevel, + Configurations* conf) { + ConfigurationType currConfig = ConfigurationType::Unknown; + std::string currValue = std::string(); + *line = base::utils::Str::trim(*line); + if (isComment(*line)) return true; + ignoreComments(line); + *line = base::utils::Str::trim(*line); + if (line->empty()) { + // Comment ignored + return true; + } + if (isLevel(*line)) { + if (line->size() <= 2) { + return true; + } + *currLevelStr = line->substr(1, line->size() - 2); + *currLevelStr = base::utils::Str::toUpper(*currLevelStr); + *currLevelStr = base::utils::Str::trim(*currLevelStr); + *currLevel = LevelHelper::convertFromString(currLevelStr->c_str()); + return true; + } + if (isConfig(*line)) { + std::size_t assignment = line->find('='); + *currConfigStr = line->substr(0, assignment); + *currConfigStr = base::utils::Str::toUpper(*currConfigStr); + *currConfigStr = base::utils::Str::trim(*currConfigStr); + currConfig = ConfigurationTypeHelper::convertFromString(currConfigStr->c_str()); + currValue = line->substr(assignment + 1); + currValue = base::utils::Str::trim(currValue); + std::size_t quotesStart = currValue.find("\"", 0); + std::size_t quotesEnd = std::string::npos; + if (quotesStart != std::string::npos) { + quotesEnd = currValue.find("\"", quotesStart + 1); + while (quotesEnd != std::string::npos && currValue.at(quotesEnd - 1) == '\\') { + currValue = currValue.erase(quotesEnd - 1, 1); + quotesEnd = currValue.find("\"", quotesEnd + 2); + } + } + if (quotesStart != std::string::npos && quotesEnd != std::string::npos) { + // Quote provided - check and strip if valid + ELPP_ASSERT((quotesStart < quotesEnd), "Configuration error - No ending quote found in [" + << currConfigStr << "]"); + ELPP_ASSERT((quotesStart + 1 != quotesEnd), "Empty configuration value for [" << currConfigStr << "]"); + if ((quotesStart != quotesEnd) && (quotesStart + 1 != quotesEnd)) { + // Explicit check in case if assertion is disabled + currValue = currValue.substr(quotesStart + 1, quotesEnd - 1); + } + } + } + ELPP_ASSERT(*currLevel != Level::Unknown, "Unrecognized severity level [" << *currLevelStr << "]"); + ELPP_ASSERT(currConfig != ConfigurationType::Unknown, "Unrecognized configuration [" << *currConfigStr << "]"); + if (*currLevel == Level::Unknown || currConfig == ConfigurationType::Unknown) { + return false; // unrecognizable level or config + } + conf->set(*currLevel, currConfig, currValue); + return true; +} + +void Configurations::unsafeSetIfNotExist(Level level, ConfigurationType configurationType, const std::string& value) { + Configuration* conf = RegistryWithPred::get(level, configurationType); + if (conf == nullptr) { + unsafeSet(level, configurationType, value); + } +} + +void Configurations::unsafeSet(Level level, ConfigurationType configurationType, const std::string& value) { + Configuration* conf = RegistryWithPred::get(level, configurationType); + if (conf == nullptr) { + registerNew(new Configuration(level, configurationType, value)); + } else { + conf->setValue(value); + } + if (level == Level::Global) { + unsafeSetGlobally(configurationType, value, false); + } +} + +void Configurations::setGlobally(ConfigurationType configurationType, const std::string& value, + bool includeGlobalLevel) { + if (includeGlobalLevel) { + set(Level::Global, configurationType, value); + } + base::type::EnumType lIndex = LevelHelper::kMinValid; + LevelHelper::forEachLevel(&lIndex, [&](void) -> bool { + set(LevelHelper::castFromInt(lIndex), configurationType, value); + return false; // Do not break lambda function yet as we need to set all levels regardless + }); +} + +void Configurations::unsafeSetGlobally(ConfigurationType configurationType, const std::string& value, + bool includeGlobalLevel) { + if (includeGlobalLevel) { + unsafeSet(Level::Global, configurationType, value); + } + base::type::EnumType lIndex = LevelHelper::kMinValid; + LevelHelper::forEachLevel(&lIndex, [&](void) -> bool { + unsafeSet(LevelHelper::castFromInt(lIndex), configurationType, value); + return false; // Do not break lambda function yet as we need to set all levels regardless + }); +} + +// LogBuilder + +void LogBuilder::convertToColoredOutput(base::type::string_t* logLine, Level level) { + if (!m_termSupportsColor) return; + const base::type::char_t* resetColor = ELPP_LITERAL("\x1b[0m"); + if (level == Level::Error || level == Level::Fatal) + *logLine = ELPP_LITERAL("\x1b[31m") + *logLine + resetColor; + else if (level == Level::Warning) + *logLine = ELPP_LITERAL("\x1b[33m") + *logLine + resetColor; + else if (level == Level::Debug) + *logLine = ELPP_LITERAL("\x1b[32m") + *logLine + resetColor; + else if (level == Level::Info) + *logLine = ELPP_LITERAL("\x1b[36m") + *logLine + resetColor; + else if (level == Level::Trace) + *logLine = ELPP_LITERAL("\x1b[35m") + *logLine + resetColor; +} + +// Logger + +Logger::Logger(const std::string& id, base::LogStreamsReferenceMapPtr logStreamsReference) : + m_id(id), + m_typedConfigurations(nullptr), + m_parentApplicationName(std::string()), + m_isConfigured(false), + m_logStreamsReference(logStreamsReference) { + initUnflushedCount(); +} + +Logger::Logger(const std::string& id, const Configurations& configurations, + base::LogStreamsReferenceMapPtr logStreamsReference) : + m_id(id), + m_typedConfigurations(nullptr), + m_parentApplicationName(std::string()), + m_isConfigured(false), + m_logStreamsReference(logStreamsReference) { + initUnflushedCount(); + configure(configurations); +} + +Logger::Logger(const Logger& logger) { + base::utils::safeDelete(m_typedConfigurations); + m_id = logger.m_id; + m_typedConfigurations = logger.m_typedConfigurations; + m_parentApplicationName = logger.m_parentApplicationName; + m_isConfigured = logger.m_isConfigured; + m_configurations = logger.m_configurations; + m_unflushedCount = logger.m_unflushedCount; + m_logStreamsReference = logger.m_logStreamsReference; +} + +Logger& Logger::operator=(const Logger& logger) { + if (&logger != this) { + base::utils::safeDelete(m_typedConfigurations); + m_id = logger.m_id; + m_typedConfigurations = logger.m_typedConfigurations; + m_parentApplicationName = logger.m_parentApplicationName; + m_isConfigured = logger.m_isConfigured; + m_configurations = logger.m_configurations; + m_unflushedCount = logger.m_unflushedCount; + m_logStreamsReference = logger.m_logStreamsReference; + } + return *this; +} + +void Logger::configure(const Configurations& configurations) { + m_isConfigured = false; // we set it to false in case if we fail + initUnflushedCount(); + if (m_typedConfigurations != nullptr) { + Configurations* c = const_cast(m_typedConfigurations->configurations()); + if (c->hasConfiguration(Level::Global, ConfigurationType::Filename)) { + flush(); + } + } + base::threading::ScopedLock scopedLock(lock()); + if (m_configurations != configurations) { + m_configurations.setFromBase(const_cast(&configurations)); + } + base::utils::safeDelete(m_typedConfigurations); + m_typedConfigurations = new base::TypedConfigurations(&m_configurations, m_logStreamsReference); + resolveLoggerFormatSpec(); + m_isConfigured = true; +} + +void Logger::reconfigure(void) { + ELPP_INTERNAL_INFO(1, "Reconfiguring logger [" << m_id << "]"); + configure(m_configurations); +} + +bool Logger::isValidId(const std::string& id) { + for (std::string::const_iterator it = id.begin(); it != id.end(); ++it) { + if (!base::utils::Str::contains(base::consts::kValidLoggerIdSymbols, *it)) { + return false; + } + } + return true; +} + +void Logger::flush(void) { + ELPP_INTERNAL_INFO(3, "Flushing logger [" << m_id << "] all levels"); + base::threading::ScopedLock scopedLock(lock()); + base::type::EnumType lIndex = LevelHelper::kMinValid; + LevelHelper::forEachLevel(&lIndex, [&](void) -> bool { + flush(LevelHelper::castFromInt(lIndex), nullptr); + return false; + }); +} + +void Logger::flush(Level level, base::type::fstream_t* fs) { + if (fs == nullptr && m_typedConfigurations->toFile(level)) { + fs = m_typedConfigurations->fileStream(level); + } + if (fs != nullptr) { + fs->flush(); + std::unordered_map::iterator iter = m_unflushedCount.find(level); + if (iter != m_unflushedCount.end()) { + iter->second = 0; + } + Helpers::validateFileRolling(this, level); + } +} + +void Logger::initUnflushedCount(void) { + m_unflushedCount.clear(); + base::type::EnumType lIndex = LevelHelper::kMinValid; + LevelHelper::forEachLevel(&lIndex, [&](void) -> bool { + m_unflushedCount.insert(std::make_pair(LevelHelper::castFromInt(lIndex), 0)); + return false; + }); +} + +void Logger::resolveLoggerFormatSpec(void) const { + base::type::EnumType lIndex = LevelHelper::kMinValid; + LevelHelper::forEachLevel(&lIndex, [&](void) -> bool { + base::LogFormat* logFormat = + const_cast(&m_typedConfigurations->logFormat(LevelHelper::castFromInt(lIndex))); + base::utils::Str::replaceFirstWithEscape(logFormat->m_format, base::consts::kLoggerIdFormatSpecifier, m_id); + return false; + }); +} + +// el::base +namespace base { + +// el::base::utils +namespace utils { + +// File + +base::type::fstream_t* File::newFileStream(const std::string& filename) { + base::type::fstream_t *fs = new base::type::fstream_t(filename.c_str(), + base::type::fstream_t::out +#if !defined(ELPP_FRESH_LOG_FILE) + | base::type::fstream_t::app +#endif + ); +#if defined(ELPP_UNICODE) + std::locale elppUnicodeLocale(""); +# if ELPP_OS_WINDOWS + std::locale elppUnicodeLocaleWindows(elppUnicodeLocale, new std::codecvt_utf8_utf16); + elppUnicodeLocale = elppUnicodeLocaleWindows; +# endif // ELPP_OS_WINDOWS + fs->imbue(elppUnicodeLocale); +#endif // defined(ELPP_UNICODE) + if (fs->is_open()) { + fs->flush(); + } else { + base::utils::safeDelete(fs); + ELPP_INTERNAL_ERROR("Bad file [" << filename << "]", true); + } + return fs; +} + +std::size_t File::getSizeOfFile(base::type::fstream_t* fs) { + if (fs == nullptr) { + return 0; + } + // Since the file stream is appended to or truncated, the current + // offset is the file size. + std::size_t size = static_cast(fs->tellg()); + return size; +} + +bool File::pathExists(const char* path, bool considerFile) { + if (path == nullptr) { + return false; + } +#if ELPP_OS_UNIX + ELPP_UNUSED(considerFile); + struct stat st; + return (stat(path, &st) == 0); +#elif ELPP_OS_WINDOWS + DWORD fileType = GetFileAttributesA(path); + if (fileType == INVALID_FILE_ATTRIBUTES) { + return false; + } + return considerFile ? true : ((fileType & FILE_ATTRIBUTE_DIRECTORY) == 0 ? false : true); +#endif // ELPP_OS_UNIX +} + +bool File::createPath(const std::string& path) { + if (path.empty()) { + return false; + } + if (base::utils::File::pathExists(path.c_str())) { + return true; + } + int status = -1; + + char* currPath = const_cast(path.c_str()); + std::string builtPath = std::string(); +#if ELPP_OS_UNIX + if (path[0] == '/') { + builtPath = "/"; + } + currPath = STRTOK(currPath, base::consts::kFilePathSeparator, 0); +#elif ELPP_OS_WINDOWS + // Use secure functions API + char* nextTok_ = nullptr; + currPath = STRTOK(currPath, base::consts::kFilePathSeparator, &nextTok_); + ELPP_UNUSED(nextTok_); +#endif // ELPP_OS_UNIX + while (currPath != nullptr) { + builtPath.append(currPath); + builtPath.append(base::consts::kFilePathSeparator); +#if ELPP_OS_UNIX + status = mkdir(builtPath.c_str(), ELPP_LOG_PERMS); + currPath = STRTOK(nullptr, base::consts::kFilePathSeparator, 0); +#elif ELPP_OS_WINDOWS + status = _mkdir(builtPath.c_str()); + currPath = STRTOK(nullptr, base::consts::kFilePathSeparator, &nextTok_); +#endif // ELPP_OS_UNIX + } + if (status == -1) { + ELPP_INTERNAL_ERROR("Error while creating path [" << path << "]", true); + return false; + } + return true; +} + +std::string File::extractPathFromFilename(const std::string& fullPath, const char* separator) { + if ((fullPath == "") || (fullPath.find(separator) == std::string::npos)) { + return fullPath; + } + std::size_t lastSlashAt = fullPath.find_last_of(separator); + if (lastSlashAt == 0) { + return std::string(separator); + } + return fullPath.substr(0, lastSlashAt + 1); +} + +void File::buildStrippedFilename(const char* filename, char buff[], std::size_t limit) { + std::size_t sizeOfFilename = strlen(filename); + if (sizeOfFilename >= limit) { + filename += (sizeOfFilename - limit); + if (filename[0] != '.' && filename[1] != '.') { // prepend if not already + filename += 3; // 3 = '..' + STRCAT(buff, "..", limit); + } + } + STRCAT(buff, filename, limit); +} + +void File::buildBaseFilename(const std::string& fullPath, char buff[], std::size_t limit, const char* separator) { + const char *filename = fullPath.c_str(); + std::size_t lastSlashAt = fullPath.find_last_of(separator); + filename += lastSlashAt ? lastSlashAt+1 : 0; + std::size_t sizeOfFilename = strlen(filename); + if (sizeOfFilename >= limit) { + filename += (sizeOfFilename - limit); + if (filename[0] != '.' && filename[1] != '.') { // prepend if not already + filename += 3; // 3 = '..' + STRCAT(buff, "..", limit); + } + } + STRCAT(buff, filename, limit); +} + +// Str + +bool Str::wildCardMatch(const char* str, const char* pattern) { + while (*pattern) { + switch (*pattern) { + case '?': + if (!*str) + return false; + ++str; + ++pattern; + break; + case '*': + if (wildCardMatch(str, pattern + 1)) + return true; + if (*str && wildCardMatch(str + 1, pattern)) + return true; + return false; + default: + if (*str++ != *pattern++) + return false; + break; + } + } + return !*str && !*pattern; +} + +std::string& Str::ltrim(std::string& str) { + str.erase(str.begin(), std::find_if(str.begin(), str.end(), [](char c) { + return !std::isspace(c); + } )); + return str; +} + +std::string& Str::rtrim(std::string& str) { + str.erase(std::find_if(str.rbegin(), str.rend(), [](char c) { + return !std::isspace(c); + }).base(), str.end()); + return str; +} + +std::string& Str::trim(std::string& str) { + return ltrim(rtrim(str)); +} + +bool Str::startsWith(const std::string& str, const std::string& start) { + return (str.length() >= start.length()) && (str.compare(0, start.length(), start) == 0); +} + +bool Str::endsWith(const std::string& str, const std::string& end) { + return (str.length() >= end.length()) && (str.compare(str.length() - end.length(), end.length(), end) == 0); +} + +std::string& Str::replaceAll(std::string& str, char replaceWhat, char replaceWith) { + std::replace(str.begin(), str.end(), replaceWhat, replaceWith); + return str; +} + +std::string& Str::replaceAll(std::string& str, const std::string& replaceWhat, + const std::string& replaceWith) { + if (replaceWhat == replaceWith) + return str; + std::size_t foundAt = std::string::npos; + while ((foundAt = str.find(replaceWhat, foundAt + 1)) != std::string::npos) { + str.replace(foundAt, replaceWhat.length(), replaceWith); + } + return str; +} + +void Str::replaceFirstWithEscape(base::type::string_t& str, const base::type::string_t& replaceWhat, + const base::type::string_t& replaceWith) { + std::size_t foundAt = base::type::string_t::npos; + while ((foundAt = str.find(replaceWhat, foundAt + 1)) != base::type::string_t::npos) { + if (foundAt > 0 && str[foundAt - 1] == base::consts::kFormatSpecifierChar) { + str.erase(foundAt - 1, 1); + ++foundAt; + } else { + str.replace(foundAt, replaceWhat.length(), replaceWith); + return; + } + } +} +#if defined(ELPP_UNICODE) +void Str::replaceFirstWithEscape(base::type::string_t& str, const base::type::string_t& replaceWhat, + const std::string& replaceWith) { + replaceFirstWithEscape(str, replaceWhat, base::type::string_t(replaceWith.begin(), replaceWith.end())); +} +#endif // defined(ELPP_UNICODE) + +std::string& Str::toUpper(std::string& str) { + std::transform(str.begin(), str.end(), str.begin(), + [](char c) { + return static_cast(::toupper(c)); + }); + return str; +} + +bool Str::cStringEq(const char* s1, const char* s2) { + if (s1 == nullptr && s2 == nullptr) return true; + if (s1 == nullptr || s2 == nullptr) return false; + return strcmp(s1, s2) == 0; +} + +bool Str::cStringCaseEq(const char* s1, const char* s2) { + if (s1 == nullptr && s2 == nullptr) return true; + if (s1 == nullptr || s2 == nullptr) return false; + + // With thanks to cygwin for this code + int d = 0; + + while (true) { + const int c1 = toupper(*s1++); + const int c2 = toupper(*s2++); + + if (((d = c1 - c2) != 0) || (c2 == '\0')) { + break; + } + } + + return d == 0; +} + +bool Str::contains(const char* str, char c) { + for (; *str; ++str) { + if (*str == c) + return true; + } + return false; +} + +char* Str::convertAndAddToBuff(std::size_t n, int len, char* buf, const char* bufLim, bool zeroPadded) { + char localBuff[10] = ""; + char* p = localBuff + sizeof(localBuff) - 2; + if (n > 0) { + for (; n > 0 && p > localBuff && len > 0; n /= 10, --len) + *--p = static_cast(n % 10 + '0'); + } else { + *--p = '0'; + --len; + } + if (zeroPadded) + while (p > localBuff && len-- > 0) *--p = static_cast('0'); + return addToBuff(p, buf, bufLim); +} + +char* Str::addToBuff(const char* str, char* buf, const char* bufLim) { + while ((buf < bufLim) && ((*buf = *str++) != '\0')) + ++buf; + return buf; +} + +char* Str::clearBuff(char buff[], std::size_t lim) { + STRCPY(buff, "", lim); + ELPP_UNUSED(lim); // For *nix we dont have anything using lim in above STRCPY macro + return buff; +} + +/// @brief Converts wchar* to char* +/// NOTE: Need to free return value after use! +char* Str::wcharPtrToCharPtr(const wchar_t* line) { + std::size_t len_ = wcslen(line) + 1; + char* buff_ = static_cast(malloc(len_ + 1)); +# if ELPP_OS_UNIX || (ELPP_OS_WINDOWS && !ELPP_CRT_DBG_WARNINGS) + std::wcstombs(buff_, line, len_); +# elif ELPP_OS_WINDOWS + std::size_t convCount_ = 0; + mbstate_t mbState_; + ::memset(static_cast(&mbState_), 0, sizeof(mbState_)); + wcsrtombs_s(&convCount_, buff_, len_, &line, len_, &mbState_); +# endif // ELPP_OS_UNIX || (ELPP_OS_WINDOWS && !ELPP_CRT_DBG_WARNINGS) + return buff_; +} + +// OS + +#if ELPP_OS_WINDOWS +/// @brief Gets environment variables for Windows based OS. +/// We are not using getenv(const char*) because of CRT deprecation +/// @param varname Variable name to get environment variable value for +/// @return If variable exist the value of it otherwise nullptr +const char* OS::getWindowsEnvironmentVariable(const char* varname) { + const DWORD bufferLen = 50; + static char buffer[bufferLen]; + if (GetEnvironmentVariableA(varname, buffer, bufferLen)) { + return buffer; + } + return nullptr; +} +#endif // ELPP_OS_WINDOWS +#if ELPP_OS_ANDROID +std::string OS::getProperty(const char* prop) { + char propVal[PROP_VALUE_MAX + 1]; + int ret = __system_property_get(prop, propVal); + return ret == 0 ? std::string() : std::string(propVal); +} + +std::string OS::getDeviceName(void) { + std::stringstream ss; + std::string manufacturer = getProperty("ro.product.manufacturer"); + std::string model = getProperty("ro.product.model"); + if (manufacturer.empty() || model.empty()) { + return std::string(); + } + ss << manufacturer << "-" << model; + return ss.str(); +} +#endif // ELPP_OS_ANDROID + +const std::string OS::getBashOutput(const char* command) { +#if (ELPP_OS_UNIX && !ELPP_OS_ANDROID && !ELPP_CYGWIN) + if (command == nullptr) { + return std::string(); + } + FILE* proc = nullptr; + if ((proc = popen(command, "r")) == nullptr) { + ELPP_INTERNAL_ERROR("\nUnable to run command [" << command << "]", true); + return std::string(); + } + char hBuff[4096]; + if (fgets(hBuff, sizeof(hBuff), proc) != nullptr) { + pclose(proc); + const std::size_t buffLen = strlen(hBuff); + if (buffLen > 0 && hBuff[buffLen - 1] == '\n') { + hBuff[buffLen - 1] = '\0'; + } + return std::string(hBuff); + } else { + pclose(proc); + } + return std::string(); +#else + ELPP_UNUSED(command); + return std::string(); +#endif // (ELPP_OS_UNIX && !ELPP_OS_ANDROID && !ELPP_CYGWIN) +} + +std::string OS::getEnvironmentVariable(const char* variableName, const char* defaultVal, + const char* alternativeBashCommand) { +#if ELPP_OS_UNIX + const char* val = getenv(variableName); +#elif ELPP_OS_WINDOWS + const char* val = getWindowsEnvironmentVariable(variableName); +#endif // ELPP_OS_UNIX + if ((val == nullptr) || ((strcmp(val, "") == 0))) { +#if ELPP_OS_UNIX && defined(ELPP_FORCE_ENV_VAR_FROM_BASH) + // Try harder on unix-based systems + std::string valBash = base::utils::OS::getBashOutput(alternativeBashCommand); + if (valBash.empty()) { + return std::string(defaultVal); + } else { + return valBash; + } +#elif ELPP_OS_WINDOWS || ELPP_OS_UNIX + ELPP_UNUSED(alternativeBashCommand); + return std::string(defaultVal); +#endif // ELPP_OS_UNIX && defined(ELPP_FORCE_ENV_VAR_FROM_BASH) + } + return std::string(val); +} + +std::string OS::currentUser(void) { +#if ELPP_OS_UNIX && !ELPP_OS_ANDROID + return getEnvironmentVariable("USER", base::consts::kUnknownUser, "whoami"); +#elif ELPP_OS_WINDOWS + return getEnvironmentVariable("USERNAME", base::consts::kUnknownUser); +#elif ELPP_OS_ANDROID + ELPP_UNUSED(base::consts::kUnknownUser); + return std::string("android"); +#else + return std::string(); +#endif // ELPP_OS_UNIX && !ELPP_OS_ANDROID +} + +std::string OS::currentHost(void) { +#if ELPP_OS_UNIX && !ELPP_OS_ANDROID + return getEnvironmentVariable("HOSTNAME", base::consts::kUnknownHost, "hostname"); +#elif ELPP_OS_WINDOWS + return getEnvironmentVariable("COMPUTERNAME", base::consts::kUnknownHost); +#elif ELPP_OS_ANDROID + ELPP_UNUSED(base::consts::kUnknownHost); + return getDeviceName(); +#else + return std::string(); +#endif // ELPP_OS_UNIX && !ELPP_OS_ANDROID +} + +bool OS::termSupportsColor(void) { + std::string term = getEnvironmentVariable("TERM", ""); + return term == "xterm" || term == "xterm-color" || term == "xterm-256color" + || term == "screen" || term == "linux" || term == "cygwin" + || term == "screen-256color"; +} + +// DateTime + +void DateTime::gettimeofday(struct timeval* tv) { +#if ELPP_OS_WINDOWS + if (tv != nullptr) { +# if ELPP_COMPILER_MSVC || defined(_MSC_EXTENSIONS) + const unsigned __int64 delta_ = 11644473600000000Ui64; +# else + const unsigned __int64 delta_ = 11644473600000000ULL; +# endif // ELPP_COMPILER_MSVC || defined(_MSC_EXTENSIONS) + const double secOffSet = 0.000001; + const unsigned long usecOffSet = 1000000; + FILETIME fileTime; + GetSystemTimeAsFileTime(&fileTime); + unsigned __int64 present = 0; + present |= fileTime.dwHighDateTime; + present = present << 32; + present |= fileTime.dwLowDateTime; + present /= 10; // mic-sec + // Subtract the difference + present -= delta_; + tv->tv_sec = static_cast(present * secOffSet); + tv->tv_usec = static_cast(present % usecOffSet); + } +#else + ::gettimeofday(tv, nullptr); +#endif // ELPP_OS_WINDOWS +} + +std::string DateTime::getDateTime(const char* format, const base::SubsecondPrecision* ssPrec) { + struct timeval currTime; + gettimeofday(&currTime); + return timevalToString(currTime, format, ssPrec); +} + +std::string DateTime::timevalToString(struct timeval tval, const char* format, + const el::base::SubsecondPrecision* ssPrec) { + struct ::tm timeInfo; + buildTimeInfo(&tval, &timeInfo); + const int kBuffSize = 30; + char buff_[kBuffSize] = ""; + parseFormat(buff_, kBuffSize, format, &timeInfo, static_cast(tval.tv_usec / ssPrec->m_offset), + ssPrec); + return std::string(buff_); +} + +base::type::string_t DateTime::formatTime(unsigned long long time, base::TimestampUnit timestampUnit) { + base::type::EnumType start = static_cast(timestampUnit); + const base::type::char_t* unit = base::consts::kTimeFormats[start].unit; + for (base::type::EnumType i = start; i < base::consts::kTimeFormatsCount - 1; ++i) { + if (time <= base::consts::kTimeFormats[i].value) { + break; + } + if (base::consts::kTimeFormats[i].value == 1000.0f && time / 1000.0f < 1.9f) { + break; + } + time /= static_cast(base::consts::kTimeFormats[i].value); + unit = base::consts::kTimeFormats[i + 1].unit; + } + base::type::stringstream_t ss; + ss << time << " " << unit; + return ss.str(); +} + +unsigned long long DateTime::getTimeDifference(const struct timeval& endTime, const struct timeval& startTime, + base::TimestampUnit timestampUnit) { + if (timestampUnit == base::TimestampUnit::Microsecond) { + return static_cast(static_cast(1000000 * endTime.tv_sec + endTime.tv_usec) - + static_cast(1000000 * startTime.tv_sec + startTime.tv_usec)); + } + // milliseconds + auto conv = [](const struct timeval& tim) { + return static_cast((tim.tv_sec * 1000) + (tim.tv_usec / 1000)); + }; + return static_cast(conv(endTime) - conv(startTime)); +} + +struct ::tm* DateTime::buildTimeInfo(struct timeval* currTime, struct ::tm* timeInfo) { +#if ELPP_OS_UNIX + time_t rawTime = currTime->tv_sec; + ::elpptime_r(&rawTime, timeInfo); + return timeInfo; +#else +# if ELPP_COMPILER_MSVC + ELPP_UNUSED(currTime); + time_t t; +# if defined(_USE_32BIT_TIME_T) + _time32(&t); +# else + _time64(&t); +# endif + elpptime_s(timeInfo, &t); + return timeInfo; +# else + // For any other compilers that don't have CRT warnings issue e.g, MinGW or TDM GCC- we use different method + time_t rawTime = currTime->tv_sec; + struct tm* tmInf = elpptime(&rawTime); + *timeInfo = *tmInf; + return timeInfo; +# endif // ELPP_COMPILER_MSVC +#endif // ELPP_OS_UNIX +} + +char* DateTime::parseFormat(char* buf, std::size_t bufSz, const char* format, const struct tm* tInfo, + std::size_t msec, const base::SubsecondPrecision* ssPrec) { + const char* bufLim = buf + bufSz; + for (; *format; ++format) { + if (*format == base::consts::kFormatSpecifierChar) { + switch (*++format) { + case base::consts::kFormatSpecifierChar: // Escape + break; + case '\0': // End + --format; + break; + case 'd': // Day + buf = base::utils::Str::convertAndAddToBuff(tInfo->tm_mday, 2, buf, bufLim); + continue; + case 'a': // Day of week (short) + buf = base::utils::Str::addToBuff(base::consts::kDaysAbbrev[tInfo->tm_wday], buf, bufLim); + continue; + case 'A': // Day of week (long) + buf = base::utils::Str::addToBuff(base::consts::kDays[tInfo->tm_wday], buf, bufLim); + continue; + case 'M': // month + buf = base::utils::Str::convertAndAddToBuff(tInfo->tm_mon + 1, 2, buf, bufLim); + continue; + case 'b': // month (short) + buf = base::utils::Str::addToBuff(base::consts::kMonthsAbbrev[tInfo->tm_mon], buf, bufLim); + continue; + case 'B': // month (long) + buf = base::utils::Str::addToBuff(base::consts::kMonths[tInfo->tm_mon], buf, bufLim); + continue; + case 'y': // year (two digits) + buf = base::utils::Str::convertAndAddToBuff(tInfo->tm_year + base::consts::kYearBase, 2, buf, bufLim); + continue; + case 'Y': // year (four digits) + buf = base::utils::Str::convertAndAddToBuff(tInfo->tm_year + base::consts::kYearBase, 4, buf, bufLim); + continue; + case 'h': // hour (12-hour) + buf = base::utils::Str::convertAndAddToBuff(tInfo->tm_hour % 12, 2, buf, bufLim); + continue; + case 'H': // hour (24-hour) + buf = base::utils::Str::convertAndAddToBuff(tInfo->tm_hour, 2, buf, bufLim); + continue; + case 'm': // minute + buf = base::utils::Str::convertAndAddToBuff(tInfo->tm_min, 2, buf, bufLim); + continue; + case 's': // second + buf = base::utils::Str::convertAndAddToBuff(tInfo->tm_sec, 2, buf, bufLim); + continue; + case 'z': // subsecond part + case 'g': + buf = base::utils::Str::convertAndAddToBuff(msec, ssPrec->m_width, buf, bufLim); + continue; + case 'F': // AM/PM + buf = base::utils::Str::addToBuff((tInfo->tm_hour >= 12) ? base::consts::kPm : base::consts::kAm, buf, bufLim); + continue; + default: + continue; + } + } + if (buf == bufLim) break; + *buf++ = *format; + } + return buf; +} + +// CommandLineArgs + +void CommandLineArgs::setArgs(int argc, char** argv) { + m_params.clear(); + m_paramsWithValue.clear(); + if (argc == 0 || argv == nullptr) { + return; + } + m_argc = argc; + m_argv = argv; + for (int i = 1; i < m_argc; ++i) { + const char* v = (strstr(m_argv[i], "=")); + if (v != nullptr && strlen(v) > 0) { + std::string key = std::string(m_argv[i]); + key = key.substr(0, key.find_first_of('=')); + if (hasParamWithValue(key.c_str())) { + ELPP_INTERNAL_INFO(1, "Skipping [" << key << "] arg since it already has value [" + << getParamValue(key.c_str()) << "]"); + } else { + m_paramsWithValue.insert(std::make_pair(key, std::string(v + 1))); + } + } + if (v == nullptr) { + if (hasParam(m_argv[i])) { + ELPP_INTERNAL_INFO(1, "Skipping [" << m_argv[i] << "] arg since it already exists"); + } else { + m_params.push_back(std::string(m_argv[i])); + } + } + } +} + +bool CommandLineArgs::hasParamWithValue(const char* paramKey) const { + return m_paramsWithValue.find(std::string(paramKey)) != m_paramsWithValue.end(); +} + +const char* CommandLineArgs::getParamValue(const char* paramKey) const { + std::unordered_map::const_iterator iter = m_paramsWithValue.find(std::string(paramKey)); + return iter != m_paramsWithValue.end() ? iter->second.c_str() : ""; +} + +bool CommandLineArgs::hasParam(const char* paramKey) const { + return std::find(m_params.begin(), m_params.end(), std::string(paramKey)) != m_params.end(); +} + +bool CommandLineArgs::empty(void) const { + return m_params.empty() && m_paramsWithValue.empty(); +} + +std::size_t CommandLineArgs::size(void) const { + return m_params.size() + m_paramsWithValue.size(); +} + +base::type::ostream_t& operator<<(base::type::ostream_t& os, const CommandLineArgs& c) { + for (int i = 1; i < c.m_argc; ++i) { + os << ELPP_LITERAL("[") << c.m_argv[i] << ELPP_LITERAL("]"); + if (i < c.m_argc - 1) { + os << ELPP_LITERAL(" "); + } + } + return os; +} + +} // namespace utils + +// el::base::threading +namespace threading { + +#if ELPP_THREADING_ENABLED +# if ELPP_USE_STD_THREADING +# if ELPP_ASYNC_LOGGING +static void msleep(int ms) { + // Only when async logging enabled - this is because async is strict on compiler +# if defined(ELPP_NO_SLEEP_FOR) + usleep(ms * 1000); +# else + std::this_thread::sleep_for(std::chrono::milliseconds(ms)); +# endif // defined(ELPP_NO_SLEEP_FOR) +} +# endif // ELPP_ASYNC_LOGGING +# endif // !ELPP_USE_STD_THREADING +#endif // ELPP_THREADING_ENABLED + +} // namespace threading + +// el::base + +// SubsecondPrecision + +void SubsecondPrecision::init(int width) { + if (width < 1 || width > 6) { + width = base::consts::kDefaultSubsecondPrecision; + } + m_width = width; + switch (m_width) { + case 3: + m_offset = 1000; + break; + case 4: + m_offset = 100; + break; + case 5: + m_offset = 10; + break; + case 6: + m_offset = 1; + break; + default: + m_offset = 1000; + break; + } +} + +// LogFormat + +LogFormat::LogFormat(void) : + m_level(Level::Unknown), + m_userFormat(base::type::string_t()), + m_format(base::type::string_t()), + m_dateTimeFormat(std::string()), + m_flags(0x0), + m_currentUser(base::utils::OS::currentUser()), + m_currentHost(base::utils::OS::currentHost()) { +} + +LogFormat::LogFormat(Level level, const base::type::string_t& format) + : m_level(level), m_userFormat(format), m_currentUser(base::utils::OS::currentUser()), + m_currentHost(base::utils::OS::currentHost()) { + parseFromFormat(m_userFormat); +} + +LogFormat::LogFormat(const LogFormat& logFormat): + m_level(logFormat.m_level), + m_userFormat(logFormat.m_userFormat), + m_format(logFormat.m_format), + m_dateTimeFormat(logFormat.m_dateTimeFormat), + m_flags(logFormat.m_flags), + m_currentUser(logFormat.m_currentUser), + m_currentHost(logFormat.m_currentHost) { +} + +LogFormat::LogFormat(LogFormat&& logFormat) { + m_level = std::move(logFormat.m_level); + m_userFormat = std::move(logFormat.m_userFormat); + m_format = std::move(logFormat.m_format); + m_dateTimeFormat = std::move(logFormat.m_dateTimeFormat); + m_flags = std::move(logFormat.m_flags); + m_currentUser = std::move(logFormat.m_currentUser); + m_currentHost = std::move(logFormat.m_currentHost); +} + +LogFormat& LogFormat::operator=(const LogFormat& logFormat) { + if (&logFormat != this) { + m_level = logFormat.m_level; + m_userFormat = logFormat.m_userFormat; + m_dateTimeFormat = logFormat.m_dateTimeFormat; + m_flags = logFormat.m_flags; + m_currentUser = logFormat.m_currentUser; + m_currentHost = logFormat.m_currentHost; + } + return *this; +} + +bool LogFormat::operator==(const LogFormat& other) { + return m_level == other.m_level && m_userFormat == other.m_userFormat && m_format == other.m_format && + m_dateTimeFormat == other.m_dateTimeFormat && m_flags == other.m_flags; +} + +/// @brief Updates format to be used while logging. +/// @param userFormat User provided format +void LogFormat::parseFromFormat(const base::type::string_t& userFormat) { + // We make copy because we will be changing the format + // i.e, removing user provided date format from original format + // and then storing it. + base::type::string_t formatCopy = userFormat; + m_flags = 0x0; + auto conditionalAddFlag = [&](const base::type::char_t* specifier, base::FormatFlags flag) { + std::size_t foundAt = base::type::string_t::npos; + while ((foundAt = formatCopy.find(specifier, foundAt + 1)) != base::type::string_t::npos) { + if (foundAt > 0 && formatCopy[foundAt - 1] == base::consts::kFormatSpecifierChar) { + if (hasFlag(flag)) { + // If we already have flag we remove the escape chars so that '%%' is turned to '%' + // even after specifier resolution - this is because we only replaceFirst specifier + formatCopy.erase(foundAt - 1, 1); + ++foundAt; + } + } else { + if (!hasFlag(flag)) addFlag(flag); + } + } + }; + conditionalAddFlag(base::consts::kAppNameFormatSpecifier, base::FormatFlags::AppName); + conditionalAddFlag(base::consts::kSeverityLevelFormatSpecifier, base::FormatFlags::Level); + conditionalAddFlag(base::consts::kSeverityLevelShortFormatSpecifier, base::FormatFlags::LevelShort); + conditionalAddFlag(base::consts::kLoggerIdFormatSpecifier, base::FormatFlags::LoggerId); + conditionalAddFlag(base::consts::kThreadIdFormatSpecifier, base::FormatFlags::ThreadId); + conditionalAddFlag(base::consts::kLogFileFormatSpecifier, base::FormatFlags::File); + conditionalAddFlag(base::consts::kLogFileBaseFormatSpecifier, base::FormatFlags::FileBase); + conditionalAddFlag(base::consts::kLogLineFormatSpecifier, base::FormatFlags::Line); + conditionalAddFlag(base::consts::kLogLocationFormatSpecifier, base::FormatFlags::Location); + conditionalAddFlag(base::consts::kLogFunctionFormatSpecifier, base::FormatFlags::Function); + conditionalAddFlag(base::consts::kCurrentUserFormatSpecifier, base::FormatFlags::User); + conditionalAddFlag(base::consts::kCurrentHostFormatSpecifier, base::FormatFlags::Host); + conditionalAddFlag(base::consts::kMessageFormatSpecifier, base::FormatFlags::LogMessage); + conditionalAddFlag(base::consts::kVerboseLevelFormatSpecifier, base::FormatFlags::VerboseLevel); + // For date/time we need to extract user's date format first + std::size_t dateIndex = std::string::npos; + if ((dateIndex = formatCopy.find(base::consts::kDateTimeFormatSpecifier)) != std::string::npos) { + while (dateIndex != std::string::npos && dateIndex > 0 && formatCopy[dateIndex - 1] == base::consts::kFormatSpecifierChar) { + dateIndex = formatCopy.find(base::consts::kDateTimeFormatSpecifier, dateIndex + 1); + } + if (dateIndex != std::string::npos) { + addFlag(base::FormatFlags::DateTime); + updateDateFormat(dateIndex, formatCopy); + } + } + m_format = formatCopy; + updateFormatSpec(); +} + +void LogFormat::updateDateFormat(std::size_t index, base::type::string_t& currFormat) { + if (hasFlag(base::FormatFlags::DateTime)) { + index += ELPP_STRLEN(base::consts::kDateTimeFormatSpecifier); + } + const base::type::char_t* ptr = currFormat.c_str() + index; + if ((currFormat.size() > index) && (ptr[0] == '{')) { + // User has provided format for date/time + ++ptr; + int count = 1; // Start by 1 in order to remove starting brace + std::stringstream ss; + for (; *ptr; ++ptr, ++count) { + if (*ptr == '}') { + ++count; // In order to remove ending brace + break; + } + ss << static_cast(*ptr); + } + currFormat.erase(index, count); + m_dateTimeFormat = ss.str(); + } else { + // No format provided, use default + if (hasFlag(base::FormatFlags::DateTime)) { + m_dateTimeFormat = std::string(base::consts::kDefaultDateTimeFormat); + } + } +} + +void LogFormat::updateFormatSpec(void) { + // Do not use switch over strongly typed enums because Intel C++ compilers dont support them yet. + if (m_level == Level::Debug) { + base::utils::Str::replaceFirstWithEscape(m_format, base::consts::kSeverityLevelFormatSpecifier, + base::consts::kDebugLevelLogValue); + base::utils::Str::replaceFirstWithEscape(m_format, base::consts::kSeverityLevelShortFormatSpecifier, + base::consts::kDebugLevelShortLogValue); + } else if (m_level == Level::Info) { + base::utils::Str::replaceFirstWithEscape(m_format, base::consts::kSeverityLevelFormatSpecifier, + base::consts::kInfoLevelLogValue); + base::utils::Str::replaceFirstWithEscape(m_format, base::consts::kSeverityLevelShortFormatSpecifier, + base::consts::kInfoLevelShortLogValue); + } else if (m_level == Level::Warning) { + base::utils::Str::replaceFirstWithEscape(m_format, base::consts::kSeverityLevelFormatSpecifier, + base::consts::kWarningLevelLogValue); + base::utils::Str::replaceFirstWithEscape(m_format, base::consts::kSeverityLevelShortFormatSpecifier, + base::consts::kWarningLevelShortLogValue); + } else if (m_level == Level::Error) { + base::utils::Str::replaceFirstWithEscape(m_format, base::consts::kSeverityLevelFormatSpecifier, + base::consts::kErrorLevelLogValue); + base::utils::Str::replaceFirstWithEscape(m_format, base::consts::kSeverityLevelShortFormatSpecifier, + base::consts::kErrorLevelShortLogValue); + } else if (m_level == Level::Fatal) { + base::utils::Str::replaceFirstWithEscape(m_format, base::consts::kSeverityLevelFormatSpecifier, + base::consts::kFatalLevelLogValue); + base::utils::Str::replaceFirstWithEscape(m_format, base::consts::kSeverityLevelShortFormatSpecifier, + base::consts::kFatalLevelShortLogValue); + } else if (m_level == Level::Verbose) { + base::utils::Str::replaceFirstWithEscape(m_format, base::consts::kSeverityLevelFormatSpecifier, + base::consts::kVerboseLevelLogValue); + base::utils::Str::replaceFirstWithEscape(m_format, base::consts::kSeverityLevelShortFormatSpecifier, + base::consts::kVerboseLevelShortLogValue); + } else if (m_level == Level::Trace) { + base::utils::Str::replaceFirstWithEscape(m_format, base::consts::kSeverityLevelFormatSpecifier, + base::consts::kTraceLevelLogValue); + base::utils::Str::replaceFirstWithEscape(m_format, base::consts::kSeverityLevelShortFormatSpecifier, + base::consts::kTraceLevelShortLogValue); + } + if (hasFlag(base::FormatFlags::User)) { + base::utils::Str::replaceFirstWithEscape(m_format, base::consts::kCurrentUserFormatSpecifier, + m_currentUser); + } + if (hasFlag(base::FormatFlags::Host)) { + base::utils::Str::replaceFirstWithEscape(m_format, base::consts::kCurrentHostFormatSpecifier, + m_currentHost); + } + // Ignore Level::Global and Level::Unknown +} + +// TypedConfigurations + +TypedConfigurations::TypedConfigurations(Configurations* configurations, + LogStreamsReferenceMapPtr logStreamsReference) { + m_configurations = configurations; + m_logStreamsReference = logStreamsReference; + build(m_configurations); +} + +TypedConfigurations::TypedConfigurations(const TypedConfigurations& other) { + this->m_configurations = other.m_configurations; + this->m_logStreamsReference = other.m_logStreamsReference; + build(m_configurations); +} + +bool TypedConfigurations::enabled(Level level) { + return getConfigByVal(level, &m_enabledMap, "enabled"); +} + +bool TypedConfigurations::toFile(Level level) { + return getConfigByVal(level, &m_toFileMap, "toFile"); +} + +const std::string& TypedConfigurations::filename(Level level) { + return getConfigByRef(level, &m_filenameMap, "filename"); +} + +bool TypedConfigurations::toStandardOutput(Level level) { + return getConfigByVal(level, &m_toStandardOutputMap, "toStandardOutput"); +} + +const base::LogFormat& TypedConfigurations::logFormat(Level level) { + return getConfigByRef(level, &m_logFormatMap, "logFormat"); +} + +const base::SubsecondPrecision& TypedConfigurations::subsecondPrecision(Level level) { + return getConfigByRef(level, &m_subsecondPrecisionMap, "subsecondPrecision"); +} + +const base::MillisecondsWidth& TypedConfigurations::millisecondsWidth(Level level) { + return getConfigByRef(level, &m_subsecondPrecisionMap, "millisecondsWidth"); +} + +bool TypedConfigurations::performanceTracking(Level level) { + return getConfigByVal(level, &m_performanceTrackingMap, "performanceTracking"); +} + +base::type::fstream_t* TypedConfigurations::fileStream(Level level) { + return getConfigByRef(level, &m_fileStreamMap, "fileStream").get(); +} + +std::size_t TypedConfigurations::maxLogFileSize(Level level) { + return getConfigByVal(level, &m_maxLogFileSizeMap, "maxLogFileSize"); +} + +std::size_t TypedConfigurations::logFlushThreshold(Level level) { + return getConfigByVal(level, &m_logFlushThresholdMap, "logFlushThreshold"); +} + +void TypedConfigurations::build(Configurations* configurations) { + base::threading::ScopedLock scopedLock(lock()); + auto getBool = [] (std::string boolStr) -> bool { // Pass by value for trimming + base::utils::Str::trim(boolStr); + return (boolStr == "TRUE" || boolStr == "true" || boolStr == "1"); + }; + std::vector withFileSizeLimit; + for (Configurations::const_iterator it = configurations->begin(); it != configurations->end(); ++it) { + Configuration* conf = *it; + // We cannot use switch on strong enums because Intel C++ dont support them yet + if (conf->configurationType() == ConfigurationType::Enabled) { + setValue(conf->level(), getBool(conf->value()), &m_enabledMap); + } else if (conf->configurationType() == ConfigurationType::ToFile) { + setValue(conf->level(), getBool(conf->value()), &m_toFileMap); + } else if (conf->configurationType() == ConfigurationType::ToStandardOutput) { + setValue(conf->level(), getBool(conf->value()), &m_toStandardOutputMap); + } else if (conf->configurationType() == ConfigurationType::Filename) { + // We do not yet configure filename but we will configure in another + // loop. This is because if file cannot be created, we will force ToFile + // to be false. Because configuring logger is not necessarily performance + // sensitive operation, we can live with another loop; (by the way this loop + // is not very heavy either) + } else if (conf->configurationType() == ConfigurationType::Format) { + setValue(conf->level(), base::LogFormat(conf->level(), + base::type::string_t(conf->value().begin(), conf->value().end())), &m_logFormatMap); + } else if (conf->configurationType() == ConfigurationType::SubsecondPrecision) { + setValue(Level::Global, + base::SubsecondPrecision(static_cast(getULong(conf->value()))), &m_subsecondPrecisionMap); + } else if (conf->configurationType() == ConfigurationType::PerformanceTracking) { + setValue(Level::Global, getBool(conf->value()), &m_performanceTrackingMap); + } else if (conf->configurationType() == ConfigurationType::MaxLogFileSize) { + auto v = getULong(conf->value()); + setValue(conf->level(), static_cast(v), &m_maxLogFileSizeMap); + if (v != 0) { + withFileSizeLimit.push_back(conf); + } + } else if (conf->configurationType() == ConfigurationType::LogFlushThreshold) { + setValue(conf->level(), static_cast(getULong(conf->value())), &m_logFlushThresholdMap); + } + } + // As mentioned earlier, we will now set filename configuration in separate loop to deal with non-existent files + for (Configurations::const_iterator it = configurations->begin(); it != configurations->end(); ++it) { + Configuration* conf = *it; + if (conf->configurationType() == ConfigurationType::Filename) { + insertFile(conf->level(), conf->value()); + } + } + for (std::vector::iterator conf = withFileSizeLimit.begin(); + conf != withFileSizeLimit.end(); ++conf) { + // This is not unsafe as mutex is locked in currect scope + unsafeValidateFileRolling((*conf)->level(), base::defaultPreRollOutCallback); + } +} + +unsigned long TypedConfigurations::getULong(std::string confVal) { + bool valid = true; + base::utils::Str::trim(confVal); + valid = !confVal.empty() && std::find_if(confVal.begin(), confVal.end(), + [](char c) { + return !base::utils::Str::isDigit(c); + }) == confVal.end(); + if (!valid) { + valid = false; + ELPP_ASSERT(valid, "Configuration value not a valid integer [" << confVal << "]"); + return 0; + } + return atol(confVal.c_str()); +} + +std::string TypedConfigurations::resolveFilename(const std::string& filename) { + std::string resultingFilename = filename; + std::size_t dateIndex = std::string::npos; + std::string dateTimeFormatSpecifierStr = std::string(base::consts::kDateTimeFormatSpecifierForFilename); + if ((dateIndex = resultingFilename.find(dateTimeFormatSpecifierStr.c_str())) != std::string::npos) { + while (dateIndex > 0 && resultingFilename[dateIndex - 1] == base::consts::kFormatSpecifierChar) { + dateIndex = resultingFilename.find(dateTimeFormatSpecifierStr.c_str(), dateIndex + 1); + } + if (dateIndex != std::string::npos) { + const char* ptr = resultingFilename.c_str() + dateIndex; + // Goto end of specifier + ptr += dateTimeFormatSpecifierStr.size(); + std::string fmt; + if ((resultingFilename.size() > dateIndex) && (ptr[0] == '{')) { + // User has provided format for date/time + ++ptr; + int count = 1; // Start by 1 in order to remove starting brace + std::stringstream ss; + for (; *ptr; ++ptr, ++count) { + if (*ptr == '}') { + ++count; // In order to remove ending brace + break; + } + ss << *ptr; + } + resultingFilename.erase(dateIndex + dateTimeFormatSpecifierStr.size(), count); + fmt = ss.str(); + } else { + fmt = std::string(base::consts::kDefaultDateTimeFormatInFilename); + } + base::SubsecondPrecision ssPrec(3); + std::string now = base::utils::DateTime::getDateTime(fmt.c_str(), &ssPrec); + base::utils::Str::replaceAll(now, '/', '-'); // Replace path element since we are dealing with filename + base::utils::Str::replaceAll(resultingFilename, dateTimeFormatSpecifierStr, now); + } + } + return resultingFilename; +} + +void TypedConfigurations::insertFile(Level level, const std::string& fullFilename) { + std::string resolvedFilename = resolveFilename(fullFilename); + if (resolvedFilename.empty()) { + std::cerr << "Could not load empty file for logging, please re-check your configurations for level [" + << LevelHelper::convertToString(level) << "]"; + } + std::string filePath = base::utils::File::extractPathFromFilename(resolvedFilename, base::consts::kFilePathSeparator); + if (filePath.size() < resolvedFilename.size()) { + base::utils::File::createPath(filePath); + } + auto create = [&](Level level) { + base::LogStreamsReferenceMap::iterator filestreamIter = m_logStreamsReference->find(resolvedFilename); + base::type::fstream_t* fs = nullptr; + if (filestreamIter == m_logStreamsReference->end()) { + // We need a completely new stream, nothing to share with + fs = base::utils::File::newFileStream(resolvedFilename); + m_filenameMap.insert(std::make_pair(level, resolvedFilename)); + m_fileStreamMap.insert(std::make_pair(level, base::FileStreamPtr(fs))); + m_logStreamsReference->insert(std::make_pair(resolvedFilename, base::FileStreamPtr(m_fileStreamMap.at(level)))); + } else { + // Woops! we have an existing one, share it! + m_filenameMap.insert(std::make_pair(level, filestreamIter->first)); + m_fileStreamMap.insert(std::make_pair(level, base::FileStreamPtr(filestreamIter->second))); + fs = filestreamIter->second.get(); + } + if (fs == nullptr) { + // We display bad file error from newFileStream() + ELPP_INTERNAL_ERROR("Setting [TO_FILE] of [" + << LevelHelper::convertToString(level) << "] to FALSE", false); + setValue(level, false, &m_toFileMap); + } + }; + // If we dont have file conf for any level, create it for Level::Global first + // otherwise create for specified level + create(m_filenameMap.empty() && m_fileStreamMap.empty() ? Level::Global : level); +} + +bool TypedConfigurations::unsafeValidateFileRolling(Level level, const PreRollOutCallback& preRollOutCallback) { + base::type::fstream_t* fs = unsafeGetConfigByRef(level, &m_fileStreamMap, "fileStream").get(); + if (fs == nullptr) { + return true; + } + std::size_t maxLogFileSize = unsafeGetConfigByVal(level, &m_maxLogFileSizeMap, "maxLogFileSize"); + std::size_t currFileSize = base::utils::File::getSizeOfFile(fs); + if (maxLogFileSize != 0 && currFileSize >= maxLogFileSize) { + std::string fname = unsafeGetConfigByRef(level, &m_filenameMap, "filename"); + ELPP_INTERNAL_INFO(1, "Truncating log file [" << fname << "] as a result of configurations for level [" + << LevelHelper::convertToString(level) << "]"); + fs->close(); + preRollOutCallback(fname.c_str(), currFileSize); + fs->open(fname, std::fstream::out | std::fstream::trunc); + return true; + } + return false; +} + +// RegisteredHitCounters + +bool RegisteredHitCounters::validateEveryN(const char* filename, base::type::LineNumber lineNumber, std::size_t n) { + base::threading::ScopedLock scopedLock(lock()); + base::HitCounter* counter = get(filename, lineNumber); + if (counter == nullptr) { + registerNew(counter = new base::HitCounter(filename, lineNumber)); + } + counter->validateHitCounts(n); + bool result = (n >= 1 && counter->hitCounts() != 0 && counter->hitCounts() % n == 0); + return result; +} + +/// @brief Validates counter for hits >= N, i.e, registers new if does not exist otherwise updates original one +/// @return True if validation resulted in triggering hit. Meaning logs should be written everytime true is returned +bool RegisteredHitCounters::validateAfterN(const char* filename, base::type::LineNumber lineNumber, std::size_t n) { + base::threading::ScopedLock scopedLock(lock()); + base::HitCounter* counter = get(filename, lineNumber); + if (counter == nullptr) { + registerNew(counter = new base::HitCounter(filename, lineNumber)); + } + // Do not use validateHitCounts here since we do not want to reset counter here + // Note the >= instead of > because we are incrementing + // after this check + if (counter->hitCounts() >= n) + return true; + counter->increment(); + return false; +} + +/// @brief Validates counter for hits are <= n, i.e, registers new if does not exist otherwise updates original one +/// @return True if validation resulted in triggering hit. Meaning logs should be written everytime true is returned +bool RegisteredHitCounters::validateNTimes(const char* filename, base::type::LineNumber lineNumber, std::size_t n) { + base::threading::ScopedLock scopedLock(lock()); + base::HitCounter* counter = get(filename, lineNumber); + if (counter == nullptr) { + registerNew(counter = new base::HitCounter(filename, lineNumber)); + } + counter->increment(); + // Do not use validateHitCounts here since we do not want to reset counter here + if (counter->hitCounts() <= n) + return true; + return false; +} + +// RegisteredLoggers + +RegisteredLoggers::RegisteredLoggers(const LogBuilderPtr& defaultLogBuilder) : + m_defaultLogBuilder(defaultLogBuilder) { + m_defaultConfigurations.setToDefault(); + m_logStreamsReference = std::make_shared(); +} + +Logger* RegisteredLoggers::get(const std::string& id, bool forceCreation) { + base::threading::ScopedLock scopedLock(lock()); + Logger* logger_ = base::utils::Registry::get(id); + if (logger_ == nullptr && forceCreation) { + bool validId = Logger::isValidId(id); + if (!validId) { + ELPP_ASSERT(validId, "Invalid logger ID [" << id << "]. Not registering this logger."); + return nullptr; + } + logger_ = new Logger(id, m_defaultConfigurations, m_logStreamsReference); + logger_->m_logBuilder = m_defaultLogBuilder; + registerNew(id, logger_); + LoggerRegistrationCallback* callback = nullptr; + for (const std::pair& h + : m_loggerRegistrationCallbacks) { + callback = h.second.get(); + if (callback != nullptr && callback->enabled()) { + callback->handle(logger_); + } + } + } + return logger_; +} + +bool RegisteredLoggers::remove(const std::string& id) { + if (id == base::consts::kDefaultLoggerId) { + return false; + } + // get has internal lock + Logger* logger = base::utils::Registry::get(id); + if (logger != nullptr) { + // unregister has internal lock + unregister(logger); + } + return true; +} + +void RegisteredLoggers::unsafeFlushAll(void) { + ELPP_INTERNAL_INFO(1, "Flushing all log files"); + for (base::LogStreamsReferenceMap::iterator it = m_logStreamsReference->begin(); + it != m_logStreamsReference->end(); ++it) { + if (it->second.get() == nullptr) continue; + it->second->flush(); + } +} + +// VRegistry + +VRegistry::VRegistry(base::type::VerboseLevel level, base::type::EnumType* pFlags) : m_level(level), m_pFlags(pFlags) { +} + +/// @brief Sets verbose level. Accepted range is 0-9 +void VRegistry::setLevel(base::type::VerboseLevel level) { + base::threading::ScopedLock scopedLock(lock()); + if (level > 9) + m_level = base::consts::kMaxVerboseLevel; + else + m_level = level; +} + +void VRegistry::setModules(const char* modules) { + base::threading::ScopedLock scopedLock(lock()); + auto addSuffix = [](std::stringstream& ss, const char* sfx, const char* prev) { + if (prev != nullptr && base::utils::Str::endsWith(ss.str(), std::string(prev))) { + std::string chr(ss.str().substr(0, ss.str().size() - strlen(prev))); + ss.str(std::string("")); + ss << chr; + } + if (base::utils::Str::endsWith(ss.str(), std::string(sfx))) { + std::string chr(ss.str().substr(0, ss.str().size() - strlen(sfx))); + ss.str(std::string("")); + ss << chr; + } + ss << sfx; + }; + auto insert = [&](std::stringstream& ss, base::type::VerboseLevel level) { + if (!base::utils::hasFlag(LoggingFlag::DisableVModulesExtensions, *m_pFlags)) { + addSuffix(ss, ".h", nullptr); + m_modules.insert(std::make_pair(ss.str(), level)); + addSuffix(ss, ".c", ".h"); + m_modules.insert(std::make_pair(ss.str(), level)); + addSuffix(ss, ".cpp", ".c"); + m_modules.insert(std::make_pair(ss.str(), level)); + addSuffix(ss, ".cc", ".cpp"); + m_modules.insert(std::make_pair(ss.str(), level)); + addSuffix(ss, ".cxx", ".cc"); + m_modules.insert(std::make_pair(ss.str(), level)); + addSuffix(ss, ".-inl.h", ".cxx"); + m_modules.insert(std::make_pair(ss.str(), level)); + addSuffix(ss, ".hxx", ".-inl.h"); + m_modules.insert(std::make_pair(ss.str(), level)); + addSuffix(ss, ".hpp", ".hxx"); + m_modules.insert(std::make_pair(ss.str(), level)); + addSuffix(ss, ".hh", ".hpp"); + } + m_modules.insert(std::make_pair(ss.str(), level)); + }; + bool isMod = true; + bool isLevel = false; + std::stringstream ss; + int level = -1; + for (; *modules; ++modules) { + switch (*modules) { + case '=': + isLevel = true; + isMod = false; + break; + case ',': + isLevel = false; + isMod = true; + if (!ss.str().empty() && level != -1) { + insert(ss, static_cast(level)); + ss.str(std::string("")); + level = -1; + } + break; + default: + if (isMod) { + ss << *modules; + } else if (isLevel) { + if (isdigit(*modules)) { + level = static_cast(*modules) - 48; + } + } + break; + } + } + if (!ss.str().empty() && level != -1) { + insert(ss, static_cast(level)); + } +} + +bool VRegistry::allowed(base::type::VerboseLevel vlevel, const char* file) { + base::threading::ScopedLock scopedLock(lock()); + if (m_modules.empty() || file == nullptr) { + return vlevel <= m_level; + } else { + char baseFilename[base::consts::kSourceFilenameMaxLength] = ""; + base::utils::File::buildBaseFilename(file, baseFilename); + std::unordered_map::iterator it = m_modules.begin(); + for (; it != m_modules.end(); ++it) { + if (base::utils::Str::wildCardMatch(baseFilename, it->first.c_str())) { + return vlevel <= it->second; + } + } + if (base::utils::hasFlag(LoggingFlag::AllowVerboseIfModuleNotSpecified, *m_pFlags)) { + return true; + } + return false; + } +} + +void VRegistry::setFromArgs(const base::utils::CommandLineArgs* commandLineArgs) { + if (commandLineArgs->hasParam("-v") || commandLineArgs->hasParam("--verbose") || + commandLineArgs->hasParam("-V") || commandLineArgs->hasParam("--VERBOSE")) { + setLevel(base::consts::kMaxVerboseLevel); + } else if (commandLineArgs->hasParamWithValue("--v")) { + setLevel(static_cast(atoi(commandLineArgs->getParamValue("--v")))); + } else if (commandLineArgs->hasParamWithValue("--V")) { + setLevel(static_cast(atoi(commandLineArgs->getParamValue("--V")))); + } else if ((commandLineArgs->hasParamWithValue("-vmodule")) && vModulesEnabled()) { + setModules(commandLineArgs->getParamValue("-vmodule")); + } else if (commandLineArgs->hasParamWithValue("-VMODULE") && vModulesEnabled()) { + setModules(commandLineArgs->getParamValue("-VMODULE")); + } +} + +#if !defined(ELPP_DEFAULT_LOGGING_FLAGS) +# define ELPP_DEFAULT_LOGGING_FLAGS 0x0 +#endif // !defined(ELPP_DEFAULT_LOGGING_FLAGS) +// Storage +#if ELPP_ASYNC_LOGGING +Storage::Storage(const LogBuilderPtr& defaultLogBuilder, base::IWorker* asyncDispatchWorker) : +#else +Storage::Storage(const LogBuilderPtr& defaultLogBuilder) : +#endif // ELPP_ASYNC_LOGGING + m_registeredHitCounters(new base::RegisteredHitCounters()), + m_registeredLoggers(new base::RegisteredLoggers(defaultLogBuilder)), + m_flags(ELPP_DEFAULT_LOGGING_FLAGS), + m_vRegistry(new base::VRegistry(0, &m_flags)), + +#if ELPP_ASYNC_LOGGING + m_asyncLogQueue(new base::AsyncLogQueue()), + m_asyncDispatchWorker(asyncDispatchWorker), +#endif // ELPP_ASYNC_LOGGING + + m_preRollOutCallback(base::defaultPreRollOutCallback) { + // Register default logger + m_registeredLoggers->get(std::string(base::consts::kDefaultLoggerId)); + // We register default logger anyway (worse case it's not going to register) just in case + m_registeredLoggers->get("default"); + +#if defined(ELPP_FEATURE_ALL) || defined(ELPP_FEATURE_PERFORMANCE_TRACKING) + // Register performance logger and reconfigure format + Logger* performanceLogger = m_registeredLoggers->get(std::string(base::consts::kPerformanceLoggerId)); + m_registeredLoggers->get("performance"); + performanceLogger->configurations()->setGlobally(ConfigurationType::Format, std::string("%datetime %level %msg")); + performanceLogger->reconfigure(); +#endif // defined(ELPP_FEATURE_ALL) || defined(ELPP_FEATURE_PERFORMANCE_TRACKING) + +#if defined(ELPP_SYSLOG) + // Register syslog logger and reconfigure format + Logger* sysLogLogger = m_registeredLoggers->get(std::string(base::consts::kSysLogLoggerId)); + sysLogLogger->configurations()->setGlobally(ConfigurationType::Format, std::string("%level: %msg")); + sysLogLogger->reconfigure(); +#endif // defined(ELPP_SYSLOG) + addFlag(LoggingFlag::AllowVerboseIfModuleNotSpecified); +#if ELPP_ASYNC_LOGGING + installLogDispatchCallback(std::string("AsyncLogDispatchCallback")); +#else + installLogDispatchCallback(std::string("DefaultLogDispatchCallback")); +#endif // ELPP_ASYNC_LOGGING +#if defined(ELPP_FEATURE_ALL) || defined(ELPP_FEATURE_PERFORMANCE_TRACKING) + installPerformanceTrackingCallback + (std::string("DefaultPerformanceTrackingCallback")); +#endif // defined(ELPP_FEATURE_ALL) || defined(ELPP_FEATURE_PERFORMANCE_TRACKING) + ELPP_INTERNAL_INFO(1, "Easylogging++ has been initialized"); +#if ELPP_ASYNC_LOGGING + m_asyncDispatchWorker->start(); +#endif // ELPP_ASYNC_LOGGING +} + +Storage::~Storage(void) { + ELPP_INTERNAL_INFO(4, "Destroying storage"); +#if ELPP_ASYNC_LOGGING + ELPP_INTERNAL_INFO(5, "Replacing log dispatch callback to synchronous"); + uninstallLogDispatchCallback(std::string("AsyncLogDispatchCallback")); + installLogDispatchCallback(std::string("DefaultLogDispatchCallback")); + ELPP_INTERNAL_INFO(5, "Destroying asyncDispatchWorker"); + base::utils::safeDelete(m_asyncDispatchWorker); + ELPP_INTERNAL_INFO(5, "Destroying asyncLogQueue"); + base::utils::safeDelete(m_asyncLogQueue); +#endif // ELPP_ASYNC_LOGGING + ELPP_INTERNAL_INFO(5, "Destroying registeredHitCounters"); + base::utils::safeDelete(m_registeredHitCounters); + ELPP_INTERNAL_INFO(5, "Destroying registeredLoggers"); + base::utils::safeDelete(m_registeredLoggers); + ELPP_INTERNAL_INFO(5, "Destroying vRegistry"); + base::utils::safeDelete(m_vRegistry); +} + +bool Storage::hasCustomFormatSpecifier(const char* formatSpecifier) { + base::threading::ScopedLock scopedLock(customFormatSpecifiersLock()); + return std::find(m_customFormatSpecifiers.begin(), m_customFormatSpecifiers.end(), + formatSpecifier) != m_customFormatSpecifiers.end(); +} + +void Storage::installCustomFormatSpecifier(const CustomFormatSpecifier& customFormatSpecifier) { + if (hasCustomFormatSpecifier(customFormatSpecifier.formatSpecifier())) { + return; + } + base::threading::ScopedLock scopedLock(customFormatSpecifiersLock()); + m_customFormatSpecifiers.push_back(customFormatSpecifier); +} + +bool Storage::uninstallCustomFormatSpecifier(const char* formatSpecifier) { + base::threading::ScopedLock scopedLock(customFormatSpecifiersLock()); + std::vector::iterator it = std::find(m_customFormatSpecifiers.begin(), + m_customFormatSpecifiers.end(), formatSpecifier); + if (it != m_customFormatSpecifiers.end() && strcmp(formatSpecifier, it->formatSpecifier()) == 0) { + m_customFormatSpecifiers.erase(it); + return true; + } + return false; +} + +void Storage::setApplicationArguments(int argc, char** argv) { + m_commandLineArgs.setArgs(argc, argv); + m_vRegistry->setFromArgs(commandLineArgs()); + // default log file +#if !defined(ELPP_DISABLE_LOG_FILE_FROM_ARG) + if (m_commandLineArgs.hasParamWithValue(base::consts::kDefaultLogFileParam)) { + Configurations c; + c.setGlobally(ConfigurationType::Filename, + std::string(m_commandLineArgs.getParamValue(base::consts::kDefaultLogFileParam))); + registeredLoggers()->setDefaultConfigurations(c); + for (base::RegisteredLoggers::iterator it = registeredLoggers()->begin(); + it != registeredLoggers()->end(); ++it) { + it->second->configure(c); + } + } +#endif // !defined(ELPP_DISABLE_LOG_FILE_FROM_ARG) +#if defined(ELPP_LOGGING_FLAGS_FROM_ARG) + if (m_commandLineArgs.hasParamWithValue(base::consts::kLoggingFlagsParam)) { + int userInput = atoi(m_commandLineArgs.getParamValue(base::consts::kLoggingFlagsParam)); + if (ELPP_DEFAULT_LOGGING_FLAGS == 0x0) { + m_flags = userInput; + } else { + base::utils::addFlag(userInput, &m_flags); + } + } +#endif // defined(ELPP_LOGGING_FLAGS_FROM_ARG) +} + +} // namespace base + +// LogDispatchCallback +#if defined(ELPP_THREAD_SAFE) +void LogDispatchCallback::handle(const LogDispatchData* data) { + base::threading::ScopedLock scopedLock(m_fileLocksMapLock); + std::string filename = data->logMessage()->logger()->typedConfigurations()->filename(data->logMessage()->level()); + auto lock = m_fileLocks.find(filename); + if (lock == m_fileLocks.end()) { + m_fileLocks.emplace(std::make_pair(filename, std::unique_ptr(new base::threading::Mutex))); + } +} +#else +void LogDispatchCallback::handle(const LogDispatchData* /*data*/) {} +#endif + +base::threading::Mutex& LogDispatchCallback::fileHandle(const LogDispatchData* data) { + auto it = m_fileLocks.find(data->logMessage()->logger()->typedConfigurations()->filename(data->logMessage()->level())); + return *(it->second.get()); +} + +namespace base { +// DefaultLogDispatchCallback + +void DefaultLogDispatchCallback::handle(const LogDispatchData* data) { +#if defined(ELPP_THREAD_SAFE) + LogDispatchCallback::handle(data); + base::threading::ScopedLock scopedLock(fileHandle(data)); +#endif + m_data = data; + dispatch(m_data->logMessage()->logger()->logBuilder()->build(m_data->logMessage(), + m_data->dispatchAction() == base::DispatchAction::NormalLog)); +} + +void DefaultLogDispatchCallback::dispatch(base::type::string_t&& logLine) { + if (m_data->dispatchAction() == base::DispatchAction::NormalLog) { + if (m_data->logMessage()->logger()->m_typedConfigurations->toFile(m_data->logMessage()->level())) { + base::type::fstream_t* fs = m_data->logMessage()->logger()->m_typedConfigurations->fileStream( + m_data->logMessage()->level()); + if (fs != nullptr) { + fs->write(logLine.c_str(), logLine.size()); + if (fs->fail()) { + ELPP_INTERNAL_ERROR("Unable to write log to file [" + << m_data->logMessage()->logger()->m_typedConfigurations->filename(m_data->logMessage()->level()) << "].\n" + << "Few possible reasons (could be something else):\n" << " * Permission denied\n" + << " * Disk full\n" << " * Disk is not writable", true); + } else { + if (ELPP->hasFlag(LoggingFlag::ImmediateFlush) + || (m_data->logMessage()->logger()->isFlushNeeded(m_data->logMessage()->level()))) { + m_data->logMessage()->logger()->flush(m_data->logMessage()->level(), fs); + } + } + } else { + ELPP_INTERNAL_ERROR("Log file for [" << LevelHelper::convertToString(m_data->logMessage()->level()) << "] " + << "has not been configured but [TO_FILE] is configured to TRUE. [Logger ID: " + << m_data->logMessage()->logger()->id() << "]", false); + } + } + if (m_data->logMessage()->logger()->m_typedConfigurations->toStandardOutput(m_data->logMessage()->level())) { + if (ELPP->hasFlag(LoggingFlag::ColoredTerminalOutput)) + m_data->logMessage()->logger()->logBuilder()->convertToColoredOutput(&logLine, m_data->logMessage()->level()); + ELPP_COUT << ELPP_COUT_LINE(logLine); + } + } +#if defined(ELPP_SYSLOG) + else if (m_data->dispatchAction() == base::DispatchAction::SysLog) { + // Determine syslog priority + int sysLogPriority = 0; + if (m_data->logMessage()->level() == Level::Fatal) + sysLogPriority = LOG_EMERG; + else if (m_data->logMessage()->level() == Level::Error) + sysLogPriority = LOG_ERR; + else if (m_data->logMessage()->level() == Level::Warning) + sysLogPriority = LOG_WARNING; + else if (m_data->logMessage()->level() == Level::Info) + sysLogPriority = LOG_INFO; + else if (m_data->logMessage()->level() == Level::Debug) + sysLogPriority = LOG_DEBUG; + else + sysLogPriority = LOG_NOTICE; +# if defined(ELPP_UNICODE) + char* line = base::utils::Str::wcharPtrToCharPtr(logLine.c_str()); + syslog(sysLogPriority, "%s", line); + free(line); +# else + syslog(sysLogPriority, "%s", logLine.c_str()); +# endif + } +#endif // defined(ELPP_SYSLOG) +} + +#if ELPP_ASYNC_LOGGING + +// AsyncLogDispatchCallback + +void AsyncLogDispatchCallback::handle(const LogDispatchData* data) { + base::type::string_t logLine = data->logMessage()->logger()->logBuilder()->build(data->logMessage(), + data->dispatchAction() == base::DispatchAction::NormalLog); + if (data->dispatchAction() == base::DispatchAction::NormalLog + && data->logMessage()->logger()->typedConfigurations()->toStandardOutput(data->logMessage()->level())) { + if (ELPP->hasFlag(LoggingFlag::ColoredTerminalOutput)) + data->logMessage()->logger()->logBuilder()->convertToColoredOutput(&logLine, data->logMessage()->level()); + ELPP_COUT << ELPP_COUT_LINE(logLine); + } + // Save resources and only queue if we want to write to file otherwise just ignore handler + if (data->logMessage()->logger()->typedConfigurations()->toFile(data->logMessage()->level())) { + ELPP->asyncLogQueue()->push(AsyncLogItem(*(data->logMessage()), *data, logLine)); + } +} + +// AsyncDispatchWorker +AsyncDispatchWorker::AsyncDispatchWorker() { + setContinueRunning(false); +} + +AsyncDispatchWorker::~AsyncDispatchWorker() { + setContinueRunning(false); + ELPP_INTERNAL_INFO(6, "Stopping dispatch worker - Cleaning log queue"); + clean(); + ELPP_INTERNAL_INFO(6, "Log queue cleaned"); +} + +bool AsyncDispatchWorker::clean(void) { + std::mutex m; + std::unique_lock lk(m); + cv.wait(lk, [] { return !ELPP->asyncLogQueue()->empty(); }); + emptyQueue(); + lk.unlock(); + cv.notify_one(); + return ELPP->asyncLogQueue()->empty(); +} + +void AsyncDispatchWorker::emptyQueue(void) { + while (!ELPP->asyncLogQueue()->empty()) { + AsyncLogItem data = ELPP->asyncLogQueue()->next(); + handle(&data); + base::threading::msleep(100); + } +} + +void AsyncDispatchWorker::start(void) { + base::threading::msleep(5000); // 5s (why?) + setContinueRunning(true); + std::thread t1(&AsyncDispatchWorker::run, this); + t1.join(); +} + +void AsyncDispatchWorker::handle(AsyncLogItem* logItem) { + LogDispatchData* data = logItem->data(); + LogMessage* logMessage = logItem->logMessage(); + Logger* logger = logMessage->logger(); + base::TypedConfigurations* conf = logger->typedConfigurations(); + base::type::string_t logLine = logItem->logLine(); + if (data->dispatchAction() == base::DispatchAction::NormalLog) { + if (conf->toFile(logMessage->level())) { + base::type::fstream_t* fs = conf->fileStream(logMessage->level()); + if (fs != nullptr) { + fs->write(logLine.c_str(), logLine.size()); + if (fs->fail()) { + ELPP_INTERNAL_ERROR("Unable to write log to file [" + << conf->filename(logMessage->level()) << "].\n" + << "Few possible reasons (could be something else):\n" << " * Permission denied\n" + << " * Disk full\n" << " * Disk is not writable", true); + } else { + if (ELPP->hasFlag(LoggingFlag::ImmediateFlush) || (logger->isFlushNeeded(logMessage->level()))) { + logger->flush(logMessage->level(), fs); + } + } + } else { + ELPP_INTERNAL_ERROR("Log file for [" << LevelHelper::convertToString(logMessage->level()) << "] " + << "has not been configured but [TO_FILE] is configured to TRUE. [Logger ID: " << logger->id() << "]", false); + } + } + } +# if defined(ELPP_SYSLOG) + else if (data->dispatchAction() == base::DispatchAction::SysLog) { + // Determine syslog priority + int sysLogPriority = 0; + if (logMessage->level() == Level::Fatal) + sysLogPriority = LOG_EMERG; + else if (logMessage->level() == Level::Error) + sysLogPriority = LOG_ERR; + else if (logMessage->level() == Level::Warning) + sysLogPriority = LOG_WARNING; + else if (logMessage->level() == Level::Info) + sysLogPriority = LOG_INFO; + else if (logMessage->level() == Level::Debug) + sysLogPriority = LOG_DEBUG; + else + sysLogPriority = LOG_NOTICE; +# if defined(ELPP_UNICODE) + char* line = base::utils::Str::wcharPtrToCharPtr(logLine.c_str()); + syslog(sysLogPriority, "%s", line); + free(line); +# else + syslog(sysLogPriority, "%s", logLine.c_str()); +# endif + } +# endif // defined(ELPP_SYSLOG) +} + +void AsyncDispatchWorker::run(void) { + while (continueRunning()) { + emptyQueue(); + base::threading::msleep(10); // 10ms + } +} +#endif // ELPP_ASYNC_LOGGING + +// DefaultLogBuilder + +base::type::string_t DefaultLogBuilder::build(const LogMessage* logMessage, bool appendNewLine) const { + base::TypedConfigurations* tc = logMessage->logger()->typedConfigurations(); + const base::LogFormat* logFormat = &tc->logFormat(logMessage->level()); + base::type::string_t logLine = logFormat->format(); + char buff[base::consts::kSourceFilenameMaxLength + base::consts::kSourceLineMaxLength] = ""; + const char* bufLim = buff + sizeof(buff); + if (logFormat->hasFlag(base::FormatFlags::AppName)) { + // App name + base::utils::Str::replaceFirstWithEscape(logLine, base::consts::kAppNameFormatSpecifier, + logMessage->logger()->parentApplicationName()); + } + if (logFormat->hasFlag(base::FormatFlags::ThreadId)) { + // Thread ID + base::utils::Str::replaceFirstWithEscape(logLine, base::consts::kThreadIdFormatSpecifier, + ELPP->getThreadName(base::threading::getCurrentThreadId())); + } + if (logFormat->hasFlag(base::FormatFlags::DateTime)) { + // DateTime + base::utils::Str::replaceFirstWithEscape(logLine, base::consts::kDateTimeFormatSpecifier, + base::utils::DateTime::getDateTime(logFormat->dateTimeFormat().c_str(), + &tc->subsecondPrecision(logMessage->level()))); + } + if (logFormat->hasFlag(base::FormatFlags::Function)) { + // Function + base::utils::Str::replaceFirstWithEscape(logLine, base::consts::kLogFunctionFormatSpecifier, logMessage->func()); + } + if (logFormat->hasFlag(base::FormatFlags::File)) { + // File + base::utils::Str::clearBuff(buff, base::consts::kSourceFilenameMaxLength); + base::utils::File::buildStrippedFilename(logMessage->file().c_str(), buff); + base::utils::Str::replaceFirstWithEscape(logLine, base::consts::kLogFileFormatSpecifier, std::string(buff)); + } + if (logFormat->hasFlag(base::FormatFlags::FileBase)) { + // FileBase + base::utils::Str::clearBuff(buff, base::consts::kSourceFilenameMaxLength); + base::utils::File::buildBaseFilename(logMessage->file(), buff); + base::utils::Str::replaceFirstWithEscape(logLine, base::consts::kLogFileBaseFormatSpecifier, std::string(buff)); + } + if (logFormat->hasFlag(base::FormatFlags::Line)) { + // Line + char* buf = base::utils::Str::clearBuff(buff, base::consts::kSourceLineMaxLength); + buf = base::utils::Str::convertAndAddToBuff(logMessage->line(), base::consts::kSourceLineMaxLength, buf, bufLim, false); + base::utils::Str::replaceFirstWithEscape(logLine, base::consts::kLogLineFormatSpecifier, std::string(buff)); + } + if (logFormat->hasFlag(base::FormatFlags::Location)) { + // Location + char* buf = base::utils::Str::clearBuff(buff, + base::consts::kSourceFilenameMaxLength + base::consts::kSourceLineMaxLength); + base::utils::File::buildStrippedFilename(logMessage->file().c_str(), buff); + buf = base::utils::Str::addToBuff(buff, buf, bufLim); + buf = base::utils::Str::addToBuff(":", buf, bufLim); + buf = base::utils::Str::convertAndAddToBuff(logMessage->line(), base::consts::kSourceLineMaxLength, buf, bufLim, + false); + base::utils::Str::replaceFirstWithEscape(logLine, base::consts::kLogLocationFormatSpecifier, std::string(buff)); + } + if (logMessage->level() == Level::Verbose && logFormat->hasFlag(base::FormatFlags::VerboseLevel)) { + // Verbose level + char* buf = base::utils::Str::clearBuff(buff, 1); + buf = base::utils::Str::convertAndAddToBuff(logMessage->verboseLevel(), 1, buf, bufLim, false); + base::utils::Str::replaceFirstWithEscape(logLine, base::consts::kVerboseLevelFormatSpecifier, std::string(buff)); + } + if (logFormat->hasFlag(base::FormatFlags::LogMessage)) { + // Log message + base::utils::Str::replaceFirstWithEscape(logLine, base::consts::kMessageFormatSpecifier, logMessage->message()); + } +#if !defined(ELPP_DISABLE_CUSTOM_FORMAT_SPECIFIERS) + el::base::threading::ScopedLock lock_(ELPP->customFormatSpecifiersLock()); + ELPP_UNUSED(lock_); + for (std::vector::const_iterator it = ELPP->customFormatSpecifiers()->begin(); + it != ELPP->customFormatSpecifiers()->end(); ++it) { + std::string fs(it->formatSpecifier()); + base::type::string_t wcsFormatSpecifier(fs.begin(), fs.end()); + base::utils::Str::replaceFirstWithEscape(logLine, wcsFormatSpecifier, it->resolver()(logMessage)); + } +#endif // !defined(ELPP_DISABLE_CUSTOM_FORMAT_SPECIFIERS) + if (appendNewLine) logLine += ELPP_LITERAL("\n"); + return logLine; +} + +// LogDispatcher + +void LogDispatcher::dispatch(void) { + if (m_proceed && m_dispatchAction == base::DispatchAction::None) { + m_proceed = false; + } + if (!m_proceed) { + return; + } +#ifndef ELPP_NO_GLOBAL_LOCK + // see https://github.com/muflihun/easyloggingpp/issues/580 + // global lock is turned on by default unless + // ELPP_NO_GLOBAL_LOCK is defined + base::threading::ScopedLock scopedLock(ELPP->lock()); +#endif + base::TypedConfigurations* tc = m_logMessage->logger()->m_typedConfigurations; + if (ELPP->hasFlag(LoggingFlag::StrictLogFileSizeCheck)) { + tc->validateFileRolling(m_logMessage->level(), ELPP->preRollOutCallback()); + } + LogDispatchCallback* callback = nullptr; + LogDispatchData data; + for (const std::pair& h + : ELPP->m_logDispatchCallbacks) { + callback = h.second.get(); + if (callback != nullptr && callback->enabled()) { + data.setLogMessage(m_logMessage); + data.setDispatchAction(m_dispatchAction); + callback->handle(&data); + } + } +} + +// MessageBuilder + +void MessageBuilder::initialize(Logger* logger) { + m_logger = logger; + m_containerLogSeparator = ELPP->hasFlag(LoggingFlag::NewLineForContainer) ? + ELPP_LITERAL("\n ") : ELPP_LITERAL(", "); +} + +MessageBuilder& MessageBuilder::operator<<(const wchar_t* msg) { + if (msg == nullptr) { + m_logger->stream() << base::consts::kNullPointer; + return *this; + } +# if defined(ELPP_UNICODE) + m_logger->stream() << msg; +# else + char* buff_ = base::utils::Str::wcharPtrToCharPtr(msg); + m_logger->stream() << buff_; + free(buff_); +# endif + if (ELPP->hasFlag(LoggingFlag::AutoSpacing)) { + m_logger->stream() << " "; + } + return *this; +} + +// Writer + +Writer& Writer::construct(Logger* logger, bool needLock) { + m_logger = logger; + initializeLogger(logger->id(), false, needLock); + m_messageBuilder.initialize(m_logger); + return *this; +} + +Writer& Writer::construct(int count, const char* loggerIds, ...) { + if (ELPP->hasFlag(LoggingFlag::MultiLoggerSupport)) { + va_list loggersList; + va_start(loggersList, loggerIds); + const char* id = loggerIds; + m_loggerIds.reserve(count); + for (int i = 0; i < count; ++i) { + m_loggerIds.push_back(std::string(id)); + id = va_arg(loggersList, const char*); + } + va_end(loggersList); + initializeLogger(m_loggerIds.at(0)); + } else { + initializeLogger(std::string(loggerIds)); + } + m_messageBuilder.initialize(m_logger); + return *this; +} + +void Writer::initializeLogger(const std::string& loggerId, bool lookup, bool needLock) { + if (lookup) { + m_logger = ELPP->registeredLoggers()->get(loggerId, ELPP->hasFlag(LoggingFlag::CreateLoggerAutomatically)); + } + if (m_logger == nullptr) { + { + if (!ELPP->registeredLoggers()->has(std::string(base::consts::kDefaultLoggerId))) { + // Somehow default logger has been unregistered. Not good! Register again + ELPP->registeredLoggers()->get(std::string(base::consts::kDefaultLoggerId)); + } + } + Writer(Level::Debug, m_file, m_line, m_func).construct(1, base::consts::kDefaultLoggerId) + << "Logger [" << loggerId << "] is not registered yet!"; + m_proceed = false; + } else { + if (needLock) { + m_logger->acquireLock(); // This should not be unlocked by checking m_proceed because + // m_proceed can be changed by lines below + } + if (ELPP->hasFlag(LoggingFlag::HierarchicalLogging)) { + m_proceed = m_level == Level::Verbose ? m_logger->enabled(m_level) : + LevelHelper::castToInt(m_level) >= LevelHelper::castToInt(ELPP->m_loggingLevel); + } else { + m_proceed = m_logger->enabled(m_level); + } + } +} + +void Writer::processDispatch() { +#if ELPP_LOGGING_ENABLED + if (ELPP->hasFlag(LoggingFlag::MultiLoggerSupport)) { + bool firstDispatched = false; + base::type::string_t logMessage; + std::size_t i = 0; + do { + if (m_proceed) { + if (firstDispatched) { + m_logger->stream() << logMessage; + } else { + firstDispatched = true; + if (m_loggerIds.size() > 1) { + logMessage = m_logger->stream().str(); + } + } + triggerDispatch(); + } else if (m_logger != nullptr) { + m_logger->stream().str(ELPP_LITERAL("")); + m_logger->releaseLock(); + } + if (i + 1 < m_loggerIds.size()) { + initializeLogger(m_loggerIds.at(i + 1)); + } + } while (++i < m_loggerIds.size()); + } else { + if (m_proceed) { + triggerDispatch(); + } else if (m_logger != nullptr) { + m_logger->stream().str(ELPP_LITERAL("")); + m_logger->releaseLock(); + } + } +#else + if (m_logger != nullptr) { + m_logger->stream().str(ELPP_LITERAL("")); + m_logger->releaseLock(); + } +#endif // ELPP_LOGGING_ENABLED +} + +void Writer::triggerDispatch(void) { + try { + if (m_proceed) { + if (m_msg == nullptr) { + LogMessage msg(m_level, m_file, m_line, m_func, m_verboseLevel, + m_logger); + base::LogDispatcher(m_proceed, &msg, m_dispatchAction).dispatch(); + } else { + base::LogDispatcher(m_proceed, m_msg, m_dispatchAction).dispatch(); + } + } + if (m_logger != nullptr) { + m_logger->stream().str(ELPP_LITERAL("")); + m_logger->releaseLock(); + } + if (m_proceed && m_level == Level::Fatal + && !ELPP->hasFlag(LoggingFlag::DisableApplicationAbortOnFatalLog)) { + base::Writer(Level::Warning, m_file, m_line, m_func).construct(1, base::consts::kDefaultLoggerId) + << "Aborting application. Reason: Fatal log at [" << m_file << ":" << m_line << "]"; + std::stringstream reasonStream; + reasonStream << "Fatal log at [" << m_file << ":" << m_line << "]" + << " If you wish to disable 'abort on fatal log' please use " + << "el::Loggers::addFlag(el::LoggingFlag::DisableApplicationAbortOnFatalLog)"; + base::utils::abort(1, reasonStream.str()); + } + m_proceed = false; + } + catch(std::exception & ex){ + // Extremely low memory situation; don't let exception be unhandled. + } +} + +// PErrorWriter + +PErrorWriter::~PErrorWriter(void) { + if (m_proceed) { +#if ELPP_COMPILER_MSVC + char buff[256]; + strerror_s(buff, 256, errno); + m_logger->stream() << ": " << buff << " [" << errno << "]"; +#else + m_logger->stream() << ": " << strerror(errno) << " [" << errno << "]"; +#endif + } +} + +// PerformanceTracker + +#if defined(ELPP_FEATURE_ALL) || defined(ELPP_FEATURE_PERFORMANCE_TRACKING) + +PerformanceTracker::PerformanceTracker(const std::string& blockName, + base::TimestampUnit timestampUnit, + const std::string& loggerId, + bool scopedLog, Level level) : + m_blockName(blockName), m_timestampUnit(timestampUnit), m_loggerId(loggerId), m_scopedLog(scopedLog), + m_level(level), m_hasChecked(false), m_lastCheckpointId(std::string()), m_enabled(false) { +#if !defined(ELPP_DISABLE_PERFORMANCE_TRACKING) && ELPP_LOGGING_ENABLED + // We store it locally so that if user happen to change configuration by the end of scope + // or before calling checkpoint, we still depend on state of configuration at time of construction + el::Logger* loggerPtr = ELPP->registeredLoggers()->get(loggerId, false); + m_enabled = loggerPtr != nullptr && loggerPtr->m_typedConfigurations->performanceTracking(m_level); + if (m_enabled) { + base::utils::DateTime::gettimeofday(&m_startTime); + } +#endif // !defined(ELPP_DISABLE_PERFORMANCE_TRACKING) && ELPP_LOGGING_ENABLED +} + +PerformanceTracker::~PerformanceTracker(void) { +#if !defined(ELPP_DISABLE_PERFORMANCE_TRACKING) && ELPP_LOGGING_ENABLED + if (m_enabled) { + base::threading::ScopedLock scopedLock(lock()); + if (m_scopedLog) { + base::utils::DateTime::gettimeofday(&m_endTime); + base::type::string_t formattedTime = getFormattedTimeTaken(); + PerformanceTrackingData data(PerformanceTrackingData::DataType::Complete); + data.init(this); + data.m_formattedTimeTaken = formattedTime; + PerformanceTrackingCallback* callback = nullptr; + for (const std::pair& h + : ELPP->m_performanceTrackingCallbacks) { + callback = h.second.get(); + if (callback != nullptr && callback->enabled()) { + callback->handle(&data); + } + } + } + } +#endif // !defined(ELPP_DISABLE_PERFORMANCE_TRACKING) +} + +void PerformanceTracker::checkpoint(const std::string& id, const char* file, base::type::LineNumber line, + const char* func) { +#if !defined(ELPP_DISABLE_PERFORMANCE_TRACKING) && ELPP_LOGGING_ENABLED + if (m_enabled) { + base::threading::ScopedLock scopedLock(lock()); + base::utils::DateTime::gettimeofday(&m_endTime); + base::type::string_t formattedTime = m_hasChecked ? getFormattedTimeTaken(m_lastCheckpointTime) : ELPP_LITERAL(""); + PerformanceTrackingData data(PerformanceTrackingData::DataType::Checkpoint); + data.init(this); + data.m_checkpointId = id; + data.m_file = file; + data.m_line = line; + data.m_func = func; + data.m_formattedTimeTaken = formattedTime; + PerformanceTrackingCallback* callback = nullptr; + for (const std::pair& h + : ELPP->m_performanceTrackingCallbacks) { + callback = h.second.get(); + if (callback != nullptr && callback->enabled()) { + callback->handle(&data); + } + } + base::utils::DateTime::gettimeofday(&m_lastCheckpointTime); + m_hasChecked = true; + m_lastCheckpointId = id; + } +#endif // !defined(ELPP_DISABLE_PERFORMANCE_TRACKING) && ELPP_LOGGING_ENABLED + ELPP_UNUSED(id); + ELPP_UNUSED(file); + ELPP_UNUSED(line); + ELPP_UNUSED(func); +} + +const base::type::string_t PerformanceTracker::getFormattedTimeTaken(struct timeval startTime) const { + if (ELPP->hasFlag(LoggingFlag::FixedTimeFormat)) { + base::type::stringstream_t ss; + ss << base::utils::DateTime::getTimeDifference(m_endTime, + startTime, m_timestampUnit) << " " << base::consts::kTimeFormats[static_cast + (m_timestampUnit)].unit; + return ss.str(); + } + return base::utils::DateTime::formatTime(base::utils::DateTime::getTimeDifference(m_endTime, + startTime, m_timestampUnit), m_timestampUnit); +} + +#endif // defined(ELPP_FEATURE_ALL) || defined(ELPP_FEATURE_PERFORMANCE_TRACKING) + +namespace debug { +#if defined(ELPP_FEATURE_ALL) || defined(ELPP_FEATURE_CRASH_LOG) + +// StackTrace + +StackTrace::StackTraceEntry::StackTraceEntry(std::size_t index, const std::string& loc, const std::string& demang, + const std::string& hex, + const std::string& addr) : + m_index(index), + m_location(loc), + m_demangled(demang), + m_hex(hex), + m_addr(addr) { +} + +std::ostream& operator<<(std::ostream& ss, const StackTrace::StackTraceEntry& si) { + ss << "[" << si.m_index << "] " << si.m_location << (si.m_hex.empty() ? "" : "+") << si.m_hex << " " << si.m_addr << + (si.m_demangled.empty() ? "" : ":") << si.m_demangled; + return ss; +} + +std::ostream& operator<<(std::ostream& os, const StackTrace& st) { + std::vector::const_iterator it = st.m_stack.begin(); + while (it != st.m_stack.end()) { + os << " " << *it++ << "\n"; + } + return os; +} + +void StackTrace::generateNew(void) { +#ifdef HAVE_EXECINFO + m_stack.clear(); + void* stack[kMaxStack]; + unsigned int size = backtrace(stack, kMaxStack); + char** strings = backtrace_symbols(stack, size); + if (size > kStackStart) { // Skip StackTrace c'tor and generateNew + for (std::size_t i = kStackStart; i < size; ++i) { + std::string mangName; + std::string location; + std::string hex; + std::string addr; + + // entry: 2 crash.cpp.bin 0x0000000101552be5 _ZN2el4base5debug10StackTraceC1Ev + 21 + const std::string line(strings[i]); + auto p = line.find("_"); + if (p != std::string::npos) { + mangName = line.substr(p); + mangName = mangName.substr(0, mangName.find(" +")); + } + p = line.find("0x"); + if (p != std::string::npos) { + addr = line.substr(p); + addr = addr.substr(0, addr.find("_")); + } + // Perform demangling if parsed properly + if (!mangName.empty()) { + int status = 0; + char* demangName = abi::__cxa_demangle(mangName.data(), 0, 0, &status); + // if demangling is successful, output the demangled function name + if (status == 0) { + // Success (see http://gcc.gnu.org/onlinedocs/libstdc++/libstdc++-html-USERS-4.3/a01696.html) + StackTraceEntry entry(i - 1, location, demangName, hex, addr); + m_stack.push_back(entry); + } else { + // Not successful - we will use mangled name + StackTraceEntry entry(i - 1, location, mangName, hex, addr); + m_stack.push_back(entry); + } + free(demangName); + } else { + StackTraceEntry entry(i - 1, line); + m_stack.push_back(entry); + } + } + } + free(strings); +#else + ELPP_INTERNAL_INFO(1, "Stacktrace generation not supported for selected compiler"); +#endif // ELPP_STACKTRACE +} + +// Static helper functions + +static std::string crashReason(int sig) { + std::stringstream ss; + bool foundReason = false; + for (int i = 0; i < base::consts::kCrashSignalsCount; ++i) { + if (base::consts::kCrashSignals[i].numb == sig) { + ss << "Application has crashed due to [" << base::consts::kCrashSignals[i].name << "] signal"; + if (ELPP->hasFlag(el::LoggingFlag::LogDetailedCrashReason)) { + ss << std::endl << + " " << base::consts::kCrashSignals[i].brief << std::endl << + " " << base::consts::kCrashSignals[i].detail; + } + foundReason = true; + } + } + if (!foundReason) { + ss << "Application has crashed due to unknown signal [" << sig << "]"; + } + return ss.str(); +} +/// @brief Logs reason of crash from sig +static void logCrashReason(int sig, bool stackTraceIfAvailable, Level level, const char* logger) { + if (sig == SIGINT && ELPP->hasFlag(el::LoggingFlag::IgnoreSigInt)) { + return; + } + std::stringstream ss; + ss << "CRASH HANDLED; "; + ss << crashReason(sig); +#if ELPP_STACKTRACE + if (stackTraceIfAvailable) { + ss << std::endl << " ======= Backtrace: =========" << std::endl << base::debug::StackTrace(); + } +#else + ELPP_UNUSED(stackTraceIfAvailable); +#endif // ELPP_STACKTRACE + ELPP_WRITE_LOG(el::base::Writer, level, base::DispatchAction::NormalLog, logger) << ss.str(); +} + +static inline void crashAbort(int sig) { + base::utils::abort(sig, std::string()); +} + +/// @brief Default application crash handler +/// +/// @detail This function writes log using 'default' logger, prints stack trace for GCC based compilers and aborts program. +static inline void defaultCrashHandler(int sig) { + base::debug::logCrashReason(sig, true, Level::Fatal, base::consts::kDefaultLoggerId); + base::debug::crashAbort(sig); +} + +// CrashHandler + +CrashHandler::CrashHandler(bool useDefault) { + if (useDefault) { + setHandler(defaultCrashHandler); + } +} + +void CrashHandler::setHandler(const Handler& cHandler) { + m_handler = cHandler; +#if defined(ELPP_HANDLE_SIGABRT) + int i = 0; // SIGABRT is at base::consts::kCrashSignals[0] +#else + int i = 1; +#endif // defined(ELPP_HANDLE_SIGABRT) + for (; i < base::consts::kCrashSignalsCount; ++i) { + m_handler = signal(base::consts::kCrashSignals[i].numb, cHandler); + } +} + +#endif // defined(ELPP_FEATURE_ALL) || defined(ELPP_FEATURE_CRASH_LOG) +} // namespace debug +} // namespace base + +// el + +// Helpers + +#if defined(ELPP_FEATURE_ALL) || defined(ELPP_FEATURE_CRASH_LOG) + +void Helpers::crashAbort(int sig, const char* sourceFile, unsigned int long line) { + std::stringstream ss; + ss << base::debug::crashReason(sig).c_str(); + ss << " - [Called el::Helpers::crashAbort(" << sig << ")]"; + if (sourceFile != nullptr && strlen(sourceFile) > 0) { + ss << " - Source: " << sourceFile; + if (line > 0) + ss << ":" << line; + else + ss << " (line number not specified)"; + } + base::utils::abort(sig, ss.str()); +} + +void Helpers::logCrashReason(int sig, bool stackTraceIfAvailable, Level level, const char* logger) { + el::base::debug::logCrashReason(sig, stackTraceIfAvailable, level, logger); +} + +#endif // defined(ELPP_FEATURE_ALL) || defined(ELPP_FEATURE_CRASH_LOG) + +// Loggers + +Logger* Loggers::getLogger(const std::string& identity, bool registerIfNotAvailable) { + return ELPP->registeredLoggers()->get(identity, registerIfNotAvailable); +} + +void Loggers::setDefaultLogBuilder(el::LogBuilderPtr& logBuilderPtr) { + ELPP->registeredLoggers()->setDefaultLogBuilder(logBuilderPtr); +} + +bool Loggers::unregisterLogger(const std::string& identity) { + return ELPP->registeredLoggers()->remove(identity); +} + +bool Loggers::hasLogger(const std::string& identity) { + return ELPP->registeredLoggers()->has(identity); +} + +Logger* Loggers::reconfigureLogger(Logger* logger, const Configurations& configurations) { + if (!logger) return nullptr; + logger->configure(configurations); + return logger; +} + +Logger* Loggers::reconfigureLogger(const std::string& identity, const Configurations& configurations) { + return Loggers::reconfigureLogger(Loggers::getLogger(identity), configurations); +} + +Logger* Loggers::reconfigureLogger(const std::string& identity, ConfigurationType configurationType, + const std::string& value) { + Logger* logger = Loggers::getLogger(identity); + if (logger == nullptr) { + return nullptr; + } + logger->configurations()->set(Level::Global, configurationType, value); + logger->reconfigure(); + return logger; +} + +void Loggers::reconfigureAllLoggers(const Configurations& configurations) { + for (base::RegisteredLoggers::iterator it = ELPP->registeredLoggers()->begin(); + it != ELPP->registeredLoggers()->end(); ++it) { + Loggers::reconfigureLogger(it->second, configurations); + } +} + +void Loggers::reconfigureAllLoggers(Level level, ConfigurationType configurationType, + const std::string& value) { + for (base::RegisteredLoggers::iterator it = ELPP->registeredLoggers()->begin(); + it != ELPP->registeredLoggers()->end(); ++it) { + Logger* logger = it->second; + logger->configurations()->set(level, configurationType, value); + logger->reconfigure(); + } +} + +void Loggers::setDefaultConfigurations(const Configurations& configurations, bool reconfigureExistingLoggers) { + ELPP->registeredLoggers()->setDefaultConfigurations(configurations); + if (reconfigureExistingLoggers) { + Loggers::reconfigureAllLoggers(configurations); + } +} + +const Configurations* Loggers::defaultConfigurations(void) { + return ELPP->registeredLoggers()->defaultConfigurations(); +} + +const base::LogStreamsReferenceMapPtr Loggers::logStreamsReference(void) { + return ELPP->registeredLoggers()->logStreamsReference(); +} + +base::TypedConfigurations Loggers::defaultTypedConfigurations(void) { + return base::TypedConfigurations( + ELPP->registeredLoggers()->defaultConfigurations(), + ELPP->registeredLoggers()->logStreamsReference()); +} + +std::vector* Loggers::populateAllLoggerIds(std::vector* targetList) { + targetList->clear(); + for (base::RegisteredLoggers::iterator it = ELPP->registeredLoggers()->list().begin(); + it != ELPP->registeredLoggers()->list().end(); ++it) { + targetList->push_back(it->first); + } + return targetList; +} + +void Loggers::configureFromGlobal(const char* globalConfigurationFilePath) { + std::ifstream gcfStream(globalConfigurationFilePath, std::ifstream::in); + ELPP_ASSERT(gcfStream.is_open(), "Unable to open global configuration file [" << globalConfigurationFilePath + << "] for parsing."); + std::string line = std::string(); + std::stringstream ss; + Logger* logger = nullptr; + auto configure = [&](void) { + ELPP_INTERNAL_INFO(8, "Configuring logger: '" << logger->id() << "' with configurations \n" << ss.str() + << "\n--------------"); + Configurations c; + c.parseFromText(ss.str()); + logger->configure(c); + }; + while (gcfStream.good()) { + std::getline(gcfStream, line); + ELPP_INTERNAL_INFO(1, "Parsing line: " << line); + base::utils::Str::trim(line); + if (Configurations::Parser::isComment(line)) continue; + Configurations::Parser::ignoreComments(&line); + base::utils::Str::trim(line); + if (line.size() > 2 && base::utils::Str::startsWith(line, std::string(base::consts::kConfigurationLoggerId))) { + if (!ss.str().empty() && logger != nullptr) { + configure(); + } + ss.str(std::string("")); + line = line.substr(2); + base::utils::Str::trim(line); + if (line.size() > 1) { + ELPP_INTERNAL_INFO(1, "Getting logger: '" << line << "'"); + logger = getLogger(line); + } + } else { + ss << line << "\n"; + } + } + if (!ss.str().empty() && logger != nullptr) { + configure(); + } +} + +bool Loggers::configureFromArg(const char* argKey) { +#if defined(ELPP_DISABLE_CONFIGURATION_FROM_PROGRAM_ARGS) + ELPP_UNUSED(argKey); +#else + if (!Helpers::commandLineArgs()->hasParamWithValue(argKey)) { + return false; + } + configureFromGlobal(Helpers::commandLineArgs()->getParamValue(argKey)); +#endif // defined(ELPP_DISABLE_CONFIGURATION_FROM_PROGRAM_ARGS) + return true; +} + +void Loggers::flushAll(void) { + ELPP->registeredLoggers()->flushAll(); +} + +void Loggers::setVerboseLevel(base::type::VerboseLevel level) { + ELPP->vRegistry()->setLevel(level); +} + +base::type::VerboseLevel Loggers::verboseLevel(void) { + return ELPP->vRegistry()->level(); +} + +void Loggers::setVModules(const char* modules) { + if (ELPP->vRegistry()->vModulesEnabled()) { + ELPP->vRegistry()->setModules(modules); + } +} + +void Loggers::clearVModules(void) { + ELPP->vRegistry()->clearModules(); +} + +// VersionInfo + +const std::string VersionInfo::version(void) { + return std::string("9.96.7"); +} +/// @brief Release date of current version +const std::string VersionInfo::releaseDate(void) { + return std::string("24-11-2018 0728hrs"); +} + +} // namespace el diff --git a/utils/Log/src/easyloggingpp/easylogging++.h b/utils/Log/src/easyloggingpp/easylogging++.h new file mode 100644 index 0000000..04ea9da --- /dev/null +++ b/utils/Log/src/easyloggingpp/easylogging++.h @@ -0,0 +1,4580 @@ +// +// Bismillah ar-Rahmaan ar-Raheem +// +// Easylogging++ v9.96.7 +// Single-header only, cross-platform logging library for C++ applications +// +// Copyright (c) 2012-2018 Amrayn Web Services +// Copyright (c) 2012-2018 @abumusamq +// +// This library is released under the MIT Licence. +// https://github.com/amrayn/easyloggingpp/blob/master/LICENSE +// +// https://amrayn.com +// http://muflihun.com +// + +#ifndef EASYLOGGINGPP_H +#define EASYLOGGINGPP_H + +#define ELPP_THREAD_SAFE // Working in threads. Added by xiaojiazhu +// #define ELPP_STL_LOGGING + +// Compilers and C++0x/C++11 Evaluation +#if __cplusplus >= 201103L +# define ELPP_CXX11 1 +#endif // __cplusplus >= 201103L +#if (defined(__GNUC__)) +# define ELPP_COMPILER_GCC 1 +#else +# define ELPP_COMPILER_GCC 0 +#endif +#if ELPP_COMPILER_GCC +# define ELPP_GCC_VERSION (__GNUC__ * 10000 \ ++ __GNUC_MINOR__ * 100 \ ++ __GNUC_PATCHLEVEL__) +# if defined(__GXX_EXPERIMENTAL_CXX0X__) +# define ELPP_CXX0X 1 +# endif +#endif +// Visual C++ +#if defined(_MSC_VER) +# define ELPP_COMPILER_MSVC 1 +#else +# define ELPP_COMPILER_MSVC 0 +#endif +#define ELPP_CRT_DBG_WARNINGS ELPP_COMPILER_MSVC +#if ELPP_COMPILER_MSVC +# if (_MSC_VER == 1600) +# define ELPP_CXX0X 1 +# elif(_MSC_VER >= 1700) +# define ELPP_CXX11 1 +# endif +#endif +// Clang++ +#if (defined(__clang__) && (__clang__ == 1)) +# define ELPP_COMPILER_CLANG 1 +#else +# define ELPP_COMPILER_CLANG 0 +#endif +#if ELPP_COMPILER_CLANG +# if __has_include() +# include // Make __GLIBCXX__ defined when using libstdc++ +# if !defined(__GLIBCXX__) || __GLIBCXX__ >= 20150426 +# define ELPP_CLANG_SUPPORTS_THREAD +# endif // !defined(__GLIBCXX__) || __GLIBCXX__ >= 20150426 +# endif // __has_include() +#endif +#if (defined(__MINGW32__) || defined(__MINGW64__)) +# define ELPP_MINGW 1 +#else +# define ELPP_MINGW 0 +#endif +#if (defined(__CYGWIN__) && (__CYGWIN__ == 1)) +# define ELPP_CYGWIN 1 +#else +# define ELPP_CYGWIN 0 +#endif +#if (defined(__INTEL_COMPILER)) +# define ELPP_COMPILER_INTEL 1 +#else +# define ELPP_COMPILER_INTEL 0 +#endif +// Operating System Evaluation +// Windows +#if (defined(_WIN32) || defined(_WIN64)) +# define ELPP_OS_WINDOWS 1 +#else +# define ELPP_OS_WINDOWS 0 +#endif +// Linux +#if (defined(__linux) || defined(__linux__)) +# define ELPP_OS_LINUX 1 +#else +# define ELPP_OS_LINUX 0 +#endif +#if (defined(__APPLE__)) +# define ELPP_OS_MAC 1 +#else +# define ELPP_OS_MAC 0 +#endif +#if (defined(__FreeBSD__) || defined(__FreeBSD_kernel__)) +# define ELPP_OS_FREEBSD 1 +#else +# define ELPP_OS_FREEBSD 0 +#endif +#if (defined(__sun)) +# define ELPP_OS_SOLARIS 1 +#else +# define ELPP_OS_SOLARIS 0 +#endif +#if (defined(_AIX)) +# define ELPP_OS_AIX 1 +#else +# define ELPP_OS_AIX 0 +#endif +#if (defined(__NetBSD__)) +# define ELPP_OS_NETBSD 1 +#else +# define ELPP_OS_NETBSD 0 +#endif +#if defined(__EMSCRIPTEN__) +# define ELPP_OS_EMSCRIPTEN 1 +#else +# define ELPP_OS_EMSCRIPTEN 0 +#endif +#if (defined(__QNX__) || defined(__QNXNTO__)) +# define ELPP_OS_QNX 1 +#else +# define ELPP_OS_QNX 0 +#endif +// Unix +#if ((ELPP_OS_LINUX || ELPP_OS_MAC || ELPP_OS_FREEBSD || ELPP_OS_NETBSD || ELPP_OS_SOLARIS || ELPP_OS_AIX || ELPP_OS_EMSCRIPTEN || ELPP_OS_QNX) && (!ELPP_OS_WINDOWS)) +# define ELPP_OS_UNIX 1 +#else +# define ELPP_OS_UNIX 0 +#endif +#if (defined(__ANDROID__)) +# define ELPP_OS_ANDROID 1 +#else +# define ELPP_OS_ANDROID 0 +#endif +// Evaluating Cygwin as *nix OS +#if !ELPP_OS_UNIX && !ELPP_OS_WINDOWS && ELPP_CYGWIN +# undef ELPP_OS_UNIX +# undef ELPP_OS_LINUX +# define ELPP_OS_UNIX 1 +# define ELPP_OS_LINUX 1 +#endif // !ELPP_OS_UNIX && !ELPP_OS_WINDOWS && ELPP_CYGWIN +#if !defined(ELPP_INTERNAL_DEBUGGING_OUT_INFO) +# define ELPP_INTERNAL_DEBUGGING_OUT_INFO std::cout +#endif // !defined(ELPP_INTERNAL_DEBUGGING_OUT) +#if !defined(ELPP_INTERNAL_DEBUGGING_OUT_ERROR) +# define ELPP_INTERNAL_DEBUGGING_OUT_ERROR std::cerr +#endif // !defined(ELPP_INTERNAL_DEBUGGING_OUT) +#if !defined(ELPP_INTERNAL_DEBUGGING_ENDL) +# define ELPP_INTERNAL_DEBUGGING_ENDL std::endl +#endif // !defined(ELPP_INTERNAL_DEBUGGING_OUT) +#if !defined(ELPP_INTERNAL_DEBUGGING_MSG) +# define ELPP_INTERNAL_DEBUGGING_MSG(msg) msg +#endif // !defined(ELPP_INTERNAL_DEBUGGING_OUT) +// Internal Assertions and errors +#if !defined(ELPP_DISABLE_ASSERT) +# if (defined(ELPP_DEBUG_ASSERT_FAILURE)) +# define ELPP_ASSERT(expr, msg) if (!(expr)) { \ +std::stringstream internalInfoStream; internalInfoStream << msg; \ +ELPP_INTERNAL_DEBUGGING_OUT_ERROR \ +<< "EASYLOGGING++ ASSERTION FAILED (LINE: " << __LINE__ << ") [" #expr << "] WITH MESSAGE \"" \ +<< ELPP_INTERNAL_DEBUGGING_MSG(internalInfoStream.str()) << "\"" << ELPP_INTERNAL_DEBUGGING_ENDL; base::utils::abort(1, \ +"ELPP Assertion failure, please define ELPP_DEBUG_ASSERT_FAILURE"); } +# else +# define ELPP_ASSERT(expr, msg) if (!(expr)) { \ +std::stringstream internalInfoStream; internalInfoStream << msg; \ +ELPP_INTERNAL_DEBUGGING_OUT_ERROR\ +<< "ASSERTION FAILURE FROM EASYLOGGING++ (LINE: " \ +<< __LINE__ << ") [" #expr << "] WITH MESSAGE \"" << ELPP_INTERNAL_DEBUGGING_MSG(internalInfoStream.str()) << "\"" \ +<< ELPP_INTERNAL_DEBUGGING_ENDL; } +# endif // (defined(ELPP_DEBUG_ASSERT_FAILURE)) +#else +# define ELPP_ASSERT(x, y) +#endif //(!defined(ELPP_DISABLE_ASSERT) +#if ELPP_COMPILER_MSVC +# define ELPP_INTERNAL_DEBUGGING_WRITE_PERROR \ +{ char buff[256]; strerror_s(buff, 256, errno); \ +ELPP_INTERNAL_DEBUGGING_OUT_ERROR << ": " << buff << " [" << errno << "]";} (void)0 +#else +# define ELPP_INTERNAL_DEBUGGING_WRITE_PERROR \ +ELPP_INTERNAL_DEBUGGING_OUT_ERROR << ": " << strerror(errno) << " [" << errno << "]"; (void)0 +#endif // ELPP_COMPILER_MSVC +#if defined(ELPP_DEBUG_ERRORS) +# if !defined(ELPP_INTERNAL_ERROR) +# define ELPP_INTERNAL_ERROR(msg, pe) { \ +std::stringstream internalInfoStream; internalInfoStream << " " << msg; \ +ELPP_INTERNAL_DEBUGGING_OUT_ERROR \ +<< "ERROR FROM EASYLOGGING++ (LINE: " << __LINE__ << ") " \ +<< ELPP_INTERNAL_DEBUGGING_MSG(internalInfoStream.str()) << ELPP_INTERNAL_DEBUGGING_ENDL; \ +if (pe) { ELPP_INTERNAL_DEBUGGING_OUT_ERROR << " "; ELPP_INTERNAL_DEBUGGING_WRITE_PERROR; }} (void)0 +# endif +#else +# undef ELPP_INTERNAL_INFO +# define ELPP_INTERNAL_ERROR(msg, pe) +#endif // defined(ELPP_DEBUG_ERRORS) +#if (defined(ELPP_DEBUG_INFO)) +# if !(defined(ELPP_INTERNAL_INFO_LEVEL)) +# define ELPP_INTERNAL_INFO_LEVEL 9 +# endif // !(defined(ELPP_INTERNAL_INFO_LEVEL)) +# if !defined(ELPP_INTERNAL_INFO) +# define ELPP_INTERNAL_INFO(lvl, msg) { if (lvl <= ELPP_INTERNAL_INFO_LEVEL) { \ +std::stringstream internalInfoStream; internalInfoStream << " " << msg; \ +ELPP_INTERNAL_DEBUGGING_OUT_INFO << ELPP_INTERNAL_DEBUGGING_MSG(internalInfoStream.str()) \ +<< ELPP_INTERNAL_DEBUGGING_ENDL; }} +# endif +#else +# undef ELPP_INTERNAL_INFO +# define ELPP_INTERNAL_INFO(lvl, msg) +#endif // (defined(ELPP_DEBUG_INFO)) +#if (defined(ELPP_FEATURE_ALL)) || (defined(ELPP_FEATURE_CRASH_LOG)) +# if (ELPP_COMPILER_GCC && !ELPP_MINGW && !ELPP_CYGWIN && !ELPP_OS_ANDROID && !ELPP_OS_EMSCRIPTEN && !ELPP_OS_QNX) +# define ELPP_STACKTRACE 1 +# else +# if ELPP_COMPILER_MSVC +# pragma message("Stack trace not available for this compiler") +# else +# warning "Stack trace not available for this compiler"; +# endif // ELPP_COMPILER_MSVC +# define ELPP_STACKTRACE 0 +# endif // ELPP_COMPILER_GCC +#else +# define ELPP_STACKTRACE 0 +#endif // (defined(ELPP_FEATURE_ALL)) || (defined(ELPP_FEATURE_CRASH_LOG)) +// Miscellaneous macros +#define ELPP_UNUSED(x) (void)x +#if ELPP_OS_UNIX +// Log file permissions for unix-based systems +# define ELPP_LOG_PERMS S_IRUSR | S_IWUSR | S_IXUSR | S_IWGRP | S_IRGRP | S_IXGRP | S_IWOTH | S_IXOTH +#endif // ELPP_OS_UNIX +#if defined(ELPP_AS_DLL) && ELPP_COMPILER_MSVC +# if defined(ELPP_EXPORT_SYMBOLS) +# define ELPP_EXPORT __declspec(dllexport) +# else +# define ELPP_EXPORT __declspec(dllimport) +# endif // defined(ELPP_EXPORT_SYMBOLS) +#else +# define ELPP_EXPORT +#endif // defined(ELPP_AS_DLL) && ELPP_COMPILER_MSVC +// Some special functions that are VC++ specific +#undef STRTOK +#undef STRERROR +#undef STRCAT +#undef STRCPY +#if ELPP_CRT_DBG_WARNINGS +# define STRTOK(a, b, c) strtok_s(a, b, c) +# define STRERROR(a, b, c) strerror_s(a, b, c) +# define STRCAT(a, b, len) strcat_s(a, len, b) +# define STRCPY(a, b, len) strcpy_s(a, len, b) +#else +# define STRTOK(a, b, c) strtok(a, b) +# define STRERROR(a, b, c) strerror(c) +# define STRCAT(a, b, len) strcat(a, b) +# define STRCPY(a, b, len) strcpy(a, b) +#endif +// Compiler specific support evaluations +#if (ELPP_MINGW && !defined(ELPP_FORCE_USE_STD_THREAD)) +# define ELPP_USE_STD_THREADING 0 +#else +# if ((ELPP_COMPILER_CLANG && defined(ELPP_CLANG_SUPPORTS_THREAD)) || \ + (!ELPP_COMPILER_CLANG && defined(ELPP_CXX11)) || \ + defined(ELPP_FORCE_USE_STD_THREAD)) +# define ELPP_USE_STD_THREADING 1 +# else +# define ELPP_USE_STD_THREADING 0 +# endif +#endif +#undef ELPP_FINAL +#if ELPP_COMPILER_INTEL || (ELPP_GCC_VERSION < 40702) +# define ELPP_FINAL +#else +# define ELPP_FINAL final +#endif // ELPP_COMPILER_INTEL || (ELPP_GCC_VERSION < 40702) +#if defined(ELPP_EXPERIMENTAL_ASYNC) +# define ELPP_ASYNC_LOGGING 1 +#else +# define ELPP_ASYNC_LOGGING 0 +#endif // defined(ELPP_EXPERIMENTAL_ASYNC) +#if defined(ELPP_THREAD_SAFE) || ELPP_ASYNC_LOGGING +# define ELPP_THREADING_ENABLED 1 +#else +# define ELPP_THREADING_ENABLED 0 +#endif // defined(ELPP_THREAD_SAFE) || ELPP_ASYNC_LOGGING +// Function macro ELPP_FUNC +#undef ELPP_FUNC +#if ELPP_COMPILER_MSVC // Visual C++ +# define ELPP_FUNC __FUNCSIG__ +#elif ELPP_COMPILER_GCC // GCC +# define ELPP_FUNC __PRETTY_FUNCTION__ +#elif ELPP_COMPILER_INTEL // Intel C++ +# define ELPP_FUNC __PRETTY_FUNCTION__ +#elif ELPP_COMPILER_CLANG // Clang++ +# define ELPP_FUNC __PRETTY_FUNCTION__ +#else +# if defined(__func__) +# define ELPP_FUNC __func__ +# else +# define ELPP_FUNC "" +# endif // defined(__func__) +#endif // defined(_MSC_VER) +#undef ELPP_VARIADIC_TEMPLATES_SUPPORTED +// Keep following line commented until features are fixed +#define ELPP_VARIADIC_TEMPLATES_SUPPORTED \ +(ELPP_COMPILER_GCC || ELPP_COMPILER_CLANG || ELPP_COMPILER_INTEL || (ELPP_COMPILER_MSVC && _MSC_VER >= 1800)) +// Logging Enable/Disable macros +#if defined(ELPP_DISABLE_LOGS) +#define ELPP_LOGGING_ENABLED 0 +#else +#define ELPP_LOGGING_ENABLED 1 +#endif +#if (!defined(ELPP_DISABLE_DEBUG_LOGS) && (ELPP_LOGGING_ENABLED)) +# define ELPP_DEBUG_LOG 1 +#else +# define ELPP_DEBUG_LOG 0 +#endif // (!defined(ELPP_DISABLE_DEBUG_LOGS) && (ELPP_LOGGING_ENABLED)) +#if (!defined(ELPP_DISABLE_INFO_LOGS) && (ELPP_LOGGING_ENABLED)) +# define ELPP_INFO_LOG 1 +#else +# define ELPP_INFO_LOG 0 +#endif // (!defined(ELPP_DISABLE_INFO_LOGS) && (ELPP_LOGGING_ENABLED)) +#if (!defined(ELPP_DISABLE_WARNING_LOGS) && (ELPP_LOGGING_ENABLED)) +# define ELPP_WARNING_LOG 1 +#else +# define ELPP_WARNING_LOG 0 +#endif // (!defined(ELPP_DISABLE_WARNING_LOGS) && (ELPP_LOGGING_ENABLED)) +#if (!defined(ELPP_DISABLE_ERROR_LOGS) && (ELPP_LOGGING_ENABLED)) +# define ELPP_ERROR_LOG 1 +#else +# define ELPP_ERROR_LOG 0 +#endif // (!defined(ELPP_DISABLE_ERROR_LOGS) && (ELPP_LOGGING_ENABLED)) +#if (!defined(ELPP_DISABLE_FATAL_LOGS) && (ELPP_LOGGING_ENABLED)) +# define ELPP_FATAL_LOG 1 +#else +# define ELPP_FATAL_LOG 0 +#endif // (!defined(ELPP_DISABLE_FATAL_LOGS) && (ELPP_LOGGING_ENABLED)) +#if (!defined(ELPP_DISABLE_TRACE_LOGS) && (ELPP_LOGGING_ENABLED)) +# define ELPP_TRACE_LOG 1 +#else +# define ELPP_TRACE_LOG 0 +#endif // (!defined(ELPP_DISABLE_TRACE_LOGS) && (ELPP_LOGGING_ENABLED)) +#if (!defined(ELPP_DISABLE_VERBOSE_LOGS) && (ELPP_LOGGING_ENABLED)) +# define ELPP_VERBOSE_LOG 1 +#else +# define ELPP_VERBOSE_LOG 0 +#endif // (!defined(ELPP_DISABLE_VERBOSE_LOGS) && (ELPP_LOGGING_ENABLED)) +#if (!(ELPP_CXX0X || ELPP_CXX11)) +# error "C++0x (or higher) support not detected! (Is `-std=c++11' missing?)" +#endif // (!(ELPP_CXX0X || ELPP_CXX11)) +// Headers +#if defined(ELPP_SYSLOG) +# include +#endif // defined(ELPP_SYSLOG) +#include +#include +#include +#include +#include +#include +#include +#include +#if defined(ELPP_UNICODE) +# include +# if ELPP_OS_WINDOWS +# include +# endif // ELPP_OS_WINDOWS +#endif // defined(ELPP_UNICODE) +#ifdef HAVE_EXECINFO +# include +# include +#endif // ENABLE_EXECINFO +#if ELPP_OS_ANDROID +# include +#endif // ELPP_OS_ANDROID +#if ELPP_OS_UNIX +# include +# include +#elif ELPP_OS_WINDOWS +# include +# include +# if defined(WIN32_LEAN_AND_MEAN) +# if defined(ELPP_WINSOCK2) +# include +# else +# include +# endif // defined(ELPP_WINSOCK2) +# endif // defined(WIN32_LEAN_AND_MEAN) +#endif // ELPP_OS_UNIX +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#if ELPP_THREADING_ENABLED +# if ELPP_USE_STD_THREADING +# include +# include +# else +# if ELPP_OS_UNIX +# include +# endif // ELPP_OS_UNIX +# endif // ELPP_USE_STD_THREADING +#endif // ELPP_THREADING_ENABLED +#if ELPP_ASYNC_LOGGING +# if defined(ELPP_NO_SLEEP_FOR) +# include +# endif // defined(ELPP_NO_SLEEP_FOR) +# include +# include +# include +#endif // ELPP_ASYNC_LOGGING +#if defined(ELPP_STL_LOGGING) +// For logging STL based templates +# include +# include +# include +# include +# include +# include +# if defined(ELPP_LOG_STD_ARRAY) +# include +# endif // defined(ELPP_LOG_STD_ARRAY) +# if defined(ELPP_LOG_UNORDERED_SET) +# include +# endif // defined(ELPP_UNORDERED_SET) +#endif // defined(ELPP_STL_LOGGING) +#if defined(ELPP_QT_LOGGING) +// For logging Qt based classes & templates +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +#endif // defined(ELPP_QT_LOGGING) +#if defined(ELPP_BOOST_LOGGING) +// For logging boost based classes & templates +# include +# include +# include +# include +# include +# include +# include +# include +#endif // defined(ELPP_BOOST_LOGGING) +#if defined(ELPP_WXWIDGETS_LOGGING) +// For logging wxWidgets based classes & templates +# include +#endif // defined(ELPP_WXWIDGETS_LOGGING) +#if defined(ELPP_UTC_DATETIME) +# define elpptime_r gmtime_r +# define elpptime_s gmtime_s +# define elpptime gmtime +#else +# define elpptime_r localtime_r +# define elpptime_s localtime_s +# define elpptime localtime +#endif // defined(ELPP_UTC_DATETIME) +// Forward declarations +namespace el { +class Logger; +class LogMessage; +class PerformanceTrackingData; +class Loggers; +class Helpers; +template class Callback; +class LogDispatchCallback; +class PerformanceTrackingCallback; +class LoggerRegistrationCallback; +class LogDispatchData; +namespace base { +class Storage; +class RegisteredLoggers; +class PerformanceTracker; +class MessageBuilder; +class Writer; +class PErrorWriter; +class LogDispatcher; +class DefaultLogBuilder; +class DefaultLogDispatchCallback; +#if ELPP_ASYNC_LOGGING +class AsyncLogDispatchCallback; +class AsyncDispatchWorker; +#endif // ELPP_ASYNC_LOGGING +class DefaultPerformanceTrackingCallback; +} // namespace base +} // namespace el +/// @brief Easylogging++ entry namespace +namespace el { +/// @brief Namespace containing base/internal functionality used by Easylogging++ +namespace base { +/// @brief Data types used by Easylogging++ +namespace type { +#undef ELPP_LITERAL +#undef ELPP_STRLEN +#undef ELPP_COUT +#if defined(ELPP_UNICODE) +# define ELPP_LITERAL(txt) L##txt +# define ELPP_STRLEN wcslen +# if defined ELPP_CUSTOM_COUT +# define ELPP_COUT ELPP_CUSTOM_COUT +# else +# define ELPP_COUT std::wcout +# endif // defined ELPP_CUSTOM_COUT +typedef wchar_t char_t; +typedef std::wstring string_t; +typedef std::wstringstream stringstream_t; +typedef std::wfstream fstream_t; +typedef std::wostream ostream_t; +#else +# define ELPP_LITERAL(txt) txt +# define ELPP_STRLEN strlen +# if defined ELPP_CUSTOM_COUT +# define ELPP_COUT ELPP_CUSTOM_COUT +# else +# define ELPP_COUT std::cout +# endif // defined ELPP_CUSTOM_COUT +typedef char char_t; +typedef std::string string_t; +typedef std::stringstream stringstream_t; +typedef std::fstream fstream_t; +typedef std::ostream ostream_t; +#endif // defined(ELPP_UNICODE) +#if defined(ELPP_CUSTOM_COUT_LINE) +# define ELPP_COUT_LINE(logLine) ELPP_CUSTOM_COUT_LINE(logLine) +#else +# define ELPP_COUT_LINE(logLine) logLine << std::flush +#endif // defined(ELPP_CUSTOM_COUT_LINE) +typedef unsigned int EnumType; +typedef unsigned short VerboseLevel; +typedef unsigned long int LineNumber; +typedef std::shared_ptr StoragePointer; +typedef std::shared_ptr LogDispatchCallbackPtr; +typedef std::shared_ptr PerformanceTrackingCallbackPtr; +typedef std::shared_ptr LoggerRegistrationCallbackPtr; +typedef std::unique_ptr PerformanceTrackerPtr; +} // namespace type +/// @brief Internal helper class that prevent copy constructor for class +/// +/// @detail When using this class simply inherit it privately +class NoCopy { + protected: + NoCopy(void) {} + private: + NoCopy(const NoCopy&); + NoCopy& operator=(const NoCopy&); +}; +/// @brief Internal helper class that makes all default constructors private. +/// +/// @detail This prevents initializing class making it static unless an explicit constructor is declared. +/// When using this class simply inherit it privately +class StaticClass { + private: + StaticClass(void); + StaticClass(const StaticClass&); + StaticClass& operator=(const StaticClass&); +}; +} // namespace base +/// @brief Represents enumeration for severity level used to determine level of logging +/// +/// @detail With Easylogging++, developers may disable or enable any level regardless of +/// what the severity is. Or they can choose to log using hierarchical logging flag +enum class Level : base::type::EnumType { + /// @brief Generic level that represents all the levels. Useful when setting global configuration for all levels + Global = 1, + /// @brief Information that can be useful to back-trace certain events - mostly useful than debug logs. + Trace = 2, + /// @brief Informational events most useful for developers to debug application + Debug = 4, + /// @brief Severe error information that will presumably abort application + Fatal = 8, + /// @brief Information representing errors in application but application will keep running + Error = 16, + /// @brief Useful when application has potentially harmful situations + Warning = 32, + /// @brief Information that can be highly useful and vary with verbose logging level. + Verbose = 64, + /// @brief Mainly useful to represent current progress of application + Info = 128, + /// @brief Represents unknown level + Unknown = 1010 +}; +} // namespace el +namespace std { +template<> struct hash { + public: + std::size_t operator()(const el::Level& l) const { + return hash {}(static_cast(l)); + } +}; +} +namespace el { +/// @brief Static class that contains helper functions for el::Level +class LevelHelper : base::StaticClass { + public: + /// @brief Represents minimum valid level. Useful when iterating through enum. + static const base::type::EnumType kMinValid = static_cast(Level::Trace); + /// @brief Represents maximum valid level. This is used internally and you should not need it. + static const base::type::EnumType kMaxValid = static_cast(Level::Info); + /// @brief Casts level to int, useful for iterating through enum. + static base::type::EnumType castToInt(Level level) { + return static_cast(level); + } + /// @brief Casts int(ushort) to level, useful for iterating through enum. + static Level castFromInt(base::type::EnumType l) { + return static_cast(l); + } + /// @brief Converts level to associated const char* + /// @return Upper case string based level. + static const char* convertToString(Level level); + /// @brief Converts from levelStr to Level + /// @param levelStr Upper case string based level. + /// Lower case is also valid but providing upper case is recommended. + static Level convertFromString(const char* levelStr); + /// @brief Applies specified function to each level starting from startIndex + /// @param startIndex initial value to start the iteration from. This is passed as pointer and + /// is left-shifted so this can be used inside function (fn) to represent current level. + /// @param fn function to apply with each level. This bool represent whether or not to stop iterating through levels. + static void forEachLevel(base::type::EnumType* startIndex, const std::function& fn); +}; +/// @brief Represents enumeration of ConfigurationType used to configure or access certain aspect +/// of logging +enum class ConfigurationType : base::type::EnumType { + /// @brief Determines whether or not corresponding level and logger of logging is enabled + /// You may disable all logs by using el::Level::Global + Enabled = 1, + /// @brief Whether or not to write corresponding log to log file + ToFile = 2, + /// @brief Whether or not to write corresponding level and logger log to standard output. + /// By standard output meaning termnal, command prompt etc + ToStandardOutput = 4, + /// @brief Determines format of logging corresponding level and logger. + Format = 8, + /// @brief Determines log file (full path) to write logs to for corresponding level and logger + Filename = 16, + /// @brief Specifies precision of the subsecond part. It should be within range (1-6). + SubsecondPrecision = 32, + /// @brief Alias of SubsecondPrecision (for backward compatibility) + MillisecondsWidth = SubsecondPrecision, + /// @brief Determines whether or not performance tracking is enabled. + /// + /// @detail This does not depend on logger or level. Performance tracking always uses 'performance' logger + PerformanceTracking = 64, + /// @brief Specifies log file max size. + /// + /// @detail If file size of corresponding log file (for corresponding level) is >= specified size, log file will + /// be truncated and re-initiated. + MaxLogFileSize = 128, + /// @brief Specifies number of log entries to hold until we flush pending log data + LogFlushThreshold = 256, + /// @brief Represents unknown configuration + Unknown = 1010 +}; +/// @brief Static class that contains helper functions for el::ConfigurationType +class ConfigurationTypeHelper : base::StaticClass { + public: + /// @brief Represents minimum valid configuration type. Useful when iterating through enum. + static const base::type::EnumType kMinValid = static_cast(ConfigurationType::Enabled); + /// @brief Represents maximum valid configuration type. This is used internally and you should not need it. + static const base::type::EnumType kMaxValid = static_cast(ConfigurationType::MaxLogFileSize); + /// @brief Casts configuration type to int, useful for iterating through enum. + static base::type::EnumType castToInt(ConfigurationType configurationType) { + return static_cast(configurationType); + } + /// @brief Casts int(ushort) to configuration type, useful for iterating through enum. + static ConfigurationType castFromInt(base::type::EnumType c) { + return static_cast(c); + } + /// @brief Converts configuration type to associated const char* + /// @returns Upper case string based configuration type. + static const char* convertToString(ConfigurationType configurationType); + /// @brief Converts from configStr to ConfigurationType + /// @param configStr Upper case string based configuration type. + /// Lower case is also valid but providing upper case is recommended. + static ConfigurationType convertFromString(const char* configStr); + /// @brief Applies specified function to each configuration type starting from startIndex + /// @param startIndex initial value to start the iteration from. This is passed by pointer and is left-shifted + /// so this can be used inside function (fn) to represent current configuration type. + /// @param fn function to apply with each configuration type. + /// This bool represent whether or not to stop iterating through configurations. + static inline void forEachConfigType(base::type::EnumType* startIndex, const std::function& fn); +}; +/// @brief Flags used while writing logs. This flags are set by user +enum class LoggingFlag : base::type::EnumType { + /// @brief Makes sure we have new line for each container log entry + NewLineForContainer = 1, + /// @brief Makes sure if -vmodule is used and does not specifies a module, then verbose + /// logging is allowed via that module. + AllowVerboseIfModuleNotSpecified = 2, + /// @brief When handling crashes by default, detailed crash reason will be logged as well + LogDetailedCrashReason = 4, + /// @brief Allows to disable application abortion when logged using FATAL level + DisableApplicationAbortOnFatalLog = 8, + /// @brief Flushes log with every log-entry (performance sensitive) - Disabled by default + ImmediateFlush = 16, + /// @brief Enables strict file rolling + StrictLogFileSizeCheck = 32, + /// @brief Make terminal output colorful for supported terminals + ColoredTerminalOutput = 64, + /// @brief Supports use of multiple logging in same macro, e.g, CLOG(INFO, "default", "network") + MultiLoggerSupport = 128, + /// @brief Disables comparing performance tracker's checkpoints + DisablePerformanceTrackingCheckpointComparison = 256, + /// @brief Disable VModules + DisableVModules = 512, + /// @brief Disable VModules extensions + DisableVModulesExtensions = 1024, + /// @brief Enables hierarchical logging + HierarchicalLogging = 2048, + /// @brief Creates logger automatically when not available + CreateLoggerAutomatically = 4096, + /// @brief Adds spaces b/w logs that separated by left-shift operator + AutoSpacing = 8192, + /// @brief Preserves time format and does not convert it to sec, hour etc (performance tracking only) + FixedTimeFormat = 16384, + // @brief Ignore SIGINT or crash + IgnoreSigInt = 32768, +}; +namespace base { +/// @brief Namespace containing constants used internally. +namespace consts { +static const char kFormatSpecifierCharValue = 'v'; +static const char kFormatSpecifierChar = '%'; +static const unsigned int kMaxLogPerCounter = 100000; +static const unsigned int kMaxLogPerContainer = 100; +static const unsigned int kDefaultSubsecondPrecision = 3; + +#ifdef ELPP_DEFAULT_LOGGER +static const char* kDefaultLoggerId = ELPP_DEFAULT_LOGGER; +#else +static const char* kDefaultLoggerId = "default"; +#endif + +#if defined(ELPP_FEATURE_ALL) || defined(ELPP_FEATURE_PERFORMANCE_TRACKING) +#ifdef ELPP_DEFAULT_PERFORMANCE_LOGGER +static const char* kPerformanceLoggerId = ELPP_DEFAULT_PERFORMANCE_LOGGER; +#else +static const char* kPerformanceLoggerId = "performance"; +#endif // ELPP_DEFAULT_PERFORMANCE_LOGGER +#endif + +#if defined(ELPP_SYSLOG) +static const char* kSysLogLoggerId = "syslog"; +#endif // defined(ELPP_SYSLOG) + +#if ELPP_OS_WINDOWS +static const char* kFilePathSeparator = "\\"; +#else +static const char* kFilePathSeparator = "/"; +#endif // ELPP_OS_WINDOWS + +static const std::size_t kSourceFilenameMaxLength = 100; +static const std::size_t kSourceLineMaxLength = 10; +static const Level kPerformanceTrackerDefaultLevel = Level::Info; +const struct { + double value; + const base::type::char_t* unit; +} kTimeFormats[] = { + { 1000.0f, ELPP_LITERAL("us") }, + { 1000.0f, ELPP_LITERAL("ms") }, + { 60.0f, ELPP_LITERAL("seconds") }, + { 60.0f, ELPP_LITERAL("minutes") }, + { 24.0f, ELPP_LITERAL("hours") }, + { 7.0f, ELPP_LITERAL("days") } +}; +static const int kTimeFormatsCount = sizeof(kTimeFormats) / sizeof(kTimeFormats[0]); +const struct { + int numb; + const char* name; + const char* brief; + const char* detail; +} kCrashSignals[] = { + // NOTE: Do not re-order, if you do please check CrashHandler(bool) constructor and CrashHandler::setHandler(..) + { + SIGABRT, "SIGABRT", "Abnormal termination", + "Program was abnormally terminated." + }, + { + SIGFPE, "SIGFPE", "Erroneous arithmetic operation", + "Arithmetic operation issue such as division by zero or operation resulting in overflow." + }, + { + SIGILL, "SIGILL", "Illegal instruction", + "Generally due to a corruption in the code or to an attempt to execute data." + }, + { + SIGSEGV, "SIGSEGV", "Invalid access to memory", + "Program is trying to read an invalid (unallocated, deleted or corrupted) or inaccessible memory." + }, + { + SIGINT, "SIGINT", "Interactive attention signal", + "Interruption generated (generally) by user or operating system." + }, +}; +static const int kCrashSignalsCount = sizeof(kCrashSignals) / sizeof(kCrashSignals[0]); +} // namespace consts +} // namespace base +typedef std::function PreRollOutCallback; +namespace base { +static inline void defaultPreRollOutCallback(const char*, std::size_t) {} +/// @brief Enum to represent timestamp unit +enum class TimestampUnit : base::type::EnumType { + Microsecond = 0, Millisecond = 1, Second = 2, Minute = 3, Hour = 4, Day = 5 +}; +/// @brief Format flags used to determine specifiers that are active for performance improvements. +enum class FormatFlags : base::type::EnumType { + DateTime = 1 << 1, + LoggerId = 1 << 2, + File = 1 << 3, + Line = 1 << 4, + Location = 1 << 5, + Function = 1 << 6, + User = 1 << 7, + Host = 1 << 8, + LogMessage = 1 << 9, + VerboseLevel = 1 << 10, + AppName = 1 << 11, + ThreadId = 1 << 12, + Level = 1 << 13, + FileBase = 1 << 14, + LevelShort = 1 << 15 +}; +/// @brief A subsecond precision class containing actual width and offset of the subsecond part +class SubsecondPrecision { + public: + SubsecondPrecision(void) { + init(base::consts::kDefaultSubsecondPrecision); + } + explicit SubsecondPrecision(int width) { + init(width); + } + bool operator==(const SubsecondPrecision& ssPrec) { + return m_width == ssPrec.m_width && m_offset == ssPrec.m_offset; + } + int m_width; + unsigned int m_offset; + private: + void init(int width); +}; +/// @brief Type alias of SubsecondPrecision +typedef SubsecondPrecision MillisecondsWidth; +/// @brief Namespace containing utility functions/static classes used internally +namespace utils { +/// @brief Deletes memory safely and points to null +template +static +typename std::enable_if::value, void>::type +safeDelete(T*& pointer) { + if (pointer == nullptr) + return; + delete pointer; + pointer = nullptr; +} +/// @brief Bitwise operations for C++11 strong enum class. This casts e into Flag_T and returns value after bitwise operation +/// Use these function as
flag = bitwise::Or(MyEnum::val1, flag);
+namespace bitwise { +template +static inline base::type::EnumType And(Enum e, base::type::EnumType flag) { + return static_cast(flag) & static_cast(e); +} +template +static inline base::type::EnumType Not(Enum e, base::type::EnumType flag) { + return static_cast(flag) & ~(static_cast(e)); +} +template +static inline base::type::EnumType Or(Enum e, base::type::EnumType flag) { + return static_cast(flag) | static_cast(e); +} +} // namespace bitwise +template +static inline void addFlag(Enum e, base::type::EnumType* flag) { + *flag = base::utils::bitwise::Or(e, *flag); +} +template +static inline void removeFlag(Enum e, base::type::EnumType* flag) { + *flag = base::utils::bitwise::Not(e, *flag); +} +template +static inline bool hasFlag(Enum e, base::type::EnumType flag) { + return base::utils::bitwise::And(e, flag) > 0x0; +} +} // namespace utils +namespace threading { +#if ELPP_THREADING_ENABLED +# if !ELPP_USE_STD_THREADING +namespace internal { +/// @brief A mutex wrapper for compiler that dont yet support std::recursive_mutex +class Mutex : base::NoCopy { + public: + Mutex(void) { +# if ELPP_OS_UNIX + pthread_mutexattr_t attr; + pthread_mutexattr_init(&attr); + pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE); + pthread_mutex_init(&m_underlyingMutex, &attr); + pthread_mutexattr_destroy(&attr); +# elif ELPP_OS_WINDOWS + InitializeCriticalSection(&m_underlyingMutex); +# endif // ELPP_OS_UNIX + } + + virtual ~Mutex(void) { +# if ELPP_OS_UNIX + pthread_mutex_destroy(&m_underlyingMutex); +# elif ELPP_OS_WINDOWS + DeleteCriticalSection(&m_underlyingMutex); +# endif // ELPP_OS_UNIX + } + + inline void lock(void) { +# if ELPP_OS_UNIX + pthread_mutex_lock(&m_underlyingMutex); +# elif ELPP_OS_WINDOWS + EnterCriticalSection(&m_underlyingMutex); +# endif // ELPP_OS_UNIX + } + + inline bool try_lock(void) { +# if ELPP_OS_UNIX + return (pthread_mutex_trylock(&m_underlyingMutex) == 0); +# elif ELPP_OS_WINDOWS + return TryEnterCriticalSection(&m_underlyingMutex); +# endif // ELPP_OS_UNIX + } + + inline void unlock(void) { +# if ELPP_OS_UNIX + pthread_mutex_unlock(&m_underlyingMutex); +# elif ELPP_OS_WINDOWS + LeaveCriticalSection(&m_underlyingMutex); +# endif // ELPP_OS_UNIX + } + + private: +# if ELPP_OS_UNIX + pthread_mutex_t m_underlyingMutex; +# elif ELPP_OS_WINDOWS + CRITICAL_SECTION m_underlyingMutex; +# endif // ELPP_OS_UNIX +}; +/// @brief Scoped lock for compiler that dont yet support std::lock_guard +template +class ScopedLock : base::NoCopy { + public: + explicit ScopedLock(M& mutex) { + m_mutex = &mutex; + m_mutex->lock(); + } + + virtual ~ScopedLock(void) { + m_mutex->unlock(); + } + private: + M* m_mutex; + ScopedLock(void); +}; +} // namespace internal +typedef base::threading::internal::Mutex Mutex; +typedef base::threading::internal::ScopedLock ScopedLock; +# else +typedef std::recursive_mutex Mutex; +typedef std::lock_guard ScopedLock; +# endif // !ELPP_USE_STD_THREADING +#else +namespace internal { +/// @brief Mutex wrapper used when multi-threading is disabled. +class NoMutex : base::NoCopy { + public: + NoMutex(void) {} + inline void lock(void) {} + inline bool try_lock(void) { + return true; + } + inline void unlock(void) {} +}; +/// @brief Lock guard wrapper used when multi-threading is disabled. +template +class NoScopedLock : base::NoCopy { + public: + explicit NoScopedLock(Mutex&) { + } + virtual ~NoScopedLock(void) { + } + private: + NoScopedLock(void); +}; +} // namespace internal +typedef base::threading::internal::NoMutex Mutex; +typedef base::threading::internal::NoScopedLock ScopedLock; +#endif // ELPP_THREADING_ENABLED +/// @brief Base of thread safe class, this class is inheritable-only +class ThreadSafe { + public: + virtual inline void acquireLock(void) ELPP_FINAL { m_mutex.lock(); } + virtual inline void releaseLock(void) ELPP_FINAL { m_mutex.unlock(); } + virtual inline base::threading::Mutex& lock(void) ELPP_FINAL { return m_mutex; } + protected: + ThreadSafe(void) {} + virtual ~ThreadSafe(void) {} + private: + base::threading::Mutex m_mutex; +}; + +#if ELPP_THREADING_ENABLED +# if !ELPP_USE_STD_THREADING +/// @brief Gets ID of currently running threading in windows systems. On unix, nothing is returned. +static std::string getCurrentThreadId(void) { + std::stringstream ss; +# if (ELPP_OS_WINDOWS) + ss << GetCurrentThreadId(); +# endif // (ELPP_OS_WINDOWS) + return ss.str(); +} +# else +/// @brief Gets ID of currently running threading using std::this_thread::get_id() +static std::string getCurrentThreadId(void) { + std::stringstream ss; + ss << std::this_thread::get_id(); + return ss.str(); +} +# endif // !ELPP_USE_STD_THREADING +#else +static inline std::string getCurrentThreadId(void) { + return std::string(); +} +#endif // ELPP_THREADING_ENABLED +} // namespace threading +namespace utils { +class File : base::StaticClass { + public: + /// @brief Creates new out file stream for specified filename. + /// @return Pointer to newly created fstream or nullptr + static base::type::fstream_t* newFileStream(const std::string& filename); + + /// @brief Gets size of file provided in stream + static std::size_t getSizeOfFile(base::type::fstream_t* fs); + + /// @brief Determines whether or not provided path exist in current file system + static bool pathExists(const char* path, bool considerFile = false); + + /// @brief Creates specified path on file system + /// @param path Path to create. + static bool createPath(const std::string& path); + /// @brief Extracts path of filename with leading slash + static std::string extractPathFromFilename(const std::string& fullPath, + const char* separator = base::consts::kFilePathSeparator); + /// @brief builds stripped filename and puts it in buff + static void buildStrippedFilename(const char* filename, char buff[], + std::size_t limit = base::consts::kSourceFilenameMaxLength); + /// @brief builds base filename and puts it in buff + static void buildBaseFilename(const std::string& fullPath, char buff[], + std::size_t limit = base::consts::kSourceFilenameMaxLength, + const char* separator = base::consts::kFilePathSeparator); +}; +/// @brief String utilities helper class used internally. You should not use it. +class Str : base::StaticClass { + public: + /// @brief Checks if character is digit. Dont use libc implementation of it to prevent locale issues. + static inline bool isDigit(char c) { + return c >= '0' && c <= '9'; + } + + /// @brief Matches wildcards, '*' and '?' only supported. + static bool wildCardMatch(const char* str, const char* pattern); + + static std::string& ltrim(std::string& str); + static std::string& rtrim(std::string& str); + static std::string& trim(std::string& str); + + /// @brief Determines whether or not str starts with specified string + /// @param str String to check + /// @param start String to check against + /// @return Returns true if starts with specified string, false otherwise + static bool startsWith(const std::string& str, const std::string& start); + + /// @brief Determines whether or not str ends with specified string + /// @param str String to check + /// @param end String to check against + /// @return Returns true if ends with specified string, false otherwise + static bool endsWith(const std::string& str, const std::string& end); + + /// @brief Replaces all instances of replaceWhat with 'replaceWith'. Original variable is changed for performance. + /// @param [in,out] str String to replace from + /// @param replaceWhat Character to replace + /// @param replaceWith Character to replace with + /// @return Modified version of str + static std::string& replaceAll(std::string& str, char replaceWhat, char replaceWith); + + /// @brief Replaces all instances of 'replaceWhat' with 'replaceWith'. (String version) Replaces in place + /// @param str String to replace from + /// @param replaceWhat Character to replace + /// @param replaceWith Character to replace with + /// @return Modified (original) str + static std::string& replaceAll(std::string& str, const std::string& replaceWhat, + const std::string& replaceWith); + + static void replaceFirstWithEscape(base::type::string_t& str, const base::type::string_t& replaceWhat, + const base::type::string_t& replaceWith); +#if defined(ELPP_UNICODE) + static void replaceFirstWithEscape(base::type::string_t& str, const base::type::string_t& replaceWhat, + const std::string& replaceWith); +#endif // defined(ELPP_UNICODE) + /// @brief Converts string to uppercase + /// @param str String to convert + /// @return Uppercase string + static std::string& toUpper(std::string& str); + + /// @brief Compares cstring equality - uses strcmp + static bool cStringEq(const char* s1, const char* s2); + + /// @brief Compares cstring equality (case-insensitive) - uses toupper(char) + /// Dont use strcasecmp because of CRT (VC++) + static bool cStringCaseEq(const char* s1, const char* s2); + + /// @brief Returns true if c exist in str + static bool contains(const char* str, char c); + + static char* convertAndAddToBuff(std::size_t n, int len, char* buf, const char* bufLim, bool zeroPadded = true); + static char* addToBuff(const char* str, char* buf, const char* bufLim); + static char* clearBuff(char buff[], std::size_t lim); + + /// @brief Converts wchar* to char* + /// NOTE: Need to free return value after use! + static char* wcharPtrToCharPtr(const wchar_t* line); +}; +/// @brief Operating System helper static class used internally. You should not use it. +class OS : base::StaticClass { + public: +#if ELPP_OS_WINDOWS + /// @brief Gets environment variables for Windows based OS. + /// We are not using getenv(const char*) because of CRT deprecation + /// @param varname Variable name to get environment variable value for + /// @return If variable exist the value of it otherwise nullptr + static const char* getWindowsEnvironmentVariable(const char* varname); +#endif // ELPP_OS_WINDOWS +#if ELPP_OS_ANDROID + /// @brief Reads android property value + static std::string getProperty(const char* prop); + + /// @brief Reads android device name + static std::string getDeviceName(void); +#endif // ELPP_OS_ANDROID + + /// @brief Runs command on terminal and returns the output. + /// + /// @detail This is applicable only on unix based systems, for all other OS, an empty string is returned. + /// @param command Bash command + /// @return Result of bash output or empty string if no result found. + static const std::string getBashOutput(const char* command); + + /// @brief Gets environment variable. This is cross-platform and CRT safe (for VC++) + /// @param variableName Environment variable name + /// @param defaultVal If no environment variable or value found the value to return by default + /// @param alternativeBashCommand If environment variable not found what would be alternative bash command + /// in order to look for value user is looking for. E.g, for 'user' alternative command will 'whoami' + static std::string getEnvironmentVariable(const char* variableName, const char* defaultVal, + const char* alternativeBashCommand = nullptr); + /// @brief Gets current username. + static std::string currentUser(void); + + /// @brief Gets current host name or computer name. + /// + /// @detail For android systems this is device name with its manufacturer and model separated by hyphen + static std::string currentHost(void); + /// @brief Whether or not terminal supports colors + static bool termSupportsColor(void); +}; +/// @brief Contains utilities for cross-platform date/time. This class make use of el::base::utils::Str +class DateTime : base::StaticClass { + public: + /// @brief Cross platform gettimeofday for Windows and unix platform. This can be used to determine current microsecond. + /// + /// @detail For unix system it uses gettimeofday(timeval*, timezone*) and for Windows, a separate implementation is provided + /// @param [in,out] tv Pointer that gets updated + static void gettimeofday(struct timeval* tv); + + /// @brief Gets current date and time with a subsecond part. + /// @param format User provided date/time format + /// @param ssPrec A pointer to base::SubsecondPrecision from configuration (non-null) + /// @returns string based date time in specified format. + static std::string getDateTime(const char* format, const base::SubsecondPrecision* ssPrec); + + /// @brief Converts timeval (struct from ctime) to string using specified format and subsecond precision + static std::string timevalToString(struct timeval tval, const char* format, + const el::base::SubsecondPrecision* ssPrec); + + /// @brief Formats time to get unit accordingly, units like second if > 1000 or minutes if > 60000 etc + static base::type::string_t formatTime(unsigned long long time, base::TimestampUnit timestampUnit); + + /// @brief Gets time difference in milli/micro second depending on timestampUnit + static unsigned long long getTimeDifference(const struct timeval& endTime, const struct timeval& startTime, + base::TimestampUnit timestampUnit); + + + static struct ::tm* buildTimeInfo(struct timeval* currTime, struct ::tm* timeInfo); + private: + static char* parseFormat(char* buf, std::size_t bufSz, const char* format, const struct tm* tInfo, + std::size_t msec, const base::SubsecondPrecision* ssPrec); +}; +/// @brief Command line arguments for application if specified using el::Helpers::setArgs(..) or START_EASYLOGGINGPP(..) +class CommandLineArgs { + public: + CommandLineArgs(void) { + setArgs(0, static_cast(nullptr)); + } + CommandLineArgs(int argc, const char** argv) { + setArgs(argc, argv); + } + CommandLineArgs(int argc, char** argv) { + setArgs(argc, argv); + } + virtual ~CommandLineArgs(void) {} + /// @brief Sets arguments and parses them + inline void setArgs(int argc, const char** argv) { + setArgs(argc, const_cast(argv)); + } + /// @brief Sets arguments and parses them + void setArgs(int argc, char** argv); + /// @brief Returns true if arguments contain paramKey with a value (separated by '=') + bool hasParamWithValue(const char* paramKey) const; + /// @brief Returns value of arguments + /// @see hasParamWithValue(const char*) + const char* getParamValue(const char* paramKey) const; + /// @brief Return true if arguments has a param (not having a value) i,e without '=' + bool hasParam(const char* paramKey) const; + /// @brief Returns true if no params available. This exclude argv[0] + bool empty(void) const; + /// @brief Returns total number of arguments. This exclude argv[0] + std::size_t size(void) const; + friend base::type::ostream_t& operator<<(base::type::ostream_t& os, const CommandLineArgs& c); + + private: + int m_argc; + char** m_argv; + std::unordered_map m_paramsWithValue; + std::vector m_params; +}; +/// @brief Abstract registry (aka repository) that provides basic interface for pointer repository specified by T_Ptr type. +/// +/// @detail Most of the functions are virtual final methods but anything implementing this abstract class should implement +/// unregisterAll() and deepCopy(const AbstractRegistry&) and write registerNew() method according to container +/// and few more methods; get() to find element, unregister() to unregister single entry. +/// Please note that this is thread-unsafe and should also implement thread-safety mechanisms in implementation. +template +class AbstractRegistry : public base::threading::ThreadSafe { + public: + typedef typename Container::iterator iterator; + typedef typename Container::const_iterator const_iterator; + + /// @brief Default constructor + AbstractRegistry(void) {} + + /// @brief Move constructor that is useful for base classes + AbstractRegistry(AbstractRegistry&& sr) { + if (this == &sr) { + return; + } + unregisterAll(); + m_list = std::move(sr.m_list); + } + + bool operator==(const AbstractRegistry& other) { + if (size() != other.size()) { + return false; + } + for (std::size_t i = 0; i < m_list.size(); ++i) { + if (m_list.at(i) != other.m_list.at(i)) { + return false; + } + } + return true; + } + + bool operator!=(const AbstractRegistry& other) { + if (size() != other.size()) { + return true; + } + for (std::size_t i = 0; i < m_list.size(); ++i) { + if (m_list.at(i) != other.m_list.at(i)) { + return true; + } + } + return false; + } + + /// @brief Assignment move operator + AbstractRegistry& operator=(AbstractRegistry&& sr) { + if (this == &sr) { + return *this; + } + unregisterAll(); + m_list = std::move(sr.m_list); + return *this; + } + + virtual ~AbstractRegistry(void) { + } + + /// @return Iterator pointer from start of repository + virtual inline iterator begin(void) ELPP_FINAL { + return m_list.begin(); + } + + /// @return Iterator pointer from end of repository + virtual inline iterator end(void) ELPP_FINAL { + return m_list.end(); + } + + + /// @return Constant iterator pointer from start of repository + virtual inline const_iterator cbegin(void) const ELPP_FINAL { + return m_list.cbegin(); + } + + /// @return End of repository + virtual inline const_iterator cend(void) const ELPP_FINAL { + return m_list.cend(); + } + + /// @return Whether or not repository is empty + virtual inline bool empty(void) const ELPP_FINAL { + return m_list.empty(); + } + + /// @return Size of repository + virtual inline std::size_t size(void) const ELPP_FINAL { + return m_list.size(); + } + + /// @brief Returns underlying container by reference + virtual inline Container& list(void) ELPP_FINAL { + return m_list; + } + + /// @brief Returns underlying container by constant reference. + virtual inline const Container& list(void) const ELPP_FINAL { + return m_list; + } + + /// @brief Unregisters all the pointers from current repository. + virtual void unregisterAll(void) = 0; + + protected: + virtual void deepCopy(const AbstractRegistry&) = 0; + void reinitDeepCopy(const AbstractRegistry& sr) { + unregisterAll(); + deepCopy(sr); + } + + private: + Container m_list; +}; + +/// @brief A pointer registry mechanism to manage memory and provide search functionalities. (non-predicate version) +/// +/// @detail NOTE: This is thread-unsafe implementation (although it contains lock function, it does not use these functions) +/// of AbstractRegistry. Any implementation of this class should be +/// explicitly (by using lock functions) +template +class Registry : public AbstractRegistry> { + public: + typedef typename Registry::iterator iterator; + typedef typename Registry::const_iterator const_iterator; + + Registry(void) {} + + /// @brief Copy constructor that is useful for base classes. Try to avoid this constructor, use move constructor. + Registry(const Registry& sr) : AbstractRegistry>() { + if (this == &sr) { + return; + } + this->reinitDeepCopy(sr); + } + + /// @brief Assignment operator that unregisters all the existing registries and deeply copies each of repo element + /// @see unregisterAll() + /// @see deepCopy(const AbstractRegistry&) + Registry& operator=(const Registry& sr) { + if (this == &sr) { + return *this; + } + this->reinitDeepCopy(sr); + return *this; + } + + virtual ~Registry(void) { + unregisterAll(); + } + + protected: + virtual void unregisterAll(void) ELPP_FINAL { + if (!this->empty()) { + for (auto&& curr : this->list()) { + base::utils::safeDelete(curr.second); + } + this->list().clear(); + } + } + +/// @brief Registers new registry to repository. + virtual void registerNew(const T_Key& uniqKey, T_Ptr* ptr) ELPP_FINAL { + unregister(uniqKey); + this->list().insert(std::make_pair(uniqKey, ptr)); + } + +/// @brief Unregisters single entry mapped to specified unique key + void unregister(const T_Key& uniqKey) { + T_Ptr* existing = get(uniqKey); + if (existing != nullptr) { + this->list().erase(uniqKey); + base::utils::safeDelete(existing); + } + } + +/// @brief Gets pointer from repository. If none found, nullptr is returned. + T_Ptr* get(const T_Key& uniqKey) { + iterator it = this->list().find(uniqKey); + return it == this->list().end() + ? nullptr + : it->second; + } + + private: + virtual void deepCopy(const AbstractRegistry>& sr) ELPP_FINAL { + for (const_iterator it = sr.cbegin(); it != sr.cend(); ++it) { + registerNew(it->first, new T_Ptr(*it->second)); + } + } +}; + +/// @brief A pointer registry mechanism to manage memory and provide search functionalities. (predicate version) +/// +/// @detail NOTE: This is thread-unsafe implementation of AbstractRegistry. Any implementation of this class +/// should be made thread-safe explicitly +template +class RegistryWithPred : public AbstractRegistry> { + public: + typedef typename RegistryWithPred::iterator iterator; + typedef typename RegistryWithPred::const_iterator const_iterator; + + RegistryWithPred(void) { + } + + virtual ~RegistryWithPred(void) { + unregisterAll(); + } + + /// @brief Copy constructor that is useful for base classes. Try to avoid this constructor, use move constructor. + RegistryWithPred(const RegistryWithPred& sr) : AbstractRegistry>() { + if (this == &sr) { + return; + } + this->reinitDeepCopy(sr); + } + + /// @brief Assignment operator that unregisters all the existing registries and deeply copies each of repo element + /// @see unregisterAll() + /// @see deepCopy(const AbstractRegistry&) + RegistryWithPred& operator=(const RegistryWithPred& sr) { + if (this == &sr) { + return *this; + } + this->reinitDeepCopy(sr); + return *this; + } + + friend base::type::ostream_t& operator<<(base::type::ostream_t& os, const RegistryWithPred& sr) { + for (const_iterator it = sr.list().begin(); it != sr.list().end(); ++it) { + os << ELPP_LITERAL(" ") << **it << ELPP_LITERAL("\n"); + } + return os; + } + + protected: + virtual void unregisterAll(void) ELPP_FINAL { + if (!this->empty()) { + for (auto&& curr : this->list()) { + base::utils::safeDelete(curr); + } + this->list().clear(); + } + } + + virtual void unregister(T_Ptr*& ptr) ELPP_FINAL { + if (ptr) { + iterator iter = this->begin(); + for (; iter != this->end(); ++iter) { + if (ptr == *iter) { + break; + } + } + if (iter != this->end() && *iter != nullptr) { + this->list().erase(iter); + base::utils::safeDelete(*iter); + } + } + } + + virtual inline void registerNew(T_Ptr* ptr) ELPP_FINAL { + this->list().push_back(ptr); + } + +/// @brief Gets pointer from repository with specified arguments. Arguments are passed to predicate +/// in order to validate pointer. + template + T_Ptr* get(const T& arg1, const T2 arg2) { + iterator iter = std::find_if(this->list().begin(), this->list().end(), Pred(arg1, arg2)); + if (iter != this->list().end() && *iter != nullptr) { + return *iter; + } + return nullptr; + } + + private: + virtual void deepCopy(const AbstractRegistry>& sr) { + for (const_iterator it = sr.list().begin(); it != sr.list().end(); ++it) { + registerNew(new T_Ptr(**it)); + } + } +}; +class Utils { + public: + template + static bool installCallback(const std::string& id, std::unordered_map* mapT) { + if (mapT->find(id) == mapT->end()) { + mapT->insert(std::make_pair(id, TPtr(new T()))); + return true; + } + return false; + } + + template + static void uninstallCallback(const std::string& id, std::unordered_map* mapT) { + if (mapT->find(id) != mapT->end()) { + mapT->erase(id); + } + } + + template + static T* callback(const std::string& id, std::unordered_map* mapT) { + typename std::unordered_map::iterator iter = mapT->find(id); + if (iter != mapT->end()) { + return static_cast(iter->second.get()); + } + return nullptr; + } +}; +} // namespace utils +} // namespace base +/// @brief Base of Easylogging++ friendly class +/// +/// @detail After inheriting this class publicly, implement pure-virtual function `void log(std::ostream&) const` +class Loggable { + public: + virtual ~Loggable(void) {} + virtual void log(el::base::type::ostream_t&) const = 0; + private: + friend inline el::base::type::ostream_t& operator<<(el::base::type::ostream_t& os, const Loggable& loggable) { + loggable.log(os); + return os; + } +}; +namespace base { +/// @brief Represents log format containing flags and date format. This is used internally to start initial log +class LogFormat : public Loggable { + public: + LogFormat(void); + LogFormat(Level level, const base::type::string_t& format); + LogFormat(const LogFormat& logFormat); + LogFormat(LogFormat&& logFormat); + LogFormat& operator=(const LogFormat& logFormat); + virtual ~LogFormat(void) {} + bool operator==(const LogFormat& other); + + /// @brief Updates format to be used while logging. + /// @param userFormat User provided format + void parseFromFormat(const base::type::string_t& userFormat); + + inline Level level(void) const { + return m_level; + } + + inline const base::type::string_t& userFormat(void) const { + return m_userFormat; + } + + inline const base::type::string_t& format(void) const { + return m_format; + } + + inline const std::string& dateTimeFormat(void) const { + return m_dateTimeFormat; + } + + inline base::type::EnumType flags(void) const { + return m_flags; + } + + inline bool hasFlag(base::FormatFlags flag) const { + return base::utils::hasFlag(flag, m_flags); + } + + virtual void log(el::base::type::ostream_t& os) const { + os << m_format; + } + + protected: + /// @brief Updates date time format if available in currFormat. + /// @param index Index where %datetime, %date or %time was found + /// @param [in,out] currFormat current format that is being used to format + virtual void updateDateFormat(std::size_t index, base::type::string_t& currFormat) ELPP_FINAL; + + /// @brief Updates %level from format. This is so that we dont have to do it at log-writing-time. It uses m_format and m_level + virtual void updateFormatSpec(void) ELPP_FINAL; + + inline void addFlag(base::FormatFlags flag) { + base::utils::addFlag(flag, &m_flags); + } + + private: + Level m_level; + base::type::string_t m_userFormat; + base::type::string_t m_format; + std::string m_dateTimeFormat; + base::type::EnumType m_flags; + std::string m_currentUser; + std::string m_currentHost; + friend class el::Logger; // To resolve loggerId format specifier easily +}; +} // namespace base +/// @brief Resolving function for format specifier +typedef std::function FormatSpecifierValueResolver; +/// @brief User-provided custom format specifier +/// @see el::Helpers::installCustomFormatSpecifier +/// @see FormatSpecifierValueResolver +class CustomFormatSpecifier { + public: + CustomFormatSpecifier(const char* formatSpecifier, const FormatSpecifierValueResolver& resolver) : + m_formatSpecifier(formatSpecifier), m_resolver(resolver) {} + inline const char* formatSpecifier(void) const { + return m_formatSpecifier; + } + inline const FormatSpecifierValueResolver& resolver(void) const { + return m_resolver; + } + inline bool operator==(const char* formatSpecifier) { + return strcmp(m_formatSpecifier, formatSpecifier) == 0; + } + + private: + const char* m_formatSpecifier; + FormatSpecifierValueResolver m_resolver; +}; +/// @brief Represents single configuration that has representing level, configuration type and a string based value. +/// +/// @detail String based value means any value either its boolean, integer or string itself, it will be embedded inside quotes +/// and will be parsed later. +/// +/// Consider some examples below: +/// * el::Configuration confEnabledInfo(el::Level::Info, el::ConfigurationType::Enabled, "true"); +/// * el::Configuration confMaxLogFileSizeInfo(el::Level::Info, el::ConfigurationType::MaxLogFileSize, "2048"); +/// * el::Configuration confFilenameInfo(el::Level::Info, el::ConfigurationType::Filename, "/var/log/my.log"); +class Configuration : public Loggable { + public: + Configuration(const Configuration& c); + Configuration& operator=(const Configuration& c); + + virtual ~Configuration(void) { + } + + /// @brief Full constructor used to sets value of configuration + Configuration(Level level, ConfigurationType configurationType, const std::string& value); + + /// @brief Gets level of current configuration + inline Level level(void) const { + return m_level; + } + + /// @brief Gets configuration type of current configuration + inline ConfigurationType configurationType(void) const { + return m_configurationType; + } + + /// @brief Gets string based configuration value + inline const std::string& value(void) const { + return m_value; + } + + /// @brief Set string based configuration value + /// @param value Value to set. Values have to be std::string; For boolean values use "true", "false", for any integral values + /// use them in quotes. They will be parsed when configuring + inline void setValue(const std::string& value) { + m_value = value; + } + + virtual void log(el::base::type::ostream_t& os) const; + + /// @brief Used to find configuration from configuration (pointers) repository. Avoid using it. + class Predicate { + public: + Predicate(Level level, ConfigurationType configurationType); + + bool operator()(const Configuration* conf) const; + + private: + Level m_level; + ConfigurationType m_configurationType; + }; + + private: + Level m_level; + ConfigurationType m_configurationType; + std::string m_value; +}; + +/// @brief Thread-safe Configuration repository +/// +/// @detail This repository represents configurations for all the levels and configuration type mapped to a value. +class Configurations : public base::utils::RegistryWithPred { + public: + /// @brief Default constructor with empty repository + Configurations(void); + + /// @brief Constructor used to set configurations using configuration file. + /// @param configurationFile Full path to configuration file + /// @param useDefaultsForRemaining Lets you set the remaining configurations to default. + /// @param base If provided, this configuration will be based off existing repository that this argument is pointing to. + /// @see parseFromFile(const std::string&, Configurations* base) + /// @see setRemainingToDefault() + Configurations(const std::string& configurationFile, bool useDefaultsForRemaining = true, + Configurations* base = nullptr); + + virtual ~Configurations(void) { + } + + /// @brief Parses configuration from file. + /// @param configurationFile Full path to configuration file + /// @param base Configurations to base new configuration repository off. This value is used when you want to use + /// existing Configurations to base all the values and then set rest of configuration via configuration file. + /// @return True if successfully parsed, false otherwise. You may define 'ELPP_DEBUG_ASSERT_FAILURE' to make sure you + /// do not proceed without successful parse. + bool parseFromFile(const std::string& configurationFile, Configurations* base = nullptr); + + /// @brief Parse configurations from configuration string. + /// + /// @detail This configuration string has same syntax as configuration file contents. Make sure all the necessary + /// new line characters are provided. + /// @param base Configurations to base new configuration repository off. This value is used when you want to use + /// existing Configurations to base all the values and then set rest of configuration via configuration text. + /// @return True if successfully parsed, false otherwise. You may define 'ELPP_DEBUG_ASSERT_FAILURE' to make sure you + /// do not proceed without successful parse. + bool parseFromText(const std::string& configurationsString, Configurations* base = nullptr); + + /// @brief Sets configuration based-off an existing configurations. + /// @param base Pointer to existing configurations. + void setFromBase(Configurations* base); + + /// @brief Determines whether or not specified configuration type exists in the repository. + /// + /// @detail Returns as soon as first level is found. + /// @param configurationType Type of configuration to check existence for. + bool hasConfiguration(ConfigurationType configurationType); + + /// @brief Determines whether or not specified configuration type exists for specified level + /// @param level Level to check + /// @param configurationType Type of configuration to check existence for. + bool hasConfiguration(Level level, ConfigurationType configurationType); + + /// @brief Sets value of configuration for specified level. + /// + /// @detail Any existing configuration for specified level will be replaced. Also note that configuration types + /// ConfigurationType::SubsecondPrecision and ConfigurationType::PerformanceTracking will be ignored if not set for + /// Level::Global because these configurations are not dependant on level. + /// @param level Level to set configuration for (el::Level). + /// @param configurationType Type of configuration (el::ConfigurationType) + /// @param value A string based value. Regardless of what the data type of configuration is, it will always be string + /// from users' point of view. This is then parsed later to be used internally. + /// @see Configuration::setValue(const std::string& value) + /// @see el::Level + /// @see el::ConfigurationType + void set(Level level, ConfigurationType configurationType, const std::string& value); + + /// @brief Sets single configuration based on other single configuration. + /// @see set(Level level, ConfigurationType configurationType, const std::string& value) + void set(Configuration* conf); + + inline Configuration* get(Level level, ConfigurationType configurationType) { + base::threading::ScopedLock scopedLock(lock()); + return RegistryWithPred::get(level, configurationType); + } + + /// @brief Sets configuration for all levels. + /// @param configurationType Type of configuration + /// @param value String based value + /// @see Configurations::set(Level level, ConfigurationType configurationType, const std::string& value) + inline void setGlobally(ConfigurationType configurationType, const std::string& value) { + setGlobally(configurationType, value, false); + } + + /// @brief Clears repository so that all the configurations are unset + inline void clear(void) { + base::threading::ScopedLock scopedLock(lock()); + unregisterAll(); + } + + /// @brief Gets configuration file used in parsing this configurations. + /// + /// @detail If this repository was set manually or by text this returns empty string. + inline const std::string& configurationFile(void) const { + return m_configurationFile; + } + + /// @brief Sets configurations to "factory based" configurations. + void setToDefault(void); + + /// @brief Lets you set the remaining configurations to default. + /// + /// @detail By remaining, it means that the level/type a configuration does not exist for. + /// This function is useful when you want to minimize chances of failures, e.g, if you have a configuration file that sets + /// configuration for all the configurations except for Enabled or not, we use this so that ENABLED is set to default i.e, + /// true. If you dont do this explicitly (either by calling this function or by using second param in Constructor + /// and try to access a value, an error is thrown + void setRemainingToDefault(void); + + /// @brief Parser used internally to parse configurations from file or text. + /// + /// @detail This class makes use of base::utils::Str. + /// You should not need this unless you are working on some tool for Easylogging++ + class Parser : base::StaticClass { + public: + /// @brief Parses configuration from file. + /// @param configurationFile Full path to configuration file + /// @param sender Sender configurations pointer. Usually 'this' is used from calling class + /// @param base Configurations to base new configuration repository off. This value is used when you want to use + /// existing Configurations to base all the values and then set rest of configuration via configuration file. + /// @return True if successfully parsed, false otherwise. You may define '_STOP_ON_FIRSTELPP_ASSERTION' to make sure you + /// do not proceed without successful parse. + static bool parseFromFile(const std::string& configurationFile, Configurations* sender, + Configurations* base = nullptr); + + /// @brief Parse configurations from configuration string. + /// + /// @detail This configuration string has same syntax as configuration file contents. Make sure all the necessary + /// new line characters are provided. You may define '_STOP_ON_FIRSTELPP_ASSERTION' to make sure you + /// do not proceed without successful parse (This is recommended) + /// @param configurationsString the configuration in plain text format + /// @param sender Sender configurations pointer. Usually 'this' is used from calling class + /// @param base Configurations to base new configuration repository off. This value is used when you want to use + /// existing Configurations to base all the values and then set rest of configuration via configuration text. + /// @return True if successfully parsed, false otherwise. + static bool parseFromText(const std::string& configurationsString, Configurations* sender, + Configurations* base = nullptr); + + private: + friend class el::Loggers; + static void ignoreComments(std::string* line); + static bool isLevel(const std::string& line); + static bool isComment(const std::string& line); + static inline bool isConfig(const std::string& line); + static bool parseLine(std::string* line, std::string* currConfigStr, std::string* currLevelStr, Level* currLevel, + Configurations* conf); + }; + + private: + std::string m_configurationFile; + bool m_isFromFile; + friend class el::Loggers; + + /// @brief Unsafely sets configuration if does not already exist + void unsafeSetIfNotExist(Level level, ConfigurationType configurationType, const std::string& value); + + /// @brief Thread unsafe set + void unsafeSet(Level level, ConfigurationType configurationType, const std::string& value); + + /// @brief Sets configurations for all levels including Level::Global if includeGlobalLevel is true + /// @see Configurations::setGlobally(ConfigurationType configurationType, const std::string& value) + void setGlobally(ConfigurationType configurationType, const std::string& value, bool includeGlobalLevel); + + /// @brief Sets configurations (Unsafely) for all levels including Level::Global if includeGlobalLevel is true + /// @see Configurations::setGlobally(ConfigurationType configurationType, const std::string& value) + void unsafeSetGlobally(ConfigurationType configurationType, const std::string& value, bool includeGlobalLevel); +}; + +namespace base { +typedef std::shared_ptr FileStreamPtr; +typedef std::unordered_map LogStreamsReferenceMap; +typedef std::shared_ptr LogStreamsReferenceMapPtr; +/// @brief Configurations with data types. +/// +/// @detail el::Configurations have string based values. This is whats used internally in order to read correct configurations. +/// This is to perform faster while writing logs using correct configurations. +/// +/// This is thread safe and final class containing non-virtual destructor (means nothing should inherit this class) +class TypedConfigurations : public base::threading::ThreadSafe { + public: + /// @brief Constructor to initialize (construct) the object off el::Configurations + /// @param configurations Configurations pointer/reference to base this typed configurations off. + /// @param logStreamsReference Use ELPP->registeredLoggers()->logStreamsReference() + TypedConfigurations(Configurations* configurations, LogStreamsReferenceMapPtr logStreamsReference); + + TypedConfigurations(const TypedConfigurations& other); + + virtual ~TypedConfigurations(void) { + } + + const Configurations* configurations(void) const { + return m_configurations; + } + + bool enabled(Level level); + bool toFile(Level level); + const std::string& filename(Level level); + bool toStandardOutput(Level level); + const base::LogFormat& logFormat(Level level); + const base::SubsecondPrecision& subsecondPrecision(Level level = Level::Global); + const base::MillisecondsWidth& millisecondsWidth(Level level = Level::Global); + bool performanceTracking(Level level = Level::Global); + base::type::fstream_t* fileStream(Level level); + std::size_t maxLogFileSize(Level level); + std::size_t logFlushThreshold(Level level); + + private: + Configurations* m_configurations; + std::unordered_map m_enabledMap; + std::unordered_map m_toFileMap; + std::unordered_map m_filenameMap; + std::unordered_map m_toStandardOutputMap; + std::unordered_map m_logFormatMap; + std::unordered_map m_subsecondPrecisionMap; + std::unordered_map m_performanceTrackingMap; + std::unordered_map m_fileStreamMap; + std::unordered_map m_maxLogFileSizeMap; + std::unordered_map m_logFlushThresholdMap; + LogStreamsReferenceMapPtr m_logStreamsReference = nullptr; + + friend class el::Helpers; + friend class el::base::MessageBuilder; + friend class el::base::Writer; + friend class el::base::DefaultLogDispatchCallback; + friend class el::base::LogDispatcher; + + template + inline Conf_T getConfigByVal(Level level, const std::unordered_map* confMap, const char* confName) { + base::threading::ScopedLock scopedLock(lock()); + return unsafeGetConfigByVal(level, confMap, confName); // This is not unsafe anymore - mutex locked in scope + } + + template + inline Conf_T& getConfigByRef(Level level, std::unordered_map* confMap, const char* confName) { + base::threading::ScopedLock scopedLock(lock()); + return unsafeGetConfigByRef(level, confMap, confName); // This is not unsafe anymore - mutex locked in scope + } + + template + Conf_T unsafeGetConfigByVal(Level level, const std::unordered_map* confMap, const char* confName) { + ELPP_UNUSED(confName); + typename std::unordered_map::const_iterator it = confMap->find(level); + if (it == confMap->end()) { + try { + return confMap->at(Level::Global); + } catch (...) { + ELPP_INTERNAL_ERROR("Unable to get configuration [" << confName << "] for level [" + << LevelHelper::convertToString(level) << "]" + << std::endl << "Please ensure you have properly configured logger.", false); + return Conf_T(); + } + } + return it->second; + } + + template + Conf_T& unsafeGetConfigByRef(Level level, std::unordered_map* confMap, const char* confName) { + ELPP_UNUSED(confName); + typename std::unordered_map::iterator it = confMap->find(level); + if (it == confMap->end()) { + try { + return confMap->at(Level::Global); + } catch (...) { + ELPP_INTERNAL_ERROR("Unable to get configuration [" << confName << "] for level [" + << LevelHelper::convertToString(level) << "]" + << std::endl << "Please ensure you have properly configured logger.", false); + } + } + return it->second; + } + + template + void setValue(Level level, const Conf_T& value, std::unordered_map* confMap, + bool includeGlobalLevel = true) { + // If map is empty and we are allowed to add into generic level (Level::Global), do it! + if (confMap->empty() && includeGlobalLevel) { + confMap->insert(std::make_pair(Level::Global, value)); + return; + } + // If same value exist in generic level already, dont add it to explicit level + typename std::unordered_map::iterator it = confMap->find(Level::Global); + if (it != confMap->end() && it->second == value) { + return; + } + // Now make sure we dont double up values if we really need to add it to explicit level + it = confMap->find(level); + if (it == confMap->end()) { + // Value not found for level, add new + confMap->insert(std::make_pair(level, value)); + } else { + // Value found, just update value + confMap->at(level) = value; + } + } + + void build(Configurations* configurations); + unsigned long getULong(std::string confVal); + std::string resolveFilename(const std::string& filename); + void insertFile(Level level, const std::string& fullFilename); + bool unsafeValidateFileRolling(Level level, const PreRollOutCallback& preRollOutCallback); + + inline bool validateFileRolling(Level level, const PreRollOutCallback& preRollOutCallback) { + base::threading::ScopedLock scopedLock(lock()); + return unsafeValidateFileRolling(level, preRollOutCallback); + } +}; +/// @brief Class that keeps record of current line hit for occasional logging +class HitCounter { + public: + HitCounter(void) : + m_filename(""), + m_lineNumber(0), + m_hitCounts(0) { + } + + HitCounter(const char* filename, base::type::LineNumber lineNumber) : + m_filename(filename), + m_lineNumber(lineNumber), + m_hitCounts(0) { + } + + HitCounter(const HitCounter& hitCounter) : + m_filename(hitCounter.m_filename), + m_lineNumber(hitCounter.m_lineNumber), + m_hitCounts(hitCounter.m_hitCounts) { + } + + HitCounter& operator=(const HitCounter& hitCounter) { + if (&hitCounter != this) { + m_filename = hitCounter.m_filename; + m_lineNumber = hitCounter.m_lineNumber; + m_hitCounts = hitCounter.m_hitCounts; + } + return *this; + } + + virtual ~HitCounter(void) { + } + + /// @brief Resets location of current hit counter + inline void resetLocation(const char* filename, base::type::LineNumber lineNumber) { + m_filename = filename; + m_lineNumber = lineNumber; + } + + /// @brief Validates hit counts and resets it if necessary + inline void validateHitCounts(std::size_t n) { + if (m_hitCounts >= base::consts::kMaxLogPerCounter) { + m_hitCounts = (n >= 1 ? base::consts::kMaxLogPerCounter % n : 0); + } + ++m_hitCounts; + } + + inline const char* filename(void) const { + return m_filename; + } + + inline base::type::LineNumber lineNumber(void) const { + return m_lineNumber; + } + + inline std::size_t hitCounts(void) const { + return m_hitCounts; + } + + inline void increment(void) { + ++m_hitCounts; + } + + class Predicate { + public: + Predicate(const char* filename, base::type::LineNumber lineNumber) + : m_filename(filename), + m_lineNumber(lineNumber) { + } + inline bool operator()(const HitCounter* counter) { + return ((counter != nullptr) && + (strcmp(counter->m_filename, m_filename) == 0) && + (counter->m_lineNumber == m_lineNumber)); + } + + private: + const char* m_filename; + base::type::LineNumber m_lineNumber; + }; + + private: + const char* m_filename; + base::type::LineNumber m_lineNumber; + std::size_t m_hitCounts; +}; +/// @brief Repository for hit counters used across the application +class RegisteredHitCounters : public base::utils::RegistryWithPred { + public: + /// @brief Validates counter for every N, i.e, registers new if does not exist otherwise updates original one + /// @return True if validation resulted in triggering hit. Meaning logs should be written everytime true is returned + bool validateEveryN(const char* filename, base::type::LineNumber lineNumber, std::size_t n); + + /// @brief Validates counter for hits >= N, i.e, registers new if does not exist otherwise updates original one + /// @return True if validation resulted in triggering hit. Meaning logs should be written everytime true is returned + bool validateAfterN(const char* filename, base::type::LineNumber lineNumber, std::size_t n); + + /// @brief Validates counter for hits are <= n, i.e, registers new if does not exist otherwise updates original one + /// @return True if validation resulted in triggering hit. Meaning logs should be written everytime true is returned + bool validateNTimes(const char* filename, base::type::LineNumber lineNumber, std::size_t n); + + /// @brief Gets hit counter registered at specified position + inline const base::HitCounter* getCounter(const char* filename, base::type::LineNumber lineNumber) { + base::threading::ScopedLock scopedLock(lock()); + return get(filename, lineNumber); + } +}; +/// @brief Action to be taken for dispatching +enum class DispatchAction : base::type::EnumType { + None = 1, NormalLog = 2, SysLog = 4 +}; +} // namespace base +template +class Callback : protected base::threading::ThreadSafe { + public: + Callback(void) : m_enabled(true) {} + inline bool enabled(void) const { + return m_enabled; + } + inline void setEnabled(bool enabled) { + base::threading::ScopedLock scopedLock(lock()); + m_enabled = enabled; + } + protected: + virtual void handle(const T* handlePtr) = 0; + private: + bool m_enabled; +}; +class LogDispatchData { + public: + LogDispatchData() : m_logMessage(nullptr), m_dispatchAction(base::DispatchAction::None) {} + inline const LogMessage* logMessage(void) const { + return m_logMessage; + } + inline base::DispatchAction dispatchAction(void) const { + return m_dispatchAction; + } + inline void setLogMessage(LogMessage* logMessage) { + m_logMessage = logMessage; + } + inline void setDispatchAction(base::DispatchAction dispatchAction) { + m_dispatchAction = dispatchAction; + } + private: + LogMessage* m_logMessage; + base::DispatchAction m_dispatchAction; + friend class base::LogDispatcher; + +}; +class LogDispatchCallback : public Callback { + protected: + virtual void handle(const LogDispatchData* data); + base::threading::Mutex& fileHandle(const LogDispatchData* data); + private: + friend class base::LogDispatcher; + std::unordered_map> m_fileLocks; + base::threading::Mutex m_fileLocksMapLock; +}; +class PerformanceTrackingCallback : public Callback { + private: + friend class base::PerformanceTracker; +}; +class LoggerRegistrationCallback : public Callback { + private: + friend class base::RegisteredLoggers; +}; +class LogBuilder : base::NoCopy { + public: + LogBuilder() : m_termSupportsColor(base::utils::OS::termSupportsColor()) {} + virtual ~LogBuilder(void) { + ELPP_INTERNAL_INFO(3, "Destroying log builder...") + } + virtual base::type::string_t build(const LogMessage* logMessage, bool appendNewLine) const = 0; + void convertToColoredOutput(base::type::string_t* logLine, Level level); + private: + bool m_termSupportsColor; + friend class el::base::DefaultLogDispatchCallback; +}; +typedef std::shared_ptr LogBuilderPtr; +/// @brief Represents a logger holding ID and configurations we need to write logs +/// +/// @detail This class does not write logs itself instead its used by writer to read configurations from. +class Logger : public base::threading::ThreadSafe, public Loggable { + public: + Logger(const std::string& id, base::LogStreamsReferenceMapPtr logStreamsReference); + Logger(const std::string& id, const Configurations& configurations, base::LogStreamsReferenceMapPtr logStreamsReference); + Logger(const Logger& logger); + Logger& operator=(const Logger& logger); + + virtual ~Logger(void) { + base::utils::safeDelete(m_typedConfigurations); + } + + virtual inline void log(el::base::type::ostream_t& os) const { + os << m_id.c_str(); + } + + /// @brief Configures the logger using specified configurations. + void configure(const Configurations& configurations); + + /// @brief Reconfigures logger using existing configurations + void reconfigure(void); + + inline const std::string& id(void) const { + return m_id; + } + + inline const std::string& parentApplicationName(void) const { + return m_parentApplicationName; + } + + inline void setParentApplicationName(const std::string& parentApplicationName) { + m_parentApplicationName = parentApplicationName; + } + + inline Configurations* configurations(void) { + return &m_configurations; + } + + inline base::TypedConfigurations* typedConfigurations(void) { + return m_typedConfigurations; + } + + static bool isValidId(const std::string& id); + + /// @brief Flushes logger to sync all log files for all levels + void flush(void); + + void flush(Level level, base::type::fstream_t* fs); + + inline bool isFlushNeeded(Level level) { + return ++m_unflushedCount.find(level)->second >= m_typedConfigurations->logFlushThreshold(level); + } + + inline LogBuilder* logBuilder(void) const { + return m_logBuilder.get(); + } + + inline void setLogBuilder(const LogBuilderPtr& logBuilder) { + m_logBuilder = logBuilder; + } + + inline bool enabled(Level level) const { + return m_typedConfigurations->enabled(level); + } + +#if ELPP_VARIADIC_TEMPLATES_SUPPORTED +# define LOGGER_LEVEL_WRITERS_SIGNATURES(FUNCTION_NAME)\ +template \ +inline void FUNCTION_NAME(const char*, const T&, const Args&...);\ +template \ +inline void FUNCTION_NAME(const T&); + + template + inline void verbose(int, const char*, const T&, const Args&...); + + template + inline void verbose(int, const T&); + + LOGGER_LEVEL_WRITERS_SIGNATURES(info) + LOGGER_LEVEL_WRITERS_SIGNATURES(debug) + LOGGER_LEVEL_WRITERS_SIGNATURES(warn) + LOGGER_LEVEL_WRITERS_SIGNATURES(error) + LOGGER_LEVEL_WRITERS_SIGNATURES(fatal) + LOGGER_LEVEL_WRITERS_SIGNATURES(trace) +# undef LOGGER_LEVEL_WRITERS_SIGNATURES +#endif // ELPP_VARIADIC_TEMPLATES_SUPPORTED + private: + std::string m_id; + base::TypedConfigurations* m_typedConfigurations; + base::type::stringstream_t m_stream; + std::string m_parentApplicationName; + bool m_isConfigured; + Configurations m_configurations; + std::unordered_map m_unflushedCount; + base::LogStreamsReferenceMapPtr m_logStreamsReference = nullptr; + LogBuilderPtr m_logBuilder; + + friend class el::LogMessage; + friend class el::Loggers; + friend class el::Helpers; + friend class el::base::RegisteredLoggers; + friend class el::base::DefaultLogDispatchCallback; + friend class el::base::MessageBuilder; + friend class el::base::Writer; + friend class el::base::PErrorWriter; + friend class el::base::Storage; + friend class el::base::PerformanceTracker; + friend class el::base::LogDispatcher; + + Logger(void); + +#if ELPP_VARIADIC_TEMPLATES_SUPPORTED + template + void log_(Level, int, const char*, const T&, const Args&...); + + template + inline void log_(Level, int, const T&); + + template + void log(Level, const char*, const T&, const Args&...); + + template + inline void log(Level, const T&); +#endif // ELPP_VARIADIC_TEMPLATES_SUPPORTED + + void initUnflushedCount(void); + + inline base::type::stringstream_t& stream(void) { + return m_stream; + } + + void resolveLoggerFormatSpec(void) const; +}; +namespace base { +/// @brief Loggers repository +class RegisteredLoggers : public base::utils::Registry { + public: + explicit RegisteredLoggers(const LogBuilderPtr& defaultLogBuilder); + + virtual ~RegisteredLoggers(void) { + unsafeFlushAll(); + } + + inline void setDefaultConfigurations(const Configurations& configurations) { + base::threading::ScopedLock scopedLock(lock()); + m_defaultConfigurations.setFromBase(const_cast(&configurations)); + } + + inline Configurations* defaultConfigurations(void) { + return &m_defaultConfigurations; + } + + Logger* get(const std::string& id, bool forceCreation = true); + + template + inline bool installLoggerRegistrationCallback(const std::string& id) { + return base::utils::Utils::installCallback(id, + &m_loggerRegistrationCallbacks); + } + + template + inline void uninstallLoggerRegistrationCallback(const std::string& id) { + base::utils::Utils::uninstallCallback(id, &m_loggerRegistrationCallbacks); + } + + template + inline T* loggerRegistrationCallback(const std::string& id) { + return base::utils::Utils::callback(id, &m_loggerRegistrationCallbacks); + } + + bool remove(const std::string& id); + + inline bool has(const std::string& id) { + return get(id, false) != nullptr; + } + + inline void unregister(Logger*& logger) { + base::threading::ScopedLock scopedLock(lock()); + base::utils::Registry::unregister(logger->id()); + } + + inline LogStreamsReferenceMapPtr logStreamsReference(void) { + return m_logStreamsReference; + } + + inline void flushAll(void) { + base::threading::ScopedLock scopedLock(lock()); + unsafeFlushAll(); + } + + inline void setDefaultLogBuilder(LogBuilderPtr& logBuilderPtr) { + base::threading::ScopedLock scopedLock(lock()); + m_defaultLogBuilder = logBuilderPtr; + } + + private: + LogBuilderPtr m_defaultLogBuilder; + Configurations m_defaultConfigurations; + base::LogStreamsReferenceMapPtr m_logStreamsReference = nullptr; + std::unordered_map m_loggerRegistrationCallbacks; + friend class el::base::Storage; + + void unsafeFlushAll(void); +}; +/// @brief Represents registries for verbose logging +class VRegistry : base::NoCopy, public base::threading::ThreadSafe { + public: + explicit VRegistry(base::type::VerboseLevel level, base::type::EnumType* pFlags); + + /// @brief Sets verbose level. Accepted range is 0-9 + void setLevel(base::type::VerboseLevel level); + + inline base::type::VerboseLevel level(void) const { + return m_level; + } + + inline void clearModules(void) { + base::threading::ScopedLock scopedLock(lock()); + m_modules.clear(); + } + + void setModules(const char* modules); + + bool allowed(base::type::VerboseLevel vlevel, const char* file); + + inline const std::unordered_map& modules(void) const { + return m_modules; + } + + void setFromArgs(const base::utils::CommandLineArgs* commandLineArgs); + + /// @brief Whether or not vModules enabled + inline bool vModulesEnabled(void) { + return !base::utils::hasFlag(LoggingFlag::DisableVModules, *m_pFlags); + } + + private: + base::type::VerboseLevel m_level; + base::type::EnumType* m_pFlags; + std::unordered_map m_modules; +}; +} // namespace base +class LogMessage { + public: + LogMessage(Level level, const std::string& file, base::type::LineNumber line, const std::string& func, + base::type::VerboseLevel verboseLevel, Logger* logger) : + m_level(level), m_file(file), m_line(line), m_func(func), + m_verboseLevel(verboseLevel), m_logger(logger), m_message(logger->stream().str()) { + } + inline Level level(void) const { + return m_level; + } + inline const std::string& file(void) const { + return m_file; + } + inline base::type::LineNumber line(void) const { + return m_line; + } + inline const std::string& func(void) const { + return m_func; + } + inline base::type::VerboseLevel verboseLevel(void) const { + return m_verboseLevel; + } + inline Logger* logger(void) const { + return m_logger; + } + inline const base::type::string_t& message(void) const { + return m_message; + } + private: + Level m_level; + std::string m_file; + base::type::LineNumber m_line; + std::string m_func; + base::type::VerboseLevel m_verboseLevel; + Logger* m_logger; + base::type::string_t m_message; +}; +namespace base { +#if ELPP_ASYNC_LOGGING +class AsyncLogItem { + public: + explicit AsyncLogItem(const LogMessage& logMessage, const LogDispatchData& data, const base::type::string_t& logLine) + : m_logMessage(logMessage), m_dispatchData(data), m_logLine(logLine) {} + virtual ~AsyncLogItem() {} + inline LogMessage* logMessage(void) { + return &m_logMessage; + } + inline LogDispatchData* data(void) { + return &m_dispatchData; + } + inline base::type::string_t logLine(void) { + return m_logLine; + } + private: + LogMessage m_logMessage; + LogDispatchData m_dispatchData; + base::type::string_t m_logLine; +}; +class AsyncLogQueue : public base::threading::ThreadSafe { + public: + virtual ~AsyncLogQueue() { + ELPP_INTERNAL_INFO(6, "~AsyncLogQueue"); + } + + inline AsyncLogItem next(void) { + base::threading::ScopedLock scopedLock(lock()); + AsyncLogItem result = m_queue.front(); + m_queue.pop(); + return result; + } + + inline void push(const AsyncLogItem& item) { + base::threading::ScopedLock scopedLock(lock()); + m_queue.push(item); + } + inline void pop(void) { + base::threading::ScopedLock scopedLock(lock()); + m_queue.pop(); + } + inline AsyncLogItem front(void) { + base::threading::ScopedLock scopedLock(lock()); + return m_queue.front(); + } + inline bool empty(void) { + base::threading::ScopedLock scopedLock(lock()); + return m_queue.empty(); + } + private: + std::queue m_queue; +}; +class IWorker { + public: + virtual ~IWorker() {} + virtual void start() = 0; +}; +#endif // ELPP_ASYNC_LOGGING +/// @brief Easylogging++ management storage +class Storage : base::NoCopy, public base::threading::ThreadSafe { + public: +#if ELPP_ASYNC_LOGGING + Storage(const LogBuilderPtr& defaultLogBuilder, base::IWorker* asyncDispatchWorker); +#else + explicit Storage(const LogBuilderPtr& defaultLogBuilder); +#endif // ELPP_ASYNC_LOGGING + + virtual ~Storage(void); + + inline bool validateEveryNCounter(const char* filename, base::type::LineNumber lineNumber, std::size_t occasion) { + return hitCounters()->validateEveryN(filename, lineNumber, occasion); + } + + inline bool validateAfterNCounter(const char* filename, base::type::LineNumber lineNumber, std::size_t n) { + return hitCounters()->validateAfterN(filename, lineNumber, n); + } + + inline bool validateNTimesCounter(const char* filename, base::type::LineNumber lineNumber, std::size_t n) { + return hitCounters()->validateNTimes(filename, lineNumber, n); + } + + inline base::RegisteredHitCounters* hitCounters(void) const { + return m_registeredHitCounters; + } + + inline base::RegisteredLoggers* registeredLoggers(void) const { + return m_registeredLoggers; + } + + inline base::VRegistry* vRegistry(void) const { + return m_vRegistry; + } + +#if ELPP_ASYNC_LOGGING + inline base::AsyncLogQueue* asyncLogQueue(void) const { + return m_asyncLogQueue; + } +#endif // ELPP_ASYNC_LOGGING + + inline const base::utils::CommandLineArgs* commandLineArgs(void) const { + return &m_commandLineArgs; + } + + inline void addFlag(LoggingFlag flag) { + base::utils::addFlag(flag, &m_flags); + } + + inline void removeFlag(LoggingFlag flag) { + base::utils::removeFlag(flag, &m_flags); + } + + inline bool hasFlag(LoggingFlag flag) const { + return base::utils::hasFlag(flag, m_flags); + } + + inline base::type::EnumType flags(void) const { + return m_flags; + } + + inline void setFlags(base::type::EnumType flags) { + m_flags = flags; + } + + inline void setPreRollOutCallback(const PreRollOutCallback& callback) { + m_preRollOutCallback = callback; + } + + inline void unsetPreRollOutCallback(void) { + m_preRollOutCallback = base::defaultPreRollOutCallback; + } + + inline PreRollOutCallback& preRollOutCallback(void) { + return m_preRollOutCallback; + } + + bool hasCustomFormatSpecifier(const char* formatSpecifier); + void installCustomFormatSpecifier(const CustomFormatSpecifier& customFormatSpecifier); + bool uninstallCustomFormatSpecifier(const char* formatSpecifier); + + const std::vector* customFormatSpecifiers(void) const { + return &m_customFormatSpecifiers; + } + + base::threading::Mutex& customFormatSpecifiersLock() { + return m_customFormatSpecifiersLock; + } + + inline void setLoggingLevel(Level level) { + m_loggingLevel = level; + } + + template + inline bool installLogDispatchCallback(const std::string& id) { + return base::utils::Utils::installCallback(id, &m_logDispatchCallbacks); + } + + template + inline void uninstallLogDispatchCallback(const std::string& id) { + base::utils::Utils::uninstallCallback(id, &m_logDispatchCallbacks); + } + template + inline T* logDispatchCallback(const std::string& id) { + return base::utils::Utils::callback(id, &m_logDispatchCallbacks); + } + +#if defined(ELPP_FEATURE_ALL) || defined(ELPP_FEATURE_PERFORMANCE_TRACKING) + template + inline bool installPerformanceTrackingCallback(const std::string& id) { + return base::utils::Utils::installCallback(id, + &m_performanceTrackingCallbacks); + } + + template + inline void uninstallPerformanceTrackingCallback(const std::string& id) { + base::utils::Utils::uninstallCallback(id, + &m_performanceTrackingCallbacks); + } + + template + inline T* performanceTrackingCallback(const std::string& id) { + return base::utils::Utils::callback(id, &m_performanceTrackingCallbacks); + } +#endif // defined(ELPP_FEATURE_ALL) || defined(ELPP_FEATURE_PERFORMANCE_TRACKING) + + /// @brief Sets thread name for current thread. Requires std::thread + inline void setThreadName(const std::string& name) { + if (name.empty()) return; + base::threading::ScopedLock scopedLock(m_threadNamesLock); + m_threadNames[base::threading::getCurrentThreadId()] = name; + } + + inline std::string getThreadName(const std::string& threadId) { + base::threading::ScopedLock scopedLock(m_threadNamesLock); + std::unordered_map::const_iterator it = m_threadNames.find(threadId); + if (it == m_threadNames.end()) { + return threadId; + } + return it->second; + } + private: + base::RegisteredHitCounters* m_registeredHitCounters; + base::RegisteredLoggers* m_registeredLoggers; + base::type::EnumType m_flags; + base::VRegistry* m_vRegistry; +#if ELPP_ASYNC_LOGGING + base::AsyncLogQueue* m_asyncLogQueue; + base::IWorker* m_asyncDispatchWorker; +#endif // ELPP_ASYNC_LOGGING + base::utils::CommandLineArgs m_commandLineArgs; + PreRollOutCallback m_preRollOutCallback; + std::unordered_map m_logDispatchCallbacks; + std::unordered_map m_performanceTrackingCallbacks; + std::unordered_map m_threadNames; + std::vector m_customFormatSpecifiers; + base::threading::Mutex m_customFormatSpecifiersLock; + base::threading::Mutex m_threadNamesLock; + Level m_loggingLevel; + + friend class el::Helpers; + friend class el::base::DefaultLogDispatchCallback; + friend class el::LogBuilder; + friend class el::base::MessageBuilder; + friend class el::base::Writer; + friend class el::base::PerformanceTracker; + friend class el::base::LogDispatcher; + + void setApplicationArguments(int argc, char** argv); + + inline void setApplicationArguments(int argc, const char** argv) { + setApplicationArguments(argc, const_cast(argv)); + } +}; +extern ELPP_EXPORT base::type::StoragePointer elStorage; +#define ELPP el::base::elStorage +class DefaultLogDispatchCallback : public LogDispatchCallback { + protected: + void handle(const LogDispatchData* data); + private: + const LogDispatchData* m_data; + void dispatch(base::type::string_t&& logLine); +}; +#if ELPP_ASYNC_LOGGING +class AsyncLogDispatchCallback : public LogDispatchCallback { + protected: + void handle(const LogDispatchData* data); +}; +class AsyncDispatchWorker : public base::IWorker, public base::threading::ThreadSafe { + public: + AsyncDispatchWorker(); + virtual ~AsyncDispatchWorker(); + + bool clean(void); + void emptyQueue(void); + virtual void start(void); + void handle(AsyncLogItem* logItem); + void run(void); + + void setContinueRunning(bool value) { + base::threading::ScopedLock scopedLock(m_continueRunningLock); + m_continueRunning = value; + } + + bool continueRunning(void) const { + return m_continueRunning; + } + private: + std::condition_variable cv; + bool m_continueRunning; + base::threading::Mutex m_continueRunningLock; +}; +#endif // ELPP_ASYNC_LOGGING +} // namespace base +namespace base { +class DefaultLogBuilder : public LogBuilder { + public: + base::type::string_t build(const LogMessage* logMessage, bool appendNewLine) const; +}; +/// @brief Dispatches log messages +class LogDispatcher : base::NoCopy { + public: + LogDispatcher(bool proceed, LogMessage* logMessage, base::DispatchAction dispatchAction) : + m_proceed(proceed), + m_logMessage(logMessage), + m_dispatchAction(std::move(dispatchAction)) { + } + + void dispatch(void); + + private: + bool m_proceed; + LogMessage* m_logMessage; + base::DispatchAction m_dispatchAction; +}; +#if defined(ELPP_STL_LOGGING) +/// @brief Workarounds to write some STL logs +/// +/// @detail There is workaround needed to loop through some stl containers. In order to do that, we need iterable containers +/// of same type and provide iterator interface and pass it on to writeIterator(). +/// Remember, this is passed by value in constructor so that we dont change original containers. +/// This operation is as expensive as Big-O(std::min(class_.size(), base::consts::kMaxLogPerContainer)) +namespace workarounds { +/// @brief Abstract IterableContainer template that provides interface for iterable classes of type T +template +class IterableContainer { + public: + typedef typename Container::iterator iterator; + typedef typename Container::const_iterator const_iterator; + IterableContainer(void) {} + virtual ~IterableContainer(void) {} + iterator begin(void) { + return getContainer().begin(); + } + iterator end(void) { + return getContainer().end(); + } + private: + virtual Container& getContainer(void) = 0; +}; +/// @brief Implements IterableContainer and provides iterable std::priority_queue class +template, typename Comparator = std::less> +class IterablePriorityQueue : public IterableContainer, + public std::priority_queue { + public: + IterablePriorityQueue(std::priority_queue queue_) { + std::size_t count_ = 0; + while (++count_ < base::consts::kMaxLogPerContainer && !queue_.empty()) { + this->push(queue_.top()); + queue_.pop(); + } + } + private: + inline Container& getContainer(void) { + return this->c; + } +}; +/// @brief Implements IterableContainer and provides iterable std::queue class +template> +class IterableQueue : public IterableContainer, public std::queue { + public: + IterableQueue(std::queue queue_) { + std::size_t count_ = 0; + while (++count_ < base::consts::kMaxLogPerContainer && !queue_.empty()) { + this->push(queue_.front()); + queue_.pop(); + } + } + private: + inline Container& getContainer(void) { + return this->c; + } +}; +/// @brief Implements IterableContainer and provides iterable std::stack class +template> +class IterableStack : public IterableContainer, public std::stack { + public: + IterableStack(std::stack stack_) { + std::size_t count_ = 0; + while (++count_ < base::consts::kMaxLogPerContainer && !stack_.empty()) { + this->push(stack_.top()); + stack_.pop(); + } + } + private: + inline Container& getContainer(void) { + return this->c; + } +}; +} // namespace workarounds +#endif // defined(ELPP_STL_LOGGING) +// Log message builder +class MessageBuilder { + public: + MessageBuilder(void) : m_logger(nullptr), m_containerLogSeparator(ELPP_LITERAL("")) {} + void initialize(Logger* logger); + +# define ELPP_SIMPLE_LOG(LOG_TYPE)\ +MessageBuilder& operator<<(LOG_TYPE msg) {\ +m_logger->stream() << msg;\ +if (ELPP->hasFlag(LoggingFlag::AutoSpacing)) {\ +m_logger->stream() << " ";\ +}\ +return *this;\ +} + + inline MessageBuilder& operator<<(const std::string& msg) { + return operator<<(msg.c_str()); + } + ELPP_SIMPLE_LOG(char) + ELPP_SIMPLE_LOG(bool) + ELPP_SIMPLE_LOG(signed short) + ELPP_SIMPLE_LOG(unsigned short) + ELPP_SIMPLE_LOG(signed int) + ELPP_SIMPLE_LOG(unsigned int) + ELPP_SIMPLE_LOG(signed long) + ELPP_SIMPLE_LOG(unsigned long) + ELPP_SIMPLE_LOG(float) + ELPP_SIMPLE_LOG(double) + ELPP_SIMPLE_LOG(char*) + ELPP_SIMPLE_LOG(const char*) + ELPP_SIMPLE_LOG(const void*) + ELPP_SIMPLE_LOG(long double) + inline MessageBuilder& operator<<(const std::wstring& msg) { + return operator<<(msg.c_str()); + } + MessageBuilder& operator<<(const wchar_t* msg); + // ostream manipulators + inline MessageBuilder& operator<<(std::ostream& (*OStreamMani)(std::ostream&)) { + m_logger->stream() << OStreamMani; + return *this; + } +#define ELPP_ITERATOR_CONTAINER_LOG_ONE_ARG(temp) \ +template \ +inline MessageBuilder& operator<<(const temp& template_inst) { \ +return writeIterator(template_inst.begin(), template_inst.end(), template_inst.size()); \ +} +#define ELPP_ITERATOR_CONTAINER_LOG_TWO_ARG(temp) \ +template \ +inline MessageBuilder& operator<<(const temp& template_inst) { \ +return writeIterator(template_inst.begin(), template_inst.end(), template_inst.size()); \ +} +#define ELPP_ITERATOR_CONTAINER_LOG_THREE_ARG(temp) \ +template \ +inline MessageBuilder& operator<<(const temp& template_inst) { \ +return writeIterator(template_inst.begin(), template_inst.end(), template_inst.size()); \ +} +#define ELPP_ITERATOR_CONTAINER_LOG_FOUR_ARG(temp) \ +template \ +inline MessageBuilder& operator<<(const temp& template_inst) { \ +return writeIterator(template_inst.begin(), template_inst.end(), template_inst.size()); \ +} +#define ELPP_ITERATOR_CONTAINER_LOG_FIVE_ARG(temp) \ +template \ +inline MessageBuilder& operator<<(const temp& template_inst) { \ +return writeIterator(template_inst.begin(), template_inst.end(), template_inst.size()); \ +} + +#if defined(ELPP_STL_LOGGING) + ELPP_ITERATOR_CONTAINER_LOG_TWO_ARG(std::vector) + ELPP_ITERATOR_CONTAINER_LOG_TWO_ARG(std::list) + ELPP_ITERATOR_CONTAINER_LOG_TWO_ARG(std::deque) + ELPP_ITERATOR_CONTAINER_LOG_THREE_ARG(std::set) + ELPP_ITERATOR_CONTAINER_LOG_THREE_ARG(std::multiset) + ELPP_ITERATOR_CONTAINER_LOG_FOUR_ARG(std::map) + ELPP_ITERATOR_CONTAINER_LOG_FOUR_ARG(std::multimap) + template + inline MessageBuilder& operator<<(const std::queue& queue_) { + base::workarounds::IterableQueue iterableQueue_ = + static_cast >(queue_); + return writeIterator(iterableQueue_.begin(), iterableQueue_.end(), iterableQueue_.size()); + } + template + inline MessageBuilder& operator<<(const std::stack& stack_) { + base::workarounds::IterableStack iterableStack_ = + static_cast >(stack_); + return writeIterator(iterableStack_.begin(), iterableStack_.end(), iterableStack_.size()); + } + template + inline MessageBuilder& operator<<(const std::priority_queue& priorityQueue_) { + base::workarounds::IterablePriorityQueue iterablePriorityQueue_ = + static_cast >(priorityQueue_); + return writeIterator(iterablePriorityQueue_.begin(), iterablePriorityQueue_.end(), iterablePriorityQueue_.size()); + } + template + MessageBuilder& operator<<(const std::pair& pair_) { + m_logger->stream() << ELPP_LITERAL("("); + operator << (static_cast(pair_.first)); + m_logger->stream() << ELPP_LITERAL(", "); + operator << (static_cast(pair_.second)); + m_logger->stream() << ELPP_LITERAL(")"); + return *this; + } + template + MessageBuilder& operator<<(const std::bitset& bitset_) { + m_logger->stream() << ELPP_LITERAL("["); + operator << (bitset_.to_string()); + m_logger->stream() << ELPP_LITERAL("]"); + return *this; + } +# if defined(ELPP_LOG_STD_ARRAY) + template + inline MessageBuilder& operator<<(const std::array& array) { + return writeIterator(array.begin(), array.end(), array.size()); + } +# endif // defined(ELPP_LOG_STD_ARRAY) +# if defined(ELPP_LOG_UNORDERED_MAP) + ELPP_ITERATOR_CONTAINER_LOG_FIVE_ARG(std::unordered_map) + ELPP_ITERATOR_CONTAINER_LOG_FIVE_ARG(std::unordered_multimap) +# endif // defined(ELPP_LOG_UNORDERED_MAP) +# if defined(ELPP_LOG_UNORDERED_SET) + ELPP_ITERATOR_CONTAINER_LOG_FOUR_ARG(std::unordered_set) + ELPP_ITERATOR_CONTAINER_LOG_FOUR_ARG(std::unordered_multiset) +# endif // defined(ELPP_LOG_UNORDERED_SET) +#endif // defined(ELPP_STL_LOGGING) +#if defined(ELPP_QT_LOGGING) + inline MessageBuilder& operator<<(const QString& msg) { +# if defined(ELPP_UNICODE) + m_logger->stream() << msg.toStdWString(); +# else + m_logger->stream() << msg.toStdString(); +# endif // defined(ELPP_UNICODE) + return *this; + } + inline MessageBuilder& operator<<(const QByteArray& msg) { + return operator << (QString(msg)); + } + inline MessageBuilder& operator<<(const QStringRef& msg) { + return operator<<(msg.toString()); + } + inline MessageBuilder& operator<<(qint64 msg) { +# if defined(ELPP_UNICODE) + m_logger->stream() << QString::number(msg).toStdWString(); +# else + m_logger->stream() << QString::number(msg).toStdString(); +# endif // defined(ELPP_UNICODE) + return *this; + } + inline MessageBuilder& operator<<(quint64 msg) { +# if defined(ELPP_UNICODE) + m_logger->stream() << QString::number(msg).toStdWString(); +# else + m_logger->stream() << QString::number(msg).toStdString(); +# endif // defined(ELPP_UNICODE) + return *this; + } + inline MessageBuilder& operator<<(QChar msg) { + m_logger->stream() << msg.toLatin1(); + return *this; + } + inline MessageBuilder& operator<<(const QLatin1String& msg) { + m_logger->stream() << msg.latin1(); + return *this; + } + ELPP_ITERATOR_CONTAINER_LOG_ONE_ARG(QList) + ELPP_ITERATOR_CONTAINER_LOG_ONE_ARG(QVector) + ELPP_ITERATOR_CONTAINER_LOG_ONE_ARG(QQueue) + ELPP_ITERATOR_CONTAINER_LOG_ONE_ARG(QSet) + ELPP_ITERATOR_CONTAINER_LOG_ONE_ARG(QLinkedList) + ELPP_ITERATOR_CONTAINER_LOG_ONE_ARG(QStack) + template + MessageBuilder& operator<<(const QPair& pair_) { + m_logger->stream() << ELPP_LITERAL("("); + operator << (static_cast(pair_.first)); + m_logger->stream() << ELPP_LITERAL(", "); + operator << (static_cast(pair_.second)); + m_logger->stream() << ELPP_LITERAL(")"); + return *this; + } + template + MessageBuilder& operator<<(const QMap& map_) { + m_logger->stream() << ELPP_LITERAL("["); + QList keys = map_.keys(); + typename QList::const_iterator begin = keys.begin(); + typename QList::const_iterator end = keys.end(); + int max_ = static_cast(base::consts::kMaxLogPerContainer); // to prevent warning + for (int index_ = 0; begin != end && index_ < max_; ++index_, ++begin) { + m_logger->stream() << ELPP_LITERAL("("); + operator << (static_cast(*begin)); + m_logger->stream() << ELPP_LITERAL(", "); + operator << (static_cast(map_.value(*begin))); + m_logger->stream() << ELPP_LITERAL(")"); + m_logger->stream() << ((index_ < keys.size() -1) ? m_containerLogSeparator : ELPP_LITERAL("")); + } + if (begin != end) { + m_logger->stream() << ELPP_LITERAL("..."); + } + m_logger->stream() << ELPP_LITERAL("]"); + return *this; + } + template + inline MessageBuilder& operator<<(const QMultiMap& map_) { + operator << (static_cast>(map_)); + return *this; + } + template + MessageBuilder& operator<<(const QHash& hash_) { + m_logger->stream() << ELPP_LITERAL("["); + QList keys = hash_.keys(); + typename QList::const_iterator begin = keys.begin(); + typename QList::const_iterator end = keys.end(); + int max_ = static_cast(base::consts::kMaxLogPerContainer); // prevent type warning + for (int index_ = 0; begin != end && index_ < max_; ++index_, ++begin) { + m_logger->stream() << ELPP_LITERAL("("); + operator << (static_cast(*begin)); + m_logger->stream() << ELPP_LITERAL(", "); + operator << (static_cast(hash_.value(*begin))); + m_logger->stream() << ELPP_LITERAL(")"); + m_logger->stream() << ((index_ < keys.size() -1) ? m_containerLogSeparator : ELPP_LITERAL("")); + } + if (begin != end) { + m_logger->stream() << ELPP_LITERAL("..."); + } + m_logger->stream() << ELPP_LITERAL("]"); + return *this; + } + template + inline MessageBuilder& operator<<(const QMultiHash& multiHash_) { + operator << (static_cast>(multiHash_)); + return *this; + } +#endif // defined(ELPP_QT_LOGGING) +#if defined(ELPP_BOOST_LOGGING) + ELPP_ITERATOR_CONTAINER_LOG_TWO_ARG(boost::container::vector) + ELPP_ITERATOR_CONTAINER_LOG_TWO_ARG(boost::container::stable_vector) + ELPP_ITERATOR_CONTAINER_LOG_TWO_ARG(boost::container::list) + ELPP_ITERATOR_CONTAINER_LOG_TWO_ARG(boost::container::deque) + ELPP_ITERATOR_CONTAINER_LOG_FOUR_ARG(boost::container::map) + ELPP_ITERATOR_CONTAINER_LOG_FOUR_ARG(boost::container::flat_map) + ELPP_ITERATOR_CONTAINER_LOG_THREE_ARG(boost::container::set) + ELPP_ITERATOR_CONTAINER_LOG_THREE_ARG(boost::container::flat_set) +#endif // defined(ELPP_BOOST_LOGGING) + + /// @brief Macro used internally that can be used externally to make containers easylogging++ friendly + /// + /// @detail This macro expands to write an ostream& operator<< for container. This container is expected to + /// have begin() and end() methods that return respective iterators + /// @param ContainerType Type of container e.g, MyList from WX_DECLARE_LIST(int, MyList); in wxwidgets + /// @param SizeMethod Method used to get size of container. + /// @param ElementInstance Instance of element to be fed out. Instance name is "elem". See WXELPP_ENABLED macro + /// for an example usage +#define MAKE_CONTAINERELPP_FRIENDLY(ContainerType, SizeMethod, ElementInstance) \ +el::base::type::ostream_t& operator<<(el::base::type::ostream_t& ss, const ContainerType& container) {\ +const el::base::type::char_t* sep = ELPP->hasFlag(el::LoggingFlag::NewLineForContainer) ? \ +ELPP_LITERAL("\n ") : ELPP_LITERAL(", ");\ +ContainerType::const_iterator elem = container.begin();\ +ContainerType::const_iterator endElem = container.end();\ +std::size_t size_ = container.SizeMethod; \ +ss << ELPP_LITERAL("[");\ +for (std::size_t i = 0; elem != endElem && i < el::base::consts::kMaxLogPerContainer; ++i, ++elem) { \ +ss << ElementInstance;\ +ss << ((i < size_ - 1) ? sep : ELPP_LITERAL(""));\ +}\ +if (elem != endElem) {\ +ss << ELPP_LITERAL("...");\ +}\ +ss << ELPP_LITERAL("]");\ +return ss;\ +} +#if defined(ELPP_WXWIDGETS_LOGGING) + ELPP_ITERATOR_CONTAINER_LOG_ONE_ARG(wxVector) +# define ELPP_WX_PTR_ENABLED(ContainerType) MAKE_CONTAINERELPP_FRIENDLY(ContainerType, size(), *(*elem)) +# define ELPP_WX_ENABLED(ContainerType) MAKE_CONTAINERELPP_FRIENDLY(ContainerType, size(), (*elem)) +# define ELPP_WX_HASH_MAP_ENABLED(ContainerType) MAKE_CONTAINERELPP_FRIENDLY(ContainerType, size(), \ +ELPP_LITERAL("(") << elem->first << ELPP_LITERAL(", ") << elem->second << ELPP_LITERAL(")") +#else +# define ELPP_WX_PTR_ENABLED(ContainerType) +# define ELPP_WX_ENABLED(ContainerType) +# define ELPP_WX_HASH_MAP_ENABLED(ContainerType) +#endif // defined(ELPP_WXWIDGETS_LOGGING) + // Other classes + template + ELPP_SIMPLE_LOG(const Class&) +#undef ELPP_SIMPLE_LOG +#undef ELPP_ITERATOR_CONTAINER_LOG_ONE_ARG +#undef ELPP_ITERATOR_CONTAINER_LOG_TWO_ARG +#undef ELPP_ITERATOR_CONTAINER_LOG_THREE_ARG +#undef ELPP_ITERATOR_CONTAINER_LOG_FOUR_ARG +#undef ELPP_ITERATOR_CONTAINER_LOG_FIVE_ARG + private: + Logger* m_logger; + const base::type::char_t* m_containerLogSeparator; + + template + MessageBuilder& writeIterator(Iterator begin_, Iterator end_, std::size_t size_) { + m_logger->stream() << ELPP_LITERAL("["); + for (std::size_t i = 0; begin_ != end_ && i < base::consts::kMaxLogPerContainer; ++i, ++begin_) { + operator << (*begin_); + m_logger->stream() << ((i < size_ - 1) ? m_containerLogSeparator : ELPP_LITERAL("")); + } + if (begin_ != end_) { + m_logger->stream() << ELPP_LITERAL("..."); + } + m_logger->stream() << ELPP_LITERAL("]"); + if (ELPP->hasFlag(LoggingFlag::AutoSpacing)) { + m_logger->stream() << " "; + } + return *this; + } +}; +/// @brief Writes nothing - Used when certain log is disabled +class NullWriter : base::NoCopy { + public: + NullWriter(void) {} + + // Null manipulator + inline NullWriter& operator<<(std::ostream& (*)(std::ostream&)) { + return *this; + } + + template + inline NullWriter& operator<<(const T&) { + return *this; + } + + inline operator bool() { + return true; + } +}; +/// @brief Main entry point of each logging +class Writer : base::NoCopy { + public: + Writer(Level level, const char* file, base::type::LineNumber line, + const char* func, base::DispatchAction dispatchAction = base::DispatchAction::NormalLog, + base::type::VerboseLevel verboseLevel = 0) : + m_msg(nullptr), m_level(level), m_file(file), m_line(line), m_func(func), m_verboseLevel(verboseLevel), + m_logger(nullptr), m_proceed(false), m_dispatchAction(dispatchAction) { + } + + Writer(LogMessage* msg, base::DispatchAction dispatchAction = base::DispatchAction::NormalLog) : + m_msg(msg), m_level(msg != nullptr ? msg->level() : Level::Unknown), + m_line(0), m_logger(nullptr), m_proceed(false), m_dispatchAction(dispatchAction) { + } + + virtual ~Writer(void) { + processDispatch(); + } + + template + inline Writer& operator<<(const T& log) { +#if ELPP_LOGGING_ENABLED + if (m_proceed) { + m_messageBuilder << log; + } +#endif // ELPP_LOGGING_ENABLED + return *this; + } + + inline Writer& operator<<(std::ostream& (*log)(std::ostream&)) { +#if ELPP_LOGGING_ENABLED + if (m_proceed) { + m_messageBuilder << log; + } +#endif // ELPP_LOGGING_ENABLED + return *this; + } + + inline operator bool() { + return true; + } + + Writer& construct(Logger* logger, bool needLock = true); + Writer& construct(int count, const char* loggerIds, ...); + protected: + LogMessage* m_msg; + Level m_level; + const char* m_file; + const base::type::LineNumber m_line; + const char* m_func; + base::type::VerboseLevel m_verboseLevel; + Logger* m_logger; + bool m_proceed; + base::MessageBuilder m_messageBuilder; + base::DispatchAction m_dispatchAction; + std::vector m_loggerIds; + friend class el::Helpers; + + void initializeLogger(const std::string& loggerId, bool lookup = true, bool needLock = true); + void processDispatch(); + void triggerDispatch(void); +}; +class PErrorWriter : public base::Writer { + public: + PErrorWriter(Level level, const char* file, base::type::LineNumber line, + const char* func, base::DispatchAction dispatchAction = base::DispatchAction::NormalLog, + base::type::VerboseLevel verboseLevel = 0) : + base::Writer(level, file, line, func, dispatchAction, verboseLevel) { + } + + virtual ~PErrorWriter(void); +}; +} // namespace base +// Logging from Logger class. Why this is here? Because we have Storage and Writer class available +#if ELPP_VARIADIC_TEMPLATES_SUPPORTED +template +void Logger::log_(Level level, int vlevel, const char* s, const T& value, const Args&... args) { + base::MessageBuilder b; + b.initialize(this); + while (*s) { + if (*s == base::consts::kFormatSpecifierChar) { + if (*(s + 1) == base::consts::kFormatSpecifierChar) { + ++s; + } else { + if (*(s + 1) == base::consts::kFormatSpecifierCharValue) { + ++s; + b << value; + log_(level, vlevel, ++s, args...); + return; + } + } + } + b << *s++; + } + ELPP_INTERNAL_ERROR("Too many arguments provided. Unable to handle. Please provide more format specifiers", false); +} +template +void Logger::log_(Level level, int vlevel, const T& log) { + if (level == Level::Verbose) { + if (ELPP->vRegistry()->allowed(vlevel, __FILE__)) { + base::Writer(Level::Verbose, "FILE", 0, "FUNCTION", + base::DispatchAction::NormalLog, vlevel).construct(this, false) << log; + } else { + stream().str(ELPP_LITERAL("")); + releaseLock(); + } + } else { + base::Writer(level, "FILE", 0, "FUNCTION").construct(this, false) << log; + } +} +template +inline void Logger::log(Level level, const char* s, const T& value, const Args&... args) { + acquireLock(); // released in Writer! + log_(level, 0, s, value, args...); +} +template +inline void Logger::log(Level level, const T& log) { + acquireLock(); // released in Writer! + log_(level, 0, log); +} +# if ELPP_VERBOSE_LOG +template +inline void Logger::verbose(int vlevel, const char* s, const T& value, const Args&... args) { + acquireLock(); // released in Writer! + log_(el::Level::Verbose, vlevel, s, value, args...); +} +template +inline void Logger::verbose(int vlevel, const T& log) { + acquireLock(); // released in Writer! + log_(el::Level::Verbose, vlevel, log); +} +# else +template +inline void Logger::verbose(int, const char*, const T&, const Args&...) { + return; +} +template +inline void Logger::verbose(int, const T&) { + return; +} +# endif // ELPP_VERBOSE_LOG +# define LOGGER_LEVEL_WRITERS(FUNCTION_NAME, LOG_LEVEL)\ +template \ +inline void Logger::FUNCTION_NAME(const char* s, const T& value, const Args&... args) {\ +log(LOG_LEVEL, s, value, args...);\ +}\ +template \ +inline void Logger::FUNCTION_NAME(const T& value) {\ +log(LOG_LEVEL, value);\ +} +# define LOGGER_LEVEL_WRITERS_DISABLED(FUNCTION_NAME, LOG_LEVEL)\ +template \ +inline void Logger::FUNCTION_NAME(const char*, const T&, const Args&...) {\ +return;\ +}\ +template \ +inline void Logger::FUNCTION_NAME(const T&) {\ +return;\ +} + +# if ELPP_INFO_LOG +LOGGER_LEVEL_WRITERS(info, Level::Info) +# else +LOGGER_LEVEL_WRITERS_DISABLED(info, Level::Info) +# endif // ELPP_INFO_LOG +# if ELPP_DEBUG_LOG +LOGGER_LEVEL_WRITERS(debug, Level::Debug) +# else +LOGGER_LEVEL_WRITERS_DISABLED(debug, Level::Debug) +# endif // ELPP_DEBUG_LOG +# if ELPP_WARNING_LOG +LOGGER_LEVEL_WRITERS(warn, Level::Warning) +# else +LOGGER_LEVEL_WRITERS_DISABLED(warn, Level::Warning) +# endif // ELPP_WARNING_LOG +# if ELPP_ERROR_LOG +LOGGER_LEVEL_WRITERS(error, Level::Error) +# else +LOGGER_LEVEL_WRITERS_DISABLED(error, Level::Error) +# endif // ELPP_ERROR_LOG +# if ELPP_FATAL_LOG +LOGGER_LEVEL_WRITERS(fatal, Level::Fatal) +# else +LOGGER_LEVEL_WRITERS_DISABLED(fatal, Level::Fatal) +# endif // ELPP_FATAL_LOG +# if ELPP_TRACE_LOG +LOGGER_LEVEL_WRITERS(trace, Level::Trace) +# else +LOGGER_LEVEL_WRITERS_DISABLED(trace, Level::Trace) +# endif // ELPP_TRACE_LOG +# undef LOGGER_LEVEL_WRITERS +# undef LOGGER_LEVEL_WRITERS_DISABLED +#endif // ELPP_VARIADIC_TEMPLATES_SUPPORTED +#if ELPP_COMPILER_MSVC +# define ELPP_VARIADIC_FUNC_MSVC(variadicFunction, variadicArgs) variadicFunction variadicArgs +# define ELPP_VARIADIC_FUNC_MSVC_RUN(variadicFunction, ...) ELPP_VARIADIC_FUNC_MSVC(variadicFunction, (__VA_ARGS__)) +# define el_getVALength(...) ELPP_VARIADIC_FUNC_MSVC_RUN(el_resolveVALength, 0, ## __VA_ARGS__,\ +10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0) +#else +# if ELPP_COMPILER_CLANG +# define el_getVALength(...) el_resolveVALength(0, __VA_ARGS__, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0) +# else +# define el_getVALength(...) el_resolveVALength(0, ## __VA_ARGS__, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0) +# endif // ELPP_COMPILER_CLANG +#endif // ELPP_COMPILER_MSVC +#define el_resolveVALength(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, N, ...) N +#define ELPP_WRITE_LOG(writer, level, dispatchAction, ...) \ +writer(level, __FILE__, __LINE__, ELPP_FUNC, dispatchAction).construct(el_getVALength(__VA_ARGS__), __VA_ARGS__) +#define ELPP_WRITE_LOG_IF(writer, condition, level, dispatchAction, ...) if (condition) \ +writer(level, __FILE__, __LINE__, ELPP_FUNC, dispatchAction).construct(el_getVALength(__VA_ARGS__), __VA_ARGS__) +#define ELPP_WRITE_LOG_EVERY_N(writer, occasion, level, dispatchAction, ...) \ +ELPP->validateEveryNCounter(__FILE__, __LINE__, occasion) && \ +writer(level, __FILE__, __LINE__, ELPP_FUNC, dispatchAction).construct(el_getVALength(__VA_ARGS__), __VA_ARGS__) +#define ELPP_WRITE_LOG_AFTER_N(writer, n, level, dispatchAction, ...) \ +ELPP->validateAfterNCounter(__FILE__, __LINE__, n) && \ +writer(level, __FILE__, __LINE__, ELPP_FUNC, dispatchAction).construct(el_getVALength(__VA_ARGS__), __VA_ARGS__) +#define ELPP_WRITE_LOG_N_TIMES(writer, n, level, dispatchAction, ...) \ +ELPP->validateNTimesCounter(__FILE__, __LINE__, n) && \ +writer(level, __FILE__, __LINE__, ELPP_FUNC, dispatchAction).construct(el_getVALength(__VA_ARGS__), __VA_ARGS__) +#if defined(ELPP_FEATURE_ALL) || defined(ELPP_FEATURE_PERFORMANCE_TRACKING) +class PerformanceTrackingData { + public: + enum class DataType : base::type::EnumType { + Checkpoint = 1, Complete = 2 + }; + // Do not use constructor, will run into multiple definition error, use init(PerformanceTracker*) + explicit PerformanceTrackingData(DataType dataType) : m_performanceTracker(nullptr), + m_dataType(dataType), m_firstCheckpoint(false), m_file(""), m_line(0), m_func("") {} + inline const std::string* blockName(void) const; + inline const struct timeval* startTime(void) const; + inline const struct timeval* endTime(void) const; + inline const struct timeval* lastCheckpointTime(void) const; + inline const base::PerformanceTracker* performanceTracker(void) const { + return m_performanceTracker; + } + inline PerformanceTrackingData::DataType dataType(void) const { + return m_dataType; + } + inline bool firstCheckpoint(void) const { + return m_firstCheckpoint; + } + inline std::string checkpointId(void) const { + return m_checkpointId; + } + inline const char* file(void) const { + return m_file; + } + inline base::type::LineNumber line(void) const { + return m_line; + } + inline const char* func(void) const { + return m_func; + } + inline const base::type::string_t* formattedTimeTaken() const { + return &m_formattedTimeTaken; + } + inline const std::string& loggerId(void) const; + private: + base::PerformanceTracker* m_performanceTracker; + base::type::string_t m_formattedTimeTaken; + PerformanceTrackingData::DataType m_dataType; + bool m_firstCheckpoint; + std::string m_checkpointId; + const char* m_file; + base::type::LineNumber m_line; + const char* m_func; + inline void init(base::PerformanceTracker* performanceTracker, bool firstCheckpoint = false) { + m_performanceTracker = performanceTracker; + m_firstCheckpoint = firstCheckpoint; + } + + friend class el::base::PerformanceTracker; +}; +namespace base { +/// @brief Represents performanceTracker block of code that conditionally adds performance status to log +/// either when goes outside the scope of when checkpoint() is called +class PerformanceTracker : public base::threading::ThreadSafe, public Loggable { + public: + PerformanceTracker(const std::string& blockName, + base::TimestampUnit timestampUnit = base::TimestampUnit::Millisecond, + const std::string& loggerId = std::string(el::base::consts::kPerformanceLoggerId), + bool scopedLog = true, Level level = base::consts::kPerformanceTrackerDefaultLevel); + /// @brief Copy constructor + PerformanceTracker(const PerformanceTracker& t) : + m_blockName(t.m_blockName), m_timestampUnit(t.m_timestampUnit), m_loggerId(t.m_loggerId), m_scopedLog(t.m_scopedLog), + m_level(t.m_level), m_hasChecked(t.m_hasChecked), m_lastCheckpointId(t.m_lastCheckpointId), m_enabled(t.m_enabled), + m_startTime(t.m_startTime), m_endTime(t.m_endTime), m_lastCheckpointTime(t.m_lastCheckpointTime) { + } + virtual ~PerformanceTracker(void); + /// @brief A checkpoint for current performanceTracker block. + void checkpoint(const std::string& id = std::string(), const char* file = __FILE__, + base::type::LineNumber line = __LINE__, + const char* func = ""); + inline Level level(void) const { + return m_level; + } + private: + std::string m_blockName; + base::TimestampUnit m_timestampUnit; + std::string m_loggerId; + bool m_scopedLog; + Level m_level; + bool m_hasChecked; + std::string m_lastCheckpointId; + bool m_enabled; + struct timeval m_startTime, m_endTime, m_lastCheckpointTime; + + PerformanceTracker(void); + + friend class el::PerformanceTrackingData; + friend class base::DefaultPerformanceTrackingCallback; + + const inline base::type::string_t getFormattedTimeTaken() const { + return getFormattedTimeTaken(m_startTime); + } + + const base::type::string_t getFormattedTimeTaken(struct timeval startTime) const; + + virtual inline void log(el::base::type::ostream_t& os) const { + os << getFormattedTimeTaken(); + } +}; +class DefaultPerformanceTrackingCallback : public PerformanceTrackingCallback { + protected: + void handle(const PerformanceTrackingData* data) { + m_data = data; + base::type::stringstream_t ss; + if (m_data->dataType() == PerformanceTrackingData::DataType::Complete) { + ss << ELPP_LITERAL("Executed [") << m_data->blockName()->c_str() << ELPP_LITERAL("] in [") << + *m_data->formattedTimeTaken() << ELPP_LITERAL("]"); + } else { + ss << ELPP_LITERAL("Performance checkpoint"); + if (!m_data->checkpointId().empty()) { + ss << ELPP_LITERAL(" [") << m_data->checkpointId().c_str() << ELPP_LITERAL("]"); + } + ss << ELPP_LITERAL(" for block [") << m_data->blockName()->c_str() << ELPP_LITERAL("] : [") << + *m_data->performanceTracker(); + if (!ELPP->hasFlag(LoggingFlag::DisablePerformanceTrackingCheckpointComparison) + && m_data->performanceTracker()->m_hasChecked) { + ss << ELPP_LITERAL(" ([") << *m_data->formattedTimeTaken() << ELPP_LITERAL("] from "); + if (m_data->performanceTracker()->m_lastCheckpointId.empty()) { + ss << ELPP_LITERAL("last checkpoint"); + } else { + ss << ELPP_LITERAL("checkpoint '") << m_data->performanceTracker()->m_lastCheckpointId.c_str() << ELPP_LITERAL("'"); + } + ss << ELPP_LITERAL(")]"); + } else { + ss << ELPP_LITERAL("]"); + } + } + el::base::Writer(m_data->performanceTracker()->level(), m_data->file(), m_data->line(), m_data->func()).construct(1, + m_data->loggerId().c_str()) << ss.str(); + } + private: + const PerformanceTrackingData* m_data; +}; +} // namespace base +inline const std::string* PerformanceTrackingData::blockName() const { + return const_cast(&m_performanceTracker->m_blockName); +} +inline const struct timeval* PerformanceTrackingData::startTime() const { + return const_cast(&m_performanceTracker->m_startTime); +} +inline const struct timeval* PerformanceTrackingData::endTime() const { + return const_cast(&m_performanceTracker->m_endTime); +} +inline const struct timeval* PerformanceTrackingData::lastCheckpointTime() const { + return const_cast(&m_performanceTracker->m_lastCheckpointTime); +} +inline const std::string& PerformanceTrackingData::loggerId(void) const { + return m_performanceTracker->m_loggerId; +} +#endif // defined(ELPP_FEATURE_ALL) || defined(ELPP_FEATURE_PERFORMANCE_TRACKING) +namespace base { +/// @brief Contains some internal debugging tools like crash handler and stack tracer +namespace debug { +#if defined(ELPP_FEATURE_ALL) || defined(ELPP_FEATURE_CRASH_LOG) +class StackTrace : base::NoCopy { + public: + static const unsigned int kMaxStack = 64; + static const unsigned int kStackStart = 2; // We want to skip c'tor and StackTrace::generateNew() + class StackTraceEntry { + public: + StackTraceEntry(std::size_t index, const std::string& loc, const std::string& demang, const std::string& hex, + const std::string& addr); + StackTraceEntry(std::size_t index, const std::string& loc) : + m_index(index), + m_location(loc) { + } + std::size_t m_index; + std::string m_location; + std::string m_demangled; + std::string m_hex; + std::string m_addr; + friend std::ostream& operator<<(std::ostream& ss, const StackTraceEntry& si); + + private: + StackTraceEntry(void); + }; + + StackTrace(void) { + generateNew(); + } + + virtual ~StackTrace(void) { + } + + inline std::vector& getLatestStack(void) { + return m_stack; + } + + friend std::ostream& operator<<(std::ostream& os, const StackTrace& st); + + private: + std::vector m_stack; + + void generateNew(void); +}; +/// @brief Handles unexpected crashes +class CrashHandler : base::NoCopy { + public: + typedef void (*Handler)(int); + + explicit CrashHandler(bool useDefault); + explicit CrashHandler(const Handler& cHandler) { + setHandler(cHandler); + } + void setHandler(const Handler& cHandler); + + private: + Handler m_handler; +}; +#else +class CrashHandler { + public: + explicit CrashHandler(bool) {} +}; +#endif // defined(ELPP_FEATURE_ALL) || defined(ELPP_FEATURE_CRASH_LOG) +} // namespace debug +} // namespace base +extern base::debug::CrashHandler elCrashHandler; +#define MAKE_LOGGABLE(ClassType, ClassInstance, OutputStreamInstance) \ +el::base::type::ostream_t& operator<<(el::base::type::ostream_t& OutputStreamInstance, const ClassType& ClassInstance) +/// @brief Initializes syslog with process ID, options and facility. calls closelog() on d'tor +class SysLogInitializer { + public: + SysLogInitializer(const char* processIdent, int options = 0, int facility = 0) { +#if defined(ELPP_SYSLOG) + (void)base::consts::kSysLogLoggerId; + openlog(processIdent, options, facility); +#else + ELPP_UNUSED(processIdent); + ELPP_UNUSED(options); + ELPP_UNUSED(facility); +#endif // defined(ELPP_SYSLOG) + } + virtual ~SysLogInitializer(void) { +#if defined(ELPP_SYSLOG) + closelog(); +#endif // defined(ELPP_SYSLOG) + } +}; +#define ELPP_INITIALIZE_SYSLOG(id, opt, fac) el::SysLogInitializer elSyslogInit(id, opt, fac) +/// @brief Static helpers for developers +class Helpers : base::StaticClass { + public: + /// @brief Shares logging repository (base::Storage) + static inline void setStorage(base::type::StoragePointer storage) { + ELPP = storage; + } + /// @return Main storage repository + static inline base::type::StoragePointer storage() { + return ELPP; + } + /// @brief Sets application arguments and figures out whats active for logging and whats not. + static inline void setArgs(int argc, char** argv) { + ELPP->setApplicationArguments(argc, argv); + } + /// @copydoc setArgs(int argc, char** argv) + static inline void setArgs(int argc, const char** argv) { + ELPP->setApplicationArguments(argc, const_cast(argv)); + } + /// @brief Sets thread name for current thread. Requires std::thread + static inline void setThreadName(const std::string& name) { + ELPP->setThreadName(name); + } + static inline std::string getThreadName() { + return ELPP->getThreadName(base::threading::getCurrentThreadId()); + } +#if defined(ELPP_FEATURE_ALL) || defined(ELPP_FEATURE_CRASH_LOG) + /// @brief Overrides default crash handler and installs custom handler. + /// @param crashHandler A functor with no return type that takes single int argument. + /// Handler is a typedef with specification: void (*Handler)(int) + static inline void setCrashHandler(const el::base::debug::CrashHandler::Handler& crashHandler) { + el::elCrashHandler.setHandler(crashHandler); + } + /// @brief Abort due to crash with signal in parameter + /// @param sig Crash signal + static void crashAbort(int sig, const char* sourceFile = "", unsigned int long line = 0); + /// @brief Logs reason of crash as per sig + /// @param sig Crash signal + /// @param stackTraceIfAvailable Includes stack trace if available + /// @param level Logging level + /// @param logger Logger to use for logging + static void logCrashReason(int sig, bool stackTraceIfAvailable = false, + Level level = Level::Fatal, const char* logger = base::consts::kDefaultLoggerId); +#endif // defined(ELPP_FEATURE_ALL) || defined(ELPP_FEATURE_CRASH_LOG) + /// @brief Installs pre rollout callback, this callback is triggered when log file is about to be rolled out + /// (can be useful for backing up) + static inline void installPreRollOutCallback(const PreRollOutCallback& callback) { + ELPP->setPreRollOutCallback(callback); + } + /// @brief Uninstalls pre rollout callback + static inline void uninstallPreRollOutCallback(void) { + ELPP->unsetPreRollOutCallback(); + } + /// @brief Installs post log dispatch callback, this callback is triggered when log is dispatched + template + static inline bool installLogDispatchCallback(const std::string& id) { + return ELPP->installLogDispatchCallback(id); + } + /// @brief Uninstalls log dispatch callback + template + static inline void uninstallLogDispatchCallback(const std::string& id) { + ELPP->uninstallLogDispatchCallback(id); + } + template + static inline T* logDispatchCallback(const std::string& id) { + return ELPP->logDispatchCallback(id); + } +#if defined(ELPP_FEATURE_ALL) || defined(ELPP_FEATURE_PERFORMANCE_TRACKING) + /// @brief Installs post performance tracking callback, this callback is triggered when performance tracking is finished + template + static inline bool installPerformanceTrackingCallback(const std::string& id) { + return ELPP->installPerformanceTrackingCallback(id); + } + /// @brief Uninstalls post performance tracking handler + template + static inline void uninstallPerformanceTrackingCallback(const std::string& id) { + ELPP->uninstallPerformanceTrackingCallback(id); + } + template + static inline T* performanceTrackingCallback(const std::string& id) { + return ELPP->performanceTrackingCallback(id); + } +#endif // defined(ELPP_FEATURE_ALL) || defined(ELPP_FEATURE_PERFORMANCE_TRACKING) + /// @brief Converts template to std::string - useful for loggable classes to log containers within log(std::ostream&) const + template + static std::string convertTemplateToStdString(const T& templ) { + el::Logger* logger = + ELPP->registeredLoggers()->get(el::base::consts::kDefaultLoggerId); + if (logger == nullptr) { + return std::string(); + } + base::MessageBuilder b; + b.initialize(logger); + logger->acquireLock(); + b << templ; +#if defined(ELPP_UNICODE) + std::string s = std::string(logger->stream().str().begin(), logger->stream().str().end()); +#else + std::string s = logger->stream().str(); +#endif // defined(ELPP_UNICODE) + logger->stream().str(ELPP_LITERAL("")); + logger->releaseLock(); + return s; + } + /// @brief Returns command line arguments (pointer) provided to easylogging++ + static inline const el::base::utils::CommandLineArgs* commandLineArgs(void) { + return ELPP->commandLineArgs(); + } + /// @brief Reserve space for custom format specifiers for performance + /// @see std::vector::reserve + static inline void reserveCustomFormatSpecifiers(std::size_t size) { + ELPP->m_customFormatSpecifiers.reserve(size); + } + /// @brief Installs user defined format specifier and handler + static inline void installCustomFormatSpecifier(const CustomFormatSpecifier& customFormatSpecifier) { + ELPP->installCustomFormatSpecifier(customFormatSpecifier); + } + /// @brief Uninstalls user defined format specifier and handler + static inline bool uninstallCustomFormatSpecifier(const char* formatSpecifier) { + return ELPP->uninstallCustomFormatSpecifier(formatSpecifier); + } + /// @brief Returns true if custom format specifier is installed + static inline bool hasCustomFormatSpecifier(const char* formatSpecifier) { + return ELPP->hasCustomFormatSpecifier(formatSpecifier); + } + static inline void validateFileRolling(Logger* logger, Level level) { + if (ELPP == nullptr || logger == nullptr) return; + logger->m_typedConfigurations->validateFileRolling(level, ELPP->preRollOutCallback()); + } +}; +/// @brief Static helpers to deal with loggers and their configurations +class Loggers : base::StaticClass { + public: + /// @brief Gets existing or registers new logger + static Logger* getLogger(const std::string& identity, bool registerIfNotAvailable = true); + /// @brief Changes default log builder for future loggers + static void setDefaultLogBuilder(el::LogBuilderPtr& logBuilderPtr); + /// @brief Installs logger registration callback, this callback is triggered when new logger is registered + template + static inline bool installLoggerRegistrationCallback(const std::string& id) { + return ELPP->registeredLoggers()->installLoggerRegistrationCallback(id); + } + /// @brief Uninstalls log dispatch callback + template + static inline void uninstallLoggerRegistrationCallback(const std::string& id) { + ELPP->registeredLoggers()->uninstallLoggerRegistrationCallback(id); + } + template + static inline T* loggerRegistrationCallback(const std::string& id) { + return ELPP->registeredLoggers()->loggerRegistrationCallback(id); + } + /// @brief Unregisters logger - use it only when you know what you are doing, you may unregister + /// loggers initialized / used by third-party libs. + static bool unregisterLogger(const std::string& identity); + /// @brief Whether or not logger with id is registered + static bool hasLogger(const std::string& identity); + /// @brief Reconfigures specified logger with new configurations + static Logger* reconfigureLogger(Logger* logger, const Configurations& configurations); + /// @brief Reconfigures logger with new configurations after looking it up using identity + static Logger* reconfigureLogger(const std::string& identity, const Configurations& configurations); + /// @brief Reconfigures logger's single configuration + static Logger* reconfigureLogger(const std::string& identity, ConfigurationType configurationType, + const std::string& value); + /// @brief Reconfigures all the existing loggers with new configurations + static void reconfigureAllLoggers(const Configurations& configurations); + /// @brief Reconfigures single configuration for all the loggers + static inline void reconfigureAllLoggers(ConfigurationType configurationType, const std::string& value) { + reconfigureAllLoggers(Level::Global, configurationType, value); + } + /// @brief Reconfigures single configuration for all the loggers for specified level + static void reconfigureAllLoggers(Level level, ConfigurationType configurationType, + const std::string& value); + /// @brief Sets default configurations. This configuration is used for future (and conditionally for existing) loggers + static void setDefaultConfigurations(const Configurations& configurations, + bool reconfigureExistingLoggers = false); + /// @brief Returns current default + static const Configurations* defaultConfigurations(void); + /// @brief Returns log stream reference pointer if needed by user + static const base::LogStreamsReferenceMapPtr logStreamsReference(void); + /// @brief Default typed configuration based on existing defaultConf + static base::TypedConfigurations defaultTypedConfigurations(void); + /// @brief Populates all logger IDs in current repository. + /// @param [out] targetList List of fill up. + static std::vector* populateAllLoggerIds(std::vector* targetList); + /// @brief Sets configurations from global configuration file. + static void configureFromGlobal(const char* globalConfigurationFilePath); + /// @brief Configures loggers using command line arg. Ensure you have already set command line args, + /// @return False if invalid argument or argument with no value provided, true if attempted to configure logger. + /// If true is returned that does not mean it has been configured successfully, it only means that it + /// has attempted to configure logger using configuration file provided in argument + static bool configureFromArg(const char* argKey); + /// @brief Flushes all loggers for all levels - Be careful if you dont know how many loggers are registered + static void flushAll(void); + /// @brief Adds logging flag used internally. + static inline void addFlag(LoggingFlag flag) { + ELPP->addFlag(flag); + } + /// @brief Removes logging flag used internally. + static inline void removeFlag(LoggingFlag flag) { + ELPP->removeFlag(flag); + } + /// @brief Determines whether or not certain flag is active + static inline bool hasFlag(LoggingFlag flag) { + return ELPP->hasFlag(flag); + } + /// @brief Adds flag and removes it when scope goes out + class ScopedAddFlag { + public: + ScopedAddFlag(LoggingFlag flag) : m_flag(flag) { + Loggers::addFlag(m_flag); + } + ~ScopedAddFlag(void) { + Loggers::removeFlag(m_flag); + } + private: + LoggingFlag m_flag; + }; + /// @brief Removes flag and add it when scope goes out + class ScopedRemoveFlag { + public: + ScopedRemoveFlag(LoggingFlag flag) : m_flag(flag) { + Loggers::removeFlag(m_flag); + } + ~ScopedRemoveFlag(void) { + Loggers::addFlag(m_flag); + } + private: + LoggingFlag m_flag; + }; + /// @brief Sets hierarchy for logging. Needs to enable logging flag (HierarchicalLogging) + static void setLoggingLevel(Level level) { + ELPP->setLoggingLevel(level); + } + /// @brief Sets verbose level on the fly + static void setVerboseLevel(base::type::VerboseLevel level); + /// @brief Gets current verbose level + static base::type::VerboseLevel verboseLevel(void); + /// @brief Sets vmodules as specified (on the fly) + static void setVModules(const char* modules); + /// @brief Clears vmodules + static void clearVModules(void); +}; +class VersionInfo : base::StaticClass { + public: + /// @brief Current version number + static const std::string version(void); + + /// @brief Release date of current version + static const std::string releaseDate(void); +}; +} // namespace el +#undef VLOG_IS_ON +/// @brief Determines whether verbose logging is on for specified level current file. +#define VLOG_IS_ON(verboseLevel) (ELPP->vRegistry()->allowed(verboseLevel, __FILE__)) +#undef TIMED_BLOCK +#undef TIMED_SCOPE +#undef TIMED_SCOPE_IF +#undef TIMED_FUNC +#undef TIMED_FUNC_IF +#undef ELPP_MIN_UNIT +#if defined(ELPP_PERFORMANCE_MICROSECONDS) +# define ELPP_MIN_UNIT el::base::TimestampUnit::Microsecond +#else +# define ELPP_MIN_UNIT el::base::TimestampUnit::Millisecond +#endif // (defined(ELPP_PERFORMANCE_MICROSECONDS)) +/// @brief Performance tracked scope. Performance gets written when goes out of scope using +/// 'performance' logger. +/// +/// @detail Please note in order to check the performance at a certain time you can use obj->checkpoint(); +/// @see el::base::PerformanceTracker +/// @see el::base::PerformanceTracker::checkpoint +// Note: Do not surround this definition with null macro because of obj instance +#define TIMED_SCOPE_IF(obj, blockname, condition) el::base::type::PerformanceTrackerPtr obj( condition ? \ + new el::base::PerformanceTracker(blockname, ELPP_MIN_UNIT) : nullptr ) +#define TIMED_SCOPE(obj, blockname) TIMED_SCOPE_IF(obj, blockname, true) +#define TIMED_BLOCK(obj, blockName) for (struct { int i; el::base::type::PerformanceTrackerPtr timer; } obj = { 0, \ + el::base::type::PerformanceTrackerPtr(new el::base::PerformanceTracker(blockName, ELPP_MIN_UNIT)) }; obj.i < 1; ++obj.i) +/// @brief Performance tracked function. Performance gets written when goes out of scope using +/// 'performance' logger. +/// +/// @detail Please note in order to check the performance at a certain time you can use obj->checkpoint(); +/// @see el::base::PerformanceTracker +/// @see el::base::PerformanceTracker::checkpoint +#define TIMED_FUNC_IF(obj,condition) TIMED_SCOPE_IF(obj, ELPP_FUNC, condition) +#define TIMED_FUNC(obj) TIMED_SCOPE(obj, ELPP_FUNC) +#undef PERFORMANCE_CHECKPOINT +#undef PERFORMANCE_CHECKPOINT_WITH_ID +#define PERFORMANCE_CHECKPOINT(obj) obj->checkpoint(std::string(), __FILE__, __LINE__, ELPP_FUNC) +#define PERFORMANCE_CHECKPOINT_WITH_ID(obj, id) obj->checkpoint(id, __FILE__, __LINE__, ELPP_FUNC) +#undef ELPP_COUNTER +#undef ELPP_COUNTER_POS +/// @brief Gets hit counter for file/line +#define ELPP_COUNTER (ELPP->hitCounters()->getCounter(__FILE__, __LINE__)) +/// @brief Gets hit counter position for file/line, -1 if not registered yet +#define ELPP_COUNTER_POS (ELPP_COUNTER == nullptr ? -1 : ELPP_COUNTER->hitCounts()) +// Undef levels to support LOG(LEVEL) +#undef INFO +#undef WARNING +#undef DEBUG +#undef ERROR +#undef FATAL +#undef TRACE +#undef VERBOSE +// Undef existing +#undef CINFO +#undef CWARNING +#undef CDEBUG +#undef CFATAL +#undef CERROR +#undef CTRACE +#undef CVERBOSE +#undef CINFO_IF +#undef CWARNING_IF +#undef CDEBUG_IF +#undef CERROR_IF +#undef CFATAL_IF +#undef CTRACE_IF +#undef CVERBOSE_IF +#undef CINFO_EVERY_N +#undef CWARNING_EVERY_N +#undef CDEBUG_EVERY_N +#undef CERROR_EVERY_N +#undef CFATAL_EVERY_N +#undef CTRACE_EVERY_N +#undef CVERBOSE_EVERY_N +#undef CINFO_AFTER_N +#undef CWARNING_AFTER_N +#undef CDEBUG_AFTER_N +#undef CERROR_AFTER_N +#undef CFATAL_AFTER_N +#undef CTRACE_AFTER_N +#undef CVERBOSE_AFTER_N +#undef CINFO_N_TIMES +#undef CWARNING_N_TIMES +#undef CDEBUG_N_TIMES +#undef CERROR_N_TIMES +#undef CFATAL_N_TIMES +#undef CTRACE_N_TIMES +#undef CVERBOSE_N_TIMES +// Normal logs +#if ELPP_INFO_LOG +# define CINFO(writer, dispatchAction, ...) ELPP_WRITE_LOG(writer, el::Level::Info, dispatchAction, __VA_ARGS__) +#else +# define CINFO(writer, dispatchAction, ...) el::base::NullWriter() +#endif // ELPP_INFO_LOG +#if ELPP_WARNING_LOG +# define CWARNING(writer, dispatchAction, ...) ELPP_WRITE_LOG(writer, el::Level::Warning, dispatchAction, __VA_ARGS__) +#else +# define CWARNING(writer, dispatchAction, ...) el::base::NullWriter() +#endif // ELPP_WARNING_LOG +#if ELPP_DEBUG_LOG +# define CDEBUG(writer, dispatchAction, ...) ELPP_WRITE_LOG(writer, el::Level::Debug, dispatchAction, __VA_ARGS__) +#else +# define CDEBUG(writer, dispatchAction, ...) el::base::NullWriter() +#endif // ELPP_DEBUG_LOG +#if ELPP_ERROR_LOG +# define CERROR(writer, dispatchAction, ...) ELPP_WRITE_LOG(writer, el::Level::Error, dispatchAction, __VA_ARGS__) +#else +# define CERROR(writer, dispatchAction, ...) el::base::NullWriter() +#endif // ELPP_ERROR_LOG +#if ELPP_FATAL_LOG +# define CFATAL(writer, dispatchAction, ...) ELPP_WRITE_LOG(writer, el::Level::Fatal, dispatchAction, __VA_ARGS__) +#else +# define CFATAL(writer, dispatchAction, ...) el::base::NullWriter() +#endif // ELPP_FATAL_LOG +#if ELPP_TRACE_LOG +# define CTRACE(writer, dispatchAction, ...) ELPP_WRITE_LOG(writer, el::Level::Trace, dispatchAction, __VA_ARGS__) +#else +# define CTRACE(writer, dispatchAction, ...) el::base::NullWriter() +#endif // ELPP_TRACE_LOG +#if ELPP_VERBOSE_LOG +# define CVERBOSE(writer, vlevel, dispatchAction, ...) if (VLOG_IS_ON(vlevel)) writer(\ +el::Level::Verbose, __FILE__, __LINE__, ELPP_FUNC, dispatchAction, vlevel).construct(el_getVALength(__VA_ARGS__), __VA_ARGS__) +#else +# define CVERBOSE(writer, vlevel, dispatchAction, ...) el::base::NullWriter() +#endif // ELPP_VERBOSE_LOG +// Conditional logs +#if ELPP_INFO_LOG +# define CINFO_IF(writer, condition_, dispatchAction, ...) \ +ELPP_WRITE_LOG_IF(writer, (condition_), el::Level::Info, dispatchAction, __VA_ARGS__) +#else +# define CINFO_IF(writer, condition_, dispatchAction, ...) el::base::NullWriter() +#endif // ELPP_INFO_LOG +#if ELPP_WARNING_LOG +# define CWARNING_IF(writer, condition_, dispatchAction, ...)\ +ELPP_WRITE_LOG_IF(writer, (condition_), el::Level::Warning, dispatchAction, __VA_ARGS__) +#else +# define CWARNING_IF(writer, condition_, dispatchAction, ...) el::base::NullWriter() +#endif // ELPP_WARNING_LOG +#if ELPP_DEBUG_LOG +# define CDEBUG_IF(writer, condition_, dispatchAction, ...)\ +ELPP_WRITE_LOG_IF(writer, (condition_), el::Level::Debug, dispatchAction, __VA_ARGS__) +#else +# define CDEBUG_IF(writer, condition_, dispatchAction, ...) el::base::NullWriter() +#endif // ELPP_DEBUG_LOG +#if ELPP_ERROR_LOG +# define CERROR_IF(writer, condition_, dispatchAction, ...)\ +ELPP_WRITE_LOG_IF(writer, (condition_), el::Level::Error, dispatchAction, __VA_ARGS__) +#else +# define CERROR_IF(writer, condition_, dispatchAction, ...) el::base::NullWriter() +#endif // ELPP_ERROR_LOG +#if ELPP_FATAL_LOG +# define CFATAL_IF(writer, condition_, dispatchAction, ...)\ +ELPP_WRITE_LOG_IF(writer, (condition_), el::Level::Fatal, dispatchAction, __VA_ARGS__) +#else +# define CFATAL_IF(writer, condition_, dispatchAction, ...) el::base::NullWriter() +#endif // ELPP_FATAL_LOG +#if ELPP_TRACE_LOG +# define CTRACE_IF(writer, condition_, dispatchAction, ...)\ +ELPP_WRITE_LOG_IF(writer, (condition_), el::Level::Trace, dispatchAction, __VA_ARGS__) +#else +# define CTRACE_IF(writer, condition_, dispatchAction, ...) el::base::NullWriter() +#endif // ELPP_TRACE_LOG +#if ELPP_VERBOSE_LOG +# define CVERBOSE_IF(writer, condition_, vlevel, dispatchAction, ...) if (VLOG_IS_ON(vlevel) && (condition_)) writer( \ +el::Level::Verbose, __FILE__, __LINE__, ELPP_FUNC, dispatchAction, vlevel).construct(el_getVALength(__VA_ARGS__), __VA_ARGS__) +#else +# define CVERBOSE_IF(writer, condition_, vlevel, dispatchAction, ...) el::base::NullWriter() +#endif // ELPP_VERBOSE_LOG +// Occasional logs +#if ELPP_INFO_LOG +# define CINFO_EVERY_N(writer, occasion, dispatchAction, ...)\ +ELPP_WRITE_LOG_EVERY_N(writer, occasion, el::Level::Info, dispatchAction, __VA_ARGS__) +#else +# define CINFO_EVERY_N(writer, occasion, dispatchAction, ...) el::base::NullWriter() +#endif // ELPP_INFO_LOG +#if ELPP_WARNING_LOG +# define CWARNING_EVERY_N(writer, occasion, dispatchAction, ...)\ +ELPP_WRITE_LOG_EVERY_N(writer, occasion, el::Level::Warning, dispatchAction, __VA_ARGS__) +#else +# define CWARNING_EVERY_N(writer, occasion, dispatchAction, ...) el::base::NullWriter() +#endif // ELPP_WARNING_LOG +#if ELPP_DEBUG_LOG +# define CDEBUG_EVERY_N(writer, occasion, dispatchAction, ...)\ +ELPP_WRITE_LOG_EVERY_N(writer, occasion, el::Level::Debug, dispatchAction, __VA_ARGS__) +#else +# define CDEBUG_EVERY_N(writer, occasion, dispatchAction, ...) el::base::NullWriter() +#endif // ELPP_DEBUG_LOG +#if ELPP_ERROR_LOG +# define CERROR_EVERY_N(writer, occasion, dispatchAction, ...)\ +ELPP_WRITE_LOG_EVERY_N(writer, occasion, el::Level::Error, dispatchAction, __VA_ARGS__) +#else +# define CERROR_EVERY_N(writer, occasion, dispatchAction, ...) el::base::NullWriter() +#endif // ELPP_ERROR_LOG +#if ELPP_FATAL_LOG +# define CFATAL_EVERY_N(writer, occasion, dispatchAction, ...)\ +ELPP_WRITE_LOG_EVERY_N(writer, occasion, el::Level::Fatal, dispatchAction, __VA_ARGS__) +#else +# define CFATAL_EVERY_N(writer, occasion, dispatchAction, ...) el::base::NullWriter() +#endif // ELPP_FATAL_LOG +#if ELPP_TRACE_LOG +# define CTRACE_EVERY_N(writer, occasion, dispatchAction, ...)\ +ELPP_WRITE_LOG_EVERY_N(writer, occasion, el::Level::Trace, dispatchAction, __VA_ARGS__) +#else +# define CTRACE_EVERY_N(writer, occasion, dispatchAction, ...) el::base::NullWriter() +#endif // ELPP_TRACE_LOG +#if ELPP_VERBOSE_LOG +# define CVERBOSE_EVERY_N(writer, occasion, vlevel, dispatchAction, ...)\ +CVERBOSE_IF(writer, ELPP->validateEveryNCounter(__FILE__, __LINE__, occasion), vlevel, dispatchAction, __VA_ARGS__) +#else +# define CVERBOSE_EVERY_N(writer, occasion, vlevel, dispatchAction, ...) el::base::NullWriter() +#endif // ELPP_VERBOSE_LOG +// After N logs +#if ELPP_INFO_LOG +# define CINFO_AFTER_N(writer, n, dispatchAction, ...)\ +ELPP_WRITE_LOG_AFTER_N(writer, n, el::Level::Info, dispatchAction, __VA_ARGS__) +#else +# define CINFO_AFTER_N(writer, n, dispatchAction, ...) el::base::NullWriter() +#endif // ELPP_INFO_LOG +#if ELPP_WARNING_LOG +# define CWARNING_AFTER_N(writer, n, dispatchAction, ...)\ +ELPP_WRITE_LOG_AFTER_N(writer, n, el::Level::Warning, dispatchAction, __VA_ARGS__) +#else +# define CWARNING_AFTER_N(writer, n, dispatchAction, ...) el::base::NullWriter() +#endif // ELPP_WARNING_LOG +#if ELPP_DEBUG_LOG +# define CDEBUG_AFTER_N(writer, n, dispatchAction, ...)\ +ELPP_WRITE_LOG_AFTER_N(writer, n, el::Level::Debug, dispatchAction, __VA_ARGS__) +#else +# define CDEBUG_AFTER_N(writer, n, dispatchAction, ...) el::base::NullWriter() +#endif // ELPP_DEBUG_LOG +#if ELPP_ERROR_LOG +# define CERROR_AFTER_N(writer, n, dispatchAction, ...)\ +ELPP_WRITE_LOG_AFTER_N(writer, n, el::Level::Error, dispatchAction, __VA_ARGS__) +#else +# define CERROR_AFTER_N(writer, n, dispatchAction, ...) el::base::NullWriter() +#endif // ELPP_ERROR_LOG +#if ELPP_FATAL_LOG +# define CFATAL_AFTER_N(writer, n, dispatchAction, ...)\ +ELPP_WRITE_LOG_AFTER_N(writer, n, el::Level::Fatal, dispatchAction, __VA_ARGS__) +#else +# define CFATAL_AFTER_N(writer, n, dispatchAction, ...) el::base::NullWriter() +#endif // ELPP_FATAL_LOG +#if ELPP_TRACE_LOG +# define CTRACE_AFTER_N(writer, n, dispatchAction, ...)\ +ELPP_WRITE_LOG_AFTER_N(writer, n, el::Level::Trace, dispatchAction, __VA_ARGS__) +#else +# define CTRACE_AFTER_N(writer, n, dispatchAction, ...) el::base::NullWriter() +#endif // ELPP_TRACE_LOG +#if ELPP_VERBOSE_LOG +# define CVERBOSE_AFTER_N(writer, n, vlevel, dispatchAction, ...)\ +CVERBOSE_IF(writer, ELPP->validateAfterNCounter(__FILE__, __LINE__, n), vlevel, dispatchAction, __VA_ARGS__) +#else +# define CVERBOSE_AFTER_N(writer, n, vlevel, dispatchAction, ...) el::base::NullWriter() +#endif // ELPP_VERBOSE_LOG +// N Times logs +#if ELPP_INFO_LOG +# define CINFO_N_TIMES(writer, n, dispatchAction, ...)\ +ELPP_WRITE_LOG_N_TIMES(writer, n, el::Level::Info, dispatchAction, __VA_ARGS__) +#else +# define CINFO_N_TIMES(writer, n, dispatchAction, ...) el::base::NullWriter() +#endif // ELPP_INFO_LOG +#if ELPP_WARNING_LOG +# define CWARNING_N_TIMES(writer, n, dispatchAction, ...)\ +ELPP_WRITE_LOG_N_TIMES(writer, n, el::Level::Warning, dispatchAction, __VA_ARGS__) +#else +# define CWARNING_N_TIMES(writer, n, dispatchAction, ...) el::base::NullWriter() +#endif // ELPP_WARNING_LOG +#if ELPP_DEBUG_LOG +# define CDEBUG_N_TIMES(writer, n, dispatchAction, ...)\ +ELPP_WRITE_LOG_N_TIMES(writer, n, el::Level::Debug, dispatchAction, __VA_ARGS__) +#else +# define CDEBUG_N_TIMES(writer, n, dispatchAction, ...) el::base::NullWriter() +#endif // ELPP_DEBUG_LOG +#if ELPP_ERROR_LOG +# define CERROR_N_TIMES(writer, n, dispatchAction, ...)\ +ELPP_WRITE_LOG_N_TIMES(writer, n, el::Level::Error, dispatchAction, __VA_ARGS__) +#else +# define CERROR_N_TIMES(writer, n, dispatchAction, ...) el::base::NullWriter() +#endif // ELPP_ERROR_LOG +#if ELPP_FATAL_LOG +# define CFATAL_N_TIMES(writer, n, dispatchAction, ...)\ +ELPP_WRITE_LOG_N_TIMES(writer, n, el::Level::Fatal, dispatchAction, __VA_ARGS__) +#else +# define CFATAL_N_TIMES(writer, n, dispatchAction, ...) el::base::NullWriter() +#endif // ELPP_FATAL_LOG +#if ELPP_TRACE_LOG +# define CTRACE_N_TIMES(writer, n, dispatchAction, ...)\ +ELPP_WRITE_LOG_N_TIMES(writer, n, el::Level::Trace, dispatchAction, __VA_ARGS__) +#else +# define CTRACE_N_TIMES(writer, n, dispatchAction, ...) el::base::NullWriter() +#endif // ELPP_TRACE_LOG +#if ELPP_VERBOSE_LOG +# define CVERBOSE_N_TIMES(writer, n, vlevel, dispatchAction, ...)\ +CVERBOSE_IF(writer, ELPP->validateNTimesCounter(__FILE__, __LINE__, n), vlevel, dispatchAction, __VA_ARGS__) +#else +# define CVERBOSE_N_TIMES(writer, n, vlevel, dispatchAction, ...) el::base::NullWriter() +#endif // ELPP_VERBOSE_LOG +// +// Custom Loggers - Requires (level, dispatchAction, loggerId/s) +// +// undef existing +#undef CLOG +#undef CLOG_VERBOSE +#undef CVLOG +#undef CLOG_IF +#undef CLOG_VERBOSE_IF +#undef CVLOG_IF +#undef CLOG_EVERY_N +#undef CVLOG_EVERY_N +#undef CLOG_AFTER_N +#undef CVLOG_AFTER_N +#undef CLOG_N_TIMES +#undef CVLOG_N_TIMES +// Normal logs +#define CLOG(LEVEL, ...)\ +C##LEVEL(el::base::Writer, el::base::DispatchAction::NormalLog, __VA_ARGS__) +#define CVLOG(vlevel, ...) CVERBOSE(el::base::Writer, vlevel, el::base::DispatchAction::NormalLog, __VA_ARGS__) +// Conditional logs +#define CLOG_IF(condition, LEVEL, ...)\ +C##LEVEL##_IF(el::base::Writer, condition, el::base::DispatchAction::NormalLog, __VA_ARGS__) +#define CVLOG_IF(condition, vlevel, ...)\ +CVERBOSE_IF(el::base::Writer, condition, vlevel, el::base::DispatchAction::NormalLog, __VA_ARGS__) +// Hit counts based logs +#define CLOG_EVERY_N(n, LEVEL, ...)\ +C##LEVEL##_EVERY_N(el::base::Writer, n, el::base::DispatchAction::NormalLog, __VA_ARGS__) +#define CVLOG_EVERY_N(n, vlevel, ...)\ +CVERBOSE_EVERY_N(el::base::Writer, n, vlevel, el::base::DispatchAction::NormalLog, __VA_ARGS__) +#define CLOG_AFTER_N(n, LEVEL, ...)\ +C##LEVEL##_AFTER_N(el::base::Writer, n, el::base::DispatchAction::NormalLog, __VA_ARGS__) +#define CVLOG_AFTER_N(n, vlevel, ...)\ +CVERBOSE_AFTER_N(el::base::Writer, n, vlevel, el::base::DispatchAction::NormalLog, __VA_ARGS__) +#define CLOG_N_TIMES(n, LEVEL, ...)\ +C##LEVEL##_N_TIMES(el::base::Writer, n, el::base::DispatchAction::NormalLog, __VA_ARGS__) +#define CVLOG_N_TIMES(n, vlevel, ...)\ +CVERBOSE_N_TIMES(el::base::Writer, n, vlevel, el::base::DispatchAction::NormalLog, __VA_ARGS__) +// +// Default Loggers macro using CLOG(), CLOG_VERBOSE() and CVLOG() macros +// +// undef existing +#undef LOG +#undef VLOG +#undef LOG_IF +#undef VLOG_IF +#undef LOG_EVERY_N +#undef VLOG_EVERY_N +#undef LOG_AFTER_N +#undef VLOG_AFTER_N +#undef LOG_N_TIMES +#undef VLOG_N_TIMES +#undef ELPP_CURR_FILE_LOGGER_ID +#if defined(ELPP_DEFAULT_LOGGER) +# define ELPP_CURR_FILE_LOGGER_ID ELPP_DEFAULT_LOGGER +#else +# define ELPP_CURR_FILE_LOGGER_ID el::base::consts::kDefaultLoggerId +#endif +#undef ELPP_TRACE +#define ELPP_TRACE CLOG(TRACE, ELPP_CURR_FILE_LOGGER_ID) +// Normal logs +#define LOG(LEVEL) CLOG(LEVEL, ELPP_CURR_FILE_LOGGER_ID) +#define VLOG(vlevel) CVLOG(vlevel, ELPP_CURR_FILE_LOGGER_ID) +// Conditional logs +#define LOG_IF(condition, LEVEL) CLOG_IF(condition, LEVEL, ELPP_CURR_FILE_LOGGER_ID) +#define VLOG_IF(condition, vlevel) CVLOG_IF(condition, vlevel, ELPP_CURR_FILE_LOGGER_ID) +// Hit counts based logs +#define LOG_EVERY_N(n, LEVEL) CLOG_EVERY_N(n, LEVEL, ELPP_CURR_FILE_LOGGER_ID) +#define VLOG_EVERY_N(n, vlevel) CVLOG_EVERY_N(n, vlevel, ELPP_CURR_FILE_LOGGER_ID) +#define LOG_AFTER_N(n, LEVEL) CLOG_AFTER_N(n, LEVEL, ELPP_CURR_FILE_LOGGER_ID) +#define VLOG_AFTER_N(n, vlevel) CVLOG_AFTER_N(n, vlevel, ELPP_CURR_FILE_LOGGER_ID) +#define LOG_N_TIMES(n, LEVEL) CLOG_N_TIMES(n, LEVEL, ELPP_CURR_FILE_LOGGER_ID) +#define VLOG_N_TIMES(n, vlevel) CVLOG_N_TIMES(n, vlevel, ELPP_CURR_FILE_LOGGER_ID) +// Generic PLOG() +#undef CPLOG +#undef CPLOG_IF +#undef PLOG +#undef PLOG_IF +#undef DCPLOG +#undef DCPLOG_IF +#undef DPLOG +#undef DPLOG_IF +#define CPLOG(LEVEL, ...)\ +C##LEVEL(el::base::PErrorWriter, el::base::DispatchAction::NormalLog, __VA_ARGS__) +#define CPLOG_IF(condition, LEVEL, ...)\ +C##LEVEL##_IF(el::base::PErrorWriter, condition, el::base::DispatchAction::NormalLog, __VA_ARGS__) +#define DCPLOG(LEVEL, ...)\ +if (ELPP_DEBUG_LOG) C##LEVEL(el::base::PErrorWriter, el::base::DispatchAction::NormalLog, __VA_ARGS__) +#define DCPLOG_IF(condition, LEVEL, ...)\ +C##LEVEL##_IF(el::base::PErrorWriter, (ELPP_DEBUG_LOG) && (condition), el::base::DispatchAction::NormalLog, __VA_ARGS__) +#define PLOG(LEVEL) CPLOG(LEVEL, ELPP_CURR_FILE_LOGGER_ID) +#define PLOG_IF(condition, LEVEL) CPLOG_IF(condition, LEVEL, ELPP_CURR_FILE_LOGGER_ID) +#define DPLOG(LEVEL) DCPLOG(LEVEL, ELPP_CURR_FILE_LOGGER_ID) +#define DPLOG_IF(condition, LEVEL) DCPLOG_IF(condition, LEVEL, ELPP_CURR_FILE_LOGGER_ID) +// Generic SYSLOG() +#undef CSYSLOG +#undef CSYSLOG_IF +#undef CSYSLOG_EVERY_N +#undef CSYSLOG_AFTER_N +#undef CSYSLOG_N_TIMES +#undef SYSLOG +#undef SYSLOG_IF +#undef SYSLOG_EVERY_N +#undef SYSLOG_AFTER_N +#undef SYSLOG_N_TIMES +#undef DCSYSLOG +#undef DCSYSLOG_IF +#undef DCSYSLOG_EVERY_N +#undef DCSYSLOG_AFTER_N +#undef DCSYSLOG_N_TIMES +#undef DSYSLOG +#undef DSYSLOG_IF +#undef DSYSLOG_EVERY_N +#undef DSYSLOG_AFTER_N +#undef DSYSLOG_N_TIMES +#if defined(ELPP_SYSLOG) +# define CSYSLOG(LEVEL, ...)\ +C##LEVEL(el::base::Writer, el::base::DispatchAction::SysLog, __VA_ARGS__) +# define CSYSLOG_IF(condition, LEVEL, ...)\ +C##LEVEL##_IF(el::base::Writer, condition, el::base::DispatchAction::SysLog, __VA_ARGS__) +# define CSYSLOG_EVERY_N(n, LEVEL, ...) C##LEVEL##_EVERY_N(el::base::Writer, n, el::base::DispatchAction::SysLog, __VA_ARGS__) +# define CSYSLOG_AFTER_N(n, LEVEL, ...) C##LEVEL##_AFTER_N(el::base::Writer, n, el::base::DispatchAction::SysLog, __VA_ARGS__) +# define CSYSLOG_N_TIMES(n, LEVEL, ...) C##LEVEL##_N_TIMES(el::base::Writer, n, el::base::DispatchAction::SysLog, __VA_ARGS__) +# define SYSLOG(LEVEL) CSYSLOG(LEVEL, el::base::consts::kSysLogLoggerId) +# define SYSLOG_IF(condition, LEVEL) CSYSLOG_IF(condition, LEVEL, el::base::consts::kSysLogLoggerId) +# define SYSLOG_EVERY_N(n, LEVEL) CSYSLOG_EVERY_N(n, LEVEL, el::base::consts::kSysLogLoggerId) +# define SYSLOG_AFTER_N(n, LEVEL) CSYSLOG_AFTER_N(n, LEVEL, el::base::consts::kSysLogLoggerId) +# define SYSLOG_N_TIMES(n, LEVEL) CSYSLOG_N_TIMES(n, LEVEL, el::base::consts::kSysLogLoggerId) +# define DCSYSLOG(LEVEL, ...) if (ELPP_DEBUG_LOG) C##LEVEL(el::base::Writer, el::base::DispatchAction::SysLog, __VA_ARGS__) +# define DCSYSLOG_IF(condition, LEVEL, ...)\ +C##LEVEL##_IF(el::base::Writer, (ELPP_DEBUG_LOG) && (condition), el::base::DispatchAction::SysLog, __VA_ARGS__) +# define DCSYSLOG_EVERY_N(n, LEVEL, ...)\ +if (ELPP_DEBUG_LOG) C##LEVEL##_EVERY_N(el::base::Writer, n, el::base::DispatchAction::SysLog, __VA_ARGS__) +# define DCSYSLOG_AFTER_N(n, LEVEL, ...)\ +if (ELPP_DEBUG_LOG) C##LEVEL##_AFTER_N(el::base::Writer, n, el::base::DispatchAction::SysLog, __VA_ARGS__) +# define DCSYSLOG_N_TIMES(n, LEVEL, ...)\ +if (ELPP_DEBUG_LOG) C##LEVEL##_EVERY_N(el::base::Writer, n, el::base::DispatchAction::SysLog, __VA_ARGS__) +# define DSYSLOG(LEVEL) DCSYSLOG(LEVEL, el::base::consts::kSysLogLoggerId) +# define DSYSLOG_IF(condition, LEVEL) DCSYSLOG_IF(condition, LEVEL, el::base::consts::kSysLogLoggerId) +# define DSYSLOG_EVERY_N(n, LEVEL) DCSYSLOG_EVERY_N(n, LEVEL, el::base::consts::kSysLogLoggerId) +# define DSYSLOG_AFTER_N(n, LEVEL) DCSYSLOG_AFTER_N(n, LEVEL, el::base::consts::kSysLogLoggerId) +# define DSYSLOG_N_TIMES(n, LEVEL) DCSYSLOG_N_TIMES(n, LEVEL, el::base::consts::kSysLogLoggerId) +#else +# define CSYSLOG(LEVEL, ...) el::base::NullWriter() +# define CSYSLOG_IF(condition, LEVEL, ...) el::base::NullWriter() +# define CSYSLOG_EVERY_N(n, LEVEL, ...) el::base::NullWriter() +# define CSYSLOG_AFTER_N(n, LEVEL, ...) el::base::NullWriter() +# define CSYSLOG_N_TIMES(n, LEVEL, ...) el::base::NullWriter() +# define SYSLOG(LEVEL) el::base::NullWriter() +# define SYSLOG_IF(condition, LEVEL) el::base::NullWriter() +# define SYSLOG_EVERY_N(n, LEVEL) el::base::NullWriter() +# define SYSLOG_AFTER_N(n, LEVEL) el::base::NullWriter() +# define SYSLOG_N_TIMES(n, LEVEL) el::base::NullWriter() +# define DCSYSLOG(LEVEL, ...) el::base::NullWriter() +# define DCSYSLOG_IF(condition, LEVEL, ...) el::base::NullWriter() +# define DCSYSLOG_EVERY_N(n, LEVEL, ...) el::base::NullWriter() +# define DCSYSLOG_AFTER_N(n, LEVEL, ...) el::base::NullWriter() +# define DCSYSLOG_N_TIMES(n, LEVEL, ...) el::base::NullWriter() +# define DSYSLOG(LEVEL) el::base::NullWriter() +# define DSYSLOG_IF(condition, LEVEL) el::base::NullWriter() +# define DSYSLOG_EVERY_N(n, LEVEL) el::base::NullWriter() +# define DSYSLOG_AFTER_N(n, LEVEL) el::base::NullWriter() +# define DSYSLOG_N_TIMES(n, LEVEL) el::base::NullWriter() +#endif // defined(ELPP_SYSLOG) +// +// Custom Debug Only Loggers - Requires (level, loggerId/s) +// +// undef existing +#undef DCLOG +#undef DCVLOG +#undef DCLOG_IF +#undef DCVLOG_IF +#undef DCLOG_EVERY_N +#undef DCVLOG_EVERY_N +#undef DCLOG_AFTER_N +#undef DCVLOG_AFTER_N +#undef DCLOG_N_TIMES +#undef DCVLOG_N_TIMES +// Normal logs +#define DCLOG(LEVEL, ...) if (ELPP_DEBUG_LOG) CLOG(LEVEL, __VA_ARGS__) +#define DCLOG_VERBOSE(vlevel, ...) if (ELPP_DEBUG_LOG) CLOG_VERBOSE(vlevel, __VA_ARGS__) +#define DCVLOG(vlevel, ...) if (ELPP_DEBUG_LOG) CVLOG(vlevel, __VA_ARGS__) +// Conditional logs +#define DCLOG_IF(condition, LEVEL, ...) if (ELPP_DEBUG_LOG) CLOG_IF(condition, LEVEL, __VA_ARGS__) +#define DCVLOG_IF(condition, vlevel, ...) if (ELPP_DEBUG_LOG) CVLOG_IF(condition, vlevel, __VA_ARGS__) +// Hit counts based logs +#define DCLOG_EVERY_N(n, LEVEL, ...) if (ELPP_DEBUG_LOG) CLOG_EVERY_N(n, LEVEL, __VA_ARGS__) +#define DCVLOG_EVERY_N(n, vlevel, ...) if (ELPP_DEBUG_LOG) CVLOG_EVERY_N(n, vlevel, __VA_ARGS__) +#define DCLOG_AFTER_N(n, LEVEL, ...) if (ELPP_DEBUG_LOG) CLOG_AFTER_N(n, LEVEL, __VA_ARGS__) +#define DCVLOG_AFTER_N(n, vlevel, ...) if (ELPP_DEBUG_LOG) CVLOG_AFTER_N(n, vlevel, __VA_ARGS__) +#define DCLOG_N_TIMES(n, LEVEL, ...) if (ELPP_DEBUG_LOG) CLOG_N_TIMES(n, LEVEL, __VA_ARGS__) +#define DCVLOG_N_TIMES(n, vlevel, ...) if (ELPP_DEBUG_LOG) CVLOG_N_TIMES(n, vlevel, __VA_ARGS__) +// +// Default Debug Only Loggers macro using CLOG(), CLOG_VERBOSE() and CVLOG() macros +// +#if !defined(ELPP_NO_DEBUG_MACROS) +// undef existing +#undef DLOG +#undef DVLOG +#undef DLOG_IF +#undef DVLOG_IF +#undef DLOG_EVERY_N +#undef DVLOG_EVERY_N +#undef DLOG_AFTER_N +#undef DVLOG_AFTER_N +#undef DLOG_N_TIMES +#undef DVLOG_N_TIMES +// Normal logs +#define DLOG(LEVEL) DCLOG(LEVEL, ELPP_CURR_FILE_LOGGER_ID) +#define DVLOG(vlevel) DCVLOG(vlevel, ELPP_CURR_FILE_LOGGER_ID) +// Conditional logs +#define DLOG_IF(condition, LEVEL) DCLOG_IF(condition, LEVEL, ELPP_CURR_FILE_LOGGER_ID) +#define DVLOG_IF(condition, vlevel) DCVLOG_IF(condition, vlevel, ELPP_CURR_FILE_LOGGER_ID) +// Hit counts based logs +#define DLOG_EVERY_N(n, LEVEL) DCLOG_EVERY_N(n, LEVEL, ELPP_CURR_FILE_LOGGER_ID) +#define DVLOG_EVERY_N(n, vlevel) DCVLOG_EVERY_N(n, vlevel, ELPP_CURR_FILE_LOGGER_ID) +#define DLOG_AFTER_N(n, LEVEL) DCLOG_AFTER_N(n, LEVEL, ELPP_CURR_FILE_LOGGER_ID) +#define DVLOG_AFTER_N(n, vlevel) DCVLOG_AFTER_N(n, vlevel, ELPP_CURR_FILE_LOGGER_ID) +#define DLOG_N_TIMES(n, LEVEL) DCLOG_N_TIMES(n, LEVEL, ELPP_CURR_FILE_LOGGER_ID) +#define DVLOG_N_TIMES(n, vlevel) DCVLOG_N_TIMES(n, vlevel, ELPP_CURR_FILE_LOGGER_ID) +#endif // defined(ELPP_NO_DEBUG_MACROS) +#if !defined(ELPP_NO_CHECK_MACROS) +// Check macros +#undef CCHECK +#undef CPCHECK +#undef CCHECK_EQ +#undef CCHECK_NE +#undef CCHECK_LT +#undef CCHECK_GT +#undef CCHECK_LE +#undef CCHECK_GE +#undef CCHECK_BOUNDS +#undef CCHECK_NOTNULL +#undef CCHECK_STRCASEEQ +#undef CCHECK_STRCASENE +#undef CHECK +#undef PCHECK +#undef CHECK_EQ +#undef CHECK_NE +#undef CHECK_LT +#undef CHECK_GT +#undef CHECK_LE +#undef CHECK_GE +#undef CHECK_BOUNDS +#undef CHECK_NOTNULL +#undef CHECK_STRCASEEQ +#undef CHECK_STRCASENE +#define CCHECK(condition, ...) CLOG_IF(!(condition), FATAL, __VA_ARGS__) << "Check failed: [" << #condition << "] " +#define CPCHECK(condition, ...) CPLOG_IF(!(condition), FATAL, __VA_ARGS__) << "Check failed: [" << #condition << "] " +#define CHECK(condition) CCHECK(condition, ELPP_CURR_FILE_LOGGER_ID) +#define PCHECK(condition) CPCHECK(condition, ELPP_CURR_FILE_LOGGER_ID) +#define CCHECK_EQ(a, b, ...) CCHECK(a == b, __VA_ARGS__) +#define CCHECK_NE(a, b, ...) CCHECK(a != b, __VA_ARGS__) +#define CCHECK_LT(a, b, ...) CCHECK(a < b, __VA_ARGS__) +#define CCHECK_GT(a, b, ...) CCHECK(a > b, __VA_ARGS__) +#define CCHECK_LE(a, b, ...) CCHECK(a <= b, __VA_ARGS__) +#define CCHECK_GE(a, b, ...) CCHECK(a >= b, __VA_ARGS__) +#define CCHECK_BOUNDS(val, min, max, ...) CCHECK(val >= min && val <= max, __VA_ARGS__) +#define CHECK_EQ(a, b) CCHECK_EQ(a, b, ELPP_CURR_FILE_LOGGER_ID) +#define CHECK_NE(a, b) CCHECK_NE(a, b, ELPP_CURR_FILE_LOGGER_ID) +#define CHECK_LT(a, b) CCHECK_LT(a, b, ELPP_CURR_FILE_LOGGER_ID) +#define CHECK_GT(a, b) CCHECK_GT(a, b, ELPP_CURR_FILE_LOGGER_ID) +#define CHECK_LE(a, b) CCHECK_LE(a, b, ELPP_CURR_FILE_LOGGER_ID) +#define CHECK_GE(a, b) CCHECK_GE(a, b, ELPP_CURR_FILE_LOGGER_ID) +#define CHECK_BOUNDS(val, min, max) CCHECK_BOUNDS(val, min, max, ELPP_CURR_FILE_LOGGER_ID) +#define CCHECK_NOTNULL(ptr, ...) CCHECK((ptr) != nullptr, __VA_ARGS__) +#define CCHECK_STREQ(str1, str2, ...) CLOG_IF(!el::base::utils::Str::cStringEq(str1, str2), FATAL, __VA_ARGS__) \ +<< "Check failed: [" << #str1 << " == " << #str2 << "] " +#define CCHECK_STRNE(str1, str2, ...) CLOG_IF(el::base::utils::Str::cStringEq(str1, str2), FATAL, __VA_ARGS__) \ +<< "Check failed: [" << #str1 << " != " << #str2 << "] " +#define CCHECK_STRCASEEQ(str1, str2, ...) CLOG_IF(!el::base::utils::Str::cStringCaseEq(str1, str2), FATAL, __VA_ARGS__) \ +<< "Check failed: [" << #str1 << " == " << #str2 << "] " +#define CCHECK_STRCASENE(str1, str2, ...) CLOG_IF(el::base::utils::Str::cStringCaseEq(str1, str2), FATAL, __VA_ARGS__) \ +<< "Check failed: [" << #str1 << " != " << #str2 << "] " +#define CHECK_NOTNULL(ptr) CCHECK_NOTNULL((ptr), ELPP_CURR_FILE_LOGGER_ID) +#define CHECK_STREQ(str1, str2) CCHECK_STREQ(str1, str2, ELPP_CURR_FILE_LOGGER_ID) +#define CHECK_STRNE(str1, str2) CCHECK_STRNE(str1, str2, ELPP_CURR_FILE_LOGGER_ID) +#define CHECK_STRCASEEQ(str1, str2) CCHECK_STRCASEEQ(str1, str2, ELPP_CURR_FILE_LOGGER_ID) +#define CHECK_STRCASENE(str1, str2) CCHECK_STRCASENE(str1, str2, ELPP_CURR_FILE_LOGGER_ID) +#undef DCCHECK +#undef DCCHECK_EQ +#undef DCCHECK_NE +#undef DCCHECK_LT +#undef DCCHECK_GT +#undef DCCHECK_LE +#undef DCCHECK_GE +#undef DCCHECK_BOUNDS +#undef DCCHECK_NOTNULL +#undef DCCHECK_STRCASEEQ +#undef DCCHECK_STRCASENE +#undef DCPCHECK +#undef DCHECK +#undef DCHECK_EQ +#undef DCHECK_NE +#undef DCHECK_LT +#undef DCHECK_GT +#undef DCHECK_LE +#undef DCHECK_GE +#undef DCHECK_BOUNDS_ +#undef DCHECK_NOTNULL +#undef DCHECK_STRCASEEQ +#undef DCHECK_STRCASENE +#undef DPCHECK +#define DCCHECK(condition, ...) if (ELPP_DEBUG_LOG) CCHECK(condition, __VA_ARGS__) +#define DCCHECK_EQ(a, b, ...) if (ELPP_DEBUG_LOG) CCHECK_EQ(a, b, __VA_ARGS__) +#define DCCHECK_NE(a, b, ...) if (ELPP_DEBUG_LOG) CCHECK_NE(a, b, __VA_ARGS__) +#define DCCHECK_LT(a, b, ...) if (ELPP_DEBUG_LOG) CCHECK_LT(a, b, __VA_ARGS__) +#define DCCHECK_GT(a, b, ...) if (ELPP_DEBUG_LOG) CCHECK_GT(a, b, __VA_ARGS__) +#define DCCHECK_LE(a, b, ...) if (ELPP_DEBUG_LOG) CCHECK_LE(a, b, __VA_ARGS__) +#define DCCHECK_GE(a, b, ...) if (ELPP_DEBUG_LOG) CCHECK_GE(a, b, __VA_ARGS__) +#define DCCHECK_BOUNDS(val, min, max, ...) if (ELPP_DEBUG_LOG) CCHECK_BOUNDS(val, min, max, __VA_ARGS__) +#define DCCHECK_NOTNULL(ptr, ...) if (ELPP_DEBUG_LOG) CCHECK_NOTNULL((ptr), __VA_ARGS__) +#define DCCHECK_STREQ(str1, str2, ...) if (ELPP_DEBUG_LOG) CCHECK_STREQ(str1, str2, __VA_ARGS__) +#define DCCHECK_STRNE(str1, str2, ...) if (ELPP_DEBUG_LOG) CCHECK_STRNE(str1, str2, __VA_ARGS__) +#define DCCHECK_STRCASEEQ(str1, str2, ...) if (ELPP_DEBUG_LOG) CCHECK_STRCASEEQ(str1, str2, __VA_ARGS__) +#define DCCHECK_STRCASENE(str1, str2, ...) if (ELPP_DEBUG_LOG) CCHECK_STRCASENE(str1, str2, __VA_ARGS__) +#define DCPCHECK(condition, ...) if (ELPP_DEBUG_LOG) CPCHECK(condition, __VA_ARGS__) +#define DCHECK(condition) DCCHECK(condition, ELPP_CURR_FILE_LOGGER_ID) +#define DCHECK_EQ(a, b) DCCHECK_EQ(a, b, ELPP_CURR_FILE_LOGGER_ID) +#define DCHECK_NE(a, b) DCCHECK_NE(a, b, ELPP_CURR_FILE_LOGGER_ID) +#define DCHECK_LT(a, b) DCCHECK_LT(a, b, ELPP_CURR_FILE_LOGGER_ID) +#define DCHECK_GT(a, b) DCCHECK_GT(a, b, ELPP_CURR_FILE_LOGGER_ID) +#define DCHECK_LE(a, b) DCCHECK_LE(a, b, ELPP_CURR_FILE_LOGGER_ID) +#define DCHECK_GE(a, b) DCCHECK_GE(a, b, ELPP_CURR_FILE_LOGGER_ID) +#define DCHECK_BOUNDS(val, min, max) DCCHECK_BOUNDS(val, min, max, ELPP_CURR_FILE_LOGGER_ID) +#define DCHECK_NOTNULL(ptr) DCCHECK_NOTNULL((ptr), ELPP_CURR_FILE_LOGGER_ID) +#define DCHECK_STREQ(str1, str2) DCCHECK_STREQ(str1, str2, ELPP_CURR_FILE_LOGGER_ID) +#define DCHECK_STRNE(str1, str2) DCCHECK_STRNE(str1, str2, ELPP_CURR_FILE_LOGGER_ID) +#define DCHECK_STRCASEEQ(str1, str2) DCCHECK_STRCASEEQ(str1, str2, ELPP_CURR_FILE_LOGGER_ID) +#define DCHECK_STRCASENE(str1, str2) DCCHECK_STRCASENE(str1, str2, ELPP_CURR_FILE_LOGGER_ID) +#define DPCHECK(condition) DCPCHECK(condition, ELPP_CURR_FILE_LOGGER_ID) +#endif // defined(ELPP_NO_CHECK_MACROS) +#if defined(ELPP_DISABLE_DEFAULT_CRASH_HANDLING) +# define ELPP_USE_DEF_CRASH_HANDLER false +#else +# define ELPP_USE_DEF_CRASH_HANDLER true +#endif // defined(ELPP_DISABLE_DEFAULT_CRASH_HANDLING) +#define ELPP_CRASH_HANDLER_INIT +#define ELPP_INIT_EASYLOGGINGPP(val) \ +namespace el { \ +namespace base { \ +el::base::type::StoragePointer elStorage(val); \ +} \ +el::base::debug::CrashHandler elCrashHandler(ELPP_USE_DEF_CRASH_HANDLER); \ +} + +#if ELPP_ASYNC_LOGGING +# define INITIALIZE_EASYLOGGINGPP ELPP_INIT_EASYLOGGINGPP(new el::base::Storage(el::LogBuilderPtr(new el::base::DefaultLogBuilder()),\ +new el::base::AsyncDispatchWorker())) +#else +# define INITIALIZE_EASYLOGGINGPP ELPP_INIT_EASYLOGGINGPP(new el::base::Storage(el::LogBuilderPtr(new el::base::DefaultLogBuilder()))) +#endif // ELPP_ASYNC_LOGGING +#define INITIALIZE_NULL_EASYLOGGINGPP \ +namespace el {\ +namespace base {\ +el::base::type::StoragePointer elStorage;\ +}\ +el::base::debug::CrashHandler elCrashHandler(ELPP_USE_DEF_CRASH_HANDLER);\ +} +#define SHARE_EASYLOGGINGPP(initializedStorage)\ +namespace el {\ +namespace base {\ +el::base::type::StoragePointer elStorage(initializedStorage);\ +}\ +el::base::debug::CrashHandler elCrashHandler(ELPP_USE_DEF_CRASH_HANDLER);\ +} + +#if defined(ELPP_UNICODE) +# define START_EASYLOGGINGPP(argc, argv) el::Helpers::setArgs(argc, argv); std::locale::global(std::locale("")) +#else +# define START_EASYLOGGINGPP(argc, argv) el::Helpers::setArgs(argc, argv) +#endif // defined(ELPP_UNICODE) +#endif // EASYLOGGINGPP_H diff --git a/utils/McuProtocol/CMakeLists.txt b/utils/McuProtocol/CMakeLists.txt new file mode 100644 index 0000000..0b37a80 --- /dev/null +++ b/utils/McuProtocol/CMakeLists.txt @@ -0,0 +1,57 @@ +include(${CMAKE_SOURCE_DIR_IPCSDK}/build/global_config.cmake) +set(EXECUTABLE_OUTPUT_PATH ${EXEC_OUTPUT_PATH}) +set(LIBRARY_OUTPUT_PATH ${LIBS_OUTPUT_PATH}) + +include_directories( + ./src + ./include + # ${UTILS_SOURCE_PATH}/LinuxApi/include + ${UTILS_SOURCE_PATH}/StatusCode/include + ${UTILS_SOURCE_PATH}/ModBusCRC16/include + ${UTILS_SOURCE_PATH}/Log/include +) +# link_directories( +# ${EXTERNAL_SOURCE_PATH}/libconfig/libconfig-1.7.3/lib/.libs +# ) + + + +aux_source_directory(./src SRC_FILES) + +set(TARGET_NAME McuProtocol) +add_library(${TARGET_NAME} STATIC ${SRC_FILES}) +target_link_libraries(${TARGET_NAME} ModBusCRC16 StatusCode Log) + +if ("${COMPILE_IMPROVE_SUPPORT}" MATCHES "true") +add_custom_target( + McuProtocol_code_check + COMMAND ${CLANG_TIDY_EXE} + -checks='${CLANG_TIDY_CHECKS}' + --header-filter=.* + --system-headers=false + ${SRC_FILES} + ${CLANG_TIDY_CONFIG} + -p ${PLATFORM_PATH}/cmake-shell + WORKING_DIRECTORY ${UTILS_SOURCE_PATH}/McuProtocol +) +file(GLOB_RECURSE HEADER_FILES *.h) +add_custom_target( + McuProtocol_code_format + COMMAND ${CLANG_FORMAT_EXE} + -style=file + -i ${SRC_FILES} ${HEADER_FILES} + WORKING_DIRECTORY ${UTILS_SOURCE_PATH}/McuProtocol +) +add_custom_command( + TARGET ${TARGET_NAME} + PRE_BUILD + COMMAND make McuProtocol_code_check + COMMAND make McuProtocol_code_format + WORKING_DIRECTORY ${PLATFORM_PATH}/cmake-shell/ +) +endif() + +define_file_name(${TARGET_NAME}) + +file(GLOB_RECURSE INSTALL_HEADER_FILES include/*.h) +install(FILES ${INSTALL_HEADER_FILES} DESTINATION include) \ No newline at end of file diff --git a/utils/McuProtocol/README.md b/utils/McuProtocol/README.md new file mode 100644 index 0000000..e973787 --- /dev/null +++ b/utils/McuProtocol/README.md @@ -0,0 +1,170 @@ + +# 1. MCU协议模块 + +  负责对MCU协议进行封包/解包,负责协议的多态替换。协议数据统一使用网络字节序(大端字节序)。 + +**注:协议数据统一使用网络字节序(大端字节序)** + +| 版本 | 时间 | 说明 | +| ---- | ---- | ---- | +| V1.0 | 2024-5-14 | 首次评审。 | +| V1.1 | 2024-5-16 | 补充数据同步协议。 | + +## 1.1. 协议格式 + +| 协议头 | 流水号 | 命令字 | 长度 | 数据段 | 校验码 | +|----|----|----|----|----|----| +| 2字节
0xFAC1 | 4字节 | 2字节 | 2字节
协议包总长度 | - | 2字节 | + +**流水号** + +  流水号用于强绑定问答型协议的发送数据和回复数据,回复者原数据回传即可。例如: + +``` +unsigned char ASK_IPC_MISSION[] = {0xFA, 0xC1, 0x00, 0x00, 0x00, 0x01, 0x81, 0x01, 0x00, 0x0C, 0x71, 0x88}; +unsigned char REPLY_IPC_MISSION[] = {0xFA, 0xC1, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x00, 0x0D, 0x01, 0xAA, 0x89}; +``` + +  流水号管理发送方和接收方互相独立,各自往外发的协议中流水号存在重复的可能,接收方直接复制回传即可。 + +  流水号必须大于等于1,0用于代码初始化值,代码意义上表示无效的流水号。 + +**协议收发匹配逻辑** +  协议发送时,附带自管理的流水号,对端回复时,根据流水号绑定发送的协议,为了避免对端回复时,流水号错误导致的业务逻辑错乱,本端除了匹配流水号,还需要根据协议匹配回复的命令字,保证回复内容的有效匹配。如果流水号和命令字无法同时匹配,该回复的数据包被丢弃。 + +**校验码算法** + +  校验码算法使用ModBus CRC16方法计算。 + +**参考代码(查表法)** + +``` +/* Table of CRC values for high–order byte */ +unsigned char chCRCHi[] ={ + 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, + 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, + 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, + 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, + 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, + 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, + 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, + 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, + 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, + 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, + 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, + 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, + 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, + 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, + 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, + 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, + 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, + 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, + 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, + 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, + 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, + 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, + 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, + 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, + 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, + 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40 +}; + +/* Table of CRC values for low–order byte */ +unsigned char chCRCLo[] ={ + 0x00, 0xC0, 0xC1, 0x01, 0xC3, 0x03, 0x02, 0xC2, 0xC6, 0x06, + 0x07, 0xC7, 0x05, 0xC5, 0xC4, 0x04, 0xCC, 0x0C, 0x0D, 0xCD, + 0x0F, 0xCF, 0xCE, 0x0E, 0x0A, 0xCA, 0xCB, 0x0B, 0xC9, 0x09, + 0x08, 0xC8, 0xD8, 0x18, 0x19, 0xD9, 0x1B, 0xDB, 0xDA, 0x1A, + 0x1E, 0xDE, 0xDF, 0x1F, 0xDD, 0x1D, 0x1C, 0xDC, 0x14, 0xD4, + 0xD5, 0x15, 0xD7, 0x17, 0x16, 0xD6, 0xD2, 0x12, 0x13, 0xD3, + 0x11, 0xD1, 0xD0, 0x10, 0xF0, 0x30, 0x31, 0xF1, 0x33, 0xF3, + 0xF2, 0x32, 0x36, 0xF6, 0xF7, 0x37, 0xF5, 0x35, 0x34, 0xF4, + 0x3C, 0xFC, 0xFD, 0x3D, 0xFF, 0x3F, 0x3E, 0xFE, 0xFA, 0x3A, + 0x3B, 0xFB, 0x39, 0xF9, 0xF8, 0x38, 0x28, 0xE8, 0xE9, 0x29, + 0xEB, 0x2B, 0x2A, 0xEA, 0xEE, 0x2E, 0x2F, 0xEF, 0x2D, 0xED, + 0xEC, 0x2C, 0xE4, 0x24, 0x25, 0xE5, 0x27, 0xE7, 0xE6, 0x26, + 0x22, 0xE2, 0xE3, 0x23, 0xE1, 0x21, 0x20, 0xE0, 0xA0, 0x60, + 0x61, 0xA1, 0x63, 0xA3, 0xA2, 0x62, 0x66, 0xA6, 0xA7, 0x67, + 0xA5, 0x65, 0x64, 0xA4, 0x6C, 0xAC, 0xAD, 0x6D, 0xAF, 0x6F, + 0x6E, 0xAE, 0xAA, 0x6A, 0x6B, 0xAB, 0x69, 0xA9, 0xA8, 0x68, + 0x78, 0xB8, 0xB9, 0x79, 0xBB, 0x7B, 0x7A, 0xBA, 0xBE, 0x7E, + 0x7F, 0xBF, 0x7D, 0xBD, 0xBC, 0x7C, 0xB4, 0x74, 0x75, 0xB5, + 0x77, 0xB7, 0xB6, 0x76, 0x72, 0xB2, 0xB3, 0x73, 0xB1, 0x71, + 0x70, 0xB0, 0x50, 0x90, 0x91, 0x51, 0x93, 0x53, 0x52, 0x92, + 0x96, 0x56, 0x57, 0x97, 0x55, 0x95, 0x94, 0x54, 0x9C, 0x5C, + 0x5D, 0x9D, 0x5F, 0x9F, 0x9E, 0x5E, 0x5A, 0x9A, 0x9B, 0x5B, + 0x99, 0x59, 0x58, 0x98, 0x88, 0x48, 0x49, 0x89, 0x4B, 0x8B, + 0x8A, 0x4A, 0x4E, 0x8E, 0x8F, 0x4F, 0x8D, 0x4D, 0x4C, 0x8C, + 0x44, 0x84, 0x85, 0x45, 0x87, 0x47, 0x46, 0x86, 0x82, 0x42, + 0x43, 0x83, 0x41, 0x81, 0x80, 0x40 +}; +/** + * @brief 计算校验和 + * + * @param pData 数据指针 + * @param length 数据长度 + * @return unsigned short + */ +unsigned short calculate_check_sum(const unsigned char* pData, unsigned short length) +{ + unsigned char CRCHi = 0xFF; + unsigned char CRCLo = 0xFF; + int uIndex; + while (length--) + { + uIndex = CRCHi ^ *pData++; + CRCHi = CRCLo ^ chCRCHi[uIndex]; + CRCLo = chCRCLo[uIndex]; + } + return (CRCHi << 8 | CRCLo); +} +``` + +## 1.2. 协议内容 + +**基本描述** +1. 大于等于0x8000的命令字由CPU发给MCU; +2. 小于0x8000的命令字由MCU发给CPU; +3. 大于0x0801而小于0xC101的命令由CPU主动发送给MCU,大于等于0xC101的命令由CPU回复发送给MCU; +4. 问答型协议只有按位最高位相反,其它位相等,例如:0x8101与0x0101是一问一答的协议; + +| 命令字 | CPU | MCU | 数据段 | 协议解析 | 备注 | +| ---- | ---- | ---- | ---- | ---- | ---- | +| 0x8101 | ask | - | - | 获取启动模式 | - | +| 0x0101 | - | reply | Data[0]:启动模式
0x01:PIR启动
0x02:TEST启动
0x03:ON启动
0x04:连拍启动
0x05:PIR延时启动
0x06:定时(间隔一定时间)启动
0x07:关机
0x08:低电关机
0x09:异常启动 | 回复启动模式 | 异常启动数据:Data[1]
0x00:喂狗异常启动
0x01:超时异常启动 | +| 0x8102 | ask | - | - | 断电关机 | 断电关机不用回复 | +| 0x8103 | ask | - | - | 喂狗 | 喂狗不用回复 | +| 0x8104 | ask | - | Data[2]:两字节的数字,单位s | 设置喂狗周期 | 0s代表关闭喂狗 修改为两个字节,单位s | +| 0x0104 | - | reply | Data[0]:结果
0x01:成功
0x02:失败 | 设置喂狗周期回复 | - | +| 0x8105 | ask | - | - | 关闭狗 | 取消 | +| 0x0105 | - | reply | Data[0]:结果
0x01:成功
0x02:失败 | 关闭狗回复 | 取消 | +| 0x8106 | ask | - | Data[0]:Hour
0-23
Data[1]:Min
0-59
Data[2]:Sec
0-59 | 设置间隔启动时间 | 定时启动 | +| 0x0106 | - | reply | Data[0]:结果
0x01:成功
0x02:失败 | 设置间隔启动时间回复 | - | +| 0x8107 | ask | - | Data[0]:Year
0-255
Data[1]:Mon
1-12
Data[2]:Day
0-31
Data[3]:Hour
0-23
Data[4]:Min
0-59
Data[5]:Sec
0-59 | 设置日期和时间 | 年份需要+1970修正 | +| 0x0107 | - | reply | Data[0]:结果
0x01:成功
0x02:失败 | 设置日期和时间回复 | - | +| 0x8108 | ask | - | Data[0]:灵敏度
0-9 | 设置PIR灵敏度 | - | +| 0x0108 | - | reply | Data[0]:结果
0x01:成功
0x02:失败 | 设置PIR灵敏度回复 | - | +| 0x8109 | ask | - | Data[0]:连拍间隔,单位s | 设置连拍启动 | 设置连拍启动后会立即关机,一般连拍间隔较大时使用该功能,连拍间隔较小时Linux单次完成连拍。 | +| 0x0109 | - | reply | Data[0]:结果
0x01:成功
0x02:失败 | 设置连拍启动回复 | - | +| 0x810A | ask | - | Data[0]:控制模式
0-关闭红外灯
1-开启红外灯 | 红外灯控制 | 取消 | +| 0x010A | - | reply | Data[0]:结果
0x01:成功
0x02:失败 | 红外灯控制回复 | 取消 | +| 0x810B | ask | - | - | 获取光敏值 | 取消 | +| 0x010B | - | reply | Data[0]:结果
0-100 | 获取光敏值回复 | 取消 | +| ====== | === | ====== | ============================ | ==================== | ======= | +| 0xC101 | reply | - | Data[0]:结果
0x01:成功
0x02:失败 | 发送启动模式回复 | - | +| 0x4101 | - | ask | Data[0]:启动模式
0x01:PIR启动
0x02:TEST启动
0x03:ON启动
0x04:连拍启动
0x05:PIR延时启动
0x06:定时(间隔一定时间)启动
0x07:关机
0x08:低电关机
0x09:异常启动 | 发送启动模式 | 异常启动数据:Data[1]
0x00:喂狗异常启动
0x01:超时异常启动 | +| 0xC102 | reply | - | - | 回复心跳包 | 取消 | +| 0x4102 | - | ask | - | 发送心跳包 | 取消 | +| 0xC106 | reply | - | Data[0]:Hour
0-23
Data[1]:Min
0-59
Data[2]:Sec
0-59 | 回复获取间隔启动时间 | - | +| 0x4106 | - | ask | - | 获取间隔启动时间 | - | +| 0xC107 | reply | - | Data[0]:Year
0-255
Data[1]:Mon
1-12
Data[2]:Day
0-31
Data[3]:Hour
0-23
Data[4]:Min
0-59
Data[5]:Sec
0-59 | 回复获取日期和时间 | 年份需要+1970修正 | +| 0x4107 | - | ask | - | 获取日期和时间 | - | +| 0xC108 | reply | - | Data[0]:灵敏度
0-9 | 回复获取PIR灵敏度 | - | +| 0x4108 | - | ask | - | 获取PIR灵敏度 | - | + +### 1.2.1. 名词解析 + +| 名词 | 解析 | +| ---- | ---- | +| 超时异常启动 | MCU给主控上电后(**非TEST启动**),不管主控是否正常喂狗,如果单次上电时长超过3min未关机,给主控断电重启一次。 | +| 间隔启动时间 | 例如:60min/次。 | \ No newline at end of file diff --git a/utils/McuProtocol/include/McuProtocol.h b/utils/McuProtocol/include/McuProtocol.h new file mode 100644 index 0000000..74302c6 --- /dev/null +++ b/utils/McuProtocol/include/McuProtocol.h @@ -0,0 +1,162 @@ +/* + * Copyright (c) 2023 Fancy Code. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef MCU_PROTOCOL_H +#define MCU_PROTOCOL_H +#include "StatusCode.h" +#include +#include +#include +#include +#include +#include +/** + * @brief The context of protocol processing is used for the association binding of protocol data transmission and + * reception, ensuring the correct logic of protocol question and answer. + * + */ +class VProtocolContext +{ +public: + VProtocolContext() = default; + virtual ~VProtocolContext() = default; + unsigned int mSerialNumber; + short mCheckCode; // TODO: Need to improve +}; +template +class ProtocolContext : public VProtocolContext +{ + +public: + ProtocolContext(T &value) : mData(value) + { + } + virtual ~ProtocolContext() = default; + +public: + T mData; +}; +class SingleMcuPacket +{ +public: + SingleMcuPacket(const void *buf, const size_t &length) : mBuf(buf), mLength(length) + { + } + ~SingleMcuPacket() = default; + const void *mBuf; + const size_t mLength; +}; +class VProtocolBase +{ +public: + VProtocolBase() = default; + virtual ~VProtocolBase() = default; + +protected: + virtual std::shared_ptr SharedFromThis(void); + virtual ssize_t WriteData(const void *buff, const size_t buffLength, std::shared_ptr &context, + const unsigned int &serialNumber); + +protected: + virtual size_t GetKeyHeadLength(void); + virtual StatusCode GetDataLength(const void *keyHead, const size_t &headLength, size_t &dataLength); + virtual void PushMcuData(const void *buf, const size_t &length); + virtual StatusCode CheckHeader(void *header, size_t &headLength); +}; +enum class ReplyResult +{ + SUCCEED = 1, + FAILED, + END +}; +class OtherSideReply +{ +public: + OtherSideReply() = default; + virtual ~OtherSideReply() = default; + virtual void GetIpcMissionReply(const unsigned int &serialNumber, const unsigned char &mission); + virtual void OnlyResultReply(const unsigned int &serialNumber, const ReplyResult &result); +}; +class OtherSideAsk +{ +public: + OtherSideAsk() = default; + virtual ~OtherSideAsk() = default; + virtual void OtherSideSendIpcMission(const unsigned int &serialNumber, const unsigned char &mission); + virtual void OtherSideSendHearBeat(const unsigned int &serialNumber); + virtual void OtherSideSendGetIntervalStart(const unsigned int &serialNumber); + virtual void OtherSideSendGetDateTime(const unsigned int &serialNumber); + virtual void OtherSideSendGetPirSensitivity(const unsigned int &serialNumber); +}; +class VProtocolRecv : public OtherSideReply, public OtherSideAsk +{ +public: + VProtocolRecv() = default; + virtual ~VProtocolRecv() = default; + static std::shared_ptr &GetInstance(std::shared_ptr *impl = nullptr); +}; +class McuProtocol : virtual public VProtocolBase +{ +public: + McuProtocol() = default; + virtual ~McuProtocol() = default; + +protected: + const StatusCode Init(void); + const StatusCode UnInit(void); + const StatusCode GetIpcMission(std::shared_ptr &context); + const StatusCode CutOffPowerSupply(std::shared_ptr &context); + const StatusCode FeedWatchDog(std::shared_ptr &context); + const StatusCode SetFeedingCycleForWatchDog(const unsigned char &hour, const unsigned char &min, + const unsigned char &second, + std::shared_ptr &context); + const StatusCode McuSetDateTime(const unsigned short &year, const unsigned char &mon, const unsigned char &day, + const unsigned char &hour, const unsigned char &min, const unsigned char &second, + std::shared_ptr &context); + const StatusCode SetPirSensitivity(const unsigned char &sensitivity, std::shared_ptr &context); + const StatusCode ContorlInfraredLight(const unsigned char &control, std::shared_ptr &context); + const StatusCode GetPhotosensitivityValue(std::shared_ptr &context); + void DataHandleThread(void); + +protected: + void ReplyOtherSideSendIpcMission(const ReplyResult &result, const unsigned int &serialNumber); + void ReplyOtherSideSendHearBeat(const ReplyResult &result, const unsigned int &serialNumber); + void ReplyOtherSideSendGetIntervalStart(const ReplyResult &result, const unsigned int &serialNumber, + const unsigned char &hour, const unsigned char &min, + const unsigned char &second); + void ReplyOtherSideSendGetDateTime(const ReplyResult &result, const unsigned int &serialNumber, + const unsigned short &year, const unsigned char &mon, const unsigned char &day, + const unsigned char &hour, const unsigned char &min, + const unsigned char &second); + void ReplyOtherSideSendGetPirSensitivity(const ReplyResult &result, const unsigned int &serialNumber, + const unsigned char &sensitivity); + +protected: + size_t GetKeyHeadLength(void) override; + StatusCode GetDataLength(const void *keyHead, const size_t &headLength, size_t &dataLength) override; + void PushMcuData(const void *buf, const size_t &length) override; + StatusCode CheckHeader(void *header, size_t &headLength) override; + +private: + StatusCode WriteProtocolData(const void *buff, const size_t buffLength, std::shared_ptr &context, + const unsigned int &serialNumber); + +private: + std::mutex mMutex; + sem_t mSem; + bool mThreadRuning; + std::thread mDataHandleThread; + std::list mMcuDataList; +}; +#endif \ No newline at end of file diff --git a/utils/McuProtocol/src/LittleEndianHandle.cpp b/utils/McuProtocol/src/LittleEndianHandle.cpp new file mode 100644 index 0000000..d6e848e --- /dev/null +++ b/utils/McuProtocol/src/LittleEndianHandle.cpp @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2023 Fancy Code. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "LittleEndianHandle.h" +#include "ILog.h" +#include "ModBusCRC16.h" +#include +#include +LittleEndianHandle::LittleEndianHandle(const std::shared_ptr ¶m) : ProtocolHandle(param) +{ +} +LittleEndianHandle::LittleEndianHandle(const void *data, const size_t &length) : ProtocolHandle(data, length) +{ +} +void LittleEndianHandle::BigEndianConversion(ProtocolPacket &packet) +{ + packet.mHead = htons(packet.mHead); + packet.mCommand = htons(packet.mCommand); + packet.mLength = htons(packet.mLength); + packet.mCheckCode = htons(packet.mCheckCode); + packet.mSerialNumber = htonl(packet.mSerialNumber); +} +short LittleEndianHandle::BigEndianConversion(const short &number) +{ + return htons(number); +} +void LittleEndianHandle::HostByteOrderConversion(ProtocolPacket &packet) +{ + packet.mHead = ntohs(packet.mHead); + packet.mCommand = ntohs(packet.mCommand); + packet.mLength = ntohs(packet.mLength); + packet.mCheckCode = ntohs(packet.mCheckCode); + packet.mSerialNumber = ntohl(packet.mSerialNumber); +} +bool LittleEndianHandle::CheckoutTheCheckCode(const ProtocolPacket &packet) +{ + ProtocolHandle::PrintHexadecimalData(&packet.mCheckCode, CHECK_CODE_LENGTH, "CheckoutTheCheckCode little endian:"); + short code = calculate_check_sum(mProtocolData, mProtocolDataLength - sizeof(short)); + // code = ntohs(code); // TODO: + if (code == packet.mCheckCode) { + return true; + } + return false; +} \ No newline at end of file diff --git a/utils/McuProtocol/src/LittleEndianHandle.h b/utils/McuProtocol/src/LittleEndianHandle.h new file mode 100644 index 0000000..72f93b2 --- /dev/null +++ b/utils/McuProtocol/src/LittleEndianHandle.h @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2023 Fancy Code. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef LITTLE_ENDIAN_HANDLE_H +#define LITTLE_ENDIAN_HANDLE_H +#include "ProtocolHandle.h" +class LittleEndianHandle : public ProtocolHandle +{ +public: + LittleEndianHandle(const std::shared_ptr ¶m); + LittleEndianHandle(const void *data, const size_t &length); + virtual ~LittleEndianHandle() = default; + +private: + void BigEndianConversion(ProtocolPacket &packet) override; + short BigEndianConversion(const short &number) override; + void HostByteOrderConversion(ProtocolPacket &packet) override; + bool CheckoutTheCheckCode(const ProtocolPacket &packet) override; +}; +#endif \ No newline at end of file diff --git a/utils/McuProtocol/src/McuProtocol.cpp b/utils/McuProtocol/src/McuProtocol.cpp new file mode 100644 index 0000000..55d6539 --- /dev/null +++ b/utils/McuProtocol/src/McuProtocol.cpp @@ -0,0 +1,286 @@ +/* + * Copyright (c) 2023 Fancy Code. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "McuProtocol.h" +#include "ILog.h" +#include "ProtocolHandle.h" +#include +std::shared_ptr VProtocolBase::SharedFromThis(void) +{ + return std::make_shared(); +} +ssize_t VProtocolBase::WriteData(const void *buff, const size_t buffLength, std::shared_ptr &context, + const unsigned int &serialNumber) +{ + return 0; +} +size_t VProtocolBase::GetKeyHeadLength(void) +{ + return 0; +} +StatusCode VProtocolBase::GetDataLength(const void *keyHead, const size_t &headLength, size_t &dataLength) +{ + return CreateStatusCode(STATUS_CODE_INVALID_PARAMENTER); +} +void VProtocolBase::PushMcuData(const void *buf, const size_t &length) +{ +} +StatusCode VProtocolBase::CheckHeader(void *header, size_t &headLength) +{ + return CreateStatusCode(STATUS_CODE_INVALID_PARAMENTER); +} +void OtherSideReply::GetIpcMissionReply(const unsigned int &serialNumber, const unsigned char &mission) +{ +} +void OtherSideReply::OnlyResultReply(const unsigned int &serialNumber, const ReplyResult &result) +{ +} +void OtherSideAsk::OtherSideSendIpcMission(const unsigned int &serialNumber, const unsigned char &mission) +{ +} +void OtherSideAsk::OtherSideSendHearBeat(const unsigned int &serialNumber) +{ +} +void OtherSideAsk::OtherSideSendGetIntervalStart(const unsigned int &serialNumber) +{ +} +void OtherSideAsk::OtherSideSendGetDateTime(const unsigned int &serialNumber) +{ +} +void OtherSideAsk::OtherSideSendGetPirSensitivity(const unsigned int &serialNumber) +{ +} +std::shared_ptr &VProtocolRecv::GetInstance(std::shared_ptr *impl) +{ + static auto instance = std::make_shared(); + if (impl) { + instance = *impl; + } + return instance; +} +const StatusCode McuProtocol::Init(void) +{ + ProtocolHandle::Init(); + constexpr int THREAD_SHARING = 0; + constexpr int INITIAL_VALUE_OF_SEMAPHORE = 0; + sem_init(&mSem, THREAD_SHARING, INITIAL_VALUE_OF_SEMAPHORE); + auto dataHandleThread = [](std::shared_ptr handle) { + handle->DataHandleThread(); + }; + std::shared_ptr handle = std::dynamic_pointer_cast(SharedFromThis()); + mDataHandleThread = std::thread(dataHandleThread, handle); + std::shared_ptr reply = std::dynamic_pointer_cast(SharedFromThis()); + if (reply) { + VProtocolRecv::GetInstance(&reply); + } + else { + LogWarning("The VProtocolRecv class has not been overloaded, which can result in incomplete functionality.\n"); + } + return CreateStatusCode(STATUS_CODE_OK); +} +const StatusCode McuProtocol::UnInit(void) +{ + ProtocolHandle::UnInit(); + mThreadRuning = false; + sem_post(&mSem); + if (mDataHandleThread.joinable()) { + mDataHandleThread.join(); + } + std::shared_ptr reply = std::make_shared(); + VProtocolRecv::GetInstance(&reply); + sem_destroy(&mSem); + return CreateStatusCode(STATUS_CODE_OK); +} +const StatusCode McuProtocol::GetIpcMission(std::shared_ptr &context) +{ + std::shared_ptr param = std::make_shared(PROTOCOL_COMMAND::ASK_IPC_MISSION); + std::shared_ptr handle = ProtocolHandle::CreateProtocolData(param); + return WriteProtocolData( + handle->GetProtocolDataBuff(), handle->GetProtocolDataLength(), context, handle->GetSerialNumber()); +} +const StatusCode McuProtocol::CutOffPowerSupply(std::shared_ptr &context) +{ + std::shared_ptr param = + std::make_shared(PROTOCOL_COMMAND::ASK_CUT_OFF_PWOER_SUPPLY); + std::shared_ptr handle = ProtocolHandle::CreateProtocolData(param); + return WriteProtocolData( + handle->GetProtocolDataBuff(), handle->GetProtocolDataLength(), context, handle->GetSerialNumber()); +} +const StatusCode McuProtocol::FeedWatchDog(std::shared_ptr &context) +{ + std::shared_ptr param = std::make_shared(PROTOCOL_COMMAND::ASK_FEED_WATCH_DOG); + std::shared_ptr handle = ProtocolHandle::CreateProtocolData(param); + return WriteProtocolData( + handle->GetProtocolDataBuff(), handle->GetProtocolDataLength(), context, handle->GetSerialNumber()); +} +const StatusCode McuProtocol::SetFeedingCycleForWatchDog(const unsigned char &hour, const unsigned char &min, + const unsigned char &second, + std::shared_ptr &context) +{ + SetTime watchDogParam(hour, min, second); + std::shared_ptr param = + std::make_shared>(PROTOCOL_COMMAND::ASK_SET_FEEDING_CYCLE, watchDogParam); + std::shared_ptr handle = ProtocolHandle::CreateProtocolData(param); + return WriteProtocolData( + handle->GetProtocolDataBuff(), handle->GetProtocolDataLength(), context, handle->GetSerialNumber()); +} +const StatusCode McuProtocol::McuSetDateTime(const unsigned short &year, const unsigned char &mon, + const unsigned char &day, const unsigned char &hour, + const unsigned char &min, const unsigned char &second, + std::shared_ptr &context) +{ + SetDateTime dateTime(year, mon, day, hour, min, second); + std::shared_ptr param = + std::make_shared>(PROTOCOL_COMMAND::ASK_SET_DATE_TIME, dateTime); + std::shared_ptr handle = ProtocolHandle::CreateProtocolData(param); + return WriteProtocolData( + handle->GetProtocolDataBuff(), handle->GetProtocolDataLength(), context, handle->GetSerialNumber()); +} +const StatusCode McuProtocol::SetPirSensitivity(const unsigned char &sensitivity, + std::shared_ptr &context) +{ + std::shared_ptr param = + std::make_shared>(PROTOCOL_COMMAND::ASK_SET_PIR_SENSITIVITY, sensitivity); + std::shared_ptr handle = ProtocolHandle::CreateProtocolData(param); + return WriteProtocolData( + handle->GetProtocolDataBuff(), handle->GetProtocolDataLength(), context, handle->GetSerialNumber()); +} +const StatusCode McuProtocol::ContorlInfraredLight(const unsigned char &control, + std::shared_ptr &context) +{ + std::shared_ptr param = + std::make_shared>(PROTOCOL_COMMAND::ASK_CONTORL_INFRARED_LIGHT, control); + std::shared_ptr handle = ProtocolHandle::CreateProtocolData(param); + return WriteProtocolData( + handle->GetProtocolDataBuff(), handle->GetProtocolDataLength(), context, handle->GetSerialNumber()); +} +const StatusCode McuProtocol::GetPhotosensitivityValue(std::shared_ptr &context) +{ + std::shared_ptr param = + std::make_shared(PROTOCOL_COMMAND::ASK_GET_PHOTOSENSITIVITY); + std::shared_ptr handle = ProtocolHandle::CreateProtocolData(param); + return WriteProtocolData( + handle->GetProtocolDataBuff(), handle->GetProtocolDataLength(), context, handle->GetSerialNumber()); +} +void McuProtocol::DataHandleThread(void) +{ + mThreadRuning = true; + while (mThreadRuning) { + sem_wait(&mSem); + mMutex.lock(); + if (mMcuDataList.size() == 0) { + mMutex.unlock(); + continue; + } + SingleMcuPacket packet = mMcuDataList.front(); + mMcuDataList.pop_front(); + mMutex.unlock(); + ProtocolHandle::ProtocolAnalysis(packet.mBuf, packet.mLength); + free((void *)packet.mBuf); + } +} +void McuProtocol::ReplyOtherSideSendIpcMission(const ReplyResult &result, const unsigned int &serialNumber) +{ + std::shared_ptr NULL_CONTEXT; + std::shared_ptr param = std::make_shared>( + PROTOCOL_COMMAND::REPLY_OTHER_SIDE_ASK_SEND_IPC_MISSION, static_cast(result)); + param->mSerialNumber = serialNumber; + std::shared_ptr handle = ProtocolHandle::CreateProtocolData(param); + WriteProtocolData( + handle->GetProtocolDataBuff(), handle->GetProtocolDataLength(), NULL_CONTEXT, handle->GetSerialNumber()); +} +void McuProtocol::ReplyOtherSideSendHearBeat(const ReplyResult &result, const unsigned int &serialNumber) +{ + std::shared_ptr NULL_CONTEXT; + std::shared_ptr param = + std::make_shared(PROTOCOL_COMMAND::REPLY_OTHER_SIDE_ASK_SEND_HEART_BEAT); + param->mSerialNumber = serialNumber; + std::shared_ptr handle = ProtocolHandle::CreateProtocolData(param); + WriteProtocolData( + handle->GetProtocolDataBuff(), handle->GetProtocolDataLength(), NULL_CONTEXT, handle->GetSerialNumber()); +} +void McuProtocol::ReplyOtherSideSendGetIntervalStart(const ReplyResult &result, const unsigned int &serialNumber, + const unsigned char &hour, const unsigned char &min, + const unsigned char &second) +{ + std::shared_ptr NULL_CONTEXT; + SetTime interval(hour, min, second); + std::shared_ptr param = + std::make_shared>(PROTOCOL_COMMAND::REPLY_OTHER_SIDE_ASK_GET_INTERVAL_START, interval); + param->mSerialNumber = serialNumber; + std::shared_ptr handle = ProtocolHandle::CreateProtocolData(param); + WriteProtocolData( + handle->GetProtocolDataBuff(), handle->GetProtocolDataLength(), NULL_CONTEXT, handle->GetSerialNumber()); +} +void McuProtocol::ReplyOtherSideSendGetDateTime(const ReplyResult &result, const unsigned int &serialNumber, + const unsigned short &year, const unsigned char &mon, + const unsigned char &day, const unsigned char &hour, + const unsigned char &min, const unsigned char &second) +{ + std::shared_ptr NULL_CONTEXT; + SetDateTime dateTime(year - 1970, mon, day, hour, min, second); + std::shared_ptr param = + std::make_shared>(PROTOCOL_COMMAND::REPLY_OTHER_SIDE_ASK_GET_DATE_TIME, dateTime); + param->mSerialNumber = serialNumber; + std::shared_ptr handle = ProtocolHandle::CreateProtocolData(param); + WriteProtocolData( + handle->GetProtocolDataBuff(), handle->GetProtocolDataLength(), NULL_CONTEXT, handle->GetSerialNumber()); +} +void McuProtocol::ReplyOtherSideSendGetPirSensitivity(const ReplyResult &result, const unsigned int &serialNumber, + const unsigned char &sensitivity) +{ + std::shared_ptr NULL_CONTEXT; + std::shared_ptr param = std::make_shared>( + PROTOCOL_COMMAND::REPLY_OTHER_SIDE_ASK_GET_PIR_SENSITIVITY, static_cast(sensitivity)); + param->mSerialNumber = serialNumber; + std::shared_ptr handle = ProtocolHandle::CreateProtocolData(param); + WriteProtocolData( + handle->GetProtocolDataBuff(), handle->GetProtocolDataLength(), NULL_CONTEXT, handle->GetSerialNumber()); +} +size_t McuProtocol::GetKeyHeadLength(void) +{ + return ProtocolHandle::GetKeyHeadLength(); +} +StatusCode McuProtocol::GetDataLength(const void *keyHead, const size_t &headLength, size_t &dataLength) +{ + return ProtocolHandle::GetDataLength(keyHead, headLength, dataLength); +} +void McuProtocol::PushMcuData(const void *buf, const size_t &length) +{ + char *data = (char *)malloc(length); + if (nullptr == data) { + LogError("malloc failed, PushMcuData failed.\n"); + return; + } + LogInfo("PushMcuData\n"); + memcpy(data, buf, length); + ProtocolHandle::PrintHexadecimalData(data, length, "PushMcuData="); + std::lock_guard locker(mMutex); + SingleMcuPacket packet(data, length); + mMcuDataList.push_back(packet); + sem_post(&mSem); +} +StatusCode McuProtocol::CheckHeader(void *header, size_t &headLength) +{ + return ProtocolHandle::CheckHeader(header, headLength); +} +StatusCode McuProtocol::WriteProtocolData(const void *buff, const size_t buffLength, + std::shared_ptr &context, const unsigned int &serialNumber) +{ + size_t length = WriteData(buff, buffLength, context, serialNumber); + if (buffLength == length) { + return CreateStatusCode(STATUS_CODE_OK); + } + return CreateStatusCode(STATUS_CODE_NOT_OK); +} \ No newline at end of file diff --git a/utils/McuProtocol/src/McuProtocolMakePtr.cpp b/utils/McuProtocol/src/McuProtocolMakePtr.cpp new file mode 100644 index 0000000..a1c7625 --- /dev/null +++ b/utils/McuProtocol/src/McuProtocolMakePtr.cpp @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2023 Fancy Code. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "McuProtocolMakePtr.h" +#include "ILog.h" +#include "LittleEndianHandle.h" +#include +std::shared_ptr &McuProtocolMakePtr::GetInstance(std::shared_ptr *impl) +{ + static auto instance = std::make_shared(); + if (impl) { + if (instance.use_count() == 1) { + LogInfo("Instance changed succeed.\n"); + instance = *impl; + } + else { + LogError("Can't changing the instance becase of using by some one.\n"); + } + } + return instance; +} +McuProtocolMakePtr::McuProtocolMakePtr() +{ + mByteOrder = 0x00; +} +const StatusCode McuProtocolMakePtr::CreateProtocolHandle(std::shared_ptr &impl, + const std::shared_ptr ¶m) +{ + std::shared_ptr tmp; + char byteOrder = ProtocolHandle::GetByteOrder(); + if (ORDER_BIG_ENDIAN == byteOrder) { + tmp = std::make_shared(param); + } + else { + tmp = std::make_shared(param); + } + impl = tmp; + return CreateStatusCode(STATUS_CODE_OK); +} +const StatusCode McuProtocolMakePtr::CreateProtocolHandle(std::shared_ptr &impl, const void *data, + const size_t &length) +{ + std::shared_ptr tmp; + char byteOrder = ProtocolHandle::GetByteOrder(); + if (ORDER_BIG_ENDIAN == byteOrder) { + tmp = std::make_shared(data, length); + } + else { + tmp = std::make_shared(data, length); + } + impl = tmp; + return CreateStatusCode(STATUS_CODE_OK); +} \ No newline at end of file diff --git a/utils/McuProtocol/src/McuProtocolMakePtr.h b/utils/McuProtocol/src/McuProtocolMakePtr.h new file mode 100644 index 0000000..8265b2a --- /dev/null +++ b/utils/McuProtocol/src/McuProtocolMakePtr.h @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2023 Fancy Code. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef MCU_PROTOCOL_MAKE_PTR_H +#define MCU_PROTOCOL_MAKE_PTR_H +#include "ProtocolHandle.h" +#include "StatusCode.h" +#include +class McuProtocolMakePtr +{ +public: + McuProtocolMakePtr(); + virtual ~McuProtocolMakePtr() = default; + static std::shared_ptr &GetInstance(std::shared_ptr *impl = nullptr); + virtual const StatusCode CreateProtocolHandle(std::shared_ptr &impl, + const std::shared_ptr ¶m); + virtual const StatusCode CreateProtocolHandle(std::shared_ptr &impl, const void *data, + const size_t &length); + +private: + char mByteOrder; +}; +#endif // !MCU_PROTOCOL_MAKE_PTR_H \ No newline at end of file diff --git a/utils/McuProtocol/src/ProtocolHandle.cpp b/utils/McuProtocol/src/ProtocolHandle.cpp new file mode 100644 index 0000000..ea453b4 --- /dev/null +++ b/utils/McuProtocol/src/ProtocolHandle.cpp @@ -0,0 +1,403 @@ +/* + * Copyright (c) 2023 Fancy Code. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "ProtocolHandle.h" +#include "ILog.h" +#include "McuProtocol.h" +#include "McuProtocolMakePtr.h" +#include "ModBusCRC16.h" +#include +#include +using std::placeholders::_1; +// using std::placeholders::_2; +// using std::placeholders::_3; +// using std::placeholders::_4; +unsigned int ProtocolHandle::mSerialNumber = 1; +std::mutex ProtocolHandle::mMutex; +ProtocolHandle::ProtocolHandle(const std::shared_ptr ¶m) : mParam(param) +{ + mProtocolData = nullptr; + mProtocolDataLength = 0; + mMakePacketFunc[ASK_IPC_MISSION] = std::bind(&ProtocolHandle::MakeAskIpcMissionPacket, this, _1); + mMakePacketFunc[ASK_CUT_OFF_PWOER_SUPPLY] = std::bind(&ProtocolHandle::MakeAskCutOffPowerSupplyPacket, this, _1); + mMakePacketFunc[ASK_FEED_WATCH_DOG] = std::bind(&ProtocolHandle::MakeAskFeedWatchDogPacket, this, _1); + mMakePacketFunc[ASK_SET_FEEDING_CYCLE] = std::bind(&ProtocolHandle::MakeAskSetFeedingCyclePacket, this, _1); + mMakePacketFunc[ASK_SET_DATE_TIME] = std::bind(&ProtocolHandle::MakeAskSetDateTimePacket, this, _1); + mMakePacketFunc[ASK_SET_PIR_SENSITIVITY] = std::bind(&ProtocolHandle::MakeAskSetPirSensitivityPacket, this, _1); + mMakePacketFunc[ASK_CONTORL_INFRARED_LIGHT] = + std::bind(&ProtocolHandle::MakeAskControlInfraredLightPacket, this, _1); + mMakePacketFunc[ASK_GET_PHOTOSENSITIVITY] = std::bind(&ProtocolHandle::MakeAskGetPhotosensitivityPacket, this, _1); + /**************************************************************************************************************************/ + mMakePacketFunc[REPLY_OTHER_SIDE_ASK_SEND_IPC_MISSION] = + std::bind(&ProtocolHandle::MakeReplyOtherSideSendIpcMissionPacket, this, _1); + mMakePacketFunc[REPLY_OTHER_SIDE_ASK_SEND_HEART_BEAT] = + std::bind(&ProtocolHandle::MakeReplyOtherSideSendHeartBeatPacket, this, _1); + mMakePacketFunc[REPLY_OTHER_SIDE_ASK_GET_INTERVAL_START] = + std::bind(&ProtocolHandle::MakeReplyOtherSideGetIntervalStartPacket, this, _1); + mMakePacketFunc[REPLY_OTHER_SIDE_ASK_GET_DATE_TIME] = + std::bind(&ProtocolHandle::MakeReplyOtherSideGetDateTimePacket, this, _1); + mMakePacketFunc[REPLY_OTHER_SIDE_ASK_GET_PIR_SENSITIVITY] = + std::bind(&ProtocolHandle::MakeReplyOtherSideGetPirSensitivityPacket, this, _1); +} +ProtocolHandle::ProtocolHandle(const void *data, const size_t &length) +{ + mProtocolData = (unsigned char *)malloc(length); + if (nullptr != mProtocolData) { + memcpy(mProtocolData, data, length); + } + mProtocolDataLength = length; + mAnalyzePacketFunc[REPLY_IPC_MISSION] = std::bind(&ProtocolHandle::AnalyzeReplyIpcMissionPacket, this, _1); + mAnalyzePacketFunc[REPLY_SET_FEEDING_CYCLE] = std::bind(&ProtocolHandle::AnalyzeReplyResultPacket, this, _1); + mAnalyzePacketFunc[REPLY_SET_DATE_TIME] = std::bind(&ProtocolHandle::AnalyzeReplyResultPacket, this, _1); + mAnalyzePacketFunc[REPLY_SET_PIR_SENSITIVITY] = std::bind(&ProtocolHandle::AnalyzeReplyResultPacket, this, _1); + mAnalyzePacketFunc[REPLY_CONTORL_INFRARED_LIGHT] = std::bind(&ProtocolHandle::AnalyzeReplyResultPacket, this, _1); + mAnalyzePacketFunc[REPLY_GET_PHOTOSENSITIVITY] = std::bind(&ProtocolHandle::AnalyzeReplyResultPacket, this, _1); + /**************************************************************************************************************************/ + mAnalyzePacketFunc[OTHER_SIDE_ASK_SEND_IPC_MISSION] = + std::bind(&ProtocolHandle::AnalyzeOtherSideSendIpcMissionPacket, this, _1); + mAnalyzePacketFunc[OTHER_SIDE_ASK_SEND_HEART_BEAT] = + std::bind(&ProtocolHandle::AnalyzeOtherSideSendHeartBeatPacket, this, _1); + mAnalyzePacketFunc[OTHER_SIDE_ASK_GET_INTERVAL_START] = + std::bind(&ProtocolHandle::AnalyzeOtherSideSendGetIntervalStart, this, _1); + mAnalyzePacketFunc[OTHER_SIDE_ASK_GET_DATE_TIME] = + std::bind(&ProtocolHandle::AnalyzeOtherSideSendGetDataTime, this, _1); + mAnalyzePacketFunc[OTHER_SIDE_ASK_GET_PIR_SENSITIVITY] = + std::bind(&ProtocolHandle::AnalyzeOtherSideSendGetPirSensitivity, this, _1); +} +ProtocolHandle::~ProtocolHandle() +{ + if (nullptr != mProtocolData) { + free(mProtocolData); + mProtocolData = nullptr; + } +} +ProtocolPacket ProtocolHandle::CreatePocketWithSerialNumber(void) +{ + std::lock_guard locker(mMutex); + ProtocolPacket packet; + memset(&packet, 0, sizeof(ProtocolPacket)); + if (SERIAL_NUMBER_NEED_TO_MAKE_A_LOCAL_ONE == mProtocolSerialNumber) { + packet.mSerialNumber = mSerialNumber; + mProtocolSerialNumber = packet.mSerialNumber; + mSerialNumber++; + } + else { + packet.mSerialNumber = mProtocolSerialNumber; + } + return packet; +} +void ProtocolHandle::MallocPacketDataBuff(const void *data, const size_t dataLength, const short &command) +{ + size_t packetLength = KEY_HEAD_LENGTH + dataLength + CHECK_CODE_LENGTH; + mProtocolData = (unsigned char *)malloc(packetLength); + if (nullptr == mProtocolData) { + LogError("malloc failed, MakeAskIpcMissionPacket return.\n"); + return; + } + ProtocolPacket packet = CreatePocketWithSerialNumber(); + packet.mHead = PROTOCOL_HEAD; + packet.mCommand = command; + packet.mLength = packetLength; + BigEndianConversion(packet); + memcpy(mProtocolData, &packet, KEY_HEAD_LENGTH); + memcpy(mProtocolData + KEY_HEAD_LENGTH, data, dataLength); + packet.mCheckCode = calculate_check_sum(mProtocolData, packetLength - CHECK_CODE_LENGTH); + packet.mCheckCode = BigEndianConversion(packet.mCheckCode); + memcpy(mProtocolData + packetLength - CHECK_CODE_LENGTH, &packet.mCheckCode, CHECK_CODE_LENGTH); + mProtocolDataLength = packetLength; + ProtocolHandle::PrintHexadecimalData(mProtocolData, mProtocolDataLength, "Make protocol packet:"); +} +void ProtocolHandle::MakeProtocolPacket(const std::shared_ptr ¶m) +{ + if (mProtocolData != nullptr) { + LogError("Something wrong happened, make packet failed.\n"); + return; + } + mProtocolSerialNumber = param->mSerialNumber; + std::map::iterator iter; + iter = mMakePacketFunc.find(param->mCommand); + if (iter != mMakePacketFunc.end()) { + mMakePacketFunc[param->mCommand](param); + } + else { + LogError("Unknown command.\n"); + } +} +void ProtocolHandle::MakeNoUserDataPacket(const std::shared_ptr ¶m) +{ + size_t packetLength = KEY_HEAD_LENGTH + CHECK_CODE_LENGTH; + mProtocolData = (unsigned char *)malloc(packetLength); + if (nullptr == mProtocolData) { + LogError("malloc failed, MakeAskIpcMissionPacket return.\n"); + return; + } + ProtocolPacket packet = CreatePocketWithSerialNumber(); + packet.mHead = PROTOCOL_HEAD; + packet.mCommand = param->mCommand; + packet.mLength = packetLength; + BigEndianConversion(packet); + memcpy(mProtocolData, &packet, KEY_HEAD_LENGTH); + packet.mCheckCode = calculate_check_sum(mProtocolData, packetLength - CHECK_CODE_LENGTH); + packet.mCheckCode = BigEndianConversion(packet.mCheckCode); + memcpy(mProtocolData + KEY_HEAD_LENGTH, &packet.mCheckCode, CHECK_CODE_LENGTH); + mProtocolDataLength = packetLength; + if (param->mCommand != ASK_FEED_WATCH_DOG) { + ProtocolHandle::PrintHexadecimalData( + mProtocolData, mProtocolDataLength - CHECK_CODE_LENGTH, "MakeNoUserDataPacket:"); + } +} +void ProtocolHandle::MakeAskIpcMissionPacket(const std::shared_ptr ¶m) +{ + MakeNoUserDataPacket(param); +} +void ProtocolHandle::MakeAskCutOffPowerSupplyPacket(const std::shared_ptr ¶m) +{ + MakeNoUserDataPacket(param); +} +void ProtocolHandle::MakeAskFeedWatchDogPacket(const std::shared_ptr ¶m) +{ + MakeNoUserDataPacket(param); +} +void ProtocolHandle::MakeAskSetFeedingCyclePacket(const std::shared_ptr ¶m) +{ + MakeProtocolData(param); +} +void ProtocolHandle::MakeAskSetDateTimePacket(const std::shared_ptr ¶m) +{ + MakeProtocolData(param); +} +void ProtocolHandle::MakeAskSetPirSensitivityPacket(const std::shared_ptr ¶m) +{ + MakeProtocolData(param); +} +void ProtocolHandle::MakeReplyOtherSideSendIpcMissionPacket(const std::shared_ptr ¶m) +{ + MakeProtocolData(param); +} +void ProtocolHandle::MakeReplyOtherSideSendHeartBeatPacket(const std::shared_ptr ¶m) +{ + MakeNoUserDataPacket(param); +} +void ProtocolHandle::MakeAskControlInfraredLightPacket(const std::shared_ptr ¶m) +{ + MakeProtocolData(param); +} +void ProtocolHandle::MakeAskGetPhotosensitivityPacket(const std::shared_ptr ¶m) +{ + MakeNoUserDataPacket(param); +} +void ProtocolHandle::MakeReplyOtherSideGetIntervalStartPacket(const std::shared_ptr ¶m) +{ + MakeProtocolData(param); +} +void ProtocolHandle::MakeReplyOtherSideGetDateTimePacket(const std::shared_ptr ¶m) +{ + MakeProtocolData(param); +} +void ProtocolHandle::MakeReplyOtherSideGetPirSensitivityPacket(const std::shared_ptr ¶m) +{ + MakeProtocolData(param); +} +void ProtocolHandle::AnalyzeProtocolPacket(void) +{ + ProtocolPacket packet = {0}; + memcpy(&packet, mProtocolData, KEY_HEAD_LENGTH); + memcpy(&packet.mCheckCode, + (unsigned char *)mProtocolData + mProtocolDataLength - sizeof(unsigned short), + sizeof(unsigned short)); + HostByteOrderConversion(packet); + if (CheckoutTheCheckCode(packet) == false) { + LogError("CheckoutTheCheckCode failed.\n"); + return; + } + /** + * @brief unsigned int number = packet.mSerialNumber This line of code is for + * avoiding errors: runtime error: reference binding to misaligned address xxxx + */ + mProtocolSerialNumber = packet.mSerialNumber; + LogInfo("AnalyzeProtocolPacket, command = 0x%04X\n", packet.mCommand); + PROTOCOL_COMMAND command = static_cast(packet.mCommand); + std::map::iterator iter; + iter = mAnalyzePacketFunc.find(command); + if (iter != mAnalyzePacketFunc.end()) { + mAnalyzePacketFunc[command](packet); + } + else { + LogError("Unknown command.\n"); + } +} +unsigned char ProtocolHandle::ReplyOneBytePacketResult(const ProtocolPacket &packet) +{ + constexpr unsigned char UNKNOWN_RESULT = 0xFF; + constexpr unsigned int PROTOCOL_DATA_START_ADDRESS = KEY_HEAD_LENGTH; + unsigned char replyResult = UNKNOWN_RESULT; + replyResult = mProtocolData[PROTOCOL_DATA_START_ADDRESS]; + LogInfo("Other side send: result = 0x%02X\n", replyResult); + return replyResult; +} +void ProtocolHandle::AnalyzeReplyResultPacket(const ProtocolPacket &packet) +{ + LogInfo("AnalyzeReplyResultPacket\n"); + unsigned char replyResult = ReplyOneBytePacketResult(packet); + VProtocolRecv::GetInstance()->OnlyResultReply(mProtocolSerialNumber, static_cast(replyResult)); +} +void ProtocolHandle::AnalyzeReplyIpcMissionPacket(const ProtocolPacket &packet) +{ + LogInfo("AnalyzeReplyIpcMissionPacket\n"); + unsigned char ipcMission = ReplyOneBytePacketResult(packet); + VProtocolRecv::GetInstance()->GetIpcMissionReply(mProtocolSerialNumber, ipcMission); +} +void ProtocolHandle::AnalyzeOtherSideSendIpcMissionPacket(const ProtocolPacket &packet) +{ + LogInfo("AnalyzeOtherSideSendIpcMissionPacket\n"); + unsigned char ipcMission = ReplyOneBytePacketResult(packet); + VProtocolRecv::GetInstance()->OtherSideSendIpcMission(mProtocolSerialNumber, ipcMission); +} +void ProtocolHandle::AnalyzeOtherSideSendHeartBeatPacket(const ProtocolPacket &packet) +{ + LogInfo("AnalyzeOtherSideSendHeartBeatPacket\n"); + VProtocolRecv::GetInstance()->OtherSideSendHearBeat(mProtocolSerialNumber); +} +void ProtocolHandle::AnalyzeOtherSideSendGetIntervalStart(const ProtocolPacket &packet) +{ + LogInfo("AnalyzeOtherSideSendGetIntervalStart\n"); + VProtocolRecv::GetInstance()->OtherSideSendGetIntervalStart(mProtocolSerialNumber); +} +void ProtocolHandle::AnalyzeOtherSideSendGetDataTime(const ProtocolPacket &packet) +{ + LogInfo("AnalyzeOtherSideSendGetDataTime\n"); + VProtocolRecv::GetInstance()->OtherSideSendGetDateTime(mProtocolSerialNumber); +} +void ProtocolHandle::AnalyzeOtherSideSendGetPirSensitivity(const ProtocolPacket &packet) +{ + LogInfo("AnalyzeOtherSideSendGetPirSensitivity\n"); + VProtocolRecv::GetInstance()->OtherSideSendGetPirSensitivity(mProtocolSerialNumber); +} +bool ProtocolHandle::CheckoutTheCheckCode(const ProtocolPacket &packet) +{ + ProtocolHandle::PrintHexadecimalData(&packet.mCheckCode, CHECK_CODE_LENGTH, "CheckoutTheCheckCode:"); + short code = calculate_check_sum(mProtocolData, mProtocolDataLength - CHECK_CODE_LENGTH); + if (code == packet.mCheckCode) { + return true; + } + return false; +} + +void ProtocolHandle::Init(void) +{ + mSerialNumber = 1; +} +void ProtocolHandle::UnInit(void) +{ + mSerialNumber = 1; +} +std::shared_ptr ProtocolHandle::CreateProtocolData(const std::shared_ptr ¶m) +{ + std::shared_ptr handle; + McuProtocolMakePtr::GetInstance()->CreateProtocolHandle(handle, param); + if (handle) { + handle->MakeProtocolPacket(param); + } + return handle; +} +void ProtocolHandle::ProtocolAnalysis(const void *data, const size_t &length) +{ + LogInfo("ProtocolAnalysis\n"); + std::shared_ptr handle; + McuProtocolMakePtr::GetInstance()->CreateProtocolHandle(handle, data, length); + if (handle) { + handle->AnalyzeProtocolPacket(); + } +} +size_t ProtocolHandle::GetKeyHeadLength(void) +{ + return KEY_HEAD_LENGTH; +} +StatusCode ProtocolHandle::CheckHeader(void *header, size_t &headLength) +{ + unsigned char *data = (unsigned char *)header; + unsigned short protocolHead = 0; + protocolHead = PROTOCOL_HEAD; + // LogInfo("head = 0x%02X, 0x%02X\n", protocolHead >> 8, protocolHead & 0x00FF); + for (size_t i = 0; i < headLength; i++) { + if ((protocolHead >> 8 == data[i]) && ((protocolHead & 0x00FF) == data[i + 1])) { + headLength -= i; + if (0 == i) { + return CreateStatusCode(STATUS_CODE_OK); + } + unsigned char fixedHeader[KEY_HEAD_LENGTH] = {0}; + memcpy(fixedHeader, data + i, headLength); + memcpy(header, fixedHeader, headLength); + LogWarning("Protocol head unmatch, unknow data before head. headLength =%d\n", headLength); + ProtocolHandle::PrintHexadecimalData(header, headLength, "Fixed header:"); + return CreateStatusCode(STATUS_CODE_NOT_OK); + } + } + LogWarning("Protocol head unmatch, head not found.\n"); + return CreateStatusCode(STATUS_CODE_NOT_OK); +} +StatusCode ProtocolHandle::GetDataLength(const void *keyHead, const size_t &headLength, size_t &dataLength) +{ + if (KEY_HEAD_LENGTH != headLength) { + LogError("key head buf error.\n"); + return CreateStatusCode(STATUS_CODE_NOT_OK); + } + unsigned short headNum = PROTOCOL_HEAD; + char byteOrder = ProtocolHandle::GetByteOrder(); + if (ORDER_LITTLE_ENDIAN == byteOrder) { + headNum = htons(PROTOCOL_HEAD); + } + unsigned char head[3] = {0}; + memcpy(head, &headNum, sizeof(unsigned short)); + if (strstr((const char *)keyHead, (const char *)head) == nullptr) { + LogError("Protocol head unmatch.\n"); + return CreateStatusCode(STATUS_CODE_NOT_OK); + } + ProtocolPacket packet = {0}; + memcpy(&packet, keyHead, headLength); + if (ORDER_LITTLE_ENDIAN == byteOrder) { + packet.mLength = htons(packet.mLength); + } + dataLength = packet.mLength; + return CreateStatusCode(STATUS_CODE_OK); +} +char ProtocolHandle::GetByteOrder(void) +{ + static char byteOrder = 0x00; + if (0x00 != byteOrder) { + return byteOrder; + } + unsigned int value = 0x12345678; + unsigned int networkOrder = htonl(value); + if (value == networkOrder) { + LogInfo("Big Endian.\n"); + byteOrder = ORDER_BIG_ENDIAN; + } + else { + LogInfo("Little Endian.\n"); + byteOrder = ORDER_LITTLE_ENDIAN; + } + return byteOrder; +} +void ProtocolHandle::PrintHexadecimalData(const void *buf, const size_t &bufLength, const char *log) +{ + if (log) { + printf("%s", log); + } + printf(" {0x%02X", *(unsigned char *)buf); + for (size_t i = 1; i < bufLength; i++) { + printf(", 0x%02X", *((unsigned char *)buf + i)); + } + printf("}\n"); +} \ No newline at end of file diff --git a/utils/McuProtocol/src/ProtocolHandle.h b/utils/McuProtocol/src/ProtocolHandle.h new file mode 100644 index 0000000..d8fe6cb --- /dev/null +++ b/utils/McuProtocol/src/ProtocolHandle.h @@ -0,0 +1,244 @@ +/* + * Copyright (c) 2023 Fancy Code. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef PROTOCOL_HANDLE_H +#define PROTOCOL_HANDLE_H +#include "ILog.h" +#include "StatusCode.h" +#include +#include +#include +#include +constexpr char ORDER_BIG_ENDIAN = 0x01; +constexpr char ORDER_LITTLE_ENDIAN = 0x02; +constexpr size_t KEY_HEAD_LENGTH = sizeof(short) + sizeof(unsigned int) + sizeof(short) + sizeof(short); +constexpr size_t CHECK_CODE_LENGTH = sizeof(short); +constexpr unsigned int SERIAL_NUMBER_NEED_TO_MAKE_A_LOCAL_ONE = 0; +#pragma pack(1) +typedef struct protocol_packet +{ + short mHead; + unsigned int mSerialNumber; + short mCommand; + short mLength; + // char *mData; + short mCheckCode; +} ProtocolPacket; +#pragma pack() +constexpr unsigned short PROTOCOL_HEAD = 0xFAC1; +enum PROTOCOL_COMMAND +{ + ASK_IPC_MISSION = 0x8101, + REPLY_IPC_MISSION = 0x0101, + ASK_CUT_OFF_PWOER_SUPPLY = 0x8102, + ASK_FEED_WATCH_DOG = 0x8103, + ASK_SET_FEEDING_CYCLE = 0x8104, + REPLY_SET_FEEDING_CYCLE = 0x0104, + ASK_SET_DATE_TIME = 0x8107, + REPLY_SET_DATE_TIME = 0x0107, + ASK_SET_PIR_SENSITIVITY = 0x8108, + REPLY_SET_PIR_SENSITIVITY = 0x0108, + ASK_CONTORL_INFRARED_LIGHT = 0x810A, // Temporarily cancelled, reserved. + REPLY_CONTORL_INFRARED_LIGHT = 0x010A, // Temporarily cancelled, reserved. + ASK_GET_PHOTOSENSITIVITY = 0x810B, // Temporarily cancelled, reserved. + REPLY_GET_PHOTOSENSITIVITY = 0x010B, // Temporarily cancelled, reserved. + /** + * @brief The protocol starting from here is a request sent from the other end. + * + */ + REPLY_OTHER_SIDE_ASK_SEND_IPC_MISSION = 0xC101, + OTHER_SIDE_ASK_SEND_IPC_MISSION = 0x4101, + REPLY_OTHER_SIDE_ASK_SEND_HEART_BEAT = 0xC102, // Temporarily cancelled, reserved. + OTHER_SIDE_ASK_SEND_HEART_BEAT = 0x4102, // Temporarily cancelled, reserved. + REPLY_OTHER_SIDE_ASK_GET_INTERVAL_START = 0xC106, + OTHER_SIDE_ASK_GET_INTERVAL_START = 0x4106, + REPLY_OTHER_SIDE_ASK_GET_DATE_TIME = 0xC107, + OTHER_SIDE_ASK_GET_DATE_TIME = 0x4107, + REPLY_OTHER_SIDE_ASK_GET_PIR_SENSITIVITY = 0xC108, + OTHER_SIDE_ASK_GET_PIR_SENSITIVITY = 0x4108, + PROTOCOL_COMMAND_END +}; +constexpr unsigned char ZERO_MEANS_SHUTDOWN_WATCH_DOG = 0x00; +#pragma pack(1) +typedef struct watch_dog_param +{ + watch_dog_param(const unsigned char &hour, const unsigned char &min, const unsigned char &second) + : mHour(hour), mMin(min), mSecond(second) + { + } + const unsigned char mHour; + const unsigned char mMin; + const unsigned char mSecond; +} SetTime; +typedef struct set_date_time +{ + set_date_time(const unsigned char &year, const unsigned char &mon, const unsigned char &day, + const unsigned char &hour, const unsigned char &min, const unsigned char &second) + : mYear(year), mMon(mon), mDay(day), mHour(hour), mMin(min), mSecond(second) + { + } + const unsigned char mYear; + const unsigned char mMon; + const unsigned char mDay; + const unsigned char mHour; + const unsigned char mMin; + const unsigned char mSecond; +} SetDateTime; +#pragma pack() +class VProtocolParam +{ +public: + VProtocolParam(const PROTOCOL_COMMAND &command) : mCommand(command) + { + mSerialNumber = SERIAL_NUMBER_NEED_TO_MAKE_A_LOCAL_ONE; + } + virtual ~VProtocolParam() = default; + const PROTOCOL_COMMAND mCommand; + unsigned int mSerialNumber; +}; +template +class ProtocolParam : public VProtocolParam +{ + +public: + ProtocolParam(const PROTOCOL_COMMAND &command, T &value) : VProtocolParam(command), mData(value) + { + } + virtual ~ProtocolParam() = default; + +public: + /** + * @brief + * Protocol data. + */ + T mData; +}; +using MakePacketFunc = std::function &)>; +using AnalyzePacketFunc = std::function; +/** + * @brief Protocol processing classes need to focus on data size issues, and ProtocolHandle defaults to handling large + * end data. + */ +class ProtocolHandle +{ +public: + ProtocolHandle(const std::shared_ptr ¶m); + ProtocolHandle(const void *data, const size_t &length); + virtual ~ProtocolHandle(); + void *GetProtocolDataBuff(void) + { + return mProtocolData; + } + size_t GetProtocolDataLength(void) + { + return mProtocolDataLength; + } + unsigned int GetSerialNumber(void) + { + return mProtocolSerialNumber; + } + + /** + * @brief These functions assemble scattered data into continuous memory protocol packets and complete the sending + * of protocol packets. + */ +private: + ProtocolPacket CreatePocketWithSerialNumber(void); + void MallocPacketDataBuff(const void *data, const size_t dataLength, const short &command); + void MakeProtocolPacket(const std::shared_ptr ¶m); + void MakeNoUserDataPacket(const std::shared_ptr ¶m); + void MakeAskIpcMissionPacket(const std::shared_ptr ¶m); + void MakeAskCutOffPowerSupplyPacket(const std::shared_ptr ¶m); + void MakeAskFeedWatchDogPacket(const std::shared_ptr ¶m); + void MakeAskSetFeedingCyclePacket(const std::shared_ptr ¶m); + void MakeAskSetDateTimePacket(const std::shared_ptr ¶m); + void MakeAskSetPirSensitivityPacket(const std::shared_ptr ¶m); + void MakeAskControlInfraredLightPacket(const std::shared_ptr ¶m); + void MakeAskGetPhotosensitivityPacket(const std::shared_ptr ¶m); + void MakeReplyOtherSideSendIpcMissionPacket(const std::shared_ptr ¶m); + void MakeReplyOtherSideSendHeartBeatPacket(const std::shared_ptr ¶m); + void MakeReplyOtherSideGetIntervalStartPacket(const std::shared_ptr ¶m); + void MakeReplyOtherSideGetDateTimePacket(const std::shared_ptr ¶m); + void MakeReplyOtherSideGetPirSensitivityPacket(const std::shared_ptr ¶m); + template + void MakeProtocolData(const std::shared_ptr ¶m) + { + constexpr int PARAM_DATA_LENGTH = sizeof(T); + std::shared_ptr> SetParam = std::dynamic_pointer_cast>(param); + if (!SetParam) { + LogError("MakeProtocolData::Invalid param.\n"); + return; + } + MallocPacketDataBuff(&(SetParam->mData), PARAM_DATA_LENGTH, param->mCommand); + } + +private: + /** + * @brief These function implementations parse the received frame by frame continuous data into the data required by + * the application, completing the protocol unpacking. + */ + void AnalyzeProtocolPacket(void); + unsigned char ReplyOneBytePacketResult(const ProtocolPacket &packet); + void AnalyzeReplyResultPacket(const ProtocolPacket &packet); + void AnalyzeReplyIpcMissionPacket(const ProtocolPacket &packet); + void AnalyzeOtherSideSendIpcMissionPacket(const ProtocolPacket &packet); + void AnalyzeOtherSideSendHeartBeatPacket(const ProtocolPacket &packet); + void AnalyzeOtherSideSendGetIntervalStart(const ProtocolPacket &packet); + void AnalyzeOtherSideSendGetDataTime(const ProtocolPacket &packet); + void AnalyzeOtherSideSendGetPirSensitivity(const ProtocolPacket &packet); + +private: + virtual void BigEndianConversion(ProtocolPacket &packet) + { + } + virtual short BigEndianConversion(const short &number) + { + return number; + } + virtual void HostByteOrderConversion(ProtocolPacket &packet) + { + } + virtual bool CheckoutTheCheckCode(const ProtocolPacket &packet); + +protected: + std::shared_ptr mParam; + std::map mMakePacketFunc; + std::map mAnalyzePacketFunc; + unsigned char *mProtocolData; + size_t mProtocolDataLength; + /** + * @brief Single protocol package serial number. + * + */ + unsigned int mProtocolSerialNumber; + /** + * @brief The global protocol packet serial number is used to bind the data content of the one question one answer + * protocol. + * + */ + static unsigned int mSerialNumber; + static std::mutex mMutex; + +public: + static void Init(void); + static void UnInit(void); + static std::shared_ptr CreateProtocolData(const std::shared_ptr ¶m); + static void ProtocolAnalysis(const void *data, const size_t &length); + static size_t GetKeyHeadLength(void); + static StatusCode CheckHeader(void *header, size_t &headLength); + static StatusCode GetDataLength(const void *keyHead, const size_t &headLength, size_t &dataLength); + static char GetByteOrder(void); + static void PrintHexadecimalData(const void *buf, const size_t &bufLength, const char *log = nullptr); +}; +#endif \ No newline at end of file diff --git a/utils/MediaAdapter/CMakeLists.txt b/utils/MediaAdapter/CMakeLists.txt new file mode 100644 index 0000000..48c919b --- /dev/null +++ b/utils/MediaAdapter/CMakeLists.txt @@ -0,0 +1,57 @@ +include(${CMAKE_SOURCE_DIR_IPCSDK}/build/global_config.cmake) +set(EXECUTABLE_OUTPUT_PATH ${EXEC_OUTPUT_PATH}) +set(LIBRARY_OUTPUT_PATH ${LIBS_OUTPUT_PATH}) + +include_directories( + ./src + ./include + # ${UTILS_SOURCE_PATH}/LinuxApi/include + ${UTILS_SOURCE_PATH}/StatusCode/include + ${UTILS_SOURCE_PATH}/ModBusCRC16/include + ${UTILS_SOURCE_PATH}/Log/include +) +# link_directories( +# ${EXTERNAL_SOURCE_PATH}/libconfig/libconfig-1.7.3/lib/.libs +# ) + + + +aux_source_directory(./src SRC_FILES) + +set(TARGET_NAME MediaAdapter) +add_library(${TARGET_NAME} STATIC ${SRC_FILES}) +target_link_libraries(${TARGET_NAME} StatusCode Log) + +if ("${COMPILE_IMPROVE_SUPPORT}" MATCHES "true") +add_custom_target( + MediaAdapter_code_check + COMMAND ${CLANG_TIDY_EXE} + -checks='${CLANG_TIDY_CHECKS}' + --header-filter=.* + --system-headers=false + ${SRC_FILES} + ${CLANG_TIDY_CONFIG} + -p ${PLATFORM_PATH}/cmake-shell + WORKING_DIRECTORY ${UTILS_SOURCE_PATH}/MediaAdapter +) +file(GLOB_RECURSE HEADER_FILES *.h) +add_custom_target( + MediaAdapter_code_format + COMMAND ${CLANG_FORMAT_EXE} + -style=file + -i ${SRC_FILES} ${HEADER_FILES} + WORKING_DIRECTORY ${UTILS_SOURCE_PATH}/MediaAdapter +) +add_custom_command( + TARGET ${TARGET_NAME} + PRE_BUILD + COMMAND make MediaAdapter_code_check + COMMAND make MediaAdapter_code_format + WORKING_DIRECTORY ${PLATFORM_PATH}/cmake-shell/ +) +endif() + +define_file_name(${TARGET_NAME}) + +file(GLOB_RECURSE INSTALL_HEADER_FILES include/*.h) +install(FILES ${INSTALL_HEADER_FILES} DESTINATION include) \ No newline at end of file diff --git a/utils/MediaAdapter/include/MediaAdapter.h b/utils/MediaAdapter/include/MediaAdapter.h new file mode 100644 index 0000000..c9bf0de --- /dev/null +++ b/utils/MediaAdapter/include/MediaAdapter.h @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2023 Fancy Code. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef MEDIA_ADAPTER_H +#define MEDIA_ADAPTER_H +#include "StatusCode.h" +#ifdef __cplusplus +extern "C" { +#endif +enum SENSOR_NUM +{ + SENSOR_NUM_1 = 0, + SENSOR_NUM_2, + SENSOR_NUM_END +}; +void *CreateMediaAdapter(const SENSOR_NUM num); +void TestApi(void *object); +void IMediaAdapterFree(void *object); +#ifdef __cplusplus +} +#endif +#endif \ No newline at end of file diff --git a/utils/MediaAdapter/src/IMediaAdapter.cpp b/utils/MediaAdapter/src/IMediaAdapter.cpp new file mode 100644 index 0000000..c040413 --- /dev/null +++ b/utils/MediaAdapter/src/IMediaAdapter.cpp @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2023 Fancy Code. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "IMediaAdapter.h" +#include "ILog.h" +#include +static const char *MEDIA_ADAPTER_NAME = "media_adapter"; +const char inline *GetMediaAdapterModuleName(void) +{ + return MEDIA_ADAPTER_NAME; +} +std::shared_ptr *NewIMediaAdapter(const SENSOR_NUM &num) +{ + LogInfo("Create the uart device object.\n"); + MeidaAdapter *impl = (MeidaAdapter *)malloc(sizeof(MeidaAdapter)); + MeidaAdapter tmp; + memcpy((void *)impl, (void *)&tmp, sizeof(MeidaAdapter)); + impl->mHeader.mCheckName = MEDIA_ADAPTER_NAME; + impl->mIMediaAdapter = std::make_shared(); + return (std::shared_ptr *)(((char *)impl) + sizeof(MediaAdapterHeader)); +} \ No newline at end of file diff --git a/utils/MediaAdapter/src/IMediaAdapter.h b/utils/MediaAdapter/src/IMediaAdapter.h new file mode 100644 index 0000000..7835c54 --- /dev/null +++ b/utils/MediaAdapter/src/IMediaAdapter.h @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2023 Fancy Code. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef I_MEDIA_ADAPTER_H +#define I_MEDIA_ADAPTER_H +#include "MediaAdapter.h" +#include +class IMediaAdapter +{ +public: + IMediaAdapter() = default; + virtual ~IMediaAdapter() = default; + virtual void TestApi(void) + { + } +}; +typedef struct media_adapter_header +{ + const char *mCheckName; +} MediaAdapterHeader; +typedef struct media_adapter +{ + MediaAdapterHeader mHeader; + std::shared_ptr mIMediaAdapter; +} MeidaAdapter; +const char *GetMediaAdapterModuleName(void); +std::shared_ptr *NewIMediaAdapter(const SENSOR_NUM &num); +#endif \ No newline at end of file diff --git a/utils/MediaAdapter/src/MediaAdapter.cpp b/utils/MediaAdapter/src/MediaAdapter.cpp new file mode 100644 index 0000000..7f0a49e --- /dev/null +++ b/utils/MediaAdapter/src/MediaAdapter.cpp @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2023 Fancy Code. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "MediaAdapter.h" +#include "ILog.h" +#include "IMediaAdapter.h" +void *CreateMediaAdapter(const SENSOR_NUM num) +{ + return NewIMediaAdapter(num); +} +static bool ObjectCheck(void *object) +{ + if (nullptr == object) { + LogError("nullptr object!\n"); + return false; + } + if (*((const char **)(((char *)object) - sizeof(IMediaAdapter))) != GetMediaAdapterModuleName()) { + LogError("Illegal object!\n"); + return false; + } + return true; +} +void TestApi(void *object) +{ + if (nullptr == object) { + LogError("nullptr object!\n"); + return; + } + if (*((const char **)(((char *)object) - sizeof(IMediaAdapter))) != GetMediaAdapterModuleName()) { + LogError("Illegal object!\n"); + return; + } + (*(std::shared_ptr *)object)->TestApi(); +} +void IMediaAdapterFree(void *object) +{ + if (ObjectCheck(object) == true) { + (*(std::shared_ptr *)object).reset(); + free(((char *)object) - sizeof(IMediaAdapter)); + } +} \ No newline at end of file diff --git a/utils/ModBusCRC16/CMakeLists.txt b/utils/ModBusCRC16/CMakeLists.txt new file mode 100644 index 0000000..a60d4dc --- /dev/null +++ b/utils/ModBusCRC16/CMakeLists.txt @@ -0,0 +1,46 @@ + +include(${CMAKE_SOURCE_DIR_IPCSDK}/build/global_config.cmake) +set(EXECUTABLE_OUTPUT_PATH ${EXEC_OUTPUT_PATH}) +set(LIBRARY_OUTPUT_PATH ${LIBS_OUTPUT_PATH}) + +include_directories( + ./src + ./include + ${UTILS_SOURCE_PATH}/ModBusCRC16/include + ${UTILS_SOURCE_PATH}/Log/include +) +#do not rely on any other library +#link_directories( +#) + + + +aux_source_directory(./src SRC_FILES) + +set(TARGET_NAME ModBusCRC16) +add_library(${TARGET_NAME} STATIC ${SRC_FILES}) +target_link_libraries(${TARGET_NAME} Log) + +if ("${COMPILE_IMPROVE_SUPPORT}" MATCHES "true") +add_custom_target( + ModBusCRC16_code_check + COMMAND ${CLANG_TIDY_EXE} + -checks='${CLANG_TIDY_CHECKS}' + --header-filter=.* + --system-headers=false + ${SRC_FILES} + ${CLANG_TIDY_CONFIG} + -p ${PLATFORM_PATH}/cmake-shell + WORKING_DIRECTORY ${UTILS_SOURCE_PATH}/ModBusCRC16 +) +add_custom_command( + TARGET ${TARGET_NAME} + PRE_BUILD + COMMAND make ModBusCRC16_code_check + WORKING_DIRECTORY ${PLATFORM_PATH}/cmake-shell/ +) +endif() +define_file_name(${TARGET_NAME}) + +file(GLOB_RECURSE INSTALL_HEADER_FILES include/*.h) +install(FILES ${INSTALL_HEADER_FILES} DESTINATION include) \ No newline at end of file diff --git a/utils/ModBusCRC16/include/ModBusCRC16.h b/utils/ModBusCRC16/include/ModBusCRC16.h new file mode 100644 index 0000000..38903a0 --- /dev/null +++ b/utils/ModBusCRC16/include/ModBusCRC16.h @@ -0,0 +1,24 @@ +/* + * Copyright (c) 2023 Fancy Code. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef MODBUSCRC16_H +#define MODBUSCRC16_H +#ifdef __cplusplus +extern "C" { +#endif +unsigned short calculate_check_sum(const unsigned char *pData, unsigned short length); +#ifdef __cplusplus +} +#endif +#endif \ No newline at end of file diff --git a/utils/ModBusCRC16/src/ModBusCRC16.cpp b/utils/ModBusCRC16/src/ModBusCRC16.cpp new file mode 100644 index 0000000..688660d --- /dev/null +++ b/utils/ModBusCRC16/src/ModBusCRC16.cpp @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2023 Fancy Code. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "ModBusCRC16.h" +#include +/* Table of CRC values for high–order byte */ +unsigned char chCRCHi[] = { + 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, + 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, + 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, + 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, + 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, + 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, + 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, + 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, + 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, + 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, + 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, + 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, + 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, + 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40}; + +/* Table of CRC values for low–order byte */ +unsigned char chCRCLo[] = { + 0x00, 0xC0, 0xC1, 0x01, 0xC3, 0x03, 0x02, 0xC2, 0xC6, 0x06, 0x07, 0xC7, 0x05, 0xC5, 0xC4, 0x04, 0xCC, 0x0C, 0x0D, + 0xCD, 0x0F, 0xCF, 0xCE, 0x0E, 0x0A, 0xCA, 0xCB, 0x0B, 0xC9, 0x09, 0x08, 0xC8, 0xD8, 0x18, 0x19, 0xD9, 0x1B, 0xDB, + 0xDA, 0x1A, 0x1E, 0xDE, 0xDF, 0x1F, 0xDD, 0x1D, 0x1C, 0xDC, 0x14, 0xD4, 0xD5, 0x15, 0xD7, 0x17, 0x16, 0xD6, 0xD2, + 0x12, 0x13, 0xD3, 0x11, 0xD1, 0xD0, 0x10, 0xF0, 0x30, 0x31, 0xF1, 0x33, 0xF3, 0xF2, 0x32, 0x36, 0xF6, 0xF7, 0x37, + 0xF5, 0x35, 0x34, 0xF4, 0x3C, 0xFC, 0xFD, 0x3D, 0xFF, 0x3F, 0x3E, 0xFE, 0xFA, 0x3A, 0x3B, 0xFB, 0x39, 0xF9, 0xF8, + 0x38, 0x28, 0xE8, 0xE9, 0x29, 0xEB, 0x2B, 0x2A, 0xEA, 0xEE, 0x2E, 0x2F, 0xEF, 0x2D, 0xED, 0xEC, 0x2C, 0xE4, 0x24, + 0x25, 0xE5, 0x27, 0xE7, 0xE6, 0x26, 0x22, 0xE2, 0xE3, 0x23, 0xE1, 0x21, 0x20, 0xE0, 0xA0, 0x60, 0x61, 0xA1, 0x63, + 0xA3, 0xA2, 0x62, 0x66, 0xA6, 0xA7, 0x67, 0xA5, 0x65, 0x64, 0xA4, 0x6C, 0xAC, 0xAD, 0x6D, 0xAF, 0x6F, 0x6E, 0xAE, + 0xAA, 0x6A, 0x6B, 0xAB, 0x69, 0xA9, 0xA8, 0x68, 0x78, 0xB8, 0xB9, 0x79, 0xBB, 0x7B, 0x7A, 0xBA, 0xBE, 0x7E, 0x7F, + 0xBF, 0x7D, 0xBD, 0xBC, 0x7C, 0xB4, 0x74, 0x75, 0xB5, 0x77, 0xB7, 0xB6, 0x76, 0x72, 0xB2, 0xB3, 0x73, 0xB1, 0x71, + 0x70, 0xB0, 0x50, 0x90, 0x91, 0x51, 0x93, 0x53, 0x52, 0x92, 0x96, 0x56, 0x57, 0x97, 0x55, 0x95, 0x94, 0x54, 0x9C, + 0x5C, 0x5D, 0x9D, 0x5F, 0x9F, 0x9E, 0x5E, 0x5A, 0x9A, 0x9B, 0x5B, 0x99, 0x59, 0x58, 0x98, 0x88, 0x48, 0x49, 0x89, + 0x4B, 0x8B, 0x8A, 0x4A, 0x4E, 0x8E, 0x8F, 0x4F, 0x8D, 0x4D, 0x4C, 0x8C, 0x44, 0x84, 0x85, 0x45, 0x87, 0x47, 0x46, + 0x86, 0x82, 0x42, 0x43, 0x83, 0x41, 0x81, 0x80, 0x40}; +/** + * @brief Calculate check sum. + * + * @param pData pointer of data + * @param length length of data + * @return unsigned short + */ +unsigned short calculate_check_sum(const unsigned char *pData, unsigned short length) +{ + unsigned char CRCHi = 0xFF; + unsigned char CRCLo = 0xFF; + int uIndex; + while (length--) { + uIndex = CRCHi ^ *pData++; + CRCHi = CRCLo ^ chCRCHi[uIndex]; + CRCLo = chCRCLo[uIndex]; + } + // printf("CRCHi = 0x%x, CRCLo = 0x%x\n", CRCHi, CRCLo); + return (CRCHi << 8 | CRCLo); +} \ No newline at end of file diff --git a/utils/MultiProcess/CMakeLists.txt b/utils/MultiProcess/CMakeLists.txt new file mode 100644 index 0000000..59ff909 --- /dev/null +++ b/utils/MultiProcess/CMakeLists.txt @@ -0,0 +1,55 @@ +include(${CMAKE_SOURCE_DIR_IPCSDK}/build/global_config.cmake) +set(EXECUTABLE_OUTPUT_PATH ${EXEC_OUTPUT_PATH}) +set(LIBRARY_OUTPUT_PATH ${LIBS_OUTPUT_PATH}) + +include_directories( + ./src + ./include + ${UTILS_SOURCE_PATH}/StatusCode/include + ${UTILS_SOURCE_PATH}/Log/include +) +# link_directories( +# ${EXTERNAL_SOURCE_PATH}/libconfig/libconfig-1.7.3/lib/.libs +# ) + + + +aux_source_directory(./src SRC_FILES) + +set(TARGET_NAME MultiProcess) +add_library(${TARGET_NAME} STATIC ${SRC_FILES}) +target_link_libraries(${TARGET_NAME} StatusCode Log) + +if ("${COMPILE_IMPROVE_SUPPORT}" MATCHES "true") +add_custom_target( + MultiProcess_code_check + COMMAND ${CLANG_TIDY_EXE} + -checks='${CLANG_TIDY_CHECKS}' + --header-filter=.* + --system-headers=false + ${SRC_FILES} + ${CLANG_TIDY_CONFIG} + -p ${PLATFORM_PATH}/cmake-shell + WORKING_DIRECTORY ${UTILS_SOURCE_PATH}/MultiProcess +) +file(GLOB_RECURSE HEADER_FILES *.h) +add_custom_target( + MultiProcess_code_format + COMMAND ${CLANG_FORMAT_EXE} + -style=file + -i ${SRC_FILES} ${HEADER_FILES} + WORKING_DIRECTORY ${UTILS_SOURCE_PATH}/MultiProcess +) +add_custom_command( + TARGET ${TARGET_NAME} + PRE_BUILD + COMMAND make MultiProcess_code_check + COMMAND make MultiProcess_code_format + WORKING_DIRECTORY ${PLATFORM_PATH}/cmake-shell/ +) +endif() + +define_file_name(${TARGET_NAME}) + +file(GLOB_RECURSE INSTALL_HEADER_FILES include/*.h) +install(FILES ${INSTALL_HEADER_FILES} DESTINATION include) \ No newline at end of file diff --git a/utils/MultiProcess/include/MultiProcess.h b/utils/MultiProcess/include/MultiProcess.h new file mode 100644 index 0000000..4832020 --- /dev/null +++ b/utils/MultiProcess/include/MultiProcess.h @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2023 Fancy Code. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef MULTI_PROCESS_H +#define MULTI_PROCESS_H +#include "StatusCode.h" +#ifdef __cplusplus +extern "C" { +#endif +enum PROCESS_TYPE +{ + PROCESS_TYPE_PRIMARY = 0, + PROCESS_TYPE_MINOR, + PROCESS_TYPE_END +}; +typedef struct multi_process MultiProcess; +typedef struct multi_process +{ + const StatusCode (*mMakeSharedData)(MultiProcess *, const unsigned int, const unsigned int); + const StatusCode (*mCleanSharedData)(MultiProcess *); + const StatusCode (*mGetReadableData)(MultiProcess *, void *, const unsigned int); + void (*mSetWritableData)(MultiProcess *, void *, const unsigned int); + void (*mFree)(void *); +} MultiProcess; +MultiProcess *CreateProcessObject(const enum PROCESS_TYPE name, const char *path, const int projectId); +// static inline const StatusCode IMakeSharedData(MultiProcess *object, const unsigned int readableSize, +// const unsigned int writableSize) +// { +// return object->mMakeSharedData(object, readableSize, writableSize); +// } +// static inline const StatusCode ICleanSharedData(MultiProcess *object) { return object->mCleanSharedData(object); } +// static inline const StatusCode IGetReadableData(MultiProcess *object, void *buf, const unsigned int bufLength) +// { +// return object->mGetReadableData(object, buf, bufLength); +// } +// static inline void ISetWritableData(MultiProcess *object, void *buf, const unsigned int bufLength) +// { +// object->mSetWritableData(object, buf, bufLength); +// } +// static inline void IShareDataFree(MultiProcess *object) { object->mFree(object); } +#ifdef __cplusplus +} +#endif +#endif \ No newline at end of file diff --git a/utils/MultiProcess/src/MultiProcess.cpp b/utils/MultiProcess/src/MultiProcess.cpp new file mode 100644 index 0000000..0107a18 --- /dev/null +++ b/utils/MultiProcess/src/MultiProcess.cpp @@ -0,0 +1,15 @@ +/* + * Copyright (c) 2023 Fancy Code. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "MultiProcess.h" \ No newline at end of file diff --git a/utils/README.md b/utils/README.md new file mode 100644 index 0000000..869d9b8 --- /dev/null +++ b/utils/README.md @@ -0,0 +1,13 @@ +# 1. utils工具类库 + +  工具类库,常用的功能单一的通用的功能独立模块或者半功能模块。 + +## 1.1. 完整功能工具类库 + +  暴露头文件中的接口提供且仅提供了完整的独立功能的接口,使用者可直接使用完整的工具功能。往往暴露的头文件不会存在多余的用户数据接口和结构。 + +## 1.2. 半功能工具类库 + +  暴露头文件中的接口无法完成独立功能,往往带着部分未实现的虚函数,需要使用者重载才能实现完整功能。 + +  例如:McuProtocol类库,需要与读写数据模块组合封装成新类才能实现一个独立功能的模块。McuProtocol模块只完成了协议数据的组包和协议数据包的解析功能,需要与协议的读取/读取方式的模块组合封装成一个完整的功能模块。这样设计的初衷是让协议的处理和协议的收发解耦合,可以快速更换协议版本或者替换数据收发功能模块。 \ No newline at end of file diff --git a/utils/Servers/CMakeLists.txt b/utils/Servers/CMakeLists.txt new file mode 100755 index 0000000..1e356c4 --- /dev/null +++ b/utils/Servers/CMakeLists.txt @@ -0,0 +1,143 @@ + +include(${CMAKE_SOURCE_DIR_IPCSDK}/build/global_config.cmake) +set(EXECUTABLE_OUTPUT_PATH ${EXEC_OUTPUT_PATH}) +set(LIBRARY_OUTPUT_PATH ${LIBS_OUTPUT_PATH}) + +include_directories( + ./src + ./include + ${UTILS_SOURCE_PATH}/StatusCode/include + ${UTILS_SOURCE_PATH}/Log/include + ${EXTERNAL_SOURCE_PATH}/curl/curl-8.1.2/include +) +#do not rely on any other library +# link_directories( +# ${EXTERNAL_SOURCE_PATH}/curl/curl-8.1.2/build/lib +# ) + +aux_source_directory(./src SRC_FILES) + +set(TARGET_NAME Servers) +add_library(${TARGET_NAME} STATIC ${SRC_FILES}) +target_link_libraries(${TARGET_NAME} Log) +if(${CURL_OPENSSL_LIB_SHARED_ENABLE} MATCHES "false") + target_link_libraries(${TARGET_NAME} ${LIBS_OUTPUT_PATH}/libcurl.a) + target_link_libraries(${TARGET_NAME} ${LIBS_OUTPUT_PATH}/libssl.a) + target_link_libraries(${TARGET_NAME} ${LIBS_OUTPUT_PATH}/libcrypto.a dl pthread) +else() + target_link_libraries(${TARGET_NAME} ${LIBS_OUTPUT_PATH}/libcurl.so) + target_link_libraries(${TARGET_NAME} ${LIBS_OUTPUT_PATH}/libssl.so) + target_link_libraries(${TARGET_NAME} ${LIBS_OUTPUT_PATH}/libcrypto.so dl pthread) +endif() + +# ------------------ openssl ------------------ start +execute_process(COMMAND sh build_openssl.sh WORKING_DIRECTORY ${EXTERNAL_SOURCE_PATH}/openssl/) + +if(${CURL_OPENSSL_LIB_SHARED_ENABLE} MATCHES "false") + # set(OPENSSL_TAILOR no-asm no-async no-md2 no-mdc2 no-poly1305 no-blake2 + # no-siphash no-sm3 no-rc2 no-rc4 no-rc5 no-idea no-aria no-bf no-cast + # no-camellia no-chacha no-ec no-sm2 no-dso + # no-err no-comp no-cms no-ts no-cmac no-ct + # no-hw-padlock no-nextprotoneg no-psk no-rfc3779 no-srtp + # no-dgram no-dynamic-engine no-ec2m no-filenames no-gost + # no-afalgeng no-async no-autoalginit no-autoerrinit no-capieng + # no-tests + # no-ssl-trace no-static-engine no-stdio no-threads no-deprecated no-makedepend + # no-multiblock) +else() + # set(OPENSSL_TAILOR no-asm no-tests) +endif() +if(${TARGET_PLATFORM} MATCHES ${DEFINE_LINUX}) + set(OPENSSL_OS_PLATFORM "linux-x86_64") +else() + set(OPENSSL_OS_PLATFORM "linux-armv4") + # set(LINK_FLAGS "${LINK_FLAGS} -Wl,-rpath,${CURL_SHARED_LIBS_PATH}") + # set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} ${LINK_FLAGS}") + # set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${LINK_FLAGS}") +endif() +add_custom_target( + openssl + COMMAND echo "Build openssl." + COMMAND rm -rf ${EXTERNAL_SOURCE_PATH}/openssl/build + COMMAND ./Configure ${OPENSSL_TAILOR} ${OPENSSL_OS_PLATFORM} --prefix=${EXTERNAL_SOURCE_PATH}/openssl/build --openssldir=${EXTERNAL_SOURCE_PATH}/openssl/build --cross-compile-prefix=${CROSS_COMPILE_PREFIX} CC=gcc AR=ar + COMMAND make + COMMAND make install + WORKING_DIRECTORY ${EXTERNAL_SOURCE_PATH}/openssl/openssl-1.1.1s/ +) +# ------------------ openssl ------------------ end + +# ------------------ curl ------------------ start +execute_process(COMMAND sh build_curl.sh WORKING_DIRECTORY ${EXTERNAL_SOURCE_PATH}/curl/) +set(CURL_TAILOR --disable-dict --disable-file --disable-gopher + --disable-imap --disable-ldap --disable-pop3 --disable-rtmp + --disable-rtsp --disable-scp --disable-sftp --disable-smb + --disable-telnet --disable-tftp --disable-ipv6) +if(${TARGET_PLATFORM} MATCHES ${DEFINE_LINUX}) + set(CURL_HOST "") +else() + set(CURL_HOST "--host=arm-linux") +endif() +add_custom_target( + curl + DEPENDS openssl + COMMAND echo "Build curl. openssl path = ${EXTERNAL_SOURCE_PATH}" + COMMAND ./configure --without-zlib --prefix=${EXTERNAL_SOURCE_PATH}/curl --with-ssl=${EXTERNAL_SOURCE_PATH}/openssl/build ${CURL_HOST} CC=${CMAKE_C_COMPILER} + COMMAND make + COMMAND cp ${EXTERNAL_SOURCE_PATH}/curl/curl-8.1.2/lib/.libs/lib*.a ${LIBS_OUTPUT_PATH} + COMMAND cp ${EXTERNAL_SOURCE_PATH}/openssl/build/lib/lib*.a ${LIBS_OUTPUT_PATH} + # COMMAND cp ${EXTERNAL_SOURCE_PATH}/curl/curl-8.1.2/lib/.libs/lib*.so* ${LIBS_OUTPUT_PATH} + # COMMAND cp ${EXTERNAL_SOURCE_PATH}/openssl/build/lib/lib*.so* ${LIBS_OUTPUT_PATH} + WORKING_DIRECTORY ${EXTERNAL_SOURCE_PATH}/curl/curl-8.1.2/ +) +# ------------------ curl ------------------ end + +add_custom_command( + OUTPUT ${EXTERNAL_SOURCE_PATH}/curl/curl-8.1.2/lib/.libs/libcurl.a + COMMAND make curl + WORKING_DIRECTORY ${PROJECT_ROOT_PATH}/cmake-shell/ +) +add_custom_target( + compile_curl + DEPENDS ${EXTERNAL_SOURCE_PATH}/curl/curl-8.1.2/lib/.libs/libcurl.a + WORKING_DIRECTORY ${EXTERNAL_SOURCE_PATH}/curl/curl-8.1.2/ +) +add_custom_command( + TARGET ${TARGET_NAME} + PRE_BUILD + COMMAND make compile_curl + WORKING_DIRECTORY ${PROJECT_ROOT_PATH}/cmake-shell/ +) + +if ("${COMPILE_IMPROVE_SUPPORT}" MATCHES "true") +add_custom_target( + Servers_code_check + COMMAND ${CLANG_TIDY_EXE} + -checks='${CLANG_TIDY_CHECKS}' + --header-filter=.* + --system-headers=false + ${SRC_FILES} + ${CLANG_TIDY_CONFIG} + -p ${PLATFORM_PATH}/cmake-shell + WORKING_DIRECTORY ${UTILS_SOURCE_PATH}/Servers +) +file(GLOB_RECURSE HEADER_FILES *.h) +add_custom_target( + Servers_code_format + COMMAND ${CLANG_FORMAT_EXE} + -style=file + -i ${SRC_FILES} ${HEADER_FILES} + WORKING_DIRECTORY ${UTILS_SOURCE_PATH}/Servers +) +add_custom_command( + TARGET ${TARGET_NAME} + PRE_BUILD + COMMAND make Servers_code_check + COMMAND make Servers_code_format + WORKING_DIRECTORY ${PLATFORM_PATH}/cmake-shell/ +) +endif() + +define_file_name(${TARGET_NAME}) + +file(GLOB_RECURSE INSTALL_HEADER_FILES include/*.h) +install(FILES ${INSTALL_HEADER_FILES} DESTINATION include) \ No newline at end of file diff --git a/utils/Servers/README.md b/utils/Servers/README.md new file mode 100644 index 0000000..763d0ff --- /dev/null +++ b/utils/Servers/README.md @@ -0,0 +1,109 @@ +# 1. 服务器协议接口 + +## 1.1. 概述 +   提供HTTP/FTP/SMTP协议的接口数据,使用**curl+openssl开源库**二次封装。 + +## 1.2. 开发过程记录 +1. 先编译openssl,再编译curl,curl的构建配置需要指定openssl的输出文件目录,否则curl将会构建失败; +2. http协议,指定url,指定post数据,curl即可按照POST发送请求; +3. ftp/ftps协议对应不同的服务器IP,必须一一对应; +4. curl配置了忽略证书校验,未知有什么实际影响; +5. 分别对openssl和curl进行了裁剪; + +## 1.3. 关于MQTT +   curl源码包里并未提供MQTT相关的example,网上搜索编码示例未找到。 + +## 1.4. Servers协议栈模块构建 +   使用SifarSDK的构建体系进行编译,编译curl时自动关联openssl进行编译。 +* 配置文件(此处以/build/cmake/toolchain/linux.toolchain.cmake为示例): +``` +# ------------ build curl + openssl ------------ start +# 工具链前缀 +set(CROSS_COMPILE_PREFIX "") +# 是否启动动态库(预留,暂未支持) +set(CURL_OPENSSL_LIB_SHARED_ENABLE "false") +# ------------ build curl + openssl ------------ end +``` +* 项目根目录编译 +``` +make cmake +cd cmake-shell-linux/ +make curl // 由于curl属于三方构建的开源库,首次需要先编译curl + openssl +make Servers +``` + +## 1.5. Example +头文件:servser.h +链接库:libServers.a,libLog.a +三方依赖库(依次按序): libcurl.a,libssl.a,libcrypto.a + +下述以FTP为例子: +``` +TEST(ServersTest, FtpsUpload) + { + const char *url = "ftp://150.109.112.64/ServersTest";test/bin/ServersTest"; + const char *uploadFile = "./ServersTest"; + const char *user_password = "ftp_user:Sifar%123456"; + ServerParam init = { + .logFlag = LOG_FLAG_ENABLE, // 开启curl日志 + .sslVerifyFlag = SSL_VERIFY_DISABLE, //关闭ssl的证书校验功能 + }; + InitLog(LOG_EASYLOGGING, nullptr); // 初始化自研log库 + ServersInit(init); // 初始化Servers模块 + LogInfo("servers test start.\n"); + ServerFtp *ftp = NewServersFtp(url, FTPS_FLAG_ENABLE); // 创建ftp的参数“句柄” + if (ftp) + { + ftp->user_password = (char *)user_password; + ftp->filePath = (char *)uploadFile; + FtpUpload(ftp); // 使用FTP上传文件 + if (SERVERS_CODE_OK == ftp->code) + { + LogInfo("ftp succeed.\n"); + } + else + { + LogError("ftp failed, code = %d.\n", static_cast(ftp->code)); + } + DeleteServersFtp(ftp); // 释放ftp“句柄” + } + UnInitLog(); + } +``` +**注意:** +* 必须调用servers_init()函数初始化Servers协议栈库; +* 必须使用模块接口创建对应的协议参数“句柄”,示例为new_servers_ftp(); +* 必须释放协议参数“句柄”; + +更多示例详见:/test/component/Servers/src/ServersTest.cpp + +## 1.6. 测试 +   libServers.a自研代码通过asan测试,未存在内存安全相关漏洞。 +测试用例详见:/test/component/Servers/src/ServersTest.cpp + +* 测试用例编译 +``` +make cmake +cd cmake-shell-linux/ +make ServersTest +``` + +## 1.7. 总结 +* 实现http/https/ftp/ftps/smtp/smtps接口; +* 交叉编译测试ftp/ftps上传下载速率大概为1M/s(333DE芯片平台); +* 裁剪openssl和curl后编译的demo大小 < 1.2M; + +## 1.8. S530项目配置 +* 工具链 +``` +set(CMAKE_C_COMPILER /opt/arm-ca9-linux-uclibcgnueabihf-8.4.01/usr/bin/arm-linux-gcc) +set(CMAKE_CXX_COMPILER /opt/arm-ca9-linux-uclibcgnueabihf-8.4.01/usr/bin/arm-linux-g++) +``` +* curl + openssl +``` +# ------------ build curl + openssl ------------ start +set(CROSS_COMPILE_PREFIX "/opt/arm-ca9-linux-uclibcgnueabihf-8.4.01/usr/bin/arm-linux-") +set(CURL_OPENSSL_LIB_SHARED_ENABLE "false") +set(CURL_SHARED_LIBS_PATH "/mnt/mmc") +# ------------ build curl + openssl ------------ end +``` \ No newline at end of file diff --git a/utils/Servers/include/servers.h b/utils/Servers/include/servers.h new file mode 100644 index 0000000..e7bf7ea --- /dev/null +++ b/utils/Servers/include/servers.h @@ -0,0 +1,110 @@ +/* + * Copyright (c) 2023 Fancy Code. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef SERVERS_H +#define SERVERS_H +#ifdef __cplusplus +extern "C" { +#endif +#define SERVERS_PARAM_NULL_MEANS_END NULL +#define SERVERS_NEVER_TIMEOUT 0 +typedef enum +{ + SERVERS_CODE_DEFAULT = -1, + SERVERS_CODE_OK = 0, + SERVERS_CODE_END +} ServersCode; +typedef enum +{ + FTPS_FLAG_ENABLE = 0, + FTPS_FLAG_DISABLE, + FTPS_FLAG_END +} FtpsFlag; +typedef enum +{ + LOG_FLAG_ENABLE = 0, + LOG_FLAG_DISABLE, + LOG_FLAG_END +} LogFlag; +typedef enum +{ + SSL_VERIFY_ENABLE = 0, + SSL_VERIFY_DISABLE, + SSL_VERIFY_END +} SslFlag; +typedef struct ServersInit +{ + LogFlag logFlag; + SslFlag sslVerifyFlag; +} ServerParam; +typedef struct servers_http +{ + const char *url; + char *postData; + char *filePath; + char **header; + char *reply; + unsigned int replyLength; + int code; +} ServerHttp; +typedef struct servers_ftp +{ + const char *url; + const FtpsFlag ftpsFlag; + char *user_password; + char *filePath; + unsigned int timeOutMs; + int code; +} ServerFtp; +typedef struct servers_smtp +{ + const char *url; + const char *subject; + const char *from; + const char *to; + const char *userName; + const char *password; + const char *date; + char *inlineText; + char **toList; + unsigned int toListLength; + // char *text; + // unsigned int textLength; + char **attachment; + int code; +} ServerSmtp; +void ServersInit(ServerParam init); +void ServersUnInit(void); +// HTTP API +ServerHttp *NewServersHttp(const char *url); +void DeleteServersHttp(ServerHttp *ptr); +void HttpGet(ServerHttp *param); +void HttpPost(ServerHttp *param); +void HttpPostFile(ServerHttp *param); +void HttpPut(ServerHttp *param); +// FTP API +ServerFtp *NewServersFtp(const char *url, const FtpsFlag ftpsFlag); +void DeleteServersFtp(ServerFtp *ptr); +void FtpServersCheck(ServerFtp *param); +void FtpDownload(ServerFtp *param); +void FtpUpload(ServerFtp *param); +// SMTP API +ServerSmtp *new_servers_smtp(const char *url, const char *subject, const char *from, const char *to, + const char *userName, const char *password, const char *date); +void delete_servers_smtp(ServerSmtp *ptr); +void smtp_send_email(ServerSmtp *param); +#ifdef __cplusplus +} +#endif +#endif // !SERVERS_H \ No newline at end of file diff --git a/utils/Servers/src/curl_serve.c b/utils/Servers/src/curl_serve.c new file mode 100644 index 0000000..92ae975 --- /dev/null +++ b/utils/Servers/src/curl_serve.c @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2023 Fancy Code. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "curl_serve.h" +static ServerParam gCurlServe; +void SetVerboseLog(LogFlag flag) +{ + gCurlServe.logFlag = flag; +} +void SetSslVerify(SslFlag flag) +{ + gCurlServe.sslVerifyFlag = flag; +} +CURL *CurlEasyMake(void) +{ + CURL *curl; + curl = curl_easy_init(); + if (!curl) { + return curl; + } + if (SSL_VERIFY_DISABLE == gCurlServe.sslVerifyFlag) { + + /* + * If you want to connect to a site who is not using a certificate that is + * signed by one of the certs in the CA bundle you have, you can skip the + * verification of the server's certificate. This makes the connection + * A LOT LESS SECURE. + * + * If you have a CA cert for the server stored someplace else than in the + * default bundle, then the CURLOPT_CAPATH option might come handy for + * you. + */ + curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L); + /* + * If the site you are connecting to uses a different host name that what + * they have mentioned in their server certificate's commonName (or + * subjectAltName) fields, libcurl will refuse to connect. You can skip + * this check, but this will make the connection less secure. + */ + curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0L); + } + if (LOG_FLAG_ENABLE == gCurlServe.logFlag) { + /* Switch on full protocol/debug output */ + curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L); + } + return curl; +} \ No newline at end of file diff --git a/utils/Servers/src/curl_serve.h b/utils/Servers/src/curl_serve.h new file mode 100644 index 0000000..04a6f53 --- /dev/null +++ b/utils/Servers/src/curl_serve.h @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2023 Fancy Code. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef CURL_SERVE_H +#define CURL_SERVE_H +#include "servers.h" +#include +#ifdef __cplusplus +extern "C" { +#endif +void SetVerboseLog(LogFlag flag); +void SetSslVerify(SslFlag flag); +CURL *CurlEasyMake(void); +#ifdef __cplusplus +} +#endif +#endif // !CURL_SERVE_H \ No newline at end of file diff --git a/utils/Servers/src/ftp_servers.c b/utils/Servers/src/ftp_servers.c new file mode 100644 index 0000000..fd5f43a --- /dev/null +++ b/utils/Servers/src/ftp_servers.c @@ -0,0 +1,218 @@ +/* + * Copyright (c) 2023 Fancy Code. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "ftp_servers.h" +#include "ILog.h" +#include "curl_serve.h" +#include +#include +#include +#include +#include +struct FtpFile +{ + const char *filename; + FILE *stream; +}; +static size_t my_fwrite(void *buffer, size_t size, size_t nmemb, void *stream) +{ + struct FtpFile *out = (struct FtpFile *)stream; + if (!out->stream) { + /* open file for writing */ + out->stream = fopen(out->filename, "wb"); + if (!out->stream) + return -1; /* failure, cannot open file to write */ + } + return fwrite(buffer, size, nmemb, out->stream); +} +static CURL *ftp_curl_easy_make(ServerFtp *param) +{ + CURL *curl = NULL; + curl = CurlEasyMake(); + if (curl) { + /* + * You better replace the URL with one that works! + */ + curl_easy_setopt(curl, CURLOPT_URL, param->url); + /* User and password for the FTP login */ + if (param->user_password) { + curl_easy_setopt(curl, CURLOPT_USERPWD, param->user_password); + } + /* We activate SSL and we require it for both control and data */ + if (FTPS_FLAG_ENABLE == param->ftpsFlag) { + curl_easy_setopt(curl, CURLOPT_USE_SSL, CURLUSESSL_ALL); + } + if (SERVERS_NEVER_TIMEOUT < param->timeOutMs) { + curl_easy_setopt(curl, CURLOPT_TIMEOUT_MS, param->timeOutMs); + } + } + return curl; +} +void FtpServersCheckConnect(ServerFtp *param) +{ + if (!param) { + LogError("null pointer.\n"); + return; + } + CURL *curl; + CURLcode res; + + curl_global_init(CURL_GLOBAL_DEFAULT); + + curl = ftp_curl_easy_make(param); + if (curl) { + curl_easy_setopt(curl, CURLOPT_CONNECT_ONLY, 1L); + res = curl_easy_perform(curl); + param->code = res; + + /* always cleanup */ + curl_easy_cleanup(curl); + + if (CURLE_OK != res) { + /* we failed */ + fprintf(stderr, "curl told us %d\n", res); + } + } + + curl_global_cleanup(); +} +void FtpServersDownload(ServerFtp *param) +{ + if (!param) { + LogError("null pointer.\n"); + return; + } + CURL *curl; + CURLcode res; + struct FtpFile ftpfile = {param->filePath, /* name to store the file as if successful */ + NULL}; + + curl_global_init(CURL_GLOBAL_DEFAULT); + + curl = ftp_curl_easy_make(param); + if (curl) { + /* Define our callback to get called when there's data to be written */ + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, my_fwrite); + /* Set a pointer to our struct to pass to the callback */ + curl_easy_setopt(curl, CURLOPT_WRITEDATA, &ftpfile); + + res = curl_easy_perform(curl); + param->code = res; + + /* always cleanup */ + curl_easy_cleanup(curl); + + if (CURLE_OK != res) { + /* we failed */ + fprintf(stderr, "curl told us %d\n", res); + } + } + + if (ftpfile.stream) + fclose(ftpfile.stream); /* close the local file */ + + curl_global_cleanup(); +} +/* NOTE: if you want this example to work on Windows with libcurl as a + DLL, you MUST also provide a read callback with CURLOPT_READFUNCTION. + Failing to do so will give you a crash since a DLL may not use the + variable's memory when passed in to it from an app like this. */ +static size_t read_callback(char *ptr, size_t size, size_t nmemb, void *stream) +{ + unsigned long nread; + /* in real-world cases, this would probably get this data differently + as this fread() stuff is exactly what the library already would do + by default internally */ + size_t retcode = fread(ptr, size, nmemb, stream); + + if (retcode > 0) { + nread = (unsigned long)retcode; + fprintf(stderr, "*** We read %lu bytes from file\n", nread); + } + + return retcode; +} +void FtpServersUpload(ServerFtp *param) +{ + if (!param) { + LogError("null pointer.\n"); + return; + } + CURL *curl; + CURLcode res; + FILE *hd_src; + struct stat file_info; + unsigned long fsize; + + struct curl_slist *headerlist = NULL; + // static const char buf_1[] = "RNFR " UPLOAD_FILE_AS; // TODO: + // static const char buf_2[] = "RNTO " RENAME_FILE_TO; + + /* get the file size of the local file */ + if (stat(param->filePath, &file_info)) { + LogInfo("Couldn't open '%s'\n", param->filePath); + return; + } + fsize = (unsigned long)file_info.st_size; + + LogInfo("Local file size: %lu bytes.\n", fsize); + + /* get a FILE * of the same file */ + hd_src = fopen(param->filePath, "rb"); + + /* In windows, this will init the winsock stuff */ + curl_global_init(CURL_GLOBAL_ALL); + + /* get a curl handle */ + curl = ftp_curl_easy_make(param); + if (curl) { + /* build a list of commands to pass to libcurl */ + // headerlist = curl_slist_append(headerlist, buf_1); // TODO: + // headerlist = curl_slist_append(headerlist, buf_2); + + /* we want to use our own read function */ + curl_easy_setopt(curl, CURLOPT_READFUNCTION, read_callback); + + /* enable uploading */ + curl_easy_setopt(curl, CURLOPT_UPLOAD, 1L); + + /* pass in that last of FTP commands to run after the transfer */ + curl_easy_setopt(curl, CURLOPT_POSTQUOTE, headerlist); + + /* now specify which file to upload */ + curl_easy_setopt(curl, CURLOPT_READDATA, hd_src); + + /* Set the size of the file to upload (optional). If you give a *_LARGE + option you MUST make sure that the type of the passed-in argument is a + curl_off_t. If you use CURLOPT_INFILESIZE (without _LARGE) you must + make sure that to pass in a type 'long' argument. */ + curl_easy_setopt(curl, CURLOPT_INFILESIZE_LARGE, (curl_off_t)fsize); + + /* Now run off and do what you have been told! */ + res = curl_easy_perform(curl); + param->code = res; + /* Check for errors */ + if (res != CURLE_OK) + fprintf(stderr, "curl_easy_perform() failed: %s\n", curl_easy_strerror(res)); + + /* clean up the FTP commands list */ + curl_slist_free_all(headerlist); + + /* always cleanup */ + curl_easy_cleanup(curl); + } + fclose(hd_src); /* close the local file */ + + curl_global_cleanup(); +} \ No newline at end of file diff --git a/utils/Servers/src/ftp_servers.h b/utils/Servers/src/ftp_servers.h new file mode 100644 index 0000000..2e12142 --- /dev/null +++ b/utils/Servers/src/ftp_servers.h @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2023 Fancy Code. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef FTP_SERVERS_H +#define FTP_SERVERS_H +#include "servers.h" +#ifdef __cplusplus +extern "C" { +#endif +void FtpServersCheckConnect(ServerFtp *param); +void FtpServersDownload(ServerFtp *param); +void FtpServersUpload(ServerFtp *param); +#ifdef __cplusplus +} +#endif +#endif // !FTP_SERVERS_H \ No newline at end of file diff --git a/utils/Servers/src/http_servers.c b/utils/Servers/src/http_servers.c new file mode 100644 index 0000000..99e88b5 --- /dev/null +++ b/utils/Servers/src/http_servers.c @@ -0,0 +1,280 @@ +/* + * Copyright (c) 2023 Fancy Code. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "http_servers.h" +#include "ILog.h" +#include "curl_serve.h" +#include +#include +#include +#include +#include +#include + +static size_t write_cb(char *data, size_t n, size_t l, void *userp) +{ + /* take care of the data here, ignored in this example */ + (void)data; + (void)userp; + ServerHttp *p = (ServerHttp *)userp; + const int LENGTH = n * l; + // p->reply = (char *)realloc(p->reply, LENGTH); + char *newData = (char *)malloc(p->replyLength + LENGTH); + if (newData) { + if (p->reply) { + memcpy(newData, p->reply, p->replyLength); + free(p->reply); + p->reply = NULL; + } + memcpy(newData + p->replyLength, data, LENGTH); + p->replyLength += LENGTH; + p->reply = newData; + } + return n * l; +} +void HttpServersGet(ServerHttp *param) +{ + if (!param) { + LogError("null pointer.\n"); + return; + } + CURL *curl; + CURLcode res; + + curl = CurlEasyMake(); + if (curl) { + curl_easy_setopt(curl, CURLOPT_URL, param->url); + /* example.com is redirected, so we tell libcurl to follow redirection */ + curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L); + // + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_cb); + curl_easy_setopt(curl, CURLOPT_WRITEDATA, param); + + /* Perform the request, res will get the return code */ + res = curl_easy_perform(curl); + param->code = res; + /* Check for errors */ + if (res != CURLE_OK) + fprintf(stderr, "curl_easy_perform() failed: %s\n", curl_easy_strerror(res)); + + /* always cleanup */ + curl_easy_cleanup(curl); + } +} +void HttpServersPost(ServerHttp *param) +{ + if (!param) { + LogError("null pointer.\n"); + return; + } + CURL *curl; + CURLcode res; + + /* In windows, this will init the winsock stuff */ + curl_global_init(CURL_GLOBAL_ALL); + + /* get a curl handle */ + curl = CurlEasyMake(); + if (curl) { + /* First set the URL that is about to receive our POST. This URL can + just as well be an https:// URL if that is what should receive the + data. */ + curl_easy_setopt(curl, CURLOPT_URL, param->url); + struct curl_slist *headers = NULL; + + /* default type with postfields is application/x-www-form-urlencoded, + change it if you want */ + headers = curl_slist_append(headers, "Content-Type: application/json"); + curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers); + /* Now specify the POST data */ + if (param->postData) { + curl_easy_setopt(curl, CURLOPT_POSTFIELDS, param->postData); + } + else { + const char *NULL_POST_DATA = "{}"; + curl_easy_setopt(curl, CURLOPT_POSTFIELDS, NULL_POST_DATA); + } + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_cb); + curl_easy_setopt(curl, CURLOPT_WRITEDATA, param); + // curl_easy_setopt(curl, CURLOPT_TIMEOUT, 1L); + // curl_easy_setopt(curl, CURLOPT_TIMEOUT_MS, 5000L); + + /* Perform the request, res will get the return code */ + res = curl_easy_perform(curl); + /* Check for errors */ + if (res != CURLE_OK) + fprintf(stderr, "curl_easy_perform() failed: %s\n", curl_easy_strerror(res)); + + /* always cleanup */ + curl_easy_cleanup(curl); + /* free headers */ + curl_slist_free_all(headers); + } + curl_global_cleanup(); +} +void HttpServersPostFile(ServerHttp *param) +{ + if (!param) { + LogError("null pointer.\n"); + return; + } + CURL *curl = NULL; + CURLcode res; + + struct curl_httppost *formpost = NULL; + struct curl_httppost *lastptr = NULL; + struct curl_slist *headerlist = NULL; + static const char buf[] = "Expect:"; + + curl_global_init(CURL_GLOBAL_ALL); + + /* Fill in the file upload field */ + curl_formadd(&formpost, &lastptr, CURLFORM_COPYNAME, "sendfile", CURLFORM_FILE, param->filePath, CURLFORM_END); + + /* Fill in the filename field */ + curl_formadd( + &formpost, &lastptr, CURLFORM_COPYNAME, "filename", CURLFORM_COPYCONTENTS, param->filePath, CURLFORM_END); + + /* Fill in the submit field too, even if this is rarely needed */ + curl_formadd(&formpost, &lastptr, CURLFORM_COPYNAME, "submit", CURLFORM_COPYCONTENTS, "send", CURLFORM_END); + + /* get a curl handle */ + curl = CurlEasyMake(); + /* initialize custom header list (stating that Expect: 100-continue is not + wanted */ + headerlist = curl_slist_append(headerlist, buf); + if (curl) { + /* what URL that receives this POST */ + curl_easy_setopt(curl, CURLOPT_URL, param->url); + // if((argc == 2) && (!strcmp(argv[1], "noexpectheader"))) + /* only disable 100-continue header if explicitly requested */ + curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headerlist); + curl_easy_setopt(curl, CURLOPT_HTTPPOST, formpost); + + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_cb); + curl_easy_setopt(curl, CURLOPT_WRITEDATA, param); + /* Perform the request, res will get the return code */ + res = curl_easy_perform(curl); + /* Check for errors */ + if (res != CURLE_OK) + fprintf(stderr, "curl_easy_perform() failed: %s\n", curl_easy_strerror(res)); + + /* always cleanup */ + curl_easy_cleanup(curl); + + /* then cleanup the formpost chain */ + curl_formfree(formpost); + /* free slist */ + curl_slist_free_all(headerlist); + } +} +static size_t read_callback(char *ptr, size_t size, size_t nmemb, void *stream) +{ + size_t retcode; + unsigned long nread; + + /* in real-world cases, this would probably get this data differently + as this fread() stuff is exactly what the library already would do + by default internally */ + retcode = fread(ptr, size, nmemb, stream); + + if (retcode > 0) { + nread = (unsigned long)retcode; + fprintf(stderr, "*** We read %lu bytes from file\n", nread); + } + + return retcode; +} +void HttpServersPut(ServerHttp *param) +{ + if (!param) { + LogError("null pointer.\n"); + return; + } + CURL *curl; + CURLcode res; + FILE *hd_src; + struct stat file_info; + struct curl_slist *headers = NULL; + + char *file; + if (!param->filePath) { + LogError("file path is needed.\n"); + return; + } + + file = param->filePath; + + /* get the file size of the local file */ + stat(file, &file_info); + + /* get a FILE * of the same file, could also be made with + fdopen() from the previous descriptor, but hey this is just + an example! */ + hd_src = fopen(file, "rb"); + if (!hd_src) { + LogError("fopen files failed.\n"); + return; + } + + /* In windows, this will init the winsock stuff */ + curl_global_init(CURL_GLOBAL_ALL); + + /* get a curl handle */ + curl = CurlEasyMake(); + if (curl) { + /* we want to use our own read function */ + curl_easy_setopt(curl, CURLOPT_READFUNCTION, read_callback); + + /* enable uploading (implies PUT over HTTP) */ + curl_easy_setopt(curl, CURLOPT_UPLOAD, 1L); + + /* specify target URL, and note that this URL should include a file + name, not only a directory */ + curl_easy_setopt(curl, CURLOPT_URL, param->url); + + /* now specify which file to upload */ + curl_easy_setopt(curl, CURLOPT_READDATA, hd_src); + + /* provide the size of the upload, we typecast the value to curl_off_t + since we must be sure to use the correct data size */ + curl_easy_setopt(curl, CURLOPT_INFILESIZE_LARGE, (curl_off_t)file_info.st_size); + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_cb); + curl_easy_setopt(curl, CURLOPT_WRITEDATA, param); + // Add header + char **cpp = NULL; + for (cpp = param->header; *cpp; cpp++) { + headers = curl_slist_append(headers, *cpp); + } + if (headers) { + curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers); + } + + /* Now run off and do what you have been told! */ + res = curl_easy_perform(curl); + /* Check for errors */ + if (res != CURLE_OK) + fprintf(stderr, "curl_easy_perform() failed: %s\n", curl_easy_strerror(res)); + + /* always cleanup */ + curl_easy_cleanup(curl); + /* free headers */ + if (headers) { + curl_slist_free_all(headers); + } + } + fclose(hd_src); /* close the local file */ + + curl_global_cleanup(); +} \ No newline at end of file diff --git a/utils/Servers/src/http_servers.h b/utils/Servers/src/http_servers.h new file mode 100644 index 0000000..fbeef1d --- /dev/null +++ b/utils/Servers/src/http_servers.h @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2023 Fancy Code. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef HTTP_SERVERS_H +#define HTTP_SERVERS_H +#include "servers.h" +#ifdef __cplusplus +extern "C" { +#endif +void HttpServersGet(ServerHttp *param); +void HttpServersPost(ServerHttp *param); +void HttpServersPostFile(ServerHttp *param); +void HttpServersPut(ServerHttp *param); +#ifdef __cplusplus +} +#endif +#endif // !HTTP_SERVERS_H \ No newline at end of file diff --git a/utils/Servers/src/servers.c b/utils/Servers/src/servers.c new file mode 100644 index 0000000..e827197 --- /dev/null +++ b/utils/Servers/src/servers.c @@ -0,0 +1,156 @@ +/* + * Copyright (c) 2023 Fancy Code. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "servers.h" +#include "ILog.h" +#include "curl_serve.h" +#include "ftp_servers.h" +#include "http_servers.h" +#include "smtp_servers.h" +#include +#include +#include +void ServersInit(ServerParam init) +{ + SetVerboseLog(init.logFlag); + SetSslVerify(init.sslVerifyFlag); +} +void ServersUnInit(void) +{ +} +void HttpGet(ServerHttp *param) +{ + LogInfo("HttpGet\n"); + HttpServersGet(param); +} +void HttpPost(ServerHttp *param) +{ + LogInfo("HttpPost\n"); + HttpServersPost(param); +} +void HttpPostFile(ServerHttp *param) +{ + LogInfo("HttpPostFile\n"); + HttpServersPostFile(param); +} +void HttpPut(ServerHttp *param) +{ + LogInfo("HttpPut\n"); + HttpServersPut(param); +} +void smtp_send_email(ServerSmtp *param) +{ + LogInfo("smtp_send_email\n"); + SmtpServersSendEmail(param); + // SmtpServersSendEmailOnlyText(param); +} +ServerHttp *NewServersHttp(const char *url) +{ + if (!url) { + LogError("NewServersHttp failed.\n"); + return NULL; + } + ServerHttp result = { + .url = url, .postData = NULL, .filePath = NULL, .header = NULL, .reply = NULL, .replyLength = 0, .code = 0}; + const int LENGTH = sizeof(ServerHttp); + ServerHttp *p = (ServerHttp *)malloc(LENGTH); + if (p) { + LogInfo("malloc succeed.\n"); + memcpy(p, &result, LENGTH); + } + return p; +} +void DeleteServersHttp(ServerHttp *ptr) +{ + if (ptr) { + if (ptr->reply) { + free(ptr->reply); + ptr->reply = NULL; + } + free(ptr); + } +} +ServerFtp *NewServersFtp(const char *url, const FtpsFlag ftpsFlag) +{ + if (!url) { + LogError("NewServersFtp failed.\n"); + return NULL; + } + ServerFtp result = {.url = url, + .ftpsFlag = ftpsFlag, + .user_password = NULL, + .filePath = NULL, + .timeOutMs = SERVERS_NEVER_TIMEOUT, + .code = -1}; + const int LENGTH = sizeof(ServerFtp); + ServerFtp *p = (ServerFtp *)malloc(LENGTH); + if (p) { + LogInfo("malloc succeed.\n"); + memcpy(p, &result, LENGTH); + } + return p; +} +void DeleteServersFtp(ServerFtp *ptr) +{ + if (ptr) { + free(ptr); + } +} +void FtpServersCheck(ServerFtp *param) +{ + FtpServersCheckConnect(param); +} +void FtpDownload(ServerFtp *param) +{ + LogInfo("FtpDownload\n"); + FtpServersDownload(param); +} +void FtpUpload(ServerFtp *param) +{ + LogInfo("FtpUpload\n"); + FtpServersUpload(param); +} +ServerSmtp *new_servers_smtp(const char *url, const char *subject, const char *from, const char *to, + const char *userName, const char *password, const char *date) +{ + if (!url || !from || !to || !userName || !password || !subject || !date) { + LogError("new_servers_smtp failed.\n"); + return NULL; + } + ServerSmtp result = {.url = url, + .subject = subject, + .from = from, + .to = to, + .userName = userName, + .password = password, + .date = date, + .inlineText = NULL, + // .text = NULL, + // .textLength = 0, + .attachment = NULL, + .code = -1}; + const int LENGTH = sizeof(ServerSmtp); + ServerSmtp *p = (ServerSmtp *)malloc(LENGTH); + if (p) { + LogInfo("malloc succeed.\n"); + memcpy(p, &result, LENGTH); + } + return p; +} +void delete_servers_smtp(ServerSmtp *ptr) +{ + if (ptr) { + free(ptr); + } +} \ No newline at end of file diff --git a/utils/Servers/src/smtp_servers.c b/utils/Servers/src/smtp_servers.c new file mode 100644 index 0000000..5052812 --- /dev/null +++ b/utils/Servers/src/smtp_servers.c @@ -0,0 +1,345 @@ +/* + * Copyright (c) 2023 Fancy Code. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "smtp_servers.h" +#include "ILog.h" +#include "curl_serve.h" +#include +#include +#include +#include +struct upload_status +{ + char *payload_text; + size_t bytes_read; +}; +// static size_t payload_source(char *ptr, size_t size, size_t nmemb, void *userp) +// { +// struct upload_status *upload_ctx = (struct upload_status *)userp; +// const char *data; +// size_t room = size * nmemb; + +// if ((size == 0) || (nmemb == 0) || ((size * nmemb) < 1)) +// { +// return 0; +// } + +// data = &upload_ctx->payload_text[upload_ctx->bytes_read]; + +// if (data) +// { +// size_t len = strlen(data); +// if (room < len) +// len = room; +// memcpy(ptr, data, len); +// upload_ctx->bytes_read += len; + +// return len; +// } + +// return 0; +// } +// static const char inline_html[] = +// "\r\n" +// "

This is the inline HTML message of the email.

" +// "
\r\n" +// "

It could be a lot of HTML data that would be displayed by " +// "email viewers able to handle HTML.

" +// "\r\n"; +#define SEND_EMAIL_HEADERS_ARRAY_LENGTH 5 +#define SEND_EMAIL_HEADERS_TEXT_LENGTH 1024 +#define DATE "Date: " +#define TO "To: " +#define FROM "From: " +#define SUBJECT "Subject: " +#define HEADER_END "\r\n" +enum HEADERS_NAME +{ + HEADERS_NAME_DATE = 0, + HEADERS_NAME_TO, + HEADERS_NAME_FROM, + HEADERS_NAME_SUBJECT, + HEADERS_NAME_END +}; +static void headers_make(char **headers, ServerSmtp *param) +{ + int textLength = strlen(DATE) + strlen(param->date) + 1; + char *text = (char *)malloc(textLength); + if (text) { + memset(text, 0, textLength); + snprintf(text, textLength, "%s%s", DATE, param->date); + headers[HEADERS_NAME_DATE] = text; + LogInfo("HEADERS_NAME_DATE:%s\n", text); + } + textLength = strlen(TO) + strlen(param->to) + 1; + text = (char *)malloc(textLength); + if (text) { + memset(text, 0, textLength); + snprintf(text, textLength, "%s%s", TO, param->to); + headers[HEADERS_NAME_TO] = text; + LogInfo("HEADERS_NAME_TO:%s\n", text); + } + textLength = strlen(FROM) + strlen(param->from) + strlen(param->userName) + strlen("()") + 1; + text = (char *)malloc(textLength); + if (text) { + memset(text, 0, textLength); + snprintf(text, textLength, "%s%s(%s)", FROM, param->from, param->userName); + headers[HEADERS_NAME_FROM] = text; + LogInfo("HEADERS_NAME_FROM:%s\n", text); + } + textLength = strlen(SUBJECT) + strlen(param->subject) + 1; + text = (char *)malloc(textLength); + if (text) { + memset(text, 0, textLength); + snprintf(text, textLength, "%s%s", SUBJECT, param->subject); + headers[HEADERS_NAME_SUBJECT] = text; + LogInfo("HEADERS_NAME_SUBJECT:%s\n", text); + } +} +static void headers_free(char **headers) +{ + char **cpp = headers; + for (int i = 0; i < SEND_EMAIL_HEADERS_ARRAY_LENGTH; i++) { + if (*cpp) { + free(*cpp); + headers[i] = NULL; + } + cpp++; + } +} +void SmtpServersSendEmail(ServerSmtp *param) +{ + if (!param) { + LogError("null pointer.\n"); + return; + } + char *headers_text[SEND_EMAIL_HEADERS_ARRAY_LENGTH] = {NULL}; + headers_make(headers_text, param); + CURL *curl; + CURLcode res = CURLE_OK; + + curl = CurlEasyMake(); + if (curl) { + struct curl_slist *headers = NULL; + struct curl_slist *recipients = NULL; + struct curl_slist *slist = NULL; + curl_mime *mime; + curl_mime *alt; + curl_mimepart *part; + const char **cpp; + const char **attachment; + + /* This is the URL for your mailserver */ + curl_easy_setopt(curl, CURLOPT_URL, param->url); + /* Set the username and password */ + curl_easy_setopt(curl, CURLOPT_USERNAME, param->userName); + curl_easy_setopt(curl, CURLOPT_PASSWORD, param->password); + + /* Note that this option is not strictly required, omitting it will result + * in libcurl sending the MAIL FROM command with empty sender data. All + * autoresponses should have an empty reverse-path, and should be directed + * to the address in the reverse-path which triggered them. Otherwise, + * they could cause an endless loop. See RFC 5321 Section 4.5.5 for more + * details. + */ + curl_easy_setopt(curl, CURLOPT_MAIL_FROM, param->from); + + /* Add two recipients, in this particular case they correspond to the + * To: and Cc: addressees in the header, but they could be any kind of + * recipient. */ + recipients = curl_slist_append(recipients, param->to); + // recipients = curl_slist_append(recipients, CC); + curl_easy_setopt(curl, CURLOPT_MAIL_RCPT, recipients); + + /* Build and set the message header list. */ + for (cpp = (const char **)headers_text; *cpp; cpp++) { + headers = curl_slist_append(headers, *cpp); + } + curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers); + + /* Build the mime message. */ + mime = curl_mime_init(curl); + + /* The inline part is an alternative proposing the html and the text + versions of the email. */ + alt = curl_mime_init(curl); + + /* HTML message. */ + // part = curl_mime_addpart(alt); + // curl_mime_data(part, inline_html, CURL_ZERO_TERMINATED); + // curl_mime_type(part, "text/html"); + + /* Text message. */ + if (param->inlineText) { + part = curl_mime_addpart(alt); + curl_mime_data(part, param->inlineText, CURL_ZERO_TERMINATED); + curl_mime_type(part, "text/plain"); + } + + /* Create the inline part. */ + part = curl_mime_addpart(mime); + curl_mime_subparts(part, alt); + curl_mime_type(part, "multipart/alternative"); + slist = curl_slist_append(NULL, "Content-Disposition: inline"); + curl_mime_headers(part, slist, 1); + + /* Add the attachment. */ + if (param->attachment) { + for (attachment = (const char **)param->attachment; *attachment; attachment++) { + part = curl_mime_addpart(mime); + curl_mime_filedata(part, *attachment); + } + curl_easy_setopt(curl, CURLOPT_MIMEPOST, mime); + } + + /* Send the message */ + res = curl_easy_perform(curl); + param->code = res; + + /* Check for errors */ + if (res != CURLE_OK) + fprintf(stderr, "curl_easy_perform() failed: %s\n", curl_easy_strerror(res)); + + /* Free lists. */ + curl_slist_free_all(recipients); + if (headers) + curl_slist_free_all(headers); + + /* curl will not send the QUIT command until you call cleanup, so you + * should be able to re-use this connection for additional messages + * (setting CURLOPT_MAIL_FROM and CURLOPT_MAIL_RCPT as required, and + * calling curl_easy_perform() again. It may not be a good idea to keep + * the connection open for a very long time though (more than a few + * minutes may result in the server timing out the connection), and you do + * want to clean up in the end. + */ + curl_easy_cleanup(curl); + + /* Free multipart message. */ + curl_mime_free(mime); + headers_free(headers_text); + } +} +void SmtpServersSendEmailOnlyText(ServerSmtp *param) +{ +#if 0 // only for test. + if (!param) + { + LogError("null pointer.\n"); + return; + } + int payloadTextLength = + strlen(DATE HEADER_END) + strlen(TO HEADER_END) + strlen(FROM HEADER_END) + strlen(SUBJECT HEADER_END) + + strlen(param->date) + strlen(param->to) + strlen(param->from) + strlen(param->subject); + if (param->inlineText) + { + payloadTextLength += strlen(param->inlineText); + } + char *payload_text = (char *)malloc(payloadTextLength); + if (!payload_text) + { + LogError("null pointer.\n"); + return; + } + snprintf(payload_text, payloadTextLength, "%s%s%s" + "%s%s%s" + "%s%s%s" + "%s%s%s" + "%s", + DATE, param->date, HEADER_END, + TO, param->to, HEADER_END, + FROM, param->from, HEADER_END, + SUBJECT, param->subject, HEADER_END HEADER_END, + param->inlineText); + // LogInfo("payload_text =\n%s\n", payload_text); + CURL *curl; + CURLcode res = CURLE_OK; + struct curl_slist *recipients = NULL; + struct upload_status upload_ctx = {0}; + upload_ctx.payload_text = payload_text; + + curl = CurlEasyMake(); + if (curl) + { + curl_mime *mime; + curl_mime *alt; + curl_mimepart *part; + struct curl_slist *slist = NULL; + /* This is the URL for your mailserver */ + curl_easy_setopt(curl, CURLOPT_URL, param->url); + /* Set the username and password */ + curl_easy_setopt(curl, CURLOPT_USERNAME, param->userName); + curl_easy_setopt(curl, CURLOPT_PASSWORD, param->password); + + /* Note that this option is not strictly required, omitting it will result + * in libcurl sending the MAIL FROM command with empty sender data. All + * autoresponses should have an empty reverse-path, and should be directed + * to the address in the reverse-path which triggered them. Otherwise, + * they could cause an endless loop. See RFC 5321 Section 4.5.5 for more + * details. + */ + curl_easy_setopt(curl, CURLOPT_MAIL_FROM, param->from); + + /* Add two recipients, in this particular case they correspond to the + * To: and Cc: addressees in the header, but they could be any kind of + * recipient. */ + recipients = curl_slist_append(recipients, param->to); + // recipients = curl_slist_append(recipients, CC_ADDR); + curl_easy_setopt(curl, CURLOPT_MAIL_RCPT, recipients); + + /* We are using a callback function to specify the payload (the headers and + * body of the message). You could just use the CURLOPT_READDATA option to + * specify a FILE pointer to read from. */ + curl_easy_setopt(curl, CURLOPT_READFUNCTION, payload_source); + curl_easy_setopt(curl, CURLOPT_READDATA, &upload_ctx); + curl_easy_setopt(curl, CURLOPT_UPLOAD, 1L); + + // /* Build the mime message. */ + // mime = curl_mime_init(curl); + // /* Create the inline part. */ + // part = curl_mime_addpart(mime); + // curl_mime_subparts(part, alt); + // curl_mime_type(part, "multipart/alternative"); + // slist = curl_slist_append(NULL, "Content-Disposition: inline"); + // curl_mime_headers(part, slist, 1); + // curl_mime_filedata(part, "./Makefile"); + // curl_easy_setopt(curl, CURLOPT_MIMEPOST, mime); + + /* Send the message */ + res = curl_easy_perform(curl); + param->code = res; + + /* Check for errors */ + if (res != CURLE_OK) + fprintf(stderr, "curl_easy_perform() failed: %s\n", + curl_easy_strerror(res)); + + /* Free the list of recipients */ + curl_slist_free_all(recipients); + + /* curl will not send the QUIT command until you call cleanup, so you + * should be able to re-use this connection for additional messages + * (setting CURLOPT_MAIL_FROM and CURLOPT_MAIL_RCPT as required, and + * calling curl_easy_perform() again. It may not be a good idea to keep + * the connection open for a very long time though (more than a few + * minutes may result in the server timing out the connection), and you do + * want to clean up in the end. + */ + curl_easy_cleanup(curl); + /* Free multipart message. */ + curl_mime_free(mime); + free(payload_text); + } +#endif +} \ No newline at end of file diff --git a/utils/Servers/src/smtp_servers.h b/utils/Servers/src/smtp_servers.h new file mode 100644 index 0000000..a589718 --- /dev/null +++ b/utils/Servers/src/smtp_servers.h @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2023 Fancy Code. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef SMTP_SERVERS_H +#define SMTP_SERVERS_H +#include "servers.h" +#ifdef __cplusplus +extern "C" { +#endif +void SmtpServersSendEmail(ServerSmtp *param); +void SmtpServersSendEmailOnlyText(ServerSmtp *param); +#ifdef __cplusplus +} +#endif +#endif // !SMTP_SERVERS_H \ No newline at end of file diff --git a/utils/Servers/test/CMakeLists.txt b/utils/Servers/test/CMakeLists.txt new file mode 100644 index 0000000..85d2bd4 --- /dev/null +++ b/utils/Servers/test/CMakeLists.txt @@ -0,0 +1,35 @@ + +include(${CMAKE_SOURCE_DIR_SIFARSDK}/build/global_config.cmake) +set(EXECUTABLE_OUTPUT_PATH ${EXEC_OUTPUT_PATH}) +set(LIBRARY_OUTPUT_PATH ${LIBS_OUTPUT_PATH}) + +include_directories( + ${COMPONENT_SOURCE_PATH}/Log/include + ${COMPONENT_SOURCE_PATH}/Servers/include +) +#do not rely on any other library +# link_directories( +# ${EXTERNAL_SOURCE_PATH}/curl/curl-8.1.2/build/lib +# ) + +set(CMAKE_AUTOMOC ON) +set(CMAKE_INCLUDE_CURRENT_DIR ON) + +aux_source_directory(./ SRC_FILES1) + +set(TARGET_NAME ServersMainTest) +add_executable(${TARGET_NAME} ${SRC_FILES1}) +target_link_libraries(${TARGET_NAME} Servers) +if(${CURL_OPENSSL_LIB_SHARED_ENABLE} MATCHES "false") + target_link_libraries(${TARGET_NAME} ${EXTERNAL_SOURCE_PATH}/curl/curl-8.1.2/lib/.libs/libcurl.a) + target_link_libraries(${TARGET_NAME} ${EXTERNAL_SOURCE_PATH}/openssl/build/lib/libssl.a) + target_link_libraries(${TARGET_NAME} ${EXTERNAL_SOURCE_PATH}/openssl/build/lib/libcrypto.a) +else() + target_link_libraries(${TARGET_NAME} ${EXTERNAL_SOURCE_PATH}/curl/curl-8.1.2/lib/.libs/libcurl.so) + target_link_libraries(${TARGET_NAME} ${EXTERNAL_SOURCE_PATH}/openssl/build/lib/libssl.so) + target_link_libraries(${TARGET_NAME} ${EXTERNAL_SOURCE_PATH}/openssl/build/lib/libcrypto.so) +endif() +target_link_libraries(${TARGET_NAME} pthread dl) +if(${COVERAGE_ON} MATCHES "true") + target_link_libraries(${TARGET_NAME} gcov) +endif() \ No newline at end of file diff --git a/utils/Servers/test/test_main.c b/utils/Servers/test/test_main.c new file mode 100644 index 0000000..514db6b --- /dev/null +++ b/utils/Servers/test/test_main.c @@ -0,0 +1,38 @@ +#include +#include "servers.h" +void test0() +{ + // InitLog(LOG_EASYLOGGING, NULL); + // LogInfo("servers test start.\n"); + ServerHttp *http = NewServersHttp("http://example.com"); + if (http) + { + HttpGet(http); + if (http->reply) + { + // LogInfo("HttpGet ========\n %s\n", http->reply); + } + DeleteServersHttp(http); + } + // UnInitLog(); +} +void test1() +{ + // InitLog(LOG_EASYLOGGING, NULL); + // LogInfo("servers test start.\n"); + // ServerHttp *http = NewServersHttp("https://example.com"); + // if (http) + // { + // HttpGet(http); + // if (http->reply) + // { + // LogInfo("HttpGet ========\n %s\n", http->reply); + // } + // DeleteServersHttp(http); + // } + // UnInitLog(); +} +int main(int argc, char *argv[]) +{ + return 0; +} \ No newline at end of file diff --git a/utils/SharedData/CMakeLists.txt b/utils/SharedData/CMakeLists.txt new file mode 100644 index 0000000..2bd1ce4 --- /dev/null +++ b/utils/SharedData/CMakeLists.txt @@ -0,0 +1,54 @@ +include(${CMAKE_SOURCE_DIR_IPCSDK}/build/global_config.cmake) +set(EXECUTABLE_OUTPUT_PATH ${EXEC_OUTPUT_PATH}) +set(LIBRARY_OUTPUT_PATH ${LIBS_OUTPUT_PATH}) + +include_directories( + ./src + ./include + ${UTILS_SOURCE_PATH}/StatusCode/include + ${UTILS_SOURCE_PATH}/Log/include + ${UTILS_SOURCE_PATH}/LinuxApi/include +) +# link_directories( +# ${EXTERNAL_SOURCE_PATH}/libconfig/libconfig-1.7.3/lib/.libs +# ) + +aux_source_directory(./src SRC_FILES) + +set(TARGET_NAME SharedData) +add_library(${TARGET_NAME} STATIC ${SRC_FILES}) +target_link_libraries(${TARGET_NAME} StatusCode Log LinuxApi) + +if ("${COMPILE_IMPROVE_SUPPORT}" MATCHES "true") +add_custom_target( + SharedData_code_check + COMMAND ${CLANG_TIDY_EXE} + -checks='${CLANG_TIDY_CHECKS}' + --header-filter=.* + --system-headers=false + ${SRC_FILES} + ${CLANG_TIDY_CONFIG} + -p ${PLATFORM_PATH}/cmake-shell + WORKING_DIRECTORY ${UTILS_SOURCE_PATH}/SharedData +) +file(GLOB_RECURSE HEADER_FILES *.h) +add_custom_target( + SharedData_code_format + COMMAND ${CLANG_FORMAT_EXE} + -style=file + -i ${SRC_FILES} ${HEADER_FILES} + WORKING_DIRECTORY ${UTILS_SOURCE_PATH}/SharedData +) +add_custom_command( + TARGET ${TARGET_NAME} + PRE_BUILD + COMMAND make SharedData_code_check + COMMAND make SharedData_code_format + WORKING_DIRECTORY ${PLATFORM_PATH}/cmake-shell/ +) +endif() + +define_file_name(${TARGET_NAME}) + +file(GLOB_RECURSE INSTALL_HEADER_FILES include/*.h) +install(FILES ${INSTALL_HEADER_FILES} DESTINATION include) \ No newline at end of file diff --git a/utils/SharedData/README.md b/utils/SharedData/README.md new file mode 100644 index 0000000..4a1e21c --- /dev/null +++ b/utils/SharedData/README.md @@ -0,0 +1,5 @@ +# 1. Linux跨进程共享数据 + +## 1.1. 概述 + +  用于跨进程进行数据共享的中间件。 \ No newline at end of file diff --git a/utils/SharedData/include/SharedData.h b/utils/SharedData/include/SharedData.h new file mode 100644 index 0000000..0701a31 --- /dev/null +++ b/utils/SharedData/include/SharedData.h @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2023 Fancy Code. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef SHARED_DATA_H +#define SHARED_DATA_H +#include "StatusCode.h" +#ifdef __cplusplus +extern "C" { +#endif +enum SHARED_DATA_CODE +{ + SHARED_DATA_CODE_INIT_FAILED = STATUS_CODE_END, + SHARED_DATA_CODE_WRONG_PEER_PARAMETERS, + SHARED_DATA_CODE_END +}; +enum SHARER_NAME +{ + SHARER_NAME_PRIMARY = 0, + SHARER_NAME_MINOR, + SHARER_NAME_END +}; +void *CreateSharedData(const enum SHARER_NAME name, const char *path, const int projectId); +const StatusCode IMakeSharedData(void *object, const unsigned int readableSize, const unsigned int writableSize); +const StatusCode ICleanSharedData(void *object); +const StatusCode IGetReadableData(void *object, void *buf, const unsigned int bufLength); +void ISetWritableData(void *object, void *buf, const unsigned int bufLength); +void IShareDataFree(void *object); +#ifdef __cplusplus +} +#endif +#endif \ No newline at end of file diff --git a/utils/SharedData/src/ISharedData.cpp b/utils/SharedData/src/ISharedData.cpp new file mode 100644 index 0000000..d5c18d3 --- /dev/null +++ b/utils/SharedData/src/ISharedData.cpp @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2023 Fancy Code. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "ISharedData.h" +#include "ILog.h" +#include "SharedDataImpl.h" // TODO: 互相包含,需要修改 +#include +void ISharedData::MakeSharedMemory(const unsigned int readableSize, const unsigned int writableSize) +{ +} +const StatusCode ISharedData::GetReadableMemory(void *buf, const unsigned int &bufLength) +{ + return CreateStatusCode(STATUS_CODE_VIRTUAL_FUNCTION); +} +void ISharedData::SetWritableMemory(void *buf, const unsigned int &bufLength) +{ +} +StatusCode ISharedData::FreeSharedMemory(void) +{ + return CreateStatusCode(STATUS_CODE_VIRTUAL_FUNCTION); +} +static const char *SHARED_DATA_NAME = "shared_data"; +const char *GetSharedDataModuleName(void) +{ + return SHARED_DATA_NAME; +} +std::shared_ptr *NewSharedData(const enum SHARER_NAME &name, const char *path, const int &projectId) +{ + LogInfo("Create the shared data object.\n"); + SharedData *impl = (SharedData *)malloc(sizeof(SharedData)); + if (nullptr == impl) { + LogError("NewConfigBase::malloc failed.\n"); + return nullptr; + } + SharedData tmp; + memcpy((void *)impl, (void *)&tmp, sizeof(SharedData)); + impl->mHeader.mCheckName = SHARED_DATA_NAME; + impl->mISharedData = std::make_shared(name, path, projectId); + return (std::shared_ptr *)(((char *)impl) + sizeof(ISharedDataHeader)); +} \ No newline at end of file diff --git a/utils/SharedData/src/ISharedData.h b/utils/SharedData/src/ISharedData.h new file mode 100644 index 0000000..93d7ec6 --- /dev/null +++ b/utils/SharedData/src/ISharedData.h @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2023 Fancy Code. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef I_SHARED_DATA_H +#define I_SHARED_DATA_H +#include "SharedData.h" +#include "StatusCode.h" +#include +class ISharedData +{ +public: + ISharedData() = default; + virtual ~ISharedData() = default; + virtual void MakeSharedMemory(const unsigned int readableSize, const unsigned int writableSize); + virtual const StatusCode GetReadableMemory(void *buf, const unsigned int &bufLength); + virtual void SetWritableMemory(void *buf, const unsigned int &bufLength); + virtual StatusCode FreeSharedMemory(void); +}; +typedef struct i_shared_data_header +{ + const char *mCheckName; +} ISharedDataHeader; +typedef struct config_base +{ + ISharedDataHeader mHeader; + std::shared_ptr mISharedData; +} SharedData; +const char *GetSharedDataModuleName(void); +std::shared_ptr *NewSharedData(const enum SHARER_NAME &name, const char *path, const int &projectId); +#endif \ No newline at end of file diff --git a/utils/SharedData/src/SharedData.cpp b/utils/SharedData/src/SharedData.cpp new file mode 100644 index 0000000..211f204 --- /dev/null +++ b/utils/SharedData/src/SharedData.cpp @@ -0,0 +1,74 @@ +/* + * Copyright (c) 2023 Fancy Code. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "SharedData.h" +#include "ILog.h" +#include "ISharedData.h" +static bool ObjectCheck(void *object) +{ + if (nullptr == object) { + LogError("nullptr object!\n"); + return false; + } + if (*((const char **)(((char *)object) - sizeof(ISharedDataHeader))) != GetSharedDataModuleName()) { + LogError("Illegal object!\n"); + return false; + } + return true; +} +void *CreateSharedData(const enum SHARER_NAME name, const char *path, const int projectId) +{ + std::shared_ptr *sharedDataObject = NewSharedData(name, path, projectId); + if (nullptr != sharedDataObject) { + // (*sharedDataObject)->OpenConfigFile(); + } + return sharedDataObject; +} +const StatusCode IMakeSharedData(void *object, const unsigned int readableSize, const unsigned int writableSize) +{ + if (ObjectCheck(object) == true) { + (*(std::shared_ptr *)object)->MakeSharedMemory(readableSize, writableSize); + return CreateStatusCode(STATUS_CODE_OK); + } + return CreateStatusCode(STATUS_CODE_INVALID_PARAMENTER); +} +const StatusCode ICleanSharedData(void *object) +{ + if (ObjectCheck(object) == true) { + // (*(std::shared_ptr *)object)->MakeSharedMemory(readableSize, writableSize); + return CreateStatusCode(STATUS_CODE_INVALID_PARAMENTER); + } + return CreateStatusCode(STATUS_CODE_INVALID_PARAMENTER); +} +const StatusCode IGetReadableData(void *object, void *buf, const unsigned int bufLength) +{ + if (ObjectCheck(object) == true) { + return (*(std::shared_ptr *)object)->GetReadableMemory(buf, bufLength); + } + return CreateStatusCode(STATUS_CODE_INVALID_PARAMENTER); +} +void ISetWritableData(void *object, void *buf, const unsigned int bufLength) +{ + if (ObjectCheck(object) == true) { + (*(std::shared_ptr *)object)->SetWritableMemory(buf, bufLength); + } +} +void IShareDataFree(void *object) +{ + if (ObjectCheck(object) == true) { + (*(std::shared_ptr *)object)->FreeSharedMemory(); + (*(std::shared_ptr *)object).reset(); + free(((char *)object) - sizeof(ISharedDataHeader)); + } +} \ No newline at end of file diff --git a/utils/SharedData/src/SharedDataCode.c b/utils/SharedData/src/SharedDataCode.c new file mode 100644 index 0000000..ea96da8 --- /dev/null +++ b/utils/SharedData/src/SharedDataCode.c @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2023 Fancy Code. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "SharedDataCode.h" +#include "ILog.h" +#include +static const char *StatusCodeString[SHARED_DATA_CODE_END - STATUS_CODE_END + 1] = { + "SHARED_DATA_CODE_INIT_FAILED", "SHARED_DATA_CODE_WRONG_PEER_PARAMETERS", "SHARED_DATA_CODE_END"}; +static const char *_PrintStringCode_(const StatusCode this) +{ + if (STATUS_CODE_END <= this.mStatusCode && this.mStatusCode <= SHARED_DATA_CODE_END) { + LogInfo("Status code = [ %s ]\n", StatusCodeString[this.mStatusCode - STATUS_CODE_END]); + return StatusCodeString[this.mStatusCode - STATUS_CODE_END]; + } + LogError("Status code = [ %s ]\n", StatusCodeString[SHARED_DATA_CODE_END - STATUS_CODE_END]); + return StatusCodeString[SHARED_DATA_CODE_END - STATUS_CODE_END]; +} +static const bool CodeEqual(const StatusCode code, const char *value) +{ + if (STATUS_CODE_END >= code.mStatusCode || code.mStatusCode >= SHARED_DATA_CODE_END) { + return false; + } + if (strlen(value) != strlen(StatusCodeString[code.mStatusCode - STATUS_CODE_END])) { + return false; + } + if (memcmp(value, StatusCodeString[code.mStatusCode - STATUS_CODE_END], strlen(value)) == 0) { + return true; + } + return false; +} +static StatusCode NewStatusCode(const long int code) +{ + StatusCode result = {_PrintStringCode_, CodeEqual, code}; + return result; +} +const StatusCode CreateSharedDataCode(const long int code) +{ + if (STATUS_CODE_END <= code && code <= SHARED_DATA_CODE_END) { + return NewStatusCode(code); + } + LogError("undefined code.\n"); + return NewStatusCode(SHARED_DATA_CODE_END); +} \ No newline at end of file diff --git a/utils/SharedData/src/SharedDataCode.h b/utils/SharedData/src/SharedDataCode.h new file mode 100644 index 0000000..7312b42 --- /dev/null +++ b/utils/SharedData/src/SharedDataCode.h @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2023 Fancy Code. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef SHARED_DATA_CODE_H +#define SHARED_DATA_CODE_H +#include "SharedData.h" +#include "StatusCode.h" +#include +#ifdef __cplusplus +extern "C" { +#endif +const StatusCode CreateSharedDataCode(const long int code); +#ifdef __cplusplus +} +#endif +#endif \ No newline at end of file diff --git a/utils/SharedData/src/SharedDataImpl.cpp b/utils/SharedData/src/SharedDataImpl.cpp new file mode 100644 index 0000000..a3dbdab --- /dev/null +++ b/utils/SharedData/src/SharedDataImpl.cpp @@ -0,0 +1,142 @@ +/* + * Copyright (c) 2023 Fancy Code. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "SharedDataImpl.h" +#include "ILog.h" +#include "SharedDataCode.h" +#include +static const char *SHARED_DATA_NAME = "shared_data"; +constexpr short THERE_TWO_USER_DATA_HEADER = 2; +SharedDataImpl::SharedDataImpl(const SHARER_NAME &sharerName, const char *path, const int &projectId) + : SharedMemory(path, projectId), mSharerName(sharerName) +{ + mPrimaryReadSize = 0; + mMinorReadSize = 0; + mSharedMemeory = nullptr; +} +void SharedDataImpl::MakeSharedMemory(const unsigned int readableSize, const unsigned int writableSize) +{ + if (SHARER_NAME_PRIMARY == mSharerName) { + mPrimaryReadSize = readableSize; + mMinorReadSize = writableSize; + } + else if (SHARER_NAME_MINOR == mSharerName) { + mPrimaryReadSize = writableSize; + mMinorReadSize = readableSize; + } + else { + LogError("Make shared memory failed.\n"); + return; + } + const int SHARED_MEMORY_SIZE = readableSize + writableSize + sizeof(UserDataHeader) * THERE_TWO_USER_DATA_HEADER; + SharedMemory::MakeSharedMemory(SHARED_MEMORY_SIZE); + mSharedMemeory = SharedMemory::GetMemory(); + WritableDataInit(); +} +const StatusCode SharedDataImpl::GetReadableMemory(void *buf, const unsigned int &bufLength) +{ + if (nullptr == mSharedMemeory) { + LogError("mSharedMemeory is nullptr, failed.\n"); + return CreateStatusCode(STATUS_CODE_NOT_OK); + } + if (SHARER_NAME_PRIMARY == mSharerName && bufLength == mPrimaryReadSize) { + UserDataHeader *readableHeader = (UserDataHeader *)((char *)mSharedMemeory); + LogInfo("Want to read %d and can be read %d\n", bufLength, readableHeader->mDataLength); + if (memcmp(readableHeader->mUserName, USER_NAME_INIT_NAME, USER_NAME_BUF_LENGTH) == 0 && + bufLength == readableHeader->mDataLength) { + memcpy(buf, (char *)mSharedMemeory + sizeof(UserDataHeader), bufLength); + } + else { + LogError("Readable memory didn't init yet or init error.\n"); + return CreateSharedDataCode(SHARED_DATA_CODE_WRONG_PEER_PARAMETERS); + } + } + else if (SHARER_NAME_MINOR == mSharerName && bufLength == mMinorReadSize) { + UserDataHeader *readableHeader = + (UserDataHeader *)((char *)mSharedMemeory + sizeof(UserDataHeader) + mPrimaryReadSize); + LogInfo("Want to read %d and can be read %d\n", bufLength, readableHeader->mDataLength); + if (memcmp(readableHeader->mUserName, USER_NAME_INIT_NAME, USER_NAME_BUF_LENGTH) == 0 && + bufLength == readableHeader->mDataLength) { + memcpy(buf, + (char *)mSharedMemeory + sizeof(UserDataHeader) * THERE_TWO_USER_DATA_HEADER + mPrimaryReadSize, + bufLength); + } + else { + LogError("Readable memory didn't init yet or init error.\n"); + return CreateSharedDataCode(SHARED_DATA_CODE_WRONG_PEER_PARAMETERS); + } + } + else { + LogError("Get readable memory failed.\n"); + return CreateStatusCode(STATUS_CODE_INVALID_PARAMENTER); + } + return CreateStatusCode(STATUS_CODE_OK); +} +void SharedDataImpl::SetWritableMemory(void *buf, const unsigned int &bufLength) +{ + if (nullptr == mSharedMemeory) { + LogError("mSharedMemeory is nullptr, failed.\n"); + return; + } + if (SHARER_NAME_PRIMARY == mSharerName && bufLength == mMinorReadSize) { + UserDataHeader *readableHeader = (UserDataHeader *)((char *)mSharedMemeory); + if (memcmp(readableHeader->mUserName, USER_NAME_INIT_NAME, USER_NAME_BUF_LENGTH) == 0) { + if (mPrimaryReadSize == readableHeader->mDataLength) { + memcpy((char *)mSharedMemeory + sizeof(UserDataHeader) * THERE_TWO_USER_DATA_HEADER + mPrimaryReadSize, + buf, + bufLength); + } + else { + LogError("Peer not match.\n"); + } + } + else { + memcpy((char *)mSharedMemeory + sizeof(UserDataHeader) * THERE_TWO_USER_DATA_HEADER + mPrimaryReadSize, + buf, + bufLength); + } + } + else if (SHARER_NAME_MINOR == mSharerName && bufLength == mPrimaryReadSize) { + memcpy((char *)mSharedMemeory + sizeof(UserDataHeader), buf, bufLength); + } + else { + LogError("Set writable memory failed.\n"); + } +} +StatusCode SharedDataImpl::FreeSharedMemory(void) +{ + return CleanSharedMemory(); +} +void SharedDataImpl::WritableDataInit(void) +{ + if (nullptr == mSharedMemeory) { + LogError("mSharedMemeory is nullptr, failed.\n"); + return; + } + if (SHARER_NAME_PRIMARY == mSharerName) { + UserDataHeader *writableHeader = + (UserDataHeader *)((char *)mSharedMemeory + sizeof(UserDataHeader) + mPrimaryReadSize); + memcpy(writableHeader->mUserName, USER_NAME_INIT_NAME, USER_NAME_BUF_LENGTH); + writableHeader->mDataLength = mMinorReadSize; + } + else if (SHARER_NAME_MINOR == mSharerName) { + UserDataHeader *writableHeader = (UserDataHeader *)((char *)mSharedMemeory); + memcpy(writableHeader->mUserName, USER_NAME_INIT_NAME, USER_NAME_BUF_LENGTH); + writableHeader->mDataLength = mPrimaryReadSize; + } + else { + LogError("Make shared memory failed.\n"); + return; + } +} \ No newline at end of file diff --git a/utils/SharedData/src/SharedDataImpl.h b/utils/SharedData/src/SharedDataImpl.h new file mode 100644 index 0000000..9abf720 --- /dev/null +++ b/utils/SharedData/src/SharedDataImpl.h @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2023 Fancy Code. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef SHARED_DATA_IMPL_H +#define SHARED_DATA_IMPL_H +#include "ISharedData.h" +#include "SharedMemory.h" +#include +// #include +constexpr int USER_NAME_BUF_LENGTH = 4; +#define USER_NAME_INIT_NAME "XING" +enum USER_DATA_INIT +{ + USER_DATA_INIT_SUCCEED = 0, + USER_DATA_INIT_FAILED, + USER_DATA_INIT_END +}; +typedef struct shared_data_header +{ + const char *mCheckName; +} SharedDataHeader; +// TODO: Use mutex to luck memory data? +// typedef struct shared_memory_header +// { +// std::mutex mMutex; +// }; +typedef struct user_data_header +{ + char mUserName[USER_NAME_BUF_LENGTH]; + unsigned int mDataLength; + int mIsInit; +} UserDataHeader; +class SharedDataImpl : public ISharedData, public SharedMemory +{ +public: + SharedDataImpl(const SHARER_NAME &sharerName, const char *path, const int &projectId); + virtual ~SharedDataImpl() = default; + void MakeSharedMemory(const unsigned int readableSize, const unsigned int writableSize) override; + const StatusCode GetReadableMemory(void *buf, const unsigned int &bufLength) override; + void SetWritableMemory(void *buf, const unsigned int &bufLength) override; + StatusCode FreeSharedMemory(void) override; + +private: + void WritableDataInit(void); + +private: + const SHARER_NAME mSharerName; + unsigned int mPrimaryReadSize; + unsigned int mMinorReadSize; + void *mSharedMemeory; +}; +#endif \ No newline at end of file diff --git a/utils/SharedData/src/SharedMemory.cpp b/utils/SharedData/src/SharedMemory.cpp new file mode 100644 index 0000000..0f18784 --- /dev/null +++ b/utils/SharedData/src/SharedMemory.cpp @@ -0,0 +1,75 @@ +/* + * Copyright (c) 2023 Fancy Code. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "SharedMemory.h" +#include "ILog.h" +#include "LinuxApi.h" +#include "SharedDataCode.h" +#include +#include +#include +#include +#include +#include +#include +constexpr int SHMGET_FAILED = -1; +SharedMemory::SharedMemory(const char *path, const int &projectId) : mPath(path), mProjectId(projectId) +{ + mId = SHMGET_FAILED; +} +StatusCode SharedMemory::MakeSharedMemory(const int &size) +{ + char touchPath[128] = {0}; + if (access(mPath, F_OK) != 0) { + sprintf(touchPath, "%s %s", "touch", mPath); + fx_system(touchPath); + } + key_t key = ftok(mPath, mProjectId); + if (key < 0) { + LogError("ftok failed.\n"); + return CreateStatusCode(STATUS_CODE_NOT_OK); + } + mId = shmget(key, size, IPC_CREAT | 0666); + if (mId == SHMGET_FAILED) { + constexpr int MAYBE_CODE_22_MEANS_PEER_SIZE_WAS_NOT_MATCH = 22; + if (MAYBE_CODE_22_MEANS_PEER_SIZE_WAS_NOT_MATCH == errno) { + LogInfo("errno = %d, errmsg = %s\n", errno, strerror(errno)); + return CreateSharedDataCode(SHARED_DATA_CODE_WRONG_PEER_PARAMETERS); + } + LogError("shmget failed. memory size = %d\n", size); + return CreateStatusCode(STATUS_CODE_NOT_OK); + } + LogInfo("Make shared memory succeed. memory size = %d\n", size); + return CreateStatusCode(STATUS_CODE_OK); +} +StatusCode SharedMemory::CleanSharedMemory(void) +{ + if (SHMGET_FAILED == mId) { + LogError("mId error.\n"); + return CreateStatusCode(STATUS_CODE_NOT_OK); + } + if (shmctl(mId, IPC_RMID, NULL) < 0) { + LogError("shmctl failed.\n"); + return CreateStatusCode(STATUS_CODE_NOT_OK); + } + return CreateStatusCode(STATUS_CODE_OK); +} +void *SharedMemory::GetMemory(void) +{ + if (SHMGET_FAILED == mId) { + LogError("mId error.\n"); + return nullptr; + } + return shmat(mId, NULL, 0); +} \ No newline at end of file diff --git a/utils/SharedData/src/SharedMemory.h b/utils/SharedData/src/SharedMemory.h new file mode 100644 index 0000000..c2c3675 --- /dev/null +++ b/utils/SharedData/src/SharedMemory.h @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2023 Fancy Code. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef SHARED_MEMORY_H +#define SHARED_MEMORY_H +#include "StatusCode.h" +class SharedMemory +{ +public: + SharedMemory(const char *path, const int &projectId); + virtual ~SharedMemory() = default; + StatusCode MakeSharedMemory(const int &size); + StatusCode CleanSharedMemory(void); + void *GetMemory(void); + +private: + const char *mPath; + const int mProjectId; + int mId; +}; +#endif // !SHARED_MEMORY_H \ No newline at end of file diff --git a/utils/StatusCode/CMakeLists.txt b/utils/StatusCode/CMakeLists.txt new file mode 100644 index 0000000..3f2d415 --- /dev/null +++ b/utils/StatusCode/CMakeLists.txt @@ -0,0 +1,55 @@ + +include(${CMAKE_SOURCE_DIR_IPCSDK}/build/global_config.cmake) +set(EXECUTABLE_OUTPUT_PATH ${EXEC_OUTPUT_PATH}) +set(LIBRARY_OUTPUT_PATH ${LIBS_OUTPUT_PATH}) + +include_directories( + ./src + ./include + ${UTILS_SOURCE_PATH}/StatusCode/include + ${UTILS_SOURCE_PATH}/Log/include +) + +#do not rely on any other library +#link_directories( +#) + +aux_source_directory(./src SRC_FILES) + +set(TARGET_NAME StatusCode) +add_library(${TARGET_NAME} STATIC ${SRC_FILES}) +target_link_libraries(${TARGET_NAME} Log) + +if ("${COMPILE_IMPROVE_SUPPORT}" MATCHES "true") +add_custom_target( + StatusCode_code_check + COMMAND ${CLANG_TIDY_EXE} + -checks='${CLANG_TIDY_CHECKS}' + --header-filter=.* + --system-headers=false + ${SRC_FILES} + ${CLANG_TIDY_CONFIG} + -p ${PLATFORM_PATH}/cmake-shell + WORKING_DIRECTORY ${UTILS_SOURCE_PATH}/StatusCode +) +file(GLOB_RECURSE HEADER_FILES *.h) +add_custom_target( + StatusCode_code_format + COMMAND ${CLANG_FORMAT_EXE} + -style=file + -i ${SRC_FILES} ${HEADER_FILES} + WORKING_DIRECTORY ${UTILS_SOURCE_PATH}/StatusCode +) +add_custom_command( + TARGET ${TARGET_NAME} + PRE_BUILD + COMMAND make StatusCode_code_check + COMMAND make StatusCode_code_format + WORKING_DIRECTORY ${PLATFORM_PATH}/cmake-shell/ +) +endif() + +define_file_name(${TARGET_NAME}) + +file(GLOB_RECURSE INSTALL_HEADER_FILES include/*.h) +install(FILES ${INSTALL_HEADER_FILES} DESTINATION include) \ No newline at end of file diff --git a/utils/StatusCode/include/StatusCode.h b/utils/StatusCode/include/StatusCode.h new file mode 100644 index 0000000..4700d39 --- /dev/null +++ b/utils/StatusCode/include/StatusCode.h @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2023 Fancy Code. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef STATUSCODE_H +#define STATUSCODE_H +#include +#ifdef __cplusplus +extern "C" { +#endif +enum STATUS_CODE +{ + STATUS_CODE_OK = 0, + STATUS_CODE_NOT_OK, + STATUS_CODE_VIRTUAL_FUNCTION, + STATUS_CODE_INVALID_PARAMENTER, + STATUS_CODE_MAKE_SHARED_PTR_FAILED, + STATUS_CODE_END +}; +typedef struct status_code StatusCode; +typedef struct status_code +{ + const char *(*mPrintStringCode)(const StatusCode); + const bool (*mCodeEqual)(const StatusCode, const char *); + const long int mStatusCode; +} StatusCode; +const StatusCode CreateStatusCode(const long int code); +static inline const char *PrintStringCode(const StatusCode code) +{ + return code.mPrintStringCode(code); +} +static inline bool IsCodeOK(const StatusCode code) +{ + return STATUS_CODE_OK == code.mStatusCode ? true : false; +} +static inline bool StatusCodeEqual(const StatusCode code, const char *value) +{ + return code.mCodeEqual(code, value); +} +#ifdef __cplusplus +} +#endif +#endif \ No newline at end of file diff --git a/utils/StatusCode/src/StatusCode.c b/utils/StatusCode/src/StatusCode.c new file mode 100644 index 0000000..fa86750 --- /dev/null +++ b/utils/StatusCode/src/StatusCode.c @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2023 Fancy Code. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "StatusCode.h" +#include "ILog.h" +#include +#include +static const char *StatusCodeString[STATUS_CODE_END + 1] = {"STATUS_CODE_OK", + "STATUS_CODE_NOT_OK", + "STATUS_CODE_VIRTUAL_FUNCTION", + "STATUS_CODE_INVALID_PARAMENTER", + "STATUS_CODE_END"}; +static const char *_PrintStringCode_(const StatusCode this) +{ + if (STATUS_CODE_OK <= this.mStatusCode && this.mStatusCode <= STATUS_CODE_END) { + LogInfo("Status code = [ %s ]\n", StatusCodeString[this.mStatusCode]); + return StatusCodeString[this.mStatusCode]; + } + LogError("Status code = [ %s ]\n", StatusCodeString[STATUS_CODE_INVALID_PARAMENTER]); + return StatusCodeString[STATUS_CODE_INVALID_PARAMENTER]; +} +static const bool CodeEqual(const StatusCode code, const char *value) +{ + if (STATUS_CODE_OK >= code.mStatusCode || code.mStatusCode >= STATUS_CODE_END) { + return false; + } + if (strlen(value) != strlen(StatusCodeString[code.mStatusCode])) { + return false; + } + if (memcmp(value, StatusCodeString[code.mStatusCode], strlen(value)) == 0) { + return true; + } + return false; +} +static StatusCode NewStatusCode(const long int code) +{ + StatusCode result = {_PrintStringCode_, CodeEqual, code}; + return result; +} +const StatusCode CreateStatusCode(const long int code) +{ + if (STATUS_CODE_OK <= code && code <= STATUS_CODE_END) { + return NewStatusCode(code); + } + LogError("undefined code.\n"); + return NewStatusCode(STATUS_CODE_END); +} diff --git a/utils/TcpModule/CMakeLists.txt b/utils/TcpModule/CMakeLists.txt new file mode 100644 index 0000000..a3d94d2 --- /dev/null +++ b/utils/TcpModule/CMakeLists.txt @@ -0,0 +1,55 @@ +include(${CMAKE_SOURCE_DIR_IPCSDK}/build/global_config.cmake) +set(EXECUTABLE_OUTPUT_PATH ${EXEC_OUTPUT_PATH}) +set(LIBRARY_OUTPUT_PATH ${LIBS_OUTPUT_PATH}) + +include_directories( + ./src + ./include + ${UTILS_SOURCE_PATH}/LinuxApi/include + ${UTILS_SOURCE_PATH}/StatusCode/include + ${UTILS_SOURCE_PATH}/Log/include + ${EXTERNAL_SOURCE_PATH}/libhv/libhv-1.3.2/include/hv +) +# link_directories( +# ${EXTERNAL_SOURCE_PATH}/libconfig/libconfig-1.7.3/lib/.libs +# ) + +aux_source_directory(./src SRC_FILES) + +set(TARGET_NAME TcpModule) +add_library(${TARGET_NAME} STATIC ${SRC_FILES}) +target_link_libraries(${TARGET_NAME} LinuxApi hv_static StatusCode Log) + +if ("${COMPILE_IMPROVE_SUPPORT}" MATCHES "true") +add_custom_target( + TcpModule_code_check + COMMAND ${CLANG_TIDY_EXE} + -checks='${CLANG_TIDY_CHECKS}' + --header-filter=.* + --system-headers=false + ${SRC_FILES} + ${CLANG_TIDY_CONFIG} + -p ${PLATFORM_PATH}/cmake-shell + WORKING_DIRECTORY ${UTILS_SOURCE_PATH}/TcpModule +) +file(GLOB_RECURSE HEADER_FILES *.h) +add_custom_target( + TcpModule_code_format + COMMAND ${CLANG_FORMAT_EXE} + -style=file + -i ${SRC_FILES} ${HEADER_FILES} + WORKING_DIRECTORY ${UTILS_SOURCE_PATH}/TcpModule +) +add_custom_command( + TARGET ${TARGET_NAME} + PRE_BUILD + COMMAND make TcpModule_code_check + COMMAND make TcpModule_code_format + WORKING_DIRECTORY ${PLATFORM_PATH}/cmake-shell/ +) +endif() + +define_file_name(${TARGET_NAME}) + +file(GLOB_RECURSE INSTALL_HEADER_FILES include/*.h) +install(FILES ${INSTALL_HEADER_FILES} DESTINATION include) \ No newline at end of file diff --git a/utils/TcpModule/include/TcpModule.h b/utils/TcpModule/include/TcpModule.h new file mode 100644 index 0000000..f3a57c5 --- /dev/null +++ b/utils/TcpModule/include/TcpModule.h @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2023 Fancy Code. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef TCP_MODULE_H +#define TCP_MODULE_H +#include +#include +#define TCP_MODULE_WRITE_ERROR -1; +#ifdef __cplusplus +extern "C" { +#endif +typedef void (*TcpReadFunction)(const void *, const ssize_t, const void *); +typedef bool (*TcpAcceptClientFunction)(void *, const char *); +typedef void (*SocketClosedFunction)(const void *); +typedef struct client_accept_parm +{ + const TcpReadFunction mReadFunc; + const SocketClosedFunction mClosedFunc; +} ClientAcceptParam; +typedef struct tcp_server_parm +{ + const char *mIp; + const int mPort; + const TcpAcceptClientFunction mAcceptClientFunc; + const ClientAcceptParam mClientAcceptParam; +} TcpServerParam; +typedef struct tcp_parm +{ + const char *mIp; + const int mPort; + const TcpReadFunction mReadFunc; + const SocketClosedFunction mClosedFunc; +} TcpClientParam; +void *CreateTcpServer(const TcpServerParam param); +void FreeTcpServer(void *object); +ssize_t AcceptClientWrite(void *object, const void *buf, const size_t bufLenght); +void AcceptClientClose(void *object); +void *CreateTcpClient(const TcpClientParam param); +void FreeTcpClient(void *object); +ssize_t TcpClientWrite(void *object, const void *buf, const size_t bufLenght); +#ifdef __cplusplus +} +#endif +#endif \ No newline at end of file diff --git a/utils/TcpModule/src/ITcpClient.cpp b/utils/TcpModule/src/ITcpClient.cpp new file mode 100644 index 0000000..256ab40 --- /dev/null +++ b/utils/TcpModule/src/ITcpClient.cpp @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2023 Fancy Code. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "ITcpClient.h" +#include "ILog.h" +#include "TcpModule.h" +#include +void ITcpClient::Init(void) +{ +} +void ITcpClient::UnInit(void) +{ +} +void ITcpClient::Readed(const void *data, size_t length) +{ +} +ssize_t ITcpClient::Write(const void *buf, const size_t bufLenght) +{ + return TCP_MODULE_WRITE_ERROR; +} +void ITcpClient::Closed(void) +{ +} +static const char *TCP_CLIENT_NAME = "tcp_client"; +const char *GetTcpClientModuleName(void) +{ + return TCP_CLIENT_NAME; +} \ No newline at end of file diff --git a/utils/TcpModule/src/ITcpClient.h b/utils/TcpModule/src/ITcpClient.h new file mode 100644 index 0000000..8948a45 --- /dev/null +++ b/utils/TcpModule/src/ITcpClient.h @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2023 Fancy Code. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef I_TCP_CLIENT_H +#define I_TCP_CLIENT_H +#include "StatusCode.h" +#include +class ITcpClient +{ +public: + ITcpClient() = default; + virtual ~ITcpClient() = default; + virtual void Init(void); + virtual void UnInit(void); + virtual void Readed(const void *data, size_t length); + virtual ssize_t Write(const void *buf, const size_t bufLenght); + virtual void Closed(void); +}; +typedef struct i_tcp_client_header +{ + const char *mCheckName; +} ITcpClientHeader; +typedef struct tcp_client +{ + ITcpClientHeader mHeader; + std::shared_ptr mTcpClient; +} TcpClient; +const char *GetTcpClientModuleName(void); +#endif \ No newline at end of file diff --git a/utils/TcpModule/src/ITcpServer.cpp b/utils/TcpModule/src/ITcpServer.cpp new file mode 100644 index 0000000..a26c05c --- /dev/null +++ b/utils/TcpModule/src/ITcpServer.cpp @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2023 Fancy Code. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "ITcpServer.h" +#include "ILog.h" +#include "TcpModule.h" +#include +void ITcpClientAccept::Close(void) +{ +} +void ITcpClientAccept::Readed(const void *data, size_t length) +{ +} +ssize_t ITcpClientAccept::Write(const void *data, size_t length) +{ + return TCP_MODULE_WRITE_ERROR; +} +void ITcpClientAccept::Closed(void) +{ +} +void ITcpServer::Init(void) +{ +} +void ITcpServer::UnInit(void) +{ +} +static const char *TCP_SERVER_NAME = "tcp_server"; +static const char *TCP_CLIENT_ACCEPT_NAME = "tcp_client_accept"; +const char *GetTcpServerModuleName(void) +{ + return TCP_SERVER_NAME; +} +const char *GetTcpClientAcceptName(void) +{ + return TCP_CLIENT_ACCEPT_NAME; +} +bool TcpClientAcceptObjectCheck(void *object) +{ + if (nullptr == object) { + LogError("nullptr object!\n"); + return false; + } + if (*((const char **)(((char *)object) - sizeof(ITcpServerHeader))) != GetTcpClientAcceptName()) { + LogError("Illegal object!\n"); + return false; + } + return true; +} \ No newline at end of file diff --git a/utils/TcpModule/src/ITcpServer.h b/utils/TcpModule/src/ITcpServer.h new file mode 100644 index 0000000..098ad0b --- /dev/null +++ b/utils/TcpModule/src/ITcpServer.h @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2023 Fancy Code. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef I_TCP_SERVER_H +#define I_TCP_SERVER_H +#include "StatusCode.h" +#include "TcpModule.h" +#include +class ITcpClientAccept +{ +public: + ITcpClientAccept() = default; + virtual ~ITcpClientAccept() = default; + virtual void Close(void); + virtual void Readed(const void *data, size_t length); + virtual ssize_t Write(const void *data, size_t length); + virtual void Closed(void); +}; +class ITcpServer +{ +public: + ITcpServer() = default; + virtual ~ITcpServer() = default; + virtual void Init(void); + virtual void UnInit(void); +}; +typedef struct i_tcp_server_header +{ + const char *mCheckName; +} ITcpServerHeader; +typedef struct tcp_server +{ + ITcpServerHeader mHeader; + std::shared_ptr mTcpServer; +} TcpServer; +typedef struct tcp_client_accept +{ + ITcpServerHeader mHeader; + std::shared_ptr mTcpClientAccept; +} TcpClientAccept; +const char *GetTcpServerModuleName(void); +const char *GetTcpClientAcceptName(void); +bool TcpClientAcceptObjectCheck(void *object); +#endif \ No newline at end of file diff --git a/utils/TcpModule/src/TcpClientImpl.cpp b/utils/TcpModule/src/TcpClientImpl.cpp new file mode 100644 index 0000000..5d76d64 --- /dev/null +++ b/utils/TcpModule/src/TcpClientImpl.cpp @@ -0,0 +1,128 @@ +/* + * Copyright (c) 2023 Fancy Code. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "TcpClientImpl.h" +#include "ILog.h" +#include +static void on_message(hio_t *io, void *buf, int len) +{ + LogInfo("onmessage: %.*s\n", len, (char *)buf); + TcpClientImpl *tcpClient = (TcpClientImpl *)hevent_userdata(io); + tcpClient->Readed(buf, len); +} +static void on_connect(hio_t *io) +{ + LogInfo("onconnect: connfd=%d\n", hio_fd(io)); + hio_setcb_read(io, on_message); + hio_read(io); +} +static void on_close(hio_t *io) +{ + LogInfo("onclose: connfd=%d error=%d\n", hio_fd(io), hio_error(io)); + TcpClientImpl *tcpClient = (TcpClientImpl *)hevent_userdata(io); + tcpClient->Closed(); +} +TcpClientImpl::TcpClientImpl(const TcpClientParam ¶m, const void *object) : mParam(param), mObjectThis(object) +{ +} +void TcpClientImpl::Init(void) +{ + constexpr int NO_FALGS = 0; + mLoop = hloop_new(NO_FALGS); + if (nullptr == mLoop) { + LogError("TcpClientImpl::Init hloop_new failed.\n"); + return; + } + hio_t *io = hio_create_socket(mLoop, mParam.mIp, mParam.mPort, HIO_TYPE_TCP, HIO_CLIENT_SIDE); + if (nullptr == io) { + LogError("TcpClientImpl::Init hio_create_socket failed.\n"); + return; + } + hevent_set_userdata(io, this); + hio_setcb_connect(io, on_connect); + hio_setcb_close(io, on_close); + hio_connect(io); + mIo = io; + std::shared_ptr impl = std::dynamic_pointer_cast(shared_from_this()); + auto recvThread = [](std::shared_ptr tcpClient) { + tcpClient->Loop(); + }; + mTcpClientThread = std::thread(recvThread, impl); +} +void TcpClientImpl::UnInit(void) +{ + if (nullptr != mIo) { + LogInfo("Close io.\n"); + hio_close(mIo); + mIo = nullptr; + } + if (mTcpClientThread.joinable()) { + mTcpClientThread.join(); + } +} +void TcpClientImpl::Readed(const void *data, size_t length) +{ + if (nullptr != mParam.mReadFunc) { + mParam.mReadFunc(data, length, nullptr); + return; + } + LogError("mParam.mReadFunc is nullptr.\n"); +} +ssize_t TcpClientImpl::Write(const void *buf, const size_t bufLenght) +{ + std::lock_guard locker(mMutex); + if (nullptr == mIo) { + LogError("mIo is nullptr.\n"); + return TCP_MODULE_WRITE_ERROR; + } + return hio_write(mIo, buf, bufLenght); +} +void TcpClientImpl::Closed(void) +{ + std::lock_guard locker(mMutex); + mIo = nullptr; + if (nullptr != mParam.mClosedFunc) { + mParam.mClosedFunc(mObjectThis); + } + if (nullptr != mLoop) { + LogInfo("Stop loop.\n"); + hloop_stop(mLoop); + } +} +void TcpClientImpl::Loop(void) +{ + if (nullptr == mLoop) { + LogError("mLoop is null\n"); + return; + } + hloop_run(mLoop); + hloop_free(&mLoop); + mLoop = nullptr; +} +std::shared_ptr *NewTcpClient(const TcpClientParam ¶m) +{ + LogInfo("Create tcp server object.\n"); + TcpClient *impl = (TcpClient *)malloc(sizeof(TcpClient)); + if (nullptr == impl) { + LogError("NewTcpServer::malloc failed.\n"); + return nullptr; + } + TcpClient tmp; + memcpy((void *)impl, (void *)&tmp, sizeof(TcpClient)); + impl->mHeader.mCheckName = GetTcpClientModuleName(); + std::shared_ptr *objectThis = + (std::shared_ptr *)(((char *)impl) + sizeof(ITcpClientHeader)); + impl->mTcpClient = std::make_shared(param, objectThis); + return objectThis; +} \ No newline at end of file diff --git a/utils/TcpModule/src/TcpClientImpl.h b/utils/TcpModule/src/TcpClientImpl.h new file mode 100644 index 0000000..4e0a337 --- /dev/null +++ b/utils/TcpModule/src/TcpClientImpl.h @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2023 Fancy Code. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef TCP_CLIENT_IMPL_H +#define TCP_CLIENT_IMPL_H +#include "ITcpClient.h" +#include "TcpModule.h" +#include "hloop.h" +#include "hsocket.h" +#include "hssl.h" +#include +#include +#include +class TcpClientImpl : public ITcpClient, public std::enable_shared_from_this +{ +public: + TcpClientImpl(const TcpClientParam ¶m, const void *object); + virtual ~TcpClientImpl() = default; + void Init(void) override; + void UnInit(void) override; + void Readed(const void *data, size_t length) override; + ssize_t Write(const void *buf, const size_t bufLenght) override; + void Closed(void) override; + void Loop(void); + +private: + std::mutex mMutex; + hloop_t *mLoop; + hio_t *mIo; + const TcpClientParam mParam; + std::thread mTcpClientThread; + const void *mObjectThis; +}; +std::shared_ptr *NewTcpClient(const TcpClientParam ¶m); +#endif \ No newline at end of file diff --git a/utils/TcpModule/src/TcpModule.cpp b/utils/TcpModule/src/TcpModule.cpp new file mode 100644 index 0000000..1cfa9df --- /dev/null +++ b/utils/TcpModule/src/TcpModule.cpp @@ -0,0 +1,96 @@ +/* + * Copyright (c) 2023 Fancy Code. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "TcpModule.h" +#include "ILog.h" +#include "ITcpServer.h" +#include "TcpClientImpl.h" +#include "TcpModuleImpl.h" +#include "TcpModuleMakePtr.h" +static bool TcpServerObjectCheck(void *object) +{ + if (nullptr == object) { + LogError("nullptr object!\n"); + return false; + } + if (*((const char **)(((char *)object) - sizeof(ITcpServerHeader))) != GetTcpServerModuleName()) { + LogError("Illegal object!\n"); + return false; + } + return true; +} +static bool TcpClientObjectCheck(void *object) +{ + if (nullptr == object) { + LogError("nullptr object!\n"); + return false; + } + if (*((const char **)(((char *)object) - sizeof(ITcpClientHeader))) != GetTcpClientModuleName()) { + LogError("Illegal object!\n"); + return false; + } + return true; +} +void *CreateTcpServer(const TcpServerParam param) +{ + std::shared_ptr *server = TcpModuleMakePtr::GetInstance()->CreateTcpServer(param); + if (nullptr != *server) { + (*server)->Init(); + } + return server; +} +void FreeTcpServer(void *object) +{ + if (TcpServerObjectCheck(object) == true) { + (*(std::shared_ptr *)object)->UnInit(); + (*(std::shared_ptr *)object).reset(); + free(((char *)object) - sizeof(ITcpServerHeader)); // TODO: bug? + } +} +ssize_t AcceptClientWrite(void *object, const void *buf, const size_t bufLenght) +{ + if (TcpClientAcceptObjectCheck(object) == true) { + return (*(std::shared_ptr *)object)->Write(buf, bufLenght); + } + return TCP_MODULE_WRITE_ERROR; +} +void AcceptClientClose(void *object) +{ + if (TcpClientAcceptObjectCheck(object) == true) { + return (*(std::shared_ptr *)object)->Close(); + } +} +void *CreateTcpClient(const TcpClientParam param) +{ + std::shared_ptr *client = TcpModuleMakePtr::GetInstance()->CreateTcpClient(param); + if (nullptr != *client) { + (*client)->Init(); + } + return client; +} +void FreeTcpClient(void *object) +{ + if (TcpClientObjectCheck(object) == true) { + (*(std::shared_ptr *)object)->UnInit(); + (*(std::shared_ptr *)object).reset(); + free(((char *)object) - sizeof(ITcpClientHeader)); + } +} +ssize_t TcpClientWrite(void *object, const void *buf, const size_t bufLenght) +{ + if (TcpClientObjectCheck(object) == true) { + return (*(std::shared_ptr *)object)->Write(buf, bufLenght); + } + return -1; +} \ No newline at end of file diff --git a/utils/TcpModule/src/TcpModuleImpl.cpp b/utils/TcpModule/src/TcpModuleImpl.cpp new file mode 100644 index 0000000..08e20a8 --- /dev/null +++ b/utils/TcpModule/src/TcpModuleImpl.cpp @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2023 Fancy Code. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "TcpModuleImpl.h" +#include "ILog.h" +#include +// static const char *TCP_MODULE_NAME = "tcp_module"; +// const char *GetTcpModuleName(void) +// { +// return TCP_MODULE_NAME; +// } +// void *NewTcpModuleImpl(const TcpClientParam &tcpParam) +// { +// if (nullptr == tcpParam.mIp) { +// LogError("Parament error, nullptr == tcpParam.mIp\n"); +// return nullptr; +// } +// LogInfo("Create the tcp module object.\n"); +// TcpServer *impl = (TcpServer *)malloc(sizeof(TcpServer)); +// TcpServer tmp; +// memcpy((void *)impl, (void *)&tmp, sizeof(TcpServer)); +// impl->mHeader.mCheckName = TCP_MODULE_NAME; +// impl->mTcpImpl = std::make_shared(tcpParam); +// return (void *)(((char *)impl) + sizeof(TcpModuleHeader)); +// } +// void *NewTcpServer(const TcpClientParam &tcpParam) +// { +// return nullptr; +// } \ No newline at end of file diff --git a/utils/TcpModule/src/TcpModuleImpl.h b/utils/TcpModule/src/TcpModuleImpl.h new file mode 100644 index 0000000..3320ea4 --- /dev/null +++ b/utils/TcpModule/src/TcpModuleImpl.h @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2023 Fancy Code. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef TCP_MODULE_IMPL_H +#define TCP_MODULE_IMPL_H +#include "StatusCode.h" +#include "TcpModule.h" +#include +// #define TCP_MODULE_INIT_NAME "UART" +// typedef struct tcp_module_header +// { +// const char *mCheckName; +// } TcpModuleHeader; +// class TcpModuleImpl +// { +// public: +// TcpModuleImpl(const TcpClientParam &uatrInfo); +// virtual ~TcpModuleImpl() = default; + +// private: +// const StatusCode SetConfig(void); + +// private: +// const TcpClientParam mUatrInfo; +// int mFd; +// }; +// TODO: There may be a CPU byte alignment bug. +// typedef struct tcp_server TcpServer; +// typedef struct tcp_server +// { +// TcpModuleHeader mHeader; +// std::shared_ptr mTcpImpl; +// } TcpServer; +// void *NewTcpModuleImpl(const TcpClientParam &tcpParam); +// void *NewTcpServer(const TcpClientParam &tcpParam); +// static inline TcpServer *TcpModuleImplConvert(void *object) +// { +// return ((TcpServer *)(((char *)object) - sizeof(TcpModuleHeader))); +// } +// const char *GetTcpModuleName(void); +#endif \ No newline at end of file diff --git a/utils/TcpModule/src/TcpModuleMakePtr.cpp b/utils/TcpModule/src/TcpModuleMakePtr.cpp new file mode 100644 index 0000000..975d350 --- /dev/null +++ b/utils/TcpModule/src/TcpModuleMakePtr.cpp @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2023 Fancy Code. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "TcpModuleMakePtr.h" +#include "ILog.h" +#include "TcpClientImpl.h" +#include "TcpServerImpl.h" +std::shared_ptr &TcpModuleMakePtr::GetInstance(std::shared_ptr *impl) +{ + static auto instance = std::make_shared(); + if (impl) { + instance = *impl; + } + return instance; +} +std::shared_ptr *TcpModuleMakePtr::CreateTcpServer(const TcpServerParam ¶m) +{ + return NewTcpServer(param); +} +std::shared_ptr *TcpModuleMakePtr::CreateTcpClient(const TcpClientParam ¶m) +{ + return NewTcpClient(param); +} \ No newline at end of file diff --git a/utils/TcpModule/src/TcpModuleMakePtr.h b/utils/TcpModule/src/TcpModuleMakePtr.h new file mode 100644 index 0000000..f381029 --- /dev/null +++ b/utils/TcpModule/src/TcpModuleMakePtr.h @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2023 Fancy Code. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef TCP_MODULE_MAKE_PTR_H +#define TCP_MODULE_MAKE_PTR_H +#include "ITcpClient.h" +#include "ITcpServer.h" +#include "StatusCode.h" +#include "TcpModule.h" +#include +class TcpModuleMakePtr +{ +public: + TcpModuleMakePtr() = default; + virtual ~TcpModuleMakePtr() = default; + static std::shared_ptr &GetInstance(std::shared_ptr *impl = nullptr); + virtual std::shared_ptr *CreateTcpServer(const TcpServerParam ¶m); + virtual std::shared_ptr *CreateTcpClient(const TcpClientParam ¶m); +}; +#endif // !TCP_MODULE_MAKE_PTR_H \ No newline at end of file diff --git a/utils/TcpModule/src/TcpServerHandle.cpp b/utils/TcpModule/src/TcpServerHandle.cpp new file mode 100644 index 0000000..3006dc0 --- /dev/null +++ b/utils/TcpModule/src/TcpServerHandle.cpp @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2023 Fancy Code. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "TcpServerHandle.h" +#include "ILog.h" +std::shared_ptr &TcpServerHandle::GetInstance(std::shared_ptr *impl) +{ + static auto instance = std::make_shared(); + if (impl) { + if (instance.use_count() == 1) { + LogInfo("Instance changed succeed.\n"); + instance = *impl; + } + else { + LogError("Can't changing the instance becase of using by some one.\n"); + } + } + return instance; +} +void TcpServerHandle::AddServer(const int &fd, const std::shared_ptr &server) +{ + LogInfo("AddServer fd = %d\n", fd); + mFd[fd] = server; +} +bool TcpServerHandle::GetServer(const int &fd, std::shared_ptr &server) +{ + auto it = mFd.find(fd); + if (it != mFd.end()) { + server = it->second; + return true; + } + LogError("There no ITcpServer fd = %d\n", fd); + return false; +} \ No newline at end of file diff --git a/utils/TcpModule/src/TcpServerHandle.h b/utils/TcpModule/src/TcpServerHandle.h new file mode 100644 index 0000000..8413bec --- /dev/null +++ b/utils/TcpModule/src/TcpServerHandle.h @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2023 Fancy Code. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef TCP_SERVER_HANDLE_H +#define TCP_SERVER_HANDLE_H +#include "ITcpServer.h" +#include +#include +class TcpServerHandle +{ +public: + TcpServerHandle() = default; + ~TcpServerHandle() = default; + static std::shared_ptr &GetInstance(std::shared_ptr *impl = nullptr); + void AddServer(const int &fd, const std::shared_ptr &server); + bool GetServer(const int &fd, std::shared_ptr &server); + +private: + std::map> mFd; +}; +#endif \ No newline at end of file diff --git a/utils/TcpModule/src/TcpServerImpl.cpp b/utils/TcpModule/src/TcpServerImpl.cpp new file mode 100644 index 0000000..231a1c2 --- /dev/null +++ b/utils/TcpModule/src/TcpServerImpl.cpp @@ -0,0 +1,248 @@ +/* + * Copyright (c) 2023 Fancy Code. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "TcpServerImpl.h" +#include "ILog.h" +#include "TcpServerHandle.h" +static void on_close(hio_t *io) +{ + LogInfo("on_close fd=%d error=%d\n", hio_fd(io), hio_error(io)); + TcpServerImpl *server = (TcpServerImpl *)hevent_userdata(io); + server->RemoveClient(io); +} +static void server_on_close(hio_t *io) +{ + LogInfo("server_on_close fd=%d error=%d\n", hio_fd(io), hio_error(io)); + TcpServerImpl *server = (TcpServerImpl *)hevent_userdata(io); + server->Closed(); +} +static void on_recv(hio_t *io, void *buf, int readbytes) +{ + LogInfo("on_recv fd=%d readbytes=%d\n", hio_fd(io), readbytes); + char localaddrstr[SOCKADDR_STRLEN] = {0}; + char peeraddrstr[SOCKADDR_STRLEN] = {0}; + LogInfo( + "[%s] <=> [%s]\n", SOCKADDR_STR(hio_localaddr(io), localaddrstr), SOCKADDR_STR(hio_peeraddr(io), peeraddrstr)); + LogInfo("< %.*s", readbytes, (char *)buf); + TcpServerImpl *server = (TcpServerImpl *)hevent_userdata(io); + std::shared_ptr *client = server->GetClient(io); + (*client)->Readed((const char *)buf, readbytes); +} +static void on_accept(hio_t *io) +{ + LogInfo("on_accept connfd=%d\n", hio_fd(io)); + char localaddrstr[SOCKADDR_STRLEN] = {0}; + char peeraddrstr[SOCKADDR_STRLEN] = {0}; + LogInfo("accept connfd=%d [%s] <= [%s]\n", + hio_fd(io), + SOCKADDR_STR(hio_localaddr(io), localaddrstr), + SOCKADDR_STR(hio_peeraddr(io), peeraddrstr)); + + hio_setcb_close(io, on_close); + hio_setcb_read(io, on_recv); + // std::shared_ptr *client = NewTcpClientAccept(io); + TcpServerImpl *server = (TcpServerImpl *)hevent_userdata(io); + server->AddClient(io); + hio_read_start(io); +} +TcpClientAcceptImpl::TcpClientAcceptImpl(const hio_t *io, const void *object, const ClientAcceptParam ¶m) + : mIo(io), mObjectThis(object), mParam(param) +{ +} +void TcpClientAcceptImpl::Close(void) +{ + if (nullptr != mIo) { + hio_t *io = (hio_t *)mIo; + hio_close(io); + } +} +void TcpClientAcceptImpl::Readed(const void *data, size_t length) +{ + if (nullptr != mParam.mReadFunc) { + mParam.mReadFunc(data, length, mObjectThis); + return; + } + LogWarning("mParam.mClosedFunc is null\n"); +} +ssize_t TcpClientAcceptImpl::Write(const void *data, size_t length) +{ + if (mIo) { + hio_t *io = (hio_t *)mIo; + return hio_write(io, data, length); + } + LogError("mIo is null\n"); + return TCP_MODULE_WRITE_ERROR; +} +void TcpClientAcceptImpl::Closed(void) +{ + if (nullptr != mParam.mClosedFunc) { + mParam.mClosedFunc(mObjectThis); + return; + } + LogWarning("mParam.mClosedFunc is null\n"); +} +TcpServerImpl::TcpServerImpl(const TcpServerParam param) : mParam(param) +{ + mLoop = nullptr; + mIo = nullptr; +} +void TcpServerImpl::Init(void) +{ + constexpr int NO_FALGS = 0; + mLoop = hloop_new(NO_FALGS); + if (nullptr == mLoop) { + LogError("hloop_new failed\n"); + return; + } + hio_t *listenio = hloop_create_tcp_server(mLoop, mParam.mIp, mParam.mPort, on_accept); + if (nullptr == listenio) { + LogError("hloop_create_tcp_server failed\n"); + return; + } + LogInfo("listenfd=%d\n", hio_fd(listenio)); + hevent_set_userdata(listenio, this); + mIo = listenio; + hio_setcb_close(mIo, server_on_close); + std::shared_ptr server = shared_from_this(); + std::shared_ptr impl = std::dynamic_pointer_cast(server); + auto recvThread = [](std::shared_ptr tcpServer) { + tcpServer->Loop(); + }; + mTcpServerThread = std::thread(recvThread, impl); +} +void TcpServerImpl::UnInit(void) +{ + LogInfo("UnInit TcpServerImpl\n"); + FreeClients(); + if (nullptr != mIo) { + hio_close(mIo); + mIo = nullptr; + } + if (mTcpServerThread.joinable()) { + mTcpServerThread.join(); + } +} +void TcpServerImpl::Loop(void) +{ + if (nullptr == mLoop) { + LogError("mLoop is null\n"); + return; + } + hloop_run(mLoop); + hloop_free(&mLoop); + mLoop = nullptr; +} +void TcpServerImpl::AddClient(hio_t *io) +{ + mMutex.lock(); + char localaddrstr[SOCKADDR_STRLEN] = {0}; + char peeraddrstr[SOCKADDR_STRLEN] = {0}; + LogInfo("accept connfd=%d [%s] <= [%s]\n", + hio_fd(io), + SOCKADDR_STR(hio_localaddr(io), localaddrstr), + SOCKADDR_STR(hio_peeraddr(io), peeraddrstr)); + std::shared_ptr *addClient = NewTcpClientAccept(io, mParam.mClientAcceptParam); + mClients[hio_fd(io)] = addClient; + if (mParam.mAcceptClientFunc) { + if (mParam.mAcceptClientFunc(addClient, peeraddrstr) == true) { + mMutex.unlock(); + return; + } + LogWarning("User did not accept client.\n"); + } + mMutex.unlock(); + hio_close(io); + LogWarning("AddClient failed.\n"); +} +std::shared_ptr *TcpServerImpl::GetClient(hio_t *io) +{ + std::lock_guard locker(mMutex); + auto it = mClients.find(hio_fd(io)); + if (it != mClients.end()) { + return it->second; + } + else { + LogError("GetClient failed, client not exit.\n"); + return nullptr; + } +} +void TcpServerImpl::RemoveClient(hio_t *io) +{ + std::lock_guard locker(mMutex); + auto it = mClients.find(hio_fd(io)); + if (it != mClients.end()) { + void *object = (void *)it->second; + if (TcpClientAcceptObjectCheck(object) == true) { + (*(std::shared_ptr *)object)->Closed(); + (*(std::shared_ptr *)object).reset(); + free(((char *)object) - sizeof(ITcpServerHeader)); + } + mClients.erase(it); + } + else { + LogError("RemoveClient failed, client not exit.\n"); + } +} +void TcpServerImpl::FreeClients(void) +{ + std::lock_guard locker(mMutex); + for (auto &client : mClients) { + if (nullptr != client.second) { + void *object = (void *)client.second; + if (TcpClientAcceptObjectCheck(object) == true) { + (*(std::shared_ptr *)object)->Closed(); + (*(std::shared_ptr *)object).reset(); + free(((char *)object) - sizeof(ITcpServerHeader)); + } + } + } + mClients.clear(); +} +void TcpServerImpl::Closed(void) +{ + if (nullptr != mLoop) { + LogInfo("Stop loop.\n"); + hloop_stop(mLoop); + } +} +std::shared_ptr *NewTcpServer(const TcpServerParam ¶m) +{ + LogInfo("Create tcp server object.\n"); + TcpServer *impl = (TcpServer *)malloc(sizeof(TcpServer)); + if (nullptr == impl) { + LogError("NewTcpServer::malloc failed.\n"); + return nullptr; + } + TcpServer tmp; + memcpy((void *)impl, (void *)&tmp, sizeof(TcpServer)); + impl->mHeader.mCheckName = GetTcpServerModuleName(); + impl->mTcpServer = std::make_shared(param); + return (std::shared_ptr *)(((char *)impl) + sizeof(ITcpServerHeader)); +} +std::shared_ptr *NewTcpClientAccept(const hio_t *io, const ClientAcceptParam ¶m) +{ + LogInfo("Create tcp server object.\n"); + TcpClientAccept *impl = (TcpClientAccept *)malloc(sizeof(TcpClientAccept)); + if (nullptr == impl) { + LogError("NewTcpServer::malloc failed.\n"); + return nullptr; + } + TcpClientAccept tmp; + memcpy((void *)impl, (void *)&tmp, sizeof(TcpClientAccept)); + impl->mHeader.mCheckName = GetTcpClientAcceptName(); + std::shared_ptr *objectThis = + (std::shared_ptr *)(((char *)impl) + sizeof(ITcpServerHeader)); + impl->mTcpClientAccept = std::make_shared(io, objectThis, param); + return objectThis; +} \ No newline at end of file diff --git a/utils/TcpModule/src/TcpServerImpl.h b/utils/TcpModule/src/TcpServerImpl.h new file mode 100644 index 0000000..007f63f --- /dev/null +++ b/utils/TcpModule/src/TcpServerImpl.h @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2023 Fancy Code. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef TCP_SERVER_IMPL_H +#define TCP_SERVER_IMPL_H +#include "ITcpServer.h" +#include "TcpModule.h" +#include "hloop.h" +#include "hsocket.h" +#include "hssl.h" +#include +#include +#include +class TcpClientAcceptImpl : public ITcpClientAccept, public std::enable_shared_from_this +{ +public: + TcpClientAcceptImpl(const hio_t *io, const void *object, const ClientAcceptParam ¶m); + virtual ~TcpClientAcceptImpl() = default; + void Close(void) override; + void Readed(const void *data, size_t length) override; + ssize_t Write(const void *data, size_t length) override; + void Closed(void) override; + +private: + const hio_t *mIo; + const void *mObjectThis; + const ClientAcceptParam mParam; +}; +class TcpServerImpl : public ITcpServer, public std::enable_shared_from_this +{ +public: + TcpServerImpl(const TcpServerParam param); + virtual ~TcpServerImpl() = default; + void Init(void) override; + void UnInit(void) override; + void Loop(void); + void AddClient(hio_t *io); + std::shared_ptr *GetClient(hio_t *io); + void RemoveClient(hio_t *io); + void FreeClients(void); + void Closed(void); + +private: + std::mutex mMutex; + hloop_t *mLoop; + hio_t *mIo; + const TcpServerParam mParam; + std::thread mTcpServerThread; + std::map *> mClients; +}; +std::shared_ptr *NewTcpServer(const TcpServerParam ¶m); +std::shared_ptr *NewTcpClientAccept(const hio_t *io, const ClientAcceptParam ¶m); +#endif \ No newline at end of file diff --git a/utils/UartDevice/CMakeLists.txt b/utils/UartDevice/CMakeLists.txt new file mode 100644 index 0000000..eafbfeb --- /dev/null +++ b/utils/UartDevice/CMakeLists.txt @@ -0,0 +1,54 @@ +include(${CMAKE_SOURCE_DIR_IPCSDK}/build/global_config.cmake) +set(EXECUTABLE_OUTPUT_PATH ${EXEC_OUTPUT_PATH}) +set(LIBRARY_OUTPUT_PATH ${LIBS_OUTPUT_PATH}) + +include_directories( + ./src + ./include + ${UTILS_SOURCE_PATH}/LinuxApi/include + ${UTILS_SOURCE_PATH}/StatusCode/include + ${UTILS_SOURCE_PATH}/Log/include +) +# link_directories( +# ${EXTERNAL_SOURCE_PATH}/libconfig/libconfig-1.7.3/lib/.libs +# ) + +aux_source_directory(./src SRC_FILES) + +set(TARGET_NAME UartDevice) +add_library(${TARGET_NAME} STATIC ${SRC_FILES}) +target_link_libraries(${TARGET_NAME} LinuxApi StatusCode Log) + +if ("${COMPILE_IMPROVE_SUPPORT}" MATCHES "true") +add_custom_target( + UartDevice_code_check + COMMAND ${CLANG_TIDY_EXE} + -checks='${CLANG_TIDY_CHECKS}' + --header-filter=.* + --system-headers=false + ${SRC_FILES} + ${CLANG_TIDY_CONFIG} + -p ${PLATFORM_PATH}/cmake-shell + WORKING_DIRECTORY ${UTILS_SOURCE_PATH}/UartDevice +) +file(GLOB_RECURSE HEADER_FILES *.h) +add_custom_target( + UartDevice_code_format + COMMAND ${CLANG_FORMAT_EXE} + -style=file + -i ${SRC_FILES} ${HEADER_FILES} + WORKING_DIRECTORY ${UTILS_SOURCE_PATH}/UartDevice +) +add_custom_command( + TARGET ${TARGET_NAME} + PRE_BUILD + COMMAND make UartDevice_code_check + COMMAND make UartDevice_code_format + WORKING_DIRECTORY ${PLATFORM_PATH}/cmake-shell/ +) +endif() + +define_file_name(${TARGET_NAME}) + +file(GLOB_RECURSE INSTALL_HEADER_FILES include/*.h) +install(FILES ${INSTALL_HEADER_FILES} DESTINATION include) \ No newline at end of file diff --git a/utils/UartDevice/include/UartDevice.h b/utils/UartDevice/include/UartDevice.h new file mode 100644 index 0000000..15e7817 --- /dev/null +++ b/utils/UartDevice/include/UartDevice.h @@ -0,0 +1,80 @@ +/* + * Copyright (c) 2023 Fancy Code. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef UARTD_EVICE_H +#define UARTD_EVICE_H +#include "StatusCode.h" +#include +#include +#ifdef __cplusplus +extern "C" { +#endif +typedef struct uart_info +{ + const char *mDevice; + const int mSpeed; + const int mFlowCtrl; + const int mDataBits; + const int mStopBits; + const int mParity; +} UartInfo; +/** + * @brief Create a serial port object instance. + * + * @param info Serial port information + * @return void* + */ +void *CreateUartDevice(const UartInfo info); +/** + * @brief Open the serial port. + * + * @param object Serial port object pointer. + * @return const StatusCode + */ +const StatusCode IUartOpen(void *object); +/** + * @brief Send data using a serial port. + * + * @param object Serial port object pointer. + * @param buff The data to be sent. + * @param buffLength The length of data to be sent. + * @return const ssize_t + */ +const ssize_t IUartSend(void *object, const void *buff, const size_t buffLength); +/** + * @brief Use a serial port to receive data. + * + * @param object Serial port object pointer. + * @param buff The cache area for receiving data. + * @param buffLength The size of the cache area for receiving data. + * @param timeoutMs The timeout time in milliseconds. + * @return const ssize_t + */ +const ssize_t IUartRecv(void *object, void *buff, const size_t buffLength, const unsigned int timeoutMs); +/** + * @brief Clear the serial port cache data. + * + * @param object Serial port object pointer. + */ +void IUartTcflush(void *object); +/** + * @brief Destroy a serial object instance. + * + * @param object Serial port object pointer. + */ +void IUartDeviceFree(void *object); +#ifdef __cplusplus +} +#endif +#endif \ No newline at end of file diff --git a/utils/UartDevice/src/UartDevice.cpp b/utils/UartDevice/src/UartDevice.cpp new file mode 100644 index 0000000..dae1279 --- /dev/null +++ b/utils/UartDevice/src/UartDevice.cpp @@ -0,0 +1,82 @@ +/* + * Copyright (c) 2023 Fancy Code. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "UartDevice.h" +#include "ILog.h" +#include "StatusCode.h" +#include "UartDeviceImpl.h" +void *CreateUartDevice(const UartInfo info) +{ + return NewUartDeviceImpl(info); +} +const StatusCode IUartOpen(void *object) +{ + if (nullptr == object) { + LogError("nullptr object!\n"); + return CreateStatusCode(STATUS_CODE_NOT_OK); + } + if (*((const char **)(((char *)object) - sizeof(UartDeviceHeader))) != GetUartDeviceModuleName()) { + LogError("Illegal object!\n"); + return CreateStatusCode(STATUS_CODE_NOT_OK); + } + return ((UartDevice *)object)->mOpen((UartDevice *)object); +} +const ssize_t IUartSend(void *object, const void *buff, const size_t buffLength) +{ + if (nullptr == object) { + LogError("nullptr object!\n"); + return 0; + } + if (*((const char **)(((char *)object) - sizeof(UartDeviceHeader))) != GetUartDeviceModuleName()) { + LogError("Illegal object!\n"); + return 0; + } + return ((UartDevice *)object)->mSend((UartDevice *)object, buff, buffLength); +} +const ssize_t IUartRecv(void *object, void *buff, const size_t buffLength, const unsigned int timeoutMs) +{ + if (nullptr == object) { + LogError("nullptr object!\n"); + return 0; + } + if (*((const char **)(((char *)object) - sizeof(UartDeviceHeader))) != GetUartDeviceModuleName()) { + LogError("Illegal object!\n"); + return 0; + } + return ((UartDevice *)object)->mRecv((UartDevice *)object, buff, buffLength, timeoutMs); +} +void IUartTcflush(void *object) +{ + if (nullptr == object) { + LogError("nullptr object!\n"); + return; + } + if (*((const char **)(((char *)object) - sizeof(UartDeviceHeader))) != GetUartDeviceModuleName()) { + LogError("Illegal object!\n"); + return; + } + ((UartDevice *)object)->mTcflush((UartDevice *)object); +} +void IUartDeviceFree(void *object) +{ + if (nullptr == object) { + LogError("nullptr object!\n"); + return; + } + if (*((const char **)(((char *)object) - sizeof(UartDeviceHeader))) != GetUartDeviceModuleName()) { + LogError("Illegal object!\n"); + return; + } + ((UartDevice *)object)->mFree((UartDevice *)object); +} \ No newline at end of file diff --git a/utils/UartDevice/src/UartDeviceImpl.cpp b/utils/UartDevice/src/UartDeviceImpl.cpp new file mode 100644 index 0000000..419ccbd --- /dev/null +++ b/utils/UartDevice/src/UartDeviceImpl.cpp @@ -0,0 +1,290 @@ +/* + * Copyright (c) 2023 Fancy Code. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "UartDeviceImpl.h" +#include "ILog.h" +#include "LinuxApi.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +constexpr int INVALID_FD = -1; +static const char *UART_DEVICE_NAME = "uart_device"; +const char *GetUartDeviceModuleName(void) +{ + return UART_DEVICE_NAME; +} +UartDeviceImpl::UartDeviceImpl(const UartInfo &uatrInfo) : mUatrInfo(uatrInfo) +{ + mFd = -1; +} +const StatusCode UartDeviceImpl::UartOpen(void) +{ + mFd = fx_open(mUatrInfo.mDevice, O_RDWR | O_NOCTTY | O_NONBLOCK); + if (INVALID_FD == mFd) { + perror("Can't Open Serial Port"); + return CreateStatusCode(STATUS_CODE_NOT_OK); + } + LogInfo("open uart device = %s, fd = %d\n", mUatrInfo.mDevice, mFd); + return SetConfig(); +} + +const ssize_t UartDeviceImpl::UartSend(const void *buff, const size_t &buffLength) +{ + constexpr ssize_t SEND_FAILED = -1; + ssize_t writeLength = -1; + if (nullptr == buff) { + LogError("data buff is nullptr.\n"); + return SEND_FAILED; + } + if (mFd <= 0) { + LogError("open uart failed.\n"); + return SEND_FAILED; + } + size_t writeTotal = 0; + while (writeTotal < buffLength) { + writeLength = fx_write(mFd, buff, buffLength - writeTotal); + if (SEND_FAILED == writeLength) { + tcflush(mFd, TCOFLUSH); + return SEND_FAILED; + } + writeTotal += writeLength; + } + if (writeTotal == buffLength) { + return writeTotal; + } + tcflush(mFd, TCOFLUSH); + return writeTotal; +} +const ssize_t UartDeviceImpl::UartRecv(void *buff, const size_t &buffLength, const unsigned int &timeOutMs, + const unsigned int delayReadMs) +{ + constexpr size_t RECV_FAILED = -1; + ssize_t readLength = 0; + int selectResult = -1; + fd_set fdsRead; + struct timeval time; + + if (nullptr == buff) { + LogError("data buff is nullptr.\n"); + return RECV_FAILED; + } + if (mFd <= 0) { + LogError("open uart failed.\n"); + return RECV_FAILED; + } + + FD_ZERO(&fdsRead); + FD_SET(mFd, &fdsRead); + time.tv_sec = timeOutMs / 1000; + time.tv_usec = (timeOutMs % 1000) * 1000; + selectResult = fx_select(mFd + 1, &fdsRead, NULL, NULL, &time); + if (selectResult) { + std::this_thread::sleep_for(std::chrono::milliseconds(delayReadMs)); + readLength = fx_read(mFd, buff, buffLength); + return readLength; + } + return RECV_FAILED; +} +void UartDeviceImpl::UartTcflush(void) +{ + if (mFd > 0) { + tcflush(mFd, TCIFLUSH); + } +} +const StatusCode UartDeviceImpl::SetConfig(void) +{ + int i; + int speed_arr[] = {B1152000, + B1000000, + B921600, + B576000, + B500000, + B460800, + B230400, + B115200, + B19200, + B9600, + B4800, + B2400, + B1200, + B300}; + int name_arr[] = { + 1152000, 1000000, 921600, 576000, 500000, 460800, 230400, 115200, 19200, 9600, 4800, 2400, 1200, 300}; + struct termios options; + + if (fx_tcgetattr(mFd, &options) != 0) { + perror("SetupSerial 1"); + return CreateStatusCode(STATUS_CODE_NOT_OK); + } + + // set buater rate + for (i = 0; i < (int)(sizeof(speed_arr) / sizeof(int)); i++) { + if (mUatrInfo.mSpeed == name_arr[i]) { + cfsetispeed(&options, speed_arr[i]); + cfsetospeed(&options, speed_arr[i]); + } + } + + // set control model + options.c_cflag |= CLOCAL; + options.c_cflag |= CREAD; + + // set flow control + switch (mUatrInfo.mFlowCtrl) { + case 0: // none + options.c_cflag &= ~CRTSCTS; + break; + case 1: // use hard ware + options.c_cflag |= CRTSCTS; + break; + case 2: // use sofware + options.c_cflag |= IXON | IXOFF | IXANY; + break; + } + + // select data bit + options.c_cflag &= ~CSIZE; + switch (mUatrInfo.mDataBits) { + case 5: + options.c_cflag |= CS5; + break; + case 6: + options.c_cflag |= CS6; + break; + case 7: + options.c_cflag |= CS7; + break; + case 8: + options.c_cflag |= CS8; + break; + default: + LogError("Unsupported data size\n"); + return CreateStatusCode(STATUS_CODE_NOT_OK); + } + + // select parity bit + switch (mUatrInfo.mParity) { + case 'n': + case 'N': + options.c_cflag &= ~PARENB; + options.c_iflag &= ~INPCK; + break; + case 'o': + case 'O': + options.c_cflag |= (PARODD | PARENB); + options.c_iflag |= INPCK; + break; + case 'e': + case 'E': + options.c_cflag |= PARENB; + options.c_cflag &= ~PARODD; + options.c_iflag |= INPCK; + break; + case 's': + case 'S': + options.c_cflag &= ~PARENB; + options.c_cflag &= ~CSTOPB; + break; + default: + fprintf(stderr, "Unsupported parity\n"); + return CreateStatusCode(STATUS_CODE_NOT_OK); + } + // set stopbit + switch (mUatrInfo.mStopBits) { + case 1: + options.c_cflag &= ~CSTOPB; + break; + case 2: + options.c_cflag |= CSTOPB; + break; + default: + fprintf(stderr, "Unsupported stop bits\n"); + return CreateStatusCode(STATUS_CODE_NOT_OK); + } + + // set raw data output + options.c_oflag &= ~OPOST; + options.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG); + // options.c_lflag &= ~(ISIG | ICANON); + options.c_iflag &= ~ICRNL; + // set wait time + options.c_cc[VTIME] = 1; + options.c_cc[VMIN] = 1; + tcflush(mFd, TCIFLUSH); + // set the attribute to HiSerial device + if (fx_tcsetattr(mFd, TCSANOW, &options) != 0) { + perror("com set error!\n"); + return CreateStatusCode(STATUS_CODE_NOT_OK); + } + return CreateStatusCode(STATUS_CODE_OK); +} +const StatusCode (*mOpen)(UartDevice *); +static const StatusCode UartOpen(UartDevice *object) +{ + UartDeviceImpl_S *impl = UartDeviceImplConvert(object); + return impl->mUartImpl->UartOpen(); +} +static const ssize_t UartSend(UartDevice *object, const void *buff, const size_t buffLength) +{ + UartDeviceImpl_S *impl = UartDeviceImplConvert(object); + return impl->mUartImpl->UartSend(buff, buffLength); +} +static const ssize_t UartRecv(UartDevice *object, void *buff, const size_t buffLength, const unsigned int timeoutMs) +{ + UartDeviceImpl_S *impl = UartDeviceImplConvert(object); + constexpr int READ_NOT_DELAY = 0; + return impl->mUartImpl->UartRecv(buff, buffLength, timeoutMs, READ_NOT_DELAY); +} +static void UartTcflush(UartDevice *object) +{ + UartDeviceImpl_S *impl = UartDeviceImplConvert(object); + return impl->mUartImpl->UartTcflush(); +} +static void UartImpllFree(void *ptr) +{ + UartDeviceImpl_S *object = ((UartDeviceImpl_S *)(((char *)ptr) - sizeof(UartDeviceHeader))); + if (UART_DEVICE_NAME == object->mHeader.mCheckName) { + object->mUartImpl = nullptr; + free(((char *)ptr) - sizeof(UartDeviceHeader)); + } + else { + LogError("Unknow ptr.\n"); + } +} +// static const char *test = "zhanqu"; +UartDevice *NewUartDeviceImpl(const UartInfo &uartInfo) +{ + if (nullptr == uartInfo.mDevice) { + LogError("Parament error, nullptr == uartInfo.mDevice\n"); + return nullptr; + } + LogInfo("Create the uart device object.\n"); + UartDeviceImpl_S *impl = (UartDeviceImpl_S *)malloc(sizeof(UartDeviceImpl_S)); + UartDeviceImpl_S tmp; + memcpy((void *)impl, (void *)&tmp, sizeof(UartDeviceImpl_S)); + impl->mHeader.mCheckName = UART_DEVICE_NAME; + impl->mBase.mOpen = UartOpen; + impl->mBase.mSend = UartSend; + impl->mBase.mRecv = UartRecv; + impl->mBase.mTcflush = UartTcflush; + impl->mBase.mFree = UartImpllFree; + impl->mUartImpl = std::make_shared(uartInfo); + return (UartDevice *)(((char *)impl) + sizeof(UartDeviceHeader)); +} \ No newline at end of file diff --git a/utils/UartDevice/src/UartDeviceImpl.h b/utils/UartDevice/src/UartDeviceImpl.h new file mode 100644 index 0000000..8859bfe --- /dev/null +++ b/utils/UartDevice/src/UartDeviceImpl.h @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2023 Fancy Code. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef UARTDE_VICEIMPL_H +#define UARTDE_VICEIMPL_H +#include "StatusCode.h" +#include "UartDevice.h" +#include +#include +#define UART_DEVICE_INIT_NAME "UART" +typedef struct uart_device_header +{ + const char *mCheckName; +} UartDeviceHeader; +class UartDeviceImpl +{ +public: + UartDeviceImpl(const UartInfo &uatrInfo); + virtual ~UartDeviceImpl() = default; + const StatusCode UartOpen(void); + const ssize_t UartSend(const void *buff, const size_t &buffLength); + const ssize_t UartRecv(void *buff, const size_t &buffLength, const unsigned int &timeOutMs = 0, + const unsigned int delayReadMs = 0); + void UartTcflush(void); + +private: + const StatusCode SetConfig(void); + +private: + const UartInfo mUatrInfo; + int mFd; +}; +typedef struct uart_device UartDevice; +typedef struct uart_device +{ + const StatusCode (*mOpen)(UartDevice *); + const ssize_t (*mSend)(UartDevice *, const void *, const size_t); + const ssize_t (*mRecv)(UartDevice *, void *, const size_t, const unsigned int); + void (*mTcflush)(UartDevice *); + void (*mFree)(void *); +} UartDevice; +// TODO: There may be a CPU byte alignment bug. +typedef struct uart_device_impl UartDeviceImpl_S; +typedef struct uart_device_impl +{ + UartDeviceHeader mHeader; + UartDevice mBase; + std::shared_ptr mUartImpl; +} UartDeviceImpl_S; +UartDevice *NewUartDeviceImpl(const UartInfo &uartInfo); +static inline UartDeviceImpl_S *UartDeviceImplConvert(UartDevice *object) +{ + return ((UartDeviceImpl_S *)(((char *)object) - sizeof(UartDeviceHeader))); +} +const char *GetUartDeviceModuleName(void); +#endif \ No newline at end of file diff --git a/utils/UpgradeBase/CMakeLists.txt b/utils/UpgradeBase/CMakeLists.txt new file mode 100644 index 0000000..e34986c --- /dev/null +++ b/utils/UpgradeBase/CMakeLists.txt @@ -0,0 +1,54 @@ +include(${CMAKE_SOURCE_DIR_IPCSDK}/build/global_config.cmake) +set(EXECUTABLE_OUTPUT_PATH ${EXEC_OUTPUT_PATH}) +set(LIBRARY_OUTPUT_PATH ${LIBS_OUTPUT_PATH}) + +include_directories( + ./src + ./include + ${UTILS_SOURCE_PATH}/LinuxApi/include + ${UTILS_SOURCE_PATH}/StatusCode/include + ${UTILS_SOURCE_PATH}/Log/include +) +# link_directories( +# ${EXTERNAL_SOURCE_PATH}/libconfig/libconfig-1.7.3/lib/.libs +# ) + +aux_source_directory(./src SRC_FILES) + +set(TARGET_NAME UpgradeBase) +add_library(${TARGET_NAME} STATIC ${SRC_FILES}) +target_link_libraries(${TARGET_NAME} StatusCode Log) + +if ("${COMPILE_IMPROVE_SUPPORT}" MATCHES "true") +add_custom_target( + UpgradeBase_code_check + COMMAND ${CLANG_TIDY_EXE} + -checks='${CLANG_TIDY_CHECKS}' + --header-filter=.* + --system-headers=false + ${SRC_FILES} + ${CLANG_TIDY_CONFIG} + -p ${PLATFORM_PATH}/cmake-shell + WORKING_DIRECTORY ${UTILS_SOURCE_PATH}/UpgradeBase +) +file(GLOB_RECURSE HEADER_FILES *.h) +add_custom_target( + UpgradeBase_code_format + COMMAND ${CLANG_FORMAT_EXE} + -style=file + -i ${SRC_FILES} ${HEADER_FILES} + WORKING_DIRECTORY ${UTILS_SOURCE_PATH}/UpgradeBase +) +add_custom_command( + TARGET ${TARGET_NAME} + PRE_BUILD + COMMAND make UpgradeBase_code_check + COMMAND make UpgradeBase_code_format + WORKING_DIRECTORY ${PLATFORM_PATH}/cmake-shell/ +) +endif() + +define_file_name(${TARGET_NAME}) + +file(GLOB_RECURSE INSTALL_HEADER_FILES include/*.h) +install(FILES ${INSTALL_HEADER_FILES} DESTINATION include) \ No newline at end of file diff --git a/utils/UpgradeBase/README.md b/utils/UpgradeBase/README.md new file mode 100644 index 0000000..ed1806f --- /dev/null +++ b/utils/UpgradeBase/README.md @@ -0,0 +1,23 @@ +# 1. 升级功能基类 + +## 1.1. 概述 + +  升级功能基类,用于升级功能模块,提供升级功能模块的通用方法。 + +## 1.2. 文件头定义 + +**32字节头定义** + +| 打包时间(6) | 版本号(4) | 型号代码(2) | 项目代码(2) | 升级类型(1) | 预留(15) | 校验码(尾2) | +|----|----|----|----|----|----|----| +|----|----|----|----|----|----|----| + +**升级类型说明** + +0x00:仅仅应用程序; +0x01:烧录flash; + +## 1.3. 仅升级应用程序 + +1. 插卡后检查sd卡是否存在升级文件; +2. 校验升级文件并拷贝到升级目录; \ No newline at end of file diff --git a/utils/UpgradeBase/include/UpgradeBase.h b/utils/UpgradeBase/include/UpgradeBase.h new file mode 100644 index 0000000..31296fa --- /dev/null +++ b/utils/UpgradeBase/include/UpgradeBase.h @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2023 Fancy Code. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef UPGRADE_BASE_H +#define UPGRADE_BASE_H +#include "StatusCode.h" +constexpr int VERSION_LENGTH = 4; +typedef struct __attribute__((packed)) upgrade_file_header +{ + unsigned char packTime[6]; + unsigned char version[VERSION_LENGTH]; + unsigned char product[2]; + unsigned char project[2]; + unsigned char upgradeType[1]; + unsigned char reserved[15]; + unsigned char checkCode[2]; +} UpgradeFileHeader; +enum class UpgradeType +{ + APPLICATION_ONLY = 0, + END +}; +class UpgradeBase +{ +public: + UpgradeBase() = default; + virtual ~UpgradeBase() = default; + virtual StatusCode CheckFileHeader(const UpgradeFileHeader &header) = 0; + /** + * @brief Verify if the file belongs to the upgrade file. + * + * @param fileName The file name being verified. Enter the parameter using an absolute path. + * @return StatusCode + */ + StatusCode CheckUpgradeFile(const char *fileName, UpgradeFileHeader &header); + StatusCode MoveUpgradeFile(const char *sourceFile, const char *targetFile); + +private: + void PrintfHeader(const UpgradeFileHeader &header); +}; +#endif \ No newline at end of file diff --git a/utils/UpgradeBase/src/UpgradeBase.cpp b/utils/UpgradeBase/src/UpgradeBase.cpp new file mode 100644 index 0000000..d79b228 --- /dev/null +++ b/utils/UpgradeBase/src/UpgradeBase.cpp @@ -0,0 +1,112 @@ +/* + * Copyright (c) 2023 Fancy Code. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "UpgradeBase.h" +#include "ILog.h" +#include "LinuxApi.h" +#include +#include +#include +StatusCode UpgradeBase::CheckUpgradeFile(const char *fileName, UpgradeFileHeader &header) +{ + FILE *file = nullptr; + LogInfo("Checking file:%s\n", fileName); + file = fopen(fileName, "rb"); + if (file == nullptr) { + LogError("Error opening file:%s\n", fileName); + return CreateStatusCode(STATUS_CODE_NOT_OK); + } + + size_t bytesRead = fread(&header, sizeof(UpgradeFileHeader), 1, file); + if (bytesRead != 1) { + LogError("Error reading file header\n"); + fclose(file); + return CreateStatusCode(STATUS_CODE_NOT_OK); + } + PrintfHeader(header); + return CheckFileHeader(header); +} +StatusCode UpgradeBase::MoveUpgradeFile(const char *sourceFile, const char *targetFile) +{ + FILE *inputFile = nullptr; + FILE *outputFile = nullptr; + constexpr int BUFF_SIZE = 1024 * 200; + UpgradeFileHeader fileHeader; + const size_t headerSize = sizeof(UpgradeFileHeader); + unsigned char buffer[BUFF_SIZE]; + size_t bytesRead; + + inputFile = fopen(sourceFile, "rb"); + if (inputFile == nullptr) { + LogError("Error opening input file\n"); + return CreateStatusCode(STATUS_CODE_NOT_OK); + } + + outputFile = fopen(targetFile, "wb"); + if (outputFile == nullptr) { + LogError("Error opening output file\n"); + fclose(inputFile); + return CreateStatusCode(STATUS_CODE_NOT_OK); + } + + bytesRead = fread(&fileHeader, 1, headerSize, inputFile); + if (bytesRead != headerSize) { + LogError("Error reading file header\n"); + fclose(inputFile); + fclose(outputFile); + return CreateStatusCode(STATUS_CODE_NOT_OK); + } + + while ((bytesRead = fread(buffer, 1, sizeof(buffer), inputFile)) > 0) { + fwrite(buffer, 1, bytesRead, outputFile); + if (ferror(inputFile) || ferror(outputFile)) { + LogError("Error reading or writing file\n"); + fclose(inputFile); + fclose(outputFile); + return CreateStatusCode(STATUS_CODE_NOT_OK); + } + } + + fclose(inputFile); + fclose(outputFile); + + LogInfo("File processed successfully.\n"); + + return CreateStatusCode(STATUS_CODE_OK); +} +void UpgradeBase::PrintfHeader(const UpgradeFileHeader &header) +{ + printf("=====================================\n"); + printf("packTime:"); + for (long unsigned int i = 0; i < sizeof(header.packTime); ++i) { + printf("0x%02x ", header.packTime[i]); + } + printf("\n"); + printf("version:"); + for (long unsigned int i = 0; i < sizeof(header.version); ++i) { + printf("0x%02x ", header.version[i]); + } + printf("\n"); + printf("product:"); + for (long unsigned int i = 0; i < sizeof(header.product); ++i) { + printf("0x%02x ", header.product[i]); + } + printf("\n"); + printf("upgradeType:"); + for (long unsigned int i = 0; i < sizeof(header.upgradeType); ++i) { + printf("0x%02x ", header.upgradeType[i]); + } + printf("\n"); + printf("=====================================\n"); +} \ No newline at end of file diff --git a/utils/UpgradeTool/CMakeLists.txt b/utils/UpgradeTool/CMakeLists.txt new file mode 100644 index 0000000..9665d8b --- /dev/null +++ b/utils/UpgradeTool/CMakeLists.txt @@ -0,0 +1,55 @@ +include(${CMAKE_SOURCE_DIR_IPCSDK}/build/global_config.cmake) +set(EXECUTABLE_OUTPUT_PATH ${EXEC_OUTPUT_PATH}) +set(LIBRARY_OUTPUT_PATH ${LIBS_OUTPUT_PATH}) + +include_directories( + ./src + ./include + ${UTILS_SOURCE_PATH}/StatusCode/include + ${UTILS_SOURCE_PATH}/Log/include + ${UTILS_SOURCE_PATH}/UpgradeBase/include + ${UTILS_SOURCE_PATH}/LinuxApi/include +) +# link_directories( +# ${EXTERNAL_SOURCE_PATH}/libconfig/libconfig-1.7.3/lib/.libs +# ) + +aux_source_directory(./src SRC_FILES) + +set(TARGET_NAME UpgradeTool) +add_library(${TARGET_NAME} STATIC ${SRC_FILES}) +target_link_libraries(${TARGET_NAME} LinuxApi StatusCode Log) + +if ("${COMPILE_IMPROVE_SUPPORT}" MATCHES "true") +add_custom_target( + UpgradeTool_code_check + COMMAND ${CLANG_TIDY_EXE} + -checks='${CLANG_TIDY_CHECKS}' + --header-filter=.* + --system-headers=false + ${SRC_FILES} + ${CLANG_TIDY_CONFIG} + -p ${PLATFORM_PATH}/cmake-shell + WORKING_DIRECTORY ${UTILS_SOURCE_PATH}/UpgradeTool +) +file(GLOB_RECURSE HEADER_FILES *.h) +add_custom_target( + UpgradeTool_code_format + COMMAND ${CLANG_FORMAT_EXE} + -style=file + -i ${SRC_FILES} ${HEADER_FILES} + WORKING_DIRECTORY ${UTILS_SOURCE_PATH}/UpgradeTool +) +add_custom_command( + TARGET ${TARGET_NAME} + PRE_BUILD + COMMAND make UpgradeTool_code_check + COMMAND make UpgradeTool_code_format + WORKING_DIRECTORY ${PLATFORM_PATH}/cmake-shell/ +) +endif() + +define_file_name(${TARGET_NAME}) + +file(GLOB_RECURSE INSTALL_HEADER_FILES include/*.h) +install(FILES ${INSTALL_HEADER_FILES} DESTINATION include) \ No newline at end of file diff --git a/utils/UpgradeTool/README.md b/utils/UpgradeTool/README.md new file mode 100644 index 0000000..20c4101 --- /dev/null +++ b/utils/UpgradeTool/README.md @@ -0,0 +1,5 @@ +# 升级打包工具库 + +## 概述 + +  升级打包工具库,用于把升级文件添加校验字节头进行打包。 \ No newline at end of file diff --git a/utils/UpgradeTool/include/UpgradeTool.h b/utils/UpgradeTool/include/UpgradeTool.h new file mode 100644 index 0000000..5f827dd --- /dev/null +++ b/utils/UpgradeTool/include/UpgradeTool.h @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2023 Fancy Code. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef UPGRADE_TOOL_H +#define UPGRADE_TOOL_H +#include "StatusCode.h" +#include +class UpgradeTool +{ +public: + UpgradeTool() = default; + virtual ~UpgradeTool() = default; + static std::shared_ptr &GetInstance(std::shared_ptr *impl = nullptr); + void PackFile(const std::string &fileName, const std::string &outputFile, const std::string &version, + const std::string &product, const std::string &project, const std::string &upgradeType); + +private: + void FillInTime(unsigned char packTime[6]); + bool StringToVersionBytes(const std::string &versionString, unsigned char result[4]); +}; +#endif \ No newline at end of file diff --git a/utils/UpgradeTool/src/UpgradeTool.cpp b/utils/UpgradeTool/src/UpgradeTool.cpp new file mode 100644 index 0000000..d84e188 --- /dev/null +++ b/utils/UpgradeTool/src/UpgradeTool.cpp @@ -0,0 +1,134 @@ +/* + * Copyright (c) 2023 Fancy Code. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "UpgradeTool.h" +#include "ILog.h" +#include "LinuxApi.h" +#include "UpgradeBase.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +std::shared_ptr &UpgradeTool::GetInstance(std::shared_ptr *impl) +{ + static auto instance = std::make_shared(); + if (impl) { + if (instance.use_count() == 1) { + LogInfo("Instance changed succeed.\n"); + instance = *impl; + } + else { + LogError("Can't changing the instance becase of using by some one.\n"); + } + } + return instance; +} +bool UpgradeTool::StringToVersionBytes(const std::string &versionString, unsigned char result[4]) +{ + std::stringstream ss(versionString); + std::string segment; + int index = 0; + + while (std::getline(ss, segment, '.') && index < 4) { + unsigned int value = 0; + std::stringstream segmentStream(segment); + segmentStream >> std::dec >> value; + + if (value > 255) { + return false; + } + + result[index++] = static_cast(value); + printf("version[%d]: %02x\n", index, result[index - 1]); + } + + if (index != 4) { + return false; + } + + return true; +} +void UpgradeTool::FillInTime(unsigned char packTime[6]) +{ + time_t rawtime; + struct tm *timeinfo; + + time(&rawtime); + timeinfo = localtime(&rawtime); + packTime[0] = timeinfo->tm_year; + packTime[1] = timeinfo->tm_mon + 1; // 月份(1-12) + packTime[2] = timeinfo->tm_mday; // 日期(1-31) + packTime[3] = timeinfo->tm_hour; // 小时(0-23) + packTime[4] = timeinfo->tm_min; // 分钟(0-59) + packTime[5] = timeinfo->tm_sec; // 秒(0-59) +} +void UpgradeTool::PackFile(const std::string &fileName, const std::string &outputFile, const std::string &version, + const std::string &product, const std::string &project, const std::string &upgradeType) +{ + struct stat fileStat; + UpgradeFileHeader header; + memset(&header, 0, sizeof(UpgradeFileHeader)); + StringToVersionBytes(version, header.version); + FillInTime(header.packTime); + FILE *input_file = fopen(fileName.c_str(), "rb"); + if (!input_file) { + perror("Error opening input file"); + return; + } + if (stat(fileName.c_str(), &fileStat) == -1) { + LogError("Error getting file size\n"); + fclose(input_file); + return; + } + LogInfo("File size:%ld\n", fileStat.st_size); + + FILE *output_file = fopen(outputFile.c_str(), "wb"); + if (!output_file) { + perror("Error opening output file"); + fclose(input_file); + return; + } + long writeSize = 0; + fwrite(&header, sizeof(header), 1, output_file); + char buffer[1024]; + size_t bytes_read = 0; + size_t bytes_write = 0; + while ((bytes_read = fread(buffer, 1, sizeof(buffer), input_file)) > 0) { + bytes_write = fwrite(buffer, 1, bytes_read, output_file); + if (bytes_write != bytes_read) { + LogError("fwrite failed, break;\n"); + break; + } + writeSize += bytes_write; + } + LogInfo("write size:%ld\n", writeSize); + if (writeSize != fileStat.st_size) { + LogError("write file failed, remove output file.\n"); + char cmd[1024]; + snprintf(cmd, sizeof(cmd), "rm -f %s", outputFile.c_str()); + fx_system(cmd); + } + else { + LogInfo("write file success.\n"); + } + fclose(input_file); + fclose(output_file); + fx_system("sync"); +} \ No newline at end of file diff --git a/utils/WebServer/CMakeLists.txt b/utils/WebServer/CMakeLists.txt new file mode 100644 index 0000000..4b0c815 --- /dev/null +++ b/utils/WebServer/CMakeLists.txt @@ -0,0 +1,133 @@ +include(${CMAKE_SOURCE_DIR_IPCSDK}/build/global_config.cmake) +include(${EXTERNAL_SOURCE_PATH}/goahead-5.2.0/goahead.cmake) +include(${UTILS_SOURCE_PATH}/WebServer/build/webserver.cmake) +set(EXECUTABLE_OUTPUT_PATH ${EXEC_OUTPUT_PATH}) +set(LIBRARY_OUTPUT_PATH ${LIBS_OUTPUT_PATH}) + +include_directories( + ./src + ./include + ${UTILS_SOURCE_PATH}/StatusCode/include + ${UTILS_SOURCE_PATH}/Log/include + ${EXTERNAL_SOURCE_PATH}/goahead-5.2.0/GoAhead/build/${GOAHEAD_INCLUDE_PATH}/inc + # ${UTILS_SOURCE_PATH}/LinuxApi/include +) +# link_directories( +# ${EXTERNAL_SOURCE_PATH}/libconfig/libconfig-1.7.3/lib/.libs +# ) + +if (NOT DEFINED GOAHEAD_UPLOAD_TMP_PATH) +message(FATAL_ERROR "YYou should set the GOAHEAD_UPLOAD_TMP_PATH variable so that the GOAHEAD module can temporarily save uploaded files. +For example: set(GOAHEAD_UPLOAD_TMP_PATH \"./goahead\") +See:${IPC_SDK_PATH}/build/cmake/toolchain/linux.toolchain.cmake") +endif() +add_definitions(-DGOAHEAD_UPLOAD_TMP_PATH=\"${GOAHEAD_UPLOAD_TMP_PATH}\") + +if (NOT DEFINED GOAHEAD_UPLOAD_PATH) +message(FATAL_ERROR "YYou should set the GOAHEAD_UPLOAD_PATH variable so that the GOAHEAD module can temporarily save uploaded files. +For example: set(GOAHEAD_UPLOAD_PATH \"./goahead\") +See:${IPC_SDK_PATH}/build/cmake/toolchain/linux.toolchain.cmake") +endif() +add_definitions(-DGOAHEAD_UPLOAD_PATH=\"${GOAHEAD_UPLOAD_PATH}\") + +if (DEFINED GOAHEAD_LIMIT_POST) + set(ME_GOAHEAD_LIMIT_POST "ME_GOAHEAD_LIMIT_POST=${GOAHEAD_LIMIT_POST}") +else() + set(ME_GOAHEAD_LIMIT_POST "") +endif() + +aux_source_directory(./src SRC_FILES) + +set(TARGET_NAME WebServer) +add_library(${TARGET_NAME} STATIC ${SRC_FILES}) +target_link_libraries(${TARGET_NAME} StatusCode Log -Wl,--start-group libgo.a libmbedtls.a libgoahead-mbedtls.a -Wl,--end-group) + +if ("${COMPILE_IMPROVE_SUPPORT}" MATCHES "true") +add_custom_target( + WebServer_code_check + COMMAND ${CLANG_TIDY_EXE} + -checks='${CLANG_TIDY_CHECKS}' + --header-filter=.* + --system-headers=false + ${SRC_FILES} + ${CLANG_TIDY_CONFIG} + -p ${PLATFORM_PATH}/cmake-shell + WORKING_DIRECTORY ${UTILS_SOURCE_PATH}/WebServer +) +file(GLOB_RECURSE HEADER_FILES *.h) +add_custom_target( + WebServer_code_format + COMMAND ${CLANG_FORMAT_EXE} + -style=file + -i ${SRC_FILES} ${HEADER_FILES} + WORKING_DIRECTORY ${UTILS_SOURCE_PATH}/WebServer +) +add_custom_command( + TARGET ${TARGET_NAME} + PRE_BUILD + COMMAND make WebServer_code_check + COMMAND make WebServer_code_format + WORKING_DIRECTORY ${PLATFORM_PATH}/cmake-shell/ +) +endif() + +# build goahead before make libgo.a +add_custom_command( + OUTPUT ${EXTERNAL_SOURCE_PATH}/goahead-5.2.0/GoAhead/Makefile + COMMAND echo "tar zxvf goahead-5.2.0.tar.gz" + COMMAND tar zxvf goahead-5.2.0.tar.gz + COMMAND cp ${EXTERNAL_SOURCE_PATH}/goahead-5.2.0/modify/http.c ${EXTERNAL_SOURCE_PATH}/goahead-5.2.0/GoAhead/src + WORKING_DIRECTORY ${EXTERNAL_SOURCE_PATH}/goahead-5.2.0/ +) +add_custom_command( + OUTPUT ${EXTERNAL_LIBS_OUTPUT_PATH}/libgo.a + DEPENDS ${EXTERNAL_SOURCE_PATH}/goahead-5.2.0/GoAhead/Makefile + COMMAND make CC=${CMAKE_C_COMPILER} ARCH=${SET_ARCH} PROFILE=${LIB_TYPE} ME_GOAHEAD_UPLOAD_DIR=\"${GOAHEAD_UPLOAD_TMP_PATH}\" + ME_GOAHEAD_SSL_KEY=\"${GOAHEAD_CONFIG_FILE_PATH}/self.key\" ME_GOAHEAD_SSL_CERTIFICATE=\"${GOAHEAD_CONFIG_FILE_PATH}/self.crt\" + ${ME_GOAHEAD_LIMIT_POST} -f ../modify/goahead-linux-static-fancy.mk + # COMMAND make CC=${CMAKE_C_COMPILER} ARCH=${SET_ARCH} PROFILE=${LIB_TYPE} -f ../modify/goahead-linux-static-fancy.mk // TODO: + COMMAND echo "Copy goahead lib to output path." + COMMAND mv ${EXTERNAL_SOURCE_PATH}/goahead-5.2.0/GoAhead/build/${GOAHEAD_INCLUDE_PATH}/bin/libgo.a ${EXTERNAL_LIBS_OUTPUT_PATH}/libgo.a + COMMAND mv ${EXTERNAL_SOURCE_PATH}/goahead-5.2.0/GoAhead/build/${GOAHEAD_INCLUDE_PATH}/bin/libmbedtls.a ${EXTERNAL_LIBS_OUTPUT_PATH}/libmbedtls.a + COMMAND mv ${EXTERNAL_SOURCE_PATH}/goahead-5.2.0/GoAhead/build/${GOAHEAD_INCLUDE_PATH}/bin/libgoahead-mbedtls.a ${EXTERNAL_LIBS_OUTPUT_PATH}/libgoahead-mbedtls.a + COMMAND cp ${EXTERNAL_SOURCE_PATH}/goahead-5.2.0/GoAhead/build/${GOAHEAD_INCLUDE_PATH}/bin/self.crt ${PLATFORM_PATH}/cmake-shell/ + COMMAND cp ${EXTERNAL_SOURCE_PATH}/goahead-5.2.0/GoAhead/build/${GOAHEAD_INCLUDE_PATH}/bin/self.key ${PLATFORM_PATH}/cmake-shell/ + COMMAND cp ${EXTERNAL_SOURCE_PATH}/goahead-5.2.0/GoAhead/test/route.txt ${PLATFORM_PATH}/cmake-shell/ + COMMAND cp ${EXTERNAL_SOURCE_PATH}/goahead-5.2.0/GoAhead/test/auth.txt ${PLATFORM_PATH}/cmake-shell/ + WORKING_DIRECTORY ${EXTERNAL_SOURCE_PATH}/goahead-5.2.0/GoAhead +) +add_custom_target( + libgo.a + DEPENDS ${EXTERNAL_LIBS_OUTPUT_PATH}/libgo.a + # COMMAND mkdir ${GOAHEAD_UPLOAD_TMP_PATH} + COMMAND cp ${EXTERNAL_SOURCE_PATH}/goahead-5.2.0/modify/http.c ${EXTERNAL_SOURCE_PATH}/goahead-5.2.0/GoAhead/src + COMMAND touch ${EXTERNAL_SOURCE_PATH}/goahead-5.2.0/GoAhead/src/http.c + WORKING_DIRECTORY ${PLATFORM_PATH}/cmake-shell/ +) +add_custom_target( + goahead_compile + COMMAND test ! -f ${EXTERNAL_LIBS_OUTPUT_PATH}/libgo.a || rm ${EXTERNAL_LIBS_OUTPUT_PATH}/libgo.a + COMMAND test ! -f ${EXTERNAL_LIBS_OUTPUT_PATH}/libmbedtls.a || rm ${EXTERNAL_LIBS_OUTPUT_PATH}/libmbedtls.a + COMMAND test ! -f ${EXTERNAL_LIBS_OUTPUT_PATH}/libgoahead-mbedtls.a || rm ${EXTERNAL_LIBS_OUTPUT_PATH}/libgoahead-mbedtls.a + COMMAND make CC=${CMAKE_C_COMPILER} ARCH=${SET_ARCH} PROFILE=${LIB_TYPE} ME_GOAHEAD_UPLOAD_DIR=\"${GOAHEAD_UPLOAD_TMP_PATH}\" + ME_GOAHEAD_SSL_KEY=\"${GOAHEAD_CONFIG_FILE_PATH}/self.key\" ME_GOAHEAD_SSL_CERTIFICATE=\"${GOAHEAD_CONFIG_FILE_PATH}/self.crt\" + ${ME_GOAHEAD_LIMIT_POST} -f ../modify/goahead-linux-static-fancy.mk + COMMAND echo "Copy goahead lib to output path." + COMMAND mv ${EXTERNAL_SOURCE_PATH}/goahead-5.2.0/GoAhead/build/${GOAHEAD_INCLUDE_PATH}/bin/libgo.a ${EXTERNAL_LIBS_OUTPUT_PATH}/libgo.a + COMMAND mv ${EXTERNAL_SOURCE_PATH}/goahead-5.2.0/GoAhead/build/${GOAHEAD_INCLUDE_PATH}/bin/libmbedtls.a ${EXTERNAL_LIBS_OUTPUT_PATH}/libmbedtls.a + COMMAND mv ${EXTERNAL_SOURCE_PATH}/goahead-5.2.0/GoAhead/build/${GOAHEAD_INCLUDE_PATH}/bin/libgoahead-mbedtls.a ${EXTERNAL_LIBS_OUTPUT_PATH}/libgoahead-mbedtls.a + COMMAND cp ${EXTERNAL_SOURCE_PATH}/goahead-5.2.0/GoAhead/build/${GOAHEAD_INCLUDE_PATH}/bin/self.crt ${PLATFORM_PATH}/cmake-shell/ + COMMAND cp ${EXTERNAL_SOURCE_PATH}/goahead-5.2.0/GoAhead/build/${GOAHEAD_INCLUDE_PATH}/bin/self.key ${PLATFORM_PATH}/cmake-shell/ + COMMAND cp ${EXTERNAL_SOURCE_PATH}/goahead-5.2.0/GoAhead/test/route.txt ${PLATFORM_PATH}/cmake-shell/ + COMMAND cp ${EXTERNAL_SOURCE_PATH}/goahead-5.2.0/GoAhead/test/auth.txt ${PLATFORM_PATH}/cmake-shell/ + WORKING_DIRECTORY ${EXTERNAL_SOURCE_PATH}/goahead-5.2.0/GoAhead +) + +define_file_name(${TARGET_NAME}) + +file(GLOB_RECURSE INSTALL_HEADER_FILES include/*.h) +install(FILES ${INSTALL_HEADER_FILES} DESTINATION include) +install(FILES ${EXTERNAL_SOURCE_PATH}/goahead-5.2.0/GoAhead/build/${GOAHEAD_INCLUDE_PATH}/bin/self.crt DESTINATION config) +install(FILES ${EXTERNAL_SOURCE_PATH}/goahead-5.2.0/GoAhead/build/${GOAHEAD_INCLUDE_PATH}/bin/self.key DESTINATION config) +install(FILES ${EXTERNAL_SOURCE_PATH}/goahead-5.2.0/GoAhead/test/route.txt DESTINATION config) +install(FILES ${EXTERNAL_SOURCE_PATH}/goahead-5.2.0/GoAhead/test/auth.txt DESTINATION config) \ No newline at end of file diff --git a/utils/WebServer/README.md b/utils/WebServer/README.md new file mode 100644 index 0000000..b032c77 --- /dev/null +++ b/utils/WebServer/README.md @@ -0,0 +1,144 @@ +# 1. Goahead开发文档 + +## 1.1. Goahead版本 + +5.2.0 + +## 1.2. 编译脚本 + +``` +# build goahead before make libgo.a +add_custom_command( + OUTPUT ${EXTERNAL_SOURCE_PATH}/goahead-5.2.0/GoAhead/Makefile + COMMAND echo "tar zxvf goahead-5.2.0.tar.gz" + COMMAND tar zxvf goahead-5.2.0.tar.gz // 解压源码包 + WORKING_DIRECTORY ${EXTERNAL_SOURCE_PATH}/goahead-5.2.0/ +) +add_custom_command( + OUTPUT ${EXTERNAL_LIBS_OUTPUT_PATH}/libgo.a + DEPENDS ${EXTERNAL_SOURCE_PATH}/goahead-5.2.0/GoAhead/Makefile + COMMAND make CC=${CMAKE_C_COMPILER} ARCH=${SET_ARCH} PROFILE=${LIB_TYPE} + COMMAND mv ${EXTERNAL_SOURCE_PATH}/goahead-5.2.0/GoAhead/build/${GOAHEAD_INCLUDE_PATH}/bin/libgo.a ${EXTERNAL_LIBS_OUTPUT_PATH}/libgo.a // 拷贝静态库到output目录 + COMMAND mv ${EXTERNAL_SOURCE_PATH}/goahead-5.2.0/GoAhead/build/${GOAHEAD_INCLUDE_PATH}/bin/libmbedtls.a ${EXTERNAL_LIBS_OUTPUT_PATH}/libmbedtls.a // 拷贝静态库到output目录 + COMMAND mv ${EXTERNAL_SOURCE_PATH}/goahead-5.2.0/GoAhead/build/${GOAHEAD_INCLUDE_PATH}/bin/libgoahead-mbedtls.a ${EXTERNAL_LIBS_OUTPUT_PATH}/libgoahead-mbedtls.a // 拷贝静态库到output目录 + COMMAND cp ${EXTERNAL_SOURCE_PATH}/goahead-5.2.0/GoAhead/build/${GOAHEAD_INCLUDE_PATH}/bin/self.crt ${PLATFORM_PATH}/cmake-shell/ // 拷贝self.crt,self.key两个到运行目录; + COMMAND cp ${EXTERNAL_SOURCE_PATH}/goahead-5.2.0/GoAhead/build/${GOAHEAD_INCLUDE_PATH}/bin/self.key ${PLATFORM_PATH}/cmake-shell/ // 拷贝self.crt,self.key两个到运行目录; + COMMAND cp ${EXTERNAL_SOURCE_PATH}/goahead-5.2.0/GoAhead/test/route.txt ${GOAHEAD_CONFIG_FILE_PATH} // 配置文件两个:route.txt,auth.txt; + COMMAND cp ${EXTERNAL_SOURCE_PATH}/goahead-5.2.0/GoAhead/test/auth.txt ${GOAHEAD_CONFIG_FILE_PATH} // 配置文件两个:route.txt,auth.txt; + WORKING_DIRECTORY ${EXTERNAL_SOURCE_PATH}/goahead-5.2.0/GoAhead +) +add_custom_target( + libgo.a // 创建goahead目标 + DEPENDS ${EXTERNAL_LIBS_OUTPUT_PATH}/libgo.a +) +``` + +## 1.3. 环境配置 + +1. 拷贝self.crt,self.key两个到运行目录; +2. 配置文件两个:route.txt,auth.txt; + +  self.crt,self.key两个文件默认使用当前路径,需要配置。 + +详见配置:**GOAHEAD_CONFIG_FILE_PATH** + +``` +# ------------ build GoAhead ------------ # +set(GOAHEAD_DOCUMENTS_PATH "web") +set(GOAHEAD_UPLOAD_TMP_PATH "./goahead") +set(GOAHEAD_UPLOAD_PATH "${GOAHEAD_UPLOAD_TMP_PATH}") +set(GOAHEAD_LIMIT_POST "335544320") # If not defined means using default setting. See goahead-linux-static-fancy.mk +# GOAHEAD_CONFIG_FILE_PATH should be set when cross compile +set(GOAHEAD_CONFIG_FILE_PATH ".") +# ------------ build GoAhead end ------------ # +``` + +转换成ME_GOAHEAD_SSL_KEY和ME_GOAHEAD_SSL_CERTIFICATE两个宏来指定文件路径。 + +``` +add_custom_command( + OUTPUT ${EXTERNAL_LIBS_OUTPUT_PATH}/libgo.a + DEPENDS ${EXTERNAL_SOURCE_PATH}/goahead-5.2.0/GoAhead/Makefile + COMMAND make CC=${CMAKE_C_COMPILER} ARCH=${SET_ARCH} PROFILE=${LIB_TYPE} ME_GOAHEAD_UPLOAD_DIR=\"${GOAHEAD_UPLOAD_TMP_PATH}\" + ME_GOAHEAD_SSL_KEY=\"${GOAHEAD_CONFIG_FILE_PATH}/self.key\" ME_GOAHEAD_SSL_CERTIFICATE=\"${GOAHEAD_CONFIG_FILE_PATH}/self.crt\" + ${ME_GOAHEAD_LIMIT_POST} -f ../modify/goahead-linux-static-fancy.mk + COMMAND mv ${EXTERNAL_SOURCE_PATH}/goahead-5.2.0/GoAhead/build/${GOAHEAD_INCLUDE_PATH}/bin/libgo.a ${EXTERNAL_LIBS_OUTPUT_PATH}/libgo.a + COMMAND mv ${EXTERNAL_SOURCE_PATH}/goahead-5.2.0/GoAhead/build/${GOAHEAD_INCLUDE_PATH}/bin/libmbedtls.a ${EXTERNAL_LIBS_OUTPUT_PATH}/libmbedtls.a + COMMAND mv ${EXTERNAL_SOURCE_PATH}/goahead-5.2.0/GoAhead/build/${GOAHEAD_INCLUDE_PATH}/bin/libgoahead-mbedtls.a ${EXTERNAL_LIBS_OUTPUT_PATH}/libgoahead-mbedtls.a + COMMAND cp ${EXTERNAL_SOURCE_PATH}/goahead-5.2.0/GoAhead/build/${GOAHEAD_INCLUDE_PATH}/bin/self.crt ${PLATFORM_PATH}/cmake-shell/ + COMMAND cp ${EXTERNAL_SOURCE_PATH}/goahead-5.2.0/GoAhead/build/${GOAHEAD_INCLUDE_PATH}/bin/self.key ${PLATFORM_PATH}/cmake-shell/ + COMMAND cp ${EXTERNAL_SOURCE_PATH}/goahead-5.2.0/GoAhead/test/route.txt ${GOAHEAD_CONFIG_FILE_PATH} + COMMAND cp ${EXTERNAL_SOURCE_PATH}/goahead-5.2.0/GoAhead/test/auth.txt ${GOAHEAD_CONFIG_FILE_PATH} + WORKING_DIRECTORY ${EXTERNAL_SOURCE_PATH}/goahead-5.2.0/GoAhead +) +``` + +## 1.4. 代码整改 + +### 1.4.1. web服务安全退出 + +  由于官方源码是不超时阻塞,为了方便自动化测试,把官方源码的不超时阻塞改成超时阻塞。 +``` +PUBLIC void websServiceEvents(int *finished) +{ + int delay, nextEvent; + + if (finished) { + *finished = 0; + } + // ===================== added by xiao ===================== // + // delay = 0; // open source code + #define TIME_OUT_MS 1000 + delay = TIME_OUT_MS; // modifed by xiao, time out 1000 ms + // ===================== added by xiao end ===================== // + while (!finished || !*finished) { + if (socketSelect(-1, delay)) { + socketProcess(); + } +#if ME_GOAHEAD_CGI + delay = websCgiPoll(); +#else + delay = MAXINT; +#endif + nextEvent = websRunEvents(); + delay = min(delay, nextEvent); + // ===================== added by xiao ===================== // + delay = (delay <= 0 || delay >= TIME_OUT_MS) ? TIME_OUT_MS : delay; + // ===================== added by xiao end ===================== // + } +} +``` + +### 1.4.2. HTTP POST 文件限制 + +  源码限制POST文件大小太小,这里修改为32M。 + +``` +``` + +## 1.5. goahead的bug + +  当goahead使用无效的ip进行listen时,asan工具会报内存泄漏问题,目前暂时忽略该bug + +``` +================================================================= +==1420508==ERROR: LeakSanitizer: detected memory leaks + +Direct leak of 17 byte(s) in 1 object(s) allocated from: + #0 0x7f3f4325d808 in __interceptor_malloc ../../../../src/libsanitizer/asan/asan_malloc_linux.cc:144 + #1 0x55bc16b87490 in walloc src/alloc.c:280 + #2 0x55bc16b991ae in sclone src/runtime.c:2430 + #3 0x55bc16b9be26 in socketParseAddress src/socket.c:1334 + #4 0x55bc16b891bf in websListen src/http.c:646 + #5 0x55bc16b86f59 in WebServerInit /home/xiaojiazhu/project/rkipc/battery/ipc-rk1106/ipc-sdk/utils/WebServer/src/WebServer.cpp:152 + #6 0x55bc16b81f4a in AppManager::HttpServerThread(app_param const&) /home/xiaojiazhu/project/rkipc/battery/ipc-rk1106/ipc-sdk/middleware/AppManager/src/AppManager.cpp:79 + #7 0x55bc16b829f0 in operator() /home/xiaojiazhu/project/rkipc/battery/ipc-rk1106/ipc-sdk/middleware/AppManager/src/AppManager.cpp:52 + #8 0x55bc16b829f0 in __invoke_impl)>, std::shared_ptr > /usr/include/c++/9/bits/invoke.h:60 + #9 0x55bc16b829f0 in __invoke)>, std::shared_ptr > /usr/include/c++/9/bits/invoke.h:95 + #10 0x55bc16b829f0 in _M_invoke<0, 1> /usr/include/c++/9/thread:244 + #11 0x55bc16b829f0 in operator() /usr/include/c++/9/thread:251 + #12 0x55bc16b829f0 in _M_run /usr/include/c++/9/thread:195 + #13 0x7f3f4301bdf3 (/lib/x86_64-linux-gnu/libstdc++.so.6+0xd6df3) + +SUMMARY: AddressSanitizer: 17 byte(s) leaked in 1 allocation(s). +``` \ No newline at end of file diff --git a/utils/WebServer/build/webserver.cmake b/utils/WebServer/build/webserver.cmake new file mode 100644 index 0000000..9ff2327 --- /dev/null +++ b/utils/WebServer/build/webserver.cmake @@ -0,0 +1,11 @@ + +if (NOT DEFINED GOAHEAD_DOCUMENTS_PATH) + set(GOAHEAD_DOCUMENTS_PATH "web") +endif() +if (NOT DEFINED GOAHEAD_CONFIG_FILE_PATH) + set(GOAHEAD_CONFIG_FILE_PATH "${PLATFORM_PATH}/cmake-shell/") + add_definitions(-DGOAHEAD_CONFIG_FILE_PATH=\"./\") +else() + add_definitions(-DGOAHEAD_CONFIG_FILE_PATH=\"${GOAHEAD_CONFIG_FILE_PATH}\") # 代码中使用该宏定义下的配置文件 +endif() +add_definitions(-DGOAHEAD_DOCUMENTS_PATH=\"${GOAHEAD_DOCUMENTS_PATH}\") \ No newline at end of file diff --git a/utils/WebServer/include/WebServer.h b/utils/WebServer/include/WebServer.h new file mode 100644 index 0000000..8e779a5 --- /dev/null +++ b/utils/WebServer/include/WebServer.h @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2023 Fancy Code. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef WEB_SERVER_H +#define WEB_SERVER_H +#include "StatusCode.h" +#ifdef __cplusplus +extern "C" { +#endif +typedef void (*ResponseHandle)(const char *, void *); +typedef void (*HttpHandleCallback)(const char *, const unsigned int, ResponseHandle, void *); +typedef struct web_server_param +{ + const char *mIp; + int mPort; + HttpHandleCallback mHttpRequestHandle; +} WebServerParam; +StatusCode WebServerInit(const WebServerParam webParam); +StatusCode WebServerExit(void); +StatusCode WebServerUnInit(void); +#ifdef __cplusplus +} +#endif +#endif \ No newline at end of file diff --git a/utils/WebServer/src/WebServer.cpp b/utils/WebServer/src/WebServer.cpp new file mode 100644 index 0000000..d1cfdcc --- /dev/null +++ b/utils/WebServer/src/WebServer.cpp @@ -0,0 +1,230 @@ +/* + * Copyright (c) 2023 Fancy Code. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "WebServer.h" +#include "ILog.h" +#include "goahead.h" +#include "js.h" +#include +static int finished = 0; +static HttpHandleCallback gHttpHandle = NULL; +constexpr int UNKNOWN_LENGTH = -1; +static void sigHandler(int signo) +{ + LogInfo("Stop goahead web server.\n"); + finished = 1; +} +static void CheckUploadDir(void) +{ + const char *directory = GOAHEAD_UPLOAD_TMP_PATH; + if (access(directory, F_OK) != 0) { + int result = mkdir(directory, 0777); + + if (result == 0) { + LogInfo("mkdir upload tmp path successfuly.\n"); + } + else { + LogError("mkdir upload tmp path failed.\n"); + } + } + + const char *directory2 = GOAHEAD_UPLOAD_PATH; + if (access(directory2, F_OK) != 0) { + int result = mkdir(directory2, 0777); + + if (result == 0) { + LogInfo("mkdir upload path successfuly.\n"); + } + else { + LogError("mkdir upload path failed.\n"); + } + } +} +static void logHeader(void) +{ + char home[ME_GOAHEAD_LIMIT_STRING]; + + char *result = getcwd(home, sizeof(home)); + if (nullptr == result) { + LogWarning("Can't get path.\n"); + } + logmsg(2, "Configuration for %s", ME_TITLE); + logmsg(2, "---------------------------------------------"); + logmsg(2, "Version: %s", ME_VERSION); + logmsg(2, "BuildType: %s", ME_DEBUG ? "Debug" : "Release"); + logmsg(2, "CPU: %s", ME_CPU); + logmsg(2, "OS: %s", ME_OS); + logmsg(2, "Host: %s", websGetServer()); + logmsg(2, "Directory: %s", home); + logmsg(2, "Documents: %s", websGetDocuments()); + logmsg(2, "Configure: %s", ME_CONFIG_CMD); + logmsg(2, "---------------------------------------------"); +} +void initPlatform(void) +{ + signal(SIGINT, sigHandler); + signal(SIGTERM, sigHandler); + signal(SIGKILL, sigHandler); + signal(SIGPIPE, SIG_IGN); +} +static void response_handle(const char *responseStr, void *context) +{ + struct Webs *wp = (struct Webs *)context; + if (NULL != responseStr) { + websWrite(wp, "%s", responseStr); + } +} +static void get_thumbnail_handle(const char *thumbnailUrl, void *context) +{ + struct Webs *wp = (struct Webs *)context; + if (nullptr == thumbnailUrl) { + LogError("thumbnailUrl is nullptr.\n"); + return; + } + FILE *file = nullptr; + unsigned char *buffer = nullptr; + size_t bytesRead = 0; + long int fsize = 0; + file = fopen(thumbnailUrl, "rb"); + if (file == nullptr) { + LogError("Open picture failed[%s].\n", thumbnailUrl); + goto END; + } + fseek(file, 0, SEEK_END); + fsize = ftell(file); + rewind(file); + buffer = (unsigned char *)malloc(fsize); + if (!buffer) { + LogError("malloc failed.\n"); + goto END; + } + + while ((bytesRead = fread(buffer, 1, sizeof(buffer), file)) > 0) { + websWriteBlock(wp, (cchar *)buffer, bytesRead); + } + +END: + if (buffer) { + free(buffer); + } + if (file) { + fclose(file); + } +} +static bool AppRequestHandle(Webs *wp) +{ + websSetStatus(wp, 200); + websWriteHeaders(wp, UNKNOWN_LENGTH, 0); + websWriteHeader(wp, "Content-Type", "text/plain"); + websWriteEndHeaders(wp); + gHttpHandle(wp->url, 0, response_handle, wp); + websDone(wp); + return 1; +} +static bool AppUploadHandle(Webs *wp) +{ + WebsKey *s; + WebsUpload *up; + char *upfile = nullptr; + + websSetStatus(wp, 200); + websWriteHeaders(wp, UNKNOWN_LENGTH, 0); + websWriteHeader(wp, "Content-Type", "text/plain"); + websWriteEndHeaders(wp); + + if (scaselessmatch(wp->method, "POST")) { + for (s = hashFirst(wp->files); s; s = hashNext(wp->files, s)) { + up = (WebsUpload *)s->content.value.symbol; + // LogInfo("FILE: %s\r\n", s->name.value.string); + // LogInfo("FILENAME=%s\r\n", up->filename); + LogInfo("CLIENT=%s\r\n", up->clientFilename); + // LogInfo("TYPE=%s\r\n", up->contentType); + LogInfo("SIZE=%d\r\n", up->size); + upfile = sfmt(GOAHEAD_UPLOAD_PATH "/%s", up->clientFilename); + if (rename(up->filename, upfile) < 0) { + error("Cannot rename uploaded file: %s to %s, errno %d", up->filename, upfile, errno); + } + LogInfo("Upload file:%s\n", upfile); + wfree(upfile); + upfile = nullptr; + } + gHttpHandle(wp->url, 0, response_handle, wp); + } + websDone(wp); + return 1; +} +static bool AppGetThumbnail(Webs *wp) +{ + LogInfo("AppGetThumbnail url = %s\n", wp->url); + websSetStatus(wp, 200); + websWriteHeaders(wp, UNKNOWN_LENGTH, 0); + websWriteHeader(wp, "Content-Type", "image/jpeg"); + websWriteEndHeaders(wp); + gHttpHandle(wp->url, 0, get_thumbnail_handle, wp); + websDone(wp); + return 1; +} +StatusCode WebServerInit(const WebServerParam webParam) +{ + CheckUploadDir(); + websSetDebug(1); + logSetPath("stdout:2"); + const char *documents = GOAHEAD_DOCUMENTS_PATH; + constexpr int BUF_LENGTH = 128; + char routePath[BUF_LENGTH] = {0}; + char authPath[BUF_LENGTH] = {0}; + char listen[BUF_LENGTH] = {0}; + snprintf(routePath, BUF_LENGTH, "%s/route.txt", GOAHEAD_CONFIG_FILE_PATH); + snprintf(authPath, BUF_LENGTH, "%s/auth.txt", GOAHEAD_CONFIG_FILE_PATH); + snprintf(listen, BUF_LENGTH, "%s:%d", webParam.mIp, webParam.mPort); + LogInfo("GOAHEAD_CONFIG_FILE_PATH = %s\n", GOAHEAD_CONFIG_FILE_PATH); + LogInfo("listen = %s\n", listen); + initPlatform(); + if (websOpen(documents, routePath) < 0) { + LogError("Cannot initialize server. Exiting.\n"); + return CreateStatusCode(STATUS_CODE_NOT_OK); + } + logHeader(); + if (websLoad(authPath) < 0) { + LogError("Cannot load %s", authPath); + return CreateStatusCode(STATUS_CODE_NOT_OK); + } + if (websListen(listen) < 0) { + return CreateStatusCode(STATUS_CODE_NOT_OK); + } + if (nullptr != webParam.mHttpRequestHandle) { + gHttpHandle = webParam.mHttpRequestHandle; + websDefineHandler("appRequestHandle", 0, AppRequestHandle, 0, 0); + websAddRoute("/app", "appRequestHandle", 0); + websDefineHandler("AppUploadHandle", 0, AppUploadHandle, 0, 0); + websAddRoute("/upload", "AppUploadHandle", 0); + websDefineHandler("AppGetThumbnail", 0, AppGetThumbnail, 0, 0); + websAddRoute("/app/getthumbnail", "AppGetThumbnail", 0); + } + websServiceEvents(&finished); + logmsg(1, "Instructed to exit\n"); + websClose(); + return CreateStatusCode(STATUS_CODE_OK); +} +StatusCode WebServerExit(void) +{ + LogInfo("Stop goahead web server.\n"); + finished = 1; + return CreateStatusCode(STATUS_CODE_OK); +} +StatusCode WebServerUnInit(void) +{ + LogInfo("WebServerUnInit.\n"); + return CreateStatusCode(STATUS_CODE_OK); +} \ No newline at end of file