XNMS项目-业务统计-中转台RSSI

文章目录

一、背景

XNMS(Extended Network Management System,增强型网络管理系统)是一款远程监控和管理常规中转台的软件。

中转台是系统的核心设备,所有业务都通过其进行中转。因此,只要对中转台进行监控,就能全面掌握系统的运行状况。而中转台通常部署室外,容易受到日晒雨淋等自然条件影响,造成设备损坏。为保证通讯系统正常运行,工作人员需要对中转台进行实时监控,发现中转台的异常问题,从而采取相关措施进行补救。

通过XNMS软件,工作人员可实时监控常规中转台的各项参数和告警情况,对异常问题进行排查;还可以查询或统计某时间段内中转台或终端的业务,从而全面了解常规系统的运行状况。

项目采用:Arco Design+java+mysql+springboot+vue3

二、页面

中转台RSSI

统计指定时间段内,各中转台接收终端各级别RSSI的次数及其占比。



三、代码

TransferDeskRSSI.vue

java 复制代码
<template>
  <layout_1 :auto-height="true">
    <div class="--search-line">
      <div>
        <div class="key">{{ $t(queryButtonValue[7]) }}&nbsp;</div>
        <div class="val">
          <a-range-picker
            style="width: 280px"
            :allow-clear="false"
            :disabled-date="disabledDate"
            v-model="param.timeRange"
          >
            <template #suffix-icon>
              <svg-loader :width="20" :height="20" name="clock"></svg-loader>
            </template>
            <template #separator>
              <svg-loader
                :width="16"
                :height="16"
                name="arrow-right"
              ></svg-loader>
            </template>
          </a-range-picker>
        </div>
      </div>
      <div>
        <div class="key">{{ $t(queryButtonValue[8]) }}&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="search" type="primary">
        <template #icon>
          <icon-search size="18" /> </template
        >{{ $t(queryButtonValue[1]) }}
      </a-button>
      <a-button class="huge" @click="resetSearch">
        <template #icon>
          <svg-loader
            :width="20"
            :height="20"
            name="reset"
          ></svg-loader> </template
        >{{ $t(queryButtonValue[21]) }}
      </a-button>
     
    </div>
    <div class="statistics-box">
      <a-scrollbar
        outer-style="height: 100%; width: calc(100%)"
        style="height: 100%; overflow: scroll"
      >
        <div v-for="item in dataList" :key="item.id">
          <div class="title">
            {{ item.rptSN }}({{ item.alias }})
          </div>
          <div class="content" id="transfer_business_content_box">
            <div class="content-item box-1 inline">
              <div class="content-item-title">
                {{ $t(queryColumnValue[92]) }}
              </div>
              <div
                :id="`business_rssi_level_times_bar_${item.rptSN}`"
                class="chart-1"
              ></div>
            </div>
            <div class="content-item box-2 inline">
              <div class="content-item-title">
                {{ $t(queryColumnValue[93]) }}
              </div>
              <div
                :id="`business_rssi_pie_${item.rptSN}`"
                class="chart-2"
              ></div>
              <div class="chart-2-tooltip">
                <div class="chart-2-tooltip-item">
                  <div
                    class="circle inline"
                    :style="{ background: color.level_1 }"
                  ></div>
                  <div class="text inline">Good</div>
                  <div class="sp-line inline"></div>
                  <div class="percentage inline">
                    {{ item.level_1?.toFixed(2) }}%
                  </div>
                </div>
                <div class="chart-2-tooltip-item">
                  <div
                    class="circle inline"
                    :style="{ background: color.level_2 }"
                  ></div>
                  <div class="text inline">Normal</div>
                  <div class="sp-line inline"></div>
                  <div class="percentage inline">
                    {{ item.level_2?.toFixed(2) }}%
                  </div>
                </div>
                <div class="chart-2-tooltip-item">
                  <div
                    class="circle inline"
                    :style="{ background: color.level_3 }"
                  ></div>
                  <div class="text inline">Average</div>
                  <div class="sp-line inline"></div>
                  <div class="percentage inline">
                    {{ item.level_3?.toFixed(2) }}
                  </div>
                </div>
                <div class="chart-2-tooltip-item">
                  <div
                    class="circle inline"
                    :style="{ background: color.level_4 }"
                  ></div>
                  <div class="text inline">Bad</div>
                  <div class="sp-line inline"></div>
                  <div class="percentage inline">
                    {{ item.level_4?.toFixed(2) }}%
                  </div>
                </div>
                <div class="chart-2-tooltip-item">
                  <div
                    class="circle inline"
                    :style="{ background: color.level_5 }"
                  ></div>
                  <div class="text inline">Very Bad</div>
                  <div class="sp-line inline"></div>
                  <div class="percentage inline">
                    {{ item.level_5?.toFixed(2) }}%
                  </div>
                </div>
              </div>
            </div>
          </div>
        </div>
      </a-scrollbar>
    </div>
  </layout_1>
