| | |
| | | /* |
| | | * 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.io.InputStream; |
| | | import java.net.InetAddress; |
| | | import java.nio.ByteBuffer; |
| | | import java.util.ArrayList; |
| | | import java.util.List; |
| | | import java.util.Timer; |
| | | |
| | | import javax.net.ServerSocketFactory; |
| | | |
| | | import org.openmuc.josistack.AcseAssociation; |
| | | import org.openmuc.josistack.ServerAcseSap; |
| | | |
| | | /** |
| | | * The <code>ServerSap</code> class represents the IEC 61850 service access point for server applications. It |
| | | * corresponds to the AccessPoint defined in the ICD/SCL file. A server application that is to listen for client |
| | | * connections should first get an instance of <code>ServerSap</code> using the static function |
| | | * ServerSap.getSapsFromSclFile(). Next all the necessary configuration parameters can be set. Finally the |
| | | * <code>startListening</code> function is called to listen for client associations. Changing properties of a ServerSap |
| | | * after starting to listen is not recommended and has unknown effects. |
| | | */ |
| | | public final class ServerSap { |
| | | |
| | | static final int MINIMUM_MMS_PDU_SIZE = 64; |
| | | private static final int MAXIMUM_MMS_PDU_SIZE = 65000; |
| | | |
| | | private int proposedMaxMmsPduSize = 65000; |
| | | private int proposedMaxServOutstandingCalling = 5; |
| | | private int proposedMaxServOutstandingCalled = 5; |
| | | private int proposedDataStructureNestingLevel = 10; |
| | | byte[] servicesSupportedCalled = new byte[] { (byte) 0xee, 0x1c, 0, 0, 0x04, 0x08, 0, 0, 0x79, (byte) 0xef, 0x18 }; |
| | | byte[] cbbBitString = { (byte) (0xfb), 0x00 }; |
| | | private int maxAssociations = 100; |
| | | |
| | | ServerEventListener serverEventListener; |
| | | private ServerAcseSap acseSap; |
| | | |
| | | private final String name; |
| | | private int port = 102; |
| | | private int backlog = 0; |
| | | private InetAddress bindAddr = null; |
| | | private ServerSocketFactory serverSocketFactory = null; |
| | | |
| | | Timer timer; |
| | | |
| | | List<ServerAssociation> associations = new ArrayList<>(); |
| | | boolean listening = false; |
| | | |
| | | public final ServerModel serverModel;//2021-5-6 for common model use |
| | | |
| | | public static List<ServerSap> getSapsFromSclFile(String sclFilePath) throws SclParseException { |
| | | SclParser sclParserObject = new SclParser(); |
| | | sclParserObject.parse(sclFilePath); |
| | | return sclParserObject.getServerSaps(); |
| | | } |
| | | |
| | | public static List<ServerSap> getSapsFromSclFile(InputStream sclFileStream) throws SclParseException { |
| | | SclParser sclParserObject = new SclParser(); |
| | | sclParserObject.parse(sclFileStream); |
| | | return sclParserObject.getServerSaps(); |
| | | } |
| | | |
| | | /** |
| | | * Creates a ServerSap. |
| | | * |
| | | * @param port |
| | | * local port to listen on for new connections |
| | | * @param backlog |
| | | * The maximum queue length for incoming connection indications (a request to connect) is set to the |
| | | * backlog parameter. If a connection indication arrives when the queue is full, the connection is |
| | | * refused. Set to 0 or less for the default value. |
| | | * @param bindAddr |
| | | * local IP address to bind to, pass null to bind to all |
| | | * @param serverSocketFactory |
| | | * the factory class to generate the ServerSocket. Could be used to create SSLServerSockets. null = |
| | | * default |
| | | * |
| | | */ |
| | | ServerSap(int port, int backlog, InetAddress bindAddr, ServerModel serverModel, String name, |
| | | ServerSocketFactory serverSocketFactory) { |
| | | this.port = port; |
| | | this.backlog = backlog; |
| | | this.bindAddr = bindAddr; |
| | | this.serverSocketFactory = serverSocketFactory; |
| | | this.name = name; |
| | | this.serverModel = serverModel; |
| | | } |
| | | |
| | | /** |
| | | * Sets local port to listen on for new connections. |
| | | * |
| | | * @param port |
| | | * local port to listen on for new connections |
| | | */ |
| | | public void setPort(int port) { |
| | | this.port = port; |
| | | } |
| | | |
| | | public int getPort() { |
| | | return port; |
| | | } |
| | | |
| | | /** |
| | | * Sets the maximum queue length for incoming connection indications (a request to connect) is set to the backlog |
| | | * parameter. If a connection indication arrives when the queue is full, the connection is refused. Set to 0 or less |
| | | * for the default value. |
| | | * |
| | | * @param backlog |
| | | * the maximum queue length for incoming connections. |
| | | */ |
| | | public void setBacklog(int backlog) { |
| | | this.backlog = backlog; |
| | | } |
| | | |
| | | public int getBacklog() { |
| | | return backlog; |
| | | } |
| | | |
| | | /** |
| | | * Sets the local IP address to bind to, pass null to bind to all |
| | | * |
| | | * @param bindAddr |
| | | * the local IP address to bind to |
| | | */ |
| | | public void setBindAddress(InetAddress bindAddr) { |
| | | this.bindAddr = bindAddr; |
| | | } |
| | | |
| | | public InetAddress getBindAddress() { |
| | | return bindAddr; |
| | | } |
| | | |
| | | /** |
| | | * Returns the name of the ServerSap / AccessPoint as specified in the SCL file. |
| | | * |
| | | * @return the name. |
| | | */ |
| | | public String getName() { |
| | | return name; |
| | | } |
| | | |
| | | /** |
| | | * Sets the factory class to generate the ServerSocket. The ServerSocketFactory could be used to create |
| | | * SSLServerSockets. Set to <code>null</code> to use <code>ServerSocketFactory.getDefault()</code>. |
| | | * |
| | | * @param serverSocketFactory |
| | | * the factory class to generate the ServerSocket. |
| | | */ |
| | | public void setServerSocketFactory(ServerSocketFactory serverSocketFactory) { |
| | | this.serverSocketFactory = serverSocketFactory; |
| | | } |
| | | |
| | | /** |
| | | * Sets the maximum MMS PDU size in bytes that the server will support. If the client requires the use of a smaller |
| | | * maximum MMS PDU size, then the smaller size will be accepted by the server. 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; |
| | | } |
| | | |
| | | /** |
| | | * Set the maximum number of associations that are allowed in parallel by the server. |
| | | * |
| | | * @param maxAssociations |
| | | * the number of associations allowed (default is 100) |
| | | */ |
| | | public void setMaxAssociations(int maxAssociations) { |
| | | this.maxAssociations = maxAssociations; |
| | | } |
| | | |
| | | /** |
| | | * 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. If such a timeout is thrown, the association/socket is closed. |
| | | * |
| | | * @param timeout |
| | | * the message fragment timeout in milliseconds. The default is 60000. |
| | | */ |
| | | public void setMessageFragmentTimeout(int timeout) { |
| | | acseSap.serverTSap.setMessageFragmentTimeout(timeout); |
| | | } |
| | | |
| | | /** |
| | | * Sets the ProposedMaxServOutstandingCalling parameter. The given parameter has no affect on the functionality of |
| | | * this server. |
| | | * |
| | | * @param maxCalling |
| | | * the ProposedMaxServOutstandingCalling parameter. The default is 5. |
| | | */ |
| | | public void setProposedMaxServOutstandingCalling(int maxCalling) { |
| | | proposedMaxServOutstandingCalling = maxCalling; |
| | | } |
| | | |
| | | /** |
| | | * Gets the ProposedMaxServOutstandingCalling parameter. |
| | | * |
| | | * @return the ProposedMaxServOutstandingCalling parameter. |
| | | */ |
| | | public int getProposedMaxServOutstandingCalling() { |
| | | return proposedMaxServOutstandingCalling; |
| | | } |
| | | |
| | | /** |
| | | * Sets the ProposedMaxServOutstandingCalled parameter.The given parameter has no affect on the functionality of |
| | | * this server. |
| | | * |
| | | * @param maxCalled |
| | | * the ProposedMaxServOutstandingCalled parameter. The default is 5. |
| | | */ |
| | | public void setProposedMaxServOutstandingCalled(int maxCalled) { |
| | | proposedMaxServOutstandingCalled = maxCalled; |
| | | } |
| | | |
| | | /** |
| | | * Gets the ProposedMaxServOutstandingCalled parameter. |
| | | * |
| | | * @return the ProposedMaxServOutstandingCalled parameter. |
| | | */ |
| | | public int getProposedMaxServOutstandingCalled() { |
| | | return proposedMaxServOutstandingCalled; |
| | | } |
| | | |
| | | /** |
| | | * Sets the ProposedDataStructureNestingLevel parameter. The given parameter has no affect on the functionality of |
| | | * this server.runServer |
| | | * |
| | | * @param nestingLevel |
| | | * the ProposedDataStructureNestingLevel parameter. The default is 10. |
| | | */ |
| | | public void setProposedDataStructureNestingLevel(int nestingLevel) { |
| | | proposedDataStructureNestingLevel = nestingLevel; |
| | | } |
| | | |
| | | /** |
| | | * Gets the ProposedDataStructureNestingLevel parameter. |
| | | * |
| | | * @return the ProposedDataStructureNestingLevel parameter. |
| | | */ |
| | | public int getProposedDataStructureNestingLevel() { |
| | | return proposedDataStructureNestingLevel; |
| | | } |
| | | |
| | | /** |
| | | * Sets the SevicesSupportedCalled parameter. The given parameter has no affect on the functionality of this server. |
| | | * |
| | | * @param services |
| | | * the ServicesSupportedCalled parameter |
| | | */ |
| | | public void setServicesSupportedCalled(byte[] services) { |
| | | if (services.length != 11) { |
| | | throw new IllegalArgumentException("The services parameter needs to be of lenth 11"); |
| | | } |
| | | servicesSupportedCalled = services; |
| | | } |
| | | |
| | | /** |
| | | * Gets the ServicesSupportedCalled parameter. |
| | | * |
| | | * @return the ServicesSupportedCalled parameter. |
| | | */ |
| | | public byte[] getServicesSupportedCalled() { |
| | | return servicesSupportedCalled; |
| | | } |
| | | |
| | | /** |
| | | * Creates a server socket waiting on the configured port for incoming association requests. |
| | | * |
| | | * @param serverEventListener |
| | | * the listener that is notified of incoming writes and when the server stopped listening for new |
| | | * connections. |
| | | * @throws IOException |
| | | * if an error occurs binding to the port. |
| | | */ |
| | | public void startListening(ServerEventListener serverEventListener) throws IOException { |
| | | timer = new Timer(); |
| | | if (serverSocketFactory == null) { |
| | | serverSocketFactory = ServerSocketFactory.getDefault(); |
| | | } |
| | | acseSap = new ServerAcseSap(port, backlog, bindAddr, new AcseListener(this), serverSocketFactory); |
| | | acseSap.serverTSap.setMaxConnections(maxAssociations); |
| | | this.serverEventListener = serverEventListener; |
| | | listening = true; |
| | | acseSap.startListening(); |
| | | } |
| | | |
| | | /** |
| | | * Stops listening for new connections and closes all existing connections/associations. |
| | | */ |
| | | public void stop() { |
| | | acseSap.stopListening(); |
| | | synchronized (associations) { |
| | | listening = false; |
| | | for (ServerAssociation association : associations) { |
| | | association.close(); |
| | | } |
| | | associations.clear(); |
| | | } |
| | | timer.cancel(); |
| | | timer.purge(); |
| | | } |
| | | |
| | | void connectionIndication(AcseAssociation acseAssociation, ByteBuffer psdu) { |
| | | |
| | | ServerAssociation association; |
| | | synchronized (associations) { |
| | | if (listening) { |
| | | association = new ServerAssociation(this); |
| | | associations.add(association); |
| | | } |
| | | else { |
| | | acseAssociation.close(); |
| | | return; |
| | | } |
| | | } |
| | | |
| | | try { |
| | | association.handleNewAssociation(acseAssociation, psdu); |
| | | } catch (Exception e) { |
| | | // Association closed because of an unexpected exception. |
| | | } |
| | | |
| | | association.close(); |
| | | synchronized (associations) { |
| | | associations.remove(association); |
| | | } |
| | | } |
| | | |
| | | void serverStoppedListeningIndication(IOException e) { |
| | | if (serverEventListener != null) { |
| | | serverEventListener.serverStoppedListening(this); |
| | | } |
| | | } |
| | | |
| | | public ServerModel getModelCopy() { |
| | | return serverModel.copy(); |
| | | } |
| | | |
| | | public void setValues(List<BasicDataAttribute> bdas) { |
| | | synchronized (serverModel) { |
| | | for (BasicDataAttribute bda : bdas) { |
| | | // if (bda.getFunctionalConstraint() != FunctionalConstraint.ST) { |
| | | // logger.debug("fc:" + bda.getFunctionalConstraint()); |
| | | // throw new IllegalArgumentException( |
| | | // "One can only set values of BDAs with Functional Constraint ST(status)"); |
| | | // } |
| | | |
| | | BasicDataAttribute bdaMirror = bda.mirror; |
| | | |
| | | if (bdaMirror.dchg && bdaMirror.chgRcbs.size() != 0 && !bda.equals(bdaMirror)) { |
| | | bdaMirror.setValueFrom(bda); |
| | | synchronized (bdaMirror.chgRcbs) { |
| | | for (Urcb urcb : bdaMirror.chgRcbs) { |
| | | if (bdaMirror.dupd && urcb.getTrgOps().isDataUpdate()) { |
| | | urcb.report(bdaMirror, true, false, true); |
| | | } |
| | | else { |
| | | urcb.report(bdaMirror, true, false, false); |
| | | } |
| | | } |
| | | } |
| | | } |
| | | else if (bdaMirror.dupd && bdaMirror.dupdRcbs.size() != 0) { |
| | | bdaMirror.setValueFrom(bda); |
| | | synchronized (bdaMirror.dupdRcbs) { |
| | | for (Urcb urcb : bdaMirror.dupdRcbs) { |
| | | urcb.report(bdaMirror, false, false, true); |
| | | } |
| | | } |
| | | } |
| | | else if (bdaMirror.qchg && bdaMirror.chgRcbs.size() != 0 && !bda.equals(bdaMirror)) { |
| | | bdaMirror.setValueFrom(bda); |
| | | synchronized (bdaMirror.chgRcbs) { |
| | | for (Urcb urcb : bdaMirror.chgRcbs) { |
| | | urcb.report(bdaMirror, false, true, false); |
| | | } |
| | | } |
| | | } |
| | | else { |
| | | bdaMirror.setValueFrom(bda); |
| | | } |
| | | } |
| | | } |
| | | } |
| | | } |
| | | /*
|
| | | * 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.io.InputStream;
|
| | | import java.net.InetAddress;
|
| | | import java.nio.ByteBuffer;
|
| | | import java.util.ArrayList;
|
| | | import java.util.List;
|
| | | import java.util.Timer;
|
| | |
|
| | | import javax.net.ServerSocketFactory;
|
| | |
|
| | | import org.openmuc.josistack.AcseAssociation;
|
| | | import org.openmuc.josistack.ServerAcseSap;
|
| | |
|
| | | /**
|
| | | * The <code>ServerSap</code> class represents the IEC 61850 service access point for server applications. It
|
| | | * corresponds to the AccessPoint defined in the ICD/SCL file. A server application that is to listen for client
|
| | | * connections should first get an instance of <code>ServerSap</code> using the static function
|
| | | * ServerSap.getSapsFromSclFile(). Next all the necessary configuration parameters can be set. Finally the
|
| | | * <code>startListening</code> function is called to listen for client associations. Changing properties of a ServerSap
|
| | | * after starting to listen is not recommended and has unknown effects.
|
| | | */
|
| | | public final class ServerSap {
|
| | |
|
| | | static final int MINIMUM_MMS_PDU_SIZE = 64;
|
| | | private static final int MAXIMUM_MMS_PDU_SIZE = 65000;
|
| | |
|
| | | private int proposedMaxMmsPduSize = 65000;
|
| | | private int proposedMaxServOutstandingCalling = 5;
|
| | | private int proposedMaxServOutstandingCalled = 5;
|
| | | private int proposedDataStructureNestingLevel = 10;
|
| | | byte[] servicesSupportedCalled = new byte[] { (byte) 0xee, 0x1c, 0, 0, 0x04, 0x08, 0, 0, 0x79, (byte) 0xef, 0x18 };
|
| | | byte[] cbbBitString = { (byte) (0xfb), 0x00 };
|
| | | private int maxAssociations = 100;
|
| | |
|
| | | ServerEventListener serverEventListener;
|
| | | private ServerAcseSap acseSap;
|
| | |
|
| | | private final String name;
|
| | | private int port = 102;
|
| | | private int backlog = 0;
|
| | | private InetAddress bindAddr = null;
|
| | | private ServerSocketFactory serverSocketFactory = null;
|
| | |
|
| | | Timer timer;
|
| | |
|
| | | List<ServerAssociation> associations = new ArrayList<>();
|
| | | boolean listening = false;
|
| | |
|
| | | public final ServerModel serverModel;
|
| | |
|
| | | public static List<ServerSap> getSapsFromSclFile(String sclFilePath) throws SclParseException {
|
| | | SclParser sclParserObject = new SclParser();
|
| | | sclParserObject.parse(sclFilePath);
|
| | | return sclParserObject.getServerSaps();
|
| | | }
|
| | |
|
| | | public static List<ServerSap> getSapsFromSclFile(InputStream sclFileStream) throws SclParseException {
|
| | | SclParser sclParserObject = new SclParser();
|
| | | sclParserObject.parse(sclFileStream);
|
| | | return sclParserObject.getServerSaps();
|
| | | }
|
| | |
|
| | | /**
|
| | | * Creates a ServerSap.
|
| | | * |
| | | * @param port
|
| | | * local port to listen on for new connections
|
| | | * @param backlog
|
| | | * The maximum queue length for incoming connection indications (a request to connect) is set to the
|
| | | * backlog parameter. If a connection indication arrives when the queue is full, the connection is
|
| | | * refused. Set to 0 or less for the default value.
|
| | | * @param bindAddr
|
| | | * local IP address to bind to, pass null to bind to all
|
| | | * @param serverSocketFactory
|
| | | * the factory class to generate the ServerSocket. Could be used to create SSLServerSockets. null =
|
| | | * default
|
| | | * |
| | | */
|
| | | ServerSap(int port, int backlog, InetAddress bindAddr, ServerModel serverModel, String name,
|
| | | ServerSocketFactory serverSocketFactory) {
|
| | | this.port = port;
|
| | | this.backlog = backlog;
|
| | | this.bindAddr = bindAddr;
|
| | | this.serverSocketFactory = serverSocketFactory;
|
| | | this.name = name;
|
| | | this.serverModel = serverModel;
|
| | | }
|
| | |
|
| | | /**
|
| | | * Sets local port to listen on for new connections.
|
| | | * |
| | | * @param port
|
| | | * local port to listen on for new connections
|
| | | */
|
| | | public void setPort(int port) {
|
| | | this.port = port;
|
| | | }
|
| | |
|
| | | public int getPort() {
|
| | | return port;
|
| | | }
|
| | |
|
| | | /**
|
| | | * Sets the maximum queue length for incoming connection indications (a request to connect) is set to the backlog
|
| | | * parameter. If a connection indication arrives when the queue is full, the connection is refused. Set to 0 or less
|
| | | * for the default value.
|
| | | * |
| | | * @param backlog
|
| | | * the maximum queue length for incoming connections.
|
| | | */
|
| | | public void setBacklog(int backlog) {
|
| | | this.backlog = backlog;
|
| | | }
|
| | |
|
| | | public int getBacklog() {
|
| | | return backlog;
|
| | | }
|
| | |
|
| | | /**
|
| | | * Sets the local IP address to bind to, pass null to bind to all
|
| | | * |
| | | * @param bindAddr
|
| | | * the local IP address to bind to
|
| | | */
|
| | | public void setBindAddress(InetAddress bindAddr) {
|
| | | this.bindAddr = bindAddr;
|
| | | }
|
| | |
|
| | | public InetAddress getBindAddress() {
|
| | | return bindAddr;
|
| | | }
|
| | |
|
| | | /**
|
| | | * Returns the name of the ServerSap / AccessPoint as specified in the SCL file.
|
| | | * |
| | | * @return the name.
|
| | | */
|
| | | public String getName() {
|
| | | return name;
|
| | | }
|
| | |
|
| | | /**
|
| | | * Sets the factory class to generate the ServerSocket. The ServerSocketFactory could be used to create
|
| | | * SSLServerSockets. Set to <code>null</code> to use <code>ServerSocketFactory.getDefault()</code>.
|
| | | * |
| | | * @param serverSocketFactory
|
| | | * the factory class to generate the ServerSocket.
|
| | | */
|
| | | public void setServerSocketFactory(ServerSocketFactory serverSocketFactory) {
|
| | | this.serverSocketFactory = serverSocketFactory;
|
| | | }
|
| | |
|
| | | /**
|
| | | * Sets the maximum MMS PDU size in bytes that the server will support. If the client requires the use of a smaller
|
| | | * maximum MMS PDU size, then the smaller size will be accepted by the server. 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;
|
| | | }
|
| | |
|
| | | /**
|
| | | * Set the maximum number of associations that are allowed in parallel by the server.
|
| | | * |
| | | * @param maxAssociations
|
| | | * the number of associations allowed (default is 100)
|
| | | */
|
| | | public void setMaxAssociations(int maxAssociations) {
|
| | | this.maxAssociations = maxAssociations;
|
| | | }
|
| | |
|
| | | /**
|
| | | * 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. If such a timeout is thrown, the association/socket is closed.
|
| | | * |
| | | * @param timeout
|
| | | * the message fragment timeout in milliseconds. The default is 60000.
|
| | | */
|
| | | public void setMessageFragmentTimeout(int timeout) {
|
| | | acseSap.serverTSap.setMessageFragmentTimeout(timeout);
|
| | | }
|
| | |
|
| | | /**
|
| | | * Sets the ProposedMaxServOutstandingCalling parameter. The given parameter has no affect on the functionality of
|
| | | * this server.
|
| | | * |
| | | * @param maxCalling
|
| | | * the ProposedMaxServOutstandingCalling parameter. The default is 5.
|
| | | */
|
| | | public void setProposedMaxServOutstandingCalling(int maxCalling) {
|
| | | proposedMaxServOutstandingCalling = maxCalling;
|
| | | }
|
| | |
|
| | | /**
|
| | | * Gets the ProposedMaxServOutstandingCalling parameter.
|
| | | * |
| | | * @return the ProposedMaxServOutstandingCalling parameter.
|
| | | */
|
| | | public int getProposedMaxServOutstandingCalling() {
|
| | | return proposedMaxServOutstandingCalling;
|
| | | }
|
| | |
|
| | | /**
|
| | | * Sets the ProposedMaxServOutstandingCalled parameter.The given parameter has no affect on the functionality of
|
| | | * this server.
|
| | | * |
| | | * @param maxCalled
|
| | | * the ProposedMaxServOutstandingCalled parameter. The default is 5.
|
| | | */
|
| | | public void setProposedMaxServOutstandingCalled(int maxCalled) {
|
| | | proposedMaxServOutstandingCalled = maxCalled;
|
| | | }
|
| | |
|
| | | /**
|
| | | * Gets the ProposedMaxServOutstandingCalled parameter.
|
| | | * |
| | | * @return the ProposedMaxServOutstandingCalled parameter.
|
| | | */
|
| | | public int getProposedMaxServOutstandingCalled() {
|
| | | return proposedMaxServOutstandingCalled;
|
| | | }
|
| | |
|
| | | /**
|
| | | * Sets the ProposedDataStructureNestingLevel parameter. The given parameter has no affect on the functionality of
|
| | | * this server.runServer
|
| | | * |
| | | * @param nestingLevel
|
| | | * the ProposedDataStructureNestingLevel parameter. The default is 10.
|
| | | */
|
| | | public void setProposedDataStructureNestingLevel(int nestingLevel) {
|
| | | proposedDataStructureNestingLevel = nestingLevel;
|
| | | }
|
| | |
|
| | | /**
|
| | | * Gets the ProposedDataStructureNestingLevel parameter.
|
| | | * |
| | | * @return the ProposedDataStructureNestingLevel parameter.
|
| | | */
|
| | | public int getProposedDataStructureNestingLevel() {
|
| | | return proposedDataStructureNestingLevel;
|
| | | }
|
| | |
|
| | | /**
|
| | | * Sets the SevicesSupportedCalled parameter. The given parameter has no affect on the functionality of this server.
|
| | | * |
| | | * @param services
|
| | | * the ServicesSupportedCalled parameter
|
| | | */
|
| | | public void setServicesSupportedCalled(byte[] services) {
|
| | | if (services.length != 11) {
|
| | | throw new IllegalArgumentException("The services parameter needs to be of lenth 11");
|
| | | }
|
| | | servicesSupportedCalled = services;
|
| | | }
|
| | |
|
| | | /**
|
| | | * Gets the ServicesSupportedCalled parameter.
|
| | | * |
| | | * @return the ServicesSupportedCalled parameter.
|
| | | */
|
| | | public byte[] getServicesSupportedCalled() {
|
| | | return servicesSupportedCalled;
|
| | | }
|
| | |
|
| | | /**
|
| | | * Creates a server socket waiting on the configured port for incoming association requests.
|
| | | * |
| | | * @param serverEventListener
|
| | | * the listener that is notified of incoming writes and when the server stopped listening for new
|
| | | * connections.
|
| | | * @throws IOException
|
| | | * if an error occurs binding to the port.
|
| | | */
|
| | | public void startListening(ServerEventListener serverEventListener) throws IOException {
|
| | | timer = new Timer();
|
| | | if (serverSocketFactory == null) {
|
| | | serverSocketFactory = ServerSocketFactory.getDefault();
|
| | | }
|
| | | acseSap = new ServerAcseSap(port, backlog, bindAddr, new AcseListener(this), serverSocketFactory);
|
| | | acseSap.serverTSap.setMaxConnections(maxAssociations);
|
| | | this.serverEventListener = serverEventListener;
|
| | | listening = true;
|
| | | acseSap.startListening();
|
| | | }
|
| | |
|
| | | /**
|
| | | * Stops listening for new connections and closes all existing connections/associations.
|
| | | */
|
| | | public void stop() {
|
| | | acseSap.stopListening();
|
| | | synchronized (associations) {
|
| | | listening = false;
|
| | | for (ServerAssociation association : associations) {
|
| | | association.close();
|
| | | }
|
| | | associations.clear();
|
| | | }
|
| | | timer.cancel();
|
| | | timer.purge();
|
| | | }
|
| | |
|
| | | void connectionIndication(AcseAssociation acseAssociation, ByteBuffer psdu) {
|
| | |
|
| | | ServerAssociation association;
|
| | | synchronized (associations) {
|
| | | if (listening) {
|
| | | association = new ServerAssociation(this);
|
| | | associations.add(association);
|
| | | }
|
| | | else {
|
| | | acseAssociation.close();
|
| | | return;
|
| | | }
|
| | | }
|
| | |
|
| | | try {
|
| | | association.handleNewAssociation(acseAssociation, psdu);
|
| | | } catch (Exception e) {
|
| | | // Association closed because of an unexpected exception.
|
| | | }
|
| | |
|
| | | association.close();
|
| | | synchronized (associations) {
|
| | | associations.remove(association);
|
| | | }
|
| | | }
|
| | |
|
| | | void serverStoppedListeningIndication(IOException e) {
|
| | | if (serverEventListener != null) {
|
| | | serverEventListener.serverStoppedListening(this);
|
| | | }
|
| | | }
|
| | |
|
| | | public ServerModel getModelCopy() {
|
| | | return serverModel.copy();
|
| | | }
|
| | |
|
| | | public void setValues(List<BasicDataAttribute> bdas) {
|
| | | synchronized (serverModel) {
|
| | | for (BasicDataAttribute bda : bdas) {
|
| | | // if (bda.getFunctionalConstraint() != FunctionalConstraint.ST) {
|
| | | // logger.debug("fc:" + bda.getFunctionalConstraint());
|
| | | // throw new IllegalArgumentException(
|
| | | // "One can only set values of BDAs with Functional Constraint ST(status)");
|
| | | // }
|
| | |
|
| | | BasicDataAttribute bdaMirror = bda.mirror;
|
| | |
|
| | | if (bdaMirror.dchg && bdaMirror.chgRcbs.size() != 0 && !bda.equals(bdaMirror)) {
|
| | | bdaMirror.setValueFrom(bda);
|
| | | synchronized (bdaMirror.chgRcbs) {
|
| | | for (Urcb urcb : bdaMirror.chgRcbs) {
|
| | | if (bdaMirror.dupd && urcb.getTrgOps().isDataUpdate()) {
|
| | | urcb.report(bdaMirror, true, false, true);
|
| | | }
|
| | | else {
|
| | | urcb.report(bdaMirror, true, false, false);
|
| | | }
|
| | | }
|
| | | }
|
| | | }
|
| | | else if (bdaMirror.dupd && bdaMirror.dupdRcbs.size() != 0) {
|
| | | bdaMirror.setValueFrom(bda);
|
| | | synchronized (bdaMirror.dupdRcbs) {
|
| | | for (Urcb urcb : bdaMirror.dupdRcbs) {
|
| | | urcb.report(bdaMirror, false, false, true);
|
| | | }
|
| | | }
|
| | | }
|
| | | else if (bdaMirror.qchg && bdaMirror.chgRcbs.size() != 0 && !bda.equals(bdaMirror)) {
|
| | | bdaMirror.setValueFrom(bda);
|
| | | synchronized (bdaMirror.chgRcbs) {
|
| | | for (Urcb urcb : bdaMirror.chgRcbs) {
|
| | | urcb.report(bdaMirror, false, true, false);
|
| | | }
|
| | | }
|
| | | }
|
| | | else {
|
| | | bdaMirror.setValueFrom(bda);
|
| | | }
|
| | | }
|
| | | }
|
| | | }
|
| | | }
|