Bläddra i källkod

动态组加载和计算 build包

zhaoyk 2 år sedan
förälder
incheckning
e6d13c13ad

+ 182 - 0
adm-business/adm-diagram/src/main/java/com/persagy/adm/diagram/core/build/CalcContext.java

@@ -0,0 +1,182 @@
+package com.persagy.adm.diagram.core.build;
+
+import cn.hutool.core.util.StrUtil;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+import com.persagy.adm.diagram.core.DataStrategy;
+import com.persagy.adm.diagram.core.model.Diagram;
+import com.persagy.adm.diagram.core.model.DiagramNode;
+import com.persagy.adm.diagram.core.model.EquipmentNode;
+import com.persagy.adm.diagram.core.model.Line;
+import com.persagy.adm.diagram.core.model.base.Container;
+import com.persagy.adm.diagram.core.model.base.IDataBind;
+import com.persagy.adm.diagram.core.model.template.DiagramTemplate;
+import com.persagy.adm.diagram.core.model.template.MainPipe;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.stream.Collectors;
+
+/**
+ * 系统图计算上下文
+ * @author zhaoyk
+ */
+public class CalcContext {
+
+	private Diagram diagram;
+
+	private DiagramTemplate template;
+
+	private DataStrategy dataStrategy;
+
+	private DiagramBuilder diagramBuilder;
+
+	private List<ObjectNode> optionalObjs;
+
+	private List<ObjectNode> optionalRels;
+
+	/**
+	 * 当前动态组
+	 */
+	private CalcGroup currentGroup;
+
+	/**
+	 * 记录已经使用的数据id和对应的组件
+	 */
+	private HashMap<String, IDataBind> equipMap = new HashMap<>();
+
+	/**
+	 * 记录已经使用的关系id和对应的连线
+	 */
+	private HashMap<String, Line> lineMap = new HashMap<>();
+
+	public CalcContext(Diagram diagram) {
+		this.diagram = diagram;
+		this.template = diagram.getTemplate();
+
+		init();
+	}
+
+	private void init() {
+		//记录节点中已使用的数据id
+		diagram.getNodes().forEach(node -> {
+			if (EquipmentNode.TYPE.equals(node.getCompType())) {
+				equipMap.put(((EquipmentNode) node).getDataObjectId(), (EquipmentNode)node);
+			}
+		});
+		//记录干管中已使用的数据id
+		if(template != null && template.getMainPipes() != null) {
+			template.getMainPipes().forEach(mainPipe -> {
+				if(StrUtil.isNotBlank(mainPipe.getDataObjectId())) {
+					equipMap.put(mainPipe.getDataObjectId(), mainPipe);
+				}
+			});
+		}
+
+		diagram.getLines().forEach(line -> {
+			if (StrUtil.isNotBlank(line.getDataObjectId())){
+				lineMap.put(line.getDataObjectId(), line);
+			}
+		});
+	}
+
+	public DiagramBuilder getDiagramBuilder() {
+		return diagramBuilder;
+	}
+
+	public void setDiagramBuilder(DiagramBuilder diagramBuilder) {
+		this.diagramBuilder = diagramBuilder;
+	}
+
+	public void setCurrentGroup(CalcGroup calcGroup) {
+		if(currentGroup != null){
+			calcGroup.setParent(currentGroup);
+		}
+		currentGroup = calcGroup;
+	}
+
+	public void clearCurrentGroup(){
+		if (currentGroup != null) {
+			currentGroup = currentGroup.getParent();
+		}
+	}
+
+	public <T> T getComp(String id, String type) {
+		if(currentGroup != null){
+			Object comp = currentGroup.getComp(id);
+			if(comp != null) {
+				return (T)comp;
+			}
+		}
+
+		if(Container.TYPE.equals(type)) {
+			return (T)template.getContainerById(id);
+		} else if(MainPipe.TYPE.equals(type)) {
+			return (T)template.getMainPipeById(id);
+		} else if(EquipmentNode.TYPE.equals(type)) {
+			for(DiagramNode node : diagram.getNodes()) {
+				if(node.getId().equals(id)){
+					return (T)node;
+				}
+			}
+		} else {
+			//TODO 其他类型
+		}
+		return null;
+	}
+
+	public DataStrategy getDataStrategy() {
+		return dataStrategy;
+	}
+
+	public void setDataStrategy(DataStrategy dataStrategy) {
+		this.dataStrategy = dataStrategy;
+	}
+
+	public Diagram getDiagram() {
+		return diagram;
+	}
+
+	public DiagramTemplate getTemplate() {
+		return template;
+	}
+
+	public HashMap<String, IDataBind> getEquipMap() {
+		return equipMap;
+	}
+
+	public HashMap<String, Line> getLineMap() {
+		return lineMap;
+	}
+
+	public List<ObjectNode> getOptionalObjs() {
+		return optionalObjs;
+	}
+
+	public void setOptionalObjs(List<ObjectNode> optionalObjs) {
+		if (optionalObjs == null) {
+			optionalObjs = new ArrayList<>(0);
+		} else if (equipMap.size() > 0) {
+			//去掉已经使用的数据项
+			optionalObjs = optionalObjs.stream().filter(obj -> !equipMap.containsKey(obj.get("id").asText())).collect(Collectors.toList());
+		}
+		this.optionalObjs = optionalObjs;
+	}
+
+	public List<ObjectNode> getOptionalRels() {
+		return optionalRels;
+	}
+
+	public void setOptionalRels(List<ObjectNode> optionalRels) {
+		this.optionalRels = optionalRels;
+	}
+
+	public String getProjectId() {
+		return diagram.getProjectId();
+	}
+
+	public String getGroupCode() {
+		return diagram.getGroupCode();
+	}
+
+}