</template>

<script setup>
import Layout_1 from "@/views/pages/_common/layout_1.vue";
import * as moment from "moment/moment";
import * as echarts from "echarts";
import { nextTick, reactive, ref, inject } from "vue";
import { qryTransferNodeList } from "@/views/pages/topology/_request";
import {
  getRssiColor,
  getRssiList,
} from "@/views/pages/business/_request";
import { commonResponse } from "@/views/pages/_common";
import {
  queryButtonValue,
  queryColumnValue,
} from "@/views/pages/_common/enum";
const t = inject("t");
const param = reactive({
  timeRange: [null, null],
  repeaterSNs: [],
});
let reqParam = {
  startTime: null,
  endTime: null,
  repeaterSNs: [],
};

const treeSelectNodeData = ref([]);
const getTransferNodeList = () => {
  const principal = sessionStorage.getItem("principal");
  if (principal) {
    const principalObject = JSON.parse(principal);
    qryTransferNodeList({ userName: principalObject.userName }).then(
        (response) => {
          treeSelectNodeData.value = [
            {
              serialNo: "-1",
              name: t(queryButtonValue[22]),
              children: response.data,
            },
          ];
        }
    );
  }
};

const dataList = ref([]);
const tableLoading = ref(false);
const getAllData = () => {
  tableLoading.value = true;
  getRssiList({
    ...reqParam,
  })
    .then((response) => {
      tableLoading.value = false;
      commonResponse({
        response,
        onSuccess: () => {
          dataList.value = response.data;
          nextTick(() => {
            initCharts();
          });
        },
      });
    })
    .catch((e) => (tableLoading.value = true));
};

const initCharts = () => {
  const chartList = [];
  dataList.value?.forEach((item) => {
    const { Good, Normal, Average, Bad, Very_Bad } = item.datas;
    const total = Good + Normal + Average + Bad + Very_Bad;
    item.level_1 = total ? (Good / total) * 100 : 25;
    item.level_2 = total ? (Normal / total) * 100 : 25;
    item.level_3 = total ? (Average / total) * 100 : 25;
    item.level_4 = total ? (Bad / total) * 100 : 25;
    item.level_5 = total ? (Very_Bad / total) * 100 : 25;
    chartList.push(initLevelTimesBar(item.rptSN, item.datas));
    chartList.push(initBusinessTimePie(item.rptSN, item.datas));
  });
  window.removeEventListener("resize", () => {});
  window.addEventListener("resize", () => {
    chartList.forEach((item) => item.resize({ width: "auto", height: "auto" }));
  });
};

const color = reactive({
  level_1: "",
  level_2: "",
  level_3: "",
  level_4: "",
  level_5: "",
});
const getColor = async () => {
  return new Promise((resolve, reject) => {
    getRssiColor().then((response) => {
      commonResponse({
        response,
        onSuccess: () => {
          color.level_1 = response.data[0].color;
          color.level_2 = response.data[1].color;
          color.level_3 = response.data[2].color;
          color.level_4 = response.data[3].color;
          color.level_5 = response.data[4].color;
          resolve();
        },
      });
    });
  });
};

