org.openmuc.jdlms.internal.lnassociation.Association Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of jdlms Show documentation
Show all versions of jdlms Show documentation
jDLMS is a library implementing the DLMS/COSEM (IEC 62056) communication standard.
package org.openmuc.jdlms.internal.lnassociation;
import java.io.EOFException;
import java.io.IOException;
import java.net.SocketTimeoutException;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import org.openmuc.jdlms.ServerConnectionInfo.Status;
import org.openmuc.jdlms.ServerConnectionListener;
import org.openmuc.jdlms.internal.APdu;
import org.openmuc.jdlms.internal.DataDirectory;
import org.openmuc.jdlms.internal.DataDirectory.DlmsLogicalDevice;
import org.openmuc.jdlms.internal.ReleaseReqReason;
import org.openmuc.jdlms.internal.ReleaseRespReason;
import org.openmuc.jdlms.internal.ServerConnectionData;
import org.openmuc.jdlms.internal.ServiceError;
import org.openmuc.jdlms.internal.StateError;
import org.openmuc.jdlms.internal.asn1.cosem.COSEMpdu;
import org.openmuc.jdlms.internal.asn1.iso.acse.ACSEApdu;
import org.openmuc.jdlms.internal.asn1.iso.acse.AssociationInformation;
import org.openmuc.jdlms.internal.asn1.iso.acse.RLREApdu;
import org.openmuc.jdlms.internal.asn1.iso.acse.RLRQApdu;
import org.openmuc.jdlms.internal.asn1.iso.acse.ReleaseResponseReason;
import org.openmuc.jdlms.internal.sessionlayer.ServerSessionLayer;
import org.openmuc.jdlms.internal.settings.ServerSettings;
import org.openmuc.jdlms.internal.transportlayer.ServerConnectionInformationImpl;
public class Association implements Runnable {
private final DataDirectory directory;
private final ServerSessionLayer sessionLayer;
private final Long connectionId;
private final ServerConnectionData connectionData;
private final ServerSettings settings;
private final ServerConnectionInformationImpl serverConnectionInformation;
public Association(DataDirectory directory, ServerSessionLayer sessionLayer, Long connectionId,
ServerSettings settings, ServerConnectionInformationImpl serverConnectionInformation) {
this.directory = directory;
this.sessionLayer = sessionLayer;
this.connectionId = connectionId;
this.settings = settings;
this.serverConnectionInformation = serverConnectionInformation;
this.connectionData = new ServerConnectionData();
this.directory.addConnection(connectionId, this.connectionData);
}
@Override
public void run() {
AssociationMessenger associationMessenger = new AssociationMessenger(connectionData, directory, sessionLayer,
connectionId);
ServerConnectionListener connectionListener = settings.connectionListener;
try {
byte[] payload = this.sessionLayer.readNextMessage();
Status status = Status.OPEN;
notifyListener(connectionListener, status);
RequestProcessorData requestProcessorData = new RequestProcessorData(this.sessionLayer.getLogicalDeviceId(),
connectionId, directory, connectionData);
this.connectionData.clientId = this.sessionLayer.getClientId();
DlmsLogicalDevice dlmsLogicalDevice = this.directory
.getLogicalDeviceFor(this.sessionLayer.getLogicalDeviceId());
APdu aarqAPdu = new InitialmessageProcessor(connectionData, dlmsLogicalDevice)
.processInitialMessage(payload);
associationMessenger.encodeAndSend(aarqAPdu);
Map requestProcessors = buildRequestProcessors(associationMessenger,
requestProcessorData);
while (true) {
APdu apdu = associationMessenger.readNextApdu();
ACSEApdu acseApdu = apdu.getAcseAPdu();
COSEMpdu cosemPdu = apdu.getCosemPdu();
if (acseApdu != null && acseApdu.rlrq != null) {
sendDisconnectMessage(associationMessenger, acseApdu.rlrq);
return;
}
if (!this.connectionData.authenticated
&& cosemPdu.getChoiceIndex() != COSEMpdu.Choices.ACTION_REQUEST) {
throw new AssociationException(StateError.SERVICE_NOT_ALLOWED, ServiceError.OPERATION_NOT_POSSIBLE);
}
RequestProcessor requestProcessor = requestProcessors.get(cosemPdu.getChoiceIndex());
if (requestProcessor != null) {
requestProcessor.processRequest(cosemPdu);
}
else {
// TODO handle other requests..
}
if (!this.connectionData.authenticated) {
throw new IOException("Client failed to authenticate..");
}
}
} catch (GenericAssociationException e) {
try {
associationMessenger.encodeAndSend(e.getErrorMessageApdu());
} catch (IOException e1) {
// ignore any exception here..
}
} catch (IOException e) {
if (e instanceof EOFException) {
// client closed the connection..
return;
}
if (e instanceof SocketTimeoutException) {
// client was too slow
return;
}
e.printStackTrace();
// TODO ignore??
} finally {
this.directory.removeConnection(this.connectionId);
try {
sessionLayer.close();
} catch (IOException e1) {
// ignore
}
notifyListener(connectionListener, Status.CLOSED);
}
}
private void sendDisconnectMessage(AssociationMessenger associationMessenger, RLRQApdu rlrq) throws IOException {
ReleaseReqReason reqReason = ReleaseReqReason.reasonFor(rlrq.reason.value);
ReleaseRespReason respReason;
switch (reqReason) {
case URGENT:
respReason = ReleaseRespReason.NOT_FINISHED;
break;
case USER_DEFINED:
respReason = ReleaseRespReason.USER_DEFINED;
break;
case NORMAL:
default:
case UNKNOWN:
respReason = ReleaseRespReason.NORMAL;
break;
}
ReleaseResponseReason reason = new ReleaseResponseReason(respReason.getCode());
AssociationInformation userInformation = null;
RLREApdu rlre = new RLREApdu(reason, userInformation);
ACSEApdu reAcse = new ACSEApdu(null, null, null, rlre);
APdu reponseApdu = new APdu(reAcse, null);
byte[] buffer = new byte[6];
int length = reponseApdu.encode(buffer, null);
associationMessenger.send(Arrays.copyOfRange(buffer, buffer.length - length, buffer.length));
sessionLayer.close();
}
private void notifyListener(ServerConnectionListener connectionListener, Status status) {
if (connectionListener == null) {
return;
}
serverConnectionInformation.clientId = this.sessionLayer.getClientId();
serverConnectionInformation.logicalDeviceAddress = this.sessionLayer.getLogicalDeviceId();
serverConnectionInformation.status = status;
connectionListener.connectionChanged(serverConnectionInformation);
}
private Map buildRequestProcessors(AssociationMessenger associationMessenger,
RequestProcessorData requestProcessorData) {
Map requestProcessors = new HashMap<>();
requestProcessors.put(COSEMpdu.Choices.ACTION_REQUEST,
new ActionRequestProcessor(associationMessenger, requestProcessorData));
requestProcessors.put(COSEMpdu.Choices.GET_REQUEST,
new GetRequestProcessor(associationMessenger, requestProcessorData));
requestProcessors.put(COSEMpdu.Choices.SET_REQUEST,
new SetRequestProcessor(associationMessenger, requestProcessorData));
return requestProcessors;
}
}