更新配置文件获取方式
This commit is contained in:
parent
592f33ecf8
commit
7810209196
@ -1,120 +0,0 @@
|
||||
// Read an INI file into easy-to-access name/value pairs.
|
||||
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
// Copyright (C) 2009-2025, Ben Hoyt
|
||||
|
||||
// inih and INIReader are released under the New BSD license (see LICENSE.txt).
|
||||
// Go to the project home page for more info:
|
||||
//
|
||||
// https://github.com/benhoyt/inih
|
||||
|
||||
#ifndef INIREADER_H
|
||||
#define INIREADER_H
|
||||
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <cstdint>
|
||||
#include <vector>
|
||||
#include <set>
|
||||
|
||||
// Visibility symbols, required for Windows DLLs
|
||||
#ifndef INI_API
|
||||
#if defined _WIN32 || defined __CYGWIN__
|
||||
# ifdef INI_SHARED_LIB
|
||||
# ifdef INI_SHARED_LIB_BUILDING
|
||||
# define INI_API __declspec(dllexport)
|
||||
# else
|
||||
# define INI_API __declspec(dllimport)
|
||||
# endif
|
||||
# else
|
||||
# define INI_API
|
||||
# endif
|
||||
#else
|
||||
# if defined(__GNUC__) && __GNUC__ >= 4
|
||||
# define INI_API __attribute__ ((visibility ("default")))
|
||||
# else
|
||||
# define INI_API
|
||||
# endif
|
||||
#endif
|
||||
#endif
|
||||
|
||||
// Read an INI file into easy-to-access name/value pairs. (Note that I've gone
|
||||
// for simplicity here rather than speed, but it should be pretty decent.)
|
||||
class INIReader
|
||||
{
|
||||
public:
|
||||
// Construct INIReader and parse given filename. See ini.h for more info
|
||||
// about the parsing.
|
||||
INI_API explicit INIReader(const std::string& filename);
|
||||
|
||||
// Construct INIReader and parse given buffer. See ini.h for more info
|
||||
// about the parsing.
|
||||
INI_API explicit INIReader(const char *buffer, size_t buffer_size);
|
||||
|
||||
// Return the result of ini_parse(), i.e., 0 on success, line number of
|
||||
// first error on parse error, -1 on file open error, or -2 if there was a
|
||||
// memory allocation error.
|
||||
INI_API int ParseError() const;
|
||||
|
||||
// Return a message that describes the type of error that occurred.
|
||||
// It will return "" (empty string) if there was no error.
|
||||
INI_API std::string ParseErrorMessage() const;
|
||||
|
||||
// Get a string value from INI file, returning default_value if not found.
|
||||
INI_API std::string Get(const std::string& section, const std::string& name,
|
||||
const std::string& default_value) const;
|
||||
|
||||
// Get a string value from INI file, returning default_value if not found,
|
||||
// empty, or contains only whitespace.
|
||||
INI_API std::string GetString(const std::string& section, const std::string& name,
|
||||
const std::string& default_value) const;
|
||||
|
||||
// Get an integer (long) value from INI file, returning default_value if
|
||||
// not found or not a valid integer (decimal "1234", "-1234", or hex "0x4d2").
|
||||
INI_API long GetInteger(const std::string& section, const std::string& name, long default_value) const;
|
||||
|
||||
// Get a 64-bit integer (int64_t) value from INI file, returning default_value if
|
||||
// not found or not a valid integer (decimal "1234", "-1234", or hex "0x4d2").
|
||||
INI_API int64_t GetInteger64(const std::string& section, const std::string& name, int64_t default_value) const;
|
||||
|
||||
// Get an unsigned integer (unsigned long) value from INI file, returning default_value if
|
||||
// not found or not a valid unsigned integer (decimal "1234", or hex "0x4d2").
|
||||
INI_API unsigned long GetUnsigned(const std::string& section, const std::string& name, unsigned long default_value) const;
|
||||
|
||||
// Get an unsigned 64-bit integer (uint64_t) value from INI file, returning default_value if
|
||||
// not found or not a valid unsigned integer (decimal "1234", or hex "0x4d2").
|
||||
INI_API uint64_t GetUnsigned64(const std::string& section, const std::string& name, uint64_t default_value) const;
|
||||
|
||||
// Get a real (floating point double) value from INI file, returning
|
||||
// default_value if not found or not a valid floating point value
|
||||
// according to strtod().
|
||||
INI_API double GetReal(const std::string& section, const std::string& name, double default_value) const;
|
||||
|
||||
// Get a boolean value from INI file, returning default_value if not found or if
|
||||
// not a valid true/false value. Valid true values are "true", "yes", "on", "1",
|
||||
// and valid false values are "false", "no", "off", "0" (not case sensitive).
|
||||
INI_API bool GetBoolean(const std::string& section, const std::string& name, bool default_value) const;
|
||||
|
||||
// Return a newly-allocated vector of all section names, in alphabetical order.
|
||||
INI_API std::vector<std::string> Sections() const;
|
||||
|
||||
// Return a newly-allocated vector of keys in the given section, in alphabetical order.
|
||||
INI_API std::vector<std::string> Keys(const std::string& section) const;
|
||||
|
||||
// Return true if the given section exists (section must contain at least
|
||||
// one name=value pair).
|
||||
INI_API bool HasSection(const std::string& section) const;
|
||||
|
||||
// Return true if a value exists with the given section and field names.
|
||||
INI_API bool HasValue(const std::string& section, const std::string& name) const;
|
||||
|
||||
protected:
|
||||
int _error;
|
||||
std::map<std::string, std::string> _values;
|
||||
static std::string MakeKey(const std::string& section, const std::string& name);
|
||||
static int ValueHandler(void* user, const char* section, const char* name,
|
||||
const char* value);
|
||||
};
|
||||
|
||||
#endif // INIREADER_H
|
||||
48
include/config/TboxConfigClient.h
Normal file
48
include/config/TboxConfigClient.h
Normal file
@ -0,0 +1,48 @@
|
||||
#pragma once
|
||||
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <unordered_map>
|
||||
|
||||
#include "nlohmann/json.hpp"
|
||||
|
||||
// 前置声明,避免头文件膨胀
|
||||
namespace httplib
|
||||
{
|
||||
class Client;
|
||||
}
|
||||
|
||||
class TboxConfigClient
|
||||
{
|
||||
public:
|
||||
explicit TboxConfigClient(std::string server = "127.0.0.1", int port = 18080);
|
||||
|
||||
// ================= 拉取配置 =================
|
||||
bool fetch();
|
||||
|
||||
uint64_t version() const;
|
||||
bool isDirty() const;
|
||||
|
||||
// ================= 读取 =================
|
||||
std::optional<std::string> getString(std::string_view semantic) const;
|
||||
std::optional<int> getInt(std::string_view semantic) const;
|
||||
std::optional<bool> getBool(std::string_view semantic) const;
|
||||
|
||||
// ================= 修改(仅本地 staging) =================
|
||||
void setString(std::string_view semantic, const std::string& v);
|
||||
void setInt(std::string_view semantic, int v);
|
||||
void setBool(std::string_view semantic, bool v);
|
||||
|
||||
// ================= 提交并确认 =================
|
||||
bool commitAndFetch();
|
||||
|
||||
private:
|
||||
std::string server_;
|
||||
int port_;
|
||||
uint64_t version_{0};
|
||||
bool dirty_{false};
|
||||
|
||||
std::unordered_map<std::string, nlohmann::json> cache_;
|
||||
std::unordered_map<std::string, nlohmann::json> pending_;
|
||||
};
|
||||
158
include/config/TboxConfigManager.h
Normal file
158
include/config/TboxConfigManager.h
Normal file
@ -0,0 +1,158 @@
|
||||
#pragma once
|
||||
#include <chrono>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
|
||||
#include "TboxConfigClient.h"
|
||||
#include "TboxConfigSemantic.h"
|
||||
|
||||
// =======================================================
|
||||
// TboxConfigManager
|
||||
// - 表驱动生成 getter / setter
|
||||
// - 支持多项修改 + commit
|
||||
// - 自动 version 检测 + reload
|
||||
// =======================================================
|
||||
class TboxConfigManager
|
||||
{
|
||||
public:
|
||||
static TboxConfigManager& instance()
|
||||
{
|
||||
static TboxConfigManager inst;
|
||||
return inst;
|
||||
}
|
||||
|
||||
// ================= 初始化 =================
|
||||
bool init(const std::string& server = "127.0.0.1", int port = 18080)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(mtx_);
|
||||
client_ = std::make_unique<TboxConfigClient>(server, port);
|
||||
bool ok = client_->fetch();
|
||||
last_check_ = std::chrono::steady_clock::now();
|
||||
return ok;
|
||||
}
|
||||
|
||||
uint64_t version()
|
||||
{
|
||||
ensure();
|
||||
return client_ ? client_->version() : 0;
|
||||
}
|
||||
|
||||
// ===================================================
|
||||
// 🔑 配置字段定义表(唯一维护点)
|
||||
// type, Name, semantic, default
|
||||
// ===================================================
|
||||
#define TBOX_CONFIG_FIELDS(X) \
|
||||
/* -------- 基础身份 -------- */ \
|
||||
X(std::string, DeviceNo, tbox::semantic::DEVICE_NO, "KL001") \
|
||||
X(std::string, VehicleId, tbox::semantic::VEHICLE_ID, "V010001") \
|
||||
X(std::string, Vin, tbox::semantic::VIN, "") \
|
||||
\
|
||||
/* -------- 云平台 -------- */ \
|
||||
X(std::string, PlatformIp, tbox::semantic::PLATFORM_IP, "") \
|
||||
X(int, PlatformPort, tbox::semantic::PLATFORM_PORT, 0) \
|
||||
X(std::string, MqttIp, tbox::semantic::MQTT_IP, "") \
|
||||
X(int, MqttPort, tbox::semantic::MQTT_PORT, 1883) \
|
||||
X(std::string, MqttUsername, tbox::semantic::MQTT_USERNAME, "") \
|
||||
X(std::string, MqttPassword, tbox::semantic::MQTT_PASSWORD, "") \
|
||||
\
|
||||
/* -------- 驾驶舱 -------- */ \
|
||||
X(std::string, CockpitMqttIp, tbox::semantic::COCKPIT_MQTT_IP, "") \
|
||||
X(int, CockpitMqttPort, tbox::semantic::COCKPIT_MQTT_PORT, 1883) \
|
||||
\
|
||||
/* -------- 协议相关 -------- */ \
|
||||
X(int, LoginSeq, tbox::semantic::LOGIN_SEQ, 1) \
|
||||
X(std::string, LoginSeqDate, tbox::semantic::LOGIN_SEQ_DATE, "000000") \
|
||||
X(int, HeartbeatInterval, tbox::semantic::HEARTBEAT_INTERVAL, 60)
|
||||
|
||||
// ================= Getter 自动生成 =================
|
||||
#define GEN_GET(type, name, semantic, def) \
|
||||
type get##name() { return getValue<type>(semantic, def); }
|
||||
|
||||
TBOX_CONFIG_FIELDS(GEN_GET)
|
||||
#undef GEN_GET
|
||||
|
||||
// ================= Setter(写入缓存,不立即提交) =================
|
||||
#define GEN_SET(type, name, semantic, def) \
|
||||
void set##name(const type& v) { setValue<type>(semantic, v); }
|
||||
|
||||
TBOX_CONFIG_FIELDS(GEN_SET)
|
||||
#undef GEN_SET
|
||||
|
||||
// ================= 统一提交 =================
|
||||
bool commit()
|
||||
{
|
||||
ensure();
|
||||
return client_->commitAndFetch();
|
||||
}
|
||||
|
||||
private:
|
||||
TboxConfigManager() = default;
|
||||
~TboxConfigManager() = default;
|
||||
TboxConfigManager(const TboxConfigManager&) = delete;
|
||||
TboxConfigManager& operator=(const TboxConfigManager&) = delete;
|
||||
|
||||
// ================= 自动刷新机制 =================
|
||||
void ensureFresh()
|
||||
{
|
||||
auto now = std::chrono::steady_clock::now();
|
||||
if (now - last_check_ < check_interval_) return;
|
||||
|
||||
last_check_ = now;
|
||||
|
||||
// 简单粗暴,但稳定:直接 fetch
|
||||
client_->fetch();
|
||||
}
|
||||
|
||||
void ensure()
|
||||
{
|
||||
if (!client_)
|
||||
{
|
||||
client_ = std::make_unique<TboxConfigClient>();
|
||||
client_->fetch();
|
||||
}
|
||||
ensureFresh();
|
||||
}
|
||||
|
||||
// ================= 泛型访问 =================
|
||||
template <typename T>
|
||||
T getValue(std::string_view semantic, const T& def)
|
||||
{
|
||||
ensure();
|
||||
if constexpr (std::is_same_v<T, int>)
|
||||
{
|
||||
auto v = client_->getInt(semantic);
|
||||
return v ? *v : def;
|
||||
}
|
||||
else if constexpr (std::is_same_v<T, bool>)
|
||||
{
|
||||
auto v = client_->getBool(semantic);
|
||||
return v ? *v : def;
|
||||
}
|
||||
else
|
||||
{
|
||||
auto v = client_->getString(semantic);
|
||||
return v ? *v : def;
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void setValue(std::string_view semantic, const T& v)
|
||||
{
|
||||
ensure();
|
||||
if constexpr (std::is_same_v<T, int>)
|
||||
client_->setInt(semantic, v);
|
||||
else if constexpr (std::is_same_v<T, bool>)
|
||||
client_->setBool(semantic, v);
|
||||
else
|
||||
client_->setString(semantic, v);
|
||||
}
|
||||
|
||||
private:
|
||||
std::mutex mtx_;
|
||||
std::unique_ptr<TboxConfigClient> client_;
|
||||
|
||||
std::chrono::steady_clock::time_point last_check_{};
|
||||
const std::chrono::seconds check_interval_{30};
|
||||
};
|
||||
28
include/config/TboxConfigSemantic.h
Normal file
28
include/config/TboxConfigSemantic.h
Normal file
@ -0,0 +1,28 @@
|
||||
#pragma once
|
||||
#include <string_view>
|
||||
|
||||
namespace tbox::semantic
|
||||
{
|
||||
// ================= 基础身份 =================
|
||||
inline constexpr std::string_view DEVICE_NO = "tbox.device_no";
|
||||
inline constexpr std::string_view VEHICLE_ID = "tbox.vehicle_id";
|
||||
inline constexpr std::string_view VIN = "tbox.vin";
|
||||
|
||||
// ================= 云平台 =================
|
||||
inline constexpr std::string_view PLATFORM_IP = "tbox.platform_ip";
|
||||
inline constexpr std::string_view PLATFORM_PORT = "tbox.platform_port";
|
||||
|
||||
inline constexpr std::string_view MQTT_IP = "tbox.mqtt_ip";
|
||||
inline constexpr std::string_view MQTT_PORT = "tbox.mqtt_port";
|
||||
inline constexpr std::string_view MQTT_USERNAME = "tbox.mqtt_username";
|
||||
inline constexpr std::string_view MQTT_PASSWORD = "tbox.mqtt_password";
|
||||
|
||||
// ================= 驾驶舱 =================
|
||||
inline constexpr std::string_view COCKPIT_MQTT_IP = "tbox.cockpit_mqtt_ip";
|
||||
inline constexpr std::string_view COCKPIT_MQTT_PORT = "tbox.cockpit_mqtt_port";
|
||||
|
||||
// ================= 协议相关 =================
|
||||
inline constexpr std::string_view LOGIN_SEQ = "tbox.login_seq";
|
||||
inline constexpr std::string_view LOGIN_SEQ_DATE = "tbox.login_seq_date";
|
||||
inline constexpr std::string_view HEARTBEAT_INTERVAL = "tbox.heartbeat_interval";
|
||||
} // namespace tbox::semantic
|
||||
@ -1,240 +0,0 @@
|
||||
#pragma once
|
||||
#include <algorithm>
|
||||
#include <cctype>
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "INIReader.h"
|
||||
|
||||
// ------------------------
|
||||
// 线程安全配置管理单例类
|
||||
// ------------------------
|
||||
class ConfigManager
|
||||
{
|
||||
public:
|
||||
// 获取单例
|
||||
static ConfigManager& instance()
|
||||
{
|
||||
static ConfigManager inst;
|
||||
return inst;
|
||||
}
|
||||
|
||||
// 加载 INI 文件
|
||||
bool load(const std::string& path)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(mtx);
|
||||
file_path = path;
|
||||
|
||||
auto r = std::make_unique<INIReader>(path);
|
||||
if (r->ParseError() < 0)
|
||||
{
|
||||
std::cerr << "Can't load config file: " << path << ", will create default on save\n";
|
||||
reader.reset();
|
||||
buildMapFromReader(); // 用 schema 默认值初始化
|
||||
return false;
|
||||
}
|
||||
|
||||
reader = std::move(r);
|
||||
buildMapFromReader();
|
||||
return true;
|
||||
}
|
||||
|
||||
// ==================== Vehicle ====================
|
||||
std::string getVin() { return getValue("vehicle", "vin", "LSV1234567890KUNL"); }
|
||||
std::string getVehicleId() { return getValue("vehicle", "vehicle_id", "V010001"); }
|
||||
void setVin(const std::string& vin) { setValue("vehicle", "vin", vin); }
|
||||
void setVehicleId(const std::string& vid) { setValue("vehicle", "vehicle_id", vid); }
|
||||
|
||||
// ==================== Cloud ====================
|
||||
std::string getDeviceNo() { return getValue("cloud", "device_no", "KL001"); }
|
||||
std::string getPlatformIp() { return getValue("cloud", "platform_ip", "192.168.1.100"); }
|
||||
int getPlatformPort() { return getInt("cloud", "platform_port", 8888); }
|
||||
std::string getMqttIp() { return getValue("cloud", "mqtt_ip", "192.168.1.101"); }
|
||||
int getMqttPort() { return getInt("cloud", "mqtt_port", 1883); }
|
||||
// ✅ 新增
|
||||
std::string getMqttUsername() { return getValue("cloud", "mqtt_username", ""); }
|
||||
std::string getMqttPassword() { return getValue("cloud", "mqtt_password", ""); }
|
||||
int getHeartbeatInterval() { return getInt("cloud", "heartbeat_interval", 60); }
|
||||
|
||||
void setDeviceNo(const std::string& v) { setValue("cloud", "device_no", v); }
|
||||
void setPlatformIp(const std::string& v) { setValue("cloud", "platform_ip", v); }
|
||||
void setPlatformPort(int v) { setValue("cloud", "platform_port", std::to_string(v)); }
|
||||
void setMqttIp(const std::string& v) { setValue("cloud", "mqtt_ip", v); }
|
||||
void setMqttPort(int v) { setValue("cloud", "mqtt_port", std::to_string(v)); }
|
||||
// ✅ 新增
|
||||
void setMqttUsername(const std::string& v) { setValue("cloud", "mqtt_username", v); }
|
||||
void setMqttPassword(const std::string& v) { setValue("cloud", "mqtt_password", v); }
|
||||
void setHeartbeatInterval(int interval) { setValue("cloud", "heartbeat_interval", std::to_string(interval)); }
|
||||
|
||||
// ==================== Cockpit ====================
|
||||
std::string getCockpitId() { return getValue("cockpit", "cockpit_id", "C010001"); }
|
||||
std::string getCockpitMqttIp() { return getValue("cockpit", "mqtt_ip", "192.168.1.110"); }
|
||||
int getCockpitMqttPort() { return getInt("cockpit", "mqtt_port", 1883); }
|
||||
|
||||
void setCockpitId(const std::string& v) { setValue("cockpit", "cockpit_id", v); }
|
||||
void setCockpitMqttIp(const std::string& v) { setValue("cockpit", "mqtt_ip", v); }
|
||||
void setCockpitMqttPort(int v) { setValue("cockpit", "mqtt_port", std::to_string(v)); }
|
||||
|
||||
// ==================== Serial ====================
|
||||
std::string getSerialDev() { return getValue("serial", "dev_name", "/dev/ttyUSB3"); }
|
||||
int getSerialBaudrate() { return getInt("serial", "baudrate", 115200); }
|
||||
|
||||
void setSerialDev(const std::string& v) { setValue("serial", "dev_name", v); }
|
||||
void setSerialBaudrate(int v) { setValue("serial", "baudrate", std::to_string(v)); }
|
||||
|
||||
// ==================== TBox ====================
|
||||
int getLoginSeq() { return getInt("tbox", "login_seq", 1); }
|
||||
std::string getLoginSeqDate() { return getValue("tbox", "login_seq_date", "000000"); }
|
||||
|
||||
void setLoginSeq(int v) { setValue("tbox", "login_seq", std::to_string(v)); }
|
||||
void setLoginSeqDate(const std::string& v) { setValue("tbox", "login_seq_date", v); }
|
||||
|
||||
// 保存当前内存 map 到文件
|
||||
bool save()
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(mtx);
|
||||
std::ofstream ofs(file_path);
|
||||
if (!ofs.is_open()) return false;
|
||||
|
||||
for (const auto& [section, items] : schema)
|
||||
{
|
||||
ofs << "[" << section << "]\n";
|
||||
for (const auto& item : items)
|
||||
{
|
||||
ofs << item.key << "=" << getValueUnlocked(section, item.key, item.default_value) << "\n";
|
||||
}
|
||||
ofs << "\n";
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
ConfigManager() = default;
|
||||
~ConfigManager() = default;
|
||||
ConfigManager(const ConfigManager&) = delete;
|
||||
ConfigManager& operator=(const ConfigManager&) = delete;
|
||||
|
||||
struct ConfigItem
|
||||
{
|
||||
std::string key;
|
||||
std::string default_value;
|
||||
};
|
||||
|
||||
const std::vector<std::pair<std::string, std::vector<ConfigItem>>> schema = {
|
||||
{"vehicle",
|
||||
{
|
||||
{"vin", "LSV1234567890KUNL"},
|
||||
{"vehicle_id", "V010001"},
|
||||
}},
|
||||
{"cloud",
|
||||
{
|
||||
{"device_no", "KL001"},
|
||||
{"platform_ip", "192.168.1.100"},
|
||||
{"platform_port", "8888"},
|
||||
{"mqtt_ip", "192.168.1.101"},
|
||||
{"mqtt_port", "1883"},
|
||||
{"mqtt_username", ""}, // ✅ 新增
|
||||
{"mqtt_password", ""}, // ✅ 新增
|
||||
{"heartbeat_interval", "60"},
|
||||
}},
|
||||
{"cockpit",
|
||||
{
|
||||
{"cockpit_id", "C010001"},
|
||||
{"mqtt_ip", "192.168.1.110"},
|
||||
{"mqtt_port", "1883"},
|
||||
}},
|
||||
{"serial",
|
||||
{
|
||||
{"dev_name", "/dev/ttyUSB3"},
|
||||
{"baudrate", "115200"},
|
||||
}},
|
||||
{"tbox",
|
||||
{
|
||||
{"login_seq", "1"},
|
||||
{"login_seq_date", "000000"},
|
||||
}},
|
||||
};
|
||||
|
||||
std::unique_ptr<INIReader> reader;
|
||||
std::string file_path;
|
||||
std::map<std::string, std::map<std::string, std::string>> sections;
|
||||
std::mutex mtx; // 多线程保护
|
||||
|
||||
// 根据 INIReader 初始化 map
|
||||
void buildMapFromReader()
|
||||
{
|
||||
sections.clear();
|
||||
|
||||
for (const auto& [section, items] : schema)
|
||||
{
|
||||
for (const auto& item : items)
|
||||
{
|
||||
if (reader) { sections[section][item.key] = reader->Get(section, item.key, item.default_value); }
|
||||
else
|
||||
{
|
||||
sections[section][item.key] = item.default_value;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int getInt(const std::string& section, const std::string& key, int def)
|
||||
{
|
||||
std::string v = getValue(section, key, std::to_string(def));
|
||||
try
|
||||
{
|
||||
size_t idx = 0;
|
||||
int result = std::stoi(v, &idx);
|
||||
if (idx != v.size()) throw std::invalid_argument("trailing chars");
|
||||
return result;
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
// 自动纠错 + 修复配置
|
||||
setValue(section, key, std::to_string(def));
|
||||
return def;
|
||||
}
|
||||
}
|
||||
|
||||
bool getBool(const std::string& section, const std::string& key, bool def)
|
||||
{
|
||||
std::string v = getValue(section, key, def ? "1" : "0");
|
||||
std::string lv = v;
|
||||
std::transform(lv.begin(), lv.end(), lv.begin(), [](unsigned char c) { return std::tolower(c); });
|
||||
if (lv == "1" || lv == "true" || lv == "yes") return true;
|
||||
if (lv == "0" || lv == "false" || lv == "no") return false;
|
||||
|
||||
setValue(section, key, def ? "1" : "0");
|
||||
return def;
|
||||
}
|
||||
|
||||
std::string getValueUnlocked(const std::string& section, const std::string& key, const std::string& def)
|
||||
{
|
||||
auto& sec = sections[section];
|
||||
auto it = sec.find(key);
|
||||
if (it != sec.end()) return it->second;
|
||||
|
||||
sec[key] = def;
|
||||
return def;
|
||||
}
|
||||
|
||||
// 从 map 获取值
|
||||
std::string getValue(const std::string& section, const std::string& key, const std::string& def)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(mtx);
|
||||
return getValueUnlocked(section, key, def);
|
||||
}
|
||||
|
||||
// 写入 map
|
||||
void setValue(const std::string& section, const std::string& key, const std::string& value)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(mtx);
|
||||
sections[section][key] = value;
|
||||
}
|
||||
};
|
||||
12337
include/config/httplib.h
Normal file
12337
include/config/httplib.h
Normal file
File diff suppressed because it is too large
Load Diff
@ -1,189 +0,0 @@
|
||||
/* inih -- simple .INI file parser
|
||||
|
||||
SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
Copyright (C) 2009-2025, Ben Hoyt
|
||||
|
||||
inih is released under the New BSD license (see LICENSE.txt). Go to the project
|
||||
home page for more info:
|
||||
|
||||
https://github.com/benhoyt/inih
|
||||
|
||||
*/
|
||||
|
||||
#ifndef INI_H
|
||||
#define INI_H
|
||||
|
||||
/* Make this header file easier to include in C++ code */
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
/* Nonzero if ini_handler callback should accept lineno parameter. */
|
||||
#ifndef INI_HANDLER_LINENO
|
||||
#define INI_HANDLER_LINENO 0
|
||||
#endif
|
||||
|
||||
/* Visibility symbols, required for Windows DLLs */
|
||||
#ifndef INI_API
|
||||
#if defined _WIN32 || defined __CYGWIN__
|
||||
# ifdef INI_SHARED_LIB
|
||||
# ifdef INI_SHARED_LIB_BUILDING
|
||||
# define INI_API __declspec(dllexport)
|
||||
# else
|
||||
# define INI_API __declspec(dllimport)
|
||||
# endif
|
||||
# else
|
||||
# define INI_API
|
||||
# endif
|
||||
#else
|
||||
# if defined(__GNUC__) && __GNUC__ >= 4
|
||||
# define INI_API __attribute__ ((visibility ("default")))
|
||||
# else
|
||||
# define INI_API
|
||||
# endif
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/* Typedef for prototype of handler function.
|
||||
|
||||
Note that even though the value parameter has type "const char*", the user
|
||||
may cast to "char*" and modify its content, as the value is not used again
|
||||
after the call to ini_handler. This is not true of section and name --
|
||||
those must not be modified.
|
||||
*/
|
||||
#if INI_HANDLER_LINENO
|
||||
typedef int (*ini_handler)(void* user, const char* section,
|
||||
const char* name, const char* value,
|
||||
int lineno);
|
||||
#else
|
||||
typedef int (*ini_handler)(void* user, const char* section,
|
||||
const char* name, const char* value);
|
||||
#endif
|
||||
|
||||
/* Typedef for prototype of fgets-style reader function. */
|
||||
typedef char* (*ini_reader)(char* str, int num, void* stream);
|
||||
|
||||
/* Parse given INI-style file. May have [section]s, name=value pairs
|
||||
(whitespace stripped), and comments starting with ';' (semicolon). Section
|
||||
is "" if name=value pair parsed before any section heading. name:value
|
||||
pairs are also supported as a concession to Python's configparser.
|
||||
|
||||
For each name=value pair parsed, call handler function with given user
|
||||
pointer as well as section, name, and value (data only valid for duration
|
||||
of handler call). Handler should return nonzero on success, zero on error.
|
||||
|
||||
Returns 0 on success, line number of first error on parse error (doesn't
|
||||
stop on first error), -1 on file open error, or -2 on memory allocation
|
||||
error (only when INI_USE_STACK is zero).
|
||||
*/
|
||||
INI_API int ini_parse(const char* filename, ini_handler handler, void* user);
|
||||
|
||||
/* Same as ini_parse(), but takes a FILE* instead of filename. This doesn't
|
||||
close the file when it's finished -- the caller must do that. */
|
||||
INI_API int ini_parse_file(FILE* file, ini_handler handler, void* user);
|
||||
|
||||
/* Same as ini_parse(), but takes an ini_reader function pointer instead of
|
||||
filename. Used for implementing custom or string-based I/O (see also
|
||||
ini_parse_string). */
|
||||
INI_API int ini_parse_stream(ini_reader reader, void* stream, ini_handler handler,
|
||||
void* user);
|
||||
|
||||
/* Same as ini_parse(), but takes a zero-terminated string with the INI data
|
||||
instead of a file. Useful for parsing INI data from a network socket or
|
||||
which is already in memory. */
|
||||
INI_API int ini_parse_string(const char* string, ini_handler handler, void* user);
|
||||
|
||||
/* Same as ini_parse_string(), but takes a string and its length, avoiding
|
||||
strlen(). Useful for parsing INI data from a network socket or which is
|
||||
already in memory, or interfacing with C++ std::string_view. */
|
||||
INI_API int ini_parse_string_length(const char* string, size_t length, ini_handler handler, void* user);
|
||||
|
||||
/* Nonzero to allow multi-line value parsing, in the style of Python's
|
||||
configparser. If allowed, ini_parse() will call the handler with the same
|
||||
name for each subsequent line parsed. */
|
||||
#ifndef INI_ALLOW_MULTILINE
|
||||
#define INI_ALLOW_MULTILINE 1
|
||||
#endif
|
||||
|
||||
/* Nonzero to allow a UTF-8 BOM sequence (0xEF 0xBB 0xBF) at the start of
|
||||
the file. See https://github.com/benhoyt/inih/issues/21 */
|
||||
#ifndef INI_ALLOW_BOM
|
||||
#define INI_ALLOW_BOM 1
|
||||
#endif
|
||||
|
||||
/* Chars that begin a start-of-line comment. Per Python configparser, allow
|
||||
both ; and # comments at the start of a line by default. */
|
||||
#ifndef INI_START_COMMENT_PREFIXES
|
||||
#define INI_START_COMMENT_PREFIXES ";#"
|
||||
#endif
|
||||
|
||||
/* Nonzero to allow inline comments (with valid inline comment characters
|
||||
specified by INI_INLINE_COMMENT_PREFIXES). Set to 0 to turn off and match
|
||||
Python 3.2+ configparser behaviour. */
|
||||
#ifndef INI_ALLOW_INLINE_COMMENTS
|
||||
#define INI_ALLOW_INLINE_COMMENTS 1
|
||||
#endif
|
||||
#ifndef INI_INLINE_COMMENT_PREFIXES
|
||||
#define INI_INLINE_COMMENT_PREFIXES ";"
|
||||
#endif
|
||||
|
||||
/* Nonzero to use stack for line buffer, zero to use heap (malloc/free). */
|
||||
#ifndef INI_USE_STACK
|
||||
#define INI_USE_STACK 1
|
||||
#endif
|
||||
|
||||
/* Maximum line length for any line in INI file (stack or heap). Note that
|
||||
this must be 3 more than the longest line (due to '\r', '\n', and '\0'). */
|
||||
#ifndef INI_MAX_LINE
|
||||
#define INI_MAX_LINE 200
|
||||
#endif
|
||||
|
||||
/* Nonzero to allow heap line buffer to grow via realloc(), zero for a
|
||||
fixed-size buffer of INI_MAX_LINE bytes. Only applies if INI_USE_STACK is
|
||||
zero. */
|
||||
#ifndef INI_ALLOW_REALLOC
|
||||
#define INI_ALLOW_REALLOC 0
|
||||
#endif
|
||||
|
||||
/* Initial size in bytes for heap line buffer. Only applies if INI_USE_STACK
|
||||
is zero. */
|
||||
#ifndef INI_INITIAL_ALLOC
|
||||
#define INI_INITIAL_ALLOC 200
|
||||
#endif
|
||||
|
||||
/* Stop parsing on first error (default is to keep parsing). */
|
||||
#ifndef INI_STOP_ON_FIRST_ERROR
|
||||
#define INI_STOP_ON_FIRST_ERROR 0
|
||||
#endif
|
||||
|
||||
/* Nonzero to call the handler at the start of each new section (with
|
||||
name and value NULL). Default is to only call the handler on
|
||||
each name=value pair. */
|
||||
#ifndef INI_CALL_HANDLER_ON_NEW_SECTION
|
||||
#define INI_CALL_HANDLER_ON_NEW_SECTION 0
|
||||
#endif
|
||||
|
||||
/* Nonzero to allow a name without a value (no '=' or ':' on the line) and
|
||||
call the handler with value NULL in this case. Default is to treat
|
||||
no-value lines as an error. */
|
||||
#ifndef INI_ALLOW_NO_VALUE
|
||||
#define INI_ALLOW_NO_VALUE 0
|
||||
#endif
|
||||
|
||||
/* Nonzero to use custom ini_malloc, ini_free, and ini_realloc memory
|
||||
allocation functions (INI_USE_STACK must also be 0). These functions must
|
||||
have the same signatures as malloc/free/realloc and behave in a similar
|
||||
way. ini_realloc is only needed if INI_ALLOW_REALLOC is set. */
|
||||
#ifndef INI_CUSTOM_ALLOCATOR
|
||||
#define INI_CUSTOM_ALLOCATOR 0
|
||||
#endif
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* INI_H */
|
||||
@ -4,8 +4,8 @@
|
||||
#include <string>
|
||||
#include <thread>
|
||||
|
||||
#include "TboxConfigManager.h"
|
||||
#include "can_bus_instance.h"
|
||||
#include "config.h"
|
||||
#include "logger.h"
|
||||
#include "mqtt_client_instance.h"
|
||||
#include "serial_instance.h"
|
||||
|
||||
@ -7,9 +7,10 @@
|
||||
|
||||
std::unique_ptr<MqttClient> mqtt_client_rc_ctrl;
|
||||
|
||||
static const std::string veh_rc_topic = "/kun/vehicle/ctrl/" + ConfigManager::instance().getVehicleId(); // 远控主题
|
||||
static const std::string veh_rc_topic =
|
||||
"/kun/vehicle/ctrl/" + TboxConfigManager::instance().getVehicleId(); // 远控主题
|
||||
static const std::string veh_report_topic =
|
||||
"/kun/vehicle/info/" + ConfigManager::instance().getVehicleId(); // 状态上报主题
|
||||
"/kun/vehicle/info/" + TboxConfigManager::instance().getVehicleId(); // 状态上报主题
|
||||
|
||||
std::atomic<bool> watchdogEnabled{false};
|
||||
std::atomic<bool> emergencyActive{false};
|
||||
|
||||
@ -35,7 +35,7 @@ static void serial_at_send_loop()
|
||||
|
||||
for (auto it = at_tasks.begin(); it != at_tasks.end();)
|
||||
{
|
||||
auto &task = *it;
|
||||
auto& task = *it;
|
||||
bool should_send = false;
|
||||
|
||||
if (task.interval > 0)
|
||||
@ -66,8 +66,9 @@ static void serial_at_send_loop()
|
||||
if (task.cmd == "AT+CCID")
|
||||
{
|
||||
init_tcp_client_tbox_router(
|
||||
ConfigManager::instance().getPlatformIp(),
|
||||
ConfigManager::instance().getPlatformPort()); // CCID 获取失败,继续初始化 TBoxRouter
|
||||
TboxConfigManager::instance().getPlatformIp(),
|
||||
TboxConfigManager::instance()
|
||||
.getPlatformPort()); // CCID 获取失败,继续初始化 TBoxRouter
|
||||
}
|
||||
|
||||
it = at_tasks.erase(it); // 移除任务
|
||||
@ -90,7 +91,7 @@ static void serial_at_send_loop()
|
||||
}
|
||||
}
|
||||
|
||||
static void handle_serial_at_data(const std::string &data)
|
||||
static void handle_serial_at_data(const std::string& data)
|
||||
{
|
||||
// LOG_INFO(veh_rc_logger, "[serial_at] Received: " + data);
|
||||
|
||||
@ -113,20 +114,20 @@ static void handle_serial_at_data(const std::string &data)
|
||||
LOG_INFO(veh_rc_logger, "[serial_at] Extracted CCID = " + ccid);
|
||||
|
||||
init_tcp_client_tbox_router(
|
||||
ConfigManager::instance().getPlatformIp(),
|
||||
ConfigManager::instance().getPlatformPort()); // 成功获取 CCID 后,初始化 TBoxRouter
|
||||
TboxConfigManager::instance().getPlatformIp(),
|
||||
TboxConfigManager::instance().getPlatformPort()); // 成功获取 CCID 后,初始化 TBoxRouter
|
||||
|
||||
{
|
||||
// 收到 CCID 响应,移除对应任务
|
||||
std::lock_guard<std::mutex> lock(at_tasks_mutex);
|
||||
at_tasks.erase(std::remove_if(at_tasks.begin(), at_tasks.end(),
|
||||
[](const AtTask &task) { return task.cmd == "AT+CCID"; }),
|
||||
[](const AtTask& task) { return task.cmd == "AT+CCID"; }),
|
||||
at_tasks.end());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void init_serial_at(const std::string &device, int baudrate)
|
||||
void init_serial_at(const std::string& device, int baudrate)
|
||||
{
|
||||
serial_at = std::make_unique<SerialPort>("serial_at", device, baudrate, veh_rc_logger, 5);
|
||||
|
||||
|
||||
@ -31,7 +31,7 @@ static const CmdMqttMap kCmdMqttTable[] = {
|
||||
|
||||
std::string build_mqtt_topic_by_cmd(uint8_t cmd_id)
|
||||
{
|
||||
const auto deviceNo = ConfigManager::instance().getDeviceNo();
|
||||
const auto deviceNo = TboxConfigManager::instance().getDeviceNo();
|
||||
|
||||
for (const auto& it : kCmdMqttTable)
|
||||
{
|
||||
|
||||
@ -6,7 +6,7 @@ static std::string build_packet(uint8_t command_id, F&& fill_data_unit)
|
||||
FullPacket pkt;
|
||||
pkt.command_id = command_id;
|
||||
pkt.response_flag = 0xFE;
|
||||
pkt.vin = ConfigManager::instance().getVin();
|
||||
pkt.vin = TboxConfigManager::instance().getVin();
|
||||
pkt.encryption_method = 0x01;
|
||||
|
||||
pkt.data_unit.clear();
|
||||
@ -46,8 +46,8 @@ static std::string build_login_request()
|
||||
<< (int)time_bytes[2];
|
||||
std::string date_str = oss.str();
|
||||
|
||||
auto last_login_date = ConfigManager::instance().getLoginSeqDate();
|
||||
int last_login_seq = ConfigManager::instance().getLoginSeq();
|
||||
auto last_login_date = TboxConfigManager::instance().getLoginSeqDate();
|
||||
int last_login_seq = TboxConfigManager::instance().getLoginSeq();
|
||||
|
||||
uint16_t login_seq = 1;
|
||||
if (last_login_date == date_str)
|
||||
@ -55,9 +55,9 @@ static std::string build_login_request()
|
||||
login_seq = (last_login_seq >= 65531) ? 1 : last_login_seq + 1;
|
||||
}
|
||||
|
||||
ConfigManager::instance().setLoginSeqDate(date_str);
|
||||
ConfigManager::instance().setLoginSeq(login_seq);
|
||||
ConfigManager::instance().save();
|
||||
TboxConfigManager::instance().setLoginSeqDate(date_str);
|
||||
TboxConfigManager::instance().setLoginSeq(login_seq);
|
||||
TboxConfigManager::instance().commit();
|
||||
|
||||
du.push_back((login_seq >> 8) & 0xFF);
|
||||
du.push_back(login_seq & 0xFF);
|
||||
|
||||
@ -37,7 +37,7 @@ static void handle_login_request(const ReceivedPacket& packet)
|
||||
|
||||
while (true)
|
||||
{
|
||||
int interval = ConfigManager::instance().getHeartbeatInterval();
|
||||
int interval = TboxConfigManager::instance().getHeartbeatInterval();
|
||||
auto client = wp.lock();
|
||||
if (!client)
|
||||
{
|
||||
@ -240,8 +240,8 @@ static void handle_tbox_status(bool connected)
|
||||
// 断线后,重新请求网关地址
|
||||
if (!tcp_client_router || !tcp_client_router->is_connected())
|
||||
{
|
||||
init_tcp_client_tbox_router(ConfigManager::instance().getPlatformIp(),
|
||||
ConfigManager::instance().getPlatformPort());
|
||||
init_tcp_client_tbox_router(TboxConfigManager::instance().getPlatformIp(),
|
||||
TboxConfigManager::instance().getPlatformPort());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,206 +0,0 @@
|
||||
// Read an INI file into easy-to-access name/value pairs.
|
||||
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
// Copyright (C) 2009-2025, Ben Hoyt
|
||||
|
||||
// inih and INIReader are released under the New BSD license (see LICENSE.txt).
|
||||
// Go to the project home page for more info:
|
||||
//
|
||||
// https://github.com/benhoyt/inih
|
||||
|
||||
#include <algorithm>
|
||||
#include <cctype>
|
||||
#include <cstdlib>
|
||||
#include "ini.h"
|
||||
#include "INIReader.h"
|
||||
|
||||
using std::string;
|
||||
|
||||
INIReader::INIReader(const string &filename)
|
||||
{
|
||||
_error = ini_parse(filename.c_str(), ValueHandler, this);
|
||||
}
|
||||
|
||||
INIReader::INIReader(const char *buffer, size_t buffer_size)
|
||||
{
|
||||
_error = ini_parse_string_length(buffer, buffer_size, ValueHandler, this);
|
||||
}
|
||||
|
||||
int INIReader::ParseError() const
|
||||
{
|
||||
return _error;
|
||||
}
|
||||
|
||||
string INIReader::ParseErrorMessage() const
|
||||
{
|
||||
// If _error is positive it means it is the line number on which a parse
|
||||
// error occurred. This could be an overlong line, that ValueHandler
|
||||
// indicated a user defined error, an unterminated section name, or a name
|
||||
// without a value.
|
||||
if (_error > 0)
|
||||
{
|
||||
return "parse error on line " + std::to_string(_error) + "; missing ']' or '='?";
|
||||
}
|
||||
|
||||
// If _error is negative it is a system type error, and 0 means success.
|
||||
switch (_error)
|
||||
{
|
||||
case -2:
|
||||
return "unable to allocate memory";
|
||||
|
||||
case -1:
|
||||
return "unable to open file";
|
||||
|
||||
case 0:
|
||||
return "";
|
||||
}
|
||||
|
||||
// This should never be reached. It probably means a new error code was
|
||||
// added to the C API without updating this method.
|
||||
return "unknown error " + std::to_string(_error);
|
||||
}
|
||||
|
||||
string INIReader::Get(const string §ion, const string &name, const string &default_value) const
|
||||
{
|
||||
string key = MakeKey(section, name);
|
||||
// Use _values.find() here instead of _values.at() to support pre C++11 compilers
|
||||
return _values.count(key) ? _values.find(key)->second : default_value;
|
||||
}
|
||||
|
||||
string INIReader::GetString(const string §ion, const string &name, const string &default_value) const
|
||||
{
|
||||
const string str = Get(section, name, "");
|
||||
return str.empty() ? default_value : str;
|
||||
}
|
||||
|
||||
long INIReader::GetInteger(const string §ion, const string &name, long default_value) const
|
||||
{
|
||||
string valstr = Get(section, name, "");
|
||||
const char *value = valstr.c_str();
|
||||
char *end;
|
||||
// This parses "1234" (decimal) and also "0x4D2" (hex)
|
||||
long n = strtol(value, &end, 0);
|
||||
return end > value ? n : default_value;
|
||||
}
|
||||
|
||||
INI_API int64_t INIReader::GetInteger64(const string §ion, const string &name, int64_t default_value) const
|
||||
{
|
||||
string valstr = Get(section, name, "");
|
||||
const char *value = valstr.c_str();
|
||||
char *end;
|
||||
// This parses "1234" (decimal) and also "0x4D2" (hex)
|
||||
int64_t n = strtoll(value, &end, 0);
|
||||
return end > value ? n : default_value;
|
||||
}
|
||||
|
||||
unsigned long INIReader::GetUnsigned(const string §ion, const string &name, unsigned long default_value) const
|
||||
{
|
||||
string valstr = Get(section, name, "");
|
||||
const char *value = valstr.c_str();
|
||||
char *end;
|
||||
// This parses "1234" (decimal) and also "0x4D2" (hex)
|
||||
unsigned long n = strtoul(value, &end, 0);
|
||||
return end > value ? n : default_value;
|
||||
}
|
||||
|
||||
INI_API uint64_t INIReader::GetUnsigned64(const string §ion, const string &name, uint64_t default_value) const
|
||||
{
|
||||
string valstr = Get(section, name, "");
|
||||
const char *value = valstr.c_str();
|
||||
char *end;
|
||||
// This parses "1234" (decimal) and also "0x4D2" (hex)
|
||||
uint64_t n = strtoull(value, &end, 0);
|
||||
return end > value ? n : default_value;
|
||||
}
|
||||
|
||||
double INIReader::GetReal(const string §ion, const string &name, double default_value) const
|
||||
{
|
||||
string valstr = Get(section, name, "");
|
||||
const char *value = valstr.c_str();
|
||||
char *end;
|
||||
double n = strtod(value, &end);
|
||||
return end > value ? n : default_value;
|
||||
}
|
||||
|
||||
bool INIReader::GetBoolean(const string §ion, const string &name, bool default_value) const
|
||||
{
|
||||
string valstr = Get(section, name, "");
|
||||
// Convert to lower case to make string comparisons case-insensitive
|
||||
std::transform(valstr.begin(), valstr.end(), valstr.begin(),
|
||||
[](const unsigned char &ch)
|
||||
{ return static_cast<unsigned char>(::tolower(ch)); });
|
||||
if (valstr == "true" || valstr == "yes" || valstr == "on" || valstr == "1")
|
||||
return true;
|
||||
else if (valstr == "false" || valstr == "no" || valstr == "off" || valstr == "0")
|
||||
return false;
|
||||
else
|
||||
return default_value;
|
||||
}
|
||||
|
||||
std::vector<string> INIReader::Sections() const
|
||||
{
|
||||
std::set<string> sectionSet;
|
||||
for (std::map<string, string>::const_iterator it = _values.begin(); it != _values.end(); ++it)
|
||||
{
|
||||
size_t pos = it->first.find('=');
|
||||
if (pos != string::npos)
|
||||
{
|
||||
sectionSet.insert(it->first.substr(0, pos));
|
||||
}
|
||||
}
|
||||
return std::vector<string>(sectionSet.begin(), sectionSet.end());
|
||||
}
|
||||
|
||||
std::vector<string> INIReader::Keys(const string §ion) const
|
||||
{
|
||||
std::vector<string> keys;
|
||||
string keyPrefix = MakeKey(section, "");
|
||||
for (std::map<string, string>::const_iterator it = _values.begin(); it != _values.end(); ++it)
|
||||
{
|
||||
if (it->first.compare(0, keyPrefix.length(), keyPrefix) == 0)
|
||||
{
|
||||
keys.push_back(it->first.substr(keyPrefix.length()));
|
||||
}
|
||||
}
|
||||
return keys;
|
||||
}
|
||||
|
||||
bool INIReader::HasSection(const string §ion) const
|
||||
{
|
||||
const string key = MakeKey(section, "");
|
||||
std::map<string, string>::const_iterator pos = _values.lower_bound(key);
|
||||
if (pos == _values.end())
|
||||
return false;
|
||||
// Does the key at the lower_bound pos start with "section"?
|
||||
return pos->first.compare(0, key.length(), key) == 0;
|
||||
}
|
||||
|
||||
bool INIReader::HasValue(const string §ion, const string &name) const
|
||||
{
|
||||
string key = MakeKey(section, name);
|
||||
return _values.count(key);
|
||||
}
|
||||
|
||||
string INIReader::MakeKey(const string §ion, const string &name)
|
||||
{
|
||||
string key = section + "=" + name;
|
||||
// Convert to lower case to make section/name lookups case-insensitive
|
||||
std::transform(key.begin(), key.end(), key.begin(),
|
||||
[](const unsigned char &ch)
|
||||
{ return static_cast<unsigned char>(::tolower(ch)); });
|
||||
return key;
|
||||
}
|
||||
|
||||
int INIReader::ValueHandler(void *user, const char *section, const char *name,
|
||||
const char *value)
|
||||
{
|
||||
if (!name) // Happens when INI_CALL_HANDLER_ON_NEW_SECTION enabled
|
||||
return 1;
|
||||
INIReader *reader = static_cast<INIReader *>(user);
|
||||
string key = MakeKey(section, name);
|
||||
if (reader->_values[key].size() > 0)
|
||||
reader->_values[key] += "\n";
|
||||
reader->_values[key] += value ? value : "";
|
||||
return 1;
|
||||
}
|
||||
89
src/config/TboxConfigClient.cpp
Normal file
89
src/config/TboxConfigClient.cpp
Normal file
@ -0,0 +1,89 @@
|
||||
#include "TboxConfigClient.h"
|
||||
|
||||
#include "httplib.h"
|
||||
|
||||
// ================= 构造 =================
|
||||
TboxConfigClient::TboxConfigClient(std::string server, int port) : server_(std::move(server)), port_(port) {}
|
||||
|
||||
// ================= 拉取配置 =================
|
||||
bool TboxConfigClient::fetch()
|
||||
{
|
||||
httplib::Client cli(server_, port_);
|
||||
auto res = cli.Get("/api/config");
|
||||
if (!res || res->status != 200) return false;
|
||||
|
||||
auto json = nlohmann::json::parse(res->body);
|
||||
|
||||
if (!json.contains("tbox") || !json.contains("version")) return false;
|
||||
|
||||
cache_.clear();
|
||||
for (auto& [k, v] : json["tbox"].items()) { cache_[k] = v; }
|
||||
|
||||
version_ = json["version"].get<uint64_t>();
|
||||
dirty_ = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
uint64_t TboxConfigClient::version() const { return version_; }
|
||||
|
||||
bool TboxConfigClient::isDirty() const { return dirty_; }
|
||||
|
||||
// ================= 读取 =================
|
||||
std::optional<std::string> TboxConfigClient::getString(std::string_view semantic) const
|
||||
{
|
||||
auto it = cache_.find(std::string(semantic));
|
||||
if (it == cache_.end() || !it->second.is_string()) return std::nullopt;
|
||||
return it->second.get<std::string>();
|
||||
}
|
||||
|
||||
std::optional<int> TboxConfigClient::getInt(std::string_view semantic) const
|
||||
{
|
||||
auto it = cache_.find(std::string(semantic));
|
||||
if (it == cache_.end() || !it->second.is_number_integer()) return std::nullopt;
|
||||
return it->second.get<int>();
|
||||
}
|
||||
|
||||
std::optional<bool> TboxConfigClient::getBool(std::string_view semantic) const
|
||||
{
|
||||
auto it = cache_.find(std::string(semantic));
|
||||
if (it == cache_.end() || !it->second.is_boolean()) return std::nullopt;
|
||||
return it->second.get<bool>();
|
||||
}
|
||||
|
||||
// ================= 修改(仅本地 staging) =================
|
||||
void TboxConfigClient::setString(std::string_view semantic, const std::string& v)
|
||||
{
|
||||
pending_[std::string(semantic)] = v;
|
||||
dirty_ = true;
|
||||
}
|
||||
|
||||
void TboxConfigClient::setInt(std::string_view semantic, int v)
|
||||
{
|
||||
pending_[std::string(semantic)] = v;
|
||||
dirty_ = true;
|
||||
}
|
||||
|
||||
void TboxConfigClient::setBool(std::string_view semantic, bool v)
|
||||
{
|
||||
pending_[std::string(semantic)] = v;
|
||||
dirty_ = true;
|
||||
}
|
||||
|
||||
// ================= 提交并确认 =================
|
||||
bool TboxConfigClient::commitAndFetch()
|
||||
{
|
||||
if (pending_.empty()) return true;
|
||||
|
||||
nlohmann::json body;
|
||||
body["changes"] = pending_;
|
||||
body["base_version"] = version_;
|
||||
|
||||
httplib::Client cli(server_, port_);
|
||||
auto res = cli.Post("/api/config/update", body.dump(), "application/json");
|
||||
if (!res || res->status != 200) return false;
|
||||
|
||||
pending_.clear();
|
||||
|
||||
// 🔴 关键点:立刻重新拉取
|
||||
return fetch();
|
||||
}
|
||||
326
src/config/ini.c
326
src/config/ini.c
@ -1,326 +0,0 @@
|
||||
/* inih -- simple .INI file parser
|
||||
|
||||
SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
Copyright (C) 2009-2025, Ben Hoyt
|
||||
|
||||
inih is released under the New BSD license (see LICENSE.txt). Go to the project
|
||||
home page for more info:
|
||||
|
||||
https://github.com/benhoyt/inih
|
||||
|
||||
*/
|
||||
|
||||
#if defined(_MSC_VER) && !defined(_CRT_SECURE_NO_WARNINGS)
|
||||
#define _CRT_SECURE_NO_WARNINGS
|
||||
#endif
|
||||
|
||||
#include <stdio.h>
|
||||
#include <ctype.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "ini.h"
|
||||
|
||||
#if !INI_USE_STACK
|
||||
#if INI_CUSTOM_ALLOCATOR
|
||||
#include <stddef.h>
|
||||
void* ini_malloc(size_t size);
|
||||
void ini_free(void* ptr);
|
||||
void* ini_realloc(void* ptr, size_t size);
|
||||
#else
|
||||
#include <stdlib.h>
|
||||
#define ini_malloc malloc
|
||||
#define ini_free free
|
||||
#define ini_realloc realloc
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#define MAX_SECTION 50
|
||||
#define MAX_NAME 50
|
||||
|
||||
/* Used by ini_parse_string() to keep track of string parsing state. */
|
||||
typedef struct {
|
||||
const char* ptr;
|
||||
size_t num_left;
|
||||
} ini_parse_string_ctx;
|
||||
|
||||
/* Strip whitespace chars off end of given string, in place. end must be a
|
||||
pointer to the NUL terminator at the end of the string. Return s. */
|
||||
static char* ini_rstrip(char* s, char* end)
|
||||
{
|
||||
while (end > s && isspace((unsigned char)(*--end)))
|
||||
*end = '\0';
|
||||
return s;
|
||||
}
|
||||
|
||||
/* Return pointer to first non-whitespace char in given string. */
|
||||
static char* ini_lskip(const char* s)
|
||||
{
|
||||
while (*s && isspace((unsigned char)(*s)))
|
||||
s++;
|
||||
return (char*)s;
|
||||
}
|
||||
|
||||
/* Return pointer to first char (of chars) or inline comment in given string,
|
||||
or pointer to NUL at end of string if neither found. Inline comment must
|
||||
be prefixed by a whitespace character to register as a comment. */
|
||||
static char* ini_find_chars_or_comment(const char* s, const char* chars)
|
||||
{
|
||||
#if INI_ALLOW_INLINE_COMMENTS
|
||||
int was_space = 0;
|
||||
while (*s && (!chars || !strchr(chars, *s)) &&
|
||||
!(was_space && strchr(INI_INLINE_COMMENT_PREFIXES, *s))) {
|
||||
was_space = isspace((unsigned char)(*s));
|
||||
s++;
|
||||
}
|
||||
#else
|
||||
while (*s && (!chars || !strchr(chars, *s))) {
|
||||
s++;
|
||||
}
|
||||
#endif
|
||||
return (char*)s;
|
||||
}
|
||||
|
||||
/* Similar to strncpy, but ensures dest (size bytes) is
|
||||
NUL-terminated, and doesn't pad with NULs. */
|
||||
static char* ini_strncpy0(char* dest, const char* src, size_t size)
|
||||
{
|
||||
/* Could use strncpy internally, but it causes gcc warnings (see issue #91) */
|
||||
size_t i;
|
||||
for (i = 0; i < size - 1 && src[i]; i++)
|
||||
dest[i] = src[i];
|
||||
dest[i] = '\0';
|
||||
return dest;
|
||||
}
|
||||
|
||||
/* See documentation in header file. */
|
||||
int ini_parse_stream(ini_reader reader, void* stream, ini_handler handler,
|
||||
void* user)
|
||||
{
|
||||
/* Uses a fair bit of stack (use heap instead if you need to) */
|
||||
#if INI_USE_STACK
|
||||
char line[INI_MAX_LINE];
|
||||
size_t max_line = INI_MAX_LINE;
|
||||
#else
|
||||
char* line;
|
||||
size_t max_line = INI_INITIAL_ALLOC;
|
||||
#endif
|
||||
#if INI_ALLOW_REALLOC && !INI_USE_STACK
|
||||
char* new_line;
|
||||
#endif
|
||||
char section[MAX_SECTION] = "";
|
||||
#if INI_ALLOW_MULTILINE
|
||||
char prev_name[MAX_NAME] = "";
|
||||
#endif
|
||||
|
||||
size_t offset;
|
||||
char* start;
|
||||
char* end;
|
||||
char* name;
|
||||
char* value;
|
||||
int lineno = 0;
|
||||
int error = 0;
|
||||
char abyss[16]; /* Used to consume input when a line is too long. */
|
||||
|
||||
#if !INI_USE_STACK
|
||||
line = (char*)ini_malloc(INI_INITIAL_ALLOC);
|
||||
if (!line) {
|
||||
return -2;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if INI_HANDLER_LINENO
|
||||
#define HANDLER(u, s, n, v) handler(u, s, n, v, lineno)
|
||||
#else
|
||||
#define HANDLER(u, s, n, v) handler(u, s, n, v)
|
||||
#endif
|
||||
|
||||
/* Scan through stream line by line */
|
||||
while (reader(line, (int)max_line, stream) != NULL) {
|
||||
offset = strlen(line);
|
||||
|
||||
#if INI_ALLOW_REALLOC && !INI_USE_STACK
|
||||
while (max_line < INI_MAX_LINE &&
|
||||
offset == max_line - 1 && line[offset - 1] != '\n') {
|
||||
max_line *= 2;
|
||||
if (max_line > INI_MAX_LINE)
|
||||
max_line = INI_MAX_LINE;
|
||||
new_line = ini_realloc(line, max_line);
|
||||
if (!new_line) {
|
||||
ini_free(line);
|
||||
return -2;
|
||||
}
|
||||
line = new_line;
|
||||
if (reader(line + offset, (int)(max_line - offset), stream) == NULL)
|
||||
break;
|
||||
offset += strlen(line + offset);
|
||||
}
|
||||
#endif
|
||||
|
||||
lineno++;
|
||||
|
||||
/* If line exceeded INI_MAX_LINE bytes, discard till end of line. */
|
||||
if (offset == max_line - 1 && line[offset - 1] != '\n') {
|
||||
while (reader(abyss, sizeof(abyss), stream) != NULL) {
|
||||
if (!error)
|
||||
error = lineno;
|
||||
if (abyss[strlen(abyss) - 1] == '\n')
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
start = line;
|
||||
#if INI_ALLOW_BOM
|
||||
if (lineno == 1 && (unsigned char)start[0] == 0xEF &&
|
||||
(unsigned char)start[1] == 0xBB &&
|
||||
(unsigned char)start[2] == 0xBF) {
|
||||
start += 3;
|
||||
}
|
||||
#endif
|
||||
start = ini_rstrip(ini_lskip(start), line + offset);
|
||||
|
||||
if (strchr(INI_START_COMMENT_PREFIXES, *start)) {
|
||||
/* Start-of-line comment */
|
||||
}
|
||||
#if INI_ALLOW_MULTILINE
|
||||
else if (*prev_name && *start && start > line) {
|
||||
#if INI_ALLOW_INLINE_COMMENTS
|
||||
end = ini_find_chars_or_comment(start, NULL);
|
||||
*end = '\0';
|
||||
ini_rstrip(start, end);
|
||||
#endif
|
||||
/* Non-blank line with leading whitespace, treat as continuation
|
||||
of previous name's value (as per Python configparser). */
|
||||
if (!HANDLER(user, section, prev_name, start) && !error)
|
||||
error = lineno;
|
||||
}
|
||||
#endif
|
||||
else if (*start == '[') {
|
||||
/* A "[section]" line */
|
||||
end = ini_find_chars_or_comment(start + 1, "]");
|
||||
if (*end == ']') {
|
||||
*end = '\0';
|
||||
ini_strncpy0(section, start + 1, sizeof(section));
|
||||
#if INI_ALLOW_MULTILINE
|
||||
*prev_name = '\0';
|
||||
#endif
|
||||
#if INI_CALL_HANDLER_ON_NEW_SECTION
|
||||
if (!HANDLER(user, section, NULL, NULL) && !error)
|
||||
error = lineno;
|
||||
#endif
|
||||
}
|
||||
else if (!error) {
|
||||
/* No ']' found on section line */
|
||||
error = lineno;
|
||||
}
|
||||
}
|
||||
else if (*start) {
|
||||
/* Not a comment, must be a name[=:]value pair */
|
||||
end = ini_find_chars_or_comment(start, "=:");
|
||||
if (*end == '=' || *end == ':') {
|
||||
*end = '\0';
|
||||
name = ini_rstrip(start, end);
|
||||
value = end + 1;
|
||||
#if INI_ALLOW_INLINE_COMMENTS
|
||||
end = ini_find_chars_or_comment(value, NULL);
|
||||
*end = '\0';
|
||||
#endif
|
||||
value = ini_lskip(value);
|
||||
ini_rstrip(value, end);
|
||||
|
||||
#if INI_ALLOW_MULTILINE
|
||||
ini_strncpy0(prev_name, name, sizeof(prev_name));
|
||||
#endif
|
||||
/* Valid name[=:]value pair found, call handler */
|
||||
if (!HANDLER(user, section, name, value) && !error)
|
||||
error = lineno;
|
||||
}
|
||||
else {
|
||||
/* No '=' or ':' found on name[=:]value line */
|
||||
#if INI_ALLOW_NO_VALUE
|
||||
*end = '\0';
|
||||
name = ini_rstrip(start, end);
|
||||
if (!HANDLER(user, section, name, NULL) && !error)
|
||||
error = lineno;
|
||||
#else
|
||||
if (!error)
|
||||
error = lineno;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
#if INI_STOP_ON_FIRST_ERROR
|
||||
if (error)
|
||||
break;
|
||||
#endif
|
||||
}
|
||||
|
||||
#if !INI_USE_STACK
|
||||
ini_free(line);
|
||||
#endif
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
/* See documentation in header file. */
|
||||
int ini_parse_file(FILE* file, ini_handler handler, void* user)
|
||||
{
|
||||
return ini_parse_stream((ini_reader)fgets, file, handler, user);
|
||||
}
|
||||
|
||||
/* See documentation in header file. */
|
||||
int ini_parse(const char* filename, ini_handler handler, void* user)
|
||||
{
|
||||
FILE* file;
|
||||
int error;
|
||||
|
||||
file = fopen(filename, "r");
|
||||
if (!file)
|
||||
return -1;
|
||||
error = ini_parse_file(file, handler, user);
|
||||
fclose(file);
|
||||
return error;
|
||||
}
|
||||
|
||||
/* An ini_reader function to read the next line from a string buffer. This
|
||||
is the fgets() equivalent used by ini_parse_string(). */
|
||||
static char* ini_reader_string(char* str, int num, void* stream) {
|
||||
ini_parse_string_ctx* ctx = (ini_parse_string_ctx*)stream;
|
||||
const char* ctx_ptr = ctx->ptr;
|
||||
size_t ctx_num_left = ctx->num_left;
|
||||
char* strp = str;
|
||||
char c;
|
||||
|
||||
if (ctx_num_left == 0 || num < 2)
|
||||
return NULL;
|
||||
|
||||
while (num > 1 && ctx_num_left != 0) {
|
||||
c = *ctx_ptr++;
|
||||
ctx_num_left--;
|
||||
*strp++ = c;
|
||||
if (c == '\n')
|
||||
break;
|
||||
num--;
|
||||
}
|
||||
|
||||
*strp = '\0';
|
||||
ctx->ptr = ctx_ptr;
|
||||
ctx->num_left = ctx_num_left;
|
||||
return str;
|
||||
}
|
||||
|
||||
/* See documentation in header file. */
|
||||
int ini_parse_string(const char* string, ini_handler handler, void* user) {
|
||||
return ini_parse_string_length(string, strlen(string), handler, user);
|
||||
}
|
||||
|
||||
/* See documentation in header file. */
|
||||
int ini_parse_string_length(const char* string, size_t length,
|
||||
ini_handler handler, void* user) {
|
||||
ini_parse_string_ctx ctx;
|
||||
|
||||
ctx.ptr = string;
|
||||
ctx.num_left = length;
|
||||
return ini_parse_stream((ini_reader)ini_reader_string, &ctx, handler,
|
||||
user);
|
||||
}
|
||||
131
src/main.cpp
131
src/main.cpp
@ -1,87 +1,35 @@
|
||||
#include "main.h"
|
||||
|
||||
#include <unistd.h>
|
||||
|
||||
#include <csignal>
|
||||
#include <filesystem>
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include <thread>
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// 命令行参数解析
|
||||
// -----------------------------------------------------------------------------
|
||||
// ==========================
|
||||
// 命令行参数
|
||||
// ==========================
|
||||
struct CmdOptions
|
||||
{
|
||||
bool show_help = false;
|
||||
bool show_version = false;
|
||||
bool init_config = false;
|
||||
std::string config_path;
|
||||
};
|
||||
|
||||
CmdOptions parseArgs(int argc, char* argv[])
|
||||
{
|
||||
CmdOptions opts;
|
||||
|
||||
if (argc == 1)
|
||||
{
|
||||
// 默认情况:使用当前目录下的 config.ini
|
||||
opts.config_path = "./config.ini";
|
||||
return opts;
|
||||
}
|
||||
|
||||
for (int i = 1; i < argc; ++i)
|
||||
{
|
||||
std::string arg = argv[i];
|
||||
|
||||
if (arg == "-h" || arg == "--help")
|
||||
{
|
||||
if (opts.show_help || opts.show_version || !opts.config_path.empty())
|
||||
{
|
||||
std::cerr << "Error: duplicate or conflicting options.\n";
|
||||
exit(1);
|
||||
}
|
||||
opts.show_help = true;
|
||||
}
|
||||
else if (arg == "-v" || arg == "--version")
|
||||
{
|
||||
if (opts.show_help || opts.show_version || !opts.config_path.empty())
|
||||
{
|
||||
std::cerr << "Error: duplicate or conflicting options.\n";
|
||||
exit(1);
|
||||
}
|
||||
opts.show_version = true;
|
||||
}
|
||||
else if (arg == "-c" || arg == "--config")
|
||||
{
|
||||
if (!opts.config_path.empty() || opts.show_help || opts.show_version)
|
||||
{
|
||||
std::cerr << "Error: duplicate or conflicting options.\n";
|
||||
exit(1);
|
||||
}
|
||||
if (i + 1 < argc) { opts.config_path = argv[++i]; }
|
||||
else
|
||||
{
|
||||
std::cerr << "Error: missing config file path after " << arg << "\n";
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
else if (arg == "--init-config")
|
||||
{
|
||||
if (opts.show_help || opts.show_version || opts.init_config)
|
||||
{
|
||||
std::cerr << "Error: duplicate or conflicting options.\n";
|
||||
exit(1);
|
||||
}
|
||||
opts.init_config = true;
|
||||
}
|
||||
|
||||
if (arg == "-h" || arg == "--help") { opts.show_help = true; }
|
||||
else if (arg == "-v" || arg == "--version") { opts.show_version = true; }
|
||||
else
|
||||
{
|
||||
std::cerr << "Unknown option: " << arg << "\n";
|
||||
std::cerr << "Use -h to see available options.\n";
|
||||
exit(1);
|
||||
std::cerr << "Use -h or --help for usage.\n";
|
||||
std::exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
@ -102,28 +50,9 @@ int main(int argc, char* argv[])
|
||||
{
|
||||
CmdOptions opts = parseArgs(argc, argv);
|
||||
|
||||
if (opts.init_config)
|
||||
{
|
||||
std::string path = opts.config_path.empty() ? "./config.ini" : opts.config_path;
|
||||
|
||||
if (std::filesystem::exists(path))
|
||||
{
|
||||
std::cerr << "Config file already exists at " << path << "\n";
|
||||
return 1;
|
||||
}
|
||||
|
||||
ConfigManager::instance().load(path); // 使用 schema 默认值
|
||||
ConfigManager::instance().save(); // 写出完整配置
|
||||
|
||||
std::cout << "Default config file created at:\n " << path << "\n";
|
||||
std::cout << "Please review and edit it before running the app.\n";
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (opts.show_help)
|
||||
{
|
||||
std::cout << "Usage: " << argv[0] << " [-v] [-h] [-c <config_path>]\n";
|
||||
std::cout << "If no arguments are given, './config.ini' will be used by default.\n";
|
||||
std::cout << "Usage: tbox [-h] [-v]\n";
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -133,34 +62,40 @@ int main(int argc, char* argv[])
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!opts.config_path.empty())
|
||||
// =========================================================
|
||||
// 1️⃣ 初始化 TBox 配置客户端(Config Server 是前提)
|
||||
// =========================================================
|
||||
LOG_INFO(tbox_logger, "Initializing TboxConfigManager...");
|
||||
|
||||
if (!TboxConfigManager::instance().init("127.0.0.1", 18080))
|
||||
{
|
||||
if (!std::filesystem::exists(opts.config_path))
|
||||
{
|
||||
std::cerr << "Error: config file not found at " << opts.config_path << "\n";
|
||||
return 1;
|
||||
}
|
||||
std::cout << "Using config file: " << opts.config_path << "\n";
|
||||
ConfigManager::instance().load(opts.config_path);
|
||||
LOG_ERROR(tbox_logger, "Failed to connect config server, exit");
|
||||
return 1;
|
||||
}
|
||||
|
||||
// 启动线程
|
||||
// init_can_bus_rc_ctrl("can0"); // 初始化远控 CAN 总线
|
||||
// init_tcp_server_tbox_autodata("0.0.0.0", 50018); // 获取自驾数据
|
||||
// init_tcp_client_vehicle_position("192.168.1.151", 3333); // 获取定位数据
|
||||
LOG_INFO(tbox_logger, "Config loaded, version=" + std::to_string(TboxConfigManager::instance().version()));
|
||||
|
||||
// init_serial_at(ConfigManager::instance().getSerialDev(), ConfigManager::instance().getSerialBaudrate());
|
||||
// =========================================================
|
||||
// 2️⃣ 启动各业务模块(开始真正运行)
|
||||
// =========================================================
|
||||
init_can_bus_rc_ctrl("can0"); // 初始化远控 CAN 总线
|
||||
init_tcp_server_tbox_autodata("0.0.0.0", 50018); // 获取自驾数据
|
||||
init_tcp_client_vehicle_position("192.168.1.151", 3333); // 获取定位数据
|
||||
|
||||
init_serial_at("/dev/ttyUSB3", 115200);
|
||||
|
||||
init_tcp_server_tbox_v2v("0.0.0.0", 10005); // 建立与域控间的V2V链路
|
||||
|
||||
init_mqtt_client_tbox_v2v(ConfigManager::instance().getMqttIp(), ConfigManager::instance().getMqttPort(),
|
||||
ConfigManager::instance().getMqttUsername(),
|
||||
ConfigManager::instance().getMqttPassword()); // 连接平台V2V MQTT服务器
|
||||
init_mqtt_client_tbox_v2v(TboxConfigManager::instance().getMqttIp(), TboxConfigManager::instance().getMqttPort(),
|
||||
TboxConfigManager::instance().getMqttUsername(),
|
||||
TboxConfigManager::instance().getMqttPassword()); // 连接平台V2V MQTT服务器
|
||||
|
||||
// init_mqtt_client_veh_rc(ConfigManager::instance().getCockpitMqttIp(),
|
||||
// ConfigManager::instance().getCockpitMqttPort()); // 连接台架的 MQTT 服务器
|
||||
init_mqtt_client_veh_rc(TboxConfigManager::instance().getCockpitMqttIp(),
|
||||
TboxConfigManager::instance().getCockpitMqttPort()); // 连接台架的 MQTT 服务器
|
||||
|
||||
// 阻塞
|
||||
// =========================================================
|
||||
// 3️⃣ 主循环
|
||||
// =========================================================
|
||||
while (true) { std::this_thread::sleep_for(std::chrono::seconds(5)); }
|
||||
|
||||
return 0;
|
||||
|
||||
Loading…
Reference in New Issue
Block a user