
文章目录
一、背景
XNMS(Extended Network Management System,增强型网络管理系统)是一款远程监控和管理常规中转台的软件。中转台是系统的核心设备,所有业务都通过其进行中转。因此,只要对中转台进行监控,就能全面掌握系统的运行状况。而中转台通常部署室外,容易受到日晒雨淋等自然条件影响,造成设备损坏。为保证通讯系统正常运行,工作人员需要对中转台进行实时监控,发现中转台的异常问题,从而采取相关措施进行补救。
通过XNMS软件,工作人员可实时监控常规中转台的各项参数和告警情况,对异常问题进行排查;还可以查询或统计某时间段内中转台或终端的业务,从而全面了解常规系统的运行状况。
- 拓扑图:
- 查看已注册到XNMS服务器的中转台数量,包括全部中转台、告警中转台、离线中转台和正常运行的中转台。
- 查看系统中各中转台的分布情况和运行状态。
- 查看某台中转台的信息,包括提示信息、监控告警信息和设备参数。
二、页面
常规模式


XPT模式


三、代码
TopologyView.vue
java
<template>
<layout_2 style="position: relative">
<div id="electronic_map"></div>
<div class="transparent-box"></div>
<div class="transparent-box-bottom"></div>
<div class="--search-line in-map-tl">
<div>
<div class="key">{{ $t("TreeViewRepeater") }} :</div>
<div class="val">
<a-tree-select
class="arco-tree-select --arco-select"
style="width: 230px"
:field-names="{
key: 'serialNo',
title: 'name',
children: 'children',
}"
:data="treeSelectNodeData"
:multiple="true"
:tree-checkable="true"
tree-checked-strategy="child"
:max-tag-count="1"
v-model:model-value="param.repeaterSNs"
>
</a-tree-select>
</div>
</div>
<a-button class="huge" @click="queryTopoView" type="primary">
<template #icon> <icon-search size="18" /> </template>{{ $t("Query") }}
</a-button>
</div>
<div id="topo_view" class="topo-view"></div>
<statistic-repeater
ref="statisticRepeaterRef"
@click-transfer-desk="getSiteInfoByTypeFunction"
></statistic-repeater>
</layout_2>
<base-info
v-model:visible="baseInfoShow"
v-if="baseInfoShow"
ref="baseInfoRef"
@refresh-flag="successFresh"
:rpt-state="rptState"
></base-info>
<site-alias v-model:visible="siteAliasShow" v-if="siteAliasShow" :rpt-state="rptState" ></site-alias>
<monitor-alarm
v-model:visible="monitorAlarmShow"
v-if="monitorAlarmShow"
ref="monitorAlarmRef"
:rpt-state="rptState"
></monitor-alarm>
<device-param
v-model:visible="deviceParamShow"
v-if="deviceParamShow"
ref="deviceParamRef"
:rpt-state="rptState"
></device-param>
<a-popover
arrow-class="--arrow-none"
v-model:popup-visible="popoverVisible"
:style="overlayStyle"
content-class="--arco-popover-popup-content"
>
<template #content>
<div class="dropdownBasic">
<div class="dropdownItemitem">
<div class="baselayersFWrapper">
<svg-loader class="baselayersFIcon" name="base-info"></svg-loader>
</div>
<div class="text" @click="openBaseInfo">{{ $t("BaseInfo") }}</div>
</div>
<div class="dropdownItemitem">
<div class="baselayersFWrapper">
<svg-loader
class="baselayersFIcon"
name="alarm-monitor"
></svg-loader>
</div>
<div class="text" @click="openMonitorAlarm">
{{ $t("MonitoringAlarm") }}
</div>
</div>
<div class="dropdownItemitem">
<div class="baselayersFWrapper">
<svg-loader
class="baselayersFIcon"
name="device-param"
></svg-loader>
</div>
<div class="text" @click="openDeviceParam">
{{ $t("ParameterSetting") }}
</div>
</div>
</div>
</template>
</a-popover>
<a-modal
v-model:visible="showVisible"
@ok="handleOk"
:ok-text="$t('ButtonOK')"
:hide-cancel="true"
>
<template #title>
{{ $t("Prompt") }}
</template>
<div>{{ $t("RepeaterOffline") }}</div>
</a-modal>
</template>
<script setup>
import * as echarts from "echarts";
import networkImg from "@/assets/img/network.png";
import siteImg from "@/assets/img/site.png";
import deviceImg0 from "@/assets/img/device-0.png";
import deviceImg1 from "@/assets/img/device-1.png";
import deviceImg2 from "@/assets/img/device-2.png";
import deviceImgNote0 from "@/assets/img/device-note-0.png";
import deviceImgNote1 from "@/assets/img/device-note-1.png";
import deviceImgNote2 from "@/assets/img/device-note-2.png";
import {
inject,
onMounted,
onUnmounted,
reactive,
ref,
computed,
nextTick,
provide,
} from "vue";
import { getSiteInfo, getSiteInfoByType } from "@/views/pages/system/system";
import { qryTransferNodeList } from "@/views/pages/topology/_request";
import { getServiceHealth} from "@/views/pages/device/_request";
import { queryButtonValue } from "@/views/pages/_common/enum";
import Layout_2 from "@/views/pages/_common/layout_2.vue";
import BaseInfo from "./topologyView/BaseInfo.vue";
import SiteAlias from "./topologyView/SiteAlias.vue";
import MonitorAlarm from "./topologyView/MonitorAlarm.vue";
import StatisticRepeater from "./StatisticRepeater.vue";
import DeviceParam from "./topologyView/DeviceParam.vue";
import { getSystemType } from "@/views/login/_request";
import { commonResponse } from "@/views/pages/_common";
import { useStore } from "@/stores";
const t = inject("t");
const datasource = ref({});
const treeSelectNodeData = ref([]);
const deviceParamRef = ref(null);
const monitorAlarmRef = ref(null);
const statisticRepeaterRef = ref(null);
const userName = ref(null);
const selectTopoNode = ref(null);
const param = reactive({
repeaterSNs: [],
});
const baseInfoShow = ref(false);
const sipLoading = ref(true);
const siteAliasShow = ref(false);
const monitorAlarmShow = ref(false);
const deviceParamShow = ref(false);
const showVisible = ref(false);
const baseInfoRef = ref(null);
const versionInfo = ref("");
let repeaterStateMap = new Map();
const alarmRepeaterCountValue = ref(0);
const unconnectedRepeaterCountValue = ref(0);
const normalRepeaterCountValue = ref(0);
const rptState = ref(null);
const openBaseInfo = () => {
baseInfoShow.value = true;
sipLoading.value = true;
nextTick(() => {
const repeater = useStore().websocketRepeaterList.find(
(repeater) => repeater.serialNo === selectTopoNode.value.serialNo
);
rptState.value = (
(selectTopoNode.value.symbol.includes("device-1")
? 1
: selectTopoNode.value.symbol.includes("device-2")
? 2
: 0) || 0
) + "," + selectTopoNode.value.serialNo + "," + selectTopoNode.value.name;
const selectRptState = repeater ? repeater.rptState : null;
baseInfoRef.value.setData(selectTopoNode.value.serialNo, selectRptState);
});
};
const getServiceHealthData = async () => {
getServiceHealth().then((response) => {
commonResponse({
response,
onError: () => {
console.log('TopologyView-服务连接失败');
},
});
});
};
const handleOk = () => {
showVisible.value = false;
};
const successFresh = () => {
baseInfoShow.value = false;
getSiteInfoFunction();
};
const openMonitorAlarm = () => {
if (selectTopoNode.value.symbol.includes("device-0")) {
showVisible.value = true;
return;
}
monitorAlarmShow.value = true;
nextTick(() => {
rptState.value = (
(selectTopoNode.value.symbol.includes("device-1")
? 1
: selectTopoNode.value.symbol.includes("device-2")
? 2
: 0) || 0
) + "," + selectTopoNode.value.serialNo + "," + selectTopoNode.value.name;
monitorAlarmRef.value.setRssiId(selectTopoNode.value.serialNo);
});
};
const openDeviceParam = () => {
if (selectTopoNode.value.symbol.includes("device-0")) {
showVisible.value = true;
return;
}
deviceParamShow.value = true;
nextTick(() => {
rptState.value = (
(selectTopoNode.value.symbol.includes("device-1")
? 1
: selectTopoNode.value.symbol.includes("device-2")
? 2
: 0) || 0
) + "," + selectTopoNode.value.serialNo + "," + selectTopoNode.value.name;
deviceParamRef.value.baseSettingXptFunction(selectTopoNode.value.serialNo, deviceParamShow.value, selectTopoNode.value.name);
});
};
let webSocket = null;
const connectWebSocket = (url) => {
// if (webSocket) webSocket.close();
if (
webSocket &&
(webSocket.readyState === WebSocket.OPEN ||
webSocket.readyState === WebSocket.CONNECTING)
) {
return;
}
webSocket = new WebSocket(url);
webSocket.onopen = () => console.log("TopologyView.vue WebSocket已连接");
webSocket.onmessage = handleWebSocketMessage;
webSocket.onclose = () => console.log("TopologyView.vue WebSocket已关闭");
webSocket.onerror = (error) =>
console.error("TopologyView.vue WebSocket错误:", error);
};
// WebSocket处理逻辑
const handleWebSocketMessage = (event) => {
try {
const message = JSON.parse(event.data);
const messageType = message?.type ?? "";
if (messageType === "heartbeat") {
getServiceHealthData();
return;
}
const index = useStore().websocketRepeaterList.findIndex(
(item) => item.serialNo === message.serialNo
);
if (index !== -1) {
useStore().websocketRepeaterList[index] = message;
} else {
useStore().websocketRepeaterList.push(message);
}
const receiveRepeaterId = message.repeaterId;
const receiveRptState = message.rptState;
const receiveSerialNo = message.serialNo;
const note1 = message.note1;
if (
datasource.value === undefined ||
datasource.value === null ||
(datasource.value === "object" &&
Object.keys(datasource.value).length === 0) ||
datasource.value?.children?.length <= 0
) {
console.log("queryTopoView");
queryTopoView();
}
let networkChildren = datasource.value.children;
if (versionInfo.value == 1 || versionInfo.value == 2) {
for (let i = 0; i < networkChildren.length; i++) {
let xptChildren = networkChildren[i].children;
for (let j = 0; j < xptChildren.length; j++) {
let siteChildren = xptChildren[j].children;
for (let k = 0; k < siteChildren.length; k++) {
if (siteChildren[k].repeaterId == receiveRepeaterId) {
repeaterStateMap.set(receiveSerialNo, receiveRptState);
if (receiveRptState == 3) {
siteChildren[k].symbol = note1 ? `image://${deviceImgNote0}` : `image://${deviceImg0}`;
} else if (receiveRptState == 1) {
siteChildren[k].symbol = note1 ? `image://${deviceImgNote1}` : `image://${deviceImg1}`;
} else if (receiveRptState == 2) {
siteChildren[k].symbol = note1 ? `image://${deviceImgNote2}` : `image://${deviceImg2}`;
} else {
siteChildren[k].symbol = note1 ? `image://${deviceImgNote0}` : `image://${deviceImg0}`;
}
}
let masterChildren = siteChildren[k].children;
for (let l = 0; l < masterChildren.length; l++) {
if (masterChildren[l].repeaterId == receiveRepeaterId) {
repeaterStateMap.set(receiveSerialNo, receiveRptState);
if (receiveRptState == 3) {
masterChildren[l].symbol = note1 ? `image://${deviceImgNote0}` : `image://${deviceImg0}`;
} else if (receiveRptState == 1) {
masterChildren[l].symbol = note1 ? `image://${deviceImgNote1}` : `image://${deviceImg1}`;
} else if (receiveRptState == 2) {
masterChildren[l].symbol = note1 ? `image://${deviceImgNote2}` : `image://${deviceImg2}`;
} else {
masterChildren[l].symbol = note1 ? `image://${deviceImgNote0}` : `image://${deviceImg0}`;
}
}
}
}
}
}
} else if (versionInfo.value == 0) {
for (let i = 0; i < networkChildren.length; i++) {
let siteChildren = networkChildren[i].children;
for (let k = 0; k < siteChildren.length; k++) {
if (siteChildren[k].repeaterId == receiveRepeaterId) {
repeaterStateMap.set(receiveSerialNo, receiveRptState);
if (receiveRptState == 3) {
siteChildren[k].symbol = note1 ? `image://${deviceImgNote0}` : `image://${deviceImg0}`;
} else if (receiveRptState == 1) {
siteChildren[k].symbol = note1 ? `image://${deviceImgNote1}` : `image://${deviceImg1}`;
} else if (receiveRptState == 2) {
siteChildren[k].symbol = note1 ? `image://${deviceImgNote2}` : `image://${deviceImg2}`;
} else {
siteChildren[k].symbol = note1 ? `image://${deviceImgNote0}` : `image://${deviceImg0}`;
}
}
let masterChildren = siteChildren[k].children;
for (let l = 0; l < masterChildren.length; l++) {
if (masterChildren[l].repeaterId == receiveRepeaterId) {
repeaterStateMap.set(receiveSerialNo, receiveRptState);
if (receiveRptState == 3) {
masterChildren[l].symbol = note1 ? `image://${deviceImgNote0}` : `image://${deviceImg0}`;
} else if (receiveRptState == 1) {
masterChildren[l].symbol = note1 ? `image://${deviceImgNote1}` : `image://${deviceImg1}`;
} else if (receiveRptState == 2) {
masterChildren[l].symbol = note1 ? `image://${deviceImgNote2}` : `image://${deviceImg2}`;
} else {
masterChildren[l].symbol = note1 ? `image://${deviceImgNote0}` : `image://${deviceImg0}`;
}
}
}
}
}
}
nextTick(()=>{
countRepeaterStateMap();
statisticRepeaterRef.value?.updateStatistic(alarmRepeaterCountValue.value, unconnectedRepeaterCountValue.value, normalRepeaterCountValue.value);
})
// 更新图表选项
updateChart();
} catch (error) {
console.error("TopologyView.vue WebSocket消息处理错误:", error);
}
};
const countRepeaterStateMap = () => {
alarmRepeaterCountValue.value = 0;
unconnectedRepeaterCountValue.value = 0;
normalRepeaterCountValue.value = 0;
const stateMapping = {
1: normalRepeaterCountValue,
2: alarmRepeaterCountValue,
3: unconnectedRepeaterCountValue,
};
for (const rptState of repeaterStateMap.values()) {
const stateType = stateMapping[rptState];
if (stateType) {
stateType.value++;
} else {
console.warn(`未知的中转台状态: ${rptState}`);
}
}
};
const queryTopoView = () => {
getSiteInfoFunction();
};
const getTransferNodeList = () => {
const principal = sessionStorage.getItem("principal");
if (principal) {
const principalObject = JSON.parse(principal);
qryTransferNodeList({ userName: principalObject.userName }).then(
(response) => {
if (response.data) {
treeSelectNodeData.value = [
{
serialNo: "-1",
name: t(queryButtonValue[22]),
children: response.data,
},
];
repeaterStateMap = new Map();
response.data.forEach(ontLayer => {
if (ontLayer.serialNo) repeaterStateMap.set(ontLayer.serialNo, ontLayer.rptState);
if (ontLayer.children?.length) {
ontLayer.children.forEach(twoLayer => {
if (twoLayer.serialNo) repeaterStateMap.set(twoLayer.serialNo, twoLayer.rptState);
if (twoLayer.children?.length) {
twoLayer.children.forEach(threeLayer => {
if (threeLayer.children?.length) {
if (threeLayer.serialNo) repeaterStateMap.set(threeLayer.serialNo, threeLayer.rptState);
}
})
}
})
}
})
}
}
);
}
};
const transform = (node) => {
const transformedNode = {
name: "",
children: [],
};
if (node.name != null || node.name != "") {
transformedNode.name = node.name;
}
if (node.repeaterId != null || node.repeaterId != "") {
transformedNode.repeaterId = node.repeaterId;
transformedNode.serialNo = node.serialNo;
transformedNode.siteId = node.siteId;
transformedNode.siteIndex = node.siteIndex;
}
if (node.repeaterId?.includes("network")) {
transformedNode.symbol = `image://${networkImg}`;
transformedNode.symbolSize = [48, 48];
} else if (
node.repeaterId?.includes("site") ||
node.repeaterId?.includes("xpt")
) {
transformedNode.symbol = `image://${siteImg}`;
transformedNode.symbolSize = [36, 48];
} else {
if (node.rptState == 3) {
if (node.note) {
transformedNode.symbol = `image://${deviceImgNote0}`;
} else {
transformedNode.symbol = `image://${deviceImg0}`;
}
transformedNode.symbolSize = [45, 31];
} else if (node.rptState == 1) {
if (node.note) {
transformedNode.symbol = `image://${deviceImgNote1}`;
} else {
transformedNode.symbol = `image://${deviceImg1}`;
}
transformedNode.symbolSize = [45, 31];
} else if (node.rptState == 2) {
if (node.note) {
transformedNode.symbol = `image://${deviceImgNote2}`;
} else {
transformedNode.symbol = `image://${deviceImg2}`;
}
transformedNode.symbolSize = [45, 31];
} else {
if (node.note) {
transformedNode.symbol = `image://${deviceImgNote0}`;
} else {
transformedNode.symbol = `image://${deviceImg0}`;
}
transformedNode.symbolSize = [45, 31];
}
}
// 递归处理子节点
if (node.children && node.children.length > 0) {
transformedNode.children = node.children.map(transform);
}
return transformedNode;
};
const getSiteInfoByTypeFunction = (index) => {
getSiteInfoByType({ rptState: index }).then((response) => {
if (response.data) {
datasource.value = transform(response.data);
updateChart();
}
});
};
const getSiteInfoFunction = async () => {
await getSiteInfo({ serialNoArr: param.repeaterSNs }).then((response) => {
if (response.data) {
const datasourceCopy = transform(response.data);
if (datasourceCopy.children?.length) {
datasourceCopy.children.forEach(ontLayer => {
if (ontLayer.children?.length) {
ontLayer.children.forEach(twoLayer => {
if (twoLayer.children?.length) {
twoLayer.children.forEach(threeLayer => {
if (threeLayer.children?.length) {
threeLayer.children.sort((a, b) => {
const aIndex = a.siteIndex || 0;
const bIndex = b.siteIndex || 0;
return aIndex - bIndex;
});
}
})
}
})
}
})
}
datasource.value = datasourceCopy;
updateChart();
}
});
};
// 更新图表的函数
const updateChart = () => {
var chartDom = document.querySelector("#topo_view");
if (chartDom != null) {
var myChart = echarts.init(chartDom);
if (myChart) {
myChart.dispose();
}
myChart = echarts.init(chartDom);
const option = {
tooltip: {
trigger: "item",
triggerOn: "mousemove",
},
series: [
{
type: "tree",
id: 0,
name: "tree1",
data: [datasource.value],
top: "10%",
left: "8%",
bottom: "22%",
right: "20%",
edgeForkPosition: "63%",
initialTreeDepth: 5,
lineStyle: {
width: 4,
color: "#6AAAFF",
},
label: {
backgroundColor: "#fff",
position: "left",
verticalAlign: "middle",
align: "right",
},
leaves: {
label: {
position: "right",
verticalAlign: "middle",
align: "left",
},
},
emphasis: {
focus: "descendant",
},
expandAndCollapse: true,
animationDuration: 550,
animationDurationUpdate: 750,
},
],
};
// 更新图表
option && myChart.setOption(option);
myChart.on("contextmenu", handleNodeClick);
}
};
const popoverVisible = ref(false);
const popoverPosition = ref({ top: 0, left: 0 });
const handleNodeClick = (params) => {
event.preventDefault();
selectTopoNode.value = params.data;
let repeaterId = params.data.repeaterId;
if (
(!repeaterId.includes("network") &&
!repeaterId.includes("xpt_") &&
!repeaterId.includes("site_") &&
!repeaterId.includes("virtual")) ||
(!repeaterId.includes("network") &&
params.data.children &&
params.data.children.length === 0)
) {
// 计算弹出框的位置
const chartDom = document.querySelector("#topo_view");
const chartRect = chartDom.getBoundingClientRect();
const offsetX = params.event.offsetX;
const offsetY = params.event.offsetY;
popoverPosition.value = {
top: chartRect.top + offsetY + window.scrollY,
left: chartRect.left + offsetX + window.scrollX,
};
popoverVisible.value = true;
}
};
const overlayStyle = computed(() => ({
position: "absolute",
top: `${popoverPosition.value.top}px`,
left: `${popoverPosition.value.left}px`,
zIndex: 1000, // 确保弹出框在最上层
}));
const getSystemTypeData = async () => {
await getSystemType().then((response) => {
commonResponse({
response,
onSuccess: () => {
versionInfo.value = response.data;
},
});
});
};
onMounted(async () => {
await getSiteInfoFunction();
const principal = sessionStorage.getItem("principal");
if (principal) {
const principalObject = JSON.parse(principal);
userName.value = principalObject.userName;
}
updateChart();
try {
if (process.env.NODE_ENV === "development") {
connectWebSocket("ws://localhost:62000/topoView");
} else {
connectWebSocket(`ws://${window.location.host}/ws/topoView`);
}
} catch (error) {
console.error("TopologyView.vue WebSocket连接消息处理错误:", error);
connectWebSocket("/ws/topoView");
console.error("TopologyView.vue WebSocket连接 ...");
}
});
onUnmounted(() => {
// if (webSocket) webSocket.close();
if (
webSocket &&
(webSocket.readyState === WebSocket.OPEN ||
webSocket.readyState === WebSocket.CONNECTING)
) {
webSocket.close();
}
});
const init = () => {
getTransferNodeList();
getSystemTypeData();
};
init();
</script>
<style scoped lang="less">
.topo-view {
height: 100%;
background-image: url("@/assets/img/背景.png");
}
.transparent-box {
position: absolute;
z-index: 400;
pointer-events: none;
background-image: linear-gradient(to bottom, #ffffff, transparent);
opacity: 0.8;
top: 0;
height: 80px;
width: 100%;
}
.transparent-box-bottom {
position: absolute;
z-index: 400;
pointer-events: none;
background-image: linear-gradient(to top, #ffffff, transparent);
opacity: 0.8;
bottom: 0;
height: 80px;
width: 100%;
}
.in-map-tl {
position: absolute;
z-index: 401;
left: 20px;
top: 17px;
}
/* 确保弹出框内容样式 */
.baselayersFIcon {
width: 16px;
position: relative;
height: 16px;
overflow: hidden;
flex-shrink: 0;
}
.baselayersFWrapper {
width: 24px;
border-radius: 80px;
background-color: #fff;
border: 1px solid #dfdfdf;
box-sizing: border-box;
height: 24px;
display: flex;
flex-direction: row;
align-items: center;
justify-content: center;
padding: 8px;
}
.text {
flex: 1;
position: relative;
line-height: 22px;
display: inline-block;
height: 22px;
//鼠标滑动经过时字体加粗
cursor: pointer;
}
.dropdownItemitem {
align-self: stretch;
height: 36px;
display: flex;
flex-direction: row;
align-items: center;
justify-content: flex-start;
padding: 5px 12px 5px 16px;
box-sizing: border-box;
gap: 8px;
}
.dropdownItemitem:hover {
background: #e8f7ff;
font-weight: bold;
color: #3348ff;
}
.text2 {
flex: 1;
position: relative;
line-height: 22px;
font-weight: 600;
display: inline-block;
height: 22px;
}
.dropdownItemitem2 {
align-self: stretch;
background-color: #e8f7ff;
height: 36px;
display: flex;
flex-direction: row;
align-items: center;
justify-content: flex-start;
padding: 5px 12px 5px 16px;
box-sizing: border-box;
gap: 8px;
color: #3348ff;
}
.frameChild {
width: 16px;
position: relative;
height: 16px;
}
.dropdownBasic {
width: 100%;
position: relative;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 8px 0px;
text-align: left;
font-size: 14px;
color: #202b40;
font-family: "PingFang SC";
}
</style>
<style>
.--arrow-none {
display: none;
}
.--arco-popover-popup-content {
box-shadow: 0px 1px 4px rgba(0, 0, 0, 0.1), 0px 8px 24px rgba(0, 0, 0, 0.1);
border-radius: 12px;
background: linear-gradient(180deg, #eff8ff, #fff);
border: 1.5px solid #fff;
box-sizing: border-box;
width: 170px;
}
/* .arco-popover-content {
margin-left: -20px;
} */
</style>
BaseInfo.vue
java
<template>
<a-modal :width="480" :align-center="true">
<template #title>
<div class="title">{{$t('BaseInfo')}}</div>
</template>
<a-spin class="frameParent" :size="80" :loading="uploadeLoading" :tip="$t('Loading')">
<div class="frameParent">
<div class="frameGroup">
<div class="parent">
<div class="label">{{$t('RadioAlias')}}:</div>
<div class="value">{{param.radioAlias}}</div>
</div>
<div class="parent">
<div class="label">{{$t('SerialNo')}}:</div>
<div class="value">{{param.serialNo}}</div>
</div>
<div class="parent">
<div class="value" style="color: #9da2c1" v-show="param.status == 0">{{$t('StateOffLine')}}</div>
<div class="value" v-show="param.status == 1">{{$t('StateOnline')}}</div>
<div class="value" style="color: tomato" v-show="param.status == 2">{{$t('StateAlarm')}}</div>
<div class="value" style="color: #9da2c1" v-show="param.status == 3">{{$t('StateOffLine')}}</div>
</div>
<div class="groupWrapper">
<div class="remark-div">
<div class="frameDiv">
<div class="remark-label">{{$t('Remarks')}}:</div>
<a-button class="iconBasepencilSquareF huge" @click="clickEidtRemark" style="margin-left: 30px">
<template #icon>
<icon-edit size="16"/>
</template>
</a-button>
<div style="word-wrap:break-word !important;" class="remark-value" v-show="showEidtRemark">{{param.remark}}</div>
<a-textarea max-length="108" allow-clear v-show="!showEidtRemark" v-model="param.remark"/>
</div>
</div>
</div>
</div>
</div>
</a-spin>
<template #footer>
<div v-show="!showEidtRemark">
<a-button @click="close()">{{ $t('cancelLabel') }}</a-button>
<a-button class="button-group" type="primary" @click="save()">{{ $t('ExportTaskEnter') }}</a-button>
</div>
</template>
</a-modal>
</template>
<script setup>
import {defineEmits, defineExpose, inject, reactive, ref} from "vue";
import {queryDevice, updateRepeaterNote} from "@/views/pages/system/system";
import {commonResponse} from "@/views/pages/_common";
import {Message} from "@arco-design/web-vue";
const uploadeLoading = ref(false)
const t = inject('t')
const showEidtRemark = ref(true);
const emits = defineEmits(['refresh-flag'])
const param = reactive({
radioAlias: "",
serialNo: "",
status: "",
remark: "",
})
const save = () => {
updateRepeaterNote({"content": param.remark,"index": 1,"serialNo": param.serialNo}).then(response => {
commonResponse({
response,
onSuccess: () => {
Message.success(t('PasswordChangeSuccess'));
emits('refresh-flag', 1)
}
})
})
}
const close = () => {
emits('refresh-flag', 1)
}
const clickEidtRemark = () => {
showEidtRemark.value = false;
}
const setData = async (serialNo, rptState) => {
uploadeLoading.value = true;
await queryDevice(serialNo).then(response => {
commonResponse({
response,
onSuccess: () => {
let data = response.data;
param.radioAlias = data.alias;
param.serialNo = data.serialNo;
param.status = rptState == null ? data.rptState : rptState;
if (data.note1 == undefined || data.note1 == null || data.note1 == "") {
param.remark = "";
} else {
param.remark = data.note1;
}
}
})
})
uploadeLoading.value = false;
}
defineExpose({
setData
})
</script>
<style scoped lang="less">
.label {
position: relative;
line-height: 22px;
}
.value {
width: 400px;
position: relative;
line-height: 22px;
color: #041126;
display: inline-block;
}
.parent {
align-self: stretch;
display: flex;
flex-direction: column;
align-items: flex-start;
justify-content: center;
gap: 2px;
}
.iconBasepencilSquareF {
position: absolute;
top: 0px;
left: 40px;
cursor: pointer;
width: 22px;
height: 22px;
border: none;
}
.remark-label {
align-self: stretch;
position: relative;
line-height: 22px;
}
.remark-value {
align-self: stretch;
position: relative;
line-height: 22px;
color: #041126;
}
.frameDiv {
position: absolute;
top: 0px;
left: 0px;
width: 400px;
display: flex;
flex-direction: column;
align-items: flex-start;
justify-content: flex-start;
gap: 2px;
}
.remark-div {
width: 400px;
position: relative;
height: 100px;
}
.groupWrapper {
align-self: stretch;
display: flex;
flex-direction: row;
align-items: center;
justify-content: flex-start;
}
.frameGroup {
flex: 1;
display: flex;
flex-direction: column;
align-items: flex-start;
justify-content: flex-start;
gap: 12px;
}
.frameParent {
width: 400px;
position: relative;
display: flex;
flex-direction: row;
align-items: flex-end;
justify-content: flex-start;
flex-wrap: wrap;
align-content: flex-end;
padding: 0px 20px;
box-sizing: border-box;
text-align: left;
font-size: 14px;
color: #778091;
font-family: 'PingFang SC';
}
.border-box {
border: 1px solid #ccc;
border-radius: 5px;
margin: 20px 0px;
width: 260px;
height: 100px;
}
.button-group {
margin-left: 10px;
}
</style>
StatisticRepeater.vue
java
<template>
<div class="in-map-rt">
<div class="card-item" :class="{active: activeKey === 0}" @click="clickTransferDesk(0)">
<div class="card-item-title">{{$t('AllRepeater')}}</div>
<div class="card-item-count">
{{param.allRepeaterCount}}
<div class="card-item-count-percentage"></div>
</div>
</div>
<div class="card-item" :class="{active: activeKey === 2}" @click="clickTransferDesk(2)">
<div class="card-item-title">{{$t('AlarmRepeater')}}</div>
<div class="card-item-count">
{{param.alarmRepeaterCount}}
<div class="card-item-count-percentage">{{param.alarmRepeaterPercent}}</div>
</div>
<div class="percentage-chart">
<a-progress :percent="0.2" :show-text="false" style="height: 6px" color="#eb4331" />
</div>
</div>
<div class="card-item" :class="{active: activeKey === 3}" @click="clickTransferDesk(3)">
<div class="card-item-title">{{$t('UnconnectedRepeater')}}</div>
<div class="card-item-count">
{{param.unconnectedRepeaterCount}}
<div class="card-item-count-percentage">{{param.unconnectedRepeaterPercent}}</div>
</div>
<div class="percentage-chart">
<a-progress :percent="0.2" :show-text="false" style="height: 6px" color="#959aa1" />
</div>
</div>
<div class="card-item" :class="{active: activeKey === 1}" @click="clickTransferDesk(1)">
<div class="card-item-title">{{$t('NormalRepeater')}}</div>
<div class="card-item-count">
{{param.normalRepeaterCount}}
<div class="card-item-count-percentage">{{param.normalRepeaterPercent}}</div>
</div>
<div class="percentage-chart">
<a-progress :percent="0.2" :show-text="false" style="height: 6px" color="#1bc6ff" />
</div>
</div>
</div>
</template>
<script setup>
import {reactive, ref, defineExpose, defineEmits} from "vue";
import {queryTypeCount} from "@/views/pages/system/system";
const activeKey = ref(0)
const emits = defineEmits([])
const param = reactive({
allRepeaterCount: 0,
alarmRepeaterCount: 0,
alarmRepeaterPercent: "0%",
unconnectedRepeaterCount: 0,
unconnectedRepeaterPercent: "0%",
normalRepeaterCount: 0,
normalRepeaterPercent: "0%",
})
const updateStatistic = (alarmRepeaterCount, offlineRepeaterCount, normalRepeaterCount) => {
param.alarmRepeaterCount = alarmRepeaterCount;
let alarmPercent = Math.floor((param.alarmRepeaterCount / param.allRepeaterCount) * 100);
param.unconnectedRepeaterCount = offlineRepeaterCount;
let offlinePercent = Math.floor((param.unconnectedRepeaterCount / param.allRepeaterCount) * 100);
param.unconnectedRepeaterPercent = offlinePercent + "%";
param.normalRepeaterCount = normalRepeaterCount;
if (normalRepeaterCount == 0) {
param.normalRepeaterPercent = "0%";
param.alarmRepeaterPercent = (100 - offlinePercent) + "%";
} else {
let totalPercent = alarmPercent + offlinePercent + normalRepeaterCount;
if (totalPercent < 100) {
normalRepeaterCount += (100 - totalPercent);
}
param.normalRepeaterPercent = normalRepeaterCount + "%";
param.alarmRepeaterPercent = alarmPercent + "%";
}
}
const clickTransferDesk = (index) => {
if (activeKey.value === index) {
activeKey.value = 0
} else {
activeKey.value = index
}
emits('click-transfer-desk', index)
emits('click', activeKey.value)
}
const queryTypeCountFunction = () => {
queryTypeCount().then(response => {
if (response.data) {
const arr = response.data;
let normalPercent = 0;
let alarmPercent = 0;
let offlinePercent = 0;
for (let i = 0; i < arr.length; i++) {
if (arr[i].type == 0) {
param.allRepeaterCount = arr[i].count;
} else if (arr[i].type == 1) {
param.normalRepeaterCount = arr[i].count;
if (param.allRepeaterCount > 0) {
normalPercent = Math.floor((param.normalRepeaterCount / param.allRepeaterCount) * 100);
} else {
normalPercent = 0;
}
} else if (arr[i].type == 2) {
param.alarmRepeaterCount = arr[i].count;
if (param.allRepeaterCount > 0) {
alarmPercent = Math.floor((param.alarmRepeaterCount / param.allRepeaterCount) * 100);
} else {
alarmPercent = 0;
}
} else {
param.unconnectedRepeaterCount = arr[i].count;
if (param.allRepeaterCount > 0) {
offlinePercent = Math.floor((param.unconnectedRepeaterCount / param.allRepeaterCount) * 100);
} else {
offlinePercent = 0;
}
}
}
param.alarmRepeaterPercent = alarmPercent + "%";
param.unconnectedRepeaterPercent = offlinePercent + "%";
if (normalPercent == 0) {
param.normalRepeaterPercent = "0%";
} else {
let totalPercent = alarmPercent + offlinePercent + normalPercent;
if (totalPercent < 100) {
normalPercent += (100 - totalPercent);
}
param.normalRepeaterPercent = normalPercent + "%";
}
}
})
}
defineExpose({
updateStatistic,
queryTypeCountFunction,
})
const init = () => {
queryTypeCountFunction();
}
init()
</script>
<style scoped lang="less">
.in-map-rt {
position: absolute;
display: flex;
flex-direction: column;
gap: 20px;
z-index: 401;
top: 16px;
right: 20px;
.card-item {
box-sizing: border-box;
padding: 16px;
border-radius: 12px;
background: #F7F9FC;
box-shadow: 0px 3px 6px 0px rgba(193, 203, 214, 0.70);
width: 150px;
cursor: pointer;
&.active {
border-radius: 12px;
border: 2px solid #7FAFFF;
padding: 14px;
background: #FAFCFF;
box-shadow: 0px 3px 6px 0px rgba(193, 203, 214, 0.70);
}
&-title {
color: #202B40;
font-family: "PingFang SC";
font-size: 14px;
font-style: normal;
font-weight: 400;
line-height: 22px;
}
&-count {
position: relative;
margin-top: 4px;
height: 48px;
color: #202B40;
font-family: Roboto;
font-size: 32px;
font-style: normal;
font-weight: 600;
line-height: 48px;
&-percentage {
position: absolute;
top: 19px;
right: 0;
color: #A7B1C6;
text-align: right;
font-family: "PingFang SC";
font-size: 14px;
font-style: normal;
font-weight: 400;
line-height: 22px;
}
}
.percentage-chart {
}
}
}
</style>
TopologyControl
java
package com.xnms.client.service.controller.topology;
import com.xnms.client.service.controller.common.ResponseModel;
import com.xnms.client.service.utils.RepeaterCache;
import com.xnms.client.service.view.topo.view.page.TopologyUserControl;
import com.xnms.data.contract.database.db.Account;
import com.xnms.data.contract.database.db.QueryTopoCondition;
import com.xnms.data.contract.database.db.RepeaterTypeCount;
import com.xnms.data.contract.database.db.TopoRepeaterData;
import io.swagger.annotations.Api;
import io.swagger.v3.oas.annotations.Operation;
import org.apache.shiro.SecurityUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
import java.util.Map;
@Api(tags = "拓扑展示")
@RestController
@RequestMapping("/api/topology")
public class TopologyControl {
@Autowired
private TopologyUserControl topologyUserControl;
@Operation(summary = "下拉列表中转台查询")
@GetMapping(value = "/repeaterDropAndDownList")
public ResponseModel<List<TopoRepeaterData>> repeaterDropAndDownList(@RequestParam String userName) {
return ResponseModel.ofSuccess(topologyUserControl.repeaterDropAndDownList(userName));
}
@Operation(summary = "整个网络的拓扑架构数据查询")
@PostMapping(value = "/getSiteInfo")
public ResponseModel<TopoRepeaterData> getSiteInfo(@RequestBody QueryTopoCondition queryTopoCondition) {
Account account = (Account) SecurityUtils.getSubject().getPrincipal();
return ResponseModel.ofSuccess(topologyUserControl.getSiteInfo(account.getUserName(), queryTopoCondition.getSerialNoArr()));
}
@Operation(summary = "根据告警类型过滤拓扑图")
@GetMapping(value = "/getSiteInfoByType")
public ResponseModel<TopoRepeaterData> getSiteInfoByType(@RequestParam Integer rptState) {
Account account = (Account) SecurityUtils.getSubject().getPrincipal();
return ResponseModel.ofSuccess(topologyUserControl.getSiteInfoByType(account.getUserName(), rptState));
}
@Operation(summary = "左侧中转台列表树查询")
@GetMapping(value = "/repeaterDropAndDownListTree")
public ResponseModel<List<TopoRepeaterData>> repeaterDropAndDownListTree() {
Account account = (Account) SecurityUtils.getSubject().getPrincipal();
return ResponseModel.ofSuccess(topologyUserControl.repeaterDropAndDownListTree(account.getUserName()));
}
@Operation(summary = "右上角展示不同种类中转台数量查询")
@GetMapping(value = "/queryTypeCount")
public ResponseModel<List<RepeaterTypeCount>> queryTypeCount() {
return ResponseModel.ofSuccess(topologyUserControl.queryTypeCount());
}
@Operation(summary = "更新中转台备注(备注内容,索引)")
@PostMapping(value = "/updateRepeaterNote")
public ResponseModel updateRepeaterNote(@RequestBody QueryTopoCondition queryTopoCondition) {
return ResponseModel.ofSuccess(topologyUserControl.updateRepeaterNote(queryTopoCondition.getContent(), queryTopoCondition.getIndex(), queryTopoCondition.getSerialNo()));
}
@Operation(summary = "查询用户中转台权限")
@GetMapping(value = "/queryUserRepeatersPermission")
public ResponseModel<Map<String, List<String>>> queryUserRepeatersPermission() {
return ResponseModel.ofSuccess(RepeaterCache.getInstance().getAllUserRepeater());
}
}
TopologyUserControl
java
package com.xnms.client.service.view.topo.view.page;
import com.xnms.client.service.Utility.MultiUsedFun;
import com.xnms.client.service.Utility.RepServiceTypeEnum;
import com.xnms.client.service.Utility.RepeaterStatusEnum;
import com.xnms.client.service.ViewModel.RepeaterViewModel;
import com.xnms.client.service.ViewModel.SiteViewModel;
import com.xnms.client.service.dao.dsI.commonl.CommonIClient;
import com.xnms.client.service.utils.RepeaterCache;
import com.xnms.data.contract.ApiResponse;
import com.xnms.data.contract.MultiResponse;
import com.xnms.data.contract.Utility;
import com.xnms.data.contract.database.db.*;
import com.xnms.data.contract.util.AllRepeaterCache;
import com.xnms.data.service.api.client.ClientService;
import com.xnms.data.service.api.common.CommonService;
import com.xnms.data.service.dao.LastDataDao;
import com.xnms.data.service.entity.RepeaterLastDataEntity;
import com.xnms.data.service.service.impl.client.SiteServImpl;
import com.xnms.data.service.service.impl.users.UserServImpl;
import com.xnms.data.service.service.impl.websocket.PushService;
import com.xnms.data.service.util.language.LanguageFind;
import com.xnms.service.center.utility.ConfigValue;
import org.apache.shiro.SecurityUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import javax.annotation.PostConstruct;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;
@Component
public class TopologyUserControl {
private static final Logger logger = LoggerFactory.getLogger(TopologyUserControl.class);
@Autowired
private CommonService commonService;
@Autowired
private CommonIClient commonIClient;
@Autowired
private LanguageFind languageFind;
@Autowired
private UserServImpl userServImpl;
@Autowired
private ConfigValue configValue;
@Autowired
private SiteServImpl siteService;
@Autowired
private MultiUsedFun multiUsedFun;
@Autowired
private LastDataDao lastDataDao;
//自己生成的XPT网络架构
private static List<XptSiteModel> xptSiteModle = new ArrayList<>();
private static List<SiteViewModel> conventionalSiteModle = new ArrayList<>();
@Autowired
private ClientService clientService;
public static List<XptSiteModel> getXptSiteModle() {
return xptSiteModle;
}
public static List<SiteViewModel> getConventionalSiteModle() {
return conventionalSiteModle;
}
@Autowired
private PushService pushService;
@PostConstruct
public void init() {
initUpdateUserRepeater();
}
public void initUpdateUserRepeater() {
RepeaterCache.getInstance().clearAllUserRepeater();
AllRepeaterCache.getInstance().clearAllRepeater();
MultiResponse<Repeater> repeatersResponse = commonService.getRepeaters();
if (repeatersResponse.isSuccess() == true && repeatersResponse.getData() != null) {
MultiResponse<UserRoleData> allUsersResponse = clientService.getUsers();
List<String> userNamesList = allUsersResponse.getData().stream().map(UserRoleData::getName).collect(Collectors.toList());
List<Repeater> allRepeaterList = repeatersResponse.getData();
AllRepeaterCache.getInstance().updateAllRepeater(allRepeaterList);
for (String userName : userNamesList) {
if (!userName.equals("Admin")) {
List<String> userRepeaterIdList = clientService.getUserRepeaterByUserName(userName);
List<String> serialNoList = allRepeaterList.stream()
.filter(repeater -> userRepeaterIdList.contains(repeater.getRepeaterId().toString()))
.map(Repeater::getSerialNo)
.collect(Collectors.toList());
if (serialNoList != null && serialNoList.size() > 0) {
RepeaterCache.getInstance().updateUserRepeater(userName, serialNoList);
}
}
}
}
pushService.messageDataFormatting("userRepeatersPermission", RepeaterCache.getInstance().getAllUserRepeater());
}
/**
* 下拉列表中转台查询
*/
public List<TopoRepeaterData> repeaterDropAndDownList(String userName) {
TopoRepeaterData rootSite = getSiteInfo(userName, new String[]{});
List<TopoRepeaterData> list = new ArrayList<>();
Integer systemType = Integer.parseInt(configValue.obtainConfigValue("xnms.service.center.WorkMode"));
for (TopoRepeaterData twoLayerSite : rootSite.getChildren()) {
if (systemType == Utility.EnumSystemType.XPT.getValue() || systemType == Utility.EnumSystemType.CPT.getValue()) {
for (TopoRepeaterData threeLayerSite : twoLayerSite.getChildren()) {
List<TopoRepeaterData> threeLayerSiteChildren = threeLayerSite.getChildren();
threeLayerSite.setChildren(new ArrayList<>());
for (TopoRepeaterData fourLayerSite : threeLayerSiteChildren) {
List<TopoRepeaterData> fourLayerSiteChildren = fourLayerSite.getChildren();
fourLayerSite.setChildren(new ArrayList<>());
threeLayerSite.getChildren().add(fourLayerSite);
if (fourLayerSiteChildren.size() > 0) {
for (TopoRepeaterData fiveLayerSite : fourLayerSiteChildren) {
threeLayerSite.getChildren().add(fiveLayerSite);
}
}
}
list.add(threeLayerSite);
}
} else {
List<TopoRepeaterData> twoLayerSiteChildren = twoLayerSite.getChildren();
twoLayerSite.setChildren(new ArrayList<>());
for (TopoRepeaterData threeLayerSite : twoLayerSiteChildren) {
List<TopoRepeaterData> threeLayerSiteChildren = threeLayerSite.getChildren();
threeLayerSite.setChildren(new ArrayList<>());
if (!StringUtils.isEmpty(threeLayerSite.getSerialNo())) {
twoLayerSite.getChildren().add(threeLayerSite);
}
if (threeLayerSiteChildren.size() > 0) {
for (TopoRepeaterData fourLayerSite : threeLayerSiteChildren) {
twoLayerSite.getChildren().add(fourLayerSite);
}
}
}
list.add(twoLayerSite);
}
}
return list;
}
public List<TopoRepeaterData> repeaterDropAndDownListTree(String userName) {
List<TopoRepeaterData> tree = new ArrayList<>();
Integer systemType = Integer.parseInt(configValue.obtainConfigValue("xnms.service.center.WorkMode"));
if (systemType == Utility.EnumSystemType.XPT.getValue() || systemType == Utility.EnumSystemType.CPT.getValue()) {
TopoRepeaterData rootSiteInfo = getXptSiteTopoData(userName, new String[]{}, false);
for (TopoRepeaterData twoLayer : rootSiteInfo.getChildren()) {
for (TopoRepeaterData threeLayer : twoLayer.getChildren()) {
TopoRepeaterData threeLayerCopy = new TopoRepeaterData();
BeanUtils.copyProperties(threeLayer, threeLayerCopy);
List<TopoRepeaterData> newChildren = new ArrayList<>();
for (TopoRepeaterData child : threeLayer.getChildren()) {
newChildren.add(child);
}
threeLayerCopy.setChildren(newChildren);
List<TopoRepeaterData> fourLayer = threeLayer.getChildren();
for (TopoRepeaterData fourLayerItem : fourLayer) {
for (TopoRepeaterData fiveLayerItem : fourLayerItem.getChildren()) {
threeLayerCopy.getChildren().add(fiveLayerItem);
}
}
tree.add(threeLayerCopy);
}
}
} else {
TopoRepeaterData rootSiteInfo = getConventionalSiteAndRepeater(userName, new String[]{}, false);
for (TopoRepeaterData twoLayer : rootSiteInfo.getChildren()) {
tree.add(twoLayer);
}
}
return tree;
}
/**
* 根据siteId获取站点名称
*
* @param siteID 站点ID
* @return 站点名称
*/
public String querySiteNameBySiteId(String siteID) {
logger.info("querySiteNameBySiteId-siteID:{}", siteID);
String siteName = "";
Repeater repeater = clientService.tryGetRepeaterByRepeaterId(siteID).getData();
if (repeater != null && !StringUtils.isEmpty(repeater.getAlias())) {
siteName = repeater.getAlias();
}
return siteName;
}
public TopoRepeaterData getSite(String siteID) {
logger.info("querySiteNameBySiteId-siteID:{}", siteID);
Account account = (Account) SecurityUtils.getSubject().getPrincipal();
List<TopoRepeaterData> list = repeaterDropAndDownList(account.getUserName());
for (TopoRepeaterData selectSiteRepeater : list) {
if (selectSiteRepeater.getRepeaterId().equals(siteID)) {
return selectSiteRepeater;
}else {
Integer systemType = Integer.parseInt(configValue.obtainConfigValue("xnms.service.center.WorkMode"));
if (systemType == Utility.EnumSystemType.XPT.getValue() || systemType == Utility.EnumSystemType.CPT.getValue()) {
if (selectSiteRepeater.getRepeaterId().equals("site_" + siteID)) {
return selectSiteRepeater;
}
}
}
}
return new TopoRepeaterData();
}
/**
* 整个网络的拓扑架构数据查询
*/
public TopoRepeaterData getSiteInfo(String userName, String serialNoArr[]) {
TopoRepeaterData siteInfo = null;
Integer systemType = Integer.parseInt(configValue.obtainConfigValue("xnms.service.center.WorkMode"));
if (systemType == Utility.EnumSystemType.XPT.getValue() || systemType == Utility.EnumSystemType.CPT.getValue()) {
siteInfo = getXptSiteTopoData(userName, serialNoArr, true);
} else {
siteInfo = getConventionalSiteAndRepeater(userName, serialNoArr, true);
}
return siteInfo;
}
public TopoRepeaterData getSiteInfoByType(String userName, Integer rptState) {
TopoRepeaterData siteInfo = getSiteInfo(userName, new String[]{});
if (rptState == 1 || rptState == 2 || rptState == 3) {
TopoRepeaterData filteredRoot = filterTreeByRptState(siteInfo, rptState);
if (filteredRoot == null) {
filteredRoot = new TopoRepeaterData();
filteredRoot.setName(languageFind.findKey("Network"));
filteredRoot.setRepeaterId("network");
}
return filteredRoot;
}
return siteInfo;
}
public static TopoRepeaterData filterTreeByRptState(TopoRepeaterData node, Integer rptState) {
TopoRepeaterData newNode = new TopoRepeaterData();
newNode.setRptState(node.getRptState());
if (node.getType() != null) newNode.setType(node.getType());
if (node.getSiteId() != null) newNode.setSiteId(node.getSiteId());
if (node.getSerialNo() != null) newNode.setSerialNo(node.getSerialNo());
if (node.getSiteIndex() != null) newNode.setSiteIndex(node.getSiteIndex());
if (node.getName() != null) newNode.setName(node.getName());
if (node.getMasterIp() != null) newNode.setMasterIp(node.getMasterIp());
if (node.getRepeaterId() != null) newNode.setRepeaterId(node.getRepeaterId());
if (node.getChildren() != null && !node.getChildren().isEmpty()) {
List<TopoRepeaterData> filteredChildren = new ArrayList<>();
for (TopoRepeaterData child : node.getChildren()) {
TopoRepeaterData filteredChild = filterTreeByRptState(child, rptState);
if (filteredChild != null) {
filteredChildren.add(filteredChild);
}
}
if (filteredChildren.isEmpty()) {
return null;
}
newNode.setChildren(filteredChildren);
} else {
if (node.getRptState() != rptState) {
return null;
}
}
return newNode;
}
/**
* 得到XPT整个网络的拓扑架构数据
* 网络 -> XPT -> 站点 -> 主机 -> 从机 五级层级
*/
public TopoRepeaterData getXptSiteTopoData(String userName, String serialNoArr[], Boolean queryAlarm) {
TopoRepeaterData rootData = new TopoRepeaterData();
rootData.setName(languageFind.findKey("Network"));
rootData.setRepeaterId("network");
getXptSites(userName, serialNoArr, queryAlarm);
if (xptSiteModle == null || xptSiteModle.size() < 1) return rootData;
List<Site> siteAliasList = siteService.getSite();
//数据处理
if (xptSiteModle.size() > 0) {
List<XptSiteModel> xptSiteLists = xptSiteModle.stream().sorted(Comparator.comparing(XptSiteModel::getSiteID)).collect(Collectors.toList());//站点按site ID排序
for (XptSiteModel site : xptSiteLists) {
TopoRepeaterData topoSite = new TopoRepeaterData();
topoSite.setName(site.getSitename());
if (siteAliasList.size() > 0) {
List<Site> siteAliasTemp = siteAliasList.stream().filter(item -> Integer.parseInt(item.getSiteID()) == site.getSiteID()).collect(Collectors.toList());
if (siteAliasTemp.size() > 0) {
String aliasName = siteAliasTemp.get(0).getSiteAlias().trim();
if (!StringUtils.isEmpty(aliasName)) {
if (site.getSiteID() < 1) {
topoSite.setName(languageFind.findKey("UnRegisterSite") + "(" + aliasName + ")");
} else {
topoSite.setName(languageFind.findKey("TreeViewSite") + site.getSiteID() + "(" + aliasName + ")");
}
}
}
}
topoSite.setSiteId(site.getSiteID());
topoSite.setRepeaterId("site_" + site.getSiteID());//站点ID为"XNMS_xpt_Site"+站点ID
topoSite.setSerialNo("site_" + site.getSiteID());
topoSite.setMasterIp(site.getMasterIP());
//取站点下的中转台
List<RepBaseInfo> repeaters = site.getXptRptList().stream().sorted(Comparator.comparing(RepBaseInfo::getSerialNo)).toList(); //中转台按SN排序
if (repeaters != null) {
//先在站点的中转台中找到主机,如果主机没有找子网主机
List<RepBaseInfo> masterRptInfo = repeaters.stream().filter(item -> item.getRepeaterNetType() == 01).toList();
TopoRepeaterData masterRpt = new TopoRepeaterData(); //主机数据
if (masterRptInfo.size() > 0) {
masterRpt.setName(masterRptInfo.get(0).getAlias());
masterRpt.setRepeaterId(masterRptInfo.get(0).getRepeaterId().toString());
masterRpt.setRptState(masterRptInfo.get(0).getRepeaterStatus().getValue());
masterRpt.setSerialNo(masterRptInfo.get(0).getSerialNo());
masterRpt.setSiteId(masterRptInfo.get(0).getSiteID());
masterRpt.setSiteIndex(masterRptInfo.get(0).getSiteIndex());
masterRpt.setType(masterRptInfo.get(0).getType());
masterRpt.setNote(masterRptInfo.get(0).getNote());
topoSite.getChildren().add(masterRpt);
} else {
//没有主机的情况看是否有子网主机
masterRptInfo = repeaters.stream().filter(item -> item.getRepeaterNetType() == 04).toList();
if (masterRptInfo.size() > 0) {
masterRpt.setName(masterRptInfo.get(0).getAlias());
masterRpt.setRepeaterId(masterRptInfo.get(0).getRepeaterId().toString());
masterRpt.setRptState(masterRptInfo.get(0).getRepeaterStatus().getValue());
masterRpt.setSerialNo(masterRptInfo.get(0).getSerialNo());
masterRpt.setSiteId(masterRptInfo.get(0).getSiteID());
masterRpt.setSiteIndex(masterRptInfo.get(0).getSiteIndex());
masterRpt.setType(masterRptInfo.get(0).getType());
masterRpt.setNote(masterRptInfo.get(0).getNote());
topoSite.getChildren().add(masterRpt);
}
}
for (RepBaseInfo item : repeaters) {
//如果存在主机,将从机挂到主机下
if (masterRptInfo.size() > 0) {
if (item.getSerialNo() != masterRptInfo.get(0).getSerialNo()) {
TopoRepeaterData slaveRpt = new TopoRepeaterData();//从机数据
slaveRpt.setName(item.getAlias());
slaveRpt.setRepeaterId(item.getRepeaterId().toString());
slaveRpt.setRptState(item.getRepeaterStatus().getValue());
slaveRpt.setSerialNo(item.getSerialNo());
slaveRpt.setSiteId(item.getSiteID());
slaveRpt.setSiteIndex(item.getSiteIndex());
slaveRpt.setType(item.getType());
slaveRpt.setNote(item.getNote());
masterRpt.getChildren().add(slaveRpt);
}
}
//如果没有主机则所有中转台挂到站点下面
else {
TopoRepeaterData repeater = new TopoRepeaterData();
repeater.setName(item.getAlias());
repeater.setRepeaterId(item.getRepeaterId().toString());
repeater.setRptState(item.getRepeaterStatus().getValue());
repeater.setSerialNo(item.getSerialNo());
repeater.setSiteId(item.getSiteID());
repeater.setSiteIndex(item.getSiteIndex());
repeater.setType(item.getType());
repeater.setNote(item.getNote());
topoSite.getChildren().add(repeater);
}
}
}
//查看该站点的主机的MasterIP是否和其他站点的主机MasterIP一样,如果一样说明应该在同一个XPT网络下
//则将该站点加到同一个XPT网络下,否则就新建一个XPT网络
boolean bFind = false;
List<TopoRepeaterData> sitesData = rootData.getChildren();
for (TopoRepeaterData rtpsite : sitesData) {
if (rtpsite.getMasterIp().equals(topoSite.getMasterIp()) && !rtpsite.getMasterIp().equals("0.0.0.0") && !StringUtils.isEmpty(topoSite.getMasterIp())) {
rtpsite.getChildren().add(topoSite);
bFind = true;
break;
}
}
Integer systemType = Integer.parseInt(configValue.obtainConfigValue("xnms.service.center.WorkMode"));
//如果没有相同的XPT网络的,则新建一个
if (!bFind) {
TopoRepeaterData topoXpt = new TopoRepeaterData();
String systemTypeString = "";
if (systemType == 0) {
systemTypeString = Utility.EnumSystemType.Conventional.toString();
} else if (systemType == 1) {
systemTypeString = Utility.EnumSystemType.XPT.toString();
} else if (systemType == 2) {
systemTypeString = Utility.EnumSystemType.CPT.toString();
} else {
systemTypeString = Utility.EnumSystemType.Unknown.toString();
}
topoXpt.setName(systemTypeString);
topoXpt.setRepeaterId("xpt_" + site.getSiteID());//XPT节点的ID为1
topoXpt.setMasterIp(site.getMasterIP());
topoXpt.getChildren().add(topoSite);
rootData.getChildren().add(topoXpt);
}
}
}
return rootData;
}
/**
* 获取XPT站点数据
*/
public void getXptSites(String userName, String serialNoArr[], Boolean queryAlarm) {
List<Repeater> dbRepeaters = commonIClient.getAuthorityRepeaters(userName);
if (serialNoArr.length > 0) {
dbRepeaters = dbRepeaters.stream().filter(repeater -> Arrays.stream(serialNoArr).anyMatch(id -> id.equals(repeater.getSerialNo()))).collect(Collectors.toList());
}
getXptMulitSite(dbRepeaters, queryAlarm);
}
public void getXptMulitSite(List<Repeater> rptList, Boolean queryAlarm) {
try {
List<XptSiteModel> xptSiteModleTemp = new ArrayList<>();
List<Repeater> repeaterList = rptList.stream().sorted(Comparator.comparing(Repeater::getSiteId)).collect(Collectors.toList());
XptSiteModel xptSite = null;
Integer systemType = Integer.parseInt(configValue.obtainConfigValue("xnms.service.center.WorkMode"));
for (Repeater rpt : repeaterList) {
if (rpt == null) {
continue;
}
if (rpt.getSystemType() != systemType && rpt.getSystemType() != -1) {
continue;
}
xptSite = xptSiteModleTemp.stream().filter(item -> item.getSiteID() == rpt.getSiteId()).findFirst().orElse(null);
//如果site为空,则说明需要创建一个site
if (xptSite == null) {
xptSite = new XptSiteModel();
xptSite.setSiteID(rpt.getSiteId());
xptSite.setSitename(multiUsedFun.getSiteAlias(xptSite.getSiteID(), Utility.EnumSystemType.XPT));
xptSiteModleTemp.add(xptSite);
}
RepBaseInfo repBase = getNewRepeaterViewModel(rpt, queryAlarm);
xptSite.getXptRptList().add(repBase);
}
setXptSiteMasterIP(xptSiteModleTemp);
if (xptSiteModle.size() < 1) {
xptSiteModle = xptSiteModleTemp;
}
xptSiteModle = xptSiteModleTemp;
} catch (Exception e) {
logger.error("-getXptMulitSite-Exception:{}", e);
}
}
/**
* 根据Repeater表里的内容获取XPT多站网络架构
*
* @param siteCollection
*/
public void setXptSiteMasterIP(List<XptSiteModel> siteCollection) {
for (XptSiteModel site : siteCollection) {
site.setMasterIP(getSiteMasterIP(site));
}
}
/**
* 得到XPT站点下的主中转台并返回主中转台的MasterIP
*
* @param site
*/
public String getSiteMasterIP(XptSiteModel site) {
String mastIP = "";
//先找01master
List<RepBaseInfo> tempRtpList = site.getXptRptList().stream().filter(item -> item.getSystemType() != Utility.EnumSystemType.Unknown.getValue() && item.getRepeaterNetType() == 01).toList();
if (tempRtpList.size() > 0) {
//如果为主机那么就将中转台自己的IP设为master IP
mastIP = tempRtpList.get(0).getRepeaterIP();
} else {
tempRtpList = site.getXptRptList().stream().filter(item -> item.getSystemType() != Utility.EnumSystemType.Unknown.getValue() && item.getRepeaterNetType() == 04).toList();
if (tempRtpList.size() > 0) {
//如果为子网主机那么设为master IP
mastIP = tempRtpList.get(0).getMasterIP();
}
}
return mastIP;
}
/**
* 获取一个新RepBaseInfo实例,并赋以基本信息
* <param name="repeaterData">从数据库返回的中转台基本信息</param>
* <returns>返回中转台新实例</returns>
*
* @param repeaterData
*/
public RepBaseInfo getNewRepeaterViewModel(Repeater repeaterData, Boolean queryAlarm) {
if (repeaterData == null) {
return null;
}
RepBaseInfo newRepeater = new RepBaseInfo();
newRepeater.setAlias(repeaterData.getAlias());
newRepeater.setMasterIP(repeaterData.getMasterIp());
newRepeater.setRepeaterIP(repeaterData.getIpAddress());
newRepeater.setServerIP(repeaterData.getServerIp());
newRepeater.setSerialNo(repeaterData.getSerialNo());
newRepeater.setSiteID(repeaterData.getSiteId());
newRepeater.setSystemType(repeaterData.getSystemType());
newRepeater.setRepeaterNetType(repeaterData.getRepeaterSiteType());
newRepeater.setRepeaterId(repeaterData.getRepeaterId());
newRepeater.setSiteIndex(repeaterData.getSiteIndex());
newRepeater.setType(repeaterData.getType());
newRepeater.setNote(repeaterData.getNote1());
if (repeaterData.getStatus() == 0) {
newRepeater.setRepeaterStatus(com.xnms.data.contract.util.RepeaterStatusEnum.Normal);
} else {
newRepeater.setRepeaterStatus(com.xnms.data.contract.util.RepeaterStatusEnum.Offline);
}
if (queryAlarm && repeaterData.getStatus() == 0) {
Boolean status = getRepeaterAlarmStatus(repeaterData.getSerialNo());
if (status) newRepeater.setRepeaterStatus(com.xnms.data.contract.util.RepeaterStatusEnum.Alarm);
}
return newRepeater;
}
/**
* 获取常规模式站点信息和中转台信息
* @param userName 用户名
* @param serialNoArr 序列号数组
* @return 根节点
*/
public TopoRepeaterData getConventionalSiteAndRepeater(String userName, String serialNoArr[], Boolean queryAlarm) {
TopoRepeaterData rootSite = new TopoRepeaterData();
try {
rootSite.setName(languageFind.findKey("Network"));
getConventionalSiteValue(userName, serialNoArr, queryAlarm);
rootSite.setRepeaterId("network");
List<TopoRepeaterData> listTopoXptsite = new ArrayList<>();
if (conventionalSiteModle == null || conventionalSiteModle.size() < 1)
return rootSite;
for (SiteViewModel site : conventionalSiteModle) {
TopoRepeaterData topoSite = new TopoRepeaterData();
if (site.getSiteID() > 0) {
topoSite.setName(languageFind.findKey("TreeViewConventional"));
} else {
topoSite.setName(languageFind.findKey("UnRegisterSite"));
}
topoSite.setSiteId(site.getSiteID());
topoSite.setRepeaterId("site_" + site.getSiteID());
topoSite.setSerialNo("site_" + site.getSiteID());
topoSite.setChildren(getRepeaterInfo(site.getAllRepeatersList()));
listTopoXptsite.add(topoSite);
}
rootSite.setChildren(listTopoXptsite);
} catch (Exception e) {
logger.error("-getConventionalSiteAndRepeater-Exception:{}", e);
}
return rootSite;
}
public List<TopoRepeaterData> getRepeaterInfo(List<RepeaterViewModel> RepeatersList) {
List<TopoRepeaterData> listSites = new ArrayList<>();
for (RepeaterViewModel repeaterView : RepeatersList) {
TopoRepeaterData temp = new TopoRepeaterData();
temp.setName(repeaterView.getRepeaterName());
if (repeaterView.getRepeaterStatus() == RepeaterStatusEnum.Unknown) {
temp.setRptState(0);
} else if (repeaterView.getRepeaterStatus() == RepeaterStatusEnum.Normal) {
temp.setRptState(1);
} else if (repeaterView.getRepeaterStatus() == RepeaterStatusEnum.Alarm) {
temp.setRptState(2);
} else if (repeaterView.getRepeaterStatus() == RepeaterStatusEnum.Offline) {
temp.setRptState(3);
} else {
temp.setRptState(4);
}
if (repeaterView.getRepeaterStatus() == RepeaterStatusEnum.Virtual) {
temp.setRepeaterId("virtual_" + repeaterView.getSiteID()); //虚拟中转台ID为2
temp.setName(languageFind.findKey("VirtualRepeater"));
} else {
temp.setSiteId(repeaterView.getSiteID());
temp.setSiteIndex(repeaterView.getRepeaterIndex());
temp.setType(repeaterView.getType());
temp.setRepeaterId(repeaterView.getRepeaterId().toString());
temp.setSerialNo(repeaterView.getSerialNumber());
temp.setNote(repeaterView.getNote());
}
temp.setChildren(getRepeaterInfo(repeaterView.getAllRepeatersList()));
listSites.add(temp);
}
return listSites.stream().sorted(Comparator.comparing(TopoRepeaterData::getRepeaterId)).collect(Collectors.toList());
}
public List<Repeater> getAuthorityRepeaters(String userName) {
logger.info("-getAuthorityRepeaters-userName:{}", userName);
List<Repeater> repeaters = null;
List<UserRepeater> userRepeaters = null;
try {
userRepeaters = userServImpl.getUserRepeater(userName);
MultiResponse<Repeater> repeatersResponse = commonService.getRepeaters();
if (userRepeaters != null && repeatersResponse.isSuccess() == true && repeatersResponse.getData() != null) {
List<String> authorityRepeaters = userRepeaters.stream().map(item -> item.getRepeaterId()).collect(Collectors.toList());
Integer systemType = Integer.parseInt(configValue.obtainConfigValue("xnms.service.center.WorkMode"));
repeaters = repeatersResponse.getData().stream().filter(item -> (item.getSystemType() == systemType || item.getSystemType() == Utility.EnumSystemType.Unknown.getValue())).collect(Collectors.toList());
if (!userName.equals("Admin")) {
if (systemType == Utility.EnumSystemType.Conventional.getValue()) {
repeaters = repeaters.stream().filter(item -> authorityRepeaters.contains(item.getRepeaterId().toString())).collect(Collectors.toList());
} else if (systemType == Utility.EnumSystemType.XPT.getValue() || systemType == Utility.EnumSystemType.CPT.getValue()) {
repeaters = repeaters.stream().filter(item -> authorityRepeaters.contains("site_" + item.getSiteId())).collect(Collectors.toList());
} else {
repeaters = new ArrayList<>();
}
}
}
} catch (Exception e) {
logger.error("-getAuthorityRepeaters-error:", e);
}
return repeaters;
}
public void getConventionalSiteValue(String userName, String serialNoArr[], Boolean queryAlarm) {
List<Repeater> dbRepeaters = getAuthorityRepeaters(userName);
if (serialNoArr.length > 0) {
dbRepeaters = dbRepeaters.stream().filter(repeater -> Arrays.stream(serialNoArr).anyMatch(id -> id.equals(repeater.getSerialNo()))).collect(Collectors.toList());
}
int[] orderArr = new int[] { 1, 4, 3, 2, 0 };
//常规时候按照iP地址排序
List<Repeater> repeaterList = dbRepeaters.stream().sorted(new Comparator<Repeater>() {
@Override
public int compare(Repeater x, Repeater y) {
int indexX = indexOf(orderArr, x.getRepeaterSiteType());
int indexY = indexOf(orderArr, y.getRepeaterSiteType());
// 如果都在 orderArr 中,按索引比较
if (indexX != -1 && indexY != -1) {
return Integer.compare(indexX, indexY);
}
// 如果只有 x 在 orderArr 中,x 优先
if (indexX != -1) {
return -1; // x 优先
}
// 如果只有 y 在 orderArr 中,y 优先
if (indexY != -1) {
return 1; // y 优先
}
// 都不在 orderArr 中,保持原有顺序
return 0; // 相等
}
}).collect(Collectors.toList());
Integer systemType = Integer.parseInt(configValue.obtainConfigValue("xnms.service.center.WorkMode"));
SiteViewModel site = null;
RepeaterViewModel repeater = null;
RepeaterViewModel findRpt = null;
List<SiteViewModel> siteModleTemp = new ArrayList<>();
for (Repeater rpt : repeaterList) {
if (rpt == null) {
continue;
}
if (rpt.getSystemType() != systemType && rpt.getSystemType() != -1) {
continue;
}
//先得到站点信息
site = siteModleTemp.stream().filter(item -> item.getSiteID() == rpt.getSiteId()).findFirst().orElse(null);
//如果site为空,则说明需要创建一个site
if (site == null) {
site = new SiteViewModel();
site.setSiteID(rpt.getSiteId());
site.setSiteName(multiUsedFun.getSiteAlias(site.getSiteID(), Utility.EnumSystemType.Conventional));
String siteName = site.getSiteName();
SiteViewModel siteViewModel = siteModleTemp.stream().filter(item -> item.getSiteName().equals(siteName)).findFirst().orElse(null);
if (siteViewModel == null) {
siteModleTemp.add(site);
}
}
//如果为最上层网络,则直接加到站点的中转台列表
if ((StringUtils.isEmpty(rpt.getMasterIp()) || rpt.getMasterIp().equals("0.0.0.0") || rpt.getMasterPort() <= 0) && !rpt.getMasterIp().equals("255.255.255.255")) {
//如果是常规系统且增加了一个常规系统站点,那么不在增加站点,所有的主机中转台都应该挂在第一个常规系统下
String siteName = site.getSiteName();
site = siteModleTemp.stream().filter(item -> item.getSiteName().equals(siteName)).findFirst().orElse(null);
repeater = getNewRepeaterViewModel(rpt, systemType, queryAlarm);
site.getAllRepeatersList().add(repeater);
} else {
boolean virtualRepeater = true;
String siteName = site.getSiteName();
site = siteModleTemp.stream().filter(item -> item.getSiteName().equals(siteName)).findFirst().orElse(null);
repeater = getNewRepeaterViewModel(rpt, systemType, queryAlarm);
List<RepeaterViewModel> fatherRepeatersList = site.getAllRepeatersList();
for (RepeaterViewModel parentRepeater : fatherRepeatersList) {
if (!StringUtils.isEmpty(parentRepeater.getSnmpLanIpAddress()) && parentRepeater.getSnmpLanIpAddress().equals(repeater.getSnmpWanIpAddress())) {
virtualRepeater = false;
parentRepeater.getAllRepeatersList().add(repeater);
break;
}
}
if (virtualRepeater) {
RepeaterViewModel virtualSite = site.getAllRepeatersList().stream().filter(
item -> item.getRepeaterName().contains("虚拟中转台") ||
item.getRepeaterName().contains("Virtual Repeater") ||
item.getRepeaterName().contains("Виртуальный ретранслятор")
).findFirst().orElse(null);
if (virtualSite == null) {
findRpt = new RepeaterViewModel();
findRpt.setRepeaterStatus(RepeaterStatusEnum.Virtual);
findRpt.setSnmpIpAddress(rpt.getMasterIp());
findRpt.setIpConnectPort(rpt.getMasterPort());
findRpt.setSiteID(site.getSiteID());
findRpt.setRepeaterName(languageFind.findKey("VirtualRepeater") );
findRpt.setOwnerRepeater(null);
site.addRepeater(findRpt);
}
RepeaterViewModel newRepeaterViewModel = getNewRepeaterViewModel(rpt, systemType, queryAlarm);
site.getAllRepeatersList().stream().filter(
item -> item.getRepeaterName().contains("虚拟中转台") ||
item.getRepeaterName().contains("Virtual Repeater") ||
item.getRepeaterName().contains("Виртуальный ретранслятор")
).findFirst().orElse(null).getAllRepeatersList().add(newRepeaterViewModel);
}
}
}
conventionalSiteModle = siteModleTemp;
}
private static int indexOf(int[] arr, int value) {
for (int i = 0; i < arr.length; i++) {
if (arr[i] == value) {
return i;
}
}
return -1; // Not found
}
/**
* 取一个新RepeaterViewModel实例,并赋以基本信息
* @param repeaterData 从数据库返回的中转台基本信息
* @param systemType 当前系统模式类型
* @return 返回中转台新实例
*/
private RepeaterViewModel getNewRepeaterViewModel(Repeater repeaterData, Integer systemType, Boolean queryAlarm)
{
if (repeaterData == null)
{
return null;
}
RepeaterViewModel newRepeater = new RepeaterViewModel();
//基本信息
newRepeater.setSerialNumber(repeaterData.getSerialNo());
newRepeater.setSiteID(repeaterData.getSiteId());
newRepeater.setRepeaterIndex(repeaterData.getIndex());
newRepeater.setType(repeaterData.getType());
//如果数据库未增加中转台别名,则用序列号代替
newRepeater.setRepeaterName(repeaterData.getAlias());
newRepeater.setRepeaterId(repeaterData.getRepeaterId());
if (StringUtils.isEmpty(newRepeater.getRepeaterName()))
newRepeater.setRepeaterName(newRepeater.getSerialNumber());
if (repeaterData.getStatus() == 0) {
newRepeater.setRepeaterStatus(RepeaterStatusEnum.Normal);
} else {
newRepeater.setRepeaterStatus(RepeaterStatusEnum.Offline);
}
if (queryAlarm && repeaterData.getStatus() == 0) {
Boolean status = getRepeaterAlarmStatus(repeaterData.getSerialNo());
if (status) newRepeater.setRepeaterStatus(RepeaterStatusEnum.Alarm);
}
if (repeaterData.getType() == RepeaterType.VOICE_REPEATER.getValue())
newRepeater.setRepeaterServiceType(RepServiceTypeEnum.VoiceRepeater);
else if (repeaterData.getType() == RepeaterType.DATA_REPEATER.getValue())
newRepeater.setRepeaterServiceType(RepServiceTypeEnum.DataRepeater);
else
newRepeater.setRepeaterServiceType(RepServiceTypeEnum.UnknownRepeater);
//详细信息
newRepeater.getParamSystemInfo().setModelNo(repeaterData.getMachineNo());
newRepeater.getParamSystemInfo().setRadioID(repeaterData.getRadioId());
newRepeater.getParamSystemInfo().setFirmwareVersion(repeaterData.getFirmwaveNo());
newRepeater.getParamSystemInfo().setRcdbVersion(repeaterData.getRcdbNo());
newRepeater.setSnmpPort(repeaterData.getSnmpPort());
newRepeater.getParamSystemInfo().setChannelName(repeaterData.getChannelName());
//IPAddress.TryParse(rpt.Ipaddress, out ip);
newRepeater.setSnmpIpAddress(repeaterData.getIpAddress());
//如果是XPT,IP互联端口设为无效(0)
newRepeater.setIpConnectPort((systemType == Utility.EnumSystemType.XPT.getValue()) || systemType == Utility.EnumSystemType.CPT.getValue() ? 0 : repeaterData.getIpConnectPort());
if (!StringUtils.isEmpty(repeaterData.getServerIp())) {
newRepeater.setSnmpLanIpAddress(repeaterData.getServerIp());
} else {
newRepeater.setSnmpLanIpAddress("");
}
if (!StringUtils.isEmpty(repeaterData.getMasterIp())) {
newRepeater.setSnmpWanIpAddress(repeaterData.getMasterIp());
} else {
newRepeater.setSnmpWanIpAddress("");
}
if (!StringUtils.isEmpty(repeaterData.getCurrentTxFrequence())) {
Double intNumCurrentTxFrequence = Double.valueOf(repeaterData.getCurrentTxFrequence());
newRepeater.getParamSystemInfo().setCurTxFreq(intNumCurrentTxFrequence);
}
if (!StringUtils.isEmpty(repeaterData.getCurrentRxFrequence())) {
Double intNumCurrentRxFrequence = Double.valueOf(repeaterData.getCurrentRxFrequence());
newRepeater.getParamSystemInfo().setCurRxFreq(intNumCurrentRxFrequence);
}
//初始化时的父节点先为null
newRepeater.setOwnerRepeater(null);
newRepeater.setNote(repeaterData.getNote1());
return newRepeater;
}
public List<RepeaterTypeCount> queryTypeCount() {
List<RepeaterTypeCount> list = new ArrayList<>();
Account account = (Account) SecurityUtils.getSubject().getPrincipal();
List<Repeater> dbRepeaters = commonIClient.getAuthorityRepeaters(account.getUserName());
int allCount = 0;
int normalCount = 0;
int alarmCount = 0;
int onfflineCount = 0;
allCount = dbRepeaters.size();
onfflineCount = (int) dbRepeaters.stream().filter(repeater -> repeater.getStatus() == 1).count();
int normalAndAlarmCount = allCount - onfflineCount;
for (Repeater repeater : dbRepeaters) {
if (repeater.getStatus() == 0) {
Boolean status = getRepeaterAlarmStatus(repeater.getSerialNo());
if (status) alarmCount++;
}
}
normalCount = normalAndAlarmCount - alarmCount;
list.add(new RepeaterTypeCount(0, allCount));
list.add(new RepeaterTypeCount(1, normalCount));
list.add(new RepeaterTypeCount(2, alarmCount));
list.add(new RepeaterTypeCount(3, onfflineCount));
return list;
}
/**
* 根据中转台ID获取告警状态
*/
public Boolean getRepeaterAlarmStatus(String sn) {
Boolean flag = false;
List<String> oidList = getOIDList();
for (String oid : oidList) {
RepeaterLastDataEntity lastData = lastDataDao.getLastData(sn, oid);
if (lastData != null) {
String value = lastData.getValue();
double v = Double.parseDouble(value);
int i = (int) v;
if (i == 1 || i == 2 || i == 3) {
flag = true;
break;
}
}
}
return flag;
}
public List<String> getOIDList() {
List<String> list = new ArrayList<>();
list.add("1.3.6.1.4.1.40297.1.2.1.1.1.0");
list.add("1.3.6.1.4.1.40297.1.2.1.1.2.0");
list.add("1.3.6.1.4.1.40297.1.2.1.1.6.0");
list.add("1.3.6.1.4.1.40297.1.2.1.1.7.0");
list.add("1.3.6.1.4.1.40297.1.2.1.1.8.0");
list.add("1.3.6.1.4.1.40297.1.2.1.1.10.0");
list.add("1.3.6.1.4.1.40297.1.2.1.1.11.0");
list.add("1.3.6.1.4.1.40297.1.2.1.1.12.0");
list.add("1.3.6.1.4.1.40297.1.2.1.1.13.0");
return list;
}
public ApiResponse updateRepeaterNote(String content, Integer index, String serialNo) {
return clientService.updateRepeaterNote(content, index, serialNo);
}
}
CommonIClient
java
package com.xnms.client.service.dao.dsI.commonl;
import com.xnms.data.contract.MultiResponse;
import com.xnms.data.contract.SingleResponse;
import com.xnms.data.contract.Utility;
import com.xnms.data.contract.database.db.Repeater;
import com.xnms.data.contract.database.db.UserRepeater;
import com.xnms.data.service.api.client.ClientService;
import com.xnms.data.service.api.common.CommonService;
import com.xnms.data.service.service.impl.users.UserServImpl;
import com.xnms.service.center.utility.ConfigValue;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
@Component
public class CommonIClient {
private static final Logger logger = LoggerFactory.getLogger(CommonIClient.class);
@Autowired
private CommonService commonService;
@Autowired
private UserServImpl userServImpl;
@Autowired
private ConfigValue configValue;
@Autowired
private ClientService clientService;
/**
* 根据序列号获取中转台Id
* @param repeaterSN repeaterSN
* @return 结果
*/
public String getRepeaterIDFromSN(String repeaterSN) {
SingleResponse<Repeater> response = clientService.getRepeater(repeaterSN);
String repeaterID = response.getData().getRepeaterId().toString();
return repeaterID;
}
/**
* 根据用户名获取可用的中转台列表
* @param userName 用户名
* @return 中转台列表
*/
public List<Repeater> getAuthorityRepeaters(String userName) {
logger.info("-getAuthorityRepeaters-userName:{}", userName);
List<Repeater> repeaters = new ArrayList<>();
List<UserRepeater> userRepeaters = new ArrayList<>();
try {
userRepeaters = userServImpl.getUserRepeater(userName);
MultiResponse<Repeater> repeatersResponse = commonService.getRepeaters();
if (userRepeaters != null && repeatersResponse.isSuccess() == true && repeatersResponse.getData() != null) {
List<String> authorityRepeaters = userRepeaters.stream().map(item -> item.getRepeaterId()).collect(Collectors.toList());
Integer systemType = Integer.parseInt(configValue.obtainConfigValue("xnms.service.center.WorkMode"));
repeaters = repeatersResponse.getData().stream().filter(item -> (item.getSystemType() == systemType || item.getSystemType() == Utility.EnumSystemType.Unknown.getValue())).collect(Collectors.toList());
if (!userName.equals("Admin")) {
if (systemType == Utility.EnumSystemType.Conventional.getValue()) {
repeaters = repeaters.stream().filter(item -> authorityRepeaters.contains(item.getRepeaterId().toString())).collect(Collectors.toList());
} else if (systemType == Utility.EnumSystemType.XPT.getValue() || systemType == Utility.EnumSystemType.CPT.getValue()) {
repeaters = repeaters.stream().filter(item -> authorityRepeaters.contains("site_" + item.getSiteId())).collect(Collectors.toList());
} else {
repeaters = new ArrayList<>();
}
}
}
} catch (Exception e) {
logger.error("-getAuthorityRepeaters-error:", e);
}
logger.info("-getAuthorityRepeaters-end");
return repeaters;
}
}
四、说明
说明点1:中转台分3种状态【告警中转台、离线中转台、正常中转台】
划分规律:中转台分在线中转台、离线中转台,而在线中转台又分成2种包括正常中转台、告警中转台。
在线/离线中转台根据数据库表字段status进行区分,【0:在线、1:离线】
告警中转台这去告警表中查询是否有任意一条业务类型的告警记录,有记录就代表告警中转台;没有则代表正常中转台
中转台10s一上报业务数据,最多3次超过30s未收到业务数据就代表离线了,更新中转台的在线状态。
拓扑图中中转台状态会动态改变。
说明点2:拓扑图分CVT和XPT两种模式,最终对应的图也不一样
CVT模式下拓扑图:组网(父子节点中转台)中转台前面只包含3个节点:网络+常规系统+虚拟中转台,其中虚拟中转台代表包含的中转台没有找到父节点,而独立中转台或者有父子关系的中转台则放在常规系统中

XPT模式下拓扑图:组网(父子节点中转台)中转台前面只包含3个节点:网络+XPT+站点

说明点3:拓扑图中点击节点,比如点击XPT节点会进行收缩和展开效果,同时上方中转台列表可以进行拓扑图过滤,点击右侧的不同状态统计占比也会进行拓扑图过滤。

说明点4:中转台鼠标右键弹窗,包含基本信息、监控告警、设备参数




基本信息:包含别名、序列号、状态、备注
监控告警、设备参数:都是打开其他页面参数,公用相同的页面。
只有在线中转台进入才能进入监控告警、设备参数,离线中转台右键进入弹窗"离线中转台不允许查看!"

说明点5:中转台节点不同状态图标不一样
离线且无备注图标
离线且有备注图标
正常且无备注图标
正常且有备注图标
告警且无备注图标
告警且有备注图标
说明点6:总结下不同模式下拓扑图父子关系图的判断规则:
CVT模式下:
java
getConventionalSiteAndRepeater()
创建根节点网络节点
拼接节点getConventionalSiteValue()
查询所有中转台,循环遍历
判断系统节点和未知中转台节是否存在,不存在则创建
根据siteID找到父节点
判断逻辑符合,说明是父节点(StringUtils.isEmpty(rpt.getMasterIp()) || rpt.getMasterIp().equals("0.0.0.0") || rpt.getMasterPort() <= 0) && !rpt.getMasterIp().equals("255.255.255.255")),添加到站点中
判断逻辑不符合,说明是从节点
获取站点所有子集,循环遍历,找到父节点serverip不为空且从节点masterip和父节点serverip相同,将从节点添加到父节点中
如果未找到,则添加到虚拟中转台中
将常规系统节点和未知中转台节点添加到根节点中
XPT模式下:
java
getXptSiteTopoData()
创建根节点网络节点
拼接节点getXptSites(),此时根据SiteID是否存在创建站点,此时集合只包含站点及1层所有子节点,目前还没有构建父子关系,其中站点ip规则如下:如果为主机那么就将中转台自己的IP设为master IP;如果为子网主机那么设为master IP;
遍历xptSiteModle,获取站点
判断站点节点和未知中转台节是否存在,不存在则创建
获取当前站点下所有中转台
先在站点的中转台中找到主机,getRepeaterNetType()==01,找到后添加到站点中
没有主机的情况看是否有子网主机,getRepeaterNetType()==04,找到后添加到站点中
循环遍历站点下所有中转台
如果存在主机,将从机挂到主机下
找到从节点序列号和主节点序列号不相同的,将从节点添加到父节点中
如果没有主机则所有中转台挂到站点下面
查看该站点的主机的MasterIP是否和其他站点的主机MasterIP一样,如果一样说明应该在同一个XPT网络下
如果没有相同的XPT网络的,则新建一个
总结:规则不同,至于为啥这么设计不清楚!
CVT模式下:判断从节点masterip和父节点serverip相同去组网
XPT模式下:在相同站点下找到第一个获取到的独立主机或者子网主机,其他从节点自动添加到父节点中





