def __init__(self, _scl, TR): ## Constructor for Server self.scl = _scl ## Pointer to the SCL as provided by 'minidom'. self.TR = TR ## Instance of the TRACE service. self.pLN = Parse_LN( TR ) ## Invoking the constructor of ParseLN, to initialize TRACE service. self.Dyn = DynImport()
def __init__(self, _scl, _TR): self.scl = _scl # self.TRX = _TR # self.Dyn = DynImport( ) ## Create an instance of DynImpot for private TAG
class ParseCommunication: def __init__(self, _scl, _TR): self.scl = _scl # self.TRX = _TR # self.Dyn = DynImport( ) ## Create an instance of DynImpot for private TAG ## # \b ParseCommSection handle the SubNetwork section of the SCL # # Parsing all the instance of SubNetwork # < Communication # < SubNetwork # <BitRate # <ConnectedAP # ... # @param pNetwork pointer to 'SubNetwork' tag in the SCL def ParseCommSection(self, pNetWork): # Analyse d'un IED # # # ... tNetWork = [] pSubNet = pNetWork[0].firstChild.nextSibling if pSubNet.localName != "SubNetwork": self.TRX.Trace(("Error: Tag SubNetwork not found"), TL.ERROR) while pSubNet: if pSubNet is None: break _name = pSubNet.getAttribute( "name" ) ##_name : A name identifying this bus; unique within this SCL file _type = pSubNet.getAttribute( "type" ) ## # @param _type : The SubNetwork protocol type; protocol types are defined by the SCSMs. In the ## examples, 8-MMS is used for the protocol defined in IEC 61850-8-1; IP should be used ## for all IP based protocols except those explicitly standardized. PHYSICAL should be ## used, if only physical connections shall be modeled, e.g. at a hub. _desc = pSubNet.getAttribute( "desc" ) ## @param _desc : Some descriptive text to this SubNetwork iSubNet = Comm.SubNetwork(_name, _type, _desc, 'text', 'bitrate', None) # None: reservé for ConnectedAP self.TRX.Trace(("SubNetWork: name:" + _name + " type:" + _type + " desc:" + _desc), TL.DETAIL) iSubNet = self.ParseSubNet(pSubNet, iSubNet) tNetWork.append(iSubNet) if pSubNet.firstChild is None: self.TRX.Trace(("Error: SubNet.firstChild is None"), TL.ERROR) if pSubNet.localName is None: pSubNet = pSubNet.nextSibling continue pSubNet = pSubNet.nextSibling if pSubNet is not None: pSubNet = pSubNet.nextSibling return (tNetWork) ## # \b ParseCommSection handle the SubNetwork section of the SCL # # Parse the subsequent TAG to 'subNetwork': # <Text ... # <BitRate ... # <ConnectedAP .. # # @param pSubNet pointer to 'SubNetwork' tag in the SCL # @param iSubNet instance of SubNetwork class def ParseSubNet(self, pSubNet, iSubNet): pSubNet = pSubNet.firstChild tAP = [] # Tableau des accès point pour un 'subNetWork'. while pSubNet is not None: if pSubNet is not None: pSubNet = pSubNet.nextSibling if pSubNet is None: break if pSubNet.localName is None: continue if pSubNet.localName == 'Private': self.TRX.Trace(("Private in SubNetworkSubNetwork"), TL.ERROR) self.Dyn.PrivateDynImport(type, pSubNet, iSubNet) continue if pSubNet.localName == "Text": if pSubNet.firstChild is not None: localText = pSubNet.firstChild.data else: localText = "pas de texte !" self.TRX.Trace(("SubNetWork" + localText), TL.GENERAL) iSubNet.text = localText if pSubNet.localName == "BitRate": Unit = pSubNet.getAttribute("unit") Value = pSubNet.firstChild.data _BitRate = Comm.SubNetwork.BitRate(Unit, Value) iSubNet.bitRate = _BitRate self.TRX.Trace( ("BitRate: Unit:" + Unit + "Value:" + Value), TL.DETAIL) if pSubNet.localName == "ConnectedAP": # def __init__(self,_iedName,_apName,_desc,_Address,_SMV,_PhysConn): pAP = pSubNet _iedName = pAP.getAttribute("iedName") _apName = pAP.getAttribute("apName") _desc = pAP.getAttribute("desc") _redProt = pAP.getAttribute("redProt") self.TRX.Trace((" ConnectedAP: iedName:" + _iedName + " apName:" + _apName + " desc: " + _desc), TL.DETAIL) iCnxAP = Comm.SubNetwork.ConnectedAP(_iedName, _apName, _desc, _redProt) iCnxAP = self.ParseConnectedAP(pAP, iCnxAP, self.TRX) tAP.append(iCnxAP) continue iSubNet.tConnectedAP = tAP return iSubNet def ParseConnectedAP(self, SubNet, CnxAP, TRX): # Partie Address de connected pAP. # <Address > # < P type = "IP-SUBNET" > 255.255.255.0 < / P > # ... # < P type = "OSI-SSEL" > 01 < / P > # </ Address > tAddress = [] if SubNet.firstChild is None: # cas de: <ConnectedAP iedName="L2_MU_SCU_VIZ_MGU" apName="ETH_2" /> return CnxAP pAP = SubNet.firstChild.nextSibling # CnxAP tGSE = [] tSMV = [] while pAP: # Pour tout les "ConnectedAP"... if pAP.localName is None: pAP = pAP.nextSibling if pAP is None: break continue if pAP.localName == "Private": self.TRX.Trace(("Private in SubNetworkSubNetwork"), TL.ERROR) self.Dyn.PrivateDynImport(type, pAP, CnxAP) pAP = pAP.nextSibling continue if pAP.localName == "Address": tAddress = self.ParseAddress(pAP.firstChild.nextSibling, "Address") #<Adress> # tAddress.append(iAddress) CnxAP.tAddress = tAddress if pAP.localName == "PhysConn": _type = pAP.getAttribute("type") iPhysConn = Comm.SubNetwork.ConnectedAP.PhysConn(_type, None) iPhysConn.tPhysAddress = self.ParseAddress( pAP.firstChild.nextSibling, "PhysConn") CnxAP.PhysConn.append(iPhysConn) if pAP.localName == "SMV": # <SMV ldInst="MU01" cbName="MSVCB01" desc="bla...bla> _ldInst = pAP.getAttribute("ldInst") # <Address> .... _cbName = pAP.getAttribute("cbName") _desc = pAP.getAttribute("desc") iSMV = Comm.SubNetwork.ConnectedAP.SMV(_ldInst, _cbName, _desc) # self.TRX.Trace((" SMV: ldInst:"+_ldInst+" cbName:"+_cbName+" desc:"+_desc),TL.DETAIL) iAddressSMV = pAP.firstChild.nextSibling # <Address> if (iAddressSMV.localName == "Address" ): # <P type="MAC-Address">01-0C-CD-04-00-00</P> iAddressSMV = self.ParseAddress( iAddressSMV.firstChild.nextSibling, "SMV Address:") # ... iSMV.tSMVAddress = iAddressSMV # <P type="VLAN-PRIORITY">4</P> # tSMV.append(iSMV) # </Address> CnxAP.tSMV.append(iSMV) if pAP.localName == "GSE": #<GSE ldInst="PIGO" cbName="GoCB1"> _ldInst = pAP.getAttribute("ldInst") # <Address> _cbName = pAP.getAttribute( "cbName" ) # <P type="MAC-Address">01-01-19-01-11-01</P> _desc = pAP.getAttribute( "desc") # <P type="VLAN-ID">00B</P> iGSE = Comm.SubNetwork.ConnectedAP.GSE( _ldInst, _cbName, _desc) # <P type="APPID">1911</P> # self.TRX.Trace((" GSE: ldInst:" + _ldInst + " cbName:" + _cbName + " desc:" + _desc), TL.DETAIL) pGSE = pAP.firstChild.nextSibling # <P type="VLAN-PRIORITY">4</P> while pGSE is not None: if pAP.localName == "Private": self.TRX.Trace(("Private in GSE"), TL.ERROR) self.Dyn.PrivateDynImport(type, pGSE, iGSE) pAP = pAP.nextSibling continue if (pGSE.localName == "Address"): # </Address> _Adr = self.ParseAddress(pGSE.firstChild.nextSibling, "GSE Address:") iGSE.tGSEAddress = _Adr pGSE = pGSE.nextSibling if ( pGSE.localName == "MinTime" ): # <MinTime unit="s" multiplier="m">2</MinTime></GSE> _unit = pGSE.getAttribute("unit") _multiplier = pGSE.getAttribute("multiplier") _MinTime = pGSE.firstChild.nodeValue minTime = Comm.SubNetwork.ConnectedAP.GSE.MinTime( _unit, _multiplier, _MinTime) iGSE.minTime = minTime self.TRX.Trace( (" GSE: MinTime.unit:" + _unit + " multi:" + _multiplier + " MinTime=" + _MinTime), TL.DETAIL) pGSE = pGSE.nextSibling if (pGSE.localName == "MaxTime" ): # <MaxTime unit="s" multiplier="m">5000</MaxTime> _unit = pGSE.getAttribute("unit") _multiplier = pGSE.getAttribute("multiplier") _MaxTime = pGSE.firstChild.nodeValue maxTime = Comm.SubNetwork.ConnectedAP.GSE.MaxTime( _unit, _multiplier, _MaxTime) iGSE.maxTime = maxTime self.TRX.Trace( (" GSE: MaxTime.unit:" + _unit + " multi:" + _multiplier + " MaxTime=" + _MaxTime), TL.DETAIL) pGSE = pGSE.nextSibling pGSE = pGSE.nextSibling CnxAP.tGSE.append(iGSE) pAP = pAP.nextSibling continue return CnxAP ## # \b ParseAddress handle the Address section of the SCL # # @description # # Parse the list <P Type ... from the SCL # <Text ... # <BitRate ... # <ConnectedAP .. # # Nota: the validity of the 'P Type' is not checked. # # @param pAP pointer to 'SubNetwork' tag in the SCL # @param Titre instance of SubNetwork class def ParseAddress(self, pAP, Titre): self.TRX.Trace((" " + Titre + ":"), TL.DETAIL) # tAdress = [] pAdr = pAP # Adresse IP Connected AP while pAdr.nextSibling: # <Address> if pAdr.localName is None: # <P type="OSI-AP-Title">1,3,9999,23</P> pAdr = pAdr.nextSibling # <P type="OSI-AE-Qualifier">23</P> continue # <P type="OSI-PSEL">00000001</P> type = pAdr.getAttribute("type") # <P type="OSI-SSEL">0001</P> value = pAdr.firstChild.data # <P type="OSI-TSEL">0001</P> self.TRX.Trace((" " + type + "='" + value + "'"), TL.DETAIL) # </Address> iPtype = Comm.SubNetwork.ConnectedAP.PhysConn.PType(type, value) tAdress.append(iPtype) pAdr = pAdr.nextSibling continue return (tAdress)
def __init__(self, _TR): self.TRX = _TR ## Instance of the TRACE service. self.Dyn = DynImport( ) ## Create an instance of DynImpot for private TAG
class Parse_LN: ## # __init__(self , _TR) # # Constructor for LN parsing, initialieze the DynImport class # # @param _TR Trace system def __init__(self, _TR): self.TRX = _TR ## Instance of the TRACE service. self.Dyn = DynImport( ) ## Create an instance of DynImpot for private TAG ## # \b Parse_LN: # # Main function of this class # # The function hierarchy is as follow # # Parse_LN # - private: <private> .... </Private> # - Dyn.Import (from IEC_PrivateSupport) # - inputs: # - invoke the method Parse_ExtRef() from this class # - DOI: # - invoke the methode Parse_DOI() from this class # - ParseDAI_VAL() invoked by Parse_DOI() # - Parse_SDI() invoked by Parse_DOI() # - Log (not parsed) \b #TODO not supported # - SettingControl (not parsed) \b #TODO not supported # - GSEControl (local to this method) # - LogControl (local to this method) # - SampledValueControl (local to this method) # - DataSet: # - invoke the method Parse_DataSet() from this class # - ReportControl: # - invoke the method Parse_ReportControl() from this class # # @param pLN : is the result of scl.getElementsByTagName("DataTypeTemplates") # @param IEDname : Use to create the full IEC oath to the data # @param AP_Name : Use to create the full IEC oath to the data # @param tDAI : table of DAi # # @return iLN : The logical node with all its sub-classes. def Parse_LN(self, pLN, IEDname, AP_Name, tDAI): # # LN contains DataSet, ReportControl, GooseControl and DOI/SDI.../SDI/DAI sections (up to 3 levels of SDI # _lnPrefix = pLN.getAttribute( "prefix") ## The LN prefix part (none for LN0) _lnClass = pLN.getAttribute( "lnClass" ) ## The LN class according to IEC 61850-7-x or other domain specific standards (LLN0 for LN0) _inst = pLN.getAttribute( "inst" ) ## The LN instance number identifying this LN – an unsigned integer; leading zeros are ## not recommended, as they formally lead to another instance identification (Not presnet for LN0) _lnType = pLN.getAttribute( "lnType" ) ## The instantiable type definition of this logical node, reference to a LNodeType definition _desc = pLN.getAttribute( "desc") ## The description text for the logical node iLN = IED.AccessPoint.Server.LN(_lnPrefix, _lnType, _inst, _lnClass, _desc) if pLN.firstChild is not None: # pLN is used to browse the XML tree pLN = pLN.firstChild.nextSibling # else: return iLN # LN0 is empty, usually the case for ICD IID file tiRCB = [] # Tableau des instances des RCB du LN0 tiSVC = [] # Tableau des instances des SVC du LN0 tiGCB = [] # Tableau des instances des GCB du LN0 tiLCB = [] # Tableau des instances des LCB du LN0 tExtRef = [] # Tableau des ExtRef (Inputs) tDS = [ ] # Tableau des instances des DatatSet du LN0 (l'objet DA contient la liste des FCDA) while pLN: if pLN.localName is None: pLN = pLN.nextSibling continue if pLN.localName == "Private": type = pLN.getAttribute("type") self.Dyn.PrivateDynImport(type, pLN, iLN) # Dynamic import for private pLN = pLN.nextSibling continue if pLN.localName == "Inputs": # < Inputs > # < ExtRef doName = "SPCSO3" daName = "q" serviceType = "GOOSE"... self.TRX.Trace((" *** Inputs"), TL.DETAIL) iLN.tInputs = self.Parse_ExtRef(pLN) pLN = pLN.nextSibling continue if pLN.localName == "DOI": ### DOI et DAI à TRAITER DANS LE DATA MODEL. self.TRX.Trace((" *** DOI"), TL.DETAIL) LN_id = iLN.lnPrefix + iLN.lnClass + iLN.lnInst DoName = IEDname + AP_Name + '/' + LN_id # Use for collecting DAI iDOI, iLN = self.Parse_DOI(iLN, pLN, DoName) pLN = pLN.nextSibling continue # TODO ???gérer les balises Log, et SettingControl? . if pLN.localName == "Log": # Non utilisé dans R#Space self.TRX.Trace((" Log: NON TRAITE"), TL.ERROR) pLN = pLN.nextSibling continue if pLN.localName == "LogControl": # Non utilisé dans R#Space self.TRX.Trace((" LogControl"), TL.DETAIL) tiLCB = self.Parse_LogControl(pLN, tiLCB) pLN = pLN.nextSibling continue if pLN.localName == "SettingControl": # Non utilisé dans R#Space self.TRX.Trace( (" SettingControl: NON TRAITE"), TL.ERROR) ## TODO handle the SettingControl class pLN = pLN.nextSibling continue if pLN.localName == "GSEControl": self.TRX.Trace((" GSEControl"), TL.DETAIL) _name = pLN.getAttribute( "name") ## The name identifying this GOOSE control block _desc = pLN.getAttribute("desc") ## A description text _datSet = pLN.getAttribute( "datSet" ) ## The name of the data set to be sent by the GSE control block. _confRev = pLN.getAttribute( "confRev" ) ## The configuration revision number of this control block. _type = pLN.getAttribute( "type" ) ## The default type value is GOOSE (GSSE is deprecated) _appID = pLN.getAttribute( "appID" ) ## A system wide unique identification of the application to which the GOOSE message belong _fixedOffs = pLN.getAttribute( "fixedOffs" ) ## Default value false. If set to true it shows all receivers, that the values GOOSE message have fixed offset. _securityEnabled = pLN.getAttribute( "securityEnabled" ) ## Default: None. Allows to configure the security options per control block instance. GOOSE = IED.AccessPoint.Server.LN.GSEControl( _name, _desc, _datSet, _confRev, _type, _appID, _fixedOffs, _securityEnabled) # TODO LISTE DES IEDs à accrocjer GSECONTROL tiGCB.append(GOOSE) self.TRX.Trace( (" GSEControl, name: " + _name + " datSet:" + _datSet), TL.DETAIL) pLN = pLN.nextSibling continue if pLN.localName == "SampledValueControl": self.TRX.Trace((" SampledValueControl"), TL.DETAIL) _name = pLN.getAttribute( "name" ) ## name A name identifying this SMV control block _desc = pLN.getAttribute( "desc") ## desc The description text _datSet = pLN.getAttribute( "datSet" ) ## datSet The name of the data set whose values shall be sent. _confRev = pLN.getAttribute( "confRev" ) ## confRev The configuration revision number of this control block; mandatory. _smvID = pLN.getAttribute( "smvID" ) ## smvID Multicast CB: the MsvID for the sampled value definition as defined (Unicast: deprecated) _multicast = pLN.getAttribute( "multicast" ) ## multicast Indicates Unicast SMV services only meaning that smvID = UsvI _smpRate = pLN.getAttribute( "smpRate" ) ## smpRate Sample rate as defined in IEC 61850-7-2. If no smpMod is defined, i _nofASDU = pLN.getAttribute( "nofASDU" ) ## nofASDU Number of ASDU (Application service data unit) – see IEC 61850-9-2 _smpMod = pLN.getAttribute( "smpMod" ) ## smpMod The sampling mode as defined in IEC 61850-7-2; default: SmpPerPeriod; i _securityEnabled = pLN.getAttribute( "securityEnabled" ) ## securityEnabled Default: None. Allows to configure the security options per control block instance SVC = IED.AccessPoint.Server.LN.SampledValueControl( _name, _desc, _datSet, _confRev, _smvID, _multicast, _smpRate, _nofASDU, _smpMod, _securityEnabled) tiSVC.append(SVC) self.TRX.Trace((" SampledValueControl, name:" + _name + " smvID:" + _smvID + " datSet:" + _datSet), TL.DETAIL) #TODO Parse smvOptions ## # \b smvOption # # param refreshTime The meaning of the options is described in IEC 61850-7-2. # param sampleRate If any of the attributes is set to true, the appropriate values shall be included into the SMV telegram # param dataSet If the attribute is set to true, the dataset name shall be included into the SMV telegram # param security See IEC61850-9-2... # param synchSourceId if true, the SV message contains the identity of the synchronizing master clock according to IEC 61850-9-3; default = false pLN = pLN.nextSibling continue # Extraction of data Set: # <DataSet name = "DS_TAXD" desc = "Data Set..." > # <FCDA ldInst = "LD_all" prefix = "" lnInst = "1" lnClass = "TAXD" doName = "EEName" fc = "DC" /> # <FCDA ldInst = "LD_all" prefix = "" lnInst = "1" lnClass = "TAXD" doName = "EEHealth" fc = "ST" /> # <FCDA ldInst = "LD_all" prefix = "" lnInst = "1" lnClass = "TAXD" doName = "AxDspSv" fc = "MX" / > # <FCDA ldInst = "LD_all" prefix = "" lnInst = "1" lnClass = "TAXD" doName = "SmpRte" fc = "SP" /> # </DataSet > if pLN.localName == "DataSet": _name = pLN.getAttribute("name") ## _desc = pLN.getAttribute("desc") ## self.TRX.Trace( (" DataSet, dsName:" + _name + " desc:" + _desc), TL.DETAIL) DS = IED.AccessPoint.Server.LN.DataSet( _name, _desc) # "" Tableau des FCDA DS = self.Parse_DataSet(pLN, DS) tDS.append(DS) pLN = pLN.nextSibling continue if pLN.localName == "ReportControl": self.TRX.Trace((" ReportControl"), TL.DETAIL) iRCB = self.Parse_ReportControl(pLN) tiRCB.append(iRCB) pLN = pLN.nextSibling continue continue iLN.tSVCtrl = tiSVC iLN.tDataSet = tDS iLN.tGSECtrl = tiGCB iLN.tRptCtrl = tiRCB iLN.tLogCtrl = tiLCB return iLN ## # \b Parse_DataSet: # # Parsing the dataset and its list of FCDA # # @param pLN : pointer to SCL DataSet section # @param DS : DataSet object, the tFCDA table is used to collect the list of FCDA for the DataSet # # @return DS: table of FCDA def Parse_DataSet(self, pLN, DS): FCDAi = pLN.firstChild while FCDAi: if FCDAi.localName is None: FCDAi = FCDAi.nextSibling continue _ldInst = FCDAi.getAttribute( "ldInst") ## The LD where the DO resides _prefix = FCDAi.getAttribute( "prefix" ) ## Prefix identifying together with lnInst and lnClass the LN where the DO resides _lnClass = FCDAi.getAttribute( "lnClass") ## LN class of the LN where the DO resides; _lnInst = FCDAi.getAttribute( "lnInst") ## Instance number of the LN where the DO resides _doName = FCDAi.getAttribute( "doName" ) ## A name identifying the DO (within the LN). A name standardized in IEC 61850-7-4. _daName = FCDAi.getAttribute( "daName" ) ## The attribute name – if missing, all attributes with functional characteristic given by fc are selected _cf = FCDAi.getAttribute( "fc" ) ## All attributes of this functional constraint are selected. See IEC 61850-7-2 or the fc definition in 9.5 _ix = FCDAi.getAttribute( "ix" ) ## An index to select an array element in case that one of the data elements is an array. iFCDA = IED.AccessPoint.Server.LN.DataSet.FCDA( _ldInst, _prefix, _lnClass, _lnInst, _doName, _daName, _cf, _ix) DS.tFCDA.append(iFCDA) FCDAi = FCDAi.nextSibling return DS ## # \b Parse_ReportControl # # Parsing the Report Control structures: RCB, TrgOps, RptEnabled # # @param pLN : pointer to SCL ReportControl section # # @return DS: table of FCDA def Parse_ReportControl(self, pLN): _name = pLN.getAttribute( "name") ## name Name of the report control block. _desc = pLN.getAttribute("desc") ## desc The description text _rptID = pLN.getAttribute( "rptID" ) ## datSet The name of the data set to be sent by the report control block _datSet = pLN.getAttribute( "datSet" ) ## intgPd Integrity period in milliseconds – see IEC 61850-7-2. Only relev _intgPd = pLN.getAttribute( "intgPD" ) ## rptID Identifier for the report control block, optional. _confRev = pLN.getAttribute( "confRev" ) ## confRev The configuration revision number of this report control block. _buffered = pLN.getAttribute( "buffered" ) ## buffered Specifies if reports are buffered or not – see IEC 61850-7-2; de _bufTime = pLN.getAttribute( "bufTime" ) ## bufTime Buffer time – see IEC 61850-7-2; default: 0 _indexed = pLN.getAttribute( "indexed" ) ## indexed If true, the report control block instance names are built from supplied name, followed by an index number from 01 up to maximum 99. # _Opt = LN.getAttribute("optFields") iRCB = IED.AccessPoint.Server.LN.ReportControl(_name, _desc, _datSet, _intgPd, _rptID, _confRev, _buffered, _bufTime, _indexed) self.TRX.Trace(("ReportControl: " + _rptID + " name:" + _name + " datSet" + _datSet), TL.DETAIL) # Récupération des attribues des sous-sections: TrgOps, OptFields et RptEnabled rptCtrl = pLN.firstChild while rptCtrl: if rptCtrl.localName is None: rptCtrl = rptCtrl.nextSibling continue if rptCtrl.localName == "Private": rptCtrl = rptCtrl.nextSibling continue if rptCtrl.localName == "TrgOps": self.TRX.Trace(("ReportControl: TrgOps"), TL.DETAIL) _qchg = rptCtrl.getAttribute("qchg") _dchg = rptCtrl.getAttribute("dchg") _dupd = rptCtrl.getAttribute("dupd") _period = rptCtrl.getAttribute("period") _gi = rptCtrl.getAttribute("gi") iRCB.TrgOps = IED.AccessPoint.Server.LN.TrgOps( _qchg, _dchg, _dupd, _period, _gi) rptCtrl = rptCtrl.nextSibling continue if rptCtrl.localName == "OptFields": _seqNum = rptCtrl.getAttribute("seqNum") _timeStamp = rptCtrl.getAttribute("timeStamp") _reasonCode = rptCtrl.getAttribute("reasonCode") _dataSet = rptCtrl.getAttribute("dataSet") _dataRef = rptCtrl.getAttribute("dataRef") _entryID = rptCtrl.getAttribute("entryID") _configRef = rptCtrl.getAttribute("configRef") _bufOvfl = rptCtrl.getAttribute("bufOvfl") iRCB.OptField = IED.AccessPoint.Server.LN.ReportControl.OptFields( _seqNum, _timeStamp, _reasonCode, _dataSet, _dataRef, _entryID, _configRef, _bufOvfl) rptCtrl = rptCtrl.nextSibling continue if rptCtrl.localName == "RptEnabled": self.TRX.Trace(("ReportControl: RptEnabled"), TL.DETAIL) _max = rptCtrl.getAttribute("max") iRCB.RptEnable = IED.AccessPoint.Server.LN.ReportControl.RptEnabled( _max) pClientLN = rptCtrl.firstChild if pClientLN is not None: pClientLN = pClientLN.nextSibling while pClientLN is not None: if pClientLN.localName == "ClientLN": self.TRX.Trace(("ReportControl: ClientLN"), TL.DETAIL) #iedName, _ldInst, _lnPrefix, _lnClass, _lnInst, _desc, _apRef): _iedName = pClientLN.getAttribute( "iedName" ) ## The name of the IED where the LN resides _ldInst = pClientLN.getAttribute( "ldInst" ) ## The instance identification of the LD where the LN resides _prefix = pClientLN.getAttribute( "prefix") ## The LN prefix _lnClass = pClientLN.getAttribute( "lnClass" ) ## The LN class according to IEC 61850-7-4 _lnInst = pClientLN.getAttribute( "lnInst" ) ## The instance id of this LN instance of below LN class in the IED _desc = pClientLN.getAttribute( "desc" ) ## optional descriptive text, e.g. about purpose of the client _apRef = pClientLN.getAttribute( "apRef") ## Application reference iClientLN = IED.AccessPoint.Server.LN.ReportControl.RptEnabled.ClientLN( _iedName, _ldInst, _prefix, _lnClass, _lnInst, _desc, _apRef) iRCB.RptEnable.tClientLN.append(iClientLN) pClientLN = pClientLN.nextSibling continue rptCtrl = rptCtrl.nextSibling continue return iRCB ## # \b Parse_LogControl # # Parsing the LogControl section # @param pLN : pointer to SCL ReportControl section # @param tiLCB : Table of instance of Log Control Block (empty) # # @return tiLCB : Table of instance of Log Control Block completed def Parse_LogControl(self, pLN, tiLCB): _name = pLN.getAttribute( "name") ## name the name of the log control block _desc = pLN.getAttribute("desc") ## A description text _datSet = pLN.getAttribute( "datSet" ) ## The name of the data set whose values shall be logged; datSet should only be _intgPd = pLN.getAttribute( "intgPd" ) ## Integrity scan period in milliseconds – see IEC 61850-7-2. _ldInst = pLN.getAttribute( "ldInst" ) ## The identification of the LD where the log resides; if missing, the same LD _prefix = pLN.getAttribute( "prefix" ) ## Prefix of LN where the log resides; if missing, empty string _lnClass = pLN.getAttribute( "lnClass" ) ## Class of the LN where the log resides; if missing, LLN0 _lnInst = pLN.getAttribute( "lnInst" ) ## Instance number of LN, where the log resides; missing for LLN0 _logName = pLN.getAttribute( "logName" ) ## Relative name of the log within its hosting LN; name of the log element _logEna = pLN.getAttribute( "logEna" ) ## TRUE enables immediate logging; FALSE prohibits logging until enabled online _reasonCode = pLN.getAttribute( "reasonCode" ) ## If true, the reason code for the event trigger is also stored into the log – iLogControl = IED.AccessPoint.Server.LN.LogControl( _name, _desc, _datSet, _intgPd, _ldInst, _prefix, _lnClass, _lnInst, _logName, _logEna, _reasonCode) self.TRX.Trace((" LogControl: name:" + _name + " datSet: " + _datSet + " logName:" + _logName), TL.DETAIL) logCtrl = pLN.firstChild while logCtrl: if logCtrl.localName is None: logCtrl = logCtrl.nextSibling continue if logCtrl.localName == "Private": self.TRX.Trace((" LogControl: Private"), TL.DETAIL) logCtrl = logCtrl.nextSibling continue if logCtrl.localName == "TrgOps": self.TRX.Trace((" LogControl: TrgOps"), TL.DETAIL) _qchg = logCtrl.getAttribute("qchg") _dchg = logCtrl.getAttribute("dchg") _dupd = logCtrl.getAttribute("dupd") _period = logCtrl.getAttribute("period") _gi = logCtrl.getAttribute("gi") iTrgOps = IED.AccessPoint.Server.LN.TrgOps( _qchg, _dchg, _dupd, _period, _gi) self.TRX.Trace((" TrgOps qchg" + _qchg + " dchg:" + _dchg + " dupd:" + _dupd + \ " period+" + _period + "_gi" + _gi), TL.DETAIL) iLogControl.TrgOps = iTrgOps logCtrl = logCtrl.nextSibling continue tiLCB.append(iLogControl) return tiLCB ## # \b Parse_ExtRef # # Parse the tInputs sections of a LN0 declaration # # Features: # - Create instance of an 'ExtRef' object by getting the attributes froml the SCL IED defined without any Server part. # - Dynamically calling Private support class/method if a private is encountered. # # @return tInputs: the table of ExtRef objects def Parse_ExtRef(self, pLN): _tInputs = IED.AccessPoint.Server.LN.Inputs([]) pExtRef = pLN.firstChild.nextSibling while pExtRef: if pExtRef.localName is None: pExtRef = pExtRef.nextSibling continue if pExtRef.localName == 'Private': type = pExtRef.getAttribute("type") self.Dyn.PrivateDynImport(type, pExtRef, IED.AccessPoint.Server.LN.Inputs) if pExtRef.localName == 'ExtRef': _iedName = pExtRef.getAttribute("iedName") _ldInst = pExtRef.getAttribute("ldInst") _prefix = pExtRef.getAttribute("prefix") _lnClass = pExtRef.getAttribute("lnClass") _lnInst = pExtRef.getAttribute("lnInst") _doName = pExtRef.getAttribute("doName") _daName = pExtRef.getAttribute("daName") _intAddr = pExtRef.getAttribute("intAddr") _desc = pExtRef.getAttribute("desc") _serviceType = pExtRef.getAttribute("serviceType") _srcLDInst = pExtRef.getAttribute("srcLDInst") _srcPrefix = pExtRef.getAttribute("srcPrefix") _srcLNClass = pExtRef.getAttribute("srcLNClass") _srcLNInst = pExtRef.getAttribute("srcLNInst") _srcCBName = pExtRef.getAttribute("srcCBName") _pServT = pExtRef.getAttribute("pServT") _pLN = pExtRef.getAttribute("pLN") _pDO = pExtRef.getAttribute("pDO") _pDA = pExtRef.getAttribute("pDA") iExtRef = IED.AccessPoint.Server.LN.Inputs.ExtRef( _iedName, _ldInst, _prefix, _lnClass, _lnInst, _doName, _daName, _intAddr, _desc, _serviceType, _srcLDInst, _srcPrefix, _srcLNClass, _srcLNInst, _srcCBName, _pDO, _pLN, _pDA, _pServT) _tInputs.tExtRef.append(iExtRef) pExtRef = pExtRef.nextSibling return _tInputs ## # \b Parse_DOI # # Features: # - Create instance of an 'DAI' and collected sub-sequent value # # The code of Parse_SDI(pDAI1, iDOI, n, TRX)is used to collect the # value of DO/DA looking like this: # <DAI name="SIUnit" valKind="RO"> #==> <Val>cos(phi)</Val> <=== # </DAI> # # @return pDAI new value of the pointer to SCL # @return _value _value found (or None) # @return iDOI modified instance DOI. def Parse_DOI(self, iLN, pDOI, DoName): # Instance of LN & DOI is scl pointto DOI tag _name = pDOI.getAttribute( "name") ## The name identifying this GOOSE control block _desc = pDOI.getAttribute("desc") ## A description text _ix = pDOI.getAttribute( "ix" ) ## The name of the data set to be sent by the GSE control block. _accessControl = pDOI.getAttribute( "accessControl" ) ## The configuration revision number of this control block. self.TRX.Trace(("DOI: name:" + _name + " desc:" + _desc), TL.DETAIL) iDOI = IED.AccessPoint.Server.LN.DOI( _desc, _name, _ix, _accessControl) #, None, None, None) # None for RTE private Type setattr(iLN, iDOI.name, iDOI) pDAI = pDOI.firstChild # Pointeur DAI ou SDI while pDAI: # USE CASE: if pDAI.localName is None: # <DOI name="LEDRs" desc="RstOper"> pDAI = pDAI.nextSibling # <SDI name="Oper"> continue # <SDI name="origin"> if pDAI.localName == "Private": # <DAI name="orCat" sAddr="96.1.3.10.4" /> type = pDAI.getAttribute("type") # WG10 PRIVATE if type == "eTr-IEC61850-90-2": pI90_2 = pDAI.firstChild.nextSibling if (pI90_2.nodeName == "eTr-IEC61850-90-2:ProxyOf"): _externalScl = pI90_2.getAttribute("externalScl") _iedName = pI90_2.getAttribute("iedName") _ldInst = pI90_2.getAttribute("ldInst") _prefix = pI90_2.getAttribute("prefix") _lnClass = pI90_2.getAttribute("lnClass") _lnInst = pI90_2.getAttribute("lnInst") _doName = pI90_2.getAttribute("doName") iDOI.IEC_90_2 = IED.AccessPoint.Server.LN.DOI.IEC_90_2( _externalScl, _iedName, _ldInst, _prefix, _lnClass, _lnInst, _doName) # WG10 PRIVATE elif type == "IEC_60870_5_104": p104 = pDAI.firstChild if p104 is not None: p104 = p104.nextSibling if (p104.localName == "Address") and ( p104.nodeName == "'IEC_60870_5_104:Address'"): _casdu = p104.getAttribute("casdu") _ioa = p104.getAttribute("ioa") _ti = p104.getAttribute("ti") _usedBy = p104.getAttribute("usedBy") _inverted = p104.getAttribute("inverted") iDOI.IEC104 = IED.AccessPoint.Server.LN.DOI.IEC104( _casdu, _ioa, _ti, _usedBy, _inverted) else: self.Dyn.PrivateDynImport(type, pDAI, iDOI) pDAI = pDAI.nextSibling # </SDI> continue # <DAI name="ctlVal" sAddr="96.1.3.10.3" /> if pDAI.localName == "DAI": # <DAI name="ctlNum" sAddr="96.1.3.10.6" /> pX, value, iDAI = self.Parse_DAI_VAL( pDAI, iDOI) # <DAI name="T" sAddr="96.1.3.10.7" /> # tDAI.append(iDOI) pDAI = pDAI.nextSibling continue # <DAI name="Test" sAddr="96.1.3.10.8" /> if pDAI.localName == "SDI": # <DAI name="Check" sAddr="96.1.3.10.9" /> self.Parse_SDI(pDAI, iDOI, DoName) pDAI = pDAI.nextSibling continue pDAI = pDAI.nextSibling return iDOI, iLN ## # \b Parse_DAI_VAL # # @param pDAI The SCL pointer to DAI # @param iDOI The current instance of a DOI # # @return pDAI pDAI is pointing to the next Tag # @return _value # @return iDOI The meaning of the value from the engineering phases. If missing, the valKind from the type definition applies for any attached value. def Parse_DAI_VAL(self, pDAI, iDOI): _value = None if pDAI.localName == "DAI": _desc = pDAI.getAttribute( "desc" ) ## desc The description text for the DAI element _name = pDAI.getAttribute( "name" ) ## name The name of the Data attribute whose value is given. _sAddr = pDAI.getAttribute( "sAddr") ## sAddr Short address of this Data attribute _valKind = pDAI.getAttribute( "valKind" ) ## valKind The meaning of the value from the engineering phases. If missing, the va _ix = pDAI.getAttribute( "ix" ) ## ix Index of the DAI element in case of an array type _valImport = pDAI.getAttribute( "valImport" ) ## valImport if true, an IED / IED configurator can import values modified by another ## value implementation artefact, the declared value is stored here. iDAI = IED.AccessPoint.Server.LN.DOI.DAI(_desc, _name, _sAddr, _valKind, _ix, _valImport, _value) # _value ! setattr(iDOI, _name, iDAI) # Is there a value ? p1 = pDAI.firstChild if p1 is not None: # The SCL may contains empty tag like </xxxx> <xxxx/> p1 = p1.nextSibling if (p1.firstChild is not None): if p1.localName == "Val": _value = p1.firstChild.data iDAI.value = _value self.TRX.Trace((" Balise1 <VAL/>: " + _value), TL.DETAIL) p1 = p1.firstChild.nextSibling if p1 is not None and p1.localName == "Private": pType = pDAI.firstChild.nextSibling _type = pType.getAttribute("type") self.Dyn.PrivateDynImport(_type, pType, iDAI) setattr(iDOI, _name, iDAI) # I add the iDAI named '_name' to iDOI else: setattr(iDOI, _name, iDAI) _value = None self.TRX.Trace((" Balise2 <VAL/> vide"), TL.DETAIL) else: pDAI = pDAI.nextSibling return pDAI, _value, iDOI """ The code of Parse_SDI(pDAI1, iDOI, n, TRX)is used to collect the value of DO/DA looking like this: </DOI> <DOI name="CosPhi"> <SDI name="phsA"> <SDI name="cVal"> <SDI name="mag"> <DAI name="f" sAddr="MX:0;0x070F2800;DB=0:P;MIN=0;MAX=0"/> </SDI> </SDI> <SDI name="units"> <DAI name="SIUnit" valKind="RO"> ==> <Val>cos(phi)</Val> <=== </DAI> </SDI> </SDI> """ ## # \b Parse_SDI # @param pDAI : the SCL pointer to a SDI # @param iDOI : an instance of DO, which may have SDI value to add # @param DoName: # @return sdi_name : the name of the instance # # \b Principle # # The instances of SDI is added to the DOI by dynamic creation of an attribute (setattr) # The code assume that there is a maximum of three levels of SDI. # def Parse_SDI(self, pDAI, iDOI, DoName): pDAI1 = pDAI BaseName = DoName + '.' + iDOI.name t_IX = [None, None, None, None] while pDAI1 is not None: if pDAI1.localName is None: pDAI1 = pDAI1.nextSibling continue iSDI1, sdi_name = self.Parse_SDI_Val(pDAI1, iDOI, BaseName, 0, t_IX, '') if pDAI1.firstChild is None: # No value found by Parse_SDI_Val pDAI1 = pDAI1.nextSibling continue pDAI2 = pDAI1.firstChild.nextSibling # Prochaine balise DAI ou à SDI cas 'origin'. if pDAI2.localName == "SDI": # SDI imbriqué while pDAI2 is not None: if pDAI2.localName == "SDI": # SDI imbriqué iSDI2, sdi_name = self.Parse_SDI_Val( pDAI2, iSDI1, BaseName, 1, t_IX, sdi_name) if pDAI2.firstChild is None: pDAI2 = pDAI2.nextSibling continue pDAI3 = pDAI2.firstChild.nextSibling # Prochaine balise DAI ou à SDI cas 'origin'. if pDAI3.localName == "SDI": # SDI imbriqué while pDAI3 is not None: if pDAI3.localName == "SDI": # SDI imbriqué iSDI3, sdi_name = self.Parse_SDI_Val( pDAI3, iSDI2, BaseName, 2, t_IX, sdi_name) pDAI3 = pDAI3.nextSibling pDAI2 = pDAI2.nextSibling pDAI1 = pDAI1.nextSibling return # tDAI #.nextSibling, ## # \b Parse_SDI_Val # # @param pDAI_v : the SCL pointer to a SDI # @param iDOI : an instance of DO, which may have SDI value to add # @param BaseName : used to build the name of the attribute DO.SDO.DA.BDA... # @param n : SDI1 or SDI2 ou SDI3 (for trace only) # @param t_IX : Table of value, if the DO is an array. # @param sdi_name : Building the string DA_name.SDA_name.XX_name # # @return iSDI: an instance od SDI object with value (if found) # @return sdi_name: tha name of the instance. # # \b Principle # The instances of SDI is added to the DOI by dynamic creation of an attribute (setattr) def Parse_SDI_Val(self, pDAI_v, iDOI, BaseName, n, t_IX, sdi_name): _desc = pDAI_v.getAttribute("desc") _name = pDAI_v.getAttribute("name") _sdi_ix = pDAI_v.getAttribute("ix") _sAddr = pDAI_v.getAttribute("sAddr") _sdi_name = sdi_name + '.' + _name # </SDI> iSDI = IED.AccessPoint.Server.LN.DOI.DAI.SDI(_desc, _name, _sdi_ix, _sAddr) # Is it a table of SDI ? if len(_sdi_ix) > 0: t_IX[n] = _sdi_ix # Adding the attribute to iDOI dynamically. if _sdi_ix is not None and len(_sdi_ix) > 0: setattr(iDOI, _name + _sdi_ix, iSDI) else: setattr(iDOI, _name, iSDI) if pDAI_v.firstChild is None: self.TRX.Trace(("DAI without value:" + _name + "sAddr:" + _sAddr), TL.DETAIL) return iSDI, sdi_name # Reading the Value pVAL = pDAI_v.firstChild.nextSibling pVAL, value, iSDI = self.Parse_DAI_VAL(pVAL, iSDI) if value is not None: found = False for i in range(0, 2): if t_IX[i] is not None: self.TRX.Trace( ("Value for SDI[idx]: " + str(n) + BaseName + sdi_name + '_' + str(t_IX[i]) + '\t:' + value), TL.DETAIL) found = True break if not found: self.TRX.Trace(("Value for SDI: " + str(n) + BaseName + sdi_name + '\t:' + value), TL.DETAIL) return iSDI, sdi_name
def __init__(self, _scl, _TR): self.scl = _scl ## scl self.TRX = _TR ## TRX instance of Trace System self.Dyn = DynImport() ## Dyn instance of DynImport to handle private section if any/
class ParseSubStation: ## \b Description # Constructor is used to keep the dictionary of DOType available. # # @param _scl: pointer to the SCL structure created by miniDOM # @param _TRX: Trace function def __init__(self, _scl, _TR): self.scl = _scl ## scl self.TRX = _TR ## TRX instance of Trace System self.Dyn = DynImport() ## Dyn instance of DynImport to handle private section if any/ ## \b Description # Constructor is used to keep the dictionary of DOType available. # # @param _pTal - pointer to the SCL structure, 'Terminal' TAG # @param pCNXNode - Pointer to the data model at pConnectionNode level def ParseTerminal(self, pTal, pCNXNode): idxTal = 0 while (pTal is not None) and (pTal.localName is not None): if pTal.localName == "Terminal": _name = pTal.getAttribute("name") ## _name The optional relative name of the terminal at this Equipment. The default is the empty _desc = pTal.getAttribute("desc") ## _desc Descriptive text to the terminal _connectivityNode = pTal.getAttribute("connectivityNode") ## _connectivityNode The pathname of the connectivity node to which this terminal connects. _substationName = pTal.getAttribute("substationName") ## _substationName The name of the substation containing the connectivityNode _voltageLevelName = pTal.getAttribute("voltageLevelName") ## _voltageLevelName The name of the voltage level containing the connectivityNode _bayName = pTal.getAttribute("bayName") ## _bayName The name of the bay containing the connectivityNode _cNodeName = pTal.getAttribute("cNodeName") ## _cNodeName (relative) name of the connectivityNode within its bay _lineName = pTal.getAttribute("lineName") ## _lineName Ed 2 ?? _neutralPoint = pTal.getAttribute("neutralPoint") ## _neutralPoint Ed 2.1 ??? iTerminal = SubStation.SubEquipement.Terminal(_name, _desc, _connectivityNode,_substationName,_voltageLevelName, _bayName,_cNodeName,_lineName, _neutralPoint) pCNXNode.tTerminal.append(iTerminal) idxTal = idxTal + 1 pTal = pTal.nextSibling if pTal is not None: pTal = pTal.nextSibling ## \b Description # # Parse the VoltageLevel class # # @param Substation - the global daa model structure for subStation section of SCL # @param pCNXNode - Pointer to the data model at pConnectionNode level def ParseVoltageLevelSection(self, Substation): # Analyse d'un IED # # <IED.. # # < Communication > # < SubNetwork tVoltage = [] _name = Substation[0].getAttribute("name") # SubStation name _desc = Substation[0].getAttribute("desc") # Description text poste=SubStation(_name,_desc) pVoltageLevel = Substation[0].firstChild.nextSibling while pVoltageLevel: _name = pVoltageLevel.getAttribute("name") # _nomFreq = pVoltageLevel.getAttribute("nomFreq") # _numpPhases = pVoltageLevel.getAttribute("numPhases") _desc = pVoltageLevel.getAttribute("desc") Tension = poste.VoltageLevel(_name,_desc) poste.tVoltage.append(Tension) pVolt = pVoltageLevel.firstChild if pVolt is not None: pVolt=pVolt.nextSibling idxVolt = len(poste.tVoltage)-1 while (pVolt is not None) and (pVolt.localName is not None): if pVolt.localName == "PowerTransformer": _name = pVolt.getAttribute("name") _desc = pVolt.getAttribute("desc") _type = pVolt.getAttribute("type") _virtual = pVolt.getAttribute("virtual") iTransfo = poste.VoltageLevel.PowerTransformer(_name,_desc, _type, _virtual ) poste.tVoltage[idxVolt].tPwrTfo.append(iTransfo) self.TRX.Trace(('Substation/Transformer.name:' + _name + ' desc:' + _desc + " type:" + _type),TL.DETAIL) if pVolt.localName == "Voltage": _unit = pVolt.getAttribute("unit") _multiplier = pVolt.getAttribute("multiplier") _value = pVolt.getAttribute("value") iVolt = poste.VoltageLevel.Voltage(_unit, _multiplier, _value) poste.tVoltage[idxVolt].Voltage=iVolt self.TRX.Trace(('Substation/Voltage.unit:' + _unit + ' mul:' + _multiplier + " value:" + _value ),TL.DETAIL) if pVolt.localName == "Bay": _name = pVolt.getAttribute("name") _desc = pVolt.getAttribute("desc") _sx_y = pVolt.getAttribute("sxy:y") _sx_x = pVolt.getAttribute("sxy:x") iBay = poste.Bay(_name, _desc, _sx_x, _sx_y) poste.tVoltage[idxVolt].tBay.append(iBay) self.TRX.Trace(('Substation/Bay.name:' + _name + ' desc:' + _desc + " sxy:y:" + _sx_y + " sxy:x:" + _sx_x), TL.DETAIL) pBay = pVolt.firstChild if pBay is not None: pBay = pBay.nextSibling idxConEqt = 0 idxConMode = 0 idxBay = len(poste.tVoltage[idxVolt].tBay)-1 while(pBay is not None) and (pBay.localName is not None): if pBay.localName == "ConductingEquipment": _name = pBay.getAttribute("name") _desc = pBay.getAttribute("desc") _virtual = pBay.getAttribute("virtual") _sx_y = pBay.getAttribute("sxy:y") _sx_x = pBay.getAttribute("sxy:x") _sx_dir = pBay.getAttribute("sxy:dir") iCondEqt = SubStation.ConductingEquipment(_name, _desc, _virtual, _sx_y, _sx_x, _sx_dir ) poste.tVoltage[idxVolt].tBay[idxBay].tConductingEquipment.append(iCondEqt) self.TRX.Trace(('Substation/Bay/ConductEqt.name:' + _name + ' desc:' + _desc + ' virtual:' + _virtual +\ " sxy:y:" + _sx_y + " sxy:x:" + _sx_x), TL.DETAIL) pTal = pBay.firstChild if pTal is not None: pTal=pTal.nextSibling self.ParseTerminal(pTal, poste.tVoltage[idxVolt].tBay[idxBay].tConductingEquipment[idxConEqt]) idxConEqt = idxConEqt+1 elif pBay.localName == "ConnectivityNode": _name = pBay.getAttribute("name") _desc = pBay.getAttribute("desc") _pathName = pBay.getAttribute("pathName") _sx_y = pBay.getAttribute("sxy:y") _sx_x = pBay.getAttribute("sxy:x") iConnNode = SubStation.SubEquipement.Terminal.ConnectivityNode(_name, _desc, _pathName, _sx_y, _sx_x) poste.tVoltage[idxVolt].tBay[idxBay].tConnectivityNode.append(iConnNode) self.TRX.Trace(('Substation/Bay/ConnnectNode.name:' + _name + ' desc:' + _desc + ' pathName:' + _pathName +\ " sxy:y:" + _sx_y + " sxy:x:" + _sx_x), TL.DETAIL) pTal = pBay.firstChild if pTal is not None: pTal=pTal.nextSibling self.ParseTerminal(pTal, poste.tVoltage[idxVolt].tBay[idxBay].tConnectivityNode[idxConMode]) idxConMode = idxConMode + 1 elif pBay.localName == "Function": _name = pBay.getAttribute("name") _desc = pBay.getAttribute("desc") iFunction = poste.Function(_name, _desc) poste.tVoltage[idxVolt].tBay[idxBay].tFunction.append(iFunction) self.TRX.Trace(('Substation/Bay/Function.name: ' + _name + ' desc:' + _desc), TL.DETAIL) pPrivate = pBay.firstChild if pPrivate is not None: pPrivate = pPrivate.nextSibling if pPrivate.localName == "Private": type = pPrivate.getAttribute("type") pDataModel = poste.tVoltage[idxVolt].tBay[idxBay].tFunction[0] self.Dyn.DynImport(type, pPrivate, pDataModel) elif pBay.localName == "LNode": _iedName = pBay.getAttribute("iedName") _lnClass = pBay.getAttribute("lnClass") _lnType = pBay.getAttribute("lnType") _lnInst = pBay.getAttribute("lnInst") iLNode = poste.Bay.LNode(_iedName,_lnClass, _lnType, _lnInst) poste.tVoltage[idxVolt].tBay[idxBay].tLNode.append(iLNode) self.TRX.Trace(('Substation/Bay/Lnode.iedName: '+ _iedName + " lnClass:" + _lnClass + \ " lnType:" + _lnType + " lnInst:" + _lnInst), TL.DETAIL) pBay = pBay.nextSibling if pBay is not None: pBay = pBay.nextSibling pVolt = pVolt.nextSibling if pVolt is not None: pVolt = pVolt.nextSibling pVoltageLevel = pVoltageLevel.nextSibling if pVoltageLevel is not None: pVoltageLevel = pVoltageLevel.nextSibling return pVoltageLevel
class Parse_Server: ## \b Description # Constructor is used to keep initialize the Parse_LN class (forwarding TRACE class). # @param _scl: pointer to the SCL structure created by miniDOM # @param _TRX: Trace function def __init__(self, _scl, TR): ## Constructor for Server self.scl = _scl ## Pointer to the SCL as provided by 'minidom'. self.TR = TR ## Instance of the TRACE service. self.pLN = Parse_LN( TR ) ## Invoking the constructor of ParseLN, to initialize TRACE service. self.Dyn = DynImport() ## # Parse the IED list created by a self.scl.getElementsByTagName("IED" # # Features: # - Detects IED defined without any Server part. # - Parse the AccessPoints and the Servers: # - Created nested tables AccessPoint and tables of servers # - Once the Server level is reached, the parsing continue with: # - Parse_LD_LN # - Parse LN (from IEC_LN class # # @param iIED_array: the trace Object type to look up. # @param pServer pointer to the SCL structure # @param idxAP index to access a an instance of AccessPoint # @param idxServer index to access a an instance of Server # # @return # # \bThe DataModel is build dynamically by 'setAttr' def Parse_LD_LN(self, iIED_array, pServer, idxAP, idxServer): tLDevice = [] LDi = pServer inst = LDi.getAttribute( "inst" ) ## Identification of the LDevice within the IED. Identification of the LDevice within the IED. Its value cannot be the empty string. ldName = LDi.getAttribute( "ldName" ) ## The explicitly specified name of the logical device according to IEC 61850-7-1 and 7-2 within the communication. desc = LDi.getAttribute("desc") ## The description text trLDevice = " LDevice: " + ldName + ' inst:' + inst + ' desc:' + desc self.TR.Trace(trLDevice, TL.GENERAL) iDeviceInstance = IED.AccessPoint.Server.LDevice( inst, desc, ldName) # LN0 et les LN sont ajoutés après iIED_array.tAccessPoint[idxAP].tServer[idxServer].tLDevice.append( iDeviceInstance) LDeviceName = ldName + inst tLN = [ ] # Table of LN0 and LNs instanciation in the current Logical Device. LN_index = 0 # Index to this array pLNi = LDi.firstChild.nextSibling lnClass1 = "" LNtxt = "" while pLNi is not None: if pLNi.localName is None: pLNi = pLNi.nextSibling continue if pLNi.localName == 'Private': pLNi = pLNi.nextSibling continue iLN = self.pLN.Parse_LN(pLNi, iIED_array.name, iIED_array.tAccessPoint[idxAP].name, iIED_array.tDAI) tLN.append(iLN) LNtxt = (" LN: " + iLN.lnPrefix + '_' + iLN.lnType + '_' + iLN.lnInst + \ " Class:" + iLN.lnClass + ' Desc:' + iLN.lnDesc) self.TR.Trace((LNtxt), TL.DETAIL) # The tree of nested level of DO / SDO / DA / SDA ishnadled in IEC_ParcoursDataModel LN_id = iLN.lnPrefix + iLN.lnClass + iLN.lnInst # tLN.append(iLN) LN_index = LN_index + 1 setattr(iDeviceInstance, LN_id, iLN) # = LDeviceInstance if (pLNi.nextSibling) is not None: pLNi = pLNi.nextSibling continue LDi = LDi.nextSibling iDeviceInstance.LN = tLN # Name = iDeviceInstance.inst return iDeviceInstance, iDeviceInstance.inst ## # Parse the IED list created by a self.scl.getElementsByTagName("IED" # # Features: # - Detects IED defined without any Server part. # - Parse the AccessPoints and the Servers: # - Created nested tables AccessPoint and tables of servers # - Once the Server level is reached, the parsing continue with: # - Parse_LD_LN # - Parse LN (from IEC_LN class # # @param TR: the trace Object type to look up. # @return tIED_array: the table of IED and their full data model def Parse_IED(self, TR): LD_index = 0 tIED_array = [ ] ## Iterative version of the Data Model: Table of LN, Table of DO.... tIED_struct = [ ] ## Named based version of the Data Model: MEASURE.MMXU.X.X IEDlst = self.scl.getElementsByTagName("IED") for ied in IEDlst: IEDname = ied.getAttribute( "name" ) ## The identification of the IED, 'Template' in ICD, unique in SCD. _desc = ied.getAttribute("desc") ## The description text _type = ied.getAttribute( "type") ## The (manufacturer specific) IED product type _manufacturer = ied.getAttribute( "manufacturer") ## The manufacturer's name _configVersion = ied.getAttribute( "configVersion" ) ## The basic configuration version of this IED configuration _originalSclVersion = ied.getAttribute( "originalSclVersion" ) ## The original SCL schema version of the IEDs ICD file; optional _originalSclRevision = ied.getAttribute( "originalSclRevision" ) ## The original SCL schema revision of the IEDs ICD file; optional _engRight = ied.getAttribute( "engRight") ## The engineering right transferred by a SED file _owner = ied.getAttribute( "owner") ## The owner project of this IED, self.TR.Trace( ("IED:" + IEDname + ' type:' + _type + ' desc:' + _desc), TL.DETAIL) iIED_array = IED(IEDname, _desc, _type, _manufacturer, _configVersion, _originalSclVersion, _originalSclRevision, _engRight, _owner) iIED_struct = IED(IEDname, _desc, _type, _manufacturer, _configVersion, _originalSclVersion, _originalSclRevision, _engRight, _owner) Services = ied.firstChild.nextSibling # Skip any Private... while (Services.localName is None): Services = Services.nextSibling continue # Skip Service section (Parsed in a different section) while Services.localName == "Private": type = Services.getAttribute("type") self.Dyn.PrivateDynImport(type, Services, iIED_array) self.Dyn.PrivateDynImport(type, Services, iIED_struct) Services = Services.nextSibling Services = Services.nextSibling if Services.localName == "Services": Services = Services.nextSibling Services = Services.nextSibling ## AccessPoint.localName: is the key to the ConnectedAP related to this access point if Services.localName == "AccessPoint": pAcccess = Services _name = pAcccess.getAttribute("name") ## The description text _desc = pAcccess.getAttribute( "desc" ) ## Reference identifying this access point within the IED _router = pAcccess.getAttribute( "router" ) ## The presence and setting to true defines this IED to have a router function. _clock = pAcccess.getAttribute( "clock" ) ## The presence and setting to true defines this IED to be a master clock at this bus. _AccessPoint = IED.AccessPoint(_name, _desc, _router, _clock) iIED_array.tAccessPoint.append(_AccessPoint) idxAccessPoint = len(iIED_array.tAccessPoint) - 1 if Services.firstChild is None: ### In case of a strict client application, the access Point is only declared self.TR.Trace(("AccessPoint: no firstChild"), TL.DETAIL) continue else: ServerSection = Services.firstChild.nextSibling if (ServerSection is None): #Pas de Server, application client pure self.TR.Trace(('Application client:', IEDname, desc), TL.DETAIL) continue if (ServerSection.firstChild is None): # Pas de Server, application client pure TR.Trace(('Application client:', IEDname, desc), TL.DETAIL) if (len(iIED_struct.tAccessPoint)) > 0: iIED_struct.tAccessPoint[ idxAccessPoint].tServer = None else: iIED_struct.tAccessPoint = None iIED_array.Server = None iIED_struct.Server = None iIED_array.tLDevice = None iIED_struct.tLDevice = None continue else: while ServerSection.localName == "Server": _desc = ServerSection.getAttribute( "desc") ## A descriptive text _timeout = ServerSection.getAttribute( "timeout" ) ## Time out in seconds: if a started transaction isnot completed within this time, it is cancelled and reset iServer = IED.AccessPoint.Server(_desc, _timeout) iIED_array.tAccessPoint[ idxAccessPoint].tServer.append(iServer) idxServer = 0 pServer = ServerSection.firstChild.nextSibling while pServer.nextSibling: if pServer.localName is None: pServer = pServer.nextSibling continue if pServer.localName == "Authentication": none = pServer.getAttribute( "none") ## IEC No authentication password = pServer.getAttribute( "password" ) ## Defined in the stack mappings (SCSMs) weak = pServer.getAttribute( "weak" ) ## Defined in the stack mappings (SCSMs) strong = pServer.getAttribute( "strong" ) ## Defined in the stack mappings (SCSMs) certificate = pServer.getAttribute( "certificate" ) ## Defined in the stack mappings (SCSMs) # TODO eventually AccesPoint without server iAuthentication = IED.AccessPoint.Server.Authentication( none, password, weak, strong, certificate) iIED_array.tAccessPoint[ idxAccessPoint].tServer[ idxServer].authentication = iAuthentication pServer = pServer.nextSibling continue if pServer.localName == "Private": # TODO est-ce qu'il y a des balises privées RTE ? pServer = pServer.nextSibling continue if pServer.localName == "LDevice": iDeviceInstance, Name = self.Parse_LD_LN( iIED_array, pServer, idxAccessPoint, idxServer) setattr( iIED_array.tAccessPoint[idxAccessPoint] .tServer[idxServer], Name, iDeviceInstance) # = LDeviceInstance pServer = pServer.nextSibling continue ServerSection = ServerSection.nextSibling if ServerSection is None: break # Version itérative tIED_array.append(iIED_array) # Version structurée # iIED_struct.Server = tServer # tIED_struct.append(iIED_struct) continue self.TR.Trace(("FIN SERVER"), TL.DETAIL) self.TR.Trace(("FIN IED"), TL.DETAIL) return tIED_array