整理代码

This commit is contained in:
蒲爽 2026-09-28 21:33:04 +08:00
commit c7b4e178ec
364 changed files with 59691 additions and 0 deletions

15
.gitignore vendored Normal file
View file

@ -0,0 +1,15 @@
**/.build
**/build
**/.vscode
**/.vs
# 运行时生成:配置目录与日志
config/
logs/
*.log
*.o
*.a
*.exe
*.ilk
*.pdb

308
Application.cpp Normal file
View file

@ -0,0 +1,308 @@
/*
*******************************************************************************
* INCLUDE
*******************************************************************************
*/
#include "Application.h"
#ifdef USING_NETWORK_SUBSYSTEM
#include "NetworkSubsystem.h"
#endif
#ifdef USING_GRAPHICS_SUBSYSTEM
#include "GraphicsSubsystem.h"
#endif
#include <Poco/Dynamic/Var.h>
#include <Poco/File.h>
#include <Poco/FileStream.h>
#include <Poco/JSON/Object.h>
#include <Poco/JSON/Parser.h>
#include <Poco/JSON/PrintHandler.h>
#include <Poco/Net/DNS.h>
#include <Poco/Net/IPAddress.h>
#include <Poco/Net/NetworkInterface.h>
#include <Poco/Path.h>
#include <Poco/Pipe.h>
#include <Poco/PipeStream.h>
#include <Poco/Process.h>
#include <Poco/StreamCopier.h>
#include <Poco/Timespan.h>
#include <Poco/Util/JSONConfiguration.h>
#include <Poco/Util/Timer.h>
#include <Poco/Checksum.h>
#include <atca_basic.h>
#include <atca_cfgs.h>
#include <atca_device.h>
#include <host/atca_host.h>
#include <sys/stat.h>
#include <iostream>
#include <fstream>
#include <arpa/inet.h>
#include <linux/rtnetlink.h>
#include <net/if.h>
#include <netinet/in.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <unistd.h>
#include <sys/reboot.h>
#include <sys/time.h>
#include <time.h>
#include "hardware/BoardInfo.h"
#include "hardware/GPIO.h"
#include "hardware/HardwareSubsystem.h"
#include "util/HexBinaryCodec.h"
#include "util/SystemInfo.h"
#include "util/Temperature.h"
/*
*******************************************************************************
* CONSTANTS
*******************************************************************************
*/
static const std::string CONFIG_ROOT_PATH = "/customer/conf";
static const std::string ROOTCFG_FILENAME = CONFIG_ROOT_PATH + "/config.json";
static const std::string DEFAULT_ROOTCFG_FILENAME = CONFIG_ROOT_PATH + "/config_default.json";
const std::string VERSION_FILE = "/customer/VERSION";
const std::string IMAGE_VERSION_FILE = "/etc/IMAGE_VERSION";
/*
*******************************************************************************
* CLASS DEFINE
*******************************************************************************
*/
const std::string Application::ROOT_CONFIG = "config";
void Application::initialize(Poco::Util::Application& self)
{
_exitRequst = false;
initConfigAll();
Poco::Util::Application::initialize(self);
}
void Application::uninitialize()
{
Poco::Util::Application::uninitialize();
}
int Application::main(const std::vector<std::string>& args)
{
while (!_exitRequst)
Poco::Thread::sleep(1000);
return EXIT_OK;
}
std::string Application::getConfigFilePath(const std::string& label)
{
Poco::Path path(getConfig(Application::ROOT_CONFIG).getString("buss_params_path"));
if (path.isRelative())
path = path.makeAbsolute(Poco::Path::current());
return path.toString() + label + ".json";
}
void Application::initConfigAll()
{
config().add(new Poco::Util::JSONConfiguration(ROOTCFG_FILENAME), ROOT_CONFIG, PRIO_SYSTEM, true);
}
void Application::initConfig(const std::string& label)
{
int retry = 0;
while (retry++ < 3)
{
try
{
config().add(new Poco::Util::JSONConfiguration(getConfigFilePath(label)), label, PRIO_APPLICATION, true);
break;
}
catch (Poco::Exception& e)
{
logger().error("Load config file '%s' FAIL: %s, USE DEFAULT CONFIG FILE INSTEAD!", label, e.displayText());
Poco::File dFile(getConfigFilePath(label + "_default"));
if (dFile.exists())
{
dFile.copyTo(getConfigFilePath(label));
}
else
{
logger().error("Load config file '%s' FAIL: %s, DEFAULT CONFIG FILE IS NOT EXIST", label,
e.displayText());
}
}
}
}
std::string Application::version() const
{
try
{
Poco::FileInputStream istr(VERSION_FILE);
std::string version;
istr >> version;
return version;
}
catch (...)
{
return "0.0.0";
}
}
Poco::Util::AbstractConfiguration& Application::getConfig() const
{
return config();
}
Poco::Util::AbstractConfiguration& Application::getConfig(const std::string& label) const
{
return findConfig<Poco::Util::JSONConfiguration>(label);
}
void Application::saveConfig(const std::string& label)
{
logger().information("Saving config...");
std::string tmpFilePath = "/tmp/" + label + ".json.tmp";
std::string filePath = getConfigFilePath(label);
Poco::FileOutputStream fost(tmpFilePath);
dumpConfig(label, fost);
fost.flush();
fost.close();
try
{
Poco::File(tmpFilePath).moveTo(filePath);
logger().information("Config saved successfully.");
}
catch (Poco::Exception& e)
{
logger().error("Save config file ERROR: %s", e.displayText());
}
}
void Application::loadConfig(const std::string& label, std::istream& istr)
{
auto& config = findConfig<Poco::Util::JSONConfiguration>(label);
config.load(istr);
saveConfig(label);
ConfigChanged.notifyAsync(this, label);
}
void Application::loadConfig(const std::string& label, const std::string& str)
{
std::istringstream istr(str);
loadConfig(label, istr);
}
void Application::dumpConfig(const std::string& label, std::ostream& os)
{
auto& config = findConfig<Poco::Util::JSONConfiguration>(label);
config.save(os);
}
void Application::configNotifyAsync(const std::string& label)
{
ConfigChanged.notifyAsync(this, label);
}
void Application::restoreFactorySettings()
{
try
{
printf("Restore system factory settings...\r\n");
system("/customer/reset-key -Pweb");
}
catch (...)
{
}
reboot(2000);
}
void Application::reboot(uint32_t delay)
{
_delayRebootThread.startFunc([=]() -> void {
logger().warning("Reboot after %u ms ...", delay);
Poco::Thread::sleep(delay);
exit();
::sync();
if (system("reboot") != 0)
logger().error("System reboot command failed!");
::reboot(RB_AUTOBOOT);
});
}
void Application::setSystemTime(uint32_t timestamp, int timezone)
{
struct timeval tv;
tv.tv_sec = timestamp;
tv.tv_usec = 0;
settimeofday(&tv, NULL);
try
{
std::string cmd = "";
if (timezone >= 0)
{
cmd = Poco::format("ln -snf /customer/zoneinfo/Etc/GMT-%d /etc/localtime", timezone);
}
else
{
cmd = Poco::format("ln -snf /customer/zoneinfo/Etc/GMT+%d /etc/localtime", -1 * timezone);
}
system(cmd.c_str());
}
catch (const std::exception& e)
{
std::cerr << e.what() << '\n';
}
system("hwclock -w -l");
}
void Application::exit()
{
ApplicationExitNotify.notify(this);
_exitRequst = true;
}
void Application::upgrade(const std::string& file)
{
#if defined(FIRMWARE_UPGRADE_FROM_BACKUP_PARTITION)
Poco::File("/customer/DIRTY").createFile();
logger().warning("Reboot to upgrade firmware ....");
#else
std::string specCharacters[] = {
"\\", " ", "\"", "'", "(", ")", "*", "!", "$", "%", "&", ">", "<", "|", "`",
};
for (auto& c : specCharacters)
file = Poco::replace(file, c, "\\" + c);
Poco::Pipe inPipe, outPipe;
Poco::Process::Args args;
Poco::ProcessHandle handle = Poco::Process::launch("sh", args, &inPipe, &outPipe, 0);
Poco::PipeOutputStream outputStream(inPipe);
std::string tmpdir = "/tmp/upgrade";
outputStream << Poco::format("mkdir -p %s", tmpdir) << std::endl;
outputStream << Poco::format("tar xf %s -C %s", file, tmpdir) << std::endl;
outputStream << Poco::format("%s/upgrade.sh", tmpdir) << std::endl;
outputStream.flush();
outputStream.close();
handle.wait();
Poco::PipeInputStream inputStream(outPipe);
std::string str;
Poco::StreamCopier::copyToString(inputStream, str);
logger().information("Firmware upgrade complete, reboot....");
#endif
reboot(2000);
}

115
Application.h Normal file
View file

@ -0,0 +1,115 @@
#ifndef C7F1A9D3_4E28_4B60_9C15_8A3E6D2F0B94
#define C7F1A9D3_4E28_4B60_9C15_8A3E6D2F0B94
/*
*******************************************************************************
* INCLUDE
*******************************************************************************
*/
#include <Poco/BasicEvent.h>
#include <Poco/Dynamic/Var.h>
#include <Poco/JSON/Array.h>
#include <Poco/JSON/Object.h>
#include <Poco/JSON/Parser.h>
#include <Poco/Net/IPAddress.h>
#include <Poco/Net/NetworkInterface.h>
#include <Poco/Util/Application.h>
#include <atomic>
#include "util/JsonUtil.h"
/*
*******************************************************************************
* CONSTANTS
*******************************************************************************
*/
#define APPLICATION_VERSION application().version()
/*
*******************************************************************************
* MACROS
*******************************************************************************
*/
#define application() (*(Application::getInstance()))
/*
*******************************************************************************
* TYPEDEFS
*******************************************************************************
*/
using ConfigurationPtr = Poco::Util::AbstractConfiguration::Ptr;
using Configuration = Poco::Util::AbstractConfiguration;
/*
*******************************************************************************
* CLASS DECLARE
*******************************************************************************
*/
class Application: public Poco::Util::Application
{
public:
struct SystemStatus
{
};
public:
static const std::string ROOT_CONFIG;
Poco::BasicEvent<std::string> ConfigChanged;
Poco::BasicEvent<void> ApplicationExitNotify;
protected:
void initialize(Poco::Util::Application& self) override;
void uninitialize() override;
int main(const std::vector<std::string>& args) override;
std::string getConfigFilePath(const std::string& label);
template <class T> T& findConfig(const std::string& label) const
{
return *static_cast<T*>(config().find(label).get());
}
void initConfigAll();
void initConfig(const std::string& label);
public:
std::string version() const;
Poco::Util::AbstractConfiguration& getConfig() const;
Poco::Util::AbstractConfiguration& getConfig(const std::string& label) const;
void saveConfig(const std::string& label);
void loadConfig(const std::string& label, std::istream& istr);
void loadConfig(const std::string& label, const std::string& istr);
void dumpConfig(const std::string& label, std::ostream& ostr);
void configNotifyAsync(const std::string& label);
void restoreFactorySettings();
void reboot(uint32_t delay = 2000);
void setSystemTime(uint32_t timestamp, int timezone = 8);
void exit();
void upgrade(const std::string& file);
static Application* getInstance()
{
Poco::Util::Application& app = instance();
return static_cast<Application*>(&app);
}
private:
Poco::Thread _delayRebootThread;
std::atomic<bool> _exitRequst;
};
#endif

162
CMakeLists.txt Normal file
View file

@ -0,0 +1,162 @@
cmake_minimum_required(VERSION 3.16)
set(PROJNAME encoder)
include(config.cmake)
include(FetchContent)
project(${PROJNAME})
set(ARCH arm)
if (USING_HISIMPP5)
add_compile_definitions(
-DUSING_HISIMPP5
)
endif()
add_compile_definitions(
-DFIRMWARE_UPGRADE_FROM_BACKUP_PARTITION
)
execute_process(
COMMAND git log -1 --format=%h
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
OUTPUT_VARIABLE GIT_COMMIT_HASH
OUTPUT_STRIP_TRAILING_WHITESPACE
)
add_compile_definitions(-DGIT_COMMIT_HASH=\"${GIT_COMMIT_HASH}\")
# 未定义或为空
if (NOT DEFINED CMAKE_BUILD_TYPE OR NOT CMAKE_BUILD_TYPE)
set (CMAKE_BUILD_TYPE Debug)
endif()
message("Project \"${GIT_COMMIT_HASH}\" build type: " ${CMAKE_BUILD_TYPE})
if (CMAKE_BUILD_TYPE MATCHES Debug)
add_compile_definitions(-DDEBUG)
endif()
if (CMAKE_SYSTEM_NAME MATCHES "Linux")
set(Poco_DIR $ENV{POCO_PATH}/lib/cmake/Poco)
link_libraries(
pthread
dl
m
atomic
)
if (CMAKE_BUILD_TYPE MATCHES Debug)
# 定义Debug编译参数
SET(CMAKE_CXX_FLAGS_DEBUG "$ENV{CXXFLAGS} -Og -Wall -g3 -Wno-psabi -std=c++1z -mfpu=neon -mfloat-abi=softfp") # GDB调试
# SET(CMAKE_CXX_FLAGS_DEBUG "$ENV{CXXFLAGS} -Og -Wall -g3 -Wno-psabi -std=c++1z -mfpu=neon -mfloat-abi=softfp -fsanitize=address -fno-eliminate-unused-debug-types -fno-omit-frame-pointer") # 内存检测
# SET(CMAKE_CXX_FLAGS_DEBUG "$ENV{CXXFLAGS} -O0 -Wall -Wno-psabi")
# SET(CMAKE_CXX_FLAGS_DEBUG "$ENV{CXXFLAGS} -O0 -Wall -Wno-psabi -rdynamic -funwind-tables -ffunction-sections") # 可以在应用里捕获SIGSEGV信号,然后用backtrace()函数把堆栈信息打印出来
else()
# 定义Release编译参数
SET(CMAKE_CXX_FLAGS_RELEASE "$ENV{CXXFLAGS} -O3 -Wall -Wno-psabi -mfpu=neon -mfloat-abi=softfp")
endif()
add_compile_definitions(-DOS_LINUX)
elseif (CMAKE_SYSTEM_NAME MATCHES "Windows")
set(Poco_DIR $ENV{POCO_PATH}/cmake)
# 强制Visual Studio使用UTF-8编码
add_compile_options("$<$<C_COMPILER_ID:MSVC>:/utf-8>")
add_compile_options("$<$<CXX_COMPILER_ID:MSVC>:/utf-8>")
# 忽略部分警告
add_compile_definitions(/wd"4828")
# 使用Unicode字符集
add_compile_definitions(-DUNICODE -D_UNICODE)
if (USING_LVGL_PLATFORM OR USING_SOUI_PLATFORM)
# 设置为窗口程序
set(GUI_EXECUTABLE WIN32)
add_compile_definitions(-DWINDOWS_GUI_APPLICATION)
endif()
link_directories($ENV{POCO_PATH}/lib)
endif()
find_package(Poco REQUIRED COMPONENTS Foundation Net XML Encodings Util Zip Crypto)
include_directories(
.
${Poco_LIBRARIES}
$ENV{JSONC_PATH}/include
$ENV{CRYPTOAUTHLIB_PATH}/include/cryptoauthlib
$ENV{OPENSSL_PATH}/include
$ENV{PAHO_MQTTC_PATH}/include
)
link_libraries(
${Poco_LIBRARIES}
$ENV{CRYPTOAUTHLIB_PATH}/lib/libcryptoauth.a
$ENV{OPENSSL_PATH}/lib/libssl.a
$ENV{OPENSSL_PATH}/lib/libcrypto.a
#$ENV{PAHO_MQTTC_PATH}/lib/libpaho-mqtt3a.a
#$ENV{PAHO_MQTTC_PATH}/lib/libpaho-mqtt3as.a
$ENV{PAHO_MQTTC_PATH}/lib/libpaho-mqtt3c.a
#$ENV{PAHO_MQTTC_PATH}/lib/libpaho-mqtt3cs.a
)
if (USING_HISIMPP5)
include_directories(
$ENV{MPP_PATH}/include
)
link_libraries(
$ENV{MPP_PATH}/lib/libmpi.a
$ENV{MPP_PATH}/lib/libdnvqe.a
$ENV{MPP_PATH}/lib/libupvqe.a
$ENV{MPP_PATH}/lib/libvqe_res.a
$ENV{MPP_PATH}/lib/libVoiceEngine.a
$ENV{MPP_PATH}/lib/libsecurec.so
$ENV{MPP_PATH}/lib/libhdmi.a
$ENV{MPP_PATH}/lib/libtde.a
)
endif()
aux_source_directory(. MAIN_SOURCES)
add_executable(${PROJNAME} ${GUI_EXECUTABLE} ${MAIN_SOURCES})
# target_compile_options(${PROJNAME} PRIVATE
# -fsanitize=address,undefined
# -fsanitize=float-divide-by-zero
# -fsanitize=null
# -fsanitize=bounds-strict
# )
# target_link_options(${PROJNAME} PRIVATE
# -fsanitize=address,undefined
# )
if (CMAKE_SYSTEM_NAME MATCHES "Windows")
SET_PROPERTY (DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} PROPERTY VS_STARTUP_PROJECT ${PROJNAME} )
endif()
if (USING_VIDEO_SUBSYSTEM)
add_compile_definitions(-DUSING_VIDEO_SUBSYSTEM)
endif()
# 网络
if (USING_NETWORK_SUBSYSTEM)
add_compile_definitions(-DUSING_NETWORK_SUBSYSTEM)
endif()
# 板级设备
if (USING_HARDWARE_SUBSYSTEM)
add_compile_definitions(-DUSING_HARDWARE_SUBSYSTEM)
endif()
if (USING_VIDEO_SUBSYSTEM)
include(video/video.cmake)
endif()
# 网络
if (USING_NETWORK_SUBSYSTEM)
include(network/network.cmake)
endif()
# 板级设备
if (USING_HARDWARE_SUBSYSTEM)
include(hardware/hardware.cmake)
endif()
# Util
include(util/util.cmake)

6
Logger.cpp Normal file
View file

@ -0,0 +1,6 @@
/*
*******************************************************************************
* INCLUDE
*******************************************************************************
*/
#include "Logger.h"

16
Logger.h Normal file
View file

@ -0,0 +1,16 @@
#ifndef E4B8FD4A_9AEF_4A23_9C62_8D091F7306FE
#define E4B8FD4A_9AEF_4A23_9C62_8D091F7306FE
/*
*******************************************************************************
* INCLUDE
*******************************************************************************
*/
#include "Application.h"
/*
*******************************************************************************
* MACROS
*******************************************************************************
*/
#define logger() (application().logger())
#endif

32
config.cmake Normal file
View file

@ -0,0 +1,32 @@
message(BOARD..${BOARD})
option(USING_HARDWARE_SUBSYSTEM "Using Hardware subsystem" ON)
option(USING_HISIMPP5 "Using Hisilicon MPP5" ON)
# Video
option(USING_VIDEO_SUBSYSTEM "Using Video subsystem" ON)
option(USING_FFMPEG "Using ffmpeg library" ON)
option(USING_MEDIA_FRAMEWORK "Using Media-Framework" ON)
option(USING_ZLMEDIAKIT "Using ZLMediaKit" ON)
option(USING_ONVIF "Using ONVIF" ON)
option(USING_ITE_FRONTEND "Using ITE IT66021" ON)
option(USING_SEMTECH_DEVICE "Using SEMTECH device" OFF)
option(USING_MEDIASERVER "Using media-server" ON)
option(USING_LIBOPUS "Using libopus" ON)
option(USING_SHINE "Using shine MP3" ON)
# Serialization
option(USING_PROTOBUF "Using Google's data interchange format" OFF)
# Network
option(USING_NETWORK_SUBSYSTEM "Using Network subsystem" ON)
option(USING_TCPIP "Using TCP/IP protocol" ON)
# Licence
option(USING_LICENCE_FROM_LOCAL_ENABLE "Using Licence form local enable" ON)
set(CMAKE_C_COMPILER $ENV{GCC_PATH}/$ENV{GCC_PREFIX}gcc)
set(CMAKE_CXX_COMPILER $ENV{GCC_PATH}/$ENV{GCC_PREFIX}g++)
set(CMAKE_BUILD_TYPE Release)
# set(CMAKE_BUILD_TYPE Debug)

28
hardware/BoardInfo.cpp Normal file
View file

@ -0,0 +1,28 @@
/*
*******************************************************************************
* INCLUDE
*******************************************************************************
*/
#include "BoardInfo.h"
namespace Hardware
{
/*
*******************************************************************************
* CLASS DEFINE
*******************************************************************************
*/
Board::Ptr BoardInfo::_pBoard = nullptr;
Board::~Board()
{
}
std::string BoardInfo::serialNumber()
{
if (_pBoard.isNull())
return "NOT IMPLEMENTED";
return _pBoard->serialNumber();
}
}

45
hardware/BoardInfo.h Normal file
View file

@ -0,0 +1,45 @@
#ifndef FD5BB51A_6CF1_4A59_B157_25E320495F52
#define FD5BB51A_6CF1_4A59_B157_25E320495F52
/*
*******************************************************************************
* INCLUDE
*******************************************************************************
*/
#include <Poco/SharedPtr.h>
namespace Hardware
{
/*
*******************************************************************************
* CLASS DECLARE
*******************************************************************************
*/
class Board
{
public:
using Ptr = Poco::SharedPtr<Board>;
public:
~Board();
public:
virtual std::string serialNumber() = 0;
};
class BoardInfo
{
public:
static void registerBoard(Board::Ptr pBoard);
static std::string serialNumber();
private:
static Board::Ptr _pBoard;
};
inline void BoardInfo::registerBoard(Board::Ptr pBoard)
{
_pBoard = pBoard;
}
}
#endif

29
hardware/CMakeLists.txt Normal file
View file

@ -0,0 +1,29 @@
set(TARGET hardware)
aux_source_directory(. HARDWARE_SRCS)
aux_source_directory(user USER_SOURCES)
if (USING_HISIMPP5)
aux_source_directory(hisilicon HISI_SOURCES)
list(APPEND HARDWARE_SRCS ${HISI_SOURCES})
include_directories($ENV{MPP_PATH}/include)
endif()
if (USING_ITE_DEVICE)
message("Using ITE device ...")
aux_source_directory(ite ITE_SOURCES)
list(APPEND HARDWARE_SRCS ${ITE_SOURCES})
endif()
if (USING_SEMTECH_DEVICE)
message("Using SEMTECH device ...")
aux_source_directory(semtech SEMTECH_SOURCES)
list(APPEND HARDWARE_SRCS ${SEMTECH_SOURCES})
endif()
add_library(${TARGET} STATIC
${HARDWARE_SRCS}
${USER_SOURCES}
)

81
hardware/GPIO.cpp Normal file
View file

@ -0,0 +1,81 @@
/*
*******************************************************************************
* INCLUDE
*******************************************************************************
*/
#include "GPIO.h"
#include <unistd.h>
#include <fcntl.h>
#include <sys/poll.h>
#include <Poco/Exception.h>
#include <Poco/Format.h>
namespace Hardware
{
/*
*******************************************************************************
* CLASS DEFINE
*******************************************************************************
*/
GPIO::GPIO(int pinno): _pinno(pinno)
{
std::string s = std::to_string(_pinno);
writeFile("/sys/class/gpio/export", s.c_str(), s.length());
_fd = ::open(Poco::format("/sys/class/gpio/gpio%d/value", _pinno).c_str(), O_RDWR);
if (_fd < 0)
throw Poco::InvalidAccessException("Open FAIL");
}
GPIO::~GPIO()
{
::close(_fd);
std::string s = std::to_string(_pinno);
writeFile("/sys/class/gpio/unexport", s.c_str(), s.length());
}
void GPIO::direction(Direction dir)
{
switch (dir)
{
case Direction::OUT:
default:
writeDirection("out");
break;
case Direction::IN:
writeDirection("in");
break;
}
}
void GPIO::write(unsigned level)
{
if (level)
::write(_fd, "1", 1);
else
::write(_fd, "0", 1);
}
unsigned GPIO::read()
{
char value;
::read(_fd, &value, 1);
return value != '0';
}
void GPIO::writeFile(const std::string& path, const void* pData, int len)
{
int fd = ::open(path.c_str(), O_WRONLY);
if (fd < 0)
throw Poco::InvalidAccessException("Open FAIL");
::write(fd, pData, len);
::close(fd);
}
void GPIO::writeDirection(const std::string& value)
{
writeFile(Poco::format("/sys/class/gpio/gpio%d/direction", _pinno), value.c_str(), value.length());
}
}

44
hardware/GPIO.h Normal file
View file

@ -0,0 +1,44 @@
#ifndef C88B7A31_D4F3_4E43_B4CE_45003C99E915
#define C88B7A31_D4F3_4E43_B4CE_45003C99E915
/*
*******************************************************************************
* INCLUDE
*******************************************************************************
*/
#include <string>
namespace Hardware
{
/*
*******************************************************************************
* CLASS DECLARE
*******************************************************************************
*/
class GPIO
{
public:
enum class Direction
{
IN,
OUT,
};
public:
GPIO(int pinno);
~GPIO();
public:
void direction(Direction dir);
void write(unsigned level);
unsigned read();
private:
void writeFile(const std::string& path, const void* pData, int len);
void writeDirection(const std::string& value);
private:
int _fd;
int _pinno;
};
}
#endif

View file

@ -0,0 +1,35 @@
/*
*******************************************************************************
* INCLUDE
*******************************************************************************
*/
#include "HardwareSubsystem.h"
#include "BoardInfo.h"
#include "hisilicon/Board.h"
#include "Sensor.h"
#include "hisilicon/TemperatureSensor.h"
namespace Hardware
{
/*
*******************************************************************************
* CLASS DEFINE
*******************************************************************************
*/
const char* HardwareSubsystem::name() const
{
return "Hardware";
}
void HardwareSubsystem::initialize(Poco::Util::Application& app)
{
BoardInfo::registerBoard(new Hisilicon::Board());
SensorManager::registerSensor(Sensor::Type::TEMPERATURE, new Hisilicon::TemperatureSensor());
}
void HardwareSubsystem::uninitialize()
{
stopAll();
}
}

View file

@ -0,0 +1,30 @@
#ifndef CEE64BF6_3FB0_4B34_9E98_BCFE7C712DA1
#define CEE64BF6_3FB0_4B34_9E98_BCFE7C712DA1
/*
*******************************************************************************
* INCLUDE
*******************************************************************************
*/
#include "Poco/Util/Application.h"
#include "Poco/Util/Subsystem.h"
#include "Application.h"
#include "util/Subsystem.h"
namespace Hardware
{
/*
*******************************************************************************
* CLASS DECLARE
*******************************************************************************
*/
class HardwareSubsystem: public Util::Subsystem
{
public:
virtual const char* name() const override;
protected:
virtual void initialize(Poco::Util::Application& app) override;
virtual void uninitialize();
};
}
#endif

73
hardware/SPI.cpp Normal file
View file

@ -0,0 +1,73 @@
/*
*******************************************************************************
* INCLUDE
*******************************************************************************
*/
#include "SPI.h"
#include <Poco/Format.h>
#include <Poco/Exception.h>
#include <linux/spi/spidev.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <unistd.h>
#include <fcntl.h>
namespace Hardware
{
/*
*******************************************************************************
* CLASS DEFINE
*******************************************************************************
*/
const uint8_t SPI::MODE_0 = SPI_MODE_0;
const uint8_t SPI::MODE_1 = SPI_MODE_1;
const uint8_t SPI::MODE_2 = SPI_MODE_2;
const uint8_t SPI::MODE_3 = SPI_MODE_3;
SPI::SPI(int bus, int cs)
{
_fd = open(Poco::format("/dev/spidev%d.%d", bus, cs).c_str(), O_RDWR);
if (_fd < 0)
throw Poco::IOException("SPI: Open device FAIL");
}
SPI::~SPI()
{
close(_fd);
}
int SPI::setSpeed(uint32_t speed)
{
return ioctl(_fd, SPI_IOC_WR_MAX_SPEED_HZ, &speed);
}
int SPI::setMode(uint8_t mode)
{
return ioctl(_fd, SPI_IOC_WR_MODE, &mode);
}
int SPI::setBitsPerWord(uint8_t bits)
{
return ioctl(_fd, SPI_IOC_WR_BITS_PER_WORD, &bits);
}
int SPI::setLSBFirst(bool enable)
{
uint8_t lsb = enable ? 1 : 0;
return ioctl(_fd, SPI_IOC_WR_LSB_FIRST, &lsb);
}
int SPI::writeThenRead(const uint8_t* tx, int txlen, uint8_t* rx, int rxlen)
{
struct spi_ioc_transfer trans[2] = {{0}, {0}};
trans[0].tx_buf = (unsigned long)tx;
trans[0].rx_buf = 0;
trans[0].len = txlen;
trans[1].tx_buf = 0;
trans[1].rx_buf = (unsigned long)rx;
trans[1].len = rxlen;
return ioctl(_fd, SPI_IOC_MESSAGE(2), trans);
}
}

41
hardware/SPI.h Normal file
View file

@ -0,0 +1,41 @@
#ifndef C9ED230E_05FF_4E41_87B9_664DF1FD835C
#define C9ED230E_05FF_4E41_87B9_664DF1FD835C
/*
*******************************************************************************
* INCLUDE
*******************************************************************************
*/
#include <stdint.h>
namespace Hardware
{
/*
*******************************************************************************
* CLASS DECLARE
*******************************************************************************
*/
class SPI
{
public:
static const uint8_t MODE_0;
static const uint8_t MODE_1;
static const uint8_t MODE_2;
static const uint8_t MODE_3;
public:
SPI(int bus, int cs);
~SPI();
public:
int setSpeed(uint32_t speed);
int setMode(uint8_t mode);
int setBitsPerWord(uint8_t bits);
int setLSBFirst(bool enable);
int writeThenRead(const uint8_t* tx, int txlen, uint8_t* rx, int rxlen);
private:
int _fd;
};
}
#endif

47
hardware/Sensor.cpp Normal file
View file

@ -0,0 +1,47 @@
/*
*******************************************************************************
* INCLUDE
*******************************************************************************
*/
#include "Sensor.h"
namespace Hardware
{
/*
*******************************************************************************
* CLASS DEFINE
*******************************************************************************
*/
Sensor::Sensor(Type type): _type(type)
{
}
Sensor::~Sensor()
{
}
void Sensor::read(float& value)
{
}
void Sensor::read(float values[])
{
}
Sensor::Type Sensor::type()
{
return _type;
}
SensorManager::SensorCollection SensorManager::_sensorCollection;
Sensor& SensorManager::getDefaultSensor(Sensor::Type type)
{
return *_sensorCollection[type][0];
}
void SensorManager::registerSensor(Sensor::Type type, Sensor::Ptr pSensor)
{
_sensorCollection[type].push_back(pSensor);
}
}

57
hardware/Sensor.h Normal file
View file

@ -0,0 +1,57 @@
#ifndef F49794A5_C3E3_4955_B9B2_6BAA267D3436
#define F49794A5_C3E3_4955_B9B2_6BAA267D3436
/*
*******************************************************************************
* INCLUDE
*******************************************************************************
*/
#include <Poco/SharedPtr.h>
#include <map>
#include <vector>
namespace Hardware
{
/*
*******************************************************************************
* CLASS DECLARE
*******************************************************************************
*/
class Sensor
{
public:
using Ptr = Poco::SharedPtr<Sensor>;
enum Type
{
TEMPERATURE,
};
public:
Sensor(Type type);
virtual ~Sensor();
public:
virtual void read(float& value);
virtual void read(float values[]);
Type type();
private:
Type _type;
};
class SensorManager
{
public:
static Sensor& getDefaultSensor(Sensor::Type type);
static void registerSensor(Sensor::Type type, Sensor::Ptr pSensor);
private:
using SensorList = std::vector<Sensor::Ptr>;
using SensorCollection = std::map<Sensor::Type, SensorList>;
private:
static SensorCollection _sensorCollection;
};
}
#endif

313
hardware/SerialPort.cpp Normal file
View file

