scd_area.go 17 KB

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