package bo import ( "errors" "fmt" "regexp" "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 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 } } //先清除ied与间隔关联关系 clearSql := "delete from t_data_check_area_ied where area_id in(select id from t_data_check_area where scd_id=?)" _, err := db.Raw(clearSql, c.ScdId).Exec() if err != nil { logger.Logger.Error(err, fmt.Sprintf("SQL:%s 参数:%+v", clearSql, []interface{}{c.ScdId})) } clearSql = "delete from t_data_check_area where scd_id=?" _, err = db.Raw(clearSql, c.ScdId).Exec() if err != nil { logger.Logger.Error(err, fmt.Sprintf("SQL:%s 参数:%+v", clearSql, []interface{}{c.ScdId})) } //获取当前scd信息中获取到对应的电压等级id /* scdMgr := new(ScdMgr) scdinfo, _ := scdMgr.One(fmt.Sprintf("%d", c.ScdId)) stationid := tools.IsEmpty(scdinfo["station_id"]) volid := 0 if stationid != "" { basearea := new(BasicArea) stationonof, _ := basearea.One(stationid) volid = stationonof.AreaLevel } */ 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) 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) if err != nil { logger.Logger.Error(err) } 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 } //获取指定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_scd_show, enum.LogType_Query, enum.OptEventType_Bus, enum.OptEventLevel_Low, sqllog, c.GetUserInfo()) } else { new(SystemLog).Success(enum.AuditType_scd_show, 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,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_scd_show, enum.LogType_Query, enum.OptEventType_Bus, enum.OptEventLevel_Low, sqllog, c.GetUserInfo()) } else { new(SystemLog).Success(enum.AuditType_scd_show, 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_scd_show, 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_scd_show, 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, ",") _, 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 { _, err = db.Raw("insert into t_data_check_area_ied(scd_id,area_id,ied_name)values(?,?,?)", scdid, areaid, row).Exec() if err != nil { logger.Logger.Error(err) break } } 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_scd_show, 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() 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 } 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(scdXmlObj, scdNode, scdNodeRule, area_name, model_refs[modelid], area_ieds[area_id], "PE", area_ruleid) c.cJ(scdXmlObj, scdNode, scdNodeRule, area_name, model_refs[modelid], area_ieds[area_id], "PJ", area_ruleid) c.cJ(scdXmlObj, scdNode, scdNodeRule, area_name, model_refs[modelid], area_ieds[area_id], "PK", area_ruleid) c.cJ(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(scdXmlObj, scdNode, scdNodeRule, area_name, model_refs[modelid], area_ieds[area_id], area_ruleid) } if areaCode == "T" { //变压器齐间隔 c.cT(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") } 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) scdNodeMgr := new(ScdNode) //当前装置的所有信号接收端子列表 var getIedExtRefs = func(iedname string) map[string]*node_attr.NExtRef { iedObj := scdNodeMgr.GetIed(scdXmlObj, tools.IsEmpty(c.ScdId), iedname) if iedObj == nil { 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 } //当前装置的所有信号发送端子列表 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) []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) { iedlst = append(iedlst, r) } } } else { tmpLst := map[string]orm.Params{} for _, r := range s1 { iedname := tools.IsEmpty(r["ied_name"]) if strings.HasPrefix(iedname, ied_type) { tmpLst[iedname] = r } } h, m, l := c.getIedListByVol(ied_type, tmpLst, volMap) if volLevelCode == "H" { iedlst = h } if volLevelCode == "M" { iedlst = m } if volLevelCode == "L" { iedlst = l } } return iedlst } //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"]) //获取该间隔模型配置的所有端子关系列表 tmpMgr := new(SysCheckModelFcdaRalationMgr) tmpMgr.Model.ModelId = modelid funclist, _ := tmpMgr.GetModelAllFcdaRef() if funclist == nil { continue } if len(funclist) == 0 { logger.Logger.Error(errors.New(fmt.Sprintf("模型%d还未配置装置关系", modelid))) continue } //获取该间隔下该类型的装置 s1 := []orm.Params{} _, err = db.Raw("select ied_name from t_data_check_area_ied where area_id=?", area_id).Values(&s1) //循环处理关联关系 for ied_type, fromrowlist := range funclist { //找出信号输出装置 outiedlist := map[string][]orm.Params{} for _, r := range fromrowlist { fromiedcode := tools.IsEmpty(r["from_ied_code"]) ied_type, vol := getIedTypeAndVolCode(fromiedcode) outiedlist2 := filterAreaIeds(ied_type, vol, s1) for _, iedrow := range outiedlist2 { iedname := tools.IsEmpty(iedrow["ied_name"]) if outiedlist[iedname] == nil { outiedlist[iedname] = []orm.Params{} } outiedlist[iedname] = append(outiedlist[iedname], r) } } if len(outiedlist) == 0 { continue } ied_type, vol := getIedTypeAndVolCode(ied_type) iedlst := filterAreaIeds(ied_type, vol, s1) for _, ied := range iedlst { iedname := tools.IsEmpty(ied["ied_name"]) extreflist := getIedExtRefs(iedname) for outiedname, refrow := range outiedlist { iedObj := scdNodeMgr.GetIed(scdXmlObj, fmt.Sprintf("%d", c.ScdId), iedname) outIedObj := scdNodeMgr.GetIed(scdXmlObj, fmt.Sprintf("%d", c.ScdId), outiedname) 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"]) fcda_name_exp := tools.IsEmpty(r["from_fcda_match_exp"]) funcExist := false //设计的功能是否存在 extrefObj := new(node_attr.NExtRef) for desc, item := range extreflist { rex, err := regexp.Compile(extref_name_exp) if err != nil { logger.Logger.Error(err, "无效的正则表达式:"+extref_name_exp) return err } if rex.MatchString(desc) { funcExist = true extrefObj = item break } } if !funcExist { iedObj := scdNodeMgr.GetIed(scdXmlObj, fmt.Sprintf("%d", c.ScdId), iedname) outIedObj := scdNodeMgr.GetIed(scdXmlObj, fmt.Sprintf("%d", c.ScdId), outiedname) parse_result := "" 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": iedObj.Desc, "out_ied_name": outiedname, "out_ied_desc": outIedObj.Desc, "fcda_desc": extref_name, "fcda_addr": "", "out_fcda_desc": fcda_name, "out_fcda_addr": "", "error_type": "3", } //检查未通过 scdNodeRule.AppendFcdaCheckResult(re) } else { //检查端子是否关联正确 fcda2Exist := false fcdaObj := new(node_attr.NFCDA) for desc, item := range outiedFcdaList { rex, err := regexp.Compile(fcda_name_exp) if err != nil { logger.Logger.Error(err, "无效的正则表达式:"+fcda_name_exp) return err } if rex.MatchString(desc) { fcda2Exist = true fcdaObj = item break } } if fcda2Exist && (extrefObj.LdInst != fcdaObj.LdInst || extrefObj.LnInst != fcdaObj.LnInst || extrefObj.LnClass != fcdaObj.LnClass || extrefObj.Prefix != fcdaObj.Prefix || extrefObj.DoName != fcdaObj.DoName) { //不正确 iedObj := scdNodeMgr.GetIed(scdXmlObj, fmt.Sprintf("%d", c.ScdId), iedname) outIedObj := scdNodeMgr.GetIed(scdXmlObj, fmt.Sprintf("%d", c.ScdId), outiedname) parse_result := fmt.Sprintf("间隔%s的装置%s端子%s与装置%s端子%s关联错误", area_name, iedname, extref_name, outiedname, fcda_name) daname := "" if fcdaObj.DaName != "" { daname = "." + fcdaObj.DaName } 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": extref_name, "fcda_addr": extrefObj.IntAddr, "out_fcda_desc": fcda_name, "out_fcda_addr": fmt.Sprintf("%s/%s%s%s.%s%s", fcdaObj.LdInst, fcdaObj.Prefix, fcdaObj.LnClass, fcdaObj.LnInst, fcdaObj.DoName, daname), "error_type": "2", } //检查未通过 scdNodeRule.AppendFcdaCheckResult(re) } } } //检查是否有多余(SCD中有不存在于设计中)的端子 for extrefdesc, r := range extreflist { //scd中的端子关系 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"]) rex, err := regexp.Compile(extref_name_exp) if err != nil { logger.Logger.Error(err) return err } if rex.MatchString(extrefdesc) { //端子存在 isHave = true break } } if !isHave { parse_result := fmt.Sprintf("间隔%s的装置%s端子%s在设计中不存在", area_name, iedname, extref_name) 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(outiedname, r.LdInst, r.LnClass, r.LnInst, r.Prefix, r.DoName, "") if doi != nil { fcda_name = doi.(*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": 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) } } 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) } } } } } } } } scdNodeRule.CheckFinish() scdNodeRule.Flush() return nil } //解析模型间隔。根据模型定义解析出间隔中的装置 func (c *CheckAreaMgr) ParseModelArea() { 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"]) volcode := strings.ReplaceAll(tools.IsEmpty(row["vol_code"]), "v_level_", "") c.pJ(modelid, volcode, iedtypes, iedMap, "PE") c.pJ(modelid, volcode, iedtypes, iedMap, "PJ") c.pJ(modelid, volcode, iedtypes, iedMap, "PK") c.pJ(modelid, volcode, iedtypes, iedMap, "PF") } } 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 } //获取模型内中装备关系定义 //主变间隔分析:需要查站内该电压等级下的高中低压侧装置或者高低压装置组成一个间隔,以主变保护装置(PT)为起始 if areaCode == "T" { c.pT(modelid, iedtypes, iedMap, HasAreaJ) } //线路保护间隔分析:以线路保护测控装置(PL)为开始分析 if areaCode == "L" { c.pL(modelid, volcode, iedtypes, iedMap) } } } //变压器间隔分析 func (c *CheckAreaMgr) pT(modelid int, iedtypes string, ieds map[string]orm.Params, HasAreaJ bool) { scdParseMgr := new(ScdParse) 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) //低压电压等级 } var getIedByDesc = func(iedinfo map[string]orm.Params, desc string) (string, string) { scdParseMgr := new(ScdParse) scdXmlObj, _ := scdParseMgr.GetScdXmlObjectBySCDID(tools.IsEmpty(c.ScdId)) for vn, row := range iedinfo { v := tools.IsEmpty(row["ied_desc"]) if v == "" { if scdXmlObj == nil { return "", "" } scdNode := new(ScdNode) ied := scdNode.GetIed(scdXmlObj, "", vn) if ied == nil { return "", "" } v = ied.Desc } if strings.Contains(v, desc) { return v, vn } } return "", "" } //判断是否将各电压等级的保护装置合并还是分开的间隔 //如果存在高中低压的保护装置,则说明是分开的主变间隔 //isMarge := true // 默认为合并的主变间隔 for _, row := range ieds { if tools.IsEmpty(row["ied_type"]) != "P" || tools.IsEmpty(row["p_type"]) != "T" { continue } //判断主变保护是否是按电压等级分开配置的模型 /* if strings.Contains(iedtypes, "PT#H") || strings.Contains(iedtypes, "PT#M") || strings.Contains(iedtypes, "PT#L") { h, m, l := c.getIedListByVol("PT", ieds, volMap) } else { pl_iedname := tools.IsEmpty(row["ied_name"]) } */ //pmIedName := "" //mmIedName := "" pl_iedname := tools.IsEmpty(row["ied_name"]) iednameParts := scdParseMgr.ParseIedName(pl_iedname) //添加间隔数据 dbdata := T_data_check_area{ ModelId: modelid, ScdId: c.ScdId, AreaType: "T", AreaName: tools.IsEmpty(row["area_name"]) + iednameParts[7], } 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 := pl_iedname insvalues = append(insvalues, fmt.Sprintf("(%d,%d,'%s','%s','%s')", c.ScdId, newid, inAreaIedName, "P", "T")) //判断间隔是否需要包含差动装置 if strings.Contains(iedtypes, "PT#C") { ieddesc, iedname := getIedByDesc(ieds, "差动") if strings.Contains(ieddesc, "差动") { insvalues = append(insvalues, fmt.Sprintf("(%d,%d,'%s','%s','%s')", c.ScdId, newid, iedname, "P", "TC")) } } //判断间隔是否需要包含本体保护装置 if strings.Contains(iedtypes, "PT#0") { ieddesc, iedname := getIedByDesc(ieds, "本体") if strings.Contains(ieddesc, "本体") { insvalues = append(insvalues, fmt.Sprintf("(%d,%d,'%s','%s','%s')", c.ScdId, newid, iedname, "P", "T0")) } } //合智一体IMT/MIT,分高中低压 h, m, l := c.getIedListByVol("IMT", ieds, volMap) h = append(h, m...) h = append(h, l...) for _, r := range h { inAreaIedName = tools.IsEmpty(r["ied_name"]) tmpIednameParts := scdParseMgr.ParseIedName(inAreaIedName) lastChar := tmpIednameParts[7] if lastChar == iednameParts[7] { insvalues = append(insvalues, fmt.Sprintf("(%d,%d,'%s','%s','%s')", c.ScdId, newid, inAreaIedName, "IM", "T")) } } h, m, l = c.getIedListByVol("MIT", ieds, volMap) h = append(h, m...) h = append(h, l...) for _, r := range h { inAreaIedName = tools.IsEmpty(r["ied_name"]) tmpIednameParts := scdParseMgr.ParseIedName(inAreaIedName) lastChar := tmpIednameParts[7] if lastChar == iednameParts[7] { insvalues = append(insvalues, fmt.Sprintf("(%d,%d,'%s','%s','%s')", c.ScdId, newid, inAreaIedName, "MI", "T")) } } //母联智能终端IE需要分高中压,无低压侧 if HasAreaJ { h, m, l = c.getIedListByVol("IE", ieds, volMap) for _, r := range h { //高压侧,AB套必须与PT装置相同 inAreaIedName = tools.IsEmpty(r["ied_name"]) tmpIednameParts := scdParseMgr.ParseIedName(inAreaIedName) lastChar := tmpIednameParts[7] if lastChar == iednameParts[7] { insvalues = append(insvalues, fmt.Sprintf("(%d,%d,'%s','%s','%s')", c.ScdId, newid, inAreaIedName, "I", "E")) } } for _, r := range m { //中压侧 inAreaIedName = tools.IsEmpty(r["ied_name"]) tmpIednameParts := scdParseMgr.ParseIedName(inAreaIedName) lastChar := tmpIednameParts[7] if lastChar == "" || lastChar == iednameParts[7] { insvalues = append(insvalues, fmt.Sprintf("(%d,%d,'%s','%s','%s')", c.ScdId, newid, inAreaIedName, "I", "E")) } } } //合并单元MT分高中低压侧;低压侧无关联母线合并单元MM h, m, l = c.getIedListByVol("MT", ieds, volMap) h = append(h, m...) h = append(h, l...) for _, r := range h { inAreaIedName = tools.IsEmpty(r["ied_name"]) tmpIednameParts := scdParseMgr.ParseIedName(inAreaIedName) lastChar := tmpIednameParts[7] if lastChar == iednameParts[7] { insvalues = append(insvalues, fmt.Sprintf("(%d,%d,'%s','%s','%s')", c.ScdId, newid, inAreaIedName, "M", "T")) } } //测控装置CT分高中低压侧,且可能是多套合并单元MT共用,既不分AB套 h, m, l = c.getIedListByVol("CT", ieds, volMap) h = append(h, m...) h = append(h, l...) for _, r := range h { inAreaIedName = tools.IsEmpty(r["ied_name"]) tmpIednameParts := scdParseMgr.ParseIedName(inAreaIedName) lastChar := tmpIednameParts[7] tmpVol := tmpIednameParts[3] + tmpIednameParts[4] //高中低压电压等级相同的 if (tmpVol == volMap["low"] || tmpVol == volMap["middle"] || tmpVol == volMap["hight"]) && (lastChar == "" || lastChar == iednameParts[7]) { insvalues = append(insvalues, fmt.Sprintf("(%d,%d,'%s','%s','%s')", c.ScdId, newid, inAreaIedName, "C", "T")) } } //智能终端(IB:开关\IT:分支\IF:分段)分高中低压侧 h, m, l = c.getIedListByVol("IB", ieds, volMap) h = append(h, m...) h = append(h, l...) for _, r := range h { inAreaIedName = tools.IsEmpty(r["ied_name"]) tmpIednameParts := scdParseMgr.ParseIedName(inAreaIedName) lastChar := tmpIednameParts[7] tmpVol := tmpIednameParts[3] + tmpIednameParts[4] //高中低压电压等级相同的 if (tmpVol == volMap["low"] || tmpVol == volMap["middle"] || tmpVol == volMap["hight"]) && (lastChar == "" || lastChar == iednameParts[7]) { insvalues = append(insvalues, fmt.Sprintf("(%d,%d,'%s','%s','%s')", c.ScdId, newid, inAreaIedName, "I", "B")) } } h, m, l = c.getIedListByVol("IT", ieds, volMap) h = append(h, m...) h = append(h, l...) for _, r := range h { inAreaIedName = tools.IsEmpty(r["ied_name"]) tmpIednameParts := scdParseMgr.ParseIedName(inAreaIedName) lastChar := tmpIednameParts[7] tmpVol := tmpIednameParts[3] + tmpIednameParts[4] //高中低压电压等级相同的 if (tmpVol == volMap["low"] || tmpVol == volMap["middle"] || tmpVol == volMap["hight"]) && (lastChar == "" || lastChar == iednameParts[7]) { insvalues = append(insvalues, fmt.Sprintf("(%d,%d,'%s','%s','%s')", c.ScdId, newid, inAreaIedName, "I", "T")) } } h, m, l = c.getIedListByVol("IF", ieds, volMap) h = append(h, m...) h = append(h, l...) for _, r := range h { inAreaIedName = tools.IsEmpty(r["ied_name"]) tmpIednameParts := scdParseMgr.ParseIedName(inAreaIedName) lastChar := tmpIednameParts[7] tmpVol := tmpIednameParts[3] + tmpIednameParts[4] //高中低压电压等级相同的 if (tmpVol == volMap["low"] || tmpVol == volMap["middle"] || tmpVol == volMap["hight"]) && (lastChar == "" || lastChar == iednameParts[7]) { insvalues = append(insvalues, fmt.Sprintf("(%d,%d,'%s','%s','%s')", c.ScdId, newid, inAreaIedName, "I", "F")) } } //母线保护PM及母线合并单元MM,可能是多套合并单元MT共用,既不分AB套;低压侧智能终端无关联母线保护 h, m, l = c.getIedListByVol("PM", ieds, volMap) for _, r := range h { //高压侧,AB套必须与PT装置相同 inAreaIedName = tools.IsEmpty(r["ied_name"]) tmpIednameParts := scdParseMgr.ParseIedName(inAreaIedName) lastChar := tmpIednameParts[7] if lastChar == iednameParts[7] { insvalues = append(insvalues, fmt.Sprintf("(%d,%d,'%s','%s','%s')", c.ScdId, newid, inAreaIedName, "P", "M")) //MM装置 mmIedName := c.getMMName(tmpIednameParts, ieds, iednameParts[7]) insvalues = append(insvalues, fmt.Sprintf("(%d,%d,'%s','%s','%s')", c.ScdId, newid, mmIedName, "M", "M")) break } } for _, r := range m { //中压侧 inAreaIedName = tools.IsEmpty(r["ied_name"]) tmpIednameParts := scdParseMgr.ParseIedName(inAreaIedName) lastChar := tmpIednameParts[7] if lastChar == "" || lastChar == iednameParts[7] { insvalues = append(insvalues, fmt.Sprintf("(%d,%d,'%s','%s','%s')", c.ScdId, newid, inAreaIedName, "P", "M")) //MM装置 mmIedName := c.getMMName(tmpIednameParts, ieds, iednameParts[7]) insvalues = append(insvalues, fmt.Sprintf("(%d,%d,'%s','%s','%s')", c.ScdId, newid, mmIedName, "M", "M")) break } } _, 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() //scdXmlObject, _ := scdParseMgr.GetScdXmlObjectBySCDID(tools.IsEmpty(c.ScdId)) for _, row := range ieds { if tools.IsEmpty(row["vol"]) != vol || tools.IsEmpty(row["ied_type"]) != "P" || tools.IsEmpty(row["p_type"]) != "L" { continue } pmIedName := "" mmIedName := "" pl_iedname := tools.IsEmpty(row["ied_name"]) iednameParts := scdParseMgr.ParseIedName(pl_iedname) //添加间隔数据 dbdata := T_data_check_area{ ModelId: modelid, ScdId: c.ScdId, AreaType: "L", AreaName: tools.IsEmpty(row["area_name"]) + iednameParts[7], } 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{} for _, ty := range strings.Split(iedtypes, ",") { inAreaIedName := "" if ty == "PM" { //母线保护和母线合并单元装置编号跟随变压器 //查找变压器编号 //1号变压器->2号变压器->3号 inAreaIedName = c.getPMName(iednameParts, ieds) pmIedName = inAreaIedName if mmIedName == "" { insvalues = append(insvalues, fmt.Sprintf("(%d,%d,'%s','%s','%s')", c.ScdId, newid, inAreaIedName, ty[0:1], ty[1:2])) pmIedNameParts := scdParseMgr.ParseIedName(pmIedName) //使用PM装置的名称去定义MM的名称 ty = "MM" inAreaIedName = c.getMMName(pmIedNameParts, ieds, iednameParts[7]) mmIedName = inAreaIedName } } else if ty == "MM" { if pmIedName != "" && mmIedName == "" { pmIedNameParts := scdParseMgr.ParseIedName(pmIedName) //使用PM装置的名称去定义MM的名称 inAreaIedName = c.getMMName(pmIedNameParts, ieds, iednameParts[7]) mmIedName = inAreaIedName } else { continue } } else { inAreaIedName = ty + iednameParts[3] + iednameParts[4] + iednameParts[5] + iednameParts[6] + iednameParts[7] } if strings.Index("ABCDE", iednameParts[7]) > -1 { //最后一位是字母则说明是AB套 switch ty { case "CL": //测控装置,先判断是否分了AB套,没有标识(ied名称的最后一位是否是字母)则说明是多套共用装置 clIedname := inAreaIedName + iednameParts[7] iedObj := ieds[inAreaIedName] if iedObj != nil { //当前测控装置也分了AB套 inAreaIedName = clIedname } break default: break } } insvalues = append(insvalues, fmt.Sprintf("(%d,%d,'%s','%s','%s')", c.ScdId, newid, inAreaIedName, ty[0:1], ty[1:2])) } _, 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(pmIedNameParts, 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() //scdXmlObject, _ := scdParseMgr.GetScdXmlObjectBySCDID(tools.IsEmpty(c.ScdId)) for _, row := range ieds { if tools.IsEmpty(row["vol"]) != vol || tools.IsEmpty(row["ied_type"]) != "P" || tools.IsEmpty(row["p_type"]) != pjIed[1:2] { continue } pmIedName := "" mmIedName := "" pl_iedname := tools.IsEmpty(row["ied_name"]) iednameParts := scdParseMgr.ParseIedName(pl_iedname) //添加间隔数据 dbdata := T_data_check_area{ ModelId: modelid, ScdId: c.ScdId, AreaType: "J", AreaName: tools.IsEmpty(row["area_name"]) + iednameParts[7], } 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{} for _, ty := range strings.Split(iedtypes, ",") { inAreaIedName := "" if ty == "PM" { //母线保护和母线合并单元装置编号跟随变压器 //查找变压器编号 //1号变压器->2号变压器->3号 inAreaIedName = c.getPMName(iednameParts, ieds) pmIedName = inAreaIedName if mmIedName == "" { insvalues = append(insvalues, fmt.Sprintf("(%d,%d,'%s','%s','%s')", c.ScdId, newid, inAreaIedName, ty[0:1], ty[1:2])) pmIedNameParts := scdParseMgr.ParseIedName(pmIedName) //使用PM装置的名称去定义MM的名称 ty = "MM" inAreaIedName = c.getMMName(pmIedNameParts, ieds, iednameParts[7]) mmIedName = inAreaIedName } } else if ty == "MM" { if pmIedName != "" && mmIedName == "" { pmIedNameParts := scdParseMgr.ParseIedName(pmIedName) //使用PM装置的名称去定义MM的名称 inAreaIedName = c.getMMName(pmIedNameParts, ieds, iednameParts[7]) mmIedName = inAreaIedName } else { continue } } else { ty = ty[0:1] + pjIed[1:2] inAreaIedName = ty + iednameParts[3] + iednameParts[4] + iednameParts[5] + iednameParts[6] + iednameParts[7] //判断与基准保护装置相同套号的装置是否存在 if ieds[inAreaIedName] == nil { //尝试去除套号 inAreaIedName = ty + iednameParts[3] + iednameParts[4] + iednameParts[5] + iednameParts[6] } } insvalues = append(insvalues, fmt.Sprintf("(%d,%d,'%s','%s','%s')", c.ScdId, newid, inAreaIedName, ty[0:1], ty[1:2])) } _, err = db.Raw(ins1 + strings.Join(insvalues, ",")).Exec() if err != nil { logger.Logger.Error(err) return } } } //母联间隔装置关系检查 func (c *CheckAreaMgr) cJ(scdXmlObj *node_attr.SCL, scdNodeMgr *ScdNode, scdNodeRule *ScdNodeRule, area_name string, ied_refs []orm.Params, area_ieds []orm.Params, pjIed, area_ruleid string) { 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 := tools.IsEmpty(row["from_ied_code"]) toiedtype := 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(scdXmlObj *node_attr.SCL, scdNodeMgr *ScdNode, scdNodeRule *ScdNodeRule, area_name string, ied_refs []orm.Params, area_ieds []orm.Params, area_ruleid string) { masterIed := new(node_attr.NIED) findIedName := "" for _, row2 := range area_ieds { findIedName = tools.IsEmpty(row2["ied_name"]) if strings.HasPrefix(findIedName, "PL") { masterIed = scdNodeMgr.GetIed(scdXmlObj, "", findIedName) break } } if masterIed == nil { return } dealFromIed := map[string]int{} for _, row := range ied_refs { fromiedtype := tools.IsEmpty(row["from_ied_code"]) toiedtype := 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) cT(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 := "" for _, row2 := range area_ieds { findIedName = tools.IsEmpty(row2["ied_name"]) if strings.HasPrefix(findIedName, "PT") { masterIed = scdNodeMgr.GetIed(scdXmlObj, "", findIedName) break } } if masterIed == nil { return } scdParseMgr := new(ScdParse) dealFromIed := map[string]int{} for _, row := range ied_refs { fromiedtype := tools.IsEmpty(row["from_ied_code"]) fromiedtype = strings.Split(fromiedtype, "#")[0] //去除后面的电压级别标识 toiedtype := strings.Split(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} //检查未通过 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} //检查未通过 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} //检查未通过 scdNodeRule.AppendFcdaCheckResult(r) } } if hasSameVolIed { break } } if toiedtype != "PT" { 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} //检查未通过 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} //检查未通过 scdNodeRule.AppendFcdaCheckResult(r) } } } } } //根据参考ied name找出应该关联PM装置 func (c *CheckAreaMgr) getPMName(iednameParts []string, ieds map[string]orm.Params) string { tmpIedName := "PM" + iednameParts[3] + iednameParts[4] + iednameParts[5] + "1" + iednameParts[7] iedObj := ieds[tmpIedName] if iedObj != nil { } else { tmpIedName = "PM" + iednameParts[3] + iednameParts[4] + iednameParts[5] + "2" + iednameParts[7] iedObj = ieds[tmpIedName] if iedObj != nil { return tmpIedName } } return tmpIedName } //根据参考ied name找出应该关联MM装置 func (c *CheckAreaMgr) getMMName(iednameParts []string, ieds map[string]orm.Params, ab string) string { tmpIedName := "MM" + iednameParts[3] + iednameParts[4] + iednameParts[5] + iednameParts[6] + ab if ieds[tmpIedName] == nil { tmpIedName = "MM" + iednameParts[3] + iednameParts[4] + iednameParts[5] + iednameParts[6] + iednameParts[7] } return tmpIedName } //根据当前设备列表,分析出电压等级(高、中、低)的设备列表 // CT测控、IT智能终端、MT合并单元需要判断是否是本体装置,是则将其归为主变高压侧 func (c *CheckAreaMgr) getIedListByVol(iedtype string, ieds map[string]orm.Params, vollevel map[string]string) (hightLst, middleLst, lowLst []orm.Params) { tmpLst := map[string][]orm.Params{} for _, v := range vollevel { tmpLst[v] = []orm.Params{} } scdParseMgr := new(ScdParse) for _, row := range ieds { pl_iedname := tools.IsEmpty(row["ied_name"]) if pl_iedname[0:len(iedtype)] != iedtype { continue } iednameParts := scdParseMgr.ParseIedName(pl_iedname) volvalue := iednameParts[3] + iednameParts[4] if tmpLst[volvalue] == nil { tmpLst[volvalue] = []orm.Params{row} } else { tmpLst[volvalue] = append(tmpLst[volvalue], row) } } 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" _, err := db.Raw(sql, scdid).Values(&rowset) return rowset, err }