package bo import ( "errors" "fmt" "log" "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" ) //SCD间隔管理 type ScdAreaMgr struct { DeviceBaseModel //SCD文件ID ScdId int64 //电压等级定义 VoltageLevelDef map[string]int32 //设备类型定义 DeviceTypeDef map[string]int CacheAreaID map[string]int32 CacheAreaIDByIedNameNo map[string]int32 CacheLock sync.RWMutex } type T_substation_area struct { Id int64 `orm:"pk"` Name string NameNo string ScdId int64 VoltageLevel int32 DispatchNo string SubstationId int CreatedBy int CreatedTime string } type t_area_ied_relation struct { Id int32 `orm:"pk"` AreaId int32 IedId int64 IedName string IedType string IedDesc string PType string //归属设备类型 Vol string //电压编码 PCode string //归属设备编码 IedNo string //IED编号(A\B\C) ScdId int64 } // 其他非标准装置列表。通过系统参数OtherIedNameList维护 var otherIedNameList map[string]bool func init() { orm.RegisterModel(new(T_substation_area)) orm.RegisterModel(new(t_area_ied_relation)) } func (c *ScdAreaMgr) Init(scdid int64) { c.VoltageLevelDef = map[string]int32{} c.DeviceTypeDef = map[string]int{} c.CacheAreaIDByIedNameNo = map[string]int32{} c.CacheAreaID = map[string]int32{} c.CacheLock = sync.RWMutex{} 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_", "")] = int32(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 } } } //保存指定间隔所属的电压等级 func (c *ScdAreaMgr) SetVoltageLevel(id string, voltagelevel int) error { db := orm.NewOrm() _, err := db.Raw("update t_substation_area set voltage_level=? where id=?", voltagelevel, id).Exec() return err } //修改指定间隔的名称 func (c *ScdAreaMgr) UpdateName(scdid int64, area_id int, name string) error { db := orm.NewOrm() areaM := T_substation_area{Id: int64(area_id), ScdId: scdid} err := db.Read(&areaM) if err != nil { logger.Logger.Error(err) return err } areaM.Name = name _, err = db.Update(&areaM) if err != nil { logger.Logger.Error(err) } return err } //修改指定IED的所属间隔 func (c *ScdAreaMgr) UpdateIedArea(scdid int64, iedname string, area_id int) error { db := orm.NewOrm() _, err := db.Raw("update t_area_ied_relation set area_id=? where scd_id=? and ied_name=?", area_id, scdid, iedname).Exec() return err } //获取指定scd的间隔信息 func (c *ScdAreaMgr) GetAreaList(scdid int64) ([]orm.Params, error) { db := orm.NewOrm() sql := "select t.*,(select count(1) from t_area_ied_relation where area_id=t.id) iedcount from t_substation_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 *ScdAreaMgr) GetAreaListByVol(scdid int64, vl int32) ([]orm.Params, error) { db := orm.NewOrm() sql := "select * from t_substation_area where scd_id=? and voltage_level=? order by name" rowset := []orm.Params{} _, err := db.Raw(sql, scdid, vl).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 *ScdAreaMgr) GetIedList(scdid int64, voltage_level_id, areaid int32, device_type string) ([]orm.Params, error) { db := orm.NewOrm() sql := "select * from t_substation_area t,t_area_ied_relation t1 where t.scd_id=? and t.id=t1.area_id " sqlParamters := []interface{}{} sqlParamters = append(sqlParamters, scdid) if voltage_level_id > 0 { sql = sql + " and t.voltage_level=?" sqlParamters = append(sqlParamters, voltage_level_id) } 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{} if device_type != "" { //根据装备类型查询IED sql = sql + " and t1.ied_type=?" sqlParamters = append(sqlParamters, device_type) } _, 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 _, row := range rowset { iedid, _ := strconv.ParseInt(tools.IsEmpty(row["ied_id"]), 10, 64) iedObj := scdNode.GetIedByID(scdXmlObj, tools.IsEmpty(scdid), iedid) if iedObj == nil { continue } row["attr_name"] = iedObj.Name row["attr_desc"] = iedObj.Desc row["attr_config_version"] = iedObj.ConfigVersion row["attr_type"] = iedObj.Type row["attr_manufacturer"] = iedObj.Manufacturer row["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 } //获取指定SCD的IED类型列表 func (c *ScdAreaMgr) GetIedTypeList(scdid int64) ([]orm.Params, error) { db := orm.NewOrm() sql := "select t2.* from t_area_ied_relation 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 *ScdAreaMgr) One(id string) (interface{}, error) { db := orm.NewOrm() idInt, err := strconv.ParseInt(id, 10, 64) if err != nil { return nil, err } areaM := T_substation_area{Id: idInt} err = db.Read(&areaM) if err == nil { return areaM, nil } return nil, err } //重置scd的间隔信息 func (c *ScdAreaMgr) Reset(scdid int64) error { db := orm.NewOrm() db.Raw("delete from t_area_ied_relation where area_id in(select id from t_substation_area where scd_id=?)", scdid).Exec() db.Raw("delete from t_substation_area where scd_id=?", scdid).Exec() sql := "select * from t_scd_ied_attrs where scd_id=?" rowset := []orm.Params{} _, err := db.Raw(sql, scdid).Values(&rowset) if err != nil { log.Println(err) return err } for _, row := range rowset { ied := new(t_scd_node_scl) ied.Id, _ = strconv.ParseInt(tools.IsEmpty(row["node_id"]), 10, 64) iedNodeDesc := tools.IsEmpty(row["attr_desc"]) ied.NodeName = tools.IsEmpty(row["attr_name"]) ied.ScdId = scdid c.AppendIedNode(scdid, ied.Id, ied.NodeName, iedNodeDesc) } logdesc := fmt.Sprintf("重置SCD %d间隔成功", scdid) new(SystemLog).Success(enum.AuditType_scd_show, enum.LogType_Update, enum.OptEventType_Bus, enum.OptEventLevel_Low, logdesc, c.GetUserInfo()) return nil } //根据scd中的Bay节点解析间隔 func (c *ScdAreaMgr) ParseBay(substation_id int, lst []*node_attr.NVoltage) { if c.DeviceTypeDef == nil { c.Init(c.ScdId) } db := orm.NewOrm() voltages := []string{"其它", "其他"} for _, item := range lst { voltages = append(voltages, item.Name) } //电压等级节点属性表:t_scd_voltage_attrs。对比电压等级与定义的码表,如果缺失时自动新增 sql := `select t1.id,t1.name,t1.code from global_const_code t1 where t1.parentcode='voltage_level' and t1.name in('` + strings.Join(voltages, "','") + `') ` rowset := []orm.Params{} _, err := db.Raw(sql).Values(&rowset) if err != nil { logger.Logger.Error(err, fmt.Sprintf("SQL:%s ", sql)) } else { voltage_id_map := map[string]string{} for _, row := range rowset { voltage_name := tools.IsEmpty(row["code"]) voltage_id_map[strings.ReplaceAll(voltage_name, "v_level_", "")] = tools.IsEmpty(row["id"]) } clearSql := "delete from t_substation_area where substation_id=? and scd_id=?" _, err = db.Raw(clearSql, substation_id, c.ScdId).Exec() if err != nil { logger.Logger.Error(err, fmt.Sprintf("SQL:%s 参数:%+v", clearSql, []interface{}{substation_id, c.ScdId})) return } //先清除ied与间隔关联关系 clearSql = "delete from t_area_ied_relation where area_id in(select id from t_substation_area where substation_id=? and scd_id=?)" _, err = db.Raw(clearSql, substation_id, c.ScdId).Exec() if err != nil { logger.Logger.Error(err, fmt.Sprintf("SQL:%s 参数:%+v", clearSql, []interface{}{substation_id, c.ScdId})) return } bayCols := "insert into t_substation_area(substation_id,scd_id,voltage_level,name,name_no)values" bayValues := []string{} for _, volitem := range lst { voltage_name := volitem.Name voltage_id := voltage_id_map[voltage_name] if voltage_id == "" { voltage_id = voltage_id_map["其它"] } if voltage_id == "" { voltage_id = voltage_id_map["其他"] } for _, bayitem := range volitem.Bay { bayValues = append(bayValues, fmt.Sprintf(`(%d,%d,%s,'%s','')`, substation_id, c.ScdId, voltage_id, bayitem.Name)) } sql := bayCols + strings.Join(bayValues, ",") //添加间隔 _, err = db.Raw(sql).Exec() if err != nil { logger.Logger.Error(err, fmt.Sprintf("SQL:%s", sql)) } else { //创建ied与间隔关联关系 relCols := "insert into t_area_ied_relation(scd_id, area_id,ied_id,ied_name,ied_desc,ied_type,p_type,vol,p_code,ied_no)values" relValues := []string{} for _, bayitem := range volitem.Bay { relValues = []string{} areaRowset := []orm.Params{} _, err = db.Raw("select id from t_substation_area where substation_id=? and scd_id=? and voltage_level=?", substation_id, c.ScdId, voltage_id).Values(&areaRowset) if err != nil { logger.Logger.Error(err) } else { areaid := tools.IsEmpty(areaRowset[0]["id"]) for _, iedItem := range bayitem.IED { //解析类型 //补全实际ied name为8位 new_ied_name_full := new(ScdParse).ParseIedName(iedItem.Name) iedtype := "xy" //其他装置类型 ptype := "" vol := "" pcode := "" iedno := "" name := iedItem.Name if len(name) >= 3 { lastchar := new_ied_name_full[0] + new_ied_name_full[1] ptype = new_ied_name_full[2] vol = new_ied_name_full[3] + new_ied_name_full[4] pcode = new_ied_name_full[5] + new_ied_name_full[6] iedno = new_ied_name_full[7] if c.DeviceTypeDef[lastchar] == 0 { iedtype = "xy" } else { iedtype = lastchar } voltagelevelid := c.VoltageLevelDef[vol] if voltagelevelid == 0 { vol = "999" } } relValues = append(relValues, fmt.Sprintf("(%d,%s,%d,'%s','%s','%s','%s','%s','%s','%s')", c.ScdId, areaid, iedItem.NodeId, iedItem.Name, iedItem.Desc, iedtype, ptype, vol, pcode, iedno)) } if len(relValues) == 0 { continue } sql2 := relCols + strings.Join(relValues, ",") //添加间隔 _, err = db.Raw(sql2).Exec() if err != nil { logger.Logger.Error(err, fmt.Sprintf("SQL:%s", sql2)) } } } } } } } //添加新的需要解析的Ied装置 //name:ied的name属性值 //str:ied的desc属性值 func (c *ScdAreaMgr) AppendIedNode(scdId, iedId int64, name, str string) { //fmt.Println("ScdAreaMgr.AppendIedNode:" + name + " " + str) if str == "" { return } if c.DeviceTypeDef == nil { c.Init(c.ScdId) } go func(scdId, iedId int64, name, str string) { areaStartPos := 0 areaEndPos := 0 reg := regexp.MustCompile(`(?i)kv`) r := reg.FindStringIndex(str) if r != nil { areaStartPos = r[1] } voltagelevel := strings.ToLower(strings.Trim(str[0:areaStartPos], " ")) //优先匹配关键词 reg = regexp.MustCompile(`(测控|智能|保护|合并)`) r = reg.FindStringIndex(str) if r != nil { areaEndPos = r[0] } areaResult := "" if len(r) == 0 { //次级匹配关键词。在装置desc中未发现任意优先匹配关键词,再按以下关键尝试匹配 reg = regexp.MustCompile(`(分段|备|自投)`) r = reg.FindStringIndex(str) if r != nil { areaEndPos = r[0] } if areaEndPos == 0 { areaResult = "公用" } else { areaResult = strings.Trim(str[areaStartPos:areaEndPos], " ") } } else { areaResult = strings.Trim(str[areaStartPos:areaEndPos], " ") } if len(areaResult) < 2 { areaResult = str } areaResult = strings.Trim(strings.ReplaceAll(areaResult, "线路", "线"), " ") //logger.Logger.Debug(fmt.Sprintf("解析装置%s电压及间隔:电压%s 间隔:%s", str, voltagelevel, areaResult)) c.CacheLock.Lock() //补全实际ied name为8位 new_ied_name_full := new(ScdParse).ParseIedName(name) //解析类型 iedtype := "xy" //其他装置类型 ptype := "" vol := "" pcode := "" iedno := "" if len(name) >= 3 { lastchar := new_ied_name_full[0] + new_ied_name_full[1] if c.DeviceTypeDef[lastchar] == 0 { iedtype = "xy" } else { iedtype = lastchar } ptype = new_ied_name_full[2] vol = new_ied_name_full[3] + new_ied_name_full[4] pcode = new_ied_name_full[5] + new_ied_name_full[6] iedno = new_ied_name_full[7] voltagelevel = vol } //所属间隔判断规则 //1、判断解析得到的间隔名称是否已存在 //2、不存在时,根据装置name中的数字编号(如CM1101中的1101),查找同编号且已归属间隔的任意设置所属间隔 db := orm.NewOrm() voltagelevelid := c.VoltageLevelDef[voltagelevel] logger.Logger.Debug(fmt.Sprintf("%s %s 电压等级:%s ID:%d 间隔名称:%s ", name, str, voltagelevel, voltagelevelid, areaResult)) isNoOtherVL := 1 if voltagelevelid == 0 { isNoOtherVL = 0 voltagelevelid = c.VoltageLevelDef["其它"] vol = "999" } if otherIedNameList[name] { voltagelevelid = c.VoltageLevelDef["其它"] } areaid := c.CacheAreaID[fmt.Sprintf("%d%s", voltagelevelid, areaResult)] if areaid == 0 { //新增一个新的间隔 areaM := T_substation_area{} areaM.Name = areaResult areaM.ScdId = scdId areaM.NameNo = vol areaM.VoltageLevel = voltagelevelid areaM.CreatedTime = tools.NowTime() newid, err := db.Insert(&areaM) if err != nil { logger.Logger.Error(err) } c.CacheAreaID[fmt.Sprintf("%d%s", voltagelevelid, areaResult)] = int32(newid) areaid = int32(newid) } else { //如果当前电压等级不是“其它”,则将之前归属于其他电压的间隔更新到当前电压等级下 if isNoOtherVL == 1 { db.Raw("update t_substation_area set voltage_level=? where name_no=? and scd_id=?", voltagelevelid, vol, scdId).Exec() } } c.CacheLock.Unlock() rel := t_area_ied_relation{AreaId: areaid, IedId: iedId, IedName: name, IedDesc: str, IedType: iedtype, PType: ptype, Vol: vol, PCode: pcode, IedNo: iedno, ScdId: scdId} db.Insert(&rel) //logdesc := fmt.Sprintf("添加间隔,操作数据:%+v", rel) //new(SystemLog).Success(enum.AuditType_scd_show, enum.LogType_Insert, enum.OptEventType_Bus, enum.OptEventLevel_Low, logdesc, c.UserInfo) }(scdId, iedId, name, str) } //测试 func (c *ScdAreaMgr) TestAppendNode(iedname string) { //t := t_scd_node_scl{NodeName: iedname} //c.AppendIedNode(&t) }