const initLevelTimesBar = (id, data) => {
  const chartDom = document.querySelector(
    `#business_rssi_level_times_bar_${id}`
  );
  const chart = echarts.init(chartDom);

  const option = {
    tooltip: {
      trigger: "axis",
    },
    legend: {
      itemGap: 20,
      textStyle: {
        color: "#202B40",
        fontFamily: "PingFang SC",
        fontSize: 13,
        fontWeight: 400,
        lineHeight: 22,
      },
    },
    grid: {
      left: "3%",
      right: "4%",
      bottom: "3%",
      containLabel: true,
    },
    toolbox: {
      feature: {
        saveAsImage: {},
      },
    },

    xAxis: {
      type: "category",
      boundaryGap: [40, 40],
      data: ["Good", "Normal", "Average", "Bad", "Very Bad"],
    },
    yAxis: {
      type: "value",
    },
    series: [
      {
        type: "bar",
        data: [
          { value: data.Good, itemStyle: { color: color.level_1 } },
          { value: data.Normal, itemStyle: { color: color.level_2 } },
          { value: data.Average, itemStyle: { color: color.level_3 } },
          { value: data.Bad, itemStyle: { color: color.level_4 } },
          { value: data.Very_Bad, itemStyle: { color: color.level_5 } },
        ],
        barWidth: 40,
      },
    ],
  };
  option && chart.setOption(option);
  return chart;
};

const initBusinessTimePie = (id, data) => {
  const chartDom = document.querySelector(`#business_rssi_pie_${id}`);
  const chart = echarts.init(chartDom);
  const option = {
    color: [
      color.level_1,
      color.level_2,
      color.level_3,
      color.level_4,
      color.level_5,
    ],
    tooltip: {
      trigger: "item",
      formatter: "{b} : {c}",
    },
    series: [
      {
        type: "pie",
        radius: ["50%", "80%"],
        padAngle: 0.5,
        label: {
          show: true,
          alignTo: "labelLine",
          position: "outer",
          color: "#192840",
          fontSize: 12,
          fontWeight: 400,
          lineHeight: 22,
          fontFamily: "PingFang SC",
          formatter: "{d}%",
        },
        labelLine: {
          show: true,
          length: 0,
          length2: 40,
        },
        labelLayout: {
          align: "center",
          verticalAlign: "bottom",
        },
        data: [
          { value: data.Good, name: "Good" },
          { value: data.Normal, name: "Normal" },
          { value: data.Average, name: "Average" },
          { value: data.Bad, name: "Bad" },
          { value: data.Very_Bad, name: "Very Bad" },
        ],
      },
    ],
  };
  option && chart.setOption(option);
  return chart;
};

const search = () => {
  reqParam = {
    ...reqParam,
    ...param,
  };
  reqParam.startTime = moment(reqParam.timeRange[0]).format(
    "YYYY-MM-DDT00:00:00.000"
  );
  reqParam.endTime = moment(reqParam.timeRange[1]).format(
    "YYYY-MM-DDT23:59:59.999"
  );
  delete reqParam.timeRange;
  getAllData();
};

const resetSearch = () => {
  param.timeRange = [moment().add(-9, "days"), moment()];
  param.repeaterSNs = [];
  dataList.value = [];
};

const disabledDate = (date) => {
  return date.getTime() > moment().format("x");
};

const init = async () => {
  resetSearch();
  await getColor();
  getTransferNodeList();
};

init();
</script>

<style scoped lang="less">
.statistics-box {
  box-sizing: border-box;
  height: calc(100vh - 140px);
  .title {
    overflow: hidden;
    color: #192840;
    font-family: "PingFang SC";
    font-size: 20px;
    font-style: normal;
    font-weight: 600;
    line-height: 72px; /* 160% */
    height: 72px;
  }
  :deep(.arco-scrollbar-track-direction-vertical) {
    right: -20px;
  }
  .content {
    width: 100%;
    &-item {
      position: relative;
      box-sizing: border-box;
      border: 1px solid #e5e7ec;
      border-radius: 10px;
      padding: 20px;
      &.box-1 {
        width: calc(100% - 454px);
        .chart-1 {
          height: 343px;
        }
      }
      &.box-2 {
        margin-left: 20px;
        width: 434px;
        height: 413px;
        .chart-2 {
          height: 240px;
        }
      }
      &-title {
        color: #192840;
        font-family: "PingFang SC";
        font-size: 18px;
        font-style: normal;
        font-weight: 600;
        line-height: 28px; /* 155.556% */
      }
      .chart-2-tooltip {
        position: absolute;
        display: flex;
        justify-content: space-between;
        flex-wrap: wrap;
        left: 50%;
        transform: translateX(-50%);
        margin-top: 10px;
        width: 330px;
        &-item {
          height: 22px;
          min-width: 150px;
          .circle {
            margin-top: 6px;
            border-radius: 5px;
            height: 10px;
            width: 10px;
          }
          .text {
            margin-left: 8px;
            min-width: 64px;
            color: #202b40;
            font-family: "PingFang SC";
            font-size: 14px;
            font-style: normal;
            font-weight: 400;
            line-height: 22px;
          }
          .sp-line {
            margin-top: 5px;
            margin-left: 6px;
            width: 1px;
            height: 12px;
            background-color: #dde4ed;
          }
          .percentage {
            margin-left: 12px;
            color: #7987a3;
            font-family: "PingFang SC";
            font-size: 14px;
            font-style: normal;
            font-weight: 400;
            line-height: 22px;
          }
        }
      }
    }
  }
}
</style>

