package com.ruoyi.device.utils; import cn.hutool.core.io.IORuntimeException; import cn.hutool.http.HttpUtil; import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONArray; import com.alibaba.fastjson.JSONObject; import com.baomidou.mybatisplus.core.toolkit.ObjectUtils; import com.baomidou.mybatisplus.core.toolkit.StringUtils; import com.ruoyi.device.pojo.DataConfig; import com.ruoyi.device.pojo.Device; import com.ruoyi.framework.exception.ErrorException; import javax.script.ScriptEngine; import javax.script.ScriptEngineManager; import javax.servlet.http.HttpServletRequest; import java.math.BigDecimal; import java.text.DecimalFormat; import java.time.LocalDateTime; import java.time.format.DateTimeFormatter; import java.util.*; import java.util.concurrent.atomic.AtomicInteger; import java.util.regex.Matcher; import java.util.regex.Pattern; import java.util.stream.Collectors; public class DataAcquisition { private static final String HTTP = "http://"; private static final String GETFILE = ":9527/lims/getFile"; // 获取文件接口 private static final String MOVEFILE = ":9527/lims/moveFile"; // 文件移动地址 private static final String splitIdentifier = "@-@"; // 自定义唯一标识分割符 public static final String frequency = "frequency"; /** * 数采入口 * * @param dataConfig * @param device * @return */ public static Map dataAcquisitionEntrance(List dataConfig, Device device, String entrustCode, String sampleCode, String ip, String cableTag) { // 判断是否是影像测量仪 if (device.getManagementNumber().equals("JCZX-ZB-OP07001")) { if (device.getFileType().equals(".xlsx")) { String url = device.getCollectUrl() + "\\" + sampleCode.replace("/", ""); if (StringUtils.isNotBlank(dataConfig.get(0).getAnotherName())) { url += dataConfig.get(0).getAnotherName(); } url += ".xlsx"; device.setCollectUrl(url); } } // 判断是否是电缆燃烧烟密度测量系统 if (device.getManagementNumber().equals("JCZX-ZB-ZT03002")) { if (device.getFileType().equals(".txt")) { device.setCollectUrl(device.getCollectUrl() + "\\-" + sampleCode.replace("/", "")); } } /** * filePath 文件采集路径 * fileExtension 文件后缀 * entrustCode 委托编号 * sampleCode 样品编号 * mdbEntrustCode mdb文件需要:委托编号字段 为什么没有去这个mdb前缀呢?因为已经给客户的部分电脑上安装了采集器,而用户不接受重新安装采集器,所以就没有去除 * mdbSampleCode mdb文件需要:样品编号字段 */ String http = HTTP + ip + GETFILE + "?filePath=" + device.getCollectUrl() + "&fileExtension=" + device.getFileType() + "&entrustCode=" + entrustCode + "&sampleCode=" + sampleCode + "&mdbEntrustCode=" + device.getEntrustCode() + "&mdbSampleCode=" + device.getSampleCode() + "&dbFileName=" + device.getDbFileName(); String result = null; try { result = HttpUtil.get(http); } catch (IORuntimeException e) { e.printStackTrace(); throw new ErrorException("所在电脑未安装或未启动:LIMS文件采集器!"); } JSONObject jsonObject = JSON.parseObject(result); if (Objects.equals(jsonObject.get("code"), 1)) { if (ObjectUtils.isEmpty(jsonObject.get("msg"))) { throw new ErrorException("未查询到文件!可能该路径(" + device.getCollectUrl() + ")下并没有所需(" + device.getFileType() + ")文件!"); } else { throw new ErrorException(jsonObject.get("msg") + ""); } } else { String data = jsonObject.get("data") + ""; // 考虑到一个检测项可能会存在多个数采配置,所以需要进行分组 Map> userMap = dataConfig.stream() .peek(i -> { String itemName = i.getInspectionItem(); if (StringUtils.isNotBlank(i.getInspectionItemClass())) { itemName += "@" + i.getInspectionItemClass(); } String name = i.getInspectionItem().equals(i.getInspectionItemSubclass()) ? itemName + "," : itemName + "," + i.getInspectionItemSubclass(); // 添加检验项名称 i.setInsProductItem(name); }) .collect(Collectors.groupingBy(DataConfig::getInsProductItem)); Map map; switch (device.getFileType()) { case ".docx": map = analysisString(data, userMap, device, entrustCode, sampleCode); break; case ".xlsx": map = analysisList(data, userMap, device, entrustCode, sampleCode); break; case ".xls": map = analysisXlsList(data, userMap, device, entrustCode, sampleCode); break; case ".txt": map = analysisTxt(data, userMap, device, entrustCode, sampleCode); break; case ".csv": map = analysisList(data, userMap, device, entrustCode, sampleCode); break; case ".mdb": // 判断是否是拉力机 if (device.getManagementNumber().equals("JCZX-ZB-FF01014")) { map = analysisMdbByPull(data, userMap, device, cableTag); } else { map = analysisMdb(data, userMap, device); } break; case ".db": map = analysisDb(data, userMap, device); break; case ".png": map = readPngString(data, userMap, device); break; case ".pdf": map = readPdfString(data, userMap, device); break; case ".et": map = readEtString(data, userMap, device); break; default: map = null; break; } // 如果存在存储地址,则移动地址 if (ObjectUtils.isNotEmpty(device.getStorageUrl())) { String s = HTTP + ip + MOVEFILE + "?startFilePath=" + device.getCollectUrl() + "&endFilePath=" + device.getStorageUrl() + "&fileType=" + device.getFileType(); HttpUtil.get(s); } return map; } } private static Map readEtString(String data, Map> dataConfig, Device device) { Map map = new HashMap<>(); // 定义 RoHS 指标列表 List rohsIndicators = Arrays.asList("RoHS指标,镉(Cd)", "RoHS指标,总溴联苯(Br)", "RoHS指标,总铬(Cr)", "RoHS指标,汞(Hg)", "RoHS指标,铅(Pb)"); dataConfig.forEach((k, v) -> { List list; boolean containsRoHS = false; for (DataConfig item : v) { if (rohsIndicators.contains(item.getInsProductItem())) { containsRoHS = true; break; } } if (containsRoHS) { list = analyzeEt1Data(data, v, splitIdentifier); } else { list = analyzeEtData(data, v, splitIdentifier); } // 进行公式计算 Object resultValue = calculationFormula(list, v.get(0), k, device); map.put(k, resultValue); }); return map; } /** * 荧光光谱仪 .et文件 * * @param data * @param v * @param splitIdentifier * @return */ private static List analyzeEt1Data(String data, List v, String splitIdentifier) { List finalResult = new ArrayList<>(); List matchingLines = new ArrayList<>(); // 遍历 DataConfig 列表 for (DataConfig config : v) { String referx = getRefer(config.getReferx()); if (ObjectUtils.isEmpty(referx)) { continue; } // 使用正则表达式构建完整匹配的模式 String regex = "\\b" + Pattern.quote(referx) + "\\b"; Pattern pattern = Pattern.compile(regex); String[] lines = data.replaceAll(" ", "").split("\n"); // 筛选出包含 referx 的行 for (String line : lines) { if (pattern.matcher(line).find()) { matchingLines.add(line); } } } // 遍历匹配的行,提取 splitIdentifier 第七个和第八个分隔符中间的值 for (String line : matchingLines) { int startIndex = -1; int endIndex = -1; int count = 0; int index = 0; while ((index = line.indexOf(splitIdentifier, index)) != -1) { count++; if (count == 7) { startIndex = index + splitIdentifier.length(); } else if (count == 8) { endIndex = index; break; } index++; } if (startIndex != -1 && endIndex != -1) { finalResult.add(line.substring(startIndex, endIndex)); } } // 确保返回值为数组格式 if (finalResult.size() == 1) { List singleResultAsArray = new ArrayList<>(); singleResultAsArray.add(finalResult.get(0)); return singleResultAsArray; } return finalResult; } /** * 拉力机.et文件 * * @param data * @param v * @param splitIdentifier * @return */ public static List analyzeEtData(String data, List v, String splitIdentifier) { List finalResult = new ArrayList<>(); for (DataConfig config : v) { String referx = getRefer(config.getReferx()); if (ObjectUtils.isEmpty(referx)) { continue; } String[] lines = data.replaceAll(" ", "").split("\n"); boolean foundReferx = false; List validLines = new ArrayList<>(); int referxColumnIndex = -1; // 用于存储referx所在列索引 // 先找出referx所在列索引 for (String line : lines) { String[] parts = line.split(splitIdentifier); for (int i = 0; i < parts.length; i++) { if (parts[i].contains(referx)) { referxColumnIndex = i; break; } } if (referxColumnIndex != -1) { break; } } // 若未找到匹配的referx,抛出异常 if (referxColumnIndex == -1) { throw new IllegalArgumentException("请输入正确的x值,文件中未找到与 " + referx + " 匹配的值。"); } for (int i = 0; i < lines.length; i++) { String line = lines[i]; String[] parts = line.split(splitIdentifier, 2); if (!foundReferx) { if (containsReferx(parts, referx)) { foundReferx = true; } continue; } if (i > 0 && foundReferx) { if (parts.length > 0 && isNumeric(parts[0])) { validLines.add(line); } } } for (String line : validLines) { String[] parts = line.split(splitIdentifier); if (referxColumnIndex < parts.length) { String value = parts[referxColumnIndex].trim(); if (value.isEmpty()) { value = ""; } finalResult.add(value); } else { finalResult.add(""); } } } return finalResult; } private static boolean containsReferx(String[] parts, String referx) { for (String part : parts) { if (part.contains(referx)) { return true; } } return false; } private static boolean isNumeric(String str) { try { Double.parseDouble(str); return true; } catch (NumberFormatException e) { return false; } } /** * 色谱pdf文件需要定位x,y * * @param data * @param dataConfig * @param device * @return */ private static Map readPdfString(String data, Map> dataConfig, Device device) { Map map = new HashMap<>(); dataConfig.forEach((k, v) -> { List list = new ArrayList<>(); for (int config = 0; config < v.size(); config++) { String referX = getRefer(v.get(config).getReferx()); String referY = getRefer(v.get(config).getRefery()); String[] aColumnY = data.split("\n"); for (String line : aColumnY) { if (line.contains(referX) || line.contains(referY)) { list.add(line); } } int referYColumnIndex = -1; for (Object item : list) { // 使用正则表达式 \\s+ 分割字符串 String[] columns = ((String) item).split("\\s+"); for (int i = 0; i < columns.length; i++) { if (columns[i].equals(referY)) { referYColumnIndex = i; break; } } if (referYColumnIndex != -1) { break; } } List targetColumnValues = new ArrayList<>(); if (referYColumnIndex != -1) { for (Object item : list) { String line = (String) item; // 过滤掉包含 referY 的行 if (!line.contains(referY)) { String[] columns = line.split("\\s+"); if (columns.length > referYColumnIndex) { targetColumnValues.add(columns[referYColumnIndex]); } } } } Object resultValue = calculationFormula(targetColumnValues, v.get(0), k, device); map.put(k, resultValue); } }); return map; } public static List extractColumnValues(List list, String columnName) { List columnValues = new ArrayList<>(); if (list.isEmpty()) { return columnValues; } String[] headers = ((String) list.get(0)).split(","); int columnIndex = -1; for (int i = 0; i < headers.length; i++) { if (headers[i].equals(columnName)) { columnIndex = i; break; } } if (columnIndex == -1) { return columnValues; } for (int i = 1; i < list.size(); i++) { String[] columns = ((String) list.get(i)).split(","); if (columnIndex < columns.length) { columnValues.add(columns[columnIndex]); } } return columnValues; } public static Map createFrequency(String entrustCode, String sampleCode, Map map) { Set set = new LinkedHashSet<>(); map.forEach((key, value) -> { String[] split = key.split(","); String inspectionItem = split[0]; // 只要有一个不为空就set进去 if (ObjectUtils.isNotEmpty(value)) { set.add(inspectionItem); } }); Map result = new HashMap<>(); for (String inspectionItemKey : set) { Map hashMap = new HashMap<>(); map.forEach((key, value) -> { String[] split = key.split(","); String inspectionItem = split[0]; if (inspectionItemKey.equals(inspectionItem)) { if (split.length > 1) { hashMap.put(split[1], value); } else { hashMap.put("", value); } } }); String frequency = createKey(entrustCode, sampleCode, inspectionItemKey); hashMap.put("frequency", frequency); result.put(inspectionItemKey, hashMap); } return result; } public static String createKey(String entrustCode, String sampleCode, String inspectionItemKey) { return "1"; } /** * 需要通过X,Y轴定位 * * @param data * @param dataConfig * @return */ private static Map analysisDb(String data, Map> dataConfig, Device device) { JSONObject jsonObject = JSON.parseObject(data); Map map = new HashMap<>(); if (jsonObject.isEmpty()) { return map; } JSONArray dataList = JSONArray.parseArray(jsonObject.get("data").toString()); dataConfig.forEach((k, v) -> { AtomicInteger numberOfDataEntries = new AtomicInteger(); List list = new ArrayList<>(); for (int config = 0; config < v.size(); config++) { String refery = getRefer(v.get(config).getRefery()); for (int i = 0; i < dataList.size(); i++) { JSONObject jsonObject1 = JSON.parseObject(dataList.get(i).toString()); Object o = jsonObject1.get(refery); if (ObjectUtils.isNotEmpty(o)) { numberOfDataEntries.addAndGet(1); list.add(o); } } } // 拼接数采配置 List result = new ArrayList<>(); for (int i = 0; i < numberOfDataEntries.get(); i++) { String aggregate = ""; for (int j = 0; j < v.size(); j++) { int index; if (j == 0) { index = i; } else { index = numberOfDataEntries.get() + i; } aggregate += list.get(index).toString() + ","; } int lastIndex = aggregate.lastIndexOf(","); String substring = aggregate.substring(0, lastIndex); result.add(substring); } // 进行公式计算 Object resultValue = calculationFormula(result, v.get(0), k, device); map.put(k, resultValue); }); return map; } /** * @param data * @param dataConfig * @return */ private static Map analysisMdb(String data, Map> dataConfig, Device device) { JSONObject jsonObject = JSON.parseObject(data); Map map = new HashMap<>(); if (jsonObject.isEmpty()) { return map; } JSONArray dataList = JSONArray.parseArray(jsonObject.get("data").toString()); dataConfig.forEach((k, v) -> { DataConfig configVo = v.get(0); AtomicInteger numberOfDataEntries = new AtomicInteger(); List list = new ArrayList<>(); for (int config = 0; config < v.size(); config++) { String refery = getRefer(v.get(config).getRefery()); for (int i = 0; i < dataList.size(); i++) { JSONObject jsonObject1 = JSON.parseObject(dataList.get(i).toString()); Object o = jsonObject1.get(refery); if (ObjectUtils.isNotEmpty(o)) { numberOfDataEntries.addAndGet(1); list.add(o); } } } // 拼接数采配置 List result = new ArrayList<>(); for (int i = 0; i < numberOfDataEntries.get(); i++) { String aggregate = ""; for (int j = 0; j < v.size(); j++) { int index; if (j == 0) { index = i; } else { index = numberOfDataEntries.get() + i; } aggregate += list.get(index).toString() + ","; } int lastIndex = aggregate.lastIndexOf(","); String substring = aggregate.substring(0, lastIndex); result.add(substring); } // 进行公式计算 Object resultValue = calculationFormula(result, v.get(0), k, device); map.put(k, resultValue); }); return map; } /** * 拉力机数采 * * @param data * @param dataConfig * @return */ private static Map analysisMdbByPull(String data, Map> dataConfig, Device device, String cableTag) { JSONObject jsonObject = JSON.parseObject(data); Map map = new HashMap<>(); if (jsonObject.isEmpty()) { return map; } JSONArray dataList = JSONArray.parseArray(jsonObject.get("data").toString()); dataConfig.forEach((k, v) -> { List> resultValue = new ArrayList<>(); DataConfig configVo = v.get(0); for (int i = 0; i < dataList.size(); i++) { JSONObject jsonObject1 = JSON.parseObject(dataList.get(i).toString()); // 获取时间 String dDate = jsonObject1.getString("dDate"); String dTime = jsonObject1.getString("dTime"); DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSSSSS"); // 解析第一个日期时间字符串 LocalDateTime dateTime1 = LocalDateTime.parse(dDate, formatter); // 获取年月日 String yearMonthDay = dateTime1.toLocalDate().toString(); // 解析第二个日期时间字符串 LocalDateTime dateTime2 = LocalDateTime.parse(dTime, formatter); // 获取时分 String hourMinute = dateTime2.toLocalTime().toString(); // 拼接年月日和时分 String mergedDateTime = yearMonthDay + " " + hourMinute; // 获取厚度 String report = jsonObject1.getString("Report"); String thickness = extractValue(report, "厚度:\\s*(\\S*?)~"); // 获取拉伸强度伸长率 String result = ""; if (StringUtils.isNotBlank(configVo.getMatchingName()) && configVo.getMatchingName().contains("拉伸强度")) { result = extractValue(report, "拉伸强度:\\s*(\\S*?)~"); } //判断检验子项是否是拉伸强度 if (StringUtils.isNotBlank(configVo.getMatchingName()) && configVo.getMatchingName().contains("伸长率")) { result = extractValue(report, "伸长率:\\s*(\\S*?)~"); } Map reportMap = new HashMap<>(); reportMap.put("mergedDateTime", mergedDateTime); reportMap.put("thickness", thickness); reportMap.put("result", result); resultValue.add(reportMap); } Map hashMap = new HashMap<>(); hashMap.put("equipName", device.getDeviceName()); hashMap.put("equipValue", device.getManagementNumber()); hashMap.put("result", resultValue); map.put(k, hashMap); }); return map; } private static String extractValue(String input, String regex) { Pattern pattern = Pattern.compile(regex); Matcher matcher = pattern.matcher(input); return matcher.find() ? matcher.group(1) : null; } private static Pattern SPATTERN = Pattern.compile("([-+])?\\d+(\\.\\d+)?"); /** * 只需X轴 * * @param data 采集到的文件字符串 * @param dataConfig 用户配置好的x,y轴定位数据与参照物 * @return */ private static Map readPngString(String data, Map> dataConfig, Device device) { Map map = new HashMap<>(); dataConfig.forEach((k, v) -> { List list = new ArrayList<>(); for (int config = 0; config < v.size(); config++) { String referx = getRefer(v.get(config).getReferx()); String result = null; // 通过\n将字符串分割为行 String[] aColumnY = data.split("\n"); List list1 = new ArrayList<>(); // 该循环得出用户配置的y轴 for (int i = 0; i < aColumnY.length; i++) { String addDataWithSpaces = referx.replaceAll("", " "); int x = getXOrY(v.get(config).getX(), k, "X"); if (aColumnY[i].contains(addDataWithSpaces)) { Matcher matcher = SPATTERN.matcher(aColumnY[i]); while (matcher.find()) { String group = matcher.group(); list1.add(group); } } if (ObjectUtils.isNotEmpty(list1)) { result = list1.get(x); } } if (ObjectUtils.isNotEmpty(result)) { list.add(result); } } // 进行公式计算 Object resultValue = calculationFormula(list, v.get(0), k, device); map.put(k, resultValue); }); return map; } /** * 从文件中提取出来的文字,如果有公式,进行公式计算,否则取列表第一个值 * * @param list 提取出的数字 * @param dataConfig 存储公式的对象 * @return */ private static Object calculationFormula(List list, DataConfig dataConfig, String insProductItem, Device device) { if (list.size() == 0) { Map hashMap = new HashMap<>(); hashMap.put("equipName", device.getDeviceName()); hashMap.put("equipValue", device.getManagementNumber()); hashMap.put("result", null); return hashMap; } ArrayList listResult = new ArrayList<>(); Map hashMap = new HashMap<>(); // 如果不为空,进行公式计算 if (ObjectUtils.isNotEmpty(dataConfig.getFormula()) && !"(null)".equals(dataConfig.getFormula())) { // 否则:没有公式代表不需要计算,直接提取List里面的数据 if (ObjectUtils.isEmpty(device.getEntrustCode()) && ObjectUtils.isEmpty(device.getSampleCode())) { String s = calculationFormulaList(list, dataConfig.getFormula()); listResult.add(s); } else { list.forEach(i -> { List strings = Arrays.asList(i.toString().split(",")); String s = calculationFormulaList(strings, dataConfig.getFormula()); listResult.add(s); }); } } else { listResult.addAll(list); } // 替换 "n.a." 为空字符串 for (int i = 0; i < listResult.size(); i++) { if ("n.a.".equals(listResult.get(i))) { listResult.set(i, ""); } } // 为了给前端做数据区分 if (listResult.size() > 1) { hashMap.put("result", listResult); } else { hashMap.put("result", listResult.get(0).toString()); } hashMap.put("equipName", device.getDeviceName()); hashMap.put("equipValue", device.getManagementNumber()); return hashMap; } /** * 解析String数据 * * @param data 采集到的文件字符串 * @param dataConfig 用户配置好的x,y轴定位数据与参照物 * @return */ private static Map analysisTxt(String data, Map> dataConfig, Device device, String entrustCode, String sampleCode) { Map map = new HashMap<>(); dataConfig.forEach((k, v) -> { List list = new ArrayList<>(); // 委托编号与样品编号不存在,定:1、Y定范围,X定横坐标;2、只存在Y;3、只存在X if (ObjectUtils.isEmpty(device.getEntrustCode()) && ObjectUtils.isEmpty(device.getSampleCode())) { // 判断是否是烟密度 if (device.getManagementNumber().equals("JCZX-ZB-ZT03002")) { // 按行分割数据 String[] lines = data.split("\n"); // 提取最后一行的第一个数字 String lastLine = lines[lines.length - 1]; String firstNumber = lastLine.split("\t")[0]; list.add(firstNumber); } else { list = analyzeData(data, v, k, ","); } // 委托编号与样品编号存在 } else if (ObjectUtils.isNotEmpty(device.getEntrustCode()) && ObjectUtils.isNotEmpty(device.getSampleCode())) { list = analyzeDataEntrustCodAndSampleCode(data, v, k, ",", device, entrustCode, sampleCode); } // 进行公式计算 Object resultValue = calculationFormula(list, v.get(0), k, device); map.put(k, resultValue); }); return map; } /** * @param data 采集到的文件字符串 * @param dataConfig 用户配置好的x,y轴定位数据与参照物 * @return */ private static Map analysisString(String data, Map> dataConfig, Device device, String entrustCode, String sampleCode) { String processingDataAfterSpaces = data .replaceAll(" +", splitIdentifier) .replaceAll("\r", "") .replaceAll(" ", ""); Map map = new HashMap<>(); dataConfig.forEach((k, v) -> { List list = new ArrayList<>(); // 委托编号与样品编号不存在,定:1、Y定范围,X定横坐标;2、只存在Y;3、只存在X if (ObjectUtils.isEmpty(device.getEntrustCode()) && ObjectUtils.isEmpty(device.getSampleCode())) { list = analyzeData(processingDataAfterSpaces, v, k, splitIdentifier); // 委托编号与样品编号存在 } else if (ObjectUtils.isNotEmpty(device.getEntrustCode()) && ObjectUtils.isNotEmpty(device.getSampleCode())) { list = analyzeDataEntrustCodAndSampleCode(processingDataAfterSpaces, v, k, splitIdentifier, device, entrustCode, sampleCode); } // 进行公式计算 Object resultValue = calculationFormula(list, v.get(0), k, device); map.put(k, resultValue); }); return map; } /** * 取X,Y两个定位 * * @param data 采集到的文件字符串 * @param dataConfig 用户配置好的x,y轴定位数据与参照物 * @return */ public static Map analysisList(String data, Map> dataConfig, Device device, String entrustCode, String sampleCode) { Map map = new HashMap<>(); List rohsIndicators = Arrays.asList("成束释放,SPR", "成束释放,HRR", "成束释放,THR", "成束释放,TSP", "成束释放,FIGRA"); dataConfig.forEach((k, v) -> { List list = new ArrayList<>(); // // 委托编号与样品编号不存在,定:1、Y定范围,X定横坐标;2、只存在Y;3、只存在X // if (ObjectUtils.isEmpty(device.getEntrustCode()) && ObjectUtils.isEmpty(device.getSampleCode())) { // list = analyzeXlsData(data, v, k, splitIdentifier); // // 委托编号与样品编号存在 // } else if (ObjectUtils.isNotEmpty(device.getEntrustCode()) && ObjectUtils.isNotEmpty(device.getSampleCode())) { // list = analyzeDataEntrustCodAndSampleCode(data, v, k, splitIdentifier, device, entrustCode, sampleCode); // } boolean containsRoHS = false; for (DataConfig item : v) { if (rohsIndicators.contains(item.getInsProductItem())) { containsRoHS = true; break; } } if (containsRoHS) { list = analyzeXlsxData(data, v, k, splitIdentifier); } else { list = analyzeXlsx1Data(data, v, k, splitIdentifier); } // 进行公式计算 Object resultValue = calculationFormula(list, v.get(0), k, device); map.put(k, resultValue); }); return map; } private static List analyzeXlsx1Data(String data, List v, String k, String splitIdentifier) { List finalResult = new ArrayList<>(); if (data == null || data.isEmpty()) { throw new IllegalArgumentException("输入的数据为空,请检查输入。"); } // 分割数据为行 String[] lines = data.replaceAll(" ", "").split("\n"); if (lines.length == 0) { throw new IllegalArgumentException("输入的数据没有有效的行,请检查输入。"); } // 获取表头 String[] headers = lines[0].split("\\s+"); if (headers.length == 0) { throw new IllegalArgumentException("表头数据为空,请检查输入格式。"); } // 查找测试时间列的索引 int testTimeIndex = -1; for (int i = 0; i < headers.length; i++) { if (headers[i].contains("测试时间")) { testTimeIndex = i; break; } } // 若未找到测试时间列,抛出异常 if (testTimeIndex == -1) { throw new IllegalArgumentException("未找到包含 '测试时间' 的列,请检查表头。"); } // 用于存储测试时间为 2400 的行数据 String[] targetLine = null; // 查找测试时间为 2400 的行 for (int i = 1; i < lines.length; i++) { try { String[] lineData = lines[i].split("\\s+"); double testTime = Double.parseDouble(lineData[testTimeIndex]); if (testTime == 2400) { targetLine = lineData; break; } } catch (NumberFormatException e) { throw new RuntimeException("第 " + (i + 1) + " 行的 '测试时间' 列数据无法转换为数字,请检查数据格式。", e); } catch (ArrayIndexOutOfBoundsException e) { throw new RuntimeException("第 " + (i + 1) + " 行的数据列数不足,无法获取 '测试时间' 列,请检查数据格式。", e); } } // 若未找到测试时间为 2400 的行,抛出异常 if (targetLine == null) { throw new RuntimeException("未找到测试时间为 2400 的数据,请检查数据内容。"); } for (DataConfig config : v) { String referx = getRefer(config.getReferx()); if (ObjectUtils.isEmpty(referx)) { continue; } // 查找 referx 列的索引 int referxIndex = -1; for (int i = 0; i < headers.length; i++) { if (headers[i].trim().contains(referx.trim())) { referxIndex = i; break; } } // 若未找到 referx 列,抛出异常 if (referxIndex == -1) { throw new IllegalArgumentException("未找到 '" + referx + "' 列,请检查表头和配置。"); } try { double value = Double.parseDouble(targetLine[referxIndex]); // 四舍五入保留两位小数 DecimalFormat df = new DecimalFormat("#.00"); String formattedValue = df.format(value); finalResult.add(Double.parseDouble(formattedValue)); } catch (NumberFormatException e) { throw new RuntimeException("'测试时间' 为 2400 的行中,'" + referx + "' 列的数据无法转换为数字,请检查数据格式。", e); } catch (ArrayIndexOutOfBoundsException e) { throw new RuntimeException("'测试时间' 为 2400 的行的数据列数不足,无法获取 '" + referx + "' 列,请检查数据格式。", e); } } return finalResult; } private static List analyzeXlsxData(String data, List v, String k, String splitIdentifier) { List finalResult = new ArrayList<>(); String ss = k; // 存储符合测试时间范围的数据行 List validLines = new ArrayList<>(); String[] lines = data.replaceAll(" ", "").split("\n"); String[] headers = lines[0].split("\\s+"); int testTimeIndex = -1; for (int i = 0; i < headers.length; i++) { if ("测试时间".equals(headers[i])) { testTimeIndex = i; break; } } if (testTimeIndex == -1) { return finalResult; } // 筛选出测试时间在 300 到 1500 之间的数据行 for (int i = 1; i < lines.length; i++) { String[] lineData = lines[i].split("\\s+"); try { double testTime = Double.parseDouble(lineData[testTimeIndex]); if (testTime > 300 && testTime <= 1500) { validLines.add(lineData); } else if (testTime > 1500) { break; } } catch (NumberFormatException e) { continue; } } for (DataConfig config : v) { String referx = getRefer(config.getReferx()); if (ObjectUtils.isEmpty(referx)) { continue; } int referxIndex = -1; for (int i = 0; i < headers.length; i++) { if (referx.equals(headers[i])) { referxIndex = i; break; } } if (referxIndex == -1) { continue; } List values = new ArrayList<>(); for (String[] lineData : validLines) { try { double value = Double.parseDouble(lineData[referxIndex]); values.add(value); } catch (NumberFormatException e) { continue; } } Object result; if ("HRR".equals(referx) || "SPR".equals(referx) || "FIGRA".equals(referx)) { result = values.stream().mapToDouble(Double::doubleValue).max().orElse(0); } else if ("THR".equals(referx) || "TSP".equals(referx)) { result = values.stream().mapToDouble(Double::doubleValue).sum(); } else { continue; } if (result != null) { // 四舍五入保留两位小数 DecimalFormat df = new DecimalFormat("#.00"); result = Double.parseDouble(df.format(result)); finalResult.add(result); } } return finalResult; } /** * 取X定位 * * @param data 采集到的文件字符串 * @param dataConfig 用户配置好的x定位数据与参照物 * @return */ public static Map analysisXlsList(String data, Map> dataConfig, Device device, String entrustCode, String sampleCode) { Map map = new HashMap<>(); dataConfig.forEach((k, v) -> { List list = new ArrayList<>(); // 委托编号与样品编号不存在,定:1、Y定范围,X定横坐标;2、只存在Y;3、只存在X if (ObjectUtils.isEmpty(device.getEntrustCode()) && ObjectUtils.isEmpty(device.getSampleCode())) { list = analyzeXlsData(data, v, k, splitIdentifier); // 委托编号与样品编号存在 } else if (ObjectUtils.isNotEmpty(device.getEntrustCode()) && ObjectUtils.isNotEmpty(device.getSampleCode())) { list = analyzeDataEntrustCodAndSampleCode(data, v, k, splitIdentifier, device, entrustCode, sampleCode); } // 进行公式计算 Object resultValue = calculationFormula(list, v.get(0), k, device); map.put(k, resultValue); }); return map; } private static List analyzeDataEntrustCodAndSampleCode(String data, List v, String k, String splitIdentifier, Device device, String entrustCodeValue, String sampleCodeValue) { entrustCodeValue = entrustCodeValue.replaceAll(" ", ""); sampleCodeValue = sampleCodeValue.replaceAll(" ", ""); // 最终结果 List list = new ArrayList<>(); int numberOfDataEntries = 0; // 取entrustCode与sampleCode所在位 for (int config = 0; config < v.size(); config++) { numberOfDataEntries = 0; Integer entrustCodeY = null; Integer sampleCodeY = null; Integer referYCoordinate = null; String refery = getRefer(v.get(config).getRefery()); String entrustCode = getRefer(device.getEntrustCode()); // 委托编号字段 String sampleCode = getRefer(device.getSampleCode()); // 样品编号字段 if (ObjectUtils.isEmpty(refery)) { continue; } // 去除所有的空格,通过\n将字符串分割为行 String[] aColumnY = data.replaceAll(" ", "").split("\n"); for (int i = 0; i < aColumnY.length; i++) { // 如果通过判断,定位到Y轴 if (aColumnY[i].contains(entrustCode) && aColumnY[i].contains(sampleCode)) { String[] aLine = aColumnY[i].split(splitIdentifier); for (int j = 0; j < aLine.length; j++) { if (aLine[j].contains(entrustCode)) { entrustCodeY = j; } if (aLine[j].contains(sampleCode)) { sampleCodeY = j; } if (aLine[j].contains(refery)) { referYCoordinate = j; } } } if (ObjectUtils.isNotEmpty(entrustCodeY) && ObjectUtils.isNotEmpty(sampleCodeY) && ObjectUtils.isNotEmpty(referYCoordinate)) { String[] aLine = aColumnY[i].split(splitIdentifier); try { if (aLine[entrustCodeY].contains(entrustCodeValue) && aLine[sampleCodeY].contains(sampleCodeValue)) { String result = aLine[referYCoordinate]; // 防止计算公式的时候出现:[null] 这种数据 if (ObjectUtils.isNotEmpty(result)) { numberOfDataEntries += 1; list.add(result); } } } catch (Exception e) { } } } } // 拼接数采配置 List result = new ArrayList<>(); for (int i = 0; i < numberOfDataEntries; i++) { String aggregate = ""; for (int j = 0; j < v.size(); j++) { int index; if (j == 0) { index = i; } else { index = numberOfDataEntries + i; } aggregate += list.get(index).toString() + ","; } int lastIndex = aggregate.lastIndexOf(","); String substring = aggregate.substring(0, lastIndex); result.add(substring); } return result; } // 由于在方法中会大量的判断,所以做一个方法 private static int getXOrY(String value, String k, String tips) { try { return Integer.parseInt(value); } catch (NumberFormatException e) { throw new ErrorException(k + ":未配置" + tips + "坐标轴的值!"); } } // 防止参照物为空报错,进行判断如果为空赋值空字符 private static String getRefer(String refer) { return ObjectUtils.isNotEmpty(refer) ? refer.replaceAll(" ", "") : ""; } /** * 委托编号与样品编号都为空执行 * * @param data * @param v * @param k * @param split * @return */ public static List analyzeData(String data, List v, String k, String split) { List list = new ArrayList<>(); for (int config = 0; config < v.size(); config++) { // 取两个用户配置的参照物 String referx = getRefer(v.get(config).getReferx()); String refery = getRefer(v.get(config).getRefery()); if (ObjectUtils.isEmpty(refery) && ObjectUtils.isEmpty(referx)) { continue; } // 最终结果 List result = new ArrayList<>(); // 通过\n将字符串分割为行 String[] aColumnY = data.replaceAll(" ", "").split("\n"); Integer end = null; // 采集数据:Y轴 for (int i = 0; i < aColumnY.length; i++) { // 如果Y参照不为空与X参照为空则执行,同时该行包含Y参照 if (ObjectUtils.isNotEmpty(refery) && ObjectUtils.isEmpty(referx) && aColumnY[i].contains(refery)) { // 取Y坐标值 int y = getXOrY(v.get(config).getY(), k, "Y"); String[] aLineX = aColumnY[i].split(split); for (int j = 0; j < aLineX.length; j++) { if (aLineX[j].contains(refery)) { String[] split1 = new String[0]; try { split1 = aColumnY[i + y].split(split); } catch (Exception e) { throw new ErrorException(k + ":Y轴定位超出!"); } try { result.add(split1[j]); } catch (Exception e) { throw new ErrorException(k + ":X轴定位超出!"); } } } // 如果Y参照不为空与X参照不为空则执行,此处Y定区域 } else if (ObjectUtils.isNotEmpty(refery) && ObjectUtils.isNotEmpty(referx)) { // 取x的值,防止报错 int x = getXOrY(v.get(config).getX(), k, "X"); // 取Y坐标值 int y = getXOrY(v.get(config).getY(), k, "Y"); // 缓存Y的结束值 if (ObjectUtils.isEmpty(end) && aColumnY[i].contains(refery)) { end = i + y; } // 判断是否在参照物为起到,Y坐标值为最终范围 if (ObjectUtils.isNotEmpty(end) && i <= end) { String[] aLineX = aColumnY[i].split(split); for (int j = 0; j < aLineX.length; j++) { if (aLineX[j].contains(referx)) { try { result.add(aLineX[j + x]); } catch (Exception e) { throw new ErrorException(k + ":X轴定位超出!"); } break; } } } // 如果X参照不为空同时该行包含X参照,则执行下面的代码 } else if (aColumnY[i].contains(referx) && ObjectUtils.isEmpty(refery)) { String[] aLineX = aColumnY[i].split(split); // 取x的值,防止报错 int x = getXOrY(v.get(config).getX(), k, "X"); for (int j = 0; j < aLineX.length; j++) { if (aLineX[j].contains(referx)) { try { result.add(aLineX[j + x]); } catch (Exception e) { throw new ErrorException(k + ":X轴定位超出!"); } } } } } // 防止计算公式的时候出现:[null] 这种数据 if (ObjectUtils.isNotEmpty(result)) { // String formatProcessing = getFormatProcessing(result); list.addAll(result); } } return list; } public static List analyzeXlsData(String data, List v, String k, String split) { List finalResult = new ArrayList<>(); // 最终返回的结果列表 for (DataConfig config : v) { // 取用户配置的参照物 String referx = getRefer(config.getReferx()); if (ObjectUtils.isEmpty(referx)) { continue; } // 第一步:解析数据,找到符合条件的行(第二个分隔符前和冒号后为数字) List validLines = new ArrayList<>(); String[] lines = data.replaceAll(" ", "").split("\n"); for (String line : lines) { String[] parts = line.split(split); if (parts.length > 1) { // 检查第二个分隔符前面和冒号后面是否为数字 String beforeSplit = parts[1]; String afterColon = ""; if (beforeSplit.contains(":")) { String[] colonParts = beforeSplit.split(":"); if (colonParts.length > 1) { afterColon = colonParts[1].trim(); } } try { Double.parseDouble(afterColon); // 冒号后是否为数字 validLines.add(line); // 如果符合条件,添加到临时列表中 } catch (NumberFormatException e) { // 不符合数字条件,忽略 } } } // 第二步:从临时列表中提取包含 referx 的行,并获取其后面的数字值 List result = new ArrayList<>(); for (String line : validLines) { if (line.contains(referx)) { String[] parts = line.split(referx); if (parts.length > 1) { String xValue = parts[1].trim(); // 提取 referx 后面到下一个分隔符之间的数字 String[] splitParts = xValue.split(split); if (splitParts.length > 0) { try { String trim = splitParts[0].trim(); if (trim.contains(":")) { trim = trim.replaceAll(":", ""); } Double.parseDouble(trim); // 判断是否为数字 result.add(trim); } catch (NumberFormatException e) { // 不是数字,忽略 } } } } } // 防止计算公式的时候出现:[null] 这种数据 if (!result.isEmpty()) { finalResult.addAll(result); } } return finalResult; } public static String getFormatProcessing(String value) { value = value.replaceAll("%", ""); if (value.contains("=")) { String[] split = value.split("="); return split[split.length - 1]; } else if (value.contains(":")) { String[] split = value.split(":"); return split[split.length - 1]; } else { return value; } } public static String getIp(HttpServletRequest request) { String ipAddress = request.getRemoteAddr(); // 防止回环地址变为IPv6 return ipAddress.equals("0:0:0:0:0:0:0:1") ? "127.0.0.1" : ipAddress; } public static String calculationFormulaList(List list, String formula) { //首先将list转换为bigdecmic List bigDecimalList = list.stream() .map(obj -> { return new BigDecimal((obj).toString()); }).collect(Collectors.toList()); //将中文的(转换英文的()) formula = formula.replace("(", "(") .replace(")", ")") .replace(",", ","); //然后提取公式 String strs = formula.substring(0, formula.indexOf("(")); String upperStr = strs.toUpperCase(); if (upperStr.matches(".*\\d.*")) { upperStr = ""; } //然后获取最外面括号里面的值,再根据","分割 int start = formula.indexOf("("); int end = -1; int a = 0; for (int i = start; i < formula.length(); i++) { char c = formula.charAt(i); if (c == '(') { a++; } else if (c == ')') { a--; if (a == 0) { end = i; } } } if (start == -1 || end == -1) { throw new ErrorException("公式括号不匹配: " + formula); } String argumentsStr = formula.substring(start + 1, end); List arguments = new ArrayList<>(); int bracketCount = 0; StringBuilder currentArgument = new StringBuilder(); for (char c : argumentsStr.toCharArray()) { if (c == ',' && bracketCount == 0) { arguments.add(currentArgument.toString()); currentArgument.setLength(0); } else { if (c == '(') bracketCount++; if (c == ')') bracketCount--; currentArgument.append(c); } } arguments.add(currentArgument.toString()); String[] bracketStrs = arguments.toArray(new String[0]); List results = new ArrayList<>(); for (String expr : bracketStrs) { Pattern pattern = Pattern.compile("([A-Z])(\\d+)"); Matcher matcher = pattern.matcher(expr); StringBuffer sb = new StringBuffer(); while (matcher.find()) { String letter = matcher.group(1); int index = Integer.parseInt(matcher.group(2)) - 1; // 将1-based转为0-based if (index < bigDecimalList.size()) { matcher.appendReplacement(sb, bigDecimalList.get(index).toString()); } else { throw new RuntimeException("公式中的下标 " + index + " 超出范围"); } } matcher.appendTail(sb); // 计算表达式 ScriptEngine engine = new ScriptEngineManager().getEngineByName("JavaScript"); try { Object result = engine.eval(sb.toString()); results.add(new BigDecimal(result.toString())); } catch (Exception e) { throw new IllegalArgumentException("无法计算公式: " + sb, e); } } // 根据函数名称进行相应计算 BigDecimal finalResult; if (upperStr.equals("") || upperStr == null) { finalResult = results.get(0); } else { switch (upperStr) { case "MAX": finalResult = results.stream().max(BigDecimal::compareTo) .orElseThrow(() -> new IllegalArgumentException("无法计算MAX值")); break; case "MIN": finalResult = results.stream().min(BigDecimal::compareTo) .orElseThrow(() -> new IllegalArgumentException("无法计算MIN值")); break; case "SUM": finalResult = results.stream().reduce(BigDecimal.ZERO, BigDecimal::add); break; case "ABS": finalResult = results.stream().map(BigDecimal::abs).reduce(BigDecimal.ZERO, BigDecimal::add); break; case "AVERAGE": finalResult = results.stream().reduce(BigDecimal.ZERO, BigDecimal::divide) .divide(BigDecimal.valueOf(results.size()), 2, BigDecimal.ROUND_HALF_UP); break; case "MEDIAN": int size = results.size(); if (size % 2 == 1) { finalResult = results.get(size / 2); } else { BigDecimal sum = results.get(size / 2 - 1).add(results.get(size / 2)); finalResult = sum.divide(BigDecimal.valueOf(2), 2, BigDecimal.ROUND_HALF_UP); } break; default: throw new UnsupportedOperationException("暂不支持函数: " + upperStr); } } return finalResult.toString(); // 否则:没有公式代表不需要计算,直接提取List里面的数据 } }