/*
|
* Copyright 2011-17 Fraunhofer ISE, energy & meteo Systems GmbH and other contributors
|
*
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
* you may not use this file except in compliance with the License.
|
* You may obtain a copy of the License at
|
*
|
* http://www.apache.org/licenses/LICENSE-2.0
|
*
|
* Unless required by applicable law or agreed to in writing, software
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
* See the License for the specific language governing permissions and
|
* limitations under the License.
|
*
|
*/
|
package org.openmuc.openiec61850;
|
|
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.LinkedList;
|
import java.util.List;
|
import java.util.Map;
|
|
import javax.xml.parsers.DocumentBuilderFactory;
|
|
import org.openmuc.openiec61850.internal.scl.AbstractDataAttribute;
|
import org.openmuc.openiec61850.internal.scl.Bda;
|
import org.openmuc.openiec61850.internal.scl.Da;
|
import org.openmuc.openiec61850.internal.scl.DaType;
|
import org.openmuc.openiec61850.internal.scl.Do;
|
import org.openmuc.openiec61850.internal.scl.DoType;
|
import org.openmuc.openiec61850.internal.scl.EnumType;
|
import org.openmuc.openiec61850.internal.scl.EnumVal;
|
import org.openmuc.openiec61850.internal.scl.LnSubDef;
|
import org.openmuc.openiec61850.internal.scl.LnType;
|
import org.openmuc.openiec61850.internal.scl.Sdo;
|
import org.openmuc.openiec61850.internal.scl.TypeDefinitions;
|
import org.w3c.dom.Document;
|
import org.w3c.dom.NamedNodeMap;
|
import org.w3c.dom.Node;
|
import org.w3c.dom.NodeList;
|
|
final class SclParser {
|
|
private TypeDefinitions typeDefinitions;
|
private final Map<String, DataSet> dataSetsMap = new HashMap<>();
|
|
private Document doc;
|
private String iedName;
|
private List<ServerSap> serverSaps = null;
|
private boolean useResvTmsAttributes = false;
|
|
private final List<LnSubDef> dataSetDefs = new ArrayList<>();
|
|
public List<ServerSap> getServerSaps() {
|
return serverSaps;
|
}
|
|
public void parse(String icdFile) throws SclParseException {
|
try {
|
parse(new FileInputStream(icdFile));
|
} catch (FileNotFoundException e) {
|
throw new SclParseException(e);
|
}
|
}
|
|
public void parse(InputStream icdFileStream) throws SclParseException {
|
|
typeDefinitions = new TypeDefinitions();
|
|
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
|
factory.setIgnoringComments(true);
|
|
try {
|
doc = factory.newDocumentBuilder().parse(icdFileStream);
|
} catch (Exception e) {
|
throw new SclParseException(e);
|
}
|
|
Node rootNode = doc.getDocumentElement();
|
|
if (!"SCL".equals(rootNode.getNodeName())) {
|
throw new SclParseException("Root node in SCL file is not of type \"SCL\"");
|
}
|
|
readTypeDefinitions();
|
|
NodeList iedList = doc.getElementsByTagName("IED");
|
if (iedList.getLength() == 0) {
|
throw new SclParseException("No IED section found!");
|
}
|
|
Node nameAttribute = iedList.item(0).getAttributes().getNamedItem("name");
|
|
iedName = nameAttribute.getNodeValue();
|
if ((iedName == null) || (iedName.length() == 0)) {
|
throw new SclParseException("IED must have a name!");
|
}
|
|
NodeList iedElements = iedList.item(0).getChildNodes();
|
|
serverSaps = new ArrayList<>(iedElements.getLength());
|
for (int i = 0; i < iedElements.getLength(); i++) {
|
Node element = iedElements.item(i);
|
String nodeName = element.getNodeName();
|
if ("AccessPoint".equals(nodeName)) {
|
serverSaps.add(createAccessPoint(element));
|
}
|
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");
|
}
|
}
|
}
|
}
|
}
|
}
|
|
private void readTypeDefinitions() throws SclParseException {
|
|
NodeList dttSections = doc.getElementsByTagName("DataTypeTemplates");
|
|
if (dttSections.getLength() != 1) {
|
throw new SclParseException("Only one DataTypeSection allowed");
|
}
|
|
Node dtt = dttSections.item(0);
|
|
NodeList dataTypes = dtt.getChildNodes();
|
|
for (int i = 0; i < dataTypes.getLength(); i++) {
|
Node element = dataTypes.item(i);
|
|
String nodeName = element.getNodeName();
|
|
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));
|
}
|
}
|
}
|
|
private ServerSap createAccessPoint(Node iedServer) throws SclParseException {
|
ServerSap serverSap = null;
|
|
NodeList elements = iedServer.getChildNodes();
|
|
for (int i = 0; i < elements.getLength(); i++) {
|
Node element = elements.item(i);
|
|
if (element.getNodeName().equals("Server")) {
|
|
ServerModel server = createServerModel(element);
|
|
Node namedItem = iedServer.getAttributes().getNamedItem("name");
|
if (namedItem == null) {
|
throw new SclParseException("AccessPoint has no name attribute!");
|
}
|
String name = namedItem.getNodeValue();
|
serverSap = new ServerSap(102, 0, null, server, name, null);
|
|
break;
|
}
|
}
|
|
if (serverSap == null) {
|
throw new SclParseException("AccessPoint has no server!");
|
}
|
|
return serverSap;
|
}
|
|
private ServerModel createServerModel(Node serverXMLNode) throws SclParseException {
|
|
NodeList elements = serverXMLNode.getChildNodes();
|
List<LogicalDevice> logicalDevices = new ArrayList<>(elements.getLength());
|
|
for (int i = 0; i < elements.getLength(); i++) {
|
Node element = elements.item(i);
|
|
if (element.getNodeName().equals("LDevice")) {
|
logicalDevices.add(createNewLDevice(element));
|
}
|
}
|
|
ServerModel serverModel = new ServerModel(logicalDevices, null);
|
|
for (LnSubDef dataSetDef : dataSetDefs) {
|
DataSet dataSet = createDataSet(serverModel, dataSetDef.logicalNode, dataSetDef.defXmlNode);
|
dataSetsMap.put(dataSet.getReferenceStr(), dataSet);
|
}
|
|
serverModel.addDataSets(dataSetsMap.values());
|
|
return serverModel;
|
|
}
|
|
private LogicalDevice createNewLDevice(Node ldXmlNode) throws SclParseException {
|
|
String inst = null;
|
String ldName = null;
|
|
NamedNodeMap attributes = ldXmlNode.getAttributes();
|
|
for (int i = 0; i < attributes.getLength(); i++) {
|
Node node = attributes.item(i);
|
String nodeName = node.getNodeName();
|
|
if (nodeName.equals("inst")) {
|
inst = node.getNodeValue();
|
}
|
else if (nodeName.equals("ldName")) {
|
ldName = node.getNodeValue();
|
}
|
}
|
|
if (inst == null) {
|
throw new SclParseException("Required attribute \"inst\" in logical device not found!");
|
}
|
|
NodeList elements = ldXmlNode.getChildNodes();
|
List<LogicalNode> logicalNodes = new ArrayList<>();
|
|
String ref;
|
if ((ldName != null) && (ldName.length() != 0)) {
|
ref = ldName;
|
}
|
else {
|
ref = iedName + inst;
|
}
|
|
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));
|
}
|
}
|
|
LogicalDevice lDevice = new LogicalDevice(new ObjectReference(ref), logicalNodes);
|
|
return lDevice;
|
}
|
|
private LogicalNode createNewLogicalNode(Node lnXmlNode, String parentRef) throws SclParseException {
|
|
// attributes not needed: desc
|
|
String inst = null;
|
String lnClass = null;
|
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();
|
|
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();
|
}
|
}
|
|
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!");
|
}
|
|
String ref = parentRef + '/' + prefix + lnClass + inst;
|
|
LnType lnTypeDef = typeDefinitions.getLNodeType(lnType);
|
|
List<FcDataObject> dataObjects = new ArrayList<>();
|
|
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;
|
}
|
}
|
}
|
|
dataObjects.addAll(createFcDataObjects(dobject.getName(), ref, dobject.getType(), doiNodeFound));
|
|
}
|
|
// 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));
|
}
|
}
|
|
LogicalNode lNode = new LogicalNode(new ObjectReference(ref), dataObjects);
|
|
// 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;
|
}
|
|
private DataSet createDataSet(ServerModel serverModel, LogicalNode lNode, Node dsXmlNode) throws SclParseException {
|
|
Node nameAttribute = dsXmlNode.getAttributes().getNamedItem("name");
|
if (nameAttribute == null) {
|
throw new SclParseException("DataSet must have a name");
|
}
|
|
String name = nameAttribute.getNodeValue();
|
|
List<FcModelNode> 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();
|
|
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());
|
}
|
}
|
|
}
|
|
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<FcDataObject> fcDataObjects = ((LogicalNode) logicalNode).getChildren(fc);
|
for (FcDataObject dataObj : fcDataObjects) {
|
dsMembers.add(dataObj);
|
}
|
|
}
|
|
}
|
|
}
|
|
DataSet dataSet = new DataSet(lNode.getReference().toString() + '.' + name, dsMembers, false);
|
return dataSet;
|
}
|
|
private List<Rcb> createReportControlBlocks(Node xmlNode, String parentRef) throws SclParseException {
|
|
Fc fc = Fc.RP;
|
NamedNodeMap rcbNodeAttributes = xmlNode.getAttributes();
|
Node attribute = rcbNodeAttributes.getNamedItem("buffered");
|
if (attribute != null && "true".equalsIgnoreCase(attribute.getNodeValue())) {
|
fc = Fc.BR;
|
}
|
|
Node nameAttribute = rcbNodeAttributes.getNamedItem("name");
|
if (nameAttribute == null) {
|
throw new SclParseException("Report Control Block has no name attribute.");
|
}
|
|
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);
|
}
|
}
|
}
|
}
|
|
List<Rcb> rcbInstances = new ArrayList<>(maxInstances);
|
|
for (int z = 1; z <= maxInstances; z++) {
|
|
ObjectReference reportObjRef;
|
|
if (maxInstances == 1) {
|
|
reportObjRef = new ObjectReference(parentRef + "." + nameAttribute.getNodeValue());
|
}
|
else {
|
reportObjRef = new ObjectReference(
|
parentRef + "." + nameAttribute.getNodeValue() + String.format("%02d", z));
|
}
|
|
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);
|
}
|
}
|
}
|
}
|
|
if (fc == Fc.RP) {
|
optFields.setEntryId(false);
|
optFields.setBufferOverflow(false);
|
}
|
|
List<FcModelNode> 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());
|
}
|
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());
|
}
|
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));
|
}
|
|
children.add(new BdaOctetString(new ObjectReference(reportObjRef.toString() + ".Owner"), fc, "", 64,
|
false, false));
|
|
rcb = new Brcb(reportObjRef, children);
|
|
}
|
else {
|
children.add(new BdaOctetString(new ObjectReference(reportObjRef.toString() + ".Owner"), fc, "", 64,
|
false, false));
|
|
rcb = new Urcb(reportObjRef, children);
|
|
}
|
|
rcbInstances.add(rcb);
|
}
|
|
return rcbInstances;
|
|
}
|
|
private List<FcDataObject> createFcDataObjects(String name, String parentRef, String doTypeID, Node doiNode)
|
throws SclParseException {
|
|
DoType doType = typeDefinitions.getDOType(doTypeID);
|
|
if (doType == null) {
|
throw new SclParseException("DO type " + doTypeID + " not defined!");
|
}
|
|
String ref = parentRef + '.' + name;
|
|
List<ModelNode> childNodes = new ArrayList<>();
|
|
for (Da dattr : doType.das) {
|
|
// look for DAI node with the name of the DA
|
Node iNodeFound = findINode(doiNode, dattr.getName());
|
|
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));
|
}
|
|
}
|
|
for (Sdo sdo : doType.sdos) {
|
|
// 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.
|
|
Node iNodeFound = findINode(doiNode, sdo.getName());
|
|
childNodes.addAll(createFcDataObjects(sdo.getName(), ref, sdo.getType(), iNodeFound));
|
|
}
|
|
Map<Fc, List<FcModelNode>> subFCDataMap = new LinkedHashMap<>();
|
|
for (Fc fc : Fc.values()) {
|
subFCDataMap.put(fc, new LinkedList<FcModelNode>());
|
}
|
|
for (ModelNode childNode : childNodes) {
|
subFCDataMap.get(((FcModelNode) childNode).getFc()).add((FcModelNode) childNode);
|
}
|
|
List<FcDataObject> fcDataObjects = new LinkedList<>();
|
ObjectReference objectReference = new ObjectReference(ref);
|
|
for (Fc fc : Fc.values()) {
|
if (subFCDataMap.get(fc).size() > 0) {
|
fcDataObjects.add(new FcDataObject(objectReference, fc, subFCDataMap.get(fc)));
|
}
|
}
|
|
return fcDataObjects;
|
}
|
|
private Node findINode(Node iNode, String dattrName) {
|
|
if (iNode == null) {
|
return null;
|
}
|
|
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<FcModelNode> 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 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();
|
}
|
|
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<FcModelNode> subDataAttributes = new ArrayList<>();
|
for (Bda bda : datype.bdas) {
|
|
Node iNodeFound = findINode(iXmlNode, bda.getName());
|
|
subDataAttributes
|
.add(createDataAttribute(ref + '.' + bda.getName(), fc, bda, iNodeFound, dchg, dupd, qchg));
|
}
|
return new ConstructedDataAttribute(new ObjectReference(ref), fc, subDataAttributes);
|
}
|
|
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;
|
}
|
}
|
|
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(new Boolean(true));
|
}
|
else if (val.equalsIgnoreCase("false") || val.equals("0")) {
|
bda.setValue(new Boolean(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);
|
}
|
}
|
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());
|
}
|
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());
|
}
|
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("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(new Short((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(new Byte((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());
|
}
|
return bda;
|
}
|
else {
|
throw new SclParseException("Invalid bType: " + bType);
|
}
|
|
}
|
}
|