Browse Source

cdsj解析

master
review512jwy@163.com 3 weeks ago
parent
commit
31166ecc93
  1. 194
      src/main/java/com/techsor/datacenter/sender/dao/CsdjDao.java
  2. 23
      src/main/java/com/techsor/datacenter/sender/entitiy/csdj/CsdjAnalogInputTerminal.java
  3. 83
      src/main/java/com/techsor/datacenter/sender/entitiy/csdj/CsdjDeviceRealtime.java
  4. 21
      src/main/java/com/techsor/datacenter/sender/entitiy/csdj/CsdjDigitalOutputTerminal.java
  5. 15
      src/main/java/com/techsor/datacenter/sender/entitiy/csdj/CsdjNotificationData.java
  6. 25
      src/main/java/com/techsor/datacenter/sender/entitiy/csdj/CsdjSystemState.java
  7. 319
      src/main/java/com/techsor/datacenter/sender/service/CsdjService.java
  8. 254
      src/main/java/com/techsor/datacenter/sender/utils/csdj/CsdjNotificationParser.java
  9. 76
      src/test/java/com/techsor/datacenter/sender/csdj/data.txt

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

@ -2,6 +2,7 @@
package com.techsor.datacenter.sender.dao; package com.techsor.datacenter.sender.dao;
import com.techsor.datacenter.sender.entitiy.csdj.CsdjAlarmHistory; import com.techsor.datacenter.sender.entitiy.csdj.CsdjAlarmHistory;
import com.techsor.datacenter.sender.entitiy.csdj.CsdjDeviceRealtime;
import jakarta.annotation.Resource; import jakarta.annotation.Resource;
import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
@ -14,15 +15,88 @@ public class CsdjDao {
@Resource @Resource
private JdbcTemplate jdbcTemplate; private JdbcTemplate jdbcTemplate;
public void insertCsdjLog(String terminalId, String rawHex, Long createdAt) {
String sql = "INSERT INTO csdj_log (terminal_id, raw_hex, created_at) VALUES (?, ?, ?)";
jdbcTemplate.update(sql, terminalId, rawHex, createdAt);
}
/** /**
* 插入CSDJ告警历史记录 * 从数据库读取设备实时数据
* @param csdjAlarmHistory 告警历史对象
*/ */
public CsdjDeviceRealtime getDeviceRealtime(String terminalId) {
String sql = "SELECT * FROM csdj_device_realtime WHERE terminal_id = ?";
try {
return jdbcTemplate.queryForObject(sql, (rs, rowNum) -> {
CsdjDeviceRealtime device = new CsdjDeviceRealtime();
device.setTerminalId(rs.getString("terminal_id"));
device.setCompanyId(rs.getLong("company_id"));
// 基础信息
java.sql.Timestamp ts = rs.getTimestamp("last_notification_time");
if (ts != null) device.setLastNotificationTime(ts.toLocalDateTime());
device.setLastNotificationTimestamp(rs.getLong("last_notification_timestamp"));
device.setLastRawHex(rs.getString("last_raw_hex"));
// デジタル入力
device.setDigitalInputJson(rs.getString("digital_input_json"));
device.setDi1No(rs.getObject("di_1_no", Integer.class));
device.setDi1IsAlarm(rs.getObject("di_1_is_alarm", Integer.class));
device.setDi1StateRaw(rs.getObject("di_1_state_raw", Integer.class));
device.setDi1StateText(rs.getString("di_1_state_text"));
device.setDi1AbnormalMode(rs.getObject("di_1_abnormal_mode", Integer.class));
device.setDi2No(rs.getObject("di_2_no", Integer.class));
device.setDi2IsAlarm(rs.getObject("di_2_is_alarm", Integer.class));
device.setDi2StateRaw(rs.getObject("di_2_state_raw", Integer.class));
device.setDi2StateText(rs.getString("di_2_state_text"));
device.setDi2AbnormalMode(rs.getObject("di_2_abnormal_mode", Integer.class));
device.setDi3No(rs.getObject("di_3_no", Integer.class));
device.setDi3IsAlarm(rs.getObject("di_3_is_alarm", Integer.class));
device.setDi3StateRaw(rs.getObject("di_3_state_raw", Integer.class));
device.setDi3StateText(rs.getString("di_3_state_text"));
device.setDi3AbnormalMode(rs.getObject("di_3_abnormal_mode", Integer.class));
// アナログ入力
device.setAnalogInputJson(rs.getString("analog_input_json"));
device.setAi1No(rs.getObject("ai_1_no", Integer.class));
device.setAi1IsThresholdAlarm(rs.getObject("ai_1_is_threshold_alarm", Integer.class));
device.setAi1ThresholdNo(rs.getObject("ai_1_threshold_no", Integer.class));
device.setAi1AdditionalData(rs.getObject("ai_1_additional_data", Long.class));
device.setAi1StateRaw(rs.getObject("ai_1_state_raw", Integer.class));
device.setAi2No(rs.getObject("ai_2_no", Integer.class));
device.setAi2IsThresholdAlarm(rs.getObject("ai_2_is_threshold_alarm", Integer.class));
device.setAi2ThresholdNo(rs.getObject("ai_2_threshold_no", Integer.class));
device.setAi2AdditionalData(rs.getObject("ai_2_additional_data", Long.class));
device.setAi2StateRaw(rs.getObject("ai_2_state_raw", Integer.class));
// デジタル出力
device.setDigitalOutputJson(rs.getString("digital_output_json"));
device.setDo1No(rs.getObject("do_1_no", Integer.class));
device.setDo1OutputState(rs.getString("do_1_output_state"));
device.setDo1StateRaw(rs.getObject("do_1_state_raw", Integer.class));
device.setDo1AdditionalData(rs.getObject("do_1_additional_data", Long.class));
device.setDo2No(rs.getObject("do_2_no", Integer.class));
device.setDo2OutputState(rs.getString("do_2_output_state"));
device.setDo2StateRaw(rs.getObject("do_2_state_raw", Integer.class));
device.setDo2AdditionalData(rs.getObject("do_2_additional_data", Long.class));
// システム状態
device.setSystemStateNo(rs.getObject("system_state_no", Integer.class));
device.setSystemScheduledTimeNo(rs.getObject("system_scheduled_time_no", Integer.class));
device.setSystemModeState(rs.getObject("system_mode_state", Integer.class));
device.setSystemIsPowerOff(rs.getObject("system_is_power_off", Integer.class));
device.setSystemIsBatteryLow(rs.getObject("system_is_battery_low", Integer.class));
device.setSystemIsLineDisconnected(rs.getObject("system_is_line_disconnected", Integer.class));
device.setSystemIsSlaveCommunicationError(rs.getObject("system_is_slave_communication_error", Integer.class));
device.setSystemStateRaw(rs.getObject("system_state_raw", Integer.class));
return device;
}, terminalId);
} catch (Exception e) {
// 如果没有找到记录,返回null
return null;
}
}
public void insertCsdjLog(String terminalId, String rawHex, Long createdAt, String parsedJson, String parsedContent) {
String sql = "INSERT INTO csdj_log (terminal_id, raw_hex, created_at, parsed_json, parsed_content) VALUES (?, ?, ?, ?, ?)";
jdbcTemplate.update(sql, terminalId, rawHex, createdAt, parsedJson, parsedContent);
}
public void insertAlarm(CsdjAlarmHistory csdjAlarmHistory) { public void insertAlarm(CsdjAlarmHistory csdjAlarmHistory) {
String sql = "INSERT IGNORE INTO csdj_alarm_history (" + String sql = "INSERT IGNORE INTO csdj_alarm_history (" +
"company_id, raw_hex, terminal_id, terminal_no, " + "company_id, raw_hex, terminal_id, terminal_no, " +
@ -37,7 +111,6 @@ public class CsdjDao {
csdjAlarmHistory.getTerminalId(), csdjAlarmHistory.getTerminalId(),
csdjAlarmHistory.getTerminalNo(), csdjAlarmHistory.getTerminalNo(),
// 时间处理:LocalDateTime 转 java.sql.Timestamp
csdjAlarmHistory.getAlarmTime() != null csdjAlarmHistory.getAlarmTime() != null
? java.sql.Timestamp.valueOf(csdjAlarmHistory.getAlarmTime()) ? java.sql.Timestamp.valueOf(csdjAlarmHistory.getAlarmTime())
: null, : null,
@ -52,7 +125,6 @@ public class CsdjDao {
csdjAlarmHistory.getAbnormalMode(), csdjAlarmHistory.getAbnormalMode(),
csdjAlarmHistory.getAlertHistoryId(), csdjAlarmHistory.getAlertHistoryId(),
// 自动时间字段
csdjAlarmHistory.getCreatedAt() != null csdjAlarmHistory.getCreatedAt() != null
? java.sql.Timestamp.valueOf(csdjAlarmHistory.getCreatedAt()) ? java.sql.Timestamp.valueOf(csdjAlarmHistory.getCreatedAt())
: java.sql.Timestamp.valueOf(LocalDateTime.now()), : java.sql.Timestamp.valueOf(LocalDateTime.now()),
@ -62,4 +134,108 @@ public class CsdjDao {
); );
} }
public void saveOrUpdateDeviceRealtime(CsdjDeviceRealtime device) {
String sql = "INSERT INTO csdj_device_realtime (" +
"terminal_id, company_id, " +
"last_notification_time, last_notification_timestamp, last_raw_hex, " +
"digital_input_json, " +
"di_1_no, di_1_is_alarm, di_1_state_raw, di_1_state_text, di_1_abnormal_mode, " +
"di_2_no, di_2_is_alarm, di_2_state_raw, di_2_state_text, di_2_abnormal_mode, " +
"di_3_no, di_3_is_alarm, di_3_state_raw, di_3_state_text, di_3_abnormal_mode, " +
"analog_input_json, " +
"ai_1_no, ai_1_is_threshold_alarm, ai_1_threshold_no, ai_1_additional_data, ai_1_state_raw, " +
"ai_2_no, ai_2_is_threshold_alarm, ai_2_threshold_no, ai_2_additional_data, ai_2_state_raw, " +
"digital_output_json, " +
"do_1_no, do_1_output_state, do_1_state_raw, do_1_additional_data, " +
"do_2_no, do_2_output_state, do_2_state_raw, do_2_additional_data, " +
"system_state_no, system_scheduled_time_no, system_mode_state, " +
"system_is_power_off, system_is_battery_low, system_is_line_disconnected, " +
"system_is_slave_communication_error, system_state_raw, " +
"created_at, updated_at" +
") VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) " +
"ON DUPLICATE KEY UPDATE " +
"company_id = VALUES(company_id), " +
"last_notification_time = VALUES(last_notification_time), " +
"last_notification_timestamp = VALUES(last_notification_timestamp), " +
"last_raw_hex = VALUES(last_raw_hex), " +
"digital_input_json = VALUES(digital_input_json), " +
"di_1_no = VALUES(di_1_no), di_1_is_alarm = VALUES(di_1_is_alarm), di_1_state_raw = VALUES(di_1_state_raw), di_1_state_text = VALUES(di_1_state_text), di_1_abnormal_mode = VALUES(di_1_abnormal_mode), " +
"di_2_no = VALUES(di_2_no), di_2_is_alarm = VALUES(di_2_is_alarm), di_2_state_raw = VALUES(di_2_state_raw), di_2_state_text = VALUES(di_2_state_text), di_2_abnormal_mode = VALUES(di_2_abnormal_mode), " +
"di_3_no = VALUES(di_3_no), di_3_is_alarm = VALUES(di_3_is_alarm), di_3_state_raw = VALUES(di_3_state_raw), di_3_state_text = VALUES(di_3_state_text), di_3_abnormal_mode = VALUES(di_3_abnormal_mode), " +
"analog_input_json = VALUES(analog_input_json), " +
"ai_1_no = VALUES(ai_1_no), ai_1_is_threshold_alarm = VALUES(ai_1_is_threshold_alarm), ai_1_threshold_no = VALUES(ai_1_threshold_no), ai_1_additional_data = VALUES(ai_1_additional_data), ai_1_state_raw = VALUES(ai_1_state_raw), " +
"ai_2_no = VALUES(ai_2_no), ai_2_is_threshold_alarm = VALUES(ai_2_is_threshold_alarm), ai_2_threshold_no = VALUES(ai_2_threshold_no), ai_2_additional_data = VALUES(ai_2_additional_data), ai_2_state_raw = VALUES(ai_2_state_raw), " +
"digital_output_json = VALUES(digital_output_json), " +
"do_1_no = VALUES(do_1_no), do_1_output_state = VALUES(do_1_output_state), do_1_state_raw = VALUES(do_1_state_raw), do_1_additional_data = VALUES(do_1_additional_data), " +
"do_2_no = VALUES(do_2_no), do_2_output_state = VALUES(do_2_output_state), do_2_state_raw = VALUES(do_2_state_raw), do_2_additional_data = VALUES(do_2_additional_data), " +
"system_state_no = VALUES(system_state_no), system_scheduled_time_no = VALUES(system_scheduled_time_no), system_mode_state = VALUES(system_mode_state), " +
"system_is_power_off = VALUES(system_is_power_off), system_is_battery_low = VALUES(system_is_battery_low), system_is_line_disconnected = VALUES(system_is_line_disconnected), " +
"system_is_slave_communication_error = VALUES(system_is_slave_communication_error), system_state_raw = VALUES(system_state_raw), " +
"updated_at = VALUES(updated_at)";
jdbcTemplate.update(sql,
device.getTerminalId(),
device.getCompanyId(),
device.getLastNotificationTime() != null
? java.sql.Timestamp.valueOf(device.getLastNotificationTime())
: null,
device.getLastNotificationTimestamp(),
device.getLastRawHex(),
device.getDigitalInputJson(),
device.getDi1No(),
device.getDi1IsAlarm(),
device.getDi1StateRaw(),
device.getDi1StateText(),
device.getDi1AbnormalMode(),
device.getDi2No(),
device.getDi2IsAlarm(),
device.getDi2StateRaw(),
device.getDi2StateText(),
device.getDi2AbnormalMode(),
device.getDi3No(),
device.getDi3IsAlarm(),
device.getDi3StateRaw(),
device.getDi3StateText(),
device.getDi3AbnormalMode(),
device.getAnalogInputJson(),
device.getAi1No(),
device.getAi1IsThresholdAlarm(),
device.getAi1ThresholdNo(),
device.getAi1AdditionalData(),
device.getAi1StateRaw(),
device.getAi2No(),
device.getAi2IsThresholdAlarm(),
device.getAi2ThresholdNo(),
device.getAi2AdditionalData(),
device.getAi2StateRaw(),
device.getDigitalOutputJson(),
device.getDo1No(),
device.getDo1OutputState(),
device.getDo1StateRaw(),
device.getDo1AdditionalData(),
device.getDo2No(),
device.getDo2OutputState(),
device.getDo2StateRaw(),
device.getDo2AdditionalData(),
device.getSystemStateNo(),
device.getSystemScheduledTimeNo(),
device.getSystemModeState(),
device.getSystemIsPowerOff(),
device.getSystemIsBatteryLow(),
device.getSystemIsLineDisconnected(),
device.getSystemIsSlaveCommunicationError(),
device.getSystemStateRaw(),
java.sql.Timestamp.valueOf(LocalDateTime.now()),
java.sql.Timestamp.valueOf(LocalDateTime.now())
);
}
} }

