全局点表
点表作用
每一个网关中都会有配置点表,它会在里面写入一些网关运行过程中所需要的配置项,比如连接的服务器地址、当前固件版本号等。而点表最重要的作用就是定义网关需要采集或者处理的设备点抽象,实际开发中我们会将每个需要关注的终端设备抽象成一个具体的数据点,这个点里通常包含key:代表终端设备的唯一键值
、name:当前点代表的设备
、type:当前设备的数据类型,比如温度传感器是浮点型的,灯是bool类型的
,其它的成员可能略有不同,比如modbus设备会有具体的modbus协议地址。点表不是一成不变的,因为网关是个通用品,很可能对下采集的设备会发生改变。所以程序设计时候必须根据实际情况来,直接在程序中写死需要处理的数据点不具备扩展性。如果不考虑扩展性,把设备定死也是可以做的,根据项目自行决定即可。
点表使用
真实物联网场景中,点表通过客户端界面(上位机或者web页面)编辑,生成json文件后下发给设备,设备解析使用。下发过程详见网关开发指导--设备搜索响应进程
,注意网关设备端进程在调试过程中,可以先固定一个目录:比如/mnt/config
,把点表固定放到此目录,所有进程共享。在搜索响应进程
获取点表之前,其它模块调试时候可以先提前放一个到目录使用,最后再联调即可。
{
"version": "v1.0", //协议版本,方便后续升级
"report": { //设备上报属性
"type": 1, //0-不上报,客户端主动采集;1-变化上报,即连续2次值不相等;2-周期上报
"period": 5 //上报周期时间,单位秒,仅在type=2时有效
},
"mqtt_server": { //mqtt服务器配置
"addr": "192.168.x.x",
"port": 1883
},
"mb_dev": { //modbus设备地址配置
"addr": "192.168.x.x",
"port": 502
},
"mb_app": { //手机IP地址
"addr": "192.168.xx.xx",
"port": 8887
},
"stm32":{
"data": [{
"key": 301, //第一个点比较特殊,根据自己组实际传感器的实际情况来,可以修改
"name": "sensor", //数据点名称(确保单个设备内的唯一性)
"type": 3 //数据点值的类型,1:bool类型 2:int型 3:float型
},
{
"key": 302,
"name": "bat", //电池电量
"type": 3
},
{
"key": 303,
"name": "light", //电灯
"type": 1
}
]
},
"m0": {
"data": [{
"key": 1, //数据点唯一标识(确保数据点表内的唯一性)
"name": "temperature", //数据点名称(确保单个设备内的唯一性)
"type": 3 //数据点值的类型,1:bool类型 2:int型 3:float型
},
{
"key": 2,
"name": "humidity",
"type": 3
},
{
"key": 3,
"name": "ill",
"type": 2
},
{
"key": 4,
"name": "light",
"type": 1
},
{
"key": 5,
"name": "alarm",
"type": 1
},
{
"key": 6,
"name": "fan",
"type": 2
}
]
},
"modbus": {
"data": [{
"key": 101, //数据点唯一标识(确保数据点表内的唯一性)
"name": "temperature", //数据点名称(确保单个设备内的唯一性)
"addr": 30001, //modbus地址,根据设备类型选择,温度传感器是只读的输入寄存器
"type": 3 //数据点值的类型,1:bool类型 2:int型 3:float型
},
{
"key": 102,
"name": "humidity",
"addr": 30003, //湿度传感器是只读的输入寄存器
"type": 3
},
{
"key": 103,
"name": "air-switch",
"addr": 1, //空调开关是线圈寄存器
"type": 1
},
{
"key": 104,
"name": "air-temp",
"addr": 40001, //空调温度控制,保持寄存器
"type": 3
},
{
"key": 105,
"name": "light",
"addr": 2, //灯控,线圈寄存器
"type": 1
}
]
},
"mbapp": {
"data": [{
"key": 201, //数据点唯一标识(确保数据点表内的唯一性)
"name": "ill", //数据点名称(确保单个设备内的唯一性)
"addr": 40001, //modbus地址,根据设备类型选择,温度传感器是只读的输入寄存器
"type": 2 //数据点值的类型,1:bool类型 2:int型 3:float型
},
{
"key": 202,
"name": "x",
"addr": 40002, //湿度传感器是只读的输入寄存器
"type": 3
},
{
"key": 203,
"name": "y",
"addr": 40003, //空调开关是线圈寄存器
"type": 3
},
{
"key": 204,
"name": "z",
"addr": 40004, //空调温度控制,保持寄存器
"type": 3
},
{
"key": 205,
"name": "light",
"addr": 1, //灯控,线圈寄存器
"type": 1
},
{
"key": 206,
"name": "alarm",
"addr": 2, //报警
"type": 1
}
]
}
}
下文拓展性的实现方法,如果不使用python的话,可以直接看实现方法二:基于QT实现
拓展性实现
一、数据库存储配置点表
数据库选型与设计
- 可以选择关系型数据库(如 MySQL、PostgreSQL)或者非关系型数据库(如 MongoDB)来存储配置点表。如果选择关系型数据库,可以创建一个名为`gateway_configuration`的表,包含字段如`id`(自增主键)、`key`(终端设备唯一键值)、`name`(设备名称)、`type`(数据类型)、`server_address`(连接的服务器地址)、`firmware_version`(固件版本号)等。对于有特殊协议要求的设备,如 Modbus 设备,可以添加`modbus_address`字段。
- 例如,在 MySQL 中创建表的 SQL 语句如下:
CREATE TABLE gateway_configuration (
id INT AUTO_INCREMENT PRIMARY KEY,
key VARCHAR(255) NOT NULL,
name VARCHAR(255),
type VARCHAR(50),
server_address VARCHAR(255),
firmware_version VARCHAR(50),
modbus_address VARCHAR(50)
);
数据访问层实现
- 设计数据访问层(Data Access Layer,DAL)来操作数据库。可以使用编程语言对应的数据库驱动或者 ORM(Object - Relational Mapping)框架。例如,在 Python 中使用 SQLAlchemy,定义一个`GatewayConfiguration`类来映射数据库表。
from sqlalchemy import Column, String, Integer
from sqlalchemy.ext.declarative import declarative_base
Base = declarative_base()
class GatewayConfiguration(Base):
__tablename__ = 'gateway_configuration'
id = Column(Integer, primary_key=True)
key = Column(String)
name = Column(String)
type = Column(String)
server_address = Column(String)
firmware_version = Column(String)
modbus_address = Column(String)
- 然后可以通过这个类来进行数据的增删改查操作,如添加新的配置点:
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker
engine = create_engine('mysql+pymysql://user:password@localhost:3306/database_name')
Session = sessionmaker(bind=engine)
session = Session()
new_config = GatewayConfiguration(key='device1', name='Temperature Sensor', type='float', server_address='192.168.1.100', firmware_version='1.0', modbus_address='')
session.add(new_config)
session.commit()
动态加载配置
- 在网关启动时,通过数据访问层从数据库中读取配置点表的数据,并将其加载到内存中的数据结构(如字典或列表)中。例如,在 Python 中:
def load_configuration():
configuration_list = []
configs = session.query(GatewayConfiguration).all()
for config in configs:
config_dict = {
'key': config.key,
'name': config.name,
'type': config.type,
'server_address': config.server_address,
'firmware_version': config.firmware_version,
'modbus_address': config.modbus_address
}
configuration_list.append(config_dict)
return configuration_list
configuration_data = load_configuration()
- 这样,当采集设备发生变化时,只需要修改数据库中的记录,网关下次启动或重新加载配置时就可以获取新的配置信息。
二、配置文件存储配置点表(如 JSON 或 XML 格式)
配置文件格式选择与设计
- **JSON 格式**:JSON 文件易于阅读和编写,也方便在不同编程语言之间进行解析。例如,创建一个`gateway_configuration.json`文件,其结构如下:
{
"configurations": [
{
"key": "device1",
"name": "Temperature Sensor",
"type": "float",
"server_address": "192.168.1.100",
"firmware_version": "1.0",
"modbus_address": ""
},
{
"key": "device2",
"name": "Light Switch",
"type": "bool",
"server_address": "192.168.1.100",
"firmware_version": "1.0",
"modbus_address": ""
}
]
}
- XML 格式:XML 格式更加灵活,支持复杂的结构和验证机制。例如,一个类似的
gateway_configuration.xml
文件可以这样设计:
<?xml version="1.0" encoding="UTF-8"?>
<gateway_configurations>
<configuration>
<key>device1</key>
<name>Temperature Sensor</name>
<type>float</type>
<server_address>192.168.1.100</server_address>
<firmware_version>1.0</firmware_version>
<modbus_address></modbus_address>
</configuration>
<configuration>
<key>device2</key>
<name>Light Switch</name>
<type>bool</type>
<server_address>192.168.1.100</server_address>
<firmware_version>1.0</firmware_version>
<modbus_address></modbus_address>
</configuration>
</gateway_configurations>
配置文件解析与加载
- **JSON 解析(以 Python 为例)**:
import json
with open('gateway_configuration.json', 'r') as file:
configuration_data = json.load(file)['configurations']
- XML 解析(以 Python 为例):可以使用
xml.etree.ElementTree
模块来解析 XML 文件。
import xml.etree.ElementTree as ET
tree = ET.parse('gateway_configuration.xml')
root = tree.getroot()
configuration_data = []
for config in root.findall('configuration'):
config_dict = {}
for child in config:
config_dict[child.tag] = child.text
configuration_data.append(config_dict)
动态更新配置文件
- 当采集设备发生变化时,可以通过程序来更新配置文件。例如,在 Python 中,要向 JSON 配置文件中添加一个新的配置项,可以这样做:
new_config = {
"key": "device3",
"name": "Humidity Sensor",
"type": "float",
"server_address": "192.168.1.100",
"firmware_version": "1.0",
"modbus_address": ""
}
with open('gateway_configuration.json', 'r') as file:
configuration_data = json.load(file)
configuration_data['configurations'].append(new_config)
with open('gateway_configuration.json', 'w') as file:
json.dump(configuration_data, file, indent=4)
- 对于 XML 配置文件,更新操作会相对复杂一些,需要创建新的 XML 元素并插入到合适的位置。
三、使用配置管理服务(适用于大型分布式系统)
配置管理服务选型与集成
- 可以选择开源的配置管理工具,如 Apollo、Nacos 等。以 Apollo 为例,首先需要在服务器端部署 Apollo 服务,包括配置中心和管理控制台。
- 在网关项目中,集成 Apollo 客户端库。例如,在 Java 项目中,添加 Apollo 客户端的依赖,然后在配置文件(如`application.properties`)中配置 Apollo 服务器的地址和应用相关的信息。
app.id=gateway - application
apollo.meta=http://apollo - server - address:8080
- 在代码中,通过 Apollo 客户端 API 来获取配置点表的信息。例如:
import com.ctrip.framework.apollo.Config;
import com.ctrip.framework.apollo.ConfigService;
public class GatewayConfigurationLoader {
public static void main(String[] args) {
Config config = ConfigService.getAppConfig();
String key = config.getProperty("device.key", "default_key");
String name = config.getProperty("device.name", "default_name");
// 其他配置项获取类似
}
}
动态更新与推送机制
- Apollo 配置管理服务支持配置的实时更新和推送。当在管理控制台修改配置点表后,Apollo 会自动将更新后的配置推送给所有连接的网关客户端。网关客户端可以通过实现配置更新监听器来接收并处理配置更新事件。例如,在 Java 中:
import com.ctrip.framework.apollo.Config;
import com.ctrip.framework.apollo.model.ConfigChange;
import com.ctrip.framework.apollo.model.ConfigChangeEvent;
import com.ctrip.framework.apollo.spring.annotation.ApolloConfigChangeListener;
import org.springframework.stereotype.Component;
@Component
public class GatewayConfigurationChangeListener {
@ApolloConfigChangeListener
private void onChange(ConfigChangeEvent changeEvent) {
Config config = changeEvent.getConfig();
for (String key : changeEvent.changedKeys()) {
ConfigChange change = changeEvent.getChange(key);
System.out.println("配置项 " + key + " 从 " + change.getOldValue() + " 变更为 " + change.getNewValue());
// 根据配置项的变更进行相应的处理,如重新加载配置或更新运行时状态
}
}
}
高可用与分布式考虑
- 配置管理服务本身需要考虑高可用性和分布式部署。例如,Apollo 服务可以通过多副本部署、数据备份等方式来确保服务的可靠性。同时,网关客户端在连接配置管理服务时,也可以配置重试机制和连接池等,以应对网络故障或服务暂时不可用的情况。
- 使用 Qt 实现网关配置点表扩展性的完整流程及代码示例。
拓展性实现二(QT)
一、使用数据库存储配置点表(以 SQLite 为例)
数据库创建与连接
在 Qt 项目中,可以使用 Qt 的 SQL 模块来连接和操作数据库。首先创建一个数据库连接:
#include <QCoreApplication>
#include <QSqlDatabase>
#include <QSqlError>
#include <QSqlQuery>
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
QSqlDatabase database = QSqlDatabase::addDatabase("QSQLITE");
database.setDatabaseName("gateway_configuration.db");
if (!database.open()) {
qDebug() << "Error opening database:" << database.lastError().text();
return -1;
}
// 创建表(如果表不存在)
QSqlQuery query;
query.exec("CREATE TABLE IF NOT EXISTS gateway_configuration ("
"id INTEGER PRIMARY KEY AUTOINCREMENT,"
"key TEXT,"
"name TEXT,"
"type TEXT,"
"server_address TEXT,"
"firmware_version TEXT,"
"modbus_address TEXT"
")");
// 后续操作...
return a.exec();
}
数据访问函数
添加、查询、更新和删除数据的函数:
// 添加新的配置项
bool addConfiguration(const QString &key, const QString &name, const QString &type, const QString &serverAddress, const QString &firmwareVersion, const QString &modbusAddress) {
QSqlQuery query;
query.prepare("INSERT INTO gateway_configuration (key, name, type, server_address, firmware_version, modbus_address) "
"VALUES (:key, :name, :type, :server_address, :firmware_version, :modbus_address)");
query.bindValue(":key", key);
query.bindValue(":name", name);
query.bindValue(":type", type);
query.bindValue(":server_address", serverAddress);
query.bindValue(":firmware_version", firmwareVersion);
query.bindValue(":modbus_address", modbusAddress);
return query.exec();
}
// 查询所有配置项
QList<QMap<QString, QString>> getConfigurations() {
QList<QMap<QString, QString>> configurations;
QSqlQuery query("SELECT * FROM gateway_configuration");
while (query.next()) {
QMap<QString, QString> config;
config["id"] = query.value(0).toString();
config["key"] = query.value(1).toString();
config["name"] = query.value(2).toString();
config["type"] = query.value(3).toString();
config["server_address"] = query.value(4).toString();
config["firmware_version"] = query.value(5).toString();
config["modbus_address"] = query.value(6).toString();
configurations.append(config);
}
return configurations;
}
// 根据 ID 查询配置项
QMap<QString, QString> getConfigurationById(int id) {
QMap<QString, QString> config;
QSqlQuery query(QString("SELECT * FROM gateway_configuration WHERE id = %1").arg(id));
if (query.next()) {
config["id"] = query.value(0).toString();
config["key"] = query.value(1).toString();
config["name"] = query.value(2).toString();
config["type"] = query.value(3).toString();
config["server_address"] = query.value(4).toString();
config["firmware_version"] = query.value(5).toString();
config["modbus_address"] = query.value(6).toString();
}
return config;
}
// 更新配置项
bool updateConfiguration(int id, const QString &key, const QString &name, const QString &type, const QString &serverAddress, const QString &firmwareVersion, const QString &modbusAddress) {
QSqlQuery query;
query.prepare("UPDATE gateway_configuration SET key = :key, name = :name, type = :type, server_address = :server_address, firmware_version = :firmware_version, modbus_address = :modbus_address WHERE id = :id");
query.bindValue(":id", id);
query.bindValue(":key", key);
query.bindValue(":name", name);
query.bindValue(":type", type);
query.bindValue(":server_address", serverAddress);
query.bindValue(":firmware_version", firmwareVersion);
query.bindValue(":modbus_address", modbusAddress);
return query.exec();
}
// 删除配置项
bool deleteConfiguration(int id) {
QSqlQuery query;
query.prepare("DELETE FROM gateway_configuration WHERE id = :id");
query.bindValue(":id", id);
return query.exec();
}
使用示例
在程序中使用这些函数来添加、查询、更新和删除配置项:
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
// 添加新配置项
addConfiguration("device1", "Temperature Sensor", "float", "192.168.1.100", "1.0", "");
addConfiguration("device2", "Light Switch", "bool", "192.168.1.100", "1.0", "");
// 查询所有配置项
QList<QMap<QString, QString>> configurations = getConfigurations();
for (const QMap<QString, QString> &config : configurations) {
qDebug() << "Key:" << config["key"] << "Name:" << config["name"] << "Type:" << config["type"];
}
// 根据 ID 查询配置项
QMap<QString, QString> configById = getConfigurationById(1);
if (!configById.isEmpty()) {
qDebug() << "Configuration by ID:" << configById["name"];
}
// 更新配置项
updateConfiguration(1, "device1", "Updated Temperature Sensor", "float", "192.168.1.100", "1.1", "");
// 删除配置项
deleteConfiguration(2);
return a.exec();
}
二、使用配置文件存储配置点表(以 JSON 格式为例)
读写 JSON 文件的函数
#include <QCoreApplication>
#include <QFile>
#include <QJsonDocument>
#include <QJsonObject>
#include <QJsonArray>
// 读取 JSON 配置文件
QJsonDocument readJsonFile(const QString &fileName) {
QFile file(fileName);
if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
qDebug() << "Error opening file:" << file.errorString();
return QJsonDocument();
}
QString jsonData = file.readAll();
file.close();
return QJsonDocument::fromJson(jsonData.toUtf8());
}
// 写入 JSON 配置文件
bool writeJsonFile(const QString &fileName, const QJsonDocument &jsonDoc) {
QFile file(fileName);
if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) {
qDebug() << "Error opening file for writing:" << file.errorString();
return false;
}
file.write(jsonDoc.toJson());
file.close();
return true;
}
操作配置数据的函数
// 添加新的配置项到 JSON 数据
void addConfigurationToJson(QJsonArray &configurations, const QString &key, const QString &name, const QString &type, const QString &serverAddress, const QString &firmwareVersion, const QString &modbusAddress) {
QJsonObject config;
config["key"] = key;
config["name"] = name;
config["type"] = type;
config["server_address"] = serverAddress;
config["firmware_version"] = firmwareVersion;
config["modbus_address"] = modbusAddress;
configurations.append(config);
}
// 根据键值查找配置项
QJsonObject findConfigurationByKey(const QJsonArray &configurations, const QString &key) {
for (const QJsonValue &value : configurations) {
QJsonObject config = value.toObject();
if (config["key"].toString() == key) {
return config;
}
}
return QJsonObject();
}
// 更新配置项
bool updateConfigurationInJson(QJsonArray &configurations, const QString &key, const QString &name, const QString &type, const QString &serverAddress, const QString &firmwareVersion, const QString &modbusAddress) {
for (QJsonValueRef value : configurations) {
QJsonObject config = value.toObject();
if (config["key"].toString() == key) {
config["name"] = name;
config["type"] = type;
config["server_address"] = serverAddress;
config["firmware_version"] = firmwareVersion;
config["modbus_address"] = modbusAddress;
return true;
}
}
return false;
}
// 删除配置项
bool deleteConfigurationFromJson(QJsonArray &configurations, const QString &key) {
QJsonArray newConfigurations;
bool found = false;
for (const QJsonValue &value : configurations) {
QJsonObject config = value.toObject();
if (config["key"].toString()!= key) {
newConfigurations.append(config);
} else {
found = true;
}
}
configurations = newConfigurations;
return found;
}
使用示例
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
// 读取 JSON 配置文件
QJsonDocument jsonDoc = readJsonFile("gateway_configuration.json");
QJsonArray configurations = jsonDoc.object()["configurations"].toArray();
// 添加新配置项
addConfigurationToJson(configurations, "device1", "Temperature Sensor", "float", "192.168.1.100", "1.0", "");
writeJsonFile("gateway_configuration.json", QJsonDocument(QJsonObject{{"configurations", configurations}}));
// 查找配置项
QJsonObject foundConfig = findConfigurationByKey(configurations, "device1");
if (!foundConfig.isEmpty()) {
qDebug() << "Found configuration:" << foundConfig["name"].toString();
}
// 更新配置项
updateConfigurationInJson(configurations, "device1", "Updated Temperature Sensor", "float", "192.168.1.100", "1.1", "");
writeJsonFile("gateway_configuration.json", QJsonDocument(QJsonObject{{"configurations", configurations}}));
// 删除配置项
bool deleted = deleteConfigurationFromJson(configurations, "device1");
if (deleted) {
qDebug() << "Configuration deleted successfully.";
writeJsonFile("gateway_configuration.json", QJsonDocument(QJsonObject{{"configurations", configurations}}));
}
return a.exec();
}
通过以上两种方法,可以在 Qt 项目中实现网关配置点表的扩展性,根据实际需求选择合适的存储方式。如果需要更复杂的配置管理功能,可以结合 Qt 的信号与槽机制、界面设计等,构建更加完善的网关配置管理系统。
标签:点表,网关,name,type,json,key,address,query,config From: https://blog.csdn.net/shenzhou17/article/details/143225383