update graph

This commit is contained in:
Fine 2022-08-09 20:02:02 +08:00
parent 0f8c522cfd
commit 5f763dafb9
5 changed files with 116 additions and 45 deletions

View File

@ -42,6 +42,8 @@ interface EbpfStore {
aggregateType: string; aggregateType: string;
nodes: ProcessNode[]; nodes: ProcessNode[];
calls: Call[]; calls: Call[];
node: Nullable<ProcessNode>;
call: Nullable<Call>;
} }
export const ebpfStore = defineStore({ export const ebpfStore = defineStore({
@ -61,6 +63,8 @@ export const ebpfStore = defineStore({
aggregateType: "COUNT", aggregateType: "COUNT",
nodes: [], nodes: [],
calls: [], calls: [],
node: null,
call: null,
}), }),
actions: { actions: {
setSelectedTask(task: EBPFTaskList) { setSelectedTask(task: EBPFTaskList) {
@ -75,6 +79,35 @@ export const ebpfStore = defineStore({
setAnalyzeTrees(tree: AnalyzationTrees[]) { setAnalyzeTrees(tree: AnalyzationTrees[]) {
this.analyzeTrees = tree; this.analyzeTrees = tree;
}, },
setNode(node: Node) {
this.node = node;
},
setLink(link: Call) {
this.call = link;
},
setTopology(data: { nodes: ProcessNode[]; calls: Call[] }) {
const obj = {} as any;
const calls = (data.calls || []).reduce((prev: Call[], next: Call) => {
if (!obj[next.id]) {
obj[next.id] = true;
next.value = next.value || 1;
for (const node of data.nodes) {
if (next.source === node.id) {
next.sourceObj = node;
}
if (next.target === node.id) {
next.targetObj = node;
}
}
next.value = next.value || 1;
prev.push(next);
}
return prev;
}, []);
this.calls = calls;
this.nodes = data.nodes;
},
async getCreateTaskData(serviceId: string) { async getCreateTaskData(serviceId: string) {
const res: AxiosResponse = await graphql const res: AxiosResponse = await graphql
.query("getCreateTaskData") .query("getCreateTaskData")
@ -223,7 +256,9 @@ export const ebpfStore = defineStore({
this.calls = []; this.calls = [];
return res.data; return res.data;
} }
const topo = res.data.data; const { topology } = res.data.data;
this.setTopology(topology);
return res.data; return res.data;
}, },
}, },

View File

@ -45,13 +45,12 @@ import ProcessTopology from "./components/ProcessTopology.vue";
.item { .item {
width: 100%; width: 100%;
overflow: auto; height: calc(100% - 210px);
height: calc(100% - 100px); background-color: #333840;
padding-bottom: 10px;
} }
.schedules { .schedules {
min-height: 90px; height: 200px;
border-bottom: 1px solid #ccc; border-bottom: 1px solid #ccc;
padding-right: 10px; padding-right: 10px;
} }

View File

@ -13,17 +13,15 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and See the License for the specific language governing permissions and
limitations under the License. --> limitations under the License. -->
<template> <template>
<div ref="chart" class="topology"></div> <div ref="chart" class="micro-topo-chart"></div>
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import type { PropType } from "vue"; import type { PropType } from "vue";
import { ref, onMounted } from "vue"; import { ref, onMounted, watch } from "vue";
import * as d3 from "d3"; import * as d3 from "d3";
import { useI18n } from "vue-i18n"; import { useI18n } from "vue-i18n";
import { useEbpfStore } from "@/store/modules/ebpf"; import { useEbpfStore } from "@/store/modules/ebpf";
import { useSelectorStore } from "@/store/modules/selectors";
import { useDashboardStore } from "@/store/modules/dashboard"; import { useDashboardStore } from "@/store/modules/dashboard";
import { useAppStoreWithOut } from "@/store/modules/app";
import d3tip from "d3-tip"; import d3tip from "d3-tip";
import { simulationInit, simulationSkip } from "./utils/simulation"; import { simulationInit, simulationSkip } from "./utils/simulation";
import { linkElement, anchorElement, arrowMarker } from "./utils/linkElement"; import { linkElement, anchorElement, arrowMarker } from "./utils/linkElement";
@ -40,10 +38,8 @@ const props = defineProps({
}, },
}); });
const { t } = useI18n(); const { t } = useI18n();
const selectorStore = useSelectorStore();
const dashboardStore = useDashboardStore(); const dashboardStore = useDashboardStore();
const ebpfStore = useEbpfStore(); const ebpfStore = useEbpfStore();
const appStore = useAppStoreWithOut();
const height = ref<number>(100); const height = ref<number>(100);
const width = ref<number>(100); const width = ref<number>(100);
const simulation = ref<any>(null); const simulation = ref<any>(null);
@ -61,20 +57,25 @@ onMounted(() => {
}); });
async function init() { async function init() {
await getTopology();
const dom = document.querySelector(".topology")?.getBoundingClientRect() || {
height: 40,
width: 0,
};
height.value = dom.height - 40;
width.value = dom.width;
svg.value = d3.select(chart.value).append("svg").attr("class", "process-svg"); svg.value = d3.select(chart.value).append("svg").attr("class", "process-svg");
window.addEventListener("resize", resize);
if (!ebpfStore.nodes.length) {
return;
}
drawGraph(); drawGraph();
update(); update();
} }
function drawGraph() { function drawGraph() {
const dom = document
.querySelector(".micro-topo-chart")
?.getBoundingClientRect() || {
height: 40,
width: 0,
};
height.value = dom.height - 40;
width.value = dom.width;
svg.value.attr("height", height.value).attr("width", width.value);
tip.value = (d3tip as any)().attr("class", "d3-tip").offset([-8, 0]); tip.value = (d3tip as any)().attr("class", "d3-tip").offset([-8, 0]);
graph.value = svg.value graph.value = svg.value
.append("g") .append("g")
@ -111,10 +112,6 @@ function update() {
d3, d3,
node.value.enter(), node.value.enter(),
{ {
// dragstart: dragstart,
// dragged: dragged,
// dragended: dragended,
handleNodeClick: handleNodeClick,
tipHtml: (data: ProcessNode) => { tipHtml: (data: ProcessNode) => {
return ` <div class="mb-5"><span class="grey">name: </span>${data.name}</div>`; return ` <div class="mb-5"><span class="grey">name: </span>${data.name}</div>`;
}, },
@ -206,31 +203,42 @@ function ticked() {
); );
} }
function getTopology() { function resize() {
const serviceInstanceId = const dom = chart.value?.getBoundingClientRect() || {
(selectorStore.currentPod && selectorStore.currentPod.id) || ""; height: 40,
ebpfStore.getProcessTopology({ width: 0,
serviceInstanceId, };
duration: appStore.durationTime, height.value = dom.height - 40;
}); width.value = dom.width;
svg.value.attr("height", height.value).attr("width", width.value);
} }
function handleNodeClick(d: Node & { x: number; y: number }) { async function freshNodes() {
ebpfStore.setNode(d); svg.value.selectAll(".svg-graph").remove();
ebpfStore.setLink(null); if (!ebpfStore.nodes.length) {
return;
} }
drawGraph();
update();
}
watch(
() => ebpfStore.nodes,
() => {
freshNodes();
}
);
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
.topology { .micro-topo-chart {
width: calc(100% - 5px); width: calc(100% - 10px);
margin: 0 5px 5px 0; margin: 0 5px 5px 0;
height: 100%; height: 100%;
min-height: 150px; min-height: 150px;
} }
.topo-svg { .process-svg {
width: 100%; width: 100%;
height: calc(100% - 5px); height: calc(100% - 10px);
cursor: move; cursor: move;
} }
</style> </style>

View File

@ -106,7 +106,6 @@ watch(
.time-ranges { .time-ranges {
width: calc(100% - 5px); width: calc(100% - 5px);
margin: 0 5px 5px 0; margin: 0 5px 5px 0;
height: 100%; height: 150px;
min-height: 150px;
} }
</style> </style>

View File

@ -85,10 +85,14 @@ import { useSelectorStore } from "@/store/modules/selectors";
import { EBPFTaskList } from "@/types/ebpf"; import { EBPFTaskList } from "@/types/ebpf";
import { ElMessage } from "element-plus"; import { ElMessage } from "element-plus";
import TaskDetails from "../../components/TaskDetails.vue"; import TaskDetails from "../../components/TaskDetails.vue";
import dateFormatStep from "@/utils/dateFormat";
import getLocalTime from "@/utils/localtime";
import { useAppStoreWithOut } from "@/store/modules/app";
const { t } = useI18n(); const { t } = useI18n();
const selectorStore = useSelectorStore(); const selectorStore = useSelectorStore();
const ebpfStore = useEbpfStore(); const ebpfStore = useEbpfStore();
const appStore = useAppStoreWithOut();
const dateFormat = (date: number, pattern = "YYYY-MM-DD HH:mm:ss") => const dateFormat = (date: number, pattern = "YYYY-MM-DD HH:mm:ss") =>
dayjs(date).format(pattern); dayjs(date).format(pattern);
const viewDetail = ref<boolean>(false); const viewDetail = ref<boolean>(false);
@ -97,12 +101,37 @@ fetchTasks();
async function changeTask(item: EBPFTaskList) { async function changeTask(item: EBPFTaskList) {
ebpfStore.setSelectedNetworkTask(item); ebpfStore.setSelectedNetworkTask(item);
const res = await ebpfStore.getEBPFSchedules({ getTopology();
taskId: item.taskId,
});
if (res.errors) {
ElMessage.error(res.errors);
} }
async function getTopology() {
const serviceInstanceId =
(selectorStore.currentPod && selectorStore.currentPod.id) || "";
const resp = await ebpfStore.getProcessTopology({
serviceInstanceId,
duration: {
start: dateFormatStep(
getLocalTime(
appStore.utc,
new Date(ebpfStore.selectedNetworkTask.taskStartTime)
),
appStore.duration.step,
true
),
end: dateFormatStep(
getLocalTime(
appStore.utc,
new Date(
ebpfStore.selectedNetworkTask.taskStartTime +
ebpfStore.selectedNetworkTask.fixedTriggerDuration * 1000
)
),
appStore.duration.step,
true
),
step: appStore.duration.step,
},
});
return resp;
} }
async function createTask() { async function createTask() {
const serviceId = const serviceId =
@ -132,8 +161,9 @@ async function fetchTasks() {
}); });
if (res.errors) { if (res.errors) {
ElMessage.error(res.errors); return ElMessage.error(res.errors);
} }
getTopology();
} }
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>