460 lines
14 KiB
C++
460 lines
14 KiB
C++
#include "utils.h"
|
|
#include <iostream>
|
|
#include <iomanip>
|
|
#include <sstream>
|
|
#include <chrono>
|
|
#include <ctime>
|
|
#include <fstream>
|
|
#include <algorithm>
|
|
#include <stdexcept>
|
|
|
|
// 实现 splitByDelimiter 方法
|
|
std::pair<std::string, std::string> StringUtils::splitByDelimiter(
|
|
const std::string &str,
|
|
char delimiter)
|
|
{
|
|
size_t pos = str.find(delimiter);
|
|
|
|
if (pos != std::string::npos)
|
|
{
|
|
// 找到分隔符,拆分字符串
|
|
std::string first = str.substr(0, pos);
|
|
std::string second = str.substr(pos + 1);
|
|
return std::make_pair(first, second);
|
|
}
|
|
|
|
// 未找到分隔符,返回原始字符串和空字符串
|
|
return std::make_pair(str, "");
|
|
}
|
|
|
|
// 实现 splitA_B 方法
|
|
std::pair<std::string, std::string> StringUtils::splitA_B(const std::string &str)
|
|
{
|
|
return splitByDelimiter(str, '_');
|
|
}
|
|
|
|
// 实现 splitToTwoParts 方法
|
|
bool StringUtils::splitToTwoParts(
|
|
const std::string &str,
|
|
std::string &part1,
|
|
std::string &part2,
|
|
char delimiter)
|
|
{
|
|
size_t pos = str.find(delimiter);
|
|
|
|
if (pos != std::string::npos)
|
|
{
|
|
part1 = str.substr(0, pos);
|
|
part2 = str.substr(pos + 1);
|
|
return true; // 成功拆分
|
|
}
|
|
|
|
// 未找到分隔符
|
|
part1 = str;
|
|
part2 = "";
|
|
return false; // 拆分失败
|
|
}
|
|
|
|
// 实现 splitStrict 方法
|
|
std::pair<std::string, std::string> StringUtils::splitStrict(
|
|
const std::string &str,
|
|
char delimiter)
|
|
{
|
|
size_t pos = str.find(delimiter);
|
|
|
|
// 检查是否找到分隔符
|
|
if (pos == std::string::npos)
|
|
{
|
|
throw std::invalid_argument("Delimiter '" + std::string(1, delimiter) +
|
|
"' not found in string: \"" + str + "\"");
|
|
}
|
|
|
|
// 检查是否有多于一个分隔符
|
|
size_t nextPos = str.find(delimiter, pos + 1);
|
|
if (nextPos != std::string::npos)
|
|
{
|
|
throw std::invalid_argument("Multiple delimiters '" + std::string(1, delimiter) +
|
|
"' found in string: \"" + str + "\"");
|
|
}
|
|
|
|
std::string first = str.substr(0, pos);
|
|
std::string second = str.substr(pos + 1);
|
|
|
|
return std::make_pair(first, second);
|
|
}
|
|
|
|
namespace utils
|
|
{
|
|
|
|
// Base64 字符表
|
|
const std::string base64_chars =
|
|
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
|
"abcdefghijklmnopqrstuvwxyz"
|
|
"0123456789+/";
|
|
|
|
// 判断字符是否为有效的 Base64 字符
|
|
static bool is_base64(unsigned char c)
|
|
{
|
|
return (isalnum(c) || (c == '+') || (c == '/'));
|
|
}
|
|
|
|
std::string get_current_time()
|
|
{
|
|
auto now = std::chrono::system_clock::now();
|
|
auto in_time_t = std::chrono::system_clock::to_time_t(now);
|
|
auto ms = std::chrono::duration_cast<std::chrono::milliseconds>(
|
|
now.time_since_epoch()) %
|
|
1000;
|
|
|
|
std::stringstream ss;
|
|
ss << std::put_time(std::gmtime(&in_time_t), "%Y-%m-%dT%H:%M:%S");
|
|
ss << "." << std::setfill('0') << std::setw(3) << ms.count() << "Z";
|
|
return ss.str();
|
|
}
|
|
|
|
std::string get_current_timestamp()
|
|
{
|
|
auto now = std::chrono::system_clock::now();
|
|
auto in_time_t = std::chrono::system_clock::to_time_t(now);
|
|
auto milliseconds = std::chrono::duration_cast<std::chrono::milliseconds>(
|
|
now.time_since_epoch()) %
|
|
1000;
|
|
|
|
std::stringstream ss;
|
|
ss << std::put_time(std::localtime(&in_time_t), "%Y-%m-%d %H:%M:%S");
|
|
ss << '.' << std::setfill('0') << std::setw(3) << milliseconds.count();
|
|
return ss.str();
|
|
}
|
|
|
|
// void setup_cors_headers(httplib::Response &res)
|
|
// {
|
|
// res.set_header("Access-Control-Allow-Origin", "*");
|
|
// res.set_header("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS, PATCH");
|
|
// res.set_header("Access-Control-Allow-Headers", "Content-Type, Authorization, X-Requested-With, X-API-Key, Accept, Origin");
|
|
// res.set_header("Access-Control-Expose-Headers", "Content-Length, Content-Type, X-Request-Id");
|
|
// res.set_header("Access-Control-Max-Age", "86400"); // 24 小时
|
|
// res.set_header("Vary", "Origin");
|
|
// }
|
|
|
|
json create_error_response(int code, const std::string &message, const std::string &details)
|
|
{
|
|
json response = {
|
|
{"success", false},
|
|
{"code", code},
|
|
{"message", message},
|
|
{"timestamp", get_current_time()}};
|
|
|
|
if (!details.empty())
|
|
{
|
|
response["details"] = details;
|
|
}
|
|
|
|
return response;
|
|
}
|
|
|
|
json create_success_response(const json &data, const std::string &message)
|
|
{
|
|
return {
|
|
{"success", true},
|
|
{"code", 0},
|
|
{"message", message},
|
|
{"data", data},
|
|
{"timestamp", get_current_time()}};
|
|
}
|
|
|
|
bool is_valid_utf8(const std::string &str)
|
|
{
|
|
for (size_t i = 0; i < str.size(); ++i)
|
|
{
|
|
unsigned char c = str[i];
|
|
if (c <= 0x7F)
|
|
{
|
|
continue; // ASCII 字符
|
|
}
|
|
else if ((c & 0xE0) == 0xC0)
|
|
{
|
|
// 2 字节 UTF-8
|
|
if (i + 1 >= str.size() || (str[i + 1] & 0xC0) != 0x80)
|
|
{
|
|
return false;
|
|
}
|
|
i += 1;
|
|
}
|
|
else if ((c & 0xF0) == 0xE0)
|
|
{
|
|
// 3 字节 UTF-8
|
|
if (i + 2 >= str.size() || (str[i + 1] & 0xC0) != 0x80 || (str[i + 2] & 0xC0) != 0x80)
|
|
{
|
|
return false;
|
|
}
|
|
i += 2;
|
|
}
|
|
else if ((c & 0xF8) == 0xF0)
|
|
{
|
|
// 4 字节 UTF-8
|
|
if (i + 3 >= str.size() || (str[i + 1] & 0xC0) != 0x80 ||
|
|
(str[i + 2] & 0xC0) != 0x80 || (str[i + 3] & 0xC0) != 0x80)
|
|
{
|
|
return false;
|
|
}
|
|
i += 3;
|
|
}
|
|
else
|
|
{
|
|
return false; // 非法 UTF-8 字节
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
std::string sanitize_utf8(const std::string &str)
|
|
{
|
|
std::string result;
|
|
result.reserve(str.size());
|
|
|
|
for (size_t i = 0; i < str.size(); ++i)
|
|
{
|
|
unsigned char c = str[i];
|
|
if (c <= 0x7F)
|
|
{
|
|
result += c; // ASCII 字符
|
|
}
|
|
else if ((c & 0xE0) == 0xC0)
|
|
{
|
|
// 2 字节 UTF-8
|
|
if (i + 1 < str.size() && (str[i + 1] & 0xC0) == 0x80)
|
|
{
|
|
result += c;
|
|
result += str[i + 1];
|
|
i += 1;
|
|
}
|
|
}
|
|
else if ((c & 0xF0) == 0xE0)
|
|
{
|
|
// 3 字节 UTF-8
|
|
if (i + 2 < str.size() && (str[i + 1] & 0xC0) == 0x80 && (str[i + 2] & 0xC0) == 0x80)
|
|
{
|
|
result += c;
|
|
result += str[i + 1];
|
|
result += str[i + 2];
|
|
i += 2;
|
|
}
|
|
}
|
|
else if ((c & 0xF8) == 0xF0)
|
|
{
|
|
// 4 字节 UTF-8
|
|
if (i + 3 < str.size() && (str[i + 1] & 0xC0) == 0x80 &&
|
|
(str[i + 2] & 0xC0) == 0x80 && (str[i + 3] & 0xC0) == 0x80)
|
|
{
|
|
result += c;
|
|
result += str[i + 1];
|
|
result += str[i + 2];
|
|
result += str[i + 3];
|
|
i += 3;
|
|
}
|
|
}
|
|
// 跳过非法 UTF-8 字节
|
|
}
|
|
return result;
|
|
}
|
|
|
|
json create_api_response(bool success, int code, const std::string &msg,
|
|
const std::string &req_code, const std::string &req_from,
|
|
const std::string &req_cmd, const json &res_data)
|
|
{
|
|
json response;
|
|
response["success"] = success;
|
|
response["code"] = code;
|
|
response["msg"] = msg;
|
|
response["req_code"] = req_code;
|
|
response["req_from"] = req_from;
|
|
response["req_cmd"] = req_cmd;
|
|
response["timestamp"] = std::time(nullptr);
|
|
|
|
if (!res_data.is_null())
|
|
{
|
|
response["res_data"] = res_data;
|
|
}
|
|
else
|
|
{
|
|
response["res_data"] = json::object();
|
|
}
|
|
|
|
return response;
|
|
}
|
|
|
|
// Base64 解码函数
|
|
std::string base64_decode(const std::string &encoded_string)
|
|
{
|
|
int in_len = encoded_string.size();
|
|
int i = 0;
|
|
int j = 0;
|
|
int in_ = 0;
|
|
unsigned char char_array_4[4], char_array_3[3];
|
|
std::string ret;
|
|
|
|
// 去掉输入中的换行和空格
|
|
std::string clean_encoded;
|
|
for (char c : encoded_string)
|
|
{
|
|
if (c != '\n' && c != '\r' && c != ' ')
|
|
{
|
|
clean_encoded += c;
|
|
}
|
|
}
|
|
in_len = clean_encoded.size();
|
|
|
|
while (in_len-- && (clean_encoded[in_] != '=') && is_base64(clean_encoded[in_]))
|
|
{
|
|
char_array_4[i++] = clean_encoded[in_];
|
|
in_++;
|
|
if (i == 4)
|
|
{
|
|
for (i = 0; i < 4; i++)
|
|
{
|
|
size_t pos = base64_chars.find(char_array_4[i]);
|
|
if (pos == std::string::npos)
|
|
{
|
|
throw std::runtime_error("Invalid base64 character");
|
|
}
|
|
char_array_4[i] = pos;
|
|
}
|
|
|
|
char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4);
|
|
char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2);
|
|
char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3];
|
|
|
|
for (i = 0; i < 3; i++)
|
|
{
|
|
ret += char_array_3[i];
|
|
}
|
|
i = 0;
|
|
}
|
|
}
|
|
|
|
if (i)
|
|
{
|
|
for (j = i; j < 4; j++)
|
|
{
|
|
char_array_4[j] = 0;
|
|
}
|
|
|
|
for (j = 0; j < 4; j++)
|
|
{
|
|
size_t pos = base64_chars.find(char_array_4[j]);
|
|
if (pos == std::string::npos && j >= i)
|
|
{
|
|
// 补齐的占位字符按 0 处理
|
|
char_array_4[j] = 0;
|
|
}
|
|
else if (pos != std::string::npos)
|
|
{
|
|
char_array_4[j] = pos;
|
|
}
|
|
else
|
|
{
|
|
throw std::runtime_error("Invalid base64 character");
|
|
}
|
|
}
|
|
|
|
char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4);
|
|
char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2);
|
|
char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3];
|
|
|
|
for (j = 0; j < i - 1; j++)
|
|
{
|
|
ret += char_array_3[j];
|
|
}
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
// 将 Base64 编码的 URDF 转成字符串
|
|
std::string base64_to_urdf(const std::string &base64_urdf)
|
|
{
|
|
try
|
|
{
|
|
std::string urdf_content = base64_decode(base64_urdf);
|
|
|
|
// 校验解码结果是否像合法的 URDF/XML
|
|
if (urdf_content.find("<?xml") != std::string::npos ||
|
|
urdf_content.find("<robot") != std::string::npos)
|
|
{
|
|
return urdf_content;
|
|
}
|
|
else
|
|
{
|
|
throw std::runtime_error("Decoded content does not appear to be a valid URDF file");
|
|
}
|
|
}
|
|
catch (const std::exception &e)
|
|
{
|
|
throw std::runtime_error(std::string("Failed to decode URDF: ") + e.what());
|
|
}
|
|
}
|
|
|
|
// 将 Base64 编码的 URDF 保存到文件
|
|
bool save_base64_urdf_to_file(const std::string &base64_urdf, const std::string &filename)
|
|
{
|
|
try
|
|
{
|
|
std::string urdf_content = base64_to_urdf(base64_urdf);
|
|
return save_urdf_string_to_file(urdf_content, filename);
|
|
}
|
|
catch (const std::exception &e)
|
|
{
|
|
std::cerr << "Error saving base64 URDF to file: " << e.what() << std::endl;
|
|
return false;
|
|
}
|
|
}
|
|
|
|
// 将 URDF 字符串保存到文件
|
|
bool save_urdf_string_to_file(const std::string &urdf_content, const std::string &filename)
|
|
{
|
|
try
|
|
{
|
|
std::ofstream out_file(filename);
|
|
if (!out_file)
|
|
{
|
|
throw std::runtime_error("Cannot create output file: " + filename);
|
|
}
|
|
|
|
out_file << urdf_content;
|
|
out_file.close();
|
|
|
|
std::cout << "URDF file successfully saved to: " << filename << std::endl;
|
|
std::cout << "File size: " << urdf_content.size() << " bytes" << std::endl;
|
|
|
|
return true;
|
|
}
|
|
catch (const std::exception &e)
|
|
{
|
|
std::cerr << "Error saving URDF to file: " << e.what() << std::endl;
|
|
return false;
|
|
}
|
|
}
|
|
|
|
// 校验 Base64 字符串是否可解析为合法 URDF
|
|
bool validate_urdf_base64(const std::string &base64_urdf)
|
|
{
|
|
try
|
|
{
|
|
std::string urdf_content = base64_to_urdf(base64_urdf);
|
|
|
|
// 基础 URDF 结构校验
|
|
bool has_xml_decl = urdf_content.find("<?xml") != std::string::npos;
|
|
bool has_robot_tag = urdf_content.find("<robot") != std::string::npos;
|
|
bool has_link_tag = urdf_content.find("<link") != std::string::npos;
|
|
bool has_joint_tag = urdf_content.find("<joint") != std::string::npos;
|
|
|
|
return has_robot_tag && (has_link_tag || has_joint_tag);
|
|
}
|
|
catch (...)
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
} // namespace utils
|