package bo import ( "errors" "fmt" "log" "os" "runtime" "scd_check_tools/global" "scd_check_tools/logger" "scd_check_tools/models/enum" "scd_check_tools/models/node_attr" "scd_check_tools/mqtt" "scd_check_tools/tools" "strconv" "strings" "sync" "time" "github.com/astaxie/beego/orm" ) //节点规则解析管理 //单独启用专用解析进程,与节点入库操作为异步关系。该进程在没有新scd入库时为睡眠状态,当有新scd需要解析时,需要外部将其唤醒 //进程被唤醒后,定时查询节点表中未被解析的节点进行规则有效性验证,如果验证不通过,则将结果写入规则解析结果表 //每个节点状态都将按以下状态进行切换:0 未解析->1 解析中-> 2已解析 type ScdNodeRule struct { ScdID int64 ScdName string DeviceBaseModel doiMap *sync.Map daMap *sync.Map lnodeTypeMap *sync.Map // map[string]*node_attr.NLNodeType{} doTypeMap *sync.Map // map[string]*node_attr.NDOType{} daTypeMap *sync.Map // map[string]*node_attr.NDOType{} //当前未通过验证的节点列表 CheckFailList []map[string]interface{} //等待解析的队列 NodeRuleList map[string]orm.Params // NodeRuleFunctionList []orm.Params //是否正在解析节点。默认为0 isRun int parseLock sync.RWMutex checkFailListLock sync.RWMutex scdXmlObject *node_attr.SCL } type t_scd_node_rule_parse struct { Id int64 `orm:"pk"` ScdId int64 NodeId int64 RuleId int64 ParseResult string CreatedBy int CreatedTime string } //语法语义规则模型 type t_scd_scl_check struct { Id int64 `orm:"pk"` CheckName string CheckType string HintText string ApplyStandard string ApplyStandardNo string ObjectType string ObjectName string FuncName string CheckDesc string CheckArea string AlertLevel string NodePath string IsExists string IsRef string IsNotnull string DataType string MinLen string MaxLen string MinValue string MaxValue string IsuniqueByParent string IsuniqueByGlobal string ConstValue string EmunValue string RegexpValue string Enable string CreatedBy int CreatedTime string } //逻辑规则模型.(已不使用) type t_scd_node_check struct { Id int64 `orm:"pk"` CheckObject string CheckDesc string CheckArea string AlertLevel string IsExists string IsRef string IsUnique string HintText string Enable string CreatedBy int CreatedTime string } func init() { orm.RegisterModel(new(t_scd_node_rule_parse)) orm.RegisterModel(new(t_scd_node_check)) orm.RegisterModel(new(t_scd_scl_check)) } var checktopic = "/jujutong/scd_check_tools/ruleparse" func (c *ScdNodeRule) TestCheckRule() { c.scdXmlObject, _ = new(ScdParse).GetScdXmlObjectBySCDID(tools.IsEmpty(c.ScdID)) if c.scdXmlObject == nil { logger.Logger.Error(errors.New("无效的SCD")) mqtt.PublishMessage(fmt.Sprintf("%s/%d", checktopic, c.ScdID), `{"code":0,"scdid":"`+tools.IsEmpty(c.ScdID)+`","state":0,"msg":"无效的SCD"}`) return } if _, h := global.CheckingScd.Load(c.ScdID); h { logger.Logger.Debug(fmt.Sprintf("%d文件正在校验中", c.ScdID)) mqtt.PublishMessage(fmt.Sprintf("%s/%d", checktopic, c.ScdID), `{"code":0,"scdid":"`+tools.IsEmpty(c.ScdID)+`","state":0,"msg":"该SCD正在校验中"}`) return } c.doiMap = &sync.Map{} c.daMap = &sync.Map{} c.lnodeTypeMap = &sync.Map{} c.doTypeMap = &sync.Map{} c.daTypeMap = &sync.Map{} c.NodeRuleList = map[string]orm.Params{} c.NodeRuleFunctionList = []orm.Params{} db := orm.NewOrm() lst := []orm.Params{} db.Raw("select * from t_scd_scl_check where enable=1").Values(&lst) for _, r := range lst { //object_name := r["object_name"].(string) //object_type := r["object_type"].(string) checkname := tools.IsEmpty(r["check_name"]) if checkname != "" { c.NodeRuleList[checkname] = r } } mqtt.PublishMessage(fmt.Sprintf("%s/%d", checktopic, c.ScdID), `{"code":1,"scdid":"`+tools.IsEmpty(c.ScdID)+`","state":0,"msg":"SCD正在校验中"}`) global.CheckingScd.Store(c.ScdID, "1") c.StartFunctionNodeParse() } //查询规则定义列表 func (c *ScdNodeRule) GetDefList(param map[string]interface{}, pageno, pagesize int) ([]orm.Params, int, error) { db := orm.NewOrm() rule_type := param["rule_type"] sqlParams := []interface{}{} sql := "select t.*,ifnull(c.name,'其它') check_type_name from t_scd_scl_check t left join global_const_code c on t.check_type=c.id and c.parentcode='checkrule_type' where 1=1" if v1, ok := param["rule_id"]; ok { v := tools.IsEmpty(v1) if v != "" { sql = sql + " and t.id=?" sqlParams = append(sqlParams, v) } } if v1, ok := param["check_type"]; ok { v := tools.IsEmpty(v1) if v != "" { sql = sql + " and t.check_type=?" sqlParams = append(sqlParams, v) } } if v1, ok := param["check_name"]; ok { v := tools.IsEmpty(v1) if v != "" { sql = sql + " and t.check_name like ?" sqlParams = append(sqlParams, "%"+v+"%") } } if v1, ok := param["enable"]; ok { v := tools.IsEmpty(v1) if v != "" { sql = sql + " and t.enable=?" sqlParams = append(sqlParams, v) } } if v1, ok := param["object_type"]; ok { v := tools.IsEmpty(v1) if v != "" { sql = sql + " and t.object_type=?" sqlParams = append(sqlParams, v) } } if v1, ok := param["object_name"]; ok { v := tools.IsEmpty(v1) if v != "" { if rule_type == "logic" { sql = sql + " and t.check_object like ?" sqlParams = append(sqlParams, v+"%") } else { sql = sql + " and t.object_name like ?" sqlParams = append(sqlParams, v+"%") } } } if v1, ok := param["apply_standard"]; ok { v := tools.IsEmpty(v1) if v != "" { sql = sql + " and t.apply_standard like ?" sqlParams = append(sqlParams, "%"+v+"%") } } if v1, ok := param["alert_level"]; ok { v := tools.IsEmpty(v1) if v != "" { sql = sql + " and t.alert_level=?" sqlParams = append(sqlParams, v) } } limit := fmt.Sprintf(" order by t.check_type desc,t.id limit %d,%d", (pageno-1)*pagesize, pagesize) rowset := []orm.Params{} _, err := db.Raw(sql+limit, sqlParams).Values(&rowset) if err != nil { logger.Logger.Error(err, fmt.Sprintf("SQL:%s 参数:%+v", sql+limit, sqlParams)) return nil, 0, err } totalSql := "select count(1) cnt " + sql[strings.Index(sql, "from "):] tmpRowset := []orm.Params{} _, err = db.Raw(totalSql, sqlParams).Values(&tmpRowset) if err != nil { logger.Logger.Error(err, fmt.Sprintf("SQL:%s 参数:%+v", totalSql, sqlParams)) return nil, 0, err } totalCnt, _ := strconv.Atoi(tools.IsEmpty(tmpRowset[0]["cnt"])) return rowset, totalCnt, err } //删除指定的规则定义 func (c *ScdNodeRule) DeleteRuleDef(id, rule_type string) error { db := orm.NewOrm() sql := "" sql2 := "" if rule_type == "logic" { //逻辑规则 sql = "delete from t_scd_node_check t where id=?" sql2 = "delete from t_scd_node_rule_parse where rule_target='node' and rule_id=?" } else { //语法规则 sql = "delete from t_scd_scl_check t where id=?" sql2 = "delete from t_scd_node_rule_parse where rule_target='attr' and rule_id=?" } dblog := new(SystemLog) dblog.SetUserInfo(c.GetUserInfo()) dblog.Audittype = enum.AuditType_admin_rule dblog.Logtype = enum.LogType_Delete dblog.Eventtype = enum.OptEventType_Bus dblog.Eventlevel = enum.OptEventLevel_Hight _, err := db.Raw(sql, id).Exec() if err != nil { logger.Logger.Error(err) dblog.Description = fmt.Sprintf("删除scd校验规则(id=%s)失败:%s", id, err.Error()) dblog.Fail2() return err } _, err = db.Raw(sql2, id).Exec() if err != nil { logger.Logger.Error(err) dblog.Description = fmt.Sprintf("删除scd校验规则(id=%s)失败:%s", id, err.Error()) dblog.Fail2() return err } dblog.Description = fmt.Sprintf("删除scd校验规则(id=%s)成功", id) dblog.Success2() return nil } //添加新的规则定义 func (c *ScdNodeRule) AddRuleDef(param map[string]string, rule_type string) error { db := orm.NewOrm() id, _ := strconv.Atoi(param["id"]) dblog := new(SystemLog) dblog.SetUserInfo(c.GetUserInfo()) dblog.Audittype = enum.AuditType_admin_rule dblog.Logtype = enum.LogType_Insert dblog.Eventtype = enum.OptEventType_Bus dblog.Eventlevel = enum.OptEventLevel_Hight if rule_type == "logic" { //逻辑规则 mo := t_scd_node_check{} mo.CreatedBy, _ = strconv.Atoi(c.GetUserId()) mo.CreatedTime = tools.NowTime() mo.Enable = param["enable"] mo.AlertLevel = param["alert_level"] mo.CheckArea = param["check_area"] mo.CheckDesc = param["check_desc"] mo.CheckObject = param["check_object"] mo.HintText = param["hint_text"] mo.IsExists = param["is_exists"] mo.IsRef = param["is_ref"] mo.IsUnique = param["is_unique"] if id != 0 { mo.Id = int64(id) _, err := db.Update(&mo) if err != nil { dblog.Description = fmt.Sprintf("编辑scd逻辑校验规则(id=%d)失败:%s", id, err.Error()) dblog.Fail2() return err } dblog.Description = fmt.Sprintf("编辑scd逻辑校验规则(id=%d)成功", id) dblog.Success2() return nil } else { _, err := db.Insert(&mo) if err != nil { dblog.Description = fmt.Sprintf("新增scd逻辑校验规则(id=%d)失败:%s", id, err.Error()) dblog.Fail2() return err } dblog.Description = fmt.Sprintf("新增scd逻辑校验规则(id=%d)成功", id) dblog.Success2() return nil } } else { //语法规则 mo := t_scd_scl_check{} mo.FuncName = param["func_name"] mo.CheckType = tools.IsEmpty(param["check_type"], "0") mo.CheckName = param["check_name"] mo.CheckArea = param["check_area"] mo.CheckDesc = param["check_desc"] mo.HintText = param["hint_text"] mo.ApplyStandard = tools.IsEmpty(param["apply_standard"], "") mo.ApplyStandardNo = tools.IsEmpty(param["apply_standard_no"], "") mo.IsExists = tools.IsEmpty(param["is_exists"], "0") mo.IsRef = tools.IsEmpty(param["is_ref"], "0") mo.AlertLevel = param["alert_level"] mo.ConstValue = param["const_value"] mo.CreatedBy, _ = strconv.Atoi(c.GetUserId()) mo.CreatedTime = tools.NowTime() mo.DataType = param["data_type"] mo.EmunValue = param["emun_value"] mo.Enable = param["enable"] mo.IsNotnull = tools.IsEmpty(param["is_notnull"], "0") mo.IsuniqueByGlobal = tools.IsEmpty(param["isunique_by_global"], "0") mo.IsuniqueByParent = tools.IsEmpty(param["isunique_by_parent"], "0") mo.MaxLen = tools.IsEmpty(param["max_len"], "0") mo.MinLen = tools.IsEmpty(param["min_len"], "0") mo.MaxValue = tools.IsEmpty(param["max_value"], "") mo.MinValue = tools.IsEmpty(param["min_value"], "") mo.NodePath = param["node_path"] mo.ObjectName = param["object_name"] mo.ObjectType = param["object_type"] mo.RegexpValue = param["regexp_value"] syslog := new(SystemLog) syslog.SetUserInfo(c.GetUserInfo()) mo.CreatedBy, _ = strconv.Atoi(c.GetUserId()) mo.CreatedTime = tools.NowTime() if id != 0 { mo.Id = int64(id) _, err := db.Update(&mo) if err != nil { dblog.Description = fmt.Sprintf("编辑scd语法语义校验规则(id=%d)失败:%s", id, err.Error()) dblog.Fail2() //syslog.Fail("规则管理", fmt.Sprintf("编辑scd语法语义校验规则(id=%d)失败:%s", id, err.Error())) //SaveSyslog(fmt.Sprintf("编辑scd语法语义校验规则(id=%d)失败:%s", id, err.Error()), "规则管理", false, tools.IsEmpty(c.UserInfo["name"])) return err } dblog.Description = fmt.Sprintf("编辑scd语法语义校验规则(id=%d)成功", id) dblog.Fail2() return nil } else { _, err := db.Insert(&mo) if err != nil { dblog.Description = fmt.Sprintf("新增scd语法语义校验规则(id=%d)失败:%s", id, err.Error()) dblog.Fail2() return err } dblog.Description = fmt.Sprintf("新增scd语法语义校验规则(id=%d)成功", id) dblog.Success2() return nil } } } //按校验等级统计数量 func (c *ScdNodeRule) ResultStatByLevel(scdname, scdpath, ied_name, node_name, node_id string) ([]orm.Params, error) { if c.ScdID == 0 { fline := string(os.PathSeparator) fileFirstChar := scdpath[0:1] if fileFirstChar != "." { if fileFirstChar == fline { scdpath = "." + scdpath } else { scdpath = "." + fline + scdpath } } if scdname == "" && scdpath == "" { return nil, errors.New("scd名称和路径不能为空") } db := orm.NewOrm() sql := "select id from t_scd_scl where scd_name=? and path=? limit 0,1" tmpRowset := []orm.Params{} db.Raw(sql, scdname, scdpath).Values(&tmpRowset) if len(tmpRowset) == 0 { return nil, errors.New("无效的scd名称和路径") } cScdID, _ := strconv.Atoi(tools.IsEmpty(tmpRowset[0]["id"])) c.ScdID = int64(cScdID) } where := "" param := []interface{}{} param = append(param, c.ScdID) if ied_name != "" { where += " and t.ied_name=?" param = append(param, ied_name) } if node_name != "" { if node_name == "area" { //间隔 where += " and EXISTS(select 1 from t_area_ied_relation a where a.ied_name=t.ied_name and a.area_id=? )" param = append(param, node_id) } else if node_name == "voltage_level" { //电压等级 where += " and EXISTS(select 1 from t_substation_area v1, t_area_ied_relation v2 where v1.id=v2.area_id and v2.ied_name=t.ied_name and v1.voltage_level=? and v1.scd_id=? )" param = append(param, node_id) param = append(param, c.ScdID) } else if node_name == "Communication" { //通讯类 where += " and t1.object_name=?" param = append(param, "Communication") } else if node_name == "DataTypeTemplates" { //通讯类 where += " and t1.object_name=?" param = append(param, "DataTypeTemplates") } else if node_name == "SCLSyntax" { //语法类 where += " and t1.object_name=?" param = append(param, "SCLSyntax") } } db := orm.NewOrm() sql := `select t1.alert_level, count(0) cnt from t_scd_scl_check t1 ,t_scd_node_rule_parse t where t.scd_id=? ` + where + ` and t.rule_id=t1.id GROUP BY alert_level` rowset := []orm.Params{} _, err := db.Raw(sql, param).Values(&rowset) if err != nil { logger.Logger.Error(err, fmt.Sprintf("SQL:%s 参数:%+v", sql, c.ScdID)) } return rowset, err } //汇总统计校验结果数量 func (c *ScdNodeRule) SumCheckResult() ([]orm.Params, error) { db := orm.NewOrm() sql := `SELECT t1.object_name, t1.alert_level ,a.ied_name from t_scd_node_rule_parse a ,t_scd_scl_check t1 where a.rule_id=t1.id and a.scd_id=? GROUP BY t1.object_name,a.ied_name ,t1.alert_level` rowset := []orm.Params{} _, err := db.Raw(sql, c.ScdID).Values(&rowset) if err != nil { logger.Logger.Error(err, fmt.Sprintf("SQL:%s 参数:%+v", sql, []interface{}{c.ScdID, c.ScdID, c.ScdID})) return rowset, err } return rowset, nil } //统计指定站当前scd的正确率 func (c *ScdNodeRule) RightRateStat(stationid string) (orm.Params, error) { db := orm.NewOrm() sql := `select a.* from ( select t1.node_cnt nodetotal, max(t2.id) station_id,max(t2.AREA_NAME) station_name,max(t1.id) scd_id, count(t.id) cnt from t_scd_node_rule_parse t,t_scd_scl t1,t_data_area t2 where t.scd_id=t1.id and t1.station_id=t2.id and t1.station_id=? and t1.version like '在运版%') a ` rowset := []orm.Params{} _, err := db.Raw(sql, stationid).Values(&rowset) if err != nil { logger.Logger.Error(err, fmt.Sprintf("SQL:%s 参数:%+v", sql, stationid)) return nil, err } if len(rowset) == 0 { return nil, nil } return rowset[0], nil } func (c *ScdNodeRule) ExportData(level, ied_name string) ([]orm.Params, error) { sql := "select t.*,g.alert_level,g.apply_standard,g.apply_standard_no from t_scd_node_rule_parse t inner join t_scd_scl_check g on t.rule_id=g.id where t.scd_id=? and t.rule_target='attr' " param := []interface{}{} param = append(param, c.ScdID) if level != "" { sql += " and g.alert_level=? " param = append(param, level) } if ied_name != "" { sql += " and t.ied_name=?" param = append(param, ied_name) } limit := fmt.Sprintf(" order by id ") rowset := []orm.Params{} db := orm.NewOrm() _, err := db.Raw(sql+limit, param).Values(&rowset) if err != nil { logger.Logger.Error(err, fmt.Sprintf("SQL:%s 参数:%+v", sql+limit, param)) } return rowset, err } //查询校验规则列表 //level 规则检查结果级别 为空时查询所有的结果 func (c *ScdNodeRule) ResultList(scdname, scdpath string, level, ied_name, node_name, node_id string, pageno, pagesize int) ([]orm.Params, int, error) { if c.ScdID == 0 { fline := string(os.PathSeparator) fileFirstChar := scdpath[0:1] if fileFirstChar != "." { if fileFirstChar == fline { scdpath = "." + scdpath } else { scdpath = "." + fline + scdpath } } if scdname == "" && scdpath == "" { return nil, 0, errors.New("scd名称和路径不能为空") } db := orm.NewOrm() sql := "select id from t_scd_scl where scd_name=? and path=? limit 0,1" tmpRowset := []orm.Params{} db.Raw(sql, scdname, scdpath).Values(&tmpRowset) if len(tmpRowset) == 0 { return nil, 0, errors.New("无效的scd名称和路径") } cScdID, _ := strconv.Atoi(tools.IsEmpty(tmpRowset[0]["id"])) c.ScdID = int64(cScdID) } sql := "select t.*,g.alert_level,g.apply_standard,g.apply_standard_no from t_scd_node_rule_parse t inner join t_scd_scl_check g on t.rule_id=g.id where t.scd_id=? " where := "" param := []interface{}{} param = append(param, c.ScdID) if level != "" { where += " and g.alert_level=? " param = append(param, level) } if ied_name != "" { where += " and t.ied_name=?" param = append(param, ied_name) } if node_name != "" { if node_name == "area" { //间隔 where += " and EXISTS(select 1 from t_area_ied_relation a where a.ied_name=t.ied_name and a.area_id=? )" param = append(param, node_id) } else if node_name == "voltage_level" { //电压等级 where += " and EXISTS(select 1 from t_substation_area v1, t_area_ied_relation v2 where v1.id=v2.area_id and v2.ied_name=t.ied_name and v1.voltage_level=? and v1.scd_id=? )" param = append(param, node_id) param = append(param, c.ScdID) } else if node_name == "Communication" { //通讯类 where += " and g.object_name=?" param = append(param, "Communication") } else if node_name == "DataTypeTemplates" { //通讯类 where += " and g.object_name=?" param = append(param, "DataTypeTemplates") } else if node_name == "SCLSyntax" { //语法类 where += " and g.object_name=?" param = append(param, "SCLSyntax") } } sql += where limit := fmt.Sprintf(" order by g.alert_level limit %d,%d", (pageno-1)*pagesize, pagesize) rowset := []orm.Params{} db := orm.NewOrm() _, err := db.Raw(sql+limit, param).Values(&rowset) if err != nil { logger.Logger.Error(err, fmt.Sprintf("SQL:%s 参数:%+v", sql+limit, param)) return rowset, 0, err } totalNum := 0 if err == nil { tmpRowset := []orm.Params{} _, err = db.Raw(strings.ReplaceAll(sql, "t.*,g.alert_level", "count(1) cnt"), param).Values(&tmpRowset) if err != nil { logger.Logger.Error(err, fmt.Sprintf("SQL:%s 参数:%+v", sql, param)) } else if len(tmpRowset) > 0 { for _, r1 := range tmpRowset { totalNumi, _ := strconv.Atoi(tools.IsEmpty(r1["cnt"])) totalNum += totalNumi } } } return rowset, totalNum, err } //启动自定义校验 func (c *ScdNodeRule) StartFunctionNodeParse() { new(TaskMgr).SetStep(tools.IsEmpty(c.ScdID), enum.TaskStep_SCD_rule_parse.Code(), 1) c.CheckFunc_header_number(nil) c.CheckFunc_header_name_structure(nil) c.CheckFunc_header_toolid(nil) c.CheckFunc_header_complete(nil) c.CheckFunc_header_version(nil) c.CheckFunc_header_hitem_complete(nil) c.CheckFunc_substation_number(nil) c.CheckFunc_substation_name(nil) c.CheckFunc_voltagelevel_complete(nil) c.CheckFunc_communication_connectedap(nil) dATypeMap := c.CheckFunc_datatypetemplates(nil) c.CheckFunc_ied(dATypeMap) new(TaskMgr).SetStep(tools.IsEmpty(c.ScdID), enum.TaskStep_SCD_rule_parse.Code(), 2) //在crc校验完成后将所有校验结果写入数据库 go c.CheckFunc_crc() c.doiMap = nil c.daMap = nil } //----------------------自定义的规则校验方法------------------ func (c *ScdNodeRule) CheckFunc_header_number(para orm.Params) { ruleid := c.getRuleIdByName("Header元素完备性校验") if ruleid != "" { if c.scdXmlObject.Header == nil { parse_result := fmt.Sprintf("Header元素缺失") r := map[string]interface{}{"scdid": c.ScdID, "lineno": c.scdXmlObject.Lineno, "ruleid": ruleid, "nodeid": c.scdXmlObject.NodeId, "parse_result": parse_result} c.AppendPaseResult(r) } } } func (c *ScdNodeRule) CheckFunc_header_name_structure(para orm.Params) { ruleid := c.getRuleIdByName("Header元素完备性校验") if ruleid != "" { if c.scdXmlObject.Header != nil && c.scdXmlObject.Header.NameStructure != "IEDName" { parse_result := fmt.Sprintf("Header元素中的属性nameStructure值不正确,其值只能为IEDName") r := map[string]interface{}{"scdid": c.ScdID, "lineno": c.scdXmlObject.Header.Lineno, "ruleid": ruleid, "nodeid": c.scdXmlObject.Header.NodeId, "parse_result": parse_result} //唯一性检查未通过 c.AppendPaseResult(r) } } } func (c *ScdNodeRule) CheckFunc_header_toolid(para orm.Params) { ruleid := c.getRuleIdByName("toolID属性检查") if ruleid != "" { if c.scdXmlObject.Header != nil && c.scdXmlObject.Header.ToolID == "" { parse_result := fmt.Sprintf("Header元素中的属性toolID配置不规范,其值为空") r := map[string]interface{}{"scdid": c.ScdID, "lineno": c.scdXmlObject.Header.Lineno, "ruleid": ruleid, "nodeid": c.scdXmlObject.Header.NodeId, "parse_result": parse_result} //唯一性检查未通过 c.AppendPaseResult(r) } if c.scdXmlObject.Header != nil && c.scdXmlObject.Header.ToolID != "" { ary := strings.Split(c.scdXmlObject.Header.ToolID, "_") if len(ary) != 3 { parse_result := fmt.Sprintf("Header元素属性toolID配置不规范,格式应为:厂商编码_配置工具名称_工具软件版本") r := map[string]interface{}{"scdid": c.ScdID, "lineno": c.scdXmlObject.Header.Lineno, "ruleid": ruleid, "nodeid": c.scdXmlObject.Header.NodeId, "parse_result": parse_result} //唯一性检查未通过 c.AppendPaseResult(r) } } } } //完整性校验 func (c *ScdNodeRule) CheckFunc_header_complete(para orm.Params) { if c.scdXmlObject.Header == nil { return } ruleid := c.getRuleIdByName("Header元素完备性校验") if ruleid != "" { if c.scdXmlObject.Header.Id == "" { //验证不通过 parse_result := fmt.Sprintf("Header元素中id属性缺失") r := map[string]interface{}{"scdid": c.ScdID, "lineno": c.scdXmlObject.Lineno, "ruleid": ruleid, "nodeid": c.scdXmlObject.Header.NodeId, "parse_result": parse_result} //唯一性检查未通过 c.AppendPaseResult(r) } if c.scdXmlObject.Header.Version == "" { //验证不通过 parse_result := fmt.Sprintf("Header元素中version属性缺失") r := map[string]interface{}{"scdid": c.ScdID, "lineno": c.scdXmlObject.Header.Lineno, "ruleid": ruleid, "nodeid": c.scdXmlObject.Header.NodeId, "parse_result": parse_result} //唯一性检查未通过 c.AppendPaseResult(r) } if c.scdXmlObject.Header.Revision == "" { //验证不通过 parse_result := fmt.Sprintf("Header元素中revision属性缺失") r := map[string]interface{}{"scdid": c.ScdID, "lineno": c.scdXmlObject.Header.Lineno, "ruleid": ruleid, "nodeid": c.scdXmlObject.Header.NodeId, "parse_result": parse_result} //唯一性检查未通过 c.AppendPaseResult(r) } } } func (c *ScdNodeRule) CheckFunc_header_version(para orm.Params) { if c.scdXmlObject.Header == nil { return } ruleid := c.getRuleIdByName("Header版本一致性") if ruleid != "" { ver := c.scdXmlObject.Header.Version rever := c.scdXmlObject.Header.Revision if c.scdXmlObject.Header.History != nil { hitems := c.scdXmlObject.Header.History.Hitem if len(hitems) > 0 { lastHitem := hitems[len(hitems)-1] if ver != lastHitem.Version && rever != lastHitem.Revision { //验证不通过 parse_result := fmt.Sprintf("Header中的version=%s、revision=%s与History中的最后一个Item不一致", ver, rever) r := map[string]interface{}{"scdid": c.ScdID, "lineno": lastHitem.Lineno, "ruleid": ruleid, "nodeid": c.scdXmlObject.Header.NodeId, "parse_result": parse_result} //唯一性检查未通过 c.AppendPaseResult(r) } } } } } func (c *ScdNodeRule) CheckFunc_header_hitem_complete(para orm.Params) { if c.scdXmlObject.Header == nil { return } if c.scdXmlObject.Header.History != nil { hitems := c.scdXmlObject.Header.History.Hitem if len(hitems) > 0 { ver := "" rever := "" hitemruleid1 := c.getRuleIdByName("Hitem元素完备性校验") hitemruleid2 := c.getRuleIdByName("History版本连续性") for _, hitem := range hitems { if hitemruleid1 != "" { errattr := []string{} if hitem.Version == "" { errattr = append(errattr, "version") } if hitem.Revision == "" { errattr = append(errattr, "revision") } if hitem.When == "" { errattr = append(errattr, "when") } if len(errattr) > 0 { //验证不通过 parse_result := fmt.Sprintf("Hitem元素中%s属性缺失", strings.Join(errattr, ",")) r := map[string]interface{}{"scdid": c.ScdID, "lineno": hitem.Lineno, "ruleid": hitemruleid1, "nodeid": hitem.NodeId, "parse_result": parse_result} c.AppendPaseResult(r) } } if hitemruleid2 != "" { //连续性判断 if ver == "" { ver = hitem.Version rever = hitem.Revision } else { v1, _ := strconv.Atoi(hitem.Version) v2, _ := strconv.Atoi(hitem.Revision) v3, _ := strconv.Atoi(ver) v4, _ := strconv.Atoi(rever) if v1 == v3 && int(float64(v2)-float64(v4)-0.1) != 0 { //验证不通过 parse_result := fmt.Sprintf("History版本元素中版本信息不连续:version=%s,revison=%s->version=%s,revison=%s", ver, rever, hitem.Version, hitem.Revision) r := map[string]interface{}{"scdid": c.ScdID, "lineno": hitem.Lineno, "ruleid": hitemruleid2, "nodeid": hitem.NodeId, "parse_result": parse_result} c.AppendPaseResult(r) } if v1 != v3 && hitem.Revision != "1.0" { //验证不通过 parse_result := fmt.Sprintf("History版本元素中版本信息不规范:文件版本增加时,文件修订版本应置为1.0") r := map[string]interface{}{"scdid": c.ScdID, "lineno": hitem.Lineno, "ruleid": hitemruleid2, "nodeid": hitem.NodeId, "parse_result": parse_result} c.AppendPaseResult(r) } ver = hitem.Version rever = hitem.Revision } } } } } } func (c *ScdNodeRule) CheckFunc_substation_number(para orm.Params) { ruleid := c.getRuleIdByName("Substation完备性校验") if ruleid != "" { if c.scdXmlObject.Substation == nil { //验证不通过 parse_result := fmt.Sprintf("Substation元素缺失") r := map[string]interface{}{"scdid": c.ScdID, "lineno": c.scdXmlObject.Lineno, "ruleid": ruleid, "nodeid": c.scdXmlObject.NodeId, "parse_result": parse_result} c.AppendPaseResult(r) return } } ruleid = c.getRuleIdByName("Substation对象缺失CIME-dtype元素") ruleid2 := c.getRuleIdByName("Substation对象缺失CIME-area元素") if ruleid != "" || ruleid2 != "" { if len(c.scdXmlObject.Substation.Private) == 0 { parse_result := fmt.Sprintf("Substation对象缺失CIME-dtype元素") r := map[string]interface{}{"scdid": c.ScdID, "lineno": c.scdXmlObject.Substation.Lineno, "ruleid": ruleid, "nodeid": c.scdXmlObject.Substation.NodeId, "parse_result": parse_result} c.AppendPaseResult(r) return } isFound := false isFoundCIMEArea := false for _, pr := range c.scdXmlObject.Substation.Private { if pr.Type == "CIME-dtype" { isFound = true ruleid3 := c.getRuleIdByName("Substation对象对应CIME-dtype中属性缺失") if ruleid3 != "" && (pr.Desc == "") { parse_result := fmt.Sprintf("Substation对象对应CIME-dtype中属性(desc)缺失") r := map[string]interface{}{"scdid": c.ScdID, "lineno": pr.Lineno, "ruleid": ruleid3, "nodeid": c.scdXmlObject.Substation.NodeId, "parse_result": parse_result} c.AppendPaseResult(r) } } if pr.Type == "CIME-area" { isFoundCIMEArea = true ruleid3 := c.getRuleIdByName("Substation对象对应CIME-area中属性缺失") if ruleid3 != "" && (pr.Name == "" || pr.Desc == "") { parse_result := fmt.Sprintf("Substation对象对应CIME-area中属性(name或desc)缺失") r := map[string]interface{}{"scdid": c.ScdID, "lineno": pr.Lineno, "ruleid": ruleid3, "nodeid": c.scdXmlObject.Substation.NodeId, "parse_result": parse_result} c.AppendPaseResult(r) } } } if !isFound { parse_result := fmt.Sprintf("Substation对象缺失CIME-dtype元素") r := map[string]interface{}{"scdid": c.ScdID, "lineno": c.scdXmlObject.Substation.Lineno, "ruleid": ruleid, "nodeid": c.scdXmlObject.Substation.NodeId, "parse_result": parse_result} c.AppendPaseResult(r) } if !isFoundCIMEArea { parse_result := fmt.Sprintf("Substation对象缺失CIME-area元素") r := map[string]interface{}{"scdid": c.ScdID, "lineno": c.scdXmlObject.Substation.Lineno, "ruleid": ruleid2, "nodeid": c.scdXmlObject.Substation.NodeId, "parse_result": parse_result} c.AppendPaseResult(r) } } } func (c *ScdNodeRule) CheckFunc_substation_name(para orm.Params) { if c.scdXmlObject.Substation == nil { return } ruleid := c.getRuleIdByName("变电站名称一致性") if ruleid != "" { if c.scdXmlObject.Substation.Name != c.scdXmlObject.Header.Id { //验证不通过 parse_result := fmt.Sprintf("Substation的name(%s)与Header中的id(%s)不一致", c.scdXmlObject.Substation.Name, c.scdXmlObject.Header.Id) r := map[string]interface{}{"scdid": c.ScdID, "lineno": c.scdXmlObject.Substation.Lineno, "ruleid": ruleid, "nodeid": c.scdXmlObject.Substation.NodeId, "parse_result": parse_result} c.AppendPaseResult(r) } } } //VoltageLevel 对象的命名 name 应按“‘额定电压’ kV”形式命名,如“ 1000kV”“110kV”等,并且全站唯一。 desc 描述参照 name 命名方式 func (c *ScdNodeRule) CheckFunc_voltagelevel_complete(para orm.Params) { if c.scdXmlObject.Substation == nil { return } ruleid1 := c.getRuleIdByName("VoltageLevel命名错误") ruleid2 := c.getRuleIdByName("VoltageLevel下Voltage缺失") ruleid3 := c.getRuleIdByName("Voltage对象的取值/属性错误") ruleid4 := c.getRuleIdByName("SSD关联关系错误") if len(c.scdXmlObject.Substation.VoltageLevel) == 0 { parse_result := fmt.Sprintf("Substation中存在层级关系错误:未定义VoltageLevel") r := map[string]interface{}{"scdid": c.ScdID, "lineno": c.scdXmlObject.Substation.Lineno, "ruleid": ruleid4, "nodeid": c.scdXmlObject.Substation.NodeId, "parse_result": parse_result} c.AppendPaseResult(r) return } priMap := map[string]string{} for _, item := range c.scdXmlObject.Substation.VoltageLevel { name := item.Name //desc := item.Desc if ruleid1 != "" { if _, h := priMap[name]; h { //验证不通过 parse_result := fmt.Sprintf("VoltageLevel命名错误:name应该全站唯一") r := map[string]interface{}{"scdid": c.ScdID, "lineno": item.Lineno, "ruleid": ruleid1, "nodeid": item.NodeId, "parse_result": parse_result} c.AppendPaseResult(r) continue } priMap[name] = "" if name == "" || len(name) < 3 || !strings.HasSuffix(name, "kV") { //验证不通过 parse_result := fmt.Sprintf("VoltageLevel命名错误:name应按“‘额定电压’ kV”形式命名,如“1000kV”“110kV”等,并且全站唯一") r := map[string]interface{}{"scdid": c.ScdID, "lineno": item.Lineno, "ruleid": ruleid1, "nodeid": item.NodeId, "parse_result": parse_result} c.AppendPaseResult(r) } } if ruleid2 != "" { if item.Voltage == nil { //验证不通过 parse_result := fmt.Sprintf("VoltageLevel(%s)下缺失Voltage对象", item.Name) r := map[string]interface{}{"scdid": c.ScdID, "lineno": item.Lineno, "ruleid": ruleid2, "nodeid": item.NodeId, "parse_result": parse_result} c.AppendPaseResult(r) } } if ruleid3 != "" && item.Voltage != nil { if item.Voltage.InnerText != item.Name[0:len(item.Name)-2] { //验证不通过 parse_result := fmt.Sprintf("VoltageLevel(%s)下属Voltage对象的取值错误", item.Name) r := map[string]interface{}{"scdid": c.ScdID, "lineno": item.Voltage.Lineno, "ruleid": ruleid3, "nodeid": item.NodeId, "parse_result": parse_result} c.AppendPaseResult(r) } if item.Voltage.Multiplier != "k" { //验证不通过 parse_result := fmt.Sprintf("VoltageLevel(%s)下属Voltage对象属性(%s=%s)错误", item.Name, "multiplier", item.Voltage.Multiplier) r := map[string]interface{}{"scdid": c.ScdID, "lineno": item.Voltage.Lineno, "ruleid": ruleid3, "nodeid": item.NodeId, "parse_result": parse_result} c.AppendPaseResult(r) } if item.Voltage.Unit != "V" { //验证不通过 parse_result := fmt.Sprintf("VoltageLevel(%s)下属Voltage对象属性(%s=%s)错误", item.Name, "unit", item.Voltage.Unit) r := map[string]interface{}{"scdid": c.ScdID, "lineno": item.Voltage.Lineno, "ruleid": ruleid3, "nodeid": item.NodeId, "parse_result": parse_result} c.AppendPaseResult(r) } } //Bay校验 if len(item.Bay) > 0 { ruleid5 := c.getRuleIdByName("Bay对象缺失CIME-dtype元素") ruleid6 := c.getRuleIdByName("Bay对象对应CIME-dtype中desc属性值错误") if ruleid5 != "" { for _, bayrow := range item.Bay { if bayrow.Private == nil { parse_result := fmt.Sprintf("Bay对象缺失CIME-dtype元素") r := map[string]interface{}{"scdid": c.ScdID, "lineno": bayrow.Lineno, "ruleid": ruleid5, "nodeid": c.scdXmlObject.Substation.NodeId, "parse_result": parse_result} c.AppendPaseResult(r) } else { if ruleid6 != "" && bayrow.Private.Desc == "" { parse_result := fmt.Sprintf("Bay对象对应CIME-dtype中desc属性值错误") r := map[string]interface{}{"scdid": c.ScdID, "lineno": bayrow.Lineno, "ruleid": ruleid6, "nodeid": c.scdXmlObject.Substation.NodeId, "parse_result": parse_result} c.AppendPaseResult(r) } } bayRule1 := c.getRuleIdByName("间隔关联逻辑节点不存在") if len(bayrow.PowerTransformer) > 0 { ruleid7 := c.getRuleIdByName("PowerTransformer对象命名错误") ruleid8 := c.getRuleIdByName("PowerTransformer对象type错误") for _, tmpr := range bayrow.PowerTransformer { if ruleid7 != "" && (tmpr.Name == "" || !strings.HasPrefix(tmpr.Name, "PTR")) { parse_result := fmt.Sprintf("PowerTransformer对象命名(%s)错误", tmpr.Name) r := map[string]interface{}{"scdid": c.ScdID, "lineno": tmpr.Lineno, "ruleid": ruleid7, "nodeid": c.scdXmlObject.Substation.NodeId, "parse_result": parse_result} c.AppendPaseResult(r) } if ruleid8 != "" && tmpr.Type != "PTR" { parse_result := fmt.Sprintf("PowerTransformer对象(%s)type(%s)不为PTR", tmpr.Name, tmpr.Type) r := map[string]interface{}{"scdid": c.ScdID, "lineno": tmpr.Lineno, "ruleid": ruleid8, "nodeid": c.scdXmlObject.Substation.NodeId, "parse_result": parse_result} c.AppendPaseResult(r) } if len(tmpr.TransformerWinding) > 0 { ruleid9 := c.getRuleIdByName("TransformerWinding对象缺失:CIME-voltageLevel") ruleid10 := c.getRuleIdByName("TransformerWinding对象中CIME-voltageLevel属性错误或缺失") for _, rw := range tmpr.TransformerWinding { if ruleid9 != "" && (rw.Private != nil || rw.Private.Type != "CIME-voltageLevel") { parse_result := fmt.Sprintf("TransformerWinding对象(%s)缺失:CIME-voltageLevel", rw.Name) r := map[string]interface{}{"scdid": c.ScdID, "lineno": rw.Lineno, "ruleid": ruleid9, "nodeid": c.scdXmlObject.Substation.NodeId, "parse_result": parse_result} c.AppendPaseResult(r) } if ruleid10 != "" && rw.Private != nil && rw.Private.Desc == "" { parse_result := fmt.Sprintf("TransformerWinding对象(%s)中的CIME-voltageLevel的desc属性缺失", rw.Desc) r := map[string]interface{}{"scdid": c.ScdID, "lineno": rw.Lineno, "ruleid": ruleid10, "nodeid": c.scdXmlObject.Substation.NodeId, "parse_result": parse_result} c.AppendPaseResult(r) } if ruleid10 != "" && rw.Private != nil && rw.Private.Name == "" { parse_result := fmt.Sprintf("TransformerWinding对象(%s)中的CIME-voltageLevel的name属性缺失", rw.Name) r := map[string]interface{}{"scdid": c.ScdID, "lineno": rw.Lineno, "ruleid": ruleid10, "nodeid": c.scdXmlObject.Substation.NodeId, "parse_result": parse_result} c.AppendPaseResult(r) } if ruleid10 != "" && rw.Private != nil && rw.Private.Name != item.Name { parse_result := fmt.Sprintf("TransformerWinding对象(%s)中的CIME-voltageLevel的name属性(%s)与已有电压等级(%s)不一致", rw.Name, rw.Private.Name, item.Name) r := map[string]interface{}{"scdid": c.ScdID, "lineno": rw.Lineno, "ruleid": ruleid10, "nodeid": c.scdXmlObject.Substation.NodeId, "parse_result": parse_result} c.AppendPaseResult(r) } } } if bayRule1 != "" && len(tmpr.LNode) > 0 { scdnode := new(ScdNode) for _, i1 := range tmpr.LNode { if scdnode.GetIed(c.scdXmlObject, tools.IsEmpty(c.ScdID), i1.IedName) == nil { parse_result := fmt.Sprintf("间隔(%s)关联逻辑节点(%s)不存在", bayrow.Name, i1.IedName) r := map[string]interface{}{"scdid": c.ScdID, "lineno": i1.Lineno, "ruleid": bayRule1, "nodeid": c.scdXmlObject.Substation.NodeId, "parse_result": parse_result} c.AppendPaseResult(r) } } } } } if len(bayrow.ConductingEquipment) > 0 { ruleid7 := c.getRuleIdByName("ConductingEquipment命名错误") condmap := map[string]int{} for _, r := range bayrow.ConductingEquipment { if ruleid7 != "" { if r.Name == "" { parse_result := fmt.Sprintf("ConductingEquipment命名错误:name不能为空") rt := map[string]interface{}{"scdid": c.ScdID, "lineno": r.Lineno, "ruleid": ruleid7, "nodeid": c.scdXmlObject.Substation.NodeId, "parse_result": parse_result} c.AppendPaseResult(rt) continue } if condmap[r.Name] == 1 { parse_result := fmt.Sprintf("在同一Bay内不应有两个同名的ConductingEquipment(%s)元素", r.Name) rt := map[string]interface{}{"scdid": c.ScdID, "lineno": r.Lineno, "ruleid": ruleid7, "nodeid": c.scdXmlObject.Substation.NodeId, "parse_result": parse_result} c.AppendPaseResult(rt) continue } condmap[r.Name] = 1 } ruleid8 := c.getRuleIdByName("ConductingEquipment对象缺失CIME-dtype元素") if ruleid8 != "" && (r.Private == nil || r.Private.Type != "CIME-dtype") { parse_result := fmt.Sprintf("ConductingEquipment对象(%s)缺失CIME-dtype元素", r.Name) rt := map[string]interface{}{"scdid": c.ScdID, "lineno": r.Private.Lineno, "ruleid": ruleid8, "nodeid": c.scdXmlObject.Substation.NodeId, "parse_result": parse_result} c.AppendPaseResult(rt) } ruleid9 := c.getRuleIdByName("ConductingEquipment对象对应CIME-dtype中desc属性值错误") if ruleid9 != "" && r.Private != nil && r.Private.Type == "CIME-dtype" { if r.Private.Desc == "" { parse_result := fmt.Sprintf("ConductingEquipment对象(%s)对应CIME-dtype元素中desc属性值错误", r.Name) rt := map[string]interface{}{"scdid": c.ScdID, "lineno": r.Private.Lineno, "ruleid": ruleid9, "nodeid": c.scdXmlObject.Substation.NodeId, "parse_result": parse_result} c.AppendPaseResult(rt) } } if bayRule1 != "" && len(r.LNode) > 0 { scdnode := new(ScdNode) for _, i1 := range r.LNode { if scdnode.GetIed(c.scdXmlObject, tools.IsEmpty(c.ScdID), i1.IedName) == nil { parse_result := fmt.Sprintf("间隔(%s)关联逻辑节点(%s)不存在", bayrow.Name, i1.IedName) rt := map[string]interface{}{"scdid": c.ScdID, "lineno": i1.Lineno, "ruleid": bayRule1, "nodeid": c.scdXmlObject.Substation.NodeId, "parse_result": parse_result} c.AppendPaseResult(rt) } } } } } if len(bayrow.ConnectivityNode) > 0 { ruleid10 := c.getRuleIdByName("ConnectivityNode对象命名错误") if ruleid10 != "" { for _, r := range bayrow.ConnectivityNode { if r.Name == "" { parse_result := fmt.Sprintf("ConnectivityNode对象命名错误:不能为空") rt := map[string]interface{}{"scdid": c.ScdID, "lineno": r.Lineno, "ruleid": ruleid10, "nodeid": c.scdXmlObject.Substation.NodeId, "parse_result": parse_result} c.AppendPaseResult(rt) continue } if r.Name[0:1] != "C" { parse_result := fmt.Sprintf("ConnectivityNode对象(%s)命名不符合规范:Cn进行命名(n)为间隔内ConnectivityNode实例的序号", r.Name) rt := map[string]interface{}{"scdid": c.ScdID, "lineno": r.Lineno, "ruleid": ruleid10, "nodeid": c.scdXmlObject.Substation.NodeId, "parse_result": parse_result} c.AppendPaseResult(rt) } else { no := r.Name[1:] _, er := strconv.Atoi(no) if er != nil { parse_result := fmt.Sprintf("ConnectivityNode对象(%s)命名不符合规范:Cn进行命名(n)为间隔内ConnectivityNode实例的序号", r.Name) rt := map[string]interface{}{"scdid": c.ScdID, "lineno": r.Lineno, "ruleid": ruleid10, "nodeid": c.scdXmlObject.Substation.NodeId, "parse_result": parse_result} c.AppendPaseResult(rt) } } } } } } } } } } //主要校验以下规则 //访问点命名一致性校验:〈Communication)下的apName届性值是否指向已存在的TED访问点 //IED命名一致性校验:〈Communication)下(ConnectedAP>的iedName属性值是否指向己存在的IED //GSE命名一致性校验:〈Communication)下的cbName、Idlnst属性值是否指向已存在的GOOSE控制块 //SMV命名一致性校验:〈Communication)下的cbName、Idlnst属性值是否指向已存在的SMV控制块 //ConnectedAP唯一性校验:ConnectedAP 不应重复配置 // func (c *ScdNodeRule) CheckFunc_communication_connectedap(para orm.Params) { logger.Logger.Debug(fmt.Sprintf("校验SCD %d的通信节点", c.ScdID)) if c.scdXmlObject.Communication == nil { //验证不通过 parse_result := fmt.Sprintf("Communication未定义") r := map[string]interface{}{"scdid": c.ScdID, "lineno": c.scdXmlObject.Lineno, "ruleid": c.getRuleIdByName("Communication元素完备性校验"), "nodeid": c.scdXmlObject.NodeId, "parse_result": parse_result} c.AppendPaseResult(r) return } if len(c.scdXmlObject.Communication.SubNetwork) == 0 { //验证不通过 parse_result := fmt.Sprintf("SubNetwork未定义") r := map[string]interface{}{"scdid": c.ScdID, "lineno": c.scdXmlObject.Communication.Lineno, "ruleid": c.getRuleIdByName("SubNetwork元素完备性校验"), "nodeid": c.scdXmlObject.NodeId, "parse_result": parse_result} c.AppendPaseResult(r) return } gseP := map[string]string{} smvP := map[string]string{} ipAddressIp := map[string]string{} gseAppidMap := map[string]string{} for _, subnet := range c.scdXmlObject.Communication.SubNetwork { apPri := map[string]int{} for _, apitem := range subnet.ConnectedAP { if apPri[apitem.ApName+apitem.IedName] == 1 { parse_result := fmt.Sprintf("%s子网下ConnectedAP(apName=%s,iedName=%s)重复配置", subnet.Name, apitem.ApName, apitem.IedName) r := map[string]interface{}{"scdid": c.ScdID, "lineno": apitem.Lineno, "ruleid": c.getRuleIdByName("ConnectedAP唯一性校验"), "nodeid": apitem.NodeId, "parse_result": parse_result} c.AppendPaseResult(r) continue } apPri[apitem.ApName+apitem.IedName] = 1 ied := new(ScdNode).GetIed(c.scdXmlObject, "", apitem.IedName) if ied == nil { parse_result := fmt.Sprintf("%s子网下ConnectedAP(%s)的iedName(%s)未指向已存在的IED", subnet.Name, apitem.ApName, apitem.IedName) r := map[string]interface{}{"scdid": c.ScdID, "lineno": apitem.Lineno, "ruleid": c.getRuleIdByName("IED命名一致性校验"), "nodeid": apitem.NodeId, "parse_result": parse_result} c.AppendPaseResult(r) continue } apHas := false if ied.AccessPoint != nil { for _, iedap := range ied.AccessPoint { if iedap.Name == apitem.ApName { apHas = true break } } } if !apHas { parse_result := fmt.Sprintf("%s子网下ConnectedAP(%s)的apName未指向已存在的IED访问点", subnet.Name, apitem.ApName) r := map[string]interface{}{"scdid": c.ScdID, "lineno": apitem.Lineno, "ruleid": c.getRuleIdByName("访问点命名一致性校验"), "nodeid": apitem.NodeId, "parse_result": parse_result} c.AppendPaseResult(r) } if len(apitem.SMV) > 0 { for _, apsmvitem := range apitem.SMV { foundLdInst := false foundCbname := false if ied.AccessPoint != nil { for _, iedap := range ied.AccessPoint { if iedap.Server == nil { continue } for _, iedld := range iedap.Server.LDevice { if iedld.Inst == apsmvitem.LdInst { //查找控制块 if iedld.LN0 != nil { for _, iedSmvCb := range iedld.LN0.SampledValueControl { if iedSmvCb.Name == apsmvitem.CbName { foundCbname = true if iedSmvCb.SmvID == "" { parse_result := fmt.Sprintf("IED(%s)在%s子网下的控制块(%s)的通信参数(svmID)缺失", apitem.IedName, subnet.Name, iedSmvCb.Name) r := map[string]interface{}{"scdid": c.ScdID, "lineno": iedSmvCb.Lineno, "ruleid": c.getRuleIdByName("SV通信参数缺失"), "nodeid": apitem.NodeId, "parse_result": parse_result} c.AppendPaseResult(r, ied) } else { if v, h := gseAppidMap[iedSmvCb.SmvID]; h { parse_result := fmt.Sprintf("IED(%s)在%s子网下的控制块(%s)的smvID(%s)与IED(%s)重复", apitem.IedName, subnet.Name, iedSmvCb.Name, iedSmvCb.SmvID, v) r := map[string]interface{}{"scdid": c.ScdID, "lineno": iedSmvCb.Lineno, "ruleid": c.getRuleIdByName("SV通信参数唯一性"), "nodeid": apitem.NodeId, "parse_result": parse_result} c.AppendPaseResult(r, ied) } gseAppidMap[iedSmvCb.SmvID] = ied.Name } if iedSmvCb.ConfRev == "" { parse_result := fmt.Sprintf("IED(%s)在%s子网下的控制块(%s)的通信参数(confRev)缺失", apitem.IedName, subnet.Name, iedSmvCb.Name) r := map[string]interface{}{"scdid": c.ScdID, "lineno": iedSmvCb.Lineno, "ruleid": c.getRuleIdByName("SV通信参数缺失"), "nodeid": apitem.NodeId, "parse_result": parse_result} c.AppendPaseResult(r, ied) } break } } } foundLdInst = true break } } if foundLdInst { break } } } if !foundLdInst { parse_result := fmt.Sprintf("%s子网下ConnectedAP(%s,%s)的SMV(cbName=%s,ldInst=%s)中的ldInst未指向已存在的IED逻辑设备inst", subnet.Name, apitem.ApName, apitem.IedName, apsmvitem.CbName, apsmvitem.LdInst) r := map[string]interface{}{"scdid": c.ScdID, "lineno": apsmvitem.Lineno, "ruleid": c.getRuleIdByName("SMV命名一致性校验"), "nodeid": apitem.NodeId, "parse_result": parse_result} c.AppendPaseResult(r, ied) } else { if !foundCbname { parse_result := fmt.Sprintf("%s子网下ConnectedAP(%s,%s)的SMV(cbName=%s,ldInst=%s)中的cbName未指向已存在的IED SMV控制块", subnet.Name, apitem.ApName, apitem.IedName, apsmvitem.CbName, apsmvitem.LdInst) r := map[string]interface{}{"scdid": c.ScdID, "lineno": apsmvitem.Lineno, "ruleid": c.getRuleIdByName("SMV命名一致性校验"), "nodeid": apitem.NodeId, "parse_result": parse_result} c.AppendPaseResult(r, ied) } } if apsmvitem.Address != nil { mACAddress := "" for _, tmpP := range apsmvitem.Address.P { if tmpP.InnerText == "" { parse_result := fmt.Sprintf("IED(%s)下的LD(%s)在%s子网下的控制块(%s)的通信参数(%s)缺失", apitem.IedName, apsmvitem.LdInst, subnet.Name, apsmvitem.CbName, tmpP.Type) r := map[string]interface{}{"scdid": c.ScdID, "lineno": tmpP.Lineno, "ruleid": c.getRuleIdByName("SV通信参数缺失"), "nodeid": apitem.NodeId, "parse_result": parse_result} c.AppendPaseResult(r, ied) continue } key := tmpP.Type + tmpP.InnerText tmpIednameV := smvP[key] switch tmpP.Type { case "MAC-Address": if tmpIednameV != "" { parse_result := fmt.Sprintf("IED(%s)在%s子网(%s)下的控制块(cbName=%s,ldInst=%s)的MAC地址(%s)与IED(%s)重复", apitem.IedName, subnet.Name, apsmvitem.CbName, apsmvitem.LdInst, tmpP.InnerText, tmpIednameV) r := map[string]interface{}{"scdid": c.ScdID, "lineno": tmpP.Lineno, "ruleid": c.getRuleIdByName("SV通信参数唯一性"), "nodeid": apitem.NodeId, "parse_result": parse_result} c.AppendPaseResult(r, ied) break } ary := strings.Split(tmpP.InnerText, "-") if len(ary) == 6 && strings.HasPrefix(tmpP.InnerText, "01-0C-CD-04-") { //校验是否越界 p1, er1 := strconv.ParseUint(ary[4], 16, 32) //16进制转10进制 p2, er2 := strconv.ParseUint(ary[5], 16, 32) //16进制转10进制 if er1 != nil || er2 != nil { parse_result := fmt.Sprintf("IED(%s)在%s子网下的控制块(cbName=%s,ldInst=%s)的MAC地址(%s)配置不规范", apitem.IedName, subnet.Name, apsmvitem.CbName, apsmvitem.LdInst, tmpP.InnerText) r := map[string]interface{}{"scdid": c.ScdID, "lineno": tmpP.Lineno, "ruleid": c.getRuleIdByName("SV通信参数Mac地址越界"), "nodeid": apitem.NodeId, "parse_result": parse_result} c.AppendPaseResult(r) } else { p00, _ := strconv.ParseUint("00", 16, 32) pff, _ := strconv.ParseUint("FF", 16, 32) if p1 < p00 || p1 > pff || p2 < p00 || p2 > pff { parse_result := fmt.Sprintf("IED(%s)在%s子网下的控制块(cbName=%s,ldInst=%s)的MAC地址(%s)越界", apitem.IedName, subnet.Name, apsmvitem.CbName, apsmvitem.LdInst, tmpP.InnerText) r := map[string]interface{}{"scdid": c.ScdID, "lineno": tmpP.Lineno, "ruleid": c.getRuleIdByName("SV通信参数Mac地址越界"), "nodeid": apitem.NodeId, "parse_result": parse_result} c.AppendPaseResult(r) } else { mACAddress = tmpP.InnerText } } } else { parse_result := fmt.Sprintf("IED(%s)在%s子网下的控制块(cbName=%s,ldInst=%s)的MAC地址(%s)配置不规范", apitem.IedName, subnet.Name, apsmvitem.CbName, apsmvitem.LdInst, tmpP.InnerText) r := map[string]interface{}{"scdid": c.ScdID, "lineno": tmpP.Lineno, "ruleid": c.getRuleIdByName("SV通信参数Mac地址越界"), "nodeid": apitem.NodeId, "parse_result": parse_result} c.AppendPaseResult(r, ied) } break case "APPID": if tmpIednameV != "" { parse_result := fmt.Sprintf("IED(%s)在%s子网(%s)下的控制块(cbName=%s,ldInst=%s)的APPID(%s)与IED(%s)重复", apitem.IedName, subnet.Name, apsmvitem.CbName, apsmvitem.LdInst, tmpP.InnerText, tmpIednameV) r := map[string]interface{}{"scdid": c.ScdID, "lineno": tmpP.Lineno, "ruleid": c.getRuleIdByName("SV通信参数唯一性"), "nodeid": apitem.NodeId, "parse_result": parse_result} c.AppendPaseResult(r, ied) } else { //校验APPID是否越界 p16, er := strconv.ParseUint(tmpP.InnerText, 16, 32) if len(tmpP.InnerText) != 4 || er != nil { parse_result := fmt.Sprintf("IED(%s)在%s子网下的控制块(cbName=%s,ldInst=%s)的APPID(%s)配置不规范,应为4位16进制值", apitem.IedName, subnet.Name, apsmvitem.CbName, apsmvitem.LdInst, tmpP.InnerText) r := map[string]interface{}{"scdid": c.ScdID, "lineno": tmpP.Lineno, "ruleid": c.getRuleIdByName("SV通信参数APPID越界"), "nodeid": apitem.NodeId, "parse_result": parse_result} c.AppendPaseResult(r, ied) } else { p1000, _ := strconv.ParseUint("4000", 16, 32) p1fff, _ := strconv.ParseUint("4FFF", 16, 32) if p16 < p1000 || p16 > p1fff { parse_result := fmt.Sprintf("IED(%s)在%s子网下的控制块(cbName=%s,ldInst=%s)的APPID(%s)越界,其范围为0x4000~0x4FFF", apitem.IedName, subnet.Name, apsmvitem.CbName, apsmvitem.LdInst, tmpP.InnerText) r := map[string]interface{}{"scdid": c.ScdID, "lineno": tmpP.Lineno, "ruleid": c.getRuleIdByName("SV通信参数APPID越界"), "nodeid": apitem.NodeId, "parse_result": parse_result} c.AppendPaseResult(r, ied) } if mACAddress != "" { //校验appid生成是否正确 macAry := strings.Split(mACAddress, "-") tmpAppid := macAry[3][1:] + macAry[4][1:] + macAry[5] if tmpP.InnerText != tmpAppid { parse_result := fmt.Sprintf("IED(%s)在%s子网下的控制块(cbName=%s,ldInst=%s)的APPID(%s)与mac地址不匹配", apitem.IedName, subnet.Name, apsmvitem.CbName, apsmvitem.LdInst, tmpP.InnerText) r := map[string]interface{}{"scdid": c.ScdID, "lineno": tmpP.Lineno, "ruleid": c.getRuleIdByName("SV通信参数APPID与Mac地址不匹配"), "nodeid": apitem.NodeId, "parse_result": parse_result} c.AppendPaseResult(r, ied) } } } } break case "VLAN-ID": p16, er := strconv.ParseUint(tmpP.InnerText, 16, 32) if er != nil || len(tmpP.InnerText) < 3 { parse_result := fmt.Sprintf("IED(%s)在%s子网下的控制块(cbName=%s,ldInst=%s)的VLAN-ID(%s)配置不规范,应为3位16进制值", apitem.IedName, subnet.Name, apsmvitem.CbName, apsmvitem.LdInst, tmpP.InnerText) r := map[string]interface{}{"scdid": c.ScdID, "lineno": tmpP.Lineno, "ruleid": c.getRuleIdByName("SV通信参数VLAN-ID越界"), "nodeid": apitem.NodeId, "parse_result": parse_result} c.AppendPaseResult(r, ied) } else { p1000, _ := strconv.ParseUint("000", 16, 32) p1fff, _ := strconv.ParseUint("FFF", 16, 32) if p16 < p1000 || p16 > p1fff { parse_result := fmt.Sprintf("IED(%s)在%s子网下的控制块(cbName=%s,ldInst=%s)的VLAN-ID(%s)越界,其范围为0x000~0xFFF", apitem.IedName, subnet.Name, apsmvitem.CbName, apsmvitem.LdInst, tmpP.InnerText) r := map[string]interface{}{"scdid": c.ScdID, "lineno": tmpP.Lineno, "ruleid": c.getRuleIdByName("SV通信参数VLAN-ID越界"), "nodeid": apitem.NodeId, "parse_result": parse_result} c.AppendPaseResult(r, ied) } } break case "VLAN-Priority": p16, er := strconv.ParseUint(tmpP.InnerText, 16, 32) if len(tmpP.InnerText) != 1 || er != nil { parse_result := fmt.Sprintf("IED(%s)在%s子网下的控制块(cbName=%s,ldInst=%s)的VLAN-Priority(%s)配置不规范,应为1位16进制值", apitem.IedName, subnet.Name, apsmvitem.CbName, apsmvitem.LdInst, tmpP.InnerText) r := map[string]interface{}{"scdid": c.ScdID, "lineno": tmpP.Lineno, "ruleid": c.getRuleIdByName("SV通信参数VLAN-Priority越界"), "nodeid": apitem.NodeId, "parse_result": parse_result} c.AppendPaseResult(r, ied) } else { if p16 < 0 || p16 > 7 { parse_result := fmt.Sprintf("IED(%s)在%s子网下的控制块(cbName=%s,ldInst=%s)的VLAN-Priority(%s)越界,其范围为0~7", apitem.IedName, subnet.Name, apsmvitem.CbName, apsmvitem.LdInst, tmpP.InnerText) r := map[string]interface{}{"scdid": c.ScdID, "lineno": tmpP.Lineno, "ruleid": c.getRuleIdByName("SV通信参数VLAN-Priority越界"), "nodeid": apitem.NodeId, "parse_result": parse_result} c.AppendPaseResult(r, ied) } } break } smvP[key] = apitem.IedName } } } } if len(apitem.GSE) > 0 { for _, apsmvitem := range apitem.GSE { foundLdInst := false foundCbname := false if ied.AccessPoint != nil { for _, iedap := range ied.AccessPoint { if iedap.Server == nil { continue } for _, iedld := range iedap.Server.LDevice { if iedld.Inst == apsmvitem.LdInst { //查找控制块 if iedld.LN0 != nil { for _, iedCb := range iedld.LN0.GSEControl { if iedCb.Name == apsmvitem.CbName { foundCbname = true if iedCb.AppID == "" { parse_result := fmt.Sprintf("IED(%s)在%s子网下的控制块(%s)的通信参数(appID)缺失", apitem.IedName, subnet.Name, iedCb.Name) r := map[string]interface{}{"scdid": c.ScdID, "lineno": iedCb.Lineno, "ruleid": c.getRuleIdByName("GOOSE通信参数缺失"), "nodeid": apitem.NodeId, "parse_result": parse_result} c.AppendPaseResult(r, ied) } else { if v, h := gseAppidMap[iedCb.AppID]; h { parse_result := fmt.Sprintf("IED(%s)在%s子网下的控制块(%s)的appID(%s)与IED(%s)重复", apitem.IedName, subnet.Name, iedCb.Name, iedCb.AppID, v) r := map[string]interface{}{"scdid": c.ScdID, "lineno": iedCb.Lineno, "ruleid": c.getRuleIdByName("GOOSE通信参数唯一性"), "nodeid": apitem.NodeId, "parse_result": parse_result} c.AppendPaseResult(r, ied) } gseAppidMap[iedCb.AppID] = ied.Name } if iedCb.ConfRev == "" { parse_result := fmt.Sprintf("IED(%s)在%s子网下的控制块(%s)的通信参数(confRev)缺失", apitem.IedName, subnet.Name, iedCb.Name) r := map[string]interface{}{"scdid": c.ScdID, "lineno": iedCb.Lineno, "ruleid": c.getRuleIdByName("GOOSE通信参数缺失"), "nodeid": apitem.NodeId, "parse_result": parse_result} c.AppendPaseResult(r, ied) } break } } } foundLdInst = true break } } if foundLdInst { break } } } if !foundLdInst { parse_result := fmt.Sprintf("%s子网下ConnectedAP(%s,%s)的GSE(cbName=%s,ldInst=%s)中的ldInst未指向已存在的IED逻辑设备inst", subnet.Name, apitem.ApName, apitem.IedName, apsmvitem.CbName, apsmvitem.LdInst) r := map[string]interface{}{"scdid": c.ScdID, "lineno": apsmvitem.Lineno, "ruleid": c.getRuleIdByName("GSE命名一致性校验"), "nodeid": apitem.NodeId, "parse_result": parse_result} c.AppendPaseResult(r, ied) } else { if !foundCbname { parse_result := fmt.Sprintf("%s子网下ConnectedAP(%s,%s)的GSE(cbName=%s,ldInst=%s)中的cbName未指向已存在的IED GOOSE控制块", subnet.Name, apitem.ApName, apitem.IedName, apsmvitem.CbName, apsmvitem.LdInst) r := map[string]interface{}{"scdid": c.ScdID, "lineno": apsmvitem.Lineno, "ruleid": c.getRuleIdByName("GSE命名一致性校验"), "nodeid": apitem.NodeId, "parse_result": parse_result} c.AppendPaseResult(r, ied) } } //GOOSE通信参数校验:GOOSE通信参数唯一性、GOOSE通信参数Mac地址越界、GOOSE通信参数APPID越界、GOOSE通信参数APPID与Mac地址不匹配、GOOSE通信参数VLAN-ID越界、GOOSE通信参数VLAN-Priority越界、GOOSE通信参数MinTime 和 MaxTime 不为推荐值、GOOSE通信参数缺失 if apsmvitem.Address != nil { mACAddress := "" for _, tmpP := range apsmvitem.Address.P { if tmpP.InnerText == "" { parse_result := fmt.Sprintf("IED(%s)下的LD(%s)在%s子网控制块(%s)的通信参数(%s)缺失", apitem.IedName, apsmvitem.LdInst, subnet.Name, apsmvitem.CbName, tmpP.Type) r := map[string]interface{}{"scdid": c.ScdID, "lineno": tmpP.Lineno, "ruleid": c.getRuleIdByName("GOOSE通信参数缺失"), "nodeid": apitem.NodeId, "parse_result": parse_result} c.AppendPaseResult(r, ied) continue } key := tmpP.Type + tmpP.InnerText tmpIednameV := gseP[key] switch tmpP.Type { case "MAC-Address": if tmpIednameV != "" { parse_result := fmt.Sprintf("IED(%s)在%s子网下的控制块(cbName=%s,ldInst=%s)的MAC地址(%s)与IED(%s)重复", apitem.IedName, subnet.Name, apsmvitem.CbName, apsmvitem.LdInst, tmpP.InnerText, tmpIednameV) r := map[string]interface{}{"scdid": c.ScdID, "lineno": tmpP.Lineno, "ruleid": c.getRuleIdByName("GOOSE通信参数唯一性"), "nodeid": apitem.NodeId, "parse_result": parse_result} c.AppendPaseResult(r, ied) break } ary := strings.Split(tmpP.InnerText, "-") if len(ary) == 6 && strings.HasPrefix(tmpP.InnerText, "01-0C-CD-01-") { //校验是否越界 p1, er1 := strconv.ParseUint(ary[4], 16, 32) //16进制转10进制 p2, er2 := strconv.ParseUint(ary[5], 16, 32) //16进制转10进制 if er1 != nil || er2 != nil { parse_result := fmt.Sprintf("IED(%s)在%s子网下的控制块(cbName=%s,ldInst=%s)的MAC地址(%s)配置不规范", apitem.IedName, subnet.Name, apsmvitem.CbName, apsmvitem.LdInst, tmpP.InnerText) r := map[string]interface{}{"scdid": c.ScdID, "lineno": tmpP.Lineno, "ruleid": c.getRuleIdByName("GOOSE通信参数Mac地址越界"), "nodeid": apitem.NodeId, "parse_result": parse_result} c.AppendPaseResult(r) } else { p00, _ := strconv.ParseUint("00", 16, 32) pff, _ := strconv.ParseUint("FF", 16, 32) if p1 < p00 || p1 > pff || p2 < p00 || p2 > pff { parse_result := fmt.Sprintf("IED(%s)在%s子网下的控制块(cbName=%s,ldInst=%s)的MAC地址(%s)越界", apitem.IedName, subnet.Name, apsmvitem.CbName, apsmvitem.LdInst, tmpP.InnerText) r := map[string]interface{}{"scdid": c.ScdID, "lineno": tmpP.Lineno, "ruleid": c.getRuleIdByName("GOOSE通信参数Mac地址越界"), "nodeid": apitem.NodeId, "parse_result": parse_result} c.AppendPaseResult(r) } else { mACAddress = tmpP.InnerText } } } else { parse_result := fmt.Sprintf("IED(%s)在%s子网下的控制块(cbName=%s,ldInst=%s)的MAC地址(%s)配置不规范", apitem.IedName, subnet.Name, apsmvitem.CbName, apsmvitem.LdInst, tmpP.InnerText) r := map[string]interface{}{"scdid": c.ScdID, "lineno": tmpP.Lineno, "ruleid": c.getRuleIdByName("GOOSE通信参数Mac地址越界"), "nodeid": apitem.NodeId, "parse_result": parse_result} c.AppendPaseResult(r, ied) } break case "APPID": if tmpIednameV != "" { parse_result := fmt.Sprintf("IED(%s)在%s子网下的控制块(cbName=%s,ldInst=%s)的APPID(%s)与IED(%s)重复", apitem.IedName, subnet.Name, apsmvitem.CbName, apsmvitem.LdInst, tmpP.InnerText, tmpIednameV) r := map[string]interface{}{"scdid": c.ScdID, "lineno": tmpP.Lineno, "ruleid": c.getRuleIdByName("GOOSE通信参数唯一性"), "nodeid": apitem.NodeId, "parse_result": parse_result} c.AppendPaseResult(r, ied) } else { //校验APPID是否越界 p16, er := strconv.ParseUint(tmpP.InnerText, 16, 32) if len(tmpP.InnerText) != 4 || er != nil { parse_result := fmt.Sprintf("IED(%s)在%s子网下的控制块(cbName=%s,ldInst=%s)的APPID(%s)配置不规范,应为4位16进制值", apitem.IedName, subnet.Name, apsmvitem.CbName, apsmvitem.LdInst, tmpP.InnerText) r := map[string]interface{}{"scdid": c.ScdID, "lineno": tmpP.Lineno, "ruleid": c.getRuleIdByName("GOOSE通信参数APPID越界"), "nodeid": apitem.NodeId, "parse_result": parse_result} c.AppendPaseResult(r, ied) } else { p1000, _ := strconv.ParseUint("1000", 16, 32) p1fff, _ := strconv.ParseUint("1FFF", 16, 32) if p16 < p1000 || p16 > p1fff { parse_result := fmt.Sprintf("IED(%s)在%s子网下的控制块(cbName=%s,ldInst=%s)的APPID(%s)越界,其范围为0x1000~0x1FFF", apitem.IedName, subnet.Name, apsmvitem.CbName, apsmvitem.LdInst, tmpP.InnerText) r := map[string]interface{}{"scdid": c.ScdID, "lineno": tmpP.Lineno, "ruleid": c.getRuleIdByName("GOOSE通信参数APPID越界"), "nodeid": apitem.NodeId, "parse_result": parse_result} c.AppendPaseResult(r, ied) } if mACAddress != "" { //校验appid生成是否正确 macAry := strings.Split(mACAddress, "-") tmpAppid := macAry[3][1:] + macAry[4][1:] + macAry[5] if tmpP.InnerText != tmpAppid { parse_result := fmt.Sprintf("IED(%s)在%s子网下的控制块(cbName=%s,ldInst=%s)的APPID(%s)与mac地址不匹配", apitem.IedName, subnet.Name, apsmvitem.CbName, apsmvitem.LdInst, tmpP.InnerText) r := map[string]interface{}{"scdid": c.ScdID, "lineno": tmpP.Lineno, "ruleid": c.getRuleIdByName("GOOSE通信参数APPID与Mac地址不匹配"), "nodeid": apitem.NodeId, "parse_result": parse_result} c.AppendPaseResult(r, ied) } } } } break case "VLAN-ID": p16, er := strconv.ParseUint(tmpP.InnerText, 16, 32) if er != nil || len(tmpP.InnerText) < 3 { parse_result := fmt.Sprintf("IED(%s)在%s子网下的控制块(cbName=%s,ldInst=%s)的VLAN-ID(%s)配置不规范,应为3位16进制值", apitem.IedName, subnet.Name, apsmvitem.CbName, apsmvitem.LdInst, tmpP.InnerText) r := map[string]interface{}{"scdid": c.ScdID, "lineno": tmpP.Lineno, "ruleid": c.getRuleIdByName("GOOSE通信参数VLAN-ID越界"), "nodeid": apitem.NodeId, "parse_result": parse_result} c.AppendPaseResult(r, ied) } else { p1000, _ := strconv.ParseUint("000", 16, 32) p1fff, _ := strconv.ParseUint("FFF", 16, 32) if p16 < p1000 || p16 > p1fff { parse_result := fmt.Sprintf("IED(%s)在%s子网下的控制块(cbName=%s,ldInst=%s)的VLAN-ID(%s)越界,其范围为0x000~0xFFF", apitem.IedName, subnet.Name, apsmvitem.CbName, apsmvitem.LdInst, tmpP.InnerText) r := map[string]interface{}{"scdid": c.ScdID, "lineno": tmpP.Lineno, "ruleid": c.getRuleIdByName("GOOSE通信参数VLAN-ID越界"), "nodeid": apitem.NodeId, "parse_result": parse_result} c.AppendPaseResult(r, ied) } } break case "VLAN-Priority": p16, er := strconv.ParseUint(tmpP.InnerText, 16, 32) if len(tmpP.InnerText) != 1 || er != nil { parse_result := fmt.Sprintf("IED(%s)在%s子网下的控制块(cbName=%s,ldInst=%s)的VLAN-Priority(%s)配置不规范,应为1位16进制值", apitem.IedName, subnet.Name, apsmvitem.CbName, apsmvitem.LdInst, tmpP.InnerText) r := map[string]interface{}{"scdid": c.ScdID, "lineno": tmpP.Lineno, "ruleid": c.getRuleIdByName("GOOSE通信参数VLAN-Priority越界"), "nodeid": apitem.NodeId, "parse_result": parse_result} c.AppendPaseResult(r, ied) } else { if p16 < 0 || p16 > 7 { parse_result := fmt.Sprintf("IED(%s)在%s子网下的控制块(cbName=%s,ldInst=%s)的VLAN-Priority(%s)越界,其范围为0~7", apitem.IedName, subnet.Name, apsmvitem.CbName, apsmvitem.LdInst, tmpP.InnerText) r := map[string]interface{}{"scdid": c.ScdID, "lineno": tmpP.Lineno, "ruleid": c.getRuleIdByName("GOOSE通信参数VLAN-Priority越界"), "nodeid": apitem.NodeId, "parse_result": parse_result} c.AppendPaseResult(r, ied) } } break } gseP[key] = apitem.IedName } } if apsmvitem.MaxTime == nil { parse_result := fmt.Sprintf("IED(%s)在%s子网下的控制块(%s)的通信参数(MaxTime)缺失", apitem.IedName, subnet.Name, apsmvitem.CbName) r := map[string]interface{}{"scdid": c.ScdID, "lineno": apsmvitem.Lineno, "ruleid": c.getRuleIdByName("GOOSE通信参数缺失"), "nodeid": apitem.NodeId, "parse_result": parse_result} c.AppendPaseResult(r, ied) } else { if apsmvitem.MaxTime.InnerText != "5000" { parse_result := fmt.Sprintf("IED(%s)在%s子网下的控制块(%s)的通信参数MaxTime(%s)不为标准值5000", apitem.IedName, subnet.Name, apsmvitem.CbName, apsmvitem.MaxTime.InnerText) r := map[string]interface{}{"scdid": c.ScdID, "lineno": apsmvitem.Lineno, "ruleid": c.getRuleIdByName("GOOSE通信参数MinTime和MaxTime不为推荐值"), "nodeid": apitem.NodeId, "parse_result": parse_result} c.AppendPaseResult(r, ied) } } if apsmvitem.MinTime == nil { parse_result := fmt.Sprintf("IED(%s)在%s子网下的控制块(%s)的通信参数(MinTime)缺失", apitem.IedName, subnet.Name, apsmvitem.CbName) r := map[string]interface{}{"scdid": c.ScdID, "lineno": apsmvitem.Lineno, "ruleid": c.getRuleIdByName("GOOSE通信参数缺失"), "nodeid": apitem.NodeId, "parse_result": parse_result} c.AppendPaseResult(r, ied) } else { if apsmvitem.MinTime.InnerText != "2" { parse_result := fmt.Sprintf("IED(%s)在%s子网下的控制块(%s)的通信参数MinTime(%s)不为标准值2", apitem.IedName, subnet.Name, apsmvitem.CbName, apsmvitem.MinTime.InnerText) r := map[string]interface{}{"scdid": c.ScdID, "lineno": apsmvitem.Lineno, "ruleid": c.getRuleIdByName("GOOSE通信参数MinTime和MaxTime不为推荐值"), "nodeid": apitem.NodeId, "parse_result": parse_result} c.AppendPaseResult(r, ied) } } } } //检查站控层通信参数校验 //1、IP地址同一子网内唯一性 if apitem.Address != nil { for _, ips := range apitem.Address.P { if ips.Type == "IP" { if ips.InnerText == "" { parse_result := fmt.Sprintf("IED(%s)在%s子网下未配置IP地址", apitem.ApName, subnet.Name, subnet.Desc) r := map[string]interface{}{"scdid": c.ScdID, "lineno": ips.Lineno, "ruleid": c.getRuleIdByName("IP地址未配置"), "nodeid": apitem.NodeId, "parse_result": parse_result} c.AppendPaseResult(r, ied) } else { //判断IP是否越界 ipparts := strings.Split(ips.InnerText, ".") if len(ipparts) != 4 { parse_result := fmt.Sprintf("IED(%s)在%s子网下IP地址(%s)无效", apitem.ApName, subnet.Name, ips.InnerText) r := map[string]interface{}{"scdid": c.ScdID, "lineno": ips.Lineno, "ruleid": c.getRuleIdByName("IP地址配置不规范"), "nodeid": apitem.NodeId, "parse_result": parse_result} c.AppendPaseResult(r, ied) } else { ip1, _ := strconv.Atoi(ipparts[0]) ip2, _ := strconv.Atoi(ipparts[1]) ip3, _ := strconv.Atoi(ipparts[2]) ip4, _ := strconv.Atoi(ipparts[3]) if (ip1 < 0 && ip1 > 255) || (ip2 < 0 && ip2 > 255) || (ip3 < 0 && ip3 > 255) || (ip4 < 0 && ip4 > 255) { parse_result := fmt.Sprintf("IED(%s)在%s子网下IP地址(%s)越界", apitem.ApName, subnet.Name, ips.InnerText) r := map[string]interface{}{"scdid": c.ScdID, "lineno": ips.Lineno, "ruleid": c.getRuleIdByName("IP地址越界"), "nodeid": apitem.NodeId, "parse_result": parse_result} c.AppendPaseResult(r, ied) } } } if tmpIednameV, h := ipAddressIp[ips.InnerText]; h { parse_result := fmt.Sprintf("IED(%s)在%s子网下的IP地址与IED(%s)重复", apitem.ApName, subnet.Name, tmpIednameV) r := map[string]interface{}{"scdid": c.ScdID, "lineno": ips.Lineno, "ruleid": c.getRuleIdByName("IP地址唯一性"), "nodeid": apitem.NodeId, "parse_result": parse_result} c.AppendPaseResult(r, ied) } ipAddressIp[ips.InnerText] = apitem.IedName } if ips.Type == "IP-SUBNET" { if ips.InnerText == "" { parse_result := fmt.Sprintf("IED(%s)在%s子网下未配置IP-SUBNET地址", apitem.ApName, subnet.Name, subnet.Desc) r := map[string]interface{}{"scdid": c.ScdID, "lineno": ips.Lineno, "ruleid": c.getRuleIdByName("IP-SUBNET地址未配置"), "nodeid": apitem.NodeId, "parse_result": parse_result} c.AppendPaseResult(r, ied) } else { ipparts := strings.Split(ips.InnerText, ".") if len(ipparts) != 4 { parse_result := fmt.Sprintf("IED(%s)在%s子网下IP-SUBNET地址(%s)无效", apitem.ApName, subnet.Name, ips.InnerText) r := map[string]interface{}{"scdid": c.ScdID, "lineno": ips.Lineno, "ruleid": c.getRuleIdByName("IP-SUBNET地址配置不规范"), "nodeid": apitem.NodeId, "parse_result": parse_result} c.AppendPaseResult(r, ied) } else { ip1, _ := strconv.Atoi(ipparts[0]) ip2, _ := strconv.Atoi(ipparts[1]) ip3, _ := strconv.Atoi(ipparts[2]) ip4, _ := strconv.Atoi(ipparts[3]) if (ip1 < 0 && ip1 > 255) || (ip2 < 0 && ip2 > 255) || (ip3 < 0 && ip3 > 255) || (ip4 < 0 && ip4 > 255) { parse_result := fmt.Sprintf("IED(%s)在%s子网下IP-SUBNET地址(%s)越界", apitem.ApName, subnet.Name, ips.InnerText) r := map[string]interface{}{"scdid": c.ScdID, "lineno": ips.Lineno, "ruleid": c.getRuleIdByName("IP-SUBNET地址越界"), "nodeid": apitem.NodeId, "parse_result": parse_result} c.AppendPaseResult(r, ied) } } } } } } } } } //IED校验 //.IED(XXX)中Server对象缺失 //.IED(XXX)中访问点***中的Server缺失LD对象 //.IED(XXX)中LD***中缺失LLN0 //.IED(XXX)中LD***中缺失LPHD //.IED(XXX)中LD***中缺失非LPHD外其他LN func (c *ScdNodeRule) CheckFunc_ied(dATypeMap map[string]*node_attr.NDAType) { logger.Logger.Debug(fmt.Sprintf("校验SCD %d的IED节点", c.ScdID)) if c.scdXmlObject == nil { return } //fcdaMap := map[string]string{} gooseAppidMap := sync.Map{} // map[string]*node_attr.NIED{} smvAppidMap := sync.Map{} // map[string]*node_attr.NIED{} reportRptIDMap := sync.Map{} var confDataSet *node_attr.NConfDataSet for _, iedObj := range c.scdXmlObject.IED { //判断ccd校验码 需要在生成crc校验码后进行 ccdCrc := "" for _, iedPrivate := range iedObj.Priavate { if iedPrivate.Type == "IED virtual terminal conection CRC" { ccdCrc = iedPrivate.InnerText break } } if ccdCrc == "" { parse_result := fmt.Sprintf("IED(%s)不存在ICD文件CRC校验码", iedObj.Name) r := map[string]interface{}{"scdid": c.ScdID, "lineno": iedObj.Lineno, "ruleid": c.getRuleIdByName("ICD文件校验码存在性"), "nodeid": iedObj.NodeId, "parse_result": parse_result} c.AppendPaseResult(r, iedObj) } if iedObj.Services != nil { confDataSet = iedObj.Services.ConfDataSet } signalMapFound := false for _, iedPrivate := range iedObj.Priavate { if iedPrivate.Type == "Signal Map" { signalMapFound = true break } } if !signalMapFound { parse_result := fmt.Sprintf("IED(%s)定义关联关系的信号或软压板不存在", iedObj.Name) r := map[string]interface{}{"scdid": c.ScdID, "lineno": iedObj.Lineno, "ruleid": c.getRuleIdByName("信号关联关系格式检查"), "nodeid": iedObj.NodeId, "parse_result": parse_result} c.AppendPaseResult(r, iedObj) } if len(iedObj.Name) < 5 || len(iedObj.Name) > 8 { //IED 的 name 由 5 部分共 8 位合法可视字符组成,分别代表: IED 类型、归属设备类型、电压等级、归属设备编号、 IED 编号,具体要求如附录 B 所示 parse_result := fmt.Sprintf("IED(%s)名称不符合规范要求", iedObj.Name) r := map[string]interface{}{"scdid": c.ScdID, "lineno": iedObj.Lineno, "ruleid": c.getRuleIdByName("IEDName合法性校验"), "nodeid": iedObj.NodeId, "parse_result": parse_result} c.AppendPaseResult(r, iedObj) } if len(iedObj.AccessPoint) == 0 { parse_result := fmt.Sprintf("IED(%s)中AccessPoint对象缺失", iedObj.Name) r := map[string]interface{}{"scdid": c.ScdID, "lineno": iedObj.Lineno, "ruleid": c.getRuleIdByName("访问点(AccessPoint)名称校验"), "nodeid": iedObj.NodeId, "parse_result": parse_result} c.AppendPaseResult(r, iedObj) continue } for _, ap := range iedObj.AccessPoint { if ap.Server == nil { parse_result := fmt.Sprintf("IED(%s)中Server对象缺失", iedObj.Name) r := map[string]interface{}{"scdid": c.ScdID, "lineno": ap.Lineno, "ruleid": c.getRuleIdByName("逻辑设备建模正确性"), "nodeid": iedObj.NodeId, "parse_result": parse_result} c.AppendPaseResult(r, iedObj) continue } if len(ap.Server.LDevice) == 0 { parse_result := fmt.Sprintf("IED(%s)中访问点(%s:%s)缺失LD对象", iedObj.Name, ap.Name, ap.Desc) r := map[string]interface{}{"scdid": c.ScdID, "lineno": ap.Server.Lineno, "ruleid": c.getRuleIdByName("逻辑设备建模正确性"), "nodeid": iedObj.NodeId, "parse_result": parse_result} c.AppendPaseResult(r, iedObj) continue } for _, ld := range ap.Server.LDevice { //校验DOI/SDI引用模板不一致、DAI引用模板不一致 c.check_ln_doidai(iedObj, dATypeMap, ld) if ld.LN0 == nil { parse_result := fmt.Sprintf("IED(%s)中访问点(%s:%s)下的LD(%s)缺失对象LLN0", iedObj.Name, ap.Name, ap.Desc, ld.Desc) r := map[string]interface{}{"scdid": c.ScdID, "lineno": ld.Lineno, "ruleid": c.getRuleIdByName("逻辑设备建模正确性"), "nodeid": iedObj.NodeId, "parse_result": parse_result} c.AppendPaseResult(r, iedObj) continue } else { if _, h := c.lnodeTypeMap.Load(ld.LN0.LnType); !h { parse_result := fmt.Sprintf("IED(%s)中访问点(%s:%s)下的LD(%s)下的LN0(%s)引用的LNodeType(%s)在模板中不存在", iedObj.Name, ap.Name, ap.Desc, ld.Inst, ld.LN0.Desc, ld.LN0.LnType) r := map[string]interface{}{"scdid": c.ScdID, "lineno": ld.LN0.Lineno, "ruleid": c.getRuleIdByName("LN引用LNodeType不存在"), "nodeid": iedObj.NodeId, "parse_result": parse_result} c.AppendPaseResult(r, iedObj) } if confDataSet != nil { max, _ := strconv.Atoi(confDataSet.Max) if len(ld.LN0.DataSet) > max { parse_result := fmt.Sprintf("IED(%s)中访问点(%s:%s)下的LD(%s)数据集个数超过允许的最大值(当前数=%d, 最大值=%s)", iedObj.Name, ap.Name, ap.Desc, ld.Inst, len(ld.LN0.DataSet), confDataSet.Max) r := map[string]interface{}{"scdid": c.ScdID, "lineno": ld.LN0.Lineno, "ruleid": c.getRuleIdByName("数据集数目校验"), "nodeid": iedObj.NodeId, "parse_result": parse_result} c.AppendPaseResult(r, iedObj) } } datsetList := map[string]*node_attr.NDataSet{} //校验FCDA唯一性 for _, datset := range ld.LN0.DataSet { datsetList[datset.Name] = datset if confDataSet != nil { max, _ := strconv.Atoi(confDataSet.MaxAttributes) if len(datset.FCDA) > max { parse_result := fmt.Sprintf("IED(%s)中访问点(%s:%s)下的LD(%s)数据集(%s)成员FCDA个数超过允许的最大值(当前数=%d, 最大值=%s)", iedObj.Name, ap.Name, ap.Desc, ld.Inst, datset.Name, len(datset.FCDA), confDataSet.MaxAttributes) r := map[string]interface{}{"scdid": c.ScdID, "lineno": datset.Lineno, "ruleid": c.getRuleIdByName("数据集成员数目校验"), "nodeid": iedObj.NodeId, "parse_result": parse_result} c.AppendPaseResult(r, iedObj) } } //dsGOOSE 数据集成员总数不应超过256个 if strings.HasPrefix(datset.Name, "dsGOOSE") && len(datset.FCDA) > 256 { parse_result := fmt.Sprintf("IED(%s)中访问点(%s:%s)下的LD(%s)数据集(%s)成员FCDA个数%d不能超过256", iedObj.Name, ap.Name, ap.Desc, ld.Inst, datset.Name, len(datset.FCDA)) r := map[string]interface{}{"scdid": c.ScdID, "lineno": datset.Lineno, "ruleid": c.getRuleIdByName("校验数据集成员构成"), "nodeid": iedObj.NodeId, "parse_result": parse_result} c.AppendPaseResult(r, iedObj) } for _, fcdaitem := range datset.FCDA { key := fmt.Sprintf("%s/%s%s%s.%s", fcdaitem.LdInst, fcdaitem.Prefix, fcdaitem.LnClass, fcdaitem.LnInst, fcdaitem.DoName) if fcdaitem.DaName != "" { key = key + "." + fcdaitem.DaName } if fcdaitem.LdInst != ld.Inst { parse_result := fmt.Sprintf("IED(%s)中访问点(%s:%s)下的LD(%s)数据集(%s)FCDA成员(%s)不允许跨LD", iedObj.Name, ap.Name, ap.Desc, ld.Inst, datset.Name, key) r := map[string]interface{}{"scdid": c.ScdID, "lineno": fcdaitem.Lineno, "ruleid": c.getRuleIdByName("数据集成员跨LD校验"), "nodeid": iedObj.NodeId, "parse_result": parse_result} c.AppendPaseResult(r, iedObj) continue } //成员有效性 has1, _ := c.IedFcdaExist(iedObj.Name, fcdaitem.LdInst, fcdaitem.LnClass, fcdaitem.LnInst, fcdaitem.Prefix, fcdaitem.DoName, fcdaitem.DaName) if has1 == nil { parse_result := fmt.Sprintf("IED(%s)中访问点(%s:%s)下的LD(%s)数据集(%s)FCDA成员(%s)不存在", iedObj.Name, ap.Name, ap.Desc, ld.Inst, datset.Name, key) r := map[string]interface{}{"scdid": c.ScdID, "lineno": fcdaitem.Lineno, "ruleid": c.getRuleIdByName("数据集成员有效性校验"), "nodeid": iedObj.NodeId, "parse_result": parse_result} c.AppendPaseResult(r, iedObj) } //dsGOOSE 数据集的所有成员都应采用FCDA构成方式,其它数据集成员都应采用FCD构成方式。 if strings.HasPrefix(datset.Name, "dsGOOSE") && fcdaitem.DaName == "" { parse_result := fmt.Sprintf("IED(%s)中访问点(%s:%s)下的LD(%s)数据集(%s)成员(%s)不是FCDA格式", iedObj.Name, ap.Name, ap.Desc, ld.Inst, datset.Name, key) r := map[string]interface{}{"scdid": c.ScdID, "lineno": fcdaitem.Lineno, "ruleid": c.getRuleIdByName("校验数据集成员构成"), "nodeid": iedObj.NodeId, "parse_result": parse_result} c.AppendPaseResult(r, iedObj) } if !strings.HasPrefix(datset.Name, "dsGOOSE") && fcdaitem.DaName != "" { //parse_result := fmt.Sprintf("IED(%s)中访问点(%s:%s)下的LD(%s)数据集(%s)成员(%s)不是FCD格式", iedObj.Name, ap.Name, ap.Desc, ld.Inst, datset.Name, key) //r := map[string]interface{}{"scdid": c.ScdID, "ruleid": c.getRuleIdByName("校验数据集成员构成"), "nodeid": iedObj.NodeId, "parse_result": parse_result} //c.AppendPaseResult(r, iedObj) } } } //虚回路校验 if ld.LN0.Inputs != nil { for _, extref := range ld.LN0.Inputs.ExtRef { //检查端子2端模型是否相同:DO对应DO,DA对应DA hasinsideda := extref.DaName != "" hasoutsideda := strings.Count(extref.IntAddr, ".") > 1 if hasinsideda != hasoutsideda { extrefstr := fmt.Sprintf("IED=%s,addr=%s/%s%s%s.%s", extref.IedName, extref.LdInst, extref.Prefix, extref.LnClass, extref.LnInst, extref.DoName) if extref.DaName != "" { extrefstr = extrefstr + "." + extref.DaName } parse_result := fmt.Sprintf("IED(%s)中访问点(%s:%s)中LD(%s)的关联内(%s)外部虚端子(%s)类型不一致", iedObj.Name, ap.Name, ap.Desc, ld.Inst, extref.IntAddr, extrefstr) r := map[string]interface{}{"scdid": c.ScdID, "lineno": extref.Lineno, "ruleid": c.getRuleIdByName("连线两端数据不匹配"), "nodeid": iedObj.NodeId, "parse_result": parse_result} c.AppendPaseResult(r, iedObj) } has1, cdc1 := c.IedFcdaExist(extref.IedName, extref.LdInst, extref.LnClass, extref.LnInst, extref.Prefix, extref.DoName, extref.DaName) if has1 == nil { extrefstr := fmt.Sprintf("IED=%s,addr=%s/%s%s%s.%s", extref.IedName, extref.LdInst, extref.Prefix, extref.LnClass, extref.LnInst, extref.DoName) if extref.DaName != "" { extrefstr = extrefstr + "." + extref.DaName } parse_result := fmt.Sprintf("IED(%s)中访问点(%s:%s)中LD(%s)的关联外部虚端子(%s)不存在", iedObj.Name, ap.Name, ap.Desc, ld.Inst, extrefstr) r := map[string]interface{}{"scdid": c.ScdID, "lineno": extref.Lineno, "ruleid": c.getRuleIdByName("连线外部虚端子合法性"), "nodeid": iedObj.NodeId, "parse_result": parse_result} c.AppendPaseResult(r, iedObj) } //内部地址对应的定义是否存在校验 /* has2 := c.iedIntAddrExist(iedObj.Name, extref.IntAddr) if has2 == nil { parse_result := fmt.Sprintf("IED(%s)中访问点(%s:%s)中LD(%s)的虚回路关联内部虚端子(%s)不存在", iedObj.Name, ap.Name, ap.Desc, ld.Inst, extref.IntAddr) r := map[string]interface{}{"scdid": c.ScdID, "lineno": extref.Lineno, "ruleid": c.getRuleIdByName("连线内部参引对应数据不存在"), "nodeid": iedObj.NodeId, "parse_result": parse_result} c.AppendPaseResult(r, iedObj) } */ //da或do实例化校验 has2, cdc2 := c.iedIntAddrDoiOrDaiExist(iedObj.Name, extref.IntAddr) if has2 == nil { //parse_result := fmt.Sprintf("IED(%s)中访问点(%s:%s)中LD(%s)的虚回路关联内部虚端子(%s)属性值与实例化不匹配", iedObj.Name, ap.Name, ap.Desc, ld.Inst, extref.IntAddr) //r := map[string]interface{}{"scdid": c.ScdID, "lineno": extref.Lineno, "ruleid": c.getRuleIdByName("连线内部参引对应数据不存在"), "nodeid": iedObj.NodeId, "parse_result": parse_result} //c.AppendPaseResult(r, iedObj) } //连接2端端子数据类型匹配检查。即DO数据模板的cdc属性值以及Da的bType if has1 != nil && has2 != nil && cdc1 != cdc2 { tmpTypeName := "CDC" if extref.DaName != "" { tmpTypeName = "bType" } parse_result := fmt.Sprintf("IED(%s)中访问点(%s:%s)中LD(%s)的虚端子(%s)%s类型(%s)与关联外部虚端子(%s %s/%s%s%s.%s)的类型(%s)不匹配", iedObj.Name, ap.Name, ap.Desc, ld.Inst, extref.IntAddr, tmpTypeName, cdc2, extref.IedName, extref.LdInst, extref.Prefix, extref.LnClass, extref.LnInst, extref.DoName, cdc1) r := map[string]interface{}{"scdid": c.ScdID, "lineno": extref.Lineno, "ruleid": c.getRuleIdByName("连线两端数据不匹配"), "nodeid": iedObj.NodeId, "parse_result": parse_result} c.AppendPaseResult(r, iedObj) } } } //GOOSE控制块校验 gooseitemMap := map[string]int{} if iedObj.Services.GOOSE != nil { max, _ := strconv.Atoi(iedObj.Services.GOOSE.Max) if len(ld.LN0.GSEControl) > max { parse_result := fmt.Sprintf("IED(%s)中访问点(%s:%s)下的LD(%s)GOOSE控制块个数%d超出声明的最大值(%d)", iedObj.Name, ap.Name, ap.Desc, ld.Inst, len(ld.LN0.GSEControl), max) r := map[string]interface{}{"scdid": c.ScdID, "lineno": iedObj.Services.GOOSE.Lineno, "ruleid": c.getRuleIdByName("GOOSE控制块的个数超出声明的最大值"), "nodeid": iedObj.NodeId, "parse_result": parse_result} c.AppendPaseResult(r, iedObj) } } for _, gooseitem := range ld.LN0.GSEControl { if gooseitemMap[gooseitem.Name] == 1 { parse_result := fmt.Sprintf("IED(%s)中访问点(%s:%s)下的LD(%s)GOOSE控制块(%s)name属性值在LN内重复配置", iedObj.Name, ap.Name, ap.Desc, ld.Inst, gooseitem.Name) r := map[string]interface{}{"scdid": c.ScdID, "lineno": gooseitem.Lineno, "ruleid": c.getRuleIdByName("GOOSE控制块的name属性值在LN内重复配置"), "nodeid": iedObj.NodeId, "parse_result": parse_result} c.AppendPaseResult(r) continue } gooseitemMap[gooseitem.Name] = 1 if datsetList[gooseitem.DatSet] == nil { parse_result := fmt.Sprintf("IED(%s)中访问点(%s:%s)下的LD(%s)GOOSE控制块(%s)引用的数据集(%s)不存在", iedObj.Name, ap.Name, ap.Desc, ld.Inst, gooseitem.Name, gooseitem.DatSet) r := map[string]interface{}{"scdid": c.ScdID, "lineno": gooseitem.Lineno, "ruleid": c.getRuleIdByName("GOOSE控制块引用的数据集不存在"), "nodeid": iedObj.NodeId, "parse_result": parse_result} c.AppendPaseResult(r, iedObj) } if gooseitem.AppID == "" { parse_result := fmt.Sprintf("IED(%s)中访问点(%s:%s)下的LD(%s)GOOSE控制块(%s)appID属性配置错误", iedObj.Name, ap.Name, ap.Desc, ld.Inst, gooseitem.Name) r := map[string]interface{}{"scdid": c.ScdID, "lineno": gooseitem.Lineno, "ruleid": c.getRuleIdByName("GOOSE控制块的appID属性配置错误(缺失、值为空)"), "nodeid": iedObj.NodeId, "parse_result": parse_result} c.AppendPaseResult(r, iedObj) } else { if tmpIED, h := gooseAppidMap.Load(gooseitem.AppID); h { iedPointer := tmpIED.(*node_attr.NIED) parse_result := fmt.Sprintf("IED(%s)中访问点(%s:%s)下的LD(%s)GOOSE控制块(%s)的appID(%s)与IED(%s)中重复定义", iedObj.Name, ap.Name, ap.Desc, ld.Inst, gooseitem.Name, gooseitem.AppID, iedPointer.Name) r := map[string]interface{}{"scdid": c.ScdID, "lineno": gooseitem.Lineno, "ruleid": c.getRuleIdByName("GOOSE控制块的appID唯一性"), "nodeid": iedObj.NodeId, "parse_result": parse_result} c.AppendPaseResult(r, iedObj) } //格式校验 newGooseAppid := fmt.Sprintf("%s%s/LLN0.%s", iedObj.Name, ld.Inst, gooseitem.Name) if gooseitem.AppID != newGooseAppid { parse_result := fmt.Sprintf("IED(%s)中访问点(%s:%s)下的LD(%s)GOOSE控制块(%s)的appID(%s)格式不符合规范", iedObj.Name, ap.Name, ap.Desc, ld.Inst, gooseitem.Name, gooseitem.AppID) r := map[string]interface{}{"scdid": c.ScdID, "lineno": gooseitem.Lineno, "ruleid": c.getRuleIdByName("GOOSE控制块appID格式校验"), "nodeid": iedObj.NodeId, "parse_result": parse_result} c.AppendPaseResult(r, iedObj) } gooseAppidMap.Store(gooseitem.AppID, iedObj) if gooseitem.ConfRev == "" { parse_result := fmt.Sprintf("IED(%s)中访问点(%s:%s)下的LD(%s)GOOSE控制块(%s)的confRev属性配置错误", iedObj.Name, ap.Name, ap.Desc, ld.Inst, gooseitem.Name) r := map[string]interface{}{"scdid": c.ScdID, "lineno": gooseitem.Lineno, "ruleid": c.getRuleIdByName("GOOSE控制块的confRev属性配置错误(缺失、值为空)"), "nodeid": iedObj.NodeId, "parse_result": parse_result} c.AppendPaseResult(r, iedObj) } //是否配置网络参数 gseNetFound := false for _, net := range c.scdXmlObject.Communication.SubNetwork { if net.Type != gooseitem.Type && net.Type != "IECGOOSE" { continue } for _, connAp := range net.ConnectedAP { if connAp.IedName == iedObj.Name { for _, gseNet := range connAp.GSE { if gseNet.CbName == gooseitem.Name && gseNet.LdInst == ld.Inst { gseNetFound = true } } } } } if !gseNetFound { parse_result := fmt.Sprintf("IED(%s)中访问点(%s:%s)下的LD(%s)GOOSE控制块(%s)未配置网络参数", iedObj.Name, ap.Name, ap.Desc, ld.Inst, gooseitem.Name) r := map[string]interface{}{"scdid": c.ScdID, "lineno": gooseitem.Lineno, "ruleid": c.getRuleIdByName("GOOSE控制块未配置网络参数"), "nodeid": iedObj.NodeId, "parse_result": parse_result} c.AppendPaseResult(r, iedObj) } } } //SV控制块 smvitemMap := map[string]int{} for _, gooseitem := range ld.LN0.SampledValueControl { if smvitemMap[gooseitem.Name] == 1 { parse_result := fmt.Sprintf("IED(%s)中访问点(%s:%s)下的LD(%s)SV控制块(%s)name属性值在LN内重复配置", iedObj.Name, ap.Name, ap.Desc, ld.Inst, gooseitem.Name) r := map[string]interface{}{"scdid": c.ScdID, "lineno": gooseitem.Lineno, "ruleid": c.getRuleIdByName("SV控制块的name属性值在LN内重复配置"), "nodeid": iedObj.NodeId, "parse_result": parse_result} c.AppendPaseResult(r, iedObj) continue } smvitemMap[gooseitem.Name] = 1 if datsetList[gooseitem.DatSet] == nil { parse_result := fmt.Sprintf("IED(%s)中访问点(%s:%s)下的LD(%s)SV控制块(%s)引用的数据集(%s)不存在", iedObj.Name, ap.Name, ap.Desc, ld.Inst, gooseitem.Name, gooseitem.DatSet) r := map[string]interface{}{"scdid": c.ScdID, "lineno": gooseitem.Lineno, "ruleid": c.getRuleIdByName("SV控制块引用的数据集不存在"), "nodeid": iedObj.NodeId, "parse_result": parse_result} c.AppendPaseResult(r, iedObj) } if gooseitem.SmvID == "" { parse_result := fmt.Sprintf("IED(%s)中访问点(%s:%s)下的LD(%s)SV控制块(%s)smvID属性配置错误", iedObj.Name, ap.Name, ap.Desc, ld.Inst, gooseitem.Name) r := map[string]interface{}{"scdid": c.ScdID, "lineno": gooseitem.Lineno, "ruleid": c.getRuleIdByName("SV控制块的smvID属性配置错误(缺失、值为空)"), "nodeid": iedObj.NodeId, "parse_result": parse_result} c.AppendPaseResult(r, iedObj) } else { if tmpIED, h := smvAppidMap.Load(gooseitem.SmvID); h { iedPointer := tmpIED.(*node_attr.NIED) parse_result := fmt.Sprintf("IED(%s)中访问点(%s:%s)下的LD(%s)SV控制块(%s)的smvID(%s)与IED(%s)中重复定义", iedObj.Name, ap.Name, ap.Desc, ld.Inst, gooseitem.Name, gooseitem.SmvID, iedPointer.Name) r := map[string]interface{}{"scdid": c.ScdID, "lineno": gooseitem.Lineno, "ruleid": c.getRuleIdByName("SV控制块的smvID唯一性"), "nodeid": iedObj.NodeId, "parse_result": parse_result} c.AppendPaseResult(r, iedObj) } //格式校验 newGooseAppid := fmt.Sprintf("%s%s/LLN0.%s", iedObj.Name, ld.Inst, gooseitem.Name) if gooseitem.SmvID != newGooseAppid { parse_result := fmt.Sprintf("IED(%s)中访问点(%s:%s)下的LD(%s)SV控制块(%s)的appID(%s)格式不符合规范", iedObj.Name, ap.Name, ap.Desc, ld.Inst, gooseitem.Name, gooseitem.SmvID) r := map[string]interface{}{"scdid": c.ScdID, "lineno": gooseitem.Lineno, "ruleid": c.getRuleIdByName("SV控制块appID格式校验"), "nodeid": iedObj.NodeId, "parse_result": parse_result} c.AppendPaseResult(r, iedObj) } smvAppidMap.Store(gooseitem.SmvID, iedObj) if gooseitem.ConfRev == "" { parse_result := fmt.Sprintf("IED(%s)中访问点(%s:%s)下的LD(%s)SV控制块(%s)的confRev属性配置错误", iedObj.Name, ap.Name, ap.Desc, ld.Inst, gooseitem.Name) r := map[string]interface{}{"scdid": c.ScdID, "lineno": gooseitem.Lineno, "ruleid": c.getRuleIdByName("SV控制块的confRev属性配置错误(缺失、值为空)"), "nodeid": iedObj.NodeId, "parse_result": parse_result} c.AppendPaseResult(r, iedObj) } if gooseitem.SmpRate == "" { parse_result := fmt.Sprintf("IED(%s)中访问点(%s:%s)下的LD(%s)SV控制块(%s)的smpRate属性配置错误", iedObj.Name, ap.Name, ap.Desc, ld.Inst, gooseitem.Name) r := map[string]interface{}{"scdid": c.ScdID, "lineno": gooseitem.Lineno, "ruleid": c.getRuleIdByName("SV控制块的smpRate属性配置错误(缺失、值为空)"), "nodeid": iedObj.NodeId, "parse_result": parse_result} c.AppendPaseResult(r, iedObj) } if gooseitem.NofASDU == "" { parse_result := fmt.Sprintf("IED(%s)中访问点(%s:%s)下的LD(%s)SV控制块(%s)的nofASDU属性配置错误", iedObj.Name, ap.Name, ap.Desc, ld.Inst, gooseitem.Name) r := map[string]interface{}{"scdid": c.ScdID, "lineno": gooseitem.Lineno, "ruleid": c.getRuleIdByName("SV控制块的nofASDU属性配置错误(缺失、值为空)"), "nodeid": iedObj.NodeId, "parse_result": parse_result} c.AppendPaseResult(r, iedObj) } //是否配置网络参数 gseNetFound := false for _, net := range c.scdXmlObject.Communication.SubNetwork { if net.Type != "SMV" { continue } for _, connAp := range net.ConnectedAP { if connAp.IedName == iedObj.Name { for _, gseNet := range connAp.SMV { if gseNet.CbName == gooseitem.Name && gseNet.LdInst == ld.Inst { gseNetFound = true } } } } } if !gseNetFound { parse_result := fmt.Sprintf("IED(%s)中访问点(%s:%s)下的LD(%s)SV控制块(%s)未配置网络参数", iedObj.Name, ap.Name, ap.Desc, ld.Inst, gooseitem.Name) r := map[string]interface{}{"scdid": c.ScdID, "lineno": gooseitem.Lineno, "ruleid": c.getRuleIdByName("SV控制块未配置网络参数"), "nodeid": iedObj.NodeId, "parse_result": parse_result} c.AppendPaseResult(r, iedObj) } } } //日志控制块校验 logNameMap := map[string]int{} for _, logItem := range ld.LN0.LogControl { if logItem.DatSet == "" { parse_result := fmt.Sprintf("IED(%s)中访问点(%s:%s)下的LD(%s)日志控制块(%s)datSet属性配置错误(缺失、值为空)", iedObj.Name, ap.Name, ap.Desc, ld.Inst, logItem.Name) r := map[string]interface{}{"scdid": c.ScdID, "lineno": logItem.Lineno, "ruleid": c.getRuleIdByName("日志控制块的datSet属性配置错误(缺失、值为空)"), "nodeid": iedObj.NodeId, "parse_result": parse_result} c.AppendPaseResult(r, iedObj) } else { logDatsetFound := false for _, dsitem := range ld.LN0.DataSet { if dsitem.Name == logItem.DatSet { logDatsetFound = true break } } if !logDatsetFound { parse_result := fmt.Sprintf("IED(%s)中访问点(%s:%s)下的LD(%s)日志控制块(%s)引用的数据集(%s)不存在", iedObj.Name, ap.Name, ap.Desc, ld.Inst, logItem.Name, logItem.DatSet) r := map[string]interface{}{"scdid": c.ScdID, "lineno": logItem.Lineno, "ruleid": c.getRuleIdByName("日志控制块引用的数据集不存在"), "nodeid": iedObj.NodeId, "parse_result": parse_result} c.AppendPaseResult(r, iedObj) } } if logItem.IntgPd == "" { parse_result := fmt.Sprintf("IED(%s)中访问点(%s:%s)下的LD(%s)日志控制块(%s)intgPd属性配置错误(缺失、值为空)", iedObj.Name, ap.Name, ap.Desc, ld.Inst, logItem.Name) r := map[string]interface{}{"scdid": c.ScdID, "lineno": logItem.Lineno, "ruleid": c.getRuleIdByName("日志控制块的intgPd属性配置错误(缺失、值为空)"), "nodeid": iedObj.NodeId, "parse_result": parse_result} c.AppendPaseResult(r, iedObj) } logLdnameFound := false for _, logLd := range ap.Server.LDevice { if logLd.Inst == logItem.LogName { logLdnameFound = true break } } if !logLdnameFound { parse_result := fmt.Sprintf("IED(%s)中访问点(%s:%s)下的LD(%s)日志控制块(%s)对应的日志名(%s)不存在", iedObj.Name, ap.Name, ap.Desc, ld.Inst, logItem.Name, logItem.LogName) r := map[string]interface{}{"scdid": c.ScdID, "lineno": logItem.Lineno, "ruleid": c.getRuleIdByName("日志控制块对应的日志名不存在"), "nodeid": iedObj.NodeId, "parse_result": parse_result} c.AppendPaseResult(r, iedObj) } if _, h := logNameMap[logItem.Name]; h { parse_result := fmt.Sprintf("IED(%s)中访问点(%s:%s)下的LD(%s)日志控制块的name(%s)属性在LN下配置重复", iedObj.Name, ap.Name, ap.Desc, ld.Inst, logItem.Name) r := map[string]interface{}{"scdid": c.ScdID, "lineno": logItem.Lineno, "ruleid": c.getRuleIdByName("日志控制块的name属性在LN下配置重复"), "nodeid": iedObj.NodeId, "parse_result": parse_result} c.AppendPaseResult(r, iedObj) } logNameMap[logItem.Name] = 1 } //报告控制块校验 reportNameMap := map[string]int{} for _, reportItem := range ld.LN0.ReportControl { if reportItem.DatSet != "" { datsetFound := false for _, dsitem := range ld.LN0.DataSet { if dsitem.Name == reportItem.DatSet { datsetFound = true break } } if !datsetFound { parse_result := fmt.Sprintf("IED(%s)中访问点(%s:%s)下的LD(%s)报告控制块(%s)引用的数据集(%s)不存在", iedObj.Name, ap.Name, ap.Desc, ld.Inst, reportItem.Name, reportItem.DatSet) r := map[string]interface{}{"scdid": c.ScdID, "lineno": reportItem.Lineno, "ruleid": c.getRuleIdByName("报告控制块引用的数据集不存在"), "nodeid": iedObj.NodeId, "parse_result": parse_result} c.AppendPaseResult(r, iedObj) } } if reportItem.RptID == "" { parse_result := fmt.Sprintf("IED(%s)中访问点(%s:%s)下的LD(%s)报告控制块(%s)中的rptID属性配置错误(缺失、值为空)", iedObj.Name, ap.Name, ap.Desc, ld.Inst, reportItem.Name) r := map[string]interface{}{"scdid": c.ScdID, "lineno": reportItem.Lineno, "ruleid": c.getRuleIdByName("报告控制块的rptID属性配置错误(缺失、值为空)"), "nodeid": iedObj.NodeId, "parse_result": parse_result} c.AppendPaseResult(r, iedObj) } else { if v, h := reportRptIDMap.Load(iedObj.Name + reportItem.RptID); h { v1 := v.(map[string]interface{}) parse_result := fmt.Sprintf("IED(%s)中访问点(%s:%s)下的LD(%s)报告控制块(%s)属性rptID(%s)与IED(%s)中报告控制块(%s)rptID重复", iedObj.Name, ap.Name, ap.Desc, ld.Inst, reportItem.Name, reportItem.RptID, tools.IsEmpty(v1["iedname"]), tools.IsEmpty(v1["reportcontrol"])) r := map[string]interface{}{"scdid": c.ScdID, "lineno": reportItem.Lineno, "ruleid": c.getRuleIdByName("报告控制块的rptID重复"), "nodeid": iedObj.NodeId, "parse_result": parse_result} c.AppendPaseResult(r, iedObj) } reportRptIDMap.Store(iedObj.Name+reportItem.RptID, map[string]interface{}{"iedname": iedObj.Name, "reportcontrol": reportItem.Name}) } if reportItem.ConfRev == "" { parse_result := fmt.Sprintf("IED(%s)中访问点(%s:%s)下的LD(%s)报告控制块(%s)中的confRev属性配置错误(缺失、值为空)", iedObj.Name, ap.Name, ap.Desc, ld.Inst, reportItem.Name) r := map[string]interface{}{"scdid": c.ScdID, "lineno": reportItem.Lineno, "ruleid": c.getRuleIdByName("报告控制块的confRev属性配置错误(缺失、值为空)"), "nodeid": iedObj.NodeId, "parse_result": parse_result} c.AppendPaseResult(r, iedObj) } if _, h := reportNameMap[iedObj.Name+reportItem.Name]; h { parse_result := fmt.Sprintf("IED(%s)中访问点(%s:%s)下的LD(%s)报告控制块的name(%s)属性在LN下配置重复", iedObj.Name, ap.Name, ap.Desc, ld.Inst, reportItem.Name) r := map[string]interface{}{"scdid": c.ScdID, "lineno": reportItem.Lineno, "ruleid": c.getRuleIdByName("报告控制块的name属性在LN下配置重复"), "nodeid": iedObj.NodeId, "parse_result": parse_result} c.AppendPaseResult(r, iedObj) } reportNameMap[iedObj.Name+reportItem.Name] = 1 if len(reportItem.Name) >= 4 && (reportItem.Name[0:4] == "brcb" || reportItem.Name[0:4] == "urcb") { //名称符合规范 } else { parse_result := fmt.Sprintf("IED(%s)中访问点(%s:%s)下的LD(%s)报告控制块的name(%s)属性未按规范命名", iedObj.Name, ap.Name, ap.Desc, ld.Inst, reportItem.Name) r := map[string]interface{}{"scdid": c.ScdID, "lineno": reportItem.Lineno, "ruleid": c.getRuleIdByName("报告控制块属性正确性及规范性检查"), "nodeid": iedObj.NodeId, "parse_result": parse_result} c.AppendPaseResult(r, iedObj) } } //定值控制块校验 if ld.LN0.SettingControl != nil { if ld.LN0.SettingControl.ActSG == "" { parse_result := fmt.Sprintf("IED(%s)中访问点(%s:%s)下的LD(%s)定值控制块actSG属性配置值不正确", iedObj.Name, ap.Name, ap.Desc, ld.Inst) r := map[string]interface{}{"scdid": c.ScdID, "lineno": ld.LN0.SettingControl.Lineno, "ruleid": c.getRuleIdByName("定值控制块的actSG属性配置错误(值不正确)"), "nodeid": iedObj.NodeId, "parse_result": parse_result} c.AppendPaseResult(r, iedObj) } if ld.LN0.SettingControl.NumOfSGs == "" { parse_result := fmt.Sprintf("IED(%s)中访问点(%s:%s)下的LD(%s)定值控制块numOfSGs属性配置值不正确", iedObj.Name, ap.Name, ap.Desc, ld.Inst) r := map[string]interface{}{"scdid": c.ScdID, "lineno": ld.LN0.SettingControl.Lineno, "ruleid": c.getRuleIdByName("定值控制块的numOfSGs属性配置错误(值不正确)"), "nodeid": iedObj.NodeId, "parse_result": parse_result} c.AppendPaseResult(r, iedObj) } } } lphdFound := false otherLnFound := false for _, lns := range ld.LN { if _, h := c.lnodeTypeMap.Load(lns.LnType); !h { parse_result := fmt.Sprintf("IED(%s)中访问点(%s:%s)中LD(%s)下的LN(%s)引用的LNodeType(%s)在模板中不存在", iedObj.Name, ap.Name, ap.Desc, ld.Inst, lns.Desc, lns.LnType) r := map[string]interface{}{"scdid": c.ScdID, "lineno": lns.Lineno, "ruleid": c.getRuleIdByName("LN引用LNodeType不存在"), "nodeid": iedObj.NodeId, "parse_result": parse_result} c.AppendPaseResult(r, iedObj) } if lns.LnClass == "LPHD" { lphdFound = true } else { otherLnFound = true } } if !lphdFound { parse_result := fmt.Sprintf("IED(%s)中访问点(%s:%s)中LD(%s)的缺失对象LPHD", iedObj.Name, ap.Name, ap.Desc, ld.Inst) r := map[string]interface{}{"scdid": c.ScdID, "lineno": ld.Lineno, "ruleid": c.getRuleIdByName("逻辑设备建模正确性"), "nodeid": iedObj.NodeId, "parse_result": parse_result} c.AppendPaseResult(r, iedObj) } if !otherLnFound { parse_result := fmt.Sprintf("IED(%s)中访问点(%s:%s)中LD(%s)的缺失非LPHD外其他LN", iedObj.Name, ap.Name, ap.Desc, ld.Inst) r := map[string]interface{}{"scdid": c.ScdID, "lineno": ld.Lineno, "ruleid": c.getRuleIdByName("逻辑设备建模正确性"), "nodeid": iedObj.NodeId, "parse_result": parse_result} c.AppendPaseResult(r, iedObj) } } } } } //校验IED crc一致性 func (c *ScdNodeRule) CheckFunc_crc() { defer func() { c.isRun = 2 //校验完成标识 c.Flush() //将校验结果写数据库 }() logger.Logger.Debug(fmt.Sprintf("校验SCD %d的IED Crc", c.ScdID)) if c.scdXmlObject == nil { return } checkcnt := 1 timeoutTotal := len(c.scdXmlObject.IED) * 3 for { //检查crc提取是否完成 if v, h := global.IedCrcMakeState.Load(fmt.Sprintf("crc_%d", c.ScdID)); h { v1 := tools.IsEmpty(v) if v1 == "0" || v1 == "2" { //提取失败0或者已完成2 break } } time.Sleep(500 * time.Millisecond) checkcnt = checkcnt + 1 if checkcnt > timeoutTotal { //crc提取超时 logger.Logger.Error(errors.New(fmt.Sprintf("SCD %d的IED Crc提取超时", c.ScdID))) break } } crclist, _ := global.CachedScdCrc.Load(fmt.Sprintf("crc_%d", c.ScdID)) if crclist == nil { logger.Logger.Debug(fmt.Sprintf("未找到SCD %d的IED Crc提取结果数据", c.ScdID)) return } crcmap := crclist.(map[string]string) for _, iedObj := range c.scdXmlObject.IED { crc := "" p := new(node_attr.NPrivate) for _, p = range iedObj.Priavate { if p.Type == "IED virtual terminal conection CRC" { crc = p.InnerText break } } iedcrc2 := crcmap[iedObj.Name] if crc != "" && iedcrc2 != crc { parse_result := fmt.Sprintf("IED(%s)中存储的CCD校验码(%s)与实际校验码(%s)不一致", iedObj.Name, crc, iedcrc2) r := map[string]interface{}{"scdid": c.ScdID, "lineno": p.Lineno, "ruleid": c.getRuleIdByName("CCD校验码校验"), "nodeid": c.scdXmlObject.DataTypeTemplates.NodeId, "parse_result": parse_result} c.AppendPaseResult(r, iedObj) } } } func (c *ScdNodeRule) check_ln_doidai(iedObj *node_attr.NIED, dATypeMap map[string]*node_attr.NDAType, ld *node_attr.NLDevice) { if c.daMap == nil { c.daMap = &sync.Map{} } if c.doiMap == nil { c.doiMap = &sync.Map{} } if ld.LN0 != nil { vlnnode, _ := c.lnodeTypeMap.Load(ld.LN0.LnType) if vlnnode == nil { parse_result := fmt.Sprintf("IED(%s)中LD(%s%s)下的LN(%s%s)引用的LNodeType(%s)在模板中不存在", iedObj.Name, ld.Desc, ld.Inst, ld.LN0.Desc, ld.LN0.Inst, ld.LN0.LnType) r := map[string]interface{}{"scdid": c.ScdID, "lineno": ld.LN0.Lineno, "ruleid": c.getRuleIdByName("LN引用LNodeType不存在"), "nodeid": iedObj.NodeId, "parse_result": parse_result} c.AppendPaseResult(r, iedObj) } else { lnnode := vlnnode.(*node_attr.NLNodeType) lnchilderdoi := map[string]string{} v_lnchilderdoi, _ := c.doiMap.Load(ld.LN0.LnType) if v_lnchilderdoi == nil { for _, do := range lnnode.DO { lnchilderdoi[do.Name] = do.Type } c.doiMap.Store(ld.LN0.LnType, lnchilderdoi) } else { lnchilderdoi = v_lnchilderdoi.(map[string]string) } for _, doi := range ld.LN0.DOI { if lnchilderdoi[doi.Name] == "" { //fmt.Println(fmt.Sprintf("ied:%s LN0.LnType:%s doi.Name:%s %+v", iedObj.Name, ld.LN0.LnType, doi.Name, lnchilderdoi)) parse_result := fmt.Sprintf("IED(%s)中LD(%s%s)下的LN(%s%s)引用的DOI/SDI(%s)在模板中不存在", iedObj.Name, ld.Desc, ld.Inst, ld.LN0.Desc, ld.LN0.Inst, doi.Name) r := map[string]interface{}{"scdid": c.ScdID, "lineno": doi.Lineno, "ruleid": c.getRuleIdByName("DOI/SDI引用模板不一致"), "nodeid": iedObj.NodeId, "parse_result": parse_result} c.AppendPaseResult(r, iedObj) } } } } for _, ln := range ld.LN { vlnnode, _ := c.lnodeTypeMap.Load(ln.LnType) if vlnnode == nil { parse_result := fmt.Sprintf("IED(%s)中LD(%s%s)下的LN(%s%s)引用的LNodeType(%s)在模板中不存在", iedObj.Name, ld.Desc, ld.Inst, ln.Desc, ln.Inst, ln.LnType) r := map[string]interface{}{"scdid": c.ScdID, "lineno": ln.Lineno, "ruleid": c.getRuleIdByName("LN引用LNodeType不存在"), "nodeid": iedObj.NodeId, "parse_result": parse_result} c.AppendPaseResult(r, iedObj) } else { lnnode := vlnnode.(*node_attr.NLNodeType) lnchilderdoi := map[string]string{} v_lnchilderdoi, _ := c.doiMap.Load(ln.LnType) if v_lnchilderdoi == nil { for _, do := range lnnode.DO { lnchilderdoi[do.Name] = do.Type } c.doiMap.Store(ln.LnType, lnchilderdoi) } else { lnchilderdoi = v_lnchilderdoi.(map[string]string) } for _, doi := range ln.DOI { if lnchilderdoi[doi.Name] == "" { //fmt.Println(fmt.Sprintf("ied:%s LN.LnType:%s doi.Name:%s %+v", iedObj.Name, ln.LnType, doi.Name, lnchilderdoi)) parse_result := fmt.Sprintf("IED(%s)中LD(%s%s)下的LN(%s%s)引用的DOI/SDI(%s)在模板中不存在", iedObj.Name, ld.Desc, ld.Inst, ln.Desc, ln.Inst, doi.Name) r := map[string]interface{}{"scdid": c.ScdID, "lineno": doi.Lineno, "ruleid": c.getRuleIdByName("DOI/SDI引用模板不一致"), "nodeid": iedObj.NodeId, "parse_result": parse_result} c.AppendPaseResult(r) continue } dotype := lnchilderdoi[doi.Name] dalist := map[string]string{} v_dalist, _ := c.daMap.Load(dotype) if v_dalist == nil { v, _ := c.doTypeMap.Load(dotype) if v == nil { //do模板不存在 continue } for _, daitem := range v.(*node_attr.NDOType).DA { dalist[daitem.Name] = "1" } c.daMap.Store(dotype, dalist) } else { dalist = v_dalist.(map[string]string) } for _, da := range doi.DAI { if dalist[da.Name] == "" { //fmt.Println(fmt.Sprintf("IED(%s)中LD(%s%s)下的LN(%s%s)引用的DAI(%s) %+v", iedObj.Name, ld.Desc, ld.Inst, ln.Desc, ln.Inst, da.Name, daMap[dotype])) parse_result := fmt.Sprintf("IED(%s)中LD(%s%s)下的LN(%s%s)引用的DAI(%s)在模板中不存在", iedObj.Name, ld.Desc, ld.Inst, ln.Desc, ln.Inst, da.Name) r := map[string]interface{}{"scdid": c.ScdID, "lineno": da.Lineno, "ruleid": c.getRuleIdByName("DAI引用模板不一致"), "nodeid": iedObj.NodeId, "parse_result": parse_result} c.AppendPaseResult(r, iedObj) } } } } } } //DataTypeTemplates校验 //数据实例引用模板正确性检查 //数据类型模板引用正确性检查 //模板重复定义校验 //冗余模板校验 func (c *ScdNodeRule) CheckFunc_datatypetemplates(para orm.Params) (a3 map[string]*node_attr.NDAType) { logger.Logger.Debug(fmt.Sprintf("校验SCD %d的数据模板节点", c.ScdID)) if c.scdXmlObject == nil { return } usedLNodeTypeMap := map[string]int{} for _, iedObj := range c.scdXmlObject.IED { for _, acc := range iedObj.AccessPoint { if acc.Server == nil { continue } for _, ld := range acc.Server.LDevice { usedLNodeTypeMap[ld.LN0.LnType] = 1 for _, ln := range ld.LN { usedLNodeTypeMap[ln.LnType] = 1 } } } } usedDoTypeMap := map[string]int{} daTypeMap := map[string]*node_attr.NDAType{} usedDaTypeMap := map[string]int{} enumTypeMap := map[string]*node_attr.NEnumType{} usedEnumTypeMap := map[string]int{} for _, enumtype := range c.scdXmlObject.DataTypeTemplates.EnumType { if enumTypeMap[enumtype.Id] != nil { parse_result := fmt.Sprintf("EnumType(%s)定义重复", enumtype.Id) r := map[string]interface{}{"scdid": c.ScdID, "lineno": enumtype.Lineno, "ruleid": c.getRuleIdByName("模板重复定义校验"), "nodeid": c.scdXmlObject.DataTypeTemplates.NodeId, "parse_result": parse_result} c.AppendPaseResult(r) continue } enumTypeMap[enumtype.Id] = enumtype } for _, datype := range c.scdXmlObject.DataTypeTemplates.DAType { if daTypeMap[datype.Id] != nil { parse_result := fmt.Sprintf("DAType(%s)定义重复", datype.Id) r := map[string]interface{}{"scdid": c.ScdID, "lineno": datype.Lineno, "ruleid": c.getRuleIdByName("模板重复定义校验"), "nodeid": c.scdXmlObject.DataTypeTemplates.NodeId, "parse_result": parse_result} c.AppendPaseResult(r) continue } daTypeMap[datype.Id] = datype c.daTypeMap.Store(datype.Id, datype) for _, enum := range datype.BDA { if enum.Type == "" { continue } if enum.BType == "Enum" { usedEnumTypeMap[enum.Type] = 1 //使用的模板 if enumTypeMap[enum.Type] == nil { parse_result := fmt.Sprintf("DAType(%s)中引用的EnumType(%s)不存在", datype.Id, enum.Type) r := map[string]interface{}{"scdid": c.ScdID, "lineno": enum.Lineno, "ruleid": c.getRuleIdByName("EnumType模板引用合法性校验"), "nodeid": c.scdXmlObject.DataTypeTemplates.NodeId, "parse_result": parse_result} c.AppendPaseResult(r) } } } } for _, dotype := range c.scdXmlObject.DataTypeTemplates.DOType { if v, _ := c.doTypeMap.Load(dotype.Id); v != nil { parse_result := fmt.Sprintf("DOType(%s)定义重复", dotype.Id) r := map[string]interface{}{"scdid": c.ScdID, "lineno": dotype.Lineno, "ruleid": c.getRuleIdByName("模板重复定义校验"), "nodeid": c.scdXmlObject.DataTypeTemplates.NodeId, "parse_result": parse_result} c.AppendPaseResult(r) continue } c.doTypeMap.Store(dotype.Id, dotype) for _, da := range dotype.DA { if da.Type == "" { continue } if da.BType == "Enum" { usedEnumTypeMap[da.Type] = 1 //使用的模板 if enumTypeMap[da.Type] == nil { parse_result := fmt.Sprintf("DOType(%s)中引用的btype为Enum的DAType(%s)不存在", dotype.Id, da.Type) r := map[string]interface{}{"scdid": c.ScdID, "lineno": da.Lineno, "ruleid": c.getRuleIdByName("EnumType模板引用合法性校验"), "nodeid": c.scdXmlObject.DataTypeTemplates.NodeId, "parse_result": parse_result} c.AppendPaseResult(r) } } else { usedDaTypeMap[da.Type] = 1 if daTypeMap[da.Type] == nil { parse_result := fmt.Sprintf("DOType(%s)中引用的DAType(%s)不存在", dotype.Id, da.Type) r := map[string]interface{}{"scdid": c.ScdID, "lineno": da.Lineno, "ruleid": c.getRuleIdByName("DaType模板引用合法性校验"), "nodeid": c.scdXmlObject.DataTypeTemplates.NodeId, "parse_result": parse_result} c.AppendPaseResult(r) } } } } //查找未被引用的enum模板 for enumtype, obj := range enumTypeMap { if _, h := usedEnumTypeMap[enumtype]; !h { parse_result := fmt.Sprintf("EnumType(%s)未被引用", enumtype) r := map[string]interface{}{"scdid": c.ScdID, "lineno": obj.Lineno, "ruleid": c.getRuleIdByName("冗余模板校验"), "nodeid": c.scdXmlObject.DataTypeTemplates.NodeId, "parse_result": parse_result} c.AppendPaseResult(r) } } //查找未被引用的da模板 //fmt.Println(fmt.Sprintf("%+v", daTypeMap)) for datype, obj := range daTypeMap { if _, h := usedDaTypeMap[datype]; !h { parse_result := fmt.Sprintf("DAType(%s)未被引用", datype) r := map[string]interface{}{"scdid": c.ScdID, "lineno": obj.Lineno, "ruleid": c.getRuleIdByName("冗余模板校验"), "nodeid": c.scdXmlObject.DataTypeTemplates.NodeId, "parse_result": parse_result} c.AppendPaseResult(r) } } for _, lntype := range c.scdXmlObject.DataTypeTemplates.LNodeType { if v, _ := c.lnodeTypeMap.Load(lntype.Id); v != nil { parse_result := fmt.Sprintf("LNodeType(%s)定义重复", lntype.Id) r := map[string]interface{}{"scdid": c.ScdID, "lineno": lntype.Lineno, "ruleid": c.getRuleIdByName("模板重复定义校验"), "nodeid": c.scdXmlObject.DataTypeTemplates.NodeId, "parse_result": parse_result} c.AppendPaseResult(r) continue } //查找未被引用的LNodeType模板 if _, h := usedLNodeTypeMap[lntype.Id]; !h { parse_result := fmt.Sprintf("LNodeType(%s)未被引用", lntype.Id) r := map[string]interface{}{"scdid": c.ScdID, "lineno": lntype.Lineno, "ruleid": c.getRuleIdByName("冗余模板校验"), "nodeid": c.scdXmlObject.DataTypeTemplates.NodeId, "parse_result": parse_result} c.AppendPaseResult(r) continue } c.lnodeTypeMap.Store(lntype.Id, lntype) for _, do := range lntype.DO { if do.Type == "" { continue } usedDoTypeMap[do.Type] = 1 if v, _ := c.doTypeMap.Load(do.Type); v == nil { parse_result := fmt.Sprintf("LNodeType(%s)中引用的DoType(%s)不存在", lntype.Id, do.Type) r := map[string]interface{}{"scdid": c.ScdID, "lineno": do.Lineno, "ruleid": c.getRuleIdByName("DoType模板引用合法性校验"), "nodeid": c.scdXmlObject.DataTypeTemplates.NodeId, "parse_result": parse_result} c.AppendPaseResult(r) } } } //查找未被引用的DOtype模板 c.doTypeMap.Range(func(k, v any) bool { dotype := tools.IsEmpty(k) if _, h := usedDoTypeMap[dotype]; !h { parse_result := fmt.Sprintf("DOType(%s)未被引用", dotype) r := map[string]interface{}{"scdid": c.ScdID, "lineno": v.(*node_attr.NDOType).Lineno, "ruleid": c.getRuleIdByName("冗余模板校验"), "nodeid": c.scdXmlObject.DataTypeTemplates.NodeId, "parse_result": parse_result} c.AppendPaseResult(r) } return true }) return daTypeMap } //发送端虚端子检查 //存在性检查 //cdc检查 // func (c *ScdNodeRule) IedFcdaExist(iedname, ldinst, lnclass, lninst, prefix, doname, daname string) (obj interface{}, cdc string) { if c.scdXmlObject == nil { return nil, "" } donames := strings.Split(doname, ".") //检查是否是sdi定义 doname = donames[0] sdiname := "" if len(donames) > 1 { sdiname = donames[1] } for _, iedObj := range c.scdXmlObject.IED { if iedObj.Name != iedname { continue } for _, ap := range iedObj.AccessPoint { if ap.Server == nil { continue } for _, ld := range ap.Server.LDevice { if ld.Inst != ldinst { continue } if ld.LN0 != nil && lnclass == ld.LN0.LnClass && lninst == ld.LN0.Inst && prefix == ld.LN0.Prefix { for _, doi := range ld.LN0.DOI { if doi.Name == doname { if sdiname != "" { foundsdi := false for _, sdi := range doi.SDI { if sdi.Name == sdiname { foundsdi = true } } if !foundsdi { return nil, "" } } cdc := c.getDoICdc(ld.LN0.LnType, doname, daname) return doi, cdc } } } for _, ln := range ld.LN { if lnclass == ln.LnClass && lninst == ln.Inst && prefix == ln.Prefix { for _, doi := range ln.DOI { if doi.Name == doname { if sdiname != "" { foundsdi := false for _, sdi := range doi.SDI { if sdi.Name == sdiname { foundsdi = true } } if !foundsdi { return nil, "" } } cdc := c.getDoICdc(ln.LnType, doname, daname) return doi, cdc } } } } } } } return nil, "" } func (c *ScdNodeRule) iedIntAddrExist(iedname, intAddr string) (obj interface{}) { if c.scdXmlObject == nil { return nil } //intaddr格式:2-B:PIGO1/GOINGGIO1.SPCSO8.stVal addrs := strings.Split(intAddr, ":") if len(addrs) == 2 { intAddr = addrs[1] } intAddrs := strings.Split(intAddr, "/") if len(intAddrs) == 1 { logger.Logger.Error(errors.New(fmt.Sprintf("装置%s发现无效的intAddr:%s", iedname, intAddr))) return nil } ldinst := intAddrs[0] lnstr := intAddrs[1] v_tmpAry := strings.Split(lnstr, ".") if len(v_tmpAry) < 2 { logger.Logger.Error(errors.New(fmt.Sprintf("装置%s发现无效的intAddr:%s", iedname, intAddr))) return nil } doda := v_tmpAry[1:] //doname := "" daname := "" if len(doda) == 1 { //只有do //doname = doda[0] } else { //doname = doda[0] daname = strings.Join(doda[1:], ".") //还原da } for _, iedObj := range c.scdXmlObject.IED { if iedObj.Name != iedname { continue } for _, ap := range iedObj.AccessPoint { if ap.Server != nil { for _, ld := range ap.Server.LDevice { if ld.Inst == ldinst && ld.LN0 != nil { for _, ln := range ld.LN0.DataSet { for _, fcda := range ln.FCDA { str := fmt.Sprintf("%s%s%s.%s", fcda.Prefix, fcda.LnClass, fcda.LnInst, fcda.DoName) if daname != "" { str = str + "." + fcda.DaName } if str == lnstr { return fcda } } } return nil } } } } } return nil } //校验内部端子的DO或DA实例是否存在 func (c *ScdNodeRule) iedIntAddrDoiOrDaiExist(iedname, intAddr string) (obj interface{}, cdc string) { if c.scdXmlObject == nil { return nil, "" } //intaddr格式:2-B:PIGO1/GOINGGIO1.SPCSO8.stVal addrs := strings.Split(intAddr, ":") if len(addrs) == 2 { intAddr = addrs[1] } intAddrs := strings.Split(intAddr, "/") if len(intAddrs) == 1 { logger.Logger.Error(errors.New(fmt.Sprintf("装置%s发现无效的intAddr:%s", iedname, intAddr))) return nil, "" } ldinst := intAddrs[0] lnstr := intAddrs[1] for _, iedObj := range c.scdXmlObject.IED { if iedObj.Name != iedname { continue } for _, ap := range iedObj.AccessPoint { if ap.Server != nil { for _, ld := range ap.Server.LDevice { if ld.Inst == ldinst { for _, ln := range ld.LN { str := fmt.Sprintf("%s%s%s", ln.Prefix, ln.LnClass, ln.Inst) v_tmpAry := strings.Split(lnstr, ".") if v_tmpAry[0] == str { doda := v_tmpAry[1:] doname := "" daname := "" if len(doda) == 1 { //只有do doname = doda[0] } else { doname = doda[0] daname = strings.Join(doda[1:], ".") //还原da } for _, doi := range ln.DOI { if doi.Name == doname { cdc := c.getDoICdc(ln.LnType, doi.Name, daname) if daname == "" { return doi, cdc } for _, dai := range doi.DAI { if dai.Name == daname { return dai, cdc } } das := strings.Split(daname, ".") if len(das) > 1 { for _, sdi := range doi.SDI { if sdi.Name == das[0] { for _, dai := range sdi.DAI { if dai.Name == das[1] { return dai, cdc } } for _, sdi2 := range sdi.SDI { for _, dai := range sdi2.DAI { if dai.Name == das[1] { return dai, cdc } } } return nil, cdc } } } return nil, cdc } } return nil, "" } } return nil, "" } } } } } return nil, "" } //根据LNtype和doi名称返回其cdc属性值 func (c *ScdNodeRule) getDoICdc(lntype, doiname, daname string) string { v, _ := c.lnodeTypeMap.Load(lntype) if v == nil { return "" } for _, do := range v.(*node_attr.NLNodeType).DO { if do.Name == doiname { if do.Type == "" { return "" } d1, _ := c.doTypeMap.Load(do.Type) if d1 == nil { return "" } if daname == "" { return d1.(*node_attr.NDOType).Cdc } das := strings.Split(daname, ".") //多层da for _, daitem := range d1.(*node_attr.NDOType).DA { if daitem.Name == das[0] { if len(das) == 1 { return daitem.BType } else { bdatype := daitem.Type da1, _ := c.daTypeMap.Load(bdatype) if da1 == nil { return "" } for _, dbaitem := range da1.(*node_attr.NDAType).BDA { if dbaitem.Name == das[1] { return dbaitem.BType } } } return "" } } } } return "" } //--------------------------------------------------------- func (c *ScdNodeRule) AppendPaseResult(r map[string]interface{}, ied ...*node_attr.NIED) { var iedobj *node_attr.NIED if len(ied) > 0 && ied[0] != nil { iedobj = ied[0] r["ied_name"] = iedobj.Name r["ied_desc"] = iedobj.Desc } c.checkFailListLock.Lock() c.CheckFailList = append(c.CheckFailList, r) c.checkFailListLock.Unlock() //logger.Logger.Info(fmt.Sprintf("========校验结果:%v", r)) //实时将结果通过mqtt发送解析结果 //结果处理客户端需要订阅/jujutong/scd_check_tools/ruleparse/{scdid} //go mqtt.PublishMessage("/jujutong/scd_check_tools/ruleparse/"+fmt.Sprintf("%d", c.ScdID), r["parse_result"].(string)) } //添加端子检查结果 func (c *ScdNodeRule) AppendFcdaCheckResult(r map[string]interface{}) { r["check_type"] = "fcda" c.checkFailListLock.Lock() c.CheckFailList = append(c.CheckFailList, r) c.checkFailListLock.Unlock() } func (c *ScdNodeRule) CheckFinish() { c.isRun = 2 } //节点规则验证完成 func (c *ScdNodeRule) Flush() { //判断等待验证队列中的数据是否已处理完,没有处理完则一直等待 if c.isRun != 2 { for { time.Sleep(100 * time.Millisecond) if c.isRun == 2 { break } } } logger.Logger.Debug(fmt.Sprintln("规则校验结果,共%d条!", len(c.CheckFailList))) c.writeNodeDB() } //根据规则名称获取规则ID.返回为""时表示还未定义该规则名称 func (c *ScdNodeRule) getRuleIdByName(name string) string { row := c.NodeRuleList[name] if row == nil { return "0" } return tools.IsEmpty(row["id"]) } //将验证结果写入数据库。仅写入验证不通过的结果 //每5000条写入一次 func (c *ScdNodeRule) writeNodeDB() { if len(c.CheckFailList) == 0 { return } db := orm.NewOrm() s1 := time.Now().Unix() nodeCols := "(scd_id,node_id,line_no,rule_target,rule_id,parse_result,ied_name,ied_desc)" fcdaCols := "(scd_id,ied_name,ied_desc,fcda_desc,fcda_addr,out_ied_name,out_ied_desc,out_fcda_desc,out_fcda_addr,rule_id,parse_result,error_type)" loadedRec := 0 nodeSqlValuesAry := []string{} fcdaSqlValuesAry := []string{} for _, m := range c.CheckFailList { check_type := tools.IsEmpty(m["check_type"]) // fcda检查结果写入t_scd_fcda_check_result if check_type == "fcda" { fcdaSqlValuesAry = append(fcdaSqlValuesAry, "("+tools.IsEmpty(m["scdid"])+",'"+tools.IsEmpty(m["ied_name"])+"','"+tools.IsEmpty(m["ied_desc"])+"','"+tools.IsEmpty(m["fcda_desc"])+"','"+tools.IsEmpty(m["fcda_addr"])+"','"+tools.IsEmpty(m["out_ied_name"])+"','"+tools.IsEmpty(m["out_ied_desc"])+"','"+tools.IsEmpty(m["out_fcda_desc"])+"','"+tools.IsEmpty(m["out_fcda_addr"])+"',"+tools.IsEmpty(m["ruleid"], "0")+",'"+tools.IsEmpty(m["parse_result"])+"',"+tools.IsEmpty(m["error_type"])+")") } else { nodeSqlValuesAry = append(nodeSqlValuesAry, "("+tools.IsEmpty(m["scdid"])+","+tools.IsEmpty(m["nodeid"], "0")+","+tools.IsEmpty(m["lineno"], "0")+",'"+tools.IsEmpty(m["rule_target"])+"',"+tools.IsEmpty(m["ruleid"], "0")+",'"+tools.IsEmpty(m["parse_result"])+"','"+tools.IsEmpty(m["ied_name"])+"','"+tools.IsEmpty(m["ied_desc"])+"')") } if len(nodeSqlValuesAry) == 5000 { sql := "insert into t_scd_node_rule_parse" + nodeCols + "values" + strings.Join(nodeSqlValuesAry, ",") _, err := db.Raw(sql).Exec() if err != nil { log.Println(err) } loadedRec = loadedRec + 5000 nodeSqlValuesAry = nil nodeSqlValuesAry = []string{} } if len(fcdaSqlValuesAry) == 5000 { sql := "insert into t_scd_fcda_check_result" + fcdaCols + "values" + strings.Join(nodeSqlValuesAry, ",") _, err := db.Raw(sql).Exec() if err != nil { log.Println(err) } loadedRec = loadedRec + 5000 fcdaSqlValuesAry = nil fcdaSqlValuesAry = []string{} } } if len(nodeSqlValuesAry) > 0 { sql := "insert into t_scd_node_rule_parse" + nodeCols + "values" + strings.Join(nodeSqlValuesAry, ",") _, err := db.Raw(sql).Exec() if err != nil { log.Println(err) } nodeSqlValuesAry = nil } if len(fcdaSqlValuesAry) > 0 { sql := "insert into t_scd_fcda_check_result" + fcdaCols + "values" + strings.Join(nodeSqlValuesAry, ",") _, err := db.Raw(sql).Exec() if err != nil { log.Println(err) } fcdaSqlValuesAry = nil } c.CheckFailList = nil runtime.GC() s2 := time.Now().Unix() fmt.Println(fmt.Sprintf("===================Flush NodeAttrParse 完成!。耗时:%d秒", s2-s1)) fmt.Println("===========节点属性验证完成") mqtt.PublishMessage(fmt.Sprintf("%s/%d", checktopic, c.ScdID), `{"code":1,"scdid":"`+tools.IsEmpty(c.ScdID)+`","state":1,"msg":"该SCD校验完成"}`) global.CheckingScd.Delete(c.ScdID) }