RssiStatisticController

java 复制代码
package com.xnms.client.service.controller.data.statistic;

import com.xnms.client.service.ViewModel.StatRssiResult;
import com.xnms.client.service.controller.common.ResponseModel;
import com.xnms.client.service.view.naviagation.page.data.statistice.RssiStatisticUserControl;
import com.xnms.client.service.view.naviagation.page.data.statistice.StatisticSelectUserControl;
import com.xnms.data.contract.MultiResponse;
import com.xnms.data.contract.database.db.Pagination;
import com.xnms.data.contract.database.db.QueryConditionBase;
import com.xnms.data.contract.database.db.RssiLevelSetting;
import io.swagger.annotations.Api;
import io.swagger.v3.oas.annotations.Operation;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

import java.util.List;

@Api(tags = "业务统计 - 中转台RSSI")
@RestController
@RequestMapping("/api/rssi_statistic")
public class RssiStatisticController implements InitializingBean {

    private static final Logger logger = LoggerFactory.getLogger(RssiStatisticController.class);

    @Autowired
    private StatisticSelectUserControl statisticSelectUserControl;

    @Autowired
    private RssiStatisticUserControl rssiStatisticUserControl;

    @Override
    public void afterPropertiesSet() throws Exception {

    }


    @Operation(summary = "RSSI各级别次数,时长占比")
    @PostMapping
    public ResponseModel<List<StatRssiResult>> statisticRssi(@RequestBody QueryConditionBase queryConditionBase) {
        logger.info("RssiStatisticController,statisticRssi:queryConditionBase = [{}]", queryConditionBase);
        String msg = statisticSelectUserControl.verifyRssiStatisticQueryCondition(queryConditionBase, "Query_Operate_Exception");
        if (!StringUtils.isBlank(msg)) {
            return ResponseModel.ofError(msg);
        }
        MultiResponse<StatRssiResult> businessDetailDataResult = rssiStatisticUserControl.statisticRssi(queryConditionBase);
        return ResponseModel.ofSuccess(businessDetailDataResult.getData(), Pagination.of(queryConditionBase.getCurrentPage(), queryConditionBase.getPageSize(), businessDetailDataResult.getTotalCount()));
    }


    @Operation(summary = "RSSI级别枚举(颜色在这里获取)")
    @GetMapping(value = "/rssi_Level_setting")
    public ResponseModel<List<RssiLevelSetting>> rssiLevelSettings() {
        logger.info("RssiStatisticController,Rssi Start...");
        MultiResponse<RssiLevelSetting> repeaterBusinessDetailDataResult = rssiStatisticUserControl.rssiLevelSettings();
        return ResponseModel.ofSuccess(repeaterBusinessDetailDataResult.getData());
    }

}

StacRssiDao

java 复制代码
package com.xnms.data.service.dao;

import com.xnms.data.contract.database.db.StatRssi;
import com.xnms.data.service.entity.StacRssiEntity;

import java.util.Date;
import java.util.List;

public interface StacRssiDao {


    /**
     * 获取指定时间范围内的 StacRssi 实体列表
     *
     * @param fromTime 开始时间
     * @param endTime  结束时间
     * @return StacRssi 实体列表
     */
    List<StacRssiEntity> getStacRssiEntityList(Date fromTime, Date endTime);

