From 4b194a18f83a2c64e519aba11017154633932f8c Mon Sep 17 00:00:00 2001 From: Qiuxia Fan Date: Mon, 7 Feb 2022 15:23:41 +0800 Subject: [PATCH] feat: add graph tools --- .../{Topology.vue => topology/Graph.vue} | 0 .../dashboard/related/topology/utils/icons.js | 34 +++++++ .../related/topology/utils/legend.js | 42 +++++++++ .../related/topology/utils/linkElement.js | 40 ++++++++ .../related/topology/utils/nodeElement.js | 86 ++++++++++++++++++ .../related/topology/utils/simulation.js | 51 +++++++++++ .../dashboard/related/topology/utils/tool.js | 70 ++++++++++++++ .../related/topology/utils/tool/ALARM.png | Bin 0 -> 5916 bytes .../related/topology/utils/tool/API.png | Bin 0 -> 4070 bytes .../related/topology/utils/tool/ENDPOINT.png | Bin 0 -> 4702 bytes .../related/topology/utils/tool/INSTANCE.png | Bin 0 -> 6844 bytes .../related/topology/utils/tool/TRACE.png | Bin 0 -> 4853 bytes .../dashboard/related/topology/utils/zoom.js | 26 ++++++ 13 files changed, 349 insertions(+) rename src/views/dashboard/related/{Topology.vue => topology/Graph.vue} (100%) create mode 100755 src/views/dashboard/related/topology/utils/icons.js create mode 100644 src/views/dashboard/related/topology/utils/legend.js create mode 100644 src/views/dashboard/related/topology/utils/linkElement.js create mode 100644 src/views/dashboard/related/topology/utils/nodeElement.js create mode 100644 src/views/dashboard/related/topology/utils/simulation.js create mode 100644 src/views/dashboard/related/topology/utils/tool.js create mode 100644 src/views/dashboard/related/topology/utils/tool/ALARM.png create mode 100644 src/views/dashboard/related/topology/utils/tool/API.png create mode 100644 src/views/dashboard/related/topology/utils/tool/ENDPOINT.png create mode 100644 src/views/dashboard/related/topology/utils/tool/INSTANCE.png create mode 100644 src/views/dashboard/related/topology/utils/tool/TRACE.png create mode 100644 src/views/dashboard/related/topology/utils/zoom.js diff --git a/src/views/dashboard/related/Topology.vue b/src/views/dashboard/related/topology/Graph.vue similarity index 100% rename from src/views/dashboard/related/Topology.vue rename to src/views/dashboard/related/topology/Graph.vue diff --git a/src/views/dashboard/related/topology/utils/icons.js b/src/views/dashboard/related/topology/utils/icons.js new file mode 100755 index 00000000..8c669f60 --- /dev/null +++ b/src/views/dashboard/related/topology/utils/icons.js @@ -0,0 +1,34 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +const requireComponent = require.context("../../assets", false, /\.png$/); + +const result = {}; +function capitalizeFirstLetter(str) { + return str.toUpperCase(); +} +function validateFileName(str) { + return ( + /^\S+\.png$/.test(str) && + str.replace(/^\S+\/(\w+)\.png$/, (rs, $1) => capitalizeFirstLetter($1)) + ); +} +requireComponent.keys().forEach((filePath) => { + const componentConfig = requireComponent(filePath); + const fileName = validateFileName(filePath); + result[fileName] = componentConfig; +}); +export default result; diff --git a/src/views/dashboard/related/topology/utils/legend.js b/src/views/dashboard/related/topology/utils/legend.js new file mode 100644 index 00000000..7c075674 --- /dev/null +++ b/src/views/dashboard/related/topology/utils/legend.js @@ -0,0 +1,42 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import icons from "./icons"; + +export default function topoLegend(graph, clientHeight, clientWidth) { + for (const item of ["CUBE", "CUBEERROR"]) { + graph + .append("image") + .attr("width", 30) + .attr("height", 30) + .attr("x", clientWidth - (item === "CUBEERROR" ? 340 : 440)) + .attr("y", clientHeight - 50) + .attr("xlink:href", () => + item === "CUBEERROR" ? icons.CUBEERROR : icons.CUBE + ); + graph + .append("text") + .attr("x", clientWidth - (item === "CUBEERROR" ? 310 : 410)) + .attr("y", clientHeight - 30) + .text(() => { + return item === "CUBEERROR" + ? "Unhealthy (Successful Rate < 95% and Traffic > 1 call/min)" + : "Healthy"; + }) + .style("fill", "#efeff1") + .style("font-size", "11px"); + } +} diff --git a/src/views/dashboard/related/topology/utils/linkElement.js b/src/views/dashboard/related/topology/utils/linkElement.js new file mode 100644 index 00000000..c56c14d7 --- /dev/null +++ b/src/views/dashboard/related/topology/utils/linkElement.js @@ -0,0 +1,40 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +export const linkElement = (graph) => { + const linkEnter = graph + .append("path") + .attr("class", "topo-line") + .attr("stroke", (d) => (d.cpm ? "#217EF25f" : "#6a6d7777")); + return linkEnter; +}; +export const anchorElement = (graph, funcs, tip) => { + const linkEnter = graph + .append("circle") + .attr("class", "topo-line-anchor") + .attr("r", 5) + .attr("fill", (d) => (d.cpm ? "#217EF25f" : "#6a6d7777")) + .on("mouseover", function (d) { + tip.html(funcs.$tip).show(d, this); + }) + .on("mouseout", function () { + tip.hide(this); + }) + .on("click", (d) => { + funcs.handleLinkClick(d); + }); + return linkEnter; +}; diff --git a/src/views/dashboard/related/topology/utils/nodeElement.js b/src/views/dashboard/related/topology/utils/nodeElement.js new file mode 100644 index 00000000..c81136f8 --- /dev/null +++ b/src/views/dashboard/related/topology/utils/nodeElement.js @@ -0,0 +1,86 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import icons from "./icons"; + +icons["KAFKA-CONSUMER"] = icons.KAFKA; +export default (d3, graph, tool, funcs, tip) => { + const nodeEnter = graph + .append("g") + .call( + d3 + .drag() + .on("start", funcs.dragstart) + .on("drag", funcs.dragged) + .on("end", funcs.dragended) + ) + .on("mouseover", function (d) { + tip.html((data) => `
${data.name}
`).show(d, this); + }) + .on("mouseout", function () { + tip.hide(this); + }) + .on("click", (d) => { + event.stopPropagation(); + event.preventDefault(); + tool.attr("style", "display: none"); + funcs.handleNodeClick(d); + if (d.isReal) { + tool + .attr("transform", `translate(${d.x},${d.y - 20})`) + .attr("style", "display: block"); + } + }); + nodeEnter + .append("image") + .attr("width", 49) + .attr("height", 49) + .attr("x", 2) + .attr("y", 10) + .attr("style", "cursor: move;") + .attr("xlink:href", (d) => + d.sla < 95 && d.isReal && d.cpm > 1 ? icons.CUBEERROR : icons.CUBE + ); + nodeEnter + .append("image") + .attr("width", 32) + .attr("height", 32) + .attr("x", 6) + .attr("y", -10) + .attr("style", "opacity: 0.5;") + .attr("xlink:href", icons.LOCAL); + nodeEnter + .append("image") + .attr("width", 18) + .attr("height", 18) + .attr("x", 13) + .attr("y", -7) + .attr("xlink:href", (d) => + !d.type || d.type === "N/A" + ? icons.UNDEFINED + : icons[d.type.toUpperCase().replace("-", "")] + ); + nodeEnter + .append("text") + .attr("class", "topo-text") + .attr("text-anchor", "middle") + .attr("x", 22) + .attr("y", 70) + .text((d) => + d.name.length > 20 ? `${d.name.substring(0, 20)}...` : d.name + ); + return nodeEnter; +}; diff --git a/src/views/dashboard/related/topology/utils/simulation.js b/src/views/dashboard/related/topology/utils/simulation.js new file mode 100644 index 00000000..58f37b4d --- /dev/null +++ b/src/views/dashboard/related/topology/utils/simulation.js @@ -0,0 +1,51 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +export const simulationInit = (d3, data_nodes, data_links, ticked) => { + const simulation = d3 + .forceSimulation(data_nodes) + .force( + "collide", + d3.forceCollide().radius(() => 60) + ) + .force("yPos", d3.forceY().strength(1)) + .force("xPos", d3.forceX().strength(1)) + .force("charge", d3.forceManyBody().strength(-520)) + .force( + "link", + d3.forceLink(data_links).id((d) => d.id) + ) + .force( + "center", + d3.forceCenter(window.innerWidth / 2, window.innerHeight / 2 - 20) + ) + .on("tick", ticked) + .stop(); + simulationSkip(d3, simulation, ticked); + return simulation; +}; + +export const simulationSkip = (d3, simulation, ticked) => { + d3.timeout(() => { + const n = Math.ceil( + Math.log(simulation.alphaMin()) / Math.log(1 - simulation.alphaDecay()) + ); + for (let i = 0; i < n; i += 1) { + simulation.tick(); + ticked(); + } + }); +}; diff --git a/src/views/dashboard/related/topology/utils/tool.js b/src/views/dashboard/related/topology/utils/tool.js new file mode 100644 index 00000000..98a9f81c --- /dev/null +++ b/src/views/dashboard/related/topology/utils/tool.js @@ -0,0 +1,70 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +const requireComponent = require.context("./tool", false, /\.png$/); + +const icons = {}; +function capitalizeFirstLetter(str) { + return str.toUpperCase(); +} +function validateFileName(str) { + return ( + /^\S+\.png$/.test(str) && + str.replace(/^\S+\/(\w+)\.png$/, (rs, $1) => capitalizeFirstLetter($1)) + ); +} +requireComponent.keys().forEach((filePath) => { + const componentConfig = requireComponent(filePath); + const fileName = validateFileName(filePath); + icons[fileName] = componentConfig; +}); + +const Hexagon = (side, r, cx, cy) => { + let path = ""; + for (let i = 0; i < side; i += 1) { + let x = Math.cos(((2 / side) * i + 1 / side) * Math.PI) * r + cx; + let y = -Math.sin(((2 / side) * i + 1 / side) * Math.PI) * r + cy; + path += !i ? `M${x},${y} ` : `L${x},${y} `; + if (i == side - 1) path += "Z"; + } + return path; +}; + +export default (graph, data) => { + const tool = graph.append("g").attr("class", "topo-tool"); + const side = 6; + for (let i = 0; i < data.length; i += 1) { + let x = Math.cos((2 / side) * i * Math.PI) * 34; + let y = -Math.sin((2 / side) * i * Math.PI) * 34; + const tool_g = tool + .append("g") + .attr("class", "topo-tool-i") + .on("click", data[i].click); + tool_g + .append("path") + .attr("class", "tool-hexagon") + .attr("d", Hexagon(6, 17, x, y)); + tool_g + .append("svg:image") + .attr("width", 14) + .attr("height", 14) + .attr("x", x - 7) + .attr("y", y - 7) + .attr("style", "opacity: 0.8") + .attr("xlink:href", icons[data[i].icon]); + } + return tool; +}; diff --git a/src/views/dashboard/related/topology/utils/tool/ALARM.png b/src/views/dashboard/related/topology/utils/tool/ALARM.png new file mode 100644 index 0000000000000000000000000000000000000000..85009cb2a9eba4d3d6e212827801d50877ff45b0 GIT binary patch literal 5916 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 b+Y!XVy7g9EkAX5sy@8v0rn(i{_oMy?afq>= literal 0 HcmV?d00001 diff --git a/src/views/dashboard/related/topology/utils/tool/API.png b/src/views/dashboard/related/topology/utils/tool/API.png new file mode 100644 index 0000000000000000000000000000000000000000..385dcaf61c77925cffaede4cc564e7d39dca6ef0 GIT binary patch literal 4070 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 XpJ_nsilx&u0A5xWwwNOGa}oam9sXl_ literal 0 HcmV?d00001 diff --git a/src/views/dashboard/related/topology/utils/tool/ENDPOINT.png b/src/views/dashboard/related/topology/utils/tool/ENDPOINT.png new file mode 100644 index 0000000000000000000000000000000000000000..de26135f3c475b60ac2d3127e990fcc50bca1656 GIT binary patch literal 4702 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 literal 0 HcmV?d00001 diff --git a/src/views/dashboard/related/topology/utils/tool/INSTANCE.png b/src/views/dashboard/related/topology/utils/tool/INSTANCE.png new file mode 100644 index 0000000000000000000000000000000000000000..f0f1dbff72e4ede0254b9f70589348c5c065d067 GIT binary patch literal 6844 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�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 VmJyOm1pm$w@_>889=M**`Zpi|TVenJ literal 0 HcmV?d00001 diff --git a/src/views/dashboard/related/topology/utils/zoom.js b/src/views/dashboard/related/topology/utils/zoom.js new file mode 100644 index 00000000..f7046881 --- /dev/null +++ b/src/views/dashboard/related/topology/utils/zoom.js @@ -0,0 +1,26 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +export default (d3, graph) => + d3 + .zoom() + .scaleExtent([0.3, 10]) + .on("zoom", () => { + graph.attr( + "transform", + `translate(${d3.event.transform.x},${d3.event.transform.y})scale(${d3.event.transform.k})` + ); + });