一、前言

/**************************设备Topology信息**************************/
void DeviceTopologyMgr::parse_device_topology()
{
    // 保证多次调用, 只存在一份设备解析数据
    m_topology = DeviceTopology();
    const std::string filename = std::string(xbasic::get_module_path()) + "device_topology.json";
    std::string content = xbasic::read_data_from_json_file(filename);
    if (content.empty()) {
        xbasic::debug_output("DeviceTopologyMgr::parse_device_topology() get json content failed\n");
        return;
    }
    cjson_object js_root(content);
    // 1、解析顶层字段信息
    if (!js_root.get("version", m_topology.version)) { // 非关键信息, 无需返回
        xbasic::debug_output("DeviceTopologyMgr::parse_device_topology() version was not existed\n");
    }
    if (!js_root.get("product_type", m_topology.product_type)) {
        xbasic::debug_output("DeviceTopologyMgr::parse_device_topology() product_type was not existed\n");
        return;
    }
    if (!js_root.get("device_name", m_topology.device_name)) {
        xbasic::debug_output("DeviceTopologyMgr::parse_device_topology() device_name was not existed\n");
        return;
    }
    if (!js_root.get("datatag_file", m_topology.datatag_file)) {
        xbasic::debug_output("DeviceTopologyMgr::parse_device_topology() datatag_file was not existed\n");
        return;
    }
    // 1.1、解析板级协议数据
    BoardDataTagMgr* board_tag_mgr = BoardDataTagMgr::get_instance();
    board_tag_mgr->setFileName(m_topology.datatag_file);
    // 1.2、解析升级信息
    UpgradeTopologyMgr* upgrade_mgr = UpgradeTopologyMgr::get_instance();
    upgrade_mgr->parse_upgrade_config();
    // 1.3、解析版本查询信息
    VersionInfoMgr* version_mgr = VersionInfoMgr::get_instance();
    version_mgr->parse_system_version_config();
    // 2、解析device_config数组
    cjson_object device_config_arr = js_root["device_config"];
    int device_config_num = device_config_arr.get_array_size();
    xbasic::debug_output("DeviceTopologyMgr::parse_device_topology() device_config num=%d\n", device_config_num);
    parse_device_config_info(device_config_arr, device_config_num);
    // 3、解析device_config数组
    cjson_object communication_config = js_root["communication"];
    int communication_config_num = communication_config.get_array_size();
    xbasic::debug_output("DeviceTopologyMgr::parse_device_topology() communication num=%d\n", communication_config_num);
    parse_communication_config_info(communication_config, communication_config_num);
    // 4、解析devices数组
    cjson_object devices_arr = js_root["devices"];
    int devices_num = devices_arr.get_array_size();
    xbasic::debug_output("DeviceTopologyMgr::parse_device_topology() devices num=%d\n", devices_num);
    parse_devices_info(devices_arr, devices_num);
    return;
}

这段 parse_device_topology() 函数截取自实际项目代码,是一个设备拓扑配置文件的解析入口,干的事就是:

  1. 从程序目录里读 device_topology.json 配置文件
  2. 把JSON里的各种配置(版本、设备、通信协议等)解析出来,存到内存里的 m_topology 对象
  3. 再调用其他管理器,继续解析协议、升级、版本、设备列表这些子配置

二、逐段拆解

1. 初始化和读取JSON文件

// 保证多次调用,只存在一份设备解析数据
m_topology = DeviceTopology(); 

// 1. 拼接配置文件路径:程序当前目录 + "device_topology.json"
const std::string filename = std::string(xbasic::get_module_path()) + "device_topology.json";

// 2. 读取JSON文件内容
std::string content = xbasic::read_data_from_json_file(filename);

// 如果文件读失败/内容为空,打日志直接返回
if (content.empty()) {
    xbasic::debug_output("... get json content failed\n");
    return;
}
  • 这里的 get_module_path(),是自己实现的函数:拿到程序当前目录,用于拼接配置文件名
  • read_data_from_json_file把JSON文件的内容读到 content 字符串里,读失败就直接退出

2. 解析JSON根节点的基础信息

// 把字符串解析成cJSON对象(假设是封装好的JSON解析器)
cjson_object js_root(content);

// 解析version字段(非关键,不存在也不返回,只是打日志)
if (!js_root.get("version", m_topology.version)) {
    xbasic::debug_output("... version was not existed\n");
}

// 解析product_type(必须存在,否则返回)
if (!js_root.get("product_type", m_topology.product_type)) {
    xbasic::debug_output("... product_type was not existed\n");
    return;
}

// 解析device_name(必须存在)
if (!js_root.get("device_name", m_topology.device_name)) {
    return;
}

