
文章目录
一、背景
XNMS(Extended Network Management System,增强型网络管理系统)是一款远程监控和管理常规中转台的软件。中转台是系统的核心设备,所有业务都通过其进行中转。因此,只要对中转台进行监控,就能全面掌握系统的运行状况。而中转台通常部署室外,容易受到日晒雨淋等自然条件影响,造成设备损坏。为保证通讯系统正常运行,工作人员需要对中转台进行实时监控,发现中转台的异常问题,从而采取相关措施进行补救。
通过XNMS软件,工作人员可实时监控常规中转台的各项参数和告警情况,对异常问题进行排查;还可以查询或统计某时间段内中转台或终端的业务,从而全面了解常规系统的运行状况。
系统在初始化时,会自动为未来一年的业务数据按周创建独立的存储表。
二、页面


三、代码
ScheduledTaskController
java
package com.xnms.client.service.controller.config;
import com.xnms.data.service.util.DateUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Controller;
import java.util.Calendar;
import java.util.Date;
@Controller
public class ScheduledTaskController {
@Autowired
private DateUtil dateUtil;
// 定义任务,每年12月31日23:58执行
@Scheduled(cron = "0 58 23 31 12 ?")
public void executeTask() {
Date nextYearFirstMoment = getNextYearFirstMoment();
// 获取动态表名
String tempTableName = DateUtil.convertStartTimeToTableName(nextYearFirstMoment);
// 确保表已经存在,如果没有则创建
dateUtil.createTableOrNot(tempTableName, nextYearFirstMoment);
}
// 获取明年1月1日0点0分10秒的时间
public Date getNextYearFirstMoment() {
// 获取当前时间
Calendar calendar = Calendar.getInstance();
// 获取明年年份
int nextYear = calendar.get(Calendar.YEAR) + 1;
// 设置为明年1月1日0点0分0秒
calendar.set(nextYear, Calendar.JANUARY, 1, 0, 0, 0);
calendar.set(Calendar.MILLISECOND, 0);
// 加10秒
calendar.add(Calendar.SECOND, 10);
// 返回计算后的时间
return calendar.getTime();
}
}
DateUtil
java
package com.xnms.data.service.util;
import com.xnms.data.service.dao.SystemDao;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.text.SimpleDateFormat;
import java.time.LocalDate;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.List;
@Component
public class DateUtil {
private static final Logger logger = LoggerFactory.getLogger(DateUtil.class);
private static final SimpleDateFormat YMDHMS = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); // Adjust format as needed
// 假设你有 SystemDao 的实现
@Autowired
private SystemDao systemDao;
/**
* 计算业务表名
* rptbizyearweek
*
* @param startTime 开始时间
* @return 计算得到的表名
*/
public static String convertStartTimeToTableName(Date startTime) {
String tableName = "";
try {
// 使用 Calendar 获取年份和日历信息
Calendar calendar = Calendar.getInstance();
calendar.setTime(startTime);
int dayOfYear = calendar.get(Calendar.DAY_OF_YEAR);
int num1 = dayOfYear / 7;
int num2 = dayOfYear % 7;
int week;
if (num2 != 0) {
week = num1 + 1;
} else {
week = num1;
}
// 获取年份
int year = calendar.get(Calendar.YEAR);
if (String.valueOf(week).length() == 1) {
tableName = "rptbiz" + year + "0" + week;
} else {
tableName = "rptbiz" + year + week;
}
} catch (Exception ex) {
logger.error("<convertStartTimeToTableName> " + ex.getMessage(), ex);
}
return tableName;
}
public static Date getPreviousWeekDate() {
Calendar cal = Calendar.getInstance();
cal.add(Calendar.WEEK_OF_YEAR, -1);
return cal.getTime();
}
/**
* 判断要写入数据的表是否存在,不存在则创建所在年份的所有的表
*
* @param tableName 要检查的表名
* @param starttime 起始时间
*/
public void createTableOrNot(String tableName, Date starttime) {
// 判断要写入数据的表是否存在,不存在则创建所在年份的所有的表
try {
if (!systemDao.existTable(tableName)) {
int year = getYear(starttime); // 获取年份
List<String> tableNameList = new ArrayList<>();
for (int j = 1; j <= 53; j++) { // 创建 1 到 53 周的表名
String week;
if (String.valueOf(j).length() == 1) {
week = "0" + j; // 如果周数是 1 位数,加一个前导零
} else {
week = String.valueOf(j); // 否则直接使用周数
}
String tempTableName = "rptbiz" + year + week; // 生成表名
tableNameList.add(tempTableName); // 将表名添加到列表中
}
systemDao.createTables(tableNameList); // 创建表
}
} catch (Exception ex) {
logger.error("<createTableOrNot> " + ex.getMessage() +
(ex.getCause() != null ? "\r\n" + ex.getCause().getMessage() : ""), ex);
}
}
// 获取年份的辅助方法
private int getYear(Date date) {
java.util.Calendar calendar = java.util.Calendar.getInstance();
calendar.setTime(date);
return calendar.get(java.util.Calendar.YEAR);
}
public static String addDaysToCurrentDate(int days) {
return LocalDate.now().plusDays(days).toString(); // 返回 ISO 8601 格式的日期
}
public static String adjustEndTimeToLastMomentOfDay(Date endtime) {
if (endtime == null) {
return null;
}
// 将 endtime 转换为 Calendar 进行调整
java.util.Calendar calendar = java.util.Calendar.getInstance();
calendar.setTime(endtime);
calendar.set(java.util.Calendar.HOUR_OF_DAY, 23);
calendar.set(java.util.Calendar.MINUTE, 59);
calendar.set(java.util.Calendar.SECOND, 59);
calendar.set(java.util.Calendar.MILLISECOND, 999);
return YMDHMS.format(calendar.getTime());
}
public static String adjustStartTimeToFirstMomentOfDay(Date starttime) {
if (starttime == null) {
return null;
}
// 将 starttime 转换为 Calendar 进行调整
java.util.Calendar calendar = java.util.Calendar.getInstance();
calendar.setTime(starttime);
calendar.set(java.util.Calendar.HOUR_OF_DAY, 0); // 设置为00小时
calendar.set(java.util.Calendar.MINUTE, 0); // 设置为00分钟
calendar.set(java.util.Calendar.SECOND, 0); // 设置为00秒
calendar.set(java.util.Calendar.MILLISECOND, 0); // 设置为00毫秒
return YMDHMS.format(calendar.getTime());
}
// public static void main(String[] args) {
// Date startTime = new Date(); // 示例:当前日期
// String tableName = convertStartTimeToTableName(startTime);
// System.out.println("计算得到的表名: " + tableName);
// }
}
SystemDaoImpl
java
package com.xnms.data.service.dao.mysql.impl;
import com.xnms.data.contract.database.db.DbInfo;
import com.xnms.data.contract.database.db.InformationSchemaEntity;
import com.xnms.data.contract.database.db.QueryBackupDatabaseParams;
import com.xnms.data.contract.database.db.UserRoleData;
import com.xnms.data.service.dao.SystemDao;
import com.xnms.data.service.util.CommonUtil;
import com.xnms.data.service.util.DecryptionManager;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
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.io.BufferedReader;
import java.io.File;
import java.io.FileWriter;
import java.io.InputStreamReader;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.Statement;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Properties;
@Repository
public class SystemDaoImpl implements SystemDao {
private static final Logger logger = LoggerFactory.getLogger(SystemDaoImpl.class);
@PersistenceContext
private EntityManager entityManager;
private static final SimpleDateFormat dateFormat = new SimpleDateFormat("yyyyMMddHHmmss");
//todo 获取数据名称的配置
@Value("${xnms.data.service.DSDatabaseName}")
private String databaseName;
@Override
public boolean existTable(String tableName) {
List<String> bizTableNameList = getBizTableNameList();
return bizTableNameList.contains(tableName);
}
@Override
public List<String> getBizTableNameList() {
List<String> tableNameList = new ArrayList<>();
// SQL 查询语句
String sql = "SELECT TABLE_NAME FROM information_schema.tables WHERE TABLE_NAME LIKE :tableName AND TABLE_SCHEMA = :tableSchema ORDER BY TABLE_NAME";
// 设置参数
String tableNamePattern = String.format("rptbiz%s", "______");
// 创建查询
Query query = entityManager.createNativeQuery(sql);
query.setParameter("tableName", tableNamePattern);
query.setParameter("tableSchema", databaseName);
try {
tableNameList = query.getResultList();
} catch (Exception ex) {
// 日志记录错误
logger.error("<SystemDao GetBizTableNameList> Error: {}, Sql: {}", ex.getMessage(), sql, ex);
}
return tableNameList;
}
@Transactional
@Override
public void createTables(List<String> tableNameList) {
try {
// 遍历表名列表,为每个表构建 SQL
for (String tableName : tableNameList) {
String sql = String.format(
"CREATE TABLE %s (SERIAL_NO varchar(32), MODE INT(11), STATUS INT(11), TYPE INT(11), " +
"CALLTYPE INT(11), TARGETID INT(11), SENDERID INT(11), SLOT INT(11), RSSI INT(11), " +
"HANDSTATUS INT(11), STARTTIME DATETIME(3), ENDTIME DATETIME(3), DURATION BIGINT(20), " +
"EASTWEST varchar(10), NORTHSOUTH varchar(10), LAT DOUBLE DEFAULT 0, LNG DOUBLE DEFAULT 0, " +
"ELEVATION INT DEFAULT 0, CROSSSITE INT DEFAULT 1, RPTALIAS varchar(32)) " +
"ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_bin;", tableName);
// 执行创建表 SQL
entityManager.createNativeQuery(sql).executeUpdate();
// 创建索引
String indexSql1 = String.format("CREATE INDEX INDEX_SN ON %s (SERIAL_NO);", tableName);
String indexSql2 = String.format("CREATE INDEX INDEX_TARGETID ON %s (TARGETID);", tableName);
String indexSql3 = String.format("CREATE INDEX INDEX_SENDERID ON %s (SENDERID);", tableName);
String indexSql4 = String.format("CREATE INDEX INDEX_STARTTIME ON %s (STARTTIME);", tableName);
String indexSql5 = String.format("CREATE INDEX INDEX_ENDTIME ON %s (ENDTIME);", tableName);
String indexSql6 = String.format("CREATE INDEX IDX_SERIAL_START_END ON %s (SERIAL_NO, STARTTIME, ENDTIME);", tableName);
String indexSql7 = String.format("CREATE INDEX INDEX_STARTTIME_LAT_LNG ON %s (STARTTIME,LAT,LNG);", tableName);
String indexSql8 = String.format("CREATE INDEX INDEX_STARTTIME_SERIALNO_LAT_LNG ON %s (STARTTIME,SERIAL_NO,LAT,LNG);", tableName);
// 执行创建索引的 SQL
entityManager.createNativeQuery(indexSql1).executeUpdate();
entityManager.createNativeQuery(indexSql2).executeUpdate();
entityManager.createNativeQuery(indexSql3).executeUpdate();
entityManager.createNativeQuery(indexSql4).executeUpdate();
entityManager.createNativeQuery(indexSql5).executeUpdate();
entityManager.createNativeQuery(indexSql6).executeUpdate();
entityManager.createNativeQuery(indexSql7).executeUpdate();
entityManager.createNativeQuery(indexSql8).executeUpdate();
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
java
CREATE TABLE `rptbiz202604` (
`SERIAL_NO` varchar(32) COLLATE utf8_bin DEFAULT NULL,
`MODE` int(11) DEFAULT NULL,
`STATUS` int(11) DEFAULT NULL,
`TYPE` int(11) DEFAULT NULL,
`CALLTYPE` int(11) DEFAULT NULL,
`TARGETID` int(11) DEFAULT NULL,
`SENDERID` int(11) DEFAULT NULL,
`SLOT` int(11) DEFAULT NULL,
`RSSI` int(11) DEFAULT NULL,
`HANDSTATUS` int(11) DEFAULT NULL,
`STARTTIME` datetime(3) DEFAULT NULL,
`ENDTIME` datetime(3) DEFAULT NULL,
`DURATION` bigint(20) DEFAULT NULL,
`EASTWEST` varchar(10) COLLATE utf8_bin DEFAULT NULL,
`NORTHSOUTH` varchar(10) COLLATE utf8_bin DEFAULT NULL,
`LAT` double DEFAULT '0',
`LNG` double DEFAULT '0',
`ELEVATION` int(11) DEFAULT '0',
`CROSSSITE` int(11) DEFAULT '1',
`RPTALIAS` varchar(32) COLLATE utf8_bin DEFAULT NULL,
KEY `INDEX_SN` (`SERIAL_NO`),
KEY `INDEX_TARGETID` (`TARGETID`),
KEY `INDEX_SENDERID` (`SENDERID`),
KEY `INDEX_STARTTIME` (`STARTTIME`),
KEY `INDEX_ENDTIME` (`ENDTIME`),
KEY `IDX_SERIAL_START_END` (`SERIAL_NO`,`STARTTIME`,`ENDTIME`),
KEY `INDEX_STARTTIME_LAT_LNG` (`STARTTIME`,`LAT`,`LNG`),
KEY `INDEX_STARTTIME_SERIALNO_LAT_LNG` (`STARTTIME`,`SERIAL_NO`,`LAT`,`LNG`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_bin;
四、说明
说明点1:举例说明rptbiz202604表字段介绍
- SERIAL_NO:序列号
- MODE:分群或者全网【0:Normal: 全网模式、1:Selective: 分群模式】
- STATUS:状态
- 0x00:本地中转
- 0x01:本地呼叫持续时间
- 0x02:IP中转
- 0x03:IP呼叫持续时间
- 0x04:信道持续时间
- 0x05:休眠
- 0x06:远程PTT发射
- 0x07:远程PTT持续时间
- 0x08:远程PTT发射结束
- 0x09:远程PTT等待回应
- 0x0A:远程PTT TOT
- 0x0B:本地PTT发射
- 0x0C:本地PTT持续时间
- 0x0D:本地PTT发射结束
- 0x0E:本地PTT等待回应
- 0x0F:本地 PTT TOT
- 16:其他
- TYPE:业务类型
- 0: 'Query_HeaderTypeone', //None
- 1: 'Query_HeaderTypeVoice', //语音
- 2: 'Query_HeaderTypeTextMessage', //短消息
- 3: 'Query_HeaderTypeGPS', //GPS(定位协议)
- 4: 'Query_HeaderTypeRRS', //RRS(注册服务)
- 5: 'Query_HeaderTypeTelemetry', //遥测
- 6: 'Query_HeaderTypeSupplementary', //辅助业务
- 7: 'Query_HeaderTypeE2E', //端到端数据
- 31: 'Query_HeaderTypePhoneVoice', //电话语音业务
- CALLTYPE:呼叫类型
- 0: 'Query_HeaderPrivateCall', // 个呼
- 1: 'Query_HeaderGroupCall', // 组呼
- 2: 'Query_HeaderAllCall', // 全呼
- 3: 'Query_HeaderEmergencyGroupCall', // 紧急组呼
- 4: 'Query_HeaderRemoteMonitorCall', // 远程监听
- 5: 'Query_HeaderEmergencyAlarmCall', // 紧急告警
- 6: 'Query_HeaderPriorityPrivateCall', // 优先个呼
- 7: 'Query_HeaderPriorityGroupCall', // 优先组合呼
- 8: 'Query_HeaderPriorityAllCall', // 优先全呼
- 11: 'Query_CallAlert', // 呼叫提示
- 12: 'BusinessCallType_12', // 告警取消
- 13: 'BusinessCallType_13', // 对讲机检查
- 14: 'BusinessCallType_14', // 对讲机激活
- 15: 'BusinessCallType_15', // 对讲机遥毙
- 16: 'FULL_DUPLEX_CALL', // 双工呼叫
- TARGETID:目的id
- SENDERID:发送方id
- SLOT:时隙信息
- RSSI:场强值信息
- HANDSTATUS:握手状态消息
- STARTTIME:开始时间
- ENDTIME:结束时间
- DURATION:保持时间
- EASTWEST:东西经
- NORTHSOUTH:南北纬
- LAT:纬度
- LNG:经度
- ELEVATION:海拔
- CROSSSITE:跨域
- RPTALIAS:别名
说明点2:业务表用于存储中转台和终端上报的所有业务数据,包括:语音、短消息、GPS(定位协议)、RRS(注册服务)、遥测、辅助业务、端到端数据、电话语音业务。
说明点3:OID协议文档定义



