commit a1adfd9cbe50a59d1973de7f3889ac01b9ae96a9
Author: zzp <472876707@qq.com>
Date: Wed Sep 4 14:38:10 2024 +0800
init
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..ea4af49
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,170 @@
+#################
+## Eclipse
+#################
+
+_site
+*.iml
+.idea/
+target/
+*.log
+*.gz
+out/
+
+*.pydevproject
+.project
+.metadata
+bin/
+tmp/
+*.tmp
+*.bak
+*.swp
+*~.nib
+.classpath
+.settings/
+.loadpath
+
+# External tool builders
+.externalToolBuilders/
+
+# Locally stored "Eclipse launch configurations"
+*.launch
+
+# CDT-specific
+.cproject
+
+# PDT-specific
+.buildpath
+
+
+#################
+## Visual Studio
+#################
+
+## Ignore Visual Studio temporary files, build results, and
+## files generated by popular Visual Studio add-ons.
+
+# User-specific files
+*.suo
+*.user
+*.sln.docstates
+
+# Build results
+[Dd]ebug/
+[Rr]elease/
+*_i.c
+*_p.c
+*.ilk
+*.meta
+*.obj
+*.pch
+*.pdb
+*.pgc
+*.pgd
+*.rsp
+*.sbr
+*.tlb
+*.tli
+*.tlh
+*.vspscc
+.builds
+*.dotCover
+
+#packages/
+
+# Visual C++ cache files
+ipch/
+*.aps
+*.ncb
+*.opensdf
+*.sdf
+
+# Visual Studio profiler
+*.psess
+*.vsp
+
+# ReSharper is a .NET coding add-in
+_ReSharper*
+
+# Installshield output folder
+[Ee]xpress
+
+# DocProject is a documentation generator add-in
+DocProject/buildhelp/
+DocProject/Help/*.HxT
+DocProject/Help/*.HxC
+DocProject/Help/*.hhc
+DocProject/Help/*.hhk
+DocProject/Help/*.hhp
+DocProject/Help/Html2
+DocProject/Help/html
+
+# Click-Once directory
+publish
+
+# Others
+[Bb]in
+[Oo]bj
+# sql
+TestResults
+*.Cache
+ClientBin
+stylecop.*
+~$*
+*.dbmdl
+Generated_Code #added for RIA/Silverlight projects
+
+# Backup & report files from converting an old project file to a newer
+# Visual Studio version. Backup files are not needed, because we have git ;-)
+_UpgradeReport_Files/
+Backup*/
+UpgradeLog*.XML
+
+
+
+############
+## Windows
+############
+
+# Windows image file caches
+Thumbs.db
+
+# Folder config file
+Desktop.ini
+
+
+#############
+## Python
+#############
+
+*.py[co]
+
+# Packages
+*.egg
+*.egg-info
+dist
+build
+eggs
+parts
+bin
+var
+sdist
+develop-eggs
+.installed.cfg
+
+# Installer logs
+pip-log.txt
+
+# Unit test / coverage reports
+.coverage
+.tox
+
+#Translations
+*.mo
+
+#Mr Developer
+.mr.developer.cfg
+
+# Mac crap
+.DS_Store
+
+.gradle
diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar
new file mode 100644
index 0000000..e644113
Binary files /dev/null and b/gradle/wrapper/gradle-wrapper.jar differ
diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
new file mode 100644
index 0000000..a441313
--- /dev/null
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,7 @@
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+distributionUrl=https\://services.gradle.org/distributions/gradle-8.8-bin.zip
+networkTimeout=10000
+validateDistributionUrl=true
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
diff --git a/gradlew b/gradlew
new file mode 100644
index 0000000..b740cf1
--- /dev/null
+++ b/gradlew
@@ -0,0 +1,249 @@
+#!/bin/sh
+
+#
+# Copyright © 2015-2021 the original 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.
+#
+
+##############################################################################
+#
+# Gradle start up script for POSIX generated by Gradle.
+#
+# Important for running:
+#
+# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is
+# noncompliant, but you have some other compliant shell such as ksh or
+# bash, then to run this script, type that shell name before the whole
+# command line, like:
+#
+# ksh Gradle
+#
+# Busybox and similar reduced shells will NOT work, because this script
+# requires all of these POSIX shell features:
+# * functions;
+# * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,
+# «${var#prefix}», «${var%suffix}», and «$( cmd )»;
+# * compound commands having a testable exit status, especially «case»;
+# * various built-in commands including «command», «set», and «ulimit».
+#
+# Important for patching:
+#
+# (2) This script targets any POSIX shell, so it avoids extensions provided
+# by Bash, Ksh, etc; in particular arrays are avoided.
+#
+# The "traditional" practice of packing multiple parameters into a
+# space-separated string is a well documented source of bugs and security
+# problems, so this is (mostly) avoided, by progressively accumulating
+# options in "$@", and eventually passing that to Java.
+#
+# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,
+# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;
+# see the in-line comments for details.
+#
+# There are tweaks for specific operating systems such as AIX, CygWin,
+# Darwin, MinGW, and NonStop.
+#
+# (3) This script is generated from the Groovy template
+# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
+# within the Gradle project.
+#
+# You can find Gradle at https://github.com/gradle/gradle/.
+#
+##############################################################################
+
+# Attempt to set APP_HOME
+
+# Resolve links: $0 may be a link
+app_path=$0
+
+# Need this for daisy-chained symlinks.
+while
+ APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path
+ [ -h "$app_path" ]
+do
+ ls=$( ls -ld "$app_path" )
+ link=${ls#*' -> '}
+ case $link in #(
+ /*) app_path=$link ;; #(
+ *) app_path=$APP_HOME$link ;;
+ esac
+done
+
+# This is normally unused
+# shellcheck disable=SC2034
+APP_BASE_NAME=${0##*/}
+# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)
+APP_HOME=$( cd "${APP_HOME:-./}" > /dev/null && pwd -P ) || exit
+
+# Use the maximum available, or set MAX_FD != -1 to use that value.
+MAX_FD=maximum
+
+warn () {
+ echo "$*"
+} >&2
+
+die () {
+ echo
+ echo "$*"
+ echo
+ exit 1
+} >&2
+
+# OS specific support (must be 'true' or 'false').
+cygwin=false
+msys=false
+darwin=false
+nonstop=false
+case "$( uname )" in #(
+ CYGWIN* ) cygwin=true ;; #(
+ Darwin* ) darwin=true ;; #(
+ MSYS* | MINGW* ) msys=true ;; #(
+ NONSTOP* ) nonstop=true ;;
+esac
+
+CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
+
+
+# Determine the Java command to use to start the JVM.
+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
+ if [ ! -x "$JAVACMD" ] ; then
+ die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+ fi
+else
+ JAVACMD=java
+ if ! command -v java >/dev/null 2>&1
+ then
+ die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+ fi
+fi
+
+# Increase the maximum file descriptors if we can.
+if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
+ case $MAX_FD in #(
+ max*)
+ # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.
+ # shellcheck disable=SC2039,SC3045
+ MAX_FD=$( ulimit -H -n ) ||
+ warn "Could not query maximum file descriptor limit"
+ esac
+ case $MAX_FD in #(
+ '' | soft) :;; #(
+ *)
+ # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.
+ # shellcheck disable=SC2039,SC3045
+ ulimit -n "$MAX_FD" ||
+ warn "Could not set maximum file descriptor limit to $MAX_FD"
+ esac
+fi
+
+# Collect all arguments for the java command, stacking in reverse order:
+# * args from the command line
+# * the main class name
+# * -classpath
+# * -D...appname settings
+# * --module-path (only if needed)
+# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.
+
+# For Cygwin or MSYS, switch paths to Windows format before running java
+if "$cygwin" || "$msys" ; then
+ APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
+ CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
+
+ JAVACMD=$( cygpath --unix "$JAVACMD" )
+
+ # Now convert the arguments - kludge to limit ourselves to /bin/sh
+ for arg do
+ if
+ case $arg in #(
+ -*) false ;; # don't mess with options #(
+ /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath
+ [ -e "$t" ] ;; #(
+ *) false ;;
+ esac
+ then
+ arg=$( cygpath --path --ignore --mixed "$arg" )
+ fi
+ # Roll the args list around exactly as many times as the number of
+ # args, so each arg winds up back in the position where it started, but
+ # possibly modified.
+ #
+ # NB: a `for` loop captures its iteration list before it begins, so
+ # changing the positional parameters here affects neither the number of
+ # iterations, nor the values presented in `arg`.
+ shift # remove old arg
+ set -- "$@" "$arg" # push replacement arg
+ done
+fi
+
+
+# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
+
+# Collect all arguments for the java command:
+# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,
+# and any embedded shellness will be escaped.
+# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be
+# treated as '${Hostname}' itself on the command line.
+
+set -- \
+ "-Dorg.gradle.appname=$APP_BASE_NAME" \
+ -classpath "$CLASSPATH" \
+ org.gradle.wrapper.GradleWrapperMain \
+ "$@"
+
+# Stop when "xargs" is not available.
+if ! command -v xargs >/dev/null 2>&1
+then
+ die "xargs is not available"
+fi
+
+# Use "xargs" to parse quoted args.
+#
+# With -n1 it outputs one arg per line, with the quotes and backslashes removed.
+#
+# In Bash we could simply go:
+#
+# readarray ARGS < <( xargs -n1 <<<"$var" ) &&
+# set -- "${ARGS[@]}" "$@"
+#
+# but POSIX shell has neither arrays nor command substitution, so instead we
+# post-process each arg (as a line of input to sed) to backslash-escape any
+# character that might be a shell metacharacter, then use eval to reverse
+# that process (while maintaining the separation between arguments), and wrap
+# the whole thing up as a single "set" statement.
+#
+# This will of course break if any of these variables contains a newline or
+# an unmatched quote.
+#
+
+eval "set -- $(
+ printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" |
+ xargs -n1 |
+ sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' |
+ tr '\n' ' '
+ )" '"$@"'
+
+exec "$JAVACMD" "$@"
diff --git a/gradlew.bat b/gradlew.bat
new file mode 100644
index 0000000..25da30d
--- /dev/null
+++ b/gradlew.bat
@@ -0,0 +1,92 @@
+@rem
+@rem Copyright 2015 the original author or authors.
+@rem
+@rem Licensed under the Apache License, Version 2.0 (the "License");
+@rem you may not use this file except in compliance with the License.
+@rem 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, software
+@rem distributed under the License is distributed on an "AS IS" BASIS,
+@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+@rem See the License for the specific language governing permissions and
+@rem limitations under the License.
+@rem
+
+@if "%DEBUG%"=="" @echo off
+@rem ##########################################################################
+@rem
+@rem Gradle startup script for Windows
+@rem
+@rem ##########################################################################
+
+@rem Set local scope for the variables with windows NT shell
+if "%OS%"=="Windows_NT" setlocal
+
+set DIRNAME=%~dp0
+if "%DIRNAME%"=="" set DIRNAME=.
+@rem This is normally unused
+set APP_BASE_NAME=%~n0
+set APP_HOME=%DIRNAME%
+
+@rem Resolve any "." and ".." in APP_HOME to make it shorter.
+for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
+
+@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
+
+@rem Find java.exe
+if defined JAVA_HOME goto findJavaFromJavaHome
+
+set JAVA_EXE=java.exe
+%JAVA_EXE% -version >NUL 2>&1
+if %ERRORLEVEL% equ 0 goto execute
+
+echo. 1>&2
+echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2
+echo. 1>&2
+echo Please set the JAVA_HOME variable in your environment to match the 1>&2
+echo location of your Java installation. 1>&2
+
+goto fail
+
+:findJavaFromJavaHome
+set JAVA_HOME=%JAVA_HOME:"=%
+set JAVA_EXE=%JAVA_HOME%/bin/java.exe
+
+if exist "%JAVA_EXE%" goto execute
+
+echo. 1>&2
+echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2
+echo. 1>&2
+echo Please set the JAVA_HOME variable in your environment to match the 1>&2
+echo location of your Java installation. 1>&2
+
+goto fail
+
+:execute
+@rem Setup the command line
+
+set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
+
+
+@rem Execute Gradle
+"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
+
+:end
+@rem End local scope for the variables with windows NT shell
+if %ERRORLEVEL% equ 0 goto mainEnd
+
+:fail
+rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
+rem the _cmd.exe /c_ return code!
+set EXIT_CODE=%ERRORLEVEL%
+if %EXIT_CODE% equ 0 set EXIT_CODE=1
+if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE%
+exit /b %EXIT_CODE%
+
+:mainEnd
+if "%OS%"=="Windows_NT" endlocal
+
+:omega
diff --git a/pom.xml b/pom.xml
new file mode 100644
index 0000000..828b5da
--- /dev/null
+++ b/pom.xml
@@ -0,0 +1,106 @@
+
+
+ 4.0.0
+
+ com.cisdi.data
+ dacoo-data-sdk-yongfeng
+ 1.0-SNAPSHOT
+
+
+ 8
+ 8
+
+
+
+
+ junit
+ junit
+ 4.13.2
+ test
+
+
+
+
+
+
+ org.apache.tomcat.embed
+ tomcat-embed-core
+ 8.5.51
+
+
+ org.apache.tomcat.embed
+ tomcat-embed-el
+ 8.5.51
+
+
+ org.apache.tomcat.embed
+ tomcat-embed-websocket
+ 8.5.51
+
+
+ org.apache.tomcat
+ tomcat-annotations-api
+ 8.5.51
+
+
+ com.cisdi.data
+ dacoo-data-common
+ 1.0-SNAPSHOT
+
+
+ com.cisdi.data
+ dacoo-data-sdk
+ 1.0-SNAPSHOT
+
+
+ netty-all
+ io.netty
+
+
+
+
+ io.netty
+ netty-all
+ 4.1.97.Final
+
+
+ org.apache.commons
+ commons-dbcp2
+ 2.8.0
+
+
+ mysql
+ mysql-connector-java
+ 5.1.46
+
+
+
+ com.google.guava
+ guava
+ 27.0-jre
+
+
+ com.alibaba
+ easyexcel
+ 1.1.2-beta5
+
+
+ org.java-websocket
+ Java-WebSocket
+ 1.3.8
+
+
+
+
+
+ org.projectlombok
+ lombok
+ 1.18.28
+ provided
+
+
+
+
+
\ No newline at end of file
diff --git a/rfid测试说明.txt b/rfid测试说明.txt
new file mode 100644
index 0000000..f7c73e7
--- /dev/null
+++ b/rfid测试说明.txt
@@ -0,0 +1,28 @@
+心跳报文
+02486561727442656103
+
+一个标签
+02524630313030303103
+
+--2条电文
+0252463031303030310302524830333030303103
+
+-- 4条电文
+02524630313030303103025248303330303031030252463031303030310302524830333030303103
+
+-- 4条电文心跳2:
+025246303130303031030252483033303030310302524630313030303103025248303330303031030248656172744265610302486561727442656103
+
+一个标签+心跳+ 一个标签
+02524630313030303103024865617274426561030252463031303030310302524630313030303103025246303130303031030252463031303030310302524630313030303103025246303130303031030252483033303030310302524630313030303103025248303330303031030248656172744265610302486561727442656103
+
+
+
+-- 启用心跳
+{"enableHeartbeat":true,"heartbeatMaxLostCount":3,"heartbeatTimeInMs":5000}
+
+--多个serverId
+{"serverList":["10.233.8.22:2112","10.233.8.23:2112","10.233.8.24:2112","10.233.8.25:2112"]}
+
+--启用心跳+ 多个server
+{"enableHeartbeat":true,"heartbeatMaxLostCount":3,"heartbeatTimeInMs":5000,"serverList":["10.233.8.22:2112","10.233.8.23:2112","10.233.8.24:2112","10.233.8.25:2112"]}
\ No newline at end of file
diff --git a/settings.gradle b/settings.gradle
new file mode 100644
index 0000000..492c1ff
--- /dev/null
+++ b/settings.gradle
@@ -0,0 +1,2 @@
+rootProject.name = 'dacoo-data-sdk-DI-client'
+
diff --git a/src/main/java/com/cisdi/data/AMETEKSurfaceVision/AMETEKSurfaceVisionGateway.java b/src/main/java/com/cisdi/data/AMETEKSurfaceVision/AMETEKSurfaceVisionGateway.java
new file mode 100644
index 0000000..8d4543f
--- /dev/null
+++ b/src/main/java/com/cisdi/data/AMETEKSurfaceVision/AMETEKSurfaceVisionGateway.java
@@ -0,0 +1,209 @@
+package com.cisdi.data.AMETEKSurfaceVision;
+
+import com.alibaba.fastjson.JSON;
+import com.cisdi.data.AMETEKSurfaceVision.gateway.SurfaceVisionAliveCheckTask;
+import com.cisdi.data.AMETEKSurfaceVision.gateway.SurfaceVisionChannelInitializer;
+import com.cisdi.data.AMETEKSurfaceVision.gateway.SurfaceVisionFrameDecoder;
+import com.cisdi.data.AMETEKSurfaceVision.gateway.SurfaceVisionIoSession;
+import com.cisdi.data.AMETEKSurfaceVision.gateway.SurfaceVisionSessionFactory;
+import com.cisdi.data.AMETEKSurfaceVision.gateway.SurfaceVisionSocketReturnMessage;
+import com.cisdi.data.common.exception.BusinessException;
+import com.cisdi.data.sdk.consts.ServiceName;
+import com.cisdi.data.sdk.enums.GatewayState;
+import com.cisdi.data.sdk.gateway.base.SocketGatewayBase;
+import com.cisdi.data.sdk.gateway.netty.IoSession;
+import com.cisdi.data.sdk.gateway.netty.SessionFactory;
+import com.cisdi.data.sdk.gateway.netty.TcpIoService;
+import com.cisdi.data.sdk.gateway.netty.impl.DefaultTcpIoService;
+import com.cisdi.data.sdk.param.ListenSocketParam;
+import com.cisdi.data.sdk.procotol.message.SocketReturnMessage;
+import com.cisdi.data.sdk.service.RouteService;
+import com.cisdi.data.sdk.vo.DeviceVo;
+import com.cisdi.data.sdk.vo.ExeResultVo;
+import com.google.common.collect.Sets;
+import java.util.List;
+import java.util.Set;
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.atomic.AtomicBoolean;
+import lombok.SneakyThrows;
+import org.apache.commons.lang3.StringUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.BeanUtils;
+
+/**
+ * 表检仪tcp协议 平台:server rfid:客户端
+ *
+ * @author tzz
+ */
+public class AMETEKSurfaceVisionGateway extends SocketGatewayBase {
+ private static Logger logger = LoggerFactory.getLogger(AMETEKSurfaceVisionGateway.class);
+ private TcpIoService ioService = null;
+ private SessionFactory sessionFactory = null;
+ private AtomicBoolean shouldRun = new AtomicBoolean(false);
+ private String deviceId;
+ private BlockingQueue sendQueue = new LinkedBlockingQueue<>(1);
+
+ /**
+ * 下发命令超时时间,单位毫秒
+ */
+ private static int maxSendTime = 5000;
+
+ private static final int sleepInternal = 100;
+ //10毫秒
+
+ @SneakyThrows
+ @Override
+ public ExeResultVo sendReturnMessage(SocketReturnMessage returnMsg) {
+ logger.info("send msg is :{}", returnMsg);
+ //阻塞队列,容量为1,若上一个命令未发送则阻塞
+ sendQueue.put(returnMsg);
+
+ ExeResultVo sendResult = new ExeResultVo();
+ sendResult.setSuccess(false);
+
+ try {
+ SurfaceVisionSocketReturnMessage sendMsg = new SurfaceVisionSocketReturnMessage();
+ BeanUtils.copyProperties(returnMsg, sendMsg);
+ sendMsg.setSendReturnFlag(false);
+
+ //发送下发命令
+ List sessions = sessionFactory.getSessions();
+ SurfaceVisionIoSession ioSession = (SurfaceVisionIoSession) sessions.get(0);
+ logger.info("send msg SurfaceVisionIoSession is :{}", ioSession);
+ ioSession.send(sendMsg);
+
+ //等待下发命令返回,若等待时间超过最大等待时长,直接返回下发失败,每隔sleepInternal时间判断一次
+ long sendTime = System.currentTimeMillis();
+ while (!sendMsg.getSendReturnFlag()) {
+ long now = System.currentTimeMillis();
+ if ((now - sendTime) > (maxSendTime)) {
+ sendResult.setMessage("下发命令响应超时,超时时间为:" + maxSendTime + "ms");
+ ioSession.setCurrentSend(null);
+ return sendResult;
+ }
+ Thread.sleep(sleepInternal);
+ }
+ ioSession.setCurrentSend(null);
+ logger.info("send result is :{}", sendMsg.getRealData());
+
+ sendResult = sendMsg.getRealData();
+ }catch (Exception e){
+ logger.error("下发中断,异常为: ", e);
+ sendResult.setMessage("下发异常中断");
+ }finally {
+ //空出阻塞队列
+ sendQueue.take();
+ }
+ return sendResult;
+ }
+
+
+ @Override
+ public void start() {
+ if(state == GatewayState.RUNNING) {
+ return;
+ }
+
+ //配置信息校验
+ ListenSocketParam socketParam = JSON.parseObject(getInstanceVo().getParameter(), ListenSocketParam.class);
+
+ maxSendTime = socketParam.getTimeout();
+
+ if (StringUtils.isEmpty(socketParam.getListenIp()) || socketParam.getListenPort() == null) {
+ throw new BusinessException("未正确配置监听ip地址和监听端口。");
+ }
+
+ logger.info("AMETEKSurfaceVision网关:{}读取启动配置参数:{}",instanceVo.getRunId(), getInstanceVo().getParameter());
+ //读取网关连接配置参数
+ RouteService routeService = (RouteService) serviceProvider.getByName(ServiceName.Route);
+ List deviceVos = routeService.findByRunId(instanceVo.getRunId());
+
+ //一个网关连接只能配置一个设备
+ if(deviceVos.size() > 1) {
+ throw new BusinessException("网关Id:" +instanceVo.getRunId() + "为AMETEKSurfaceVision网关,只允许关联一个设备Id");
+ }
+
+ deviceId = deviceVos.size() > 0 ? deviceVos.get(0).getDeviceId() : null;
+
+ if(StringUtils.isEmpty(deviceId)) {
+ throw new BusinessException("runId=" + getInstanceVo().getRunId() + "未正确配置网关和设备关联");
+ }
+
+ //创建session工厂并初始化
+ DefaultTcpIoService defaultIoService = new DefaultTcpIoService();
+ SessionFactory factory = new SurfaceVisionSessionFactory(deviceId);
+ factory.init(serviceProvider, this);
+
+ //初始化channel
+ SurfaceVisionChannelInitializer channelInitializer =
+ new SurfaceVisionChannelInitializer<>(factory);
+
+ //初始化ioService
+ defaultIoService.init(instanceVo, factory, channelInitializer);
+
+ ioService = defaultIoService;
+ sessionFactory = factory;
+
+ //启动监听
+ boolean open = ioService.open();
+
+ if(open == true) {
+ state = GatewayState.RUNNING;
+ logger.info("AMETEKSurfaceVision网关:{}启动成功 参数:{}", instanceVo.getRunId(), getInstanceVo().getParameter());
+ instanceVo.setLogOpen(true);
+ //心跳机制
+ shouldRun.set(true);
+//
+// Integer keepAlive = socketParam.getKeepAlive();
+//
+// if(keepAlive == null || keepAlive < 10) {
+// keepAlive = 10;
+// }else if (keepAlive > 60) {
+// keepAlive = 60;
+// }
+//
+// SurfaceVisionAliveCheckTask task = new SurfaceVisionAliveCheckTask(keepAlive, shouldRun, factory);
+//
+// Thread thread = new Thread(task, "ametek-surface-alive-check-thread-" + getInstanceVo().getRunId());
+// thread.start();
+ }else {
+ logger.info("AMETEKSurfaceVision网关:{}启动失败 参数:{}", instanceVo.getRunId(), getInstanceVo().getParameter());
+ }
+
+ }
+
+ @Override
+ public void shutdown() {
+ if(state == GatewayState.CLOSED) {
+ return;
+ }
+
+ //关闭channel
+ if(sessionFactory != null) {
+ for (IoSession ioSession : sessionFactory.getSessions()) {
+ SurfaceVisionIoSession imsIoSession = (SurfaceVisionIoSession)ioSession;
+ imsIoSession.close();
+ }
+ }
+
+ //关闭监听和会话
+ boolean close = ioService.close();
+ if(close == true) {
+ sessionFactory = null;
+ state = GatewayState.CLOSED;
+ shouldRun.set(false);
+ logger.info("AMETEKSurfaceVision网关:{}关闭成功", instanceVo.getRunId());
+ }else {
+ logger.info("AMETEKSurfaceVision网关:{}关闭失败", instanceVo.getRunId());
+ }
+ }
+
+ @Override
+ public Set getActiveDeviceIds() {
+ Set set = Sets.newHashSet();
+ set.add(deviceId);
+ return set;
+ }
+}
diff --git a/src/main/java/com/cisdi/data/AMETEKSurfaceVision/AMETEKSurfaceVisionSocketProtocol.java b/src/main/java/com/cisdi/data/AMETEKSurfaceVision/AMETEKSurfaceVisionSocketProtocol.java
new file mode 100644
index 0000000..0c2a1bc
--- /dev/null
+++ b/src/main/java/com/cisdi/data/AMETEKSurfaceVision/AMETEKSurfaceVisionSocketProtocol.java
@@ -0,0 +1,41 @@
+package com.cisdi.data.AMETEKSurfaceVision;
+
+import com.cisdi.data.AMETEKSurfaceVision.gateway.SurfaceVisionConstants;
+import java.util.Map;
+
+import com.cisdi.data.sdk.gateway.message.SocketMessage;
+import com.cisdi.data.sdk.procotol.SocketProtocol;
+import com.cisdi.data.sdk.procotol.base.ProtocolBase;
+import com.cisdi.data.sdk.procotol.message.SocketReturnMessage;
+import com.cisdi.data.sdk.vo.DecodeVo;
+import com.cisdi.data.sdk.vo.ReturnVo;
+
+/**
+ * 表检仪协议发送给平台的数据编解码
+ *
+ * @author tzz
+ */
+public class AMETEKSurfaceVisionSocketProtocol extends ProtocolBase implements SocketProtocol {
+ @SuppressWarnings("unchecked")
+ @Override
+ public DecodeVo deCode(SocketMessage message) {
+ DecodeVo vo = new DecodeVo();
+
+ Object object = message.getPropsMap().get(SurfaceVisionConstants.S_V_SEND_PLATFORM_DATA);
+
+ Map map = (Map)object;
+
+ vo.setData(map);
+ return vo;
+ }
+
+ @Override
+ public SocketReturnMessage enCode(ReturnVo returnVo) {
+ SocketReturnMessage message = new SocketReturnMessage();
+ message.setDeviceId(returnVo.getDeviceId());
+ message.setMsgId(returnVo.getMsgId());
+ message.setMsgKey(returnVo.getMsgKey());
+ message.getPropsMap().put(SurfaceVisionConstants.S_V_SEND_DATA, returnVo.getValue());
+ return message;
+ }
+}
diff --git a/src/main/java/com/cisdi/data/AMETEKSurfaceVision/DynamicCodeActionParamDTO.java b/src/main/java/com/cisdi/data/AMETEKSurfaceVision/DynamicCodeActionParamDTO.java
new file mode 100644
index 0000000..584f02b
--- /dev/null
+++ b/src/main/java/com/cisdi/data/AMETEKSurfaceVision/DynamicCodeActionParamDTO.java
@@ -0,0 +1,98 @@
+package com.cisdi.data.AMETEKSurfaceVision;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * 实例点位信息
+ *
+ * @author tzz
+ */
+public class DynamicCodeActionParamDTO {
+ public enum PropertyType {
+ BYTE,
+ BOOLEAN,
+ SHORT,
+ INT,
+ FLOAT,
+ DOUBLE,
+ STRING,
+ LONG,
+ PLACEHOLDER;
+ }
+
+ private List propertyList;
+
+ public void appendProperty(PropertyConfig propertyConfig) {
+
+ if (propertyConfig == null) {
+ return;
+ }
+
+ if (propertyList == null) {
+ propertyList = new ArrayList<>();
+ }
+ propertyList.add(propertyConfig);
+ }
+
+ public static class PropertyConfig{
+
+ /** 属性Id*/
+ private String propertyId;
+
+ /** 属性类型*/
+ private PropertyType propertyType;
+
+ /**长度*/
+ private Integer length;
+
+ public String getPropertyId() {
+ return propertyId;
+ }
+
+ public void setPropertyId(String propertyId) {
+ this.propertyId = propertyId;
+ }
+
+ public PropertyType getPropertyType() {
+ return propertyType;
+ }
+
+ public void setPropertyType(PropertyType propertyType) {
+ this.propertyType = propertyType;
+ }
+
+ public Integer getLength() {
+ return length;
+ }
+
+ public void setLength(Integer length) {
+ this.length = length;
+ }
+
+ @Override
+ public String toString() {
+ return "PropertyConfig{" +
+ "propertyId='" + propertyId + '\'' +
+ ", propertyType=" + propertyType +
+ ", length=" + length +
+ '}';
+ }
+ }
+
+ public List getPropertyList() {
+ return propertyList;
+ }
+
+ public void setPropertyList(List propertyList) {
+ this.propertyList = propertyList;
+ }
+
+ @Override
+ public String toString() {
+ return "DynamicCodeActionParamDTO{" +
+ "propertyList=" + propertyList +
+ '}';
+ }
+
+}
diff --git a/src/main/java/com/cisdi/data/AMETEKSurfaceVision/gateway/SurfaceVisionAliveCheckTask.java b/src/main/java/com/cisdi/data/AMETEKSurfaceVision/gateway/SurfaceVisionAliveCheckTask.java
new file mode 100644
index 0000000..889e6bf
--- /dev/null
+++ b/src/main/java/com/cisdi/data/AMETEKSurfaceVision/gateway/SurfaceVisionAliveCheckTask.java
@@ -0,0 +1,70 @@
+package com.cisdi.data.AMETEKSurfaceVision.gateway;
+
+import com.cisdi.data.sdk.gateway.netty.IoSession;
+import com.cisdi.data.sdk.gateway.netty.SessionFactory;
+import java.util.List;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+import lombok.extern.slf4j.Slf4j;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * 保活时间检测,如果超过指定时间,则关闭连接
+ * @author tzz
+ *
+ */
+@Slf4j
+public class SurfaceVisionAliveCheckTask implements Runnable {
+
+ private SessionFactory sessionFactory;
+ private AtomicBoolean shouldRun = null;
+
+ /**
+ * 会话允许的保活时间,单位秒
+ */
+ private int keepAlive;
+
+ private static final double multiply = 2;
+
+ private static final int sleepInternal = 1000; // 1秒
+
+ public SurfaceVisionAliveCheckTask(int keepAlive, AtomicBoolean shouldRun, SessionFactory sessionFactory) {
+ super();
+ this.keepAlive = keepAlive;
+ this.shouldRun = shouldRun;
+ this.sessionFactory = sessionFactory;
+ }
+
+ @Override
+ public void run() {
+ log.info("启动SurfaceVision心跳及检测心跳线程");
+
+ while (shouldRun != null && shouldRun.get() == true) {
+ try {
+ long now = System.currentTimeMillis();
+
+ List sessions = sessionFactory.getSessions();
+
+ for (IoSession ioSession : sessions) {
+ SurfaceVisionIoSession session = (SurfaceVisionIoSession)ioSession;
+ log.info("{} 定时心跳发送检测", session.gwPrefix());
+ session.sendBeat();
+
+ // 超出指定倍数心跳时间,关闭通道
+ if((now - session.getLastAliveTime()) > (multiply * keepAlive * 1000)) {
+ session.close();
+ log.warn("{} 超出指定倍数{}心跳时间{},单位秒,关闭通道",
+ session.gwPrefix(), multiply, keepAlive);
+ }
+ }
+
+ Thread.sleep(sleepInternal);
+ } catch (Exception e) {
+ log.warn(e.getLocalizedMessage(), e);
+ }
+ }
+
+ log.info("结束SurfaceVision保活超时检测线程");
+ }
+}
diff --git a/src/main/java/com/cisdi/data/AMETEKSurfaceVision/gateway/SurfaceVisionChannelInitializer.java b/src/main/java/com/cisdi/data/AMETEKSurfaceVision/gateway/SurfaceVisionChannelInitializer.java
new file mode 100644
index 0000000..42910a1
--- /dev/null
+++ b/src/main/java/com/cisdi/data/AMETEKSurfaceVision/gateway/SurfaceVisionChannelInitializer.java
@@ -0,0 +1,51 @@
+package com.cisdi.data.AMETEKSurfaceVision.gateway;
+
+import com.cisdi.data.sdk.gateway.netty.SessionFactory;
+import com.cisdi.data.sdk.gateway.netty.impl.AbstractChannelInitializer;
+import com.cisdi.data.sdk.gateway.netty.impl.AbstractIoChannelHandler;
+import com.cisdi.data.sdk.gateway.netty.impl.DefaultIoChannelHandler;
+import io.netty.channel.Channel;
+import io.netty.channel.ChannelHandler;
+import io.netty.handler.timeout.IdleStateHandler;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * 表检仪协议通道初始化
+ *
+ * @author tzz
+ */
+public class SurfaceVisionChannelInitializer extends AbstractChannelInitializer {
+ private SessionFactory factory;
+
+ /**
+ * 构造通道初始化器对象
+ * @param factory 会话工厂
+ */
+ public SurfaceVisionChannelInitializer(SessionFactory factory) {
+ this.factory = factory;
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override
+ protected AbstractIoChannelHandler getTailHandler() {
+ return new DefaultIoChannelHandler(factory);
+ }
+
+ @Override
+ protected List getHandlers() {
+ List handlers = new ArrayList();
+ handlers.add(new SurfaceVisionFrameDecoder());
+ return handlers;
+ }
+
+ @Override
+ protected IdleStateHandler getIdleStateHandler() {
+ return new IdleStateHandler(15, 15, 30);
+ }
+
+ @Override
+ protected ChannelHandler getSslChannelHandler(Channel socketChannel) {
+ return null;
+ }
+}
diff --git a/src/main/java/com/cisdi/data/AMETEKSurfaceVision/gateway/SurfaceVisionConstants.java b/src/main/java/com/cisdi/data/AMETEKSurfaceVision/gateway/SurfaceVisionConstants.java
new file mode 100644
index 0000000..001b4f4
--- /dev/null
+++ b/src/main/java/com/cisdi/data/AMETEKSurfaceVision/gateway/SurfaceVisionConstants.java
@@ -0,0 +1,73 @@
+package com.cisdi.data.AMETEKSurfaceVision.gateway;
+
+/**
+ * 存储表检仪协议常量信息
+ *
+ * @author tzz
+ */
+public final class SurfaceVisionConstants {
+
+ //下发命令返回消息头类型
+
+ /**
+ * 非Query类型命令下发返回消息头,使用starWith判断
+ */
+ public static final String RETURN_HEADER1 = "REPLY_";
+ /**
+ * 默认下发返回消息头(应为下发命令报错时消息返回头),使用equals判断
+ */
+ public static final String RETURN_HEADER2 = "REPLY";
+ /**
+ * Query类型命令下发返回消息头,使用equals判断
+ */
+ public static final String RETURN_HEADER3 = "RESULT";
+
+
+ /**
+ * 命令下发返回成功状态值,ok代表成功
+ */
+ public static final String RETURN_OK = "OK";
+
+ /**
+ * 消息分隔符
+ */
+ public static final String SEPARATE1 = "\\|";
+
+ /**
+ * 消息分隔符
+ */
+ public static final char SEPARATE = '|';
+
+ /**
+ * 消息结束符(除|以外)
+ */
+ public static final char[] END = {'$','\r','\n'};
+
+ /**
+ * Query类型命令下发消息头,使用starWith判断
+ */
+ public static final String QUERY = "QUERY";
+
+ /**
+ * 下发命令计数器最大值
+ */
+ public static final int MAX_COUNT = 100;
+
+ /**
+ * 下发命令正常返回消息最小字段数
+ */
+ public static final int MIN_NORMAL_RETURN = 3;
+
+ /**
+ * 下发命令数据KEY
+ */
+ public static final String S_V_SEND_DATA = "surfaceVisionSendData";
+
+ /**
+ * 返回给平台数据KEY
+ */
+ public static final String S_V_SEND_PLATFORM_DATA = "surfaceVisionSendPlatformData";
+
+ /** 心跳符,$的16进制 */
+ public static final byte BEAT_BYTE = 0x07;
+}
diff --git a/src/main/java/com/cisdi/data/AMETEKSurfaceVision/gateway/SurfaceVisionEncoder.java b/src/main/java/com/cisdi/data/AMETEKSurfaceVision/gateway/SurfaceVisionEncoder.java
new file mode 100644
index 0000000..a13d2e4
--- /dev/null
+++ b/src/main/java/com/cisdi/data/AMETEKSurfaceVision/gateway/SurfaceVisionEncoder.java
@@ -0,0 +1,49 @@
+package com.cisdi.data.AMETEKSurfaceVision.gateway;
+
+import com.cisdi.data.AMETEKSurfaceVision.DynamicCodeActionParamDTO.PropertyConfig;
+import com.cisdi.data.common.exception.BusinessException;
+import io.netty.buffer.ByteBuf;
+import io.netty.buffer.Unpooled;
+import java.nio.charset.StandardCharsets;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * 表检仪编码
+ *
+ * @author zhongda
+ */
+public class SurfaceVisionEncoder {
+
+ /**
+ * 下发命令编码
+ * @param msgKey 消息头,也是平台电文ID
+ * @param propsMap 下发消息
+ * @param propertyConfigList 下发消息点位配置表
+ * @param count 下发消息replayId
+ */
+ public static ByteBuf Encode(String msgKey, Map propsMap, List propertyConfigList, int count) {
+ StringBuffer stringBuffer = new StringBuffer();
+ //组装消息头
+ stringBuffer.append(msgKey).append(SurfaceVisionConstants.SEPARATE);
+ //非Query命令添加replayId
+ if (!msgKey.startsWith(SurfaceVisionConstants.QUERY)){
+ stringBuffer.append(count).append(SurfaceVisionConstants.SEPARATE);
+ }
+ //组装消息体
+ for (PropertyConfig propertyConfig : propertyConfigList){
+ String key = propertyConfig.getPropertyId();
+ stringBuffer.append(propsMap.get(key))
+ .append(SurfaceVisionConstants.SEPARATE);
+ }
+ //组装消息结尾
+ stringBuffer.append(String.valueOf(SurfaceVisionConstants.END));
+
+ //UTF_8编码下发消息
+ byte[] bytes = stringBuffer.toString().getBytes(StandardCharsets.UTF_8);
+ ByteBuf wrappedBuffer = Unpooled.wrappedBuffer(bytes);
+
+ return wrappedBuffer;
+ }
+
+}
diff --git a/src/main/java/com/cisdi/data/AMETEKSurfaceVision/gateway/SurfaceVisionFrameDecoder.java b/src/main/java/com/cisdi/data/AMETEKSurfaceVision/gateway/SurfaceVisionFrameDecoder.java
new file mode 100644
index 0000000..c8df639
--- /dev/null
+++ b/src/main/java/com/cisdi/data/AMETEKSurfaceVision/gateway/SurfaceVisionFrameDecoder.java
@@ -0,0 +1,59 @@
+package com.cisdi.data.AMETEKSurfaceVision.gateway;
+
+import io.netty.buffer.ByteBuf;
+import io.netty.buffer.Unpooled;
+import io.netty.channel.ChannelHandlerContext;
+import io.netty.handler.codec.ByteToMessageDecoder;
+import java.util.List;
+
+/**
+ * 表检仪协议帧解码
+ *
+ * @author tzz
+ */
+public class SurfaceVisionFrameDecoder extends ByteToMessageDecoder {
+ /** 结束符除|第一位,$的16进制 */
+ private byte end = 0x24;
+ /** 结束符除|第二位,\r的16进制 */
+ private byte end1 = 0x0D;
+ /** 结束符除|第三位,\n的16进制 */
+ private byte end2 = 0x0A;
+
+ @Override
+ protected void decode(ChannelHandlerContext ctx, ByteBuf bufferIn, List
+* Description: 标准Socket协议会话实现
+* @author lhp
+* @date 2019年2月12日
+* @version 1.0
+ */
+public class EmiaProIoSession extends AbstractIoSession implements IoSession {
+
+ private long lastAliveTime = System.currentTimeMillis();
+
+ private static Logger logger = LoggerFactory.getLogger(EmiaProIoSession.class);
+
+ private EmiaProSessionFactory factory = null;
+
+ private String deviceId = null;
+
+ private Short prevSeq = EmiaProConsts.Dummy_Sequence;
+
+ protected Set clientDeviceIds = Sets.newConcurrentHashSet();
+
+ /**
+ * 如果有值,代表所有会话都使用这一个设备id,且允许多会话共享此一个设备id,不T除旧会话
+ */
+ private String onlyOneDeviceId;
+
+ public EmiaProIoSession(EmiaProSessionFactory factory, String onlyOneDeviceId) {
+ super();
+ this.factory = factory;
+ this.onlyOneDeviceId = onlyOneDeviceId;
+ }
+
+ @Override
+ public void onRead(Object message) {
+
+ ByteOrder byteOrder = socketGateway == null ? ByteOrder.BIGENDIAN : socketGateway.getInstanceVo().getByteOrder();
+
+ int length = ((ByteBuf)message).readableBytes();
+ ByteBuf inBuf = (ByteBuf)message;
+ String data=inBuf.toString(CharsetUtil.UTF_8);
+ //剔除分隔符,
+ data=data.replace(",","");
+ Integer readerIndex = null;
+
+ try {
+ // 预备处理透传
+ if(socketGateway != null && Boolean.TRUE.equals(socketGateway.getInstanceVo().getNeedPassthrough())) {
+ readerIndex = inBuf.readerIndex();
+ }
+
+ if(socketGateway!= null && Boolean.TRUE.equals(socketGateway.getInstanceVo().getLogOpen())) {
+ logger.info("EMIA-PRO {} 报文长度:{} vo:{} ", getChannel(), length, data);
+ }
+
+
+ if(serviceProvider != null && socketGateway != null&& onlyOneDeviceId!=null) {
+ String runId = socketGateway.getInstanceVo().getRunId();
+ RouteService routeService = (RouteService)serviceProvider.getByName(ServiceName.Route);
+ DeviceVo deviceVo = routeService.findDeviceByDeviceId(onlyOneDeviceId);
+ if(deviceVo == null || !Strings.equals(runId, deviceVo.getRunId())) {
+ logger.error("查找不到 deviceId={} 的设备或此设备和本连接未正确配置关联,关闭当前会话", onlyOneDeviceId);
+ close();
+ }
+ }
+
+ // 需要检查之前是否存在,必须为多设备支持才如此, 如果存在,则T掉之前的会话
+ if(factory != null && onlyOneDeviceId != null) {
+ IoSession ioSession = factory.getSessionByDeviceId(onlyOneDeviceId);
+ // 发现了已有设备会话,T掉
+ if(ioSession != null && ioSession != this) {
+ logger.error("发现设备:{}已存在会话:{},同一设备只能绑定一个会话,自动将旧会话关闭",
+ onlyOneDeviceId, ioSession.getChannel());
+ EmiaProIoSession session = (EmiaProIoSession)ioSession;
+ session.close();
+ }
+ factory.newDeviceConnect(onlyOneDeviceId, this);
+ }
+
+ lastAliveTime = System.currentTimeMillis();
+
+
+ if(StringUtils.isNotBlank(data) && serviceProvider != null && socketGateway != null) {
+ SendService service = (SendService)serviceProvider.getByName(ServiceName.Send);
+
+ SocketMessage socketMessage = socketGateway.buildSocketMessage();
+ socketMessage.setDeviceId(onlyOneDeviceId);
+ socketMessage.setData(data.getBytes());
+ socketMessage.setMsgKey("default");
+ socketMessage.getPropsMap().put("charset", Charset.forName("UTF-8"));
+
+ service.sendMessage(socketMessage);
+ }else {
+ String hexString = HexUtils.toHexString(data.getBytes());
+ logger.info("长度:{},报文:{}", data.length(), hexString);
+ }
+ } finally {
+ if(readerIndex != null) { // 需要透传
+ inBuf.readerIndex(readerIndex);
+ passthrough(inBuf);
+ }
+ }
+
+ }
+
+ private void passthrough(ByteBuf passthroughBuf) {
+ try {
+ if(serviceProvider != null && socketGateway != null) {
+ PassthroughService service = (PassthroughService)serviceProvider.getByName(ServiceName.Passthrough);
+ service.transfer(passthroughBuf, socketGateway.getInstanceVo().getPassthroughAddress());
+ }
+ }catch (Exception e) {
+ logger.error("tcp标准协议透传失败:" + e.getLocalizedMessage(), e);
+ }
+ }
+
+ @Override
+ public void onOpen() {
+ super.onOpen();
+ logger.info("建立连接,channel:{}", getChannel());
+ }
+
+ @Override
+ public void onClose() {
+ super.onClose();
+ factory = null;
+ }
+
+ public void close() {
+ if(getChannel() != null) {
+ try {
+ getChannel().close().sync();
+ } catch (InterruptedException e) {
+ logger.warn(e.getLocalizedMessage(), e);
+ }
+ }
+ }
+
+ private String gwPrefixCache = null;
+
+ public String gwPrefix() {
+ if(gwPrefixCache != null) {
+ return gwPrefixCache;
+ }
+
+ gwPrefixCache = "";
+ if(socketGateway != null && socketGateway.getInstanceVo() != null) {
+ gwPrefixCache = "网关Id:" + socketGateway.getInstanceVo().getRunId() + "连接:" + getChannel() + " ";
+ }else {
+ gwPrefixCache = "连接:" + getChannel() + " ";
+ }
+
+ return gwPrefixCache;
+ }
+
+ public long getLastAliveTime() {
+ return lastAliveTime;
+ }
+
+ @Override
+ public String[] getDeviceIds() {
+ String cpDeviceId = deviceId;
+ String[] result = new String[cpDeviceId == null ? 0 : 1];
+ if(cpDeviceId != null) {
+ result[0] = cpDeviceId;
+ }
+ return result;
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/com/cisdi/data/emserver/gateway/impl/EmiaProSessionFactory.java b/src/main/java/com/cisdi/data/emserver/gateway/impl/EmiaProSessionFactory.java
new file mode 100644
index 0000000..bff7598
--- /dev/null
+++ b/src/main/java/com/cisdi/data/emserver/gateway/impl/EmiaProSessionFactory.java
@@ -0,0 +1,23 @@
+package com.cisdi.data.emserver.gateway.impl;
+
+import java.util.UUID;
+
+import com.cisdi.data.sdk.gateway.netty.IoSession;
+import com.cisdi.data.sdk.gateway.netty.impl.AbstractSessionFactory;
+
+public class EmiaProSessionFactory extends AbstractSessionFactory {
+
+ private String onlyOneDeviceId;
+
+ public EmiaProSessionFactory(String onlyoneDeviceId) {
+ this.onlyOneDeviceId = onlyoneDeviceId;
+ }
+
+ @Override
+ public IoSession newSession() {
+ IoSession session = new EmiaProIoSession(this, onlyOneDeviceId);
+ session.init(UUID.randomUUID().toString(), provider, socketGateway);
+ sessionSet.add(session);
+ return session;
+ }
+}
diff --git a/src/main/java/com/cisdi/data/emserver/gateway/impl/EmiaProVo.java b/src/main/java/com/cisdi/data/emserver/gateway/impl/EmiaProVo.java
new file mode 100644
index 0000000..19775cf
--- /dev/null
+++ b/src/main/java/com/cisdi/data/emserver/gateway/impl/EmiaProVo.java
@@ -0,0 +1,26 @@
+//package com.cisdi.data.emia.gateway.impl;
+//
+//import java.io.Serializable;
+//
+//import com.alibaba.fastjson.JSON;
+//
+//public class EmiaProVo implements Serializable {
+// private static final long serialVersionUID = 1251102466675497863L;
+//
+// private byte[] body;
+//
+//
+// public byte[] getBody() {
+// return body;
+// }
+//
+// public void setBody(byte[] body) {
+// this.body = body;
+// }
+//
+// @Override
+// public String toString() {
+// String jsonString = JSON.toJSONString(this);
+// return jsonString;
+// }
+//}
diff --git a/src/main/java/com/cisdi/data/emserver/gateway/impl/EmiaProVoDecode.java b/src/main/java/com/cisdi/data/emserver/gateway/impl/EmiaProVoDecode.java
new file mode 100644
index 0000000..bc88c9f
--- /dev/null
+++ b/src/main/java/com/cisdi/data/emserver/gateway/impl/EmiaProVoDecode.java
@@ -0,0 +1,58 @@
+//package com.cisdi.data.emia.gateway.impl;
+//
+//import com.cisdi.data.sdk.enums.ByteOrder;
+//
+//import io.netty.buffer.ByteBuf;
+//
+///**
+// *
+//* Title: StandardVoDecode.java
+//* Description: 将报文做下一步分解
+//* @author lhp
+//* @date 2019年3月22日
+//* @version 1.0
+// */
+//public class EmiaProVoDecode {
+//
+// public static EmiaProVo Decode(ByteBuf in, ByteOrder byteOrder) {
+//
+// EmiaProVo result = new EmiaProVo();
+//
+// if(ByteOrder.LITTLEENDIAN.code().equals(byteOrder.code())) {
+// short length = in.readShortLE();
+// short msgId = in.readShortLE();
+// short seq = in.readShortLE();
+// short reserved = in.readShortLE();
+// int deviceId = in.readIntLE();
+// int destDeviceId = in.readIntLE();
+//
+// result.setLength(length);
+// result.setMsgKey(String.valueOf(msgId));
+// result.setSeq(seq);
+// result.setReserved(reserved);
+// result.setDeviceId(String.valueOf(deviceId));
+// result.setDestDeviceId(String.valueOf(destDeviceId));
+// }else {
+// short length = in.readShort();
+// short msgId = in.readShort();
+// short seq = in.readShort();
+// short reserved = in.readShort();
+// int deviceId = in.readInt();
+// int destDeviceId = in.readInt();
+//
+// result.setLength(length);
+// result.setMsgKey(String.valueOf(msgId));
+// result.setSeq(seq);
+// result.setReserved(reserved);
+// result.setDeviceId(String.valueOf(deviceId));
+// result.setDestDeviceId(String.valueOf(destDeviceId));
+// }
+//
+// byte[] bodyBytes = new byte[result.getLength() - 16];
+// in.readBytes(bodyBytes);
+//
+// result.setBody(bodyBytes);
+//
+// return result;
+// }
+//}
diff --git a/src/main/java/com/cisdi/data/emserver/gateway/impl/EmiaProVoEncoder.java b/src/main/java/com/cisdi/data/emserver/gateway/impl/EmiaProVoEncoder.java
new file mode 100644
index 0000000..9f2432b
--- /dev/null
+++ b/src/main/java/com/cisdi/data/emserver/gateway/impl/EmiaProVoEncoder.java
@@ -0,0 +1,96 @@
+//package com.cisdi.data.emia.gateway.impl;
+//
+//import org.slf4j.Logger;
+//import org.slf4j.LoggerFactory;
+//
+//import com.cisdi.data.common.exception.BusinessException;
+//import com.cisdi.data.sdk.enums.ByteOrder;
+//import com.cisdi.data.sdk.procotol.message.SocketReturnMessage;
+//
+//import io.netty.buffer.ByteBuf;
+//import io.netty.buffer.Unpooled;
+//import io.netty.channel.ChannelHandlerContext;
+//import io.netty.handler.codec.MessageToByteEncoder;
+//
+//public class EmiaProVoEncoder extends MessageToByteEncoder {
+// private ByteOrder byteOrder = ByteOrder.BIGENDIAN;
+//
+// public EmiaProVoEncoder(ByteOrder byteOrder) {
+// this.byteOrder = byteOrder;
+// }
+//
+// private static final Logger logger = LoggerFactory.getLogger(EmiaProVoEncoder.class);
+//
+// private Object lockObj = new Object();
+//
+// @Override
+// protected void encode(ChannelHandlerContext ctx, EmiaProVo msg, ByteBuf out) throws Exception {
+// try {
+// if(msg == null) {
+// throw new BusinessException("encode EmiaProVo is null");
+// }
+//
+// if(msg.getBody() == null) {
+// throw new BusinessException("消息体为null");
+// }
+//
+// synchronized (lockObj) {
+// if(ByteOrder.LITTLEENDIAN.code().equals(byteOrder.code())) {
+// out.writeShortLE(16 + msg.getBody().length);
+// out.writeShortLE(msg.getMsgKey() == null ? 0 : Short.valueOf(msg.getMsgKey()));
+// out.writeShortLE(0);
+// out.writeShortLE(0);
+// out.writeIntLE(msg.getDeviceId() == null ? 0 : Integer.valueOf(msg.getDeviceId()));
+// out.writeIntLE(msg.getDestDeviceId() == null ? 0 : Integer.valueOf(msg.getDestDeviceId()));
+// out.writeBytes(msg.getBody());
+// }else {
+// out.writeShort(16 + msg.getBody().length);
+// out.writeShort(msg.getMsgKey() == null ? 0 : Short.valueOf(msg.getMsgKey()));
+// out.writeShort(0);
+// out.writeShort(0);
+// out.writeInt(msg.getDeviceId() == null ? 0 : Integer.valueOf(msg.getDeviceId()));
+// out.writeInt(msg.getDestDeviceId() == null ? 0 : Integer.valueOf(msg.getDestDeviceId()));
+// out.writeBytes(msg.getBody());
+// }
+// }
+// } catch (Exception e) {
+// logger.warn("编码消息发生异常" + e.getMessage(), e);
+// throw e;
+// }
+// }
+//
+// public static byte[] encode(SocketReturnMessage msg, ByteOrder order) {
+// ByteBuf out = null;
+// byte[] result = null;
+//
+// try {
+// out = Unpooled.buffer();
+// if(ByteOrder.LITTLEENDIAN.code().equals(order.code())) {
+// out.writeShortLE(16 + msg.getData().length);
+// out.writeShortLE(msg.getMsgKey() == null ? 0 : Short.valueOf(msg.getMsgKey()));
+// out.writeShortLE(0);
+// out.writeShortLE(0);
+// out.writeIntLE(0);
+// out.writeIntLE(msg.getDeviceId() == null ? 0 : Integer.valueOf(msg.getDeviceId()));
+// out.writeBytes(msg.getData());
+// }else {
+// out.writeShort(16 + msg.getData().length);
+// out.writeShort(msg.getMsgKey() == null ? 0 : Short.valueOf(msg.getMsgKey()));
+// out.writeShort(0);
+// out.writeShort(0);
+// out.writeInt(0);
+// out.writeInt(msg.getDeviceId() == null ? 0 : Integer.valueOf(msg.getDeviceId()));
+// out.writeBytes(msg.getData());
+// }
+//
+// result = new byte[out.readableBytes()];
+// out.readBytes(result);
+// } finally {
+// if(out != null) {
+// out.release();
+// }
+// }
+//
+// return result;
+// }
+//}
\ No newline at end of file
diff --git a/src/main/java/com/cisdi/data/emserver/gateway/impl/SocketClient.java b/src/main/java/com/cisdi/data/emserver/gateway/impl/SocketClient.java
new file mode 100644
index 0000000..b6c7d06
--- /dev/null
+++ b/src/main/java/com/cisdi/data/emserver/gateway/impl/SocketClient.java
@@ -0,0 +1,100 @@
+package com.cisdi.data.emserver.gateway.impl;
+
+import java.io.DataOutputStream;
+import java.io.IOException;
+import java.io.UnsupportedEncodingException;
+import java.net.InetSocketAddress;
+import java.net.Socket;
+import java.net.UnknownHostException;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.cisdi.data.common.exception.BusinessException;
+
+public class SocketClient {
+
+ private static Logger logger = LoggerFactory.getLogger(SocketClient.class);
+
+ //连接服务器的IP
+ public String ip = null;
+ //连接服务器的端口
+ public Integer port = null;
+ //套节字对象
+ private Socket socket = null;
+ //超时时间,以毫秒为单位
+ private Integer timeout = 2000;
+
+ public SocketClient(String ip, Integer port) {
+ this.ip = ip;
+ this.port = port;
+ }
+
+ /**
+ * 打开连接
+ */
+ public void open(){
+ try {
+ socket = new Socket();
+ socket.connect(new InetSocketAddress(ip, port), timeout);//设置连接请求超时时间
+ socket.setKeepAlive(true);//开启保持活动状态的套接字
+ socket.setSoTimeout(timeout);//设置超时时间
+ } catch (UnknownHostException e) {
+ logger.error(e.getMessage(),e);
+ } catch (IOException e) {
+ logger.error(e.getMessage(),e);
+ }
+ }
+
+ /**
+ * 发送数据(同步返回)
+ * @param data
+ * @return
+ */
+ public String send(byte[] data) {
+
+ String resultString = null;
+
+ DataOutputStream out = null;
+ try {
+ out = new DataOutputStream(socket.getOutputStream());
+
+ out.write(data);
+ out.flush();
+
+ } catch (Exception e) {
+ throw new BusinessException(e.getMessage(), e);
+ } finally {
+ if (out != null){
+ try {
+ out.close();
+ } catch (IOException e) {
+ logger.error("DataOutputStream out 关闭异常:", e);
+ }
+ }
+ }
+
+ return resultString;
+ }
+
+ public void close() {
+ if(socket != null) {
+ try {
+ socket.close();
+ } catch (IOException e) {
+ logger.warn(e.getLocalizedMessage(), e);
+ }
+ }
+ }
+
+
+ public static void main(String[] args) throws UnsupportedEncodingException
+ {
+ String testmsg=(char)0x02+",T1234,S0123456789ABCDEFGHIJ,C12.345678 ,S12.345678 ,W 0.987654 ,C 36,S 45,M012,H12:00,D01M12Y10,"+(char)0x0a+(char)0x0b+",A001196302303,N "+(char)0x0a+(char)0x0b+(char)0x03;
+
+ SocketClient socketClient=new SocketClient("10.76.48.66",5002);
+ socketClient.open();
+ socketClient.send(testmsg.getBytes());
+ socketClient.close();
+ }
+}
diff --git a/src/main/java/com/cisdi/data/emserver/gateway/vo/OtherParameterVo.java b/src/main/java/com/cisdi/data/emserver/gateway/vo/OtherParameterVo.java
new file mode 100644
index 0000000..0ac4d9c
--- /dev/null
+++ b/src/main/java/com/cisdi/data/emserver/gateway/vo/OtherParameterVo.java
@@ -0,0 +1,27 @@
+package com.cisdi.data.emserver.gateway.vo;
+
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.List;
+
+import com.alibaba.fastjson.JSON;
+
+public class OtherParameterVo implements Serializable {
+ private static final long serialVersionUID = 2247503397521281192L;
+
+ private List routes = new ArrayList();
+
+ public List getRoutes() {
+ return routes;
+ }
+
+ public void setRoutes(List routes) {
+ this.routes = routes;
+ }
+
+ @Override
+ public String toString() {
+ String jsonString = JSON.toJSONString(this);
+ return jsonString;
+ }
+}
diff --git a/src/main/java/com/cisdi/data/emserver/gateway/vo/RouteVo.java b/src/main/java/com/cisdi/data/emserver/gateway/vo/RouteVo.java
new file mode 100644
index 0000000..932c23e
--- /dev/null
+++ b/src/main/java/com/cisdi/data/emserver/gateway/vo/RouteVo.java
@@ -0,0 +1,46 @@
+package com.cisdi.data.emserver.gateway.vo;
+
+import java.io.Serializable;
+
+import com.alibaba.fastjson.JSON;
+
+/**
+ * 下发到设备的电文路由规则
+ * @author cup
+ *
+ */
+public class RouteVo implements Serializable {
+ private static final long serialVersionUID = -1745777213966420113L;
+
+ /**
+ * 电文key
+ */
+ private String msgKey;
+
+ /**
+ * 形如 192.168.5.9:9002 的字符串
+ */
+ private String ipAndPort;
+
+ public String getMsgKey() {
+ return msgKey;
+ }
+
+ public void setMsgKey(String msgKey) {
+ this.msgKey = msgKey;
+ }
+
+ public String getIpAndPort() {
+ return ipAndPort;
+ }
+
+ public void setIpAndPort(String ipAndPort) {
+ this.ipAndPort = ipAndPort;
+ }
+
+ @Override
+ public String toString() {
+ String jsonString = JSON.toJSONString(this);
+ return jsonString;
+ }
+}
diff --git a/src/main/java/com/cisdi/data/util/HexUtil.java b/src/main/java/com/cisdi/data/util/HexUtil.java
new file mode 100644
index 0000000..471c9b4
--- /dev/null
+++ b/src/main/java/com/cisdi/data/util/HexUtil.java
@@ -0,0 +1,74 @@
+package com.cisdi.data.util;
+
+public class HexUtil {
+ /*
+ * Convert byte[] to hex string.这里我们可以将byte转换成int,然后利用Integer.toHexString(int)来转换成16进制字符串。
+ * @param src byte[] data
+ * @return hex string
+ */
+ public static String bytesToHexString(byte[] src){
+ StringBuilder stringBuilder = new StringBuilder("");
+ if (src == null || src.length <= 0) {
+ return null;
+ }
+ for (int i = 0; i < src.length; i++) {
+ int v = src[i] & 0xFF;
+ String hv = Integer.toHexString(v);
+ if (hv.length() < 2) {
+ stringBuilder.append(0);
+ }
+ stringBuilder.append(hv);
+ }
+ return stringBuilder.toString();
+ }
+
+ /**
+ * Convert hex string to byte[]
+ * @param hexString the hex string
+ * @return byte[]
+ */
+ public static byte[] hexStringToBytes(String hexString) {
+ if (hexString == null || hexString.equals("")) {
+ return null;
+ }
+ hexString = hexString.toUpperCase();
+ int length = hexString.length() / 2;
+ char[] hexChars = hexString.toCharArray();
+ byte[] d = new byte[length];
+ for (int i = 0; i < length; i++) {
+ int pos = i * 2;
+ d[i] = (byte) (charToByte(hexChars[pos]) << 4 | charToByte(hexChars[pos + 1]));
+ }
+ return d;
+ }
+
+ /**
+ * Convert char to byte
+ * @param c char
+ * @return byte
+ */
+ private static byte charToByte(char c) {
+ return (byte) "0123456789ABCDEF".indexOf(c);
+ }
+
+ //将指定byte数组以16进制的形式打印到控制台
+ public static void printHexString( byte[] b) {
+ for (int i = 0; i < b.length; i++) {
+ String hex = Integer.toHexString(b[i] & 0xFF);
+ if (hex.length() == 1) {
+ hex = '0' + hex;
+ }
+ System.out.print(hex.toUpperCase() );
+ }
+ }
+
+ public static Byte Byte(int value) {
+ Byte result = (byte)(value & 0xff);
+ return result;
+ }
+
+ public static Short Short(int value) {
+ Short result = (short)(value & 0xffff);
+ return result;
+ }
+}
diff --git a/src/test/java/com/cisdi/data/RFID/gateway/test/StartRfidTest.java b/src/test/java/com/cisdi/data/RFID/gateway/test/StartRfidTest.java
new file mode 100644
index 0000000..a46b9bc
--- /dev/null
+++ b/src/test/java/com/cisdi/data/RFID/gateway/test/StartRfidTest.java
@@ -0,0 +1,39 @@
+package com.cisdi.data.RFID.gateway.test;
+
+import com.cisdi.data.RFID.gateway.RfidClient;
+import com.cisdi.data.RFID.gateway.RfidDaemonTask;
+import com.cisdi.data.RFID.gateway.RfidTaskStartVo;
+import com.cisdi.data.sdk.param.PullSocketParam;
+
+import java.util.concurrent.atomic.AtomicBoolean;
+
+/**
+ * @program: dacoo-data-sdk-DI-client
+ * @description: RFID测试
+ * @author: Mo
+ * @created: 2021/06/02 14:46
+ */
+public class StartRfidTest {
+ public static void main(String[] args) {
+ try {
+ RfidTaskStartVo taskStartVo = new RfidTaskStartVo();
+ taskStartVo.setDeviceId("1000800");
+ taskStartVo.setShouldRun(new AtomicBoolean(true));
+ PullSocketParam socketParam = new PullSocketParam();
+ socketParam.setPullInternal(2000);
+ socketParam.setPeerIp("127.0.0.1");
+ socketParam.setPeerPort(2112);
+ socketParam.setTimeout(5000);
+ taskStartVo.setSocketParam(socketParam);
+
+ RfidClient fireClient = new RfidClient(taskStartVo);
+ RfidDaemonTask daemonTask = new RfidDaemonTask(fireClient, taskStartVo);
+ Thread thread = new Thread(daemonTask, "Rfid-Daemon-Task" );
+ thread.start();
+ Thread.sleep(100000000);
+ } catch (Exception e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+ }
+}
\ No newline at end of file