commit e2a4f08c0febc046ca00ac2b86553e1caa763868
Author: caixiang <939387484@qq.com>
Date: Fri Aug 27 17:43:55 2021 +0800
Initial commit
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..549e00a
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,33 @@
+HELP.md
+target/
+!.mvn/wrapper/maven-wrapper.jar
+!**/src/main/**/target/
+!**/src/test/**/target/
+
+### STS ###
+.apt_generated
+.classpath
+.factorypath
+.project
+.settings
+.springBeans
+.sts4-cache
+
+### IntelliJ IDEA ###
+.idea
+*.iws
+*.iml
+*.ipr
+
+### NetBeans ###
+/nbproject/private/
+/nbbuild/
+/dist/
+/nbdist/
+/.nb-gradle/
+build/
+!**/src/main/**/build/
+!**/src/test/**/build/
+
+### VS Code ###
+.vscode/
diff --git a/.mvn/wrapper/MavenWrapperDownloader.java b/.mvn/wrapper/MavenWrapperDownloader.java
new file mode 100644
index 0000000..a45eb6b
--- /dev/null
+++ b/.mvn/wrapper/MavenWrapperDownloader.java
@@ -0,0 +1,118 @@
+/*
+ * Copyright 2007-present the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://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.
+ */
+
+import java.net.*;
+import java.io.*;
+import java.nio.channels.*;
+import java.util.Properties;
+
+public class MavenWrapperDownloader {
+
+ private static final String WRAPPER_VERSION = "0.5.6";
+ /**
+ * Default URL to download the maven-wrapper.jar from, if no 'downloadUrl' is provided.
+ */
+ private static final String DEFAULT_DOWNLOAD_URL = "https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/"
+ + WRAPPER_VERSION + "/maven-wrapper-" + WRAPPER_VERSION + ".jar";
+
+ /**
+ * Path to the maven-wrapper.properties file, which might contain a downloadUrl property to
+ * use instead of the default one.
+ */
+ private static final String MAVEN_WRAPPER_PROPERTIES_PATH =
+ ".mvn/wrapper/maven-wrapper.properties";
+
+ /**
+ * Path where the maven-wrapper.jar will be saved to.
+ */
+ private static final String MAVEN_WRAPPER_JAR_PATH =
+ ".mvn/wrapper/maven-wrapper.jar";
+
+ /**
+ * Name of the property which should be used to override the default download url for the wrapper.
+ */
+ private static final String PROPERTY_NAME_WRAPPER_URL = "wrapperUrl";
+
+ public static void main(String args[]) {
+ System.out.println("- Downloader started");
+ File baseDirectory = new File(args[0]);
+ System.out.println("- Using base directory: " + baseDirectory.getAbsolutePath());
+
+ // If the maven-wrapper.properties exists, read it and check if it contains a custom
+ // wrapperUrl parameter.
+ File mavenWrapperPropertyFile = new File(baseDirectory, MAVEN_WRAPPER_PROPERTIES_PATH);
+ String url = DEFAULT_DOWNLOAD_URL;
+ if (mavenWrapperPropertyFile.exists()) {
+ FileInputStream mavenWrapperPropertyFileInputStream = null;
+ try {
+ mavenWrapperPropertyFileInputStream = new FileInputStream(mavenWrapperPropertyFile);
+ Properties mavenWrapperProperties = new Properties();
+ mavenWrapperProperties.load(mavenWrapperPropertyFileInputStream);
+ url = mavenWrapperProperties.getProperty(PROPERTY_NAME_WRAPPER_URL, url);
+ } catch (IOException e) {
+ System.out.println("- ERROR loading '" + MAVEN_WRAPPER_PROPERTIES_PATH + "'");
+ } finally {
+ try {
+ if (mavenWrapperPropertyFileInputStream != null) {
+ mavenWrapperPropertyFileInputStream.close();
+ }
+ } catch (IOException e) {
+ // Ignore ...
+ }
+ }
+ }
+ System.out.println("- Downloading from: " + url);
+
+ File outputFile = new File(baseDirectory.getAbsolutePath(), MAVEN_WRAPPER_JAR_PATH);
+ if (!outputFile.getParentFile().exists()) {
+ if (!outputFile.getParentFile().mkdirs()) {
+ System.out.println(
+ "- ERROR creating output directory '" + outputFile.getParentFile().getAbsolutePath() + "'");
+ }
+ }
+ System.out.println("- Downloading to: " + outputFile.getAbsolutePath());
+ try {
+ downloadFileFromURL(url, outputFile);
+ System.out.println("Done");
+ System.exit(0);
+ } catch (Throwable e) {
+ System.out.println("- Error downloading");
+ e.printStackTrace();
+ System.exit(1);
+ }
+ }
+
+ private static void downloadFileFromURL(String urlString, File destination) throws Exception {
+ if (System.getenv("MVNW_USERNAME") != null && System.getenv("MVNW_PASSWORD") != null) {
+ String username = System.getenv("MVNW_USERNAME");
+ char[] password = System.getenv("MVNW_PASSWORD").toCharArray();
+ Authenticator.setDefault(new Authenticator() {
+ @Override
+ protected PasswordAuthentication getPasswordAuthentication() {
+ return new PasswordAuthentication(username, password);
+ }
+ });
+ }
+ URL website = new URL(urlString);
+ ReadableByteChannel rbc;
+ rbc = Channels.newChannel(website.openStream());
+ FileOutputStream fos = new FileOutputStream(destination);
+ fos.getChannel().transferFrom(rbc, 0, Long.MAX_VALUE);
+ fos.close();
+ rbc.close();
+ }
+
+}
diff --git a/.mvn/wrapper/maven-wrapper.jar b/.mvn/wrapper/maven-wrapper.jar
new file mode 100644
index 0000000..2cc7d4a
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..ffdc10e
--- /dev/null
+++ b/.mvn/wrapper/maven-wrapper.properties
@@ -0,0 +1,2 @@
+distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.8.1/apache-maven-3.8.1-bin.zip
+wrapperUrl=https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar
diff --git a/mvnw b/mvnw
new file mode 100644
index 0000000..a16b543
--- /dev/null
+++ b/mvnw
@@ -0,0 +1,310 @@
+#!/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
+#
+# https://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.
+# ----------------------------------------------------------------------------
+
+# ----------------------------------------------------------------------------
+# Maven Start Up Batch script
+#
+# Required ENV vars:
+# ------------------
+# JAVA_HOME - location of a JDK home dir
+#
+# Optional ENV vars
+# -----------------
+# M2_HOME - location of maven2's installed home dir
+# 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 /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
+ export JAVA_HOME="`/usr/libexec/java_home`"
+ else
+ export JAVA_HOME="/Library/Java/Home"
+ fi
+ fi
+ ;;
+esac
+
+if [ -z "$JAVA_HOME" ] ; then
+ if [ -r /etc/gentoo-release ] ; then
+ JAVA_HOME=`java-config --jre-home`
+ fi
+fi
+
+if [ -z "$M2_HOME" ] ; then
+ ## resolve links - $0 may be a link to maven's home
+ PRG="$0"
+
+ # need this for relative symlinks
+ while [ -h "$PRG" ] ; do
+ ls=`ls -ld "$PRG"`
+ link=`expr "$ls" : '.*-> \(.*\)$'`
+ if expr "$link" : '/.*' > /dev/null; then
+ PRG="$link"
+ else
+ PRG="`dirname "$PRG"`/$link"
+ fi
+ done
+
+ saveddir=`pwd`
+
+ M2_HOME=`dirname "$PRG"`/..
+
+ # make it fully qualified
+ M2_HOME=`cd "$M2_HOME" && pwd`
+
+ cd "$saveddir"
+ # echo Using m2 at $M2_HOME
+fi
+
+# For Cygwin, ensure paths are in UNIX format before anything is touched
+if $cygwin ; then
+ [ -n "$M2_HOME" ] &&
+ M2_HOME=`cygpath --unix "$M2_HOME"`
+ [ -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 "$M2_HOME" ] &&
+ M2_HOME="`(cd "$M2_HOME"; pwd)`"
+ [ -n "$JAVA_HOME" ] &&
+ JAVA_HOME="`(cd "$JAVA_HOME"; 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="`which 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
+
+CLASSWORLDS_LAUNCHER=org.codehaus.plexus.classworlds.launcher.Launcher
+
+# 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/.."; pwd`
+ fi
+ # end of workaround
+ done
+ echo "${basedir}"
+}
+
+# concatenates all lines of a file
+concat_lines() {
+ if [ -f "$1" ]; then
+ echo "$(tr -s '\n' ' ' < "$1")"
+ fi
+}
+
+BASE_DIR=`find_maven_basedir "$(pwd)"`
+if [ -z "$BASE_DIR" ]; then
+ exit 1;
+fi
+
+##########################################################################################
+# 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.
+##########################################################################################
+if [ -r "$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" ]; then
+ if [ "$MVNW_VERBOSE" = true ]; then
+ echo "Found .mvn/wrapper/maven-wrapper.jar"
+ fi
+else
+ if [ "$MVNW_VERBOSE" = true ]; then
+ echo "Couldn't find .mvn/wrapper/maven-wrapper.jar, downloading it ..."
+ fi
+ if [ -n "$MVNW_REPOURL" ]; then
+ jarUrl="$MVNW_REPOURL/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar"
+ else
+ jarUrl="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar"
+ fi
+ while IFS="=" read key value; do
+ case "$key" in (wrapperUrl) jarUrl="$value"; break ;;
+ esac
+ done < "$BASE_DIR/.mvn/wrapper/maven-wrapper.properties"
+ if [ "$MVNW_VERBOSE" = true ]; then
+ echo "Downloading from: $jarUrl"
+ fi
+ wrapperJarPath="$BASE_DIR/.mvn/wrapper/maven-wrapper.jar"
+ if $cygwin; then
+ wrapperJarPath=`cygpath --path --windows "$wrapperJarPath"`
+ fi
+
+ if command -v wget > /dev/null; then
+ if [ "$MVNW_VERBOSE" = true ]; then
+ echo "Found wget ... using wget"
+ fi
+ if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then
+ wget "$jarUrl" -O "$wrapperJarPath"
+ else
+ wget --http-user=$MVNW_USERNAME --http-password=$MVNW_PASSWORD "$jarUrl" -O "$wrapperJarPath"
+ fi
+ elif command -v curl > /dev/null; then
+ if [ "$MVNW_VERBOSE" = true ]; then
+ echo "Found curl ... using curl"
+ fi
+ if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then
+ curl -o "$wrapperJarPath" "$jarUrl" -f
+ else
+ curl --user $MVNW_USERNAME:$MVNW_PASSWORD -o "$wrapperJarPath" "$jarUrl" -f
+ fi
+
+ else
+ if [ "$MVNW_VERBOSE" = true ]; then
+ echo "Falling back to using Java to download"
+ fi
+ javaClass="$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.java"
+ # For Cygwin, switch paths to Windows format before running javac
+ if $cygwin; then
+ javaClass=`cygpath --path --windows "$javaClass"`
+ fi
+ if [ -e "$javaClass" ]; then
+ if [ ! -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then
+ if [ "$MVNW_VERBOSE" = true ]; then
+ echo " - Compiling MavenWrapperDownloader.java ..."
+ fi
+ # Compiling the Java class
+ ("$JAVA_HOME/bin/javac" "$javaClass")
+ fi
+ if [ -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then
+ # Running the downloader
+ if [ "$MVNW_VERBOSE" = true ]; then
+ echo " - Running MavenWrapperDownloader.java ..."
+ fi
+ ("$JAVA_HOME/bin/java" -cp .mvn/wrapper MavenWrapperDownloader "$MAVEN_PROJECTBASEDIR")
+ fi
+ fi
+ fi
+fi
+##########################################################################################
+# End of extension
+##########################################################################################
+
+export MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"}
+if [ "$MVNW_VERBOSE" = true ]; then
+ echo $MAVEN_PROJECTBASEDIR
+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 "$M2_HOME" ] &&
+ M2_HOME=`cygpath --path --windows "$M2_HOME"`
+ [ -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
+
+exec "$JAVACMD" \
+ $MAVEN_OPTS \
+ -classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \
+ "-Dmaven.home=${M2_HOME}" "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \
+ ${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@"
diff --git a/mvnw.cmd b/mvnw.cmd
new file mode 100644
index 0000000..c8d4337
--- /dev/null
+++ b/mvnw.cmd
@@ -0,0 +1,182 @@
+@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 https://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 Maven Start Up Batch script
+@REM
+@REM Required ENV vars:
+@REM JAVA_HOME - location of a JDK home dir
+@REM
+@REM Optional ENV vars
+@REM M2_HOME - location of maven2's installed home dir
+@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 "%HOME%\mavenrc_pre.bat" call "%HOME%\mavenrc_pre.bat"
+if exist "%HOME%\mavenrc_pre.cmd" call "%HOME%\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 DOWNLOAD_URL="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar"
+
+FOR /F "tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO (
+ IF "%%A"=="wrapperUrl" SET DOWNLOAD_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 DOWNLOAD_URL="%MVNW_REPOURL%/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar"
+ )
+ if "%MVNW_VERBOSE%" == "true" (
+ echo Couldn't find %WRAPPER_JAR%, downloading it ...
+ echo Downloading from: %DOWNLOAD_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('%DOWNLOAD_URL%', '%WRAPPER_JAR%')"^
+ "}"
+ if "%MVNW_VERBOSE%" == "true" (
+ echo Finished downloading %WRAPPER_JAR%
+ )
+)
+@REM End of extension
+
+@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 "%HOME%\mavenrc_post.bat" call "%HOME%\mavenrc_post.bat"
+if exist "%HOME%\mavenrc_post.cmd" call "%HOME%\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%
+
+exit /B %ERROR_CODE%
diff --git a/pom.xml b/pom.xml
new file mode 100644
index 0000000..87d0823
--- /dev/null
+++ b/pom.xml
@@ -0,0 +1,173 @@
+
+
+ 4.0.0
+
+ org.springframework.boot
+ spring-boot-starter-parent
+ 2.5.4
+
+
+ com.qgs
+ dc
+ 0.0.1-SNAPSHOT
+ dc
+ data collection
+
+ 1.8
+
+
+
+
+ org.eclipse.milo
+ sdk-client
+ 0.6.3
+
+
+
+ org.eclipse.milo
+ stack-client
+ 0.6.3
+
+
+
+ org.eclipse.milo
+ stack-server
+ 0.6.3
+
+
+ org.eclipse.milo
+ sdk-server
+ 0.6.3
+
+
+
+
+
+
+ org.slf4j
+ slf4j-api
+ 1.7.32
+
+
+
+ ch.qos.logback
+ logback-core
+ 1.2.5
+
+
+
+ ch.qos.logback
+ logback-classic
+ 1.2.5
+
+
+
+
+
+
+
+
+
+ org.springframework.boot
+ spring-boot-starter-websocket
+
+
+
+
+
+ com.alibaba
+ fastjson
+ 1.2.78
+
+
+
+
+ cn.hutool
+ hutool-all
+ 5.7.9
+
+
+
+
+
+ org.springframework.boot
+ spring-boot-starter-amqp
+
+
+
+
+
+
+ com.alibaba
+ fastjson
+ 1.2.75
+
+
+
+ org.projectlombok
+ lombok
+
+
+
+
+
+ com.fasterxml.jackson.core
+ jackson-databind
+ 2.12.2
+
+
+
+ com.fasterxml.jackson.core
+ jackson-core
+ 2.12.2
+
+
+
+ com.fasterxml.jackson.core
+ jackson-annotations
+ 2.12.2
+
+
+
+
+
+ org.springframework.boot
+ spring-boot-starter
+
+
+ org.springframework.boot
+ spring-boot-starter-web
+
+
+ org.projectlombok
+ lombok
+ true
+
+
+ org.springframework.boot
+ spring-boot-starter-test
+ test
+
+
+
+
+
+
+ org.springframework.boot
+ spring-boot-maven-plugin
+ 2.5.4
+
+
+
+
+ org.projectlombok
+ lombok
+
+
+
+
+
+
+
+
diff --git a/src/main/java/com/qgs/dc/DcApplication.java b/src/main/java/com/qgs/dc/DcApplication.java
new file mode 100644
index 0000000..b626cac
--- /dev/null
+++ b/src/main/java/com/qgs/dc/DcApplication.java
@@ -0,0 +1,13 @@
+package com.qgs.dc;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+
+@SpringBootApplication
+public class DcApplication {
+
+ public static void main(String[] args) {
+ SpringApplication.run(DcApplication.class, args);
+ }
+
+}
diff --git a/src/main/java/com/qgs/dc/common/utils/CommonFunction.java b/src/main/java/com/qgs/dc/common/utils/CommonFunction.java
new file mode 100644
index 0000000..3d0b998
--- /dev/null
+++ b/src/main/java/com/qgs/dc/common/utils/CommonFunction.java
@@ -0,0 +1,208 @@
+package com.qgs.dc.common.utils;
+
+import com.alibaba.fastjson.JSON;
+import com.alibaba.fastjson.JSONObject;
+import com.qgs.dc.mq.entity.MQMessage;
+import com.qgs.dc.opcua.constant.PLCTypeConstant;
+import org.eclipse.milo.opcua.stack.core.types.builtin.ByteString;
+import org.eclipse.milo.opcua.stack.core.types.builtin.NodeId;
+import org.eclipse.milo.opcua.stack.core.types.builtin.unsigned.*;
+import org.springframework.messaging.Message;
+
+import java.io.File;
+import java.time.LocalDateTime;
+import java.time.format.DateTimeFormatter;
+import java.util.Arrays;
+import java.util.UUID;
+
+/**
+ * 1.在从OPC Server 中 读到变量,要把变量包装一下 传给前端
+ * 2.如果有些 opc-server 变量类型并不是 ns-iden 而仅仅是 iden 那么需要调用initialNodeId 初始化节点
+ *
+ * */
+public class CommonFunction {
+ public static boolean isLinux() {
+ return System.getProperty("os.name").toLowerCase().contains("linux");
+ }
+
+ public static boolean isWindows() {
+ return System.getProperty("os.name").toLowerCase().contains("windows");
+ }
+
+ public static String extractError(String as){
+ String[] errMsgs = as.split(",");
+ /*for(String i:errMsgs){
+ System.err.println(i);
+ }*/
+
+ String[] s = errMsgs[errMsgs.length-2].split(":");
+ String errStatus = s[s.length-1];
+ return errStatus.split("=")[1];
+ }
+
+ //注意:下面类型 必须和 PLCType枚举类里面的 数据类型按顺序一一对应
+ //含义 是什么数据类型 就返回什么数据类型,如果是数组的话也会返回数组里面的数据类型如 "QArray|Boolen"
+ public static String judgeVarType(Object value){
+ if(value instanceof UByte){
+ return PLCTypeConstant.QUByte;
+ }else if(value instanceof UInteger){
+ return PLCTypeConstant.QUInteger;
+ }else if(value instanceof UShort){
+ return PLCTypeConstant.QUShort;
+ }else if(value instanceof ULong){
+ return PLCTypeConstant.QULong;
+ }else if(value instanceof Boolean){
+ return PLCTypeConstant.QBoolean;
+ }else if(value instanceof String){
+ return PLCTypeConstant.QString;
+ }else if(value instanceof Double){
+ return PLCTypeConstant.QDouble;
+ }else if(value instanceof Float){
+ return PLCTypeConstant.QFloat;
+ }else if(value instanceof Long){
+ return PLCTypeConstant.QLong;
+ }else if(value instanceof Integer){
+ return PLCTypeConstant.QInteger;
+ }else if(value instanceof Short){
+ return PLCTypeConstant.QShort;
+ }else if(value.getClass().isArray()){
+ Object[] newArray = (Object[]) value;
+ if(newArray.length==0){
+ return PLCTypeConstant.QArray+"|0";
+ }
+ Object o = newArray[0];
+ Object o1 = judgeVarType(o);
+ return PLCTypeConstant.QArray+"|"+o1;
+ }else if(value instanceof Byte){
+ return PLCTypeConstant.QByte;
+ }else if(value instanceof ByteString){
+ return PLCTypeConstant.QByteString;
+ }else {
+ //如果是20 那么是数组类型了,数组的话只要string返回回去就行了
+ //还有 如 二维数组、日期变量 那么如果需要可以补充 ,,暂时没有
+ return null;
+ }
+ }
+
+ public static void createDirIfNotExit(String url){
+ File file =new File(url);
+ //如果文件夹不存在则创建
+ if (!file.exists() && !file.isDirectory())
+ {
+ System.out.println("//不存在");
+ boolean mkdir = file.mkdir();
+ }
+ }
+
+ /**
+ * 用处:在从OPC Server 中 读到变量,要把变量包装一下 传给前端
+ * 含义: 包装变量
+ * 如果传入的是数组,,那么把这个Object 包装成List,如果不是数组,就直接原路返回
+ * //注意 传入的Objec,必须dataValue.getValue().getValue()
+ * */
+ public static Object var(Object object){
+ if(object.getClass().isArray()){
+ return Arrays.asList(object);
+ }
+ return object.toString();
+ }
+
+
+ public static Object var2String(Object object){
+ if(object.getClass().isArray()){
+ Object[] o = (Object[])object;
+ String res = "";
+ if(o.length>0){
+ for(int i=0;i message){
+ byte[] bytes =(byte[]) message.getPayload();
+ MQMessage mqMessage = JSONObject.parseObject(bytes, MQMessage.class);
+ return mqMessage;
+ }
+
+
+
+ /**
+ * 解析body
+ * JSONObject对象 ==》java对象
+ * @param body body
+ * @param clazz ResponseBody、RequestBody 里面的body
+ * @return
+ */
+ public static Object parseBody(Object body,Class> clazz){
+ JSONObject jsonObject = (JSONObject) body;
+ Object o = JSON.toJavaObject(jsonObject, clazz);
+ return o;
+ }
+
+ /*
+ type:
+ 1.返回的是这种格式:2021-08-16 15:00:05
+ 2.返回的是这种格式:20210816150021
+ */
+ public static String getNowDate(Integer type){
+ if(type==1){
+ return LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
+ }else {
+ return LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMddHHmmss"));
+ }
+ }
+
+ /**
+ *32位默认长度的uuid
+ * (获取32位uuid)
+ *
+ * @return
+ */
+ public static String getUUID()
+ {
+ return UUID.randomUUID().toString().replace("-", "");
+ }
+
+ /**
+ * 获取指定长度的随机数
+ * (获取指定长度uuid)
+ *
+ * @return
+ */
+ public static String getUUID(int len)
+ {
+ if(0 >= len)
+ {
+ return null;
+ }
+
+ String uuid = getUUID();
+ StringBuffer str = new StringBuffer();
+
+ for (int i = 0; i < len; i++)
+ {
+ str.append(uuid.charAt(i));
+ }
+
+ return str.toString();
+ }
+
+}
diff --git a/src/main/java/com/qgs/dc/common/utils/SpringContextUtils.java b/src/main/java/com/qgs/dc/common/utils/SpringContextUtils.java
new file mode 100644
index 0000000..1385eb9
--- /dev/null
+++ b/src/main/java/com/qgs/dc/common/utils/SpringContextUtils.java
@@ -0,0 +1,51 @@
+/**
+ * Copyright (c) 2016-2019 人人开源 All rights reserved.
+ *
+ * https://www.renren.io
+ *
+ * 版权所有,侵权必究!
+ */
+
+package com.qgs.dc.common.utils;
+
+import org.springframework.beans.BeansException;
+import org.springframework.context.ApplicationContext;
+import org.springframework.context.ApplicationContextAware;
+import org.springframework.stereotype.Component;
+
+/**
+ * Spring Context 工具类
+ *
+ * @author Mark sunlightcs@gmail.com
+ */
+@Component
+public class SpringContextUtils implements ApplicationContextAware {
+ public static ApplicationContext applicationContext;
+
+ @Override
+ public void setApplicationContext(ApplicationContext applicationContext)
+ throws BeansException {
+ applicationContext = applicationContext;
+ }
+
+ public static Object getBean(String name) {
+ return applicationContext.getBean(name);
+ }
+
+ public static T getBean(String name, Class requiredType) {
+ return applicationContext.getBean(name, requiredType);
+ }
+
+ public static boolean containsBean(String name) {
+ return applicationContext.containsBean(name);
+ }
+
+ public static boolean isSingleton(String name) {
+ return applicationContext.isSingleton(name);
+ }
+
+ public static Class extends Object> getType(String name) {
+ return applicationContext.getType(name);
+ }
+
+}
\ No newline at end of file
diff --git a/src/main/java/com/qgs/dc/common/websocket/WebSocketConfig.java b/src/main/java/com/qgs/dc/common/websocket/WebSocketConfig.java
new file mode 100644
index 0000000..40d112a
--- /dev/null
+++ b/src/main/java/com/qgs/dc/common/websocket/WebSocketConfig.java
@@ -0,0 +1,14 @@
+package com.qgs.dc.common.websocket;
+
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.web.socket.server.standard.ServerEndpointExporter;
+
+@Configuration
+public class WebSocketConfig {
+ @Bean
+ public ServerEndpointExporter serverEndpointExporter() {
+ return new ServerEndpointExporter();
+ }
+
+}
diff --git a/src/main/java/com/qgs/dc/common/websocket/WebSocketServer.java b/src/main/java/com/qgs/dc/common/websocket/WebSocketServer.java
new file mode 100644
index 0000000..428c282
--- /dev/null
+++ b/src/main/java/com/qgs/dc/common/websocket/WebSocketServer.java
@@ -0,0 +1,163 @@
+package com.qgs.dc.common.websocket;
+
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+import org.springframework.stereotype.Component;
+
+import javax.websocket.*;
+import javax.websocket.server.PathParam;
+import javax.websocket.server.ServerEndpoint;
+import java.io.IOException;
+import java.util.UUID;
+import java.util.concurrent.ConcurrentHashMap;
+
+/**
+ * 注意:
+ * 1.如果多个客户端来 连接 websocket,,那么 不要相同id。。如果是相同id只能发送一个。所以前端连接的id 号要由后台给出 getUniqeId() 这个方法
+ * */
+@Component
+//访问服务端的url地址
+@ServerEndpoint(value = "/opcua/websocket/{id}")
+public class WebSocketServer {
+ private static int onlineCount = 0;
+ private static ConcurrentHashMap webSocketSet = new ConcurrentHashMap<>();
+
+ //前端的id 右后端给他
+ public synchronized String getUniqeId(){
+ return UUID.randomUUID().toString()+System.currentTimeMillis();
+ }
+
+ //与某个客户端的连接会话,需要通过它来给客户端发送数据
+ private Session session;
+ private static Logger log = LogManager.getLogger(WebSocketServer.class);
+ private String id = "";
+
+ public Integer getCurrentNum(){
+ return webSocketSet.size();
+ }
+
+ /**
+ * 连接建立成功调用的方法*/
+ @OnOpen
+ public void onOpen(@PathParam(value = "id") String id, Session session) {
+ this.session = session;
+ this.id = id;//接收到发送消息的人员编号
+ webSocketSet.put(id, this); //加入set中
+ addOnlineCount(); //在线数加1
+ log.info("用户"+id+"加入!当前在线人数为" + getOnlineCount());
+ try {
+ sendMessage("连接成功");
+ } catch (IOException e) {
+ log.error("websocket IO异常");
+ }
+ }
+
+ /**
+ * 连接关闭调用的方法
+ */
+ /*@OnClose
+ public void onClose() {
+
+ webSocketSet.remove(this); //从set中删除
+ subOnlineCount(); //在线数减1
+ log.info("有一连接关闭!当前在线人数为" + getOnlineCount());
+ }*/
+
+ // 关闭连接触发事件
+ @OnClose
+ public void onClose(Session session, CloseReason closeReason) {
+ String[] uris = session.getRequestURI().toString().split("/");
+ webSocketSet.remove(uris[uris.length-1]);
+ subOnlineCount();
+ log.info("有一连接关闭!当前在线人数为" + getOnlineCount());
+ }
+
+ /**
+ * 收到客户端消息后调用的方法
+ *
+ * @param message 客户端发送过来的消息
+ * */
+ @OnMessage
+ public void onMessage(String message, Session session) {
+ log.info("来自客户端的消息:" + message);
+ //可以自己约定字符串内容,比如 内容|0 表示信息群发,内容|X 表示信息发给id为X的用户
+ String sendMessage = message.split("[|]")[0];
+ String sendUserId = message.split("[|]")[1];
+ try {
+ if(sendUserId.equals("0")){
+ sendtoAll(sendMessage);
+ }
+ else{
+ sendtoUser(sendMessage,sendUserId);
+ }
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+
+ }
+
+ /**
+ *
+ * @param session
+ * @param error
+ */
+ @OnError
+ public void onError(Session session, Throwable error) {
+ log.error("发生错误");
+ error.printStackTrace();
+ }
+
+
+ public void sendMessage(String message) throws IOException {
+ this.session.getBasicRemote().sendText(message);
+ }
+
+ /**
+ * 发送信息给指定ID用户,如果用户不在线则返回不在线信息给自己
+ * @param message
+ * @param sendUserId
+ * @throws IOException
+ */
+ public void sendtoUser(String message,String sendUserId) throws IOException {
+ if (webSocketSet.get(sendUserId) != null) {
+ if(!id.equals(sendUserId)){
+ webSocketSet.get(sendUserId).sendMessage( "用户" + id + "发来消息:" + "
" + message);
+ }
+ else{
+ webSocketSet.get(sendUserId).sendMessage(message);
+ }
+ } else {
+ //如果用户不在线则返回不在线信息给自己
+ sendtoUser("当前用户不在线",id);
+ }
+ }
+
+ /**
+ * 发送信息给所有人
+ * @param message
+ * @throws IOException
+ */
+ public void sendtoAll(String message) throws IOException {
+ for (String key : webSocketSet.keySet()) {
+ try {
+ webSocketSet.get(key).sendMessage(message);
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+ }
+
+
+ public static synchronized int getOnlineCount() {
+ return onlineCount;
+ }
+
+ public static synchronized void addOnlineCount() {
+ WebSocketServer.onlineCount++;
+ }
+
+ public static synchronized void subOnlineCount() {
+ WebSocketServer.onlineCount--;
+ }
+}
+
diff --git a/src/main/java/com/qgs/dc/mq/Constant/SecsGemTimeout.java b/src/main/java/com/qgs/dc/mq/Constant/SecsGemTimeout.java
new file mode 100644
index 0000000..0cb22df
--- /dev/null
+++ b/src/main/java/com/qgs/dc/mq/Constant/SecsGemTimeout.java
@@ -0,0 +1,10 @@
+package com.qgs.dc.mq.Constant;
+
+/**
+ * @Desc: ""
+ * @Author: caixiang
+ * @DATE: 2021/8/27 15:43
+ */
+public class SecsGemTimeout {
+ public static final Long T3_TIMEOUT = new Long(30000);
+}
diff --git a/src/main/java/com/qgs/dc/mq/configuration/ConfigOf00B.java b/src/main/java/com/qgs/dc/mq/configuration/ConfigOf00B.java
new file mode 100644
index 0000000..4075224
--- /dev/null
+++ b/src/main/java/com/qgs/dc/mq/configuration/ConfigOf00B.java
@@ -0,0 +1,96 @@
+package com.qgs.dc.mq.configuration;
+
+
+import org.springframework.amqp.core.Binding;
+import org.springframework.amqp.core.BindingBuilder;
+import org.springframework.amqp.core.DirectExchange;
+import org.springframework.amqp.core.Queue;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+/**
+ * @Desc: "设备:00B 相关信息定义"
+ * @Author: caixiang
+ * @DATE: 2021/6/7 9:11
+ */
+@Configuration
+public class ConfigOf00B {
+
+ //水平扩展其他设备的时候 只要:control+R 然后 00B=>00C 然后replace all
+ public static final String EQUIPMENT_NAME_00B = "00B";
+
+ public static final String EXCHANGE_NAME_00B = EQUIPMENT_NAME_00B +"_Exchange";
+ public static final String EAP_REQUEST_QUEUE_00B = EQUIPMENT_NAME_00B +"_EAP_Request_Queue";
+ public static final String EAP_RESPONSE_QUEUE_00B = EQUIPMENT_NAME_00B +"_EAP_Response_Queue";
+ public static final String MES_REQUEST_QUEUE_00B = EQUIPMENT_NAME_00B +"_MES_Request_Queue";
+ public static final String MES_RESPONSE_QUEUE_00B = EQUIPMENT_NAME_00B +"_MES_Response_Queue";
+ public static final String EAP_REQUEST_QUEUE_ROUTINGKEY_00B = EQUIPMENT_NAME_00B +"_EAP_Request_Queue_RoutingKey";
+ public static final String EAP_RESPONSE_QUEUE_ROUTINGKEY_00B = EQUIPMENT_NAME_00B +"_EAP_Response_Queue_RoutingKey";
+ public static final String MES_REQUEST_QUEUE_ROUTINGKEY_00B = EQUIPMENT_NAME_00B +"_MES_Request_Queue_RoutingKey";
+ public static final String MES_RESPONSE_QUEUE_ROUTINGKEY_00B = EQUIPMENT_NAME_00B +"_MES_Response_Queue_RoutingKey";
+
+
+ @Bean
+ public DirectExchange EXCHANGE_NAME_00B(){
+ return new DirectExchange(EXCHANGE_NAME_00B);
+ }
+
+ //todo
+ @Bean
+ public Queue MES_REQUEST_QUEUE_00B(){
+ Queue queue = new Queue(MES_REQUEST_QUEUE_00B);
+ queue.addArgument("x-dead-letter-exchange",ConfigOfDeadLetterQueue.EXCHANGE_NAME_DLE);
+ queue.addArgument("x-dead-letter-routing-key",ConfigOfDeadLetterQueue.EAP_REQUEST_QUEUE_ROUTINGKEY_DLE);
+ queue.addArgument("x-max-priority",ConfigOfDeadLetterQueue.MAX_PRIORITY);
+ return queue;
+ }
+ @Bean
+ public Queue MES_RESPONSE_QUEUE_00B(){
+ Queue queue = new Queue(MES_RESPONSE_QUEUE_00B);
+ queue.addArgument("x-dead-letter-exchange",ConfigOfDeadLetterQueue.EXCHANGE_NAME_DLE);
+ queue.addArgument("x-dead-letter-routing-key",ConfigOfDeadLetterQueue.EAP_REQUEST_QUEUE_ROUTINGKEY_DLE);
+ queue.addArgument("x-max-priority",ConfigOfDeadLetterQueue.MAX_PRIORITY);
+ return queue;
+ }
+ @Bean
+ public Queue EAP_REQUEST_QUEUE_00B(){
+ Queue queue = new Queue(EAP_REQUEST_QUEUE_00B);
+ queue.addArgument("x-dead-letter-exchange",ConfigOfDeadLetterQueue.EXCHANGE_NAME_DLE);
+ queue.addArgument("x-dead-letter-routing-key",ConfigOfDeadLetterQueue.EAP_REQUEST_QUEUE_ROUTINGKEY_DLE);
+ queue.addArgument("x-max-priority",ConfigOfDeadLetterQueue.MAX_PRIORITY);
+ return queue;
+
+ }
+ @Bean
+ public Queue EAP_RESPONSE_QUEUE_00B(){
+ Queue queue = new Queue(EAP_RESPONSE_QUEUE_00B);
+ queue.addArgument("x-dead-letter-exchange",ConfigOfDeadLetterQueue.EXCHANGE_NAME_DLE);
+ queue.addArgument("x-dead-letter-routing-key",ConfigOfDeadLetterQueue.EAP_REQUEST_QUEUE_ROUTINGKEY_DLE);
+ queue.addArgument("x-max-priority",ConfigOfDeadLetterQueue.MAX_PRIORITY);
+ return queue;
+ }
+
+
+ @Bean
+ public Binding bindExchangeAndQueueA_00B(){
+ return BindingBuilder.bind(EAP_REQUEST_QUEUE_00B()).to(EXCHANGE_NAME_00B())
+ .with(EAP_REQUEST_QUEUE_ROUTINGKEY_00B);
+ }
+ @Bean
+ public Binding bindExchangeAndQueueB_00B(){
+ return BindingBuilder.bind(EAP_RESPONSE_QUEUE_00B()).to(EXCHANGE_NAME_00B())
+ .with(EAP_RESPONSE_QUEUE_ROUTINGKEY_00B);
+ }
+
+ @Bean
+ public Binding bindExchangeAndQueueC_00B(){
+ return BindingBuilder.bind(MES_REQUEST_QUEUE_00B()).to(EXCHANGE_NAME_00B())
+ .with(MES_REQUEST_QUEUE_ROUTINGKEY_00B);
+ }
+
+ @Bean
+ public Binding bindExchangeAndQueueD_00B(){
+ return BindingBuilder.bind(MES_RESPONSE_QUEUE_00B()).to(EXCHANGE_NAME_00B())
+ .with(MES_RESPONSE_QUEUE_ROUTINGKEY_00B);
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/com/qgs/dc/mq/configuration/ConfigOf00C.java b/src/main/java/com/qgs/dc/mq/configuration/ConfigOf00C.java
new file mode 100644
index 0000000..e0f1b65
--- /dev/null
+++ b/src/main/java/com/qgs/dc/mq/configuration/ConfigOf00C.java
@@ -0,0 +1,98 @@
+package com.qgs.dc.mq.configuration;
+
+
+import org.springframework.amqp.core.Binding;
+import org.springframework.amqp.core.BindingBuilder;
+import org.springframework.amqp.core.DirectExchange;
+import org.springframework.amqp.core.Queue;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+/**
+ * @Desc: "设备:00C 相关信息定义"
+ * @Author: caixiang
+ * @DATE: 2021/6/7 9:11
+ */
+@Configuration
+public class ConfigOf00C {
+
+ //水平扩展其他设备的时候 只要改 EQUIPMENT_NAME 就行了
+ //1.现在MQConstant 上新增 EQUIPMENT_***; 2.然后复制 本class,然后EQUIPMENT_NAME = MQConstant.EQUIPMENT_***;就行了。
+ public static final String EQUIPMENT_NAME_00C = "00C";
+
+ public static final String EXCHANGE_NAME_00C = EQUIPMENT_NAME_00C +"_Exchange";
+ public static final String EAP_REQUEST_QUEUE_00C = EQUIPMENT_NAME_00C +"_EAP_Request_Queue";
+ public static final String EAP_RESPONSE_QUEUE_00C = EQUIPMENT_NAME_00C +"_EAP_Response_Queue";
+ public static final String MES_REQUEST_QUEUE_00C = EQUIPMENT_NAME_00C +"_MES_Request_Queue";
+ public static final String MES_RESPONSE_QUEUE_00C = EQUIPMENT_NAME_00C +"_MES_Response_Queue";
+ public static final String EAP_REQUEST_QUEUE_ROUTINGKEY_00C = EQUIPMENT_NAME_00C +"_EAP_Request_Queue_RoutingKey";
+ public static final String EAP_RESPONSE_QUEUE_ROUTINGKEY_00C = EQUIPMENT_NAME_00C +"_EAP_Response_Queue_RoutingKey";
+ public static final String MES_REQUEST_QUEUE_ROUTINGKEY_00C = EQUIPMENT_NAME_00C +"_MES_Request_Queue_RoutingKey";
+ public static final String MES_RESPONSE_QUEUE_ROUTINGKEY_00C = EQUIPMENT_NAME_00C +"_MES_Response_Queue_RoutingKey";
+
+
+ @Bean
+ public DirectExchange EXCHANGE_NAME_00C(){
+ return new DirectExchange(EXCHANGE_NAME_00C);
+ }
+
+ //todo
+ @Bean
+ public Queue MES_REQUEST_QUEUE_00C(){
+ Queue queue = new Queue(MES_REQUEST_QUEUE_00C);
+ queue.addArgument("x-dead-letter-exchange",ConfigOfDeadLetterQueue.EXCHANGE_NAME_DLE);
+ queue.addArgument("x-dead-letter-routing-key",ConfigOfDeadLetterQueue.EAP_REQUEST_QUEUE_ROUTINGKEY_DLE);
+ queue.addArgument("x-max-priority",ConfigOfDeadLetterQueue.MAX_PRIORITY);
+ return queue;
+ }
+
+ @Bean
+ public Queue MES_RESPONSE_QUEUE_00C(){
+ Queue queue = new Queue(MES_RESPONSE_QUEUE_00C);
+ queue.addArgument("x-dead-letter-exchange",ConfigOfDeadLetterQueue.EXCHANGE_NAME_DLE);
+ queue.addArgument("x-dead-letter-routing-key",ConfigOfDeadLetterQueue.EAP_REQUEST_QUEUE_ROUTINGKEY_DLE);
+ queue.addArgument("x-max-priority",ConfigOfDeadLetterQueue.MAX_PRIORITY);
+ return queue;
+ }
+ @Bean
+ public Queue EAP_REQUEST_QUEUE_00C(){
+ Queue queue = new Queue(EAP_REQUEST_QUEUE_00C);
+ queue.addArgument("x-dead-letter-exchange",ConfigOfDeadLetterQueue.EXCHANGE_NAME_DLE);
+ queue.addArgument("x-dead-letter-routing-key",ConfigOfDeadLetterQueue.EAP_REQUEST_QUEUE_ROUTINGKEY_DLE);
+ queue.addArgument("x-max-priority",ConfigOfDeadLetterQueue.MAX_PRIORITY);
+ return queue;
+
+ }
+ @Bean
+ public Queue EAP_RESPONSE_QUEUE_00C(){
+ Queue queue = new Queue(EAP_RESPONSE_QUEUE_00C);
+ queue.addArgument("x-dead-letter-exchange",ConfigOfDeadLetterQueue.EXCHANGE_NAME_DLE);
+ queue.addArgument("x-dead-letter-routing-key",ConfigOfDeadLetterQueue.EAP_REQUEST_QUEUE_ROUTINGKEY_DLE);
+ queue.addArgument("x-max-priority",ConfigOfDeadLetterQueue.MAX_PRIORITY);
+ return queue;
+ }
+
+
+ @Bean
+ public Binding bindExchangeAndQueueA_00C(){
+ return BindingBuilder.bind(EAP_REQUEST_QUEUE_00C()).to(EXCHANGE_NAME_00C())
+ .with(EAP_REQUEST_QUEUE_ROUTINGKEY_00C);
+ }
+ @Bean
+ public Binding bindExchangeAndQueueB_00C(){
+ return BindingBuilder.bind(EAP_RESPONSE_QUEUE_00C()).to(EXCHANGE_NAME_00C())
+ .with(EAP_RESPONSE_QUEUE_ROUTINGKEY_00C);
+ }
+
+ @Bean
+ public Binding bindExchangeAndQueueC_00C(){
+ return BindingBuilder.bind(MES_REQUEST_QUEUE_00C()).to(EXCHANGE_NAME_00C())
+ .with(MES_REQUEST_QUEUE_ROUTINGKEY_00C);
+ }
+
+ @Bean
+ public Binding bindExchangeAndQueueD_00C(){
+ return BindingBuilder.bind(MES_RESPONSE_QUEUE_00C()).to(EXCHANGE_NAME_00C())
+ .with(MES_RESPONSE_QUEUE_ROUTINGKEY_00C);
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/com/qgs/dc/mq/configuration/ConfigOfDeadLetterQueue.java b/src/main/java/com/qgs/dc/mq/configuration/ConfigOfDeadLetterQueue.java
new file mode 100644
index 0000000..794853c
--- /dev/null
+++ b/src/main/java/com/qgs/dc/mq/configuration/ConfigOfDeadLetterQueue.java
@@ -0,0 +1,52 @@
+package com.qgs.dc.mq.configuration;
+
+import org.springframework.amqp.core.Binding;
+import org.springframework.amqp.core.BindingBuilder;
+import org.springframework.amqp.core.DirectExchange;
+import org.springframework.amqp.core.Queue;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+/**
+ * @Desc: ""
+ * @Author: caixiang
+ * @DATE: 2021/8/14 10:45
+ */
+@Configuration
+public class ConfigOfDeadLetterQueue {
+ /**
+ * 队列默认参数:
+ * durable: true
+ * exclusive: false
+ * autoDelete: false
+ */
+
+ /**
+ * 死信队列 定义
+ *
+ * 所以 MES-Received 处理异常的 Message 都会被 投递到 Dead_Letter_Exchange => Dead_Letter_Queue(这个队列)
+ */
+ public static final String EXCHANGE_NAME_DLE = "Dead_Letter_Exchange";
+ public static final String EAP_REQUEST_QUEUE_DLE = "Dead_Letter_Queue";
+ public static final String EAP_REQUEST_QUEUE_ROUTINGKEY_DLE = "DL_For_MESReceived_Rk";
+ public static final Integer MAX_PRIORITY = 10;
+
+ @Bean
+ public Queue EAP_REQUEST_QUEUE_DLE(){
+ Queue queue = new Queue(EAP_REQUEST_QUEUE_DLE);
+ queue.setIgnoreDeclarationExceptions(true);
+
+ return queue;
+ }
+
+ @Bean
+ public DirectExchange EXCHANGE_NAME_DLE(){
+ return new DirectExchange(EXCHANGE_NAME_DLE);
+ }
+
+ @Bean
+ public Binding EAP_REQUEST_QUEUE_ROUTINGKEY_DLE(){
+ return BindingBuilder.bind(EAP_REQUEST_QUEUE_DLE()).to(EXCHANGE_NAME_DLE())
+ .with(EAP_REQUEST_QUEUE_ROUTINGKEY_DLE);
+ }
+}
diff --git a/src/main/java/com/qgs/dc/mq/consumer/DeadLetterQueueReceived.java b/src/main/java/com/qgs/dc/mq/consumer/DeadLetterQueueReceived.java
new file mode 100644
index 0000000..a08b5d1
--- /dev/null
+++ b/src/main/java/com/qgs/dc/mq/consumer/DeadLetterQueueReceived.java
@@ -0,0 +1,42 @@
+package com.qgs.dc.mq.consumer;
+
+
+import com.qgs.dc.mq.configuration.ConfigOfDeadLetterQueue;
+import com.rabbitmq.client.Channel;
+import org.springframework.amqp.rabbit.annotation.RabbitHandler;
+import org.springframework.amqp.rabbit.annotation.RabbitListener;
+import org.springframework.amqp.support.AmqpHeaders;
+import org.springframework.messaging.Message;
+import org.springframework.stereotype.Component;
+
+/**
+ * @Desc: ""
+ * @Author: caixiang
+ * @DATE: 2021/6/22 15:36
+ */
+@Component
+public class DeadLetterQueueReceived {
+// @RabbitListener(
+// bindings = @QueueBinding(
+// value = @Queue(value="Dead_Letter_Queue_PID00B",durable = "true"),
+// exchange = @Exchange(name = "Dead_Letter_Exchange",durable = "true",type = ExchangeTypes.DIRECT,ignoreDeclarationExceptions = "true"),
+// key = "Dead_Letter_Queue_PID00B_RoutingKey"
+// )
+// )
+
+
+ @RabbitListener(queues = ConfigOfDeadLetterQueue.EAP_REQUEST_QUEUE_DLE)
+ @RabbitHandler
+ public void dlForPID00B(Message> message, Channel channel)throws Exception{
+
+ System.out.println("线程名"+Thread.currentThread().hashCode()+"==============Dead_Letter_Exchange=================,"+",attr2"+message.getHeaders().get("attr2"));
+
+ Thread.sleep(500);
+ Long deliveryTag = (Long)message.getHeaders().get(AmqpHeaders.DELIVERY_TAG);
+
+ //throw new Exception("12331");
+
+ //channel.basicNack(deliveryTag,false,true);
+ channel.basicAck(deliveryTag,false);
+ }
+}
diff --git a/src/main/java/com/qgs/dc/mq/consumer/PID00BReceived.java b/src/main/java/com/qgs/dc/mq/consumer/PID00BReceived.java
new file mode 100644
index 0000000..2a8b17c
--- /dev/null
+++ b/src/main/java/com/qgs/dc/mq/consumer/PID00BReceived.java
@@ -0,0 +1,144 @@
+package com.qgs.dc.mq.consumer;
+
+import cn.hutool.http.HttpRequest;
+import cn.hutool.http.HttpUtil;
+import com.alibaba.fastjson.JSONObject;
+import com.qgs.dc.common.utils.CommonFunction;
+import com.qgs.dc.mq.configuration.ConfigOf00B;
+import com.qgs.dc.mq.consumer.commonHandler.MQMessageHandler;
+import com.qgs.dc.mq.entity.MQMessage;
+import com.qgs.dc.mq.entity.common.Header;
+import com.qgs.dc.mq.entity.specificBody.QueryEQStatusBody;
+import com.qgs.dc.mq.secsgem.SendedList;
+import com.rabbitmq.client.Channel;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.amqp.rabbit.annotation.RabbitHandler;
+import org.springframework.amqp.rabbit.annotation.RabbitListener;
+import org.springframework.amqp.support.AmqpHeaders;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.messaging.Message;
+import org.springframework.stereotype.Component;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+
+/**
+ * @Desc: "PID00B设备 接收MQ消息 监听类"
+ * @Author: caixiang
+ * @DATE: 2021/6/22 15:30
+ *
+ * Ctrl+R 替换设备名
+ */
+@Component
+public class PID00BReceived {
+
+ private static final Logger logger = LoggerFactory.getLogger(PID00BReceived.class);
+
+ @Autowired
+ MQMessageHandler mqMessageHandler;
+
+ @RabbitListener(queues = ConfigOf00B.EAP_REQUEST_QUEUE_00B)
+ @RabbitHandler
+ public void eapRequest(Message> message, Channel channel)throws Exception{
+
+ logger.info("==============received message-EAP_REQUEST_QUEUE_00B=================,priority:"+"equipmentName"+message.getHeaders().get("attr2"));
+
+ Long deliveryTag = (Long)message.getHeaders().get(AmqpHeaders.DELIVERY_TAG);
+ //MQMessage 中的 transactionId
+ String transactionId = (String)message.getHeaders().get("spring_returned_message_correlation");
+ //logger.info("transactionId:"+transactionId);
+
+ try {
+
+ MQMessage mqMessage = CommonFunction.parse(message);
+
+ //1. 正常情况
+ //Integer integer = mqMessageHandler.requestHandler(message);
+ String integer= HttpUtil.post("localhost:8080/api/mq/mq-message-received/receivedFromEapRequest",JSONObject.toJSONString(mqMessage));
+ Integer result = Integer.valueOf(integer);
+
+ //注意 这里特别注意 已经拒收的消息 再签收是要出错的,这里要特别注意。
+ if(result == 1){
+ logger.info("在 EAP_REQUEST_QUEUE_00B 队列中,transitionId"+transactionId+", 这条消息处理成功");
+ channel.basicAck(deliveryTag,false);
+ }else {
+ logger.error("在 EAP_REQUEST_QUEUE_00B 队列中,transitionId"+transactionId+" 处理消息的时候 出现异常,然后 拒签消息 ,然后丢到死信队列");
+ channel.basicNack(deliveryTag,false,false);
+ }
+
+
+ //2.模拟异常 ,然后 拒签消息 ,然后丢到死信队列
+
+ //throw new Exception("11111");
+ }catch (Exception e){
+ // 第一个false 是 不批量签收;第二个false 是 不重回队列
+ channel.basicNack(deliveryTag,false,false);
+ return;
+ }
+
+
+
+ }
+
+ public static void main(String[] args) {
+ //localhost:8001
+
+
+ MQMessage mqMessage = new MQMessage();
+
+ Header header = new Header("Request","Execute","QUERYEQPStatus","12");
+ QueryEQStatusBody queryEQStatusBody = new QueryEQStatusBody();
+ queryEQStatusBody.setVidType("u4");
+ List vids = new ArrayList<>();
+ vids.add("10000");
+ vids.add("10001");
+ vids.add("10002");
+ queryEQStatusBody.setVidList(vids);
+ mqMessage.setBody(JSONObject.toJSONBytes(queryEQStatusBody));
+ mqMessage.setHeader(header);
+
+
+ String s = JSONObject.toJSONString(mqMessage);
+
+ System.out.println(s);
+ String result= HttpUtil.post("localhost:8001/receivedFromEapRequest",s);
+
+ System.out.println(result);
+ }
+
+ @RabbitListener(queues = ConfigOf00B.MES_RESPONSE_QUEUE_00B)
+ @RabbitHandler
+ public void mesResponse(Message> message, Channel channel)throws Exception{
+ logger.info("==============PID00B_Exchange-MES_Response_Queue=================,priority:"+message.getHeaders().get("priority")+",attr1"+message.getHeaders().get("attr1"));
+
+
+ Long deliveryTag = (Long)message.getHeaders().get(AmqpHeaders.DELIVERY_TAG);
+
+ //模拟异常 ,然后 拒签消息 ,然后丢到死信队列
+ try {
+ MQMessage mqMessage = CommonFunction.parse(message);
+ String transactionId = mqMessage.getHeader().getTransactionId();
+ Integer result = mqMessageHandler.responseHandler(message);
+ if(result == 1){
+ logger.info("在 MES_RESPONSE_QUEUE_00B 队列中 , transitionId"+transactionId+" 这条消息处理成功");
+ channel.basicAck(deliveryTag,false);
+ }else {
+ logger.error("在 MES_RESPONSE_QUEUE_00B 队列中 ,transitionId"+transactionId+" 处理消息的时候 出现异常,然后 拒签消息 ,然后丢到死信队列");
+ channel.basicNack(deliveryTag,false,false);
+ }
+
+ //throw new Exception("11111");
+ }catch (Exception e){
+ // 第一个false 是 不批量签收;第二个false 是 不重回队列
+ channel.basicNack(deliveryTag,false,false);
+ return;
+ }
+
+ //注意 这里特别注意 已经拒收的消息 再签收是要出错的,这里要特别注意。
+ //channel.basicAck(deliveryTag,false);
+
+ }
+
+}
diff --git a/src/main/java/com/qgs/dc/mq/consumer/PID00CReceived.java b/src/main/java/com/qgs/dc/mq/consumer/PID00CReceived.java
new file mode 100644
index 0000000..9b32d58
--- /dev/null
+++ b/src/main/java/com/qgs/dc/mq/consumer/PID00CReceived.java
@@ -0,0 +1,73 @@
+package com.qgs.dc.mq.consumer;
+
+import com.qgs.dc.mq.configuration.ConfigOf00C;
+import com.rabbitmq.client.Channel;
+import org.springframework.amqp.rabbit.annotation.RabbitHandler;
+import org.springframework.amqp.rabbit.annotation.RabbitListener;
+import org.springframework.amqp.support.AmqpHeaders;
+import org.springframework.messaging.Message;
+import org.springframework.stereotype.Component;
+
+/**
+ * @Desc: "PID00B设备 接收MQ消息 监听类"
+ * @Author: caixiang
+ * @DATE: 2021/6/22 15:30
+ *
+ * Ctrl+R 替换设备名
+ */
+@Component
+public class PID00CReceived {
+
+ @RabbitListener(queues = ConfigOf00C.EAP_REQUEST_QUEUE_00C)
+ @RabbitHandler
+ public void eapRequest(Message> message, Channel channel)throws Exception{
+
+ System.out.println("==============received message-EAP_REQUEST_QUEUE_00C=================,priority:"+"equipmentName"+message.getHeaders().get("attr2"));
+ Thread.sleep(100);
+
+ String correlationId = (String)message.getHeaders().get("spring_returned_message_correlation");
+ Long deliveryTag = (Long)message.getHeaders().get(AmqpHeaders.DELIVERY_TAG);
+
+
+
+
+ //模拟异常 ,然后 拒签消息 ,然后丢到死信队列
+ try {
+ System.err.println("处理消息的时候 出现异常,然后 拒签消息 ,然后丢到死信队列");
+ throw new Exception("11111");
+ }catch (Exception e){
+ // 第一个false 是 不批量签收;第二个false 是 不重回队列
+ channel.basicNack(deliveryTag,false,false);
+ return;
+ }
+
+ //注意 这里特别注意 已经拒收的消息 再签收是要出错的,这里要特别注意。
+ //channel.basicAck(deliveryTag,false);
+
+ }
+
+ @RabbitListener(queues = ConfigOf00C.MES_RESPONSE_QUEUE_00C)
+ @RabbitHandler
+ public void mesResponse(Message> message, Channel channel)throws Exception{
+ System.out.println("==============PID00B_Exchange-MES_Response_Queue=================,priority:"+message.getHeaders().get("priority")+",attr1"+message.getHeaders().get("attr1"));
+ Thread.sleep(100);
+
+ Long deliveryTag = (Long)message.getHeaders().get(AmqpHeaders.DELIVERY_TAG);
+
+
+ //模拟异常 ,然后 拒签消息 ,然后丢到死信队列
+ try {
+ System.err.println("处理消息的时候 出现异常,然后 拒签消息 ,然后丢到死信队列");
+ throw new Exception("11111");
+ }catch (Exception e){
+ // 第一个false 是 不批量签收;第二个false 是 不重回队列
+ channel.basicNack(deliveryTag,false,false);
+ return;
+ }
+
+ //注意 这里特别注意 已经拒收的消息 再签收是要出错的,这里要特别注意。
+ //channel.basicAck(deliveryTag,false);
+
+ }
+
+}
diff --git a/src/main/java/com/qgs/dc/mq/consumer/commonHandler/MQMessageHandler.java b/src/main/java/com/qgs/dc/mq/consumer/commonHandler/MQMessageHandler.java
new file mode 100644
index 0000000..1f1b9d2
--- /dev/null
+++ b/src/main/java/com/qgs/dc/mq/consumer/commonHandler/MQMessageHandler.java
@@ -0,0 +1,96 @@
+package com.qgs.dc.mq.consumer.commonHandler;
+
+import com.qgs.dc.common.utils.CommonFunction;
+import com.qgs.dc.mq.consumer.PID00BReceived;
+import com.qgs.dc.mq.entity.MQMessage;
+import com.qgs.dc.mq.entity.specificBody.QueryEQStatusBody;
+import com.qgs.dc.mq.producer.component.RabbitSender;
+import com.qgs.dc.mq.secsgem.SendedList;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.messaging.Message;
+import org.springframework.stereotype.Component;
+
+/**
+ * @Desc: ""
+ * @Author: caixiang
+ * @DATE: 2021/8/25 15:17
+ */
+@Component
+public class MQMessageHandler {
+
+ @Autowired
+ RabbitSender rabbitSender;
+
+ private static final Logger logger = LoggerFactory.getLogger(MQMessageHandler.class);
+
+ /**
+ * 执行 queryEquipmentStatus 业务逻辑
+ * @param mqMessage
+ * @return 结果依据requestHandler return来
+ */
+ public Integer queryEquipmentStatus(MQMessage mqMessage){
+ QueryEQStatusBody queryEqStatusBody = (QueryEQStatusBody)CommonFunction.parseBody(mqMessage.getBody(), QueryEQStatusBody.class);
+ //开始
+ //这里写 业务逻辑
+
+ //结束
+ System.out.println(queryEqStatusBody.toString());
+
+ return 1;
+ }
+
+ public static void main(String[] args) {
+ String s = "";
+ }
+
+ /**
+ * reply消息 处理方法
+ * @param message
+ * @return
+ */
+ public Integer responseHandler(Message> message){
+ MQMessage mqMessage = CommonFunction.parse(message);
+ String transactionId = mqMessage.getHeader().getTransactionId();
+ SendedList.set(transactionId,mqMessage);
+
+ return 1;
+ }
+
+ /**
+ * 业务处理方法
+ * @param message
+ * @return
+ * 1 = 代表成功 (外部可以 正常签收message (basicAck))
+ * 2 = 代表业务处理异常 (外部 拒收 (basicNack))
+ * 3 = 代表业务处理失败 (可以选择 签收 也可以拒收 依据自己业务决定 )
+ */
+ public Integer requestHandler(Message> message){
+
+ MQMessage mqMessage = CommonFunction.parse(message);
+ String messageName = mqMessage.getHeader().getMessageName();
+ Integer result = -1;
+ //业务逻辑
+ switch (messageName) {
+ case "QUERYEQPStatus":
+ result = queryEquipmentStatus(mqMessage);
+ break;
+ case "b": //b分支
+ System.out.println("匹配成功2");
+ break;
+ case "c": //c分支
+ System.out.println("匹配成功3");
+ break;
+ case "d": //d分支
+ System.out.println("匹配成功4");
+ break;
+ default:
+ break;
+ }
+
+ return result;
+ }
+
+
+}
diff --git a/src/main/java/com/qgs/dc/mq/controller/MQController.java b/src/main/java/com/qgs/dc/mq/controller/MQController.java
new file mode 100644
index 0000000..925cfc7
--- /dev/null
+++ b/src/main/java/com/qgs/dc/mq/controller/MQController.java
@@ -0,0 +1,91 @@
+package com.qgs.dc.mq.controller;
+
+import cn.hutool.http.HttpUtil;
+import com.alibaba.fastjson.JSONObject;
+import com.qgs.dc.mq.Constant.SecsGemTimeout;
+import com.qgs.dc.mq.entity.MQMessage;
+import com.qgs.dc.mq.entity.CallbackMessageEntity;
+import com.qgs.dc.mq.entity.common.Header;
+import com.qgs.dc.mq.entity.specificBody.QueryEQStatusBody;
+import com.qgs.dc.mq.producer.component.RabbitSender;
+import com.qgs.dc.mq.secsgem.AsyncFuture;
+import com.qgs.dc.mq.secsgem.SendedList;
+import com.qgs.dc.opcua.controller.R;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * @Author: 蔡翔
+ * @Date: 2019/10/12 15:15
+ * @Version 1.0
+ */
+@RestController
+@RequestMapping("/mq")
+public class MQController {
+ private static final Logger logger = LoggerFactory.getLogger(MQController.class);
+
+ @Autowired
+ RabbitSender rabbitSender;
+
+ @PostMapping("/eapResponse")
+ /**
+ * desc : 回复EAP的request 的回调接口(向rabbitmq中发送消息(direct模式))
+ * MES => DC(数据采集中间件) => MQ => EAP
+ */
+ public R eapResponse(@RequestBody CallbackMessageEntity callbackMessageEntity){
+ try {
+ logger.info("MES => EAP (EAP_Response), 状态:DC已收到 ,内容:"+ callbackMessageEntity.toString());
+ //properties 这里的参数是写在MQ消息 header里面的,如果EAP端 需要某些参数 可以写在这里,eap去取更方便一些
+ Map properties = new HashMap<>();
+ properties.put("equipmentName","PID001");
+ properties.put("transitionId", callbackMessageEntity.getMqMessage().getHeader().getTransactionId());
+ rabbitSender.reply(callbackMessageEntity.getMqMessage(),properties, callbackMessageEntity.getExchangeName(), callbackMessageEntity.getRoutingKey(),"6000");
+ logger.info("MES => EAP (EAP_Response) , 状态:DC已发送给MQ , 内容:"+ callbackMessageEntity.toString());
+ return R.ok("回复成功");
+ }catch (Exception e){
+ logger.error("MES => EAP (EAP_Response) , 状态:DC处理异常 , 内容:"+ callbackMessageEntity.toString());
+ return R.error().put("result",e.getMessage());
+ }
+ }
+
+ @PostMapping("/mesRequest")
+ /**
+ * desc : MES给EAP发送远程指令(MES_Request)(向rabbitmq中发送消息(direct模式))
+ * MES => DC(数据采集中间件) => MQ => EAP
+ * return :返回 的就是这个指令的回复指令
+ */
+ public R mesRequest(@RequestBody CallbackMessageEntity callbackMessageEntity){
+ try {
+ String transitionId = callbackMessageEntity.getMqMessage().getHeader().getTransactionId();
+ MQMessage mqMessage = callbackMessageEntity.getMqMessage();
+ String exchangeName = callbackMessageEntity.getExchangeName();
+ String routingKey = callbackMessageEntity.getRoutingKey();
+
+ logger.info("MES => EAP (MES_Request), 状态:DC已收到 , 内容:"+ callbackMessageEntity.toString());
+ //properties 这里的参数是写在MQ消息 header里面的,如果EAP端 需要某些参数 可以写在这里,eap去取更方便一些
+ Map properties = new HashMap<>();
+ properties.put("equipmentName","PID001");
+ properties.put("transitionId",transitionId);
+
+ rabbitSender.reply(mqMessage,properties,exchangeName,routingKey,"6000");
+ logger.info("MES => EAP (MES_Request) , 状态:DC已发送给MQ , 内容:"+ callbackMessageEntity.toString());
+
+ AsyncFuture add = SendedList.add(transitionId,mqMessage);
+ MQMessage mqMessageResponse = add.get(SecsGemTimeout.T3_TIMEOUT);
+ return R.ok().put("responseMessage",mqMessageResponse);
+ }catch (Exception e){
+ logger.error("MES => EAP (MES_Request) , 状态:DC处理异常 , 内容:"+ callbackMessageEntity.toString());
+ return R.error().put("result",e.getMessage());
+ }
+ }
+}
diff --git a/src/main/java/com/qgs/dc/mq/entity/CallbackMessageEntity.java b/src/main/java/com/qgs/dc/mq/entity/CallbackMessageEntity.java
new file mode 100644
index 0000000..d6e2790
--- /dev/null
+++ b/src/main/java/com/qgs/dc/mq/entity/CallbackMessageEntity.java
@@ -0,0 +1,17 @@
+package com.qgs.dc.mq.entity;
+
+import com.alibaba.fastjson.annotation.JSONType;
+import lombok.Data;
+
+/**
+ * @Desc: ""
+ * @Author: caixiang
+ * @DATE: 2021/8/27 10:17
+ */
+@Data
+@JSONType(orders={"exchangeName","routingKey","mqMessage"})
+public class CallbackMessageEntity {
+ private String exchangeName;
+ private String routingKey;
+ private MQMessage mqMessage;
+}
diff --git a/src/main/java/com/qgs/dc/mq/entity/MQMessage.java b/src/main/java/com/qgs/dc/mq/entity/MQMessage.java
new file mode 100644
index 0000000..df5d0da
--- /dev/null
+++ b/src/main/java/com/qgs/dc/mq/entity/MQMessage.java
@@ -0,0 +1,19 @@
+package com.qgs.dc.mq.entity;
+
+import com.alibaba.fastjson.annotation.JSONType;
+import com.qgs.dc.mq.entity.common.Header;
+import com.qgs.dc.mq.entity.common.Returns;
+import lombok.Data;
+
+/**
+ * @Desc: ""
+ * @Author: caixiang
+ * @DATE: 2021/8/12 15:38
+ */
+@Data
+@JSONType(orders={"header","body","returns"})
+public class MQMessage {
+ private Header header;
+ private byte[] body;
+ private Returns returns;
+}
\ No newline at end of file
diff --git a/src/main/java/com/qgs/dc/mq/entity/common/Header.java b/src/main/java/com/qgs/dc/mq/entity/common/Header.java
new file mode 100644
index 0000000..3d94aa4
--- /dev/null
+++ b/src/main/java/com/qgs/dc/mq/entity/common/Header.java
@@ -0,0 +1,103 @@
+package com.qgs.dc.mq.entity.common;
+
+import com.alibaba.fastjson.annotation.JSONType;
+import com.qgs.dc.common.utils.CommonFunction;
+import lombok.Data;
+
+/**
+ * @Desc: ""
+ * @Author: caixiang
+ * @DATE: 2021/8/12 15:26
+ */
+@Data
+@JSONType(orders={"transactionId","messageType","messageCategory","messageName","equipmentId","sendTimestamp","from","to"})
+public class Header {
+ private String transactionId;
+ private String messageType;
+ private String messageCategory;
+ private String messageName;
+ private String equipmentId;
+ private String sendTimestamp;
+ private String from;
+ private String to;
+
+ //注意:一定要构造一个空的 构造函数,不然fastjson在序列化 和反序列化的时候会用下面的Header这样导致数据一致
+ public Header() {
+
+ }
+ public Header(String messageType, String messageCategory, String messageName, String equipmentId) {
+
+ this.transactionId = equipmentId+"_"+ CommonFunction.getNowDate(2)+"_"+CommonFunction.getUUID(5);
+ this.messageType = messageType;
+ this.messageCategory = messageCategory;
+ this.messageName = messageName;
+ this.equipmentId = equipmentId;
+ this.sendTimestamp = CommonFunction.getNowDate(1);
+ this.from = "mes";
+ this.to = "eap";
+ }
+
+ public String getTransactionId() {
+ return transactionId;
+ }
+
+ public void setTransactionId(String transactionId) {
+ this.transactionId = transactionId;
+ }
+
+ public String getMessageType() {
+ return messageType;
+ }
+
+ public void setMessageType(String messageType) {
+ this.messageType = messageType;
+ }
+
+ public String getMessageCategory() {
+ return messageCategory;
+ }
+
+ public void setMessageCategory(String messageCategory) {
+ this.messageCategory = messageCategory;
+ }
+
+ public String getMessageName() {
+ return messageName;
+ }
+
+ public void setMessageName(String messageName) {
+ this.messageName = messageName;
+ }
+
+ public String getEquipmentId() {
+ return equipmentId;
+ }
+
+ public void setEquipmentId(String equipmentId) {
+ this.equipmentId = equipmentId;
+ }
+
+ public String getSendTimestamp() {
+ return sendTimestamp;
+ }
+
+ public void setSendTimestamp(String sendTimestamp) {
+ this.sendTimestamp = sendTimestamp;
+ }
+
+ public String getFrom() {
+ return from;
+ }
+
+ public void setFrom(String from) {
+ this.from = from;
+ }
+
+ public String getTo() {
+ return to;
+ }
+
+ public void setTo(String to) {
+ this.to = to;
+ }
+}
diff --git a/src/main/java/com/qgs/dc/mq/entity/common/Returns.java b/src/main/java/com/qgs/dc/mq/entity/common/Returns.java
new file mode 100644
index 0000000..4d48826
--- /dev/null
+++ b/src/main/java/com/qgs/dc/mq/entity/common/Returns.java
@@ -0,0 +1,37 @@
+package com.qgs.dc.mq.entity.common;
+
+import com.alibaba.fastjson.annotation.JSONType;
+import lombok.Data;
+
+/**
+ * @Desc: ""
+ * @Author: caixiang
+ * @DATE: 2021/8/12 15:35
+ */
+@Data
+@JSONType(orders={"returnCode","ReasonCode"})
+public class Returns {
+ private String returnCode;
+ private String ReasonCode;
+
+ public String getReturnCode() {
+ return returnCode;
+ }
+
+ public void setReturnCode(String returnCode) {
+ this.returnCode = returnCode;
+ }
+
+ public String getReasonCode() {
+ return ReasonCode;
+ }
+
+ public void setReasonCode(String reasonCode) {
+ ReasonCode = reasonCode;
+ }
+
+ public Returns(String returnCode, String reasonCode) {
+ this.returnCode = returnCode;
+ ReasonCode = reasonCode;
+ }
+}
diff --git a/src/main/java/com/qgs/dc/mq/entity/specificBody/QueryEQStatusBody.java b/src/main/java/com/qgs/dc/mq/entity/specificBody/QueryEQStatusBody.java
new file mode 100644
index 0000000..435e090
--- /dev/null
+++ b/src/main/java/com/qgs/dc/mq/entity/specificBody/QueryEQStatusBody.java
@@ -0,0 +1,18 @@
+package com.qgs.dc.mq.entity.specificBody;
+
+import com.alibaba.fastjson.annotation.JSONType;
+import lombok.Data;
+
+import java.util.List;
+
+/**
+ * @Desc: ""
+ * @Author: caixiang
+ * @DATE: 2021/8/12 15:43
+ */
+@Data
+@JSONType(orders={"VIDType","VIDList"})
+public class QueryEQStatusBody {
+ private String vidType;
+ private List vidList;
+}
diff --git a/src/main/java/com/qgs/dc/mq/exception/T3TimeoutException.java b/src/main/java/com/qgs/dc/mq/exception/T3TimeoutException.java
new file mode 100644
index 0000000..7e26db4
--- /dev/null
+++ b/src/main/java/com/qgs/dc/mq/exception/T3TimeoutException.java
@@ -0,0 +1,19 @@
+package com.qgs.dc.mq.exception;
+
+/**
+ * @Desc: ""
+ * @Author: caixiang
+ * @DATE: 2021/8/17 16:03
+ */
+public class T3TimeoutException extends Exception{
+ private static final long serialVersionUID = 1L;
+
+ // 提供无参数的构造方法
+ public T3TimeoutException() {
+ }
+
+ // 提供一个有参数的构造方法,可自动生成
+ public T3TimeoutException(String message) {
+ super(message);// 把参数传递给Throwable的带String参数的构造方法
+ }
+}
diff --git a/src/main/java/com/qgs/dc/mq/producer/component/RabbitSender.java b/src/main/java/com/qgs/dc/mq/producer/component/RabbitSender.java
new file mode 100644
index 0000000..5636468
--- /dev/null
+++ b/src/main/java/com/qgs/dc/mq/producer/component/RabbitSender.java
@@ -0,0 +1,350 @@
+package com.qgs.dc.mq.producer.component;
+
+import com.alibaba.fastjson.JSONObject;
+import com.qgs.dc.common.utils.CommonFunction;
+import com.qgs.dc.mq.entity.MQMessage;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import org.springframework.amqp.AmqpException;
+import org.springframework.amqp.core.MessagePostProcessor;
+import org.springframework.amqp.core.ReturnedMessage;
+import org.springframework.amqp.rabbit.connection.CorrelationData;
+import org.springframework.amqp.rabbit.core.RabbitTemplate;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.messaging.Message;
+import org.springframework.messaging.MessageHeaders;
+import org.springframework.messaging.support.MessageBuilder;
+import org.springframework.stereotype.Component;
+
+import java.util.Map;
+import java.util.Random;
+import java.util.UUID;
+
+@Component
+public class RabbitSender {
+ @Autowired
+ private RabbitTemplate rabbitTemplate;
+
+
+ private static final Logger logger = LoggerFactory.getLogger(RabbitSender.class);
+
+
+ final RabbitTemplate.ReturnsCallback returnsCallback = new RabbitTemplate.ReturnsCallback(){
+ //rabbitTemplate.setReturnCallback 会调用这个接口
+ //第二种publisher-returns模式,该模式会在消息没有被路由到queues时将消息返回
+ @Override
+ public void returnedMessage(ReturnedMessage returned) {
+ org.springframework.amqp.core.Message message = returned.getMessage();
+ logger.error("消息未能成功路由到指定queues : return--message:" + new String(message.getBody()) + ",replyCode:" + returned.getReplyCode()
+ + ",replyText:" + returned.getReplyText() + ",exchange:" + returned.getExchange() + ",routingKey:" + returned.getRoutingKey());
+ }
+ };
+
+
+ /**
+ * rabbitTemplate.setConfirmCallback() 会调用这个接口。
+ * 确认消息 的回调监听接口,,用于确认消息是否被broker所收到(也就是 是否投递到exchange(不能保证是否投递到queue))。
+ * 当消息被broker 成功接收到 后 会调用这个方法
+ * */
+ /**
+ * 参数:
+ * CorrelationData 作为一个唯一标识( 生产者 消费者 消息确认的唯一标识 )
+ * b broker是否落盘成功
+ * s 失败的一些异常信息会送
+ * */
+ final RabbitTemplate.ConfirmCallback confirmCallback = new RabbitTemplate.ConfirmCallback() {
+ /**
+ * 参数:
+ * CorrelationData 作为一个唯一标识( 生产者 消费者 消息确认的唯一标识 )
+ * ack broker是否落盘成功
+ * s 失败的一些异常信息会送
+ * */
+ @Override
+ public void confirm(CorrelationData correlationData, boolean ack, String cause) {
+ //如果RabbitMQ 因为自身内部错
+ //误导致消息丢失,就会发送一条nack (Basic . Nack )命令,生产者应用程序同样可以在回调
+ //方法中处理该nack 命令。
+ if (ack) {
+ logger.info("消息 transactionId: [" + correlationData.getId() + "] 成功发送到指定ExChange");
+ } else {
+ logger.info("消息 transactionId: [" + correlationData.getId() + "] 发送到ExChange失败,原因 : " + cause);
+ }
+ }
+ };
+
+
+ /**
+ * @Description 向rabbitmq中发送消息(topic模式)
+ * @Param
+ * message 具体的消息内容
+ * properties 额外的附加属性
+ * @Return void
+ * @Author caixiang
+ * @Date 2020/6/17
+ **/
+ public void sendTopic(Object message, Map properties, String routingKey) throws Exception{
+ MessageHeaders messageHeaders = new MessageHeaders(properties);
+ //message的Object 传进来的可能就是 泛型。。createMessage会自动生成 deliveryTag
+ Message> msg = MessageBuilder.createMessage(message,messageHeaders);
+
+ //唯一标识
+ CorrelationData cd = new CorrelationData(UUID.randomUUID().toString());
+
+ //设置确认 confirm
+ rabbitTemplate.setConfirmCallback(confirmCallback);
+
+ /**
+ * 声明消息处理器 (这个消息处理器 是在 发给broker 之前执行的(就是对你要发送给mq 的消息 进行封装处理 和 上面的confirmCallback不同)。)
+ * 这个对消息进行处理 可以设置一些参数 对消息进行一些定制化处理
+ * 我们这里 来设置消息的编码 以及消息的过期时间
+ * 因为在.net 以及其他版本过期时间不一致 这里的时间毫秒值 为字符串
+ * */
+ MessagePostProcessor mpp = new MessagePostProcessor(){
+ //当发送消息到broker 成功 之后调用下面的方法。
+ @Override
+ public org.springframework.amqp.core.Message postProcessMessage(org.springframework.amqp.core.Message message) throws AmqpException {
+ System.err.println("posted todo:"+message);
+ //message.getMessageProperties() 下 可以进行各种 SET 属性
+ return message;
+ }
+ };
+ //发送端 要可以注重routingkey。。。因为exchange 要依据这个routingkey 把这条消息转发给队列。
+ rabbitTemplate.convertAndSend("rc_exchange",
+ routingKey,
+ msg,
+ mpp,
+ cd
+ );
+ }
+
+
+ /**
+ * @Description 向rabbitmq中发送消息(direct模式)
+ * @Param
+ * mqMessage 通用的消息格式
+ * properties 额外的附加属性,放在Message Header里
+ * exchange exchangeName
+ * routingKey 路由键
+ * expiration 设置这条消息的过期时间,如 6000 =》 6s ,6s后未被消费就过期
+ * @Return void
+ * @Author caixiang
+ * @Date 2020/6/17
+ **/
+ public void sendDirect(MQMessage mqMessage, Map properties, String exchange, String routingKey, String expiration) throws Exception{
+ byte[] message = JSONObject.toJSONBytes(mqMessage);
+
+ MessageHeaders messageHeaders = new MessageHeaders(properties);
+ //message的Object 传进来的可能就是 泛型。。createMessage会自动生成 deliveryTag
+
+ //content_type 默认 就是application/octet-stream 不是json格式的
+ Message> msg = MessageBuilder.createMessage(message,messageHeaders);
+
+
+ //唯一标识
+ String transitionId = mqMessage.getHeader().getEquipmentId()+"_"+ CommonFunction.getNowDate(2)+"_"+CommonFunction.getUUID(5);
+ CorrelationData cd = new CorrelationData(transitionId);
+
+
+ //设置确认 confirm(),投递到Exchange 成功的时候 会调用下面这个回调方法
+ //rabbitTemplate.setConfirmCallback(confirmCallback); //ok
+
+
+ //注意 有关rabbitTemplate 配置 的信息 不能写在这里,
+ // 因为 每次外部REST接口过来(不同线程) 调用sendDirect方法的时候 都会setConfirmCallback(new ConfirmBackSender());
+ //而rabbitmq只允许 设置 一次 ConfirmBack;这里new 了这么多 肯定不对
+ //rabbitTemplate.setConfirmCallback(new ConfirmBackSender()); //这样不行
+ rabbitTemplate.setConfirmCallback(confirmCallback); //这样可以,confirmBackSender就是把回调处理类 独立出去了 ,和写在本类中的returnCallback 做一个区别
+
+ //设置确认 returnedMessage(); 当未投递到queue的时候 会调用下面这个方法。
+ //注意 :如果要设置setReturnCallback 那么 必须配置spring.rabbitmq.publisher-returns=true,spring.rabbitmq.template.mandatory=true
+ rabbitTemplate.setReturnsCallback(returnsCallback);
+
+ /**
+ * 声明消息处理器 (这个消息处理器 是在 发给broker 之前执行的(就是对你要发送给mq 的消息 进行封装处理 和 上面的confirmCallback不同)。)
+ * 这个对消息进行处理 可以设置一些参数 对消息进行一些定制化处理
+ * 我们这里 来设置消息的编码 以及消息的过期时间
+ * 因为在.net 以及其他版本过期时间不一致 这里的时间毫秒值 为字符串
+ * */
+ MessagePostProcessor mpp = new MessagePostProcessor(){
+ //当发送消息到broker 之前调用下面的方法,也就是对消息进行加工。
+ @Override
+ public org.springframework.amqp.core.Message postProcessMessage(org.springframework.amqp.core.Message message) throws AmqpException {
+// System.err.println("posted todo:"+message);
+// //message.getMessageProperties() 下 可以进行各种 SET 属性
+// Random random = new Random();
+// int i = random.nextInt(10);
+// message.getMessageProperties().setPriority(i);
+ //过期时间: 单位毫秒 (注意:这些消息 只有在队列队首的 时候过期了才会移除,否则是不会被移除的)
+ message.getMessageProperties().setExpiration(expiration);
+ return message;
+ }
+ };
+
+
+ //发送端 要可以注重routingkey。。。因为exchange 要依据这个routingkey 把这条消息转发给队列。
+ rabbitTemplate.convertAndSend(exchange,
+ routingKey,
+ msg,
+ mpp,
+ cd
+ );
+ }
+
+ /**
+ * @Description 回复EAP的request(向rabbitmq中发送消息(direct模式))
+ * @Param
+ * mqMessage 通用的消息格式
+ * properties 额外的附加属性,放在Message Header里
+ * exchange exchangeName
+ * routingKey 路由键
+ * expiration 设置这条消息的过期时间,如 6000 =》 6s ,6s后未被消费就过期
+ * @Return void
+ * @Author caixiang
+ * @Date 2020/6/17
+ **/
+ public void reply(MQMessage mqMessage, Map properties, String exchange, String routingKey, String expiration) throws Exception{
+ byte[] message = JSONObject.toJSONBytes(mqMessage);
+
+ MessageHeaders messageHeaders = new MessageHeaders(properties);
+ //message的Object 传进来的可能就是 泛型。。createMessage会自动生成 deliveryTag
+
+ //content_type 默认 就是application/octet-stream 不是json格式的
+ Message> msg = MessageBuilder.createMessage(message,messageHeaders);
+
+
+ //唯一标识
+ String transitionId = mqMessage.getHeader().getEquipmentId()+"_"+ CommonFunction.getNowDate(2)+"_"+CommonFunction.getUUID(5);
+ CorrelationData cd = new CorrelationData(transitionId);
+
+
+ //设置确认 confirm(),投递到Exchange 成功的时候 会调用下面这个回调方法
+ //rabbitTemplate.setConfirmCallback(confirmCallback); //ok
+
+
+ //注意 有关rabbitTemplate 配置 的信息 不能写在这里,
+ // 因为 每次外部REST接口过来(不同线程) 调用sendDirect方法的时候 都会setConfirmCallback(new ConfirmBackSender());
+ //而rabbitmq只允许 设置 一次 ConfirmBack;这里new 了这么多 肯定不对
+ //rabbitTemplate.setConfirmCallback(new ConfirmBackSender()); //这样不行
+ rabbitTemplate.setConfirmCallback(confirmCallback); //这样可以,confirmBackSender就是把回调处理类 独立出去了 ,和写在本类中的returnCallback 做一个区别
+
+ //设置确认 returnedMessage(); 当未投递到queue的时候 会调用下面这个方法。
+ //注意 :如果要设置setReturnCallback 那么 必须配置spring.rabbitmq.publisher-returns=true,spring.rabbitmq.template.mandatory=true
+ rabbitTemplate.setReturnsCallback(returnsCallback);
+
+ /**
+ * 声明消息处理器 (这个消息处理器 是在 发给broker 之前执行的(就是对你要发送给mq 的消息 进行封装处理 和 上面的confirmCallback不同)。)
+ * 这个对消息进行处理 可以设置一些参数 对消息进行一些定制化处理
+ * 我们这里 来设置消息的编码 以及消息的过期时间
+ * 因为在.net 以及其他版本过期时间不一致 这里的时间毫秒值 为字符串
+ * */
+ MessagePostProcessor mpp = new MessagePostProcessor(){
+ //当发送消息到broker 之前调用下面的方法,也就是对消息进行加工。
+ @Override
+ public org.springframework.amqp.core.Message postProcessMessage(org.springframework.amqp.core.Message message) throws AmqpException {
+// System.err.println("posted todo:"+message);
+// //message.getMessageProperties() 下 可以进行各种 SET 属性
+// Random random = new Random();
+// int i = random.nextInt(10);
+// message.getMessageProperties().setPriority(i);
+ //过期时间: 单位毫秒 (注意:这些消息 只有在队列队首的 时候过期了才会移除,否则是不会被移除的)
+ message.getMessageProperties().setExpiration(expiration);
+ return message;
+ }
+ };
+
+
+ //发送端 要可以注重routingkey。。。因为exchange 要依据这个routingkey 把这条消息转发给队列。
+ rabbitTemplate.convertAndSend(exchange,
+ routingKey,
+ msg,
+ mpp,
+ cd
+ );
+ }
+ //todo sendDirect应该改成 sendCommon(加上T3超时校验),还要加一个reply() 方法 专门用于 eapRequest 回复用
+
+
+
+ public void sendDirect(MQMessage mqMessage, Map properties, String exchange, String routingKey) throws Exception{
+ byte[] message = JSONObject.toJSONBytes(mqMessage);
+ MessageHeaders messageHeaders = new MessageHeaders(properties);
+ Message> msg = MessageBuilder.createMessage(message,messageHeaders);
+ //String transitionId = mqMessage.getHeader().getEquipmentId()+"_"+CommonFunction.getNowDate(2)+"_"+CommonFunction.getUUID(10);
+ String transitionId = mqMessage.getHeader().getTransactionId();
+ CorrelationData cd = new CorrelationData(transitionId);
+ rabbitTemplate.setConfirmCallback(confirmCallback);
+ rabbitTemplate.setReturnsCallback(returnsCallback);
+ rabbitTemplate.convertAndSend(exchange,
+ routingKey,
+ msg,
+ cd
+ );
+ }
+
+ /**
+ * @Description 向rabbitmq中发送消息(direct模式)
+ * @Param
+ * message 具体的消息内容
+ * properties 额外的附加属性
+ * @Return void
+ * @Author caixiang
+ * @Date 2020/6/17
+ **/
+ public void sendDirect(Object message, Map properties,String routingKey,String exchange) throws Exception{
+ MessageHeaders messageHeaders = new MessageHeaders(properties);
+ //message的Object 传进来的可能就是 泛型。。createMessage会自动生成 deliveryTag
+
+ //content_type 默认 就是application/octet-stream 不是json格式的
+ Message> msg = MessageBuilder.createMessage(message,messageHeaders);
+
+
+ //唯一标识
+ CorrelationData cd = new CorrelationData(UUID.randomUUID().toString());
+
+
+ //设置确认 confirm(),投递到Exchange 成功的时候 会调用下面这个回调方法
+ //rabbitTemplate.setConfirmCallback(confirmCallback); //ok
+
+
+ //注意 有关rabbitTemplate 配置 的信息 不能写在这里,
+ // 因为 每次外部REST接口过来(不同线程) 调用sendDirect方法的时候 都会setConfirmCallback(new ConfirmBackSender());
+ //而rabbitmq只允许 设置 一次 ConfirmBack;这里new 了这么多 肯定不对
+ //rabbitTemplate.setConfirmCallback(new ConfirmBackSender()); //这样不行
+ rabbitTemplate.setConfirmCallback(confirmCallback); //这样可以,confirmBackSender就是把回调处理类 独立出去了 ,和写在本类中的returnCallback 做一个区别
+
+ //设置确认 returnedMessage(); 当未投递到queue的时候 会调用下面这个方法。
+ //注意 :如果要设置setReturnCallback 那么 必须配置spring.rabbitmq.publisher-returns=true,spring.rabbitmq.template.mandatory=true
+ rabbitTemplate.setReturnsCallback(returnsCallback);
+
+ /**
+ * 声明消息处理器 (这个消息处理器 是在 发给broker 之前执行的(就是对你要发送给mq 的消息 进行封装处理 和 上面的confirmCallback不同)。)
+ * 这个对消息进行处理 可以设置一些参数 对消息进行一些定制化处理
+ * 我们这里 来设置消息的编码 以及消息的过期时间
+ * 因为在.net 以及其他版本过期时间不一致 这里的时间毫秒值 为字符串
+ * */
+ MessagePostProcessor mpp = new MessagePostProcessor(){
+ //当发送消息到broker 之前调用下面的方法,也就是对消息进行加工。
+ @Override
+ public org.springframework.amqp.core.Message postProcessMessage(org.springframework.amqp.core.Message message) throws AmqpException {
+ System.err.println("posted todo:"+message);
+ //message.getMessageProperties() 下 可以进行各种 SET 属性
+ Random random = new Random();
+ int i = random.nextInt(10);
+ message.getMessageProperties().setPriority(i);
+ //过期时间: 单位毫秒 (注意:这些消息 只有在队列队首的 时候过期了才会移除,否则是不会被移除的)
+ message.getMessageProperties().setExpiration("6000");
+ return message;
+ }
+ };
+
+
+ //发送端 要可以注重routingkey。。。因为exchange 要依据这个routingkey 把这条消息转发给队列。
+ rabbitTemplate.convertAndSend(exchange,
+ routingKey,
+ msg,
+ mpp,
+ cd
+ );
+ }
+}
diff --git a/src/main/java/com/qgs/dc/mq/producer/controller/TestController.java b/src/main/java/com/qgs/dc/mq/producer/controller/TestController.java
new file mode 100644
index 0000000..49bfa90
--- /dev/null
+++ b/src/main/java/com/qgs/dc/mq/producer/controller/TestController.java
@@ -0,0 +1,130 @@
+package com.qgs.dc.mq.producer.controller;
+
+import com.alibaba.fastjson.JSONObject;
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.qgs.dc.common.utils.CommonFunction;
+import com.qgs.dc.mq.producer.component.RabbitSender;
+
+import com.qgs.dc.mq.entity.MQMessage;
+import com.qgs.dc.mq.entity.common.Header;
+import com.qgs.dc.mq.entity.specificBody.QueryEQStatusBody;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * @Desc: ""
+ * @Author: caixiang
+ * @DATE: 2020/9/30 11:03
+ */
+@RestController
+public class TestController {
+ @Autowired
+ RabbitSender rabbitSender;
+
+
+
+ //For EAP SERVER1
+ @GetMapping("/putMessageDirect")
+ public String putMessageDirect() throws Exception {
+ Map properties = new HashMap<>();
+ properties.put("attr1",String.valueOf(1));
+ properties.put("attr2","00B");
+ //序列化
+ MQMessage mqMessage = new MQMessage();
+
+ Header header = new Header("Request","Execute","QUERYEQPStatus","12");
+ header.setTransactionId("abc123456");
+ QueryEQStatusBody queryEQStatusBody = new QueryEQStatusBody();
+ queryEQStatusBody.setVidType("u4");
+ List vids = new ArrayList<>();
+ vids.add("10000");
+ vids.add("10001");
+ vids.add("10002");
+ queryEQStatusBody.setVidList(vids);
+ mqMessage.setBody(JSONObject.toJSONBytes(queryEQStatusBody));
+ mqMessage.setHeader(header);
+
+
+
+ //rabbitSender.sendDirect(mqMessage,properties,"00B_Exchange","00B_EAP_Request_Queue_RoutingKey","6000");
+ rabbitSender.sendDirect(mqMessage,properties,"00B_Exchange","00B_MES_Response_Queue_RoutingKey");
+
+ Thread.sleep(30);
+ return null;
+ }
+
+ public static void main(String[] args) throws IOException {
+ Map properties = new HashMap<>();
+ properties.put("attr1",String.valueOf(1));
+ properties.put("attr2","00B");
+ //序列化
+ MQMessage mqMessage = new MQMessage();
+
+ Header header = new Header("Request","Execute","QUERYEQPStatus","12");
+ header.setTransactionId("abc123456");
+ QueryEQStatusBody queryEQStatusBody = new QueryEQStatusBody();
+ queryEQStatusBody.setVidType("u4");
+ List vids = new ArrayList<>();
+ vids.add("10000");
+ vids.add("10001");
+ vids.add("10002");
+ queryEQStatusBody.setVidList(vids);
+ mqMessage.setBody(JSONObject.toJSONBytes(queryEQStatusBody));
+ mqMessage.setHeader(header);
+ System.out.println(mqMessage);
+ //序列化
+ //1.jackson
+ ObjectMapper objectMapper = new ObjectMapper();
+ //byte[] byteJackson = objectMapper.writeValueAsBytes(mqMessage);
+ //2.fastjson
+ byte[] bytes = JSONObject.toJSONBytes(mqMessage);
+
+ //反序列化
+ //1.jackson
+ //MQMessage mqMessageJackson = objectMapper.readValue(byteJackson, MQMessage.class);
+ //2.fastjson
+ MQMessage mqMessageFastjson = JSONObject.parseObject(bytes, MQMessage.class);
+ System.out.println();
+ }
+
+
+// public static void main(String[] args) {
+// //序列化
+// MQMessage mesResponse = new MQMessage();
+//
+// Header header = new Header("Request","Execute","QUERYEQPStatus","12");
+// QueryEQStatusBody queryEQStatusBody = new QueryEQStatusBody();
+// queryEQStatusBody.setVidType("u4");
+// List vids = new ArrayList<>();
+// vids.add("10000");
+// vids.add("10001");
+// vids.add("10002");
+// queryEQStatusBody.setVidList(vids);
+// mesResponse.setBody(JSONObject.toJSONBytes(queryEQStatusBody));
+// mesResponse.setHeader(header);
+//
+// byte[] bytes = JSONObject.toJSONBytes(mesResponse);
+//// String s = JSONObject.toJSONString(requestBody);
+//// RequestBody parsedRequestBody = JSONObject.parseObject(bytes, RequestBody.class);
+//// JSONObject jsonObject = (JSONObject)parsedRequestBody.getBody();
+//// QueryEQStatusBody queryEQStatusBody1 = JSON.toJavaObject(jsonObject, QueryEQStatusBody.class);
+//
+// //1.先解析成MesRequest/MesResponse ,拿到header里的messageName
+//// MQMessage message = CommonFunction.parse(bytes);
+//// String messageName = message.getHeader().getMessageName();
+////
+//// //拿到具体messageName 就可以拿到具体body
+//// QueryEQStatusBody queryEQStatusBody1 = (QueryEQStatusBody)CommonFunction.parseBody(message.getBody(), QueryEQStatusBody.class);
+//// System.out.println();
+// }
+
+
+}
diff --git a/src/main/java/com/qgs/dc/mq/secsgem/AsyncFuture.java b/src/main/java/com/qgs/dc/mq/secsgem/AsyncFuture.java
new file mode 100644
index 0000000..e11f704
--- /dev/null
+++ b/src/main/java/com/qgs/dc/mq/secsgem/AsyncFuture.java
@@ -0,0 +1,63 @@
+package com.qgs.dc.mq.secsgem;
+
+
+import com.qgs.dc.mq.exception.T3TimeoutException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * @Author: 蔡翔
+ * @Date: 2019/11/6 14:11
+ * @Version 1.0
+ *
+ * (重点)
+ * AsyncFuture 这个类就是票据, 刚拿到这个票据是没有信息的,当done == true 的时候,这个票据 上就自动有信息了
+ * 这个结果类设计的比较神奇
+ */
+public class AsyncFuture implements Future {
+
+ private static final Logger logger = LoggerFactory.getLogger(AsyncFuture.class);
+
+ private volatile boolean done = false;
+ private MQMessage oldRequest;
+ private MQMessage result;
+
+ public AsyncFuture(MQMessage oldRequest) {
+ this.oldRequest = oldRequest;
+ }
+ public AsyncFuture() {
+ }
+
+ public void done(MQMessage result){
+ synchronized (this){
+ System.out.println("done");
+
+ this.result = result;
+ this.done = true;
+ //注意这里的notifyAll只能唤醒 本锁的所有 下的所有 wait(),这里的锁就是 AsyncFuture这个类
+ notifyAll();
+ }
+ }
+
+
+ @Override
+ public MQMessage get(Long timeout) throws Exception {
+ synchronized (this){
+ //其实有 synchronize就相当于有一个阻塞队列,当有线程执行了wait 方法,就会把执行wait的这个线程给加入wait 队列,
+ //当有线程执行notify方法的时候,就会往这个队列中取出一个或者多个Thread,取出来以后就能执行后续代码了
+ System.out.println("get");
+
+ // 当线程执行wait()时,会把当前的锁释放,然后让出CPU,进入等待状态。 wait()会立刻释放synchronized(obj)中的obj锁,以便其他线程可以执行obj.notify()
+ // * 当线程执行notify()/notifyAll()方法时,会唤醒一个处于等待状态该对象锁的线程,然后继续往下执行,直到执行完退出对象锁锁住的区域(synchronized修饰的代码块)后再释放锁
+ this.wait(timeout);
+ if(!done){
+ logger.error("T3 timeout , request information: "+oldRequest.toString());
+ throw new T3TimeoutException("T3 timeout , request information: "+oldRequest.toString());
+ }
+
+ //因为上面的代码是加锁的,所以这里的代码也是加锁的。
+ return result;
+ }
+
+ }
+}
diff --git a/src/main/java/com/qgs/dc/mq/secsgem/Future.java b/src/main/java/com/qgs/dc/mq/secsgem/Future.java
new file mode 100644
index 0000000..2c2da0b
--- /dev/null
+++ b/src/main/java/com/qgs/dc/mq/secsgem/Future.java
@@ -0,0 +1,14 @@
+package com.qgs.dc.mq.secsgem;
+
+
+import com.qgs.dc.mq.entity.MQMessage;
+
+/**
+ * @Author: 蔡翔
+ * @Date: 2019/11/6 13:49
+ * @Version 1.0
+ */
+public interface Future {
+ //别人调用我的时候,我先给他们返回一个结果,
+ MQMessage get(Long timeout) throws Exception;
+}
diff --git a/src/main/java/com/qgs/dc/mq/secsgem/Main.java b/src/main/java/com/qgs/dc/mq/secsgem/Main.java
new file mode 100644
index 0000000..968589f
--- /dev/null
+++ b/src/main/java/com/qgs/dc/mq/secsgem/Main.java
@@ -0,0 +1,56 @@
+package com.qgs.dc.mq.secsgem;
+
+
+
+
+import com.alibaba.fastjson.JSONObject;
+import com.qgs.dc.mq.entity.MQMessage;
+import com.qgs.dc.mq.entity.common.Header;
+import com.qgs.dc.mq.entity.common.Returns;
+import com.qgs.dc.mq.entity.specificBody.QueryEQStatusBody;
+import com.qgs.dc.common.utils.CommonFunction;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * @Desc: ""
+ * @Author: caixiang
+ * @DATE: 2021/8/17 14:35
+ */
+public class Main {
+
+ public static void main(String[] args) throws Exception {
+
+ new Thread(()->{
+ MQMessage mqMessage = new MQMessage();
+
+ Header header = new Header("Request","Execute","QUERYEQPStatus","12");
+ QueryEQStatusBody queryEQStatusBody = new QueryEQStatusBody();
+ queryEQStatusBody.setVidType("u4");
+ List vids = new ArrayList<>();
+ vids.add("10000");
+ vids.add("10001");
+ vids.add("10002");
+ queryEQStatusBody.setVidList(vids);
+ mqMessage.setBody(JSONObject.toJSONBytes(queryEQStatusBody));
+ mqMessage.setHeader(header);
+
+ AsyncFuture add = SendedList.add(header.getTransactionId(),mqMessage);
+ try {
+ MQMessage mqMessageResponse = add.get(new Long(3000));
+ System.out.println(mqMessageResponse.getReturns().getReturnCode());
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }).start();
+
+ Thread.sleep(1000);
+
+ new Thread(()->{
+ MQMessage mqMessage = new MQMessage();
+ mqMessage.setReturns(new Returns("233","2"));
+ SendedList.set("11221",mqMessage);
+ }).start();
+ }
+}
diff --git a/src/main/java/com/qgs/dc/mq/secsgem/SendedList.java b/src/main/java/com/qgs/dc/mq/secsgem/SendedList.java
new file mode 100644
index 0000000..f327821
--- /dev/null
+++ b/src/main/java/com/qgs/dc/mq/secsgem/SendedList.java
@@ -0,0 +1,42 @@
+package com.qgs.dc.mq.secsgem;
+
+
+
+
+import com.qgs.dc.mq.entity.MQMessage;
+
+import java.util.HashMap;
+
+/**
+ * @Desc: "MES端 发送远程指令列表"
+ * @Author: caixiang
+ * @DATE: 2021/8/17 14:14
+ */
+public class SendedList {
+ private static HashMap> list = new HashMap<>();
+
+// public static synchronized AsyncFuture get(String transitionId){
+// return list.get(transitionId);
+// }
+// public static synchronized void put(String transitionId,AsyncFuture asyncFuture){
+// list.put(transitionId,asyncFuture);
+// }
+
+ public static synchronized AsyncFuture add(String transitionId, MQMessage mqMessageRequest) {
+
+ AsyncFuture objectAsyncFuture = new AsyncFuture<>(mqMessageRequest);
+ list.put(transitionId,objectAsyncFuture);
+ return objectAsyncFuture;
+ }
+
+ public static synchronized Boolean set(String transitionId, MQMessage message) {
+ AsyncFuture mqMessageAsyncFuture = list.get(transitionId);
+ if(mqMessageAsyncFuture == null){
+ return false;
+ }
+ mqMessageAsyncFuture.done(message);
+ //清除 ,防止这个hashMap过大。
+ list.remove(transitionId);
+ return true;
+ }
+}
diff --git a/src/main/java/com/qgs/dc/opcua/Consumer/EventReceivedCallBack.java b/src/main/java/com/qgs/dc/opcua/Consumer/EventReceivedCallBack.java
new file mode 100644
index 0000000..bbda18e
--- /dev/null
+++ b/src/main/java/com/qgs/dc/opcua/Consumer/EventReceivedCallBack.java
@@ -0,0 +1,33 @@
+package com.qgs.dc.opcua.Consumer;
+
+import org.eclipse.milo.opcua.sdk.client.api.subscriptions.UaMonitoredItem;
+import org.eclipse.milo.opcua.stack.core.types.builtin.DataValue;
+import org.eclipse.milo.opcua.stack.core.types.builtin.Variant;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.stereotype.Component;
+
+/**
+ * @Desc: ""
+ * @Author: caixiang
+ * @DATE: 2021/8/18 14:32
+ */
+@Component
+public class EventReceivedCallBack implements UaMonitoredItem.EventConsumer,UaMonitoredItem.ValueConsumer {
+ private static final Logger logger = LoggerFactory.getLogger(EventReceivedCallBack.class);
+
+
+ @Override
+ public void onEventArrived(UaMonitoredItem uaMonitoredItem, Variant[] variants) {
+ logger.info("Server Event Received from {}", uaMonitoredItem.getReadValueId().getNodeId());
+ //这里variants 就是 0,2253 这个事件的 变量
+ for (int i = 0; i < variants.length; i++) {
+ logger.info("\t variant[{}]: {}", i, variants[i].getValue());
+ }
+ }
+
+ @Override
+ public void onValueArrived(UaMonitoredItem item, DataValue dataValue) {
+
+ }
+}
diff --git a/src/main/java/com/qgs/dc/opcua/arg/AddPLCArgEntity.java b/src/main/java/com/qgs/dc/opcua/arg/AddPLCArgEntity.java
new file mode 100644
index 0000000..d05da03
--- /dev/null
+++ b/src/main/java/com/qgs/dc/opcua/arg/AddPLCArgEntity.java
@@ -0,0 +1,63 @@
+package com.qgs.dc.opcua.arg;
+
+/**
+ * @Desc: ""
+ * @Author: caixiang
+ * @DATE: 2020/7/30 11:05
+ */
+public class AddPLCArgEntity {
+ private String plcName;
+ private String urlConfig;
+ private String policyConfig;
+ private String userConfigs;
+ private String ip;
+ private Integer messageMode;
+
+ public String getPlcName() {
+ return plcName;
+ }
+
+ public void setPlcName(String plcName) {
+ this.plcName = plcName;
+ }
+
+ public String getUrlConfig() {
+ return urlConfig;
+ }
+
+ public void setUrlConfig(String urlConfig) {
+ this.urlConfig = urlConfig;
+ }
+
+ public String getPolicyConfig() {
+ return policyConfig;
+ }
+
+ public void setPolicyConfig(String policyConfig) {
+ this.policyConfig = policyConfig;
+ }
+
+ public String getUserConfigs() {
+ return userConfigs;
+ }
+
+ public void setUserConfigs(String userConfigs) {
+ this.userConfigs = userConfigs;
+ }
+
+ public String getIp() {
+ return ip;
+ }
+
+ public void setIp(String ip) {
+ this.ip = ip;
+ }
+
+ public Integer getMessageMode() {
+ return messageMode;
+ }
+
+ public void setMessageMode(Integer messageMode) {
+ this.messageMode = messageMode;
+ }
+}
diff --git a/src/main/java/com/qgs/dc/opcua/arg/BrowsArgEntity.java b/src/main/java/com/qgs/dc/opcua/arg/BrowsArgEntity.java
new file mode 100644
index 0000000..f17fcf6
--- /dev/null
+++ b/src/main/java/com/qgs/dc/opcua/arg/BrowsArgEntity.java
@@ -0,0 +1,39 @@
+package com.qgs.dc.opcua.arg;
+
+/**
+ * @Desc: ""
+ * @Author: caixiang
+ * @DATE: 2020/7/30 11:02
+ */
+public class BrowsArgEntity {
+ private String plcName;
+ //例子: ns=5;s=StaticVariables
+ //rootNameSpace == ns
+ private Integer rootNameSpace;
+ //idenrifier == s
+ private String idenrifier;
+
+ public String getPlcName() {
+ return plcName;
+ }
+
+ public void setPlcName(String plcName) {
+ this.plcName = plcName;
+ }
+
+ public Integer getRootNameSpace() {
+ return rootNameSpace;
+ }
+
+ public void setRootNameSpace(Integer rootNameSpace) {
+ this.rootNameSpace = rootNameSpace;
+ }
+
+ public String getIdenrifier() {
+ return idenrifier;
+ }
+
+ public void setIdenrifier(String idenrifier) {
+ this.idenrifier = idenrifier;
+ }
+}
diff --git a/src/main/java/com/qgs/dc/opcua/arg/DelPlcArgEntity.java b/src/main/java/com/qgs/dc/opcua/arg/DelPlcArgEntity.java
new file mode 100644
index 0000000..9a2b12d
--- /dev/null
+++ b/src/main/java/com/qgs/dc/opcua/arg/DelPlcArgEntity.java
@@ -0,0 +1,18 @@
+package com.qgs.dc.opcua.arg;
+
+/**
+ * @Desc: ""
+ * @Author: caixiang
+ * @DATE: 2021/8/19 15:49
+ */
+public class DelPlcArgEntity {
+ private String plcName;
+
+ public String getPlcName() {
+ return plcName;
+ }
+
+ public void setPlcName(String plcName) {
+ this.plcName = plcName;
+ }
+}
diff --git a/src/main/java/com/qgs/dc/opcua/arg/GetCurrentSubArgEntity.java b/src/main/java/com/qgs/dc/opcua/arg/GetCurrentSubArgEntity.java
new file mode 100644
index 0000000..65bb7fc
--- /dev/null
+++ b/src/main/java/com/qgs/dc/opcua/arg/GetCurrentSubArgEntity.java
@@ -0,0 +1,18 @@
+package com.qgs.dc.opcua.arg;
+
+/**
+ * @Desc: ""
+ * @Author: caixiang
+ * @DATE: 2021/8/20 9:52
+ */
+public class GetCurrentSubArgEntity {
+ private String plcName;
+
+ public String getPlcName() {
+ return plcName;
+ }
+
+ public void setPlcName(String plcName) {
+ this.plcName = plcName;
+ }
+}
diff --git a/src/main/java/com/qgs/dc/opcua/arg/ReadArgEntity.java b/src/main/java/com/qgs/dc/opcua/arg/ReadArgEntity.java
new file mode 100644
index 0000000..883a997
--- /dev/null
+++ b/src/main/java/com/qgs/dc/opcua/arg/ReadArgEntity.java
@@ -0,0 +1,36 @@
+package com.qgs.dc.opcua.arg;
+
+/**
+ * @Desc: ""
+ * @Author: caixiang
+ * @DATE: 2020/7/30 10:22
+ */
+public class ReadArgEntity {
+ private Integer nameSpace;
+ private String identifier;
+ private String plcName;
+
+ public Integer getNameSpace() {
+ return nameSpace;
+ }
+
+ public void setNameSpace(Integer nameSpace) {
+ this.nameSpace = nameSpace;
+ }
+
+ public String getIdentifier() {
+ return identifier;
+ }
+
+ public void setIdentifier(String identifier) {
+ this.identifier = identifier;
+ }
+
+ public String getPlcName() {
+ return plcName;
+ }
+
+ public void setPlcName(String plcName) {
+ this.plcName = plcName;
+ }
+}
diff --git a/src/main/java/com/qgs/dc/opcua/arg/WriteArgEntity.java b/src/main/java/com/qgs/dc/opcua/arg/WriteArgEntity.java
new file mode 100644
index 0000000..b22a32b
--- /dev/null
+++ b/src/main/java/com/qgs/dc/opcua/arg/WriteArgEntity.java
@@ -0,0 +1,54 @@
+package com.qgs.dc.opcua.arg;
+
+/**
+ * @Desc: ""
+ * @Author: caixiang
+ * @DATE: 2020/7/30 10:43
+ */
+public class WriteArgEntity {
+ private Integer nameSpace;
+ private String identifier;
+ private String newValue;
+ private String type;
+ private String plcName;
+
+ public Integer getNameSpace() {
+ return nameSpace;
+ }
+
+ public void setNameSpace(Integer nameSpace) {
+ this.nameSpace = nameSpace;
+ }
+
+ public String getIdentifier() {
+ return identifier;
+ }
+
+ public void setIdentifier(String identifier) {
+ this.identifier = identifier;
+ }
+
+ public String getNewValue() {
+ return newValue;
+ }
+
+ public void setNewValue(String newValue) {
+ this.newValue = newValue;
+ }
+
+ public String getType() {
+ return type;
+ }
+
+ public void setType(String type) {
+ this.type = type;
+ }
+
+ public String getPlcName() {
+ return plcName;
+ }
+
+ public void setPlcName(String plcName) {
+ this.plcName = plcName;
+ }
+}
diff --git a/src/main/java/com/qgs/dc/opcua/config/LocalMulPLCConfig.java b/src/main/java/com/qgs/dc/opcua/config/LocalMulPLCConfig.java
new file mode 100644
index 0000000..df3499f
--- /dev/null
+++ b/src/main/java/com/qgs/dc/opcua/config/LocalMulPLCConfig.java
@@ -0,0 +1,80 @@
+package com.qgs.dc.opcua.config;
+import com.alibaba.fastjson.JSON;
+import com.alibaba.fastjson.JSONArray;
+import com.alibaba.fastjson.JSONObject;
+import com.qgs.dc.common.utils.CommonFunction;
+import com.qgs.dc.opcua.constant.PLCConstant;
+import org.eclipse.milo.opcua.stack.core.security.SecurityPolicy;
+
+import java.io.*;
+import java.nio.charset.StandardCharsets;
+import java.util.HashMap;
+
+
+public class LocalMulPLCConfig {
+
+ //读取json文件
+ public static String readJsonFile(String fileName) {
+ String jsonStr = "";
+ try {
+ File jsonFile = new File(fileName);
+ if (!jsonFile.exists()) {
+ boolean b= jsonFile.createNewFile();
+ JSONObject json = new JSONObject();
+ json =JSON.parseObject("{\"config\": []}");
+ FileWriter fw = new FileWriter(jsonFile.getAbsoluteFile());
+ BufferedWriter bw = new BufferedWriter(fw);
+ json.writeJSONString(bw);
+ bw.close();
+ }
+
+ FileReader fileReader = new FileReader(jsonFile);
+ Reader reader = new InputStreamReader(new FileInputStream(jsonFile), StandardCharsets.UTF_8);
+ int ch = 0;
+ StringBuffer sb = new StringBuffer();
+ while ((ch = reader.read()) != -1) {
+ sb.append((char) ch);
+ }
+ fileReader.close();
+ reader.close();
+ jsonStr = sb.toString();
+ return jsonStr;
+ } catch (IOException e) {
+ e.printStackTrace();
+ return null;
+ }
+ }
+
+
+
+ public static HashMap getPLCConfig(){
+ //String path = LocalMulPLCConfig.class.getClassLoader().getResource("mulPLCConfig.json").getPath();
+ String s = "";
+ if(CommonFunction.isLinux()){
+ CommonFunction.createDirIfNotExit(PLCConstant.localURLDirForLinux);
+ s = readJsonFile(PLCConstant.localURLForLinux);
+ }else if(CommonFunction.isWindows()){
+ CommonFunction.createDirIfNotExit(PLCConstant.localURLDirForWindows);
+ s = readJsonFile(PLCConstant.localURLForWindows);
+ }
+
+ JSONObject jobj = JSON.parseObject(s);
+
+
+
+ JSONArray movies = jobj.getJSONArray("config");//构建JSONArray数组
+
+ HashMap res = new HashMap<>();
+ for (int i = 0 ; i < movies.size();i++){
+ JSONObject key = (JSONObject)movies.get(i);
+ String plcName = (String)key.get("plc_name");
+ String url_config = (String)key.get("endpointUrl_config");
+ String policy_config = (String)key.get("securityPolicy_config");
+ String[] user_config =((String)key.get("identityProvider_config")).isEmpty()?null:((String)key.get("identityProvider_config")).split(",");
+ String ip = (String)key.get("ip");
+ Integer messageMode = (Integer)key.get("messageMode_config");
+ res.put(plcName,new PLCConfig(plcName,url_config, SecurityPolicy.valueOf(policy_config),user_config,ip,messageMode));
+ }
+ return res;
+ }
+}
diff --git a/src/main/java/com/qgs/dc/opcua/config/PLCConfig.java b/src/main/java/com/qgs/dc/opcua/config/PLCConfig.java
new file mode 100644
index 0000000..c19003a
--- /dev/null
+++ b/src/main/java/com/qgs/dc/opcua/config/PLCConfig.java
@@ -0,0 +1,70 @@
+package com.qgs.dc.opcua.config;
+
+import org.eclipse.milo.opcua.stack.core.security.SecurityPolicy;
+
+public class PLCConfig {
+ private String plcName;
+ private String endpointUrlConfig;
+ private SecurityPolicy securityPolicyConfig;
+ private String[] identityProviderConfig;
+ private String ip;
+ //None Or SignAndEncrypt
+ private Integer messageMode;
+
+ public PLCConfig(String plcName,String endpointUrlConfig, SecurityPolicy securityPolicyConfig, String[] identityProviderConfig,String ip,Integer messageMode) {
+ this.plcName = plcName;
+ this.endpointUrlConfig = endpointUrlConfig;
+ this.securityPolicyConfig = securityPolicyConfig;
+ this.identityProviderConfig = identityProviderConfig;
+ this.ip = ip;
+ this.messageMode = messageMode;
+ }
+
+ public Integer getMessageMode() {
+ return messageMode;
+ }
+
+ public void setMessageMode(Integer messageMode) {
+ this.messageMode = messageMode;
+ }
+
+ public String getIp() {
+ return ip;
+ }
+
+ public void setIp(String ip) {
+ this.ip = ip;
+ }
+
+ public String getEndpointUrlConfig() {
+ return endpointUrlConfig;
+ }
+
+ public String getPlcName() {
+ return plcName;
+ }
+
+ public void setPlcName(String plcName) {
+ this.plcName = plcName;
+ }
+
+ public void setEndpointUrlConfig(String endpointUrlConfig) {
+ this.endpointUrlConfig = endpointUrlConfig;
+ }
+
+ public SecurityPolicy getSecurityPolicyConfig() {
+ return securityPolicyConfig;
+ }
+
+ public void setSecurityPolicyConfig(SecurityPolicy securityPolicyConfig) {
+ this.securityPolicyConfig = securityPolicyConfig;
+ }
+
+ public String[] getIdentityProviderConfig() {
+ return identityProviderConfig;
+ }
+
+ public void setIdentityProviderConfig(String[] identityProviderConfig) {
+ this.identityProviderConfig = identityProviderConfig;
+ }
+}
diff --git a/src/main/java/com/qgs/dc/opcua/constant/PLCConstant.java b/src/main/java/com/qgs/dc/opcua/constant/PLCConstant.java
new file mode 100644
index 0000000..f40dd4a
--- /dev/null
+++ b/src/main/java/com/qgs/dc/opcua/constant/PLCConstant.java
@@ -0,0 +1,38 @@
+package com.qgs.dc.opcua.constant;
+
+public class PLCConstant {
+ //配置多PLC 的 配置文件 ( 注意如果要在 新服务器上部署 没有文件结构要配置文件结构)
+ public static final String localURLForWindows = "C:\\code\\study\\mulPLC\\mulPLCConfig.json";
+ public static final String localURLDirForWindows = "C:\\code\\study\\mulPLC";
+ public static final String localURLForLinux = "/usr/local/security/mulPLCConfig.json";
+ public static final String localURLDirForLinux = "/usr/local/security";
+
+
+ //连接OPC 的授权文件。
+ public static final String securityURLForWindows = "C:\\code\\study\\mulPLC\\security";
+ public static final String securityURLForLinux = "/usr/local/security";
+
+
+ //常用线程
+ public static Thread displayThread = new Thread();
+
+
+ //错误常量
+
+ //opc server 未授权证书
+ public static final String SECURITY_CHECKS_FAILED = "Bad_SecurityChecksFailed";
+ //Bad_Timeout 是指断网了(或者是网络波荡太久) 然后造成 连接超时
+ public static final String TIMEOUT = "Bad_Timeout";
+ //Bad_ConnectionRejected 是指opc server 宕机了
+ public static final String CONNECTION_REJECTED = "Bad_ConnectionRejected";
+ // 无session 原因:是你部署 程序的的服务器上没有配置 opc-server 的host 要加192.168.0.228 WIN-92SDA5G5VE8
+ public static final String SESSION_CLOSED = "Bad_SessionClosed";
+
+
+ //订阅
+ public static final Integer Subscription_Visited = 1;
+ public static final Integer Subscription_Function_Var = 2;
+ public static final Integer Subscription_Function_Event = 3;
+
+
+}
diff --git a/src/main/java/com/qgs/dc/opcua/constant/PLCTypeConstant.java b/src/main/java/com/qgs/dc/opcua/constant/PLCTypeConstant.java
new file mode 100644
index 0000000..7a7c2e3
--- /dev/null
+++ b/src/main/java/com/qgs/dc/opcua/constant/PLCTypeConstant.java
@@ -0,0 +1,25 @@
+package com.qgs.dc.opcua.constant;
+
+/**
+ * @Desc: ""
+ * @Author: caixiang
+ * @DATE: 2020/7/22 11:31
+ */
+public class PLCTypeConstant {
+ public static final String QUByte = "QUByte";
+ public static final String QUInteger = "QUInteger";
+ public static final String QUShort = "QUShort";
+ public static final String QULong = "QULong";
+ public static final String QBoolean = "QBoolean";
+ public static final String QString = "QString";
+ public static final String QDouble = "QDouble";
+ public static final String QFloat = "QFloat";
+ public static final String QLong = "QLong";
+ public static final String QInteger = "QInteger";
+ public static final String QShort = "QShort";
+ public static final String QArray = "QArray";
+ public static final String QByte = "QByte";
+ public static final String QByteString = "QByteString";
+
+
+}
diff --git a/src/main/java/com/qgs/dc/opcua/controller/OperateController.java b/src/main/java/com/qgs/dc/opcua/controller/OperateController.java
new file mode 100644
index 0000000..4c52d78
--- /dev/null
+++ b/src/main/java/com/qgs/dc/opcua/controller/OperateController.java
@@ -0,0 +1,405 @@
+package com.qgs.dc.opcua.controller;
+
+import com.qgs.dc.opcua.Consumer.EventReceivedCallBack;
+import com.qgs.dc.opcua.arg.*;
+import com.qgs.dc.common.utils.CommonFunction;
+import com.qgs.dc.opcua.constant.PLCConstant;
+import com.qgs.dc.opcua.selfunion.Enum.PLCType;
+import com.qgs.dc.opcua.selfunion.NodeIdKey;
+import com.qgs.dc.opcua.selfunion.UAService;
+import com.qgs.dc.opcua.selfunion.entity.CurrentSubEntity;
+import com.qgs.dc.opcua.selfunion.entity.DelSubscribeEntity;
+import com.qgs.dc.opcua.selfunion.entity.SubscribeEventArgEntity;
+import com.qgs.dc.opcua.selfunion.entity.SubscribeVarArgEntity;
+import com.qgs.dc.common.websocket.WebSocketServer;
+import org.eclipse.milo.opcua.sdk.client.api.subscriptions.UaMonitoredItem;
+import org.eclipse.milo.opcua.stack.core.types.builtin.DataValue;
+import org.eclipse.milo.opcua.stack.core.types.builtin.NodeId;
+import org.eclipse.milo.opcua.stack.core.types.builtin.unsigned.UInteger;
+import org.eclipse.milo.opcua.stack.core.types.builtin.unsigned.UShort;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.*;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+@RestController
+@RequestMapping("/opcua")
+public class OperateController {
+ private static final Logger logger = LoggerFactory.getLogger(OperateController.class);
+
+ //todo controller 需要仔细编写,,重点关注 那些报错异常(断网、opc-server宕机)
+ @Autowired
+ UAService uaService;
+
+ @Autowired
+ WebSocketServer webSocketServer;
+
+ @PostMapping("/addThisPlc")
+ /**
+ * @Description
+ * @Param [
+ * plcName, plc3
+ * urlConfig, opc.tcp://WIN-92SDA5G5VE8:55101 //换成ip就行,如果这样要配置域名解析
+ * policyConfig, None //None,Basic256,Basic128Rsa15
+ * userConfigs, "" //如果无账号密码填空,如果有 账号密码逗号隔开 如 CXCX,123456789
+ * ip, WIN-92SDA5G5VE8
+ * messageMode 1 //None(1),Sign(2),SignAndEncrypt(3);
+ * ]
+ * @Return com.qgs.dc.opcua.opcuamilnow.controller.R
+ * @Author caixiang
+ * @Date 2020/7/28 11:15
+ **/
+ public R addThisPlc(@RequestBody AddPLCArgEntity addPLCArgEntity){
+ try {
+ Integer integer = uaService.dynamicAddPlc(addPLCArgEntity.getPlcName(), addPLCArgEntity.getUrlConfig(), addPLCArgEntity.getPolicyConfig(),addPLCArgEntity.getUserConfigs() , addPLCArgEntity.getIp(),addPLCArgEntity.getMessageMode());
+ return R.ok().put("result",integer);
+ }catch (Exception e){
+ String s = uaService.extractError(e.getMessage());
+ s = s+";详细:"+e.getMessage();
+ return R.error().put("result",s);
+ }
+ }
+
+ public void unlinkSubscribeWhenRemovePlc(String plcName) throws Exception {
+ //并且把 这个plc 已订阅的 变量 给删除 -- 开始
+ List currentSubscribeVarForVisited = uaService.getCurrentSubscribeVarForVisited(plcName);
+ List ns = new ArrayList<>();
+ List iden = new ArrayList<>();
+ for(CurrentSubEntity currentSubEntity:currentSubscribeVarForVisited){
+ ns.add(currentSubEntity.getNameSpace());
+ iden.add(currentSubEntity.getIdentifier());
+ }
+ uaService.deSubscribeForVisit(ns, iden, plcName);
+ //并且把 这个plc 已订阅的 变量 给删除 -- 结束
+ }
+
+ @DeleteMapping("/removeThisPlc")
+ public R removeThisPlc(@RequestBody DelPlcArgEntity delPlcArgEntity){
+ //return R.ok().put("status",uaService.dynamicRemovePlc(plcName));
+ try {
+ Integer integer = uaService.dynamicRemovePlc(delPlcArgEntity.getPlcName());
+
+ unlinkSubscribeWhenRemovePlc(delPlcArgEntity.getPlcName());
+ if(integer == null){
+ return R.error().put("result",delPlcArgEntity.getPlcName()+" 这台plc不存在 / 或参数异常");
+ }
+ return R.ok().put("result",integer);
+ }catch (Exception e){
+ String s = uaService.extractError(e.getMessage());
+ return R.error().put("result",s);
+ }
+ }
+
+ @GetMapping("/getBrows")
+ public R getBrows(@RequestBody BrowsArgEntity browsArgEntity){
+ NodeIdKey nodeIdKey = uaService.browseA(browsArgEntity.getPlcName(), browsArgEntity.getRootNameSpace(), browsArgEntity.getIdenrifier());
+ if(nodeIdKey == null){
+ return R.error().put("result",browsArgEntity.getPlcName()+" 这台plc不存在 / 或参数异常");
+ }
+ return R.ok().put("result",nodeIdKey);
+ }
+
+ //如果是数组类型 的那么 newValue 就是 一个字符串 并且用 逗号隔开,后面跟着 数据类型 如 "1,2,4|QUByte"
+ /**
+ * 参数
+ * {
+ * "nameSpace": 6,
+ * "identifier": "S7-1200 station_1.PLC_1.TestDB80.Array[0..7] of DInt1",
+ * "newValue": "1,2,3,4,5,6,7,8#QInteger",
+ * "type": "QArray",
+ * "plcName": "plc1"
+ * }
+ *
+ * */
+ @PostMapping("/write")
+ //public R write(Integer nameSpace,String identifier,String newValue,String type,String plcName){
+ public R write(@RequestBody WriteArgEntity writeArgEntity){
+ try {
+ //PLCType.valueOf(type).convertType(newValue); 意思是 把newValue 这个数据转成type 类型的变量
+ Object var = PLCType.valueOf(writeArgEntity.getType()).convertType(writeArgEntity.getNewValue());
+ Boolean aBoolean = uaService.setValue(writeArgEntity.getNameSpace(), writeArgEntity.getIdentifier(), var, writeArgEntity.getPlcName());
+ if(aBoolean == null){
+ return R.error().put("result",writeArgEntity.getPlcName()+" 这台plc不存在 / 或参数异常");
+ }else if(aBoolean){
+ return R.ok();
+ }else {
+ return R.error();
+ }
+ }catch (Exception e){
+ return R.error().put("result", e.getMessage());
+ }
+
+ }
+
+ @GetMapping("/read")
+ //public R read(Integer nameSpace,String identifier,String plcName) {
+ public R read(@RequestBody ReadArgEntity readArgEntity) {
+ try {
+ DataValue dv = uaService.getValue(readArgEntity.getNameSpace(), readArgEntity.getIdentifier(), readArgEntity.getPlcName());
+ if(dv == null){
+ return R.error().put("result",readArgEntity.getPlcName()+" 这台plc不存在 / 或参数异常");
+ }
+ Object value = dv.getValue().getValue();
+ System.out.println(CommonFunction.judgeVarType(value));
+ return R.ok().put("result", CommonFunction.var(value));
+ }catch (Exception e){
+ return R.error().put("result", e.getMessage());
+
+ }
+
+ }
+
+ //todo 1.订阅变量数组...(并且把数据类型返还给前端)
+ /**
+ * 含义 : 可以同时订阅:变量、数组 ( 注意这个是功能型订阅 )
+ * 参数 : SubscribeArgEntity
+ * listNameSpace List
+ * listIdentifier List
+ * plcName String
+ * 注意 : ns 和 identify 必须位置上相互对应,,判断是否数组依据就是看几组数据
+ * 返回:
+ * 这边是通过websocket 返回的 是一个字符串
+ * 第一组 是变量名
+ * 第二组 是变量值
+ * 第三组 是变量值 的类型(如果是数组 那就是QArray)
+ * 第四组 是变量值 的类型(如果是数组 那这组就存在,代表数组里面变量的数据类型)
+ * 3,Byte|8,7,7,7,8|QArray|QShort
+ * 3,Byte|0|QUByte
+ * */
+ @PostMapping("subscribeVarForFunction")
+ public R subscribeVarForFunction(@RequestBody SubscribeVarArgEntity subscribeVarArgEntity) throws Exception {
+
+ Integer integer = uaService.subscribeValues(subscribeVarArgEntity.getListNameSpace(), subscribeVarArgEntity.getListIdentifier(), new Double(1000),
+ (item, dataValue) -> {
+
+ System.err.println("(测试是否 每隔intervel 都会执行这个回调方法) subscription value received: item:NodeId : " +
+ "" + item.getReadValueId().getNodeId() + ", value :" + dataValue.getValue());
+ System.out.println();
+ NodeId currentNode = item.getReadValueId().getNodeId();
+ UShort namespaceIndex = currentNode.getNamespaceIndex();
+ Object identifier = currentNode.getIdentifier();
+
+ Object value = dataValue.getValue().getValue();
+ String varType = CommonFunction.judgeVarType(value);
+
+ String res = namespaceIndex + "," + identifier + "|" + CommonFunction.var2String(value) + "|" + varType+ "|" +subscribeVarArgEntity.getPlcName();
+
+ try {
+ webSocketServer.sendtoAll(res);
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }, subscribeVarArgEntity.getPlcName(),PLCConstant.Subscription_Function_Var);
+
+ if(integer == null){
+ return R.error().put("result",subscribeVarArgEntity.getPlcName()+" 这台plc不存在 / 或参数异常");
+ }
+ return R.ok().put("result","ok");
+ }
+
+ /**
+ * 含义 : 订阅变量,界面展示用用
+ * 参数 : SubscribeArgEntity
+ * listNameSpace List
+ * listIdentifier List
+ * plcName String
+ * 注意 : ns 和 identify 必须位置上相互对应,,判断是否数组依据就是看几组数据
+ * 返回:
+ * 1 <===> 你要订阅的这个Node 订阅成功(包括这个变量已存在 然后你再次去订阅)
+ * -1 <===> 你要订阅的这个Node 订阅失败
+ * 2 <===> 你要订阅的这个Node 已订阅,请勿重复订阅
+ * websocket 传递格式:
+ * 这边是通过websocket 返回的 是一个字符串
+ * 第一组 是变量名
+ * 第二组 是变量值
+ * 第三组 是变量值 的类型(如果是数组 那就是QArray)
+ * 第四组 是变量值 的类型(如果是数组 那这组就存在,代表数组里面变量的数据类型)
+ * 第五组 是这个变量所属 的plc,,是哪个plc
+ * 3,Byte|8,7,7,7,8|QArray|QShort|plcName
+ * 3,Byte|0|QUByte|plcName
+ * */
+ @PostMapping("subscribeVarForVisit")
+ public R subscribeVarForVisit(@RequestBody SubscribeVarArgEntity subscribeVarArgEntity){
+
+ try {
+ Integer integer = uaService.subscribeForVisit(subscribeVarArgEntity.getListNameSpace(), subscribeVarArgEntity.getListIdentifier(), new Double(1000), (item, dataValue) -> {
+ UInteger attributeId = item.getReadValueId().getAttributeId();
+
+ System.err.println("(测试是否 每隔intervel 都会执行这个回调方法) subscription value received: item:NodeId : " +
+ "" + item.getReadValueId().getNodeId() + ", value :" + dataValue.getValue()+",attributeId:"+attributeId);
+
+ NodeId currentNode = item.getReadValueId().getNodeId();
+ UShort namespaceIndex = currentNode.getNamespaceIndex();
+ Object identifier = currentNode.getIdentifier();
+ Object value = dataValue.getValue().getValue();
+ String varType = CommonFunction.judgeVarType(value);
+
+ String res = namespaceIndex + "," + identifier + "|" + CommonFunction.var2String(value) + "|" + varType+ "|" +subscribeVarArgEntity.getPlcName();
+
+ try {
+ webSocketServer.sendtoAll(res);
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }, subscribeVarArgEntity.getPlcName());
+ if(integer == null){
+ return R.error().put("result",subscribeVarArgEntity.getPlcName()+" 这台plc不存在 / 或参数异常");
+ }
+
+ if(integer == 1){
+ return R.ok().put("result","成功");
+ }else if(integer == 2) {
+ return R.error().put("result","你要订阅的这个Node 已订阅,请勿重复订阅");
+ }else {
+ return R.error().put("result","你要订阅的这个Node 订阅失败");
+ }
+ }catch (Exception e){
+ return R.error().put("result",e.getMessage());
+ }
+ }
+ /**
+ * 含义 : 订阅一个或者多个事件
+ * 参数 : SubscribeArgEntity
+ * listNameSpace List
+ * listIdentifier List
+ * plcName String
+ * 注意 : ns 和 identify 必须位置上相互对应,,判断是否数组依据就是看几组数据
+ * 返回:
+ * 把事件触发 时候的一些数据 拿过来(通过过滤器过滤过来) 然后传给前端。
+ * status: 暂停(等待事件 验证好了之后 再做这些)
+ * */
+ @PostMapping("subscribeEventsForFunction")
+ public R subscribeEventsForFunction(@RequestBody SubscribeEventArgEntity subscribeVarArgEntity) throws Exception {
+ if(subscribeVarArgEntity.getListNameSpace().size()!=subscribeVarArgEntity.getListIdentifier().size()){
+ return R.error("传入参数不正确");
+ }
+ List list = new ArrayList<>();
+ int size = subscribeVarArgEntity.getListNameSpace().size();
+ UaMonitoredItem.EventConsumer a1 =((uaMonitoredItem, variants) -> {
+ logger.info("Server Event Received from {}", uaMonitoredItem.getReadValueId().getNodeId());
+ //这里variants 就是 0,2253 这个事件的 变量
+ for (int i = 0; i < variants.length; i++) {
+ logger.info("\t variant[{}]: {}", i, variants[i].getValue());
+ }
+ });
+ UaMonitoredItem.EventConsumer a2 =((uaMonitoredItem, variants) -> {
+ logger.info("myDevice Event Received from {}", uaMonitoredItem.getReadValueId().getNodeId());
+ //这里variants 就是 0,2253 这个事件的 变量
+ for (int i = 0; i < variants.length; i++) {
+ logger.info("\t variant[{}]: {}", i, variants[i].getValue());
+ }
+ });
+ UaMonitoredItem.EventConsumer a3 = new EventReceivedCallBack();
+
+ for(int i=0;i(包括事件 和变量 )
+ * 参数:
+ * 1.plcName
+ * 2.你要取消订阅 事件的 nameSpace 和 identifier
+ * 3.可以选择你要过滤的条件(现在暂时无,,后续可以补充)
+ * 4.设置回调函数(就是当有订阅 的事件发生的时候 就调用的函数 )
+ * 5.注意type 是 你要删除的订阅是在哪个(1 == 代表 展示型订阅;;2 == 代表 功能型(变量) 订阅;;3 == 代表 功能型(事件) 订阅)
+ * 返回:Integer
+ * 1 <===> 代表 删除订阅的事件 成功
+ * -2 <===> 代表 你要删除的订阅事件 不存在
+ * -1 <===> 代表 删除订阅失败/参数错误
+ * 如果有异常就直接抛出异常
+ * null 代表选中的plc异常
+ *
+ * 注意:如果之前订阅是 批量订阅的额(也就是个List),在delSubscribe的时候可以分开取消订阅。
+ *
+ * */
+ @PostMapping("delSubscribe")
+ public R delSubscribe(@RequestBody DelSubscribeEntity delSubscribeEntity) throws Exception {
+ Integer integer = uaService.deleteSubscribe(delSubscribeEntity.getPlcName(), delSubscribeEntity.getListNameSpace(), delSubscribeEntity.getListIdentifier(),delSubscribeEntity.getType());
+ if(integer == null){
+ return R.error().put("result",delSubscribeEntity.getPlcName()+" 这台plc不存在 / 或参数异常");
+ }
+ return R.ok().put("result",integer);
+ }
+
+ /**
+ * 含义:删除某个已订阅的变量
+ * 注意:
+ * 1.
+ * 参数:
+ * 回调函数BiConsumer,是当你订阅的这个变量当变量发生 改变的时候 你执行的方法(刚开始 会执行一次)
+ * 返回值:
+ * 1 <===> 你要 取消订阅的这个Node 成功(包括这个变量已存在 然后你再次去订阅)
+ * -1 <===> 你要 取消订阅的这个Node 失败
+ * 2 <===> 你要 取消订阅的这个Node 不存在
+ * 有异常直接抛出
+ * null 代表选中的plc不存在
+ *
+ * tip:Subscription有两种模式,一种是Reporting,另一种是Sampling。
+ * 如果定义为Sampling,则这个Subscription是一个Triggered Item,即被激发的订阅,需要一个定义为Reporting的Subscription(称为Triggering Item)
+ * 与它连接。这样当Triggering Item更新时,会激发Triggered Item更新。
+ *
+ * */
+ @PostMapping("deSubscribeVarForVisit")
+ public R deSubscribeVarForVisit(@RequestBody SubscribeVarArgEntity subscribeVarArgEntity) {
+
+ try {
+ Integer integer = uaService.deSubscribeForVisit(subscribeVarArgEntity.getListNameSpace() , subscribeVarArgEntity.getListIdentifier() , subscribeVarArgEntity.getPlcName());
+ if(integer == null){
+ return R.error().put("result",subscribeVarArgEntity.getPlcName()+" 这台plc不存在 / 或参数异常");
+ }
+ if(integer == 1){
+ return R.ok().put("result","成功");
+ }else if(integer == 2) {
+ return R.error().put("result","取消订阅的这个Node 不存在");
+ }else {
+ return R.error().put("result","取消订阅的这个Node 失败");
+ }
+ }catch (Exception e){
+ return R.error().put("result",e.getMessage());
+ }
+
+ }
+
+ @PostMapping("getCurrentSubscribeVarForVisited")
+ public R getCurrentSubscribeVarForVisited(@RequestBody GetCurrentSubArgEntity currentSubArgEntity) {
+ //todo 测试
+ try {
+ List currentSubscribeVarForVisited = uaService.getCurrentSubscribeVarForVisited(currentSubArgEntity.getPlcName());
+ if(currentSubscribeVarForVisited == null){
+ return R.error().put("result",currentSubArgEntity.getPlcName()+"这台plc不存在 或是 订阅不存在");
+ }
+ return R.ok().put("result",currentSubscribeVarForVisited);
+ }catch (Exception e){
+ return R.ok().put("result",e.getMessage());
+ }
+ }
+
+ @GetMapping("getUniqueWebSocketId")
+ public R getUniqueWebSocketId() throws Exception {
+ String uniqeId = webSocketServer.getUniqeId();
+ return R.ok().put("result",uniqeId);
+ }
+
+
+ @GetMapping("getConnectedPLC")
+ public R getConnectedPLC() {
+ return R.ok().put("result",uaService.getConnectedPLC());
+ }
+
+}
diff --git a/src/main/java/com/qgs/dc/opcua/controller/R.java b/src/main/java/com/qgs/dc/opcua/controller/R.java
new file mode 100644
index 0000000..efec636
--- /dev/null
+++ b/src/main/java/com/qgs/dc/opcua/controller/R.java
@@ -0,0 +1,66 @@
+/**
+ * Copyright (c) 2016-2019 人人开源 All rights reserved.
+ *
+ * https://www.renren.io
+ *
+ * 版权所有,侵权必究!
+ */
+
+package com.qgs.dc.opcua.controller;
+
+
+import org.springframework.http.HttpStatus;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * 返回数据
+ *
+ * @author Mark sunlightcs@gmail.com
+ */
+public class R extends HashMap {
+ private static final long serialVersionUID = 1L;
+ //默认成功 是1
+ public R() {
+ put("code", 1);
+ put("msg", "success");
+ }
+
+ public static R error() {
+ return error(HttpStatus.INTERNAL_SERVER_ERROR.value(), "未知异常,请联系管理员");
+ }
+
+ public static R error(String msg) {
+ return error(HttpStatus.INTERNAL_SERVER_ERROR.value(), msg);
+ }
+
+ public static R error(int code, String msg) {
+ R r = new R();
+ r.put("code", code);
+ r.put("msg", msg);
+ return r;
+ }
+
+ public static R ok(String msg) {
+ R r = new R();
+ r.put("msg", msg);
+ return r;
+ }
+
+ public static R ok(Map map) {
+ R r = new R();
+ r.putAll(map);
+ return r;
+ }
+
+ public static R ok() {
+ return new R();
+ }
+
+ @Override
+ public R put(String key, Object value) {
+ super.put(key, value);
+ return this;
+ }
+}
diff --git a/src/main/java/com/qgs/dc/opcua/controller/WmcController.java b/src/main/java/com/qgs/dc/opcua/controller/WmcController.java
new file mode 100644
index 0000000..96f2b57
--- /dev/null
+++ b/src/main/java/com/qgs/dc/opcua/controller/WmcController.java
@@ -0,0 +1,647 @@
+package com.qgs.dc.opcua.controller;
+
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+/**
+ * @Author: 蔡翔
+ * @Date: 2019/10/12 15:15
+ * @Version 1.0
+ */
+@RestController
+@RequestMapping("/testPLC1")
+public class WmcController {
+/*
+
+
+ private static final Logger logger = LoggerFactory.getLogger(WmcController.class);
+ @Autowired
+ UAService uaService;
+
+ @Autowired
+ WebSocketServer webSocketServer;
+
+ */
+/**
+ * 测试real 类型(get / set)
+ **//*
+
+ @PostMapping("/noRR")
+ public R noRr() throws Exception {
+ return R.ok().put("OK",123);
+ }
+
+ @GetMapping("/norr")
+ public R removeThisPlc() throws Exception {
+ uaService.dynamicRemovePlc("plc1");
+ return R.ok();
+ }
+
+
+ @GetMapping("testWriteOneVar")
+ public R testWriteOneVar(Integer a) throws Exception {
+ System.out.println("uaService.hashCode(): "+uaService.hashCode());
+ Boolean aBoolean = null;
+ try {
+ aBoolean = uaService.setValue(PLCVar.INT32_3, a,"plc1");
+ }catch (Exception e){
+ String[] errMsgs = e.getMessage().split(",");
+ for(String i : errMsgs){
+ System.out.println("i = 》 " + i);
+ }
+ System.err.println("err msg: "+uaService.extractError(e.getMessage()));
+ }
+
+ return R.ok().put("result",aBoolean);
+ }
+ @GetMapping("testWriteOneVarForByte")
+ public R testWriteOneVarForByte(Integer a) throws Exception {
+ System.out.println("uaService.hashCode(): "+uaService.hashCode());
+ Boolean aBoolean = null;
+ try {
+ aBoolean = uaService.setValue(PLCVar.BYTE_3, Unsigned.ubyte(a),"plc1");
+ }catch (Exception e){
+ String[] errMsgs = e.getMessage().split(",");
+ for(String i : errMsgs){
+ System.out.println("i = 》 " + i);
+ }
+ System.err.println("err msg: "+uaService.extractError(e.getMessage()));
+ }
+
+ return R.ok().put("result",aBoolean);
+ }
+
+
+ @GetMapping("testHistoryRead")
+ public R testHistoryRead() throws Exception {
+ System.out.println("uaService.hashCode(): "+uaService.hashCode());
+
+ DateTime start = new DateTime(new Date(System.currentTimeMillis()+10000));
+ DateTime end = new DateTime(new Date());
+
+ DateTime start1 = DateTime.MIN_VALUE;
+ DateTime end1 = DateTime.now();
+
+ List aBoolean = uaService.historyRead("plc1",start,end,5,"Counter1");
+ return R.ok().put("result",aBoolean);
+ }
+
+ @GetMapping("testReadStringArray")
+ public R testReadStringArray() throws Exception {
+ System.out.println("uaService.hashCode(): "+uaService.hashCode());
+
+ DataValue aBoolean = uaService.getValue(PLCVar.StringArray_3,"plc1");
+ List s = (List)aBoolean.getValue().getValue();
+ return R.ok().put("result",aBoolean.getValue().getValue());
+ }
+
+ @GetMapping("testReadBooleanArray")
+ public R testReadBooleanArray() throws Exception {
+ System.out.println("uaService.hashCode(): "+uaService.hashCode());
+ DataValue aBoolean = null;
+ try {
+ aBoolean = uaService.getValue(PLCVar.BooleanArray_3,"plc1");
+ }catch (Exception e){
+ String[] errMsgs = e.getMessage().split(",");
+ for(String i : errMsgs){
+ System.out.println("i = 》 " + i);
+ }
+
+ System.err.println("err msg: "+uaService.extractError(e.getMessage()));
+ if("Bad_ConnectionRejected".equals(uaService.extractError(e.getMessage()))){
+ System.err.println("OPC SERVER 已经宕机 请确认开启");
+ }
+ }
+ System.out.println(aBoolean.getValue().getValue().getClass().isArray());
+ return R.ok().put("result",aBoolean.getValue().getValue());
+ }
+
+ @GetMapping("testWriteTwoVar")
+ public R testWriteTwoVar(Boolean b,Integer a) throws Exception {
+ System.out.println("uaService.hashCode(): "+uaService.hashCode());
+
+ List list = new ArrayList<>();
+ list.add(PLCVar.INT32_3);
+ list.add(PLCVar.Boolean_3);
+
+ List