scd_area.go 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496
  1. package bo
  2. import (
  3. "errors"
  4. "fmt"
  5. "log"
  6. "regexp"
  7. "scd_check_tools/logger"
  8. "scd_check_tools/models/enum"
  9. "scd_check_tools/models/node_attr"
  10. "scd_check_tools/tools"
  11. "strconv"
  12. "strings"
  13. "sync"
  14. "github.com/astaxie/beego/orm"
  15. )
  16. //SCD间隔管理
  17. type ScdAreaMgr struct {
  18. DeviceBaseModel
  19. //SCD文件ID
  20. ScdId int64
  21. //电压等级定义
  22. VoltageLevelDef map[string]int32
  23. //设备类型定义
  24. DeviceTypeDef map[string]int
  25. CacheAreaID map[string]int32
  26. CacheAreaIDByIedNameNo map[string]int32
  27. CacheLock sync.RWMutex
  28. }
  29. type T_substation_area struct {
  30. Id int64 `orm:"pk"`
  31. Name string
  32. NameNo string
  33. ScdId int64
  34. VoltageLevel int32
  35. DispatchNo string
  36. SubstationId int
  37. CreatedBy int
  38. CreatedTime string
  39. }
  40. type t_area_ied_relation struct {
  41. Id int32 `orm:"pk"`
  42. AreaId int32
  43. IedId int64
  44. IedName string
  45. IedType string
  46. IedDesc string
  47. PType string //归属设备类型
  48. Vol string //电压编码
  49. PCode string //归属设备编码
  50. IedNo string //IED编号(A\B\C)
  51. ScdId int64
  52. }
  53. // 其他非标准装置列表。通过系统参数OtherIedNameList维护
  54. var otherIedNameList map[string]bool
  55. func init() {
  56. orm.RegisterModel(new(T_substation_area))
  57. orm.RegisterModel(new(t_area_ied_relation))
  58. }
  59. func (c *ScdAreaMgr) Init(scdid int64) {
  60. c.VoltageLevelDef = map[string]int32{}
  61. c.DeviceTypeDef = map[string]int{}
  62. c.CacheAreaIDByIedNameNo = map[string]int32{}
  63. c.CacheAreaID = map[string]int32{}
  64. c.CacheLock = sync.RWMutex{}
  65. db := orm.NewOrm()
  66. rowset := []orm.Params{}
  67. db.Raw("select * from global_const_code where parentcode=?", "voltage_level").Values(&rowset)
  68. for _, row := range rowset {
  69. vl := strings.ToLower(tools.IsEmpty(row["code"]))
  70. id, _ := strconv.ParseInt(tools.IsEmpty(row["id"]), 10, 32)
  71. c.VoltageLevelDef[strings.ReplaceAll(vl, "v_level_", "")] = int32(id)
  72. }
  73. db.Raw("select * from global_const_code where parentcode=?", "device_type").Values(&rowset)
  74. for _, row := range rowset {
  75. vl := tools.IsEmpty(row["code"])
  76. c.DeviceTypeDef[vl] = 1
  77. }
  78. v, _ := GetSysParamValue("OtherIedNameList", "")
  79. otherIedNameList = map[string]bool{}
  80. if v != "" {
  81. vs := strings.Split(v, ",")
  82. for _, vv := range vs {
  83. otherIedNameList[vv] = true
  84. }
  85. }
  86. }
  87. //保存指定间隔所属的电压等级
  88. func (c *ScdAreaMgr) SetVoltageLevel(id string, voltagelevel int) error {
  89. db := orm.NewOrm()
  90. _, err := db.Raw("update t_substation_area set voltage_level=? where id=?", voltagelevel, id).Exec()
  91. return err
  92. }
  93. //修改指定间隔的名称
  94. func (c *ScdAreaMgr) UpdateName(scdid int64, area_id int, name string) error {
  95. db := orm.NewOrm()
  96. areaM := T_substation_area{Id: int64(area_id), ScdId: scdid}
  97. err := db.Read(&areaM)
  98. if err != nil {
  99. logger.Logger.Error(err)
  100. return err
  101. }
  102. areaM.Name = name
  103. _, err = db.Update(&areaM)
  104. if err != nil {
  105. logger.Logger.Error(err)
  106. }
  107. return err
  108. }
  109. //修改指定IED的所属间隔
  110. func (c *ScdAreaMgr) UpdateIedArea(scdid int64, iedname string, area_id int) error {
  111. db := orm.NewOrm()
  112. _, err := db.Raw("update t_area_ied_relation set area_id=? where scd_id=? and ied_name=?", area_id, scdid, iedname).Exec()
  113. return err
  114. }
  115. //获取指定scd的间隔信息
  116. func (c *ScdAreaMgr) GetAreaList(scdid int64) ([]orm.Params, error) {
  117. db := orm.NewOrm()
  118. sql := "select t.*,(select count(1) from t_area_ied_relation where area_id=t.id) iedcount from t_substation_area t where t.scd_id=? order by t.name"
  119. rowset := []orm.Params{}
  120. _, err := db.Raw(sql, scdid).Values(&rowset)
  121. sqllog := fmt.Sprintf("SQL:%s 参数:%+v", sql, []interface{}{scdid})
  122. if err != nil {
  123. logger.Logger.Error(err, sqllog)
  124. new(SystemLog).Fail(enum.AuditType_scd_show, enum.LogType_Query, enum.OptEventType_Bus, enum.OptEventLevel_Low, sqllog, c.GetUserInfo())
  125. } else {
  126. new(SystemLog).Success(enum.AuditType_scd_show, enum.LogType_Query, enum.OptEventType_Bus, enum.OptEventLevel_Low, sqllog, c.GetUserInfo())
  127. }
  128. return rowset, nil
  129. }
  130. //获取指定scd和电压等级的间隔信息
  131. func (c *ScdAreaMgr) GetAreaListByVol(scdid int64, vl int32) ([]orm.Params, error) {
  132. db := orm.NewOrm()
  133. sql := "select * from t_substation_area where scd_id=? and voltage_level=? order by name"
  134. rowset := []orm.Params{}
  135. _, err := db.Raw(sql, scdid, vl).Values(&rowset)
  136. sqllog := fmt.Sprintf("SQL:%s 参数:%+v", sql, []interface{}{scdid, vl})
  137. if err != nil {
  138. logger.Logger.Error(err, sqllog)
  139. new(SystemLog).Fail(enum.AuditType_scd_show, enum.LogType_Query, enum.OptEventType_Bus, enum.OptEventLevel_Low, sqllog, c.GetUserInfo())
  140. } else {
  141. new(SystemLog).Success(enum.AuditType_scd_show, enum.LogType_Query, enum.OptEventType_Bus, enum.OptEventLevel_Low, sqllog, c.GetUserInfo())
  142. }
  143. return rowset, nil
  144. }
  145. //获取指定间隔下的IED列表
  146. func (c *ScdAreaMgr) GetIedList(scdid int64, voltage_level_id, areaid int32, device_type string) ([]orm.Params, error) {
  147. db := orm.NewOrm()
  148. sql := "select * from t_substation_area t,t_area_ied_relation t1 where t.scd_id=? and t.id=t1.area_id "
  149. sqlParamters := []interface{}{}
  150. sqlParamters = append(sqlParamters, scdid)
  151. if voltage_level_id > 0 {
  152. sql = sql + " and t.voltage_level=?"
  153. sqlParamters = append(sqlParamters, voltage_level_id)
  154. }
  155. if areaid > 0 {
  156. sql = sql + " and t1.area_id=?"
  157. sqlParamters = append(sqlParamters, areaid)
  158. }
  159. scdXmlObj, serr := new(ScdParse).GetScdXmlObjectBySCDID(tools.IsEmpty(scdid))
  160. if serr != nil {
  161. return nil, serr
  162. }
  163. if scdXmlObj == nil {
  164. return nil, errors.New("无效的SCD")
  165. }
  166. rowset := []orm.Params{}
  167. if device_type != "" {
  168. //根据装备类型查询IED
  169. sql = sql + " and t1.ied_type=?"
  170. sqlParamters = append(sqlParamters, device_type)
  171. }
  172. _, err := db.Raw(sql, sqlParamters).Values(&rowset)
  173. sqllog := fmt.Sprintf("SQL:%s 参数:%+v", sql, sqlParamters)
  174. if err != nil {
  175. logger.Logger.Error(err, sqllog)
  176. new(SystemLog).Fail(enum.AuditType_scd_show, enum.LogType_Query, enum.OptEventType_Bus, enum.OptEventLevel_Low, sqllog, c.GetUserInfo())
  177. } else {
  178. scdNode := new(ScdNode)
  179. for _, row := range rowset {
  180. iedid, _ := strconv.ParseInt(tools.IsEmpty(row["ied_id"]), 10, 64)
  181. iedObj := scdNode.GetIedByID(scdXmlObj, tools.IsEmpty(scdid), iedid)
  182. if iedObj == nil {
  183. continue
  184. }
  185. row["attr_name"] = iedObj.Name
  186. row["attr_desc"] = iedObj.Desc
  187. row["attr_config_version"] = iedObj.ConfigVersion
  188. row["attr_type"] = iedObj.Type
  189. row["attr_manufacturer"] = iedObj.Manufacturer
  190. row["ied_id"] = iedObj.NodeId
  191. }
  192. new(SystemLog).Success(enum.AuditType_scd_show, enum.LogType_Query, enum.OptEventType_Bus, enum.OptEventLevel_Low, sqllog, c.GetUserInfo())
  193. }
  194. return rowset, nil
  195. }
  196. //获取指定SCD的IED类型列表
  197. func (c *ScdAreaMgr) GetIedTypeList(scdid int64) ([]orm.Params, error) {
  198. db := orm.NewOrm()
  199. sql := "select t2.* from t_area_ied_relation t1,global_const_code t2 where t1.scd_id=? and t1.ied_type=t2.code and t2.parentcode='device_type' group by t1.ied_type "
  200. sqlParamters := []interface{}{}
  201. rowset := []orm.Params{}
  202. sqlParamters = append(sqlParamters, scdid)
  203. _, err := db.Raw(sql, sqlParamters).Values(&rowset)
  204. sqllog := fmt.Sprintf("SQL:%s 参数:%+v", sql, sqlParamters)
  205. if err != nil {
  206. logger.Logger.Error(err, sqllog)
  207. new(SystemLog).Fail(enum.AuditType_scd_show, enum.LogType_Query, enum.OptEventType_Bus, enum.OptEventLevel_Low, sqllog, c.GetUserInfo())
  208. }
  209. return rowset, nil
  210. }
  211. func (c *ScdAreaMgr) One(id string) (interface{}, error) {
  212. db := orm.NewOrm()
  213. idInt, err := strconv.ParseInt(id, 10, 64)
  214. if err != nil {
  215. return nil, err
  216. }
  217. areaM := T_substation_area{Id: idInt}
  218. err = db.Read(&areaM)
  219. if err == nil {
  220. return areaM, nil
  221. }
  222. return nil, err
  223. }
  224. //重置scd的间隔信息
  225. func (c *ScdAreaMgr) Reset(scdid int64) error {
  226. db := orm.NewOrm()
  227. db.Raw("delete from t_area_ied_relation where area_id in(select id from t_substation_area where scd_id=?)", scdid).Exec()
  228. db.Raw("delete from t_substation_area where scd_id=?", scdid).Exec()
  229. sql := "select * from t_scd_ied_attrs where scd_id=?"
  230. rowset := []orm.Params{}
  231. _, err := db.Raw(sql, scdid).Values(&rowset)
  232. if err != nil {
  233. log.Println(err)
  234. return err
  235. }
  236. for _, row := range rowset {
  237. ied := new(t_scd_node_scl)
  238. ied.Id, _ = strconv.ParseInt(tools.IsEmpty(row["node_id"]), 10, 64)
  239. iedNodeDesc := tools.IsEmpty(row["attr_desc"])
  240. ied.NodeName = tools.IsEmpty(row["attr_name"])
  241. ied.ScdId = scdid
  242. c.AppendIedNode(scdid, ied.Id, ied.NodeName, iedNodeDesc)
  243. }
  244. logdesc := fmt.Sprintf("重置SCD %d间隔成功", scdid)
  245. new(SystemLog).Success(enum.AuditType_scd_show, enum.LogType_Update, enum.OptEventType_Bus, enum.OptEventLevel_Low, logdesc, c.GetUserInfo())
  246. return nil
  247. }
  248. //根据scd中的Bay节点解析间隔
  249. func (c *ScdAreaMgr) ParseBay(substation_id int, lst []*node_attr.NVoltage) {
  250. if c.DeviceTypeDef == nil {
  251. c.Init(c.ScdId)
  252. }
  253. db := orm.NewOrm()
  254. voltages := []string{"其它", "其他"}
  255. for _, item := range lst {
  256. voltages = append(voltages, item.Name)
  257. }
  258. //电压等级节点属性表:t_scd_voltage_attrs。对比电压等级与定义的码表,如果缺失时自动新增
  259. sql := `select t1.id,t1.name,t1.code from global_const_code t1 where t1.parentcode='voltage_level' and t1.name in('` + strings.Join(voltages, "','") + `') `
  260. rowset := []orm.Params{}
  261. _, err := db.Raw(sql).Values(&rowset)
  262. if err != nil {
  263. logger.Logger.Error(err, fmt.Sprintf("SQL:%s ", sql))
  264. } else {
  265. voltage_id_map := map[string]string{}
  266. for _, row := range rowset {
  267. voltage_name := tools.IsEmpty(row["code"])
  268. voltage_id_map[strings.ReplaceAll(voltage_name, "v_level_", "")] = tools.IsEmpty(row["id"])
  269. }
  270. clearSql := "delete from t_substation_area where substation_id=? and scd_id=?"
  271. _, err = db.Raw(clearSql, substation_id, c.ScdId).Exec()
  272. if err != nil {
  273. logger.Logger.Error(err, fmt.Sprintf("SQL:%s 参数:%+v", clearSql, []interface{}{substation_id, c.ScdId}))
  274. return
  275. }
  276. //先清除ied与间隔关联关系
  277. clearSql = "delete from t_area_ied_relation where area_id in(select id from t_substation_area where substation_id=? and scd_id=?)"
  278. _, err = db.Raw(clearSql, substation_id, c.ScdId).Exec()
  279. if err != nil {
  280. logger.Logger.Error(err, fmt.Sprintf("SQL:%s 参数:%+v", clearSql, []interface{}{substation_id, c.ScdId}))
  281. return
  282. }
  283. bayCols := "insert into t_substation_area(substation_id,scd_id,voltage_level,name,name_no)values"
  284. bayValues := []string{}
  285. for _, volitem := range lst {
  286. voltage_name := volitem.Name
  287. voltage_id := voltage_id_map[voltage_name]
  288. if voltage_id == "" {
  289. voltage_id = voltage_id_map["其它"]
  290. }
  291. if voltage_id == "" {
  292. voltage_id = voltage_id_map["其他"]
  293. }
  294. for _, bayitem := range volitem.Bay {
  295. bayValues = append(bayValues, fmt.Sprintf(`(%d,%d,%s,'%s','')`, substation_id, c.ScdId, voltage_id, bayitem.Name))
  296. }
  297. sql := bayCols + strings.Join(bayValues, ",")
  298. //添加间隔
  299. _, err = db.Raw(sql).Exec()
  300. if err != nil {
  301. logger.Logger.Error(err, fmt.Sprintf("SQL:%s", sql))
  302. } else {
  303. //创建ied与间隔关联关系
  304. relCols := "insert into t_area_ied_relation(scd_id, area_id,ied_id,ied_name,ied_desc,ied_type,p_type,vol,p_code,ied_no)values"
  305. relValues := []string{}
  306. for _, bayitem := range volitem.Bay {
  307. relValues = []string{}
  308. areaRowset := []orm.Params{}
  309. _, err = db.Raw("select id from t_substation_area where substation_id=? and scd_id=? and voltage_level=?", substation_id, c.ScdId, voltage_id).Values(&areaRowset)
  310. if err != nil {
  311. logger.Logger.Error(err)
  312. } else {
  313. areaid := tools.IsEmpty(areaRowset[0]["id"])
  314. for _, iedItem := range bayitem.IED {
  315. //解析类型
  316. //补全实际ied name为8位
  317. new_ied_name_full := new(ScdParse).ParseIedName(iedItem.Name)
  318. iedtype := "xy" //其他装置类型
  319. ptype := ""
  320. vol := ""
  321. pcode := ""
  322. iedno := ""
  323. name := iedItem.Name
  324. if len(name) >= 3 {
  325. lastchar := new_ied_name_full[0] + new_ied_name_full[1]
  326. ptype = new_ied_name_full[2]
  327. vol = new_ied_name_full[3] + new_ied_name_full[4]
  328. pcode = new_ied_name_full[5] + new_ied_name_full[6]
  329. iedno = new_ied_name_full[7]
  330. if c.DeviceTypeDef[lastchar] == 0 {
  331. iedtype = "xy"
  332. } else {
  333. iedtype = lastchar
  334. }
  335. voltagelevelid := c.VoltageLevelDef[vol]
  336. if voltagelevelid == 0 {
  337. vol = "999"
  338. }
  339. }
  340. relValues = append(relValues, fmt.Sprintf("(%d,%s,%d,'%s','%s','%s','%s','%s','%s','%s')", c.ScdId, areaid, iedItem.NodeId, iedItem.Name, iedItem.Desc, iedtype, ptype, vol, pcode, iedno))
  341. }
  342. if len(relValues) == 0 {
  343. continue
  344. }
  345. sql2 := relCols + strings.Join(relValues, ",")
  346. //添加间隔
  347. _, err = db.Raw(sql2).Exec()
  348. if err != nil {
  349. logger.Logger.Error(err, fmt.Sprintf("SQL:%s", sql2))
  350. }
  351. }
  352. }
  353. }
  354. }
  355. }
  356. }
  357. //添加新的需要解析的Ied装置
  358. //name:ied的name属性值
  359. //str:ied的desc属性值
  360. func (c *ScdAreaMgr) AppendIedNode(scdId, iedId int64, name, str string) {
  361. //fmt.Println("ScdAreaMgr.AppendIedNode:" + name + " " + str)
  362. if str == "" {
  363. return
  364. }
  365. if c.DeviceTypeDef == nil {
  366. c.Init(c.ScdId)
  367. }
  368. go func(scdId, iedId int64, name, str string) {
  369. areaStartPos := 0
  370. areaEndPos := 0
  371. reg := regexp.MustCompile(`(?i)kv`)
  372. r := reg.FindStringIndex(str)
  373. if r != nil {
  374. areaStartPos = r[1]
  375. }
  376. voltagelevel := strings.ToLower(strings.Trim(str[0:areaStartPos], " "))
  377. //优先匹配关键词
  378. reg = regexp.MustCompile(`(测控|智能|保护|合并)`)
  379. r = reg.FindStringIndex(str)
  380. if r != nil {
  381. areaEndPos = r[0]
  382. }
  383. areaResult := ""
  384. if len(r) == 0 {
  385. //次级匹配关键词。在装置desc中未发现任意优先匹配关键词,再按以下关键尝试匹配
  386. reg = regexp.MustCompile(`(分段|备|自投)`)
  387. r = reg.FindStringIndex(str)
  388. if r != nil {
  389. areaEndPos = r[0]
  390. }
  391. if areaEndPos == 0 {
  392. areaResult = "公用"
  393. } else {
  394. areaResult = strings.Trim(str[areaStartPos:areaEndPos], " ")
  395. }
  396. } else {
  397. areaResult = strings.Trim(str[areaStartPos:areaEndPos], " ")
  398. }
  399. if len(areaResult) < 2 {
  400. areaResult = str
  401. }
  402. areaResult = strings.Trim(strings.ReplaceAll(areaResult, "线路", "线"), " ")
  403. //logger.Logger.Debug(fmt.Sprintf("解析装置%s电压及间隔:电压%s 间隔:%s", str, voltagelevel, areaResult))
  404. c.CacheLock.Lock()
  405. //补全实际ied name为8位
  406. new_ied_name_full := new(ScdParse).ParseIedName(name)
  407. //解析类型
  408. iedtype := "xy" //其他装置类型
  409. ptype := ""
  410. vol := ""
  411. pcode := ""
  412. iedno := ""
  413. if len(name) >= 3 {
  414. lastchar := new_ied_name_full[0] + new_ied_name_full[1]
  415. if c.DeviceTypeDef[lastchar] == 0 {
  416. iedtype = "xy"
  417. } else {
  418. iedtype = lastchar
  419. }
  420. ptype = new_ied_name_full[2]
  421. vol = new_ied_name_full[3] + new_ied_name_full[4]
  422. pcode = new_ied_name_full[5] + new_ied_name_full[6]
  423. iedno = new_ied_name_full[7]
  424. voltagelevel = vol
  425. }
  426. //所属间隔判断规则
  427. //1、判断解析得到的间隔名称是否已存在
  428. //2、不存在时,根据装置name中的数字编号(如CM1101中的1101),查找同编号且已归属间隔的任意设置所属间隔
  429. db := orm.NewOrm()
  430. voltagelevelid := c.VoltageLevelDef[voltagelevel]
  431. logger.Logger.Debug(fmt.Sprintf("%s %s 电压等级:%s ID:%d 间隔名称:%s ", name, str, voltagelevel, voltagelevelid, areaResult))
  432. isNoOtherVL := 1
  433. if voltagelevelid == 0 {
  434. isNoOtherVL = 0
  435. voltagelevelid = c.VoltageLevelDef["其它"]
  436. vol = "999"
  437. }
  438. if otherIedNameList[name] {
  439. voltagelevelid = c.VoltageLevelDef["其它"]
  440. }
  441. areaid := c.CacheAreaID[fmt.Sprintf("%d%s", voltagelevelid, areaResult)]
  442. if areaid == 0 {
  443. //新增一个新的间隔
  444. areaM := T_substation_area{}
  445. areaM.Name = areaResult
  446. areaM.ScdId = scdId
  447. areaM.NameNo = vol
  448. areaM.VoltageLevel = voltagelevelid
  449. areaM.CreatedTime = tools.NowTime()
  450. newid, err := db.Insert(&areaM)
  451. if err != nil {
  452. logger.Logger.Error(err)
  453. }
  454. c.CacheAreaID[fmt.Sprintf("%d%s", voltagelevelid, areaResult)] = int32(newid)
  455. areaid = int32(newid)
  456. } else {
  457. //如果当前电压等级不是“其它”,则将之前归属于其他电压的间隔更新到当前电压等级下
  458. if isNoOtherVL == 1 {
  459. db.Raw("update t_substation_area set voltage_level=? where name_no=? and scd_id=?", voltagelevelid, vol, scdId).Exec()
  460. }
  461. }
  462. c.CacheLock.Unlock()
  463. rel := t_area_ied_relation{AreaId: areaid, IedId: iedId, IedName: name, IedDesc: str, IedType: iedtype, PType: ptype, Vol: vol, PCode: pcode, IedNo: iedno, ScdId: scdId}
  464. db.Insert(&rel)
  465. //logdesc := fmt.Sprintf("添加间隔,操作数据:%+v", rel)
  466. //new(SystemLog).Success(enum.AuditType_scd_show, enum.LogType_Insert, enum.OptEventType_Bus, enum.OptEventLevel_Low, logdesc, c.UserInfo)
  467. }(scdId, iedId, name, str)
  468. }
  469. //测试
  470. func (c *ScdAreaMgr) TestAppendNode(iedname string) {
  471. //t := t_scd_node_scl{NodeName: iedname}
  472. //c.AppendIedNode(&t)
  473. }