Browse Source

feat: init

master
guoapeng 5 months ago
commit
e66b4fb670
  1. 3
      .gitattributes
  2. 38
      .gitignore
  3. 58
      build.gradle
  4. BIN
      gradle/wrapper/gradle-wrapper.jar
  5. 7
      gradle/wrapper/gradle-wrapper.properties
  6. 252
      gradlew
  7. 94
      gradlew.bat
  8. BIN
      matrix-spray.db
  9. 1
      settings.gradle
  10. 95
      sql/init.sql
  11. 15
      src/main/java/com/qyft/ms/MatrixSprayApplication.java
  12. 117
      src/main/java/com/qyft/ms/common/constant/Commands.java
  13. 27
      src/main/java/com/qyft/ms/common/constant/SysSettings.java
  14. 25
      src/main/java/com/qyft/ms/common/constant/WebSocketMessageType.java
  15. 27
      src/main/java/com/qyft/ms/common/result/CMDResultCode.java
  16. 14
      src/main/java/com/qyft/ms/config/WebSocketConfig.java
  17. 59
      src/main/java/com/qyft/ms/config/WebSocketServer.java
  18. 47
      src/main/java/com/qyft/ms/controller/CMDController.java
  19. 55
      src/main/java/com/qyft/ms/controller/LogsController.java
  20. 56
      src/main/java/com/qyft/ms/controller/SysSettingsController.java
  21. 160
      src/main/java/com/qyft/ms/device/client/TcpClient.java
  22. 38
      src/main/java/com/qyft/ms/device/common/constant/DeviceCommands.java
  23. 16
      src/main/java/com/qyft/ms/device/common/constant/TcpMessageType.java
  24. 24
      src/main/java/com/qyft/ms/device/common/jsonrpc/JsonRpcRequest.java
  25. 16
      src/main/java/com/qyft/ms/device/common/jsonrpc/JsonRpcResponse.java
  26. 37
      src/main/java/com/qyft/ms/device/config/TcpConfig.java
  27. 37
      src/main/java/com/qyft/ms/device/controller/DeviceController.java
  28. 93
      src/main/java/com/qyft/ms/device/controller/DeviceCtrlController.java
  29. 43
      src/main/java/com/qyft/ms/device/controller/TestController.java
  30. 68
      src/main/java/com/qyft/ms/device/handler/DeviceMessageHandler.java
  31. 11
      src/main/java/com/qyft/ms/device/mapper/CtrlFuncMapper.java
  32. 11
      src/main/java/com/qyft/ms/device/mapper/CtrlFuncStepMapper.java
  33. 18
      src/main/java/com/qyft/ms/device/model/bo/DeviceAlarm.java
  34. 27
      src/main/java/com/qyft/ms/device/model/bo/DeviceCtrlFuncCMD.java
  35. 26
      src/main/java/com/qyft/ms/device/model/bo/DeviceFeedback.java
  36. 38
      src/main/java/com/qyft/ms/device/model/bo/DeviceOperationalStatus.java
  37. 140
      src/main/java/com/qyft/ms/device/model/bo/DeviceStatus.java
  38. 23
      src/main/java/com/qyft/ms/device/model/entity/CtrlFunc.java
  39. 27
      src/main/java/com/qyft/ms/device/model/entity/CtrlFuncStep.java
  40. 38
      src/main/java/com/qyft/ms/device/model/form/CtrlFuncForm.java
  41. 26
      src/main/java/com/qyft/ms/device/model/vo/CtrlFuncVO.java
  42. 26
      src/main/java/com/qyft/ms/device/model/vo/DeviceCtrlFuncVO.java
  43. 12
      src/main/java/com/qyft/ms/device/service/DeviceAlarmService.java
  44. 14
      src/main/java/com/qyft/ms/device/service/DeviceService.java
  45. 47
      src/main/java/com/qyft/ms/device/service/DeviceStatusService.java
  46. 166
      src/main/java/com/qyft/ms/device/service/DeviceStepService.java
  47. 261
      src/main/java/com/qyft/ms/device/service/DeviceTcpCMDService.java
  48. 28
      src/main/java/com/qyft/ms/device/service/ICtrlFuncService.java
  49. 12
      src/main/java/com/qyft/ms/device/service/ICtrlFuncStepService.java
  50. 132
      src/main/java/com/qyft/ms/device/service/impl/CtrlFuncServiceImpl.java
  51. 20
      src/main/java/com/qyft/ms/device/service/impl/CtrlFuncStepServiceImpl.java
  52. 34
      src/main/java/com/qyft/ms/enums/DirectiveTypeEnum.java
  53. 28
      src/main/java/com/qyft/ms/generator/CodeGenerator.java
  54. 12
      src/main/java/com/qyft/ms/mapper/LogsMapper.java
  55. 29
      src/main/java/com/qyft/ms/mapper/SysSettingsMapper.java
  56. 52
      src/main/java/com/qyft/ms/model/bo/CraftsStepMethod.java
  57. 35
      src/main/java/com/qyft/ms/model/bo/CraftsStepStatus.java
  58. 23
      src/main/java/com/qyft/ms/model/bo/TubeSol.java
  59. 14
      src/main/java/com/qyft/ms/model/dto/BaseDataDTO.java
  60. 13
      src/main/java/com/qyft/ms/model/dto/CmdInjectFluidDTO.java
  61. 14
      src/main/java/com/qyft/ms/model/dto/ContainerDTO.java
  62. 14
      src/main/java/com/qyft/ms/model/dto/InjectFluid.java
  63. 10
      src/main/java/com/qyft/ms/model/dto/LogDTO.java
  64. 11
      src/main/java/com/qyft/ms/model/dto/PauseCraftsDto.java
  65. 11
      src/main/java/com/qyft/ms/model/dto/ResumeCraftsDto.java
  66. 13
      src/main/java/com/qyft/ms/model/dto/StartCraftsDTO.java
  67. 13
      src/main/java/com/qyft/ms/model/dto/StartHeatDTO.java
  68. 11
      src/main/java/com/qyft/ms/model/dto/StopCraftsDto.java
  69. 10
      src/main/java/com/qyft/ms/model/dto/StopTaskDTO.java
  70. 16
      src/main/java/com/qyft/ms/model/dto/SysSettingsDTO.java
  71. 10
      src/main/java/com/qyft/ms/model/dto/TaskDTO.java
  72. 22
      src/main/java/com/qyft/ms/model/dto/WebsocketDTO.java
  73. 35
      src/main/java/com/qyft/ms/model/entity/Container.java
  74. 25
      src/main/java/com/qyft/ms/model/entity/Crafts.java
  75. 30
      src/main/java/com/qyft/ms/model/entity/Logs.java
  76. 20
      src/main/java/com/qyft/ms/model/entity/Ores.java
  77. 20
      src/main/java/com/qyft/ms/model/entity/Solutions.java
  78. 43
      src/main/java/com/qyft/ms/model/entity/SysSettings.java
  79. 30
      src/main/java/com/qyft/ms/model/entity/TaskSteps.java
  80. 35
      src/main/java/com/qyft/ms/model/entity/Tasks.java
  81. 23
      src/main/java/com/qyft/ms/model/form/CMDForm.java
  82. 27
      src/main/java/com/qyft/ms/model/vo/BaseDataVO.java
  83. 13
      src/main/java/com/qyft/ms/model/vo/ContainerListVO.java
  84. 23
      src/main/java/com/qyft/ms/model/vo/CraftsStepResult.java
  85. 22
      src/main/java/com/qyft/ms/model/vo/ExecutionResult.java
  86. 30
      src/main/java/com/qyft/ms/model/vo/OresCraftsListVO.java
  87. 14
      src/main/java/com/qyft/ms/model/vo/SysSettingResult.java
  88. 43
      src/main/java/com/qyft/ms/model/vo/SysSettingVO.java
  89. 35
      src/main/java/com/qyft/ms/model/vo/TaskListVO.java
  90. 18
      src/main/java/com/qyft/ms/model/vo/WebsocketResult.java
  91. 61
      src/main/java/com/qyft/ms/service/CMDService.java
  92. 11
      src/main/java/com/qyft/ms/service/ILogsService.java
  93. 24
      src/main/java/com/qyft/ms/service/ISysSettingsService.java
  94. 38
      src/main/java/com/qyft/ms/service/StatusService.java
  95. 32
      src/main/java/com/qyft/ms/service/WebSocketService.java
  96. 29
      src/main/java/com/qyft/ms/service/impl/ILogsServiceImpl.java
  97. 75
      src/main/java/com/qyft/ms/service/impl/ISysSettingsServiceImpl.java
  98. 44
      src/main/java/com/qyft/ms/system/common/base/BaseEntity.java
  99. 26
      src/main/java/com/qyft/ms/system/common/base/BasePageQuery.java
  100. 84
      src/main/java/com/qyft/ms/system/common/base/IBaseEnum.java

3
.gitattributes

@ -0,0 +1,3 @@
/gradlew text eol=lf
*.bat text eol=crlf
*.jar binary

38
.gitignore

@ -0,0 +1,38 @@
.gradle
build/
logs/
!gradle/wrapper/gradle-wrapper.jar
!**/src/main/**/build/
!**/src/test/**/build/
### STS ###
.apt_generated
.classpath
.factorypath
.project
.settings
.springBeans
.sts4-cache
bin/
!**/src/main/**/bin/
!**/src/test/**/bin/
### IntelliJ IDEA ###
.idea
*.iws
*.iml
*.ipr
out/
!**/src/main/**/out/
!**/src/test/**/out/
### NetBeans ###
/nbproject/private/
/nbbuild/
/dist/
/nbdist/
/.nb-gradle/
### VS Code ###
.vscode/

58
build.gradle

@ -0,0 +1,58 @@
plugins {
id 'java'
id 'org.springframework.boot' version '3.3.8'
id 'io.spring.dependency-management' version '1.1.7'
}
group = 'com.qyft'
version = '0.0.1'
java {
toolchain {
languageVersion = JavaLanguageVersion.of(17)
}
}
configurations {
compileOnly {
extendsFrom annotationProcessor
}
}
repositories {
mavenCentral()
}
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-web'
implementation group: 'ch.qos.logback', name: 'logback-core', version: '1.5.16'
implementation group: 'org.xerial', name: 'sqlite-jdbc', version: '3.48.0.0'
implementation 'org.mybatis.spring.boot:mybatis-spring-boot-starter:3.0.4'
implementation group: 'org.freemarker', name: 'freemarker', version: '2.3.34'
implementation group: 'com.baomidou', name: 'mybatis-plus-boot-starter', version: '3.5.10.1'
implementation group: 'com.baomidou', name: 'mybatis-plus-jsqlparser', version: '3.5.10.1'
implementation group: 'com.baomidou', name: 'mybatis-plus-generator', version: '3.5.10.1'
implementation group: 'cn.hutool', name: 'hutool-all', version: '5.8.35'
implementation group: 'com.github.xiaoymin', name: 'knife4j-openapi3-jakarta-spring-boot-starter', version: '4.5.0'
implementation group: 'io.jsonwebtoken', name: 'jjwt-api', version: '0.12.6'
runtimeOnly group: 'io.jsonwebtoken', name: 'jjwt-impl', version: '0.12.6'
runtimeOnly group: 'io.jsonwebtoken', name: 'jjwt-jackson', version: '0.12.6'
implementation group: 'com.alibaba', name: 'fastjson', version: '2.0.54'
implementation group: 'org.springframework.boot', name: 'spring-boot-starter-websocket', version: '3.4.2'
implementation group: 'io.netty', name: 'netty-all', version: '4.1.118.Final'
//++++++++++++++++
//++++++++++++++++
compileOnly 'org.projectlombok:lombok'
developmentOnly 'org.springframework.boot:spring-boot-devtools'
annotationProcessor 'org.projectlombok:lombok'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
testImplementation 'org.mybatis.spring.boot:mybatis-spring-boot-starter-test:3.0.4'
testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
}
tasks.named('test') {
useJUnitPlatform()
}

BIN
gradle/wrapper/gradle-wrapper.jar

7
gradle/wrapper/gradle-wrapper.properties

@ -0,0 +1,7 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.12.1-bin.zip
networkTimeout=10000
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists

252
gradlew

@ -0,0 +1,252 @@
#!/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.
#
# 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
' "$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
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" "$@"

94
gradlew.bat

@ -0,0 +1,94 @@
@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
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

BIN
matrix-spray.db

1
settings.gradle

@ -0,0 +1 @@
rootProject.name = 'matrix-spray'

95
sql/init.sql