23
src/main/java/com/techsor/datacenter/sender/entitiy/csdj/CsdjAnalogInputTerminal.java

@ -0,0 +1,23 @@
package com.techsor.datacenter.sender.entitiy.csdj;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* CSDJアナログ入力端子实体类
*/
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class CsdjAnalogInputTerminal {
private Integer terminalNo; // 端子番号
private Integer stateRaw; // 端子状态原始值
private Boolean isThresholdAlarm; // 是否阈值告警
private Integer thresholdNo; // 阈值编号
private Long additionalData; // 附加数据(模拟值/累计值)
private String stateText; // 状态文本
}

83
src/main/java/com/techsor/datacenter/sender/entitiy/csdj/CsdjDeviceRealtime.java

@ -0,0 +1,83 @@
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 CsdjDeviceRealtime {
private Long id;
private String terminalId;
private Long companyId;
// ===== 通報基础信息 =====
private LocalDateTime lastNotificationTime;
private Long lastNotificationTimestamp;
private String lastRawHex;
// ===== デジタル入力端子 =====
private String digitalInputJson;
private Integer di1No;
private Integer di1IsAlarm;
private Integer di1StateRaw;
private String di1StateText;
private Integer di1AbnormalMode;
private Integer di2No;
private Integer di2IsAlarm;
private Integer di2StateRaw;
private String di2StateText;
private Integer di2AbnormalMode;
private Integer di3No;
private Integer di3IsAlarm;
private Integer di3StateRaw;
private String di3StateText;
private Integer di3AbnormalMode;
// ===== アナログ入力端子 =====
private String analogInputJson;
private Integer ai1No;
private Integer ai1IsThresholdAlarm;
private Integer ai1ThresholdNo;
private Long ai1AdditionalData;
private Integer ai1StateRaw;
private Integer ai2No;
private Integer ai2IsThresholdAlarm;
private Integer ai2ThresholdNo;
private Long ai2AdditionalData;
private Integer ai2StateRaw;
// ===== デジタル出力端子 =====
private String digitalOutputJson;
private Integer do1No;
private String do1OutputState;
private Integer do1StateRaw;
private Long do1AdditionalData;
private Integer do2No;
private String do2OutputState;
private Integer do2StateRaw;
private Long do2AdditionalData;
// ===== システム状態 =====
private Integer systemStateNo;
private Integer systemScheduledTimeNo;
private Integer systemModeState;
private Integer systemIsPowerOff;
private Integer systemIsBatteryLow;
private Integer systemIsLineDisconnected;
private Integer systemIsSlaveCommunicationError;
private Integer systemStateRaw;
// ===== 时间 =====
private LocalDateTime createdAt;
private LocalDateTime updatedAt;
}