+ 73 - 0
adm-business/adm-diagram/src/main/java/com/persagy/adm/diagram/core/build/CalcGroup.java

@@ -0,0 +1,73 @@
+package com.persagy.adm.diagram.core.build;
+
+import cn.hutool.core.collection.CollUtil;
+import com.persagy.adm.diagram.core.model.base.IEquipHolder;
+
+import java.util.*;
+
+/**
+ * 计算时的数据组
+ * @author zhaoyk
+ */
+public class CalcGroup {
+
+	private CalcGroup parent;
+
+	private int currentIdx;
+
+	private Map<String, Object> currentComps = new HashMap<>();
+
+	private IEquipHolder dynSrcComp;
+
+	public CalcGroup() {
+
+	}
+
+	public void setDynSrcComp(IEquipHolder dynSrcComp, Object bindData) {
+		this.dynSrcComp = dynSrcComp;
+		this.dynSrcComp.setMatchedData(CollUtil.newArrayList(bindData));
+	}
+
+	public IEquipHolder getDynSrcComp() {
+		return dynSrcComp;
+	}
+
+	public void setCurrentIdx(int currentIdx) {
+		this.currentIdx = currentIdx;
+	}
+
+	public void setParent(CalcGroup parent) {
+		this.parent = parent;
+	}
+
+	public CalcGroup getParent() {
+		return parent;
+	}
+
+	public Object getComp(String id) {
+		CalcGroup g = this;
+		while (g != null){
+			Object comp = g.currentComps.get(id);
+			if(comp != null) {
+				return comp;
+			}
+			g = g.parent;
+		}
+		return null;
+	}
+
+	public void addComp(String id, Object comp) {
+		currentComps.put(id, comp);
+	}
+
+	public List<Integer> getIdx(){
+		LinkedList<Integer> list = new LinkedList<>();
+		CalcGroup g = this;
+		while (g != null){
+			list.addFirst(g.currentIdx);
+			g = g.parent;
+		}
+		return list;
+	}
+
+}

+ 640 - 0
adm-business/adm-diagram/src/main/java/com/persagy/adm/diagram/core/build/DiagramBuilder.java

