Initial commit

This commit is contained in:
caixiang 2021-08-27 17:43:55 +08:00
commit e2a4f08c0f
67 changed files with 7875 additions and 0 deletions

33
.gitignore vendored Normal file
View File

@ -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/

118
.mvn/wrapper/MavenWrapperDownloader.java vendored Normal file
View File

@ -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();
}
}

BIN
.mvn/wrapper/maven-wrapper.jar vendored Normal file

Binary file not shown.

2
.mvn/wrapper/maven-wrapper.properties vendored Normal file
View File

@ -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

310
mvnw vendored Normal file
View File

@ -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 "$@"

182
mvnw.cmd vendored Normal file
View File

@ -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%

173
pom.xml Normal file
View File

@ -0,0 +1,173 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.5.4</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.qgs</groupId>
<artifactId>dc</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>dc</name>
<description>data collection</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<!-- OPC UA 模块依赖 开始 -->
<dependency>
<groupId>org.eclipse.milo</groupId>
<artifactId>sdk-client</artifactId>
<version>0.6.3</version>
</dependency>
<dependency>
<groupId>org.eclipse.milo</groupId>
<artifactId>stack-client</artifactId>
<version>0.6.3</version>
</dependency>
<dependency>
<groupId>org.eclipse.milo</groupId>
<artifactId>stack-server</artifactId>
<version>0.6.3</version>
</dependency>
<dependency>
<groupId>org.eclipse.milo</groupId>
<artifactId>sdk-server</artifactId>
<version>0.6.3</version>
</dependency>
<!-- OPC UA 模块依赖 结束 -->
<!-- 日志logback+slf4j 开始 -->
<!-- https://mvnrepository.com/artifact/org.slf4j/slf4j-api -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.32</version>
</dependency>
<!-- https://mvnrepository.com/artifact/ch.qos.logback/logback-core -->
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-core</artifactId>
<version>1.2.5</version>
</dependency>
<!-- https://mvnrepository.com/artifact/ch.qos.logback/logback-classic -->
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.2.5</version>
</dependency>
<!-- 日志 结束 -->
<!-- websocket 开始 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-websocket</artifactId>
</dependency>
<!-- websocket 结束 -->
<!-- fastjson 开始 -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.78</version>
</dependency>
<!-- fastjson 结束 -->
<!-- hutool 开始 -->
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>5.7.9</version>
</dependency>
<!-- hutool 结束 -->
<!-- rabbitmq 依赖 开始 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-amqp</artifactId>
</dependency>
<!-- rabbitmq 依赖 结束 -->
<!-- fastjson 开始 -->
<!-- https://mvnrepository.com/artifact/com.alibaba/fastjson -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.75</version>
</dependency>
<!-- fastjson 结束 -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<!-- jackson依赖 开始 -->
<!-- https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-databind -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.12.2</version>
</dependency>
<!-- https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-core -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>2.12.2</version>
</dependency>
<!-- https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-annotations -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
<version>2.12.2</version>
</dependency>
<!-- jackson依赖 结束 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>2.5.4</version>
<configuration>
<excludes>
<exclude>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</exclude>
</excludes>
</configuration>
</plugin>
</plugins>
</build>
</project>

View File

@ -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);
}
}

View File