    /**
     * 获取所有 StacRssi 实体列表
     *
     * @return StacRssi 实体列表
     */
    List<StacRssiEntity> getStacRssiEntityList();

    /**
     * 插入或更新 StacRssi 实体列表
     *
     * @param sqlList SQL 语句列表
     */
    void insertOrUpdateStacRssiEntityList(List<String> sqlList);

    /**
     * 执行 StacRssi 操作
     *
     * @return 操作是否成功
     */
    Boolean doStacRssi();

    /**
     * 获取 RSSI 统计数据
     *
     * @param startTime   开始时间
     * @param endTime     结束时间
     * @param repeaterSNs 重复器序列号列表
     * @return RSSI 统计数据列表
     */
    List<StatRssi> obtainRssiStatisticData(Date startTime, Date endTime, List<String> repeaterSNs);

    /**
     * 执行 StacRssi 操作并指定日期
     *
     * @param stacDate 指定日期
     * @return 操作是否成功
     */
    Boolean doStacRssi(Date stacDate, Date todayStart, Date tomorrowStart);
}

StacRssiDaoImpl

java 复制代码
package com.xnms.data.service.dao.mysql.impl;

import com.xnms.data.contract.database.db.StatRssi;
import com.xnms.data.service.dao.StacRssiDao;
import com.xnms.data.service.entity.StacRssiEntity;
import com.xnms.data.service.util.DateUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Transactional;

import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.Query;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

/**
 *
 */
@Repository
public class StacRssiDaoImpl implements StacRssiDao {

    private static final Logger logger = LoggerFactory.getLogger(StacRssiDaoImpl.class);

    private static final SimpleDateFormat yyyyMMdd = new SimpleDateFormat("yyyy-MM-dd");

    private static final SimpleDateFormat YMDHMS = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

    @PersistenceContext
    private EntityManager entityManager;

    private String getSql(String sqlBuilder) {
        String sql = sqlBuilder;
        if (sql.endsWith(";")) {
            sql = sql.substring(0, sql.length() - 1);
        }
        sql = "SELECT UUID() as uuid, rbres.* from ( " + sql + " ) rbres";
        return sql;
    }

    @Override
    public List<StacRssiEntity> getStacRssiEntityList(Date fromTime, Date endTime) {
        return null;
    }

    /**
     * Retrieves a list of StacRssiEntity objects.
     *
     * @return a list of StacRssiEntity objects, or an empty list if no entities are found
     */
    @Override
    public List<StacRssiEntity> getStacRssiEntityList() {
        return null;
    }


    /**
     * Inserts or updates a list of SQL statements in the database.
     *
     * @param sqlList the list of SQL statements to be executed
     */
    @Override
    public void insertOrUpdateStacRssiEntityList(List<String> sqlList) {
        try {
            entityManager.getTransaction().begin();
            for (String sql : sqlList) {
                entityManager.createNativeQuery(sql).executeUpdate();
            }
            entityManager.getTransaction().commit();
        } catch (Exception ex) {
            if (entityManager.getTransaction().isActive()) {
                entityManager.getTransaction().rollback();
            }
            throw ex;
        }
    }

    @Transactional
    @Override
    public Boolean doStacRssi() {
        List<String> sqlList = new ArrayList<>();
        try {
            for (int i = 0; i < 2; i++) {
                String dateStr = DateUtil.addDaysToCurrentDate(i - 1); // 假设 DateUtil 提供的功能
                String deleteSql = String.format("DELETE FROM stac_rssi_day WHERE date='%s';", dateStr);

                // 假设你有一个 DateUtil.CovertStartTimeToTableName 方法
                String tableName = DateUtil.convertStartTimeToTableName(yyyyMMdd.parse(dateStr));

                String insertSql = String.format(
                        "INSERT INTO stac_rssi_day (rptsn, date, count, rssi) " +
                                "SELECT serial_no AS rptsn, DATE_FORMAT(starttime, '%%Y-%%m-%%d') AS date, " +
                                "count(*) AS count, rssi " +
                                "FROM %s " +
                                "WHERE DATE_FORMAT(starttime, '%%Y-%%m-%%d') = '%s' " +
                                "GROUP BY serial_no, date, rssi;",
                        tableName, dateStr
                );

                sqlList.add(deleteSql);
                sqlList.add(insertSql);
            }

            // 执行 SQL 语句
            if (!sqlList.isEmpty()) {
                for (String sql : sqlList) {
                    Query query = entityManager.createNativeQuery(sql);
                    query.executeUpdate();  // 执行更新操作

                }
            }

            return true;
        } catch (Exception ex) {
            logger.error("<StacRssiDaoImpl doStacRssi> Error: {}, SQL: {}", ex.getMessage(), sqlList.toString(), ex);
            return false;
        }
    }


