1 files modified
13 files added
| New file |
| | |
| | | /gradlew text eol=lf |
| | | *.bat text eol=crlf |
| | | *.jar binary |
| | |
| | | *.class |
| | | HELP.md |
| | | .gradle |
| | | build/ |
| | | !gradle/wrapper/gradle-wrapper.jar |
| | | !**/src/main/**/build/ |
| | | !**/src/test/**/build/ |
| | | |
| | | # Mobile Tools for Java (J2ME) |
| | | .mtj.tmp/ |
| | | ### STS ### |
| | | .apt_generated |
| | | .classpath |
| | | .factorypath |
| | | .project |
| | | .settings |
| | | .springBeans |
| | | .sts4-cache |
| | | bin/ |
| | | !**/src/main/**/bin/ |
| | | !**/src/test/**/bin/ |
| | | |
| | | # Package Files # |
| | | *.jar |
| | | *.war |
| | | *.ear |
| | | ### IntelliJ IDEA ### |
| | | .idea |
| | | *.iws |
| | | *.iml |
| | | *.ipr |
| | | out/ |
| | | !**/src/main/**/out/ |
| | | !**/src/test/**/out/ |
| | | |
| | | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml |
| | | hs_err_pid* |
| | | ### NetBeans ### |
| | | /nbproject/private/ |
| | | /nbbuild/ |
| | | /dist/ |
| | | /nbdist/ |
| | | /.nb-gradle/ |
| | | |
| | | ### VS Code ### |
| | | .vscode/ |
| New file |
| | |
| | | plugins { |
| | | id 'java' |
| | | id 'maven-publish' // or 'maven' |
| | | id 'org.springframework.boot' version '2.7.18' |
| | | id 'io.spring.dependency-management' version '1.0.15.RELEASE' |
| | | } |
| | | repositories { |
| | | maven { |
| | | url 'http://1.95.69.137:9081/repository/maven-public/' |
| | | allowInsecureProtocol = true |
| | | } |
| | | mavenLocal() |
| | | maven { url 'https://maven.aliyun.com/repository/public' } |
| | | maven { url 'https://maven.aliyun.com/repository/center' } |
| | | mavenCentral() |
| | | } |
| | | |
| | | bootJar.enabled = true |
| | | jar.enabled = true |
| | | |
| | | group = 'com.eternal' |
| | | version = '1.0.0' |
| | | |
| | | java { |
| | | sourceCompatibility = '1.8' |
| | | targetCompatibility = '1.8' |
| | | } |
| | | |
| | | dependencies { |
| | | implementation fileTree(dir: '/lib', include: ['*.jar']) |
| | | |
| | | implementation 'com.eternal:com.eternal.rfid.Common:2.1.20' |
| | | implementation 'com.eternal:app-params-config:1.1.16' |
| | | |
| | | implementation 'org.springframework.boot:spring-boot-starter-web' |
| | | implementation 'org.springframework.boot:spring-boot-starter-thymeleaf' |
| | | |
| | | implementation 'com.alibaba.fastjson2:fastjson2:2.0.46' |
| | | |
| | | compileOnly 'org.projectlombok:lombok' |
| | | annotationProcessor 'org.projectlombok:lombok' |
| | | testCompileOnly('org.projectlombok:lombok') |
| | | testAnnotationProcessor('org.projectlombok:lombok') |
| | | testImplementation 'org.springframework.boot:spring-boot-starter-test' |
| | | } |
| | | |
| | | // 编译相关参数 |
| | | compileJava { |
| | | sourceCompatibility = JavaVersion.VERSION_1_8 |
| | | targetCompatibility = JavaVersion.VERSION_1_8 |
| | | options.encoding = 'UTF-8' |
| | | options.compilerArgs = [ |
| | | '-Xlint:all', '-Xlint:-processing' |
| | | ] |
| | | } |
| | | // 定义 sourcesJar 任务 |
| | | task sourcesJar(type: Jar, dependsOn: classes) { |
| | | archiveClassifier.set('sources') |
| | | from sourceSets.main.allSource |
| | | } |
| | | publishing { |
| | | publications { |
| | | mavenJava(MavenPublication) { |
| | | from components.java |
| | | // 如果需要发布源码 JAR 文件 |
| | | artifact sourcesJar |
| | | } |
| | | } |
| | | repositories { |
| | | maven { |
| | | name = 'localRepo' |
| | | url = "file://${buildDir}/repo" |
| | | } |
| | | maven { |
| | | name = 'releases' |
| | | allowInsecureProtocol = true // 显式允许不安全的 HTTP 协议 |
| | | url = 'http://1.95.69.137:9081/repository/maven-releases/' |
| | | credentials { |
| | | username = "admin" |
| | | password = "admin123" |
| | | } |
| | | } |
| | | } |
| | | } |
| | | |
| | | tasks.named('test') { |
| | | useJUnitPlatform() |
| | | } |
| New file |
| | |
| | | distributionBase=GRADLE_USER_HOME |
| | | distributionPath=wrapper/dists |
| | | distributionUrl=https\://mirrors.cloud.tencent.com/gradle/gradle-8.5-bin.zip |
| | | networkTimeout=10000 |
| | | validateDistributionUrl=true |
| | | zipStoreBase=GRADLE_USER_HOME |
| | | zipStorePath=wrapper/dists |
| New file |
| | |
| | | #!/bin/sh |
| | | |
| | | # |
| | | # Copyright © 2015 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. |
| | | # |
| | | # SPDX-License-Identifier: Apache-2.0 |
| | | # |
| | | |
| | | ############################################################################## |
| | | # |
| | | # 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 -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || 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 |
| | | |
| | | |
| | | |
| | | # 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" ) |
| | | |
| | | 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, 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" \ |
| | | -jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \ |
| | | "$@" |
| | | |
| | | # 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" "$@" |
| New file |
| | |
| | | @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 |
| | | @rem SPDX-License-Identifier: Apache-2.0 |
| | | @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 |
| | | |
| | | |
| | | |
| | | @rem Execute Gradle |
| | | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* |
| | | |
| | | :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 |
| New file |
| | |
| | | rootProject.name = 'com.eternal.rfid.equip.UF3Reader' |
| New file |
| | |
| | | package com.eternal.rfid; |
| | | |
| | | |
| | | import com.eternal.event.DeviceWorkEvent; |
| | | import com.eternal.monitor.AbstractEquipmentManager; |
| | | import com.eternal.monitor.constant.ErrorTypeEnums; |
| | | import com.eternal.monitor.constant.StatusTypeEnums; |
| | | import com.eternal.monitor.entity.Device; |
| | | import com.eternal.monitor.entity.DeviceErrorType; |
| | | import com.eternal.monitor.entity.DeviceStatus; |
| | | import com.eternal.monitor.entity.DeviceType; |
| | | import com.eternal.rfid.params.UF3ReaderConfig; |
| | | import com.eternal.rfid.service.UF3RFIDReader; |
| | | import lombok.Getter; |
| | | import lombok.extern.slf4j.Slf4j; |
| | | import org.springframework.beans.factory.annotation.Autowired; |
| | | import org.springframework.context.ApplicationEventPublisher; |
| | | import org.springframework.context.event.EventListener; |
| | | import org.springframework.scheduling.TaskScheduler; |
| | | import org.springframework.scheduling.annotation.Scheduled; |
| | | import org.springframework.stereotype.Service; |
| | | |
| | | import javax.annotation.PostConstruct; |
| | | import javax.annotation.PreDestroy; |
| | | import java.util.ArrayList; |
| | | import java.util.List; |
| | | import java.util.concurrent.atomic.AtomicInteger; |
| | | |
| | | /** |
| | | * description: 略 |
| | | * |
| | | * @author senzhi |
| | | * @since 2026/1/19 17:14 |
| | | */ |
| | | @Slf4j |
| | | @Service("UF3RFIDReader") |
| | | public class RfidReaderServer extends AbstractEquipmentManager { |
| | | @Autowired |
| | | private ApplicationEventPublisher eventPublisher; |
| | | |
| | | @Autowired |
| | | private UF3ReaderConfig readerConfig; |
| | | |
| | | @Getter |
| | | private final List<UF3RFIDReader> readers = new ArrayList<>(); |
| | | /** |
| | | * 是否允许读写器工作 |
| | | */ |
| | | private boolean enableWork = false; |
| | | |
| | | public void setEnableWork(boolean enableWork) { |
| | | log.info("RfidReaderServer work mode changed. from {} --> {}", this.enableWork, enableWork); |
| | | this.enableWork = enableWork; |
| | | } |
| | | |
| | | @EventListener(DeviceWorkEvent.class) |
| | | public void onDeviceWorkEvent(DeviceWorkEvent event) { |
| | | setEnableWork(event.isEnableWork()); |
| | | } |
| | | |
| | | private final static int maxTryTime = 3; |
| | | |
| | | private final AtomicInteger currentRunIndex = new AtomicInteger(-1); |
| | | private final AtomicInteger tryTime = new AtomicInteger(0); |
| | | |
| | | @PostConstruct |
| | | public void init() { |
| | | String readerKey = readerConfig.getReaderKey(); |
| | | String[] antennasKey = new String[4]; |
| | | for (int j = 0; j < 4; j++) { |
| | | antennasKey[j] = "reader" + 1 + "-antenna" + (j + 1); |
| | | } |
| | | UF3RFIDReader uf3RFIDReader = new UF3RFIDReader(readerKey, "uf3-rfid-reader", antennasKey, "reader-antenna"); |
| | | readers.add(uf3RFIDReader); |
| | | } |
| | | |
| | | @Override |
| | | public void start() { |
| | | for (int i = 0; i < readers.size(); i++) { |
| | | UF3RFIDReader uf3RFIDReader = readers.get(i); |
| | | uf3RFIDReader.setUpdateDeviceStatusConsumer(this::updateDeviceStatus); |
| | | uf3RFIDReader.setEventConsumer(event -> eventPublisher.publishEvent(event)); |
| | | uf3RFIDReader.init(i, readerConfig); |
| | | |
| | | } |
| | | log.info("RfidReaderServer create finished,reader number:{}", readers.size()); |
| | | } |
| | | |
| | | @Scheduled(initialDelay = 100, fixedDelay = 3000) |
| | | public void checkingTimer() { |
| | | try { |
| | | if (enableWork) { |
| | | if (currentRunIndex.get() < 0) { |
| | | currentRunIndex.set(0); |
| | | } |
| | | |
| | | UF3RFIDReader uf3RFIDReader = readers.get(currentRunIndex.get()); |
| | | if (uf3RFIDReader.isOnline()) { |
| | | log.debug("ZebraReader<ip:{}> is online", uf3RFIDReader.getIp()); |
| | | return; |
| | | } |
| | | |
| | | log.info("Try to connect to ZebraReader<ip:{}>,attempts:{}", uf3RFIDReader.getIp(), tryTime.get()); |
| | | uf3RFIDReader.start(); |
| | | if (uf3RFIDReader.isOnline()) { |
| | | tryTime.set(0); |
| | | return; |
| | | } |
| | | |
| | | // 超过最大尝试次数,切换下一个读写器 |
| | | if (tryTime.incrementAndGet() > maxTryTime) { |
| | | log.warn("The ZebraReader<{}> exceeds the maximum attempts number<{}>, switch to the next reader", uf3RFIDReader.getIp(), maxTryTime); |
| | | |
| | | // 重置尝试次数 |
| | | tryTime.set(0); |
| | | // 切换下一个读写器 |
| | | currentRunIndex.incrementAndGet(); |
| | | if (currentRunIndex.get() >= readers.size()) { |
| | | currentRunIndex.set(0); |
| | | } |
| | | } |
| | | } else { |
| | | if (currentRunIndex.get() < 0) { |
| | | log.debug("The node is not ACTIVE, do not connect to RFIDReader"); |
| | | return; |
| | | } |
| | | UF3RFIDReader zebraRFIDReader = readers.get(currentRunIndex.get()); |
| | | zebraRFIDReader.disconnectReader(); |
| | | currentRunIndex.set(-1); |
| | | |
| | | log.info("The node is not ACTIVE, disconnect from RFIDReader"); |
| | | } |
| | | } catch (Exception e) { |
| | | log.error("RfidReaderServer checkingTimer error", e); |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * 启动读 |
| | | */ |
| | | public void startRead() { |
| | | if (enableWork) { |
| | | if (currentRunIndex.get() < 0) { |
| | | log.warn("It is not working and cannot be started."); |
| | | return; |
| | | } |
| | | UF3RFIDReader zebraRFIDReader = readers.get(currentRunIndex.get()); |
| | | try { |
| | | zebraRFIDReader.startRead(); |
| | | } catch (Exception e) { |
| | | log.error("Failed to start read.", e); |
| | | } |
| | | |
| | | } |
| | | |
| | | } |
| | | |
| | | /** |
| | | * 停止读 |
| | | */ |
| | | public void stopRead() { |
| | | if (enableWork) { |
| | | if (currentRunIndex.get() < 0) { |
| | | log.warn("It is not working and cannot be stopped."); |
| | | return; |
| | | } |
| | | UF3RFIDReader zebraRFIDReader = readers.get(currentRunIndex.get()); |
| | | zebraRFIDReader.stopRead(); |
| | | } |
| | | } |
| | | |
| | | |
| | | @PreDestroy |
| | | public void destroy() { |
| | | if (enableWork) { |
| | | if (currentRunIndex.get() < 0) { |
| | | return; |
| | | } |
| | | |
| | | UF3RFIDReader zebraRFIDReader = readers.get(currentRunIndex.get()); |
| | | zebraRFIDReader.disconnectReader(); |
| | | currentRunIndex.set(-1); |
| | | |
| | | log.info("Close the rfid-reader before closing the application"); |
| | | } |
| | | } |
| | | |
| | | @Override |
| | | public void initDevices() { |
| | | for (UF3RFIDReader reader : readers) { |
| | | int index = reader.getIndex(); |
| | | // 添加设备类型 |
| | | DeviceType readerType = new DeviceType(); |
| | | readerType.setDeviceTypeKey("zebra-rfid-reader"); |
| | | readerType.setName("东集读写器"); |
| | | readerType.setTypeDescription("东集读写器"); |
| | | addDeviceType(readerType); |
| | | DeviceType antennaType = new DeviceType(); |
| | | antennaType.setDeviceTypeKey("reader-antenna"); |
| | | antennaType.setName("读写器天线"); |
| | | antennaType.setTypeDescription("RFID读写器天线"); |
| | | addDeviceType(antennaType); |
| | | |
| | | // 添加状态 |
| | | for (StatusTypeEnums.Common value : StatusTypeEnums.Common.values()) { |
| | | // 读写器状态 |
| | | DeviceStatus readerStatus = new DeviceStatus(); |
| | | readerStatus.setDeviceTypeKey(readerType.getDeviceTypeKey()); |
| | | readerStatus.setDeviceStatusKey(value.getKey()); |
| | | readerStatus.setName(value.getName()); |
| | | readerStatus.setStatusDescription(value.getDescription()); |
| | | addDeviceStatusType(readerStatus); |
| | | // 天线状态 |
| | | DeviceStatus antennaStatus = new DeviceStatus(); |
| | | antennaStatus.setDeviceTypeKey(antennaType.getDeviceTypeKey()); |
| | | antennaStatus.setDeviceStatusKey(value.getKey()); |
| | | antennaStatus.setName(value.getName()); |
| | | antennaStatus.setStatusDescription(value.getDescription()); |
| | | addDeviceStatusType(antennaStatus); |
| | | } |
| | | |
| | | // 添加错误码 |
| | | for (ErrorTypeEnums.Common value : ErrorTypeEnums.Common.values()) { |
| | | DeviceErrorType readerErrorType = new DeviceErrorType(readerType.getDeviceTypeKey(), value.getKey(), value.getName(), value.getDescription()); |
| | | addDeviceErrorType(readerErrorType); |
| | | DeviceErrorType antennaErrorType = new DeviceErrorType(readerType.getDeviceTypeKey(), value.getKey(), value.getName(), value.getDescription()); |
| | | addDeviceErrorType(antennaErrorType); |
| | | } |
| | | |
| | | // 添加设备 |
| | | Device readerDevice = new Device(); |
| | | readerDevice.setDeviceKey(reader.getReaderKey()); |
| | | readerDevice.setName(readerConfig.getEquipType() + (index + 1)); |
| | | readerDevice.setDeviceTypeKey(readerType.getDeviceTypeKey()); |
| | | readerDevice.setDeviceStatusKey(StatusTypeEnums.Common.UNKNOWN.getKey()); |
| | | addDevice(readerDevice); |
| | | for (int i = 0; i < 4; i++) { |
| | | Device antennaDevice = new Device(); |
| | | antennaDevice.setParentDeviceKey(readerDevice.getDeviceKey()); |
| | | antennaDevice.setDeviceKey(reader.getAntennasKey()[i]); |
| | | antennaDevice.setName("reader" + (index + 1) + "-antenna" + (i + 1)); |
| | | antennaDevice.setDeviceTypeKey(antennaType.getDeviceTypeKey()); |
| | | antennaDevice.setDeviceStatusKey(StatusTypeEnums.Common.UNKNOWN.getKey()); |
| | | antennaDevice.setParentDeviceKey(readerDevice.getDeviceKey()); |
| | | addDevice(antennaDevice); |
| | | } |
| | | } |
| | | |
| | | } |
| | | } |
| | | |
| New file |
| | |
| | | package com.eternal.rfid.params; |
| | | |
| | | import com.eternal.config.param.annotation.Module; |
| | | import com.eternal.config.param.annotation.Param; |
| | | import lombok.Data; |
| | | import org.springframework.stereotype.Component; |
| | | |
| | | @Component |
| | | @Data |
| | | @Module(type = "东集读写器",name = "东集写器配置参数") |
| | | public class UF3ReaderConfig { |
| | | @Param(key = "component.reader.ip", defaultValue = "192.168.0.250", name = "读写器IP", fixed = true, description = "读写器IP") |
| | | private String ip; |
| | | |
| | | @Param(key = "component.reader.readerKey", defaultValue = "uf3-reader", name = "读写器KEY", fixed = true, description = "读写器KEY") |
| | | private String readerKey; |
| | | |
| | | @Param(key = "component.reader.readerName", defaultValue = "东集读写器", name = "读写器名称", fixed = true, description = "读写器名称") |
| | | private String readerName; |
| | | |
| | | @Param(key = "component.reader.port", defaultValue = "27011", name = "端口", fixed = true, description = "读写器端口") |
| | | private int port; |
| | | |
| | | @Param(key = "component.reader.readType", defaultValue = "4", name = "读写器类型", fixed = true, description = "读写器类型,当前连接的是几口设备,例如4,连的是4天线口设备;16表示连接的16天线口设备") |
| | | private int readType; |
| | | |
| | | @Param(key = "component.reader.readLogOn_Off", defaultValue = "0", name = "日志开关", fixed = true, description = "日志开关。0-关;1-开") |
| | | private int readLogOn_Off; |
| | | |
| | | @Param(key = "component.reader.antennaPower", defaultValue = "22,22,22,26", name = "读写器功率", fixed = true, description = "读写器功率") |
| | | private String antennasPower; |
| | | |
| | | @Param(key = "component.rfid.reader.mode", defaultValue = "epc", name = "读模式", description = "epc,user,tid") |
| | | private String mode; |
| | | |
| | | @Param(key = "component.rfid.reader.read.log", defaultValue = "true", name = "是否打印读写日志", description = "是否打印读写日志") |
| | | private boolean rfidReaderReadLog; |
| | | |
| | | @Param(key = "component.rfid.reader.type", defaultValue = "uf3Reader", name = "设备类型", description = "RFID设备类型") |
| | | private String equipType; |
| | | |
| | | } |
| New file |
| | |
| | | package com.eternal.rfid.service; |
| | | |
| | | import com.eternal.event.RfidReadEvent; |
| | | import com.eternal.monitor.vo.DeviceStatusVo; |
| | | import com.rfid.CReader; |
| | | import com.rfid.ReadTag; |
| | | import com.rfid.ReaderParameter; |
| | | import com.rfid.TagCallback; |
| | | import lombok.Getter; |
| | | import lombok.Setter; |
| | | import lombok.extern.slf4j.Slf4j; |
| | | import com.eternal.rfid.params.UF3ReaderConfig; |
| | | import org.springframework.context.ApplicationEvent; |
| | | |
| | | import java.util.function.Consumer; |
| | | |
| | | @Slf4j |
| | | public class UF3RFIDReader implements TagCallback { |
| | | @Getter |
| | | private final String readerKey; |
| | | |
| | | @Getter |
| | | private final String readerTypeKey; |
| | | |
| | | @Getter |
| | | private final String[] antennasKey; |
| | | |
| | | @Getter |
| | | private final String antennaTypeKey; |
| | | |
| | | @Getter |
| | | private int index; |
| | | |
| | | private UF3ReaderConfig props; |
| | | private CReader reader; |
| | | |
| | | @Getter |
| | | private String ip; |
| | | private int port; |
| | | private int readerType; |
| | | private int readerLogOn_Off; |
| | | private String mode; |
| | | |
| | | @Setter |
| | | private Consumer<ApplicationEvent> eventConsumer; |
| | | |
| | | @Setter |
| | | private Consumer<DeviceStatusVo> updateDeviceStatusConsumer; |
| | | |
| | | public UF3RFIDReader(String readerKey, String readerTypeKey, String[] antennasKey, String antennaTypeKey) { |
| | | this.readerKey = readerKey; |
| | | this.readerTypeKey = readerTypeKey; |
| | | this.antennasKey = antennasKey; |
| | | this.antennaTypeKey = antennaTypeKey; |
| | | } |
| | | |
| | | public void init(int index, UF3ReaderConfig params) { |
| | | this.index = index; |
| | | this.ip = params.getIp(); |
| | | this.port = params.getPort(); |
| | | this.readerType = params.getReadType(); |
| | | this.readerLogOn_Off = params.getReadLogOn_Off(); |
| | | this.mode = params.getMode(); |
| | | this.props = params; |
| | | } |
| | | |
| | | |
| | | public void start() { |
| | | log.info("ip:{},port:{},readerType:{},log:{}", ip, port, readerType, readerLogOn_Off); |
| | | reader = new CReader(ip, port, readerType, readerLogOn_Off); |
| | | |
| | | int conn = reader.Connect(); |
| | | if (conn != 0) { |
| | | log.error("连接UF3设备失败,返回码={}", conn); |
| | | return; |
| | | } |
| | | log.info("UF3 Reader设备连接成功:{}:{}", ip, props.getPort()); |
| | | |
| | | //0x00关闭蜂鸣器,0x01打开蜂鸣器 |
| | | reader.SetBeepNotification(0x00); |
| | | |
| | | ReaderParameter param = new ReaderParameter(); |
| | | try { |
| | | param.SetAntenna(0x09); |
| | | param.SetReadType(0); |
| | | reader.SetInventoryParameter(param); |
| | | } catch (Exception e) { |
| | | log.warn("设置读参数时发生异常:{}", e.getMessage()); |
| | | } |
| | | |
| | | applyPowerConfig(); |
| | | |
| | | reader.SetCallBack(this); |
| | | startRead(); |
| | | } |
| | | |
| | | public void startRead() { |
| | | int start = reader.StartRead(); |
| | | if (start != 0) { |
| | | log.error("开启读失败,返回码={}", start); |
| | | } else { |
| | | getAntennaPowers(); |
| | | log.info("读流程已开启"); |
| | | } |
| | | } |
| | | |
| | | public void stopRead() { |
| | | if (reader != null) { |
| | | try { |
| | | reader.StopRead(); |
| | | } catch (Exception e) { |
| | | log.warn("停止读过程异常:{}", e.getMessage()); |
| | | } |
| | | } |
| | | } |
| | | |
| | | public void disconnectReader() { |
| | | if (reader != null) { |
| | | |
| | | try { |
| | | reader.DisConnect(); |
| | | } catch (Exception e) { |
| | | log.warn("断开连接异常:{}", e.getMessage()); |
| | | } |
| | | log.info("已停止并断开UF3设备连接"); |
| | | } |
| | | } |
| | | |
| | | public boolean isOnline() { |
| | | return reader != null; |
| | | } |
| | | |
| | | @Override |
| | | public void tagCallback(ReadTag tag) { |
| | | if (tag == null) { |
| | | return; |
| | | } |
| | | String memoryData = ""; |
| | | if (mode.equals("user")) { |
| | | memoryData = readDataByEpcMask(tag.epcId, (byte) 3, (byte) 0000, (byte) 32, "00000000"); |
| | | } else if (mode.equals("tid")) { |
| | | memoryData = readDataByEpcMask(tag.epcId, (byte) 2, (byte) 0000, (byte) 32, "00000000"); |
| | | } |
| | | long timeInReader = System.currentTimeMillis(); |
| | | long timeInPc = System.currentTimeMillis(); |
| | | int ant = tag.antId; |
| | | String epc = tag.epcId; |
| | | float rssi = tag.rssi; |
| | | double phase = 0; |
| | | int doppler = 0; |
| | | |
| | | RfidReadEvent rfidReadEvent = null; |
| | | if (mode.equals("tid")) { |
| | | rfidReadEvent = new RfidReadEvent(this, timeInReader, timeInPc, ant, epc, rssi, phase, doppler, 0L, 0, "", memoryData); |
| | | } else if (mode.equals("user")) { |
| | | rfidReadEvent = new RfidReadEvent(this, timeInReader, timeInPc, ant, epc, rssi, phase, doppler, 0L, 0, memoryData, ""); |
| | | } else { |
| | | rfidReadEvent = new RfidReadEvent(this, timeInReader, timeInPc, ant, epc, rssi, phase, doppler, 0L, 0, "", ""); |
| | | } |
| | | |
| | | publishEvent(rfidReadEvent); |
| | | |
| | | if (props.isRfidReaderReadLog()) { |
| | | log.info("标签: ip={} epc={} mem={} rssi={} ant={}", |
| | | tag.ipAddr, tag.epcId, tag.memId, tag.rssi, tag.antId); |
| | | if (!memoryData.isEmpty()) { |
| | | log.info("readDataByEpcMask 数据:{}", memoryData); |
| | | } |
| | | } |
| | | } |
| | | |
| | | @Override |
| | | public void StopReadCallback() { |
| | | log.info("设备已停止读回调"); |
| | | } |
| | | |
| | | private void applyPowerConfig() { |
| | | String[] powers = props.getAntennasPower().split(","); |
| | | if (powers != null && powers.length >= 4) { |
| | | byte[] byAnt = new byte[4]; |
| | | for (int i = 0; i < 4; i++) { |
| | | int p = Integer.parseInt(powers[i]); |
| | | if (p < 0) p = 0; |
| | | if (p > 33) p = 33; |
| | | byAnt[i] = (byte) p; |
| | | } |
| | | int ret = reader.SetRfPowerByAnt(byAnt); |
| | | if (ret == 0) { |
| | | log.info("已设置四天线功率: A1={} A2={} A3={} A4={}", byAnt[0] & 0xFF, byAnt[1] & 0xFF, byAnt[2] & 0xFF, byAnt[3] & 0xFF); |
| | | } else { |
| | | log.warn("按天线设置功率失败,返回码={}", ret); |
| | | } |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * EPC掩码读取 |
| | | * |
| | | * @param epc EPC数据 (Mask) |
| | | * @param memBank 存储区 (0:Reserved, 1:EPC, 2:TID, 3:User) |
| | | * @param startAddr 起始地址 (Word) 起始地址都是从 0000 开始 |
| | | * @param length 读取长度 (Word) |
| | | * @param password 访问密码 (8位16进制字符串) |
| | | * @return 读取到的数据 |
| | | */ |
| | | public String readDataByEpcMask(String epc, byte memBank, byte startAddr, byte length, String password) { |
| | | if (reader == null) { |
| | | log.error("Reader未初始化"); |
| | | return null; |
| | | } |
| | | try { |
| | | // 注意:调用此方法可能需要先停止自动读(StartRead),取决于SDK实现。 |
| | | // 如果是在Inventory过程中调用,可能会失败或阻塞。 |
| | | String data = reader.ReadDataByEPC(epc, memBank, startAddr, length, password); |
| | | // log.info("EPC掩码读取结果: epc={} mem={} addr={} len={} data={}", epc, memBank, startAddr, length, data); |
| | | return data; |
| | | } catch (Exception e) { |
| | | log.error("EPC掩码读取异常", e); |
| | | return null; |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * TID掩码读取 |
| | | * |
| | | * @param tid TID数据 (Mask) |
| | | * @param memBank 存储区 (0:Reserved, 1:EPC, 2:TID, 3:User) |
| | | * @param startAddr 起始地址 (Word) |
| | | * @param length 读取长度 (Word) |
| | | * @param password 访问密码 (8位16进制字符串) |
| | | * @return 读取到的数据 |
| | | */ |
| | | public String readDataByTidMask(String tid, byte memBank, byte startAddr, byte length, String password) { |
| | | if (reader == null) { |
| | | log.error("Reader未初始化"); |
| | | return null; |
| | | } |
| | | try { |
| | | String data = reader.ReadDataByTID(tid, memBank, startAddr, length, password); |
| | | log.info("TID掩码读取结果: tid={} mem={} addr={} len={} data={}", tid, memBank, startAddr, length, data); |
| | | return data; |
| | | } catch (Exception e) { |
| | | log.error("TID掩码读取异常", e); |
| | | return null; |
| | | } |
| | | } |
| | | |
| | | public int[] getAntennaPowers() { |
| | | if (reader == null) { |
| | | log.error("Reader未初始化"); |
| | | return null; |
| | | } |
| | | byte[] byAnt = new byte[4]; |
| | | int ret = reader.GetRfPowerByAnt(byAnt); |
| | | if (ret != 0) { |
| | | log.warn("查询天线功率失败,返回码={}", ret); |
| | | return null; |
| | | } |
| | | int[] powers = new int[4]; |
| | | for (int i = 0; i < 4; i++) { |
| | | powers[i] = byAnt[i] & 0xFF; |
| | | } |
| | | log.info("当前四天线功率: A1={} A2={} A3={} A4={}", powers[0], powers[1], powers[2], powers[3]); |
| | | return powers; |
| | | } |
| | | |
| | | private void publishEvent(ApplicationEvent event) { |
| | | if (eventConsumer != null) { |
| | | eventConsumer.accept(event); |
| | | } else { |
| | | log.error("{}", "No eventConsumer"); |
| | | } |
| | | } |
| | | |
| | | protected void updateDeviceStatus(DeviceStatusVo deviceStatusVo) { |
| | | if (updateDeviceStatusConsumer != null) { |
| | | updateDeviceStatusConsumer.accept(deviceStatusVo); |
| | | } else { |
| | | log.warn("updateDeviceStatusConsumer is null"); |
| | | } |
| | | } |
| | | } |
| New file |
| | |
| | | package com.reader.uf3readerdemo; |
| | | |
| | | import org.junit.jupiter.api.Test; |
| | | import org.springframework.boot.test.context.SpringBootTest; |
| | | |
| | | @SpringBootTest |
| | | class Uf3ReaderDemoApplicationTests { |
| | | |
| | | @Test |
| | | void contextLoads() { |
| | | } |
| | | |
| | | } |