@ -0,0 +1,313 @@
/*
*******************************************************************************
* INCLUDE
*******************************************************************************
*/
#include "SerialPort.h"
#include <fcntl.h>
#include <termios.h>
#include <stdio.h>
#include <unistd.h>
#include <Poco/Exception.h>
#include <Poco/Format.h>
namespace Hardware
{
/*
*******************************************************************************
* CLASS DEFINE
*******************************************************************************
*/
SerialPort::SerialPort(): _fd(-1)
{
}
SerialPort::~SerialPort()
{
if (isOpen())
close();
}
void SerialPort::open()
{
if (isOpen())
return;
if (_path.empty())
throw Poco::InvalidArgumentException("Empty path");
struct termios options = {0};
options.c_cflag |= CLOCAL | getBaudRateFlag(_baudRate);
switch (_dataBits)
{
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:
throw Poco::InvalidArgumentException(Poco::format("Invalid data bits: %d", _dataBits));
}
switch (_stopBits)
{
case 1:
options.c_cflag &= ~CSTOPB;
break;
case 2:
options.c_cflag |= CSTOPB;
break;
default:
case 3:
case 4:
throw Poco::InvalidArgumentException(Poco::format("Invalid stop bits: %d", _stopBits));
}
switch (_parity)
{
default:
case Parity::NONE:
options.c_cflag &= ~PARENB;
options.c_iflag &= ~INPCK;
break;
case Parity::ODD:
options.c_cflag |= PARENB;
options.c_cflag |= PARODD;
options.c_iflag |= INPCK;
options.c_iflag |= ISTRIP;
break;
case Parity::EVEN:
options.c_cflag |= PARENB;
options.c_cflag &= ~PARODD;
options.c_iflag |= INPCK;
options.c_iflag |= ISTRIP;
break;
}
options.c_cflag |= CREAD;
switch (_flowControl)
{
default:
case FlowControl::NONE:
options.c_cflag &= ~CRTSCTS;
break;
case FlowControl::RTSCTS:
options.c_cflag |= CRTSCTS;
break;
}
_fd = ::open(_path.c_str(), O_RDWR | O_NOCTTY | O_NONBLOCK);
if (_fd < 0)
throw Poco::InvalidAccessException(Poco::format("Open '%s' FAIL: %d", _path, _fd));
tcflush(_fd, TCIOFLUSH);
cfmakeraw(&options);
tcsetattr(_fd, TCSANOW, &options);
}
void SerialPort::close()
{
if (!isOpen())
return;
::close(_fd);
_fd = -1;
}
bool SerialPort::isOpen()
{
return _fd != -1;
}
int SerialPort::write(const void* pData, uint32_t size)
{
if (!isOpen())
return 0;
return ::write(_fd, pData, size);
}
int SerialPort::read(void* pBuffer, uint32_t length, uint32_t timeout)
{
if (!isOpen())
return 0;
struct timeval tm;
tm.tv_sec = timeout / 1000;
tm.tv_usec = (timeout % 1000) * 1000;
fd_set reads;
FD_ZERO(&reads);
FD_SET(_fd, &reads);
int rc = ::select(_fd + 1, &reads, NULL, NULL, &tm);
if (rc < 0)
return 0;
if (FD_ISSET(_fd, &reads))
return ::read(_fd, pBuffer, length);
return 0;
}
SerialPort& SerialPort::setPath(const std::string& path)
{
_path = path;
return *this;
}
SerialPort& SerialPort::setBaudRate(uint32_t baudRate)
{
_baudRate = baudRate;
return *this;
}
SerialPort& SerialPort::setParity(Parity parity)
{
_parity = parity;
return *this;
}
SerialPort& SerialPort::setDataBits(uint32_t bits)
{
_dataBits = bits;
return *this;
}
SerialPort& SerialPort::setStopBits(uint32_t bits)
{
_stopBits = bits;
return *this;
}
SerialPort& SerialPort::setFlowControl(FlowControl flowControl)
{
_flowControl = flowControl;
return *this;
}
uint32_t SerialPort::getBaudRateFlag(uint32_t br)
{
uint32_t flag;
switch (br)
{
case 0:
flag = B0;
break;
case 50:
flag = B50;
break;
case 75:
flag = B75;
break;
case 110:
flag = B110;
break;
case 134:
flag = B134;
break;
case 150:
flag = B150;
break;
case 200:
flag = B200;
break;
case 300:
flag = B300;
break;
case 600:
flag = B600;
break;
case 1200:
flag = B1200;
break;
case 1800:
flag = B1800;
break;
case 2400:
flag = B2400;
break;
case 4800:
flag = B4800;
break;
case 9600:
flag = B9600;
break;
case 19200:
flag = B19200;
break;
case 38400:
flag = B38400;
break;
case 57600:
flag = B57600;
break;
case 115200:
flag = B115200;
break;
case 230400:
flag = B230400;
break;
case 460800:
flag = B460800;
break;
case 500000:
flag = B500000;
break;
case 576000:
flag = B576000;
break;
case 921600:
flag = B921600;
break;
case 1000000:
flag = B1000000;
break;
case 1152000:
flag = B1152000;
break;
case 1500000:
flag = B1500000;
break;
case 2000000:
flag = B2000000;
break;
case 2500000:
flag = B2500000;
break;
case 3000000:
flag = B3000000;
break;
case 3500000:
flag = B3500000;
break;
case 4000000:
flag = B4000000;
break;
default:
flag = B9600;
break;
}
return flag;
}
}

101
hardware/SerialPort.h Normal file
View file

@ -0,0 +1,101 @@
#ifndef FA788814_8B75_424F_99DB_D04B4977DD8C
#define FA788814_8B75_424F_99DB_D04B4977DD8C
/*
*******************************************************************************
* INCLUDE
*******************************************************************************
*/
#include <Poco/Thread.h>
#include <atomic>
namespace Hardware
{
/*
*******************************************************************************
* CLASS DECLARE
*******************************************************************************
*/
class SerialPort
{
public:
enum class Parity
{
NONE,
ODD,
EVEN,
};
enum class FlowControl
{
NONE,
RTSCTS,
};
public:
SerialPort();
~SerialPort();
public:
SerialPort& setPath(const std::string& path);
SerialPort& setBaudRate(uint32_t baudRate);
SerialPort& setParity(Parity parity);
SerialPort& setDataBits(uint32_t bits);
SerialPort& setStopBits(uint32_t bits);
SerialPort& setFlowControl(FlowControl flowControl);
void open();
void close();
bool isOpen();
int write(const void* pData, uint32_t size);
int read(void* pBuffer, uint32_t length, uint32_t timeout);
const std::string& getPath();
uint32_t getBaudRate();
Parity getParity();
uint32_t getDataBits();
uint32_t getStopBits();
FlowControl getFlowControl();
protected:
uint32_t getBaudRateFlag(uint32_t baudRate);
private:
std::string _path;
int _fd;
uint32_t _baudRate;
Parity _parity;
uint32_t _dataBits;
uint32_t _stopBits;
FlowControl _flowControl;
};
inline const std::string& SerialPort::getPath()
{
return _path;
}
inline uint32_t SerialPort::getBaudRate()
{
return _baudRate;
}
inline SerialPort::Parity SerialPort::getParity()
{
return _parity;
}
inline uint32_t SerialPort::getDataBits()
{
return _dataBits;
}
inline uint32_t SerialPort::getStopBits()
{
return _stopBits;
}
inline SerialPort::FlowControl SerialPort::getFlowControl()
{
return _flowControl;
}
}
#endif

3
hardware/hardware.cmake Normal file
View file

@ -0,0 +1,3 @@
add_subdirectory(hardware)
include_directories(hardware)
target_link_libraries(${PROJNAME} PRIVATE hardware)

View file

@ -0,0 +1,42 @@
/*
*******************************************************************************
* INCLUDE
*******************************************************************************
*/
#include "../BoardInfo.h"
#include "Board.h"
#include <Poco/Format.h>
#include <Poco/MD5Engine.h>
#include <iostream>
#include <hi_mpi_sys.h>
namespace Hardware
{
namespace Hisilicon
{
/*
*******************************************************************************
* CLASS DEFINE
*******************************************************************************
*/
Board::Board()
{
hi_unique_id id;
hi_mpi_sys_get_unique_id(&id);
for (size_t i = 0; i < HI_UNIQUE_ID_NUM; i++)
{
std::cout << "#" << i << ": " << Poco::format("%08x", id.id[i]) << std::endl;
}
Poco::MD5Engine md5;
md5.update(id.id, sizeof(id.id));
_serialNumber = Poco::DigestEngine::digestToHex(md5.digest());
std::cout << "Serial Number: " << _serialNumber << std::endl;
}
std::string Hisilicon::Board::serialNumber()
{
return _serialNumber;
}
}
}

View file

@ -0,0 +1,33 @@
#ifndef A6A48FAD_A41E_4AF4_A92C_A3CFE3D065B1
#define A6A48FAD_A41E_4AF4_A92C_A3CFE3D065B1
/*
*******************************************************************************
* INCLUDE
*******************************************************************************
*/
#include "../BoardInfo.h"
namespace Hardware
{
namespace Hisilicon
{
/*
*******************************************************************************
* CLASS DECLARE
*******************************************************************************
*/
class Board: public Hardware::Board
{
public:
Board();
~Board();
public:
virtual std::string serialNumber() override;
private:
std::string _serialNumber;
};
}
}
#endif

View file

@ -0,0 +1,51 @@
/*
*******************************************************************************
* INCLUDE
*******************************************************************************
*/
#include "TemperatureSensor.h"
#include "memmap.h"
namespace Hardware
{
namespace Hisilicon
{
/*
*******************************************************************************
* CLASS DEFINE
*******************************************************************************
*/
#define TSENSOR_CTRL_REG_BASE 0x1102A000
TemperatureSensor::TemperatureSensor(): Sensor(Sensor::Type::TEMPERATURE)
{
_pRegisters = getRegisters();
_pRegisters->ctrl0 |= (1 << 31) | (1 << 30);
_pRegisters->ctrl1 |= (3 << 2);
}
TemperatureSensor::~TemperatureSensor()
{
_pRegisters->ctrl0 = 0;
releaseRegisters(_pRegisters);
}
void TemperatureSensor::read(float& value)
{
uint32_t rval = _pRegisters->ctrl2 & 0x3ff;
value = (float)(rval - 117) / 784 * 165 - 40;
}
TemperatureSensor::Registers* TemperatureSensor::getRegisters()
{
return (Registers*)memmap(TSENSOR_CTRL_REG_BASE, sizeof(Registers));
}
void TemperatureSensor::releaseRegisters(Registers* pRegisters)
{
memunmap(pRegisters);
}
}
}

View file

@ -0,0 +1,48 @@
#ifndef CBEF59CE_2DB7_43B5_AFA8_524952A27AF9
#define CBEF59CE_2DB7_43B5_AFA8_524952A27AF9
/*
*******************************************************************************
* INCLUDE
*******************************************************************************
*/
#include "../Sensor.h"
namespace Hardware
{
namespace Hisilicon
{
/*
*******************************************************************************
* CLASS DECLARE
*******************************************************************************
*/
class TemperatureSensor: public Sensor
{
protected:
struct Registers
{
uint32_t ctrl0;
uint32_t ctrl1;
uint32_t ctrl2;
uint32_t ctrl3;
uint32_t ctrl4;
uint32_t ctrl5;
};
public:
TemperatureSensor();
~TemperatureSensor();
public:
virtual void read(float& value) override;
protected:
Registers* getRegisters();
void releaseRegisters(Registers* pRegisters);
private:
Registers* _pRegisters;
};
}
}
#endif

275
hardware/hisilicon/memmap.c Normal file
View file

@ -0,0 +1,275 @@
/************************************************************/
/* file name : memmap.c */
/* linux /dev/mem mmap support func */
/* */
/* */
/* Copyright 2005 huawei com. */
/* Author :zhouaidi(42136) */
/* Create date: 2005-04-07 */
/* Modify history */
/* 2005-12-21: add memunmap function */
/************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <errno.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/mman.h>
typedef struct tag_MMAP_Node
{
unsigned long Start_P;
unsigned long Start_V;
unsigned long length;
unsigned long refcount;
struct tag_MMAP_Node * next;
}TMMAP_Node_t;
TMMAP_Node_t * pTMMAPNode = NULL;
#define PAGE_SIZE 0x1000
#define PAGE_SIZE_MASK (~(0xfff))
static int fd = -1;
static const char dev[]="/dev/mem";
/*
*******************************************************************************
* MACROS
*******************************************************************************
*/
#define WRITE_LOG_DEBUG(fmt, ...) printf("%05d:[debug] "fmt"\r\n", __LINE__, ##__VA_ARGS__)
#define WRITE_LOG_INFO(fmt, ...) printf("%05d:[info ] "fmt"\r\n", __LINE__, ##__VA_ARGS__)
#define WRITE_LOG_ERROR(fmt, ...) printf("%05d:[error] "fmt"\r\n", __LINE__, ##__VA_ARGS__)
/* no need considering page_size of 4K */
void * memmap(unsigned long phy_addr, unsigned long size)
{
unsigned long phy_addr_in_page;
unsigned long page_diff;
unsigned long size_in_page;
TMMAP_Node_t * pTmp;
TMMAP_Node_t * pNew;
void *addr=NULL;
if(size == 0)
{
WRITE_LOG_ERROR("memmap():size can't be zero!\n");
return NULL;
}
/* check if the physical memory space have been mmaped */
pTmp = pTMMAPNode;
while(pTmp != NULL)
{
if( (phy_addr >= pTmp->Start_P) &&
( (phy_addr + size) <= (pTmp->Start_P + pTmp->length) ) )
{
pTmp->refcount++; /* referrence count increase by 1 */
return (void *)(pTmp->Start_V + phy_addr - pTmp->Start_P);
}
pTmp = pTmp->next;
}
/* not mmaped yet */
if(fd < 0)
{
/* dev not opened yet, so open it */
fd = open (dev, O_RDWR | O_SYNC);
if (fd < 0)
{
WRITE_LOG_ERROR("memmap():open %s error!\n", dev);
return NULL;
}
}
/* addr align in page_size(4K) */
phy_addr_in_page = phy_addr & PAGE_SIZE_MASK;
page_diff = phy_addr - phy_addr_in_page;
/* size in page_size */
size_in_page =((size + page_diff - 1) & PAGE_SIZE_MASK) + PAGE_SIZE;
addr = mmap((void *)0, size_in_page, PROT_READ|PROT_WRITE, MAP_SHARED, fd, phy_addr_in_page);
if (addr == MAP_FAILED)
{
WRITE_LOG_ERROR("memmap():mmap @ 0x%x error!\n", phy_addr_in_page);
return NULL;
}
/* add this mmap to MMAP Node */
pNew = (TMMAP_Node_t *)malloc(sizeof(TMMAP_Node_t));
if(NULL == pNew)
{
WRITE_LOG_ERROR("memmap():malloc new node failed!\n");
return NULL;
}
pNew->Start_P = phy_addr_in_page;
pNew->Start_V = (unsigned long)addr;
pNew->length = size_in_page;
pNew->refcount = 1;
pNew->next = NULL;
if(pTMMAPNode == NULL)
{
pTMMAPNode = pNew;
}
else
{
pTmp = pTMMAPNode;
while(pTmp->next != NULL)
{
pTmp = pTmp->next;
}
pTmp->next = pNew;
}
return (void *)(addr+page_diff);
}
/*****************************************************************************
Prototype : memunmap
Description :
Input : void * addr_mapped
Output : None
Return Value : On success, returns 0, on failure -1
Calls :
Called By :
History :
1.Date : 2005/12/21
Author : Z42136
Modification : Created function
*****************************************************************************/
int memunmap(void * addr_mapped)
{
TMMAP_Node_t * pPre;
TMMAP_Node_t * pTmp;
if(pTMMAPNode == NULL)
{
WRITE_LOG_ERROR("memunmap(): address have not been mmaped!\n");
return -1;
}
/* check if the physical memory space have been mmaped */
pTmp = pTMMAPNode;
pPre = pTMMAPNode;
do
{
if( ((unsigned long)addr_mapped >= pTmp->Start_V) &&
((unsigned long)addr_mapped <= (pTmp->Start_V + pTmp->length)) )
{
pTmp->refcount--; /* referrence count decrease by 1 */
if(0 == pTmp->refcount)
{
// WRITE_LOG_INFO("memunmap(): map node will be remove!\n");
/* delete this map node from pMMAPNode */
if(pTmp == pTMMAPNode)
{
pTMMAPNode = NULL;
}
else
{
pPre->next = pTmp->next;
}
/* munmap */
if(munmap((void *)pTmp->Start_V, pTmp->length) != 0 )
{
WRITE_LOG_INFO("memunmap(): munmap failed!\n");
}
free(pTmp);
}
return 0;
}
pPre = pTmp;
pTmp = pTmp->next;
}while(pTmp != NULL);
WRITE_LOG_ERROR("memunmap(): address have not been mmaped!\n");
return -1;
}
void * mmapfile(char* fn, unsigned long size)
{
int fd;
void * pShm = NULL;
if (fn == NULL)
{
return NULL;
}
/* Open the file. */
fd = open (fn, O_RDWR | O_CREAT, S_IRWXU | S_IRWXG | O_SYNC);
if (fd == -1)
{
WRITE_LOG_DEBUG("Open file %s error: %i.\n",fn, errno );
return NULL;
}
/* Create the memory-mapping. */
pShm = mmap(0, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
close (fd);
if (pShm != NULL)
{
WRITE_LOG_DEBUG("Memory Map OK. Map File: %s, length:%#X, addr:0x%08lX",
fn, size, (unsigned long)pShm);
}
else
{
WRITE_LOG_DEBUG("Memory Map Failure. Map File %s, length:0x%08lX",
fn, size);
}
return pShm;
}
void * mmapfile_r(char* fn, unsigned long size)
{
int fd;
void * pShm = NULL;
if (fn == NULL)
{
return NULL;
}
/* Open the file. */
fd = open (fn, O_RDONLY | O_SYNC);
if (fd == -1)
{
WRITE_LOG_INFO("Open file %s error: %d.\n",fn, errno );
return NULL;
}
/* Create the memory-mapping. */
pShm = mmap(0, size, PROT_READ, MAP_SHARED, fd, 0);
close (fd);
if (pShm != NULL)
{
WRITE_LOG_DEBUG("Memory Map OK. Map File: %s, length:%#X, addr:0x%08lX\n",
fn, size, (unsigned long)pShm);
}
else
{
WRITE_LOG_DEBUG("Memory Map Failure. Map File %s, length:0x%08lX\n",
fn, size);
}
return pShm;
}

View file

@ -0,0 +1,33 @@
/* file name : memmap.h */
/* linux /dev/mem mmap support head file */
/* */
/* */
/* Copyright 2005 huawei com. */
/* Author :zhouaidi(42136) */
/* Create date: 2005-04-07 */
/* Modify history */
/* */
#ifndef __MEM_MAP_H__
#define __MEM_MAP_H__
#ifdef __cplusplus
#if __cplusplus
extern "C"{
#endif
#endif /* __cplusplus */
extern void * memmap(unsigned long phy_addr, unsigned long size);
extern int memunmap(void * addr_mapped);
extern void * mmapfile(char* fn, unsigned int size);
extern void * mmapfile_r(char* fn, unsigned int size);
#ifdef __cplusplus
#if __cplusplus
}
#endif
#endif /* __cplusplus */
#endif

75
main.cpp Normal file
View file

@ -0,0 +1,75 @@
/*
*******************************************************************************
* INCLUDE
*******************************************************************************
*/
#include "Application.h"
#ifdef USING_HARDWARE_SUBSYSTEM
#include "HardwareSubsystem.h"
#endif
#ifdef USING_NETWORK_SUBSYSTEM
#include "NetworkSubsystem.h"
#endif
#ifdef USING_GRAPHICS_SUBSYSTEM
#include "GraphicsSubsystem.h"
#endif
#ifdef WINDOWS_GUI_APPLICATION
#include <Windows.h>
#include <tchar.h>
#endif
#ifdef USING_VIDEO_SUBSYSTEM
#include "VideoSubsystem.h"
#endif
/*
*******************************************************************************
* FUNCTIONS
*******************************************************************************
*/
#ifdef WINDOWS_GUI_APPLICATION
int WINAPI _tWinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance, _In_ LPSTR lpCmdLine,
_In_ INT nCmdShow)
#else
int main()
#endif
{
Application app;
#ifdef USING_HARDWARE_SUBSYSTEM
app.addSubsystem(new Hardware::HardwareSubsystem());
#endif
#ifdef USING_NETWORK_SUBSYSTEM
app.addSubsystem(new Network::NetworkSubsystem());
#endif
#ifdef USING_GRAPHICS_SUBSYSTEM
app.addSubsystem(new Graphics::GraphicsSubsystem());
#endif
#ifdef USING_VIDEO_SUBSYSTEM
app.addSubsystem(new Video::VideoSubsystem());
#endif
int rc = 0;
try
{
rc = app.run();
}
catch (const std::exception& e)
{
std::cout << e.what() << std::endl;
}
catch (...)
{
std::cout << "Unknown exception" << std::endl;
}
return rc;
}

6
network/CMakeLists.txt Normal file
View file

@ -0,0 +1,6 @@
set(target network)
aux_source_directory(. NETWORK_SOURCES)
add_library(${target} STATIC
${NETWORK_SOURCES}
)

53
network/HTTPServer.cpp Normal file
View file

@ -0,0 +1,53 @@
/*
*******************************************************************************
* INCLUDE
*******************************************************************************
*/
#include "HTTPServer.h"
using namespace Poco::Net;
namespace Network
{
class RouterRequestHandlerFactory: public HTTPRequestHandlerFactory
{
public:
HTTPRequestHandler* createRequestHandler(const HTTPServerRequest& request) override
{
throw Poco::NotFoundException("Not found");
}
};
/*
*******************************************************************************
* CLASS DEFINE
*******************************************************************************
*/
std::string HTTPServer::HOST = "host";
std::string HTTPServer::PORT = "port";
HTTPServer::HTTPServer(): _pHTTPServer(nullptr)
{
setParameter(HOST, std::string("::"));
setParameter(PORT, 80);
}
HTTPServer::~HTTPServer()
{
}
void HTTPServer::start()
{
Poco::Net::SocketAddress address(getParameter<std::string>(HOST), getParameter<int>(PORT));
Poco::Net::ServerSocket socket(address);
Poco::Net::HTTPServerParams* pParams = new Poco::Net::HTTPServerParams();
pParams->setKeepAlive(false);
_pHTTPServer = new Poco::Net::HTTPServer(new RouterRequestHandlerFactory, socket, pParams);
_pHTTPServer->start();
}
void HTTPServer::stop()
{
_pHTTPServer->stopAll();
}
}

37
network/HTTPServer.h Normal file
View file

@ -0,0 +1,37 @@
#ifndef DC9BEC60_0C80_41F2_B1F7_289C63388DC1
#define DC9BEC60_0C80_41F2_B1F7_289C63388DC1
/*
*******************************************************************************
* INCLUDE
*******************************************************************************
*/
#include "Poco/Net/HTTPServer.h"
#include "Poco/SharedPtr.h"
#include "util/Configuration.h"
namespace Network
{
/*
*******************************************************************************
* CLASS DECLARE
*******************************************************************************
*/
class HTTPServer: public Util::Configuration
{
public:
static std::string HOST;
static std::string PORT;
public:
HTTPServer();
~HTTPServer();
void start();
void stop();
private:
Poco::SharedPtr<Poco::Net::HTTPServer> _pHTTPServer;
};
}
#endif

View file

@ -0,0 +1,40 @@
/*
*******************************************************************************
* INCLUDE
*******************************************************************************
*/
#include "NetworkSubsystem.h"
#include "Application.h"
#include "Logger.h"
#include "Poco/Exception.h"
#include "Poco/Net/DNS.h"
namespace Network
{
/*
*******************************************************************************
* CLASS DEFINE
*******************************************************************************
*/
NetworkSubsystem::NetworkSubsystem()
{
}
NetworkSubsystem::~NetworkSubsystem()
{
}
const char* NetworkSubsystem::name() const
{
return "Network";
}
void NetworkSubsystem::initialize(Poco::Util::Application& app)
{
}
void NetworkSubsystem::uninitialize()
{
stopAll();
}
}

View file

@ -0,0 +1,47 @@
#ifndef __NETWORK_SUBSYSTEM__H__
#define __NETWORK_SUBSYSTEM__H__
/*
*******************************************************************************
* INCLUDE
*******************************************************************************
*/
#include "Poco/Util/Application.h"
#include "util/Subsystem.h"
namespace Network
{
/*
*******************************************************************************
* CLASS DECLARE
*******************************************************************************
*/
class NetworkSubsystem: public Util::Subsystem
{
public:
NetworkSubsystem();
~NetworkSubsystem();
virtual const char* name() const override;
protected:
virtual void initialize(Poco::Util::Application& app);
virtual void uninitialize();
public:
struct ZLStreamPushMessage
{
std::string appname;
std::string streamid;
short type;
ZLStreamPushMessage(const std::string& appname, const std::string& streamid, short type)
: appname(appname), streamid(streamid), type(type)
{
}
};
Poco::BasicEvent<ZLStreamPushMessage> ZLStreamPushMessageEvent;
private:
};
}
#endif

148
network/TCPClient.cpp Normal file
View file

@ -0,0 +1,148 @@
/*
*******************************************************************************
* INCLUDE
*******************************************************************************
*/
#include "TCPClient.h"
#include <Poco/Net/NetException.h>
namespace Network
{
TCPClient::TCPClient(std::string host, int port, Poco::Timespan timeout, uint32_t sz)
: Poco::Task("TCPClient"), _address(host, port), _timeout(timeout), _bufsz(sz), _pBuffer(new uint8_t[sz])
{
}
TCPClient::~TCPClient()
{
}
int TCPClient::send(const uint8_t* data, uint32_t len)
{
try
{
int rc = _socket.sendBytes(data, len);
return rc;
}
catch (const std::exception& e)
{
}
return 0;
}
void TCPClient::start()
{
if (!_thread.isRunning())
_thread.start(*this);
}
void TCPClient::stop()
{
if (_thread.isRunning())
{
cancel();
_thread.join();
}
}
void TCPClient::setStateChangedCallback(const StateChangedCallback& callback)
{
_stateChanged = std::move(callback);
}
void TCPClient::setDataIncomingCallback(const DataIncomingCallback& callback)
{
_dataIncoming = std::move(callback);
}
bool TCPClient::connect()
{
try
{
_socket.connect(_address, _timeout);
_socket.setReceiveTimeout(Poco::Timespan(1000));
setState(State::CONNECTED, "Connected");
return true;
}
catch (Poco::TimeoutException& e)
{
setState(State::CONNECT_TIMEOUT, e.displayText());
}
catch (Poco::Net::ConnectionRefusedException& e)
{
setState(State::CONNECT_REFUSED, e.displayText());
}
catch (Poco::Exception& e)
{
setState(State::CONNECT_REFUSED, e.displayText());
}
return false;
}
void TCPClient::runTask()
{
while (!isCancelled())
{
while (!isCancelled())
{
setState(State::CONNECTING, "Connecting");
if (connect())
break;
Poco::Thread::sleep(1000);
}
while (!isCancelled())
{
try
{
int rc = _socket.receiveBytes(_pBuffer, _bufsz);
if (rc <= 0)
{
setState(State::DISCONNECTED, "Connection lost");
_socket.close();
break;
}
receive(_pBuffer, rc);
}
catch (Poco::TimeoutException& e)
{
}
catch (Poco::Exception& e)
{
setState(State::DISCONNECTED, "Connection lost");
_socket.close();
break;
}
}
Poco::Thread::sleep(1000);
}
try
{
_socket.close();
}
catch (...)
{
}
}
void TCPClient::setState(State state, const std::string& desc)
{
if (_stateChanged)
_stateChanged(state, desc);
}
void TCPClient::receive(const uint8_t* data, uint32_t len)
{
if (_dataIncoming)
_dataIncoming(data, len);
}
}

65
network/TCPClient.h Normal file
View file

@ -0,0 +1,65 @@
#ifndef __TCP_CLIENT_H__
#define __TCP_CLIENT_H__
/*
*******************************************************************************
* INCLUDE
*******************************************************************************
*/
#include <Poco/Net/StreamSocket.h>
#include <Poco/Task.h>
#include <Poco/Timespan.h>
#include <functional>
/*
*******************************************************************************
* CLASS DECLARE
*******************************************************************************
*/
namespace Network
{
class TCPClient: public Poco::Task
{
public:
enum class State
{
DISCONNECTED,
CONNECT_TIMEOUT,
CONNECT_REFUSED,
CONNECTING,
CONNECTED,
};
using StateChangedCallback = std::function<void(State, const std::string&)>;
using DataIncomingCallback = std::function<void(const uint8_t*, uint32_t)>;
public:
TCPClient(std::string host, int port, Poco::Timespan timeout, uint32_t sz = 10240);
~TCPClient();
int send(const uint8_t* data, uint32_t len);
void start();
void stop();
void setStateChangedCallback(const StateChangedCallback& callback);
void setDataIncomingCallback(const DataIncomingCallback& callback);
protected:
bool connect();
void receive(const uint8_t* data, uint32_t len);
virtual void runTask() override;
void setState(State state, const std::string& desc);
private:
Poco::Thread _thread;
Poco::Net::StreamSocket _socket;
Poco::Net::SocketAddress _address;
Poco::Timespan _timeout;
uint32_t _bufsz;
Poco::SharedPtr<uint8_t> _pBuffer;
StateChangedCallback _stateChanged;
DataIncomingCallback _dataIncoming;
};
}
#endif

3
network/network.cmake Normal file
View file

@ -0,0 +1,3 @@
add_subdirectory(network)
include_directories(network)
target_link_libraries(${PROJNAME} PRIVATE network)

57
util/AESUtil.h Normal file
View file

@ -0,0 +1,57 @@
#ifndef A89D0C3C_4F05_4631_A720_D8B2CEA5E99A
#define A89D0C3C_4F05_4631_A720_D8B2CEA5E99A
#include <Poco/Base64Decoder.h>
#include <Poco/Base64Encoder.h>
#include <Poco/Crypto/Cipher.h>
#include <Poco/Crypto/CipherFactory.h>
#include <Poco/Crypto/CipherKey.h>
#include <Poco/StreamCopier.h>
#include <iostream>
#include <memory>
#include <sstream>
#include <string>
#include <vector>
#include "Logger.h"
namespace Util
{
class AESUtil
{
public:
static std::string encryptAES(const std::string& plaintext, const std::string& key, const std::string& iv)
{
try
{
Poco::Crypto::CipherKey cipherKey("aes-256-cbc", key, iv);
Poco::Crypto::CipherFactory& factory = Poco::Crypto::CipherFactory::defaultFactory();
std::unique_ptr<Poco::Crypto::Cipher> pCipher(factory.createCipher(cipherKey));
std::string ciphertext = pCipher->encryptString(plaintext, Poco::Crypto::Cipher::ENC_BASE64_NO_LF);
return ciphertext;
}
catch (const Poco::Exception& e)
{
logger().error("encryptAES error: %s", std::string(e.what()));
return "";
}
}
static std::string decryptAES(const std::string& ciphertext, const std::string& key, const std::string& iv)
{
try
{
Poco::Crypto::CipherKey cipherKey("aes-256-cbc", key, iv);
Poco::Crypto::CipherFactory& factory = Poco::Crypto::CipherFactory::defaultFactory();
std::unique_ptr<Poco::Crypto::Cipher> pCipher(factory.createCipher(cipherKey));
std::string plaintext = pCipher->decryptString(ciphertext, Poco::Crypto::Cipher::ENC_BASE64_NO_LF);
return plaintext;
}
catch (const Poco::Exception& e)
{
logger().error("decryptAES error: %s", std::string(e.what()));
return "";
}
}
};
}
#endif

35
util/Base64Util.h Normal file
View file

@ -0,0 +1,35 @@
#ifndef CFCB972C_1202_49D2_AAD4_9066BA17F94F
#define CFCB972C_1202_49D2_AAD4_9066BA17F94F
#include <iostream>
#include <sstream>
#include <string>
#include "Poco/Base64Decoder.h"
#include "Poco/Base64Encoder.h"
#include "Poco/StreamCopier.h"
namespace Util
{
class Base64Util
{
private:
public:
static std::string base64Encode(const std::string& input)
{
std::ostringstream oss;
Poco::Base64Encoder encoder(oss, 0);
encoder.write(input.data(), input.size());
encoder.close();
return oss.str();
}
static std::string base64Decode(const std::string& input)
{
std::istringstream iss(input);
Poco::Base64Decoder decoder(iss);
std::ostringstream oss;
Poco::StreamCopier::copyStream(decoder, oss);
return oss.str();
}
};
}
#endif

144
util/Broadcast.cpp Normal file
View file

