package bo import ( "errors" "fmt" "scd_check_tools/logger" "scd_check_tools/models/enum" "scd_check_tools/models/node_attr" "scd_check_tools/tools" "strconv" "strings" "sync" "github.com/astaxie/beego/orm" ) //检测时间隔管理 type CheckAreaMgr struct { DeviceBaseModel //SCD文件ID ScdId int64 //电压等级定义 VoltageLevelDef map[string]int //设备类型定义 DeviceTypeDef map[string]int CacheAreaID map[string]int64 CacheAreaIDByIedNameNo map[string]int //检测模型列表 CheckModelList []orm.Params //模型内装置关系定义 CheckModelDef sync.Map CacheLock sync.RWMutex } type T_data_check_area struct { Id int64 `orm:"pk"` AreaName string ScdId int64 VoltageLevel int AreaType string ModelId int Cr int // '创建人' , Ct string `orm:"-"` // '创建时间' , Ur int // '更新人' , Ut string `orm:"-"` // '更新时间' } type t_data_check_area_ied struct { Id int64 `orm:"pk"` AreaId int64 IedName string IedType string ScdId int64 PType string IedNo string //所属间隔下支路(线路)在SCD中端子实际编号,如在母线间隔下支路9 Cr int // '创建人' , Ct string `orm:"-"` // '创建时间' , Ur int // '更新人' , Ut string `orm:"-"` // '更新时间' } func init() { orm.RegisterModel(new(T_data_check_area)) orm.RegisterModel(new(t_data_check_area_ied)) } func (c *CheckAreaMgr) Init(scdid int64) { c.VoltageLevelDef = map[string]int{} c.DeviceTypeDef = map[string]int{} c.CacheAreaIDByIedNameNo = map[string]int{} c.CacheAreaID = map[string]int64{} c.CacheLock = sync.RWMutex{} c.ScdId = scdid db := orm.NewOrm() rowset := []orm.Params{} db.Raw("select * from global_const_code where parentcode=?", "voltage_level").Values(&rowset) for _, row := range rowset { vl := strings.ToLower(tools.IsEmpty(row["code"])) id, _ := strconv.ParseInt(tools.IsEmpty(row["id"]), 10, 32) c.VoltageLevelDef[strings.ReplaceAll(vl, "v_level_", "")] = int(id) } db.Raw("select * from global_const_code where parentcode=?", "device_type").Values(&rowset) for _, row := range rowset { vl := tools.IsEmpty(row["code"]) c.DeviceTypeDef[vl] = 1 } v, _ := GetSysParamValue("OtherIedNameList", "") otherIedNameList = map[string]bool{} if v != "" { vs := strings.Split(v, ",") for _, vv := range vs { otherIedNameList[vv] = true } } tmplist, _ := new(SysCheckModelMgr).GetModelsByVolid(2) scdModels, _, _ := new(TaskMgr).GetModelsByScdID(c.ScdId) ms := map[string]interface{}{} for _, row := range scdModels { ms[tools.IsEmpty(row["model_id"])] = row } for _, row := range tmplist { modelid := tools.IsEmpty(row["id"]) if ms[modelid] != nil { c.CheckModelList = append(c.CheckModelList, row) } } logger.Logger.Debug(fmt.Sprintf("任务的模型列表:%+v", c.CheckModelList)) ms = nil tmplist = nil } //保存间隔 func (c *CheckAreaMgr) Save(model_id int, area_name string) (int, error) { //判断间隔名称是否重复 db := orm.NewOrm() rowset := []orm.Params{} db.Raw("select id from t_data_check_area where scd_id=? and area_name=?", c.ScdId, area_name).Values(&rowset) if len(rowset) > 0 { return 0, errors.New("间隔名称" + area_name + "已存在") } modelMgr := new(SysCheckModelMgr) modelMgr.Model.Id = model_id modelInfo, err := modelMgr.One() if err != nil { return 0, err } areatype_codeinfo := new(Global).GetCodeInfoByID(tools.IsEmpty(modelInfo.AreaType)) area_type := tools.IsEmpty(areatype_codeinfo["code"]) cr := c.GetUserId() paras := []interface{}{c.ScdId, area_name, area_type, model_id, cr} r, err := db.Raw("insert into t_data_check_area(scd_id,area_name,area_type,model_id,cr,voltage_level)values(?,?,?,?,?,0)", paras).Exec() sqllog := fmt.Sprintf("添加新间隔%s", area_name) if err != nil { logger.Logger.Error(err) new(SystemLog).Fail(enum.AuditType_check_area, enum.LogType_Insert, enum.OptEventType_Bus, enum.OptEventLevel_Hight, sqllog, c.GetUserInfo()) return 0, err } new(SystemLog).Success(enum.AuditType_check_area, enum.LogType_Insert, enum.OptEventType_Bus, enum.OptEventLevel_Hight, sqllog, c.GetUserInfo()) id, _ := r.LastInsertId() return int(id), nil } //删除指定间隔 func (c *CheckAreaMgr) Del(area_id int) error { db := orm.NewOrm() _, err := db.Raw("delete from t_data_check_area where scd_id=? and id=?", c.ScdId, area_id).Exec() _, err = db.Raw("delete from t_data_check_area_ied where scd_id=? and area_id=?", c.ScdId, area_id).Exec() _, err = db.Raw("delete from t_data_doi_mapping where area_id=?", area_id).Exec() sqllog := fmt.Sprintf("删除间隔%d", area_id) if err != nil { logger.Logger.Error(err) new(SystemLog).Fail(enum.AuditType_check_area, enum.LogType_Delete, enum.OptEventType_Bus, enum.OptEventLevel_Hight, sqllog, c.GetUserInfo()) } else { new(SystemLog).Success(enum.AuditType_check_area, enum.LogType_Delete, enum.OptEventType_Bus, enum.OptEventLevel_Hight, sqllog, c.GetUserInfo()) } return err } //保存指定间隔所属的电压等级 func (c *CheckAreaMgr) SetVoltageLevel(id string, voltagelevel int) error { db := orm.NewOrm() _, err := db.Raw("update t_data_check_area set voltage_level=? where id=?", voltagelevel, id).Exec() return err } //修改指定间隔的名称 func (c *CheckAreaMgr) UpdateName(scdid int64, area_id int, name string) error { db := orm.NewOrm() areaM := T_data_check_area{Id: int64(area_id), ScdId: scdid} err := db.Read(&areaM) if err != nil { logger.Logger.Error(err) return err } areaM.AreaName = name _, err = db.Update(&areaM) sqllog := fmt.Sprintf("修改指定间隔%d的名称为%s", area_id, name) if err != nil { logger.Logger.Error(err) new(SystemLog).Fail(enum.AuditType_check_area, enum.LogType_Query, enum.OptEventType_Bus, enum.OptEventLevel_Hight, sqllog, c.GetUserInfo()) } else { new(SystemLog).Success(enum.AuditType_check_area, enum.LogType_Query, enum.OptEventType_Bus, enum.OptEventLevel_Hight, sqllog, c.GetUserInfo()) } return err } //修改指定IED的所属间隔 func (c *CheckAreaMgr) UpdateIedArea(scdid int64, iedname string, area_id int) error { db := orm.NewOrm() _, err := db.Raw("update t_data_check_area_ied set area_id=? where scd_id=? and ied_name=?", area_id, scdid, iedname).Exec() return err } //按装置保存端子对应关系 func (c *CheckAreaMgr) SaveDoiMapping(station int, areaid int, doilst []map[string]interface{}) error { db := orm.NewOrm() var err error rowset := []orm.Params{} _, err = db.Raw("select id from t_data_doi_mapping limit 0,1").Values(&rowset) if err != nil && strings.Contains(err.Error(), "doesn't exist") { //数据表丢失,重建 sql := `CREATE TABLE t_data_doi_mapping ( id bigint NOT NULL AUTO_INCREMENT, station_id int DEFAULT '0', area_id int DEFAULT '0', ied_name varchar(255) DEFAULT NULL, doi_desc varchar(255) DEFAULT NULL, in_ied_name varchar(255) DEFAULT NULL, in_doi_desc varchar(255) DEFAULT NULL, PRIMARY KEY (id), KEY ind1 (station_id), KEY ind2 (area_id), KEY ind3 (ied_name,in_ied_name) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3` _, err = db.Raw(sql).Exec() if err != nil { logger.Logger.Error(err) return err } } _, err = db.Raw("delete from t_data_doi_mapping where area_id=?", areaid).Exec() if err != nil { logger.Logger.Error(err) return err } cols := `insert into t_data_doi_mapping(station_id,area_id,ied_name,doi_desc,in_ied_name,in_doi_desc)values` values := []string{} for _, row := range doilst { vs := []string{ tools.IsEmpty(station), tools.IsEmpty(areaid), "'" + tools.IsEmpty(row["ied_name"]) + "'", "'" + tools.IsEmpty(row["doi_desc"]) + "'", "'" + tools.IsEmpty(row["in_ied_name"]) + "'", "'" + tools.IsEmpty(row["in_doi_desc"]) + "'", } values = append(values, "("+strings.Join(vs, ",")+")") if len(values) > 300 { _, err = db.Raw(cols + strings.Join(values, ",")).Exec() values = []string{} if err != nil { logger.Logger.Error(err) break } } } if len(values) > 0 { _, err = db.Raw(cols + strings.Join(values, ",")).Exec() values = nil } return err } //查询装置端子对应关系 func (c *CheckAreaMgr) GetDoiMapping(station int, areaid int, ied_name string) ([]orm.Params, error) { db := orm.NewOrm() var err error rowset := []orm.Params{} _, err = db.Raw("select * from t_data_doi_mapping limit 0,1").Values(&rowset) if err != nil && strings.Contains(err.Error(), "doesn't exist") { //数据表丢失,重建 sql := `CREATE TABLE t_data_doi_mapping ( id bigint NOT NULL AUTO_INCREMENT, station_id int DEFAULT '0', area_id int DEFAULT '0', ied_name varchar(255) DEFAULT NULL, doi_desc varchar(255) DEFAULT NULL, in_ied_name varchar(255) DEFAULT NULL, in_doi_desc varchar(255) DEFAULT NULL, PRIMARY KEY (id), KEY ind1 (station_id), KEY ind2 (area_id), KEY ind3 (ied_name,in_ied_name) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3` _, err = db.Raw(sql).Exec() if err != nil { logger.Logger.Error(err) return rowset, err } } sql := "select * from t_data_doi_mapping where station_id=? and area_id=?" params := []interface{}{station, areaid} if ied_name != "" { sql = sql + " and ied_name=?" params = append(params, ied_name) } _, err = db.Raw(sql, params).Values(&rowset) if err != nil { logger.Logger.Error(err) return nil, err } return rowset, err } //获取指定scd的间隔信息 func (c *CheckAreaMgr) GetAreaList(scdid int64) ([]orm.Params, error) { db := orm.NewOrm() sql := "select t.*,(select count(1) from t_data_check_area_ied where area_id=t.id) iedcount from t_data_check_area t where t.scd_id=? order by t.name" rowset := []orm.Params{} _, err := db.Raw(sql, scdid).Values(&rowset) sqllog := fmt.Sprintf("SQL:%s 参数:%+v", sql, []interface{}{scdid}) if err != nil { logger.Logger.Error(err, sqllog) new(SystemLog).Fail(enum.AuditType_check_area, enum.LogType_Query, enum.OptEventType_Bus, enum.OptEventLevel_Low, sqllog, c.GetUserInfo()) } else { new(SystemLog).Success(enum.AuditType_check_area, enum.LogType_Query, enum.OptEventType_Bus, enum.OptEventLevel_Low, sqllog, c.GetUserInfo()) } return rowset, nil } //获取指定scd和电压等级的间隔信息 func (c *CheckAreaMgr) GetAreaListByVol(scdid int64, vl, linkstyleid int) ([]orm.Params, error) { db := orm.NewOrm() sql := "select t1.id model_id, t1.model_name,t1.vol_id,t1.ied_types,g1.name vol_name,t1.line_link_style,ls.name,t.id area_id, t.area_name,t.ut,t.ur from t_data_check_area t right join t_data_model_defualt t1 on t.model_id=t1.id left join t_data_link_style ls on t1.line_link_style=ls.id left join global_const_code g1 on t1.vol_id=g1.id and g1.parentcode='voltage_level' where t.scd_id=? " params := []interface{}{scdid} if vl > 0 { sql = sql + " and t1.vol_id=? " params = append(params, vl) } if linkstyleid > 0 { sql = sql + " and t1.line_link_style=? " params = append(params, linkstyleid) } rowset := []orm.Params{} _, err := db.Raw(sql+" order by t1.vol_id,t1.line_link_style,t1.id", params).Values(&rowset) sqllog := fmt.Sprintf("SQL:%s 参数:%+v", sql, []interface{}{scdid, vl}) if err != nil { logger.Logger.Error(err, sqllog) new(SystemLog).Fail(enum.AuditType_check_area, enum.LogType_Query, enum.OptEventType_Bus, enum.OptEventLevel_Low, sqllog, c.GetUserInfo()) } else { new(SystemLog).Success(enum.AuditType_check_area, enum.LogType_Query, enum.OptEventType_Bus, enum.OptEventLevel_Low, sqllog, c.GetUserInfo()) } return rowset, nil } //获取指定间隔下的IED列表 func (c *CheckAreaMgr) GetIedList(scdid int64, areaid int) ([]orm.Params, error) { db := orm.NewOrm() sql := "select t1.* from t_data_check_area t,t_data_check_area_ied t1 where t.scd_id=? and t.id=t1.area_id " sqlParamters := []interface{}{} sqlParamters = append(sqlParamters, scdid) if areaid > 0 { sql = sql + " and t1.area_id=?" sqlParamters = append(sqlParamters, areaid) } scdXmlObj, serr := new(ScdParse).GetScdXmlObjectBySCDID(tools.IsEmpty(scdid)) if serr != nil { return nil, serr } if scdXmlObj == nil { return nil, errors.New("无效的SCD") } rowset := []orm.Params{} _, err := db.Raw(sql, sqlParamters).Values(&rowset) sqllog := fmt.Sprintf("SQL:%s 参数:%+v", sql, sqlParamters) if err != nil { logger.Logger.Error(err, sqllog) new(SystemLog).Fail(enum.AuditType_check_area, enum.LogType_Query, enum.OptEventType_Bus, enum.OptEventLevel_Low, sqllog, c.GetUserInfo()) } else { scdNode := new(ScdNode) for i, row := range rowset { iedObj := scdNode.GetIed(scdXmlObj, tools.IsEmpty(scdid), tools.IsEmpty(row["ied_name"])) if iedObj == nil { continue } rowset[i]["attr_name"] = iedObj.Name rowset[i]["attr_desc"] = iedObj.Desc rowset[i]["attr_config_version"] = iedObj.ConfigVersion rowset[i]["attr_type"] = iedObj.Type rowset[i]["attr_manufacturer"] = iedObj.Manufacturer rowset[i]["ied_id"] = iedObj.NodeId } //new(SystemLog).Success(enum.AuditType_check_area, enum.LogType_Query, enum.OptEventType_Bus, enum.OptEventLevel_Low, sqllog, c.GetUserInfo()) } return rowset, nil } //更新指定间隔下的装置定义 func (c *CheckAreaMgr) UpdateIeds(scdid int64, areaid int, ieds string) error { db := orm.NewOrm() iedlist := strings.Split(ieds, ",") oldInfo := []orm.Params{} iedNoMap := map[string]orm.Params{} db.Raw("select * from t_data_check_area_ied where scd_id=? and area_id=?", scdid, areaid).Values(&oldInfo) for _, row := range oldInfo { iedNoMap[tools.IsEmpty(row["ied_name"])] = row } _, err := db.Raw("delete from t_data_check_area_ied where scd_id=? and area_id=?", scdid, areaid).Exec() if err != nil { logger.Logger.Error(err) return err } for _, row := range iedlist { iedno := tools.IsEmpty(iedNoMap[row]["ied_no"]) iedType := tools.IsEmpty(iedNoMap[row]["ied_type"]) pType := tools.IsEmpty(iedNoMap[row]["p_type"]) if iedType == "" { iednameParts := new(ScdParse).ParseIedName(row) iedType = iednameParts[0] pType = iednameParts[1] + iednameParts[2] } _, err = db.Raw("insert into t_data_check_area_ied(scd_id,area_id,ied_name,ied_no,ied_type,p_type)values(?,?,?,?,?,?)", scdid, areaid, row, iedno, iedType, pType).Exec() if err != nil { logger.Logger.Error(err) break } } return err } //设置间隔下指定装置的端子编号,该编号只有在端子匹配表达式中存在{no}标识时才会使用 func (c *CheckAreaMgr) SetIedNo(areaid int, iedname, no string) error { db := orm.NewOrm() _, err := db.Raw("update t_data_check_area_ied set ied_no=? where area_id=? and ied_name=?", no, areaid, iedname).Exec() return err } //获取指定SCD的IED类型列表 func (c *CheckAreaMgr) GetIedTypeList(scdid int64) ([]orm.Params, error) { db := orm.NewOrm() sql := "select t2.* from t_data_check_area_ied t1,global_const_code t2 where t1.scd_id=? and t1.ied_type=t2.code and t2.parentcode='device_type' group by t1.ied_type " sqlParamters := []interface{}{} rowset := []orm.Params{} sqlParamters = append(sqlParamters, scdid) _, err := db.Raw(sql, sqlParamters).Values(&rowset) sqllog := fmt.Sprintf("SQL:%s 参数:%+v", sql, sqlParamters) if err != nil { logger.Logger.Error(err, sqllog) new(SystemLog).Fail(enum.AuditType_check_area, enum.LogType_Query, enum.OptEventType_Bus, enum.OptEventLevel_Low, sqllog, c.GetUserInfo()) } return rowset, nil } func (c *CheckAreaMgr) One(id string) (interface{}, error) { db := orm.NewOrm() idInt, err := strconv.ParseInt(id, 10, 64) if err != nil { return nil, err } areaM := T_data_check_area{Id: idInt} err = db.Read(&areaM) if err == nil { return areaM, nil } return nil, err } //重置scd的间隔信息 func (c *CheckAreaMgr) Reset() error { //dbo := orm.NewOrm() //dbo.Raw("delete from t_data_check_area where scd_id=?", c.ScdId).Exec() //dbo.Raw("delete from t_data_check_area_ied where scd_id=?", c.ScdId).Exec() c.ParseModelArea(false) logdesc := fmt.Sprintf("重置SCD[%d]检测模型间隔成功", c.ScdId) new(SystemLog).Success(enum.AuditType_check_task, enum.LogType_bind, enum.OptEventType_Bus, enum.OptEventLevel_Mid, logdesc, c.GetUserInfo()) return nil } //生成SCD装置虚端子表 //scdid:SCD文件ID //areaids:指定的间隔列表。可以不指定,不指定时则生成全站所有装置的端子关系表 //iedname:指定的装置名称。可以不指定。 func (c *CheckAreaMgr) MakeExtrefReport(scdid int64, areaids []string, iedname string) (re []orm.Params, err error) { findIeds := []orm.Params{} scdParseMgr := new(ScdParse) scdXmlObj, _ := scdParseMgr.GetScdXmlObjectBySCDID(tools.IsEmpty(scdid)) if areaids != nil && len(areaids) > 0 { for _, areaid := range areaids { if areaid == "" { continue } tmpid, _ := strconv.Atoi(areaid) r, _ := c.GetIedList(scdid, tmpid) if r == nil { continue } findIeds = append(findIeds, r...) } } else if iedname != "" { scdParseMgr := new(ScdParse) scdXmlObj, _ := scdParseMgr.GetScdXmlObjectBySCDID(tools.IsEmpty(scdid)) scdNode := new(ScdNode) iedObj := scdNode.GetIed(scdXmlObj, tools.IsEmpty(scdid), iedname) if iedObj == nil { return findIeds, errors.New("无效的装置名称:" + iedname) } rowset := orm.Params{"ied_name": iedname} rowset["attr_name"] = iedObj.Name rowset["attr_desc"] = iedObj.Desc rowset["attr_config_version"] = iedObj.ConfigVersion rowset["attr_type"] = iedObj.Type rowset["attr_manufacturer"] = iedObj.Manufacturer rowset["ied_id"] = iedObj.NodeId findIeds = append(findIeds, rowset) } else { //获取所有的装置 for _, iedObj := range scdXmlObj.IED { rowset := orm.Params{"ied_name": iedObj.Name} rowset["attr_name"] = iedObj.Name rowset["attr_desc"] = iedObj.Desc rowset["attr_config_version"] = iedObj.ConfigVersion rowset["attr_type"] = iedObj.Type rowset["attr_manufacturer"] = iedObj.Manufacturer rowset["ied_id"] = iedObj.NodeId findIeds = append(findIeds, rowset) } } scdNodeRule := new(ScdNodeRule) scdNodeRule.SetScdXmlObject(scdXmlObj) scdNode := new(ScdNode) //获取端子的外部端子输入信号类型 var getFcdaType = func(inIedObj *node_attr.NIED, t2 *node_attr.NExtRef) string { isFoundType := false gooseorsv := "" for _, ap := range inIedObj.AccessPoint { if ap.Server == nil { continue } for _, ld := range ap.Server.LDevice { if ld.LN0 == nil { continue } dsname := "" for _, ds := range ld.LN0.DataSet { for _, fcda := range ds.FCDA { if fcda.LdInst == t2.LdInst && fcda.LnClass == t2.LnClass && fcda.LnInst == t2.LnInst && fcda.Prefix == t2.Prefix && fcda.DoName == t2.DoName { if t2.DaName == "" || fcda.DaName == t2.DaName { dsname = ds.Name break } } } if dsname != "" { break } } //logger.Logger.Debug(fmt.Sprintf("%s装置FCDA(%+v)的数据集名称:%s", inIedObj.Name, t2, dsname)) if dsname != "" { for _, smcrl := range ld.LN0.SampledValueControl { if smcrl.DatSet == dsname { gooseorsv = "SV" break } } if gooseorsv == "" { for _, goosecrl := range ld.LN0.GSEControl { if goosecrl.DatSet == dsname { gooseorsv = "GOOSE" break } } } isFoundType = true break } } if isFoundType { break } } return gooseorsv } //处理ied的端子关系 result := []orm.Params{} for _, ied := range findIeds { iedname := tools.IsEmpty(ied["ied_name"]) //获取extref iedObj := scdNode.GetIed(scdXmlObj, tools.IsEmpty(scdid), iedname) if iedObj == nil { logger.Logger.Error(errors.New("装置"+iedname+"未找到"), fmt.Sprintf("装置数据:%+v", ied)) continue } for _, t1 := range iedObj.AccessPoint { if t1.Server == nil || len(t1.Server.LDevice) == 0 { continue } for _, ld := range t1.Server.LDevice { if ld.LN0 != nil && ld.LN0.Inputs != nil { for _, t2 := range ld.LN0.Inputs.ExtRef { doi := scdNodeRule.IedIntAddrExist(iedname, t2.IntAddr) if doi == nil { logger.Logger.Debug(fmt.Sprintf("未发现装置%s的端子(%+v)名称", iedname, t2.IntAddr)) continue } itemRow := orm.Params{} for k, v := range ied { itemRow[k] = v } itemRow["doi_desc"] = doi.(*node_attr.NDOI).Desc itemRow["doi_addr"] = t2.IntAddr //获取外部ied信息 iniedname := t2.IedName inIedObj := scdNode.GetIed(scdXmlObj, tools.IsEmpty(scdid), iniedname) indoi, _ := scdNodeRule.IedFcdaExist(t2.IedName, t2.LdInst, t2.LnClass, t2.LnInst, t2.Prefix, t2.DoName, "") if inIedObj == nil || indoi == nil { logger.Logger.Debug(fmt.Sprintf("未发现装置%s的端子(%+v)名称", iniedname, t2)) continue } else { //查找fcda的数据集及类型 itemRow["gooseorsv"] = getFcdaType(inIedObj, t2) itemRow["in_ied_name"] = iniedname itemRow["in_ied_desc"] = inIedObj.Desc itemRow["in_doi_desc"] = indoi.(*node_attr.NDOI).Desc daname := "" if t2.DaName != "" { daname = "." + t2.DaName } itemRow["in_doi_addr"] = fmt.Sprintf("%s/%s%s%s.%s%s", t2.LdInst, t2.Prefix, t2.LnClass, t2.LnInst, t2.DoName, daname) result = append(result, itemRow) } } } for _, ln := range ld.LN { if ln.Inputs == nil { continue } for _, t2 := range ln.Inputs.ExtRef { doi := scdNodeRule.IedIntAddrExist(iedname, t2.IntAddr) if doi == nil { logger.Logger.Debug(fmt.Sprintf("未发现装置%s的端子(%+v)名称", iedname, t2.IntAddr)) continue } itemRow := orm.Params{} for k, v := range ied { itemRow[k] = v } itemRow["doi_desc"] = doi.(*node_attr.NDOI).Desc itemRow["doi_addr"] = t2.IntAddr //获取外部ied信息 iniedname := t2.IedName inIedObj := scdNode.GetIed(scdXmlObj, tools.IsEmpty(scdid), iniedname) indoi, _ := scdNodeRule.IedFcdaExist(t2.IedName, t2.LdInst, t2.LnClass, t2.LnInst, t2.Prefix, t2.DoName, "") if inIedObj == nil || indoi == nil { logger.Logger.Debug(fmt.Sprintf("未发现装置%s的端子(%+v)名称", iniedname, t2)) continue } //查找fcda的数据集及类型 itemRow["gooseorsv"] = getFcdaType(inIedObj, t2) itemRow["in_ied_name"] = iniedname itemRow["in_ied_desc"] = inIedObj.Desc itemRow["in_doi_desc"] = indoi.(*node_attr.NDOI).Desc daname := "" if t2.DaName != "" { daname = "." + t2.DaName } itemRow["in_doi_addr"] = fmt.Sprintf("%s/%s%s%s.%s%s", t2.LdInst, t2.Prefix, t2.LnClass, t2.LnInst, t2.DoName, daname) result = append(result, itemRow) } } } } } return result, nil } var areaCheckInfo = sync.Map{} //获取解析模型需要的基础数据信息 func (c *CheckAreaMgr) getAreaCheckInfo() (*node_attr.SCL, []orm.Params, string, error) { key := fmt.Sprintf("%d-checkinfo", c.ScdId) if v, h := areaCheckInfo.Load(key); h { v1 := v.([]interface{}) return v1[0].(*node_attr.SCL), v1[1].([]orm.Params), v1[2].(string), nil } arealist := []orm.Params{} db := orm.NewOrm() _, err := db.Raw("select id,area_name,area_type,model_id from t_data_check_area where scd_id=?", c.ScdId).Values(&arealist) if err != nil { logger.Logger.Error(err) return nil, nil, "", err } scdNodeRule := new(ScdNodeRule) area_ruleid := "" area_ruleList, _, _ := scdNodeRule.GetDefList(map[string]interface{}{"check_name": "间隔装置与检查模型不符"}, 1, 1) if len(area_ruleList) > 0 { area_ruleid = tools.IsEmpty(area_ruleList[0]["id"]) } if area_ruleid == "" { return nil, nil, "", errors.New(fmt.Sprintf("未定义间隔装置的检查规则“间隔装置与检查模型不符”")) } scdParseMgr := new(ScdParse) scdXmlObj, serr := scdParseMgr.GetScdXmlObjectBySCDID(tools.IsEmpty(c.ScdId)) if serr != nil { return nil, nil, "", serr } if scdXmlObj == nil { return nil, nil, "", errors.New("无效的SCD") } areaCheckInfo.Store(key, []interface{}{scdXmlObj, arealist, area_ruleid}) return scdXmlObj, arealist, area_ruleid, nil } //检测装置端子分析:检测间隔装置关系正确性(已废弃) func (c *CheckAreaMgr) CheckAreaIedRelation() error { // 获取当前scd中需要检查的间隔 scdXmlObj, arealist, area_ruleid, err := c.getAreaCheckInfo() db := orm.NewOrm() if err != nil { logger.Logger.Error(err) return err } if area_ruleid == "" { return errors.New(fmt.Sprintf("未定义间隔装置的检查规则“间隔装置与检查模型不符”")) } if scdXmlObj == nil { return errors.New("无效的SCD") } scdNodeRule := new(ScdNodeRule) scdNode := new(ScdNode) model_refs := map[string][]orm.Params{} //模型定义的装置关系定义 area_ieds := map[string][]orm.Params{} //间隔下的装置列表 iedRelationMgr := new(SysCheckModelIedRelationMgr) for _, row := range arealist { //获取间隔标准装置及关系 modelid, _ := strconv.Atoi(tools.IsEmpty(row["model_id"])) //area_name := tools.IsEmpty(row["area_name"]) //area_type := tools.IsEmpty(row["area_type"]) //间隔模型类型 area_id := tools.IsEmpty(row["id"]) s, err := iedRelationMgr.GetListByModelid(modelid) if err != nil { logger.Logger.Error(err) return err } if len(s) == 0 { return errors.New(fmt.Sprintf("模型%d还未配置装置关系", modelid)) } model_refs[fmt.Sprintf("%d", modelid)] = s /* hasIeds := map[string]bool{} for _, row1 := range s { iedname := tools.IsEmpty(row1["from_ied_code"]) if !hasIeds[iedname] { hasIeds[iedname] = true } iedname = tools.IsEmpty(row1["to_ied_code"]) if !hasIeds[iedname] { hasIeds[iedname] = true } } */ s1 := []orm.Params{} _, err = db.Raw("select ied_name from t_data_check_area_ied where area_id=?", area_id).Values(&s1) if err != nil { logger.Logger.Error(err) return err } if len(s) == 0 { return errors.New(fmt.Sprintf("间隔%s未发现任何装置", tools.IsEmpty(row["area_name"]))) } area_ieds[area_id] = s1 } //装置关联关系分析 //先分析母联间隔,如果没有选择母联间隔时,主变间隔中不包含母联终端装置 HasAreaJ := false for _, row := range arealist { area_name := tools.IsEmpty(row["area_name"]) areaCode := tools.IsEmpty(row["area_type"]) //间隔模型类型 area_id := tools.IsEmpty(row["id"]) if areaCode == "J" { HasAreaJ = true modelid := tools.IsEmpty(row["model_id"]) c.cJ(modelid, scdXmlObj, scdNode, scdNodeRule, area_name, model_refs[modelid], area_ieds[area_id], "PE", area_ruleid) c.cJ(modelid, scdXmlObj, scdNode, scdNodeRule, area_name, model_refs[modelid], area_ieds[area_id], "PJ", area_ruleid) c.cJ(modelid, scdXmlObj, scdNode, scdNodeRule, area_name, model_refs[modelid], area_ieds[area_id], "PK", area_ruleid) c.cJ(modelid, scdXmlObj, scdNode, scdNodeRule, area_name, model_refs[modelid], area_ieds[area_id], "PF", area_ruleid) } } for _, row := range arealist { area_name := tools.IsEmpty(row["area_name"]) areaCode := tools.IsEmpty(row["area_type"]) //间隔模型类型 area_id := tools.IsEmpty(row["id"]) modelid := tools.IsEmpty(row["model_id"]) if areaCode == "J" { continue } if areaCode == "L" { //线路间隔 c.cL(modelid, scdXmlObj, scdNode, scdNodeRule, area_name, model_refs[modelid], area_ieds[area_id], area_ruleid) } if areaCode == "T" { //变压器齐间隔 c.cT(modelid, scdXmlObj, scdNode, scdNodeRule, area_name, model_refs[modelid], area_ieds[area_id], area_ruleid, HasAreaJ) } } scdNodeRule.CheckFinish() scdNodeRule.Flush() return nil } //检测装置功能分析 func (c *CheckAreaMgr) CheckIedFunc() error { return nil } //检测装置端子分析 func (c *CheckAreaMgr) CheckIedFcda() error { scdXmlObj, arealist, area_ruleid, err := c.getAreaCheckInfo() if err != nil { logger.Logger.Error(err) return err } if area_ruleid == "" { return errors.New(fmt.Sprintf("未定义间隔装置的检查规则“间隔装置与检查模型不符”")) } if scdXmlObj == nil { return errors.New("无效的SCD") } scdidStr := fmt.Sprintf("%d", c.ScdId) db := orm.NewOrm() //获取当前站的各电压等级 volRows := []orm.Params{} _, err = db.Raw("select t.vol, CAST(REPLACE(UPPER(g.name),'KV','') as SIGNED) volname from t_area_ied_relation t,global_const_code g where g.code=CONCAT('v_level_',t.vol) and g.parentcode='voltage_level' and t.vol!=999 and t.scd_id=? GROUP BY t.vol ORDER BY volname desc", c.ScdId).Values(&volRows) if err != nil { logger.Logger.Error(err) return err } if len(volRows) == 0 { logger.Logger.Error(errors.New("该scd未发现任何电压等级的装置")) return errors.New("该scd未发现任何电压等级的装置") } volMap := map[string]string{} volMap["hight"] = tools.IsEmpty(volRows[0]["vol"]) //高压电压 if len(volRows) == 2 { volMap["middle"] = "" volMap["low"] = volRows[1]["vol"].(string) //低压电压等级 } else { volMap["middle"] = volRows[1]["vol"].(string) //中压电压等级 volMap["low"] = volRows[len(volRows)-1]["vol"].(string) //低压电压等级 } scdNodeRule := new(ScdNodeRule) scdNodeRule.SetScdXmlObject(scdXmlObj) scdNodeMgr := new(ScdNode) //当前装置的所有信号发送端子列表 /* var getIedFcdas = func(iedname string) map[string]*node_attr.NFCDA { iedObj := scdNodeMgr.GetIed(scdXmlObj, tools.IsEmpty(c.ScdId), iedname) if iedObj == nil { return nil } fcdaObjList := map[string]*node_attr.NFCDA{} for _, t1 := range iedObj.AccessPoint { if t1.Server == nil || len(t1.Server.LDevice) == 0 { continue } for _, ld := range t1.Server.LDevice { if ld.LN0 != nil && len(ld.LN0.DataSet) > 0 { for _, t2 := range ld.LN0.DataSet { for _, t3 := range t2.FCDA { re, _ := scdNodeRule.IedFcdaExist(iedname, t3.LdInst, t3.LnClass, t3.LnInst, t3.Prefix, t3.DoName, t3.DaName) if re == nil { continue } doi := re.(*node_attr.NDOI) fcdaObjList[doi.Desc] = t3 } } } } } return fcdaObjList } */ //从装置类型code中解析出类型代码和电压等级 var getIedTypeAndVolCode = func(ied_type string) (string, string) { tmp := strings.Split(ied_type, "#") ied_type = tmp[0] vol := "" if len(tmp) == 2 { vol = tmp[1] //电压级别 } return ied_type, vol } //从间隔装置中过滤出指定类型的IED 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 { iedname := tools.IsEmpty(r["ied_name"]) desc := tools.IsEmpty(r["ied_desc"]) if strings.HasPrefix(iedname, ied_type) || (abCode != "" && strings.HasSuffix(iedname, abCode)) { //未指明装置为本体或者差动装置时,排除这2类装置 if volLevelCode == "0" { if strings.Contains(desc, "本体") { iedlst = append(iedlst, r) } continue } if volLevelCode == "C" { if strings.Contains(desc, "差动") { iedlst = append(iedlst, r) } continue } if !strings.Contains(desc, "本体") && !strings.Contains(desc, "差动") { iedlst = append(iedlst, r) } } } } else { tmpLst := map[string]orm.Params{} for _, r := range s1 { iedname := tools.IsEmpty(r["ied_name"]) desc := tools.IsEmpty(r["ied_desc"]) if strings.HasPrefix(iedname, ied_type) || (abCode != "" && strings.HasSuffix(iedname, abCode)) { //未指明装置为本体或者差动装置时,排除这2类装置 if volLevelCode == "0" { if strings.Contains(desc, "本体") { tmpLst[iedname] = r } continue } if volLevelCode == "C" { if strings.Contains(desc, "差动") { tmpLst[iedname] = r } continue } if !strings.Contains(desc, "本体") && !strings.Contains(desc, "差动") { tmpLst[iedname] = r } } } //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 } if volLevelCode == "M" { iedlst = m } if volLevelCode == "L" { iedlst = l } } return iedlst } //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"])) area_name := tools.IsEmpty(row["area_name"]) //area_type := tools.IsEmpty(row["area_type"]) //间隔模型类型 area_id := tools.IsEmpty(row["id"]) logger.Logger.Debug(fmt.Sprintf("开始检查模型%d的间隔%s", modelid, area_name)) area_id_int, _ := strconv.Atoi(area_id) ieddoilist := []orm.Params{} db.Raw("select * from t_data_doi_mapping where area_id=? limit 0,1", area_id_int).Values(&ieddoilist) if ieddoilist != nil && len(ieddoilist) > 0 { fmt.Println("============间隔" + area_name + "直接比对端子================") //直接比对端子 c.CheckIedDoi(scdXmlObj, scdNodeRule, scdNodeMgr, area_id_int, area_ruleid) continue } //获取装置分组信息 bgm := new(SysCheckModelIedtypeGroupMgr) bgm.Model = T_data_model_iedtype_group{ModelId: modelid} groupList := bgm.List() //获取该间隔模型配置的所有端子关系列表 tmpMgr := new(SysCheckModelFcdaRalationMgr) tmpMgr.Model.ModelId = modelid funclist := map[string][]orm.Params{} key := fmt.Sprintf("%d-%d", modelid, area_id) v, h := modelFcda.Load(key) if h { funclist = v.(map[string][]orm.Params) } else { funclist, _ = tmpMgr.GetModelAllFcdaRef() if funclist == nil || len(funclist) == 0 { logger.Logger.Error(errors.New(fmt.Sprintf("模型%d下的间隔%s还未配置装置端子关联关系", modelid, area_name))) continue } modelFcda.Store(key, funclist) } //获取该间隔下该类型的装置 s1 := []orm.Params{} _, err = db.Raw("select ied_name,ied_no,'' ied_desc from t_data_check_area_ied where area_id=?", area_id).Values(&s1) for pos, sr := range s1 { iedname := tools.IsEmpty(sr["ied_name"]) iedObj := scdNodeMgr.GetIed(scdXmlObj, scdidStr, iedname) iedObjDesc := "" if iedObj != nil { iedObjDesc = iedObj.Desc } s1[pos]["ied_desc"] = iedObjDesc } iedExtrefIsExistMap := map[string]int{} //确认已存在的装置端子 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 = typeMappingMgr.GetMappingType(modelid, v) } abCode := c.getIedTypeABCode(ts[0]) ied_type, vol := getIedTypeAndVolCode(ied_type) 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 = typeMappingMgr.GetMappingType(modelid, v) } fromAbCode := c.getIedTypeABCode(ts[1]) from_ied_type, vol2 := getIedTypeAndVolCode(fromiedcode) 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#%s%s未从类型%s%s的任何电压等级%s装置接收信号", ied_type, vol, abCode, from_ied_type, fromAbCode, vol2)) continue } 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, scdidStr, iedname) iedObjDesc := tools.IsEmpty(ied["ied_desc"]) if iedObjDesc == "" { logger.Logger.Error(fmt.Sprintf("信号接收装置%s未找到!", iedname)) continue } //遥信端子列表:仅根据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 = c.getIedExtRefs(scdXmlObj, scdNodeRule, scdNodeMgr, iedname) } for outiedname, outied := range outiedlist { if iedname == outiedname { continue } //从装置所有的输入端子中过滤出当前输入装置的部分端子 extreflist2 := map[string]*node_attr.NExtRef{} for desc, item := range extreflist { if item.IedName == outiedname { extreflist2[desc] = item } } logger.Logger.Debug(fmt.Sprintf("正在匹配装置%s与%s的端子关联关系", iedname, outiedname)) outIedObj := scdNodeMgr.GetIed(scdXmlObj, scdidStr, outiedname) outIedObjDesc := "" if outIedObj == nil { logger.Logger.Error(fmt.Sprintf("信号输出装置%s未找到!", outiedname)) } else { outIedObjDesc = outIedObj.Desc } if iedObj == nil && outIedObj == nil { continue } //outiedFcdaList := getIedFcdas(outiedname) //输入装置的信号输出端子 //检查是否有错误和缺失的端子 for _, r := range refrow { 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"]) fcda_name_exp := tools.IsEmpty(r["from_fcda_match_exp"]) funcName := tools.IsEmpty(r["to_func_name"]) //端子编号处理 if strings.Index(extref_name_exp, "{no}") > -1 { extref_name_exp = strings.ReplaceAll(extref_name_exp, "{no}", tools.IsEmpty(ied["ied_no"])) } if strings.Index(fcda_name_exp, "{no}") > -1 { fcda_name_exp = strings.ReplaceAll(fcda_name_exp, "{no}", tools.IsEmpty(outied["ied_no"])) } funcExist := false //判断接收端设计的端子是否存在 extrefObj := new(node_attr.NExtRef) fcda2Exist := false isYx := false for desc, item := range extreflist2 { if item.IedName != outiedname { continue } //判断是否是遥信端子 /* if strings.Index(funcName, "遥信") > -1 && 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 } */ //logger.Logger.Debug(fmt.Sprintf("接收装置%s的设计端子%s匹配SCD ExtRef DO端子%s", iedname, extref_name_exp, desc)) macthResult, _ := tools.RexGroupTestMatch(extref_name_exp, desc) if macthResult { funcExist = true extrefObj = item extref_name = desc //实际的端子名称 break } } if isYx { //遥信端子不处理 continue } if funcExist { //判断输出端端子是否存在 doi, _ := scdNodeRule.IedFcdaExist(extrefObj.IedName, extrefObj.LdInst, extrefObj.LnClass, extrefObj.LnInst, extrefObj.Prefix, extrefObj.DoName, "") if doi != nil { daname := "" if extrefObj.DaName != "" { daname = "." + extrefObj.DaName } fcda2Exist = true fcda_name = doi.(*node_attr.NDOI).Desc //实际的端子名称 if strings.Index(funcName, "遥信") > -1 { //2端端子名称需要完全相同 if extref_name != fcda_name { parse_result := fmt.Sprintf("间隔%s的装置%s遥信端子%s与装置%s端子%s关联错误:端子名称不相同", area_name, iedname, extref_name, outiedname, fcda_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": outIedObjDesc, "fcda_desc": extref_name, "fcda_addr": extrefObj.IntAddr, "out_fcda_desc": fcda_name, "out_fcda_addr": fmt.Sprintf("%s/%s%s%s.%s%s", extrefObj.LdInst, extrefObj.Prefix, extrefObj.LnClass, extrefObj.LnInst, extrefObj.DoName, daname), "error_type": "2", } //检查未通过 scdNodeRule.AppendFcdaCheckResult(re) } else { //判断是否是双点或单点遥信端子 extrefDoi := scdNodeRule.IedIntAddrExist(iedname, extrefObj.IntAddr) extrefDoiCdcCode := new(ScdMgr).GetDoiCdcInfo(scdXmlObj, c.ScdId, extrefDoi.(*node_attr.NDOI)) fcdaCdcCode := new(ScdMgr).GetDoiCdcInfo(scdXmlObj, c.ScdId, doi.(*node_attr.NDOI)) if extrefDoiCdcCode != fcdaCdcCode { //错误:cdc类型不一致 parse_result := fmt.Sprintf("间隔%s的接收装置%s遥信端子%s与输出装置%s端子%s的CDC类型不一致(%s,%s)", area_name, iedname, extrefDoi.(*node_attr.NDOI).Desc, outiedname, doi.(*node_attr.NDOI).Desc, extrefDoiCdcCode, fcdaCdcCode) 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": outIedObjDesc, "fcda_desc": extref_name, "fcda_addr": extrefObj.IntAddr, "out_fcda_desc": fcda_name, "out_fcda_addr": fmt.Sprintf("%s/%s%s%s.%s%s", extrefObj.LdInst, extrefObj.Prefix, extrefObj.LnClass, extrefObj.LnInst, extrefObj.DoName, daname), "error_type": "2", } //检查未通过 scdNodeRule.AppendFcdaCheckResult(re) } } continue } macthResult, _ := tools.RexGroupTestMatch(fcda_name_exp, fcda_name) if !macthResult { parse_result := fmt.Sprintf("间隔%s的装置%s端子%s与装置%s端子%s关联错误.匹配模式:%s", area_name, iedname, extref_name, outiedname, fcda_name, fcda_name_exp) 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": outIedObjDesc, "fcda_desc": extref_name, "fcda_addr": extrefObj.IntAddr, "out_fcda_desc": fcda_name, "out_fcda_addr": fmt.Sprintf("%s/%s%s%s.%s%s", extrefObj.LdInst, extrefObj.Prefix, extrefObj.LnClass, extrefObj.LnInst, extrefObj.DoName, daname), "error_type": "2", } //检查未通过 scdNodeRule.AppendFcdaCheckResult(re) } } } if !funcExist { parse_result := fmt.Sprintf("间隔%s的装置%s缺失与装置%s的端子%s定义.匹配模式:%s", area_name, iedname, outiedname, extref_name, extref_name_exp) 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": outiedname, "out_ied_desc": outIedObjDesc, "fcda_desc": extref_name, "fcda_addr": "", "out_fcda_desc": "", "out_fcda_addr": "", "error_type": "3", } //检查未通过 scdNodeRule.AppendFcdaCheckResult(re) } else if !fcda2Exist { //fcda2Exist为false表示实际的端子from ied不是当前设计的ied parse_result := fmt.Sprintf("间隔%s下%s的输入装置%s缺失端子%s.匹配模式:%s", area_name, iedname, outiedname, fcda_name, fcda_name_exp) 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": outiedname, "out_ied_desc": outIedObjDesc, "fcda_desc": extref_name, "fcda_addr": extrefObj.IntAddr, "out_fcda_desc": fcda_name, "out_fcda_addr": "", "error_type": "3", } //检查未通过 scdNodeRule.AppendFcdaCheckResult(re) } } //检查是否有多余(SCD中有不存在于设计中)的端子 for extrefdesc, r := range extreflist2 { //scd中的端子关系 //判断是否是当前输入装置和遥信端子 if r.IedName != outiedname || 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 } key := fmt.Sprintf("%s%s", iedname, extrefdesc) if iedExtrefIsExistMap[key] == 1 { continue } extref_name := "" fcda_name := "" isHave := false for _, r1 := range refrow { //设计的端子关系 extref_name_exp := tools.IsEmpty(r1["to_fcda_match_exp"]) if extrefdesc == extref_name_exp { //端子存在 isHave = true iedExtrefIsExistMap[key] = 1 break } //端子编号处理 if strings.Index(extref_name_exp, "{no}") > -1 { extref_name_exp = strings.ReplaceAll(extref_name_exp, "{no}", tools.IsEmpty(ied["ied_no"])) } macthResult, _ := tools.RexGroupTestMatch(extref_name_exp, extrefdesc) if macthResult { //端子存在 isHave = true iedExtrefIsExistMap[key] = 1 break } } if !isHave { daname := "" if r.DaName != "" { daname = "." + r.DaName } doi := scdNodeRule.IedIntAddrExist(iedname, r.IntAddr) if doi != nil { extref_name = doi.(*node_attr.NDOI).Desc } 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": r.IedName, "out_ied_desc": tmpIedObj.Desc, "fcda_desc": extref_name, "fcda_addr": r.IntAddr, "out_fcda_desc": fcda_name, "out_fcda_addr": fmt.Sprintf("%s/%s%s%s.%s%s", r.LdInst, r.Prefix, r.LnClass, r.LnInst, r.DoName, daname), "error_type": "1", } //检查未通过 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, doiinfo := range yx { doiDesc := doiinfo[0] doiCdc := doiinfo[1] 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 { //端子缺失 tmpIedName := "" tmpIedDesc := "" if tmpIedObj != nil { tmpIedName = tmpIedObj.Name tmpIedDesc = tmpIedObj.Desc } 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": tmpIedName, "out_ied_desc": tmpIedDesc, "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) continue } if doiCdc != "" && (!YaoXinDBType[doiCdc] || !YaoXinSiType[doiCdc]) { //判断是否是双点或单点遥信端子 fcdaCdcCode := new(ScdMgr).GetDoiCdcInfo(scdXmlObj, c.ScdId, outFcdaDoi.(*node_attr.NDOI)) if doiCdc != fcdaCdcCode { //错误:cdc类型不一致 parse_result := fmt.Sprintf("间隔%s的接收装置%s遥信端子%s与输出装置%s端子%s的CDC类型不一致(%s,%s)", area_name, iedname, doiDesc, tmpIedObj.Name, outFcdaDoi.(*node_attr.NDOI).Desc, doiCdc, fcdaCdcCode) 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) continue } } 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 scdNodeRule.CheckFinish() scdNodeRule.Flush() return nil } func (c *CheckAreaMgr) CheckIedDoi(scdXmlObj *node_attr.SCL, scdNodeRule *ScdNodeRule, scdNodeMgr *ScdNode, area_id int, area_ruleid string) { db := orm.NewOrm() iedDois := []orm.Params{} //获取该间隔下该类型的装置 s1 := []orm.Params{} db.Raw("select ied_name,ied_no,'' ied_desc from t_data_check_area_ied where area_id=?", area_id).Values(&s1) isLoadExtref := map[string]map[string][]*node_attr.NExtRef{} for pos, sr := range s1 { iedname := tools.IsEmpty(sr["ied_name"]) iedObj := scdNodeMgr.GetIed(scdXmlObj, tools.IsEmpty(c.ScdId), iedname) iedObjDesc := "" if iedObj != nil { iedObjDesc = iedObj.Desc } s1[pos]["ied_desc"] = iedObjDesc //获取SCD现场端子列表 extreflist := isLoadExtref[iedname] if extreflist == nil { extreflist = c.getIedExtRefsByFormIedname(scdXmlObj, scdNodeRule, scdNodeMgr, iedname) } //获取设计端子列表 iedDois, _ = c.GetDoiMapping(0, area_id, iedname) for _, doi := range iedDois { extref_name := tools.IsEmpty(doi["doi_desc"]) //设计端子名称 outiedname := tools.IsEmpty(doi["in_ied_name"]) outFcdaDesc := tools.IsEmpty(doi["in_doi_desc"]) outIedObj := scdNodeMgr.GetIed(scdXmlObj, tools.IsEmpty(c.ScdId), outiedname) outIedObjDesc := outIedObj.Desc extrefObj0 := extreflist[outiedname+extref_name] if extrefObj0 == nil || len(extrefObj0) == 0 { //端子缺失 parse_result := fmt.Sprintf("装置%s缺失与装置%s的端子%s定义.", iedname, outiedname, 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": outiedname, "out_ied_desc": outIedObjDesc, "fcda_desc": extref_name, "fcda_addr": "", "out_fcda_desc": outFcdaDesc, "out_fcda_addr": "", "error_type": "2", } //检查未通过 scdNodeRule.AppendFcdaCheckResult(re) continue } extrefObj := new(node_attr.NExtRef) fcda_name := "" doi := new(node_attr.NDOI) isOk := false for _, extrefItem := range extrefObj0 { extrefObj = extrefItem doi0, _ := scdNodeRule.IedFcdaExist(outiedname, extrefObj.LdInst, extrefObj.LnClass, extrefObj.LnInst, extrefObj.Prefix, extrefObj.DoName, "") if doi0 != nil { doi = doi0.(*node_attr.NDOI) fcda_name = doi.Desc //实际的端子名称 if outFcdaDesc == fcda_name { isOk = true break } if strings.Index(outFcdaDesc, ".") > -1 { tmpStr := strings.Split(outFcdaDesc, ".") for _, dai := range doi.DAI { if fcda_name == tmpStr[0] && dai.Desc == tmpStr[1] && dai.Name == extrefObj.DaName { isOk = true break } } if isOk { //Da匹配成功 break } } } } if !isOk { daname := "" if extrefObj.DaName != "" { daname = "." + extrefObj.DaName } //错误 parse_result := fmt.Sprintf("装置%s的端子%s错误关联到装置%s端子%s,设计上应该关联到%s.", iedname, extref_name, outiedname, fcda_name, outFcdaDesc) 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": outiedname, "out_ied_desc": outIedObjDesc, "fcda_desc": extref_name, "fcda_addr": extrefObj.IntAddr, "out_fcda_desc": fcda_name, "out_fcda_addr": fmt.Sprintf("%s/%s%s%s.%s%s", extrefObj.LdInst, extrefObj.Prefix, extrefObj.LnClass, extrefObj.LnInst, extrefObj.DoName, daname), "error_type": "1", } //检查未通过 scdNodeRule.AppendFcdaCheckResult(re) } } //判断实际端子是否多的 } } //当前装置的所有信号接收端子列表 func (c *CheckAreaMgr) getIedExtRefs(scdXmlObj *node_attr.SCL, scdNodeRule *ScdNodeRule, scdNodeMgr *ScdNode, iedname string) map[string]*node_attr.NExtRef { iedObj := scdNodeMgr.GetIed(scdXmlObj, tools.IsEmpty(c.ScdId), iedname) if iedObj == nil { logger.Logger.Debug(fmt.Sprintf("在scd:%d中未发现装置%s", c.ScdId, iedname)) return nil } fcdaObjList := map[string]*node_attr.NExtRef{} for _, t1 := range iedObj.AccessPoint { if t1.Server == nil || len(t1.Server.LDevice) == 0 { continue } for _, ld := range t1.Server.LDevice { if ld.LN0 != nil && ld.LN0.Inputs != nil { for _, t2 := range ld.LN0.Inputs.ExtRef { doi := scdNodeRule.IedIntAddrExist(iedname, t2.IntAddr) if doi == nil { continue } fcdaObjList[doi.(*node_attr.NDOI).Desc] = t2 } } for _, ln := range ld.LN { if ln.Inputs == nil { continue } for _, t2 := range ln.Inputs.ExtRef { doi := scdNodeRule.IedIntAddrExist(iedname, t2.IntAddr) if doi == nil { continue } fcdaObjList[doi.(*node_attr.NDOI).Desc] = t2 } } } } return fcdaObjList } //当前装置的所有信号接收端子列表 func (c *CheckAreaMgr) getIedExtRefsByFormIedname(scdXmlObj *node_attr.SCL, scdNodeRule *ScdNodeRule, scdNodeMgr *ScdNode, iedname string) map[string][]*node_attr.NExtRef { iedObj := scdNodeMgr.GetIed(scdXmlObj, tools.IsEmpty(c.ScdId), iedname) if iedObj == nil { logger.Logger.Debug(fmt.Sprintf("在scd:%d中未发现装置%s", c.ScdId, iedname)) return nil } fcdaObjList := map[string][]*node_attr.NExtRef{} for _, t1 := range iedObj.AccessPoint { if t1.Server == nil || len(t1.Server.LDevice) == 0 { continue } for _, ld := range t1.Server.LDevice { if ld.LN0 != nil && ld.LN0.Inputs != nil { for _, t2 := range ld.LN0.Inputs.ExtRef { doi := scdNodeRule.IedIntAddrExist(iedname, t2.IntAddr) if doi == nil { continue } doidesc := doi.(*node_attr.NDOI).Desc key := t2.IedName + doidesc if fcdaObjList[key] != nil { fcdaObjList[key] = append(fcdaObjList[key], t2) } else { fcdaObjList[key] = []*node_attr.NExtRef{t2} } for _, dai := range doi.(*node_attr.NDOI).DAI { if dai.Desc != "" { key1 := key + "." + dai.Desc if fcdaObjList[key1] != nil { fcdaObjList[key1] = append(fcdaObjList[key1], t2) } else { fcdaObjList[key1] = []*node_attr.NExtRef{t2} } } } } } for _, ln := range ld.LN { if ln.Inputs == nil { continue } for _, t2 := range ln.Inputs.ExtRef { doi := scdNodeRule.IedIntAddrExist(iedname, t2.IntAddr) if doi == nil { continue } doidesc := doi.(*node_attr.NDOI).Desc key := t2.IedName + doidesc if fcdaObjList[key] != nil { fcdaObjList[key] = append(fcdaObjList[key], t2) } else { fcdaObjList[key] = []*node_attr.NExtRef{t2} } for _, dai := range doi.(*node_attr.NDOI).DAI { if dai.Desc != "" { key1 := key + "." + dai.Desc if fcdaObjList[key1] != nil { fcdaObjList[key1] = append(fcdaObjList[key1], t2) } else { fcdaObjList[key1] = []*node_attr.NExtRef{t2} } } } } } } } return fcdaObjList } //解析模型间隔。根据模型定义解析出间隔中的装置 func (c *CheckAreaMgr) ParseModelArea(isInit ...bool) { c.Init(c.ScdId) key := fmt.Sprintf("%d-checkinfo", c.ScdId) areaCheckInfo.Delete(key) // 取得当前scd所有ied及名称解析结果 dbo := orm.NewOrm() sql := "select t.*,t1.name area_name from t_area_ied_relation t,t_substation_area t1 where t.area_id=t1.id and t.scd_id=? order by t.p_type" iedlst := []orm.Params{} _, err := dbo.Raw(sql, c.ScdId).Values(&iedlst) if err != nil { logger.Logger.Error(err) return } logger.Logger.Debug(fmt.Sprintf("=====总装置数:%d", len(iedlst))) iedMap := map[string]orm.Params{} for _, r := range iedlst { iedMap[tools.IsEmpty(r["ied_name"])] = r } //先分析母联间隔,如果没有选择母联间隔时,主变间隔中不包含母联终端装置 HasAreaJ := false for _, row := range c.CheckModelList { areaCode := tools.IsEmpty(row["area_type_code"]) if areaCode == "J" { HasAreaJ = true modelid, _ := strconv.Atoi(tools.IsEmpty(row["id"])) //modelname := tools.IsEmpty(row["model_name"]) iedtypes := tools.IsEmpty(row["ied_types"]) mapptype := new(SysCheckModelIedtypeMappingMgr) mapptype.Model = T_data_model_iedtype_mapping{ModelId: modelid} mappresult := mapptype.List() if len(mappresult) > 0 { tmp := []string{} for _, r := range strings.Split(iedtypes, ",") { if mappresult[r] != "" { tmp = append(tmp, mappresult[r]) } else { tmp = append(tmp, r) } } iedtypes = strings.Join(tmp, ",") } tmpTypes := strings.Split(iedtypes, ",") masterType := "" for _, k := range tmpTypes { if k[0:1] == "P" && k != "PM" { masterType = k break } } if masterType == "" { continue } volcode := strings.ReplaceAll(tools.IsEmpty(row["vol_code"]), "v_level_", "") c.pJ(modelid, volcode, iedtypes, iedMap, masterType) } } for _, row := range c.CheckModelList { //逐一分析模型定义 modelid, _ := strconv.Atoi(tools.IsEmpty(row["id"])) modelname := tools.IsEmpty(row["model_name"]) iedtypes := tools.IsEmpty(row["ied_types"]) //模型对应的间隔代码 /* S 站用变压器 C 电容器 B 断路器 K 母分 J 母联 M 母线 X 电抗器 L 线路 T 主变压器 */ areaCode := tools.IsEmpty(row["area_type_code"]) //模型对应的电压等级 //10:10KV 35:35KV 66:66KV 11:110KV 22:220KV 50:500KV 75:750KV 33:330KV T0:1000KV volcode := strings.ReplaceAll(tools.IsEmpty(row["vol_code"]), "v_level_", "") if modelname == "" || iedtypes == "" { continue } //母联间隔已经提前分析,如果没有母联间隔时,主变间隔中不包含母联终端装置 if areaCode == "J" { continue } //获取模型中的自定义装置类型编码 mapptype := new(SysCheckModelIedtypeMappingMgr) mapptype.Model = T_data_model_iedtype_mapping{ModelId: modelid} mappresult := mapptype.List() if len(mappresult) > 0 { tmp := []string{} for _, r := range strings.Split(iedtypes, ",") { if mappresult[r] != "" { tmp = append(tmp, mappresult[r]) } else { tmp = append(tmp, r) } } iedtypes = strings.Join(tmp, ",") } //获取模型内中装备关系定义 //主变间隔分析:需要查站内该电压等级下的高中低压侧装置或者高低压装置组成一个间隔,以主变保护装置(PT)为起始 if areaCode == "T" { c.pT(modelid, volcode, iedtypes, iedMap, HasAreaJ) } //线路保护间隔分析:以线路保护测控装置(PL)为开始分析 if areaCode == "L" { c.pL(modelid, volcode, iedtypes, iedMap) } //母线间隔分析:PM if areaCode == "M" { c.pM(modelid, volcode, iedtypes, iedMap) } } } //根据默认装置类型获取其类型编码。如果没有自定义编码,则返回默认编码。返回编码时已自动去除套别标识 func (c *CheckAreaMgr) getIedTypeCode(modelid int, iedtype string) string { mapptype := new(SysCheckModelIedtypeMappingMgr) mappresult := mapptype.GetMappingType(modelid, iedtype) ptCode := iedtype if mappresult != "" { ptCode = mappresult //自定义主变保护编码 } return strings.Split(ptCode, "-")[0] } //变压器间隔分析 func (c *CheckAreaMgr) pT(modelid int, vol, iedtypes string, ieds map[string]orm.Params, HasAreaJ bool) { scdParseMgr := new(ScdParse) scdNodeMgr := new(ScdNode) db := orm.NewOrm() //获取装置分组信息 bgm := new(SysCheckModelIedtypeGroupMgr) bgm.Model = T_data_model_iedtype_group{ModelId: modelid} groupList := bgm.List() scdXmlObj, _ := scdParseMgr.GetScdXmlObjectBySCDID(tools.IsEmpty(c.ScdId)) //获取当前站的各电压等级 volRows := []orm.Params{} _, err := db.Raw("select t.vol, CAST(REPLACE(UPPER(g.name),'KV','') as SIGNED) volname from t_area_ied_relation t,global_const_code g where g.code=CONCAT('v_level_',t.vol) and g.parentcode='voltage_level' and t.vol!=999 and t.scd_id=? GROUP BY t.vol ORDER BY volname desc", c.ScdId).Values(&volRows) if err != nil { logger.Logger.Error(err) return } if len(volRows) == 0 { logger.Logger.Error(errors.New("该scd未发现任何电压等级的装置")) return } volMap := map[string]string{} volMap["hight"] = tools.IsEmpty(volRows[0]["vol"]) //高压电压 if len(volRows) == 2 { volMap["middle"] = "" volMap["low"] = volRows[1]["vol"].(string) //低压电压等级 } else { volMap["middle"] = volRows[1]["vol"].(string) //中压电压等级 volMap["low"] = volRows[len(volRows)-1]["vol"].(string) //低压电压等级 } var getIedByDesc = func(iedinfo map[string]orm.Params, iedtpye string, desc string) ([]string, []string) { scdParseMgr := new(ScdParse) scdXmlObj, _ := scdParseMgr.GetScdXmlObjectBySCDID(tools.IsEmpty(c.ScdId)) r1 := []string{} r2 := []string{} for vn, row := range iedinfo { if strings.HasPrefix(vn, iedtpye) { v := tools.IsEmpty(row["ied_desc"]) if v == "" { if scdXmlObj == nil { continue } scdNode := new(ScdNode) ied := scdNode.GetIed(scdXmlObj, "", vn) if ied == nil { continue } v = ied.Desc } if strings.Contains(v, desc) { r1 = append(r1, v) r2 = append(r2, vn) } } } return r1, r2 } //判断是否将各电压等级的保护装置合并还是分开的间隔 //如果存在高中低压的保护装置,则说明是分开的主变间隔 ptCode := c.getIedTypeCode(modelid, "PT") volValue := "" //电压等级值 if strings.Index(","+iedtypes+",", ","+ptCode+",") == -1 { //判断主变保护是否是按电压等级分开配置的模型 h, m, l := c.getIedListByVol(ptCode, ieds, volMap) if strings.Contains(iedtypes, ptCode+"#H") && len(h) > 0 { //高压侧装置 volValue = "H" } else if strings.Contains(iedtypes, ptCode+"#M") && len(m) > 0 { volValue = "M" } else if strings.Contains(iedtypes, ptCode+"#L") && len(l) > 0 { volValue = "L" } } pmCode := c.getIedTypeCode(modelid, "PM") mmCode := c.getIedTypeCode(modelid, "MM") ctCode := c.getIedTypeCode(modelid, "CT") delPm := false delMm := false for _, row := range ieds { if tools.IsEmpty(row["vol"]) != vol || tools.IsEmpty(row["ied_type"]) != ptCode[0:1] || tools.IsEmpty(row["p_type"]) != ptCode[1:] { continue } if _, h := groupList[ptCode]; h { //装置如果是分组成员装置,则不用检测 continue } pl_iedname := tools.IsEmpty(row["ied_name"]) iednameParts := scdParseMgr.ParseIedName(pl_iedname) areadesc := tools.IsEmpty(row["ied_desc"]) if areadesc == "" { iedobj := scdNodeMgr.GetIed(scdXmlObj, "", pl_iedname) if iedobj != nil { areadesc = iedobj.Desc } else { areadesc = tools.IsEmpty(row["area_name"]) } } areadesc = strings.ReplaceAll(areadesc, "装置", "") if iednameParts[7] != "" && !strings.HasSuffix(areadesc, iednameParts[7]) { areadesc = areadesc + iednameParts[7] } areaInfo := []orm.Params{} db.Raw("select * from t_data_check_area where scd_id=? and area_type='T' and area_name=?", c.ScdId, areadesc).Values(&areaInfo) if len(areaInfo) > 0 { //间隔已存在,不再进行处理 continue } //添加间隔数据 dbdata := T_data_check_area{ ModelId: modelid, ScdId: c.ScdId, AreaType: "T", AreaName: areadesc, } newid, err := db.Insert(&dbdata) if err != nil { logger.Logger.Error(err) return } ins1 := "insert into t_data_check_area_ied(scd_id,area_id,ied_name,ied_type,p_type)values" insvalues := []string{} inAreaIedName := "" hasIedNameMap := map[string]int{} //insvalues = append(insvalues, fmt.Sprintf("(%d,%d,'%s','%s','%s')", c.ScdId, newid, inAreaIedName, "P", "T")) for _, ty := range strings.Split(iedtypes, ",") { inAreaIedName = "" if _, h := groupList[ty]; h { //装置如果是分组成员装置,则不用检测 continue } //判断是否指定的套别标识 abCode := c.getIedTypeABCode(ty) if abCode != "" { iednameParts[7] = abCode } tyCode := c.getIedTypeCode(modelid, ty) if _, h := groupList[tyCode]; h { //装置如果是分组成员装置,则不用检测 continue } if tyCode[0:len(pmCode)] == pmCode { //PM和MM最后处理 delPm = true continue } if tyCode[0:len(mmCode)] == mmCode { //PM和MM最后处理 delMm = true continue } ptype := strings.Split(tyCode, "#")[0] //判断间隔是否需要包含差动装置 if strings.Contains(tyCode, "#C") { _, iedname := getIedByDesc(ieds, ptype, "差动") if len(iedname) > 0 { for _, in := range iedname { tmpIednameParts := scdParseMgr.ParseIedName(in) if tmpIednameParts[6] == iednameParts[6] && tmpIednameParts[7] == iednameParts[7] { if hasIedNameMap[in] == 1 { break } hasIedNameMap[in] = 1 insvalues = append(insvalues, fmt.Sprintf("(%d,%d,'%s','%s','%s')", c.ScdId, newid, in, ptype[0:1], ptype[1:])) break } } } continue } //判断间隔是否需要包含本体保护装置 if strings.Contains(tyCode, "#0") { _, iedname := getIedByDesc(ieds, ptype, "本体") if len(iedname) > 0 { for _, in := range iedname { tmpIednameParts := scdParseMgr.ParseIedName(in) if tmpIednameParts[6] == iednameParts[6] && tmpIednameParts[7] == iednameParts[7] { if hasIedNameMap[in] == 1 { break } hasIedNameMap[in] = 1 insvalues = append(insvalues, fmt.Sprintf("(%d,%d,'%s','%s','%s')", c.ScdId, newid, in, ptype[0:1], ptype[1:])) break } } } continue } dlIeds := []orm.Params{} imtcode := c.getIedTypeCode(modelid, tyCode) 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" || strings.Contains(tyCode, "#H") { dlIeds = append(dlIeds, h...) } if volValue == "" || volValue == "M" || strings.Contains(tyCode, "#M") { dlIeds = append(dlIeds, m...) } if volValue == "" || volValue == "L" || strings.Contains(tyCode, "#L") { dlIeds = append(dlIeds, l...) } for _, r := range dlIeds { inAreaIedName = tools.IsEmpty(r["ied_name"]) if hasIedNameMap[inAreaIedName] == 1 { continue } hasIedNameMap[inAreaIedName] = 1 tmpIednameParts := scdParseMgr.ParseIedName(inAreaIedName) if tyCode[0:len(ctCode)] == ctCode { //CT单独处理。它可能不分AB套 lastChar := tmpIednameParts[7] if tmpIednameParts[6] == iednameParts[6] && (lastChar == "" || lastChar == iednameParts[7]) { insvalues = append(insvalues, fmt.Sprintf("(%d,%d,'%s','%s','%s')", c.ScdId, newid, inAreaIedName, ptype[0:1], ptype[1:])) } } else { if tmpIednameParts[6] == iednameParts[6] && tmpIednameParts[7] == iednameParts[7] { insvalues = append(insvalues, fmt.Sprintf("(%d,%d,'%s','%s','%s')", c.ScdId, newid, inAreaIedName, ptype[0:1], ptype[1:])) } } } } //最后处理PM和MM inAreaIedName = c.getPMName(iednameParts, ieds) if delPm && inAreaIedName != "" { insvalues = append(insvalues, fmt.Sprintf("(%d,%d,'%s','%s','%s')", c.ScdId, newid, inAreaIedName, "P", "M")) //pmIedNameParts := scdParseMgr.ParseIedName(pmIedName) //使用PM装置的名称去定义MM的名称 inAreaIedName = c.getMMName(iednameParts, ieds, iednameParts[7]) if delMm && inAreaIedName != "" { insvalues = append(insvalues, fmt.Sprintf("(%d,%d,'%s','%s','%s')", c.ScdId, newid, inAreaIedName, "M", "M")) } } //写数据库 if len(insvalues) > 0 { _, err = db.Raw(ins1 + strings.Join(insvalues, ",")).Exec() } if err != nil { logger.Logger.Error(err) return } } } //线路间隔分析 //vol:电压等级 func (c *CheckAreaMgr) pL(modelid int, vol, iedtypes string, ieds map[string]orm.Params) { scdParseMgr := new(ScdParse) scdNodeMgr := new(ScdNode) db := orm.NewOrm() //获取装置分组信息 bgm := new(SysCheckModelIedtypeGroupMgr) bgm.Model = T_data_model_iedtype_group{ModelId: modelid} groupList := bgm.List() scdXmlObj, _ := scdParseMgr.GetScdXmlObjectBySCDID(tools.IsEmpty(c.ScdId)) iedCode := c.getIedTypeCode(modelid, "PL") for _, row := range ieds { if tools.IsEmpty(row["vol"]) != vol || tools.IsEmpty(row["ied_type"]) != iedCode[0:1] || tools.IsEmpty(row["p_type"]) != iedCode[1:] { continue } mmIedName := "" pl_iedname := tools.IsEmpty(row["ied_name"]) if _, h := groupList[pl_iedname]; h { //装置如果是分组成员装置,则不用检测 continue } iednameParts := scdParseMgr.ParseIedName(pl_iedname) areadesc := tools.IsEmpty(row["ied_desc"]) if areadesc == "" { iedobj := scdNodeMgr.GetIed(scdXmlObj, "", pl_iedname) if iedobj != nil { areadesc = iedobj.Desc } else { areadesc = tools.IsEmpty(row["area_name"]) } } areadesc = strings.ReplaceAll(areadesc, "装置", "") if iednameParts[7] != "" && !strings.HasSuffix(areadesc, iednameParts[7]) { areadesc = areadesc + iednameParts[7] } areaInfo := []orm.Params{} db.Raw("select * from t_data_check_area where scd_id=? and area_type='L' and area_name=?", c.ScdId, areadesc).Values(&areaInfo) if len(areaInfo) > 0 { //间隔已存在,不再进行处理 continue } //添加间隔数据 dbdata := T_data_check_area{ ModelId: modelid, ScdId: c.ScdId, AreaType: "L", AreaName: areadesc, } newid, err := db.Insert(&dbdata) if err != nil { logger.Logger.Error(err) return } hasIedNameMap := map[string]int{} ins1 := "insert into t_data_check_area_ied(scd_id,area_id,ied_name,ied_type,p_type)values" insvalues := []string{} pmCode := c.getIedTypeCode(modelid, "PM") mmCode := c.getIedTypeCode(modelid, "MM") clCode := c.getIedTypeCode(modelid, "CL") for _, ty := range strings.Split(iedtypes, ",") { inAreaIedName := "" if _, h := groupList[ty]; h { //装置如果是分组成员装置,则不用检测 continue } abCode := c.getIedTypeABCode(ty) if abCode != "" { iednameParts[7] = abCode } tyCode := c.getIedTypeCode(modelid, ty) if _, h := groupList[tyCode]; h { //装置如果是分组成员装置,则不用检测 continue } ptype := strings.Split(tyCode, "#")[0] if ptype == pmCode { //母线保护和母线合并单元装置编号跟随变压器 //查找变压器编号 //1号变压器->2号变压器->3号 inAreaIedName = c.getPMName(iednameParts, ieds) if hasIedNameMap[inAreaIedName] == 1 { continue } hasIedNameMap[inAreaIedName] = 1 insvalues = append(insvalues, fmt.Sprintf("(%d,%d,'%s','%s','%s')", c.ScdId, newid, inAreaIedName, ptype[0:1], ptype[1:2])) continue } else if ptype == mmCode { //最后处理 continue } else { inAreaIedName = ptype + iednameParts[3] + iednameParts[4] + iednameParts[5] + iednameParts[6] + iednameParts[7] } if strings.Index("ABCDE", iednameParts[7]) > -1 { //最后一位是字母则说明是AB套 switch ptype { case clCode: if strings.Contains(iedtypes, "PLC") || strings.Contains(iedtypes, "PCL") { //不处理CL装置 } else { //测控装置,先判断是否分了AB套,没有标识(ied名称的最后一位是否是字母)则说明是多套共用装置 if ieds[inAreaIedName] == nil { //当前测控装置没有分AB套 inAreaIedName = ptype + iednameParts[3] + iednameParts[4] + iednameParts[5] + iednameParts[6] } } break default: break } } if hasIedNameMap[inAreaIedName] == 1 { continue } hasIedNameMap[inAreaIedName] = 1 insvalues = append(insvalues, fmt.Sprintf("(%d,%d,'%s','%s','%s')", c.ScdId, newid, inAreaIedName, ptype[0:1], ptype[1:])) } _, err = db.Raw(ins1 + strings.Join(insvalues, ",")).Exec() if err != nil { logger.Logger.Error(err) return } //如果mm装置还未确定 if mmIedName == "" { //pmIedNameParts := scdParseMgr.ParseIedName(pmIedName) inAreaIedName := c.getMMName(iednameParts, ieds, iednameParts[7]) _, err = db.Raw(ins1 + fmt.Sprintf("(%d,%d,'%s','%s','%s')", c.ScdId, newid, inAreaIedName, "M", "M")).Exec() if err != nil { logger.Logger.Error(err) return } } } } //母联间隔分析 func (c *CheckAreaMgr) pJ(modelid int, vol, iedtypes string, ieds map[string]orm.Params, pjIed string) { scdParseMgr := new(ScdParse) scdNodeMgr := new(ScdNode) db := orm.NewOrm() pjIed = c.getIedTypeCode(modelid, pjIed) scdXmlObject, _ := scdParseMgr.GetScdXmlObjectBySCDID(tools.IsEmpty(c.ScdId)) for _, row := range ieds { if tools.IsEmpty(row["vol"]) != vol || tools.IsEmpty(row["ied_type"]) != pjIed[0:1] || tools.IsEmpty(row["p_type"]) != pjIed[1:2] { continue } pmIedName := "" mmIedName := "" pl_iedname := tools.IsEmpty(row["ied_name"]) iednameParts := scdParseMgr.ParseIedName(pl_iedname) areadesc := tools.IsEmpty(row["ied_desc"]) if areadesc == "" { iedobj := scdNodeMgr.GetIed(scdXmlObject, "", pl_iedname) if iedobj != nil { areadesc = iedobj.Desc } else { areadesc = tools.IsEmpty(row["area_name"]) } } areadesc = strings.ReplaceAll(areadesc, "装置", "") if iednameParts[7] != "" && !strings.HasSuffix(areadesc, iednameParts[7]) { areadesc = areadesc + iednameParts[7] } areaInfo := []orm.Params{} db.Raw("select * from t_data_check_area where scd_id=? and area_type='J' and area_name=?", c.ScdId, areadesc).Values(&areaInfo) if len(areaInfo) > 0 { //间隔已存在,不再进行处理 continue } //添加间隔数据 dbdata := T_data_check_area{ ModelId: modelid, ScdId: c.ScdId, AreaType: "J", AreaName: areadesc, } newid, err := db.Insert(&dbdata) if err != nil { logger.Logger.Error(err) return } hasIedNameMap := map[string]int{} ins1 := "insert into t_data_check_area_ied(scd_id,area_id,ied_name,ied_type,p_type)values" insvalues := []string{} pmCode := c.getIedTypeCode(modelid, "PM") mmCode := c.getIedTypeCode(modelid, "MM") for _, ty := range strings.Split(iedtypes, ",") { abCode := c.getIedTypeABCode(ty) if abCode != "" { iednameParts[7] = abCode } tyCode := c.getIedTypeCode(modelid, ty) ptype := strings.Split(tyCode, "#")[0] inAreaIedName := "" if ptype == pmCode { //母线保护和母线合并单元装置编号跟随变压器 //查找变压器编号 //1号变压器->2号变压器->3号 inAreaIedName = c.getPMName(iednameParts, ieds) pmIedName = inAreaIedName if mmIedName == "" { if hasIedNameMap[inAreaIedName] == 1 { continue } hasIedNameMap[inAreaIedName] = 1 insvalues = append(insvalues, fmt.Sprintf("(%d,%d,'%s','%s','%s')", c.ScdId, newid, inAreaIedName, ptype[0:1], ptype[1:])) pmIedNameParts := scdParseMgr.ParseIedName(pmIedName) //使用PM装置的名称去定义MM的名称 tyCode = "MM" inAreaIedName = c.getMMName(pmIedNameParts, ieds, iednameParts[7]) mmIedName = inAreaIedName } } else if ptype == mmCode { if pmIedName != "" && mmIedName == "" { pmIedNameParts := scdParseMgr.ParseIedName(pmIedName) //使用PM装置的名称去定义MM的名称 inAreaIedName = c.getMMName(pmIedNameParts, ieds, iednameParts[7]) mmIedName = inAreaIedName } else { continue } } else { inAreaIedName = ptype + iednameParts[3] + iednameParts[4] + iednameParts[5] + iednameParts[6] + iednameParts[7] //判断与基准保护装置相同套号的装置是否存在 if ieds[inAreaIedName] == nil { //尝试去除套号 inAreaIedName = ptype + iednameParts[3] + iednameParts[4] + iednameParts[5] + iednameParts[6] } } if hasIedNameMap[inAreaIedName] == 1 { continue } hasIedNameMap[inAreaIedName] = 1 insvalues = append(insvalues, fmt.Sprintf("(%d,%d,'%s','%s','%s')", c.ScdId, newid, inAreaIedName, ptype[0:1], ptype[1:])) } _, err = db.Raw(ins1 + strings.Join(insvalues, ",")).Exec() if err != nil { logger.Logger.Error(err) return } } } //母线间隔装置提取 func (c *CheckAreaMgr) pM(modelid int, vol, iedtypes string, ieds map[string]orm.Params) { scdParseMgr := new(ScdParse) scdNodeMgr := new(ScdNode) db := orm.NewOrm() //获取当前站的各电压等级 volRows := []orm.Params{} _, err := db.Raw("select t.vol, CAST(REPLACE(UPPER(g.name),'KV','') as SIGNED) volname from t_area_ied_relation t,global_const_code g where g.code=CONCAT('v_level_',t.vol) and g.parentcode='voltage_level' and t.vol!=999 and t.scd_id=? GROUP BY t.vol ORDER BY volname desc", c.ScdId).Values(&volRows) if err != nil { logger.Logger.Error(err) return } if len(volRows) == 0 { logger.Logger.Error(errors.New("该scd未发现任何电压等级的装置")) return } volMap := map[string]string{} volMap["hight"] = tools.IsEmpty(volRows[0]["vol"]) //高压电压 if len(volRows) == 2 { volMap["middle"] = "" volMap["low"] = volRows[1]["vol"].(string) //低压电压等级 } else { volMap["middle"] = volRows[1]["vol"].(string) //中压电压等级 volMap["low"] = volRows[len(volRows)-1]["vol"].(string) //低压电压等级 } //获取装置分组信息 bgm := new(SysCheckModelIedtypeGroupMgr) bgm.Model = T_data_model_iedtype_group{ModelId: modelid} groupList := bgm.List() scdXmlObj, _ := scdParseMgr.GetScdXmlObjectBySCDID(tools.IsEmpty(c.ScdId)) iedCode := c.getIedTypeCode(modelid, "PM") for _, row := range ieds { if tools.IsEmpty(row["vol"]) != vol || tools.IsEmpty(row["ied_type"]) != iedCode[0:1] || tools.IsEmpty(row["p_type"]) != iedCode[1:] { continue } pl_iedname := tools.IsEmpty(row["ied_name"]) iednameParts := scdParseMgr.ParseIedName(pl_iedname) areadesc := tools.IsEmpty(row["ied_desc"]) if areadesc == "" { iedobj := scdNodeMgr.GetIed(scdXmlObj, "", pl_iedname) if iedobj != nil { areadesc = iedobj.Desc } else { areadesc = tools.IsEmpty(row["area_name"]) } } areadesc = strings.ReplaceAll(areadesc, "装置", "") areaInfo := []orm.Params{} db.Raw("select * from t_data_check_area where scd_id=? and area_type='M' and area_name=?", c.ScdId, areadesc).Values(&areaInfo) if len(areaInfo) > 0 { //间隔已存在,不再进行处理 continue } //添加间隔数据 dbdata := T_data_check_area{ ModelId: modelid, ScdId: c.ScdId, AreaType: "M", AreaName: areadesc, } newid, err := db.Insert(&dbdata) if err != nil { logger.Logger.Error(err) return } pmVol := "" //当前PM装置的电压等级 for volLevel, volValue := range volMap { if volValue == iednameParts[3]+iednameParts[4] { pmVol = volLevel break } } hasIedNameMap := map[string]int{} ins1 := "insert into t_data_check_area_ied(scd_id,area_id,ied_name,ied_type,p_type)values" insvalues := []string{} mmCode := c.getIedTypeCode(modelid, "MM") for _, ty := range strings.Split(iedtypes, ",") { inAreaIedName := "" if _, h := groupList[ty]; h { //装置如果是分组成员装置,则不用检测 continue } abCode := c.getIedTypeABCode(ty) if abCode == "" { //未特别指定套别时,采用主IED装置的套别 abCode = iednameParts[7] } tyCode := c.getIedTypeCode(modelid, ty) if _, h := groupList[tyCode]; h { //装置如果是分组成员装置,则不用检测 continue } ptype := strings.Split(tyCode, "#")[0] if tyCode == mmCode { //MM:母线合并单元 inAreaIedName = c.getMMName(iednameParts, ieds, abCode) if inAreaIedName != "" { if hasIedNameMap[inAreaIedName] == 1 { continue } hasIedNameMap[inAreaIedName] = 1 insvalues = append(insvalues, fmt.Sprintf("(%d,%d,'%s','%s','%s')", c.ScdId, newid, inAreaIedName, ptype[0:1], ptype[1:])) } } else { //需要查找同电压等级的所有同类同套装置 h, m, l := c.getIedListByVol(tyCode, ieds, volMap, abCode) //logger.Logger.Debug(fmt.Sprintf("当前PM装置:%s 电压等级:%s 提取类型:%s 套别:%s h:%+v,m:%+v,l:%+v", pl_iedname, pmVol, ptype, abCode, h, m, l)) switch pmVol { case "hight": for _, item := range h { inAreaIedName = tools.IsEmpty(item["ied_name"]) if hasIedNameMap[inAreaIedName] == 1 { continue } hasIedNameMap[inAreaIedName] = 1 insvalues = append(insvalues, fmt.Sprintf("(%d,%d,'%s','%s','%s')", c.ScdId, newid, inAreaIedName, ptype[0:1], ptype[1:])) } break case "middle": for _, item := range m { inAreaIedName = tools.IsEmpty(item["ied_name"]) if hasIedNameMap[inAreaIedName] == 1 { continue } hasIedNameMap[inAreaIedName] = 1 insvalues = append(insvalues, fmt.Sprintf("(%d,%d,'%s','%s','%s')", c.ScdId, newid, inAreaIedName, ptype[0:1], ptype[1:])) } break case "low": for _, item := range l { inAreaIedName = tools.IsEmpty(item["ied_name"]) if hasIedNameMap[inAreaIedName] == 1 { continue } hasIedNameMap[inAreaIedName] = 1 insvalues = append(insvalues, fmt.Sprintf("(%d,%d,'%s','%s','%s')", c.ScdId, newid, inAreaIedName, ptype[0:1], ptype[1:])) } break } } } if len(insvalues) == 0 { continue } _, err = db.Raw(ins1 + strings.Join(insvalues, ",")).Exec() if err != nil { logger.Logger.Error(err) return } } } //母联间隔装置关系检查 func (c *CheckAreaMgr) cJ(modelid string, scdXmlObj *node_attr.SCL, scdNodeMgr *ScdNode, scdNodeRule *ScdNodeRule, area_name string, ied_refs []orm.Params, area_ieds []orm.Params, pjIed, area_ruleid string) { modelidInt, _ := strconv.Atoi(modelid) pjIed = c.getIedTypeCode(modelidInt, pjIed) masterIed := new(node_attr.NIED) findIedName := "" for _, row2 := range area_ieds { findIedName = tools.IsEmpty(row2["ied_name"]) if strings.HasPrefix(findIedName, pjIed) { masterIed = scdNodeMgr.GetIed(scdXmlObj, "", findIedName) break } } if masterIed == nil { return } dealFromIed := map[string]int{} for _, row := range ied_refs { fromiedtype := c.getIedTypeCode(modelidInt, tools.IsEmpty(row["from_ied_code"])) toiedtype := c.getIedTypeCode(modelidInt, tools.IsEmpty(row["to_ied_code"])) reftype := tools.IsEmpty(row["in_type"]) tmpFromAreaIeds := []orm.Params{} tmpToAreaIeds := []orm.Params{} for _, row2 := range area_ieds { findIedName = tools.IsEmpty(row2["ied_name"]) if strings.HasPrefix(findIedName, fromiedtype) { masterIed = scdNodeMgr.GetIed(scdXmlObj, "", findIedName) if masterIed != nil { tmpFromAreaIeds = append(tmpFromAreaIeds, row2) } else { if dealFromIed[findIedName] == 0 { parse_result := fmt.Sprintf("间隔%s的装置%s缺失", area_name, findIedName) r := map[string]interface{}{"scdid": c.ScdId, "lineno": 0, "ruleid": area_ruleid, "nodeid": 0, "parse_result": parse_result} //检查未通过 scdNodeRule.AppendFcdaCheckResult(r) } dealFromIed[findIedName] = 1 } } if strings.HasPrefix(findIedName, toiedtype) { tmpToAreaIeds = append(tmpToAreaIeds, row2) } } if len(tmpFromAreaIeds) == 0 { continue } if len(tmpToAreaIeds) == 0 { parse_result := fmt.Sprintf("间隔%s缺失类型为%s的装置", area_name, toiedtype) r := map[string]interface{}{"scdid": c.ScdId, "lineno": 0, "ruleid": area_ruleid, "nodeid": 0, "parse_result": parse_result} //检查未通过 scdNodeRule.AppendFcdaCheckResult(r) continue } toIedname := "" for _, row2 := range tmpFromAreaIeds { findIedName = tools.IsEmpty(row2["ied_name"]) hasToIedRef := false hasToIed := false for _, row3 := range tmpToAreaIeds { toIedname = tools.IsEmpty(row3["ied_name"]) masterIed = scdNodeMgr.GetIed(scdXmlObj, "", toIedname) if masterIed != nil { hasToIed = true // 获取该ied的输出(ref_type为0)装置,并从中检测是否存在toiedtype类型的装置 inout, _ := scdNodeMgr.GetIedRelations(map[string]interface{}{"scd_id": c.ScdId, "ied_name": findIedName}) logger.Logger.Debug(fmt.Sprintf("ied:%s refs:%+v", findIedName, inout)) if inout != nil { outiedlist := inout[findIedName].(orm.Params)["list"].([]orm.Params) for _, ieditem := range outiedlist { outiedname := ieditem["ref_ied_name"].(string) if outiedname == toIedname && ieditem["ref_type"].(string) == "0" { hasToIedRef = true break } } } if !hasToIedRef { parse_result := fmt.Sprintf("间隔%s的装置%s缺失与装置%s的%s信号关联", area_name, findIedName, toIedname, reftype) r := map[string]interface{}{"scdid": c.ScdId, "lineno": 0, "ruleid": area_ruleid, "nodeid": 0, "parse_result": parse_result} //检查未通过 scdNodeRule.AppendFcdaCheckResult(r) } } } if !hasToIed { parse_result := fmt.Sprintf("间隔%s的装置%s缺失关联装置%s", area_name, findIedName, toIedname) r := map[string]interface{}{"scdid": c.ScdId, "lineno": 0, "ruleid": area_ruleid, "nodeid": 0, "parse_result": parse_result} //检查未通过 scdNodeRule.AppendFcdaCheckResult(r) } } } } //线路间隔装置关系检查 func (c *CheckAreaMgr) cL(modelid string, scdXmlObj *node_attr.SCL, scdNodeMgr *ScdNode, scdNodeRule *ScdNodeRule, area_name string, ied_refs []orm.Params, area_ieds []orm.Params, area_ruleid string) { modelidInt, _ := strconv.Atoi(modelid) //获取装置分组信息 bgm := new(SysCheckModelIedtypeGroupMgr) bgm.Model = T_data_model_iedtype_group{ModelId: modelidInt} groupList := bgm.List() plIed := c.getIedTypeCode(modelidInt, "PL") if v, h := groupList[plIed]; h { //装置如果是分组成员装置 plIed = v } masterIed := new(node_attr.NIED) findIedName := "" for _, row2 := range area_ieds { findIedName = tools.IsEmpty(row2["ied_name"]) if strings.HasPrefix(findIedName, plIed) { masterIed = scdNodeMgr.GetIed(scdXmlObj, "", findIedName) break } } if masterIed == nil { return } logger.Logger.Debug(fmt.Sprintf("开始分析模型%d的线路间隔关系", modelidInt)) dealFromIed := map[string]int{} for _, row := range ied_refs { fromiedtype := c.getIedTypeCode(modelidInt, tools.IsEmpty(row["from_ied_code"])) toiedtype := c.getIedTypeCode(modelidInt, tools.IsEmpty(row["to_ied_code"])) reftype := tools.IsEmpty(row["in_type"]) tmpFromAreaIeds := []orm.Params{} tmpToAreaIeds := []orm.Params{} for _, row2 := range area_ieds { findIedName = tools.IsEmpty(row2["ied_name"]) if strings.HasPrefix(findIedName, fromiedtype) { masterIed = scdNodeMgr.GetIed(scdXmlObj, "", findIedName) if masterIed != nil { tmpFromAreaIeds = append(tmpFromAreaIeds, row2) } else { if dealFromIed[findIedName] == 0 { parse_result := fmt.Sprintf("间隔%s的装置%s缺失", area_name, findIedName) r := map[string]interface{}{"scdid": c.ScdId, "lineno": 0, "ruleid": area_ruleid, "nodeid": 0, "parse_result": parse_result, "ied_name": findIedName, "ied_desc": "", "out_ied_name": "", "out_ied_desc": "", "fcda_desc": "", "fcda_addr": "", "out_fcda_desc": "", "out_fcda_addr": "", "error_type": "9", } //检查未通过 scdNodeRule.AppendFcdaCheckResult(r) } dealFromIed[findIedName] = 1 } } if strings.HasPrefix(findIedName, toiedtype) { tmpToAreaIeds = append(tmpToAreaIeds, row2) } } if len(tmpFromAreaIeds) == 0 { continue } if len(tmpToAreaIeds) == 0 { parse_result := fmt.Sprintf("间隔%s缺失类型为%s的装置", area_name, toiedtype) r := map[string]interface{}{"scdid": c.ScdId, "lineno": 0, "ruleid": area_ruleid, "nodeid": 0, "parse_result": parse_result} //检查未通过 scdNodeRule.AppendFcdaCheckResult(r) continue } toIedname := "" for _, row2 := range tmpFromAreaIeds { findIedName = tools.IsEmpty(row2["ied_name"]) hasToIedRef := false hasToIed := false for _, row3 := range tmpToAreaIeds { toIedname = tools.IsEmpty(row3["ied_name"]) masterIed = scdNodeMgr.GetIed(scdXmlObj, "", toIedname) if masterIed != nil { hasToIed = true // 获取该ied的输出(ref_type为0)装置,并从中检测是否存在toiedtype类型的装置 inout, _ := scdNodeMgr.GetIedRelations(map[string]interface{}{"scd_id": c.ScdId, "ied_name": findIedName}) //logger.Logger.Debug(fmt.Sprintf("ied:%s refs:%+v", findIedName, inout)) if inout != nil { outiedlist := inout[findIedName].(orm.Params)["list"].([]orm.Params) for _, ieditem := range outiedlist { outiedname := ieditem["ref_ied_name"].(string) if outiedname == toIedname && ieditem["ref_type"].(string) == "0" { hasToIedRef = true break } } } if !hasToIedRef { parse_result := fmt.Sprintf("间隔%s的装置%s缺失与装置%s的%s信号关联", area_name, findIedName, toIedname, reftype) r := map[string]interface{}{"scdid": c.ScdId, "lineno": 0, "ruleid": area_ruleid, "nodeid": 0, "parse_result": parse_result, "ied_name": findIedName, "ied_desc": "", "out_ied_name": toIedname, "out_ied_desc": "", "fcda_desc": "", "fcda_addr": "", "out_fcda_desc": "", "out_fcda_addr": "", "error_type": "9", } //检查未通过 scdNodeRule.AppendFcdaCheckResult(r) } } } if !hasToIed { parse_result := fmt.Sprintf("间隔%s的装置%s缺失关联装置%s", area_name, findIedName, toIedname) r := map[string]interface{}{"scdid": c.ScdId, "lineno": 0, "ruleid": area_ruleid, "nodeid": 0, "parse_result": parse_result, "ied_name": findIedName, "ied_desc": "", "out_ied_name": toIedname, "out_ied_desc": "", "fcda_desc": "", "fcda_addr": "", "out_fcda_desc": "", "out_fcda_addr": "", "error_type": "9", } //检查未通过 scdNodeRule.AppendFcdaCheckResult(r) } } } } //变压器间隔装置关系检查 func (c *CheckAreaMgr) cT(modelid string, scdXmlObj *node_attr.SCL, scdNodeMgr *ScdNode, scdNodeRule *ScdNodeRule, area_name string, ied_refs []orm.Params, area_ieds []orm.Params, area_ruleid string, HasAreaJ bool) { masterIed := new(node_attr.NIED) findIedName := "" modelidInt, _ := strconv.Atoi(modelid) ptIed := c.getIedTypeCode(modelidInt, "PT") for _, row2 := range area_ieds { findIedName = tools.IsEmpty(row2["ied_name"]) if strings.HasPrefix(findIedName, ptIed) { masterIed = scdNodeMgr.GetIed(scdXmlObj, "", findIedName) break } } if masterIed == nil { return } scdParseMgr := new(ScdParse) dealFromIed := map[string]int{} for _, row := range ied_refs { fromiedtype := c.getIedTypeCode(modelidInt, tools.IsEmpty(row["from_ied_code"])) fromiedtype = strings.Split(fromiedtype, "#")[0] //去除后面的电压级别标识 toiedtype := strings.Split(c.getIedTypeCode(modelidInt, tools.IsEmpty(row["to_ied_code"])), "#")[0] //去除后面的电压级别标识 reftype := tools.IsEmpty(row["in_type"]) tmpFromAreaIeds := []orm.Params{} tmpToAreaIeds := []orm.Params{} for _, row2 := range area_ieds { findIedName = tools.IsEmpty(row2["ied_name"]) if strings.HasPrefix(findIedName, fromiedtype) { masterIed = scdNodeMgr.GetIed(scdXmlObj, "", findIedName) if masterIed != nil { tmpFromAreaIeds = append(tmpFromAreaIeds, row2) } else { if dealFromIed[findIedName] == 0 { parse_result := fmt.Sprintf("间隔%s的装置%s缺失", area_name, findIedName) r := map[string]interface{}{"scdid": c.ScdId, "lineno": 0, "ruleid": area_ruleid, "nodeid": 0, "parse_result": parse_result, "ied_name": findIedName, "ied_desc": "", "out_ied_name": "", "out_ied_desc": "", "fcda_desc": "", "fcda_addr": "", "out_fcda_desc": "", "out_fcda_addr": "", "error_type": "9", } //检查未通过 scdNodeRule.AppendFcdaCheckResult(r) } dealFromIed[findIedName] = 1 } } if strings.HasPrefix(findIedName, toiedtype) { tmpToAreaIeds = append(tmpToAreaIeds, row2) } } if len(tmpFromAreaIeds) == 0 { continue } if len(tmpToAreaIeds) == 0 { logger.Logger.Debug(fmt.Sprintf("缺失类型关联装置 :%+v", row)) parse_result := fmt.Sprintf("间隔%s缺失类型为%s的%s信号装置", area_name, toiedtype, reftype) r := map[string]interface{}{"scdid": c.ScdId, "lineno": 0, "ruleid": area_ruleid, "nodeid": 0, "parse_result": parse_result, "ied_name": findIedName, "ied_desc": "", "out_ied_name": "", "out_ied_desc": "", "fcda_desc": "", "fcda_addr": "", "out_fcda_desc": "", "out_fcda_addr": "", "error_type": "9", } //检查未通过 scdNodeRule.AppendFcdaCheckResult(r) continue } toIedname := "" for _, row2 := range tmpFromAreaIeds { findIedName = tools.IsEmpty(row2["ied_name"]) hasToIedRef := false hasToIed := false t1 := fromiedtype + "->" + toiedtype volLevel := "" //电压等级 if t1 == "CT->IT" || t1 == "IT->CT" || t1 == "CT->IB" || t1 == "IB->CT" || t1 == "MM->MT" || t1 == "PM->IB" || t1 == "IB->PM" || t1 == "PM->IT" || t1 == "IT->PM" { ps := scdParseMgr.ParseIedName(findIedName) volLevel = ps[3] + ps[4] } hasSameVolIed := false for _, row3 := range tmpToAreaIeds { toIedname = tools.IsEmpty(row3["ied_name"]) if volLevel != "" { ps := scdParseMgr.ParseIedName(toIedname) if volLevel != ps[3]+ps[4] { //排除不是同一电压等级的装置 continue } hasSameVolIed = true } if scdNodeMgr.GetIed(scdXmlObj, "", toIedname) != nil { hasToIed = true // 获取该ied的输出(ref_type为0)装置,并从中检测是否存在toiedtype类型的装置 inout, _ := scdNodeMgr.GetIedRelations(map[string]interface{}{"scd_id": c.ScdId, "ied_name": findIedName}) //logger.Logger.Debug(fmt.Sprintf("ied:%s refs:%+v", findIedName, inout)) if inout != nil { outiedlist := inout[findIedName].(orm.Params)["list"].([]orm.Params) for _, ieditem := range outiedlist { outiedname := ieditem["ref_ied_name"].(string) if outiedname == toIedname && ieditem["ref_type"].(string) == "0" { hasToIedRef = true break } } } if !hasToIedRef { parse_result := fmt.Sprintf("间隔%s的装置%s缺失与装置%s的%s信号关联", area_name, findIedName, toIedname, reftype) r := map[string]interface{}{"scdid": c.ScdId, "lineno": 0, "ruleid": area_ruleid, "nodeid": 0, "parse_result": parse_result, "ied_name": findIedName, "ied_desc": "", "out_ied_name": toIedname, "out_ied_desc": "", "fcda_desc": "", "fcda_addr": "", "out_fcda_desc": "", "out_fcda_addr": "", "error_type": "9", } //检查未通过 scdNodeRule.AppendFcdaCheckResult(r) } } if hasSameVolIed { break } } if toiedtype != ptIed { if volLevel != "" && !hasSameVolIed { parse_result := fmt.Sprintf("间隔%s的装置%s缺失同电压等级的关联类型%s装置", area_name, findIedName, toiedtype) r := map[string]interface{}{"scdid": c.ScdId, "lineno": 0, "ruleid": area_ruleid, "nodeid": 0, "parse_result": parse_result, "ied_name": findIedName, "ied_desc": "", "out_ied_name": "", "out_ied_desc": "", "fcda_desc": "", "fcda_addr": "", "out_fcda_desc": "", "out_fcda_addr": "", "error_type": "9", } //检查未通过 scdNodeRule.AppendFcdaCheckResult(r) } else if !hasToIed { parse_result := fmt.Sprintf("间隔%s的装置%s缺失关联装置%s", area_name, findIedName, toIedname) r := map[string]interface{}{"scdid": c.ScdId, "lineno": 0, "ruleid": area_ruleid, "nodeid": 0, "parse_result": parse_result, "ied_name": findIedName, "ied_desc": "", "out_ied_name": toIedname, "out_ied_desc": "", "fcda_desc": "", "fcda_addr": "", "out_fcda_desc": "", "out_fcda_addr": "", "error_type": "9", } //检查未通过 scdNodeRule.AppendFcdaCheckResult(r) } } } } } //根据参考ied name找出应该关联PM装置 //abcode:套别标识代码。可选。不指定时将自动从iedtype识别 func (c *CheckAreaMgr) getPMName(iednameParts []string, ieds map[string]orm.Params, abcode ...string) string { //如果只有一个PM装置,则直接返回该 装置 pmLst := []string{} for n, _ := range ieds { if strings.HasPrefix(n, "PM"+iednameParts[3]+iednameParts[4]) { pmLst = append(pmLst, n) } } 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 } } return "" } //根据参考ied name找出应该关联MM装置 func (c *CheckAreaMgr) getMMName(iednameParts []string, ieds map[string]orm.Params, ab string) string { mmLst := []string{} for n, _ := range ieds { if strings.HasPrefix(n, "MM"+iednameParts[3]+iednameParts[4]) { mmLst = append(mmLst, n) } } if len(mmLst) == 1 { return mmLst[0] } for _, n := range mmLst { if ab != "" && strings.HasSuffix(n, ab) { return n } if ab == "" && strings.HasSuffix(n, iednameParts[5]+iednameParts[6]) { return n } } //理论上的装置名称 return "MM" + iednameParts[3] + iednameParts[4] + iednameParts[5] + iednameParts[6] + ab } //获取装置编码中的套别代码(A、B..) func (c *CheckAreaMgr) getIedTypeABCode(iedtype string) string { if strings.Index(iedtype, "-") > 0 { return strings.Split(iedtype, "-")[1] } return "" } //根据当前设备列表,分析出电压等级(高、中、低)的设备列表 // CT测控、IT智能终端、MT合并单元需要判断是否是本体装置,是则将其归为主变高压侧 //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 { pl_iedname := tools.IsEmpty(row["ied_name"]) if pl_iedname[0:len(it[0])] != it[0] { 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} } else { tmpLst[volvalue] = append(tmpLst[volvalue], row) } } if len(it) > 1 { if it[1] == "H" { return tmpLst[vollevel["hight"]], []orm.Params{}, []orm.Params{} } if it[1] == "M" { return []orm.Params{}, tmpLst[vollevel["middle"]], []orm.Params{} } if it[1] == "L" { return []orm.Params{}, []orm.Params{}, tmpLst[vollevel["low"]] } } return tmpLst[vollevel["hight"]], tmpLst[vollevel["middle"]], tmpLst[vollevel["low"]] } func (c *CheckAreaMgr) GetCheckResult(scdid int64) ([]orm.Params, error) { db := orm.NewOrm() rowset := []orm.Params{} sql := "select *,case error_type when 1 then '错误' when 2 then '缺失' when 3 then '多余' else '其他' end error_type_desc from t_scd_fcda_check_result where scd_id=? order by ied_name,out_ied_name,error_type" _, err := db.Raw(sql, scdid).Values(&rowset) return rowset, err }