Browse Source

开合数据

master
review512jwy@163.com 3 weeks ago
parent
commit
73aa0d06aa
  1. 6
      src/main/java/com/techsor/datacenter/sender/controllers/MainReceiverController.java
  2. 60
      src/main/java/com/techsor/datacenter/sender/dao/CsdjDao.java
  3. 93
      src/main/java/com/techsor/datacenter/sender/entitiy/csdj/CsdjAlarmHistory.java
  4. 46
      src/main/java/com/techsor/datacenter/sender/entitiy/csdj/CsdjDigitalInputTerminal.java
  5. 2
      src/main/java/com/techsor/datacenter/sender/entitiy/csdj/CsdjEntity.java
  6. 44
      src/main/java/com/techsor/datacenter/sender/entitiy/csdj/CsdjNotificationData.java
  7. 195
      src/main/java/com/techsor/datacenter/sender/service/CsdjService.java
  8. 296
      src/main/java/com/techsor/datacenter/sender/utils/csdj/CsdjNotificationParser.java

6
src/main/java/com/techsor/datacenter/sender/controllers/MainReceiverController.java

@ -7,6 +7,7 @@ import com.techsor.datacenter.sender.config.DataSourceContextHolder;
import com.techsor.datacenter.sender.constants.CompanyConstants;
import com.techsor.datacenter.sender.dao.CsdjLogDao;
import com.techsor.datacenter.sender.entitiy.*;
import com.techsor.datacenter.sender.entitiy.csdj.CsdjEntity;
import com.techsor.datacenter.sender.entitiy.f10.RedisStreamEntity;
import com.techsor.datacenter.sender.entitiy.zaiot.ProcessZAIoTRAWEntity;
import com.techsor.datacenter.sender.processers.*;
@ -64,6 +65,9 @@ public class MainReceiverController {
@Resource
F10Service f10Service;
@Resource
CsdjService csdjService;
@Autowired
private AsyncDataProcessor asyncDataProcessor;
@ -225,6 +229,8 @@ public class MainReceiverController {
csdjEntity.getData(),
System.currentTimeMillis()
);
csdjService.handle(csdjEntity);
}catch (Exception e){
logger.error("csdjProcess error", e);
}

60
src/main/java/com/techsor/datacenter/sender/dao/CsdjDao.java

@ -0,0 +1,60 @@
package com.techsor.datacenter.sender.dao;
import com.techsor.datacenter.sender.entitiy.csdj.CsdjAlarmHistory;
import jakarta.annotation.Resource;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Component;
import java.time.LocalDateTime;
@Component
public class CsdjDao {
@Resource
private JdbcTemplate jdbcTemplate;
/**
* 插入CSDJ告警历史记录
* @param csdjAlarmHistory 告警历史对象
*/
public void insertAlarm(CsdjAlarmHistory csdjAlarmHistory) {
String sql = "INSERT IGNORE INTO csdj_alarm_history (" +
"company_id, raw_hex, terminal_id, terminal_no, " +
"alarm_time, alarm_timestamp, recovery_time, recovery_timestamp, " +
"alarm_state, state_raw, abnormal_mode, alert_history_id, " +
"created_at, updated_at" +
") VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?)";
jdbcTemplate.update(sql,
csdjAlarmHistory.getCompanyId(),
csdjAlarmHistory.getRawHex(),
csdjAlarmHistory.getTerminalId(),
csdjAlarmHistory.getTerminalNo(),
// 时间处理:LocalDateTime 转 java.sql.Timestamp
csdjAlarmHistory.getAlarmTime() != null
? java.sql.Timestamp.valueOf(csdjAlarmHistory.getAlarmTime())
: null,
csdjAlarmHistory.getAlarmTimestamp(),
csdjAlarmHistory.getRecoveryTime() != null
? java.sql.Timestamp.valueOf(csdjAlarmHistory.getRecoveryTime())
: null,
csdjAlarmHistory.getRecoveryTimestamp(),
csdjAlarmHistory.getAlarmState(),
csdjAlarmHistory.getStateRaw(),
csdjAlarmHistory.getAbnormalMode(),
csdjAlarmHistory.getAlertHistoryId(),
// 自动时间字段
csdjAlarmHistory.getCreatedAt() != null
? java.sql.Timestamp.valueOf(csdjAlarmHistory.getCreatedAt())
: java.sql.Timestamp.valueOf(LocalDateTime.now()),
csdjAlarmHistory.getUpdatedAt() != null
? java.sql.Timestamp.valueOf(csdjAlarmHistory.getUpdatedAt())
: java.sql.Timestamp.valueOf(LocalDateTime.now())
);
}
}