@ -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<o.length;i++){
if(i==o.length-1){
res += o[i].toString();
}else {
res += o[i].toString() +",";
}
}
}
return res;
}
return object.toString();
}
public static NodeId initialNodeId(Integer value){
return new NodeId(Unsigned.ushort(0),Unsigned.uint(value));
}
/**
* 字节流数据 ==java对象(MQMessage)
* @param message MQ的message原始对象
* @return
*/
public static MQMessage parse(Message<?> message){
byte[] bytes =(byte[]) message.getPayload();
MQMessage mqMessage = JSONObject.parseObject(bytes, MQMessage.class);
return mqMessage;
}
/**
* 解析body
* JSONObject对象 ==java对象
* @param body body
* @param clazz ResponseBodyRequestBody 里面的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();
}
}

View File

@ -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> T getBean(String name, Class<T> 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);
}
}

View File

@ -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();
}
}

View File

@ -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<String, WebSocketServer> 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 + "发来消息:" + " <br/> " + 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--;
}
}

View File

@ -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);
}

View File

@ -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);
}
}

View File

@ -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);
}
}

View File

@ -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);
}
}

View File

@ -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);
}
}

View File

@ -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<String> 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);
}
}

View File

@ -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);
}
}

View File

@ -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;
}
}

View File

@ -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<String,Object> 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<String,Object> 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<MQMessage> 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());
}
}
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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<String> vidList;
}

View File

@ -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参数的构造方法
}
}

View File

@ -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<String,Object> 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<String,Object> 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<String,Object> 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<String,Object> 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<String,Object> 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
);
}
}

View File

@ -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<String,Object> 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<String> 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<String,Object> 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<String> 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<String> 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();
// }
}

View File

@ -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<MQMessage> implements Future<MQMessage> {
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<Thread> 队列
//当有线程执行notify方法的时候就会往这个队列中取出一个或者多个Thread,取出来以后就能执行后续代码了
System.out.println("get");
// 当线程执行wait()会把当前的锁释放然后让出CPU进入等待状态 wait()会立刻释放synchronizedobj中的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;
}
}
}

View File

@ -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> {
//别人调用我的时候我先给他们返回一个结果
MQMessage get(Long timeout) throws Exception;
}

View File

@ -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<String> vids = new ArrayList<>();
vids.add("10000");
vids.add("10001");
vids.add("10002");
queryEQStatusBody.setVidList(vids);
mqMessage.setBody(JSONObject.toJSONBytes(queryEQStatusBody));
mqMessage.setHeader(header);
AsyncFuture<MQMessage> 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();
}
}

View File

@ -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<String, AsyncFuture<MQMessage>> list = new HashMap<>();
// public static synchronized AsyncFuture<MQMessage> get(String transitionId){
// return list.get(transitionId);
// }
// public static synchronized void put(String transitionId,AsyncFuture<MQMessage> asyncFuture){
// list.put(transitionId,asyncFuture);
// }
public static synchronized AsyncFuture<MQMessage> add(String transitionId, MQMessage mqMessageRequest) {
AsyncFuture<MQMessage> objectAsyncFuture = new AsyncFuture<>(mqMessageRequest);
list.put(transitionId,objectAsyncFuture);
return objectAsyncFuture;
}
public static synchronized Boolean set(String transitionId, MQMessage message) {
AsyncFuture<MQMessage> mqMessageAsyncFuture = list.get(transitionId);
if(mqMessageAsyncFuture == null){
return false;
}
mqMessageAsyncFuture.done(message);
//清除 防止这个hashMap过大
list.remove(transitionId);
return true;
}
}

View File

@ -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) {
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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<String,PLCConfig> 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<String,PLCConfig> 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;
}
}

View File

@ -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;
}
}

View File

@ -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;
}

View File

@ -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";
}

View File

@ -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<CurrentSubEntity> currentSubscribeVarForVisited = uaService.getCurrentSubscribeVarForVisited(plcName);
List<Integer> ns = new ArrayList<>();
List<String> 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<Integer>
* listIdentifier List<String>
* 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<Integer>
* listIdentifier List<String>
* 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<Integer>
* listIdentifier List<String>
* 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<UaMonitoredItem.EventConsumer> 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<size;i++){
list.add(a3);
}
Integer plc1 = uaService.subscribeEvents(subscribeVarArgEntity.getPlcName(), subscribeVarArgEntity.getListNameSpace(), subscribeVarArgEntity.getListIdentifier(),list);
if(plc1 == null){
return R.error().put("result",subscribeVarArgEntity.getPlcName()+" 这台plc不存在 / 或参数异常");
}
return R.ok().put("result",plc1);
}
/* *
* 原因订阅包含一个寿命计数器保存了在没有发布请求时经历的循环次数当达到阈值时会删除这个订阅以及与订阅相关的监控项在删除订阅时会发送一条StateChangeNotification消息并携带状态码Bad_Timeout
* */
/**
* 含义删除 某个订阅分类下 某个Node 或者 List<Node>(包括事件 和变量 )
* 参数
* 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<UaMonitoredItem, DataValue>是当你订阅的这个变量当变量发生 改变的时候 你执行的方法(刚开始 会执行一次)
* 返回值
* 1 <===> 你要 取消订阅的这个Node 成功包括这个变量已存在 然后你再次去订阅
* -1 <===> 你要 取消订阅的这个Node 失败
* 2 <===> 你要 取消订阅的这个Node 不存在
* 有异常直接抛出
* null 代表选中的plc不存在
*
* tipSubscription有两种模式一种是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<CurrentSubEntity> 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());
}
}

View File

@ -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<String, Object> {
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<String, Object> 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;
}
}

View File

@ -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<DataValue> 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<String> s = (List<String>)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<PLCVar> list = new ArrayList<>();
list.add(PLCVar.INT32_3);
list.add(PLCVar.Boolean_3);
List<Object> value = new ArrayList<>();
value.add(a);
value.add(b);
Boolean aBoolean = uaService.setValues(list, value,"plc1");
return R.ok().put("result",aBoolean);
}
@GetMapping("getUniqueId")
public R getUniqueId(){
return R.ok(webSocketServer.getUniqeId());
}
@GetMapping("testReadOneVar")
public R testReadOneVar() throws Exception {
System.out.println("uaService.hashCode(): "+uaService.hashCode());
DataValue dataValue = uaService.getValue(PLCVar.INT32_3,"plc1");
return R.ok().put("result",dataValue.getValue().getValue());
}
@GetMapping("testReadTwoVar")
public R testReadTwoVar() throws Exception {
System.out.println("uaService.hashCode(): "+uaService.hashCode());
List<PLCVar> list = new ArrayList<>();
list.add(PLCVar.Boolean_3);
list.add(PLCVar.INT32_3);
List<DataValue> values = uaService.getValues(list ,"plc1");
List<Object> res = new ArrayList<>();
for(DataValue dv:values){
res.add(dv.getValue().getValue());
}
return R.ok().put("result",res);
}
@GetMapping("subscribeInt32")
public R subscribeInt32() throws Exception {
//只要这个线程还在执行 就会一直未true
boolean alive = PLCConstant.displayThread.isAlive();
uaService.subscribe(PLCVar.INT32_3,new Double(1000),(item,dataValue)->{
System.err.println("(测试是否 每隔intervel 都会执行这个回调方法) subscription value received: item:NodeId : " +
""+item.getReadValueId().getNodeId()+", value :"+ dataValue.getValue());
System.out.println();
Object value = dataValue.getValue().getValue();
try {
webSocketServer.sendtoAll(value.toString());
} catch (IOException e) {
e.printStackTrace();
}
if(value.equals(11)){
System.err.println("subscribeInt32 I am over");
}
},"plc2",1);
return R.ok().put("result","ok");
}
@GetMapping("subscribeByte")
public R subscribeByte() throws Exception {
//只要这个线程还在执行 就会一直未true
boolean alive = PLCConstant.displayThread.isAlive();
uaService.subscribe(PLCVar.BYTE_3,new Double(1000),(item,dataValue)->{
System.err.println("(测试是否 每隔intervel 都会执行这个回调方法) subscription value received: item:NodeId : " +
""+item.getReadValueId().getNodeId()+", value :"+ dataValue.getValue());
System.out.println();
Object value = dataValue.getValue().getValue();
//CommonFunction.judgeVarType(value);
//System.out.println(integer);
try {
webSocketServer.sendtoAll(value.toString());
} catch (IOException e) {
e.printStackTrace();
}
if(value.equals(11)){
System.err.println("subscribeInt32 I am over");
}
},"plc1",1);
return R.ok().put("result","ok");
}
@GetMapping("suspendSubscribeInt32")
public R suspendSubscribeInt32() throws Exception {
final AtomicInteger eventCount = new AtomicInteger(0);
List<Integer> namespace = new ArrayList<>();
namespace.add(3);
List<Object> identify = new ArrayList<>();
identify.add("Int32");
return R.ok().put("result",uaService.suspendSubscribe("plc2",namespace,identify));
}
@GetMapping("delSubscribeInt32")
public R delSubscribeInt32() throws Exception {
//只要这个线程还在执行 就会一直未true
boolean alive = PLCConstant.displayThread.isAlive();
List<Integer> ns = new ArrayList<>();
ns.add(PLCVar.INT32_3.getNameSpace());
List<Object> id = new ArrayList<>();
id.add(PLCVar.INT32_3.getIdentifier());
uaService.deleteSubscribe("plc2",ns,id);
return R.ok().put("result","ok");
}
@GetMapping("subscribeCounter1")
public R subscribeCounter1() throws Exception {
uaService.subscribe(PLCVar.Counter1_5,new Double(1000),(item,dataValue)->{
System.err.println("(测试是否 每隔intervel 都会执行这个回调方法) subscription value received: item:NodeId : " +
""+item.getReadValueId().getNodeId()+", value :"+ dataValue.getValue());
System.out.println();
Object value = dataValue.getValue().getValue();
try {
webSocketServer.sendtoAll(value.toString());
} catch (IOException e) {
e.printStackTrace();
}
if(value.equals(11)){
System.err.println("subscribeInt32 I am over");
}
},"plc1",1);
return R.ok().put("result","ok");
}
@GetMapping("subscribeBoolen")
public R subscribeBoolen() throws Exception {
uaService.subscribe(PLCVar.Boolean_3,new Double(1000),(item,dataValue)->{
System.err.println("(测试是否 每隔intervel 都会执行这个回调方法) subscription value received: item:NodeId : " +
""+item.getReadValueId().getNodeId()+", value :"+ dataValue.getValue());
System.out.println();
Object value = dataValue.getValue().getValue();
//Integer integer = CommonFunction.judgeVarType(value);
//System.out.println(integer);
try {
webSocketServer.sendtoAll(value.toString());
} catch (IOException e) {
e.printStackTrace();
}
if(value.equals(false)){
System.err.println("subscribeBoolen I am over");
}
},"plc1",1);
return R.ok().put("result","ok");
}
@GetMapping("subscribeMultiply")
public R subscribeMultiply() throws Exception {
List<PLCVar> list = new ArrayList<>();
list.add(PLCVar.Boolean_3);
list.add(PLCVar.INT32_3);
uaService.subscribeValues(list,new Double(1000),(item,dataValue)->{
System.err.println("(测试是否 每隔intervel 都会执行这个回调方法) subscription value received: item:NodeId : " +
""+item.getReadValueId().getNodeId()+", value :"+ dataValue.getValue());
System.out.println();
Object value = dataValue.getValue().getValue();
try {
webSocketServer.sendtoAll(value.toString());
} catch (IOException e) {
e.printStackTrace();
}
if(value.equals(11)){
System.err.println("subscribeInt32 I am over");
}
},"plc1");
return R.ok().put("result","ok");
}
@GetMapping("subscribeEvent")
public R subscribeEvent() throws Exception {
final AtomicInteger eventCount = new AtomicInteger(0);
Integer a = uaService.subscribeEvent("plc1",0,2253,((uaMonitoredItem, variants) -> {
logger.info("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());
}
if (eventCount.incrementAndGet() == 3) {
//如果收到这个事件 3次以后 就可以退出了
}
}));
return R.ok().put("result",a);
}
@GetMapping("subscribeEvent2")
public R subscribeEvent2() throws Exception {
final AtomicInteger eventCount = new AtomicInteger(0);
uaService.subscribeEvent("plc1",2,"MyDevice",((uaMonitoredItem, variants) -> {
logger.info("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());
}
if (eventCount.incrementAndGet() == 3) {
//如果收到这个事件 3次以后 就可以退出了
}
}));
return R.ok().put("result","ok");
}
@GetMapping("subscribeEvents")
public R subscribeEvents() throws Exception {
final AtomicInteger eventCount = new AtomicInteger(0);
List<Integer> nameSpace = new ArrayList<>();
List<Object> identifier = new ArrayList<>();
nameSpace.add(0);
identifier.add(2253);
nameSpace.add(2);
identifier.add("MyDevice");
BiConsumer<UaMonitoredItem, Variant[]> 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());
}
if (eventCount.incrementAndGet() == 3) {
//如果收到这个事件 3次以后 就可以退出了
}
});
BiConsumer<UaMonitoredItem, Variant[]> 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());
}
if (eventCount.incrementAndGet() == 3) {
//如果收到这个事件 3次以后 就可以退出了
}
});
List<BiConsumer<UaMonitoredItem, Variant[]>> list = new ArrayList<>();
list.add(a1);
list.add(a2);
Integer plc1 = uaService.subscribeEvents("plc1", nameSpace, identifier,list);
return R.ok().put("result",plc1);
}
@GetMapping("suspendSubscribeEvent")
public R suspendSubscribeEvent() throws Exception {
final AtomicInteger eventCount = new AtomicInteger(0);
List<Integer> namespace = new ArrayList<>();
namespace.add(0);
List<Object> identify = new ArrayList<>();
identify.add(2253);
return R.ok().put("result",uaService.suspendSubscribe("plc1",namespace,identify));
}
@GetMapping("suspendSubscribeEvent2")
public R suspendSubscribeEvent2() throws Exception {
final AtomicInteger eventCount = new AtomicInteger(0);
List<Integer> namespace = new ArrayList<>();
namespace.add(2);
List<Object> identify = new ArrayList<>();
identify.add("MyDevice");
return R.ok().put("result",uaService.suspendSubscribe("plc1",namespace,identify));
}
@GetMapping("suspendSubscribeEvents")
public R suspendSubscribeEvents() throws Exception {
final AtomicInteger eventCount = new AtomicInteger(0);
List<Integer> namespace = new ArrayList<>();
List<Object> identify = new ArrayList<>();
namespace.add(0);
identify.add(2253);
namespace.add(2);
identify.add("MyDevice");
return R.ok().put("result",uaService.suspendSubscribe("plc1",namespace,identify));
}
@GetMapping("suspendSubscribeVar2")
public R suspendSubscribeVar2() throws Exception {
final AtomicInteger eventCount = new AtomicInteger(0);
List<Integer> namespace = new ArrayList<>();
namespace.add(3);
List<Object> identify = new ArrayList<>();
identify.add("Boolean");
return R.ok().put("result",uaService.suspendSubscribe("plc1",namespace,identify));
}
@GetMapping("suspendSubscribeVars")
public R suspendSubscribeVars() throws Exception {
final AtomicInteger eventCount = new AtomicInteger(0);
List<Integer> namespace = new ArrayList<>();
namespace.add(3);
namespace.add(3);
List<Object> identify = new ArrayList<>();
identify.add("Boolean");
identify.add("Int32");
return R.ok().put("result",uaService.suspendSubscribe("plc1",namespace,identify));
}
//test Real PLC
@GetMapping("testWriteREALPlc")
public R testWriteREALPlc(Integer a) throws Exception {
System.out.println("uaService.hashCode(): "+uaService.hashCode());
Boolean aBoolean = null;
try {
aBoolean = uaService.setValue(PLCVar.RealPLC, Unsigned.ubyte(a),"plc3");
}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("testWriteREALPlc2")
public R testWriteREALPlc2(Integer a) throws Exception {
System.out.println("uaService.hashCode(): "+uaService.hashCode());
Boolean aBoolean = null;
try {
aBoolean = uaService.setValue(PLCVar.RealPLC, a,"plc3");
}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("testReadREALPlc")
public R testReadREALPlc() throws Exception {
System.out.println("uaService.hashCode(): "+uaService.hashCode());
DataValue dataValue = uaService.getValue(PLCVar.RealPLC,"plc3");
return R.ok().put("result",CommonFunction.var(dataValue.getValue().getValue()));
}
@GetMapping("testReadREALPlc3")
public R testReadREALPlc3() throws Exception {
System.out.println("uaService.hashCode(): "+uaService.hashCode());
DataValue dataValue = uaService.getInitialNode(2255,"plc3");
Object b = dataValue.getValue().getValue();
return R.ok().put("result",CommonFunction.var(b));
}
@GetMapping("testReadREALPlc3Brow")
public R testReadREALPlc3Brow() throws Exception {
System.out.println("uaService.hashCode(): "+uaService.hashCode());
NodeIdKey nodeIdKey = uaService.browseA("plc3", 3, "@LOCALSERVER");
return R.ok().put("result",nodeIdKey);
}
//todo main
@GetMapping("testWriteArray")
public R testWriteArray() throws Exception {
System.out.println("uaService.hashCode(): "+uaService.hashCode());
String newValue = "false,false,false,false,false#QBoolean";
Object[] objects = PLCType.QArray.convertArray(newValue);
//Boolean aBoolean = uaService.setValue(3, "BooleanArray", o, "plc1");
//todo
//uaService.setValues()
return R.ok().put("result",123);
}
@GetMapping("testWriteByteArray")
public R testWriteByteArray() throws Exception {
System.out.println("uaService.hashCode(): "+uaService.hashCode());
String newValue = "6.1,6.1,6.1,6.1,6.1#QFloat";
//注意 这种方式也可以得到数据类型但是无法new 对应类型 的变量数组 如Boolen[] 只能是 Object[] 这样无法通过Milo写入OPC-SERVER
Object objectsaa = PLCType.QArray.convertType(newValue);
Object[] z1 = (Object[])objectsaa;
Object z2 = z1[0];
String name = z2.getClass().getName();
Object[] objects = PLCType.QArray.convertArray(newValue);
//Boolean aBoolean = uaService.setValue(3, "BooleanArray", o, "plc1");
//ns=3;s=ByteArray ns=3;s=FloatArray
DataValue aByte = uaService.getValue(3, "FloatArray","plc1");
//todo 可以通过下面这种方式取到对应的数据类型并且 解析出 数据里面的数据类型放到judgeVarType 函数中去
Object os = aByte.getValue().getValue();
Object[] osa = (Object[])os;
Object a = osa[0];
Object o = CommonFunction.judgeVarType(aByte.getValue().getValue());
Object o1 = CommonFunction.judgeVarType(a);
//todo
//uaService.setValues()
return R.ok().put("result",o+"|"+o1);
}
@GetMapping("testWriteDoubleArray")
public R testWriteDoubleArray() throws Exception {
System.out.println("uaService.hashCode(): "+uaService.hashCode());
String newValue = "7.1,7.1,7.1,7.1,7.1#QDouble";
Object[] objects = PLCType.QArray.convertArray(newValue);
//ns=3;s=DoubleArray
DataValue aByte = uaService.getValue(3, "DoubleArray","plc1");
Object o = CommonFunction.judgeVarType(aByte.getValue().getValue());
System.err.println("before:"+o);
Boolean aBoolean = uaService.setValue(3, "DoubleArray", objects, "plc1");
System.err.println("是否写成功"+aBoolean);
DataValue aByte1 = uaService.getValue(3, "DoubleArray","plc1");
Object o1 = CommonFunction.judgeVarType(aByte1.getValue().getValue());
System.err.println("before:"+o1);
//todo
//uaService.setValues()
return R.ok().put("result",o+"|"+o1);
}
@GetMapping("getByteArrayValue")
public R getByteArrayValue() throws Exception {
System.out.println("uaService.hashCode(): "+uaService.hashCode());
//ns=3;s=ByteArray
DataValue value = uaService.getValue(3, "BooleanArray", "plc1");
//todo
return R.ok().put("result",value.getValue().getValue());
}
@GetMapping("subscribeRealPLCVar")
public R subscribeRealPLCVar() throws Exception {
//只要这个线程还在执行 就会一直未true
boolean alive = PLCConstant.displayThread.isAlive();
uaService.subscribe(PLCVar.RealPLC,new Double(1000),(item,dataValue)->{
System.err.println("(测试是否 每隔intervel 都会执行这个回调方法) subscription value received: item:NodeId : " +
""+item.getReadValueId().getNodeId()+", value :"+ dataValue.getValue());
System.out.println();
Object value = dataValue.getValue().getValue();
IdType types = dataValue.getValue().getDataType().get().getType();
int typeValue = dataValue.getValue().getDataType().get().getType().getValue();
//Integer integer = CommonFunction.judgeVarType(value);
try {
webSocketServer.sendtoAll(value.toString());
} catch (IOException e) {
e.printStackTrace();
}
if(value.equals(11)){
System.err.println("subscribeInt32 I am over");
}
},"plc3",1);
return R.ok().put("result","ok");
}
@GetMapping("subscribeList")
public R subscribeList() throws Exception {
//只要这个线程还在执行 就会一直未true
boolean alive = PLCConstant.displayThread.isAlive();
uaService.subscribe(PLCVar.StringArray_3,new Double(1000),(item,dataValue)->{
System.err.println("(测试是否 每隔intervel 都会执行这个回调方法) subscription value received: item:NodeId : " +
""+item.getReadValueId().getNodeId()+", value :"+ dataValue.getValue());
System.out.println();
Object value = dataValue.getValue().getValue();
IdType types = dataValue.getValue().getDataType().get().getType();
System.out.println("types:"+types);
int typeValue = dataValue.getValue().getDataType().get().getType().getValue();
System.out.println("typeValue:"+typeValue);
//Integer integer = CommonFunction.judgeVarType(value);
//System.out.println("integer:"+integer);
try {
webSocketServer.sendtoAll(value.toString());
} catch (IOException e) {
e.printStackTrace();
}
if(value.equals(11)){
System.err.println("subscribeInt32 I am over");
}
},"plc1",1);
return R.ok().put("result","ok");
}
*/
}

