C++/Qt 模拟sensornetwork的工作

C++/Qt 可视化模拟sensornetwork的工作

C++/Qt 模拟sensornetwork的工作

内容简介

Sensornetwork是一种新型的网络,其基本结构如下图所示:该网络由两部分组成,Sensornode集和DataCollector。Sensornode(可简称为Sensor)能够完成感知环境数据并将其发往DataCollector的功能。DataCollector完成Sensor采集数据的收集,它就是一台带有无线接收功能的计算机。

本课程设计以Qt为框架实现了对sensornetwork的工作模拟,包括传感器节点的随机生成,传感器的数据模拟产生,节点网络的构建与图形化展示,节点最短路径的查找等,采用迪杰斯特拉算法作为路由算法,基本完整模拟了sensornetwork的工作流程,拥有良好的交互界面,包括查找定位,数据修改,规则制定等。

(一) 需求和规格说明

(1)问题描述

Sensornetwork是一种新型的网络,其基本结构如下图所示:该网络由两部分组成,Sensornode集和DataCollector。Sensornode(可简称为Sensor)能够完成感知环境数据并将其发往DataCollector的功能。DataCollector完成Sensor采集数据的收集,它就是一台带有无线接收功能的计算机。

Sensornetwork可应用到很多实际领域中,如在战争中将Sensor散播在防线的前沿,可以收集敌人的一些情报(如大规模的部队转移等)。Sensor散播的地方称为Interestedarea,Sensor在这个区域内采集各自所在位置的数据,然后将采集到的数据传送到DataCollector。各个Sensor之间通过无线广播通讯,由于Sensor广播能力的限制,它只能和位于自身的一定广播半径内的Sensor进行通讯,所以有些Sensor就需通过其它Sensor,经过多次路由后才能到达DataCollector(如上图)。如何路由由Sensor内保存的简单路由表来决定。DataCollector的位置就在InterstedArea的边缘,且固定不动。

(2)设计目的

应用数据结构知识模拟一个新型网络系统。

(3)基本要求

①应用数据结构在单机上模拟Sensornetwork的工作。

②用VC++(C也可以)实现模拟系统,N个Sensor和1个DataCollector,其具体位置随机确定,InterestArea就是屏幕。N可配置,缺省为100。

③Sensor进行周期性采集,其采集周期可配置。

④Sensor的广播半径固定,也是可配置的参数。

⑤路由算法自行选择或设计。

(4)实现提示

Sensor集可组织成数组,它采集及收到的数据包用队列存储。具体细节也可参阅有关资料。

(二) 设计

(1)框架设计

由上述需求可知,我们需要设计的是一个模拟传感器的网络系统,这个网路系统的主题由图构成,因此应该使用图形界面将图显示,直观的描绘出传感器节点(SensorNode),边(Edge),数据收集器(DataCollector)之间的相互关系,可以用图形化的界面表达出算法的运行。故选用Qt这种跨平台应用程序开发框架作为主体框架,在Qt框架的基础上进行进一步的设计。

DataCollector(数据收集类):
  • 作为传感器网络的核心节点,负责收集和处理传感器节点的数据。
  • 实现与传感器节点之间的通信和数据交换。
  • 具备管理传感器节点的功能,如初始化、数据请求等。
Graph(图类):
  • 负责存储传感器网络的拓扑结构,包括节点和边。
  • 支持基本的图操作,如添加节点、添加边、删除节点、删除边等。
  • 实现图算法,如Dijkstra算法,用于计算从DataCollector到各个传感器节点的最短路径。
SensorNode(传感器节点):
  • 表示传感器网络中的单个传感器节点。
  • 具备基本属性,如位置(x, y坐标)、ID、数据等。
  • 支持与DataCollector和其他传感器节点的通信。
SensorNetwork(网络管理):
  • 负责管理整个传感器网络,包括传感器节点和数据收集器。
  • 维护传感器网络的整体状态,并提供对外接口用于操作网络。
  • 包含数据收集周期、广播半径等可配置参数。
Widget(图形界面):
  • 使用Qt进行界面设计,负责绘制传感器网络的图形表示。
  • 显示传感器节点、数据收集器和边的图形元素。
  • 提供用户交互功能,如添加节点、删除节点、执行算法并显示结果等。

(2)界面设计

(3)类与属性说明