// 解析datatag_file(必须存在,这是后面协议解析要用的文件)
if (!js_root.get("datatag_file", m_topology.datatag_file)) {
    return;
}
  • 从JSON的根节点,把 versionproduct_typedevice_namedatatag_file 这些基础信息读出来,存到 m_topology 对象里
  • 除了 version,其他几个字段是必须的,不存在就直接退出解析

3. 调用其他管理器,解析子配置

// 1.1 解析板级协议数据:用BoardDataTagMgr,解析datatag_file里的协议配置
BoardDataTagMgr* board_tag_mgr = BoardDataTagMgr::get_instance();
board_tag_mgr->setFileName(m_topology.datatag_file);

// 1.2 解析升级信息:调用UpgradeTopologyMgr解析升级配置
UpgradeTopologyMgr* upgrade_mgr = UpgradeTopologyMgr::get_instance();
upgrade_mgr->parse_upgrade_config();

// 1.3 解析版本查询信息:调用VersionInfoMgr解析系统版本配置
VersionInfoMgr* version_mgr = VersionInfoMgr::get_instance();
version_mgr->parse_system_version_config();
  • 这里用到了单例模式get_instance() 拿到每个管理器的唯一实例
  • 把前面读到的 datatag_file 传给协议管理器,让它继续解析协议配置
  • 升级、版本这些模块也有自己的解析函数,各自处理自己的配置

4. 解析JSON里的数组配置

// 2. 解析device_config数组(设备配置)
cjson_object device_config_arr = js_root["device_config"];
int device_config_num = device_config_arr.get_array_size();
xbasic::debug_output("... device_config num=%d\n", device_config_num);
parse_device_config_info(device_config_arr, device_config_num);

// 3. 解析communication_config数组(通信配置)
cjson_object communication_config = js_root["communication"];
int communication_config_num = communication_config.get_array_size();
xbasic::debug_output("... communication num=%d\n", communication_config_num);
parse_communication_config_info(communication_config, communication_config_num);

// 4. 解析devices数组(设备列表)
cjson_object devices_arr = js_root["devices"];
int devices_num = devices_arr.get_array_size();
xbasic::debug_output("... devices num=%d\n", devices_num);
parse_devices_info(devices_arr, devices_num);
  • 从JSON里读取 device_configcommunicationdevices 这几个数组
  • 分别调用对应的解析函数,把数组里的每一项解析出来,存到对应的配置里
  • 比如 parse_devices_info 会遍历 devices 数组,把每个设备的配置存到内存里

三、整体“流程图”

parse_device_topology()
├─ 1. 初始化m_topology,重置数据
├─ 2. 读入device_topology.json文件
│  └─ 读失败 → 直接返回
├─ 3. 解析JSON根节点基础字段
│  ├─ version(可选)
│  ├─ product_type(必须)
│  ├─ device_name(必须)
│  └─ datatag_file(必须)
├─ 4. 调用其他管理器解析子配置
│  ├─ BoardDataTagMgr(解析协议文件)
│  ├─ UpgradeTopologyMgr(解析升级配置)
│  └─ VersionInfoMgr(解析版本配置)
├─ 5. 解析JSON里的数组配置
│  ├─ device_config数组 → parse_device_config_info
│  ├─ communication数组 → parse_communication_config_info
│  └─ devices数组 → parse_devices_info
└─ 解析完成,所有配置都存到内存里了

四、关键设计思想

  1. 模块化拆分:把不同类型的配置(设备、通信、协议、升级、版本)交给不同的管理器处理,每个管理器只负责自己的部分,职责清晰
  2. 分阶段解析:先读基础配置,再读子配置,最后读数组配置,依赖关系明确(比如协议管理器需要先拿到 datatag_file 路径)
  3. 错误处理:关键配置不存在就直接返回,避免后续解析出现更严重的错误
  4. 日志辅助:每个关键步骤都打日志,方便排查问题(比如配置文件不存在、数组大小是0等)

五、举个直观的JSON结构例子

你可以想象 device_topology.json 大概长这样:

{
    "version": "1.0.0",
    "product_type": "industrial_controller",
    "device_name": "main_controller",
    "datatag_file": "board_tag.json",
    "device_config": [
        {"id": 1, "name": "sensor1", "type": "input"},
        {"id": 2, "name": "relay1", "type": "output"}
    ],
    "communication": [
        {"type": "serial", "baudrate": 9600},
        {"type": "ethernet", "ip": "192.168.1.100"}
    ],
    "devices": [
        {"id": 1, "name": "temperature_sensor", "address": 0x01},
        {"id": 2, "name": "pressure_sensor", "address": 0x02}
    ]
}

这段代码就是把这个JSON里的所有信息,全部解析到程序的内存对象里,供后面的业务逻辑使用。

更多推荐