@ -0,0 +1,144 @@
/*
*******************************************************************************
* INCLUDE
*******************************************************************************
*/
#include "Broadcast.h"
#include <Poco/Mutex.h>
#include <Poco/ScopedLock.h>
namespace Util
{
/*
*******************************************************************************
* CLASS DEFINE
*******************************************************************************
*/
class BroadcastObserver
{
public:
BroadcastObserver(BroadcastReceiver& receiver, IntentFilter::Ptr pFilter): _pReceiver(&receiver), _pFilter(pFilter)
{
}
void broadcast(Intent::Ptr pIntent)
{
if (_pFilter->match(pIntent))
_pReceiver->onReceive(pIntent);
}
bool equals(const BroadcastObserver& observer)
{
return _pReceiver == observer._pReceiver;
}
bool isBoundTo(BroadcastReceiver& receiver)
{
return _pReceiver == &receiver;
}
private:
BroadcastReceiver* _pReceiver;
IntentFilter::Ptr _pFilter;
};
class BroadcastCore
{
public:
void sendBroadcast(Intent::Ptr pIntent)
{
Poco::ScopedLockWithUnlock<Poco::Mutex> lock(_mutex);
BroadcastObserverList list(_broadcastObservers);
lock.unlock();
for (auto& p : list)
p->broadcast(pIntent);
}
void registerReceiver(BroadcastReceiver& receiver, IntentFilter::Ptr pFilter)
{
Poco::Mutex::ScopedLock lock(_mutex);
for (auto& obs : _broadcastObservers)
{
if (obs->isBoundTo(receiver))
return;
}
_broadcastObservers.push_back(new BroadcastObserver(receiver, pFilter));
}
void unregisterReceiver(BroadcastReceiver& receiver)
{
Poco::Mutex::ScopedLock lock(_mutex);
for (BroadcastObserverList::iterator it = _broadcastObservers.begin(); it != _broadcastObservers.end();)
{
if ((*it)->isBoundTo(receiver))
it = _broadcastObservers.erase(it);
else
++it;
}
}
private:
using BroadcastObserverPtr = Poco::SharedPtr<BroadcastObserver>;
using BroadcastObserverList = std::vector<BroadcastObserverPtr>;
BroadcastObserverList _broadcastObservers;
mutable Poco::Mutex _mutex;
};
/*
*******************************************************************************
* CLASS DEFINE
*******************************************************************************
*/
Intent& Intent::putExtra(const std::string& key, const char* value)
{
return putExtra(key, std::string(value));
}
/*
*******************************************************************************
* CLASS DEFINE
*******************************************************************************
*/
void IntentFilter::addAction(const std::string& action)
{
_actions.push_back(action);
}
bool IntentFilter::match(Intent::Ptr pIntent) const
{
const std::string& target = pIntent->getAction();
return std::find(_actions.begin(), _actions.end(), target) != _actions.end();
}
/*
*******************************************************************************
* LOCAL VARIABLES
*******************************************************************************
*/
namespace
{
static BroadcastCore core;
}
/*
*******************************************************************************
* CLASS DEFINE
*******************************************************************************
*/
void BroadcastSender::sendBroadcast(Intent::Ptr pIntent)
{
core.sendBroadcast(pIntent);
}
void BroadcastReceiverRegistry::registerReceiver(BroadcastReceiver& receiver, IntentFilter::Ptr pFilter)
{
core.registerReceiver(receiver, pFilter);
}
void BroadcastReceiverRegistry::unregisterReceiver(BroadcastReceiver& receiver)
{
core.unregisterReceiver(receiver);
}
}

117
util/Broadcast.h Normal file
View file

@ -0,0 +1,117 @@
#ifndef C81E1B2C_54AF_4E7D_800B_F21E0DACFD8B
#define C81E1B2C_54AF_4E7D_800B_F21E0DACFD8B
/*
*******************************************************************************
* INCLUDE
*******************************************************************************
*/
#include <unordered_map>
#include <vector>
#include <Poco/Format.h>
#include <Poco/SharedPtr.h>
#include <Poco/Any.h>
namespace Util
{
/*
*******************************************************************************
* CLASS DECLARE
*******************************************************************************
*/
class Intent
{
public:
using Ptr = Poco::SharedPtr<Intent>;
template <class T> Intent& putExtra(const std::string& key, const T& value);
Intent& putExtra(const std::string& key, const char* value);
template <class T> const T getExtra(const std::string& key) const;
const std::string& getAction() const;
void setAction(const std::string& action);
private:
using Value = Poco::Any;
using Values = std::unordered_map<std::string, Value>;
Values _values;
std::string _action;
};
template <class T> inline Intent& Intent::putExtra(const std::string& key, const T& value)
{
_values[key] = value;
return *this;
}
template <class T> inline const T Intent::getExtra(const std::string& key) const
{
if (_values.find(key) == _values.end())
throw Poco::NotFoundException(Poco::format("Intent: Can't find value: %s", key), -ENODATA);
return Poco::AnyCast<T>(_values.at(key));
}
inline const std::string& Intent::getAction() const
{
return _action;
}
inline void Intent::setAction(const std::string& action)
{
_action = action;
}
/*
*******************************************************************************
* CLASS DECLARE
*******************************************************************************
*/
class IntentFilter
{
public:
using Ptr = Poco::SharedPtr<IntentFilter>;
void addAction(const std::string& action);
bool match(Intent::Ptr pIntent) const;
private:
std::vector<std::string> _actions;
};
/*
*******************************************************************************
* CLASS DECLARE
*******************************************************************************
*/
class BroadcastReceiver
{
public:
virtual void onReceive(Intent::Ptr pIntent) = 0;
};
/*
*******************************************************************************
* CLASS DECLARE
*******************************************************************************
*/
class BroadcastSender
{
public:
void sendBroadcast(Intent::Ptr pIntent);
};
/*
*******************************************************************************
* CLASS DECLARE
*******************************************************************************
*/
class BroadcastReceiverRegistry
{
public:
void registerReceiver(BroadcastReceiver& receiver, IntentFilter::Ptr pFilter);
void unregisterReceiver(BroadcastReceiver& receiver);
};
}
#endif

15
util/BuilderSet.cpp Normal file
View file

@ -0,0 +1,15 @@
/*
*******************************************************************************
* INCLUDE
*******************************************************************************
*/
#include "BuilderSet.h"
namespace Util
{
/*
*******************************************************************************
* CLASS DEFINE
*******************************************************************************
*/
}

63
util/BuilderSet.h Normal file
View file

@ -0,0 +1,63 @@
#ifndef BE7ED734_3D5C_4600_89C3_4082EB263BE0
#define BE7ED734_3D5C_4600_89C3_4082EB263BE0
/*
*******************************************************************************
* INCLUDE
*******************************************************************************
*/
#include <Poco/SharedPtr.h>
#include <map>
namespace Util
{
/*
*******************************************************************************
* CLASS DECLARE
*******************************************************************************
*/
template <class T, class TKey, class TSelector, class... TArgs> class BuilderSet
{
private:
class IBuilder
{
public:
using Ptr = Poco::SharedPtr<IBuilder>;
virtual ~IBuilder()
{
}
virtual T* build(TArgs... args) = 0;
};
template <class C> class Builder: public IBuilder
{
public:
C* build(TArgs... args) override
{
C* p = new C(args...);
return p;
}
};
using Builders = std::map<TKey, typename IBuilder::Ptr>;
public:
static T* build(TArgs... args)
{
TKey key = TSelector::select(args...);
return _builders[key]->build(args...);
}
template <class C> static void registerClass(TKey key)
{
_builders[key] = new Builder<C>();
}
private:
static Builders _builders;
};
template <class T, class TKey, class TSelect, class... TArgs>
typename BuilderSet<T, TKey, TSelect, TArgs...>::Builders BuilderSet<T, TKey, TSelect, TArgs...>::_builders;
}
#endif

5
util/CMakeLists.txt Normal file
View file

@ -0,0 +1,5 @@
set(target util)
aux_source_directory(. UTIL_SOURCES)
add_library(${target} STATIC ${UTIL_SOURCES} )

28
util/ClassAssistant.cpp Normal file
View file

@ -0,0 +1,28 @@
/*
*******************************************************************************
* INCLUDE
*******************************************************************************
*/
#include "ClassAssistant.h"
namespace Util
{
/*
*******************************************************************************
* CLASS DEFINE
*******************************************************************************
*/
std::string Util::ClassAssistant::demangle(const std::type_info& ti)
{
int status;
char* demangled = abi::__cxa_demangle(ti.name(), nullptr, nullptr, &status);
if (status == 0)
{
std::string result(demangled);
free(demangled);
return result;
}
return ti.name();
}
}

68
util/ClassAssistant.h Normal file
View file

@ -0,0 +1,68 @@
#ifndef AD5404F9_7A8A_45FF_8ABE_66F7E9E53212
#define AD5404F9_7A8A_45FF_8ABE_66F7E9E53212
/*
*******************************************************************************
* INCLUDE
*******************************************************************************
*/
#include <cxxabi.h>
#include <string>
#include <typeinfo>
/*
*******************************************************************************
* MACROS
*******************************************************************************
*/
#define PROPERTY_DECLARE(name, type, defaultValue, setAccessibility, getAccessibility) \
private: \
type _##name = defaultValue; \
setAccessibility: \
void set##name(const type& value) \
{ \
_##name = value; \
} \
getAccessibility: \
type get##name() \
{ \
return _##name; \
}
#define READONLY_PROPERTY_DECLARE(name, type) \
private: \
type _##name; \
\
public: \
const type& name() \
{ \
return _##name; \
}
namespace Util
{
/*
*******************************************************************************
* FUNCTIONS
*******************************************************************************
*/
class ClassAssistant
{
public:
template <typename T> static std::string demangle();
template <typename T> static std::string demangle(const T& object);
static std::string demangle(const std::type_info& ti);
};
template <typename T> std::string ClassAssistant::demangle()
{
return demangle(typeid(T));
}
template <typename T> inline std::string ClassAssistant::demangle(const T& object)
{
return demangle(typeid(object));
}
}
#endif

26
util/Configurable.cpp Normal file
View file

@ -0,0 +1,26 @@
/*
*******************************************************************************
* INCLUDE
*******************************************************************************
*/
#include "Configurable.h"
namespace Util
{
/*
*******************************************************************************
* CLASS DEFINE
*******************************************************************************
*/
void Configurable::commit()
{
ParameterNames names;
config().getChangedParameters(names);
if (names.size())
{
onConfigChanged(names);
for (auto& name : names)
config().confirmParameterChanged(name);
}
}
}

55
util/Configurable.h Normal file
View file