@@ -0,0 +1,640 @@
+package com.persagy.adm.diagram.core.build;
+
+import cn.hutool.core.collection.CollUtil;
+import cn.hutool.core.util.IdUtil;
+import cn.hutool.core.util.StrUtil;
+import cn.hutool.extra.spring.SpringUtil;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+import com.persagy.adm.diagram.core.ContentParser;
+import com.persagy.adm.diagram.core.model.*;
+import com.persagy.adm.diagram.core.model.base.Container;
+import com.persagy.adm.diagram.core.model.base.IComponent;
+import com.persagy.adm.diagram.core.model.base.IDataBind;
+import com.persagy.adm.diagram.core.model.base.IEquipHolder;
+import com.persagy.adm.diagram.core.model.legend.Anchor;
+import com.persagy.adm.diagram.core.model.legend.Legend;
+import com.persagy.adm.diagram.core.model.logic.DataFilter;
+import com.persagy.adm.diagram.core.model.logic.DynGroup;
+import com.persagy.adm.diagram.core.model.logic.LogicFilter;
+import com.persagy.adm.diagram.core.model.template.DiagramTemplate;
+import com.persagy.adm.diagram.core.model.template.MainPipe;
+import com.persagy.adm.diagram.core.model.template.MainPipePoint;
+import com.persagy.adm.diagram.core.model.virtual.PackNode;
+import com.persagy.dmp.digital.entity.ObjectRelation;
+
+import java.util.*;
+import java.util.stream.Collectors;
+
+/**
+ * 处理系统图的计算逻辑
+ * @author zhaoyk
+ */
+public class DiagramBuilder {
+
+	public static final Object DATA_NULL = new Object();
+
+	/**
+	 * 设备容器限制,避免模板设置不当导致过多节点
+	 */
+	public static int equipLimit = 50;
+
+	private CalcContext context;
+
+	private Diagram diagram;
+
+	private DiagramTemplate template;
+
+	/**
+	 * template.mainPipes引用,避免空指针
+	 */
+	private List<MainPipe> mainPipes;
+
+	private LegendLoader legendLoader;
+
+	private ContentParser contentParser;
+
+	private HashSet<String> refRelTypes = new HashSet<>();
+
+	public DiagramBuilder(CalcContext context, LegendLoader legendLoader) {
+		this.context = context;
+		this.legendLoader = legendLoader;
+
+		this.diagram = context.getDiagram();
+		this.template = context.getTemplate();
+		this.mainPipes = template.getMainPipes() != null ? template.getMainPipes() : new ArrayList<>();
+
+		this.context.setDiagramBuilder(this);
+
+		this.contentParser = SpringUtil.getBean("diagramContentParser");
+	}
+
+	/**
+	 * 加载设备数据,构建系统图结构
+	 */
+	public void buildStructure(){
+		calcContainerAndMainPipes(template.getFrame());
+
+		buildMainPipeAndNodes();
+
+		handleNodes();
+		handleContainers();
+	}
+
+	/**
+	 * 对容器和相关的干管进行计算和数据加载
+	 */
+	private void calcContainerAndMainPipes(Container container){
+		List<Container> subEquipBoxes = new ArrayList<>();
+		List<Container> subDynCons = new ArrayList<>();
+		container.readSubContainers(subEquipBoxes, subDynCons);
+
+		List<MainPipe> relMainPipes = getRelMainPipes(container, subEquipBoxes);
+
+		DynGroup dynGroup = container.getDynGroup();
+		if (dynGroup != null) {
+			List<ObjectNode> dynData;
+			CalcGroup group;
+			int pos = container.getParent().getChildren().indexOf(container);
+
+			boolean isCompSrc = dynGroup.isCompSrc();
+			if(isCompSrc) {
+				IEquipHolder srcComp = context.getComp(dynGroup.getDynSource(), dynGroup.getDynSourceType());
+				dynData = findMatchedData(srcComp);
+			} else {
+				//TODO 楼层,建筑等分组方式
+				dynData = new ArrayList<>();
+			}
+
+			if (CollUtil.isEmpty(dynData)) {
+				return;
+			}
+
+			group = new CalcGroup();
+			context.setCurrentGroup(group);
+
+			String containerJson = null;
+			String mainPipesJson = null;
+
+			for (int i = 0; i < dynData.size(); i++) {
+				group.setCurrentIdx(i);
+
+				if(i == 0){
+					setGroupContainer(container, group);
+					setGroupMainPipes(relMainPipes, group);
+
+					if(isCompSrc) {
+						group.setDynSrcComp(context.getComp(dynGroup.getDynSource(), dynGroup.getDynSourceType()), dynData.get(i));
+					}
+
+					loadEquipsData(subEquipBoxes, subDynCons, relMainPipes);
+				} else {
+					if(containerJson == null) {
+						containerJson = contentParser.toJson(container);
+					}
+					if(mainPipesJson == null) {
+						mainPipesJson = contentParser.toJson(relMainPipes);
+					}
+
+					String idLabel = StrUtil.join("_", group.getIdx());
+
+					Container newCon = copyContainer(containerJson, idLabel);
+					container.getParent().addComp(newCon, pos + i);
+					setGroupContainer(newCon, group);
+
+					List<MainPipe> newMps = copyRelMainPipes(mainPipesJson, idLabel);
+					mainPipes.addAll(newMps);
+					setGroupMainPipes(newMps, group);
+
+					List<Container> newSubEquipBoxes = new ArrayList<>();
+					List<Container> newSubDynCons = new ArrayList<>();
+					newCon.readSubContainers(newSubEquipBoxes, newSubDynCons);
+
+					if(isCompSrc) {
+						group.setDynSrcComp(context.getComp(dynGroup.getDynSource(), dynGroup.getDynSourceType()), dynData.get(i));
+					}
+
+					loadEquipsData(newSubEquipBoxes, newSubDynCons, newMps);
+				}
+
+			}
+
+			//清除当前计算组
+			context.clearCurrentGroup();
+		} else {
+			loadEquipsData(subEquipBoxes, subDynCons, relMainPipes);
+		}
+	}
+
+	private void setGroupContainer(Container con, CalcGroup currentGroup) {
+		for (Container item : con.listContainers()) {
+			currentGroup.addComp(getSrcId(item.getId()), item);
+		}
+	}
+
+	private void setGroupMainPipes(List<MainPipe> mps,  CalcGroup currentGroup){
+		for(MainPipe mp : mps){
+			currentGroup.addComp(getSrcId(mp.getId()), mp);
+		}
+	}
+
+	private Container copyContainer(String containerJson, String idLabel) {
+		Container container = contentParser.parseContent(containerJson, Container.class);
+		for (Container con : container.listContainers()) {
+			con.setId(getSrcId(con.getId()) + ":" + idLabel);
+		}
+		container.initParent();
+
+		return container;
+	}
+
+	private List<MainPipe> copyRelMainPipes(String mainPipesJson, String idLabel){
+		List<MainPipe> mainPipes = new ArrayList<>();
+		Collections.addAll(mainPipes, contentParser.parseContent(mainPipesJson, MainPipe[].class));
+		for(MainPipe mp : mainPipes){
+			mp.setId(getSrcId(mp.getId()) + ":" + idLabel);
+			changeMainPipeRefIds(mp);
+
+			mp.init(template);
+		}
+		return mainPipes;
+	}
+
+	private void changeMainPipeRefIds(MainPipe mp){
+		List<String> relConIds = mp.getRelatedContainers();
+		if(relConIds != null){
+			for (int i = 0; i < relConIds.size(); i++) {
+				Container con = context.getComp(relConIds.get(i), Container.TYPE);
+				if(con != null) {
+					relConIds.set(i, con.getId());
+				}
+			}
+		}
+
+		for(List<MainPipePoint> line : mp.getPath()){
+			for (MainPipePoint p : line) {
+				Container con = context.getComp(p.getContainerId(), Container.TYPE);
+				if(con != null) {
+					p.setContainerId(con.getId());
+				}
+			}
+		}
+	}
+
+	private String getSrcId(String id){
+		int pos = id.indexOf(':');
+		if(pos > 0){
+			return id.substring(0, pos);
+		}
+		return id;
+	}
+
+	private List<MainPipe> getRelMainPipes(Container container, List<Container> subEquipBoxes){
+		List<MainPipe> relMainPipes = new ArrayList<>();
+		boolean isFrame = template.isFrameContainer(container);
+		for(MainPipe mainPipe : mainPipes) {
+			List<Container> relCons = mainPipe.getRelatedContainerList();
+			boolean relWithMe;
+			if(isFrame){
+				relWithMe = CollUtil.isEmpty(relCons);
+			} else {
+				relWithMe = relCons != null && relCons.contains(container);
+			}
+			if(relWithMe){
+				relMainPipes.add(mainPipe);
+			} else {
+				for(Container subEq : subEquipBoxes) {
+					if(relCons != null && relCons.contains(subEq)){
+						relMainPipes.add(mainPipe);
+						break;
+					}
+				}
+			}
+		}
+		return relMainPipes;
+	}
+
+	private void loadEquipsData(List<Container> subEquipBoxes, List<Container> subDynCons, List<MainPipe> relMainPipes){
+		for(MainPipe mainPipe : relMainPipes) {
+			if (mainPipe.isBindEquipment()){
+				loadMatchedData(mainPipe);
+			}
+		}
+		for(Container subCon : subEquipBoxes) {
+			loadMatchedData(subCon);
+		}
+
+		if (subDynCons.size() > 0) {
+			for(Container subDyn : subDynCons) {
+				calcContainerAndMainPipes(subDyn);
+			}
+		}
+	}
+
+	public List<ObjectNode> loadMatchedData(IEquipHolder equipHolder){
+		List<ObjectNode> matchedData = equipHolder.getMatchedData();
+		if(matchedData == null){
+			matchedData = findMatchedData(equipHolder);
+			if(matchedData == null) {
+				matchedData = Collections.EMPTY_LIST;
+			}
+			equipHolder.setMatchedData(matchedData);
+		}
+		return matchedData;
+	}
+
+	private List<ObjectNode> findMatchedData(IEquipHolder equipHolder){
+		ArrayList<ObjectNode> data = null;
+		for(ObjectNode obj : context.getOptionalObjs()) {
+			if (match(obj, equipHolder)){
+				if(data == null){
+					data = new ArrayList<>();
+				}
+				data.add(obj);
+			}
+		}
+		return data;
+	}
+
+	private void buildMainPipeAndNodes(){
+		for(MainPipe mainPipe : mainPipes) {
+			if (mainPipe.isBindEquipment()) {
+				List<ObjectNode> matchedData = mainPipe.getMatchedData();
+				if(CollUtil.isNotEmpty(matchedData)) {
+					orderDataList(matchedData);
+
+					ObjectNode obj = (ObjectNode)mainPipe.getMatchedData().get(0);
+					mainPipe.setDataObject(obj);
+					mainPipe.setDataObjectId(obj.get("id").asText());
+
+					context.getEquipMap().put(mainPipe.getDataObjectId(), mainPipe);
+				}
+			}
+		}
+		for(Container con : template.getContainers()) {
+			if(con.isEquipmentBox()) {
+				List<ObjectNode> matchedData = con.getMatchedData();
+				if(CollUtil.isNotEmpty(matchedData)) {
+					orderDataList(matchedData);
+
+					for(ObjectNode obj : matchedData) {
+						if(con.getEquipPack() != null) {
+							addPackData(con, obj);
+						} else {
+							addEquipNode(con, obj);
+						}
+					}
+				}
+			}
+		}
+	}
+
+	private void orderDataList(List<ObjectNode> dataList){
+		//TODO 对设备数据进行排序操作
+	}
+
+	private void addEquipNode(Container con, ObjectNode obj){
+		if(con.getChildren().size() > equipLimit) {
+			return;
+		}
+
+		EquipmentNode node = new EquipmentNode();
+		node.setId(IdUtil.fastSimpleUUID());
+		node.setDataObjectId(obj.get("id").asText());
+		node.setObjClassCode(DiagramBuilder.getClassCode(obj));
+		node.setDataObject(obj);
+
+		initNode(node, DiagramBuilder.getName(obj), con);
+
+		Legend legend = legendLoader.findLegend(node.getObjClassCode(), obj);
+		node.setLegendId(legend.getId());
+		node.setLegend(legend);
+	}
+
+	private void initNode(EquipmentNode node, String name, Container con){
+		con.addComp(node);
+		node.setContainerId(con.getId());
+		node.setLayoutIndex(con.getChildren().size() - 1);
+
+		Label label = new Label();
+		label.setId(IdUtil.fastSimpleUUID());
+		label.setContent(name);
+		node.setLabel(label);
+
+		diagram.getNodes().add(node);
+		context.getEquipMap().put(node.getDataObjectId(), node);
+	}
+
+	private void addPackData(Container con, ObjectNode obj){
+		PackNode pn = null;
+		String classCode = getClassCode(obj);
+		Legend legend = null;
+		//single
+		if(!con.getEquipPack().isPackByType()) {
+			String packName = con.getEquipPack().getPackName();
+			if(StrUtil.isBlank(packName)) {
+				packName = getTypeName(classCode);
+			}
+			if(con.getChildren().size() == 0) {
+				pn = newPackNode(PackNode.SINGLE_PACK, con, packName);
+			} else {
+				pn = (PackNode) con.getChildren().get(0);
+			}
+		} else { //group
+			for(IComponent comp : con.getChildren()) {
+				PackNode item = (PackNode) comp;
+				if(item.getObjClassCode().equals(classCode)){
+					pn = item;
+					break;
+				}
+			}
+			if(pn == null) {
+				pn = newPackNode(classCode, con, getTypeName(classCode));
+			}
+		}
+
+		if (pn.getLegend() == null) {
+			if(con.getEquipPack().getLegendId() != null) {
+				legend = legendLoader.findLegendById(con.getEquipPack().getLegendId(), classCode.substring(0, 4));
+			}
+			if(legend == null) {
+				legend = legendLoader.findLegend(classCode, null);
+			}
+
+			pn.setLegendId(legend.getId());
+			pn.setLegend(legend);
+		}
+
+		pn.add(classCode);
+	}
+
+	private PackNode newPackNode(String objClsCode, Container con, String packName){
+		PackNode pn = new PackNode();
+		pn.setId(IdUtil.fastSimpleUUID());
+		pn.setObjClassCode(objClsCode);
+
+		initNode(pn, packName, con);
+
+		return pn;
+	}
+
+	private boolean match(ObjectNode obj, IEquipHolder equipHolder) {
+		String classCode = DiagramBuilder.getClassCode(obj);
+		if(equipHolder.getEquipmentTypes() != null && equipHolder.getEquipmentTypes().contains(classCode)) {
+			DataFilter filter = equipHolder.getDataFilter();
+			if(filter != null) {
+				return filter.filter(obj, context);
+			}
+			return true;
+		}
+		return false;
+	}
+
+	/**
+	 * 对节点进行处理,并返回图例锚点会使用到的关系类型
+	 */
+	private void handleNodes() {
+		for(DiagramNode node : diagram.getNodes()) {
+			if(PackNode.TYPE.equals(node.getCompType())) {
+				PackNode pn = (PackNode)node;
+				pn.getLabel().setContent(pn.getLabel().getContent() + ":" + pn.totalCount());
+				statRelTypes(pn);
+			} else if(EquipmentNode.TYPE.equals(node.getCompType())) {
+				statRelTypes((EquipmentNode)node);
+			}
+		}
+	}
+
+	private void statRelTypes(EquipmentNode equipmentNode){
+		List<Anchor> anchors = equipmentNode.getLegend().getAnchors();
+		if(anchors != null) {
+			for(Anchor anchor : anchors) {
+				if(anchor.getAcceptRelations() != null) {
+					anchor.getAcceptRelations().forEach(rel -> refRelTypes.add(rel));
+				}
+			}
+		}
+	}
+
+	private void handleContainers() {
+		for(Container con : template.getContainers()) {
+			if(con.isEquipmentBox()) {
+				if(Boolean.TRUE.equals(con.getProp(Container.PROP_AUTO_HIDDEN)) && CollUtil.isEmpty(con.getChildren())) {
+					con.setHidden(true);
+				}
+			}
+		}
+	}
+
+	private String getTypeName(String classCode){
+		//TODO
+		return classCode;
+	}
+
+	public void buildLines(List<ObjectNode> optionalRels){
+		//去掉已经使用的关系项
+		if(context.getLineMap().size() > 0) {
+			optionalRels = optionalRels.stream().filter(obj -> !context.getLineMap().containsKey(obj.get("id").asText())).collect(Collectors.toList());
+		}
+
+		for(ObjectNode rel : optionalRels) {
+			IDataBind fromObj = context.getEquipMap().get(rel.get(ObjectRelation.OBJ_FROM_HUM).asText());
+			IDataBind toObj = context.getEquipMap().get(rel.get(ObjectRelation.OBJ_TO_HUM).asText());
+
+			if(fromObj != null && toObj != null) {
+				String relType = rel.get(ObjectRelation.GRAPH_CODE_HUM).asText() + '/' + rel.get(ObjectRelation.REL_CODE_HUM).asText();
+				ConnectPoint from = getConnectPoint(fromObj, relType, toObj);
+				if(from != null) {
+					ConnectPoint to = getConnectPoint(toObj, relType, fromObj);
+					if(to != null) {
+						Line line = new Line();
+						line.setFrom(from);
+						line.setTo(to);
+						line.setRelType(relType);
+						line.setDataObject(rel);
+						line.setDataObjectId(rel.get("id").asText());
+
+						addLine(line);
+					}
+				}
+			}
+		}
+
+	}
+
+	private ConnectPoint getConnectPoint(IDataBind obj, String  relType, IDataBind theOtherEnd){
+		ObjectNode theOtherData = (ObjectNode) theOtherEnd.getDataObject();
+		String theOtherType = getClassCode(theOtherData);
+
+		if(obj instanceof EquipmentNode) {
+			EquipmentNode en = (EquipmentNode)obj;
+			Anchor anchor = null;
+			List<Anchor> anchors = en.getLegend().getAnchors();
+			if(CollUtil.isNotEmpty(anchors)) {
+				Anchor anchor1 = null; //部分匹配
+				for (Anchor a : anchors) {
+					Boolean relMatch = null; //关系匹配
+					Boolean equipMatch = null; //另一端设备匹配
+
+					if(CollUtil.isNotEmpty(a.getAcceptRelations())) {
+						relMatch = a.getAcceptRelations().contains(relType);
+					}
+
+					if(!Boolean.FALSE.equals(relMatch)) {
+						if(CollUtil.isNotEmpty(a.getToEquipmentTypes())) {
+							equipMatch = a.getToEquipmentTypes().contains(theOtherType);
+						}
+						if(!Boolean.FALSE.equals(equipMatch) && a.getToDataFilter() != null) {
+							equipMatch = a.getToDataFilter().filter(theOtherData, context);
+						}
+					}
+
+					if(!Boolean.FALSE.equals(relMatch) && !Boolean.FALSE.equals(equipMatch)) {
+						if(relMatch == null || equipMatch == null) {
+							//部分匹配
+							if(anchor1 == null) {
+								anchor1 = a;
+							} else if(CollUtil.isNotEmpty(anchor1.getLines()) && CollUtil.isEmpty(a.getLines())) {
+								anchor1 = a;
+							}
+						} else {
+							//完全匹配
+							if(CollUtil.isEmpty(a.getLines())) { //优先每个锚点只有一条连线
+								anchor = a;
+								break;
+							} else if(anchor == null) {
+								anchor = a;
+							}
+						}
+					}
+				}
+				if(anchor == null && anchor1 != null) {
+					anchor = anchor1;
+				}
+			}
+
+			if(anchor != null) {
+				ConnectPoint cp = new ConnectPoint();
+				cp.setHostType(EquipmentNode.TYPE);
+				cp.setHostId(en.getId());
+				cp.setAnchorCode(anchor.getCode());
+				cp.setHostObj(en);
+				return cp;
+			}
+		} else if(obj instanceof MainPipe) {
+			MainPipe mp = (MainPipe)obj;
+			if(CollUtil.isEmpty(mp.getConnectEquips()) || mp.getConnectEquips().contains(theOtherType)) {
+				ConnectPoint cp = new ConnectPoint();
+				cp.setHostType(MainPipe.TYPE);
+				cp.setHostId(mp.getId());
+				cp.setHostObj(mp);
+				return cp;
+			}
+		}
+		return null;
+	}
+
+	private void addLine(Line line) {
+		line.setId(IdUtil.fastSimpleUUID());
+		markAnchorLine(line.getFrom(), line);
+		markAnchorLine(line.getTo(), line);
+		diagram.getLines().add(line);
+	}
+
+	private void markAnchorLine(ConnectPoint p, Line line){
+		EquipmentNode en = p.getEquipmentNode();
+		if (en != null) {
+			Anchor anchor = en.getLegend().getAnchor(p.getAnchorCode());
+			anchor.addLine(line);
+		}
+	}
+
+	public HashSet<String> getRefRelTypes() {
+		return refRelTypes;
+	}
+
+	public Diagram getDiagram() {
+		return diagram;
+	}
+
+	public static boolean hasRelationFilter(IEquipHolder equipHolder) {
+		DataFilter filter = equipHolder.getDataFilter();
+		if(filter != null) {
+			if (filter.getType() == DataFilter.TYPE_RELATION) {
+				return true;
+			}
+			if (filter instanceof LogicFilter) {
+				LinkedList<DataFilter> list = new LinkedList<>();
+				list.addAll(((LogicFilter) filter).getItems());
+
+				while (list.size() > 0) {
+					DataFilter item = list.removeFirst();
+					if(item.getType() == DataFilter.TYPE_RELATION){
+						return true;
+					} else if(item instanceof LogicFilter) {
+						list.addAll(((LogicFilter) item).getItems());
+					}
+				}
+			}
+		}
+		return false;
+	}
+
+	public static String getName(ObjectNode obj){
+		String name = null;
+		if(obj.get("localName") != null) {
+			name = obj.get("localName").asText();
+		}
+		if(StrUtil.isBlank(name) && obj.get("name") != null) {
+			name = obj.get("name").asText();
+		}
+		return name;
+	}
+
+	public static String getClassCode(ObjectNode obj){
+		if(obj != null && obj.get("classCode") != null) {
+			return obj.get("classCode").asText();
+		}
+		return null;
+	}
+
+}

