1 配置管理
正常的应用程序在启动时通常需要从配置文件中恢复配置数据。这些配置文件的格式有很多种,如 INI
、XML
、JSON
等。在本文中,我们将重点介绍 JSON 格式,它是一种轻量级的数据交换格式。
1.1 JSON 格式
JSON (JavaScript Object Notation) 是一种轻量级的数据交换格式,易于人阅读和编写,也易于机器解析和生成。它主要用于在服务器和 Web 应用之间传递数据,但也广泛应用于存储配置文件、数据库的数据交换等场景。
1.1.1 JSON 格式基本规则
- 数据由键值对组成:每个键(key)都是一个字符串,后面跟着一个冒号
:
和对应的值(value)。 - 键值对之间用逗号分隔。
- 键必须是字符串,而值可以是:
- 字符串(必须用双引号
""
包裹) - 数字(整数或浮动)
- 布尔值(
true
或false
) - 数组(由方括号
[]
包裹) - 对象(由花括号
{}
包裹) null
- 字符串(必须用双引号
- 对象和数组可以嵌套:JSON 允许对象和数组互相嵌套,方便表示复杂的数据结构。
1.1.2 JSON 格式示例
以下是一个配置文件的 JSON 格式示例,该配置文件保存了一些系统设置,如串口波特率、杠杆臂参数、NTRIP 配置等。
{ "com_baudrate": { "com1": "9600", "com2": "115200", "com3": "4800" }, "lever_arm_gps": { "x": "0.1", "y": "0.2", "z": "0.3" }, "lever_arm_lcp": { "x": "0.4", "y": "0.5", "z": "0.6" }, "ntrip": { "ip": "192.168.1.1", "port": "2101", "username": "user", "password": "password", "mount_point": "mountpoint" }, "output": { "GPFPD_BIN": true, "GTIMU_BIN": false, "TEST_BIN_1": true }, "storage_path": "/path/to/storage", "decimal": "2" }
复制
1.1.3 python中JSON 解析和生成
在python中,可以使用 json
模块来解析和生成 JSON 格式的数据。
这里是存储json的代码,key-value形式存储到文件中,可以当做是C语言中的数组或者是结构体。
import json def save_config_to_file(self): """保存配置到 JSON 文件""" config = { "com_baudrate": { "com1": self.com1_baudrate_combo.currentText(), "com2": self.com2_baudrate_combo.currentText(), "com3": self.com3_baudrate_combo.currentText() }, "lever_arm_gps": { "x": self.gps_lever_arm_x.text(), "y": self.gps_lever_arm_y.text(), "z": self.gps_lever_arm_z.text() }, "lever_arm_lcp": { "x": self.lcp_lever_arm_x.text(), "y": self.lcp_lever_arm_y.text(), "z": self.lcp_lever_arm_z.text() }, "ntrip": { "ip": self.ntrip_ip.text(), "port": self.ntrip_port.text(), "username": self.ntrip_user.text(), "password": self.ntrip_password.text(), "mount_point": self.ntrip_mountpoint.text() }, "output": { "GPFPD_BIN": self.gpfpd_checkbox.isChecked(), "GTIMU_BIN": self.gtimu_checkbox.isChecked(), "TEST_BIN_1": self.testbin1_checkbox.isChecked() }, "storage_path": self.file_path_display.text(), # 保存用户选择的存储路径 "decimal":self.decimal_input.text() } try: print(f"Configuration file path: {CONFIG_FILE}") with open(CONFIG_FILE, "w") as file: json.dump(config, file, indent=4) if os.path.exists(CONFIG_FILE): print("Configuration file exists.") else: print("Configuration file not found.") with open(CONFIG_FILE, "r") as file: print(file.read()) except Exception as e: QMessageBox.critical(self, "Error", f"Failed to save configuration: {e}")
复制
这里是加载的代码,可以直接从文件中读取配置,并恢复到界面上。访问类似于C语言中的数组格式,config[“com_baudrate”][“com1”] 这样的形式来获取配置。
def load_config_from_file(self): """从 JSON 文件加载配置""" if not os.path.exists(CONFIG_FILE): return # 如果配置文件不存在,跳过加载 try: with open(CONFIG_FILE, "r") as file: config = json.load(file) # 恢复串口波特率 self.com1_baudrate_combo.setCurrentText(config["com_baudrate"]["com1"]) self.com2_baudrate_combo.setCurrentText(config["com_baudrate"]["com2"]) self.com3_baudrate_combo.setCurrentText(config["com_baudrate"]["com3"]) # 恢复杠杆臂参数 self.gps_lever_arm_x.setText(config["lever_arm_gps"]["x"]) self.gps_lever_arm_y.setText(config["lever_arm_gps"]["y"]) self.gps_lever_arm_z.setText(config["lever_arm_gps"]["z"]) self.lcp_lever_arm_x.setText(config["lever_arm_lcp"]["x"]) self.lcp_lever_arm_y.setText(config["lever_arm_lcp"]["y"]) self.lcp_lever_arm_z.setText(config["lever_arm_lcp"]["z"]) # 恢复 NTRIP 配置 self.ntrip_ip.setText(config["ntrip"]["ip"]) self.ntrip_port.setText(config["ntrip"]["port"]) self.ntrip_user.setText(config["ntrip"]["username"]) self.ntrip_password.setText(config["ntrip"]["password"]) self.ntrip_mountpoint.setText(config["ntrip"]["mount_point"]) # 恢复输出设置 self.gpfpd_checkbox.setChecked(config["output"]["GPFPD_BIN"]) self.gtimu_checkbox.setChecked(config["output"]["GTIMU_BIN"]) self.testbin1_checkbox.setChecked(config["output"]["TEST_BIN_1"]) # 恢复存储路径 if "storage_path" in config: self.file_path_display.setText(config["storage_path"]) if "decimal" in config: self.decimal_input.setText(config["decimal"]) print("Configuration loaded successfully.") except Exception as e: QMessageBox.critical(self, "Error", f"Failed to load configuration: {e}")
复制
1.1.4 c++中JSON 解析和生成
C++中也有直接的模块,本次示例为QT中对于JSON格式的示例,主要是使用QJsonDocument和QJsonObject来解析和生成JSON数据。
对于modbus寄存器与地址来说,使用json格式存储到文件中,方便读取和修改。
#include <QJsonDocument> #include <QJsonArray> #include <QJsonObject> void ConfigManager::saveModbusRegistersToJson(const QVector<QPair<int, QString>> ®isters) { // 创建一个 JSON 数组,用来存放所有 Modbus 寄存器数据 QJsonArray jsonArray; // 遍历传入的寄存器列表,将每个寄存器的数据转换为 JSON 对象,并添加到 JSON 数组中 for (const auto ® : registers) { QJsonObject jsonObj; // 将寄存器的地址(整数类型)和名称(字符串类型)添加到 JSON 对象中 jsonObj["address"] = reg.first; // 寄存器的地址 jsonObj["name"] = reg.second; // 寄存器的名称 // 将这个 JSON 对象添加到 JSON 数组中 jsonArray.append(jsonObj); } // 创建根 JSON 对象 QJsonObject rootObj; rootObj["ModbusRegisters"] = jsonArray; // 创建 QJsonDocument 对象来保存 JSON 数据 QJsonDocument jsonDoc(rootObj); QFile file("modbus_registers.json"); if (file.open(QIODevice::WriteOnly | QIODevice::Text)) { file.write(jsonDoc.toJson()); file.close();成功 } else { qDebug() << "Failed to save Modbus registers to JSON file."; } }
复制
这里是加载的代码,和上面的代码类似于组包与解包的过程,将json数据转换为QVector<QPair<int, QString>>类型。
QVector<QPair<int, QString>> ConfigManager::loadModbusRegistersFromJson() { QVector<QPair<int, QString>> registers; QFile file("modbus_registers.json"); if (!file.exists()) { qDebug() << "JSON file not found, returning empty register list."; return registers; } if (file.open(QIODevice::ReadOnly | QIODevice::Text)) { QByteArray jsonData = file.readAll(); file.close(); // 创建 QJsonDocument 对象来解析 JSON 数据 QJsonDocument jsonDoc = QJsonDocument::fromJson(jsonData); if (jsonDoc.isObject()) { // 获取根 JSON 对象 QJsonObject rootObj = jsonDoc.object(); // 获取 Modbus 寄存器数组 if (rootObj.contains("ModbusRegisters") && rootObj["ModbusRegisters"].isArray()) { // 遍历 Modbus 寄存器数组,将每个 JSON 对象转换为 QPair<int, QString> 类型,并添加到列表中 QJsonArray jsonArray = rootObj["ModbusRegisters"].toArray(); for (const QJsonValue &value : jsonArray) { if (value.isObject()) { QJsonObject jsonObj = value.toObject(); int address = jsonObj["address"].toInt(); QString name = jsonObj["name"].toString(); registers.append(qMakePair(address, name)); } } } } else { qDebug() << "Invalid JSON format in file."; } } else { qDebug() << "Failed to open JSON file for reading."; } qDebug() << "Modbus registers loaded from JSON file:" << registers; return registers; }
复制
python与c++的json解析和生成的过程基本相同,只是在python中使用json模块,而在c++中则使用QJsonDocument和QJsonObject。相对来说,python的json模块更加简单易用,只需要直接配置好格式,dump和load即可,其余的操作内部会直接实现并不用关心。而c++则需要自己组包和解包,但也更加灵活。
1.1.5 C与JSON
C语言中,有轻量级的cJSON库,可以用来解析和生成JSON数据。还会用C的偏向于单片机,对于内存使用有限制,最好的做法只是解析JSON格式转换成结构体,然后再操作结构体。
1.2 INI 格式
INI (Initialization File) 是 Windows 系统中常用的配置文件格式,它是一种简单的文件格式,易于人阅读和编写,也易于机器解析和生成。它主要用于 Windows 系统的注册表,但也广泛应用于存储配置文件、数据库的数据交换等场景。
pip install some-package -i https://pypi.tuna.tsinghua.edu.cn/simple
复制
1.3 XML 格式
XML (Extensible Markup Language) 是一种标记语言,它是一种标准通用标记语言,由 W3C 组织推荐使用。它是一种结构化的标记语言,易于人阅读和编写,也易于机器解析和生成。它主要用于存储和交换各种数据,如电子邮件、网页、配置数据、元数据等。
在QT中,使用到XML的地方主要是Qt做语言翻译的.ts文件。
JSON相对来说适合做大量数据的转换配置记录
INI相对来说适合做一些简单的配置项记录
XML目前只在ts中使用到,其他地方并未使用