@ -0,0 +1,55 @@
#ifndef E6D074E1_FEDB_4EFB_902B_95FF6BD2EB09
#define E6D074E1_FEDB_4EFB_902B_95FF6BD2EB09
/*
*******************************************************************************
* INCLUDE
*******************************************************************************
*/
#include "Configuration.h"
#include <unordered_set>
/*
*******************************************************************************
* MACROS
*******************************************************************************
*/
#define CONFIGURABLE_DECLARE_PARAMETER(name, type) \
type get##name() \
{ \
return config().getParameter<type>(#name); \
} \
void set##name(const type value) \
{ \
config().setParameter(#name, value); \
} \
const std::string _##name = (#name);
namespace Util
{
/*
*******************************************************************************
* CLASS DECLARE
*******************************************************************************
*/
class Configurable
{
public:
using ParameterNames = Configuration::ParameterNames;
public:
void commit();
Configuration& config();
protected:
virtual void onConfigChanged(ParameterNames& names) = 0;
private:
Configuration _config;
};
inline Configuration& Configurable::config()
{
return _config;
}
}
#endif

33
util/Configuration.cpp Normal file
View file

@ -0,0 +1,33 @@
/*
*******************************************************************************
* INCLUDE
*******************************************************************************
*/
#include "Configuration.h"
namespace Util
{
/*
*******************************************************************************
* CLASS DEFINE
*******************************************************************************
*/
bool Configuration::ParameterNames::exists(std::string name) const
{
return find(name) != end();
}
/*
*******************************************************************************
* CLASS DEFINE
*******************************************************************************
*/
Configuration::~Configuration()
{
}
void Configuration::copyTo(Configuration& other)
{
other._parameters = _parameters;
}
}

140
util/Configuration.h Normal file
View file

@ -0,0 +1,140 @@
#ifndef __CONFIG_CONFIGURATION_H__
#define __CONFIG_CONFIGURATION_H__
/*
*******************************************************************************
* INCLUDE
*******************************************************************************
*/
#include <iostream>
#include <map>
#include <unordered_set>
#include "Poco/Any.h"
#include "Poco/Exception.h"
#include "Poco/Format.h"
namespace Util
{
/*
*******************************************************************************
* CLASS DECLARE
*******************************************************************************
*/
class Configuration
{
private:
class Parameter
{
public:
template <class T> T getValue()
{
return Poco::AnyCast<T>(_value);
}
void setValue(const Poco::Any& value)
{
this->_value = value;
}
void setChanged(bool changed)
{
this->_changed = changed;
}
bool getChanged()
{
return _changed;
}
bool empty()
{
return _value.empty();
}
Poco::Any& value()
{
return _value;
}
private:
Poco::Any _value;
bool _changed;
};
public:
class ParameterNames: public std::unordered_set<std::string>
{
public:
bool exists(std::string name) const;
};
public:
virtual ~Configuration();
template <class T> Configuration& setParameter(const std::string& name, const T& value)
{
Parameter& parameter = _parameters[name];
if (parameter.empty() || parameter.getValue<T>() != value)
{
parameter.setChanged(true);
parameter.setValue(value);
}
return *this;
}
template <class T> T getParameter(const std::string& name)
{
return findParameter(name).getValue<T>();
}
bool isParameterChanged(const std::string& name)
{
return findParameter(name).getChanged();
}
void confirmParameterChanged(const std::string& name)
{
findParameter(name).setChanged(false);
}
void getChangedParameters(ParameterNames& names)
{
names.clear();
for (auto& p : _parameters)
{
if (p.second.getChanged())
names.insert(p.first);
}
}
const std::string& name() const
{
return _name;
}
void copyTo(Configuration& other);
protected:
void setName(std::string& name)
{
_name = name;
}
Parameter& findParameter(const std::string& name)
{
if (_parameters.find(name) == _parameters.end())
throw Poco::NotFoundException(Poco::format("Can't find parameter: %s", name), -ENODATA);
return _parameters[name];
}
private:
using Parameters = std::map<std::string, Parameter>;
Parameters _parameters;
std::string _name;
};
}
#endif

467
util/DateTimeUtils.h Normal file
View file

@ -0,0 +1,467 @@
#ifndef A283B06E_3ED5_4D78_A2B5_4B87495A80A6
#define A283B06E_3ED5_4D78_A2B5_4B87495A80A6
#include <chrono>
#include <cmath>
#include <ctime>
#include <string>
#include "Poco/DateTime.h"
#include "Poco/DateTimeFormat.h"
#include "Poco/DateTimeFormatter.h"
#include "Poco/DateTimeParser.h"
#include "Poco/Exception.h"
#include "Poco/LocalDateTime.h"
#include "Poco/Timespan.h"
#include "Poco/Timestamp.h"
namespace Util
{
class DateTimeUtils
{
public:
static Poco::DateTime now()
{
return Poco::DateTime();
}
static Poco::LocalDateTime localNow()
{
return Poco::LocalDateTime();
}
static std::string nowStr(const std::string& formatStr = "%Y-%m-%d %H:%M:%S")
{
return format(Poco::DateTime(), formatStr);
}
static std::string localNowStr(const std::string& formatStr = "%Y-%m-%d %H:%M:%S")
{
return Poco::DateTimeFormatter::format(Poco::LocalDateTime(), formatStr);
}
static Poco::DateTime parse(const std::string& str, const std::string& formatStr = "")
{
auto dt = Poco::DateTime(1970, 1, 1);
if (str.empty())
{
return dt;
}
int timeZoneDifferential = 0;
if (!formatStr.empty())
{
if (Poco::DateTimeParser::tryParse(formatStr, str, dt, timeZoneDifferential))
{
return dt;
}
}
else
{
if (Poco::DateTimeParser::tryParse(Poco::DateTimeFormat::ISO8601_FORMAT, str, dt, timeZoneDifferential) ||
Poco::DateTimeParser::tryParse("%Y-%m-%d %H:%M:%S", str, dt, timeZoneDifferential) ||
Poco::DateTimeParser::tryParse("%Y/%m/%d %H:%M:%S", str, dt, timeZoneDifferential) ||
Poco::DateTimeParser::tryParse("%Y%m%d%H%M%S", str, dt, timeZoneDifferential))
{
return dt;
}
}
return dt;
}
static std::string format(const Poco::DateTime& dt, const std::string& formatStr = "%Y-%m-%d %H:%M:%S")
{
return Poco::DateTimeFormatter::format(dt, formatStr);
}
static std::string toISO8601(const Poco::DateTime& dt)
{
return Poco::DateTimeFormatter::format(dt, Poco::DateTimeFormat::ISO8601_FORMAT);
}
static std::string toRFC1123(const Poco::DateTime& dt)
{
return Poco::DateTimeFormatter::format(dt, Poco::DateTimeFormat::RFC1123_FORMAT);
}
static Poco::DateTime addDays(const Poco::DateTime& dt, int days)
{
return dt + Poco::Timespan(days, 0, 0, 0, 0);
}
static Poco::DateTime addHours(const Poco::DateTime& dt, int hours)
{
return dt + Poco::Timespan(0, hours, 0, 0, 0);
}
static Poco::DateTime addMinutes(const Poco::DateTime& dt, int minutes)
{
return dt + Poco::Timespan(0, 0, minutes, 0, 0);
}
static Poco::DateTime addSeconds(const Poco::DateTime& dt, int seconds)
{
return dt + Poco::Timespan(0, 0, 0, seconds, 0);
}
static Poco::Timespan diff(const Poco::DateTime& dt1, const Poco::DateTime& dt2)
{
Poco::Timestamp ts1 = dt1.timestamp();
Poco::Timestamp ts2 = dt2.timestamp();
Poco::Timestamp::TimeDiff diffMicroseconds = 0;
if (ts1 > ts2)
{
diffMicroseconds = ts1 - ts2;
}
else
{
diffMicroseconds = ts2 - ts1;
}
return Poco::Timespan(diffMicroseconds);
}
static long diffSeconds(const Poco::DateTime& dt1, const Poco::DateTime& dt2)
{
Poco::Timestamp ts1 = dt1.timestamp();
Poco::Timestamp ts2 = dt2.timestamp();
Poco::Timestamp::TimeDiff diffMicroseconds = 0;
if (ts1 > ts2)
{
diffMicroseconds = ts1 - ts2;
}
else
{
diffMicroseconds = ts2 - ts1;
}
Poco::Timespan ts(diffMicroseconds);
return ts.totalSeconds();
}
static long long diffMilliseconds(const Poco::DateTime& dt1, const Poco::DateTime& dt2)
{
Poco::Timestamp ts1 = dt1.timestamp();
Poco::Timestamp ts2 = dt2.timestamp();
Poco::Timestamp::TimeDiff diffMicroseconds = 0;
if (ts1 > ts2)
{
diffMicroseconds = ts1 - ts2;
}
else
{
diffMicroseconds = ts2 - ts1;
}
return diffMicroseconds / 1000;
}
static long long diffMicroseconds(const Poco::DateTime& dt1, const Poco::DateTime& dt2)
{
Poco::Timestamp ts1 = dt1.timestamp();
Poco::Timestamp ts2 = dt2.timestamp();
Poco::Timestamp::TimeDiff diffMicroseconds = 0;
if (ts1 > ts2)
{
diffMicroseconds = ts1 - ts2;
}
else
{
diffMicroseconds = ts2 - ts1;
}
return diffMicroseconds;
}
static void diffComponents(const Poco::DateTime& dt1, const Poco::DateTime& dt2, int& days, int& hours,
int& minutes, int& seconds)
{
Poco::Timespan ts = diff(dt1, dt2);
days = ts.days();
hours = ts.hours();
minutes = ts.minutes();
seconds = ts.seconds();
}
static Poco::Timespan calculateDifference(const Poco::DateTime& dt1, const Poco::DateTime& dt2)
{
Poco::Timestamp::TimeVal us1 = dt1.timestamp().epochMicroseconds();
Poco::Timestamp::TimeVal us2 = dt2.timestamp().epochMicroseconds();
Poco::Timestamp::TimeVal diff = 0;
if (us1 > us2)
{
diff = us1 - us2;
}
else
{
diff = us2 - us1;
}
return Poco::Timespan(diff);
}
static bool isBefore(const Poco::DateTime& dt1, const Poco::DateTime& dt2)
{
Poco::Timestamp ts1 = dt1.timestamp();
Poco::Timestamp ts2 = dt2.timestamp();
return ts1 < ts2;
}
static bool isAfter(const Poco::DateTime& dt1, const Poco::DateTime& dt2)
{
Poco::Timestamp ts1 = dt1.timestamp();
Poco::Timestamp ts2 = dt2.timestamp();
return ts1 > ts2;
}
static bool isEqual(const Poco::DateTime& dt1, const Poco::DateTime& dt2)
{
Poco::Timestamp ts1 = dt1.timestamp();
Poco::Timestamp ts2 = dt2.timestamp();
return ts1 == ts2;
}
static bool isLeapYear(int year)
{
return Poco::DateTime::isLeapYear(year);
}
static int daysInMonth(int year, int month)
{
if (month < 1 || month > 12)
{
return 0;
}
Poco::DateTime nextMonth;
if (month == 12)
{
nextMonth = Poco::DateTime(year + 1, 1, 1);
}
else
{
nextMonth = Poco::DateTime(year, month + 1, 1);
}
Poco::DateTime lastDay = nextMonth - Poco::Timespan(1, 0, 0, 0, 0);
return lastDay.day();
}
static std::string getWeekdayName(int dayOfWeek)
{
static const std::string weekdays[] = {"星期日", "星期一", "星期二", "星期三", "星期四", "星期五", "星期六"};
if (dayOfWeek >= 0 && dayOfWeek <= 6)
{
return weekdays[dayOfWeek];
}
return "未知";
}
static time_t getTimestamp()
{
Poco::Timestamp ts;
return ts.epochTime();
}
static long long getTimestampMs()
{
Poco::Timestamp ts;
return ts.epochMicroseconds() / 1000;
}
static long long getTimestampUs()
{
Poco::Timestamp ts;
return ts.epochMicroseconds();
}
static Poco::DateTime fromTimestamp(time_t timestamp)
{
Poco::Timestamp ts(static_cast<Poco::Timestamp::TimeVal>(timestamp) * 1000000);
return Poco::DateTime(ts);
}
static Poco::DateTime fromTimestampMs(long long timestampMs)
{
Poco::Timestamp ts(static_cast<Poco::Timestamp::TimeVal>(timestampMs) * 1000);
return Poco::DateTime(ts);
}
static Poco::DateTime fromTimestampUs(long long timestampUs)
{
Poco::Timestamp ts(static_cast<Poco::Timestamp::TimeVal>(timestampUs));
return Poco::DateTime(ts);
}
static Poco::Timestamp toTimestamp(const Poco::DateTime& dt)
{
return dt.timestamp();
}
static Poco::DateTime toDateTime(const Poco::Timestamp& ts)
{
return Poco::DateTime(ts);
}
static Poco::DateTime getStartOfDay(const Poco::DateTime& dt)
{
return Poco::DateTime(dt.year(), dt.month(), dt.day());
}
static Poco::DateTime getEndOfDay(const Poco::DateTime& dt)
{
return Poco::DateTime(dt.year(), dt.month(), dt.day(), 23, 59, 59, 999999);
}
static int getAge(const Poco::DateTime& birthDate)
{
Poco::DateTime now = DateTimeUtils::now();
int age = now.year() - birthDate.year();
if (now.month() < birthDate.month() || (now.month() == birthDate.month() && now.day() < birthDate.day()))
{
age--;
}
return age;
}
static bool isSameDay(const Poco::DateTime& dt1, const Poco::DateTime& dt2)
{
return dt1.year() == dt2.year() && dt1.month() == dt2.month() && dt1.day() == dt2.day();
}
static std::string formatTimespan(const Poco::Timespan& ts)
{
std::string result;
if (ts.days() > 0)
{
result += std::to_string(ts.days()) + "天";
}
if (ts.hours() > 0)
{
result += std::to_string(ts.hours()) + "小时";
}
if (ts.minutes() > 0)
{
result += std::to_string(ts.minutes()) + "分";
}
if (ts.seconds() > 0)
{
result += std::to_string(ts.seconds()) + "秒";
}
if (result.empty())
{
result = "0秒";
}
return result;
}
static std::string formatTimeDifference(const Poco::DateTime& dt1, const Poco::DateTime& dt2)
{
Poco::Timespan diff = calculateDifference(dt1, dt2);
return formatTimespan(diff);
}
static std::string formatTimespanWithMs(const Poco::Timespan& ts)
{
std::string result = formatTimespan(ts);
int milliseconds = ts.milliseconds();
if (milliseconds > 0)
{
result += std::to_string(milliseconds) + "毫秒";
}
return result;
}
static int getWeekOfYear(const Poco::DateTime& dt)
{
Poco::DateTime firstDay(dt.year(), 1, 1);
Poco::Timespan diff = dt - firstDay;
int dayOfYear = diff.days() + 1;
int firstDayWeek = firstDay.dayOfWeek();
int week = (dayOfYear + firstDayWeek - 1) / 7 + 1;
return week;
}
static Poco::DateTime getFirstDayOfMonth(const Poco::DateTime& dt)
{
return Poco::DateTime(dt.year(), dt.month(), 1);
}
static Poco::DateTime getLastDayOfMonth(const Poco::DateTime& dt)
{
int lastDay = daysInMonth(dt.year(), dt.month());
return Poco::DateTime(dt.year(), dt.month(), lastDay);
}
static Poco::DateTime getFirstDayOfWeek(const Poco::DateTime& dt)
{
int dayOfWeek = dt.dayOfWeek();
int daysToMonday = (dayOfWeek == 0) ? 6 : (dayOfWeek - 1);
return addDays(dt, -daysToMonday);
}
static Poco::DateTime getLastDayOfWeek(const Poco::DateTime& dt)
{
int dayOfWeek = dt.dayOfWeek();
int daysToSunday = (dayOfWeek == 0) ? 0 : (7 - dayOfWeek);
return addDays(dt, daysToSunday);
}
static long long getTime(const Poco::DateTime& dt)
{
Poco::Timestamp ts = dt.timestamp();
return ts.epochMicroseconds() / 1000;
}
static time_t getTimeSeconds(const Poco::DateTime& dt)
{
Poco::Timestamp ts = dt.timestamp();
return ts.epochTime();
}
static Poco::DateTime fromTime(long long milliseconds)
{
Poco::Timestamp ts(static_cast<Poco::Timestamp::TimeVal>(milliseconds) * 1000);
return Poco::DateTime(ts);
}
static Poco::DateTime fromTimeSeconds(time_t seconds)
{
Poco::Timestamp ts(static_cast<Poco::Timestamp::TimeVal>(seconds) * 1000000);
return Poco::DateTime(ts);
}
static long long currentTimeMillis()
{
Poco::Timestamp ts;
return ts.epochMicroseconds() / 1000;
}
static time_t currentTimeSeconds()
{
Poco::Timestamp ts;
return ts.epochTime();
}
};
}
#endif

104
util/Device.cpp Normal file
View file

@ -0,0 +1,104 @@
/*
*******************************************************************************
* INCLUDE
*******************************************************************************
*/
#include "Device.h"
#include <stdarg.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/ioctl.h>
#include "Poco/Exception.h"
#include "Poco/Format.h"
#include <iostream>
namespace Util
{
/*
*******************************************************************************
* CLASS DEFINE
*******************************************************************************
*/
Device::Device(std::string path, Mode mode): _path(path), _mode(mode), _fd(-1)
{
}
Device::~Device()
{
close();
}
void Device::open()
{
int flags;
switch (_mode)
{
default:
case Mode::RD_ONLY:
flags = O_RDONLY;
break;
case Mode::WR_ONLY:
flags = O_WRONLY;
break;
case Mode::RDWR:
flags = O_RDWR;
break;
}
_fd = ::open(_path.c_str(), flags);
if (_fd < 0)
throw Poco::InvalidAccessException(Poco::format("Access %s error: %d", _path, _fd), _fd);
}
int Device::write(uint8_t* data, int size)
{
return ::write(_fd, data, size);
}
int Device::read(uint8_t* buffer, int count)
{
return ::read(_fd, buffer, count);
}
void Device::close()
{
if (_fd >= 0)
::close(_fd);
}
int Device::fcntl(int cmd)
{
return ::fcntl(_fd, cmd);
}
int Device::fcntl(int cmd, long arg)
{
return ::fcntl(_fd, cmd, arg);
}
int Device::fcntl(int cmd, long arg, void* lock)
{
return ::fcntl(_fd, cmd, arg, lock);
}
int Device::poll(unsigned int& revents, int timeout)
{
struct pollfd pollfd;
int rc;
pollfd.fd = _fd;
pollfd.events = POLLIN;
rc = ::poll(&pollfd, 1, timeout);
if (rc > 0)
revents = pollfd.revents;
return rc;
}
}

47
util/Device.h Normal file
View file

@ -0,0 +1,47 @@
#ifndef C5ADE9DB_6B16_4E4F_8C58_0101E9832DE0
#define C5ADE9DB_6B16_4E4F_8C58_0101E9832DE0
/*
*******************************************************************************
* INCLUDE
*******************************************************************************
*/
#include <string>
#include <poll.h>
namespace Util
{
/*
*******************************************************************************
* CLASS DECLARE
*******************************************************************************
*/
class Device
{
public:
enum Mode
{
RD_ONLY,
WR_ONLY,
RDWR,
};
public:
Device(std::string path, Mode mode = Mode::RD_ONLY);
~Device();
virtual void open();
virtual int write(uint8_t* data, int size);
virtual int read(uint8_t* buffer, int count);
virtual void close();
virtual int fcntl(int cmd);
virtual int fcntl(int cmd, long arg);
virtual int fcntl(int cmd, long arg, void* lock);
virtual int poll(unsigned int& revents, int timeout);
private:
std::string _path;
Mode _mode;
int _fd;
};
}
#endif

15
util/FactoryManager.cpp Normal file
View file

@ -0,0 +1,15 @@
/*
*******************************************************************************
* INCLUDE
*******************************************************************************
*/
#include "FactoryManager.h"
namespace Util
{
/*
*******************************************************************************
* CLASS DEFINE
*******************************************************************************
*/
}

79
util/FactoryManager.h Normal file
View file

@ -0,0 +1,79 @@
#ifndef BE13A370_B199_43EC_A4E6_E2D4FD197D63
#define BE13A370_B199_43EC_A4E6_E2D4FD197D63
/*
*******************************************************************************
* INCLUDE
*******************************************************************************
*/
#include "ClassAssistant.h"
#include <map>
#include <Poco/SharedPtr.h>
#include <Poco/Format.h>
namespace Util
{
/*
*******************************************************************************
* CLASS DECLARE
*******************************************************************************
*/
template <class T> class SimpleSelector
{
public:
static T select(T val)
{
return val;
}
};
template <class T, class TKey, class TSelector, class... TArgs> class FactoryManager
{
private:
class IFactory
{
public:
using Ptr = Poco::SharedPtr<IFactory>;
virtual ~IFactory()
{
}
public:
virtual T* create(TArgs... args) = 0;
};
template <class C> class Factory: public IFactory
{
public:
C* create(TArgs... args) override
{
return new C(args...);
}
};
using Factories = std::map<TKey, typename IFactory::Ptr>;
public:
static T* create(TArgs... args)
{
TKey key = TSelector::select(args...);
if (_factories.find(key) == _factories.end())
throw Poco::NotFoundException(
Poco::format("FactoryManager: Factory '%s' does not exists.", Util::ClassAssistant::demangle<T>()),
-EINVAL);
return _factories[key]->create(args...);
}
template <class C> static void registerClass(TKey key)
{
_factories[key] = new Factory<C>();
}
private:
static Factories _factories;
};
template <class T, class TKey, class TSelect, class... TArgs>
typename FactoryManager<T, TKey, TSelect, TArgs...>::Factories FactoryManager<T, TKey, TSelect, TArgs...>::_factories;
}
#endif

71
util/FileUtil.h Normal file
View file

@ -0,0 +1,71 @@
#ifndef B2D3FCFE_BBC5_4EFD_9AA7_7EEB5C58E2AE
#define B2D3FCFE_BBC5_4EFD_9AA7_7EEB5C58E2AE
#include <Poco/File.h>
#include <Poco/FileStream.h>
#include <Poco/StreamCopier.h>
#include <string>
#include "Logger.h"
namespace Util
{
class FileUtil
{
private:
public:
static std::string readFile(const std::string& fileFullName)
{
std::string content;
Poco::File file(fileFullName);
if (file.exists())
{
try
{
Poco::FileInputStream fis(fileFullName);
Poco::StreamCopier::copyToString(fis, content);
fis.close();
}
catch (Poco::Exception& e)
{
logger().error("readFile read error: %s", std::string(e.what()));
}
}
return content;
}
static bool writeFile(const std::string& fileFullName, const std::string& content)
{
try
{
Poco::FileOutputStream fos(fileFullName, std::ios::trunc);
fos << content;
fos.close();
int fd = ::open(fileFullName.c_str(), O_RDWR);
if (fd != -1)
{
::fsync(fd);
::close(fd);
}
return true;
}
catch (Poco::Exception& e)
{
logger().error("writeFile file error: %s", std::string(e.what()));
}
return false;
}
static void delFile(const std::string& fileFullName)
{
if (!fileFullName.empty())
{
if (Poco::File(fileFullName).exists())
{
Poco::File(fileFullName).remove();
logger().debug("删除文件: %s", fileFullName);
}
}
}
};
}
#endif

125
util/FrpUtil.h Normal file
View file

@ -0,0 +1,125 @@
#ifndef FRP_UTIL_H
#define FRP_UTIL_H
#include <fcntl.h>
#include <signal.h>
#include <unistd.h>
#include <cstdlib>
#include <cstring>
#include <string>
#include "Application.h"
#include "Poco/JSON/Object.h"
#include "util/SystemInfo.h"
namespace Util
{
class FrpUtil
{
private:
public:
static constexpr const char* frpcConfigPathName = "/customer/frp/frpc.toml";
static constexpr const char* frpcPathName = "/customer/frp/frpc";
public:
static int getFrpcPid()
{
return Util::SystemInfo::getProcessPID(frpcPathName);
}
static void startFrpc(std::string serialNum, Poco::JSON::Object::Ptr service_ports, bool forceRestart)
{
int http_local_port = service_ports->get("http_port");
int http_remote_port = service_ports->get("http_port_nat_port");
int http_api_local_port = service_ports->get("http_api_port");
int http_api_remote_port = service_ports->get("http_api_port_nat_port");
int rtmp_local_port = service_ports->get("rtmp_port");
int rtmp_remote_port = service_ports->get("rtmp_port_nat_port");
int rtsp_local_port = service_ports->get("rtsp_port");
int rtsp_remote_port = service_ports->get("rtsp_port_nat_port");
int srt_local_port = service_ports->get("srt_port");
int srt_remote_port = service_ports->get("srt_port_nat_port");
int nat_frp_enable = service_ports->get("nat_frp_enable");
try
{
int pid = getFrpcPid();
if (nat_frp_enable == 0)
{
if (pid > 0)
{
stopFrpc(pid);
}
return;
}
if (pid > 0)
{
if (forceRestart)
{
stopFrpc(pid);
}
else
{
return;
}
}
pid_t child = fork();
if (child < 0)
{
logger().information("frpc fork failed");
return;
}
if (child > 0)
{
logger().information("frpc daemon starting...");
return;
}
setsid();
for (int i = 3; i < 1024; ++i)
{
close(i);
}
int fd = open("/dev/null", O_RDWR);
if (fd >= 0)
{
dup2(fd, STDIN_FILENO);
dup2(fd, STDOUT_FILENO);
dup2(fd, STDERR_FILENO);
close(fd);
}
setenv("FRP_SERIAL_NUM", serialNum.c_str(), 1);
setenv("FRP_HTTP_LOCAL_PORT", std::to_string(http_local_port).c_str(), 1);
setenv("FRP_HTTP_REMOTE_PORT", std::to_string(http_remote_port).c_str(), 1);
setenv("FRP_HTTP_API_LOCAL_PORT", std::to_string(http_api_local_port).c_str(), 1);
setenv("FRP_HTTP_API_REMOTE_PORT", std::to_string(http_api_remote_port).c_str(), 1);
setenv("FRP_RTMP_LOCAL_PORT", std::to_string(rtmp_local_port).c_str(), 1);
setenv("FRP_RTMP_REMOTE_PORT", std::to_string(rtmp_remote_port).c_str(), 1);
setenv("FRP_RTSP_LOCAL_PORT", std::to_string(rtsp_local_port).c_str(), 1);
setenv("FRP_RTSP_REMOTE_PORT", std::to_string(rtsp_remote_port).c_str(), 1);
setenv("FRP_SRT_LOCAL_PORT", std::to_string(srt_local_port).c_str(), 1);
setenv("FRP_SRT_REMOTE_PORT", std::to_string(srt_remote_port).c_str(), 1);
execlp(frpcPathName, frpcPathName, "-c", frpcConfigPathName, (char*)NULL);
exit(EXIT_FAILURE);
}
catch (const std::exception& e)
{
logger().information("doStartFrpc,error: %s", std::string(e.what()));
}
}
static void stopFrpc(int pid)
{
if (pid > 0)
{
Util::SystemInfo::executeCommand("kill -9 " + std::to_string(pid));
logger().information("frpc stoped,pid=%d........", pid);
}
}
};
}
#endif

386
util/HTTPClient.h Normal file
View file

@ -0,0 +1,386 @@
#ifndef C99FBF1C_5805_40A3_963F_5BC1CA998C5C
#define C99FBF1C_5805_40A3_963F_5BC1CA998C5C
#include <Poco/Exception.h>
#include <Poco/Net/HTMLForm.h>
#include <Poco/Net/HTTPClientSession.h>
#include <Poco/Net/HTTPRequest.h>
#include <Poco/Net/HTTPResponse.h>
#include <Poco/Net/MessageHeader.h>
#include <Poco/StreamCopier.h>
#include <Poco/URI.h>
#include <cctype>
#include <fstream>
#include <iomanip>
#include <iostream>
#include <map>
#include <memory>
#include <sstream>
#include <string>
#include <vector>
#include "Logger.h"
namespace Util
{
class HTTPClient
{
public:
struct HttpResponse
{
int statusCode;
std::string statusMessage;
std::string content;
std::string contentType;
std::map<std::string, std::string> headers;
bool success;
HttpResponse(): statusCode(0), success(false)
{
}
};
struct ProxyConfig
{
std::string host;
int port;
bool enabled;
ProxyConfig(): port(0), enabled(false)
{
}
ProxyConfig(const std::string& h, int p): host(h), port(p), enabled(true)
{
}
};
struct RequestConfig
{
int timeout;
std::string userAgent;
std::map<std::string, std::string> headers;
bool followRedirects;
ProxyConfig proxy;
RequestConfig(): timeout(30), userAgent("HTTPClient/1.0"), followRedirects(true)
{
}
};
static HttpResponse get(const std::string& url, const RequestConfig& config = RequestConfig())
{
return executeRequest("GET", url, "", config);
}
static bool downloadFile(const std::string& url, const std::string& localFilePath,
const RequestConfig& config = RequestConfig())
{
try
{
Poco::URI uri(url);
auto session = createSession(uri, config.proxy);
session->setTimeout(Poco::Timespan(config.timeout, 0));
std::string path = getPathAndQuery(uri);
Poco::Net::HTTPRequest request(Poco::Net::HTTPRequest::HTTP_GET, path);
setupRequest(request, config);
session->sendRequest(request);
Poco::Net::HTTPResponse response;
std::istream& responseStream = session->receiveResponse(response);
if (response.getStatus() != Poco::Net::HTTPResponse::HTTP_OK)
{
std::cerr << "Download failed: HTTP " << response.getStatus() << std::endl;
return false;
}
std::ofstream file(localFilePath, std::ios::binary);
if (!file.is_open())
{
std::cerr << "Cannot open file: " << localFilePath << std::endl;
return false;
}
Poco::StreamCopier::copyStream(responseStream, file);
file.close();
return true;
}
catch (Poco::Exception& e)
{
std::cerr << "Download exception: " << e.displayText() << std::endl;
return false;
}
}
static HttpResponse postJson(const std::string& url, const std::string& jsonData,
const RequestConfig& config = RequestConfig())
{
RequestConfig jsonConfig = config;
jsonConfig.headers["Content-Type"] = "application/json";
return executeRequest("POST", url, jsonData, jsonConfig);
}
static HttpResponse postForm(const std::string& url, const std::map<std::string, std::string>& formData,
const RequestConfig& config = RequestConfig())
{
std::string formString = buildFormData(formData);
RequestConfig formConfig = config;
formConfig.headers["Content-Type"] = "application/x-www-form-urlencoded";
return executeRequest("POST", url, formString, formConfig);
}
static HttpResponse postFormWithPoco(const std::string& url, const std::map<std::string, std::string>& formData,
const RequestConfig& config = RequestConfig())
{
try
{
Poco::URI uri(url);
auto session = createSession(uri, config.proxy);
session->setTimeout(Poco::Timespan(config.timeout, 0));
std::string path = getPathAndQuery(uri);
Poco::Net::HTTPRequest request(Poco::Net::HTTPRequest::HTTP_POST, path);
setupRequest(request, config);
request.setContentType("application/x-www-form-urlencoded");
Poco::Net::HTMLForm form;
for (const auto& field : formData)
{
form.set(field.first, field.second);
}
std::ostream& requestStream = session->sendRequest(request);
form.write(requestStream);
Poco::Net::HTTPResponse response;
std::istream& responseStream = session->receiveResponse(response);
HttpResponse result;
result.statusCode = response.getStatus();
result.statusMessage = response.getReason();
result.contentType = response.getContentType();
result.success = (result.statusCode == Poco::Net::HTTPResponse::HTTP_OK);
Poco::Net::NameValueCollection::ConstIterator it = response.begin();
while (it != response.end())
{
result.headers[it->first] = it->second;
++it;
}
Poco::StreamCopier::copyToString(responseStream, result.content);
return result;
}
catch (Poco::Exception& e)
{
std::cerr << "POST form exception: " << e.displayText() << std::endl;
HttpResponse result;
result.success = false;
result.statusMessage = e.displayText();
return result;
}
}
static HttpResponse post(const std::string& url, const std::string& data,
const std::string& contentType = "text/plain",
const RequestConfig& config = RequestConfig())
{
RequestConfig postConfig = config;
postConfig.headers["Content-Type"] = contentType;
return executeRequest("POST", url, data, postConfig);
}
static HttpResponse put(const std::string& url, const std::string& data = "",
const RequestConfig& config = RequestConfig())
{
return executeRequest("PUT", url, data, config);
}
static HttpResponse deleteRequest(const std::string& url, const RequestConfig& config = RequestConfig())
{
return executeRequest("DELETE", url, "", config);
}
static RequestConfig createProxyConfig(const std::string& proxyHost, int proxyPort,
const RequestConfig& baseConfig = RequestConfig())
{
RequestConfig config = baseConfig;
config.proxy = ProxyConfig(proxyHost, proxyPort);
return config;
}
static HttpResponse executeRequest(const std::string& method, const std::string& url, const std::string& data,
const RequestConfig& config)
{
HttpResponse result;
try
{
Poco::URI uri(url);
auto session = createSession(uri, config.proxy);
session->setTimeout(Poco::Timespan(config.timeout, 0));
std::string path = getPathAndQuery(uri);
Poco::Net::HTTPRequest request(method, path);
setupRequest(request, config);
if (!data.empty())
{
request.setContentLength(data.length());
std::ostream& requestStream = session->sendRequest(request);
requestStream << data;
}
else
{
session->sendRequest(request);
}
Poco::Net::HTTPResponse response;
std::istream& responseStream = session->receiveResponse(response);
result.statusCode = response.getStatus();
result.statusMessage = response.getReason();
result.contentType = response.getContentType();
result.success = (result.statusCode == Poco::Net::HTTPResponse::HTTP_OK);
Poco::Net::NameValueCollection::ConstIterator it = response.begin();
while (it != response.end())
{
result.headers[it->first] = it->second;
++it;
}
Poco::StreamCopier::copyToString(responseStream, result.content);
if (config.followRedirects && (result.statusCode == Poco::Net::HTTPResponse::HTTP_MOVED_PERMANENTLY ||
result.statusCode == Poco::Net::HTTPResponse::HTTP_FOUND ||
result.statusCode == Poco::Net::HTTPResponse::HTTP_SEE_OTHER))
{
auto locationIt = result.headers.find("Location");
if (locationIt != result.headers.end())
{
std::cout << "Redirecting to: " << locationIt->second << std::endl;
return executeRequest(method, locationIt->second, data, config);
}
}
}
catch (Poco::TimeoutException& e)
{
std::cerr << "Request timeout: " << e.displayText() << std::endl;
result.success = false;
result.statusMessage = "Timeout";
}
catch (Poco::Exception& e)
{
std::cerr << "HTTP exception: " << e.displayText() << std::endl;
result.success = false;
result.statusMessage = e.displayText();
}
catch (std::exception& e)
{
std::cerr << "Standard exception: " << e.what() << std::endl;
result.success = false;
result.statusMessage = e.what();
}
return result;
}
private:
static std::string urlEncode(const std::string& value)
{
std::ostringstream escaped;
escaped.fill('0');
escaped << std::hex;
for (char c : value)
{
if (std::isalnum(c) || c == '-' || c == '_' || c == '.' || c == '~')
{
escaped << c;
}
else
{
escaped << '%' << std::setw(2) << int(static_cast<unsigned char>(c));
}
}
return escaped.str();
}
static std::string buildFormData(const std::map<std::string, std::string>& formData)
{
std::ostringstream formStream;
bool first = true;
for (const auto& field : formData)
{
if (!first)
{
formStream << "&";
}
formStream << urlEncode(field.first) << "=" << urlEncode(field.second);
first = false;
}
return formStream.str();
}
static std::unique_ptr<Poco::Net::HTTPClientSession> createSession(const Poco::URI& uri, const ProxyConfig& proxy)
{
std::unique_ptr<Poco::Net::HTTPClientSession> session;
logger().debug("HTTPClient url: %s", uri.toString());
if (proxy.enabled)
{
session = std::make_unique<Poco::Net::HTTPClientSession>(proxy.host, proxy.port);
session->setProxy(uri.getHost(), uri.getPort());
}
else
{
if (uri.getScheme() == "https")
{
logger().warning("HTTPClient 暂时不支持https,自动调整为使用http连接: %s", uri.toString());
session = std::make_unique<Poco::Net::HTTPClientSession>(uri.getHost(), uri.getPort());
}
else
{
session = std::make_unique<Poco::Net::HTTPClientSession>(uri.getHost(), uri.getPort());
}
}
return session;
}
static void setupRequest(Poco::Net::HTTPRequest& request, const RequestConfig& config)
{
request.setVersion(Poco::Net::HTTPMessage::HTTP_1_1);
request.set("User-Agent", config.userAgent);
request.set("Accept", "*/*");
request.set("Connection", "close");
for (const auto& header : config.headers)
{
request.set(header.first, header.second);
}
}
static std::string getPathAndQuery(const Poco::URI& uri)
{
std::string path = uri.getPathAndQuery();
if (path.empty())
{
path = "/";
}
return path;
}
};
}
#endif

0
util/HandlesManager.cpp Normal file
View file

67
util/HandlesManager.h Normal file
View file

@ -0,0 +1,67 @@
#ifndef F0270054_CDC4_4A91_B09F_73A4C75D9356
#define F0270054_CDC4_4A91_B09F_73A4C75D9356
/*
*******************************************************************************
* INCLUDE
*******************************************************************************
*/
#include <Poco/Mutex.h>
#include <set>
namespace Util
{
/*
*******************************************************************************
* CLASS DECLARE
*******************************************************************************
*/
template <typename T> class HandlesManager
{
public:
using HandleCollection = std::set<T>;
public:
HandlesManager(unsigned count): HandlesManager(0, count)
{
}
HandlesManager(unsigned begin, unsigned count)
{
for (T h = begin; h < (begin + count); h++)
_freeHandles.insert(h);
}
HandlesManager(HandleCollection& c)
{
_freeHandles.insert(c);
}
T allocHandle()
{
Poco::FastMutex::ScopedLock lock(_lock);
if (_freeHandles.size() == 0)
throw Poco::NotFoundException("Collection is empty", -ENODATA);
T h = *_freeHandles.begin();
_freeHandles.erase(h);
return h;
}
void freeHandle(T handle)
{
Poco::FastMutex::ScopedLock lock(_lock);
if (_freeHandles.count(handle) != 0)
throw Poco::NotFoundException("Double free", -EINVAL);
_freeHandles.insert(handle);
}
private:
std::set<T> _freeHandles;
Poco::FastMutex _lock;
};
}
#endif

29
util/HexBinaryCodec.cpp Executable file
View file

@ -0,0 +1,29 @@
/*
*******************************************************************************
* INCLUDE
*******************************************************************************
*/
#include "HexBinaryCodec.h"
#include <sstream>
#include <Poco/HexBinaryEncoder.h>
namespace Util
{
/*
*******************************************************************************
* CLASS DEFINE
*******************************************************************************
*/
std::string HexBinaryCodec::encode(const void* data, unsigned size, bool uppercase)
{
std::ostringstream ostr;
Poco::HexBinaryEncoder encoder(ostr);
encoder.rdbuf()->setUppercase(uppercase);
uint8_t* pBytes = (uint8_t*)data;
for (unsigned i = 0; i < size; i++)
encoder << pBytes[i];
encoder.close();
return ostr.str();
}
}

23
util/HexBinaryCodec.h Executable file
View file

@ -0,0 +1,23 @@
#ifndef E955D6BE_6497_479F_8D49_2A73C17E05E9
#define E955D6BE_6497_479F_8D49_2A73C17E05E9
/*
*******************************************************************************
* INCLUDE
*******************************************************************************
*/
#include <string>
namespace Util
{
/*
*******************************************************************************
* CLASS DECLARE
*******************************************************************************
*/
class HexBinaryCodec
{
public:
static std::string encode(const void* data, unsigned size, bool uppercase = true);
};
}
#endif

257
util/JsonUtil.h Normal file
View file

@ -0,0 +1,257 @@
#ifndef JSONUTIL_H
#define JSONUTIL_H
#include <map>
#include <regex>
#include <string>
#include <vector>
#include "util/xpack/json.h"
#include <Poco/JSON/Array.h>
#include <Poco/JSON/Object.h>
#include <Poco/JSON/Parser.h>
namespace Util
{
class JsonUtil
{
private:
public:
template <class T> static std::string encode(T t)
{
return xpack::json::encode(t);
}
template <typename T> static T decode(std::string jsonStr)
{
T t;
xpack::json::decode(jsonStr, t);
return t;
}
static std::string jsonToString(const Poco::JSON::Object::Ptr& jsonObject)
{
std::ostringstream oss;
Poco::JSON::Stringifier::stringify(jsonObject, oss);
return oss.str();
}
static Poco::JSON::Object::Ptr stringToJson(const std::string& jsonString)
{
Poco::JSON::Parser parser;
Poco::Dynamic::Var result = parser.parse(jsonString);
return result.extract<Poco::JSON::Object::Ptr>();
}
static Poco::JSON::Array::Ptr stringToJsonArray(const std::string& jsonArrayString)
{
Poco::JSON::Parser parser;
Poco::Dynamic::Var result = parser.parse(jsonArrayString);
return result.extract<Poco::JSON::Array::Ptr>();
}
static Poco::Dynamic::Var getNestedValue(Poco::JSON::Object::Ptr json, const std::string& path)
{
if (path.empty())
return json;
std::regex arrayRegex(R"((\w+)((?:\[\d+\])+))");
std::istringstream pathStream(path);
std::string segment;
Poco::Dynamic::Var currentVar = json;
while (std::getline(pathStream, segment, '.'))
{
std::smatch match;
if (std::regex_match(segment, match, arrayRegex))
{
std::string arrayKey = match[1];
std::string indicesStr = match[2];
std::vector<std::size_t> indices;
std::regex indexRegex(R"(\[(\d+)\])");
std::sregex_iterator it(indicesStr.begin(), indicesStr.end(), indexRegex);
std::sregex_iterator end;
while (it != end)
{
indices.push_back(static_cast<std::size_t>(std::stoi((*it)[1])));
++it;
}
Poco::JSON::Object::Ptr obj = currentVar.extract<Poco::JSON::Object::Ptr>();
if (!obj->has(arrayKey))
{
throw std::runtime_error("getNestedValue Key not found: " + arrayKey);
}
Poco::JSON::Array::Ptr array = obj->getArray(arrayKey);
if (!array)
{
throw std::runtime_error("getNestedValue Expected an array at key: " + arrayKey);
}
for (std::size_t i = 0; i < indices.size(); i++)
{
std::size_t index = indices[i];
if (index >= array->size())
{
throw std::out_of_range("getNestedValue Array index out of bounds: " + std::to_string(index));
}
currentVar = array->get(index);
if (i < indices.size() - 1)
{
array = currentVar.extract<Poco::JSON::Array::Ptr>();
if (!array)
{
throw std::runtime_error("getNestedValue Expected an array at index: " +
std::to_string(index));
}
}
}
}
else
{
Poco::JSON::Object::Ptr obj = currentVar.extract<Poco::JSON::Object::Ptr>();
if (!obj->has(segment))
{
throw std::runtime_error("getNestedValue Key not found: " + segment);
}
currentVar = obj->get(segment);
}
}
return currentVar;
}
static Poco::JSON::Object::Ptr getNestedValue(Poco::JSON::Object::Ptr json, const std::vector<std::string>& paths)
{
Poco::JSON::Object::Ptr results = new Poco::JSON::Object();
for (const auto& path : paths)
{
results->set(path, getNestedValue(json, path));
}
return results;
}
static void setNestedValue(Poco::JSON::Object::Ptr jsonObject, const std::string& path,
const Poco::Dynamic::Var& value)
{
if (!jsonObject)
{
throw std::invalid_argument("setNestedValue JSON object is null.");
}
std::regex arrayRegex(R"((\w+)((?:\[\d+\])+))");
std::istringstream pathStream(path);
std::string segment;
Poco::JSON::Object::Ptr currentObject = jsonObject;
while (std::getline(pathStream, segment, '.'))
{
std::smatch match;
if (std::regex_match(segment, match, arrayRegex))
{
std::string arrayKey = match[1];
std::string indicesStr = match[2];
std::vector<std::size_t> indices;
std::regex indexRegex(R"(\[(\d+)\])");
std::sregex_iterator it(indicesStr.begin(), indicesStr.end(), indexRegex);
std::sregex_iterator end;
while (it != end)
{
indices.push_back(static_cast<std::size_t>(std::stoi((*it)[1])));
++it;
}
Poco::JSON::Array::Ptr array;
if (!currentObject->has(arrayKey))
{
array = new Poco::JSON::Array();
currentObject->set(arrayKey, array);
}
else
{
array = currentObject->getArray(arrayKey);
if (!array)
{
throw std::runtime_error("setNestedValue Expected an array at key: " + arrayKey);
}
}
for (std::size_t i = 0; i < indices.size() - 1; i++)
{
while (array->size() <= indices[i])
{
Poco::JSON::Array::Ptr nestedArray = new Poco::JSON::Array();
array->add(nestedArray);
}
Poco::JSON::Array::Ptr nextArray = array->getArray(indices[i]);
if (!nextArray)
{
throw std::runtime_error("setNestedValue Expected an array at index: " +
std::to_string(indices[i]));
}
array = nextArray;
}
std::size_t lastIndex = indices.back();
while (array->size() <= lastIndex)
{
Poco::JSON::Object::Ptr nestedObject = new Poco::JSON::Object();
array->add(nestedObject);
}
if (pathStream.peek() == EOF)
{
array->set(lastIndex, value);
return;
}
currentObject = array->getObject(lastIndex);
if (!currentObject)
{
throw std::runtime_error("setNestedValue Expected an object at index: " +
std::to_string(lastIndex));
}
}
else
{
if (pathStream.peek() == EOF)
{
currentObject->set(segment, value);
return;
}
if (!currentObject->has(segment))
{
Poco::JSON::Object::Ptr nestedObject = new Poco::JSON::Object();
currentObject->set(segment, nestedObject);
}
currentObject = currentObject->getObject(segment);
if (!currentObject)
{
throw std::runtime_error("setNestedValue Expected an object at key: " + segment);
}
}
}
}
static void setNestedValue(Poco::JSON::Object::Ptr jsonObject,
const std::map<std::string, Poco::Dynamic::Var>& pathValues)
{
for (const auto& kv : pathValues)
{
setNestedValue(jsonObject, kv.first, kv.second);
}
}
};
}
#endif

25
util/Math.cpp Normal file
View file

@ -0,0 +1,25 @@
/*
*******************************************************************************
* INCLUDE
*******************************************************************************
*/
#include "Math.h"
namespace Util
{
/*
*******************************************************************************
* CLASS DEFINE
*******************************************************************************
*/
unsigned Math::alignup(unsigned num, unsigned align)
{
return (((num) + (align)-1) & ~((align)-1));
}
unsigned Math::aligndown(unsigned num, unsigned align)
{
return ((num) & ~((align)-1));
}
}

37
util/Math.h Normal file
View file

@ -0,0 +1,37 @@
#ifndef D6937363_A08A_40BF_B387_36E8AC570ECF
#define D6937363_A08A_40BF_B387_36E8AC570ECF
/*
*******************************************************************************
* INCLUDE
*******************************************************************************
*/
namespace Util
{
/*
*******************************************************************************
* CLASS DECLARE
*******************************************************************************
*/
class Math
{
public:
static unsigned alignup(unsigned num, unsigned align);
static unsigned aligndown(unsigned num, unsigned align);
template <typename T> static T max(T a, T b)
{
return a > b ? a : b;
}
template <typename T> static T min(T a, T b)
{
return a < b ? a : b;
}
template <typename T> static T clamp(T value, T minVal, T maxVal)
{
return max(minVal, min(maxVal, value));
}
};
}
#endif

37
util/Md5Util.h Normal file
View file

@ -0,0 +1,37 @@
#ifndef B6E698FD_68E6_4CB8_9BBA_BFE96C7501B0
#define B6E698FD_68E6_4CB8_9BBA_BFE96C7501B0
#include <Poco/DigestStream.h>
#include <Poco/MD5Engine.h>
#include <Poco/UUID.h>
#include <Poco/UUIDGenerator.h>
#include <iostream>
namespace Util
{
class Md5Util
{
public:
static std::string md5(const std::string& data)
{
Poco::MD5Engine md5Engine;
Poco::DigestOutputStream dos(md5Engine);
dos << data;
dos.close();
const Poco::DigestEngine::Digest& digest = md5Engine.digest();
return Poco::DigestEngine::digestToHex(digest);
}
static std::string uuid()
{
Poco::UUIDGenerator& generator = Poco::UUIDGenerator::defaultGenerator();
Poco::UUID uuid = generator.createRandom();
return uuid.toString();
}
};
}
#endif

20
util/ParallelRunner.cpp Normal file
View file

@ -0,0 +1,20 @@
/*
*******************************************************************************
* INCLUDE
*******************************************************************************
*/
#include "ParallelRunner.h"
namespace Util
{
/*
*******************************************************************************
* CLASS DEFINE
*******************************************************************************
*/
void ParallelRunner::join()
{
for (auto& t : _threads)
t->join();
}
}

35
util/ParallelRunner.h Normal file
View file

@ -0,0 +1,35 @@
#ifndef DE82057F_5520_4849_8108_E9A31AAC1878
#define DE82057F_5520_4849_8108_E9A31AAC1878
/*
*******************************************************************************
* INCLUDE
*******************************************************************************
*/
#include <functional>
#include <Poco/Thread.h>
#include <Poco/SharedPtr.h>
namespace Util
{
/*
*******************************************************************************
* CLASS DECLARE
*******************************************************************************
*/
class ParallelRunner
{
public:
template <class Func, class... Args> void run(Func func, Args... args)
{
Poco::Thread* pThread = new Poco::Thread();
pThread->startFunc([=]() -> void { func(args...); });
_threads.push_back(pThread);
}
void join();
private:
std::vector<Poco::SharedPtr<Poco::Thread>> _threads;
};
}
#endif

View file

@ -0,0 +1,44 @@
/*
*******************************************************************************
* INCLUDE
*******************************************************************************
*/
#include "PocoConfigurationCopier.h"
#include <Poco/Util/JSONConfiguration.h>
namespace Util
{
/*
*******************************************************************************
* CLASS DEFINE
*******************************************************************************
*/
void PocoConfigurationCopier::copy(Poco::Util::AbstractConfiguration::Ptr& from,
Poco::Util::AbstractConfiguration::Ptr& to)
{
bool usingJSON = false;
if (to.isNull())
{
to = new Poco::Util::JSONConfiguration();
usingJSON = true;
}
else
usingJSON = !to.cast<Poco::Util::JSONConfiguration>().isNull();
Poco::AutoPtr<Poco::Util::JSONConfiguration> pJConfig = from.cast<Poco::Util::JSONConfiguration>();
if (pJConfig && usingJSON)
{
std::stringstream ss;
pJConfig->save(ss);
to.cast<Poco::Util::JSONConfiguration>()->load(ss);
}
else
{
Poco::Util::AbstractConfiguration::Keys keys;
from->keys(keys);
for (const auto& key : keys)
to->setString(key, from->getString(key));
}
}
}

View file

@ -0,0 +1,23 @@
#ifndef B2336806_10E0_4BFA_9CC6_0995436A32EB
#define B2336806_10E0_4BFA_9CC6_0995436A32EB
/*
*******************************************************************************
* INCLUDE
*******************************************************************************
*/
#include <Poco/Util/AbstractConfiguration.h>
namespace Util
{
/*
*******************************************************************************
* CLASS DECLARE
*******************************************************************************
*/
class PocoConfigurationCopier
{
public:
static void copy(Poco::Util::AbstractConfiguration::Ptr& from, Poco::Util::AbstractConfiguration::Ptr& to);
};
}
#endif

54
util/Service.cpp Normal file
View file

@ -0,0 +1,54 @@
/*
*******************************************************************************
* INCLUDE
*******************************************************************************
*/
#include "Service.h"
#include <iostream>
/*
*******************************************************************************
* CLASS DEFINE
*******************************************************************************
*/
namespace Util
{
void Service::start()
{
if (isRunning())
return;
_pThread = new Poco::Thread();
try
{
_pThread->start(*this);
}
catch (Poco::Exception& e)
{
std::cout << " --> Service: Poco::Exception: " << e.displayText() << "(" << e.what() << ")" << std::endl;
}
catch (std::exception& e)
{
std::cout << " --> Service: std::exception: " << e.what() << std::endl;
}
}
void Service::stop()
{
if (isRunning())
{
cancel();
_pThread->join();
_pThread.reset();
reset();
}
}
bool Service::isRunning() const
{
return !_pThread.isNull() && _pThread->isRunning();
}
}

31
util/Service.h Normal file
View file

@ -0,0 +1,31 @@
#ifndef F3A063B4_C5E0_40B0_AFDA_B9DC936E9E82
#define F3A063B4_C5E0_40B0_AFDA_B9DC936E9E82
/*
*******************************************************************************
* INCLUDE
*******************************************************************************
*/
#include <Poco/Task.h>
#include <Poco/Thread.h>
namespace Util
{
/*
*******************************************************************************
* CLASS DECLARE
*******************************************************************************
*/
class Service: public Poco::Task
{
public:
using Poco::Task::Task;
virtual void start();
virtual void stop();
bool isRunning() const;
private:
Poco::SharedPtr<Poco::Thread> _pThread;
};
}
#endif

15
util/SimpleFactory.cpp Normal file
View file

@ -0,0 +1,15 @@
/*
*******************************************************************************
* INCLUDE
*******************************************************************************
*/
#include "SimpleFactory.h"
namespace Util
{
/*
*******************************************************************************
* CLASS DEFINE
*******************************************************************************
*/
}

20
util/SimpleFactory.h Normal file
View file

@ -0,0 +1,20 @@
#ifndef FF4EF0DA_D455_437D_A5ED_BA7A7994B9CB
#define FF4EF0DA_D455_437D_A5ED_BA7A7994B9CB
namespace Util
{
/*
*******************************************************************************
* CLASS DECLARE
*******************************************************************************
*/
template <class T, class... TArgs> class SimpleFactory
{
public:
static T* create(TArgs... args)
{
return new T(args...);
}
};
}
#endif

35
util/Subsystem.cpp Normal file
View file

@ -0,0 +1,35 @@
/*
*******************************************************************************
* INCLUDES
*******************************************************************************
*/
#include "Subsystem.h"
namespace Util
{
/*
*******************************************************************************
* CLASS DEFINE
*******************************************************************************
*/
Subsystem::Subsystem(int maxCapacity): _threadPool(2, 16, 60, 0), _taskManager(_threadPool)
{
}
void Subsystem::run(Service* pService)
{
_taskManager.start(pService);
}
void Subsystem::stopAll()
{
_taskManager.cancelAll();
_taskManager.joinAll();
}
void Subsystem::uninitialize()
{
stopAll();
}
}

39
util/Subsystem.h Normal file
View file

@ -0,0 +1,39 @@
#ifndef DC436124_02CE_49A1_88B1_1A95453FA546
#define DC436124_02CE_49A1_88B1_1A95453FA546
/*
*******************************************************************************
* INCLUDE
*******************************************************************************
*/
#include "Service.h"
#include <Poco/Task.h>
#include <Poco/TaskManager.h>
#include <Poco/Thread.h>
#include <Poco/ThreadPool.h>
#include <Poco/Util/Subsystem.h>
namespace Util
{
/*
*******************************************************************************
* CLASS DECLARE
*******************************************************************************
*/
class Subsystem: public Poco::Util::Subsystem
{
public:
Subsystem(int maxCapacity = 16);
protected:
void run(Service* pService);
void stopAll();
virtual void uninitialize() override;
private:
Poco::ThreadPool _threadPool;
Poco::TaskManager _taskManager;
};
}
#endif

829
util/SystemInfo.cpp Normal file
View file

@ -0,0 +1,829 @@
/*
*******************************************************************************
* INCLUDE
*******************************************************************************
*/
#include "SystemInfo.h"
#include <Poco/DirectoryIterator.h>
#include <Poco/Path.h>
#include <Poco/Pipe.h>
#include <Poco/PipeStream.h>
#include <Poco/Process.h>
#include <Poco/StreamCopier.h>
#include <dirent.h>
#include <linux/rtnetlink.h>
#include <net/if.h>
#include <netinet/in.h>
#include <stdio.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/statvfs.h>
#include <unistd.h>
#include <algorithm>
#include <atomic>
#include <cctype>
#include <chrono>
#include <cstdlib>
#include <cstring>
#include <experimental/filesystem>
#include <fstream>
#include <iostream>
#include <memory>
#include <mutex>
#include <sstream>
#include <string>
#include <thread>
#include <vector>
#include "Application.h"
#include "Logger.h"
#include "Poco/Thread.h"
#include "linux-system-usage.hpp"
using namespace get_system_usage_linux;
namespace Util
{
/*
*******************************************************************************
* CLASS DECLARE
*******************************************************************************
*/
unsigned SystemInfo::cpuUsage()
{
CPU_stats t1 = read_cpu_data();
Poco::Thread::sleep(100);
CPU_stats t2 = read_cpu_data();
return (unsigned)(100.0f * get_cpu_usage(t1, t2));
}
SystemInfo::MemoryInfo SystemInfo::readMemoryInfo()
{
auto memory_data = read_memory_data();
MemoryInfo info;
info.total = memory_data.total_memory;
info.free = memory_data.free_memory;
info.usage = 100.0f * memory_data.get_memory_usage();
info.available = memory_data.available_memory;
return info;
}
std::map<std::string, Util::SystemInfo::StorageInfo> SystemInfo::getStorageInfo(bool returnSpace)
{
std::ifstream mounts("/proc/mounts");
std::string line;
std::map<std::string, StorageInfo> storageMap;
if (!mounts.is_open())
{
logger().error("Failed to open /proc/mounts");
return storageMap;
}
while (std::getline(mounts, line))
{
std::istringstream iss(line);
std::string device, path;
iss >> device >> path;
std::string deviceType = device.find("/dev/sd") == 0 ? "disk" : "";
deviceType = !deviceType.empty() ? deviceType : (device.find("/dev/mmcblk") == 0 ? "mmcblk" : "");
deviceType = !deviceType.empty() ? deviceType : (path.find("/mnt/storages/nfs") == 0 ? "nfs" : "");
deviceType = !deviceType.empty() ? deviceType : (path.find("/mnt/storages/cifs") == 0 ? "cifs" : "");
if (!deviceType.empty())
{
try
{
if (!mountPointEnable(path))
{
logger().error("%s Mount point is not available", path);
continue;
}
StorageInfo storageInfo;
storageInfo.device = device;
storageInfo.path = path;
storageInfo.status = 1;
storageInfo.type = deviceType;
if (returnSpace)
{
struct statvfs stat;
int ret_code = statvfs(path.c_str(), &stat);
if (ret_code != 0)
{
int err = errno;
logger().error("Failed to get disk usage information for: %s", strerror(err));
}
else
{
unsigned long long total = stat.f_blocks * stat.f_frsize;
unsigned long long free = stat.f_bfree * stat.f_frsize;
unsigned long long available = stat.f_bavail * stat.f_frsize;
unsigned long long used = total - free;
storageInfo.total = total;
storageInfo.used = used;
storageInfo.available = available;
storageInfo.free = free;
}
}
storageMap[device] = storageInfo;
}
catch (const std::exception& e)
{
logger().error("SystemInfo::getStorageInfo erorr: %s", std::string(e.what()));
}
}
}
auto blocks = getBlockDevices();
for (const auto& block : blocks)
{
if (block.empty() || block.back() != '1')
{
continue;
}
auto it = storageMap.find(block);
if (it == storageMap.end())
{
StorageInfo storageInfo;
storageInfo.status = 2;
storageInfo.device = block;
storageMap[block] = storageInfo;
}
}
return storageMap;
}
bool isBlockDevice(const std::string& path)
{
struct stat statbuf;
if (stat(path.c_str(), &statbuf) != 0)
{
return false;
}
return S_ISBLK(statbuf.st_mode);
}
std::vector<std::string> SystemInfo::getBlockDevices()
{
std::vector<std::string> blockDevices;
std::ifstream file("/proc/partitions");
std::string line;
while (std::getline(file, line))
{
std::istringstream iss(line);
std::string major, minor, blocks, name;
if (!(iss >> major >> minor >> blocks >> name))
{
continue;
}
std::string devicePath = "/dev/" + name;
if (devicePath.find("/dev/sd") == 0 && isBlockDevice(devicePath))
{
blockDevices.push_back(devicePath);
}
}
return blockDevices;
}
std::vector<Util::SystemInfo::FileInfo> SystemInfo::getFileInfoList(std::string parentPath)
{
std::vector<Util::SystemInfo::FileInfo> fileInfoList;
Poco::Path path(parentPath);
if (!path.isDirectory())
{
logger().error("Error: %s is not a directory.", parentPath);
return fileInfoList;
}
try
{
Poco::DirectoryIterator it(parentPath);
Poco::DirectoryIterator end;
while (it != end)
{
const Poco::Path& p = it.path();
Util::SystemInfo::FileInfo fileInfo;
fileInfo.name = p.getFileName();
fileInfo.parentPath = parentPath;
fileInfo.type = it->isDirectory() ? 2 : (it->isFile() ? 1 : 0);
if (it->isDirectory())
{
}
else if (it->isFile())
{
Poco::File file(p.toString());
fileInfo.size = file.getSize();
fileInfo.lastModifyTime = file.getLastModified().epochTime();
}
++it;
fileInfoList.push_back(fileInfo);
}
}
catch (Poco::Exception& e)
{
logger().error("An error occurred: %s", e.displayText());
}
return fileInfoList;
}
unsigned long long Util::SystemInfo::getPathFreeSpace(const std::string& path)
{
struct statvfs stat;
if (statvfs(path.c_str(), &stat) != 0)
{
logger().error("Failed to get filesystem stats for %s", path);
return 0;
}
unsigned long long free = stat.f_bfree * stat.f_frsize;
return free;
}
unsigned long long Util::SystemInfo::checkSizeAndDelEarliestFile(const std::string& basePath,
const unsigned int limitMB,
const std::string deletePath,
const std::string& deleteExtName,
const std::set<std::string> exculdePathOrFiles)
{
int i = 10;
unsigned long long freeBytes = 0;
while (i-- > 0)
{
freeBytes = getPathFreeSpace(basePath);
unsigned long long limitBytes = static_cast<unsigned long long>(limitMB) * 1024 * 1024;
logger().information(" freeBytes=%s, limitBytes=%s", std::to_string(freeBytes), std::to_string(limitBytes));
if (freeBytes < limitBytes)
{
logger().information(" Space is less than=%s. Deleting earliest file...", std::to_string(limitMB));
try
{
std::string findCmd = "find " + deletePath + " -type f -name \"*" + deleteExtName +
"\" -exec stat -c '%mY %n' {} + | sort -n | head -n 1 | cut -d' ' -f2-";
logger().information("Execute command: %s", findCmd);
std::string oldestFile;
std::array<char, 128> buffer;
std::unique_ptr<FILE, decltype(&pclose)> pipe(popen(findCmd.c_str(), "r"), pclose);
if (!pipe)
{
throw std::runtime_error("popen() failed!");
}
while (fgets(buffer.data(), buffer.size(), pipe.get()) != nullptr)
{
oldestFile += buffer.data();
}
if (!oldestFile.empty() && oldestFile[oldestFile.size() - 1] == '\n')
{
oldestFile.erase(oldestFile.size() - 1);
}
if (!oldestFile.empty())
{
bool isExecuteDelete = true;
for (std::string name : exculdePathOrFiles)
{
if (oldestFile.find(name) == 0)
{
isExecuteDelete = false;
break;
}
}
if (isExecuteDelete)
{
try
{
Poco::File file(oldestFile);
if (file.exists())
{
file.remove();
logger().information("File deleted successfully: %s", oldestFile);
}
else
{
logger().information("File does not exist: %s", oldestFile);
}
}
catch (const Poco::Exception& ex)
{
logger().information("Failed to delete file: : %s, error:", oldestFile, ex.displayText());
}
}
else
{
logger().information("deleted old file -> exculde path or file:%s", oldestFile);
}
}
else
{
logger().information("deleted old file -> No .mp4 files found");
}
}
catch (const std::exception& e)
{
logger().error("deleted old file -> Error: ", e.what());
}
}
else
{
break;
}
}
if (freeBytes <= 0)
{
freeBytes = getPathFreeSpace(basePath);
}
return freeBytes;
}
bool Util::SystemInfo::mountPointEnable(const std::string& localPath)
{
return Util::SystemInfo::executeCommand("ls " + localPath, 5);
}
bool Util::SystemInfo::mountCifs(std::string ip, std::string remotePath, std::string localPath, std::string username,
std::string password, std::string vers, std::string charset)
{
Util::SystemInfo::umount(localPath);
Util::SystemInfo::executeCommand("rm -rf " + localPath);
Util::SystemInfo::executeCommand("mkdir -p " + localPath);
std::string _charset = charset.empty() ? "" : ",iocharset=" + charset;
std::string mountCmd = Poco::format("mount -t cifs -o username=%s,password=%s,vers=%s%s //%s%s %s", username,
password, vers, _charset, ip, remotePath, localPath);
return Util::SystemInfo::executeCommand(mountCmd);
}
bool Util::SystemInfo::mountNfs(std::string ip, std::string remotePath, std::string localPath, std::string vers)
{
Util::SystemInfo::umount(localPath);
Util::SystemInfo::executeCommand("rm -rf " + localPath);
Util::SystemInfo::executeCommand("mkdir -p " + localPath);
std::string mountCmd = Poco::format("mount -t nfs -o nolock,vers=%s %s:%s %s", vers, ip, remotePath, localPath);
return Util::SystemInfo::executeCommand(mountCmd, 5);
}
bool Util::SystemInfo::umount(std::string localPath)
{
return Util::SystemInfo::executeCommand("umount -f " + localPath);
}
int Util::SystemInfo::getProcessPID(const std::string& processName)
{
try
{
DIR* dir = opendir("/proc");
if (!dir)
{
return 0;
}
std::string targetName = processName;
size_t lastSlash = targetName.find_last_of('/');
if (lastSlash != std::string::npos)
{
targetName = targetName.substr(lastSlash + 1);
}
struct dirent* entry;
int pid = 0;
while ((entry = readdir(dir)))
{
if (entry->d_type != DT_DIR || !isdigit(entry->d_name[0]))
{
continue;
}
std::string commPath = std::string("/proc/") + entry->d_name + "/comm";
std::ifstream commFile(commPath);
if (commFile)
{
std::string commName;
if (std::getline(commFile, commName))
{
commName.erase(std::remove(commName.begin(), commName.end(), '\n'), commName.end());
if (commName == targetName)
{
pid = std::stoi(entry->d_name);
break;
}
continue;
}
}
std::string cmdlinePath = std::string("/proc/") + entry->d_name + "/cmdline";
std::ifstream cmdlineFile(cmdlinePath);
if (cmdlineFile)
{
std::string cmdline;
if (std::getline(cmdlineFile, cmdline))
{
size_t nullPos = cmdline.find('\0');
if (nullPos != std::string::npos)
{
cmdline.resize(nullPos);
}
lastSlash = cmdline.find_last_of('/');
std::string exeName = (lastSlash != std::string::npos) ? cmdline.substr(lastSlash + 1) : cmdline;
if (exeName == targetName)
{
pid = std::stoi(entry->d_name);
break;
}
}
}
}
closedir(dir);
return pid;
}
catch (const std::exception& e)
{
logger().error("getProcessPID Error: %s", std::string(e.what()));
}
return 0;
}
std::vector<std::string> Util::SystemInfo::getNetworkInterfaceNames()
{
std::vector<std::string> interfaceNames;
try
{
Poco::Net::NetworkInterface::List interfaces = Poco::Net::NetworkInterface::list(false, false);
for (const auto& interface : interfaces)
{
if (!interface.isLoopback()
&& !interface.name().empty())
{
interfaceNames.push_back(interface.name());
}
}
}
catch (const Poco::Exception& e)
{
logger().error("getNetworkInterfaceNames Error: %s", std::string(e.what()));
}
return interfaceNames;
}
Util::SystemInfo::NetworkInterfaceInfo Util::SystemInfo::getNetWorkInterfaceInfo(const std::string& interfaceName,
bool returnGateway)
{
Util::SystemInfo::NetworkInterfaceInfo info;
try
{
auto interface = Poco::Net::NetworkInterface::forName(interfaceName);
if (interface.name() == interfaceName)
{
info.name = interface.name();
info.displayName = interface.displayName();
info.ipAddress = interface.address().toString();
info.subnetMask = interface.subnetMask().toString();
info.broadcastAddress = interface.broadcastAddress().toString();
auto macAddress = interface.macAddress();
info.macAddress = Poco::format("%02X:%02X:%02X:%02X:%02X:%02X", (unsigned)macAddress[0],
(unsigned)macAddress[1], (unsigned)macAddress[2], (unsigned)macAddress[3],
(unsigned)macAddress[4], (unsigned)macAddress[5]);
info.isUp = interface.isUp();
if (returnGateway)
{
info.gateway = gatewayAddress(interfaceName).toString();
}
info.isInitialized = true;
}
}
catch (const Poco::Exception& e)
{
logger().error("Error getNetWorkInterfaceInfo info: %s", std::string(e.what()));
}
return info;
}
Poco::Net::IPAddress Util::SystemInfo::localAddress(const std::string& interfaceName)
{
try
{
return Poco::Net::NetworkInterface::forName(interfaceName).address();
}
catch (...)
{
return Poco::Net::IPAddress("0.0.0.0");
}
}
std::map<std::string, Poco::Net::IPAddress> Util::SystemInfo::localAddresses()
{
std::map<std::string, Poco::Net::IPAddress> ipMap;
try
{
Poco::Net::NetworkInterface::List interfaces = Poco::Net::NetworkInterface::list();
for (const auto& interface : interfaces)
{
if (interface.isUp() && !interface.isLoopback() && !interface.address().isWildcard() &&
interface.address().isUnicast() && interface.address().family() == Poco::Net::AddressFamily::IPv4)
{
ipMap[interface.name()] = interface.address();
}
}
}
catch (const Poco::Exception& e)
{
logger().error("localAddresses Error: %s", std::string(e.what()));
}
return ipMap;
}
std::vector<std::string> Util::SystemInfo::localAddressesList()
{
std::vector<std::string> ipList;
try
{
Poco::Net::NetworkInterface::List interfaces = Poco::Net::NetworkInterface::list();
for (const auto& interface : interfaces)
{
if (interface.isUp() && !interface.isLoopback())
{
Poco::Net::NetworkInterface::AddressList addresses = interface.addressList();
for (const auto& addressInfo : addresses)
{
const Poco::Net::IPAddress& ip = addressInfo.get<0>();
if (!ip.isWildcard() && ip.isUnicast() && ip.family() == Poco::Net::AddressFamily::IPv4)
{
ipList.push_back(ip.toString());
}
}
}
}
}
catch (const Poco::Exception& e)
{
logger().error("localAddresses Error: %s", std::string(e.what()));
}
return ipList;
}
std::string Util::SystemInfo::localAddressOfAvailable()
{
std::map<std::string, Poco::Net::IPAddress> ipMap = localAddresses();
std::string hostIp = "0.0.0.0";
if (!ipMap.empty())
{
hostIp = ipMap.begin()->second.toString();
}
return hostIp;
}
std::string Util::SystemInfo::getFirstWirelessInterfaceName()
{
auto interfaces = getNetworkInterfaceNames();
for (auto& name : interfaces)
{
std::vector<std::string> wirelessPatterns = {"wlan", "wi-fi", "wireless", "wifi", "ath", "ra", "wlp", "wlan"};
for (const auto& pattern : wirelessPatterns)
{
if (Poco::toLower(name).find(pattern) != std::string::npos)
{
return name;
}
}
}
return "";
}
Poco::Net::IPAddress Util::SystemInfo::subnetMask(const std::string& interfaceName)
{
try
{
return Poco::Net::NetworkInterface::forName(interfaceName).subnetMask();
}
catch (...)
{
return Poco::Net::IPAddress("0.0.0.0");
}
}
Poco::Net::NetworkInterface::MACAddress Util::SystemInfo::macAddress(const std::string& interfaceName)
{
try
{
return Poco::Net::NetworkInterface::forName(interfaceName).macAddress();
}
catch (...)
{
return Poco::Net::NetworkInterface::MACAddress();
}
}
Poco::Net::NetworkInterface::MACAddress Util::SystemInfo::getFirstMacAddress()
{
auto interfaces = getNetworkInterfaceNames();
try
{
for (const auto& name : interfaces)
{
return Poco::Net::NetworkInterface::forName(name).macAddress();
}
return Poco::Net::NetworkInterface::MACAddress();
}
catch (...)
{
return Poco::Net::NetworkInterface::MACAddress();
}
}
bool Util::SystemInfo::netInterfaceIsExist(const std::string& interfaceName)
{
if (interfaceName.empty())
{
return false;
}
std::vector<std::string> interfaceNames = Util::SystemInfo::getNetworkInterfaceNames();
for (const auto& name : interfaceNames)
{
if (interfaceName == name)
{
return true;
}
}
return false;
}
bool Util::SystemInfo::netInterfaceIsAvailable(const std::string& interfaceName)
{
std::vector<std::string> interfaceNames;
try
{
Poco::Net::NetworkInterface::List interfaces = Poco::Net::NetworkInterface::list(false, false);
for (const auto& interface : interfaces)
{
if (!interface.isLoopback() && interface.isUp() && !interface.name().empty())
{
if (interfaceName == interface.name())
{
return true;
}
}
}
}
catch (const Poco::Exception& e)
{
logger().error("netInterfaceIsAvailable Error: %s", std::string(e.what()));
}
return false;
}
Poco::Net::IPAddress Util::SystemInfo::gatewayAddress(const std::string& interfaceName)
{
#define BUFLEN 20480
Poco::Net::IPAddress invalid("0.0.0.0");
int fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
if (fd < 0)
{
logger().error("NETLINK socket error");
return invalid;
}
char req[BUFLEN] = {0};
struct nlmsghdr* nh = (struct nlmsghdr*)req;
nh->nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg));
nh->nlmsg_type = RTM_GETROUTE;
nh->nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP;
struct rtmsg* rtm = (struct rtmsg*)NLMSG_DATA(nh);
rtm->rtm_family = AF_INET;
rtm->rtm_table = RT_TABLE_MAIN;
struct sockaddr_nl nladdr = {0};
nladdr.nl_family = AF_NETLINK;
if (bind(fd, (struct sockaddr*)&nladdr, sizeof(nladdr)) < 0)
{
logger().error("NETLINK bind error");
close(fd);
return invalid;
}
if (send(fd, req, sizeof(req), 0) < 0)
{
logger().error("NETLINK send error");
close(fd);
return invalid;
}
char buf[BUFLEN] = {0};
struct iovec iov = {buf, sizeof(buf)};
struct sockaddr_nl sa;
struct msghdr msg = {&sa, sizeof(sa), &iov, 1, NULL, 0, 0};
int len = recvmsg(fd, &msg, 0);
if (len < 0)
{
logger().error("NETLINK recvmsg error");
close(fd);
return invalid;
}
struct nlmsghdr* nhp;
for (nhp = (struct nlmsghdr*)buf; NLMSG_OK(nhp, len); nhp = NLMSG_NEXT(nhp, len))
{
struct rtmsg* rtp = (struct rtmsg*)NLMSG_DATA(nhp);
if (rtp->rtm_table != RT_TABLE_MAIN || rtp->rtm_family != AF_INET)
{
continue;
}
struct rtattr* attr = (struct rtattr*)RTM_RTA(rtp);
int attrlen = RTM_PAYLOAD(nhp);
for (; RTA_OK(attr, attrlen); attr = RTA_NEXT(attr, attrlen))
{
if (attr->rta_type != RTA_GATEWAY)
{
continue;
}
uint32_t gateway = *(uint32_t*)RTA_DATA(attr);
char gwstr[INET_ADDRSTRLEN];
inet_ntop(AF_INET, &gateway, gwstr, sizeof(gwstr));
close(fd);
return Poco::Net::IPAddress(std::string(gwstr));
}
}
close(fd);
return invalid;
}
bool Util::SystemInfo::executeCommand(const std::string& command, int timeout_sec, std::string& output)
{
if (timeout_sec <= 0)
{
std::unique_ptr<FILE, decltype(&pclose)> pipe(popen(command.c_str(), "r"), pclose);
if (!pipe)
{
logger().error("Failed to execute command: %s", command);
return false;
}
char buffer[128];
while (fgets(buffer, sizeof(buffer), pipe.get()) != nullptr)
{
output += buffer;
}
int status = pclose(pipe.release());
int ret = WEXITSTATUS(status);
if (ret != 0)
{
logger().error("Command failed: %s, exit code: %d, error: %s", command, ret, output);
return false;
}
logger().information("Command succeeded: %s, exit code: %d", command, ret);
return true;
}
output.clear();
std::string timeoutCommand = "timeout -s KILL " + std::to_string(timeout_sec) + " " + command;
timeoutCommand += " 2>/dev/null || timeout -s KILL -t " + std::to_string(timeout_sec) + " " + command;
std::unique_ptr<FILE, decltype(&pclose)> pipe(popen(timeoutCommand.c_str(), "r"), pclose);
if (!pipe)
{
logger().error("2Failed to execute command: %s", timeoutCommand);
return false;
}
char buffer[128];
while (fgets(buffer, sizeof(buffer), pipe.get()) != nullptr)
{
output += buffer;
}
int status = pclose(pipe.release());
int ret = WEXITSTATUS(status);
if (ret != 0)
{
if (ret == 124 || output.find("time") != std::string::npos)
{
logger().error("2Command timeout: %s", timeoutCommand);
}
else
{
logger().error("2Command failed: %s, exit code: %d, error: %s", timeoutCommand, ret, output);
}
return false;
}
logger().information("2Command succeeded: %s, exit code: %d", timeoutCommand, ret);
return true;
}
}