@ -0,0 +1,95 @@
-- 创建 sys_user 表
CREATE TABLE IF NOT EXISTS sys_user
(
id INTEGER PRIMARY KEY AUTOINCREMENT,
username TEXT NOT NULL,
nickname TEXT,
password TEXT NOT NULL,
role_id INTEGER,
is_deleted TINYINT DEFAULT 0,
create_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
update_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
-- 插入测试数据
INSERT INTO sys_user (username, nickname, password, role_id, is_deleted)
VALUES ('admin', 'Admin', '12345', 1, 0),
('john_doe', 'John Doe', 'password123', 2, 0),
('test', 'test', 'test123', 3, 0);
-- 创建 sys_role 表
CREATE TABLE IF NOT EXISTS sys_role
(
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL,
code TEXT NOT NULL,
create_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
update_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
-- 插入角色数据
INSERT INTO sys_role (name, code)
VALUES ('管理员', 'ADMIN'),
('普通用户', 'USER'),
('测试用户', 'TEST');
-- 创建 matrix 基质类型表
CREATE TABLE IF NOT EXISTS matrix
(
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL,
create_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
update_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
-- 创建 matrix_craft 基质工艺表
CREATE TABLE IF NOT EXISTS matrix_craft
(
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL,
matrix_id INTEGER NOT NULL,
route_type INTEGER,
z_height INTEGER,
nitrogen_flow_velocity INTEGER,
nitrogen_air_pressure INTEGER,
matrix_flow_velocity INTEGER,
voltage INTEGER,
movement_speed INTEGER,
space INTEGER,
create_user INTEGER,
create_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
update_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
-- 创建 syringe 注射器表
CREATE TABLE IF NOT EXISTS syringe
(
id INTEGER PRIMARY KEY AUTOINCREMENT,
flow_rate INTEGER,
fluid_volume INTEGER,
run_time INTEGER,
cycle_intervals INTEGER
);
-- 创建 operation_log 操作记录表
CREATE TABLE IF NOT EXISTS operation_log
(
id INTEGER PRIMARY KEY AUTOINCREMENT,
matrix_craft_id INTEGER,
matrix_info TEXT,
status INTEGER,
create_user INTEGER,
create_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
update_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
-- 创建 sys_settings 系统配置表
CREATE TABLE IF NOT EXISTS sys_settings
(
id INTEGER PRIMARY KEY AUTOINCREMENT,
parent_id INTEGER,
name TEXT NOT NULL,
code TEXT,
value TEXT
);

15
src/main/java/com/qyft/ms/MatrixSprayApplication.java

@ -0,0 +1,15 @@
package com.qyft.ms;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.context.properties.ConfigurationPropertiesScan;
@SpringBootApplication
@ConfigurationPropertiesScan
public class MatrixSprayApplication {
public static void main(String[] args) {
SpringApplication.run(MatrixSprayApplication.class, args);
}
}

117
src/main/java/com/qyft/ms/common/constant/Commands.java

@ -0,0 +1,117 @@
package com.qyft.ms.common.constant;
/**
* 操作指令
*/
public class Commands {
/**
* 抬起托盘
*/
public static final String UP_TRAY = "upTray";
/**
* 降下托盘
*/
public static final String DOWN_TRAY = "downTray";
/**
* 添加溶液
*/
public static final String INJECT_FLUID = "injectFluid";
/**
* 恒温
*/
public static final String KEEP_HEAT = "keepHeat";
/**
* 移至加液
*/
public static final String MOVE_TO_ACTION_AREA = "moveToActionArea";
/**
* 检查加液位状态是否被占用
*/
public static final String CHECK_ACTION_AREA = "checkActionArea";
/**
* 摇匀试管架
*/
public static final String START_SHAKE_UP = "startShakeUp";
/**
* 停止摇匀试管架
*/
public static final String STOP_SHAKE_UP = "stopShakeUp";
/**
* 开始加热
*/
public static final String START_HEAT = "startHeat";
/**
* 停止加热
*/
public static final String STOP_HEAT = "stopHeat";
/**
* 拍照
*/
public static final String TAKE_PHOTO = "takePhoto";
/**
* 移至异常区
*/
public static final String MOVE_TO_UNUSUAL = "moveToUnusual";
/**
* 从异常区移回加热区
*/
public static final String MOVE_TO_HEAT_AREA = "moveToHeatArea";
/**
* 取试管架盖
*/
public static final String TAKE_OFF_CAP = "takeOffCap";
/**
* 装回试管架盖
*/
public static final String PUT_BACK_CAP = "putBackCap";
/**
* 机械臂移动至指定坐标x, y, z
*/
public static final String MOVE_MACHINE_ARM = "moveMachineArm";
/**
* 获取当前某种溶液的数量
*/
public static final String GET_LIQUID_AMOUNT = "getLiquidAmount";
/**
* 移动单个试管
*/
public static final String MOVE_TUBE = "moveTube";
/**
* 机械臂爪子开启
*/
public static final String OPEN_CLAW = "openClaw";
/**
* 机械臂爪子闭合
*/
public static final String CLOSE_CLAW = "closeClaw";
/**
* 开门
*/
public static final String OPEN_DOOR = "openDoor";
/**
* 关门
*/
public static final String CLOSE_DOOR = "closeDoor";
}

27
src/main/java/com/qyft/ms/common/constant/SysSettings.java

@ -0,0 +1,27 @@
package com.qyft.ms.common.constant;
/**
* 操作指令
*/
public class SysSettings {
/**
* 加热区域
*/
public static final String HEAT_AREA = "heat_area";
/**
* 加液区域
*/
public static final String SOLUTION_AREA = "solution_area";
/**
* 拍子区域
*/
public static final String LID_AREA = "lid_area";
/**
* 其他系统配置
*/
public static final String SYS_SETTING = "sys_setting";
}

25
src/main/java/com/qyft/ms/common/constant/WebSocketMessageType.java

@ -0,0 +1,25 @@
package com.qyft.ms.common.constant;
public class WebSocketMessageType {
/**
* 设备状态
*/
public static final String STATUS = "status";
/**
* 设备报警
*/
public static final String ALARM = "alarm";
/**
* 设备指令反馈
*/
public static final String CMD = "cmd";
/**
* 工艺执行反馈
*/
public static final String CRAFTS = "crafts";
/**
* 容器剩余状态
*/
public static final String CONTAINER = "container";
}

27
src/main/java/com/qyft/ms/common/result/CMDResultCode.java

@ -0,0 +1,27 @@
package com.qyft.ms.common.result;
import com.qyft.ms.system.common.result.IResultCode;
import lombok.AllArgsConstructor;
import lombok.NoArgsConstructor;
import java.io.Serializable;
@AllArgsConstructor
@NoArgsConstructor
public enum CMDResultCode implements IResultCode, Serializable {
SUCCESS("D0000", "执行完毕"),
FAILURE("D1111", "执行失败");
private String code;
private String msg;
@Override
public String getCode() {
return code;
}
@Override
public String getMsg() {
return msg;
}
}

14
src/main/java/com/qyft/ms/config/WebSocketConfig.java

@ -0,0 +1,14 @@
package com.qyft.ms.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.socket.server.standard.ServerEndpointExporter;
@Configuration
public class WebSocketConfig {
@Bean
public ServerEndpointExporter serverEndpointExporter() {
return new ServerEndpointExporter();
}
}

59
src/main/java/com/qyft/ms/config/WebSocketServer.java

@ -0,0 +1,59 @@
package com.qyft.ms.config;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.qyft.ms.model.dto.WebsocketDTO;
import jakarta.websocket.*;
import jakarta.websocket.server.ServerEndpoint;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
@Slf4j
@ServerEndpoint("/ws")
@Component
public class WebSocketServer {
private static final Set<Session> sessions = Collections.synchronizedSet(new HashSet<>());
public static void sendMessageToClients(String message) {
synchronized (sessions) {
for (Session session : sessions) {
try {
session.getBasicRemote().sendText(message);
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
@OnOpen
public void onOpen(Session session) {
sessions.add(session);
}
@OnMessage
public void onMessage(String message, Session session) {
ObjectMapper objectMapper = new ObjectMapper();
try {
WebsocketDTO c = objectMapper.readValue(message, WebsocketDTO.class);
log.info("解析后的信息: commandName={}, commandId={}", c.getCommandName(), c.getCommandId());
} catch (Exception e) {
e.printStackTrace();
}
}
@OnClose
public void onClose(Session session) {
sessions.remove(session); // 移除关闭连接的 Session
}
@OnError
public void onError(Throwable error) {
}
}

47
src/main/java/com/qyft/ms/controller/CMDController.java

@ -0,0 +1,47 @@
package com.qyft.ms.controller;
import cn.hutool.json.JSONUtil;
import com.qyft.ms.model.form.CMDForm;
import com.qyft.ms.service.CMDService;
import com.qyft.ms.system.common.result.Result;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.*;
@Tag(name = "控制指令")
@RestController
@RequestMapping("/api/cmd")
@RequiredArgsConstructor
@Slf4j
public class CMDController {
private final CMDService cmdService;
@Operation(summary = "执行指令")
@PostMapping("/execute")
public Result<String> execute(@RequestBody CMDForm cmdForm) {
try {
String commandId = UUID.randomUUID().toString();
if (cmdForm.getCommandId() == null || cmdForm.getCommandId().isEmpty()) {
cmdForm.setCommandId(commandId);
}
log.info("接收到指令: {}", JSONUtil.toJsonStr(cmdForm));
if (cmdService.executeCommand(cmdForm)) {
return Result.success(cmdForm.getCommandId());
} else {
return Result.failed("无效命令");
}
} catch (Exception e) {
log.error("指令执行异常: {}", JSONUtil.toJsonStr(cmdForm), e);
return Result.failed("执行失败");
}
}
}

55
src/main/java/com/qyft/ms/controller/LogsController.java

@ -0,0 +1,55 @@
package com.qyft.ms.controller;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.qyft.ms.model.entity.Logs;
import com.qyft.ms.service.ILogsService;
import com.qyft.ms.system.common.base.BasePageQuery;
import com.qyft.ms.system.common.result.PageResult;
import com.qyft.ms.system.common.result.Result;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.*;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
@Tag(name = "日志")
@RestController
@RequestMapping("/api/logs")
@RequiredArgsConstructor
@Slf4j
public class LogsController {
private final ILogsService logsService;
@Operation(summary = "日志列表")
@GetMapping("/list")
public PageResult<Logs> getAllTasks(BasePageQuery pageQuery) {
return PageResult.success(logsService.page(new Page<>(pageQuery.getPageNum(), pageQuery.getPageSize())));
}
@Operation(summary = "日志新增")
@PostMapping("/add")
public Result<Integer> add(String log) {
return Result.success(logsService.insertLog(log));
}
@Operation(summary = "日志详情")
@GetMapping("/")
public Result<Logs> detail(@RequestParam Long id) {
return Result.success(logsService.getBaseMapper().selectById(id));
}
@Operation(summary = "删除日志")
@DeleteMapping("/{ids}")
public Result<Boolean> deleteLog(@Parameter(description = "日志ID,多个以英文逗号(,)分割") @PathVariable String ids) {
List<Long> idsArr = Arrays.stream(ids.split(","))
.map(Long::parseLong)
.collect(Collectors.toList());
return Result.success(logsService.removeByIds(idsArr));
}
}

56
src/main/java/com/qyft/ms/controller/SysSettingsController.java

@ -0,0 +1,56 @@
package com.qyft.ms.controller;
import com.qyft.ms.model.dto.SysSettingsDTO;
import com.qyft.ms.service.ISysSettingsService;
import com.qyft.ms.system.common.result.Result;
import io.micrometer.common.lang.Nullable;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.*;
import java.util.List;
@Tag(name = "系统配置")
@RestController
@RequestMapping("/api/sys")
@RequiredArgsConstructor
@Slf4j
public class SysSettingsController {
private final ISysSettingsService sysSettingsService;
@Operation(summary = "获取配置")
@GetMapping("/getConfig")
public Result<List<?>> getConfig(
@Parameter(
description = "配置类型(heat_area 工作区配置 solution_area 加液区配置 lid_area 拍子区域配置 sys_setting 其他系统配置)"
)
@RequestParam
@Nullable
String type
) {
if (type == null) {
return Result.success(sysSettingsService.getAllConfig());
} else {
return Result.success(sysSettingsService.getConfig(type));
}
}
@Operation(summary = "更新配置")
@PutMapping("/updateConfig")
public Result<String> updateConfig(@RequestBody List<SysSettingsDTO> dto) {
return Result.failed();
}
@Operation(summary = "修改系统日期与时间(Linux date -s)")
@PutMapping("/setSysDate/{newTime}")
public Result<String> setSysDate(@Parameter(description = "时间字符串 YYYY-MM-DD HH:mm:ss") @PathVariable String newTime) {
boolean isSuccess = sysSettingsService.setSysDate(newTime);
if (isSuccess) {
return Result.success();
}
return Result.failed();
}
}

160
src/main/java/com/qyft/ms/device/client/TcpClient.java

@ -0,0 +1,160 @@
package com.qyft.ms.device.client;
import cn.hutool.json.JSONUtil;
import com.qyft.ms.device.common.jsonrpc.JsonRpcRequest;
import com.qyft.ms.device.config.TcpConfig;
import com.qyft.ms.device.handler.DeviceMessageHandler;
import com.qyft.ms.device.model.bo.DeviceFeedback;
import io.netty.bootstrap.Bootstrap;
import io.netty.buffer.Unpooled;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.codec.json.JsonObjectDecoder;
import io.netty.util.CharsetUtil;
import jakarta.annotation.PostConstruct;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import java.net.InetSocketAddress;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;
@Slf4j
@Component
@RequiredArgsConstructor
public class TcpClient {
private final TcpConfig tcpConfig;
private final DeviceMessageHandler deviceMessageHandler;
private final EventLoopGroup group = new NioEventLoopGroup();
private Channel channel;
private Bootstrap bootstrap;
@PostConstruct
public void init() {
if (tcpConfig.isEnable()) {
connect();
}
}
public void connect() {
try {
if (bootstrap == null) {
bootstrap = new Bootstrap();
bootstrap.group(group)
.channel(NioSocketChannel.class)
.option(ChannelOption.CONNECT_TIMEOUT_MILLIS, tcpConfig.getTimeout())
.handler(new ChannelInitializer<>() {
@Override
protected void initChannel(Channel ch) {
ch.pipeline().addLast(new JsonObjectDecoder());
ch.pipeline().addLast(deviceMessageHandler);
ch.pipeline().addLast(new TcpConnectionHandler());
}
});
}
log.info("尝试连接到TCP服务 {}:{}", tcpConfig.getHost(), tcpConfig.getPort());
ChannelFuture future = bootstrap.connect(new InetSocketAddress(tcpConfig.getHost(), tcpConfig.getPort()));
future.addListener((ChannelFutureListener) f -> {
if (f.isSuccess()) {
channel = f.channel();
log.info("已链接到TCP服务");
} else {
log.error("无法连接到TCP服务. {}ms后重试...", tcpConfig.getReconnect());
f.channel().eventLoop().schedule(this::connect, tcpConfig.getReconnect(), TimeUnit.MILLISECONDS);
}
});
} catch (Exception e) {
log.error("尝试连接到TCP服务发生意外错误: {}", e.getMessage(), e);
}
}
@Scheduled(fixedRateString = "${tcp.reconnect}")
public void checkConnection() {
if (channel == null || !channel.isActive()) {
log.error("TCP服务链接丢失");
connect();
}
}
public boolean send(String request) {
if (channel != null && channel.isActive()) {
channel.writeAndFlush(Unpooled.copiedBuffer(request, CharsetUtil.UTF_8));
return true;
} else {
log.error("TCP服务未连接,无法发送请求: {}", request);
return false;
}
}
public DeviceFeedback sendCommand(String method) {
JsonRpcRequest request = new JsonRpcRequest();
request.setMethod(method);
return this.sendCommand(request);
}
public DeviceFeedback sendCommand(String method, Map<String, Object> params) {
JsonRpcRequest request = new JsonRpcRequest();
request.setMethod(method);
request.setParams(params);
return this.sendCommand(request);
}
public DeviceFeedback sendCommand(JsonRpcRequest request) {
if (request.getId() == null) {
request.setId(UUID.randomUUID().toString());
}
CompletableFuture<DeviceFeedback> future = new CompletableFuture<>();
deviceMessageHandler.responseMap.put(request.getId(), future);
try {
if (request.getParams() == null) {
request.setParams(new HashMap<>());
}
request.getParams().put("class", "test");
String requestJsonStr = JSONUtil.toJsonStr(request);
log.info("发送TCP指令(同步) {}", requestJsonStr);
if (this.send(requestJsonStr)) {
return future.get(tcpConfig.getFeedbackTimeout(), TimeUnit.MILLISECONDS); // 等待 FEEDBACK 响应
} else {
return null;
}
} catch (Exception e) {
log.error("发送TCP指令错误(同步) {}", JSONUtil.toJsonStr(request), e);
} finally {
deviceMessageHandler.responseMap.remove(request.getId()); //确保完成后移除
}
return null;
}
private class TcpConnectionHandler extends ChannelInboundHandlerAdapter {
@Override
public void channelInactive(ChannelHandlerContext ctx) throws Exception {
// 连接断开时的处理逻辑
log.error("TCP连接丢失,准备重新连接...");
if (channel != null) {
channel.close();
}
connect();
super.channelInactive(ctx);
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
log.error("TCP连接发生异常: {}", cause.getMessage(), cause);
ctx.close();
}
}
}

38
src/main/java/com/qyft/ms/device/common/constant/DeviceCommands.java

@ -0,0 +1,38 @@
package com.qyft.ms.device.common.constant;
public class DeviceCommands {
/**
* 使指定轴的电机回原点
*/
public static final String MOTOR_MOVE_TO_HOME = "motorMoveToHome";
// 设置指定轴的电机的运行电流
public static final String SET_MOTOR_RUNNING_CURRENT = "setMotorRunningCurrent";
// 移动指定轴的电机到指定位置
public static final String MOVE_MOTOR_TO_POSITION = "moveMotorToPosition";
// 设置指定轴的电机的运行速度
public static final String SET_MOTOR_SPEED = "setMotorSpeed";
// 切换三通阀到基质状态
public static final String SWITCH_THREE_WAY_VALVE_TO_SUBSTRATE = "switchThreeWayValveToSubstrate";
// 切换三通阀到喷涂状态
public static final String SWITCH_THREE_WAY_VALVE_TO_SPRAY = "switchThreeWayValveToSpray";
// 控制指定阀的开启或关闭
public static final String CONTROL_VALVE = "controlValve";
// 以指定亮度开启照明灯板
public static final String TURN_ON_LIGHT_PANEL = "turnOnLightPanel";
// 关闭照明灯板
public static final String TURN_OFF_LIGHT_PANEL = "turnOffLightPanel";
// 以指定电压值开启高压电
public static final String TURN_ON_HIGH_VOLTAGE = "turnOnHighVoltage";
// 关闭高压电
public static final String TURN_OFF_HIGH_VOLTAGE = "turnOffHighVoltage";
// 以指定转速方向和时间开启注射泵
public static final String TURN_ON_SYRINGE_PUMP = "turnOnSyringePump";
// 停止注射泵
public static final String TURN_OFF_SYRINGE_PUMP = "turnOffSyringePump";
// 设置注射泵的容量时间比例参数
public static final String SET_SYRINGE_PUMP_PARAMETERS = "setSyringePumpParameters";
// 推送指定容量的液体
public static final String PUSH_VOLUME = "pushVolume";
}

16
src/main/java/com/qyft/ms/device/common/constant/TcpMessageType.java

@ -0,0 +1,16 @@
package com.qyft.ms.device.common.constant;
public class TcpMessageType {
/**
* 设备状态
*/
public static final String STATUS = "status";
/**
* 设备报警
*/
public static final String ALARM = "alarm";
/**
* 设备指令反馈
*/
public static final String FEEDBACK = "feedback";
}

24
src/main/java/com/qyft/ms/device/common/jsonrpc/JsonRpcRequest.java

@ -0,0 +1,24 @@
package com.qyft.ms.device.common.jsonrpc;
import lombok.Data;
import java.util.Map;
/**
* TCP JSON RPC请求
*/
@Data
public class JsonRpcRequest {
/**
* 请求id
*/
private String id;
/**
* 请求方法
*/
private String method;
/**
* 请求参数
*/
private Map<String, Object> params;
}

16
src/main/java/com/qyft/ms/device/common/jsonrpc/JsonRpcResponse.java

@ -0,0 +1,16 @@
package com.qyft.ms.device.common.jsonrpc;
import cn.hutool.json.JSONObject;
import lombok.Data;
@Data
public class JsonRpcResponse {
/**
* 数据类型
*/
private String type;
/**
* 数据
*/
private JSONObject data;
}

37
src/main/java/com/qyft/ms/device/config/TcpConfig.java

@ -0,0 +1,37 @@
package com.qyft.ms.device.config;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
@Data
@Component
@ConfigurationProperties(prefix = "tcp")
public class TcpConfig {
/**
* 是否启用 TCP 连接
*/
private boolean enable;
/**
* TCP 链接地址
*/
private String host;
/**
* TCP 端口
*/
private int port;
/**
* 断线重连间隔毫秒
*/
private int reconnect;
/**
* 连接超时时间毫秒
*/
private int timeout;
/**
* 指令反馈超时时间
*/
private int feedbackTimeout;
}

37
src/main/java/com/qyft/ms/device/controller/DeviceController.java

@ -0,0 +1,37 @@
package com.qyft.ms.device.controller;
import com.qyft.ms.device.model.bo.DeviceOperationalStatus;
import com.qyft.ms.device.model.bo.DeviceStatus;
import com.qyft.ms.device.service.DeviceStatusService;
import com.qyft.ms.system.common.result.Result;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@Tag(name = "设备控制")
@RestController
@RequestMapping("/api/device")
@RequiredArgsConstructor
@Slf4j
public class DeviceController {
private final DeviceStatusService deviceStatusService;
@Operation(summary = "获取设备业务操作状态")
@GetMapping("/operational/status")
public Result<DeviceOperationalStatus> getDeviceOperationalStatus() {
DeviceOperationalStatus deviceOperationalStatus = deviceStatusService.getDeviceOperationalStatus();
return Result.success(deviceOperationalStatus);
}
@Operation(summary = "获取当前设备状态")
@GetMapping("/status")
public Result<DeviceStatus> getDeviceStatus() {
DeviceStatus deviceStatus = deviceStatusService.getDeviceStatus();
return Result.success(deviceStatus);
}
}

93
src/main/java/com/qyft/ms/device/controller/DeviceCtrlController.java

@ -0,0 +1,93 @@
package com.qyft.ms.device.controller;
import com.qyft.ms.device.model.bo.DeviceCtrlFuncCMD;
import com.qyft.ms.device.model.entity.CtrlFunc;
import com.qyft.ms.device.model.form.CtrlFuncForm;
import com.qyft.ms.device.model.vo.DeviceCtrlFuncVO;
import com.qyft.ms.device.service.ICtrlFuncService;
import com.qyft.ms.system.common.result.Result;
import com.qyft.ms.system.common.result.ResultCode;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.*;
import java.util.List;
@Tag(name = "设备控制")
@RestController
@RequestMapping("/api/device")
@RequiredArgsConstructor
@Slf4j
public class DeviceCtrlController {
private final ICtrlFuncService ctrlFuncService;
@Operation(summary = "获取所有设备控制方法步骤")
@GetMapping("/ctrl/step")
public Result<List<DeviceCtrlFuncCMD>> getAllCtrlFuncStep() {
List<DeviceCtrlFuncCMD> ctrlFuncList = ctrlFuncService.findAllStepCMD();
return Result.success(ctrlFuncList);
}
@Operation(summary = "获取所有设备控制方法")
@GetMapping("/ctrl")
public Result<List<CtrlFunc>> getAllCtrlFunc() {
List<CtrlFunc> ctrlFuncList = ctrlFuncService.findAll();
return Result.success(ctrlFuncList);
}
@Operation(summary = "根据id获取设备控制方法与步骤")
@GetMapping("/ctrl/{id}")
public Result<DeviceCtrlFuncVO> getById(@PathVariable Long id) {
DeviceCtrlFuncVO ctrlFuncVO = ctrlFuncService.findVOById(id);
return Result.success(ctrlFuncVO);
}
@Operation(summary = "添加设备控制方法与步骤")
@PostMapping("/ctrl")
public Result<String> addCtrlFunc(@RequestBody CtrlFuncForm ctrlFuncForm) {
long count = ctrlFuncService.countFuncCmdByFuncCmd(ctrlFuncForm.getFuncCmd());
if (count > 0) {
return Result.failed(ResultCode.DATA_ALREADY_EXISTS);
}
boolean isSuccess = ctrlFuncService.addCtrlFunc(ctrlFuncForm);
if (isSuccess) {
return Result.success();
}
return Result.failed();
}
@Operation(summary = "更新设备控制方法与步骤")
@PutMapping("/ctrl/{id}")
public Result<String> updateCtrlFunc(@PathVariable Long id, @RequestBody CtrlFuncForm ctrlFuncForm) {
CtrlFunc currentCtrlFunc = ctrlFuncService.getById(id);
if (currentCtrlFunc != null && !currentCtrlFunc.getFuncCmd().equals(ctrlFuncForm.getFuncCmd())) {
//改了指令需要判断指令是否重复
long count = ctrlFuncService.countFuncCmdByFuncCmd(ctrlFuncForm.getFuncCmd());
if (count > 0) {
return Result.failed(ResultCode.DATA_ALREADY_EXISTS);
}
}
boolean isSuccess = ctrlFuncService.updateCtrlFunc(id, ctrlFuncForm);
if (isSuccess) {
return Result.success();
}
return Result.failed();
}
@Operation(summary = "删除设备控制方法")
@DeleteMapping("/ctrl/{ids}")
public Result<String> deleteCtrlFunc(@Parameter(description = "设备控制方法ID,多个以英文逗号(,)分割") @PathVariable String ids) {
boolean isSuccess = ctrlFuncService.deleteCtrlFunc(ids);
if (isSuccess) {
return Result.success();
}
return Result.failed();
}
}

43
src/main/java/com/qyft/ms/device/controller/TestController.java

@ -0,0 +1,43 @@
package com.qyft.ms.device.controller;
import com.qyft.ms.device.model.bo.DeviceStatus;
import com.qyft.ms.device.service.DeviceTcpCMDService;
import com.qyft.ms.device.service.DeviceStatusService;
import com.qyft.ms.system.common.result.Result;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
@Tag(name = "测试")
@RestController
@RequestMapping("/api/test")
@RequiredArgsConstructor
@Slf4j
public class TestController {
private final DeviceTcpCMDService deviceTcpCMDService;
private final DeviceStatusService deviceStatusService;
@Operation(summary = "生成设备状态实体")
@GetMapping("/getDeviceStatus")
public Result<DeviceStatus> getDeviceStatus() {
DeviceStatus deviceStatus = deviceStatusService.getDeviceStatus();
return Result.success(deviceStatus);
}
@Operation(summary = "触发测试报警")
@GetMapping("/alarmTest")
public Result<DeviceStatus> alarmTest(@RequestParam String code, @RequestParam String msg, @RequestParam String module) {
boolean success = deviceTcpCMDService.alarmTest(code, msg, module);
if (success) {
return Result.success();
} else {
return Result.failed("触发测试报警失败");
}
}
}

68
src/main/java/com/qyft/ms/device/handler/DeviceMessageHandler.java

@ -0,0 +1,68 @@
package com.qyft.ms.device.handler;
import cn.hutool.json.JSONUtil;
import com.qyft.ms.common.constant.WebSocketMessageType;
import com.qyft.ms.device.common.constant.TcpMessageType;
import com.qyft.ms.device.common.jsonrpc.JsonRpcResponse;
import com.qyft.ms.device.model.bo.DeviceAlarm;
import com.qyft.ms.device.model.bo.DeviceFeedback;
import com.qyft.ms.device.model.bo.DeviceStatus;
import com.qyft.ms.device.service.DeviceStatusService;
import com.qyft.ms.service.WebSocketService;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.util.CharsetUtil;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ConcurrentHashMap;
@Slf4j
@Component
@ChannelHandler.Sharable
@RequiredArgsConstructor
public class DeviceMessageHandler extends ChannelInboundHandlerAdapter {
public final Map<String, CompletableFuture<DeviceFeedback>> responseMap = new ConcurrentHashMap<>();
private final DeviceStatusService deviceStatusService;
private final WebSocketService webSocketService;
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) {
ByteBuf buf = (ByteBuf) msg;
String serverMsg = buf.toString(CharsetUtil.UTF_8);
try {
JsonRpcResponse jsonRpcResponse = JSONUtil.toBean(serverMsg, JsonRpcResponse.class);
if (TcpMessageType.STATUS.equals(jsonRpcResponse.getType())) {//设备状态
DeviceStatus deviceStatus = JSONUtil.toBean(jsonRpcResponse.getData(), DeviceStatus.class);
deviceStatusService.updateDeviceStatus(deviceStatus); // 更新设备状态
} else if (TcpMessageType.ALARM.equals(jsonRpcResponse.getType())) {//设备报警
log.error("设备报警: {}", serverMsg);
DeviceAlarm deviceAlarm = JSONUtil.toBean(jsonRpcResponse.getData(), DeviceAlarm.class);
webSocketService.pushMsg(WebSocketMessageType.ALARM, deviceAlarm);
} else if (TcpMessageType.FEEDBACK.equals(jsonRpcResponse.getType())) {//设备指令反馈
DeviceFeedback deviceFeedback = JSONUtil.toBean(jsonRpcResponse.getData(), DeviceFeedback.class);
this.handleTcpResponse(deviceFeedback);
}
} catch (Exception e) {
log.error("TCP服务消息处理错误: {}, error: {}", serverMsg, e.getMessage(), e);
} finally {
buf.release();
}
}
private void handleTcpResponse(DeviceFeedback deviceFeedback) {
String requestId = deviceFeedback.getId();
CompletableFuture<DeviceFeedback> future = responseMap.remove(requestId);
if (future != null) {
future.complete(deviceFeedback);
} else {
log.error("未找到 requestId: {} 对应的等待请求", requestId);
}
}
}

11
src/main/java/com/qyft/ms/device/mapper/CtrlFuncMapper.java

@ -0,0 +1,11 @@
package com.qyft.ms.device.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.qyft.ms.device.model.entity.CtrlFunc;
import org.apache.ibatis.annotations.Mapper;
@Mapper
public interface CtrlFuncMapper extends BaseMapper<CtrlFunc> {
}

11
src/main/java/com/qyft/ms/device/mapper/CtrlFuncStepMapper.java

@ -0,0 +1,11 @@
package com.qyft.ms.device.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.qyft.ms.device.model.entity.CtrlFuncStep;
import org.apache.ibatis.annotations.Mapper;
@Mapper
public interface CtrlFuncStepMapper extends BaseMapper<CtrlFuncStep> {
}

18
src/main/java/com/qyft/ms/device/model/bo/DeviceAlarm.java

@ -0,0 +1,18 @@
package com.qyft.ms.device.model.bo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
/**
* 设备报警信息
*/
@Schema(description = "设备报警信息")
@Data
public class DeviceAlarm {
@Schema(description = "报警代码")
private String code;
@Schema(description = "报警信息")
private String msg;
@Schema(description = "报警模块")
private String module;
}

27
src/main/java/com/qyft/ms/device/model/bo/DeviceCtrlFuncCMD.java

@ -0,0 +1,27 @@
package com.qyft.ms.device.model.bo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
@EqualsAndHashCode(callSuper = false)
@Schema(description = "设备控制方法步骤指令")
@Data
public class DeviceCtrlFuncCMD {
public DeviceCtrlFuncCMD(String name, String deviceCmd, String params) {
this.name = name;
this.deviceCmd = deviceCmd;
this.params = params;
}
@Schema(description = "指令名称")
private String name;
@Schema(description = "设备指令")
private String deviceCmd;
@Schema(description = "参数")
private String params;
}

26
src/main/java/com/qyft/ms/device/model/bo/DeviceFeedback.java

@ -0,0 +1,26 @@
package com.qyft.ms.device.model.bo;
import lombok.Data;
/**
* 设备当前状态
*/
@Data
public class DeviceFeedback {
/**
* 请求id
*/
private String id;
/**
* 请求数据
*/
private Object result;
private DeviceFeedbackError error;
@Data
static class DeviceFeedbackError {
private String code;
private String message;
}
}

38
src/main/java/com/qyft/ms/device/model/bo/DeviceOperationalStatus.java

@ -0,0 +1,38 @@
package com.qyft.ms.device.model.bo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.util.ArrayList;
import java.util.List;
@Schema(description = "设备业务操作状态")
@Data
public class DeviceOperationalStatus {
@Schema(description = "托盘列表")
private List<Tray> trayList = new ArrayList<>();
/**
* 托盘
*/
@Data
static class Tray {
@Schema(description = "所在加热区id")
private Long heatId;
@Schema(description = "是否在加液区")
private boolean isSolutionArea;
@Schema(description = "试管列表")
private List<Tube> tubeList;
}
/**
* 试管
*/
@Data
static class Tube {
@Schema(description = "试管编号")
private Integer tubeNum;
@Schema(description = "试管内是否有样品")
private boolean isSample;
}
}

140
src/main/java/com/qyft/ms/device/model/bo/DeviceStatus.java

@ -0,0 +1,140 @@
package com.qyft.ms.device.model.bo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.util.List;
/**
* 设备当前状态
*/
@Schema(description = "设备当前传感器状态")
@Data
public class DeviceStatus {
@Schema(description = "是否是急停状态,true为急停")
private Boolean emergencyStop;
@Schema(description = "门状态,true为开启状态,false为关闭状态")
private Boolean doorStatus;
@Schema(description = "导轨机械臂状态")
private RailArm railArm;
@Schema(description = "加液操作区属性")
private LiquidArea liquidArea;
@Schema(description = "加热操作区属性")
private List<HeatArea> heatArea;
@Schema(description = "碱容器状态")
private LiquidBucket alkaliBucket;
/**
* 导轨机械臂属性
*/
@Data
public static class RailArm {
@Schema(description = "当前所在x坐标")
private Integer x;
@Schema(description = "当前所在y坐标")
private Integer y;
@Schema(description = "当前所在z坐标")
private Integer z;
@Schema(description = "当前轴1角度")
private Double joint1;
@Schema(description = "当前轴2角度")
private Double joint2;
@Schema(description = "当前机械臂(轴3)上下移动距离")
private Integer distance;
@Schema(description = "当前导轨移动距离")
private Double railDistance;
@Schema(description = "当前夹爪移动距离")
private Double clawDistance;
@Schema(description = "夹爪状态,true为张开状态,false为闭合状态")
private Boolean clawStatus;
@Schema(description = "导轨是否在原点,true为在原点")
private Boolean isZeroPos;
@Schema(description = "导轨是否在限位点,true为在限位点")
private Boolean isLimitPos;
}
/**
* 加液操作区属性
*/
@Data
public static class LiquidArea {
@Schema(description = "加液机械臂状态")
private LiquidArm liquidArm;
@Schema(description = "是否正在摇匀,true为正在摇匀,false为停止摇匀")
private Boolean isShaking;
@Schema(description = "是否存在托盘,true为存在托盘,false为无托盘")
private Boolean liquidTray;
@Schema(description = "溶液容器状态")
private List<LiquidBucket> solutionBucket;
}
/**
* 加热操作区属性
*/
@Data
public static class HeatArea {
@Schema(description = "加热器设备id")
private String hardwareId;
@Schema(description = "托盘状态,0为无托盘,1为有托盘,2为托盘抬起")
private Integer trayStatus;
@Schema(description = "是否正在加热,true为正在加热,false为未加热")
private Boolean isHeating;
@Schema(description = "拍子状态,true为存在拍子,false无拍子")
private Boolean capStatus;
@Schema(description = "拍子密封状态,true为已密封,false为未密封")
private Boolean isSealed;
@Schema(description = "加热器当前温度")
private Double temperature;
}
/**
* 加液机械臂属性
*/
@Data
public static class LiquidArm {
@Schema(description = "当前所在x坐标")
private Integer x;
@Schema(description = "当前所在y坐标")
private Integer y;
@Schema(description = "当前所在z坐标")
private Integer z;
@Schema(description = "当前轴1角度")
private Double joint1;
@Schema(description = "当前轴2角度")
private Double joint2;
@Schema(description = "加液泵状态")
private List<Pump> pump;
}
/**
* 泵属性
*/
@Data
public static class Pump {
@Schema(description = "加液泵id")
private String pumpId;
@Schema(description = "是否正在加液,true正在加液,false未运行")
private Integer isPumping;
}
@Data
public static class LiquidBucket {
@Schema(description = "容器是否为空,true为空,false不为空")
private Boolean isEmpty;
@Schema(description = "容器是否为满,true为满,false不满")
private Boolean isFull;
}
}

23
src/main/java/com/qyft/ms/device/model/entity/CtrlFunc.java

@ -0,0 +1,23 @@
package com.qyft.ms.device.model.entity;
import com.baomidou.mybatisplus.annotation.TableName;
import com.qyft.ms.system.common.base.BaseEntity;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotBlank;
import lombok.Data;
import lombok.EqualsAndHashCode;
@EqualsAndHashCode(callSuper = false)
@Schema(description = "设备控制方法记录表")
@TableName("ctrl_func")
@Data
public class CtrlFunc extends BaseEntity {
@NotBlank
@Schema(description = "控制方法的名称")
private String name;
@NotBlank
@Schema(description = "控制方法的指令")
private String funcCmd;
}

27
src/main/java/com/qyft/ms/device/model/entity/CtrlFuncStep.java

@ -0,0 +1,27 @@
package com.qyft.ms.device.model.entity;
import com.baomidou.mybatisplus.annotation.TableName;
import com.qyft.ms.system.common.base.BaseEntity;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
@EqualsAndHashCode(callSuper = false)
@Schema(description = "设备控制方法步骤表")
@TableName("ctrl_func_step")
@Data
public class CtrlFuncStep extends BaseEntity {
@Schema(description = "控制方法的指令")
private String funcCmd;
@Schema(description = "设备指令")
private String deviceCmd;
@Schema(description = "备注说明")
private String remarks;
@Schema(description = "设备指令的参数,为null则动态传入")
private String params;
}

38
src/main/java/com/qyft/ms/device/model/form/CtrlFuncForm.java

@ -0,0 +1,38 @@
package com.qyft.ms.device.model.form;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.util.List;
import java.util.Map;
@EqualsAndHashCode(callSuper = false)
@Schema(description = "设备控制方法表单")
@Data
public class CtrlFuncForm {
@Schema(description = "控制方法的名称")
private String name;
@Schema(description = "控制方法的指令")
private String funcCmd;
@Schema(description = "设备控制方法步骤")
private List<CtrlFuncStepForm> ctrlFuncStepList;
@Data
public static class CtrlFuncStepForm {
@Schema(description = "设备指令")
private String deviceCmd;
@Schema(description = "备注说明")
private String remarks;
@Schema(description = "设备指令的参数,为null则动态传入")
private Map<String, Object> params;
}
}

26
src/main/java/com/qyft/ms/device/model/vo/CtrlFuncVO.java

@ -0,0 +1,26 @@
package com.qyft.ms.device.model.vo;
import com.qyft.ms.device.model.entity.CtrlFuncStep;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.util.List;
@EqualsAndHashCode(callSuper = false)
@Schema(description = "获取所有设备控制方法返回数据")
@Data
public class CtrlFuncVO {
private Long id;
@Schema(description = "控制方法的名称")
private String name;
@Schema(description = "控制方法的指令")
private String funcCmd;
@Schema(description = "设备控制方法步骤列表")
private List<CtrlFuncStep> ctrlFuncStepList;
}

26
src/main/java/com/qyft/ms/device/model/vo/DeviceCtrlFuncVO.java

@ -0,0 +1,26 @@
package com.qyft.ms.device.model.vo;
import com.qyft.ms.device.model.entity.CtrlFuncStep;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.util.List;
@EqualsAndHashCode(callSuper = false)
@Schema(description = "获取所有设备控制方法返回数据")
@Data
public class DeviceCtrlFuncVO {
private Long id;
@Schema(description = "控制方法的名称")
private String name;
@Schema(description = "控制方法的指令")
private String funcCmd;
@Schema(description = "设备控制方法步骤列表")
private List<CtrlFuncStep> ctrlFuncStepList;
}

12
src/main/java/com/qyft/ms/device/service/DeviceAlarmService.java

@ -0,0 +1,12 @@
package com.qyft.ms.device.service;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
/**
* 设备报警服务
*/
@Service
@RequiredArgsConstructor
public class DeviceAlarmService {
}

14
src/main/java/com/qyft/ms/device/service/DeviceService.java

@ -0,0 +1,14 @@
package com.qyft.ms.device.service;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
/**
* 设备操作
*/
@Service
@RequiredArgsConstructor
public class DeviceService {
}

47
src/main/java/com/qyft/ms/device/service/DeviceStatusService.java

@ -0,0 +1,47 @@
package com.qyft.ms.device.service;
import cn.hutool.core.bean.BeanUtil;
import com.qyft.ms.device.model.bo.DeviceOperationalStatus;
import com.qyft.ms.device.model.bo.DeviceStatus;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
/**
* 设备传感器状态推送
*/
@Service
@RequiredArgsConstructor
public class DeviceStatusService {
/**
* 设备运行状态
*/
private final DeviceStatus deviceStatus = new DeviceStatus();
/**
* 设备业务状态
* -- GETTER --
* 更新设备业务操作状态
*/
@Getter
private final DeviceOperationalStatus deviceOperationalStatus = new DeviceOperationalStatus();
/**
* 更新设备状态
*/
public void updateDeviceStatus(DeviceStatus newStatus) {
synchronized (deviceStatus) {
BeanUtil.copyProperties(newStatus, deviceStatus);
}
}
/**
* 获取设备状态
*/
public DeviceStatus getDeviceStatus() {
synchronized (deviceStatus) {
return BeanUtil.copyProperties(deviceStatus, DeviceStatus.class);
}
}
}

166
src/main/java/com/qyft/ms/device/service/DeviceStepService.java

@ -0,0 +1,166 @@
package com.qyft.ms.device.service;
import com.qyft.ms.model.bo.TubeSol;
import com.qyft.ms.service.CMDService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import java.util.List;
/**
* 设备步骤操作
*/
@Slf4j
@Service
@RequiredArgsConstructor
public class DeviceStepService {
CMDService cmdService;
/**
* 抬起托盘
*
* @param heatId 加热区id
*/
public boolean upTray(String heatId) {
// Map<String, Object> params = Map.of("heatId", heatId);
// List<Supplier<Boolean>> cmdList = cmdService.upTray(params);
// return cmdList.stream().allMatch(Supplier::get);
return true;
}
/**
* 降下托盘
*
* @param heatId 加热区id
*/
public boolean downTray(String heatId) {
// Map<String, Object> params = Map.of("heatId", heatId);
// List<Supplier<Boolean>> cmdList = cmdService.downTray(params);
// return cmdList.stream().allMatch(Supplier::get);
return true;
}
/**
* 添加溶液
*
* @param tubeSolList 需要添加溶液的试管与溶液
*/
public boolean addLiquid(List<TubeSol> tubeSolList) {
// for (TubeSol tubeSol : tubeSolList) {
// Map<String, Object> params = Map.of("tubeNum", tubeSol.getTubeNum(), "solutionId", tubeSol.getSolId(), "volume", tubeSol.getVolume());
// List<Supplier<Boolean>> cmdList = cmdService.downTray(params);
// boolean result = cmdList.stream().allMatch(Supplier::get);
// if (!result) {
// return false;
// }
// }
return true;
}
/**
* 将指定加热区的托盘移至加液区
*
* @param heatId 加热区id
*/
public boolean moveToSol(String heatId) {
// Map<String, Object> params = Map.of("heatId", heatId);
// List<Supplier<Boolean>> cmdList = cmdService.moveToActionArea(params);
// return cmdList.stream().allMatch(Supplier::get);
return true;
}
/**
* 移至加热
*
* @param heatId 加热区id
*/
public boolean moveToHeat(String heatId) {
// Map<String, Object> params = Map.of("heatId", heatId);
// List<Supplier<Boolean>> cmdList = cmdService.moveToHeatArea(params);
// return cmdList.stream().allMatch(Supplier::get);
return true;
}
/**
* 摇匀
*
* @param second 摇匀时间
*/
public boolean shaking(int second) {
// Map<String, Object> params = Map.of();
// List<Supplier<Boolean>> cmdList = cmdService.startShakeUp(params);
// boolean result = cmdList.stream().allMatch(Supplier::get);
// if (result) {
// this.delay(second);
// cmdList = cmdService.stopShakeUp(params);
// result = cmdList.stream().allMatch(Supplier::get);
// return result;
// }
// return false;
return true;
}
/**
* 开始加热
*
* @param heatId 加热区id
* @param temperature 目标温度
*/
public boolean startHeating(String heatId, double temperature) {
// Map<String, Object> params = Map.of("heatId", heatId, "temperature", temperature);
// List<Supplier<Boolean>> cmdList = cmdService.startHeat(params);
// return cmdList.stream().allMatch(Supplier::get);
return true;
}
/**
* 停止加热
*
* @param heatId 加热区id
*/
public boolean stopHeating(String heatId) {
// Map<String, Object> params = Map.of("heatId", heatId);
// List<Supplier<Boolean>> cmdList = cmdService.stopHeat(params);
// return cmdList.stream().allMatch(Supplier::get);
return true;
}
/**
* 停止加热
*/
public boolean takePhoto() {
// Map<String, Object> params = Map.of();
// List<Supplier<Boolean>> cmdList = cmdService.takePhoto(params);
// return cmdList.stream().allMatch(Supplier::get);
return true;
}
//移至异常
public boolean moveToExc() {
return true;
}
//移除异常
public boolean moveOutToExc() {
return true;
}
/**
* 等待
*
* @param second
*/
public boolean delay(int second) {
try {
Thread.sleep(second * 1000L);
return true;
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
return false;
}
}

261
src/main/java/com/qyft/ms/device/service/DeviceTcpCMDService.java

@ -0,0 +1,261 @@
package com.qyft.ms.device.service;
import cn.hutool.json.JSONUtil;
import com.qyft.ms.device.client.TcpClient;
import com.qyft.ms.device.common.constant.DeviceCommands;
import com.qyft.ms.device.common.jsonrpc.JsonRpcRequest;
import com.qyft.ms.device.model.bo.DeviceFeedback;
import jakarta.annotation.PostConstruct;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.*;
/**
* 设备tcp指令发送服务
*/
@Slf4j
@Service
@RequiredArgsConstructor
public class DeviceTcpCMDService {
private final TcpClient tcpClient;
private final BlockingDeque<Callable<Boolean>> taskQueue = new LinkedBlockingDeque<>();
private final ExecutorService executorService = Executors.newSingleThreadExecutor();
@PostConstruct
private void initExecutorThread() {
new Thread(this::executeTasks).start();
}
private boolean putTask(String method) {
JsonRpcRequest request = new JsonRpcRequest();
request.setMethod(method);
return this.putTask(request);
}
private boolean putTask(String method, Map<String, Object> params) {
JsonRpcRequest request = new JsonRpcRequest();
request.setMethod(method);
request.setParams(params);
return this.putTask(request);
}
private boolean putTask(JsonRpcRequest request) {
Callable<Boolean> task = () -> {
DeviceFeedback deviceFeedback = tcpClient.sendCommand(request);
if (deviceFeedback == null || deviceFeedback.getError() != null) {
log.error("TCP 指令执行错误 request:{} feedback:{}", JSONUtil.toJsonStr(request), JSONUtil.toJsonStr(deviceFeedback));
return false;
}
return true;
};
try {
taskQueue.put(task);
return executorService.submit(taskQueue.take()).get();
} catch (Exception e) {
log.error("TCP 指令执行错误 request:{}", JSONUtil.toJsonStr(request), e);
}
return false;
}
private void executeTasks() {
while (true) {
try {
Callable<Boolean> task = taskQueue.take();
task.call();
} catch (Exception e) {
Thread.currentThread().interrupt();
}
}
}
/**
* 使指定轴的电机回原点
* @param axis 轴标识可选值为 "X", "Y", "Z"
* @return 操作是否成功
*/
public boolean motorMoveToHome(String axis) {
Map<String, Object> params = new HashMap<>();
params.put("axis", axis);
return this.putTask(DeviceCommands.MOTOR_MOVE_TO_HOME, params);
}
/**
* 设置指定轴的电机运行时电流
* @param axis 轴标识可选值为 "X", "Y", "Z"
* @param current 电流值
* @return 操作是否成功
*/
public boolean setMotorRunningCurrent(String axis, double current) {
Map<String, Object> params = new HashMap<>();
params.put("axis", axis);
params.put("current", current);
DeviceFeedback deviceFeedback = tcpClient.sendCommand(DeviceCommands.SET_MOTOR_RUNNING_CURRENT, params);
if (deviceFeedback == null || deviceFeedback.getError() != null) {
log.error("TCP setMotorRunningCurrent 指令执行错误 {}", JSONUtil.toJsonStr(deviceFeedback));
return false;
}
return true;
}
/**
* 移动指定轴的电机到指定位置
* @param axis 轴标识可选值为 "X", "Y", "Z"
* @param position 轴位置单位mm
* @return 操作是否成功
*/
public boolean moveMotorToPosition(String axis, double position) {
Map<String, Object> params = new HashMap<>();
params.put("axis", axis);
params.put("position", position);
return this.putTask(DeviceCommands.MOVE_MOTOR_TO_POSITION, params);
}
/**
* 设置指定轴的电机的运行速度
* @param axis 轴标识可选值为 "X", "Y", "Z"
* @param speed 速度值
* @return 操作是否成功
*/
public boolean setMotorSpeed(String axis, double speed) {
Map<String, Object> params = new HashMap<>();
params.put("axis", axis);
params.put("speed", speed);
DeviceFeedback deviceFeedback = tcpClient.sendCommand(DeviceCommands.SET_MOTOR_SPEED, params);
if (deviceFeedback == null || deviceFeedback.getError() != null) {
log.error("TCP setMotorSpeed 指令执行错误 {}", JSONUtil.toJsonStr(deviceFeedback));
return false;
}
return true;
}
// 三通阀控制方法
/**
* 切换三通阀到基质状态
* @return 操作是否成功
*/
public boolean switchThreeWayValveToSubstrate() {
return this.putTask(DeviceCommands.SWITCH_THREE_WAY_VALVE_TO_SUBSTRATE);
}
/**
* 切换三通阀到喷涂状态
* @return 操作是否成功
*/
public boolean switchThreeWayValveToSpray() {
return this.putTask(DeviceCommands.SWITCH_THREE_WAY_VALVE_TO_SPRAY);
}
// 除湿阀清洗阀喷嘴阀控制方法
/**
* 控制指定阀的开启或关闭
* @param valveType 阀类型可选值为 "Dehumidification", "Cleaning", "Nozzle"
* @param isOpen 是否开启true 为开启false 为关闭
* @return 操作是否成功
*/
public boolean controlValve(String valveType, boolean isOpen) {
Map<String, Object> params = new HashMap<>();
params.put("valveType", valveType);
params.put("isOpen", isOpen);
return this.putTask(DeviceCommands.CONTROL_VALVE, params);
}
// 照明灯板控制方法
/**
* 以指定亮度开启照明灯板
* @param brightness 亮度值范围 0 - 100
* @return 操作是否成功
*/
public boolean turnOnLightPanel(int brightness) {
Map<String, Object> params = new HashMap<>();
params.put("brightness", brightness);
return this.putTask(DeviceCommands.TURN_ON_LIGHT_PANEL, params);
}
/**
* 关闭照明灯板
* @return 操作是否成功
*/
public boolean turnOffLightPanel() {
return this.putTask(DeviceCommands.TURN_OFF_LIGHT_PANEL);
}
// 高压电控制方法
/**
* 以指定电压值开启高压电
* @param voltage 电压值
* @return 操作是否成功
*/
public boolean turnOnHighVoltage(double voltage) {
Map<String, Object> params = new HashMap<>();
params.put("voltage", voltage);
return this.putTask(DeviceCommands.TURN_ON_HIGH_VOLTAGE, params);
}
/**
* 关闭高压电
* @return 操作是否成功
*/
public boolean turnOffHighVoltage() {
return this.putTask(DeviceCommands.TURN_OFF_HIGH_VOLTAGE);
}
// 注射泵控制方法
/**
* 以指定转速方向和时间开启注射泵
* @param rotationSpeed 转速
* @param direction 方向
* @param time 时间
* @return 操作是否成功
*/
public boolean turnOnSyringePump(double rotationSpeed, String direction, double time) {
Map<String, Object> params = new HashMap<>();
params.put("rotationSpeed", rotationSpeed);
params.put("direction", direction);
params.put("time", time);
return this.putTask(DeviceCommands.TURN_ON_SYRINGE_PUMP, params);
}
/**
* 停止注射泵
* @return 操作是否成功
*/
public boolean turnOffSyringePump() {
return this.putTask(DeviceCommands.TURN_OFF_SYRINGE_PUMP);
}
/**
* 设置注射泵的容量时间比例参数
* @param capacity 容量
* @param timeRatio 时间比例
* @return 操作是否成功
*/
public boolean setSyringePumpParameters(double capacity, double timeRatio) {
Map<String, Object> params = new HashMap<>();
params.put("capacity", capacity);
params.put("timeRatio", timeRatio);
return this.putTask(DeviceCommands.SET_SYRINGE_PUMP_PARAMETERS, params);
}
/**
* 推送指定容量的液体
* @param volume 容量单位ml
* @return 操作是否成功
*/
public boolean pushVolume(double volume) {
Map<String, Object> params = new HashMap<>();
params.put("volume", volume);
return this.putTask(DeviceCommands.PUSH_VOLUME, params);
}
}

28
src/main/java/com/qyft/ms/device/service/ICtrlFuncService.java

@ -0,0 +1,28 @@
package com.qyft.ms.device.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.qyft.ms.device.model.bo.DeviceCtrlFuncCMD;
import com.qyft.ms.device.model.entity.CtrlFunc;
import com.qyft.ms.device.model.form.CtrlFuncForm;
import com.qyft.ms.device.model.vo.DeviceCtrlFuncVO;
import java.util.List;
public interface ICtrlFuncService extends IService<CtrlFunc> {
List<DeviceCtrlFuncCMD> findAllStepCMD();
List<CtrlFunc> findAll();
DeviceCtrlFuncVO findVOById(Long id);
long countFuncCmdByFuncCmd(String funcCmd);
boolean addCtrlFunc(CtrlFuncForm ctrlFuncForm);
boolean updateCtrlFunc(Long id, CtrlFuncForm ctrlFuncForm);
boolean deleteCtrlFunc(String idsStr);
}

12
src/main/java/com/qyft/ms/device/service/ICtrlFuncStepService.java

@ -0,0 +1,12 @@
package com.qyft.ms.device.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.qyft.ms.device.model.entity.CtrlFuncStep;
import java.util.List;
public interface ICtrlFuncStepService extends IService<CtrlFuncStep> {
List<CtrlFuncStep> selectListByFuncCmd(String funcCmd);
}

132
src/main/java/com/qyft/ms/device/service/impl/CtrlFuncServiceImpl.java

@ -0,0 +1,132 @@
package com.qyft.ms.device.service.impl;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.qyft.ms.device.mapper.CtrlFuncMapper;
import com.qyft.ms.device.model.bo.DeviceCtrlFuncCMD;
import com.qyft.ms.device.model.entity.CtrlFunc;
import com.qyft.ms.device.model.entity.CtrlFuncStep;
import com.qyft.ms.device.model.form.CtrlFuncForm;
import com.qyft.ms.device.model.vo.DeviceCtrlFuncVO;
import com.qyft.ms.device.service.ICtrlFuncService;
import com.qyft.ms.device.service.ICtrlFuncStepService;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
@RequiredArgsConstructor
@Service
public class CtrlFuncServiceImpl extends ServiceImpl<CtrlFuncMapper, CtrlFunc> implements ICtrlFuncService {
private final ICtrlFuncStepService ctrlFuncStepService;
private static final List<DeviceCtrlFuncCMD> ctrlFuncDeviceCMDList = new ArrayList<>();
static {
ctrlFuncDeviceCMDList.add(new DeviceCtrlFuncCMD("开门", "openDoor", "{}"));
ctrlFuncDeviceCMDList.add(new DeviceCtrlFuncCMD("关门", "closeDoor", "{}"));
ctrlFuncDeviceCMDList.add(new DeviceCtrlFuncCMD("导轨机械臂运动到指定点位", "moveRailArmToPoint", "{\"x\":0,\"y\":0,\"z\":0}"));
ctrlFuncDeviceCMDList.add(new DeviceCtrlFuncCMD("导轨机械臂相对运动", "moveRailArmRelative", "{\"x\":0,\"y\":0,\"z\":0}"));
ctrlFuncDeviceCMDList.add(new DeviceCtrlFuncCMD("导轨机械臂移至指定加热区上方", "moveToHeatArea", "{\"heatId\":2}"));
ctrlFuncDeviceCMDList.add(new DeviceCtrlFuncCMD("导轨机械臂移至加液区上方", "moveToActionArea", "{}"));
ctrlFuncDeviceCMDList.add(new DeviceCtrlFuncCMD("导轨机械臂移至拍子存放区上方", "moveToCapArea", "{}"));
ctrlFuncDeviceCMDList.add(new DeviceCtrlFuncCMD("打开导轨机械臂夹爪", "openClaw", "{}"));
ctrlFuncDeviceCMDList.add(new DeviceCtrlFuncCMD("收合导轨机械臂夹爪", "closeClaw", "{}"));
ctrlFuncDeviceCMDList.add(new DeviceCtrlFuncCMD("指定加热区密封拍子", "sealLidOn", "{\"heatId\":2}"));
ctrlFuncDeviceCMDList.add(new DeviceCtrlFuncCMD("指定加热区解除密封拍子", "sealLidOff", "{\"heatId\":2}"));
ctrlFuncDeviceCMDList.add(new DeviceCtrlFuncCMD("拍子存放区高度向上一个级别", "capHeightUp", "{}"));
ctrlFuncDeviceCMDList.add(new DeviceCtrlFuncCMD("拍子存放区高度向下移动一个级别", "capHeightDown", "{}"));
ctrlFuncDeviceCMDList.add(new DeviceCtrlFuncCMD("指定加热区开始加热", "startHeating", "{\"heatId\":2}"));
ctrlFuncDeviceCMDList.add(new DeviceCtrlFuncCMD("指定加热区停止加热", "stopHeating", "{\"heatId\":2}"));
ctrlFuncDeviceCMDList.add(new DeviceCtrlFuncCMD("指定加热区抬起托盘", "raiseTray", "{\"heatId\":2}"));
ctrlFuncDeviceCMDList.add(new DeviceCtrlFuncCMD("指定加热区放下托盘", "lowerTray", "{\"heatId\":2}"));
ctrlFuncDeviceCMDList.add(new DeviceCtrlFuncCMD("依次添加溶液", "addLiquid", "{\"tubeSolList\":[{\"tubeNum\":1,\"solId\":15,\"volume\":20}]}"));
ctrlFuncDeviceCMDList.add(new DeviceCtrlFuncCMD("开始摇匀", "startShaking", "{}"));
ctrlFuncDeviceCMDList.add(new DeviceCtrlFuncCMD("停止摇匀", "stopShaking", "{}"));
ctrlFuncDeviceCMDList.add(new DeviceCtrlFuncCMD("拍照", "takePhoto", "{}"));
}
@Override
public List<DeviceCtrlFuncCMD> findAllStepCMD() {
return ctrlFuncDeviceCMDList;
}
@Override
public List<CtrlFunc> findAll() {
return this.baseMapper.selectList(null);
}
@Override
public DeviceCtrlFuncVO findVOById(Long id) {
CtrlFunc ctrlFunc = this.getById(id);
List<CtrlFuncStep> ctrlFuncStepList = ctrlFuncStepService.selectListByFuncCmd(ctrlFunc.getFuncCmd());
DeviceCtrlFuncVO ctrlFuncVO = new DeviceCtrlFuncVO();
ctrlFuncVO.setId(ctrlFunc.getId());
ctrlFuncVO.setName(ctrlFunc.getName());
ctrlFuncVO.setFuncCmd(ctrlFunc.getFuncCmd());
ctrlFuncVO.setFuncCmd(ctrlFunc.getFuncCmd());
ctrlFuncVO.setCtrlFuncStepList(ctrlFuncStepList);
return ctrlFuncVO;
}
@Override
public long countFuncCmdByFuncCmd(String funcCmd) {
return this.count(new LambdaQueryWrapper<CtrlFunc>().eq(CtrlFunc::getFuncCmd, funcCmd));
}
@Override
public boolean addCtrlFunc(CtrlFuncForm ctrlFuncForm) {
CtrlFunc ctrlFunc = new CtrlFunc();
ctrlFunc.setFuncCmd(ctrlFuncForm.getFuncCmd());
ctrlFunc.setName(ctrlFuncForm.getName());
if (this.save(ctrlFunc)) {
return reSaveCtrlFunc(ctrlFuncForm, ctrlFunc);
}
return false;
}
@Override
public boolean updateCtrlFunc(Long id, CtrlFuncForm ctrlFuncForm) {
CtrlFunc ctrlFunc = this.getById(id);
if (ctrlFunc != null) {
ctrlFunc.setFuncCmd(ctrlFuncForm.getFuncCmd());
ctrlFunc.setName(ctrlFuncForm.getName());
if (this.updateById(ctrlFunc)) {
return reSaveCtrlFunc(ctrlFuncForm, ctrlFunc);
}
}
return false;
}
@Override
public boolean deleteCtrlFunc(String idsStr) {
List<Long> ids = Arrays.stream(idsStr.split(","))
.map(Long::parseLong)
.collect(Collectors.toList());
for (Long id : ids) {
String funcCmd = this.getById(id).getFuncCmd();
ctrlFuncStepService.remove(new LambdaQueryWrapper<CtrlFuncStep>().eq(CtrlFuncStep::getFuncCmd, funcCmd));
}
return this.removeByIds(ids);
}
private boolean reSaveCtrlFunc(CtrlFuncForm ctrlFuncForm, CtrlFunc ctrlFunc) {
ctrlFuncStepService.remove(new LambdaQueryWrapper<CtrlFuncStep>().eq(CtrlFuncStep::getFuncCmd, ctrlFunc.getFuncCmd()));
List<CtrlFuncForm.CtrlFuncStepForm> ctrlFuncStepFormList = ctrlFuncForm.getCtrlFuncStepList();
List<CtrlFuncStep> ctrlFuncStepList = ctrlFuncStepFormList.stream()
.map(ctrlFuncStepForm -> {
CtrlFuncStep ctrlFuncStep = new CtrlFuncStep();
ctrlFuncStep.setFuncCmd(ctrlFunc.getFuncCmd());
ctrlFuncStep.setDeviceCmd(ctrlFuncStepForm.getDeviceCmd());
ctrlFuncStep.setRemarks(ctrlFuncStepForm.getRemarks());
ctrlFuncStep.setParams(JSONUtil.toJsonStr(ctrlFuncStepForm.getParams()));
return ctrlFuncStep;
}).toList();
return ctrlFuncStepService.saveBatch(ctrlFuncStepList);
}
}

20
src/main/java/com/qyft/ms/device/service/impl/CtrlFuncStepServiceImpl.java

@ -0,0 +1,20 @@
package com.qyft.ms.device.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.qyft.ms.device.mapper.CtrlFuncStepMapper;
import com.qyft.ms.device.model.entity.CtrlFuncStep;
import com.qyft.ms.device.service.ICtrlFuncStepService;
import org.springframework.stereotype.Service;
import java.util.List;
@Service
public class CtrlFuncStepServiceImpl extends ServiceImpl<CtrlFuncStepMapper, CtrlFuncStep> implements ICtrlFuncStepService {
@Override
public List<CtrlFuncStep> selectListByFuncCmd(String funcCmd) {
return this.list(new LambdaQueryWrapper<CtrlFuncStep>().eq(CtrlFuncStep::getFuncCmd, funcCmd));
}
}

34
src/main/java/com/qyft/ms/enums/DirectiveTypeEnum.java

@ -0,0 +1,34 @@
package com.qyft.ms.enums;
import cn.hutool.core.util.StrUtil;
public enum DirectiveTypeEnum {
ADD("add", "抬起托盘");
private final String typeCode;
private final String typeName;
DirectiveTypeEnum(String typeCode, String typeName) {
this.typeCode = typeCode;
this.typeName = typeName;
}
public static String getByCode(String code) {
if (StrUtil.isNotBlank(code)) {
for (DirectiveTypeEnum item : DirectiveTypeEnum.values()) {
if (item.typeCode().equals(code.trim())) {
return item.typeCode();
}
}
}
return null;
}
public String typeCode() {
return typeCode;
}
public String typeName() {
return typeName;
}
}

28
src/main/java/com/qyft/ms/generator/CodeGenerator.java

@ -0,0 +1,28 @@
package com.qyft.ms.generator;
import com.baomidou.mybatisplus.generator.FastAutoGenerator;
import com.baomidou.mybatisplus.generator.config.OutputFile;
import com.baomidou.mybatisplus.generator.engine.FreemarkerTemplateEngine;
import java.util.Collections;
public class CodeGenerator {
public static void main(String[] args) {
FastAutoGenerator.create("jdbc:sqlite:" + System.getProperty("user.dir") + "/matrix-spray.db", "", "")
.globalConfig(builder -> {
builder.author("qyft") // 设置作者
.enableSwagger() // 开启 swagger 模式
.outputDir("D://new"); // 指定输出目录
})
.packageConfig(builder -> {
builder.parent("com.qyft.ms") // 设置父包名
.moduleName("device") // 设置父包模块名
.pathInfo(Collections.singletonMap(OutputFile.xml, "D://new/resources/mapper")); // 设置mapperXml生成路径
})
.strategyConfig(builder -> {
builder.addTablePrefix("t_", "c_"); // 设置过滤表前缀
})
.templateEngine(new FreemarkerTemplateEngine()) // 使用Freemarker引擎模板默认的是Velocity引擎模板
.execute();
}
}

12
src/main/java/com/qyft/ms/mapper/LogsMapper.java

@ -0,0 +1,12 @@
package com.qyft.ms.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.qyft.ms.model.entity.Logs;
import org.apache.ibatis.annotations.Mapper;
/**
* 日志持久层接口
*/
@Mapper
public interface LogsMapper extends BaseMapper<Logs> {
}

29
src/main/java/com/qyft/ms/mapper/SysSettingsMapper.java

@ -0,0 +1,29 @@
package com.qyft.ms.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.qyft.ms.model.dto.SysSettingsDTO;
import com.qyft.ms.model.entity.SysSettings;
import com.qyft.ms.model.vo.SysSettingVO;
import org.apache.ibatis.annotations.Mapper;
import java.util.List;
/**
* <p>
* Mapper 接口
* </p>
*
* @author 郭安鹏
* @since 2025-02-17
*/
@Mapper
public interface SysSettingsMapper extends BaseMapper<SysSettings> {
List<SysSettingVO> getConfig(String type);
void updateById(SysSettingsDTO dto);
int getIdByCode(String type);
}

52
src/main/java/com/qyft/ms/model/bo/CraftsStepMethod.java

@ -0,0 +1,52 @@
package com.qyft.ms.model.bo;
import lombok.Data;
/**
* 工艺步骤执行方法
*/
@Data
public class CraftsStepMethod {
/**
* 上升托盘
*/
public static final String UP_TRAY = "upTray";
/**
* 降下托盘
*/
public static final String DOWN_TRAY = "downTray";
/**
* 加液
*/
public static final String ADD_LIQUID = "addLiquid";
/**
* 移动到溶液位置
*/
public static final String MOVE_TO_SOL = "moveToSol";
/**
* 移动到加热器位置
*/
public static final String MOVE_TO_HEAT = "moveToHeat";
/**
* 摇晃
*/
public static final String SHAKING = "shaking";
/**
* 开始加热
*/
public static final String START_HEATING = "startHeating";
/**
* 停止加热
*/
public static final String STOP_HEATING = "stopHeating";
/**
* 拍照
*/
public static final String TAKE_PHOTO = "takePhoto";
/**
* 等待
*/
public static final String DELAY = "delay";
}

35
src/main/java/com/qyft/ms/model/bo/CraftsStepStatus.java

@ -0,0 +1,35 @@
package com.qyft.ms.model.bo;
import lombok.Data;
/**
* 工艺步骤执行状态
*/
@Data
public class CraftsStepStatus {
/**
* 未执行
*/
public static final int NOT_EXECUTED = 0;
/**
* 正在执行
*/
public static final int IN_PROGRESS = 1;
/**
* 暂停执行
*/
public static final int PAUSED = 2;
/**
* 停止执行
*/
public static final int STOPPED = 3;
/**
* 执行错误
*/
public static final int ERROR = 4;
/**
* 执行完成
*/
public static final int FINISH = 6;
}

23
src/main/java/com/qyft/ms/model/bo/TubeSol.java

@ -0,0 +1,23 @@
package com.qyft.ms.model.bo;
import lombok.Data;
/**
* 试管添加溶液
*/
@Data
public class TubeSol {
/**
* 需要添加溶液的试管编号
*/
private Integer tubeNum;
/**
* 溶液id
*/
private Long solId;
/**
* 加液量
*/
private Integer volume;
}

14
src/main/java/com/qyft/ms/model/dto/BaseDataDTO.java

@ -0,0 +1,14 @@
package com.qyft.ms.model.dto;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
@Data
public class BaseDataDTO {
@Schema(description = "加热区id")
private Long id;
@Schema(description = "温度")
private Long temperature;
@Schema(description = "工艺id")
private Long craftId;
}

13
src/main/java/com/qyft/ms/model/dto/CmdInjectFluidDTO.java

@ -0,0 +1,13 @@
package com.qyft.ms.model.dto;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
@Data
public class CmdInjectFluidDTO {
@Schema(description = "指令id")
private String commandId;
@Schema(description = "加液数据list")
private InjectFluid[] injectFluids;
}

14
src/main/java/com/qyft/ms/model/dto/ContainerDTO.java

@ -0,0 +1,14 @@
package com.qyft.ms.model.dto;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
@Data
public class ContainerDTO {
@Schema(description = "容器id")
private Long id;
@Schema(description = "溶液id")
private Long solutionId;
@Schema(description = "已使用容量")
private Integer capacityUsed;
}

14
src/main/java/com/qyft/ms/model/dto/InjectFluid.java

@ -0,0 +1,14 @@
package com.qyft.ms.model.dto;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
@Data
public class InjectFluid {
@Schema(description = "试管编号")
private Integer tubeNum;
@Schema(description = "溶液id")
private Integer solutionId;
@Schema(description = "容量")
private Integer volume;
}

10
src/main/java/com/qyft/ms/model/dto/LogDTO.java

@ -0,0 +1,10 @@
package com.qyft.ms.model.dto;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
@Data
public class LogDTO {
@Schema(description = "日志信息")
private String text;
}

11
src/main/java/com/qyft/ms/model/dto/PauseCraftsDto.java

@ -0,0 +1,11 @@
package com.qyft.ms.model.dto;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
@Schema(description = "暂停执行工艺")
@Data
public class PauseCraftsDto {
@Schema(description = "加热区id")
private String heatId;
}

11
src/main/java/com/qyft/ms/model/dto/ResumeCraftsDto.java

@ -0,0 +1,11 @@
package com.qyft.ms.model.dto;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
@Schema(description = "恢复执行工艺")
@Data
public class ResumeCraftsDto {
@Schema(description = "加热区id")
private String heatId;
}

13
src/main/java/com/qyft/ms/model/dto/StartCraftsDTO.java

@ -0,0 +1,13 @@
package com.qyft.ms.model.dto;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
@Schema(description = "开始工艺")
@Data
public class StartCraftsDTO {
@Schema(description = "工艺id")
private Long craftId;
@Schema(description = "加热区id")
private String heatId;
}

13
src/main/java/com/qyft/ms/model/dto/StartHeatDTO.java

@ -0,0 +1,13 @@
package com.qyft.ms.model.dto;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
@Data
public class StartHeatDTO {
@Schema(description = "指令id")
private String commandId;
@Schema(description = "加液数据list")
private Integer[] heatIds;
}

11
src/main/java/com/qyft/ms/model/dto/StopCraftsDto.java

@ -0,0 +1,11 @@
package com.qyft.ms.model.dto;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
@Schema(description = "恢复执行工艺")
@Data
public class StopCraftsDto {
@Schema(description = "加热区id")
private String heatId;
}

10
src/main/java/com/qyft/ms/model/dto/StopTaskDTO.java

@ -0,0 +1,10 @@
package com.qyft.ms.model.dto;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
@Data
public class StopTaskDTO {
@Schema(description = "实验id")
private Long taskId;
}

16
src/main/java/com/qyft/ms/model/dto/SysSettingsDTO.java

@ -0,0 +1,16 @@
package com.qyft.ms.model.dto;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
@Data
public class SysSettingsDTO {
@Schema(description = "主键id")
private Long id;
@Schema(description = "值")
private String value;
@Schema(description = "温度")
private Long temperature;
@Schema(description = "工艺id")
private Long craftId;
}

10
src/main/java/com/qyft/ms/model/dto/TaskDTO.java

@ -0,0 +1,10 @@
package com.qyft.ms.model.dto;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
@Data
public class TaskDTO {
@Schema(description = "实验名称")
private String name;
}

22
src/main/java/com/qyft/ms/model/dto/WebsocketDTO.java

@ -0,0 +1,22 @@
package com.qyft.ms.model.dto;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotBlank;
import lombok.Data;
import java.util.List;
import java.util.Map;
@Data
public class WebsocketDTO {
@Schema(description = "指令id,不指定后台会自动生成uuid")
private String commandId;
@NotBlank()
@Schema(description = "指令类型", example = "upTray")
private String commandName;
@Schema(description = "参数")
private Map<String, Object> params;
}

35
src/main/java/com/qyft/ms/model/entity/Container.java

@ -0,0 +1,35 @@
package com.qyft.ms.model.entity;
import com.baomidou.mybatisplus.annotation.TableName;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotBlank;
import lombok.Data;
import lombok.EqualsAndHashCode;
@EqualsAndHashCode(callSuper = false)
@Schema(description = "容器表")
@TableName("container")
@Data
public class Container {
@NotBlank
@Schema(description = "容器id")
private Long id;
@NotBlank
@Schema(description = "类型 0 溶液 1 废液")
private Long type;
@Schema(description = "溶液id")
private Long solutionId;
@Schema(description = "机器id")
private String pumpId;
@NotBlank
@Schema(description = "总容量")
private Integer capacityTotal;
@NotBlank
@Schema(description = "使用容量")
private Integer capacityUsed;
}

25
src/main/java/com/qyft/ms/model/entity/Crafts.java

@ -0,0 +1,25 @@
package com.qyft.ms.model.entity;
import com.baomidou.mybatisplus.annotation.TableName;
import com.qyft.ms.system.common.base.BaseEntity;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotBlank;
import lombok.Data;
import lombok.EqualsAndHashCode;
@EqualsAndHashCode(callSuper = true)
@Schema(description = "工艺")
@TableName("crafts")
@Data
public class Crafts extends BaseEntity {
@NotBlank
@Schema(description = "工艺名称")
private String name;
@Schema(description = "工艺步骤")
private String steps;
@Schema(description = "矿石ID")
private Long oresId;
}

30
src/main/java/com/qyft/ms/model/entity/Logs.java

@ -0,0 +1,30 @@
package com.qyft.ms.model.entity;
import com.baomidou.mybatisplus.annotation.TableName;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotBlank;
import lombok.Data;
import lombok.EqualsAndHashCode;
@EqualsAndHashCode(callSuper = false)
@Schema(description = "日志")
@TableName("logs")
@Data
public class Logs {
@NotBlank
@Schema(description = "id")
private Long id;
@NotBlank
@Schema(description = "创建人")
private Long createUser;
@NotBlank
@Schema(description = "日志内容")
private String text;
@NotBlank
@Schema(description = "创建时间")
private String createTime;
}

20
src/main/java/com/qyft/ms/model/entity/Ores.java

@ -0,0 +1,20 @@
package com.qyft.ms.model.entity;
import com.baomidou.mybatisplus.annotation.TableName;
import com.qyft.ms.system.common.base.BaseEntity;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotBlank;
import lombok.Data;
import lombok.EqualsAndHashCode;
@EqualsAndHashCode(callSuper = true)
@Schema(description = "矿石")
@TableName("ores")
@Data
public class Ores extends BaseEntity {
@NotBlank()
@Schema(description = "矿石名称")
private String name;
}

20
src/main/java/com/qyft/ms/model/entity/Solutions.java

@ -0,0 +1,20 @@
package com.qyft.ms.model.entity;
import com.baomidou.mybatisplus.annotation.TableName;
import com.qyft.ms.system.common.base.BaseEntity;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotBlank;
import lombok.Data;
import lombok.EqualsAndHashCode;
@EqualsAndHashCode(callSuper = true)
@Schema(description = "溶液")
@TableName("solutions")
@Data
public class Solutions extends BaseEntity {
@NotBlank()
@Schema(description = "溶液名称")
private String name;
}

43
src/main/java/com/qyft/ms/model/entity/SysSettings.java

@ -0,0 +1,43 @@
package com.qyft.ms.model.entity;
import com.baomidou.mybatisplus.annotation.TableName;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
/**
* <p>
* 系统配置
* </p>
*
* @author 郭安鹏
* @since 2025-02-17
*/
@EqualsAndHashCode(callSuper = false)
@Schema(description = "系统配置")
@TableName("sys_settings")
@Data
public class SysSettings {
@Schema(description = "主键id")
private Long id;
@Schema(description = "父id")
private Long parentId;
@Schema(description = "名称")
private String name;
@Schema(description = "code")
private String code;
@Schema(description = "配置值")
private String value1;
@Schema(description = "配置值")
private String value2;
@Schema(description = "配置值")
private String value3;
}

30
src/main/java/com/qyft/ms/model/entity/TaskSteps.java

@ -0,0 +1,30 @@
package com.qyft.ms.model.entity;
import com.baomidou.mybatisplus.annotation.TableName;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotBlank;
import lombok.Data;
import lombok.EqualsAndHashCode;
@EqualsAndHashCode(callSuper = false)
@Schema(description = "实验步骤")
@TableName("task_steps")
@Data
public class TaskSteps {
@NotBlank
@Schema(description = "id")
private Long id;
@NotBlank
@Schema(description = "实验id")
private Long taskId;
@NotBlank
@Schema(description = "步骤描述")
private String stepDescription;
@NotBlank
@Schema(description = "创建时间")
private String createTime;
}

35
src/main/java/com/qyft/ms/model/entity/Tasks.java

@ -0,0 +1,35 @@
package com.qyft.ms.model.entity;
import com.baomidou.mybatisplus.annotation.TableName;
import com.qyft.ms.system.common.base.BaseEntity;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotBlank;
import lombok.Data;
import lombok.EqualsAndHashCode;
@EqualsAndHashCode(callSuper = true)
@Schema(description = "实验")
@TableName("tasks")
@Data
public class Tasks extends BaseEntity {
@NotBlank
@Schema(description = "实验名称")
private String name;
@NotBlank
@Schema(description = "开始时间")
private String startTime;
@Schema(description = "创建人")
private Long createUser;
@Schema(description = "结束时间")
private String endTime;
@Schema(description = "状态 1 执行中 2 执行完毕")
private Integer status;
@Schema(description = "是否删除 0 未删除 1 已删除")
private Integer isDeleted;
}

23
src/main/java/com/qyft/ms/model/form/CMDForm.java

@ -0,0 +1,23 @@
package com.qyft.ms.model.form;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotBlank;
import lombok.Data;
import java.util.List;
@Schema(description = "指令")
@Data
public class CMDForm {
@Schema(description = "指令id,不指定后台会自动生成uuid")
private String commandId;
@NotBlank()
@Schema(description = "指令类型", example = "upTray")
private String command;
@Schema(description = "参数")
private List<Object> params;
}

27
src/main/java/com/qyft/ms/model/vo/BaseDataVO.java

@ -0,0 +1,27 @@
package com.qyft.ms.model.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.util.List;
@EqualsAndHashCode(callSuper = false)
@Schema(description = "容器")
@Data
public class BaseDataVO {
@Schema(description = "加热区")
List<SysSettingVO> heat_area;
@Schema(description = "加液区")
List<SysSettingVO> solution_area;
@Schema(description = "拍子区域")
List<SysSettingVO> lid_area;
@Schema(description = "系统配置")
List<SysSettingVO> sys_setting;
@Schema(description = "容器数据")
List<ContainerListVO> containers;
}

13
src/main/java/com/qyft/ms/model/vo/ContainerListVO.java

@ -0,0 +1,13 @@
package com.qyft.ms.model.vo;
import com.qyft.ms.model.entity.Container;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
@EqualsAndHashCode(callSuper = true)
@Schema(description = "容器")
@Data
public class ContainerListVO extends Container {
}

23
src/main/java/com/qyft/ms/model/vo/CraftsStepResult.java

@ -0,0 +1,23 @@
package com.qyft.ms.model.vo;
import lombok.Data;
/**
* 工艺步骤执行状态
*/
@Data
public class CraftsStepResult {
/**
* 加热区id
*/
private String heatId;
/**
* 0 (未执行)1 (正在执行)2 (暂停执行)3 (停止执行)4(执行错误)6(执行完成)
*/
private Integer status;
/**
* 当前工艺执行的方法数组下标
*/
private Integer methodIndex;
}

22
src/main/java/com/qyft/ms/model/vo/ExecutionResult.java

@ -0,0 +1,22 @@
package com.qyft.ms.model.vo;
import lombok.Data;
@Data
public class ExecutionResult {
/**
* 任务id
*/
private String commandId;
private String commandName;
/**
* 执行结果
*/
private String status;
/**
* 结果描述
*/
private String message;
}

30
src/main/java/com/qyft/ms/model/vo/OresCraftsListVO.java

@ -0,0 +1,30 @@
package com.qyft.ms.model.vo;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.qyft.ms.model.entity.Crafts;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.time.LocalDateTime;
import java.util.List;
@Schema(description = "矿石工艺视图")
@Data
public class OresCraftsListVO {
@Schema(description = "矿石id")
private Long id;
@Schema(description = "矿石名称")
private String oresName;
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private LocalDateTime createTime;
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private LocalDateTime updateTime;
@Schema(description = "该矿石下工艺列表")
private List<Crafts> craftsList;
}

14
src/main/java/com/qyft/ms/model/vo/SysSettingResult.java

@ -0,0 +1,14 @@
package com.qyft.ms.model.vo;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.util.List;
@EqualsAndHashCode(callSuper = true)
@Data
public class SysSettingResult extends SysSettingVO {
private List<SysSettingVO> children;
}

43
src/main/java/com/qyft/ms/model/vo/SysSettingVO.java

@ -0,0 +1,43 @@
package com.qyft.ms.model.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.util.List;
@EqualsAndHashCode(callSuper = false)
@Data
public class SysSettingVO {
@Schema(description = "主键id")
private int id;
@Schema(description = "父id")
private int parentId;
@Schema(description = "名称")
private String name;
@Schema(description = "code")
private String code;
@Schema(description = "配置值")
private String value;
@Schema(description = "设备id")
private String hardwareId;
@Schema(description = "加热区温度")
private String temperature;
@Schema(description = "工艺id")
private int craftId;
@Schema(description = "试管坐标list")
private List<String> tubePositionList;
@Schema(description = "拍子坐标")
private String LidPosition;
}

35
src/main/java/com/qyft/ms/model/vo/TaskListVO.java

@ -0,0 +1,35 @@
package com.qyft.ms.model.vo;
import com.qyft.ms.model.entity.TaskSteps;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotBlank;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.util.List;
@EqualsAndHashCode(callSuper = false)
@Data
public class TaskListVO {
@Schema(description = "实验id")
private Long id;
@Schema(description = "实验名称")
private String name;
@NotBlank
@Schema(description = "开始时间")
private String startTime;
@Schema(description = "创建人")
private Long createUser;
@Schema(description = "结束时间")
private String endTime;
@Schema(description = "状态 1 执行中 2 执行完毕")
private Integer status;
@Schema(description = "实验步骤")
private List<TaskSteps> steps;
}

18
src/main/java/com/qyft/ms/model/vo/WebsocketResult.java

@ -0,0 +1,18 @@
package com.qyft.ms.model.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
@Data
public class WebsocketResult {
/**
* 推送类型(指令 cmd,报警 warn ,状态 status)
*/
@Schema(description = "推送类型(指令 cmd,报警 warn ,状态 status)")
private String type;
/**
* 执行结果
*/
@Schema(description = "推送数据")
private Object data;
}

61
src/main/java/com/qyft/ms/service/CMDService.java

@ -0,0 +1,61 @@
package com.qyft.ms.service;
import cn.hutool.json.JSONUtil;
import com.qyft.ms.common.constant.WebSocketMessageType;
import com.qyft.ms.common.result.CMDResultCode;
import com.qyft.ms.device.model.bo.DeviceCtrlFuncCMD;
import com.qyft.ms.device.service.ICtrlFuncService;
import com.qyft.ms.model.form.CMDForm;
import com.qyft.ms.model.vo.ExecutionResult;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import java.util.List;
import java.util.function.Supplier;
@Slf4j
@RequiredArgsConstructor
@Service
public class CMDService {
private final WebSocketService webSocketService;
private final ICtrlFuncService ctrlFuncService;
CMDForm form;
private void run(List<Supplier<Boolean>> cmdList) {
ExecutionResult executionResult = new ExecutionResult();
executionResult.setCommandId(form.getCommandId());
executionResult.setCommandName(form.getCommand());
// 执行所有命令
for (Supplier<Boolean> command : cmdList) {
boolean result = command.get();
if (!result) {
log.error("指令执行异常: {}", JSONUtil.toJsonStr(form));
executionResult.setStatus(CMDResultCode.FAILURE.getCode());
executionResult.setMessage(CMDResultCode.FAILURE.getMsg());
webSocketService.pushMsg(WebSocketMessageType.CMD, executionResult);
return;
}
}
executionResult.setStatus(CMDResultCode.SUCCESS.getCode());
executionResult.setMessage(CMDResultCode.SUCCESS.getMsg());
webSocketService.pushMsg(WebSocketMessageType.CMD, executionResult);
}
public boolean executeCommand(CMDForm cmdForm) {
String commandName = cmdForm.getCommand();
List<DeviceCtrlFuncCMD> deviceCtrlFuncCMDList = ctrlFuncService.findAllStepCMD();
for (DeviceCtrlFuncCMD deviceCtrlFuncCMD : deviceCtrlFuncCMDList) {
if (deviceCtrlFuncCMD.getDeviceCmd().equals(commandName)) {
new Thread(() -> run(cmdForm)).start();
return true;
}
}
return false;
}
}

11
src/main/java/com/qyft/ms/service/ILogsService.java

@ -0,0 +1,11 @@
package com.qyft.ms.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.qyft.ms.model.entity.Logs;
/**
* 日志业务接口
*/
public interface ILogsService extends IService<Logs> {
int insertLog(String log);
}

24
src/main/java/com/qyft/ms/service/ISysSettingsService.java

@ -0,0 +1,24 @@
package com.qyft.ms.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.qyft.ms.model.dto.SysSettingsDTO;
import com.qyft.ms.model.entity.SysSettings;
import com.qyft.ms.model.vo.SysSettingResult;
import com.qyft.ms.model.vo.SysSettingVO;
import java.util.List;
/**
* 系统配置接口层
*/
public interface ISysSettingsService extends IService<SysSettings> {
List<SysSettingVO> getConfig(String type);
List<SysSettingResult> getAllConfig();
Boolean updateConfig(List<SysSettingsDTO> dto);
boolean setSysDate(String newTime);
}

38
src/main/java/com/qyft/ms/service/StatusService.java

@ -0,0 +1,38 @@
package com.qyft.ms.service;
import com.qyft.ms.common.constant.WebSocketMessageType;
import com.qyft.ms.device.model.bo.DeviceStatus;
import com.qyft.ms.device.service.DeviceStatusService;
import jakarta.annotation.PostConstruct;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
@Slf4j
@Service
@RequiredArgsConstructor
public class StatusService {
private final WebSocketService webSocketService;
private final DeviceStatusService deviceStatusService;
// @PostConstruct
public void init() {
ScheduledExecutorService executorService = Executors.newSingleThreadScheduledExecutor();
executorService.scheduleAtFixedRate(() -> {
try {
DeviceStatus deviceStatus = deviceStatusService.getDeviceStatus();
webSocketService.pushMsg(WebSocketMessageType.STATUS, deviceStatus);
} catch (Exception e) {
if (!executorService.isShutdown()) {
executorService.shutdown(); // 关闭 ScheduledExecutorService
}
log.error("定时推送设备状态异常", e);
}
}, 10, 100, TimeUnit.MILLISECONDS);
}
}

32
src/main/java/com/qyft/ms/service/WebSocketService.java

@ -0,0 +1,32 @@
package com.qyft.ms.service;
import cn.hutool.json.JSONUtil;
import com.qyft.ms.config.WebSocketServer;
import com.qyft.ms.model.vo.WebsocketResult;
import jakarta.annotation.PostConstruct;
import org.springframework.stereotype.Service;
@Service
public class WebSocketService {
public void pushMsg(String type, Object result) {
WebsocketResult websocketResult = new WebsocketResult();
websocketResult.setType(type);
websocketResult.setData(result);
// 发送消息给客户端
WebSocketServer.sendMessageToClients(JSONUtil.toJsonStr(websocketResult));
}
@PostConstruct
public void init() {
// 设置 WebSocketService 实例到 WebSocketServer
WebSocketServer.setWebSocketService(this);
}
// 新增方法处理接收到的消息
public void handleReceivedMessage(String message) {
// 这里可以添加处理接收到消息的逻辑
System.out.println("Received message: " + message);
// 根据需要解析消息并进行处理
}
}

29
src/main/java/com/qyft/ms/service/impl/ILogsServiceImpl.java

@ -0,0 +1,29 @@
package com.qyft.ms.service.impl;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.qyft.ms.mapper.LogsMapper;
import com.qyft.ms.model.entity.Logs;
import com.qyft.ms.service.ILogsService;
import com.qyft.ms.system.model.entity.User;
import com.qyft.ms.system.service.IUserService;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
/**
* 实验业务实现类
*/
@Service
@RequiredArgsConstructor
public class ILogsServiceImpl extends ServiceImpl<LogsMapper, Logs> implements ILogsService {
private final LogsMapper logsMapper;
private final IUserService userService;
@Override
public int insertLog(String log) {
Logs logs = new Logs();
logs.setText(log);
User user = userService.currentUser();
logs.setCreateUser(user.getId());
return logsMapper.insert(logs);
}
}

75
src/main/java/com/qyft/ms/service/impl/ISysSettingsServiceImpl.java

@ -0,0 +1,75 @@
package com.qyft.ms.service.impl;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.qyft.ms.mapper.SysSettingsMapper;
import com.qyft.ms.model.dto.SysSettingsDTO;
import com.qyft.ms.model.entity.SysSettings;
import com.qyft.ms.model.vo.SysSettingResult;
import com.qyft.ms.model.vo.SysSettingVO;
import com.qyft.ms.service.ISysSettingsService;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.List;
/**
* 系统配置实现类
*/
@Service
@RequiredArgsConstructor
public class ISysSettingsServiceImpl extends ServiceImpl<SysSettingsMapper, SysSettings> implements ISysSettingsService {
private final SysSettingsMapper sysSettingsMapper;
@Override
public List<SysSettingVO> getConfig(String type) {
int parentId = sysSettingsMapper.getIdByCode(type);
return sysSettingsMapper.getConfig(String.valueOf(parentId));
}
@Override
public List<SysSettingResult> getAllConfig() {
List<SysSettingVO> list = sysSettingsMapper.getConfig("-1");
List<SysSettingResult> result = new ArrayList<>();
for (SysSettingVO sysSettings : list) {
List<SysSettingVO> childrenList = sysSettingsMapper.getConfig(String.valueOf(sysSettings.getId()));
SysSettingResult map = new SysSettingResult();
map.setName(sysSettings.getName());
map.setCode(sysSettings.getCode());
map.setId(sysSettings.getId());
map.setValue(sysSettings.getValue());
map.setHardwareId(sysSettings.getHardwareId());
map.setChildren(childrenList);
result.add(map);
}
return result;
}
@Override
public Boolean updateConfig(List<SysSettingsDTO> dto) {
for (SysSettingsDTO sysSettingsDTO : dto) {
sysSettingsMapper.updateById(sysSettingsDTO);
}
return true;
}
/**
* 修改服务器系统时间
*
* @param newTime 时间字符串 YYYY-MM-DD HH:mm:ss
* @return 是否修改成功
*/
@Override
public boolean setSysDate(String newTime) {
try {
String command = "date -s \"" + newTime + "\"";
Process process = Runtime.getRuntime().exec(command);
int exitCode = process.waitFor();
return exitCode == 0;
} catch (Exception e) {
log.error("修改系统时间失败", e);
return false;
}
}
}

44
src/main/java/com/qyft/ms/system/common/base/BaseEntity.java

@ -0,0 +1,44 @@
package com.qyft.ms.system.common.base;
import com.baomidou.mybatisplus.annotation.FieldFill;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.Data;
import java.io.Serial;
import java.io.Serializable;
import java.time.LocalDateTime;
/**
* 基础实体类
* 实体类的基类包含了实体类的公共属性如创建时间更新时间逻辑删除标识等</p>
*/
@Data
public class BaseEntity implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
/**
* 主键ID
*/
@TableId(type = IdType.AUTO)
private Long id;
/**
* 创建时间
*/
@TableField(fill = FieldFill.INSERT)
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private LocalDateTime createTime;
/**
* 更新时间
*/
@TableField(fill = FieldFill.INSERT_UPDATE)
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private LocalDateTime updateTime;
}

26
src/main/java/com/qyft/ms/system/common/base/BasePageQuery.java

@ -0,0 +1,26 @@
package com.qyft.ms.system.common.base;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.io.Serial;
import java.io.Serializable;
/**
* 基础分页请求对象
*/
@Data
@Schema
public class BasePageQuery implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
@Schema(description = "页码", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
private int pageNum = 1;
@Schema(description = "每页记录数", requiredMode = Schema.RequiredMode.REQUIRED, example = "10")
private int pageSize = 10;
}

84
src/main/java/com/qyft/ms/system/common/base/IBaseEnum.java

@ -0,0 +1,84 @@
package com.qyft.ms.system.common.base;
import cn.hutool.core.util.ObjectUtil;
import java.util.EnumSet;
import java.util.Objects;
/**
* 枚举通用接口
*/
public interface IBaseEnum<T> {
/**
* 根据值获取枚举
*
* @param value
* @param clazz
* @param <E> 枚举
* @return
*/
static <E extends Enum<E> & IBaseEnum> E getEnumByValue(Object value, Class<E> clazz) {
Objects.requireNonNull(value);
EnumSet<E> allEnums = EnumSet.allOf(clazz); // 获取类型下的所有枚举
E matchEnum = allEnums.stream()
.filter(e -> ObjectUtil.equal(e.getValue(), value))
.findFirst()
.orElse(null);
return matchEnum;
}
/**
* 根据文本标签获取值
*
* @param value
* @param clazz
* @param <E>
* @return
*/
static <E extends Enum<E> & IBaseEnum> String getLabelByValue(Object value, Class<E> clazz) {
Objects.requireNonNull(value);
EnumSet<E> allEnums = EnumSet.allOf(clazz); // 获取类型下的所有枚举
E matchEnum = allEnums.stream()
.filter(e -> ObjectUtil.equal(e.getValue(), value))
.findFirst()
.orElse(null);
String label = null;
if (matchEnum != null) {
label = matchEnum.getLabel();
}
return label;
}
/**
* 根据文本标签获取值
*
* @param label
* @param clazz
* @param <E>
* @return
*/
static <E extends Enum<E> & IBaseEnum> Object getValueByLabel(String label, Class<E> clazz) {
Objects.requireNonNull(label);
EnumSet<E> allEnums = EnumSet.allOf(clazz); // 获取类型下的所有枚举
String finalLabel = label;
E matchEnum = allEnums.stream()
.filter(e -> ObjectUtil.equal(e.getLabel(), finalLabel))
.findFirst()
.orElse(null);
Object value = null;
if (matchEnum != null) {
value = matchEnum.getValue();
}
return value;
}
T getValue();
String getLabel();
}

Some files were not shown because too many files changed in this diff

Loading…
Cancel
Save