style: 修复样式
This commit is contained in:
172
practical_training/views/dashboard/overview/hooks/useTopology.ts
Normal file
172
practical_training/views/dashboard/overview/hooks/useTopology.ts
Normal file
@@ -0,0 +1,172 @@
|
||||
import { parseDateToStr } from '@/utils/date-utils';
|
||||
import { computed, reactive, ref } from 'vue';
|
||||
|
||||
/**非网元元素 */
|
||||
export const notNeNodes = [
|
||||
'5GC',
|
||||
'DN',
|
||||
'UE',
|
||||
'Base',
|
||||
'lan',
|
||||
'lan1',
|
||||
'lan2',
|
||||
'lan3',
|
||||
'lan4',
|
||||
'lan5',
|
||||
'lan6',
|
||||
'lan7',
|
||||
'LAN',
|
||||
'NR',
|
||||
];
|
||||
|
||||
/**图状态 */
|
||||
export const graphState = reactive<Record<string, any>>({
|
||||
/**当前图组名 */
|
||||
group: '5GC System Architecture',
|
||||
/**图数据 */
|
||||
data: {
|
||||
combos: [],
|
||||
edges: [],
|
||||
nodes: [],
|
||||
},
|
||||
});
|
||||
|
||||
/**图实例对象 */
|
||||
export const graphG6 = ref<any>(null);
|
||||
|
||||
/**图点击选择 */
|
||||
export const graphNodeClickID = ref<string>('UPF');
|
||||
|
||||
/**图节点网元信息状态 */
|
||||
export const graphNodeState = computed(() =>
|
||||
graphState.data.nodes.map((item: any) => ({
|
||||
id: item.id,
|
||||
label: item.label,
|
||||
neInfo: item.neInfo,
|
||||
neState: item.neState,
|
||||
}))
|
||||
);
|
||||
|
||||
/**图节点网元状态数量 */
|
||||
export const graphNodeStateNum = computed(() => {
|
||||
let normal = 0;
|
||||
let abnormal = 0;
|
||||
for (const item of graphState.data.nodes) {
|
||||
const neId = item.neState.neId;
|
||||
if (neId) {
|
||||
if (item.neState.online) {
|
||||
normal += 1;
|
||||
} else {
|
||||
abnormal += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
return [normal, abnormal];
|
||||
});
|
||||
|
||||
/**网元状态请求标记 */
|
||||
export const neStateRequestMap = ref<Map<string, boolean>>(new Map());
|
||||
|
||||
/**neStateParse 网元状态 数据解析 */
|
||||
export function neStateParse(neType: string, data: Record<string, any>) {
|
||||
const { combos, edges, nodes } = graphState.data;
|
||||
const node = nodes.find((item: Record<string, any>) => item.id === neType);
|
||||
|
||||
// 更新网元状态
|
||||
const newNeState = Object.assign(node.neState, data, {
|
||||
refreshTime: parseDateToStr(data.refreshTime, 'HH:mm:ss'),
|
||||
online: !!data.cpu,
|
||||
});
|
||||
|
||||
// 通过 ID 查询节点实例
|
||||
const item = graphG6.value.findById(node.id);
|
||||
if (item) {
|
||||
const stateColor = newNeState.online ? '#52c41a' : '#f5222d'; // 状态颜色
|
||||
// 图片类型不能填充
|
||||
if (node.type.startsWith('image')) {
|
||||
// 更新节点
|
||||
if (node.label !== newNeState.neName) {
|
||||
graphG6.value.updateItem(item, {
|
||||
label: newNeState.neName,
|
||||
});
|
||||
}
|
||||
// 设置状态
|
||||
graphG6.value.setItemState(item, 'top-right-dot', stateColor);
|
||||
} else {
|
||||
// 更新节点
|
||||
graphG6.value.updateItem(item, {
|
||||
label: newNeState.neName,
|
||||
// neState: newNeState,
|
||||
style: {
|
||||
fill: stateColor, // 填充色
|
||||
stroke: stateColor, // 填充色
|
||||
},
|
||||
// labelCfg: {
|
||||
// style: {
|
||||
// fill: '#ffffff', // 标签文本色
|
||||
// },
|
||||
// },
|
||||
});
|
||||
// 设置状态
|
||||
graphG6.value.setItemState(item, 'stroke', newNeState.online);
|
||||
}
|
||||
}
|
||||
|
||||
// 设置边状态
|
||||
for (const edge of edges) {
|
||||
const edgeSource: string = edge.source;
|
||||
const edgeTarget: string = edge.target;
|
||||
const neS = nodes.find((n: any) => n.id === edgeSource);
|
||||
const neT = nodes.find((n: any) => n.id === edgeTarget);
|
||||
// console.log(neS, edgeSource, neT, edgeTarget);
|
||||
|
||||
if (neS && neT) {
|
||||
// 通过 ID 查询节点实例
|
||||
// const item = graphG6.value.findById(edge.id);
|
||||
// console.log(
|
||||
// `${edgeSource} - ${edgeTarget}`,
|
||||
// neS.neState.online && neT.neState.online
|
||||
// );
|
||||
// const stateColor = neS.neState.online && neT.neState.online ? '#000000' : '#ff4d4f'; // 状态颜色
|
||||
// 更新边
|
||||
// graphG6.value.updateItem(item, {
|
||||
// label: `${edgeSource} - ${edgeTarget}`,
|
||||
// style: {
|
||||
// stroke: stateColor, // 填充色
|
||||
// },
|
||||
// labelCfg: {
|
||||
// style: {
|
||||
// fill: '#ffffff', // 标签文本色
|
||||
// },
|
||||
// },
|
||||
// });
|
||||
// 设置状态
|
||||
graphG6.value.setItemState(
|
||||
edge.id,
|
||||
'circle-move',
|
||||
neS.neState.online && neT.neState.online
|
||||
);
|
||||
}
|
||||
if (neS && notNeNodes.includes(edgeTarget)) {
|
||||
graphG6.value.setItemState(edge.id, 'line-dash', neS.neState.online);
|
||||
}
|
||||
if (neT && notNeNodes.includes(edgeSource)) {
|
||||
graphG6.value.setItemState(edge.id, 'line-dash', neT.neState.online);
|
||||
}
|
||||
}
|
||||
|
||||
// 请求标记复位
|
||||
neStateRequestMap.value.set(neType, false);
|
||||
}
|
||||
|
||||
/**属性复位 */
|
||||
export function topologyReset() {
|
||||
graphState.data = {
|
||||
combos: [],
|
||||
edges: [],
|
||||
nodes: [],
|
||||
};
|
||||
graphG6.value = null;
|
||||
graphNodeClickID.value = 'UPF';
|
||||
neStateRequestMap.value = new Map();
|
||||
}
|
||||
@@ -0,0 +1,115 @@
|
||||
import { parseDateToStr } from '@/utils/date-utils';
|
||||
import { parseSizeFromBits, parseSizeFromKbs } from '@/utils/parse-utils';
|
||||
import { ref } from 'vue';
|
||||
|
||||
type FDType = {
|
||||
/**时间 */
|
||||
lineXTime: string[];
|
||||
/**上行 N3 */
|
||||
lineYUp: number[];
|
||||
/**下行 N6 */
|
||||
lineYDown: number[];
|
||||
/**容量 */
|
||||
cap: number;
|
||||
};
|
||||
|
||||
/**UPF-流量数据 */
|
||||
export const upfFlowData = ref<FDType>({
|
||||
lineXTime: [],
|
||||
lineYUp: [],
|
||||
lineYDown: [],
|
||||
cap: 0,
|
||||
});
|
||||
|
||||
/**UPF-流量数据 数据解析 */
|
||||
export function upfFlowParse(data: Record<string, string>) {
|
||||
upfFlowData.value.lineXTime.push(parseDateToStr(+data['timeGroup']));
|
||||
const upN3 = parseSizeFromKbs(+data['UPF.03'], 5);
|
||||
upfFlowData.value.lineYUp.push(upN3[0]);
|
||||
const downN6 = parseSizeFromKbs(+data['UPF.06'], 5);
|
||||
upfFlowData.value.lineYDown.push(downN6[0]);
|
||||
upfFlowData.value.cap += 1;
|
||||
// 超过 25 弹出
|
||||
if (upfFlowData.value.cap > 25) {
|
||||
upfFlowData.value.lineXTime.shift();
|
||||
upfFlowData.value.lineYUp.shift();
|
||||
upfFlowData.value.lineYDown.shift();
|
||||
upfFlowData.value.cap -= 1;
|
||||
}
|
||||
// UPF-总流量数0天 当天24小时
|
||||
upfTFParse('0', {
|
||||
up: upfTotalFlow.value['0'].up + +data['UPF.03'],
|
||||
down: upfTotalFlow.value['0'].down + +data['UPF.06'],
|
||||
});
|
||||
}
|
||||
|
||||
type TFType = {
|
||||
/**上行 N3 */
|
||||
up: number;
|
||||
upFrom: string;
|
||||
/**下行 N6 */
|
||||
down: number;
|
||||
downFrom: string;
|
||||
/**请求标记 */
|
||||
requestFlag: boolean;
|
||||
};
|
||||
|
||||
/**UPF-总流量数 */
|
||||
export const upfTotalFlow = ref<Record<string, TFType>>({
|
||||
'0': {
|
||||
up: 0,
|
||||
upFrom: '0 B',
|
||||
down: 0,
|
||||
downFrom: '0 B',
|
||||
requestFlag: false,
|
||||
},
|
||||
'7': {
|
||||
up: 0,
|
||||
upFrom: '0 B',
|
||||
down: 0,
|
||||
downFrom: '0 B',
|
||||
requestFlag: false,
|
||||
},
|
||||
'30': {
|
||||
up: 0,
|
||||
upFrom: '0 B',
|
||||
down: 0,
|
||||
downFrom: '0 B',
|
||||
requestFlag: false,
|
||||
},
|
||||
});
|
||||
|
||||
/**UPF-总流量数 数据解析 */
|
||||
export function upfTFParse(day: string, data: Record<string, number>) {
|
||||
let { up, down } = data;
|
||||
upfTotalFlow.value[day] = {
|
||||
up: up,
|
||||
upFrom: parseSizeFromBits(up),
|
||||
down: down,
|
||||
downFrom: parseSizeFromBits(down),
|
||||
requestFlag: false,
|
||||
};
|
||||
}
|
||||
|
||||
/**UPF-总流量数 选中 */
|
||||
export const upfTFActive = ref<string>('0');
|
||||
|
||||
/**属性复位 */
|
||||
export function upfTotalFlowReset() {
|
||||
upfFlowData.value = {
|
||||
lineXTime: [],
|
||||
lineYUp: [],
|
||||
lineYDown: [],
|
||||
cap: 0,
|
||||
};
|
||||
for (const key of Object.keys(upfTotalFlow.value)) {
|
||||
upfTotalFlow.value[key] = {
|
||||
up: 0,
|
||||
upFrom: '0 B',
|
||||
down: 0,
|
||||
downFrom: '0 B',
|
||||
requestFlag: false,
|
||||
};
|
||||
}
|
||||
upfTFActive.value = '0';
|
||||
}
|
||||
@@ -0,0 +1,148 @@
|
||||
import { ref } from 'vue';
|
||||
|
||||
/**ueEventAMFParse UE会话事件AMF 数据解析 */
|
||||
function ueEventAMFParse(
|
||||
item: Record<string, any>
|
||||
): false | Record<string, any> {
|
||||
let evData: Record<string, any> = item.eventJSON;
|
||||
if (typeof evData === 'string') {
|
||||
try {
|
||||
evData = JSON.parse(evData);
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
eType: 'amf_ue',
|
||||
eId: `amf_ue_${item.id}_${Date.now()}`,
|
||||
eTime: +item.timestamp,
|
||||
id: item.id,
|
||||
type: item.eventType,
|
||||
data: evData,
|
||||
};
|
||||
}
|
||||
|
||||
/**ueEventMMEParse UE会话事件MME 数据解析 */
|
||||
function ueEventMMEParse(
|
||||
item: Record<string, any>
|
||||
): false | Record<string, any> {
|
||||
let evData: Record<string, any> = item.eventJSON;
|
||||
if (typeof evData === 'string') {
|
||||
try {
|
||||
evData = JSON.parse(evData);
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
eType: 'mme_ue',
|
||||
eId: `mme_ue_${item.id}_${Date.now()}`,
|
||||
eTime: +item.timestamp,
|
||||
id: item.id,
|
||||
type: item.eventType,
|
||||
data: evData,
|
||||
};
|
||||
}
|
||||
|
||||
/**cdrEventIMSParse CDR会话事件IMS 数据解析 */
|
||||
function cdrEventIMSParse(
|
||||
item: Record<string, any>
|
||||
): false | Record<string, any> {
|
||||
let evData: Record<string, any> = item.cdrJSON || item.CDR;
|
||||
if (typeof evData === 'string') {
|
||||
try {
|
||||
evData = JSON.parse(evData);
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// 指定显示CDR类型MOC/MTSM
|
||||
if (!['MOC', 'MTSM'].includes(evData.recordType)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return {
|
||||
eType: 'ims_cdr',
|
||||
eId: `ims_cdr_${item.id}_${Date.now()}`,
|
||||
eTime: +item.timestamp,
|
||||
id: item.id,
|
||||
data: evData,
|
||||
};
|
||||
}
|
||||
|
||||
/**eventListParse 事件列表解析 */
|
||||
export function eventListParse(
|
||||
type: 'ims_cdr' | 'amf_ue' | 'mme_ue',
|
||||
data: any
|
||||
) {
|
||||
eventTotal.value += data.total;
|
||||
for (const item of data.rows) {
|
||||
let v: false | Record<string, any> = false;
|
||||
if (type === 'ims_cdr') {
|
||||
v = cdrEventIMSParse(item);
|
||||
}
|
||||
if (type === 'amf_ue') {
|
||||
v = ueEventAMFParse(item);
|
||||
}
|
||||
if (type === 'mme_ue') {
|
||||
v = ueEventMMEParse(item);
|
||||
}
|
||||
|
||||
if (v) {
|
||||
eventData.value.push(v);
|
||||
}
|
||||
}
|
||||
|
||||
// 有数据进行排序
|
||||
if (eventData.value.length > 5) {
|
||||
eventData.value.sort((a, b) => b.eTime - a.eTime);
|
||||
}
|
||||
if (eventData.value.length > 0) {
|
||||
eventId.value = eventData.value[0].eId;
|
||||
}
|
||||
}
|
||||
|
||||
/**eventItemParseAndPush 事件项解析并添加 */
|
||||
export async function eventItemParseAndPush(
|
||||
type: 'ims_cdr' | 'amf_ue' | 'mme_ue',
|
||||
item: any
|
||||
) {
|
||||
let v: false | Record<string, any> = false;
|
||||
if (type === 'ims_cdr') {
|
||||
v = cdrEventIMSParse(item);
|
||||
}
|
||||
if (type === 'amf_ue') {
|
||||
v = ueEventAMFParse(item);
|
||||
}
|
||||
if (type === 'mme_ue') {
|
||||
v = ueEventMMEParse(item);
|
||||
}
|
||||
|
||||
if (v) {
|
||||
eventData.value.unshift(v);
|
||||
eventTotal.value += 1;
|
||||
eventId.value = v.eId;
|
||||
await new Promise(resolve => setTimeout(resolve, 800));
|
||||
if (eventData.value.length > 20) {
|
||||
eventData.value.pop();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**CDR+UE事件数据 */
|
||||
export const eventData = ref<Record<string, any>[]>([]);
|
||||
/**CDR+UE事件总量 */
|
||||
export const eventTotal = ref<number>(0);
|
||||
/**CDR/UE事件推送id */
|
||||
export const eventId = ref<string>('');
|
||||
|
||||
/**属性复位 */
|
||||
export function userActivityReset() {
|
||||
eventData.value = [];
|
||||
eventTotal.value = 0;
|
||||
eventId.value = '';
|
||||
}
|
||||
204
practical_training/views/dashboard/overview/hooks/useWS.ts
Normal file
204
practical_training/views/dashboard/overview/hooks/useWS.ts
Normal file
@@ -0,0 +1,204 @@
|
||||
import { RESULT_CODE_ERROR } from '@/constants/result-constants';
|
||||
import { OptionsType, WS } from '@/plugins/ws-websocket';
|
||||
import { onBeforeUnmount, onMounted } from 'vue';
|
||||
import {
|
||||
eventListParse,
|
||||
eventItemParseAndPush,
|
||||
userActivityReset,
|
||||
} from './useUserActivity';
|
||||
import {
|
||||
upfTotalFlow,
|
||||
upfTFParse,
|
||||
upfFlowParse,
|
||||
upfTotalFlowReset,
|
||||
} from './useUPFTotalFlow';
|
||||
import { topologyReset, neStateParse } from './useTopology';
|
||||
import PQueue from 'p-queue';
|
||||
|
||||
/**websocket连接 */
|
||||
export default function useWS() {
|
||||
const ws = new WS();
|
||||
const queue = new PQueue({ concurrency: 1, autoStart: true });
|
||||
|
||||
/**发消息 */
|
||||
function wsSend(data: Record<string, any>) {
|
||||
ws.send(data);
|
||||
}
|
||||
|
||||
/**接收数据后回调 */
|
||||
function wsMessage(res: Record<string, any>) {
|
||||
// console.log(res);
|
||||
const { code, requestId, data } = res;
|
||||
if (code === RESULT_CODE_ERROR) {
|
||||
console.warn(res.msg);
|
||||
return;
|
||||
}
|
||||
|
||||
// 网元状态
|
||||
if (requestId && requestId.startsWith('neState')) {
|
||||
const neType = requestId.split('_')[1];
|
||||
neStateParse(neType, data);
|
||||
return;
|
||||
}
|
||||
|
||||
// 普通信息
|
||||
switch (requestId) {
|
||||
// AMF_UE会话事件
|
||||
case 'amf_1010':
|
||||
if (Array.isArray(data.rows)) {
|
||||
eventListParse('amf_ue', data);
|
||||
}
|
||||
break;
|
||||
// MME_UE会话事件
|
||||
case 'mme_1011_001':
|
||||
if (Array.isArray(data.rows)) {
|
||||
eventListParse('mme_ue', data);
|
||||
}
|
||||
break;
|
||||
// IMS_CDR会话事件
|
||||
case 'ims_1005_001':
|
||||
if (Array.isArray(data.rows)) {
|
||||
eventListParse('ims_cdr', data);
|
||||
}
|
||||
break;
|
||||
//UPF-总流量数
|
||||
case 'upf_001_0':
|
||||
upfTFParse('0', data);
|
||||
break;
|
||||
case 'upf_001_7':
|
||||
upfTFParse('7', data);
|
||||
break;
|
||||
case 'upf_001_30':
|
||||
upfTFParse('30', data);
|
||||
break;
|
||||
}
|
||||
|
||||
// 订阅组信息
|
||||
if (!data?.groupId) {
|
||||
return;
|
||||
}
|
||||
switch (data.groupId) {
|
||||
// kpiEvent 指标UPF
|
||||
case '12_001':
|
||||
if (data.data) {
|
||||
upfFlowParse(data.data);
|
||||
}
|
||||
break;
|
||||
// AMF_UE会话事件
|
||||
case '1010':
|
||||
if (data.data) {
|
||||
queue.add(() => eventItemParseAndPush('amf_ue', data.data));
|
||||
}
|
||||
break;
|
||||
// MME_UE会话事件
|
||||
case '1011_001':
|
||||
if (data.data) {
|
||||
queue.add(() => eventItemParseAndPush('mme_ue', data.data));
|
||||
}
|
||||
break;
|
||||
// IMS_CDR会话事件
|
||||
case '1005_001':
|
||||
if (data.data) {
|
||||
queue.add(() => eventItemParseAndPush('ims_cdr', data.data));
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**UPF-总流量数 发消息*/
|
||||
function upfTFSend(day: '0' | '7' | '30') {
|
||||
// 请求标记检查避免重复发送
|
||||
if (upfTotalFlow.value[day].requestFlag) {
|
||||
return;
|
||||
}
|
||||
upfTotalFlow.value[day].requestFlag = true;
|
||||
|
||||
ws.send({
|
||||
requestId: `upf_001_${day}`,
|
||||
type: 'upf_tf',
|
||||
data: {
|
||||
neType: 'UPF',
|
||||
neId: '001',
|
||||
day: Number(day),
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
/**userActivitySend 用户行为事件基础列表数据 发消息*/
|
||||
function userActivitySend() {
|
||||
// AMF_UE会话事件
|
||||
ws.send({
|
||||
requestId: 'amf_1010',
|
||||
type: 'amf_ue',
|
||||
data: {
|
||||
neType: 'AMF',
|
||||
neId: '001',
|
||||
sortField: 'timestamp',
|
||||
sortOrder: 'desc',
|
||||
pageNum: 1,
|
||||
pageSize: 20,
|
||||
},
|
||||
});
|
||||
// MME_UE会话事件
|
||||
ws.send({
|
||||
requestId: 'mme_1011_001',
|
||||
type: 'mme_ue',
|
||||
data: {
|
||||
neType: 'MME',
|
||||
neId: '001',
|
||||
sortField: 'timestamp',
|
||||
sortOrder: 'desc',
|
||||
pageNum: 1,
|
||||
pageSize: 20,
|
||||
},
|
||||
});
|
||||
// IMS_CDR会话事件
|
||||
ws.send({
|
||||
requestId: 'ims_1005_001',
|
||||
type: 'ims_cdr',
|
||||
data: {
|
||||
neType: 'IMS',
|
||||
neId: '001',
|
||||
recordType: 'MOC',
|
||||
sortField: 'timestamp',
|
||||
sortOrder: 'desc',
|
||||
pageNum: 1,
|
||||
pageSize: 20,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
const options: OptionsType = {
|
||||
url: '/ws',
|
||||
params: {
|
||||
/**订阅通道组
|
||||
*
|
||||
* 指标UPF (GroupID:12_neId)
|
||||
* AMF_UE会话事件(GroupID:1010)
|
||||
* MME_UE会话事件(GroupID:1011_neId)
|
||||
* IMS_CDR会话事件(GroupID:1005_neId)
|
||||
*/
|
||||
subGroupID: '12_001,1010,1011_001,1005_001',
|
||||
},
|
||||
onmessage: wsMessage,
|
||||
onerror: (ev: any) => {
|
||||
console.error(ev);
|
||||
},
|
||||
};
|
||||
ws.connect(options);
|
||||
});
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
ws.close();
|
||||
userActivityReset();
|
||||
upfTotalFlowReset();
|
||||
topologyReset();
|
||||
});
|
||||
|
||||
return {
|
||||
wsSend,
|
||||
userActivitySend,
|
||||
upfTFSend,
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user