140
util/SystemInfo.h Normal file
View file

@ -0,0 +1,140 @@
#ifndef C42159C1_B275_444E_86D0_9A26B781F354
#define C42159C1_B275_444E_86D0_9A26B781F354
/*
*******************************************************************************
* INCLUDE
*******************************************************************************
*/
#include <Poco/Net/IPAddress.h>
#include <Poco/Net/NetworkInterface.h>
#include <stdint.h>
#include <ctime>
#include "util/xpack/json.h"
namespace Util
{
/*
*******************************************************************************
* CLASS DECLARE
*******************************************************************************
*/
class SystemInfo
{
public:
struct MemoryInfo
{
uint32_t total;
uint32_t free;
uint32_t usage;
uint32_t available;
};
struct StorageInfo
{
std::string type = "";
int status = 0;
std::string device = "";
std::string path = "";
unsigned long long total = 0;
unsigned long long used = 0;
unsigned long long available = 0;
unsigned long long free = 0;
StorageInfo() = default;
XPACK(O(type, status, device, path, total, used, available, free));
};
struct FileInfo
{
std::string name;
std::string parentPath = "";
int type;
off_t size;
time_t lastModifyTime;
XPACK(O(name, parentPath, type, size, lastModifyTime));
};
struct NetworkInterfaceInfo
{
std::string name;
std::string displayName;
std::string ipAddress = "0.0.0.0";
std::string subnetMask = "0.0.0.0";
std::string broadcastAddress;
std::string gateway = "0.0.0.0";
std::string macAddress;
bool isUp;
bool isInitialized;
NetworkInterfaceInfo() = default;
XPACK(O(name, displayName, ipAddress, subnetMask, broadcastAddress, gateway, macAddress, isUp));
};
public:
static unsigned cpuUsage();
static MemoryInfo readMemoryInfo();
static std::map<std::string, Util::SystemInfo::StorageInfo> getStorageInfo(bool returnSpace = true);
static std::vector<std::string> getBlockDevices();
static std::vector<Util::SystemInfo::FileInfo> getFileInfoList(std::string parentPath);
static unsigned long long getPathFreeSpace(const std::string& path);
static unsigned long long checkSizeAndDelEarliestFile(const std::string& basePath, const unsigned int limitMB,
const std::string deletePath,
const std::string& deleteExtName,
const std::set<std::string> exculdePathOrFiles);
static bool mountPointEnable(const std::string& localPath);
static bool mountCifs(std::string ip, std::string remotePath, std::string localPath, std::string username,
std::string password, std::string vers = "2.0", std::string charset = "");
static bool mountNfs(std::string ip, std::string remotePath, std::string localPath, std::string vers = "4");
static bool umount(std::string localPath);
static int getProcessPID(const std::string& processName);
static std::vector<std::string> getNetworkInterfaceNames();
static NetworkInterfaceInfo getNetWorkInterfaceInfo(const std::string& interfaceName, bool returnGateway = false);
static bool netInterfaceIsExist(const std::string& interfaceName);
static bool netInterfaceIsAvailable(const std::string& interfaceName);
static Poco::Net::IPAddress localAddress(const std::string& interfaceName);
static std::map<std::string, Poco::Net::IPAddress> localAddresses();
static std::vector<std::string> localAddressesList();
static std::string localAddressOfAvailable();
static std::string getFirstWirelessInterfaceName();
static Poco::Net::IPAddress subnetMask(const std::string& interfaceName);
static Poco::Net::NetworkInterface::MACAddress macAddress(const std::string& interfaceName);
static Poco::Net::NetworkInterface::MACAddress getFirstMacAddress();
static Poco::Net::IPAddress gatewayAddress(const std::string& interfaceName);
static bool executeCommand(const std::string& command)
{
return executeCommand(command, 0);
}
static bool executeCommand(const std::string& command, int timeout_sec)
{
std::string output;
return executeCommand(command, timeout_sec, output);
}
static bool executeCommand(const std::string& command, int timeout_sec, std::string& output);
};
}
#endif

62
util/Task.cpp Normal file
View file

@ -0,0 +1,62 @@
/*
*******************************************************************************
* INCLUDE
*******************************************************************************
*/
#include "Task.h"
#include "Logger.h"
namespace Util
{
/*
*******************************************************************************
* CLASS DEFINE
*******************************************************************************
*/
Task::Task(const std::string& name): _name(name)
{
}
void Task::run(std::function<void(std::atomic<bool>& isCancelled)> func)
{
cancel();
Poco::FastMutex::ScopedLock lock(_lock);
_pThread = new Poco::Thread();
_isCancelled = false;
_pThread->startFunc(
[this, func]() -> void
{
try
{
func(_isCancelled);
}
catch (Poco::Exception& e)
{
logger().error("Task(%s): %s", name(), e.displayText());
}
catch (std::exception& e)
{
logger().error("Task(%s): %s", name(), std::string(e.what()));
}
});
}
void Task::cancel()
{
if (isRunning())
{
Poco::FastMutex::ScopedLock lock(_lock);
_isCancelled = true;
_pThread->join();
_pThread.reset();
}
}
bool Task::isRunning()
{
Poco::FastMutex::ScopedLock lock(_lock);
return !_pThread.isNull() && _pThread->isRunning();
}
}

93
util/Task.h Normal file
View file

@ -0,0 +1,93 @@
#ifndef EBDC071F_8F58_4C5D_ACB9_7C534616BEC5
#define EBDC071F_8F58_4C5D_ACB9_7C534616BEC5
/*
*******************************************************************************
* INCLUDE
*******************************************************************************
*/
#include <Poco/Thread.h>
#include <exception>
#include <functional>
#include <iostream>
#include <string>
#include <thread>
#include <tuple>
#include <utility>
namespace Util
{
/*
*******************************************************************************
* CLASS DECLARE
*******************************************************************************
*/
class Task
{
public:
Task(const std::string& name = "Anonymous");
public:
void run(std::function<void(std::atomic<bool>& isCancelled)> func);
template <typename Func, typename... Args> static void run2(Func&& func, Args&&... args)
{
run2WithError(std::forward<Func>(func), nullptr, std::forward<Args>(args)...);
}
template <typename Func, typename... Args>
static void run2WithError(Func&& func, std::function<void(std::exception_ptr)> onError, Args&&... args)
{
auto boundFunc = std::bind(std::forward<Func>(func), std::forward<Args>(args)...);
std::thread(
[boundFunc, onError]() mutable
{
try
{
boundFunc();
}
catch (const std::exception& e)
{
if (onError)
{
onError(std::current_exception());
}
else
{
std::cerr << "Util::Task::run2 exception: " << e.what() << std::endl;
}
}
catch (...)
{
if (onError)
{
onError(std::current_exception());
}
else
{
std::cerr << "Util::Task::run2 exception!" << std::endl;
}
}
})
.detach();
}
void cancel();
bool isRunning();
const std::string& name() const;
private:
const std::string _name;
std::atomic<bool> _isCancelled;
Poco::FastMutex _lock;
Poco::SharedPtr<Poco::Thread> _pThread;
};
inline const std::string& Task::name() const
{
return _name;
}
}
#endif

38
util/Temperature.cpp Normal file
View file

@ -0,0 +1,38 @@
/*
*******************************************************************************
* INCLUDE
*******************************************************************************
*/
#include "Temperature.h"
#include "Device.h"
#include "Application.h"
namespace Util
{
/*
*******************************************************************************
* CLASS DEFINE
*******************************************************************************
*/
float Temperature::read()
{
Device device("/sys/devices/virtual/mstar/msys/TEMP_R");
try
{
device.open();
char str[64];
device.read((uint8_t*)str, sizeof(str));
int temp;
sscanf(str, "Temperature %d", &temp);
device.close();
return temp;
}
catch (const std::exception& e)
{
}
return 0.0f;
}
}

22
util/Temperature.h Normal file
View file

@ -0,0 +1,22 @@
#ifndef AE8A4A92_D487_4675_9A6A_0B266E47C520
#define AE8A4A92_D487_4675_9A6A_0B266E47C520
/*
*******************************************************************************
* INCLUDE
*******************************************************************************
*/
namespace Util
{
/*
*******************************************************************************
* CLASS DECLARE
*******************************************************************************
*/
class Temperature
{
public:
static float read();
};
}
#endif

169
util/WifiUtil.h Normal file
View file

@ -0,0 +1,169 @@
#ifndef WIFI_UTIL_H
#define WIFI_UTIL_H
#include <Poco/JSON/Array.h>
#include <Poco/JSON/Object.h>
#include <Poco/StringTokenizer.h>
#include <sstream>
#include <vector>
#include "Application.h"
#include "util/SystemInfo.h"
namespace Util
{
class WifiUtil
{
private:
public:
static constexpr const char* frpcConfigPathName = "/customer/frp/frpc.toml";
static constexpr const char* frpcPathName = "/customer/frp/frpc";
public:
static Poco::JSON::Object::Ptr wifiStaStatus(const std::string& interfaceName)
{
Poco::JSON::Object::Ptr statusMap = new Poco::JSON::Object;
try
{
std::string retStr;
bool retFlag = Util::SystemInfo::executeCommand("wpa_cli -i " + interfaceName + " status", 10, retStr);
if (retFlag)
{
Poco::StringTokenizer lines(retStr, "\n", Poco::StringTokenizer::TOK_TRIM);
for (const auto& line : lines)
{
size_t eqPos = line.find('=');
if (eqPos != std::string::npos)
{
std::string key = line.substr(0, eqPos);
std::string value = line.substr(eqPos + 1);
statusMap->set(key, value);
}
}
}
}
catch (const std::exception& e)
{
logger().error("wifiStaStatus failed: %s", std::string(e.what()));
}
return statusMap;
}
static bool wifiStaScan(const std::string& interfaceName, bool wifiEnable = false)
{
try
{
return Util::SystemInfo::executeCommand("/customer/customer-network.sh " + interfaceName + " scan");
}
catch (const std::exception& e)
{
logger().error("wifiStaScan failed: %s", std::string(e.what()));
}
return false;
}
static Poco::JSON::Array::Ptr wifiStaScanResults(const std::string& interfaceName)
{
Poco::JSON::Array::Ptr jsonArray = new Poco::JSON::Array;
std::string retStr;
bool retFlag = Util::SystemInfo::executeCommand("wpa_cli -i " + interfaceName + " scan_results", 10, retStr);
if (!retFlag)
{
return jsonArray;
}
Poco::StringTokenizer lines(retStr, "\n", Poco::StringTokenizer::TOK_TRIM);
for (size_t i = 1; i < lines.count(); ++i)
{
Poco::StringTokenizer tokens(lines[i], "\t", Poco::StringTokenizer::TOK_TRIM);
if (tokens.count() < 4)
continue;
Poco::JSON::Object::Ptr netObj = new Poco::JSON::Object;
netObj->set("bssid", tokens[0]);
netObj->set("frequency", std::stoi(tokens[1]));
netObj->set("signal_level", std::stoi(tokens[2]));
if (tokens.count() == 4 && tokens[3].find('[') == std::string::npos)
{
netObj->set("flags", "OPEN");
netObj->set("ssid", convertHexEscape(tokens[3]));
}
else
{
netObj->set("flags", tokens[3]);
netObj->set("ssid", tokens.count() > 4 ? convertHexEscape(tokens[4]) : "");
}
jsonArray->add(netObj);
}
return jsonArray;
}
static bool wifiStaConnect(const std::string& interfaceName, int status)
{
if (status == 1)
{
return Util::SystemInfo::executeCommand("/customer/customer-network.sh " + interfaceName + " up");
}
else if (status == 0)
{
return Util::SystemInfo::executeCommand("/customer/customer-network.sh " + interfaceName + " down");
}
return false;
}
static Poco::JSON::Array::Ptr wifiGetApClients(const std::string& interfaceName)
{
Poco::JSON::Array::Ptr jsonArray = new Poco::JSON::Array;
std::string retStr;
bool retFlag = Util::SystemInfo::executeCommand(
"ip neigh show dev " + interfaceName + " nud reachable | awk '{print $1, $3}'", 10, retStr);
if (!retFlag)
{
return jsonArray;
}
Poco::StringTokenizer lines(retStr, "\n", Poco::StringTokenizer::TOK_TRIM);
for (size_t i = 0; i < lines.count(); ++i)
{
if (lines[i].empty())
continue;
Poco::StringTokenizer tokens(lines[i], " ", Poco::StringTokenizer::TOK_TRIM);
if (tokens.count() < 2)
{
continue;
}
std::string ip = tokens[0], mac = tokens[1];
Poco::JSON::Object::Ptr netObj = new Poco::JSON::Object;
netObj->set("ip", ip);
netObj->set("mac", mac);
jsonArray->add(netObj);
}
return jsonArray;
}
static std::string convertHexEscape(const std::string& input)
{
std::stringstream ss;
for (size_t i = 0; i < input.length();)
{
if (input.substr(i, 2) == "\\x" && i + 3 < input.length())
{
ss << static_cast<char>(std::stoi(input.substr(i + 2, 2), nullptr, 16));
i += 4;
}
else
{
ss << input[i];
i++;
}
}
return ss.str();
}
};
}
#endif