View File

@ -0,0 +1,142 @@
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("/testPLC2")
public class WmcController2plc2 {
/*
private static final Logger logger = LoggerFactory.getLogger(WmcController2plc2.class);
@Autowired
UAService uaService;
*//**
* 测试real 类型get / set
**//*
@PostMapping("/noRR")
public R noRR() throws Exception {
return R.ok().put("OK",123);
}
@GetMapping("/removeThisPlc")
public R removeThisPlc() throws Exception {
return R.ok().put("result",uaService.dynamicRemovePlc("plc2"));
}
@GetMapping("testWriteOneVar")
public R testWriteOneVar(Integer a) throws Exception {
System.out.println("uaService.hashCode(): "+uaService.hashCode());
Boolean aBoolean = uaService.setValue(PLCVar.INT32_3, a,"plc2");
return R.ok().put("result",aBoolean);
}
@GetMapping("testWriteTwoVar")
public R testWriteTwoVar(Boolean b,Integer a) throws Exception {
System.out.println("uaService.hashCode(): "+uaService.hashCode());
List<PLCVar> list = new ArrayList<>();
list.add(PLCVar.INT32_3);
list.add(PLCVar.Boolean_3);
List<Object> value = new ArrayList<>();
value.add(a);
value.add(b);
Boolean aBoolean = uaService.setValues(list, value,"plc2");
return R.ok().put("result",aBoolean);
}
@GetMapping("testReadOneVar")
public R testReadOneVar() throws Exception {
System.out.println("uaService.hashCode(): "+uaService.hashCode());
DataValue dataValue = uaService.getValue(PLCVar.INT32_3,"plc2");
return R.ok().put("result",dataValue.getValue().getValue());
}
@GetMapping("testReadTwoVar")
public R testReadTwoVar() throws Exception {
System.out.println("uaService.hashCode(): "+uaService.hashCode());
List<PLCVar> list = new ArrayList<>();
list.add(PLCVar.Boolean_3);
list.add(PLCVar.INT32_3);
List<DataValue> values = uaService.getValues(list ,"plc2");
List<Object> res = new ArrayList<>();
for(DataValue dv:values){
res.add(dv.getValue().getValue());
}
return R.ok().put("result",res);
}
@GetMapping("testReadBooleanArray")
public R testReadBooleanArray() throws Exception {
System.out.println("uaService.hashCode(): "+uaService.hashCode());
DataValue aBoolean = null;
try {
aBoolean = uaService.getValue(PLCVar.BooleanArray_3,"plc2");
}catch (Exception e){
System.out.println("err status: "+uaService.extractError(e.getMessage()));
}
return R.ok().put("result",aBoolean.getValue().getValue());
}
@GetMapping("subscribeInt32")
public R subscribeInt32() throws Exception {
uaService.subscribe(PLCVar.INT32_3,new Double(1000),(item,dataValue)->{
System.err.println("(测试是否 每隔intervel 都会执行这个回调方法) subscription value received: item:NodeId : " +
""+item.getReadValueId().getNodeId()+", value :"+ dataValue.getValue());
System.out.println();
if(dataValue.getValue().getValue().equals(11)){
System.err.println("subscribeInt32 I am over");
}
},"plc2",1);
return R.ok().put("result","ok");
}
@GetMapping("subscribeMultiply")
public R subscribeMultiply() throws Exception {
List<PLCVar> list = new ArrayList<>();
list.add(PLCVar.Boolean_3);
list.add(PLCVar.INT32_3);
uaService.subscribeValues(list,new Double(1000),(item,dataValue)->{
System.err.println("(测试是否 每隔intervel 都会执行这个回调方法) subscription value received: item:NodeId : " +
""+item.getReadValueId().getNodeId()+", value :"+ dataValue.getValue());
System.out.println();
if(dataValue.getValue().getValue().equals(11)){
System.err.println("subscribeInt32 I am over");
}
},"plc2");
return R.ok().put("result","ok");
}
@GetMapping("subscribeBoolen")
public R subscribeBoolen() throws Exception {
uaService.subscribe(PLCVar.Boolean_3,new Double(1000),(item,dataValue)->{
System.err.println("(测试是否 每隔intervel 都会执行这个回调方法) subscription value received: item:NodeId : " +
""+item.getReadValueId().getNodeId()+", value :"+ dataValue.getValue());
System.out.println();
if(dataValue.getValue().getValue().equals(false)){
System.err.println("subscribeBoolen I am over");
}
},"plc2",1);
return R.ok().put("result","ok");
}
*/
}

