diff --git a/pom.xml b/pom.xml index a4d04f5..a482f68 100644 --- a/pom.xml +++ b/pom.xml @@ -55,6 +55,12 @@ org.springframework.boot spring-boot-starter-aop + + org.jetbrains + annotations + RELEASE + compile + diff --git a/src/main/java/com/dreamworks/boditech/MyApplication.java b/src/main/java/com/dreamworks/boditech/MyApplication.java new file mode 100644 index 0000000..76c96b8 --- /dev/null +++ b/src/main/java/com/dreamworks/boditech/MyApplication.java @@ -0,0 +1,228 @@ +package com.dreamworks.boditech; +import com.dreamworks.boditech.driver.Device; +import com.dreamworks.boditech.driver.actuator.ActAnalysisScanner; +import com.dreamworks.boditech.driver.actuator.ActMotor; +import com.dreamworks.boditech.driver.actuator.ActuatorModule; +import com.dreamworks.boditech.model.MdbIdChip; +import com.dreamworks.boditech.model.MyActiveRecord; +import com.dreamworks.boditech.utils.MyCommon; +import com.dreamworks.boditech.utils.MyOptAlgo; +import com.fasterxml.jackson.databind.JsonNode; +import jakarta.annotation.Resource; +import org.jetbrains.annotations.NotNull; +import org.springframework.beans.BeansException; +import org.springframework.boot.ApplicationArguments; +import org.springframework.boot.ApplicationRunner; +import org.springframework.context.ApplicationContext; +import org.springframework.context.ApplicationContextAware; +import org.springframework.core.annotation.Order; +import org.springframework.stereotype.Component; + +import java.nio.ByteOrder; +import java.util.*; + +@Order(1) +@Component +public class MyApplication implements ApplicationRunner, ApplicationContextAware { + // application context + private static ApplicationContext context; + + // get application context + public static ApplicationContext getContext() { + return MyApplication.context; + } + + // get bean + public static T getBean(Class beanClass) { + return context.getBean(beanClass); + } + + @Override + public void setApplicationContext(@NotNull ApplicationContext applicationContext) throws BeansException { + MyApplication.context = applicationContext; + } + + + @Resource + public Device device; + + @Override + public void run(ApplicationArguments args) throws Exception { + var analysisScanMotor = (ActMotor)device.getActuator(ActuatorModule.ANALYSIS_SCAN_MOTOR); + +// analysisScanMotor.reset(); + + this.analysis(); + } + + private void analysis() { + var scanner = (ActAnalysisScanner)device.getActuator(ActuatorModule.ANALYSIS_SCAN_SCANNER); + + var sampleType = "wb"; + var idChip = MyActiveRecord.findById(MdbIdChip.class, 2); + assert idChip != null; + + // 配置相机 + if (Objects.equals(idChip.scanType, MdbIdChip.SCAN_TYPE_F)) { // F 光学扫描 + scanner.setRegister(4200, 0); // 设置扫描类型 + scanner.setRegister(4201, 2860); // 设置扫描开始坐标 + scanner.setRegister(4202, -1); // 设置扫描方向 + scanner.setRegister(4203, 1); // 设置扫描间隔 + scanner.setRegister(4204, 1200); // 设置扫描点数 + scanner.setRegister(4205, 1); // 选通通道 + scanner.setRegister(4206, 0); // 激光增益 + scanner.setRegister(4207, 0); // 扫描器增益 + scanner.setRegister(40, 7); // 刷新激光器增益到mcp41xxx + scanner.setRegister(40, 8); // 刷新设置扫描器增益到mcp41xxx + scanner.setRegister(40, 1); // 选通通道 + } else if (Objects.equals(idChip.scanType, MdbIdChip.SCAN_TYPE_T)) { // T 光学扫描 + scanner.setRegister(4200, 1); // 设置扫描类型 + scanner.setRegister(4201, 4005); // 设置扫描开始坐标 + scanner.setRegister(4202, -1); // 设置扫描方向 + scanner.setRegister(4203, 1); // 设置扫描间隔 + scanner.setRegister(4204, 1200); // 设置扫描点数 + scanner.setRegister(4205, 1); // 选通通道 + scanner.setRegister(4206, 0); // 激光增益 + scanner.setRegister(4207, 0); // 扫描器增益 + scanner.setRegister(40, 7); // 刷新激光器增益到mcp41xxx + scanner.setRegister(40, 8); // 刷新设置扫描器增益到mcp41xxx + scanner.setRegister(40, 1); // 选通通道 + } else { + throw new RuntimeException("unknown scan type" + idChip.scanType); + } + + // 开始扫描 + scanner.start(); + MyCommon.easySleep(20000); // 延时10s @TODO : 这里要根据不同的扫描类型进行延时 + + // 读取扫描数据 + var buffer = scanner.readRaw(); + buffer.order(ByteOrder.LITTLE_ENDIAN); + float[] data = new float[buffer.capacity() / 2]; + for (int i = 0; i < data.length; i++) { + data[i] = buffer.getShort(); + } + + // 分析数据 + var algo = new MyOptAlgo(); + var result = algo.calculate(data, idChip.scanPeakCount); + + // 结算结果 + List values = new ArrayList<>(); + var projects = MyCommon.jsonToNode(idChip.items); + for ( var i=0; i< idChip.itemCount; i++ ) { + double valueX = 0; + JsonNode funcInfo = null; + + var project = projects.get(i); + var itemName = project.get("item").asText(); + var isPiecewiseFunction = project.get("isPiecewiseFunction").asBoolean(); + if ( !isPiecewiseFunction ) { // 非分段函数 + var func = project.get("nonPiecewiseFunction"); + var xValueSource = func.get("xValueSource").asInt(); + valueX = this.calculateRatio(result, xValueSource, idChip, itemName); + funcInfo = func.get("wb"); + if ("serum".equals(sampleType)) { + funcInfo = project.get("serum"); + } + } else { // 分段函数 + var func = project.get("piecewiseFunction"); + var piecewiseValueSrc = func.get("piecewiseValueSrc").asInt(); + var piecewiseValueSrcValue = this.calculateRatio(result, piecewiseValueSrc, idChip, itemName); + var piecewiseValue = func.get("piecewiseValue").asDouble(); + if ( piecewiseValueSrcValue > piecewiseValue ) { // 高浓度 + var highXValueSource = func.get("highXValueSource").asInt(); + valueX = this.calculateRatio(result, highXValueSource, idChip, itemName); + funcInfo = func.get("wbHigh"); + if ("serum".equals(sampleType)) { + funcInfo = project.get("serumHigh"); + } + } else { // 低浓度 + var lowXValueSource = func.get("lowXValueSource").asInt(); + valueX = this.calculateRatio(result, lowXValueSource, idChip, itemName); + funcInfo = func.get("wbLow"); + if ("serum".equals(sampleType)) { + funcInfo = project.get("serumLow"); + } + } + } + + double valueD = funcInfo.get("d").asDouble(); + double valueB = funcInfo.get("b").asDouble(); + double valueC = funcInfo.get("c").asDouble(); + double valueA = funcInfo.get("a").asDouble(); + double value = valueA * Math.pow(valueX,3) + valueB * Math.pow(valueX,2) + valueC * valueX + valueD; + values.add(value); + System.out.println("扫描完成 : " + value); + } + + System.out.println("扫描完成 : " + values.size()); + } + + /** + * @link https://iflytop1.feishu.cn/docx/UnRtdSG4qouMTaxzRb2cjiTTnZe + * @link https://iflytop1.feishu.cn/docx/A0CHdQL6OoTTCSx8MB7cQKEjnSc + * @param result + * @param xSource + * @return + */ + private double calculateRatio(MyOptAlgo.AlgoResult result, int xSource, MdbIdChip idChip, String itenName) { + // 计算 ratio + Map ratios = new HashMap<>(); + if ( 5 == idChip.scanPeakCount ) { + var peaks = result.peakInfos; + ratios.put("ratio", peaks[3].area / peaks[4].area); // T/C + ratios.put("antiRatio", peaks[3].area / peaks[4].area); // H/C + ratios.put("antiTestRatio", peaks[3].area / peaks[2].area); // T/H + ratios.put("rfRatio", peaks[1].area / peaks[4].area); // R/C + ratios.put("rtRatio", peaks[3].area / peaks[1].area); // T/R + ratios.put("t4Ratio", peaks[0].area / peaks[4].area); // T4/C + ratios.put("t4t3Ratio", peaks[1].area / peaks[0].area); // R/T4 + } else if ( 4 == idChip.scanPeakCount && Objects.equals(idChip.scanType, MdbIdChip.SCAN_TYPE_F) ) { + var peaks = result.peakInfos; // H T C + ratios.put("ratio", peaks[1].area / peaks[2].area); // T/C + ratios.put("antiRatio", peaks[0].area / peaks[2].area); // H/C + ratios.put("antiTestRatio", peaks[1].area / peaks[0].area); // T/H + ratios.put("rfRatio", peaks[0].area / peaks[2].area); // R/C @TODO : 待定, 三联卡F光学没有R + ratios.put("rtRatio", peaks[1].area / peaks[0].area); // T/R @TODO : 待定, 三联卡F光学没有R + } else if ( 4 == idChip.scanPeakCount && Objects.equals(idChip.scanType, MdbIdChip.SCAN_TYPE_T) ) { + var peaks = result.peakInfos; // R H T C + ratios.put("ratio", peaks[2].area / peaks[3].area); // T/C + ratios.put("antiRatio", peaks[1].area / peaks[3].area); // H/C + ratios.put("antiTestRatio", peaks[2].area / peaks[1].area); // T/H + ratios.put("rfRatio", peaks[0].area / peaks[3].area); // R/C + ratios.put("rtRatio", peaks[2].area / peaks[0].area); // T/R + } else if ( 3 == idChip.scanPeakCount ) { + var peaks = result.peakInfos; // H T C + ratios.put("ratio", peaks[1].area / peaks[2].area); // T/C + ratios.put("antiRatio", peaks[0].area / peaks[2].area); // H/C + ratios.put("antiTestRatio", peaks[1].area / peaks[0].area); // T/H + } else if ("PCT".equals(itenName)) { + var peaks = result.peakInfos; // C T + ratios.put("ratio", peaks[1].area / peaks[0].area); // T/C + } else if ( 2 == idChip.scanPeakCount ) { + var peaks = result.peakInfos; // T C + ratios.put("ratio", peaks[0].area / peaks[1].area); // T/C + } else { + throw new RuntimeException("unknown scan peak count" + idChip.scanPeakCount); + } + + // calculate ratio by x source + if ( 1 == xSource ) { + return ratios.get("ratio"); + } else if ( 2 == xSource ) { + return ratios.get("antiTestRatio"); + } else if ( 3 == xSource ) { + return ratios.get("antiRatio"); + } else if ( 4 == xSource ) { + return ratios.get("ratio") + ratios.get("antiTestRatio"); + } else if ( 5 == xSource ) { + // @TODO : 这里 R-ratio 不知道是啥 + throw new RuntimeException("不知道 R-Ratio 是啥"); + } else if ( 6 == xSource ) { + return ratios.get("t4Ratio"); + } else { + throw new RuntimeException("unknown x source" + xSource); + } + } +} diff --git a/src/main/java/com/dreamworks/boditech/driver/DeviceCommand.java b/src/main/java/com/dreamworks/boditech/driver/DeviceCommand.java index 104feb9..b4cb455 100644 --- a/src/main/java/com/dreamworks/boditech/driver/DeviceCommand.java +++ b/src/main/java/com/dreamworks/boditech/driver/DeviceCommand.java @@ -3,6 +3,7 @@ public class DeviceCommand { // command constants public static final Integer CMD_MODULE_STOP = 0x0101; public static final Integer CMD_MODULE_GET_STATUS = 0x0104; + public static final Integer CMD_MODULE_SET_REG = 0x0105; public static final Integer CMD_MODULE_GET_REG = 0x0106; public static final Integer CMD_MODULE_READ_IO = 0x0107; public static final Integer CMD_MODULE_READ_RAW = 0x0113; diff --git a/src/main/java/com/dreamworks/boditech/driver/actuator/ActuatorBase.java b/src/main/java/com/dreamworks/boditech/driver/actuator/ActuatorBase.java index d9e9d8d..1223929 100644 --- a/src/main/java/com/dreamworks/boditech/driver/actuator/ActuatorBase.java +++ b/src/main/java/com/dreamworks/boditech/driver/actuator/ActuatorBase.java @@ -6,6 +6,8 @@ import com.dreamworks.boditech.utils.MyCommon; import java.nio.ByteBuffer; import java.nio.ByteOrder; +import java.util.ArrayList; +import java.util.List; import java.util.Objects; public class ActuatorBase implements Actuator { // device @@ -40,7 +42,7 @@ public class ActuatorBase implements Actuator { } // wait for finish - protected void waitForFinish() { + public void waitForFinish() { Integer status = ActuatorModule.MODULE_STATUS_BUSY; do { try { @@ -87,6 +89,29 @@ public class ActuatorBase implements Actuator { return rawText.trim(); } + // read raw + // @TODO : 数据过大时,需要分批读取, 但是仍然会出现响应分批到达导致解析失败的问题 ~~~ + public ByteBuffer readRaw() { + Integer size = this.getRegister(10); + Integer count = this.getRegister(11); + List data = new ArrayList<>(); + + for ( int i = 0; i < count; i++ ) { + ByteBuffer response = this.call(DeviceCommand.CMD_MODULE_READ_RAW, i); + var bytes = response.array(); + for ( int j = 8; j < bytes.length; j++ ) { + data.add(bytes[j]); + } + MyCommon.easySleep(100); + } + + byte[] result = new byte[data.size()]; + for ( int i = 0; i < data.size(); i++ ) { + result[i] = data.get(i); + } + return ByteBuffer.wrap(result); + } + // get register public Integer getRegister( Integer index ) { ByteBuffer response = this.call(DeviceCommand.CMD_MODULE_GET_REG, index); @@ -94,6 +119,11 @@ public class ActuatorBase implements Actuator { return value; } + // set register + public void setRegister( Integer index, Integer value ) { + this.call(DeviceCommand.CMD_MODULE_SET_REG, index, value); + } + // get integer from response protected Integer getIntegerFromResponse(ByteBuffer response, Integer index ) { response.order(ByteOrder.LITTLE_ENDIAN); diff --git a/src/main/java/com/dreamworks/boditech/driver/task/step/StepAnalysis.java b/src/main/java/com/dreamworks/boditech/driver/task/step/StepAnalysis.java index 89611aa..6c69290 100644 --- a/src/main/java/com/dreamworks/boditech/driver/task/step/StepAnalysis.java +++ b/src/main/java/com/dreamworks/boditech/driver/task/step/StepAnalysis.java @@ -8,12 +8,13 @@ import com.dreamworks.boditech.driver.actuator.ActMotor; import com.dreamworks.boditech.driver.actuator.ActuatorModule; import com.dreamworks.boditech.driver.entity.IncubatorSlot; import com.dreamworks.boditech.driver.task.TaskTest; +import com.dreamworks.boditech.model.MdbIdChip; +import com.dreamworks.boditech.model.MyActiveRecord; import com.dreamworks.boditech.utils.MyCommon; - -import java.util.ArrayList; -import java.util.List; -import java.util.Map; - +import com.dreamworks.boditech.utils.MyOptAlgo; +import com.fasterxml.jackson.databind.JsonNode; +import java.nio.ByteOrder; +import java.util.*; public class StepAnalysis extends StepBase { // task test private TaskTest taskTest; @@ -29,23 +30,15 @@ public class StepAnalysis extends StepBase { ActAnalysisScanner scanner = (ActAnalysisScanner)device.getActuator(ActuatorModule.ANALYSIS_SCAN_SCANNER); // 01. 获取卡位 - IncubatorSlot incubatorSlot = this.taskTest.getIncubatorSlot(); - incubator.moveTo(incubatorSlot.getExitLocation()); +// IncubatorSlot incubatorSlot = this.taskTest.getIncubatorSlot(); +// incubator.moveTo(incubatorSlot.getExitLocation()); // 02. 推出测试卡 pushMotor.moveTo("analysisPushMotorScanStart"); pushMotor.moveTo("analysisPushMotorStandby"); - // 03. 扫描测试卡 - scanner.start(); - try { // @TODO : wait for scan finish - Thread.sleep(1000); - } catch (InterruptedException e) { - throw new RuntimeException(e); - } - scanner.stop(); - this.calculateScanResult(); - // @TODO : read raw from scanner and parse it later + // 03. 分析 + this.analysis(executor); // 04. 丢弃测试卡 scanMotor.moveTo("analysisScanMotorDropCard"); @@ -54,8 +47,8 @@ public class StepAnalysis extends StepBase { // @TODO : process raw data from scanner // unlock incubator slot - incubator.unlockSlot(incubatorSlot.getIndex()); - executor.incubatorSlotAvailable(); +// incubator.unlockSlot(incubatorSlot.getIndex()); +// executor.incubatorSlotAvailable(); } // calculate result @@ -65,4 +58,176 @@ public class StepAnalysis extends StepBase { results.add(Map.of("name", "HbA1c", "value", 5.6, "unit","g/ml")); this.taskTest.setTestResult(MyCommon.objectToJson(results)); } + + private void analysis(Executor executor) { + Device device = executor.getDevice(); + var scanner = (ActAnalysisScanner)device.getActuator(ActuatorModule.ANALYSIS_SCAN_SCANNER); + + var sampleType = "wb"; + var idChip = MyActiveRecord.findById(MdbIdChip.class, 2); + assert idChip != null; + + // 配置相机 + if (Objects.equals(idChip.scanType, MdbIdChip.SCAN_TYPE_F)) { // F 光学扫描 + scanner.setRegister(4200, 0); // 设置扫描类型 + scanner.setRegister(4201, 2860); // 设置扫描开始坐标 + scanner.setRegister(4202, -1); // 设置扫描方向 + scanner.setRegister(4203, 1); // 设置扫描间隔 + scanner.setRegister(4204, 1200); // 设置扫描点数 + scanner.setRegister(4205, 1); // 选通通道 + scanner.setRegister(4206, 0); // 激光增益 + scanner.setRegister(4207, 0); // 扫描器增益 + scanner.setRegister(40, 7); // 刷新激光器增益到mcp41xxx + scanner.setRegister(40, 8); // 刷新设置扫描器增益到mcp41xxx + scanner.setRegister(40, 1); // 选通通道 + } else if (Objects.equals(idChip.scanType, MdbIdChip.SCAN_TYPE_T)) { // T 光学扫描 + scanner.setRegister(4200, 1); // 设置扫描类型 + scanner.setRegister(4201, 4005); // 设置扫描开始坐标 + scanner.setRegister(4202, -1); // 设置扫描方向 + scanner.setRegister(4203, 1); // 设置扫描间隔 + scanner.setRegister(4204, 1200); // 设置扫描点数 + scanner.setRegister(4205, 1); // 选通通道 + scanner.setRegister(4206, 0); // 激光增益 + scanner.setRegister(4207, 0); // 扫描器增益 + scanner.setRegister(40, 7); // 刷新激光器增益到mcp41xxx + scanner.setRegister(40, 8); // 刷新设置扫描器增益到mcp41xxx + scanner.setRegister(40, 1); // 选通通道 + } else { + throw new RuntimeException("unknown scan type" + idChip.scanType); + } + + // 开始扫描 + scanner.start(); + MyCommon.easySleep(20000); // 延时10s @TODO : 这里要根据不同的扫描类型进行延时 + + // 读取扫描数据 + var buffer = scanner.readRaw(); + buffer.order(ByteOrder.LITTLE_ENDIAN); + float[] data = new float[buffer.capacity() / 2]; + for (int i = 0; i < data.length; i++) { + data[i] = buffer.getShort(); + } + + // 分析数据 + var algo = new MyOptAlgo(); + var result = algo.calculate(data, idChip.scanPeakCount); + + // 结算结果 + List values = new ArrayList<>(); + var projects = MyCommon.jsonToNode(idChip.items); + for ( var i=0; i< idChip.itemCount; i++ ) { + double valueX = 0; + JsonNode funcInfo = null; + + var project = projects.get(i); + var itemName = project.get("item").asText(); + var isPiecewiseFunction = project.get("isPiecewiseFunction").asBoolean(); + if ( !isPiecewiseFunction ) { // 非分段函数 + var func = project.get("nonPiecewiseFunction"); + var xValueSource = func.get("xValueSource").asInt(); + valueX = this.calculateRatio(result, xValueSource, idChip, itemName); + funcInfo = func.get("wb"); + if ("serum".equals(sampleType)) { + funcInfo = project.get("serum"); + } + } else { // 分段函数 + var func = project.get("piecewiseFunction"); + var piecewiseValueSrc = func.get("piecewiseValueSrc").asInt(); + var piecewiseValueSrcValue = this.calculateRatio(result, piecewiseValueSrc, idChip, itemName); + var piecewiseValue = func.get("piecewiseValue").asDouble(); + if ( piecewiseValueSrcValue > piecewiseValue ) { // 高浓度 + var highXValueSource = func.get("highXValueSource").asInt(); + valueX = this.calculateRatio(result, highXValueSource, idChip, itemName); + funcInfo = func.get("wbHigh"); + if ("serum".equals(sampleType)) { + funcInfo = project.get("serumHigh"); + } + } else { // 低浓度 + var lowXValueSource = func.get("lowXValueSource").asInt(); + valueX = this.calculateRatio(result, lowXValueSource, idChip, itemName); + funcInfo = func.get("wbLow"); + if ("serum".equals(sampleType)) { + funcInfo = project.get("serumLow"); + } + } + } + + double valueD = funcInfo.get("d").asDouble(); + double valueB = funcInfo.get("b").asDouble(); + double valueC = funcInfo.get("c").asDouble(); + double valueA = funcInfo.get("a").asDouble(); + double value = valueA * Math.pow(valueX,3) + valueB * Math.pow(valueX,2) + valueC * valueX + valueD; + values.add(value); + System.out.println("扫描完成 : " + value); + } + + System.out.println("扫描完成 : " + values.size()); + } + + /** + * @link https://iflytop1.feishu.cn/docx/UnRtdSG4qouMTaxzRb2cjiTTnZe + * @link https://iflytop1.feishu.cn/docx/A0CHdQL6OoTTCSx8MB7cQKEjnSc + * @param result + * @param xSource + * @return + */ + private double calculateRatio(MyOptAlgo.AlgoResult result, int xSource, MdbIdChip idChip, String itenName) { + // 计算 ratio + Map ratios = new HashMap<>(); + if ( 5 == idChip.scanPeakCount ) { + var peaks = result.peakInfos; + ratios.put("ratio", peaks[3].area / peaks[4].area); // T/C + ratios.put("antiRatio", peaks[3].area / peaks[4].area); // H/C + ratios.put("antiTestRatio", peaks[3].area / peaks[2].area); // T/H + ratios.put("rfRatio", peaks[1].area / peaks[4].area); // R/C + ratios.put("rtRatio", peaks[3].area / peaks[1].area); // T/R + ratios.put("t4Ratio", peaks[0].area / peaks[4].area); // T4/C + ratios.put("t4t3Ratio", peaks[1].area / peaks[0].area); // R/T4 + } else if ( 4 == idChip.scanPeakCount && Objects.equals(idChip.scanType, MdbIdChip.SCAN_TYPE_F) ) { + var peaks = result.peakInfos; // H T C + ratios.put("ratio", peaks[1].area / peaks[2].area); // T/C + ratios.put("antiRatio", peaks[0].area / peaks[2].area); // H/C + ratios.put("antiTestRatio", peaks[1].area / peaks[0].area); // T/H + ratios.put("rfRatio", peaks[0].area / peaks[2].area); // R/C @TODO : 待定, 三联卡F光学没有R + ratios.put("rtRatio", peaks[1].area / peaks[0].area); // T/R @TODO : 待定, 三联卡F光学没有R + } else if ( 4 == idChip.scanPeakCount && Objects.equals(idChip.scanType, MdbIdChip.SCAN_TYPE_T) ) { + var peaks = result.peakInfos; // R H T C + ratios.put("ratio", peaks[2].area / peaks[3].area); // T/C + ratios.put("antiRatio", peaks[1].area / peaks[3].area); // H/C + ratios.put("antiTestRatio", peaks[2].area / peaks[1].area); // T/H + ratios.put("rfRatio", peaks[0].area / peaks[3].area); // R/C + ratios.put("rtRatio", peaks[2].area / peaks[0].area); // T/R + } else if ( 3 == idChip.scanPeakCount ) { + var peaks = result.peakInfos; // H T C + ratios.put("ratio", peaks[1].area / peaks[2].area); // T/C + ratios.put("antiRatio", peaks[0].area / peaks[2].area); // H/C + ratios.put("antiTestRatio", peaks[1].area / peaks[0].area); // T/H + } else if ("PCT".equals(itenName)) { + var peaks = result.peakInfos; // C T + ratios.put("ratio", peaks[1].area / peaks[0].area); // T/C + } else if ( 2 == idChip.scanPeakCount ) { + var peaks = result.peakInfos; // T C + ratios.put("ratio", peaks[0].area / peaks[1].area); // T/C + } else { + throw new RuntimeException("unknown scan peak count" + idChip.scanPeakCount); + } + + // calculate ratio by x source + if ( 1 == xSource ) { + return ratios.get("ratio"); + } else if ( 2 == xSource ) { + return ratios.get("antiTestRatio"); + } else if ( 3 == xSource ) { + return ratios.get("antiRatio"); + } else if ( 4 == xSource ) { + return ratios.get("ratio") + ratios.get("antiTestRatio"); + } else if ( 5 == xSource ) { + // @TODO : 这里 R-ratio 不知道是啥 + throw new RuntimeException("不知道 R-Ratio 是啥"); + } else if ( 6 == xSource ) { + return ratios.get("t4Ratio"); + } else { + throw new RuntimeException("unknown x source" + xSource); + } + } } diff --git a/src/main/java/com/dreamworks/boditech/model/MdbIdChip.java b/src/main/java/com/dreamworks/boditech/model/MdbIdChip.java new file mode 100644 index 0000000..7e8809d --- /dev/null +++ b/src/main/java/com/dreamworks/boditech/model/MdbIdChip.java @@ -0,0 +1,39 @@ +package com.dreamworks.boditech.model; +public class MdbIdChip extends MyActiveRecord { + public static final Integer SCAN_TYPE_AUTO = 0; + public static final Integer SCAN_TYPE_F = 1; + public static final Integer SCAN_TYPE_T = 2; + public static final Integer SCAN_TYPE_F_T = 3; + + @MyActiveRecordField + public Long id; + + @MyActiveRecordField + public String item; + + @MyActiveRecordField + public Integer itemCount; + + @MyActiveRecordField + public String lotCode; + + @MyActiveRecordField + public Integer expiredDate; + + @MyActiveRecordField + public Integer version; + + @MyActiveRecordField + public Integer scanType; + + @MyActiveRecordField + public Integer scanPeakCount; + + @MyActiveRecordField + public String items; + + // get table name + public static String getTableName() { + return "bdt_id_chips"; + } +} diff --git a/src/main/java/com/dreamworks/boditech/model/MyActiveRecord.java b/src/main/java/com/dreamworks/boditech/model/MyActiveRecord.java new file mode 100644 index 0000000..2ee0576 --- /dev/null +++ b/src/main/java/com/dreamworks/boditech/model/MyActiveRecord.java @@ -0,0 +1,81 @@ +package com.dreamworks.boditech.model; +import com.dreamworks.boditech.MyApplication; + +import java.lang.reflect.Constructor; +import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.Map; +public class MyActiveRecord { + // find model by given id + public static T findById(Class clazz , int id ) { + var mapper = MyApplication.getBean(MyActiveRecordMapper.class); + var criteria = new MyActiveRecordCriteria(); + criteria.tableName = MyActiveRecord.getTableNameFromModelClass(clazz); + criteria.limit = 1; + criteria.whereIs("id", id); + var rows = mapper.find(criteria); + if (rows.isEmpty()) { + return null; + } + return MyActiveRecord.fillModel(clazz, rows.get(0)); + } + + // get table name from model class + private static String getTableNameFromModelClass(Class modelClass) { + Method tableNameGetter = null; + try { + tableNameGetter = modelClass.getMethod("getTableName"); + } catch (NoSuchMethodException e) { + throw new RuntimeException(e); + } + String tableName = null; + try { + tableName = (String)tableNameGetter.invoke(null); + } catch (IllegalAccessException | InvocationTargetException e) { + throw new RuntimeException(e); + } + return tableName; + } + + // fill model + private static T fillModel(Class modelClass, Map data) { + Constructor modelConstructor = null; + try { + modelConstructor = modelClass.getDeclaredConstructor(); + } catch (NoSuchMethodException e) { + throw new RuntimeException(e); + } + + T model = null; + try { + model = modelClass.cast(modelConstructor.newInstance()); + } catch (InvocationTargetException | InstantiationException | IllegalAccessException e) { + throw new RuntimeException(e); + } + + for ( Map.Entry entry : data.entrySet() ) { + String key = entry.getKey(); + Field field = null; + try { + field = model.getClass().getDeclaredField(key); + } catch (NoSuchFieldException e) { + continue ; + } + + Class fieldType = field.getType(); + if ( !fieldType.isAssignableFrom(entry.getValue().getClass()) ) { + throw new RuntimeException("Attribute type mismatch: " + key); + } + + field.setAccessible(true); + try { + field.set(model, entry.getValue()); + } catch (IllegalAccessException e) { + throw new RuntimeException(e); + } + } + + return model; + } +} diff --git a/src/main/java/com/dreamworks/boditech/model/MyActiveRecordCriteria.java b/src/main/java/com/dreamworks/boditech/model/MyActiveRecordCriteria.java new file mode 100644 index 0000000..086b73f --- /dev/null +++ b/src/main/java/com/dreamworks/boditech/model/MyActiveRecordCriteria.java @@ -0,0 +1,46 @@ +package com.dreamworks.boditech.model; +import java.util.ArrayList; +import java.util.List; +public class MyActiveRecordCriteria { + // condition + public static class Condition { + public String name; + public String operator; + public Object value; + } + + // table name + public String tableName; + // conditions + public List conditions; + // limit + public Integer limit; + // offset + public Integer offset; + + // where is + public void whereIs(String key, Object value) { + var condition = new Condition(); + condition.name = key; + condition.operator = "="; + condition.value = value; + this.appendCondition(condition); + } + + // where is not + public void whereIsNot(String key, Object value) { + var condition = new Condition(); + condition.name = key; + condition.operator = "!="; + condition.value = value; + this.appendCondition(condition); + } + + // append condition + private void appendCondition( Condition condition ) { + if ( this.conditions == null ) { + this.conditions = new ArrayList<>(); + } + this.conditions.add(condition); + } +} diff --git a/src/main/java/com/dreamworks/boditech/model/MyActiveRecordField.java b/src/main/java/com/dreamworks/boditech/model/MyActiveRecordField.java new file mode 100644 index 0000000..eff86bc --- /dev/null +++ b/src/main/java/com/dreamworks/boditech/model/MyActiveRecordField.java @@ -0,0 +1,10 @@ +package com.dreamworks.boditech.model; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +@Retention(RetentionPolicy.RUNTIME) +@Target({ElementType.FIELD}) +public @interface MyActiveRecordField { + +} \ No newline at end of file diff --git a/src/main/java/com/dreamworks/boditech/model/MyActiveRecordMapper.java b/src/main/java/com/dreamworks/boditech/model/MyActiveRecordMapper.java new file mode 100644 index 0000000..2920472 --- /dev/null +++ b/src/main/java/com/dreamworks/boditech/model/MyActiveRecordMapper.java @@ -0,0 +1,47 @@ +package com.dreamworks.boditech.model; +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Select; +import java.util.List; +import java.util.Map; +@Mapper +public interface MyActiveRecordMapper { + @Select( + "" + ) + List> find(MyActiveRecordCriteria criteria); + + void insert(); + void update(); + void delete(); + void findOne(); + void find(); + void batchUpdate(); + void batchDelete(); +} diff --git a/src/main/java/com/dreamworks/boditech/utils/MyCommon.java b/src/main/java/com/dreamworks/boditech/utils/MyCommon.java index 9ea245b..1cfd892 100644 --- a/src/main/java/com/dreamworks/boditech/utils/MyCommon.java +++ b/src/main/java/com/dreamworks/boditech/utils/MyCommon.java @@ -1,5 +1,6 @@ package com.dreamworks.boditech.utils; +import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import org.springframework.cglib.beans.BeanMap; @@ -47,4 +48,14 @@ public class MyCommon { throw new RuntimeException(e); } } + + // json to node + public static JsonNode jsonToNode(String json) { + ObjectMapper mapper = new ObjectMapper(); + try { + return mapper.readTree(json); + } catch (Exception e) { + throw new RuntimeException(e); + } + } } diff --git a/src/main/java/com/dreamworks/boditech/utils/MyOptAlgo$AlgoResult.class b/src/main/java/com/dreamworks/boditech/utils/MyOptAlgo$AlgoResult.class new file mode 100644 index 0000000..2315cd6 Binary files /dev/null and b/src/main/java/com/dreamworks/boditech/utils/MyOptAlgo$AlgoResult.class differ diff --git a/src/main/java/com/dreamworks/boditech/utils/MyOptAlgo.java b/src/main/java/com/dreamworks/boditech/utils/MyOptAlgo.java new file mode 100644 index 0000000..3216b5a --- /dev/null +++ b/src/main/java/com/dreamworks/boditech/utils/MyOptAlgo.java @@ -0,0 +1,24 @@ +package com.dreamworks.boditech.utils; +public class MyOptAlgo { + public static class AlgoResult { + public static class PeakInfo { + public boolean findPeak; + public float peakFullArea; + public float peakBaseLineArea; + public float area; + public int peakPos; + public int peakStartPos; + public int peakEndPos; + } + + public int peakNum; + public float[] lineAvg250; + public PeakInfo[] peakInfos; + } + + static { + System.load("D:/Sige5193/boditech-opt-algo-java-lib/x64/Debug/boditech-opt-algo-java-lib.dll"); + } + + public native AlgoResult calculate(float[] data, int peakCount); +}