diff --git a/build.gradle b/build.gradle
index 1287c91..3181b66 100644
--- a/build.gradle
+++ b/build.gradle
@@ -6,15 +6,35 @@ buildscript {
}
dependencies {
classpath "net.saliman:gradle-cobertura-plugin:2.2.8"
+ classpath group: "org.asciidoctor", name: "asciidoctorj-pdf", version: "1.5.0-alpha.11"
+
}
}
plugins {
id "org.sonarqube" version "1.0"
+ id "org.asciidoctor.convert" version "1.5.3"
}
+apply plugin: "org.asciidoctor.convert"
+apply plugin: "java"
apply from: "configuration.gradle"
+asciidoctor {
+ backends = ["pdf", "html5"]
+ attributes "stylesheet": "openmuc-asciidoc.css",
+ "toc2": "left",
+ "sampleSrc": file("src/sample/java"),
+ "pdf-stylesdir": "./",
+ "pdf-style": "pdf"
+
+ resources {
+ from("$sourceDir") {
+ include "images/**"
+ }
+ }
+}
+
configure(allprojects) {
version = cfgVersion
}
@@ -122,8 +142,9 @@ configure(javaProjects) {
}
}
}
-
- build.dependsOn(jarAll)
+
+ build.dependsOn {asciidoctor}
+ build.dependsOn {jarAll}
eclipse.pathVariables([GRADLE_USER_HOME:file(gradle.gradleUserHomeDir)])
tasks.eclipse.dependsOn(cleanEclipse)
@@ -158,7 +179,7 @@ configure(repositoryProjects) {
if (cfgSignPom) {
signing {
if ( project.hasProperty('signing.keyId') ) {
- sign configurations.archives
+ sign configurations.archives
}
}
}
@@ -220,6 +241,8 @@ task javadocAll(type: Javadoc) {
}
exclude '**/internal/**'
+ exclude '**/java-gen/**'
+ exclude '**/app/**'
destinationDir = new File(buildDir, 'docs/javadoc-all')
@@ -267,6 +290,7 @@ tasks.withType(Tar) {
dependsOn(writeSettings)
dependsOn(distributionProjects.build)
dependsOn(javadocAll)
+ dependsOn(asciidoctor)
compression = Compression.GZIP
diff --git a/configuration.gradle b/configuration.gradle
index eccdc95..a82d388 100644
--- a/configuration.gradle
+++ b/configuration.gradle
@@ -1,7 +1,7 @@
project.ext {
- cfgVersion = '1.0.0'
+ cfgVersion = '1.1.0'
cfgGroup = 'org.openmuc'
@@ -35,12 +35,12 @@ tasks.withType(Tar) {
include 'build.gradle'
include 'configuration.gradle'
include 'license/**'
- include 'doc/*.txt'
- include 'doc/userguide/' + project.name + '-doc*.html'
- include 'doc/userguide/' + project.name + '-doc_img/**'
+ include 'doc/CHANGELOG.txt'
include 'run-scripts/**'
+ include 'gradle/wrapper/**'
+ include 'gradlew'
+ include 'gradlew.bat'
include 'build/libs/**'
- include 'build/docs/javadoc/**'
include 'src/**'
}
@@ -49,6 +49,22 @@ tasks.withType(Tar) {
}
}
+
+ into(project.name + '/doc/user-guide/') {
+ from('./build/asciidoc/html5/') {
+ include '**'
+ }
+ from('./build/asciidoc/pdf/') {
+ include '**'
+ }
+ }
+
+ into(project.name + '/doc/') {
+ from('./build/docs/') {
+ include 'javadoc/**'
+ }
+ }
+
}
@@ -87,4 +103,3 @@ uploadArchives {
}
}
}
-
diff --git a/doc/CHANGELOG.txt b/doc/CHANGELOG.txt
index 6dceb84..e52ee61 100644
--- a/doc/CHANGELOG.txt
+++ b/doc/CHANGELOG.txt
@@ -1,5 +1,14 @@
-v1.0 24-Feb-2016
-----------------
+v1.1.0 06-Jun-2016
+------------------
+- Renamed ClientSap to ClientConnectionBuilder and ServerSap to
+ Server.Builder
+- fixed time conversion to ms of IeTime24 thanks to Jürgen Wieferink
+ from BTC AG
+- added stopListening() to ServerSap allowing for clean close
+- refactored console client
+
+v1.0.0 24-Feb-2016
+------------------
- fixed bug when creating multiple parallel connections using
the same ClientSap
- fixed a synchronization problem in the server implementation
diff --git a/doc/user-guide/j60870-doc.html b/doc/user-guide/j60870-doc.html
new file mode 100644
index 0000000..13e991b
--- /dev/null
+++ b/doc/user-guide/j60870-doc.html
@@ -0,0 +1,581 @@
+
+
+
+
+
+
+
+j60870 User Guide
+
+
+
+
+
+
+
1. Intro
+
+
+
j60870 is an implementation of the IEC 60870-5-104 protocol standard
+for client (i.e. master or controlling station) and server (i.e. slave
+or controlled station) applications.
+
+
+
You can use j60870 to program your individual client or server
+applications. A simple console client is part of the library. You can
+execute it using the scripts found in the folder "run-scripts".
+
+
+
+
+
2. Distribution
+
+
+
After extracting the distribution tar file the j60870 library can be
+found in the folder /build/libs.
+
+
+
+
+
3. Getting Started
+
+
+
3.1. Client
+
+
The easiest way to get started is by taking a look at the code of the
+console client which can be found here:
+src/main/java/org/openmuc/j60870/app/ConsoleClient.java . This
+application in combination with the javadoc should satisfy most of
+your documentation needs.
+
+
+
Here is a short summary of the steps to get a client running:
+
+
+
+-
+
Create and configure an instance of ClientConnectionBuilder.
+
+-
+
Connect to the server using ClientConnectionBuilder.connect() which
+returns the connection. The client is now connected to the server
+via TCP/IP.
+
+-
+
Initialize the data transfer by calling
+Connection.startDataTransfer.
+
+-
+
Now all incoming ASDUs (except for confirmation messages) will be
+forwarded to the ASduListener you registered. Every ASDU contains a
+number of Information Objects. The information objects contain
+information elements that make up the actual data. You will have to
+cast the InformationElements of the ASDU to a concrete
+implementation in order to access the data inside them. Every
+standardized Information Element is implemented by a class starting
+with the letters "Ie". The Type Identifier allows you to figure what
+to cast a particular Information Element to.
+
+-
+
You can use the Connection instance to send commands.
+
+
+
+
+
+
3.2. Server
+
+
The easiest way to get started programming an IEC 60870-5-104 server
+is by taking a look at the SampleServer located at:
+src/sample/java/SampleServer.java . The easiest way to run the sample
+server is by importing the project into Eclipse and run it from
+there. This is explained here:
+https://www.openmuc.org/faq/
+
+
+
+
+
+
4. Terminology
+
+
+
+-
+
OA - Originator Address
+
+-
+
Monitor direction - direction from server to client
+
+-
+
Control direction - direction from client to server
+
+-
+
CON - confirmation message
+
+-
+
COT - Cause of transmission
+
+-
+
STARTDT ACT - Start data tranfer message. Needs to be sent by the
+client before information messages may be exchanged between client
+and server.
+
+
+
+
+
+
+
5. Develop j60870
+
+
+
We use the Gradle build automation tool. The distribution contains a
+fully functional gradle build file ("build.gradle"). Thus if you
+changed code and want to rebuild a library you can do it easily with
+Gradle. Also if you want to import our software into Eclipse you can
+easily create Eclipse project files using Gradle. Just follow the
+instructions on our FAQ site:
+https://www.openmuc.org/faq/
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/doc/user-guide/j60870-doc.pdf b/doc/user-guide/j60870-doc.pdf
new file mode 100644
index 0000000..02dd63a
Binary files /dev/null and b/doc/user-guide/j60870-doc.pdf differ
diff --git a/doc/userguide/j60870-doc-frames.html b/doc/userguide/j60870-doc-frames.html
deleted file mode 100644
index 5cd4e0e..0000000
--- a/doc/userguide/j60870-doc-frames.html
+++ /dev/null
@@ -1,807 +0,0 @@
-
-
-
-
-
-j60870 User Guide
-
-
-
-
-
-
-
-
1. Intro
-
-
j60870 is an implementation of the IEC 60870-5-104 protocol standard
-for client (i.e. master or controlling station) and server (i.e. slave
-or controlled station) applications.
-
You can use j60870 to program your individual client or server
-applications. A simple command line ClientApp is part of the
-library. You can execute it using the scripts found in the folder
-"run-scripts".
-
-
-
-
2. Distribution
-
-
After extracting the distribution tar file the j60870 library can be
-found in the folder /build/libs.
-
The javadoc for j60870 can be found in the folder build/docs/javadoc.
-
-
-
-
3. Getting Started
-
-
-
3.1. Client
-
The easiest way to get started is by taking a look at the code of the
-ClientApp which can be found here:
-src/main/java/org/openmuc/j60870/app/ClientApp.java . This application
-in combination with the javadoc should satisfy most of your
-documentation needs.
-
Here is a short summary of the steps to get a client running:
-
--
-
-Create an instance of ClientSap.
-
-
--
-
-Configure the ClientSap.
-
-
--
-
-Connect to the server using ClientSap.connect which returns the
- connection. The client is now connected to the server via
- TCP/IP.
-
-
--
-
-Initialize the data transfer by calling
- Connection.startDataTransfer.
-
-
--
-
-Now all incoming ASDUs (except for confirmation messages) will be
- forwarded to the ASduListener you registered. Every ASDU contains a
- number of Information Objects. The information objects contain
- information elements that make up the actual data. You will have to
- cast the InformationElements of the ASDU to a concrete
- implementation in order to access the data inside them. Every
- standardized Information Element is implemented by a class starting
- with the letters "Ie". The Type Identifier allows you to figure what
- to cast a particular Information Element to.
-
-
--
-
-You can use the Connection instance to send commands.
-
-
-
-
-
-
3.2. Server
-
The easiest way to get started programming an IEC 60870-5-104 server
-is by taking a look at the SampleServer located at:
-src/sample/java/SampleServer.java . The easiest way to run the sample
-server is by importing the project into Eclipse and run it from
-there. This is explained here:
-http://www.openmuc.org/index.php?id=72#faq_gradle
-
-
-
-
-
4. Terminology
-
-
--
-
-OA - Originator Address
-
-
--
-
-Monitor direction - direction from server to client
-
-
--
-
-Control direction - direction from client to server
-
-
--
-
-CON - confirmation message
-
-
--
-
-COT - Cause of transmission
-
-
--
-
-STARTDT ACT - Start data tranfer message. Needs to be sent by the
- client before information messages may be exchanged between client
- and server.
-
-
-
-
-
-
-
5. Develop j60870
-
-
We use the Gradle build automation tool. The distribution contains a
-fully functional gradle build file ("build.gradle"). Thus if you
-changed code and want to rebuild a library you can do it easily with
-Gradle. Also if you want to import our software into Eclipse you can
-easily create Eclipse project files using Gradle. Just follow the
-instructions on our FAQ site:
-http://www.openmuc.org/index.php?id=72#faq_gradle
-
-
-
-
6. Authors
-
-
-
--
-
-Stefan Feuerhahn
-
-
-
-
-
-
-
-
-
-
diff --git a/doc/userguide/j60870-doc.html b/doc/userguide/j60870-doc.html
deleted file mode 100644
index 4a927fe..0000000
--- a/doc/userguide/j60870-doc.html
+++ /dev/null
@@ -1,773 +0,0 @@
-
-
-
-
-
-j60870 User Guide
-
-
-
-
-
-
-
-
1. Intro
-
-
j60870 is an implementation of the IEC 60870-5-104 protocol standard
-for client (i.e. master or controlling station) and server (i.e. slave
-or controlled station) applications.
-
You can use j60870 to program your individual client or server
-applications. A simple command line ClientApp is part of the
-library. You can execute it using the scripts found in the folder
-"run-scripts".
-
-
-
-
2. Distribution
-
-
After extracting the distribution tar file the j60870 library can be
-found in the folder /build/libs.
-
The javadoc for j60870 can be found in the folder build/docs/javadoc.
-
-
-
-
3. Getting Started
-
-
-
3.1. Client
-
The easiest way to get started is by taking a look at the code of the
-ClientApp which can be found here:
-src/main/java/org/openmuc/j60870/app/ClientApp.java . This application
-in combination with the javadoc should satisfy most of your
-documentation needs.
-
Here is a short summary of the steps to get a client running:
-
--
-
-Create an instance of ClientSap.
-
-
--
-
-Configure the ClientSap.
-
-
--
-
-Connect to the server using ClientSap.connect which returns the
- connection. The client is now connected to the server via
- TCP/IP.
-
-
--
-
-Initialize the data transfer by calling
- Connection.startDataTransfer.
-
-
--
-
-Now all incoming ASDUs (except for confirmation messages) will be
- forwarded to the ASduListener you registered. Every ASDU contains a
- number of Information Objects. The information objects contain
- information elements that make up the actual data. You will have to
- cast the InformationElements of the ASDU to a concrete
- implementation in order to access the data inside them. Every
- standardized Information Element is implemented by a class starting
- with the letters "Ie". The Type Identifier allows you to figure what
- to cast a particular Information Element to.
-
-
--
-
-You can use the Connection instance to send commands.
-
-
-
-
-
-
3.2. Server
-
The easiest way to get started programming an IEC 60870-5-104 server
-is by taking a look at the SampleServer located at:
-src/sample/java/SampleServer.java . The easiest way to run the sample
-server is by importing the project into Eclipse and run it from
-there. This is explained here:
-http://www.openmuc.org/index.php?id=72#faq_gradle
-
-
-
-
-
4. Terminology
-
-
--
-
-OA - Originator Address
-
-
--
-
-Monitor direction - direction from server to client
-
-
--
-
-Control direction - direction from client to server
-
-
--
-
-CON - confirmation message
-
-
--
-
-COT - Cause of transmission
-
-
--
-
-STARTDT ACT - Start data tranfer message. Needs to be sent by the
- client before information messages may be exchanged between client
- and server.
-
-
-
-
-
-
-
5. Develop j60870
-
-
We use the Gradle build automation tool. The distribution contains a
-fully functional gradle build file ("build.gradle"). Thus if you
-changed code and want to rebuild a library you can do it easily with
-Gradle. Also if you want to import our software into Eclipse you can
-easily create Eclipse project files using Gradle. Just follow the
-instructions on our FAQ site:
-http://www.openmuc.org/index.php?id=72#faq_gradle
-
-
-
-
6. Authors
-
-
-
--
-
-Stefan Feuerhahn
-
-
-
-
-
-
-
-
-
-
diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar
index 8c0fb64..ca78035 100644
Binary files a/gradle/wrapper/gradle-wrapper.jar and b/gradle/wrapper/gradle-wrapper.jar differ
diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
index 1372e29..1629bfd 100644
--- a/gradle/wrapper/gradle-wrapper.properties
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-2.11-all.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-3.1-all.zip
diff --git a/gradlew b/gradlew
index 9b53f01..27309d9 100755
--- a/gradlew
+++ b/gradlew
@@ -6,12 +6,30 @@
##
##############################################################################
-# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
-DEFAULT_JVM_OPTS=""
+# Attempt to set APP_HOME
+# Resolve links: $0 may be a link
+PRG="$0"
+# Need this for relative symlinks.
+while [ -h "$PRG" ] ; do
+ ls=`ls -ld "$PRG"`
+ link=`expr "$ls" : '.*-> \(.*\)$'`
+ if expr "$link" : '/.*' > /dev/null; then
+ PRG="$link"
+ else
+ PRG=`dirname "$PRG"`"/$link"
+ fi
+done
+SAVED="`pwd`"
+cd "`dirname \"$PRG\"`/" >/dev/null
+APP_HOME="`pwd -P`"
+cd "$SAVED" >/dev/null
APP_NAME="Gradle"
APP_BASE_NAME=`basename "$0"`
+# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+DEFAULT_JVM_OPTS=""
+
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD="maximum"
@@ -30,6 +48,7 @@ die ( ) {
cygwin=false
msys=false
darwin=false
+nonstop=false
case "`uname`" in
CYGWIN* )
cygwin=true
@@ -40,31 +59,11 @@ case "`uname`" in
MINGW* )
msys=true
;;
+ NONSTOP* )
+ nonstop=true
+ ;;
esac
-# For Cygwin, ensure paths are in UNIX format before anything is touched.
-if $cygwin ; then
- [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"`
-fi
-
-# Attempt to set APP_HOME
-# Resolve links: $0 may be a link
-PRG="$0"
-# Need this for relative symlinks.
-while [ -h "$PRG" ] ; do
- ls=`ls -ld "$PRG"`
- link=`expr "$ls" : '.*-> \(.*\)$'`
- if expr "$link" : '/.*' > /dev/null; then
- PRG="$link"
- else
- PRG=`dirname "$PRG"`"/$link"
- fi
-done
-SAVED="`pwd`"
-cd "`dirname \"$PRG\"`/" >&-
-APP_HOME="`pwd -P`"
-cd "$SAVED" >&-
-
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
# Determine the Java command to use to start the JVM.
@@ -90,7 +89,7 @@ location of your Java installation."
fi
# Increase the maximum file descriptors if we can.
-if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
+if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
MAX_FD_LIMIT=`ulimit -H -n`
if [ $? -eq 0 ] ; then
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
@@ -112,8 +111,9 @@ fi
# For Cygwin, switch paths to Windows format before running java
if $cygwin ; then
- APP_HOME=`cygpath --url --mixed "$APP_HOME"`
- CLASSPATH=`cygpath --url --mixed "$CLASSPATH"`
+ APP_HOME=`cygpath --path --mixed "$APP_HOME"`
+ CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
+ JAVACMD=`cygpath --unix "$JAVACMD"`
# We build the pattern for arguments to be converted via cygpath
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
@@ -134,7 +134,7 @@ if $cygwin ; then
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
- eval `echo args$i`=`cygpath --url --ignore --mixed "$arg"`
+ eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
else
eval `echo args$i`="\"$arg\""
fi
diff --git a/gradlew.bat b/gradlew.bat
index 8a0b282..832fdb6 100644
--- a/gradlew.bat
+++ b/gradlew.bat
@@ -8,14 +8,14 @@
@rem Set local scope for the variables with windows NT shell
if "%OS%"=="Windows_NT" setlocal
-@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
-set DEFAULT_JVM_OPTS=
-
set DIRNAME=%~dp0
if "%DIRNAME%" == "" set DIRNAME=.
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
+@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+set DEFAULT_JVM_OPTS=
+
@rem Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome
@@ -46,7 +46,7 @@ echo location of your Java installation.
goto fail
:init
-@rem Get command-line arguments, handling Windowz variants
+@rem Get command-line arguments, handling Windows variants
if not "%OS%" == "Windows_NT" goto win9xME_args
if "%@eval[2+2]" == "4" goto 4NT_args
diff --git a/run-scripts/README.txt b/run-scripts/README.txt
index 36833a3..49084d5 100644
--- a/run-scripts/README.txt
+++ b/run-scripts/README.txt
@@ -1,2 +1,3 @@
-In order to run the client app on Windows you have to rename j60870-clientapp.bat.winfile to j60870-clientapp.bat .
+In order to run the client app on Windows you have to rename
+j60870-console-client.bat.winfile to j60870-console-client.bat .
diff --git a/run-scripts/j60870-clientapp.bat.winfile b/run-scripts/j60870-console-client.bat.winfile
similarity index 52%
rename from run-scripts/j60870-clientapp.bat.winfile
rename to run-scripts/j60870-console-client.bat.winfile
index e5bddca..0e7c22a 100644
--- a/run-scripts/j60870-clientapp.bat.winfile
+++ b/run-scripts/j60870-console-client.bat.winfile
@@ -3,4 +3,4 @@
set BATDIR=%~dp0
set LIBDIR=%BATDIR%..\build\libs
-java -Djava.ext.dirs=%LIBDIR% org.openmuc.j60870.app.ClientApp %*
+java -Djava.ext.dirs=%LIBDIR% org.openmuc.j60870.app.ConsoleClient %*
diff --git a/run-scripts/j60870-clientapp.sh b/run-scripts/j60870-console-client.sh
similarity index 87%
rename from run-scripts/j60870-clientapp.sh
rename to run-scripts/j60870-console-client.sh
index 4631637..c5f2674 100644
--- a/run-scripts/j60870-clientapp.sh
+++ b/run-scripts/j60870-console-client.sh
@@ -1,7 +1,7 @@
#!/bin/bash
JARS_LOCATION="../build/libs"
-MAIN_CLASS="org.openmuc.j60870.app.ClientApp"
+MAIN_CLASS="org.openmuc.j60870.app.ConsoleClient"
SYSPROPS=""
PARAMS=""
diff --git a/src/docs/asciidoc/j60870-doc.adoc b/src/docs/asciidoc/j60870-doc.adoc
new file mode 100644
index 0000000..9864fc2
--- /dev/null
+++ b/src/docs/asciidoc/j60870-doc.adoc
@@ -0,0 +1,95 @@
+j60870 User Guide
+=================
+
+:numbered:
+
+== Intro
+
+j60870 is an implementation of the IEC 60870-5-104 protocol standard
+for client (i.e. master or controlling station) and server (i.e. slave
+or controlled station) applications.
+
+You can use j60870 to program your individual client or server
+applications. A simple console client is part of the library. You can
+execute it using the scripts found in the folder "run-scripts".
+
+== Distribution
+
+After extracting the distribution tar file the j60870 library can be
+found in the folder /build/libs.
+
+== Getting Started
+
+=== Client
+
+The easiest way to get started is by taking a look at the code of the
+console client which can be found here:
+src/main/java/org/openmuc/j60870/app/ConsoleClient.java . This
+application in combination with the javadoc should satisfy most of
+your documentation needs.
+
+Here is a short summary of the steps to get a client running:
+
+* Create and configure an instance of ClientConnectionBuilder.
+
+* Connect to the server using ClientConnectionBuilder.connect() which
+ returns the connection. The client is now connected to the server
+ via TCP/IP.
+
+* Initialize the data transfer by calling
+ Connection.startDataTransfer.
+
+* Now all incoming ASDUs (except for confirmation messages) will be
+ forwarded to the ASduListener you registered. Every ASDU contains a
+ number of Information Objects. The information objects contain
+ information elements that make up the actual data. You will have to
+ cast the InformationElements of the ASDU to a concrete
+ implementation in order to access the data inside them. Every
+ standardized Information Element is implemented by a class starting
+ with the letters "Ie". The Type Identifier allows you to figure what
+ to cast a particular Information Element to.
+
+* You can use the Connection instance to send commands.
+
+=== Server
+
+The easiest way to get started programming an IEC 60870-5-104 server
+is by taking a look at the SampleServer located at:
+src/sample/java/SampleServer.java . The easiest way to run the sample
+server is by importing the project into Eclipse and run it from
+there. This is explained here:
+https://www.openmuc.org/faq/
+
+== Terminology
+
+* *OA* - Originator Address
+
+* *Monitor direction* - direction from server to client
+
+* *Control direction* - direction from client to server
+
+* *CON* - confirmation message
+
+* *COT* - Cause of transmission
+
+* *STARTDT ACT* - Start data tranfer message. Needs to be sent by the
+ client before information messages may be exchanged between client
+ and server.
+
+== Develop j60870
+
+We use the Gradle build automation tool. The distribution contains a
+fully functional gradle build file ("build.gradle"). Thus if you
+changed code and want to rebuild a library you can do it easily with
+Gradle. Also if you want to import our software into Eclipse you can
+easily create Eclipse project files using Gradle. Just follow the
+instructions on our FAQ site:
+https://www.openmuc.org/faq/
+
+== Authors
+
+Developers:
+
+* Stefan Feuerhahn
+
+
diff --git a/src/docs/asciidoc/openmuc-asciidoc.css b/src/docs/asciidoc/openmuc-asciidoc.css
new file mode 100644
index 0000000..33603b0
--- /dev/null
+++ b/src/docs/asciidoc/openmuc-asciidoc.css
@@ -0,0 +1,399 @@
+/*
+ * AsciiDoc 'volnitsky' theme for xhtml11 and html5 backends.
+ * Based on css from http://volnitsky.com, which was in turn based on default
+ * theme from AsciiDoc
+ *
+ * FIXME: The styling is still a bit rough in places.
+ *
+ */
+
+/* Default font. */
+body {
+ font-family: Georgia,"Times New Roman",Times,serif;
+
+}
+
+/* Title font. */
+
+h3,h4, h5, h6 {
+ color: #010101;
+ size:12px;
+}
+
+#toc a {
+ border-bottom: 1px dotted #999999;
+ color: #04392C !important;
+ text-decoration: none !important;
+}
+#toc a:hover {
+ border-bottom: 1px solid #049674;
+ color: #049674 !important;
+ text-decoration: none !important;
+}
+
+em {
+ font-style: italic;
+/* color: #0D6C55;*/
+ color: #4E4E4E;
+}
+
+strong {
+ font-weight: bold;
+/* color: #0D6C55;*/
+ color: #000000;
+}
+
+
+div.sectionbody {
+ margin-left: 0;
+}
+
+hr {
+ border: 1px solid #0D6C55;
+}
+
+
+pre {
+ padding: 0;
+ margin: 0;
+}
+
+#author {
+ color: #0D6C55;
+ font-weight: bold;
+ font-size: 1.1em;
+}
+
+#footer {
+ font-size: small;
+ padding-top: 0.5em;
+ margin-top: 4.0em;
+}
+
+#footer-text {
+ float: left;
+ padding-bottom: 0.5em;
+}
+
+#footer-badges {
+ float: right;
+ padding-bottom: 0.5em;
+}
+
+#preamble {
+ margin-top: 1.5em;
+ margin-bottom: 1.5em;
+}
+
+div.tableblock, div.imageblock, div.exampleblock, div.verseblock,
+div.quoteblock, div.literalblock, div.listingblock, div.sidebarblock,
+div.admonitionblock {
+ margin-top: 1.5em;
+ margin-bottom: 1.5em;
+}
+
+div.admonitionblock {
+ margin-top: 2.5em;
+ margin-bottom: 2.5em;
+}
+
+div.content { /* Block element content. */
+ padding: 0;
+
+}
+
+/* Block element titles. */
+div.title, caption.title {
+ color: #0D6C55;
+ font-weight: bold;
+ text-align: left;
+ margin-top: 1.0em;
+ margin-bottom: 0.5em;
+}
+div.title + * {
+ margin-top: 0;
+}
+
+td div.title:first-child {
+ margin-top: 0.0em;
+}
+div.content div.title:first-child {
+ margin-top: 0.0em;
+}
+div.content + div.title {
+ margin-top: 0.0em;
+}
+
+div.sidebarblock > div.content {
+ background: #ffffee;
+ border: 1px solid silver;
+ padding: 0.5em;
+}
+
+div.listingblock > div.content {
+ border: 1px solid silver;
+ background: #f4f4f4;
+ padding: 0.5em;
+}
+
+div.quoteblock {
+ padding-left: 2.0em;
+ margin-right: 10%;
+}
+div.quoteblock > div.attribution {
+ padding-top: 0.5em;
+ text-align: right;
+}
+
+div.verseblock {
+ padding-left: 2.0em;
+ margin-right: 10%;
+}
+div.verseblock > pre.content {
+ font-family: inherit;
+}
+div.verseblock > div.attribution {
+ padding-top: 0.75em;
+ text-align: left;
+}
+/* DEPRECATED: Pre version 8.2.7 verse style literal block. */
+div.verseblock + div.attribution {
+ text-align: left;
+}
+
+div.admonitionblock .icon {
+ vertical-align: top;
+ font-size: 1.1em;
+ font-weight: bold;
+ text-decoration: underline;
+ color: #0D6C55;
+ padding-right: 0.5em;
+}
+div.admonitionblock td.content {
+ padding-left: 0.5em;
+ border-left: 2px solid silver;
+}
+
+div.exampleblock > div.content {
+ border-left: 2px solid silver;
+ padding: 0.5em;
+}
+
+div.imageblock div.content { padding-left: 0; }
+span.image img { border-style: none; }
+a.image:visited { color: white; }
+
+dl {
+ margin-top: 0.8em;
+ margin-bottom: 0.8em;
+}
+dt {
+ margin-top: 0.5em;
+ margin-bottom: 0;
+ font-style: normal;
+ color: #0D6C55;
+}
+dd > *:first-child {
+ margin-top: 0.1em;
+}
+
+ul, ol {
+ list-style-position: outside;
+}
+ol.arabic {
+ list-style-type: decimal;
+}
+ol.loweralpha {
+ list-style-type: lower-alpha;
+}
+ol.upperalpha {
+ list-style-type: upper-alpha;
+}
+ol.lowerroman {
+ list-style-type: lower-roman;
+}
+ol.upperroman {
+ list-style-type: upper-roman;
+}
+
+div.compact ul, div.compact ol,
+div.compact p, div.compact p,
+div.compact div, div.compact div {
+ margin-top: 0.1em;
+ margin-bottom: 0.1em;
+}
+
+div.tableblock > table {
+ border: 3px solid #0D6C55;
+}
+thead {
+ font-weight: bold;
+ color: #0D6C55;
+}
+tfoot {
+ font-weight: bold;
+}
+td > div.verse {
+ white-space: pre;
+}
+p.table {
+ margin-top: 0;
+}
+/* Because the table frame attribute is overriden by CSS in most browsers. */
+div.tableblock > table[frame="void"] {
+ border-style: none;
+}
+div.tableblock > table[frame="hsides"] {
+ border-left-style: none;
+ border-right-style: none;
+}
+div.tableblock > table[frame="vsides"] {
+ border-top-style: none;
+ border-bottom-style: none;
+}
+
+
+div.hdlist {
+ margin-top: 0.8em;
+ margin-bottom: 0.8em;
+}
+div.hdlist tr {
+ padding-bottom: 15px;
+}
+dt.hdlist1.strong, td.hdlist1.strong {
+ font-weight: bold;
+}
+td.hdlist1 {
+ vertical-align: top;
+ font-style: normal;
+ padding-right: 0.8em;
+ color: #0D6C55;
+}
+td.hdlist2 {
+ vertical-align: top;
+}
+div.hdlist.compact tr {
+ margin: 0;
+ padding-bottom: 0;
+}
+
+.comment {
+ background: yellow;
+}
+
+@media print {
+ #footer-badges { display: none; }
+}
+
+#toctitle {
+ color: #049674;
+ font-size: 1.2em;
+ font-weight: bold;
+ margin-top: 1.0em;
+ margin-bottom: 0.1em;
+}
+
+div.toclevel1, div.toclevel2, div.toclevel3, div.toclevel4 { margin-top: 0; margin-bottom: 0; }
+div.toclevel1 { margin-top: 0.3em; margin-left: 0; font-size: 1.0em; }
+div.toclevel2 { margin-top: 0.25em; margin-left: 2em; font-size: 0.9em; }
+div.toclevel3 { margin-left: 4em; font-size: 0.8em; }
+div.toclevel4 { margin-left: 6em; font-size: 0.8em; }
+
+
+
+.monospaced, tt, div.listingblock > div.content {
+ font-family: Consolas, "Andale Mono", "Courier New", monospace;
+ color: #004400;
+ background: #f4f4f4;
+ max-width: 80em;
+ line-height: 1.2em;
+}
+
+.paragraph p {
+ line-height: 1.5em;
+ margin-top: 1em;
+}
+
+/*.paragraph p, li, dd, .content { max-width: 85em; }
+.admonitionblock { max-width: 35em; }*/
+
+div.sectionbody div.ulist > ul > li {
+ list-style-type: square;
+ color: #aaa;
+}
+ div.sectionbody div.ulist > ul > li > * {
+ color: black;
+ /*font-size: 50%;*/
+ }
+
+
+div.sectionbody div.ulist > ul > li div.ulist > ul > li {
+ color: #ccd ;
+}
+ div.sectionbody div.ulist > ul > li div.ulist > ul > li > * {
+ color: black ;
+ }
+
+/*
+ * html5 specific
+ *
+ * */
+
+table.tableblock {
+ margin-top: 1.0em;
+ margin-bottom: 1.5em;
+}
+thead, p.tableblock.header {
+ font-weight: bold;
+ color: #049674;
+}
+p.tableblock {
+ margin-top: 0;
+}
+table.tableblock {
+ border-width: 3px;
+ border-spacing: 0px;
+ border-style: solid;
+ border-color: #0D6C55;
+ border-collapse: collapse;
+}
+th.tableblock, td.tableblock {
+ border-width: 1px;
+ padding: 4px;
+ border-style: solid;
+ border-color: #0D6C55;
+}
+
+table.tableblock.frame-topbot {
+ border-left-style: hidden;
+ border-right-style: hidden;
+}
+table.tableblock.frame-sides {
+ border-top-style: hidden;
+ border-bottom-style: hidden;
+}
+table.tableblock.frame-none {
+ border-style: hidden;
+}
+
+th.tableblock.halign-left, td.tableblock.halign-left {
+ text-align: left;
+}
+th.tableblock.halign-center, td.tableblock.halign-center {
+ text-align: center;
+}
+th.tableblock.halign-right, td.tableblock.halign-right {
+ text-align: right;
+}
+
+th.tableblock.valign-top, td.tableblock.valign-top {
+ vertical-align: top;
+}
+th.tableblock.valign-middle, td.tableblock.valign-middle {
+ vertical-align: middle;
+}
+th.tableblock.valign-bottom, td.tableblock.valign-bottom {
+ vertical-align: bottom;
+}
+
+
diff --git a/src/docs/asciidoc/pdf-theme.yml b/src/docs/asciidoc/pdf-theme.yml
new file mode 100644
index 0000000..f498497
--- /dev/null
+++ b/src/docs/asciidoc/pdf-theme.yml
@@ -0,0 +1,49 @@
+title_page:
+ align: right
+
+page:
+ layout: portrait
+# margin: [0.75in, 1in, 0.75in, 1in]
+ size: A4
+
+base:
+ font_family: Times-Roman
+ font_color: #333333
+ font_size: 11
+ line_height_length: 17
+ line_height: $base_line_height_length / $base_font_size
+
+vertical_rhythm: $base_line_height_length
+
+heading:
+ font_color: #0000
+ font_size: 14
+ font_style: bold
+ line_height: 1.2
+ margin_bottom: $vertical_rhythm
+
+link:
+ font_color: #939393
+
+outline_list:
+ indent: $base_font_size * 1.5
+
+header:
+ height: 0.75in
+ line_height: 1
+ recto_content:
+ center: '{document-title}'
+ verso_content:
+ center: '{document-title}'
+
+footer:
+ height: 0.75in
+ line_height: 1
+ recto_content:
+ right: '{chapter-title} | *{page-number}*'
+ verso_content:
+ left: '*{page-number}* | {chapter-title}'
+caption:
+ align: center
+ font_color: #000000
+ font_size: 9
\ No newline at end of file
diff --git a/src/main/java/org/openmuc/j60870/APdu.java b/src/main/java/org/openmuc/j60870/APdu.java
index 8337cc0..2e67326 100644
--- a/src/main/java/org/openmuc/j60870/APdu.java
+++ b/src/main/java/org/openmuc/j60870/APdu.java
@@ -23,6 +23,8 @@
import java.io.DataInputStream;
import java.io.IOException;
+import org.openmuc.j60870.internal.ConnectionSettings;
+
final class APdu {
public enum APCI_TYPE {
diff --git a/src/main/java/org/openmuc/j60870/ASdu.java b/src/main/java/org/openmuc/j60870/ASdu.java
index 172d966..7189649 100644
--- a/src/main/java/org/openmuc/j60870/ASdu.java
+++ b/src/main/java/org/openmuc/j60870/ASdu.java
@@ -23,6 +23,8 @@
import java.io.DataInputStream;
import java.io.IOException;
+import org.openmuc.j60870.internal.ConnectionSettings;
+
/**
* The application service data unit (ASDU). The ASDU is the payload of the application protocol data unit (APDU). Its
* structure is defined in IEC 60870-5-101. The ASDU consists of the Data Unit Identifier and a number of Information
diff --git a/src/main/java/org/openmuc/j60870/ClientConnectionBuilder.java b/src/main/java/org/openmuc/j60870/ClientConnectionBuilder.java
new file mode 100644
index 0000000..7b66b57
--- /dev/null
+++ b/src/main/java/org/openmuc/j60870/ClientConnectionBuilder.java
@@ -0,0 +1,135 @@
+/*
+ * Copyright 2014-16 Fraunhofer ISE
+ *
+ * This file is part of j60870.
+ * For more information visit http://www.openmuc.org
+ *
+ * j60870 is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * j60870 is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with j60870. If not, see .
+ *
+ */
+package org.openmuc.j60870;
+
+import java.io.IOException;
+import java.net.InetAddress;
+import java.net.Socket;
+
+import javax.net.SocketFactory;
+import javax.net.ssl.SSLSocketFactory;
+
+/**
+ * The client connection builder is used to connect to IEC 60870-5-104 servers. A client application that wants to
+ * connect to a server should first create an instance of {@link ClientConnectionBuilder}. Next all the necessary
+ * configuration parameters can be set. Finally the {@link ClientConnectionBuilder#connect() connect} function is called
+ * to connect to the server. An instance of {@link ClientConnectionBuilder} can be used to create an unlimited number of
+ * connections. Changing the parameters of a {@link ClientConnectionBuilder} has no affect on connections that have
+ * already been created.
+ *
+ * Note that the configured lengths of the fields COT, CA and IOA have to be the same for all communicating nodes in a
+ * network. The default values used by {@link ClientConnectionBuilder} are those most commonly used in IEC 60870-5-104
+ * communication.
+ *
+ * @author Stefan Feuerhahn
+ *
+ */
+public class ClientConnectionBuilder extends CommonBuilder {
+
+ private SocketFactory socketFactory = SocketFactory.getDefault();
+ private InetAddress address;
+ private int port = 2404;
+ InetAddress localAddr = null;
+ int localPort;
+
+ /**
+ * Creates a client connection builder that can be used to connect to the given address.
+ *
+ * @param address
+ * the address to connect to
+ */
+ public ClientConnectionBuilder(InetAddress address) {
+ this.address = address;
+ }
+
+ /**
+ * Set the socket factory to used to create the socket for the connection. The default is
+ * {@link SocketFactory#getDefault()}. You could pass an {@link SSLSocketFactory} to enable SSL.
+ *
+ * @param socketFactory
+ * the socket factory
+ * @return this builder
+ */
+ public ClientConnectionBuilder setSocketFactory(SocketFactory socketFactory) {
+ this.socketFactory = socketFactory;
+ return this;
+ }
+
+ /**
+ * Sets the port to connect to. The default port is 2404.
+ *
+ * @param port
+ * the port to connect to.
+ * @return this builder
+ */
+ public ClientConnectionBuilder setPort(int port) {
+ this.port = port;
+ return this;
+ }
+
+ /**
+ * Sets the address to connect to.
+ *
+ * @param address
+ * the address to connect to.
+ * @return this builder
+ */
+ public ClientConnectionBuilder setAddress(InetAddress address) {
+ this.address = address;
+ return this;
+ }
+
+ /**
+ * Sets the local (client) address and port the socket will connect to.
+ *
+ * @param address
+ * the local address the socket is bound to, or null for any local address.
+ * @param port
+ * the local port the socket is bound to or zero for a system selected free port.
+ * @return this builder
+ */
+ public ClientConnectionBuilder setLocalAddress(InetAddress address, int port) {
+ this.localAddr = address;
+ this.localPort = port;
+ return this;
+ }
+
+ /**
+ * Connects to the server. The TCP/IP connection is build up and a {@link Connection} object is returned that can be
+ * used to communicate with the server.
+ *
+ * @return the {@link Connection} object that can be used to communicate with the server.
+ * @throws IOException
+ * if any kind of error occurs during connection build up.
+ */
+ public Connection connect() throws IOException {
+ Socket socket;
+ if (localAddr == null) {
+ socket = socketFactory.createSocket(address, port);
+ }
+ else {
+ socket = socketFactory.createSocket(address, port, localAddr, localPort);
+ }
+
+ return new Connection(socket, null, settings.getCopy());
+ }
+
+}
diff --git a/src/main/java/org/openmuc/j60870/ClientSap.java b/src/main/java/org/openmuc/j60870/ClientSap.java
deleted file mode 100644
index 1a7a195..0000000
--- a/src/main/java/org/openmuc/j60870/ClientSap.java
+++ /dev/null
@@ -1,249 +0,0 @@
-/*
- * Copyright 2014-16 Fraunhofer ISE
- *
- * This file is part of j60870.
- * For more information visit http://www.openmuc.org
- *
- * j60870 is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * j60870 is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with j60870. If not, see .
- *
- */
-package org.openmuc.j60870;
-
-import java.io.IOException;
-import java.net.InetAddress;
-import java.net.Socket;
-
-import javax.net.SocketFactory;
-
-/**
- * The Client Service Access Point is used to connect to IEC 60870-5-104 servers. 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 {@link ClientSap#connect(InetAddress) connect} function is called to connect to the server. An
- * instance of ClientSap can be used to create an unlimited number of connections. Changing the parameters of a
- * ClientSap has no affect on connections that have already been created.
- *
- * Note that the configured lengths of the fields COT, CA and IOA have to be the same for all communicating nodes in a
- * network. The default values used by ClientSap are those most commonly used in IEC 60870-5-104 communication.
- *
- * @author Stefan Feuerhahn
- *
- */
-public class ClientSap {
-
- private final SocketFactory socketFactory;
-
- private final ConnectionSettings settings = new ConnectionSettings();
-
- /**
- * Use this constructor to create a default client SAP that uses SocketFactory.getDefault()
as its
- * SocketFactory.
- */
- public ClientSap() {
- socketFactory = SocketFactory.getDefault();
- }
-
- /**
- * 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
- */
- public ClientSap(SocketFactory socketFactory) {
- this.socketFactory = socketFactory;
- }
-
- /**
- * 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 command function (e.g.
- * {@link Connection#interrogation(int, CauseOfTransmission, IeQualifierOfInterrogation) interrogation}) will throw
- * an IOException if the socket throws this timeout. In addition any ASDU listener will be notified of the
- * IOException. Usually the connection cannot recover from this kind of error.
- *
- * @param timeout
- * the timeout in milliseconds. The default is 5000.
- */
- public void setMessageFragmentTimeout(int timeout) {
- if (timeout < 0) {
- throw new IllegalArgumentException("invalid message fragment timeout: " + timeout);
- }
- settings.messageFragmentTimeout = timeout;
- }
-
- /**
- * Sets the length of the Cause Of Transmission (COT) field of the ASDU. Allowed values are 1 or 2. The default is
- * 2.
- *
- * @param length
- * the length of the Cause Of Transmission field
- */
- public void setCotFieldLength(int length) {
- if (length != 1 && length != 2) {
- throw new IllegalArgumentException("invalid length");
- }
- settings.cotFieldLength = length;
- }
-
- /**
- * Sets the length of the Common Address (CA) field of the ASDU. Allowed values are 1 or 2. The default is 2.
- *
- * @param length
- * the length of the Common Address (CA) field
- */
- public void setCommonAddressFieldLength(int length) {
- if (length != 1 && length != 2) {
- throw new IllegalArgumentException("invalid length");
- }
- settings.cotFieldLength = length;
- }
-
- /**
- * Sets the length of the Information Object Address (IOA) field of the ASDU. Allowed values are 1, 2 or 3. The
- * default is 3.
- *
- * @param length
- * the length of the Information Object Address field
- */
- public void setIoaFieldLength(int length) {
- if (length < 1 || length > 3) {
- throw new IllegalArgumentException("invalid length: " + length);
- }
- settings.ioaFieldLength = length;
- }
-
- /**
- * Sets the maximum time in ms that no acknowledgement has been received (for I-Frames or Test-Frames) before
- * actively closing the connection. This timeout is called t1 by the standard. Default is 15s, minimum is 1s,
- * maximum is 255s.
- *
- * @param time
- * the maximum time in ms that no acknowledgement has been received before actively closing the
- * connection.
- */
- public void setMaxTimeNoAckReceived(int time) {
- if (time < 1000 || time > 255000) {
- throw new IllegalArgumentException(
- "invalid timeout: " + time + ", time must be between 1000ms and 255000ms");
- }
- settings.maxTimeNoAckReceived = time;
- }
-
- /**
- * Sets the maximum time in ms before confirming received messages that have not yet been acknowledged using an S
- * format APDU. This timeout is called t2 by the standard. Default is 10s, minimum is 1s, maximum is 255s.
- *
- * @param time
- * the maximum time in ms before confirming received messages that have not yet been acknowledged using
- * an S format APDU.
- */
- public void setMaxTimeNoAckSent(int time) {
- if (time < 1000 || time > 255000) {
- throw new IllegalArgumentException(
- "invalid timeout: " + time + ", time must be between 1000ms and 255000ms");
- }
- settings.maxTimeNoAckSent = time;
- }
-
- /**
- * Sets the maximum time in ms that the connection may be idle before sending a test frame. This timeout is called
- * t3 by the standard. Default is 20s, minimum is 1s, maximum is 172800s (48h).
- *
- * @param time
- * the maximum time in ms that the connection may be idle before sending a test frame.
- */
- public void setMaxIdleTime(int time) {
- if (time < 1000 || time > 172800000) {
- throw new IllegalArgumentException(
- "invalid timeout: " + time + ", time must be between 1000ms and 172800000ms");
- }
- settings.maxIdleTime = time;
- }
-
- /**
- * Sets the number of unacknowledged I format APDUs received before the connection will automatically send an S
- * format APDU to confirm them. This parameter is called w by the standard. Default is 8, minimum is 1, maximum is
- * 32767.
- *
- * @param maxNum
- * the number of unacknowledged I format APDUs received before the connection will automatically send an
- * S format APDU to confirm them.
- */
- public void setMaxUnconfirmedIPdusReceived(int maxNum) {
- if (maxNum < 1 || maxNum > 32767) {
- throw new IllegalArgumentException("invalid maxNum: " + maxNum + ", must be a value between 1 and 32767");
- }
- settings.maxUnconfirmedIPdusReceived = maxNum;
- }
-
- /**
- * Connects to the given address on port 2404. The TCP/IP connection is build up and a Connection object is returned
- * that can be used to communicate with the server.
- *
- * @param address
- * the address to connect to
- * @return the ClientConnection object that can be used to communicate with the server.
- * @throws IOException
- * if any kind of error occurs during connection build up.
- */
- public Connection connect(InetAddress address) throws IOException {
- return connect(address, 2404, null, 0);
- }
-
- /**
- * Connects to the given address and port. The TCP/IP connection is build up and a Connection object is returned
- * that can be used to communicate with the server.
- *
- * @param address
- * the address to connect to
- * @param port
- * the port to connect to. The IEC 60870-5-104 standard specifies the use of port 2404.
- * @return the ClientConnection object that can be used to communicate with the server.
- * @throws IOException
- * if any kind of error occurs during connection build up.
- */
- public Connection connect(InetAddress address, int port) throws IOException {
-
- return connect(address, port, null, 0);
- }
-
- /**
- * Connects to the given address and port. The TCP/IP connection is build up and a Connection object is returned
- * that can be used to communicate with the server.
- *
- * @param address
- * the address to connect to
- * @param port
- * the port to connect to. The IEC 60870-5-104 standard specifies the use of port 2404.
- * @param localAddr
- * the local address the socket is bound to, or null for anyLocal address.
- * @param localPort
- * the local port the socket is bound to or zero for a system selected free port.
- * @return the Connection object that can be used to communicate with the server.
- * @throws IOException
- * if any kind of error occurs during connection build up.
- */
- public Connection connect(InetAddress address, int port, InetAddress localAddr, int localPort) throws IOException {
- Socket socket;
- if (localAddr == null) {
- socket = socketFactory.createSocket(address, port);
- }
- else {
- socket = socketFactory.createSocket(address, port, localAddr, localPort);
- }
-
- Connection clientConnection = new Connection(socket, null, settings.getCopy());
-
- return clientConnection;
- }
-}
diff --git a/src/main/java/org/openmuc/j60870/CommonBuilder.java b/src/main/java/org/openmuc/j60870/CommonBuilder.java
new file mode 100644
index 0000000..8e000bd
--- /dev/null
+++ b/src/main/java/org/openmuc/j60870/CommonBuilder.java
@@ -0,0 +1,158 @@
+/*
+ * Copyright 2014-16 Fraunhofer ISE
+ *
+ * This file is part of j60870.
+ * For more information visit http://www.openmuc.org
+ *
+ * j60870 is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * j60870 is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with j60870. If not, see .
+ *
+ */
+package org.openmuc.j60870;
+
+import org.openmuc.j60870.internal.ConnectionSettings;
+
+class CommonBuilder> {
+
+ final ConnectionSettings settings = new ConnectionSettings();
+
+ /**
+ * Access the casted this reference.
+ *
+ * @return the reference of the object.
+ */
+ @SuppressWarnings("unchecked")
+ private T self() {
+ return (T) this;
+ }
+
+ /**
+ * Sets the length of the Cause Of Transmission (COT) field of the ASDU. Allowed values are 1 or 2. The default is
+ * 2.
+ *
+ * @param length
+ * the length of the Cause Of Transmission field
+ * @return this builder
+ */
+ public T setCotFieldLength(int length) {
+ if (length != 1 && length != 2) {
+ throw new IllegalArgumentException("invalid length");
+ }
+ settings.cotFieldLength = length;
+ return self();
+ }
+
+ /**
+ * Sets the length of the Common Address (CA) field of the ASDU. Allowed values are 1 or 2. The default is 2.
+ *
+ * @param length
+ * the length of the Common Address (CA) field
+ * @return this builder
+ */
+ public T setCommonAddressFieldLength(int length) {
+ if (length != 1 && length != 2) {
+ throw new IllegalArgumentException("invalid length");
+ }
+ settings.cotFieldLength = length;
+ return self();
+ }
+
+ /**
+ * Sets the length of the Information Object Address (IOA) field of the ASDU. Allowed values are 1, 2 or 3. The
+ * default is 3.
+ *
+ * @param length
+ * the length of the Information Object Address field
+ * @return this builder
+ */
+ public T setIoaFieldLength(int length) {
+ if (length < 1 || length > 3) {
+ throw new IllegalArgumentException("invalid length: " + length);
+ }
+ settings.ioaFieldLength = length;
+ return self();
+ }
+
+ /**
+ * Sets the maximum time in ms that no acknowledgement has been received (for I-Frames or Test-Frames) before
+ * actively closing the connection. This timeout is called t1 by the standard. Default is 15s, minimum is 1s,
+ * maximum is 255s.
+ *
+ * @param time
+ * the maximum time in ms that no acknowledgement has been received before actively closing the
+ * connection.
+ * @return this builder
+ */
+ public T setMaxTimeNoAckReceived(int time) {
+ if (time < 1000 || time > 255000) {
+ throw new IllegalArgumentException(
+ "invalid timeout: " + time + ", time must be between 1000ms and 255000ms");
+ }
+ settings.maxTimeNoAckReceived = time;
+ return self();
+ }
+
+ /**
+ * Sets the maximum time in ms before confirming received messages that have not yet been acknowledged using an S
+ * format APDU. This timeout is called t2 by the standard. Default is 10s, minimum is 1s, maximum is 255s.
+ *
+ * @param time
+ * the maximum time in ms before confirming received messages that have not yet been acknowledged using
+ * an S format APDU.
+ * @return this builder
+ */
+ public T setMaxTimeNoAckSent(int time) {
+ if (time < 1000 || time > 255000) {
+ throw new IllegalArgumentException(
+ "invalid timeout: " + time + ", time must be between 1000ms and 255000ms");
+ }
+ settings.maxTimeNoAckSent = time;
+ return self();
+ }
+
+ /**
+ * Sets the maximum time in ms that the connection may be idle before sending a test frame. This timeout is called
+ * t3 by the standard. Default is 20s, minimum is 1s, maximum is 172800s (48h).
+ *
+ * @param time
+ * the maximum time in ms that the connection may be idle before sending a test frame.
+ * @return this builder
+ */
+ public T setMaxIdleTime(int time) {
+ if (time < 1000 || time > 172800000) {
+ throw new IllegalArgumentException(
+ "invalid timeout: " + time + ", time must be between 1000ms and 172800000ms");
+ }
+ settings.maxIdleTime = time;
+ return self();
+ }
+
+ /**
+ * Sets the number of unacknowledged I format APDUs received before the connection will automatically send an S
+ * format APDU to confirm them. This parameter is called w by the standard. Default is 8, minimum is 1, maximum is
+ * 32767.
+ *
+ * @param maxNum
+ * the number of unacknowledged I format APDUs received before the connection will automatically send an
+ * S format APDU to confirm them.
+ * @return this builder
+ */
+ public T setMaxUnconfirmedIPdusReceived(int maxNum) {
+ if (maxNum < 1 || maxNum > 32767) {
+ throw new IllegalArgumentException("invalid maxNum: " + maxNum + ", must be a value between 1 and 32767");
+ }
+ settings.maxUnconfirmedIPdusReceived = maxNum;
+ return self();
+ }
+
+}
diff --git a/src/main/java/org/openmuc/j60870/Connection.java b/src/main/java/org/openmuc/j60870/Connection.java
index 939eb3c..82d1f1e 100644
--- a/src/main/java/org/openmuc/j60870/Connection.java
+++ b/src/main/java/org/openmuc/j60870/Connection.java
@@ -36,20 +36,21 @@
import java.util.concurrent.TimeoutException;
import org.openmuc.j60870.APdu.APCI_TYPE;
+import org.openmuc.j60870.internal.ConnectionSettings;
/**
- * Represents a connection between a client and a server. It is created either through an instance of {@link ClientSap}
- * or passed to {@link ServerSapListener}. Once it has been closed it cannot be opened again. A newly created connection
- * has successfully build up a TCP/IP connection to the server. Before receiving ASDUs or sending commands one has to
- * call {@link Connection#startDataTransfer(ConnectionEventListener, int)} or
+ * Represents a connection between a client and a server. It is created either through an instance of
+ * {@link ClientConnectionBuilder} or passed to {@link ServerEventListener}. Once it has been closed it cannot be opened
+ * again. A newly created connection has successfully build up a TCP/IP connection to the server. Before receiving ASDUs
+ * or sending commands one has to call {@link Connection#startDataTransfer(ConnectionEventListener, int)} or
* {@link Connection#waitForStartDT(ConnectionEventListener, int)}. Afterwards incoming ASDUs are forwarded to the
* {@link ConnectionEventListener}. Incoming ASDUs are queued so that {@link ConnectionEventListener#newASdu(ASdu)} is
- * never called simultaneously for the same connection. Note that the ASduListener is not notified of incoming
- * confirmation messages (CONs).
+ * never called simultaneously for the same connection.
*
* Connection offers a method for every possible command defined by IEC 60870 (e.g. singleCommand). Every command
* function may throw an IOException indicating a fatal connection error. In this case the connection will be
- * automatically closed and a new connection will have to be built up.
+ * automatically closed and a new connection will have to be built up. The command methods do not wait for an
+ * acknowledgment but return right after the command has been sent.
*
* @author Stefan Feuerhahn
*
@@ -697,7 +698,7 @@ public void regulatingStepCommand(int commonAddress, CauseOfTransmission cot, in
*/
public void regulatingStepCommandWithTimeTag(int commonAddress, CauseOfTransmission cot,
int informationObjectAddress, IeRegulatingStepCommand regulatingStepCommand, IeTime56 timeTag)
- throws IOException {
+ throws IOException {
ASdu aSdu = new ASdu(TypeId.C_RC_TA_1, false, cot, false, false, originatorAddress, commonAddress,
new InformationObject[] { new InformationObject(informationObjectAddress,
@@ -1157,7 +1158,7 @@ public void fileReady(int commonAddress, int informationObjectAddress, IeNameOfF
public void sectionReady(int commonAddress, int informationObjectAddress, IeNameOfFile nameOfFile,
IeNameOfSection nameOfSection, IeLengthOfFileOrSection lengthOfSection, IeSectionReadyQualifier qualifier)
- throws IOException {
+ throws IOException {
ASdu aSdu = new ASdu(TypeId.F_SR_NA_1, false, CauseOfTransmission.FILE_TRANSFER, false, false,
originatorAddress, commonAddress,
new InformationObject[] { new InformationObject(informationObjectAddress,
@@ -1167,7 +1168,7 @@ public void sectionReady(int commonAddress, int informationObjectAddress, IeName
public void callOrSelectFiles(int commonAddress, CauseOfTransmission cot, int informationObjectAddress,
IeNameOfFile nameOfFile, IeNameOfSection nameOfSection, IeSelectAndCallQualifier qualifier)
- throws IOException {
+ throws IOException {
ASdu aSdu = new ASdu(TypeId.F_SC_NA_1, false, cot, false, false, originatorAddress, commonAddress,
new InformationObject[] { new InformationObject(informationObjectAddress,
new InformationElement[][] { { nameOfFile, nameOfSection, qualifier } }) });
@@ -1176,7 +1177,7 @@ public void callOrSelectFiles(int commonAddress, CauseOfTransmission cot, int in
public void lastSectionOrSegment(int commonAddress, int informationObjectAddress, IeNameOfFile nameOfFile,
IeNameOfSection nameOfSection, IeLastSectionOrSegmentQualifier qualifier, IeChecksum checksum)
- throws IOException {
+ throws IOException {
ASdu aSdu = new ASdu(TypeId.F_LS_NA_1, false, CauseOfTransmission.FILE_TRANSFER, false, false,
originatorAddress, commonAddress,
new InformationObject[] { new InformationObject(informationObjectAddress,
diff --git a/src/main/java/org/openmuc/j60870/IeTime24.java b/src/main/java/org/openmuc/j60870/IeTime24.java
index b94bd84..fc35bae 100644
--- a/src/main/java/org/openmuc/j60870/IeTime24.java
+++ b/src/main/java/org/openmuc/j60870/IeTime24.java
@@ -64,7 +64,7 @@ int encode(byte[] buffer, int i) {
}
public int getTimeInMs() {
- return (value[0] & 0xff) + ((value[1] & 0xff) << 8) + value[2] * 6000;
+ return (value[0] & 0xff) + ((value[1] & 0xff) << 8) + value[2] * 60000;
}
@Override
diff --git a/src/main/java/org/openmuc/j60870/InformationObject.java b/src/main/java/org/openmuc/j60870/InformationObject.java
index 8ed6085..24296ec 100644
--- a/src/main/java/org/openmuc/j60870/InformationObject.java
+++ b/src/main/java/org/openmuc/j60870/InformationObject.java
@@ -23,6 +23,8 @@
import java.io.DataInputStream;
import java.io.IOException;
+import org.openmuc.j60870.internal.ConnectionSettings;
+
/**
* Every Information Object contains:
*
diff --git a/src/main/java/org/openmuc/j60870/Server.java b/src/main/java/org/openmuc/j60870/Server.java
new file mode 100644
index 0000000..6ea5a2e
--- /dev/null
+++ b/src/main/java/org/openmuc/j60870/Server.java
@@ -0,0 +1,159 @@
+/*
+ * Copyright 2014-16 Fraunhofer ISE
+ *
+ * This file is part of j60870.
+ * For more information visit http://www.openmuc.org
+ *
+ * j60870 is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * j60870 is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with j60870. If not, see .
+ *
+ */
+package org.openmuc.j60870;
+
+import java.io.IOException;
+import java.net.InetAddress;
+
+import javax.net.ServerSocketFactory;
+
+import org.openmuc.j60870.internal.ConnectionSettings;
+
+/**
+ * The server is used to start listening for IEC 60870-5-104 client connections.
+ *
+ * @author Stefan Feuerhahn
+ *
+ */
+public class Server {
+
+ private ServerThread serverThread;
+
+ private final int port;
+ private final InetAddress bindAddr;
+ private final int backlog;
+ private final ServerSocketFactory serverSocketFactory;
+ private final int maxConnections;
+
+ private final ConnectionSettings settings;
+
+ public static class Builder extends CommonBuilder {
+
+ private int port = 2404;
+ private InetAddress bindAddr = null;
+ private int backlog = 0;
+ private ServerSocketFactory serverSocketFactory = ServerSocketFactory.getDefault();
+
+ private int maxConnections = 100;
+
+ /**
+ * Sets the TCP port that the server will listen on. IEC 60870-5-104 usually uses port 2404.
+ *
+ * @param port
+ * the port
+ * @return this builder
+ */
+ public Builder setPort(int port) {
+ this.port = port;
+ return this;
+ }
+
+ /**
+ * Sets the backlog that is passed to the java.net.ServerSocket.
+ *
+ * @param backlog
+ * the backlog
+ * @return this builder
+ */
+ public Builder setBacklog(int backlog) {
+ this.backlog = backlog;
+ return this;
+ }
+
+ /**
+ * Sets the IP address to bind to. It is passed to java.net.ServerSocket
+ *
+ * @param bindAddr
+ * the IP address to bind to
+ * @return this builder
+ */
+ public Builder setBindAddr(InetAddress bindAddr) {
+ this.bindAddr = bindAddr;
+ return this;
+ }
+
+ /**
+ * Sets the ServerSocketFactory to be used to create the ServerSocket. Default is
+ * ServerSocketFactory.getDefault().
+ *
+ * @param socketFactory
+ * the ServerSocketFactory to be used to create the ServerSocket
+ * @return this builder
+ */
+ public Builder setSocketFactory(ServerSocketFactory socketFactory) {
+ this.serverSocketFactory = socketFactory;
+ return this;
+ }
+
+ /**
+ * Set the maximum number of client connections that are allowed in parallel.
+ *
+ * @param maxConnections
+ * the number of connections allowed (default is 100) @ return this builder
+ * @return this builder
+ */
+ public Builder setMaxConnections(int maxConnections) {
+ if (maxConnections <= 0) {
+ throw new IllegalArgumentException("maxConnections is out of bound");
+ }
+ this.maxConnections = maxConnections;
+ return this;
+ }
+
+ public Server build() {
+ return new Server(this);
+ }
+
+ }
+
+ private Server(Builder builder) {
+ port = builder.port;
+ bindAddr = builder.bindAddr;
+ backlog = builder.backlog;
+ serverSocketFactory = builder.serverSocketFactory;
+ maxConnections = builder.maxConnections;
+ settings = builder.settings.getCopy();
+ }
+
+ /**
+ * Starts a new thread that listens on the configured port. This method is non-blocking.
+ *
+ * @param listener
+ * the ServerConnectionListener that will be notified when remote clients are connecting or the server
+ * stopped listening.
+ * @throws IOException
+ * if any kind of error occures while creating the server socket.
+ */
+ public void start(ServerEventListener listener) throws IOException {
+ serverThread = new ServerThread(serverSocketFactory.createServerSocket(port, backlog, bindAddr), settings,
+ maxConnections, listener);
+ serverThread.start();
+ }
+
+ /**
+ * Stop listening for new connections. Existing connections are not touched.
+ */
+ public void stop() {
+ serverThread.stopServer();
+ serverThread = null;
+ }
+
+}
diff --git a/src/main/java/org/openmuc/j60870/ServerSapListener.java b/src/main/java/org/openmuc/j60870/ServerEventListener.java
similarity index 97%
rename from src/main/java/org/openmuc/j60870/ServerSapListener.java
rename to src/main/java/org/openmuc/j60870/ServerEventListener.java
index 6d00bd2..5c9a9cc 100644
--- a/src/main/java/org/openmuc/j60870/ServerSapListener.java
+++ b/src/main/java/org/openmuc/j60870/ServerEventListener.java
@@ -22,7 +22,7 @@
import java.io.IOException;
-public interface ServerSapListener {
+public interface ServerEventListener {
public void connectionIndication(Connection connection);
diff --git a/src/main/java/org/openmuc/j60870/ServerSap.java b/src/main/java/org/openmuc/j60870/ServerSap.java
deleted file mode 100644
index 9f654d7..0000000
--- a/src/main/java/org/openmuc/j60870/ServerSap.java
+++ /dev/null
@@ -1,243 +0,0 @@
-/*
- * Copyright 2014-16 Fraunhofer ISE
- *
- * This file is part of j60870.
- * For more information visit http://www.openmuc.org
- *
- * j60870 is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * j60870 is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with j60870. If not, see .
- *
- */
-package org.openmuc.j60870;
-
-import java.io.IOException;
-import java.net.InetAddress;
-
-import javax.net.ServerSocketFactory;
-
-/**
- * The Server Service Access Point is used to start listening for IEC 60870-5-104 client connections.
- *
- * @author Stefan Feuerhahn
- *
- */
-public class ServerSap {
-
- private ServerThread serverThread;
-
- private final int port;
- private final InetAddress bindAddr;
- private final int backlog;
- private final ServerSapListener connectionListener;
- private final ServerSocketFactory serverSocketFactory;
-
- private final ConnectionSettings settings = new ConnectionSettings();
- private int maxConnections = 100;
-
- /**
- * Use this constructor to create a ServerSAP that listens on port 2404 using the default ServerSocketFactory.
- *
- * @param connectionListener
- * the ServerConnectionListener that will be notified when remote clients are connecting or the server
- * stopped listening.
- */
- public ServerSap(ServerSapListener connectionListener) {
- this(2404, 0, null, ServerSocketFactory.getDefault(), connectionListener);
- }
-
- /**
- * Use this constructor to create a ServerSAP that listens on the given port using the default ServerSocketFactory.
- *
- * @param port
- * the TCP port that the server will listen on. IEC 60870-5-104 usually uses port 2404.
- * @param connectionListener
- * the ServerConnectionListener that will be notified when remote clients are connecting or the server
- * stopped listening.
- */
- public ServerSap(int port, ServerSapListener connectionListener) {
- this(port, 0, null, ServerSocketFactory.getDefault(), connectionListener);
- }
-
- /**
- * Use this constructor to create a ServerSAP that can listen on a port with a specified ServerSocketFactory.
- *
- * @param port
- * the TCP port that the server will listen on. IEC 60870-5-104 usually uses port 2404.
- * @param backlog
- * is passed to the java.net.ServerSocket
- * @param bindAddr
- * the IP address to bind to. It is passed to java.net.ServerSocket
- * @param serverSocketFactory
- * The ServerSocketFactory to be used to create the ServerSocket
- * @param connectionListener
- * the ServerConnectionListener that will be notified when remote clients are connecting or the server
- * stopped listening.
- */
- public ServerSap(int port, int backlog, InetAddress bindAddr, ServerSocketFactory serverSocketFactory,
- ServerSapListener connectionListener) {
-
- this.port = port;
- this.backlog = backlog;
- this.bindAddr = bindAddr;
- this.connectionListener = connectionListener;
- this.serverSocketFactory = serverSocketFactory;
- }
-
- /**
- * 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 command function (e.g.
- * {@link Connection#interrogation(int, CauseOfTransmission, IeQualifierOfInterrogation) interrogation}) will throw
- * an IOException if the socket throws this timeout. In addition any ASDU listener will be notified of the
- * IOException. Usually the connection cannot recover from this kind of error.
- *
- * @param timeout
- * the timeout in milliseconds. The default is 5000.
- */
- public void setMessageFragmentTimeout(int timeout) {
- if (timeout < 0) {
- throw new IllegalArgumentException("invalid message fragment timeout: " + timeout);
- }
- settings.messageFragmentTimeout = timeout;
- }
-
- /**
- * Sets the length of the Cause Of Transmission (COT) field of the ASDU. Allowed values are 1 or 2. The default is
- * 2.
- *
- * @param length
- * the length of the Cause Of Transmission field
- */
- public void setCotFieldLength(int length) {
- if (length != 1 && length != 2) {
- throw new IllegalArgumentException("invalid length");
- }
- settings.cotFieldLength = length;
- }
-
- /**
- * Sets the length of the Common Address (CA) field of the ASDU. Allowed values are 1 or 2. The default is 2.
- *
- * @param length
- * the length of the Common Address (CA) field
- */
- public void setCommonAddressFieldLength(int length) {
- if (length != 1 && length != 2) {
- throw new IllegalArgumentException("invalid length");
- }
- settings.cotFieldLength = length;
- }
-
- /**
- * Sets the length of the Information Object Address (IOA) field of the ASDU. Allowed values are 1, 2 or 3. The
- * default is 3.
- *
- * @param length
- * the length of the Information Object Address field
- */
- public void setIoaFieldLength(int length) {
- if (length < 1 || length > 3) {
- throw new IllegalArgumentException("invalid length: " + length);
- }
- settings.ioaFieldLength = length;
- }
-
- /**
- * Sets the maximum time in ms that no acknowledgement has been received (for I-Frames or Test-Frames) before
- * actively closing the connection. This timeout is called t1 by the standard. Default is 15s, minimum is 1s,
- * maximum is 255s.
- *
- * @param time
- * the maximum time in ms that no acknowledgement has been received before actively closing the
- * connection.
- */
- public void setMaxTimeNoAckReceived(int time) {
- if (time < 1000 || time > 255000) {
- throw new IllegalArgumentException(
- "invalid timeout: " + time + ", time must be between 1000ms and 255000ms");
- }
- settings.maxTimeNoAckReceived = time;
- }
-
- /**
- * Sets the maximum time in ms before confirming received messages that have not yet been acknowledged using an S
- * format APDU. This timeout is called t2 by the standard. Default is 10s, minimum is 1s, maximum is 255s.
- *
- * @param time
- * the maximum time in ms before confirming received messages that have not yet been acknowledged using
- * an S format APDU.
- */
- public void setMaxTimeNoAckSent(int time) {
- if (time < 1000 || time > 255000) {
- throw new IllegalArgumentException(
- "invalid timeout: " + time + ", time must be between 1000ms and 255000ms");
- }
- settings.maxTimeNoAckSent = time;
- }
-
- /**
- * Sets the maximum time in ms that the connection may be idle before sending a test frame. This timeout is called
- * t3 by the standard. Default is 20s, minimum is 1s, maximum is 172800s (48h).
- *
- * @param time
- * the maximum time in ms that the connection may be idle before sending a test frame.
- */
- public void setMaxIdleTime(int time) {
- if (time < 1000 || time > 172800000) {
- throw new IllegalArgumentException(
- "invalid timeout: " + time + ", time must be between 1000ms and 172800000ms");
- }
- settings.maxIdleTime = time;
- }
-
- /**
- * Sets the number of unacknowledged I format APDUs received before the connection will automatically send an S
- * format APDU to confirm them. This parameter is called w by the standard. Default is 8, minimum is 1, maximum is
- * 32767.
- *
- * @param maxNum
- * the number of unacknowledged I format APDUs received before the connection will automatically send an
- * S format APDU to confirm them.
- */
- public void setMaxUnconfirmedIPdusReceived(int maxNum) {
- if (maxNum < 1 || maxNum > 32767) {
- throw new IllegalArgumentException("invalid maxNum: " + maxNum + ", must be a value between 1 and 32767");
- }
- settings.maxUnconfirmedIPdusReceived = maxNum;
- }
-
- /**
- * Set the maximum number of client connections that are allowed in parallel.
- *
- * @param maxConnections
- * the number of connections allowed (default is 100)
- */
- public void setMaxConnections(int maxConnections) {
- if (maxConnections <= 0) {
- throw new IllegalArgumentException("maxConnections is out of bound");
- }
- this.maxConnections = maxConnections;
- }
-
- /**
- * Starts a new thread that listens on the configured port. This method is non-blocking.
- *
- * @throws IOException
- * if any kind of error occures while creating the server socket.
- */
- public void startListening() throws IOException {
- serverThread = new ServerThread(serverSocketFactory.createServerSocket(port, backlog, bindAddr),
- settings.getCopy(), maxConnections, connectionListener);
- serverThread.start();
- }
-
-}
diff --git a/src/main/java/org/openmuc/j60870/ServerThread.java b/src/main/java/org/openmuc/j60870/ServerThread.java
index 7fb8e2a..e2aef8b 100644
--- a/src/main/java/org/openmuc/j60870/ServerThread.java
+++ b/src/main/java/org/openmuc/j60870/ServerThread.java
@@ -26,18 +26,20 @@
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
+import org.openmuc.j60870.internal.ConnectionSettings;
+
final class ServerThread extends Thread {
private final ServerSocket serverSocket;
private final ConnectionSettings settings;
private final int maxConnections;
- private final ServerSapListener serverSapListener;
+ private final ServerEventListener serverSapListener;
private boolean stopServer = false;
private int numConnections = 0;
ServerThread(ServerSocket serverSocket, ConnectionSettings settings, int maxConnections,
- ServerSapListener serverSapListener) {
+ ServerEventListener serverSapListener) {
this.serverSocket = serverSocket;
this.settings = settings;
this.maxConnections = maxConnections;
diff --git a/src/main/java/org/openmuc/j60870/app/ClientApp.java b/src/main/java/org/openmuc/j60870/app/ClientApp.java
deleted file mode 100644
index e2b3cda..0000000
--- a/src/main/java/org/openmuc/j60870/app/ClientApp.java
+++ /dev/null
@@ -1,197 +0,0 @@
-/*
- * Copyright 2014-16 Fraunhofer ISE
- *
- * This file is part of j60870.
- * For more information visit http://www.openmuc.org
- *
- * j60870 is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * j60870 is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with j60870. If not, see .
- *
- */
-package org.openmuc.j60870.app;
-
-import java.io.BufferedReader;
-import java.io.IOException;
-import java.io.InputStreamReader;
-import java.net.InetAddress;
-import java.net.UnknownHostException;
-import java.util.concurrent.TimeoutException;
-
-import org.openmuc.j60870.ASdu;
-import org.openmuc.j60870.CauseOfTransmission;
-import org.openmuc.j60870.ClientSap;
-import org.openmuc.j60870.Connection;
-import org.openmuc.j60870.ConnectionEventListener;
-import org.openmuc.j60870.IeQualifierOfInterrogation;
-import org.openmuc.j60870.IeTime56;
-
-public final class ClientApp implements ConnectionEventListener {
-
- private static Connection clientConnection;
-
- private final BufferedReader is;
-
- public ClientApp(BufferedReader is) {
- this.is = is;
- }
-
- private static void printUsage() {
- System.out.println("SYNOPSIS\n\torg.openmuc.j60870.app.ClientApp [-p ] [-ca ]");
- System.out.println("DESCRIPTION\n\tA client/master application to access IEC 60870-5-104 servers/slaves.");
- System.out.println("OPTIONS");
- System.out.println("\t\n\t The address of the slave you want to access.\n");
- System.out.println("\t-p \n\t The port to connect to. The default port is 2404.\n");
- System.out.println(
- "\t-ca \n\t The address of the target station or the broad cast address. The default is 1.\n");
- }
-
- public static void main(String[] args) {
- if (args.length < 1 || args.length > 5) {
- printUsage();
- return;
- }
-
- String remoteHost = args[0];
-
- int port = 2404;
- int commonAddress = 1;
-
- for (int i = 0; i < args.length; i++) {
- if (args[i].equals("-p")) {
- i++;
- if (i == args.length) {
- printUsage();
- System.exit(1);
- }
- try {
- port = Integer.parseInt(args[i]);
- } catch (NumberFormatException e) {
- printUsage();
- System.exit(1);
- }
- }
- else if (args[i].equals("-ca")) {
- i++;
- if (i == args.length) {
- printUsage();
- System.exit(1);
- }
- try {
- commonAddress = Integer.parseInt(args[i]);
- } catch (NumberFormatException e) {
- printUsage();
- System.exit(1);
- }
- }
- else {
- remoteHost = args[i];
- }
- }
-
- Runtime.getRuntime().addShutdownHook(new Thread() {
- @Override
- public void run() {
- if (clientConnection != null) {
- clientConnection.close();
- }
- }
- });
-
- ClientSap clientSap = new ClientSap();
-
- InetAddress address;
- try {
- address = InetAddress.getByName(remoteHost);
- } catch (UnknownHostException e) {
- System.out.println("Unknown host: " + remoteHost);
- return;
- }
-
- try {
- clientConnection = clientSap.connect(address, port);
- } catch (IOException e) {
- System.out.println("Unable to connect to remote host: " + remoteHost + ".");
- return;
- }
-
- BufferedReader is = new BufferedReader(new InputStreamReader(System.in));
-
- try {
- try {
- clientConnection.startDataTransfer(new ClientApp(is), 5000);
- } catch (TimeoutException e2) {
- throw new IOException("starting data transfer timed out.");
- }
- System.out.println("successfully connected");
-
- String line;
- while (true) {
- line = is.readLine();
-
- if (line == null) {
- throw new IOException("InputStream unexpectedly reached end of stream.");
- }
- else if (line.equals("?")) {
- printHelp();
- }
- else if (line.equals("q")) {
- return;
- }
- else if (line.equals("1")) {
- clientConnection.interrogation(commonAddress, CauseOfTransmission.ACTIVATION,
- new IeQualifierOfInterrogation(0));
- }
- else if (line.equals("2")) {
- clientConnection.synchronizeClocks(commonAddress, new IeTime56(System.currentTimeMillis()));
- }
- else {
- System.out.println("Unknown command, enter \'?\' for help");
- }
- }
-
- } catch (IOException e) {
- System.out.println("Connection closed for the following reason: " + e.getMessage());
- return;
- } finally {
- clientConnection.close();
- }
-
- }
-
- private static void printHelp() {
- System.out.println("(1) Interrogation C_IC_NA_1\n(2) Synchronize clocks C_CS_NA_1");
- }
-
- @Override
- public void newASdu(ASdu aSdu) {
- System.out.println("\nReceived ASDU:\n" + aSdu);
-
- }
-
- @Override
- public void connectionClosed(IOException e) {
- System.out.print("Received connection closed signal. Reason: ");
- if (!e.getMessage().isEmpty()) {
- System.out.println(e.getMessage());
- }
- else {
- System.out.println("unknown");
- }
-
- try {
- is.close();
- } catch (IOException e1) {
- }
- }
-
-}
diff --git a/src/main/java/org/openmuc/j60870/app/ConsoleClient.java b/src/main/java/org/openmuc/j60870/app/ConsoleClient.java
new file mode 100644
index 0000000..1044dfc
--- /dev/null
+++ b/src/main/java/org/openmuc/j60870/app/ConsoleClient.java
@@ -0,0 +1,187 @@
+/*
+ * Copyright 2014-16 Fraunhofer ISE
+ *
+ * This file is part of j60870.
+ * For more information visit http://www.openmuc.org
+ *
+ * j60870 is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * j60870 is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with j60870. If not, see .
+ *
+ */
+package org.openmuc.j60870.app;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.TimeoutException;
+
+import org.openmuc.j60870.ASdu;
+import org.openmuc.j60870.CauseOfTransmission;
+import org.openmuc.j60870.ClientConnectionBuilder;
+import org.openmuc.j60870.Connection;
+import org.openmuc.j60870.ConnectionEventListener;
+import org.openmuc.j60870.IeQualifierOfInterrogation;
+import org.openmuc.j60870.IeTime56;
+import org.openmuc.j60870.internal.cli.CliParameter;
+import org.openmuc.j60870.internal.cli.CliParameterBuilder;
+import org.openmuc.j60870.internal.cli.CliParseException;
+import org.openmuc.j60870.internal.cli.CliParser;
+import org.openmuc.j60870.internal.cli.IntCliParameter;
+import org.openmuc.j60870.internal.cli.StringCliParameter;
+
+public final class ConsoleClient {
+
+ private final StringCliParameter host = new CliParameterBuilder("-h")
+ .setDescription("The IP/domain address of the server you want to access.")
+ .setMandatory()
+ .buildStringParameter("host");
+ private final IntCliParameter port = new CliParameterBuilder("-p").setDescription("The port to connect to.")
+ .buildIntegerParameter("port", 2404);
+ private final IntCliParameter commonAddress = new CliParameterBuilder("-ca")
+ .setDescription("The address of the target station or the broad cast address.")
+ .buildIntegerParameter("common_address", 1);
+
+ private class ClientEventListener implements ConnectionEventListener {
+
+ @Override
+ public void newASdu(ASdu aSdu) {
+ System.out.println("\nReceived ASDU:\n" + aSdu);
+
+ }
+
+ @Override
+ public void connectionClosed(IOException e) {
+ System.out.print("Received connection closed signal. Reason: ");
+ if (!e.getMessage().isEmpty()) {
+ System.out.println(e.getMessage());
+ }
+ else {
+ System.out.println("unknown");
+ }
+
+ try {
+ is.close();
+ } catch (IOException e1) {
+ }
+ }
+
+ }
+
+ private volatile Connection clientConnection;
+
+ private BufferedReader is;
+
+ public static void main(String[] args) {
+ new ConsoleClient(args).start();
+ }
+
+ public ConsoleClient(String[] args) {
+ List cliParameters = new ArrayList();
+ cliParameters.add(host);
+ cliParameters.add(port);
+ cliParameters.add(commonAddress);
+
+ CliParser cliParser = new CliParser("j60870-console-client",
+ "A client/master application to access IEC 60870-5-104 servers/slaves.");
+ cliParser.addParameters(cliParameters);
+
+ try {
+ cliParser.parseArguments(args);
+ } catch (CliParseException e1) {
+ System.err.println("Error parsing command line parameters: " + e1.getMessage());
+ System.out.println(cliParser.getUsageString());
+ System.exit(1);
+ }
+
+ }
+
+ public void start() {
+
+ InetAddress address;
+ try {
+ address = InetAddress.getByName(host.getValue());
+ } catch (UnknownHostException e) {
+ System.out.println("Unknown host: " + host.getValue());
+ return;
+ }
+
+ ClientConnectionBuilder clientConnectionBuilder = new ClientConnectionBuilder(address).setPort(port.getValue());
+
+ try {
+ clientConnection = clientConnectionBuilder.connect();
+ } catch (IOException e) {
+ System.out.println("Unable to connect to remote host: " + host.getValue() + ".");
+ return;
+ }
+
+ Runtime.getRuntime().addShutdownHook(new Thread() {
+ @Override
+ public void run() {
+ clientConnection.close();
+ }
+ });
+
+ is = new BufferedReader(new InputStreamReader(System.in));
+
+ try {
+ try {
+ clientConnection.startDataTransfer(new ClientEventListener(), 5000);
+ } catch (TimeoutException e2) {
+ throw new IOException("starting data transfer timed out.");
+ }
+ System.out.println("successfully connected");
+
+ String line;
+ while (true) {
+ line = is.readLine();
+
+ if (line == null) {
+ throw new IOException("InputStream unexpectedly reached end of stream.");
+ }
+ else if (line.equals("?")) {
+ printHelp();
+ }
+ else if (line.equals("q")) {
+ return;
+ }
+ else if (line.equals("1")) {
+ clientConnection.interrogation(commonAddress.getValue(), CauseOfTransmission.ACTIVATION,
+ new IeQualifierOfInterrogation(20));
+ }
+ else if (line.equals("2")) {
+ clientConnection.synchronizeClocks(commonAddress.getValue(),
+ new IeTime56(System.currentTimeMillis()));
+ }
+ else {
+ System.out.println("Unknown command, enter \'?\' for help");
+ }
+ }
+
+ } catch (IOException e) {
+ System.out.println("Connection closed for the following reason: " + e.getMessage());
+ return;
+ } finally {
+ clientConnection.close();
+ }
+
+ }
+
+ private static void printHelp() {
+ System.out.println("(1) Interrogation C_IC_NA_1\n(2) Synchronize clocks C_CS_NA_1\n(q) Quit");
+ }
+
+}
diff --git a/src/main/java/org/openmuc/j60870/ConnectionSettings.java b/src/main/java/org/openmuc/j60870/internal/ConnectionSettings.java
similarity index 95%
rename from src/main/java/org/openmuc/j60870/ConnectionSettings.java
rename to src/main/java/org/openmuc/j60870/internal/ConnectionSettings.java
index 0920f2e..a71ed83 100644
--- a/src/main/java/org/openmuc/j60870/ConnectionSettings.java
+++ b/src/main/java/org/openmuc/j60870/internal/ConnectionSettings.java
@@ -18,9 +18,9 @@
* along with j60870. If not, see .
*
*/
-package org.openmuc.j60870;
+package org.openmuc.j60870.internal;
-final class ConnectionSettings {
+public final class ConnectionSettings {
public int messageFragmentTimeout = 5000;
diff --git a/src/main/java/org/openmuc/j60870/internal/cli/CliParameter.java b/src/main/java/org/openmuc/j60870/internal/cli/CliParameter.java
new file mode 100644
index 0000000..9fa91e5
--- /dev/null
+++ b/src/main/java/org/openmuc/j60870/internal/cli/CliParameter.java
@@ -0,0 +1,47 @@
+package org.openmuc.j60870.internal.cli;
+
+public abstract class CliParameter {
+
+ String name;
+ String description;
+ boolean optional;
+ boolean selected;
+
+ CliParameter(CliParameterBuilder builder) {
+ name = builder.name;
+ description = builder.description;
+ optional = builder.optional;
+ }
+
+ /**
+ * @return the name
+ */
+ public String getName() {
+ return name;
+ }
+
+ /**
+ * @return the description
+ */
+ public String getDescription() {
+ return description;
+ }
+
+ /**
+ * @return the optional
+ */
+ public boolean isOptional() {
+ return optional;
+ }
+
+ public boolean isSelected() {
+ return selected;
+ }
+
+ abstract int parse(String[] args, int i) throws CliParseException;
+
+ abstract int appendSynopsis(StringBuilder sb);
+
+ abstract void appendDescription(StringBuilder sb);
+
+}
diff --git a/src/main/java/org/openmuc/j60870/internal/cli/CliParameterBuilder.java b/src/main/java/org/openmuc/j60870/internal/cli/CliParameterBuilder.java
new file mode 100644
index 0000000..b333af1
--- /dev/null
+++ b/src/main/java/org/openmuc/j60870/internal/cli/CliParameterBuilder.java
@@ -0,0 +1,51 @@
+package org.openmuc.j60870.internal.cli;
+
+public class CliParameterBuilder {
+
+ final String name;
+ String description;
+ boolean optional = true;
+
+ public CliParameterBuilder(String name) {
+ this.name = name;
+ }
+
+ public CliParameterBuilder setDescription(String description) {
+ this.description = description;
+ return this;
+ }
+
+ public CliParameterBuilder setMandatory() {
+ optional = false;
+ return this;
+ }
+
+ public LongCliParameter buildLongParameter(String parameterName, long defaultValue) {
+ return new LongCliParameter(this, parameterName, defaultValue);
+ }
+
+ public LongCliParameter buildLongParameter(String parameterName) {
+ return new LongCliParameter(this, parameterName);
+ }
+
+ public IntCliParameter buildIntegerParameter(String parameterName, int defaultValue) {
+ return new IntCliParameter(this, parameterName, defaultValue);
+ }
+
+ public IntCliParameter buildIntParameter(String parameterName) {
+ return new IntCliParameter(this, parameterName);
+ }
+
+ public StringCliParameter buildStringParameter(String parameterName, String defaultValue) {
+ return new StringCliParameter(this, parameterName, defaultValue);
+ }
+
+ public StringCliParameter buildStringParameter(String parameterName) {
+ return new StringCliParameter(this, parameterName);
+ }
+
+ public FlagCliParameter buildFlagParameter() {
+ return new FlagCliParameter(this);
+ }
+
+}
diff --git a/src/main/java/org/openmuc/j60870/internal/cli/CliParseException.java b/src/main/java/org/openmuc/j60870/internal/cli/CliParseException.java
new file mode 100644
index 0000000..715416c
--- /dev/null
+++ b/src/main/java/org/openmuc/j60870/internal/cli/CliParseException.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright 2011-16 Fraunhofer ISE
+ *
+ * This file is part of OpenMUC.
+ * For more information visit http://www.openmuc.org
+ *
+ * OpenMUC is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * OpenMUC is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with OpenMUC. If not, see .
+ *
+ */
+
+package org.openmuc.j60870.internal.cli;
+
+public final class CliParseException extends Exception {
+
+ private static final long serialVersionUID = -5162894897245715377L;
+
+ public CliParseException() {
+ super();
+ }
+
+ public CliParseException(String s) {
+ super(s);
+ }
+
+ public CliParseException(Throwable cause) {
+ super(cause);
+ }
+
+ public CliParseException(String s, Throwable cause) {
+ super(s, cause);
+ }
+
+}
diff --git a/src/main/java/org/openmuc/j60870/internal/cli/CliParser.java b/src/main/java/org/openmuc/j60870/internal/cli/CliParser.java
new file mode 100644
index 0000000..787029d
--- /dev/null
+++ b/src/main/java/org/openmuc/j60870/internal/cli/CliParser.java
@@ -0,0 +1,127 @@
+package org.openmuc.j60870.internal.cli;
+
+import java.util.ArrayList;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Set;
+
+public class CliParser {
+
+ private final String name;
+ private final String description;
+ private String selectedGroup = "";
+
+ private static class ParameterGroup {
+ private final String name;
+ private final List parameters;
+
+ public ParameterGroup(String name, List parameters) {
+ this.name = name;
+ this.parameters = parameters;
+ }
+ }
+
+ private final List commandLineParameterGroups = new ArrayList();
+
+ public CliParser(String name, String description) {
+ this.name = name;
+ this.description = description;
+ }
+
+ public void addParameterGroup(String groupName, List parameters) {
+ commandLineParameterGroups.add(new ParameterGroup(groupName.toLowerCase(), parameters));
+ }
+
+ public void addParameters(List parameters) {
+ commandLineParameterGroups.clear();
+ commandLineParameterGroups.add(new ParameterGroup("", parameters));
+ }
+
+ public String getSelectedGroup() {
+ return selectedGroup;
+ }
+
+ public void parseArguments(String[] args) throws CliParseException {
+
+ List parameters = null;
+
+ int i = 0;
+ if (commandLineParameterGroups.get(0).name.isEmpty()) {
+ parameters = commandLineParameterGroups.get(0).parameters;
+ }
+ else {
+ if (args.length == 0) {
+ throw new CliParseException("No parameters found.");
+ }
+ for (ParameterGroup parameterGroup : commandLineParameterGroups) {
+ if (parameterGroup.name.equals(args[0].toLowerCase())) {
+ selectedGroup = parameterGroup.name;
+ parameters = parameterGroup.parameters;
+ }
+ }
+ if (parameters == null) {
+ throw new CliParseException("Group name " + args[0] + " is undefined.");
+ }
+ i++;
+ }
+
+ while (i < args.length) {
+ boolean found = false;
+ for (CliParameter option : parameters) {
+ if (args[i].equals(option.getName())) {
+ i += option.parse(args, i);
+ found = true;
+ break;
+ }
+ }
+ if (!found) {
+ throw new CliParseException("Unknown parameter found: " + args[i]);
+ }
+ }
+
+ for (CliParameter option : parameters) {
+ if (!option.isOptional() && !option.isSelected()) {
+ throw new CliParseException("Parameter " + option.getName() + " is mandatory but was not selected.");
+ }
+ }
+ }
+
+ public String getUsageString() {
+
+ StringBuilder sb = new StringBuilder();
+ sb.append("NAME\n\t").append(name).append(" - ").append(description).append("\n\nSYNOPSIS\n");
+
+ for (ParameterGroup parameterGroup : commandLineParameterGroups) {
+ sb.append("\t").append(name).append(" ").append(parameterGroup.name);
+
+ int characterColumn = name.length() + parameterGroup.name.length() + 1;
+
+ for (CliParameter parameter : parameterGroup.parameters) {
+ if ((characterColumn + parameter.appendSynopsis(new StringBuilder())) > 90) {
+ characterColumn = 0;
+ sb.append("\n\t ");
+ }
+ sb.append(' ');
+ characterColumn += parameter.appendSynopsis(sb) + 1;
+ }
+ sb.append("\n");
+ }
+
+ sb.append("\nOPTIONS\n");
+
+ Set parameters = new LinkedHashSet();
+
+ for (ParameterGroup parameterGroup : commandLineParameterGroups) {
+ parameters.addAll(parameterGroup.parameters);
+ }
+
+ for (CliParameter parameter : parameters) {
+ sb.append(' ');
+ parameter.appendDescription(sb);
+ sb.append("\n\n");
+ }
+
+ return sb.toString();
+ }
+
+}
diff --git a/src/main/java/org/openmuc/j60870/internal/cli/FlagCliParameter.java b/src/main/java/org/openmuc/j60870/internal/cli/FlagCliParameter.java
new file mode 100644
index 0000000..f0fcccf
--- /dev/null
+++ b/src/main/java/org/openmuc/j60870/internal/cli/FlagCliParameter.java
@@ -0,0 +1,36 @@
+package org.openmuc.j60870.internal.cli;
+
+public class FlagCliParameter extends CliParameter {
+
+ FlagCliParameter(CliParameterBuilder builder) {
+ super(builder);
+ }
+
+ @Override
+ int appendSynopsis(StringBuilder sb) {
+ int length = 0;
+ if (optional) {
+ sb.append("[");
+ length++;
+ }
+ sb.append(name);
+ length += name.length();
+ if (optional) {
+ sb.append("]");
+ length++;
+ }
+ return length;
+ }
+
+ @Override
+ void appendDescription(StringBuilder sb) {
+ sb.append("\t").append(name).append("\n\t ").append(description);
+ }
+
+ @Override
+ int parse(String[] args, int i) throws CliParseException {
+ selected = true;
+ return 1;
+ }
+
+}
diff --git a/src/main/java/org/openmuc/j60870/internal/cli/IntCliParameter.java b/src/main/java/org/openmuc/j60870/internal/cli/IntCliParameter.java
new file mode 100644
index 0000000..cde8a21
--- /dev/null
+++ b/src/main/java/org/openmuc/j60870/internal/cli/IntCliParameter.java
@@ -0,0 +1,43 @@
+package org.openmuc.j60870.internal.cli;
+
+public class IntCliParameter extends ValueCliParameter {
+
+ Integer value;
+
+ IntCliParameter(CliParameterBuilder builder, String parameterName, int defaultValue) {
+ super(builder, parameterName);
+ value = defaultValue;
+ }
+
+ IntCliParameter(CliParameterBuilder builder, String parameterName) {
+ super(builder, parameterName);
+ }
+
+ public int getValue() {
+ return value;
+ }
+
+ @Override
+ int parse(String[] args, int i) throws CliParseException {
+ selected = true;
+
+ if (args.length < (i + 2)) {
+ throw new CliParseException("Parameter " + name + " has no value.");
+ }
+
+ try {
+ value = Integer.decode(args[i + 1]);
+ } catch (Exception e) {
+ throw new CliParseException("Parameter value " + args[i + 1] + " cannot be converted to int.");
+ }
+ return 2;
+ }
+
+ @Override
+ void appendDescription(StringBuilder sb) {
+ super.appendDescription(sb);
+ if (value != null) {
+ sb.append(" Default is ").append(value).append(".");
+ }
+ }
+}
diff --git a/src/main/java/org/openmuc/j60870/internal/cli/LongCliParameter.java b/src/main/java/org/openmuc/j60870/internal/cli/LongCliParameter.java
new file mode 100644
index 0000000..65e499c
--- /dev/null
+++ b/src/main/java/org/openmuc/j60870/internal/cli/LongCliParameter.java
@@ -0,0 +1,43 @@
+package org.openmuc.j60870.internal.cli;
+
+public class LongCliParameter extends ValueCliParameter {
+
+ Long value;
+
+ LongCliParameter(CliParameterBuilder builder, String parameterName, long defaultValue) {
+ super(builder, parameterName);
+ value = defaultValue;
+ }
+
+ LongCliParameter(CliParameterBuilder builder, String parameterName) {
+ super(builder, parameterName);
+ }
+
+ public long getValue() {
+ return value;
+ }
+
+ @Override
+ int parse(String[] args, int i) throws CliParseException {
+ selected = true;
+
+ if (args.length < (i + 2)) {
+ throw new CliParseException("Parameter " + name + " has no value.");
+ }
+
+ try {
+ value = Long.decode(args[i + 1]);
+ } catch (Exception e) {
+ throw new CliParseException("Parameter value " + args[i + 1] + " cannot be converted to long.");
+ }
+ return 2;
+ }
+
+ @Override
+ void appendDescription(StringBuilder sb) {
+ super.appendDescription(sb);
+ if (value != null) {
+ sb.append(" Default is ").append(value).append(".");
+ }
+ }
+}
diff --git a/src/main/java/org/openmuc/j60870/internal/cli/StringCliParameter.java b/src/main/java/org/openmuc/j60870/internal/cli/StringCliParameter.java
new file mode 100644
index 0000000..e45bf4a
--- /dev/null
+++ b/src/main/java/org/openmuc/j60870/internal/cli/StringCliParameter.java
@@ -0,0 +1,39 @@
+package org.openmuc.j60870.internal.cli;
+
+public class StringCliParameter extends ValueCliParameter {
+
+ String value;
+
+ StringCliParameter(CliParameterBuilder builder, String parameterName, String defaultValue) {
+ super(builder, parameterName);
+ value = defaultValue;
+ }
+
+ StringCliParameter(CliParameterBuilder builder, String parameterName) {
+ super(builder, parameterName);
+ }
+
+ public String getValue() {
+ return value;
+ }
+
+ @Override
+ int parse(String[] args, int i) throws CliParseException {
+ selected = true;
+
+ if (args.length < (i + 2)) {
+ throw new CliParseException("Parameter " + name + " has no value.");
+ }
+ value = args[i + 1];
+
+ return 2;
+ }
+
+ @Override
+ public void appendDescription(StringBuilder sb) {
+ super.appendDescription(sb);
+ if (value != null) {
+ sb.append(" Default is ").append(value).append(".");
+ }
+ }
+}
diff --git a/src/main/java/org/openmuc/j60870/internal/cli/ValueCliParameter.java b/src/main/java/org/openmuc/j60870/internal/cli/ValueCliParameter.java
new file mode 100644
index 0000000..c544375
--- /dev/null
+++ b/src/main/java/org/openmuc/j60870/internal/cli/ValueCliParameter.java
@@ -0,0 +1,32 @@
+package org.openmuc.j60870.internal.cli;
+
+abstract class ValueCliParameter extends CliParameter {
+
+ String parameterName;
+
+ ValueCliParameter(CliParameterBuilder builder, String parameterName) {
+ super(builder);
+ this.parameterName = parameterName;
+ }
+
+ @Override
+ int appendSynopsis(StringBuilder sb) {
+ int length = 0;
+ if (optional) {
+ sb.append("[");
+ length++;
+ }
+ sb.append(name).append(" <").append(parameterName).append(">");
+ length += (name.length() + 3 + parameterName.length());
+ if (optional) {
+ sb.append("]");
+ length++;
+ }
+ return length;
+ }
+
+ @Override
+ void appendDescription(StringBuilder sb) {
+ sb.append("\t").append(name).append(" <").append(parameterName).append(">\n\t ").append(description);
+ }
+}
diff --git a/src/sample/java/SampleServer.java b/src/sample/java/SampleServer.java
deleted file mode 100644
index 9709f14..0000000
--- a/src/sample/java/SampleServer.java
+++ /dev/null
@@ -1,121 +0,0 @@
-
-/*
- * This file is part of j60870.
- * For more information visit http://www.openmuc.org
- *
- * You are free to use code of this sample file in any
- * way you like and without any restrictions.
- *
- */
-import java.io.EOFException;
-import java.io.IOException;
-import java.util.concurrent.TimeoutException;
-
-import org.openmuc.j60870.ASdu;
-import org.openmuc.j60870.CauseOfTransmission;
-import org.openmuc.j60870.Connection;
-import org.openmuc.j60870.ConnectionEventListener;
-import org.openmuc.j60870.IeQuality;
-import org.openmuc.j60870.IeScaledValue;
-import org.openmuc.j60870.InformationElement;
-import org.openmuc.j60870.InformationObject;
-import org.openmuc.j60870.ServerSap;
-import org.openmuc.j60870.ServerSapListener;
-import org.openmuc.j60870.TypeId;
-
-public class SampleServer implements ServerSapListener, ConnectionEventListener {
-
- private static int connectionIdCounter = 1;
-
- private Connection connection;
- private int connectionId;
-
- public static void main(String[] args) {
- ServerSap serverSap = new ServerSap(new SampleServer());
-
- try {
- serverSap.startListening();
- } catch (IOException e) {
- System.out.println("Unable to start listening: \"" + e.getMessage() + "\". Will quit.");
- }
- }
-
- public SampleServer() {
- }
-
- public SampleServer(Connection connection, int connectionId) {
- this.connection = connection;
- this.connectionId = connectionId;
- }
-
- @Override
- public void connectionIndication(Connection connection) {
-
- int myConnectionId = connectionIdCounter++;
- System.out.println("A client has connected using TCP/IP. Will listen for a StartDT request. Connection ID: "
- + myConnectionId);
-
- try {
- connection.waitForStartDT(new SampleServer(connection, myConnectionId), 5000);
- } catch (IOException e) {
- System.out.println("Connection (" + myConnectionId + ") interrupted while waiting for StartDT: "
- + e.getMessage() + ". Will quit.");
- return;
- } catch (TimeoutException e) {
- }
-
- System.out.println(
- "Started data transfer on connection (" + myConnectionId + ") Will listen for incoming commands.");
-
- }
-
- @Override
- public void serverStoppedListeningIndication(IOException e) {
- System.out.println("Server has stopped listening for new connections : \"" + e.getMessage() + "\". Will quit.");
- }
-
- @Override
- public void newASdu(ASdu aSdu) {
- try {
-
- switch (aSdu.getTypeIdentification()) {
- // interrogation command
- case C_IC_NA_1:
- connection.sendConfirmation(aSdu);
- System.out.println("Got interrogation command. Will send scaled measured values.\n");
-
- connection
- .send(new ASdu(TypeId.M_ME_NB_1, true, CauseOfTransmission.SPONTANEOUS, false, false, 0,
- aSdu.getCommonAddress(),
- new InformationObject[] { new InformationObject(1,
- new InformationElement[][] { { new IeScaledValue(-32768),
- new IeQuality(true, true, true, true, true) },
- { new IeScaledValue(10), new IeQuality(true, true, true, true, true) },
- { new IeScaledValue(-5), new IeQuality(true, true, true, true, true) } }) }));
-
- break;
- default:
- System.out.println("Got unknown request: " + aSdu + ". Will not confirm it.\n");
- }
-
- } catch (EOFException e) {
- System.out.println(
- "Will quit listening for commands on connection (" + connectionId + ") because socket was closed.");
- } catch (IOException e) {
- System.out.println("Will quit listening for commands on connection (" + connectionId
- + ") because of error: \"" + e.getMessage() + "\".");
- }
-
- }
-
- @Override
- public void connectionClosed(IOException e) {
- System.out.println("Connection (" + connectionId + ") was closed. " + e.getMessage());
- }
-
- @Override
- public void connectionAttemptFailed(IOException e) {
- System.out.println("Connection attempt failed: " + e.getMessage());
-
- }
-}
diff --git a/src/sample/java/org/openmuc/j60870/SampleServer.java b/src/sample/java/org/openmuc/j60870/SampleServer.java
new file mode 100644
index 0000000..cdcbcff
--- /dev/null
+++ b/src/sample/java/org/openmuc/j60870/SampleServer.java
@@ -0,0 +1,120 @@
+package org.openmuc.j60870;
+
+/*
+ * This file is part of j60870.
+ * For more information visit http://www.openmuc.org
+ *
+ * You are free to use code of this sample file in any
+ * way you like and without any restrictions.
+ *
+ */
+import java.io.EOFException;
+import java.io.IOException;
+import java.util.concurrent.TimeoutException;
+
+public class SampleServer {
+
+ public class ServerListener implements ServerEventListener {
+
+ public class ConnectionListener implements ConnectionEventListener {
+
+ private final Connection connection;
+ private final int connectionId;
+
+ public ConnectionListener(Connection connection, int connectionId) {
+ this.connection = connection;
+ this.connectionId = connectionId;
+ }
+
+ @Override
+ public void newASdu(ASdu aSdu) {
+ try {
+
+ switch (aSdu.getTypeIdentification()) {
+ // interrogation command
+ case C_IC_NA_1:
+ connection.sendConfirmation(aSdu);
+ System.out.println("Got interrogation command. Will send scaled measured values.\n");
+
+ connection.send(new ASdu(TypeId.M_ME_NB_1, true, CauseOfTransmission.SPONTANEOUS, false, false,
+ 0, aSdu.getCommonAddress(),
+ new InformationObject[] { new InformationObject(1, new InformationElement[][] {
+ { new IeScaledValue(-32768), new IeQuality(true, true, true, true, true) },
+ { new IeScaledValue(10), new IeQuality(true, true, true, true, true) },
+ { new IeScaledValue(-5), new IeQuality(true, true, true, true, true) } }) }));
+
+ break;
+ default:
+ System.out.println("Got unknown request: " + aSdu + ". Will not confirm it.\n");
+ }
+
+ } catch (EOFException e) {
+ System.out.println("Will quit listening for commands on connection (" + connectionId
+ + ") because socket was closed.");
+ } catch (IOException e) {
+ System.out.println("Will quit listening for commands on connection (" + connectionId
+ + ") because of error: \"" + e.getMessage() + "\".");
+ }
+
+ }
+
+ @Override
+ public void connectionClosed(IOException e) {
+ System.out.println("Connection (" + connectionId + ") was closed. " + e.getMessage());
+ }
+
+ }
+
+ @Override
+ public void connectionIndication(Connection connection) {
+
+ int myConnectionId = connectionIdCounter++;
+ System.out.println("A client has connected using TCP/IP. Will listen for a StartDT request. Connection ID: "
+ + myConnectionId);
+
+ try {
+ connection.waitForStartDT(new ConnectionListener(connection, myConnectionId), 5000);
+ } catch (IOException e) {
+ System.out.println("Connection (" + myConnectionId + ") interrupted while waiting for StartDT: "
+ + e.getMessage() + ". Will quit.");
+ return;
+ } catch (TimeoutException e) {
+ }
+
+ System.out.println(
+ "Started data transfer on connection (" + myConnectionId + ") Will listen for incoming commands.");
+
+ }
+
+ @Override
+ public void serverStoppedListeningIndication(IOException e) {
+ System.out.println(
+ "Server has stopped listening for new connections : \"" + e.getMessage() + "\". Will quit.");
+ }
+
+ @Override
+ public void connectionAttemptFailed(IOException e) {
+ System.out.println("Connection attempt failed: " + e.getMessage());
+
+ }
+
+ }
+
+ private int connectionIdCounter = 1;
+
+ public static void main(String[] args) {
+ new SampleServer().start();
+ }
+
+ public void start() {
+ Server server = new Server.Builder().build();
+
+ try {
+ server.start(new ServerListener());
+ } catch (IOException e) {
+ System.out.println("Unable to start listening: \"" + e.getMessage() + "\". Will quit.");
+ return;
+ }
+ }
+
+}
diff --git a/src/test/java/org/openmuc/j60870/CP56Time2aTest.java b/src/test/java/org/openmuc/j60870/CP56Time2aTest.java
index 9af9516..4315a4a 100644
--- a/src/test/java/org/openmuc/j60870/CP56Time2aTest.java
+++ b/src/test/java/org/openmuc/j60870/CP56Time2aTest.java
@@ -23,6 +23,12 @@
import org.junit.Assert;
import org.junit.Test;
+import java.text.SimpleDateFormat;
+import java.util.Calendar;
+import java.util.Date;
+import java.util.Locale;
+import java.util.TimeZone;
+
public class CP56Time2aTest {
@Test
@@ -31,10 +37,10 @@ public void testTimestampToCalendar() {
IeTime56 time = new IeTime56(timestamp);
byte[] buffer = new byte[7];
time.encode(buffer, 0);
- // Assert.assertArrayEquals(new byte[]{0x44, (byte) 0xd5, 0x1e, 0x17, 0x5d, 0x0a, 0x0d},
- // buffer);
- // Assert.assertEquals(timestamp, time.getTimestamp());
+ Assert.assertArrayEquals(new byte[]{0x44, (byte) 0xd5, 0x1e, 0x17, 0x5d, 0x0a, 0x0d},
+ buffer);
+ Assert.assertEquals(timestamp, time.getTimestamp());
}
}
diff --git a/src/test/java/org/openmuc/j60870/test/ClientServerITest.java b/src/test/java/org/openmuc/j60870/test/ClientServerITest.java
index c75df71..562d407 100644
--- a/src/test/java/org/openmuc/j60870/test/ClientServerITest.java
+++ b/src/test/java/org/openmuc/j60870/test/ClientServerITest.java
@@ -27,7 +27,7 @@
import org.junit.Test;
import org.openmuc.j60870.ASdu;
import org.openmuc.j60870.CauseOfTransmission;
-import org.openmuc.j60870.ClientSap;
+import org.openmuc.j60870.ClientConnectionBuilder;
import org.openmuc.j60870.Connection;
import org.openmuc.j60870.ConnectionEventListener;
import org.openmuc.j60870.IeBinaryCounterReading;
@@ -55,18 +55,17 @@
import org.openmuc.j60870.IeValueWithTransientState;
import org.openmuc.j60870.InformationElement;
import org.openmuc.j60870.InformationObject;
-import org.openmuc.j60870.ServerSap;
-import org.openmuc.j60870.ServerSapListener;
+import org.openmuc.j60870.Server;
+import org.openmuc.j60870.ServerEventListener;
import org.openmuc.j60870.TypeId;
import org.openmuc.j60870.Util;
-public class ClientServerITest implements ServerSapListener, ConnectionEventListener {
+public class ClientServerITest implements ServerEventListener, ConnectionEventListener {
private final static int port = 2404;
String host = "127.0.0.1";
- ClientSap clientSap = new ClientSap();
- ServerSap serverSap = null;
+ Server serverSap = null;
int counter = 0;
int serverCounter = 0;
int counter2 = 0;
@@ -179,16 +178,16 @@ else if (serverCounter == 6) {
new InformationObject(1,
new InformationElement[][] { { new IeSinglePointWithQuality(true,
true, true, true, true) } }),
- new InformationObject(2, new InformationElement[][] {
- { new IeSinglePointWithQuality(false, false, false, false, false) } }) }));
+ new InformationObject(2,
+ new InformationElement[][] { { new IeSinglePointWithQuality(false,
+ false, false, false, false) } }) }));
// 2
- serverConnection
- .send(new ASdu(TypeId.M_SP_NA_1, true, CauseOfTransmission.SPONTANEOUS, false, false, 0,
- aSdu.getCommonAddress(),
- new InformationObject[] { new InformationObject(1,
- new InformationElement[][] { { new IeSinglePointWithQuality(true, true,
- true, true, true) },
+ serverConnection.send(new ASdu(TypeId.M_SP_NA_1, true, CauseOfTransmission.SPONTANEOUS, false,
+ false, 0, aSdu.getCommonAddress(),
+ new InformationObject[] { new InformationObject(1,
+ new InformationElement[][] {
+ { new IeSinglePointWithQuality(true, true, true, true, true) },
{ new IeSinglePointWithQuality(false, false, false, false, false) } }) }));
// 3
@@ -217,66 +216,60 @@ else if (serverCounter == 6) {
// 5
serverConnection
- .send(new ASdu(TypeId.M_ST_NA_1, true, CauseOfTransmission.SPONTANEOUS, false, false,
- 0, aSdu
- .getCommonAddress(),
- new InformationObject[] { new InformationObject(1,
- new InformationElement[][] {
- { new IeValueWithTransientState(-5, true),
- new IeQuality(true, true, true, true, true) },
- { new IeValueWithTransientState(-5, false),
- new IeQuality(true, true, true, true, true) },
- { new IeValueWithTransientState(-64, true),
- new IeQuality(true, true, true, true, true) },
- { new IeValueWithTransientState(10, false),
- new IeQuality(true, true, true, true, true) } }) }));
-
- // 6
- serverConnection
- .send(new ASdu(TypeId.M_BO_NA_1, true, CauseOfTransmission.SPONTANEOUS, false, false, 0,
+ .send(new ASdu(TypeId.M_ST_NA_1, true, CauseOfTransmission.SPONTANEOUS, false, false, 0,
aSdu.getCommonAddress(),
new InformationObject[] {
new InformationObject(1,
new InformationElement[][] {
- { new IeBinaryStateInformation(0xff), new IeQuality(true,
- true, true, true, true) },
- { new IeBinaryStateInformation(0xffffffff),
- new IeQuality(true, true, true, true, true) } }) }));
+ { new IeValueWithTransientState(-5, true),
+ new IeQuality(true, true, true, true, true) },
+ { new IeValueWithTransientState(-5, false),
+ new IeQuality(true, true, true, true, true) },
+ { new IeValueWithTransientState(-64, true),
+ new IeQuality(true, true, true, true, true) },
+ { new IeValueWithTransientState(10, false), new IeQuality(
+ true, true, true, true, true) } }) }));
+
+ // 6
+ serverConnection.send(new ASdu(TypeId.M_BO_NA_1, true, CauseOfTransmission.SPONTANEOUS, false,
+ false, 0, aSdu.getCommonAddress(),
+ new InformationObject[] { new InformationObject(1,
+ new InformationElement[][] {
+ { new IeBinaryStateInformation(0xff),
+ new IeQuality(true, true, true, true, true) },
+ { new IeBinaryStateInformation(0xffffffff),
+ new IeQuality(true, true, true, true, true) } }) }));
// 7
serverConnection.send(new ASdu(TypeId.M_ME_NA_1, true, CauseOfTransmission.SPONTANEOUS, false,
false, 0, aSdu.getCommonAddress(),
new InformationObject[] { new InformationObject(1, new InformationElement[][] {
- { new IeNormalizedValue(-32768), new IeQuality(true, true, true, true, true) },
- { new IeNormalizedValue(0), new IeQuality(true, true, true, true, true) } }) }));
+ { new IeNormalizedValue(-32768), new IeQuality(true, true, true, true, true) }, {
+ new IeNormalizedValue(0),
+ new IeQuality(true, true, true, true, true) } }) }));
// 8
- serverConnection
- .send(new ASdu(TypeId.M_ME_NB_1, true, CauseOfTransmission.SPONTANEOUS, false, false,
- 0, aSdu
- .getCommonAddress(),
- new InformationObject[] { new InformationObject(1,
- new InformationElement[][] { { new IeScaledValue(-32768),
- new IeQuality(true, true, true, true, true) },
- { new IeScaledValue(10), new IeQuality(true, true, true, true, true) },
- { new IeScaledValue(-5),
- new IeQuality(true, true, true, true, true) } }) }));
+ serverConnection.send(new ASdu(TypeId.M_ME_NB_1, true, CauseOfTransmission.SPONTANEOUS, false,
+ false, 0, aSdu.getCommonAddress(),
+ new InformationObject[] { new InformationObject(1, new InformationElement[][] {
+ { new IeScaledValue(-32768), new IeQuality(true, true, true, true, true) },
+ { new IeScaledValue(10), new IeQuality(true, true, true, true, true) },
+ { new IeScaledValue(-5), new IeQuality(true, true, true, true, true) } }) }));
serverConnection.send(new ASdu(TypeId.M_ME_NC_1, true, CauseOfTransmission.SPONTANEOUS, false,
false, 0, aSdu.getCommonAddress(),
new InformationObject[] { new InformationObject(1, new InformationElement[][] {
- { new IeShortFloat(-32768.2f), new IeQuality(true, true, true, true, true) },
- { new IeShortFloat(10.5f), new IeQuality(true, true, true, true, true) } }) }));
+ { new IeShortFloat(-32768.2f), new IeQuality(true, true, true, true, true) }, {
+ new IeShortFloat(10.5f),
+ new IeQuality(true, true, true, true, true) } }) }));
// 10
- serverConnection
- .send(new ASdu(TypeId.M_IT_NA_1, true, CauseOfTransmission.SPONTANEOUS, false, false, 0,
- aSdu.getCommonAddress(),
- new InformationObject[] {
- new InformationObject(1,
- new InformationElement[][] { { new IeBinaryCounterReading(-300, 5,
- true, true, true) },
- { new IeBinaryCounterReading(-300, 4, false, false, false) } }) }));
+ serverConnection.send(new ASdu(TypeId.M_IT_NA_1, true, CauseOfTransmission.SPONTANEOUS, false,
+ false, 0, aSdu.getCommonAddress(),
+ new InformationObject[] { new InformationObject(1,
+ new InformationElement[][] {
+ { new IeBinaryCounterReading(-300, 5, true, true, true) }, {
+ new IeBinaryCounterReading(-300, 4, false, false, false) } }) }));
serverConnection.send(new ASdu(TypeId.M_EP_TA_1, false, CauseOfTransmission.SPONTANEOUS, false,
false, 0, aSdu.getCommonAddress(),
@@ -305,14 +298,13 @@ else if (serverCounter == 6) {
new IeTime16(300), new IeTime24(400) } }) }));
serverConnection
- .send(new ASdu(TypeId.M_PS_NA_1, false, CauseOfTransmission.SPONTANEOUS, false, false,
- 0, aSdu
- .getCommonAddress(),
+ .send(new ASdu(TypeId.M_PS_NA_1, false, CauseOfTransmission.SPONTANEOUS, false, false, 0,
+ aSdu.getCommonAddress(),
new InformationObject[] {
new InformationObject(1,
new InformationElement[][] { {
- new IeStatusAndStatusChanges(0xff0000ff),
- new IeQuality(true, true, true, true, true) } }) }));
+ new IeStatusAndStatusChanges(0xff0000ff), new IeQuality(
+ true, true, true, true, true) } }) }));
Thread.sleep(1000);
@@ -366,11 +358,11 @@ public void connectionClosed(IOException e) {
@Test
public void testClientServerCom() throws Exception {
- serverSap = new ServerSap(port, this);
- serverSap.startListening();
-
- Connection clientConnection = clientSap.connect(InetAddress.getByName("127.0.0.1"), port);
+ serverSap = new Server.Builder().setPort(port).build();// (port, this);
+ serverSap.start(this);
+ Connection clientConnection = new ClientConnectionBuilder(InetAddress.getByName("127.0.0.1")).setPort(port)
+ .connect();
try {
clientConnection.startDataTransfer(this, 5000);