View File

@ -0,0 +1,47 @@
/*
* Copyright (c) 2019 the Eclipse Milo Authors
*
* This program and the accompanying materials are made
* available under the terms of the Eclipse Public License 2.0
* which is available at https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*/
package com.qgs.dc.opcua.selfunion;
import org.eclipse.milo.opcua.sdk.client.OpcUaClient;
import org.eclipse.milo.opcua.sdk.client.api.identity.IdentityProvider;
import org.eclipse.milo.opcua.sdk.client.api.identity.UsernameProvider;
import org.eclipse.milo.opcua.stack.core.security.SecurityPolicy;
import org.eclipse.milo.opcua.stack.core.types.enumerated.MessageSecurityMode;
import org.eclipse.milo.opcua.stack.core.types.structured.EndpointDescription;
import java.util.concurrent.CompletableFuture;
import java.util.function.Predicate;
public interface ClientExample {
default String getEndpointUrl() {
return "opc.tcp://LAPTOP-HU5V73OJ:53530/OPCUA/SimulationServer";
}
default Predicate<EndpointDescription> endpointFilter() {
//只要是 就全部放进来 不过滤
return e -> e.getSecurityMode().equals(MessageSecurityMode.SignAndEncrypt);
}
default SecurityPolicy getSecurityPolicy() {
//return SecurityPolicy.None;
return SecurityPolicy.Basic128Rsa15;
}
default IdentityProvider getIdentityProvider() {
//return new AnonymousProvider();
return new UsernameProvider("CXCX","251128856");
}
void run(OpcUaClient client, CompletableFuture<OpcUaClient> future) throws Exception;
}

View File

@ -0,0 +1,195 @@
package com.qgs.dc.opcua.selfunion.Enum;
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.unsigned.UByte;
import org.eclipse.milo.opcua.stack.core.types.builtin.unsigned.UInteger;
import org.eclipse.milo.opcua.stack.core.types.builtin.unsigned.ULong;
import org.eclipse.milo.opcua.stack.core.types.builtin.unsigned.UShort;
//展示支持数据
//如果要添加新的变量
// 1.先在PLCTypeConstant中添加 变量常量
// 2.在枚举类PLCType.convertType() 方法中扩展 新的变量如果是数组的话PLCType.convertArray()也要扩展枚举类里面也要扩展QUByte(1),
// 3.CommonFunction.judgeVarType() 里面也要扩展
public enum PLCType implements PLCTypeEnum{
//注意下面类型 必须和 CommonFunction.judgeVarType() 方法里面的数据类型 按顺序一一对应
/**
* 无符号 Byte
* */
QUByte(1),
/**
* 无符号 Integer
* */
QUInteger(2),
/**
* 无符号 Short
* */
QUShort(3),
QULong(4),
QBoolean(5),
QString(6),
QDouble(7),
QFloat(8),
QLong(9),
QInteger(10),
QShort(11),
QArray(12), //如果是数组那么数据长度要小于等于原数组长度
QByte(13),
QByteString(14)
;
private Integer plcVarType;
PLCType(Integer type){
this.plcVarType = type;
}
@Override
public Integer getVarType() {
return this.plcVarType;
}
/**
* 用处在写变量到 OPC-Server的时候需要先把变量 类型转换一下 ( 转换的是非数组变量 )
* 注意1. 如果是ByteString 类型 那么传进来newValue 就是 1,2,3,4,5,1,1,1 type = ByteString
* 参数传入旧的数据类型
* 返回返回新的数据类型
* */
@Override
public Object convertType(Object oldType) {
if(plcVarType == 1){
return UByte.valueOf(String.valueOf(oldType));
}else if(plcVarType == 2){
return UInteger.valueOf(String.valueOf(oldType));
}else if(plcVarType == 3){
return UShort.valueOf(String.valueOf(oldType));
}else if(plcVarType == 4){
return ULong.valueOf(String.valueOf(oldType));
}else if(plcVarType == 5){
return Boolean.valueOf(String.valueOf(oldType));
}else if(plcVarType == 6){
return String.valueOf(oldType);
}else if(plcVarType == 7){
return Double.valueOf(String.valueOf(oldType));
}else if(plcVarType == 8){
return Float.valueOf(String.valueOf(oldType));
}else if(plcVarType == 9){
return Long.valueOf(String.valueOf(oldType));
}else if(plcVarType == 10){
return Integer.valueOf(String.valueOf(oldType));
}else if(plcVarType == 11){
return Short.valueOf(String.valueOf(oldType));
}else if(plcVarType == 12){
return convertArray(oldType);
}else if(plcVarType == 13){
return Byte.valueOf(String.valueOf(oldType));
}else if(plcVarType == 14){
// 如果知道是 ByteString 数据类型那么传进来 是String 类型 用逗号隔开
String s = (String)oldType;
String[] split = s.split(",");
byte[] u = new byte[split.length];
for(int i=0;i<split.length;i++){
u[i] = Byte.parseByte(split[i]);
}
ByteString b = ByteString.of(u);
return b;
}
return null;
}
/**
* 用处在写变量到 OPC-Server的时候需要先把变量 类型转换一下 ( 转换的是数组变量 )
* 参数传入旧的数据类型 例如 "7.1,7.1,7.1,7.1,7.1#QDouble"
* 返回返回新的数据类型 例如 [....] 合适数据类型
* */
@Override
public Object[] convertArray(Object oldType) {
//如果是数组 就先返回string 注意如果是数组 传入的是String 类型并且格式 1,2,3|QUByte 这样的 注意后面QUByte 跟着的是数组里面变量的类型
String s = (String)oldType;
String[] all = s.split("#");
String[] content = all[0].split(",");
String type = all[1];
Integer length = content.length;
if(PLCTypeConstant.QUByte.equals(type)){
UByte[] res = new UByte[length];
for(int i=0;i<content.length;i++){
res[i] = UByte.valueOf(content[i]);
}
return res;
}else if(PLCTypeConstant.QUInteger.equals(type)){
UInteger[] res = new UInteger[length];
for(int i=0;i<content.length;i++){
res[i] = UInteger.valueOf(content[i]);
}
return res;
}else if(PLCTypeConstant.QUShort.equals(type)){
UShort[] res = new UShort[length];
for(int i=0;i<content.length;i++){
res[i] = UShort.valueOf(content[i]);
}
return res;
}else if(PLCTypeConstant.QULong.equals(type)){
ULong[] res = new ULong[length];
for(int i=0;i<content.length;i++){
res[i] = ULong.valueOf(content[i]);
}
return res;
}else if(PLCTypeConstant.QBoolean.equals(type)){
Boolean[] res = new Boolean[length];
for(int i=0;i<content.length;i++){
res[i] = Boolean.valueOf(content[i]);
}
return res;
}else if(PLCTypeConstant.QString.equals(type)){
String[] res = new String[length];
for(int i=0;i<content.length;i++){
res[i] = String.valueOf(content[i]);
}
return res;
}else if(PLCTypeConstant.QDouble.equals(type)){
Double[] res = new Double[length];
for(int i=0;i<content.length;i++){
res[i] = Double.valueOf(content[i]);
}
return res;
}else if(PLCTypeConstant.QFloat.equals(type)){
Float[] res = new Float[length];
for(int i=0;i<content.length;i++){
res[i] = Float.valueOf(content[i]);
}
return res;
}else if(PLCTypeConstant.QLong.equals(type)){
Long[] res = new Long[length];
for(int i=0;i<content.length;i++){
res[i] = Long.valueOf(content[i]);
}
return res;
}else if(PLCTypeConstant.QInteger.equals(type)){
Integer[] res = new Integer[length];
for(int i=0;i<content.length;i++){
res[i] = Integer.valueOf(content[i]);
}
return res;
}else if(PLCTypeConstant.QShort.equals(type)){
Short[] res = new Short[length];
for(int i=0;i<content.length;i++){
res[i] = Short.valueOf(content[i]);
}
return res;
}else if(PLCTypeConstant.QByte.equals(type)) {
Byte[] res = new Byte[length];
for(int i=0;i<content.length;i++){
res[i] = Byte.valueOf(content[i]);
}
return res;
}else {
return null;
}
//return content;
}
}

View File

@ -0,0 +1,7 @@
package com.qgs.dc.opcua.selfunion.Enum;
public interface PLCTypeEnum {
Integer getVarType();
Object convertType(Object oldType);
Object[] convertArray(Object oldType);
}

View File

@ -0,0 +1,42 @@
package com.qgs.dc.opcua.selfunion.Enum;
public enum PLCVar implements PLCVarEnum {
/**
* 命名空间为3 identifyInt32 的变量
* */
INT32_3(3,"Int32"),
BYTE_3(3,"Byte"),
Boolean_3(3,"Boolean"),
BooleanArray_3(3,"BooleanArray"),
ByteArray_3(3,"ByteArray"),
StringArray_3(3,"StringArray"),
Counter1_5(5,"Counter1"),
Double_5(3,"Double"),
Float_5(3,"Float"),
String_5(3,"String"),
UInteger_5(3,"UInteger"),
UInt16_5(3,"UInt16"),
Int64_5(3,"Int64"),
Int32_5(3,"Int32"),
Int16_5(3,"Int16"),
RealPLC(3,"@LOCALSERVER.db1.0,b"),
;
private Integer namespace;
private String identifier;
PLCVar(Integer namespace,String identifier){
this.namespace = namespace;
this.identifier = identifier;
}
@Override
public Integer getNameSpace() {
return this.namespace;
}
@Override
public String getIdentifier() {
return this.identifier;
}
}

View File

@ -0,0 +1,6 @@
package com.qgs.dc.opcua.selfunion.Enum;
public interface PLCVarEnum {
Integer getNameSpace();
String getIdentifier();
}

View File