93
src/main/java/com/techsor/datacenter/sender/entitiy/csdj/CsdjAlarmHistory.java

@ -0,0 +1,93 @@
package com.techsor.datacenter.sender.entitiy.csdj;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.time.LocalDateTime;
/**
* CSDJ告警历史表实体类
*/
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class CsdjAlarmHistory {
/**
* 主键ID
*/
private Long id;
/**
* 公司ID
*/
private Long companyId;
/**
* 原始HEX报文
*/
private String rawHex;
/**
* 终端ID
*/
private String terminalId;
/**
* 端子番号
*/
private Integer terminalNo;
/**
* 告警时间日本时区
*/
private LocalDateTime alarmTime;
/**
* 告警时间戳毫秒
*/
private Long alarmTimestamp;
/**
* 恢复时间日本时区
*/
private LocalDateTime recoveryTime;
/**
* 恢复时间戳毫秒
*/
private Long recoveryTimestamp;
/**
* 告警状态0=已恢复1=告警中
*/
private Integer alarmState;
/**
* 端子状态原始值
*/
private Integer stateRaw;
/**
* 异常模式
*/
private Integer abnormalMode;
/**
* 告警表主键ID
*/
private Long alertHistoryId;
/**
* 创建时间
*/
private LocalDateTime createdAt;
/**
* 更新时间
*/
private LocalDateTime updatedAt;
}

46
src/main/java/com/techsor/datacenter/sender/entitiy/csdj/CsdjDigitalInputTerminal.java

@ -0,0 +1,46 @@
package com.techsor.datacenter.sender.entitiy.csdj;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* CSDJ デジタル入力端子D开头数据
*/
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class CsdjDigitalInputTerminal {
/**
* 端子番号
*/
private Integer terminalNo;
/**
* 端子状态原始字节
*/
private Integer stateRaw;
/**
* 是否是异常告警true=合上/告警false=断开/恢复
*/
private Boolean isAlarm;
/**
* 异常模式0=イベント1=パルス積算2=時間積算
*/
private Integer abnormalMode;
/**
* 附加数据只有パルス積算/時間積算时有值
*/
private Long additionalData;
/**
* 端子状态文本说明
*/
private String stateText;
}

2
src/main/java/com/techsor/datacenter/sender/entitiy/CsdjEntity.java → src/main/java/com/techsor/datacenter/sender/entitiy/csdj/CsdjEntity.java

@ -1,4 +1,4 @@
package com.techsor.datacenter.sender.entitiy;
package com.techsor.datacenter.sender.entitiy.csdj;
import lombok.Data;

44
src/main/java/com/techsor/datacenter/sender/entitiy/csdj/CsdjNotificationData.java

@ -0,0 +1,44 @@
package com.techsor.datacenter.sender.entitiy.csdj;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.time.LocalDateTime;
import java.util.List;
/**
* CSDJ 通報データ解析结果
*/
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class CsdjNotificationData {
/**
* 通報データ的总长度infoPart里的长度
*/
private Integer dataLength;
/**
* 通報类型0x07=端子状态通知トリガ動作
*/
private Integer notificationType;
/**
* 通報发生时间
*/
private LocalDateTime notificationTime;
/**
* 日本时区时间戳毫秒
*/
private Long japanTimestamp;
/**
* デジタル入力端子列表
*/
private List<CsdjDigitalInputTerminal> digitalInputTerminals;
}

