Kaynağa Gözat

完善端子检查功能

liling 1 yıl önce
ebeveyn
işleme
561b4ec296

+ 1 - 1
service/controllers/busAdminController.go

@@ -389,7 +389,7 @@ func (c *BusAdminController) UPdateSysModelIedType() {
 	}
 	oldtype := c.GetString("old_iedtype")
 	newtype := c.GetString("new_iedtype")
-	if oldtype == "" || newtype == "" {
+	if oldtype == "" {
 		c.Data["json"] = c.ResultError("装置类型编码不能为空!")
 		c.ServeJSON()
 		return

+ 6 - 0
service/models/bo/check_sysmodel_ied_func_fcda.go

@@ -165,6 +165,9 @@ func (c *SysCheckModelIedFuncFcdaMgr) AutoRelation(modelid int, relationrow []T_
 					}
 				}
 				if r, h := fcdaMap[key]; h {
+					if r.FuncFcdaId == row.FuncFcdaId {
+						continue
+					}
 					fr.Model.FromFcdaId = row.FuncFcdaId
 					fr.Model.ToFcdaId = r.FuncFcdaId
 					fr.Model.FromFuncId = row.Id
@@ -211,6 +214,9 @@ func (c *SysCheckModelIedFuncFcdaMgr) AutoRelation(modelid int, relationrow []T_
 			}
 			fromrow = item2["fromrow"].(T_data_model_func_def)
 			r := relationrow[rowno]
+			if r.FuncFcdaId == fromrow.FuncFcdaId || r.IedType == fromrow.IedType {
+				continue
+			}
 			fr.Model.FromFcdaId = fromrow.FuncFcdaId
 			fr.Model.ToFcdaId = r.FuncFcdaId
 			fr.Model.FromFuncId = fromrow.Id

+ 195 - 130
service/models/bo/checktools_area.go

@@ -460,6 +460,7 @@ func (c *CheckAreaMgr) CheckIedFcda() error {
 	if scdXmlObj == nil {
 		return errors.New("无效的SCD")
 	}
+	scdidStr := fmt.Sprintf("%d", c.ScdId)
 	db := orm.NewOrm()
 	//获取当前站的各电压等级
 	volRows := []orm.Params{}
@@ -561,11 +562,12 @@ func (c *CheckAreaMgr) CheckIedFcda() error {
 		return ied_type, vol
 	}
 	//从间隔装置中过滤出指定类型的IED
-	var filterAreaIeds = func(ied_type, volLevelCode string, s1 []orm.Params) []orm.Params {
+	var filterAreaIeds = func(ied_type, volLevelCode string, s1 []orm.Params, abCode string, isPm ...bool) []orm.Params {
 		iedlst := []orm.Params{}
 		if volLevelCode == "" || (volLevelCode != "H" && volLevelCode != "M" && volLevelCode != "L") {
 			for _, r := range s1 {
-				if strings.HasPrefix(tools.IsEmpty(r["ied_name"]), ied_type) {
+				iedname := tools.IsEmpty(r["ied_name"])
+				if strings.HasPrefix(iedname, ied_type) || (abCode != "" && strings.HasSuffix(iedname, abCode)) {
 					iedlst = append(iedlst, r)
 				}
 			}
@@ -573,11 +575,20 @@ func (c *CheckAreaMgr) CheckIedFcda() error {
 			tmpLst := map[string]orm.Params{}
 			for _, r := range s1 {
 				iedname := tools.IsEmpty(r["ied_name"])
-				if strings.HasPrefix(iedname, ied_type) {
+				if strings.HasPrefix(iedname, ied_type) || (abCode != "" && strings.HasSuffix(iedname, abCode)) {
 					tmpLst[iedname] = r
 				}
 			}
-			h, m, l := c.getIedListByVol(ied_type, tmpLst, volMap)
+			//PM类型的装置需要单独处理
+			if len(isPm) > 0 {
+				//只有1台PM装置时,不用区分电压等级直接返回
+				if len(tmpLst) == 1 {
+					for _, r1 := range tmpLst {
+						return []orm.Params{r1}
+					}
+				}
+			}
+			h, m, l := c.getIedListByVol(ied_type, tmpLst, volMap, abCode)
 			if volLevelCode == "H" {
 				iedlst = h
 			}
@@ -592,6 +603,12 @@ func (c *CheckAreaMgr) CheckIedFcda() error {
 	}
 	//iedRelationMgr := new(SysCheckModelIedRelationMgr)
 	modelFcda := sync.Map{}
+	//已经加载过遥信的装置
+	isLoadYxIed := map[string]map[*node_attr.NExtRef]string{}
+	//已经处理过遥信端子的装置
+	isDealYxIed := map[string]int{}
+	//已获取端子的装置
+	isLoadExtref := map[string]map[string]*node_attr.NExtRef{}
 	for _, row := range arealist {
 		//获取间隔标准装置及关系
 		modelid, _ := strconv.Atoi(tools.IsEmpty(row["model_id"]))
@@ -623,46 +640,44 @@ func (c *CheckAreaMgr) CheckIedFcda() error {
 		s1 := []orm.Params{}
 		_, err = db.Raw("select ied_name from t_data_check_area_ied where area_id=?", area_id).Values(&s1)
 		typeMappingMgr := new(SysCheckModelIedtypeMappingMgr)
+		pmCode := c.getIedTypeCode(modelid, "PM")
 		//循环处理关联关系
 		for to_ied_type2, refrow := range funclist {
+			logger.Logger.Debug(fmt.Sprintf("================正在检查类型%s关系===============", to_ied_type2))
 			ts := strings.Split(to_ied_type2, ",")
 			//从间隔中获取当前同类型的信号接收装置
 			ied_type := c.getIedTypeCode(modelid, ts[0])
 			if v, h := groupList[ied_type]; h {
-				ied_type = v
-			}
-			mappresult := typeMappingMgr.GetMappingType(modelid, ied_type)
-			if mappresult != "" {
-				ied_type = mappresult
+				ied_type = typeMappingMgr.GetMappingType(modelid, v)
 			}
+			abCode := c.getIedTypeABCode(ied_type)
 			ied_type, vol := getIedTypeAndVolCode(ied_type)
-			iedlst := filterAreaIeds(ied_type, vol, s1)
-			logger.Logger.Debug(fmt.Sprintf("正在检查装置类型:%s的装置(%+v)端子关系", ied_type, iedlst))
+			isReceivePm := strings.HasPrefix(ied_type, pmCode) //接收装置是Pm装置
+			iedlst := filterAreaIeds(ied_type, vol, s1, abCode, isReceivePm)
+			logger.Logger.Debug(fmt.Sprintf("装置类型%s#%s与%s的装置(%+v)端子关系", ied_type, vol, ts[1], iedlst))
 			//从间隔中获取当前信号输出装置
 			outiedlist := map[string]orm.Params{}
 			fromiedcode := c.getIedTypeCode(modelid, ts[1])
 			if v, h := groupList[fromiedcode]; h {
-				fromiedcode = v
-			}
-			mappresult = typeMappingMgr.GetMappingType(modelid, fromiedcode)
-			if mappresult != "" {
-				fromiedcode = mappresult
+				fromiedcode = typeMappingMgr.GetMappingType(modelid, v)
 			}
+			fromAbCode := c.getIedTypeABCode(fromiedcode)
 			from_ied_type, vol2 := getIedTypeAndVolCode(fromiedcode)
-			outiedlist2 := filterAreaIeds(from_ied_type, vol2, s1)
+			isOutPm := strings.HasPrefix(from_ied_type, pmCode) //输出装置是PM装置
+			outiedlist2 := filterAreaIeds(from_ied_type, vol2, s1, fromAbCode, isOutPm)
 			for _, iedrow := range outiedlist2 {
 				iedname := tools.IsEmpty(iedrow["ied_name"])
 				outiedlist[iedname] = iedrow
 			}
 			if len(outiedlist) == 0 {
-				logger.Logger.Debug(fmt.Sprintf("装置类型%s未发现输出任何装置", ied_type))
+				logger.Logger.Debug(fmt.Sprintf("装置类型%s#%s%s未从类型%s%s的任何电压等级%s装置接收信号", ied_type, vol, abCode, from_ied_type, fromAbCode, vol2))
 				continue
 			}
-			logger.Logger.Debug(fmt.Sprintf("当前装置类型:%s的装置(%+v)存在以下信号输出装置:%+v", ied_type, iedlst, outiedlist))
+			logger.Logger.Debug(fmt.Sprintf("当前装置类型:%s%s的装置(%+v)与类型%s#%s%s存在以下信号输出装置:%+v", ied_type, abCode, iedlst, from_ied_type, fromAbCode, vol2, outiedlist))
 			//logger.Logger.Debug(fmt.Sprintf("设计端子关联关系:%+v", refrow))
 			for _, ied := range iedlst {
 				iedname := tools.IsEmpty(ied["ied_name"])
-				iedObj := scdNodeMgr.GetIed(scdXmlObj, fmt.Sprintf("%d", c.ScdId), iedname)
+				iedObj := scdNodeMgr.GetIed(scdXmlObj, scdidStr, iedname)
 				iedObjDesc := ""
 				if iedObj != nil {
 					iedObjDesc = iedObj.Desc
@@ -670,31 +685,41 @@ func (c *CheckAreaMgr) CheckIedFcda() error {
 					logger.Logger.Error(fmt.Sprintf("信号接收装置%s未找到!", iedname))
 					continue
 				}
-				extreflist := getIedExtRefs(iedname)
+				//遥信端子列表:仅根据scd实际配置检查其双方端子的doi名称是否相同
+				scdMgr := new(ScdMgr)
+				yx, h := isLoadYxIed[iedname]
+				if !h {
+					yx = scdMgr.GetYxExtref(scdXmlObj, c.ScdId, iedname)
+					isLoadYxIed[iedname] = yx
+				}
+				yxAddrMap := map[string]int{}
+				for sg, _ := range yx {
+					key := fmt.Sprintf("%s%s%s%s%s%s%s", sg.IedName, sg.IntAddr, sg.LdInst, sg.Prefix, sg.LnClass, sg.LnInst, sg.DoName)
+					yxAddrMap[key] = 1
+				}
+				//所有端子列表
+				extreflist := isLoadExtref[iedname]
+				if extreflist == nil {
+					extreflist = getIedExtRefs(iedname)
+				}
 				for outiedname, _ := range outiedlist {
 					if iedname == outiedname {
 						continue
 					}
 					logger.Logger.Debug(fmt.Sprintf("正在匹配装置%s与%s的端子关联关系", iedname, outiedname))
-					outIedObj := scdNodeMgr.GetIed(scdXmlObj, fmt.Sprintf("%d", c.ScdId), outiedname)
-					//outIedObjDesc := ""
+					outIedObj := scdNodeMgr.GetIed(scdXmlObj, scdidStr, outiedname)
+					outIedObjDesc := ""
 					if outIedObj == nil {
 						logger.Logger.Error(fmt.Sprintf("信号输出装置%s未找到!", outiedname))
 					} else {
-						//outIedObjDesc = outIedObj.Desc
+						outIedObjDesc = outIedObj.Desc
 					}
 					if iedObj == nil && outIedObj == nil {
 						continue
 					}
 					outiedFcdaList := getIedFcdas(outiedname) //输入装置的信号输出端子
 					//检查是否有错误和缺失的端子
-					hasYaoXinFunc := false //是否具备遥信功能
 					for _, r := range refrow {
-						//判断装置是否具备遥信功能,具备时需要单独处理遥信类端子
-						//遥信类端子处理规则:以scd中实际配置为准,通过检查装置双方的端子名称是否完全一致,以及是否有缺失和多余的遥信端子
-						if strings.Contains(tools.IsEmpty(r["to_func_name"]), "遥信") || strings.Contains(tools.IsEmpty(r["from_func_name"]), "遥信") {
-							hasYaoXinFunc = true
-						}
 						extref_name := tools.IsEmpty(r["to_fcda_name"])
 						fcda_name := tools.IsEmpty(r["from_fcda_name"])
 						extref_name_exp := tools.IsEmpty(r["to_fcda_match_exp"])
@@ -703,14 +728,24 @@ func (c *CheckAreaMgr) CheckIedFcda() error {
 						extrefObj := new(node_attr.NExtRef)
 						fcda2Exist := false
 						fcdaObj := new(node_attr.NFCDA)
+						isYx := false
 						for desc, item := range extreflist {
 							//logger.Logger.Debug(fmt.Sprintf("接收装置%s的设计端子%s匹配SCD ExtRef DO端子%s", iedname, extref_name_exp, desc))
 							if tools.RexGroupTestMatch(extref_name_exp, desc) {
+								//判断是否是遥信端子
+								if yxAddrMap[fmt.Sprintf("%s%s%s%s%s%s%s", item.IedName, item.IntAddr, item.LdInst, item.Prefix, item.LnClass, item.LnInst, item.DoName)] == 1 {
+									isYx = true
+									break
+								}
 								funcExist = true
 								extrefObj = item
 								break
 							}
 						}
+						if isYx {
+							//遥信端子不处理
+							continue
+						}
 						if funcExist {
 							//判断输出端端子是否存在
 							for desc, item := range outiedFcdaList {
@@ -722,53 +757,33 @@ func (c *CheckAreaMgr) CheckIedFcda() error {
 								}
 							}
 						}
-						if !funcExist || !fcda2Exist {
-							if !funcExist {
-								parse_result := fmt.Sprintf("间隔%s的装置%s缺失端子%s", area_name, iedname, extref_name)
-								re := map[string]interface{}{"scdid": c.ScdId, "lineno": 0, "ruleid": area_ruleid, "nodeid": 0, "parse_result": parse_result,
-									"ied_name":      iedname,
-									"ied_desc":      iedObjDesc,
-									"out_ied_name":  "",
-									"out_ied_desc":  "",
-									"fcda_desc":     extref_name,
-									"fcda_addr":     "",
-									"out_fcda_desc": "",
-									"out_fcda_addr": "",
-									"error_type":    "3",
-								}
-								//检查未通过
-								scdNodeRule.AppendFcdaCheckResult(re)
+						if !funcExist {
+							parse_result := fmt.Sprintf("间隔%s的装置%s缺失端子%s", area_name, iedname, extref_name)
+							re := map[string]interface{}{"scdid": c.ScdId, "lineno": 0, "ruleid": area_ruleid, "nodeid": 0, "parse_result": parse_result,
+								"ied_name":      iedname,
+								"ied_desc":      iedObjDesc,
+								"out_ied_name":  "",
+								"out_ied_desc":  "",
+								"fcda_desc":     extref_name,
+								"fcda_addr":     "",
+								"out_fcda_desc": "",
+								"out_fcda_addr": "",
+								"error_type":    "3",
 							}
-							/*
-								if !fcda2Exist {
-									parse_result := fmt.Sprintf("间隔%s的装置%s缺失端子%s", area_name, outiedname, fcda_name)
-									re := map[string]interface{}{"scdid": c.ScdId, "lineno": 0, "ruleid": area_ruleid, "nodeid": 0, "parse_result": parse_result,
-										"ied_name":      "",
-										"ied_desc":      "",
-										"out_ied_name":  outiedname,
-										"out_ied_desc":  outIedObjDesc,
-										"fcda_desc":     "",
-										"fcda_addr":     "",
-										"out_fcda_desc": fcda_name,
-										"out_fcda_addr": "",
-										"error_type":    "3",
-									}
-									//检查未通过
-									scdNodeRule.AppendFcdaCheckResult(re)
-								}
-							*/
+							//检查未通过
+							scdNodeRule.AppendFcdaCheckResult(re)
 						} else {
 							//检查端子是否关联正确
-							if fcda2Exist && (extrefObj.LdInst != fcdaObj.LdInst ||
+							//fcda2Exist为false表示实际的端子from ied不是当前设计的ied
+							//fcda2Exist为true时,进一步判断端子地址是否与设计相符
+							if !fcda2Exist || (extrefObj.LdInst != fcdaObj.LdInst ||
 								extrefObj.LnInst != fcdaObj.LnInst ||
 								extrefObj.LnClass != fcdaObj.LnClass ||
 								extrefObj.Prefix != fcdaObj.Prefix ||
 								extrefObj.DoName != fcdaObj.DoName) {
 								//不正确
-								logger.Logger.Debug(fmt.Sprintf("extref_name:%s extref_name_exp:%s extrefObj:%+v", extref_name, extref_name_exp, extrefObj))
-								logger.Logger.Debug(fmt.Sprintf("fcda_name:%s fcda_name_exp:%s fcdaObj:%+v", fcda_name, fcda_name_exp, fcdaObj))
-								iedObj := scdNodeMgr.GetIed(scdXmlObj, fmt.Sprintf("%d", c.ScdId), iedname)
-								outIedObj := scdNodeMgr.GetIed(scdXmlObj, fmt.Sprintf("%d", c.ScdId), outiedname)
+								//logger.Logger.Debug(fmt.Sprintf("extref_name:%s extref_name_exp:%s extrefObj:%+v", extref_name, extref_name_exp, extrefObj))
+								//logger.Logger.Debug(fmt.Sprintf("fcda_name:%s fcda_name_exp:%s fcdaObj:%+v", fcda_name, fcda_name_exp, fcdaObj))
 								parse_result := fmt.Sprintf("间隔%s的装置%s端子%s与装置%s端子%s关联错误", area_name, iedname, extref_name, outiedname, fcda_name)
 								daname := ""
 								if fcdaObj.DaName != "" {
@@ -778,7 +793,7 @@ func (c *CheckAreaMgr) CheckIedFcda() error {
 									"ied_name":      iedname,
 									"ied_desc":      iedObj.Desc,
 									"out_ied_name":  outiedname,
-									"out_ied_desc":  outIedObj.Desc,
+									"out_ied_desc":  outIedObjDesc,
 									"fcda_desc":     extref_name,
 									"fcda_addr":     extrefObj.IntAddr,
 									"out_fcda_desc": fcda_name,
@@ -792,13 +807,14 @@ func (c *CheckAreaMgr) CheckIedFcda() error {
 					}
 					//检查是否有多余(SCD中有不存在于设计中)的端子
 					for extrefdesc, r := range extreflist { //scd中的端子关系
+						//判断是否是遥信端子
+						if yxAddrMap[fmt.Sprintf("%s%s%s%s%s%s%s", r.IedName, r.IntAddr, r.LdInst, r.Prefix, r.LnClass, r.LnInst, r.DoName)] == 1 {
+							continue
+						}
 						extref_name := ""
 						fcda_name := ""
 						isHave := false
 						for _, r1 := range refrow { //设计的端子关系
-							if strings.Contains(tools.IsEmpty(r1["to_func_name"]), "遥信") || strings.Contains(tools.IsEmpty(r1["from_func_name"]), "遥信") {
-								hasYaoXinFunc = true
-							}
 							extref_name_exp := tools.IsEmpty(r1["to_fcda_match_exp"])
 							if extrefdesc == extref_name_exp || tools.RexGroupTestMatch(extref_name_exp, extrefdesc) {
 								//端子存在
@@ -815,16 +831,17 @@ func (c *CheckAreaMgr) CheckIedFcda() error {
 							if doi != nil {
 								extref_name = doi.(*node_attr.NDOI).Desc
 							}
-							doi, _ = scdNodeRule.IedFcdaExist(outiedname, r.LdInst, r.LnClass, r.LnInst, r.Prefix, r.DoName, "")
+							doi, _ = scdNodeRule.IedFcdaExist(r.IedName, r.LdInst, r.LnClass, r.LnInst, r.Prefix, r.DoName, "")
 							if doi != nil {
 								fcda_name = doi.(*node_attr.NDOI).Desc
 							}
+							tmpIedObj := scdNodeMgr.GetIed(scdXmlObj, scdidStr, r.IedName)
 							parse_result := fmt.Sprintf("间隔%s下装置(%s)的端子%s在设计中不存在", area_name, iedname, extref_name)
 							re := map[string]interface{}{"scdid": c.ScdId, "lineno": 0, "ruleid": area_ruleid, "nodeid": 0, "parse_result": parse_result,
 								"ied_name":      iedname,
 								"ied_desc":      iedObj.Desc,
-								"out_ied_name":  outiedname,
-								"out_ied_desc":  outIedObj.Desc,
+								"out_ied_name":  r.IedName,
+								"out_ied_desc":  tmpIedObj.Desc,
 								"fcda_desc":     extref_name,
 								"fcda_addr":     r.IntAddr,
 								"out_fcda_desc": fcda_name,
@@ -835,62 +852,62 @@ func (c *CheckAreaMgr) CheckIedFcda() error {
 							scdNodeRule.AppendFcdaCheckResult(re)
 						}
 					}
-					if hasYaoXinFunc {
-						//遥信端子检查:仅根据scd实际配置检查其双方端子的doi名称是否相同
-						scdMgr := new(ScdMgr)
-						yx := scdMgr.GetYxExtref(scdXmlObj, c.ScdId, iedname)
-						if len(yx) > 0 {
-							for extref, doiDesc := range yx {
-								if extref.IedName != outiedname {
-									//非当前输入装置的,不处理
-									continue
-								}
-								daname := ""
-								if extref.DaName != "" {
-									daname = "." + extref.DaName
-								}
-								//获取输出端端子名称
-								outFcdaDoi, _ := scdNodeRule.IedFcdaExist(extref.IedName, extref.LdInst, extref.LnClass, extref.LnInst, extref.Prefix, extref.DoName, extref.DaName)
-								if outFcdaDoi == nil {
-									//端子缺失
-									parse_result := fmt.Sprintf("间隔%s的装置%s端子%s的关联端子不存在", area_name, iedname, doiDesc)
-									re := map[string]interface{}{"scdid": c.ScdId, "lineno": 0, "ruleid": area_ruleid, "nodeid": 0, "parse_result": parse_result,
-										"ied_name":      iedname,
-										"ied_desc":      iedObj.Desc,
-										"out_ied_name":  outiedname,
-										"out_ied_desc":  outIedObj.Desc,
-										"fcda_desc":     doiDesc,
-										"fcda_addr":     extref.IntAddr,
-										"out_fcda_desc": "",
-										"out_fcda_addr": fmt.Sprintf("%s/%s%s%s.%s%s", extref.LdInst, extref.Prefix, extref.LnClass, extref.LnInst, extref.DoName, daname),
-										"error_type":    "3",
-									}
-									//检查未通过
-									scdNodeRule.AppendFcdaCheckResult(re)
-								} else if outFcdaDoi.(*node_attr.NDOI).Desc != doiDesc {
-									//关联错误
-									parse_result := fmt.Sprintf("间隔%s的装置%s端子%s与装置%s端子%s名称不匹配", area_name, iedname, doiDesc, outiedname, outFcdaDoi.(*node_attr.NDOI).Desc)
-									re := map[string]interface{}{"scdid": c.ScdId, "lineno": 0, "ruleid": area_ruleid, "nodeid": 0, "parse_result": parse_result,
-										"ied_name":      iedname,
-										"ied_desc":      iedObj.Desc,
-										"out_ied_name":  outiedname,
-										"out_ied_desc":  outIedObj.Desc,
-										"fcda_desc":     doiDesc,
-										"fcda_addr":     extref.IntAddr,
-										"out_fcda_desc": outFcdaDoi.(*node_attr.NDOI).Desc,
-										"out_fcda_addr": fmt.Sprintf("%s/%s%s%s.%s%s", extref.LdInst, extref.Prefix, extref.LnClass, extref.LnInst, extref.DoName, daname),
-										"error_type":    "2",
-									}
-									//检查未通过
-									scdNodeRule.AppendFcdaCheckResult(re)
-								}
+				}
+				//判断装置是否具备遥信功能,具备时需要单独处理遥信类端子
+				//遥信类端子处理规则:以scd中实际配置为准,通过检查装置双方的端子名称是否完全一致,以及是否有缺失和多余的遥信端子
+				if len(yx) > 0 && isDealYxIed[iedname] == 0 {
+					//logger.Logger.Debug(fmt.Sprintf("=============正在处理装置%s遥信端子数:%d  %+v", iedname, len(yx), isDealYxIed))
+					isDealYxIed[iedname] = 1
+					for extref, doiDesc := range yx {
+						daname := ""
+						if extref.DaName != "" {
+							daname = "." + extref.DaName
+						}
+						//获取输出端端子名称
+						outFcdaDoi, _ := scdNodeRule.IedFcdaExist(extref.IedName, extref.LdInst, extref.LnClass, extref.LnInst, extref.Prefix, extref.DoName, extref.DaName)
+						tmpIedObj := scdNodeMgr.GetIed(scdXmlObj, scdidStr, extref.IedName)
+						if tmpIedObj == nil || outFcdaDoi == nil {
+							//端子缺失
+							parse_result := fmt.Sprintf("间隔%s的装置%s遥信端子%s的关联端子不存在", area_name, iedname, doiDesc)
+							re := map[string]interface{}{"scdid": c.ScdId, "lineno": 0, "ruleid": area_ruleid, "nodeid": 0, "parse_result": parse_result,
+								"ied_name":      iedname,
+								"ied_desc":      iedObj.Desc,
+								"out_ied_name":  tmpIedObj.Name,
+								"out_ied_desc":  tmpIedObj.Desc,
+								"fcda_desc":     doiDesc,
+								"fcda_addr":     extref.IntAddr,
+								"out_fcda_desc": "",
+								"out_fcda_addr": fmt.Sprintf("%s/%s%s%s.%s%s", extref.LdInst, extref.Prefix, extref.LnClass, extref.LnInst, extref.DoName, daname),
+								"error_type":    "3",
 							}
+							//检查未通过
+							scdNodeRule.AppendFcdaCheckResult(re)
+						} else if outFcdaDoi.(*node_attr.NDOI).Desc != doiDesc {
+							//关联错误
+							parse_result := fmt.Sprintf("间隔%s的装置%s遥信端子%s与装置%s端子%s名称不匹配", area_name, iedname, doiDesc, tmpIedObj.Name, outFcdaDoi.(*node_attr.NDOI).Desc)
+							re := map[string]interface{}{"scdid": c.ScdId, "lineno": 0, "ruleid": area_ruleid, "nodeid": 0, "parse_result": parse_result,
+								"ied_name":      iedname,
+								"ied_desc":      iedObj.Desc,
+								"out_ied_name":  tmpIedObj.Name,
+								"out_ied_desc":  tmpIedObj.Desc,
+								"fcda_desc":     doiDesc,
+								"fcda_addr":     extref.IntAddr,
+								"out_fcda_desc": outFcdaDoi.(*node_attr.NDOI).Desc,
+								"out_fcda_addr": fmt.Sprintf("%s/%s%s%s.%s%s", extref.LdInst, extref.Prefix, extref.LnClass, extref.LnInst, extref.DoName, daname),
+								"error_type":    "2",
+							}
+							//检查未通过
+							scdNodeRule.AppendFcdaCheckResult(re)
 						}
 					}
 				}
+				yxAddrMap = nil
+				extreflist = nil
 			}
 		}
 	}
+	isLoadYxIed = nil
+	isDealYxIed = nil
 	scdNodeRule.CheckFinish()
 	scdNodeRule.Flush()
 	return nil
@@ -1000,7 +1017,7 @@ func (c *CheckAreaMgr) ParseModelArea() {
 	}
 }
 
-//根据默认装置类型获取其类型编码。如果没有自定义编码,则返回默认编码
+//根据默认装置类型获取其类型编码。如果没有自定义编码,则返回默认编码。返回编码时已自动去除套别标识
 func (c *CheckAreaMgr) getIedTypeCode(modelid int, iedtype string) string {
 	mapptype := new(SysCheckModelIedtypeMappingMgr)
 	mappresult := mapptype.GetMappingType(modelid, iedtype)
@@ -1008,7 +1025,7 @@ func (c *CheckAreaMgr) getIedTypeCode(modelid int, iedtype string) string {
 	if mappresult != "" {
 		ptCode = mappresult //自定义主变保护编码
 	}
-	return ptCode
+	return strings.Split(ptCode, "-")[0]
 }
 
 //变压器间隔分析
@@ -1147,6 +1164,11 @@ func (c *CheckAreaMgr) pT(modelid int, iedtypes string, ieds map[string]orm.Para
 				delMm = true
 				continue
 			}
+			//判断是否指定的套别标识
+			abCode := c.getIedTypeABCode(tyCode)
+			if abCode != "" {
+				iednameParts[7] = abCode
+			}
 			//判断间隔是否需要包含差动装置
 			if strings.Contains(tyCode, "#C") {
 				_, iedname := getIedByDesc(ieds, tyCode, "差动")
@@ -1175,7 +1197,7 @@ func (c *CheckAreaMgr) pT(modelid int, iedtypes string, ieds map[string]orm.Para
 			}
 			dlIeds := []orm.Params{}
 			imtcode := c.getIedTypeCode(modelid, tyCode)
-			h, m, l := c.getIedListByVol(imtcode, ieds, volMap)
+			h, m, l := c.getIedListByVol(imtcode, ieds, volMap, abCode)
 			//logger.Logger.Debug(fmt.Sprintf("tyCode:%s H:%+v,M:%+v,L:%+v", tyCode, h, m, l))
 			if volValue == "" || volValue == "H" {
 				dlIeds = append(dlIeds, h...)
@@ -1286,6 +1308,10 @@ func (c *CheckAreaMgr) pL(modelid int, vol, iedtypes string, ieds map[string]orm
 				//装置如果是分组成员装置,则不用检测
 				continue
 			}
+			abCode := c.getIedTypeABCode(tyCode)
+			if abCode != "" {
+				iednameParts[7] = abCode
+			}
 			if tyCode == pmCode {
 				//母线保护和母线合并单元装置编号跟随变压器
 				//查找变压器编号
@@ -1843,7 +1869,8 @@ func (c *CheckAreaMgr) cT(modelid string, scdXmlObj *node_attr.SCL, scdNodeMgr *
 }
 
 //根据参考ied name找出应该关联PM装置
-func (c *CheckAreaMgr) getPMName(iednameParts []string, ieds map[string]orm.Params) string {
+//abcode:套别标识代码。可选。不指定时将自动从iedtype识别
+func (c *CheckAreaMgr) getPMName(iednameParts []string, ieds map[string]orm.Params, abcode ...string) string {
 	//如果只有一个PM装置,则直接返回该 装置
 	pmLst := []string{}
 	for n, _ := range ieds {
@@ -1854,8 +1881,19 @@ func (c *CheckAreaMgr) getPMName(iednameParts []string, ieds map[string]orm.Para
 	if len(pmLst) == 1 {
 		return pmLst[0]
 	}
+	ab := ""
+	if len(abcode) > 0 {
+		ab = abcode[0]
+	}
 	//暴力匹配
 	for _, n := range pmLst {
+		if ab != "" {
+			//优先完全匹配指定套别
+			if strings.HasSuffix(n, ab) {
+				return n
+			}
+			continue
+		}
 		if strings.HasSuffix(n, iednameParts[7]) {
 			return n
 		}
@@ -1882,13 +1920,36 @@ func (c *CheckAreaMgr) getMMName(iednameParts []string, ieds map[string]orm.Para
 	return ""
 }
 
+//获取装置编码中的套别代码(A、B..)
+func (c *CheckAreaMgr) getIedTypeABCode(iedtype string) string {
+	if strings.Index(iedtype, "-") > 0 {
+		return strings.Split(iedtype, "-")[1]
+	}
+	return iedtype
+}
+
 //根据当前设备列表,分析出电压等级(高、中、低)的设备列表
 // CT测控、IT智能终端、MT合并单元需要判断是否是本体装置,是则将其归为主变高压侧
-func (c *CheckAreaMgr) getIedListByVol(iedtype string, ieds map[string]orm.Params, vollevel map[string]string) (hightLst, middleLst, lowLst []orm.Params) {
+//iedtype:装置编码
+//ieds:装置筛选范围列表
+//vollevel:电压等级代码。H:高压 M:中压 L:低压
+//abcode:套别标识代码。可选。不指定时将自动从iedtype识别
+func (c *CheckAreaMgr) getIedListByVol(iedtype string, ieds map[string]orm.Params, vollevel map[string]string, abcode ...string) (hightLst, middleLst, lowLst []orm.Params) {
 	tmpLst := map[string][]orm.Params{}
 	for _, v := range vollevel {
 		tmpLst[v] = []orm.Params{}
 	}
+	ab := ""
+	if len(abcode) > 0 {
+		ab = abcode[0]
+	}
+	if strings.Index(iedtype, "-") > 0 {
+		//去除套别标识
+		iedtype = strings.Split(iedtype, "-")[0]
+		if ab == "" {
+			ab = strings.Split(iedtype, "-")[1]
+		}
+	}
 	it := strings.Split(iedtype, "#")
 	scdParseMgr := new(ScdParse)
 	for _, row := range ieds {
@@ -1897,6 +1958,10 @@ func (c *CheckAreaMgr) getIedListByVol(iedtype string, ieds map[string]orm.Param
 			continue
 		}
 		iednameParts := scdParseMgr.ParseIedName(pl_iedname)
+		if ab != "" && iednameParts[7] != ab {
+			//指定了套别时,装置套别必须与指定套别完全相同
+			continue
+		}
 		volvalue := iednameParts[3] + iednameParts[4]
 		if tmpLst[volvalue] == nil {
 			tmpLst[volvalue] = []orm.Params{row}

+ 2 - 2
service/models/bo/scd_diff_compare.go

@@ -421,8 +421,8 @@ func (c *ScdCompare) GetCompItemDetailInfo(compareId int, iedname, comptype, ite
 		for _, r := range targetRel {
 			targetRelMap[fmt.Sprintf("%s%s%s", tools.IsEmpty(r["ied_name"]), tools.IsEmpty(r["ied_name"]), tools.IsEmpty(r["attr_int_addr"]), tools.IsEmpty(r["inout_type"]))] = r
 		}
-		logger.Logger.Debug(fmt.Sprintf("sourceRelMap:%+v", sourceRelMap))
-		logger.Logger.Debug(fmt.Sprintf("targetRelMap:%+v", targetRelMap))
+		//logger.Logger.Debug(fmt.Sprintf("sourceRelMap:%+v", sourceRelMap))
+		//logger.Logger.Debug(fmt.Sprintf("targetRelMap:%+v", targetRelMap))
 		for key, item := range sourceRelMap {
 			if _, h := targetRelMap[key]; !h {
 				//新增的端子

+ 1 - 1
service/models/bo/scd_file_parse.go

@@ -3360,7 +3360,7 @@ func (c *ScdParse) ParseIedName(name string) []string {
 	//补全实际ied name为8位
 	new_ied_name_full := []string{"", "", "", "", "", "", "", ""}
 	tl := len(name)
-	logger.Logger.Debug(fmt.Sprintf("ied name:%s", name))
+	//logger.Logger.Debug(fmt.Sprintf("ied name:%s", name))
 	if tl > 4 {
 		//至少有5位置编码才可能补全
 		lastchar := name[tl-1:]

+ 7 - 7
service/models/bo/scd_node_mgr.go

@@ -1,12 +1,6 @@
 package bo
 
 import (
-	"scd_check_tools/global"
-	"scd_check_tools/logger"
-	"scd_check_tools/models/enum"
-	"scd_check_tools/models/node_attr"
-	"scd_check_tools/mqtt"
-	"scd_check_tools/tools"
 	"bufio"
 	"encoding/json"
 	"errors"
@@ -15,6 +9,12 @@ import (
 	"log"
 	"os"
 	"runtime"
+	"scd_check_tools/global"
+	"scd_check_tools/logger"
+	"scd_check_tools/models/enum"
+	"scd_check_tools/models/node_attr"
+	"scd_check_tools/mqtt"
+	"scd_check_tools/tools"
 	"strconv"
 	"strings"
 	"time"
@@ -2011,7 +2011,7 @@ func (c *ScdNode) GetIedCtrlInputsRelations(param map[string]interface{}) ([]orm
 			var ld *node_attr.NLDevice
 			if nodeInfo.ObjAddr != nil {
 				extref := nodeInfo.ObjAddr.(*node_attr.NExtRef)
-				logger.Logger.Debug(fmt.Sprintf("extref %s info:%+v", extrefid, extref))
+				//logger.Logger.Debug(fmt.Sprintf("extref %s info:%+v", extrefid, extref))
 				obj["attr_int_addr"] = extref.IntAddr
 				ld, ln_desc, doi_desc = c.GetIntAddrDesc(sIed, extref.IntAddr)
 				if ld != nil {

+ 4 - 4
service/models/bo/scd_node_rule.go

@@ -2657,23 +2657,23 @@ func (c *ScdNodeRule) writeNodeDB() {
 		} else {
 			nodeSqlValuesAry = append(nodeSqlValuesAry, "("+tools.IsEmpty(m["scdid"])+","+tools.IsEmpty(m["nodeid"], "0")+","+tools.IsEmpty(m["lineno"], "0")+",'"+tools.IsEmpty(m["rule_target"])+"',"+tools.IsEmpty(m["ruleid"], "0")+",'"+tools.IsEmpty(m["parse_result"])+"','"+tools.IsEmpty(m["ied_name"])+"','"+tools.IsEmpty(m["ied_desc"])+"')")
 		}
-		if len(nodeSqlValuesAry) == 5000 {
+		if len(nodeSqlValuesAry) == 1000 {
 			sql := "insert into t_scd_node_rule_parse" + nodeCols + "values" + strings.Join(nodeSqlValuesAry, ",")
 			_, err := db.Raw(sql).Exec()
 			if err != nil {
 				log.Println(err)
 			}
-			loadedRec = loadedRec + 5000
+			loadedRec = loadedRec + 1000
 			nodeSqlValuesAry = nil
 			nodeSqlValuesAry = []string{}
 		}
-		if len(fcdaSqlValuesAry) == 5000 {
+		if len(fcdaSqlValuesAry) == 1000 {
 			sql := "insert into t_scd_fcda_check_result" + fcdaCols + "values" + strings.Join(fcdaSqlValuesAry, ",")
 			_, err := db.Raw(sql).Exec()
 			if err != nil {
 				log.Println(err)
 			}
-			loadedRec = loadedRec + 5000
+			loadedRec = loadedRec + 1000
 			fcdaSqlValuesAry = nil
 			fcdaSqlValuesAry = []string{}
 		}

+ 28 - 3
service/models/bo/task_report.go

@@ -234,21 +234,33 @@ func (c *TaskReportMgr) ToWord(taskinfo T_data_task) (path string, err error) {
 		logger.Logger.Error(err)
 		return "", err
 	}
+	sheet_c.SetColWidth(1, 1, 30)
+	sheet_c.SetColWidth(3, 3, 80)
+	sheet_c.SetColWidth(4, 5, 30)
 	sheet_p, err := file.AddSheet("保护装置")
 	if err != nil {
 		logger.Logger.Error(err)
 		return "", err
 	}
+	sheet_p.SetColWidth(1, 1, 30)
+	sheet_p.SetColWidth(3, 3, 80)
+	sheet_p.SetColWidth(4, 5, 30)
 	sheet_i, err := file.AddSheet("智能终端")
 	if err != nil {
 		logger.Logger.Error(err)
 		return "", err
 	}
+	sheet_i.SetColWidth(1, 1, 30)
+	sheet_i.SetColWidth(3, 3, 80)
+	sheet_i.SetColWidth(4, 5, 30)
 	sheet_m, err := file.AddSheet("合并单元")
 	if err != nil {
 		logger.Logger.Error(err)
 		return "", err
 	}
+	sheet_m.SetColWidth(1, 1, 30)
+	sheet_m.SetColWidth(3, 3, 80)
+	sheet_m.SetColWidth(4, 5, 30)
 	sheet_other, err := file.AddSheet("schema语法和数据模板")
 	if err != nil {
 		logger.Logger.Error(err)
@@ -257,6 +269,8 @@ func (c *TaskReportMgr) ToWord(taskinfo T_data_task) (path string, err error) {
 	c.setCaption(sheet_other, fmt.Sprintf("%-%", scdName, "语法校验结果"))
 	//生成标题行
 	c.addCellValue(sheet_other, []string{"序号", "行号", "等级", "校验结果内容", "应用标准", "标准条款"})
+	sheet_other.SetColWidth(3, 3, 80)
+	sheet_other.SetColWidth(4, 5, 30)
 	//生成报表数据
 	iedResultMap := map[string][]orm.Params{}
 	rowInd := 1
@@ -420,7 +434,7 @@ func (c *TaskReportMgr) ToWord(taskinfo T_data_task) (path string, err error) {
 		}
 	}
 	c.addCellValue(sheet_fcda, []string{"总虚连接数目", fmt.Sprintf("%d", totalExtrefs), "参与校核虚连接数目", fmt.Sprintf("%d", checkExtrefs)})
-	c.addCellValue(sheet_fcda, []string{"未校核虚连接数目", fmt.Sprintf("%d", totalExtrefs-checkExtrefs), "正确虚连接数目", fmt.Sprintf("%d", checkExtrefs-errortype1-errortype2-errortype3)})
+	c.addCellValue(sheet_fcda, []string{"未校核虚连接数目", fmt.Sprintf("%d", totalExtrefs-checkExtrefs), "正确虚连接数目", fmt.Sprintf("%d", checkExtrefs-errortype1-errortype2)})
 	c.addCellValue(sheet_fcda, []string{"多余虚连接数目", fmt.Sprintf("%d", errortype1), "缺失虚连接数目", fmt.Sprintf("%d", errortype3)})
 	c.addCellValue(sheet_fcda, []string{"错误虚连接数目", fmt.Sprintf("%d", errortype2), "不规范虚连接数目", fmt.Sprintf("%d", 0)})
 	c.addCellValue(sheet_fcda, []string{"严重问题", fmt.Sprintf("%d", 0), "规范性问题", fmt.Sprintf("%d", 0)})
@@ -432,13 +446,13 @@ func (c *TaskReportMgr) ToWord(taskinfo T_data_task) (path string, err error) {
 	cellNo := 1
 
 	for _, row := range result {
-		if tools.IsEmpty(row["ied_name"]) == "" {
+		if tools.IsEmpty(row["ied_name"]) == "" || tools.IsEmpty(row["error_type"]) == "9" {
 			continue
 		}
 		nowIedName := fmt.Sprintf("%s", tools.IsEmpty(row["ied_name"]))
 		if lastIedName == "" || lastIedName != nowIedName {
 			lastIedName = nowIedName
-			c.setCaption(sheet_fcda, lastIedName+":"+tools.IsEmpty(row["ied_desc"]), 6)
+			c.setCaption(sheet_fcda, lastIedName+":"+tools.IsEmpty(row["ied_desc"]), 5)
 			c.addCellValue(sheet_fcda, []string{"序号", "外部IED", "发送信号", "本IED", "接收信号", "结论"})
 			cellNo = 1
 		}
@@ -476,6 +490,12 @@ func (c *TaskReportMgr) setCaption(sheet *xlsx.Sheet, caption string, margeNum .
 	cell.Value = caption
 	style := xlsx.NewStyle()
 	style.Alignment.Horizontal = "center"
+	style.Font.Bold = true
+	style.Font.Size = 14
+	style.Border.Bottom = "solid"
+	//style.Border.Left = "solid"
+	style.Border.Right = "solid"
+	//style.Border.Top = "solid"
 	cell.SetStyle(style)
 	return cell
 }
@@ -488,6 +508,11 @@ func (c *TaskReportMgr) addCellValue(sheet *xlsx.Sheet, vs []string) {
 		style := xlsx.NewStyle()
 		style.Alignment.Horizontal = "left"
 		style.Alignment.WrapText = true
+		style.Font.Size = 12
+		style.Border.Bottom = "solid"
+		//style.Border.Left = "solid"
+		style.Border.Right = "solid"
+		//style.Border.Top = "solid"
 		cell.SetStyle(style)
 	}
 }