@ -0,0 +1,111 @@
/*
* Copyright (c) 2019 the Eclipse Milo Authors
*
* This program and the accompanying materials are made
* available under the terms of the Eclipse Public License 2.0
* which is available at https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*/
package com.qgs.dc.opcua.selfunion;
import org.eclipse.milo.opcua.sdk.server.util.HostnameUtil;
import org.eclipse.milo.opcua.stack.core.util.SelfSignedCertificateBuilder;
import org.eclipse.milo.opcua.stack.core.util.SelfSignedCertificateGenerator;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.security.*;
import java.security.cert.X509Certificate;
import java.time.Period;
import java.util.regex.Pattern;
class KeyStoreLoader {
private static final Pattern IP_ADDR_PATTERN = Pattern.compile(
"^(([01]?\\d\\d?|2[0-4]\\d|25[0-5])\\.){3}([01]?\\d\\d?|2[0-4]\\d|25[0-5])$");
private static final String CLIENT_ALIAS = "client-ai";
private static final char[] PASSWORD = "password".toCharArray();
private final Logger logger = LoggerFactory.getLogger(getClass());
private X509Certificate clientCertificate;
private KeyPair clientKeyPair;
//DESKTOP-5CTK5AA
KeyStoreLoader load(Path baseDir,String ip) throws Exception {
KeyStore keyStore = KeyStore.getInstance("PKCS12");
//注意不同 plc 要不同的 证书 如果要更换证书 配置那么要把之间的证书信息给删除了不然回去读之前的证书信息 然后去匹配新的config信息这样就冲突了
String myCer = ip+"-local-client-Cer.pfx";
Path serverKeyStore = baseDir.resolve(myCer);
logger.info("Loading KeyStore at {}", serverKeyStore);
if (!Files.exists(serverKeyStore)) {
keyStore.load(null, PASSWORD);
KeyPair keyPair = SelfSignedCertificateGenerator.generateRsaKeyPair(2048);
//注意证书的uri 中必须是OPC服务器 的ip并且生成的证书uri 初始化客户端上的uri 必须相同
String uri = "urn:"+ip+":SimulationServer:clientBaseMilo";
SelfSignedCertificateBuilder builder = new SelfSignedCertificateBuilder(keyPair)
.setCommonName("QGS_OpcUaClient")
.setOrganization("digitalpetri")
.setOrganizationalUnit("dev")
.setLocalityName("Folsom")
.setStateName("CA")
.setCountryCode("CN")
//opc.tcp://DESKTOP-5CTK5AA:53530/OPCUA/SimulationServer
.setApplicationUri(uri)
.addDnsName("localhost")
.addIpAddress("127.0.0.1")
.setValidityPeriod(Period.ofYears(99));
// Get as many hostnames and IP addresses as we can listed in the certificate.
for (String hostname : HostnameUtil.getHostnames("0.0.0.0")) {
if (IP_ADDR_PATTERN.matcher(hostname).matches()) {
builder.addIpAddress(hostname);
} else {
builder.addDnsName(hostname);
}
}
X509Certificate certificate = builder.build();
keyStore.setKeyEntry(CLIENT_ALIAS, keyPair.getPrivate(), PASSWORD, new X509Certificate[]{certificate});
try (OutputStream out = Files.newOutputStream(serverKeyStore)) {
keyStore.store(out, PASSWORD);
}
} else {
try (InputStream in = Files.newInputStream(serverKeyStore)) {
keyStore.load(in, PASSWORD);
}
}
Key serverPrivateKey = keyStore.getKey(CLIENT_ALIAS, PASSWORD);
if (serverPrivateKey instanceof PrivateKey) {
clientCertificate = (X509Certificate) keyStore.getCertificate(CLIENT_ALIAS);
PublicKey serverPublicKey = clientCertificate.getPublicKey();
clientKeyPair = new KeyPair(serverPublicKey, (PrivateKey) serverPrivateKey);
}
return this;
}
X509Certificate getClientCertificate() {
return clientCertificate;
}
KeyPair getClientKeyPair() {
return clientKeyPair;
}
}

View File

@ -0,0 +1,98 @@
package com.qgs.dc.opcua.selfunion;
import java.util.ArrayList;
import java.util.List;
public class NodeIdKey {
private String nodeName;
private Integer namespace;
private String identifier;
private List<NodeIdKey> child;
/**
* type 类型 (就是Node)
* Unspecified(0),
*
* Object(1), //一般代表文件夹
*
* Variable(2), //一般代表变量
*
* Method(4), //一般代表方法
*
* ObjectType(8),
*
* VariableType(16),
*
* ReferenceType(32),
*
* DataType(64),
*
* View(128); //一般代表视图
*
* */
private Integer nodeType;
/**
* 这个代表 Node节点里面存着的变量 的类型
* */
private String varType;
public NodeIdKey(String nodeName,Integer nodeType,Integer namespace, String identifier,String varType) {
this.nodeName = nodeName;
this.nodeType = nodeType;
this.namespace = namespace;
this.identifier = identifier;
this.varType = varType;
this.child = new ArrayList<>();
}
public Integer getNodeType() {
return nodeType;
}
public void setNodeType(Integer nodeType) {
this.nodeType = nodeType;
}
public String getVarType() {
return varType;
}
public void setVarType(String varType) {
this.varType = varType;
}
public List<NodeIdKey> getChild() {
return child;
}
public void setChild(List<NodeIdKey> child) {
this.child = child;
}
public String getNodeName() {
return nodeName;
}
public void setNodeName(String nodeName) {
this.nodeName = nodeName;
}
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;
}
}

View File

@ -0,0 +1,69 @@
package com.qgs.dc.opcua.selfunion;
public class Test {
public static void main(String[] args) throws Exception {
/*
//1.测试读且 单个变量
UAService uaService = new UAService();
DataValue valueByName = uaService.getValueByName(PLCVar.INT32_3);
System.out.println(valueByName.getValue().getValue());
*/
/*
//2.测试读且 多个变量
UAService uaService = new UAService();
List<DataValue> values = uaService.getValues(PLCVar.Boolean_3,PLCVar.INT32_3);
for(DataValue i:values){
System.out.println(i.getValue().getValue());
}*/
/*
//3.write 一次性 写一个变量
UAService uaService = new UAService();
boolean valueByName = uaService.setValue(PLCVar.INT32_3,11);
System.out.println(valueByName);
*/
/*
//4.write 一次性 写多个变量
UAService uaService = new UAService();
List<PLCVar> vars = new ArrayList<>();
vars.add(PLCVar.INT32_3);
vars.add(PLCVar.Boolean_3);
List<Object> values = new ArrayList<>();
values.add(20);
values.add(false);
boolean valueByName = uaService.setValues(vars,values);
System.out.println(valueByName);*/
//5.订阅变量
/*
UAService uaService = new UAService();
uaService.subscribe(PLCVar.INT32_3,new Double(1000.0),(item, dataValue)->{
System.err.println("(测试是否 每隔intervel 都会执行这个回调方法) subscription value received: item:NodeId : " +
""+item.getReadValueId().getNodeId()+", value :"+ dataValue.getValue());
System.out.println();
if(dataValue.getValue().getValue().equals(11)){
System.out.println("I AM DONE");
}
});
uaService.subscribe(PLCVar.Boolean_3,new Double(1000.0),(item,dataValue)->{
System.err.println("(测试是否 每隔intervel 都会执行这个回调方法) subscription value received: item:NodeId : " +
""+item.getReadValueId().getNodeId()+", value :"+ dataValue.getValue());
System.out.println();
if(dataValue.getValue().getValue().equals(false)){
System.out.println("I AM DONE");
}
});
System.out.println("测试subscribe 是否异步");*/
String a = "a|b";
String[] split = a.split("\\|");
System.out.println(split[0]);
System.out.println(split[1]);
}
}

View File

