Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Contribute to GitLab
Sign in
Toggle navigation
Z
zhmes-agecal
Project
Project
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
耿迪迪
zhmes-agecal
Commits
2745cceb
Commit
2745cceb
authored
Jul 31, 2025
by
wanghao
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
1 netty 接收 机械臂 回复状态功能实现,及 传送带 状态测试。
parent
533cce1e
Show whitespace changes
Inline
Side-by-side
Showing
11 changed files
with
614 additions
and
51 deletions
+614
-51
application-dev.yml
zhmes-agecal-admin/src/main/resources/application-dev.yml
+12
-4
application-prd.yml
zhmes-agecal-admin/src/main/resources/application-prd.yml
+9
-1
application-test.yml
zhmes-agecal-admin/src/main/resources/application-test.yml
+9
-2
application.yml
zhmes-agecal-admin/src/main/resources/application.yml
+1
-1
pom.xml
zhmes-agecal-common/pom.xml
+5
-0
pom.xml
zhmes-agecal-framework/pom.xml
+5
-1
NettyConfig.java
...rc/main/java/com/zehong/framework/config/NettyConfig.java
+80
-0
NettyUdpServer.java
.../main/java/com/zehong/framework/netty/NettyUdpServer.java
+143
-0
NettyUdpServerHandler.java
...zehong/framework/netty/handler/NettyUdpServerHandler.java
+172
-0
NettyServerListener.java
.../zehong/framework/netty/listener/NettyServerListener.java
+47
-0
Modbus4jUtils.java
...ain/java/com/zehong/system/modbus/util/Modbus4jUtils.java
+131
-42
No files found.
zhmes-agecal-admin/src/main/resources/application-dev.yml
View file @
2745cceb
...
...
@@ -6,9 +6,9 @@ spring:
druid
:
# 主库数据源
master
:
url
:
jdbc:mysql://localhost:3306/
ry?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
url
:
jdbc:mysql://localhost:3306/
zh-mes-device-db?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&allowMultiQueries=true
username
:
root
password
:
root
password
:
Zh@123456
# 从库数据源
slave
:
# 从数据源开关/默认关闭
...
...
@@ -90,8 +90,16 @@ zehong:
# 实例演示开关
demoEnabled
:
true
# 文件路径 示例( Windows配置D:/zehong/uploadPath,Linux配置 /home/zehong/uploadPath)
profile
:
/home
/zehong/uploadPath
profile
:
D:
/zehong/uploadPath
# 获取ip地址开关
addressEnabled
:
false
# 验证码类型 math 数组计算 char 字符验证
captchaType
:
math
# Netty配置
netty
:
port
:
6001
# 服务端口
boss-group-thread-count
:
1
# 主线程数
worker-group-thread-count
:
8
# 工作线程数
max-frame-length
:
65535
# 最大帧长度
heartbeat-timeout
:
60
# 心跳超时时间(秒)
\ No newline at end of file
zhmes-agecal-admin/src/main/resources/application-prd.yml
View file @
2745cceb
...
...
@@ -95,3 +95,11 @@ zehong:
addressEnabled
:
false
# 验证码类型 math 数组计算 char 字符验证
captchaType
:
math
# Netty配置
netty
:
port
:
6001
# 服务端口
boss-group-thread-count
:
1
# 主线程数
worker-group-thread-count
:
8
# 工作线程数
max-frame-length
:
65535
# 最大帧长度
heartbeat-timeout
:
60
# 心跳超时时间(秒)
\ No newline at end of file
zhmes-agecal-admin/src/main/resources/application-test.yml
View file @
2745cceb
...
...
@@ -88,8 +88,15 @@ zehong:
# 实例演示开关
demoEnabled
:
true
# 文件路径 示例( Windows配置D:/zehong/uploadPath,Linux配置 /home/zehong/uploadPath)
profile
:
D:
/zehong/uploadPath
profile
:
/home
/zehong/uploadPath
# 获取ip地址开关
addressEnabled
:
false
# 验证码类型 math 数组计算 char 字符验证
captchaType
:
math
# Netty配置
netty
:
port
:
6001
# 服务端口
boss-group-thread-count
:
1
# 主线程数
worker-group-thread-count
:
8
# 工作线程数
max-frame-length
:
65535
# 最大帧长度
heartbeat-timeout
:
60
# 心跳超时时间(秒)
\ No newline at end of file
zhmes-agecal-admin/src/main/resources/application.yml
View file @
2745cceb
...
...
@@ -26,7 +26,7 @@ spring:
# 国际化资源文件路径
basename
:
i18n/messages
profiles
:
active
:
test
active
:
dev
# 文件上传
servlet
:
multipart
:
...
...
zhmes-agecal-common/pom.xml
View file @
2745cceb
...
...
@@ -125,6 +125,11 @@
<version>
3.0.3
</version>
</dependency>
<dependency>
<groupId>
io.netty
</groupId>
<artifactId>
netty-all
</artifactId>
<version>
4.1.86.Final
</version>
</dependency>
</dependencies>
</project>
\ No newline at end of file
zhmes-agecal-framework/pom.xml
View file @
2745cceb
...
...
@@ -16,7 +16,11 @@
</description>
<dependencies>
<dependency>
<groupId>
org.springframework.boot
</groupId>
<artifactId>
spring-boot-configuration-processor
</artifactId>
<optional>
true
</optional>
</dependency>
<!-- SpringBoot Web容器 -->
<dependency>
<groupId>
org.springframework.boot
</groupId>
...
...
zhmes-agecal-framework/src/main/java/com/zehong/framework/config/NettyConfig.java
0 → 100644
View file @
2745cceb
package
com
.
zehong
.
framework
.
config
;
import
org.springframework.boot.context.properties.ConfigurationProperties
;
import
org.springframework.stereotype.Component
;
/**
* @author lenovo
* @date 2025/7/31
* @description Netty配置类
*/
@Component
@ConfigurationProperties
(
prefix
=
"netty"
)
public
class
NettyConfig
{
/**
* 服务端口
*/
private
int
port
=
6001
;
/**
* 主线程数
*/
private
int
bossGroupThreadCount
=
1
;
/**
* 工作线程数
*/
private
int
workerGroupThreadCount
=
Runtime
.
getRuntime
().
availableProcessors
()
*
2
;
/**
* 最大帧长度
*/
private
int
maxFrameLength
=
65535
;
/**
* 心跳检测超时时间(秒)
*/
private
int
heartbeatTimeout
=
60
;
// getter和setter方法
public
int
getPort
()
{
return
port
;
}
public
void
setPort
(
int
port
)
{
this
.
port
=
port
;
}
public
int
getBossGroupThreadCount
()
{
return
bossGroupThreadCount
;
}
public
void
setBossGroupThreadCount
(
int
bossGroupThreadCount
)
{
this
.
bossGroupThreadCount
=
bossGroupThreadCount
;
}
public
int
getWorkerGroupThreadCount
()
{
return
workerGroupThreadCount
;
}
public
void
setWorkerGroupThreadCount
(
int
workerGroupThreadCount
)
{
this
.
workerGroupThreadCount
=
workerGroupThreadCount
;
}
public
int
getMaxFrameLength
()
{
return
maxFrameLength
;
}
public
void
setMaxFrameLength
(
int
maxFrameLength
)
{
this
.
maxFrameLength
=
maxFrameLength
;
}
public
int
getHeartbeatTimeout
()
{
return
heartbeatTimeout
;
}
public
void
setHeartbeatTimeout
(
int
heartbeatTimeout
)
{
this
.
heartbeatTimeout
=
heartbeatTimeout
;
}
}
zhmes-agecal-framework/src/main/java/com/zehong/framework/netty/NettyUdpServer.java
0 → 100644
View file @
2745cceb
package
com
.
zehong
.
framework
.
netty
;
import
com.zehong.framework.config.NettyConfig
;
import
com.zehong.framework.netty.handler.NettyUdpServerHandler
;
import
org.springframework.stereotype.Component
;
import
io.netty.bootstrap.Bootstrap
;
import
io.netty.channel.ChannelFuture
;
import
io.netty.channel.ChannelInitializer
;
import
io.netty.channel.ChannelOption
;
import
io.netty.channel.EventLoopGroup
;
import
io.netty.channel.nio.NioEventLoopGroup
;
import
io.netty.channel.socket.nio.NioDatagramChannel
;
import
io.netty.handler.codec.LengthFieldBasedFrameDecoder
;
import
io.netty.handler.codec.LengthFieldPrepender
;
import
io.netty.handler.codec.string.StringDecoder
;
import
io.netty.handler.codec.string.StringEncoder
;
import
io.netty.handler.timeout.IdleStateHandler
;
import
io.netty.util.CharsetUtil
;
import
org.slf4j.Logger
;
import
org.slf4j.LoggerFactory
;
import
org.springframework.beans.factory.annotation.Autowired
;
import
javax.annotation.PreDestroy
;
import
java.util.concurrent.TimeUnit
;
/**
* @author lenovo
* @date 2025/7/31
* @description TODO
*/
@Component
public
class
NettyUdpServer
{
private
static
final
Logger
log
=
LoggerFactory
.
getLogger
(
NettyUdpServer
.
class
);
@Autowired
private
NettyConfig
nettyConfig
;
@Autowired
private
NettyUdpServerHandler
nettyUdpServerHandler
;
private
EventLoopGroup
group
;
private
ChannelFuture
channelFuture
;
/**
* 启动UDP服务器
*/
public
void
start
()
{
// 避免重复启动
if
(
channelFuture
!=
null
&&
channelFuture
.
channel
().
isActive
())
{
log
.
warn
(
"Netty UDP服务器已启动,无需重复启动"
);
return
;
}
try
{
// UDP只需要一个事件循环组
group
=
new
NioEventLoopGroup
(
nettyConfig
.
getWorkerGroupThreadCount
());
// UDP使用Bootstrap而非ServerBootstrap
Bootstrap
bootstrap
=
new
Bootstrap
();
bootstrap
.
group
(
group
)
// 设置UDP通道类型
.
channel
(
NioDatagramChannel
.
class
)
// UDP支持广播
.
option
(
ChannelOption
.
SO_BROADCAST
,
true
)
// 设置接收缓冲区大小
.
option
(
ChannelOption
.
SO_RCVBUF
,
1024
*
1024
)
// 设置发送缓冲区大小
.
option
(
ChannelOption
.
SO_SNDBUF
,
1024
*
1024
)
// 设置处理器
.
handler
(
new
ChannelInitializer
<
NioDatagramChannel
>()
{
@Override
protected
void
initChannel
(
NioDatagramChannel
ch
)
throws
Exception
{
ch
.
pipeline
()
// UDP也可以使用心跳检测,但意义不如TCP大
.
addLast
(
new
IdleStateHandler
(
0
,
0
,
nettyConfig
.
getHeartbeatTimeout
(),
TimeUnit
.
SECONDS
))
// 帧解码,解决粘包拆包问题
.
addLast
(
new
LengthFieldBasedFrameDecoder
(
nettyConfig
.
getMaxFrameLength
(),
0
,
4
,
0
,
4
))
// 帧编码
.
addLast
(
new
LengthFieldPrepender
(
4
))
// 字符串解码
.
addLast
(
new
StringDecoder
(
CharsetUtil
.
UTF_8
))
// 字符串编码
.
addLast
(
new
StringEncoder
(
CharsetUtil
.
UTF_8
))
// 自定义UDP处理器
.
addLast
(
nettyUdpServerHandler
);
}
});
// 绑定端口,UDP不需要监听连接
channelFuture
=
bootstrap
.
bind
(
nettyConfig
.
getPort
()).
sync
();
log
.
info
(
"Netty UDP服务器启动成功,监听端口: {}"
,
nettyConfig
.
getPort
());
// 等待通道关闭
channelFuture
.
channel
().
closeFuture
().
addListener
(
future
->
{
log
.
info
(
"Netty UDP服务器通道关闭"
);
stop
();
});
}
catch
(
Exception
e
)
{
log
.
error
(
"Netty UDP服务器启动失败"
,
e
);
stop
();
}
}
/**
* 停止UDP服务器
*/
@PreDestroy
public
void
stop
()
{
try
{
if
(
channelFuture
!=
null
)
{
channelFuture
.
channel
().
close
().
sync
();
}
}
catch
(
Exception
e
)
{
log
.
error
(
"Netty UDP通道关闭异常"
,
e
);
}
finally
{
if
(
group
!=
null
)
{
group
.
shutdownGracefully
();
}
log
.
info
(
"Netty UDP服务器已停止"
);
}
}
/**
* 重启UDP服务器
*/
public
void
restart
()
{
log
.
info
(
"准备重启Netty UDP服务器..."
);
stop
();
try
{
Thread
.
sleep
(
1000
);
}
catch
(
InterruptedException
e
)
{
Thread
.
currentThread
().
interrupt
();
}
start
();
}
}
zhmes-agecal-framework/src/main/java/com/zehong/framework/netty/handler/NettyUdpServerHandler.java
0 → 100644
View file @
2745cceb
package
com
.
zehong
.
framework
.
netty
.
handler
;
import
io.netty.buffer.ByteBuf
;
import
io.netty.channel.ChannelHandler.Sharable
;
import
io.netty.channel.ChannelHandlerContext
;
import
io.netty.channel.SimpleChannelInboundHandler
;
import
io.netty.channel.socket.DatagramPacket
;
import
io.netty.handler.timeout.IdleState
;
import
io.netty.handler.timeout.IdleStateEvent
;
import
org.slf4j.Logger
;
import
org.slf4j.LoggerFactory
;
import
org.springframework.stereotype.Component
;
import
java.nio.charset.StandardCharsets
;
import
java.text.SimpleDateFormat
;
import
java.util.concurrent.locks.ReentrantLock
;
import
java.io.*
;
import
java.util.Date
;
/**
* @author lenovo
* @date 2025/7/31
* @description TODO
*/
@Component
@Sharable
public
class
NettyUdpServerHandler
extends
SimpleChannelInboundHandler
<
DatagramPacket
>
{
private
static
final
Logger
log
=
LoggerFactory
.
getLogger
(
NettyUdpServerHandler
.
class
);
// 日志文件保存目录
// 写法1:使用双反斜杠
private
static
final
String
LOG_DIR
=
"D:\\BaiduNetdiskDownload\\udp_message_logs\\"
;
// 日期格式器,用于生成文件名和日志时间
private
static
final
SimpleDateFormat
DATE_FORMATTER
=
new
SimpleDateFormat
(
"yyyyMMdd"
);
private
static
final
SimpleDateFormat
TIME_FORMATTER
=
new
SimpleDateFormat
(
"yyyy-MM-dd HH:mm:ss"
);
// 线程安全锁,确保文件写入安全
private
final
ReentrantLock
fileLock
=
new
ReentrantLock
();
/**
* 接收UDP消息
*/
@Override
protected
void
channelRead0
(
ChannelHandlerContext
ctx
,
DatagramPacket
packet
)
throws
Exception
{
try
{
// 获取消息内容的字节缓冲区
ByteBuf
content
=
packet
.
content
();
// 分配与消息内容长度相同的字节数组
byte
[]
bytes
=
new
byte
[
content
.
readableBytes
()];
// 将缓冲区内容复制到字节数组
content
.
readBytes
(
bytes
);
// 尝试多种编码方式解码,用于排查问题
String
messageGbk
=
new
String
(
bytes
,
"GBK"
);
// 日志打印多种编码结果,帮助确定发送端使用的编码
log
.
info
(
"收到来自{}的UDP消息:"
,
packet
.
sender
());
log
.
info
(
"GBK解码: {}"
,
messageGbk
);
// 根据实际情况选择正确的编码方式
// 这里假设通过日志观察后确定发送端使用GBK编码
String
correctMessage
=
messageGbk
;
// 初始值,根据实际情况修改
// 检测哪种编码更可能是正确的(简单判断)
if
(
containsValidChinese
(
messageGbk
))
{
correctMessage
=
messageGbk
;
}
// 保存消息到文件
saveMessageToFile
(
packet
.
sender
().
toString
(),
correctMessage
);
// 处理消息逻辑
String
response
=
"服务器已收到UDP消息:"
+
correctMessage
;
// 回复客户端,明确使用UTF-8编码
byte
[]
responseBytes
=
response
.
getBytes
(
StandardCharsets
.
UTF_8
);
ctx
.
writeAndFlush
(
new
DatagramPacket
(
io
.
netty
.
buffer
.
Unpooled
.
copiedBuffer
(
responseBytes
),
packet
.
sender
()));
}
catch
(
Exception
e
)
{
log
.
error
(
"处理UDP消息异常"
,
e
);
}
}
/**
* 将消息保存到本地文件
*
* @param clientAddress 客户端地址
* @param message 消息内容
*/
private
void
saveMessageToFile
(
String
clientAddress
,
String
message
)
{
// 创建日志目录(如果不存在)
File
dir
=
new
File
(
LOG_DIR
);
if
(!
dir
.
exists
())
{
dir
.
mkdirs
();
}
// 按日期生成文件名,每天一个文件
String
fileName
=
LOG_DIR
+
"udp_log_"
+
DATE_FORMATTER
.
format
(
new
Date
())
+
".log"
;
// 构建日志内容
String
logContent
=
String
.
format
(
"[%s] 客户端[%s]:%s%n"
,
TIME_FORMATTER
.
format
(
new
Date
()),
clientAddress
,
message
);
// 使用锁确保多线程写入安全
fileLock
.
lock
();
BufferedWriter
writer
=
null
;
try
{
// 以追加模式写入文件
writer
=
new
BufferedWriter
(
new
OutputStreamWriter
(
new
FileOutputStream
(
fileName
,
true
),
StandardCharsets
.
UTF_8
));
writer
.
write
(
logContent
);
writer
.
flush
();
}
catch
(
IOException
e
)
{
log
.
error
(
"保存UDP消息到文件失败"
,
e
);
}
finally
{
if
(
writer
!=
null
)
{
try
{
writer
.
close
();
}
catch
(
IOException
e
)
{
log
.
error
(
"关闭文件写入流失败"
,
e
);
}
}
fileLock
.
unlock
();
}
}
/**
* 简单判断字符串是否包含有效的中文字符
*/
private
boolean
containsValidChinese
(
String
str
)
{
if
(
str
==
null
||
str
.
isEmpty
())
{
return
false
;
}
for
(
char
c
:
str
.
toCharArray
())
{
// 中文字符的Unicode范围
if
(
c
>=
0x4E00
&&
c
<=
0x9FA5
)
{
return
true
;
}
}
return
false
;
}
/**
* 异常处理
*/
@Override
public
void
exceptionCaught
(
ChannelHandlerContext
ctx
,
Throwable
cause
)
throws
Exception
{
log
.
error
(
"UDP通道异常:{}"
,
cause
.
getMessage
(),
cause
);
// UDP无连接,通常不需要关闭通道
}
/**
* 心跳检测
*/
@Override
public
void
userEventTriggered
(
ChannelHandlerContext
ctx
,
Object
evt
)
throws
Exception
{
if
(
evt
instanceof
IdleStateEvent
)
{
IdleStateEvent
event
=
(
IdleStateEvent
)
evt
;
if
(
event
.
state
()
==
IdleState
.
ALL_IDLE
)
{
log
.
info
(
"UDP服务器超过规定时间未收到数据"
);
// UDP无连接,一般不关闭通道
}
}
else
{
super
.
userEventTriggered
(
ctx
,
evt
);
}
}
}
zhmes-agecal-framework/src/main/java/com/zehong/framework/netty/listener/NettyServerListener.java
0 → 100644
View file @
2745cceb
package
com
.
zehong
.
framework
.
netty
.
listener
;
import
com.zehong.framework.netty.NettyUdpServer
;
import
org.springframework.core.annotation.Order
;
import
org.springframework.stereotype.Component
;
import
org.slf4j.Logger
;
import
org.slf4j.LoggerFactory
;
import
org.springframework.beans.factory.annotation.Autowired
;
import
org.springframework.boot.ApplicationArguments
;
import
org.springframework.boot.ApplicationRunner
;
/**
* @author lenovo
* @date 2025/7/31
* @description TODO
*/
@Component
@Order
(
1
)
// 控制启动顺序,数值越小越先执行
public
class
NettyServerListener
implements
ApplicationRunner
{
private
static
final
Logger
log
=
LoggerFactory
.
getLogger
(
NettyServerListener
.
class
);
@Autowired
private
NettyUdpServer
nettyServer
;
/**
* 应用启动后执行,启动Netty服务器
*/
@Override
public
void
run
(
ApplicationArguments
args
)
throws
Exception
{
try
{
log
.
info
(
"准备启动Netty服务器..."
);
// 启动Netty服务
nettyServer
.
start
();
// 注册钩子,在JVM关闭时优雅停止Netty服务
Runtime
.
getRuntime
().
addShutdownHook
(
new
Thread
(()
->
{
log
.
info
(
"JVM即将关闭,准备停止Netty服务器..."
);
nettyServer
.
stop
();
}));
}
catch
(
Exception
e
)
{
log
.
error
(
"启动Netty服务器失败"
,
e
);
// 启动失败时可以选择退出应用或进行其他处理
// System.exit(1);
}
}
}
zhmes-agecal-system/src/main/java/com/zehong/system/modbus/util/Modbus4jUtils.java
View file @
2745cceb
...
...
@@ -56,14 +56,14 @@ public class Modbus4jUtils {
*/
public
static
ModbusMaster
getMaster
()
throws
ModbusInitException
{
IpParameters
params
=
new
IpParameters
();
params
.
setHost
(
"192.168.
1.10
1"
);
params
.
setPort
(
50
1
);
params
.
setHost
(
"192.168.
2.1
1"
);
params
.
setPort
(
50
2
);
//
// modbusFactory.createRtuMaster(wapper); //RTU 协议
// modbusFactory.createUdpMaster(params);//UDP 协议
// modbusFactory.createAsciiMaster(wrapper);//ASCII 协议
if
(
master
==
null
||
!
master
.
isConnected
())
{
master
=
modbusFactory
.
createTcpMaster
(
params
,
fals
e
);
// TCP 协议
master
=
modbusFactory
.
createTcpMaster
(
params
,
tru
e
);
// TCP 协议
master
.
setTimeout
(
5000
);
// 设置超时时间
master
.
setRetries
(
3
);
// 设置重试次数
master
.
init
();
...
...
@@ -85,7 +85,7 @@ public class Modbus4jUtils {
params
.
setPort
(
port
);
ModbusMaster
master
=
modbusFactory
.
createTcpMaster
(
params
,
false
);
// TCP 协议
master
.
setTimeout
(
3000
);
// 设置超时时间
master
.
setRetries
(
1
);
// 设置重试次数
master
.
setRetries
(
3
);
// 设置重试次数
master
.
init
();
return
master
;
}
...
...
@@ -149,6 +149,44 @@ public class Modbus4jUtils {
return
value
;
}
/**
* 读取[02 Input Status 1x]类型 开关数据
*
* @param slaveId
* @param offset
* @return
* @throws ModbusTransportException
* @throws ErrorResponseException
* @throws ModbusInitException
*/
public
static
Boolean
readInputStatus
(
ModbusMaster
master
,
int
slaveId
,
int
offset
)
throws
ModbusTransportException
,
ErrorResponseException
,
ModbusInitException
{
// 02 Input Status
BaseLocator
<
Boolean
>
loc
=
BaseLocator
.
inputStatus
(
slaveId
,
offset
);
Boolean
value
=
master
.
getValue
(
loc
);
return
value
;
}
/**
* 读取离散输入(功能码02)的兼容方法
* 使用ReadDiscreteInputsRequest/Response,适配更多版本
*/
private
static
boolean
[]
readDiscreteInputs
(
ModbusMaster
master
,
int
slaveId
,
int
startAddress
,
int
count
)
throws
ModbusTransportException
{
// 创建功能码02对应的请求(ReadDiscreteInputsRequest)
ModbusRequest
request
=
new
ReadDiscreteInputsRequest
(
slaveId
,
startAddress
,
count
);
// 发送请求并获取响应
ModbusResponse
response
=
master
.
send
(
request
);
// 转换为具体的响应类型
if
(
response
instanceof
ReadDiscreteInputsResponse
)
{
ReadDiscreteInputsResponse
discreteResponse
=
(
ReadDiscreteInputsResponse
)
response
;
return
discreteResponse
.
getBooleanData
();
}
else
{
throw
new
ModbusTransportException
(
"无效的响应类型: "
+
response
.
getClass
().
getSimpleName
());
}
}
/**
* 读取[03 Holding Register类型 2x]模拟量数据
*
...
...
@@ -671,72 +709,123 @@ public class Modbus4jUtils {
* @param args a
*/
public
static
void
main
(
String
[]
args
)
{
ModbusMaster
modbusMaster
=
null
;
try
{
/* 5.写入时间,年、月、日、时、分 */
// 示例:000100000006 设备地址01写时间06 寄存器地址0004(年04月05日06时07分08) 年/月/日/时/分数值07E9(2025)
Calendar
cal
=
Calendar
.
getInstance
();
// 当前年
int
y
=
cal
.
get
(
Calendar
.
YEAR
);
// 当前月
int
m
=
cal
.
get
(
Calendar
.
MONTH
)
+
1
;
// 当前日
int
d
=
cal
.
get
(
Calendar
.
DATE
);
// 当前小时
int
h
=
cal
.
get
(
Calendar
.
HOUR_OF_DAY
);
// 当前分钟
int
mm
=
cal
.
get
(
Calendar
.
MINUTE
);
boolean
yearResult
=
writeRegister
(
1
,
4
,
(
short
)
y
);
System
.
out
.
println
(
yearResult
?
"写入成功"
:
"写入失败"
);
boolean
mResult
=
writeRegister
(
1
,
5
,
(
short
)
m
);
System
.
out
.
println
(
mResult
?
"写入成功"
:
"写入失败"
);
boolean
dResult
=
writeRegister
(
1
,
6
,
(
short
)
d
);
System
.
out
.
println
(
dResult
?
"写入成功"
:
"写入失败"
);
boolean
hResult
=
writeRegister
(
1
,
7
,
(
short
)
h
);
System
.
out
.
println
(
hResult
?
"写入成功"
:
"写入失败"
);
boolean
mmResult
=
writeRegister
(
1
,
8
,
(
short
)
mm
);
System
.
out
.
println
(
mmResult
?
"写入成功"
:
"写入失败"
);
int
[]
ints
=
readDeviceRegisters
(
1
);
for
(
int
i
=
0
;
i
<
ints
.
length
;
i
++)
{
System
.
out
.
println
(
ints
[
i
]);
}
//
Calendar cal = Calendar.getInstance();
//
// 当前年
//
int y = cal.get(Calendar.YEAR);
//
// 当前月
//
int m = cal.get(Calendar.MONTH) + 1;
//
// 当前日
//
int d = cal.get(Calendar.DATE);
//
// 当前小时
//
int h = cal.get(Calendar.HOUR_OF_DAY);
//
// 当前分钟
//
int mm = cal.get(Calendar.MINUTE);
//
//
boolean yearResult = writeRegister(1, 4, (short) y);
//
System.out.println(yearResult ? "写入成功" : "写入失败");
//
boolean mResult = writeRegister(1, 5, (short) m);
//
System.out.println(mResult ? "写入成功" : "写入失败");
//
boolean dResult = writeRegister(1, 6, (short) d);
//
System.out.println(dResult ? "写入成功" : "写入失败");
//
boolean hResult = writeRegister(1, 7, (short) h);
//
System.out.println(hResult ? "写入成功" : "写入失败");
//
boolean mmResult = writeRegister(1, 8, (short) mm);
//
System.out.println(mmResult ? "写入成功" : "写入失败");
//
//
int[] ints = readDeviceRegisters(1);
//
for (int i = 0; i < ints.length; i++) {
//
System.out.println(ints[i]);
//
}
//// 01测试
// Boolean v011 = readCoilStatus(1, 0);
// Boolean v012 = readCoilStatus(1, 1);
// Boolean v013 = readCoilStatus(1, 6);
// System.out.println("v011:" + v011);
// modbusMaster = getMaster("192.168.2.12", 502);
// 这种方式 优势
// Boolean aBoolean1 = Modbus4jUtils.readInputStatus(modbusMaster,1, 0);
// Boolean aBoolean2 = Modbus4jUtils.readInputStatus(modbusMaster,1, 1);
// System.out.println("aBoolean1 = " + aBoolean1);
// System.out.println("aBoolean2 = " + aBoolean2);
// System.out.println("v012:" + v012);
// System.out.println("v013:" + v013);
// // 02测试
// Boolean v021 = readInputStatus(1, 0);
// Boolean v022 = readInputStatus(1, 1);
// Boolean v023 = readInputStatus(1, 2);
// System.out.println("v021:" + v021);
// System.out.println("v022:" + v022);
// int[] result = readDeviceRegisters(getMaster(),1);
// master.destroy();
// System.out.println("v023:" + v023);
//
//// 03测试
// Number v031 = readHoldingRegister(1,
1
, DataType.FOUR_BYTE_FLOAT);// 注意,float
// Number v032 = readHoldingRegister(1,
3
, DataType.FOUR_BYTE_FLOAT);// 同上
// Number v031 = readHoldingRegister(1,
0
, DataType.FOUR_BYTE_FLOAT);// 注意,float
// Number v032 = readHoldingRegister(1,
1
, DataType.FOUR_BYTE_FLOAT);// 同上
// System.out.println("v031:" + v031);
// System.out.println("v032:" + v032);
//
// // 04测试
// Number v041 = readInputRegisters(1, 0, DataType.FOUR_BYTE_FLOAT);
//
// Number v042 = readInputRegisters(1, 2, DataType.FOUR_BYTE_FLOAT);
//
// Number v041 = readInputRegisters(1, 0, DataType.FOUR_BYTE_FLOAT);
// Number v042 = readInputRegisters(1, 2, DataType.FOUR_BYTE_FLOAT);
// System.out.println("v041:" + v041);
// System.out.println("v042:" + v042);
// // 批量读取
// batchRead();
modbusMaster
=
getMaster
(
"192.168.2.11"
,
502
);
boolean
[]
booleans
=
readDiscreteInputs
(
modbusMaster
,
1
,
0
,
2
);
// 输出结果
for
(
int
i
=
0
;
i
<
booleans
.
length
;
i
++)
{
System
.
out
.
println
(
"离散输入 "
+
(
0
+
i
)
+
": "
+
booleans
[
i
]);
}
}
catch
(
Exception
e
)
{
e
.
printStackTrace
();
}
finally
{
destroyMaster
();
if
(
modbusMaster
!=
null
)
{
modbusMaster
.
destroy
();
System
.
out
.
println
(
"Modbus连接已关闭"
);
modbusMaster
=
null
;
}
if
(
master
!=
null
)
{
master
.
destroy
();
System
.
out
.
println
(
"Modbus连接已关闭"
);
master
=
null
;
}
}
}
/**
* 读取设备寄存器(线程安全版)
*/
public
static
int
[]
readDeviceRegisters
(
ModbusMaster
master
,
int
deviceId
)
throws
ModbusTransportException
{
// 创建读取请求
ReadHoldingRegistersRequest
request
=
Modbus4jUtils
.
getReadHoldingRegistersRequest
(
deviceId
,
START_ADDRESS
,
REGISTER_COUNT
);
// 发送请求并获取响应
ModbusResponse
response
=
master
.
send
(
request
);
// 检查响应类型
if
(!(
response
instanceof
ReadHoldingRegistersResponse
))
{
throw
new
IllegalArgumentException
(
"Invalid response type: "
+
response
.
getClass
().
getName
());
}
ReadHoldingRegistersResponse
regResponse
=
(
ReadHoldingRegistersResponse
)
response
;
short
[]
signedValues
=
regResponse
.
getShortData
();
// 转换为无符号整数
int
[]
unsignedValues
=
new
int
[
signedValues
.
length
];
for
(
int
i
=
0
;
i
<
signedValues
.
length
;
i
++)
{
unsignedValues
[
i
]
=
signedValues
[
i
]
&
0xFFFF
;
}
return
unsignedValues
;
}
}
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment