智能变电站SCD文件解析&虚端子软压板&增加描述信息

python解析智能变电站SCD文件,获取虚端子和软压板信息。

from lxml import etree
import xlwt
import os
import time
import sys
def gainNS(filename):
    # 首先获取xml文件的命名空间
    currentDir = os.getcwd()
    ns = ''
    absDir = currentDir + '\\' + filename
    with open(absDir, 'r', encoding='utf-8') as f:
        # print('正在获取命名空间...')
        for i in range(100):  # 读取前十行
            line = f.readline()
            items = line.split()  # 以空格分割
            for i in items:
                if 'xmlns=' in i:
                    ns = i.split('"')[-2]
                    break
    ns = '{' + ns + '}'
    # print('命名空间获取成功:' + ns)
    return ns
def removeNull(str): #去除identify中的null,已经用不到了
    if 'null' in str:
        return ''.join(str.split('null'))
    else:
        return str
def gainID(x): #寻找LN节点的三个属性值组成ID
    try:
        res=x.get('prefix','') + x.get('lnClass','') + x.get('inst','' )
    except Exception:
        print('未知错误', "逻辑节点:" +str(x))
    return res
def addIEDdesc(string,dictionary): #给IED名字添加描述信息
    name=string.split('.')[0]
    desc=dictionary[name]
    res=string.replace(name,name+'.'+desc)
    return res