203
util/linux-system-usage.hpp Normal file
View file

@ -0,0 +1,203 @@
/* ---------------------------------------------------------------------
* Luiz Carlos doleron @ 2021
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero Public License version 3 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU Affero Public License for more details.
* ----------------------------------------------------------------------
*/
#ifndef _GET_CPU_USAGE_LINUX_
#define _GET_CPU_USAGE_LINUX_
#include <iostream>
#include <fstream>
#include <sstream>
#include <sys/statvfs.h>
namespace get_system_usage_linux
{
struct CPU_stats
{
// see http://www.linuxhowtos.org/manpages/5/proc.htm
int user;
int nice;
int system;
int idle;
int iowait;
int irq;
int softirq;
int steal;
int guest;
int guest_nice;
int get_total_idle()
const {
return idle + iowait;
}
int get_total_active()
const {
return user + nice + system + irq + softirq + steal + guest + guest_nice;
}
};
struct Memory_stats
{
int total_memory;
int available_memory;
int free_memory;
int total_swap;
int free_swap;
float get_memory_usage() {
const float result = static_cast<float>(total_memory - available_memory) / total_memory;
return result;
}
float get_swap_usage() {
const float result = static_cast<float>(total_swap - free_swap) / total_swap;
return result;
}
};
inline CPU_stats read_cpu_data()
{
CPU_stats result;
std::ifstream proc_stat("/proc/stat");
if (proc_stat.good())
{
std::string line;
getline(proc_stat, line);
unsigned int *stats_p = (unsigned int *)&result;
std::stringstream iss(line);
std::string cpu;
iss >> cpu;
while (iss >> *stats_p)
{
stats_p++;
};
}
proc_stat.close();
return result;
}
inline int get_val(const std::string &target, const std::string &content) {
int result = -1;
std::size_t start = content.find(target);
if (start != std::string::npos) {
int begin = start + target.length();
std::size_t end = content.find("kB", start);
std::string substr = content.substr(begin, end - begin);
result = std::stoi(substr);
}
return result;
}
inline Memory_stats read_memory_data()
{
Memory_stats result;
std::ifstream proc_meminfo("/proc/meminfo");
if (proc_meminfo.good())
{
std::string content((std::istreambuf_iterator<char>(proc_meminfo)),
std::istreambuf_iterator<char>());
result.total_memory = get_val("MemTotal:", content);
result.free_memory = get_val("MemFree:", content);
result.total_swap = get_val("SwapTotal:", content);
result.free_swap = get_val("SwapFree:", content);
result.available_memory = get_val("MemAvailable:", content);
}
proc_meminfo.close();
return result;
}
inline float get_cpu_usage(const CPU_stats &first, const CPU_stats &second) {
const float active_time = static_cast<float>(second.get_total_active() - first.get_total_active());
const float idle_time = static_cast<float>(second.get_total_idle() - first.get_total_idle());
const float total_time = active_time + idle_time;
return active_time / total_time;
}
inline float get_disk_usage(const std::string & disk) {
struct statvfs diskData;
statvfs(disk.c_str(), &diskData);
auto total = diskData.f_blocks;
auto free = diskData.f_bfree;
auto diff = total - free;
float result = static_cast<float>(diff) / total;
return result;
}
//see https://unix.stackexchange.com/questions/304845/discrepancy-between-number-of-cores-and-thermal-zones-in-sys-class-thermal/342023
inline int find_thermalzone_index() {
int result = 0;
bool stop = false;
// 20 must stop anyway
for (int i = 0; !stop && i < 20; ++i) {
std::ifstream thermal_file("/sys/class/thermal/thermal_zone" + std::to_string(i) + "/type");
if (thermal_file.good())
{
std::string line;
getline(thermal_file, line);
if (line.compare("x86_pkg_temp") == 0) {
result = i;
stop = true;
}
} else {
stop = true;
}
thermal_file.close();
}
return result;
}
inline int get_thermalzone_temperature(int thermal_index) {
int result = -1;
std::ifstream thermal_file("/sys/class/thermal/thermal_zone" + std::to_string(thermal_index) + "/temp");
if (thermal_file.good())
{
std::string line;
getline(thermal_file, line);
std::stringstream iss(line);
iss >> result;
} else {
throw std::invalid_argument(std::to_string(thermal_index) + " doesn't refer to a valid thermal zone.");
}
thermal_file.close();
return result;
}
}
#endif

711
util/structs/ParamStruct.h Normal file
View file

@ -0,0 +1,711 @@
#ifndef PARAMSTRUCT_H
#define PARAMSTRUCT_H
#include <string>
#include <vector>
#include "Application.h"
#include "util/xpack/json.h"
namespace Util
{
namespace Structs
{
struct Channel_stream
{
std::string sn;
int enable = 0;
std::string name;
std::string appname;
std::string streamid;
int bitrate = 8192;
std::string bitrate_ctl;
std::string codec;
int encoded_size_mode = 0;
std::string encoded_size;
int fps = 30;
int gopmode = 0;
int goptxt = 30;
int video_flip_mirror_enable = 0;
int video_clipping_enable = 0;
int video_clipping_left = 0;
int video_clipping_right = 0;
int video_clipping_width = 100;
int video_clipping_height = 100;
int video_rotate = 0;
std::string pkey;
std::string rtmp_url;
std::string rtsp_url;
std::string flv_url;
std::string hls_url;
std::string webrtc_url;
std::string ts_url;
std::string srt_url;
std::string h5_url;
int multicast_enable = 1;
std::string multicast_ip_port;
int push_enable = 1;
std::string push_url;
int push_connected = 0;
Channel_stream() = default;
XPACK(O(enable, sn, name, appname, streamid, bitrate, bitrate_ctl, codec, encoded_size_mode, encoded_size, fps,
gopmode, goptxt, video_flip_mirror_enable, video_clipping_enable, video_clipping_left, video_clipping_right,
video_clipping_width, video_clipping_height, video_rotate, pkey, rtmp_url, rtsp_url, flv_url, hls_url,
webrtc_url, ts_url, srt_url, multicast_enable, multicast_ip_port, push_enable, push_url),
C(c_push_connected, F(0), push_connected))
template <class Decoder>
bool c_push_connected_decode(Decoder& de, Channel_stream& t, const char* name, int& data, const xpack::Extend* ext)
{
(void)t;
bool ret = false;
try
{
std::string raw;
ret = de.decode(name, raw, ext);
if (ret)
{
sscanf(raw.c_str(), "%i", &data);
}
}
catch (const std::exception& e)
{ // TODO:如果原则本身就是整数,会转换失败,怎么办???
ret = de.decode(name, data, ext);
}
return ret;
}
template <class Encoder>
bool c_push_connected_encode(Encoder& en, const Channel_stream& t, const char* name, const int& data,
const xpack::Extend* ext)
{
(void)t;
bool ret = en.encode(name, data, ext);
return ret;
}
};
struct Channel_input_type_mix_layer
{
int chn_id = 0;
int stream_id = 0;
int audio_status = 0;
float pos_x = 0.0f;
float pos_y = 0.0f;
float pos_w = 0.3333f;
float pos_h = 0.3333f;
int z_index = 0;
Channel_input_type_mix_layer() = default;
XPACK(O(chn_id, stream_id, audio_status, pos_x, pos_y, pos_w, pos_h, z_index))
};
struct Channel_input_type_mix
{
std::string layout_id;
std::vector<Channel_input_type_mix_layer> layers;
Channel_input_type_mix() = default;
XPACK(O(layout_id, layers))
};
struct Channel_input_type_ndi
{
std::string ndirecv_name;
Channel_input_type_ndi() = default;
XPACK(O(ndirecv_name))
};
struct Channel_input_type_net
{
int stream_mode = 0;
int min_delay = 500;
std::string path;
int rtp_type = 0;
std::string appname;
std::string streamid;
std::string push_remote_ip;
std::string pull_path;
int proxy_status;
int ipc_onvif = 0;
std::string ipc_ip;
int ipc_port = 8000;
int ipc_web_port = 80;
std::string ipc_username = "admin";
std::string ipc_password;
std::string ipc_ptz_xaddr;
std::string ipc_ptz_profile_token;
Channel_input_type_net() = default;
XPACK(O(stream_mode, min_delay, path, rtp_type, pull_path, proxy_status, appname, streamid, push_remote_ip,
ipc_onvif, ipc_ip, ipc_port, ipc_web_port, ipc_username, ipc_password, ipc_ptz_xaddr,
ipc_ptz_profile_token))
};
struct Channel_input_type_usb
{
std::string alsa;
int channel = -1;
std::string lnk;
Channel_input_type_usb() = default;
XPACK(O(alsa, channel, lnk))
};
struct Channel_audio_set
{
std::vector<int> audio_src = {0};
int audio_channel = 0;
int digital_volume_gain = 0;
int pts_offset = 0;
int encoder_bitrate = 128;
int encoder_methods = 0;
int sampling_rate = -1;
int collected_audio_frame = 0;
Channel_audio_set() = default;
XPACK(O(audio_src, audio_channel, digital_volume_gain, pts_offset, encoder_bitrate, encoder_methods, sampling_rate,
collected_audio_frame))
};
struct Channel_input_type_digimage_point
{
float x = 0;
float y = 0;
Channel_input_type_digimage_point() = default;
XPACK(O(x, y))
};
struct Channel_input_type_digimage
{
int auto_update = 0;
int tolerance = 0;
int src_fg_channel = 0;
std::string src_bg_type;
std::string src_bg_image;
int src_bg_channel = 0;
std::vector<Channel_input_type_digimage_point> point;
Channel_input_type_digimage() = default;
XPACK(O(auto_update, tolerance, src_fg_channel, src_bg_type, src_bg_image, src_bg_channel, point))
};
struct Channel_input_type_carousel_file
{
std::string filename;
int video_len = 0;
Channel_input_type_carousel_file() = default;
XPACK(O(filename, video_len))
};
struct Channel_input_type_carousel
{
int decodeA = 0;
int decodeV = 0;
int playing_file_index = -1;
long playing_file_position = 0;
std::vector<Channel_input_type_carousel_file> files;
Channel_input_type_carousel() = default;
XPACK(O(decodeA, decodeV, files), C(c_playing_file_index, F(0), playing_file_index))
template <class Decoder>
bool c_playing_file_index_decode(Decoder& de, Channel_input_type_carousel& t, const char* name, int& data,
const xpack::Extend* ext)
{
(void)t;
bool ret = false;
try
{
std::string raw;
ret = de.decode(name, raw, ext);
if (ret)
{
sscanf(raw.c_str(), "%i", &data);
}
}
catch (const std::exception& e)
{ // TODO:如果原则本身就是整数,会转换失败,怎么办???
ret = de.decode(name, data, ext);
}
return ret;
}
template <class Encoder>
bool c_playing_file_index_encode(Encoder& en, const Channel_input_type_carousel& t, const char* name,
const int& data, const xpack::Extend* ext)
{
(void)t;
bool ret = en.encode(name, data, ext);
return ret;
}
};
struct Channel_channel
{
int enable = 0;
std::string name = "HDMI";
std::string input_type = "HDMI";
int input_video_framerate = 0;
std::string input_video_size;
int input_video_collected_frame = 0;
int input_video_lost_frame = 0;
Channel_audio_set audio_set;
std::vector<Channel_stream> streams;
Channel_input_type_digimage input_type_digimage;
Channel_input_type_carousel input_type_carousel;
Channel_input_type_mix input_type_mix;
Channel_input_type_ndi input_type_ndi;
Channel_input_type_net input_type_net;
Channel_input_type_usb input_type_usb;
Channel_channel() = default;
XPACK(O(enable, name, input_type, input_video_framerate, input_video_size, input_video_collected_frame,
input_video_lost_frame, audio_set, streams, input_type_digimage, input_type_carousel, input_type_mix,
input_type_ndi, input_type_net, input_type_usb))
};
struct Channel_params
{
std::vector<Channel_channel> channels;
Channel_params() = default;
XPACK(O(channels))
};
struct Record_record
{
int enable = 0;
int flv = 0;
int mp4 = 1;
int mp4_max_second = 600;
int status = 0;
std::string start_time;
Record_record() = default;
XPACK(O(enable, flv, mp4, mp4_max_second, status, start_time))
};
struct Record_params
{
std::string base_path;
int loop_storage_enable = 0;
int loop_storage_min_size = 1024;
std::vector<std::vector<Record_record>> records;
Record_params() = default;
XPACK(O(base_path, loop_storage_enable, loop_storage_min_size, records))
};
struct Gb28181_chn
{
int enable = 0;
std::string gb_chn_id;
std::string gb_chn_name;
int stream_state = 0;
Gb28181_chn() = default;
XPACK(O(enable, gb_chn_id, gb_chn_name, stream_state))
};
struct Gb28181_params
{
int enable = 0;
std::string spec_model = "H.264/H.265 Encoder";
std::string manufacture = "Encoder";
int local_sip_port = 5061;
int con_state = 0;
std::string auth_id;
std::string auth_passwd;
int heartbeat_cycle = 30;
int heartbeat_timeout_cnt = 3;
int registration_period = 86400;
std::string sip_server_addr;
std::string sip_server_domain;
std::string sip_server_id;
int sip_server_port = 5060;
std::vector<std::vector<Gb28181_chn>> gb28181_chns;
Gb28181_params() = default;
XPACK(O(enable, spec_model, manufacture, local_sip_port, con_state, auth_id, auth_passwd, heartbeat_cycle,
heartbeat_timeout_cnt, registration_period, sip_server_addr, sip_server_domain, sip_server_id,
sip_server_port, gb28181_chns))
};
struct Loopout_audio
{
int enable = 1;
int chn_id = 0;
int audio_volume_gain;
Loopout_audio() = default;
XPACK(O(enable, chn_id, audio_volume_gain))
};
struct Loopout_video
{
int enable = 1;
int low_latency = 0;
std::string encoded_size;
int rotate = 0;
int mirror = 0;
int chn_id = 0;
int stream_id = 0;
std::string type;
int ui = 0;
int vid = 0;
std::string matrix;
int luma = 64;
int contrast = 64;
int saturation = 64;
int hue = 64;
int audio_volume_gain;
Loopout_video() = default;
XPACK(O(enable, low_latency, encoded_size, rotate, mirror, chn_id, stream_id, type, ui, vid, matrix, luma, contrast,
saturation, hue, audio_volume_gain))
};
struct Loopout_params
{
Loopout_audio audio;
Loopout_video hdmi;
Loopout_video sdi;
Loopout_params() = default;
XPACK(O(hdmi, sdi))
};
struct Mix_lay_def_layer
{
int chn_id = -1;
int stream_id = 0;
int audio_status = 0;
int lock = 0;
float pos_x = 0.0f;
float pos_y = 0.0f;
float pos_w = 0.3333f;
float pos_h = 0.3333f;
int z_index = 0;
Mix_lay_def_layer() = default;
XPACK(O(chn_id, stream_id, audio_status, lock, pos_x, pos_y, pos_w, pos_h, z_index))
};
struct Mix_lay_def_layout
{
std::string layout_id;
std::string layout_name;
int enable = 1;
std::vector<Mix_lay_def_layer> layers;
Mix_lay_def_layout() = default;
XPACK(O(layout_id, layout_name, layers))
};
struct Mix_lay_def
{
std::vector<Mix_lay_def_layout> layouts;
Mix_lay_def() = default;
XPACK(O(layouts))
};
struct Osd_osd
{
int enable = 0;
int chn_id = 0;
int stream_id = 0;
std::string type = "pic";
std::string content;
std::string font;
std::string color;
std::string bgcolor;
float alpha = 0;
float scale = 0;
int speed = 0;
float x = 0;
float y = 0;
int z = 0;
float w = 0;
float h = 0;
Osd_osd() = default;
XPACK(O(enable, chn_id, stream_id, type, content, font, color, bgcolor, alpha, scale, speed, x, y, z, w, h))
};
struct Osd_resource
{
std::string path;
std::string time;
std::string type;
Osd_resource() = default;
XPACK(O(path, time, type))
};
struct Osd_params
{
std::vector<std::vector<std::vector<Osd_osd>>> osds;
std::vector<Osd_resource> resources;
Osd_params() = default;
XPACK(O(osds, resources))
};
struct Push_url
{
int enable = 0;
std::string des;
std::string url;
int stream_state = 0;
Push_url() = default;
XPACK(O(enable, des, url, stream_state))
};
struct Push_params
{
int srcA = -1;
int srcV = 0;
int srcV_stream = 0;
int autorun = 0;
std::string start_day;
std::string start_time;
std::string stop_day;
std::string stop_time;
std::vector<Push_url> urls;
Push_params() = default;
XPACK(O(srcA, srcV, srcV_stream, autorun, start_day, start_time, stop_day, stop_time, urls))
};
struct Status_params
{
std::string version;
std::string image_version;
std::string serial_num;
float cpu_usage = 0;
float cpu_junc_tmp = 0;
float memory_free = 0;
float memory_total = 0;
std::string runtime;
std::string device_time;
int net_state_up = 0;
int net_state_dw = 0;
std::string fireware_download_url;
std::string fireware_upgrade_last_log;
Status_params() = default;
XPACK(O(version, serial_num, cpu_usage, cpu_junc_tmp, memory_free, memory_total, runtime, device_time, net_state_up,
net_state_dw, fireware_download_url, fireware_upgrade_last_log))
};
struct System_http
{
std::string user_name;
std::string user_password;
std::string language = "zh";
int api_auth = 0;
System_http() = default;
XPACK(O(user_name, user_password, language, api_auth))
};
struct System_service_ports
{
int nat_port_enable = 0;
std::string nat_port_host = "";
int http_api_port = 22112;
int http_api_port_nat_port = 22112;
int http_port = 80;
int http_port_nat_port = 80;
int rtmp_port = 1935;
int rtmp_port_nat_port = 1935;
int rtsp_port = 554;
int rtsp_port_nat_port = 554;
int srt_port = 9000;
int srt_port_nat_port = 9000;
int nat_frp_enable = 0;
int nat_frp_pid = 0;
System_service_ports() = default;
XPACK(O(nat_port_enable, nat_port_host, http_api_port, http_api_port_nat_port, http_port, http_port_nat_port,
rtmp_port, rtmp_port_nat_port, rtsp_port, rtsp_port_nat_port, srt_port, srt_port_nat_port, nat_frp_enable,
nat_frp_pid))
};
struct System_share_storage_cifs
{
int enable = 0;
std::string ip;
std::string path;
std::string username;
std::string password;
std::string local_path = "/mnt/storages/cifs";
std::string vers = "2.0";
System_share_storage_cifs() = default;
XPACK(O(enable, ip, path, username, password, local_path, vers))
};
struct System_share_storage_nfs
{
int enable = 0;
std::string ip;
std::string path;
std::string local_path = "/mnt/stroages/nfs";
std::string vers = "4";
System_share_storage_nfs() = default;
XPACK(O(enable, ip, path, local_path, vers))
};
struct System_share_storage
{
System_share_storage_cifs cifs;
System_share_storage_nfs nfs;
System_share_storage() = default;
XPACK(O(cifs, nfs))
};
struct Network_set_config
{
int enable = 0;
int dhcp_enable = 0;
std::string host_ip;
std::string mac_addr;
std::string netmask;
std::string gateway;
std::string dns1;
std::string dns2;
std::string ssid;
std::string ssid_pwd;
std::string ssid_mode = "STA";
Network_set_config() = default;
XPACK(O(enable, dhcp_enable, host_ip, mac_addr, netmask, gateway, dns1, dns2, ssid, ssid_pwd, ssid_mode))
};
struct Network_set
{
Network_set_config eth0;
Network_set_config wlan0;
Network_set() = default;
XPACK(O(eth0, wlan0))
};
struct Time_set
{
int ntp_enable = 1;
std::string ntp_server = "ntp.ntsc.ac.cn";
int ntp_server_port = 123;
int ntp_idle = 60;
std::string time_zone = "8";
Time_set() = default;
XPACK(O(ntp_enable, ntp_server, ntp_server_port, ntp_idle, time_zone))
};
struct Schedule_restart
{
int restart_enable = 0;
std::string restart_time = "06:10";
Schedule_restart() = default;
XPACK(O(restart_enable, restart_time))
};
struct System_serialtrans
{
std::string type = "232";
int work_mode = 0;
int baudrate = 9600;
std::string remote_addr;
int remote_port = 10000;
int listen_port = 10000;
System_serialtrans() = default;
XPACK(O(type, work_mode, baudrate, remote_addr, remote_port, listen_port))
};
struct System_nattraversal_frp
{
int enable = 0;
std::string remote_addr;
int remote_port = 10001;
int listen_port = 10001;
System_nattraversal_frp() = default;
XPACK(O(enable, remote_addr, remote_port, listen_port))
};
struct System_nattraversal_rtty
{
int enable = 0;
std::string name;
std::string remote_addr;
std::string id = "dfdfdsfdfdsfds";
std::string token;
System_nattraversal_rtty() = default;
XPACK(O(enable, name, remote_addr, id, token))
};
struct System_nattraversal
{
System_nattraversal_frp frp;
System_nattraversal_rtty rtty;
XPACK(O(frp, rtty))
};
struct System_moresettings
{
std::string logo_title = "H.265/H.264 Encoder";
std::string logo_image;
std::string menu_location = "left";
std::string h5play_title = "";
int screen_turn_time = 5;
System_moresettings() = default;
XPACK(O(logo_title, logo_image, menu_location, h5play_title, screen_turn_time))
};
struct System_params
{
System_http http;
Network_set network_set;
Time_set time_set;
Schedule_restart schedule_restart;
std::vector<System_serialtrans> serialtrans;
System_nattraversal nattraversal;
System_moresettings moresettings;
System_service_ports service_ports;
System_share_storage share_storage;
System_params() = default;
XPACK(O(http, network_set, time_set, schedule_restart, serialtrans, nattraversal, moresettings, service_ports,
share_storage))
};
struct Onvif_chn
{
uint32_t enable = 1;
Onvif_chn() = default;
XPACK(O(enable))
};
struct Onvif_params
{
int auth_enable = 0;
std::string auth_username = "admin";
std::string auth_password = "admin";
int time_sync_enable = 0;
uint32_t enable = 1;
std::string device_name = "H.264/H.265 Encoder";
std::string spec_model = "H.264/H.265 Encoder";
std::string manufacture = "DaweiVision";
std::string firmware_version;
std::string serial_number;
std::string hardware_id = "1.0";
std::string device_type = "Encoder";
std::string ip;
std::string mac;
uint web_service_bind_port = 8000;
std::string location_country = "China";
std::string location_city = "Chengdu";
std::vector<std::vector<Onvif_chn>> chns;
Onvif_params() = default;
XPACK(O(auth_enable, auth_username, auth_password, time_sync_enable, enable, device_name, spec_model, manufacture,
firmware_version, serial_number, hardware_id, device_type, ip, mac, web_service_bind_port, location_country,
location_city, chns))
};
struct Diskinfo
{
int status = 0;
std::string device = "";
std::string path = "";
unsigned long long total = 0;
unsigned long long used = 0;
unsigned long long available = 0;
unsigned long long free = 0;
Diskinfo() = default;
XPACK(O(status, device, path, total, used, available, free))
};
}
}
#endif

3
util/util.cmake Normal file
View file

@ -0,0 +1,3 @@
add_subdirectory(util)
include_directories(util)
target_link_libraries(${PROJNAME} PRIVATE util)

201
util/xpack/LICENSE Executable file
View file

@ -0,0 +1,201 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
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.

84
util/xpack/README-bson.md Executable file
View file