195
src/main/java/com/techsor/datacenter/sender/service/CsdjService.java

@ -0,0 +1,195 @@
package com.techsor.datacenter.sender.service;
import cn.hutool.core.collection.CollectionUtil;
import com.techsor.datacenter.sender.components.CommonOpt;
import com.techsor.datacenter.sender.components.GuavaRedisCache;
import com.techsor.datacenter.sender.config.DataSourceContextHolder;
import com.techsor.datacenter.sender.dao.CsdjDao;
import com.techsor.datacenter.sender.dao.DashboardAlertDao;
import com.techsor.datacenter.sender.dao.DeviceDao;
import com.techsor.datacenter.sender.dao.F10Dao;
import com.techsor.datacenter.sender.dto.DeviceInfoVO;
import com.techsor.datacenter.sender.entitiy.DeviceEntity;
import com.techsor.datacenter.sender.entitiy.DynamodbEntity;
import com.techsor.datacenter.sender.entitiy.csdj.CsdjAlarmHistory;
import com.techsor.datacenter.sender.entitiy.csdj.CsdjDigitalInputTerminal;
import com.techsor.datacenter.sender.entitiy.csdj.CsdjEntity;
import com.techsor.datacenter.sender.entitiy.csdj.CsdjNotificationData;
import com.techsor.datacenter.sender.utils.RedisUtils;
import com.techsor.datacenter.sender.utils.csdj.CsdjNotificationParser;
import jakarta.annotation.Resource;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
/**
* CSDJ 业务处理服务
*/
@Slf4j
@Service
@RequiredArgsConstructor
public class CsdjService {
private final CsdjNotificationParser csdjNotificationParser;
private final CommonOpt commonOpt;
private final GuavaRedisCache guavaRedisCache;
private final RedisUtils redisUtils;
private final CsdjDao csdjDao;
private final DeviceDao deviceDao;
private final DashboardAlertDao dashboardAlertDao;
private final IDataProcessService dataProcessService;
/**
* 处理 CSDJ 通報数据
* @param csdjEntity 接收到的数据
*/
public void handle(CsdjEntity csdjEntity) {
try {
log.info("[CSDJ] 开始处理 CSDJ 数据,terminalId:{}, data:{}",
csdjEntity.getTerminalId(), csdjEntity.getData());
// 1. 解析通報数据
CsdjNotificationData notificationData = csdjNotificationParser.parse(csdjEntity.getData());
if (notificationData == null) {
log.warn("[CSDJ] 解析通報数据失败,terminalId:{}", csdjEntity.getTerminalId());
return;
}
log.info("[CSDJ] 解析成功,notificationType:0x{}, time:{}",
Integer.toHexString(notificationData.getNotificationType() & 0xFF).toUpperCase(),
notificationData.getNotificationTime());
// 2. 处理デジタル入力端子(D开头,告警/恢复)
List<CsdjDigitalInputTerminal> digitalTerminals = notificationData.getDigitalInputTerminals();
if (digitalTerminals != null && !digitalTerminals.isEmpty()) {
for (CsdjDigitalInputTerminal terminal : digitalTerminals) {
processDigitalTerminal(csdjEntity, notificationData, terminal);
}
} else {
log.info("[CSDJ] 没有デジタル入力端子数据");
}
log.info("[CSDJ] 处理 CSDJ 数据完成,terminalId:{}", csdjEntity.getTerminalId());
} catch (Exception e) {
log.error("[CSDJ] 处理 CSDJ 数据失败,terminalId:{}", csdjEntity.getTerminalId(), e);
}
}
/**
* 处理デジタル入力端子的告警/恢复
*/
private void processDigitalTerminal(CsdjEntity csdjEntity, CsdjNotificationData notificationData, CsdjDigitalInputTerminal terminal) {
String terminalId = csdjEntity.getTerminalId();
Integer terminalNo = terminal.getTerminalNo();
Boolean isAlarm = terminal.getIsAlarm();
String stateText = terminal.getStateText();
log.info("[CSDJ] デジタル入力端子,terminalId:{}, No:{}, state:{}, isAlarm:{}",
terminalId, terminalNo, stateText, isAlarm);
// // ========== 在这里写你的告警/恢复逻辑 ==========
// if (isAlarm) {
// // TODO: 告警逻辑(合上)
// // 比如:
// // - 查设备信息
// // - 保存告警记录
// // - 推送告警通知
// log.info("[CSDJ] 发生告警!terminalId:{}, terminalNo:{}, state:{}",
// terminalId, terminalNo, stateText);
// } else {
// // TODO: 恢复逻辑(断开)
// // 比如:
// // - 更新告警记录为恢复状态
// // - 发送恢复通知
// log.info("[CSDJ] 告警恢复!terminalId:{}, terminalNo:{}, state:{}",
// terminalId, terminalNo, stateText);
// }
String topCompanyId= commonOpt.getTopCompanyId(terminalId);
if (topCompanyId==null || topCompanyId.equals("0")){
log.error("no companyId is found========================>>>> {}",terminalId);
return;
}
DataSourceContextHolder.clearCurrentDataSourceKey();
DataSourceContextHolder.setCurrentDataSourceKey("dataSourceForCompany_"+topCompanyId);
log.info("Use datasource for company:"+topCompanyId);
List<DeviceEntity> deviceEntity=this.deviceDao.getDeviceInfoByDeviceId(terminalId);
this.guavaRedisCache.incrementDailyDeviceIdCount(terminalId);
if (CollectionUtil.isEmpty(deviceEntity)){
log.error("no deviceEntity is found========================>>>> {}",terminalId);
return;
}
long currentTs = System.currentTimeMillis();
Long companyId = deviceEntity.get(0).getCompanyId();
CsdjAlarmHistory csdjAlarmHistory = new CsdjAlarmHistory();
// 设置基础字段
csdjAlarmHistory.setCompanyId(companyId);
csdjAlarmHistory.setTerminalId(terminalId);
csdjAlarmHistory.setTerminalNo(terminalNo);
csdjAlarmHistory.setStateRaw(terminal.getStateRaw());
csdjAlarmHistory.setAbnormalMode(terminal.getAbnormalMode());
// 设置原始HEX报文
String rawHex = csdjEntity.getData() != null ? csdjEntity.getData() : "N/A";
csdjAlarmHistory.setRawHex(rawHex);
DeviceInfoVO deviceInfoVO = new DeviceInfoVO();
deviceInfoVO.setDeviceId(deviceEntity.get(0).getDeviceId());
deviceInfoVO.setBuildingId(deviceEntity.get(0).getBuildingId());
deviceInfoVO.setBigClassId(deviceEntity.get(0).getBigClassId());
DynamodbEntity entity = new DynamodbEntity();
entity.setDeviceId(terminalId);
if (isAlarm){
// 告警逻辑
dataProcessService.setAlertBlock(deviceInfoVO);
entity.setAlertBlock(deviceInfoVO.getAlertBlock());
entity.setReceive_ts(currentTs);
Long dbId = dashboardAlertDao.insertAlertHistory(entity, 3);
csdjAlarmHistory.setAlertHistoryId(dbId);
// 设置告警相关字段
csdjAlarmHistory.setAlarmTime(notificationData.getNotificationTime());
csdjAlarmHistory.setAlarmTimestamp(notificationData.getJapanTimestamp());
csdjAlarmHistory.setAlarmState(1); // 1=告警中
csdjAlarmHistory.setRecoveryTime(null);
csdjAlarmHistory.setRecoveryTimestamp(null);
log.info("[CSDJ] 发生告警!terminalId:{}, terminalNo:{}, state:{}",
terminalId, terminalNo, stateText);
} else {
// 恢复逻辑
dashboardAlertDao.updateLatestAlertToAutoRecovered(entity);
// 设置恢复相关字段
csdjAlarmHistory.setAlarmState(0); // 0=已恢复
csdjAlarmHistory.setRecoveryTime(notificationData.getNotificationTime());
csdjAlarmHistory.setRecoveryTimestamp(notificationData.getJapanTimestamp());
log.info("[CSDJ] 告警恢复!terminalId:{}, terminalNo:{}, state:{}",
terminalId, terminalNo, stateText);
}
// 插入告警历史记录
csdjDao.insertAlarm(csdjAlarmHistory);
}
}

