基于图扑 HT for Web 实现拓扑关系图

拓扑结构在计算机网络设计和通信领域中非常重要,因为它描述了网络中的设备(即“点”)如何相互连接(即通过“线”)。这种结构不仅涉及物理布局,即物理拓扑,还可以涉及逻辑或虚拟的连接方式,即逻辑拓扑。


图扑软件自研 HT for Web 产品曾参与搭建了众多拓扑可视化解决方案。如机房通信拓扑可视化,实现通过图形图像直观展示机房内部网络设备、服务器、存储设备以及之间连接关系的技术。帮助 IT 管理员和网络工程师更加直观地理解机房的网络结构,便于故障排查、网络优化和规划扩展。
在 HT 中,ht.Node 可充当拓扑图中的“点”角色,ht.Node 上可显示图片图标,这使得创建拓扑图时能够直观地表示每一个“点”的特征。至于拓扑图中的“线”,即用于体现两个节点之间关系的元素,可由 ht.Edge 类型承担这一任务。ht.Edge用于连接起始和目标两个节点,两个节点间可以有多条ht.Edge存在,也允许起始和目标为同一节点。
在搭建 HT 拓扑图前,我们需要先创建一个 HT 的 2D 视图:
const dm = new ht.DataModel(); // 创建一个数据模型const g2d = new ht.graph.GraphView(dm); // 创建一个 2D 视图g2d.addToDOM(); // 将 2D 视图添加到 body 中dm.setBackground('rgb(240,237,237)'); // 设置背景
2D 视图还可按照需求开启树层次渲染:
dm.setHierarchicalRendering(true);
2D 视图创建完成之后就可以创建“点”和“线”了:
const node1 = createNode('symbols/电信/icon_交换机.json', { x: 0, y: 0 }, "交换机");const node2 = createNode('symbols/电信/icon_路由.json', { x: 300, y: 0 }, "路由");createEdge(node1, node2);function createNode(icon, position, name) {const node = new ht.Node(); // 创建一个 ht.Node 节点node.s({'label': name,'label.color': "#fff"});node.setImage(icon);node.p(position);node.setSize({ width: 100, height: 100 });dm.add(node); // 将节点加到数据模型中return node;}function createEdge(source, target, color, reverse) {const edge = new ht.Edge(source, target); // 创建一个 ht.Edgedm.add(edge); // 将连线节点加到数据模型中return edge;}
运行代码后的效果:

复杂连线
以上展示了一个简洁的示例,直观地展现了如何在图扑自研 HT for Web 中创建节点并将它们通过连线相连。实际应用中的情形往往更为复杂,需要根据实际数据构建拓扑结构。在这个过程中,核心步骤依旧是首先创建 ht.Node 实例以表示各个节点,再利用 ht.Edge 实例来实现节点之间的连接。接下来,让我们通过复杂一些的示例来演示。
创建节点
为了批量创建节点并方便管理节点数据,示例中定义了结构化的数据格式,并将数据存储在一个 JSON 文件中,再通过 ht.Default.xhrLoad() 去获取到 JSON 文件中的数据。获取到数据后就可批量创建节点。
在实际的运用场景中,也可以通过任何 web 端通讯方式 HTTP/AJAX、WebSocket 去获取数据。
equipment.json 文件中定义的数据格式:
[{"name": "核心交换机1", // 设备名称"code": "EQ_ASBB1425", // 设备编码(唯一标识)"icon": "symbols/user/900-word/电信拓扑图标/icon_核心交换机.json", // 设备 icon// 设备在图纸中的位置"size": 60, // 节点大小"position": {"x": 0,"y": 100}},{"name": "核心交换机2","code": "EQ_ASBB1478","icon": "symbols/user/900-word/电信拓扑图标/icon_核心交换机.json","position": {"x": 200,"y": 0}},{"name": "服务器1","code": "EQ_BCGJ2121","icon": "symbols/user/900-word/电信拓扑图标/空白服务器.json","position": {"x": 200,"y": 250}},...]
获取到数据并批量创建节点:
ht.Default.xhrLoad('./equipment.json', function (json) {const data = ht.Default.parse(json);data.forEach((item) => {createNode(item);})})function createNode(data) {const node = new ht.Node();node.setTag(data.code); // 设置节点唯一标识node.setImage(data.icon);node.p(data.position);node.s('2d.movable', false); // 禁止移动node.setSize({ width: data.size || 150, height: data.size || 150 });dm.add(node);return node;}

创建连线
与节点数据相同,示例中定义了连线对应格式,并且也是存储在一个 JSON 文件中,再通过 ht.Default.xhrLoad() 获取数据。JSON 文件中定义了连线中最重要的几个因素:起始节点、目标节点、连线颜色。
[{"source": "EQ_ASBB1425", // 起始节点的唯一标识"target": "EQ_BCGJ2121", // 结束节点的唯一标识"color": "rgb(0,199,7)" // 连线颜色},{"source": "EQ_ASBB1425","target": "EQ_BCGJ2131","color": "rgb(0,199,7)"},...]
获取数据并且批量创建连线,这一步需要在创建节点之后执行:
ht.Default.xhrLoad('./connectData.json', function (json) {const connectData = ht.Default.parse(json);connectData.forEach((item) => {createEdge(item);})})function createEdge(data) {const source = dm.getDataByTag(data.source);const target = dm.getDataByTag(data.target);const edge = new ht.Edge(source, target);edge.s({"edge.color": data.color || "rgb(0,199,7)","edge.width": 4,"shadow2.offset.x": -4,"shadow2.offset.y": 7,"shadow2": true,"shadow2.color": "rgba(0,0,0,0.18)",})dm.add(edge);dm.moveToTop(edge); // 将节点移动至顶部return edge;}

到这里,基本上整个拓扑的效果都已经展示出来了,但是可能还存在一些问题。如终端路由之间的连线被服务器挡住了,可能会被认为是路由 1—服务器 1—服务器 2—路由 2 这样的连接。

这种情况下,就可以采用其他的连线方式。ht.Edge 提供了多种的连线方式,可以通过 edge.s(‘edge.type’, 连接方式) 进行设置。下面展示几种不同的连接方式:
1.弯曲:edge.s(‘edge.type’, ‘flex2’)

2.正交:edge.s(‘edge.type’, ‘ortho2’)

3.先水平后垂直:edge.s(‘edge.type’, ‘h.v2’)

4.先水平后垂直:edge.s(‘edge.type’, ‘v.h2’)

......
ht.Edge 还有很多种的连线方式,这里就先介绍以上几种方式。
在这个示例内,两个路由之间的连线需要跨域多个其他的连线,为了使得连线更加美观易懂,于是我就这条连线采用了 points 的连线方式,这种方式有极高的灵活性,可在连线路径上自由地添加控制点,从而实现非常多样化的效果。
points 类型的连线,有两个非常重要的属性:
-
edge.points:控制点信息;
-
edge.segments:用来标识在绘制时如何使用 points 数组中的顶点信息。
将示例中这条连线的连线类型改为 points,并设置上相应的属性:
edge.s({'edge.type': 'points','edge.center': true,'edge.points': [{ "x": 680, "y": 105 },{ "x": 490, "y": 200 },{ "x": 470, "y": 200 },{ "x": 410, "y": 230 },{ "x": 400, "y": 250 },{ "x": 360, "y": 270 },{ "x": 340, "y": 270 },{ "x": 260, "y": 310 },{ "x": 250, "y": 330 },{ "x": 80, "y": 415 }],'edge.segments': [1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2]});
使用以上 points 连线类型实现的效果:

增加背景和装饰
可创建一些 ht.Shape 节点作为背景装饰,突出显示特定的设备区域。
createShape([{ x: -100, y: 130 },{ x: 230, y: -50 },{ x: 340, y: 15 },{ x: 10, y: 195 },{ x: -100, y: 130 }], [1, 2, 2, 2, 5])createShape([{ x: -155, y: 354 },{ x: 575, y: -30 },{ x: 805, y: 110 },{ x: 60, y: 510 },{ x: -155, y: 355 },], [1, 2, 2, 2, 5]);createShape([{ x: 300, y: 470 },{ x: 660, y: 275 },{ x: 805, y: 350 },{ x: 435, y: 550 },{ x: 300, y: 470 },], [1, 2, 2, 2, 5])function createShape(points, segments) {const shape = new ht.Shape();shape.setPoints(points);shape.setSegments(segments);shape.s({'shape.background': "#fff","shape.border.color": "rgba(13,46,79,0.67)","shape.border.width": 0.5,})dm.add(shape);dm.moveToTop(shape);return shape;}
添加背景后的效果如下:

增加一些装饰的节点,这些节点本质上也都是 ht.Node,只是显示了不同的图标/图片,效果如下:

添加箭头
在复杂的网络拓扑中,连线上常常需要表示数据流动方向。在使用图扑 HT 绘制连线时,ht.Edge 提供了 icons 属性,通过 icons 属性,可在 ht.Edge 上定义一系列图标并设置它们在连线上的位置。
在设置 icons 属性前,需要先注册好图标:
ht.Default.setImage('toArrow', {width: 40,height: 20,comps: [{type: 'shape',points: [5, 2, 10, 10, 5, 18, 20, 10],closePath: true,background: 'rgb(0,199,7)',borderWidth: 1,borderColor: 'rgb(0,199,7)',gradient: 'spread.vertical'}]});ht.Default.setImage('fromArrow', {width: 12,height: 12,comps: [{type: 'circle',rect: [1, 1, 10, 10],background: 'rgb(0,199,7)'}]});
在 ht.Edge 上设置 icons:
edge.addStyleIcon("fromArrow", {position: 15, // 图标位置keepOrien: true, // 图标是否默认自动调整方向以保持最好的阅读效果names: ['fromArrow']});edge.addStyleIcon("toArrow", {position: 19,keepOrien: true,names: ['toArrow']});
设置 icons 之后的效果:

流动动画
在图扑自研产品 HT for Web 中,使用 ht-flow.js 插件,能够为 ht.Edge 连线添加流动动画效果。这种效果可用于表示数据传输、能源流动或任何类型的动态连接。使用 ht-flow.js 插件实现的流动效果配置起来也十分简单,正确引入 ht-flow.js 插件后,使用 g2d.enableFlow(60); 开启流动,再在 ht.Edge 上设置相应的流动属性即可。
ht.Edge 配置流动效果的一些属性说明:
-
flow:布尔值,设置为 true 以启用流动效果。
-
flow.count:控制流动组的个数,默认为 1。
-
flow.step:控制流动的步进,默认为 3。
-
flow.element.count: 每个流动组中的元素的个数,默认为 10。
-
flow.element.space: 流动组中元素的间隔,默认为 3.5。
-
flow.element.image: 字符串类型,指定流动组中元素的图片,图片需要提前通过 ht.Default.setImage 注册。目前支持设置。
-
flow.element.background: 流动组中元素的背景颜色,默认为 rgba(255, 255, 114, 0.4)。
-
flow.element.shadow.begincolor: 字符串类型,表示流动组中的元素的渐变阴影的中心颜色,默认为 rgba(255, 255, 0, 0.3)。
-
flow.element.shadow.endcolor: 字符串类型,表示流动组中的元素的渐变阴影的边缘颜色,默认为 rgba(255, 255, 0, 0)。
-
flow.element.shadow.visible:流动阴影是否可见。
-
flow.begin.percent:开始的位置,值为 0 - 1,默认是 0。
-
flow.element.autorotate:是否自动朝向,根据连线的角度自动朝向。
在示例的 ht.Edge 上设置流动属性:
edge.s({"flow": true,"flow.element.background": "rgba(240, 225, 19, 0.5)","flow.element.shadow.begincolor": "rgba(240, 225, 19, 0.5)","flow.element.shadow.endcolor": "rgba(240, 225, 19, 0)","flow.element.count": 1});
设置完成后的效果:

在更为复杂的场景中,仅仅依赖简单的样式配置难以满足设计需求,为此 ht-flow.js 提供了 flow.element.image 属性,该属性支持将流动的元素设置为图片或图标,还支持设置为多个图片/图标流动的效果。
在流动上设置图标,需要先注册图标:
ht.Default.setImage('dataIcon1', {"width": 50,"height": 50,"comps": [{"type": "shape","background": "rgb(125,195,125)","borderColor": "#979797","points": [2.94441,16.1039,26.41008,16.1039,26.41008,4.28571,47.05559,25.58442,27.23783,45.71429,27.23783,33.84863,2.94441,33.84863,2.94441,16.1039]}]})ht.Default.setImage('dataIcon2', {"width": 50,"height": 50,"comps": [{"type": "shape","background": "#32D3EB","borderColor": "#979797","points": [2.94441,16.1039,26.41008,16.1039,26.41008,4.28571,47.05559,25.58442,27.23783,45.71429,27.23783,33.84863,2.94441,33.84863,2.94441,16.1039]}]});在 ht.Edge 上设置属性:edge.s({"flow": true,"flow.element.count": 2,"flow.element.image": ["dataIcon1", "dataIcon2"],"flow.element.max": 20,"flow.element.min": 20,"flow.element.shadow.visible": false,"flow.element.space": 50,"flow.element.autorotate": true});
设置完成后的效果:

拓扑可视化优点
-
直观性:将抽象的关系和数据通过图形呈现,使得人们可以直观地理解和分析系统或网络的结构。
-
互动性:现代拓扑可视化工具通常支持用户与图形的交互操作,如缩放、拖拽节点、探索节点之间的路径等,进一步提升了分析的深度和广度。
-
动态性:能够实时反映系统或网络的变化,及时展现新增元素和调整后的结构关系,对于监控和管理系统状态尤为重要。
-
灵活性:用户可以根据需要选择不同的布局算法,调整图形的展示方式,更好地适应不同的分析场景。
相关文章:
基于图扑 HT for Web 实现拓扑关系图
拓扑结构在计算机网络设计和通信领域中非常重要,因为它描述了网络中的设备(即“点”)如何相互连接(即通过“线”)。这种结构不仅涉及物理布局,即物理拓扑,还可以涉及逻辑或虚拟的连接方式&#…...
linux笔记10--编辑器之神VIM
文章目录 1. 简单介绍① 为什么叫vim② linux常见的编辑器③ 注意事项④ 其它 2. 操作模式的划分① 两种 -- 国际上普通模式(命令操作模式)插入模式 ② 三种 -- 国内普通模式如何进入与退出界面 插入模式如何进入与退出界面 命令模式如何进入与退出界面常见的命令模式 ③ 区别④…...
安全管理中心-集中管控(6点)
记忆内容: 应划分出特定的管理区域,对分布在网络中的安全设备或安全组件进行管控。(三级新增) 应能够建立一条安全的信息传输路径,对网络中的安全设备或安全组件进行管理。(三级新增) 应对网络…...
使用electron打包Vue前端项目的详细流程
使用electron打包Vue前端项目的详细流程 需要更改的东西 路由模式的修改 # 修改前:url不带#mode: history# 修改后:url带#mode: hash全局修改Cookies为localStorage 由于打包成exe或deb这类可执行文件后,本地是没有 Cookies 全局搜索Cooki…...
《计算机英语》 Unit 4 Information Management 信息管理
Section A Information Storage 信息存储 1. The importance of Information信息的重要性 词汇 reside vi属于,驻留 tablet n平板电脑 laptop n笔记本电脑 repository n仓库 claim n索赔 regulatory n法规 contractua…...
如何打包数据库文件
使用 mysqldump 命令: mysqldump -u username -p database_name > output_file.sql username 是数据库的用户名。database_name 是要导出的数据库名称。output_file.sql 是导出的 SQL 文件名,可以自定义。 示例: mysqldump -u root -p…...
iOS抓包指南 正则过滤爬取
解读iOS抓包 抓包 (packet capture)就是将网络传输发送与接收的数据包进行截获、重发、编辑、转存等操作,也用来检查网络安全。抓包也经常被用来进行数据截取等。 什么是正则表达式? 正则表达式(regular expression)是用来描述…...
FLASH仿真EEPROM---基于智芯Z20K11XM
一、介绍 电可擦和可编程只读存储器(EEPROM)可以对字节或字编程和擦除。EEPROM中的数据即使断电也能保持,但Z20K1xx芯片不含EEPROM。然而,闪存可以通过EEPROM仿真软件来模拟EEPROM。Z20K1xx包含两个flash阵列。编程和擦除操作可以在一个数组上进行&#…...
阿里云PAI大模型评测最佳实践
作者:施晨、之用、南茵、求伯、一耘、临在 背景信息 内容简介 在大模型时代,随着模型效果的显著提升,模型评测的重要性日益凸显。科学、高效的模型评测,不仅能帮助开发者有效地衡量和对比不同模型的性能,更能指导他…...
应用图扑 HT for Web 搭建拓扑关系图
拓扑结构在计算机网络设计和通信领域中非常重要,因为它描述了网络中的设备(即“点”)如何相互连接(即通过“线”)。这种结构不仅涉及物理布局,即物理拓扑,还可以涉及逻辑或虚拟的连接方式&#…...
Django Aggregation 使用指南
Django Aggregation 使用指南 在构建Django应用时,我们经常需要对数据库中的数据进行汇总或聚合操作。例如,计算某个字段的平均值、最大值或最小值。这篇文章将详细介绍如何在Django中使用聚合查询,并结合实例进行说明。 聚合查询简介 Dja…...
嵌入式学习——Linux操作系统——文件编程练习
1.使用fread和fwrite方式完成任意普通文件的拷贝功能。 模拟 文件下载 #include <stdio.h>void do_copy(FILE *fp_s,FILE *fp_d) {char buf[100] {0};int ret;while (ret fread(buf,sizeof(char),sizeof(buf),fp_s))fwrite(buf,sizeof(char),ret,fp_d); }//./a.out sr…...
用JavaScript实现了一个简单的图像坐标点标注工具
这段代码实现了一个简单的图像标注工具,允许用户在加载的图像上进行点选标注,并且通过右键确认一个点序列来形成一个多边形。 标注效果如下 实现代码如下 <!DOCTYPE html> <html lang"en"> <head><meta charset"U…...
Pytorch深度解析:Transformer嵌入层源码逐行解读
前言 本部分博客需要先阅读博客: 《Transformer实现以及Pytorch源码解读(一)-数据输入篇》 作为知识储备。 Embedding使用方式 如下面的代码中所示,embedding一般是先实例化nn.Embedding(vocab_size, embedding_dim)。实例化的…...
HSP_10章 Python面向对象编程oop_基础部分
文章目录 P107 类与实例的关系1.类与实例的关系示意图2.类与实例的代码分析 P109 对象形式和传参机制1. 类与对象的区别和联系2. 属性/成员变量3. 类的定义和使用4. 对象的传递机制 P110 对象的布尔值P111 成员方法1. 基本介绍2. 成员方法的定义和基本使用3.注意事项和使用细节…...
JavaWeb系列十七: jQuery选择器 上
jQuery选择器 jQuery基本选择器jquery层次选择器基础过滤选择器内容过滤选择器可见度过滤选择器 选择器是jQuery的核心, 在jQuery中, 对事件处理, 遍历 DOM和Ajax 操作都依赖于选择器jQuery选择器的优点 $(“#id”) 等价于 document.getElementById(“id”);$(“tagName”) 等价…...
Gone框架介绍30 - 使用`goner/gin`提供Web服务
gone是可以高效开发Web服务的Golang依赖注入框架 github地址:https://github.com/gone-io/gone 文档地址:https://goner.fun/zh/ 使用goner/gin提供Web服务 文章目录 使用goner/gin提供Web服务注册相关的Goners编写Controller挂载路由路由处理函数io.Rea…...
Lipowerline5.0 雷达电力应用软件下载使用
1.配网数据处理分析 针对配网线路点云数据,优化了分类算法,支持杆塔、导线、交跨线、建筑物、地面点和其他线路的自动分类;一键生成危险点报告和交跨报告;还能生成点云数据采集航线和自主巡检航线。 获取软件安装包联系邮箱:289…...
STM32学习之一:什么是STM32
目录 1.什么是STM32 2.STM32命名规则 3.STM32外设资源 4. STM32的系统架构 5. 从0到1搭建一个STM32工程 学习stm32已经很久了,因为种种原因,也有很久一段时间没接触过stm32了。等我捡起来的时候,发现很多都已经忘记了,重新捡…...
AI绘画Stable Diffusion 超强一键去除图片中的物体,免费使用!
大家好,我是设计师阿威 在生成图像时总有一些不完美的小瑕疵,比如多余的物体或碍眼的水印,它们破坏了图片的美感。但别担心,今天我们将介绍一款神奇的工具——sd-webui-cleaner,它可以帮助我们使用Stable Diffusion轻…...
服务器硬防的应用场景都有哪些?
服务器硬防是指一种通过硬件设备层面的安全措施来防御服务器系统受到网络攻击的方式,避免服务器受到各种恶意攻击和网络威胁,那么,服务器硬防通常都会应用在哪些场景当中呢? 硬防服务器中一般会配备入侵检测系统和预防系统&#x…...
django filter 统计数量 按属性去重
在Django中,如果你想要根据某个属性对查询集进行去重并统计数量,你可以使用values()方法配合annotate()方法来实现。这里有两种常见的方法来完成这个需求: 方法1:使用annotate()和Count 假设你有一个模型Item,并且你想…...
ESP32 I2S音频总线学习笔记(四): INMP441采集音频并实时播放
简介 前面两期文章我们介绍了I2S的读取和写入,一个是通过INMP441麦克风模块采集音频,一个是通过PCM5102A模块播放音频,那如果我们将两者结合起来,将麦克风采集到的音频通过PCM5102A播放,是不是就可以做一个扩音器了呢…...
【HTML-16】深入理解HTML中的块元素与行内元素
HTML元素根据其显示特性可以分为两大类:块元素(Block-level Elements)和行内元素(Inline Elements)。理解这两者的区别对于构建良好的网页布局至关重要。本文将全面解析这两种元素的特性、区别以及实际应用场景。 1. 块元素(Block-level Elements) 1.1 基本特性 …...
MySQL用户和授权
开放MySQL白名单 可以通过iptables-save命令确认对应客户端ip是否可以访问MySQL服务: test: # iptables-save | grep 3306 -A mp_srv_whitelist -s 172.16.14.102/32 -p tcp -m tcp --dport 3306 -j ACCEPT -A mp_srv_whitelist -s 172.16.4.16/32 -p tcp -m tcp -…...
代码随想录刷题day30
1、零钱兑换II 给你一个整数数组 coins 表示不同面额的硬币,另给一个整数 amount 表示总金额。 请你计算并返回可以凑成总金额的硬币组合数。如果任何硬币组合都无法凑出总金额,返回 0 。 假设每一种面额的硬币有无限个。 题目数据保证结果符合 32 位带…...
MacOS下Homebrew国内镜像加速指南(2025最新国内镜像加速)
macos brew国内镜像加速方法 brew install 加速formula.jws.json下载慢加速 🍺 最新版brew安装慢到怀疑人生?别怕,教你轻松起飞! 最近Homebrew更新至最新版,每次执行 brew 命令时都会自动从官方地址 https://formulae.…...
零知开源——STM32F103RBT6驱动 ICM20948 九轴传感器及 vofa + 上位机可视化教程
STM32F1 本教程使用零知标准板(STM32F103RBT6)通过I2C驱动ICM20948九轴传感器,实现姿态解算,并通过串口将数据实时发送至VOFA上位机进行3D可视化。代码基于开源库修改优化,适合嵌入式及物联网开发者。在基础驱动上新增…...
系统掌握PyTorch:图解张量、Autograd、DataLoader、nn.Module与实战模型
本文较长,建议点赞收藏,以免遗失。更多AI大模型应用开发学习视频及资料,尽在聚客AI学院。 本文通过代码驱动的方式,系统讲解PyTorch核心概念和实战技巧,涵盖张量操作、自动微分、数据加载、模型构建和训练全流程&#…...
GraphQL 实战篇:Apollo Client 配置与缓存
GraphQL 实战篇:Apollo Client 配置与缓存 上一篇:GraphQL 入门篇:基础查询语法 依旧和上一篇的笔记一样,主实操,没啥过多的细节讲解,代码具体在: https://github.com/GoldenaArcher/graphql…...