+ 169 - 0
adm-business/adm-diagram/src/main/java/com/persagy/adm/diagram/core/build/DiagramDataLoader.java

@@ -0,0 +1,169 @@
+package com.persagy.adm.diagram.core.build;
+
+import cn.hutool.core.collection.CollUtil;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+import com.persagy.adm.diagram.core.DataStrategy;
+import com.persagy.adm.diagram.core.model.Diagram;
+import com.persagy.adm.diagram.core.model.DiagramNode;
+import com.persagy.adm.diagram.core.model.EquipmentNode;
+import com.persagy.adm.diagram.core.model.Line;
+import com.persagy.adm.diagram.core.model.base.Container;
+import com.persagy.adm.diagram.core.model.template.MainPipe;
+import com.persagy.dmp.common.constant.ValidEnum;
+import com.persagy.dmp.digital.entity.ObjectRelation;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.stream.Collectors;
+
+/**
+ * 系统图数据加载
+ * @author zhaoyk
+ */
+public class DiagramDataLoader {
+
+	private Diagram diagram;
+
+	private DataStrategy dataStrategy;
+
+	private LegendLoader legendLoader;
+
+	public DiagramDataLoader(Diagram diagram, DataStrategy dataStrategy) {
+		this.diagram = diagram;
+		this.dataStrategy = dataStrategy;
+		this.legendLoader = new LegendLoader(diagram, dataStrategy);
+	}
+
+	/**
+	 * 加载已经保存的数据
+	 */
+	public void initLoad(){
+		List<String> objIds = diagram.getObjIds();
+		if(objIds.size() > 0) {
+			List<ObjectNode> objs = dataStrategy.loadObjectsById(objIds, diagram.getProjectId(), diagram.getGroupCode());
+			initNodes(objs);
+		}
+
+		List<String> relIds = diagram.getRelIds();
+		if(relIds.size() > 0) {
+			List<ObjectNode> rels = dataStrategy.loadRelationsById(relIds, diagram.getProjectId(), diagram.getGroupCode());
+			initLines(rels);
+		}
+	}
+
+	private void initNodes(List<ObjectNode> objs) {
+		HashMap<String, ObjectNode> objMap = new HashMap<>();
+		if(objs != null) {
+			objs.forEach(obj -> objMap.put(obj.get("id").asText(), obj));
+		}
+
+		for (DiagramNode node : diagram.getNodes()) {
+			ObjectNode obj = null;
+
+			if(EquipmentNode.TYPE.equals(node.getCompType())) { //设备节点
+				EquipmentNode en = (EquipmentNode) node;
+				obj = objMap.get(en.getDataObjectId());
+
+				en.setLegend(legendLoader.findLegendById(en.getLegendId(), en.getObjSystemCode()));
+
+				//设置anchorLocations
+				en.setAnchorLocations();
+			}
+
+			if(obj != null) {
+				node.setDataObject(obj);
+			}
+		}
+
+		//设备类干管
+		if(diagram.getTemplate().getMainPipes() != null) {
+			for(MainPipe mainPipe : diagram.getTemplate().getMainPipes()) {
+				if(mainPipe.isBindEquipment()){
+					ObjectNode obj = objMap.get(mainPipe.getDataObjectId());
+					if(obj != null) {
+						mainPipe.setDataObject(obj);
+					}
+				}
+			}
+		}
+	}
+
+	private void initLines(List<ObjectNode> rels){
+		HashMap<String, ObjectNode> relMap = new HashMap<>();
+		if(rels != null) {
+			rels.forEach(obj -> relMap.put(obj.get("id").asText(), obj));
+		}
+
+		for(Line line : diagram.getLines()) {
+			if(line.getDataObjectId() != null){
+				ObjectNode rel = relMap.get(line.getDataObjectId());
+				if(rel != null) {
+					line.setDataObject(rel);
+					line.setDataObjectId(rel.get("id").asText());
+				}
+			}
+		}
+	}
+
+	/**
+	 * 搜索数据并自动加载
+	 */
+	public void autoLoad(CalcContext context) {
+		List<ObjectNode> optionalObjs = null;
+		List<ObjectNode> optionalRels = null;
+
+		//查询备选数据
+		HashSet<String> equipTypes = new HashSet<>();
+		List<Container> containers = diagram.getTemplate().getContainers();
+		for(Container con : containers) {
+			if(con.getEquipmentTypes() != null) {
+				equipTypes.addAll(con.getEquipmentTypes());
+			}
+		}
+		if(diagram.getTemplate().getMainPipes() != null) {
+			for(MainPipe mainPipe : diagram.getTemplate().getMainPipes()) {
+				if(mainPipe.getEquipmentTypes() != null) {
+					equipTypes.addAll(mainPipe.getEquipmentTypes());
+				}
+			}
+		}
+
+		//TODO 打包设备查询性能优化
+		if(equipTypes.size() > 0) {
+			optionalObjs = dataStrategy.loadObjectsByType(new ArrayList<>(equipTypes), diagram.getProjectId(), diagram.getSystemId(), diagram.getGroupCode());
+			optionalObjs = filterValid(optionalObjs);
+		}
+		context.setOptionalObjs(optionalObjs);
+
+		context.setDataStrategy(dataStrategy);
+		legendLoader.setContext(context);
+		DiagramBuilder builder = new DiagramBuilder(context, legendLoader);
+
+		builder.buildStructure();
+		List<String[]> relTypes = builder.getRefRelTypes().stream().map(type -> type.split("/")).collect(Collectors.toList());
+
+		List<String> objIds = new ArrayList<>(context.getEquipMap().keySet());
+		//TODO 关系查询,需要区分打包设备
+		if(objIds.size() > 0 && relTypes.size() > 0) {
+			optionalRels = dataStrategy.loadRelationsByType(relTypes, objIds, diagram.getProjectId(), diagram.getGroupCode());
+			optionalRels = filterValid(optionalRels);
+		}
+		if(optionalRels == null){
+			optionalRels = new ArrayList<>();
+		}
+
+		builder.buildLines(optionalRels);
+	}
+
+	private List<ObjectNode> filterValid(List<ObjectNode> list){
+		if(CollUtil.isEmpty(list))
+			return list;
+
+		return list.stream().filter(obj ->
+				obj.get(ObjectRelation.PROP_VALID) == null || obj.get(ObjectRelation.PROP_VALID).asInt() == ValidEnum.TRUE.getType())
+				.collect(Collectors.toList());
+	}
+
+}