def gainStrapsAndVT(filename):
    ns=gainNS(filename)
    tree=etree.parse(filename)
    root=tree.getroot()
    IEDs=root.findall(ns+'IED') #找到所有的IED节点
    print("IED总数:"+str(len(IEDs)))
    IEDandDesc={}  #字典
    for i in IEDs:
        IEDandDesc[i.get('name')]=i.get('desc')
    # print('字典长度为:'+str(len(IEDandDesc)))
    if len(IEDs)!=len(IEDandDesc):
        print('在给IED名字添加描述信息时出错,请检查字典长度。')
    worbook = xlwt.Workbook()
    sheet1 = worbook.add_sheet('VT', cell_overwrite_ok=True)
    sheet2 = worbook.add_sheet('strap', cell_overwrite_ok=True)
    i = 0 #对虚端子计数
    j = 0 #对软压板计数
    print("正在读取虚端子和软压板...")

    for ied in IEDs:
        iedName=ied.get('name') #获取当前IED的名字
        links=ied.iter(ns+'ExtRef') #找到每条链路
        for item in links:
            intAddr = iedName+'.'+item.get('intAddr').split(':')[-1]#增加了IED名字属性
            identifyLN=item.get('intAddr').split('/')[-1].split('.')[0]  # intAddr里面的LN值可以唯一确定对应LDevice下的LN节点
            identifyLD=item.get('intAddr').split(':')[-1].split('/')[0] #从intAddr里解析出逻辑设备名
            identifyDO=item.get('intAddr').split('.')[1] #从intAddr里解析出数据对象名
            # LDevice=item.find('.......')   #先寻找当前LN0的父节点LDevice,这里使用了xpath,..表示父节点,......表示三级父节点,七个点表示当前元素的三级父节点
            LDevice = [x for x in ied.iter(ns + 'LDevice') if x.get('inst') == identifyLD][0]
            try:
                ln = [x for x in LDevice.findall(ns+"LN0") +LDevice.findall(ns+"LN") if gainID(x) == identifyLN]  # 找到LN节点
            except Exception as e:
                print('未知错误', "IED:"+iedName,"逻辑设备:"+LDevice.get('inst'),e)
                # sys.exit()
            if len(ln)!=1:
                print('寻找输入虚端子desc属性时符合条件的逻辑节点有%d个,当前IED为%s,虚端子intAddr为%s'%(len(ln),iedName,item.get('intAddr')))
            ln=ln[0]
            DOIs=[x for x in ln if x.get('name')==identifyDO]
            if len(DOIs)!=1:
                print('寻找intAddr对应的数据对象时出错,找到%d个DOI。' % len(DOIs))
            DOI=DOIs[0]
            intAddr = intAddr + '/' + ln.get('desc', '无描述') + '/' + DOI.get('desc', '无描述')
            outAddr = item.get('iedName', 'null') + '.' + item.get('ldInst', 'null') + '/' +item.get('prefix', '')+\
                      item.get('lnClass','null') + item.get('lnInst', '')+ '.' + item.get('doName', 'null') + '.' + item.get('daName', 'null')
            #找对应IED,标识是iedName
            outAddrIED=[x for x in IEDs if x.get('name')==item.get('iedName')][0]
            #找对应LDevice,标识是ldInst逻辑设备实例
            outAddrLDevice=[x for x in outAddrIED.iter(ns+'LDevice') if x.get('inst')==item.get('ldInst')]
            if len(outAddrLDevice)!=1:
                print('寻找outAddr对应的逻辑设备时出错,找到%d个逻辑设备。'%len(outAddrLDevice))
            outAddrLDevice=outAddrLDevice[0]
            #找对应LN,标识是prefix+lnClass+lnInst
            identifyLN = item.get('prefix', '') + item.get('lnClass', '') + item.get('lnInst', '')
            # outAddrLN=[x for x in outAddrLDevice.iter(ns+'LN') if gainID(x)==identify] #outAddrLDevice.iter(ns+'LN')报错,因为要找的LN有可能是LN0
            outAddrLN = [x for x in outAddrLDevice.findall(ns + "LN0") + outAddrLDevice.findall(ns + "LN") if gainID(x) == identifyLN]
            if len(outAddrLN)!=1:
                print('寻找outAddr对应的逻辑节点时出错,找到%d个逻辑节点。' % len(outAddrLN))
                print('当前intAddr为%s'%intAddr)
                print('当前outAddrIED为%s,outAddrLDevice为%s,标识为%s'%(outAddrIED.get('name'),outAddrLDevice.get('inst'),identifyLN))
            outAddrLN=outAddrLN[0]
            #找对应的DOI,获取中文描述信息,标识是doName
            outAddrDOI=[x for x in outAddrLN if x.get('name')==item.get('doName')]
            if len(outAddrDOI)!=1:
                print('寻找outAddr对应的数据对象时出错,找到%d个DOI。' % len(outAddrDOI))
            outAddrDOI=outAddrDOI[0]
            outAddrDesc=outAddrLN.get('desc','无描述')+ '/'+ outAddrDOI.get('desc','无描述')
            outAddr=outAddr+ '/' + outAddrDesc
            # intAddr=addIEDdesc(intAddr,IEDandDesc)
            # outAddr=addIEDdesc(outAddr,IEDandDesc)
            sheet1.write(i, 0, outAddr)
            sheet1.write(i, 1, intAddr)
            i += 1
        dataSet = [x for x in ied.iter(ns+'DataSet') if 'dsRelayEna' in x.get('name', 'noname')]  # 这里name是属性,所以不用加上名字空间,只有tag才需要加上名字空间
        #dataSet里面可能不止一个dsRelayEna数据集,还有dsRelayEna2数据集
        for data in dataSet: #找到软压板数据集,因为使用了列表生成式,所以dataSet是一个列表
            for item in data: #这里item是每行软压板数据FCDA节点,data是DataSet节点
                LDevice=[x for x in ied.iter(ns+'LDevice') if x.get('inst')==item.get('ldInst')][0]
                # LDevice = data.find('.....')
                identifyLN = item.get('prefix', '') + item.get('lnClass', '') + item.get('lnInst', '')  # 使用此标识寻找LDevice下的LN
                ln = [x for x in LDevice.findall(ns+"LN0") +LDevice.findall(ns+"LN") if gainID(x) == identifyLN]  # 找到LN节点
                if len(ln) != 1:
                    print('当前IED为%s,软压板标识为%s.' % (iedName, identifyLN + item.get("doName")))
                    print('找到%d个LN' % len(ln))
                ln = ln[0]
                DOIs = [x for x in ln if x.get('name') == item.get('doName')]
                if len(DOIs)!=1:
                    print('结果可能错误,当前DOI数量不唯一。')
                DOI = DOIs[0]
                desc=ln.get('desc','无描述') + '/' + DOI.get('desc','无描述')

                strap_info = iedName+'.'+item.get("ldInst", "null") + '/' + item.get('prefix', '') + \
                             item.get('lnClass', 'null') + item.get('lnInst', '') + '.' + item.get("doName", "null")+'/'+desc
                #软压板包含多个属性,strap_info没有列举完
                # strap_info=addIEDdesc(strap_info,IEDandDesc)
                sheet2.write(j, 0, strap_info)
                j+=1

    print("此文件共有%d个虚端子,已经写入Excel文件VT工作表中" % i)
    print("此文件共有%d个软压板,已经写入Excel文件strap工作表中"%j)
    worbook.save(filename.split('.')[0] + '虚端子和软压板.xls')

