/* * 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.IOException; import java.net.InetAddress; import javax.net.SocketFactory; import org.openmuc.josistack.ClientAcseSap; /** * The ClientSap class represents the IEC 61850 service access point for client applications. A client * application that wants to connect to a server should first create an instance of ClientSap. Next all the * necessary configuration parameters can be set. Finally the associate function is called to connect to * the server. An instance of ClientSap can be used to create an unlimited number of associations. Changing * the parameters of a ClientSap has no affect on associations that have already been created. */ public final class ClientSap { static final int MINIMUM_MMS_PDU_SIZE = 64; private static final int MAXIMUM_MMS_PDU_SIZE = 65000; private static final byte[] DEFAULT_TSEL_LOCAL = new byte[] { 0, 0 }; private static final byte[] DEFAULT_TSEL_REMOTE = new byte[] { 0, 1 }; private static final int DEFAUTL_TPDU_SIZE_PARAMETER = 10; // size = 1024 private int proposedMaxMmsPduSize = 65000; private final int proposedMaxServOutstandingCalling = 5; private final int proposedMaxServOutstandingCalled = 5; private final int proposedDataStructureNestingLevel = 10; private byte[] servicesSupportedCalling = new byte[] { (byte) 0xee, 0x1c, 0, 0, 0x04, 0x08, 0, 0, 0x79, (byte) 0xef, 0x18 }; private int messageFragmentTimeout = 20000; private int responseTimeout = 50000; private final ClientAcseSap acseSap; /** * Use this constructor to create a default client SAP. */ public ClientSap() { acseSap = new ClientAcseSap(); acseSap.tSap.tSelLocal = DEFAULT_TSEL_LOCAL; acseSap.tSap.tSelRemote = DEFAULT_TSEL_REMOTE; acseSap.tSap.setMaxTPDUSizeParam(DEFAUTL_TPDU_SIZE_PARAMETER); } /** * Use this constructor to create a client SAP that uses the given SocketFactory to connect to servers. * You could pass an SSLSocketFactory to enable SSL. * * @param socketFactory * the socket factory to construct the socket */ public ClientSap(SocketFactory socketFactory) { acseSap = new ClientAcseSap(socketFactory); acseSap.tSap.tSelLocal = DEFAULT_TSEL_LOCAL; acseSap.tSap.tSelRemote = DEFAULT_TSEL_REMOTE; acseSap.tSap.setMaxTPDUSizeParam(DEFAUTL_TPDU_SIZE_PARAMETER); } /** * Sets the maximum MMS PDU size in bytes that the client association will support. The client proposes this value * to the server during association. If the server requires the use of a smaller maximum MMS PDU size, then the * smaller size will be accepted by the client. The default size is 65000. * * @param size * cannot be less than 64. The upper limit is 65000 so that segmentation at the lower transport layer is * avoided. The Transport Layer's maximum PDU size is 65531. */ public void setMaxMmsPduSize(int size) { if (size >= MINIMUM_MMS_PDU_SIZE && size <= MAXIMUM_MMS_PDU_SIZE) { proposedMaxMmsPduSize = size; } else { throw new IllegalArgumentException("maximum size is out of bound"); } } /** * Gets the maximum MMS PDU size. * * @return the maximum MMS PDU size. */ public int getMaxMmsPduSize() { return proposedMaxMmsPduSize; } /** * Sets the SevicesSupportedCalling parameter. The given parameter is sent to the server but has no affect on the * functionality of this client. * * @param services * the ServicesSupportedCalling parameter */ public void setServicesSupportedCalling(byte[] services) { if (services.length != 11) { throw new IllegalArgumentException("The services parameter needs to be of lenth 11"); } servicesSupportedCalling = services; } /** * Gets the ServicesSupportedCalling parameter. * * @return the ServicesSupportedCalling parameter. */ public byte[] getServicesSupportedCalling() { return servicesSupportedCalling; } /** * Sets the remote/called Session-Selector (S-SEL). The default remote S-SEL is byte[] { 0, 1 }. * * @param sSelRemote * the remote/called S-SEL. */ public void setSSelRemote(byte[] sSelRemote) { acseSap.sSelRemote = sSelRemote; } /** * Sets the local/calling Session-Selector (S-SEL). The default local S-SEL is byte[] { 0, 1 }. * * @param sSelLocal * the local/calling S-SEL. */ public void setSSelLocal(byte[] sSelLocal) { acseSap.sSelLocal = sSelLocal; } /** * Sets the remote/called Presentation-Selector (P-SEL). The default remote P-SEL is byte[] { 0, 0, 0, 1 }. * * @param pSelRemote * the remote/called P-SEL. */ public void setPSelRemote(byte[] pSelRemote) { acseSap.pSelRemote = pSelRemote; } /** * Sets the local/calling Presentation-Selector (P-SEL). The default local P-SEL is byte[] { 0, 0, 0, 1 }. * * @param pSelLocal * the local/calling P-SEL. */ public void setPSelLocal(byte[] pSelLocal) { acseSap.pSelLocal = pSelLocal; } /** * Sets the remote/called Transport-Selector (T-SEL). It is optionally transmitted in the OSI Transport Layer * connection request (CR). The default remote T-SEL is byte[] { 0, 1 }. * * @param tSelRemote * the remote/called T-SEL. If null the T-SEL will be omitted. No maximum size is defined by the * standard. */ public void setTSelRemote(byte[] tSelRemote) { acseSap.tSap.tSelRemote = tSelRemote; } /** * Sets the local/calling Transport-Selector (T-SEL). It is optionally transmitted in the OSI Transport Layer * connection request (CR). The default local T-SEL byte[] { 0, 0 }. * * @param tSelLocal * the local/calling T-SEL. If null the T-SEL will be omitted. No maximum size is defined by the * standard. */ public void setTSelLocal(byte[] tSelLocal) { acseSap.tSap.tSelLocal = tSelLocal; } /** * Set the maxTPDUSize. The default maxTPduSize is 65531 (see RFC 1006). * * @param maxTPduSizeParam * The maximum length is equal to 2^(maxTPduSizeParam) octets. Note that the actual TSDU size that can be * transfered is equal to TPduSize-3. Default is 65531 octets (see RFC 1006), 7 <= maxTPduSizeParam * <= 16, needs to be set before listening or connecting */ public void setMaxTPduSizeParameter(int maxTPduSizeParam) { acseSap.tSap.setMaxTPDUSizeParam(maxTPduSizeParam); } /** * Sets the remote/called Application Process Title. The default value is int[] { 1, 1, 999, 1, 1 } * * @param title * the remote/called AP title. */ public void setApTitleCalled(int[] title) { acseSap.setApTitleCalled(title); } /** * Sets the local/calling Application Process Title. The default value is int[] { 1, 1, 999, 1 } * * @param title * the local/calling AP title. */ public void setApTitleCalling(int[] title) { acseSap.setApTitleCalling(title); } /** * Sets the remote/called Application Entity Qualifier. The default value is 12. * * @param qualifier * the remote/called AE Qualifier */ public void setAeQualifierCalled(int qualifier) { acseSap.setAeQualifierCalled(qualifier); } /** * Sets the local/calling Application Entity Qualifier. The default value is 12. * * @param qualifier * the local/calling AE Qualifier */ public void setAeQualifierCalling(int qualifier) { acseSap.setAeQualifierCalling(qualifier); } /** * Sets the default response timeout of the ClientAssociation that is created using this ClientSap. * * @param timeout * the response timeout in milliseconds. The default is 20000. */ public void setResponseTimeout(int timeout) { responseTimeout = timeout; } /** * Sets the message fragment timeout. This is the timeout that the socket timeout is set to after the first byte of * a message has been received. A request function (e.g. setDataValues()) will throw an IOException if the socket * throws this timeout because the association/connection cannot recover from this kind of error. * * @param timeout * the timeout in milliseconds. The default is 10000. */ public void setMessageFragmentTimeout(int timeout) { messageFragmentTimeout = timeout; } /** * Connects to the IEC 61850 MMS server at the given address and port and returns the resulting association object. * * @param address * the address to connect to. * @param port * the port to connect to. Usually the MMS port is 102. * @param authenticationParameter * an optional authentication parameters that is transmitted. It will be omitted if equal to null. * @param reportListener * the listener to be notified of incoming reports * @return the association object * @throws IOException * if any kind of error occurs trying build up the association */ public ClientAssociation associate(InetAddress address, int port, String authenticationParameter, ClientEventListener reportListener) throws IOException { return associate(address, port, authenticationParameter, null, -1, reportListener); } /** * Connects to the IEC 61850 MMS server at the given address and port and returns the resulting association object. * * @param address * the address to connect to * @param port * the port to connect to. Usually the MMS port is 102. * @param authenticationParameter * an optional authentication parameters that is transmitted. It will be omitted if equal to null. * @param localAddr * the local address to use * @param localPort * the local port to use * @param reportListener * the listener to be notified of incoming reports * @return the association object. * @throws IOException * if any kind of error occurs trying build up the association */ public ClientAssociation associate(InetAddress address, int port, String authenticationParameter, InetAddress localAddr, int localPort, ClientEventListener reportListener) throws IOException { if (port < 0 || port > 65535) { throw new IllegalArgumentException("invalid port"); } if (address == null) { throw new IllegalArgumentException("address may not be null"); } if (acseSap.sSelRemote == null) { throw new IllegalArgumentException("sSelRemote may not be null"); } if (acseSap.sSelRemote.length != 2) { throw new IllegalArgumentException("sSelRemote lenght must be two"); } if (acseSap.sSelLocal == null) { throw new IllegalArgumentException("sSelLocal may not be null"); } if (acseSap.sSelLocal.length != 2) { throw new IllegalArgumentException("sSelLocal lenght must be two"); } ClientAssociation clientAssociation = new ClientAssociation(address, port, localAddr, localPort, authenticationParameter, acseSap, proposedMaxMmsPduSize, proposedMaxServOutstandingCalling, proposedMaxServOutstandingCalled, proposedDataStructureNestingLevel, servicesSupportedCalling, responseTimeout, messageFragmentTimeout, reportListener); return clientAssociation; } }