diff --git a/src/main/java/com/xujie/sys/common/utils/ModbusUtils.java b/src/main/java/com/xujie/sys/common/utils/ModbusUtils.java index 8ae694e0..b573c5c8 100644 --- a/src/main/java/com/xujie/sys/common/utils/ModbusUtils.java +++ b/src/main/java/com/xujie/sys/common/utils/ModbusUtils.java @@ -27,11 +27,8 @@ public class ModbusUtils { try { master.connect(); Register[] registers = master.readMultipleRegisters(unitId, ref, 1); - Register[] registers2 = master.readMultipleRegisters(unitId, 300, 1); - log.info("读取300地址寄存器成功 - IP: {}, Port: {}, Ref: {}, Value: {}", - ip, port, 300, registers2[0].getValue()); if (registers != null && registers.length > 0) { - log.info("读取302地址寄存器成功 - IP: {}, Port: {}, Ref: {}, Value: {}", + log.info("读取保持寄存器成功 - IP: {}, Port: {}, Ref: {}, Value: {}", ip, port, ref, registers[0].getValue()); return registers[0].getValue(); } @@ -203,6 +200,32 @@ public class ModbusUtils { return resultMap; } + /** + * 连续读取多字保持寄存器,按批拆分后合并。 + * Modbus FC03 单次请求的字数超过从站允许上限(常见 100~125)时,从站会返回异常码 0x03 Illegal Data Value。 + */ + public static Map readHoldingRegistersBatched(String ip, int port, int slaveId, int offset, int quantity) { + final int maxPerRequest = 100; + Map merged = new HashMap<>(); + int pos = offset; + int left = quantity; + while (left > 0) { + int chunk = Math.min(maxPerRequest, left); + Map part = readHoldingRegisters(ip, port, slaveId, pos, chunk); + if (part == null || part.isEmpty()) { + log.warn("分批读取保持寄存器中断 - offset={}, 本批 quantity={}, 已合并 {} 字", pos, chunk, merged.size()); + break; + } + merged.putAll(part); + pos += chunk; + left -= chunk; + } + if (merged.size() < quantity) { + log.warn("分批读取保持寄存器未凑满 - 期望 {} 字, 实际 {} 字", quantity, merged.size()); + } + return merged; + } + public static Map readFloatHoldingRegisters(String ip, int port, int slaveId, int offset, int quantity) { Map resultMap = new HashMap<>(); ModbusTCPMaster master = new ModbusTCPMaster(ip, port); diff --git a/src/main/java/com/xujie/sys/modules/pms/service/Impl/QcServiceImpl.java b/src/main/java/com/xujie/sys/modules/pms/service/Impl/QcServiceImpl.java index fe805ab8..0415a358 100644 --- a/src/main/java/com/xujie/sys/modules/pms/service/Impl/QcServiceImpl.java +++ b/src/main/java/com/xujie/sys/modules/pms/service/Impl/QcServiceImpl.java @@ -4827,12 +4827,14 @@ public class QcServiceImpl implements QcService { // 无工单检验:先看302异常信号,1则读取异常数据并复位,0则直接返回空数据 logger.info("orderNo:{}",data.getOrderNo()); if ("000#1".equals(data.getOrderNo())) { - Integer abnormalSignal = ModbusUtils.readSingleRegister(ip, port, unitId, isexceptionAdress); + //logCollectorModelModbusProbe(ip, port, unitId, isexceptionAdress); + Integer abnormalSignal = ModbusUtils.readSingleRegister(ip, port, unitId, isexceptionAdress-1); if (abnormalSignal == null || abnormalSignal == 0) { return new ArrayList<>(); } ArrayList abnormalRows = getAbnormalCollectorData(ip, port, unitId, data); - resetCollectorAbnormalRegisters(ip, port, unitId); + + //resetCollectorAbnormalRegisters(ip, port, unitId); return abnormalRows; }else{ logger.info(port+","+ip+","+unitId+","+baseAddr+","+groups+","); @@ -4862,6 +4864,77 @@ public class QcServiceImpl implements QcService { return abnormalSignal != null && abnormalSignal == 1; } + /** + * 无工单 000#1:屏显 40300/40302 等与 j2mod PDU(ref) 换算方式不一致时会导致“采集器型号”读错。 + * 一次性读取窗口并打印多种常见换算,便于对照 Modbus 调试工具选用正确 ref。 + */ + private void logCollectorModelModbusProbe(String ip, int port, int unitId, Integer dbExceptionAddr) { + final int windowStart = 288; + final int windowLen = 36; + Map raw = ModbusUtils.readHoldingRegisters(ip, port, unitId, windowStart, windowLen); + logger.info("[采集器型号探测] ip={} port={} unitId={} DB异常寄存器ref(isexceptionAdress)={}", + ip, port, unitId, dbExceptionAddr); + if (raw == null || raw.isEmpty()) { + logger.warn("[采集器型号探测] 批量读取 {}~{} 失败或无返回,后续单项读取仍可能成功", windowStart, windowStart + windowLen - 1); + logSingleRegisterProbe(ip, port, unitId); + return; + } + StringBuilder sb = new StringBuilder(); + for (int r = windowStart; r < windowStart + windowLen; r++) { + Integer v = raw.get(r); + sb.append(String.format(Locale.ROOT, "%d=%s ", r, v == null ? "null" : String.valueOf(v & 0xFFFF))); + } + logger.info("[采集器型号探测] pdu {}~{} (unsigned): {}", windowStart, windowStart + windowLen - 1, sb.toString().trim()); + + int v299 = regU(raw, 299); + int v300 = regU(raw, 300); + int v301 = regU(raw, 301); + int v302 = regU(raw, 302); + int v303 = regU(raw, 303); + logger.info("[采集器型号探测] 常用单点: pdu299={} pdu300={} pdu301={} pdu302={} pdu303={}", + v299, v300, v301, v302, v303); + + logger.info("[采集器型号探测] 换算A 屏显40300≈40001+299 → 看 pdu299={}; 屏显40302≈40001+301 → 看 pdu301={}", v299, v301); + logger.info("[采集器型号探测] 换算B 屏显40300≈40000+300 → 看 pdu300={}; 屏显40302≈40000+302 → 看 pdu302={}", v300, v302); + logger.info("[采集器型号探测] 换算C 后三位0300/0302 即 pdu300/pdu302 → {} / {}", v300, v302); + + Map batch299 = ModbusUtils.readHoldingRegisters(ip, port, unitId, 299, 10); + Map batch300 = ModbusUtils.readHoldingRegisters(ip, port, unitId, 300, 10); + logger.info("[采集器型号探测] FC03 连续10个自 pdu299: {}", formatRegisterBlock(batch299, 299, 10)); + logger.info("[采集器型号探测] FC03 连续10个自 pdu300: {}", formatRegisterBlock(batch300, 300, 10)); + + Integer single300 = ModbusUtils.readSingleRegister(ip, port, unitId, 300); + Integer single302 = ModbusUtils.readSingleRegister(ip, port, unitId, 302); + Integer single301 = ModbusUtils.readSingleRegister(ip, port, unitId, 301); + logger.info("[采集器型号探测] readSingleRegister 对照: ref301={} ref300={} ref302={}", single301, single300, single302); + } + + /** 批量失败时再逐项单读,排除连接/批量限制问题 */ + private static void logSingleRegisterProbe(String ip, int port, int unitId) { + for (int ref : new int[]{297, 298, 299, 300, 301, 302, 303}) { + Integer v = ModbusUtils.readSingleRegister(ip, port, unitId, ref); + logger.info("[采集器型号探测] 单读 pdu{} → {}", ref, v); + } + } + + private static String formatRegisterBlock(Map raw, int start, int count) { + if (raw == null || raw.isEmpty()) { + return "(empty)"; + } + StringBuilder b = new StringBuilder(); + for (int i = 0; i < count; i++) { + int r = start + i; + Integer v = raw.get(r); + b.append(String.format(Locale.ROOT, "[%d]=%s ", r, v == null ? "null" : String.valueOf(v & 0xFFFF))); + } + return b.toString().trim(); + } + + private static int regU(Map raw, int ref) { + Integer v = raw.get(ref); + return v == null ? -1 : (v & 0xFFFF); + } + private EquipmentFolderLocation getCollectorLocation(String site) { LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); wrapper.eq(EquipmentFolderLocation::getEquipmentNo, "J001"); @@ -4872,8 +4945,8 @@ public class QcServiceImpl implements QcService { private ArrayList getAbnormalCollectorData(String ip, int port, int unitId, QcFAIRecordData data) throws Exception { int readStartAddr = 400; - int readCount = 195; // 覆盖 400~594 - Map raw = ModbusUtils.readHoldingRegisters(ip, port, unitId, readStartAddr, readCount); + int readCount = 195; // 覆盖 400~594(单次 FC03 字数不宜超过从站上限,需分批读) + Map raw = ModbusUtils.readHoldingRegistersBatched(ip, port, unitId, readStartAddr, readCount); ArrayList subDetailValues = new ArrayList<>(); if (raw == null || raw.isEmpty()) { return subDetailValues; @@ -4911,26 +4984,27 @@ public class QcServiceImpl implements QcService { continue; } - SubDetailValues detailValues = new SubDetailValues(); - detailValues.setBuNo(data.getBu()); - detailValues.setInspectionNo(data.getInspectionNo()); - detailValues.setItemNo(data.getItemNo()); - detailValues.setIsSubmit("N"); - detailValues.setSite(data.getSite()); - detailValues.setNum(1); - detailValues.setSubDetailValue(String.valueOf(abnormalValues.get(0))); - detailValues.setSubDetailValueB(String.valueOf(abnormalValues.get(1))); - detailValues.setSubDetailValueC(String.valueOf(abnormalValues.get(2))); - detailValues.setSubDetailValueD(String.valueOf(abnormalValues.get(3))); - detailValues.setSubDetailValueE(String.valueOf(abnormalValues.get(4))); - detailValues.setSamplingLocation(String.format( - "%d-%d-%d %d:%d:%d", + // 每组 1 套时间(6 个寄存器)对应 5 个异常通道:拆成 5 条记录,每条 1 数值 + 1 时间 + String collectedTime = String.format(Locale.ROOT, + "%d-%d %d:%d:%d", timeParts.get(0), timeParts.get(1), timeParts.get(2), - timeParts.get(3), timeParts.get(4), timeParts.get(5) - )); - subDetailValues.add(detailValues); + timeParts.get(3), timeParts.get(4)); + for (int i = 0; i < 5; i++) { + SubDetailValues detailValues = new SubDetailValues(); + detailValues.setBuNo(data.getBu()); + detailValues.setInspectionNo(data.getInspectionNo()); + detailValues.setItemNo(data.getItemNo()); + detailValues.setIsSubmit("N"); + detailValues.setSite(data.getSite()); + detailValues.setNum(1); + detailValues.setSubDetailValue(String.valueOf(abnormalValues.get(i))); + detailValues.setSubDetailValueB(collectedTime); + detailValues.setSamplingLocationB(collectedTime); + subDetailValues.add(detailValues); + } } logger.info("解析后获取到的数据:{}",subDetailValues.toString()); + batchInsertCollectorSubDetailRecords(data, subDetailValues); return subDetailValues; } @@ -4953,13 +5027,50 @@ public class QcServiceImpl implements QcService { logger.info("复位结束"); } + /** + * 按检验类型将采集子表明细批量写入对应 qc_*_sub_detail_record 表(常规 Modbus 与无工单异常采集共用)。 + */ + private void batchInsertCollectorSubDetailRecords(QcFAIRecordData data, ArrayList subDetailValues) { + if (subDetailValues == null || subDetailValues.isEmpty()) { + return; + } + if ("ipqc".equals(data.getSopType())) { + StringBuilder sql = new StringBuilder(); + sql.append(" INSERT INTO qc_ipqc_sub_detail_record "); + sql.append(" (inspection_no, item_no, sub_detail_value, sampling_location, site, sampling_location_b, bu_no ,num) "); + sql.append(" VALUES "); + sql.append(" (:inspectionNo, :itemNo, :subDetailValue, :samplingLocation, :site, :samplingLocationB, :buNo ,:num) "); + parameterJdbcTemplate.batchUpdate(sql.toString(), SqlParameterSourceUtils.createBatch(subDetailValues.toArray())); + } else if ("iqc".equals(data.getSopType())) { + StringBuilder sql = new StringBuilder(); + sql.append(" INSERT INTO qc_iqc_sub_detail_record "); + sql.append(" (inspection_no, item_no, sub_detail_value, sampling_location, site, sampling_location_b, bu_no ,num) "); + sql.append(" VALUES "); + sql.append(" (:inspectionNo, :itemNo, :subDetailValue, :samplingLocation, :site, :samplingLocationB, :buNo ,:num) "); + parameterJdbcTemplate.batchUpdate(sql.toString(), SqlParameterSourceUtils.createBatch(subDetailValues.toArray())); + } else if ("fai".equals(data.getSopType())) { + StringBuilder sql = new StringBuilder(); + sql.append(" INSERT INTO qc_fai_sub_detail_record "); + sql.append(" (inspection_no, item_no, sub_detail_value, sampling_location, site, sampling_location_b, bu_no ,num) "); + sql.append(" VALUES "); + sql.append(" (:inspectionNo, :itemNo, :subDetailValue, :samplingLocation, :site, :samplingLocationB, :buNo ,:num) "); + parameterJdbcTemplate.batchUpdate(sql.toString(), SqlParameterSourceUtils.createBatch(subDetailValues.toArray())); + } else if ("fqc".equals(data.getSopType())) { + StringBuilder sql = new StringBuilder(); + sql.append(" INSERT INTO qc_fqc_sub_detail_record "); + sql.append(" (inspection_no, item_no, sub_detail_value, sampling_location, site, sampling_location_b, bu_no,num) "); + sql.append(" VALUES "); + sql.append(" (:inspectionNo, :itemNo, :subDetailValue, :samplingLocation, :site, :samplingLocationB, :buNo ,:num) "); + parameterJdbcTemplate.batchUpdate(sql.toString(), SqlParameterSourceUtils.createBatch(subDetailValues.toArray())); + } + } + public ArrayList getCollectorDataByModbus(int port,String ip,int unitId,int baseAddr,int groups,QcFAIRecordData data) throws Exception { - final int avgAddr = 220; - // 每个点占用 2 个寄存器,连续读取 200~221 共 22 个寄存器 - final int quantity = (groups + 1) * 2; // 11 * 2 = 22 + // 每个点占用 2 个寄存器;只读 groups 个测量点,不包含平均值(原 220 不再采集入库) + final int quantity = groups * 2; - //固定采集两次 + // 固定采集两次:每次 groups 条,共 2 * groups 条(例如 10 通道时为 20 条) ArrayList subDetailValues = new ArrayList<>(); @@ -4995,17 +5106,6 @@ public class QcServiceImpl implements QcService { } } - // 平均值地址 220,同样按照“高字在前”组合 - int highRefAvg = avgAddr; - int lowRefAvg = avgAddr + 1; - Integer highAvg = raw.get(highRefAvg); - Integer lowAvg = raw.get(lowRefAvg); - if (lowAvg != null && highAvg != null) { - int bits = (highAvg << 16) | (lowAvg & 0xFFFF); - float f = Float.intBitsToFloat(bits); - resultMap.put(String.valueOf(avgAddr), f); - } - for (Map.Entry entry : resultMap.entrySet()) { SubDetailValues detailValues = new SubDetailValues(); detailValues.setBuNo(data.getBu()); @@ -5021,35 +5121,7 @@ public class QcServiceImpl implements QcService { } - if("ipqc".equals(data.getSopType())){ - StringBuilder sql = new StringBuilder(); - sql.append(" INSERT INTO qc_ipqc_sub_detail_record "); - sql.append(" (inspection_no, item_no, sub_detail_value, sampling_location, site, sampling_location_b, bu_no ,num) "); - sql.append(" VALUES "); - sql.append(" (:inspectionNo, :itemNo, :subDetailValue, :samplingLocation, :site, :samplingLocationB, :buNo ,:num) "); - parameterJdbcTemplate.batchUpdate(sql.toString(), SqlParameterSourceUtils.createBatch(subDetailValues.toArray())); - }else if("iqc".equals(data.getSopType())){ - StringBuilder sql = new StringBuilder(); - sql.append(" INSERT INTO qc_iqc_sub_detail_record "); - sql.append(" (inspection_no, item_no, sub_detail_value, sampling_location, site, sampling_location_b, bu_no ,num) "); - sql.append(" VALUES "); - sql.append(" (:inspectionNo, :itemNo, :subDetailValue, :samplingLocation, :site, :samplingLocationB, :buNo ,:num) "); - parameterJdbcTemplate.batchUpdate(sql.toString(), SqlParameterSourceUtils.createBatch(subDetailValues.toArray())); - }else if("fai".equals(data.getSopType())){ - StringBuilder sql = new StringBuilder(); - sql.append(" INSERT INTO qc_fai_sub_detail_record "); - sql.append(" (inspection_no, item_no, sub_detail_value, sampling_location, site, sampling_location_b, bu_no ,num) "); - sql.append(" VALUES "); - sql.append(" (:inspectionNo, :itemNo, :subDetailValue, :samplingLocation, :site, :samplingLocationB, :buNo ,:num) "); - parameterJdbcTemplate.batchUpdate(sql.toString(), SqlParameterSourceUtils.createBatch(subDetailValues.toArray())); - }else if("fqc".equals(data.getSopType())){ - StringBuilder sql = new StringBuilder(); - sql.append(" INSERT INTO qc_fqc_sub_detail_record "); - sql.append(" (inspection_no, item_no, sub_detail_value, sampling_location, site, sampling_location_b, bu_no,num) "); - sql.append(" VALUES "); - sql.append(" (:inspectionNo, :itemNo, :subDetailValue, :samplingLocation, :site, :samplingLocationB, :buNo ,:num) "); - parameterJdbcTemplate.batchUpdate(sql.toString(), SqlParameterSourceUtils.createBatch(subDetailValues.toArray())); - } + batchInsertCollectorSubDetailRecords(data, subDetailValues); return subDetailValues; }