diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..b2ca9e2
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,18 @@
+# Fleet IDE
+.fleet
+
+# Intellij
+.idea
+*.iml
+
+# Eclipse
+.project
+.classpath
+
+# Maven
+target
+
+
+debug.out
+.DS_Store
+
diff --git a/.mvn/wrapper/maven-wrapper.jar b/.mvn/wrapper/maven-wrapper.jar
new file mode 100644
index 0000000..cb28b0e
Binary files /dev/null and b/.mvn/wrapper/maven-wrapper.jar differ
diff --git a/.mvn/wrapper/maven-wrapper.properties b/.mvn/wrapper/maven-wrapper.properties
new file mode 100644
index 0000000..3c6fda8
--- /dev/null
+++ b/.mvn/wrapper/maven-wrapper.properties
@@ -0,0 +1,18 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.2/apache-maven-3.9.2-bin.zip
+wrapperUrl=https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.2.0/maven-wrapper-3.2.0.jar
diff --git a/SimpleLogFile.txt b/SimpleLogFile.txt
new file mode 100644
index 0000000..1f82eed
--- /dev/null
+++ b/SimpleLogFile.txt
@@ -0,0 +1 @@
+do5sa
\ No newline at end of file
diff --git a/lib/javafx-swt.jar b/lib/javafx-swt.jar
new file mode 100644
index 0000000..c1e132f
Binary files /dev/null and b/lib/javafx-swt.jar differ
diff --git a/lib/javafx.base.jar b/lib/javafx.base.jar
new file mode 100644
index 0000000..872eae0
Binary files /dev/null and b/lib/javafx.base.jar differ
diff --git a/lib/javafx.controls.jar b/lib/javafx.controls.jar
new file mode 100644
index 0000000..2819d75
Binary files /dev/null and b/lib/javafx.controls.jar differ
diff --git a/lib/javafx.fxml.jar b/lib/javafx.fxml.jar
new file mode 100644
index 0000000..f46d1fb
Binary files /dev/null and b/lib/javafx.fxml.jar differ
diff --git a/lib/javafx.graphics.jar b/lib/javafx.graphics.jar
new file mode 100644
index 0000000..f7f74b1
Binary files /dev/null and b/lib/javafx.graphics.jar differ
diff --git a/lib/javafx.media.jar b/lib/javafx.media.jar
new file mode 100644
index 0000000..dd39afa
Binary files /dev/null and b/lib/javafx.media.jar differ
diff --git a/lib/javafx.properties b/lib/javafx.properties
new file mode 100644
index 0000000..5496314
--- /dev/null
+++ b/lib/javafx.properties
@@ -0,0 +1,3 @@
+javafx.version=19
+javafx.runtime.version=19+11
+javafx.runtime.build=11
diff --git a/lib/javafx.swing.jar b/lib/javafx.swing.jar
new file mode 100644
index 0000000..886d72f
Binary files /dev/null and b/lib/javafx.swing.jar differ
diff --git a/lib/javafx.web.jar b/lib/javafx.web.jar
new file mode 100644
index 0000000..5bd5928
Binary files /dev/null and b/lib/javafx.web.jar differ
diff --git a/lib/sqlite-jdbc-3.39.3.0.jar b/lib/sqlite-jdbc-3.39.3.0.jar
new file mode 100644
index 0000000..23ab073
Binary files /dev/null and b/lib/sqlite-jdbc-3.39.3.0.jar differ
diff --git a/mvnw b/mvnw
new file mode 100755
index 0000000..8d937f4
--- /dev/null
+++ b/mvnw
@@ -0,0 +1,308 @@
+#!/bin/sh
+# ----------------------------------------------------------------------------
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+# ----------------------------------------------------------------------------
+
+# ----------------------------------------------------------------------------
+# Apache Maven Wrapper startup batch script, version 3.2.0
+#
+# Required ENV vars:
+# ------------------
+# JAVA_HOME - location of a JDK home dir
+#
+# Optional ENV vars
+# -----------------
+# MAVEN_OPTS - parameters passed to the Java VM when running Maven
+# e.g. to debug Maven itself, use
+# set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000
+# MAVEN_SKIP_RC - flag to disable loading of mavenrc files
+# ----------------------------------------------------------------------------
+
+if [ -z "$MAVEN_SKIP_RC" ] ; then
+
+ if [ -f /usr/local/etc/mavenrc ] ; then
+ . /usr/local/etc/mavenrc
+ fi
+
+ if [ -f /etc/mavenrc ] ; then
+ . /etc/mavenrc
+ fi
+
+ if [ -f "$HOME/.mavenrc" ] ; then
+ . "$HOME/.mavenrc"
+ fi
+
+fi
+
+# OS specific support. $var _must_ be set to either true or false.
+cygwin=false;
+darwin=false;
+mingw=false
+case "$(uname)" in
+ CYGWIN*) cygwin=true ;;
+ MINGW*) mingw=true;;
+ Darwin*) darwin=true
+ # Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home
+ # See https://developer.apple.com/library/mac/qa/qa1170/_index.html
+ if [ -z "$JAVA_HOME" ]; then
+ if [ -x "/usr/libexec/java_home" ]; then
+ JAVA_HOME="$(/usr/libexec/java_home)"; export JAVA_HOME
+ else
+ JAVA_HOME="/Library/Java/Home"; export JAVA_HOME
+ fi
+ fi
+ ;;
+esac
+
+if [ -z "$JAVA_HOME" ] ; then
+ if [ -r /etc/gentoo-release ] ; then
+ JAVA_HOME=$(java-config --jre-home)
+ fi
+fi
+
+# For Cygwin, ensure paths are in UNIX format before anything is touched
+if $cygwin ; then
+ [ -n "$JAVA_HOME" ] &&
+ JAVA_HOME=$(cygpath --unix "$JAVA_HOME")
+ [ -n "$CLASSPATH" ] &&
+ CLASSPATH=$(cygpath --path --unix "$CLASSPATH")
+fi
+
+# For Mingw, ensure paths are in UNIX format before anything is touched
+if $mingw ; then
+ [ -n "$JAVA_HOME" ] && [ -d "$JAVA_HOME" ] &&
+ JAVA_HOME="$(cd "$JAVA_HOME" || (echo "cannot cd into $JAVA_HOME."; exit 1); pwd)"
+fi
+
+if [ -z "$JAVA_HOME" ]; then
+ javaExecutable="$(which javac)"
+ if [ -n "$javaExecutable" ] && ! [ "$(expr "\"$javaExecutable\"" : '\([^ ]*\)')" = "no" ]; then
+ # readlink(1) is not available as standard on Solaris 10.
+ readLink=$(which readlink)
+ if [ ! "$(expr "$readLink" : '\([^ ]*\)')" = "no" ]; then
+ if $darwin ; then
+ javaHome="$(dirname "\"$javaExecutable\"")"
+ javaExecutable="$(cd "\"$javaHome\"" && pwd -P)/javac"
+ else
+ javaExecutable="$(readlink -f "\"$javaExecutable\"")"
+ fi
+ javaHome="$(dirname "\"$javaExecutable\"")"
+ javaHome=$(expr "$javaHome" : '\(.*\)/bin')
+ JAVA_HOME="$javaHome"
+ export JAVA_HOME
+ fi
+ fi
+fi
+
+if [ -z "$JAVACMD" ] ; then
+ if [ -n "$JAVA_HOME" ] ; then
+ if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+ # IBM's JDK on AIX uses strange locations for the executables
+ JAVACMD="$JAVA_HOME/jre/sh/java"
+ else
+ JAVACMD="$JAVA_HOME/bin/java"
+ fi
+ else
+ JAVACMD="$(\unset -f command 2>/dev/null; \command -v java)"
+ fi
+fi
+
+if [ ! -x "$JAVACMD" ] ; then
+ echo "Error: JAVA_HOME is not defined correctly." >&2
+ echo " We cannot execute $JAVACMD" >&2
+ exit 1
+fi
+
+if [ -z "$JAVA_HOME" ] ; then
+ echo "Warning: JAVA_HOME environment variable is not set."
+fi
+
+# traverses directory structure from process work directory to filesystem root
+# first directory with .mvn subdirectory is considered project base directory
+find_maven_basedir() {
+ if [ -z "$1" ]
+ then
+ echo "Path not specified to find_maven_basedir"
+ return 1
+ fi
+
+ basedir="$1"
+ wdir="$1"
+ while [ "$wdir" != '/' ] ; do
+ if [ -d "$wdir"/.mvn ] ; then
+ basedir=$wdir
+ break
+ fi
+ # workaround for JBEAP-8937 (on Solaris 10/Sparc)
+ if [ -d "${wdir}" ]; then
+ wdir=$(cd "$wdir/.." || exit 1; pwd)
+ fi
+ # end of workaround
+ done
+ printf '%s' "$(cd "$basedir" || exit 1; pwd)"
+}
+
+# concatenates all lines of a file
+concat_lines() {
+ if [ -f "$1" ]; then
+ # Remove \r in case we run on Windows within Git Bash
+ # and check out the repository with auto CRLF management
+ # enabled. Otherwise, we may read lines that are delimited with
+ # \r\n and produce $'-Xarg\r' rather than -Xarg due to word
+ # splitting rules.
+ tr -s '\r\n' ' ' < "$1"
+ fi
+}
+
+log() {
+ if [ "$MVNW_VERBOSE" = true ]; then
+ printf '%s\n' "$1"
+ fi
+}
+
+BASE_DIR=$(find_maven_basedir "$(dirname "$0")")
+if [ -z "$BASE_DIR" ]; then
+ exit 1;
+fi
+
+MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"}; export MAVEN_PROJECTBASEDIR
+log "$MAVEN_PROJECTBASEDIR"
+
+##########################################################################################
+# Extension to allow automatically downloading the maven-wrapper.jar from Maven-central
+# This allows using the maven wrapper in projects that prohibit checking in binary data.
+##########################################################################################
+wrapperJarPath="$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar"
+if [ -r "$wrapperJarPath" ]; then
+ log "Found $wrapperJarPath"
+else
+ log "Couldn't find $wrapperJarPath, downloading it ..."
+
+ if [ -n "$MVNW_REPOURL" ]; then
+ wrapperUrl="$MVNW_REPOURL/org/apache/maven/wrapper/maven-wrapper/3.2.0/maven-wrapper-3.2.0.jar"
+ else
+ wrapperUrl="https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.2.0/maven-wrapper-3.2.0.jar"
+ fi
+ while IFS="=" read -r key value; do
+ # Remove '\r' from value to allow usage on windows as IFS does not consider '\r' as a separator ( considers space, tab, new line ('\n'), and custom '=' )
+ safeValue=$(echo "$value" | tr -d '\r')
+ case "$key" in (wrapperUrl) wrapperUrl="$safeValue"; break ;;
+ esac
+ done < "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.properties"
+ log "Downloading from: $wrapperUrl"
+
+ if $cygwin; then
+ wrapperJarPath=$(cygpath --path --windows "$wrapperJarPath")
+ fi
+
+ if command -v wget > /dev/null; then
+ log "Found wget ... using wget"
+ [ "$MVNW_VERBOSE" = true ] && QUIET="" || QUIET="--quiet"
+ if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then
+ wget $QUIET "$wrapperUrl" -O "$wrapperJarPath" || rm -f "$wrapperJarPath"
+ else
+ wget $QUIET --http-user="$MVNW_USERNAME" --http-password="$MVNW_PASSWORD" "$wrapperUrl" -O "$wrapperJarPath" || rm -f "$wrapperJarPath"
+ fi
+ elif command -v curl > /dev/null; then
+ log "Found curl ... using curl"
+ [ "$MVNW_VERBOSE" = true ] && QUIET="" || QUIET="--silent"
+ if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then
+ curl $QUIET -o "$wrapperJarPath" "$wrapperUrl" -f -L || rm -f "$wrapperJarPath"
+ else
+ curl $QUIET --user "$MVNW_USERNAME:$MVNW_PASSWORD" -o "$wrapperJarPath" "$wrapperUrl" -f -L || rm -f "$wrapperJarPath"
+ fi
+ else
+ log "Falling back to using Java to download"
+ javaSource="$MAVEN_PROJECTBASEDIR/.mvn/wrapper/MavenWrapperDownloader.java"
+ javaClass="$MAVEN_PROJECTBASEDIR/.mvn/wrapper/MavenWrapperDownloader.class"
+ # For Cygwin, switch paths to Windows format before running javac
+ if $cygwin; then
+ javaSource=$(cygpath --path --windows "$javaSource")
+ javaClass=$(cygpath --path --windows "$javaClass")
+ fi
+ if [ -e "$javaSource" ]; then
+ if [ ! -e "$javaClass" ]; then
+ log " - Compiling MavenWrapperDownloader.java ..."
+ ("$JAVA_HOME/bin/javac" "$javaSource")
+ fi
+ if [ -e "$javaClass" ]; then
+ log " - Running MavenWrapperDownloader.java ..."
+ ("$JAVA_HOME/bin/java" -cp .mvn/wrapper MavenWrapperDownloader "$wrapperUrl" "$wrapperJarPath") || rm -f "$wrapperJarPath"
+ fi
+ fi
+ fi
+fi
+##########################################################################################
+# End of extension
+##########################################################################################
+
+# If specified, validate the SHA-256 sum of the Maven wrapper jar file
+wrapperSha256Sum=""
+while IFS="=" read -r key value; do
+ case "$key" in (wrapperSha256Sum) wrapperSha256Sum=$value; break ;;
+ esac
+done < "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.properties"
+if [ -n "$wrapperSha256Sum" ]; then
+ wrapperSha256Result=false
+ if command -v sha256sum > /dev/null; then
+ if echo "$wrapperSha256Sum $wrapperJarPath" | sha256sum -c > /dev/null 2>&1; then
+ wrapperSha256Result=true
+ fi
+ elif command -v shasum > /dev/null; then
+ if echo "$wrapperSha256Sum $wrapperJarPath" | shasum -a 256 -c > /dev/null 2>&1; then
+ wrapperSha256Result=true
+ fi
+ else
+ echo "Checksum validation was requested but neither 'sha256sum' or 'shasum' are available."
+ echo "Please install either command, or disable validation by removing 'wrapperSha256Sum' from your maven-wrapper.properties."
+ exit 1
+ fi
+ if [ $wrapperSha256Result = false ]; then
+ echo "Error: Failed to validate Maven wrapper SHA-256, your Maven wrapper might be compromised." >&2
+ echo "Investigate or delete $wrapperJarPath to attempt a clean download." >&2
+ echo "If you updated your Maven version, you need to update the specified wrapperSha256Sum property." >&2
+ exit 1
+ fi
+fi
+
+MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS"
+
+# For Cygwin, switch paths to Windows format before running java
+if $cygwin; then
+ [ -n "$JAVA_HOME" ] &&
+ JAVA_HOME=$(cygpath --path --windows "$JAVA_HOME")
+ [ -n "$CLASSPATH" ] &&
+ CLASSPATH=$(cygpath --path --windows "$CLASSPATH")
+ [ -n "$MAVEN_PROJECTBASEDIR" ] &&
+ MAVEN_PROJECTBASEDIR=$(cygpath --path --windows "$MAVEN_PROJECTBASEDIR")
+fi
+
+# Provide a "standardized" way to retrieve the CLI args that will
+# work with both Windows and non-Windows executions.
+MAVEN_CMD_LINE_ARGS="$MAVEN_CONFIG $*"
+export MAVEN_CMD_LINE_ARGS
+
+WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain
+
+# shellcheck disable=SC2086 # safe args
+exec "$JAVACMD" \
+ $MAVEN_OPTS \
+ $MAVEN_DEBUG_OPTS \
+ -classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \
+ "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \
+ ${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@"
diff --git a/mvnw.cmd b/mvnw.cmd
new file mode 100644
index 0000000..c4586b5
--- /dev/null
+++ b/mvnw.cmd
@@ -0,0 +1,205 @@
+@REM ----------------------------------------------------------------------------
+@REM Licensed to the Apache Software Foundation (ASF) under one
+@REM or more contributor license agreements. See the NOTICE file
+@REM distributed with this work for additional information
+@REM regarding copyright ownership. The ASF licenses this file
+@REM to you under the Apache License, Version 2.0 (the
+@REM "License"); you may not use this file except in compliance
+@REM with the License. You may obtain a copy of the License at
+@REM
+@REM http://www.apache.org/licenses/LICENSE-2.0
+@REM
+@REM Unless required by applicable law or agreed to in writing,
+@REM software distributed under the License is distributed on an
+@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+@REM KIND, either express or implied. See the License for the
+@REM specific language governing permissions and limitations
+@REM under the License.
+@REM ----------------------------------------------------------------------------
+
+@REM ----------------------------------------------------------------------------
+@REM Apache Maven Wrapper startup batch script, version 3.2.0
+@REM
+@REM Required ENV vars:
+@REM JAVA_HOME - location of a JDK home dir
+@REM
+@REM Optional ENV vars
+@REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands
+@REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a keystroke before ending
+@REM MAVEN_OPTS - parameters passed to the Java VM when running Maven
+@REM e.g. to debug Maven itself, use
+@REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000
+@REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files
+@REM ----------------------------------------------------------------------------
+
+@REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on'
+@echo off
+@REM set title of command window
+title %0
+@REM enable echoing by setting MAVEN_BATCH_ECHO to 'on'
+@if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO%
+
+@REM set %HOME% to equivalent of $HOME
+if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%")
+
+@REM Execute a user defined script before this one
+if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre
+@REM check for pre script, once with legacy .bat ending and once with .cmd ending
+if exist "%USERPROFILE%\mavenrc_pre.bat" call "%USERPROFILE%\mavenrc_pre.bat" %*
+if exist "%USERPROFILE%\mavenrc_pre.cmd" call "%USERPROFILE%\mavenrc_pre.cmd" %*
+:skipRcPre
+
+@setlocal
+
+set ERROR_CODE=0
+
+@REM To isolate internal variables from possible post scripts, we use another setlocal
+@setlocal
+
+@REM ==== START VALIDATION ====
+if not "%JAVA_HOME%" == "" goto OkJHome
+
+echo.
+echo Error: JAVA_HOME not found in your environment. >&2
+echo Please set the JAVA_HOME variable in your environment to match the >&2
+echo location of your Java installation. >&2
+echo.
+goto error
+
+:OkJHome
+if exist "%JAVA_HOME%\bin\java.exe" goto init
+
+echo.
+echo Error: JAVA_HOME is set to an invalid directory. >&2
+echo JAVA_HOME = "%JAVA_HOME%" >&2
+echo Please set the JAVA_HOME variable in your environment to match the >&2
+echo location of your Java installation. >&2
+echo.
+goto error
+
+@REM ==== END VALIDATION ====
+
+:init
+
+@REM Find the project base dir, i.e. the directory that contains the folder ".mvn".
+@REM Fallback to current working directory if not found.
+
+set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR%
+IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir
+
+set EXEC_DIR=%CD%
+set WDIR=%EXEC_DIR%
+:findBaseDir
+IF EXIST "%WDIR%"\.mvn goto baseDirFound
+cd ..
+IF "%WDIR%"=="%CD%" goto baseDirNotFound
+set WDIR=%CD%
+goto findBaseDir
+
+:baseDirFound
+set MAVEN_PROJECTBASEDIR=%WDIR%
+cd "%EXEC_DIR%"
+goto endDetectBaseDir
+
+:baseDirNotFound
+set MAVEN_PROJECTBASEDIR=%EXEC_DIR%
+cd "%EXEC_DIR%"
+
+:endDetectBaseDir
+
+IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig
+
+@setlocal EnableExtensions EnableDelayedExpansion
+for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a
+@endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS%
+
+:endReadAdditionalConfig
+
+SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe"
+set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar"
+set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain
+
+set WRAPPER_URL="https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.2.0/maven-wrapper-3.2.0.jar"
+
+FOR /F "usebackq tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO (
+ IF "%%A"=="wrapperUrl" SET WRAPPER_URL=%%B
+)
+
+@REM Extension to allow automatically downloading the maven-wrapper.jar from Maven-central
+@REM This allows using the maven wrapper in projects that prohibit checking in binary data.
+if exist %WRAPPER_JAR% (
+ if "%MVNW_VERBOSE%" == "true" (
+ echo Found %WRAPPER_JAR%
+ )
+) else (
+ if not "%MVNW_REPOURL%" == "" (
+ SET WRAPPER_URL="%MVNW_REPOURL%/org/apache/maven/wrapper/maven-wrapper/3.2.0/maven-wrapper-3.2.0.jar"
+ )
+ if "%MVNW_VERBOSE%" == "true" (
+ echo Couldn't find %WRAPPER_JAR%, downloading it ...
+ echo Downloading from: %WRAPPER_URL%
+ )
+
+ powershell -Command "&{"^
+ "$webclient = new-object System.Net.WebClient;"^
+ "if (-not ([string]::IsNullOrEmpty('%MVNW_USERNAME%') -and [string]::IsNullOrEmpty('%MVNW_PASSWORD%'))) {"^
+ "$webclient.Credentials = new-object System.Net.NetworkCredential('%MVNW_USERNAME%', '%MVNW_PASSWORD%');"^
+ "}"^
+ "[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; $webclient.DownloadFile('%WRAPPER_URL%', '%WRAPPER_JAR%')"^
+ "}"
+ if "%MVNW_VERBOSE%" == "true" (
+ echo Finished downloading %WRAPPER_JAR%
+ )
+)
+@REM End of extension
+
+@REM If specified, validate the SHA-256 sum of the Maven wrapper jar file
+SET WRAPPER_SHA_256_SUM=""
+FOR /F "usebackq tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO (
+ IF "%%A"=="wrapperSha256Sum" SET WRAPPER_SHA_256_SUM=%%B
+)
+IF NOT %WRAPPER_SHA_256_SUM%=="" (
+ powershell -Command "&{"^
+ "$hash = (Get-FileHash \"%WRAPPER_JAR%\" -Algorithm SHA256).Hash.ToLower();"^
+ "If('%WRAPPER_SHA_256_SUM%' -ne $hash){"^
+ " Write-Output 'Error: Failed to validate Maven wrapper SHA-256, your Maven wrapper might be compromised.';"^
+ " Write-Output 'Investigate or delete %WRAPPER_JAR% to attempt a clean download.';"^
+ " Write-Output 'If you updated your Maven version, you need to update the specified wrapperSha256Sum property.';"^
+ " exit 1;"^
+ "}"^
+ "}"
+ if ERRORLEVEL 1 goto error
+)
+
+@REM Provide a "standardized" way to retrieve the CLI args that will
+@REM work with both Windows and non-Windows executions.
+set MAVEN_CMD_LINE_ARGS=%*
+
+%MAVEN_JAVA_EXE% ^
+ %JVM_CONFIG_MAVEN_PROPS% ^
+ %MAVEN_OPTS% ^
+ %MAVEN_DEBUG_OPTS% ^
+ -classpath %WRAPPER_JAR% ^
+ "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" ^
+ %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %*
+if ERRORLEVEL 1 goto error
+goto end
+
+:error
+set ERROR_CODE=1
+
+:end
+@endlocal & set ERROR_CODE=%ERROR_CODE%
+
+if not "%MAVEN_SKIP_RC%"=="" goto skipRcPost
+@REM check for post script, once with legacy .bat ending and once with .cmd ending
+if exist "%USERPROFILE%\mavenrc_post.bat" call "%USERPROFILE%\mavenrc_post.bat"
+if exist "%USERPROFILE%\mavenrc_post.cmd" call "%USERPROFILE%\mavenrc_post.cmd"
+:skipRcPost
+
+@REM pause the script if MAVEN_BATCH_PAUSE is set to 'on'
+if "%MAVEN_BATCH_PAUSE%"=="on" pause
+
+if "%MAVEN_TERMINATE_CMD%"=="on" exit %ERROR_CODE%
+
+cmd /C exit /B %ERROR_CODE%
diff --git a/pmd-ruleset.xml b/pmd-ruleset.xml
new file mode 100644
index 0000000..9fe4fb7
--- /dev/null
+++ b/pmd-ruleset.xml
@@ -0,0 +1,37 @@
+
+
+
+
+
+ Custom ruleset for Java
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/pom.xml b/pom.xml
new file mode 100644
index 0000000..52e53b5
--- /dev/null
+++ b/pom.xml
@@ -0,0 +1,460 @@
+
+
+ 4.0.0
+
+ de.x08
+ praktiKST
+ 1.0-SNAPSHOT
+
+ praktiKST
+
+
+ DO5AMF
+
+
+ http://www.x08.de
+
+ jar
+
+
+
+ ${project.artifactId}
+ ${project.artifactId}
+ ${project.artifactId}
+ kst4contest.view.Kst4ContestApplication
+ 17
+ 3.6.3
+ ${project.artifactId}-${project.version}
+
+
+ 19.0.2.1
+ 24.0.1
+ 5.10.1
+ 1.18.30
+ 5.7.0
+ 3.43.2.2
+
+
+ 3.3.2
+ 3.11.0
+ 3.3.0
+ 3.1.1
+ 3.4.1
+ 3.1.1
+ 3.3.0
+ 3.3.1
+ 4.0.0-M11
+ 3.2.2
+ 3.2.0
+ 1.0.0.RC2
+ 0.1.3
+ 3.21.2
+ 6.55.0
+ 2.16.1
+ 0.0.8
+ 4.8.1.0
+ 4.8.1
+
+
+ UTF-8
+ ${java.version}
+ ${java.version}
+ ${java.version}
+ VERBOSE
+
+
+
+
+
+ org.openjfx
+ javafx-controls
+ ${javafx.version}
+
+
+ org.openjfx
+ javafx-graphics
+ ${javafx.version}
+
+
+ org.openjfx
+ javafx-fxml
+ ${javafx.version}
+
+
+ org.openjfx
+ javafx-web
+ ${javafx.version}
+
+
+ org.openjfx
+ javafx-media
+ ${javafx.version}
+
+
+
+
+ org.xerial
+ sqlite-jdbc
+ ${sqlite.version}
+
+
+
+
+ org.projectlombok
+ lombok
+ ${lombok.version}
+ provided
+
+
+
+
+ org.junit.jupiter
+ junit-jupiter-engine
+ ${junit.version}
+ test
+
+
+ org.junit.jupiter
+ junit-jupiter-params
+ ${junit.version}
+ test
+
+
+
+
+ org.mockito
+ mockito-core
+ ${mockito.version}
+ test
+
+
+ org.mockito
+ mockito-junit-jupiter
+ ${mockito.version}
+ test
+
+
+
+
+ org.jetbrains
+ annotations
+ ${jetbrains.annotations.version}
+ compile
+
+
+
+
+
+
+
+ ${jar.filename}
+
+
+ org.apache.maven.plugins
+ maven-wrapper-plugin
+ ${maven.wrapper.plugin}
+
+
+ org.apache.maven.plugins
+ maven-enforcer-plugin
+ ${maven.enforcer.plugin}
+
+
+ enforce-versions
+
+ enforce
+
+
+
+
+ ${required.maven.version}
+
+
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-compiler-plugin
+ ${maven.compiler.plugin}
+
+ ${java.version}
+
+
+ org.projectlombok
+ lombok
+ ${lombok.version}
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-clean-plugin
+ ${maven.clean.plugin}
+
+
+
+ org.apache.maven.plugins
+ maven-deploy-plugin
+ ${maven.deploy.plugin}
+
+
+
+ org.apache.maven.plugins
+ maven-install-plugin
+ ${maven.install.plugin}
+
+
+
+ org.apache.maven.plugins
+ maven-jar-plugin
+ ${maven.jar.plugin}
+
+
+
+ org.apache.maven.plugins
+ maven-resources-plugin
+ ${maven.resources.plugin}
+
+
+
+ org.apache.maven.plugins
+ maven-site-plugin
+ ${maven.site.plugin}
+
+
+
+ org.apache.maven.plugins
+ maven-surefire-plugin
+ ${maven.surfire.plugin}
+
+ true
+
+
+
+
+ com.github.spotbugs
+ spotbugs-maven-plugin
+ ${spotbugs.maven.plugin}
+
+
+
+ com.github.spotbugs
+ spotbugs
+ ${spotbugs.version}
+
+
+
+
+ prepare-package
+
+
+ spotbugs
+
+
+
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-pmd-plugin
+ ${maven.pmd.version}
+
+
+ net.sourceforge.pmd
+ pmd-core
+ ${pmd.version}
+
+
+ net.sourceforge.pmd
+ pmd-java
+ ${pmd.version}
+
+
+ net.sourceforge.pmd
+ pmd-javascript
+ ${pmd.version}
+
+
+ net.sourceforge.pmd
+ pmd-jsp
+ ${pmd.version}
+
+
+
+ ${project.build.sourceEncoding}
+ 100
+ ${java.version}
+ false
+
+ pmd-ruleset.xml
+
+
+
+
+ prepare-package
+
+
+ pmd
+
+
+
+
+
+
+
+
+
+ org.codehaus.mojo
+ versions-maven-plugin
+ ${codehaus.version.plugin}
+
+
+ validate
+
+ display-dependency-updates
+ display-plugin-updates
+ display-property-updates
+
+
+
+
+
+
+
+ org.openjfx
+ javafx-maven-plugin
+ ${javafx.maven.plugin}
+
+ ${main.class}
+
+
+
+
+
+
+
+
+
+
+ Image
+
+
+ Image
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-dependency-plugin
+ ${maven.dependency.plugin}
+
+
+
+ copy-dependencies
+ package
+
+ copy-dependencies
+
+
+ ${project.build.directory}/modules
+ runtime
+ false
+ false
+ true
+
+
+
+
+
+ copy
+ install
+
+ copy
+
+
+ ${project.build.directory}/modules
+
+
+ ${project.groupId}
+ ${project.artifactId}
+ ${project.version}
+ ${project.packaging}
+ ${project.build.finalName}.jar
+
+
+ true
+
+
+
+
+
+
+ com.github.akman
+ jpackage-maven-plugin
+ ${jpackage.maven.plugin}
+
+ ${appName}
+ IMAGE
+
+
+
+
+ javafx\..*
+
+
+
+
+
+ javafx.controls
+ javafx.graphics
+ javafx.fxml
+ javafx.web
+ java.sql
+
+ ${main.class}
+ ${project.build.directory}/modules
+ ${jar.filename}.jar
+
+
+
+ install
+
+ jpackage
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/main/java/kst4contest/ApplicationConstants.java b/src/main/java/kst4contest/ApplicationConstants.java
new file mode 100644
index 0000000..6fcd828
--- /dev/null
+++ b/src/main/java/kst4contest/ApplicationConstants.java
@@ -0,0 +1,8 @@
+package kst4contest;
+
+public class ApplicationConstants {
+ /**
+ * Name of the Application.
+ */
+ public static final String APPLICATION_NAME = "praktiKST";
+}
diff --git a/src/main/java/kst4contest/controller/AirScoutPeriodicalAPReflectionInquirerTask.java b/src/main/java/kst4contest/controller/AirScoutPeriodicalAPReflectionInquirerTask.java
new file mode 100644
index 0000000..eff506d
--- /dev/null
+++ b/src/main/java/kst4contest/controller/AirScoutPeriodicalAPReflectionInquirerTask.java
@@ -0,0 +1,202 @@
+package kst4contest.controller;
+
+
+import java.io.IOException;
+import java.net.DatagramPacket;
+import java.net.DatagramSocket;
+import java.net.InetAddress;
+import java.net.NoRouteToHostException;
+import java.net.SocketException;
+import java.net.UnknownHostException;
+import java.util.TimerTask;
+
+import javafx.collections.ObservableList;
+import kst4contest.model.ChatMember;
+
+
+public class AirScoutPeriodicalAPReflectionInquirerTask extends TimerTask {
+
+ private ChatController client;
+
+ public AirScoutPeriodicalAPReflectionInquirerTask(ChatController client) {
+
+ this.client = client;
+
+ }
+
+ @Override
+ public void run() {
+
+ Thread.currentThread().setName("AirscoutPeriodicalReflectionInquirierTask");
+
+ //TODO: Manage prefixes kst and as via preferences file and instance
+ //TODO: Check if locator is changeable via the preferences object, need to be correct if it changes
+ DatagramSocket dsocket;
+
+ String prefix_asSetpath ="ASSETPATH: \"KST\" \"AS\" ";
+ String prefix_asWatchList = "ASWATCHLIST: \"KST\" \"AS\" ";
+ String bandString = "1440000";
+ String myCallAndMyLocString = this.client.getChatPreferences().getLoginCallSign() + "," + this.client.getChatPreferences().getLoginLocator();
+ String suffix = ""; //"FOREIGNCALL,FOREIGNLOC " -- dont forget the space at the end!!!
+ String asWatchListString = prefix_asWatchList + bandString + "," + myCallAndMyLocString;
+ String asWatchListStringSuffix = asWatchListString;
+
+ String host = "255.255.255.255";
+ int port = 9872;
+
+// byte[] message = "ASSETPATH: \"KST\" \"AS\" 1440000,DO5AMF,JN49GL,OK1MZM,JN89IW ".getBytes(); Original, ging
+ InetAddress address;
+
+
+ /**
+ * Iterate over chatmemberlist and asking airscout for plane reflection information
+ * To avoid a concurrentmodifyexception, we have to convert the original list to an array at first
+ * since the iterator brakes if the list changing during the iteration time
+ */
+ ObservableList praktiKSTActiveUserList = this.client.getLst_chatMemberList();
+
+
+ ChatMember[] ary_threadSafeChatMemberArray = new ChatMember[praktiKSTActiveUserList.size()];
+ praktiKSTActiveUserList.toArray(ary_threadSafeChatMemberArray);
+
+ for (ChatMember i : ary_threadSafeChatMemberArray) {
+
+ suffix = i.getCallSign() + "," + i.getQra() + " ";
+//
+ String queryStringToAirScout = "";
+
+ queryStringToAirScout += prefix_asSetpath + bandString + "," + myCallAndMyLocString + "," + suffix;
+
+ byte[] queryStringToAirScoutMSG = queryStringToAirScout.getBytes();
+
+ try {
+ address = InetAddress.getByName("255.255.255.255");
+ DatagramPacket packet = new DatagramPacket(queryStringToAirScoutMSG, queryStringToAirScoutMSG.length, address, port);
+ dsocket = new DatagramSocket();
+ dsocket.setBroadcast(true);
+ dsocket.send(packet);
+ dsocket.close();
+ } catch (UnknownHostException e1) {
+ // TODO Auto-generated catch block
+ e1.printStackTrace();
+ } catch (NoRouteToHostException e) {
+ e.printStackTrace();
+ }
+ catch (SocketException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ } catch (IOException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+// System.out.println("[ASUDPTask, info:] sent query " + queryStringToAirScout);
+
+ asWatchListStringSuffix += "," + i.getCallSign() + "," + i.getQra();
+
+ }
+
+
+// for (Iterator iterator = praktiKSTActiveUserList.iterator(); iterator.hasNext();) {
+// ChatMember chatMember = (ChatMember) iterator.next();
+//
+// suffix = chatMember.getCallSign() + "," + chatMember.getQra() + " ";
+//
+// String queryStringToAirScout = "";
+//
+// queryStringToAirScout += prefix_asSetpath + bandString + "," + myCallAndMyLocString + "," + suffix;
+//
+// byte[] queryStringToAirScoutMSG = queryStringToAirScout.getBytes();
+//
+// try {
+// address = InetAddress.getByName("255.255.255.255");
+// DatagramPacket packet = new DatagramPacket(queryStringToAirScoutMSG, queryStringToAirScoutMSG.length, address, port);
+// dsocket = new DatagramSocket();
+// dsocket.setBroadcast(true);
+// dsocket.send(packet);
+// dsocket.close();
+// } catch (UnknownHostException e1) {
+// // TODO Auto-generated catch block
+// e1.printStackTrace();
+// } catch (SocketException e) {
+// // TODO Auto-generated catch block
+// e.printStackTrace();
+// } catch (IOException e) {
+// // TODO Auto-generated catch block
+// e.printStackTrace();
+// }
+//
+//// System.out.println("[ASUDPTask, info:] sent query " + queryStringToAirScout);
+//
+// asWatchListStringSuffix += "," + chatMember.getCallSign() + "," + chatMember.getQra();
+//
+// }}
+
+
+// for (Iterator iterator = praktiKSTActiveUserList.iterator(); iterator.hasNext();) {
+// ChatMember chatMember = (ChatMember) iterator.next();
+//
+// suffix = chatMember.getCallSign() + "," + chatMember.getQra() + " ";
+//
+// String queryStringToAirScout = "";
+//
+// queryStringToAirScout += prefix_asSetpath + bandString + "," + myCallAndMyLocString + "," + suffix;
+//
+// byte[] queryStringToAirScoutMSG = queryStringToAirScout.getBytes();
+//
+// try {
+// address = InetAddress.getByName("255.255.255.255");
+// DatagramPacket packet = new DatagramPacket(queryStringToAirScoutMSG, queryStringToAirScoutMSG.length, address, port);
+// dsocket = new DatagramSocket();
+// dsocket.setBroadcast(true);
+// dsocket.send(packet);
+// dsocket.close();
+// } catch (UnknownHostException e1) {
+// // TODO Auto-generated catch block
+// e1.printStackTrace();
+// } catch (SocketException e) {
+// // TODO Auto-generated catch block
+// e.printStackTrace();
+// } catch (IOException e) {
+// // TODO Auto-generated catch block
+// e.printStackTrace();
+// }
+//
+//// System.out.println("[ASUDPTask, info:] sent query " + queryStringToAirScout);
+//
+// asWatchListStringSuffix += "," + chatMember.getCallSign() + "," + chatMember.getQra();
+//
+// }
+
+ /**
+ * As next we will set the ASWatchlist. All stations in chat will be watched by airscout causing following code.\n\n
+ * ASWATCHLIST: "KST" "AS" 4320000,DO5AMF,JN49GL,DF9QX,JO42HD,DG2KBC,JN58MI,DJ0PY,JO32MF,DL1YDI,JO42FA,DL6BF,JO32QI,F1NZC,JN15MR,F4TXU,JN23CX,F5GHP,IN96LE,F6HTJ,JN12KQ,G0GGG,IO81VE,G0JCC,IO82MA,G0JDL,JO02SI,G0MBL,JO01QH,G4AEP,IO91MB,G4CLA,IO92JL,G4DCV,IO91OF,G4LOH,IO70JC,G4MKF,IO91HJ,G4TRA,IO81WN,G8GXP,IO93FQ,G8VHI,IO92FM,GW0RHC,IO71UN,HA4ND,JN97MJ,I5/HB9SJV/P,JN52JS,IW2DAL,JN45NN,OK1FPR,JO80CE,OK6M,JN99CR,OV3T,JO46CM,OZ2M,JO65FR,PA0V,JO33II,PA2RU,JO32LT,PA3DOL,JO22MT,PA9R,JO22JK,PE1EVX,JO22MP,S51AT,JN75GW,SM7KOJ,JO66ND,SP9TTG,JO90KW�
+ * The watchlist-String is bult by the for loop which builds the AP queries
+ */
+ asWatchListStringSuffix += " ";
+
+ byte[] queryStringToAirScoutMSG = asWatchListStringSuffix.getBytes();
+
+ try {
+ address = InetAddress.getByName("255.255.255.255");
+ DatagramPacket packet = new DatagramPacket(queryStringToAirScoutMSG, queryStringToAirScoutMSG.length, address, port);
+ dsocket = new DatagramSocket();
+ dsocket.setBroadcast(true);
+ dsocket.send(packet);
+ dsocket.close();
+ } catch (UnknownHostException e1) {
+ // TODO Auto-generated catch block
+ e1.printStackTrace();
+ } catch (SocketException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ } catch (IOException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+
+// System.out.println("[ASUDPTask, info:] set watchlist: " + asWatchListStringSuffix);
+
+
+ }
+
+}
diff --git a/src/main/java/kst4contest/controller/AppRunner.java b/src/main/java/kst4contest/controller/AppRunner.java
new file mode 100644
index 0000000..36907e3
--- /dev/null
+++ b/src/main/java/kst4contest/controller/AppRunner.java
@@ -0,0 +1,13 @@
+package kst4contest.controller;
+
+import kst4contest.view.Kst4ContestApplication;
+
+public class AppRunner {
+
+ public static void main(String[] args) {
+
+ Kst4ContestApplication.main(args);
+
+ }
+
+}
diff --git a/src/main/java/kst4contest/controller/BeaconTask.java b/src/main/java/kst4contest/controller/BeaconTask.java
new file mode 100644
index 0000000..8c823f4
--- /dev/null
+++ b/src/main/java/kst4contest/controller/BeaconTask.java
@@ -0,0 +1,63 @@
+package kst4contest.controller;
+
+import java.util.TimerTask;
+
+import kst4contest.model.ChatMessage;
+
+/**
+ * This class is for sending beacons intervalled to the public chat. Gets all
+ * preferences and instances via the Chatpreferences-object of the
+ * Chatcontroller.
+ *
+ * The task will be runned out of the singleton ChatController instance in an
+ * intervall as specified by the Chatpreferences-instance (typically as
+ * configured in the xml file.
+ *
+ *
+ * @author prakt
+ *
+ */
+public class BeaconTask extends TimerTask {
+
+ private ChatController chatController;
+
+ public BeaconTask(ChatController client) {
+
+ this.chatController = client;
+
+ }
+
+ @Override
+ public void run() {
+ Thread.currentThread().setName("BeaconTask");
+
+ ChatMessage beaconMSG = new ChatMessage();
+
+ String replaceVariables = this.chatController.getChatPreferences().getBcn_beaconText();
+// replaceVariables = bcn_beaconText;
+
+ replaceVariables = replaceVariables.replaceAll("MYQRG", this.chatController.getChatPreferences().getMYQRG().getValue());
+ replaceVariables = replaceVariables.replaceAll("MYCALL", this.chatController.getChatPreferences().getLoginCallSign());
+ replaceVariables = replaceVariables.replaceAll("MYLOCATOR", this.chatController.getChatPreferences().getLoginLocator());
+ replaceVariables = replaceVariables.replaceAll("MYQTF", this.chatController.getChatPreferences().getActualQTF().getValue() + "");
+
+
+ beaconMSG.setMessageText(
+ "MSG|" + this.chatController.getChatPreferences().getLoginChatCategory().getCategoryNumber() + "|0|" + replaceVariables + "|0|");
+ beaconMSG.setMessageDirectedToServer(true);
+
+// System.out.println("########### " + replaceVariables);
+
+ if (this.chatController.getChatPreferences().isBcn_beaconsEnabled() ) {
+
+ System.out.println(new Utils4KST().time_generateCurrentMMDDhhmmTimeString()
+ + " [BeaconTask, Info]: Sending CQ: " + beaconMSG.getMessageText());
+ this.chatController.getMessageTXBus().add(beaconMSG);
+ } else {
+ //do nothing, CQ is disabled
+ }
+
+
+ }
+
+}
diff --git a/src/main/java/kst4contest/controller/ChatController.java b/src/main/java/kst4contest/controller/ChatController.java
new file mode 100644
index 0000000..9d70464
--- /dev/null
+++ b/src/main/java/kst4contest/controller/ChatController.java
@@ -0,0 +1,973 @@
+package kst4contest.controller;
+
+import java.net.*;
+import java.sql.SQLException;
+import java.time.OffsetDateTime;
+import java.time.ZoneOffset;
+import java.time.format.DateTimeFormatter;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Timer;
+import java.util.TimerTask;
+import java.util.concurrent.LinkedBlockingQueue;
+
+import javafx.collections.FXCollections;
+import javafx.collections.ObservableList;
+import kst4contest.model.ChatCategory;
+import kst4contest.model.ChatMember;
+import kst4contest.model.ChatMessage;
+import kst4contest.model.ChatPreferences;
+import kst4contest.model.ClusterMessage;
+
+import java.io.*;
+
+/**
+ *
+ * Central Chat kst4contest.controller. Instantiate only one time per category of kst Chat.
+ * Provides complex data types for communication with the gui and drives the
+ * threads of telnet tx, telnet rx and message processing.
+ * SINGLETON
+ *
+ */
+public class ChatController {
+
+ /**
+ * Chat selection ? 50/70 MHz..............1 144/432 MHz............2
+ * Microwave..............3 EME/JT65...............4 Low Band...............5 50
+ * MHz IARU Region 3...6 50 MHz IARU Region 2...7 144/432 MHz IARU R 2...8
+ * 144/432 MHz IARU R 3...9 kHz (2000-630m).......10 Warc (30,17,12m)......11 28
+ * MHz................12 Your choice :
+ *
+ */
+// private int category = ChatCategory.VUHF;
+
+ private ChatPreferences chatPreferences;
+
+ private ChatCategory category;
+ boolean connectedAndLoggedIn;
+ boolean connectedAndNOTLoggedIn;
+ boolean disconnected;
+ boolean disconnectionPerformedByUser = false;
+
+ public boolean isDisconnectionPerformedByUser() {
+ return disconnectionPerformedByUser;
+ }
+
+ public void setDisconnectionPerformedByUser(boolean disconnectionPerformedByUser) {
+ this.disconnectionPerformedByUser = disconnectionPerformedByUser;
+ }
+
+ public String getChatState() {
+ return chatState;
+ }
+
+ public void setChatState(String chatState) {
+ this.chatState = chatState;
+ }
+
+ public boolean isConnectedAndLoggedIn() {
+ return connectedAndLoggedIn;
+ }
+
+ public void setConnectedAndLoggedIn(boolean connectedAndLoggedIn) {
+ this.connectedAndLoggedIn = connectedAndLoggedIn;
+ }
+
+ public boolean isConnectedAndNOTLoggedIn() {
+ return connectedAndNOTLoggedIn;
+ }
+
+ public void setConnectedAndNOTLoggedIn(boolean connectedAndNOTLoggedIn) {
+ this.connectedAndNOTLoggedIn = connectedAndNOTLoggedIn;
+ }
+
+ public boolean isDisconnected() {
+ return disconnected;
+ }
+
+ public void setDisconnected(boolean disconnected) {
+ this.disconnected = disconnected;
+ }
+
+ /**
+ * Handles the disconnect of either the chat (Case DISCONNECTONLY) or the
+ * complete application life including all threads (case CLOSEALL)
+ *
+ * @param action: "CLOSEALL" or "DISCONNECTONLYCHAT", on application close event
+ * (Settings Window closed), Disconnect on Disconnect-Button
+ * clicked (new connection may follow)
+ */
+ public void disconnect(String action) {
+
+ this.setDisconnectionPerformedByUser(true);
+
+ if (action.equals("CLOSEALL")) {
+ this.setDisconnected(true);
+ this.setConnectedAndLoggedIn(false);
+ this.setConnectedAndNOTLoggedIn(false);
+ // disconnect telnet and kill all sockets and connections
+
+ keepAliveTimer.cancel();
+ keepAliveTimer.purge();
+
+ ChatMessage killThreadPoisonPillMsg = new ChatMessage();
+ killThreadPoisonPillMsg.setMessageText("POISONPILL_KILLTHREAD");
+ killThreadPoisonPillMsg.setMessageSenderName("POISONPILL_KILLTHREAD");
+
+ messageRXBus.clear();
+ messageTXBus.clear();
+ messageRXBus.add(killThreadPoisonPillMsg); //kills messageprocessor
+ messageTXBus.add(killThreadPoisonPillMsg); //kills writethread
+
+ writeThread.interrupt();
+
+ readThread.interrupt();
+
+ beaconTimer.purge();
+ beaconTimer.cancel();
+ ASQueryTimer.purge();
+ ASQueryTimer.cancel();
+ socketCheckTimer.purge();
+ socketCheckTimer.cancel();
+
+ userActualizationtimer.purge();
+ userActualizationtimer.cancel();
+
+ userActualizationtimer.purge();
+ userActualizationtimer.cancel();
+
+// consoleReader.interrupt();
+ messageProcessor.interrupt();
+
+ readUDPbyUCXThread.interrupt();
+
+ airScoutUDPReaderThread.interrupt();
+
+ dbHandler.closeDBConnection();
+
+
+ try {
+
+ if (socket != null) {
+ socket.close();
+ }
+
+ } catch (IOException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ } catch (Exception e2) {
+ // TODO Auto-generated catch block
+ e2.printStackTrace();
+ }
+ } else if (action.equals("JUSTDSICCAUSEPWWRONG")){
+
+ this.setDisconnected(true);
+ this.setConnectedAndLoggedIn(false);
+ this.setConnectedAndNOTLoggedIn(true);
+ // disconnect telnet and kill all sockets and connections
+
+ keepAliveTimer.cancel();
+ keepAliveTimer.purge();
+
+ ChatMessage killThreadPoisonPillMsg = new ChatMessage();
+ killThreadPoisonPillMsg.setMessageText("POISONPILL_KILLTHREAD");
+ killThreadPoisonPillMsg.setMessageSenderName("POISONPILL_KILLTHREAD");
+
+ messageRXBus.clear();
+ messageTXBus.clear();
+ messageRXBus.add(killThreadPoisonPillMsg); //kills messageprocessor
+ messageTXBus.add(killThreadPoisonPillMsg); //kills writethread
+
+ writeThread.interrupt();
+
+ readThread.interrupt();
+
+ beaconTimer.purge();
+ beaconTimer.cancel();
+ ASQueryTimer.purge();
+ ASQueryTimer.cancel();
+ socketCheckTimer.purge();
+ socketCheckTimer.cancel();
+
+ userActualizationtimer.purge();
+ userActualizationtimer.cancel();
+
+ userActualizationtimer.purge();
+ userActualizationtimer.cancel();
+
+// consoleReader.interrupt();
+ messageProcessor.interrupt();
+
+ readUDPbyUCXThread.interrupt();
+
+ airScoutUDPReaderThread.interrupt();
+
+ dbHandler.closeDBConnection();
+
+ }
+
+ }
+
+ // private String userName = "DO5AMF";
+// private String password = "uxskezcj";
+ private String userName;
+ private String password;
+ private String showedName;
+ private String qra;
+
+ private String chatState;
+
+ private String hostname = "109.90.0.130";
+ private String praktiKSTVersion = "wtKST 3.1.4.6";
+// private String praktiKSTVersion = "praktiKST 1.0";
+ private String praktiKSTVersionInfo = "2022-10 - 2022-12\ndeveloped by DO5AMF, Marc\nContact: praktimarc@gmail.com\nDonations via paypal are welcome";
+
+ private int port = 23001; // kst4contest.test 4 23001
+ private ReadUDPbyUCXMessageThread readUDPbyUCXThread;
+ private WriteThread writeThread;
+ private ReadThread readThread;
+ private InputReaderThread consoleReader;
+ private ChatMember ownChatMemberObject; // Todo: set at startup
+ private ChatController chatController;
+ private MessageBusManagementThread messageProcessor;
+ private ReadUDPbyAirScoutMessageThread airScoutUDPReaderThread;
+
+ private TimerTask userActualizationTask;
+
+ private TimerTask keepAliveMessageSenderTask;
+
+ private LinkedBlockingQueue messageRXBus; // Queue in which all Chatmessages are buffered, sources are
+ // read- and write-thread
+ private LinkedBlockingQueue messageTXBus; // Queue in which all Chatmessages are buffered, sources are
+ // read- and write-thread
+ private String observedSendThisMessageString;
+
+ private DBController dbHandler;
+
+ private Socket socket;
+
+ private Timer userActualizationtimer;
+
+ private Timer keepAliveTimer;
+
+ private Timer beaconTimer;
+
+ private Timer ASQueryTimer;
+
+ private Timer socketCheckTimer;
+
+ // ******All abstract types below here are used by the messageprocessor!
+ // ***************
+// private Hashtable chatMemberTable = new Hashtable();
+// private HashMap chatMemberTable = new HashMap();
+// private Hashtable dXClusterMemberTable = new Hashtable();
+
+ private ObservableList lst_toAllMessageList = FXCollections.observableArrayList(); // directed to all
+ // (beacon)
+ private ObservableList lst_toMeMessageList = FXCollections.observableArrayList(); // directed to my
+ // call
+ private ObservableList lst_toOtherMessageList = FXCollections.observableArrayList(); // directed to a
+ // call but not
+ // mine
+ private ObservableList chatMemberList = FXCollections.observableArrayList(); // List of active stations
+ // in chat
+ private ObservableList lst_chatMemberList = FXCollections.synchronizedObservableList(chatMemberList); // List
+ // of
+ // active
+ // stations
+ // in
+ // chat
+ private ObservableList lst_clusterMemberList = FXCollections.observableArrayList();
+
+ private ObservableList lst_DBBasedWkdCallSignList = FXCollections.observableArrayList();
+
+// private HashMap map_ucxLogInfoWorkedCalls = new HashMap(); //Destination of ucx-log worked-messages
+
+ // ******************************************************************************************************************************************
+
+ /**
+ * checks if the callsign-String of a given chatmember instance and a given list
+ * instance is in the list. If yes, returns the index in the List, if not,
+ * returns -1.
+ *
+ * @param lookForThis
+ * @return Integer (index), -1 for not found
+ */
+ public int checkListForChatMemberIndexByCallSign(ChatMember lookForThis) {
+
+ if (lookForThis == null) {
+
+// System.out.println("[ChatCtrl] ERROR: null Value for Chatmember detected! Member cannot be in the list!");
+ return -1;
+ } else if (lookForThis.getCallSign() == null) {
+ System.out.println("[ChatCtrl] ERROR: null Value in Callsign detected! Member cannot be in the list!");
+ return -1;
+ }
+
+ for (Iterator iterator = lst_chatMemberList.iterator(); iterator.hasNext();) {
+ ChatMember chatMember = (ChatMember) iterator.next();
+ if (chatMember.getCallSign().equals(lookForThis.getCallSign())) {
+// System.out
+// .println("MSGBUSHELPER: Found " + chatMember.getCallSign() + " at " + lst_chatMemberList.indexOf(chatMember));
+
+ return lst_chatMemberList.indexOf(chatMember);
+ } else {
+
+ }
+ }
+ /**
+ * At this point we know, the callsign is not active in the chat.
+ */
+// System.out
+// .println("[ChatCtrl, ERROR:] ChecklistForChatMemberIndexByCallsign: " + lookForThis.getCallSign() + "\n" + "List: ");
+// for (Iterator iterator = lst_chatMemberList.iterator(); iterator.hasNext();) {
+// ChatMember chatMember = (ChatMember) iterator.next();
+// System.out.println(lst_chatMemberList.indexOf(lookForThis) + ": " + chatMember.getCallSign());
+// }
+
+ return -1;
+
+ }
+
+ public String getHostname() {
+ return hostname;
+ }
+
+ public void setHostname(String hostname) {
+ this.hostname = hostname;
+ }
+
+ public int getPort() {
+ return port;
+ }
+
+ public void setPort(int port) {
+ this.port = port;
+ }
+
+ public Socket getSocket() {
+ return socket;
+ }
+
+ public void setSocket(Socket socket) {
+ this.socket = socket;
+ }
+
+ public void setMessageTXBus(LinkedBlockingQueue messageTXBus) {
+ this.messageTXBus = messageTXBus;
+ }
+
+ public String getPraktiKSTVersion() {
+ return praktiKSTVersion;
+ }
+
+ public void setPraktiKSTVersion(String praktiKSTVersion) {
+ this.praktiKSTVersion = praktiKSTVersion;
+ }
+
+ public String getPraktiKSTVersionInfo() {
+ return praktiKSTVersionInfo;
+ }
+
+ public void setPraktiKSTVersionInfo(String praktiKSTVersionInfo) {
+ this.praktiKSTVersionInfo = praktiKSTVersionInfo;
+ }
+
+// public HashMap getMap_ucxLogInfoWorkedCalls() {
+// return map_ucxLogInfoWorkedCalls;
+// }
+
+// public void setMap_ucxLogInfoWorkedCalls(HashMap map_ucxLogInfoWorkedCalls) {
+// this.map_ucxLogInfoWorkedCalls = map_ucxLogInfoWorkedCalls;
+// }
+
+ public ObservableList getLst_chatMemberList() {
+ return lst_chatMemberList;
+ }
+
+ public ObservableList getLst_DBBasedWkdCallSignList() {
+ return lst_DBBasedWkdCallSignList;
+ }
+
+ public void setLst_DBBasedWkdCallSignList(ObservableList lst_DBBasedWkdCallSignList) {
+ this.lst_DBBasedWkdCallSignList = lst_DBBasedWkdCallSignList;
+ }
+
+ public void setLst_chatMemberList(ObservableList lst_chatMemberList) {
+ this.lst_chatMemberList = lst_chatMemberList;
+ }
+
+ public ObservableList getLst_clusterMemberList() {
+ return lst_clusterMemberList;
+ }
+
+ public void setLst_clusterMemberList(ObservableList lst_clusterMemberList) {
+ this.lst_clusterMemberList = lst_clusterMemberList;
+ }
+
+ public ObservableList getLst_toAllMessageList() {
+ return lst_toAllMessageList;
+ }
+
+ public void setLst_toAllMessageList(ObservableList lst_toAllMessageList) {
+ this.lst_toAllMessageList = lst_toAllMessageList;
+ }
+
+ public ObservableList getLst_toMeMessageList() {
+ return lst_toMeMessageList;
+ }
+
+ public void setLst_toMeMessageList(ObservableList lst_toMeMessageList) {
+ this.lst_toMeMessageList = lst_toMeMessageList;
+ }
+
+ public ObservableList getLst_toOtherMessageList() {
+ return lst_toOtherMessageList;
+ }
+
+ public void setLst_toOtherMessageList(ObservableList lst_toOtherMessageList) {
+ this.lst_toOtherMessageList = lst_toOtherMessageList;
+ }
+
+ public LinkedBlockingQueue getMessageTXBus() {
+ return messageTXBus;
+ }
+
+ public ChatController() {
+ super();
+
+ category = new ChatCategory(2);
+ ownChatMemberObject = new ChatMember();
+ ownChatMemberObject.setCallSign(userName);
+ ownChatMemberObject.setName(showedName);
+ ownChatMemberObject.setQra(qra);
+
+// this.category = ChatCategory.VUHF;
+ this.userName = ownChatMemberObject.getName();
+// this.password = "uxskezcj";
+ this.hostname = "www.on4kst.info";
+ this.port = port;
+ }
+
+ /**
+ * This constructor is used by the Main()-Class of the praktiKST javaFX-gui.
+ *
+ * @param setCategory
+ * @param setOwnChatMemberObject
+ */
+ public ChatController(ChatMember setOwnChatMemberObject) {
+ super();
+
+ chatPreferences = new ChatPreferences();
+ chatPreferences.readPreferencesFromXmlFile(); // set the praktikst Prefs by file or default if file is corrupted
+
+ category = chatPreferences.getLoginChatCategory();
+ this.userName = chatPreferences.getLoginCallSign();
+ this.password = chatPreferences.getLoginPassword();
+// category = setCategory;
+ ownChatMemberObject = setOwnChatMemberObject;
+
+// this.userName = ownChatMemberObject.getName();
+// this.password = ownChatMemberObject.getPassword();
+ this.hostname = "www.on4kst.info";
+ this.port = port;
+
+ }
+
+ public ChatPreferences getChatPreferences() {
+ return chatPreferences;
+ }
+
+ public void setChatPreferences(ChatPreferences chatPreferences) {
+ this.chatPreferences = chatPreferences;
+ }
+
+ public ChatMember getownChatMemberObject() {
+ return ownChatMemberObject;
+ }
+
+ public void setOwnCall(ChatMember ownCall) {
+ this.ownChatMemberObject = ownCall;
+ }
+
+ public LinkedBlockingQueue getMessageRXBus() {
+ return messageRXBus;
+ }
+
+ public void setMessageRXBus(LinkedBlockingQueue messageBus) {
+ this.messageRXBus = messageBus;
+ }
+
+ public WriteThread getWriteThread() {
+ return writeThread;
+ }
+
+ public void setWriteThread(WriteThread writeThread) {
+ this.writeThread = writeThread;
+ }
+
+ public ReadThread getReadThread() {
+ return readThread;
+ }
+
+ public void setReadThread(ReadThread readThread) {
+ this.readThread = readThread;
+ }
+
+ public ChatCategory getCategory() {
+ return category;
+ }
+
+ public void setCategory(ChatCategory category) {
+ this.category = category;
+ }
+
+// public void setChatMemberTable(Hashtable chatMemberTable) {
+// this.chatMemberTable = chatMemberTable;
+// }
+//
+// public void setChatMemberTable(HashMap chatMemberTable) {
+// this.chatMemberTable = chatMemberTable;
+// }
+
+ public DBController getDbHandler() {
+ return dbHandler;
+ }
+
+ public void setDbHandler(DBController dbHandler) {
+ this.dbHandler = dbHandler;
+ }
+
+ public void execute() throws InterruptedException, IOException {
+
+ chatController = this;
+
+ // This block constructs a sample message
+// ChatMessage Test = new ChatMessage();
+// Test.setMessage("kst4contest.test");
+// Test.setMessageDirectedToCommunity(false);
+// Test.setMessageGeneratedTime(new Utils4KST().time_convertEpochToReadable("1664669836"));
+// Test.setMessageSenderName("marc");
+// Test.setMessageText("test2");
+// Test.setSender(ownChatMemberObject);
+// getLst_toAllMessageList().add(Test);
+
+ try {
+ dbHandler = new DBController();
+
+ messageRXBus = new LinkedBlockingQueue();
+ messageTXBus = new LinkedBlockingQueue();
+// messageBus.add("");
+
+ socket = new Socket(hostname, port);
+ System.out.println("Connected to the chat server: " + socket.isConnected());
+
+// consoleReader = new InputReaderThread(this);
+// consoleReader.start();
+
+ readThread = new ReadThread(socket, this);
+ readThread.setName("ReadThread-telnetreader");
+ readThread.start();
+
+ writeThread = new WriteThread(socket, this);
+ writeThread.setName("Writethread-telnetwriter");
+ writeThread.start();
+
+ readUDPbyUCXThread = new ReadUDPbyUCXMessageThread(12060, this);
+ readUDPbyUCXThread.setName("readUDPbyUCXThread");
+ readUDPbyUCXThread.start();
+
+ messageProcessor = new MessageBusManagementThread(this);
+ messageProcessor.setName("messagebusManagementThread");
+ messageProcessor.start();
+
+ airScoutUDPReaderThread = new ReadUDPbyAirScoutMessageThread(9872, this, "AS", "KST");
+ airScoutUDPReaderThread.setName("airscoutudpreaderThread");
+ airScoutUDPReaderThread.start();
+
+ userActualizationtimer = new Timer();
+ userActualizationtimer.schedule(new UserActualizationTask(this), 4000, 60000);// TODO: Temporary
+ // userlistoutput
+ // with
+ // known qrgs
+
+ keepAliveTimer = new Timer();
+ keepAliveTimer.schedule(new keepAliveMessageSenderTask(this), 4000, 60000);// TODO: Temporary
+ // userlistoutput
+ // with
+
+// keepAliveMessageSenderTask = new keepAliveMessageSenderTask(this);
+// keepAliveMessageSenderTask.run();
+
+// userActualizationTask = new UserActualizationTask(this); // kst4contest.test 4 23001
+// userActualizationTask.run();// kst4contest.test 4 23001
+
+ this.setConnectedAndLoggedIn(true);
+
+ /**
+ * The CQ-beacon-Task will be executed every time but checks for itself whether
+ * CQ messages are enabled or not
+ */
+// Timer beaconTimer;
+ beaconTimer = new Timer();
+ beaconTimer.schedule(new BeaconTask(this), 10000,
+ this.getChatPreferences().getBcn_beaconIntervalInMinutes() * 60000);
+ // 60000 * intervalInMinutes = IntervalInMillis
+
+ /**
+ * The AS querier task will be executed every time but checks for itself whether
+ * AS usage is enabled or not
+ */
+// Timer ASQueryTimer;
+ ASQueryTimer = new Timer();
+ ASQueryTimer.schedule(new AirScoutPeriodicalAPReflectionInquirerTask(this), 10000, 5000);
+ // 60000 * intervalInMinutes = IntervalInMillis
+
+ /**
+ * Check if socket works
+ */
+// Timer socketCheckTimer;
+ socketCheckTimer = new Timer();
+ socketCheckTimer.schedule(new TimerTask() {
+
+ @Override
+ public void run() {
+// System.out.println("[Chatcontroller, info: ] periodical socketcheck");
+
+ Thread.currentThread().setName("SocketcheckTimer");
+
+ if (!socket.isConnected() || socket.isClosed()) {
+ try {
+ messageRXBus.clear();
+ messageTXBus.clear();
+ socket.close();
+
+ chatController.setConnectedAndLoggedIn(false);
+ chatController.getLst_chatMemberList().clear();
+
+// messageProcessor.interrupt();
+// chatController.getReadThread().interrupt();
+// chatController.getWriteThread().interrupt();
+
+// keepAliveTimer.wait();
+
+// chatController.getstat
+ System.out.println("[Chatcontroller, Warning: ] Socket closed or disconnected");
+
+ ChatMessage killThreadPoisonPillMsg = new ChatMessage();
+ killThreadPoisonPillMsg.setMessageText("POISONPILL_KILLTHREAD");
+ killThreadPoisonPillMsg.setMessageSenderName("POISONPILL_KILLTHREAD");
+
+ ChatMessage killThreadPoisonPillMsg2 = new ChatMessage();
+ killThreadPoisonPillMsg2.setMessageText("POISONPILL_KILLTHREAD");
+ killThreadPoisonPillMsg2.setMessageSenderName("POISONPILL_KILLTHREAD");
+
+
+ messageRXBus.add(killThreadPoisonPillMsg);
+
+ messageTXBus.add(killThreadPoisonPillMsg2);
+ chatController.getReadThread().interrupt();
+
+
+ } catch (Exception e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+
+ /**
+ * The next block will attempt to reinit the chatclient after accidental
+ * disconnection
+ */
+ try {
+
+ if (!disconnectionPerformedByUser) {
+
+ messageRXBus.clear();
+ messageTXBus.clear();
+
+ socket = new Socket(hostname, port);
+
+// readThread.interrupt();
+
+ chatController.setReadThread(new ReadThread(socket, chatController));
+ chatController.readThread.start();
+
+ chatController.setWriteThread(new WriteThread(socket, chatController));
+ chatController.writeThread.start();
+
+ messageProcessor = new MessageBusManagementThread(chatController);
+ messageProcessor.start();
+
+// chatController.setMessageProcessor= new MessageBusManagementThread(chatController);
+// messageProcessor.start();
+ System.out.println("[Chatcontroller, info: initialized new socket, is connected? ] "
+ + socket.isConnected() + " " + socket.isClosed());
+
+ initialize23001();
+
+ Timer waitABit = new Timer();
+ socketCheckTimer.schedule(new TimerTask() {
+
+ @Override
+ public void run() {
+ Thread.currentThread().setName("waiting");
+
+ if (socket.isConnected()) {
+ chatController.setConnectedAndLoggedIn(true);
+ }
+
+ //just take a breath
+ }
+ }, 5000);
+
+
+// keepAliveTimer.notify();
+
+ }
+
+ } catch (Exception e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+
+ }
+
+ }
+ }, 10000, 10000);
+
+ } catch (UnknownHostException ex) {
+ System.out.println("Server not found: " + ex.getMessage());
+ } catch (IOException ex) {
+ System.out.println("I/O Error: " + ex.getMessage());
+ }
+
+ while (readThread == null) {
+ // do nothing, wait!
+ System.out.println("Reader not ready.");
+ }
+
+// initialize();//kst4contest.test 4 23001
+ initialize23001(); // init Chatcontroller for using port 23001
+
+ }
+
+ public long getCurrentEpochTime() {
+
+ OffsetDateTime currentTimeInUtc = OffsetDateTime.now(ZoneOffset.UTC);
+
+ System.out.println(currentTimeInUtc.format(DateTimeFormatter.ofPattern("yyyy-MM-dd hh:mm X")));
+
+ long millisecondsSinceEpoch = currentTimeInUtc.toInstant().toEpochMilli() / 1000;
+// System.out.println(millisecondsSinceEpoch);
+ return millisecondsSinceEpoch;
+ }
+
+ /**
+ * Setting the initial parameters at the chat via port 23001
+ *
+ * Login parameter format is like that:
+ * LOGINC|do5amf|uxskezcj|2|wtKST 3.1.4.6|25|0|1|1663879818|0|
+ * SDONE|2|
+ *
+ * @throws InterruptedException
+ * @throws IOException
+ */
+ public void initialize23001() throws InterruptedException, IOException {
+
+ messageTXBus.clear();
+
+ ChatMessage message = new ChatMessage();
+
+ new Timer().schedule(new TimerTask() {
+
+ @Override
+ public void run() {
+
+ Thread.currentThread().setName("LoginStringTimer");
+
+ String loginString = "";
+ loginString = "LOGINC|" + chatPreferences.getLoginCallSign() + "|" + chatPreferences.getLoginPassword()
+ + "|" + chatPreferences.getLoginChatCategory().getCategoryNumber() + "|" + praktiKSTVersion
+ + "|25|0|1|" + getCurrentEpochTime() + "|0|";
+
+ // System.out.println(loginString);
+ ChatMessage message = new ChatMessage();
+ message.setMessageText(loginString);
+ message.setMessageDirectedToServer(true);
+ getMessageTXBus().add(message);
+
+ }
+ }, 2000);
+
+ new Timer().schedule(new TimerTask() {
+
+ @Override
+ public void run() {
+
+ Thread.currentThread().setName("SDONEStringTimer");
+ ChatMessage message = new ChatMessage();
+ message.setMessageText("SDONE|" + chatPreferences.getLoginChatCategory().getCategoryNumber() + "|\r");
+ message.setMessageDirectedToServer(true);
+ getMessageTXBus().add(message);
+
+ }
+ }, 3000);
+
+ new Timer().schedule(new TimerTask() {
+
+ @Override
+ public void run() {
+ Thread.currentThread().setName("SETLOCTIMER");
+ ChatMessage message = new ChatMessage();
+ message.setMessageText("MSG|" + chatPreferences.getLoginChatCategory().getCategoryNumber()
+ + "|0|/SETLOC " + chatPreferences.getLoginLocator() + "|0|\r");
+ message.setMessageDirectedToServer(true);
+ getMessageTXBus().add(message);
+
+ }
+ }, 4000);
+
+ new Timer().schedule(new TimerTask() {
+
+ @Override
+ public void run() {
+ Thread.currentThread().setName("SETNAMETIMER");
+ ChatMessage message = new ChatMessage();
+ message.setMessageText("MSG|" + chatPreferences.getLoginChatCategory().getCategoryNumber()
+ + "|0|/SETNAME " + chatPreferences.getLoginName() + "|0|\r");
+ message.setMessageDirectedToServer(true);
+ getMessageTXBus().add(message);
+
+ }
+ }, 5000);
+
+ new Timer().schedule(new TimerTask() {
+ HashMap getWorkedDataFromDb;
+
+ @Override
+ public void run() {
+
+ Thread.currentThread().setName("fetchWorkedFromDBTimer");
+
+ try {
+ getWorkedDataFromDb = dbHandler.fetchChatMemberWkdDataFromDB();
+ } catch (SQLException e) {
+ System.out.println("[Chatctrl, Error: ] got no worked data from DB due to communication error");
+ }
+
+ for (Iterator iterator = getLst_chatMemberList().iterator(); iterator.hasNext();) {
+ ChatMember chatMember = (ChatMember) iterator.next();
+ System.out.println("[Chatctrl]: Marking ChatMembers wkd information: "
+ + getWorkedDataFromDb.get(chatMember.getCallSign()).getCallSign());
+ chatMember.setWorked(getWorkedDataFromDb.get(chatMember.getCallSign()).isWorked());
+ chatMember.setWorked144(getWorkedDataFromDb.get(chatMember.getCallSign()).isWorked144());
+ ;
+ chatMember.setWorked432(getWorkedDataFromDb.get(chatMember.getCallSign()).isWorked432());
+ ;
+ chatMember.setWorked1240(getWorkedDataFromDb.get(chatMember.getCallSign()).isWorked1240());
+ ;
+ chatMember.setWorked2300(getWorkedDataFromDb.get(chatMember.getCallSign()).isWorked2300());
+ ;
+ chatMember.setWorked3400(getWorkedDataFromDb.get(chatMember.getCallSign()).isWorked3400());
+ ;
+ chatMember.setWorked5600(getWorkedDataFromDb.get(chatMember.getCallSign()).isWorked5600());
+ ;
+ chatMember.setWorked10G(getWorkedDataFromDb.get(chatMember.getCallSign()).isWorked10G());
+ ;
+ }
+
+ /**
+ *
+ * This creates the list of the worked stations which had to be displayed in the
+ * settings menu. TODO: May make this List editable
+ *
+ */
+
+ getWorkedDataFromDb.forEach((key, value) -> {
+
+ chatController.getLst_DBBasedWkdCallSignList().add(value);
+
+// System.out.println("Key=" + key + ", Value=" + value);
+ });
+
+// for (Iterator iterator = getWorkedDataFromDb.entrySet().iterator(); iterator.hasNext();) {
+// ChatMember chatMember = (ChatMember) iterator.next();
+// getLst_DBBasedWkdCallSignList().add(chatMember);
+// }
+
+ /* Try the not exceptional way to iterate */
+// for (ChatMember chatMemberAvl : new ArrayList(getLst_chatMemberList())) {
+// if (getWorkedDataFromDb.containsKey(chatMemberAvl.getCallSign())) {
+//
+// }
+// }
+
+ }
+ }, 10000);
+
+// message = new ChatMessage();
+// message.setMessageText("MSG|2|0|/SETNAME " + ownChatMemberObject.getName() + "|0|\r");
+// message.setMessageDirectedToServer(true);
+// this.getMessageTXBus().add(message);
+
+ // message.setMessageText(ownCall.getCallSign());
+// this.getMessageTXBus().add(message);
+
+ }
+
+
+ public void resetWorkedInfoInGuiLists() {
+
+ this.chatController.getLst_chatMemberList().forEach(
+ chatMember -> chatMember.resetWorkedInformationAtAllBands());
+
+ }
+
+ /**
+ * Setting the initial parameters at the chat
+ *
+ * @throws InterruptedException
+ * @throws IOException
+ */
+ public void initialize23000() throws InterruptedException, IOException {
+
+ ChatMessage message = new ChatMessage();
+
+// message.setDirectedToServer(true);
+ message.setMessageText(ownChatMemberObject.getCallSign());
+
+ this.getMessageTXBus().add(message);
+
+ message = new ChatMessage();
+// message.setDirectedToServer(true);
+ message.setMessageText(password);
+ this.getMessageTXBus().add(message);
+//
+ message = new ChatMessage();
+// message.setDirectedToServer(true);
+ message.setMessageText(category + "");
+ this.getMessageTXBus().add(message);
+//
+ message = new ChatMessage();
+// message.setDirectedToServer(true);
+ message.setMessageText("/set qra " + ownChatMemberObject.getQra());
+ this.getMessageTXBus().add(message);
+//
+ message = new ChatMessage();
+// message.setDirectedToServer(true);
+ message.setMessageText("/set name " + ownChatMemberObject.getName());
+ this.getMessageTXBus().add(message);
+//
+ message = new ChatMessage();
+// message.setDirectedToServer(true);
+ message.setMessageText("/set here");
+ this.getMessageTXBus().add(message);
+
+// message = new ChatMessage();
+// message.setDirectedToServer(true);
+// message.setMessageText("/show user");
+// this.getMessageTXBus().add(message);
+ // will done by another Thread
+ }
+
+}
\ No newline at end of file
diff --git a/src/main/java/kst4contest/controller/DBController.java b/src/main/java/kst4contest/controller/DBController.java
new file mode 100644
index 0000000..a348bf6
--- /dev/null
+++ b/src/main/java/kst4contest/controller/DBController.java
@@ -0,0 +1,542 @@
+package kst4contest.controller;
+
+import java.sql.Connection;
+import java.sql.DriverManager;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.sql.Statement;
+import java.util.HashMap;
+
+import kst4contest.ApplicationConstants;
+import kst4contest.model.ChatMember;
+import kst4contest.utils.ApplicationFileUtils;
+
+public class DBController {
+
+ /**
+ * Name of the database file.
+ */
+ public static final String DATABASE_FILE = "praktiKST.db";
+
+ /**
+ * Resource path for the database
+ */
+ public static final String DATABASE_RESOURCE = "/praktiKST.db";
+
+ private static final DBController dbcontroller = new DBController();
+ private static Connection connection;
+// private static final String DB_PATH = System.getProperty("praktiKST.db");
+ private static final String DB_PATH = ApplicationFileUtils.getFilePath(ApplicationConstants.APPLICATION_NAME, DATABASE_FILE);
+
+
+/*
+ static {
+ try {
+ Class.forName("org.sqlite.JDBC");
+ } catch (ClassNotFoundException e) {
+ System.err.println("Fehler beim Laden des JDBC-Treibers");
+ e.printStackTrace();
+ }
+ }
+ */
+
+ public DBController() {
+ initDBConnection();
+ }
+
+ public static DBController getInstance() {
+ return dbcontroller;
+ }
+
+ /**
+ * Closes the db connecttion and all statements
+ */
+ public void closeDBConnection() {
+ try {
+ this.connection.close();
+ } catch (SQLException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+ }
+
+ private void initDBConnection() {
+ try {
+ ApplicationFileUtils.copyResourceIfRequired(
+ ApplicationConstants.APPLICATION_NAME,
+ DATABASE_RESOURCE,
+ DATABASE_FILE
+ );
+ if (connection != null)
+ return;
+ System.out.println("Creating Connection to Database...");
+ connection = DriverManager.getConnection("jdbc:sqlite:" + DB_PATH);
+ if (!connection.isClosed())
+ System.out.println("...Connection established");
+ } catch (SQLException e) {
+ throw new RuntimeException(e);
+ }
+
+ Runtime.getRuntime().addShutdownHook(new Thread() {
+ public void run() {
+ try {
+ if (!connection.isClosed() && connection != null) {
+ connection.close();
+ if (connection.isClosed())
+ System.out.println("Connection to Database closed");
+ }
+ } catch (SQLException e) {
+ e.printStackTrace();
+ }
+ }
+ });
+ }
+
+// private void handleDB() {
+// try {
+// Statement stmt = connection.createStatement();
+// stmt.executeUpdate("DROP TABLE IF EXISTS books;");
+// stmt.executeUpdate("CREATE TABLE books (author, title, publication, pages, price);");
+// stmt.execute(
+// "INSERT INTO books (author, title, publication, pages, price) VALUES ('Paulchen Paule', 'Paul der Penner', "
+// + Date.valueOf("2001-05-06") + ", '1234', '5.67')");
+//
+// PreparedStatement ps = connection.prepareStatement("INSERT INTO books VALUES (?, ?, ?, ?, ?);");
+//
+// ps.setString(1, "Willi Winzig");
+// ps.setString(2, "Willi's Wille");
+// ps.setDate(3, Date.valueOf("2011-05-16"));
+// ps.setInt(4, 432);
+// ps.setDouble(5, 32.95);
+// ps.addBatch();
+//
+// ps.setString(1, "Anton Antonius");
+// ps.setString(2, "Anton's Alarm");
+// ps.setDate(3, Date.valueOf("2009-10-01"));
+// ps.setInt(4, 123);
+// ps.setDouble(5, 98.76);
+// ps.addBatch();
+//
+// connection.setAutoCommit(false);
+// ps.executeBatch();
+// connection.setAutoCommit(true);
+//
+// ResultSet rs = stmt.executeQuery("SELECT * FROM books;");
+// while (rs.next()) {
+// System.out.println("Autor = " + rs.getString("author"));
+// System.out.println("Titel = " + rs.getString("title"));
+// System.out.println("Erscheinungsdatum = " + rs.getDate("publication"));
+// System.out.println("Seiten = " + rs.getInt("pages"));
+// System.out.println("Preis = " + rs.getDouble("price"));
+// }
+// rs.close();
+// connection.close();
+// } catch (SQLException e) {
+// System.err.println("Couldn't handle DB-Query");
+// e.printStackTrace();
+// }
+// }
+
+ /**************************************************************
+ *
+ * Stores a chatmember with its data to the database.
+ * It will not insert a callsign entry, if that exists already but update
+ * locator, name and activity-timer. Callsign is unique and pk!
+ *
+ * Structure is like following
+ *
+ * "callsign" TEXT NOT NULL UNIQUE,
+ * "qra" TEXT,
+ * "name" TEXT,
+ * "lastActivityDateTime" TEXT,
+ * "worked" BOOLEAN,
+ * "worked144" BOOLEAN,
+ * "worked432" BOOLEAN,
+ * "worked1240" BOOLEAN,
+ * "worked2300" BOOLEAN,
+ * "worked3400" BOOLEAN,
+ * "worked5600" BOOLEAN,
+ * "worked10G" BOOLEAN,
+ *
+ * @throws SQLException
+ */
+ public void storeChatMember(ChatMember chatMemberToStore) throws SQLException {
+ try {
+ Statement stmt = connection.createStatement();
+// ResultSet rs = stmt.executeQuery(
+// "SELECT * FROM ChatMember where callsign = '" + chatMemberToStore.getCallSign() + "';");
+
+// if (!rs.next()) {
+
+ PreparedStatement ps = connection.prepareStatement(
+ "INSERT OR IGNORE INTO ChatMember VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) ON CONFLICT(callsign) DO UPDATE SET qra = '"
+ + chatMemberToStore.getQra() + "', name = '" + chatMemberToStore.getName()
+ + "', lastActivityDateTime = '" + chatMemberToStore.getLastActivity()
+ + "' where callsign = '" + chatMemberToStore.getCallSign() + "';");
+
+ ps.setString(1, chatMemberToStore.getCallSign()); // primary key! Not null!
+ ps.setString(2, chatMemberToStore.getQra());
+ ps.setString(3, chatMemberToStore.getName());
+ ps.setString(4, chatMemberToStore.getLastActivity().toString());
+ ps.setInt(5, helper_booleanIntConverter(chatMemberToStore.isWorked()));
+ ps.setInt(6, helper_booleanIntConverter(chatMemberToStore.isWorked144()));
+ ps.setInt(7, helper_booleanIntConverter(chatMemberToStore.isWorked432()));
+ ps.setInt(8, helper_booleanIntConverter(chatMemberToStore.isWorked1240()));
+ ps.setInt(9, helper_booleanIntConverter(chatMemberToStore.isWorked2300()));
+ ps.setInt(10, helper_booleanIntConverter(chatMemberToStore.isWorked3400()));
+ ps.setInt(11, helper_booleanIntConverter(chatMemberToStore.isWorked5600()));
+ ps.setInt(12, helper_booleanIntConverter(chatMemberToStore.isWorked10G()));
+
+ ps.addBatch();
+
+ connection.setAutoCommit(false);
+ ps.executeBatch();
+ connection.setAutoCommit(true);
+ stmt.close();
+// } else {
+
+// System.out.println("DBC: nothing to do");
+ // Will not store the callsign entry in the database, it exists already
+// }
+
+// ResultSet rs = stmt.executeQuery("SELECT * FROM ChatMember;");
+// rs.close();
+// connection.close();
+ } catch (SQLException e) {
+ System.err.println("[DBH, ERROR:] Chatmember could not been stored.");
+ e.printStackTrace();
+ connection.close();
+ }
+ }
+
+ /**************************************************************
+ *
+ * This method does a select and build a hashmap of chatmembers whith all worked
+ * band data, which are stored at the database
+ * Usage: one time after startup, for synching the live list with the stored
+ * list, e.g. after program ended
+ *
+ * Structure is like following
+ *
+ * "callsign" TEXT NOT NULL UNIQUE,
+ * "qra" TEXT,
+ * "name" TEXT,
+ * "lastActivityDateTime" TEXT,
+ * "worked" BOOLEAN,
+ * "worked144" BOOLEAN,
+ * "worked432" BOOLEAN,
+ * "worked1240" BOOLEAN,
+ * "worked2300" BOOLEAN,
+ * "worked3400" BOOLEAN,
+ * "worked5600" BOOLEAN,
+ * "worked10G" BOOLEAN,
+ *
+ * @throws SQLException
+ */
+ public HashMap fetchChatMemberWkdDataFromDB() throws SQLException {
+
+ HashMap fetchedWorkeddata = new HashMap<>();
+
+ try {
+ Statement stmt = connection.createStatement();
+
+ ResultSet rs = stmt.executeQuery("SELECT * FROM ChatMember;");
+
+ ChatMember updateWkdData;
+
+ while (rs.next()) {
+
+ updateWkdData = new ChatMember();
+
+ updateWkdData.setCallSign(rs.getString("callsign"));
+ updateWkdData.setWorked(helper_IntToBooleanConverter(rs.getInt("worked")));
+ updateWkdData.setWorked144(helper_IntToBooleanConverter(rs.getInt("worked144")));
+ updateWkdData.setWorked432(helper_IntToBooleanConverter(rs.getInt("worked432")));
+ updateWkdData.setWorked1240(helper_IntToBooleanConverter(rs.getInt("worked1240")));
+ updateWkdData.setWorked2300(helper_IntToBooleanConverter(rs.getInt("worked2300")));
+ updateWkdData.setWorked3400(helper_IntToBooleanConverter(rs.getInt("worked3400")));
+ updateWkdData.setWorked5600(helper_IntToBooleanConverter(rs.getInt("worked5600")));
+ updateWkdData.setWorked10G(helper_IntToBooleanConverter(rs.getInt("worked10G")));
+
+ fetchedWorkeddata.put(updateWkdData.getCallSign(), updateWkdData);
+
+// System.out.println(
+// "[DBH, Info:] providing callsign wkd info, wkd, 144, 432, ... : " + updateWkdData.toString());
+
+ }
+ stmt.close();
+ rs.close();
+
+ return fetchedWorkeddata;
+
+// connection.close();
+ } catch (SQLException e) {
+ System.err.println("[DBH, ERROR:] Couldn't handle DB-Query");
+ e.printStackTrace();
+ connection.close();
+ }
+ return fetchedWorkeddata; // TODO: what to do if its empty?
+
+ }
+
+ /**************************************************************
+ *
+ * This method does a select and build a hashmap of chatmembers whith all worked
+ * band data, which are stored at the database for restore worked state in the
+ * chat after someone disconnected and reconnected.
+ * Usage: MessagebusManagementThread, every time after a new User connects to
+ * the chat
+ *
+ *
+ * @return a modified version of the chatmember-object, which the method takes
+ *
+ * @throws SQLException
+ */
+ public ChatMember fetchChatMemberWkdDataForOnlyOneCallsignFromDB(ChatMember checkForThis) {
+
+ try {
+ Statement stmt = connection.createStatement();
+
+ ResultSet rs = stmt
+ .executeQuery("SELECT * FROM ChatMember where callsign = '" + checkForThis.getCallSign() + "' ;");
+
+// ChatMember updateWkdData;
+
+// if (!rs.isBeforeFirst()) { //if there are no data to update....
+
+ while (rs.next()) {
+
+// updateWkdData = new ChatMember();
+
+// updateWkdData.setCallSign(rs.getString("callsign"));
+ checkForThis.setWorked(helper_IntToBooleanConverter(rs.getInt("worked")));
+ checkForThis.setWorked144(helper_IntToBooleanConverter(rs.getInt("worked144")));
+ checkForThis.setWorked432(helper_IntToBooleanConverter(rs.getInt("worked432")));
+ checkForThis.setWorked1240(helper_IntToBooleanConverter(rs.getInt("worked1240")));
+ checkForThis.setWorked2300(helper_IntToBooleanConverter(rs.getInt("worked2300")));
+ checkForThis.setWorked3400(helper_IntToBooleanConverter(rs.getInt("worked3400")));
+ checkForThis.setWorked5600(helper_IntToBooleanConverter(rs.getInt("worked5600")));
+ checkForThis.setWorked10G(helper_IntToBooleanConverter(rs.getInt("worked10G")));
+
+ System.out.println(
+ "[DBH, Info:] providing callsign wkd info, wkd, 144, 432, ... for UA5 new chatmember : "
+ + checkForThis.toString());
+
+ }
+// }
+ rs.close();
+ stmt.close();
+
+ return checkForThis;
+
+// connection.close();
+ } catch (SQLException e) {
+ System.err.println("[DBH, ERROR:] Couldn't handle DB-Query");
+ e.printStackTrace();
+
+ try {
+ connection.close();
+ } catch (SQLException e1) {
+ // TODO Auto-generated catch block
+ e1.printStackTrace();
+ }
+ }
+ return checkForThis; // TODO: what to do if its empty?
+
+ }
+
+ /**************************************************************
+ *
+ * This method removes all worked band data for each callsign in the
+ * database.
+ * Usage: User triggered after User clicked the reset-wkd button, may in each
+ * new contest period
+ *
+ *
+ * @return true if reset was successful
+ *
+ * @throws SQLException
+ */
+ public int resetWorkedDataInDB() {
+
+ try {
+ Statement stmt = connection.createStatement();
+
+ int affected = stmt.executeUpdate("update ChatMember set worked = 0, worked144 = 0, worked432 = 0, worked1240 = 0, worked2300 = 0, worked3400 = 0, worked5600 = 0, worked10G = 0;");
+
+ stmt.close();
+
+ return affected;
+
+ } catch (SQLException e) {
+ System.err.println("[DBH, ERROR:] Couldn't reset the worked data");
+ e.printStackTrace();
+
+ try {
+ connection.close();
+ } catch (SQLException e1) {
+ e1.printStackTrace();
+ return -1;
+ }
+ return -1;
+ }
+// return checkForThis; // TODO: what to do if its empty?
+
+ }
+
+ /**************************************************************
+ *
+ * Updates the worked-information for a chatmember at the db
+ * It will not revert wkd info, only add wkd info for a band (only new worked
+ * bands will be stored, no worked info will be removed).
+ * The wkd fields should be cleaned by the user at the begin of a new
+ * contest
+ *
+ * // This will update the worked info on a worked chatmember. The DBHandler
+ * will check, if an entry at the db had been modified. If not, then the worked
+ * station had not been stored yet and have to be stored. The informations have
+ * to be stored, then by calling the store method of the DBHandler (it´s up
+ * to you, guy!)!
+ *
+ *
+ * @return true if an entry had been modified, false if not
+ *
+ *
+ *
+ *
+ * Structure is like following
+ *
+ * "callsign" TEXT NOT NULL UNIQUE,
+ * "qra" TEXT,
+ * "name" TEXT,
+ * "lastActivityDateTime" TEXT,
+ * "worked" BOOLEAN,
+ * "worked144" BOOLEAN,
+ * "worked432" BOOLEAN,
+ * "worked1240" BOOLEAN,
+ * "worked2300" BOOLEAN,
+ * "worked3400" BOOLEAN,
+ * "worked5600" BOOLEAN,
+ * "worked10G" BOOLEAN,
+ *
+ * @throws SQLException
+ */
+ public boolean updateWkdInfoOnChatMember(ChatMember chatMemberToStore) throws SQLException {
+ try {
+ Statement stmt = connection.createStatement();
+// stmt.close();
+
+ /**
+ * at first, mark the station as worked, always
+ */
+ PreparedStatement ps = connection.prepareStatement("UPDATE ChatMember set worked = ? WHERE CallSign = ?");
+
+ ps.setInt(1, 1); // 1st variable will be set
+ ps.setString(2, chatMemberToStore.getCallSign());
+
+ ps.addBatch();
+
+ connection.setAutoCommit(false);
+ ps.executeBatch();
+ connection.setAutoCommit(true);
+
+ /**
+ * Then, handle the update information of received worked udp message
+ */
+
+ String bandVariable = "worked";
+
+ if (chatMemberToStore.isWorked144()) {
+ bandVariable = "worked144";
+ } else if (chatMemberToStore.isWorked432()) {
+ bandVariable = "worked432";
+ } else if (chatMemberToStore.isWorked1240()) {
+ bandVariable = "worked1240";
+ } else if (chatMemberToStore.isWorked2300()) {
+ bandVariable = "worked2300";
+ } else if (chatMemberToStore.isWorked3400()) {
+ bandVariable = "worked3400";
+ } else if (chatMemberToStore.isWorked5600()) {
+ bandVariable = "worked5600";
+ } else if (chatMemberToStore.isWorked10G()) {
+ bandVariable = "worked10G";
+ } else {
+ System.out.println("[DBCtrl, Error]: unknown at which band the qso had been!");
+ }
+
+ PreparedStatement ps2 = connection
+ .prepareStatement("UPDATE ChatMember set " + bandVariable + " = ? WHERE CallSign = ?");
+
+ ps2.setInt(1, 1); // 1st variable will be set
+ ps2.setString(2, chatMemberToStore.getCallSign());
+
+ ps2.addBatch();
+
+ connection.setAutoCommit(false);
+ ps2.executeBatch();
+ connection.setAutoCommit(true);
+
+ stmt.close();
+
+
+ System.out.println("updated count of cols: " + ps2.getUpdateCount());
+ if (ps2.getUpdateCount() != 0) {
+ return true;
+ } else
+ return false; // no entry to update: user had not been stored at a chatmember in table
+// } else {
+
+// System.out.println("DBC: nothing to do");
+ // Will not store the callsign entry in the database, it exists already
+// }
+
+// ResultSet rs = stmt.executeQuery("SELECT * FROM ChatMember;");
+// rs.close();
+// connection.close();
+
+ } catch (SQLException e) {
+ System.err.println("[DBH, ERROR:] Couldn't handle DB-Query");
+ e.printStackTrace();
+ connection.close();
+ return false;
+ }
+ }
+
+ private int helper_booleanIntConverter(boolean convertToInt) {
+
+ if (convertToInt) {
+ return 1;
+ } else
+ return 0;
+
+ }
+
+ private boolean helper_IntToBooleanConverter(int valueFromDBField) {
+
+ if (valueFromDBField != 0) {
+ return true;
+ } else
+ return false;
+
+ }
+
+ public static void main(String[] args) throws SQLException {
+ DBController dbc = DBController.getInstance();
+// dbc.initDBConnection();
+
+ ChatMember dummy = new ChatMember();
+ dummy.setCallSign("DM5M");
+ dummy.setQra("jo51ij");
+ dummy.setName("Team Test");
+ dummy.setLastActivity(new Utils4KST().time_generateActualTimeInDateFormat());
+ dummy.setWorked5600(true);
+// dummy.setWorked432(true);
+
+// dbc.storeChatMember(dummy);
+
+ dbc.updateWkdInfoOnChatMember(dummy);
+
+// dbc.handleDB();
+ }
+}
diff --git a/src/main/java/kst4contest/controller/InputReaderThread.java b/src/main/java/kst4contest/controller/InputReaderThread.java
new file mode 100644
index 0000000..8d35b51
--- /dev/null
+++ b/src/main/java/kst4contest/controller/InputReaderThread.java
@@ -0,0 +1,63 @@
+package kst4contest.controller;
+
+import java.io.*;
+import java.net.*;
+
+import kst4contest.model.ChatMessage;
+
+/**
+ * Consolereader, reads a full line and adds it to the tx-queue after formatting it like:
+ *
+ * MSG|2|0|/cq DO5AMF kst4contest.test|0|
+ *
+ * No need for it as it´s not longer a console application
+ */
+public class InputReaderThread extends Thread {
+ private PrintWriter writer;
+ private Socket socket;
+ private ChatController client;
+
+ public InputReaderThread(ChatController client) throws InterruptedException {
+ this.client = client;
+
+ }
+
+
+ public void run() {
+
+
+
+ while (true) {
+
+ BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
+ ChatMessage ownMSG = new ChatMessage();
+// ownMSG.setDirectedToServer(true);
+
+
+ String sendThisMessage23001 = "";
+
+ try {
+ sendThisMessage23001 = reader.readLine();
+ } catch (IOException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+
+ ownMSG.setMessageText("MSG|" + this.client.getCategory().getCategoryNumber() + "|0|" + sendThisMessage23001 + "|0|");
+
+// System.out.println("inreader " + ownMSG.getMessage() + client.getMessageTXBus().size());
+
+// client.getMessageTXBus().add(ownMSG);
+ client.getMessageTXBus().add(ownMSG);
+
+ try {
+ this.sleep(500);
+ } catch (InterruptedException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+
+ }
+
+ }
+}
diff --git a/src/main/java/kst4contest/controller/MessageBusManagementThread.java b/src/main/java/kst4contest/controller/MessageBusManagementThread.java
new file mode 100644
index 0000000..64c383b
--- /dev/null
+++ b/src/main/java/kst4contest/controller/MessageBusManagementThread.java
@@ -0,0 +1,1195 @@
+package kst4contest.controller;
+
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.sql.SQLException;
+//import java.net.Socket;
+//import java.util.ArrayList;
+import java.util.Hashtable;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import javafx.beans.property.SimpleStringProperty;
+import javafx.collections.ObservableList;
+import kst4contest.model.AirPlaneReflectionInfo;
+import kst4contest.model.ChatMember;
+import kst4contest.model.ChatMessage;
+import kst4contest.model.ClusterMessage;
+
+/**
+ *
+ * This thread is responsible for processing rx and tx messages, synchronize tx
+ * and rx and saving the whole chat content in a textfile and write the text to
+ * the stdio.
+ *
+ */
+public class MessageBusManagementThread extends Thread {
+
+ int index;
+
+ private PrintWriter writer;
+// private Socket socket;
+ private ChatController client;
+// private File fileLogRAW;
+// private TimerTask userActualizationTask; // Is used as a temporary userout-print
+// private TimerTask userActualizationTask; //kst4contest.test 4 23001
+ // private boolean serverReady = false; //kst4contest.test 4 23001
+ private boolean serverReady = true;
+ private Hashtable chatMemberTable;
+ private final String PTRN_USERLISTENTRY = "([a-zA-Z0-9]{2}/{1})?([a-zA-Z0-9]{1,3}[0-9][a-zA-Z0-9]{0,3}[a-zA-Z]{0,3})(/p)? [a-zA-Z]{2}[0-9]{2}[a-zA-Z]{2} [ -~]{1,20}";
+ private final String PTRN_QRG_CAT2 = "(([0-9]{3,4}[\\.|,| ]?[0-9]{3})([\\.|,][\\d]{1,2})?)|(([a-zA-Z][0-4]{1}[\\d]{2}\\b)([\\.|,][\\d]{1,2}\\b)?)|((\\b[0-4]{1}[\\d]{2}\\b)([\\.|,][\\d]{1,2}\\b)?)";
+// BufferedWriter bufwrtrDBGMSGOut;
+
+// private String text;
+
+// public BufferedWriter getBufwrtrDBGMSGOut() {
+// return bufwrtrDBGMSGOut;
+// }
+//
+// public void setBufwrtrDBGMSGOut(BufferedWriter bufwrtrDBGMSGOut) {
+// this.bufwrtrDBGMSGOut = bufwrtrDBGMSGOut;
+// }
+
+ public boolean isServerready() {
+ return serverReady;
+ }
+
+ public void setServerready(boolean serverReady) {
+ this.serverReady = serverReady;
+ }
+
+ public MessageBusManagementThread(ChatController client) {
+
+ this.client = client;
+
+ }
+
+ /**
+ * check if a chatmessage is part of the userlist via telnet 23000 port
+ * Updates userlist!
+ *
+ * @param chatMessage
+ */
+ private void checkIfItsUserListEntry(ChatMessage messageToProcess) {
+
+ Pattern pattern = Pattern.compile(PTRN_USERLISTENTRY);
+ Matcher matcher = pattern.matcher(messageToProcess.getMessageText());
+ /**
+ * "([a-zA-Z0-9]{1,2}\/)?[a-zA-Z0-9]{1,3}[0-9][a-zA-Z0-9]{0,3}[a-zA-Z](\/(p|m))?(
+ * )[a-zA-Z]{2}[0-9]{2}[a-zA-Z]{2}[ -~]{0,30}" Thats a line of the show users
+ * list
+ */
+
+ while (matcher.find()) {
+// System.out.println("Chatmember detected: "+ matcher.group() + " " + matcher.start());
+
+ ChatMember member = new ChatMember();
+ String matchedString = matcher.group();
+
+ String[] splittedUserString;
+ splittedUserString = matchedString.split(" ");
+
+ member.setCallSign(splittedUserString[0]);
+ member.setQra(splittedUserString[1]);
+
+ String stringAggregation = "";
+ for (int i = 2; i < splittedUserString.length; i++) {
+ stringAggregation += splittedUserString[i] + " ";
+ }
+ member.setName(stringAggregation);
+
+// this.client.getChatMemberTable().put(member.getCallSign(), member);//deleted cause change if list type
+
+// if (member.getName().)
+
+// System.out.println("Processed Userlist Entry [" + this.client.getChatMemberTable().size() + "]: Call: "
+// + member.getCallSign() + ", QRA: " + member.getQra() + ", Name: " + member.getName());
+ }
+ }
+
+ /**
+ * check if a chatmessage is part of the userlist via telnet 23000 port
+ * Updates userlist!
+ *
+ * @param chatMessage
+ */
+ private void checkIfItsUserListEntry23001(ChatMessage messageToProcess) {
+
+ Pattern pattern = Pattern.compile(PTRN_USERLISTENTRY);
+ Matcher matcher = pattern.matcher(messageToProcess.getMessageText());
+ /**
+ * "([a-zA-Z0-9]{1,2}\/)?[a-zA-Z0-9]{1,3}[0-9][a-zA-Z0-9]{0,3}[a-zA-Z](\/(p|m))?(
+ * )[a-zA-Z]{2}[0-9]{2}[a-zA-Z]{2}[ -~]{0,30}" Thats a line of the show users
+ * list
+ */
+
+ while (matcher.find()) {
+// System.out.println("Chatmember detected: "+ matcher.group() + " " + matcher.start());
+
+ ChatMember member = new ChatMember();
+ String matchedString = matcher.group();
+
+ String[] splittedUserString;
+ splittedUserString = matchedString.split(" ");
+
+ member.setCallSign(splittedUserString[0]);
+ member.setQra(splittedUserString[1]);
+
+ String stringAggregation = "";
+ for (int i = 2; i < splittedUserString.length; i++) {
+ stringAggregation += splittedUserString[i] + " ";
+ }
+ member.setName(stringAggregation);
+
+// this.client.getChatMemberTable().put(member.getCallSign(), member);
+
+// if (member.getName().)
+
+// System.out.println("[MSGBUSMGT:] Processed Userlist Entry [" + this.client.getChatMemberTable().size()
+// + "]: Call: " + member.getCallSign() + ", QRA: " + member.getQra() + ", Name: " + member.getName());
+ }
+ }
+
+ /**
+ * check if a chatmessage or a name of a chatmember contains a frequency
+ * returns String = "" if no frequency found
+ *
+ * @param chatMessage
+ */
+ private String checkIfMessageInhibitsFrequency(ChatMessage messageToProcess) {
+
+ Pattern pattern = Pattern.compile(PTRN_QRG_CAT2); // TODO: PTRN should depend to category-selection of own stn
+ Matcher matcher = pattern.matcher(messageToProcess.getMessageText());
+ String[] splittedQRGString;
+// splittedQRGString[0] = "0";
+
+ String stringAggregation = "";
+
+// if (matcher.) {
+// stringAggregation = ""; //reset aggregated string
+// }
+
+ while (matcher.find()) {
+// System.out.println("QRG detected: "+ matcher.group() + " " + matcher.start());
+
+// ChatMember member = new ChatMember();
+ String matchedString = matcher.group();
+
+// splittedQRGString = new String[0];
+ splittedQRGString = matchedString.split(" ");
+
+ for (int i = 0; i < splittedQRGString.length; i++) {
+ stringAggregation += splittedQRGString[i] + " ";
+ }
+
+ System.out.println("[MSGBUSMGT:] Processed qrg info: " + stringAggregation);
+
+// if (member.getName().)
+
+// System.out.println("Processed QRG Entry [" + this.client.getChatMemberTable().size() + "]: Call: "
+// + member.getCallSign() + ", QRA: " + member.getQra() + ", Name: " + member.getName());
+ }
+ return stringAggregation;
+ }
+
+ /**
+ * Builds UserList and gets meta informations out of the chat, as far as it is
+ * possible. \n This is the only place where the Chatmember-List will be written
+ *
+ * @param messageToProcess
+ */
+ private void processRXMessage23000(ChatMessage messageToProcess) {
+
+ String reduce;
+
+ reduce = new String(messageToProcess.getMessageText());
+ reduce = reduce.replaceAll("\\s+", " "); // reduce bursts of spaces to one space sign
+
+ messageToProcess.setMessageText(reduce);
+
+ if (messageToProcess.getMessageText().isEmpty()) {
+ System.out.println("[MSGBUSMGTT:] ######################no processable data");
+ } else {
+
+ if (reduce.length() >= 14 && reduce.length() <= 40) {
+ checkIfItsUserListEntry(messageToProcess); // 23001 kst4contest.test unneccessary
+ }
+
+ checkIfMessageInhibitsFrequency(messageToProcess);
+ }
+ }
+
+// private boolean isUserInTheUserTable(String chatMemberCallsign) {
+//
+// String checkThisCallsign = chatMemberCallsign;
+//
+// if (this.client.getChatMemberTable().containsKey(checkThisCallsign)) {
+// return true;
+// } else
+// return false;
+//
+// }
+
+ /**
+ * checks if the callsign-String of a given chatmember instance and a given list
+ * instance is in the list. If yes, returns the index in the List, if not,
+ * returns -1.
+ *
+ * @param lookForThis
+ * @return Integer (index), -1 for not found
+ */
+ private int checkListForChatMemberIndexByCallSign(ObservableList list, ChatMember lookForThis) {
+
+ if (lookForThis == null) {
+
+ System.out.println(
+ "[ChecklistForChatMemberIndexByCallsign] ERROR: null Value for Chatmember detected! Member cannot be in the list!");
+ return -1;
+ } else if (lookForThis.getCallSign() == null) {
+ System.out.println(
+ "[ChecklistForChatMemberIndexByCallsign] ERROR: null Value in Callsign detected! Member cannot be in the list!");
+ return -1;
+ }
+ /***
+ * Old mechanic for index search, new one implemented due concurrentmodificationexc, which works - start
+ *
+ */
+// for (Iterator iterator = list.iterator(); iterator.hasNext();) {
+// ChatMember chatMember = (ChatMember) iterator.next();
+// if (chatMember.getCallSign().equals(lookForThis.getCallSign())) {
+//// System.out
+//// .println("MSGBUSHELBER: Found " + chatMember.getCallSign() + " at " + list.indexOf(chatMember));
+//
+// return list.indexOf(chatMember);
+// } else {
+//
+// }
+// }
+//
+// System.out.println("[MsgBusMgr, ERROR:] ChecklistForChatMemberIndexByCallsign, not found: "
+// + lookForThis.getCallSign() + "\n ");
+ /***
+ * Old mechanic for index search,new one implemented due concurrentmodificationexcm which works - end
+ *
+ */
+
+ for (int i = 0; i < list.size(); i++) {
+ if (list.get(i).getCallSign().equals(lookForThis.getCallSign())) {
+// System.out
+// .println("MSGBUSHELBER: Found " + chatMember.getCallSign() + " at " + list.indexOf(chatMember));
+
+ return list.indexOf(list.get(i));
+ }
+ }
+
+// for (Iterator iterator = list.iterator(); iterator.hasNext();) {
+// ChatMember chatMember = (ChatMember) iterator.next();
+// System.out.println(list.indexOf(lookForThis) + ": " + chatMember.getCallSign());
+// }
+
+ return -1; // if it´s not found, the method will always end here and return -1
+
+ }
+
+ /**
+ * Processes received messages via port 23001 (improved telnet Interface)
+ *
+ * @param messageToProcess
+ * @throws IOException
+ * @throws SQLException
+ */
+ private void processRXMessage23001(ChatMessage messageToProcess) throws IOException, SQLException {
+
+ final String INITIALUSERLISTENTRY = "UA0";
+ final String USERENTEREDCHAT = "UA5";
+ final String USERENTEREDCHAT2 = "UA2"; // seen at 50MHZ Chat
+ final String initialChatHistoryEntry = "CR";
+ final String SERVERMESSAGE = "CR";
+ final String USERLEFTCHAT = "UR6";
+ final String USERLEFTCHAT2 = "UR7";
+ final String CHATCHANNELMESSAGE = "CH";
+ final String REGISTREDUSERCOUNT = "UE";
+ final String USERSTATECHANGE = "US4";
+ final String USERLOCATORCHANGE = "LOC";
+ final String USERINFOUPDATEORUSERISBACK = "UM3";
+ final String DXCLUSTERMESSAGE1 = "DM";
+ final String DXCLUSTERMESSAGE2 = "DL";
+ final String DXCLUSTERMESSAGE3 = "MA";
+ final String SRVR_DXCEND = "DF";
+ final String SRVR_USERLISTEND = "UE";
+ final String SRVR_COMMUNICATIONK = "CK";
+ final String SRVR_LOGSTAT = "LOGSTAT";
+ final String SRVR_LOGSTAT_WRONGPASSWORD = "Wrong password!";
+ final String SRVR_LOGINOK = "100";
+ final String SRVR_LOGINWRONGPW = "114";
+ final String SRVR_LOGINWRONGEMPTYCALL = "102";
+ final String SRVR_LOGINWRONGCALLSYNTAX = "103";
+ final String SRVR_LOGINWRONGCALLUNKNOWN = "101";
+
+ if (messageToProcess.getMessageText().isEmpty()) {
+ System.out.println("[MSGBUSMGTT:] ######################no processable data");
+
+ } else {
+
+ if (messageToProcess.getMessageText().contains(SRVR_LOGSTAT)) {
+ String logstatMessage[];
+ logstatMessage = messageToProcess.getMessageText().split("\\|");
+ if (logstatMessage[1].contains(SRVR_LOGINOK)) {
+ this.client.setConnectedAndLoggedIn(true);
+ } else {
+ this.client.setConnectedAndNOTLoggedIn(true);
+ this.client.setConnectedAndLoggedIn(false);
+ }
+ }
+
+ String splittedMessageLine[] = messageToProcess.getMessageText().split("\\|");
+
+ /**
+ * Initializes the Userlist if entry fits UA0
+ */
+ if (splittedMessageLine[0].contains(INITIALUSERLISTENTRY)) {
+// System.out.println("MSGBUS: User detected");
+
+ ChatMember newMember = new ChatMember();
+
+ newMember.setAirPlaneReflectInfo(new AirPlaneReflectionInfo()); // TODO: Only bugfix, check
+
+ newMember.setCallSign(splittedMessageLine[2]);
+ newMember.setName(splittedMessageLine[3]);
+ newMember.setQra(splittedMessageLine[4]);
+ newMember.setState(Integer.parseInt(splittedMessageLine[5]));
+ newMember.setLastActivity(new Utils4KST().time_generateActualTimeInDateFormat());
+
+// this.client.getChatMemberTable().put(splittedMessageLine[2], newMember); //TODO: map -> List
+
+ this.client.getLst_chatMemberList().add(newMember);
+
+ this.client.getDbHandler().storeChatMember(newMember);
+
+// bufwrtrDBGMSGOut.write(new Utils4KST().time_generateCurrentMMDDhhmmTimeString()
+// + "[MSGBUSMGT:] User detected and added to list [" + this.client.getChatMemberTable().size()
+// + "] :" + newMember.getCallSign() + "\n");
+// bufwrtrDBGMSGOut.flush();
+// System.out.println("[MSGBUSMGT:] User detected and added to list ["
+// + this.client.getChatMemberTable().size() + "] :" + newMember.getCallSign());
+ } else
+
+ /**
+ * Actualize Userlist, add new entry UA5 or UA2
+ */
+ if (splittedMessageLine[0].contains(USERENTEREDCHAT) || splittedMessageLine[0].contains(USERENTEREDCHAT2)) {
+// System.out.println("MSGBUS: User detected");
+
+ ChatMember newMember = new ChatMember();
+
+ newMember.setAirPlaneReflectInfo(new AirPlaneReflectionInfo());
+ newMember.setCallSign(splittedMessageLine[2]);
+ newMember.setName(splittedMessageLine[3]);
+ newMember.setQra(splittedMessageLine[4]);
+ newMember.setState(Integer.parseInt(splittedMessageLine[5]));
+ newMember.setLastActivity(new Utils4KST().time_generateActualTimeInDateFormat());
+
+ newMember = this.client.getDbHandler().fetchChatMemberWkdDataForOnlyOneCallsignFromDB(newMember);
+
+ this.client.getLst_chatMemberList().add(newMember);
+
+ this.client.getDbHandler().storeChatMember(newMember);
+
+// this.client.getChatMemberTable().put(splittedMessageLine[2], newMember);
+
+// System.out.println("[MSGBUSMGT:] New entered User detected and added to list ["
+// + this.client.getChatMemberTable().size() + "] :" + newMember.getCallSign());
+ } else
+
+ /**
+ * Actualize Userlist, remove entry UR6, UR7
+ */
+ if (splittedMessageLine[0].contains(USERLEFTCHAT) || splittedMessageLine[0].contains(USERLEFTCHAT2)) {
+// System.out.println("MSGBUS: User detected");
+
+ ChatMember newMember = new ChatMember();
+
+ newMember.setCallSign(splittedMessageLine[2]);
+
+// this.client.getChatMemberTable().remove(newMember.getCallSign());
+
+ System.out.println("[MSGBUSMGT, Info:] User left Chat and will be removed from list ["
+ + this.client.getLst_chatMemberList().size() + "] :" + newMember.getCallSign());
+ try {
+ this.client.getLst_chatMemberList().remove(
+ checkListForChatMemberIndexByCallSign(this.client.getLst_chatMemberList(), newMember));
+
+ } catch (Exception e) {
+ System.out.println("[MSGBUSMGT, EXC!, Error:] User sent left chat but had not been there ... ["
+ + this.client.getLst_chatMemberList().size() + "] :" + newMember.getCallSign() + "\n"
+ + e.getStackTrace());
+// e.printStackTrace();
+ }
+
+// int indexToDelete = checkListForChatMemberIndexByCallSign(this.client.getLst_chatMemberList(),
+// newMember);
+// if (indexToDelete != -1) {
+// System.out.println("[MSGBUSMGT:] User left Chat and is removed from list ["
+// + this.client.getLst_chatMemberList().size() + "] :" + newMember.getCallSign());
+//
+// this.client.getLst_chatMemberList().remove(indexToDelete);
+//
+// } else {
+// System.out.println("[MSGBUSMGT:] Error, user sent left chat but had not been there ... ["
+// + this.client.getLst_chatMemberList().size() + "] :" + newMember.getCallSign());
+//
+// }
+
+ } else
+
+ /**
+ * Chatmessage dm5m to do5amf CH|2|1663966534|DM5M|dm5m-team|0|kst4contest.test|DO5AMF|
+ *
+ * CH|2|1663966535|DM5M|dm5m-team|0|kst4contest.test|0|
+ */
+ if (splittedMessageLine[0].contains(CHATCHANNELMESSAGE)) {
+// System.out.println("MSGBUS: User detected");
+
+ ChatMessage newMessage = new ChatMessage();
+ newMessage.setChatCategory(this.client.getCategory());
+ newMessage.setMessageGeneratedTime(splittedMessageLine[2]); // TODO: insert readable time?
+
+ if (splittedMessageLine[3].equals("SERVER")) {
+ ChatMember dummy = new ChatMember();
+ dummy.setCallSign("SERVER");
+ dummy.setName("Sysop");
+ newMessage.setSender(dummy);
+ } else {
+
+ ChatMember temp = new ChatMember();
+ temp.setCallSign(splittedMessageLine[3]);
+
+ int index = checkListForChatMemberIndexByCallSign(this.client.getLst_chatMemberList(), temp);
+
+ if (index != -1) {
+
+ newMessage.setSender(this.client.getLst_chatMemberList().get(index)); // set sender to member of
+ // b4 init list
+ } else {
+
+ if (!temp.getCallSign().equals(this.client.getChatPreferences().getLoginCallSign().toUpperCase())) {
+ temp.setCallSign("[n/a]" + temp.getCallSign());
+ }
+
+ newMessage.setSender(temp); // if someone sent a message without being in the userlist (cause
+ // on4kst missed implementing....)
+ }
+
+// newMessage.setSender(this.client.getChatMemberTable().get(splittedMessageLine[3]));
+ }
+
+ newMessage.setMessageSenderName(splittedMessageLine[4]);
+ newMessage.setMessageText(splittedMessageLine[6]);
+
+ if (splittedMessageLine[7].equals("0")) {
+ // message is not directed to anyone
+ ChatMember dummy = new ChatMember();
+ dummy.setCallSign("ALL");
+ newMessage.setReceiver(dummy);
+
+ this.client.getLst_toAllMessageList().add(0, newMessage); // sdtout to all message-List
+
+ } else {
+
+ ChatMember temp2 = new ChatMember();
+ temp2.setCallSign(splittedMessageLine[7]);
+
+ int index = checkListForChatMemberIndexByCallSign(this.client.getLst_chatMemberList(), temp2);
+
+ if (index != -1) {
+ newMessage.setReceiver(this.client.getLst_chatMemberList().get(index));// -1: Member left Chat
+ // before...
+ } else {
+ temp2.setCallSign(temp2.getCallSign() + "(left)");
+ newMessage.setReceiver(temp2);
+ }
+
+ System.out.println("message directed to: " + newMessage.getReceiver().getCallSign() + ". EQ?: " + this.client.getownChatMemberObject().getCallSign() + " sent by: " + newMessage.getSender().getCallSign().toUpperCase() + " -> EQ?: "+ this.client.getChatPreferences().getLoginCallSign().toUpperCase());
+
+ if (newMessage.getReceiver().getCallSign()
+ .equals(this.client.getChatPreferences().getLoginCallSign())) {
+
+ this.client.getLst_toMeMessageList().add(0, newMessage);
+
+ System.out.println("message directed to me: " + newMessage.getReceiver().getCallSign() + ".");
+
+ } else if (newMessage.getSender().getCallSign().toUpperCase() // if you sent the message, it will be sorted in to
+ // the "to you message list"
+ .equals(this.client.getChatPreferences().getLoginCallSign().toUpperCase())) {
+ String originalMessage = newMessage.getMessageText();
+ newMessage
+ .setMessageText("(>" + newMessage.getReceiver().getCallSign() + ")" + originalMessage);
+ this.client.getLst_toMeMessageList().add(0, newMessage); // TODO:check
+
+ } else {
+ this.client.getLst_toOtherMessageList().add(0, newMessage);
+
+// System.out.println("MSGBS bgfx: tx call = " + newMessage.getSender().getCallSign() + " / rx call = " + newMessage.getReceiver().getCallSign());
+ }
+
+ // sdtout to me message-List
+
+// newMessage.setReceiver(this.client.getChatMemberTable().get(splittedMessageLine[7])); // set sender
+ // to the
+ // member of
+ // before
+ // initialized
+ // list
+ }
+
+// System.out.println("[MSGBUSMGT:] processed message: " + newMessage.getChatCategory().getCategoryNumber()
+// + " " + newMessage.getSender().getCallSign() + ", " + newMessage.getMessageSenderName() + " -> "
+// + newMessage.getReceiver().getCallSign() + ": " + newMessage.getMessageText());
+
+ String locatedFrequencies = checkIfMessageInhibitsFrequency(newMessage);
+
+ SimpleStringProperty qrg = new SimpleStringProperty(locatedFrequencies);
+
+ if (!splittedMessageLine[3].equals("SERVER")) {
+
+ if (locatedFrequencies.equals("")) {
+ // no qrg found, nothing to do
+ } else {
+
+// String stringAggregation = "";
+//
+// for (int i = 0; i < locatedFrequencies.length; i++) {
+// stringAggregation += locatedFrequencies[i] + " ";
+// }
+
+ ChatMember temp3 = new ChatMember();
+ temp3.setCallSign(splittedMessageLine[3]);
+ int index = checkListForChatMemberIndexByCallSign(this.client.getLst_chatMemberList(), temp3);
+
+ if (index == -1) { // user is not in the userlist but sent message...
+
+ /**
+ * CH|2|1664663240|IK7LMX|Gilberto QRO|0|pse ant to jn80|YT5W| Caused this line
+ */
+ System.out.println("[MSGBUSMGT:] ERROR, Frequency for " + splittedMessageLine[3]
+ + " is not settable, Callsign is not in the Member-list!");
+
+ ChatMember newMember = new ChatMember();
+ newMember.setCallSign(splittedMessageLine[3]);
+ newMember.setName(splittedMessageLine[4]);
+ newMember.setFrequency(qrg);
+// newMember.setFrequency(locatedFrequencies);
+// this.client.getLst_chatMemberList().add(newMember);
+
+ } else {
+ /**
+ * User is in the list...
+ *
+ */
+ this.client.getLst_chatMemberList().get(index).setFrequency(qrg);
+ System.out.println("[MSGBUSMGT:] Frequency for " + splittedMessageLine[3] + " setted: "
+ + locatedFrequencies);
+
+// this.client.getLst_chatMemberList().
+
+// ChatMember dummy = new ChatMember();
+//
+// dummy.setAirPlaneReflectInfo(new AirPlaneReflectionInfo()); //TODO: check if this is neccessary
+// this.client.getLst_chatMemberList().add(dummy); // TODO: Bugfix for UI actualization, maybe we dont need that any more
+// this.client.getLst_chatMemberList().remove(dummy);
+// this.client.getLst_chatMemberList().sorted();
+
+//
+ }
+ }
+ }
+
+ // TODO: Next: get frequency infos out of name?
+ } else
+
+ /**
+ * LOC|1664012560|I4GHG/6|JN63DT| Actualize singleton Userlist, changes locator
+ * of existing user or add him with this locator
+ */
+ if (splittedMessageLine[0].contains(USERLOCATORCHANGE)) {
+// System.out.println("MSGBUS: User detected");
+
+ ChatMember temp4 = new ChatMember();
+ temp4.setCallSign(splittedMessageLine[2]);
+ temp4.setQra(splittedMessageLine[3]);
+ temp4.setLastActivity(new Utils4KST().time_generateActualTimeInDateFormat());
+
+ int index = checkListForChatMemberIndexByCallSign(this.client.getLst_chatMemberList(), temp4);
+
+ if (index != -1) {
+
+ System.out.println("[MSGBUSMGT:] Locator Change of [" + (splittedMessageLine[2] + "], old was: "
+ + this.client.getLst_chatMemberList().get(index).getQra() + " new is: "
+ + splittedMessageLine[3]));
+
+ this.client.getLst_chatMemberList().get(index).setQra(splittedMessageLine[3]);
+
+ } else {
+ System.out.println("[MSGBUSMGT:] ERROR! Locator Change of ["
+ + (splittedMessageLine[2] + "] is not possible, user is not in the Table!"));
+
+// ChatMember newMember = new ChatMember();
+// newMember.setCallSign(splittedMessageLine[2]);
+// newMember.setQra(splittedMessageLine[3]);
+// this.client.getChatMemberTable().put(newMember.getCallSign(), newMember);
+
+// this.client.getLst_chatMemberList().add(temp4);
+ }
+
+ this.client.getDbHandler().storeChatMember(temp4); // TODO thats a bit unclean, its less an insert but a
+ // locator update
+
+ } else
+
+ /**
+ * DX-Cluster-Message type 1
+ * DM|0|1664050013|2006|w4cwf|144118.0|PA2CHR|EM85WH<>JO22 hrd
+ * -21db|EM85WH|JO32DB|
+ */
+ if (splittedMessageLine[0].contains(DXCLUSTERMESSAGE1)) {
+// System.out.println("MSGBUS: User detected");
+
+ ClusterMessage dxcMsg = new ClusterMessage();
+
+ dxcMsg.setTimeGenerated(splittedMessageLine[2]);
+
+ ChatMember newDXCListSender = new ChatMember();
+ newDXCListSender.setCallSign(splittedMessageLine[4]);
+ newDXCListSender.setQra(splittedMessageLine[8]);
+
+ ChatMember newDXCListReceiver = new ChatMember();
+ newDXCListReceiver.setFrequency(new SimpleStringProperty(splittedMessageLine[5]));
+ newDXCListReceiver.setCallSign(splittedMessageLine[6]);
+ newDXCListReceiver.setQra(splittedMessageLine[9]);
+
+ dxcMsg.setSender(newDXCListSender);
+ dxcMsg.setReceiver(newDXCListReceiver);
+
+ dxcMsg.setMessageInhibited(splittedMessageLine[7]);
+ dxcMsg.setQrgSpotted(splittedMessageLine[5]);
+
+ this.client.getLst_clusterMemberList().add(0, dxcMsg);
+
+// System.out.println("[MSGBUSMGT:] DXCluster Message detected ");
+
+// if (!this.client.getdXClusterMemberTable().contains(splittedMessageLine[6])) {
+// this.client.getdXClusterMemberTable().put(newDXCListMember.getCallSign(), newDXCListMember);
+// }
+
+ } else
+
+ /**
+ * DX-Cluster-Message type 2
+ * DL|1664047594|1926|dg9yih|144000.0|DL6BF|JO32PC
+ *
+ * JO32QI zerstoert qso|JO32PC|JO32QI| -> Clustermessage
+ * DL|1664048232|1937|pu2pyb|144500.0|PU2NEZ|FM| | |
+ */
+ if (splittedMessageLine[0].contains(DXCLUSTERMESSAGE2)) {
+// System.out.println("MSGBUS: User detected");
+
+ ClusterMessage dxcMsg2 = new ClusterMessage();
+
+ dxcMsg2.setTimeGenerated(splittedMessageLine[1]);
+
+ ChatMember newDXCListSender2 = new ChatMember();
+ newDXCListSender2.setCallSign(splittedMessageLine[3]);
+ newDXCListSender2.setQra(splittedMessageLine[7]);
+
+ ChatMember newDXCListReceiver2 = new ChatMember();
+ newDXCListReceiver2.setFrequency(new SimpleStringProperty(splittedMessageLine[4]));
+ newDXCListReceiver2.setCallSign(splittedMessageLine[5]);
+ newDXCListReceiver2.setQra(splittedMessageLine[8]);
+
+ dxcMsg2.setSender(newDXCListSender2);
+ dxcMsg2.setReceiver(newDXCListReceiver2);
+
+ dxcMsg2.setMessageInhibited(splittedMessageLine[6]);
+ dxcMsg2.setQrgSpotted(splittedMessageLine[4]);
+
+ this.client.getLst_clusterMemberList().add(0, dxcMsg2);
+
+ } else
+
+ /**
+ * DX-Cluster-Message type 3
+ * MA|0|1687204743|e77ar|OK2AF|JN94AS|JN89AR|
+ */
+ if (splittedMessageLine[0].contains(DXCLUSTERMESSAGE3)) {
+// System.out.println("MSGBUS: User detected");
+
+ ClusterMessage dxcMsg3 = new ClusterMessage();
+
+ dxcMsg3.setTimeGenerated(splittedMessageLine[2]);
+
+ ChatMember newDXCListSender3 = new ChatMember();
+ newDXCListSender3.setCallSign(splittedMessageLine[3]);
+ newDXCListSender3.setQra(splittedMessageLine[5]);
+
+ ChatMember newDXCListReceiver3 = new ChatMember();
+// newDXCListReceiver3.setFrequency(splittedMessageLine[4]);
+ newDXCListReceiver3.setCallSign(splittedMessageLine[4]);
+ newDXCListReceiver3.setQra(splittedMessageLine[5]);
+
+ dxcMsg3.setSender(newDXCListSender3);
+ dxcMsg3.setReceiver(newDXCListReceiver3);
+
+ dxcMsg3.setMessageInhibited("");
+ dxcMsg3.setQrgSpotted("");
+
+ this.client.getLst_clusterMemberList().add(0, dxcMsg3);
+
+ } else
+
+ /**
+ * Userstatechange:, last digit 0 = in chat, 1 away, 2 here, 3 also away...
+ * US4|2|DM5M|0|
+ */
+ if (splittedMessageLine[0].contains(USERSTATECHANGE)) {
+// System.out.println("MSGBUS: User detected");
+
+ ChatMember stateChangeMember = new ChatMember();
+
+ stateChangeMember.setCallSign(splittedMessageLine[2]);
+ stateChangeMember.setState(Integer.parseInt(splittedMessageLine[3]));
+
+// System.out.println("[MSGBUSMGT:] DXCluster Message detected ");
+
+ int index = checkListForChatMemberIndexByCallSign(this.client.getLst_chatMemberList(),
+ stateChangeMember);
+
+ if (index != -1 && index != 0) {
+ this.client.getLst_chatMemberList().get(index).setState(stateChangeMember.getState());
+ }
+
+// this.client.getChatMemberTable().get(stateChangeMember.getCallSign())
+// .setState(stateChangeMember.getState());
+
+ } else
+
+ /**
+ * Userinfo-update: UM3|2|HA4XN|Zoli 2m SSB/CW|JN96LX|2|
+ */
+ if (splittedMessageLine[0].contains(USERINFOUPDATEORUSERISBACK)) {
+
+ ChatMember stateChangeMember = new ChatMember();
+
+ stateChangeMember.setCallSign(splittedMessageLine[2]);
+ stateChangeMember.setName(splittedMessageLine[3]);
+ stateChangeMember.setQra(splittedMessageLine[4]);
+ stateChangeMember.setState(Integer.parseInt(splittedMessageLine[5]));
+ stateChangeMember.setLastActivity(new Utils4KST().time_generateActualTimeInDateFormat());
+
+ this.client.getDbHandler().storeChatMember(stateChangeMember); // TODO: not clean, it should be an
+ // upodate
+
+// System.out.println("[MSGBUSMGT:] DXCluster Message detected ");
+
+ int index = checkListForChatMemberIndexByCallSign(this.client.getLst_chatMemberList(),
+ stateChangeMember);
+
+ this.client.getLst_chatMemberList().get(index).setName(stateChangeMember.getName());
+ this.client.getLst_chatMemberList().get(index).setQra(stateChangeMember.getQra());
+ this.client.getLst_chatMemberList().get(index).setState(stateChangeMember.getState());
+
+// this.client.getChatMemberTable().get(stateChangeMember.getCallSign())
+// .setName(stateChangeMember.getName());
+// this.client.getChatMemberTable().get(stateChangeMember.getCallSign())
+// .setQra(stateChangeMember.getQra());
+// this.client.getChatMemberTable().get(stateChangeMember.getCallSign())
+// .setState(stateChangeMember.getState());
+
+ } else
+
+ /**
+ * Userinfo-update: UE|2|22562|
+ */
+ if (splittedMessageLine[0].contains(SRVR_USERLISTEND)) {
+
+ // No worthy information, count of users
+ } else
+
+ if (splittedMessageLine[0].contains(SRVR_DXCEND)) {
+
+ // No worthy information, count of users
+ } else
+
+ if (splittedMessageLine[0].contains(SRVR_COMMUNICATIONK)) {
+ // No worthy information, end of srvrmsgs
+ } else
+
+ //-> LOGSTAT|114|Wrong password!|
+ if (splittedMessageLine[0].contains(SRVR_LOGSTAT) && splittedMessageLine.length <= 5) {
+ System.out.println("Passwort falsch!");
+
+ if (splittedMessageLine[2].contains("password")) {
+ splittedMessageLine[2] += "pse disc- and reconnect";
+ }
+
+ ChatMember server = new ChatMember();
+ server.setCallSign("SERVER");
+ server.setName("SERVER");
+
+ ChatMessage pwErrorMsg = new ChatMessage();
+
+ pwErrorMsg.setMessageGeneratedTime(client.getCurrentEpochTime()+"");
+ pwErrorMsg.setSender(server);
+ pwErrorMsg.setMessageText(splittedMessageLine[2]);
+
+ for (int i = 0; i < 10; i++) {
+ client.getLst_toMeMessageList().add(pwErrorMsg);
+ client.getLst_toAllMessageList().add(pwErrorMsg);
+ }
+
+// this.client.disconnect();
+ }
+
+ else {
+
+// bufwrtrDBGMSGOut.write(new Utils4KST().time_generateCurrentMMDDhhmmTimeString()
+// + "[MSGBUSMGT:] Critical, detected unhandled Chatmessage -> "
+// + messageToProcess.getMessageText() + "\n");
+// bufwrtrDBGMSGOut.flush();
+
+ System.out.print(new Utils4KST().time_generateCurrentMMDDhhmmTimeString()
+ + " [MSGBUSMGT:] Critical, detected unhandled Chatmessage -> "
+ + messageToProcess.getMessageText() + "\n");
+
+ }
+
+ // ******************************************************************QUICKNDIRTY........
+// ChatMember thisMemberActualizesUserListForRefreshingIntheGuy = new ChatMember();
+// thisMemberActualizesUserListForRefreshingIntheGuy.setCallSign("REFR");
+// thisMemberActualizesUserListForRefreshingIntheGuy.setAirPlaneReflectInfo(new AirPlaneReflectionInfo());
+// this.client.getLst_chatMemberList().add(thisMemberActualizesUserListForRefreshingIntheGuy);
+// this.client.getLst_chatMemberList().isEmpty();
+// System.out.println("MSGBUS BGFX Listactualizer");
+// this.client.getLst_chatMemberList().remove(thisMemberActualizesUserListForRefreshingIntheGuy);
+ // ******************************************************************QUICKNDIRTY........
+
+// checkIfMessageInhibitsFrequency(messageToProcess);
+ }
+ }
+
+ @Override
+ public void interrupt() {
+ super.interrupt();
+
+ }
+
+
+ public void run() {
+
+// fileLogRAW = new File(new Utils4KST().time_generateCurrentMMddString() + "_praktiKST_raw.txt");
+
+// FileWriter fileWriterRAWChatMSGOut = null;
+// BufferedWriter bufwrtrRawMSGOut;
+
+// try {
+// fileWriterRAWChatMSGOut = new FileWriter(fileLogRAW, true);
+//
+// } catch (IOException e1) {
+// // TODO Auto-generated catch block
+// e1.printStackTrace();
+// }
+
+// bufwrtrRawMSGOut = new BufferedWriter(fileWriterRAWChatMSGOut);
+
+// File fileLogClientOut = new File(new Utils4KST().time_generateCurrentMMddString() + "_praktiKST_out.txt");
+
+// FileWriter fileWriterOutChatMSGOut = null;
+//
+// try {
+// fileWriterOutChatMSGOut = new FileWriter(fileLogClientOut, true);
+// } catch (IOException e2) {
+// // TODO Auto-generated catch block
+// e2.printStackTrace();
+// }
+//
+// try {
+// fileWriterRAWChatMSGOut = new FileWriter(fileLogClientOut, true);
+//
+// } catch (IOException e1) {
+// // TODO Auto-generated catch block
+// e1.printStackTrace();
+// try {
+// fileWriterRAWChatMSGOut.close();
+// } catch (IOException e) {
+// // TODO Auto-generated catch block
+// e.printStackTrace();
+// }
+// }
+
+// bufwrtrDBGMSGOut = new BufferedWriter(fileWriterOutChatMSGOut);
+
+// new Timer().schedule(new UserActualizationTask(client), 4000, 60000);// TODO: Temporary userlistoutput with
+// // known qrgs
+//
+// userActualizationTask = new UserActualizationTask(client); // kst4contest.test 4 23001
+// userActualizationTask.run();// kst4contest.test 4 23001
+
+ ChatMessage messageTextRaw = new ChatMessage(); // moved out of the while
+ String messageLine;
+
+ while (true) {
+
+
+
+ try {
+ messageTextRaw = client.getMessageRXBus().take();
+
+ if (messageTextRaw.getMessageText().equals("POISONPILL_KILLTHREAD") && messageTextRaw.getMessageSenderName().equals("POISONPILL_KILLTHREAD")) {
+ client.getMessageRXBus().clear();
+ break;
+ }
+ else {
+ messageLine = messageTextRaw.getMessageText();
+
+ /***********************************************
+ * CASE RX
+ ***********************************************/
+
+// if (client.getMessageRXBus().peek() != null) {
+
+// try {
+// messageTextRaw = client.getMessageRXBus().take();
+ //
+//// System.out.println("MSBGBUS: rxed: " + messageTextRaw);
+// } catch (InterruptedException e) {
+// // TODO Auto-generated catch block
+// e.printStackTrace();
+// }
+
+ if (messageTextRaw.getMessageText() == null) {
+ System.out.println("[MSGBUSMGT:] ERROR! got NULL message! BYE!");
+// this.interrupt();
+// break;
+ }
+
+ messageLine = messageTextRaw.getMessageText();
+
+// try {
+// bufwrtrRawMSGOut.write(messageLine + "\n");
+// bufwrtrRawMSGOut.flush();
+ //
+// } catch (IOException e) {
+// // TODO Auto-generated catch block
+// e.printStackTrace();
+// }
+
+ System.out.println(messageTextRaw.getMessageText() + " <- RXed"); // Stdout at
+ // Console#######################################################TODO:Wichtig
+
+ try {
+ processRXMessage23001(messageTextRaw);
+ } catch (IOException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ } catch (SQLException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+ }
+
+ } catch (InterruptedException e1) {
+ this.interrupt();
+ // TODO Auto-generated catch block
+ e1.printStackTrace();
+ // client.getMessageRXBus().clear();
+ }
+ {
+// System.out.println("MessagebusmgtThread: Readthread is interrupted! Queue will be resetted");
+// this.interrupt();
+// client.getMessageRXBus().clear();
+ }
+
+// if (client.getMessageRXBus().peek() == null) {
+//
+// Timer doNothingTimer = new Timer();
+// doNothingTimer.schedule(new TimerTask() {
+//
+// @Override
+// public void run() {
+//
+// //do nothing
+//
+// }
+// }, 100);// TODO: Temporary
+// }
+//
+//
+// if (client.getMessageRXBus().peek() == null && client.getMessageTXBus().peek() == null) {
+//
+// if (this.client.isDisconnectionPerformedByUser()) {
+// break;//TODO: what if it´s not the finally closage but a band channel change?
+// }
+// // do nothing
+//// try {
+//// this.sleep(20);
+//// } catch (InterruptedException e) {
+//// // TODO Auto-generated catch block
+//// e.printStackTrace();
+//// } catch (Exception e2) {
+//// // TODO Auto-generated catch block
+//// e2.printStackTrace();
+//// }
+// }
+// else
+ {
+
+// messageLine = messageTextRaw.getMessageText();
+//
+// /***********************************************
+// * CASE RX
+// ***********************************************/
+//
+//// if (client.getMessageRXBus().peek() != null) {
+//
+//// try {
+//// messageTextRaw = client.getMessageRXBus().take();
+////
+////// System.out.println("MSBGBUS: rxed: " + messageTextRaw);
+//// } catch (InterruptedException e) {
+//// // TODO Auto-generated catch block
+//// e.printStackTrace();
+//// }
+//
+// if (messageTextRaw.getMessageText() == null) {
+// System.out.println("[MSGBUSMGT:] ERROR! got NULL message! BYE!");
+//// this.interrupt();
+//// break;
+// }
+//
+// messageLine = messageTextRaw.getMessageText();
+//
+//// try {
+//// bufwrtrRawMSGOut.write(messageLine + "\n");
+//// bufwrtrRawMSGOut.flush();
+////
+//// } catch (IOException e) {
+//// // TODO Auto-generated catch block
+//// e.printStackTrace();
+//// }
+//
+// System.out.println(messageTextRaw.getMessageText() + " <- RXed"); // Stdout at
+// // Console#######################################################TODO:Wichtig
+//
+// try {
+// processRXMessage23001(messageTextRaw);
+// } catch (IOException e) {
+// // TODO Auto-generated catch block
+// e.printStackTrace();
+// } catch (SQLException e) {
+// // TODO Auto-generated catch block
+// e.printStackTrace();
+// }
+
+// } //end peek != null
+
+ /**************************************************************
+ * End of case RX
+ **************************************************************/
+
+ /**************************************************************
+ * Start of case TX
+ **************************************************************/
+
+// if (client.getMessageTXBus().peek() != null) {
+// /***********************************************
+// * CASE TX
+// ***********************************************/
+//
+// if (this.isServerready()) {
+// // then send the line
+//
+// try {
+// messageTextRaw = client.getMessageTXBus().take();
+//// this.setServerready(false); // after tx always wait for an answer prompt //23000
+// this.setServerready(true);
+//
+// } catch (InterruptedException e) {
+// // TODO Auto-generated catch block
+// e.printStackTrace();
+// }
+//
+// messageLine = messageTextRaw.getMessageText();
+//
+// if (messageTextRaw.isMessageDirectedToServer()) {
+// /**
+// * We have to check if we only commands the server (keepalive) or want do talk
+// * to the community
+// */
+//
+// try {
+// client.getWriteThread().tx(messageTextRaw);
+// System.out.println("BUS: tx: " + messageTextRaw.getMessageText());
+//
+// } catch (InterruptedException e) {
+// // TODO Auto-generated catch block
+// e.printStackTrace();
+// }
+//
+// //////////////////////// bgfx ab here//////////////////////////////
+//// try {
+//// bufwrtrRawMSGOut.write(messageLine + "\r");
+////// bw.write(messageLine + "\n");//kst4contest.test 4 23001
+//// bufwrtrRawMSGOut.flush();
+////
+//// } catch (IOException e) {
+//// // TODO Auto-generated catch block
+//// e.printStackTrace();
+//// }
+// ///////////////////////////////////////////////////////////////////
+// } else {
+//
+// ChatMessage ownMSG = new ChatMessage();
+//
+//// ownMSG.setMessageText(
+//// "MSG|" + this.client.getCategory().getCategoryNumber() + "|0|" + messageLine + "|0|");
+//
+// ownMSG.setMessageText(
+// "MSG|" + this.client.getChatPreferences().getLoginChatCategory().getCategoryNumber()
+// + "|0|" + messageLine + "|0|");
+//
+// try {
+// client.getWriteThread().tx(ownMSG);
+// System.out.println("BUS: tx: " + ownMSG.getMessageText());
+//
+// } catch (InterruptedException e) {
+// // TODO Auto-generated catch block
+// e.printStackTrace();
+// }
+// }
+//
+// if (messageTextRaw.equals("/QUIT")) {
+// try {
+// this.client.getReadThread().terminateConnection();
+// this.client.getReadThread().interrupt();
+// this.client.getWriteThread().terminateConnection();
+// this.client.getWriteThread().interrupt();
+// this.interrupt();
+//
+// } catch (IOException e) {
+// // TODO Auto-generated catch block
+// e.printStackTrace();
+// }
+// }
+//
+// } else {
+//// System.out.println("msgbus no elements yet");
+// }
+// } //end tx.peek != null
+ }
+
+// System.out.println("messagebusmgt while performed");
+
+ } // while true end
+ System.out.println("Msgbusmgt: interrupt");
+ this.interrupt();
+ }
+}
diff --git a/src/main/java/kst4contest/controller/ReadThread.java b/src/main/java/kst4contest/controller/ReadThread.java
new file mode 100644
index 0000000..a3d35a5
--- /dev/null
+++ b/src/main/java/kst4contest/controller/ReadThread.java
@@ -0,0 +1,158 @@
+package kst4contest.controller;
+
+import java.io.*;
+import java.net.*;
+
+import kst4contest.model.ChatMessage;
+
+/**
+ * This thread is responsible for reading telnet servers input at port 23001 and printing it
+ * to the console.
+ * It runs in an infinite loop until the client disconnects from the server.
+ *
+ * @author www.codejava.net
+ */
+public class ReadThread extends Thread {
+ private BufferedReader reader;
+ private Socket socket;
+ private ChatController client;
+ public boolean accidentalDisconnected;
+
+
+
+ public boolean isAccidentalDisconnected() {
+ return accidentalDisconnected;
+ }
+
+ public void setAccidentalDisconnected(boolean accidentalDisconnected) {
+ this.accidentalDisconnected = accidentalDisconnected;
+ }
+
+ // private boolean readingFinished = true; //kst4contest.test 4 23001
+ private boolean readingFinished = true;
+
+ InputStream input;
+
+ public ReadThread(Socket socket, ChatController client) {
+ this.socket = socket;
+ this.client = client;
+
+ try {
+ input = socket.getInputStream();
+ reader = new BufferedReader(new InputStreamReader(input));
+
+ } catch (IOException ex) {
+ System.out.println("Error getting input stream: " + ex.getMessage());
+ ex.printStackTrace();
+ }
+ }
+
+ public void run() {
+ Thread.currentThread().setName("ReadFromTelnetThread");
+
+ ChatMessage message; //bugfix leak, moved out of while
+ while (true) {
+
+// System.out.println("rdth");
+
+ try {
+
+ String response = reader.readLine();
+ message = new ChatMessage();
+ message.setMessageText(response);
+
+// message.setDirectedToServer(false);
+// message.setDirectedToServer(false);
+// message.setDirectedToServer(false);
+
+
+ if (response != null) {
+ client.getMessageRXBus().put(message);
+// System.out.println("[RT]: read message and added it to msgrxqueue --- " + response + " ---");
+ } else {
+ System.out.println("[RT]: read message responsed a nullstring, do nothing, buffersize = " + socket.getReceiveBufferSize() + ", reader ready? "
+ + reader.ready());
+// reader = new BufferedReader(new InputStreamReader(input));
+// response = reader.readLine();
+ this.client.getSocket().close();
+ this.interrupt();
+
+ }
+
+ }
+ catch (Exception sexc) {
+ System.out.println("[ReadThread, CRITICAL: ] Socket geschlossen: " + sexc.getMessage());
+ try {
+ this.client.getSocket().close();
+ this.interrupt();
+ break;
+
+ } catch (IOException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+
+// try {
+// sleep(3000);
+// } catch (InterruptedException e) {
+// // TODO Auto-generated catch block
+// e.printStackTrace();
+// }
+
+// try {
+// System.out.println("RDTH: try new socket");
+// this.client.getSocket().close();
+// this.client.getSocket().close();
+// this.client.setSocket(new Socket(this.client.getHostname(), this.client.getPort()));
+// socket.connect(new InetSocketAddress(this.client.getHostname(), this.client.getPort()));
+// System.out.println("[Readthread, Warning:] new socket connected? -> " + socket.isConnected());
+
+// input = socket.getInputStream();
+// reader = new BufferedReader(new InputStreamReader(input));
+//
+// this.sleep(5000);
+// } catch (IOException | InterruptedException e2) {
+// // TODO Auto-generated catch block
+// System.out.println("fucktah");
+// e2.printStackTrace();
+// }
+// try {
+// sleep(2000);
+// } catch (InterruptedException e1) {
+// // TODO Auto-generated catch block
+// e1.printStackTrace();
+// }
+// try {
+// this.client.getSocket().close();
+// this.client.setSocket(new Socket(this.client.getHostname(), this.client.getPort()));
+// } catch (UnknownHostException e) {
+// // TODO Auto-generated catch block
+// e.printStackTrace();
+// } catch (IOException e) {
+// // TODO Auto-generated catch block
+// e.printStackTrace();
+// }
+
+ }
+
+ }
+ }
+
+ public boolean terminateConnection() throws IOException {
+ this.reader.close();
+ this.input.close();
+ this.socket.close();
+
+ return true;
+ }
+
+ public boolean isReadingFinished() {
+ return readingFinished;
+ }
+
+ public void setReadingFinished(boolean readingReady) {
+ this.readingFinished = readingReady;
+ }
+
+
+}
\ No newline at end of file
diff --git a/src/main/java/kst4contest/controller/ReadUDPbyAirScoutMessageThread.java b/src/main/java/kst4contest/controller/ReadUDPbyAirScoutMessageThread.java
new file mode 100644
index 0000000..53ea3e0
--- /dev/null
+++ b/src/main/java/kst4contest/controller/ReadUDPbyAirScoutMessageThread.java
@@ -0,0 +1,270 @@
+package kst4contest.controller;
+
+import java.io.*;
+import java.net.*;
+import java.util.Comparator;
+
+import javafx.collections.FXCollections;
+import javafx.collections.ObservableList;
+import kst4contest.model.AirPlane;
+import kst4contest.model.AirPlaneReflectionInfo;
+import kst4contest.model.ChatMember;
+
+/**
+ * This thread is responsible for reading server's input and printing it to the
+ * console. It runs in an infinite loop until the client disconnects from the
+ * server.
+ *
+ * @author www.codejava.net
+ */
+public class ReadUDPbyAirScoutMessageThread extends Thread {
+ private BufferedReader reader;
+ private Socket socket;
+ private ChatController client;
+ private int localPort;
+ private String ASIdentificator, ChatClientIdentificator;
+
+ public ReadUDPbyAirScoutMessageThread(int localPort) {
+ this.localPort = localPort;
+ }
+
+ public ReadUDPbyAirScoutMessageThread(int localPort, ChatController client, String ASIdentificator,
+ String ChatClientIdentificator) {
+ this.localPort = localPort;
+ this.client = client;
+ this.ASIdentificator = ASIdentificator;
+ this.ChatClientIdentificator = ChatClientIdentificator;
+ }
+
+ @Override
+ public void interrupt() {
+ super.interrupt();
+ try {
+ if (this.socket != null) {
+
+ this.socket.close();
+ }
+ } catch (Exception e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+ }
+
+
+
+ public void run() {
+ Thread.currentThread().setName("ReadUDPByAirScoutThread");
+
+ DatagramSocket socket = null;
+ boolean running;
+ byte[] buf = new byte[1777];
+ DatagramPacket packet;
+// DatagramPacket packet = new DatagramPacket(buf, buf.length); //changed due to save memory
+ packet = new DatagramPacket(buf, buf.length);
+
+ try {
+
+ socket = new DatagramSocket(null);
+ socket.setReuseAddress(true);
+ socket.bind(new InetSocketAddress(localPort));
+ socket.receive(packet);
+ socket.setSoTimeout(3000);
+
+ } catch (SocketException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+
+ } catch (IOException e) {
+ // TODO Auto-generated catch block
+// e.printStackTrace();
+ }
+
+
+ while (true) {
+// packet = new DatagramPacket(buf, buf.length);
+// DatagramPacket packet = new DatagramPacket(SRPDefinitions.BYTE_BUFFER_MAX_LENGTH);
+ try {
+ if (this.client.isDisconnectionPerformedByUser()) {
+ break;//TODO: what if it´s not the finally closage but a band channel change?
+ }
+
+ socket.receive(packet);
+ } catch (SocketTimeoutException e2) {
+ // this will catch the repeating Sockettimeoutexception...nothing to do
+// e2.printStackTrace();
+ }
+ catch (IOException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+
+ InetAddress address = packet.getAddress();
+ int port = packet.getPort();
+// packet = new DatagramPacket(buf, buf.length, address, port);
+ String received = new String(packet.getData(), packet.getOffset(), packet.getLength());
+ received = received.trim();
+
+ if (received.contains("ASSETPATH") || received.contains("ASWATCHLIST")) {
+ // do nothing, that is your own message
+ } else if (received.contains("ASNEAREST:")) {
+ processASUDPMessage(received);
+// System.out.println("[ReadUSPASTh, info:] received AS String " + received);
+
+ AirPlaneReflectionInfo apReflectInfoForChatMember;
+
+ apReflectInfoForChatMember = processASUDPMessage(received);
+ if (this.client.getLst_chatMemberList().size() != 0) {
+
+ try {
+
+// if (this.client.checkListForChatMemberIndexByCallSign(apReflectInfoForChatMember.getReceiver()) != -1) {
+
+ this.client.getLst_chatMemberList()
+ .get(this.client.checkListForChatMemberIndexByCallSign(
+ apReflectInfoForChatMember.getReceiver()))
+ .setAirPlaneReflectInfo(apReflectInfoForChatMember); // TODO: here we set the ap info at
+ // the central instance of
+ // chatmember list .... -1 is a
+ // problem!
+ /**
+ * CK| MSGBUS BGFX Listactualizer Exception in thread "Thread-10"
+ * java.util.ConcurrentModificationException at
+ * java.base/java.util.AbstractList$Itr.checkForComodification(AbstractList.java:399)
+ * at java.base/java.util.AbstractList$Itr.next(AbstractList.java:368) at
+ * kst4contest.controller.ChatController.checkListForChatMemberIndexByCallSign(ChatController.java:173)
+ * at
+ * kst4contest.controller.ReadUDPbyAirScoutMessageThread.run(ReadUDPbyAirScoutMessageThread.java:93)
+ *
+ */
+// }
+ } catch (Exception e) {
+
+ System.out.println("ReadUdpByAsMsgTh, Warning:"
+ + apReflectInfoForChatMember.getReceiver().getCallSign()
+ + " is not in the Chatmemberlist or the Chatmemberlist is modified by another Thread");
+ // TODO: handle exception
+ }
+
+ }
+
+ }
+// packet = null; //reset packet
+ buf = new byte[1777]; // reset buffer for future smaller packets
+
+ }
+
+ }
+
+ public AirPlaneReflectionInfo processASUDPMessage(String udpStringToProcess) {
+
+// System.out.println("RDUDPAS RECV: " + udpStringToProcess);
+
+ // TODO: filter messages which are directed to another client
+
+ /*
+ * Example mesage: ASNEAREST: "AS" "KST"
+ * "2023-04-01 21:33:42Z,DO5AMF,JN49GL,G4CLA,IO92JL,9,VLG2PT,M,190,75,14,BAW809,M,250,50,18,BEL6CM,M,143,50,12,WZZ6719,M,148,50,11,KLM1678,M,313,75,22,TRA1B,M,271,75,20,SAS4728,M,125,75,9,RYR6TL,M,90,75,6,UAE10,S,96,50,6"
+ * Syntax: ASNEAREST: "AS" "KST" "2023-03-09
+ * 23:21:50Z,DO5AMF,JN49GL,DM5M,JO51IJ,3, SWT8TB,M,121,75,16, ^kleines Ding
+ * ^^^Distanz km ^^Potenzial 0-100% ^^Dauer bis ankunft minutes
+ */
+ String[] fullStringSplitter;
+ String[] apStringSplitter;
+ AirPlaneReflectionInfo apInfo = new AirPlaneReflectionInfo();
+ ObservableList airplaneList = FXCollections.observableArrayList();
+
+ if (udpStringToProcess.contains("ASNEAREST: ")) {
+ udpStringToProcess = udpStringToProcess.replace("ASNEAREST: ", "");
+ udpStringToProcess = udpStringToProcess.replace(" ", "");
+ fullStringSplitter = udpStringToProcess.split("\"");
+
+// for (int i = 0; i < fullStringSplitter.length; i++) {
+// System.out.println(i + " " + fullStringSplitter[i]);
+// }
+
+ String APInfoString = fullStringSplitter[5];
+ apStringSplitter = APInfoString.split(",");
+ String[] allAPInfos = new String[apStringSplitter.length - 6]; // new String shold only provide aps, nothing
+ // other
+
+ for (int i = 0; i < apStringSplitter.length; i++) {
+
+ if (i >= 6) {
+ allAPInfos[i - 6] = apStringSplitter[i];
+ }
+
+// System.out.println(i + ": " + apStringSplitter[i]);
+
+// if (i>=6) {
+// allAPInfos[i-6] = apStringSplitter[i];
+// System.out.println(i-5 + " > " + apStringSplitter[i]);
+//
+//
+//
+// }
+
+ }
+ AirPlane airPlane = new AirPlane();
+
+ for (int i = 0; i < allAPInfos.length; i++) {
+ if (((i) % 5) == 0) {
+ airPlane = new AirPlane();
+// airPlane = new AirPlane();
+
+ airPlane.setApCallSign(allAPInfos[i]);
+
+// System.out.println(i + " AP: " + allAPInfos[i]);
+ } else if (((i) % 5) == 1) {
+
+ airPlane.setApSizeCategory(allAPInfos[i]);
+// System.out.println(i + " cat: " + allAPInfos[i]);
+ } else if (((i) % 5) == 2) {
+
+ airPlane.setDistanceKm(Integer.parseInt(allAPInfos[i]));
+// System.out.println(i + " dist: " + allAPInfos[i]);
+ } else if (((i) % 5) == 3) {
+
+ airPlane.setPotential(Integer.parseInt(allAPInfos[i]));
+// System.out.println(i + " potential: " + allAPInfos[i]);
+
+ }
+ if (((i) % 5) == 4) {
+
+// System.out.println(i + " duration: " + allAPInfos[i]);
+ airPlane.setArrivingDurationMinutes(Integer.parseInt(allAPInfos[i]));
+ airplaneList.add(airPlane);
+ }
+ }
+
+ apInfo.setDate(apStringSplitter[0]);
+ ChatMember apStartCallSign = new ChatMember();
+ apStartCallSign.setCallSign(apStringSplitter[1]);
+ apStartCallSign.setQra(apStringSplitter[2]);
+ apInfo.setSender(apStartCallSign); // usally its the callsign of own chatmember object, may check this
+
+ ChatMember apDestCallSign = new ChatMember();
+ apDestCallSign.setCallSign(apStringSplitter[3]);
+ apDestCallSign.setQra(apStringSplitter[4]);
+ apInfo.setReceiver(apDestCallSign);
+
+ apInfo.setAirPlanesReachableCntr(Integer.parseInt(apStringSplitter[5]));
+ apInfo.setRisingAirplanes(airplaneList);
+
+// System.out.println("total airplanes for rx stn " + apInfo.getReceiver().getCallSign() + ": " + airplaneList.size() + " " + apInfo.toString());
+
+ airplaneList.sort(Comparator.comparing(AirPlane::getPotential).reversed()
+ .thenComparing(AirPlane::getArrivingDurationMinutes));
+
+ }
+
+ return apInfo;
+ }
+
+ public boolean terminateConnection() throws IOException {
+
+ this.socket.close();
+
+ return true;
+ }
+
+}
\ No newline at end of file
diff --git a/src/main/java/kst4contest/controller/ReadUDPbyUCXMessageThread.java b/src/main/java/kst4contest/controller/ReadUDPbyUCXMessageThread.java
new file mode 100644
index 0000000..36f7dee
--- /dev/null
+++ b/src/main/java/kst4contest/controller/ReadUDPbyUCXMessageThread.java
@@ -0,0 +1,406 @@
+package kst4contest.controller;
+
+import java.io.*;
+import java.net.*;
+import java.sql.SQLException;
+
+import javax.xml.XMLConstants;
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.parsers.ParserConfigurationException;
+
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+import org.xml.sax.InputSource;
+import org.xml.sax.SAXException;
+
+import kst4contest.model.ChatMember;
+
+/**
+ * This thread is responsible for reading server's input and printing it to the
+ * console. It runs in an infinite loop until the client disconnects from the
+ * server.
+ *
+ * @author www.codejava.net
+ */
+public class ReadUDPbyUCXMessageThread extends Thread {
+ private BufferedReader reader;
+ private Socket socket;
+ private ChatController client;
+
+ public ReadUDPbyUCXMessageThread(int localPort) {
+
+ }
+
+ public ReadUDPbyUCXMessageThread(int localPort, ChatController client) {
+ this.client = client;
+ }
+
+ @Override
+ public void interrupt() {
+ super.interrupt();
+ try {
+ if (this.socket != null) {
+
+ this.socket.close();
+ }
+ } catch (Exception e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+ }
+
+ public void run() {
+
+ Thread.currentThread().setName("ReadUDPByUCXLogThread");
+
+ DatagramSocket socket = null;
+ boolean running;
+ byte[] buf = new byte[1777];
+ DatagramPacket packet = new DatagramPacket(buf, buf.length);
+
+ try {
+ socket = new DatagramSocket(12060);
+ socket.setSoTimeout(11000); //TODO try for end properly
+ }
+
+ catch (SocketException e) {
+ //this will catch the repeating Sockettimeoutexception...nothing to do
+// e.printStackTrace();
+ }
+
+ while (true) {
+
+ boolean timeOutIndicator = false;
+
+ if (this.client.isDisconnectionPerformedByUser()) {
+ break;//TODO: what if it´s not the finally closage but a band channel change?
+ }
+// packet = new DatagramPacket(buf, buf.length); //TODO: Changed that due to memory leak, check if all works (seems like that)
+// DatagramPacket packet = new DatagramPacket(SRPDefinitions.BYTE_BUFFER_MAX_LENGTH); //TODO: Changed that due to memory leak, check if all works (seems like that)
+ try {
+ socket.receive(packet);
+ } catch (SocketTimeoutException e2) {
+
+ timeOutIndicator = true;
+ // this will catch the repeating Sockettimeoutexception...nothing to do
+// e2.printStackTrace();
+ }
+ catch (IOException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+
+ InetAddress address = packet.getAddress();
+ int port = packet.getPort();
+// packet = new DatagramPacket(buf, buf.length, address, port);
+ String received = new String(packet.getData(), packet.getOffset(), packet.getLength());
+ received = received.trim();
+
+// System.out.println("recvudpucx");
+
+
+ if (!timeOutIndicator) {
+ processUCXUDPMessage(received);
+ } else {
+ //dont process the empty message
+ }
+
+ buf = new byte[1777]; // reset buffer for future smaller packets
+
+ }
+
+ }
+
+ public String processUCXUDPMessage(String udpPacketToProcess) {
+
+ File logUDPMessageToThisFile;
+
+ String udpMsg = udpPacketToProcess;
+
+ ChatMember modifyThat = null;
+
+// System.out.println(udpMsg);
+
+ DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
+ try {
+ dbf.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
+ } catch (ParserConfigurationException e1) {
+ // TODO Auto-generated catch block
+ e1.printStackTrace();
+ }
+
+ try {
+ DocumentBuilder db = dbf.newDocumentBuilder();
+ Document doc = db.parse(new InputSource(new StringReader(udpMsg)));
+
+ /**
+ * case Log-QSO-Packet in ucxlog
+ *
+ */
+ NodeList list = doc.getElementsByTagName("contactinfo");
+ if (list.getLength() != 0) {
+
+ for (int temp = 0; temp < list.getLength(); temp++) {
+
+ Node node = list.item(temp);
+
+ if (node.getNodeType() == Node.ELEMENT_NODE) {
+
+ Element element = (Element) node;
+
+ String call = element.getElementsByTagName("call").item(0).getTextContent();
+// call = call.toLowerCase();
+ String band = element.getElementsByTagName("band").item(0).getTextContent();
+
+ System.out.println("[Readudp, info ]: received Current Element :" + node.getNodeName()
+ + "call: " + call + " / " + band);
+
+ ChatMember workedCall = new ChatMember();
+ workedCall.setCallSign(call);
+ workedCall.setWorked(true);
+
+ switch (band) {
+ case "144": {
+ workedCall.setWorked144(true);
+ break;
+ }
+
+ case "432": {
+ workedCall.setWorked432(true);
+ break;
+ }
+
+ case "1240": {
+ workedCall.setWorked1240(true);
+ break;
+ }
+
+ case "2300": {
+ workedCall.setWorked2300(true);
+ break;
+ }
+
+ case "3400": {
+ workedCall.setWorked3400(true);
+ break;
+ }
+
+ case "5600": {
+ workedCall.setWorked5600(true);
+ break;
+ }
+
+ case "10G": {
+ workedCall.setWorked10G(true);
+
+ }
+
+ default:
+ System.out.println("[ReadUDPFromUCX, Error:] unexpected band value: \"" + band + "\"");
+ break;
+ }
+
+// if (!client.getMap_ucxLogInfoWorkedCalls().containsKey("call")) {
+// client.getMap_ucxLogInfoWorkedCalls().put(call, workedCall);
+
+// } else
+ {
+ /**
+ * That means, the station is worked already but maybe at another band. So we
+ * have to get the worked ChatMember out of the list and to modify the worked
+ * options.
+ */
+
+// modifyThat = (ChatMember) client.getMap_ucxLogInfoWorkedCalls().get(call);
+
+ int indexOfChatMemberInTable = -1;
+ indexOfChatMemberInTable = client.checkListForChatMemberIndexByCallSign(workedCall);
+
+ if (indexOfChatMemberInTable == -1) {
+ // do nothing
+ } else {
+ modifyThat = client.getLst_chatMemberList().get(indexOfChatMemberInTable);
+// modifyThat.setWorked(true);
+
+ client.getLst_chatMemberList()
+ .get(client.checkListForChatMemberIndexByCallSign(modifyThat)).setWorked(true);
+
+ if (workedCall.isWorked144()) {
+ modifyThat.setWorked144(true);
+ client.getLst_chatMemberList()
+ .get(client.checkListForChatMemberIndexByCallSign(modifyThat))
+ .setWorked144(true);
+
+ } else if (workedCall.isWorked432()) {
+ modifyThat.setWorked432(true);
+ client.getLst_chatMemberList()
+ .get(client.checkListForChatMemberIndexByCallSign(modifyThat))
+ .setWorked432(true);
+
+ } else if (workedCall.isWorked1240()) {
+ modifyThat.setWorked1240(true);
+ client.getLst_chatMemberList()
+ .get(client.checkListForChatMemberIndexByCallSign(modifyThat))
+ .setWorked1240(true);
+
+ } else if (workedCall.isWorked2300()) {
+ modifyThat.setWorked2300(true);
+ client.getLst_chatMemberList()
+ .get(client.checkListForChatMemberIndexByCallSign(modifyThat))
+ .setWorked2300(true);
+
+ } else if (workedCall.isWorked3400()) {
+ modifyThat.setWorked3400(true);
+ client.getLst_chatMemberList()
+ .get(client.checkListForChatMemberIndexByCallSign(modifyThat))
+ .setWorked3400(true);
+
+ } else if (workedCall.isWorked5600()) {
+ modifyThat.setWorked5600(true);
+ client.getLst_chatMemberList()
+ .get(client.checkListForChatMemberIndexByCallSign(modifyThat))
+ .setWorked5600(true);
+
+ } else if (workedCall.isWorked10G()) {
+ modifyThat.setWorked10G(true);
+ client.getLst_chatMemberList()
+ .get(client.checkListForChatMemberIndexByCallSign(modifyThat))
+ .setWorked10G(true);
+ }
+
+ }
+ }
+
+ boolean isInChat = this.client.getDbHandler().updateWkdInfoOnChatMember(workedCall);
+ // This will update the worked info on a worked chatmember. DBHandler will
+ // check, if an entry at the db had been modified. If not, then the worked
+ // station had not been stored. DBHandler will store the informations then.
+ if (!isInChat) {
+
+ workedCall.setName("unknown");
+ workedCall.setQra("unknown");
+ workedCall.setLastActivity(new Utils4KST().time_generateActualTimeInDateFormat());
+ this.client.getDbHandler().storeChatMember(workedCall);
+ }
+
+
+ logUDPMessageToThisFile = new File(this.client.getChatPreferences()
+ .getLogSynch_storeWorkedCallSignsFileNameUDPMessageBackup());
+
+ FileWriter fileWriterPersistUDPToFile = null;
+ BufferedWriter bufwrtrRawMSGOut;
+
+ try {
+ fileWriterPersistUDPToFile = new FileWriter(logUDPMessageToThisFile, true);
+
+ } catch (IOException e1) {
+ // TODO Auto-generated catch block
+ e1.printStackTrace();
+ }
+
+ bufwrtrRawMSGOut = new BufferedWriter(fileWriterPersistUDPToFile);
+
+ if (modifyThat != null) {
+ bufwrtrRawMSGOut.write("\n" + modifyThat.toString());
+ bufwrtrRawMSGOut.flush();
+ bufwrtrRawMSGOut.close();
+
+ } else {
+ bufwrtrRawMSGOut.write("\n" + workedCall.toString());
+ bufwrtrRawMSGOut.flush();
+ bufwrtrRawMSGOut.close();
+
+ }
+
+ }
+ }
+ } else {
+ list = doc.getElementsByTagName("RadioInfo");
+
+ for (int temp = 0; temp < list.getLength(); temp++) {
+
+ Node node = list.item(temp);
+
+ if (node.getNodeType() == Node.ELEMENT_NODE) {
+
+ String formattedQRG;
+
+ Element element = (Element) node;
+
+ String qrg = element.getElementsByTagName("Freq").item(0).getTextContent();
+ String mode = element.getElementsByTagName("Mode").item(0).getTextContent();
+
+// System.out.println("QRG Length: " + qrg.length() + " // " + qrg);
+
+ /**
+ * The following if statement is only for formatting the frequency input for
+ * good readability to avoid values like 129601000 and set it to something
+ * readable like 1296.010.00
+ *
+ */
+ if (qrg.length() == 6) {
+ // 701000 KHz
+ formattedQRG = qrg.format("%s.%s.%s", qrg.substring(0, 1), qrg.substring(2, 5),
+ qrg.substring(5, 6));
+
+ } else if (qrg.length() == 7) {
+ // 700000 KHz
+ formattedQRG = qrg.format("%s.%s.%s", qrg.substring(0, 2), qrg.substring(2, 5),
+ qrg.substring(5, 7));
+ } else if (qrg.length() == 8) {
+ // 144.123.22 KHz
+ formattedQRG = qrg.format("%s.%s.%s", qrg.substring(0, 3), qrg.substring(3, 6),
+ qrg.substring(6, 8));
+ } else if (qrg.length() == 9) {
+ // 1296.010.00
+ formattedQRG = qrg.format("%s.%s.%s", qrg.substring(0, 4), qrg.substring(4, 7),
+ qrg.substring(7, 9));
+ } else if (qrg.length() == 10) {
+ // 10000.010.00
+ formattedQRG = qrg.format("%s.%s.%s", qrg.substring(0, 5), qrg.substring(5, 8),
+ qrg.substring(8, 10));
+ }
+
+ else {
+ formattedQRG = qrg;
+ }
+
+// System.out.println("Current Element :" + node.getNodeName());
+// System.out.println("Radio QRG : " + qrg);
+// System.out.println("Radio Mode: " + mode);
+// System.out.println("[ReadUDPFromUCX, Info:] Setted QRG pref to: \"" + qrg + "\"" );
+
+ this.client.getChatPreferences().getMYQRG().set(formattedQRG);
+
+ System.out.println("[ReadUDPbyUCXTh: ] Radioinfo processed: " + formattedQRG);
+ }
+ }
+
+ }
+
+ } catch (ParserConfigurationException | SAXException | IOException e) {
+ e.printStackTrace();
+ System.out.println(e.getCause());
+ System.out.println(e.getMessage());
+ } catch (SQLException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+
+// System.out.println("[ReadUDPbyUCXTh: ] worked size = " + this.client.getMap_ucxLogInfoWorkedCalls().size());
+// System.out.println("[ReadUDPbyUCXTh: ] worked size = removeThisActions" );
+
+ return "";
+ }
+
+ public boolean terminateConnection() throws IOException {
+
+ this.socket.close();
+
+ return true;
+ }
+
+}
\ No newline at end of file
diff --git a/src/main/java/kst4contest/controller/RegexTester.java b/src/main/java/kst4contest/controller/RegexTester.java
new file mode 100644
index 0000000..7cd6c55
--- /dev/null
+++ b/src/main/java/kst4contest/controller/RegexTester.java
@@ -0,0 +1,86 @@
+package kst4contest.controller;
+
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import kst4contest.model.ChatMember;
+
+public class RegexTester {
+
+ public static void main(String[] args) {
+
+
+ String temp;
+
+ temp = "DL1KDA JO30EP Alex\r\n"
+ + "DL3LST JO61FI Ray(Rainer)\r\n"
+ + "DL4ZAQ JN49MP Peter 2m&70cm&6m\r\n"
+ + "DL9LBH JN59ID Hans 2/70/23\r\n"
+ + "DO5AMF JN49FK Marc\r\n"
+ + "F5ICN JN03BF Alex QRV 2/70/23\r\n"
+ + "F6GRA JN04DB Carol 2m\r\n"
+ + "F6HTJ JN12KQ Michel\r\n"
+ + "G0BIX JO01GI Terry\r\n"
+ + "G0HOF IO92UL Ken 11el + 400 W\r\n"
+ + "G0LBK JO03BD David\r\n"
+ + "G0MBL JO01QH Andrew 2m\r\n"
+ + "G1SDX IO80FL Grant\r\n"
+ + "G3MXH JO02LF Terry\r\n"
+ + "G3OVH IO92JP Tony\r\n"
+ + "G3VCA IO93MG bob\r\n"
+ + "G4AEP IO91MB bill\r\n"
+ + "G4DHF IO92UU David\r\n"
+ + "G4FUF JO01GN Keith\r\n"
+ + "G4TRA IO81WN Steve\r\n"
+ + "G6HKS IO92OB Richard\r\n"
+ + "G8SEI IO92FO Jeff 6/4/2/70\r\n"
+ + "GM0EWX IO67UL Calum\r\n"
+ + "GW8IZR IO73TI Paul\r\n"
+ + "IV3GTH JN65RU Gigi\r\n"
+ + "IZ2XZM JN45KH Nick\r\n"
+ + "LY1BWB KO24PR VU Club 2/70\r\n"
+ + "LY2EN KO24PQ QRT\r\n"
+ + "OK1FPR JO80CE Milos\r\n"
+ + "OV3T JO46CM Thomas\r\n"
+ + "OY4TN IP62NB Trygvi 11EL/350W\r\n"
+ + "OZ2M JO65FR Bo PNGonVHF\r\n"
+ + "OZ3Z JO45UN Anders @432.228\r\n"
+ + "OZ7UV JO65DH Svend\r\n"
+ + "PA3DOL JO22MT Sjoerd\r\n"
+ + "PA3PCV JO20VV Marcel 2m\r\n"
+ + "PA9R JO22JK Rob\r\n"
+ + "S52FO JN76EF Janez\r\n"
+ + "S53RM JN76HD Sine\r\n"
+ + "SM2CEW KP15CR Peter\r\n"
+ + "SP6VGJ JO81HU Jacek";
+
+
+ Pattern pattern = Pattern.compile("([a-zA-Z0-9]{2}/{1})?([a-zA-Z0-9]{1,3}[0-9][a-zA-Z0-9]{0,3}[a-zA-Z]{0,3})(/p)? [a-zA-Z]{2}[0-9]{2}[a-zA-Z]{2} [ -~]{1,20}");
+ Matcher matcher = pattern.matcher(temp);
+ /**
+ * "([a-zA-Z0-9]{1,2}\/)?[a-zA-Z0-9]{1,3}[0-9][a-zA-Z0-9]{0,3}[a-zA-Z](\/(p|m))?( )[a-zA-Z]{2}[0-9]{2}[a-zA-Z]{2}[ -~]{0,30}"gm
+ * Thats a line of the show users list
+ */
+ while (matcher.find()) {
+// System.out.println("Chatmember detected: "+ matcher.group() + " " + matcher.start());
+
+ ChatMember member = new ChatMember();
+ String matchedString = matcher.group();
+
+ String[] splittedUserString;
+ splittedUserString = matchedString.split(" ");
+
+ member.setCallSign(splittedUserString[0]);
+ member.setQra(splittedUserString[1]);
+
+ String stringAggregation = "";
+ for (int i = 2; i < splittedUserString.length; i++) {
+ stringAggregation += splittedUserString[i] + " ";
+ }
+ member.setName(stringAggregation);
+
+ System.out.println("Call: " + member.getCallSign() + ", QRA: " + member.getQra() + ", Name: " + member.getName());
+ }
+ }
+
+}
diff --git a/src/main/java/kst4contest/controller/StartChat.java b/src/main/java/kst4contest/controller/StartChat.java
new file mode 100644
index 0000000..4693705
--- /dev/null
+++ b/src/main/java/kst4contest/controller/StartChat.java
@@ -0,0 +1,28 @@
+/**
+ *
+ */
+package kst4contest.controller;
+
+import java.io.IOException;
+
+/**
+ * @author mywire
+ *
+ */
+public class StartChat {
+
+ /**
+ * @param args
+ * @throws InterruptedException
+ * @throws IOException
+ */
+ public static void main(String[] args) throws InterruptedException, IOException {
+
+ System.out.println("[Startchat:] Starting new Chat instance");
+
+ ChatController client = new ChatController();
+ client.execute();
+
+ }
+
+}
diff --git a/src/main/java/kst4contest/controller/UCXLogFileToHashsetParser.java b/src/main/java/kst4contest/controller/UCXLogFileToHashsetParser.java
new file mode 100644
index 0000000..c8a97c8
--- /dev/null
+++ b/src/main/java/kst4contest/controller/UCXLogFileToHashsetParser.java
@@ -0,0 +1,83 @@
+package kst4contest.controller;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.util.HashMap;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import kst4contest.model.ChatMember;
+
+public class UCXLogFileToHashsetParser {
+
+ public BufferedReader fileReader;
+ private final String PTRN_CallSign = "(([a-zA-Z]{1,2}[\\d{1}]?\\/)?(\\d{1}[a-zA-Z][\\d{1}][a-zA-Z]{1,3})((\\/p)|(\\/\\d))?)|(([a-zA-Z0-9]{1,2}[\\d{1}]?\\/)?(([a-zA-Z]{1,2}(\\d{1}[a-zA-Z]{1,4})))((\\/p)|(\\/\\d))?)";
+
+ public UCXLogFileToHashsetParser(String filePathAndName) {
+
+ try {
+ fileReader = new BufferedReader(new InputStreamReader(new FileInputStream(new File(filePathAndName))));
+
+ } catch (FileNotFoundException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+
+ }
+
+ /**
+ * check if a line of the ucxlog-Logfile inhibits a Callsign
+ * returns ChatMember = null, if no frequency found
+ *
+ * @param chatMessage
+ */
+ private ChatMember checkIfLineInhibitsCallSign(String line) {
+
+ Pattern pattern = Pattern.compile(PTRN_CallSign); // TODO: PTRN should depend to category-selection of own stn
+ Matcher matcher = pattern.matcher(line);
+
+ String matchedString = "";
+
+ while (matcher.find()) {
+
+ matchedString = matcher.group();
+// System.out.println("[UCXLogFile:] Processed worked Callsign from file: " + matchedString);
+
+ }
+
+ ChatMember newChatMember = new ChatMember();
+
+ newChatMember.setCallSign(matchedString.toUpperCase());
+
+ return newChatMember;
+
+ }
+
+ /**
+ * Parses an ucxlog-live-file (full qualified path given by constructor
+ * argument), looks by regex for callsigns and builds a hashmap with only one
+ * entry by callsign
+ */
+ public HashMap parse() throws IOException {
+
+ HashMap chatMemberMap = new HashMap();
+
+ String line;
+ while ((line = fileReader.readLine()) != null) {
+// System.out.println("raw: " + line);
+ ChatMember temp = checkIfLineInhibitsCallSign(line);
+
+ if (temp.getCallSign() != "") {
+ chatMemberMap.put(temp.getCallSign(), temp.getCallSign());
+ }
+ }
+// System.out.println(chatMemberMap.size());
+ return chatMemberMap;
+
+ }
+
+}
diff --git a/src/main/java/kst4contest/controller/UserActualizationTask.java b/src/main/java/kst4contest/controller/UserActualizationTask.java
new file mode 100644
index 0000000..e4dbf89
--- /dev/null
+++ b/src/main/java/kst4contest/controller/UserActualizationTask.java
@@ -0,0 +1,326 @@
+package kst4contest.controller;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.TimerTask;
+
+import javafx.collections.ObservableList;
+import kst4contest.model.ChatMember;
+import kst4contest.model.ClusterMessage;
+
+public class UserActualizationTask extends TimerTask {
+
+ private ChatController client;
+
+ public UserActualizationTask(ChatController client) {
+
+ this.client = client;
+
+ }
+
+ @Override
+ public void run() {
+
+ Thread.currentThread().setName("UserActualizationTask");
+
+// System.out.println("[Useract: ] Thread runned now");
+
+// System.out.println("***********************Useract started");
+
+ /**
+ * ******************************************since here: old mechanic for
+ * marking worked stations by .ucx-file
+ */
+
+ HashMap fetchedWorkedSet = new HashMap<>();
+// HashMap fetchedWorkedSetUdpBckup = new HashMap<>();
+
+ File f = new File(this.client.getChatPreferences().getLogsynch_fileBasedWkdCallInterpreterFileNameReadOnly());
+ if (!f.exists() && !f.isDirectory()) {
+ try {
+ f.createNewFile();
+ } catch (IOException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+ }
+
+ UCXLogFileToHashsetParser getWorkedCallsignsOfUCXLogFile = new UCXLogFileToHashsetParser(
+ this.client.getChatPreferences().getLogsynch_fileBasedWkdCallInterpreterFileNameReadOnly());
+
+// UCXLogFileToHashsetParser getWorkedCallsignsOfUDPBackupFile = new UCXLogFileToHashsetParser(
+// this.client.getChatPreferences().getLogSynch_storeWorkedCallSignsFileNameUDPMessageBackup());
+
+ try {
+ fetchedWorkedSet = getWorkedCallsignsOfUCXLogFile.parse();
+// fetchedWorkedSetUdpBckup = getWorkedCallsignsOfUDPBackupFile.parse();
+
+// for (HashMap.Entry entry : fetchedWorkedSet.entrySet()) {
+// String key = (String) entry.getKey();
+// Object value = entry.getValue();
+// System.out.println("key " + key);
+// }
+
+ System.out.println("USERACT: fetchedWorkedSet size: " + fetchedWorkedSet.size());
+// System.out.println("USERACT: fetchedWorkedSetudpbckup size: " + fetchedWorkedSetUdpBckup.size());
+
+ } catch (IOException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+
+ ObservableList praktiKSTActiveUserList = this.client.getLst_chatMemberList();
+
+ for (Iterator iterator = praktiKSTActiveUserList.iterator(); iterator.hasNext();) {
+ ChatMember chatMember = (ChatMember) iterator.next();
+
+// System.out.println(chatMember.getCallSign());
+// System.out.println("USERACT active user list entries " + praktiKSTActiveUserList.size());
+
+ if (fetchedWorkedSet.containsKey(chatMember.getCallSign())) {
+ chatMember.setWorked(true);
+ System.out.println("[USERACT, info:] marking Chatuser " + chatMember.getCallSign()
+ + " as worked, based on READONLY-Logfile.");
+ }
+
+// if (fetchedWorkedSetUdpBckup.containsKey(chatMember.getCallSign())) {
+// chatMember.setWorked(true);
+// System.out.println("[USERACT, info:] marking Chatuser " + chatMember.getCallSign() + " as worked, based on UDPLsnBackup-Logfile.");
+// }
+ }
+
+ ObservableList praktiKSTClusterList = this.client.getLst_clusterMemberList();
+
+ for (Iterator iterator = praktiKSTClusterList.iterator(); iterator.hasNext();) {
+ ClusterMessage clusterMessage = (ClusterMessage) iterator.next();
+
+ if (fetchedWorkedSet.containsKey(clusterMessage.getReceiver().getCallSign())) {
+ clusterMessage.setReceiverWkd(true);
+ System.out.println("[USERACT, info:] marking Clusterspotted "
+ + clusterMessage.getReceiver().getCallSign() + " as worked.");
+ }
+
+// if (fetchedWorkedSetUdpBckup.containsKey(clusterMessage.getReceiver().getCallSign())) {
+// clusterMessage.setReceiverWkd(true);
+// System.out.println("[USERACT, info:] marking Clusterspotted "
+// + clusterMessage.getReceiver().getCallSign() + " as worked.");
+// }
+
+ }
+
+ /**
+ * ******************************************end here: old mechanic for marking
+ * worked stations by .ucx-file
+ *
+ */
+
+ /**
+ * ******************************************since here: new mechanic for
+ * marking worked stations by udp/adif based information
+ */
+// HashMap fetchedWorkedMap = new HashMap<>();
+//
+// fetchedWorkedMap = this.client.getMap_ucxLogInfoWorkedCalls();
+
+// ObservableList praktiKSTActiveUserList1 = this.client.getLst_chatMemberList();
+//
+// for (Iterator iterator = praktiKSTActiveUserList.iterator(); iterator.hasNext();) {
+// ChatMember chatMember = (ChatMember) iterator.next();
+//
+// if (fetchedWorkedMap.containsKey(chatMember.getCallSign())) {
+// chatMember.setWorked(true);
+// System.out.println("[USERACT, info:] marking Chatuser " + chatMember.getCallSign() + " as worked based on UDP Log Info Collector.");
+// }
+// }
+
+// ObservableList praktiKSTClusterList1 = this.client.getLst_clusterMemberList();
+//
+// for (Iterator iterator = praktiKSTClusterList.iterator(); iterator.hasNext();) {
+// ClusterMessage clusterMessage = (ClusterMessage) iterator.next();
+//
+// if (fetchedWorkedMap.containsKey(clusterMessage.getReceiver().getCallSign())) {
+// clusterMessage.setReceiverWkd(true);
+// System.out.println("[USERACT, info:] marking Clusterspotted "
+// + clusterMessage.getReceiver().getCallSign() + " as worked based on UDP Log Info Collector.");
+// }
+//
+// }
+
+//
+// UCXLogFileToHashsetParser getWorkedCallsignsOfUCXLogFile = new UCXLogFileToHashsetParser(
+// "C:\\UcxLog\\Logs\\DO5AMF\\DVU322_I.UCX");
+// try {
+// fetchedWorkedSet = getWorkedCallsignsOfUCXLogFile.parse();
+// } catch (IOException e) {
+// // TODO Auto-generated catch block
+// e.printStackTrace();
+// }
+//
+// ObservableList praktiKSTActiveUserList = this.client.getLst_chatMemberList();
+//
+// for (Iterator iterator = praktiKSTActiveUserList.iterator(); iterator.hasNext();) {
+// ChatMember chatMember = (ChatMember) iterator.next();
+//
+// if (fetchedWorkedSet.containsKey(chatMember.getCallSign())) {
+// chatMember.setWorked(true);
+// System.out.println("[USERACT, info:] marking Chatuser " + chatMember.getCallSign() + " as worked.");
+// }
+// }
+//
+//
+// ObservableList praktiKSTClusterList = this.client.getLst_clusterMemberList();
+//
+// for (Iterator iterator = praktiKSTClusterList.iterator(); iterator.hasNext();) {
+// ClusterMessage clusterMessage = (ClusterMessage) iterator.next();
+//
+// if (fetchedWorkedSet.containsKey(clusterMessage.getReceiver().getCallSign())) {
+// clusterMessage.setReceiverWkd(true);
+// System.out.println("[USERACT, info:] marking Clusterspotted "
+// + clusterMessage.getReceiver().getCallSign() + " as worked.");
+// }
+//
+// }
+
+ /**
+ * ******************************************end here: new mechanic for marking
+ * worked stations by udp/adif based information
+ *
+ */
+
+// System.out.println("[UserActualizationtask:] Userlist actualization will be performed now. "
+// + LocalDateTime.ofInstant(Instant.ofEpochMilli(scheduledExecutionTime()),
+// ZoneId.systemDefault()));
+
+// ChatMessage actualizeUserMsg = new ChatMessage();
+// actualizeUserMsg.setDirectedToServer(true);
+// actualizeUserMsg.setMessage("/show users");
+
+// client.getMessageTXBus().add(actualizeUserMsg);
+
+// Enumeration e = this.client.getChatMemberTable().keys();
+//
+// while (e.hasMoreElements()) {
+// String key = e.nextElement();
+//
+// System.out.println(this.client.getChatMemberTable().get(key).getCallSign() + ", "
+// + this.client.getChatMemberTable().get(key).getQra() + ": "
+// + this.client.getChatMemberTable().get(key).getFrequency());
+//
+// }
+
+// System.out.println("[UserAct]: Show the Cluster with known frequencies now: ");
+//
+// Enumeration e2 = this.client.getdXClusterMemberTable().keys();
+//
+// while (e2.hasMoreElements()) {
+// String key = e2.nextElement();
+//
+// System.out.println(this.client.getdXClusterMemberTable().get(key).getCallSign() + ", "
+// + this.client.getdXClusterMemberTable().get(key).getQra() + ": "
+// + this.client.getdXClusterMemberTable().get(key).getFrequency());
+//
+// }
+
+// for (int i = 0; i < 100; i++) {
+//
+// System.out.print("\n");
+// }
+
+ /**
+ * keeepalive start
+ */
+// ChatMessage keepAliveMSG = new ChatMessage();
+// keepAliveMSG.setMessageText("\r");
+// keepAliveMSG.setMessageDirectedToServer(true);
+//
+// System.out.println(new Utils4KST().time_generateCurrentMMDDhhmmTimeString() + " [UserAct]: Sending keepalive: "
+// + keepAliveMSG.getMessageText());
+// /**
+// * Sending keepalive
+// */
+// this.client.getMessageTXBus().add(keepAliveMSG);
+
+ /**
+ * keeepalive end
+ */
+
+// System.out.println("[UserAct]: Show the Userlist with known frequencies sorted now: ");
+
+// ObservableList userlist = this.client.getLst_chatMemberList();
+
+// for (Iterator iterator = userlist.iterator(); iterator.hasNext();) {
+// ChatMember chatMember = (ChatMember) iterator.next();
+// System.out.println("[Useract] Entry " + this.client.getLst_chatMemberList().indexOf(chatMember) + ": " + chatMember.getCallSign());
+// }
+
+//
+// String chatMembers ="";
+
+// SortedSet keys = new TreeSet<>(this.client.getChatMemberTable().keySet());
+// for (String key : keys) {
+//
+// chatMembers += this.client.getChatMemberTable().get(key).getCallSign() + ", "
+// + this.client.getChatMemberTable().get(key).getName() + " in "
+// + this.client.getChatMemberTable().get(key).getQra() + " @ QRG: "
+// + this.client.getChatMemberTable().get(key).getFrequency() + "\n";
+//
+// System.out.println(this.client.getChatMemberTable().get(key).getCallSign() + ", "
+// + this.client.getChatMemberTable().get(key).getName() + " in "
+// + this.client.getChatMemberTable().get(key).getQra() + " @ QRG: "
+// + this.client.getChatMemberTable().get(key).getFrequency());
+// }
+
+// System.out.println("\n[UserAct]: Show the Clusterlist with known frequencies sorted now: ");
+
+// String dxcMembers ="";
+
+// SortedSet keys2 = new TreeSet<>(this.client.getdXClusterMemberTable().keySet());
+// for (String key : keys2) {
+// System.out.println(this.client.getdXClusterMemberTable().get(key).getCallSign() + " in "
+// + this.client.getdXClusterMemberTable().get(key).getQra() + " @ QRG: "
+// + this.client.getdXClusterMemberTable().get(key).getFrequency());
+//
+// dxcMembers += this.client.getdXClusterMemberTable().get(key).getCallSign() + " in "
+// + this.client.getdXClusterMemberTable().get(key).getQra() + " @ QRG: "
+// + this.client.getdXClusterMemberTable().get(key).getFrequency();
+//
+// }
+
+// File userListLogger = new File(new Utils4KST().time_generateCurrentMMddString() + "_praktiKST_userlist.txt");
+//
+// FileWriter fileWriterRAWChatMSGOut = null;
+//
+// try {
+// fileWriterRAWChatMSGOut = new FileWriter(userListLogger, true);
+// } catch (IOException e1) {
+// // TODO Auto-generated catch block
+// e1.printStackTrace();
+// }
+//
+// BufferedWriter bufwrtrRawMSGOut;
+//
+// bufwrtrRawMSGOut = new BufferedWriter(fileWriterRAWChatMSGOut);
+
+// System.out.println("#######################################" + chatMembers);
+// try {
+// bufwrtrRawMSGOut.write(new Utils4KST().time_generateCurrentMMDDhhmmTimeString() + " " +this.client.getChatMemberTable().size() + " Chatmembers:\n" + chatMembers+ "\n");
+// bufwrtrRawMSGOut.write(new Utils4KST().time_generateCurrentMMDDhhmmTimeString() + " " + this.client.getdXClusterMemberTable().size() + " Clusterentries:\n" + dxcMembers + "\n");
+
+// bufwrtrRawMSGOut.flush();
+
+// } catch (IOException e) {
+// // TODO Auto-generated catch block
+// e.printStackTrace();
+// }
+
+// try {
+// bufwrtrRawMSGOut.close();
+// } catch (IOException e) {
+ // TODO Auto-generated catch block
+// e.printStackTrace();
+// }
+ }
+
+}
diff --git a/src/main/java/kst4contest/controller/Utils4KST.java b/src/main/java/kst4contest/controller/Utils4KST.java
new file mode 100644
index 0000000..65d5eeb
--- /dev/null
+++ b/src/main/java/kst4contest/controller/Utils4KST.java
@@ -0,0 +1,59 @@
+package kst4contest.controller;
+
+import java.text.DateFormat;
+import java.text.SimpleDateFormat;
+import java.time.OffsetDateTime;
+import java.time.ZoneOffset;
+import java.time.format.DateTimeFormatter;
+import java.util.Date;
+import java.util.TimeZone;
+
+public class Utils4KST {
+
+ public long time_generateCurrentEpochTime() {
+
+ OffsetDateTime currentTimeInUtc = OffsetDateTime.now(ZoneOffset.UTC);
+
+// System.out.println(currentTimeInUtc.format(DateTimeFormatter.ofPattern("yyyy-MM-dd hh:mm X")));
+ long millisecondsSinceEpoch = currentTimeInUtc.toInstant().toEpochMilli() / 1000;
+// System.out.println(millisecondsSinceEpoch);
+ return millisecondsSinceEpoch;
+ }
+
+ public String time_generateCurrentMMDDhhmmTimeString() {
+
+ OffsetDateTime currentTimeInUtc = OffsetDateTime.now(ZoneOffset.UTC);
+ return currentTimeInUtc.format(DateTimeFormatter.ofPattern("MM-dd hh:mm"));
+
+ }
+
+ public String time_generateCurrentMMddString() {
+
+ OffsetDateTime currentTimeInUtc = OffsetDateTime.now(ZoneOffset.UTC);
+ return currentTimeInUtc.format(DateTimeFormatter.ofPattern("MM-dd"));
+
+ }
+
+ public String time_convertEpochToReadable(String epochFromServer) {
+
+ long epoch = Long.parseLong(epochFromServer);
+// Instant instant = Instant.ofEpochSecond(epoch);
+
+ Date date = new Date(epoch * 1000L);
+ DateFormat format = new SimpleDateFormat("dd.MM HH:mm:ss");
+ format.setTimeZone(TimeZone.getTimeZone("Etc/UTC"));
+ String formatted = format.format(date);
+
+// System.out.println("UTIL " + formatted);
+
+ return formatted;
+
+ }
+
+ public Date time_generateActualTimeInDateFormat() {
+ Date date = new Date(time_generateCurrentEpochTime() * 1000L);
+ return date;
+
+ }
+
+}
diff --git a/src/main/java/kst4contest/controller/WriteThread.java b/src/main/java/kst4contest/controller/WriteThread.java
new file mode 100644
index 0000000..957bb22
--- /dev/null
+++ b/src/main/java/kst4contest/controller/WriteThread.java
@@ -0,0 +1,255 @@
+package kst4contest.controller;
+
+import java.io.*;
+import java.net.*;
+
+import kst4contest.model.ChatMessage;
+
+/**
+ * This thread is responsible for sending content to the chat. As we only use
+ * the tx function, there is no content in run() method
+ *
+ *
+ */
+public class WriteThread extends Thread {
+ private PrintWriter writer;
+ private Socket socket;
+ private ChatController client;
+ private OutputStream output;
+
+ private ChatMessage messageTextRaw;
+
+ public WriteThread(Socket socket, ChatController client) throws InterruptedException {
+ this.socket = socket;
+ this.client = client;
+
+ try {
+ output = socket.getOutputStream();
+ writer = new PrintWriter(output, true);
+ } catch (IOException ex) {
+ System.out.println("Error getting output stream: " + ex.getMessage());
+ ex.printStackTrace();
+ }
+ }
+
+ /**
+ * This method is used to send a message to the server, raw formatted. E.g. for
+ * the keepalive message.
+ *
+ * @param messageToServer
+ * @throws InterruptedException
+ */
+ public void tx(ChatMessage messageToServer) throws InterruptedException {
+
+// writer.println(messageToServer.getMessage()); //kst4contest.test 4 23001
+// writer.flush(); //kst4contest.test 4 23001
+ System.out.println(messageToServer.getMessageText() + "< sended to the writer");
+ writer.println(messageToServer.getMessageText());
+
+ }
+
+ /**
+ * This method gets a textmessage to the chat and adds some characters to hit
+ * the neccessarry format to send a message in the on4kst chat either to another
+ * station or to the public.
+ *
+ * @param messageToServer
+ * @throws InterruptedException
+ */
+ public void txKSTFormatted(ChatMessage messageToServer) throws InterruptedException {
+
+// writer.println(messageToServer.getMessageText());
+ messageTextRaw = messageToServer;
+
+ try {
+
+ messageTextRaw = client.getMessageTXBus().take();
+// this.client.getmesetChatsetServerready(true);
+
+ } catch (InterruptedException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+
+ String messageLine = messageTextRaw.getMessageText();
+
+ if (messageTextRaw.isMessageDirectedToServer()) {
+ /**
+ * We have to check if we only commands the server (keepalive) or want do talk
+ * to the community
+ */
+
+ try {
+ tx(messageTextRaw);
+ System.out.println("BUS: tx: " + messageTextRaw.getMessageText());
+
+ } catch (InterruptedException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+
+ } else {
+
+ ChatMessage ownMSG = new ChatMessage();
+
+// ownMSG.setMessageText(
+// "MSG|" + this.client.getCategory().getCategoryNumber() + "|0|" + messageLine + "|0|");
+
+ ownMSG.setMessageText("MSG|" + this.client.getChatPreferences().getLoginChatCategory().getCategoryNumber()
+ + "|0|" + messageLine + "|0|");
+
+ try {
+ tx(ownMSG);
+ System.out.println("BUS: tx: " + ownMSG.getMessageText());
+
+ } catch (InterruptedException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+ }
+
+ if (messageTextRaw.equals("/QUIT")) {
+ try {
+ this.client.getReadThread().terminateConnection();
+ this.client.getReadThread().interrupt();
+ this.client.getWriteThread().terminateConnection();
+ this.client.getWriteThread().interrupt();
+ this.interrupt();
+
+ } catch (IOException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+ }
+
+ }
+
+ public boolean terminateConnection() throws IOException {
+
+ this.output.close();
+ this.socket.close();
+
+ return true;
+ }
+
+ public void run() {
+ Thread.currentThread().setName("WriteToTelnetThread");
+
+ while (true) {
+ try {
+ messageTextRaw = client.getMessageTXBus().take();
+
+ if (messageTextRaw.getMessageText().equals("POISONPILL_KILLTHREAD")
+ && messageTextRaw.getMessageSenderName().equals("POISONPILL_KILLTHREAD")) {
+ client.getMessageRXBus().clear();
+ this.interrupt();
+ break;
+ } else {
+ String messageLine = messageTextRaw.getMessageText();
+
+ if (messageTextRaw.isMessageDirectedToServer()) {
+ /**
+ * We have to check if we only commands the server (keepalive) or want do talk
+ * to the community
+ */
+
+ try {
+ tx(messageTextRaw);
+ System.out.println("BUS: tx: " + messageTextRaw.getMessageText());
+
+ } catch (InterruptedException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+
+ } else {
+
+ ChatMessage ownMSG = new ChatMessage();
+
+// ownMSG.setMessageText(
+// "MSG|" + this.client.getCategory().getCategoryNumber() + "|0|" + messageLine + "|0|");
+
+ ownMSG.setMessageText(
+ "MSG|" + this.client.getChatPreferences().getLoginChatCategory().getCategoryNumber() + "|0|"
+ + messageLine + "|0|");
+
+ try {
+ tx(ownMSG);
+ System.out.println("BUS: tx: " + ownMSG.getMessageText());
+
+ } catch (InterruptedException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+ }
+ }
+
+ System.out.println("WritheTh: got message out of the queue: " + messageTextRaw.getMessageText());
+
+// this.client.getmesetChatsetServerready(true);
+
+ } catch (InterruptedException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ client.getMessageTXBus().clear();
+ }
+
+// String messageLine = messageTextRaw.getMessageText();
+//
+// if (messageTextRaw.isMessageDirectedToServer()) {
+// /**
+// * We have to check if we only commands the server (keepalive) or want do talk
+// * to the community
+// */
+//
+// try {
+// tx(messageTextRaw);
+// System.out.println("BUS: tx: " + messageTextRaw.getMessageText());
+//
+// } catch (InterruptedException e) {
+// // TODO Auto-generated catch block
+// e.printStackTrace();
+// }
+//
+// } else {
+//
+// ChatMessage ownMSG = new ChatMessage();
+//
+//// ownMSG.setMessageText(
+//// "MSG|" + this.client.getCategory().getCategoryNumber() + "|0|" + messageLine + "|0|");
+//
+// ownMSG.setMessageText(
+// "MSG|" + this.client.getChatPreferences().getLoginChatCategory().getCategoryNumber() + "|0|"
+// + messageLine + "|0|");
+//
+// try {
+// tx(ownMSG);
+// System.out.println("BUS: tx: " + ownMSG.getMessageText());
+//
+// } catch (InterruptedException e) {
+// // TODO Auto-generated catch block
+// e.printStackTrace();
+// }
+// }
+ }
+// if (messageTextRaw.equals("/QUIT")) {
+// try {
+// this.client.getReadThread().terminateConnection();
+// this.client.getReadThread().interrupt();
+// this.client.getWriteThread().terminateConnection();
+// this.client.getWriteThread().interrupt();
+// this.interrupt();
+//
+// } catch (IOException e) {
+// // TODO Auto-generated catch block
+// e.printStackTrace();
+// }
+// }
+
+
+// while (true) {
+//
+// }
+
+ }
+}
diff --git a/src/main/java/kst4contest/controller/keepAliveMessageSenderTask.java b/src/main/java/kst4contest/controller/keepAliveMessageSenderTask.java
new file mode 100644
index 0000000..974e92d
--- /dev/null
+++ b/src/main/java/kst4contest/controller/keepAliveMessageSenderTask.java
@@ -0,0 +1,36 @@
+package kst4contest.controller;
+
+import java.util.TimerTask;
+
+import kst4contest.model.ChatMessage;
+
+public class keepAliveMessageSenderTask extends TimerTask {
+
+ private ChatController client;
+
+ public keepAliveMessageSenderTask(ChatController client) {
+
+ this.client = client;
+
+ }
+
+ @Override
+ public void run() {
+
+ Thread.currentThread().setName("KeepAliveMessageSenderTask");
+
+// System.out.println("[keepalive: ] Thread runned now");
+
+ ChatMessage keepAliveMSG = new ChatMessage();
+ keepAliveMSG.setMessageText("\r");
+ keepAliveMSG.setMessageDirectedToServer(true);
+
+// System.out.println(new Utils4KST().time_generateCurrentMMDDhhmmTimeString() + " [keepaliveTask]: Sending keepalive: "
+// + keepAliveMSG.getMessageText());
+ /**
+ * Sending keepalive
+ */
+ this.client.getMessageTXBus().add(keepAliveMSG);
+ }
+
+}
diff --git a/src/main/java/kst4contest/locatorUtils/Angle.java b/src/main/java/kst4contest/locatorUtils/Angle.java
new file mode 100644
index 0000000..f2b5fa2
--- /dev/null
+++ b/src/main/java/kst4contest/locatorUtils/Angle.java
@@ -0,0 +1,140 @@
+package kst4contest.locatorUtils;
+
+import java.math.BigDecimal;
+import java.math.MathContext;
+
+/**
+ * Allow for easy use of angles without messy conversions to and from degrees
+ * and radians.
+ *
+ * @author Paul Picazo
+ *
+ */
+public class Angle {
+
+ /**
+ * pi.
+ */
+ private static final BigDecimal BIG_PI = new BigDecimal(Math.PI);
+
+ /**
+ * representation of the angle in radians.
+ */
+ protected BigDecimal radians;
+
+ /**
+ * no argument constructor.
+ *
+ */
+ public Angle() {
+ }
+
+ /**
+ * @param radiansIn
+ * angle in radians
+ */
+ public Angle(final double radiansIn) {
+ this.radians = new BigDecimal(radiansIn);
+ }
+
+ @Override
+ public int hashCode() {
+ final int prime = 7;
+ if (radians == null) {
+ return prime;
+ }
+ return prime + radians.hashCode();
+ }
+
+ @Override
+ public boolean equals(final Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj == null) {
+ return false;
+ }
+ if (!(obj instanceof Angle)) {
+ return false;
+ }
+ return ((Angle) obj).hashCode() == this.hashCode();
+ }
+
+ /**
+ * @param degrees
+ * angle in degrees
+ * @return angle
+ */
+ public static Angle fromDegrees(final double degrees) {
+ Angle angle = new Angle();
+ angle.radians = degreesToRadians(new BigDecimal(degrees),
+ MathContext.DECIMAL128);
+ return angle;
+ }
+
+ /**
+ * @param radiansIn
+ * the radians to set
+ */
+ public void setRadians(final double radiansIn) {
+ this.radians = new BigDecimal(radiansIn);
+ }
+
+ /**
+ * @return the radians
+ */
+ public double getRadians() {
+ return radians.doubleValue();
+ }
+
+ /**
+ * @return degrees
+ */
+ public double toDegrees() {
+ return radiansToDegrees(this.radians, MathContext.DECIMAL128).doubleValue();
+ }
+
+ /**
+ * @param radians
+ * input
+ * @return degrees
+ */
+ public static double radiansToDegrees(final double radians) {
+ return radiansToDegrees(new BigDecimal(radians), MathContext.DECIMAL128)
+ .doubleValue();
+ }
+
+ /**
+ * @param degrees
+ * input
+ * @return radians
+ */
+ public static double degreesToRadians(final double degrees) {
+ return degreesToRadians(new BigDecimal(degrees), MathContext.DECIMAL128)
+ .doubleValue();
+ }
+
+ /**
+ * @param radians
+ * input
+ * @param mc
+ * MathContext for division
+ * @return degrees
+ */
+ protected static BigDecimal radiansToDegrees(final BigDecimal radians,
+ final MathContext mc) {
+ return radians.multiply(new BigDecimal(180)).divide(BIG_PI, mc);
+ }
+
+ /**
+ * @param degrees
+ * input
+ * @param mc
+ * MathContext for division
+ * @return radians
+ */
+ protected static BigDecimal degreesToRadians(final BigDecimal degrees,
+ final MathContext mc) {
+ return degrees.divide(new BigDecimal(180), mc).multiply(BIG_PI);
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/kst4contest/locatorUtils/Latitude.java b/src/main/java/kst4contest/locatorUtils/Latitude.java
new file mode 100644
index 0000000..b21ec60
--- /dev/null
+++ b/src/main/java/kst4contest/locatorUtils/Latitude.java
@@ -0,0 +1,48 @@
+package kst4contest.locatorUtils;
+
+import java.math.BigDecimal;
+import java.math.MathContext;
+
+/**
+ * Latitude.
+ *
+ * @author Paul Picazo
+ *
+ */
+public class Latitude extends Angle {
+
+ @Override
+ public boolean equals(final Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj == null) {
+ return false;
+ }
+ if (!(obj.getClass() == this.getClass())) {
+ return false;
+ }
+ return ((Latitude) obj).hashCode() == this.hashCode();
+ }
+
+ @Override
+ public int hashCode() {
+ final int prime = 37;
+ if (radians == null) {
+ return prime;
+ }
+ return prime + radians.hashCode();
+ }
+
+ /**
+ * @param degrees input
+ * @return latitude object
+ */
+ public static Latitude fromDegrees(final double degrees) {
+ Latitude latitude = new Latitude();
+ latitude.radians = Angle.degreesToRadians(new BigDecimal(degrees),
+ MathContext.DECIMAL128);
+ return latitude;
+ }
+
+}
\ No newline at end of file
diff --git a/src/main/java/kst4contest/locatorUtils/Location.java b/src/main/java/kst4contest/locatorUtils/Location.java
new file mode 100644
index 0000000..6903c55
--- /dev/null
+++ b/src/main/java/kst4contest/locatorUtils/Location.java
@@ -0,0 +1,305 @@
+package kst4contest.locatorUtils;
+
+/**
+ * Location class with methods allowing conversion to and from Maidenhead
+ * locator (grid squares) based off of
+ * "Conversion Between Geodetic and Grid Locator Systems" by Edmund T. Tyson,
+ * N5JTY in QST January 1989, pp. 29-30, 43
+ *
+ * @author Paul Picazo
+ *
+ */
+public class Location {
+
+ /**
+ * Average earth radius in kilometers, IUGG definition.
+ */
+ private static final double AVG_EARTH_RADIUS_KM = 6371.009;
+ /**
+ * Average earth radius in statute miles, IUGG definition.
+ */
+ private static final double AVG_EARTH_RADIUS_SM = 3958.761;
+ /**
+ * Average earth radius in nautical miles, IUGG definition.
+ */
+ private static final double AVG_EARTH_RADIUS_NM = 3440.069;
+
+ /**
+ * latitude of location.
+ */
+ private Latitude latitude;
+ /**
+ * longitude of location.
+ */
+ private Longitude longitude;
+
+ /**
+ * No argument constructor.
+ *
+ */
+ public Location() {
+ this.latitude = new Latitude();
+ this.longitude = new Longitude();
+ }
+
+ /**
+ * @param latitudeIn
+ * initial latitude
+ * @param longitudeIn
+ * initial longitude
+ */
+ public Location(final Latitude latitudeIn, final Longitude longitudeIn) {
+ this.latitude = latitudeIn;
+ this.longitude = longitudeIn;
+ }
+
+ /**
+ * @param latitudeIn
+ * initial latitude
+ * @param longitudeIn
+ * initial longitude
+ */
+ public Location(final double latitudeIn, final double longitudeIn) {
+ this.latitude = Latitude.fromDegrees(latitudeIn);
+ this.longitude = Longitude.fromDegrees(longitudeIn);
+ }
+
+ /**
+ * @param maidenhead
+ * used construct location from maidenhead locator string
+ */
+ public Location(final String maidenhead) {
+ this.latitude = extractLat(maidenhead);
+ this.longitude = extractLon(maidenhead);
+ }
+
+ @Override
+ public boolean equals(final Object obj) {
+ if (obj instanceof Location) {
+ return ((Location) obj).hashCode() == this.hashCode();
+ }
+ return false;
+ }
+
+ @Override
+ public int hashCode() {
+ int hash = 1;
+ hash = hash * 17 + this.latitude.hashCode();
+ hash = hash * 31 + this.longitude.hashCode();
+ return hash;
+ }
+
+ /**
+ * @return maidenhead locator string
+ */
+ public String toMaidenhead() {
+ return toMaidenhead(this.latitude.toDegrees(),
+ this.longitude.toDegrees());
+ }
+
+ /**
+ * @param latitudeIn
+ * latitude component of locator string
+ * @param longitudeIn
+ * longitude component of locator string
+ * @return maidenhead locator string
+ */
+ public static String toMaidenhead(final double latitudeIn,
+ final double longitudeIn) {
+
+ double longitude = longitudeIn + 180;
+ longitude /= 2;
+ char lonFirst = (char) ('A' + (longitude / 10));
+ char lonSecond = (char) ('0' + longitude % 10);
+ char lonThird = (char) ('A' + (longitude % 1) * 24);
+
+ double latitude = latitudeIn + 90;
+ char latFirst = (char) ('A' + (latitude / 10));
+ char latSecond = (char) ('0' + latitude % 10);
+ char latThird = (char) ('A' + (latitude % 1) * 24);
+
+ StringBuilder sb = new StringBuilder();
+ sb.append(lonFirst);
+ sb.append(latFirst);
+ sb.append(lonSecond);
+ sb.append(latSecond);
+ sb.append(("" + lonThird).toLowerCase());
+ sb.append(("" + latThird).toLowerCase());
+
+ return sb.toString();
+ }
+
+ /**
+ * @param maidenheadIn
+ * locator string to be converted
+ * @return latitude
+ */
+ public static Latitude extractLat(final String maidenheadIn) {
+ String maidenhead = maidenheadIn.toUpperCase();
+ double latitude = -90 + 10 * (maidenhead.charAt(1) - 'A')
+ + (maidenhead.charAt(3) - '0') + 2.5 / 60
+ * (maidenhead.charAt(5) - 'A') + 2.5 / 60 / 2;
+ return Latitude.fromDegrees(latitude);
+ }
+
+ /**
+ * @param maidenheadIn
+ * locator string to be converted
+ * @return longitude
+ */
+ public static Longitude extractLon(final String maidenheadIn) {
+ String maidenhead = maidenheadIn.toUpperCase();
+ double longitude = -180 + 20 * (maidenhead.charAt(0) - 'A') + 2
+ * (maidenhead.charAt(2) - '0') + 5.0 / 60
+ * (maidenhead.charAt(4) - 'A') + 5.0 / 60 / 2;
+ return Longitude.fromDegrees(longitude);
+ }
+
+ /**
+ * @return latitude
+ */
+ public Latitude getLatitude() {
+ return latitude;
+ }
+
+ /**
+ * @return longitude
+ */
+ public Longitude getLongitude() {
+ return longitude;
+ }
+
+ /**
+ * @param latitudeIn
+ * north/south component of location
+ */
+ public void setLatitude(final Latitude latitudeIn) {
+ this.latitude = latitudeIn;
+ }
+
+ /**
+ * @param longitudeIn
+ * east/west component of location
+ */
+ public void setLongitude(final Longitude longitudeIn) {
+ this.longitude = longitudeIn;
+ }
+
+ /**
+ * @param loc2
+ * second location
+ * @return great circle distance in miles
+ */
+ public double getDistanceMi(final Location loc2) {
+ return getDistanceMi(this, loc2);
+ }
+
+ /**
+ * @param loc2
+ * second location
+ * @return great circle distance in kilometers
+ */
+ public double getDistanceKm(final Location loc2) {
+ return getDistanceKm(this, loc2);
+ }
+
+ /**
+ * @param loc2
+ * second location
+ * @return great circle distance in nautical miles
+ */
+ public double getDistanceNm(final Location loc2) {
+ return getDistanceNm(this, loc2);
+ }
+
+ /**
+ * @param loc1
+ * first location
+ * @param loc2
+ * second location
+ * @return great circle distance in miles
+ */
+ public static double getDistanceMi(final Location loc1, final Location loc2) {
+ return getDistance(loc1, loc2, AVG_EARTH_RADIUS_SM);
+ }
+
+ /**
+ * @param loc1
+ * first location
+ * @param loc2
+ * second location
+ * @return great circle distance in kilometers
+ */
+ private static double getDistanceKm(final Location loc1, final Location loc2) {
+ return getDistance(loc1, loc2, AVG_EARTH_RADIUS_KM);
+ }
+
+ /**
+ * @param loc1
+ * first location
+ * @param loc2
+ * second location
+ * @return great circle distance in nautical miles
+ */
+ private static double getDistanceNm(final Location loc1, final Location loc2) {
+ return getDistance(loc1, loc2, AVG_EARTH_RADIUS_NM);
+ }
+
+ /**
+ * @param loc1
+ * first location
+ * @param loc2
+ * second location
+ * @param radius
+ * radius of the earth in the units desired for result
+ * @return great circle distance between the two locations, result units same
+ * of the radius units
+ */
+ private static double getDistance(final Location loc1, final Location loc2,
+ final double radius) {
+ if (loc1.equals(loc2)) {
+ return 0;
+ }
+ return Math.acos(Math.sin(loc1.getLatitude().getRadians())
+ * Math.sin(loc2.getLatitude().getRadians())
+ + Math.cos(loc1.getLatitude().getRadians())
+ * Math.cos(loc2.getLatitude().getRadians())
+ * Math.cos(loc2.getLongitude().getRadians()
+ - loc1.getLongitude().getRadians()))
+ * radius;
+ }
+
+ /**
+ * @param loc2
+ * destination location
+ * @return bearing in degrees
+ */
+ public double getBearing(final Location loc2) {
+ return getBearing(this, loc2);
+ }
+
+ /**
+ * @param loc1
+ * source location
+ * @param loc2
+ * destination location
+ * @return bearing in degrees
+ */
+ public static double getBearing(final Location loc1, final Location loc2) {
+ if (loc1.equals(loc2)) {
+ return Double.NaN;
+ }
+
+ double dLon = loc2.getLongitude().getRadians()
+ - loc1.getLongitude().getRadians();
+
+ double y = Math.sin(dLon)
+ * Math.cos(loc2.getLatitude().getRadians());
+ double x = Math.cos(loc1.getLatitude().getRadians())
+ * Math.sin(loc2.getLatitude().getRadians())
+ - Math.sin(loc1.getLatitude().getRadians())
+ * Math.cos(loc2.getLatitude().getRadians())
+ * Math.cos(dLon);
+ return (Angle.radiansToDegrees(Math.atan2(y, x)) + 360) % 360;
+ }
+}
diff --git a/src/main/java/kst4contest/locatorUtils/Longitude.java b/src/main/java/kst4contest/locatorUtils/Longitude.java
new file mode 100644
index 0000000..8bf2778
--- /dev/null
+++ b/src/main/java/kst4contest/locatorUtils/Longitude.java
@@ -0,0 +1,49 @@
+package kst4contest.locatorUtils;
+
+import java.math.BigDecimal;
+import java.math.MathContext;
+
+/**
+ * Longitude.
+ *
+ * @author Paul Picazo
+ *
+ */
+public class Longitude extends Angle {
+
+ @Override
+ public boolean equals(final Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj == null) {
+ return false;
+ }
+ if (!(obj.getClass() == this.getClass())) {
+ return false;
+ }
+ return ((Longitude) obj).hashCode() == this.hashCode();
+ }
+
+ @Override
+ public int hashCode() {
+ final int prime = 29;
+ if (radians == null) {
+ return prime;
+ }
+ return prime + radians.hashCode();
+ }
+
+ /**
+ * @param degrees input
+ * @return longitude object
+ */
+ public static Longitude fromDegrees(final double degrees) {
+ Longitude longitude = new Longitude();
+ longitude.radians = Angle.degreesToRadians(new BigDecimal(degrees),
+ MathContext.DECIMAL128);
+ return longitude;
+ }
+
+
+}
\ No newline at end of file
diff --git a/src/main/java/kst4contest/model/AirPlane.java b/src/main/java/kst4contest/model/AirPlane.java
new file mode 100644
index 0000000..2aeed03
--- /dev/null
+++ b/src/main/java/kst4contest/model/AirPlane.java
@@ -0,0 +1,48 @@
+package kst4contest.model;
+
+public class AirPlane {
+
+ String apCallSign, apSizeCategory;
+ int distanceKm, potential, arrivingDurationMinutes;
+ public String getApCallSign() {
+ return apCallSign;
+ }
+ public void setApCallSign(String apCallSign) {
+ this.apCallSign = apCallSign;
+ }
+ public String getApSizeCategory() {
+ return apSizeCategory;
+ }
+ public void setApSizeCategory(String apSizeCategory) {
+ this.apSizeCategory = apSizeCategory;
+ }
+ public int getDistanceKm() {
+ return distanceKm;
+ }
+ public void setDistanceKm(int distanceKm) {
+ this.distanceKm = distanceKm;
+ }
+ public int getPotential() {
+ return potential;
+ }
+ public void setPotential(int potential) {
+ this.potential = potential;
+ }
+ public int getArrivingDurationMinutes() {
+ return arrivingDurationMinutes;
+ }
+ public void setArrivingDurationMinutes(int arrivingDurationMinutes) {
+ this.arrivingDurationMinutes = arrivingDurationMinutes;
+ }
+
+ @Override
+ public String toString() {
+
+ String toStringString = "\n";
+
+ toStringString += this.apCallSign + ", category: " + this.apSizeCategory + ", distance: " + this.getDistanceKm() + ", potential: " + this.potential + ", distance: " + this.getDistanceKm() + ", duration " + this.arrivingDurationMinutes + "" ;
+
+ return toStringString;
+ }
+
+}
diff --git a/src/main/java/kst4contest/model/AirPlaneReflectionInfo.java b/src/main/java/kst4contest/model/AirPlaneReflectionInfo.java
new file mode 100644
index 0000000..fbb7538
--- /dev/null
+++ b/src/main/java/kst4contest/model/AirPlaneReflectionInfo.java
@@ -0,0 +1,62 @@
+package kst4contest.model;
+
+import javafx.collections.ObservableList;
+
+public class AirPlaneReflectionInfo {
+
+ String date;
+ ChatMember sender, receiver;
+ int airPlanesReachableCntr;
+ ObservableList risingAirplanes;
+
+ public String getDate() {
+ return date;
+ }
+
+ public void setDate(String date) {
+ this.date = date;
+ }
+
+ public ChatMember getSender() {
+ return sender;
+ }
+
+ public void setSender(ChatMember sender) {
+ this.sender = sender;
+ }
+
+ public ChatMember getReceiver() {
+ return receiver;
+ }
+
+ public void setReceiver(ChatMember receiver) {
+ this.receiver = receiver;
+ }
+
+ public int getAirPlanesReachableCntr() {
+ return airPlanesReachableCntr;
+ }
+
+ public void setAirPlanesReachableCntr(int airPlanesReachableCntr) {
+ this.airPlanesReachableCntr = airPlanesReachableCntr;
+ }
+
+ public ObservableList getRisingAirplanes() {
+ return risingAirplanes;
+ }
+
+ public void setRisingAirplanes(ObservableList risingAirplanes) {
+ this.risingAirplanes = risingAirplanes;
+ }
+
+ @Override
+ public String toString() {
+ String toStringString = "";
+
+
+ toStringString += this.sender.getCallSign() + " > " + this.getReceiver().getCallSign() + " at " + this.getDate() + " " + this.airPlanesReachableCntr + " planes: " + this.getRisingAirplanes().toString();
+
+ return toStringString;
+ }
+
+}
diff --git a/src/main/java/kst4contest/model/ChatCategory.java b/src/main/java/kst4contest/model/ChatCategory.java
new file mode 100644
index 0000000..85630ac
--- /dev/null
+++ b/src/main/java/kst4contest/model/ChatCategory.java
@@ -0,0 +1,290 @@
+package kst4contest.model;
+
+/* 50/70 MHz..............1
144/432 MHz............2
Microwave..............3
+* EME/JT65...............4
Low Band...............5
50 MHz IARU Region 3...6
50
+* MHz IARU Region 2...7
144/432 MHz IARU R 2...8
144/432 MHz IARU R 3...9
kHz
+* (2000-630m).......10
Warc (30,17,12m)......11
28 MHz................12
+*
+* TODO: LowBand and khz need to be continued later...
+*/
+public class ChatCategory {
+
+ @Override
+ public String toString() {
+ String toStringString ="";
+
+ toStringString = this.getCategoryNumber()+": " + this.getChatCategoryName(categoryNumber);
+
+ return toStringString;
+ }
+
+ public static final int FIFTYSEVENTYMHz = 1;
+ public static final int VUHF = 2;
+ public static final int MICROWAVE = 3;
+ public static final int EMEJT65 = 4;
+ public static final int LOWBAND = 5;
+ public static final int FIFTYR3 = 6;
+ public static final int FIFTYR2 = 7;
+ public static final int VUHFR2 = 8;
+ public static final int VUHFR3 = 9;
+ public static final int KHZ2KM630M = 10;
+ public static final int WARC301712 = 11;
+ public static final int TENMeter = 12;
+
+ private int categoryNumber;
+
+ public int[] getPossibleCategoryNumbers() {
+ int[] possibleVals = new int[12];
+ possibleVals[0] = 1;
+ possibleVals[1] = 2;
+ possibleVals[2] = 3;
+ possibleVals[3] = 4;
+ possibleVals[4] = 5;
+ possibleVals[5] = 6;
+ possibleVals[6] = 7;
+ possibleVals[7] = 8;
+ possibleVals[8] = 9;
+ possibleVals[9] = 10;
+ possibleVals[10] = 11;
+ possibleVals[11] = 12;
+
+ return possibleVals;
+ }
+
+ public ChatCategory(int setThiscategoryNumber) {
+ this.categoryNumber = setThiscategoryNumber;
+ }
+
+
+
+
+
+ public int getCategoryNumber() {
+ return categoryNumber;
+ }
+
+
+
+
+
+ public void setCategoryNumber(int categoryNumber) {
+ this.categoryNumber = categoryNumber;
+ }
+
+
+
+
+
+ /**
+ * Returns an Array of int with possible frequency prefixes, due to in the chat
+ * normally the following format is used (not ever):
+ * "pse listen at .325"
+ *
+ *
+ * 50/70 MHz..............1
+ * 144/432 MHz............2
+ * Microwave..............3
+ * EME/JT65...............4
+ * Low Band...............5
+ * 50 MHz IARU Region 3...6
+ * 50 MHz IARU Region 2...7
+ * 144/432 MHz IARU R 2...8
+ * 144/432 MHz IARU R 3...9
+ * kHz (2000-630m).......10
+ * Warc (30,17,12m)......11
+ * 28 MHz................12
+ * TODO: LowBand and khz need to be continued later...
+ *
+ * @param int chatCategory (KST ChatCategory)
+ * @return Array of int with frequency prefixes, beginning with the lowest band
+ * (e.G. int[2] = [144, 430] for VUHF)
+ */
+ public int[] getChatCategoryFrequencyPrefix(int chatCategory) {
+ int[] answer = new int[1];
+ answer[0] = 0;
+
+ switch (chatCategory) {
+
+ case ChatCategory.FIFTYSEVENTYMHz: {
+ answer = new int[2];
+ answer[0] = 50;
+ answer[1] = 70;
+ return answer;
+
+ }
+
+ case ChatCategory.VUHF: {
+ answer = new int[2];
+ answer[0] = 144;
+ answer[1] = 432;
+ return answer;
+
+ }
+ case ChatCategory.VUHFR2: {
+ answer = new int[2];
+ answer[0] = 144;
+ answer[1] = 432;
+ return answer;
+
+ }
+ case ChatCategory.VUHFR3: {
+ answer = new int[2];
+ answer[0] = 144;
+ answer[1] = 432;
+ return answer;
+
+ }
+
+ case ChatCategory.EMEJT65: { // 1296.2
+ answer = new int[13];
+ answer[0] = 144;
+ answer[1] = 432;
+ answer[2] = 1296;
+ answer[3] = 3400;
+ answer[4] = 5760;
+ answer[5] = 10368;
+ answer[6] = 24048;
+ answer[7] = 47000;
+ answer[8] = 77500;
+ answer[9] = 122250;
+ answer[10] = 134928;
+ answer[11] = 241920;
+ answer[12] = 453000;
+ return answer;
+
+ }
+
+ case ChatCategory.MICROWAVE: { // 1296.2
+ answer = new int[13];
+ answer[0] = 144;
+ answer[1] = 432;
+ answer[2] = 1296;
+ answer[3] = 3400;
+ answer[4] = 5760;
+ answer[5] = 10368;
+ answer[6] = 24048;
+ answer[7] = 47000;
+ answer[8] = 77500;
+ answer[9] = 122250;
+ answer[10] = 134928;
+ answer[11] = 241920;
+ answer[12] = 453000;
+ return answer;
+
+ }
+
+ case ChatCategory.TENMeter: {
+ answer = new int[1];
+ answer[0] = 28;
+ return answer;
+
+ }
+
+ case ChatCategory.WARC301712: {
+ answer = new int[3];
+ answer[0] = 10;
+ answer[1] = 18;
+ answer[2] = 24;
+ return answer;
+
+ }
+ default:
+ return answer;
+ }
+
+ };
+
+ /**
+ * Returns an Array of int with possible frequency prefixes, due to in the chat
+ * normally the following format is used (not ever):
+ * "pse listen at .325"
+ *
+ *
+ * 50/70 MHz..............1
+ * 144/432 MHz............2
+ * Microwave..............3
+ * EME/JT65...............4
+ * Low Band...............5
+ * 50 MHz IARU Region 3...6
+ * 50 MHz IARU Region 2...7
+ * 144/432 MHz IARU R 2...8
+ * 144/432 MHz IARU R 3...9
+ * kHz (2000-630m).......10
+ * Warc (30,17,12m)......11
+ * 28 MHz................12
+ * TODO: LowBand and khz need to be continued later...
+ *
+ * @param int chatCategory (KST ChatCategory)
+ * @return Array of int with frequency prefixes, beginning with the lowest band
+ * (e.G. int[2] = [144, 430] for VUHF)
+ */
+ public String getChatCategoryName(int chatCategory) {
+
+
+ switch (chatCategory) {
+
+ case ChatCategory.FIFTYSEVENTYMHz: {
+
+ return "50/70 MHz";
+
+ }
+
+ case ChatCategory.FIFTYR2: {
+ return "50 MHz IARU Region 2";
+ }
+
+ case ChatCategory.FIFTYR3: {
+ return "50 MHz IARU Region 3";
+ }
+
+
+ case ChatCategory.LOWBAND: {
+ return "Low Band";
+ }
+
+ case ChatCategory.KHZ2KM630M: {
+ return "kHz (2000-630m)";
+ }
+
+
+ case ChatCategory.VUHF: {
+
+ return "144/432 MHz";
+
+ }
+ case ChatCategory.VUHFR2: {
+ return "144/432 MHz IARU R 2";
+
+ }
+ case ChatCategory.VUHFR3: {
+ return "144/432 MHz IARU R 3";
+
+ }
+
+ case ChatCategory.EMEJT65: { // 1296.2
+
+ return "EME/JT65";
+
+ }
+
+ case ChatCategory.MICROWAVE: { // 1296.2
+ return "Microwave";
+
+ }
+
+ case ChatCategory.TENMeter: {
+ return "28 MHz";
+
+ }
+
+ case ChatCategory.WARC301712: {
+ return "Warc (30,17,12m)";
+
+ }
+ default:
+ return "ERRROR: unknown";
+ }
+
+ };
+
+}
diff --git a/src/main/java/kst4contest/model/ChatMember.java b/src/main/java/kst4contest/model/ChatMember.java
new file mode 100644
index 0000000..e36e18a
--- /dev/null
+++ b/src/main/java/kst4contest/model/ChatMember.java
@@ -0,0 +1,270 @@
+package kst4contest.model;
+
+import java.util.Date;
+
+import javafx.beans.property.SimpleStringProperty;
+import javafx.beans.property.StringProperty;
+
+public class ChatMember {
+
+ AirPlaneReflectionInfo airPlaneReflectInfo;
+ String callSign;
+ String qra;
+ String name;
+// String frequency; // last known qrg of the station
+
+ StringProperty frequency = new SimpleStringProperty();
+
+ String password; // only used by own instance of the chatmember instance to login to the chat
+ ChatCategory chatCategory; // only used by own instance of the chatmember instance to login to the chat
+// ChatCategory chatCategory;//only used by own instance of the chatmember instance to login to the chat
+
+ long activityCounter; // time of last activity in epochtimesec
+ Date lastActivity; // time of last activity in epochtimesec
+ Date lastActualizationTimeOfThisMember; // time of last state change if that member
+ int qrb;
+ int state;
+
+ int QTFdirection; // antenna direction in deg
+ int[] workedCategories; // Chatcategory where the station is in the log, see kst4contest.model.ChatCategory
+
+ boolean worked; // true if the callsign is logged already - for temporary worked processing
+ boolean worked144;
+ boolean worked432;
+ boolean worked1240;
+ boolean worked2300;
+ boolean worked3400;
+ boolean worked5600;
+ boolean worked10G;
+
+ public AirPlaneReflectionInfo getAirPlaneReflectInfo() {
+ return airPlaneReflectInfo;
+ }
+
+ public void setAirPlaneReflectInfo(AirPlaneReflectionInfo airPlaneReflectInfo) {
+ this.airPlaneReflectInfo = airPlaneReflectInfo;
+ }
+
+ public ChatCategory getChatCategory() {
+ return chatCategory;
+ }
+
+ public void setChatCategory(ChatCategory chatCategory) {
+ this.chatCategory = chatCategory;
+ }
+
+ public String getPassword() {
+ return password;
+ }
+
+ public void setPassword(String password) {
+ this.password = password;
+ }
+
+ public boolean isWorked144() {
+ return worked144;
+ }
+
+ public void setWorked144(boolean worked144) {
+ this.worked144 = worked144;
+ }
+
+ public boolean isWorked432() {
+ return worked432;
+ }
+
+ public void setWorked432(boolean worked432) {
+ this.worked432 = worked432;
+ }
+
+ public boolean isWorked1240() {
+ return worked1240;
+ }
+
+ public void setWorked1240(boolean worked1240) {
+ this.worked1240 = worked1240;
+ }
+
+ public boolean isWorked2300() {
+ return worked2300;
+ }
+
+ public void setWorked2300(boolean worked2300) {
+ this.worked2300 = worked2300;
+ }
+
+ public boolean isWorked3400() {
+ return worked3400;
+ }
+
+ public void setWorked3400(boolean worked3400) {
+ this.worked3400 = worked3400;
+ }
+
+ public boolean isWorked5600() {
+ return worked5600;
+ }
+
+ public void setWorked5600(boolean worked5600) {
+ this.worked5600 = worked5600;
+ }
+
+ public boolean isWorked10G() {
+ return worked10G;
+ }
+
+ public void setWorked10G(boolean worked10g) {
+ worked10G = worked10g;
+ }
+
+ public int[] getWorkedCategories() {
+ return workedCategories;
+ }
+
+ public void setWorkedCategories(int[] workedCategories) {
+ this.workedCategories = workedCategories;
+ }
+
+ public void setActivityCounter(long activityCounter) {
+ this.activityCounter = activityCounter;
+ }
+
+ public int getState() {
+ return state;
+ }
+
+ public void setState(int state) {
+ this.state = state;
+ }
+
+ public Date getLastActivity() {
+ return lastActivity;
+ }
+
+ public void setLastActivity(Date lastActivity) {
+ this.lastActivity = lastActivity;
+ }
+
+ public Date getLastActualizationTimeOfThisMember() {
+ return lastActualizationTimeOfThisMember;
+ }
+
+ public void setLastActualizationTimeOfThisMember(Date lastActualizationTimeOfThisMember) {
+ this.lastActualizationTimeOfThisMember = lastActualizationTimeOfThisMember;
+ }
+
+ public int getQrb() {
+ return qrb;
+ }
+
+ public void setQrb(int qrb) {
+ this.qrb = qrb;
+ }
+
+ public int getQTFdirection() {
+ return QTFdirection;
+ }
+
+ public void setQTFdirection(int qTFdirection) {
+ QTFdirection = qTFdirection;
+ }
+
+// public int getWorkedCategory() {
+// return workedCategory;
+// }
+// public void setWorkedCategory(int workedCategory) {
+// this.workedCategory = workedCategory;
+// }
+ public String getCallSign() {
+ return callSign;
+ }
+
+ public void setCallSign(String callSign) {
+ this.callSign = callSign;
+ }
+
+ public String getQra() {
+ return qra;
+ }
+
+ public void setQra(String qra) {
+ this.qra = qra;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public StringProperty getFrequency() {
+
+ return frequency;
+// return frequency;
+ }
+
+ public void setFrequency(StringProperty frequency) {
+
+ this.frequency = frequency;
+ }
+
+ public long getActivityCounter() {
+ return activityCounter;
+ }
+
+ public void setActivityCounter(int activityCounter) {
+ this.activityCounter = activityCounter;
+ }
+
+ public boolean isWorked() {
+ return worked;
+ }
+
+ public void setWorked(boolean worked) {
+ this.worked = worked;
+ }
+
+ /**
+ * Sets all worked information of this object to false. Scope: GUI, Reset Button
+ * for worked info, called by appcontroller
+ */
+ public void resetWorkedInformationAtAllBands() {
+
+ this.setWorked(false);
+ this.setWorked144(false);
+ this.setWorked432(false);
+ this.setWorked1240(false);
+ this.setWorked2300(false);
+ this.setWorked3400(false);
+ this.setWorked5600(false);
+ this.setWorked10G(false);
+ }
+
+ @Override
+ public String toString() {
+ String chatMemberSerialization = "";
+
+ chatMemberSerialization += callSign + ";" + name + ";" + qra + ";" + frequency + ";" + worked + ";" + worked144
+ + ";" + worked432 + ";" + worked1240 + ";" + worked2300 + ";" + worked3400 + ";" + worked5600 + ";"
+ + worked10G;
+
+ return chatMemberSerialization;
+ }
+
+ /**
+ * Finds out if a given Chatmember-instance has the same callsign as this
+ * instance. Callsign is used as a key.
+ *
+ * @param anotherChatMember
+ * @return
+ */
+ public boolean equals(ChatMember anotherChatMember) {
+ if (this.getCallSign().toUpperCase().equals(anotherChatMember.getCallSign().toUpperCase())) {
+ return true;
+ } else
+ return false;
+ }
+
+}
\ No newline at end of file
diff --git a/src/main/java/kst4contest/model/ChatMessage.java b/src/main/java/kst4contest/model/ChatMessage.java
new file mode 100644
index 0000000..4760e3c
--- /dev/null
+++ b/src/main/java/kst4contest/model/ChatMessage.java
@@ -0,0 +1,90 @@
+package kst4contest.model;
+
+//import java.time.OffsetDateTime;
+
+/**
+ * boolean directedToServer is important
+ * @author mywire
+ *
+ */
+public class ChatMessage {
+
+ ChatMember sender;
+ String messageText;
+ ChatMember receiver; //the callsign to whom the message is directed
+ String messageGeneratedTime; //generated time in epochtimemillis/1000, as used by the chatserver
+
+ ChatCategory chatCategory;
+ String messageSenderName;
+
+ boolean messageByServer; //whether the chat message is directed do the server
+ boolean messageDirectedToCommunity; //wheter the message is directed to anyone or to all users
+ boolean messageDirectedToMe; //wheter message is directed to the praktiKST User
+ boolean messageDirectedToServer;
+
+
+
+ public boolean isMessageDirectedToServer() {
+ return messageDirectedToServer;
+ }
+ public void setMessageDirectedToServer(boolean messageDirectedToServer) {
+ this.messageDirectedToServer = messageDirectedToServer;
+ }
+ public boolean isMessageDirectedToCommunity() {
+ return messageDirectedToCommunity;
+ }
+ public void setMessageDirectedToCommunity(boolean messageDirectedToCommunity) {
+ this.messageDirectedToCommunity = messageDirectedToCommunity;
+ }
+ public boolean isMessageDirectedToMe() {
+ return messageDirectedToMe;
+ }
+ public void setMessageDirectedToMe(boolean messageDirectedToMe) {
+ this.messageDirectedToMe = messageDirectedToMe;
+ }
+ public String getMessageSenderName() {
+ return messageSenderName;
+ }
+ public void setMessageSenderName(String messageSenderName) {
+ this.messageSenderName = messageSenderName;
+ }
+ public ChatCategory getChatCategory() {
+ return chatCategory;
+ }
+ public void setChatCategory(ChatCategory chatCategory) {
+ this.chatCategory = chatCategory;
+ }
+ public boolean isMessageByServer() {
+ return messageByServer;
+ }
+ public void setMessageByServer(boolean isMessageByServer) {
+ this.messageByServer = isMessageByServer;
+ }
+ public void setMessageText(String messageText) {
+ this.messageText = messageText;
+ }
+ public String getMessageGeneratedTime() {
+ return messageGeneratedTime;
+ }
+ public void setMessageGeneratedTime(String messageGeneratedTime) {
+ this.messageGeneratedTime = messageGeneratedTime;
+ }
+ public ChatMember getSender() {
+ return sender;
+ }
+ public void setSender(ChatMember sender) {
+ this.sender = sender;
+ }
+ public String getMessageText() {
+ return messageText;
+ }
+ public ChatMember getReceiver() {
+ return receiver;
+ }
+ public void setReceiver(ChatMember receiver) {
+ this.receiver = receiver;
+ }
+
+
+
+}
diff --git a/src/main/java/kst4contest/model/ChatPreferences.java b/src/main/java/kst4contest/model/ChatPreferences.java
new file mode 100644
index 0000000..80fa711
--- /dev/null
+++ b/src/main/java/kst4contest/model/ChatPreferences.java
@@ -0,0 +1,1169 @@
+package kst4contest.model;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.util.ArrayList;
+import java.util.Iterator;
+
+import javax.xml.XMLConstants;
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.parsers.ParserConfigurationException;
+import javax.xml.transform.OutputKeys;
+import javax.xml.transform.Transformer;
+import javax.xml.transform.TransformerException;
+import javax.xml.transform.TransformerFactory;
+import javax.xml.transform.dom.DOMSource;
+import javax.xml.transform.stream.StreamResult;
+
+import kst4contest.ApplicationConstants;
+import kst4contest.utils.ApplicationFileUtils;
+import org.w3c.dom.Comment;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+import org.xml.sax.SAXException;
+
+import javafx.beans.property.IntegerProperty;
+import javafx.beans.property.SimpleIntegerProperty;
+import javafx.beans.property.SimpleStringProperty;
+import javafx.beans.property.StringProperty;
+import javafx.collections.FXCollections;
+import javafx.collections.ObservableList;
+
+public class ChatPreferences {
+
+ /**
+ * Name of file to store preferences in.
+ */
+ public static final String PREFERENCES_FILE = "preferences.xml";
+
+ /**
+ * Resource with the example properties xml file.
+ */
+ public static final String PREFERENCE_RESOURCE = "/praktiKSTpreferences.xml";
+
+ /**
+ * Default constructor will set the default values (also for predefined texts
+ * and shorts) automatically at initialization
+ *
+ * TODO: delete this from the kst4contest.view/Main.java!
+ */
+ public ChatPreferences() {
+ ApplicationFileUtils.copyResourceIfRequired(ApplicationConstants.APPLICATION_NAME, PREFERENCE_RESOURCE, PREFERENCES_FILE);
+
+// shortcuts[2] = "pse";
+// shortcuts[3] = "turn";
+// shortcuts[4] = "ant";
+// shortcuts[5] = "my";
+// shortcuts[6] = "dir";
+// shortcuts[7] = "sked";
+// shortcuts[8] = "ssb";
+// shortcuts[9] = "cw";
+// shortcuts[10] = "try";
+// shortcuts[11] = "agn";
+// shortcuts[12] = "nw";
+// shortcuts[13] = "qrg";
+// shortcuts[14] = "beaming";
+// shortcuts[15] = "calling";
+// shortcuts[16] = "lsn to";
+// shortcuts[17] = "qsb";
+// shortcuts[18] = "rpt";
+// shortcuts[19] = "nr";
+// shortcuts[20] = "ur";
+// shortcuts[21] = "I";
+// shortcuts[22] = "hear";
+// shortcuts[23] = "you";
+// shortcuts[24] = "weak";
+// shortcuts[25] = "nil, sry";
+// shortcuts[26] = "maybe";
+// shortcuts[27] = "later";
+// shortcuts[28] = "tmw";
+// shortcuts[29] = "rrr";
+// shortcuts[30] = "tnx";
+// shortcuts[31] = "qso";
+// shortcuts[32] = "73";
+// shortcuts[33] = "?";
+// shortcuts[34] = "!";
+// shortcuts[35] = ",";
+// shortcuts[36] = "MYQRG";
+//
+// textSnippets[0] = "Hi OM, try sked?";
+// textSnippets[1] = "I am calling cq ur dir, pse lsn to me at ";
+// textSnippets[2] = "pse ur qrg?";
+// textSnippets[3] = "rrr, I move to your qrg nw, pse ant dir me";
+// textSnippets[4] = "Hrd you but many qrm here, pse agn";
+//
+// textSnippets[5] = "I turn my ant to you";
+//
+// textSnippets[6] = "Sry, strong qrm by local station there, may try ";
+// textSnippets[7] = "Sry, in qso nw, pse qrx, I will meep you";
+//
+// textSnippets[8] = "Ur ant my dir nw?";
+// textSnippets[9] = "nil?";
+// textSnippets[10] = "No cw op here, could we use ssb?";
+// textSnippets[11] = "No chance in ssb, could we use cw?";
+//
+// textSnippets[12] = "Nil till now, are you calling?";
+// textSnippets[13] = "Nil, I will look for an ap";
+// textSnippets[14] = "Tnx try, maybe later!";
+// textSnippets[15] = "Tnx fb qso, all ok, 73 es gl!";
+ }
+
+ /**
+ * Preferences for the preferences
+ * kst4contest@googlegroups.com
+ * praktimarc+kst4contest@gmail.com
+ *
+ */
+
+ String programVersion = "Chat is powered by ON4KST \n\nUsage is free. You are welcome to support: \n\n- my project (donations, bugreports, good ideas are welcome), \n- ON4KST Servers, \n- AirScout developers and \n- OV3T (best AS-data provider of the world). \n\n73 de DO5AMF, Marc (DM5M / DARC X08)";
+ String logsynch_storeWorkedCallSignsFileNameUDPMessageBackup = "udpReaderBackup.txt";
+ String storeAndRestorePreferencesFileName = ApplicationFileUtils.getFilePath(ApplicationConstants.APPLICATION_NAME, PREFERENCES_FILE);
+ String chatState; // working variable only for use by primarystage (title bar)
+// ObservableStringValue chatState;
+
+ /**
+ * Station preferences
+ */
+
+ String loginCallSign = "do5amf";
+ String loginPassword = "";
+ String loginName = "Marc";
+ String loginLocator = "jn49fk";
+ ChatCategory loginChatCategory = new ChatCategory(2);
+ IntegerProperty actualQTF = new SimpleIntegerProperty(360); // will be updated by user at runtime!
+
+ /**
+ * Log Synch preferences
+ */
+ String logsynch_fileBasedWkdCallInterpreterFileNameReadOnly = "SimpleLogFile.txt";
+ boolean logsynch_fileBasedWkdCallInterpreterEnabled = true;
+
+ int logsynch_ucxUDPWkdCallListenerPort = 12060;
+ boolean logsynch_ucxUDPWkdCallListenerEnabled = true;
+
+ /**
+ * TRX Synch prefs
+ */
+ StringProperty MYQRG = new SimpleStringProperty(); // own qrg will be set by user entry or ucxlog if trx Synch is
+ // activated
+ boolean trxSynch_ucxLogUDPListenerEnabled = false;
+
+ /**
+ * AirScout prefs
+ */
+ boolean AirScout_asUDPListenerEnabled;
+ String AirScout_asServerNameString, AirScout_asClientNameString, AirScout_asBandString;
+ int AirScout_asCommunicationPort;
+
+ /**
+ * Notification prefs
+ */
+
+ /**
+ * Shortcuts and Textsnippets prefs
+ */
+// String[] shortcuts = new String[37];
+// String[] textSnippets = new String[16];
+ ObservableList lst_txtSnipList = FXCollections.observableArrayList();
+ ObservableList lst_txtShortCutBtnList = FXCollections.observableArrayList();
+
+ /**
+ * Beacon prefs
+ */
+
+ boolean bcn_beaconsEnabled = true;
+ int bcn_beaconIntervalInMinutes = 20;
+ String bcn_beaconText = "Hi, pse call us";
+
+ /**
+ * Unworked station requester prefs
+ */
+
+ boolean unwkd_unworkedStnRequesterBeaconsEnabled;
+ int unwkd_unworkedStnRequesterBeaconsInterval;
+ String unwkd_unworkedStnRequesterBeaconsText;
+ String unwkd_beaconUnworkedstationsPrefix;
+
+// public String getMYQRG() {
+// return MYQRG;
+// }
+//
+// public void setMYQRG(String mYQRG) {
+//
+// MYQRG = mYQRG;
+// }
+
+ public String getLoginCallSign() {
+ return loginCallSign;
+ }
+
+ public String getAirScout_asBandString() {
+ return AirScout_asBandString;
+ }
+
+ public void setAirScout_asBandString(String airScout_asBandString) {
+ AirScout_asBandString = airScout_asBandString;
+ }
+
+ public String getAirScout_asServerNameString() {
+ return AirScout_asServerNameString;
+ }
+
+ public void setAirScout_asServerNameString(String airScout_asServerNameString) {
+ AirScout_asServerNameString = airScout_asServerNameString;
+ }
+
+ public String getAirScout_asClientNameString() {
+ return AirScout_asClientNameString;
+ }
+
+ public void setAirScout_asClientNameString(String airScout_asClientNameString) {
+ AirScout_asClientNameString = airScout_asClientNameString;
+ }
+
+ public int getAirScout_asCommunicationPort() {
+ return AirScout_asCommunicationPort;
+ }
+
+ public void setAirScout_asCommunicationPort(int airScout_asCommunicationPort) {
+ AirScout_asCommunicationPort = airScout_asCommunicationPort;
+ }
+
+ public boolean isAirScout_asUDPListenerEnabled() {
+ return AirScout_asUDPListenerEnabled;
+ }
+
+ public void setAirScout_asUDPListenerEnabled(boolean airScout_asUDPListenerEnabled) {
+ AirScout_asUDPListenerEnabled = airScout_asUDPListenerEnabled;
+ }
+
+ public String getChatState() {
+ return chatState;
+ }
+
+ public void setChatState(String chatState) {
+ this.chatState = chatState;
+ }
+
+ public String getProgramVersion() {
+ return programVersion;
+ }
+
+ public void setProgramVersion(String programVersion) {
+ this.programVersion = programVersion;
+ }
+
+ public String getLogsynch_storeWorkedCallSignsFileNameUDPMessageBackup() {
+ return logsynch_storeWorkedCallSignsFileNameUDPMessageBackup;
+ }
+
+ public void setLogsynch_storeWorkedCallSignsFileNameUDPMessageBackup(
+ String logsynch_storeWorkedCallSignsFileNameUDPMessageBackup) {
+ this.logsynch_storeWorkedCallSignsFileNameUDPMessageBackup = logsynch_storeWorkedCallSignsFileNameUDPMessageBackup;
+ }
+
+ /**
+ * actualQTF, int, QTF in degrees
+ *
+ * @param actualQTF, int, QTF in degrees
+ */
+
+ public StringProperty getMYQRG() {
+ return MYQRG;
+ }
+
+ public IntegerProperty getActualQTF() {
+ return actualQTF;
+ }
+
+ public void setActualQTF(IntegerProperty actualQTF) {
+ this.actualQTF = actualQTF;
+ }
+
+ public void setMYQRG(StringProperty mYQRG) {
+ MYQRG = mYQRG;
+ }
+
+ public void setLoginCallSign(String loginCallSign) {
+ this.loginCallSign = loginCallSign;
+ }
+
+ public String getLoginPassword() {
+ return loginPassword;
+ }
+
+ public void setLoginPassword(String loginPassword) {
+ this.loginPassword = loginPassword;
+ }
+
+ public String getLoginName() {
+ return loginName;
+ }
+
+ public void setLoginName(String loginName) {
+ this.loginName = loginName;
+ }
+
+ public String getLoginLocator() {
+ return loginLocator;
+ }
+
+ public void setLoginLocator(String loginLocator) {
+ this.loginLocator = loginLocator;
+ }
+
+ public ChatCategory getLoginChatCategory() {
+ return loginChatCategory;
+ }
+
+ public void setLoginChatCategory(ChatCategory loginChatCategory) {
+ this.loginChatCategory = loginChatCategory;
+ }
+
+ public String getLogsynch_fileBasedWkdCallInterpreterFileNameReadOnly() {
+ return logsynch_fileBasedWkdCallInterpreterFileNameReadOnly;
+ }
+
+ public void setLogsynch_fileBasedWkdCallInterpreterFileNameReadOnly(
+ String logsynch_fileBasedWkdCallInterpreterFileNameReadOnly) {
+ this.logsynch_fileBasedWkdCallInterpreterFileNameReadOnly = logsynch_fileBasedWkdCallInterpreterFileNameReadOnly;
+ }
+
+ public boolean isLogsynch_fileBasedWkdCallInterpreterEnabled() {
+ return logsynch_fileBasedWkdCallInterpreterEnabled;
+ }
+
+ public void setLogsynch_fileBasedWkdCallInterpreterEnabled(boolean logsynch_fileBasedWkdCallInterpreterEnabled) {
+ this.logsynch_fileBasedWkdCallInterpreterEnabled = logsynch_fileBasedWkdCallInterpreterEnabled;
+ }
+
+ public int getLogsynch_ucxUDPWkdCallListenerPort() {
+ return logsynch_ucxUDPWkdCallListenerPort;
+ }
+
+ public void setLogsynch_ucxUDPWkdCallListenerPort(int logsynch_ucxUDPWkdCallListenerPort) {
+ this.logsynch_ucxUDPWkdCallListenerPort = logsynch_ucxUDPWkdCallListenerPort;
+ }
+
+ public boolean isLogsynch_ucxUDPWkdCallListenerEnabled() {
+ return logsynch_ucxUDPWkdCallListenerEnabled;
+ }
+
+ public void setLogsynch_ucxUDPWkdCallListenerEnabled(boolean logsynch_ucxUDPWkdCallListenerEnabled) {
+ this.logsynch_ucxUDPWkdCallListenerEnabled = logsynch_ucxUDPWkdCallListenerEnabled;
+ }
+
+ public boolean isTrxSynch_ucxLogUDPListenerEnabled() {
+ return trxSynch_ucxLogUDPListenerEnabled;
+ }
+
+ public void setTrxSynch_ucxLogUDPListenerEnabled(boolean trxSynch_ucxLogUDPListenerEnabled) {
+ this.trxSynch_ucxLogUDPListenerEnabled = trxSynch_ucxLogUDPListenerEnabled;
+ }
+
+// public String[] getShortcuts() {
+// return shortcuts;
+// }
+//
+// public void setShortcuts(String[] shortcuts) {
+// this.shortcuts = shortcuts;
+// }
+
+// public String[] getTextSnippets() {
+// return textSnippets;
+// }
+//
+// public void setTextSnippets(String[] textSnippets) {
+// this.textSnippets = textSnippets;
+// }
+
+ public boolean isBcn_beaconsEnabled() {
+ return bcn_beaconsEnabled;
+ }
+
+ public ObservableList getLst_txtShortCutBtnList() {
+ return lst_txtShortCutBtnList;
+ }
+
+ public void setLst_txtShortCutBtnList(ObservableList lst_txtShortCutBtnList) {
+ this.lst_txtShortCutBtnList = lst_txtShortCutBtnList;
+ }
+
+ public ObservableList getLst_txtSnipList() {
+ return lst_txtSnipList;
+ }
+
+ public void setLst_txtSnipList(ObservableList lst_txtSnipList) {
+ this.lst_txtSnipList = lst_txtSnipList;
+ }
+
+ public void setBcn_beaconsEnabled(boolean bcn_beaconsEnabled) {
+ this.bcn_beaconsEnabled = bcn_beaconsEnabled;
+ }
+
+ public int getBcn_beaconIntervalInMinutes() {
+ return bcn_beaconIntervalInMinutes;
+ }
+
+ public void setBcn_beaconIntervalInMinutes(int bcn_beaconIntervalInMinutes) {
+ this.bcn_beaconIntervalInMinutes = bcn_beaconIntervalInMinutes;
+ }
+
+ public String getBcn_beaconText() {
+ return bcn_beaconText;
+ }
+
+ public void setBcn_beaconText(String bcn_beaconText) {
+
+ this.bcn_beaconText = bcn_beaconText;
+ }
+
+
+
+ public String getUnwkd_beaconUnworkedstationsPrefix() {
+ return unwkd_beaconUnworkedstationsPrefix;
+ }
+
+ public void setUnwkd_beaconUnworkedstationsPrefix(String unwkd_beaconUnworkedstationsPrefix) {
+ this.unwkd_beaconUnworkedstationsPrefix = unwkd_beaconUnworkedstationsPrefix;
+ }
+
+ public boolean isUnwkd_unworkedStnRequesterBeaconsEnabled() {
+ return unwkd_unworkedStnRequesterBeaconsEnabled;
+ }
+
+ public void setUnwkd_unworkedStnRequesterBeaconsEnabled(boolean unwkd_unworkedStnRequesterBeaconsEnabled) {
+ this.unwkd_unworkedStnRequesterBeaconsEnabled = unwkd_unworkedStnRequesterBeaconsEnabled;
+ }
+
+ public int getUnwkd_unworkedStnRequesterBeaconsInterval() {
+ return unwkd_unworkedStnRequesterBeaconsInterval;
+ }
+
+ public void setUnwkd_unworkedStnRequesterBeaconsInterval(int unwkd_unworkedStnRequesterBeaconsInterval) {
+ this.unwkd_unworkedStnRequesterBeaconsInterval = unwkd_unworkedStnRequesterBeaconsInterval;
+ }
+
+ public String getUnwkd_unworkedStnRequesterBeaconsText() {
+ return unwkd_unworkedStnRequesterBeaconsText;
+ }
+
+ public void setUnwkd_unworkedStnRequesterBeaconsText(String unwkd_unworkedStnRequesterBeaconsText) {
+ this.unwkd_unworkedStnRequesterBeaconsText = unwkd_unworkedStnRequesterBeaconsText;
+ }
+
+ public String getLogSynch_storeWorkedCallSignsFileNameUDPMessageBackup() {
+ return logsynch_storeWorkedCallSignsFileNameUDPMessageBackup;
+ }
+
+ public void setLogSynch_storeWorkedCallSignsFileNameUDPMessageBackup(
+ String logSynch_storeWorkedCallSignsFileNameUDPMessageBackup) {
+ this.logsynch_storeWorkedCallSignsFileNameUDPMessageBackup = logSynch_storeWorkedCallSignsFileNameUDPMessageBackup;
+ }
+
+ public String getStoreAndRestorePreferencesFileName() {
+ return storeAndRestorePreferencesFileName;
+ }
+
+ public void setStoreAndRestorePreferencesFileName(String storeAndRestorePreferencesFileName) {
+ this.storeAndRestorePreferencesFileName = storeAndRestorePreferencesFileName;
+ }
+
+ /**
+ *
+ * @return true if the file writing was successful, else false
+ */
+ public boolean writePreferencesToXmlFile() {
+
+ DocumentBuilderFactory docFactory = DocumentBuilderFactory.newInstance();
+ try {
+
+ // root elements
+ DocumentBuilder docBuilder = docFactory.newDocumentBuilder();
+ Document doc = docBuilder.newDocument();
+ Element rootElement = doc.createElement("praktiKST");
+ doc.appendChild(rootElement);
+
+
+ Element station = doc.createElement("station");
+ rootElement.appendChild(station);
+
+ Element LoginCallSign = doc.createElement("LoginCallSign");
+ LoginCallSign.setTextContent(this.getLoginCallSign());
+ station.appendChild(LoginCallSign);
+
+
+ Element LoginPassword = doc.createElement("LoginPassword");
+ LoginPassword.setTextContent(this.getLoginPassword());
+ station.appendChild(LoginPassword);
+
+ Element LoginDisplayedName = doc.createElement("LoginDisplayedName");
+ LoginDisplayedName.setTextContent(this.getLoginName());
+ station.appendChild(LoginDisplayedName);
+
+ Element LoginLocator = doc.createElement("LoginLocator");
+ LoginLocator.setTextContent(this.getLoginLocator());
+ station.appendChild(LoginLocator);
+
+ Element ChatCategory = doc.createElement("ChatCategory");
+ ChatCategory.setTextContent(this.getLoginChatCategory().getCategoryNumber()+"");
+ station.appendChild(ChatCategory);
+
+// Element salary = doc.createElement("salary");
+// salary.setAttribute("currency", "USD");
+// salary.setTextContent("5000");
+// staff.appendChild(salary);
+
+ // add xml comment
+ Comment comment = doc.createComment(
+ "for special characters like < &, need CDATA");
+// staff.appendChild(comment);
+
+// Element bio = doc.createElement("bio");
+ // add xml CDATA
+
+// staff.appendChild(bio);
+
+ /**
+ * LOGSYNCH
+ */
+
+ Element logsynch = doc.createElement("logsynch");
+ rootElement.appendChild(logsynch);
+
+ Element logsynch_fileBasedWkdCallInterpreterFileNameReadOnly = doc.createElement("logsynch_fileBasedWkdCallInterpreterFileNameReadOnly");
+ logsynch_fileBasedWkdCallInterpreterFileNameReadOnly.setTextContent(this.getLogsynch_fileBasedWkdCallInterpreterFileNameReadOnly());
+ logsynch.appendChild(logsynch_fileBasedWkdCallInterpreterFileNameReadOnly);
+
+ Element logsynch_storeWorkedCallSignsFileNameUDPMessageBackup = doc.createElement("logsynch_storeWorkedCallSignsFileNameUDPMessageBackup");
+ logsynch_storeWorkedCallSignsFileNameUDPMessageBackup.setTextContent(this.getLogsynch_storeWorkedCallSignsFileNameUDPMessageBackup());
+ logsynch.appendChild(logsynch_storeWorkedCallSignsFileNameUDPMessageBackup);
+
+ Element logsynch_fileBasedWkdCallInterpreterEnabled = doc.createElement("logsynch_fileBasedWkdCallInterpreterEnabled");
+ logsynch_fileBasedWkdCallInterpreterEnabled.setTextContent(this.isLogsynch_fileBasedWkdCallInterpreterEnabled()+"");
+ logsynch.appendChild(logsynch_fileBasedWkdCallInterpreterEnabled);
+
+ Element logsynch_ucxUDPWkdCallListenerPort = doc.createElement("logsynch_ucxUDPWkdCallListenerPort");
+ logsynch_ucxUDPWkdCallListenerPort.setTextContent(this.getLogsynch_ucxUDPWkdCallListenerPort()+"");
+ logsynch.appendChild(logsynch_ucxUDPWkdCallListenerPort);
+
+ Element logsynch_ucxUDPWkdCallListenerEnabled = doc.createElement("logsynch_ucxUDPWkdCallListenerEnabled");
+ logsynch_ucxUDPWkdCallListenerEnabled.setTextContent(this.isTrxSynch_ucxLogUDPListenerEnabled()+"");
+ logsynch.appendChild(logsynch_ucxUDPWkdCallListenerEnabled);
+
+
+ /**
+ * trxSynchUCX
+ */
+
+ Element trxSynchUCX = doc.createElement("trxSynchUCX");
+ rootElement.appendChild(trxSynchUCX);
+
+ Element trxSynch_ucxLogUDPListenerEnabled = doc.createElement("trxSynch_ucxLogUDPListenerEnabled");
+ trxSynch_ucxLogUDPListenerEnabled.setTextContent(this.isTrxSynch_ucxLogUDPListenerEnabled()+"");
+ trxSynchUCX.appendChild(trxSynch_ucxLogUDPListenerEnabled);
+
+ Element trxSynch_defaultMYQRGValue = doc.createElement("trxSynch_defaultMYQRGValue");
+ trxSynch_defaultMYQRGValue.setTextContent(this.getMYQRG().getValue());
+ trxSynchUCX.appendChild(trxSynch_defaultMYQRGValue);
+
+
+
+
+ /**
+ * AirScout
+ */
+
+ Element AirScoutQuerier = doc.createElement("AirScoutQuerier");
+ rootElement.appendChild(AirScoutQuerier);
+
+
+ Element asQry_airScoutCommunicationEnabled = doc.createElement("asQry_airScoutCommunicationEnabled");
+ asQry_airScoutCommunicationEnabled.setTextContent(this.isAirScout_asUDPListenerEnabled()+"");
+ AirScoutQuerier.appendChild(asQry_airScoutCommunicationEnabled);
+
+ Element asQry_airScoutServerName = doc.createElement("asQry_airScoutServerName");
+ asQry_airScoutServerName.setTextContent(this.getAirScout_asServerNameString());
+ AirScoutQuerier.appendChild(asQry_airScoutServerName);
+
+ Element asQry_airScoutClientName = doc.createElement("asQry_airScoutClientName");
+ asQry_airScoutClientName.setTextContent(this.getAirScout_asClientNameString());
+ AirScoutQuerier.appendChild(asQry_airScoutClientName);
+
+ Element asQry_airScoutUDPPort = doc.createElement("asQry_airScoutUDPPort");
+ asQry_airScoutUDPPort.setTextContent(this.getAirScout_asCommunicationPort()+"");
+ AirScoutQuerier.appendChild(asQry_airScoutUDPPort);
+
+ Element asQry_airScoutBandValue = doc.createElement("asQry_airScoutBandValue");
+ asQry_airScoutBandValue.setTextContent(this.getAirScout_asBandString());
+ AirScoutQuerier.appendChild(asQry_airScoutBandValue);
+
+ /**
+ * Shortcuts
+ */
+
+ Element shortCuts = doc.createElement("shortCuts");
+ rootElement.appendChild(shortCuts);
+
+ for (Iterator iterator = lst_txtShortCutBtnList.iterator(); iterator.hasNext();) {
+ String string = (String) iterator.next();
+ Element temp = doc.createElement("t");
+ temp.setTextContent(string);
+ shortCuts.appendChild(temp);
+ }
+
+ /**
+ * Textsnippets (right click menu)
+ */
+
+ Element textSnippets = doc.createElement("textSnippets");
+ rootElement.appendChild(textSnippets);
+
+ for (Iterator iterator = lst_txtSnipList.iterator(); iterator.hasNext();) {
+ String string = (String) iterator.next();
+ Element temp = doc.createElement("t");
+ temp.setTextContent(string);
+ textSnippets.appendChild(temp);
+ }
+
+
+ /**
+ * BeaconCQ
+ */
+
+ Element beaconCQ = doc.createElement("beaconCQ");
+ rootElement.appendChild(beaconCQ);
+
+
+ Element beaconCQText = doc.createElement("beaconCQText");
+ beaconCQText.setTextContent(this.getBcn_beaconText());
+ beaconCQ.appendChild(beaconCQText);
+
+ Element beaconCQIntervalMinutes = doc.createElement("beaconCQIntervalMinutes");
+ beaconCQIntervalMinutes.setTextContent(this.getBcn_beaconIntervalInMinutes()+"");
+ beaconCQ.appendChild(beaconCQIntervalMinutes);
+
+ Element beaconCQEnabled = doc.createElement("beaconCQEnabled");
+ beaconCQEnabled.setTextContent(this.isBcn_beaconsEnabled()+"");
+ beaconCQ.appendChild(beaconCQEnabled);
+
+
+
+ /**
+ * Beacon Unworked Stations
+ */
+
+ Element beaconUnworkedstations = doc.createElement("beaconUnworkedstations");
+ rootElement.appendChild(beaconUnworkedstations);
+
+
+ Element beaconUnworkedstationsText = doc.createElement("beaconUnworkedstationsText");
+ beaconUnworkedstationsText.setTextContent(this.getUnwkd_unworkedStnRequesterBeaconsText());
+ beaconUnworkedstations.appendChild(beaconUnworkedstationsText);
+
+ Element beaconUnworkedstationsIntervalMinutes = doc.createElement("beaconUnworkedstationsIntervalMinutes");
+ beaconUnworkedstationsIntervalMinutes.setTextContent(this.getUnwkd_unworkedStnRequesterBeaconsInterval()+"");
+ beaconUnworkedstations.appendChild(beaconUnworkedstationsIntervalMinutes);
+
+ Element beaconUnworkedstationsEnabled = doc.createElement("beaconUnworkedstationsEnabled");
+ beaconUnworkedstationsEnabled.setTextContent(this.isUnwkd_unworkedStnRequesterBeaconsEnabled()+"");
+ beaconUnworkedstations.appendChild(beaconUnworkedstationsEnabled);
+
+ Element beaconUnworkedstationsPrefix = doc.createElement("beaconUnworkedstationsPrefix");
+ beaconUnworkedstationsPrefix.setTextContent(this.getUnwkd_beaconUnworkedstationsPrefix());
+ beaconUnworkedstations.appendChild(beaconUnworkedstationsPrefix);
+
+
+
+ writeXml(doc, System.out);
+
+ // write dom document to a file
+ try (FileOutputStream output =
+ new FileOutputStream(storeAndRestorePreferencesFileName)) {
+ writeXml(doc, output);
+ } catch (IOException e) {
+ e.printStackTrace();
+ } catch (TransformerException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+
+
+ } catch (ParserConfigurationException | TransformerException e1) {
+ // TODO Auto-generated catch block
+ e1.printStackTrace();
+ }
+ return true;
+
+ // root elements
+
+ //...create XML elements, and others...
+
+ // write dom document to a file
+
+ }
+
+ // write doc to output stream
+ private static void writeXml(Document doc, OutputStream output) throws TransformerException {
+
+ TransformerFactory transformerFactory = TransformerFactory.newInstance();
+ Transformer transformer = transformerFactory.newTransformer();
+ DOMSource source = new DOMSource(doc);
+ StreamResult result = new StreamResult(output);
+
+ transformer.setOutputProperty(OutputKeys.INDENT, "yes");
+
+ transformer.transform(source, result);
+
+ }
+
+ /**
+ *
+ * @return true if the file reading was successful, else false
+ */
+ public boolean readPreferencesFromXmlFile() {
+
+ System.out.println("[ChatPreferences, Info]: restoring prefs from file " + storeAndRestorePreferencesFileName);
+
+ DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
+ try {
+ dbf.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
+ } catch (ParserConfigurationException e1) {
+ // TODO Auto-generated catch block
+ e1.printStackTrace();
+ }
+
+ try {
+
+ File xmlConfigFile = new File(storeAndRestorePreferencesFileName);
+
+ DocumentBuilder db = dbf.newDocumentBuilder();
+ Document doc = db.parse(xmlConfigFile);
+
+ /**
+ * case station settings
+ *
+ */
+ NodeList list = doc.getElementsByTagName("station");
+ if (list.getLength() != 0) {
+
+ for (int temp = 0; temp < list.getLength(); temp++) {
+
+ Node node = list.item(temp);
+
+ if (node.getNodeType() == Node.ELEMENT_NODE) {
+
+ Element element = (Element) node;
+
+ String call = element.getElementsByTagName("LoginCallSign").item(0).getTextContent();
+ loginCallSign = call;
+
+// call = call.toLowerCase();
+ String password = element.getElementsByTagName("LoginPassword").item(0).getTextContent();
+ loginPassword = password;
+
+ String loginDisplayedName = element.getElementsByTagName("LoginDisplayedName").item(0)
+ .getTextContent();
+ loginName = loginDisplayedName;
+
+ String qra = element.getElementsByTagName("LoginLocator").item(0).getTextContent();
+ loginLocator = qra;
+
+ String category = element.getElementsByTagName("ChatCategory").item(0).getTextContent();
+
+ if (isNumeric(category)) {
+ ChatCategory chatCategory = new ChatCategory(Integer.parseInt(category));
+ loginChatCategory = chatCategory;
+ } else {
+
+ loginChatCategory = new ChatCategory(2); // TODO: Set this default at another place
+ }
+
+ System.out.println("[ChatPreferences, info]: Current Element: " + node.getNodeName()
+ + " --> call: " + call + " / " + password + " / " + loginDisplayedName + " / " + qra
+ + " / " + category);
+
+ }
+ }
+ }
+
+ /**
+ * Case log synchronizatrion
+ */
+
+ list = doc.getElementsByTagName("logsynch");
+ if (list.getLength() != 0) {
+
+ for (int temp = 0; temp < list.getLength(); temp++) {
+
+ Node node = list.item(temp);
+
+ if (node.getNodeType() == Node.ELEMENT_NODE) {
+
+ Element element = (Element) node;
+
+ String logsynchReadFile = element
+ .getElementsByTagName("logsynch_fileBasedWkdCallInterpreterFileNameReadOnly").item(0)
+ .getTextContent();
+
+ logsynch_fileBasedWkdCallInterpreterFileNameReadOnly = logsynchReadFile;
+
+ String UDPMessageBackupFileName = element
+ .getElementsByTagName("logsynch_storeWorkedCallSignsFileNameUDPMessageBackup").item(0)
+ .getTextContent();
+
+ logsynch_storeWorkedCallSignsFileNameUDPMessageBackup = UDPMessageBackupFileName;
+
+// call = call.toLowerCase();
+ String fileBasedLogSynchEnabled = element
+ .getElementsByTagName("logsynch_fileBasedWkdCallInterpreterEnabled").item(0)
+ .getTextContent();
+
+ if (fileBasedLogSynchEnabled.equals("true")) {
+
+ logsynch_fileBasedWkdCallInterpreterEnabled = true;
+ } else {
+ logsynch_fileBasedWkdCallInterpreterEnabled = false;
+ }
+
+ String ucxUDPLogSynchListenerPort = element
+ .getElementsByTagName("logsynch_ucxUDPWkdCallListenerPort").item(0).getTextContent();
+
+ if (isNumeric(ucxUDPLogSynchListenerPort)) {
+ logsynch_ucxUDPWkdCallListenerPort = Integer.parseInt(ucxUDPLogSynchListenerPort);
+ } else {
+ logsynch_ucxUDPWkdCallListenerPort = 12060; // TODO: Set default at another place or with
+ // STATIC VAR
+ }
+
+ String ucxUDPLogSynchListenerEnabled = element
+ .getElementsByTagName("logsynch_ucxUDPWkdCallListenerEnabled").item(0).getTextContent();
+
+ if (ucxUDPLogSynchListenerEnabled.equals("true")) {
+ logsynch_ucxUDPWkdCallListenerEnabled = true;
+ } else {
+ logsynch_ucxUDPWkdCallListenerEnabled = false;
+ }
+
+ System.out.println(
+ "[ChatPreferences, info]: Set the Universal file based worked-Call Interpreter to : "
+ + logsynch_fileBasedWkdCallInterpreterEnabled);
+
+ System.out.println("[ChatPreferences, info]: Set the UCX UDP Worked Call Listener to : "
+ + logsynch_ucxUDPWkdCallListenerEnabled);
+
+ }
+ }
+ }
+
+ /**
+ * Case trx synchronizatrion
+ */
+
+ list = doc.getElementsByTagName("trxSynchUCX");
+ if (list.getLength() != 0) {
+
+ for (int temp = 0; temp < list.getLength(); temp++) {
+
+ Node node = list.item(temp);
+
+ if (node.getNodeType() == Node.ELEMENT_NODE) {
+
+ Element element = (Element) node;
+
+ String trxSynchUCX = element.getElementsByTagName("trxSynch_ucxLogUDPListenerEnabled").item(0)
+ .getTextContent();
+
+ if (trxSynchUCX.equals("true")) {
+ trxSynch_ucxLogUDPListenerEnabled = true;
+ } else {
+ trxSynch_ucxLogUDPListenerEnabled = false;
+ }
+
+ String trxSynch_defaultMYQRGValue = element.getElementsByTagName("trxSynch_defaultMYQRGValue")
+ .item(0).getTextContent();
+
+ this.getMYQRG().setValue(trxSynch_defaultMYQRGValue);
+
+// this.getMYQRG().addListener((observable, oldValue, newValue) -> {
+// System.out.println("[Chatprefs.java, Info]: MYQRG changed from " + oldValue + " to " + newValue);
+//// this.getMYQRG().
+//// txt_ownqrg.setText(newValue);
+// });
+
+//
+ System.out.println(
+ "[ChatPreferences, info]: Set the trx qrg synch to " + trxSynch_ucxLogUDPListenerEnabled
+ + " and default value to " + this.getMYQRG().getValue());
+
+ }
+ }
+ }
+
+ /**
+ * Case AirScout querier
+ */
+
+ list = doc.getElementsByTagName("AirScoutQuerier");
+ if (list.getLength() != 0) {
+
+ for (int temp = 0; temp < list.getLength(); temp++) {
+
+ Node node = list.item(temp);
+
+ if (node.getNodeType() == Node.ELEMENT_NODE) {
+
+ Element element = (Element) node;
+
+ String asQuerierUDPEnabled = element.getElementsByTagName("asQry_airScoutCommunicationEnabled")
+ .item(0).getTextContent();
+
+ if (asQuerierUDPEnabled.equals("true")) {
+ AirScout_asUDPListenerEnabled = true;
+ } else {
+ AirScout_asUDPListenerEnabled = false;
+ }
+
+ this.setAirScout_asServerNameString(
+ element.getElementsByTagName("asQry_airScoutServerName").item(0).getTextContent());
+ this.setAirScout_asClientNameString(
+ element.getElementsByTagName("asQry_airScoutClientName").item(0).getTextContent());
+ this.setAirScout_asCommunicationPort(Integer.parseInt(
+ element.getElementsByTagName("asQry_airScoutUDPPort").item(0).getTextContent()));
+ this.setAirScout_asBandString(
+ element.getElementsByTagName("asQry_airScoutBandValue").item(0).getTextContent());
+// this.getMYQRG().addListener((observable, oldValue, newValue) -> {
+// System.out.println("[Chatprefs.java, Info]: MYQRG changed from " + oldValue + " to " + newValue);
+//// this.getMYQRG().
+//// txt_ownqrg.setText(newValue);
+// });
+
+//
+ System.out.println("[ChatPreferences, info]: Set the Airscout Querier to " + asQuerierUDPEnabled
+ + " for qrg " + AirScout_asBandString);
+
+ }
+ }
+ }
+
+ /**
+ * Case shortCuts
+ */
+
+ list = doc.getElementsByTagName("shortCuts");
+ String[] shortCutsExtractedOutOfXML = new String[0];
+
+ if (list.getLength() != 0) {
+
+ ArrayList textShorts = new ArrayList();
+
+ for (int temp = 0; temp < list.getLength(); temp++) {
+
+ Node node = list.item(temp);
+
+ Element element = (Element) node;
+
+ for (int i = 0; i < element.getChildNodes().getLength(); i++) {
+ if (element.getChildNodes().item(i).getNodeType() == Node.ELEMENT_NODE) {
+ textShorts.add(element.getChildNodes().item(i).getTextContent());
+ shortCutsExtractedOutOfXML = textShorts.toArray(String[]::new); // String[]::new = API
+ // Collection.toArray(IntFunction
+ // generator)
+// System.out.println(element.getChildNodes().item(i).getNodeType() + ": " + element.getChildNodes().item(i).getNodeName() + " - " + element.getChildNodes().item(i).getTextContent());
+ }
+ }
+
+ }
+
+ // if praktiKST found Shortcuts in the configfile, set it to the preferences
+// shortcuts = shortCutsExtractedOutOfXML;
+ // else use defaults (as the initialization vars had been)
+ for (int i = 0; i < shortCutsExtractedOutOfXML.length; i++) {
+ lst_txtShortCutBtnList.add(shortCutsExtractedOutOfXML[i]);
+ System.out.println("[Chatpreferences, Info]: Setting Short " + i + " \""
+ + shortCutsExtractedOutOfXML[i] + "\"");
+ }
+ }
+
+ /**
+ * Case textSnippets
+ */
+
+ list = doc.getElementsByTagName("textSnippets");
+ String[] textSnippetsExtractedOutOfXML = new String[0];
+
+ if (list.getLength() != 0) {
+
+ ArrayList textShorts = new ArrayList();
+
+ for (int temp = 0; temp < list.getLength(); temp++) {
+
+ Node node = list.item(temp);
+
+ Element element = (Element) node;
+
+ for (int i = 0; i < element.getChildNodes().getLength(); i++) {
+ if (element.getChildNodes().item(i).getNodeType() == Node.ELEMENT_NODE) {
+ textShorts.add(element.getChildNodes().item(i).getTextContent());
+ textSnippetsExtractedOutOfXML = textShorts.toArray(String[]::new); // String[]::new = API
+ // Collection.toArray(IntFunction
+ // generator)
+// System.out.println(element.getChildNodes().item(i).getNodeType() + ": " + element.getChildNodes().item(i).getNodeName() + " - " + element.getChildNodes().item(i).getTextContent());
+ }
+ }
+
+ }
+
+ // if praktiKST found Shortcuts in the configfile, set it to the preferences
+// textSnippets = textSnippetsExtractedOutOfXML;
+ // else use defaults (as the initialization vars had been)
+ for (int i = 0; i < textSnippetsExtractedOutOfXML.length; i++) {
+ lst_txtSnipList.add(textSnippetsExtractedOutOfXML[i]);
+ System.out.println("[Chatpreferences, Info]: Setting Snip " + i + " \""
+ + textSnippetsExtractedOutOfXML[i] + "\"");
+ }
+ }
+
+ /**
+ * Case beaconCQ
+ */
+
+ list = doc.getElementsByTagName("beaconCQ");
+ if (list.getLength() != 0) {
+
+ for (int temp = 0; temp < list.getLength(); temp++) {
+
+ Node node = list.item(temp);
+
+ if (node.getNodeType() == Node.ELEMENT_NODE) {
+
+ Element element = (Element) node;
+
+ String beaconCQEnabled = element.getElementsByTagName("beaconCQEnabled").item(0)
+ .getTextContent();
+
+ if (beaconCQEnabled.equals("true")) {
+
+ bcn_beaconsEnabled = true;
+ } else {
+ bcn_beaconsEnabled = false;
+ }
+
+ String beaconCQIntervalMinutes = element.getElementsByTagName("beaconCQIntervalMinutes").item(0)
+ .getTextContent();
+
+ if (isNumeric(beaconCQIntervalMinutes)) {
+ bcn_beaconIntervalInMinutes = Integer.parseInt(beaconCQIntervalMinutes);
+ } else {
+ bcn_beaconIntervalInMinutes = 20; // Default value, TODO: Set this in default list
+ }
+
+ String beaconCQText = element.getElementsByTagName("beaconCQText").item(0).getTextContent();
+ this.setBcn_beaconText(beaconCQText);
+
+//
+ System.out.println("[ChatPreferences, info]: set the beacon text to: " + beaconCQText);
+
+ }
+ }
+ }
+
+ /**
+ * Case beaconUnworkedstations
+ *
+ */
+
+ list = doc.getElementsByTagName("beaconUnworkedstations");
+ if (list.getLength() != 0) {
+
+ for (int temp = 0; temp < list.getLength(); temp++) {
+
+ Node node = list.item(temp);
+
+ if (node.getNodeType() == Node.ELEMENT_NODE) {
+
+ Element element = (Element) node;
+//
+ String beaconUnworkedstationsText = element.getElementsByTagName("beaconUnworkedstationsText")
+ .item(0).getTextContent();
+ unwkd_unworkedStnRequesterBeaconsText = beaconUnworkedstationsText;
+
+ String beaconUnworkedstationsIntervalMinutes = element
+ .getElementsByTagName("beaconUnworkedstationsIntervalMinutes").item(0).getTextContent();
+
+ if (isNumeric(beaconUnworkedstationsIntervalMinutes)) {
+ unwkd_unworkedStnRequesterBeaconsInterval = Integer
+ .parseInt(beaconUnworkedstationsIntervalMinutes);
+ } else {
+ unwkd_unworkedStnRequesterBeaconsInterval = 20;
+ }
+
+ String beaconUnworkedstationsEnabled = element
+ .getElementsByTagName("beaconUnworkedstationsEnabled").item(0).getTextContent();
+
+ if (beaconUnworkedstationsEnabled.equals("true")) {
+ unwkd_unworkedStnRequesterBeaconsEnabled = true;
+ } else {
+ unwkd_unworkedStnRequesterBeaconsEnabled = false;
+ }
+
+ String beaconUnworkedstationsPrefix = element
+ .getElementsByTagName("beaconUnworkedstationsPrefix").item(0).getTextContent();
+
+ unwkd_beaconUnworkedstationsPrefix = beaconUnworkedstationsPrefix;
+
+ }
+ }
+ System.out.println("[ChatPreferences, info]: set the unworked stn beacon text to: "
+ + unwkd_unworkedStnRequesterBeaconsText);
+ }
+
+ } catch (ParserConfigurationException | SAXException | IOException e) {
+ e.printStackTrace();
+ System.out.println(e.getCause());
+ System.out.println(e.getMessage());
+ }
+
+ return true;
+
+ }
+
+
+ /**
+ *
+ * If the file-reading goes wrong, set the defaults
+ */
+ public void setPreferencesDefaults() {
+ System.out.println("[ChatPreferences, Info]: restoring prefs from defaults");
+
+ }
+
+ /**
+ * Checks wheter the input value of the String is numeric or not, true if yes
+ * TODO: Move to a utils class for checking input values by user...
+ *
+ * @param str
+ * @return
+ */
+ private static boolean isNumeric(String str) {
+ return str != null && str.matches("[0-9.]+");
+ }
+
+// public ObservableList getTxtSnippetsAsObservableList(){
+// ObservableList lst_txtSnipList = FXCollections.observableArrayList();
+//
+// for (int i = 0; i < textSnippets.length; i++) {
+// lst_txtSnipList.add(textSnippets[i]);
+// System.out.println(textSnippets[i]);
+// }
+//
+// return lst_txtSnipList;
+//
+// }
+
+}
diff --git a/src/main/java/kst4contest/model/ClusterMessage.java b/src/main/java/kst4contest/model/ClusterMessage.java
new file mode 100644
index 0000000..887abd1
--- /dev/null
+++ b/src/main/java/kst4contest/model/ClusterMessage.java
@@ -0,0 +1,67 @@
+package kst4contest.model;
+
+public class ClusterMessage {
+
+ String timeGenerated;
+ ChatMember sender;
+ ChatMember receiver;
+ String qrgSpotted;
+ String qraOfSender;
+ String qraOfReceiver;
+ String messageInhibited;
+ boolean receiverWkd;
+
+
+
+ public boolean isReceiverWkd() {
+ return receiverWkd;
+ }
+ public void setReceiverWkd(boolean receiverWkd) {
+ this.receiverWkd = receiverWkd;
+ }
+ public String getTimeGenerated() {
+ return timeGenerated;
+ }
+ public void setTimeGenerated(String timeGenerated) {
+ this.timeGenerated = timeGenerated;
+ }
+ public ChatMember getSender() {
+ return sender;
+ }
+ public void setSender(ChatMember sender) {
+ this.sender = sender;
+ }
+ public ChatMember getReceiver() {
+ return receiver;
+ }
+ public void setReceiver(ChatMember receiver) {
+ this.receiver = receiver;
+ }
+ public String getQrgSpotted() {
+ return qrgSpotted;
+ }
+ public void setQrgSpotted(String qrgSpotted) {
+ this.qrgSpotted = qrgSpotted;
+ }
+ public String getQraOfSender() {
+ return qraOfSender;
+ }
+ public void setQraOfSender(String qraOfSender) {
+ this.qraOfSender = qraOfSender;
+ }
+ public String getQraOfReceiver() {
+ return qraOfReceiver;
+ }
+ public void setQraOfReceiver(String qraOfReceiver) {
+ this.qraOfReceiver = qraOfReceiver;
+ }
+ public String getMessageInhibited() {
+ return messageInhibited;
+ }
+ public void setMessageInhibited(String messageInhibited) {
+ this.messageInhibited = messageInhibited;
+ }
+
+
+
+}
diff --git a/src/main/java/kst4contest/utils/ApplicationFileUtils.java b/src/main/java/kst4contest/utils/ApplicationFileUtils.java
new file mode 100644
index 0000000..3f457cd
--- /dev/null
+++ b/src/main/java/kst4contest/utils/ApplicationFileUtils.java
@@ -0,0 +1,69 @@
+package kst4contest.utils;
+
+import kst4contest.controller.DBController;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.file.Path;
+
+/**
+ * This class has utility methods to handle application files inside the home directory.
+ */
+public class ApplicationFileUtils {
+
+ /**
+ * Gets the path of a file inside the home directory of the user.
+ * @param applicationName Name off the application which is used for the hidden directory
+ * @param fileName Name of the file.
+ * @return The full path inside the user home directory.
+ */
+ public static String getFilePath(final String applicationName, final String fileName) {
+ return Path.of(System.getProperty("user.home"),
+ "." + applicationName,
+ fileName).toString();
+ }
+
+ /**
+ * Copies a resource to the application folder if required.
+ * @param applicationName Name of the application.
+ * @param resourcePath Path of the resource.
+ * @param fileName Name of the target file.
+ */
+ public static void copyResourceIfRequired(final String applicationName, final String resourcePath, final String fileName) {
+ File file = new File(getFilePath(applicationName, fileName));
+
+ if (file.exists()) {
+ return;
+ }
+
+ File parentDir = file.getParentFile();
+ if (!parentDir.exists()) {
+ parentDir.mkdirs();
+ }
+
+ copyResourceToFile(resourcePath, file.getPath());
+ }
+
+ /**
+ * Copies a given resource at a resourcePath to the file path.
+ * @param resourcePath
+ * @param filePath
+ */
+ public static void copyResourceToFile(String resourcePath, String filePath) {
+ try (InputStream resourceStream = ApplicationFileUtils.class.getResourceAsStream(resourcePath);
+ FileOutputStream fileOutputStream = new FileOutputStream(filePath)) {
+
+ if (resourceStream == null) {
+ throw new IllegalArgumentException("Resource not found: " + resourcePath);
+ }
+
+ resourceStream.transferTo(fileOutputStream);
+ } catch (IOException ex) {
+ System.err.println("Exception when copying Application file: " + ex.getMessage());
+ ex.printStackTrace(System.err);
+ }
+ }
+
+}
diff --git a/src/main/java/kst4contest/view/GuiUtils.java b/src/main/java/kst4contest/view/GuiUtils.java
new file mode 100644
index 0000000..9531991
--- /dev/null
+++ b/src/main/java/kst4contest/view/GuiUtils.java
@@ -0,0 +1,15 @@
+package kst4contest.view;
+
+public class GuiUtils {
+
+ /**
+ * Checks wheter the input value of the String is numeric or not, true if yes
+ * TODO: Move to a utils class for checking input values by user...
+ * @param str
+ * @return
+ */
+ static boolean isNumeric(String str){
+ return str != null && str.matches("[0-9.]+");
+ }
+
+}
diff --git a/src/main/java/kst4contest/view/Kst4ContestApplication.java b/src/main/java/kst4contest/view/Kst4ContestApplication.java
new file mode 100644
index 0000000..0abcef9
--- /dev/null
+++ b/src/main/java/kst4contest/view/Kst4ContestApplication.java
@@ -0,0 +1,3220 @@
+package kst4contest.view;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.Date;
+import java.util.Iterator;
+import java.util.Optional;
+import java.util.Timer;
+import java.util.TimerTask;
+
+import kst4contest.controller.ChatController;
+import kst4contest.controller.Utils4KST;
+import javafx.application.Application;
+import javafx.application.Platform;
+import javafx.beans.property.SimpleStringProperty;
+import javafx.beans.property.StringProperty;
+import javafx.beans.value.ChangeListener;
+import javafx.beans.value.ObservableValue;
+import javafx.collections.ListChangeListener;
+import javafx.collections.ObservableList;
+import javafx.event.ActionEvent;
+import javafx.event.EventHandler;
+import javafx.geometry.Insets;
+import javafx.geometry.Orientation;
+import javafx.geometry.Pos;
+import javafx.geometry.VPos;
+import javafx.scene.Node;
+import javafx.scene.Scene;
+import javafx.scene.control.Alert;
+import javafx.scene.control.Alert.AlertType;
+import javafx.scene.control.Button;
+import javafx.scene.control.ButtonType;
+import javafx.scene.control.CheckBox;
+import javafx.scene.control.ChoiceBox;
+import javafx.scene.control.ContextMenu;
+import javafx.scene.control.Label;
+import javafx.scene.control.Menu;
+import javafx.scene.control.MenuBar;
+import javafx.scene.control.MenuItem;
+import javafx.scene.control.PasswordField;
+import javafx.scene.control.SelectionMode;
+import javafx.scene.control.Separator;
+import javafx.scene.control.SplitPane;
+import javafx.scene.control.Tab;
+import javafx.scene.control.TabPane;
+import javafx.scene.control.TableCell;
+import javafx.scene.control.TableColumn;
+import javafx.scene.control.TableColumn.CellDataFeatures;
+import javafx.scene.control.TableColumn.CellEditEvent;
+import javafx.scene.control.TableView;
+import javafx.scene.control.TableView.TableViewSelectionModel;
+import javafx.scene.control.TextField;
+import javafx.scene.control.Tooltip;
+import javafx.scene.control.cell.TextFieldTableCell;
+import javafx.scene.input.KeyCode;
+import javafx.scene.input.KeyEvent;
+import javafx.scene.input.MouseButton;
+import javafx.scene.input.MouseEvent;
+import javafx.scene.layout.AnchorPane;
+import javafx.scene.layout.BorderPane;
+import javafx.scene.layout.FlowPane;
+import javafx.scene.layout.GridPane;
+import javafx.scene.layout.HBox;
+import javafx.scene.layout.VBox;
+import javafx.scene.paint.Color;
+import javafx.stage.FileChooser;
+import javafx.stage.Stage;
+import javafx.stage.WindowEvent;
+import javafx.util.Callback;
+import kst4contest.model.ChatCategory;
+import kst4contest.model.ChatMember;
+import kst4contest.model.ChatMessage;
+import kst4contest.model.ClusterMessage;
+
+public class Kst4ContestApplication extends Application {
+
+ String chatState;
+ ChatController chatcontroller;
+ Button MYQRGButton; // TODO: clean code? Got the myqrg button out of the factory method to modify
+ // the text later
+ Button MYCALLSetQRGButton;
+
+ Timer timer_buildWindowTitle;
+ Timer timer_chatMemberTableSortTimer; // need that because javafx bug, it´s the only way to actualize the table...
+ Timer timer_updatePrivatemessageTable; // same here
+
+ private TableView initChatMemberTable() {
+
+ TableView tbl_chatMemberTable = new TableView();
+ tbl_chatMemberTable.setTooltip(new Tooltip(
+ "Stations available \n\n Use right click to a station to select predefined texts \n\n Texts can be changed in the config-file"));
+
+ TableColumn callSignCol = new TableColumn("Callsign");
+ callSignCol.setCellValueFactory(new Callback, ObservableValue>() {
+
+ @Override
+ public ObservableValue call(CellDataFeatures cellDataFeatures) {
+ SimpleStringProperty callsgn = new SimpleStringProperty();
+
+ callsgn.setValue(cellDataFeatures.getValue().getCallSign());
+
+ return callsgn;
+ }
+ });
+
+ TableColumn nameCol = new TableColumn("Name");
+ nameCol.setCellValueFactory(new Callback, ObservableValue>() {
+
+ @Override
+ public ObservableValue call(CellDataFeatures cellDataFeatures) {
+ SimpleStringProperty name = new SimpleStringProperty();
+
+ name.setValue(cellDataFeatures.getValue().getName());
+
+ return name;
+ }
+ });
+
+ TableColumn qraCol = new TableColumn("QRA");
+ qraCol.setCellValueFactory(new Callback, ObservableValue>() {
+
+ @Override
+ public ObservableValue call(CellDataFeatures cellDataFeatures) {
+ SimpleStringProperty qra = new SimpleStringProperty();
+
+ qra.setValue(cellDataFeatures.getValue().getQra());
+
+ return qra;
+ }
+ });
+
+ TableColumn qrgCol = new TableColumn("QRG");
+ qrgCol.setCellValueFactory(new Callback, ObservableValue>() {
+
+ @Override
+ public ObservableValue call(CellDataFeatures cellDataFeatures) {
+// StringProperty qrg = new SimpleStringProperty();
+
+// qrg.setValue(cellDataFeatures.getValue().getFrequency());
+// qrg = (cellDataFeatures.getValue().getFrequency());
+
+// if (!qrg.getValue().equals("")) {
+//
+// }
+
+ return cellDataFeatures.getValue().getFrequency();
+ }
+
+ });
+
+ TableColumn airScoutCol = new TableColumn("AP [minutes / pot%]");
+ airScoutCol.setCellValueFactory(new Callback, ObservableValue>() {
+
+ @Override
+ public ObservableValue call(CellDataFeatures cellDataFeatures) {
+ SimpleStringProperty airPlaneInfo = new SimpleStringProperty();
+
+ if (cellDataFeatures.getValue().getAirPlaneReflectInfo().getRisingAirplanes() == null) {
+ airPlaneInfo.setValue(" ");
+ }
+
+ else if (cellDataFeatures.getValue().getAirPlaneReflectInfo().getRisingAirplanes().size() <= 0) {
+ airPlaneInfo.setValue(" ");
+ } else {
+ String apInfoText = ""
+ + cellDataFeatures.getValue().getAirPlaneReflectInfo().getRisingAirplanes().get(0)
+ .getArrivingDurationMinutes()
+ + " (" + cellDataFeatures.getValue().getAirPlaneReflectInfo().getRisingAirplanes().get(0)
+ .getPotential()
+ + "%)";
+//
+//
+ if (cellDataFeatures.getValue().getAirPlaneReflectInfo().getRisingAirplanes().size() > 1) {
+ apInfoText += " / "
+ + cellDataFeatures.getValue().getAirPlaneReflectInfo().getRisingAirplanes().get(1)
+ .getArrivingDurationMinutes()
+ + " (" + cellDataFeatures.getValue().getAirPlaneReflectInfo().getRisingAirplanes()
+ .get(1).getPotential()
+ + "%)";
+ }
+
+ airPlaneInfo.setValue(apInfoText);
+ }
+
+ return airPlaneInfo;
+ }
+ });
+ /**
+ * HIGH EXPERIMENTAL::::::::
+ */
+ airScoutCol.setCellFactory(new Callback, TableCell>() {
+ public TableCell call(TableColumn param) {
+ return new TableCell() {
+
+ @Override
+ public void updateItem(String item, boolean empty) {
+ super.updateItem(item, empty);
+ if (!isEmpty()) {
+ this.setTextFill(Color.BLACK);
+ // Get fancy and change color based on data
+ if (item.contains("100%")) {
+ this.setTextFill(Color.BLUEVIOLET);
+ } else if (item.contains("75%") && !item.contains("100%")) {
+ this.setTextFill(Color.RED);
+ } else if (item.contains("50%") && ((!item.contains("100%")) || (!item.contains("75%")))) {
+ this.setTextFill(Color.ORANGE);
+ }
+
+ setText(item);
+ }
+ }
+ };
+ }
+ });
+ /**
+ * END HIGH EXPERIMENTAL::::::::
+ */
+
+ TableColumn workedCol = new TableColumn("worked");
+ workedCol.setCellValueFactory(new Callback, ObservableValue>() {
+
+ @Override
+ public ObservableValue call(CellDataFeatures cellDataFeatures) {
+ SimpleStringProperty wkd = new SimpleStringProperty();
+
+ if (cellDataFeatures.getValue().isWorked()) {
+ wkd.setValue("X");
+ } else {
+ wkd.setValue("");
+ }
+
+ return wkd;
+ }
+ });
+
+ TableColumn wkdAny_subcol = new TableColumn("wkd");
+ wkdAny_subcol
+ .setCellValueFactory(new Callback, ObservableValue>() {
+
+ @Override
+ public ObservableValue call(CellDataFeatures cellDataFeatures) {
+ SimpleStringProperty wkd = new SimpleStringProperty();
+
+ if (cellDataFeatures.getValue().isWorked()) {
+ wkd.setValue("X");
+ } else {
+ wkd.setValue("");
+ }
+
+ return wkd;
+ }
+ });
+ wkdAny_subcol.prefWidthProperty().bind(tbl_chatMemberTable.widthProperty().divide(28));
+
+ TableColumn vhfCol_subcol = new TableColumn("144");
+ vhfCol_subcol
+ .setCellValueFactory(new Callback, ObservableValue>() {
+
+ @Override
+ public ObservableValue call(CellDataFeatures cellDataFeatures) {
+ SimpleStringProperty wkd = new SimpleStringProperty();
+
+ if (cellDataFeatures.getValue().isWorked144()) {
+ wkd.setValue("X");
+ } else {
+ wkd.setValue("");
+ }
+
+ return wkd;
+ }
+ });
+ vhfCol_subcol.prefWidthProperty().bind(tbl_chatMemberTable.widthProperty().divide(28));
+
+ TableColumn uhfCol_subcol = new TableColumn("432");
+ uhfCol_subcol
+ .setCellValueFactory(new Callback, ObservableValue>() {
+
+ @Override
+ public ObservableValue call(CellDataFeatures cellDataFeatures) {
+ SimpleStringProperty wkd = new SimpleStringProperty();
+
+ if (cellDataFeatures.getValue().isWorked432()) {
+ wkd.setValue("X");
+ } else {
+ wkd.setValue("");
+ }
+
+ return wkd;
+ }
+ });
+
+ uhfCol_subcol.prefWidthProperty().bind(tbl_chatMemberTable.widthProperty().divide(28));
+
+ TableColumn shf23_subcol = new TableColumn("23");
+ shf23_subcol.setCellValueFactory(new Callback, ObservableValue>() {
+
+ @Override
+ public ObservableValue call(CellDataFeatures cellDataFeatures) {
+ SimpleStringProperty wkd = new SimpleStringProperty();
+
+ if (cellDataFeatures.getValue().isWorked1240()) {
+ wkd.setValue("X");
+ } else {
+ wkd.setValue("");
+ }
+
+ return wkd;
+ }
+ });
+ shf23_subcol.prefWidthProperty().bind(tbl_chatMemberTable.widthProperty().divide(30));
+
+ TableColumn shf13_subcol = new TableColumn("13");
+ shf13_subcol.setCellValueFactory(new Callback, ObservableValue>() {
+
+ @Override
+ public ObservableValue call(CellDataFeatures cellDataFeatures) {
+ SimpleStringProperty wkd = new SimpleStringProperty();
+
+ if (cellDataFeatures.getValue().isWorked2300()) {
+ wkd.setValue("X");
+ } else {
+ wkd.setValue("");
+ }
+
+ return wkd;
+ }
+ });
+ shf13_subcol.prefWidthProperty().bind(tbl_chatMemberTable.widthProperty().divide(30));
+
+ TableColumn shf9_subcol = new TableColumn("9");
+ shf9_subcol.setCellValueFactory(new Callback, ObservableValue>() {
+
+ @Override
+ public ObservableValue call(CellDataFeatures cellDataFeatures) {
+ SimpleStringProperty wkd = new SimpleStringProperty();
+
+ if (cellDataFeatures.getValue().isWorked3400()) {
+ wkd.setValue("X");
+ } else {
+ wkd.setValue("");
+ }
+
+ return wkd;
+ }
+ });
+ shf9_subcol.prefWidthProperty().bind(tbl_chatMemberTable.widthProperty().divide(32));
+
+ TableColumn shf6_subcol = new TableColumn("6");
+ shf6_subcol.setCellValueFactory(new Callback, ObservableValue>() {
+
+ @Override
+ public ObservableValue call(CellDataFeatures cellDataFeatures) {
+ SimpleStringProperty wkd = new SimpleStringProperty();
+
+ if (cellDataFeatures.getValue().isWorked5600()) {
+ wkd.setValue("X");
+ } else {
+ wkd.setValue("");
+ }
+
+ return wkd;
+ }
+ });
+ shf6_subcol.prefWidthProperty().bind(tbl_chatMemberTable.widthProperty().divide(32));
+
+ TableColumn shf3_subcol = new TableColumn("3");
+ shf3_subcol.setCellValueFactory(new Callback, ObservableValue>() {
+
+ @Override
+ public ObservableValue call(CellDataFeatures cellDataFeatures) {
+ SimpleStringProperty wkd = new SimpleStringProperty();
+
+ if (cellDataFeatures.getValue().isWorked10G()) {
+ wkd.setValue("X");
+ } else {
+ wkd.setValue("");
+ }
+
+ return wkd;
+ }
+ });
+
+ shf3_subcol.prefWidthProperty().bind(tbl_chatMemberTable.widthProperty().divide(32));
+
+// TableColumn uhfCol_subcol = new TableColumn("432"); //TODO: Worked band analysis
+// TableColumn shf23_subcol = new TableColumn("23");
+// TableColumn shf13_subcol = new TableColumn("13");
+// TableColumn shf9_subcol = new TableColumn("9");
+// TableColumn shf6_subcol = new TableColumn("6");
+// TableColumn shf3_subcol = new TableColumn("3");
+ workedCol.getColumns().addAll(wkdAny_subcol, vhfCol_subcol, uhfCol_subcol, shf23_subcol, shf13_subcol,
+ shf9_subcol, shf6_subcol, shf3_subcol); // TODO: automatize enabling to users bandChoice
+
+ tbl_chatMemberTable.getColumns().addAll(callSignCol, nameCol, qraCol, qrgCol, airScoutCol, workedCol);
+
+ tbl_chatMemberTable.setItems(chatcontroller.getLst_chatMemberList());
+
+// chatcontroller.getLst_chatMemberList().addListener(new ListChangeListener() {
+//// ObservableStringValue chatState = new SimpleStringProperty();
+//
+// @Override
+// public void onChanged(javafx.collections.ListChangeListener.Change extends ChatMember> pChange) {
+//// while (pChange.next()) {
+//// System.out.println("List changed");
+//
+// //TODO: Das kann man ggf anders machen
+//
+// String chatState = chatcontroller.getChatPreferences().getProgramVersion() + " / "
+// + "Connected to: " + chatcontroller.getChatPreferences().getLoginChatCategory() + " / "
+// + chatcontroller.getLst_chatMemberList().size() + " users online.";
+// chatcontroller.getChatPreferences().setChatState(chatState);
+//
+//// chatcontroller.getChatPreferences().setChatState(chatcontroller.getChatPreferences().getProgramVersion() + " / "
+//// + "Connected to: " + chatcontroller.getChatPreferences().getLoginChatCategory() + " / "
+//// + chatcontroller.getLst_chatMemberList().size() + " users online.");
+//// primaryStage.setTitle("asdf");
+//// primaryStage.setTitle(chatcontroller.getChatPreferences().getProgramVersion() + " / "
+//// + "Connected to: " + chatcontroller.getChatPreferences().getLoginChatCategory() + " / "
+//// + chatcontroller.getLst_chatMemberList().size() + " users online.");
+//// }
+// }
+// });
+
+ tbl_chatMemberTable.getSortOrder().add(callSignCol);
+
+ /**
+ * timer_chatMemberTableSortTimer -->
+ * This part fixes a javafx bug. The update of the Chatmember fields is (for any
+ * reason) not visible in the ui. Its neccessarry to sort the table in intervals
+ * to keep the table up to date. Otherwise
+ */
+
+ timer_chatMemberTableSortTimer = new Timer();
+
+ timer_chatMemberTableSortTimer.scheduleAtFixedRate(new TimerTask() {
+
+ public void run() {
+ Thread.currentThread().setName("chatMemberTableSortTimer");
+
+ Platform.runLater(() -> {
+
+ try {
+
+// tbl_chatMemberTable.sort();
+
+ } catch (Exception e) {
+ System.out.println("[Main.java, Warning:] Table sorting (actualizing) failed this time.");
+ }
+
+ tbl_chatMemberTable.refresh();
+
+ });
+ }
+ }, new Date(), 3000);
+
+ tbl_chatMemberTable.setColumnResizePolicy(TableView.UNCONSTRAINED_RESIZE_POLICY);
+ tbl_chatMemberTable.autosize();
+
+ return tbl_chatMemberTable;
+ }
+
+ /**
+ * Initializes the right click contextmenu for the chatmember-table, sets the
+ * clickhandler for the contextmenu out of a string array (each menuitam will be
+ * created out of exact one array-entry). These are initialized by the
+ * chatpreferences object out of the config-xml
+ *
+ * @param menuTexts
+ * @return
+ */
+// private ContextMenu initChatMemberTableContextMenu(String[] menuTexts) { old mechanic
+//
+// ContextMenu chatMemberContextMenu = new ContextMenu();
+//
+// for (int i = 0; i < menuTexts.length; i++) {
+// final MenuItem menuItem = new MenuItem(menuTexts[i]);
+// menuItem.setOnAction(new EventHandler() {
+// public void handle(ActionEvent event) {
+// txt_chatMessageUserInput.setText(txt_chatMessageUserInput.getText() + menuItem.getText());
+// }
+// });
+//
+// chatMemberContextMenu.getItems().add(menuItem);
+// }
+//
+//// MenuItem macro1 = new MenuItem("Pse Sked?");
+//// macro1.setOnAction(new EventHandler() {
+//// public void handle(ActionEvent event) {
+//// txt_chatMessageUserInput.setText(txt_chatMessageUserInput.getText() + macro1.getText());
+//// }
+//// });
+//// MenuItem macro10 = new MenuItem("Pse qrg 2m?");
+//// MenuItem macro20 = new MenuItem("Pse Call at ");
+//// MenuItem macro30 = new MenuItem("In qso nw, pse qrx, I will meep you");
+//// MenuItem macro40 = new MenuItem("Pse qrg 70cm?");
+//// MenuItem macro50 = new MenuItem("pse qrg 23cm?");
+//// MenuItem macro60 = new MenuItem("____________________________________");
+//// MenuItem macro70 = new MenuItem("Watch QSO history");
+////
+//// chatMemberContextMenu.getItems().add(macro1);
+//// chatMemberContextMenu.getItems().add(macro10);
+//// chatMemberContextMenu.getItems().add(macro20);
+//// chatMemberContextMenu.getItems().add(macro30);
+//// chatMemberContextMenu.getItems().add(macro40);
+//// chatMemberContextMenu.getItems().add(macro50);
+//// chatMemberContextMenu.getItems().add(macro60);
+//// chatMemberContextMenu.getItems().add(macro70);
+//
+// return chatMemberContextMenu;
+//
+// }
+
+ /**
+ * Initializes the right click contextmenu for the chatmember-table, sets the
+ * clickhandler for the contextmenu out of a string array (each menuitam will be
+ * created out of exact one array-entry). These are initialized by the
+ * chatpreferences object out of the config-xml
+ *
+ * @param menuTexts
+ * @return
+ */
+ private ContextMenu initChatMemberTableContextMenu(ObservableList contextMenuEntries) { // new mechanic
+
+ ContextMenu chatMemberContextMenu = new ContextMenu();
+
+ for (Iterator iterator = contextMenuEntries.iterator(); iterator.hasNext();) {
+ String string = (String) iterator.next();
+ final MenuItem menuItem = new MenuItem(string);
+ menuItem.setOnAction(new EventHandler() {
+ public void handle(ActionEvent event) {
+ txt_chatMessageUserInput.setText(txt_chatMessageUserInput.getText() + menuItem.getText());
+ }
+ });
+
+ chatMemberContextMenu.getItems().add(menuItem);
+
+ }
+
+// MenuItem macro1 = new MenuItem("Pse Sked?");
+// macro1.setOnAction(new EventHandler() {
+// public void handle(ActionEvent event) {
+// txt_chatMessageUserInput.setText(txt_chatMessageUserInput.getText() + macro1.getText());
+// }
+// });
+// MenuItem macro10 = new MenuItem("Pse qrg 2m?");
+// MenuItem macro20 = new MenuItem("Pse Call at ");
+// MenuItem macro30 = new MenuItem("In qso nw, pse qrx, I will meep you");
+// MenuItem macro40 = new MenuItem("Pse qrg 70cm?");
+// MenuItem macro50 = new MenuItem("pse qrg 23cm?");
+// MenuItem macro60 = new MenuItem("____________________________________");
+// MenuItem macro70 = new MenuItem("Watch QSO history");
+//
+// chatMemberContextMenu.getItems().add(macro1);
+// chatMemberContextMenu.getItems().add(macro10);
+// chatMemberContextMenu.getItems().add(macro20);
+// chatMemberContextMenu.getItems().add(macro30);
+// chatMemberContextMenu.getItems().add(macro40);
+// chatMemberContextMenu.getItems().add(macro50);
+// chatMemberContextMenu.getItems().add(macro60);
+// chatMemberContextMenu.getItems().add(macro70);
+
+ return chatMemberContextMenu;
+
+ }
+
+ /**
+ * initializes the tableview in which the cq- and beacon-texts are shown
+ *
+ * @return
+ */
+ private TableView initChatGeneralMSGTable() {
+
+ TableView tbl_generalMSGTable = new TableView();
+ tbl_generalMSGTable.setTooltip(new Tooltip("General messages are shown here (handle it like CQ messages)"));
+
+ TableColumn timeCol = new TableColumn("Time");
+ timeCol.setCellValueFactory(new Callback, ObservableValue>() {
+
+ @Override
+ public ObservableValue call(CellDataFeatures cellDataFeatures) {
+ SimpleStringProperty time = new SimpleStringProperty();
+
+ time.setValue(new Utils4KST()
+ .time_convertEpochToReadable(cellDataFeatures.getValue().getMessageGeneratedTime()));
+
+ return time;
+ }
+ });
+
+ TableColumn callSignCol = new TableColumn("Callsign");
+ callSignCol.setCellValueFactory(new Callback, ObservableValue>() {
+
+ @Override
+ public ObservableValue call(CellDataFeatures cellDataFeatures) {
+ SimpleStringProperty callSign = new SimpleStringProperty();
+
+ if (cellDataFeatures.getValue().getSender() != null) {
+
+ callSign.setValue(cellDataFeatures.getValue().getSender().getCallSign());
+ } else {
+
+ callSign.setValue("");// TODO: Prevents a bug of not setting all values as a default
+ }
+ return callSign;
+ }
+ });
+
+ TableColumn nameCol = new TableColumn("Name");
+ nameCol.setCellValueFactory(new Callback, ObservableValue>() {
+
+ @Override
+ public ObservableValue call(CellDataFeatures cellDataFeatures) {
+ SimpleStringProperty name = new SimpleStringProperty();
+
+ if (cellDataFeatures.getValue().getSender() != null) {
+
+ name.setValue(cellDataFeatures.getValue().getSender().getName());
+ } else {
+
+ name.setValue("");// TODO: Prevents a bug of not setting all values as a default
+ }
+ return name;
+ }
+ });
+
+ TableColumn qrgCol = new TableColumn("Last QRG");
+ qrgCol.setCellValueFactory(new Callback, ObservableValue>() {
+
+ @Override
+ public ObservableValue call(CellDataFeatures cellDataFeatures) {
+ StringProperty qrg = new SimpleStringProperty();
+
+ if (cellDataFeatures.getValue().getSender() != null) {
+
+// qrg.setValue(cellDataFeatures.getValue().getSender().getFrequency());
+ qrg = cellDataFeatures.getValue().getSender().getFrequency();
+ } else {
+
+ qrg.setValue("");// TODO: Prevents a bug of not setting all values as a default
+ }
+ return qrg;
+ }
+ });
+
+ TableColumn msgCol = new TableColumn("Message");
+ msgCol.setCellValueFactory(new Callback, ObservableValue>() {
+
+ @Override
+ public ObservableValue call(CellDataFeatures cellDataFeatures) {
+ SimpleStringProperty msg = new SimpleStringProperty();
+
+ if (cellDataFeatures.getValue().getMessageText() != null) {
+
+ msg.setValue(cellDataFeatures.getValue().getMessageText());
+ } else {
+
+ msg.setValue("");// TODO: Prevents a bug of not setting all values as a default
+ }
+ return msg;
+ }
+ });
+ msgCol.prefWidthProperty().bind(tbl_generalMSGTable.widthProperty().divide(2));
+
+ msgCol.setCellFactory(new Callback, TableCell>() {
+ public TableCell call(TableColumn param) {
+ return new TableCell() {
+
+ @Override
+ public void updateItem(String item, boolean empty) {
+ super.updateItem(item, empty);
+ if (!isEmpty()) {
+ this.setTextFill(Color.BLACK);
+ // Get fancy and change color based on data
+ if (item.toUpperCase()
+ .contains(chatcontroller.getChatPreferences().getLoginCallSign().toUpperCase())) {
+ this.setTextFill(Color.GREEN);
+ }
+ setText(item);
+ }
+ }
+ };
+ }
+ });
+
+ tbl_generalMSGTable.getColumns().addAll(timeCol, callSignCol, nameCol, msgCol, qrgCol);
+
+ ObservableList generalMSGList = chatcontroller.getLst_toAllMessageList();
+ tbl_generalMSGTable.setItems(generalMSGList);
+
+ return tbl_generalMSGTable;
+ }
+
+ private TableView initChatprivateMSGTable() {
+
+ TableView tbl_privateMSGTable = new TableView();
+ tbl_privateMSGTable.setTooltip(new Tooltip("Private messages to you are shown here"));
+
+ TableColumn timeCol = new TableColumn("Time");
+ timeCol.setCellValueFactory(new Callback, ObservableValue>() {
+
+ @Override
+ public ObservableValue call(CellDataFeatures cellDataFeatures) {
+ SimpleStringProperty time = new SimpleStringProperty();
+
+ time.setValue(new Utils4KST()
+ .time_convertEpochToReadable(cellDataFeatures.getValue().getMessageGeneratedTime()));
+
+ return time;
+ }
+ });
+
+ TableColumn callSignCol = new TableColumn("Callsign");
+ callSignCol.setCellValueFactory(new Callback, ObservableValue>() {
+
+ @Override
+ public ObservableValue call(CellDataFeatures cellDataFeatures) {
+ SimpleStringProperty callSign = new SimpleStringProperty();
+
+ if (cellDataFeatures.getValue().getSender() != null) {
+
+ callSign.setValue(cellDataFeatures.getValue().getSender().getCallSign());
+ } else {
+
+ callSign.setValue("");// TODO: Prevents a bug of not setting all values as a default
+ }
+ return callSign;
+ }
+ });
+
+ callSignCol.setCellFactory(new Callback, TableCell>() {
+ public TableCell call(TableColumn param) {
+ return new TableCell() {
+
+ @Override
+ public void updateItem(String item, boolean empty) {
+ super.updateItem(item, empty);
+ if (!isEmpty()) {
+ this.setTextFill(Color.BLACK);
+ // Get fancy and change color based on data
+ if (item.contains(chatcontroller.getChatPreferences().getLoginCallSign())) {
+ this.setTextFill(Color.GREEN);
+
+// AudioClip sound = new AudioClip(getClass().getResource("/K.mp3").toExternalForm());
+// sound.play();
+
+ }
+ setText(item);
+ }
+ }
+ };
+ }
+ });
+
+ TableColumn nameCol = new TableColumn("Name");
+ nameCol.setCellValueFactory(new Callback, ObservableValue>() {
+
+ @Override
+ public ObservableValue call(CellDataFeatures cellDataFeatures) {
+ SimpleStringProperty name = new SimpleStringProperty();
+
+ if (cellDataFeatures.getValue().getSender() != null) {
+
+ name.setValue(cellDataFeatures.getValue().getSender().getName());
+ } else {
+
+ name.setValue("");// TODO: Prevents a bug of not setting all values as a default
+ }
+ return name;
+ }
+ });
+
+ TableColumn qraCol = new TableColumn("QRA");
+ qraCol.setCellValueFactory(new Callback, ObservableValue>() {
+
+ @Override
+ public ObservableValue call(CellDataFeatures cellDataFeatures) {
+ SimpleStringProperty qra = new SimpleStringProperty();
+
+ if (cellDataFeatures.getValue().getSender() != null) {
+
+ qra.setValue(cellDataFeatures.getValue().getSender().getQra());
+ } else {
+
+ qra.setValue("");// TODO: Prevents a bug of not setting all values as a default
+ }
+ return qra;
+ }
+ });
+
+ TableColumn qrgCol = new TableColumn("Last known QRG");
+ qrgCol.setCellValueFactory(new Callback, ObservableValue>() {
+
+ @Override
+ public ObservableValue call(CellDataFeatures cellDataFeatures) {
+ StringProperty qrg = new SimpleStringProperty();
+
+ if (cellDataFeatures.getValue().getSender() != null) {
+
+// qrg.setValue(cellDataFeatures.getValue().getSender().getFrequency());
+ qrg = cellDataFeatures.getValue().getSender().getFrequency();
+ } else {
+
+ qrg.setValue("");// TODO: Prevents a bug of not setting all values as a default
+ }
+ return qrg;
+ }
+ });
+
+ TableColumn msgCol = new TableColumn("Message");
+ msgCol.setCellValueFactory(new Callback, ObservableValue