@ -0,0 +1,39 @@
package com.qgs.dc.opcua.selfunion;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
public class Test2 {
public static void main(String[] args) throws Exception {/*
UAService uaService = new UAService();
*//*boolean valueByName = uaService.setValue(PLCVar.INT32_3,11);
System.out.println("before:"+valueByName);*//*
long start = System.currentTimeMillis();
uaService.setValue(PLCVar.Boolean_3,true);
long end = System.currentTimeMillis();
System.out.println("after:need time "+(end-start));*/
CompletableFuture<String> future1 = CompletableFuture.supplyAsync(() -> {
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
return "101";
});
CompletableFuture<Integer> future2 = CompletableFuture.supplyAsync(() -> 100);
//s 参数是主体i 是future的结果 并且会等 future1future2 都完成了
CompletableFuture<Double> future = future1.thenCombine(future2, (s, i) -> Double.parseDouble(s + i));
try {
System.out.println(future.get());
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
}

View File

@ -0,0 +1,57 @@
package com.qgs.dc.opcua.selfunion;
import com.qgs.dc.opcua.selfunion.Enum.PLCVar;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
/**
* @Desc: ""
* @Author: caixiang
* @DATE: 2020/7/20 8:52
*/
public class Test3 {
public static void main(String[] args) {
List<String> as = new ArrayList<>();
as.add("1");
as.add("2");
as.add("3");
HashMap<String,String> asas = new HashMap<>();
asas.put("1","a");
asas.put("2","b");
asas.put("3","c");
String[] ss = new String[3];
ss[0] = "a";
ss[1] = "b";
ss[2] = "c";
System.out.println("as:"+asas.getClass().isArray());
System.out.println("asas:"+asas.getClass().isArray());
System.out.println("ss:"+ Arrays.asList(ss));
List<Object> list = new ArrayList<>();
list = Arrays.asList(ss);
System.out.println("list"+list);
System.out.println("INT32_3:"+ PLCVar.valueOf("INT32_3").getIdentifier());
String newValue = "6,7,8,9,10#QUByte";
String[] split = newValue.split("#");
System.out.println(split);
Object[] b = te();
System.out.println(b);
}
public static Object[] te(){
Object[] as = new Object[3];
as[0] = true;
as[1] = true;
as[2] = true;
return as;
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,72 @@
package com.qgs.dc.opcua.selfunion.entity;
/**
* @Desc: ""
* @Author: caixiang
* @DATE: 2020/8/6 8:48
*/
public class CurrentSubEntity {
private Integer nameSpace;
private String identifier;
private Object value;
private String valueType;
private Boolean isArray;
private String plcName;
public CurrentSubEntity(Integer nameSpace, String identifier, Object value, String valueType, Boolean isArray, String plcName) {
this.nameSpace = nameSpace;
this.identifier = identifier;
this.value = value;
this.valueType = valueType;
this.isArray = isArray;
this.plcName = plcName;
}
public Boolean getArray() {
return isArray;
}
public void setArray(Boolean array) {
isArray = array;
}
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 Object getValue() {
return value;
}
public void setValue(Object value) {
this.value = value;
}
public String getValueType() {
return valueType;
}
public void setValueType(String valueType) {
this.valueType = valueType;
}
public String getPlcName() {
return plcName;
}
public void setPlcName(String plcName) {
this.plcName = plcName;
}
}

View File

@ -0,0 +1,47 @@
package com.qgs.dc.opcua.selfunion.entity;
import java.util.List;
/**
* @Desc: ""
* @Author: caixiang
* @DATE: 2020/8/7 9:57
*/
public class DelSubscribeEntity {
List<Integer> listNameSpace;
List<Object> listIdentifier;
String plcName;
Integer type;
public Integer getType() {
return type;
}
public void setType(Integer type) {
this.type = type;
}
public List<Integer> getListNameSpace() {
return listNameSpace;
}
public void setListNameSpace(List<Integer> listNameSpace) {
this.listNameSpace = listNameSpace;
}
public List<Object> getListIdentifier() {
return listIdentifier;
}
public void setListIdentifier(List<Object> listIdentifier) {
this.listIdentifier = listIdentifier;
}
public String getPlcName() {
return plcName;
}
public void setPlcName(String plcName) {
this.plcName = plcName;
}
}

View File

@ -0,0 +1,61 @@
package com.qgs.dc.opcua.selfunion.entity;
import org.eclipse.milo.opcua.sdk.client.api.subscriptions.UaSubscription;
import java.util.HashMap;
public class SubscribeEntity {
private UaSubscription uaSubscription;
// 1 代表已经订阅成功 2 代表 这个订阅以被取消
private Integer status;
//1 == 代表 展示型订阅2 == 代表 功能型(变量) 订阅3 == 代表 功能型(事件) 订阅
private Integer type;
/**
* type == 1 的时候
* content 中包含所有的 节点信息key:"ns,identifier" value:""value 是空值只有当type==2 的时候才有意义
* type == 2 的时候
* content 就包含key:"ns,identifier" value:"后调函数的接口"
* */
private HashMap<String,String> content;
public SubscribeEntity(UaSubscription uaSubscription, Integer status,Integer type) {
this.uaSubscription = uaSubscription;
this.status = status;
this.type = type;
content = new HashMap<>();
}
public Integer getType() {
return type;
}
public void setType(Integer type) {
this.type = type;
}
public HashMap<String, String> getContent() {
return content;
}
public void setContent(HashMap<String, String> content) {
this.content = content;
}
public UaSubscription getUaSubscription() {
return uaSubscription;
}
public void setUaSubscription(UaSubscription uaSubscription) {
this.uaSubscription = uaSubscription;
}
public Integer getStatus() {
return status;
}
public void setStatus(Integer status) {
this.status = status;
}
}

View File

@ -0,0 +1,38 @@
package com.qgs.dc.opcua.selfunion.entity;
import java.util.List;
/**
* @Desc: ""
* @Author: caixiang
* @DATE: 2020/7/23 14:51
*/
public class SubscribeEventArgEntity {
List<Integer> listNameSpace;
List<Object> listIdentifier;
String plcName;
public List<Integer> getListNameSpace() {
return listNameSpace;
}
public void setListNameSpace(List<Integer> listNameSpace) {
this.listNameSpace = listNameSpace;
}
public List<Object> getListIdentifier() {
return listIdentifier;
}
public void setListIdentifier(List<Object> listIdentifier) {
this.listIdentifier = listIdentifier;
}
public String getPlcName() {
return plcName;
}
public void setPlcName(String plcName) {
this.plcName = plcName;
}
}

View File

@ -0,0 +1,38 @@
package com.qgs.dc.opcua.selfunion.entity;
import java.util.List;
/**
* @Desc: ""
* @Author: caixiang
* @DATE: 2020/7/23 14:51
*/
public class SubscribeVarArgEntity {
List<Integer> listNameSpace;
List<String> listIdentifier;
String plcName;
public List<Integer> getListNameSpace() {
return listNameSpace;
}
public void setListNameSpace(List<Integer> listNameSpace) {
this.listNameSpace = listNameSpace;
}
public List<String> getListIdentifier() {
return listIdentifier;
}
public void setListIdentifier(List<String> listIdentifier) {
this.listIdentifier = listIdentifier;
}
public String getPlcName() {
return plcName;
}
public void setPlcName(String plcName) {
this.plcName = plcName;
}
}

View File

@ -0,0 +1,29 @@
server:
port: 8009
spring:
rabbitmq:
# 如果是rabbitmq+haproxy+keepalived集群 那么192.168.0.176是haproxy代理的地址严格来说是keepalived的vip
addresses: 192.168.0.176:5672
username: cdte
password: cdte
virtual-host: cdte
connection-timeout: 15000
publisher-confirm-type: correlated
publisher-returns: true
template:
mandatory: true
listener:
simple:
acknowledge-mode: manual
concurrency: 1
max-concurrency: 10
prefetch: 5
#================重试机制 开始
#retry:
#max-attempts: 3 #最大重试次数
#enabled: true #是否开启消费者重试为false时关闭消费者重试这时消费端代码异常会一直重复收到消息
#initial-interval: 2000 #重试间隔时间(单位毫秒)
#max-interval: 10000 # 重试最大间隔时间
#multiplier: 2 # 间隔时间乘子,间隔时间*乘子=下一次的间隔时间,最大不能超过设置的最大间隔时间
#================重试机制 结束

View File

@ -0,0 +1,512 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- 日志级别从低到高分为TRACE < DEBUG < INFO < WARN < ERROR < FATAL如果设置为WARN则低于WARN的信息都不会输出 -->
<!-- scan:当此属性设置为true时配置文档如果发生改变将会被重新加载默认值为true -->
<!-- scanPeriod:设置监测配置文档是否有修改的时间间隔,如果没有给出时间单位,默认单位是毫秒。
当scan为true时此属性生效。默认的时间间隔为1分钟。 -->
<!-- debug:当此属性设置为true时将打印出logback内部日志信息实时查看logback运行状态。默认值为false。 -->
<configuration scan="true" scanPeriod="10 seconds">
<contextName>logback-spring</contextName>
<!-- name的值是变量的名称value的值时变量定义的值。通过定义的值会被插入到logger上下文中。定义后可以使“${}”来使用变量。 -->
<property name="logging.pathwork" value="C:/qgs_logger/work" />
<property name="logging.pathopc" value="C:/qgs_logger/opc" />
<property name="logging.pathmq" value="C:/qgs_logger/mq" />
<!--0. 日志格式和颜色渲染 -->
<!-- 彩色日志依赖的渲染类 -->
<conversionRule conversionWord="clr" converterClass="org.springframework.boot.logging.logback.ColorConverter" />
<conversionRule conversionWord="wex" converterClass="org.springframework.boot.logging.logback.WhitespaceThrowableProxyConverter" />
<conversionRule conversionWord="wEx" converterClass="org.springframework.boot.logging.logback.ExtendedWhitespaceThrowableProxyConverter" />
<!-- 彩色日志格式 -->
<property name="CONSOLE_LOG_PATTERN" value="${CONSOLE_LOG_PATTERN:-%clr(%d{yyyy-MM-dd HH:mm:ss.SSS}){faint} %clr(${LOG_LEVEL_PATTERN:-%5p}) %clr(${PID:- }){magenta} %clr(---){faint} %clr([%15.15t]){faint} %clr(%-40.40logger{39}){cyan} %clr(:){faint} %m%n${LOG_EXCEPTION_CONVERSION_WORD:-%wEx}}"/>
<!--1. 输出到控制台-->
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
<!--此日志appender是为开发使用只配置最底级别控制台输出的日志级别是大于或等于此级别的日志信息-->
<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
<level>debug</level>
</filter>
<encoder>
<Pattern>${CONSOLE_LOG_PATTERN}</Pattern>
<!-- 设置字符集 -->
<charset>UTF-8</charset>
</encoder>
</appender>
<!--2. 输出到文档-->
<!-- 2.1 level为 DEBUG 日志,时间滚动输出 -->
<appender name="DEBUG_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<!-- 正在记录的日志文档的路径及文档名 -->
<file>${logging.pathwork}/work-log-debug.log</file>
<!--日志文档输出格式-->
<encoder>
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern>
<charset>UTF-8</charset> <!-- 设置字符集 -->
</encoder>
<!-- 日志记录器的滚动策略,按日期,按大小记录 -->
<!-- <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">-->
<!-- &lt;!&ndash; 日志归档 &ndash;&gt;-->
<!-- <fileNamePattern>${logging.pathwork}/work-log-debug-%d{yyyy-MM-dd}.%i.log</fileNamePattern>-->
<!--&lt;!&ndash; <timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">&ndash;&gt;-->
<!--&lt;!&ndash; <maxFileSize>100MB</maxFileSize>&ndash;&gt;-->
<!--&lt;!&ndash; </timeBasedFileNamingAndTriggeringPolicy>&ndash;&gt;-->
<!-- <timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">-->
<!-- <maxFileSize>100MB</maxFileSize>-->
<!-- </timeBasedFileNamingAndTriggeringPolicy>-->
<!-- &lt;!&ndash;日志文档保留天数&ndash;&gt;-->
<!-- <maxHistory>999</maxHistory>-->
<!-- </rollingPolicy>-->
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<!-- rollover daily -->
<fileNamePattern>${logging.pathwork}/work-log-debug-%d{yyyy-MM-dd}.%i.log</fileNamePattern>
<!-- each file should be at most 100MB, keep 60 days worth of history, but at most 20GB -->
<maxFileSize>100MB</maxFileSize>
<maxHistory>999</maxHistory>
<totalSizeCap>200GB</totalSizeCap>
</rollingPolicy>
<!-- 此日志文档只记录debug级别的 -->
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>debug</level>
<onMatch>ACCEPT</onMatch>
<onMismatch>DENY</onMismatch>
</filter>
</appender>
<appender name="OPCUA_DEBUG_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<!-- 正在记录的日志文档的路径及文档名 -->
<file>${logging.pathopc}/opcua-log-debug.log</file>
<!--日志文档输出格式-->
<encoder>
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern>
<charset>UTF-8</charset> <!-- 设置字符集 -->
</encoder>
<!-- 日志记录器的滚动策略,按日期,按大小记录 -->
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<!-- rollover daily -->
<fileNamePattern>${logging.pathopc}/opcua-log-debug-%d{yyyy-MM-dd}.%i.log</fileNamePattern>
<!-- each file should be at most 100MB, keep 60 days worth of history, but at most 20GB -->
<maxFileSize>100MB</maxFileSize>
<maxHistory>999</maxHistory>
<totalSizeCap>200GB</totalSizeCap>
</rollingPolicy>
<!-- 此日志文档只记录debug级别的 -->
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>debug</level>
<onMatch>ACCEPT</onMatch>
<onMismatch>DENY</onMismatch>
</filter>
</appender>
<appender name="MQ_DEBUG_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<!-- 正在记录的日志文档的路径及文档名 -->
<file>${logging.pathmq}/mq-log-debug.log</file>
<!--日志文档输出格式-->
<encoder>
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern>
<charset>UTF-8</charset> <!-- 设置字符集 -->
</encoder>
<!-- 日志记录器的滚动策略,按日期,按大小记录 -->
<!-- <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">-->
<!-- &lt;!&ndash; 日志归档 &ndash;&gt;-->
<!-- <fileNamePattern>${logging.pathmq}/mq-log-debug-%d{yyyy-MM-dd}.%i.log</fileNamePattern>-->
<!-- <timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">-->
<!-- <maxFileSize>100MB</maxFileSize>-->
<!-- </timeBasedFileNamingAndTriggeringPolicy>-->
<!-- &lt;!&ndash;日志文档保留天数&ndash;&gt;-->
<!-- <maxHistory>999</maxHistory>-->
<!-- </rollingPolicy>-->
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<!-- rollover daily -->
<fileNamePattern>${logging.pathmq}/mq-log-debug-%d{yyyy-MM-dd}.%i.log</fileNamePattern>
<!-- each file should be at most 100MB, keep 60 days worth of history, but at most 20GB -->
<maxFileSize>100MB</maxFileSize>
<maxHistory>999</maxHistory>
<totalSizeCap>200GB</totalSizeCap>
</rollingPolicy>
<!-- 此日志文档只记录debug级别的 -->
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>debug</level>
<onMatch>ACCEPT</onMatch>
<onMismatch>DENY</onMismatch>
</filter>
</appender>
<!-- 2.2 level为 INFO 日志,时间滚动输出 -->
<appender name="INFO_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<!-- 正在记录的日志文档的路径及文档名 -->
<file>${logging.pathwork}/work-log-info.log</file>
<!--日志文档输出格式-->
<encoder>
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern>
<charset>UTF-8</charset>
</encoder>
<!-- 日志记录器的滚动策略,按日期,按大小记录 -->
<!-- <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">-->
<!-- &lt;!&ndash; 每天日志归档路径以及格式 &ndash;&gt;-->
<!-- <fileNamePattern>${logging.pathwork}/work-log-info-%d{yyyy-MM-dd}.%i.log</fileNamePattern>-->
<!-- <timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">-->
<!-- <maxFileSize>100MB</maxFileSize>-->
<!-- </timeBasedFileNamingAndTriggeringPolicy>-->
<!-- &lt;!&ndash;日志文档保留天数&ndash;&gt;-->
<!-- <maxHistory>999</maxHistory>-->
<!-- </rollingPolicy>-->
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<!-- rollover daily -->
<fileNamePattern>${logging.pathwork}/work-log-info-%d{yyyy-MM-dd}.%i.log</fileNamePattern>
<!-- each file should be at most 100MB, keep 60 days worth of history, but at most 20GB -->
<maxFileSize>100MB</maxFileSize>
<maxHistory>999</maxHistory>
<totalSizeCap>200GB</totalSizeCap>
</rollingPolicy>
<!-- 此日志文档只记录info级别的 -->
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>info</level>
<onMatch>ACCEPT</onMatch>
<onMismatch>DENY</onMismatch>
</filter>
</appender>
<appender name="OPCUA_INFO_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<!-- 正在记录的日志文档的路径及文档名 -->
<file>${logging.pathopc}/opcua-log-info.log</file>
<!--日志文档输出格式-->
<encoder>
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern>
<charset>UTF-8</charset>
</encoder>
<!-- 日志记录器的滚动策略,按日期,按大小记录 -->
<!-- <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">-->
<!-- &lt;!&ndash; 每天日志归档路径以及格式 &ndash;&gt;-->
<!-- <fileNamePattern>${logging.pathopc}/opcua-log-info-%d{yyyy-MM-dd}.%i.log</fileNamePattern>-->
<!-- <timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">-->
<!-- <maxFileSize>100MB</maxFileSize>-->
<!-- </timeBasedFileNamingAndTriggeringPolicy>-->
<!-- &lt;!&ndash;日志文档保留天数&ndash;&gt;-->
<!-- <maxHistory>999</maxHistory>-->
<!-- </rollingPolicy>-->
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<!-- rollover daily -->
<fileNamePattern>${logging.pathopc}/opcua-log-info-%d{yyyy-MM-dd}.%i.log</fileNamePattern>
<!-- each file should be at most 100MB, keep 60 days worth of history, but at most 20GB -->
<maxFileSize>100MB</maxFileSize>
<maxHistory>999</maxHistory>
<totalSizeCap>200GB</totalSizeCap>
</rollingPolicy>
<!-- 此日志文档只记录info级别的 -->
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>info</level>
<onMatch>ACCEPT</onMatch>
<onMismatch>DENY</onMismatch>
</filter>
</appender>
<appender name="MQ_INFO_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<!-- 正在记录的日志文档的路径及文档名 -->
<file>${logging.pathmq}/mq-log-info.log</file>
<!--日志文档输出格式-->
<encoder>
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern>
<charset>UTF-8</charset>
</encoder>
<!-- 日志记录器的滚动策略,按日期,按大小记录 -->
<!-- <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">-->
<!-- &lt;!&ndash; 每天日志归档路径以及格式 &ndash;&gt;-->
<!-- <fileNamePattern>${logging.pathmq}/mq-log-info-%d{yyyy-MM-dd}.%i.log</fileNamePattern>-->
<!-- <timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">-->
<!-- <maxFileSize>100MB</maxFileSize>-->
<!-- </timeBasedFileNamingAndTriggeringPolicy>-->
<!-- &lt;!&ndash;日志文档保留天数&ndash;&gt;-->
<!-- <maxHistory>999</maxHistory>-->
<!-- </rollingPolicy>-->
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<!-- rollover daily -->
<fileNamePattern>${logging.pathmq}/mq-log-info-%d{yyyy-MM-dd}.%i.log</fileNamePattern>
<!-- each file should be at most 100MB, keep 60 days worth of history, but at most 20GB -->
<maxFileSize>100MB</maxFileSize>
<maxHistory>999</maxHistory>
<totalSizeCap>200GB</totalSizeCap>
</rollingPolicy>
<!-- 此日志文档只记录info级别的 -->
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>info</level>
<onMatch>ACCEPT</onMatch>
<onMismatch>DENY</onMismatch>
</filter>
</appender>
<!-- 2.3 level为 WARN 日志,时间滚动输出 -->
<appender name="WARN_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<!-- 正在记录的日志文档的路径及文档名 -->
<file>${logging.pathwork}/work-log-warn.log</file>
<!--日志文档输出格式-->
<encoder>
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern>
<charset>UTF-8</charset> <!-- 此处设置字符集 -->
</encoder>
<!-- 日志记录器的滚动策略,按日期,按大小记录 -->
<!-- <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">-->
<!-- <fileNamePattern>${logging.pathwork}/work-log-warn-%d{yyyy-MM-dd}.%i.log</fileNamePattern>-->
<!-- <timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">-->
<!-- <maxFileSize>100MB</maxFileSize>-->
<!-- </timeBasedFileNamingAndTriggeringPolicy>-->
<!-- &lt;!&ndash;日志文档保留天数&ndash;&gt;-->
<!-- <maxHistory>999</maxHistory>-->
<!-- </rollingPolicy>-->
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<!-- rollover daily -->
<fileNamePattern>${logging.pathwork}/work-log-warn-%d{yyyy-MM-dd}.%i.log</fileNamePattern>
<!-- each file should be at most 100MB, keep 60 days worth of history, but at most 20GB -->
<maxFileSize>100MB</maxFileSize>
<maxHistory>999</maxHistory>
<totalSizeCap>200GB</totalSizeCap>
</rollingPolicy>
<!-- 此日志文档只记录warn级别的 -->
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>warn</level>
<onMatch>ACCEPT</onMatch>
<onMismatch>DENY</onMismatch>
</filter>
</appender>
<appender name="OPCUA_WARN_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<!-- 正在记录的日志文档的路径及文档名 -->
<file>${logging.pathopc}/opcua-log-warn.log</file>
<!--日志文档输出格式-->
<encoder>
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern>
<charset>UTF-8</charset> <!-- 此处设置字符集 -->
</encoder>
<!-- 日志记录器的滚动策略,按日期,按大小记录 -->
<!-- <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">-->
<!-- <fileNamePattern>${logging.pathopc}/opcua-log-warn-%d{yyyy-MM-dd}.%i.log</fileNamePattern>-->
<!-- <timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">-->
<!-- <maxFileSize>100MB</maxFileSize>-->
<!-- </timeBasedFileNamingAndTriggeringPolicy>-->
<!-- &lt;!&ndash;日志文档保留天数&ndash;&gt;-->
<!-- <maxHistory>999</maxHistory>-->
<!-- </rollingPolicy>-->
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<!-- rollover daily -->
<fileNamePattern>${logging.pathopc}/opcua-log-warn-%d{yyyy-MM-dd}.%i.log</fileNamePattern>
<!-- each file should be at most 100MB, keep 60 days worth of history, but at most 20GB -->
<maxFileSize>100MB</maxFileSize>
<maxHistory>999</maxHistory>
<totalSizeCap>200GB</totalSizeCap>
</rollingPolicy>
<!-- 此日志文档只记录warn级别的 -->
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>warn</level>
<onMatch>ACCEPT</onMatch>
<onMismatch>DENY</onMismatch>
</filter>
</appender>
<appender name="MQ_WARN_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<!-- 正在记录的日志文档的路径及文档名 -->
<file>${logging.pathmq}/mq-log-warn.log</file>
<!--日志文档输出格式-->
<encoder>
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern>
<charset>UTF-8</charset> <!-- 此处设置字符集 -->
</encoder>
<!-- 日志记录器的滚动策略,按日期,按大小记录 -->
<!-- <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">-->
<!-- <fileNamePattern>${logging.pathmq}/mq-log-warn-%d{yyyy-MM-dd}.%i.log</fileNamePattern>-->
<!-- <timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">-->
<!-- <maxFileSize>100MB</maxFileSize>-->
<!-- </timeBasedFileNamingAndTriggeringPolicy>-->
<!-- &lt;!&ndash;日志文档保留天数&ndash;&gt;-->
<!-- <maxHistory>999</maxHistory>-->
<!-- </rollingPolicy>-->
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<!-- rollover daily -->
<fileNamePattern>${logging.pathmq}/mq-log-warn-%d{yyyy-MM-dd}.%i.log</fileNamePattern>
<!-- each file should be at most 100MB, keep 60 days worth of history, but at most 20GB -->
<maxFileSize>100MB</maxFileSize>
<maxHistory>999</maxHistory>
<totalSizeCap>200GB</totalSizeCap>
</rollingPolicy>
<!-- 此日志文档只记录warn级别的 -->
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>warn</level>
<onMatch>ACCEPT</onMatch>
<onMismatch>DENY</onMismatch>
</filter>
</appender>
<!-- 2.4 level为 ERROR 日志,时间滚动输出 -->
<appender name="ERROR_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<!-- 正在记录的日志文档的路径及文档名 -->
<file>${logging.pathwork}/work-log-error.log</file>
<!--日志文档输出格式-->
<encoder>
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern>
<charset>UTF-8</charset> <!-- 此处设置字符集 -->
</encoder>
<!-- 日志记录器的滚动策略,按日期,按大小记录 -->
<!-- <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">-->
<!-- <fileNamePattern>${logging.pathwork}/work-log-error-%d{yyyy-MM-dd}.%i.log</fileNamePattern>-->
<!-- <timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">-->
<!-- <maxFileSize>100MB</maxFileSize>-->
<!-- </timeBasedFileNamingAndTriggeringPolicy>-->
<!-- &lt;!&ndash;日志文档保留天数&ndash;&gt;-->
<!-- <maxHistory>999</maxHistory>-->
<!-- </rollingPolicy>-->
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<!-- rollover daily -->
<fileNamePattern>${logging.pathwork}/work-log-error-%d{yyyy-MM-dd}.%i.log</fileNamePattern>
<!-- each file should be at most 100MB, keep 60 days worth of history, but at most 20GB -->
<maxFileSize>100MB</maxFileSize>
<maxHistory>999</maxHistory>
<totalSizeCap>200GB</totalSizeCap>
</rollingPolicy>
<!-- 此日志文档只记录ERROR级别的 -->
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>ERROR</level>
<onMatch>ACCEPT</onMatch>
<onMismatch>DENY</onMismatch>
</filter>
</appender>
<appender name="OPCUA_ERROR_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<!-- 正在记录的日志文档的路径及文档名 -->
<file>${logging.pathopc}/opcua-log-error.log</file>
<!--日志文档输出格式-->
<encoder>
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern>
<charset>UTF-8</charset> <!-- 此处设置字符集 -->
</encoder>
<!-- 日志记录器的滚动策略,按日期,按大小记录 -->
<!-- <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">-->
<!-- <fileNamePattern>${logging.pathopc}/opcua-log-error-%d{yyyy-MM-dd}.%i.log</fileNamePattern>-->
<!-- <timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">-->
<!-- <maxFileSize>100MB</maxFileSize>-->
<!-- </timeBasedFileNamingAndTriggeringPolicy>-->
<!-- &lt;!&ndash;日志文档保留天数&ndash;&gt;-->
<!-- <maxHistory>999</maxHistory>-->
<!-- </rollingPolicy>-->
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<!-- rollover daily -->
<fileNamePattern>${logging.pathopc}/opcua-log-error-%d{yyyy-MM-dd}.%i.log</fileNamePattern>
<!-- each file should be at most 100MB, keep 60 days worth of history, but at most 20GB -->
<maxFileSize>100MB</maxFileSize>
<maxHistory>999</maxHistory>
<totalSizeCap>200GB</totalSizeCap>
</rollingPolicy>
<!-- 此日志文档只记录ERROR级别的 -->
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>ERROR</level>
<onMatch>ACCEPT</onMatch>
<onMismatch>DENY</onMismatch>
</filter>
</appender>
<appender name="MQ_ERROR_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<!-- 正在记录的日志文档的路径及文档名 -->
<file>${logging.pathmq}/mq-log-error.log</file>
<!--日志文档输出格式-->
<encoder>
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern>
<charset>UTF-8</charset> <!-- 此处设置字符集 -->
</encoder>
<!-- 日志记录器的滚动策略,按日期,按大小记录 -->
<!-- <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">-->
<!-- <fileNamePattern>${logging.pathmq}/mq-log-error-%d{yyyy-MM-dd}.%i.log</fileNamePattern>-->
<!-- <timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">-->
<!-- <maxFileSize>100MB</maxFileSize>-->
<!-- </timeBasedFileNamingAndTriggeringPolicy>-->
<!-- &lt;!&ndash;日志文档保留天数&ndash;&gt;-->
<!-- <maxHistory>999</maxHistory>-->
<!-- </rollingPolicy>-->
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<!-- rollover daily -->
<fileNamePattern>${logging.pathmq}/mq-log-error-%d{yyyy-MM-dd}.%i.log</fileNamePattern>
<!-- each file should be at most 100MB, keep 60 days worth of history, but at most 20GB -->
<maxFileSize>100MB</maxFileSize>
<maxHistory>999</maxHistory>
<totalSizeCap>200GB</totalSizeCap>
</rollingPolicy>
<!-- 此日志文档只记录ERROR级别的 -->
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>ERROR</level>
<onMatch>ACCEPT</onMatch>
<onMismatch>DENY</onMismatch>
</filter>
</appender>
<!--
<logger>用来设置某一个包或者具体的某一个类的日志打印级别、
以及指定<appender><logger>仅有一个name属性
一个可选的level和一个可选的addtivity属性。
name:用来指定受此logger约束的某一个包或者具体的某一个类。
level:用来设置打印级别大小写无关TRACE, DEBUG, INFO, WARN, ERROR, ALL 和 OFF
还有一个特俗值INHERITED或者同义词NULL代表强制执行上级的级别。
如果未设置此属性那么当前logger将会继承上级的级别。
addtivity:是否向上级logger传递打印信息。默认是true。
<logger name="org.springframework.web" level="info"/>
<logger name="org.springframework.scheduling.annotation.ScheduledAnnotationBeanPostProcessor" level="INFO"/>
-->
<!--
使用mybatis的时候sql语句是debug下才会打印而这里我们只配置了info所以想要查看sql语句的话有以下两种操作
第一种把<root level="info">改成<root level="DEBUG">这样就会打印sql不过这样日志那边会出现很多其他消息
第二种就是单独给dao下目录配置debug模式代码如下这样配置sql语句会打印其他还是正常info级别
【logging.level.org.mybatis=debug logging.level.dao=debug】
-->
<!--
root节点是必选节点用来指定最基础的日志输出级别只有一个level属性
level:用来设置打印级别大小写无关TRACE, DEBUG, INFO, WARN, ERROR, ALL 和 OFF
不能设置为INHERITED或者同义词NULL。默认是DEBUG
可以包含零个或多个元素标识这个appender将会添加到这个logger。
-->
<!-- name就是包名这个包下的 所有logger 输出就以下配置 -->
<logger name="com.qgs.dc.opcua" additivity="false">
<!--使用哪一个Appender-->
<appender-ref ref="OPCUA_DEBUG_FILE" />
<appender-ref ref="OPCUA_INFO_FILE" />
<appender-ref ref="OPCUA_WARN_FILE" />
<appender-ref ref="OPCUA_ERROR_FILE" />
</logger>
<logger name="com.qgs.dc.mq" additivity="false">
<appender-ref ref="MQ_DEBUG_FILE" />
<appender-ref ref="MQ_INFO_FILE" />
<appender-ref ref="MQ_WARN_FILE" />
<appender-ref ref="MQ_ERROR_FILE" />
</logger>
<root level="info">
<appender-ref ref="CONSOLE" />
<appender-ref ref="DEBUG_FILE" />
<appender-ref ref="INFO_FILE" />
<appender-ref ref="WARN_FILE" />
<appender-ref ref="ERROR_FILE" />
</root>
<!-- 4.2 生产环境:输出到文档
<springProfile name="pro">
<root level="info">
<appender-ref ref="CONSOLE" />
<appender-ref ref="DEBUG_FILE" />
<appender-ref ref="INFO_FILE" />
<appender-ref ref="ERROR_FILE" />
<appender-ref ref="WARN_FILE" />
</root>
</springProfile> -->
</configuration>

View File

@ -0,0 +1,13 @@
package com.qgs.dc;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
@SpringBootTest
class DcApplicationTests {
@Test
void contextLoads() {
}
}