diff --git a/src/main/java/com/beanit/iec61850bean/BdaTimestamp.java b/src/main/java/com/beanit/iec61850bean/BdaTimestamp.java index 3e328e9..6ec5d0b 100644 --- a/src/main/java/com/beanit/iec61850bean/BdaTimestamp.java +++ b/src/main/java/com/beanit/iec61850bean/BdaTimestamp.java @@ -17,226 +17,266 @@ import com.beanit.iec61850bean.internal.mms.asn1.Data; import com.beanit.iec61850bean.internal.mms.asn1.TypeDescription; import com.beanit.iec61850bean.internal.mms.asn1.UtcTime; + import java.time.Instant; import java.util.Date; public final class BdaTimestamp extends BasicDataAttribute { - private volatile byte[] value; - - public BdaTimestamp( - ObjectReference objectReference, Fc fc, String sAddr, boolean dchg, boolean dupd) { - super(objectReference, fc, sAddr, dchg, dupd); - basicType = BdaType.TIMESTAMP; - setDefault(); - } - - /** - * The SecondSinceEpoch shall be the interval in seconds continuously counted from the epoch - * 1970-01-01 00:00:00 UTC - */ - - /** - * Returns the value as the number of seconds since epoch 1970-01-01 00:00:00 UTC - * - * @return the number of seconds since epoch 1970-01-01 00:00:00 UTC - */ - private long getSecondsSinceEpoch() { - return ((0xffL & value[0]) << 24 - | (0xffL & value[1]) << 16 - | (0xffL & value[2]) << 8 - | (0xffL & value[3])); - } - - /** - * The attribute FractionOfSecond shall be the fraction of the current second when the value of - * the TimeStamp has been determined. The fraction of second shall be calculated as - * (SUM from I = 0 to 23 of bi*2**–(I+1) s). NOTE 1 The resolution is the smallest unit by - * which the time stamp is updated. The 24 bits of the integer provides 1 out of 16777216 counts - * as the smallest unit; calculated by 1/2**24 which equals approximately 60 ns. - * - *

NOTE 2 The resolution of a time stamp may be 1/2**1 (= 0,5 s) if only the first bit is used; - * or may be 1/2**2 (= 0,25 s) if the first two bits are used; or may be approximately 60 ns if - * all 24 bits are used. The resolution provided by an IED is outside the scope of this standard. - * - * @return the fraction of seconds - */ - private int getFractionOfSecond() { - return ((0xff & value[4]) << 16 | (0xff & value[5]) << 8 | (0xff & value[6])); - } - - @Override - public void setValueFrom(BasicDataAttribute bda) { - byte[] srcValue = ((BdaTimestamp) bda).getValue(); - if (value.length != srcValue.length) { - value = new byte[srcValue.length]; - } - System.arraycopy(srcValue, 0, value, 0, srcValue.length); - } - - public Instant getInstant() { - if (value == null || value.length == 0) { - return null; - } - long time = - getSecondsSinceEpoch() * 1000L - + (long) (((float) getFractionOfSecond()) / (1 << 24) * 1000 + 0.5); - return Instant.ofEpochMilli(time); - } - - public void setInstant(Instant instant) { - setInstant(instant, true, false, false, 10); - } - - public void setInstant( - Instant instant, - boolean leapSecondsKnown, - boolean clockFailure, - boolean clockNotSynchronized, - int timeAccuracy) { - if (value == null) { - value = new byte[8]; - } - - int secondsSinceEpoch = (int) (instant.toEpochMilli() / 1000L); - int fractionOfSecond = (int) ((instant.toEpochMilli() % 1000L) / 1000.0 * (1 << 24)); - - int timeQuality = timeAccuracy & 0x1f; - if (leapSecondsKnown) { - timeQuality = timeQuality | 0x80; - } - if (clockFailure) { - timeQuality = timeQuality | 0x40; - } - if (clockNotSynchronized) { - timeQuality = timeQuality | 0x20; - } - - value = - new byte[] { - (byte) ((secondsSinceEpoch >> 24) & 0xff), - (byte) ((secondsSinceEpoch >> 16) & 0xff), - (byte) ((secondsSinceEpoch >> 8) & 0xff), - (byte) (secondsSinceEpoch & 0xff), - (byte) ((fractionOfSecond >> 16) & 0xff), - (byte) ((fractionOfSecond >> 8) & 0xff), - (byte) (fractionOfSecond & 0xff), - (byte) timeQuality - }; - } - - public byte[] getValue() { - return value; - } - - public void setValue(byte[] value) { - if (value == null) { - this.value = new byte[8]; - } - this.value = value; - } - - /** - * The value TRUE of the attribute LeapSecondsKnown shall indicate that the value for - * SecondSinceEpoch takes into account all leap seconds occurred. If it is FALSE then the value - * does not take into account the leap seconds that occurred before the initialization of the time - * source of the device. - * - * @return TRUE of the attribute LeapSecondsKnown shall indicate that the value for - * SecondSinceEpoch takes into account all leap seconds occurred - */ - public boolean getLeapSecondsKnown() { - return ((value[7] & 0x80) != 0); - } - - /** - * The attribute clockFailure shall indicate that the time source of the sending device is - * unreliable. The value of the TimeStamp shall be ignored. - * - * @return true if the time source of the sending device is unreliable - */ - public boolean getClockFailure() { - return ((value[7] & 0x40) != 0); - } - - /** - * The attribute clockNotSynchronized shall indicate that the time source of the sending device is - * not synchronized with the external UTC time. - * - * @return true if the time source of the sending device is not synchronized - */ - public boolean getClockNotSynchronized() { - return ((value[7] & 0x20) != 0); - } - - /** - * The attribute TimeAccuracy shall represent the time accuracy class of the time source of the - * sending device relative to the external UTC time. The timeAccuracy classes shall represent the - * number of significant bits in the FractionOfSecond - * - *

If the time is set via Java {@link Date} objects, the accuracy is 1 ms, that is a - * timeAccuracy value of 10. - * - * @return the time accuracy - */ - public int getTimeAccuracy() { - return ((value[7] & 0x1f)); - } - - /** Sets Timestamp the empty byte array (indicating an invalid Timestamp) */ - @Override - public void setDefault() { - value = new byte[8]; - } - - /** Sets Timestamp to current time */ - public void setCurrentTime() { - setInstant(Instant.now()); - } - - @Override - public BdaTimestamp copy() { - BdaTimestamp copy = new BdaTimestamp(objectReference, fc, sAddr, dchg, dupd); - byte[] valueCopy = new byte[value.length]; - System.arraycopy(value, 0, valueCopy, 0, value.length); - copy.setValue(valueCopy); - if (mirror == null) { - copy.mirror = this; - } else { - copy.mirror = mirror; - } - return copy; - } - - @Override - Data getMmsDataObj() { - Data data = new Data(); - data.setUtcTime(new UtcTime(value)); - return data; - } - - @Override - void setValueFromMmsDataObj(Data data) throws ServiceError { - if (data.getUtcTime() == null) { - throw new ServiceError(ServiceError.TYPE_CONFLICT, "expected type: utc_time/timestamp"); - } - value = data.getUtcTime().value; - } - - @Override - TypeDescription getMmsTypeSpec() { - TypeDescription typeDescription = new TypeDescription(); - typeDescription.setUtcTime(new BerNull()); - return typeDescription; - } - - @Override - public String toString() { - return getReference().toString() + ": " + getInstant(); - } - - @Override - public String getValueString() { - return getInstant().toString(); - } + private volatile byte[] value; + + public BdaTimestamp( + ObjectReference objectReference, Fc fc, String sAddr, boolean dchg, boolean dupd) { + super(objectReference, fc, sAddr, dchg, dupd); + basicType = BdaType.TIMESTAMP; + setDefault(); + } + + /** + * The SecondSinceEpoch shall be the interval in seconds continuously counted from the epoch + * 1970-01-01 00:00:00 UTC + */ + + /** + * Returns the value as the number of seconds since epoch 1970-01-01 00:00:00 UTC + * + * @return the number of seconds since epoch 1970-01-01 00:00:00 UTC + */ + private long getSecondsSinceEpoch() { + return ((0xffL & value[0]) << 24 + | (0xffL & value[1]) << 16 + | (0xffL & value[2]) << 8 + | (0xffL & value[3])); + } + + /** + * The attribute FractionOfSecond shall be the fraction of the current second when the value of + * the TimeStamp has been determined. The fraction of second shall be calculated as + * (SUM from I = 0 to 23 of bi*2**–(I+1) s). NOTE 1 The resolution is the smallest unit by + * which the time stamp is updated. The 24 bits of the integer provides 1 out of 16777216 counts + * as the smallest unit; calculated by 1/2**24 which equals approximately 60 ns. + * + *

NOTE 2 The resolution of a time stamp may be 1/2**1 (= 0,5 s) if only the first bit is used; + * or may be 1/2**2 (= 0,25 s) if the first two bits are used; or may be approximately 60 ns if + * all 24 bits are used. The resolution provided by an IED is outside the scope of this standard. + * + * @return the fraction of seconds + */ + private int getFractionOfSecond() { + return ((0xff & value[4]) << 16 | (0xff & value[5]) << 8 | (0xff & value[6])); + } + + public Date getDate() { + if (value == null || value.length == 0) { + return null; + } + long time = + getSecondsSinceEpoch() * 1000L + + (long) (((float) getFractionOfSecond()) / (1 << 24) * 1000 + 0.5); + return new Date(time); + } + + public void setDate(Date date) { + if (value == null) { + value = new byte[8]; + } + + int secondsSinceEpoch = (int) (date.getTime() / 1000L); + int fractionOfSecond = (int) ((date.getTime() % 1000L) / 1000.0 * (1 << 24)); + + // 0x8a = time accuracy of 10 and LeapSecondsKnown = true, ClockFailure + // = false, ClockNotSynchronized = false + value = + new byte[] { + (byte) ((secondsSinceEpoch >> 24) & 0xff), + (byte) ((secondsSinceEpoch >> 16) & 0xff), + (byte) ((secondsSinceEpoch >> 8) & 0xff), + (byte) (secondsSinceEpoch & 0xff), + (byte) ((fractionOfSecond >> 16) & 0xff), + (byte) ((fractionOfSecond >> 8) & 0xff), + (byte) (fractionOfSecond & 0xff), + (byte) 0x8a + }; + } + + + + @Override + public void setValueFrom(BasicDataAttribute bda) { + byte[] srcValue = ((BdaTimestamp) bda).getValue(); + if (value.length != srcValue.length) { + value = new byte[srcValue.length]; + } + System.arraycopy(srcValue, 0, value, 0, srcValue.length); + } + + public Instant getInstant() { + if (value == null || value.length == 0) { + return null; + } + long time = + getSecondsSinceEpoch() * 1000L + + (long) (((float) getFractionOfSecond()) / (1 << 24) * 1000 + 0.5); + return Instant.ofEpochMilli(time); + } + + public void setInstant(Instant instant) { + setInstant(instant, true, false, false, 10); + } + + public void setInstant( + Instant instant, + boolean leapSecondsKnown, + boolean clockFailure, + boolean clockNotSynchronized, + int timeAccuracy) { + if (value == null) { + value = new byte[8]; + } + + int secondsSinceEpoch = (int) (instant.toEpochMilli() / 1000L); + int fractionOfSecond = (int) ((instant.toEpochMilli() % 1000L) / 1000.0 * (1 << 24)); + + int timeQuality = timeAccuracy & 0x1f; + if (leapSecondsKnown) { + timeQuality = timeQuality | 0x80; + } + if (clockFailure) { + timeQuality = timeQuality | 0x40; + } + if (clockNotSynchronized) { + timeQuality = timeQuality | 0x20; + } + + value = + new byte[]{ + (byte) ((secondsSinceEpoch >> 24) & 0xff), + (byte) ((secondsSinceEpoch >> 16) & 0xff), + (byte) ((secondsSinceEpoch >> 8) & 0xff), + (byte) (secondsSinceEpoch & 0xff), + (byte) ((fractionOfSecond >> 16) & 0xff), + (byte) ((fractionOfSecond >> 8) & 0xff), + (byte) (fractionOfSecond & 0xff), + (byte) timeQuality + }; + } + + public byte[] getValue() { + return value; + } + + public void setValue(byte[] value) { + if (value == null) { + this.value = new byte[8]; + } + this.value = value; + } + + /** + * The value TRUE of the attribute LeapSecondsKnown shall indicate that the value for + * SecondSinceEpoch takes into account all leap seconds occurred. If it is FALSE then the value + * does not take into account the leap seconds that occurred before the initialization of the time + * source of the device. + * + * @return TRUE of the attribute LeapSecondsKnown shall indicate that the value for + * SecondSinceEpoch takes into account all leap seconds occurred + */ + public boolean getLeapSecondsKnown() { + return ((value[7] & 0x80) != 0); + } + + /** + * The attribute clockFailure shall indicate that the time source of the sending device is + * unreliable. The value of the TimeStamp shall be ignored. + * + * @return true if the time source of the sending device is unreliable + */ + public boolean getClockFailure() { + return ((value[7] & 0x40) != 0); + } + + /** + * The attribute clockNotSynchronized shall indicate that the time source of the sending device is + * not synchronized with the external UTC time. + * + * @return true if the time source of the sending device is not synchronized + */ + public boolean getClockNotSynchronized() { + return ((value[7] & 0x20) != 0); + } + + /** + * The attribute TimeAccuracy shall represent the time accuracy class of the time source of the + * sending device relative to the external UTC time. The timeAccuracy classes shall represent the + * number of significant bits in the FractionOfSecond + * + *

If the time is set via Java {@link Date} objects, the accuracy is 1 ms, that is a + * timeAccuracy value of 10. + * + * @return the time accuracy + */ + public int getTimeAccuracy() { + return ((value[7] & 0x1f)); + } + + /** + * Sets Timestamp the empty byte array (indicating an invalid Timestamp) + */ + @Override + public void setDefault() { + value = new byte[8]; + } + + /** + * Sets Timestamp to current time + */ + public void setCurrentTime() { + setInstant(Instant.now()); + } + + @Override + public BdaTimestamp copy() { + BdaTimestamp copy = new BdaTimestamp(objectReference, fc, sAddr, dchg, dupd); + byte[] valueCopy = new byte[value.length]; + System.arraycopy(value, 0, valueCopy, 0, value.length); + copy.setValue(valueCopy); + if (mirror == null) { + copy.mirror = this; + } else { + copy.mirror = mirror; + } + return copy; + } + + @Override + Data getMmsDataObj() { + Data data = new Data(); + data.setUtcTime(new UtcTime(value)); + return data; + } + + @Override + void setValueFromMmsDataObj(Data data) throws ServiceError { + if (data.getUtcTime() == null) { + throw new ServiceError(ServiceError.TYPE_CONFLICT, "expected type: utc_time/timestamp"); + } + value = data.getUtcTime().value; + } + + @Override + TypeDescription getMmsTypeSpec() { + TypeDescription typeDescription = new TypeDescription(); + typeDescription.setUtcTime(new BerNull()); + return typeDescription; + } + + @Override + public String toString() { + return getReference().toString() + ": " + getInstant(); + } + + @Override + public String getValueString() { + return getInstant().toString(); + } } diff --git a/src/main/java/com/beanit/iec61850bean/ConnectionParam.java b/src/main/java/com/beanit/iec61850bean/ConnectionParam.java new file mode 100644 index 0000000..0e25626 --- /dev/null +++ b/src/main/java/com/beanit/iec61850bean/ConnectionParam.java @@ -0,0 +1,148 @@ +package com.beanit.iec61850bean; + +import java.util.Objects; + +public class ConnectionParam { + private String iedName; + + private String IP; + private String IP_SUBNET; + private String OSI_AP_Title; + private String OSI_AE_Qualifier; + private String OSI_PSEL; + private String OSI_SSEL; + private String OSI_TSEL; + private String IP_GATEWAY; + private String S_Profile; + private String MAC_Address; + + + public String getIedName() { + return iedName; + } + + public void setIedName(String iedName) { + this.iedName = iedName; + } + + public String getIP() { + return IP; + } + + public void setIP(String IP) { + this.IP = IP; + } + + public String getIP_SUBNET() { + return IP_SUBNET; + } + + public void setIP_SUBNET(String IP_SUBNET) { + this.IP_SUBNET = IP_SUBNET; + } + + public String getOSI_AP_Title() { + return OSI_AP_Title; + } + + public void setOSI_AP_Title(String OSI_AP_Title) { + this.OSI_AP_Title = OSI_AP_Title; + } + + public String getOSI_AE_Qualifier() { + return OSI_AE_Qualifier; + } + + public void setOSI_AE_Qualifier(String OSI_AE_Qualifier) { + this.OSI_AE_Qualifier = OSI_AE_Qualifier; + } + + public String getOSI_PSEL() { + return OSI_PSEL; + } + + public void setOSI_PSEL(String OSI_PSEL) { + this.OSI_PSEL = OSI_PSEL; + } + + public String getOSI_SSEL() { + return OSI_SSEL; + } + + public void setOSI_SSEL(String OSI_SSEL) { + this.OSI_SSEL = OSI_SSEL; + } + + public String getOSI_TSEL() { + return OSI_TSEL; + } + + public void setOSI_TSEL(String OSI_TSEL) { + this.OSI_TSEL = OSI_TSEL; + } + + public String getIP_GATEWAY() { + return IP_GATEWAY; + } + + public void setIP_GATEWAY(String IP_GATEWAY) { + this.IP_GATEWAY = IP_GATEWAY; + } + + public String getS_Profile() { + return S_Profile; + } + + public void setS_Profile(String s_Profile) { + S_Profile = s_Profile; + } + + public String getMAC_Address() { + return MAC_Address; + } + + public void setMAC_Address(String MAC_Address) { + this.MAC_Address = MAC_Address; + } + + + @Override + public String toString() { + return "iedName = " + iedName + '\n' + + "IP = " + IP + '\n' + + "IP_SUBNET = " + IP_SUBNET + '\n' + + "OSI_AP_Title = " + OSI_AP_Title + '\n' + + "OSI_AE_Qualifier = " + OSI_AE_Qualifier + '\n' + + "OSI_PSEL = " + OSI_PSEL + '\n' + + "OSI_SSEL = " + OSI_SSEL + '\n' + + "OSI_TSEL = " + OSI_TSEL + '\n' + + "IP_GATEWAY = " + IP_GATEWAY + '\n' + + "S_Profile = " + S_Profile + '\n' + + "MAC-Address = " + MAC_Address + ; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + ConnectionParam that = (ConnectionParam) o; + return iedName.equals(that.iedName) && + IP.equals(that.IP) && + Objects.equals(IP_SUBNET, that.IP_SUBNET) && + Objects.equals(OSI_AP_Title, that.OSI_AP_Title) && + Objects.equals(OSI_AE_Qualifier, that.OSI_AE_Qualifier) && + Objects.equals(OSI_PSEL, that.OSI_PSEL) && + Objects.equals(OSI_SSEL, that.OSI_SSEL) && + Objects.equals(OSI_TSEL, that.OSI_TSEL) && + Objects.equals(IP_GATEWAY, that.IP_GATEWAY) && + Objects.equals(S_Profile, that.S_Profile) && + Objects.equals(MAC_Address, that.MAC_Address); + } + + @Override + public int hashCode() { + return Objects.hash(iedName, IP, IP_SUBNET, OSI_AP_Title, OSI_AE_Qualifier, OSI_PSEL, OSI_SSEL, + OSI_TSEL, IP_GATEWAY, S_Profile, MAC_Address); + } +} \ No newline at end of file diff --git a/src/main/java/com/beanit/iec61850bean/FcDataObject.java b/src/main/java/com/beanit/iec61850bean/FcDataObject.java index 8b0cf5a..4f6b2e3 100644 --- a/src/main/java/com/beanit/iec61850bean/FcDataObject.java +++ b/src/main/java/com/beanit/iec61850bean/FcDataObject.java @@ -14,6 +14,7 @@ package com.beanit.iec61850bean; import com.beanit.iec61850bean.internal.mms.asn1.Data; + import java.util.ArrayList; import java.util.Iterator; import java.util.LinkedHashMap; @@ -30,66 +31,89 @@ */ public class FcDataObject extends FcModelNode { - public FcDataObject(ObjectReference objectReference, Fc fc, List children) { + private final List fcModelNodes; + private String doType; + private String cdc; - this.children = new LinkedHashMap<>((int) ((children.size() / 0.75) + 1)); - this.objectReference = objectReference; - for (ModelNode child : children) { - this.children.put(child.getReference().getName(), child); - child.setParent(this); - } - this.fc = fc; - } - - @Override - public FcDataObject copy() { - List childCopies = new ArrayList<>(children.size()); - for (ModelNode childNode : children.values()) { - childCopies.add((FcModelNode) childNode.copy()); + + public FcDataObject(ObjectReference objectReference, Fc fc, List children) { + this.fcModelNodes = children; + super.setDataAttributes(children); + this.children = new LinkedHashMap<>((int) ((children.size() / 0.75) + 1)); + this.objectReference = objectReference; + + for (ModelNode child : children) { + this.children.put(child.getReference().getName(), child); + child.setParent(this); + } + this.fc = fc; } - return new FcDataObject(objectReference, fc, childCopies); - } - - @Override - Data getMmsDataObj() { - Data.Structure dataStructure = new Data.Structure(); - List seq = dataStructure.getData(); - - for (ModelNode modelNode : getChildren()) { - Data child = modelNode.getMmsDataObj(); - if (child == null) { - throw new IllegalArgumentException( - "Unable to convert Child: " + modelNode.objectReference + " to MMS Data Object."); - } - seq.add(child); + + @Override + public FcDataObject copy() { + List childCopies = new ArrayList<>(children.size()); + for (ModelNode childNode : children.values()) { + childCopies.add((FcModelNode) childNode.copy()); + } + return new FcDataObject(objectReference, fc, childCopies); } - if (seq.size() == 0) { - throw new IllegalArgumentException( - "Converting ModelNode: " - + objectReference - + " to MMS Data Object resulted in Sequence of size zero."); + + @Override + Data getMmsDataObj() { + Data.Structure dataStructure = new Data.Structure(); + List seq = dataStructure.getData(); + + for (ModelNode modelNode : getChildren()) { + Data child = modelNode.getMmsDataObj(); + if (child == null) { + throw new IllegalArgumentException( + "Unable to convert Child: " + modelNode.objectReference + " to MMS Data Object."); + } + seq.add(child); + } + if (seq.size() == 0) { + throw new IllegalArgumentException( + "Converting ModelNode: " + + objectReference + + " to MMS Data Object resulted in Sequence of size zero."); + } + + Data data = new Data(); + data.setStructure(dataStructure); + + return data; } - Data data = new Data(); - data.setStructure(dataStructure); + @Override + void setValueFromMmsDataObj(Data data) throws ServiceError { + if (data.getStructure() == null) { + throw new ServiceError(ServiceError.TYPE_CONFLICT, "expected type: structure"); + } + if (data.getStructure().getData().size() != children.size()) { + throw new ServiceError( + ServiceError.TYPE_CONFLICT, + "expected type: structure with " + children.size() + " elements"); + } + + Iterator iterator = data.getStructure().getData().iterator(); + for (ModelNode child : children.values()) { + child.setValueFromMmsDataObj(iterator.next()); + } + } - return data; - } + public String getDoType() { + return doType; + } - @Override - void setValueFromMmsDataObj(Data data) throws ServiceError { - if (data.getStructure() == null) { - throw new ServiceError(ServiceError.TYPE_CONFLICT, "expected type: structure"); + public void setDoType(String doType) { + this.doType = doType; } - if (data.getStructure().getData().size() != children.size()) { - throw new ServiceError( - ServiceError.TYPE_CONFLICT, - "expected type: structure with " + children.size() + " elements"); + + public String getCdc() { + return cdc; } - Iterator iterator = data.getStructure().getData().iterator(); - for (ModelNode child : children.values()) { - child.setValueFromMmsDataObj(iterator.next()); + public void setCdc(String cdc) { + this.cdc = cdc; } - } } diff --git a/src/main/java/com/beanit/iec61850bean/FcModelNode.java b/src/main/java/com/beanit/iec61850bean/FcModelNode.java index e414979..1447f4d 100644 --- a/src/main/java/com/beanit/iec61850bean/FcModelNode.java +++ b/src/main/java/com/beanit/iec61850bean/FcModelNode.java @@ -13,240 +13,373 @@ */ package com.beanit.iec61850bean; -import static java.nio.charset.StandardCharsets.UTF_8; - -import com.beanit.iec61850bean.internal.mms.asn1.AlternateAccess; -import com.beanit.iec61850bean.internal.mms.asn1.AlternateAccessSelection; +import com.beanit.iec61850bean.internal.mms.asn1.*; import com.beanit.iec61850bean.internal.mms.asn1.AlternateAccessSelection.SelectAccess; import com.beanit.iec61850bean.internal.mms.asn1.AlternateAccessSelection.SelectAccess.Component; -import com.beanit.iec61850bean.internal.mms.asn1.BasicIdentifier; -import com.beanit.iec61850bean.internal.mms.asn1.Identifier; -import com.beanit.iec61850bean.internal.mms.asn1.ObjectName; -import com.beanit.iec61850bean.internal.mms.asn1.Unsigned32; -import com.beanit.iec61850bean.internal.mms.asn1.VariableDefs; -import com.beanit.iec61850bean.internal.mms.asn1.VariableSpecification; + import java.util.List; import java.util.Timer; import java.util.TimerTask; +import static java.nio.charset.StandardCharsets.UTF_8; + public abstract class FcModelNode extends ModelNode { - Fc fc; - private VariableDefs.SEQUENCE variableDef = null; - private ServerAssociation selected = null; - private TimerTask task = null; + Fc fc; + private VariableDefs.SEQUENCE variableDef = null; + private ServerAssociation selected = null; + private TimerTask task = null; + + private String daType; + private BdaType basicType = null; + private String sAddr; + private String daiVal; + private boolean dchg; + private boolean dupd; + private boolean qchg; + + private String bType; + private List bdas; + + private short valueShort; + private byte[] valueBytes; + private byte valueByte; + private boolean valueBoolean; + private int valueInt; + private long valueLong; + + public Fc getFc() { + return fc; + } - public Fc getFc() { - return fc; - } + boolean select(ServerAssociation association, Timer timer) { + if (selected != null) { + if (selected != association) { + return false; + } + } else { + selected = association; + association.selects.add(this); + } - boolean select(ServerAssociation association, Timer timer) { - if (selected != null) { - if (selected != association) { - return false; - } - } else { - selected = association; - association.selects.add(this); - } + ModelNode sboTimeoutNode = + association.serverModel.findModelNode(objectReference, Fc.CF).getChild("sboTimeout"); - ModelNode sboTimeoutNode = - association.serverModel.findModelNode(objectReference, Fc.CF).getChild("sboTimeout"); + if (sboTimeoutNode == null) { + return true; + } - if (sboTimeoutNode == null) { - return true; - } + long sboTimeout = ((BdaInt32U) sboTimeoutNode).getValue(); - long sboTimeout = ((BdaInt32U) sboTimeoutNode).getValue(); + if (sboTimeout == 0) { + return true; + } - if (sboTimeout == 0) { - return true; - } + class SelectResetTask extends TimerTask { + final ServerAssociation association; + + SelectResetTask(ServerAssociation association) { + this.association = association; + } + + @Override + public void run() { + synchronized (association.serverModel) { + if (task == this) { + task = null; + deselectAndRemove(association); + } + } + } + } - class SelectResetTask extends TimerTask { - ServerAssociation association; + if (task != null) { + task.cancel(); + } - SelectResetTask(ServerAssociation association) { - this.association = association; - } + task = new SelectResetTask(association); + timer.schedule(task, sboTimeout); + + return true; + } - @Override - public void run() { - synchronized (association.serverModel) { - if (task == this) { + void deselectAndRemove(ServerAssociation association) { + selected = null; + if (task != null) { + task.cancel(); task = null; - deselectAndRemove(association); - } } - } + association.selects.remove(this); } - if (task != null) { - task.cancel(); + void deselect() { + selected = null; + if (task != null) { + task.cancel(); + task = null; + } } - task = new SelectResetTask(association); - timer.schedule(task, sboTimeout); - - return true; - } - - void deselectAndRemove(ServerAssociation association) { - selected = null; - if (task != null) { - task.cancel(); - task = null; + boolean isSelected() { + return selected != null; } - association.selects.remove(this); - } - void deselect() { - selected = null; - if (task != null) { - task.cancel(); - task = null; + boolean isSelectedBy(ServerAssociation association) { + return selected == association; } - } - boolean isSelected() { - return selected != null; - } + VariableDefs.SEQUENCE getMmsVariableDef() { + + if (variableDef != null) { + return variableDef; + } - boolean isSelectedBy(ServerAssociation association) { - return selected == association; - } + AlternateAccess alternateAccess = null; - VariableDefs.SEQUENCE getMmsVariableDef() { + StringBuilder preArrayIndexItemId = new StringBuilder(objectReference.get(1)); + preArrayIndexItemId.append("$"); + preArrayIndexItemId.append(fc); - if (variableDef != null) { - return variableDef; - } + int arrayIndexPosition = objectReference.getArrayIndexPosition(); + if (arrayIndexPosition != -1) { - AlternateAccess alternateAccess = null; + for (int i = 2; i < arrayIndexPosition; i++) { + preArrayIndexItemId.append("$"); + preArrayIndexItemId.append(objectReference.get(i)); + } - StringBuilder preArrayIndexItemId = new StringBuilder(objectReference.get(1)); - preArrayIndexItemId.append("$"); - preArrayIndexItemId.append(fc); + alternateAccess = new AlternateAccess(); + List subSeqOfAlternateAccess = alternateAccess.getCHOICE(); + Unsigned32 indexBerInteger = + new Unsigned32(Integer.parseInt(objectReference.get(arrayIndexPosition))); - int arrayIndexPosition = objectReference.getArrayIndexPosition(); - if (arrayIndexPosition != -1) { + if (arrayIndexPosition < (objectReference.size() - 1)) { + // this reference points to a sub-node of an array element - for (int i = 2; i < arrayIndexPosition; i++) { - preArrayIndexItemId.append("$"); - preArrayIndexItemId.append(objectReference.get(i)); - } + StringBuilder postArrayIndexItemId = + new StringBuilder(objectReference.get(arrayIndexPosition + 1)); + + for (int i = (arrayIndexPosition + 2); i < objectReference.size(); i++) { + postArrayIndexItemId.append("$"); + postArrayIndexItemId.append(objectReference.get(i)); + } + + BasicIdentifier subIndexReference = + new BasicIdentifier(postArrayIndexItemId.toString().getBytes(UTF_8)); + + AlternateAccessSelection.SelectAccess subIndexReferenceSelectAccess = + new AlternateAccessSelection.SelectAccess(); + Component component = new Component(); + component.setBasic(subIndexReference); + subIndexReferenceSelectAccess.setComponent(component); + + AlternateAccessSelection subIndexReferenceAlternateAccessSelection = + new AlternateAccessSelection(); + subIndexReferenceAlternateAccessSelection.setSelectAccess(subIndexReferenceSelectAccess); + + AlternateAccess.CHOICE subIndexReferenceAlternateAccessSubChoice = + new AlternateAccess.CHOICE(); + subIndexReferenceAlternateAccessSubChoice.setUnnamed( + subIndexReferenceAlternateAccessSelection); + + AlternateAccess subIndexReferenceAlternateAccess = new AlternateAccess(); + + List subIndexReferenceAlternateAccessSubSeqOf = + subIndexReferenceAlternateAccess.getCHOICE(); + subIndexReferenceAlternateAccessSubSeqOf.add(subIndexReferenceAlternateAccessSubChoice); + + // set array index: + + AlternateAccessSelection.SelectAlternateAccess.AccessSelection indexAccessSelectionChoice = + new AlternateAccessSelection.SelectAlternateAccess.AccessSelection(); + indexAccessSelectionChoice.setIndex(indexBerInteger); + + AlternateAccessSelection.SelectAlternateAccess indexAndLowerReferenceSelectAlternateAccess = + new AlternateAccessSelection.SelectAlternateAccess(); + indexAndLowerReferenceSelectAlternateAccess.setAccessSelection(indexAccessSelectionChoice); + indexAndLowerReferenceSelectAlternateAccess.setAlternateAccess( + subIndexReferenceAlternateAccess); + + AlternateAccessSelection indexAndLowerReferenceAlternateAccessSelection = + new AlternateAccessSelection(); + indexAndLowerReferenceAlternateAccessSelection.setSelectAlternateAccess( + indexAndLowerReferenceSelectAlternateAccess); + + AlternateAccess.CHOICE indexAndLowerReferenceAlternateAccessChoice = + new AlternateAccess.CHOICE(); + indexAndLowerReferenceAlternateAccessChoice.setUnnamed( + indexAndLowerReferenceAlternateAccessSelection); + + subSeqOfAlternateAccess.add(indexAndLowerReferenceAlternateAccessChoice); + + } else { + SelectAccess selectAccess = new SelectAccess(); + selectAccess.setIndex(indexBerInteger); + + AlternateAccessSelection alternateAccessSelection = new AlternateAccessSelection(); + alternateAccessSelection.setSelectAccess(selectAccess); + + AlternateAccess.CHOICE alternateAccessChoice = new AlternateAccess.CHOICE(); + alternateAccessChoice.setUnnamed(alternateAccessSelection); + + subSeqOfAlternateAccess.add(alternateAccessChoice); + } - alternateAccess = new AlternateAccess(); - List subSeqOfAlternateAccess = alternateAccess.getCHOICE(); - Unsigned32 indexBerInteger = - new Unsigned32(Integer.parseInt(objectReference.get(arrayIndexPosition))); + } else { - if (arrayIndexPosition < (objectReference.size() - 1)) { - // this reference points to a sub-node of an array element + for (int i = 2; i < objectReference.size(); i++) { + preArrayIndexItemId.append("$"); + preArrayIndexItemId.append(objectReference.get(i)); + } + } + + ObjectName.DomainSpecific domainSpecificObjectName = new ObjectName.DomainSpecific(); + domainSpecificObjectName.setDomainID(new Identifier(objectReference.get(0).getBytes(UTF_8))); + domainSpecificObjectName.setItemID( + new Identifier(preArrayIndexItemId.toString().getBytes(UTF_8))); + + ObjectName objectName = new ObjectName(); + objectName.setDomainSpecific(domainSpecificObjectName); - StringBuilder postArrayIndexItemId = - new StringBuilder(objectReference.get(arrayIndexPosition + 1)); + VariableSpecification varSpec = new VariableSpecification(); + varSpec.setName(objectName); + + variableDef = new VariableDefs.SEQUENCE(); + variableDef.setAlternateAccess(alternateAccess); + variableDef.setVariableSpecification(varSpec); + + return variableDef; + } - for (int i = (arrayIndexPosition + 2); i < objectReference.size(); i++) { - postArrayIndexItemId.append("$"); - postArrayIndexItemId.append(objectReference.get(i)); + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append(getReference().toString()).append(" [").append(fc).append("]"); + for (ModelNode childNode : children.values()) { + sb.append("\n"); + sb.append(childNode.toString()); } + return sb.toString(); + } - BasicIdentifier subIndexReference = - new BasicIdentifier(postArrayIndexItemId.toString().getBytes(UTF_8)); + public String getDaType() { + return daType; + } - AlternateAccessSelection.SelectAccess subIndexReferenceSelectAccess = - new AlternateAccessSelection.SelectAccess(); - Component component = new Component(); - component.setBasic(subIndexReference); - subIndexReferenceSelectAccess.setComponent(component); + public void setDaType(String daType) { + this.daType = daType; + } + + public BdaType getBasicType() { + return basicType; + } + + public void setBasicType(BdaType basicType) { + this.basicType = basicType; + } + + public String getsAddr() { + return sAddr; + } + + public void setsAddr(String sAddr) { + this.sAddr = sAddr; + } + + public String getDaiVal() { + return daiVal; + } + + public void setDaiVal(String daiVal) { + this.daiVal = daiVal; + } - AlternateAccessSelection subIndexReferenceAlternateAccessSelection = - new AlternateAccessSelection(); - subIndexReferenceAlternateAccessSelection.setSelectAccess(subIndexReferenceSelectAccess); + public boolean isDchg() { + return dchg; + } - AlternateAccess.CHOICE subIndexReferenceAlternateAccessSubChoice = - new AlternateAccess.CHOICE(); - subIndexReferenceAlternateAccessSubChoice.setUnnamed( - subIndexReferenceAlternateAccessSelection); + public void setDchg(boolean dchg) { + this.dchg = dchg; + } - AlternateAccess subIndexReferenceAlternateAccess = new AlternateAccess(); + public boolean isDupd() { + return dupd; + } - List subIndexReferenceAlternateAccessSubSeqOf = - subIndexReferenceAlternateAccess.getCHOICE(); - subIndexReferenceAlternateAccessSubSeqOf.add(subIndexReferenceAlternateAccessSubChoice); + public void setDupd(boolean dupd) { + this.dupd = dupd; + } - // set array index: + public boolean isQchg() { + return qchg; + } - AlternateAccessSelection.SelectAlternateAccess.AccessSelection indexAccessSelectionChoice = - new AlternateAccessSelection.SelectAlternateAccess.AccessSelection(); - indexAccessSelectionChoice.setIndex(indexBerInteger); + public void setQchg(boolean qchg) { + this.qchg = qchg; + } - AlternateAccessSelection.SelectAlternateAccess indexAndLowerReferenceSelectAlternateAccess = - new AlternateAccessSelection.SelectAlternateAccess(); - indexAndLowerReferenceSelectAlternateAccess.setAccessSelection(indexAccessSelectionChoice); - indexAndLowerReferenceSelectAlternateAccess.setAlternateAccess( - subIndexReferenceAlternateAccess); + public String getbType() { + return bType; + } - AlternateAccessSelection indexAndLowerReferenceAlternateAccessSelection = - new AlternateAccessSelection(); - indexAndLowerReferenceAlternateAccessSelection.setSelectAlternateAccess( - indexAndLowerReferenceSelectAlternateAccess); + public void setbType(String bType) { + this.bType = bType; + } - AlternateAccess.CHOICE indexAndLowerReferenceAlternateAccessChoice = - new AlternateAccess.CHOICE(); - indexAndLowerReferenceAlternateAccessChoice.setUnnamed( - indexAndLowerReferenceAlternateAccessSelection); + public List getDataAttributes() { + return bdas; + } - subSeqOfAlternateAccess.add(indexAndLowerReferenceAlternateAccessChoice); + public FcModelNode setDataAttributes(List bdas) { + this.bdas = bdas; + return this; + } - } else { - SelectAccess selectAccess = new SelectAccess(); - selectAccess.setIndex(indexBerInteger); + public short getValueShort() { + return valueShort; + } - AlternateAccessSelection alternateAccessSelection = new AlternateAccessSelection(); - alternateAccessSelection.setSelectAccess(selectAccess); + public void setValueShort(short valueShort) { + this.valueShort = valueShort; + } - AlternateAccess.CHOICE alternateAccessChoice = new AlternateAccess.CHOICE(); - alternateAccessChoice.setUnnamed(alternateAccessSelection); + public byte[] getValueBytes() { + return valueBytes; + } - subSeqOfAlternateAccess.add(alternateAccessChoice); - } + public void setValueBytes(byte[] valueBytes) { + this.valueBytes = valueBytes; + } - } else { + public byte getValueByte() { + return valueByte; + } - for (int i = 2; i < objectReference.size(); i++) { - preArrayIndexItemId.append("$"); - preArrayIndexItemId.append(objectReference.get(i)); - } + public void setValueByte(byte valueByte) { + this.valueByte = valueByte; } - ObjectName.DomainSpecific domainSpecificObjectName = new ObjectName.DomainSpecific(); - domainSpecificObjectName.setDomainID(new Identifier(objectReference.get(0).getBytes(UTF_8))); - domainSpecificObjectName.setItemID( - new Identifier(preArrayIndexItemId.toString().getBytes(UTF_8))); + public boolean isValueBoolean() { + return valueBoolean; + } - ObjectName objectName = new ObjectName(); - objectName.setDomainSpecific(domainSpecificObjectName); + public void setValueBoolean(boolean valueBoolean) { + this.valueBoolean = valueBoolean; + } - VariableSpecification varSpec = new VariableSpecification(); - varSpec.setName(objectName); + public int getValueInt() { + return valueInt; + } - variableDef = new VariableDefs.SEQUENCE(); - variableDef.setAlternateAccess(alternateAccess); - variableDef.setVariableSpecification(varSpec); + public void setValueInt(int valueInt) { + this.valueInt = valueInt; + } - return variableDef; - } + public long getValueLong() { + return valueLong; + } - @Override - public String toString() { - StringBuilder sb = new StringBuilder(); - sb.append(getReference().toString()).append(" [").append(fc).append("]"); - for (ModelNode childNode : children.values()) { - sb.append("\n"); - sb.append(childNode.toString()); + public void setValueLong(long valueLong) { + this.valueLong = valueLong; } - return sb.toString(); - } } diff --git a/src/main/java/com/beanit/iec61850bean/LogicalDevice.java b/src/main/java/com/beanit/iec61850bean/LogicalDevice.java index c67bfe6..d46d6e6 100644 --- a/src/main/java/com/beanit/iec61850bean/LogicalDevice.java +++ b/src/main/java/com/beanit/iec61850bean/LogicalDevice.java @@ -19,7 +19,11 @@ public final class LogicalDevice extends ModelNode { + private final List logicalNodes; + private String ldInst; + public LogicalDevice(ObjectReference objectReference, List logicalNodes) { + this.logicalNodes = logicalNodes; children = new LinkedHashMap<>((int) ((logicalNodes.size() / 0.75) + 1)); this.objectReference = objectReference; for (LogicalNode logicalNode : logicalNodes) { @@ -36,4 +40,16 @@ public LogicalDevice copy() { } return new LogicalDevice(objectReference, childCopies); } + + public List getLogicalNodes() { + return logicalNodes; + } + + public String getLdInst() { + return ldInst; + } + + public void setLdInst(String ldInst) { + this.ldInst = ldInst; + } } diff --git a/src/main/java/com/beanit/iec61850bean/LogicalNode.java b/src/main/java/com/beanit/iec61850bean/LogicalNode.java index c5f9e2a..38393f7 100644 --- a/src/main/java/com/beanit/iec61850bean/LogicalNode.java +++ b/src/main/java/com/beanit/iec61850bean/LogicalNode.java @@ -28,7 +28,14 @@ public final class LogicalNode extends ModelNode { private final Map urcbs = new HashMap<>(); private final Map brcbs = new HashMap<>(); + private final List dataObjects; + private String prefix; + private String lnClass; + private String lnInst; + private String lnType; + public LogicalNode(ObjectReference objectReference, List fcDataObjects) { + this.dataObjects = fcDataObjects; children = new LinkedHashMap<>(); for (Fc fc : Fc.values()) { this.fcDataObjects.put(fc, new LinkedHashMap()); @@ -59,8 +66,7 @@ public LogicalNode copy() { dataObjectsCopy.add((FcDataObject) obj.copy()); } - LogicalNode copy = new LogicalNode(objectReference, dataObjectsCopy); - return copy; + return new LogicalNode(objectReference, dataObjectsCopy); } public List getChildren(Fc fc) { @@ -134,4 +140,36 @@ public String toString() { } return sb.toString(); } + + public String getPrefix() { + return prefix; + } + + public void setPrefix(String prefix) { + this.prefix = prefix; + } + + public String getLnClass() { + return lnClass; + } + + public void setLnClass(String lnClass) { + this.lnClass = lnClass; + } + + public String getLnInst() { + return lnInst; + } + + public void setLnInst(String lnInst) { + this.lnInst = lnInst; + } + + public String getLnType() { + return lnType; + } + + public void setLnType(String lnType) { + this.lnType = lnType; + } } diff --git a/src/main/java/com/beanit/iec61850bean/Report.java b/src/main/java/com/beanit/iec61850bean/Report.java index 80f1c42..038cf29 100644 --- a/src/main/java/com/beanit/iec61850bean/Report.java +++ b/src/main/java/com/beanit/iec61850bean/Report.java @@ -14,6 +14,7 @@ package com.beanit.iec61850bean; import com.beanit.iec61850bean.internal.HexString; + import java.util.List; public class Report { @@ -172,7 +173,7 @@ public String toString() { sb.append("\nEntry ID: ").append(HexString.fromBytes(entryId.getValue())); } if (confRev != null) { - sb.append("\nConfiguration revision: ").append(confRev.toString()); + sb.append("\nConfiguration revision: ").append(confRev); } sb.append("\nReported data set members:"); int index = 0; diff --git a/src/main/java/com/beanit/iec61850bean/SclParser.java b/src/main/java/com/beanit/iec61850bean/SclParser.java index adc5a10..e5b94da 100644 --- a/src/main/java/com/beanit/iec61850bean/SclParser.java +++ b/src/main/java/com/beanit/iec61850bean/SclParser.java @@ -1,3 +1,4 @@ + /* * Copyright 2011 The IEC61850bean Authors * @@ -13,1098 +14,1267 @@ */ package com.beanit.iec61850bean; -import static java.nio.charset.StandardCharsets.UTF_8; - -import com.beanit.iec61850bean.internal.scl.AbstractDataAttribute; -import com.beanit.iec61850bean.internal.scl.Bda; -import com.beanit.iec61850bean.internal.scl.Da; -import com.beanit.iec61850bean.internal.scl.DaType; -import com.beanit.iec61850bean.internal.scl.Do; -import com.beanit.iec61850bean.internal.scl.DoType; -import com.beanit.iec61850bean.internal.scl.EnumType; -import com.beanit.iec61850bean.internal.scl.EnumVal; -import com.beanit.iec61850bean.internal.scl.LnSubDef; -import com.beanit.iec61850bean.internal.scl.LnType; -import com.beanit.iec61850bean.internal.scl.Sdo; -import com.beanit.iec61850bean.internal.scl.TypeDefinitions; -import java.io.FileInputStream; -import java.io.FileNotFoundException; -import java.io.InputStream; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; -import javax.xml.parsers.DocumentBuilderFactory; +import com.beanit.iec61850bean.internal.scl.*; import org.w3c.dom.Document; import org.w3c.dom.NamedNodeMap; import org.w3c.dom.Node; import org.w3c.dom.NodeList; -public class SclParser { - - private final Map dataSetsMap = new HashMap<>(); - private final List dataSetDefs = new ArrayList<>(); - private TypeDefinitions typeDefinitions; - private Document doc; - private String iedName; - private List serverModels = new ArrayList<>(); - private boolean useResvTmsAttributes = false; - - private SclParser() {} - - public static List parse(InputStream is) throws SclParseException { - SclParser sclParser = new SclParser(); - sclParser.parseStream(is); - return sclParser.serverModels; - } - - public static List parse(String sclFilePath) throws SclParseException { - try { - return parse(new FileInputStream(sclFilePath)); - } catch (FileNotFoundException e) { - throw new SclParseException(e); - } - } - - private void parseStream(InputStream icdFileStream) throws SclParseException { - - typeDefinitions = new TypeDefinitions(); - - DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); - factory.setIgnoringComments(true); +import javax.xml.parsers.DocumentBuilderFactory; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.InputStream; +import java.util.*; - try { - doc = factory.newDocumentBuilder().parse(icdFileStream); - } catch (Exception e) { - throw new SclParseException(e); - } +import static java.nio.charset.StandardCharsets.UTF_8; - Node rootNode = doc.getDocumentElement(); +public class SclParser { - if (!"SCL".equals(rootNode.getNodeName())) { - throw new SclParseException("Root node in SCL file is not of type \"SCL\""); + private final Map dataSetsMap = new HashMap<>(); + private final List dataSetDefs = new ArrayList<>(); + private TypeDefinitions typeDefinitions; + private Document doc; + private String iedName; + private final List serverModels = new ArrayList<>(); + private boolean useResvTmsAttributes = false; + + private String iedConnectedAP; + ConnectionParam connectionParm; + private final Map iedConnectionMap = new HashMap<>(); + private String iedManufacturer; + Map descMap; + + private List ldsInsts; + private List ldsRefs; + private Set lnSet; + private Map> lns; + private final HashSet lnS = new HashSet<>(); + private final Map cdcDOMap = new HashMap<>(); + private Map daiDescMap; + private List logicalDevices; + + private SclParser() { } - readTypeDefinitions(); - - NodeList iedList = doc.getElementsByTagName("IED"); - if (iedList.getLength() == 0) { - throw new SclParseException("No IED section found!"); + public static List parse(InputStream is) throws SclParseException { + SclParser sclParser = new SclParser(); + sclParser.parseStream(is); + return sclParser.serverModels; } - for (int z = 0; z < iedList.getLength(); z++) { - Node iedNode = iedList.item(z); - - useResvTmsAttributes = false; - - Node nameAttribute = iedNode.getAttributes().getNamedItem("name"); - - iedName = nameAttribute.getNodeValue(); - if ((iedName == null) || (iedName.length() == 0)) { - throw new SclParseException("IED must have a name!"); - } - - NodeList iedElements = iedNode.getChildNodes(); - - for (int i = 0; i < iedElements.getLength(); i++) { - Node element = iedElements.item(i); - String nodeName = element.getNodeName(); - if ("AccessPoint".equals(nodeName)) { - ServerSap serverSap = createAccessPoint(element); - if (serverSap != null) { - serverModels.add(serverSap.serverModel); - } - } else if ("Services".equals(nodeName)) { - NodeList servicesElements = element.getChildNodes(); - for (int j = 0; j < servicesElements.getLength(); j++) { - if ("ReportSettings".equals(servicesElements.item(j).getNodeName())) { - Node resvTmsAttribute = - servicesElements.item(j).getAttributes().getNamedItem("resvTms"); - if (resvTmsAttribute != null) { - useResvTmsAttributes = resvTmsAttribute.getNodeValue().equalsIgnoreCase("true"); - } - } - } + public static List parse(String sclFilePath) throws SclParseException { + try { + return parse(new FileInputStream(sclFilePath)); + } catch (FileNotFoundException e) { + throw new SclParseException(e); } - } } - } - private void readTypeDefinitions() throws SclParseException { + private void parseStream(InputStream icdFileStream) throws SclParseException { - NodeList dttSections = doc.getElementsByTagName("DataTypeTemplates"); + typeDefinitions = new TypeDefinitions(); - if (dttSections.getLength() != 1) { - throw new SclParseException("Only one DataTypeSection allowed"); - } + DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); + factory.setIgnoringComments(true); - Node dtt = dttSections.item(0); + try { + doc = factory.newDocumentBuilder().parse(icdFileStream); + } catch (Exception e) { + throw new SclParseException(e); + } + + Node rootNode = doc.getDocumentElement(); - NodeList dataTypes = dtt.getChildNodes(); + if (!"SCL".equals(rootNode.getNodeName())) { + throw new SclParseException("Root node in SCL file is not of type \"SCL\""); + } - for (int i = 0; i < dataTypes.getLength(); i++) { - Node element = dataTypes.item(i); + readTypeDefinitions(); - String nodeName = element.getNodeName(); + NodeList connectedAPs = doc.getElementsByTagName("ConnectedAP"); - if (nodeName.equals("LNodeType")) { - typeDefinitions.putLNodeType(new LnType(element)); - } else if (nodeName.equals("DOType")) { - typeDefinitions.putDOType(new DoType(element)); - } else if (nodeName.equals("DAType")) { - typeDefinitions.putDAType(new DaType(element)); - } else if (nodeName.equals("EnumType")) { - typeDefinitions.putEnumType(new EnumType(element)); - } - } - } + for (int zz = 0; zz < connectedAPs.getLength(); zz++) { + Node connectedAP = connectedAPs.item(zz); + Node attributeName = connectedAP.getAttributes().getNamedItem("iedName"); + iedConnectedAP = attributeName.getNodeValue(); + connectionParm = new ConnectionParam(); + if ((iedConnectedAP == null) || (iedConnectedAP.length() == 0)) { + throw new SclParseException("ConnectedAP must has a name"); + } + NodeList elementsConnectedAP = connectedAP.getChildNodes(); + + for (int yy = 0; yy < elementsConnectedAP.getLength(); yy++) { + Node elementConnectedAP = elementsConnectedAP.item(yy); + String nodeAddress = elementConnectedAP.getNodeName(); + if (nodeAddress.equals("Address")) { + + NodeList address = elementConnectedAP.getChildNodes(); + for (int ii = 0; ii < address.getLength(); ii++) { + + Node elementAddress = address.item(ii); + + String nodeCheck = elementAddress.getNodeName(); + if ("P".equals(nodeCheck)) { + setConnectionParm(elementAddress); + } + } + connectionParm.setIedName(iedConnectedAP); + iedConnectionMap.put(iedConnectedAP, connectionParm); + } + } + } + + NodeList iedList = doc.getElementsByTagName("IED"); + if (iedList.getLength() == 0) { + throw new SclParseException("No IED section found!"); + } - private ServerSap createAccessPoint(Node iedServer) throws SclParseException { - ServerSap serverSap = null; + for (int z = 0; z < iedList.getLength(); z++) { + Node iedNode = iedList.item(z); - NodeList elements = iedServer.getChildNodes(); + useResvTmsAttributes = false; - for (int i = 0; i < elements.getLength(); i++) { - Node element = elements.item(i); + Node nameAttribute = iedNode.getAttributes().getNamedItem("name"); - if (element.getNodeName().equals("Server")) { + Node manufacturerAttr = iedNode.getAttributes().getNamedItem("manufacturer"); + iedManufacturer = manufacturerAttr.getNodeValue(); - ServerModel server = createServerModel(element); + iedName = nameAttribute.getNodeValue(); + if ((iedName == null) || (iedName.length() == 0)) { + throw new SclParseException("IED must have a name!"); + } - Node namedItem = iedServer.getAttributes().getNamedItem("name"); - if (namedItem == null) { - throw new SclParseException("AccessPoint has no name attribute!"); + NodeList iedElements = iedNode.getChildNodes(); + + for (int i = 0; i < iedElements.getLength(); i++) { + Node element = iedElements.item(i); + String nodeName = element.getNodeName(); + if ("AccessPoint".equals(nodeName)) { + ServerSap serverSap = createAccessPoint(element); + if (serverSap != null) { + serverModels.add(serverSap.serverModel); + } + } else if ("Services".equals(nodeName)) { + NodeList servicesElements = element.getChildNodes(); + for (int j = 0; j < servicesElements.getLength(); j++) { + if ("ReportSettings".equals(servicesElements.item(j).getNodeName())) { + Node resvTmsAttribute = + servicesElements.item(j).getAttributes().getNamedItem("resvTms"); + if (resvTmsAttribute != null) { + useResvTmsAttributes = resvTmsAttribute.getNodeValue().equalsIgnoreCase("true"); + } + } + } + } + } } - // TODO save this name? - serverSap = new ServerSap(102, 0, null, server, null); + } - break; - } + public void setConnectionParm(Node P) { + String nodeValue; + + P.getAttributes().getNamedItem("type"); + String typeValue = P.getAttributes().getNamedItem("type").getNodeValue(); + + nodeValue = P.getTextContent(); + + switch (typeValue) { + case "IP": + connectionParm.setIP(nodeValue); + break; + case "IP-SUBNET": + connectionParm.setIP_SUBNET(nodeValue); + break; + case "OSI-AP-Title": + connectionParm.setOSI_AP_Title(nodeValue); + break; + case "OSI-AE-Qualifier": + connectionParm.setOSI_AE_Qualifier(nodeValue); + break; + case "OSI-PSEL": + connectionParm.setOSI_PSEL(nodeValue); + break; + case "OSI-SSEL": + connectionParm.setOSI_SSEL(nodeValue); + break; + case "OSI-TSEL": + connectionParm.setOSI_TSEL(nodeValue); + break; + case "IP-GATEWAY": + connectionParm.setIP_GATEWAY(nodeValue); + break; + case "S-Profile": + connectionParm.setS_Profile(nodeValue); + break; + case "MAC-Address": + connectionParm.setMAC_Address(nodeValue); + break; + } } - return serverSap; - } + private void readTypeDefinitions() throws SclParseException { - private ServerModel createServerModel(Node serverXMLNode) throws SclParseException { + NodeList dttSections = doc.getElementsByTagName("DataTypeTemplates"); - NodeList elements = serverXMLNode.getChildNodes(); - List logicalDevices = new ArrayList<>(elements.getLength()); + if (dttSections.getLength() != 1) { + throw new SclParseException("Only one DataTypeSection allowed"); + } - for (int i = 0; i < elements.getLength(); i++) { - Node element = elements.item(i); + Node dtt = dttSections.item(0); - if (element.getNodeName().equals("LDevice")) { - logicalDevices.add(createNewLDevice(element)); - } - } + NodeList dataTypes = dtt.getChildNodes(); - ServerModel serverModel = new ServerModel(logicalDevices, null); + for (int i = 0; i < dataTypes.getLength(); i++) { + Node element = dataTypes.item(i); - dataSetsMap.clear(); + String nodeName = element.getNodeName(); - for (LnSubDef dataSetDef : dataSetDefs) { - DataSet dataSet = createDataSet(serverModel, dataSetDef.logicalNode, dataSetDef.defXmlNode); - dataSetsMap.put(dataSet.getReferenceStr(), dataSet); + switch (nodeName) { + case "LNodeType": + typeDefinitions.putLNodeType(new LnType(element)); + break; + case "DOType": + typeDefinitions.putDOType(new DoType(element)); + break; + case "DAType": + typeDefinitions.putDAType(new DaType(element)); + break; + case "EnumType": + typeDefinitions.putEnumType(new EnumType(element)); + break; + } + } } - serverModel.addDataSets(dataSetsMap.values()); + private ServerSap createAccessPoint(Node iedServer) throws SclParseException { + ServerSap serverSap = null; - dataSetDefs.clear(); + NodeList elements = iedServer.getChildNodes(); - return serverModel; - } + for (int i = 0; i < elements.getLength(); i++) { + Node element = elements.item(i); - private LogicalDevice createNewLDevice(Node ldXmlNode) throws SclParseException { + if (element.getNodeName().equals("Server")) { - String inst = null; - String ldName = null; + ldsInsts = new ArrayList<>(); + ldsRefs = new ArrayList<>(); + descMap = new HashMap<>(); + daiDescMap = new HashMap<>(); + logicalDevices = new ArrayList<>(); - NamedNodeMap attributes = ldXmlNode.getAttributes(); + ServerModel server = createServerModel(element); - for (int i = 0; i < attributes.getLength(); i++) { - Node node = attributes.item(i); - String nodeName = node.getNodeName(); + Node namedItem = iedServer.getAttributes().getNamedItem("name"); + if (namedItem == null) { + throw new SclParseException("AccessPoint has no name attribute!"); + } + // TODO save this name? + serverSap = new ServerSap(102, 0, null, server, null); - if (nodeName.equals("inst")) { - inst = node.getNodeValue(); - } else if (nodeName.equals("ldName")) { - ldName = node.getNodeValue(); - } - } + break; + } + } - if (inst == null) { - throw new SclParseException("Required attribute \"inst\" in logical device not found!"); + return serverSap; } - NodeList elements = ldXmlNode.getChildNodes(); - List logicalNodes = new ArrayList<>(); + private ServerModel createServerModel(Node serverXMLNode) throws SclParseException { - String ref; - if ((ldName != null) && (ldName.length() != 0)) { - ref = ldName; - } else { - ref = iedName + inst; - } + NodeList elements = serverXMLNode.getChildNodes(); + List logicalDevices = new ArrayList<>(elements.getLength()); - for (int i = 0; i < elements.getLength(); i++) { - Node element = elements.item(i); + for (int i = 0; i < elements.getLength(); i++) { + Node element = elements.item(i); - if (element.getNodeName().equals("LN") || element.getNodeName().equals("LN0")) { - logicalNodes.add(createNewLogicalNode(element, ref)); - } - } + if (element.getNodeName().equals("LDevice")) { + lns = new HashMap<>(); + logicalDevices.add(createNewLDevice(element)); + this.logicalDevices.add(createNewLDevice(element)); + } + } - LogicalDevice lDevice = new LogicalDevice(new ObjectReference(ref), logicalNodes); + ServerModel serverModel = new ServerModel(logicalDevices, null); - return lDevice; - } + dataSetsMap.clear(); - private LogicalNode createNewLogicalNode(Node lnXmlNode, String parentRef) - throws SclParseException { + for (LnSubDef dataSetDef : dataSetDefs) { + DataSet dataSet = createDataSet(serverModel, dataSetDef.logicalNode, dataSetDef.defXmlNode); + dataSetsMap.put(dataSet.getReferenceStr(), dataSet); + } - // attributes not needed: desc + serverModel.addDataSets(dataSetsMap.values()); - String inst = null; - String lnClass = null; - String lnType = null; - String prefix = ""; + dataSetDefs.clear(); - NamedNodeMap attributes = lnXmlNode.getAttributes(); + serverModel.setConnectionParam(iedConnectionMap.get(iedName)); + serverModel.setIedName(iedName); + serverModel.setIedManufacturer(iedManufacturer); + serverModel.setLns(lns); + serverModel.setLnS(lnS); + serverModel.setDos(cdcDOMap); - for (int i = 0; i < attributes.getLength(); i++) { - Node node = attributes.item(i); - String nodeName = node.getNodeName(); + serverModel.setLdsInsts(ldsInsts); + serverModel.setLdsRefs(ldsRefs); + serverModel.setDescriptions(descMap); + serverModel.setdAIDescriptions(daiDescMap); + serverModel.setLogicalDevices(logicalDevices); - if (nodeName.equals("inst")) { - inst = node.getNodeValue(); - } else if (nodeName.equals("lnType")) { - lnType = node.getNodeValue(); - } else if (nodeName.equals("lnClass")) { - lnClass = node.getNodeValue(); - } else if (nodeName.equals("prefix")) { - prefix = node.getNodeValue(); - } + return serverModel; } - if (inst == null) { - throw new SclParseException("Required attribute \"inst\" not found!"); - } - if (lnType == null) { - throw new SclParseException("Required attribute \"lnType\" not found!"); - } - if (lnClass == null) { - throw new SclParseException("Required attribute \"lnClass\" not found!"); - } + private LogicalDevice createNewLDevice(Node ldXmlNode) throws SclParseException { - String ref = parentRef + '/' + prefix + lnClass + inst; + String inst = null; + String ldName = null; - LnType lnTypeDef = typeDefinitions.getLNodeType(lnType); + NamedNodeMap attributes = ldXmlNode.getAttributes(); - List dataObjects = new ArrayList<>(); + for (int i = 0; i < attributes.getLength(); i++) { + Node node = attributes.item(i); + String nodeName = node.getNodeName(); - if (lnTypeDef == null) { - throw new SclParseException("LNType " + lnType + " not defined!"); - } - for (Do dobject : lnTypeDef.dos) { - - // look for DOI node with the name of the DO - Node doiNodeFound = null; - for (int i = 0; i < lnXmlNode.getChildNodes().getLength(); i++) { - Node childNode = lnXmlNode.getChildNodes().item(i); - if ("DOI".equals(childNode.getNodeName())) { - - NamedNodeMap doiAttributes = childNode.getAttributes(); - Node nameAttribute = doiAttributes.getNamedItem("name"); - if (nameAttribute != null && nameAttribute.getNodeValue().equals(dobject.getName())) { - doiNodeFound = childNode; - } + if (nodeName.equals("inst")) { + inst = node.getNodeValue(); + } else if (nodeName.equals("ldName")) { + ldName = node.getNodeValue(); + } } - } - dataObjects.addAll( - createFcDataObjects(dobject.getName(), ref, dobject.getType(), doiNodeFound)); - } + if (inst == null) { + throw new SclParseException("Required attribute \"inst\" in logical device not found!"); + } - // look for ReportControl - for (int i = 0; i < lnXmlNode.getChildNodes().getLength(); i++) { - Node childNode = lnXmlNode.getChildNodes().item(i); - if ("ReportControl".equals(childNode.getNodeName())) { - dataObjects.addAll(createReportControlBlocks(childNode, ref)); - } - } + ldsInsts.add(inst); - LogicalNode lNode = new LogicalNode(new ObjectReference(ref), dataObjects); + NodeList elements = ldXmlNode.getChildNodes(); + List logicalNodes = new ArrayList<>(); - // look for DataSet definitions - for (int i = 0; i < lnXmlNode.getChildNodes().getLength(); i++) { - Node childNode = lnXmlNode.getChildNodes().item(i); - if ("DataSet".equals(childNode.getNodeName())) { - dataSetDefs.add(new LnSubDef(childNode, lNode)); - } - } - return lNode; - } + String ref; + if ((ldName != null) && (ldName.length() != 0)) { + ref = ldName; + ldsRefs.add(ref); + } else { + ref = iedName + inst; + ldsRefs.add(ref); + } + lnSet = new HashSet<>(); - private DataSet createDataSet(ServerModel serverModel, LogicalNode lNode, Node dsXmlNode) - throws SclParseException { + for (int i = 0; i < elements.getLength(); i++) { + Node element = elements.item(i); - Node nameAttribute = dsXmlNode.getAttributes().getNamedItem("name"); - if (nameAttribute == null) { - throw new SclParseException("DataSet must have a name"); - } + if (element.getNodeName().equals("LN") || element.getNodeName().equals("LN0")) { + logicalNodes.add(createNewLogicalNode(element, ref)); + } + } + lns.put(ref, lnSet); - String name = nameAttribute.getNodeValue(); + LogicalDevice lDevice = new LogicalDevice(new ObjectReference(ref), logicalNodes); + lDevice.setLdInst(inst); - List dsMembers = new ArrayList<>(); + return lDevice; + } - for (int i = 0; i < dsXmlNode.getChildNodes().getLength(); i++) { - Node fcdaXmlNode = dsXmlNode.getChildNodes().item(i); - if ("FCDA".equals(fcdaXmlNode.getNodeName())) { + private LogicalNode createNewLogicalNode(Node lnXmlNode, String parentRef) + throws SclParseException { - // For the definition of FCDA see Table 22 part6 ed2 + // attributes not needed: desc - String ldInst = null; - String prefix = ""; + String inst = null; String lnClass = null; - String lnInst = ""; - String doName = ""; - String daName = ""; - Fc fc = null; - - NamedNodeMap attributes = fcdaXmlNode.getAttributes(); - - for (int j = 0; j < attributes.getLength(); j++) { - Node node = attributes.item(j); - String nodeName = node.getNodeName(); - - if (nodeName.equals("ldInst")) { - ldInst = node.getNodeValue(); - } else if (nodeName.equals("lnInst")) { - lnInst = node.getNodeValue(); - } else if (nodeName.equals("lnClass")) { - lnClass = node.getNodeValue(); - } else if (nodeName.equals("prefix")) { - prefix = node.getNodeValue(); - } else if (nodeName.equals("doName")) { - doName = node.getNodeValue(); - } else if (nodeName.equals("daName")) { - if (!node.getNodeValue().isEmpty()) { - daName = "." + node.getNodeValue(); - } - } else if (nodeName.equals("fc")) { - fc = Fc.fromString(node.getNodeValue()); - if (fc == null) { - throw new SclParseException("FCDA contains invalid FC: " + node.getNodeValue()); + String lnType = null; + String prefix = ""; + + NamedNodeMap attributes = lnXmlNode.getAttributes(); + + for (int i = 0; i < attributes.getLength(); i++) { + Node node = attributes.item(i); + String nodeName = node.getNodeName(); + + switch (nodeName) { + case "inst": + inst = node.getNodeValue(); + break; + case "lnType": + lnType = node.getNodeValue(); + break; + case "lnClass": + lnClass = node.getNodeValue(); + break; + case "prefix": + prefix = node.getNodeValue(); + break; } - } } - if (ldInst == null) { - throw new SclParseException( - "Required attribute \"ldInst\" not found in FCDA: " + nameAttribute + "!"); - } + lnSet.add(prefix + lnClass + inst); - if (lnClass == null) { - throw new SclParseException("Required attribute \"lnClass\" not found in FCDA!"); + if (inst == null) { + throw new SclParseException("Required attribute \"inst\" not found!"); } - if (fc == null) { - throw new SclParseException("Required attribute \"fc\" not found in FCDA!"); + if (lnType == null) { + throw new SclParseException("Required attribute \"lnType\" not found!"); } - if (!doName.isEmpty()) { - - String objectReference = - iedName + ldInst + "/" + prefix + lnClass + lnInst + "." + doName + daName; - - ModelNode fcdaNode = serverModel.findModelNode(objectReference, fc); - - if (fcdaNode == null) { - throw new SclParseException( - "Specified FCDA: " - + objectReference - + " in DataSet: " - + nameAttribute - + " not found in Model."); - } - dsMembers.add((FcModelNode) fcdaNode); - } else { - String objectReference = iedName + ldInst + "/" + prefix + lnClass + lnInst; - ModelNode logicalNode = serverModel.findModelNode(objectReference, null); - if (logicalNode == null) { - throw new SclParseException( - "Specified FCDA: " - + objectReference - + " in DataSet: " - + nameAttribute - + " not found in Model."); - } - List fcDataObjects = ((LogicalNode) logicalNode).getChildren(fc); - for (FcDataObject dataObj : fcDataObjects) { - dsMembers.add(dataObj); - } + if (lnClass == null) { + throw new SclParseException("Required attribute \"lnClass\" not found!"); } - } - } - DataSet dataSet = new DataSet(lNode.getReference().toString() + '.' + name, dsMembers, false); - return dataSet; - } + String ref = parentRef + '/' + prefix + lnClass + inst; + lnS.add(ref); - private List createReportControlBlocks(Node xmlNode, String parentRef) - throws SclParseException { + LnType lnTypeDef = typeDefinitions.getLNodeType(lnType); - Fc fc = Fc.RP; - NamedNodeMap rcbNodeAttributes = xmlNode.getAttributes(); - Node attribute = rcbNodeAttributes.getNamedItem("buffered"); - if (attribute != null && "true".equalsIgnoreCase(attribute.getNodeValue())) { - fc = Fc.BR; - } + List dataObjects = new ArrayList<>(); - Node nameAttribute = rcbNodeAttributes.getNamedItem("name"); - if (nameAttribute == null) { - throw new SclParseException("Report Control Block has no name attribute."); - } + if (lnTypeDef == null) { + throw new SclParseException("LNType " + lnType + " not defined!"); + } + for (Do dobject : lnTypeDef.dos) { + + // look for DOI node with the name of the DO + Node doiNodeFound = null; + for (int i = 0; i < lnXmlNode.getChildNodes().getLength(); i++) { + Node childNode = lnXmlNode.getChildNodes().item(i); + if ("DOI".equals(childNode.getNodeName())) { + + NamedNodeMap doiAttributes = childNode.getAttributes(); + Node nameAttribute = doiAttributes.getNamedItem("name"); + Node cdcAttribute = doiAttributes.getNamedItem("desc"); + + if (cdcAttribute != null) { + descMap.put(ref + "." + nameAttribute.getNodeValue(), cdcAttribute.getNodeValue()); + } + + if (nameAttribute != null && nameAttribute.getNodeValue().equals(dobject.getName())) { + doiNodeFound = childNode; + } + } + } - int maxInstances = 1; - for (int i = 0; i < xmlNode.getChildNodes().getLength(); i++) { - Node childNode = xmlNode.getChildNodes().item(i); - - if ("RptEnabled".equals(childNode.getNodeName())) { - Node rptEnabledMaxAttr = childNode.getAttributes().getNamedItem("max"); - if (rptEnabledMaxAttr != null) { - maxInstances = Integer.parseInt(rptEnabledMaxAttr.getNodeValue()); - if (maxInstances < 1 || maxInstances > 99) { - throw new SclParseException( - "Report Control Block max instances should be between 1 and 99 but is: " - + maxInstances); - } + dataObjects.addAll( + createFcDataObjects(dobject.getName(), ref, dobject.getType(), doiNodeFound, dobject.getType())); } - } - } - List rcbInstances = new ArrayList<>(maxInstances); + // look for ReportControl + for (int i = 0; i < lnXmlNode.getChildNodes().getLength(); i++) { + Node childNode = lnXmlNode.getChildNodes().item(i); + if ("ReportControl".equals(childNode.getNodeName())) { + dataObjects.addAll(createReportControlBlocks(childNode, ref)); + } + } - for (int z = 1; z <= maxInstances; z++) { + LogicalNode lNode = new LogicalNode(new ObjectReference(ref), dataObjects); - ObjectReference reportObjRef; + // look for DataSet definitions + for (int i = 0; i < lnXmlNode.getChildNodes().getLength(); i++) { + Node childNode = lnXmlNode.getChildNodes().item(i); + if ("DataSet".equals(childNode.getNodeName())) { + dataSetDefs.add(new LnSubDef(childNode, lNode)); + } + } - if (maxInstances == 1) { + lNode.setPrefix(prefix); + lNode.setLnClass(lnClass); + lNode.setLnInst(inst); + lNode.setLnType(lnType); - reportObjRef = new ObjectReference(parentRef + "." + nameAttribute.getNodeValue()); - } else { - reportObjRef = - new ObjectReference( - parentRef + "." + nameAttribute.getNodeValue() + String.format("%02d", z)); - } + return lNode; + } - BdaTriggerConditions trigOps = - new BdaTriggerConditions(new ObjectReference(reportObjRef + ".TrgOps"), fc); - BdaOptFlds optFields = new BdaOptFlds(new ObjectReference(reportObjRef + ".OptFlds"), fc); - for (int i = 0; i < xmlNode.getChildNodes().getLength(); i++) { - Node childNode = xmlNode.getChildNodes().item(i); - if (childNode.getNodeName().equals("TrgOps")) { + private DataSet createDataSet(ServerModel serverModel, LogicalNode lNode, Node dsXmlNode) + throws SclParseException { - NamedNodeMap attributes = childNode.getAttributes(); + Node nameAttribute = dsXmlNode.getAttributes().getNamedItem("name"); + if (nameAttribute == null) { + throw new SclParseException("DataSet must have a name"); + } - if (attributes != null) { - for (int j = 0; j < attributes.getLength(); j++) { - Node node = attributes.item(j); - String nodeName = node.getNodeName(); + String name = nameAttribute.getNodeValue(); + + List dsMembers = new ArrayList<>(); + + for (int i = 0; i < dsXmlNode.getChildNodes().getLength(); i++) { + Node fcdaXmlNode = dsXmlNode.getChildNodes().item(i); + if ("FCDA".equals(fcdaXmlNode.getNodeName())) { + + // For the definition of FCDA see Table 22 part6 ed2 + + String ldInst = null; + String prefix = ""; + String lnClass = null; + String lnInst = ""; + String doName = ""; + String daName = ""; + Fc fc = null; + + NamedNodeMap attributes = fcdaXmlNode.getAttributes(); + + for (int j = 0; j < attributes.getLength(); j++) { + Node node = attributes.item(j); + String nodeName = node.getNodeName(); + + switch (nodeName) { + case "ldInst": + ldInst = node.getNodeValue(); + break; + case "lnInst": + lnInst = node.getNodeValue(); + break; + case "lnClass": + lnClass = node.getNodeValue(); + break; + case "prefix": + prefix = node.getNodeValue(); + break; + case "doName": + doName = node.getNodeValue(); + break; + case "daName": + if (!node.getNodeValue().isEmpty()) { + daName = "." + node.getNodeValue(); + } + break; + case "fc": + fc = Fc.fromString(node.getNodeValue()); + if (fc == null) { + throw new SclParseException("FCDA contains invalid FC: " + node.getNodeValue()); + } + break; + } + } + + if (ldInst == null) { + throw new SclParseException( + "Required attribute \"ldInst\" not found in FCDA: " + nameAttribute + "!"); + } + + if (lnClass == null) { + throw new SclParseException("Required attribute \"lnClass\" not found in FCDA!"); + } + if (fc == null) { + throw new SclParseException("Required attribute \"fc\" not found in FCDA!"); + } + if (!doName.isEmpty()) { + + String objectReference = + iedName + ldInst + "/" + prefix + lnClass + lnInst + "." + doName + daName; + + ModelNode fcdaNode = serverModel.findModelNode(objectReference, fc); + + if (fcdaNode == null) { + throw new SclParseException( + "Specified FCDA: " + + objectReference + + " in DataSet: " + + nameAttribute + + " not found in Model."); + } + dsMembers.add((FcModelNode) fcdaNode); + } else { + String objectReference = iedName + ldInst + "/" + prefix + lnClass + lnInst; + ModelNode logicalNode = serverModel.findModelNode(objectReference, null); + if (logicalNode == null) { + throw new SclParseException( + "Specified FCDA: " + + objectReference + + " in DataSet: " + + nameAttribute + + " not found in Model."); + } + List fcDataObjects = ((LogicalNode) logicalNode).getChildren(fc); + for (FcDataObject dataObj : fcDataObjects) { + dsMembers.add(dataObj); + } + } + } + } - if ("dchg".equals(nodeName)) { - trigOps.setDataChange(node.getNodeValue().equalsIgnoreCase("true")); - } else if ("qchg".equals(nodeName)) { - trigOps.setQualityChange(node.getNodeValue().equalsIgnoreCase("true")); + return new DataSet(lNode.getReference().toString() + '.' + name, dsMembers, false); + } - } else if ("dupd".equals(nodeName)) { - trigOps.setDataUpdate(node.getNodeValue().equalsIgnoreCase("true")); + private List createReportControlBlocks(Node xmlNode, String parentRef) + throws SclParseException { - } else if ("period".equals(nodeName)) { - trigOps.setIntegrity(node.getNodeValue().equalsIgnoreCase("true")); + Fc fc = Fc.RP; + NamedNodeMap rcbNodeAttributes = xmlNode.getAttributes(); + Node attribute = rcbNodeAttributes.getNamedItem("buffered"); + if (attribute != null && "true".equalsIgnoreCase(attribute.getNodeValue())) { + fc = Fc.BR; + } - } else if ("gi".equals(nodeName)) { - trigOps.setGeneralInterrogation(node.getNodeValue().equalsIgnoreCase("true")); - } - } - } - } else if ("OptFields".equals(childNode.getNodeName())) { - - NamedNodeMap attributes = childNode.getAttributes(); - - if (attributes != null) { - for (int j = 0; j < attributes.getLength(); j++) { - - Node node = attributes.item(j); - String nodeName = node.getNodeName(); - - if ("seqNum".equals(nodeName)) { - optFields.setSequenceNumber(node.getNodeValue().equalsIgnoreCase("true")); - } else if ("timeStamp".equals(nodeName)) { - optFields.setReportTimestamp(node.getNodeValue().equalsIgnoreCase("true")); - - } else if ("reasonCode".equals(nodeName)) { - optFields.setReasonForInclusion(node.getNodeValue().equalsIgnoreCase("true")); - - } else if ("dataSet".equals(nodeName)) { - optFields.setDataSetName(node.getNodeValue().equalsIgnoreCase("true")); - - } - // not supported for now - // else if (nodeName.equals("dataRef")) { - // optFields.setDataReference(node.getNodeValue().equals("true")); - // - // } - else if (nodeName.equals("bufOvfl")) { - optFields.setBufferOverflow(node.getNodeValue().equalsIgnoreCase("true")); - - } else if (nodeName.equals("entryID")) { - optFields.setEntryId(node.getNodeValue().equalsIgnoreCase("true")); - } - // not supported for now: - // else if (nodeName.equals("configRef")) { - // optFields.setConfigRevision(node.getNodeValue().equals("true")); - // } - } - } - } else if ("RptEnabled".equals(childNode.getNodeName())) { - Node rptEnabledMaxAttr = childNode.getAttributes().getNamedItem("max"); - if (rptEnabledMaxAttr != null) { - maxInstances = Integer.parseInt(rptEnabledMaxAttr.getNodeValue()); - if (maxInstances < 1 || maxInstances > 99) { - throw new SclParseException( - "Report Control Block max instances should be between 1 and 99 but is: " - + maxInstances); - } - } + Node nameAttribute = rcbNodeAttributes.getNamedItem("name"); + if (nameAttribute == null) { + throw new SclParseException("Report Control Block has no name attribute."); } - } - - if (fc == Fc.RP) { - optFields.setEntryId(false); - optFields.setBufferOverflow(false); - } - - List children = new ArrayList<>(); - - BdaVisibleString rptId = - new BdaVisibleString( - new ObjectReference(reportObjRef.toString() + ".RptID"), fc, "", 129, false, false); - attribute = rcbNodeAttributes.getNamedItem("rptID"); - if (attribute != null) { - rptId.setValue(attribute.getNodeValue().getBytes(UTF_8)); - } else { - rptId.setValue(reportObjRef.toString()); - } - - children.add(rptId); - - children.add( - new BdaBoolean( - new ObjectReference(reportObjRef.toString() + ".RptEna"), fc, "", false, false)); - - if (fc == Fc.RP) { - children.add( - new BdaBoolean( - new ObjectReference(reportObjRef.toString() + ".Resv"), fc, "", false, false)); - } - - BdaVisibleString datSet = - new BdaVisibleString( - new ObjectReference(reportObjRef.toString() + ".DatSet"), fc, "", 129, false, false); - - attribute = xmlNode.getAttributes().getNamedItem("datSet"); - if (attribute != null) { - String nodeValue = attribute.getNodeValue(); - String dataSetName = parentRef + "$" + nodeValue; - datSet.setValue(dataSetName.getBytes(UTF_8)); - } - children.add(datSet); - - BdaInt32U confRef = - new BdaInt32U( - new ObjectReference(reportObjRef.toString() + ".ConfRev"), fc, "", false, false); - attribute = xmlNode.getAttributes().getNamedItem("confRev"); - if (attribute == null) { - throw new SclParseException( - "Report Control Block does not contain mandatory attribute confRev"); - } - confRef.setValue(Long.parseLong(attribute.getNodeValue())); - children.add(confRef); - - children.add(optFields); - - BdaInt32U bufTm = - new BdaInt32U( - new ObjectReference(reportObjRef.toString() + ".BufTm"), fc, "", false, false); - attribute = xmlNode.getAttributes().getNamedItem("bufTime"); - if (attribute != null) { - bufTm.setValue(Long.parseLong(attribute.getNodeValue())); - } - children.add(bufTm); - - children.add( - new BdaInt8U( - new ObjectReference(reportObjRef.toString() + ".SqNum"), fc, "", false, false)); - - children.add(trigOps); - - BdaInt32U intgPd = - new BdaInt32U( - new ObjectReference(reportObjRef.toString() + ".IntgPd"), fc, "", false, false); - attribute = xmlNode.getAttributes().getNamedItem("intgPd"); - if (attribute != null) { - intgPd.setValue(Long.parseLong(attribute.getNodeValue())); - } - children.add(intgPd); - - children.add( - new BdaBoolean( - new ObjectReference(reportObjRef.toString() + ".GI"), fc, "", false, false)); - - Rcb rcb = null; - - if (fc == Fc.BR) { - - children.add( - new BdaBoolean( - new ObjectReference(reportObjRef.toString() + ".PurgeBuf"), fc, "", false, false)); - - children.add( - new BdaOctetString( - new ObjectReference(reportObjRef.toString() + ".EntryID"), - fc, - "", - 8, - false, - false)); - - children.add( - new BdaEntryTime( - new ObjectReference(reportObjRef.toString() + ".TimeOfEntry"), - fc, - "", - false, - false)); - - if (useResvTmsAttributes) { - children.add( - new BdaInt16( - new ObjectReference(reportObjRef.toString() + ".ResvTms"), fc, "", false, false)); + + int maxInstances = 1; + for (int i = 0; i < xmlNode.getChildNodes().getLength(); i++) { + Node childNode = xmlNode.getChildNodes().item(i); + + if ("RptEnabled".equals(childNode.getNodeName())) { + Node rptEnabledMaxAttr = childNode.getAttributes().getNamedItem("max"); + if (rptEnabledMaxAttr != null) { + maxInstances = Integer.parseInt(rptEnabledMaxAttr.getNodeValue()); + if (maxInstances < 1 || maxInstances > 99) { + throw new SclParseException( + "Report Control Block max instances should be between 1 and 99 but is: " + + maxInstances); + } + } + } } - children.add( - new BdaOctetString( - new ObjectReference(reportObjRef.toString() + ".Owner"), fc, "", 64, false, false)); + List rcbInstances = new ArrayList<>(maxInstances); - rcb = new Brcb(reportObjRef, children); + for (int z = 1; z <= maxInstances; z++) { - } else { - children.add( - new BdaOctetString( - new ObjectReference(reportObjRef.toString() + ".Owner"), fc, "", 64, false, false)); + ObjectReference reportObjRef; - rcb = new Urcb(reportObjRef, children); - } + if (maxInstances == 1) { - rcbInstances.add(rcb); - } + reportObjRef = new ObjectReference(parentRef + "." + nameAttribute.getNodeValue()); + } else { + reportObjRef = + new ObjectReference( + parentRef + "." + nameAttribute.getNodeValue() + String.format("%02d", z)); + } - return rcbInstances; - } + BdaTriggerConditions trigOps = + new BdaTriggerConditions(new ObjectReference(reportObjRef + ".TrgOps"), fc); + BdaOptFlds optFields = new BdaOptFlds(new ObjectReference(reportObjRef + ".OptFlds"), fc); + for (int i = 0; i < xmlNode.getChildNodes().getLength(); i++) { + Node childNode = xmlNode.getChildNodes().item(i); + if (childNode.getNodeName().equals("TrgOps")) { + + NamedNodeMap attributes = childNode.getAttributes(); + + if (attributes != null) { + for (int j = 0; j < attributes.getLength(); j++) { + Node node = attributes.item(j); + String nodeName = node.getNodeName(); + + if ("dchg".equals(nodeName)) { + trigOps.setDataChange(node.getNodeValue().equalsIgnoreCase("true")); + } else if ("qchg".equals(nodeName)) { + trigOps.setQualityChange(node.getNodeValue().equalsIgnoreCase("true")); + + } else if ("dupd".equals(nodeName)) { + trigOps.setDataUpdate(node.getNodeValue().equalsIgnoreCase("true")); + + } else if ("period".equals(nodeName)) { + trigOps.setIntegrity(node.getNodeValue().equalsIgnoreCase("true")); + + } else if ("gi".equals(nodeName)) { + trigOps.setGeneralInterrogation(node.getNodeValue().equalsIgnoreCase("true")); + } + } + } + } else if ("OptFields".equals(childNode.getNodeName())) { + + NamedNodeMap attributes = childNode.getAttributes(); + + if (attributes != null) { + for (int j = 0; j < attributes.getLength(); j++) { + + Node node = attributes.item(j); + String nodeName = node.getNodeName(); + + if ("seqNum".equals(nodeName)) { + optFields.setSequenceNumber(node.getNodeValue().equalsIgnoreCase("true")); + } else if ("timeStamp".equals(nodeName)) { + optFields.setReportTimestamp(node.getNodeValue().equalsIgnoreCase("true")); + + } else if ("reasonCode".equals(nodeName)) { + optFields.setReasonForInclusion(node.getNodeValue().equalsIgnoreCase("true")); + + } else if ("dataSet".equals(nodeName)) { + optFields.setDataSetName(node.getNodeValue().equalsIgnoreCase("true")); + + } + // not supported for now + // else if (nodeName.equals("dataRef")) { + // optFields.setDataReference(node.getNodeValue().equals("true")); + // + // } + else if (nodeName.equals("bufOvfl")) { + optFields.setBufferOverflow(node.getNodeValue().equalsIgnoreCase("true")); + + } else if (nodeName.equals("entryID")) { + optFields.setEntryId(node.getNodeValue().equalsIgnoreCase("true")); + } + // not supported for now: + // else if (nodeName.equals("configRef")) { + // optFields.setConfigRevision(node.getNodeValue().equals("true")); + // } + } + } + } else if ("RptEnabled".equals(childNode.getNodeName())) { + Node rptEnabledMaxAttr = childNode.getAttributes().getNamedItem("max"); + if (rptEnabledMaxAttr != null) { + maxInstances = Integer.parseInt(rptEnabledMaxAttr.getNodeValue()); + if (maxInstances < 1 || maxInstances > 99) { + throw new SclParseException( + "Report Control Block max instances should be between 1 and 99 but is: " + + maxInstances); + } + } + } + } - private List createFcDataObjects( - String name, String parentRef, String doTypeID, Node doiNode) throws SclParseException { + if (fc == Fc.RP) { + optFields.setEntryId(false); + optFields.setBufferOverflow(false); + } - DoType doType = typeDefinitions.getDOType(doTypeID); + List children = new ArrayList<>(); - if (doType == null) { - throw new SclParseException("DO type " + doTypeID + " not defined!"); - } + BdaVisibleString rptId = + new BdaVisibleString( + new ObjectReference(reportObjRef + ".RptID"), fc, "", 129, false, false); + attribute = rcbNodeAttributes.getNamedItem("rptID"); + if (attribute != null) { + rptId.setValue(attribute.getNodeValue().getBytes(UTF_8)); + } else { + rptId.setValue(reportObjRef.toString()); + } - String ref = parentRef + '.' + name; + children.add(rptId); - List childNodes = new ArrayList<>(); + children.add( + new BdaBoolean( + new ObjectReference(reportObjRef + ".RptEna"), fc, "", false, false)); - for (Da dattr : doType.das) { + if (fc == Fc.RP) { + children.add( + new BdaBoolean( + new ObjectReference(reportObjRef + ".Resv"), fc, "", false, false)); + } - // look for DAI node with the name of the DA - Node iNodeFound = findINode(doiNode, dattr.getName()); + BdaVisibleString datSet = + new BdaVisibleString( + new ObjectReference(reportObjRef + ".DatSet"), fc, "", 129, false, false); - if (dattr.getCount() >= 1) { - childNodes.add(createArrayOfDataAttributes(ref + '.' + dattr.getName(), dattr, iNodeFound)); - } else { - childNodes.add( - createDataAttribute( - ref + '.' + dattr.getName(), - dattr.getFc(), - dattr, - iNodeFound, - false, - false, - false)); - } - } + attribute = xmlNode.getAttributes().getNamedItem("datSet"); + if (attribute != null) { + String nodeValue = attribute.getNodeValue(); + String dataSetName = parentRef + "$" + nodeValue; + datSet.setValue(dataSetName.getBytes(UTF_8)); + } + children.add(datSet); + + BdaInt32U confRef = + new BdaInt32U( + new ObjectReference(reportObjRef + ".ConfRev"), fc, "", false, false); + attribute = xmlNode.getAttributes().getNamedItem("confRev"); + if (attribute == null) { + throw new SclParseException( + "Report Control Block does not contain mandatory attribute confRev"); + } + confRef.setValue(Long.parseLong(attribute.getNodeValue())); + children.add(confRef); - for (Sdo sdo : doType.sdos) { + children.add(optFields); - // parsing Arrays of SubDataObjects is ignored for now because no SCL file was found to test - // against. The - // only DO that contains an Array of SDOs is Harmonic Value (HMV). The Kalkitech SCL Manager - // handles the - // array of SDOs in HMV as an array of DAs. + BdaInt32U bufTm = + new BdaInt32U( + new ObjectReference(reportObjRef + ".BufTm"), fc, "", false, false); + attribute = xmlNode.getAttributes().getNamedItem("bufTime"); + if (attribute != null) { + bufTm.setValue(Long.parseLong(attribute.getNodeValue())); + } + children.add(bufTm); - Node iNodeFound = findINode(doiNode, sdo.getName()); + children.add( + new BdaInt8U( + new ObjectReference(reportObjRef + ".SqNum"), fc, "", false, false)); - childNodes.addAll(createFcDataObjects(sdo.getName(), ref, sdo.getType(), iNodeFound)); - } + children.add(trigOps); - Map> subFCDataMap = new LinkedHashMap<>(); + BdaInt32U intgPd = + new BdaInt32U( + new ObjectReference(reportObjRef + ".IntgPd"), fc, "", false, false); + attribute = xmlNode.getAttributes().getNamedItem("intgPd"); + if (attribute != null) { + intgPd.setValue(Long.parseLong(attribute.getNodeValue())); + } + children.add(intgPd); + + children.add( + new BdaBoolean( + new ObjectReference(reportObjRef + ".GI"), fc, "", false, false)); + + Rcb rcb; + + if (fc == Fc.BR) { + + children.add( + new BdaBoolean( + new ObjectReference(reportObjRef + ".PurgeBuf"), fc, "", false, false)); + + children.add( + new BdaOctetString( + new ObjectReference(reportObjRef + ".EntryID"), + fc, + "", + 8, + false, + false)); + + children.add( + new BdaEntryTime( + new ObjectReference(reportObjRef + ".TimeOfEntry"), + fc, + "", + false, + false)); + + if (useResvTmsAttributes) { + children.add( + new BdaInt16( + new ObjectReference(reportObjRef + ".ResvTms"), fc, "", false, false)); + } + + children.add( + new BdaOctetString( + new ObjectReference(reportObjRef + ".Owner"), fc, "", 64, false, false)); + + rcb = new Brcb(reportObjRef, children); + + } else { + children.add( + new BdaOctetString( + new ObjectReference(reportObjRef + ".Owner"), fc, "", 64, false, false)); + + rcb = new Urcb(reportObjRef, children); + } - for (Fc fc : Fc.values()) { - subFCDataMap.put(fc, new ArrayList<>()); - } + rcbInstances.add(rcb); + } - for (ModelNode childNode : childNodes) { - subFCDataMap.get(((FcModelNode) childNode).getFc()).add((FcModelNode) childNode); + return rcbInstances; } - List fcDataObjects = new ArrayList<>(); - ObjectReference objectReference = new ObjectReference(ref); + private List createFcDataObjects( + String name, String parentRef, String doTypeID, Node doiNode, String type) throws SclParseException { - for (Fc fc : Fc.values()) { - if (subFCDataMap.get(fc).size() > 0) { - fcDataObjects.add(new FcDataObject(objectReference, fc, subFCDataMap.get(fc))); - } - } + DoType doType = typeDefinitions.getDOType(doTypeID); - return fcDataObjects; - } + if (doType == null) { + throw new SclParseException("DO type " + doTypeID + " not defined!"); + } - private Node findINode(Node iNode, String dattrName) { + String ref = parentRef + '.' + name; - if (iNode == null) { - return null; - } + String cdc = doType.getCdc(); - for (int i = 0; i < iNode.getChildNodes().getLength(); i++) { - Node childNode = iNode.getChildNodes().item(i); - if (childNode.getAttributes() != null) { - Node nameAttribute = childNode.getAttributes().getNamedItem("name"); - if (nameAttribute != null && nameAttribute.getNodeValue().equals(dattrName)) { - return childNode; - } - } - } - return null; - } - - private Array createArrayOfDataAttributes(String ref, Da dataAttribute, Node iXmlNode) - throws SclParseException { - - Fc fc = dataAttribute.getFc(); - int size = dataAttribute.getCount(); - - List arrayItems = new ArrayList<>(); - for (int i = 0; i < size; i++) { - // TODO go down the iXmlNode using the ix attribute? - arrayItems.add( - createDataAttribute( - ref + '(' + i + ')', - fc, - dataAttribute, - iXmlNode, - dataAttribute.isDchg(), - dataAttribute.isDupd(), - dataAttribute.isQchg())); - } + cdcDOMap.put(ref, cdc); - return new Array(new ObjectReference(ref), fc, arrayItems); - } - - /** returns a ConstructedDataAttribute or BasicDataAttribute */ - private FcModelNode createDataAttribute( - String ref, - Fc fc, - AbstractDataAttribute dattr, - Node iXmlNode, - boolean dchg, - boolean dupd, - boolean qchg) - throws SclParseException { - - if (dattr instanceof Da) { - Da dataAttribute = (Da) dattr; - dchg = dataAttribute.isDchg(); - dupd = dataAttribute.isDupd(); - qchg = dataAttribute.isQchg(); - } + List childNodes = new ArrayList<>(); - String bType = dattr.getbType(); + for (Da dattr : doType.das) { - if (bType.equals("Struct")) { - DaType datype = typeDefinitions.getDaType(dattr.getType()); + // look for DAI node with the name of the DA + Node iNodeFound = findINode(doiNode, dattr.getName()); - if (datype == null) { - throw new SclParseException("DAType " + dattr.getbType() + " not declared!"); - } + if (iNodeFound != null) { + NamedNodeMap daiAttributes = iNodeFound.getAttributes(); + if (daiAttributes != null) { + Node nameAttribute = daiAttributes.getNamedItem("name"); + Node cdcAttribute = daiAttributes.getNamedItem("desc"); - List subDataAttributes = new ArrayList<>(); - for (Bda bda : datype.bdas) { + if (cdcAttribute != null) { + daiDescMap.put(ref + "." + nameAttribute.getNodeValue(), cdcAttribute.getNodeValue()); + } + } + } - Node iNodeFound = findINode(iXmlNode, bda.getName()); + if (dattr.getCount() >= 1) { + childNodes.add(createArrayOfDataAttributes(ref + '.' + dattr.getName(), dattr, iNodeFound)); + } else { + FcModelNode fcModelNode = + createDataAttribute( + ref + '.' + dattr.getName(), + dattr.getFc(), + dattr, + iNodeFound, + false, + false, + false + ); + + fcModelNode.setbType(dattr.getbType()); + fcModelNode.setDaType(dattr.getType()); + childNodes.add(fcModelNode); + } + } - subDataAttributes.add( - createDataAttribute(ref + '.' + bda.getName(), fc, bda, iNodeFound, dchg, dupd, qchg)); - } - return new ConstructedDataAttribute(new ObjectReference(ref), fc, subDataAttributes); - } + for (Sdo sdo : doType.sdos) { - String val = null; - String sAddr = null; - if (iXmlNode != null) { - NamedNodeMap attributeMap = iXmlNode.getAttributes(); - Node sAddrAttribute = attributeMap.getNamedItem("sAddr"); - if (sAddrAttribute != null) { - sAddr = sAddrAttribute.getNodeValue(); - } - - NodeList elements = iXmlNode.getChildNodes(); - for (int i = 0; i < elements.getLength(); i++) { - Node node = elements.item(i); - if (node.getNodeName().equals("Val")) { - val = node.getTextContent(); - } - } - if (val == null) { - // insert value from DA element - val = dattr.value; - } - } + // parsing Arrays of SubDataObjects is ignored for now because no SCL file was found to test + // against. The + // only DO that contains an Array of SDOs is Harmonic Value (HMV). The Kalkitech SCL Manager + // handles the + // array of SDOs in HMV as an array of DAs. - if (bType.equals("BOOLEAN")) { - BdaBoolean bda = new BdaBoolean(new ObjectReference(ref), fc, sAddr, dchg, dupd); - if (val != null) { - if (val.equalsIgnoreCase("true") || val.equals("1")) { - bda.setValue(true); - } else if (val.equalsIgnoreCase("false") || val.equals("0")) { - bda.setValue(false); - } else { - throw new SclParseException("invalid boolean configured value: " + val); - } - } - return bda; - } else if (bType.equals("INT8")) { - BdaInt8 bda = new BdaInt8(new ObjectReference(ref), fc, sAddr, dchg, dupd); - if (val != null) { - try { - bda.setValue(Byte.parseByte(val)); - } catch (NumberFormatException e) { - throw new SclParseException("invalid INT8 configured value: " + val); - } - } - return bda; - } else if (bType.equals("INT16")) { - BdaInt16 bda = new BdaInt16(new ObjectReference(ref), fc, sAddr, dchg, dupd); - if (val != null) { - try { - bda.setValue(Short.parseShort(val)); - } catch (NumberFormatException e) { - throw new SclParseException("invalid INT16 configured value: " + val); + Node iNodeFound = findINode(doiNode, sdo.getName()); + + childNodes.addAll(createFcDataObjects(sdo.getName(), ref, sdo.getType(), iNodeFound, type)); } - } - return bda; - } else if (bType.equals("INT32")) { - BdaInt32 bda = new BdaInt32(new ObjectReference(ref), fc, sAddr, dchg, dupd); - if (val != null) { - try { - bda.setValue(Integer.parseInt(val)); - } catch (NumberFormatException e) { - throw new SclParseException("invalid INT32 configured value: " + val); + + Map> subFCDataMap = new LinkedHashMap<>(); + + for (Fc fc : Fc.values()) { + subFCDataMap.put(fc, new ArrayList<>()); } - } - return bda; - } else if (bType.equals("INT64")) { - BdaInt64 bda = new BdaInt64(new ObjectReference(ref), fc, sAddr, dchg, dupd); - if (val != null) { - try { - bda.setValue(Long.parseLong(val)); - } catch (NumberFormatException e) { - throw new SclParseException("invalid INT64 configured value: " + val); + + for (ModelNode childNode : childNodes) { + subFCDataMap.get(((FcModelNode) childNode).getFc()).add((FcModelNode) childNode); } - } - return bda; - } else if (bType.equals("INT128")) { - BdaInt128 bda = new BdaInt128(new ObjectReference(ref), fc, sAddr, dchg, dupd); - if (val != null) { - try { - bda.setValue(Long.parseLong(val)); - } catch (NumberFormatException e) { - throw new SclParseException("invalid INT128 configured value: " + val); + + List fcDataObjects = new ArrayList<>(); + ObjectReference objectReference = new ObjectReference(ref); + + for (Fc fc : Fc.values()) { + if (subFCDataMap.get(fc).size() > 0) { + FcDataObject fcDataObject = new FcDataObject(objectReference, fc, subFCDataMap.get(fc)); + fcDataObject.setDoType(type); + fcDataObject.setCdc(cdc); + fcDataObjects.add(fcDataObject); + } } - } - return bda; - } else if (bType.equals("INT8U")) { - BdaInt8U bda = new BdaInt8U(new ObjectReference(ref), fc, sAddr, dchg, dupd); - if (val != null) { - try { - bda.setValue(Short.parseShort(val)); - } catch (NumberFormatException e) { - throw new SclParseException("invalid INT8U configured value: " + val); + + return fcDataObjects; + } + + private Node findINode(Node iNode, String dattrName) { + + if (iNode == null) { + return null; } - } - return bda; - } else if (bType.equals("INT16U")) { - BdaInt16U bda = new BdaInt16U(new ObjectReference(ref), fc, sAddr, dchg, dupd); - if (val != null) { - try { - bda.setValue(Integer.parseInt(val)); - } catch (NumberFormatException e) { - throw new SclParseException("invalid INT16U configured value: " + val); + + for (int i = 0; i < iNode.getChildNodes().getLength(); i++) { + Node childNode = iNode.getChildNodes().item(i); + if (childNode.getAttributes() != null) { + Node nameAttribute = childNode.getAttributes().getNamedItem("name"); + if (nameAttribute != null && nameAttribute.getNodeValue().equals(dattrName)) { + return childNode; + } + } } - } - return bda; - } else if (bType.equals("INT32U")) { - BdaInt32U bda = new BdaInt32U(new ObjectReference(ref), fc, sAddr, dchg, dupd); - if (val != null) { - try { - bda.setValue(Long.parseLong(val)); - } catch (NumberFormatException e) { - throw new SclParseException("invalid INT32U configured value: " + val); + return null; + } + + private Array createArrayOfDataAttributes(String ref, Da dataAttribute, Node iXmlNode) + throws SclParseException { + + Fc fc = dataAttribute.getFc(); + int size = dataAttribute.getCount(); + + List arrayItems = new ArrayList<>(); + for (int i = 0; i < size; i++) { + // TODO go down the iXmlNode using the ix attribute? + arrayItems.add( + createDataAttribute( + ref + '(' + i + ')', + fc, + dataAttribute, + iXmlNode, + dataAttribute.isDchg(), + dataAttribute.isDupd(), + dataAttribute.isQchg())); } - } - return bda; - } else if (bType.equals("FLOAT32")) { - BdaFloat32 bda = new BdaFloat32(new ObjectReference(ref), fc, sAddr, dchg, dupd); - if (val != null) { - try { - bda.setFloat(Float.parseFloat(val)); - } catch (NumberFormatException e) { - throw new SclParseException("invalid FLOAT32 configured value: " + val); + + return new Array(new ObjectReference(ref), fc, arrayItems); + } + + /** + * returns a ConstructedDataAttribute or BasicDataAttribute + */ + private FcModelNode createDataAttribute( + String ref, + Fc fc, + AbstractDataAttribute dattr, + Node iXmlNode, + boolean dchg, + boolean dupd, + boolean qchg) + throws SclParseException { + + if (dattr instanceof Da) { + Da dataAttribute = (Da) dattr; + dchg = dataAttribute.isDchg(); + dupd = dataAttribute.isDupd(); + qchg = dataAttribute.isQchg(); } - } - return bda; - } else if (bType.equals("FLOAT64")) { - BdaFloat64 bda = new BdaFloat64(new ObjectReference(ref), fc, sAddr, dchg, dupd); - if (val != null) { - try { - bda.setDouble(Double.parseDouble(val)); - } catch (NumberFormatException e) { - throw new SclParseException("invalid FLOAT64 configured value: " + val); + + String bType = dattr.getbType(); + + if (bType.equals("Struct")) { + DaType datype = typeDefinitions.getDaType(dattr.getType()); + + if (datype == null) { + throw new SclParseException("DAType " + dattr.getbType() + " not declared!"); + } + + List subDataAttributes = new ArrayList<>(); + for (Bda bda : datype.bdas) { + + Node iNodeFound = findINode(iXmlNode, bda.getName()); + + FcModelNode fcModelNode = createDataAttribute(ref + '.' + bda.getName(), fc, bda, iNodeFound, dchg, dupd, qchg); + fcModelNode.setDaType(bda.getType()); + fcModelNode.setbType(bda.getbType()); + + subDataAttributes.add(fcModelNode); + } + return new ConstructedDataAttribute(new ObjectReference(ref), fc, subDataAttributes); } - } - return bda; - } else if (bType.startsWith("VisString")) { - BdaVisibleString bda = - new BdaVisibleString( - new ObjectReference(ref), - fc, - sAddr, - Integer.parseInt(dattr.getbType().substring(9)), - dchg, - dupd); - if (val != null) { - bda.setValue(val.getBytes(UTF_8)); - } - return bda; - } else if (bType.startsWith("Unicode")) { - BdaUnicodeString bda = - new BdaUnicodeString( - new ObjectReference(ref), - fc, - sAddr, - Integer.parseInt(dattr.getbType().substring(7)), - dchg, - dupd); - if (val != null) { - bda.setValue(val.getBytes(UTF_8)); - } - return bda; - } else if (bType.startsWith("Octet")) { - BdaOctetString bda = - new BdaOctetString( - new ObjectReference(ref), - fc, - sAddr, - Integer.parseInt(dattr.getbType().substring(5)), - dchg, - dupd); - if (val != null) { - // TODO - // throw new SclParseException("parsing configured value for octet string is not supported - // yet."); - } - return bda; - } else if (bType.equals("Quality")) { - return new BdaQuality(new ObjectReference(ref), fc, sAddr, qchg); - } else if (bType.equals("Check")) { - return new BdaCheck(new ObjectReference(ref)); - } else if (bType.equals("Dbpos")) { - return new BdaDoubleBitPos(new ObjectReference(ref), fc, sAddr, dchg, dupd); - } else if (bType.equals("Tcmd")) { - return new BdaTapCommand(new ObjectReference(ref), fc, sAddr, dchg, dupd); - } else if (bType.equals("OptFlds")) { - return new BdaOptFlds(new ObjectReference(ref), fc); - } else if (bType.equals("TrgOps")) { - return new BdaTriggerConditions(new ObjectReference(ref), fc); - } else if (bType.equals("EntryID")) { - return new BdaOctetString(new ObjectReference(ref), fc, sAddr, 8, dchg, dupd); - } else if (bType.equals("EntryTime")) { - return new BdaEntryTime(new ObjectReference(ref), fc, sAddr, dchg, dupd); - } else if (bType.equals("PhyComAddr")) { - // TODO not correct! - return new BdaOctetString(new ObjectReference(ref), fc, sAddr, 6, dchg, dupd); - } else if (bType.equals("Timestamp")) { - BdaTimestamp bda = new BdaTimestamp(new ObjectReference(ref), fc, sAddr, dchg, dupd); - if (val != null) { - // TODO - throw new SclParseException("parsing configured value for TIMESTAMP is not supported yet."); - } - return bda; - } else if (bType.equals("Enum")) { - String type = dattr.getType(); - if (type == null) { - throw new SclParseException("The exact type of the enumeration is not set."); - } - EnumType enumType = typeDefinitions.getEnumType(type); - - if (enumType == null) { - throw new SclParseException("Definition of enum type: " + type + " not found."); - } - - if (enumType.max > 127 || enumType.min < -128) { - BdaInt16 bda = new BdaInt16(new ObjectReference(ref), fc, sAddr, dchg, dupd); - if (val != null) { - for (EnumVal enumVal : enumType.getValues()) { - if (val.equals(enumVal.getId())) { - bda.setValue((short) enumVal.getOrd()); - return bda; + + String val = null; + String sAddr = null; + if (iXmlNode != null) { + NamedNodeMap attributeMap = iXmlNode.getAttributes(); + Node sAddrAttribute = attributeMap.getNamedItem("sAddr"); + if (sAddrAttribute != null) { + sAddr = sAddrAttribute.getNodeValue(); + } + + NodeList elements = iXmlNode.getChildNodes(); + for (int i = 0; i < elements.getLength(); i++) { + Node node = elements.item(i); + if (node.getNodeName().equals("Val")) { + val = node.getTextContent(); + } + } + if (val == null) { + // insert value from DA element + val = dattr.value; } - } - throw new SclParseException("unknown enum value: " + val); } - return bda; - } else { - BdaInt8 bda = new BdaInt8(new ObjectReference(ref), fc, sAddr, dchg, dupd); - if (val != null) { - for (EnumVal enumVal : enumType.getValues()) { - if (val.equals(enumVal.getId())) { - bda.setValue((byte) enumVal.getOrd()); - return bda; + + if (bType.equals("BOOLEAN")) { + BdaBoolean bda = new BdaBoolean(new ObjectReference(ref), fc, sAddr, dchg, dupd); + if (val != null) { + if (val.equalsIgnoreCase("true") || val.equals("1")) { + bda.setValue(true); + } else if (val.equalsIgnoreCase("false") || val.equals("0")) { + bda.setValue(false); + } else { + throw new SclParseException("invalid boolean configured value: " + val); + } + } + return bda; + } else if (bType.equals("INT8")) { + BdaInt8 bda = new BdaInt8(new ObjectReference(ref), fc, sAddr, dchg, dupd); + if (val != null) { + try { + bda.setValue(Byte.parseByte(val)); + } catch (NumberFormatException e) { + throw new SclParseException("invalid INT8 configured value: " + val); + } + } + return bda; + } else if (bType.equals("INT16")) { + BdaInt16 bda = new BdaInt16(new ObjectReference(ref), fc, sAddr, dchg, dupd); + if (val != null) { + try { + bda.setValue(Short.parseShort(val)); + } catch (NumberFormatException e) { + throw new SclParseException("invalid INT16 configured value: " + val); + } + } + return bda; + } else if (bType.equals("INT32")) { + BdaInt32 bda = new BdaInt32(new ObjectReference(ref), fc, sAddr, dchg, dupd); + if (val != null) { + try { + bda.setValue(Integer.parseInt(val)); + } catch (NumberFormatException e) { + throw new SclParseException("invalid INT32 configured value: " + val); + } + } + return bda; + } else if (bType.equals("INT64")) { + BdaInt64 bda = new BdaInt64(new ObjectReference(ref), fc, sAddr, dchg, dupd); + if (val != null) { + try { + bda.setValue(Long.parseLong(val)); + } catch (NumberFormatException e) { + throw new SclParseException("invalid INT64 configured value: " + val); + } + } + return bda; + } else if (bType.equals("INT128")) { + BdaInt128 bda = new BdaInt128(new ObjectReference(ref), fc, sAddr, dchg, dupd); + if (val != null) { + try { + bda.setValue(Long.parseLong(val)); + } catch (NumberFormatException e) { + throw new SclParseException("invalid INT128 configured value: " + val); + } + } + return bda; + } else if (bType.equals("INT8U")) { + BdaInt8U bda = new BdaInt8U(new ObjectReference(ref), fc, sAddr, dchg, dupd); + if (val != null) { + try { + bda.setValue(Short.parseShort(val)); + } catch (NumberFormatException e) { + throw new SclParseException("invalid INT8U configured value: " + val); + } + } + return bda; + } else if (bType.equals("INT16U")) { + BdaInt16U bda = new BdaInt16U(new ObjectReference(ref), fc, sAddr, dchg, dupd); + if (val != null) { + try { + bda.setValue(Integer.parseInt(val)); + } catch (NumberFormatException e) { + throw new SclParseException("invalid INT16U configured value: " + val); + } + } + return bda; + } else if (bType.equals("INT32U")) { + BdaInt32U bda = new BdaInt32U(new ObjectReference(ref), fc, sAddr, dchg, dupd); + if (val != null) { + try { + bda.setValue(Long.parseLong(val)); + } catch (NumberFormatException e) { + throw new SclParseException("invalid INT32U configured value: " + val); + } + } + return bda; + } else if (bType.equals("FLOAT32")) { + BdaFloat32 bda = new BdaFloat32(new ObjectReference(ref), fc, sAddr, dchg, dupd); + if (val != null) { + try { + bda.setFloat(Float.parseFloat(val)); + } catch (NumberFormatException e) { + throw new SclParseException("invalid FLOAT32 configured value: " + val); + } + } + return bda; + } else if (bType.equals("FLOAT64")) { + BdaFloat64 bda = new BdaFloat64(new ObjectReference(ref), fc, sAddr, dchg, dupd); + if (val != null) { + try { + bda.setDouble(Double.parseDouble(val)); + } catch (NumberFormatException e) { + throw new SclParseException("invalid FLOAT64 configured value: " + val); + } } - } - throw new SclParseException("unknown enum value: " + val); + return bda; + } else if (bType.startsWith("VisString")) { + BdaVisibleString bda = + new BdaVisibleString( + new ObjectReference(ref), + fc, + sAddr, + Integer.parseInt(dattr.getbType().substring(9)), + dchg, + dupd); + if (val != null) { + bda.setValue(val.getBytes(UTF_8)); + } + return bda; + } else if (bType.startsWith("Unicode")) { + BdaUnicodeString bda = + new BdaUnicodeString( + new ObjectReference(ref), + fc, + sAddr, + Integer.parseInt(dattr.getbType().substring(7)), + dchg, + dupd); + if (val != null) { + bda.setValue(val.getBytes(UTF_8)); + } + return bda; + } else if (bType.startsWith("Octet")) { + BdaOctetString bda = + new BdaOctetString( + new ObjectReference(ref), + fc, + sAddr, + Integer.parseInt(dattr.getbType().substring(5)), + dchg, + dupd); + if (val != null) { + // TODO + // throw new SclParseException("parsing configured value for octet string is not supported + // yet."); + } + return bda; + } else if (bType.equals("Quality")) { + return new BdaQuality(new ObjectReference(ref), fc, sAddr, qchg); + } else if (bType.equals("Check")) { + return new BdaCheck(new ObjectReference(ref)); + } else if (bType.equals("Dbpos")) { + return new BdaDoubleBitPos(new ObjectReference(ref), fc, sAddr, dchg, dupd); + } else if (bType.equals("Tcmd")) { + return new BdaTapCommand(new ObjectReference(ref), fc, sAddr, dchg, dupd); + } else if (bType.equals("OptFlds")) { + return new BdaOptFlds(new ObjectReference(ref), fc); + } else if (bType.equals("TrgOps")) { + return new BdaTriggerConditions(new ObjectReference(ref), fc); + } else if (bType.equals("EntryID")) { + return new BdaOctetString(new ObjectReference(ref), fc, sAddr, 8, dchg, dupd); + } else if (bType.equals("EntryTime")) { + return new BdaEntryTime(new ObjectReference(ref), fc, sAddr, dchg, dupd); + } else if (bType.equals("PhyComAddr")) { + // TODO not correct! + return new BdaOctetString(new ObjectReference(ref), fc, sAddr, 6, dchg, dupd); + } else if (bType.equals("Timestamp")) { + BdaTimestamp bda = new BdaTimestamp(new ObjectReference(ref), fc, sAddr, dchg, dupd); + if (val != null) { + // TODO + bda.setDate(new Date(System.currentTimeMillis())); + } + return bda; + } else if (bType.equals("Enum")) { + String type = dattr.getType(); + if (type == null) { + throw new SclParseException("The exact type of the enumeration is not set."); + } + EnumType enumType = typeDefinitions.getEnumType(type); + + if (enumType == null) { + throw new SclParseException("Definition of enum type: " + type + " not found."); + } + + if (enumType.max > 127 || enumType.min < -128) { + BdaInt16 bda = new BdaInt16(new ObjectReference(ref), fc, sAddr, dchg, dupd); + if (val != null) { + for (EnumVal enumVal : enumType.getValues()) { + if (val.equals(enumVal.getId())) { + bda.setValue((short) enumVal.getOrd()); + return bda; + } + } + throw new SclParseException("unknown enum value: " + val); + } + return bda; + } else { + BdaInt8 bda = new BdaInt8(new ObjectReference(ref), fc, sAddr, dchg, dupd); + if (val != null) { + for (EnumVal enumVal : enumType.getValues()) { + if (val.equals(enumVal.getId())) { + bda.setValue((byte) enumVal.getOrd()); + return bda; + } + } + throw new SclParseException("unknown enum value: " + val); + } + return bda; + } + } else if (bType.equals("ObjRef")) { + BdaVisibleString bda = + new BdaVisibleString(new ObjectReference(ref), fc, sAddr, 129, dchg, dupd); + if (val != null) { + bda.setValue(val.getBytes(UTF_8)); + } + return bda; + } else { + throw new SclParseException("Invalid bType: " + bType); } - return bda; - } - } else if (bType.equals("ObjRef")) { - BdaVisibleString bda = - new BdaVisibleString(new ObjectReference(ref), fc, sAddr, 129, dchg, dupd); - if (val != null) { - bda.setValue(val.getBytes(UTF_8)); - } - return bda; - } else { - throw new SclParseException("Invalid bType: " + bType); } - } -} +} \ No newline at end of file diff --git a/src/main/java/com/beanit/iec61850bean/ServerAssociation.java b/src/main/java/com/beanit/iec61850bean/ServerAssociation.java index dde88d3..e10e5a3 100644 --- a/src/main/java/com/beanit/iec61850bean/ServerAssociation.java +++ b/src/main/java/com/beanit/iec61850bean/ServerAssociation.java @@ -13,84 +13,45 @@ */ package com.beanit.iec61850bean; -import static java.nio.charset.StandardCharsets.UTF_8; - import com.beanit.asn1bean.ber.ReverseByteArrayOutputStream; import com.beanit.asn1bean.ber.types.BerInteger; import com.beanit.asn1bean.ber.types.BerNull; import com.beanit.asn1bean.ber.types.string.BerVisibleString; import com.beanit.iec61850bean.internal.BerBoolean; import com.beanit.iec61850bean.internal.NamedThreadFactory; -import com.beanit.iec61850bean.internal.mms.asn1.AccessResult; -import com.beanit.iec61850bean.internal.mms.asn1.ConfirmedErrorPDU; -import com.beanit.iec61850bean.internal.mms.asn1.ConfirmedRequestPDU; -import com.beanit.iec61850bean.internal.mms.asn1.ConfirmedResponsePDU; -import com.beanit.iec61850bean.internal.mms.asn1.ConfirmedServiceRequest; -import com.beanit.iec61850bean.internal.mms.asn1.ConfirmedServiceResponse; -import com.beanit.iec61850bean.internal.mms.asn1.Data; -import com.beanit.iec61850bean.internal.mms.asn1.DataAccessError; -import com.beanit.iec61850bean.internal.mms.asn1.DefineNamedVariableListRequest; -import com.beanit.iec61850bean.internal.mms.asn1.DefineNamedVariableListResponse; -import com.beanit.iec61850bean.internal.mms.asn1.DeleteNamedVariableListRequest; -import com.beanit.iec61850bean.internal.mms.asn1.DeleteNamedVariableListResponse; -import com.beanit.iec61850bean.internal.mms.asn1.GetNameListRequest; -import com.beanit.iec61850bean.internal.mms.asn1.GetNameListResponse; +import com.beanit.iec61850bean.internal.mms.asn1.*; import com.beanit.iec61850bean.internal.mms.asn1.GetNameListResponse.ListOfIdentifier; -import com.beanit.iec61850bean.internal.mms.asn1.GetNamedVariableListAttributesResponse; -import com.beanit.iec61850bean.internal.mms.asn1.GetVariableAccessAttributesRequest; -import com.beanit.iec61850bean.internal.mms.asn1.GetVariableAccessAttributesResponse; -import com.beanit.iec61850bean.internal.mms.asn1.Identifier; -import com.beanit.iec61850bean.internal.mms.asn1.InitiateRequestPDU; -import com.beanit.iec61850bean.internal.mms.asn1.InitiateResponsePDU; -import com.beanit.iec61850bean.internal.mms.asn1.Integer16; -import com.beanit.iec61850bean.internal.mms.asn1.Integer32; -import com.beanit.iec61850bean.internal.mms.asn1.Integer8; -import com.beanit.iec61850bean.internal.mms.asn1.MMSpdu; -import com.beanit.iec61850bean.internal.mms.asn1.ObjectName; import com.beanit.iec61850bean.internal.mms.asn1.ObjectName.DomainSpecific; -import com.beanit.iec61850bean.internal.mms.asn1.ParameterSupportOptions; -import com.beanit.iec61850bean.internal.mms.asn1.ReadRequest; -import com.beanit.iec61850bean.internal.mms.asn1.ReadResponse; import com.beanit.iec61850bean.internal.mms.asn1.ReadResponse.ListOfAccessResult; import com.beanit.iec61850bean.internal.mms.asn1.ServiceError.ErrorClass; -import com.beanit.iec61850bean.internal.mms.asn1.ServiceSupportOptions; -import com.beanit.iec61850bean.internal.mms.asn1.TypeDescription; import com.beanit.iec61850bean.internal.mms.asn1.TypeDescription.Structure; import com.beanit.iec61850bean.internal.mms.asn1.TypeDescription.Structure.Components; -import com.beanit.iec61850bean.internal.mms.asn1.TypeSpecification; -import com.beanit.iec61850bean.internal.mms.asn1.Unsigned32; -import com.beanit.iec61850bean.internal.mms.asn1.VariableAccessSpecification; -import com.beanit.iec61850bean.internal.mms.asn1.VariableDefs; -import com.beanit.iec61850bean.internal.mms.asn1.WriteRequest; -import com.beanit.iec61850bean.internal.mms.asn1.WriteResponse; import com.beanit.josistack.AcseAssociation; import com.beanit.josistack.ByteBufferInputStream; import com.beanit.josistack.DecodingException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + import java.io.ByteArrayInputStream; import java.io.EOFException; import java.io.IOException; import java.net.SocketTimeoutException; import java.nio.ByteBuffer; -import java.util.ArrayList; -import java.util.Collection; -import java.util.HashMap; -import java.util.Iterator; -import java.util.List; -import java.util.ListIterator; +import java.util.*; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeoutException; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; + +import static java.nio.charset.StandardCharsets.UTF_8; final class ServerAssociation { private static final Logger logger = LoggerFactory.getLogger(ServerAssociation.class); private static final WriteResponse.CHOICE writeSuccess = new WriteResponse.CHOICE(); - private static String[] mmsFcs = { - "MX", "ST", "CO", "CF", "DC", "SP", "SG", "RP", "LG", "BR", "GO", "GS", "SV", "SE", "EX", "SR", - "OR", "BL" + private static final String[] mmsFcs = { + "MX", "ST", "CO", "CF", "DC", "SP", "SG", "RP", "LG", "BR", "GO", "GS", "SV", "SE", "EX", "SR", + "OR", "BL" }; static { diff --git a/src/main/java/com/beanit/iec61850bean/ServerModel.java b/src/main/java/com/beanit/iec61850bean/ServerModel.java index 9a4f59f..a13f073 100644 --- a/src/main/java/com/beanit/iec61850bean/ServerModel.java +++ b/src/main/java/com/beanit/iec61850bean/ServerModel.java @@ -17,384 +17,488 @@ import com.beanit.iec61850bean.internal.mms.asn1.ObjectName; import com.beanit.iec61850bean.internal.mms.asn1.ObjectName.DomainSpecific; import com.beanit.iec61850bean.internal.mms.asn1.VariableDefs; -import java.util.ArrayList; -import java.util.Collection; -import java.util.HashMap; -import java.util.Iterator; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; + +import java.util.*; public final class ServerModel extends ModelNode { - private final Map dataSets = new LinkedHashMap<>(); + private final Map dataSets = new LinkedHashMap<>(); + + private final Map urcbs = new HashMap<>(); + private final Map brcbs = new HashMap<>(); + + private ConnectionParam connectionParam; + private String iedName; + private String iedManufacturer; + private List ldsInsts; + private List ldsRefs; + private List lnsRefs; + private Map> lns; + private HashSet lnS; + private Map descriptions; + private Map dAIDescriptions; + private Map dos; + private List logicalDevices; + + public ServerModel(List logicalDevices, Collection dataSets) { + children = new LinkedHashMap<>(); + objectReference = null; + for (LogicalDevice logicalDevice : logicalDevices) { + children.put(logicalDevice.getReference().getName(), logicalDevice); + logicalDevice.setParent(this); + } + + if (dataSets != null) { + addDataSets(dataSets); + } + + for (LogicalDevice ld : logicalDevices) { + for (ModelNode ln : ld.getChildren()) { + for (Urcb urcb : ((LogicalNode) ln).getUrcbs()) { + urcbs.put(urcb.getReference().toString(), urcb); + urcb.dataSet = getDataSet(urcb.getDatSet().getStringValue().replace('$', '.')); + } + for (Brcb brcb : ((LogicalNode) ln).getBrcbs()) { + brcbs.put(brcb.getReference().toString(), brcb); + brcb.dataSet = getDataSet(brcb.getDatSet().getStringValue().replace('$', '.')); + } + } + } + } - private final Map urcbs = new HashMap<>(); - private final Map brcbs = new HashMap<>(); + @Override + public ServerModel copy() { + List childCopies = new ArrayList<>(children.size()); + for (ModelNode childNode : children.values()) { + childCopies.add((LogicalDevice) childNode.copy()); + } + + List dataSetCopies = new ArrayList<>(dataSets.size()); + for (DataSet dataSet : dataSets.values()) { + dataSetCopies.add(dataSet); + } + + return new ServerModel(childCopies, dataSetCopies); + } + + /** + * Get the data set with the given reference. Return null if none is found. + * + * @param reference the reference of the requested data set. + * @return the data set with the given reference. + */ + public DataSet getDataSet(String reference) { + return dataSets.get(reference); + } + + void addDataSet(DataSet dataSet) { + dataSets.put(dataSet.getReferenceStr().replace('$', '.'), dataSet); + for (ModelNode ld : children.values()) { + for (ModelNode ln : ld.getChildren()) { + for (Urcb urcb : ((LogicalNode) ln).getUrcbs()) { + urcb.dataSet = getDataSet(urcb.getDatSet().getStringValue().replace('$', '.')); + } + for (Brcb brcb : ((LogicalNode) ln).getBrcbs()) { + brcb.dataSet = getDataSet(brcb.getDatSet().getStringValue().replace('$', '.')); + } + } + } + } + + void addDataSets(Collection dataSets) { + for (DataSet dataSet : dataSets) { + addDataSet(dataSet); + } + for (ModelNode ld : children.values()) { + for (ModelNode ln : ld.getChildren()) { + for (Urcb urcb : ((LogicalNode) ln).getUrcbs()) { + urcb.dataSet = getDataSet(urcb.getDatSet().getStringValue().replace('$', '.')); + } + for (Brcb brcb : ((LogicalNode) ln).getBrcbs()) { + brcb.dataSet = getDataSet(brcb.getDatSet().getStringValue().replace('$', '.')); + } + } + } + } - public ServerModel(List logicalDevices, Collection dataSets) { - children = new LinkedHashMap<>(); - objectReference = null; - for (LogicalDevice logicalDevice : logicalDevices) { - children.put(logicalDevice.getReference().getName(), logicalDevice); - logicalDevice.setParent(this); + List getDataSetNames(String ldName) { + // TODO make thread save + List dataSetNames = new ArrayList<>(); + for (String dataSetRef : dataSets.keySet()) { + if (dataSetRef.startsWith(ldName)) { + dataSetNames.add(dataSetRef.substring(dataSetRef.indexOf('/') + 1).replace('.', '$')); + } + } + return dataSetNames; } - if (dataSets != null) { - addDataSets(dataSets); + /** + * Get a collection of all data sets that exist in this model. + * + * @return a collection of all data sets + */ + public Collection getDataSets() { + return dataSets.values(); } - for (LogicalDevice ld : logicalDevices) { - for (ModelNode ln : ld.getChildren()) { - for (Urcb urcb : ((LogicalNode) ln).getUrcbs()) { - urcbs.put(urcb.getReference().toString(), urcb); - urcb.dataSet = getDataSet(urcb.getDatSet().getStringValue().replace('$', '.')); + /** + * @param dataSetReference the data set reference + * @return returns the DataSet that was removed, null if no DataSet with the given reference was + * found or the data set is not deletable. + */ + DataSet removeDataSet(String dataSetReference) { + DataSet dataSet = dataSets.get(dataSetReference); + if (dataSet == null || !dataSet.isDeletable()) { + return null; } - for (Brcb brcb : ((LogicalNode) ln).getBrcbs()) { - brcbs.put(brcb.getReference().toString(), brcb); - brcb.dataSet = getDataSet(brcb.getDatSet().getStringValue().replace('$', '.')); + DataSet removedDataSet = dataSets.remove(dataSetReference); + for (ModelNode ld : children.values()) { + for (ModelNode ln : ld.getChildren()) { + for (Urcb urcb : ((LogicalNode) ln).getUrcbs()) { + urcb.dataSet = getDataSet(urcb.getDatSet().getStringValue().replace('$', '.')); + } + for (Brcb brcb : ((LogicalNode) ln).getBrcbs()) { + brcb.dataSet = getDataSet(brcb.getDatSet().getStringValue().replace('$', '.')); + } + } } - } + return removedDataSet; + } + + void addUrcb(Urcb urcb) { + urcbs.put(urcb.getReference().getName(), urcb); } - } - @Override - public ServerModel copy() { - List childCopies = new ArrayList<>(children.size()); - for (ModelNode childNode : children.values()) { - childCopies.add((LogicalDevice) childNode.copy()); + /** + * Get the unbuffered report control block (URCB) with the given reference. + * + * @param reference the reference of the requested URCB. + * @return the reference to the requested URCB or null if none with the given reference is found. + */ + public Urcb getUrcb(String reference) { + return urcbs.get(reference); } - List dataSetCopies = new ArrayList<>(dataSets.size()); - for (DataSet dataSet : dataSets.values()) { - dataSetCopies.add(dataSet); + /** + * Get a collection of all unbuffered report control blocks (URCB) that exist in this model. + * + * @return a collection of all unbuffered report control blocks (URCB) + */ + public Collection getUrcbs() { + return urcbs.values(); } - return new ServerModel(childCopies, dataSetCopies); - } + /** + * Get the buffered report control block (BRCB) with the given reference. + * + * @param reference the reference of the requested BRCB. + * @return the reference to the requested BRCB or null if none with the given reference is found. + */ + public Brcb getBrcb(String reference) { + return brcbs.get(reference); + } - /** - * Get the data set with the given reference. Return null if none is found. - * - * @param reference the reference of the requested data set. - * @return the data set with the given reference. - */ - public DataSet getDataSet(String reference) { - return dataSets.get(reference); - } + /** + * Get a collection of all buffered report control blocks (BRCB) that exist in this model. + * + * @return a collection of all buffered report control blocks (BRCB) + */ + public Collection getBrcbs() { + return brcbs.values(); + } - void addDataSet(DataSet dataSet) { - dataSets.put(dataSet.getReferenceStr().replace('$', '.'), dataSet); - for (ModelNode ld : children.values()) { - for (ModelNode ln : ld.getChildren()) { - for (Urcb urcb : ((LogicalNode) ln).getUrcbs()) { - urcb.dataSet = getDataSet(urcb.getDatSet().getStringValue().replace('$', '.')); + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + for (ModelNode logicalDevice : children.values()) { + sb.append(logicalDevice.toString()); } - for (Brcb brcb : ((LogicalNode) ln).getBrcbs()) { - brcb.dataSet = getDataSet(brcb.getDatSet().getStringValue().replace('$', '.')); + sb.append("\n\n\n---------------------\nURCBs:"); + for (Urcb urcb : getUrcbs()) { + sb.append("\n\n").append(urcb); } - } + + sb.append("\n\n\n---------------------\nBRCBs:"); + for (Brcb brcb : getBrcbs()) { + sb.append("\n\n").append(brcb); + } + + sb.append("\n\n\n---------------------\nData sets:"); + for (DataSet dataSet : getDataSets()) { + sb.append("\n\n").append(dataSet); + } + + return sb.toString(); + } + + /** + * Searches and returns the model node with the given object reference and FC. If searching for + * Logical Devices and Logical Nodes the given fc parameter may be null. + * + * @param objectReference the object reference of the node that is being searched for. It has a + * syntax like "ldname/ln.do....". + * @param fc the functional constraint of the requested model node. May be null for Logical Device + * and Logical Node references. + * @return the model node if it was found or null otherwise + */ + public ModelNode findModelNode(ObjectReference objectReference, Fc fc) { + + ModelNode currentNode = this; + Iterator searchedNodeReferenceIterator = objectReference.iterator(); + + while (searchedNodeReferenceIterator.hasNext()) { + currentNode = currentNode.getChild(searchedNodeReferenceIterator.next(), fc); + if (currentNode == null) { + return null; + } + } + return currentNode; } - } - void addDataSets(Collection dataSets) { - for (DataSet dataSet : dataSets) { - addDataSet(dataSet); + /** + * Searches and returns the model node with the given object reference and FC. If searching for + * Logical Devices and Logical Nodes the given fc parameter may be null. + * + * @param objectReference the object reference of the node that is being searched for. It has a + * syntax like "ldname/ln.do....". + * @param fc the functional constraint of the requested model node. May be null for Logical Device + * and Logical Node references. + * @return the model node if it was found or null otherwise + */ + public ModelNode findModelNode(String objectReference, Fc fc) { + return findModelNode(new ObjectReference(objectReference), fc); } - for (ModelNode ld : children.values()) { - for (ModelNode ln : ld.getChildren()) { - for (Urcb urcb : ((LogicalNode) ln).getUrcbs()) { - urcb.dataSet = getDataSet(urcb.getDatSet().getStringValue().replace('$', '.')); + + /** + * Returns the subModelNode that is referenced by the given VariableDef. Return null in case the + * referenced ModelNode is not found. + * + * @param variableDef the variableDef + * @return the subModelNode that is referenced by the given VariableDef + * @throws ServiceError if an error occurs + */ + FcModelNode getNodeFromVariableDef(VariableDefs.SEQUENCE variableDef) throws ServiceError { + + ObjectName objectName = variableDef.getVariableSpecification().getName(); + + if (objectName == null) { + throw new ServiceError( + ServiceError.FAILED_DUE_TO_COMMUNICATIONS_CONSTRAINT, + "name in objectName is not selected"); + } + + DomainSpecific domainSpecific = objectName.getDomainSpecific(); + + if (domainSpecific == null) { + throw new ServiceError( + ServiceError.FAILED_DUE_TO_COMMUNICATIONS_CONSTRAINT, + "domain_specific in name is not selected"); } - for (Brcb brcb : ((LogicalNode) ln).getBrcbs()) { - brcb.dataSet = getDataSet(brcb.getDatSet().getStringValue().replace('$', '.')); + + ModelNode modelNode = getChild(domainSpecific.getDomainID().toString()); + + if (modelNode == null) { + return null; + } + + String mmsItemId = domainSpecific.getItemID().toString(); + int index1 = mmsItemId.indexOf('$'); + + if (index1 == -1) { + throw new ServiceError( + ServiceError.FAILED_DUE_TO_COMMUNICATIONS_CONSTRAINT, + "invalid mms item id: " + domainSpecific.getItemID()); } - } - } - } - - List getDataSetNames(String ldName) { - // TODO make thread save - List dataSetNames = new ArrayList<>(); - for (String dataSetRef : dataSets.keySet()) { - if (dataSetRef.startsWith(ldName)) { - dataSetNames.add(dataSetRef.substring(dataSetRef.indexOf('/') + 1).replace('.', '$')); - } - } - return dataSetNames; - } - - /** - * Get a collection of all data sets that exist in this model. - * - * @return a collection of all data sets - */ - public Collection getDataSets() { - return dataSets.values(); - } - - /** - * @param dataSetReference the data set reference - * @return returns the DataSet that was removed, null if no DataSet with the given reference was - * found or the data set is not deletable. - */ - DataSet removeDataSet(String dataSetReference) { - DataSet dataSet = dataSets.get(dataSetReference); - if (dataSet == null || !dataSet.isDeletable()) { - return null; - } - DataSet removedDataSet = dataSets.remove(dataSetReference); - for (ModelNode ld : children.values()) { - for (ModelNode ln : ld.getChildren()) { - for (Urcb urcb : ((LogicalNode) ln).getUrcbs()) { - urcb.dataSet = getDataSet(urcb.getDatSet().getStringValue().replace('$', '.')); + + LogicalNode ln = (LogicalNode) modelNode.getChild(mmsItemId.substring(0, index1)); + + if (ln == null) { + return null; } - for (Brcb brcb : ((LogicalNode) ln).getBrcbs()) { - brcb.dataSet = getDataSet(brcb.getDatSet().getStringValue().replace('$', '.')); + + int index2 = mmsItemId.indexOf('$', index1 + 1); + + if (index2 == -1) { + throw new ServiceError( + ServiceError.FAILED_DUE_TO_COMMUNICATIONS_CONSTRAINT, "invalid mms item id"); } - } - } - return removedDataSet; - } - - void addUrcb(Urcb urcb) { - urcbs.put(urcb.getReference().getName(), urcb); - } - - /** - * Get the unbuffered report control block (URCB) with the given reference. - * - * @param reference the reference of the requested URCB. - * @return the reference to the requested URCB or null if none with the given reference is found. - */ - public Urcb getUrcb(String reference) { - return urcbs.get(reference); - } - - /** - * Get a collection of all unbuffered report control blocks (URCB) that exist in this model. - * - * @return a collection of all unbuffered report control blocks (URCB) - */ - public Collection getUrcbs() { - return urcbs.values(); - } - - /** - * Get the buffered report control block (BRCB) with the given reference. - * - * @param reference the reference of the requested BRCB. - * @return the reference to the requested BRCB or null if none with the given reference is found. - */ - public Brcb getBrcb(String reference) { - return brcbs.get(reference); - } - - /** - * Get a collection of all buffered report control blocks (BRCB) that exist in this model. - * - * @return a collection of all buffered report control blocks (BRCB) - */ - public Collection getBrcbs() { - return brcbs.values(); - } - - @Override - public String toString() { - StringBuilder sb = new StringBuilder(); - for (ModelNode logicalDevice : children.values()) { - sb.append(logicalDevice.toString()); - } - sb.append("\n\n\n---------------------\nURCBs:"); - for (Urcb urcb : getUrcbs()) { - sb.append("\n\n").append(urcb); - } - - sb.append("\n\n\n---------------------\nBRCBs:"); - for (Brcb brcb : getBrcbs()) { - sb.append("\n\n").append(brcb); - } - - sb.append("\n\n\n---------------------\nData sets:"); - for (DataSet dataSet : getDataSets()) { - sb.append("\n\n").append(dataSet); - } - - return sb.toString(); - } - /** - * Searches and returns the model node with the given object reference and FC. If searching for - * Logical Devices and Logical Nodes the given fc parameter may be null. - * - * @param objectReference the object reference of the node that is being searched for. It has a - * syntax like "ldname/ln.do....". - * @param fc the functional constraint of the requested model node. May be null for Logical Device - * and Logical Node references. - * @return the model node if it was found or null otherwise - */ - public ModelNode findModelNode(ObjectReference objectReference, Fc fc) { - - ModelNode currentNode = this; - Iterator searchedNodeReferenceIterator = objectReference.iterator(); + Fc fc = Fc.fromString(mmsItemId.substring(index1 + 1, index2)); - while (searchedNodeReferenceIterator.hasNext()) { - currentNode = currentNode.getChild(searchedNodeReferenceIterator.next(), fc); - if (currentNode == null) { - return null; - } - } - return currentNode; - } + if (fc == null) { + throw new ServiceError( + ServiceError.FAILED_DUE_TO_COMMUNICATIONS_CONSTRAINT, + "unknown functional constraint: " + mmsItemId.substring(index1 + 1, index2)); + } - /** - * Searches and returns the model node with the given object reference and FC. If searching for - * Logical Devices and Logical Nodes the given fc parameter may be null. - * - * @param objectReference the object reference of the node that is being searched for. It has a - * syntax like "ldname/ln.do....". - * @param fc the functional constraint of the requested model node. May be null for Logical Device - * and Logical Node references. - * @return the model node if it was found or null otherwise - */ - public ModelNode findModelNode(String objectReference, Fc fc) { - return findModelNode(new ObjectReference(objectReference), fc); - } + index1 = index2; + + index2 = mmsItemId.indexOf('$', index1 + 1); + + if (index2 == -1) { + if (fc == Fc.RP) { + return ln.getUrcb(mmsItemId.substring(index1 + 1)); + } + if (fc == Fc.BR) { + return ln.getBrcb(mmsItemId.substring(index1 + 1)); + } + return (FcModelNode) ln.getChild(mmsItemId.substring(index1 + 1), fc); + } + + if (fc == Fc.RP) { + modelNode = ln.getUrcb(mmsItemId.substring(index1 + 1, index2)); + } else if (fc == Fc.BR) { + modelNode = ln.getBrcb(mmsItemId.substring(index1 + 1, index2)); + } else { + modelNode = ln.getChild(mmsItemId.substring(index1 + 1, index2), fc); + } + + index1 = index2; + index2 = mmsItemId.indexOf('$', index1 + 1); + while (index2 != -1) { + modelNode = modelNode.getChild(mmsItemId.substring(index1 + 1, index2)); + index1 = index2; + index2 = mmsItemId.indexOf('$', index1 + 1); + } - /** - * Returns the subModelNode that is referenced by the given VariableDef. Return null in case the - * referenced ModelNode is not found. - * - * @param variableDef the variableDef - * @return the subModelNode that is referenced by the given VariableDef - * @throws ServiceError if an error occurs - */ - FcModelNode getNodeFromVariableDef(VariableDefs.SEQUENCE variableDef) throws ServiceError { + modelNode = modelNode.getChild(mmsItemId.substring(index1 + 1)); - ObjectName objectName = variableDef.getVariableSpecification().getName(); + if (variableDef.getAlternateAccess() == null) { + // no array is in this node path + return (FcModelNode) modelNode; + } - if (objectName == null) { - throw new ServiceError( - ServiceError.FAILED_DUE_TO_COMMUNICATIONS_CONSTRAINT, - "name in objectName is not selected"); + AlternateAccessSelection altAccIt = + variableDef.getAlternateAccess().getCHOICE().get(0).getUnnamed(); + + if (altAccIt.getSelectAlternateAccess() != null) { + // path to node below an array element + modelNode = + ((Array) modelNode) + .getChild( + altAccIt.getSelectAlternateAccess().getAccessSelection().getIndex().intValue()); + + String mmsSubArrayItemId = + altAccIt + .getSelectAlternateAccess() + .getAlternateAccess() + .getCHOICE() + .get(0) + .getUnnamed() + .getSelectAccess() + .getComponent() + .getBasic() + .toString(); + + index1 = -1; + index2 = mmsSubArrayItemId.indexOf('$'); + while (index2 != -1) { + modelNode = modelNode.getChild(mmsSubArrayItemId.substring(index1 + 1, index2)); + index1 = index2; + index2 = mmsItemId.indexOf('$', index1 + 1); + } + + return (FcModelNode) modelNode.getChild(mmsSubArrayItemId.substring(index1 + 1)); + } else { + // path to an array element + return (FcModelNode) + ((Array) modelNode).getChild(altAccIt.getSelectAccess().getIndex().intValue()); + } } - DomainSpecific domainSpecific = objectName.getDomainSpecific(); + public ConnectionParam getConnectionParam() { + return connectionParam; + } - if (domainSpecific == null) { - throw new ServiceError( - ServiceError.FAILED_DUE_TO_COMMUNICATIONS_CONSTRAINT, - "domain_specific in name is not selected"); + public void setConnectionParam(ConnectionParam connectionParam) { + this.connectionParam = connectionParam; } - ModelNode modelNode = getChild(domainSpecific.getDomainID().toString()); + public String getIedName() { + return iedName; + } - if (modelNode == null) { - return null; + public void setIedName(String iedName) { + this.iedName = iedName; } - String mmsItemId = domainSpecific.getItemID().toString(); - int index1 = mmsItemId.indexOf('$'); + public String getIedManufacturer() { + return iedManufacturer; + } - if (index1 == -1) { - throw new ServiceError( - ServiceError.FAILED_DUE_TO_COMMUNICATIONS_CONSTRAINT, - "invalid mms item id: " + domainSpecific.getItemID()); + public void setIedManufacturer(String iedManufacturer) { + this.iedManufacturer = iedManufacturer; } - LogicalNode ln = (LogicalNode) modelNode.getChild(mmsItemId.substring(0, index1)); + public List getLdsInsts() { + return ldsInsts; + } - if (ln == null) { - return null; + public void setLdsInsts(List ldsInsts) { + this.ldsInsts = ldsInsts; } - int index2 = mmsItemId.indexOf('$', index1 + 1); + public List getLdsRefs() { + return ldsRefs; + } - if (index2 == -1) { - throw new ServiceError( - ServiceError.FAILED_DUE_TO_COMMUNICATIONS_CONSTRAINT, "invalid mms item id"); + public void setLdsRefs(List ldsRefs) { + this.ldsRefs = ldsRefs; } - Fc fc = Fc.fromString(mmsItemId.substring(index1 + 1, index2)); + public List getLnsRefs() { + return lnsRefs; + } - if (fc == null) { - throw new ServiceError( - ServiceError.FAILED_DUE_TO_COMMUNICATIONS_CONSTRAINT, - "unknown functional constraint: " + mmsItemId.substring(index1 + 1, index2)); + public void setLnsRefs(List lnsRefs) { + this.lnsRefs = lnsRefs; } - index1 = index2; + public Map> getLns() { + return lns; + } - index2 = mmsItemId.indexOf('$', index1 + 1); + public void setLns(Map> lns) { + this.lns = lns; + } - if (index2 == -1) { - if (fc == Fc.RP) { - return ln.getUrcb(mmsItemId.substring(index1 + 1)); - } - if (fc == Fc.BR) { - return ln.getBrcb(mmsItemId.substring(index1 + 1)); - } - return (FcModelNode) ln.getChild(mmsItemId.substring(index1 + 1), fc); + public HashSet getLnS() { + return lnS; } - if (fc == Fc.RP) { - modelNode = ln.getUrcb(mmsItemId.substring(index1 + 1, index2)); - } else if (fc == Fc.BR) { - modelNode = ln.getBrcb(mmsItemId.substring(index1 + 1, index2)); - } else { - modelNode = ln.getChild(mmsItemId.substring(index1 + 1, index2), fc); + public void setLnS(HashSet lnS) { + this.lnS = lnS; } - index1 = index2; - index2 = mmsItemId.indexOf('$', index1 + 1); - while (index2 != -1) { - modelNode = modelNode.getChild(mmsItemId.substring(index1 + 1, index2)); - index1 = index2; - index2 = mmsItemId.indexOf('$', index1 + 1); + public Map getDescriptions() { + return descriptions; } - modelNode = modelNode.getChild(mmsItemId.substring(index1 + 1)); + public void setDescriptions(Map descriptions) { + this.descriptions = descriptions; + } - if (variableDef.getAlternateAccess() == null) { - // no array is in this node path - return (FcModelNode) modelNode; + public Map getdAIDescriptions() { + return dAIDescriptions; } - AlternateAccessSelection altAccIt = - variableDef.getAlternateAccess().getCHOICE().get(0).getUnnamed(); + public void setdAIDescriptions(Map dAIDescriptions) { + this.dAIDescriptions = dAIDescriptions; + } - if (altAccIt.getSelectAlternateAccess() != null) { - // path to node below an array element - modelNode = - ((Array) modelNode) - .getChild( - altAccIt.getSelectAlternateAccess().getAccessSelection().getIndex().intValue()); + public Map getDos() { + return dos; + } - String mmsSubArrayItemId = - altAccIt - .getSelectAlternateAccess() - .getAlternateAccess() - .getCHOICE() - .get(0) - .getUnnamed() - .getSelectAccess() - .getComponent() - .getBasic() - .toString(); + public void setDos(Map dos) { + this.dos = dos; + } - index1 = -1; - index2 = mmsSubArrayItemId.indexOf('$'); - while (index2 != -1) { - modelNode = modelNode.getChild(mmsSubArrayItemId.substring(index1 + 1, index2)); - index1 = index2; - index2 = mmsItemId.indexOf('$', index1 + 1); - } + public List getLogicalDevices() { + return logicalDevices; + } - return (FcModelNode) modelNode.getChild(mmsSubArrayItemId.substring(index1 + 1)); - } else { - // path to an array element - return (FcModelNode) - ((Array) modelNode).getChild(altAccIt.getSelectAccess().getIndex().intValue()); + public void setLogicalDevices(List logicalDevices) { + this.logicalDevices = logicalDevices; } - } -} +} \ No newline at end of file diff --git a/src/main/java/com/beanit/iec61850bean/ServerSap.java b/src/main/java/com/beanit/iec61850bean/ServerSap.java index 722de00..e1aa9a2 100644 --- a/src/main/java/com/beanit/iec61850bean/ServerSap.java +++ b/src/main/java/com/beanit/iec61850bean/ServerSap.java @@ -15,13 +15,14 @@ import com.beanit.josistack.AcseAssociation; import com.beanit.josistack.ServerAcseSap; + +import javax.net.ServerSocketFactory; import java.io.IOException; import java.net.InetAddress; import java.nio.ByteBuffer; import java.util.ArrayList; import java.util.List; import java.util.Timer; -import javax.net.ServerSocketFactory; /** * The ServerSap class represents the IEC 61850 service access point for server @@ -340,11 +341,7 @@ public void setValues(List bdas) { bdaMirror.setValueFrom(bda); synchronized (bdaMirror.chgRcbs) { for (Urcb urcb : bdaMirror.chgRcbs) { - if (bdaMirror.dupd && urcb.getTrgOps().isDataUpdate()) { - urcb.report(bdaMirror, true, false, true); - } else { - urcb.report(bdaMirror, true, false, false); - } + urcb.report(bdaMirror, true, false, bdaMirror.dupd && urcb.getTrgOps().isDataUpdate()); } } } else if (bdaMirror.dupd && bdaMirror.dupdRcbs.size() != 0) { diff --git a/src/main/java/com/beanit/iec61850bean/clientgui/ClientGui.java b/src/main/java/com/beanit/iec61850bean/clientgui/ClientGui.java index a981eca..dc21245 100644 --- a/src/main/java/com/beanit/iec61850bean/clientgui/ClientGui.java +++ b/src/main/java/com/beanit/iec61850bean/clientgui/ClientGui.java @@ -18,11 +18,13 @@ import com.beanit.iec61850bean.ServerModel; import com.beanit.iec61850bean.ServiceError; import com.beanit.iec61850bean.clientgui.util.Counter; -import java.awt.Component; -import java.awt.Dimension; -import java.awt.GridBagConstraints; -import java.awt.GridBagLayout; -import java.awt.Insets; + +import javax.swing.*; +import javax.swing.event.TreeSelectionEvent; +import javax.swing.event.TreeSelectionListener; +import javax.swing.tree.DefaultMutableTreeNode; +import javax.swing.tree.DefaultTreeModel; +import java.awt.*; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.io.FileInputStream; @@ -32,23 +34,6 @@ import java.net.InetAddress; import java.net.UnknownHostException; import java.util.Properties; -import javax.swing.Box; -import javax.swing.BoxLayout; -import javax.swing.JButton; -import javax.swing.JFrame; -import javax.swing.JLabel; -import javax.swing.JPanel; -import javax.swing.JScrollPane; -import javax.swing.JSeparator; -import javax.swing.JTextField; -import javax.swing.JTree; -import javax.swing.ToolTipManager; -import javax.swing.UIManager; -import javax.swing.UnsupportedLookAndFeelException; -import javax.swing.event.TreeSelectionEvent; -import javax.swing.event.TreeSelectionListener; -import javax.swing.tree.DefaultMutableTreeNode; -import javax.swing.tree.DefaultTreeModel; public class ClientGui extends JFrame implements ActionListener, TreeSelectionListener { @@ -416,7 +401,7 @@ private void showDataDetails(DataTreeNode node, String pre, Counter y) { for (int i = 0; i < node.getChildCount(); i++) { y.increment(); DataObjectTreeNode childNode = (DataObjectTreeNode) node.getChildAt(i); - showDataDetails(childNode, pre + "." + childNode.toString(), y); + showDataDetails(childNode, pre + "." + childNode, y); detailsPanel.add(new JSeparator()); addDetailsComponent(new JSeparator(), 0, y.getValue(), 3, 1, 1, 0); } diff --git a/src/main/java/com/beanit/iec61850bean/internal/scl/DoType.java b/src/main/java/com/beanit/iec61850bean/internal/scl/DoType.java index 67bfce6..0a0a567 100644 --- a/src/main/java/com/beanit/iec61850bean/internal/scl/DoType.java +++ b/src/main/java/com/beanit/iec61850bean/internal/scl/DoType.java @@ -14,36 +14,45 @@ package com.beanit.iec61850bean.internal.scl; import com.beanit.iec61850bean.SclParseException; -import java.util.ArrayList; -import java.util.List; import org.w3c.dom.Node; import org.w3c.dom.NodeList; +import java.util.ArrayList; +import java.util.List; + public final class DoType extends AbstractType { - // attributes not needed: cdc, iedType + // attributes not needed: cdc, iedType - public List das = new ArrayList<>(); - public List sdos = new ArrayList<>(); + public List das = new ArrayList<>(); + public List sdos = new ArrayList<>(); - public DoType(Node xmlNode) throws SclParseException { + private final String cdc; - super(xmlNode); + public DoType(Node xmlNode) throws SclParseException { - if (xmlNode.getAttributes().getNamedItem("cdc") == null) { - throw new SclParseException("Required attribute \"cdc\" not found in DOType!"); - } + super(xmlNode); + + if (xmlNode.getAttributes().getNamedItem("cdc") == null) { + throw new SclParseException("Required attribute \"cdc\" not found in DOType!"); + } else { + cdc = xmlNode.getAttributes().getNamedItem("cdc").getNodeValue(); + } - NodeList elements = xmlNode.getChildNodes(); + NodeList elements = xmlNode.getChildNodes(); + + for (int i = 0; i < elements.getLength(); i++) { + Node node = elements.item(i); + if (node.getNodeName().equals("SDO")) { + sdos.add(new Sdo(node)); + } + if (node.getNodeName().equals("DA")) { + das.add(new Da(node)); + } + } + } - for (int i = 0; i < elements.getLength(); i++) { - Node node = elements.item(i); - if (node.getNodeName().equals("SDO")) { - sdos.add(new Sdo(node)); - } - if (node.getNodeName().equals("DA")) { - das.add(new Da(node)); - } + public String getCdc() { + return cdc; } - } } diff --git a/src/main/java/com/beanit/iec61850bean/internal/scl/LnType.java b/src/main/java/com/beanit/iec61850bean/internal/scl/LnType.java index 7947ec3..34e898e 100644 --- a/src/main/java/com/beanit/iec61850bean/internal/scl/LnType.java +++ b/src/main/java/com/beanit/iec61850bean/internal/scl/LnType.java @@ -14,32 +14,44 @@ package com.beanit.iec61850bean.internal.scl; import com.beanit.iec61850bean.SclParseException; -import java.util.ArrayList; -import java.util.List; import org.w3c.dom.Node; import org.w3c.dom.NodeList; +import java.util.ArrayList; +import java.util.List; + public final class LnType extends AbstractType { - // attributes not needed: lnClass, iedType + // attributes not needed: lnClass, iedType + + public List dos = new ArrayList<>(); + private String lnClass; - public List dos = new ArrayList<>(); + public LnType(Node xmlNode) throws SclParseException { - public LnType(Node xmlNode) throws SclParseException { + super(xmlNode); - super(xmlNode); + if (xmlNode.getAttributes().getNamedItem("lnClass") == null) { + throw new SclParseException("Required attribute \"lnClass\" not found in LNType!"); + } else { + lnClass = xmlNode.getAttributes().getNamedItem("lnClass").getNodeValue(); + } - if (xmlNode.getAttributes().getNamedItem("lnClass") == null) { - throw new SclParseException("Required attribute \"lnClass\" not found in LNType!"); + NodeList elements = xmlNode.getChildNodes(); + + for (int i = 0; i < elements.getLength(); i++) { + Node node = elements.item(i); + if (node.getNodeName().equals("DO")) { + dos.add(new Do(node)); + } + } } - NodeList elements = xmlNode.getChildNodes(); + public String getLnClass() { + return lnClass; + } - for (int i = 0; i < elements.getLength(); i++) { - Node node = elements.item(i); - if (node.getNodeName().equals("DO")) { - dos.add(new Do(node)); - } + public void setLnClass(String lnClass) { + this.lnClass = lnClass; } - } } diff --git a/src/main/java/com/beanit/jositransport/TConnection.java b/src/main/java/com/beanit/jositransport/TConnection.java index 8cd6e71..7576e2c 100644 --- a/src/main/java/com/beanit/jositransport/TConnection.java +++ b/src/main/java/com/beanit/jositransport/TConnection.java @@ -14,12 +14,8 @@ package com.beanit.jositransport; import com.beanit.iec61850bean.internal.util.SequenceNumber; -import java.io.BufferedInputStream; -import java.io.BufferedOutputStream; -import java.io.DataInputStream; -import java.io.DataOutputStream; -import java.io.EOFException; -import java.io.IOException; + +import java.io.*; import java.net.Socket; import java.net.SocketTimeoutException; import java.nio.ByteBuffer;