Bladeren bron

增加由当前功图转为模板功图功能相关接口、手动诊断功图

chenwen 1 jaar geleden
bovenliggende
commit
ac955500ba

+ 9 - 0
pom.xml

@@ -89,6 +89,15 @@
 		  <artifactId>commons-fileupload</artifactId>
 		  <version>1.3</version>
 	   </dependency>
+	   
+	   <dependency>
+            <groupId>org.opencv</groupId>
+            <artifactId>opencv</artifactId>
+            <version>4.8.0</version>
+            <scope>system</scope>
+            <systemPath>D:\workspace\springboot3\zl-opd-server\lib\opencv-480.jar</systemPath>
+        </dependency>
+
 
   </dependencies>
 	

+ 152 - 3
src/main/java/com/hb/proj/analysis/controller/DiagramDiagnoseController.java

@@ -1,10 +1,19 @@
 package com.hb.proj.analysis.controller;
 
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
 import java.util.ArrayList;
+import java.util.Collections;
 import java.util.Date;
 import java.util.List;
+import java.util.Map;
+import java.util.UUID;
 
+import org.apache.commons.io.FileUtils;
 import org.apache.commons.lang3.StringUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 import org.springframework.beans.BeanUtils;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.beans.factory.annotation.Value;
@@ -15,28 +24,37 @@ import org.springframework.web.bind.annotation.RestController;
 
 import com.hb.proj.allconfig.AccessToken;
 import com.hb.proj.allconfig.SysLog;
-import com.hb.proj.analysis.service.DiagramDetecter;
 import com.hb.proj.analysis.service.DiagramDiagnoseService;
+import com.hb.proj.analysis.service.DiagramFeatureDetecter;
 import com.hb.proj.base.service.WellParamService;
 import com.hb.proj.constant.SortCodeConstant;
 import com.hb.proj.data.controller.DataTransUtils;
 import com.hb.proj.data.service.DiagramDataService;
+import com.hb.proj.diagram.diagnose.DiagramChartBuilder;
+import com.hb.proj.diagram.diagnose.DiagramSimilarDetecter;
 import com.hb.proj.model.DiagramDiagnoseStandardPO;
 import com.hb.proj.model.DiagramDiagnoseStandardVO;
 import com.hb.proj.model.DiagramFeature;
 import com.hb.proj.model.MultiDataVO;
 import com.hb.proj.model.WellParamPO;
+import com.hb.proj.sys.service.UploadService;
 import com.hb.proj.utils.RequestParams;
 import com.hb.proj.utils.RespVO;
 import com.hb.proj.utils.RespVOBuilder;
 import com.hb.xframework.dao.util.PageModel;
+import com.hb.xframework.util.DateUtil;
+import com.hb.xframework.util.MapUtils;
 
+import jakarta.servlet.http.HttpServletRequest;
 import jakarta.validation.constraints.NotBlank;