21
src/main/java/com/techsor/datacenter/sender/entitiy/csdj/CsdjDigitalOutputTerminal.java

@ -0,0 +1,21 @@
package com.techsor.datacenter.sender.entitiy.csdj;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* CSDJデジタル出力端子实体类
*/
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class CsdjDigitalOutputTerminal {
private Integer terminalNo; // 端子番号
private Integer stateRaw; // 端子状态原始值
private String outputState; // 输出接点状态(OFF/ON)
private Long additionalData; // 附加数据
}

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

@ -41,4 +41,19 @@ public class CsdjNotificationData {
* デジタル入力端子列表 * デジタル入力端子列表
*/ */
private List<CsdjDigitalInputTerminal> digitalInputTerminals; private List<CsdjDigitalInputTerminal> digitalInputTerminals;
/**
* アナログ入力端子列表
*/
private List<CsdjAnalogInputTerminal> analogInputTerminals;
/**
* デジタル出力端子列表
*/
private List<CsdjDigitalOutputTerminal> digitalOutputTerminals;
/**
* システム状態
*/
private CsdjSystemState systemState;
} }

25
src/main/java/com/techsor/datacenter/sender/entitiy/csdj/CsdjSystemState.java

@ -0,0 +1,25 @@
package com.techsor.datacenter.sender.entitiy.csdj;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* CSDJシステム状態实体类
*/
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class CsdjSystemState {
private Integer stateNo; // 状态番号
private Integer scheduledTimeNo; // 定时编号
private Integer modeState; // 模式状态
private Boolean isPowerOff; // 停电状态(0=正常,1=停电)
private Boolean isBatteryLow; // 电池状态(0=正常,1=低)
private Boolean isLineDisconnected; // 线路状态(0=正常,1=断开)
private Boolean isSlaveCommunicationError; // 子机通信状态
private Integer stateRaw; // 原始状态值
}

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