296
src/main/java/com/techsor/datacenter/sender/utils/csdj/CsdjNotificationParser.java

@ -0,0 +1,296 @@
package com.techsor.datacenter.sender.utils.csdj;
import com.techsor.datacenter.sender.entitiy.csdj.CsdjDigitalInputTerminal;
import com.techsor.datacenter.sender.entitiy.csdj.CsdjNotificationData;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.util.ArrayList;
import java.util.List;
/**
* CSDJ 通報データ解析工具类
*/
@Slf4j
@Component
public class CsdjNotificationParser {
/**
* 日本时区 ZoneId
*/
private static final ZoneId JAPAN_ZONE = ZoneId.of("Asia/Tokyo");
/**
* 端子種別 - デジタル入力
*/
private static final byte TERMINAL_TYPE_DI = (byte) 0xC0;
/**
* 端子種別 - アナログ入力
*/
private static final byte TERMINAL_TYPE_AI = (byte) 0xC2;
/**
* 端子種別 - デジタル出力
*/
private static final byte TERMINAL_TYPE_DO = (byte) 0xC3;
/**
* 端子種別 - システム状態
*/
private static final byte TERMINAL_TYPE_SYSTEM = (byte) 0xC6;
/**
* 解析通報データ的十六进制字符串
* @param hexData 十六进制字符串带空格或不带都可以
* @return 解析结果
*/
public CsdjNotificationData parse(String hexData) {
try {
// 1. 把十六进制字符串转成字节数组
byte[] data = hexStringToBytes(hexData);
log.info("[CSDJ] 开始解析,原始数据长度:{}", data.length);
log.info("[CSDJ] 原始数据 hex:{}", bytesToHex(data));
// for (int i = 0; i < data.length; i++) {
// log.info("[CSDJ] index {} → {}", i, String.format("%02X", data[i] & 0xFF));
// }
// 2. 解析通報数据
return parseData(data);
} catch (Exception e) {
log.error("[CSDJ] 解析通報数据失败,hexData:{}", hexData, e);
return null;
}
}
/**
* 解析通報データ的字节数组
* @param data infoPart字节数组
* @return 解析结果
*/
public CsdjNotificationData parseData(byte[] data) {
if (data == null || data.length < 10) {
log.warn("[CSDJ] 数据长度太短,无法解析,length:{}", data == null ? 0 : data.length);
return null;
}
CsdjNotificationData.CsdjNotificationDataBuilder builder = CsdjNotificationData.builder();
// ========== 1. 解析开头的部分 ==========
int offset = 0;
// 数据长(2字节,大端序)
int dataLength = ((data[offset + 0] & 0xFF) << 8) | (data[offset + 1] & 0xFF);
builder.dataLength(dataLength);
offset += 2;
log.info("[CSDJ] [Offset:{}] 数据长:{}", offset - 2, dataLength);
// 端子状态通知(1字节)
int notificationType = data[offset] & 0xFF;
builder.notificationType(notificationType);
offset += 1;
log.info("[CSDJ] [Offset:{}] 通報类型:0x{}", offset - 1, Integer.toHexString(notificationType & 0xFF).toUpperCase());
// 预留(1字节)
offset += 1;
log.info("[CSDJ] [Offset:{}] 预留 1字节", offset - 1);
// 时间数据(6字节:年两位、月、日、时、分、秒,都是BCD码)
LocalDateTime time = parseTime(data, offset);
builder.notificationTime(time);
offset += 6;
log.info("[CSDJ] [Offset:{}] 时间:{}", offset - 6, time);
// 计算日本时区时间戳
if (time != null) {
long timestamp = convertToJapanTimestamp(time);
builder.japanTimestamp(timestamp);
log.info("[CSDJ] [Offset:{}] 日本时区时间戳:{}", offset - 6, timestamp);
}
log.info("[CSDJ] [Offset:{}] 开头部分解析完成,准备解析端子", offset);
// ========== 2. 解析各个端子块 ==========
List<CsdjDigitalInputTerminal> digitalInputTerminals = new ArrayList<>();
while (offset < data.length - 1) {
byte terminalType = data[offset];
offset += 1;
log.info("[CSDJ] [Offset:{}] 端子类型:0x{}", offset - 1, Integer.toHexString(terminalType & 0xFF).toUpperCase());
if (terminalType == TERMINAL_TYPE_DI) {
// ========== デジタル入力端子 ==========
if (offset >= data.length) {
log.warn("[CSDJ] 数据不足,无法读取端子数");
break;
}
int terminalCount = data[offset] & 0xFF;
offset += 1;
log.info("[CSDJ] [Offset:{}] 端子数:{}", offset - 1, terminalCount);
for (int i = 0; i < terminalCount; i++) {
if (offset + 6 > data.length) {
log.warn("[CSDJ] 数据不足,无法读取第{}个端子,offset:{}, length:{}", i + 1, offset, data.length);
break;
}
CsdjDigitalInputTerminal terminal = parseDigitalInput(data, offset);
digitalInputTerminals.add(terminal);
offset += 6; // 每个端子6字节
}
} else if (terminalType == TERMINAL_TYPE_AI || terminalType == TERMINAL_TYPE_DO || terminalType == TERMINAL_TYPE_SYSTEM) {
// ========== 其他类型暂时跳过 ==========
if (offset >= data.length) {
break;
}
int terminalCount = data[offset] & 0xFF;
offset += 1;
log.info("[CSDJ] [Offset:{}] 跳过端子类型:0x{}, 端子数:{}", offset - 1, Integer.toHexString(terminalType & 0xFF).toUpperCase(), terminalCount);
offset += 6 * terminalCount;
} else {
// 未知类型,继续往下走
log.warn("[CSDJ] [Offset:{}] 未知的端子类型:0x{}", offset - 1, Integer.toHexString(terminalType & 0xFF).toUpperCase());
}
}
builder.digitalInputTerminals(digitalInputTerminals);
log.info("[CSDJ] 解析完成,找到デジタル入力端子数:{}", digitalInputTerminals.size());
return builder.build();
}
/**
* 解析デジタル入力端子
*/
private CsdjDigitalInputTerminal parseDigitalInput(byte[] data, int offset) {
CsdjDigitalInputTerminal.CsdjDigitalInputTerminalBuilder builder = CsdjDigitalInputTerminal.builder();
// 第1字节:端子状态
byte stateByte = data[offset];
int stateRaw = stateByte & 0xFF;
builder.stateRaw(stateRaw);
offset += 1;
// 第2字节:端子番号
int terminalNo = data[offset] & 0xFF;
builder.terminalNo(terminalNo);
offset += 1;
// 第3-6字节:附加数据(大端序)
long additionalData = 0;
additionalData = (additionalData << 8) | (data[offset + 0] & 0xFF);
additionalData = (additionalData << 8) | (data[offset + 1] & 0xFF);
additionalData = (additionalData << 8) | (data[offset + 2] & 0xFF);
additionalData = (additionalData << 8) | (data[offset + 3] & 0xFF);
builder.additionalData(additionalData);
// ========== 解析端子状态 ==========
// b3位:1=異常,0=復旧
boolean isAlarm = (stateByte & 0x08) != 0;
builder.isAlarm(isAlarm);
// b6-b5位:异常模式
int abnormalMode = (stateByte >> 5) & 0x03;
builder.abnormalMode(abnormalMode);
// 状态文本
String stateText = isAlarm ? "異常(告警/合上)" : "復旧(恢复/断开)";
switch (abnormalMode) {
case 0:
stateText = isAlarm ? "イベント異常" : "イベント復旧";
break;
case 1:
stateText = isAlarm ? "パルス積算異常" : "パルス積算復旧";
break;
case 2:
stateText = isAlarm ? "時間積算異常" : "時間積算復旧";
break;
}
builder.stateText(stateText);
log.info("[CSDJ] [Offset:{}] デジタル入力端子解析:No:{}, state:0x{}, isAlarm:{}",
offset - 6, terminalNo, Integer.toHexString(stateRaw).toUpperCase(), isAlarm);
return builder.build();
}
/**
* 解析时间BCD码
*/
private LocalDateTime parseTime(byte[] data, int offset) {
try {
// BCD码转十进制
int yearBcd = data[offset + 0] & 0xFF;
int year = (yearBcd >> 4) * 10 + (yearBcd & 0x0F);
year += 2000; // 加上2000年
int monthBcd = data[offset + 1] & 0xFF;
int month = (monthBcd >> 4) * 10 + (monthBcd & 0x0F);
int dayBcd = data[offset + 2] & 0xFF;
int day = (dayBcd >> 4) * 10 + (dayBcd & 0x0F);
int hourBcd = data[offset + 3] & 0xFF;
int hour = (hourBcd >> 4) * 10 + (hourBcd & 0x0F);
int minuteBcd = data[offset + 4] & 0xFF;
int minute = (minuteBcd >> 4) * 10 + (minuteBcd & 0x0F);
int secondBcd = data[offset + 5] & 0xFF;
int second = (secondBcd >> 4) * 10 + (secondBcd & 0x0F);
return LocalDateTime.of(year, month, day, hour, minute, second);
} catch (Exception e) {
log.warn("[CSDJ] 解析时间失败", e);
return null;
}
}
/**
* LocalDateTime 转日本时区时间戳毫秒
*/
private long convertToJapanTimestamp(LocalDateTime time) {
ZonedDateTime zonedDateTime = ZonedDateTime.of(time, JAPAN_ZONE);
return zonedDateTime.toInstant().toEpochMilli();
}
/**
* 十六进制字符串转字节数组
*/
public static byte[] hexStringToBytes(String hexStr) {
// 去掉空格和换行
String cleanHex = hexStr.replaceAll("\\s+", "").toUpperCase();
if (cleanHex.length() % 2 != 0) {
throw new IllegalArgumentException("十六进制字符串长度必须为偶数");
}
int len = cleanHex.length();
byte[] data = new byte[len / 2];
for (int i = 0; i < len; i += 2) {
int high = Character.digit(cleanHex.charAt(i), 16);
int low = Character.digit(cleanHex.charAt(i + 1), 16);
if (high < 0 || low < 0) {
throw new IllegalArgumentException("无效的十六进制字符");
}
data[i / 2] = (byte) ((high << 4) | low);
}
return data;
}
/**
* 字节数组转十六进制字符串
*/
public static String bytesToHex(byte[] bytes) {
StringBuilder sb = new StringBuilder();
for (byte b : bytes) {
sb.append(String.format("%02X ", b));
}
return sb.toString().trim();
}
}
Loading…
Cancel
Save