diff --git a/.vscode/settings.json b/.vscode/settings.json index fe6f51f..808dd7f 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -73,6 +73,8 @@ "variant": "cpp", "forward_list": "cpp", "ranges": "cpp", - "valarray": "cpp" + "valarray": "cpp", + "charconv": "cpp", + "unordered_set": "cpp" } } \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt index 49becb5..8e78f25 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -49,7 +49,7 @@ add_library(edge_proxy_lib STATIC #modbus src/modbus/modbus_rtu_client.cc - src/modbus/modbus_rtu_poller_service.cc + src/modbus/modbus_rtu_bus_service.cc src/modbus/generic_modbus_parser.cc # --- Modbus Master --- src/protocol/modbus/modbus_protocol.cc diff --git a/config/devices.json b/config/devices.json index 73bc846..62ba2d1 100644 --- a/config/devices.json +++ b/config/devices.json @@ -50,5 +50,10 @@ {"name": "valve_status","address": 104, "type": "UINT16", "scale": 1.0} ] } - ] + ], + "modbus_rtu_bus_configs": { + "/dev/ttyS7": { + "inter_device_delay_ms": 150 + } + } } \ No newline at end of file diff --git a/src/deviceManager/device_manager.cc b/src/deviceManager/device_manager.cc index caa7edf..c7db2af 100644 --- a/src/deviceManager/device_manager.cc +++ b/src/deviceManager/device_manager.cc @@ -8,7 +8,7 @@ using json = nlohmann::json; -// 辅助函数:将JSON中的字符串类型映射到C++枚举 + static ModbusDataType string_to_modbus_data_type(const std::string& type_str) { static const std::map type_map = { {"UINT16", ModbusDataType::UINT16}, @@ -44,8 +44,10 @@ void DeviceManager::load_and_start(const std::string& config_path) { try { json config_json = json::parse(config_file); - // --- 加载 Modbus RTU 设备 --- + // --- MODIFIED: 加载 Modbus RTU 设备的逻辑 --- if (config_json.contains("modbus_rtu_devices")) { + // 1. 先将所有启用的设备按 port_path (总线) 分组 + std::map> rtu_device_groups; for (const auto& dev_json : config_json["modbus_rtu_devices"]) { if (!dev_json.value("enabled", false)) continue; @@ -61,18 +63,27 @@ void DeviceManager::load_and_start(const std::string& config_path) { dp_json.at("name").get(), (uint16_t)dp_json.at("address").get(), string_to_modbus_data_type(dp_json.at("type").get()), - dp_json.value("scale", 1.0) // .value for optional fields + dp_json.value("scale", 1.0) }); } - auto service = std::make_unique(config, m_report_callback); + // 将解析后的配置加入对应的分组 + rtu_device_groups[config.port_path].push_back(config); + } + + // 2. 为每个分组 (即每个物理串口) 创建一个 BusService + for (const auto& pair : rtu_device_groups) { + const std::string& port_path = pair.first; + const std::vector& devices_on_bus = pair.second; + + auto service = std::make_unique(port_path, devices_on_bus, m_report_callback); service->start(); - m_rtu_services.push_back(std::move(service)); - spdlog::info("Started Modbus RTU service for device '{}'.", config.device_id); + m_rtu_bus_services.push_back(std::move(service)); + spdlog::info("Started Modbus RTU Bus service for port '{}' with {} device(s).", port_path, devices_on_bus.size()); } } - // --- 加载 Modbus TCP 设备 --- + // --- 加载 Modbus TCP 设备 (逻辑保持不变) --- if (config_json.contains("modbus_tcp_devices")) { for (const auto& dev_json : config_json["modbus_tcp_devices"]) { if (!dev_json.value("enabled", false)) continue; @@ -84,7 +95,6 @@ void DeviceManager::load_and_start(const std::string& config_path) { config.slave_id = dev_json.at("slave_id").get(); config.poll_interval_ms = dev_json.at("poll_interval_ms").get(); - // 解析 data_points for (const auto& dp_json : dev_json.at("data_points")) { config.data_points.push_back({ dp_json.at("name").get(), @@ -111,11 +121,13 @@ void DeviceManager::stop_all() { std::lock_guard lock(m_mutex); spdlog::info("Stopping all device services..."); - for (auto& service : m_rtu_services) { + // 停止所有 RTU 总线服务 + for (auto& service : m_rtu_bus_services) { service->stop(); } - m_rtu_services.clear(); + m_rtu_bus_services.clear(); + // 停止所有 TCP 轮询服务 for (auto& poller : m_tcp_pollers) { poller->stop(); } @@ -124,43 +136,38 @@ void DeviceManager::stop_all() { spdlog::info("All device services stopped."); } - std::vector DeviceManager::get_all_device_info() const { - // 使用 lock_guard 确保在函数返回前互斥锁能被自动释放 std::lock_guard lock(m_mutex); std::vector all_devices; - all_devices.reserve(m_rtu_services.size() + m_tcp_pollers.size()); + all_devices.reserve(m_tcp_pollers.size()); // 初始容量可以先按TCP设备数算 - // 遍历 RTU 设备 - for (const auto& service : m_rtu_services) { - // !! 前提: ModbusRtuPollerService 必须有 get_config() 方法 - const auto& config = service->get_config(); - - DeviceInfo info; - info.id = config.device_id; - info.type = "ModbusRTU"; - info.is_running = service->is_running(); // 假设 service 有 is_running() 方法 - info.connection_details["Port Path"] = config.port_path; - info.connection_details["Baud Rate"] = std::to_string(config.baud_rate); - info.connection_details["Slave ID"] = std::to_string(config.slave_id); - - all_devices.push_back(info); + // 遍历所有 RTU 总线服务 + for (const auto& service : m_rtu_bus_services) { + const auto& configs_on_bus = service->get_all_device_configs(); + for (const auto& config : configs_on_bus) { + DeviceInfo info; + info.id = config.device_id; + info.type = "ModbusRTU"; + info.is_running = service->is_running(); + info.connection_details["Port Path"] = config.port_path; + info.connection_details["Baud Rate"] = std::to_string(config.baud_rate); + info.connection_details["Slave ID"] = std::to_string(config.slave_id); + all_devices.push_back(info); + } } - // 遍历 TCP 设备 + // 遍历 TCP 设备 (逻辑不变) for (const auto& poller : m_tcp_pollers) { - // !! 前提: ModbusMasterPoller 必须有 get_config() 方法 const auto& config = poller->get_config(); DeviceInfo info; info.id = config.device_id; info.type = "ModbusTCP"; - info.is_running = poller->is_running(); // 假设 poller 有 is_running() 方法 + info.is_running = poller->is_running(); info.connection_details["IP Address"] = config.ip_address; info.connection_details["Port"] = std::to_string(config.port); info.connection_details["Slave ID"] = std::to_string(config.slave_id); - all_devices.push_back(info); } @@ -179,15 +186,15 @@ bool DeviceManager::send_control_command(const std::string& device_id, uint16_t } } - // 然后,在 RTU service 列表中查找 - for (const auto& service : m_rtu_services) { - if (service->get_config().device_id == device_id) { - spdlog::info("Found RTU device '{}'. Dispatching write command to address {}.", device_id, address); - service->write_single_register(address, value); + // 然后,在 RTU bus service 列表中查找哪个服务管理着该设备 + for (const auto& service : m_rtu_bus_services) { + if (service->manages_device(device_id)) { + spdlog::info("Found RTU device '{}' on a bus. Dispatching write command.", device_id); + service->write_single_register(device_id, address, value); return true; } } - spdlog::warn("send_control_command failed: Device with ID '{}' not found.", device_id); + spdlog::warn("send_control_command failed: Device with ID '{}' not found in any service.", device_id); return false; } \ No newline at end of file diff --git a/src/deviceManager/device_manager.h b/src/deviceManager/device_manager.h index ae9d265..3798c76 100644 --- a/src/deviceManager/device_manager.h +++ b/src/deviceManager/device_manager.h @@ -3,7 +3,7 @@ #define DEVICE_MANAGER_H #include "protocol/iprotocol_adapter.h" -#include "modbus/modbus_rtu_poller_service.h" +#include "modbus/modbus_rtu_bus_service.h" #include "modbus/modbus_master_poller.h" #include #include @@ -69,11 +69,9 @@ private: ReportDataCallback m_report_callback; // 用于存储正在运行的服务实例,以管理其生命周期 - std::vector> m_rtu_services; + std::vector> m_rtu_bus_services; std::vector> m_tcp_pollers; - // --- <<< 新增成员 >>> --- - // mutable 关键字允许在 const 成员函数中修改它 (例如在 get_all_device_info 中加锁) mutable std::mutex m_mutex; }; diff --git a/src/modbus/modbus_rtu_bus_service.cc b/src/modbus/modbus_rtu_bus_service.cc new file mode 100644 index 0000000..c99a17f --- /dev/null +++ b/src/modbus/modbus_rtu_bus_service.cc @@ -0,0 +1,150 @@ +// 文件名: src/modbus/modbus_rtu_bus_service.cc (注意文件名已更改) +#include "modbus_rtu_bus_service.h" +#include "generic_modbus_parser.h" +#include "spdlog/spdlog.h" +#include +#include + +// --- MODIFIED: 构造函数接收一个设备列表 --- +ModbusRtuBusService::ModbusRtuBusService(std::string port_path, + std::vector devices, + ReportDataCallback report_cb) + : m_port_path(std::move(port_path)), + m_devices(std::move(devices)), + m_report_callback(std::move(report_cb)) {} + +ModbusRtuBusService::~ModbusRtuBusService() { + stop(); +} + +// --- start, stop, is_running 逻辑基本不变 --- +void ModbusRtuBusService::start() { + if (m_thread.joinable()) { return; } + m_stop_flag = false; + m_thread = std::thread(&ModbusRtuBusService::run, this); + spdlog::info("[Modbus RTU Bus] Service for port '{}' started.", m_port_path); +} + +void ModbusRtuBusService::stop() { + if (m_stop_flag.exchange(true)) { return; } + if (m_thread.joinable()) { + m_thread.join(); + spdlog::info("[Modbus RTU Bus] Service for port '{}' has been stopped.", m_port_path); + } +} +bool ModbusRtuBusService::is_running() const { + return !m_stop_flag && m_thread.joinable(); +} + + +// --- NEW: 新增辅助方法 --- +bool ModbusRtuBusService::manages_device(const std::string& device_id) const { + for (const auto& device_config : m_devices) { + if (device_config.device_id == device_id) { + return true; + } + } + return false; +} + +// ---写方法现在需要 device_id 来查找 slave_id --- +void ModbusRtuBusService::write_single_register(const std::string& device_id, uint16_t address, uint16_t value) { + uint8_t slave_id = 0; + bool device_found = false; + // 首先根据 device_id 找到对应的 slave_id + for (const auto& config : m_devices) { + if (config.device_id == device_id) { + slave_id = config.slave_id; + device_found = true; + break; + } + } + + if (!device_found) { + spdlog::warn("[Modbus RTU Bus] Write failed: Device '{}' not managed by service on port '{}'.", device_id, m_port_path); + return; + } + + // 锁定串口资源,执行写操作 + std::lock_guard lock(m_client_mutex); + try { + spdlog::debug("[Modbus RTU Bus] Port '{}': Writing to device '{}' (Slave ID {})...", m_port_path, device_id, slave_id); + m_client.writeSingleRegister(slave_id, address, value); + spdlog::info("[Modbus RTU Bus] Port '{}': Successfully wrote to device '{}'.", m_port_path, device_id); + } catch (const std::exception& e) { + spdlog::error("[Modbus RTU Bus] Port '{}': Failed to write to device '{}': {}", m_port_path, device_id, e.what()); + } +} + +const std::vector& ModbusRtuBusService::get_all_device_configs() const { + return m_devices; +} + +// --- 以串行轮询多个设备 --- +void ModbusRtuBusService::run() { + if (m_devices.empty()) { + spdlog::warn("[Modbus RTU Bus] No devices configured for port '{}'. Thread will not run.", m_port_path); + return; + } + + // 只需要设置一次串口参数 + // 我们假设同一总线上的设备波特率等参数一致,这是 Modbus RTU 的基本要求 + if (!m_client.setPortSettings(m_port_path, m_devices[0].baud_rate)) { + spdlog::error("[Modbus RTU Bus] Failed to set up serial port '{}'. Thread exiting.", m_port_path); + return; + } + + // 主循环 + while (!m_stop_flag) { + // 依次轮询此总线上的每一个设备 + for (const auto& config : m_devices) { + if (m_stop_flag) break; // 每次轮询前都检查停止标志 + + { // 创建作用域以控制锁 + std::lock_guard lock(m_client_mutex); + try { + // --- 针对当前设备计算轮询范围 --- + if (config.data_points.empty()) continue; + auto [min_it, max_it] = std::minmax_element(config.data_points.begin(), config.data_points.end(), + [](const DataPointConfig& a, const DataPointConfig& b) { return a.address < b.address; }); + uint16_t start_address = min_it->address; + uint16_t last_address = max_it->address; + if (max_it->type >= ModbusDataType::UINT32) { last_address += 1; } + uint16_t quantity = last_address - start_address + 1; + + std::vector raw_registers = m_client.readHoldingRegisters(config.slave_id, start_address, quantity); + + std::map registers_map; + for (uint16_t i = 0; i < raw_registers.size(); ++i) { + registers_map[start_address + i] = raw_registers[i]; + } + nlohmann::json data_j = GenericModbusParser::parse(registers_map, config.data_points); + UnifiedData report_data; + report_data.device_id = config.device_id; + report_data.timestamp_ms = std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count(); + report_data.data_json = data_j.dump(); + if (m_report_callback) { + m_report_callback(report_data); + } + } catch (const std::exception& e) { + spdlog::error("[Modbus RTU Bus] Port '{}', Device '{}': Communication error: {}", m_port_path, config.device_id, e.what()); + } + } // 锁释放 + + // 在轮询下一个设备前,短暂延时,给总线和设备响应时间 + std::this_thread::sleep_for(std::chrono::milliseconds(150)); + } + + // 完成一轮所有设备的轮询后,等待一个大的周期 + if (!m_stop_flag) { + // 这里的等待时间可以根据需求调整,例如使用第一个设备的 poll_interval_ms + auto wake_up_time = std::chrono::steady_clock::now() + std::chrono::milliseconds(m_devices[0].poll_interval_ms); + while (std::chrono::steady_clock::now() < wake_up_time && !m_stop_flag) { + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + } + } + } + + m_client.closePort(); + spdlog::info("[RTU Bus Service {}] Run loop finished. Thread exiting.", m_port_path); +} \ No newline at end of file diff --git a/src/modbus/modbus_rtu_bus_service.h b/src/modbus/modbus_rtu_bus_service.h new file mode 100644 index 0000000..8740558 --- /dev/null +++ b/src/modbus/modbus_rtu_bus_service.h @@ -0,0 +1,65 @@ +// 文件名: src/modbus/modbus_rtu_bus_service.h (注意文件名已更改) +#ifndef MODBUS_RTU_BUS_SERVICE_H +#define MODBUS_RTU_BUS_SERVICE_H + +#include "protocol/iprotocol_adapter.h" +#include "modbus_rtu_client.h" +#include "modbus_common.h" +#include +#include +#include +#include // <--- 新增 +#include + +/** + * @brief Modbus RTU 总线服务类 + * * 负责在一个独立的后台线程中,管理一个物理串口,并依次轮询该串口上的所有设备。 + */ +class ModbusRtuBusService { +public: + /** + * @brief 构造函数 + * @param port_path 该总线所使用的串口路径, e.g., "/dev/ttyS0" + * @param devices 该总线上所有设备的配置列表 + * @param report_cb 用于上报数据的回调函数 + */ + ModbusRtuBusService(std::string port_path, + std::vector devices, + ReportDataCallback report_cb); + ~ModbusRtuBusService(); + + ModbusRtuBusService(const ModbusRtuBusService&) = delete; + ModbusRtuBusService& operator=(const ModbusRtuBusService&) = delete; + + void start(); + void stop(); + bool is_running() const; + + /** + * @brief 检查该总线服务是否管理着指定的设备ID + */ + bool manages_device(const std::string& device_id) const; + const std::vector& get_all_device_configs() const; + + /** + * @brief (线程安全) 向总线上的某个设备写入单个寄存器 + * @param device_id 目标设备的ID + * @param address 寄存器地址 + * @param value 要写入的值 + */ + void write_single_register(const std::string& device_id, uint16_t address, uint16_t value); + +private: + void run(); + + std::string m_port_path; + std::vector m_devices; + ReportDataCallback m_report_callback; + ModbusRTUClient m_client; + + std::thread m_thread; + std::atomic m_stop_flag{false}; + std::mutex m_client_mutex; // 保护 m_client 的互斥锁 +}; + +#endif // MODBUS_RTU_BUS_SERVICE_H \ No newline at end of file diff --git a/src/modbus/modbus_rtu_poller_service.cc b/src/modbus/modbus_rtu_poller_service.cc deleted file mode 100644 index 7ffef49..0000000 --- a/src/modbus/modbus_rtu_poller_service.cc +++ /dev/null @@ -1,118 +0,0 @@ -// 文件名: src/modbus/modbus_rtu_poller_service.cc -#include "modbus_rtu_poller_service.h" -#include "generic_modbus_parser.h" // 使用通用解析器 -#include "spdlog/spdlog.h" -#include -#include // for std::minmax_element - -ModbusRtuPollerService::ModbusRtuPollerService(ModbusRtuDeviceConfig config, ReportDataCallback report_cb) - : m_config(std::move(config)), - m_report_callback(std::move(report_cb)) {} - -ModbusRtuPollerService::~ModbusRtuPollerService() { - stop(); -} - -void ModbusRtuPollerService::start() { - if (m_thread.joinable()) { - spdlog::warn("[Modbus RTU Service] Poller for device '{}' is already running.", m_config.device_id); - return; - } - m_stop_flag = false; - // 启动一个新线程,并将 run() 方法作为入口点 - // 'this' 指针被传递,以便新线程可以调用类的成员函数 - m_thread = std::thread(&ModbusRtuPollerService::run, this); - spdlog::info("[Modbus RTU Service] Poller for device '{}' started in a background thread.", m_config.device_id); -} - -void ModbusRtuPollerService::stop() { - m_stop_flag = true; - if (m_thread.joinable()) { - m_thread.join(); // 等待线程安全退出 - spdlog::info("[Modbus RTU Service] Poller for device '{}' has been stopped.", m_config.device_id); - } -} - -const ModbusRtuDeviceConfig& ModbusRtuPollerService::get_config() const { - return m_config; -} -bool ModbusRtuPollerService::is_running() const { - return !m_stop_flag && m_thread.joinable(); -} - -void ModbusRtuPollerService::write_single_register(uint16_t address, uint16_t value) { - std::lock_guard lock(m_client_mutex); - - try { - spdlog::debug("[Modbus RTU] Device '{}': Writing value {} to address {}.", m_config.device_id, value, address); - m_client.writeSingleRegister(m_config.slave_id, address, value); - spdlog::info("[Modbus RTU] Device '{}': Successfully wrote value {} to address {}.", m_config.device_id, value, address); - } catch (const std::exception& e) { - spdlog::error("[Modbus RTU] Device '{}': Failed to write to address {}: {}", m_config.device_id, address, e.what()); - } -} - -void ModbusRtuPollerService::run() { - if (m_config.data_points.empty()) { - spdlog::warn("[Modbus RTU] Device '{}' has no data points configured. Thread will not run.", m_config.device_id); - return; - } - - if (!m_client.setPortSettings(m_config.port_path, m_config.baud_rate)) { - spdlog::error("[Modbus RTU] Failed to set up serial port '{}' for device '{}'. Thread exiting.", m_config.port_path, m_config.device_id); - return; - } - - auto [min_it, max_it] = std::minmax_element(m_config.data_points.begin(), m_config.data_points.end(), - [](const DataPointConfig& a, const DataPointConfig& b) { - return a.address < b.address; - }); - uint16_t start_address = min_it->address; - uint16_t last_address = max_it->address; - - if (max_it->type == ModbusDataType::UINT32 || max_it->type == ModbusDataType::INT32 || max_it->type == ModbusDataType::FLOAT32) { - last_address += 1; - } - uint16_t quantity = last_address - start_address + 1; - - spdlog::info("[Modbus RTU] Device '{}' will poll {} registers starting from address {}.", m_config.device_id, quantity, start_address); - - while (!m_stop_flag) { - { // <--- 创建一个新的作用域来控制锁的生命周期 - std::lock_guard lock(m_client_mutex); - try { - std::vector raw_registers = m_client.readHoldingRegisters(m_config.slave_id, start_address, quantity); - - std::map registers_map; - for (uint16_t i = 0; i < raw_registers.size(); ++i) { - registers_map[start_address + i] = raw_registers[i]; - } - - nlohmann::json data_j = GenericModbusParser::parse(registers_map, m_config.data_points); - - UnifiedData report_data; - report_data.device_id = m_config.device_id; - report_data.timestamp_ms = std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count(); - report_data.data_json = data_j.dump(); - - if (m_report_callback) { - m_report_callback(report_data); - } - - } catch (const std::exception& e) { - spdlog::error("[Modbus RTU] Error during communication with device '{}': {}", m_config.device_id, e.what()); - } - } // <--- 锁在这里被释放 - - auto wake_up_time = std::chrono::steady_clock::now() + std::chrono::milliseconds(m_config.poll_interval_ms); - while (std::chrono::steady_clock::now() < wake_up_time) { - if (m_stop_flag) { - break; - } - std::this_thread::sleep_for(std::chrono::milliseconds(100)); - } - } - - m_client.closePort(); - spdlog::info("[RTU Poller {}] Run loop finished. Thread exiting.", m_config.device_id); -} \ No newline at end of file diff --git a/src/modbus/modbus_rtu_poller_service.h b/src/modbus/modbus_rtu_poller_service.h deleted file mode 100644 index c5ebe49..0000000 --- a/src/modbus/modbus_rtu_poller_service.h +++ /dev/null @@ -1,84 +0,0 @@ -// 文件名: src/modbus/modbus_rtu_poller_service.h -#ifndef MODBUS_RTU_POLLER_SERVICE_H -#define MODBUS_RTU_POLLER_SERVICE_H - -#include "protocol/iprotocol_adapter.h" -#include "modbus_rtu_client.h" -#include "modbus_common.h" // 包含 ModbusRtuDeviceConfig 和 DataPointConfig 定义 -#include -#include -#include -#include - -/** - * @brief Modbus RTU 轮询服务类 - * * 负责在一个独立的后台线程中,根据配置周期性地轮询一个Modbus RTU设备。 - * 这种设计将同步阻塞的串口IO操作与主程序的异步事件循环隔离开来, - * 避免了主线程被阻塞。 - */ -class ModbusRtuPollerService { -public: - /** - * @brief 构造函数 - * @param config 描述了要连接的设备、串口参数以及需要采集的数据点列表 - * @param report_cb 用于将解析后的数据上报给核心业务层的回调函数 - */ - ModbusRtuPollerService(ModbusRtuDeviceConfig config, ReportDataCallback report_cb); - - /** - * @brief 析构函数 - * * 确保在对象销毁时,后台线程能够被安全地停止和清理。 - */ - ~ModbusRtuPollerService(); - - // 禁止拷贝和赋值,因为该类管理着一个线程资源 - ModbusRtuPollerService(const ModbusRtuPollerService&) = delete; - ModbusRtuPollerService& operator=(const ModbusRtuPollerService&) = delete; - - /** - * @brief 启动轮询服务 - * * 创建并启动一个新的后台线程来执行 run() 方法中的轮询逻辑。 - */ - void start(); - - /** - * @brief 停止轮询服务 - * * 设置停止标志位,并等待后台线程安全地执行完毕并退出。 - */ - void stop(); - - /** - * @brief 获取设备的配置信息 (const-ref to avoid copy) - */ - const ModbusRtuDeviceConfig& get_config() const; - - /** - * @brief 检查轮询服务是否正在运行 - */ - bool is_running() const; - - /** - * @brief 向设备写入单个寄存器 - * @param address 寄存器地址 - * @param value 要写入的值 - */ - void write_single_register(uint16_t address, uint16_t value); -private: - /** - * @brief 线程的主循环函数 - * * 该函数是后台线程的入口点。它包含了连接串口、周期性轮询、 - * 解析数据和上报数据的完整逻辑。 - */ - void run(); - - ModbusRtuDeviceConfig m_config; - ReportDataCallback m_report_callback; - ModbusRTUClient m_client; - - std::thread m_thread; - std::atomic m_stop_flag{false}; - - std::mutex m_client_mutex; -}; - -#endif // MODBUS_RTU_POLLER_SERVICE_H \ No newline at end of file diff --git a/src/test.cc b/src/test.cc index e3ffe58..4dadc3c 100644 --- a/src/test.cc +++ b/src/test.cc @@ -1,13 +1,91 @@ #include -#include #include -#include -#include -#include -#include +// #include "dataStorage/data_storage.h" // 包含你的头文件 +// 假设这些头文件已经包含在你的主程序中 +#include "modbus/modbus_common.h" // 包含 ModbusRtuDeviceConfig, DataPointConfig, ModbusDataType +#include "modbus/modbus_rtu_poller_service.h" // 包含 ModbusRtuPollerService 类 +// 模拟一个从设备接收到的原始数据 +// void mockDataReceiving() { +// DataStorage& storage = DataStorage::getInstance(); + +// // 1. 初始化数据库(只需要做一次) + +// if (!storage.initialize("/app/db/my_app_data.db")) { +// std::cerr << "Failed to initialize database. Exiting." << std::endl; +// return; +// } -int main(int argc, char* argv[]) { - printf("HELLO"); - return 0; -} +// // 2. 模拟收到一批原始数据 +// for (int i = 0; i < 10; ++i) { +// std::string raw_payload = "RAW_SENSOR_DATA_" + std::to_string(i) + "_VALUE=123"; +// std::string device_id = "sensor-alpha-001"; +// std::string protocol = "custom_protocol_v1"; + +// // 2.1 存储原始数据 +// if (!storage.storeRawData(device_id, raw_payload, protocol)) { +// std::cerr << "Failed to store raw data for " << device_id << std::endl; +// } +// } + +// // 3. 模拟协议解析器工作,并将数据转换为 UnifiedData 格式 +// for (int i = 0; i < 5; ++i) { +// UnifiedData data; +// data.device_id = "sensor-beta-002"; +// data.timestamp_ms = std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count(); + +// // 构造一个复杂的 JSON 字符串 +// data.data_json = "{" +// "\"temperature\": " + std::to_string(25.5 + i * 0.1) + "," +// "\"humidity\": " + std::to_string(60.2 - i * 0.5) + "," +// "\"status\": \"active\"," +// "\"error_code\": 0" +// "}"; + +// // 3.2 存储处理后的数据 +// if (!storage.storeProcessedData(data)) { +// std::cerr << "Failed to store processed data for " << data.device_id << std::endl; +// } +// } + +// std::cout << "Data storage simulation finished." << std::endl; +// } + +// int main() { +// mockDataReceiving(); +// return 0; +// } +// 假设这些头文件已经包含在你的主程序中 +// #include "modbus/modbus_common.h" // 包含 ModbusRtuDeviceConfig, DataPointConfig, ModbusDataType +// #include "modbus_rtu_poller_service.h" // 包含 ModbusRtuPollerService 类 +// #include "data_storage.h" // 上一个问题中的数据存储类,用来接收最终数据 +// #include +// 这是你的主程序或某个初始化函数中的代码 +int main() { + // 1. 创建具体的数据点配置列表 + std::vector data_points; + + // 配置第一个数据点:温度 + DataPointConfig temp_config; + temp_config.name = "temperature_celsius"; + temp_config.address = 0x01; + temp_config.type = ModbusDataType::FLOAT32; + temp_config.scale = 1.0; // 假设原始值需要乘以0.1才能得到真实温度 + data_points.push_back(temp_config); + // 配置第二个数据点:状态 + DataPointConfig status_config; + status_config.name = "device_status"; + status_config.address = 0x03; + status_config.type = ModbusDataType::FLOAT32; + status_config.scale = 1.0; // 不需要缩放 + data_points.push_back(status_config); + // 2. 创建完整的设备配置 + ModbusRtuDeviceConfig device_config; + device_config.device_id = "boiler-room-sensor-01"; // 给设备一个唯一的ID,很重要! + device_config.port_path = "/dev/ttyS7"; // Linux下的串口设备名 + device_config.baud_rate = 9600; // 波特率 + device_config.slave_id = 0x02; // Modbus从站地址 + device_config.poll_interval_ms = 2000; // 每2秒轮询一次 + device_config.data_points = data_points; // 将我们刚刚配置的数据点列表放进去 + // ... 接下来进入管理层 ... +} \ No newline at end of file