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.
libiec61850/tools/model_generator_dotnet/SCLParser/src/SclDocument.cs

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; }
}
}