类名 属性 类型/返回值 名称
DataCollector 变量
x double
y double
model QStandardItemModel *
方法
addItem(TData data) void
removeItem(TData data) void
Edge 变量
to int
weight double
type int
desister double
方法
Set(int a) void
Graph 变量
adjList unordered_map<int, std::vector>
方法
addEdge(int from, int to, double weight) void
findEdge(int from, int to) Edge
Data 变量
final bool
dist double
path int
方法
Set(bool f, double d, int p) void
SensorNetwork 变量
sensors QList<SensorNode*>
dataCollector DataCollector *
sensorCount int
broadcastRadius double
T int
graph Graph
multiple int
PathMap unordered_map<int, Data>
方法
getShortestPath(int targetNode) QPair<double, QList>
printPathMap() void
initializeNetwork() void
Dijkstra() void
initEdgeType() void
StartT() void
GetNew() void
GetNewD() void
TData 变量
fromNode int
data int
type QString
currentDateTime QDateTime
方法
toString() QString
SensorNode 变量
a int
x double
y double
type int
broadcastRadius double
dataQueue QQueue
model QStandardItemModel *
方法
receiveData(const TData &data) void
collectData() void
SetType(int a) void
Widget 变量
sensorNetwork SensorNetwork*
timer QTimer*
sensorCount int
broadcastRadius double
T int
T2 int
Judge bool
timer1 QTimer *
timer2 QTimer *
lastClickedSensorIndex int
nowNode int
方法
paintEvent(QPaintEvent *event) void
collect() void
Send() void
mousePressEvent(QMouseEvent *event) void
on_pushButton_clicked() void
on_pushButton_2_clicked() void

(4)重点函数说明:

4.1 最优路径规划---迪杰斯特拉算法实现
cpp 复制代码
void SensorNetwork::Dijkstra() {
    // 初始化路径表
    for (int i = 0; i <= sensorCount; i++) {
        PathMap[i].Set(false, std::numeric_limits<double>::infinity(), -1); // 将距离设为无穷大
    }

    // 放入第一个节点
    PathMap[sensorCount].Set(true, 0, -1);

    // 初始化第一个节点的邻接节点的距离
    for (const Edge& edge : graph.adjList.at(sensorCount)) {
        PathMap[edge.to].Set(false, edge.weight, sensorCount);
    }

    for (int i = 0; i < sensorCount; i++) {
        // 找到未处理节点中距离最短的节点
        int minNode = -1;
        double minDist = std::numeric_limits<double>::infinity();

        for (const auto& entry : PathMap) {
            if (!entry.second.final && entry.second.dist < minDist) {
                minDist = entry.second.dist;
                minNode = entry.first;
            }
        }

        //if (minNode == -1) {
        //    break; // 所有节点已处理完毕
        //}

        PathMap[minNode].final = true; // 将该节点标记为已处理

        // 更新该节点邻接节点的距离
        for (const Edge& edge : graph.adjList[minNode]) {
            if (!PathMap[edge.to].final && PathMap[minNode].dist + edge.weight < PathMap[edge.to].dist) {
                PathMap[edge.to].Set(false, PathMap[minNode].dist + edge.weight, minNode);
            }
        }
    }
}

初始化路径表:

  • 为每个节点创建一个记录,包含三个部分:是否已处理 (final)、距离 (dist)、和前驱节点 (prev)。
  • 使用一个特殊的值(无穷大)初始化所有节点的距离,表示这些节点当前还不可达。
  • 对数据收集器节点(假设为 sensorCount)初始化距离为0,前驱节点为-1,标记为已处理。
  • 初始化第一个节点的邻接节点:
  • 遍历数据收集器节点的邻接节点,并初始化这些节点的距离。
  • 将这些邻接节点的前驱节点设置为数据收集器节点。
  • 主循环(处理所有节点):
  • 找到未处理节点中距离最短的节点 minNode。这是Dijkstra算法的核心部分,确保每次选择的节点是当前已知最短路径的节点。
  • 将该节点标记为已处理。
  • 遍历该节点的所有邻接节点,更新这些邻接节点的距离(如果通过当前节点找到的路径更短)。
  • 更新操作:如果通过 minNode 达到某个邻接节点的距离小于之前记录的距离,则更新该邻接节点的距离,并将其前驱节点更新为 minNode。
4.2 最短路径查找
cpp 复制代码
QPair<double, QList<int>> SensorNetwork::getShortestPath(int targetNode) {
    QList<int> path;
    double totalDistance = PathMap[targetNode].dist;

    // 检查目标节点是否可达
    if (totalDistance == std::numeric_limits<double>::infinity()) {
        return qMakePair(totalDistance, path); // 返回空路径和无穷距离
    }

    // 使用堆栈逆序构建路径
    QStack<int> stack;
    int currentNode = targetNode;

    while (currentNode != -1) {
        stack.push(currentNode);
        currentNode = PathMap[currentNode].path;
    }

    // 将堆栈中的路径顺序放入 QList 中
    while (!stack.isEmpty()) {
        path.append(stack.pop());
    }

    return qMakePair(totalDistance, path);
}