if __name__ == '__main__':
    beginTime=time.time()
    
    gainStrapsAndVT('files\\xxx.scd')

    endTime=time.time()
    print("共耗时:"+str(endTime-beginTime)+"秒。")

解析excel文件,获得全局连接矩阵:

import xlrd
from lxml import etree
import extractNew
import numpy as np

def readExcel(filename):
    wb = xlrd.open_workbook(filename)  # 打开文件
    sheetVT = wb.sheet_by_name('VT')  # 通过名字获取表格
    sheetStraps=wb.sheet_by_name('strap')
    sender = sheetVT.col_values(0)  # 获取列内容,第一列是发送虚端子
    receiver=sheetVT.col_values(1)
    straps=sheetStraps.col_values(0)
    return sender,receiver,straps

def getIEDFromFile(scdFile): #从scd文件中读取IED
    tree=etree.parse(scdFile)
    root=tree.getroot()
    ns=extractNew.gainNS(scdFile)
    IEDs=root.iter(ns+'IED')
    IEDs=[x.get('name') for x in IEDs]
    return IEDs

def getMissingIED(a,b): #找到不含虚端子的IED(对比从excel文件和scd文件读取到的IED即可)
    res=[]
    for i in a:
        if i not in b:
            res.append(i)
    return res

def getIEDGlobalMat(filename): #获取全局IED连接矩阵
    sender,receiver,straps = readExcel(filename)
    senderIED = [x.split('.')[0] for x in sender]
    receiverIED=[x.split('.')[0] for x in receiver]
    # IEDs=[] #从excel文件中读取IED
    # for i in receiverIED + senderIED:
    #     IEDs.append(i)
    # IEDs=list(set(IEDs))
    IEDs=getIEDFromFile(filename.split('虚端子和软压板')[-2]+'.scd')
    # misingIED= getMissingIED(IEDFromFile,IEDs) #并非所有IED都有输入输出虚端子
    lengthOfIEDs=len(IEDs)
    IEDIndex=list(zip(range(lengthOfIEDs),IEDs)) #将IEDs中的顺序固定下来
    IEDGlobalMat=np.zeros([lengthOfIEDs,lengthOfIEDs])
    links = list(zip(senderIED, receiverIED))  # IED之间的联系
    # 遍历links,填充ied矩阵
    for link in links:
        sender = link[0]  # 发送IED
        receiver=link[1] #link[1]是接收IED
        indexX=IEDs.index(sender) #x表示发送IED,y表示接收IED
        indexY=IEDs.index(receiver)
        IEDGlobalMat[indexX,indexY]+=1 #每一行表示以对应IED为发送IED的连接关系,每一列表示以对应IED为接收IED的连接关系
    writeResToFile(IEDGlobalMat,IEDIndex,filename.replace('虚端子和软压板.xls','全局IED连接矩阵(m).txt'))
    print("全局IED连接矩阵已经写入txt文件中。")
    return IEDGlobalMat,IEDIndex  #IED连接矩阵不一定是对角阵

