package com.yuanchu.mom.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.yuanchu.mom.exception.ErrorException;
|
import com.yuanchu.mom.pojo.DataConfig;
|
import com.yuanchu.mom.pojo.Device;
|
|
import javax.script.ScriptEngine;
|
import javax.script.ScriptEngineManager;
|
import javax.servlet.http.HttpServletRequest;
|
import java.math.BigDecimal;
|
import java.util.*;
|
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<String, String> dataAcquisitionEntrance(List<DataConfig> dataConfig, Device device, String entrustCode, String sampleCode, String ip) {
|
/**
|
* filePath 文件采集路径
|
* fileExtension 文件后缀
|
* entrustCode 委托编号
|
* sampleCode 样品编号
|
* mdbEntrustCode mdb文件需要:委托编号字段
|
* mdbSampleCode mdb文件需要:样品编号字段
|
*/
|
String http = HTTP + ip + GETFILE +
|
"?filePath=" + device.getCollectUrl() +
|
"&fileExtension=" + device.getFileType() +
|
"&entrustCode=" + entrustCode +
|
"&sampleCode=" + sampleCode +
|
"&mdbEntrustCode=" + device.getMdbEntrustCode() +
|
"&mdbSampleCode=" + device.getMdbSampleCode();
|
String result = null;
|
try {
|
result = HttpUtil.get(http);
|
} catch (IORuntimeException e) {
|
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("未查询到数据,可能文件路径配置错误!");
|
} else {
|
System.out.println(jsonObject);
|
throw new ErrorException("未知错误,请联系管理员!");
|
}
|
} else {
|
String data = jsonObject.get("data").toString();
|
// 考虑到一个检测项可能会存在多个数采配置,所以需要进行分组
|
Map<String, List<DataConfig>> userMap = dataConfig.stream()
|
.peek(i -> i.setInsProductItem(
|
i.getInspectionItem().equals(i.getInspectionItemSubclass()) ? i.getInspectionItem() + "," : i.getInspectionItem() + "," + i.getInspectionItemSubclass()
|
))
|
.collect(Collectors.groupingBy(DataConfig::getInsProductItem));
|
Map<String, String> map;
|
switch (device.getFileType()) {
|
case ".docx":
|
map = analysisString(data, userMap);
|
break;
|
case ".xlsx":
|
map = analysisList(data, userMap);
|
break;
|
case ".txt":
|
map = analysisTxt(data, userMap);
|
break;
|
case ".csv":
|
map = analysisList(data, userMap);
|
break;
|
case ".mdb":
|
map = analysisMdb(data, userMap);
|
break;
|
case ".db":
|
map = analysisDb(data, userMap);
|
break;
|
case ".png":
|
map = readPngString(data, userMap);
|
break;
|
default:
|
map = null;
|
break;
|
}
|
// 如果存在存储地址,则移动地址
|
if (ObjectUtils.isNotEmpty(device.getStorageUrl())) {
|
String s = HTTP + ip + MOVEFILE + "?startFilePath=" + device.getCollectUrl() + "&endFilePath=" + device.getStorageUrl() + "&fileType=" + device.getFileType();
|
String storageUrlResult = HttpUtil.get(s);
|
JSONObject storageUrlResultJson = JSON.parseObject(storageUrlResult);
|
}
|
return map;
|
}
|
}
|
|
public static String createFrequency(String entrustCode, String sampleCode) {
|
String key = frequency + ":" + entrustCode + ":" + sampleCode;
|
boolean b = RedisUtil.hasKey(key);
|
String frequencyValue;
|
if (b) {
|
long incr = RedisUtil.incr(key, 1);
|
frequencyValue = String.valueOf(incr);
|
} else {
|
RedisUtil.set(key, 1);
|
frequencyValue = "1";
|
}
|
return frequencyValue;
|
}
|
|
/**
|
* 需要通过X,Y轴定位
|
*
|
* @param data
|
* @param dataConfig
|
* @return
|
*/
|
private static Map<String, String> analysisDb(String data, Map<String, List<DataConfig>> dataConfig) {
|
JSONObject jsonObject = JSON.parseObject(data);
|
Map<String, String> map = new HashMap<>();
|
if (jsonObject.isEmpty()) {
|
return map;
|
}
|
JSONArray dataList = JSONArray.parseArray(jsonObject.get("data").toString());
|
dataConfig.forEach((k, v) -> {
|
List<Object> list = new ArrayList<>();
|
for (int config = 0; config < v.size(); config++) {
|
String referx = getRefer(v.get(config).getReferx());
|
for (int i = 0; i < dataList.size(); i++) {
|
JSONObject jsonObject1 = JSON.parseObject(dataList.get(i).toString());
|
Object o = jsonObject1.get(referx);
|
if (ObjectUtils.isNotEmpty(o)) {
|
list.add(o);
|
}
|
}
|
}
|
// 进行公式计算
|
String resultValue = calculationFormula(list, v.get(0), k);
|
map.put(k, resultValue);
|
});
|
return map;
|
}
|
|
/**
|
* @param data
|
* @param dataConfig
|
* @return
|
*/
|
private static Map<String, String> analysisMdb(String data, Map<String, List<DataConfig>> dataConfig) {
|
JSONObject jsonObject = JSON.parseObject(data);
|
Map<String, String> map = new HashMap<>();
|
if (jsonObject.isEmpty()) {
|
return map;
|
}
|
JSONArray dataList = JSONArray.parseArray(jsonObject.get("data").toString());
|
dataConfig.forEach((k, v) -> {
|
List<Object> list = new ArrayList<>();
|
for (int config = 0; config < v.size(); config++) {
|
String referx = getRefer(v.get(config).getReferx());
|
for (int i = 0; i < dataList.size(); i++) {
|
JSONObject jsonObject1 = JSON.parseObject(dataList.get(i).toString());
|
Object o = jsonObject1.get(referx);
|
if (ObjectUtils.isNotEmpty(o)) {
|
list.add(o);
|
}
|
}
|
}
|
// 进行公式计算
|
String resultValue = calculationFormula(list, v.get(0), k);
|
map.put(k, resultValue);
|
});
|
return map;
|
}
|
|
private static Pattern SPATTERN = Pattern.compile("([-+])?\\d+(\\.\\d+)?");
|
|
/**
|
* 只需X轴
|
*
|
* @param data 采集到的文件字符串
|
* @param dataConfig 用户配置好的x,y轴定位数据与参照物
|
* @return
|
*/
|
private static Map<String, String> readPngString(String data, Map<String, List<DataConfig>> dataConfig) {
|
Map<String, String> map = new HashMap<>();
|
dataConfig.forEach((k, v) -> {
|
List<Object> 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<String> 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);
|
}
|
}
|
// 进行公式计算
|
String resultValue = calculationFormula(list, v.get(0), k);
|
map.put(k, resultValue);
|
});
|
return map;
|
}
|
|
/**
|
* 从文件中提取出来的文字,如果有公式,进行公式计算,否则取列表第一个值
|
*
|
* @param list 提取出的数字
|
* @param dataConfig 存储公式的对象
|
* @return
|
*/
|
private static String calculationFormula(List<Object> list, DataConfig dataConfig, String insProductItem) {
|
if (list.size() == 0) {
|
return null;
|
}
|
// 如果不为空,进行公式计算
|
if (ObjectUtils.isNotEmpty(dataConfig.getFormula())) {
|
String formula = dataConfig.getFormula();
|
//首先将list转换为bigdecmic
|
List<BigDecimal> bigDecimalList = list.stream()
|
.map(obj -> {
|
return new BigDecimal((obj).toString());
|
}).collect(Collectors.toList());
|
System.out.println(bigDecimalList);
|
|
//将中文的(转换英文的())
|
formula = formula.replace("(", "(")
|
.replace(")", ")")
|
.replace(",", ",");
|
//然后提取公式
|
String strs = formula.substring(0,formula.indexOf("("));
|
String upperStr = strs.toUpperCase();
|
if (upperStr.matches(".*\\d.*")){
|
upperStr="";
|
}
|
System.out.println(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<String> 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<BigDecimal> results = new ArrayList<>();
|
for (String expr : bracketStrs) {
|
System.out.println("替换前" + expr);
|
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);
|
System.out.println("替换后" + sb.toString());
|
|
// 计算表达式
|
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);
|
}
|
}
|
System.out.println(results);
|
System.out.println("计算结果: " + finalResult);
|
|
return finalResult.toString();
|
// 否则:没有公式代表不需要计算,直接提取List里面的数据
|
} else {
|
// 这里只会取列表第一个数据
|
if (list.size() > 1) {
|
throw new ErrorException("未给:" + insProductItem + " 配置公式,可是却采集到了" + list.size() + "个值!分别为:" + list);
|
} else {
|
return list.get(0).toString();
|
}
|
}
|
}
|
|
/**
|
* 解析String数据
|
*
|
* @param data 采集到的文件字符串
|
* @param dataConfig 用户配置好的x,y轴定位数据与参照物
|
* @return
|
*/
|
private static Map<String, String> analysisTxt(String data, Map<String, List<DataConfig>> dataConfig) {
|
Map<String, String> map = new HashMap<>();
|
dataConfig.forEach((k, v) -> {
|
List<Object> list = analyzeData(data, v, k, ",");
|
// 进行公式计算
|
String resultValue = calculationFormula(list, v.get(0), k);
|
map.put(k, resultValue);
|
});
|
return map;
|
}
|
|
/**
|
* @param data 采集到的文件字符串
|
* @param dataConfig 用户配置好的x,y轴定位数据与参照物
|
* @return
|
*/
|
private static Map<String, String> analysisString(String data, Map<String, List<DataConfig>> dataConfig) {
|
String processingDataAfterSpaces = data
|
.replaceAll(" +", splitIdentifier)
|
.replaceAll("\r", "")
|
.replaceAll(" ", "");
|
Map<String, String> map = new HashMap<>();
|
dataConfig.forEach((k, v) -> {
|
List<Object> list = analyzeData(processingDataAfterSpaces, v, k, splitIdentifier);
|
// 进行公式计算
|
String resultValue = calculationFormula(list, v.get(0), k);
|
map.put(k, resultValue);
|
});
|
return map;
|
}
|
|
/**
|
* 取X,Y两个定位
|
*
|
* @param data 采集到的文件字符串
|
* @param dataConfig 用户配置好的x,y轴定位数据与参照物
|
* @return
|
*/
|
public static Map<String, String> analysisList(String data, Map<String, List<DataConfig>> dataConfig) {
|
Map<String, String> map = new HashMap<>();
|
dataConfig.forEach((k, v) -> {
|
// 用户可能给一个子项目配置了多个配置,即一个项目取多个值,所以需要循环
|
List<Object> list = analyzeData(data, v, k, splitIdentifier);
|
// 进行公式计算
|
String resultValue = calculationFormula(list, v.get(0), k);
|
map.put(k, resultValue);
|
});
|
return map;
|
}
|
|
// 由于在方法中会大量的判断,所以做一个方法
|
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(" ", "") : "";
|
}
|
|
public static List<Object> analyzeData(String data, List<DataConfig> v, String k, String split) {
|
List<Object> 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 result = null;
|
// 通过\n将字符串分割为行
|
String[] aColumnY = data.replaceAll(" ", "").split("\n");
|
Integer end = null;
|
// 采集数据:Y轴
|
for (int i = 0; i < aColumnY.length; i++) {
|
// 取x的值,防止报错
|
int x = getXOrY(v.get(config).getX(), k, "X");
|
// 如果Y与X用户都配置了则执行
|
if (ObjectUtils.isNotEmpty(refery)) {
|
// 取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 = aLineX[j + x];
|
} catch (Exception e) {
|
throw new ErrorException(k + ":X轴定位超出!");
|
}
|
break;
|
}
|
}
|
}
|
// 如果只配置了X,则执行下面的代码
|
} else if (aColumnY[i].contains(referx) && ObjectUtils.isEmpty(refery)) {
|
String[] aLineX = aColumnY[i].split(split);
|
for (int j = 0; j < aLineX.length; j++) {
|
if (aLineX[j].contains(referx)) {
|
try {
|
result = aLineX[j + x];
|
} catch (Exception e) {
|
throw new ErrorException(k + ":X轴定位超出!");
|
}
|
}
|
}
|
}
|
}
|
// 防止计算公式的时候出现:[null] 这种数据
|
if (ObjectUtils.isNotEmpty(result)) {
|
String formatProcessing = getFormatProcessing(result);
|
list.add(formatProcessing);
|
}
|
}
|
return list;
|
}
|
|
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;
|
}
|
}
|