    @Override
    public List<StatRssi> obtainRssiStatisticData(Date startTime, Date endTime, List<String> repeaterSNs) {
        List<StatRssi> rssiStatisticRecords = new ArrayList<>();
        try {
            // 构建 SQL 查询字符串
            String sql = "SELECT rptsn, date, count, rssi FROM stac_rssi_day WHERE rptsn IN (:repeaterSNs) AND date >= :startDate AND date <= :endDate AND rssi != -200";
            Query query = entityManager.createNativeQuery(getSql(sql), StatRssi.class);

            // 设置参数
            query.setParameter("startDate", yyyyMMdd.format(startTime));
            query.setParameter("endDate", yyyyMMdd.format(endTime));
            query.setParameter("repeaterSNs", repeaterSNs);

            // 执行查询并获取结果
            rssiStatisticRecords = query.getResultList();

        } catch (Exception ex) {
            // 日志记录
            logger.error("[StacRssiDaoImpl] ObtainRssiStatisticData: {}", ex.getMessage(), ex);
        }

        return rssiStatisticRecords;
    }

    /**
     * Main method that serves as the entry point of the application.
     * This method attempts to perform a division by zero, which will result in an ArithmeticException.
     *
     * @param args command line arguments passed to the application
     */
    public static void main(String[] args) {
        int i = 1 / 0;
        System.out.println(i);
    }


    @Transactional
    @Override
    public Boolean doStacRssi(Date stacDate, Date todayStart, Date tomorrowStart) {
        List<String> sqlList = new ArrayList<>();
        try {
            // Create SQL list
            String stacDateStr = yyyyMMdd.format(stacDate);
            String deleteSql = String.format("DELETE FROM stac_rssi_day WHERE date = '%s';",
                    stacDateStr);
            String tableName = DateUtil.convertStartTimeToTableName(stacDate);

            String insertSql = String.format(
                    "INSERT INTO stac_rssi_day (rptsn, date, count, rssi) " +
                            "SELECT serial_no AS rptsn, DATE_FORMAT(starttime, '%%Y-%%m-%%d') AS date, " +
                            "count(*) AS count, rssi " +
                            "FROM %s " +
                            "WHERE STARTTIME between '%s' and '%s' " +
                            "GROUP BY serial_no, date, rssi;",
                    tableName, YMDHMS.format(todayStart), YMDHMS.format(tomorrowStart)
            );

            sqlList.add(deleteSql);
            sqlList.add(insertSql);
            // 执行 SQL 语句
            if (!sqlList.isEmpty()) {
                for (String sql : sqlList) {
                    Query query = entityManager.createNativeQuery(sql);
                    query.executeUpdate();  // 执行更新操作
                }
            }
            return true;
        } catch (Exception ex) {
            logger.error("<StacRssiDaoImpl doStacRssi> Error: {}", ex.getMessage(), ex);
            return false;
        }
    }
}

四、说明

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

相关推荐
刘大猫.4 天前
XNMS项目-业务统计-终端业务
业务统计·中转业务·中转个呼·中转组呼·终端业务·终端组业务·自定义报表
刘大猫.7 天前
XNMS项目-业务统计-中转业务
业务统计·中转业务·中转个呼·中转组呼·终端业务·终端组业务·自定义报表
刘大猫.1 个月前
XNMS项目-mysql数据库同步
数据库·统计·同步·数据同步·数据统计·数据库同步·业务统计
Evand J2 个月前
【MATLAB例程】多锚点RSSI定位和基站选择方法,基于GDOP、基站距离等因素。以Wi-Fi定位为例,附下载链接
开发语言·matlab·定位·gdop·rssi