+ 98 - 0
adm-business/adm-diagram/src/main/java/com/persagy/adm/diagram/core/build/LegendLoader.java

@@ -0,0 +1,98 @@
+package com.persagy.adm.diagram.core.build;
+
+import cn.hutool.core.collection.CollUtil;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+import com.persagy.adm.diagram.core.DataStrategy;
+import com.persagy.adm.diagram.core.model.Diagram;
+import com.persagy.adm.diagram.core.model.legend.Legend;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+
+/**
+ * 图例加载
+ * @author zhaoyk
+ */
+public class LegendLoader {
+
+	private Diagram diagram;
+
+	private DataStrategy dataStrategy;
+
+	private CalcContext context;
+
+	private HashMap<String, List<Legend>> legendsCache = new HashMap<>();
+
+	public LegendLoader(Diagram diagram, DataStrategy dataStrategy) {
+		this.diagram = diagram;
+		this.dataStrategy = dataStrategy;
+	}
+
+	public void setContext(CalcContext context) {
+		this.context = context;
+	}
+
+	public Legend findLegend(String classCode, ObjectNode obj){
+		List<Legend> legends = legendsCache.get(classCode);
+		if (legends == null) {
+			legends = dataStrategy.getLegendsForEquipment(classCode);
+			if (legends == null) {
+				legends = new ArrayList<>();
+			}
+			legendsCache.put(classCode, legends);
+		}
+		if(legends.size() > 0) {
+			Legend l0 = null, l1 = null;
+			for(Legend legend : legends) {
+				//系统图类型匹配
+				boolean typeMatch = CollUtil.isNotEmpty(legend.getDiagramTypes()) && legend.getDiagramTypes().contains(diagram.getType());
+				//过滤条件匹配
+				boolean filterMatch;
+				if(obj != null && legend.getDataFilter() != null) {
+					filterMatch = legend.getDataFilter().filter(obj, context);
+				} else {
+					filterMatch = true;
+				}
+
+				if(typeMatch && filterMatch) {
+					return legend;
+				}
+
+				if(typeMatch && l0 == null) {
+					l0 = legend;
+				}
+				if(filterMatch && l1 == null) {
+					l1 = legend;
+				}
+			}
+			if(l0 != null) {
+				return l0;
+			}
+			if(l1 != null) {
+				return l1;
+			}
+			//没有优先匹配的话取第一个
+			return legends.get(0);
+		}
+
+		//返回一个缺省图例
+		return emptyLegend();
+	}
+
+	public Legend findLegendById(String legendId, String systemCode) {
+		Legend legend = null;
+		if(legendId != null) {
+			legend = dataStrategy.getLegend(legendId, systemCode); //TODO 批量查询优化
+		}
+		return legend != null ? legend : emptyLegend();
+	}
+
+	public static Legend emptyLegend(){
+		Legend l = new Legend();
+		l.setWidth(100);
+		l.setHeight(100);
+		return l;
+	}
+
+}