From 509e2ea71338c46c19eada045e9ea17a914746a4 Mon Sep 17 00:00:00 2001 From: Dudley Hartmann Date: Tue, 24 Dec 2024 11:42:05 +0800 Subject: [PATCH 1/4] feat: add batch reading method --- .../iec61850bean/ClientAssociation.java | 81 +++++++++++++++++++ 1 file changed, 81 insertions(+) diff --git a/src/main/java/com/beanit/iec61850bean/ClientAssociation.java b/src/main/java/com/beanit/iec61850bean/ClientAssociation.java index 866ae46..c9a3e3f 100644 --- a/src/main/java/com/beanit/iec61850bean/ClientAssociation.java +++ b/src/main/java/com/beanit/iec61850bean/ClientAssociation.java @@ -117,6 +117,9 @@ public final class ClientAssociation { private boolean closed = false; + // maximum read quantity at once batch read + private int maxNodeSize = 1000; + ClientAssociation( InetAddress address, int port, @@ -809,6 +812,84 @@ public final class ClientAssociation { decodeGetDataValuesResponse(confirmedServiceResponse, modelNode); } + /** + * read batch data values. + * + * @param nodeList the functionally constrained model node that is to be read. + * @return the map of nodes that could not be read or failed. key - ModelNode; value - ErrorMessage + * @throws ServiceError + * @throws IOException + */ + public Map getDataValueList(List nodeList) throws ServiceError, IOException { + Map errorNodes = new HashMap<>(); + if (nodeList.size() > maxNodeSize) { + for (int i = 0; i < nodeList.size(); i += maxNodeSize) { + errorNodes.putAll(doBatchRead(nodeList.subList(i, Math.min(i + maxNodeSize, nodeList.size())))); + } + } else { + errorNodes.putAll(doBatchRead(nodeList)); + } + return errorNodes; + } + + public Map doBatchRead(List nodeList) throws ServiceError, IOException { + ConfirmedServiceRequest serviceRequest = constructGetDataValueListRequest(nodeList); + ConfirmedServiceResponse confirmedServiceResponse = encodeWriteReadDecode(serviceRequest); + return decodeGetDataValueListResponse(confirmedServiceResponse, nodeList); + } + + private ConfirmedServiceRequest constructGetDataValueListRequest(List nodeList) { + VariableAccessSpecification varAccessSpec = constructVariableAccessSpecification(nodeList); + + ReadRequest readRequest = new ReadRequest(); + readRequest.setVariableAccessSpecification(varAccessSpec); + + ConfirmedServiceRequest confirmedServiceRequest = new ConfirmedServiceRequest(); + confirmedServiceRequest.setRead(readRequest); + + return confirmedServiceRequest; + } + + /** + * @param confirmedServiceResponse response from the server + * @param nodeList list of nodes to read + * @return the set of nodes that have failed + * @throws ServiceError + */ + private Map decodeGetDataValueListResponse( + ConfirmedServiceResponse confirmedServiceResponse, List nodeList) throws ServiceError { + + if (confirmedServiceResponse.getRead() == null) { + throw new ServiceError( + ServiceError.FAILED_DUE_TO_COMMUNICATIONS_CONSTRAINT, + "Error decoding GetDataValuesReponsePdu"); + } + + List listOfAccessResults = + confirmedServiceResponse.getRead().getListOfAccessResult().getAccessResult(); + + if (listOfAccessResults.size() != nodeList.size()) { + throw new ServiceError( + ServiceError.PARAMETER_VALUE_INAPPROPRIATE, "Number of results does not match."); + } + Map errorNode = new HashMap<>(); + + for (int i = 0; i < listOfAccessResults.size(); i++) { + AccessResult accRes = listOfAccessResults.get(i); + + if (accRes.getFailure() != null) { + errorNode.put(nodeList.get(i), mmsDataAccessErrorToServiceError(accRes.getFailure()).getMessage()); + } else { + try { + nodeList.get(i).setValueFromMmsDataObj(accRes.getSuccess()); + } catch (ServiceError e) { + errorNode.put(nodeList.get(i), e.getMessage()); + } + } + } + return errorNode; + } + private boolean decodeGetFileDirectoryResponse( ConfirmedServiceResponse confirmedServiceResponse, List files) throws ServiceError { From a1a9856727661081d5063b446941405821db653e Mon Sep 17 00:00:00 2001 From: Dudley Hartmann Date: Tue, 24 Dec 2024 11:45:45 +0800 Subject: [PATCH 2/4] add list constructVariableAccessSpecification method --- .../com/beanit/iec61850bean/ClientAssociation.java | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/main/java/com/beanit/iec61850bean/ClientAssociation.java b/src/main/java/com/beanit/iec61850bean/ClientAssociation.java index c9a3e3f..6138d8f 100644 --- a/src/main/java/com/beanit/iec61850bean/ClientAssociation.java +++ b/src/main/java/com/beanit/iec61850bean/ClientAssociation.java @@ -1239,6 +1239,20 @@ public final class ClientAssociation { return variableAccessSpecification; } + private VariableAccessSpecification constructVariableAccessSpecification(List nodeList) { + VariableDefs listOfVariable = new VariableDefs(); + + List variableDefsSeqOf = listOfVariable.getSEQUENCE(); + for (FcModelNode modelNodes : nodeList) { + variableDefsSeqOf.add(modelNodes.getMmsVariableDef()); + } + + VariableAccessSpecification variableAccessSpecification = new VariableAccessSpecification(); + variableAccessSpecification.setListOfVariable(listOfVariable); + + return variableAccessSpecification; + } + private void decodeSetDataValuesResponse(ConfirmedServiceResponse confirmedServiceResponse) throws ServiceError { From c699e5bd90160f9c881edc1f1d5efc02eda6a4bd Mon Sep 17 00:00:00 2001 From: Dudley Hartmann Date: Wed, 25 Dec 2024 10:48:12 +0800 Subject: [PATCH 3/4] fix: introduce missing dependencies --- .../java/com/beanit/iec61850bean/ClientAssociation.java | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/main/java/com/beanit/iec61850bean/ClientAssociation.java b/src/main/java/com/beanit/iec61850bean/ClientAssociation.java index 6138d8f..6d797cf 100644 --- a/src/main/java/com/beanit/iec61850bean/ClientAssociation.java +++ b/src/main/java/com/beanit/iec61850bean/ClientAssociation.java @@ -78,11 +78,7 @@ import java.net.InetAddress; import java.nio.ByteBuffer; import java.text.ParseException; import java.time.Instant; -import java.util.ArrayList; -import java.util.Calendar; -import java.util.Collection; -import java.util.Iterator; -import java.util.List; +import java.util.*; import java.util.concurrent.BlockingQueue; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.TimeUnit; From 0c863fa5187b29c764f5865bc342313372092b5d Mon Sep 17 00:00:00 2001 From: Dudley Hartmann Date: Wed, 25 Dec 2024 10:58:53 +0800 Subject: [PATCH 4/4] fix: Change the doBatchRead method to private --- src/main/java/com/beanit/iec61850bean/ClientAssociation.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/com/beanit/iec61850bean/ClientAssociation.java b/src/main/java/com/beanit/iec61850bean/ClientAssociation.java index 6d797cf..7cb6085 100644 --- a/src/main/java/com/beanit/iec61850bean/ClientAssociation.java +++ b/src/main/java/com/beanit/iec61850bean/ClientAssociation.java @@ -828,7 +828,7 @@ public final class ClientAssociation { return errorNodes; } - public Map doBatchRead(List nodeList) throws ServiceError, IOException { + private Map doBatchRead(List nodeList) throws ServiceError, IOException { ConfirmedServiceRequest serviceRequest = constructGetDataValueListRequest(nodeList); ConfirmedServiceResponse confirmedServiceResponse = encodeWriteReadDecode(serviceRequest); return decodeGetDataValueListResponse(confirmedServiceResponse, nodeList);