def getBoundary(filename,iedName): #找检修边界
    IEDGlobalMat, IEDIndex=getIEDGlobalMat(filename)
    for i in range(len(IEDIndex)):
        if IEDIndex[i][1]==iedName:
            index=i
    listX = IEDGlobalMat[index] #第i行
    listY = IEDGlobalMat[:,index] #第i列
    connectedIED = []
    for i in range(len(listX)):
        if listX[i] >0 or listY[i] >0:
            connectedIED.append(IEDIndex[i][1])
    # print(connectedIED)

    return

def matchVTandStraps(filename):  #虚端子软压板匹配
    sender, receiver, straps= readExcel(filename)
    IEDs=getIEDFromFile(filename.split('虚端子和软压板')[-2]+'.scd')
    return


def writeResToFile(res,index,filename):
    with open(filename, 'w', encoding='utf-8') as f:
        f.write(str(index)+'\n')
        for i in res:
            for j in i:
                # f.write(str(j)[0]+'   ') #这三种方法都可以
                # f.write(str('%.0f'%j) + '   ')
                f.write(str('%d'%j) + ' ')
            f.write('\n')

if __name__ == '__main__':
    # res = getIEDGlobalMat('files\\xxx.xls')
    


其他程序:

import numpy as np
import getIEDMatrix2
import xlwt
import getPeiTing
import xlrd

def getVTandStraps(filename): #提取每个IED的软压板和虚端子对,保存在新的表单里
    IEDIndex, IEDGlobalMat=getPeiTing.readTXT(filename)
    sender, receiver, straps = getIEDMatrix2.readExcel(filename.split('全局IED连接矩阵')[0] + '虚端子和软压板.xls')
    links = list(zip(sender,receiver))
    worbook = xlwt.Workbook()
    for index,iedName in IEDIndex.items():
        linksToMatch=[]
        strapsToMatch=[]
        for i in range(len(sender)):        #先是以IED为发送的链路,再是接收
            if sender[i].split('.')[0]==iedName:
                linksToMatch.append(links[i])
        for i in range(len(receiver)):
            if receiver[i].split('.')[0]==iedName:
                linksToMatch.append(links[i])
        for i in range(len(straps)):
            if straps[i].split('.')[0]==iedName:
                strapsToMatch.append(straps[i])

        sheet = worbook.add_sheet(iedName, cell_overwrite_ok=True)
        sheet.write(0, 0, iedName+'的软压板')
        sheet.write(0, 1, iedName+'的虚端子对')
        for i in range(len(strapsToMatch)):
            sheet.write(i+1, 0, strapsToMatch[i])
        for i in range(len(linksToMatch)):
            sheet.write(i+1, 1, linksToMatch[i][0])
            sheet.write(i+1, 2, linksToMatch[i][1])
    worbook.save(filename.split('全局IED连接矩阵')[0] + '待匹配虚端子和软压板new.xls')
    print("文件写入成功。")

if __name__ == '__main__':
    filename = 'files\\xxx.txt'
    getVTandStraps(filename)
import xlrd
import xlwt
import numpy as np

def mergeExcel(filename):       #把虚端子对应表融合成一个表单
    wbRead = xlrd.open_workbook(filename)  # 打开文件
    sheetNames = wbRead.sheet_names()
    resArray = []  # 先声明一个空list
    for i in sheetNames:
        table = wbRead.sheet_by_name(i)
        for j in range(table.nrows):  # table.nrows表示总行数
            line = table.row_values(j)  # 读取每行数据,保存在line里面,line是list
            resArray.append(line)  # 将line加入到resArray中,resArray是二维list
    # resArray = np.array(resArray)  # 将resArray从二维list变成数组

    wbWrite = xlwt.Workbook()
    table = wbWrite.add_sheet('工作表1', cell_overwrite_ok=True)
    i = 0
    for j in resArray:
        for k in range(len(j)):
            table.write(i, k, j[k])
        i += 1
    wbWrite.save('test.xls')
    return
if __name__ == '__main__':
    filename = 'files\\虚端子对应表.xlsx'
    mergeExcel(filename)

版权声明:本文为zhhy236400原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。