You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
2172 lines
75 KiB
C#
2172 lines
75 KiB
C#
/*
|
|
* Copyright 2013-2025 Michael Zillgith, MZ Automation GmbH
|
|
*
|
|
* This file is part of MZ Automation IEC 61850 SDK
|
|
*
|
|
* All rights reserved.
|
|
*/
|
|
|
|
using IEC61850.SCL.DataModel;
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.IO;
|
|
using System.Linq;
|
|
using System.Reflection;
|
|
using System.Text;
|
|
using System.Xml;
|
|
using System.Xml.Schema;
|
|
using DataSet = IEC61850.SCL.DataModel.DataSet;
|
|
|
|
namespace IEC61850.SCL
|
|
{
|
|
|
|
public class SclParserException : Exception
|
|
{
|
|
private XmlNode xmlNode;
|
|
|
|
public XmlNode XmlNode
|
|
{
|
|
get
|
|
{
|
|
return xmlNode;
|
|
}
|
|
}
|
|
|
|
public SclParserException(XmlNode xmlNode, string message)
|
|
: base(message)
|
|
{
|
|
this.xmlNode = xmlNode;
|
|
}
|
|
}
|
|
|
|
public class SclFileIssue
|
|
{
|
|
public string Severity { get; set; }
|
|
|
|
public int Line { get; set; }
|
|
|
|
public string Type { get; set; }
|
|
|
|
public string Issue { get; set; }
|
|
|
|
public object Object { get; set; }
|
|
|
|
public string ObjectIssue { get; set; }
|
|
|
|
public int Index { get; set; }
|
|
|
|
public override string ToString()
|
|
{
|
|
return "Severity: " + Severity + "| Line: " + Line.ToString() + " | Type: " + Type + " | Issue: " + Issue;
|
|
}
|
|
|
|
}
|
|
|
|
public class SclDocument
|
|
{
|
|
public const string SCL_XMLNS = "http://www.iec.ch/61850/2003/SCL";
|
|
private string filename = null;
|
|
private List<SclFileIssue> sclFileIssues = new List<SclFileIssue>();
|
|
private List<SclValidatorMessage> messages = new List<SclValidatorMessage>();
|
|
private PositionXmlDocument doc;
|
|
|
|
public void InitializePositionDoc()
|
|
{
|
|
using (var reader = new XmlTextReader(filename))
|
|
{
|
|
doc = new PositionXmlDocument();
|
|
doc.Load(reader);
|
|
}
|
|
}
|
|
|
|
public PositionXmlElement GetXmlNodePosition(XmlNode xmlNode)
|
|
{
|
|
try
|
|
{
|
|
if (doc == null)
|
|
InitializePositionDoc();
|
|
|
|
string xpath = FindXPath(xmlNode);
|
|
var node = doc.SelectSingleNode(xpath, nsManager);
|
|
|
|
return (PositionXmlElement)node;
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Console.WriteLine(ex.Message);
|
|
return null;
|
|
}
|
|
|
|
}
|
|
|
|
static string FindXPath(XmlNode node)
|
|
{
|
|
StringBuilder builder = new StringBuilder();
|
|
while (node != null)
|
|
{
|
|
switch (node.NodeType)
|
|
{
|
|
case XmlNodeType.Attribute:
|
|
builder.Insert(0, "/@" + node.Name);
|
|
node = ((XmlAttribute)node).OwnerElement;
|
|
break;
|
|
case XmlNodeType.Element:
|
|
|
|
int index = FindElementIndex((XmlElement)node);
|
|
|
|
if (node.ParentNode.Name == "SCL")
|
|
{
|
|
builder.Insert(0, "//scl:" + node.Name + "[" + index + "]");
|
|
return builder.ToString();
|
|
}
|
|
else
|
|
{
|
|
builder.Insert(0, "/scl:" + node.Name + "[" + index + "]");
|
|
node = node.ParentNode;
|
|
}
|
|
|
|
break;
|
|
case XmlNodeType.Document:
|
|
return builder.ToString();
|
|
default:
|
|
throw new ArgumentException("Only elements and attributes are supported");
|
|
}
|
|
}
|
|
throw new ArgumentException("Node was not in a document");
|
|
}
|
|
|
|
static int FindElementIndex(XmlElement element)
|
|
{
|
|
XmlNode parentNode = element.ParentNode;
|
|
if (parentNode is XmlDocument)
|
|
{
|
|
return 1;
|
|
}
|
|
XmlElement parent = (XmlElement)parentNode;
|
|
int index = 1;
|
|
foreach (XmlNode candidate in parent.ChildNodes)
|
|
{
|
|
if (candidate is XmlElement && candidate.Name == element.Name)
|
|
{
|
|
if (candidate == element)
|
|
{
|
|
return index;
|
|
}
|
|
index++;
|
|
}
|
|
}
|
|
throw new ArgumentException("Couldn't find element within parent");
|
|
}
|
|
|
|
public List<SclValidatorMessage> Messages
|
|
{
|
|
get
|
|
{
|
|
return messages;
|
|
}
|
|
}
|
|
|
|
private XmlDocument sclDocument;
|
|
private XmlNamespaceManager nsManager;
|
|
|
|
public XmlNamespaceManager NsManager
|
|
{
|
|
get { return nsManager; }
|
|
}
|
|
|
|
private SclDataTypeTemplates dataTypeTemplates = null;
|
|
|
|
private List<SclIED> ieds = null;
|
|
private List<SclSubstation> substations = null;
|
|
private SclHeader header = null;
|
|
private SclCommunication communication = null;
|
|
private string sclVersion = null;
|
|
private string sclRevision = null;
|
|
private string sclRelease = null;
|
|
private static bool changed = false;
|
|
|
|
|
|
|
|
public string SclVersion
|
|
{
|
|
get { return sclVersion; }
|
|
|
|
set { XmlHelper.SetAttributeCreateIfNotExists(sclDocument, sclDocument.SelectSingleNode("//scl:SCL", nsManager), "version", value); sclVersion = value; }
|
|
|
|
}
|
|
|
|
public string SclRevision
|
|
{
|
|
get { return sclRevision; }
|
|
|
|
set { XmlHelper.SetAttributeCreateIfNotExists(sclDocument, sclDocument.SelectSingleNode("//scl:SCL", nsManager), "revision", value); sclRevision = value; }
|
|
|
|
}
|
|
|
|
public string SclRelease
|
|
{
|
|
get { return sclRelease; }
|
|
}
|
|
|
|
XmlNodeChangedEventHandler handler = (sender, e) => changed = true;
|
|
|
|
private List<string> schemaFiles = new List<string> {"SCL.xsd", "SCL_BaseSimpleTypes.xsd","SCL_BaseTypes.xsd", "SCL_Communication.xsd",
|
|
"SCL_DataTypeTemplates.xsd","SCL_Enums.xsd","SCL_IED.xsd","SCL_Substation.xsd"};
|
|
|
|
private string ProgramDirectory()
|
|
{
|
|
return Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
|
|
}
|
|
|
|
private void XmlDocValidation()
|
|
{
|
|
|
|
if (!changed)
|
|
{
|
|
XmlReaderSettings xmlSettings = new XmlReaderSettings();
|
|
|
|
if (sclRevision != null)
|
|
{
|
|
if (sclRevision == "B")
|
|
{
|
|
foreach (string schemaName in schemaFiles)
|
|
{
|
|
if (sclRelease == null)
|
|
{
|
|
xmlSettings.Schemas.Add("http://www.iec.ch/61850/2003/SCL", ProgramDirectory() + "\\IEC_61850-6.2009.SCL.2007B\\" + schemaName);
|
|
|
|
}
|
|
else if (sclRelease == "4")
|
|
{
|
|
xmlSettings.Schemas.Add("http://www.iec.ch/61850/2003/SCL", ProgramDirectory() + "\\IEC_61850-6.2018.SCL.2007B4\\" + schemaName);
|
|
}
|
|
|
|
}
|
|
}
|
|
else
|
|
{
|
|
foreach (string schemaName in schemaFiles)
|
|
{
|
|
xmlSettings.Schemas.Add("http://www.iec.ch/61850/2003/SCL", ProgramDirectory() + "\\IEC_61850-6.2018.SCL.2007A\\" + schemaName);
|
|
}
|
|
|
|
}
|
|
}
|
|
else
|
|
{
|
|
foreach (string schemaName in schemaFiles)
|
|
{
|
|
|
|
if (sclRelease == null)
|
|
{
|
|
xmlSettings.Schemas.Add("http://www.iec.ch/61850/2003/SCL", ProgramDirectory() + "\\IEC_61850-6.2009.SCL.2007B\\" + schemaName);
|
|
|
|
}
|
|
else if (sclRelease == "4")
|
|
{
|
|
xmlSettings.Schemas.Add("http://www.iec.ch/61850/2003/SCL", ProgramDirectory() + "\\IEC_61850-6.2018.SCL.2007B4\\" + schemaName);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
xmlSettings.Schemas.XmlResolver = new XmlUrlResolver();
|
|
|
|
xmlSettings.ValidationType = ValidationType.Schema;
|
|
|
|
xmlSettings.ValidationEventHandler += new ValidationEventHandler(delegate (object sender, ValidationEventArgs e)
|
|
{
|
|
|
|
if (e.Exception != null)
|
|
messages.Add(new SclValidatorMessage(e.Severity, e.Message, e.Exception.LineNumber, e.Exception.LinePosition));
|
|
else
|
|
messages.Add(new SclValidatorMessage(e.Severity, e.Message));
|
|
|
|
|
|
});
|
|
|
|
try
|
|
{
|
|
if (filename != null)
|
|
{
|
|
XmlReader scl = XmlReader.Create(filename, xmlSettings);
|
|
|
|
while (scl.Read())
|
|
{
|
|
}
|
|
|
|
scl.Dispose();
|
|
}
|
|
|
|
}
|
|
catch (XmlSchemaValidationException e)
|
|
{
|
|
|
|
if (e.SourceObject != null)
|
|
Console.WriteLine(e.SourceObject.ToString());
|
|
|
|
Console.WriteLine(e.Message);
|
|
|
|
if (e.SourceSchemaObject != null)
|
|
Console.WriteLine(e.SourceSchemaObject.SourceUri);
|
|
|
|
Console.WriteLine(e.LineNumber);
|
|
Console.WriteLine(e.SourceUri);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
XmlDocument xmlDoc = sclDocument;
|
|
|
|
xmlDoc.Schemas = new XmlSchemaSet();
|
|
|
|
if (header != null)
|
|
{
|
|
if (sclRevision == "B")
|
|
{
|
|
foreach (string schemaName in schemaFiles)
|
|
{
|
|
if (sclRelease == null)
|
|
xmlDoc.Schemas.Add("http://www.iec.ch/61850/2003/SCL", ProgramDirectory() + "\\IEC_61850-6.2009.SCL.2007B\\" + schemaName);
|
|
else if (sclRelease == "4")
|
|
xmlDoc.Schemas.Add("http://www.iec.ch/61850/2003/SCL", ProgramDirectory() + "\\IEC_61850-6.2018.SCL.2007B4\\" + schemaName);
|
|
}
|
|
|
|
}
|
|
else
|
|
{
|
|
foreach (string schemaName in schemaFiles)
|
|
{
|
|
xmlDoc.Schemas.Add("http://www.iec.ch/61850/2003/SCL", ProgramDirectory() + "\\IEC_61850-6.2018.SCL.2007A\\" + schemaName);
|
|
|
|
}
|
|
|
|
}
|
|
}
|
|
else
|
|
{
|
|
foreach (string schemaName in schemaFiles)
|
|
{
|
|
if (sclRelease == null)
|
|
xmlDoc.Schemas.Add("http://www.iec.ch/61850/2003/SCL", ProgramDirectory() + "\\IEC_61850-6.2009.SCL.2007B\\" + schemaName);
|
|
else if (sclRelease == "4")
|
|
xmlDoc.Schemas.Add("http://www.iec.ch/61850/2003/SCL", ProgramDirectory() + "\\IEC_61850-6.2018.SCL.2007B4\\" + schemaName);
|
|
}
|
|
}
|
|
|
|
xmlDoc.Validate(new ValidationEventHandler(delegate (object sender, ValidationEventArgs e)
|
|
{
|
|
if (e.Exception != null)
|
|
messages.Add(new SclValidatorMessage(e.Severity, e.Message, e.Exception.LineNumber, e.Exception.LinePosition));
|
|
else
|
|
messages.Add(new SclValidatorMessage(e.Severity, e.Message));
|
|
|
|
}));
|
|
}
|
|
|
|
|
|
}
|
|
|
|
public void AddIssue(XmlNode xmlNode, string Severity, string Type, string Issue, object Object, string ObjectIssue)
|
|
{
|
|
SclFileIssue sclFileIssue = new SclFileIssue();
|
|
sclFileIssue.Issue = Issue;
|
|
sclFileIssue.Severity = Severity;
|
|
sclFileIssue.Type = Type;
|
|
sclFileIssue.Object = Object;
|
|
sclFileIssue.ObjectIssue = ObjectIssue;
|
|
|
|
if (xmlNode != null)
|
|
{
|
|
PositionXmlElement positionXmlElement = GetXmlNodePosition(xmlNode);
|
|
if (positionXmlElement != null)
|
|
sclFileIssue.Line = positionXmlElement.LineNumber;
|
|
|
|
}
|
|
|
|
if (!sclFileIssues.Exists(x => x.Issue == sclFileIssue.Issue && x.Severity == sclFileIssue.Severity && x.Type == sclFileIssue.Type && x.Object == sclFileIssue.Object &&
|
|
x.ObjectIssue == sclFileIssue.ObjectIssue && x.Line == sclFileIssue.Line))
|
|
sclFileIssues.Add(sclFileIssue);
|
|
}
|
|
|
|
public List<SclFileIssue> SclFileIssues
|
|
{
|
|
get { return sclFileIssues; }
|
|
}
|
|
|
|
public List<SclFileIssue> ValidationMessages()
|
|
{
|
|
List<SclFileIssue> validationMessages = new List<SclFileIssue>();
|
|
|
|
messages = new List<SclValidatorMessage>();
|
|
|
|
XmlDocValidation();
|
|
|
|
foreach (SclValidatorMessage msg in messages)
|
|
{
|
|
validationMessages.Add(new SclFileIssue()
|
|
{
|
|
Line = msg.LineNo,
|
|
Severity = msg.Level == System.Xml.Schema.XmlSeverityType.Error ? "ERROR" : "WARNING",
|
|
Type = "Schema",
|
|
Issue = msg.Message
|
|
});
|
|
}
|
|
|
|
return validationMessages;
|
|
|
|
|
|
}
|
|
|
|
public XmlDocument XmlDocument
|
|
{
|
|
get
|
|
{
|
|
return sclDocument;
|
|
}
|
|
set
|
|
{
|
|
sclDocument = value;
|
|
}
|
|
}
|
|
|
|
public List<SclIED> IEDs
|
|
{
|
|
get
|
|
{
|
|
return new List<SclIED>(ieds);
|
|
}
|
|
}
|
|
|
|
public void CheckUsedDataTypes()
|
|
{
|
|
if (dataTypeTemplates != null)
|
|
MarkAllDataTypesAsUnused();
|
|
|
|
foreach (SclIED ied in ieds)
|
|
{
|
|
foreach (SclAccessPoint ap in ied.AccessPoints)
|
|
{
|
|
if (ap.Server != null)
|
|
{
|
|
IEDDataModel iedModel = GetDataModel(ied.Name, ap.Name);
|
|
|
|
foreach (LogicalDevice ld in iedModel.LogicalDevices)
|
|
{
|
|
foreach (LogicalNode ln in ld.LogicalNodes)
|
|
{
|
|
if (dataTypeTemplates != null)
|
|
{
|
|
SclLNodeType sclLNodeType = dataTypeTemplates.LNTypes.Find(x => x.Id == ln.SclElement.LnType);
|
|
|
|
CheckUsedEnumtypes(ln);
|
|
|
|
if (sclLNodeType != null)
|
|
{
|
|
sclLNodeType.IsUsed = true;
|
|
if (!sclLNodeType.UsedOn.Contains(ln))
|
|
sclLNodeType.UsedOn.Add(ln);
|
|
}
|
|
|
|
foreach (DataObject dobj in ln.DataObjects)
|
|
{
|
|
CheckUsedDataObject(dobj);
|
|
}
|
|
}
|
|
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private void CheckUsedEnumtypes(LogicalNode logicalNode)
|
|
{
|
|
foreach (SclDOI sclDOI_ in logicalNode.SclElement.DOIs)
|
|
{
|
|
CheckUsedPredefinedValuesEnumType(sclDOI_, logicalNode, null);
|
|
}
|
|
}
|
|
|
|
private void CheckUsedPredefinedValuesEnumType(Object Object, LogicalNode logicalNode, SclDOI sclDOI_)
|
|
{
|
|
if (Object is SclDOI sclDOI)
|
|
{
|
|
if (sclDOI.SclDAIs.Count > 0)
|
|
{
|
|
foreach (SclDAI sclDAI in sclDOI.SclDAIs)
|
|
{
|
|
foreach (SclVal sclVal in sclDAI.GetValues())
|
|
{
|
|
DataAttribute dataAttribute = GetDataAttribute(logicalNode, sclDOI, sclDAI);
|
|
if (dataAttribute != null)
|
|
{
|
|
if (sclVal.Value != null)
|
|
{
|
|
if (dataAttribute.Definition.AttributeType == AttributeType.ENUMERATED)
|
|
{
|
|
|
|
string EnumType = dataAttribute.Definition.Type;
|
|
if (EnumType != null)
|
|
{
|
|
SclEnumType sclEnumType = DataTypeTemplates.GetEnumType(EnumType);
|
|
if (sclEnumType != null)
|
|
{
|
|
sclEnumType.IsUsed = true;
|
|
if (!sclEnumType.UsedOn.Contains(dataAttribute))
|
|
sclEnumType.UsedOn.Add(dataAttribute);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
if (sclDOI.SclSDIs.Count > 0)
|
|
{
|
|
foreach (SclSDI sclSDI in sclDOI.SclSDIs)
|
|
CheckUsedPredefinedValuesEnumType(sclSDI, logicalNode, sclDOI);
|
|
}
|
|
}
|
|
|
|
else if (Object is SclSDI)
|
|
{
|
|
SclSDI sclSDI = Object as SclSDI;
|
|
|
|
if (sclSDI.SclSDIs.Count > 0)
|
|
{
|
|
foreach (SclSDI sclSDI_ in sclSDI.SclSDIs)
|
|
CheckUsedPredefinedValuesEnumType(sclSDI_, logicalNode, sclDOI_);
|
|
}
|
|
}
|
|
}
|
|
|
|
private void CheckUsedDataObject(DataObject dataObject)
|
|
{
|
|
SclDOType sclDOType = dataTypeTemplates.DOTypes.Find(x => x == dataObject.DOType);
|
|
|
|
if (sclDOType != null)
|
|
{
|
|
sclDOType.IsUsed = true;
|
|
if (!sclDOType.UsedOn.Contains(dataObject))
|
|
sclDOType.UsedOn.Add(dataObject);
|
|
|
|
CheckUsedSubDataObjectsAndDataAttributes(sclDOType);
|
|
}
|
|
}
|
|
|
|
private void CheckUsedSubDataObjectsAndDataAttributes(SclDOType type)
|
|
{
|
|
if (type.SubDataObjects != null)
|
|
{
|
|
foreach (SclDataObjectDefinition doDef in type.SubDataObjects)
|
|
{
|
|
SclDOType doType = DataTypeTemplates?.GetDOType(doDef.Type);
|
|
|
|
if (doType != null)
|
|
{
|
|
doType.IsUsed = true;
|
|
|
|
CheckUsedSubDataObjectsAndDataAttributes(doType);
|
|
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
if (type.DataAttributes != null)
|
|
{
|
|
|
|
foreach (SclDataAttributeDefinition daDef in type.DataAttributes)
|
|
{
|
|
if (daDef.AttributeType == AttributeType.CONSTRUCTED)
|
|
{
|
|
SclDAType daType = DataTypeTemplates?.GetDAType(daDef.Type);
|
|
|
|
if (daType != null)
|
|
{
|
|
daType.IsUsed = true;
|
|
|
|
CheckUsedSubDataAttributes(daDef.Fc, daType);
|
|
}
|
|
|
|
|
|
}
|
|
else if (daDef.AttributeType == AttributeType.ENUMERATED)
|
|
{
|
|
SclEnumType enumType = DataTypeTemplates?.GetEnumType(daDef.Type);
|
|
|
|
if (enumType != null)
|
|
enumType.IsUsed = true;
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
private void CheckUsedSubDataAttributes(SclFC fc, SclDAType daType)
|
|
{
|
|
foreach (SclDataAttributeDefinition daDef in daType.SubDataAttributes)
|
|
{
|
|
|
|
SclFC newFc;
|
|
|
|
if (daDef.Fc != SclFC.NONE)
|
|
newFc = daDef.Fc;
|
|
else
|
|
newFc = fc;
|
|
|
|
|
|
if (daDef.AttributeType == AttributeType.CONSTRUCTED)
|
|
{
|
|
SclDAType subDaType = DataTypeTemplates?.GetDAType(daDef.Type);
|
|
|
|
if (subDaType != null)
|
|
{
|
|
subDaType.IsUsed = true;
|
|
|
|
//CheckUsedSubDataAttributes(newFc, subDaType);// check this statement
|
|
}
|
|
|
|
}
|
|
else
|
|
{
|
|
if (daDef.AttributeType == AttributeType.ENUMERATED)
|
|
{
|
|
SclEnumType enumType = DataTypeTemplates?.GetEnumType(daDef.Type);
|
|
|
|
if (enumType == null)
|
|
enumType.IsUsed = true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private void MarkAllDataTypesAsUnused()
|
|
{
|
|
foreach (SclType type in dataTypeTemplates.AllTypes)
|
|
{
|
|
type.IsUsed = false;
|
|
type.UsedOn.Clear();
|
|
}
|
|
}
|
|
|
|
public SclDataTypeTemplates DataTypeTemplates
|
|
{
|
|
get
|
|
{
|
|
return dataTypeTemplates;
|
|
}
|
|
set
|
|
{
|
|
dataTypeTemplates = value;
|
|
}
|
|
}
|
|
|
|
public void Remove(SclIED ied)
|
|
{
|
|
XmlNode parent = ied.xmlNode.ParentNode;
|
|
|
|
if (parent != null)
|
|
{
|
|
parent.RemoveChild(ied.xmlNode);
|
|
}
|
|
|
|
ieds.Remove(ied);
|
|
}
|
|
|
|
public void RemoveSubstation(SclSubstation sclSubstation)
|
|
{
|
|
XmlNode parent = sclSubstation.xmlNode.ParentNode;
|
|
|
|
if (parent != null)
|
|
{
|
|
parent.RemoveChild(sclSubstation.xmlNode);
|
|
}
|
|
|
|
substations.Remove(sclSubstation);
|
|
}
|
|
|
|
|
|
public List<SclSubstation> Substations
|
|
{
|
|
get
|
|
{
|
|
return new List<SclSubstation>(substations);
|
|
}
|
|
}
|
|
|
|
public void Remove(SclSubstation substation)
|
|
{
|
|
XmlNode parent = substation.xmlNode.ParentNode;
|
|
|
|
if (parent != null)
|
|
{
|
|
parent.RemoveChild(substation.xmlNode);
|
|
}
|
|
|
|
substations.Remove(substation);
|
|
}
|
|
|
|
public bool HasDataTypeTemplates() => (dataTypeTemplates != null);
|
|
|
|
public SclHeader Header
|
|
{
|
|
get
|
|
{
|
|
return header;
|
|
}
|
|
}
|
|
|
|
public SclCommunication Communication
|
|
{
|
|
get
|
|
{
|
|
return communication;
|
|
}
|
|
}
|
|
|
|
public string Filename
|
|
{
|
|
get
|
|
{
|
|
return filename;
|
|
}
|
|
set
|
|
{
|
|
filename = value;
|
|
}
|
|
|
|
}
|
|
|
|
public void Save(string filename)
|
|
{
|
|
if (filename != null)
|
|
{
|
|
sclDocument.Save(filename);
|
|
|
|
changed = false;
|
|
}
|
|
|
|
}
|
|
|
|
private void ParseDataTypeTemplatesSection()
|
|
{
|
|
XmlNode dttNode = sclDocument.SelectSingleNode("//scl:DataTypeTemplates", nsManager);
|
|
|
|
if (dttNode != null)
|
|
{
|
|
dataTypeTemplates = new SclDataTypeTemplates(this, dttNode);
|
|
}
|
|
}
|
|
|
|
public SclHeader AddHeader()
|
|
{
|
|
if (header == null)
|
|
{
|
|
SclHeader newheader = new SclHeader(this, NsManager);
|
|
|
|
XmlNode newNode = newheader.XmlNode;
|
|
|
|
if (newNode.OwnerDocument != sclDocument)
|
|
{
|
|
newNode = sclDocument.ImportNode(newheader.XmlNode.CloneNode(true), true);
|
|
}
|
|
|
|
if (substations.Count > 0)
|
|
{
|
|
XmlNode parent = substations[0].xmlNode.ParentNode;
|
|
|
|
parent.InsertBefore(newNode, substations[0].xmlNode);
|
|
}
|
|
|
|
else if (communication != null)
|
|
{
|
|
XmlNode parent = communication.xmlNode.ParentNode;
|
|
|
|
parent.InsertAfter(newNode, communication.xmlNode);
|
|
}
|
|
|
|
else if (ieds.Count > 0)
|
|
{
|
|
XmlNode parent = ieds[0].xmlNode.ParentNode;
|
|
|
|
parent.InsertBefore(newNode, ieds[0].xmlNode);
|
|
}
|
|
|
|
else if (dataTypeTemplates != null)
|
|
{
|
|
XmlNode parent = dataTypeTemplates.xmlNode.ParentNode;
|
|
|
|
parent.InsertBefore(newNode, dataTypeTemplates.xmlNode);
|
|
}
|
|
|
|
else
|
|
{
|
|
XmlNode parent = sclDocument.SelectSingleNode("//scl:SCL", nsManager);
|
|
parent.AppendChild(newNode);
|
|
|
|
}
|
|
|
|
try
|
|
{
|
|
header = new SclHeader(this, newNode, NsManager);
|
|
return header;
|
|
|
|
}
|
|
catch (SclParserException e)
|
|
{
|
|
Console.WriteLine("Failed to add Header");
|
|
Console.WriteLine(e.ToString());
|
|
|
|
return null;
|
|
}
|
|
}
|
|
else
|
|
return null;
|
|
}
|
|
|
|
public bool DeleteHeader()
|
|
{
|
|
if (header != null)
|
|
{
|
|
XmlNode parent = header.XmlNode.ParentNode;
|
|
|
|
if (parent != null)
|
|
{
|
|
parent.RemoveChild(header.XmlNode);
|
|
}
|
|
|
|
header = null;
|
|
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
public bool DeleteDataTypeTemplates()
|
|
{
|
|
if (dataTypeTemplates != null)
|
|
{
|
|
XmlNode parent = dataTypeTemplates.XmlNode.ParentNode;
|
|
|
|
if (parent != null)
|
|
{
|
|
parent.RemoveChild(dataTypeTemplates.XmlNode);
|
|
}
|
|
|
|
dataTypeTemplates = null;
|
|
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
private void ParseIEDSections()
|
|
{
|
|
XmlNodeList iedNodes = sclDocument.SelectNodes("//scl:SCL/scl:IED", nsManager);
|
|
|
|
if (iedNodes.Count < 1)
|
|
AddIssue(null, "ERROR", "Model integrity", "SCL contains no IED element", this, "IED");
|
|
|
|
foreach (XmlNode ied in iedNodes)
|
|
{
|
|
ieds.Add(new SclIED(this, ied, nsManager));
|
|
}
|
|
|
|
if (Communication != null)
|
|
{
|
|
List<SclSubNetwork> subNetworks = communication.GetSubNetworks();
|
|
|
|
if (subNetworks != null)
|
|
{
|
|
foreach (SclSubNetwork subnetwork in subNetworks)
|
|
{
|
|
List<SclConnectedAP> connectedAPs = subnetwork.GetConnectedAPs();
|
|
|
|
if (connectedAPs != null)
|
|
{
|
|
foreach (SclConnectedAP connectedAP in connectedAPs)
|
|
{
|
|
SclIED connectedAP_IED = ieds.Find(x => x.Name == connectedAP.IedName);
|
|
|
|
if (connectedAP_IED == null)
|
|
{
|
|
AddIssue(connectedAP.xmlNode, "ERROR", "Model integrity", "IED " + connectedAP.IedName + " in ConnectedAP doesn't exist", this, "connectedAP");
|
|
AddIssue(connectedAP.xmlNode, "ERROR", "Model integrity", "Access Point " + connectedAP.ApName + " in ConnectedAP doesn't exist", this, "connectedAP");
|
|
}
|
|
else
|
|
{
|
|
if (connectedAP_IED.AccessPoints.Find(x => x.Name == connectedAP.ApName) == null)
|
|
{
|
|
AddIssue(connectedAP.xmlNode, "ERROR", "Model integrity", "Access Point " + connectedAP.ApName + " in ConnectedAP doesn't exist", this, "connectedAP");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
private void ParseSubstationSections()
|
|
{
|
|
XmlNodeList substationNodes = sclDocument.SelectNodes("//scl:SCL/scl:Substation", nsManager);
|
|
|
|
foreach (XmlNode substationNode in substationNodes)
|
|
{
|
|
substations.Add(new SclSubstation(sclDocument, this, substationNode, nsManager));
|
|
}
|
|
}
|
|
|
|
private void ParseScl()
|
|
{
|
|
XmlNode sclVersion = sclDocument.SelectSingleNode("//scl:SCL", nsManager);
|
|
if (sclVersion != null)
|
|
{
|
|
this.sclVersion = XmlHelper.GetAttributeValue(sclVersion, "version");
|
|
sclRevision = XmlHelper.GetAttributeValue(sclVersion, "revision");
|
|
sclRelease = XmlHelper.GetAttributeValue(sclVersion, "release");
|
|
|
|
}
|
|
}
|
|
|
|
private void ParseHeaderSection()
|
|
{
|
|
XmlNode headerNode = sclDocument.SelectSingleNode("//scl:Header", nsManager);
|
|
|
|
if (headerNode == null)
|
|
AddIssue(null, "ERROR", "Model integrity", "Header is missing", this, "Header");
|
|
else
|
|
header = new SclHeader(this, headerNode, nsManager);
|
|
}
|
|
|
|
private void ParseCommunicationSection()
|
|
{
|
|
XmlNode communicationNode = sclDocument.SelectSingleNode("//scl:Communication", nsManager);
|
|
|
|
if (communicationNode != null)
|
|
communication = new SclCommunication(communicationNode, this, nsManager);
|
|
}
|
|
|
|
private void ParseDocument()
|
|
{
|
|
ieds = new List<SclIED>();
|
|
substations = new List<SclSubstation>();
|
|
header = null;
|
|
communication = null;
|
|
|
|
ParseScl();
|
|
|
|
ParseHeaderSection();
|
|
|
|
ParseSubstationSections();
|
|
|
|
ParseCommunicationSection();
|
|
|
|
ParseDataTypeTemplatesSection();
|
|
|
|
ParseIEDSections();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Rebuild the internal document structure by the DOM model
|
|
/// </summary>
|
|
public void Rebuild()
|
|
{
|
|
ParseDocument();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Removes the "Private" elements from the SCL document.
|
|
/// </summary>
|
|
public void RemovePrivateElements()
|
|
{
|
|
XmlNodeList privateNodes = sclDocument.SelectNodes("//scl:Private", nsManager);
|
|
|
|
foreach (XmlNode privateNode in privateNodes)
|
|
{
|
|
XmlNode parent = privateNode.ParentNode;
|
|
|
|
if (parent != null)
|
|
{
|
|
parent.RemoveChild(privateNode);
|
|
}
|
|
}
|
|
}
|
|
|
|
private void InitializeDocument()
|
|
{
|
|
nsManager = new XmlNamespaceManager(sclDocument.NameTable);
|
|
nsManager.AddNamespace("scl", SCL_XMLNS);
|
|
|
|
ParseDocument();
|
|
}
|
|
|
|
public SclDocument(XmlDocument xmlDocument)
|
|
{
|
|
sclDocument = xmlDocument;
|
|
|
|
InitializeDocument();
|
|
}
|
|
|
|
public SclDocument(string filePath)
|
|
{
|
|
Filename = filePath;
|
|
sclDocument = new XmlDocument();
|
|
|
|
sclDocument.Load(filePath);
|
|
|
|
InitializeDocument();
|
|
|
|
sclDocument.NodeChanged += handler;
|
|
sclDocument.NodeInserted += handler;
|
|
sclDocument.NodeRemoved += handler;
|
|
}
|
|
|
|
public List<string> GetIedNames()
|
|
{
|
|
List<string> iedNames = new List<string>();
|
|
|
|
foreach (SclIED ied in ieds)
|
|
iedNames.Add(ied.Name);
|
|
|
|
return iedNames;
|
|
}
|
|
|
|
private SclIED GetIedByName(string iedName)
|
|
{
|
|
foreach (SclIED ied in ieds)
|
|
{
|
|
if (ied.Name.Equals(iedName))
|
|
return ied;
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
private void AddSubDataAttributesToDataAttribute(DataAttribute da, SclFC fc, SclDAType daType)
|
|
{
|
|
foreach (SclDataAttributeDefinition daDef in daType.SubDataAttributes)
|
|
{
|
|
|
|
SclFC newFc;
|
|
|
|
if (daDef.Fc != SclFC.NONE)
|
|
newFc = daDef.Fc;
|
|
else
|
|
newFc = fc;
|
|
|
|
DataAttribute subDataAttribute = new DataAttribute(daDef.Name, da, newFc, daDef.AttributeType, daDef.Count, daDef);
|
|
|
|
if (daDef.AttributeType == AttributeType.CONSTRUCTED)
|
|
{
|
|
SclDAType subDaType = DataTypeTemplates?.GetDAType(daDef.Type);
|
|
|
|
if (subDaType == null)
|
|
AddIssue(daDef.XmlNode, "ERROR", "Model integrity", "DAType \"" + daDef.Type + "\" of DA \"" + daDef.Name + "\" is not defined", this, "DAType");
|
|
else
|
|
{
|
|
subDaType.IsUsed = true;
|
|
AddSubDataAttributesToDataAttribute(subDataAttribute, newFc, subDaType);
|
|
}
|
|
|
|
}
|
|
else
|
|
{
|
|
if (daDef.AttributeType == AttributeType.ENUMERATED)
|
|
{
|
|
SclEnumType enumType = DataTypeTemplates?.GetEnumType(daDef.Type);
|
|
|
|
if (enumType == null)
|
|
AddIssue(daDef.XmlNode, "ERROR", "Model integrity", "EnumType \"" + daDef.Type + "\" of DA \"" + daDef.Name + "\" is not defined", this, "EnumType");
|
|
else
|
|
enumType.IsUsed = true;
|
|
}
|
|
}
|
|
|
|
da.SubDataAttributes.Add(subDataAttribute);
|
|
}
|
|
}
|
|
|
|
private void AddSubDataObjectsAndDataAttributesToDataObject(DataObject dataObject, SclDOType type)
|
|
{
|
|
|
|
if (type.SubDataObjects != null)
|
|
{
|
|
foreach (SclDataObjectDefinition doDef in type.SubDataObjects)
|
|
{
|
|
|
|
SclDOType doType = DataTypeTemplates?.GetDOType(doDef.Type);
|
|
|
|
if (doType == null)
|
|
AddIssue(doDef.XmlNode, "ERROR", "Model integrity", "DOType \"" + doDef.Type + "\" of DO \"" + doDef.Name + "\" is not defined", this, "DOType");
|
|
else
|
|
{
|
|
doType.IsUsed = true;
|
|
|
|
DataObject subDataObject = new DataObject(doDef, doType, dataObject);
|
|
AddSubDataObjectsAndDataAttributesToDataObject(subDataObject, doType);// EXCEPTION WHEN ADDING NEW LN
|
|
|
|
dataObject.DataObjectsAndAttributes.Add(subDataObject);
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
if (type.DataAttributes != null)
|
|
{
|
|
|
|
foreach (SclDataAttributeDefinition daDef in type.DataAttributes)
|
|
{
|
|
|
|
DataAttribute da = new DataAttribute(daDef.Name, dataObject, daDef.Fc, daDef.AttributeType, daDef.Count, daDef);
|
|
|
|
if (daDef.AttributeType == AttributeType.CONSTRUCTED)
|
|
{
|
|
SclDAType daType = DataTypeTemplates?.GetDAType(daDef.Type);
|
|
|
|
if (daType == null)
|
|
AddIssue(daDef.XmlNode, "ERROR", "Model integrity", "DAType \"" + daDef.Type + "\" of DA \"" + daDef.Name + "\" is not defined", this, "DAType");
|
|
else
|
|
{
|
|
daType.IsUsed = true;
|
|
if (!da.ObjRef.EndsWith(daDef.Name + "." + daDef.Name))
|
|
AddSubDataAttributesToDataAttribute(da, daDef.Fc, daType);
|
|
}
|
|
|
|
|
|
}
|
|
else if (daDef.AttributeType == AttributeType.ENUMERATED)
|
|
{
|
|
SclEnumType enumType = DataTypeTemplates?.GetEnumType(daDef.Type);
|
|
|
|
if (enumType == null)
|
|
AddIssue(daDef.XmlNode, "ERROR", "Model integrity", "EnumType \"" + daDef.Type + "\" of DA \"" + daDef.Name + "\" is not defined", this, "EnumType");
|
|
else
|
|
enumType.IsUsed = true;
|
|
}
|
|
|
|
dataObject.DataObjectsAndAttributes.Add(da);
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
public void AddDataObjectsToLogicalNode(LogicalNode logicalNode, SclLN ln)
|
|
{
|
|
SclLNodeType lnType = DataTypeTemplates?.GetLNodeType(ln.LnType);
|
|
|
|
if (lnType == null)
|
|
AddIssue(ln.xmlNode, "ERROR", "Model integrity", "LNType \"" + ln.LnType + "\" of LN \"" + logicalNode.ObjRef + "\" is not defined", this, "LNType");
|
|
else
|
|
{
|
|
lnType.IsUsed = true;
|
|
|
|
if (logicalNode.DataObjects.Count > 0)
|
|
logicalNode.DataObjects.Clear();
|
|
|
|
foreach (SclDataObjectDefinition doDef in lnType.DataObjects)
|
|
{
|
|
|
|
SclDOType doType = DataTypeTemplates?.GetDOType(doDef.Type);
|
|
|
|
if (doType == null)
|
|
AddIssue(doDef.XmlNode, "ERROR", "Model integrity", "DOType \"" + doDef.Type + "\" of DO \"" + doDef.Name + "\" is not defined", this, "DOType");
|
|
else
|
|
{
|
|
doType.IsUsed = true;
|
|
|
|
DataObject dobject = new DataObject(doDef, doType, logicalNode);
|
|
AddSubDataObjectsAndDataAttributesToDataObject(dobject, doType);
|
|
|
|
logicalNode.DataObjects.Add(dobject);
|
|
}
|
|
|
|
|
|
}
|
|
}
|
|
|
|
|
|
}
|
|
|
|
private void AddLogicalNodesToLogicalDevice(LogicalDevice logicalDevice, SclLDevice lDevice)
|
|
{
|
|
if (lDevice.LogicalNodes != null)
|
|
{
|
|
foreach (SclLN ln in lDevice.LogicalNodes)
|
|
{
|
|
|
|
LogicalNode logicalNode = new LogicalNode(ln, logicalDevice);
|
|
|
|
AddDataObjectsToLogicalNode(logicalNode, ln);
|
|
|
|
logicalDevice.LogicalNodes.Add(logicalNode);
|
|
|
|
CheckIfControlExistForDOIandDAI(logicalNode);
|
|
|
|
//CheckIfFcdaExistsOnDataTemplates(logicalNode);
|
|
|
|
if (logicalNode.LnClass == "LLN0")
|
|
CheckIf_LLN0NamPltconfigRevExist(logicalNode);
|
|
|
|
CheckDANameSpaces(logicalNode);
|
|
}
|
|
}
|
|
}
|
|
|
|
private List<string> NSDsNameSpace = new List<string>() { "IEC_61850-7-4", "IEC_61850-7-4_2003", "IEC_61850-7-4_2007","IEC_61850-7-4_2007A",
|
|
"IEC_61850-7-4_2007B","IEC_61850-7-2_2007A3", "IEC_61850-7-2_2007B3", "IEC_61850-7-3_2007A3", "IEC_61850-7-3_2007B3",
|
|
"IEC_61850-7-4_2007A3", "IEC_61850-7-4_2007B3", "IEC_61850-7-420_2019A4", "IEC_61850-7-420_2019A", "IEC_61850-8-1_2003A2"};
|
|
|
|
private void CheckDANameSpaces(LogicalNode logicalNode)
|
|
{
|
|
string ldNsValue = null;
|
|
bool ldNsFound = false;
|
|
string lnn0NsValue = null;
|
|
bool lnn0Found = false;
|
|
string lnNsValue = null;
|
|
bool lnNsFound = false;
|
|
LogicalDevice logicalDevice = logicalNode.Parent as LogicalDevice;
|
|
|
|
if (logicalNode.LnClass == "LLN0")
|
|
{
|
|
SclDOI sclDOI = logicalNode.SclElement.DOIs.Find(x => x.Name == "NamPlt");
|
|
|
|
if (sclDOI != null)
|
|
{
|
|
SclDAI sclDAIldNs = sclDOI.SclDAIs.Find(x => x.Name == "ldNs");
|
|
|
|
if (sclDAIldNs != null)
|
|
{
|
|
ldNsFound = true;
|
|
string value = sclDAIldNs.Val;
|
|
|
|
if (value != null)
|
|
{
|
|
ldNsValue = value;
|
|
}
|
|
}
|
|
|
|
SclDAI sclDAIlnNs = sclDOI.SclDAIs.Find(x => x.Name == "lnNs");
|
|
|
|
if (sclDAIlnNs != null)
|
|
{
|
|
lnn0Found = true;
|
|
string value = sclDAIlnNs.Val;
|
|
|
|
if (value != null)
|
|
{
|
|
lnn0NsValue = value;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (ldNsValue == null)
|
|
{
|
|
DataObject dataObject = logicalNode.DataObjects.Find(x => x.Name == "NamPlt" && x.DOType.Cdc == "LPL");
|
|
|
|
if (dataObject != null)
|
|
{
|
|
SclDataAttributeDefinition daLdNs = dataObject.DOType.DataAttributes.Find(x => x.Name == "ldNs" && x.Fc == SclFC.EX);
|
|
|
|
if (daLdNs != null)
|
|
{
|
|
ldNsFound = true;
|
|
SclVal sclVal = daLdNs.GetVal();
|
|
if (sclVal != null)
|
|
{
|
|
ldNsValue = sclVal.Value;
|
|
}
|
|
|
|
}
|
|
}
|
|
}
|
|
|
|
if (ldNsValue != null)
|
|
{
|
|
try
|
|
{
|
|
logicalDevice.NameSpace = new Namespace(ldNsValue);
|
|
|
|
if (!NSDsNameSpace.Contains(logicalDevice.NameSpace.NsdPath))
|
|
{
|
|
logicalDevice.NameSpace.NsdPathFound = false;
|
|
AddIssue(logicalNode.SclElement.XmlNode, "WARNING", "Model integrity", "Namespace on " + logicalDevice.Name + "/" + logicalNode.Name + ".NamPlt.ldNs not found on NSD files. Value: " + ldNsValue, this, "LLN0.NamPlt.ldNs");
|
|
}
|
|
}
|
|
catch (Exception)
|
|
{
|
|
AddIssue(logicalNode.SclElement.XmlNode, "WARNING", "Model integrity", "Namespace on " + logicalDevice.Name + "/" + logicalNode.Name + ".NamPlt.ldNs not found on NSD files. Value: " + ldNsValue, this, "LLN0.NamPlt.ldNs");
|
|
}
|
|
}
|
|
|
|
if (ldNsValue == null && ldNsFound)
|
|
{
|
|
AddIssue(logicalNode.SclElement.XmlNode, "ERROR", "Model integrity", "Namespace on " + logicalDevice.Name + "/" + logicalNode.Name + ".NamPlt.ldNs has no value", this, "LLN0.NamPlt.ldNs");
|
|
}
|
|
|
|
if (lnn0NsValue == null)
|
|
{
|
|
DataObject dataObject = logicalNode.DataObjects.Find(x => x.Name == "NamPlt" && x.DOType.Cdc == "LPL");
|
|
|
|
if (dataObject != null)
|
|
{
|
|
SclDataAttributeDefinition daLnNs = dataObject.DOType.DataAttributes.Find(x => x.Name == "lnNs" && x.Fc == SclFC.EX);
|
|
|
|
if (daLnNs != null)
|
|
{
|
|
lnn0Found = true;
|
|
SclVal sclVal = daLnNs.GetVal();
|
|
|
|
if (sclVal != null)
|
|
{
|
|
lnn0NsValue = sclVal.Value;
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
|
|
}
|
|
|
|
if (lnn0NsValue != null)
|
|
{
|
|
try
|
|
{
|
|
|
|
logicalNode.NameSpace = new Namespace(lnn0NsValue);
|
|
|
|
if (!NSDsNameSpace.Contains(logicalNode.NameSpace.NsdPath))
|
|
{
|
|
logicalNode.NameSpace.NsdPathFound = false;
|
|
|
|
AddIssue(logicalNode.SclElement.XmlNode, "WARNING", "Model integrity", "Namespace on " + logicalDevice.Name + "/" + logicalNode.Name + ".NamPlt.lnNs not found on NSD files. Value: " + lnn0NsValue, this, "LLN0.NamPlt.lnNs");
|
|
}
|
|
}
|
|
catch (Exception)
|
|
{
|
|
AddIssue(logicalNode.SclElement.XmlNode, "WARNING", "Model integrity", "Namespace on " + logicalDevice.Name + "/" + logicalNode.Name + ".NamPlt.lnNs not found on NSD files. Value: " + lnn0NsValue, this, "LLN0.NamPlt.lnNs");
|
|
}
|
|
}
|
|
|
|
if (lnn0NsValue == null && lnn0Found)
|
|
{
|
|
AddIssue(logicalNode.SclElement.XmlNode, "ERROR", "Model integrity", "Namespace on " + logicalDevice.Name + "/" + logicalNode.Name + ".NamPlt.lnNs has no value", this, "LLN0.NamPlt.lnNs");
|
|
}
|
|
|
|
|
|
}
|
|
else
|
|
{
|
|
SclDOI sclDOI = logicalNode.SclElement.DOIs.Find(x => x.Name == "NamPlt");
|
|
|
|
if (sclDOI != null)
|
|
{
|
|
SclDAI sclDAIlnNs = sclDOI.SclDAIs.Find(x => x.Name == "lnNs");
|
|
|
|
if (sclDAIlnNs != null)
|
|
{
|
|
lnNsFound = true;
|
|
string value = sclDAIlnNs.Val;
|
|
|
|
if (value != null)
|
|
{
|
|
lnNsValue = value;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (lnNsValue == null)
|
|
{
|
|
DataObject dataObject = logicalNode.DataObjects.Find(x => x.Name == "NamPlt" && x.DOType.Cdc == "LPL");
|
|
|
|
if (dataObject != null)
|
|
{
|
|
//Attribute for logical device basic namespace (LNName.NamPlt.lnNs)
|
|
SclDataAttributeDefinition daLnNs = dataObject.DOType.DataAttributes.Find(x => x.Name == "lnNs" && x.Fc == SclFC.EX);
|
|
|
|
if (daLnNs != null)
|
|
{
|
|
lnNsFound = true;
|
|
SclVal sclVal = daLnNs.GetVal();
|
|
|
|
if (sclVal != null)
|
|
{
|
|
lnNsValue = sclVal.Value;
|
|
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (lnNsValue != null)
|
|
{
|
|
try
|
|
{
|
|
logicalNode.NameSpace = new Namespace(lnNsValue);
|
|
|
|
if (!NSDsNameSpace.Contains(logicalNode.NameSpace.NsdPath))
|
|
{
|
|
logicalNode.NameSpace.NsdPathFound = false;
|
|
|
|
AddIssue(logicalNode.SclElement.XmlNode, "WARNING", "Model integrity", "Namespace on " + logicalDevice.Name + "/" + logicalNode.Name + ".NamPlt.lnNs not found on NSD files. Value: " + lnNsValue, this, "LLN0.NamPlt.lnNs");
|
|
}
|
|
}
|
|
catch (Exception)
|
|
{
|
|
AddIssue(logicalNode.SclElement.XmlNode, "WARNING", "Model integrity", "Namespace on " + logicalDevice.Name + "/" + logicalNode.Name + ".NamPlt.lnNs not found on NSD files. Value: " + lnNsValue, this, "LLN0.NamPlt.lnNs");
|
|
}
|
|
}
|
|
|
|
if (lnNsValue == null && lnNsFound)
|
|
{
|
|
AddIssue(logicalNode.SclElement.XmlNode, "ERROR", "Model integrity", "Namespace on " + logicalDevice.Name + "/" + logicalNode.Name + ".NamPlt.lnNs has no value", this, "LLN0.NamPlt.ldNs");
|
|
}
|
|
}
|
|
}
|
|
|
|
private void CheckIf_LLN0NamPltconfigRevExist(LogicalNode LLNO)
|
|
{
|
|
bool LLN0NamPltconfigRev = false;
|
|
DataObject dataObject = LLNO.DataObjects.Find(x => x.Name == "NamPlt");
|
|
if (dataObject != null)
|
|
{
|
|
SclDataAttributeDefinition da = dataObject.DOType.DataAttributes.Find(x => x.Name == "configRev");
|
|
if (da != null)
|
|
{
|
|
LLN0NamPltconfigRev = true;
|
|
|
|
if (da.GetVal() != null)
|
|
{
|
|
LogicalDevice logicalDevice = LLNO.Parent as LogicalDevice;
|
|
AddIssue(LLNO.SclElement.XmlNode, "ERROR", "Model integrity", "DataAttribute LLN0.NamPlt.configRev found on LD " + logicalDevice.Inst + " but has not value", this, "LLN0.NamPlt.configRev");
|
|
}
|
|
else
|
|
{
|
|
LLN0NamPltconfigRev = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!LLN0NamPltconfigRev)
|
|
{
|
|
SclDOI sclDOI = LLNO.SclElement.DOIs.Find(x => x.Name == "NamPlt");
|
|
if (sclDOI != null)
|
|
{
|
|
SclDAI sclDAI = sclDOI.SclDAIs.Find(x => x.Name == "configRev");
|
|
if (sclDAI != null)
|
|
{
|
|
LLN0NamPltconfigRev = true;
|
|
|
|
if (sclDAI.Val == null)
|
|
{
|
|
LogicalDevice logicalDevice = LLNO.Parent as LogicalDevice;
|
|
AddIssue(LLNO.SclElement.XmlNode, "ERROR", "Model integrity", "DAI LLN0.NamPlt.configRev found on LD " + logicalDevice.Inst + " but has not value", this, "LLN0.NamPlt.configRev");
|
|
|
|
}
|
|
|
|
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!LLN0NamPltconfigRev)
|
|
{
|
|
LogicalDevice logicalDevice = LLNO.Parent as LogicalDevice;
|
|
AddIssue(LLNO.SclElement.XmlNode, "ERROR", "Model integrity", "DA or DAI LLN0.NamPlt.configRev not found on LD " + logicalDevice.Inst, this, "LLN0.NamPlt.configRev");
|
|
|
|
}
|
|
}
|
|
|
|
private void CheckIfFcdaExistsOnDataTemplates(LogicalNode logicalNode)
|
|
{
|
|
try
|
|
{
|
|
foreach (DataSet dataSet in logicalNode.DataSets)
|
|
{
|
|
foreach (SclFCDA sclFCDA in dataSet.SclDataSet.Fcdas)
|
|
{
|
|
LogicalDevice logicalDevice = logicalNode.Parent as LogicalDevice;
|
|
IEDDataModel iEDDataModel = logicalDevice.Parent as IEDDataModel;
|
|
LogicalDevice fcdaLD = iEDDataModel.LogicalDevices.Find(x => x.Inst == sclFCDA.LdInst);
|
|
|
|
if (fcdaLD == null)
|
|
{
|
|
AddIssue(sclFCDA.xmlNode, "ERROR", "Model integrity", "FCDA " + sclFCDA.GetObjectReference() + " on DataSet " + dataSet.ObjRef + " not found on data type templates", this, "FCDA");
|
|
break;
|
|
}
|
|
|
|
string lnName = "";
|
|
if (sclFCDA.Prefix != null)
|
|
lnName += sclFCDA.Prefix;
|
|
if (sclFCDA.LnClass != null)
|
|
lnName += sclFCDA.LnClass;
|
|
if (sclFCDA.LnInst != null)
|
|
lnName += sclFCDA.LnInst;
|
|
|
|
LogicalNode fcdaLN = fcdaLD.LogicalNodes.Find(x => x.Name == lnName);
|
|
if (fcdaLN == null)
|
|
{
|
|
AddIssue(sclFCDA.xmlNode, "ERROR", "Model integrity", "FCDA " + sclFCDA.GetObjectReference() + " on DataSet " + dataSet.ObjRef + " not found on data type templates", this, "FCDA");
|
|
break;
|
|
}
|
|
|
|
SclLNodeType sclLNodeType = dataTypeTemplates.GetLNodeType(fcdaLN.SclElement.LnType);
|
|
|
|
if (sclLNodeType != null)
|
|
{
|
|
|
|
SclDataObjectDefinition dataObject = sclLNodeType.DataObjects.Find(x => x.Name == sclFCDA.DoName);
|
|
|
|
if (dataObject == null)
|
|
{
|
|
AddIssue(sclFCDA.xmlNode, "ERROR", "Model integrity", "FCDA " + sclFCDA.GetObjectReference() + " on DataSet " + dataSet.ObjRef + " not found on data type templates", this, "FCDA");
|
|
}
|
|
else
|
|
{
|
|
SclDOType sclDoType = dataTypeTemplates.GetDOType(dataObject.Type);
|
|
|
|
if (sclDoType == null)
|
|
{
|
|
AddIssue(sclFCDA.xmlNode, "ERROR", "Model integrity", "FCDA " + sclFCDA.GetObjectReference() + " on DataSet " + dataSet.ObjRef + " not found on data type templates", this, "FCDA");
|
|
}
|
|
|
|
else
|
|
{
|
|
if (sclFCDA.DaName != null)
|
|
{
|
|
|
|
SclDataAttributeDefinition sclDataAttributeDefinition = GetSclDataAttributeDefinition(sclDoType, sclFCDA.DaName);
|
|
//SclDataAttributeDefinition sclDataAttributeDefinition = sclDoType.DataAttributes.Find(x => x.Name == sclFCDA.DaName);
|
|
|
|
if (sclDataAttributeDefinition == null)
|
|
{
|
|
AddIssue(sclFCDA.xmlNode, "ERROR", "Model integrity", "FCDA " + sclFCDA.GetObjectReference() + " on DataSet " + dataSet.ObjRef + " not found on data type templates", this, "FCDA");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
}
|
|
else
|
|
{
|
|
AddIssue(sclFCDA.xmlNode, "ERROR", "Model integrity", "FCDA " + sclFCDA.GetObjectReference() + " on DataSet " + dataSet.ObjRef + " not found on data type templates", this, "FCDA");
|
|
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
|
|
}
|
|
catch (Exception)
|
|
{
|
|
|
|
}
|
|
|
|
}
|
|
|
|
private SclDataAttributeDefinition GetSclDataAttributeDefinition(object parent, string name)
|
|
{
|
|
|
|
SclDataAttributeDefinition sclDataAttributeDefinition = null;
|
|
int index = name.IndexOf(".");
|
|
|
|
if (index > 0)
|
|
{
|
|
string ParentName = name.Substring(0, index);
|
|
|
|
SclDataAttributeDefinition parentDA;
|
|
if (parent is SclDOType doType)
|
|
{
|
|
parentDA = doType.DataAttributes.Find(x => x.Name == ParentName);
|
|
}
|
|
else
|
|
{
|
|
parentDA = (parent as SclDAType).SubDataAttributes.Find(x => x.Name == ParentName);
|
|
}
|
|
|
|
SclDAType daType = dataTypeTemplates.GetDAType(parentDA.Type);
|
|
|
|
string daName = name.Substring(index + 1);
|
|
|
|
return GetSclDataAttributeDefinition(daType, daName);
|
|
}
|
|
else
|
|
{
|
|
if (parent is SclDOType doType)
|
|
{
|
|
sclDataAttributeDefinition = doType.DataAttributes.Find(x => x.Name == name);
|
|
}
|
|
else
|
|
{
|
|
sclDataAttributeDefinition = (parent as SclDAType).SubDataAttributes.Find(x => x.Name == name);
|
|
}
|
|
|
|
|
|
}
|
|
|
|
return sclDataAttributeDefinition;
|
|
}
|
|
|
|
private void CheckIfControlExistForDOIandDAI(LogicalNode logicalNode)
|
|
{
|
|
foreach (SclDOI sclDOI_ in logicalNode.SclElement.DOIs)
|
|
CheckPredefinedValuesExiste(sclDOI_, logicalNode, null, null, null);
|
|
}
|
|
|
|
private DataAttribute GetDataAttribute(LogicalNode logicalNode, SclDOI sclDOI, SclDAI sclDAI)
|
|
{
|
|
DataAttribute dataAttribute = null;
|
|
foreach (DataObject dataObject in logicalNode.DataObjects)
|
|
{
|
|
if (dataObject.Name == sclDOI.Name)
|
|
{
|
|
foreach (DataAttribute dataAttribute_ in GetDataAttribute(dataObject))
|
|
{
|
|
if (dataAttribute_.Name == sclDAI.Name)
|
|
{
|
|
dataAttribute = dataAttribute_;
|
|
break;
|
|
}
|
|
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
return dataAttribute;
|
|
}
|
|
|
|
private List<DataAttribute> GetDataAttribute(object Object)
|
|
{
|
|
List<DataAttribute> dataAttributes = new List<DataAttribute>();
|
|
|
|
if (Object is DataObject)
|
|
{
|
|
DataObject dataObject = Object as DataObject;
|
|
|
|
foreach (DataObjectOrAttribute DataObjectOrAttribute in dataObject.DataObjectsAndAttributes)
|
|
{
|
|
foreach (DataAttribute attribute in GetDataAttribute(DataObjectOrAttribute))
|
|
dataAttributes.Add(attribute);
|
|
}
|
|
|
|
}
|
|
else
|
|
{
|
|
DataAttribute dataAttribute_ = Object as DataAttribute;
|
|
|
|
if (dataAttribute_.SubDataAttributes != null)
|
|
{
|
|
foreach (DataAttribute subDataAttribute in dataAttribute_.SubDataAttributes)
|
|
{
|
|
foreach (DataAttribute attribute in GetDataAttribute(subDataAttribute))
|
|
dataAttributes.Add(attribute);
|
|
}
|
|
}
|
|
else
|
|
dataAttributes.Add(dataAttribute_);
|
|
}
|
|
|
|
return dataAttributes;
|
|
}
|
|
|
|
private readonly List<AttributeType> attributeTypeVisibleString = new List<AttributeType>
|
|
{
|
|
AttributeType.VISIBLE_STRING_32, AttributeType.VISIBLE_STRING_64,
|
|
AttributeType.VISIBLE_STRING_65,AttributeType.VISIBLE_STRING_129,AttributeType.VISIBLE_STRING_255
|
|
};
|
|
|
|
private readonly List<AttributeType> attributeTypeInteger = new List<AttributeType>
|
|
{
|
|
AttributeType.INT8, AttributeType.INT16,
|
|
AttributeType.INT32, AttributeType.INT64, AttributeType.INT8U, AttributeType.INT16U, AttributeType.INT24U, AttributeType.INT32U
|
|
};
|
|
|
|
private readonly List<AttributeType> attributeTypeFloat = new List<AttributeType>
|
|
{
|
|
AttributeType.FLOAT32,
|
|
AttributeType.FLOAT64
|
|
};
|
|
|
|
public bool IsBase64String(string value)
|
|
{
|
|
if (value == null || value.Length == 0 || value.Length % 4 != 0
|
|
|| value.Contains(' ') || value.Contains('\t') || value.Contains('\r') || value.Contains('\n'))
|
|
return false;
|
|
|
|
var index = value.Length - 1;
|
|
|
|
if (value[index] == '=')
|
|
index--;
|
|
if (value[index] == '=')
|
|
index--;
|
|
|
|
for (var i = 0; i <= index; i++)
|
|
if (IsInvalid(value[i]))
|
|
return false;
|
|
return true;
|
|
}
|
|
|
|
private bool IsInvalid(char value)
|
|
{
|
|
var intValue = (Int32)value;
|
|
if (intValue >= 48 && intValue <= 57)
|
|
return false;
|
|
if (intValue >= 65 && intValue <= 90)
|
|
return false;
|
|
if (intValue >= 97 && intValue <= 122)
|
|
return false;
|
|
return intValue != 43 && intValue != 47;
|
|
}
|
|
|
|
private bool CheckValidDaiValue(string value, AttributeType attributeType)
|
|
{
|
|
if (attributeTypeInteger.Contains(attributeType) || attributeTypeFloat.Contains(attributeType))
|
|
{
|
|
if (attributeType == AttributeType.INT8)
|
|
{
|
|
if (int.TryParse(value, out int i))
|
|
{
|
|
if (i >= -128 && i <= 127)
|
|
return true;
|
|
else
|
|
return false;
|
|
}
|
|
else
|
|
return false;
|
|
}
|
|
else if (attributeType == AttributeType.INT16)
|
|
{
|
|
return Int16.TryParse(value, out Int16 i);
|
|
}
|
|
else if (attributeType == AttributeType.INT32)
|
|
{
|
|
return Int32.TryParse(value, out Int32 i);
|
|
}
|
|
else if (attributeType == AttributeType.INT64)
|
|
{
|
|
return Int64.TryParse(value, out Int64 i);
|
|
}
|
|
else if (attributeType == AttributeType.INT8U)
|
|
{
|
|
if (int.TryParse(value, out int i))
|
|
{
|
|
if (i >= 0 && i <= 255)
|
|
return true;
|
|
else
|
|
return false;
|
|
}
|
|
else
|
|
return false;
|
|
|
|
}
|
|
else if (attributeType == AttributeType.INT16U)
|
|
{
|
|
return UInt16.TryParse(value, out UInt16 i);
|
|
}
|
|
else if (attributeType == AttributeType.INT32U)
|
|
{
|
|
return UInt32.TryParse(value, out UInt32 i);
|
|
}
|
|
else if (attributeType == AttributeType.FLOAT32 || attributeType == AttributeType.FLOAT64)
|
|
{
|
|
return float.TryParse(value, out float i);
|
|
}
|
|
else
|
|
return false;
|
|
|
|
}
|
|
|
|
else
|
|
{
|
|
if (attributeType == AttributeType.OCTET_STRING_64)
|
|
{
|
|
return IsBase64String(value);
|
|
}
|
|
else
|
|
return true;
|
|
|
|
}
|
|
}
|
|
|
|
|
|
private void CheckPredefinedValuesExiste(Object Object, LogicalNode logicalNode, SclDOI sclDOI_, string sclSdiNames, DataObject dataObject)
|
|
{
|
|
if (Object is SclDOI sclDOI)
|
|
{
|
|
if (sclDOI.SclDAIs.Count > 0)
|
|
{
|
|
foreach (SclDAI sclDAI in sclDOI.SclDAIs)
|
|
{
|
|
foreach (SclVal sclVal in sclDAI.GetValues())
|
|
{
|
|
DataAttribute dataAttribute = GetDataAttribute(logicalNode, sclDOI, sclDAI);
|
|
if (dataAttribute != null)
|
|
{
|
|
if (sclVal.Value != null)
|
|
{
|
|
if (dataAttribute.Definition.AttributeType == AttributeType.ENUMERATED)
|
|
{
|
|
|
|
string EnumType = dataAttribute.Definition.Type;
|
|
if (EnumType != null)
|
|
{
|
|
SclEnumType sclEnumType = DataTypeTemplates.GetEnumType(EnumType);
|
|
if (sclEnumType != null)
|
|
{
|
|
if (!sclEnumType.EnumValues.Exists(x => x.SymbolicName == sclVal.Value))
|
|
{
|
|
AddIssue(sclDAI.Node, "ERROR", "Model integrity", "Value " + sclVal.Value + " in DAI " + dataAttribute.ObjRef +
|
|
" does not exist in enumerated type " + EnumType + ". Reload the file after fixing the problem!", this, "DAI_DataAttributeMissing");
|
|
|
|
}
|
|
}
|
|
else
|
|
{
|
|
AddIssue(sclDAI.Node, "ERROR", "Model integrity", "Wrong ENUMERATED type " + EnumType + " in DAI " + dataAttribute.ObjRef
|
|
, this, "DAI_DataAttributeMissing");
|
|
}
|
|
|
|
}
|
|
else
|
|
{
|
|
AddIssue(sclDAI.Node, "ERROR", "Model integrity", "Wrong ENUMERATED type in DAI " + dataAttribute.ObjRef, this, "DAI_DataAttributeMissing");
|
|
}
|
|
|
|
}
|
|
else if (attributeTypeInteger.Contains(dataAttribute.Definition.AttributeType) ||
|
|
attributeTypeFloat.Contains(dataAttribute.Definition.AttributeType) || dataAttribute.Definition.AttributeType == AttributeType.OCTET_STRING_64)
|
|
{
|
|
if (!CheckValidDaiValue(sclVal.Value, dataAttribute.Definition.AttributeType))
|
|
{
|
|
AddIssue(sclDAI.Node, "ERROR", "Model integrity", "Value " + sclVal.Value + " in DAI " + dataAttribute.ObjRef +
|
|
" wrong for attribute type " + dataAttribute.Definition.AttributeType.ToString() + ". Reload the file after fixing the problem!", this, "DAI_DataAttributeMissing");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
else
|
|
{
|
|
AddIssue(sclDAI.Node, "ERROR", "Model integrity", "There is no DataAttribute definition for DAI " + sclDAI.Name + " on LogicalNode " + logicalNode.ObjRef, this, "DAI_DataAttributeMissing");
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
if (sclDOI.SclSDIs.Count > 0)
|
|
{
|
|
foreach (SclSDI sclSDI in sclDOI.SclSDIs)
|
|
CheckPredefinedValuesExiste(sclSDI, logicalNode, sclDOI, null, dataObject);
|
|
}
|
|
}
|
|
|
|
else if (Object is SclSDI)
|
|
{
|
|
SclSDI sclSDI = Object as SclSDI;
|
|
string name = null;
|
|
|
|
if (sclSdiNames != null)
|
|
{
|
|
name = sclSdiNames + "." + sclSDI.Name;
|
|
|
|
if (sclSDI.Ix != null)
|
|
name += "[" + sclSDI.Ix + "]";
|
|
}
|
|
|
|
else
|
|
{
|
|
name = sclSDI.Name;
|
|
|
|
if (sclSDI.Ix != null)
|
|
name += "[" + sclSDI.Ix + "]";
|
|
}
|
|
|
|
if (sclSDI.SclDAIs.Count > 0)
|
|
{
|
|
foreach (SclDAI sclDAI in sclSDI.SclDAIs)
|
|
{
|
|
foreach (SclVal sclVal in sclDAI.GetValues())
|
|
{
|
|
DataAttribute dataAttribute = GetDataAttribute(logicalNode, sclDOI_, sclDAI);
|
|
if (dataAttribute != null)
|
|
{
|
|
//TODO check if values is correct
|
|
}
|
|
else
|
|
{
|
|
AddIssue(sclDAI.Node, "ERROR", "Model integrity", "There is no SubDataAttribute for DAI " + name + " on LogicalNode " + logicalNode.ObjRef, this, "DAI_DataAttributeMissing");
|
|
}
|
|
}
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (sclSDI.SclSDIs.Count > 0)
|
|
{
|
|
foreach (SclSDI sclSDI_ in sclSDI.SclSDIs)
|
|
CheckPredefinedValuesExiste(sclSDI_, logicalNode, sclDOI_, name, dataObject);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
private IEDDataModel CreateDataModel(string iedName, SclAccessPoint ap)
|
|
{
|
|
IEDDataModel dataModel = null;
|
|
|
|
SclServer server = ap.Server;
|
|
|
|
dataModel = new IEDDataModel(iedName);
|
|
|
|
if (server != null)
|
|
{
|
|
List<SclLDevice> lDevices = server.LogicalDevices;
|
|
|
|
if (lDevices != null)
|
|
{
|
|
foreach (SclLDevice lDevice in lDevices)
|
|
{
|
|
string ldName = lDevice.LdName;
|
|
|
|
if (ldName == null)
|
|
ldName = iedName + lDevice.Inst;
|
|
|
|
LogicalDevice logicalDevice = new LogicalDevice(this, lDevice, ldName, lDevice.Inst, dataModel);
|
|
|
|
AddLogicalNodesToLogicalDevice(logicalDevice, lDevice);
|
|
|
|
dataModel.LogicalDevices.Add(logicalDevice);
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
return dataModel;
|
|
}
|
|
|
|
public IEDDataModel GetDataModel(string iedName, string accessPointName)
|
|
{
|
|
IEDDataModel dataModel = null;
|
|
|
|
if (iedName != null)
|
|
{
|
|
SclIED ied = GetIedByName(iedName);
|
|
|
|
if (ied != null)
|
|
{
|
|
foreach (SclAccessPoint ap in ied.AccessPoints)
|
|
{
|
|
|
|
if ((accessPointName == null) || ap.Name.Equals(accessPointName))
|
|
{
|
|
dataModel = CreateDataModel(iedName, ap);
|
|
|
|
if (dataModel != null)
|
|
{
|
|
foreach (LogicalDevice logicalDevice in dataModel.LogicalDevices)
|
|
{
|
|
foreach (LogicalNode logicalNode in logicalDevice.LogicalNodes)
|
|
CheckIfFcdaExistsOnDataTemplates(logicalNode);
|
|
}
|
|
}
|
|
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
return dataModel;
|
|
}
|
|
|
|
public SclConnectedAP GetConnectedAP(string apName, string iedName)
|
|
{
|
|
if (Communication != null)
|
|
return communication.GetConnectedAP(apName, iedName);
|
|
else
|
|
return null;
|
|
}
|
|
}
|
|
|
|
public class PositionXmlDocument : XmlDocument
|
|
{
|
|
IXmlLineInfo lineInfo; // a reference to the XmlReader, only set during load time
|
|
|
|
/// <summary>
|
|
/// Creates a PositionXmlElement.
|
|
/// </summary>
|
|
public override XmlElement CreateElement(string prefix, string localName, string namespaceURI)
|
|
{
|
|
return new PositionXmlElement(prefix, localName, namespaceURI, this, lineInfo);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Loads the XML document from the specified <see cref="XmlReader"/>.
|
|
/// </summary>
|
|
public override void Load(XmlReader reader)
|
|
{
|
|
lineInfo = reader as IXmlLineInfo;
|
|
try
|
|
{
|
|
base.Load(reader);
|
|
}
|
|
finally
|
|
{
|
|
lineInfo = null;
|
|
}
|
|
}
|
|
}
|
|
|
|
public class PositionXmlElement : XmlElement, IXmlLineInfo
|
|
{
|
|
internal PositionXmlElement(string prefix, string localName, string namespaceURI, XmlDocument doc, IXmlLineInfo lineInfo)
|
|
: base(prefix, localName, namespaceURI, doc)
|
|
{
|
|
if (lineInfo != null)
|
|
{
|
|
lineNumber = lineInfo.LineNumber;
|
|
linePosition = lineInfo.LinePosition;
|
|
hasLineInfo = true;
|
|
}
|
|
}
|
|
|
|
int lineNumber;
|
|
int linePosition;
|
|
bool hasLineInfo;
|
|
|
|
/// <summary>
|
|
/// Gets whether the element has line information.
|
|
/// </summary>
|
|
public bool HasLineInfo()
|
|
{
|
|
return hasLineInfo;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the line number.
|
|
/// </summary>
|
|
public int LineNumber
|
|
{
|
|
get { return lineNumber; }
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the line position (column).
|
|
/// </summary>
|
|
public int LinePosition
|
|
{
|
|
get { return linePosition; }
|
|
}
|
|
}
|
|
|
|
public class Namespace
|
|
{
|
|
private string namespaceIdentifier = null;
|
|
private string version = null;
|
|
private string revision = null;
|
|
private string release = null;
|
|
private string nsdPath = null;
|
|
private bool nsdPathFound = true;
|
|
|
|
public Namespace(string fullNamespaceName)
|
|
{
|
|
try
|
|
{
|
|
string namespaceIdentifier_ = null;
|
|
string version_ = null;
|
|
string revision_ = null;
|
|
string release_ = null;
|
|
string nsdPath_ = null;
|
|
|
|
if (fullNamespaceName != null)
|
|
{
|
|
int index = fullNamespaceName.IndexOf(":");
|
|
|
|
if (index != -1)
|
|
{
|
|
namespaceIdentifier_ = fullNamespaceName.Substring(0, index);
|
|
|
|
version_ = fullNamespaceName.Substring(index + 1, 4);
|
|
|
|
if ((index + 5) < fullNamespaceName.Length)
|
|
revision_ = fullNamespaceName.Substring(index + 5, 1);
|
|
|
|
if (revision_ == null)
|
|
revision_ = "A";
|
|
|
|
if ((index + 6) < fullNamespaceName.Length)
|
|
release_ = fullNamespaceName.Substring(index + 6, fullNamespaceName.Length - index - 6);
|
|
|
|
nsdPath_ = fullNamespaceName.Replace(" ", "_");
|
|
nsdPath_ = nsdPath_.Replace(":", "_");
|
|
}
|
|
|
|
if (namespaceIdentifier_ != null)
|
|
namespaceIdentifier = namespaceIdentifier_;
|
|
|
|
if (version_ != null)
|
|
version = version_;
|
|
|
|
if (revision_ != null)
|
|
revision = revision_;
|
|
|
|
if (release_ != null)
|
|
release = release_;
|
|
|
|
if (nsdPath_ != null)
|
|
nsdPath = nsdPath_;
|
|
|
|
}
|
|
}
|
|
catch (Exception)
|
|
{
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
public Namespace()
|
|
{
|
|
|
|
}
|
|
|
|
public string NamespaceIdentifier { get => namespaceIdentifier; set => namespaceIdentifier = value; }
|
|
public string Version { get => version; set => version = value; }
|
|
public string Revision { get => revision; set => revision = value; }
|
|
public string Release { get => release; set => release = value; }
|
|
public string NsdPath { get => nsdPath; set => nsdPath = value; }
|
|
public bool NsdPathFound { get => nsdPathFound; set => nsdPathFound = value; }
|
|
}
|
|
|
|
}
|
|
|