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中使用到,其他地方并未使用