package bo import ( "encoding/json" "encoding/xml" "errors" "fmt" "io/fs" "log" "os" "os/exec" "regexp" "runtime" "scd_check_tools/conf" "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管理 type ScdMgr struct { DeviceBaseModel //设置删除scd数据时是否是同步模式,同步模式下会等待数据删除完成时才返回结果;反之为后台删除模式.默认为异步删除模式 SyncDeleteMod bool } func (c *ScdMgr) IedTotal(stationid string) (int, error) { dblog := new(SystemLog) dblog.SetUserInfo(c.GetUserInfo()) dblog.Audittype = enum.AuditType_scd_show dblog.Logtype = enum.LogType_datastat dblog.Eventtype = enum.OptEventType_Bus dblog.Eventlevel = enum.OptEventLevel_Low db := orm.NewOrm() sql := "select count(0) cnt from t_scd_node_scl t,(select max(id) scd_id from t_scd_scl where enable=1 and station_id=?) t1 where t.scd_id=t1.scd_id and t.node_name='IED'" rowset := []orm.Params{} _, err := db.Raw(sql, stationid).Values(&rowset) dblog.Description = fmt.Sprintf("SQL:%s,参数:%+v", sql, stationid) if err != nil { logger.Logger.Error(err, dblog.Description) dblog.Fail2() return 0, err } dblog.Success2() if len(rowset) == 0 { return 0, nil } cnt, _ := strconv.Atoi(tools.IsEmpty(rowset[0]["cnt"])) return cnt, nil } func (c *ScdMgr) IedStat(param map[string]interface{}) (map[string]interface{}, error) { var result = map[string]interface{}{} result["data"] = []orm.Params{} constCode := new(Global) list, _ := constCode.GetChildrenGlobalCode("device_type") result["comments"] = list db := orm.NewOrm() sqlParams := []interface{}{} uaObj := new(UserAreaRelationObject) uaObj.SetUserInfo(c.GetUserInfo()) areaFilerWhere := uaObj.MakeAreaFilerIds("station_id", "and") scdSql := "select max(id) scd_id from t_scd_scl where enable=1 " + areaFilerWhere + " group by station_id" station_id := tools.IsEmpty(param["station_id"]) if station_id != "" { scdSql = "select max(id) scd_id from t_scd_scl where enable=1 and station_id=?" sqlParams = append(sqlParams, station_id) } sql := ` select t.ied_type,count(0) cnt from t_area_ied_relation t , (` + scdSql + `) t1 where t.scd_id=t1.scd_id GROUP BY t.ied_type` rowset := []orm.Params{} _, err := db.Raw(sql, sqlParams).Values(&rowset) dblog := new(SystemLog) dblog.SetUserInfo(c.GetUserInfo()) dblog.Audittype = enum.AuditType_scd_show dblog.Logtype = enum.LogType_datastat dblog.Eventtype = enum.OptEventType_Bus dblog.Eventlevel = enum.OptEventLevel_Low dblog.Description = fmt.Sprintf("SQL:%s,参数:%+v", sql, sqlParams) if err != nil { logger.Logger.Error(err, dblog.Description) dblog.Fail2() return nil, err } dblog.Success2() result["data"] = rowset return result, nil } func (c *ScdMgr) GetLastScd(stationid string) (orm.Params, error) { dblog := new(SystemLog) dblog.SetUserInfo(c.GetUserInfo()) dblog.Audittype = enum.AuditType_scd_show dblog.Logtype = enum.LogType_Query dblog.Eventtype = enum.OptEventType_Bus dblog.Eventlevel = enum.OptEventLevel_Low db := orm.NewOrm() lst := []orm.Params{} sql := "select * from t_scd_scl where station_id=? order by id desc limit 0,1" _, err := db.Raw(sql, stationid).Values(&lst) dblog.Description = fmt.Sprintf("SQL:%s,参数:%+v", sql, stationid) if err != nil { logger.Logger.Error(err, dblog.Description) dblog.Fail2() return nil, err } dblog.Success2() if len(lst) == 0 { return nil, nil } return lst[0], err } //指定scd解析完成 //isdefaultRun:是否立即启用为在运版本。1 立即启用 0 不启用 func (c *ScdMgr) ParseFinish(scdid int64, isdefaultRun ...int) { scdidStr := tools.IsEmpty(scdid) c.SetParseStateByID(scdidStr, 1) v_isdefaultRun := 0 if len(isdefaultRun) > 0 { v_isdefaultRun = isdefaultRun[0] } if v_isdefaultRun == 1 { //解除当前站下的所有scd锁定状态 c.SetLock("", scdidStr, 0) c.SetStateByID(scdidStr, 1) c.UpdateScdVersion(scdid, "", "", "") c.UpdateRtCrc(scdid) } go c.CrcCheck(scdidStr) } //获取并更新唯一校验码 func (c *ScdMgr) UpdateRtCrc(scdid int64) { scdobj, h := global.GoCahce.Get(tools.IsEmpty(scdid)) if !h { logger.Logger.Error(errors.New("未找到scd解析结果缓存!")) return } crc := "" for _, item := range scdobj.(*node_attr.SCL).Private { if item.Type == global.SCD_CheckoutCrcKey { crc = item.InnerText break } } if crc == "" { return } //更新scl的签入crc,该crc为通过本系统签出的scd才有 sql := "update t_scd_scl set in_rt_crc=? where scd_id=? " _, err := orm.NewOrm().Raw(sql, crc, scdid).Exec() if err != nil { logger.Logger.Error(err, fmt.Sprintf("SQL:%s 参数:%+v", sql, []interface{}{crc, scdid})) } } func (c *ScdMgr) SetStateByID(scdid string, isenable int) error { db := orm.NewOrm() _, err := db.Raw("update t_scd_scl set enable=? where id=?", isenable, scdid).Exec() return err } func (c *ScdMgr) SetParseStateByID(scdid string, isenable int) error { db := orm.NewOrm() _, err := db.Raw("update t_scd_scl set is_parse=? where id=?", isenable, scdid).Exec() return err } //锁定/解锁指定scd func (c *ScdMgr) SetLock(stationid, scdid string, islock int) error { dblog := new(SystemLog) dblog.SetUserInfo(c.GetUserInfo()) dblog.Audittype = enum.AuditType_scd_parse dblog.Logtype = enum.LogType_lock dblog.Eventtype = enum.OptEventType_Bus dblog.Eventlevel = enum.OptEventLevel_Hight db := orm.NewOrm() var err error sql := "" sqlParameters := []interface{}{} if scdid == "" { sql = "update t_scd_scl set checkout_lock=? where station_id=?" sqlParameters = []interface{}{islock, stationid} _, err = db.Raw(sql, sqlParameters).Exec() } else { sql = "update t_scd_scl set checkout_lock=? where station_id=? and id=?" sqlParameters = []interface{}{islock, stationid, scdid} _, err = db.Raw(sql, sqlParameters).Exec() } dblog.Description = fmt.Sprintf("SQL:%s,参数:%+v", sql, sqlParameters) if err != nil { logger.Logger.Error(err, dblog.Description) dblog.Fail2() return err } dblog.Success2() return nil } //锁定/解锁指定scd func (c *ScdMgr) IsDispose(scdid string) error { dblog := new(SystemLog) dblog.SetUserInfo(c.GetUserInfo()) dblog.Audittype = enum.AuditType_scd_parse dblog.Logtype = enum.LogType_unlock dblog.Eventtype = enum.OptEventType_Bus dblog.Eventlevel = enum.OptEventLevel_Hight db := orm.NewOrm() sql := "update t_scd_scl set checkout_lock=0 where id=?" _, err := db.Raw(sql, scdid).Exec() dblog.Description = fmt.Sprintf("SQL:%s,参数:%s", sql, scdid) if err != nil { logger.Logger.Error(err, dblog.Description) dblog.Fail2() return err } dblog.Success2() return err } //获取指定站下的已做签出锁定的scd信息 func (c *ScdMgr) GetCheckoutLockScd(stationid string) ([]orm.Params, error) { db := orm.NewOrm() sql := "select * from t_scd_scl where station_id=? and checkout_lock=1" rowset := []orm.Params{} _, err := db.Raw(sql, stationid).Values(&rowset) if err != nil { log.Println(err) return nil, err } return rowset, nil } //更新指定scd的版本号 func (c *ScdMgr) UpdateScdVersion(scdid int64, stationid, scdname, scdpath string) error { db := orm.NewOrm() var err error var err2 error db.Begin() if scdid > 0 { _, err = db.Raw("update t_scd_scl set checkout_lock=0,enable=0, version=replace(version,'在运版','历史版') where id=?", scdid).Exec() _, err2 = db.Raw("update t_scd_scl a set a.enable=1,a.version='在运版' where a.id=?", scdid).Exec() } else { fileFirstChar := scdpath[0:1] fline := string(os.PathSeparator) if fileFirstChar != "." { if fileFirstChar == fline { scdpath = "." + scdpath } else { scdpath = "." + fline + scdpath } } _, err = db.Raw("update t_scd_scl set checkout_lock=0,enable=0, version=replace(version,'在运版','历史版') where station_id=? and version like '在运版%'", stationid).Exec() _, err2 = db.Raw("update t_scd_scl a set a.enable=1, a.version='在运版' where a.station_id=? and a.scd_name=? and a.path=?", stationid, scdname, scdpath).Exec() } if err != nil || err2 != nil { db.Rollback() if err != nil { return err } if err2 != nil { return err2 } } db.Commit() return nil } func (c *ScdMgr) SetStateByNamePath(scdname, scdpath string, isenable int) error { fileFirstChar := scdpath[0:1] fline := string(os.PathSeparator) if fileFirstChar != "." { if fileFirstChar == fline { scdpath = "." + scdpath } else { scdpath = "." + fline + scdpath } } db := orm.NewOrm() _, err := db.Raw("update t_scd_scl set enable=? where scd_name=? and path=?", isenable, scdname, scdpath).Exec() return err } func (c *ScdMgr) List(param map[string]interface{}) ([]orm.Params, int, error) { c.ModelTableName = "t_scd_scl" db := orm.NewOrm() sql := "select t.*,u.name user_name,s.area_name station_name from " + c.ModelTableName + " t left join t_data_user u on t.CREATED_BY=u.id left join t_data_area s on t.station_id=s.id where 1=1 " where := []string{} sqlParas := []interface{}{} //状态默认查询 if v, ok := param["enable"]; ok { v1 := tools.IsEmpty(v) if v1 != "" { sqlParas = append(sqlParas, v1) where = append(where, "t.enable=?") } } if v, ok := param["ischeckinscd"]; ok { v1 := tools.IsEmpty(v) if v1 != "" { sqlParas = append(sqlParas, v1) where = append(where, "t.is_checkin_scd=?") } } if v, ok := param["stationid"]; ok { v1 := tools.IsEmpty(v) if v1 != "" { where = append(where, "t.station_id=?") sqlParas = append(sqlParas, v) } else { usObj := new(UserAreaRelationObject) usObj.SetUserInfo(c.GetUserInfo()) usScope := usObj.MakeAreaFilerWhere("t.station_id", "and") if usScope != "" { where = append(where, usScope) } } } if v, ok := param["id"]; ok { v1 := tools.IsEmpty(v) if v1 != "" { where = append(where, "t.id=?") sqlParas = append(sqlParas, v) } } if v, ok := param["name"]; ok { v1 := tools.IsEmpty(v) if v1 != "" { where = append(where, "t.name like ?") sqlParas = append(sqlParas, "%"+tools.IsEmpty(v)+"%") } } rowset := []orm.Params{} if len(where) > 0 { where = append([]string{""}, where...) } pageno, _ := strconv.Atoi(tools.IsEmpty(param["pageno"], "1")) pagesize, _ := strconv.Atoi(tools.IsEmpty(param["pagesize"], "100")) limit := fmt.Sprintf(" order by t.CREATED_TIME desc limit %d,%d", (pageno-1)*pagesize, pagesize) _, err := db.Raw(sql+strings.Join(where, " and ")+limit, sqlParas).Values(&rowset) totalCnt := 0 if err != nil { log.Println(err) } else { totalSql := "select count(1) cnt from " + c.ModelTableName + " t where 1=1 " + strings.Join(where, " and ") tmpRowset := []orm.Params{} _, err = db.Raw(totalSql, sqlParas).Values(&tmpRowset) if err != nil { logger.Logger.Error(err, fmt.Sprintf("SQL:%s 参数:%+v", totalSql, sqlParas)) return nil, 0, err } totalCnt, _ = strconv.Atoi(tools.IsEmpty(tmpRowset[0]["cnt"])) } return rowset, totalCnt, err } //获取指定scd的间隔信息 func (c *ScdMgr) GetAreaList(scdid string) ([]orm.Params, error) { db := orm.NewOrm() sql := "select * from t_substation_area where scdid=? order by voltage_level, id" rowset := []orm.Params{} _, err := db.Raw(sql, scdid).Values(&rowset) if err != nil { log.Println(err) return nil, err } return rowset, nil } func (c *ScdMgr) One(id string) (orm.Params, error) { key := fmt.Sprintf("scd_info_%s", id) if v, h := global.GoCahce.Get(key); h { return v.(orm.Params), nil } para := map[string]interface{}{"id": id} rowset, _, err := c.List(para) if err == nil && len(rowset) > 0 { global.GoCahce.Set(key, rowset[0], -1) return rowset[0], nil } return nil, err } //检查当前正在解析中的scd是否超过并发解析限制 //返回true表示当前并发数已满,系统不能接收新的解析请求;false表示允许进行解析新的scd //从2方面进行限制:一是设置的最大并发解析数 二是当前服务器性能指标大于80%时不接收新的解析 func (c *ScdMgr) CheckParseMaxLimit() (bool, string) { cpuHC, _ := strconv.Atoi(tools.IsEmpty(conf.GlobalConfig["alarm_cpu_value"], "90")) //cpu高耗用率 memHC, _ := strconv.Atoi(tools.IsEmpty(conf.GlobalConfig["alarm_mem_value"], "90")) //内存高耗用率 diskHCC, _ := strconv.Atoi(tools.IsEmpty(conf.GlobalConfig["alarm_disk_value"], "90")) //C磁盘高耗用率 //内存检查 if ishc, h := global.PerformanceRuntimeMonitorResult.Load("mem"); h { if ishc.(int) > memHC { return true, fmt.Sprintf("内存耗用率%d", ishc.(int)) } } if ishc, h := global.PerformanceRuntimeMonitorResult.Load("cpu"); h { if ishc.(int) > cpuHC { return true, fmt.Sprintf("CPU耗用率%d", ishc.(int)) } } if ishc, h := global.PerformanceRuntimeMonitorResult.Load("disk"); h { if ishc.(int) > diskHCC { return true, fmt.Sprintf("磁盘空间耗用率%d", ishc.(int)) } } scdparse_max_count, _ := GetSysParamValue("scdparse_max_count", "5") db := orm.NewOrm() sql := "select 'cnt' cnt, count(1) value from t_scd_scl where is_parse=0 and CREATED_TIME= scdparse_max_count { return true, fmt.Sprintf("正在解析SCD数为%d", tools.IsEmpty(result["value"])) } return false, "" } //根据scd名称和path返回scd信息 func (c *ScdMgr) OneByName(scdname, scdpath string) (orm.Params, error) { if scdpath == "" || scdname == "" { return nil, errors.New("无效的参数!") } fileFirstChar := scdpath[0:1] fline := string(os.PathSeparator) if fileFirstChar != "." { if fileFirstChar == fline { scdpath = "." + scdpath } else { scdpath = "." + fline + scdpath } } db := orm.NewOrm() rowset := []orm.Params{} _, err := db.Raw("select t.*,s.area_name station_id from t_scd_scl t,t_data_area s where t.station_id=s.id and t.scd_name=? and t.path=?", scdname, scdpath).Values(&rowset) if err == nil && len(rowset) > 0 { return rowset[0], nil } if err != nil { logger.Logger.Error(err) } return nil, err } //重新解析指定SCD func (c *ScdMgr) AginParse(scdid string) error { if scdid == "" { return errors.New("SCD编号不能为空") } if h, msg := c.CheckParseMaxLimit(); h { return errors.New("系统繁忙:" + msg + ",请稍候(约5分钟)再试") } //获取scd基本信息 db := orm.NewOrm() sql := "select * from t_scd_scl where id=? " rowset := []orm.Params{} db.Raw(sql, scdid).Values(&rowset) if len(rowset) == 0 { return errors.New("无效的SCD编号") } go func(row orm.Params) { db.Raw("update t_scd_scl set is_parse=0 where id=?", scdid).Exec() //先清除旧数据 c.SyncDeleteMod = true c.DeleteScd(scdid, false) //log.Println("====================清理完成") scdObj := GetScdParseInstance() ischeckinscd, _ := strconv.Atoi(tools.IsEmpty(row["is_checkin_scd"], "1")) scdObj.IsCheckinScd = ischeckinscd scdObj.Idseq, _ = strconv.ParseInt(scdid, 10, 64) scdObj.Idseq = scdObj.Idseq - 1 //新生成 的SCD ID是在该变量基础上加1,为了保持原ID不变,所以先减1 //log.Println(fmt.Sprintf("%v", rowset[0])) err := scdObj.XmlParse(tools.IsEmpty(row["station_id"]), tools.IsEmpty(row["path"]), tools.IsEmpty(row["scd_name"]), 0) if err != nil { log.Println(err) } if ischeckinscd == 1 { db.Raw("update t_scd_scl set version=?,enable=?,checkout_lock=? where id=?", tools.IsEmpty(row["version"]), tools.IsEmpty(row["enable"]), tools.IsEmpty(row["checkout_lock"]), scdid).Exec() } new(SystemLog).Success(enum.AuditType_scd_resetparse, enum.LogType_Execute, enum.OptEventType_System, enum.OptEventLevel_Hight, fmt.Sprintf("对变电站[id=%s]scd文件%s重新进行了解析", tools.IsEmpty(row["station_id"]), tools.IsEmpty(row["scd_name"])), c.GetUserInfo(), ) //SaveSyslog(, "SCD管理", true, tools.IsEmpty(c.UserInfo["name"]), tools.IsEmpty(c.UserInfo["ip"])) }(rowset[0]) return nil } //删除指定scd的所有数据 //isall:true 包含流转数据、签入时的其他上传文件等 false:只清除scd本身数据 func (c *ScdMgr) DeleteScd(scdid string, isall ...bool) error { scdidInt64, _ := strconv.ParseInt(scdid, 10, 64) db := orm.NewOrm() sql := "select t.*,a.area_name station_name,s1.check_flag from t_scd_scl t left join t_data_area a on t.station_id=a.id left JOIN t_sys_attachment s1 on t.id=s1.scd_id and s1.file_suffix='scd' where t.id=? " rowset0 := []orm.Params{} db.Raw(sql, scdidInt64).Values(&rowset0) if len(rowset0) == 0 { new(SystemLog).Fail(enum.AuditType_scd_show, enum.LogType_Delete, enum.OptEventType_Bus, enum.OptEventLevel_Hight, fmt.Sprintf("SQL:%s,参数:%s", sql, scdid), c.GetUserInfo(), ) return errors.New("无效的SCD编号") } //如果当前scd是签入的,需要解锁该scd所属变电站的签入锁定 if tools.IsEmpty(rowset0[0]["check_flag"]) == "1" { checkinLockKey := fmt.Sprintf("%s%s", tools.IsEmpty(rowset0[0]["station_id"]), "scdin") global.CheckingInInfo.Delete(checkinLockKey) } scd_name := tools.IsEmpty(rowset0[0]["scd_name"]) scd_path := tools.IsEmpty(rowset0[0]["path"]) //获取scd属性表 sql = "select table_name , table_comment from information_schema.tables where table_schema = (select database()) and table_name like 't_scd_%' " rowset := []orm.Params{} _, err := db.Raw(sql).Values(&rowset) if err != nil { logger.Logger.Error(err) new(SystemLog).Fail(enum.AuditType_scd_show, enum.LogType_Delete, enum.OptEventType_Bus, enum.OptEventLevel_Hight, fmt.Sprintf("SQL:%s", sql), c.GetUserInfo(), ) return err } go new(ScdParse).RemoveScdCache(scdid) go mqtt.PublishMessage("/jujutong/scd_check_tools/delete/"+scdid, `{"state":0,"scd_name":"`+tools.IsEmpty(rowset0[0]["scd_name"])+`","user":"`+c.GetUserName()+`"}`) deleteAll := true if len(isall) > 0 { deleteAll = isall[0] } var wg = sync.WaitGroup{} if c.SyncDeleteMod { wg.Add(1) } go func() { if c.SyncDeleteMod { defer func() { wg.Done() }() } dirChar := string(os.PathSeparator) for _, row := range rowset { tb := tools.IsEmpty(row["TABLE_NAME"]) + tools.IsEmpty(row["table_name"]) if tb == "t_scd_scl" { continue } for { sql = "delete from " + tb + " where scd_id=? limit 10000" r, _ := db.Raw(sql, scdidInt64).Exec() if r != nil { cnt, _ := r.RowsAffected() if cnt == 0 || cnt < 10000 { break } } else { break } } } db.Raw("delete from t_ied_relation where scd_id=?", scdidInt64).Exec() db.Raw("delete from t_ied_ctrl_relation where scd_id=?", scdidInt64).Exec() db.Raw("delete from t_area_ied_relation where area_id in(select id from t_substation_area where scd_id=?)", scdidInt64).Exec() db.Raw("delete from t_substation_area where scd_id=?", scdidInt64).Exec() db.Raw("delete from t_scd_diff_compare_detail where compare_id=(select id from t_scd_diff_compare where source_id=?)", scdidInt64).Exec() db.Raw("delete from t_scd_node_rule_parse where scd_id=?", scdidInt64).Exec() os.Remove(scd_path + ".parsed.xml") os.Remove(strings.Join([]string{".", "static", "upload", scdid + ".scd.crc"}, dirChar)) os.Remove(strings.Join([]string{".", "static", "upload", scdid + ".schema_valid"}, dirChar)) if deleteAll { db.Raw("delete from t_scd_diff_compare where source_id=? or target_id=?", scdidInt64, scdidInt64).Exec() db.Raw("delete from t_scd_scl where id=?", scdidInt64).Exec() os.Remove(scd_path) attachmentlist := []orm.Params{} //清除相关附件 db.Raw("select save_path from t_sys_attachment where scd_id=?", scdidInt64).Values(&attachmentlist) for _, fr := range attachmentlist { savepath := tools.IsEmpty(fr["save_path"]) if savepath != "" { savepath = "." + strings.ReplaceAll(savepath, "\\", dirChar) os.Remove(savepath) } } db.Raw("delete from t_sys_attachment where scd_id=?", scdidInt64).Exec() if len(scd_path) > 0 && scd_path[0:1] == "." { scd_path = scd_path[1:] } db.Raw("delete from t_sys_flow_run_detail where flow_run_id in(select t.id from t_sys_flow_run t where t.scd_name=? and t.scd_path=?)", scd_name, scd_path).Exec() db.Raw("delete from t_sys_flow_run where scd_name=? and scd_path=?", scd_name, scd_path).Exec() } db.Raw("delete from t_sys_attachment where check_flag=2 and scd_id=?", scdidInt64).Exec() //删除裁剪文件记录 db.Raw("update t_sys_attachment set scd_id=0 where scd_id=?", scdidInt64).Exec() //删除该scd相关文件,包括每个装置配置ied、crc等文件 tmpPart := []string{".", "static", "upload", "ied", scdid} iedpath := strings.Join(tmpPart, dirChar) os.RemoveAll(iedpath) new(SystemLog).Success(enum.AuditType_scd_show, enum.LogType_Delete, enum.OptEventType_Bus, enum.OptEventLevel_Hight, fmt.Sprintf("清除了变电站[id=%s]scd文件%s的所有数据", tools.IsEmpty(rowset0[0]["station_name"]), tools.IsEmpty(rowset0[0]["scd_name"])), c.GetUserInfo(), ) go mqtt.PublishMessage("/jujutong/scd_check_tools/delete/"+scdid, `{"state":1,"station_name":"`+tools.IsEmpty(rowset0[0]["station_name"])+`","scd_name":"`+tools.IsEmpty(rowset0[0]["scd_name"])+`","user":"`+c.GetUserName()+`"}`) }() if c.SyncDeleteMod { wg.Wait() } //如果当前删除的是签入SCD,则需要把当前站最后签出记录的签出锁定状态更改为1,否则该站将无法再次签入了 if deleteAll && tools.IsEmpty(rowset0[0]["is_checkin_scd"]) == "1" { station_id := tools.IsEmpty(rowset0[0]["station_id"]) //判断该站有没有签出记录 rowset0 = []orm.Params{} sql = "select id from t_scd_scl where station_id=? order by id desc limit 1" db.Raw(sql, station_id).Values(&rowset0) if len(rowset0) > 0 { db.Raw("update t_scd_scl set checkout_lock=1 where id=?", tools.IsEmpty(rowset0[0]["id"])).Exec() } } return nil } //获取指定scd中装置网络地址 func (c *ScdMgr) GetIedNetAddr(scdid string) ([]orm.Params, error) { key := fmt.Sprintf("scd_netinfo_%d", scdid) if v, h := global.GoCahce.Get(key); h { return v.([]orm.Params), nil } scdXmlObj, serr := new(ScdParse).GetScdXmlObjectBySCDID(scdid) if serr != nil { return nil, serr } if scdXmlObj == nil { return nil, errors.New("无效的SCD") } rowset := []orm.Params{} for _, item := range scdXmlObj.Communication.SubNetwork { for _, c1 := range item.ConnectedAP { if c1.Address != nil { for _, p1 := range c1.Address.P { if p1.Type == "IP" || p1.Type == "MAC-Address" { p1type := "ip" if p1.Type == "MAC-Address" { p1type = "mac" } rowobj := orm.Params{ "id": p1.NodeId, "node_name": "P", "node_value": p1.InnerText, "parent_node_id": c1.NodeId, "addrtype": p1type, "attr_ied_name": c1.IedName, "attr_ap_name": c1.ApName, } rowset = append(rowset, rowobj) } } } } } global.GoCahce.Set(key, rowset, -1) return rowset, nil } //对scd进行crc校验 func (c *ScdMgr) CrcCheck(scdid string) (bool, error) { crcFilePath := fmt.Sprintf(strings.Join([]string{".", "static", "upload", ""}, string(os.PathSeparator))+"%s.scd.crc", scdid) f, _ := os.Stat(crcFilePath) if f != nil { crcmap := map[string]string{} txt, _ := os.ReadFile(crcFilePath) err := json.Unmarshal(txt, &crcmap) if err == nil { global.CachedScdCrc.Store(fmt.Sprintf("crc_%s", scdid), crcmap) //crc提取完成 global.IedCrcMakeState.Store(fmt.Sprintf("crc_%s", scdid), "2") new(TaskMgr).SetStep(scdid, enum.TaskStep_SCD_crc_extract.Code(), 2) new(TaskMgr).SetStep(scdid, enum.TaskStep_SCD_ccd_extract.Code(), 2) new(TaskMgr).SetStep(scdid, enum.TaskStep_SCD_cid_extract.Code(), 2) new(TaskMgr).SetStep(scdid, enum.TaskStep_SCD_icd_extract.Code(), 2) return true, nil } } scdInfo, err := c.One(scdid) if err != nil { //crc提取失败 global.IedCrcMakeState.Store(fmt.Sprintf("crc_%s", scdid), "0") new(TaskMgr).SetStep(scdid, enum.TaskStep_SCD_crc_extract.Code(), 3, err.Error()) new(TaskMgr).SetStep(scdid, enum.TaskStep_SCD_ccd_extract.Code(), 3, err.Error()) new(TaskMgr).SetStep(scdid, enum.TaskStep_SCD_cid_extract.Code(), 3, err.Error()) new(TaskMgr).SetStep(scdid, enum.TaskStep_SCD_icd_extract.Code(), 3, err.Error()) return false, err } if scdInfo == nil { //crc提取失败 global.IedCrcMakeState.Store(fmt.Sprintf("crc_%s", scdid), "0") new(TaskMgr).SetStep(scdid, enum.TaskStep_SCD_crc_extract.Code(), 3, "无效的scd名称和路径") new(TaskMgr).SetStep(scdid, enum.TaskStep_SCD_ccd_extract.Code(), 3, "无效的scd名称和路径") new(TaskMgr).SetStep(scdid, enum.TaskStep_SCD_cid_extract.Code(), 3, "无效的scd名称和路径") new(TaskMgr).SetStep(scdid, enum.TaskStep_SCD_icd_extract.Code(), 3, "无效的scd名称和路径") return false, errors.New("无效的scd名称和路径") } scdpath := scdInfo["path"].(string) scdname := scdInfo["scd_name"].(string) station_id := tools.IsEmpty(scdInfo["station_id"]) data := map[string]string{"name": scdname, "stationid": station_id, "rootid": scdid, "state": "0", "node": "crc-file", "msg": ""} dataMsg, _ := json.Marshal(data) mqtt.PublishMessage(fmt.Sprintf("/jujutong/scd_check_tools/parse/%s/%s", station_id, scdid), string(dataMsg)) //crc提取开始 new(TaskMgr).SetStep(scdid, enum.TaskStep_SCD_crc_extract.Code(), 1) global.IedCrcMakeState.Store(fmt.Sprintf("crc_%s", scdid), "1") osname := string(runtime.GOOS) var subProcess *exec.Cmd if osname == "windows" { subProcess = exec.Command("cmd", "/c", "crclibtest.exe") } else { subProcess = exec.Command("bash", "-c", "./crctestdemo") } stdin, err := subProcess.StdinPipe() if err != nil { logger.Logger.Error(err) //crc提取失败 global.IedCrcMakeState.Store(fmt.Sprintf("crc_%s", scdid), "0") new(TaskMgr).SetStep(scdid, enum.TaskStep_SCD_crc_extract.Code(), 3, err.Error()) new(TaskMgr).SetStep(scdid, enum.TaskStep_SCD_ccd_extract.Code(), 3, err.Error()) new(TaskMgr).SetStep(scdid, enum.TaskStep_SCD_cid_extract.Code(), 3, err.Error()) new(TaskMgr).SetStep(scdid, enum.TaskStep_SCD_icd_extract.Code(), 3, err.Error()) return false, err } defer stdin.Close() stdout, _ := subProcess.StdoutPipe() //subProcess.Stdout = os.Stdout //subProcess.Stderr = os.Stderr logger.Logger.Debug("====正在提取IED校验码!") if err = subProcess.Start(); err != nil { logger.Logger.Error(err) //crc提取失败 global.IedCrcMakeState.Store(fmt.Sprintf("crc_%s", scdid), "0") new(TaskMgr).SetStep(scdid, enum.TaskStep_SCD_crc_extract.Code(), 3, err.Error()) new(TaskMgr).SetStep(scdid, enum.TaskStep_SCD_ccd_extract.Code(), 3, err.Error()) new(TaskMgr).SetStep(scdid, enum.TaskStep_SCD_cid_extract.Code(), 3, err.Error()) new(TaskMgr).SetStep(scdid, enum.TaskStep_SCD_icd_extract.Code(), 3, err.Error()) return false, err } txt := make([]byte, 1024) for { time.Sleep(100 * time.Millisecond) n, _ := stdout.Read(txt) str := string(txt[0:n]) if strings.Index(str, "number(1-3)") > 1 { break } } _, err = stdin.Write([]byte("1\n")) if err != nil { logger.Logger.Error(err) //crc提取失败 global.IedCrcMakeState.Store(fmt.Sprintf("crc_%s", scdid), "0") new(TaskMgr).SetStep(scdid, enum.TaskStep_SCD_crc_extract.Code(), 3, err.Error()) new(TaskMgr).SetStep(scdid, enum.TaskStep_SCD_ccd_extract.Code(), 3, err.Error()) new(TaskMgr).SetStep(scdid, enum.TaskStep_SCD_cid_extract.Code(), 3, err.Error()) new(TaskMgr).SetStep(scdid, enum.TaskStep_SCD_icd_extract.Code(), 3, err.Error()) return false, err } txt = make([]byte, 1024) for { time.Sleep(100 * time.Millisecond) n, _ := stdout.Read(txt) if n == 0 { break } str := string(txt[0:n]) if strings.Index(str, "SCD file") > -1 { break } } stdin.Write([]byte(scdpath + "\n")) //io.WriteString(stdin, scdpath+"\n") //输入scd路径 outContent := "" for { time.Sleep(1 * time.Second) n, _ := stdout.Read(txt) str := tools.ConvertByte2String(txt[0:n], "GB18030") outContent = outContent + str if strings.Index(str, "The CRC of SCD is") > -1 { break } } stdin.Write([]byte("\n")) stdin.Close() logger.Logger.Debug("====IED校验码提取完成!") crclist := strings.Split(outContent, "\n") logger.Logger.Debug(fmt.Sprintf("%+v", crclist)) crcmap := map[string]string{} for _, crc := range crclist { if crc == "" { continue } if crc[0:6] == "Please" { continue } if strings.Index(crc, "The CRC of SCD is") > -1 { crcmap["scdcrc"] = strings.Trim(strings.Split(crc, ":")[1], "\r") //根据所有IED生成的scd校验码 continue } v_crc := strings.Split(crc, ":") if len(v_crc) > 1 { if len(v_crc[0]) > 2 && v_crc[0][0:3] == "SCD" { continue } crcmap[v_crc[0]] = strings.Trim(v_crc[1], "\r") } } global.CachedScdCrc.Store(fmt.Sprintf("crc_%s", scdid), crcmap) //crc提取完成 global.IedCrcMakeState.Store(fmt.Sprintf("crc_%s", scdid), "2") crcdata, _ := json.Marshal(crcmap) err = os.WriteFile(crcFilePath, crcdata, fs.ModePerm) if err != nil { logger.Logger.Error(err) } data = map[string]string{"name": scdname, "stationid": station_id, "rootid": scdid, "state": "1", "node": "crc-file", "msg": ""} dataMsg, _ = json.Marshal(data) mqtt.PublishMessage(fmt.Sprintf("/jujutong/scd_check_tools/parse/%s/%s", station_id, scdid), string(dataMsg)) new(TaskMgr).SetStep(scdid, enum.TaskStep_SCD_crc_extract.Code(), 2) dirChar := string(os.PathSeparator) tmpPart := []string{".", "static", "upload", "ied", scdid} ccdpath := strings.Join(tmpPart, dirChar) os.MkdirAll(ccdpath, fs.ModePerm) go func(scdid, scdname, station_id, scdpath, ccdpath string) { //提取CCD文件 new(TaskMgr).SetStep(scdid, enum.TaskStep_SCD_ccd_extract.Code(), 1) logger.Logger.Debug("====开始提取IED装置CCD文件...") ccdFileSqls := []string{} for iedname, _ := range crcmap { if iedname == "scdcrc" { continue } var subProcess *exec.Cmd logger.Logger.Debug("====正在提取IED装置" + iedname + "的CCD文件到" + ccdpath) if osname == "windows" { subProcess = exec.Command("cmd", "/c", "crclibtest.exe") } else { subProcess = exec.Command("bash", "-c", "./crctestdemo") } stdin, err := subProcess.StdinPipe() if err != nil { logger.Logger.Error(err) continue } stdout, _ := subProcess.StdoutPipe() if err = subProcess.Start(); err != nil { logger.Logger.Error(err) stdin.Close() continue } txt := make([]byte, 1024) for { time.Sleep(10 * time.Millisecond) n, _ := stdout.Read(txt) str := string(txt[0:n]) if n > 0 { logger.Logger.Debug(str) } if strings.Index(str, "number(1-3)") > 1 { break } } _, err = stdin.Write([]byte("3\n")) if err != nil { logger.Logger.Error(err) stdin.Close() continue } txt = make([]byte, 1024) for { time.Sleep(10 * time.Millisecond) n, _ := stdout.Read(txt) str := string(txt[0:n]) if n > 0 { logger.Logger.Debug(str) } if strings.Index(str, "SCD file") > 0 { break } } _, err = stdin.Write([]byte(scdpath + "\n")) if err != nil { logger.Logger.Error(err) stdin.Close() continue } txt = make([]byte, 1024) for { time.Sleep(10 * time.Millisecond) n, _ := stdout.Read(txt) str := string(txt[0:n]) if n > 0 { logger.Logger.Debug(str) } if strings.Index(str, "IED:") > 0 { break } } _, err = stdin.Write([]byte(iedname + "\n")) if err != nil { logger.Logger.Error(err) stdin.Close() continue } txt = make([]byte, 1024) for { time.Sleep(10 * time.Millisecond) n, _ := stdout.Read(txt) str := string(txt[0:n]) if n > 0 { logger.Logger.Debug(str) } if strings.Index(str, "directory:") > 0 { break } } _, err = stdin.Write([]byte(ccdpath + "\n")) if err != nil { logger.Logger.Error(err) stdin.Close() continue } txt = make([]byte, 1024) for i := 0; i < 10; i++ { time.Sleep(10 * time.Millisecond) n, _ := stdout.Read(txt) if n > 0 { logger.Logger.Debug(string(txt[0:n])) break } } stdin.Write([]byte(" \n")) logger.Logger.Debug("====IED装置" + iedname + "的CCD文件提取完成!") stdin.Close() tmpF, e := os.Stat(ccdpath + dirChar + iedname + ".ccd") if e != nil { logger.Logger.Error(e) } else { ccdFileType, _ := global.GoCahce.Get("global_code_file_typesfile_ccd") //ccd文件类型的字典id //(station_id,scd_id,ied_name,file_name,file_size,save_path,file_type,file_suffix,check_flag) ccdFileSqls = append(ccdFileSqls, fmt.Sprintf("(%s,%s,'%s','%s','%s','%s','%s','%s',%d,now())", station_id, scdid, iedname, iedname+".ccd", tools.FormatFileSize(tmpF.Size()), strings.ReplaceAll(ccdpath+dirChar+iedname+".ccd", "\\", "/")[1:], tools.IsEmpty(ccdFileType.(orm.Params)["id"]), "ccd", 2)) } } if len(ccdFileSqls) > 0 { odb := orm.NewOrm() odb.Raw("insert into t_sys_attachment(station_id,scd_id,ied_name,file_name,file_size,save_path,file_type,file_suffix,check_flag,created_time)values" + strings.Join(ccdFileSqls, ",")).Exec() ccdFileSqls = nil } new(TaskMgr).SetStep(scdid, enum.TaskStep_SCD_ccd_extract.Code(), 2) logger.Logger.Debug("====IED装置CCD文件提取完成!") data := map[string]string{"name": scdname, "stationid": station_id, "rootid": scdid, "state": "1", "node": "ccd-file", "msg": ""} dataMsg, _ := json.Marshal(data) mqtt.PublishMessage(fmt.Sprintf("/jujutong/scd_check_tools/parse/%s/%s", station_id, scdid), string(dataMsg)) }(scdid, scdname, station_id, scdpath, ccdpath) //icd,cid提取 go func(scdid, savepath string) { new(TaskMgr).SetStep(scdid, enum.TaskStep_SCD_cid_extract.Code(), 1) new(TaskMgr).SetStep(scdid, enum.TaskStep_SCD_icd_extract.Code(), 1) logger.Logger.Debug("====开始提取SCD【" + scdid + "】的IED装置ICD、CID文件...") scdXmlObj, serr := new(ScdParse).GetScdXmlObjectBySCDID(scdid) if serr != nil { logger.Logger.Error(serr) return } if scdXmlObj == nil { return } lnodetypes := c.GetLNodeType(scdXmlObj) dotypes := c.GetDOType(scdXmlObj) dirChar := string(os.PathSeparator) xxxFileSqls := []string{} icdFileType, _ := global.GoCahce.Get("global_code_file_typesfile_icd") //icd文件类型的字典id cidFileType, _ := global.GoCahce.Get("global_code_file_typesfile_cid") //cid文件类型的字典id for iedname, _ := range crcmap { if iedname == "scdcrc" { continue } iedObj := new(ScdNode).GetIed(scdXmlObj, scdid, iedname) if iedObj == nil { logger.Logger.Error(errors.New(fmt.Sprintf("scd【%s】中未发现装置%s", scdid, iedname))) continue } xmllines := []string{`` + "\r\n"} xmllines = append(xmllines, ``+"\r\n") //icd提取 xmlbytes, err := xml.MarshalIndent(iedObj, "", " ") if err == nil { xmlStr := string(xmlbytes) pattern := `( lineno="\d{1,}" nodeid="\d{1,}")|( )|( ix="")|(.*?)` //替换其中的lineno和nodeid属性 reg := regexp.MustCompile(pattern) xmlStr = reg.ReplaceAllString(xmlStr, "") xmlStr = "\r\n" xmlStr = xmlStr + "\r\n" //提取LNodeType isSub := map[string]bool{} for _, apt := range iedObj.AccessPoint { if apt.Server == nil { continue } for _, ld := range apt.Server.LDevice { lnnode := lnodetypes[ld.LN0.LnType] if lnnode != nil && isSub[ld.LN0.LnType] == false { xmlbytes, _ = xml.MarshalIndent(lnnode, "", " ") lnnodeXmlStr := string(xmlbytes) lnnodeXmlStr = "<" + lnnodeXmlStr[2:] lnnodeXmlStr = lnnodeXmlStr[0:len(lnnodeXmlStr)-13] + "\r\n" xmlStr += reg.ReplaceAllString(lnnodeXmlStr, "") isSub[ld.LN0.LnType] = true for _, do := range lnnode.DO { if isSub[do.Type] == false { dotype := dotypes[do.Type] xmlbytes, _ = xml.MarshalIndent(dotype, "", " ") lnnodeXmlStr := string(xmlbytes) lnnodeXmlStr = "<" + lnnodeXmlStr[2:] lnnodeXmlStr = lnnodeXmlStr[0:len(lnnodeXmlStr)-10] + "\r\n" xmlStr += reg.ReplaceAllString(lnnodeXmlStr, "") isSub[do.Type] = true } } } for _, ln := range ld.LN { lnnode = lnodetypes[ln.LnType] if lnnode != nil && isSub[ln.LnType] == false { xmlbytes, _ = xml.MarshalIndent(lnnode, "", " ") lnnodeXmlStr := string(xmlbytes) lnnodeXmlStr = "<" + lnnodeXmlStr[2:] lnnodeXmlStr = lnnodeXmlStr[0:len(lnnodeXmlStr)-13] + "\r\n" xmlStr += reg.ReplaceAllString(lnnodeXmlStr, "") isSub[ln.LnType] = true for _, do := range lnnode.DO { if isSub[do.Type] == false { dotype := dotypes[do.Type] xmlbytes, _ = xml.MarshalIndent(dotype, "", " ") lnnodeXmlStr := string(xmlbytes) lnnodeXmlStr = "<" + lnnodeXmlStr[2:] lnnodeXmlStr = lnnodeXmlStr[0:len(lnnodeXmlStr)-10] + "\r\n" xmlStr += reg.ReplaceAllString(lnnodeXmlStr, "") isSub[do.Type] = true } } } } } } xmlStr = xmlStr + "\r\n" //生成解析完成结果副本文件 os.WriteFile(savepath+dirChar+iedname+".icd", []byte(strings.Join(xmllines, "")+xmlStr), fs.ModePerm) tmpF, e := os.Stat(savepath + dirChar + iedname + ".icd") if e != nil { logger.Logger.Error(e) } else { xxxFileSqls = append(xxxFileSqls, fmt.Sprintf("(%s,%s,'%s','%s','%s','%s','%s','%s',%d,now())", station_id, scdid, iedname, iedname+".icd", tools.FormatFileSize(tmpF.Size()), strings.ReplaceAll(savepath+dirChar+iedname+".icd", "\\", "/")[1:], tools.IsEmpty(icdFileType.(orm.Params)["id"]), "icd", 2)) } xmlStr = "" } else { logger.Logger.Error(err) } //cid提取 xmlbytes, err = xml.MarshalIndent(iedObj, "", " ") if err == nil { //生成解析完成结果副本文件 xmlStr := string(xmlbytes) pattern := `( lineno="\d{1,}" nodeid="\d{1,}")|( )|( ix="")|(.*?)` //替换其中的lineno和nodeid属性 reg := regexp.MustCompile(pattern) //提取通讯参数 commXml := "\r\n" subnetworkXml := []string{} for _, comm := range scdXmlObj.Communication.SubNetwork { apXml := []string{} for _, ap := range comm.ConnectedAP { if ap.IedName == iedname { apxmlbytes, _ := xml.MarshalIndent(ap, "", " ") lnnodeXmlStr := string(apxmlbytes) lnnodeXmlStr = "<" + lnnodeXmlStr[2:] lnnodeXmlStr = lnnodeXmlStr[0:len(lnnodeXmlStr)-15] + "\r\n" apXml = append(apXml, lnnodeXmlStr) } } if len(apXml) > 0 { subnetworkXml = append(subnetworkXml, fmt.Sprintf("\r\n", comm.Name, comm.Type)) if comm.BitRate != nil { subnetworkXml = append(subnetworkXml, fmt.Sprintf("%s\r\n", comm.BitRate.Unit, comm.BitRate.Multiplier, comm.BitRate.InnerText)) } subnetworkXml = append(subnetworkXml, reg.ReplaceAllString(strings.Join(apXml, ""), "")+"\r\n") subnetworkXml = append(subnetworkXml, "\r\n") } } if len(subnetworkXml) > 0 { commXml += strings.Join(subnetworkXml, "") commXml += "\r\n" } else { commXml = "" } xmlStr = reg.ReplaceAllString(xmlStr, "") xmlStr = reg.ReplaceAllString(xmlStr, "") xmlStr = commXml + "\r\n" xmlStr = xmlStr + "\r\n" //提取LNodeType isSub := map[string]bool{} for _, apt := range iedObj.AccessPoint { if apt.Server == nil { continue } for _, ld := range apt.Server.LDevice { lnnode := lnodetypes[ld.LN0.LnType] if lnnode != nil && isSub[ld.LN0.LnType] == false { xmlbytes, _ = xml.MarshalIndent(lnnode, "", " ") lnnodeXmlStr := string(xmlbytes) lnnodeXmlStr = "<" + lnnodeXmlStr[2:] lnnodeXmlStr = lnnodeXmlStr[0:len(lnnodeXmlStr)-13] + "\r\n" xmlStr += reg.ReplaceAllString(lnnodeXmlStr, "") isSub[ld.LN0.LnType] = true for _, do := range lnnode.DO { if isSub[do.Type] == false { dotype := dotypes[do.Type] xmlbytes, _ = xml.MarshalIndent(dotype, "", " ") lnnodeXmlStr := string(xmlbytes) lnnodeXmlStr = "<" + lnnodeXmlStr[2:] lnnodeXmlStr = lnnodeXmlStr[0:len(lnnodeXmlStr)-10] + "\r\n" xmlStr += reg.ReplaceAllString(lnnodeXmlStr, "") isSub[do.Type] = true } } } for _, ln := range ld.LN { lnnode = lnodetypes[ln.LnType] if lnnode != nil && isSub[ln.LnType] == false { xmlbytes, _ = xml.MarshalIndent(lnnode, "", " ") lnnodeXmlStr := string(xmlbytes) lnnodeXmlStr = "<" + lnnodeXmlStr[2:] lnnodeXmlStr = lnnodeXmlStr[0:len(lnnodeXmlStr)-13] + "\r\n" xmlStr += reg.ReplaceAllString(lnnodeXmlStr, "") isSub[ln.LnType] = true for _, do := range lnnode.DO { if isSub[do.Type] == false { dotype := dotypes[do.Type] xmlbytes, _ = xml.MarshalIndent(dotype, "", " ") lnnodeXmlStr := string(xmlbytes) lnnodeXmlStr = "<" + lnnodeXmlStr[2:] lnnodeXmlStr = lnnodeXmlStr[0:len(lnnodeXmlStr)-10] + "\r\n" xmlStr += reg.ReplaceAllString(lnnodeXmlStr, "") isSub[do.Type] = true } } } } } } xmlStr = xmlStr + "\r\n" os.WriteFile(savepath+dirChar+iedname+".cid", []byte(strings.Join(xmllines, "")+xmlStr), fs.ModePerm) tmpF, e := os.Stat(savepath + dirChar + iedname + ".cid") if e != nil { logger.Logger.Error(e) } else { xxxFileSqls = append(xxxFileSqls, fmt.Sprintf("(%s,%s,'%s','%s','%s','%s','%s','%s',%d,now())", station_id, scdid, iedname, iedname+".cid", tools.FormatFileSize(tmpF.Size()), strings.ReplaceAll(savepath+dirChar+iedname+".cid", "\\", "/")[1:], tools.IsEmpty(cidFileType.(orm.Params)["id"]), "cid", 2)) } xmlStr = "" } else { logger.Logger.Error(err) continue } } if len(xxxFileSqls) > 0 { odb := orm.NewOrm() odb.Raw("insert into t_sys_attachment(station_id,scd_id,ied_name,file_name,file_size,save_path,file_type,file_suffix,check_flag,created_time)values" + strings.Join(xxxFileSqls, ",")).Exec() xxxFileSqls = nil } new(TaskMgr).SetStep(scdid, enum.TaskStep_SCD_cid_extract.Code(), 2) new(TaskMgr).SetStep(scdid, enum.TaskStep_SCD_icd_extract.Code(), 2) data := map[string]string{"name": scdname, "stationid": station_id, "rootid": scdid, "state": "1", "node": "cid-file", "msg": ""} dataMsg, _ := json.Marshal(data) mqtt.PublishMessage(fmt.Sprintf("/jujutong/scd_check_tools/parse/%s/%s", station_id, scdid), string(dataMsg)) }(scdid, ccdpath) return true, nil } var YaoCeMap = map[string]string{ "MV": "测量值", "CMV": "复数测量值", "SAV": "呆样值", "WYE": "三相项系统中相对地相关测量值", "DEL": "三相项系统中相对相相关测量值", "SEQ": "顺序值", "INC": "可控整数状态", "BSC": "二进制受控步位置信息", "ISC": "整数受控步位置信息", "APC": "可控模拟过程信息", "ENC": "可控枚举状态", "HMV": "谐波值", "HWYE": "WYE谐波值", "HDEL": "DEL谐波值", "BAC": "可控的二进制模拟过程值", "ING": "整数状态定值", } var YaoXinMap = map[string]string{ "SPS": "单点状态信息", "DPS": "双点状态信息", "ACT": "保护激活信息", "ACD": "方向保护激活信息", "SPC": "可控的单点", "DPC": "可控的双点", "ENS": "枚举状态", "VSS": "可见字符串状态", "HST": "直方图", "LPL": "逻辑节点铭牌", "DPL": "设备铭牌", "SEC": "安全违例计数", } //双点遥信类型 var YaoXinDBType = map[string]bool{ "DPS": true, "DPC": true, } //单点遥信类型 var YaoXinSiType = map[string]bool{ "SPS": true, "SPC": true, } var YaoKongMap = map[string]string{ "SPC": "可控的单点", "DPC": "可控的双点", "INC": "可控的整数状态", "BSC": "二进制受控步位置信息", "ISC": "整数受控步位置信息", "APC": "可控模拟过程信息", "ENC": "可控枚举状态", "BAC": "可控的二进制模拟过程值", } var DingZhiMap = map[string]string{ "SPG": "单点定值", "ING": "整数状态定值", "ASG": "模拟定值", "STG": "STG", "ENG": "枚举状态定值", "ORG": "对象引用定值", "TSG": "时间定值组", "CSG": "曲线形状定值", "CUG": "可见字符串定值", "VSG": "VSG", "CURVE": "定值曲线", } var OtherCdcTypeMap = map[string]string{ "INS": "整数状态", "BCR": "二进制计数器读数", "CSD": "曲线形状描述", } //根据cdc返回对应的数据类型名称 func (c *ScdMgr) GetCdcDataTypeName(cdccode string) string { name := DingZhiMap[cdccode] if name == "" { name = YaoKongMap[cdccode] } if name == "" { name = YaoXinMap[cdccode] } if name == "" { name = YaoCeMap[cdccode] } if name == "" { name = OtherCdcTypeMap[cdccode] } return name } //获取指定scd的数据模板LNodeType集合 func (c *ScdMgr) GetLNodeType(scdXmlObj *node_attr.SCL) map[string]*node_attr.NLNodeType { key := fmt.Sprintf("%d%s", scdXmlObj.NodeId, "lnodetype") if v, h := global.GoCahce.Get(key); h { return v.(map[string]*node_attr.NLNodeType) } mapSourceLNodeType := map[string]*node_attr.NLNodeType{} for _, lnnodetpye := range scdXmlObj.DataTypeTemplates.LNodeType { mapSourceLNodeType[lnnodetpye.Id] = lnnodetpye } global.GoCahce.Set(key, mapSourceLNodeType, 30*time.Minute) return mapSourceLNodeType } //获取指定scd的数据模板DOType集合 func (c *ScdMgr) GetDOType(scdXmlObj *node_attr.SCL) map[string]*node_attr.NDOType { key := fmt.Sprintf("%d%s", scdXmlObj.NodeId, "dotype") if v, h := global.GoCahce.Get(key); h { return v.(map[string]*node_attr.NDOType) } mapSourceDOType := map[string]*node_attr.NDOType{} for _, lnnodetpye := range scdXmlObj.DataTypeTemplates.DOType { mapSourceDOType[lnnodetpye.Id] = lnnodetpye } global.GoCahce.Set(key, mapSourceDOType, 30*time.Minute) return mapSourceDOType } //获取遥控DOType集合 //遥控测点规则:模型模板中,DOType包含cdc为"SPC", "DPC", "INC", "BSC", "ISC", "APC",”ENC”,”BAC”,同时DOType的DA子节点中含有name为Oper元素 func (c *ScdMgr) GetYaoKongDoTypeMap(scdXmlObj *node_attr.SCL) map[string]*node_attr.NDOType { result := map[string]*node_attr.NDOType{} for _, lnnodetpye := range scdXmlObj.DataTypeTemplates.DOType { if _, h := YaoKongMap[lnnodetpye.Cdc]; h { for _, da := range lnnodetpye.DA { if da.Name == "Oper" { result[lnnodetpye.Id] = lnnodetpye break } } } } return result } //获取指定IED下的遥控测点DOI实例 func (c *ScdMgr) GetYaoKongDOIMap(scdXmlObj *node_attr.SCL, iedname string) map[string]*node_attr.NDOI { result := map[string]*node_attr.NDOI{} yaokongDOType := c.GetYaoKongDoTypeMap(scdXmlObj) if len(yaokongDOType) == 0 { //集合为空,则全文遥控数据为空 return nil } key := fmt.Sprintf("%d%s%s", scdXmlObj.NodeId, iedname, "yaoking") if v, h := global.GoCahce.Get(key); h { return v.(map[string]*node_attr.NDOI) } lnnodetype := c.GetLNodeType(scdXmlObj) daiTxtKey := map[string]bool{ "1": true, "2": true, "3": true, "4": true, "direct-with-normal-security": true, "sbo-with-normal-security": true, "direct-with-enhanced-security": true, "sbo-with-enhanced-security": true, } iedObj := new(ScdNode).GetIed(scdXmlObj, "", iedname) var getdoicdc = func(lntype, doiname string) string { doNames := lnnodetype[lntype] if doNames == nil { return "" } for _, do := range doNames.DO { if do.Name == doiname && yaokongDOType[do.Type] != nil { return yaokongDOType[do.Type].Cdc } } return "" } //遍历模型实例化中,所有步骤1集合中的DOI实例 for _, ap := range iedObj.AccessPoint { if ap.Server == nil { continue } for _, ld := range ap.Server.LDevice { if ld.LN0 != nil { for _, doi := range ld.LN0.DOI { //获取doi的cdc cdc := getdoicdc(ld.LN0.LnType, doi.Name) if cdc == "" { continue } key := fmt.Sprintf("%s.%s.%s", ld.Inst, ld.LN0.Inst, doi.Name) //检查DOI下是否含有name为ctlModel的DAI,若不含DAI,则默认为遥控, //若含有DAI,则判断ctlModel的txt值,当text值为1、2、3、4或”direct-with-normal-security”、“sbo-with-normal-security”、“direct-with-enhanced-security”、“sbo-with-enhanced-security”时,认为该点为遥控 hasctlModel := false for _, dai := range doi.DAI { if dai.Name == "ctlModel" { hasctlModel = true if dai.Val != nil { txt := dai.Val.InnerText if _, h := daiTxtKey[txt]; h { result[key] = doi } } break } } if !hasctlModel { result[key] = doi } } } for _, ln := range ld.LN { for _, doi := range ln.DOI { //获取doi的cdc cdc := getdoicdc(ld.LN0.LnType, doi.Name) if cdc == "" { continue } key := fmt.Sprintf("%s.%s.%s", ld.Inst, ln.Inst, doi.Name) hasctlModel := false for _, dai := range doi.DAI { if dai.Name == "ctlModel" { hasctlModel = true if dai.Val != nil { txt := dai.Val.InnerText if _, h := daiTxtKey[txt]; h { result[key] = doi } } break } } if !hasctlModel { result[key] = doi } } } } } global.GoCahce.Set(key, result, 31*time.Minute) return result } //获取指定IED下的定值测点DOI实例 func (c *ScdMgr) GetDingZhiDOIMap(scdXmlObj *node_attr.SCL, iedname string) map[string]*node_attr.NDOI { result := map[string]*node_attr.NDOI{} dingzhiDOType := c.GetDingZhiDoTypeMap(scdXmlObj) if len(dingzhiDOType) == 0 { //集合为空,则全文定值数据为空 return nil } key := fmt.Sprintf("%d%s%s", scdXmlObj.NodeId, iedname, "dingzhi") if v, h := global.GoCahce.Get(key); h { return v.(map[string]*node_attr.NDOI) } lnnodetype := c.GetLNodeType(scdXmlObj) iedObj := new(ScdNode).GetIed(scdXmlObj, "", iedname) ldList := map[string]*node_attr.NLDevice{} for _, ap := range iedObj.AccessPoint { if ap.Server == nil { continue } for _, ld := range ap.Server.LDevice { ldList[ld.Inst] = ld } } var foundFcdaDingZhi = func(dst *node_attr.NDataSet) map[string]*node_attr.NDOI { result := map[string]*node_attr.NDOI{} for _, fcda := range dst.FCDA { if fcda.Fc == "SP" || fcda.Fc == "SG" || fcda.Fc == "SE" { ld := ldList[fcda.LdInst] if fcda.LnClass == "LLN0" && ld.LN0.LnClass == fcda.LnClass { lnodetype := lnnodetype[ld.LN0.LnType] if lnodetype == nil { continue } for _, doi := range ld.LN0.DOI { if doi.Name == fcda.DoName { for _, do := range lnodetype.DO { if do.Name == doi.Name && dingzhiDOType[do.Type] != nil { key := fmt.Sprintf("%s/%s%s%s.%s/%s", fcda.LdInst, fcda.Prefix, fcda.LnClass, fcda.LnInst, fcda.DoName, fcda.Fc) result[key] = doi break } } break } } } else { for _, ln := range ld.LN { lnodetype := lnnodetype[ln.LnType] if lnodetype == nil { continue } if ln.LnClass == fcda.LnClass && ln.Inst == fcda.LnInst { for _, doi := range ln.DOI { if doi.Name == fcda.DoName { for _, do := range lnodetype.DO { if do.Name == doi.Name && dingzhiDOType[do.Type] != nil { key := fmt.Sprintf("%s/%s%s%s.%s/%s", fcda.LdInst, fcda.Prefix, fcda.LnClass, fcda.LnInst, fcda.DoName, fcda.Fc) result[key] = doi break } } break } } break } } } } } return result } //遍历模型实例化中,所有步骤1集合中的DOI实例 for _, ld := range ldList { if ld.LN0 != nil && ld.LN0.SettingControl != nil { for _, dst := range ld.LN0.DataSet { dingzhi := foundFcdaDingZhi(dst) for k, v := range dingzhi { result[k] = v } } } for _, ln := range ld.LN { if ln.SettingControl == nil { continue } for _, dst := range ln.DataSet { dingzhi := foundFcdaDingZhi(dst) for k, v := range dingzhi { result[k] = v } } } } global.GoCahce.Set(key, result, 31*time.Minute) return result } //获取指定IED下的遥测、遥信、遥脉测点 func (c *ScdMgr) GetYcYkYmMap(scdXmlObj *node_attr.SCL, iedname string) (yc map[string]interface{}, yx map[string]interface{}, ym map[string]interface{}) { key := fmt.Sprintf("%d%s%s", scdXmlObj.NodeId, iedname, "YcYkYm") if v, h := global.GoCahce.Get(key); h { v1 := v.([]interface{}) return v1[0].(map[string]interface{}), v1[1].(map[string]interface{}), v1[2].(map[string]interface{}) } targetIedObj := new(ScdNode).GetIed(scdXmlObj, "", iedname) //IED内的遥信 targetYaoXin := map[string]interface{}{} //IED内的遥测 targetYaoCe := map[string]interface{}{} //IED内的遥脉 targetYaoMai := map[string]interface{}{} mapTargetLNodeType := c.GetLNodeType(scdXmlObj) mapTargetLDoType := c.GetDOType(scdXmlObj) for _, ap := range targetIedObj.AccessPoint { if ap.Server == nil { continue } for _, ld := range ap.Server.LDevice { if ld.LN0 == nil { break } for _, rpb := range ld.LN0.ReportControl { if rpb.DatSet != "dsLog" { //处理遥信、遥测、遥脉数据 for _, dst := range ld.LN0.DataSet { if dst.Name == rpb.DatSet { for _, fcda := range dst.FCDA { if fcda.Fc == "" { //数据集成员的fc为空或为"",跳过 continue } key := fmt.Sprintf("%s/%s%s%s.%s.%s", fcda.LdInst, fcda.Prefix, fcda.LnClass, fcda.LnInst, fcda.DoName, fcda.DaName) //根据FCDA的prefix、lnClass、lnInst在DataSet所属的LDevice的LN0、LN中找到对应的节点,取到节点的lnType lnType := "" if ld.LN0.Prefix == fcda.Prefix && ld.LN0.LnClass == fcda.LnClass && ld.LN0.Inst == fcda.LnInst { lnType = ld.LN0.LnType } else { for _, lnitem := range ld.LN { if lnitem.Prefix == fcda.Prefix && lnitem.LnClass == fcda.LnClass && lnitem.Inst == fcda.LnInst { lnType = lnitem.LnType break } } } if lnType != "" { if lnnodetpye, h := mapTargetLNodeType[lnType]; h { for _, doitem := range lnnodetpye.DO { if doitem.Name == fcda.DoName { dotype := doitem.Type if dotypeobj, h := mapTargetLDoType[dotype]; h { cdctype := new(ScdMgr).GetDoTypePointType(dotypeobj, fcda) switch cdctype { case "YC": targetYaoCe[key] = fcda break case "YX": targetYaoXin[key] = fcda break case "YM": targetYaoMai[key] = fcda break } } break } } } } } break } } } } } } global.GoCahce.Set(key, []interface{}{targetYaoCe, targetYaoXin, targetYaoMai}, 30*time.Minute) return targetYaoCe, targetYaoXin, targetYaoMai } //获取指定IED下遥信接收端子 //return: map对象。key为端子对象地址,value为字符串数组,固定2个元素。第一个元素为端子描述,第二个元素为端子的cdc类型码 func (c *ScdMgr) GetYxExtref(scdXmlObj *node_attr.SCL, scdid int64, iedname string) map[*node_attr.NExtRef][]string { result := map[*node_attr.NExtRef][]string{} targetIedObj := new(ScdNode).GetIed(scdXmlObj, "", iedname) mapTargetLNodeType := c.GetLNodeType(scdXmlObj) mapTargetLDoType := c.GetDOType(scdXmlObj) tmpdata, _ := GlobalNodeMap.Load(scdid) nodeCacheMap := tmpdata.(map[int64]NodeCacheMap) scdNode := new(ScdNodeRule) scdNode.scdXmlObject = scdXmlObj for _, ap := range targetIedObj.AccessPoint { if ap.Server == nil { continue } for _, ld := range ap.Server.LDevice { if ld.LN0 != nil && ld.LN0.Inputs != nil { for _, extref := range ld.LN0.Inputs.ExtRef { doi := scdNode.IedIntAddrExist(iedname, extref.IntAddr) if doi == nil { continue } doiObj := doi.(*node_attr.NDOI) lnnode := nodeCacheMap[nodeCacheMap[doiObj.NodeId].ParentNodeId] lnType := lnnode.ObjAddr.(*node_attr.NLN).LnType if lnType != "" { if lnnodetpye, h := mapTargetLNodeType[lnType]; h { for _, doitem := range lnnodetpye.DO { if doitem.Name == doiObj.Name { dotype := doitem.Type if dotypeobj, h := mapTargetLDoType[dotype]; h { cdctype := new(ScdMgr).GetDoTypePointType(dotypeobj, nil) if cdctype == "YX" { result[extref] = []string{doiObj.Desc, dotypeobj.Cdc} } } break } } } } } break } } } return result } //获取定值DOType集合 //规则:模型模板中,DOType包含cdc为"SPG", "ING", "ASG", "STG", "ENG", "ORG",”TSG”,”CSG”,”CUG”,”VSG”,”CURVE” //同时DOType的DA子节点中含有name为setVal或setMag元素的DOType func (c *ScdMgr) GetDingZhiDoTypeMap(scdXmlObj *node_attr.SCL) map[string]*node_attr.NDOType { result := map[string]*node_attr.NDOType{} for _, lnnodetpye := range scdXmlObj.DataTypeTemplates.DOType { if _, h := DingZhiMap[lnnodetpye.Cdc]; h { for _, da := range lnnodetpye.DA { if da.Name == "setVal" || da.Name == "setMag" { result[lnnodetpye.Id] = lnnodetpye break } } } } return result } //判断cdc对应的测点数据类型。YX:遥信、YC:遥测、YM:遥脉、YK:遥控、DZ:定值 func (c *ScdMgr) GetDoTypePointType(dotype *node_attr.NDOType, fcda *node_attr.NFCDA) string { if dotype == nil { return "" } if _, h := YaoCeMap[dotype.Cdc]; h { return "YC" } if _, h := YaoXinMap[dotype.Cdc]; h { return "YX" } if dotype.Cdc == "INS" && fcda != nil { //判断DOType下DA中的name等于FCDA的daName的元素的bType值,如果bType值小写后为enum,分两种情况: //1. DOType下DA中含有type,并且type值为fltloop,类型为遥测 //2. 不满足1为遥信; //3. 如果bType值小写后不为enum,类型为遥测 for _, daitem := range dotype.DA { if daitem.Name == fcda.DaName { if daitem.BType == "Enum" { if daitem.Type == "fltloop" { return "YC" } else { return "YX" } } else { return "YC" } } } } if dotype.Cdc == "BCR" && fcda != nil { //判断LnClass,若LnClass为MMTR,则认为是遥脉,否则认为是遥测 if fcda.LnClass == "MMTR" { return "YM" } else { return "YC" } } return "" } //根据doi对象获取对应的cdc代码及名称 func (c *ScdMgr) GetDoiCdcInfo(scdXmlObj *node_attr.SCL, scdid int64, doiObj *node_attr.NDOI) string { mapTargetLNodeType := c.GetLNodeType(scdXmlObj) mapTargetLDoType := c.GetDOType(scdXmlObj) tmpdata, _ := GlobalNodeMap.Load(scdid) nodeCacheMap := tmpdata.(map[int64]NodeCacheMap) lnnode := nodeCacheMap[nodeCacheMap[doiObj.NodeId].ParentNodeId] lnType := lnnode.ObjAddr.(*node_attr.NLN).LnType if lnnodetpye, h := mapTargetLNodeType[lnType]; h { for _, doitem := range lnnodetpye.DO { if doitem.Name == doiObj.Name { dotype := doitem.Type if dotypeobj, h := mapTargetLDoType[dotype]; h { return dotypeobj.Cdc } break } } } return "" }