ntopng/http_src/vue/sankey-test.vue
2025-07-02 16:15:37 +02:00

311 lines
10 KiB
Vue

<!--
(C) 2013-22 - ntop.org
-->
<template>
<div class="row">
<div class="col-md-12 col-lg-12">
<div class="card card-shadow">
<Loading v-if="loading"></Loading>
<Sankey ref="sankey_chart" :width="width" :height="height" :no_data_message="no_data_message"
:sankey_data="sankey_data" @update_width="update_width" @update_height="update_height"
@node_click="on_node_click">
</Sankey>
</div>
</div>
</div>
</template>
<script setup>
import { ref, onMounted, onBeforeMount } from "vue";
import { default as Loading } from "./loading.vue"
import { ntopng_utility, ntopng_url_manager } from "../services/context/ntopng_globals_services.js";
import { default as Sankey } from "./sankey.vue";
const active_filter_list = {}
const props = defineProps({
ifid: Number,
available_filters: Object,
});
const _i18n = (t) => i18n(t);
const max_entries_reached = ref(false)
const max_entry_title = _i18n('ports_analysis.max_entries')
const no_data_message = _i18n('ports_analysis.no_data')
const sankey_chart = ref(null)
const body_div = ref(null);
const width = ref(null);
const height = ref(null);
const sankey_data = ref({});
const live_rest = `${http_prefix}/lua/pro/rest/v2/get/vlan/live_ports.lua`
const historical_rest = `${http_prefix}/lua/pro/rest/v2/get/vlan/historical_ports.lua`
const loading = ref(false)
onBeforeMount(() => {
/* Before mounting the various widgets, update the url to the correct one, by adding ifid, ecc. */
});
onMounted(() => {
update_height();
update_width();
set_sankey_data();
});
function on_node_click(node) {
if (node.is_link_node == true) { return; }
if (node.link) { ntopng_url_manager.go_to_url(node.link); }
}
const reload = function () {
update_sankey()
}
const click_item = function (item) {
ntopng_url_manager.set_key_to_url(item.filter_name, item.id)
update_sankey();
}
const update_sankey = function () {
set_sankey_data();
}
function check_max_entries(data) {
max_entries_reached.value = data.max_entries_reached
}
async function set_sankey_data() {
loading.value = true;
let data = {
nodes: [
{ index: 0, name: "Liikevaihto", hours: "100%" },
{ index: 1, name: "Kiinteät kulut", hours: "85%" },
{ index: 2, name: "Muuttuvat kulut", hours: "3:00" },
{ index: 3, name: "Palkkakulut", hours: "1:20" },
{ index: 4, name: "Muut kiinte", hours: "1:40" },
{ index: 5, name: "Kate", hours: "1:40" }
],
links: [
{ source: 0, target: 1, value: 75, hours: "+1:00" },
{ source: 0, target: 2, value: 10, hours: "+2:00" },
{ source: 1, target: 3, value: 69, hours: "+1:20" },
{ source: 1, target: 4, value: 6, hours: "+1:40" },
{ source: 0, target: 5, value: 15, hours: "+1:40" }
]
};
sankey_data.value = data;
loading.value = false;
}
async function get_sankey_data() {
const url_request = get_sankey_url();
let graph = await ntopng_utility.http_request(url_request);
check_max_entries(graph);
graph = make_complete_graph(graph);
const sankey_data = get_sankey_data_from_rest_data(graph);
return sankey_data;
}
function get_sankey_url() {
let vlan = ntopng_url_manager.get_url_entry("vlan");
let timeframe = ntopng_url_manager.get_url_entry("timeframe");
let l4proto = ntopng_url_manager.get_url_entry("l4proto");
if (vlan == 'none') { vlan = ''; }
if (timeframe == 'none') { timeframe = ''; }
if (l4proto == 'none') { l4proto = ''; }
let url_request = '';
let params = {
ifid: ntopng_url_manager.get_url_entry("ifid"),
vlan: vlan,
timeframe: timeframe,
l4proto: l4proto
};
let url_params = ntopng_url_manager.obj_to_url_params(params);
if (timeframe == '') { url_request = `${live_rest}?${url_params}`; }
else { url_request = `${historical_rest}?${url_params}`; }
return url_request;
}
function get_sankey_data_from_rest_data(res) {
let node_dict = {}, link_to_nodes_dict = {};
// create a node dict
res.nodes.forEach((node) => node_dict[node.node_id] = node);
let f_get_link_node_id = (link) => {
return `${link.source_node_id}_${link.label}`;
};
// merge all links by label
res.links.forEach((link) => {
let link_node_id = f_get_link_node_id(link);
let link_to_nodes = link_to_nodes_dict[link_node_id];
if (link_to_nodes == null) {
link_to_nodes = {
id: link_node_id,
label: link.label,
link: link.optional_info.link,
node_links: [],
};
link_to_nodes_dict[link_node_id] = link_to_nodes;
}
link_to_nodes.node_links.push({
source: node_dict[link.source_node_id],
target: node_dict[link.target_node_id],
value: link.value,
});
});
// create nodes and links
let nodes = res.nodes.map((n) => n), links = [];
for (let link_node_id in link_to_nodes_dict) {
let link_to_nodes = link_to_nodes_dict[link_node_id];
let link_node = {
node_id: link_to_nodes.id,
label: link_to_nodes.label,
link: link_to_nodes.link,
};
nodes.push(link_node);
link_to_nodes.node_links.forEach((link) => {
links.push({
source_node_id: link.source.node_id,
target_node_id: link_node.node_id,
label: `${link.source.label} - ${link.target.label}: ${link_node.label}`,
value: link.value,
});
links.push({
source_node_id: link_node.node_id,
target_node_id: link.target.node_id,
label: `${link.source.label} - ${link.target.label}: ${link_node.label}`,
value: link.value,
});
});
}
let sankey_nodes = nodes.map((n, index) => {
return { index, label: n.label, data: n };
});
let sankey_node_dict = {};
sankey_nodes.forEach((sn, index) => sankey_node_dict[sn.data.node_id] = sn);
let sankey_links = links.map((l) => {
let source_index = sankey_node_dict[l.source_node_id].index;
let target_index = sankey_node_dict[l.target_node_id].index;
return {
source: source_index,
target: target_index,
value: l.value,
label: l.label,
};
});
return { nodes: sankey_nodes, links: sankey_links };
}
// remove all links with a not existing node
function make_complete_graph(graph) {
let f_log_link = (l) => console.error(`link (source: ${l.source_node_id}, target: ${l.target_node_id}) removed for not existing source/target node`);
let links = get_links_with_existing_node(graph, f_log_link);
return { nodes: graph.nodes, links };
}
// remeove all circular links and return a dag graph
function make_dag_graph(graph) {
let nodes_dest_dict = {}; // dictionary { [node_source_id]: nodes_target[] }
graph.links.forEach((l) => {
let nodes_dest = nodes_dest_dict[l.source_node_id];
if (nodes_dest == null) {
nodes_dest = [];
nodes_dest_dict[l.source_node_id] = nodes_dest;
}
nodes_dest.push(l.target_node_id);
});
let nodes_to_check = {}; // temp dictionary used from f_add_circular_link
graph.nodes.forEach((n) => {
nodes_to_check[n.node_id] = { checked: false, visited: false };
});
// circular links dict (key: `${source_node_id}_${target_node_id}`)
let circular_links = {};
let f_get_link_key = (source_id, target_id) => `${source_id}_${target_id}`;
// deep navigate starting from node_id and add circular_links visited in circular_links dict
let f_set_circular_links = (node_id, from_node_id) => {
let node_to_check = nodes_to_check[node_id];
if (node_to_check.checked == true) { return; }
else if (node_to_check.visited == true) {
let link_key = f_get_link_key(from_node_id, node_id);
circular_links[link_key] = true;
console.error(`Link (source: ${from_node_id}, target: ${node_id} ) is a circular link`);
node_to_check.visited = false;
return;
}
node_to_check.visited = true;
let nodes_dest = nodes_dest_dict[node_id];
if (nodes_dest != null) {
for (let i = 0; i < nodes_dest.length; i += 1) {
let target_node_id = nodes_dest[i];
f_set_circular_links(target_node_id, node_id);
}
}
node_to_check.visited = false;
node_to_check.checked = true;
};
// set circular_links dictionary
graph.nodes.forEach((n) => f_set_circular_links(n.node_id));
// remove no dag nodes/links
let f_filter_link = (l) => {
let link_key = f_get_link_key(l.source_node_id, l.target_node_id);
let take_link = circular_links[link_key] == null;
return take_link;
};
let f_log_link = (l) => console.error(`link (source: ${l.source_node_id}, target: ${l.target_node_id}) removed for circular links`);
let links = filter_log(graph.links, f_filter_link, f_log_link);
let f_log_node = (n) => console.error(`node ${n.node_id} removed for circular links`);
let nodes = get_nodes_with_existing_link({ nodes: graph.nodes, links }, f_log_node);
// return a dag graph
return { nodes, links };
}
function get_links_with_existing_node(graph, f_log) {
let node_dict = {};
graph.nodes.forEach((n) => node_dict[n.node_id] = true);
let f_filter = (l) => node_dict[l.source_node_id] != null && node_dict[l.target_node_id] != null;
let links = filter_log(graph.links, f_filter, f_log);
return links;
}
function get_nodes_with_existing_link(graph, f_log) {
let link_source_dict = {};
let link_target_dict = {};
graph.links.forEach((l) => {
link_source_dict[l.source_node_id] = true;
link_target_dict[l.target_node_id] = true;
});
let f_filter = (n) => link_source_dict[n.node_id] == true || link_target_dict[n.node_id] == true;
let nodes = filter_log(graph.nodes, f_filter, f_log);
return nodes;
}
// log elements deleted if f_log != null
function filter_log(elements, f_filter, f_log) {
return elements.filter((e) => {
const take_element = f_filter(e);
if (take_element == false && f_log != null) {
f_log(e);
}
return take_element;
});
}
function update_height() {
height.value = $(body_div.value).height() - 100;
}
function update_width() {
width.value = $(body_div.value).width() - 10;
}
</script>