You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1652 lines
64 KiB
Java
1652 lines
64 KiB
Java
9 months ago
|
/*
|
||
|
* Copyright 2011 The IEC61850bean Authors
|
||
|
*
|
||
|
* 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 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.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 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.concurrent.Executors;
|
||
|
import java.util.concurrent.ScheduledExecutorService;
|
||
|
import java.util.concurrent.TimeoutException;
|
||
|
import org.slf4j.Logger;
|
||
|
import org.slf4j.LoggerFactory;
|
||
|
|
||
|
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"
|
||
|
};
|
||
|
|
||
|
static {
|
||
|
writeSuccess.setSuccess(new BerNull());
|
||
|
}
|
||
|
|
||
|
final ServerModel serverModel;
|
||
|
private final ServerSap serverSap;
|
||
|
private final ReverseByteArrayOutputStream reverseOStream =
|
||
|
new ReverseByteArrayOutputStream(500, true);
|
||
|
ScheduledExecutorService executor = null;
|
||
|
HashMap<String, DataSet> nonPersistentDataSets = new HashMap<>();
|
||
|
List<FcModelNode> selects = new ArrayList<>();
|
||
|
List<Urcb> rsvdURCBs = new ArrayList<>();
|
||
|
private AcseAssociation acseAssociation = null;
|
||
|
private int negotiatedMaxPduSize;
|
||
|
private ByteBuffer pduBuffer;
|
||
|
private boolean insertRef;
|
||
|
private String continueAfter;
|
||
|
|
||
|
public ServerAssociation(ServerSap serverSap) {
|
||
|
this.serverSap = serverSap;
|
||
|
serverModel = serverSap.serverModel;
|
||
|
executor =
|
||
|
Executors.newScheduledThreadPool(
|
||
|
2, new NamedThreadFactory("iec61850bean-server-connection"));
|
||
|
}
|
||
|
|
||
|
private static void insertMmsRef(ModelNode node, List<String> mmsRefs, String parentRef) {
|
||
|
String ref = parentRef + '$' + node.getName();
|
||
|
mmsRefs.add(ref);
|
||
|
if (!(node instanceof Array)) {
|
||
|
for (ModelNode childNode : node) {
|
||
|
insertMmsRef(childNode, mmsRefs, ref);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
private static String convertToDataSetReference(ObjectName mmsObjectName) {
|
||
|
if (mmsObjectName.getDomainSpecific() != null) {
|
||
|
return mmsObjectName.getDomainSpecific().getDomainID().toString()
|
||
|
+ "/"
|
||
|
+ mmsObjectName.getDomainSpecific().getItemID().toString().replace('$', '.');
|
||
|
} else if (mmsObjectName.getAaSpecific() != null) {
|
||
|
// format is "@DataSetName"
|
||
|
return mmsObjectName.getAaSpecific().toString();
|
||
|
}
|
||
|
return null;
|
||
|
}
|
||
|
|
||
|
public void handleNewAssociation(AcseAssociation acseAssociation, ByteBuffer associationRequest) {
|
||
|
|
||
|
this.acseAssociation = acseAssociation;
|
||
|
|
||
|
try {
|
||
|
associate(acseAssociation, associationRequest);
|
||
|
} catch (IOException e) {
|
||
|
logger.warn("Error during association build up", e);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
handleConnection();
|
||
|
}
|
||
|
|
||
|
private void associate(AcseAssociation acseAssociation, ByteBuffer associationRequest)
|
||
|
throws IOException {
|
||
|
|
||
|
MMSpdu mmsPdu = new MMSpdu();
|
||
|
|
||
|
mmsPdu.decode(new ByteBufferInputStream(associationRequest), null);
|
||
|
|
||
|
MMSpdu initiateResponseMmsPdu = constructAssociationResponsePdu(mmsPdu.getInitiateRequestPDU());
|
||
|
|
||
|
initiateResponseMmsPdu.encode(reverseOStream);
|
||
|
|
||
|
acseAssociation.accept(reverseOStream.getByteBuffer());
|
||
|
}
|
||
|
|
||
|
private MMSpdu constructAssociationResponsePdu(InitiateRequestPDU associationRequestMMSpdu) {
|
||
|
|
||
|
negotiatedMaxPduSize = serverSap.getMaxMmsPduSize();
|
||
|
|
||
|
if (associationRequestMMSpdu.getLocalDetailCalling() != null) {
|
||
|
int proposedMaxMmsPduSize = associationRequestMMSpdu.getLocalDetailCalling().intValue();
|
||
|
if (negotiatedMaxPduSize > proposedMaxMmsPduSize
|
||
|
&& proposedMaxMmsPduSize >= ServerSap.MINIMUM_MMS_PDU_SIZE) {
|
||
|
negotiatedMaxPduSize = proposedMaxMmsPduSize;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
int negotiatedMaxServOutstandingCalling = serverSap.getProposedMaxServOutstandingCalling();
|
||
|
int proposedMaxServOutstandingCalling =
|
||
|
associationRequestMMSpdu.getProposedMaxServOutstandingCalling().intValue();
|
||
|
|
||
|
if (negotiatedMaxServOutstandingCalling > proposedMaxServOutstandingCalling
|
||
|
&& proposedMaxServOutstandingCalling > 0) {
|
||
|
negotiatedMaxServOutstandingCalling = proposedMaxServOutstandingCalling;
|
||
|
}
|
||
|
|
||
|
int negotiatedMaxServOutstandingCalled = serverSap.getProposedMaxServOutstandingCalled();
|
||
|
int proposedMaxServOutstandingCalled =
|
||
|
associationRequestMMSpdu.getProposedMaxServOutstandingCalled().intValue();
|
||
|
|
||
|
if (negotiatedMaxServOutstandingCalled > proposedMaxServOutstandingCalled
|
||
|
&& proposedMaxServOutstandingCalled > 0) {
|
||
|
negotiatedMaxServOutstandingCalled = proposedMaxServOutstandingCalled;
|
||
|
}
|
||
|
|
||
|
int negotiatedDataStructureNestingLevel = serverSap.getProposedDataStructureNestingLevel();
|
||
|
|
||
|
if (associationRequestMMSpdu.getProposedDataStructureNestingLevel() != null) {
|
||
|
int proposedDataStructureNestingLevel =
|
||
|
associationRequestMMSpdu.getProposedDataStructureNestingLevel().intValue();
|
||
|
if (negotiatedDataStructureNestingLevel > proposedDataStructureNestingLevel) {
|
||
|
negotiatedDataStructureNestingLevel = proposedDataStructureNestingLevel;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
pduBuffer = ByteBuffer.allocate(negotiatedMaxPduSize + 500);
|
||
|
|
||
|
byte[] negotiatedParameterCbbBitString = serverSap.cbbBitString;
|
||
|
|
||
|
byte[] servicesSupportedCalledBitString = serverSap.servicesSupportedCalled;
|
||
|
|
||
|
InitiateResponsePDU.InitResponseDetail initRespDetail =
|
||
|
new InitiateResponsePDU.InitResponseDetail();
|
||
|
initRespDetail.setNegotiatedVersionNumber(new Integer16(1));
|
||
|
initRespDetail.setNegotiatedParameterCBB(
|
||
|
new ParameterSupportOptions(
|
||
|
negotiatedParameterCbbBitString, negotiatedParameterCbbBitString.length * 8 - 5));
|
||
|
initRespDetail.setServicesSupportedCalled(
|
||
|
new ServiceSupportOptions(
|
||
|
servicesSupportedCalledBitString, servicesSupportedCalledBitString.length * 8 - 3));
|
||
|
|
||
|
InitiateResponsePDU initRespPdu = new InitiateResponsePDU();
|
||
|
initRespPdu.setLocalDetailCalled(new Integer32(negotiatedMaxPduSize));
|
||
|
initRespPdu.setNegotiatedMaxServOutstandingCalling(
|
||
|
new Integer16(negotiatedMaxServOutstandingCalling));
|
||
|
initRespPdu.setNegotiatedMaxServOutstandingCalled(
|
||
|
new Integer16(negotiatedMaxServOutstandingCalled));
|
||
|
initRespPdu.setNegotiatedDataStructureNestingLevel(
|
||
|
new Integer8(negotiatedDataStructureNestingLevel));
|
||
|
initRespPdu.setInitResponseDetail(initRespDetail);
|
||
|
|
||
|
MMSpdu initiateResponseMMSpdu = new MMSpdu();
|
||
|
initiateResponseMMSpdu.setInitiateResponsePDU(initRespPdu);
|
||
|
|
||
|
return initiateResponseMMSpdu;
|
||
|
}
|
||
|
|
||
|
private void handleConnection() {
|
||
|
|
||
|
while (true) {
|
||
|
|
||
|
MMSpdu mmsRequestPdu = listenForMmsRequest(acseAssociation);
|
||
|
if (mmsRequestPdu == null) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
ConfirmedRequestPDU confirmedRequestPdu = mmsRequestPdu.getConfirmedRequestPDU();
|
||
|
// Do not have to check whether confirmedRequestPdu is null because that was already done by
|
||
|
// listenForMmsRequest()
|
||
|
|
||
|
if (confirmedRequestPdu.getInvokeID() == null) {
|
||
|
// cannot respond with ServiceError because no InvokeID was received
|
||
|
logger.warn("Got unexpected MMS PDU or no invokeID");
|
||
|
continue;
|
||
|
}
|
||
|
int invokeId = confirmedRequestPdu.getInvokeID().intValue();
|
||
|
|
||
|
try {
|
||
|
if (confirmedRequestPdu.getService() == null) {
|
||
|
throw new ServiceError(
|
||
|
ServiceError.FAILED_DUE_TO_COMMUNICATIONS_CONSTRAINT,
|
||
|
"Got an invalid MMS packet: confirmedServiceRequest empty");
|
||
|
}
|
||
|
|
||
|
ConfirmedServiceRequest confirmedServiceRequest = confirmedRequestPdu.getService();
|
||
|
|
||
|
ConfirmedServiceResponse confirmedServiceResponse = new ConfirmedServiceResponse();
|
||
|
|
||
|
if (confirmedServiceRequest.getGetNameList() != null) {
|
||
|
|
||
|
GetNameListRequest getNameListRequest = confirmedServiceRequest.getGetNameList();
|
||
|
GetNameListResponse response = null;
|
||
|
|
||
|
if (getNameListRequest.getObjectClass().getBasicObjectClass() == null) {
|
||
|
throw new ServiceError(
|
||
|
ServiceError.FAILED_DUE_TO_COMMUNICATIONS_CONSTRAINT,
|
||
|
"Got an invalid MMS packet: ObjectClass was not selected in GetNameList request");
|
||
|
}
|
||
|
|
||
|
long basicObjectClass =
|
||
|
getNameListRequest.getObjectClass().getBasicObjectClass().longValue();
|
||
|
if (basicObjectClass == 9) {
|
||
|
logger.debug("Got a GetServerDirectory (MMS GetNameList[DOMAIN]) request");
|
||
|
response = handleGetServerDirectoryRequest();
|
||
|
} else if (basicObjectClass == 0) {
|
||
|
logger.debug("Got a Get{LD|LN}Directory (MMS GetNameList[NAMED_VARIABLE]) request");
|
||
|
response = handleGetDirectoryRequest(getNameListRequest);
|
||
|
} else if (basicObjectClass == 2) {
|
||
|
logger.debug(
|
||
|
"Got a GetLogicalNodeDirectory[DataSet] (MMS GetNameList[NAMED_VARIABLE_LIST]) request");
|
||
|
response = handleGetDataSetNamesRequest(getNameListRequest);
|
||
|
} else {
|
||
|
throw new ServiceError(
|
||
|
ServiceError.FAILED_DUE_TO_SERVER_CONSTRAINT,
|
||
|
"Unable to handle Get directory request for basic object class: "
|
||
|
+ basicObjectClass);
|
||
|
}
|
||
|
// else if (basicObjectClass == 8) {
|
||
|
// logger.debug("Got a GetLogicalNodeDirectory[Log] (MMS GetNameList[JOURNAL]) request");
|
||
|
// response =
|
||
|
// handleGetNameListJournalRequest(getNameListRequest);
|
||
|
// }
|
||
|
|
||
|
confirmedServiceResponse.setGetNameList(response);
|
||
|
|
||
|
} else if (confirmedServiceRequest.getGetVariableAccessAttributes() != null) {
|
||
|
logger.debug(
|
||
|
"Got a GetDataDirectory/GetDataDefinition (MMS GetVariableAccessAttributes) request");
|
||
|
GetVariableAccessAttributesResponse response =
|
||
|
handleGetVariableAccessAttributesRequest(
|
||
|
confirmedServiceRequest.getGetVariableAccessAttributes());
|
||
|
|
||
|
confirmedServiceResponse.setGetVariableAccessAttributes(response);
|
||
|
|
||
|
} else if (confirmedServiceRequest.getRead() != null) {
|
||
|
// GetDataValues, GetDataSetValues, GetBRCBValues and GetURCBValues map to this
|
||
|
ReadResponse response = handleGetDataValuesRequest(confirmedServiceRequest.getRead());
|
||
|
|
||
|
confirmedServiceResponse.setRead(response);
|
||
|
} else if (confirmedServiceRequest.getWrite() != null) {
|
||
|
logger.debug("Got a Write request");
|
||
|
|
||
|
WriteResponse response = handleSetDataValuesRequest(confirmedServiceRequest.getWrite());
|
||
|
|
||
|
confirmedServiceResponse.setWrite(response);
|
||
|
|
||
|
}
|
||
|
// for Data Sets
|
||
|
else if (confirmedServiceRequest.getDefineNamedVariableList() != null) {
|
||
|
logger.debug("Got a CreateDataSet request");
|
||
|
|
||
|
DefineNamedVariableListResponse response =
|
||
|
handleCreateDataSetRequest(confirmedServiceRequest.getDefineNamedVariableList());
|
||
|
|
||
|
confirmedServiceResponse.setDefineNamedVariableList(response);
|
||
|
} else if (confirmedServiceRequest.getGetNamedVariableListAttributes() != null) {
|
||
|
logger.debug("Got a GetDataSetDirectory request");
|
||
|
GetNamedVariableListAttributesResponse response =
|
||
|
handleGetDataSetDirectoryRequest(
|
||
|
confirmedServiceRequest.getGetNamedVariableListAttributes());
|
||
|
|
||
|
confirmedServiceResponse.setGetNamedVariableListAttributes(response);
|
||
|
|
||
|
} else if (confirmedServiceRequest.getDeleteNamedVariableList() != null) {
|
||
|
logger.debug("Got a DeleteDataSet request");
|
||
|
DeleteNamedVariableListResponse response =
|
||
|
handleDeleteDataSetRequest(confirmedServiceRequest.getDeleteNamedVariableList());
|
||
|
|
||
|
confirmedServiceResponse.setDeleteNamedVariableList(response);
|
||
|
} else {
|
||
|
throw new ServiceError(
|
||
|
ServiceError.FAILED_DUE_TO_COMMUNICATIONS_CONSTRAINT,
|
||
|
"invalid MMS packet: unknown request type.");
|
||
|
}
|
||
|
|
||
|
ConfirmedResponsePDU confirmedResponsePdu = new ConfirmedResponsePDU();
|
||
|
confirmedResponsePdu.setInvokeID(confirmedRequestPdu.getInvokeID());
|
||
|
confirmedResponsePdu.setService(confirmedServiceResponse);
|
||
|
|
||
|
MMSpdu mmsResponsePdu = new MMSpdu();
|
||
|
mmsResponsePdu.setConfirmedResponsePDU(confirmedResponsePdu);
|
||
|
|
||
|
if (!sendAnMmsPdu(mmsResponsePdu)) {
|
||
|
return;
|
||
|
}
|
||
|
} catch (ServiceError e) {
|
||
|
logger.warn(e.getMessage());
|
||
|
if (!sendAnMmsPdu(createServiceErrorResponse(e, invokeId))) {
|
||
|
return;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void cleanUpConnection() {
|
||
|
synchronized (serverModel) {
|
||
|
for (FcModelNode selectedCdo : selects) {
|
||
|
selectedCdo.deselect();
|
||
|
}
|
||
|
for (Urcb rsvdUrcb : rsvdURCBs) {
|
||
|
synchronized (rsvdUrcb) {
|
||
|
if (rsvdUrcb.enabled) {
|
||
|
rsvdUrcb.disable();
|
||
|
}
|
||
|
rsvdUrcb.reserved = null;
|
||
|
rsvdUrcb.getResv().setValue(false);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
boolean sendAnMmsPdu(MMSpdu mmsResponsePdu) {
|
||
|
|
||
|
synchronized (reverseOStream) {
|
||
|
reverseOStream.reset();
|
||
|
try {
|
||
|
mmsResponsePdu.encode(reverseOStream);
|
||
|
} catch (IOException e1) {
|
||
|
logger.error("IOException while encoding MMS PDU. Closing association.", e1);
|
||
|
return false;
|
||
|
}
|
||
|
try {
|
||
|
acseAssociation.send(reverseOStream.getByteBuffer());
|
||
|
} catch (IOException e) {
|
||
|
logger.warn("IOException while sending MMS PDU. Closing association.", e);
|
||
|
return false;
|
||
|
}
|
||
|
}
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
private MMSpdu listenForMmsRequest(AcseAssociation acseAssociation) {
|
||
|
|
||
|
while (true) {
|
||
|
MMSpdu mmsRequestPdu;
|
||
|
byte[] buffer;
|
||
|
pduBuffer.clear();
|
||
|
try {
|
||
|
buffer = acseAssociation.receive(pduBuffer);
|
||
|
} catch (EOFException e) {
|
||
|
logger.debug("Connection was closed by client.");
|
||
|
return null;
|
||
|
} catch (SocketTimeoutException e) {
|
||
|
logger.warn(
|
||
|
"Message fragment timeout occured while receiving request. Closing association.", e);
|
||
|
return null;
|
||
|
} catch (IOException e) {
|
||
|
logger.warn(
|
||
|
"IOException at lower layers while listening for incoming request. Closing association.",
|
||
|
e);
|
||
|
return null;
|
||
|
} catch (DecodingException e) {
|
||
|
logger.error("Error decoding request at OSI layers.", e);
|
||
|
continue;
|
||
|
} catch (TimeoutException e) {
|
||
|
logger.error(
|
||
|
"Illegal state: message timeout while receiving request though this timeout should 0 and never be thrown",
|
||
|
e);
|
||
|
return null;
|
||
|
}
|
||
|
mmsRequestPdu = new MMSpdu();
|
||
|
|
||
|
try {
|
||
|
mmsRequestPdu.decode(new ByteArrayInputStream(buffer), null);
|
||
|
} catch (IOException e) {
|
||
|
logger.warn("IOException decoding received MMS request PDU.", e);
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
if (mmsRequestPdu.getConfirmedRequestPDU() == null) {
|
||
|
if (mmsRequestPdu.getConcludeRequestPDU() != null) {
|
||
|
logger.debug("Got Conclude request, will close connection");
|
||
|
return null;
|
||
|
} else {
|
||
|
logger.warn("Got unexpected MMS PDU, will ignore it");
|
||
|
continue;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return mmsRequestPdu;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
private MMSpdu createServiceErrorResponse(ServiceError e, int invokeId) {
|
||
|
|
||
|
ErrorClass errClass = new ErrorClass();
|
||
|
|
||
|
switch (e.getErrorCode()) {
|
||
|
case ServiceError.NO_ERROR:
|
||
|
break;
|
||
|
case ServiceError.INSTANCE_NOT_AVAILABLE:
|
||
|
errClass.setAccess(new BerInteger(e.getErrorCode()));
|
||
|
break;
|
||
|
case ServiceError.INSTANCE_IN_USE:
|
||
|
errClass.setDefinition(new BerInteger(e.getErrorCode()));
|
||
|
break;
|
||
|
case ServiceError.ACCESS_VIOLATION:
|
||
|
errClass.setAccess(new BerInteger(e.getErrorCode()));
|
||
|
break;
|
||
|
case ServiceError.ACCESS_NOT_ALLOWED_IN_CURRENT_STATE:
|
||
|
errClass.setOthers(new BerInteger(e.getErrorCode()));
|
||
|
break;
|
||
|
case ServiceError.INSTANCE_LOCKED_BY_OTHER_CLIENT:
|
||
|
errClass.setFile(new BerInteger(2));
|
||
|
break;
|
||
|
case ServiceError.TYPE_CONFLICT:
|
||
|
errClass.setFile(new BerInteger(4));
|
||
|
break;
|
||
|
default:
|
||
|
errClass.setOthers(new BerInteger(e.getErrorCode()));
|
||
|
}
|
||
|
com.beanit.iec61850bean.internal.mms.asn1.ServiceError asn1ServiceError;
|
||
|
|
||
|
asn1ServiceError = new com.beanit.iec61850bean.internal.mms.asn1.ServiceError();
|
||
|
asn1ServiceError.setErrorClass(errClass);
|
||
|
asn1ServiceError.setAdditionalDescription(new BerVisibleString(e.getMessage()));
|
||
|
|
||
|
ConfirmedErrorPDU confirmedErrorPDU = new ConfirmedErrorPDU();
|
||
|
confirmedErrorPDU.setInvokeID(new Unsigned32(invokeId));
|
||
|
confirmedErrorPDU.setServiceError(asn1ServiceError);
|
||
|
|
||
|
MMSpdu mmsPdu = new MMSpdu();
|
||
|
mmsPdu.setConfirmedErrorPDU(confirmedErrorPDU);
|
||
|
|
||
|
return mmsPdu;
|
||
|
}
|
||
|
|
||
|
private GetNameListResponse handleGetServerDirectoryRequest() throws ServiceError {
|
||
|
|
||
|
ListOfIdentifier listOfIdentifier = new ListOfIdentifier();
|
||
|
List<Identifier> identifiers = listOfIdentifier.getIdentifier();
|
||
|
|
||
|
for (ModelNode ld : serverModel) {
|
||
|
identifiers.add(new Identifier(ld.getName().getBytes(UTF_8)));
|
||
|
}
|
||
|
|
||
|
GetNameListResponse getNameListResponse = new GetNameListResponse();
|
||
|
getNameListResponse.setListOfIdentifier(listOfIdentifier);
|
||
|
getNameListResponse.setMoreFollows(new BerBoolean(false));
|
||
|
|
||
|
return getNameListResponse;
|
||
|
}
|
||
|
|
||
|
private GetNameListResponse handleGetDirectoryRequest(GetNameListRequest getNameListRequest)
|
||
|
throws ServiceError {
|
||
|
|
||
|
// the ObjectScope can be vmdSpecific,domainSpecific, or aaSpecific. vmdSpecific and aaSpecific
|
||
|
// are not part of
|
||
|
// 61850-8-1 but are used by some IEC 61850 clients anyways. This stack will return an empty
|
||
|
// list on vmdSpecific
|
||
|
// and aaSpecific requests.
|
||
|
if (getNameListRequest.getObjectScope().getAaSpecific() != null
|
||
|
|| getNameListRequest.getObjectScope().getVmdSpecific() != null) {
|
||
|
ListOfIdentifier listOfIden = new ListOfIdentifier();
|
||
|
listOfIden.getIdentifier();
|
||
|
GetNameListResponse getNameListResponse = new GetNameListResponse();
|
||
|
getNameListResponse.setListOfIdentifier(listOfIden);
|
||
|
getNameListResponse.setMoreFollows(new BerBoolean(false));
|
||
|
return getNameListResponse;
|
||
|
}
|
||
|
|
||
|
String mmsDomainId = getNameListRequest.getObjectScope().getDomainSpecific().toString();
|
||
|
|
||
|
ModelNode logicalDeviceMn = serverModel.getChild(mmsDomainId);
|
||
|
|
||
|
if (logicalDeviceMn == null) {
|
||
|
throw new ServiceError(
|
||
|
ServiceError.PARAMETER_VALUE_INAPPROPRIATE,
|
||
|
"Got an invalid MMS request: given Domain name in GetNameList request is not a Logical Device name");
|
||
|
}
|
||
|
|
||
|
LogicalDevice logicalDevice = (LogicalDevice) logicalDeviceMn;
|
||
|
|
||
|
insertRef = true;
|
||
|
|
||
|
if (getNameListRequest.getContinueAfter() != null) {
|
||
|
continueAfter = getNameListRequest.getContinueAfter().toString();
|
||
|
insertRef = false;
|
||
|
}
|
||
|
|
||
|
List<String> mmsReferences = new ArrayList<>();
|
||
|
|
||
|
for (ModelNode logicalNodeMn : logicalDevice) {
|
||
|
LogicalNode logicalNode = (LogicalNode) logicalNodeMn;
|
||
|
mmsReferences.add(logicalNode.getName());
|
||
|
|
||
|
for (String mmsFC : mmsFcs) {
|
||
|
Fc fc = Fc.fromString(mmsFC);
|
||
|
if (fc != null) {
|
||
|
|
||
|
List<FcDataObject> fcDataObjects = logicalNode.getChildren(fc);
|
||
|
if (fcDataObjects != null) {
|
||
|
mmsReferences.add(logicalNode.getName() + "$" + mmsFC);
|
||
|
for (FcDataObject dataObject : fcDataObjects) {
|
||
|
insertMmsRef(dataObject, mmsReferences, logicalNode.getName() + "$" + mmsFC);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
ListOfIdentifier listOfIden = new ListOfIdentifier();
|
||
|
List<Identifier> identifiers = listOfIden.getIdentifier();
|
||
|
|
||
|
int identifierSize = 0;
|
||
|
boolean moreFollows = false;
|
||
|
for (String mmsReference : mmsReferences) {
|
||
|
if (insertRef == true) {
|
||
|
if (identifierSize > negotiatedMaxPduSize - 200) {
|
||
|
moreFollows = true;
|
||
|
logger.debug(" ->maxMMSPduSize of " + negotiatedMaxPduSize + " Bytes reached");
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
Identifier identifier;
|
||
|
|
||
|
identifier = new Identifier(mmsReference.getBytes(UTF_8));
|
||
|
|
||
|
identifiers.add(identifier);
|
||
|
identifierSize += mmsReference.length() + 2;
|
||
|
} else {
|
||
|
if (mmsReference.equals(continueAfter)) {
|
||
|
insertRef = true;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
GetNameListResponse getNameListResponse = new GetNameListResponse();
|
||
|
getNameListResponse.setListOfIdentifier(listOfIden);
|
||
|
getNameListResponse.setMoreFollows(new BerBoolean(moreFollows));
|
||
|
|
||
|
return getNameListResponse;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* GetVariableAccessAttributes (GetDataDefinition/GetDataDirectory) can be called with different
|
||
|
* kinds of references. Examples: 1. DGEN1 2. DGEN1$CF 3. DGEN1$CF$GnBlk
|
||
|
*/
|
||
|
private GetVariableAccessAttributesResponse handleGetVariableAccessAttributesRequest(
|
||
|
GetVariableAccessAttributesRequest getVariableAccessAttributesRequest) throws ServiceError {
|
||
|
if (getVariableAccessAttributesRequest.getName() == null) {
|
||
|
throw new ServiceError(
|
||
|
ServiceError.FAILED_DUE_TO_COMMUNICATIONS_CONSTRAINT,
|
||
|
"Got an invalid MMS packet: name is not selected in GetVariableAccessAttributesRequest");
|
||
|
}
|
||
|
|
||
|
DomainSpecific domainSpecific =
|
||
|
getVariableAccessAttributesRequest.getName().getDomainSpecific();
|
||
|
|
||
|
if (domainSpecific == null) {
|
||
|
throw new ServiceError(
|
||
|
ServiceError.FAILED_DUE_TO_COMMUNICATIONS_CONSTRAINT,
|
||
|
"Got an invalid MMS packet: Domain specific is not selected in GetVariableAccessAttributesRequest");
|
||
|
}
|
||
|
|
||
|
ModelNode modelNode = serverModel.getChild(domainSpecific.getDomainID().toString());
|
||
|
|
||
|
if (modelNode == null) {
|
||
|
throw new ServiceError(
|
||
|
ServiceError.INSTANCE_NOT_AVAILABLE,
|
||
|
"GetVariableAccessAttributes (GetDataDefinition): no object with domainId "
|
||
|
+ getVariableAccessAttributesRequest.getName().getDomainSpecific().getDomainID()
|
||
|
+ " and ItemID "
|
||
|
+ getVariableAccessAttributesRequest.getName().getDomainSpecific().getItemID()
|
||
|
+ " was found.");
|
||
|
}
|
||
|
|
||
|
String itemIdString = domainSpecific.getItemID().toString();
|
||
|
|
||
|
int index1 = itemIdString.indexOf('$');
|
||
|
|
||
|
LogicalNode logicalNode = null;
|
||
|
|
||
|
if (index1 != -1) {
|
||
|
logicalNode = (LogicalNode) modelNode.getChild(itemIdString.substring(0, index1));
|
||
|
if (logicalNode == null) {
|
||
|
throw new ServiceError(
|
||
|
ServiceError.INSTANCE_NOT_AVAILABLE,
|
||
|
"GetVariableAccessAttributes (GetDataDefinition): no object with domainId "
|
||
|
+ getVariableAccessAttributesRequest.getName().getDomainSpecific().getDomainID()
|
||
|
+ " and ItemID "
|
||
|
+ getVariableAccessAttributesRequest.getName().getDomainSpecific().getItemID()
|
||
|
+ " was found.");
|
||
|
}
|
||
|
int index2 = itemIdString.indexOf('$', index1 + 2);
|
||
|
if (index2 != -1) {
|
||
|
Fc fc = Fc.fromString(itemIdString.substring(index1 + 1, index2));
|
||
|
if (fc == null) {
|
||
|
throw new ServiceError(
|
||
|
ServiceError.INSTANCE_NOT_AVAILABLE,
|
||
|
"GetVariableAccessAttributes (GetDataDefinition): no object with domainId "
|
||
|
+ getVariableAccessAttributesRequest.getName().getDomainSpecific().getDomainID()
|
||
|
+ " and ItemID "
|
||
|
+ getVariableAccessAttributesRequest.getName().getDomainSpecific().getItemID()
|
||
|
+ " was found.");
|
||
|
}
|
||
|
index1 = itemIdString.indexOf('$', index2 + 2);
|
||
|
ModelNode subNode;
|
||
|
if (index1 == -1) {
|
||
|
subNode = logicalNode.getChild(itemIdString.substring(index2 + 1), fc);
|
||
|
if (subNode == null) {
|
||
|
throw new ServiceError(
|
||
|
ServiceError.INSTANCE_NOT_AVAILABLE,
|
||
|
"GetVariableAccessAttributes (GetDataDefinition): no object with domainId "
|
||
|
+ getVariableAccessAttributesRequest.getName().getDomainSpecific().getDomainID()
|
||
|
+ " and ItemID "
|
||
|
+ getVariableAccessAttributesRequest.getName().getDomainSpecific().getItemID()
|
||
|
+ " was found.");
|
||
|
}
|
||
|
} else {
|
||
|
subNode = logicalNode.getChild(itemIdString.substring(index2 + 1, index1), fc);
|
||
|
if (subNode == null) {
|
||
|
throw new ServiceError(
|
||
|
ServiceError.INSTANCE_NOT_AVAILABLE,
|
||
|
"GetVariableAccessAttributes (GetDataDefinition): no object with domainId "
|
||
|
+ getVariableAccessAttributesRequest.getName().getDomainSpecific().getDomainID()
|
||
|
+ " and ItemID "
|
||
|
+ getVariableAccessAttributesRequest.getName().getDomainSpecific().getItemID()
|
||
|
+ " was found.");
|
||
|
}
|
||
|
index2 = itemIdString.indexOf('$', index1 + 2);
|
||
|
while (index2 != -1) {
|
||
|
subNode = subNode.getChild(itemIdString.substring(index1 + 1, index2));
|
||
|
if (subNode == null) {
|
||
|
throw new ServiceError(
|
||
|
ServiceError.INSTANCE_NOT_AVAILABLE,
|
||
|
"GetVariableAccessAttributes (GetDataDefinition): no object with domainId "
|
||
|
+ getVariableAccessAttributesRequest
|
||
|
.getName()
|
||
|
.getDomainSpecific()
|
||
|
.getDomainID()
|
||
|
+ " and ItemID "
|
||
|
+ getVariableAccessAttributesRequest.getName().getDomainSpecific().getItemID()
|
||
|
+ " was found.");
|
||
|
}
|
||
|
index1 = index2;
|
||
|
index2 = itemIdString.indexOf('$', index1 + 2);
|
||
|
}
|
||
|
subNode = subNode.getChild(itemIdString.substring(index1 + 1));
|
||
|
if (subNode == null) {
|
||
|
throw new ServiceError(
|
||
|
ServiceError.INSTANCE_NOT_AVAILABLE,
|
||
|
"GetVariableAccessAttributes (GetDataDefinition): no object with domainId "
|
||
|
+ getVariableAccessAttributesRequest.getName().getDomainSpecific().getDomainID()
|
||
|
+ " and ItemID "
|
||
|
+ getVariableAccessAttributesRequest.getName().getDomainSpecific().getItemID()
|
||
|
+ " was found.");
|
||
|
}
|
||
|
}
|
||
|
|
||
|
GetVariableAccessAttributesResponse getVariableAccessAttributesResponse =
|
||
|
new GetVariableAccessAttributesResponse();
|
||
|
getVariableAccessAttributesResponse.setMmsDeletable(new BerBoolean(false));
|
||
|
getVariableAccessAttributesResponse.setTypeDescription(subNode.getMmsTypeSpec());
|
||
|
|
||
|
return getVariableAccessAttributesResponse;
|
||
|
} else {
|
||
|
Fc fc = Fc.fromString(itemIdString.substring(index1 + 1));
|
||
|
|
||
|
if (fc == null) {
|
||
|
throw new ServiceError(
|
||
|
ServiceError.INSTANCE_NOT_AVAILABLE,
|
||
|
"GetVariableAccessAttributes (GetDataDefinition): no object with domainId "
|
||
|
+ getVariableAccessAttributesRequest.getName().getDomainSpecific().getDomainID()
|
||
|
+ " and ItemID "
|
||
|
+ getVariableAccessAttributesRequest.getName().getDomainSpecific().getItemID()
|
||
|
+ " was found.");
|
||
|
}
|
||
|
|
||
|
List<FcDataObject> fcDataObjects = logicalNode.getChildren(fc);
|
||
|
|
||
|
if (fcDataObjects == null || fcDataObjects.size() == 0) {
|
||
|
throw new ServiceError(
|
||
|
ServiceError.INSTANCE_NOT_AVAILABLE,
|
||
|
"GetVariableAccessAttributes (GetDataDefinition): no object with domainId "
|
||
|
+ getVariableAccessAttributesRequest.getName().getDomainSpecific().getDomainID()
|
||
|
+ " and ItemID "
|
||
|
+ getVariableAccessAttributesRequest.getName().getDomainSpecific().getItemID()
|
||
|
+ " was found.");
|
||
|
}
|
||
|
|
||
|
Components comp = new Components();
|
||
|
List<TypeDescription.Structure.Components.SEQUENCE> doStructComponents = comp.getSEQUENCE();
|
||
|
for (ModelNode child : fcDataObjects) {
|
||
|
TypeSpecification typeSpecification = new TypeSpecification();
|
||
|
typeSpecification.setTypeDescription(child.getMmsTypeSpec());
|
||
|
|
||
|
TypeDescription.Structure.Components.SEQUENCE structComponent =
|
||
|
new TypeDescription.Structure.Components.SEQUENCE();
|
||
|
structComponent.setComponentName(new Identifier(child.getName().getBytes(UTF_8)));
|
||
|
structComponent.setComponentType(typeSpecification);
|
||
|
doStructComponents.add(structComponent);
|
||
|
}
|
||
|
|
||
|
Structure struct = new Structure();
|
||
|
struct.setComponents(comp);
|
||
|
|
||
|
TypeDescription typeDescription = new TypeDescription();
|
||
|
typeDescription.setStructure(struct);
|
||
|
|
||
|
GetVariableAccessAttributesResponse getVariableAccessAttributesResponse =
|
||
|
new GetVariableAccessAttributesResponse();
|
||
|
getVariableAccessAttributesResponse.setMmsDeletable(new BerBoolean(false));
|
||
|
getVariableAccessAttributesResponse.setTypeDescription(typeDescription);
|
||
|
|
||
|
return getVariableAccessAttributesResponse;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
logicalNode = (LogicalNode) modelNode.getChild(itemIdString);
|
||
|
if (logicalNode == null) {
|
||
|
throw new ServiceError(
|
||
|
ServiceError.INSTANCE_NOT_AVAILABLE,
|
||
|
"GetVariableAccessAttributes (GetDataDefinition): no object with domainId "
|
||
|
+ getVariableAccessAttributesRequest.getName().getDomainSpecific().getDomainID()
|
||
|
+ " and ItemID "
|
||
|
+ getVariableAccessAttributesRequest.getName().getDomainSpecific().getItemID()
|
||
|
+ " was found.");
|
||
|
}
|
||
|
|
||
|
Components components = new Components();
|
||
|
List<TypeDescription.Structure.Components.SEQUENCE> structComponents = components.getSEQUENCE();
|
||
|
|
||
|
for (String mmsFc : mmsFcs) {
|
||
|
Fc fc = Fc.fromString(mmsFc);
|
||
|
if (fc != null) {
|
||
|
|
||
|
Collection<FcDataObject> fcDataObjects = logicalNode.getChildren(fc);
|
||
|
if (fcDataObjects == null) {
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
Components comp = new Components();
|
||
|
List<TypeDescription.Structure.Components.SEQUENCE> doStructComponents = comp.getSEQUENCE();
|
||
|
|
||
|
for (ModelNode child : fcDataObjects) {
|
||
|
TypeSpecification typeSpecification = new TypeSpecification();
|
||
|
typeSpecification.setTypeDescription(child.getMmsTypeSpec());
|
||
|
|
||
|
TypeDescription.Structure.Components.SEQUENCE doStructComponent =
|
||
|
new TypeDescription.Structure.Components.SEQUENCE();
|
||
|
doStructComponent.setComponentName(new Identifier(child.getName().getBytes(UTF_8)));
|
||
|
doStructComponent.setComponentType(typeSpecification);
|
||
|
|
||
|
doStructComponents.add(doStructComponent);
|
||
|
}
|
||
|
|
||
|
Structure struct = new Structure();
|
||
|
struct.setComponents(comp);
|
||
|
|
||
|
TypeDescription fcTypeSpec = new TypeDescription();
|
||
|
fcTypeSpec.setStructure(struct);
|
||
|
|
||
|
TypeSpecification typeSpecification = new TypeSpecification();
|
||
|
typeSpecification.setTypeDescription(fcTypeSpec);
|
||
|
|
||
|
TypeDescription.Structure.Components.SEQUENCE structCom =
|
||
|
new TypeDescription.Structure.Components.SEQUENCE();
|
||
|
structCom.setComponentName(new Identifier(mmsFc.getBytes(UTF_8)));
|
||
|
structCom.setComponentType(typeSpecification);
|
||
|
|
||
|
structComponents.add(structCom);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
Structure struct = new Structure();
|
||
|
struct.setComponents(components);
|
||
|
|
||
|
TypeDescription typeSpec = new TypeDescription();
|
||
|
typeSpec.setStructure(struct);
|
||
|
|
||
|
GetVariableAccessAttributesResponse getVariableAccessAttributesResponse =
|
||
|
new GetVariableAccessAttributesResponse();
|
||
|
getVariableAccessAttributesResponse.setMmsDeletable(new BerBoolean(false));
|
||
|
getVariableAccessAttributesResponse.setTypeDescription(typeSpec);
|
||
|
|
||
|
return getVariableAccessAttributesResponse;
|
||
|
}
|
||
|
|
||
|
private ReadResponse handleGetDataValuesRequest(ReadRequest mmsReadRequest) throws ServiceError {
|
||
|
|
||
|
VariableAccessSpecification variableAccessSpecification =
|
||
|
mmsReadRequest.getVariableAccessSpecification();
|
||
|
|
||
|
if (mmsReadRequest.getSpecificationWithResult() == null
|
||
|
|| mmsReadRequest.getSpecificationWithResult().value == false) {
|
||
|
|
||
|
if (variableAccessSpecification.getListOfVariable() == null) {
|
||
|
throw new ServiceError(
|
||
|
ServiceError.FAILED_DUE_TO_COMMUNICATIONS_CONSTRAINT,
|
||
|
"handleGetDataValuesRequest: Got an invalid MMS packet");
|
||
|
}
|
||
|
|
||
|
List<VariableDefs.SEQUENCE> listOfVariable =
|
||
|
variableAccessSpecification.getListOfVariable().getSEQUENCE();
|
||
|
|
||
|
if (listOfVariable.size() < 1) {
|
||
|
throw new ServiceError(
|
||
|
ServiceError.PARAMETER_VALUE_INCONSISTENT,
|
||
|
"handleGetDataValuesRequest: less than one variableAccessSpecification is not allowed");
|
||
|
}
|
||
|
|
||
|
ListOfAccessResult listOfAccessResult = new ListOfAccessResult();
|
||
|
List<AccessResult> accessResults = listOfAccessResult.getAccessResult();
|
||
|
|
||
|
synchronized (serverModel) {
|
||
|
for (VariableDefs.SEQUENCE variableDef : listOfVariable) {
|
||
|
|
||
|
FcModelNode modelNode = serverModel.getNodeFromVariableDef(variableDef);
|
||
|
|
||
|
if (modelNode == null) {
|
||
|
logger.debug("Got a GetDataValues request for a non existent model node.");
|
||
|
// 10 indicates error "object-non-existent"
|
||
|
AccessResult accessResult = new AccessResult();
|
||
|
accessResult.setFailure(new DataAccessError(10L));
|
||
|
accessResults.add(accessResult);
|
||
|
} else {
|
||
|
if (logger.isDebugEnabled()) {
|
||
|
logger.debug("Got a GetDataValues request for node: " + modelNode);
|
||
|
if (!(modelNode instanceof BasicDataAttribute)) {
|
||
|
for (BasicDataAttribute bda : modelNode.getBasicDataAttributes()) {
|
||
|
logger.debug("sub BDA is:" + bda);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
accessResults.add(getReadResult(modelNode));
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
ReadResponse readResponse = new ReadResponse();
|
||
|
readResponse.setListOfAccessResult(listOfAccessResult);
|
||
|
return readResponse;
|
||
|
} else {
|
||
|
logger.debug("Got a GetDataSetValues request.");
|
||
|
|
||
|
String dataSetReference =
|
||
|
convertToDataSetReference(variableAccessSpecification.getVariableListName());
|
||
|
|
||
|
if (dataSetReference == null) {
|
||
|
throw new ServiceError(
|
||
|
ServiceError.PARAMETER_VALUE_INCONSISTENT,
|
||
|
"handleGetDataSetValuesRequest: DataSet name incorrect");
|
||
|
}
|
||
|
|
||
|
ListOfAccessResult listOfAccessResult = new ListOfAccessResult();
|
||
|
List<AccessResult> accessResults = listOfAccessResult.getAccessResult();
|
||
|
|
||
|
if (dataSetReference.startsWith("@")) {
|
||
|
DataSet dataSet = nonPersistentDataSets.get(dataSetReference);
|
||
|
if (dataSet == null) {
|
||
|
throw new ServiceError(
|
||
|
ServiceError.PARAMETER_VALUE_INCONSISTENT,
|
||
|
"handleGetDataSetValuesRequest: a DataSet with the given reference does not exist");
|
||
|
}
|
||
|
|
||
|
for (FcModelNode dsMember : dataSet) {
|
||
|
accessResults.add(getReadResult(dsMember));
|
||
|
}
|
||
|
} else {
|
||
|
synchronized (serverModel) {
|
||
|
DataSet dataSet = serverModel.getDataSet(dataSetReference);
|
||
|
|
||
|
if (dataSet == null) {
|
||
|
throw new ServiceError(
|
||
|
ServiceError.PARAMETER_VALUE_INCONSISTENT,
|
||
|
"handleGetDataSetValuesRequest: a DataSet with the given reference does not exist");
|
||
|
}
|
||
|
|
||
|
for (FcModelNode dsMember : dataSet) {
|
||
|
accessResults.add(getReadResult(dsMember));
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
ReadResponse readResponse = new ReadResponse();
|
||
|
readResponse.setListOfAccessResult(listOfAccessResult);
|
||
|
return readResponse;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
private AccessResult getReadResult(FcModelNode modelNode) {
|
||
|
|
||
|
AccessResult accessResult = new AccessResult();
|
||
|
|
||
|
if (modelNode.getFc() == Fc.CO && modelNode.getName().equals("SBO")) {
|
||
|
// if (modelNode.getName().equals("SBO")) {
|
||
|
FcModelNode cdcParent = (FcModelNode) modelNode.getParent();
|
||
|
ModelNode ctlModelNode =
|
||
|
serverModel.findModelNode(cdcParent.getReference(), Fc.CF).getChild("ctlModel");
|
||
|
if (ctlModelNode == null
|
||
|
|| !(ctlModelNode instanceof BdaInt8)
|
||
|
|| ((BdaInt8) ctlModelNode).getValue() != 2) {
|
||
|
logger.warn(
|
||
|
"Selecting controle DO fails because ctlModel is not set to \"sbo-with-normal-security\"");
|
||
|
// 3 indicates error "object_access_denied"
|
||
|
accessResult.setFailure(new DataAccessError(3L));
|
||
|
return accessResult;
|
||
|
}
|
||
|
if (!cdcParent.select(this, serverSap.timer)) {
|
||
|
Data data = new Data();
|
||
|
data.setVisibleString(new BerVisibleString(""));
|
||
|
accessResult.setSuccess(data);
|
||
|
return accessResult;
|
||
|
}
|
||
|
Data data = new Data();
|
||
|
data.setVisibleString(new BerVisibleString("success"));
|
||
|
accessResult.setSuccess(data);
|
||
|
return accessResult;
|
||
|
|
||
|
// }
|
||
|
// else {
|
||
|
// logger.warn("A client tried to read a control variable other than SBO. This is not
|
||
|
// allowed.");
|
||
|
// // 3 indicates error "object_access_denied"
|
||
|
// return new AccessResult(new BerInteger(3L), null);
|
||
|
// }
|
||
|
|
||
|
}
|
||
|
|
||
|
Data data = modelNode.getMmsDataObj();
|
||
|
|
||
|
if (data == null) {
|
||
|
// 11 indicates error "object_value_invalid"
|
||
|
accessResult.setFailure(new DataAccessError(11L));
|
||
|
return accessResult;
|
||
|
}
|
||
|
|
||
|
accessResult.setSuccess(data);
|
||
|
return accessResult;
|
||
|
}
|
||
|
|
||
|
private WriteResponse handleSetDataValuesRequest(WriteRequest mmsWriteRequest)
|
||
|
throws ServiceError {
|
||
|
|
||
|
VariableAccessSpecification variableAccessSpecification =
|
||
|
mmsWriteRequest.getVariableAccessSpecification();
|
||
|
|
||
|
List<Data> listOfData = mmsWriteRequest.getListOfData().getData();
|
||
|
|
||
|
WriteResponse writeResponse = new WriteResponse();
|
||
|
List<WriteResponse.CHOICE> mmsResponseValues = writeResponse.getCHOICE();
|
||
|
|
||
|
if (variableAccessSpecification.getListOfVariable() != null) {
|
||
|
logger.debug("Got a SetDataValues request.");
|
||
|
|
||
|
List<VariableDefs.SEQUENCE> listOfVariable =
|
||
|
variableAccessSpecification.getListOfVariable().getSEQUENCE();
|
||
|
|
||
|
if (listOfVariable.size() < 1
|
||
|
|| listOfData.size() < 1
|
||
|
|| listOfVariable.size() != listOfData.size()) {
|
||
|
throw new ServiceError(
|
||
|
ServiceError.PARAMETER_VALUE_INCONSISTENT,
|
||
|
"handleSetDataValuesRequest: less than one variableAccessSpecification or data element is not allowed, or listOfData ne listOfVar");
|
||
|
}
|
||
|
|
||
|
Iterator<Data> mmsDataIterator = listOfData.iterator();
|
||
|
|
||
|
List<BasicDataAttribute> totalBdasToBeWritten = new ArrayList<>();
|
||
|
int[] numBdas = new int[listOfData.size()];
|
||
|
|
||
|
int i = -1;
|
||
|
synchronized (serverModel) {
|
||
|
for (VariableDefs.SEQUENCE variableDef : listOfVariable) {
|
||
|
i++;
|
||
|
Data mmsData = mmsDataIterator.next();
|
||
|
|
||
|
FcModelNode modelNode = serverModel.getNodeFromVariableDef(variableDef);
|
||
|
|
||
|
if (modelNode == null) {
|
||
|
// 10 indicates error "object-non-existent"
|
||
|
WriteResponse.CHOICE writeResponseChoice = new WriteResponse.CHOICE();
|
||
|
writeResponseChoice.setFailure(new DataAccessError(10L));
|
||
|
mmsResponseValues.add(writeResponseChoice);
|
||
|
} else {
|
||
|
|
||
|
getFirstWriteResults(
|
||
|
mmsResponseValues, totalBdasToBeWritten, numBdas, i, modelNode, mmsData);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
writeAndFillMissingWriteResults(mmsResponseValues, totalBdasToBeWritten, numBdas);
|
||
|
}
|
||
|
|
||
|
} else if (variableAccessSpecification.getVariableListName() != null) {
|
||
|
logger.debug("Got a SetDataSetValues request.");
|
||
|
|
||
|
String dataSetRef =
|
||
|
convertToDataSetReference(variableAccessSpecification.getVariableListName());
|
||
|
|
||
|
// TODO handle non-persisten DataSets too
|
||
|
|
||
|
DataSet dataSet = serverModel.getDataSet(dataSetRef);
|
||
|
|
||
|
Iterator<Data> mmsDataIterator = listOfData.iterator();
|
||
|
|
||
|
List<BasicDataAttribute> totalBdasToBeWritten = new ArrayList<>();
|
||
|
int[] numBdas = new int[listOfData.size()];
|
||
|
|
||
|
int i = -1;
|
||
|
synchronized (serverModel) {
|
||
|
for (FcModelNode dataSetMember : dataSet) {
|
||
|
i++;
|
||
|
Data mmsData = mmsDataIterator.next();
|
||
|
|
||
|
getFirstWriteResults(
|
||
|
mmsResponseValues, totalBdasToBeWritten, numBdas, i, dataSetMember, mmsData);
|
||
|
}
|
||
|
|
||
|
writeAndFillMissingWriteResults(mmsResponseValues, totalBdasToBeWritten, numBdas);
|
||
|
}
|
||
|
|
||
|
} else {
|
||
|
throw new ServiceError(
|
||
|
ServiceError.PARAMETER_VALUE_INAPPROPRIATE,
|
||
|
"handleSetDataValuesRequest: invalid MMS request");
|
||
|
}
|
||
|
|
||
|
return writeResponse;
|
||
|
}
|
||
|
|
||
|
private void writeAndFillMissingWriteResults(
|
||
|
List<WriteResponse.CHOICE> mmsResponseValues,
|
||
|
List<BasicDataAttribute> totalBdasToBeWritten,
|
||
|
int[] numBdas) {
|
||
|
int i;
|
||
|
if (totalBdasToBeWritten.size() != 0) {
|
||
|
List<ServiceError> serviceErrors = serverSap.serverEventListener.write(totalBdasToBeWritten);
|
||
|
ListIterator<WriteResponse.CHOICE> mmsResponseIterator = mmsResponseValues.listIterator();
|
||
|
if (serviceErrors == null || serviceErrors.size() != totalBdasToBeWritten.size()) {
|
||
|
while (mmsResponseIterator.hasNext()) {
|
||
|
if (mmsResponseIterator.next() == null) {
|
||
|
mmsResponseIterator.set(writeSuccess);
|
||
|
}
|
||
|
}
|
||
|
for (BasicDataAttribute bda : totalBdasToBeWritten) {
|
||
|
bda.mirror.setValueFrom(bda);
|
||
|
}
|
||
|
} else {
|
||
|
i = -1;
|
||
|
Iterator<ServiceError> serviceErrorIterator = serviceErrors.iterator();
|
||
|
Iterator<BasicDataAttribute> bdaToBeWrittenIterator = totalBdasToBeWritten.iterator();
|
||
|
while (mmsResponseIterator.hasNext()) {
|
||
|
i++;
|
||
|
if (mmsResponseIterator.next() == null) {
|
||
|
for (int j = 0; j < numBdas[i]; j++) {
|
||
|
ServiceError serviceError = serviceErrorIterator.next();
|
||
|
BasicDataAttribute bda = bdaToBeWrittenIterator.next();
|
||
|
if (serviceError != null) {
|
||
|
WriteResponse.CHOICE writeResponseChoice = new WriteResponse.CHOICE();
|
||
|
writeResponseChoice.setFailure(
|
||
|
new DataAccessError(serviceErrorToMmsError(serviceError)));
|
||
|
mmsResponseIterator.set(writeResponseChoice);
|
||
|
} else {
|
||
|
bda.mirror.setValueFrom(bda);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
private void getFirstWriteResults(
|
||
|
List<WriteResponse.CHOICE> mmsResponseValues,
|
||
|
List<BasicDataAttribute> totalBdasToBeWritten,
|
||
|
int[] numBdas,
|
||
|
int i,
|
||
|
FcModelNode fcModelNode,
|
||
|
Data mmsData) {
|
||
|
WriteResponse.CHOICE writeResult = getWriteResult(fcModelNode, mmsData);
|
||
|
if (writeResult == null) {
|
||
|
FcModelNode fcModelNodeCopy = (FcModelNode) fcModelNode.copy();
|
||
|
try {
|
||
|
fcModelNodeCopy.setValueFromMmsDataObj(mmsData);
|
||
|
} catch (ServiceError e) {
|
||
|
logger.warn("SetDataValues failed because of data missmatch.", e);
|
||
|
WriteResponse.CHOICE writeResponseChoice = new WriteResponse.CHOICE();
|
||
|
writeResponseChoice.setFailure(new DataAccessError(serviceErrorToMmsError(e)));
|
||
|
mmsResponseValues.add(writeResponseChoice);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if (fcModelNodeCopy.fc == Fc.CO) {
|
||
|
// TODO timeactivate operate
|
||
|
fcModelNodeCopy = (FcModelNode) fcModelNodeCopy.getChild("ctlVal");
|
||
|
// TODO write origin and ctlNum if they exist
|
||
|
} else {
|
||
|
|
||
|
}
|
||
|
|
||
|
List<BasicDataAttribute> bdas = fcModelNodeCopy.getBasicDataAttributes();
|
||
|
totalBdasToBeWritten.addAll(bdas);
|
||
|
numBdas[i] = bdas.size();
|
||
|
mmsResponseValues.add(null);
|
||
|
|
||
|
} else {
|
||
|
mmsResponseValues.add(writeResult);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// private WriteResponse.SubChoice operate(FcModelNode modelNode, Data mmsData) {
|
||
|
// FcModelNode fcModelNodeCopy = (FcModelNode) modelNode.copy();
|
||
|
// try {
|
||
|
// fcModelNodeCopy.setValueFromMmsDataObj(mmsData);
|
||
|
// } catch (ServiceError e) {
|
||
|
// logger.warn("SetDataValues failed because of data missmatch.", e);
|
||
|
// return new WriteResponse.SubChoice(new BerInteger(serviceErrorToMmsError(e)), null);
|
||
|
// }
|
||
|
//
|
||
|
// // TODO timeactivate operate
|
||
|
//
|
||
|
// BasicDataAttribute ctlValBda = (BasicDataAttribute) fcModelNodeCopy.getChild("ctlVal");
|
||
|
// List<BasicDataAttribute> bdas = new ArrayList<BasicDataAttribute>(1);
|
||
|
// bdas.add(ctlValBda);
|
||
|
// List<ServiceError> serviceErrors;
|
||
|
// try {
|
||
|
// serviceErrors = serverSap.serverEventListener.write(bdas);
|
||
|
// } catch (ServiceError e) {
|
||
|
// return new WriteResponse.SubChoice(new BerInteger(serviceErrorToMmsError(e)), null);
|
||
|
// }
|
||
|
// if (serviceErrors != null && serviceErrors.size() == bdas.size() && serviceErrors.get(1) !=
|
||
|
// null) {
|
||
|
// return new WriteResponse.SubChoice(new
|
||
|
// BerInteger(serviceErrorToMmsError(serviceErrors.get(1))), null);
|
||
|
// }
|
||
|
//
|
||
|
// ctlValBda.mirror.setValueFrom(ctlValBda);
|
||
|
// // TODO write origin and ctlNum if they exist
|
||
|
//
|
||
|
// return writeSuccess;
|
||
|
// }
|
||
|
|
||
|
private WriteResponse.CHOICE getWriteResult(FcModelNode modelNode, Data mmsData) {
|
||
|
|
||
|
WriteResponse.CHOICE writeResponse = new WriteResponse.CHOICE();
|
||
|
|
||
|
Fc fc = modelNode.getFc();
|
||
|
if (fc == Fc.ST || fc == Fc.MX || fc == Fc.OR || fc == Fc.EX) {
|
||
|
// 3 indicates error "object_access_denied"
|
||
|
writeResponse.setFailure(new DataAccessError(3L));
|
||
|
return writeResponse;
|
||
|
}
|
||
|
|
||
|
if (fc == Fc.CO) {
|
||
|
String nodeName = modelNode.getName();
|
||
|
|
||
|
if (nodeName.equals("Oper")) {
|
||
|
FcModelNode cdcParent = (FcModelNode) modelNode.getParent();
|
||
|
ModelNode ctlModelNode =
|
||
|
serverModel.findModelNode(cdcParent.getReference(), Fc.CF).getChild("ctlModel");
|
||
|
if (ctlModelNode == null || !(ctlModelNode instanceof BdaInt8)) {
|
||
|
logger.warn("Operatring controle DO failed because ctlModel is not set.");
|
||
|
// 3 indicates error "object_access_denied"
|
||
|
writeResponse.setFailure(new DataAccessError(3L));
|
||
|
return writeResponse;
|
||
|
}
|
||
|
|
||
|
int ctlModel = ((BdaInt8) ctlModelNode).getValue();
|
||
|
|
||
|
/* Direct control with normal security (direct-operate) */
|
||
|
if (ctlModel == 1) {
|
||
|
return null;
|
||
|
}
|
||
|
/* SBO control with normal security (operate-once or operate-many) */
|
||
|
else if (ctlModel == 2) {
|
||
|
if (cdcParent.isSelectedBy(this)) {
|
||
|
return null;
|
||
|
} else {
|
||
|
// 3 indicates error "object_access_denied"
|
||
|
writeResponse.setFailure(new DataAccessError(3L));
|
||
|
return writeResponse;
|
||
|
}
|
||
|
|
||
|
} else {
|
||
|
logger.warn("SetDataValues failed because of unsupported ctlModel: " + ctlModel);
|
||
|
// 9 indicates error "object_access_unsupported"
|
||
|
writeResponse.setFailure(new DataAccessError(9L));
|
||
|
return writeResponse;
|
||
|
}
|
||
|
} else {
|
||
|
logger.warn(
|
||
|
"SetDataValues failed because of the operation is not allowed yet: "
|
||
|
+ modelNode.getName());
|
||
|
// 9 indicates error "object_access_unsupported"
|
||
|
writeResponse.setFailure(new DataAccessError(9L));
|
||
|
return writeResponse;
|
||
|
}
|
||
|
} else if (fc == Fc.RP) {
|
||
|
|
||
|
if (modelNode instanceof Rcb) {
|
||
|
// 3 indicates error "object_access_denied"
|
||
|
writeResponse.setFailure(new DataAccessError(3L));
|
||
|
return writeResponse;
|
||
|
}
|
||
|
|
||
|
FcModelNode fcModelNodeCopy = (FcModelNode) modelNode.copy();
|
||
|
|
||
|
try {
|
||
|
fcModelNodeCopy.setValueFromMmsDataObj(mmsData);
|
||
|
} catch (ServiceError e) {
|
||
|
WriteResponse.CHOICE writeResponseChoice = new WriteResponse.CHOICE();
|
||
|
writeResponseChoice.setFailure(new DataAccessError(serviceErrorToMmsError(e)));
|
||
|
return writeResponseChoice;
|
||
|
}
|
||
|
|
||
|
Urcb urcb = (Urcb) modelNode.getParent();
|
||
|
|
||
|
String nodeName = modelNode.getName();
|
||
|
|
||
|
synchronized (urcb) {
|
||
|
if (nodeName.equals("RptEna")) {
|
||
|
BdaBoolean rptEnaNode = (BdaBoolean) fcModelNodeCopy;
|
||
|
if (rptEnaNode.getValue()) {
|
||
|
if (urcb.dataSet == null) {
|
||
|
logger.info("client tried to enable RCB even though there is no configured data set");
|
||
|
// 3 indicates error "object_access_denied"
|
||
|
writeResponse.setFailure(new DataAccessError(3L));
|
||
|
return writeResponse;
|
||
|
}
|
||
|
if (urcb.reserved == null) {
|
||
|
urcb.reserved = this;
|
||
|
urcb.enable();
|
||
|
rsvdURCBs.add(urcb);
|
||
|
((BdaBoolean) modelNode).setValue(true);
|
||
|
return writeSuccess;
|
||
|
} else if (urcb.reserved == this) {
|
||
|
urcb.enable();
|
||
|
((BdaBoolean) modelNode).setValue(true);
|
||
|
return writeSuccess;
|
||
|
} else {
|
||
|
// 3 indicates error "object_access_denied"
|
||
|
writeResponse.setFailure(new DataAccessError(3L));
|
||
|
return writeResponse;
|
||
|
}
|
||
|
|
||
|
} else {
|
||
|
// disable reporting
|
||
|
if (urcb.reserved == this) {
|
||
|
urcb.disable();
|
||
|
((BdaBoolean) modelNode).setValue(false);
|
||
|
return writeSuccess;
|
||
|
} else {
|
||
|
// 3 indicates error "object_access_denied"
|
||
|
writeResponse.setFailure(new DataAccessError(3L));
|
||
|
return writeResponse;
|
||
|
}
|
||
|
}
|
||
|
} else if (nodeName.equals("Resv")) {
|
||
|
BdaBoolean rptResvNode = (BdaBoolean) fcModelNodeCopy;
|
||
|
if (rptResvNode.getValue()) {
|
||
|
|
||
|
if (urcb.reserved == null) {
|
||
|
urcb.reserved = this;
|
||
|
urcb.getResv().setValue(true);
|
||
|
rsvdURCBs.add(urcb);
|
||
|
return writeSuccess;
|
||
|
} else if (urcb.reserved == this) {
|
||
|
return writeSuccess;
|
||
|
} else {
|
||
|
// 3 indicates error "object_access_denied"
|
||
|
writeResponse.setFailure(new DataAccessError(3L));
|
||
|
return writeResponse;
|
||
|
}
|
||
|
} else {
|
||
|
if (urcb.reserved == this) {
|
||
|
urcb.reserved = null;
|
||
|
urcb.getResv().setValue(false);
|
||
|
rsvdURCBs.remove(urcb);
|
||
|
return writeSuccess;
|
||
|
} else {
|
||
|
// 3 indicates error "object_access_denied"
|
||
|
writeResponse.setFailure(new DataAccessError(3L));
|
||
|
return writeResponse;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
} else if (nodeName.equals("DatSet")) {
|
||
|
if ((urcb.reserved == null || urcb.reserved == this) && !urcb.enabled) {
|
||
|
String dataSetRef =
|
||
|
((BdaVisibleString) fcModelNodeCopy).getStringValue().replace('$', '.');
|
||
|
if (dataSetRef.isEmpty()) {
|
||
|
urcb.dataSet = null;
|
||
|
((BasicDataAttribute) modelNode).setValueFrom((BasicDataAttribute) fcModelNodeCopy);
|
||
|
return writeSuccess;
|
||
|
|
||
|
} else {
|
||
|
DataSet dataSet = serverModel.getDataSet(dataSetRef);
|
||
|
if (dataSet == null) {
|
||
|
dataSet = nonPersistentDataSets.get(dataSetRef);
|
||
|
}
|
||
|
if (dataSet != null) {
|
||
|
urcb.dataSet = dataSet;
|
||
|
((BasicDataAttribute) modelNode).setValueFrom((BasicDataAttribute) fcModelNodeCopy);
|
||
|
return writeSuccess;
|
||
|
} else {
|
||
|
logger.info(
|
||
|
"Client tried to set dataSetReference of URCB to non existant data set.");
|
||
|
// 3 indicates error "object_access_denied"
|
||
|
writeResponse.setFailure(new DataAccessError(3L));
|
||
|
return writeResponse;
|
||
|
}
|
||
|
}
|
||
|
} else {
|
||
|
logger.info(
|
||
|
"Client tried to write RCB parameter even though URCB is reserved by other client or already enabled.");
|
||
|
// 3 indicates error "object_access_denied"
|
||
|
writeResponse.setFailure(new DataAccessError(3L));
|
||
|
return writeResponse;
|
||
|
}
|
||
|
} else if (nodeName.equals("OptFlds")) {
|
||
|
if ((urcb.reserved == null || urcb.reserved == this) && !urcb.enabled) {
|
||
|
if (!((BdaOptFlds) modelNode).isBufferOverflow()
|
||
|
&& !((BdaOptFlds) modelNode).isConfigRevision()
|
||
|
&& !((BdaOptFlds) modelNode).isDataReference()
|
||
|
&& !((BdaOptFlds) modelNode).isEntryId()) {
|
||
|
((BasicDataAttribute) modelNode).setValueFrom((BasicDataAttribute) fcModelNodeCopy);
|
||
|
return writeSuccess;
|
||
|
} else {
|
||
|
logger.info("Client tried to write OptFlds with usupported field set to true.");
|
||
|
// 3 indicates error "object_access_denied"
|
||
|
writeResponse.setFailure(new DataAccessError(3L));
|
||
|
return writeResponse;
|
||
|
}
|
||
|
} else {
|
||
|
logger.info(
|
||
|
"Client tried to write RCB parameter even though URCB is reserved by other client or already enabled.");
|
||
|
// 3 indicates error "object_access_denied"
|
||
|
writeResponse.setFailure(new DataAccessError(3L));
|
||
|
return writeResponse;
|
||
|
}
|
||
|
|
||
|
} else if (nodeName.equals("GI")) {
|
||
|
|
||
|
if ((urcb.reserved == this)
|
||
|
&& urcb.enabled
|
||
|
&& ((BdaTriggerConditions) urcb.getChild("TrgOps")).isGeneralInterrogation()) {
|
||
|
urcb.generalInterrogation();
|
||
|
return writeSuccess;
|
||
|
} else {
|
||
|
logger.info(
|
||
|
"Client tried to initiate a general interrogation even though URCB is not enabled by this client or general interrogation is not enabled in the trigger options.");
|
||
|
// 3 indicates error "object_access_denied"
|
||
|
writeResponse.setFailure(new DataAccessError(3L));
|
||
|
return writeResponse;
|
||
|
}
|
||
|
|
||
|
} else if (nodeName.equals("RptID")
|
||
|
|| nodeName.equals("BufTm")
|
||
|
|| nodeName.equals("TrgOps")
|
||
|
|| nodeName.equals("IntgPd")) {
|
||
|
if ((urcb.reserved == null || urcb.reserved == this) && !urcb.enabled) {
|
||
|
((BasicDataAttribute) modelNode).setValueFrom((BasicDataAttribute) fcModelNodeCopy);
|
||
|
return writeSuccess;
|
||
|
} else {
|
||
|
// 3 indicates error "object_access_denied"
|
||
|
writeResponse.setFailure(new DataAccessError(3L));
|
||
|
return writeResponse;
|
||
|
}
|
||
|
|
||
|
} else {
|
||
|
// nodes sqnum, ConfRev, and owner may not be read
|
||
|
// 3 indicates error "object_access_denied"
|
||
|
writeResponse.setFailure(new DataAccessError(3L));
|
||
|
return writeResponse;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
} else {
|
||
|
|
||
|
return null;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
private int serviceErrorToMmsError(ServiceError e) {
|
||
|
|
||
|
switch (e.getErrorCode()) {
|
||
|
case ServiceError.FAILED_DUE_TO_SERVER_CONSTRAINT:
|
||
|
return 1;
|
||
|
case ServiceError.INSTANCE_LOCKED_BY_OTHER_CLIENT:
|
||
|
return 2;
|
||
|
case ServiceError.ACCESS_VIOLATION:
|
||
|
return 3;
|
||
|
case ServiceError.TYPE_CONFLICT:
|
||
|
return 7;
|
||
|
case ServiceError.INSTANCE_NOT_AVAILABLE:
|
||
|
return 10;
|
||
|
case ServiceError.PARAMETER_VALUE_INCONSISTENT:
|
||
|
return 11;
|
||
|
default:
|
||
|
return 9;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
private GetNameListResponse handleGetDataSetNamesRequest(GetNameListRequest getNameListRequest)
|
||
|
throws ServiceError {
|
||
|
|
||
|
BerVisibleString domainSpecific = getNameListRequest.getObjectScope().getDomainSpecific();
|
||
|
|
||
|
List<String> dsList = null;
|
||
|
if (domainSpecific == null) {
|
||
|
dsList = new ArrayList<>(nonPersistentDataSets.size());
|
||
|
for (String dataSet : nonPersistentDataSets.keySet()) {
|
||
|
dsList.add(dataSet);
|
||
|
}
|
||
|
} else {
|
||
|
dsList = serverModel.getDataSetNames(domainSpecific.toString());
|
||
|
}
|
||
|
|
||
|
insertRef = true;
|
||
|
if (getNameListRequest.getContinueAfter() != null) {
|
||
|
continueAfter = getNameListRequest.getContinueAfter().toString();
|
||
|
insertRef = false;
|
||
|
}
|
||
|
|
||
|
ListOfIdentifier listOf = new ListOfIdentifier();
|
||
|
List<Identifier> identifiers = listOf.getIdentifier();
|
||
|
|
||
|
int identifierSize = 0;
|
||
|
boolean moreFollows = false;
|
||
|
|
||
|
if (dsList != null) {
|
||
|
for (String dsRef : dsList) {
|
||
|
if (insertRef == true) {
|
||
|
if (identifierSize > negotiatedMaxPduSize - 200) {
|
||
|
moreFollows = true;
|
||
|
logger.info("maxMMSPduSize reached");
|
||
|
break;
|
||
|
}
|
||
|
identifiers.add(new Identifier(dsRef.getBytes(UTF_8)));
|
||
|
identifierSize += dsRef.length() + 2;
|
||
|
} else {
|
||
|
if (dsRef.equals(continueAfter)) {
|
||
|
insertRef = true;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
GetNameListResponse getNameListResponse = new GetNameListResponse();
|
||
|
getNameListResponse.setListOfIdentifier(listOf);
|
||
|
getNameListResponse.setMoreFollows(new BerBoolean(moreFollows));
|
||
|
|
||
|
return getNameListResponse;
|
||
|
}
|
||
|
|
||
|
private GetNamedVariableListAttributesResponse handleGetDataSetDirectoryRequest(
|
||
|
ObjectName mmsGetNamedVarListAttReq) throws ServiceError {
|
||
|
|
||
|
String dataSetReference = convertToDataSetReference(mmsGetNamedVarListAttReq);
|
||
|
|
||
|
DataSet dataSet;
|
||
|
|
||
|
if (dataSetReference.startsWith("@")) {
|
||
|
dataSet = nonPersistentDataSets.get(dataSetReference);
|
||
|
} else {
|
||
|
dataSet = serverModel.getDataSet(dataSetReference);
|
||
|
}
|
||
|
|
||
|
if (dataSet == null) {
|
||
|
throw new ServiceError(
|
||
|
ServiceError.PARAMETER_VALUE_INAPPROPRIATE,
|
||
|
"DataSet with that reference is does not exist.");
|
||
|
}
|
||
|
|
||
|
VariableDefs variableDefs = new VariableDefs();
|
||
|
|
||
|
List<VariableDefs.SEQUENCE> listOfVariable = variableDefs.getSEQUENCE();
|
||
|
|
||
|
for (FcModelNode member : dataSet) {
|
||
|
listOfVariable.add(member.getMmsVariableDef());
|
||
|
}
|
||
|
|
||
|
GetNamedVariableListAttributesResponse getNamedVariableListAttributesResponse =
|
||
|
new GetNamedVariableListAttributesResponse();
|
||
|
getNamedVariableListAttributesResponse.setListOfVariable(variableDefs);
|
||
|
getNamedVariableListAttributesResponse.setMmsDeletable(new BerBoolean(dataSet.isDeletable()));
|
||
|
|
||
|
return getNamedVariableListAttributesResponse;
|
||
|
}
|
||
|
|
||
|
private DefineNamedVariableListResponse handleCreateDataSetRequest(
|
||
|
DefineNamedVariableListRequest mmsDefineNamedVariableListRequest) throws ServiceError {
|
||
|
String dataSetReference =
|
||
|
convertToDataSetReference(mmsDefineNamedVariableListRequest.getVariableListName());
|
||
|
if (dataSetReference == null) {
|
||
|
throw new ServiceError(
|
||
|
ServiceError.PARAMETER_VALUE_INCONSISTENT,
|
||
|
"handleCreateDataSetRequest: invalid MMS request (No DataSet Name Specified)");
|
||
|
}
|
||
|
|
||
|
List<VariableDefs.SEQUENCE> nameList =
|
||
|
mmsDefineNamedVariableListRequest.getListOfVariable().getSEQUENCE();
|
||
|
|
||
|
List<FcModelNode> dataSetMembers = new ArrayList<>(nameList.size());
|
||
|
|
||
|
for (VariableDefs.SEQUENCE variableDef : nameList) {
|
||
|
dataSetMembers.add(serverModel.getNodeFromVariableDef(variableDef));
|
||
|
}
|
||
|
|
||
|
DataSet dataSet = new DataSet(dataSetReference, dataSetMembers, true);
|
||
|
|
||
|
if (dataSetReference.startsWith("@")) {
|
||
|
if (nonPersistentDataSets.containsKey(dataSetReference)) {
|
||
|
throw new ServiceError(
|
||
|
ServiceError.PARAMETER_VALUE_INAPPROPRIATE, "data set with that name exists already");
|
||
|
}
|
||
|
nonPersistentDataSets.put(dataSetReference, dataSet);
|
||
|
} else {
|
||
|
serverModel.addDataSet(dataSet);
|
||
|
}
|
||
|
|
||
|
return new DefineNamedVariableListResponse();
|
||
|
}
|
||
|
|
||
|
private DeleteNamedVariableListResponse handleDeleteDataSetRequest(
|
||
|
DeleteNamedVariableListRequest mmsDelNamVarListReq) throws ServiceError {
|
||
|
String dataSetReference =
|
||
|
convertToDataSetReference(
|
||
|
mmsDelNamVarListReq.getListOfVariableListName().getObjectName().get(0));
|
||
|
|
||
|
DeleteNamedVariableListResponse deleteNamedVariableListResponse =
|
||
|
new DeleteNamedVariableListResponse();
|
||
|
|
||
|
if (dataSetReference.startsWith("@")) {
|
||
|
if (nonPersistentDataSets.remove(dataSetReference) == null) {
|
||
|
deleteNamedVariableListResponse.setNumberMatched(new Unsigned32(0));
|
||
|
deleteNamedVariableListResponse.setNumberDeleted(new Unsigned32(0));
|
||
|
return deleteNamedVariableListResponse;
|
||
|
} else {
|
||
|
deleteNamedVariableListResponse.setNumberMatched(new Unsigned32(1));
|
||
|
deleteNamedVariableListResponse.setNumberDeleted(new Unsigned32(1));
|
||
|
return deleteNamedVariableListResponse;
|
||
|
}
|
||
|
} else {
|
||
|
synchronized (serverModel) {
|
||
|
if (serverModel.removeDataSet(dataSetReference) == null) {
|
||
|
if (serverModel.getDataSet(dataSetReference) == null) {
|
||
|
// DataSet with the name does not exist.
|
||
|
deleteNamedVariableListResponse.setNumberMatched(new Unsigned32(0));
|
||
|
deleteNamedVariableListResponse.setNumberDeleted(new Unsigned32(0));
|
||
|
return deleteNamedVariableListResponse;
|
||
|
} else {
|
||
|
// DataSet exists but is not deletable
|
||
|
deleteNamedVariableListResponse.setNumberMatched(new Unsigned32(1));
|
||
|
deleteNamedVariableListResponse.setNumberDeleted(new Unsigned32(0));
|
||
|
return deleteNamedVariableListResponse;
|
||
|
}
|
||
|
} else {
|
||
|
deleteNamedVariableListResponse.setNumberMatched(new Unsigned32(1));
|
||
|
deleteNamedVariableListResponse.setNumberDeleted(new Unsigned32(1));
|
||
|
return deleteNamedVariableListResponse;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void close() {
|
||
|
cleanUpConnection();
|
||
|
executor.shutdown();
|
||
|
if (acseAssociation != null) {
|
||
|
acseAssociation.disconnect();
|
||
|
}
|
||
|
}
|
||
|
}
|