diff --git a/src/store/modules/network-profiling.ts b/src/store/modules/network-profiling.ts index b1bbedf9..eda6338d 100644 --- a/src/store/modules/network-profiling.ts +++ b/src/store/modules/network-profiling.ts @@ -70,7 +70,7 @@ export const networkProfilingStore = defineStore({ }, setTopology(data: { nodes: ProcessNode[]; calls: Call[] }) { const obj = {} as any; - const calls = (data.calls || []).reduce((prev: Call[], next: Call) => { + let calls = (data.calls || []).reduce((prev: Call[], next: Call) => { if (!obj[next.id]) { obj[next.id] = true; next.value = next.value || 1; @@ -87,7 +87,16 @@ export const networkProfilingStore = defineStore({ } return prev; }, []); - + calls = calls.map((d: any) => { + d.sourceId = d.source; + d.targetId = d.target; + d.source = d.sourceObj; + d.target = d.targetObj; + delete d.sourceObj; + delete d.targetObj; + return d; + }); + console.log(calls); this.calls = calls; this.nodes = data.nodes; }, diff --git a/src/views/dashboard/related/network-profiling/components/Graph/linkProcess.ts b/src/views/dashboard/related/network-profiling/components/Graph/linkProcess.ts new file mode 100644 index 00000000..0fe644ad --- /dev/null +++ b/src/views/dashboard/related/network-profiling/components/Graph/linkProcess.ts @@ -0,0 +1,93 @@ +/** + * 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: any) => { + const linkEnter = graph + .append("path") + .attr("class", "topo-call") + .attr("marker-end", "url(#arrow)") + .attr("stroke", "#afc4dd") + .attr("d", (d: any) => { + const controlPos = computeControlPoint( + [d.source.x, d.source.y - 5], + [d.target.x, d.target.y - 5], + 1 + ); + return ( + "M" + + d.source.x + + " " + + (d.source.y - 5) + + " " + + "Q" + + controlPos[0] + + " " + + controlPos[1] + + " " + + d.target.x + + " " + + (d.target.y - 5) + ); + }); + return linkEnter; +}; +export const anchorElement = (graph: any, funcs: any, tip: any) => { + const linkEnter = graph + .append("circle") + .attr("class", "topo-line-anchor") + .attr("r", 5) + .attr("fill", "#217EF25f") + .on("mouseover", function (event: unknown, d: unknown) { + tip.html(funcs.tipHtml).show(d, this); + }) + .on("mouseout", function () { + tip.hide(this); + }) + .on("click", (event: unknown, d: unknown) => { + funcs.handleLinkClick(event, d); + }); + return linkEnter; +}; +export const arrowMarker = (graph: any) => { + const defs = graph.append("defs"); + const arrow = defs + .append("marker") + .attr("id", "arrow") + .attr("class", "topo-line-arrow") + .attr("markerUnits", "strokeWidth") + .attr("markerWidth", "6") + .attr("markerHeight", "6") + .attr("viewBox", "0 0 12 12") + .attr("refX", "10") + .attr("refY", "6") + .attr("orient", "auto"); + const arrowPath = "M2,2 L10,6 L2,10 L6,6 L2,2"; + + arrow.append("path").attr("d", arrowPath).attr("fill", "#afc4dd"); + return arrow; +}; +function computeControlPoint(ps: number[], pe: number[], arc = 0.5) { + const deltaX = pe[0] - ps[0]; + const deltaY = pe[1] - ps[1]; + const theta = Math.atan(deltaY / deltaX); + const len = (Math.sqrt(deltaX * deltaX + deltaY * deltaY) / 2) * arc; + const newTheta = theta - Math.PI / 2; + return [ + (ps[0] + pe[0]) / 2 + len * Math.cos(newTheta), + (ps[1] + pe[1]) / 2 + len * Math.sin(newTheta), + ]; +} diff --git a/src/views/dashboard/related/components/D3Graph/nodeProcess.ts b/src/views/dashboard/related/network-profiling/components/Graph/nodeProcess.ts similarity index 92% rename from src/views/dashboard/related/components/D3Graph/nodeProcess.ts rename to src/views/dashboard/related/network-profiling/components/Graph/nodeProcess.ts index 57d65448..1185b4a9 100644 --- a/src/views/dashboard/related/components/D3Graph/nodeProcess.ts +++ b/src/views/dashboard/related/network-profiling/components/Graph/nodeProcess.ts @@ -37,16 +37,16 @@ export default (d3: any, graph: any, funcs: any, tip: any) => { .append("image") .attr("width", 35) .attr("height", 35) - .attr("x", (d: any) => d.x) - .attr("y", (d: any) => d.y) + .attr("x", (d: any) => d.x - 15) + .attr("y", (d: any) => d.y - 15) .attr("style", "cursor: move;") .attr("xlink:href", icons.CUBE); nodeEnter .append("text") .attr("fill", "#000") .attr("text-anchor", "middle") - .attr("x", (d: any) => d.x + 15) - .attr("y", (d: any) => d.y + 42) + .attr("x", (d: any) => d.x + 5) + .attr("y", (d: any) => d.y + 28) .text((d: { name: string }) => d.name.length > 10 ? `${d.name.substring(0, 10)}...` : d.name ); diff --git a/src/views/dashboard/related/components/D3Graph/zoom.ts b/src/views/dashboard/related/network-profiling/components/Graph/zoom.ts similarity index 100% rename from src/views/dashboard/related/components/D3Graph/zoom.ts rename to src/views/dashboard/related/network-profiling/components/Graph/zoom.ts diff --git a/src/views/dashboard/related/network-profiling/components/ProcessTopology.vue b/src/views/dashboard/related/network-profiling/components/ProcessTopology.vue index ea2e9a14..866fd321 100644 --- a/src/views/dashboard/related/network-profiling/components/ProcessTopology.vue +++ b/src/views/dashboard/related/network-profiling/components/ProcessTopology.vue @@ -13,16 +13,16 @@ 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. --> @@ -37,12 +37,8 @@ import { useNetworkProfilingStore } from "@/store/modules/network-profiling"; import { useDashboardStore } from "@/store/modules/dashboard"; import { useSelectorStore } from "@/store/modules/selectors"; import d3tip from "d3-tip"; -import { - linkElement, - anchorElement, - arrowMarker, -} from "../../components/D3Graph/linkElement"; -import nodeElement from "../../components/D3Graph/nodeProcess"; +import { linkElement, anchorElement, arrowMarker } from "./Graph/linkProcess"; +import nodeElement from "./Graph/nodeProcess"; import { Call } from "@/types/topology"; // import zoom from "../../components/D3Graph/zoom"; import { ProcessNode } from "@/types/ebpf"; @@ -109,7 +105,7 @@ function drawGraph() { .attr("transform", `translate(300, 300)`); graph.value.call(tip.value); node.value = graph.value.append("g").selectAll(".topo-node"); - link.value = graph.value.append("g").selectAll(".topo-line"); + link.value = graph.value.append("g").selectAll(".topo-call"); anchor.value = graph.value.append("g").selectAll(".topo-line-anchor"); arrow.value = graph.value.append("g").selectAll(".topo-line-arrow"); // svg.value.call(zoom(d3, graph.value)); @@ -157,7 +153,7 @@ function update() { if (!node.value || !link.value) { return; } - const obj: any = (chart.value && chart.value.getBoundingClientRect()) || {}; + // const obj: any = (chart.value && chart.value.getBoundingClientRect()) || {}; const p = { hexagonParam: [27, 0.04, 5, 0.04, 0], count: 1, @@ -180,7 +176,7 @@ function update() { const centers = hexGrid(p.count, 100, origin); // cube centers const cubeCenters = []; for (let i = 0; i < centers.length; i++) { - // const polygon = createPolygon(90, 6, 0); + // const polygon = createPolygon(100, 6, 0); // const vertices: any = []; // a hexagon vertices // for (let v = 0; v < polygon.length; v++) { // vertices.push([ @@ -196,9 +192,27 @@ function update() { // .attr("stroke", "#ccc") // .attr("stroke-width", 1) // .style("fill", "none"); - const c = hexGrid(p.count, 25, centers[i]); + const c = hexGrid(p.count, 30, centers[i]); cubeCenters.push(...c); } + // for (let i = 0; i < cubeCenters.length; i++) { + // const polygon = createPolygon(30, 6, 0); + // const vertices: any = []; // a hexagon vertices + // for (let v = 0; v < polygon.length; v++) { + // vertices.push([ + // cubeCenters[i][0] + polygon[v][0], + // cubeCenters[i][1] + polygon[v][1], + // ]); + // } + // const linePath = d3.line(); + // linePath.curve(d3.curveLinearClosed); + // graph.value + // .append("path") + // .attr("d", linePath(vertices)) + // .attr("stroke", "#ccc") + // .attr("stroke-width", 1) + // .style("fill", "none"); + // } shuffleArray(cubeCenters); const pos = hexGrid(p.count, 30, [p.radius * 2 + 20]); // cube centers const nodeArr = networkProfilingStore.nodes.filter( @@ -211,7 +225,6 @@ function update() { nodeArr[v].y = y; } node.value = node.value.data(nodeArr, (d: ProcessNode) => d.id); - node.value.exit().remove(); node.value = nodeElement( d3, @@ -223,6 +236,50 @@ function update() { }, tip.value ).merge(node.value); + // line element + const obj = {} as any; + const calls = networkProfilingStore.calls.reduce((prev: any[], next: any) => { + if ( + !( + obj[next.sourceId + next.targetId] && obj[next.targetId + next.sourceId] + ) + ) { + obj[next.sourceId + next.targetId] = true; + obj[next.targetId + next.sourceId] = true; + prev.push(next); + } + return prev; + }, []); + + link.value = link.value.data(calls, (d: Call) => d.id); + link.value.exit().remove(); + link.value = linkElement(link.value.enter()).merge(link.value); + // anchorElement + // anchor.value = anchor.value.data( + // networkProfilingStore.calls, + // (d: Call) => d.id + // ); + // anchor.value.exit().remove(); + // anchor.value = anchorElement( + // anchor.value.enter(), + // { + // handleLinkClick: handleLinkClick, + // tipHtml: (data: Call) => { + // const html = `
${t( + // "detectPoint" + // )}:${data.detectPoints.join(" | ")}
`; + // return html; + // }, + // }, + // tip.value + // ).merge(anchor.value); + // arrow marker + arrow.value = arrow.value.data( + networkProfilingStore.calls, + (d: Call) => d.id + ); + arrow.value.exit().remove(); + arrow.value = arrowMarker(arrow.value.enter()).merge(arrow.value); } function shuffleArray(array: number[][]) { @@ -261,27 +318,6 @@ function handleLinkClick(event: any, d: Call) { window.open(routeUrl.href, "_blank"); } -function ticked() { - link.value.attr( - "d", - (d: Call | any) => - `M${d.source.x} ${d.source.y} Q ${(d.source.x + d.target.x) / 2} ${ - (d.target.y + d.source.y) / 2 - d.loopFactor * 90 - } ${d.target.x} ${d.target.y}` - ); - anchor.value.attr( - "transform", - (d: Call | any) => - `translate(${(d.source.x + d.target.x) / 2}, ${ - (d.target.y + d.source.y) / 2 - d.loopFactor * 45 - })` - ); - node.value.attr( - "transform", - (d: Node | any) => `translate(${d.x - 22},${d.y - 22})` - ); -} - function updateSettings(config: any) { config.value = config; } @@ -324,8 +360,8 @@ watch( } ); - diff --git a/src/views/dashboard/related/topology/components/Graph.vue b/src/views/dashboard/related/topology/components/Graph.vue index 39ec45d2..83a6b65d 100644 --- a/src/views/dashboard/related/topology/components/Graph.vue +++ b/src/views/dashboard/related/topology/components/Graph.vue @@ -94,17 +94,10 @@ import { import { useI18n } from "vue-i18n"; import * as d3 from "d3"; import d3tip from "d3-tip"; -import zoom from "../../components/D3Graph/zoom"; -import { - simulationInit, - simulationSkip, -} from "../../components/D3Graph/simulation"; -import nodeElement from "../../components/D3Graph/nodeElement"; -import { - linkElement, - anchorElement, - arrowMarker, -} from "../../components/D3Graph/linkElement"; +import zoom from "./utils/zoom"; +import { simulationInit, simulationSkip } from "./utils/simulation"; +import nodeElement from "./utils/nodeElement"; +import { linkElement, anchorElement, arrowMarker } from "./utils/linkElement"; import { Node, Call } from "@/types/topology"; import { useSelectorStore } from "@/store/modules/selectors"; import { useTopologyStore } from "@/store/modules/topology"; diff --git a/src/views/dashboard/related/components/D3Graph/linkElement.ts b/src/views/dashboard/related/topology/components/utils/linkElement.ts similarity index 100% rename from src/views/dashboard/related/components/D3Graph/linkElement.ts rename to src/views/dashboard/related/topology/components/utils/linkElement.ts diff --git a/src/views/dashboard/related/components/D3Graph/nodeElement.ts b/src/views/dashboard/related/topology/components/utils/nodeElement.ts similarity index 100% rename from src/views/dashboard/related/components/D3Graph/nodeElement.ts rename to src/views/dashboard/related/topology/components/utils/nodeElement.ts diff --git a/src/views/dashboard/related/components/D3Graph/simulation.ts b/src/views/dashboard/related/topology/components/utils/simulation.ts similarity index 100% rename from src/views/dashboard/related/components/D3Graph/simulation.ts rename to src/views/dashboard/related/topology/components/utils/simulation.ts diff --git a/src/views/dashboard/related/topology/components/utils/zoom.ts b/src/views/dashboard/related/topology/components/utils/zoom.ts new file mode 100644 index 00000000..70576dda --- /dev/null +++ b/src/views/dashboard/related/topology/components/utils/zoom.ts @@ -0,0 +1,28 @@ +/** + * 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: any, graph: any) => + d3 + .zoom() + .scaleExtent([0.3, 10]) + .on("zoom", (d: any) => { + graph + .attr("transform", d3.zoomTransform(graph.node())) + .attr( + `translate(${d.transform.x},${d.transform.y})scale(${d.transform.k})` + ); + });