初始化:

  • 创建一个 QList 来存储路径节点。
  • 获取目标节点的总距离 totalDistance。
  • 检查目标节点可达性:
  • 如果目标节点的总距离为无穷大,表示目标节点不可达。此时,返回总距离为无穷大和空路径。
  • 逆序构建路径:
  • 使用 QStack 堆栈存储路径节点,方便之后顺序输出路径。
  • 从目标节点开始,逐步追踪其前驱节点 path,将节点压入堆栈,直到到达数据收集器节点(即 path 为 -1)。
  • 构建有序路径:
  • 从堆栈中弹出所有节点,并按顺序放入 QList 中,形成正确顺序的路径。
  • 返回结果:
  • 返回一个 QPair<double, QList>,包含总距离和节点路径。

(三) 用户手册

开始运行时会按照设定的默认值初始化传感器节点数字为100,广播半径为150,采集周期为5,发送间隔为2。可以在自己设置这些参数后点击配置按钮,及可按照输入的配置信息配置图像,在查找前的输入框中输入查找内容,可以查找到节点并在图形展示区将节点该节点变为红色并显示该节点到收集站的路径,将路径展示为红色,在节点信息栏显示节点的信息并实时更新,显示出当前节点上储存的信息与节点距离与信息多少。在最左边为信息总览,包含所有节点传输到收集站的信息,并可以修改。最左边的滑轮可以设置阻塞比例,调整距离与阻塞对边权重的影响。可以直接点击图中的节点,及定位到此节点。

初始化设置

启动程序时,系统会根据以下默认值进行初始化:

  • 传感器节点数量:100
  • 广播半径:150
  • 采集周期:5
  • 发送间隔:2

参数配置

用户可以在设置这些参数后点击"配置"按钮,系统将根据输入的配置信息重新配置图像。

查找节点

在查找前的输入框中输入节点信息,可以查找到对应节点,并在图形展示区将该节点标记为红色。系统还会显示该节点到数据收集站的路径,路径以红色展示。

节点信息

在节点信息栏中显示节点的详细信息并实时更新,包括:

  • 当前节点上存储的信息
  • 节点与数据收集站的距离
  • 信息的数量

信息总览

界面最左侧显示信息总览,包含所有节点传输到数据收集站的信息。用户可以在此修改信息。

阻塞比例调整

界面左侧的滑轮可以设置阻塞比例,调整距离与阻塞对边权重的影响。

节点定位

用户可以直接点击图中的节点,系统将自动定位到该节点。

(四) 调试及测试

(五) 运行实例:

初始化后,将节点数改为150,采集周期改为10,发送间隔改为1

手动配置:

输入节点106,查找节点位置与最短路径。

查找节点:

修改总数据中的条数据信息。

数据修改:

点击图中节点,自动定位节点与路径。

点击选择节点:

(六)进一步改进

现在的实现有一个问题,当随机产生的节点有节点无法加入网络时,在遍历寻找时会报错导致程序奔溃,在后期优化要将不能加入网络的节点异常处理。

现在的界面有些单调,确实点美感,在后期需要对显示中的图点与边进行优化。

当前缺少直接在总数据中查找数据的功能,对收集到的数据没有处理,在后期可以加入对收集到的数据进行数据分析的功能。

目前实现只是用了迪杰斯特拉算法一种,比较单调,可以尝试是用多种算法实现这个功能。

相关源码:https://github.com/HeJiguang/StudyNotions/tree/main/Another/模拟sensornetwork的工作

相关推荐
明月醉窗台2 分钟前
Qt 入门 1 之第一个程序 Hello World
开发语言·c++·qt
hy____12325 分钟前
类与对象(中)(详解)
开发语言·c++
wen__xvn30 分钟前
c++STL入门
开发语言·c++·算法
the_nov1 小时前
20.IP协议
linux·服务器·网络·c++·tcp/ip
只有月亮知道1 小时前
C++list常用接口和模拟实现
开发语言·c++
勘察加熊人1 小时前
c#和c++脚本解释器科学运算
开发语言·c++·c#
TechNomad2 小时前
Qt进阶开发:QDirModel的使用
qt
zyx没烦恼2 小时前
Linux 下 日志系统搭建全攻略
linux·服务器·开发语言·c++
冰红茶兑滴水3 小时前
Qt 音乐播放器项目
开发语言·qt
MCYH02063 小时前
C++抽卡模拟器
java·c++·算法·概率·原神