@ -0,0 +1,84 @@
* 用于在C++结构体和bson之间互相转换
* 这个库可以配合[mongoxclient](https://github.com/xyz347/mongoxclient)使用
# 目录
- [encoder](#encoder)
- [decoder](#decoder)
- [bson types](#bsontypes)
- [builder](#builder)
- [重要说明](#重要说明)
## encoder
用于将结构体转成bson格式。范例:
```C++
struct Test {
int64_t id;
std::string name;
std::string email;
XPACK(A(id, "bson:_id"), O(name, email));
};
Test t;
std::string data = xpack::bson::encode(t); // data就是t转成bson的数据
```
## decoder
用于将bson数据转成结构体
用于将结构体转成bson格式。范例:
```C++
struct Test {
int64_t id;
std::string name;
std::string email;
XPACK(A(id, "bson:_id"), O(name, email));
};
std::string data;
// data = xxxx; data存储了相应的bson数据
Test t;
xpack::bson::decode(data, t);
// xpack::bson::decode 还有另一个参数方式:static void decode(const uint8_t* data, size_t len, T &val)
```
## bsontypes
- date time: `bson_date_time_t`
- timestamp: `bson_timestamp_t`
- binary: `bson_binary_t`
- regex: `bson_regex_t`
- oid: `bson_oid_t`
- decimal128: `bson_decimal128_t`
前4种是xbson额外定义的,后面两种是定义在libbson内的,为了统一,前4种也是定义在全局命名空间。
## builder
**需要C++11或以上版本支持**。用json的格式来构造bson数据。查询接口中的query,一般只需要指定很少几个字段,这个场景用结构体表达不是很方便。用builder就会简单很多。基本用法是:
1. 定义一个BsonBuilder:
- 参数是一个json格式的字符串
- 里面的双引号用单引号替代
- 变量用问号作为占位符,问号后面可以加一个单词用来作为助记符,比如"?"和"?uid"是等效的
- key也可以是变量,key作为变量,问号不用加引号
- BsonBuilder是线程安全的,建议定义为static变量,这样json字符串只需解析一次
范例:
```C++
static xpack::BsonBuilder bd("{'_id':?, ?:?}"); // 带占位符的builder
static xpack::BsonBuilder empty("{}"); // 空bson
static xpack::BsonBuilder student("{'type':'student'}"); // 不带占位符的builder
```
2. 调用Encode函数来构造bson数据,Encode的参数对应的是各个占位符,因此参数个数应该和占位符一致
比如对于上述的BsonBuilder
```C++
std::string data1 = bd.Encode(12345, "type", "master");
std::string data2 = empty.Encode();
std::string data3 = student.Encode();
// 这些bson的数据可以用在mongoxclient的各个接口中
```
3. 其他的一些API
- EncodeAsJson 和Encode类似,但是输出的是json格式的数据,可以用于调试等场景(所以这个其实也是个JsonBuilder)
- Error 如果构造BsonBuilder的json字符串有误,这个接口可以返回错误信息
- static std::string En(const std::string&fmt, Args... args)。这个是把构造BsonBuilder和Encode合在一起了,不是很建议使用,建议用于调试,因为每次都要构造一次BsonBuilder
## 重要说明
- 依赖于[libbson](https://github.com/mongodb/libbson/tree/1.0.0),需自行安装

407
util/xpack/README-en.md Executable file
View file

@ -0,0 +1,407 @@
xpack
====
* Used to convert between C++ structure and json/xml, bson is supported in [xbson](https://github.com/xyz347/xbson).
* Only header files, no need to compile library files, so there is no Makefile.
* Support MySQL, depends on `libmysqlclient-dev`, need to install by yourself. **not fully tested**
* Support Sqlite, depends on [libsqlite3](https://cppget.org/libsqlite3), need to install it yourself. **not fully tested**
* Support yaml, depend on [yaml-cpp](https://github.com/jbeder/yaml-cpp), need to install it yourself. **not fully tested**
* For details, please refer to the example
------
* [Quick start](#quick-start)
* [Container Support](#container-support)
* [FLAG](#flag)
* [Alias](#alias)
* [Bitfield](#bitfield)
* [Inherit](#inherit)
* [enum](#enum)
* [Custom codec](#custom-codec)
* [union](#union)
* [Schema indeterminate type](#schema-indeterminate-type)
* [Define macro outside the structure](#define-macro-outside-the-structure)
* [Array](#array)
* [Format indentation](#format-indentation)
* [XML array](#xml-array)
* [CDATA](#cdata)
* [Qt support](#qt-support)
* [Important note](#important-note)
Quick start
----
- Use macro `XPACK`(or `XPACK_OUT` if you don't want to change the structure) include structure members
- Use xpack::json::encode to convert structure to json
- Use xpack::json::decode to convert json to structure
```C++
#include <iostream>
#include "xpack/json.h" // include this header file for json or "xpack/xml.h" for xml
using namespace std;
struct User {
int id;
string name;
XPACK(O(id, name)); // Add XPACK at the end of structure, up to 99 'O' can be added, and each 'O' can contain up to 99 members
};
int main(int argc, char *argv[]) {
User u;
string data = "{\"id\":12345, \"name\":\"xpack\"}";
xpack::json::decode(data, u); // json to structure
cout<<u.id<<';'<<u.name<<endl;
string json = xpack::json::encode(u); // structure to json
cout<<json<<endl;
return 0;
}
```
Container Support
----
The following containers are currently supported (std)
- vector
- set
- list
- map<string, T>
- map<integer, T> // only for json
- unordered_map<string, T> (need C++11)
- shared_ptr (need C++11)
FLAG
----
In the macro `XPACK`, variables need to be enclosed with letters, such as `XPACK(O(a,b))`, `XPACK` can contain multiple letters, and each letter can contain multiple variables. Currently supported letters are:
- X Usage: X(F(flag1, flag2...), member1, member2,...) Multiple flags can be written in 'F', currently supported flags are:
- 0 no flag
- OE omitempty. When encoding, if the variable is 0 or an empty string or false, the variable is ignored
- EN empty as null. When json encoding, encode empty variable as null(OE is ignore the variable)
- M mandatory. When decoding, if the key corresponding to the variable does not exist, an exception is thrown
- ATTR attribute. When xml encode, put the value in attribute
- SL single line, When json encoding, put vector in single line
- C Usage: C(customcodec, F(flag1,flags...), member1, member2,...) For custom codec function, please refer to [Custom codec](#custom-codec) for details
- O Usage: O(member1, member2, ...) Same as X(F(0), member1, member2, ...) no flags
- M Usage: M(member1, member2, ...) Same as X(F(M), member1, member2, ...) mandatory
- A Usage: A(member1, alias1, member2, alias2...) Used when the key and the variable name are different, please refer to [Alias](#alias) for details
- AF Usage: AF(F(flag1, flag2,...), member1, alias1, member2, alias2...) Alias with flags
- B Usage: B(F(flag1, flag2, ...), member1, member2, ...) For bitfield, please refer to [Bitfield](#bitfield) for details. **bitfield not support alias**
- I Usage: I(baseclass1, baseclass2....), baseclassx is parent class, please refer to [Inherit](#inherit) for details
- E Usage: E(member1, member2) For enumeration variables that do not support C++11. **E not support alias**
Alias
----
- Used when the key and the variable name are different
- Usage: A(member1, alias1, member2, alias2...) or AF(F(flag1, flag2,...), member1, alias1, member2, alias2...), alias's format is "x t:n"
- x stands for global alias, not required
- t stands for type(currently support json/xml/bson), n stands for type alias, type alias is not required
- The order of precedence is: type alias, global alias, variable name
- For example, `id bson:_id`, in bson will use `_id` and json/xml will use `id`
- Type alias can take some type flags, format is "t:n,flag1,flag2,...", currently supported flags are:
- cdata. For xml, indicates that it is cdata
- sbs. For xml vector, In normal, `vector<int> nums` will be encoded into `<nums><nums>1</nums><nums>2</nums><nums>`, in sbs is `<nums>1</nums><nums>2</nums>`
``` C++
#include <iostream>
#include "xpack/json.h"
using namespace std;
struct Test {
long uid;
string name;
XPACK(A(uid, "id"), O(name)); // "uid" use alias "id"
};
int main(int argc, char *argv[]) {
Test t;
string json="{\"id\":123, \"name\":\"Pony\"}";
xpack::json::decode(json, t);
cout<<t.uid<<endl;
return 0;
}
```
Bitfield
----
- Use 'B' include bitfield members. **bitfield not support alias**
``` C++
#include <iostream>
#include "xpack/json.h"
using namespace std;
struct Test {
short ver:8;
short len:8;
string name;
XPACK(B(F(0), ver, len), O(name));
};
int main(int argc, char *argv[]) {
Test t;
string json="{\"ver\":4, \"len\":20, \"name\":\"IPv4\"}";
xpack::json::decode(json, t);
cout<<t.ver<<endl;
cout<<t.len<<endl;
return 0;
}
```
Inherit
----
- Use 'I' include parent classes. If you don't need variables from the parent class, you don't need use 'I'
- Parent class need add macro XPACK/XPACK_OUT
```C++
#include <iostream>
#include "xpack/json.h"
using namespace std;
struct P1 {
string mail;
XPACK(O(mail));
};
struct P2 {
long version;
XPACK(O(version));
};
struct Test:public P1, public P2 {
long uid;
string name;
XPACK(I(P1, P2), O(uid, name));
};
int main(int argc, char *argv[]) {
Test t;
string json="{\"mail\":\"pony@xpack.com\", \"version\":2019, \"id\":123, \"name\":\"Pony\"}";
xpack::json::decode(json, t);
cout<<t.mail<<endl;
cout<<t.version<<endl;
return 0;
}
```
enum
----
- If support C++11, enum is same as other type, use X/O/M/A is ok
- Otherwise need to use 'E', usage: E(F(...), member1, member2, ...)
```C++
#include <iostream>
#include "xpack/json.h"
using namespace std;
enum Enum {
X = 0,
Y = 1,
Z = 2,
};
struct Test {
string name;
Enum e;
XPACK(O(name), E(F(0), e));
};
int main(int argc, char *argv[]) {
Test t;
string json="{\"name\":\"IPv4\", \"e\":1}";
xpack::json::decode(json, t);
cout<<t.name<<endl;
cout<<t.e<<endl;
return 0;
}
```
Custom codec
----
Used in scenarios where you want to define your own codec method, such as:
- Wish to encode numeric types in string or hexadecimal
- Don't want to encode the struct variable by variable:
```C++
struct Time {
long ts; //unix timestamp
};
```
Don't want to encode to {"ts":1218196800} but "2008-08-08 20:00:00"
There are two ways to achieve:
- Use xtype. You can refer to this [example](example/xtype.cpp)
- Use 'C' include members need to custome codec(Hereinafter referred to as the `C method`), You can refer to this [example](example/custom.cpp)
Both methods need to implement custom codec, but there are some differences:
- xtype is at the type level, that is, once a certain type is encapsulated with xtype, the custom encode/decode will take effect for this type. xtype cannot be used for basic types (int/string, etc.)
- The C method can support basic types (int/string, etc.) and non-basic types, but only works on variables contained in C, such as
```C++
int a;
int b;
XPACK(O(a), C(custome_int, F(0), b ))
```
Then "a" still uses the default codec, and "b" uses the custom codec.
1. xtype takes precedence over the XPACK macro, that is, if xtype is defined, the encode/decode of xtype will be used first
2. The C method takes precedence over xtype, that is, the variables contained in C must use the codec method specified in C
With these two features, some more flexible codec control can be realized. For example, this [example](example/xtype_advance.cpp) realizes a function of encoding according to the variable situation. If Sub.type==1, then encode seq1, Otherwise encode seq2. `__x_pack_decode` and `__x_pack_encode` are the decode/encode functions added to the structure by the XPACK macro, and the custom codec function can call the default codec function of xpack through these functions.
union
----
Unions can be handled using [Custom codec](#custom-codec). You can refer to this [example](example/union1.cpp)
Array
----
- When decoding, if the number of elements exceeds the length of the array, it will be truncated
- The char array needs to have a '\0' terminator
```C++
#include <iostream>
#include "xpack/json.h"
using namespace std;
struct Test {
char name[64];
char email[64];
XPACK(O(name, email));
};
int main(int argc, char *argv[]) {
Test t;
string json="{\"name\":\"Pony\", \"email\":\"pony@xpack.com\"}";
xpack::json::decode(json, t);
cout<<t.name<<endl;
cout<<t.email<<endl;
return 0;
}
```
Schema indeterminate type
----
- Json only
- Use [xpack::JsonData](json_data.h) for this kind of content
- You can refer to this [example](example/json-data.cpp)
- The main APIs for xpack::JsonData are:
- Type. To get the field type
- IsXXX. To check is the field type is xxx, same as `return Type()==xxxx;`
- GetXXX. To get the field value
- operator bool. To check is't a valid xpack::JsonData
- Size. To get the array's elements number
- `operator [](size_t index)`. Used to get the index element in array
- `operator [](const char *key)`. Used to get the element by key in object
- Iterator:
- Begin. Get the first element's iterator
- Next. Get the next iterator
- Key. Get the key of iterator
- Val. Get the value of iterator
Define macro outside the structure
----
- Use `XPACK_OUT` instead of `XPACK` to include structure members
- `XPACK_OUT` must define in global namespace
```c++
#include <sys/time.h>
#include <iostream>
#include "xpack/json.h"
using namespace std;
/*
struct timeval {
time_t tv_sec;
suseconds_t tv_usec;
};
*/
// timeval is thirdparty struct
XPACK_OUT(timeval, O(tv_sec, tv_usec));
struct T {
int a;
string b;
timeval t;
XPACK(O(a, b, t));
};
int main(int argc, char *argv[]) {
T t;
T r;
t.a = 123;
t.b = "xpack";
t.t.tv_sec = 888;
t.t.tv_usec = 999;
string s = xpack::json::encode(t);
cout<<s<<endl;
xpack::json::decode(s, r);
cout<<r.a<<','<<r.b<<','<<r.t.tv_sec<<','<<r.t.tv_usec<<endl;
return 0;
}
```
Format indentation
----
- The default encode is not indented, and the readability is not good
- If indentation is required, it can be controlled by the last two parameters of encode:
- indentCount Indicates the number of characters for indentation, <0 means no indentation, 0 means newline but no indentation
- indentChar Characters that represent indentation, use spaces or tabs
XML array
----
- Arrays use variable names as element labels by default, such as "ids":[1,2,3] will be encoded as:
``` xml
<ids>
<ids>1</ids>
<ids>2</ids>
<ids>3</ids>
</ids>
```
- You can use alias to change the label name. For example: A(ids,"xml:ids,vl@id"), vl@id means to use id as label:
``` xml
<ids>
<id>1</id>
<id>2</id>
<id>3</id>
</ids>
```
- If want to encode array element side by side, can use `sbs` flag. For example: `A(ids, "xml:ids,sbs")`. **Note that the sbs tag can only be used for arrays(vector), and it may crash if used elsewhere**
``` xml
<ids>1</ids>
<ids>2</ids>
<ids>3</ids>
```
CDATA
----
- For xml CDATA, need to use `cdata` flag. For example: A(data, "xml:data,cdata")
- variable type must be std::string
- Non-cdata types can also add cdata flag, which will be processed as ordinary strings
Qt support
----
- Modify [config.h](config.h) to enable XPACK_SUPPORT_QT(or enable it in compile flags)
- Currently supports: QString/QMap/QList/QVector
Important note
----
- Try not to start the variable name with __x_pack, otherwise it may conflict with the library.
- vc6 is not supported
- msvc has only been tested on vs2019
- The serialization and deserialization of json uses [rapidjson](https://github.com/Tencent/rapidjson) (November 2018 edition)
- The deserialization of xml uses [rapidxml](http://rapidxml.sourceforge.net)
- The serialization of xml is written by myself, without reference to RFC, there may be some differences from the standard.

418
util/xpack/README.md Executable file
View file

@ -0,0 +1,418 @@
xpack
====
[English](README-en.md)
* 用于在C++结构体和json/xml/yaml/bson/mysql/sqlite之间互相转换
* 只有头文件, 无需编译库文件,所以也没有Makefile。
* 支持bson,依赖于`libbson-1.0`,需自行安装。**未经充分测试**,具体请参考[README](README-bson.md)
* 支持MySQL,依赖于`libmysqlclient-dev`,需自行安装。**未经充分测试**
* 支持Sqlite,依赖于[libsqlite3](https://cppget.org/libsqlite3),需自行安装。**未经充分测试**
* 支持yaml,依赖于[yaml-cpp](https://github.com/jbeder/yaml-cpp),需自行安装。**未经充分测试**
* 具体可以参考example的例子
------
* [基本用法](#基本用法)
* [容器支持](#容器支持)
* [FLAG](#flag)
* [别名](#别名)
* [位域](#位域)
* [继承](#继承)
* [枚举](#枚举)
* [自定义编解码](#自定义编解码)
* [union](#union)
* [不定类型](#不定类型)
* [数组](#数组)
* [第三方类和结构体](#第三方类和结构体)
* [格式化缩进](#格式化缩进)
* [XML数组](#xml数组)
* [CDATA](#cdata)
* [Qt支持](#qt支持)
* [MySQL](#mysql)
* [重要说明](#重要说明)
基本用法
----
- 结构体后面用XPACK宏包含各个变量,XPACK内还需要一个字母,不同字母的意义请参考[FLAG](#flag)
- 用xpack::json::encode把结构体转json
- 用xpack::json::decode把json转结构体
```C++
#include <iostream>
#include "xpack/json.h" // Json包含这个头文件,xml则包含xpack/xml.h
using namespace std;
struct User {
int id;
string name;
XPACK(O(id, name)); // 添加宏定义XPACK在结构体定义结尾
};
int main(int argc, char *argv[]) {
User u;
string data = "{\"id\":12345, \"name\":\"xpack\"}";
xpack::json::decode(data, u); // json转结构体
cout<<u.id<<';'<<u.name<<endl;
string json = xpack::json::encode(u); // 结构体转json
cout<<json<<endl;
return 0;
}
```
容器支持
----
目前支持下列容器(std)
- vector
- set
- list
- map<string, T>
- map<integer, T> // 仅JSON,XML不支持
- unordered_map<string, T> (需要C++11支持)
- shared_ptr (需要C++11支持)
FLAG
----
宏XPACK里面,需要用字母将变量包含起来,比如XPACK(O(a,b)),XPACK可以包含多个字母,每个字母可以包含多个变量。目前支持的字母有:
- X。格式是X(F(flag1, flag2...), member1, member2,...) F里面包含各种FLAG,目前支持的有:
- 0 没有任何FLAG
- OE omitempty,encode的时候,如果变量是0或者空字符串或者false,则不生成对应的key信息
- EN empty as null, 用于json的encode,OE是直接不生成empty的字段,EN则是生成一个null
- M mandatory,decode的时候,如果这个字段不存在,则抛出异常,用于一些id字段。
- ATTR attribute,xml encode的时候,把值放到attribute里面。
- SL single line, json encode的时候,对于数组,放在一行里面
- C。格式是C(customcodec, F(flag1,flags...), member1, member2,...)用于自定义编解码函数,详情请参考[自定义编解码](#自定义编解码)
- O。等价于X(F(0), ...) 没有任何FLAG。
- M。等价于X(F(M),...) 表示这些字段是必须存在的。
- A。[别名](#别名),A(member1, alias1, member2, alias2...),用于变量和key名不一样的情况
- AF。带FLAG的[别名](#别名),AF(F(flag1, flag2,...), member1, alias1, member2, alias2...)
- B。[位域](#位域),B(F(flag1, flag2, ...), member1, member2, ...) **位域不支持别名**
- I。[继承](#继承),I(baseclass1, baseclass2....),里面放父类
- E。[枚举](#枚举):
- 如果编译器支持C++11,不需要用E,枚举可以放X/O/M/A里面。
- 否则枚举只能放E里面,不支持别名
别名
----
- 用于变量名和key名不一致的场景
- 格式是A(变量,别名....)或者AF(F(flags), 变量,别名....),别名的格式是"x t:n"的格式
- x表示全局别名,t表示类型(目前支持json、xml、bson),n表示类型下的别名
- 全局别名可以没有,比如`json:_id`是合法的
- 类型别名可以没有,比如`_id`是合法的
- 有类型别名优先用类型别名,否则用全局别名,都没有,则用变量名
``` C++
#include <iostream>
#include "xpack/json.h"
using namespace std;
struct Test {
long uid;
string name;
XPACK(A(uid, "id"), O(name)); // "uid"的别名是"id"
};
int main(int argc, char *argv[]) {
Test t;
string json="{\"id\":123, \"name\":\"Pony\"}";
xpack::json::decode(json, t);
cout<<t.uid<<endl;
return 0;
}
```
位域
----
- 使用"B"来包含位域变量,**位域不支持别名**
``` C++
#include <iostream>
#include "xpack/json.h"
using namespace std;
struct Test {
short ver:8;
short len:8;
string name;
XPACK(B(F(0), ver, len), O(name));
};
int main(int argc, char *argv[]) {
Test t;
string json="{\"ver\":4, \"len\":20, \"name\":\"IPv4\"}";
xpack::json::decode(json, t);
cout<<t.ver<<endl;
cout<<t.len<<endl;
return 0;
}
```
继承
----
- 使用"I"来包含父类。需要用到父类的变量就包含,用不到可以不包含。
- 父类的父类也需要包含,比如class Base; class Base1:public Base; class Base2:public Base1;那么在Base2中需要I(Base1, Base)
- 父类也需要定义XPACK/XPACK_OUT宏。
```C++
#include <iostream>
#include "xpack/json.h"
using namespace std;
struct P1 {
string mail;
XPACK(O(mail));
};
struct P2 {
long version;
XPACK(O(version));
};
struct Test:public P1, public P2 {
long uid;
string name;
XPACK(I(P1, P2), O(uid, name));
};
int main(int argc, char *argv[]) {
Test t;
string json="{\"mail\":\"pony@xpack.com\", \"version\":2019, \"id\":123, \"name\":\"Pony\"}";
xpack::json::decode(json, t);
cout<<t.mail<<endl;
cout<<t.version<<endl;
return 0;
}
```
枚举
----
- 如果编译器支持C++11,则枚举和普通变量名一样,放X/O/M/A里面皆可。
- 否则需要放到E里面,格式是E(F(...), member1, member2, ...)
```C++
#include <iostream>
#include "xpack/json.h"
using namespace std;
enum Enum {
X = 0,
Y = 1,
Z = 2,
};
struct Test {
string name;
Enum e;
XPACK(O(name), E(F(0), e));
};
int main(int argc, char *argv[]) {
Test t;
string json="{\"name\":\"IPv4\", \"e\":1}";
xpack::json::decode(json, t);
cout<<t.name<<endl;
cout<<t.e<<endl;
return 0;
}
```
自定义编解码
----
应用场景
- 有些基础类型想用自定义方式编码,比如用字符串的方式来编码整数/浮点数
- 部分类型可能不想按结构体变量逐个编码,比如定义了一个时间结构体:
```C++
struct Time {
long ts; //unix timestamp
};
```
并不希望编码成{"ts":1218196800} 这种格式,而是希望编码成"2008-08-08 20:00:00"这种格式。
这里有两种方式:
- 使用xtype,可以参考[例子](example/xtype.cpp)
- 用C来包含需要自定义编解码的变量(下面简称C方法),可以参考[例子](example/custom.cpp)
两种方法本质上都是自己去实现encode/decode,但是有以下区别:
- xtype是类型级别的,也就是一旦某个类型用xtype封装之后,那么自定义的encode/decode就对这个类型生效。xtype无法作用于基本类型(int/string等)
- C方法能支持基本类型(int/string等)和非基本类型,但是仅作用于用C包含的变量,比如int a;int b; O(a), C(custome_int, F(0), b);那么a还是用默认的编解码,b才是用自定义的编解码。
1. xtype优先于XPACK宏,也就是定义了xtype的,会优先使用xtype的encode/decode
2. C方法优先于xtype,也就是用C包含的变量,一定会用C里面指定的编解码方法。
用这两个特性,可以实现一些比较灵活的编解码控制,比如这个[例子](example/xtype_advance.cpp)实现了一个根据变量情况来编码的功能,如果Sub.type==1则encode seq1,否则encode seq2. `__x_pack_decode`和`__x_pack_encode`是XPACK宏给结构体添加的decode/encode函数,自定义编解码函数可以通过这些函数调用xpack默认的编解码功能。
union
----
可以使用[自定义编解码](#自定义编解码)来处理联合体,可以参考[范例](example/union1.cpp)
数组
----
- decode的时候如果元素个数超过数组的长度,会截断
- char数组按有\0结束符处理
```C++
#include <iostream>
#include "xpack/json.h"
using namespace std;
struct Test {
char name[64];
char email[64];
XPACK(O(name, email));
};
int main(int argc, char *argv[]) {
Test t;
string json="{\"name\":\"Pony\", \"email\":\"pony@xpack.com\"}";
xpack::json::decode(json, t);
cout<<t.name<<endl;
cout<<t.email<<endl;
return 0;
}
```
不定类型
----
- 用于json的schema不确定的场景
- 用[xpack::JsonData](json_data.h)来接收这些信息
- 可以参考[例子](example/json-data.cpp)
- xpack::JsonData主要的方法有:
- Type。用于获取类型
- IsXXX系列函数。用于判断是否是某种类型,基本等价于return Type()==xxxx;
- GetXXX系列函数。用来提取值。
- 重载bool。用来判断是否是一个合法的JsonData。
- Size。用于数组类型判断元素的个数
- `operator [](size_t index)` 用来取数组的第index个元素(从0开始)
- `operator [](const char *key)` 用来根据key取Object类型的元素
- Begin。用来遍历Object的元素,取第一个。
- Next。配合Begin使用,获取下一个元素。
- Key。配置Begin和Next使用,遍历的时候获取Key
第三方类和结构体
----
- 用XPACK_OUT而非XPACK来包含变量
- XPACK_OUT必须定义在全局命名空间
```c++
#include <sys/time.h>
#include <iostream>
#include "xpack/json.h"
using namespace std;
/*
struct timeval {
time_t tv_sec;
suseconds_t tv_usec;
};
*/
// timeval is thirdparty struct
XPACK_OUT(timeval, O(tv_sec, tv_usec));
struct T {
int a;
string b;
timeval t;
XPACK(O(a, b, t));
};
int main(int argc, char *argv[]) {
T t;
T r;
t.a = 123;
t.b = "xpack";
t.t.tv_sec = 888;
t.t.tv_usec = 999;
string s = xpack::json::encode(t);
cout<<s<<endl;
xpack::json::decode(s, r);
cout<<r.a<<','<<r.b<<','<<r.t.tv_sec<<','<<r.t.tv_usec<<endl;
return 0;
}
```
格式化缩进
----
- encode缺省生成的json/xml是没有缩进的,适合程序使用,如果让人读,可以进行缩进。
- encode的最后两个参数控制
- indentCount 表示缩进的字符数,<0表示不缩进,0则是换行但是不缩进
- indentChar 表示缩进的字符,用空格或者制表符
XML数组
----
- 数组默认会用变量名作为元素的标签,比如"ids":[1,2,3],对应的xml是:
``` xml
<ids>
<ids>1</ids>
<ids>2</ids>
<ids>3</ids>
</ids>
```
- 可以用别名的方式来控制数组的元素的标签,比如A(ids,"xml:ids,vl@id"),vl后面跟着一个@xx,xx就是数组的标签,生成的结果就是:
``` xml
<ids>
<id>1</id>
<id>2</id>
<id>3</id>
</ids>
```
- 如果希望数组直接展开,而不是在外面包一层,可以用别名加"sbs"的flag来实现,比如A(ids, "xml:ids,sbs"),**注意,sbs标签仅能用于数组,其他地方使用可能会崩溃**
``` xml
<ids>1</ids>
<ids>2</ids>
<ids>3</ids>
```
CDATA
----
- 对于CDATA类型,需要用"cdata"的flag来实现,比如A(data, "xml:data,cdata")
- cdata只能用std::string来接收
- 如果变量对应的xml不是CDATA结构,会按普通字符串来处理比如`<data>hello</data>`也可以解析成功
Qt支持
----
- 修改config.h,开启XPACK_SUPPORT_QT这个宏(或者在编译选项开启)
- 当前支持 QString/QMap/QList/QVector
MySQL
----
- 目前仅支持decode,还不支持encode
- 未经过完整测试,使用需要谨慎
- 当前支持类型有:
- string。简单测试。
- 整型。简单测试。
- 浮点型。未测试。
- 用整型(比如time_t)接收TIME/DATETIME/TIMESTAMP。未测试。
- 自定义类型转换,is_xpack_mysql_xtype,类似于xtype。未测试。
- api有两个(xpack::mysql::) :
- `static void decode(MYSQL_RES *result, T &val)`
- 用来将MYSQL_RES转成结构体或者vector<>,如果是非vector,则只转换第一个row
- `static void decode(MYSQL_RES *result, const std::string&field, T &val)`
- 用来解析某个字段,用于只想获得某个字段内容的场景,比如select id from mytable where name = lilei,只想获得id信息。val支持vector
重要说明
----
- 变量名尽量不要用__x_pack开头,不然可能会和库有冲突。
- vc6不支持。
- msvc没有做很多测试,只用2019做过简单测试。
- json的序列化反序列化用的是[rapidjson](https://github.com/Tencent/rapidjson)
- xml的反序列化用的是[rapidxml](http://rapidxml.sourceforge.net)
- xml的序列化是我自己写的,没有参考RFC,可能有和标准不一样的地方.
- 有疑问可以加QQ群878041110

54
util/xpack/bson.h Executable file
View file

@ -0,0 +1,54 @@
/*
* Copyright (C) 2021 Duowan Inc. All rights reserved.
*
* 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 __X_PACK_BSON_H
#define __X_PACK_BSON_H
#include "bson_decoder.h"
#include "bson_encoder.h"
#include "xpack.h"
#ifdef X_PACK_SUPPORT_CXX0X // support c++11 or later
#include "bson_builder.h"
#endif
namespace xpack {
class bson {
public:
template <class T>
static void decode(const std::string &data, T &val) {
BsonDecoder de;
de.decode(data, val);
}
template <class T>
static void decode(const uint8_t* data, size_t len, T &val) {
BsonDecoder de;
de.decode(data, len, val);
}
template <class T>
static std::string encode(const T &val) {
BsonEncoder en;
return en.encode(val);
}
};
}
#endif

588
util/xpack/bson_builder.h Executable file
View file

@ -0,0 +1,588 @@
/*
* Copyright (C) 2021 Duowan Inc. All rights reserved.
*
* 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 __X_PACK_BSON_BUILDER_H
#define __X_PACK_BSON_BUILDER_H
#include <stdexcept>
#include "bson_encoder.h"
#ifndef X_PACK_SUPPORT_CXX0X // support c++11 or later
#error "need c++11 or later"
#endif
namespace xpack {
class BsonBuilder {
enum {
Unknow = 0,
ObjectBegin = 1<<0,
ObjectEnd = 1<<1,
ArrayBegin = 1<<2,
ArrayEnd = 1<<3,
Comma = 1<<4,
Colon = 1<<5,
Integer = 1<<6,
Float = 1<<7,
String = 1<<8,
Bool = 1<<9,
Variable = 1<<10,
BEof = 1<<11
};
std::string tokenName(int type) const {
std::string ret;
if (0 != (ObjectBegin&type)) {
ret += "/{";
}
if (0 != (ObjectEnd&type)) {
ret += "/}";
}
if (0 != (ArrayBegin&type)) {
ret += "/[";
}
if (0 != (ArrayEnd&type)) {
ret += "/]";
}
if (0 != (Comma&type)) {
ret += "/,";
}
if (0 != (Colon&type)) {
ret += "/:";
}
if (0 != ((Float|Integer|Bool)&type)) {
ret += "/number/bool";
}
if (0 != (String&type)) {
ret += "/string";
}
if (0 != (Variable&type)) {
ret += "/variable";
}
if (!ret.empty()) {
ret = ret.substr(1);
}
return ret;
}
struct Token {
int type; // see enum
union {
const char *sval;
long lval;
double dval;
bool bval;
};
Token():type(Unknow),sval(NULL){}
};
struct Item {
Token tk;
const char *key;
bool kvar; // key is variable
Item():tk(),key(NULL), kvar(false) {
}
};
struct Encoder {
const std::vector<const Item*>* items;
BsonWriter wr;
XEncoder<BsonWriter> en;
size_t idx; // items index;
size_t vidx; // variable index;
std::vector<std::string> vkeys; // store variable key
Item tmpIt; // key:value when key is a variable, copy item to here
Item *pIt; // point to tmpIt
std::vector<size_t> arrayIndex;
std::vector<const char*> objKeys; // store object key. use in object end
std::vector<const char*> arrKeys; // store array key, use in array end
Encoder(const std::vector<const Item*>&its):items(&its), en(wr), idx(0), vidx(0), pIt(NULL){}
// array key. index: "0", "1", "2", "3"
const char *Key(const char *raw) {
if (NULL != raw) {
return raw;
} else if (arrayIndex.size()>0) {
size_t idx = arrayIndex.size() - 1;
return wr.IndexKey(arrayIndex[idx]++);
} else {
return NULL;
}
}
};
public:
BsonBuilder(const std::string& _fmt):raw(_fmt),en(NULL) {
dup = new char[raw.length()+1];
strcpy(dup, raw.c_str());
if (parse()) {
for (size_t i=0; i<items.size(); ++i) {
if (items[i]->kvar || items[i]->tk.type==Variable) {
return;
}
}
// no variable. pre build it
bstr = Encode();
}
}
~BsonBuilder() {
if (NULL != dup) {
for (size_t i=0; i<items.size(); ++i) {
delete items[i];
}
delete[] dup;
dup = NULL;
} else if (NULL != en) {
delete en;
en = NULL;
}
}
std::string Error() const {
return fmtErr;
}
template <typename... Args>
std::string Encode(Args... args) {
if (!bstr.empty()) {
return bstr;
} else if (!fmtErr.empty()) {
return "";
}
BsonBuilder bd(*this);
bd.iencode(args...);
bd.end(NULL);
return bd.en->en.String();
}
template <typename... Args>
std::string EncodeAsJson(Args... args) {
if (!fmtErr.empty()) {
return "";
}
BsonBuilder bd(*this);
bd.iencode(args...);
bd.end(NULL);
return bd.en->wr.Json();
}
template <typename... Args>
static std::string En(const std::string&fmt, Args... args) {
BsonBuilder bb(fmt);
return bb.Encode(args...);
}
private: // encoder
BsonBuilder(const BsonBuilder&bd):dup(NULL) {
en = new Encoder(bd.items);
}
// implement iencode()/iencode(T)/iencode(T, Args...) to expand template arguments
void iencode() {
}
void iencode(const char*val) { // xencoder not allow pointer
std::string s(val);
encode(s);
++en->vidx;
}
template <typename T>
void iencode(const T &val) {
encode(val);
++en->vidx;
}
template <typename T, typename... Args>
void iencode(const T& first, Args... args) {
iencode(first);
iencode(args...);
}
template <typename T>
BsonBuilder& encode(const T &val) {
// encode data items without variable
for (; en->idx<en->items->size(); ++en->idx) {
const Item *it = en->items->at(en->idx);
if ((!it->kvar) && it->tk.type!=Variable) {
add(it, 1);
} else {
break;
}
}
if (en->idx >= en->items->size()) { // finish
return *this;
} else if (NULL != en->pIt) { // key:val. key and val is variable, key replaced in last encode
add(en->pIt, val);
en->pIt = NULL;
++en->idx;
return *this;
}
const Item *it = en->items->at(en->idx);
if (it->kvar) { // key:val and key is variable, we fill string to key
en->tmpIt = *it; // copy item info
en->pIt = &en->tmpIt;
en->pIt->key = dumpStr(val); // dump key string
en->pIt->kvar = false;
if (it->tk.type != Variable) { // value not variable
add(en->pIt, 1);
en->pIt = NULL;
++en->idx;
}
} else { // val is variable
add(it, val);
++en->idx;
}
return *this;
}
// add items to encoder
template <typename T>
void add(const Item *it, const T&val) {
const char *key = en->Key(it->key);
switch (it->tk.type) {
case ObjectBegin:
en->en.ob(key);
en->objKeys.push_back(key);
break;
case ObjectEnd:
en->en.oe(en->objKeys.back());
en->objKeys.pop_back();
break;
case ArrayBegin:
en->arrayIndex.push_back(0);
en->en.ab(key);
en->arrKeys.push_back(key);
break;
case ArrayEnd:
en->arrayIndex.pop_back();
en->en.ae(en->arrKeys.back());
en->arrKeys.pop_back();
break;
case Integer:
en->en.add(key, it->tk.lval);
break;
case Float:
en->en.add(key, it->tk.dval);
break;
case String:
en->en.add(key, std::string(it->tk.sval));
break;
case Bool:
en->en.add(key, it->tk.bval);
break;
case Variable:
en->en.add(key, val);
break;
}
}
// encode the last none variable item
void end(std::string *err) {
for (; en->idx<en->items->size(); ++en->idx) {
const Item *it = en->items->at(en->idx);
if (it->kvar || it->tk.type==Variable) {
if (NULL != err) {
*err = "less variable";
}
return;
}
add(it, 1);
}
}
// type to const char*
const char *dumpStr(const std::string&str) {
en->vkeys.push_back(str);
return en->vkeys[en->vkeys.size()-1].c_str();
}
const char *dumpStr(const char *key) {
return key;
}
template <typename T>
const char *dumpStr(const T&val) {
(void)val;
std::string err = "variable "+Util::itoa(en->vidx)+" is a key, must be std::string or const char*";
throw std::runtime_error(err);
return "";
}
bool isEnd(char ch) {
return ch==' ' || ch==':' || ch==',' || ch=='}' || ch==']'|| ch=='\0';
}
// a simple bson string parser
bool parse() {
std::vector<char> stack;
Item *item;
char *p = dup;
char *end = p;
int keyIndex = -1;
char ch;
bool waitkey = false; // {key:value, expect key
int exp = ObjectBegin; // expect type
while (true) {
item = new Item;
p = end;
nextToken(p, &end, item->tk);
if (item->tk.type == BEof) {
delete item;
if (!fmtErr.empty()) {
return false;
}
if (stack.size() > 0) {
if ((ch=stack[stack.size()-1]) == '{') { // in object
fmtErr = "miss } at the end of string";
} else {
fmtErr = "miss ] at the end of string";
}
return false;
}
return true;
} else if (item->tk.type == Unknow) {
fmtErr = "unknow token:";
fmtErr += p;
delete item;
break;
}
if ((item->tk.type&exp) == 0) {
fmtErr = "unexpected token. expect["+tokenName(exp)+"]. but get["+tokenName(item->tk.type)+"]";
if (NULL != item->tk.sval) {
fmtErr += ". in [";
fmtErr += item->tk.sval;
fmtErr += "]";
}
delete item;
break;
}
// key:value parse value done
if (keyIndex >= 0) {
const Item *it = items[keyIndex];
items.pop_back();
keyIndex = -1;
if (it->tk.type == String) {
item->key = it->tk.sval;
} else {
item->kvar = true;
}
delete it;
}
switch (item->tk.type) {
case ObjectBegin:
items.push_back(item);
exp = ObjectEnd | Variable | String;
stack.push_back('{');
waitkey = true;
break;
case ObjectEnd:
waitkey = false;
case ArrayEnd:
items.push_back(item);
stack.pop_back();
if (stack.size() == 0) {
exp = BEof;
} else if ((ch=stack[stack.size()-1]) == '{') { // in object
exp = ObjectEnd | Comma | String | Variable;
} else {
exp = ArrayEnd | Comma | Integer | Float | String | Bool | Variable;
}
break;
case ArrayBegin:
items.push_back(item);
exp = ObjectBegin | ArrayBegin | ArrayEnd | Integer | Float | String | Bool | Variable;
stack.push_back('[');
break;
case Comma:
if ((ch=stack[stack.size()-1]) == '{') { // in object
exp = String | Variable;
waitkey = true;
} else {
exp = ObjectBegin | ArrayBegin | Integer | Float | String | Bool | Variable;
}
delete item;
break;
case Colon:
exp = ObjectBegin | ArrayBegin | Integer | Float | String | Bool | Variable;
keyIndex = int(items.size()) - 1;
waitkey = false;
delete item;
break;
case Integer:
case Float:
case String:
case Bool:
case Variable:
items.push_back(item);
if ((ch=stack[stack.size()-1]) == '{') { // in object
if (waitkey) {
waitkey = false;
exp = Colon;
} else {
exp = ObjectEnd | Comma;
}
} else {
exp = ArrayEnd | Comma | Integer | Float | String | Bool | Variable;
}
break;
}
}
return false;
}
// get next token from data, may modify data
void nextToken(char* data, char**end, Token &tk) {
char ch;
size_t i = 0;
*end = NULL;
while (data[i] == ' ') {
i++;
}
if (data[i] == '\0') {
tk.type = BEof;
return;
}
tk.sval = data+i;
while ((ch=data[i++]) != '\0') {
if (tk.type == String) {
if (ch != '\'') {
continue;
} else if (isEnd(data[i])) {
data[i-1] = '\0';
*end = data+i;
} else {
tk.type = Unknow;
}
return;
} else if (tk.type == Integer || tk.type == Float) {
if (ch>='0' && ch<='9') {
continue;
} else if (ch == '.') {
tk.type = Float;
continue;
} else if (isEnd(ch)) {
if (tk.type == Integer) {
data[i-1] = '\0';
if (Util::atoi(tk.sval, tk.lval)) {
*end = data+i-1;
} else {
tk.type = Unknow;
}
data[i-1] = ch;
} else {
char *dend;
tk.dval = strtod(tk.sval, &dend);
if (dend != data+i-1) {
tk.type = Unknow;
} else {
*end = dend;
}
}
} else {
tk.type = Unknow;
}
return;
} else if (tk.type == Variable) {
if ((ch>='0' && ch<='9') || (ch>='a' && ch<='z') || (ch>='A' && ch<='Z') || ch=='_') {
continue;
} else if (isEnd(ch)) {
*end = data+i-1;
} else {
tk.type = Unknow;
}
return;
} else if (ch == '{') {
tk.type = ObjectBegin;
break;
} else if (ch == '}') {
tk.type = ObjectEnd;
break;
} else if (ch == '[') {
tk.type = ArrayBegin;
break;
} else if (ch == ']') {
tk.type = ArrayEnd;
break;
} else if (ch == ',') {
tk.type = Comma;
break;
} else if (ch == ':') {
tk.type = Colon;
break;
} else if (ch == '\'') { // string
tk.type = String;
tk.sval = data+i;
} else if (ch == '?') {
tk.type = Variable;
} else if (ch=='-' || ch=='+' || (ch>='0' && ch<='9')) {
tk.type = Integer;
tk.sval = data+i-1;
} else if (ch == 't') { //
if (strncmp(data+i-1, "true", 4) == 0 && isEnd(data[i+3])) {
tk.type = Bool;
tk.bval = true;
*end = data+i+3;
}
return;
} else if (ch == 'f') { //
if (strncmp(data+i-1, "false", 5) == 0 && isEnd(data[i+4])) {
tk.type = Bool;
tk.bval = false;
*end = data+i+4;
}
return;
} else {
return;
}
}
if (tk.type != Unknow) {
if (*end==NULL && (tk.type==String||tk.type==Float||tk.type==Integer||tk.type==Variable)) {
fmtErr = "no end for string/integer/float/variable";
tk.type = BEof;
} else {
*end = data+i;
}
}
}
std::string raw;
std::string fmtErr;
std::string lastErr;
std::string bstr; // for that without any variable
char* dup; // for parse
std::vector<const Item*> items;
Encoder *en;
};
}
#endif

279
util/xpack/bson_decoder.h Executable file
View file

@ -0,0 +1,279 @@
/*
* Copyright (C) 2021 Duowan Inc. All rights reserved.
*
* 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 __X_PACK_BSON_DECODER_H
#define __X_PACK_BSON_DECODER_H
#include <string.h>
#include <libbson-1.0/bson.h>
#include "util.h"
#include "xdecoder.h"
#include "bson_type.h"
namespace xpack {
class BsonNode {
typedef XDecoder<BsonNode> decoder;
friend class BsonDecoder;
public:
typedef size_t Iterator;
BsonNode(const bson_iter_t* iter = NULL):it(iter),inited(false) {
if (NULL != it) {
type = bson_iter_type(it);
}
}
inline static const char * Name() {
return "bson";
}
operator bool () const {
return it != NULL;
}
bool IsNull() const {
return NULL!=it && BSON_TYPE_NULL==bson_iter_type(it);
}
BsonNode Find(decoder&de, const char*key, const Extend *ext) {
(void)de;
(void)ext;
if (!inited) {
this->init();
}
if (BSON_TYPE_DOCUMENT != type) {
de.decode_exception("not document", NULL);
}
node_index::iterator iter = _childs_index.find(key);
if (iter != _childs_index.end()) {
return BsonNode(&_childs[iter->second]);
} else {
return BsonNode();
}
}
size_t Size(decoder&de) {
(void)de;
if (!inited) {
this->init();
}
if (BSON_TYPE_ARRAY == type) {
return _childs.size();
} else {
de.decode_exception("not array", NULL);
return 0;
}
}
BsonNode At(size_t index) const { // no exception
return BsonNode(&(_childs[index]));
}
BsonNode Next(decoder&de, BsonNode&p, Iterator&iter, std::string&key) {
(void)de;
if (!p.inited) {
p.init();
}
if (BSON_TYPE_DOCUMENT != p.type) {
de.decode_exception("not document", NULL);
}
if (it != p.it) {
++iter;
} else {
iter = 0;
}
if (iter != p._childs.size()) {
bson_iter_t *t = &p._childs[iter];
key = bson_iter_key(t);
return BsonNode(t);
}
return BsonNode();
}
bool Get(decoder&de, std::string&val, const Extend*ext) {
(void)ext;
if (BSON_TYPE_UTF8 == type) {
uint32_t length;
const char* data = bson_iter_utf8(it, &length);
if (NULL != data) {
val = std::string(data, length);
}
} else if (BSON_TYPE_NULL != type) {
de.decode_exception("not string", NULL);
}
return true;
}
bool Get(decoder&de, bool &val, const Extend*ext) {
(void)de;
(void)ext;
val = bson_iter_as_bool(it);
return true;
}
template <class T>
typename x_enable_if<numeric<T>::is_integer, bool>::type Get(decoder&de, T &val, const Extend*ext){
(void)de;
(void)ext;
val = (T)bson_iter_as_int64(it);
return true;
}
template <class T>
typename x_enable_if<numeric<T>::is_float, bool>::type Get(decoder&de, T &val, const Extend*ext){
(void)de;
(void)ext;
val = (T)bson_iter_double(it);
return true;
}
/*std::string json() const {
if (NULL == _data) {
return "";
}
size_t length=BSON_UINT32_TO_LE(*(int32_t*)_data);
bson_t b; // local is ok
bson_init_static(&b, _data, length);
size_t s;
char *jstr = bson_as_json(&b, &s);
if (jstr) {
std::string j(jstr);
bson_free(jstr);
return j;
} else {
return "";
}
}*/
// bson type
bool decode_type_spec(bson_oid_t &val, const Extend *ext) {
(void)ext;
const bson_oid_t *t = bson_iter_oid(it);
if (t != NULL) {
bson_oid_init_from_data(&val, t->bytes);
return true;
} else {
return false;
}
}
bool decode_type_spec(bson_date_time_t &val, const Extend *ext) {
(void)ext;
val.ts = bson_iter_date_time(it);
return true;
}
bool decode_type_spec(bson_timestamp_t &val, const Extend *ext) {
(void)ext;
bson_iter_timestamp(it, &val.timestamp, &val.increment);
return true;
}
bool decode_type_spec(bson_decimal128_t &val, const Extend *ext) {
(void)ext;
return bson_iter_decimal128(it, &val);
}
bool decode_type_spec(bson_regex_t &val, const Extend *ext) {
(void)ext;
const char *options = NULL;
const char *regex = bson_iter_regex(it, &options);
if (NULL != regex) {
val.pattern = regex;
}
if (NULL != options) {
val.options = options;
}
return true;
}
bool decode_type_spec(bson_binary_t &val, const Extend *ext) {
(void)ext;
uint32_t len = 0;
const uint8_t *data = NULL;
bson_iter_binary(it, &val.subType, &len, &data);
if (data != NULL && len > 0) {
val.data = std::string((const char*)data, size_t(len));
}
return true;
}
private:
typedef std::map<const char*, size_t, cmp_str> node_index; // index of _childs
// get object or array info
void init(bool top = false) {
inited = true;
bson_iter_t sub;
if (NULL != it) {
if (top) {
type = BSON_TYPE_DOCUMENT;
memcpy((void*)&sub, (void*)it, sizeof(sub));
} else if (!bson_iter_recurse(it, &sub)) {
return;
}
size_t i = 0;
while (bson_iter_next(&sub)) {
_childs.push_back(sub);
if (type == BSON_TYPE_DOCUMENT) {
_childs_index[bson_iter_key(&sub)] = i++;
}
}
}
}
const bson_iter_t* it;
bson_type_t type;
bool inited;
std::vector<bson_iter_t> _childs; // childs
node_index _childs_index;
};
class BsonDecoder {
public:
// if length==0, length will get from data
template <class T>
bool decode(const uint8_t*data, size_t length, T&val) {
length = (length>0)?length:BSON_UINT32_TO_LE(*(int32_t*)data);
bson_t b;
bson_iter_t it;
bson_init_static(&b, data, length);
bson_iter_init(&it, &b);
BsonNode node(&it);
node.init(true);
return XDecoder<BsonNode>(NULL, (const char*)NULL, node).decode(val, NULL);
}
template <class T>
bool decode(const std::string&data, T&val) {
return decode((const uint8_t*)data.data(), data.length(), val);
}
};
template<>struct is_xpack_type_spec<BsonNode, bson_oid_t> {static bool const value = true;};
template<>struct is_xpack_type_spec<BsonNode, bson_date_time_t> {static bool const value = true;};
template<>struct is_xpack_type_spec<BsonNode, bson_timestamp_t> {static bool const value = true;};
template<>struct is_xpack_type_spec<BsonNode, bson_decimal128_t> {static bool const value = true;};
template<>struct is_xpack_type_spec<BsonNode, bson_binary_t> {static bool const value = true;};
template<>struct is_xpack_type_spec<BsonNode, bson_regex_t> {static bool const value = true;};
}
#endif

246
util/xpack/bson_encoder.h Executable file
View file

@ -0,0 +1,246 @@
/*
* Copyright (C) 2021 Duowan Inc. All rights reserved.
*
* 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 __X_PACK_BSON_ENCODER_H
#define __X_PACK_BSON_ENCODER_H
#include <libbson-1.0/bson.h>
#include "xpack/util.h"
#include "xpack/xencoder.h"
#include "bson_type.h"
namespace xpack {
class BsonWriter: private noncopyable {
friend class BsonBuilder;
struct Node {
const char *key;
Node *parent;
bson_t data;
Node(const char *_key = NULL, Node *_parent=NULL):key(_key), parent(_parent) {
if (NULL == parent) {
bson_init(&data);
}
}
~Node() {
if (NULL == parent) {
bson_destroy(&data);
}
}
};
friend class XEncoder<BsonWriter>;
friend class BsonEncoder;
const static bool support_null = true;
public:
BsonWriter() {
cur = new Node(NULL, NULL);
nodes.push_back(cur);
}
~BsonWriter() {
std::list<Node*>::iterator it;
for (it = nodes.begin(); it!=nodes.end(); ++it) {
delete *it;
}
}
private:
inline static const char *Name() {
return "bson";
}
inline const char *IndexKey(size_t index) {
if (index > indexStr.size()+1) {
std::string s = Util::itoa(index);
errCase.push_back(s);
return errCase.back().c_str();
}
if (index >= indexStr.size()) {
std::string s = Util::itoa(index);
indexStr.push_back(s);
}
return indexStr[index].c_str();
}
// return bson binary data
std::string String() const {
return std::string((const char*)bson_get_data(&cur->data), cur->data.len);
}
// return json format string
std::string Json() const {
size_t len;
char *jstr = bson_as_json(&cur->data, &len);
std::string ret(jstr);
bson_free(jstr);
return ret;
}
void ArrayBegin(const char *key, const Extend *ext) {
(void)ext;
if (key != NULL) {
Node *tmp = new Node(key, cur);
nodes.push_back(tmp);
bson_append_array_begin(&cur->data, key, strlen(key), &tmp->data);
cur = tmp;
}
}
void ArrayEnd(const char *key, const Extend *ext) {
(void)ext;
if (NULL != key) {
bson_append_array_end(&cur->parent->data, &cur->data);
cur = cur->parent; // do not pop and delete
}
}
void ObjectBegin(const char *key, const Extend *ext) {
(void)ext;
if (key != NULL) {
Node *tmp = new Node(key, cur);
nodes.push_back(tmp);
bson_append_document_begin(&cur->data, key, strlen(key), &tmp->data);
cur = tmp;
}
}
void ObjectEnd(const char *key, const Extend *ext) {
(void)ext;
if (NULL != key) { // in case of inherit, object key is NULL
bson_append_document_end(&cur->parent->data, &cur->data);
cur = cur->parent; // do not pop and delete
}
}
bool WriteNull(const char*key, const Extend *ext) {
(void)ext;
bson_append_null(&cur->data, key, strlen(key));
return true;
}
bool encode_bool(const char*key, const bool &val, const Extend *ext) {
(void)ext;
bson_append_bool(&cur->data, key, strlen(key), val);
return true;
}
bool encode_string(const char*key, const char*val, size_t length, const Extend *ext) {
(void)ext;
bson_append_utf8(&cur->data, key, strlen(key), val, length);
return true;
}
bool encode_string(const char*key, const std::string& val, const Extend *ext) {
(void)ext;
bson_append_utf8(&cur->data, key, strlen(key), (const char*)val.data(), val.length());
return true;
}
bool encode_string(const char*key, const char* val, const Extend *ext) {
(void)ext;
if (NULL != val) {
bson_append_utf8(&cur->data, key, strlen(key), val, strlen(val));
} else {
bson_append_utf8(&cur->data, key, strlen(key), "", 0);
}
return true;
}
#define X_PACK_BSON_ENCODE_NUMBER(vtype, ftype, ctype) \
inline bool encode_number(const char*key, const vtype&val, const Extend*ext) {\
(void)ext;\
bson_append_##ftype(&cur->data, key, strlen(key), (vtype)val);\
return true; \
}
X_PACK_BSON_ENCODE_NUMBER(char, int32, int32_t)
X_PACK_BSON_ENCODE_NUMBER(signed char, int32, int32_t)
X_PACK_BSON_ENCODE_NUMBER(unsigned char, int32, int32_t)
X_PACK_BSON_ENCODE_NUMBER(short, int32, int32_t)
X_PACK_BSON_ENCODE_NUMBER(unsigned short, int32, int32_t)
X_PACK_BSON_ENCODE_NUMBER(int, int32, int32_t)
X_PACK_BSON_ENCODE_NUMBER(unsigned int, int32, int32_t)
X_PACK_BSON_ENCODE_NUMBER(long, int64, int64_t)
X_PACK_BSON_ENCODE_NUMBER(unsigned long, int64, int64_t)
X_PACK_BSON_ENCODE_NUMBER(long long, int64, int64_t)
X_PACK_BSON_ENCODE_NUMBER(unsigned long long, int64, int64_t)
X_PACK_BSON_ENCODE_NUMBER(float, double, double)
X_PACK_BSON_ENCODE_NUMBER(double, double, double)
X_PACK_BSON_ENCODE_NUMBER(long double, double, double)
// bson types
bool encode_type_spec(const char*key, const bson_oid_t &val, const Extend *ext) {
(void)ext;
bson_append_oid(&cur->data, key, strlen(key), &val);
return true;
}
bool encode_type_spec(const char*key, const bson_date_time_t &val, const Extend *ext) {
(void)ext;
bson_append_date_time(&cur->data, key, strlen(key), val.ts);
return true;
}
bool encode_type_spec(const char *key, const bson_timestamp_t &val, const Extend *ext) {
(void)ext;
bson_append_timestamp(&cur->data, key, strlen(key), val.timestamp, val.increment);
return true;
}
bool encode_type_spec(const char *key, const bson_decimal128_t &val, const Extend *ext) {
(void)ext;
return bson_append_decimal128(&cur->data, key, strlen(key), &val);
}
bool encode_type_spec(const char *key, const bson_regex_t &val, const Extend *ext) {
(void)ext;
return bson_append_regex(&cur->data, key, strlen(key), val.pattern.c_str(), val.options.c_str());
}
bool encode_type_spec(const char *key, const bson_binary_t &val, const Extend *ext) {
(void)ext;
return bson_append_binary(&cur->data, key, strlen(key), val.subType, (const uint8_t*)val.data.data(), val.data.length());
}
private:
std::vector<std::string> indexStr;
std::list<std::string> errCase;
std::list<Node*> nodes;
Node *cur;
};
class BsonEncoder {
public:
BsonEncoder() {
}
template <class T>
std::string encode(const T&val) {
BsonWriter wr;
XEncoder<BsonWriter> en(wr);
en.encode(NULL, val, NULL);
return wr.String();
}
template <class T>
std::string encode_as_json(const T&val) {
BsonWriter wr;
XEncoder<BsonWriter> en(wr);
en.encode(NULL, val, NULL);
return wr.Json();
}
};
template<>struct is_xpack_type_spec<BsonWriter, bson_oid_t> {static bool const value = true;};
template<>struct is_xpack_type_spec<BsonWriter, bson_date_time_t> {static bool const value = true;};
template<>struct is_xpack_type_spec<BsonWriter, bson_timestamp_t> {static bool const value = true;};
template<>struct is_xpack_type_spec<BsonWriter, bson_decimal128_t> {static bool const value = true;};
template<>struct is_xpack_type_spec<BsonWriter, bson_binary_t> {static bool const value = true;};
template<>struct is_xpack_type_spec<BsonWriter, bson_regex_t> {static bool const value = true;};
}
#endif

450
util/xpack/bson_type.h Executable file
View file

@ -0,0 +1,450 @@
/*
* Copyright (C) 2021 Duowan Inc. All rights reserved.
*
* 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 __X_PACK_BSON_TYPE_H
#define __X_PACK_BSON_TYPE_H
#include <map>
#include <string>
#include <sstream>
#include <ctime>
#include <time.h>
#include <stdint.h>
#include <libbson-1.0/bson.h>
#include "xencoder.h"
#include "xdecoder.h"
#include "xpack.h"
/*
// define in this file
bson_date_time_t
bson_timestamp_t
bson_binary_t
bson_regex_t
// define in bson.h
bson_oid_t
bson_decimal128_t
*/
// define in global namespace for consistency with bson
struct bson_timestamp_t {
uint32_t timestamp;
uint32_t increment;
bson_timestamp_t(const uint32_t&t=0, const uint32_t&i=0):timestamp(t),increment(i){}
XPACK(A(timestamp, "t", increment, "i"));
};
struct bson_regex_t {
std::string pattern; // libbson not escape string, this should be a bug
std::string options;
// ver==1 {"$regularExpression": {pattern: <string>, "options": <string>"}}
// ver==2 {"$regex: <string>, $options: <string>"}
int ver; // ver==1
bson_regex_t():ver(2){}
};
struct bson_date_time_t {
int64_t ts; // milliseconds
bson_date_time_t(const int64_t&t=0):ts(t){}
operator int64_t() const {return ts;}
// parse rfc3339 time format(milliseconds). C++ doesn't even have a full-featured standard time library T_T
// "2000-01-01T01:01:01[.001]Z[07:00]" string should ends with \0
bool parse_rfc3339(const char buf[]) {
struct tm tm;
memset((void*)&tm, 0, sizeof(tm));
const char *end = strptime(buf, "%Y-%m-%dT%H:%M:%S", &tm);
if (end == NULL) {
return false;
}
int msec = 0;
int offset = 0;
try {
size_t pos;
if (*end == '.') {
msec = std::stoi(end+1, &pos);
if (pos==0 || pos > 3) {
return false;
}
end += pos+1;
}
if (*end != 'Z') {
pos = 0;
int hour = std::stoi(end, &pos);
if (pos == 0 || pos > 3) {
return false;
}
end += pos;
if (*end != ':') {
return false;
}
int min = std::stoi(end+1, &pos);
if (hour > 0) {
offset = hour*3600 + min*60;
} else {
offset = -(-hour*3600 + min*60);
}
} else {
++end;
if (*end != '\0') {
return false;
}
}
} catch(...) {
return false;
}
time_t tmp = mktime(&tm);
ts = tmp*1000 + msec - offset - timezone*1000;
return true;
}
};
struct bson_binary_t {
std::string data; // std::string is more general, so don't use std::basic_string<uint8_t>
bson_subtype_t subType;
inline static const char *b64() {
return "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
}
// b64_ntop and b64_pton maybe should move to a public place
static void b64_ntop(const uint8_t *bin, size_t size, std::string&out) {
const char *b = b64();
out.reserve((size+2)/3*4);
size_t n = size/3*3;
size_t i;
for (i=0; i<n; i+=3) {
out.push_back(b[bin[i]>>2]);
out.push_back(b[((bin[i]&0x3)<<4) | (bin[i+1]>>4)]);
out.push_back(b[((bin[i+1]&0xf)<<2) | (bin[i+2]>>6)]);
out.push_back(b[bin[i+2]&0x3f]);
}
size_t left = size%3;
if (left == 1) {
out.push_back(b[bin[i]>>2]);
out.push_back(b[(bin[i]&0x3)<<4]);
out.push_back('=');
out.push_back('=');
} else if (left == 2) {
out.push_back(b[bin[i]>>2]);
out.push_back(b[((bin[i]&0x3)<<4) | (bin[i+1]>>4)]);
out.push_back(b[(bin[i+1]&0xf)<<2]);
out.push_back('=');
}
}
static bool b64_pton(const std::string&in, std::string&out) {
static uint8_t bmap[256] = {0};
if (bmap[255] == 0) { // init bmap, thread safe
for (size_t i=0; i<255; i++) bmap[i] = 0xff;
const char *p = b64();
for (uint8_t i=0; p[i]!='\0'; ++i) bmap[(int)p[i]] = i;
bmap[255] = 0xff;
}
size_t size = in.length();
if (size==0 || 0 != (size&0x3)) {
return false;
}
out.reserve((size+3)/4*3);
const char *data = in.data();
for (size_t i=0; i<size; i+=4) {
uint8_t i1 = bmap[(uint8_t)data[i]];
uint8_t i2 = bmap[(uint8_t)data[i+1]];
uint8_t i3 = bmap[(uint8_t)data[i+2]];
uint8_t i4 = bmap[(uint8_t)data[i+3]];
if (i1!=0xff && i2!=0xff) {
out.push_back((char)((i1<<2) | (i2>>4)));
} else {
return false;
}
if (i3 != 0xff) {
out.push_back((char)((i2<<4) | (i3>>2)));
} else if (data[i+2]=='=' && data[i+3]=='=' && i+4==size) {
return true;
} else {
return false;
}
if (i4 != 0xff) {
out.push_back((char)((i3<<6) | i4));
} else if (data[i+3]=='=' && i+4==size) {
return true;
} else {
return false;
}
}
return true;
}
};
namespace xpack {
// bson type to json
// ref https://github.com/mongodb/specifications/blob/master/source/extended-json.rst#conversion-table
// Relaxed Extended JSON Format Only
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ObjectId ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ //
// {"$oid":"xxxxx"}
template<>
struct is_xpack_xtype<bson_oid_t> {static bool const value = true;};
template<class Decoder>
bool xpack_xtype_decode(Decoder &de, bson_oid_t &val, const Extend *ext) {
(void)ext;
char buf[25];
bool ret = de.decode("$oid", buf, NULL);
if (ret) {
bson_oid_init_from_string(&val, buf);
}
return ret;
}
template<class Encoder>
bool xpack_xtype_encode(Encoder &en, const char*key, const bson_oid_t &val, const Extend *ext) {
char buf[25];
bson_oid_to_string(&val, buf);
en.ObjectBegin(key, ext);
en.encode("$oid", buf, NULL);
en.ObjectEnd(key, ext);
return true;
}
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ date time ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~//
// {"$date": 12345}
// {"$date":"2006-01-02T15:04:05Z"}
// {"$date":{"$numberLong":"123456"}}
template<>
struct is_xpack_xtype<bson_date_time_t> {static bool const value = true;};
template<class Decoder>
bool xpack_xtype_decode(Decoder &de, bson_date_time_t &val, const Extend *ext) {
(void)ext;
static const char *dkey = "$date";
bool ret;
try {
ret = de.decode(dkey, val.ts, NULL);
return ret;
} catch (...){}
char buf[25];
try {
ret = de.decode(dkey, buf, NULL);
if (ret) {
ret = val.parse_rfc3339(buf);
}
return ret;
} catch (...) {}
try {
ret = de.decode("$numberLong", buf, NULL);
if (ret) {
ret = Util::atoi(buf, val.ts);
}
return ret;
} catch (...) {}
return false;
}
template<class Encoder>
bool xpack_xtype_encode(Encoder &en, const char*key, const bson_date_time_t &val, const Extend *ext) {
en.ObjectBegin(key, ext);
if (val.ts >= 0) {
char buf[32];
int msecs = int(val.ts % 1000);
time_t secs = (time_t)(val.ts/1000);
#ifndef _MSC_VER
struct tm posix_date;
size_t sz = strftime(buf, sizeof buf, "%FT%T", gmtime_r(&secs, &posix_date));
#else
size_t sz = strftime(buf, sizeof buf, "%FT%T", gmtime(&secs));
#endif
if (msecs > 0) {
sprintf(&buf[sz], ".%03dZ", msecs);
} else {
strcpy(&buf[sz], "Z");
}
en.encode("$date", buf, NULL);
} else { // key:{"$date":{"$numberLong":ts}} ts use string not int
en.ObjectBegin("$date", NULL);
en.encode("$numberLong", Util::itoa(val.ts), NULL);
en.ObjectEnd("$date", NULL);
}
en.ObjectEnd(key, ext);
return true;
}
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ timestamp ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ //
// {"$timestamp":{"t":xxx, "i":xxx}}
template<>
struct is_xpack_xtype<bson_timestamp_t> {static bool const value = true;};
template<class Decoder>
bool xpack_xtype_decode(Decoder &de, bson_timestamp_t &val, const Extend *ext) {
(void)ext;
bool ret = de.decode_struct("$timestamp", val, NULL);
return ret;
}
template<class Encoder>
bool xpack_xtype_encode(Encoder &en, const char*key, const bson_timestamp_t &val, const Extend *ext) {
en.ObjectBegin(key, ext);
en.encode_struct("$timestamp", val, NULL);
en.ObjectEnd(key, ext);
return true;
}
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ numberDecimal ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ //
// {"$numberDecimal":"123.4"}
template<>
struct is_xpack_xtype<bson_decimal128_t> {static bool const value = true;};
template<class Decoder>
bool xpack_xtype_decode(Decoder &de, bson_decimal128_t &val, const Extend *ext) {
(void)ext;
char buf[BSON_DECIMAL128_STRING];
bool ret = de.decode("$numberDecimal", buf, NULL);
if (ret) {
bson_decimal128_from_string(buf, &val);
}
return ret;
}
template<class Encoder>
bool xpack_xtype_encode(Encoder &en, const char*key, const bson_decimal128_t &val, const Extend *ext) {
char buf[BSON_DECIMAL128_STRING];
bson_decimal128_to_string(&val, buf);
en.ObjectBegin(key, ext);
en.encode("$numberDecimal", buf, NULL);
en.ObjectEnd(key, ext);
return true;
}
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ binary ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ //
// {"$binary":{"base64":"aGVsbG8=","subType":"80"}}
// {"$binary":"aGVsbG8=", "$type":"80"}
template<>
struct is_xpack_xtype<bson_binary_t> {static bool const value = true;};
template<class Decoder>
bool xpack_xtype_decode(Decoder &de, bson_binary_t &val, const Extend *ext) {
(void)ext;
try {
std::string subType;
std::string data;
bool ret1;
bool ret2;
Decoder st = de.Find("$type", NULL);
if (!st) {
Decoder bd = de.Find("$binary", NULL);
if (!bd) {
return false;
}
ret1 = bd.decode("subType", subType, NULL);
ret2 = bd.decode("base64", data, NULL);
} else {
ret1 = st.decode(subType, NULL);
ret2 = de.decode("$binary", data, NULL);
}
if (ret1 && (subType.length()==1 || subType.length()==2)) {
int st = 0;
for (size_t i=0; i<subType.length(); ++i) {
int tmp = (int)(subType[i]);
if (tmp>='0' && tmp<='9') {
tmp -= '0';
} else if (tmp>='a' && tmp<='f') {
tmp = 10 + tmp-'a';
} else if (tmp>='A' && tmp<='F') {
tmp = 10 + tmp-'A';
} else {
return false;
}
st = (st<<4) + tmp;
}
val.subType = (bson_subtype_t)st;
} else {
return false;
}
if (ret2) {
ret2 = bson_binary_t::b64_pton(data, val.data);
}
return ret2;
} catch (...){
return false;
}
return true;
}
template<class Encoder>
bool xpack_xtype_encode(Encoder &en, const char*key, const bson_binary_t &val, const Extend *ext) {
en.ObjectBegin(key, ext);
en.ObjectBegin("$binary", NULL);
std::string b64;
bson_binary_t::b64_ntop((const uint8_t*)val.data.data(), val.data.length(), b64);
en.encode("base64", b64, NULL);
std::stringstream ss;
ss<<std::hex<<val.subType;
en.encode("subType", ss.str(), NULL);
en.ObjectEnd("$binary", NULL);
en.ObjectEnd(key, ext);
return true;
}
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ regex ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ //
template<>
struct is_xpack_xtype<bson_regex_t> {static bool const value = true;};
template<class Decoder>
bool xpack_xtype_decode(Decoder &de, bson_regex_t &val, const Extend *ext) {
(void)ext;
Decoder v1 = de.Find("$regularExpression", NULL);
try {
if (v1) {
v1.decode("pattern", val.pattern, NULL);
v1.decode("options", val.options, NULL);
val.ver = 1;
} else {
de.decode("$regex", val.pattern, NULL);
de.decode("$options", val.options, NULL);
val.ver = 2;
}
} catch(...){}
return true;
}
template<class Encoder>
bool xpack_xtype_encode(Encoder &en, const char*key, const bson_regex_t &val, const Extend *ext) {
en.ObjectBegin(key, ext);
if (val.ver == 1) {
en.ObjectBegin("$regularExpression", NULL);
en.encode("pattern", val.pattern, NULL);
en.encode("options", val.options, NULL);
en.ObjectEnd("$regularExpression", NULL);
} else {
en.encode("$regex", val.pattern, NULL);
en.encode("$options", val.options, NULL);
}
en.ObjectEnd(key, ext);
return true;
}
}
#endif

25
util/xpack/config.h Executable file
View file

@ -0,0 +1,25 @@
/*
* Copyright (C) 2021 Duowan Inc. All rights reserved.
*
* 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 __X_PACK_CONFIG_H
#define __X_PACK_CONFIG_H
// support qt
//#define XPACK_SUPPORT_QT
#endif

27
util/xpack/example/alias.cpp Executable file
View file

@ -0,0 +1,27 @@
#include <iostream>
#include "xpack/json.h"
#include "xpack/xml.h"
using namespace std;
struct Test {
long uid;
string name;
XPACK(A(uid, "id xml:xid"), O(name)); // xml use 'xid' and others use 'id'
};
int main() {
Test t;
string json="{\"id\":123, \"name\":\"Pony\"}";
xpack::json::decode(json, t);
cout<<t.uid<<endl;
string xstr = xpack::xml::encode(t, "root");
cout<<xstr<<endl;
Test t1;
xpack::xml::decode(xstr, t1);
cout<<t1.uid<<endl;
return 0;
}

23
util/xpack/example/bitfield.cpp Executable file
View file

@ -0,0 +1,23 @@
#include <iostream>
#include "xpack/json.h"
#include "xpack/xml.h"
using namespace std;
struct Test {
short ver:8;
short len:8;
string name;
XPACK(B(F(0), ver, len), O(name));
};
int main() {
Test t;
string json="{\"ver\":4, \"len\":20, \"name\":\"IPv4\"}";
xpack::json::decode(json, t);
cout<<t.ver<<endl;
cout<<t.len<<endl;
cout<<xpack::xml::encode(t, "root")<<endl;
return 0;
}

View file

@ -0,0 +1,40 @@
/*
* Copyright (C) 2021 Duowan Inc. All rights reserved.
*
* 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 <iostream>
#include "xpack/json.h"
using namespace std;
struct Test {
char name[64];
char email[64];
XPACK(O(name, email));
};
int main(int argc, char *argv[]) {
(void)argc;
(void)argv;
Test t;
string json="{\"name\":\"Pony\", \"email\":\"pony@xpack.com\"}";
xpack::json::decode(json, t);
cout<<t.name<<endl;
cout<<t.email<<endl;
return 0;
}

66
util/xpack/example/custom.cpp Executable file
View file

@ -0,0 +1,66 @@
/*
* Copyright (C) 2021 Duowan Inc. All rights reserved.
*
* 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 <stdio.h>
#include <iostream>
#include "xpack/json.h"
using namespace std;
struct Test {
unsigned int uid;
string name;
XPACK(C(hexint, F(0), uid), O(name));
};
namespace xpack {
template <class Decoder>
bool hexint_decode(Decoder&de, Test&t, const char *name, unsigned int&data, const xpack::Extend *ext) {
(void)t;
string raw;
bool ret = de.decode(name, raw, ext);
if (ret) {
sscanf(raw.c_str(), "%x", &data);
}
return ret;
}
template <class Encoder>
bool hexint_encode(Encoder&en, const Test&t, const char *name, const unsigned &data, const xpack::Extend *ext) {
(void)t;
char buf[64];
sprintf(buf, "%x", data);
bool ret = en.encode(name, buf, ext);
return ret;
}
}
int main(int argc, char *argv[]) {
(void)argc;
(void)argv;
Test t;
t.uid = 127;
t.name = "PonyJack";
string str = xpack::json::encode(t);
cout<<str<<endl;
Test n;
xpack::json::decode(str, t);
cout<<t.uid<<endl;
return 0;
}

45
util/xpack/example/encoder.cpp Executable file
View file

@ -0,0 +1,45 @@
/*
* Copyright (C) 2021 Duowan Inc. All rights reserved.
*
* 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 <iostream>
#include "xpack/json.h"
#include "xpack/xml.h"
using namespace std;
int main(int argc, char *argv[]) {
(void)argc;
(void)argv;
vector<int> vi(3);
vi[0] = 1;
vi[1] = 2;
vi[2] = 3;
xpack::JsonWriter jw;
xpack::XEncoder<xpack::JsonWriter> je(jw);
xpack::XmlWriter xw;
xpack::XEncoder<xpack::XmlWriter> xe(xw);
je.ob(NULL).add("vv", vi).add("i",10).add("s","hello").oe();
xe.ob("root").add("vv", vi).add("i",10).add("s","hello").oe();
cout<<je.String()<<endl;
cout<<xe.String()<<endl;
return 0;
}

Some files were not shown because too many files have changed in this diff Show more