29 changed files with 634 additions and 200 deletions
-
12src/main/java/a8k/app/constant/OptConstant.java
-
29src/main/java/a8k/app/dao/AppStatePersistenceDao.java
-
7src/main/java/a8k/app/dao/type/db/OptCfg.java
-
1src/main/java/a8k/app/dao/type/db/OptRawScanData.java
-
25src/main/java/a8k/app/dao/type/db/ReactionReport.java
-
11src/main/java/a8k/app/factory/A8kOptProcessExceptionFactory.java
-
30src/main/java/a8k/app/i18n/Internationalization.java
-
269src/main/java/a8k/app/optalgo/A8kOptCurveAnalyzer.java
-
49src/main/java/a8k/app/optalgo/A8kPeakAnalyzer.java
-
106src/main/java/a8k/app/optalgo/algo/LinearRegressionCalculator.java
-
1src/main/java/a8k/app/optalgo/type/A8kOptProcessException.java
-
12src/main/java/a8k/app/optalgo/type/PeakPresetPosConfig.java
-
2src/main/java/a8k/app/optalgo/type/ReactionResultStatus.java
-
8src/main/java/a8k/app/service/data/ProjIdCardInfoMgrService.java
-
17src/main/java/a8k/app/service/data/ReactionRecordMgrService.java
-
47src/main/java/a8k/app/service/lowerctrl/OptScanModuleLowerCtrlService.java
-
2src/main/java/a8k/app/service/module/OptScanCtrlModule.java
-
18src/main/java/a8k/app/service/module/SamplePreProcessModule.java
-
11src/main/java/a8k/app/type/a8k/container/A8kOptPeakContainer.java
-
4src/main/java/a8k/app/type/a8k/opt/A8kOptPeak.java
-
1src/main/java/a8k/app/type/a8k/opt/PeakName.java
-
4src/main/java/a8k/app/type/a8k/optfn/A8kOptX.java
-
13src/main/java/a8k/app/type/a8k/state/SampleInfo.java
-
6src/main/java/a8k/extui/mgr/ExtApiPageGroupCfgMgr.java
-
70src/main/java/a8k/extui/page/optalgotest/OptAlgoTestPage.java
-
3src/main/java/a8k/extui/page/test/codetest/OptFormulaTestPageV2.java
-
76src/main/resources/db/zapp_a8k_project_opt_config.csv
-
BINtools/20250427-F-光学光学报告.xlsx
-
BINtools/20250427-T-光学光学报告-2.xlsx
@ -1,8 +1,12 @@ |
|||
package a8k.app.constant; |
|||
|
|||
public class OptConstant { |
|||
static public final Integer OPT_F_RESULT_TOLERATE = 300; |
|||
static public final Integer OPT_T_RESULT_TOLERATE = 700; |
|||
static public final Integer OPT_T_TARGET_VAL = 2300; |
|||
static public final Integer OPT_F_TARGET_VAL = 3600; |
|||
static public final Integer OPT_T_RESULT_TOLERATE = 700; |
|||
static public final Integer OPT_T_TARGET_VAL = 2300; |
|||
static public final Integer OPT_T_FINAL_TARGET_VAL = 3600; |
|||
|
|||
static public final Integer OPT_F_TARGET_VAL = 3600; |
|||
static public final Integer OPT_F_RESULT_TOLERATE = 300; |
|||
static public final Integer OPT_F_FINAL_TARGET_VAL = 3600; |
|||
static public final Integer OPT_F_OVERFLOW_VAL = 3900; |
|||
} |
@ -0,0 +1,29 @@ |
|||
package a8k.app.dao; |
|||
|
|||
import a8k.app.dao.type.db.KeyVal; |
|||
import a8k.app.utils.ZSqlite; |
|||
import jakarta.annotation.PostConstruct; |
|||
import jakarta.annotation.Resource; |
|||
import lombok.extern.slf4j.Slf4j; |
|||
import org.springframework.jdbc.core.JdbcTemplate; |
|||
import org.springframework.stereotype.Component; |
|||
|
|||
@Component |
|||
@Slf4j |
|||
public class AppStatePersistenceDao extends ZSqlite<KeyVal> { |
|||
@Resource |
|||
JdbcTemplate jdbcTemplate; |
|||
|
|||
@PostConstruct |
|||
void init() { |
|||
init(jdbcTemplate, "zapp_state_persistence_dao", KeyVal.class, false); |
|||
} |
|||
|
|||
void storage(String key, String val) { |
|||
if (key == null || val == null) { |
|||
log.error("key or val is null, key: {}, val: {}", key, val); |
|||
return; |
|||
} |
|||
} |
|||
|
|||
} |
@ -0,0 +1,11 @@ |
|||
package a8k.app.factory; |
|||
|
|||
import a8k.app.i18n.Internationalization; |
|||
import a8k.app.optalgo.type.A8kOptProcessException; |
|||
import a8k.app.optalgo.type.ReactionResultStatus; |
|||
|
|||
public class A8kOptProcessExceptionFactory { |
|||
static public A8kOptProcessException create(ReactionResultStatus status) { |
|||
return new A8kOptProcessException(status, Internationalization.reactionResultStatus2String(status)); |
|||
} |
|||
} |
@ -0,0 +1,106 @@ |
|||
package a8k.app.optalgo.algo; |
|||
|
|||
import lombok.Data; |
|||
|
|||
import java.util.List; |
|||
|
|||
public class LinearRegressionCalculator { |
|||
|
|||
|
|||
@Data |
|||
public static class Point2D { |
|||
public double x; |
|||
public double y; |
|||
|
|||
public Point2D(double x, double y) { |
|||
this.x = x; |
|||
this.y = y; |
|||
} |
|||
|
|||
} |
|||
|
|||
public static double[] calculateRegression(double[] points) { |
|||
List<Point2D> pointList = new java.util.ArrayList<>(); |
|||
for (int i = 0; i < points.length; i += 1) { |
|||
pointList.add(new Point2D(i, points[i])); |
|||
} |
|||
return calculateRegression(pointList); |
|||
} |
|||
|
|||
|
|||
|
|||
/** |
|||
* 计算线性回归和相关系数 |
|||
* @param points 数据点集合 |
|||
* @return 包含斜率、截距和相关系数的数组 [slope, intercept, r] |
|||
*/ |
|||
public static double[] calculateRegression(List<Point2D> points) { |
|||
int n = points.size(); |
|||
if (n < 2) { |
|||
throw new IllegalArgumentException("至少需要两个点进行计算"); |
|||
} |
|||
|
|||
// 计算x和y的总和 |
|||
double sumX = 0, sumY = 0; |
|||
for (Point2D p : points) { |
|||
sumX += p.getX(); |
|||
sumY += p.getY(); |
|||
} |
|||
|
|||
// 计算x和y的平均值 |
|||
double meanX = sumX / n; |
|||
double meanY = sumY / n; |
|||
|
|||
// 计算所需的和 |
|||
double sumXY = 0, sumXSq = 0, sumYSq = 0; |
|||
for (Point2D p : points) { |
|||
double xDev = p.getX() - meanX; |
|||
double yDev = p.getY() - meanY; |
|||
sumXY += xDev * yDev; |
|||
sumXSq += xDev * xDev; |
|||
sumYSq += yDev * yDev; |
|||
} |
|||
|
|||
// 计算斜率 (m) 和截距 (b) |
|||
double slope = sumXY / sumXSq; |
|||
double intercept = meanY - slope * meanX; |
|||
|
|||
// 计算相关系数 r |
|||
double r = sumXY / Math.sqrt(sumXSq * sumYSq); |
|||
|
|||
return new double[]{slope, intercept, r}; |
|||
} |
|||
|
|||
public static void main(String[] args) { |
|||
// 示例数据点 |
|||
List<Point2D> points = List.of( |
|||
new Point2D(1, 2), |
|||
new Point2D(2, 3), |
|||
new Point2D(3, 5), |
|||
new Point2D(4, 4), |
|||
new Point2D(5, 6) |
|||
); |
|||
|
|||
// 计算线性回归和相关系数 |
|||
double[] result = calculateRegression(points); |
|||
double slope = result[0]; |
|||
double intercept = result[1]; |
|||
double r = result[2]; |
|||
|
|||
// 输出结果 |
|||
System.out.println("回归方程: y = " + String.format("%.4f", slope) + "x + " + String.format("%.4f", intercept)); |
|||
System.out.println("相关系数 r = " + String.format("%.4f", r)); |
|||
System.out.println("决定系数 R² = " + String.format("%.4f", r * r)); |
|||
|
|||
// 解释相关系数 |
|||
String interpretation; |
|||
if (Math.abs(r) >= 0.8) { |
|||
interpretation = "强相关"; |
|||
} else if (Math.abs(r) >= 0.5) { |
|||
interpretation = "中度相关"; |
|||
} else { |
|||
interpretation = "弱相关"; |
|||
} |
|||
System.out.println("相关性: " + interpretation); |
|||
} |
|||
} |
@ -0,0 +1,12 @@ |
|||
package a8k.app.optalgo.type; |
|||
|
|||
import a8k.app.type.a8k.opt.PeakName; |
|||
|
|||
import java.util.List; |
|||
public class PeakPresetPosConfig { |
|||
public PeakName P040 = null; |
|||
public PeakName P080 = null; |
|||
public PeakName P120 = null; |
|||
public PeakName P160 = null; |
|||
public PeakName P200 = null; |
|||
} |
@ -0,0 +1,70 @@ |
|||
package a8k.extui.page.optalgotest; |
|||
|
|||
import a8k.app.dao.type.db.ProjExtInfoCard; |
|||
import a8k.app.dao.type.db.ReactionReport; |
|||
import a8k.app.hardware.type.A8kEcode; |
|||
import a8k.app.optalgo.A8kOptCurveAnalyzer; |
|||
|
|||
import a8k.app.optalgo.A8kPeakAnalyzer; |
|||
import a8k.app.optalgo.type.A8kOptPeakInfo; |
|||
import a8k.app.service.data.ProjInfoMgrService; |
|||
import a8k.app.service.data.ReactionRecordMgrService; |
|||
import a8k.app.type.a8k.proj.ProjInfo; |
|||
import a8k.app.type.exception.AppException; |
|||
import a8k.extui.mgr.ExtApiPageMgr; |
|||
import jakarta.annotation.PostConstruct; |
|||
import lombok.RequiredArgsConstructor; |
|||
import lombok.extern.slf4j.Slf4j; |
|||
import org.springframework.stereotype.Component; |
|||
|
|||
@Component |
|||
@RequiredArgsConstructor |
|||
@Slf4j |
|||
public class OptAlgoTestPage { |
|||
|
|||
private final ExtApiPageMgr extApiPageMgr; |
|||
private final ReactionRecordMgrService reactionRecordMgrService; |
|||
private final ProjInfoMgrService projInfoMgrService; |
|||
|
|||
public ReactionReport.ReactionResult testAlgo(Integer recordId, Integer subProjIndex) throws AppException { |
|||
ReactionReport record = reactionRecordMgrService.getRecordById(recordId); |
|||
if (record == null) { |
|||
log.error("Record with ID {} not found", recordId); |
|||
throw AppException.of(A8kEcode.CODEERROR, "RECORD NOT FOUND"); |
|||
} |
|||
|
|||
var buildInRecordInfo = projInfoMgrService.getProjBuildInInfo(record.projId); |
|||
|
|||
|
|||
A8kOptCurveAnalyzer a8KOptCurveAnalyzer = new A8kOptCurveAnalyzer(); |
|||
A8kOptPeakInfo analysResult = a8KOptCurveAnalyzer.analysisCurve( |
|||
record.projId, |
|||
buildInRecordInfo.optcfg.get(subProjIndex), |
|||
record.detailOptData.get(subProjIndex).rawData.rawData |
|||
); |
|||
|
|||
// ProjExtInfoCard projExtInfoCard = record.projExtInfoCard; |
|||
ProjExtInfoCard projExtInfoCard = null; |
|||
if (projExtInfoCard == null) { |
|||
projExtInfoCard = projInfoMgrService.getProjExtInfoCard(record.lotId); |
|||
} |
|||
if (projExtInfoCard.lotId == null || projExtInfoCard.lotId.isEmpty()) { |
|||
log.error("Project Ext Info Card lotId is empty for record ID {}", recordId); |
|||
throw AppException.of(A8kEcode.CODEERROR, "PROJECT EXT INFO CARD LOT ID IS EMPTY"); |
|||
} |
|||
|
|||
ProjInfo projInfo = new ProjInfo(projInfoMgrService.getProjBuildInInfo(record.projId), projExtInfoCard); |
|||
|
|||
|
|||
return A8kPeakAnalyzer.analysisPeakInfo(record.getSampleInfo(), projInfo, subProjIndex, analysResult); |
|||
} |
|||
|
|||
@PostConstruct |
|||
public void init() { |
|||
var page = extApiPageMgr.newPage(this); |
|||
page.addFunction("testAlgo", this::testAlgo) |
|||
.setParamVal("recordId", () -> 0) |
|||
.setParamVal("subProjIndex", () -> 0); |
|||
extApiPageMgr.addPage(page); |
|||
} |
|||
} |
@ -1,38 +1,38 @@ |
|||
id,projId,projName,subProjIndex,subProjName,subProjShortName,optType,optScanRange,optScanPeakNum,peakNameRefNum |
|||
0,1,hsCRP,0,hsCRP,CA,FOPT,150,3,3 |
|||
0,2,PCT,0,PCT,PC,TOPT,150,2,2 |
|||
0,3,TSH,0,TSH,TS,FOPT,150,2,2 |
|||
0,4,PRL,0,PRL,PL,FOPT,150,2,2 |
|||
0,5,T3,0,T3,T3,FOPT,150,2,2 |
|||
0,6,T4,0,T4,T4,FOPT,150,2,2 |
|||
0,7,Total β hCG,0,Total β hCG,HC,FOPT,150,3,3 |
|||
0,8,LH,0,LH,LH,FOPT,150,2,2 |
|||
0,9,FSH,0,FSH,FS,FOPT,150,2,2 |
|||
0,10,Progesterone,0,Progesterone,PG,FOPT,150,2,2 |
|||
0,12,Tn-I plus,0,Tn-I plus,TG,TOPT,150,3,3 |
|||
0,13,NT-proBNP,0,NT-proBNP,NB,TOPT,150,3,3 |
|||
0,14,CK-MB,0,CK-MB ,CK,FOPT,150,2,2 |
|||
0,15,Myoglobin,0,Myoglobin,MY,FOPT,150,2,2 |
|||
0,16,D-Dimer,0,D-Dimer,DD,FOPT,150,2,2 |
|||
0,17,HbAlC,0,HbAlC,HB,FOPT,150,2,2 |
|||
0,18,PCT plus,0,PCT plus,PP,FOPT,150,3,3 |
|||
0,20,Tn-I/CK-MB/Myoglobin,0,CK-MB,CK-MB,FOPT,200,3,4 |
|||
0,20,Tn-I/CK-MB/Myoglobin,1,Myoglobin,Myoglobin,FOPT,200,3,4 |
|||
0,20,Tn-I/CK-MB/Myoglobin,2,Tn-I,Tn-I,TOPT,200,4,4 |
|||
0,22,PCT/hsCRP,0,PCT,PCT,TOPT,200,3,4 |
|||
0,22,PCT/hsCRP,1,hsCRP,hsCRP,FOPT,200,3,4 |
|||
0,24,SAA,0,SAA,SA,FOPT,150,3,3 |
|||
0,25,AMH,0,AMH,AM,TOPT,150,3,3 |
|||
0,26,SAA/CRP,0,SAA,SAA,FOPT,250,5,5 |
|||
0,26,SAA/CRP,1,CRP,CRP,FOPT,250,5,5 |
|||
0,27,Vitamin D,0,Vitamin D,VD,FOPT,150,2,2 |
|||
0,33,ST2,0,ST2,ST,FOPT,150,2,2 |
|||
0,36,MxA,0,MxA,MX,TOPT,150,2,2 |
|||
0,48,IL-6,0,IL-6,IL,TOPT,150,3,3 |
|||
0,49,Gastrin 17,0,Gastrin 17,GA,TOPT,150,2,2 |
|||
0,50,Pepsinogen I/II,0,Pepsinogen I,PG-I,TOPT,150,3,3 |
|||
0,50,Pepsinogen I/II,1,Pepsinogen II,PG-II,TOPT,150,3,3 |
|||
0,52,NT-proBNP/ST2,0,ST2,ST2,FOPT,200,2,4 |
|||
0,52,NT-proBNP/ST2,1,NT-proBNP,NT-proBNP,TOPT,200,3,4 |
|||
0,54,Troponin T,0,Troponin T,TT,TOPT,150,3,3 |
|||
0,55,BNP,0,BNP,BP,TOPT,150,3,3 |
|||
id,projId,projName,subProjIndex,subProjName,subProjShortName,optType,optScanRange,optScanPeakNum,peakNameRefNum,peakConfig |
|||
0,1,hsCRP,0,hsCRP,CA,FOPT,150,3,3,"[H,T,C,N,N]" |
|||
0,2,PCT,0,PCT,PC,TOPT,150,2,2,"[N,C,T,N,N]" |
|||
0,3,TSH,0,TSH,TS,FOPT,150,2,2,"[N,T,C,N,N]" |
|||
0,4,PRL,0,PRL,PL,FOPT,150,2,2,"[N,T,C,N,N]" |
|||
0,5,T3,0,T3,T3,FOPT,150,2,2,"[N,T,C,N,N]" |
|||
0,6,T4,0,T4,T4,FOPT,150,2,2,"[N,T,C,N,N]" |
|||
0,7,Total β hCG,0,Total β hCG,HC,FOPT,150,3,3,"[H,T,C,N,N]" |
|||
0,8,LH,0,LH,LH,FOPT,150,2,2,"[N,T,C,N,N]" |
|||
0,9,FSH,0,FSH,FS,FOPT,150,2,2,"[N,T,C,N,N]" |
|||
0,10,Progesterone,0,Progesterone,PG,FOPT,150,2,2,"[N,T,C,N,N]" |
|||
0,12,Tn-I plus,0,Tn-I plus,TG,TOPT,150,3,3,"[H,T,C,N,N]" |
|||
0,13,NT-proBNP,0,NT-proBNP,NB,TOPT,150,3,3,"[H,T,C,N,N]" |
|||
0,14,CK-MB,0,CK-MB ,CK,FOPT,150,2,2,"[N,T,C,N,N]" |
|||
0,15,Myoglobin,0,Myoglobin,MY,FOPT,150,2,2,"[N,T,C,N,N]" |
|||
0,16,D-Dimer,0,D-Dimer,DD,FOPT,150,2,2,"[N,T,C,N,N]" |
|||
0,17,HbAlC,0,HbAlC,HB,FOPT,150,2,2,"[N,T,C,N,N]" |
|||
0,18,PCT plus,0,PCT plus,PP,FOPT,150,3,3,"[H,T,C,N,N]" |
|||
0,20,Tn-I/CK-MB/Myoglobin,0,CK-MB,CK-MB,FOPT,200,3,4,"[N,H,T,C,N]" |
|||
0,20,Tn-I/CK-MB/Myoglobin,1,Myoglobin,Myoglobin,FOPT,200,3,4,"[N,H,T,C,N]" |
|||
0,20,Tn-I/CK-MB/Myoglobin,2,Tn-I,Tn-I,TOPT,200,4,4,"[R,H,T,C,N]" |
|||
0,22,PCT/hsCRP,0,PCT,PCT,TOPT,200,3,4,"[N,H,T,C,N]" |
|||
0,22,PCT/hsCRP,1,hsCRP,hsCRP,FOPT,200,3,4,"[R,N,T,C,N]" |
|||
0,24,SAA,0,SAA,SA,FOPT,150,3,3,"[H,T,C,N,N]" |
|||
0,25,AMH,0,AMH,AM,TOPT,150,3,3,"[H,T,C,N,N]" |
|||
0,26,SAA/CRP,0,SAA,SAA,FOPT,250,5,5,"[T4,R,H,T,C]" |
|||
0,26,SAA/CRP,1,CRP,CRP,FOPT,250,5,5,"[T4,R,H,T,C]" |
|||
0,27,Vitamin D,0,Vitamin D,VD,FOPT,150,2,2,"[N,T,C,N,N]" |
|||
0,33,ST2,0,ST2,ST,FOPT,150,2,2,"[N,T,C,N,N]" |
|||
0,36,MxA,0,MxA,MX,TOPT,150,2,2,"[N,T,C,N,N]" |
|||
0,48,IL-6,0,IL-6,IL,TOPT,150,3,3,"[H,T,C,N,N]" |
|||
0,49,Gastrin 17,0,Gastrin 17,GA,TOPT,150,2,2,"[N,T,C,N,N]" |
|||
0,50,Pepsinogen I/II,0,Pepsinogen I,PG-I,TOPT,150,3,3,"[H,T,C,N,N]" |
|||
0,50,Pepsinogen I/II,1,Pepsinogen II,PG-II,TOPT,150,3,3,"[H,T,C,N,N]" |
|||
0,52,NT-proBNP/ST2,0,ST2,ST2,FOPT,200,2,4,"[N,N,T,C,N]" |
|||
0,52,NT-proBNP/ST2,1,NT-proBNP,NT-proBNP,TOPT,200,3,4,"[R,H,N,C,N]" |
|||
0,54,Troponin T,0,Troponin T,TT,TOPT,150,3,3,"[H,T,C,N,N]" |
|||
0,55,BNP,0,BNP,BP,TOPT,150,3,3,"[H,T,C,N,N]" |
Write
Preview
Loading…
Cancel
Save
Reference in new issue