+import jakarta.validation.constraints.NotNull;
 
 @RestController
 @RequestMapping("/diagnose/standard")
 @Validated
 public class DiagramDiagnoseController {
+	
+	private final static  Logger logger = LoggerFactory.getLogger(DiagramDiagnoseController.class);
 
 	@Autowired
 	private DiagramDiagnoseService  service;
@@ -47,6 +65,9 @@ public class DiagramDiagnoseController {
 	@Autowired
 	private DiagramDataService  diagramService;
 	
+	@Autowired
+	private UploadService  fileService;
+	
 	@Value("${upload.access.path}")
 	private String accessPathPre;
 	
@@ -143,7 +164,7 @@ public class DiagramDiagnoseController {
 		MultiDataVO diagramGatherData=diagramService.get(wparam.getParamId(), diagramTime);
 		DataTransUtils.convert(wparam,diagramGatherData);
 		
-		DiagramDetecter.extractFeature(diagramGatherData, feature);
+		DiagramFeatureDetecter.extractFeature(diagramGatherData, feature);
 		
 		feature.setModifyBy(token.getUsName());
 		if(StringUtils.isNotBlank(feature.getFeatureId())) {
@@ -182,9 +203,14 @@ public class DiagramDiagnoseController {
 		diagramData.setOths(convert(oths));
 		
 		DiagramFeature feature=service.getDiagramFeatureByWell(wellId);
-		return RespVOBuilder.ok(DiagramDetecter.detect(diagramData, feature));
+		return RespVOBuilder.ok(DiagramFeatureDetecter.detect(diagramData, feature));
 	}
 	
+	/**
+	 * 字符序列转list
+	 * @param serial
+	 * @return
+	 */
 	private List<Double>  convert(String serial){
 		String[] datas=serial.split(",");
 		List<Double> rst=new ArrayList<>(datas.length);
@@ -194,4 +220,127 @@ public class DiagramDiagnoseController {
 		return rst;
 	}
 	
+	
+	/**
+	 * 手动诊断指定功图
+	 * @param request
+	 * @param wellId
+	 * @param diagramTime
+	 * @return
+	 */
+	@RequestMapping("/startDiagnose")
+	public  RespVO<Object>   startDiagnose(HttpServletRequest request,String wellId,Date diagramTime){
+		PageModel<DiagramDiagnoseStandardVO> paged= service.query(null, 1, 100);
+		List<DiagramDiagnoseStandardVO> stds=paged.getData();
+		
+		String srcChart=createDiagramChart(request,wellId,diagramTime,null);
+		String basePath=request.getSession().getServletContext().getRealPath("");
+		
+		double minVal=Double.MAX_VALUE,temp=0;
+		DiagramDiagnoseStandardVO minVO=stds.get(0);
+		for(DiagramDiagnoseStandardVO std : stds) {
+			temp=DiagramSimilarDetecter.calcSimilary(basePath+std.getAccessPath(),srcChart);
+			logger.info("与模板功图【{}】,相似度{}",std.getConclusion(), temp);
+			if(temp<minVal) {
+				minVO=std;
+				minVal=temp;
+			}
+			
+		}
+		
+		//FileUtils.deleteQuietly(FileUtils.getFile(srcChart));
+		
+		return RespVOBuilder.ok(minVO);
+	}
+	
+	/**
+	 * 指定功图转为模板功图
+	 * @param request
+	 * @param wellId
+	 * @param diagramTime
+	 * @param conclusion
+	 * @param token
+	 * @return
+	 * @throws IOException 
+	 */
+	@RequestMapping("/convert2TempDiagram")
+	public RespVO<Object> convert2TempDiagram(HttpServletRequest request, @NotBlank(message = "缺少井号") String wellId,
+			@NotNull(message = "缺少功图时间") Date diagramTime, @NotBlank(message = "缺少诊断结论") String conclusion,AccessToken token) throws IOException {
+		
+		String destDir="upload"+File.separator+"diagnose"+File.separator+DateUtil.format(new Date(), "yyyyMM");
+		String chartPath=createDiagramChart(request,wellId,diagramTime,destDir);
+		
+		//String basePath=request.getSession().getServletContext().getRealPath("");
+		
+		//FileUtils.moveFileToDirectory(new File(chartPath), new File(basePath+destDir), true);
+		
+		String fileName=chartPath.substring(chartPath.lastIndexOf("\\")+1);
+		String usName=token!=null?token.getUsName():"unknow";
+		chartPath=destDir+File.separator+fileName;
+		
+		Map<String,Object> newFile=MapUtils.build("fileName",fileName,"filePath",chartPath,"accessPath","/"+chartPath.replaceAll("\\\\", "/"),"useFor","功图诊断模板","createBy",usName);
+		String fileId=fileService.addFile(newFile);
+		
+		DiagramDiagnoseStandardPO  std=new DiagramDiagnoseStandardPO();
+		std.setConclusion(conclusion);
+		std.setFileId(fileId);
+		std.setCreateBy(usName);
+		std.setModifyBy(usName);
+		service.add(std);
+		return RespVOBuilder.ok("详情请在功图诊断标准中查看");
+	}
+	
+	/**
+	 * 对指定功图进行图片生成(存于服务器)
+	 * @param request
+	 * @param wellId
+	 * @param diagramTime
+	 * @return
+	 */
+	@RequestMapping("/buildDiagramChart")
+	public RespVO<Object>  buildDiagramChart(HttpServletRequest request,String wellId,Date diagramTime){
+		createDiagramChart(request,wellId,diagramTime,null);
+		return RespVOBuilder.ok();
+	}
+	
+	
+	/**
+	 * 根据功图数据生成图片(存于服务器)
+	 * @param request
+	 * @param wellId
+	 * @param diagramTime
+	 * @param sortPath
+	 * @return
+	 */
+	private String createDiagramChart(HttpServletRequest request,String wellId,Date diagramTime,String sortPath) {
+		WellParamPO wparam=wpService.get(wellId, SortCodeConstant.PARAM_DIAGRAM_LOAD);
+		MultiDataVO diagramData=diagramService.get(wparam.getParamId(), diagramTime);
+		diagramData.setDisps(convert(diagramData.getDataVal1()));
+		diagramData.setOths(convert(diagramData.getDataVal2()));
+		
+		if(diagramData.getStroke()==null) { //为兼容前期采集数据中没有此数据
+			diagramData.setStroke(Collections.max(diagramData.getDisps()));
+		}
+		if(diagramData.getGlbMax()==null) { //为兼容前期采集数据中没有此数据
+			diagramData.setGlbMax(Collections.max(diagramData.getOths()));
+		}
+		if(diagramData.getGlbMin()==null) { //为兼容前期采集数据中没有此数据
+			diagramData.setGlbMin(Collections.min(diagramData.getOths()));
+		}
+		
+		String basePath=request.getSession().getServletContext().getRealPath("");
+		basePath+=StringUtils.isNotBlank(sortPath)?sortPath:("upload"+File.separator+"temp");
+		String fullName=basePath+File.separator+UUID.randomUUID()+".png";
+		try(FileOutputStream fout=FileUtils.openOutputStream(new File(fullName))) {
+			DiagramChartBuilder.writeDiagramAsJPEG(fout, diagramData);
+			
+			fout.flush();
+			fout.close();
+		}
+		catch(IOException e) {
+			e.printStackTrace();
+		}
+		
+		return fullName;
+	}
 }

+ 96 - 0
src/main/java/com/hb/proj/analysis/service/DiagramDetectUtils.java

@@ -0,0 +1,96 @@
+package com.hb.proj.analysis.service;
+
+import java.util.Arrays;
+import java.util.List;
+
+public class DiagramDetectUtils {
+
+	
+	 /**
+	  * 线性回归
+	  * @param x
+	  * @param y
+	  * @return [k,b]
+	  */
+	 public static double[] lineRegression(List<Double> x,List<Double> y) {  
+	        double sumX = 0.0;  
+	        double sumY = 0.0;  
+	        double sumXY = 0.0;  
+	        double sumXX = 0.0;  
+	        int n = x.size();  
+	  
+	        for (int i = 0; i < n; i++) {  
+	            sumX += x.get(i);  
+	            sumY += y.get(i);  
+	            sumXY += x.get(i) * y.get(i);  
+	            sumXX += x.get(i) * x.get(i);  
+	        }  
+	  
+	        double xMean = sumX / n;  
+	        double yMean = sumY / n;  
+	  
+	        // 使用最小二乘法公式计算斜率和截距  
+	        double slope = (n * sumXY - sumX * sumY) / (n * sumXX - sumX * sumX);  
+	        double intercept = yMean - slope * xMean;  
+	        
+	        return new double[] {slope,intercept};
+	}
+	 
+
+   /**
+    * 在序列中查找小于等于指定值的第一个值得索引
+    * @param val
+    * @param serial
+    * @return
+    */
+   public static int indexOfEL(double val,List<Double> serial) {
+			for(int i=0,len=serial.size();i<len;i++) {
+				if(serial.get(i).doubleValue()<=val) {
+					return i;
+				}
+			}
+			return -1;
+	}
+   
+   
+   /**
+    * 在序列中查找大于等于指定值的第一个值得索引
+    * @param val
+    * @param serial
+    * @return
+    */
+   public static int indexOfEG(double val,List<Double> serial) {
+		for(int i=0,len=serial.size();i<len;i++) {
+			if(serial.get(i).doubleValue()>=val) {
+				return i;
+			}
+		}
+		return -1;
+   }
+   
+   
+   public static Double avg(List<Double> serial) {
+	   double sum=0.0;
+	   for(Double f  : serial) {
+		   sum+=f.doubleValue();
+	   }
+	   return sum/serial.size();
+   }
+   
+   
+   public static double getAngle(double angle) {
+	   return Math.atan(angle)*180/Math.PI;
+   }
+	 
+	
+	 public static void main(String[] args) {  
+	        // 示例数据:y = 2x + 1  
+	         List<Double> x=Arrays.asList(1d, 2d, 3d, 4d, 5d);
+	        List<Double> y=Arrays.asList(3d, 5d, 7d, 9d, 11d);
+	        
+	        double[] rst=DiagramDetectUtils.lineRegression(x, y);
+	        // 使用模型进行预测  
+	        System.out.println("k="+rst[0]+",b="+rst[1]);
+	        System.out.println("预测值:" + (rst[0]*6+rst[1])); // 当x=6时,预测的y值是多少?  
+	    }  
+}

+ 157 - 0
src/main/java/com/hb/proj/analysis/service/DiagramFeatureDetecter.java

@@ -0,0 +1,157 @@
+package com.hb.proj.analysis.service;
+
+import java.util.List;
+
+import com.hb.proj.model.DiagramFeature;
+import com.hb.proj.model.MultiDataVO;
+
+/**
+ * 
+ * @author cwen
+ * 功图诊断需要3两部分的数据:
+ * 1、待诊断功图采集数据(位移序列、采集值序列、最大、最小、上下冲程转换点)
+ * 2、基于该井正常功图的经验数据(增载、卸载的速率、理论最大最小载荷)
+ * 3、异常功图判断规则及关键点数据(误差范围)
+ */
+public class DiagramFeatureDetecter {
+	
+	
+	public static DiagramFeature extractFeature(MultiDataVO diagram,DiagramFeature feature) {
+		
+		//DiagramFeature  feature=new DiagramFeature();
+		
+		int size=diagram.getDisps().size();
+		
+		//上冲程位移序列
+		List<Double>  upDisp=diagram.getDisps().subList(0,diagram.getTurnIndex());
+		
+		//下冲程位移序列
+		List<Double>  dwnDisp=diagram.getDisps().subList(diagram.getTurnIndex(), size);
+		
+		//上冲程采集值序列
+		List<Double> upOth=diagram.getOths().subList(0,diagram.getTurnIndex());
+		
+		//下冲程采集值序列
+		List<Double>  dwnOth=diagram.getOths().subList(diagram.getTurnIndex(), size);
+		
+		
+		//上冲程增载结束点
+		int addloadEnd=DiagramDetectUtils.indexOfEG(feature.getTheoryMax(),upOth);
+		
+		//下冲程卸载终点
+		int unloadEnd=DiagramDetectUtils.indexOfEL(feature.getTheoryMin(),dwnOth);
+		
+		//增载线
+		double[] lineModel=DiagramDetectUtils.lineRegression(upDisp.subList(0, addloadEnd), upOth.subList(0, addloadEnd));
+		feature.setLoadRate(lineModel[0]);
+		
+		//卸载线
+		double midLoad=(diagram.getGlbMax()+diagram.getGlbMin())/2;
+		int midIdx=DiagramDetectUtils.indexOfEL(midLoad,dwnOth);
+		lineModel=DiagramDetectUtils.lineRegression(dwnDisp.subList(midIdx, unloadEnd), dwnOth.subList(midIdx, unloadEnd));
+		feature.setUnloadRate(lineModel[0]);
+		
+		
+		return feature;
+	}
+
+	public static String detect(MultiDataVO diagram,DiagramFeature diagramFea) {
+		
+		
+		int size=diagram.getDisps().size();
+		
+		//下冲程位移序列
+		List<Double>  dwnDisp=diagram.getDisps().subList(diagram.getTurnIndex(), size);
+		
+		//下冲程采集值序列
+		List<Double>  dwnOth=diagram.getOths().subList(diagram.getTurnIndex(), size);
+		
+		//上冲程采集值序列
+		List<Double> upOth=diagram.getOths().subList(0,diagram.getTurnIndex());
+		
+		//上冲程增载结束点
+		int addloadEnd=DiagramDetectUtils.indexOfEG(diagramFea.getTheoryMax(),upOth);
+		
+		//上冲程后段有急剧卸载时
+		int upUnloadEnd=DiagramDetectUtils.indexOfEL(diagramFea.getTheoryMin(),diagram.getOths().subList(addloadEnd,diagram.getTurnIndex()));
+		
+		if(upUnloadEnd>=0) {
+			return "管式泵活塞脱出";
+		}
+		
+		double upavgload=DiagramDetectUtils.avg(upOth);
+		//上冲程的平均载荷小于理论最小载荷,功图为处在理论最小载荷以下的带状图
+		if(diagram.getGlbMax() <=diagramFea.getTheoryMin()) {
+			return "抽油杆断脱";
+		}
+		//上冲程增载达不到理论最大载荷
+		else if(upavgload>diagramFea.getTheoryMin() && diagram.getGlbMax() < diagramFea.getTheoryMax()) {
+			return "油管漏失";
+		}
+		
+		//严重吸入漏失时,悬点不能卸载,功图为处在理论最大载荷附近的带状图
+		double theorydiff=(diagramFea.getTheoryMax()-diagramFea.getTheoryMin())*0.3;
+		double dwnavgload=DiagramDetectUtils.avg(dwnOth);
+		if((upavgload-dwnavgload)<theorydiff) {
+			
+			return "严重吸入漏失";
+		}
+		
+		//上下行中有阻力(油井结蜡、稠油)
+		if(upavgload > diagramFea.getTheoryMax() && dwnavgload < diagramFea.getTheoryMin()) {
+			return "油井结蜡或稠油影响";
+		}
+		
+		
+		//下冲程卸载终点
+		int unloadEnd=DiagramDetectUtils.indexOfEL(diagramFea.getTheoryMin(),dwnOth);
+		
+		//气锁标志位置
+		int gasLockTagIdx=(int)Math.ceil(dwnOth.size()*0.9);
+		
+		if(unloadEnd>0 && unloadEnd>=gasLockTagIdx) {
+			return "气锁";
+		}
+		
+		
+		
+		double offsetDisp=(dwnOth.get(0)-diagramFea.getTheoryMin())/diagramFea.getUnloadRate();
+		double realOffsetDisp=dwnDisp.get(0)-dwnDisp.get(unloadEnd);
+		//正常功图右下角位移左移距离,实际多偏移1/5冲程则认为是气体影响或供液不足
+		if((realOffsetDisp-offsetDisp) > (diagram.getStroke()*0.12)) {
+			
+			//卸载线载荷中值位置
+			double midLoad=(diagram.getGlbMax()+diagram.getGlbMin())/2;
+			int midIdx=DiagramDetectUtils.indexOfEL(midLoad,dwnOth);
+			double[] lineRatio=DiagramDetectUtils.lineRegression(dwnDisp.subList(midIdx,unloadEnd),dwnOth.subList(midIdx,unloadEnd));
+			
+			//卸载速率与正常相等或更快
+			if(diagramFea.getUnloadRate() <= lineRatio[0]) {
+				return "供液不足";
+			}
+			else {
+				return "气体影响";
+			}
+			
+		}
+		
+		//下冲程快结束时接近起点位置
+		int dwnEndIdx=DiagramDetectUtils.indexOfEL(diagram.getDisps().get(0),dwnDisp);
+		if(dwnEndIdx>0 && dwnEndIdx < dwnDisp.size()) { //提前到达起点
+			int detectIdx=(int)Math.ceil(dwnDisp.size()+dwnEndIdx)/2;
+			if(dwnOth.get(detectIdx) < diagram.getOths().get(0)) {    //结束前还有低于起点的载荷,下环
+				return "活塞下行碰泵";
+			}
+			else {
+				//结束前上环
+			}
+		}
+		
+		
+		
+		return "正常";
+	}
+	
+	
+	
+}

+ 41 - 0
src/main/java/com/hb/proj/diagram/diagnose/DiagramChartBuilder.java

@@ -0,0 +1,41 @@
+package com.hb.proj.diagram.diagnose;
+
+import java.awt.Graphics2D;
+import java.awt.image.BufferedImage;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+
+import javax.imageio.ImageIO;
+
+import com.hb.proj.model.MultiDataVO;
+
+
+public class DiagramChartBuilder {
+	
+	public static void writeDiagramAsJPEG(OutputStream out,MultiDataVO diagram) {
+		writeDiagramAsJPEG(out,diagram,360,180);
+	}
+
+	public static void writeDiagramAsJPEG(OutputStream out, MultiDataVO diagram,int width, int height){
+		BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_BYTE_GRAY);
+		Graphics2D g2 = image.createGraphics();
+		DiagramRender render=new DiagramRender(width,height);
+		render.draw(g2,diagram.getDisps(),diagram.getOths(),diagram.getStroke(),diagram.getGlbMax(),diagram.getGlbMin());
+        if(g2!=null){
+			g2.dispose();
+		}
+        try {
+			ImageIO.write(image, "png", out);
+		} catch (IOException e) {
+			e.printStackTrace();
+		}
+       
+	}
+	
+	public static byte[] writeDiagramToByte(MultiDataVO diagram,int width, int height) throws IOException{
+		ByteArrayOutputStream  bos=new ByteArrayOutputStream();
+        writeDiagramAsJPEG(bos,diagram,width,height);
+		return bos.toByteArray();
+	}
+}

+ 136 - 0
src/main/java/com/hb/proj/diagram/diagnose/DiagramRender.java

@@ -0,0 +1,136 @@
+package com.hb.proj.diagram.diagnose;
+
+import java.awt.BasicStroke;
+import java.awt.Color;
+import java.awt.Graphics2D;
+import java.awt.geom.Line2D;
+import java.util.List;
+
+/**
+ * 功图绘制,用于功图诊断,作为被诊断对象、标准功图
+ * 只需要闭合曲线,其他不需要绘制
+ * 功图诊断时,最好标准功图和待诊断功图出自同一绘制方法、同大小
+ * @author cwen
+ *
+ */
+public class DiagramRender {
+
+	private Color canvasColor;
+	private int canvasWidth;
+	private int canvasHeight;
+	
+	private int originX ;  //原点x值
+    private int originY ; //原点y值
+	
+    private int xAxisEnd;
+	private int yAxisEnd;
+	
+    
+    private double xMinValue;
+    private double yMinValue;
+    private double xMaxValue;
+    private double yMaxValue;
+    private double xRatio;
+    private double yRatio;
+   
+    
+    public DiagramRender(){
+		this(360,180);
+	}
+    
+    public DiagramRender(int canvasWidth,int canvasHeight){
+    	this.canvasWidth=canvasWidth;
+		this.canvasHeight=canvasHeight;
+		this.canvasColor=new Color(255,255,255);
+		this.originX=10;
+		this.originY=canvasHeight-10;
+		this.xAxisEnd=canvasWidth-10;
+		this.yAxisEnd=10;
+		this.xMaxValue=3;
+		this.xMinValue=0;
+		this.yMaxValue=50;
+		this.yMinValue=0;
+	}
+    
+    public void draw(Graphics2D g2,List<Double> disps,List<Double> oths,double stroke,double maxOth,double minOth) {
+    	this.xMinValue=0;
+    	this.xMaxValue=Math.ceil(stroke);
+    	this.yMinValue=Math.floor(minOth); //定格画,避免井的个体特性影响相似性判断,相当于各类图缩放在一个区域内
+    	this.yMaxValue=Math.ceil(maxOth);
+    	
+    	this.xRatio=(this.xAxisEnd-this.originX)/(this.xMaxValue-this.xMinValue);
+    	this.yRatio=(this.originY-this.yAxisEnd)/(this.yMaxValue-this.yMinValue);
+    	
+    	this.drawCanvas(g2);
+    	//this.drawCoordinate(g2);
+    	this.drawCurve(g2, disps, oths);
+    }
+    
+    
+    
+    /**
+     * 绘制画布
+     * @param g2
+     */
+	public void drawCanvas(Graphics2D g2){
+		g2.setColor(this.canvasColor);
+		g2.fillRect(0, 0, this.canvasWidth, this.canvasHeight);
+	}
+	
+	/**
+	 * 简易坐标轴
+	 * @param g2
+	 */
+	public void drawCoordinate(Graphics2D g2) {
+		g2.setStroke(new BasicStroke(1));
+		g2.setColor(new Color(212,212,212));
+		g2.drawLine(this.originX, this.originY, this.originX, this.yAxisEnd);     //y 轴
+		g2.drawLine(this.xAxisEnd, this.originY, this.xAxisEnd, this.yAxisEnd);     //y border
+		g2.drawLine(this.originX, this.yAxisEnd, this.xAxisEnd, this.yAxisEnd);     //x border
+		g2.drawLine(this.originX, this.originY,this.xAxisEnd,this.originY);     //x bottom-border
+		
+		int yGirdCount=5;
+		double yGridDivide=(this.originY-this.yAxisEnd)/yGirdCount;
+		double tempy=0;
+		for(int i=1;i<yGirdCount;i++) {
+			tempy=this.originY-i*yGridDivide;
+			g2.draw(new Line2D.Double(this.originX, tempy, this.xAxisEnd,tempy));
+		}
+	}
+	
+	/**
+	 * 绘制曲线
+	 * @param g2
+	 * @param disps
+	 * @param oths
+	 */
+	public void drawCurve(Graphics2D g2,List<Double> disps,List<Double> oths) {
+		g2.setStroke(new BasicStroke(1));
+		g2.setPaint(new Color(0,0,0));
+		double ptx=getX(disps.get(0)),pty=getY(oths.get(0));
+		double nptx=0,npty=0;
+		for(int i=1,len=disps.size(),len2=oths.size();i<len && i<len2;i++) {
+			nptx=getX(disps.get(i));
+			npty=getY(oths.get(i));
+			g2.draw(new Line2D.Double(ptx, pty, nptx,npty));
+			
+			ptx=nptx;
+			pty=npty;
+		}
+		
+		nptx=getX(disps.get(0));
+		npty=getY(oths.get(0));
+		g2.draw(new Line2D.Double(ptx, pty, nptx,npty));
+	}
+	
+	
+	private double getX(double val) { 
+		return this.originX+(val-this.xMinValue)*this.xRatio;
+	}
+
+	private double getY(double val) { 
+		return this.originY-(val-this.yMinValue)*this.yRatio;
+	}
+	
+	
+}

+ 73 - 0
src/main/java/com/hb/proj/diagram/diagnose/DiagramSimilarDetecter.java

@@ -0,0 +1,73 @@
+package com.hb.proj.diagram.diagnose;
+
+import org.opencv.core.Core;
+import org.opencv.core.Mat;
+import org.opencv.core.MatOfFloat;
+import org.opencv.core.Scalar;
+import org.opencv.imgcodecs.Imgcodecs;
+import org.opencv.imgproc.Imgproc;
+
+public class DiagramSimilarDetecter {
+
+	// 调用OpenCV库文件
+    static {
+        System.loadLibrary(Core.NATIVE_LIBRARY_NAME);
+    }
+    
+    public static double calcViaMSE(Mat image1, Mat image2) {
+    	Mat image1Gray = new Mat();
+        Mat image2Gray = new Mat();
+        Imgproc.cvtColor(image1, image1Gray, Imgproc.COLOR_BGR2GRAY);
+        Imgproc.cvtColor(image2, image2Gray, Imgproc.COLOR_BGR2GRAY);
+        Imgproc.resize(image1Gray, image1Gray, image2Gray.size());
+        Mat diff = new Mat();
+        Core.absdiff(image1Gray, image2Gray, diff);
+        Scalar mse = Core.mean(diff.mul(diff));
+        return mse.val[0];
+    }
+    
+    // 计算结构相似性指数(SSIM,实际用的是模板匹配算法0:表示相同图像,)
+    public static double calcViaTemp(Mat image1, Mat tempImg) {
+        Mat image1Gray = new Mat();
+        Mat image2Gray = new Mat();
+        Imgproc.cvtColor(image1, image1Gray, Imgproc.COLOR_BGR2GRAY);
+        Imgproc.cvtColor(tempImg, image2Gray, Imgproc.COLOR_BGR2GRAY);
+        MatOfFloat ssimMat = new MatOfFloat();
+        Imgproc.matchTemplate(image1Gray, image2Gray, ssimMat, Imgproc.CV_COMP_CORREL);
+        Scalar ssimScalar = Core.mean(ssimMat);
+        return ssimScalar.val[0];
+    }
+    
+    public static double calcSimilary(String chartFile1,String tempChart) {
+    	Mat image1 = Imgcodecs.imread(chartFile1);  
+	    Mat image2 = Imgcodecs.imread(tempChart); 
+	    return calcViaTemp(image1, image2);
+    }
+
+	public static void main(String[] args) {
+		 
+		 /**
+		 Mat image1 = Imgcodecs.imread("C:\\Users\\cwen\\Desktop\\t2.png");  
+	     Mat image2 = Imgcodecs.imread("C:\\Users\\cwen\\Desktop\\14.png"); 
+	     
+	     double ssim = calcViaMSE(image1, image2);
+	     System.out.println("相似性指数: " + String.valueOf(ssim));
+	     */
+		 
+		System.out.println("相似性指数: " + calcSimilary("C:\\Users\\cwen\\Desktop\\t22.png","C:\\Users\\cwen\\Desktop\\k1.png"));
+		
+		System.out.println("相似性指数: " + calcSimilary("C:\\Users\\cwen\\Desktop\\t22.png","C:\\Users\\cwen\\Desktop\\k2.png"));
+	   
+	    System.out.println("相似性指数: " + calcSimilary("C:\\Users\\cwen\\Desktop\\t7.png","C:\\Users\\cwen\\Desktop\\t2.jpeg"));
+	     
+	    
+	     //double ssim = Imgproc.compareStructuralSimilarity(grayImage1, grayImage2, new Size(11, 11), 0.05, 0.2);  
+	     //System.out.println("SSIM: " + ssim);
+	     
+	    
+
+	}
+	
+	
+
+}

+ 1 - 1
src/main/java/com/hb/proj/model/DiagramDiagnoseStandardPO.java

@@ -28,7 +28,7 @@ public class DiagramDiagnoseStandardPO {
 	/**
 	 * 气体影响程度  如:严重、一般
 	 */
-	private String gasEffect;
+	private Double gasEffect;
 	
 	
 	/**

+ 51 - 0
src/main/java/com/hb/proj/model/DiagramFeature.java

@@ -0,0 +1,51 @@
+package com.hb.proj.model;
+
+import java.util.Date;
+
+import jakarta.validation.constraints.NotNull;
+import lombok.Data;
+
+/**
+ * 正常功图特征值
+ * @author cwen
+ *
+ */
+
+@Data
+public class DiagramFeature {
+	
+	private String featureId;
+	
+	/**
+	 * 井号
+	 */
+	private String wellId;
+	
+	/**
+	 * 理论最大载荷
+	 */
+	@NotNull(message="理论最大载荷不能为空")
+	private Double theoryMax;
+	
+	
+	/**
+	 * 理论最小载荷
+	 */
+	@NotNull(message="理论最小载荷不能为空")
+	private Double theoryMin;
+
+	/**
+	 * 增载速率(线性、平均值)
+	 */
+	private Double loadRate;
+	
+	/**
+	 * 卸载速率(线性、平均值)
+	 */
+	private Double unloadRate;
+	
+	private String modifyBy;
+	
+	private Date modifyTime;
+	
+}