From c053a725cb692a7d0d06f3729ba5d9c1cb1008a8 Mon Sep 17 00:00:00 2001 From: "review512jwy@163.com" <“review512jwy@163.com”> Date: Mon, 1 Jun 2026 12:59:03 +0800 Subject: [PATCH] =?UTF-8?q?F10=20200=E5=AD=97=E8=8A=82=E6=95=B0=E6=8D=AE?= =?UTF-8?q?=E5=85=BC=E5=AE=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../sender/components/CommonOpt.java | 61 +++++- .../datacenter/sender/dao/CompanyInfoDao.java | 4 +- .../datacenter/sender/dao/DeviceDao.java | 31 ++- .../techsor/datacenter/sender/dao/F10Dao.java | 58 +++++ .../entitiy/company/TopCompanyInfo.java | 16 ++ .../sender/entitiy/f10/F10ProcessingData.java | 91 ++++++++ .../datacenter/sender/service/F10Service.java | 156 +++++++++++++- .../sender/f10/F10FullParser320.java | 110 ++++++++++ .../datacenter/sender/f10/SochiData.java | 68 ++++++ .../sender/f10/SochiDataParser.java | 202 ++++++++++++++++++ .../datacenter/sender/f10/data200-2.txt | 1 + .../techsor/datacenter/sender/f10/data200.txt | 1 + .../techsor/datacenter/sender/f10/data320.txt | 1 + 13 files changed, 792 insertions(+), 8 deletions(-) create mode 100644 src/main/java/com/techsor/datacenter/sender/entitiy/company/TopCompanyInfo.java create mode 100644 src/main/java/com/techsor/datacenter/sender/entitiy/f10/F10ProcessingData.java create mode 100644 src/test/java/com/techsor/datacenter/sender/f10/F10FullParser320.java create mode 100644 src/test/java/com/techsor/datacenter/sender/f10/SochiData.java create mode 100644 src/test/java/com/techsor/datacenter/sender/f10/SochiDataParser.java create mode 100644 src/test/java/com/techsor/datacenter/sender/f10/data200-2.txt create mode 100644 src/test/java/com/techsor/datacenter/sender/f10/data200.txt create mode 100644 src/test/java/com/techsor/datacenter/sender/f10/data320.txt diff --git a/src/main/java/com/techsor/datacenter/sender/components/CommonOpt.java b/src/main/java/com/techsor/datacenter/sender/components/CommonOpt.java index a7404bc..a7c0a45 100644 --- a/src/main/java/com/techsor/datacenter/sender/components/CommonOpt.java +++ b/src/main/java/com/techsor/datacenter/sender/components/CommonOpt.java @@ -8,6 +8,7 @@ import com.techsor.datacenter.sender.dao.DeviceDao; import com.techsor.datacenter.sender.dto.RedisAlarmDTO; import com.techsor.datacenter.sender.entitiy.DeviceEntity; import com.techsor.datacenter.sender.entitiy.company.CompanyEntity; +import com.techsor.datacenter.sender.entitiy.company.TopCompanyInfo; import com.techsor.datacenter.sender.utils.HttpUtil; import org.slf4j.Logger; @@ -65,7 +66,7 @@ public class CommonOpt { try{ companyId = companyInfoDao.getTopCompanyId(companyId)+""; }catch (Exception e){ - log.error("Get top companyId Error:",e.getMessage(),e.fillInStackTrace()); + log.error("Get top companyId Error: {}",e.getMessage(),e.fillInStackTrace()); } log.info("GetTopCompanyId:"+companyId); return companyId; @@ -101,6 +102,64 @@ public class CommonOpt { return companyId; } + + + public TopCompanyInfo getCompanyIdByDevicePrefix(String prefix) { + String redisKey = "f10:deviceCompanyMapping"; + String companyId = (String) redisTemplate.opsForHash().get(redisKey, prefix); + if (companyId==null){ + //Try from mysql + companyId = getCompanyIdByDevicePrefixFromAllDB(prefix); + if (companyId.equals("")){ + log.error("Can't find topCompanyId for deviceId prefix: "+prefix+", Please notice if this device is not login on platform."); + return null; + }else{ + //Update redis cache + redisTemplate.opsForHash().put(redisKey, prefix, companyId); + } + } + String topCompanyId = ""; + try{ + topCompanyId = companyInfoDao.getTopCompanyId(companyId)+""; + }catch (Exception e){ + log.error("Get top companyId Error: {}",e.getMessage(),e.fillInStackTrace()); + } + log.info("GetTopCompanyId:"+topCompanyId); + + TopCompanyInfo topCompanyInfo = new TopCompanyInfo(); + topCompanyInfo.setSelfCompanyId(companyId); + topCompanyInfo.setTopCompanyId(topCompanyId); + return topCompanyInfo; + } + + private String getCompanyIdByDevicePrefixFromAllDB(String prefix) { + String companyId = ""; + List companyEntityList = companyInfoDao.getCompanyList(); + for (CompanyEntity companyItem : companyEntityList){ + String tempCompanyId = ""; + log.info("[getCompanyIdByDevicePrefixFromAllDB] companyId:"+companyItem.getId().toString()); + if (companyItem.getParentId()==-1 || companyItem.getParentId()==1){ + tempCompanyId = companyItem.getId().toString(); + }else{ + log.info("[getCompanyIdByDevicePrefixFromAllDB] Not level 1 company. skip."); + continue; + } + //只处理一级企业 + log.info("[getCompanyIdByDevicePrefixFromAllDB] prefix:"+prefix+" , comanyId:"+tempCompanyId); + List deviceList = deviceDao.getDeviceInfoByDevicePrefixWithDB(prefix,tempCompanyId); + if (deviceList.size()<=0){ + log.info("[getCompanyIdByDevicePrefixFromAllDB] deviceList size is 0"); + continue; + }else{ + log.info("[getCompanyIdByDevicePrefixFromAllDB] companyId get! Value:"+tempCompanyId); + companyId=tempCompanyId; + break; + } + } + log.info("GetCompanyId:"+companyId); + return companyId; + } + public void handleTargetUrl(JSONObject respObject, Map keys) { if (keys.containsKey("needTransfer") && "true".equalsIgnoreCase(keys.get("needTransfer").toString())) { String targetData = JSONObject.toJSONString(keys.get("rawData")); diff --git a/src/main/java/com/techsor/datacenter/sender/dao/CompanyInfoDao.java b/src/main/java/com/techsor/datacenter/sender/dao/CompanyInfoDao.java index f8781ec..e840593 100644 --- a/src/main/java/com/techsor/datacenter/sender/dao/CompanyInfoDao.java +++ b/src/main/java/com/techsor/datacenter/sender/dao/CompanyInfoDao.java @@ -21,7 +21,7 @@ public class CompanyInfoDao { @Cacheable(value = "CompanyInfoDao::getCompanyList",key = "'CompanyList'") public List getCompanyList() { // String sql="SELECT id,parent_id,company_name,bearer_token,third_api_host FROM data_center_admin.basic_company;"; - String sql="SELECT id,parent_id,company_name,bearer_token,third_api_host FROM data_center_aeon_admin.basic_company;"; + String sql="SELECT id,parent_id,company_name,bearer_token,third_api_host FROM data_center_aeon_admin.basic_company where flag != 1 ;"; List dataList = jdbcTemplate.query(sql, (rs, rowNum) -> { CompanyEntity item = new CompanyEntity(); @@ -57,7 +57,7 @@ public class CompanyInfoDao { } } - @Cacheable(value = "CompanyInfoDao::getCompanyList",key = "#companyId") + @Cacheable(value = "CompanyInfoDao::getTopCompanyId",key = "#companyId") public long getTopCompanyId(String companyId) { String sql="SELECT " diff --git a/src/main/java/com/techsor/datacenter/sender/dao/DeviceDao.java b/src/main/java/com/techsor/datacenter/sender/dao/DeviceDao.java index 383f958..96330b5 100644 --- a/src/main/java/com/techsor/datacenter/sender/dao/DeviceDao.java +++ b/src/main/java/com/techsor/datacenter/sender/dao/DeviceDao.java @@ -206,7 +206,36 @@ public class DeviceDao { return dataList; } - // Get the corresponding WS client ID based on the device ID + @Cacheable(value = "DeviceDao::getDeviceInfoByDevicePrefixWithDB", key = "#prefix + '::' + #companyId") + public List getDeviceInfoByDevicePrefixWithDB(String prefix,String companyId){ + if (StringUtils.isEmpty(prefix)){ + return new ArrayList<>(); + } + String sql = ""; + sql = "SELECT id,company_id,device_id,device_sn,type_id,space_id,building_id,device_name," + + "project_id,floor_id,asset_id " + + "FROM data_center_aeon_" + companyId + ".device_info " + + "WHERE device_id LIKE '" + prefix.replace("_", "\\_") + "%' " + + "AND flag != 1"; + List dataList = jdbcTemplate.query(sql, + (rs, rowNum) -> { + DeviceEntity item = new DeviceEntity(); + item.setId(rs.getInt("id")); + item.setCompanyId(rs.getLong("company_id")); + item.setDeviceId(rs.getString("device_id")); + item.setDeviceSN(rs.getString("device_sn")); + item.setTypeId(rs.getInt("type_id")); + item.setSpaceId(rs.getLong("space_id")); + item.setBuildingId(rs.getLong("building_id")); + item.setDeviceName(rs.getString("device_name")); + item.setProjectId(rs.getLong("project_id")); + item.setFloorId(rs.getLong("floor_id")); + item.setAssetId(rs.getLong("asset_id")); + return item; + }); + return dataList; + } + @Cacheable(value = "DeviceDao::getDeviceInfoByDeviceIdWithDB", key = "#deviceId + '::' + #companyId") public List getDeviceInfoByDeviceIdWithDB(String deviceId,String companyId){ if (StringUtils.isEmpty(deviceId)){ diff --git a/src/main/java/com/techsor/datacenter/sender/dao/F10Dao.java b/src/main/java/com/techsor/datacenter/sender/dao/F10Dao.java index b005dd8..800ec90 100644 --- a/src/main/java/com/techsor/datacenter/sender/dao/F10Dao.java +++ b/src/main/java/com/techsor/datacenter/sender/dao/F10Dao.java @@ -1,6 +1,7 @@ package com.techsor.datacenter.sender.dao; import com.techsor.datacenter.sender.entitiy.f10.F10JournalLog; +import com.techsor.datacenter.sender.entitiy.f10.F10ProcessingData; import jakarta.annotation.Resource; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.stereotype.Component; @@ -79,4 +80,61 @@ public class F10Dao { f10JournalLog.getAlertHistoryId() ); } + + public void insertF10ProcessingData(F10ProcessingData f10ProcessingData) { + + String sql = "INSERT IGNORE INTO f10_processing_log (" + + "gateway_id, company_id, raw_hex, raw_text, serial_no, mode, " + + "signal_seq, receive_date, receive_time, " + + "tel_time, tel_operator, command_time, cancel_time, controller_name, " + + "arrival_time, field_action_name, action_content, finish_time, writer, " + + "category_no, category_name, physical_address, contract_no, " + + "transfer_flag, incident_flag, reserved_field20, " + + "created_at, created_time, receive_timestamp" + + ") VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)"; + + jdbcTemplate.update(sql, + + // ===== 基础字段 ===== + f10ProcessingData.getGatewayId(), + f10ProcessingData.getCompanyId(), + + // 你的实体里目前没有这几个字段 + f10ProcessingData.getRawHex(), // raw_hex + f10ProcessingData.getRawText(), // raw_text + f10ProcessingData.getSignalSeq(), // serial_no + f10ProcessingData.getMode(), // mode + + // ===== F10 措置数据 ===== + f10ProcessingData.getSignalSeq(), + f10ProcessingData.getReceiveDate(), + f10ProcessingData.getReceiveTime(), + + f10ProcessingData.getTelTime(), + f10ProcessingData.getTelOperator(), + f10ProcessingData.getCommandTime(), + f10ProcessingData.getCancelTime(), + f10ProcessingData.getControllerName(), + + f10ProcessingData.getArrivalTime(), + f10ProcessingData.getFieldActionName(), + f10ProcessingData.getActionContent(), + f10ProcessingData.getFinishTime(), + f10ProcessingData.getWriter(), + + f10ProcessingData.getCategoryNo(), + f10ProcessingData.getCategoryName(), + f10ProcessingData.getPhysicalAddress(), + f10ProcessingData.getContractNo(), + + f10ProcessingData.getTransferFlag(), + f10ProcessingData.getIncidentFlag(), + f10ProcessingData.getReservedField20(), + + // ===== 时间 ===== + f10ProcessingData.getCreatedAt(), + new java.sql.Timestamp(f10ProcessingData.getCreatedAt()), + f10ProcessingData.getReceiveTimestamp() + ); + } } diff --git a/src/main/java/com/techsor/datacenter/sender/entitiy/company/TopCompanyInfo.java b/src/main/java/com/techsor/datacenter/sender/entitiy/company/TopCompanyInfo.java new file mode 100644 index 0000000..3882bfc --- /dev/null +++ b/src/main/java/com/techsor/datacenter/sender/entitiy/company/TopCompanyInfo.java @@ -0,0 +1,16 @@ +package com.techsor.datacenter.sender.entitiy.company; + + +import lombok.Data; + +import java.io.Serializable; + + +@Data +public class TopCompanyInfo implements Serializable { + + private String topCompanyId; + + private String selfCompanyId; + +} diff --git a/src/main/java/com/techsor/datacenter/sender/entitiy/f10/F10ProcessingData.java b/src/main/java/com/techsor/datacenter/sender/entitiy/f10/F10ProcessingData.java new file mode 100644 index 0000000..0534296 --- /dev/null +++ b/src/main/java/com/techsor/datacenter/sender/entitiy/f10/F10ProcessingData.java @@ -0,0 +1,91 @@ +package com.techsor.datacenter.sender.entitiy.f10; + +import lombok.Data; + +import java.time.LocalDateTime; + +@Data +public class F10ProcessingData { + + /** 1 信号通番 */ + private String signalSeq; + + /** 2 受信日付 yyyyMMdd */ + private String receiveDate; + + /** 3 受信時刻 HHmmss */ + private String receiveTime; + + /** 4 電話連絡時刻 */ + private String telTime; + + /** 5 電話対応者名 */ + private String telOperator; + + /** 6 指令時刻 */ + private String commandTime; + + /** 7 指令中止時刻 */ + private String cancelTime; + + /** 8 管制官名 */ + private String controllerName; + + /** 9 現着時刻 */ + private String arrivalTime; + + /** 10 現場対処名 */ + private String fieldActionName; + + /** 11 措置内容 */ + private String actionContent; + + /** 12 完了時刻 */ + private String finishTime; + + /** 13 記入者 */ + private String writer; + + /** 14 措置分類番号 */ + private String categoryNo; + + /** 15 措置分類名称 */ + private String categoryName; + + /** 16 物理アドレス */ + private String physicalAddress; + + /** 17 契約先番号 */ + private String contractNo; + + /** 18 転送フラグ */ + private String transferFlag; + + /** 19 発生事故記録書の有無 */ + private String incidentFlag; + + /** 20 予備領域(0x20 padding / optional field) */ + private String reservedField20; + + + // ===== 主键 ===== + private Long id; + // ===== 设备 ===== + private String gatewayId; + private Long companyId; + + // ===== 原始数据 ===== + private String rawHex; + private String rawText; + + // ===== 报文头 ===== + private String serialNo; + private String mode; + + private Long createdAt; // 毫秒时间戳 + private LocalDateTime createdTime; // 标准时间 + // 接收时间戳(JST) + private Long receiveTimestamp; + + +} diff --git a/src/main/java/com/techsor/datacenter/sender/service/F10Service.java b/src/main/java/com/techsor/datacenter/sender/service/F10Service.java index 687fe23..7d3498a 100644 --- a/src/main/java/com/techsor/datacenter/sender/service/F10Service.java +++ b/src/main/java/com/techsor/datacenter/sender/service/F10Service.java @@ -9,7 +9,9 @@ import com.techsor.datacenter.sender.dao.DeviceDao; import com.techsor.datacenter.sender.dao.F10Dao; import com.techsor.datacenter.sender.entitiy.DeviceEntity; import com.techsor.datacenter.sender.entitiy.DynamodbEntity; +import com.techsor.datacenter.sender.entitiy.company.TopCompanyInfo; import com.techsor.datacenter.sender.entitiy.f10.F10JournalLog; +import com.techsor.datacenter.sender.entitiy.f10.F10ProcessingData; import com.techsor.datacenter.sender.entitiy.f10.RedisStreamEntity; import com.techsor.datacenter.sender.utils.RedisUtils; import jakarta.annotation.Resource; @@ -52,12 +54,86 @@ public class F10Service { public void handle(RedisStreamEntity rawEntity) { String hexLog = rawEntity.getHexRawData().trim(); byte[] src = hexStringToByteArray(hexLog); - //(STX:1 + Serial:4 + Mode:2 + 正文:320 + ETX:1) - if (src.length < 328) { - log.warn("原始报文长度不足328字节"); - return; + //(STX:1 + Serial:4 + Mode:2 + 正文:320或者200 + ETX:1) + if (328 == src.length) { + handle308(rawEntity); + } else if (208 == src.length) { + handle208(rawEntity); + } else { + log.warn("原始报文长度不是328或者208字节"); } + } + private void handle208(RedisStreamEntity rawEntity) { + try { + long currentTs = System.currentTimeMillis(); + + F10ProcessingData f10ProcessingData = parse208(rawEntity.getHexRawData()); + f10ProcessingData.setCreatedAt(currentTs); + + //前缀_26契約先番号_4チャンネル番号 + //前缀还没定,先【26契約先番号_4チャンネル番号】 + f10ProcessingData.setGatewayId(f10ProcessingData.getContractNo()); + + long receivedCurrentTs = parseToTimestamp(f10ProcessingData.getReceiveDate(), f10ProcessingData.getReceiveTime()); + f10ProcessingData.setReceiveTimestamp(receivedCurrentTs); + + String redisKey = getLatestProcessingKey(f10ProcessingData.getGatewayId()); + // 从 Redis 取值 + String latestTsStr = redisUtils.get(redisKey); + Long latestTs = null; + if (latestTsStr != null) { + try { + latestTs = Long.parseLong(latestTsStr); + } catch (Exception e) { + log.error("Redis数据异常,无法转换为Long: key={}, value={}", redisKey, latestTsStr); + } + } + + boolean isExpired = false; + // ===== 判断逻辑 ===== + if (latestTs == null) { + // 首次 + redisUtils.add(redisKey, String.valueOf(receivedCurrentTs)); + } else { + if (receivedCurrentTs > latestTs) { + // 新数据 + redisUtils.add(redisKey, String.valueOf(receivedCurrentTs)); + } else if (receivedCurrentTs == latestTs) { + // 重复数据 → 直接丢弃 + log.warn("重复处理,丢弃 deviceId={}", f10ProcessingData.getGatewayId()); + return; + } else { + // 旧数据 → 标记过期 + isExpired = true; + } + } + + TopCompanyInfo topCompanyInfo= commonOpt.getCompanyIdByDevicePrefix(f10ProcessingData.getGatewayId() + "_"); + if (topCompanyInfo==null || "0".equals(topCompanyInfo.getTopCompanyId())){ + log.error("no companyId is found========================>>>> {}",f10ProcessingData.getGatewayId()); + return; + } + DataSourceContextHolder.clearCurrentDataSourceKey(); + DataSourceContextHolder.setCurrentDataSourceKey("dataSourceForCompany_"+topCompanyInfo.getTopCompanyId()); + + log.info("Use datasource for company:"+topCompanyInfo.getTopCompanyId()); + + Long companyId = Long.valueOf(topCompanyInfo.getSelfCompanyId()); + f10ProcessingData.setCompanyId(companyId); + + f10Dao.insertF10ProcessingData(f10ProcessingData); + + } catch (Exception e) { + log.error("F10解析入库失败", e); + } + } + + private String getLatestProcessingKey(String deviceId) { + return "f10:latest_processing:" + deviceId; + } + + private void handle308(RedisStreamEntity rawEntity) { try { long currentTs = System.currentTimeMillis(); @@ -142,6 +218,78 @@ public class F10Service { return "f10:latest_alarm:" + deviceId; } + public static F10ProcessingData parse208(String hexLog) { + byte[] src = hexStringToByteArray(hexLog); + + F10ProcessingData d = new F10ProcessingData(); + + int offset = 0; + // 跳过 STX + offset += 1; + + String serialNo = new String(Arrays.copyOfRange(src, offset, offset + 4), SHIFT_JIS); + String mode = new String(Arrays.copyOfRange(src, offset, offset + 2), SHIFT_JIS); + + int bodyStart = 7; + int bodyEnd = src.length - 1; + byte[] body = Arrays.copyOfRange(src, bodyStart, bodyEnd); + String rawText = new String(body, SHIFT_JIS).trim(); + + d .setRawHex(hexLog); + d .setRawText(rawText); + d .setSerialNo(serialNo); + d .setMode(mode); + + offset = 7; + + d.setSignalSeq(getTargetData(src, offset, 9)); offset += 9; + d.setReceiveDate(getTargetData(src, offset, 8)); offset += 8; + d.setReceiveTime(getTargetData(src, offset, 6)); offset += 6; + d.setTelTime(getTargetData(src, offset, 6)); offset += 6; + d.setTelOperator(getTargetData(src, offset, 16)); offset += 16; + d.setCommandTime(getTargetData(src, offset, 6)); offset += 6; + d.setCancelTime(getTargetData(src, offset, 6)); offset += 6; + d.setControllerName(getTargetData(src, offset, 16)); offset += 16; + d.setArrivalTime(getTargetData(src, offset, 6)); offset += 6; + d.setFieldActionName(getTargetData(src, offset, 16)); offset += 16; + d.setActionContent(getTargetData(src, offset, 40)); offset += 40; + d.setFinishTime(getTargetData(src, offset, 6)); offset += 6; + d.setWriter(getTargetData(src, offset, 16)); offset += 16; + d.setCategoryNo(getTargetData(src, offset, 2)); offset += 2; + d.setCategoryName(getTargetData(src, offset, 2)); offset += 2; + d.setPhysicalAddress(getTargetData(src, offset, 8)); offset += 8; + d.setContractNo(getTargetData(src, offset, 8)); offset += 8; + d.setTransferFlag(getTargetData(src, offset, 1)); offset += 1; + d.setIncidentFlag(getTargetData(src, offset, 1)); offset += 1; + + d.setReservedField20(parseReserved(src, offset)); + + return d; + } + + /** + * 予備字段(0x20) + */ + private static String parseReserved(byte[] data, int start) { + + byte[] bytes = new byte[21]; + System.arraycopy(data, start, bytes, 0, 21); + + boolean allSpace = true; + for (byte b : bytes) { + if (b != 0x20) { + allSpace = false; + break; + } + } + + if (allSpace) { + return null; + } + + return new String(bytes, SHIFT_JIS).trim(); + } + public static F10JournalLog parseToEntity(String hexLog) { byte[] src = hexStringToByteArray(hexLog); diff --git a/src/test/java/com/techsor/datacenter/sender/f10/F10FullParser320.java b/src/test/java/com/techsor/datacenter/sender/f10/F10FullParser320.java new file mode 100644 index 0000000..8ddddf9 --- /dev/null +++ b/src/test/java/com/techsor/datacenter/sender/f10/F10FullParser320.java @@ -0,0 +1,110 @@ +package com.techsor.datacenter.sender.f10; + +import java.io.IOException; +import java.nio.charset.Charset; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.util.Arrays; + +public class F10FullParser320 { + private static final Charset SHIFT_JIS = Charset.forName("Shift-JIS"); + + public static void main(String[] args) { + String rawHexLog = ""; + try { + rawHexLog = Files.readString(Paths.get("src/test/java/com/techsor/datacenter/sender/f10/data320.txt")).trim(); + } catch (IOException e) { + e.printStackTrace(); + } + // 自动运行1 的原始 HEX 字符串 +// String rawHexLog = "02 30 30 30 31 31 30 30 31 30 30 37 30 30 31 38 32 30 32 36 30 33 31 37 31 35 31 39 31 35 30 30 34 30 30 31 83 75 83 8D 83 62 83 4E 82 50 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 30 33 30 31 30 31 31 30 35 30 20 20 30 30 30 91 D2 8B 40 8F 8A 96 BC 8F CC 20 20 20 20 20 20 20 20 20 20 30 31 30 30 30 33 30 30 8C E3 95 FB 83 56 83 58 83 65 83 80 8E 8E 8C B1 97 70 95 A8 8C 8F 32 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 30 37 32 30 31 20 20 20 20 20 20 20 20 20 20 20 20 32 30 32 36 30 33 31 37 31 35 31 39 31 35 39 39 30 30 30 32 30 30 8F F3 91 D4 82 50 20 20 20 20 20 20 20 20 20 20 20 20 20 20 93 AE 8D EC 92 86 20 20 20 20 20 20 20 20 20 20 20 20 20 20 30 30 30 30 34 36 30 30 30 30 30 38 30 30 30 30 30 30 30 20 20 20 20 20 20 20 20 20 20 03"; + + String hexLog = rawHexLog.trim(); + + byte[] src = hexStringToByteArray(hexLog); + + // --- 提取并打印正文数据 --- + // 跳过前7字节 (STX:1 + Serial:4 + Mode:2),去掉最后1字节 (ETX:1) + int bodyStart = 7; + int bodyEnd = src.length - 1; + byte[] body = Arrays.copyOfRange(src, bodyStart, bodyEnd); + + String rawText = new String(body, SHIFT_JIS); + + System.out.println("=== 原始报文分析 ==="); + System.out.println("总长度: " + src.length + " 字节"); + System.out.println("正文长度: " + body.length + " 字节"); + System.out.println("正文 HEX: " + hex(body)); + System.out.println("正文 文本: " + rawText); + System.out.println("==================\n"); + + System.out.println("No 字段名称 | 解析值 | HEX "); + System.out.println("-----------------------------------------------------------------------------------------"); + + // 数据部从第8字节开始 (STX:1 + Serial:4 + Mode:2 = 7) + int offset = 7; + + // 严格按照规格书长度定义 + f(1, "信号通番", src, offset, 9); offset += 9; + f(2, "受信日付", src, offset, 8); offset += 8; + f(3, "受信時刻", src, offset, 6); offset += 6; + f(4, "チャンネル番号", src, offset, 3); offset += 3; + f(5, "試験設定", src, offset, 1); offset += 1; + f(6, "ブロック番号", src, offset, 2); offset += 2; + f(7, "ブロック名称", src, offset, 30); offset += 30; // 规格书定义为30字节 + + f(8, "信号種別1", src, offset, 2); offset += 2; + f(9, "信号種別2", src, offset, 2); offset += 2; + f(10, "信号ラベル", src, offset, 2); offset += 2; + f(11, "信号状態", src, offset, 1); offset += 1; + f(12, "機動コース番号", src, offset, 3); offset += 3; + f(13, "担当地区", src, offset, 2); offset += 2; + f(14, "機動コースコード", src, offset, 3); offset += 3; + f(15, "待機所名", src, offset, 20); offset += 20; + f(16, "物理アドレス", src, offset, 8); offset += 8; + f(17, "契約先名称", src, offset, 30); offset += 30; // 修正:规格书定义为30字节 + f(18, "電話番号", src, offset, 20); offset += 20; + f(19, "備考", src, offset, 60); offset += 60; + + f(20, "表示色", src, offset, 2); offset += 2; // 正确对应 HEX: 30 37 (07) + f(21, "異常要因", src, offset, 1); offset += 1; // 正确对应 HEX: 32 (2) + f(22, "回線種別", src, offset, 2); offset += 2; // 正确对应 HEX: 30 31 (01) + f(23, "地区名称", src, offset, 12); offset += 12; + f(24, "送信日付", src, offset, 8); offset += 8; + f(25, "送信時刻", src, offset, 6); offset += 6; + f(26, "契約先番号", src, offset, 8); offset += 8; + f(27, "表示データ1", src, offset, 20); offset += 20; + f(28, "表示データ2", src, offset, 20); offset += 20; + f(29, "指令書作成", src, offset, 1); offset += 1; + f(30, "信号コード", src, offset, 4); offset += 4; + f(31, "カード番号", src, offset, 7); offset += 7; // 正确解析为 6000008 + f(32, "カード種別", src, offset, 1); offset += 1; + f(33, "マンション棟", src, offset, 2); offset += 2; + f(34, "マンション部屋", src, offset, 4); offset += 4; + f(35, "オプション", src, offset, 10); offset += 10; + } + + public static void f(int idx, String name, byte[] src, int start, int len) { + if (start + len > src.length) return; + byte[] raw = Arrays.copyOfRange(src, start, start + len); + String value = new String(raw, SHIFT_JIS).trim(); + System.out.printf("%02d %-12s | %-20s | HEX:%s%n", idx, name, value, hex(raw)); + } + + private static String hex(byte[] raw) { + StringBuilder sb = new StringBuilder(); + for (byte b : raw) sb.append(String.format("%02X ", b)); + return sb.toString().trim(); + } + + private static byte[] hexStringToByteArray(String s) { + s = s.replace(" ", ""); + int len = s.length(); + byte[] data = new byte[len / 2]; + for (int i = 0; i < len; i += 2) { + data[i / 2] = (byte) ((Character.digit(s.charAt(i), 16) << 4) + + Character.digit(s.charAt(i + 1), 16)); + } + return data; + } +} \ No newline at end of file diff --git a/src/test/java/com/techsor/datacenter/sender/f10/SochiData.java b/src/test/java/com/techsor/datacenter/sender/f10/SochiData.java new file mode 100644 index 0000000..c019104 --- /dev/null +++ b/src/test/java/com/techsor/datacenter/sender/f10/SochiData.java @@ -0,0 +1,68 @@ +package com.techsor.datacenter.sender.f10; + +import lombok.Data; + +@Data +public class SochiData { + + /** 1 信号通番 */ + private String signalSeq; + + /** 2 受信日付 yyyyMMdd */ + private String receiveDate; + + /** 3 受信時刻 HHmmss */ + private String receiveTime; + + /** 4 電話連絡時刻 */ + private String telTime; + + /** 5 電話対応者名 */ + private String telOperator; + + /** 6 指令時刻 */ + private String commandTime; + + /** 7 指令中止時刻 */ + private String cancelTime; + + /** 8 管制官名 */ + private String controllerName; + + /** 9 現着時刻 */ + private String arrivalTime; + + /** 10 現場対処名 */ + private String fieldActionName; + + /** 11 措置内容 */ + private String actionContent; + + /** 12 完了時刻 */ + private String finishTime; + + /** 13 記入者 */ + private String writer; + + /** 14 措置分類番号 */ + private String categoryNo; + + /** 15 措置分類名称 */ + private String categoryName; + + /** 16 物理アドレス */ + private String physicalAddress; + + /** 17 契約先番号 */ + private String contractNo; + + /** 18 転送フラグ */ + private String transferFlag; + + /** 19 発生事故記録書の有無 */ + private String incidentFlag; + + /** 20 予備領域(0x20 padding / optional field) */ + private String reservedField20; + +} diff --git a/src/test/java/com/techsor/datacenter/sender/f10/SochiDataParser.java b/src/test/java/com/techsor/datacenter/sender/f10/SochiDataParser.java new file mode 100644 index 0000000..6a57fe6 --- /dev/null +++ b/src/test/java/com/techsor/datacenter/sender/f10/SochiDataParser.java @@ -0,0 +1,202 @@ +package com.techsor.datacenter.sender.f10; + +import java.io.IOException; +import java.nio.charset.Charset; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.util.Arrays; + +public class SochiDataParser { + + private static final Charset SHIFT_JIS = + Charset.forName("Shift_JIS"); + + private static final int BASE_OFFSET = 7; + + // ========================= + // 解析入口 + // ========================= + public static SochiData parse(byte[] data) { + + if (data.length < 200 + BASE_OFFSET) { + throw new IllegalArgumentException( + "数据长度不足: " + data.length); + } + + SochiData d = new SochiData(); + + int bodyStart = 7; + + d.setSignalSeq(getTargetData(data, bodyStart, 9)); bodyStart += 9; + d.setReceiveDate(getTargetData(data, bodyStart, 8)); bodyStart += 8; + d.setReceiveTime(getTargetData(data, bodyStart, 6)); bodyStart += 6; + d.setTelTime(getTargetData(data, bodyStart, 6)); bodyStart += 6; + d.setTelOperator(getTargetData(data, bodyStart, 16)); bodyStart += 16; + d.setCommandTime(getTargetData(data, bodyStart, 6)); bodyStart += 6; + d.setCancelTime(getTargetData(data, bodyStart, 6)); bodyStart += 6; + d.setControllerName(getTargetData(data, bodyStart, 16)); bodyStart += 16; + d.setArrivalTime(getTargetData(data, bodyStart, 6)); bodyStart += 6; + d.setFieldActionName(getTargetData(data, bodyStart, 16)); bodyStart += 16; + d.setActionContent(getTargetData(data, bodyStart, 40)); bodyStart += 40; + d.setFinishTime(getTargetData(data, bodyStart, 6)); bodyStart += 6; + d.setWriter(getTargetData(data, bodyStart, 16)); bodyStart += 16; + d.setCategoryNo(getTargetData(data, bodyStart, 2)); bodyStart += 2; + d.setCategoryName(getTargetData(data, bodyStart, 2)); bodyStart += 2; + d.setPhysicalAddress(getTargetData(data, bodyStart, 8)); bodyStart += 8; + d.setContractNo(getTargetData(data, bodyStart, 8)); bodyStart += 8; + d.setTransferFlag(getTargetData(data, bodyStart, 1)); bodyStart += 1; + d.setIncidentFlag(getTargetData(data, bodyStart, 1)); bodyStart += 1; + + d.setReservedField20(parseReserved(data, bodyStart)); + + return d; + } + + // ========================= + // 普通字段 + // ========================= + private static String getTargetData(byte[] src, int start, int len) { + return new String(Arrays.copyOfRange(src, start, start + len), SHIFT_JIS).trim(); + } + + // ========================= + // 予備字段(0x20) + // ========================= + private static String parseReserved(byte[] data, int start) { + + byte[] bytes = new byte[21]; + System.arraycopy(data, start, bytes, 0, 21); + + boolean allSpace = true; + for (byte b : bytes) { + if (b != 0x20) { + allSpace = false; + break; + } + } + + if (allSpace) { + return null; + } + + return new String(bytes, SHIFT_JIS).trim(); + } + + // ========================= + // Debug打印(增强版) + // ========================= + private static void f(int idx, String name, byte[] src, int start, int len) { + + if (start >= src.length) { + System.out.printf("%02d %-18s | %-25s | OUT_OF_RANGE%n", + idx, name, ""); + return; + } + + int realLen = Math.min(len, src.length - start); + + byte[] raw = Arrays.copyOfRange(src, start, start + realLen); + + String value = new String(raw, SHIFT_JIS).trim(); + + System.out.printf( + "%02d %-18s | %-25s | HEX:%s%n", + idx, + name, + value, + hex(raw) + ); + } + + // ========================= + // HEX 输出 + // ========================= + private static String hex(byte[] raw) { + + StringBuilder sb = new StringBuilder(); + + for (byte b : raw) { + sb.append(String.format("%02X ", b)); + } + + return sb.toString().trim(); + } + + // ========================= + // HEX转byte[] + // ========================= + private static byte[] hexStringToByteArray(String s) { + + s = s.replace(" ", "") + .replace("\r", "") + .replace("\n", ""); + + if (s.length() % 2 != 0) { + throw new IllegalArgumentException("HEX长度非法: " + s.length()); + } + + byte[] data = new byte[s.length() / 2]; + + for (int i = 0; i < s.length(); i += 2) { + + data[i / 2] = (byte)( + (Character.digit(s.charAt(i), 16) << 4) + + Character.digit(s.charAt(i + 1), 16) + ); + } + + return data; + } + + // ========================= + // main 调试入口(保留你原结构) + // ========================= + public static void main(String[] args) throws IOException { + + String rawHexLog = + Files.readString( + Paths.get("src/test/java/com/techsor/datacenter/sender/f10/data200.txt") + ).trim(); + + byte[] src = hexStringToByteArray(rawHexLog); + + System.out.println("=== 措置データ(200byte)解析 ==="); + System.out.println("总长度: " + src.length + " byte"); + + System.out.println("-------------------------------------------------------------"); + System.out.println("No 字段名称 | 值 | HEX"); + System.out.println("-------------------------------------------------------------"); + + int o = BASE_OFFSET; + + f(1, "信号通番", src, o, 9); o += 9; + f(2, "受信日付", src, o, 8); o += 8; + f(3, "受信時刻", src, o, 6); o += 6; + f(4, "電話連絡時刻", src, o, 6); o += 6; + f(5, "電話対応者名", src, o, 16); o += 16; + f(6, "指令時刻", src, o, 6); o += 6; + f(7, "指令中止時刻", src, o, 6); o += 6; + f(8, "管制官名", src, o, 16); o += 16; + f(9, "現着時刻", src, o, 6); o += 6; + f(10, "現場対処名", src, o, 16); o += 16; + f(11, "措置内容", src, o, 40); o += 40; + f(12, "完了時刻", src, o, 6); o += 6; + f(13, "記入者", src, o, 16); o += 16; + f(14, "措置分類番号", src, o, 2); o += 2; + f(15, "措置分類名称", src, o, 2); o += 2; + f(16, "物理アドレス", src, o, 8); o += 8; + f(17, "契約先番号", src, o, 8); o += 8; + f(18, "転送フラグ", src, o, 1); o += 1; + f(19, "事故記録書有無", src, o, 1); o += 1; + + f(20, "予備領域", src, o, 21); + + System.out.println("-------------------------------------------------------------"); + + SochiData data = parse(src); + + System.out.println(); + System.out.println("=== 对象解析结果 ==="); + System.out.println(data); + } +} \ No newline at end of file diff --git a/src/test/java/com/techsor/datacenter/sender/f10/data200-2.txt b/src/test/java/com/techsor/datacenter/sender/f10/data200-2.txt new file mode 100644 index 0000000..92f6813 --- /dev/null +++ b/src/test/java/com/techsor/datacenter/sender/f10/data200-2.txt @@ -0,0 +1 @@ +02 30 30 30 31 31 30 30 31 36 35 32 31 37 37 34 32 30 32 36 30 35 31 33 31 30 30 34 34 37 20 20 20 20 20 20 82 AD 82 DC 82 AD 82 E7 20 20 20 20 20 20 20 20 31 30 31 32 30 30 20 20 20 20 20 20 8E 9B 93 63 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 93 5F 8C 9F 92 86 81 40 8C DF 8C E3 82 A9 82 E7 8D EC 82 C8 82 CC 82 C5 82 BB 82 EA 82 DC 82 C5 94 AD 95 F1 82 A0 82 E8 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 30 30 30 30 30 31 30 35 30 32 30 30 31 35 30 31 32 33 30 30 31 30 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 03 \ No newline at end of file diff --git a/src/test/java/com/techsor/datacenter/sender/f10/data200.txt b/src/test/java/com/techsor/datacenter/sender/f10/data200.txt new file mode 100644 index 0000000..a251dee --- /dev/null +++ b/src/test/java/com/techsor/datacenter/sender/f10/data200.txt @@ -0,0 +1 @@ +02 30 30 30 31 31 30 30 31 36 35 32 31 37 37 34 32 30 32 36 30 35 31 33 31 30 30 34 34 37 20 20 20 20 20 20 82 AD 82 DC 82 AD 82 E7 20 20 20 20 20 20 20 20 31 30 31 32 30 30 20 20 20 20 20 20 8E 9B 93 63 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 93 5F 8C 9F 92 86 81 40 8C DF 8C E3 82 A9 82 E7 8D EC 82 C8 82 CC 82 C5 82 BB 82 EA 82 DC 82 C5 94 AD 95 F1 82 A0 82 E8 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 30 30 30 30 30 31 30 35 30 32 30 30 31 35 30 31 32 33 30 30 31 30 32 30 32 36 30 35 32 32 31 35 33 30 34 35 20 20 20 20 20 20 20 03 \ No newline at end of file diff --git a/src/test/java/com/techsor/datacenter/sender/f10/data320.txt b/src/test/java/com/techsor/datacenter/sender/f10/data320.txt new file mode 100644 index 0000000..64c2fd7 --- /dev/null +++ b/src/test/java/com/techsor/datacenter/sender/f10/data320.txt @@ -0,0 +1 @@ +02 30 30 30 31 31 30 30 31 30 30 37 30 30 31 38 32 30 32 36 30 33 31 37 31 35 31 39 31 35 30 30 34 30 30 31 83 75 83 8D 83 62 83 4E 82 50 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 30 33 30 31 30 31 31 30 35 30 20 20 30 30 30 91 D2 8B 40 8F 8A 96 BC 8F CC 20 20 20 20 20 20 20 20 20 20 30 31 30 30 30 33 30 30 8C E3 95 FB 83 56 83 58 83 65 83 80 8E 8E 8C B1 97 70 95 A8 8C 8F 32 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 30 37 32 30 31 20 20 20 20 20 20 20 20 20 20 20 20 32 30 32 36 30 33 31 37 31 35 31 39 31 35 39 39 30 30 30 32 30 30 8F F3 91 D4 82 50 20 20 20 20 20 20 20 20 20 20 20 20 20 20 93 AE 8D EC 92 86 20 20 20 20 20 20 20 20 20 20 20 20 20 20 30 30 30 30 34 36 30 30 30 30 30 38 30 30 30 30 30 30 30 20 20 20 20 20 20 20 20 20 20 03 \ No newline at end of file