XNMS项目-拓扑图展示

文章目录

一、背景

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") }}&nbsp;:</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模式下:在相同站点下找到第一个获取到的独立主机或者子网主机,其他从节点自动添加到父节点中

五、本人其他相关文章链接

相关推荐
夏鹏今天学习了吗3 小时前
【LeetCode热题100(95/100)】寻找重复数
算法·leetcode·职场和发展
TTGGGFF6 小时前
控制系统建模仿真(四):线性控制系统的数学模型
人工智能·算法
UXbot6 小时前
UI设计工具推荐合集
前端·人工智能·ui
kicikng6 小时前
智能体来了(西南总部)实战指南:AI调度官与AI Agent指挥官的Prompt核心逻辑
人工智能·prompt·多智能体系统
抓个马尾女孩6 小时前
为什么self-attention除以根号dk而不是其他值
人工智能·深度学习·机器学习·transformer
正在努力Coding7 小时前
SpringAI - 工具调用
java·spring·ai
叫我辉哥e17 小时前
新手进阶Python:办公看板集成ERP跨系统同步+自动备份+AI异常复盘
开发语言·人工智能·python
Loo国昌7 小时前
【LangChain1.0】第五阶段:RAG高级篇(高级检索与优化)
人工智能·后端·语言模型·架构
伊克罗德信息科技7 小时前
技术分享 | 用Dify搭建个人AI知识助手
人工智能