@ -8,52 +8,38 @@ import com.techsor.datacenter.sender.config.DataSourceContextHolder;
import com.techsor.datacenter.sender.dao.CsdjDao; import com.techsor.datacenter.sender.dao.CsdjDao;
import com.techsor.datacenter.sender.dao.DashboardAlertDao; import com.techsor.datacenter.sender.dao.DashboardAlertDao;
import com.techsor.datacenter.sender.dao.DeviceDao; 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.dto.DeviceInfoVO;
import com.techsor.datacenter.sender.entitiy.DeviceEntity; import com.techsor.datacenter.sender.entitiy.DeviceEntity;
import com.techsor.datacenter.sender.entitiy.DynamodbEntity; import com.techsor.datacenter.sender.entitiy.DynamodbEntity;
import com.techsor.datacenter.sender.entitiy.csdj.CsdjAlarmHistory; import com.techsor.datacenter.sender.entitiy.csdj.*;
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 com.techsor.datacenter.sender.utils.csdj.CsdjNotificationParser;
import jakarta.annotation.Resource; import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map;
/**
* CSDJ 业务处理服务
*/
@Slf4j @Slf4j
@Service @Service
@RequiredArgsConstructor @RequiredArgsConstructor
public class CsdjService { public class CsdjService {
private final CsdjNotificationParser csdjNotificationParser; private final CsdjNotificationParser csdjNotificationParser;
private final CommonOpt commonOpt; private final CommonOpt commonOpt;
private final GuavaRedisCache guavaRedisCache; private final GuavaRedisCache guavaRedisCache;
private final RedisUtils redisUtils;
private final CsdjDao csdjDao; private final CsdjDao csdjDao;
private final DeviceDao deviceDao; private final DeviceDao deviceDao;
private final DashboardAlertDao dashboardAlertDao; private final DashboardAlertDao dashboardAlertDao;
private final IDataProcessService dataProcessService; private final IDataProcessService dataProcessService;
/** private final ObjectMapper objectMapper = new ObjectMapper()
* 处理 CSDJ 通報数据 .registerModule(new JavaTimeModule());
* @param csdjEntity 接收到的数据
*/
public void handle(CsdjEntity csdjEntity) { public void handle(CsdjEntity csdjEntity) {
try { try {
log.info("[CSDJ] 开始处理 CSDJ 数据,terminalId:{}, data:{}", log.info("[CSDJ] 开始处理 CSDJ 数据,terminalId:{}, data:{}",
@ -61,29 +47,30 @@ public class CsdjService {
String terminalId = csdjEntity.getTerminalId(); String terminalId = csdjEntity.getTerminalId();
String topCompanyId= commonOpt.getTopCompanyId(terminalId); String topCompanyId = commonOpt.getTopCompanyId(terminalId);
if (topCompanyId==null || topCompanyId.equals("0")){ if (topCompanyId == null || topCompanyId.equals("0")) {
log.error("no companyId is found========================>>>> {}",terminalId); log.error("no companyId is found========================>>>> {}", terminalId);
return; return;
} }
DataSourceContextHolder.clearCurrentDataSourceKey(); DataSourceContextHolder.clearCurrentDataSourceKey();
DataSourceContextHolder.setCurrentDataSourceKey("dataSourceForCompany_"+topCompanyId); DataSourceContextHolder.setCurrentDataSourceKey("dataSourceForCompany_" + topCompanyId);
log.info("Use datasource for company:"+topCompanyId);
long currentTs = System.currentTimeMillis(); log.info("Use datasource for company:" + topCompanyId);
//存入 csdj_log 表 List<DeviceEntity> deviceEntitys = this.deviceDao.getDeviceInfoByDeviceId(terminalId);
csdjDao.insertCsdjLog( this.guavaRedisCache.incrementDailyDeviceIdCount(terminalId);
csdjEntity.getTerminalId(), if (CollectionUtil.isEmpty(deviceEntitys)) {
csdjEntity.getData(), log.error("no deviceEntity is found========================>>>> {}", terminalId);
csdjEntity.getTs() return;
); }
DeviceEntity deviceEntity = deviceEntitys.get(0);
// 1. 解析通報数据 // 1. 解析通報数据
CsdjNotificationData notificationData = csdjNotificationParser.parse(csdjEntity.getData()); CsdjNotificationData notificationData = csdjNotificationParser.parse(csdjEntity.getData());
if (notificationData == null) { if (notificationData == null) {
log.warn("[CSDJ] 解析通報数据失败,terminalId:{}", csdjEntity.getTerminalId()); log.warn("[CSDJ] 解析通報数据失败,terminalId:{}", csdjEntity.getTerminalId());
// 即使解析失败,也先存入原始数据
csdjDao.insertCsdjLog(terminalId, csdjEntity.getData(), csdjEntity.getTs(), null, null);
return; return;
} }
@ -91,40 +78,39 @@ public class CsdjService {
Integer.toHexString(notificationData.getNotificationType() & 0xFF).toUpperCase(), Integer.toHexString(notificationData.getNotificationType() & 0xFF).toUpperCase(),
notificationData.getNotificationTime()); notificationData.getNotificationTime());
// 2. 处理デジタル入力端子(D开头,告警/恢复) // 调试信息
log.info("[CSDJ] デジタル入力端子列表: {}", notificationData.getDigitalInputTerminals());
log.info("[CSDJ] デジタル入力端子数量: {}",
(notificationData.getDigitalInputTerminals() == null) ?
"null" : notificationData.getDigitalInputTerminals().size());
// 2. 构建解析后的 JSON 和内容描述
String parsedJson = buildParsedJson(notificationData);
String parsedContent = buildParsedContent(notificationData);
// 3. 存入 csdj_log 表
csdjDao.insertCsdjLog(terminalId, csdjEntity.getData(), csdjEntity.getTs(), parsedJson, parsedContent);
// 4. 处理デジタル入力端子(告警/恢复)
List<CsdjDigitalInputTerminal> digitalTerminals = notificationData.getDigitalInputTerminals(); List<CsdjDigitalInputTerminal> digitalTerminals = notificationData.getDigitalInputTerminals();
if (digitalTerminals != null && !digitalTerminals.isEmpty()) { if (digitalTerminals != null && !digitalTerminals.isEmpty()) {
for (CsdjDigitalInputTerminal terminal : digitalTerminals) { for (CsdjDigitalInputTerminal terminal : digitalTerminals) {
processDigitalTerminal(csdjEntity, notificationData, terminal); processDigitalTerminal(csdjEntity, notificationData, terminal, deviceEntity);
} }
} else {
log.info("[CSDJ] 没有デジタル入力端子数据");
} }
// 5. 更新 csdj_device_realtime 表
Long companyId = deviceEntity.getCompanyId();
saveDeviceRealtime(terminalId, companyId, csdjEntity.getData(), notificationData);
log.info("[CSDJ] 处理 CSDJ 数据完成,terminalId:{}", csdjEntity.getTerminalId()); log.info("[CSDJ] 处理 CSDJ 数据完成,terminalId:{}", csdjEntity.getTerminalId());
} catch (Exception e) { } catch (Exception e) {
log.error("[CSDJ] 处理 CSDJ 数据失败,terminalId:{}", csdjEntity.getTerminalId(), e); log.error("[CSDJ] 处理 CSDJ 数据失败,terminalId:{}", csdjEntity.getTerminalId(), e);
} }
} }
/** private void processDigitalTerminal(CsdjEntity csdjEntity, CsdjNotificationData notificationData,
* 处理デジタル入力端子的告警/恢复 CsdjDigitalInputTerminal terminal, DeviceEntity deviceEntity) {
*
* 告警
* {
* "terminalId": "TEST002",
* "data": "00 0E 07 00 25 06 20 0E 00 00 00 C0 01 08 01 00 00 00 00",
* "ts": "1782272698000"
* }
*
* 恢复
* {
* "terminalId": "TEST002",
* "data": "00 0E 07 00 25 06 20 0E 00 00 00 C0 01 00 01 00 00 00 00",
* "ts": "1782272698000"
* }
*/
private void processDigitalTerminal(CsdjEntity csdjEntity, CsdjNotificationData notificationData, CsdjDigitalInputTerminal terminal) {
String terminalId = csdjEntity.getTerminalId(); String terminalId = csdjEntity.getTerminalId();
Integer terminalNo = terminal.getTerminalNo(); Integer terminalNo = terminal.getTerminalNo();
Boolean isAlarm = terminal.getIsAlarm(); Boolean isAlarm = terminal.getIsAlarm();
@ -133,56 +119,27 @@ public class CsdjService {
log.info("[CSDJ] デジタル入力端子,terminalId:{}, No:{}, state:{}, isAlarm:{}", log.info("[CSDJ] デジタル入力端子,terminalId:{}, No:{}, state:{}, isAlarm:{}",
terminalId, terminalNo, stateText, isAlarm); terminalId, terminalNo, stateText, isAlarm);
// // ========== 在这里写你的告警/恢复逻辑 ========== Long companyId = deviceEntity.getCompanyId();
// if (isAlarm) {
// // TODO: 告警逻辑(合上)
// // 比如:
// // - 查设备信息
// // - 保存告警记录
// // - 推送告警通知
// log.info("[CSDJ] 发生告警!terminalId:{}, terminalNo:{}, state:{}",
// terminalId, terminalNo, stateText);
// } else {
// // TODO: 恢复逻辑(断开)
// // 比如:
// // - 更新告警记录为恢复状态
// // - 发送恢复通知
// log.info("[CSDJ] 告警恢复!terminalId:{}, terminalNo:{}, state:{}",
// terminalId, terminalNo, stateText);
// }
List<DeviceEntity> deviceEntity=this.deviceDao.getDeviceInfoByDeviceId(terminalId);
this.guavaRedisCache.incrementDailyDeviceIdCount(terminalId);
if (CollectionUtil.isEmpty(deviceEntity)){
log.error("no deviceEntity is found========================>>>> {}",terminalId);
return;
}
Long companyId = deviceEntity.get(0).getCompanyId();
CsdjAlarmHistory csdjAlarmHistory = new CsdjAlarmHistory(); CsdjAlarmHistory csdjAlarmHistory = new CsdjAlarmHistory();
// 设置基础字段
csdjAlarmHistory.setCompanyId(companyId); csdjAlarmHistory.setCompanyId(companyId);
csdjAlarmHistory.setTerminalId(terminalId); csdjAlarmHistory.setTerminalId(terminalId);
csdjAlarmHistory.setTerminalNo(terminalNo); csdjAlarmHistory.setTerminalNo(terminalNo);
csdjAlarmHistory.setStateRaw(terminal.getStateRaw()); csdjAlarmHistory.setStateRaw(terminal.getStateRaw());
csdjAlarmHistory.setAbnormalMode(terminal.getAbnormalMode()); csdjAlarmHistory.setAbnormalMode(terminal.getAbnormalMode());
// 设置原始HEX报文
String rawHex = csdjEntity.getData() != null ? csdjEntity.getData() : "N/A"; String rawHex = csdjEntity.getData() != null ? csdjEntity.getData() : "N/A";
csdjAlarmHistory.setRawHex(rawHex); csdjAlarmHistory.setRawHex(rawHex);
DeviceInfoVO deviceInfoVO = new DeviceInfoVO(); DeviceInfoVO deviceInfoVO = new DeviceInfoVO();
deviceInfoVO.setDeviceId(deviceEntity.get(0).getDeviceId()); deviceInfoVO.setDeviceId(deviceEntity.getDeviceId());
deviceInfoVO.setBuildingId(deviceEntity.get(0).getBuildingId()); deviceInfoVO.setBuildingId(deviceEntity.getBuildingId());
deviceInfoVO.setBigClassId(deviceEntity.get(0).getBigClassId()); deviceInfoVO.setBigClassId(deviceEntity.getBigClassId());
DynamodbEntity entity = new DynamodbEntity(); DynamodbEntity entity = new DynamodbEntity();
entity.setDeviceId(terminalId); entity.setDeviceId(terminalId);
if (isAlarm){ if (isAlarm) {
// 告警逻辑
dataProcessService.setAlertBlock(deviceInfoVO); dataProcessService.setAlertBlock(deviceInfoVO);
entity.setAlertBlock(deviceInfoVO.getAlertBlock()); entity.setAlertBlock(deviceInfoVO.getAlertBlock());
@ -190,21 +147,18 @@ public class CsdjService {
Long dbId = dashboardAlertDao.insertAlertHistory(entity, 3); Long dbId = dashboardAlertDao.insertAlertHistory(entity, 3);
csdjAlarmHistory.setAlertHistoryId(dbId); csdjAlarmHistory.setAlertHistoryId(dbId);
// 设置告警相关字段
csdjAlarmHistory.setAlarmTime(notificationData.getNotificationTime()); csdjAlarmHistory.setAlarmTime(notificationData.getNotificationTime());
csdjAlarmHistory.setAlarmTimestamp(notificationData.getJapanTimestamp()); csdjAlarmHistory.setAlarmTimestamp(notificationData.getJapanTimestamp());
csdjAlarmHistory.setAlarmState(1); // 1=告警中 csdjAlarmHistory.setAlarmState(1);
csdjAlarmHistory.setRecoveryTime(null); csdjAlarmHistory.setRecoveryTime(null);
csdjAlarmHistory.setRecoveryTimestamp(null); csdjAlarmHistory.setRecoveryTimestamp(null);
log.info("[CSDJ] 发生告警!terminalId:{}, terminalNo:{}, state:{}", log.info("[CSDJ] 发生告警!terminalId:{}, terminalNo:{}, state:{}",
terminalId, terminalNo, stateText); terminalId, terminalNo, stateText);
} else { } else {
// 恢复逻辑
dashboardAlertDao.updateLatestAlertToAutoRecovered(entity); dashboardAlertDao.updateLatestAlertToAutoRecovered(entity);
// 设置恢复相关字段 csdjAlarmHistory.setAlarmState(0);
csdjAlarmHistory.setAlarmState(0); // 0=已恢复
csdjAlarmHistory.setRecoveryTime(notificationData.getNotificationTime()); csdjAlarmHistory.setRecoveryTime(notificationData.getNotificationTime());
csdjAlarmHistory.setRecoveryTimestamp(notificationData.getJapanTimestamp()); csdjAlarmHistory.setRecoveryTimestamp(notificationData.getJapanTimestamp());
@ -212,8 +166,177 @@ public class CsdjService {
terminalId, terminalNo, stateText); terminalId, terminalNo, stateText);
} }
// 插入告警历史记录
csdjDao.insertAlarm(csdjAlarmHistory); csdjDao.insertAlarm(csdjAlarmHistory);
}
private void saveDeviceRealtime(String terminalId, Long companyId, String rawHex,
CsdjNotificationData data) {
try {
// 1. 先从数据库读取旧数据
CsdjDeviceRealtime deviceRealtime = csdjDao.getDeviceRealtime(terminalId);
if (deviceRealtime == null) {
// 如果是新设备,初始化
deviceRealtime = new CsdjDeviceRealtime();
deviceRealtime.setTerminalId(terminalId);
}
deviceRealtime.setCompanyId(companyId);
deviceRealtime.setLastNotificationTime(data.getNotificationTime());
deviceRealtime.setLastNotificationTimestamp(data.getJapanTimestamp());
deviceRealtime.setLastRawHex(rawHex);
// 2. 只更新此次上报的数据
// 处理デジタル入力端子
if (data.getDigitalInputTerminals() != null && !data.getDigitalInputTerminals().isEmpty()) {
List<CsdjDigitalInputTerminal> list = data.getDigitalInputTerminals();
deviceRealtime.setDigitalInputJson(objectMapper.writeValueAsString(list));
for (int i = 0; i < list.size(); i++) {
CsdjDigitalInputTerminal t = list.get(i);
if (i == 0) {
deviceRealtime.setDi1No(t.getTerminalNo());
deviceRealtime.setDi1IsAlarm(t.getIsAlarm() ? 1 : 0);
deviceRealtime.setDi1StateRaw(t.getStateRaw());
deviceRealtime.setDi1StateText(t.getStateText());
deviceRealtime.setDi1AbnormalMode(t.getAbnormalMode());
} else if (i == 1) {
deviceRealtime.setDi2No(t.getTerminalNo());
deviceRealtime.setDi2IsAlarm(t.getIsAlarm() ? 1 : 0);
deviceRealtime.setDi2StateRaw(t.getStateRaw());
deviceRealtime.setDi2StateText(t.getStateText());
deviceRealtime.setDi2AbnormalMode(t.getAbnormalMode());
} else if (i == 2) {
deviceRealtime.setDi3No(t.getTerminalNo());
deviceRealtime.setDi3IsAlarm(t.getIsAlarm() ? 1 : 0);
deviceRealtime.setDi3StateRaw(t.getStateRaw());
deviceRealtime.setDi3StateText(t.getStateText());
deviceRealtime.setDi3AbnormalMode(t.getAbnormalMode());
}
}
}
// 处理アナログ入力端子
if (data.getAnalogInputTerminals() != null && !data.getAnalogInputTerminals().isEmpty()) {
List<CsdjAnalogInputTerminal> list = data.getAnalogInputTerminals();
deviceRealtime.setAnalogInputJson(objectMapper.writeValueAsString(list));
for (int i = 0; i < list.size(); i++) {
CsdjAnalogInputTerminal t = list.get(i);
if (i == 0) {
deviceRealtime.setAi1No(t.getTerminalNo());
deviceRealtime.setAi1IsThresholdAlarm(t.getIsThresholdAlarm() ? 1 : 0);
deviceRealtime.setAi1ThresholdNo(t.getThresholdNo());
deviceRealtime.setAi1AdditionalData(t.getAdditionalData());
deviceRealtime.setAi1StateRaw(t.getStateRaw());
} else if (i == 1) {
deviceRealtime.setAi2No(t.getTerminalNo());
deviceRealtime.setAi2IsThresholdAlarm(t.getIsThresholdAlarm() ? 1 : 0);
deviceRealtime.setAi2ThresholdNo(t.getThresholdNo());
deviceRealtime.setAi2AdditionalData(t.getAdditionalData());
deviceRealtime.setAi2StateRaw(t.getStateRaw());
}
}
}
// 处理デジタル出力端子
if (data.getDigitalOutputTerminals() != null && !data.getDigitalOutputTerminals().isEmpty()) {
List<CsdjDigitalOutputTerminal> list = data.getDigitalOutputTerminals();
deviceRealtime.setDigitalOutputJson(objectMapper.writeValueAsString(list));
for (int i = 0; i < list.size(); i++) {
CsdjDigitalOutputTerminal t = list.get(i);
if (i == 0) {
deviceRealtime.setDo1No(t.getTerminalNo());
deviceRealtime.setDo1OutputState(t.getOutputState());
deviceRealtime.setDo1StateRaw(t.getStateRaw());
deviceRealtime.setDo1AdditionalData(t.getAdditionalData());
} else if (i == 1) {
deviceRealtime.setDo2No(t.getTerminalNo());
deviceRealtime.setDo2OutputState(t.getOutputState());
deviceRealtime.setDo2StateRaw(t.getStateRaw());
deviceRealtime.setDo2AdditionalData(t.getAdditionalData());
}
}
}
// 处理システム状態
if (data.getSystemState() != null) {
CsdjSystemState s = data.getSystemState();
deviceRealtime.setSystemStateNo(s.getStateNo());
deviceRealtime.setSystemScheduledTimeNo(s.getScheduledTimeNo());
deviceRealtime.setSystemModeState(s.getModeState());
deviceRealtime.setSystemIsPowerOff(s.getIsPowerOff() ? 1 : 0);
deviceRealtime.setSystemIsBatteryLow(s.getIsBatteryLow() ? 1 : 0);
deviceRealtime.setSystemIsLineDisconnected(s.getIsLineDisconnected() ? 1 : 0);
deviceRealtime.setSystemIsSlaveCommunicationError(s.getIsSlaveCommunicationError() ? 1 : 0);
deviceRealtime.setSystemStateRaw(s.getStateRaw());
}
// 3. 保存
csdjDao.saveOrUpdateDeviceRealtime(deviceRealtime);
} catch (JsonProcessingException e) {
log.error("[CSDJ] 序列化 JSON 失败", e);
}
}
private String buildParsedJson(CsdjNotificationData data) {
try {
Map<String, Object> map = new HashMap<>();
map.put("notificationType", data.getNotificationType());
map.put("notificationTime", data.getNotificationTime());
map.put("japanTimestamp", data.getJapanTimestamp());
map.put("digitalInputTerminals", data.getDigitalInputTerminals());
map.put("analogInputTerminals", data.getAnalogInputTerminals());
map.put("digitalOutputTerminals", data.getDigitalOutputTerminals());
map.put("systemState", data.getSystemState());
return objectMapper.writeValueAsString(map);
} catch (Exception e) {
log.error("[CSDJ] 构建 parsedJson 失败", e);
return null;
}
}
private String buildParsedContent(CsdjNotificationData data) {
try {
StringBuilder sb = new StringBuilder();
sb.append("通知时间: ").append(data.getNotificationTime()).append("; ");
if (data.getDigitalInputTerminals() != null && !data.getDigitalInputTerminals().isEmpty()) {
sb.append("数字输入: ");
for (CsdjDigitalInputTerminal t : data.getDigitalInputTerminals()) {
sb.append("端子").append(t.getTerminalNo())
.append("(").append(t.getStateText()).append("); ");
}
}
if (data.getAnalogInputTerminals() != null && !data.getAnalogInputTerminals().isEmpty()) {
sb.append("模拟输入: ");
for (CsdjAnalogInputTerminal t : data.getAnalogInputTerminals()) {
sb.append("端子").append(t.getTerminalNo())
.append("=").append(t.getAdditionalData())
.append("(").append(t.getStateText()).append("); ");
}
}
if (data.getDigitalOutputTerminals() != null && !data.getDigitalOutputTerminals().isEmpty()) {
sb.append("数字输出: ");
for (CsdjDigitalOutputTerminal t : data.getDigitalOutputTerminals()) {
sb.append("端子").append(t.getTerminalNo())
.append("=").append(t.getOutputState()).append("; ");
}
}
if (data.getSystemState() != null) {
sb.append("系统状态: ");
sb.append("停电=").append(data.getSystemState().getIsPowerOff())
.append("; 电池=").append(data.getSystemState().getIsBatteryLow())
.append("; 线路=").append(data.getSystemState().getIsLineDisconnected());
}
return sb.toString();
} catch (Exception e) {
log.error("[CSDJ] 构建 parsedContent 失败", e);
return null;
}
} }
} }

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

@ -1,8 +1,7 @@
package com.techsor.datacenter.sender.utils.csdj; package com.techsor.datacenter.sender.utils.csdj;
import com.techsor.datacenter.sender.entitiy.csdj.CsdjDigitalInputTerminal; import com.techsor.datacenter.sender.entitiy.csdj.*;
import com.techsor.datacenter.sender.entitiy.csdj.CsdjNotificationData;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
@ -52,9 +51,6 @@ public class CsdjNotificationParser {
log.info("[CSDJ] 开始解析,原始数据长度:{}", data.length); log.info("[CSDJ] 开始解析,原始数据长度:{}", data.length);
log.info("[CSDJ] 原始数据 hex:{}", bytesToHex(data)); 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. 解析通報数据 // 2. 解析通報数据
return parseData(data); return parseData(data);
@ -80,39 +76,58 @@ public class CsdjNotificationParser {
// ========== 1. 解析开头的部分 ========== // ========== 1. 解析开头的部分 ==========
int offset = 0; int offset = 0;
// 打印整个数组的索引
log.info("[CSDJ] 原始数据索引详情:");
for (int i = 0; i < data.length; i++) {
log.info("[CSDJ] [{}] = 0x{}", i, Integer.toHexString(data[i] & 0xFF).toUpperCase());
}
// 数据长(2字节,大端序) // 数据长(2字节,大端序)
int dataLength = ((data[offset + 0] & 0xFF) << 8) | (data[offset + 1] & 0xFF); int dataLength = ((data[offset + 0] & 0xFF) << 8) | (data[offset + 1] & 0xFF);
builder.dataLength(dataLength); builder.dataLength(dataLength);
log.info("[CSDJ] [Offset:{}] 数据长:{} (0x{} 0x{})",
offset, dataLength,
Integer.toHexString(data[offset] & 0xFF).toUpperCase(),
Integer.toHexString(data[offset+1] & 0xFF).toUpperCase());
offset += 2; offset += 2;
log.info("[CSDJ] [Offset:{}] 数据长:{}", offset - 2, dataLength);
// 端子状态通知(1字节) // 端子状态通知(1字节)
int notificationType = data[offset] & 0xFF; int notificationType = data[offset] & 0xFF;
builder.notificationType(notificationType); builder.notificationType(notificationType);
log.info("[CSDJ] [Offset:{}] 通報类型:0x{}",
offset, Integer.toHexString(notificationType & 0xFF).toUpperCase());
offset += 1; offset += 1;
log.info("[CSDJ] [Offset:{}] 通報类型:0x{}", offset - 1, Integer.toHexString(notificationType & 0xFF).toUpperCase());
// 预留(1字节) // 预留(1字节)
log.info("[CSDJ] [Offset:{}] 预留(跳过):0x{}",
offset, Integer.toHexString(data[offset] & 0xFF).toUpperCase());
offset += 1; offset += 1;
log.info("[CSDJ] [Offset:{}] 预留 1字节", offset - 1);
// 时间数据(6字节:年两位、月、日、时、分、秒,都是BCD码) // 时间数据(6字节:年两位、月、日、时、分、秒,都是BCD码)
LocalDateTime time = parseTime(data, offset); LocalDateTime time = parseTime(data, offset);
builder.notificationTime(time); builder.notificationTime(time);
log.info("[CSDJ] [Offset:{}] 时间:{}", offset, time);
offset += 6; offset += 6;
log.info("[CSDJ] [Offset:{}] 时间:{}", offset - 6, time);
// 计算日本时区时间戳 // 计算日本时区时间戳
if (time != null) { if (time != null) {
long timestamp = convertToJapanTimestamp(time); long timestamp = convertToJapanTimestamp(time);
builder.japanTimestamp(timestamp); builder.japanTimestamp(timestamp);
log.info("[CSDJ] [Offset:{}] 日本时区时间戳:{}", offset - 6, timestamp);
} }
// 预留(1字节!!!)
log.info("[CSDJ] [Offset:{}] 预留(跳过1字节):0x{}",
offset,
Integer.toHexString(data[offset] & 0xFF).toUpperCase());
offset += 1;
log.info("[CSDJ] [Offset:{}] 开头部分解析完成,准备解析端子", offset); log.info("[CSDJ] [Offset:{}] 开头部分解析完成,准备解析端子", offset);
// ========== 2. 解析各个端子块 ========== // ========== 2. 解析各个端子块 ==========
List<CsdjDigitalInputTerminal> digitalInputTerminals = new ArrayList<>(); List<CsdjDigitalInputTerminal> digitalInputTerminals = new ArrayList<>();
List<CsdjAnalogInputTerminal> analogInputTerminals = new ArrayList<>();
List<CsdjDigitalOutputTerminal> digitalOutputTerminals = new ArrayList<>();
CsdjSystemState systemState = null;
while (offset < data.length - 1) { while (offset < data.length - 1) {
byte terminalType = data[offset]; byte terminalType = data[offset];
@ -129,11 +144,11 @@ public class CsdjNotificationParser {
int terminalCount = data[offset] & 0xFF; int terminalCount = data[offset] & 0xFF;
offset += 1; offset += 1;
log.info("[CSDJ] [Offset:{}] 端子数:{}", offset - 1, terminalCount); log.info("[CSDJ] [Offset:{}] デジタル入力端子数:{}", offset - 1, terminalCount);
for (int i = 0; i < terminalCount; i++) { for (int i = 0; i < terminalCount; i++) {
if (offset + 6 > data.length) { if (offset + 6 > data.length) {
log.warn("[CSDJ] 数据不足,无法读取第{}个端子,offset:{}, length:{}", i + 1, offset, data.length); log.warn("[CSDJ] 数据不足,无法读取第{}个デジタル入力端子", i + 1);
break; break;
} }
@ -141,24 +156,83 @@ public class CsdjNotificationParser {
digitalInputTerminals.add(terminal); digitalInputTerminals.add(terminal);
offset += 6; // 每个端子6字节 offset += 6; // 每个端子6字节
} }
} else if (terminalType == TERMINAL_TYPE_AI || terminalType == TERMINAL_TYPE_DO || terminalType == TERMINAL_TYPE_SYSTEM) { } else if (terminalType == TERMINAL_TYPE_AI) {
// ========== 其他类型暂时跳过 ========== // ========== アナログ入力端子 ==========
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] 数据不足,无法读取第{}个アナログ入力端子", i + 1);
break;
}
CsdjAnalogInputTerminal terminal = parseAnalogInput(data, offset);
analogInputTerminals.add(terminal);
offset += 6; // 每个端子6字节
}
} else if (terminalType == TERMINAL_TYPE_DO) {
// ========== デジタル出力端子 ==========
if (offset >= data.length) { if (offset >= data.length) {
log.warn("[CSDJ] 数据不足,无法读取端子数");
break; break;
} }
int terminalCount = data[offset] & 0xFF; int terminalCount = data[offset] & 0xFF;
offset += 1; offset += 1;
log.info("[CSDJ] [Offset:{}] 跳过端子类型:0x{}, 端子数:{}", offset - 1, Integer.toHexString(terminalType & 0xFF).toUpperCase(), terminalCount); log.info("[CSDJ] [Offset:{}] デジタル出力端子数:{}", offset - 1, terminalCount);
offset += 6 * terminalCount;
for (int i = 0; i < terminalCount; i++) {
if (offset + 6 > data.length) {
log.warn("[CSDJ] 数据不足,无法读取第{}个デジタル出力端子", i + 1);
break;
}
CsdjDigitalOutputTerminal terminal = parseDigitalOutput(data, offset);
digitalOutputTerminals.add(terminal);
offset += 6; // 每个端子6字节
}
} else if (terminalType == TERMINAL_TYPE_SYSTEM) {
// ========== システム状態 ==========
if (offset >= data.length) {
log.warn("[CSDJ] 数据不足,无法读取システム状態");
break;
}
int terminalCount = data[offset] & 0xFF; // 应该是1
offset += 1;
log.info("[CSDJ] [Offset:{}] システム状態数:{}", offset - 1, terminalCount);
if (terminalCount > 0 && offset + 6 <= data.length) {
systemState = parseSystemState(data, offset);
offset += 6; // 6字节
}
} else { } else {
// 未知类型,继续往下走 // 未知类型,跳过
log.warn("[CSDJ] [Offset:{}] 未知的端子类型:0x{}", offset - 1, Integer.toHexString(terminalType & 0xFF).toUpperCase()); log.warn("[CSDJ] [Offset:{}] 未知的端子类型:0x{}", offset - 1, Integer.toHexString(terminalType & 0xFF).toUpperCase());
if (offset < data.length) {
int terminalCount = data[offset] & 0xFF;
offset += 1;
offset += 6 * terminalCount;
}
} }
} }
builder.digitalInputTerminals(digitalInputTerminals); builder.digitalInputTerminals(digitalInputTerminals);
log.info("[CSDJ] 解析完成,找到デジタル入力端子数:{}", digitalInputTerminals.size()); builder.analogInputTerminals(analogInputTerminals);
builder.digitalOutputTerminals(digitalOutputTerminals);
builder.systemState(systemState);
log.info("[CSDJ] 解析完成:DI={}, AI={}, DO={}, System={}",
digitalInputTerminals.size(), analogInputTerminals.size(),
digitalOutputTerminals.size(), systemState != null ? 1 : 0);
return builder.build(); return builder.build();
} }
@ -211,8 +285,144 @@ public class CsdjNotificationParser {
} }
builder.stateText(stateText); builder.stateText(stateText);
log.info("[CSDJ] [Offset:{}] デジタル入力端子解析:No:{}, state:0x{}, isAlarm:{}", log.info("[CSDJ] [Offset:{}] DI端子:No:{}, isAlarm:{}, state:{}",
offset - 6, terminalNo, Integer.toHexString(stateRaw).toUpperCase(), isAlarm); offset - 6, terminalNo, isAlarm, stateText);
return builder.build();
}
/**
* 解析アナログ入力端子
*/
private CsdjAnalogInputTerminal parseAnalogInput(byte[] data, int offset) {
CsdjAnalogInputTerminal.CsdjAnalogInputTerminalBuilder builder = CsdjAnalogInputTerminal.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);
// ========== 解析端子状态 ==========
// b6-b5位:阈值类型
int thresholdType = (stateByte >> 5) & 0x03;
boolean isThresholdAlarm = thresholdType > 0;
builder.isThresholdAlarm(isThresholdAlarm);
// b2-b0位:阈值编号
Integer thresholdNo = isThresholdAlarm ? (stateByte & 0x07) : null;
builder.thresholdNo(thresholdNo);
// 状态文本
String stateText = isThresholdAlarm ?
String.format("閾値異常(No:%d)", thresholdNo) : "閾値正常";
builder.stateText(stateText);
log.info("[CSDJ] [Offset:{}] AI端子:No:{}, isAlarm:{}, value:{}",
offset - 6, terminalNo, isThresholdAlarm, additionalData);
return builder.build();
}
/**
* 解析デジタル出力端子
*/
private CsdjDigitalOutputTerminal parseDigitalOutput(byte[] data, int offset) {
CsdjDigitalOutputTerminal.CsdjDigitalOutputTerminalBuilder builder = CsdjDigitalOutputTerminal.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);
// ========== 解析端子状态 ==========
// b1-b0位:输出状态
int outputStatus = stateByte & 0x03;
String outputState = (outputStatus == 1) ? "ON" : "OFF";
builder.outputState(outputState);
log.info("[CSDJ] [Offset:{}] DO端子:No:{}, output:{}",
offset - 6, terminalNo, outputState);
return builder.build();
}
/**
* 解析システム状態
*/
private CsdjSystemState parseSystemState(byte[] data, int offset) {
CsdjSystemState.CsdjSystemStateBuilder builder = CsdjSystemState.builder();
// 第1字节:系统状态
byte stateByte = data[offset];
int stateRaw = stateByte & 0xFF;
builder.stateRaw(stateRaw);
offset += 1;
// 第2字节:状态番号
int stateNo = data[offset] & 0xFF;
builder.stateNo(stateNo);
offset += 1;
// 第3-6字节:附加内容
byte byte3 = data[offset + 0];
byte byte4 = data[offset + 1];
byte byte5 = data[offset + 2];
byte byte6 = data[offset + 3];
// 定时编号(byte3的b4-b0)
int scheduledTimeNo = byte3 & 0x1F;
builder.scheduledTimeNo(scheduledTimeNo);
// 模式状态(byte4的b1-b0)
int modeState = byte4 & 0x03;
builder.modeState(modeState);
// 停电状态(byte5的b0)
boolean isPowerOff = (byte5 & 0x01) != 0;
builder.isPowerOff(isPowerOff);
// 电池状态(byte5的b1)
boolean isBatteryLow = (byte5 & 0x02) != 0;
builder.isBatteryLow(isBatteryLow);
// 线路断开状态(byte5的b2)
boolean isLineDisconnected = (byte5 & 0x04) != 0;
builder.isLineDisconnected(isLineDisconnected);
// 子机通信状态(byte6的b0)
boolean isSlaveCommunicationError = (byte6 & 0x01) != 0;
builder.isSlaveCommunicationError(isSlaveCommunicationError);
log.info("[CSDJ] [Offset:{}] System状态:stateNo:{}, isPowerOff:{}",
offset - 6, stateNo, isPowerOff);
return builder.build(); return builder.build();
} }
@ -225,7 +435,7 @@ public class CsdjNotificationParser {
// BCD码转十进制 // BCD码转十进制
int yearBcd = data[offset + 0] & 0xFF; int yearBcd = data[offset + 0] & 0xFF;
int year = (yearBcd >> 4) * 10 + (yearBcd & 0x0F); int year = (yearBcd >> 4) * 10 + (yearBcd & 0x0F);
year += 2000; // 加上2000年 year += 2000;
int monthBcd = data[offset + 1] & 0xFF; int monthBcd = data[offset + 1] & 0xFF;
int month = (monthBcd >> 4) * 10 + (monthBcd & 0x0F); int month = (monthBcd >> 4) * 10 + (monthBcd & 0x0F);
@ -261,9 +471,7 @@ public class CsdjNotificationParser {
* 十六进制字符串转字节数组 * 十六进制字符串转字节数组
*/ */
public static byte[] hexStringToBytes(String hexStr) { public static byte[] hexStringToBytes(String hexStr) {
// 去掉空格和换行
String cleanHex = hexStr.replaceAll("\\s+", "").toUpperCase(); String cleanHex = hexStr.replaceAll("\\s+", "").toUpperCase();
if (cleanHex.length() % 2 != 0) { if (cleanHex.length() % 2 != 0) {
throw new IllegalArgumentException("十六进制字符串长度必须为偶数"); throw new IllegalArgumentException("十六进制字符串长度必须为偶数");
} }

76
src/test/java/com/techsor/datacenter/sender/csdj/data.txt

@ -0,0 +1,76 @@
一、告警/恢复
告警
{
"terminalId": "TEST002",
"data": "00 0E 07 00 25 06 20 0E 00 00 00 C0 01 08 01 00 00 00 00",
"ts": "1782438627585"
}
恢复
{
"terminalId": "TEST002",
"data": "00 0E 07 00 25 06 20 0E 00 00 00 C0 01 00 01 00 00 00 00",
"ts": "1782272698000"
}
二、アナログ入力端子(0xC2)模拟数据
{
"terminalId": "TEST002",
"data": "00 0E 07 00 25 06 20 0E 00 00 00 C2 01 00 01 00 00 00 01",
"ts": "1782272698000"
}
说明:
- 00 0E = 数据长度14
- 07 = 通知类型
- 00 = 预留
- 25 06 20 0E 00 = 时间
- 00 = 预留
- C2 = アナログ入力
- 01 = 端子数量1
- 00 01 00 00 00 01 = 端子数据(端子1,值为1,正常状态)
三、デジタル出力端子(0xC3)模拟数据
{
"terminalId": "TEST002",
"data": "00 0E 07 00 25 06 20 0E 00 00 00 C3 01 00 01 00 00 00 00",
"ts": "1782272698000"
}
说明:
- 00 0E = 数据长度14
- 07 = 通知类型
- 00 = 预留
- 25 06 20 0E 00 = 时间
- 00 = 预留
- C3 = デジタル出力
- 01 = 端子数量1
- 00 01 00 00 00 00 = 端子数据(端子1,状态OFF)
四、システム状態(0xC6)模拟数据
{
"terminalId": "TEST002",
"data": "00 0E 07 00 25 06 20 0E 00 00 00 C6 01 00 01 00 00 00 00",
"ts": "1782272698000"
}
说明:
- 00 0E = 数据长度14
- 07 = 通知类型
- 00 = 预留
- 25 06 20 0E 00 = 时间
- 00 = 预留
- C6 = システム状態
- 01 = 端子数量1
- 00 01 00 00 00 00 = 系统状态数据(正常)
五、更完整的组合数据(包含所有4种类型)
{
"terminalId": "TEST002",
"data": "00 26 07 00 25 06 20 0E 00 00 00 C0 01 08 01 00 00 00 00 C2 01 00 02 00 00 00 05 C3 01 00 03 00 00 00 00 C6 01 00 04 00 00 00 00",
"ts": "1782272698000"
}
说明:
- 包含デジタル入力、アナログ入力、デジタル出力、システム状態,每种类型各1个端子
Loading…
Cancel
Save