package bo import ( "encoding/json" "errors" "fmt" "os" "runtime" "scd_check_tools/logger" "scd_check_tools/models/enum" "scd_check_tools/models/node_attr" "scd_check_tools/mqtt" "scd_check_tools/tools" "strconv" "strings" "sync" "time" "github.com/astaxie/beego/orm" ) type t_scd_diff_compare struct { Id int64 `orm:"pk;auto"` Name string //对比的文件类型。值为scd|icd等 FileType string SourceId int64 TargetId int64 StationId int NodeState int AttrState int SourceFile string TargetFile string } type t_scd_diff_compare_detail struct { Id int64 `orm:"pk;auto"` IedName string `json:"ied_name"` IedDesc string `json:"ied_desc"` CompareId int64 `json:"compare_id"` //发生差异的对象类型:node\attr DiffObjectType string `json:"diff_object_type"` DiffObjectNodeId int64 `json:"diff_object_node_id"` DiffObjectName string `json:"diff_object_name"` DiffSourceNodeId int64 `json:"diff_source_node_id"` SourceLineNo int64 `json:"source_line_no"` ObjectLineNo int64 `json:"object_line_no"` //发生差异的操作:新增i\修改u\删除d DiffOpt string `json:"diff_opt"` //差异描述 DiffDesc string `json:"diff_desc"` } //文件scd文件差异对比 type ScdCompare struct { Userinfo map[string]interface{} lock sync.RWMutex StationId int //如果是2个对比文件时,需要对比的文件ID列表。仅当CompareType为ccd\cid\ccd_cid_scd\icd_scd时有效 CompFileIds []string //对比的文件类型码:scd|icd|ccd|...。全大写 CompareType string //用于对比的scd文件ID Sourceid int64 //被比较的scd文件id Targetid int64 //当前对比记录ID CurrCompareRecord t_scd_diff_compare //对比结果队列 CompareResultList []t_scd_diff_compare_detail //对比文件的基本信息 CompareBaseInfo map[string]interface{} } func init() { orm.RegisterModel(new(t_scd_diff_compare)) orm.RegisterModel(new(t_scd_diff_compare_detail)) } func (c *ScdCompare) GetCheckToolsTreeRoot(scdid, id, pid int64, datatype string) ([]orm.Params, error) { db := orm.NewOrm() reuslt := []orm.Params{} areaMgr := new(ScdAreaMgr) if id == 0 { //获取间隔列表 areaList, err := areaMgr.GetAreaList(scdid) if err != nil { return nil, err } //获取该电压等级下的间隔列表 for _, ararow := range areaList { //if tools.IsEmpty(ararow["voltage_level"]) == tools.IsEmpty(row2["id"]) { ararow["title"] = ararow["name"] ararow["datatype"] = "area" ararow["isParent"] = "true" ararow["pid"] = tools.IsEmpty(ararow["voltage_level"]) reuslt = append(reuslt, ararow) //} } sql := "SELECT id,(select scd_name from t_scd_scl where id=?) title,0 pid,'true' isParent,'SCL' datatype from t_scd_node_scl WHERE scd_id=? and parent_node_id=? union SELECT id,node_name title,parent_node_id pid,'false' isParent,node_name datatype from t_scd_node_scl where scd_id=? and parent_node_id=(SELECT id from t_scd_node_scl WHERE scd_id=? and parent_node_id=?) GROUP BY node_name order by id " rowset := []orm.Params{} db.Raw(sql, scdid, scdid, scdid, scdid, scdid, scdid).Values(&rowset) for _, row := range rowset { reuslt = append(reuslt, row) if tools.IsEmpty(row["title"]) == "IED" { iednodeid := tools.IsEmpty(row["id"]) //添加电压等级子节点 row["isParent"] = "true" sql = "select voltage_level id,t1.name title,0 pid ,'true' isParent,'voltage_level' datatype from t_substation_area t,global_const_code t1 where t.voltage_level=t1.id and scd_id=? GROUP BY voltage_level ORDER BY voltage_level" voltagelevelRowset := []orm.Params{} db.Raw(sql, scdid).Values(&voltagelevelRowset) for _, row2 := range voltagelevelRowset { row2["pid"] = iednodeid reuslt = append(reuslt, row2) } } } } else { if datatype == "area" { //当前为间隔时,则返回该间隔下的IED iedList, err := areaMgr.GetIedList(scdid, 0, int32(id), "") if err != nil { return nil, err } for _, row := range iedList { row["isParent"] = "false" row["pid"] = id row["id"] = row["node_id"] row["title"] = strings.Replace(tools.IsEmpty(row["attr_desc"]), tools.IsEmpty(row["name"]), "..", 1) row["datatype"] = "ied" } reuslt = iedList } } new(SystemLog).Success(enum.AuditType_scd_diffcompare, enum.LogType_Query, enum.OptEventType_Bus, enum.OptEventLevel_Low, "查询IED结构树", c.Userinfo) return reuslt, nil } func (c *ScdCompare) List() ([]orm.Params, error) { db := orm.NewOrm() sql := "select t.*,t1.scd_name source_name,case when t1.enable=0 then '签入中' else t1.version end source_version ,t2.scd_name target_name,case when t2.enable=0 then '签入中' else t2.version end target_version from t_scd_diff_compare t left JOIN t_scd_scl t1 on t.source_id=t1.id left JOIN t_scd_scl t2 on t.target_id=t2.id where t.station_id=? order by CREATED_TIME desc" rowset := []orm.Params{} _, err := db.Raw(sql, c.StationId).Values(&rowset) sqllog := fmt.Sprintf("SQL:%s,参数:%+v", sql, c.StationId) if err != nil { logger.Logger.Error(err, sqllog) new(SystemLog).Fail(enum.AuditType_scd_diffcompare, enum.LogType_Query, enum.OptEventType_Bus, enum.OptEventLevel_Low, sqllog, c.Userinfo) } else { new(SystemLog).Success(enum.AuditType_scd_diffcompare, enum.LogType_Query, enum.OptEventType_Bus, enum.OptEventLevel_Low, sqllog, c.Userinfo) } return rowset, err } func (c *ScdCompare) ExpResultList(id int, nodetype string, nodeid string) ([]orm.Params, error) { db := orm.NewOrm() diffObjectTypeTransSql := `case diff_object_type when 'scd.version' then 'SCD版本号' when 'scd.crc' then 'SCD CRC' when 'scd.ied' then '装置基本信息' when 'scd.ied.Communication' then '通信接入点' when 'scd.ied.Communication.S1' then '站控层通信参数' when 'scd.ied.Communication.GSE' then 'GOOSE 通信参数' when 'scd.ied.Communication.SMV' then 'SV 通信参数' when 'scd.ied.YX' then '遥信测点' when 'scd.ied.YK' then '遥控测点' when 'scd.ied.YC' then '遥测测点' when 'scd.ied.YM' then '遥脉测点' when 'scd.ied.DZ' then '定值测点' when 'scd.ied.FCDA' then '发布虚端子' when 'scd.ied.ExtRef' then '定阅虚端子' when 'scd.ied.DatSet' then '数据集' when 'scd.ied.DatSetMeber' then '数据集' when 'scd.ied.GSEControl' then 'GOOSE控制块' when 'scd.ied.SampledValueControl' then 'SV控制块' when 'scd.ied.ReportControl' then '报告控制块' when 'scd.ied.LogControl' then '日志控制块' else '' end` sql := `select a.scd_name source_scd_name,b.scd_name target_scd_name,case when t0.diff_opt='i' then '新增' when t0.diff_opt='u' then '修改' else '删除' end opt,t0.diff_desc 'desc',ied_name,ied_desc,` + diffObjectTypeTransSql + ` diff_object_type,diff_object_name from t_scd_diff_compare t inner join t_scd_diff_compare_detail t0 on t.id=t0.compare_id left join t_scd_scl a on t.source_id=a.id left join t_scd_scl b on t.target_id=b.id where t.id=? and t0.diff_source_node_id is not null` param := []interface{}{} if nodetype == "ied" { sql = `select t.* from t_scd_diff_compare_detail t0 ,t_scd_ied_attrs s,t_scd_node_scl t1 where s.scd_id=? and s.node_id=? and t1.scd_id=? and t1.ied_name=s.attr_name and t0.diff_source_node_id=t1.id and t0.compare_id=? and t.diff_source_node_id is not null` param = append(param, nodeid) param = append(param, id) } else { param = append(param, id) } rowset := []orm.Params{} sqllog := fmt.Sprintf("SQL:%s,参数:%+v", sql+" ORDER BY t0.ied_name,t0.diff_object_type limit 0,65535", param) _, err := db.Raw(sql+" ORDER BY t0.ied_name,t0.diff_object_type limit 0,65535", param).Values(&rowset) if err != nil { logger.Logger.Error(err, sqllog) new(SystemLog).Fail(enum.AuditType_scd_diffcompare, enum.LogType_exp, enum.OptEventType_Bus, enum.OptEventLevel_Low, sqllog, c.Userinfo) } else { new(SystemLog).Success(enum.AuditType_scd_diffcompare, enum.LogType_exp, enum.OptEventType_Bus, enum.OptEventLevel_Low, sqllog, c.Userinfo) } return rowset, err } //已废弃 func (c *ScdCompare) ResultList(dirction string, scdid int64, id int, nodetype, nodeid string, pageno, pagesize int) ([]orm.Params, int, error) { db := orm.NewOrm() sql := "" comp_col_name := "diff_source_node_id" if dirction == "target" { comp_col_name = "diff_object_node_id" } where := "" param := []interface{}{} if nodetype == "ied" { sql = `select t.* from t_scd_diff_compare_detail t where t.compare_id=? and EXISTS(select 1 from t_scd_node_scl where t.` + comp_col_name + `=id and ied_name=? and scd_id=?)` param = append(param, id) param = append(param, nodeid) param = append(param, scdid) } else { if nodetype != "" { sql = `select t.* from t_scd_diff_compare_detail t where t.compare_id=?` param = append(param, id) if nodetype == "area" { //间隔 where += " and EXISTS(select 1 from t_area_ied_relation a,t_scd_node_scl s,t_scd_node_scl s1 where a.ied_id=s.id and s1.ied_name=s.ied_name and t." + comp_col_name + "=s1.id and a.area_id=? and s.scd_id=? and s1.scd_id=?)" param = append(param, nodeid) param = append(param, scdid) param = append(param, scdid) } else if nodetype == "voltage_level" { //电压等级 where += " and EXISTS(select 1 from t_substation_area v1, t_area_ied_relation v2,t_scd_node_scl s,t_scd_node_scl s1 where v2.ied_id=s.id and s1.ied_name=s.ied_name and t." + comp_col_name + "=s1.id and v1.id=v2.area_id and v1.voltage_level=? and v1.scd_id=? and s.scd_id=? and s1.scd_id=?)" param = append(param, nodeid) param = append(param, scdid) param = append(param, scdid) param = append(param, scdid) } else if nodetype == "Communication" { //通讯类 where += " and EXISTS(select 1 from t_scd_ied_node_parents_info v2 where t." + comp_col_name + "=v2.node_id and v2.path_names like ? and v2.scd_id=? )" param = append(param, "SubNetwork%") param = append(param, scdid) } else if nodetype == "DataTypeTemplates" { //通讯类 where += " and EXISTS(select 1 from t_scd_ied_node_parents_info v2 where t." + comp_col_name + "=v2.node_id and v2.path_names like ? and v2.scd_id=? )" param = append(param, "DataTypeTemplates%") param = append(param, scdid) } else { //Header类 where += " and t." + comp_col_name + "=?" param = append(param, nodeid) } } else { sql = `select t.* from t_scd_diff_compare_detail t,t_scd_node_scl t1 where t1.scd_id=? and t.` + comp_col_name + `=t1.id and t.compare_id=?` param = append(param, scdid) param = append(param, id) } } limit := fmt.Sprintf(" order by t.id limit %d,%d", (pageno-1)*pagesize, pagesize) rowset := []orm.Params{} sqllog := fmt.Sprintf("SQL:%s,参数:%+v", sql+where+limit, param) _, err := db.Raw(sql+where+limit, param).Values(&rowset) if err != nil { logger.Logger.Error(err, sqllog) new(SystemLog).Fail(enum.AuditType_scd_diffcompare, enum.LogType_Query, enum.OptEventType_Bus, enum.OptEventLevel_Low, sqllog, c.Userinfo) } else { new(SystemLog).Success(enum.AuditType_scd_diffcompare, enum.LogType_Query, enum.OptEventType_Bus, enum.OptEventLevel_Low, sqllog, c.Userinfo) } totalNum := 0 if err == nil { tmpRowset := []orm.Params{} sql = strings.ReplaceAll(sql+where, "t.*", "count(1) cnt") sqllog = fmt.Sprintf("SQL:%s,参数:%+v", sql, param) _, err = db.Raw(sql, param).Values(&tmpRowset) if err != nil { logger.Logger.Error(err, sqllog) } else if len(tmpRowset) > 0 { for _, r1 := range tmpRowset { totalNumi, _ := strconv.Atoi(tools.IsEmpty(r1["cnt"])) totalNum += totalNumi } } } return rowset, totalNum, err } //汇总统计校验结果数量 func (c *ScdCompare) SumCompareResult(compare_id int) ([]orm.Params, error) { db := orm.NewOrm() sql := `SELECT 'u' diff_opt, diff_object_type,'' ied_name,'' ied_desc,diff_desc from t_scd_diff_compare_detail t where compare_id=? and ied_name='' GROUP BY diff_object_type UNION SELECT 'i' diff_opt,'scd.ied' diff_object_type, ied_name,ied_desc ,'' diff_desc from t_scd_diff_compare_detail t where compare_id=? and ied_name=diff_object_name and diff_opt='i' UNION SELECT 'u' diff_opt,'scd.ied' diff_object_type, ied_name,ied_desc,'' diff_desc from t_scd_diff_compare_detail t where compare_id=? and diff_object_type = 'scd.ied' and ied_name=diff_object_name and diff_opt='u' UNION SELECT 'd' diff_opt,'scd.ied' diff_object_type, ied_name,ied_desc,'' diff_desc from t_scd_diff_compare_detail t where compare_id=? and diff_object_type = 'scd.ied' and ied_name=diff_object_name and diff_opt='d' union SELECT 'u' diff_opt,'scd.ied' diff_object_type, ied_name,ied_desc,'' diff_desc from t_scd_diff_compare_detail t where compare_id=? and diff_object_type like 'scd.ied.%' GROUP BY ied_name` rowset := []orm.Params{} _, err := db.Raw(sql, compare_id, compare_id, compare_id, compare_id, compare_id).Values(&rowset) if err != nil { logger.Logger.Error(err, fmt.Sprintf("SQL:%s 参数:%d", sql, compare_id)) return nil, err } return rowset, nil } //统计指定IED各分类数量 func (c *ScdCompare) SumCompareTypeResult(compareId int, iedname, comptype string) (map[string]interface{}, error) { compObj := t_scd_diff_compare{Id: int64(compareId)} db := orm.NewOrm() err := db.Read(&compObj) if err != nil { logger.Logger.Error(err) return nil, err } sourceScdXmlObj, serr := new(ScdParse).GetScdXmlObjectBySCDID(tools.IsEmpty(compObj.SourceId)) if serr != nil { return nil, serr } if sourceScdXmlObj == nil { return nil, errors.New("无效校验的源SCD,文件可能已被删除") } sourceIedObj := new(ScdNode).GetIed(sourceScdXmlObj, "", iedname) targetScdXmlObj, serr := new(ScdParse).GetScdXmlObjectBySCDID(tools.IsEmpty(compObj.TargetId)) if serr != nil { return nil, serr } if targetScdXmlObj == nil { return nil, errors.New("无效的比对SCD,文件可能已被删除") } targetIedObj := new(ScdNode).GetIed(targetScdXmlObj, "", iedname) result := map[string]interface{}{} switch comptype { case "i": //装置基本信息 result["scd.ied"] = 1 break case "d": //装置基本信息 result["scd.ied"] = 1 break case "u": //装置基本信息 num := 0 if sourceIedObj.Desc != targetIedObj.Desc { num = num + 1 } if sourceIedObj.Type != targetIedObj.Type { num = num + 1 } if sourceIedObj.ConfigVersion != targetIedObj.ConfigVersion { num = num + 1 } if sourceIedObj.Manufacturer != targetIedObj.Manufacturer { num = num + 1 } if num > 0 { result["scd.ied"] = 1 } sql := "select diff_object_type,count(1) cnt from t_scd_diff_compare_detail t WHERE compare_id=? and ied_name=? GROUP BY diff_object_type" rowset := []orm.Params{} _, err := db.Raw(sql, compareId, iedname).Values(&rowset) if err != nil { return nil, err } for _, r := range rowset { result[tools.IsEmpty(r["diff_object_type"])] = tools.IsEmpty(r["cnt"]) } break } return result, nil } //查询IED指定差异类型的信息 //compareId:比对ID //iedname:装置名称 //comptype:差异类型(i\u\d) //item:比对内容项代码 func (c *ScdCompare) GetCompItemDetailInfo(compareId int, iedname, comptype, itemcode string) ([]interface{}, error) { compObj := t_scd_diff_compare{Id: int64(compareId)} db := orm.NewOrm() err := db.Read(&compObj) if err != nil { logger.Logger.Error(err) return nil, err } sourceScdXmlObj, serr := new(ScdParse).GetScdXmlObjectBySCDID(tools.IsEmpty(compObj.SourceId)) if serr != nil { return nil, serr } if sourceScdXmlObj == nil { return nil, errors.New("无效校验的源SCD,文件可能已被删除") } sourceIedObj := new(ScdNode).GetIed(sourceScdXmlObj, "", iedname) targetScdXmlObj, serr := new(ScdParse).GetScdXmlObjectBySCDID(tools.IsEmpty(compObj.TargetId)) if serr != nil { return nil, serr } if targetScdXmlObj == nil { return nil, errors.New("无效的比对SCD,文件可能已被删除") } if itemcode == "scd.ied.Relation" { //虚回路配置 scdNode := new(ScdNode) sourceRel, err1 := scdNode.GetIedInputsRelations(map[string]interface{}{ "ied_name": iedname, "scd_id": compObj.SourceId, }) if err1 != nil { return nil, err1 } targetRel, err2 := scdNode.GetIedInputsRelations(map[string]interface{}{ "ied_name": iedname, "scd_id": compObj.TargetId, }) if err2 != nil { return nil, err2 } iedDescMap := map[string]string{ iedname: sourceIedObj.Desc, } result := []interface{}{} sourceRelMap := map[string]orm.Params{} targetRelMap := map[string]orm.Params{} for _, r := range sourceRel { sourceRelMap[fmt.Sprintf("%s%s%s", tools.IsEmpty(r["ied_name"]), tools.IsEmpty(r["ied_name"]), tools.IsEmpty(r["attr_int_addr"]), tools.IsEmpty(r["inout_type"]))] = r } for _, r := range targetRel { targetRelMap[fmt.Sprintf("%s%s%s", tools.IsEmpty(r["ied_name"]), tools.IsEmpty(r["ied_name"]), tools.IsEmpty(r["attr_int_addr"]), tools.IsEmpty(r["inout_type"]))] = r } //logger.Logger.Debug(fmt.Sprintf("sourceRelMap:%+v", sourceRelMap)) //logger.Logger.Debug(fmt.Sprintf("targetRelMap:%+v", targetRelMap)) for key, item := range sourceRelMap { if _, h := targetRelMap[key]; !h { //新增的端子 item["opt"] = "i" tmpIedName := tools.IsEmpty(item["ied_name"]) ieddesc := iedDescMap[tmpIedName] item["scd_id"] = compObj.SourceId if ieddesc != "" { item["ied_desc"] = ieddesc } else { tmpIedObj := new(ScdNode).GetIed(sourceScdXmlObj, "", tmpIedName) if tmpIedObj != nil { iedDescMap[tmpIedName] = tmpIedObj.Desc item["ied_desc"] = tmpIedObj.Desc } } result = append(result, item) } } for key, item := range targetRelMap { if _, h := sourceRelMap[key]; !h { //删除的端子 item["opt"] = "d" tmpIedName := tools.IsEmpty(item["ied_name"]) ieddesc := iedDescMap[tmpIedName] item["scd_id"] = compObj.TargetId if ieddesc != "" { item["ied_desc"] = ieddesc } else { tmpIedObj := new(ScdNode).GetIed(targetScdXmlObj, "", tmpIedName) if tmpIedObj != nil { iedDescMap[tmpIedName] = tmpIedObj.Desc item["ied_desc"] = tmpIedObj.Desc } } result = append(result, item) } } sourceRelMap = nil targetRelMap = nil return result, nil } targetIedObj := new(ScdNode).GetIed(targetScdXmlObj, "", iedname) result := []interface{}{} if itemcode == "scd.ied" { //IED基本信息 if comptype == "i" { result = append(result, new(node_attr.BaseNode).ToMap(sourceIedObj)) } else if comptype == "d" { result = append(result, new(node_attr.BaseNode).ToMap(targetIedObj)) } else if comptype == "u" { result = append(result, new(node_attr.BaseNode).ToMap(sourceIedObj)) result = append(result, new(node_attr.BaseNode).ToMap(targetIedObj)) } } else { //需要统计新增、删除、编辑的测点 sql := "SELECT diff_object_type,diff_object_name,diff_opt,diff_desc from t_scd_diff_compare_detail where compare_id=? and ied_name=? and diff_object_type=? order by diff_opt desc,diff_object_name asc" rowset := []orm.Params{} _, err := db.Raw(sql, compareId, iedname, itemcode).Values(&rowset) if err != nil { logger.Logger.Error(err, fmt.Sprintf("SQL:%s 参数:%+v", sql, []interface{}{compareId, iedname, itemcode})) return nil, err } for _, r := range rowset { result = append(result, r) } } return result, nil } //开始比较2个文件。如果比较过程中发生错误则返回错误对象 //对比结果会存储到数据表,同时以mqtt方式进行实时发布 func (c *ScdCompare) Compare(oldid int) error { db := orm.NewOrm() if oldid > 0 { c.CurrCompareRecord = t_scd_diff_compare{Id: int64(oldid)} db.Read(&c.CurrCompareRecord) if c.CurrCompareRecord.NodeState == 0 { return errors.New("其他用户正在进行校验,需等待其完成!") } c.Sourceid = c.CurrCompareRecord.SourceId c.Targetid = c.CurrCompareRecord.TargetId sourceScdXmlObj, serr := new(ScdParse).GetScdXmlObjectBySCDID(tools.IsEmpty(c.Sourceid)) if serr != nil { return serr } if sourceScdXmlObj == nil { return errors.New("无效校验的源SCD,文件可能已被删除") } targetScdXmlObj, serr := new(ScdParse).GetScdXmlObjectBySCDID(tools.IsEmpty(c.Targetid)) if serr != nil { return serr } if targetScdXmlObj == nil { return errors.New("无效的比对SCD,文件可能已被删除") } //对原有比对进行重新比对,需要清除原有结果 db.Raw("delete from t_scd_diff_compare_detail where compare_id=?", oldid).Exec() db.Raw("delete from t_scd_diff_compare_stat where compare_id=?", oldid).Exec() db.Raw("update t_scd_diff_compare set node_state=0,attr_state=0 where id=?", oldid).Exec() if c.CompareType == "SCD" { c.ScdCompare(sourceScdXmlObj, targetScdXmlObj) } return nil } if c.CompareType == "" { return errors.New("未指定校验的类型!") } if c.CompareType == "CID" { return c.CidCompare() } if c.CompareType == "CCD" { return c.CcdCompare() } if c.CompareType == "CCD_SCD" { return c.CcdScdCompare() } if c.CompareType == "CID_SCD" { return c.CidScdCompare() } if c.StationId == 0 { return errors.New("变电站ID不能为空!") } if c.Sourceid == 0 || c.Targetid == 0 { return errors.New("用于校验的源scd文件ID或目标文件ID不能为空!") } sourceScdXmlObj, serr := new(ScdParse).GetScdXmlObjectBySCDID(tools.IsEmpty(c.Sourceid)) if serr != nil { return serr } if sourceScdXmlObj == nil { return errors.New("无效校验的源SCD") } targetScdXmlObj, serr := new(ScdParse).GetScdXmlObjectBySCDID(tools.IsEmpty(c.Targetid)) if serr != nil { return serr } if targetScdXmlObj == nil { return errors.New("无效的目标SCD") } //判断当前2个文件是否正在校验中 sql := "select 1 from t_scd_diff_compare where source_id=? and target_id=?" rowset := []orm.Params{} _, err := db.Raw(sql, c.Sourceid, c.Targetid).Values(&rowset) if err != nil { logger.Logger.Error(err, sql) return err } if len(rowset) > 0 { //当前2个文件正在校验中或者已经有校验结果 return nil } master := t_scd_diff_compare{StationId: c.StationId, FileType: c.CompareType, SourceId: c.Sourceid, SourceFile: "", TargetId: c.Targetid, TargetFile: "", NodeState: 0, AttrState: 0} master.Name = fmt.Sprintf("%s对比%s", c.CompareType, time.Now().Format("200601021504")) _, err = db.Insert(&master) if err != nil { logger.Logger.Error(err) return err } c.CurrCompareRecord = master //fmt.Println(fmt.Sprintf("==================%v", c.CurrCompareRecord)) if c.CompareType == "SCD" { c.ScdCompare(sourceScdXmlObj, targetScdXmlObj) } else { return errors.New("无效的校验文件类型:" + c.CompareType) } return nil } //Scd文件节点差异对比 func (c *ScdCompare) ScdCompare(sourceScdXmlObj *node_attr.SCL, targetScdXmlObj *node_attr.SCL) { go mqtt.PublishMessage("/jujutong/scd_check_tools/comp/"+fmt.Sprintf("%d", c.CurrCompareRecord.Id), `{"id":"`+fmt.Sprintf("%d", c.CurrCompareRecord.Id)+`","stationid":"`+fmt.Sprintf("%d", c.CurrCompareRecord.StationId)+`","state":0}`) t1 := time.Now().Unix() //scd版本、crc对比 sourcever := "" targetver := "" detailrow := t_scd_diff_compare_detail{} if sourceScdXmlObj.Header != nil && sourceScdXmlObj.Header.History != nil && len(sourceScdXmlObj.Header.History.Hitem) > 0 { ver := sourceScdXmlObj.Header.History.Hitem[len(sourceScdXmlObj.Header.History.Hitem)-1] sourcever = ver.Version + "," + ver.Revision detailrow.SourceLineNo = sourceScdXmlObj.Header.History.Lineno detailrow.DiffSourceNodeId = sourceScdXmlObj.Header.History.NodeId } if targetScdXmlObj.Header != nil && targetScdXmlObj.Header.History != nil && len(targetScdXmlObj.Header.History.Hitem) > 0 { ver := targetScdXmlObj.Header.History.Hitem[len(targetScdXmlObj.Header.History.Hitem)-1] targetver = ver.Version + "," + ver.Revision detailrow.DiffObjectNodeId = targetScdXmlObj.Header.History.NodeId detailrow.ObjectLineNo = targetScdXmlObj.Header.History.Lineno } //logger.Logger.Debug(fmt.Sprintf("=====sourceScdXmlObj sourcever:%s targetScdXmlObj targetver:%s", sourcever, targetver)) if targetver != sourcever { //scd版本差异 detailrow.CompareId = c.CurrCompareRecord.Id detailrow.DiffDesc = fmt.Sprintf(`["%s","%s"]`, sourcever, targetver) detailrow.DiffObjectType = "scd.version" detailrow.DiffOpt = "u" detailrow.DiffObjectName = "version" c.CompareResultList = append(c.CompareResultList, detailrow) } //scd CRC对比 sourcecrc := "" targetcrc := "" detailrow_crc := t_scd_diff_compare_detail{} if len(sourceScdXmlObj.Private) > 0 { for _, p := range sourceScdXmlObj.Private { if p.Type == "Substation virtual terminal conection CRC" { sourcecrc = p.InnerText detailrow_crc.SourceLineNo = p.Lineno detailrow_crc.DiffSourceNodeId = p.NodeId break } } } if len(targetScdXmlObj.Private) > 0 { for _, p := range targetScdXmlObj.Private { if p.Type == "Substation virtual terminal conection CRC" { targetcrc = p.InnerText detailrow_crc.ObjectLineNo = p.Lineno detailrow_crc.DiffObjectNodeId = p.NodeId break } } } //logger.Logger.Debug(fmt.Sprintf("=====sourceScdXmlObj sourcecrc:%s targetScdXmlObj targetcrc:%s", sourcecrc, targetcrc)) if sourcecrc != targetcrc { //scd差异 detailrow_crc.CompareId = c.CurrCompareRecord.Id detailrow_crc.DiffDesc = fmt.Sprintf(`["%s","%s"]`, sourcecrc, targetcrc) detailrow_crc.DiffObjectType = "scd.crc" detailrow_crc.DiffOpt = "u" detailrow.DiffObjectName = "crc" c.CompareResultList = append(c.CompareResultList, detailrow_crc) } //IED差异对比 sourceIedlist := map[string]*node_attr.NIED{} targetIedList := map[string]*node_attr.NIED{} for _, ied := range sourceScdXmlObj.IED { sourceIedlist[ied.Name] = ied } for _, ied := range targetScdXmlObj.IED { targetIedList[ied.Name] = ied } for iedname, iedObj := range sourceIedlist { if tmpIed, h := targetIedList[iedname]; !h { //基准scd中新增了该iED c.addResult(iedname, iedObj.Desc, "i", "scd.ied", iedname, iedname, iedObj.NodeId, iedObj.Lineno) } else { sourceedit := fmt.Sprintf(`{"desc":"%s","type":"%s","manufacturer":"%s","configVersion":"%s"}`, iedObj.Desc, iedObj.Type, iedObj.Manufacturer, iedObj.ConfigVersion) targetedit := fmt.Sprintf(`{"desc":"%s","type":"%s","manufacturer":"%s","configVersion":"%s"}`, tmpIed.Desc, tmpIed.Type, tmpIed.Manufacturer, tmpIed.ConfigVersion) //编辑了ied属性 if sourceedit != targetedit { desc := fmt.Sprintf(`[%s,%s]`, sourceedit, targetedit) c.addResult(iedname, iedObj.Desc, "u", "scd.ied", iedname, desc, iedObj.NodeId, iedObj.Lineno) } //比较通讯层参数:装置IP、掩码、网关 c.iedCommunicationCompare(sourceScdXmlObj, targetScdXmlObj, iedname, iedObj.Desc) //比较回路虚端子配置 c.iedFcdaCompare(sourceScdXmlObj, targetScdXmlObj, iedname, iedObj.Desc) } } for iedname, iedObj := range targetIedList { if _, h := sourceIedlist[iedname]; !h { //基准scd中删除了该iED c.addResult(iedname, iedObj.Desc, "d", "scd.ied", iedname, iedname, iedObj.NodeId, iedObj.Lineno) } } //差异结果写入数据库 c.writeResult() c.CurrCompareRecord.NodeState = 1 _, err := orm.NewOrm().Update(&c.CurrCompareRecord) if err != nil { logger.Logger.Error(err) return } t2 := time.Now().Unix() logger.Logger.Debug(fmt.Sprintf("文件差异对比完成,耗时:%d秒", t2-t1)) new(SystemLog).Success(enum.AuditType_scd_diffcompare, enum.LogType_Execute, enum.OptEventType_Bus, enum.OptEventLevel_Hight, "SCD差异对比", c.Userinfo) go mqtt.PublishMessage("/jujutong/scd_check_tools/comp/"+fmt.Sprintf("%d", c.CurrCompareRecord.Id), `{"id":"`+fmt.Sprintf("%d", c.CurrCompareRecord.Id)+`","stationid":"`+fmt.Sprintf("%d", c.CurrCompareRecord.StationId)+`","state":1}`) } //CID文件节点差异对比 func (c *ScdCompare) CidCompare() error { if len(c.CompFileIds) != 2 { return errors.New("CID一致性校验仅支持2个文件校验") } //根据文件id获取路径 cid1id, _ := strconv.Atoi(c.CompFileIds[0]) if cid1id == 0 { //非文件id时,表示是当前scd中的装置name fileinfo, _ := new(AttachmentMgr).GetInfoByIed(c.Sourceid, c.CompFileIds[0], "cid") if fileinfo.Id == 0 { return errors.New("基准CID文件ID(" + c.CompFileIds[0] + ")不正确") } cid1id = int(fileinfo.Id) } cid2id, _ := strconv.Atoi(c.CompFileIds[1]) if cid2id == 0 { return errors.New("比对CID文件ID(" + c.CompFileIds[1] + ")不正确") } cidmodel := new(AttachmentMgr) cidmodel.Model.Id = int32(cid1id) cidfileobj, err := cidmodel.One() if err != nil { return err } cid1FileName := cidfileobj.FileName cid1Ct := cidfileobj.CreatedTime pathChar := string(os.PathSeparator) //解析文件为对象 cid1XmlObj, err := new(ScdParse).LoadScdXml("." + strings.ReplaceAll(cidfileobj.SavePath, "\\", pathChar)) if err != nil { return err } cidmodel.Model.Id = int32(cid2id) cidfileobj, err = cidmodel.One() if err != nil { return err } cid2FileName := cidfileobj.FileName cid2Ct := cidfileobj.CreatedTime cid2XmlObj, err := new(ScdParse).LoadScdXml("." + strings.ReplaceAll(cidfileobj.SavePath, "\\", pathChar)) if err != nil { return err } if len(cid1XmlObj.IED) != 1 || len(cid2XmlObj.IED) != 1 { return errors.New("CID文件中的IED不正确,有且仅允许1个IED") } if cid1XmlObj.IED[0].Name != cid2XmlObj.IED[0].Name { return errors.New("两个CID文件中的IED不是同一装置,不能比对") } f1 := map[string]interface{}{"filename": cid1FileName, "ct": cid1Ct} f2 := map[string]interface{}{"filename": cid2FileName, "ct": cid2Ct} return c.cidCompe(f1, f2, cid1XmlObj, cid2XmlObj) } //CID与SCD文件节点差异对比 func (c *ScdCompare) CidScdCompare() error { if len(c.CompFileIds) != 1 { return errors.New("请选择一个CID文件!") } //根据文件id获取路径 if c.Sourceid == 0 { return errors.New("基准SCD文件ID(" + tools.IsEmpty(c.Sourceid) + ")不正确") } cid2id, _ := strconv.Atoi(c.CompFileIds[0]) if cid2id == 0 { return errors.New("比对CID文件ID(" + c.CompFileIds[0] + ")不正确") } cidmodel := new(AttachmentMgr) cidmodel.Model.Id = int32(cid2id) cidfileobj, err := cidmodel.One() if err != nil { return err } pathChar := string(os.PathSeparator) cid2FileName := cidfileobj.FileName cid2Ct := cidfileobj.CreatedTime cid2XmlObj, err := new(ScdParse).LoadScdXml("." + strings.ReplaceAll(cidfileobj.SavePath, "\\", pathChar)) if err != nil { return err } if len(cid2XmlObj.IED) != 1 { return errors.New("无效的Cid文件:" + cid2FileName) } iedname := cid2XmlObj.IED[0].Name scdinfo, _ := new(ScdMgr).One(tools.IsEmpty(c.Sourceid)) if scdinfo == nil { return errors.New("无效的SCD") } //解析基准文件为对象 cid1Paths := []string{".", "static", "upload", "ied", tools.IsEmpty(c.Sourceid), iedname + ".cid"} cid1Path := strings.Join(cid1Paths, pathChar) cid1Finfo, _ := os.Stat(cid1Path) if cid1Finfo == nil { return errors.New("在SCD(" + tools.IsEmpty(scdinfo["scd_name"]) + ")中未找到装置" + iedname + "的CID文件") } cid1XmlObj, err := new(ScdParse).LoadScdXml(cid1Path) if err != nil { return err } if len(cid1XmlObj.IED) != 1 || len(cid2XmlObj.IED) != 1 { return errors.New("CID文件中的IED不正确,有且仅允许1个IED") } if cid1XmlObj.IED[0].Name != cid2XmlObj.IED[0].Name { return errors.New("SCD与CID文件中的IED不是同一装置,不能比对") } f1 := map[string]interface{}{"filename": tools.IsEmpty(scdinfo["scd_name"]), "ct": tools.IsEmpty(scdinfo["CREATED_TIME"])} f2 := map[string]interface{}{"filename": cid2FileName, "ct": cid2Ct} return c.cidCompe(f1, f2, cid1XmlObj, cid2XmlObj) } func (c *ScdCompare) cidCompe(cid1info, cid2onfo map[string]interface{}, cid1XmlObj *node_attr.SCL, cid2XmlObj *node_attr.SCL) error { iedname := cid1XmlObj.IED[0].Name c.CompareBaseInfo = map[string]interface{}{} c.CompareBaseInfo["iedname"] = iedname c.CompareBaseInfo["ieddesc"] = cid1XmlObj.IED[0].Desc c.CompareBaseInfo["f1"] = cid1info c.CompareBaseInfo["f2"] = cid2onfo //对比Communication sourceedit := fmt.Sprintf(`{"desc":"%s","type":"%s","manufacturer":"%s","configVersion":"%s"}`, cid1XmlObj.IED[0].Desc, cid1XmlObj.IED[0].Type, cid1XmlObj.IED[0].Manufacturer, cid1XmlObj.IED[0].ConfigVersion) targetedit := fmt.Sprintf(`{"desc":"%s","type":"%s","manufacturer":"%s","configVersion":"%s"}`, cid2XmlObj.IED[0].Desc, cid2XmlObj.IED[0].Type, cid2XmlObj.IED[0].Manufacturer, cid2XmlObj.IED[0].ConfigVersion) //编辑了ied属性 if sourceedit != targetedit { desc := fmt.Sprintf(`[%s,%s]`, sourceedit, targetedit) c.addResult(iedname, cid1XmlObj.IED[0].Desc, "u", "scd.ied", iedname, desc, 0, 0) } //比较通讯层参数:装置IP、掩码、网关 c.iedCommunicationCompare(cid1XmlObj, cid2XmlObj, iedname, cid1XmlObj.IED[0].Desc) //比较回路虚端子配置 c.iedFcdaCompare(cid1XmlObj, cid2XmlObj, iedname, cid2XmlObj.IED[0].Desc) return nil } //CCD文件节点差异对比 func (c *ScdCompare) CcdCompare() error { if len(c.CompFileIds) != 2 { return errors.New("CCD一致性校验仅支持2个文件校验") } //根据文件id获取路径 ccd1id, _ := strconv.Atoi(c.CompFileIds[0]) if ccd1id == 0 { //非文件id时,表示是当前scd中的装置name fileinfo, _ := new(AttachmentMgr).GetInfoByIed(c.Sourceid, c.CompFileIds[0], "ccd") if fileinfo.Id == 0 { return errors.New("基准CCD文件ID(" + c.CompFileIds[0] + ")不正确") } ccd1id = int(fileinfo.Id) } ccd2id, _ := strconv.Atoi(c.CompFileIds[1]) if ccd2id == 0 { return errors.New("比对CCD文件ID(" + c.CompFileIds[1] + ")不正确") } ccdmodel := new(AttachmentMgr) ccdmodel.Model.Id = int32(ccd1id) ccdfileobj, err := ccdmodel.One() if err != nil { return err } ccd1FileName := ccdfileobj.FileName ccd1Ct := ccdfileobj.CreatedTime pathChar := string(os.PathSeparator) //解析文件为对象 ccd1XmlObj, err := new(ScdParse).LoadCcdXml("." + strings.ReplaceAll(ccdfileobj.SavePath, "\\", pathChar)) if err != nil { return err } ccdmodel.Model.Id = int32(ccd2id) ccdfileobj, err = ccdmodel.One() if err != nil { return err } ccd2FileName := ccdfileobj.FileName ccd2Ct := ccdfileobj.CreatedTime //解析文件为对象 ccd2XmlObj, err := new(ScdParse).LoadCcdXml("." + strings.ReplaceAll(ccdfileobj.SavePath, "\\", pathChar)) if err != nil { return err } if ccd1XmlObj.Name != ccd2XmlObj.Name { return errors.New("两个CCD文件中的IED不是同一装置,不能比对") } f1 := map[string]interface{}{"filename": ccd1FileName, "ct": ccd1Ct} f2 := map[string]interface{}{"filename": ccd2FileName, "ct": ccd2Ct} return c.ccdCompe(f1, f2, ccd1XmlObj, ccd2XmlObj) } //CCD文件节点差异对比 func (c *ScdCompare) CcdScdCompare() error { if len(c.CompFileIds) != 1 { return errors.New("请选择一个CCD文件!") } //根据文件id获取路径 if c.Sourceid == 0 { return errors.New("基准SCD文件ID(" + tools.IsEmpty(c.Sourceid) + ")不正确") } ccd2id, _ := strconv.Atoi(c.CompFileIds[0]) if ccd2id == 0 { return errors.New("比对CCD文件ID(" + c.CompFileIds[0] + ")不正确") } ccdmodel := new(AttachmentMgr) pathChar := string(os.PathSeparator) ccdmodel.Model.Id = int32(ccd2id) ccdfileobj, err := ccdmodel.One() if err != nil { return err } ccd2FileName := ccdfileobj.FileName ccd2Ct := ccdfileobj.CreatedTime //解析比对文件为对象 ccd2XmlObj, err := new(ScdParse).LoadCcdXml("." + strings.ReplaceAll(ccdfileobj.SavePath, "\\", pathChar)) if err != nil { return err } iedname := ccd2XmlObj.Name scdinfo, _ := new(ScdMgr).One(tools.IsEmpty(c.Sourceid)) if scdinfo == nil { return errors.New("无效的SCD") } //解析基准文件为对象 ccd1Paths := []string{".", "static", "upload", "ied", tools.IsEmpty(c.Sourceid), iedname + ".ccd"} ccd1Path := strings.Join(ccd1Paths, pathChar) ccd1Finfo, _ := os.Stat(ccd1Path) if ccd1Finfo == nil { return errors.New("在SCD(" + tools.IsEmpty(scdinfo["scd_name"]) + ")中未找到装置" + iedname + "的CCD文件") } ccd1XmlObj, err := new(ScdParse).LoadCcdXml(ccd1Path) if err != nil { return err } f1 := map[string]interface{}{"filename": tools.IsEmpty(scdinfo["scd_name"]), "ct": tools.IsEmpty(scdinfo["CREATED_TIME"])} f2 := map[string]interface{}{"filename": ccd2FileName, "ct": ccd2Ct} return c.ccdCompe(f1, f2, ccd1XmlObj, ccd2XmlObj) } //ccd校验 func (c *ScdCompare) ccdCompe(ccd1info, ccd2onfo map[string]interface{}, ccd1XmlObj *node_attr.CcdIed, ccd2XmlObj *node_attr.CcdIed) error { iedname := ccd1XmlObj.Name c.CompareBaseInfo = map[string]interface{}{} c.CompareBaseInfo["iedname"] = iedname c.CompareBaseInfo["ieddesc"] = ccd1XmlObj.Desc c.CompareBaseInfo["f1"] = ccd1info c.CompareBaseInfo["f2"] = ccd2onfo sourceedit := fmt.Sprintf(`{"desc":"%s","type":"%s","manufacturer":"%s","configVersion":"%s","crc":"%s"}`, ccd1XmlObj.Desc, ccd1XmlObj.Type, ccd1XmlObj.Manufacturer, ccd1XmlObj.ConfigVersion, ccd1XmlObj.CRC.Id) targetedit := fmt.Sprintf(`{"desc":"%s","type":"%s","manufacturer":"%s","configVersion":"%s","crc":"%s"}`, ccd2XmlObj.Desc, ccd2XmlObj.Type, ccd2XmlObj.Manufacturer, ccd2XmlObj.ConfigVersion, ccd2XmlObj.CRC.Id) //编辑了ied属性 if sourceedit != targetedit { desc := fmt.Sprintf(`[%s,%s]`, sourceedit, targetedit) c.addResult(iedname, ccd1XmlObj.Desc, "u", "scd.ied", iedname, desc, 0, 0) } sourceGoosePub := map[string]*node_attr.CcdGOCBref{} targetGoosePub := map[string]*node_attr.CcdGOCBref{} sourceGooseSub := map[string]*node_attr.CcdGOCBref{} targetGooseSub := map[string]*node_attr.CcdGOCBref{} sourceConnectedAPPub := map[string]*node_attr.NConnectedAP{} targetConnectedAPPub := map[string]*node_attr.NConnectedAP{} sourceConnectedAPSub := map[string]*node_attr.NConnectedAP{} targetConnectedAPSub := map[string]*node_attr.NConnectedAP{} sourceGooseDatsetPub := map[string]*node_attr.CcdDataSet{} targetGooseDatsetPub := map[string]*node_attr.CcdDataSet{} sourceGooseDatsetSub := map[string]*node_attr.CcdDataSet{} targetGooseDatsetSub := map[string]*node_attr.CcdDataSet{} if ccd1XmlObj.GOOSEPUB != nil { for _, item := range ccd1XmlObj.GOOSEPUB.GOCBref { sourceGoosePub[item.Name] = item if item.ConnectedAP != nil { sourceConnectedAPPub[item.GSEControl.AppID+"-"+item.ConnectedAP.ApName] = item.ConnectedAP } if item.DataSet != nil { sourceGooseDatsetPub[item.GSEControl.AppID+"-"+item.DataSet.Name] = item.DataSet } } } if ccd2XmlObj.GOOSEPUB != nil { for _, item := range ccd2XmlObj.GOOSEPUB.GOCBref { targetGoosePub[item.Name] = item if item.ConnectedAP != nil { targetConnectedAPPub[item.GSEControl.AppID+"-"+item.ConnectedAP.ApName] = item.ConnectedAP } if item.DataSet != nil { targetGooseDatsetPub[item.GSEControl.AppID+"-"+item.DataSet.Name] = item.DataSet } } } if ccd1XmlObj.GOOSESUB != nil { for _, item := range ccd1XmlObj.GOOSESUB.GOCBref { sourceGooseSub[item.Name] = item if item.ConnectedAP != nil { sourceConnectedAPSub[item.GSEControl.AppID+"-"+item.ConnectedAP.ApName] = item.ConnectedAP } if item.DataSet != nil { sourceGooseDatsetSub[item.GSEControl.AppID+"-"+item.DataSet.Name] = item.DataSet } } } if ccd2XmlObj.GOOSESUB != nil { for _, item := range ccd2XmlObj.GOOSESUB.GOCBref { targetGooseSub[item.Name] = item targetConnectedAPSub[item.GSEControl.AppID+"-"+item.ConnectedAP.ApName] = item.ConnectedAP if item.DataSet != nil { targetGooseDatsetSub[item.GSEControl.AppID+"-"+item.DataSet.Name] = item.DataSet } } } var compgoose = func(typecode string, g1, g2 map[string]*node_attr.CcdGOCBref) { for k, additem := range g1 { g2h := g2[k] if g2h == nil { //新增的 desc := fmt.Sprintf(`{"appID":"%s","confRev":"%s","datSet":"%s","name":"%s","type":"%s"}`, additem.GSEControl.AppID, additem.GSEControl.ConfRev, additem.GSEControl.DatSet, additem.GSEControl.Name, additem.GSEControl.Type) c.addResult(iedname, ccd1XmlObj.Desc, "i", typecode, k, desc, 0, 0) } else { //是否有修改 desc1 := fmt.Sprintf(`{"appID":"%s","confRev":"%s","datSet":"%s","name":"%s","type":"%s"}`, additem.GSEControl.AppID, additem.GSEControl.ConfRev, additem.GSEControl.DatSet, additem.GSEControl.Name, additem.GSEControl.Type) desc2 := fmt.Sprintf(`{"appID":"%s","confRev":"%s","datSet":"%s","name":"%s","type":"%s"}`, g2h.GSEControl.AppID, g2h.GSEControl.ConfRev, g2h.GSEControl.DatSet, g2h.GSEControl.Name, g2h.GSEControl.Type) if desc1 != desc2 { c.addResult(iedname, ccd1XmlObj.Desc, "u", typecode, k, fmt.Sprintf("[%s,%s]", desc1, desc2), 0, 0) } } } for k, additem := range g2 { if g1[k] == nil { //删除的 desc := fmt.Sprintf(`{"appID":"%s","confRev":"%s","datSet":"%s","name":"%s","type":"%s"}`, additem.GSEControl.AppID, additem.GSEControl.ConfRev, additem.GSEControl.DatSet, additem.GSEControl.Name, additem.GSEControl.Type) c.addResult(iedname, ccd1XmlObj.Desc, "d", typecode, k, desc, 0, 0) } } } //GOOSE发布 compgoose("scd.ied.goosepub", sourceGoosePub, targetGoosePub) //GOOSE订阅 compgoose("scd.ied.goosesub", sourceGooseSub, targetGooseSub) var compsv = func(typecode string, g1, g2 map[string]*node_attr.CcdSMVCBref) { for k, additem := range g1 { g2h := g2[k] if g2h == nil { //新增的 desc := fmt.Sprintf(`{"smvID":"%s","confRev":"%s","datSet":"%s","name":"%s","multicast":"%s","nofASDU":"%s","smpRate":"%s"}`, additem.SampledValueControl.SmvID, additem.SampledValueControl.ConfRev, additem.SampledValueControl.DatSet, additem.SampledValueControl.Name, additem.SampledValueControl.Multicast, additem.SampledValueControl.NofASDU, additem.SampledValueControl.SmpRate) c.addResult(iedname, ccd1XmlObj.Desc, "i", typecode, k, desc, 0, 0) } else { //是否有修改 desc1 := fmt.Sprintf(`{"smvID":"%s","confRev":"%s","datSet":"%s","name":"%s","multicast":"%s","nofASDU":"%s","smpRate":"%s"}`, additem.SampledValueControl.SmvID, additem.SampledValueControl.ConfRev, additem.SampledValueControl.DatSet, additem.SampledValueControl.Name, additem.SampledValueControl.Multicast, additem.SampledValueControl.NofASDU, additem.SampledValueControl.SmpRate) desc2 := fmt.Sprintf(`{"smvID":"%s","confRev":"%s","datSet":"%s","name":"%s","multicast":"%s","nofASDU":"%s","smpRate":"%s"}`, g2h.SampledValueControl.SmvID, g2h.SampledValueControl.ConfRev, g2h.SampledValueControl.DatSet, g2h.SampledValueControl.Name, g2h.SampledValueControl.Multicast, g2h.SampledValueControl.NofASDU, g2h.SampledValueControl.SmpRate) if desc1 != desc2 { c.addResult(iedname, ccd1XmlObj.Desc, "u", typecode, k, fmt.Sprintf("[%s,%s]", desc1, desc2), 0, 0) } } } for k, additem := range g2 { if g1[k] == nil { //删除的 desc := fmt.Sprintf(`{"smvID":"%s","confRev":"%s","datSet":"%s","name":"%s","multicast":"%s","nofASDU":"%s","smpRate":"%s"}`, additem.SampledValueControl.SmvID, additem.SampledValueControl.ConfRev, additem.SampledValueControl.DatSet, additem.SampledValueControl.Name, additem.SampledValueControl.Multicast, additem.SampledValueControl.NofASDU, additem.SampledValueControl.SmpRate) c.addResult(iedname, ccd1XmlObj.Desc, "d", typecode, k, desc, 0, 0) } } } sourceSVPub := map[string]*node_attr.CcdSMVCBref{} targetSVPub := map[string]*node_attr.CcdSMVCBref{} sourceSVSub := map[string]*node_attr.CcdSMVCBref{} targetSVSub := map[string]*node_attr.CcdSMVCBref{} sourceSVAPPub := map[string]*node_attr.NConnectedAP{} targetSVAPPub := map[string]*node_attr.NConnectedAP{} sourceSVAPSub := map[string]*node_attr.NConnectedAP{} targetSVAPSub := map[string]*node_attr.NConnectedAP{} sourceSVDatsetPub := map[string]*node_attr.CcdDataSet{} targetSVDatsetPub := map[string]*node_attr.CcdDataSet{} sourceSVDatsetSub := map[string]*node_attr.CcdDataSet{} targetSVDatsetSub := map[string]*node_attr.CcdDataSet{} if ccd1XmlObj.SVPUB != nil { for _, item := range ccd1XmlObj.SVPUB.SMVCBref { sourceSVPub[item.Name] = item if item.ConnectedAP != nil { sourceSVAPPub[item.SampledValueControl.SmvID+"-"+item.ConnectedAP.ApName] = item.ConnectedAP } if item.DataSet != nil { sourceSVDatsetPub[item.SampledValueControl.SmvID+"-"+item.DataSet.Name] = item.DataSet } } } if ccd2XmlObj.SVPUB != nil { for _, item := range ccd2XmlObj.SVPUB.SMVCBref { targetSVPub[item.Name] = item if item.ConnectedAP != nil { targetSVAPPub[item.SampledValueControl.SmvID+"-"+item.ConnectedAP.ApName] = item.ConnectedAP } if item.DataSet != nil { targetSVDatsetPub[item.SampledValueControl.SmvID+"-"+item.DataSet.Name] = item.DataSet } } } if ccd1XmlObj.SVSUB != nil { for _, item := range ccd1XmlObj.SVSUB.SMVCBref { sourceSVSub[item.Name] = item if item.ConnectedAP != nil { sourceSVAPSub[item.SampledValueControl.SmvID+"-"+item.ConnectedAP.ApName] = item.ConnectedAP } if item.DataSet != nil { sourceSVDatsetSub[item.SampledValueControl.SmvID+"-"+item.DataSet.Name] = item.DataSet } } } if ccd2XmlObj.SVSUB != nil { for _, item := range ccd2XmlObj.SVSUB.SMVCBref { targetSVSub[item.Name] = item if item.ConnectedAP != nil { targetSVAPSub[item.SampledValueControl.SmvID+"-"+item.ConnectedAP.ApName] = item.ConnectedAP } if item.DataSet != nil { targetSVDatsetSub[item.SampledValueControl.SmvID+"-"+item.DataSet.Name] = item.DataSet } } } //SV发布 compsv("scd.ied.svpub", sourceSVPub, targetSVPub) //SV订阅 compsv("scd.ied.svsub", sourceSVSub, targetSVSub) //通讯接入点比对 var compap = func(typecode string, g1, g2 map[string]*node_attr.NConnectedAP) { ieddesc := ccd1XmlObj.Desc for k, additem := range g1 { g2h := g2[k] if g2h == nil { //新增的 desc := fmt.Sprintf(`{"apName":"%s"}`, additem.ApName) c.addResult(iedname, ieddesc, "i", typecode, k, desc, 0, 0) if len(additem.GSE) > 0 { for _, item := range additem.GSE { key := additem.ApName + "/" + item.LdInst + "/" + item.CbName desc, _ := json.Marshal(item.Address) c.addResult(iedname, ieddesc, "i", typecode+".GSE", key, string(desc), 0, 0) } } } else { //通讯参数对比 //GOOSE通信参数对比 sourceGse := map[string]*node_attr.NGSE{} targetGse := map[string]*node_attr.NGSE{} if len(additem.GSE) > 0 { for _, item := range additem.GSE { sourceGse[additem.ApName+"/"+item.LdInst+"/"+item.CbName] = item } } if len(g2h.GSE) > 0 { for _, item := range g2h.GSE { targetGse[additem.ApName+"/"+item.LdInst+"/"+item.CbName] = item } } //logger.Logger.Debug(fmt.Sprintf("sourceGse====:%+v", sourceGse)) //logger.Logger.Debug(fmt.Sprintf("targetGse====:%+v", targetGse)) for cbname, gse := range sourceGse { //新增的gse if tmpGse, h := targetGse[cbname]; !h { desc, _ := json.Marshal(gse.Address) c.addResult(iedname, ieddesc, "i", typecode+".GSE", cbname, string(desc), gse.NodeId, gse.Lineno) } else { gse1, _ := json.Marshal(gse.Address) gse2, _ := json.Marshal(tmpGse.Address) str1 := string(gse1) str2 := string(gse2) if str1 != str2 { desc := fmt.Sprintf(`[%s,%s]`, str1, str2) c.addResult(iedname, ieddesc, "u", typecode+".GSE", cbname, desc, gse.NodeId, gse.Lineno) } } } //删除的GSE for cbname, gse := range targetGse { if _, h := sourceGse[cbname]; !h { desc, _ := json.Marshal(gse.Address) c.addResult(iedname, ieddesc, "d", typecode+".GSE", cbname, string(desc), gse.NodeId, gse.Lineno) } } //SV通信参数对比 sourceSmv := map[string]*node_attr.NSMV{} targetSmv := map[string]*node_attr.NSMV{} if len(additem.SMV) > 0 { for _, item := range additem.SMV { sourceSmv[additem.ApName+"/"+item.LdInst+"/"+item.CbName] = item } } if len(g2h.SMV) > 0 { for _, item := range g2h.SMV { targetSmv[additem.ApName+"/"+item.LdInst+"/"+item.CbName] = item } } for cbname, gse := range sourceSmv { //新增的SMV if tmpGse, h := targetSmv[cbname]; !h { desc, _ := json.Marshal(gse.Address) c.addResult(iedname, ieddesc, "i", typecode+".SMV", cbname, string(desc), gse.NodeId, gse.Lineno) } else { gse1, _ := json.Marshal(gse.Address) gse2, _ := json.Marshal(tmpGse.Address) str1 := string(gse1) str2 := string(gse2) if str1 != str2 { desc := fmt.Sprintf(`[%s,%s]`, str1, str2) c.addResult(iedname, ieddesc, "u", typecode+".SMV", cbname, (desc), gse.NodeId, gse.Lineno) } } } //删除的SMV for cbname, gse := range targetSmv { if _, h := sourceSmv[cbname]; !h { desc, _ := json.Marshal(gse.Address) c.addResult(iedname, ieddesc, "d", typecode+".SMV", cbname, string(desc), gse.NodeId, gse.Lineno) } } } } for k, additem := range g2 { if g1[k] == nil { //删除的 desc := fmt.Sprintf(`{"apName":"%s"}`, additem.ApName) c.addResult(iedname, ccd1XmlObj.Desc, "d", typecode, k, desc, 0, 0) if len(additem.GSE) > 0 { for _, item := range additem.GSE { key := additem.ApName + "/" + item.LdInst + "/" + item.CbName desc, _ := json.Marshal(item.Address) c.addResult(iedname, ieddesc, "d", typecode+".GSE", key, string(desc), 0, 0) } } } } } //GOOSE发布接入点 compap("scd.ied.goosepub.Communication", sourceConnectedAPPub, targetConnectedAPPub) //GOOSE订阅接入点 compap("scd.ied.goosesub.Communication", sourceConnectedAPSub, targetConnectedAPSub) //SV发布接入点 compap("scd.ied.smvpub.Communication", sourceSVAPPub, targetSVAPPub) //SV订阅接入点 compap("scd.ied.smvsub.Communication", sourceSVAPSub, targetSVAPSub) //Dataset虚端子比对 var fcdaToJsonStr = func(fcda *node_attr.CcdFCDA) (key, jsonstr string) { key = fmt.Sprintf("%s/%s%s", fcda.LdInst, fcda.LnClass, fcda.LnInst) desc := "" if len(fcda.IntAddr) > 0 { if len(fcda.IntAddr) == 1 { if fcda.IntAddr[0].DAI != nil { desc = fmt.Sprintf( `{"bType":"%s","daName":"%s","desc":"%s","doName":"%s","fc":"%s","prefix":"%s","intAddr.name":"%s","intAddr.desc":"%s","intAddr.sAddr":"%s"}`, fcda.BType, fcda.DaName, fcda.Desc, fcda.DoName, fcda.Fc, fcda.Prefix, fcda.IntAddr[0].Name, fcda.IntAddr[0].Desc, fcda.IntAddr[0].DAI.SAddr, ) } else { desc = fmt.Sprintf( `{"bType":"%s","daName":"%s","desc":"%s","doName":"%s","fc":"%s","prefix":"%s","intAddr.name":"%s","intAddr.desc":"%s"}`, fcda.BType, fcda.DaName, fcda.Desc, fcda.DoName, fcda.Fc, fcda.Prefix, fcda.IntAddr[0].Name, fcda.IntAddr[0].Desc, ) } } else { desc = fmt.Sprintf( `{"bType":"%s","daName":"%s","desc":"%s","doName":"%s","fc":"%s","prefix":"%s",`, fcda.BType, fcda.DaName, fcda.Desc, fcda.DoName, fcda.Fc, fcda.Prefix, ) // 有多个intAddr addrsList := []string{} for i, temp := range fcda.IntAddr { if temp.DAI == nil { addrsList = append(addrsList, fmt.Sprintf(`"intAddr.%d.name":"%s","intAddr.%d.desc":"%s"`, i, temp.Name, temp.Desc)) } else { addrsList = append(addrsList, fmt.Sprintf(`"intAddr.%d.name":"%s","intAddr.%d.desc":"%s","intAddr.%d.sAddr":"%s"`, i, temp.Name, temp.Desc, temp.DAI.SAddr)) } } desc = desc + strings.Join(addrsList, ",") + "}" } } else if fcda.DAI != nil { desc = fmt.Sprintf( `{"bType":"%s","daName":"%s","desc":"%s","doName":"%s","fc":"%s","prefix":"%s","DAI.sAddr":"%s"}`, fcda.BType, fcda.DaName, fcda.Desc, fcda.DoName, fcda.Fc, fcda.Prefix, fcda.DAI.SAddr, ) } else { desc = fmt.Sprintf( `{"bType":"%s","daName":"%s","desc":"%s","doName":"%s","fc":"%s","prefix":"%s"}`, fcda.BType, fcda.DaName, fcda.Desc, fcda.DoName, fcda.Fc, fcda.Prefix, ) } return key, desc } var compDatset = func(typecode string, sourceDatasets, targetDatasets map[string]*node_attr.CcdDataSet) { ieddesc := ccd1XmlObj.Desc for key, item := range sourceDatasets { if t2, h := targetDatasets[key]; !h { for _, fcda := range item.FCDA { fcdakey, fcdaStr := fcdaToJsonStr(fcda) c.addResult(iedname, ieddesc, "i", typecode, fcdakey, fcdaStr, item.NodeId, item.Lineno) } } else { //判断相同数据集中fcda是否有变化 sourceFcdaList := map[string]string{} targetFcdaList := map[string]string{} for _, fcda := range item.FCDA { fcdakey, fcdaStr := fcdaToJsonStr(fcda) sourceFcdaList[fcdakey] = fcdaStr } for _, fcda := range t2.FCDA { fcdakey, fcdaStr := fcdaToJsonStr(fcda) targetFcdaList[fcdakey] = fcdaStr } for skey, s1 := range sourceFcdaList { s2, h := targetFcdaList[skey] if !h { c.addResult(iedname, ieddesc, "i", typecode, skey, s1, 0, 0) } else if s1 != s2 { c.addResult(iedname, ieddesc, "u", typecode, skey, fmt.Sprintf(`[%s,%s]`, s1, s2), 0, 0) } } for skey, s1 := range targetFcdaList { if _, h := sourceFcdaList[skey]; !h { c.addResult(iedname, ieddesc, "d", typecode, skey, s1, 0, 0) } } sourceFcdaList = nil targetFcdaList = nil } } for k, item := range targetDatasets { if _, h := sourceDatasets[k]; !h { for _, fcda := range item.FCDA { fcdakey, fcdaStr := fcdaToJsonStr(fcda) c.addResult(iedname, ieddesc, "d", typecode, fcdakey, fcdaStr, item.NodeId, item.Lineno) } } } } //GOOSE发布虚端子 compDatset("scd.ied.goosepub.FCDA", sourceGooseDatsetPub, targetGooseDatsetPub) //GOOSE订阅虚端子 compDatset("scd.ied.goosesub.FCDA", sourceGooseDatsetSub, targetGooseDatsetSub) //SV发布虚端子 compDatset("scd.ied.smvpub.FCDA", sourceSVDatsetPub, targetSVDatsetPub) //SV订阅虚端子 compDatset("scd.ied.smvsub.FCDA", sourceSVDatsetSub, targetSVDatsetSub) return nil } //IED节点通信层对比。主要对比有没新增和删除:装置IP、掩码、网关等 func (c *ScdCompare) iedCommunicationCompare(sourceScdXmlObj *node_attr.SCL, targetScdXmlObj *node_attr.SCL, iedname, ieddesc string) { sourceCommu := map[string]*node_attr.NConnectedAP{} targetCommu := map[string]*node_attr.NConnectedAP{} //获取基准scd中该ied的网络列表及属性 for _, net := range sourceScdXmlObj.Communication.SubNetwork { for _, ap := range net.ConnectedAP { if ap.IedName == iedname { sourceCommu[fmt.Sprintf("%s:%s", net.Name, ap.ApName)] = ap } } } //获取对比scd中该ied的网络列表及属性 for _, net := range targetScdXmlObj.Communication.SubNetwork { for _, ap := range net.ConnectedAP { if ap.IedName == iedname { targetCommu[fmt.Sprintf("%s:%s", net.Name, ap.ApName)] = ap } } } //对比新增的子网接入点 for key, net := range sourceCommu { if tmpNet, h := targetCommu[key]; !h { desc, _ := json.Marshal(net) c.addResult(iedname, "", "i", "scd.ied.Communication", key, string(desc), net.NodeId, net.Lineno) } else { //对比接入点是否有更改 if net.Desc != tmpNet.Desc || net.ApName != tmpNet.ApName { desc := fmt.Sprintf(`[{"desc":"%s","apName":"%s"},{"desc":"%s","apName":"%s"}]`, net.Desc, net.ApName, tmpNet.Desc, tmpNet.ApName) c.addResult(iedname, ieddesc, "u", "scd.ied.Communication", key, desc, net.NodeId, net.Lineno) } //站控层通讯参数对比 if net.Address != nil && tmpNet.Address != nil { address1, _ := json.Marshal(net.Address) address2, _ := json.Marshal(tmpNet.Address) str1 := string(address1) str2 := string(address2) if str1 != str2 { desc := fmt.Sprintf(`[%s,%s]`, str1, str2) c.addResult(iedname, ieddesc, "u", "scd.ied.Communication.S1", key, desc, net.NodeId, net.Lineno) } } else { //对比address是否有变化 if net.Address != nil && tmpNet.Address == nil { desc, _ := json.Marshal(net.Address) c.addResult(iedname, ieddesc, "i", "scd.ied.Communication.S1", key, string(desc), net.NodeId, net.Lineno) } //对比address是否有变化 if net.Address == nil && tmpNet.Address != nil { desc, _ := json.Marshal(tmpNet.Address) c.addResult(iedname, ieddesc, "d", "scd.ied.Communication.S1", key, string(desc), net.NodeId, net.Lineno) } } //GOOSE通信参数对比 sourceGse := map[string]*node_attr.NGSE{} targetGse := map[string]*node_attr.NGSE{} if len(net.GSE) > 0 { for _, item := range net.GSE { sourceGse[item.CbName] = item } } if len(tmpNet.GSE) > 0 { for _, item := range tmpNet.GSE { targetGse[item.CbName] = item } } for cbname, gse := range sourceGse { //新增的gse if tmpGse, h := targetGse[cbname]; !h { desc, _ := json.Marshal(gse) c.addResult(iedname, ieddesc, "i", "scd.ied.Communication.GSE", cbname, string(desc), gse.NodeId, gse.Lineno) } else { gse1, _ := json.Marshal(gse) gse2, _ := json.Marshal(tmpGse) str1 := string(gse1) str2 := string(gse2) if str1 != str2 { desc := fmt.Sprintf(`[%s,%s]`, str1, str2) c.addResult(iedname, ieddesc, "u", "scd.ied.Communication.GSE", cbname, desc, gse.NodeId, gse.Lineno) } } } //删除的GSE for cbname, gse := range targetGse { if _, h := sourceGse[cbname]; !h { desc, _ := json.Marshal(gse) c.addResult(iedname, ieddesc, "d", "scd.ied.Communication.GSE", cbname, string(desc), gse.NodeId, gse.Lineno) } } //SV通信参数对比 sourceSmv := map[string]*node_attr.NSMV{} targetSmv := map[string]*node_attr.NSMV{} if len(net.SMV) > 0 { for _, item := range net.SMV { sourceSmv[item.CbName] = item } } if len(tmpNet.SMV) > 0 { for _, item := range tmpNet.SMV { targetSmv[item.CbName] = item } } for cbname, gse := range sourceSmv { //新增的SMV if tmpGse, h := targetSmv[cbname]; !h { desc, _ := json.Marshal(gse) c.addResult(iedname, ieddesc, "i", "scd.ied.Communication.SMV", cbname, string(desc), gse.NodeId, gse.Lineno) } else { gse1, _ := json.Marshal(gse) gse2, _ := json.Marshal(tmpGse) str1 := string(gse1) str2 := string(gse2) if str1 != str2 { desc := fmt.Sprintf(`[%s,%s]`, str1, str2) c.addResult(iedname, ieddesc, "u", "scd.ied.Communication.SMV", cbname, (desc), gse.NodeId, gse.Lineno) } } } //删除的SMV for cbname, gse := range targetSmv { if _, h := sourceSmv[cbname]; !h { desc, _ := json.Marshal(gse) c.addResult(iedname, ieddesc, "d", "scd.ied.Communication.SMV", cbname, string(desc), gse.NodeId, gse.Lineno) } } } } //对比删除的接入点 for key, net := range targetCommu { if _, h := sourceCommu[key]; !h { desc, _ := json.Marshal(net) c.addResult(iedname, ieddesc, "d", "scd.ied.Communication", key, string(desc), net.NodeId, net.Lineno) } } } //IED回路虚端子配置及控制块、测点信息对比 func (c *ScdCompare) iedFcdaCompare(sourceScdXmlObj *node_attr.SCL, targetScdXmlObj *node_attr.SCL, iedname, ieddesc string) { //Goose及SV控制块提取 sourceGooseBlock := map[string]*node_attr.NGSEControl{} sourceSvBlock := map[string]*node_attr.NSampledValueControl{} targetGooseBlock := map[string]*node_attr.NGSEControl{} targetSvBlock := map[string]*node_attr.NSampledValueControl{} sourceIedObj := new(ScdNode).GetIed(sourceScdXmlObj, "", iedname) targetIedObj := new(ScdNode).GetIed(targetScdXmlObj, "", iedname) sourcePublishFcda := map[string]*node_attr.NFCDA{} //发布虚端子 targetPublishFcda := map[string]*node_attr.NFCDA{} //发布虚端子 sourceSubExtref := map[string]*node_attr.NExtRef{} //订阅虚端子 targetSubExtref := map[string]*node_attr.NExtRef{} //订阅虚端子 sourceReportControl := map[string]*node_attr.NReportControl{} targetReportControl := map[string]*node_attr.NReportControl{} sourceLogControl := map[string]*node_attr.NLogControl{} targetLogControl := map[string]*node_attr.NLogControl{} //比较IED内的遥控是否存在新增、删除、变动 sourceYaoKongDoiList := new(ScdMgr).GetYaoKongDOIMap(sourceScdXmlObj, iedname) targetYaoKongDoiList := new(ScdMgr).GetYaoKongDOIMap(targetScdXmlObj, iedname) //比较IED内的定值是否存在新增、删除、变动 sourceDingZhiDoiList := new(ScdMgr).GetDingZhiDOIMap(sourceScdXmlObj, iedname) targetDingZhiDoiList := new(ScdMgr).GetDingZhiDOIMap(targetScdXmlObj, iedname) //比较IED内的遥信是否存在新增、删除、变动 sourceYaoCe, sourceYaoXin, sourceYaoMai := new(ScdMgr).GetYcYkYmMap(sourceScdXmlObj, iedname) targetYaoCe, targetYaoXin, targetYaoMai := new(ScdMgr).GetYcYkYmMap(targetScdXmlObj, iedname) //数据集及成员 sourceDatasets := map[string]*node_attr.NDataSet{} targetDatasets := map[string]*node_attr.NDataSet{} for k, item := range sourceYaoKongDoiList { if tempItem, h := targetYaoKongDoiList[k]; !h { desc, _ := json.Marshal(item) c.addResult(iedname, ieddesc, "i", "scd.ied.YK", k, string(desc), item.NodeId, item.Lineno) } else { bye1, _ := json.Marshal(item) bye2, _ := json.Marshal(tempItem) str1 := string(bye1) str2 := string(bye2) if str1 != str2 { logger.Logger.Debug(fmt.Sprintf("发现遥控测点有更改,byte1:%+v byte2:%+v", bye1, bye2)) desc := fmt.Sprintf(`[%s,%s]`, (str1), (str2)) c.addResult(iedname, ieddesc, "u", "scd.ied.YK", k, desc, item.NodeId, item.Lineno) } } } for k, item := range targetYaoKongDoiList { if _, h := sourceYaoKongDoiList[k]; !h { desc, _ := json.Marshal(item) c.addResult(iedname, ieddesc, "d", "scd.ied.YK", k, string(desc), item.NodeId, item.Lineno) } } for k, item := range sourceDingZhiDoiList { if tempItem, h := targetDingZhiDoiList[k]; !h { desc, _ := json.Marshal(item) c.addResult(iedname, ieddesc, "i", "scd.ied.DZ", k, string(desc), item.NodeId, item.Lineno) } else { bye1, _ := json.Marshal(item) bye2, _ := json.Marshal(tempItem) str1 := string(bye1) str2 := string(bye2) if str1 != str2 { desc := fmt.Sprintf(`[%s,%s]`, (str1), (str2)) c.addResult(iedname, ieddesc, "u", "scd.ied.DZ", k, desc, item.NodeId, item.Lineno) } } } for k, item := range targetDingZhiDoiList { if _, h := sourceDingZhiDoiList[k]; !h { desc, _ := json.Marshal(item) c.addResult(iedname, ieddesc, "d", "scd.ied.DZ", k, string(desc), item.NodeId, item.Lineno) } } for _, ap := range sourceIedObj.AccessPoint { if ap.Server == nil { continue } for _, ld := range ap.Server.LDevice { if ld.LN0 == nil { break } for _, goose := range ld.LN0.GSEControl { sourceGooseBlock[ld.Inst+"."+goose.Name] = goose } for _, smv := range ld.LN0.SampledValueControl { sourceSvBlock[ld.Inst+"."+smv.Name] = smv } //发布虚端子列表 for _, dst := range ld.LN0.DataSet { sourceDatasets[ld.Inst+"."+dst.Name] = dst for _, fcda := range dst.FCDA { key := fmt.Sprintf("%s/%s%s%s.%s.%s", fcda.LdInst, fcda.Prefix, fcda.LnClass, fcda.LnInst, fcda.DoName, fcda.DaName) sourcePublishFcda[key] = fcda } } //订阅虚端子 if ld.LN0.Inputs != nil { for _, extref := range ld.LN0.Inputs.ExtRef { sourceSubExtref[extref.IntAddr] = extref } } //报告控制块 for _, rpb := range ld.LN0.ReportControl { sourceReportControl[ld.Inst+rpb.Name] = rpb } //日志控制块 for _, rpb := range ld.LN0.LogControl { sourceLogControl[ld.Inst+rpb.Name] = rpb } } } for _, ap := range targetIedObj.AccessPoint { if ap.Server == nil { continue } for _, ld := range ap.Server.LDevice { if ld.LN0 == nil { break } for _, goose := range ld.LN0.GSEControl { targetGooseBlock[ld.Inst+"."+goose.Name] = goose } for _, smv := range ld.LN0.SampledValueControl { targetSvBlock[ld.Inst+"."+smv.Name] = smv } //发布虚端子列表 for _, dst := range ld.LN0.DataSet { targetDatasets[ld.Inst+"."+dst.Name] = dst for _, fcda := range dst.FCDA { key := fmt.Sprintf("%s/%s%s%s.%s.%s", fcda.LdInst, fcda.Prefix, fcda.LnClass, fcda.LnInst, fcda.DoName, fcda.DaName) targetPublishFcda[key] = fcda } } //订阅虚端子 if ld.LN0.Inputs != nil { for _, extref := range ld.LN0.Inputs.ExtRef { targetSubExtref[extref.IntAddr] = extref } } //报告控制块 for _, rpb := range ld.LN0.ReportControl { targetReportControl[ld.Inst+rpb.Name] = rpb } //日志控制块 for _, rpb := range ld.LN0.LogControl { targetLogControl[ld.Inst+rpb.Name] = rpb } } } //GOOSE控制块 for blockkey, item := range sourceGooseBlock { if tmpItem, h := targetGooseBlock[blockkey]; !h { //新增了控制块 desc, _ := json.Marshal(item) c.addResult(iedname, ieddesc, "i", "scd.ied.GSEControl", blockkey, string(desc), item.NodeId, item.Lineno) } else { b1, _ := json.Marshal(item) b2, _ := json.Marshal(tmpItem) //块参数是否更改 paraSource := string(b1) paraTarget := string(b2) if paraSource != paraTarget { desc := fmt.Sprintf(`[%s,%s]`, paraSource, paraTarget) c.addResult(iedname, ieddesc, "u", "scd.ied.GSEControl", blockkey, desc, item.NodeId, item.Lineno) } } } for blockkey, item := range targetGooseBlock { if _, h := sourceGooseBlock[blockkey]; !h { //删除了控制块 desc, _ := json.Marshal(item) c.addResult(iedname, ieddesc, "d", "scd.ied.GSEControl", blockkey, string(desc), item.NodeId, item.Lineno) } } //SV控制块 for blockkey, item := range sourceSvBlock { if tmpItem, h := targetSvBlock[blockkey]; !h { //新增了控制块 desc, _ := json.Marshal(item) c.addResult(iedname, ieddesc, "i", "scd.ied.SampledValueControl", blockkey, string(desc), item.NodeId, item.Lineno) } else { b1, _ := json.Marshal(item) b2, _ := json.Marshal(tmpItem) //块参数是否更改 paraSource := string(b1) paraTarget := string(b2) if paraSource != paraTarget { desc := fmt.Sprintf(`[%s,%s]`, paraSource, paraTarget) c.addResult(iedname, ieddesc, "u", "scd.ied.SampledValueControl", blockkey, desc, item.NodeId, item.Lineno) } } } for blockkey, item := range targetGooseBlock { if _, h := sourceGooseBlock[blockkey]; !h { //删除了控制块 desc, _ := json.Marshal(item) c.addResult(iedname, ieddesc, "d", "scd.ied.SampledValueControl", blockkey, string(desc), item.NodeId, item.Lineno) } } //控制块及发布虚端子对比 for key, item := range sourcePublishFcda { //新增的发布虚端子 if _, h := targetPublishFcda[key]; !h { desc, _ := json.Marshal(item) c.addResult(iedname, ieddesc, "i", "scd.ied.FCDA", key, string(desc), item.NodeId, item.Lineno) //发布虚端子同时也是数据集成员 c.addResult(iedname, ieddesc, "i", "scd.ied.DatSetMeber", key, string(desc), item.NodeId, item.Lineno) } } for key, item := range targetPublishFcda { //删除的发布虚端子 if _, h := sourcePublishFcda[key]; !h { desc, _ := json.Marshal(item) c.addResult(iedname, ieddesc, "d", "scd.ied.FCDA", key, string(desc), item.NodeId, item.Lineno) //发布虚端子同时也是数据集成员 c.addResult(iedname, ieddesc, "d", "scd.ied.DatSetMeber", key, string(desc), item.NodeId, item.Lineno) } } //控制块及订阅虚端子对比 for key, item := range sourceSubExtref { //新增的订阅虚端子 if _, h := targetSubExtref[key]; !h { desc, _ := json.Marshal(item) c.addResult(iedname, ieddesc, "i", "scd.ied.ExtRef", key, string(desc), item.NodeId, item.Lineno) } } for key, item := range targetSubExtref { //删除的订阅虚端子 if _, h := sourceSubExtref[key]; !h { desc, _ := json.Marshal(item) c.addResult(iedname, ieddesc, "d", "scd.ied.ExtRef", key, string(desc), item.NodeId, item.Lineno) } } //报告控制块 for key, item := range sourceReportControl { if tmpItem, h := targetReportControl[key]; !h { desc, _ := json.Marshal(item) c.addResult(iedname, ieddesc, "i", "scd.ied.ReportControl", key, string(desc), item.NodeId, item.Lineno) } else { //判断是否更改 b1, _ := json.Marshal(item) b2, _ := json.Marshal(tmpItem) paraSource := string(b1) paraTarget := string(b2) if paraSource != paraTarget { desc := fmt.Sprintf(`[%s,%s]`, paraSource, paraTarget) c.addResult(iedname, ieddesc, "u", "scd.ied.ReportControl", key, desc, item.NodeId, item.Lineno) } } } for key, item := range targetReportControl { if _, h := sourceReportControl[key]; !h { desc, _ := json.Marshal(item) c.addResult(iedname, ieddesc, "d", "scd.ied.ReportControl", key, string(desc), item.NodeId, item.Lineno) } } //日志控制块 for key, item := range sourceLogControl { if tmpItem, h := targetLogControl[key]; !h { desc, _ := json.Marshal(item) c.addResult(iedname, ieddesc, "i", "scd.ied.LogControl", key, string(desc), item.NodeId, item.Lineno) } else { //判断是否更改 b1, _ := json.Marshal(item) b2, _ := json.Marshal(tmpItem) paraSource := string(b1) paraTarget := string(b2) if paraSource != paraTarget { desc := fmt.Sprintf(`[%s,%s]`, paraSource, paraTarget) c.addResult(iedname, ieddesc, "u", "scd.ied.LogControl", key, desc, item.NodeId, item.Lineno) } } } for key, item := range targetLogControl { if _, h := sourceLogControl[key]; !h { desc, _ := json.Marshal(item) c.addResult(iedname, ieddesc, "d", "scd.ied.LogControl", key, string(desc), item.NodeId, item.Lineno) } } //遥测对比 for k, item := range sourceYaoCe { if tempitem, h := targetYaoCe[k]; !h { desc, _ := json.Marshal(item) c.addResult(iedname, ieddesc, "i", "scd.ied.YC", k, string(desc), int64(0), int64(0)) } else { b1, _ := json.Marshal(item) b2, _ := json.Marshal(tempitem) str1 := string(b1) str2 := string(b2) if str1 != str2 { desc := fmt.Sprintf(`[%s,%s]`, str1, str2) c.addResult(iedname, ieddesc, "u", "scd.ied.YC", k, desc, int64(0), int64(0)) } } } for k, item := range targetYaoCe { if _, h := sourceYaoCe[k]; !h { desc, _ := json.Marshal(item) c.addResult(iedname, ieddesc, "d", "scd.ied.YC", k, string(desc), int64(0), int64(0)) } } //遥信测点对比 for k, item := range sourceYaoXin { if tempitem, h := targetYaoXin[k]; !h { desc, _ := json.Marshal(item) c.addResult(iedname, ieddesc, "i", "scd.ied.YX", k, string(desc), int64(0), int64(0)) } else { b1, _ := json.Marshal(item) b2, _ := json.Marshal(tempitem) str1 := string(b1) str2 := string(b2) if str1 != str2 { desc := fmt.Sprintf(`[%s,%s]`, str1, str2) c.addResult(iedname, ieddesc, "u", "scd.ied.YX", k, desc, int64(0), int64(0)) } } } for k, item := range targetYaoXin { if _, h := sourceYaoXin[k]; !h { desc, _ := json.Marshal(item) c.addResult(iedname, ieddesc, "d", "scd.ied.YX", k, string(desc), int64(0), int64(0)) } } //遥脉测点对比 for k, item := range sourceYaoMai { if tempitem, h := targetYaoMai[k]; !h { desc, _ := json.Marshal(item) c.addResult(iedname, ieddesc, "i", "scd.ied.YM", k, string(desc), int64(0), int64(0)) } else { b1, _ := json.Marshal(item) b2, _ := json.Marshal(tempitem) str1 := string(b1) str2 := string(b2) if str1 != str2 { desc := fmt.Sprintf(`[%s,%s]`, str1, str2) c.addResult(iedname, ieddesc, "u", "scd.ied.YM", k, desc, int64(0), int64(0)) } } } for k, item := range targetYaoMai { if _, h := sourceYaoMai[k]; !h { desc, _ := json.Marshal(item) c.addResult(iedname, ieddesc, "d", "scd.ied.YM", k, string(desc), int64(0), int64(0)) } } //数据集对比 for key, item := range sourceDatasets { if tempitem, h := targetDatasets[key]; !h { desc := fmt.Sprintf(`{"desc":"%s","name":"%s"}`, item.Desc, item.Name) c.addResult(iedname, ieddesc, "i", "scd.ied.DataSet", key, desc, item.NodeId, item.Lineno) } else { b1 := fmt.Sprintf(`{"desc":"%s","name":"%s"}`, item.Desc, item.Name) b2 := fmt.Sprintf(`{"desc":"%s","name":"%s"}`, tempitem.Desc, tempitem.Name) if b1 != b2 { desc := fmt.Sprintf(`[%s,%s]`, b1, b2) c.addResult(iedname, ieddesc, "u", "scd.ied.DataSet", key, desc, int64(0), int64(0)) } } } for k, item := range targetDatasets { if _, h := sourceDatasets[k]; !h { desc := fmt.Sprintf(`{"desc":"%s","name":"%s"}`, item.Desc, item.Name) c.addResult(iedname, ieddesc, "d", "scd.ied.DataSet", k, desc, item.NodeId, item.Lineno) } } sourceGooseBlock = nil sourceSvBlock = nil targetGooseBlock = nil targetSvBlock = nil sourceIedObj = nil targetIedObj = nil sourcePublishFcda = nil targetPublishFcda = nil sourceSubExtref = nil targetSubExtref = nil sourceReportControl = nil targetReportControl = nil sourceLogControl = nil targetLogControl = nil sourceYaoXin = nil targetYaoXin = nil sourceYaoKongDoiList = nil targetYaoKongDoiList = nil sourceYaoCe = nil targetYaoCe = nil sourceYaoMai = nil targetYaoMai = nil sourceDingZhiDoiList = nil targetDingZhiDoiList = nil } func (c *ScdCompare) addResult(iedname, ieddesc, opt, objtype, objname, desc string, nodeid, lineno int64) { detailrow := t_scd_diff_compare_detail{} detailrow.CompareId = c.CurrCompareRecord.Id detailrow.DiffDesc = desc detailrow.DiffObjectType = objtype detailrow.DiffOpt = opt detailrow.IedName = iedname detailrow.IedDesc = ieddesc detailrow.DiffObjectName = objname detailrow.DiffObjectNodeId = nodeid detailrow.ObjectLineNo = lineno c.CompareResultList = append(c.CompareResultList, detailrow) } //将对比结果批量写入数据库 func (c *ScdCompare) writeResult() { if len(c.CompareResultList) == 0 { return } db := orm.NewOrm() nodeCols := "(compare_id,diff_object_type,diff_object_node_id,diff_object_name,diff_opt,diff_desc,diff_source_node_id,source_line_no,object_line_no,ied_name,ied_desc)" loadedRec := 0 nodeSqlValuesAry := []string{} c.lock.Lock() compareId := int64(0) for _, m := range c.CompareResultList { if compareId == 0 { compareId = m.CompareId } nodeSqlValuesAry = append(nodeSqlValuesAry, fmt.Sprintf("(%d,'%s',%d,'%s','%s','%s',%d,%d,%d,'%s','%s')", m.CompareId, m.DiffObjectType, m.DiffObjectNodeId, m.DiffObjectName, m.DiffOpt, m.DiffDesc, m.DiffSourceNodeId, m.SourceLineNo, m.ObjectLineNo, m.IedName, m.IedDesc)) if len(nodeSqlValuesAry) == 1000 { sql := "insert into t_scd_diff_compare_detail" + nodeCols + "values" + strings.Join(nodeSqlValuesAry, ",") _, err := db.Raw(sql).Exec() if err != nil { logger.Logger.Error(err) } loadedRec = loadedRec + 1000 nodeSqlValuesAry = nil nodeSqlValuesAry = []string{} } } if len(nodeSqlValuesAry) > 0 { sql := "insert into t_scd_diff_compare_detail" + nodeCols + "values" + strings.Join(nodeSqlValuesAry, ",") _, err := db.Raw(sql).Exec() if err != nil { logger.Logger.Error(err, sql) } nodeSqlValuesAry = nil } c.CompareResultList = nil c.lock.Unlock() runtime.GC() }