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
5bf63fbd
Commit
5bf63fbd
authored
Jun 27, 2025
by
wanghao
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
1 使用 modbus4j + juc 实现 读取层 72个点位的 设备数据测试中。
parent
804418ce
Show whitespace changes
Inline
Side-by-side
Showing
11 changed files
with
907 additions
and
128 deletions
+907
-128
TestTaskController.java
...m/zehong/web/controller/equipment/TestTaskController.java
+1
-18
AgingCabinetInspectionAndPowerCheckTask.java
...ong/web/task/AgingCabinetInspectionAndPowerCheckTask.java
+0
-9
DeviceCommunicationJob.java
...main/java/com/zehong/web/task/DeviceCommunicationJob.java
+19
-59
FinalExecutionJob.java
.../src/main/java/com/zehong/web/task/FinalExecutionJob.java
+1
-4
DeviceStatusReaderAndTimeSetter.java
...stem/modbus/business/DeviceStatusReaderAndTimeSetter.java
+210
-0
DeviceStatusReaderDto.java
...a/com/zehong/system/modbus/dto/DeviceStatusReaderDto.java
+63
-0
ModbusDeviceData.java
...n/java/com/zehong/system/modbus/dto/ModbusDeviceData.java
+1
-1
ModbusResultHandler.java
...com/zehong/system/modbus/handler/ModbusResultHandler.java
+36
-0
Modbus4jUtils.java
...ain/java/com/zehong/system/modbus/util/Modbus4jUtils.java
+208
-36
ModbusTcpTask.java
...ain/java/com/zehong/system/modbus/util/ModbusTcpTask.java
+1
-1
MultiDeviceModbusMonitor.java
...m/zehong/system/modbus/util/MultiDeviceModbusMonitor.java
+367
-0
No files found.
zhmes-agecal-admin/src/main/java/com/zehong/web/controller/equipment/TestTaskController.java
View file @
5bf63fbd
...
...
@@ -4,14 +4,12 @@ import com.serotonin.modbus4j.ModbusMaster;
import
com.serotonin.modbus4j.exception.ErrorResponseException
;
import
com.serotonin.modbus4j.exception.ModbusInitException
;
import
com.serotonin.modbus4j.exception.ModbusTransportException
;
import
com.zehong.framework.modbus4j.Modbus4jUtils
;
import
com.zehong.system.domain.TEquipmentAlarmData
;
import
com.zehong.system.domain.TEquipmentInfo
;
import
com.zehong.system.domain.TStoreyInfo
;
import
com.zehong.system.domain.modbus.ModbusDeviceData
;
import
com.zehong.system.modbus.util.Modbus4jUtils
;
import
com.zehong.system.service.ITEquipmentAlarmDataService
;
import
com.zehong.system.service.ITEquipmentInfoService
;
import
com.zehong.system.service.ITStoreyInfoService
;
import
com.zehong.web.task.AgingCabinetInspectionAndPowerCheckTask
;
import
com.zehong.web.task.CheckPowerOnCommandEvent
;
import
com.zehong.web.task.PowerOffCommandEvent
;
...
...
@@ -73,21 +71,6 @@ public class TestTaskController {
* 五分钟一次
* 1.老化柜、标定柜巡查
* 2.老化层断电
* 这种方式先注释掉
* // for (TEquipmentInfo equipmentInfo : equipmentInfos) {
* // Future<Map<Integer, Object>> future = executor.submit(new ModbusTcpTask(equipmentInfo, registerOffset));
* // futures.add(future);
* // }
* // List<ModbusDeviceData> results = new ArrayList<>();
* //
* // for (int i = 0; i < futures.size(); i++) {
* // Map<Integer, Object> data = futures.get(i).get();
* // ModbusDeviceData deviceData = new ModbusDeviceData();
* // deviceData.setDeviceId(equipmentInfos.get(i).getfEquipmentId().toString());
* // deviceData.setRegisterValues(data.entrySet().stream()
* // .collect(Collectors.toMap(Map.Entry::getKey, e -> e.getValue().toString())));
* // results.add(deviceData);
* // }
*/
@GetMapping
(
"/getAgingCabinetAndPowerCheck"
)
public
DeferredResult
<
List
<
ModbusDeviceData
>>
getAgingCabinetAndPowerCheck
()
{
...
...
zhmes-agecal-admin/src/main/java/com/zehong/web/task/AgingCabinetInspectionAndPowerCheckTask.java
View file @
5bf63fbd
package
com
.
zehong
.
web
.
task
;
import
com.serotonin.modbus4j.ModbusMaster
;
import
com.serotonin.modbus4j.exception.ErrorResponseException
;
import
com.serotonin.modbus4j.exception.ModbusInitException
;
import
com.serotonin.modbus4j.exception.ModbusTransportException
;
import
com.zehong.framework.modbus4j.Modbus4jUtils
;
import
com.zehong.system.domain.TEquipmentInfo
;
import
com.zehong.system.domain.modbus.ModbusDeviceData
;
import
com.zehong.system.service.ITEquipmentInfoService
;
import
com.zehong.web.controller.equipment.EquipmentDataCollection
;
import
org.slf4j.Logger
;
import
org.slf4j.LoggerFactory
;
import
org.springframework.stereotype.Component
;
import
org.springframework.web.context.request.async.DeferredResult
;
import
javax.annotation.Resource
;
import
java.util.*
;
import
java.util.concurrent.CompletableFuture
;
import
java.util.concurrent.ExecutorService
;
import
java.util.concurrent.Executors
;
import
java.util.stream.Collectors
;
/**
* @author lenovo
...
...
zhmes-agecal-admin/src/main/java/com/zehong/web/task/DeviceCommunicationJob.java
View file @
5bf63fbd
package
com
.
zehong
.
web
.
task
;
import
com.serotonin.modbus4j.ModbusMaster
;
import
com.zehong.framework.modbus4j.Modbus4jUtils
;
import
com.zehong.system.domain.TEquipmentAlarmData
;
import
com.zehong.system.domain.TStoreyInfo
;
import
com.zehong.system.mapper.TStoreyInfoMapper
;
import
com.zehong.system.modbus.business.DeviceStatusReaderAndTimeSetter
;
import
com.zehong.system.modbus.handler.ModbusResultHandler
;
import
com.zehong.system.service.ITEquipmentAlarmDataService
;
import
com.zehong.system.service.ITStoreyInfoService
;
import
org.quartz.Job
;
import
org.quartz.JobDataMap
;
import
org.quartz.JobExecutionContext
;
import
org.slf4j.Logger
;
import
org.slf4j.LoggerFactory
;
import
org.springframework.stereotype.Component
;
import
javax.annotation.Resource
;
import
java.time.Duration
;
import
java.time.Instant
;
import
java.util.Arrays
;
import
java.util.List
;
import
java.util.Map
;
/**
* @author lenovo
* @date 2025/6/25
* @description
TODO
* @description
上电以后 两分钟执行一次的逻辑
*/
@Component
public
class
DeviceCommunicationJob
implements
Job
{
...
...
@@ -34,7 +27,7 @@ public class DeviceCommunicationJob implements Job {
@Resource
private
ITEquipmentAlarmDataService
alarmDataService
;
@Resource
private
ITStoreyInfoService
tStoreyInfoService
;
private
DeviceStatusReaderAndTimeSetter
deviceStatusReaderAndTimeSetter
;
@Resource
private
TStoreyInfoMapper
tStoreyInfoMapper
;
...
...
@@ -49,35 +42,22 @@ public class DeviceCommunicationJob implements Job {
// 1. 执行Modbus通信
String
s
=
tStoreyInfo
.
getfPort
();
String
ip
=
tStoreyInfo
.
getfIp
();
// 三个端口号,对应三组Modbus通信
String
[]
split
=
s
.
split
(
","
);
List
<
Integer
>
registerOffsetsOne
=
Arrays
.
asList
(
0
,
1
,
2
,
3
,
4
,
5
,
6
,
7
,
8
,
9
,
10
,
11
,
12
,
13
,
14
,
15
,
16
,
17
,
18
,
19
,
20
,
21
,
22
,
23
,
24
,
25
,
26
);
log
.
info
(
"registerOffsetsOne.ip:{}, port{}"
,
ip
,
Integer
.
parseInt
(
split
[
0
]));
Map
<
Integer
,
Object
>
integerObjectMapOne
=
Modbus4jUtils
.
batchReadAgingCabinetStatus
(
Modbus4jUtils
.
getMaster
(
ip
,
Integer
.
parseInt
(
split
[
0
])),
registerOffsetsOne
);
for
(
Map
.
Entry
<
Integer
,
Object
>
integerObjectEntry
:
integerObjectMapOne
.
entrySet
())
{
log
.
info
(
"integerObjectMapOne 的 key是:{} value: {}"
,
integerObjectEntry
.
getKey
(),
integerObjectEntry
.
getValue
());
}
List
<
Integer
>
registerOffsetsTwo
=
Arrays
.
asList
(
27
,
28
,
29
,
30
,
31
,
32
,
33
,
34
,
35
,
36
,
37
,
38
,
39
,
40
,
41
,
42
,
43
,
44
,
45
,
46
,
47
,
48
,
49
,
50
,
51
,
52
,
53
);
log
.
info
(
"integerObjectMapTwo.ip:{}, port{}"
,
ip
,
Integer
.
parseInt
(
split
[
1
]));
Map
<
Integer
,
Object
>
integerObjectMapTwo
=
Modbus4jUtils
.
batchReadAgingCabinetStatus
(
Modbus4jUtils
.
getMaster
(
ip
,
Integer
.
parseInt
(
split
[
1
])),
registerOffsetsTwo
);
for
(
Map
.
Entry
<
Integer
,
Object
>
integerObjectEntry
:
integerObjectMapTwo
.
entrySet
())
{
log
.
info
(
"integerObjectMapTwo 的 key是:{} value: {}"
,
integerObjectEntry
.
getKey
(),
integerObjectEntry
.
getValue
());
}
List
<
Integer
>
registerOffsetsThree
=
Arrays
.
asList
(
54
,
55
,
56
,
57
,
58
,
59
,
60
,
61
,
62
,
63
,
64
,
65
,
66
,
67
,
68
,
69
,
70
,
71
);
Map
<
Integer
,
Object
>
integerObjectMapThree
=
Modbus4jUtils
.
batchReadAgingCabinetStatus
(
Modbus4jUtils
.
getMaster
(
ip
,
Integer
.
parseInt
(
split
[
2
])),
registerOffsetsThree
);
ModbusResultHandler
resultHandler
=
new
ModbusResultHandler
();
// 501 的 27个 设备id
List
<
Integer
>
registerOffsetsOne
=
Arrays
.
asList
(
1
,
2
,
3
,
4
,
5
,
6
,
7
,
8
,
9
,
10
,
11
,
12
,
13
,
14
,
15
,
16
,
17
,
18
,
19
,
20
,
21
,
22
,
23
,
24
,
25
,
26
,
27
);
// 501 对应 27个设备读取状态 并设置时间
deviceStatusReaderAndTimeSetter
.
startMultiDeviceMonitoring
(
ip
,
501
,
registerOffsetsOne
,
resultHandler
,
ModbusResultHandler
.
createDefaultStopCondition
());
// 502 端口号的 27个 设备id
List
<
Integer
>
registerOffsetsTwo
=
Arrays
.
asList
(
28
,
29
,
30
,
31
,
32
,
33
,
34
,
35
,
36
,
37
,
38
,
39
,
40
,
41
,
42
,
43
,
44
,
45
,
46
,
47
,
48
,
49
,
50
,
51
,
52
,
53
,
54
);
// 502 对应 27个设备读取状态 并设置时间
deviceStatusReaderAndTimeSetter
.
startMultiDeviceMonitoring
(
ip
,
502
,
registerOffsetsTwo
,
resultHandler
,
ModbusResultHandler
.
createDefaultStopCondition
());
// 503 端口号的 剩下的设备
List
<
Integer
>
registerOffsetsThree
=
Arrays
.
asList
(
55
,
56
,
57
,
58
,
59
,
60
,
61
,
62
,
63
,
64
,
65
,
66
,
67
,
68
,
69
,
70
,
71
,
72
);
// 503 对应 剩下设备读取状态 并设置时间
deviceStatusReaderAndTimeSetter
.
startMultiDeviceMonitoring
(
ip
,
503
,
registerOffsetsThree
,
resultHandler
,
ModbusResultHandler
.
createDefaultStopCondition
());
log
.
info
(
"integerObjectMapThree.ip:{}, port{}"
,
ip
,
Integer
.
parseInt
(
split
[
2
]));
for
(
Map
.
Entry
<
Integer
,
Object
>
integerObjectEntry
:
integerObjectMapThree
.
entrySet
())
{
log
.
info
(
"integerObjectMapThree 的 key是:{} value: {}"
,
integerObjectEntry
.
getKey
(),
integerObjectEntry
.
getValue
());
}
// 2. 检查是否到达特殊时间点
//checkSpecialTimePoints(tStoreyInfo);
}
catch
(
Exception
e
)
{
// 记录异常
TEquipmentAlarmData
alarmData
=
new
TEquipmentAlarmData
();
...
...
@@ -87,24 +67,4 @@ public class DeviceCommunicationJob implements Job {
alarmDataService
.
insertTEquipmentAlarmData
(
alarmData
);
}
}
private
void
checkSpecialTimePoints
(
TStoreyInfo
tStoreyInfo
)
{
if
(
tStoreyInfo
==
null
||
"COMPLETED"
.
equals
(
tStoreyInfo
.
getfStatus
()))
return
;
// 计算已运行小时数
long
hours
=
Duration
.
between
(
tStoreyInfo
.
getfAgingStartTime
().
toInstant
(),
Instant
.
now
()).
toHours
();
// 特殊时间点处理 (24小时和48小时)
if
(
hours
==
24
||
hours
==
48
)
{
log
.
info
(
"设备[{}]到达特殊时间点: {}小时"
,
tStoreyInfo
.
getfStoreyCode
(),
hours
);
// 执行特殊操作
performSpecialOperation
(
tStoreyInfo
);
}
}
// ... 其他辅助方法
private
void
performSpecialOperation
(
TStoreyInfo
tStoreyInfo
)
{
}
}
zhmes-agecal-admin/src/main/java/com/zehong/web/task/FinalExecutionJob.java
View file @
5bf63fbd
...
...
@@ -4,10 +4,10 @@ import com.serotonin.modbus4j.ModbusMaster;
import
com.serotonin.modbus4j.exception.ModbusInitException
;
import
com.serotonin.modbus4j.exception.ModbusTransportException
;
import
com.zehong.common.utils.StringUtils
;
import
com.zehong.framework.modbus4j.Modbus4jUtils
;
import
com.zehong.system.domain.TEquipmentAlarmData
;
import
com.zehong.system.domain.TStoreyInfo
;
import
com.zehong.system.mapper.TStoreyInfoMapper
;
import
com.zehong.system.modbus.util.Modbus4jUtils
;
import
com.zehong.system.service.ITEquipmentAlarmDataService
;
import
com.zehong.system.service.ITStoreyInfoService
;
import
org.quartz.*
;
...
...
@@ -17,9 +17,6 @@ import org.springframework.beans.factory.annotation.Autowired;
import
org.springframework.stereotype.Component
;
import
javax.annotation.Resource
;
import
java.util.Arrays
;
import
java.util.Date
;
import
java.util.List
;
/**
* @author lenovo
...
...
zhmes-agecal-system/src/main/java/com/zehong/system/modbus/business/DeviceStatusReaderAndTimeSetter.java
0 → 100644
View file @
5bf63fbd
package
com
.
zehong
.
system
.
modbus
.
business
;
import
com.serotonin.modbus4j.ModbusFactory
;
import
com.serotonin.modbus4j.ModbusMaster
;
import
com.serotonin.modbus4j.exception.ModbusInitException
;
import
com.serotonin.modbus4j.exception.ModbusTransportException
;
import
com.serotonin.modbus4j.ip.IpParameters
;
import
com.serotonin.modbus4j.msg.ModbusResponse
;
import
com.serotonin.modbus4j.msg.ReadHoldingRegistersRequest
;
import
com.serotonin.modbus4j.msg.ReadHoldingRegistersResponse
;
import
com.zehong.system.domain.TEquipmentAlarmData
;
import
com.zehong.system.modbus.dto.DeviceStatusReaderDto
;
import
com.zehong.system.modbus.util.Modbus4jUtils
;
import
com.zehong.system.service.ITEquipmentAlarmDataService
;
import
org.springframework.stereotype.Component
;
import
javax.annotation.Resource
;
import
java.util.List
;
import
java.util.concurrent.CountDownLatch
;
import
java.util.concurrent.ExecutorService
;
import
java.util.concurrent.Executors
;
import
java.util.concurrent.TimeUnit
;
import
java.util.function.Consumer
;
import
java.util.function.Predicate
;
/**
* @author lenovo
* @date 2025/6/27
* @description modbus4j 单独提取出来的 读设备状态并设置时间
*/
@Component
public
class
DeviceStatusReaderAndTimeSetter
{
// 常量改为public以便外部访问
public
static
final
int
START_ADDRESS
=
0
;
public
static
final
int
REGISTER_COUNT
=
10
;
public
static
final
int
TARGET_VALUE
=
1
;
public
static
final
int
MAX_RETRIES
=
3
;
public
static
final
int
RETRY_DELAY
=
500
;
public
static
final
int
TIMEOUT_MINUTES
=
5
;
// 工厂
private
static
final
ModbusFactory
modbusFactory
=
new
ModbusFactory
();
@Resource
private
ITEquipmentAlarmDataService
alarmDataService
;
private
static
final
org
.
slf4j
.
Logger
log
=
org
.
slf4j
.
LoggerFactory
.
getLogger
(
DeviceStatusReaderAndTimeSetter
.
class
);
/**
* 读取设备寄存器(线程安全版)
*/
private
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
;
}
/**
* 带自定义条件的重试读取(线程安全版)
*/
private
int
[]
readWithConditionalRetry
(
ModbusMaster
threadMaster
,
String
ip
,
int
port
,
int
deviceId
,
Predicate
<
int
[]>
conditionChecker
)
throws
InterruptedException
{
TEquipmentAlarmData
alarmData
;
int
[]
result
=
null
;
int
attempt
=
0
;
boolean
conditionMet
=
false
;
if
(
threadMaster
!=
null
)
{
while
(
attempt
<=
MAX_RETRIES
&&
!
conditionMet
)
{
attempt
++;
try
{
log
.
info
(
"当前 - 尝试第:{}次 读取设备ip:{} port:{} device:{}的数据"
,
attempt
,
ip
,
port
,
deviceId
);
// 读取数据
result
=
readDeviceRegisters
(
threadMaster
,
deviceId
);
// 使用自定义条件检查
if
(
conditionChecker
.
test
(
result
))
{
log
.
info
(
"当前 设备ip:{} port:{} device:{}的数据条件满足"
,
ip
,
port
,
deviceId
);
conditionMet
=
true
;
}
else
if
(
attempt
<=
MAX_RETRIES
)
{
log
.
info
(
"当前 设备ip:{} port:{} device:{}的数据条件未满足,等待重试..."
,
ip
,
port
,
deviceId
);
Thread
.
sleep
(
RETRY_DELAY
);
}
}
catch
(
Exception
e
)
{
alarmData
=
new
TEquipmentAlarmData
();
alarmData
.
setfAlarmType
(
"04"
);
//01.老化柜 02.机械臂 03.老化层 04.点位
alarmData
.
setfEquipmentCode
(
"ip:"
+
ip
+
",port:"
+
port
+
",deviceId:"
+
deviceId
);
alarmData
.
setfAlarmData
(
"读取失败"
);
alarmDataService
.
insertTEquipmentAlarmData
(
alarmData
);
if
(
attempt
<=
MAX_RETRIES
)
{
Thread
.
sleep
(
RETRY_DELAY
);
}
}
}
// 如果达到最大重试次数仍未满足条件
if
(!
conditionMet
)
{
log
.
info
(
"当前 设备ip:{} port:{} device:{}的尝试次数达到最大:{}"
,
ip
,
port
,
deviceId
,
MAX_RETRIES
);
}
}
return
result
!=
null
?
result
:
new
int
[
REGISTER_COUNT
];
}
/**
* 创建新的Modbus连接
*/
private
static
ModbusMaster
createModbusMaster
(
String
ip
,
int
port
)
throws
ModbusInitException
{
IpParameters
params
=
new
IpParameters
();
params
.
setHost
(
ip
);
params
.
setPort
(
port
);
ModbusMaster
master
=
modbusFactory
.
createTcpMaster
(
params
,
true
);
master
.
setTimeout
(
3000
);
master
.
setRetries
(
1
);
master
.
init
();
return
master
;
}
/**
* 启动多设备监控(核心方法)
*/
public
void
startMultiDeviceMonitoring
(
String
ip
,
int
port
,
List
<
Integer
>
deviceIds
,
Consumer
<
DeviceStatusReaderDto
>
resultHandler
,
Predicate
<
int
[]>
stopCondition
)
{
if
(
deviceIds
==
null
||
deviceIds
.
isEmpty
())
{
System
.
out
.
println
(
"⚠️ 警告: 设备ID列表为空,不执行监控"
);
return
;
}
final
CountDownLatch
latch
=
new
CountDownLatch
(
deviceIds
.
size
());
ExecutorService
executor
=
Executors
.
newFixedThreadPool
(
deviceIds
.
size
());
for
(
int
deviceId
:
deviceIds
)
{
final
int
devId
=
deviceId
;
executor
.
submit
(()
->
{
ModbusMaster
threadMaster
=
null
;
try
{
// 1 获取 线程专有的Modbus连接
threadMaster
=
createModbusMaster
(
ip
,
port
);
// 2 初始化线程专有的Modbus连接 并尝试读取数据
// 为什么传了 master 还传 ip 和 port ,因为 master 要读完后才释放,而 ip 和 port 是为了log用
int
[]
result
=
readWithConditionalRetry
(
threadMaster
,
ip
,
port
,
devId
,
stopCondition
);
// 创建包含完整信息的结果对象
DeviceStatusReaderDto
deviceStatusReaderDto
=
new
DeviceStatusReaderDto
(
ip
,
port
,
devId
,
result
);
// 3 设置回调数据
resultHandler
.
accept
(
deviceStatusReaderDto
);
}
catch
(
ModbusInitException
e
)
{
TEquipmentAlarmData
alarmData
=
new
TEquipmentAlarmData
();
alarmData
.
setfAlarmType
(
"03"
);
//01.老化柜 02.机械臂 03.老化层 04.点位
alarmData
.
setfEquipmentCode
(
"ip:"
+
ip
+
",port:"
+
port
);
alarmData
.
setfAlarmData
(
"定时任务巡检:Modbus初始化失败"
);
alarmDataService
.
insertTEquipmentAlarmData
(
alarmData
);
}
catch
(
Exception
e
)
{
TEquipmentAlarmData
alarmData
=
new
TEquipmentAlarmData
();
alarmData
.
setfAlarmType
(
"03"
);
//01.老化柜 02.机械臂 03.老化层 04.点位
alarmData
.
setfEquipmentCode
(
"ip:"
+
ip
+
",port:"
+
port
);
alarmData
.
setfAlarmData
(
"监控任务异常:"
+
e
.
getMessage
());
alarmDataService
.
insertTEquipmentAlarmData
(
alarmData
);
}
finally
{
if
(
threadMaster
!=
null
)
threadMaster
.
destroy
();
latch
.
countDown
();
log
.
info
(
"当前 - 设备ip:{} port:{} device:{}的监控任务完成"
,
ip
,
port
,
deviceId
);
}
});
}
executor
.
shutdown
();
try
{
if
(!
executor
.
awaitTermination
(
TIMEOUT_MINUTES
,
TimeUnit
.
MINUTES
))
{
TEquipmentAlarmData
alarmData
=
new
TEquipmentAlarmData
();
alarmData
.
setfAlarmType
(
"03"
);
//01.老化柜 02.机械臂 03.老化层 04.点位
alarmData
.
setfEquipmentCode
(
"ip:"
+
ip
+
",port:"
+
port
);
alarmData
.
setfAlarmData
(
"警告: 部分设备监控任务超时未完成"
);
alarmDataService
.
insertTEquipmentAlarmData
(
alarmData
);
}
}
catch
(
InterruptedException
e
)
{
Thread
.
currentThread
().
interrupt
();
}
}
}
zhmes-agecal-system/src/main/java/com/zehong/system/modbus/dto/DeviceStatusReaderDto.java
0 → 100644
View file @
5bf63fbd
package
com
.
zehong
.
system
.
modbus
.
dto
;
import
java.util.Arrays
;
/**
* @author lenovo
* @date 2025/6/27
* @description TODO
*/
public
class
DeviceStatusReaderDto
{
private
String
ip
;
private
int
port
;
private
int
deviceId
;
private
int
[]
registerData
;
public
DeviceStatusReaderDto
(
String
ip
,
int
port
,
int
deviceId
,
int
[]
registerData
)
{
this
.
ip
=
ip
;
this
.
port
=
port
;
this
.
deviceId
=
deviceId
;
this
.
registerData
=
registerData
;
}
public
String
getIp
()
{
return
ip
;
}
public
void
setIp
(
String
ip
)
{
this
.
ip
=
ip
;
}
public
int
getPort
()
{
return
port
;
}
public
void
setPort
(
int
port
)
{
this
.
port
=
port
;
}
public
int
getDeviceId
()
{
return
deviceId
;
}
public
void
setDeviceId
(
int
deviceId
)
{
this
.
deviceId
=
deviceId
;
}
public
int
[]
getRegisterData
()
{
return
registerData
;
}
public
void
setRegisterData
(
int
[]
registerData
)
{
this
.
registerData
=
registerData
;
}
@Override
public
String
toString
()
{
return
"ModbusResult{"
+
"ip='"
+
ip
+
'\''
+
", port="
+
port
+
", deviceId="
+
deviceId
+
", registerData="
+
Arrays
.
toString
(
registerData
)
+
'}'
;
}
}
zhmes-agecal-
framework/src/main/java/com/zehong/framework/modbus4j
/ModbusDeviceData.java
→
zhmes-agecal-
system/src/main/java/com/zehong/system/modbus/dto
/ModbusDeviceData.java
View file @
5bf63fbd
package
com
.
zehong
.
framework
.
modbus4j
;
package
com
.
zehong
.
system
.
modbus
.
dto
;
import
java.util.Map
;
...
...
zhmes-agecal-system/src/main/java/com/zehong/system/modbus/handler/ModbusResultHandler.java
0 → 100644
View file @
5bf63fbd
package
com
.
zehong
.
system
.
modbus
.
handler
;
import
com.zehong.system.modbus.business.DeviceStatusReaderAndTimeSetter
;
import
com.zehong.system.modbus.dto.DeviceStatusReaderDto
;
import
java.util.Arrays
;
import
java.util.function.Consumer
;
import
java.util.function.Predicate
;
/**
* @author lenovo
* @date 2025/6/27
* @description TODO
*/
public
class
ModbusResultHandler
implements
Consumer
<
DeviceStatusReaderDto
>
{
@Override
public
void
accept
(
DeviceStatusReaderDto
deviceStatusReaderDto
)
{
int
deviceId
=
deviceStatusReaderDto
.
getDeviceId
();
int
[]
data
=
deviceStatusReaderDto
.
getRegisterData
();
String
ip
=
deviceStatusReaderDto
.
getIp
();
int
port
=
deviceStatusReaderDto
.
getPort
();
System
.
out
.
println
(
">>> 回调处理: 接收到新数据"
);
System
.
out
.
println
(
" 数据: "
+
Arrays
.
toString
(
data
));
if
(
data
.
length
>=
2
&&
data
[
1
]
==
DeviceStatusReaderAndTimeSetter
.
TARGET_VALUE
)
{
System
.
out
.
println
(
" >>> 注意: 第二个寄存器值为1!"
);
}
}
// 创建通用的停止条件(可选)
public
static
Predicate
<
int
[]>
createDefaultStopCondition
()
{
return
values
->
values
.
length
>=
2
&&
values
[
1
]
==
DeviceStatusReaderAndTimeSetter
.
TARGET_VALUE
;
}
}
zhmes-agecal-
framework/src/main/java/com/zehong/framework/modbus4j
/Modbus4jUtils.java
→
zhmes-agecal-
system/src/main/java/com/zehong/system/modbus/util
/Modbus4jUtils.java
View file @
5bf63fbd
package
com
.
zehong
.
framework
.
modbus4j
;
package
com
.
zehong
.
system
.
modbus
.
util
;
import
com.serotonin.modbus4j.BatchRead
;
import
com.serotonin.modbus4j.BatchResults
;
...
...
@@ -14,10 +14,8 @@ import com.serotonin.modbus4j.msg.*;
import
org.slf4j.Logger
;
import
org.slf4j.LoggerFactory
;
import
java.util.Arrays
;
import
java.util.HashMap
;
import
java.util.List
;
import
java.util.Map
;
import
java.util.*
;
import
java.util.concurrent.atomic.AtomicBoolean
;
/**
* modbus通讯工具类,采用modbus4j实现
...
...
@@ -27,6 +25,19 @@ import java.util.Map;
public
class
Modbus4jUtils
{
private
static
final
Logger
log
=
LoggerFactory
.
getLogger
(
Modbus4jUtils
.
class
);
private
static
ModbusMaster
master
;
private
static
final
int
START_ADDRESS
=
0
;
// 对应40001
private
static
final
int
REGISTER_COUNT
=
10
;
// 读取10个寄存器
private
static
final
int
TARGET_VALUE
=
1
;
// 目标值(第二个寄存器的期望值)
private
static
final
int
MAX_RETRIES
=
3
;
// 最大重试次数
private
static
final
int
RETRY_DELAY
=
500
;
// 重试延迟(ms)// 监控参数
private
static
final
int
MONITOR_INTERVAL
=
5000
;
// 监控间隔(ms)
// 监控控制标志
private
static
final
AtomicBoolean
monitoring
=
new
AtomicBoolean
(
false
);
/**
* 工厂。
*/
...
...
@@ -45,13 +56,15 @@ public class Modbus4jUtils {
*/
public
static
ModbusMaster
getMaster
()
throws
ModbusInitException
{
IpParameters
params
=
new
IpParameters
();
params
.
setHost
(
"
localhost
"
);
params
.
setPort
(
50
2
);
params
.
setHost
(
"
192.168.1.101
"
);
params
.
setPort
(
50
1
);
//
// modbusFactory.createRtuMaster(wapper); //RTU 协议
// modbusFactory.createUdpMaster(params);//UDP 协议
// modbusFactory.createAsciiMaster(wrapper);//ASCII 协议
ModbusMaster
master
=
modbusFactory
.
createTcpMaster
(
params
,
false
);
// TCP 协议
master
=
modbusFactory
.
createTcpMaster
(
params
,
false
);
// TCP 协议
master
.
setTimeout
(
3000
);
// 设置超时时间
master
.
setRetries
(
3
);
// 设置重试次数
master
.
init
();
return
master
;
...
...
@@ -69,6 +82,8 @@ public class Modbus4jUtils {
params
.
setHost
(
ip
);
params
.
setPort
(
port
);
ModbusMaster
master
=
modbusFactory
.
createTcpMaster
(
params
,
false
);
// TCP 协议
master
.
setTimeout
(
3000
);
// 设置超时时间
master
.
setRetries
(
1
);
// 设置重试次数
master
.
init
();
return
master
;
}
...
...
@@ -264,6 +279,25 @@ public class Modbus4jUtils {
System
.
out
.
println
(
results
.
getValue
(
1
));
}
public
static
Map
<
Integer
,
Object
>
batchReadHoldingRegister
(
ModbusMaster
master
,
List
<
Integer
>
registerOffsets
)
throws
ModbusTransportException
,
ErrorResponseException
{
BatchRead
<
Integer
>
batch
=
new
BatchRead
<
Integer
>();
for
(
Integer
registerOffset
:
registerOffsets
)
{
batch
.
addLocator
(
registerOffset
,
BaseLocator
.
holdingRegister
(
1
,
registerOffset
,
DataType
.
EIGHT_BYTE_INT_UNSIGNED
));
}
// 非连续地址也支持
batch
.
setContiguousRequests
(
true
);
BatchResults
<
Integer
>
send
=
master
.
send
(
batch
);
//
Map
<
Integer
,
Object
>
result
=
new
HashMap
<>();
for
(
Integer
registerOffset
:
registerOffsets
)
{
result
.
put
(
registerOffset
,
send
.
getValue
(
registerOffset
));
}
return
result
;
}
/**
* 批量读取使用方法
*
...
...
@@ -290,6 +324,62 @@ public class Modbus4jUtils {
return
result
;
}
/**
* 读取保持寄存器(16位无符号整数)
*
* @param slaveId 从站ID
* @param offset 寄存器起始地址(0-based)
* @param count 读取的寄存器数量
* @return 无符号整数值数组
* 请求格式解析:
* 00 00 - 事务ID (0)
* 00 00 - 协议ID (0)
* 00 06 - 长度 (6字节)
* 01 - 从站ID (1)
* 03 - 功能码 (读保持寄存器)
* 00 03 - 起始地址 (3)
* 00 0A - 寄存器数量 (10)
* 响应格式解析:
* 00 00 00 00 00 17 - Modbus TCP头
* 01 - 从站ID
* 03 - 功能码
* 14 - 数据字节数 (20字节 = 10寄存器 × 2字节)
* 后续20字节为10个寄存器的数据
* 关键处理逻辑:
* 16位无符号转换:register.getValue() & 0xFFFF
* 寄存器地址处理:modbus4j使用0-based地址,所以地址3对应设备手册中的40004寄存器
* TCP连接管理:使用长连接提高效率
*/
public
static
int
[]
readHoldingRegisters
(
int
slaveId
,
int
offset
,
int
count
)
throws
ModbusTransportException
,
ErrorResponseException
{
// 创建读取请求
ReadHoldingRegistersRequest
request
=
new
ReadHoldingRegistersRequest
(
slaveId
,
offset
,
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
[]
results
=
new
int
[
signedValues
.
length
];
// 转为无符号整数并打印
for
(
int
i
=
0
;
i
<
signedValues
.
length
;
i
++)
{
int
unsignedValue
=
signedValues
[
i
]
&
0xFFFF
;
results
[
i
]
=
signedValues
[
i
]
&
0xFFFF
;
System
.
out
.
printf
(
"寄存器 %d: %d (无符号) %n"
,
i
,
unsignedValue
);
}
return
results
;
}
/**
* 写 [01 Coil Status(0x)]写一个 function ID = 5
*
...
...
@@ -445,6 +535,59 @@ public class Modbus4jUtils {
BaseLocator
<
Number
>
locator
=
BaseLocator
.
holdingRegister
(
slaveId
,
offset
,
dataType
);
tcpMaster
.
setValue
(
locator
,
value
);
}
/**
* 关闭Modbus连接
*/
public
static
void
destroyMaster
()
{
if
(
master
!=
null
)
{
master
.
destroy
();
System
.
out
.
println
(
"Modbus连接已关闭"
);
master
=
null
;
}
}
/**
* 获取读取Holding Register请求对象
*
* @param slaveId
* @param offset
* @param count
* @return
* @throws ModbusTransportException
*/
public
static
ReadHoldingRegistersRequest
getReadHoldingRegistersRequest
(
int
slaveId
,
int
offset
,
int
count
)
throws
ModbusTransportException
{
return
new
ReadHoldingRegistersRequest
(
slaveId
,
offset
,
count
);
}
/**
* 读取设备寄存器(线程安全版)
*/
private
static
int
[]
readDeviceRegisters
(
int
deviceId
)
throws
ModbusTransportException
,
ModbusInitException
{
ModbusMaster
master
=
getMaster
();
// 创建读取请求
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
;
}
/**
* 测试
*
...
...
@@ -452,37 +595,66 @@ public class Modbus4jUtils {
*/
public
static
void
main
(
String
[]
args
)
{
try
{
// 01测试
Boolean
v011
=
readCoilStatus
(
1
,
0
);
Boolean
v012
=
readCoilStatus
(
1
,
1
);
Boolean
v013
=
readCoilStatus
(
1
,
6
);
System
.
out
.
println
(
"v011:"
+
v011
);
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
);
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
);
// 同上
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
);
//
System
.
out
.
println
(
"v041:"
+
v041
);
System
.
out
.
println
(
"v042:"
+
v042
);
// 批量读取
batchRead
();
/* 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
,
0
,
(
short
)
y
);
boolean
mResult
=
writeRegister
(
1
,
0
,
(
short
)
m
);
boolean
dResult
=
writeRegister
(
1
,
0
,
(
short
)
d
);
boolean
hResult
=
writeRegister
(
1
,
0
,
(
short
)
h
);
boolean
mmResult
=
writeRegister
(
1
,
0
,
(
short
)
mm
);
ModbusMaster
master1
=
getMaster
();
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);
// 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);
// 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);// 同上
// 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);//
// System.out.println("v041:" + v041);
// System.out.println("v042:" + v042);
// // 批量读取
// batchRead();
}
catch
(
Exception
e
)
{
e
.
printStackTrace
();
}
finally
{
destroyMaster
();
}
}
}
zhmes-agecal-
framework/src/main/java/com/zehong/framework/modbus4j
/ModbusTcpTask.java
→
zhmes-agecal-
system/src/main/java/com/zehong/system/modbus/util
/ModbusTcpTask.java
View file @
5bf63fbd
package
com
.
zehong
.
framework
.
modbus4j
;
package
com
.
zehong
.
system
.
modbus
.
util
;
import
com.serotonin.modbus4j.ModbusMaster
;
import
com.serotonin.modbus4j.exception.ModbusInitException
;
...
...
zhmes-agecal-system/src/main/java/com/zehong/system/modbus/util/MultiDeviceModbusMonitor.java
0 → 100644
View file @
5bf63fbd
package
com
.
zehong
.
system
.
modbus
.
util
;
import
com.serotonin.modbus4j.ModbusFactory
;
import
com.serotonin.modbus4j.ModbusMaster
;
import
com.serotonin.modbus4j.exception.ErrorResponseException
;
import
com.serotonin.modbus4j.exception.ModbusInitException
;
import
com.serotonin.modbus4j.exception.ModbusTransportException
;
import
com.serotonin.modbus4j.ip.IpParameters
;
import
com.serotonin.modbus4j.msg.ModbusResponse
;
import
com.serotonin.modbus4j.msg.ReadHoldingRegistersRequest
;
import
com.serotonin.modbus4j.msg.ReadHoldingRegistersResponse
;
import
java.io.BufferedReader
;
import
java.io.InputStreamReader
;
import
java.util.HashMap
;
import
java.util.Map
;
import
java.util.concurrent.ExecutorService
;
import
java.util.concurrent.Executors
;
import
java.util.concurrent.atomic.AtomicBoolean
;
import
java.util.concurrent.atomic.AtomicInteger
;
import
java.util.function.Consumer
;
import
java.util.function.Predicate
;
/**
* 独立的 多线程处理 多设备Modbus监控
*/
public
class
MultiDeviceModbusMonitor
{
private
static
final
ModbusFactory
modbusFactory
=
new
ModbusFactory
();
// 配置参数
private
static
final
String
HOST
=
"192.168.1.101"
;
private
static
final
int
PORT
=
501
;
private
static
final
int
START_ADDRESS
=
0
;
// 对应40001
private
static
final
int
REGISTER_COUNT
=
10
;
// 每个设备读取10个寄存器
private
static
final
int
MAX_RETRIES
=
3
;
// 最大重试次数
private
static
final
int
RETRY_DELAY
=
500
;
// 重试延迟(ms)
private
static
final
int
MONITOR_INTERVAL
=
2000
;
// 监控间隔(ms)
private
static
final
int
DEVICE_COUNT
=
4
;
// 监控27个设备
// 监控控制标志
private
static
final
AtomicBoolean
monitoring
=
new
AtomicBoolean
(
false
);
private
static
final
AtomicInteger
activeDevices
=
new
AtomicInteger
(
0
);
// 设备统计信息
private
static
final
Map
<
Integer
,
DeviceStats
>
deviceStats
=
new
HashMap
<>();
public
static
void
main
(
String
[]
args
)
{
try
{
System
.
out
.
println
(
"===== 启动27设备监控系统 ====="
);
System
.
out
.
printf
(
"监控配置: %d设备 | %d寄存器/设备 | %dms间隔%n"
,
DEVICE_COUNT
,
REGISTER_COUNT
,
MONITOR_INTERVAL
);
// 初始化设备统计
for
(
int
i
=
1
;
i
<=
DEVICE_COUNT
;
i
++)
{
deviceStats
.
put
(
i
,
new
DeviceStats
());
}
// 创建结果处理器
Consumer
<
int
[]>
resultHandler
=
data
->
{
// 实际应用中可添加业务逻辑
// 这里只做简单演示
};
// 启动监控
monitoring
.
set
(
true
);
startMultiDeviceMonitoring
(
resultHandler
,
values
->
values
.
length
>=
2
&&
values
[
1
]
==
1
);
// 主线程等待监控结束
System
.
out
.
println
(
"\n主线程运行中...输入命令控制监控:"
);
System
.
out
.
println
(
" status - 显示设备状态"
);
System
.
out
.
println
(
" stop - 停止所有监控"
);
System
.
out
.
println
(
" exit - 退出程序"
);
BufferedReader
reader
=
new
BufferedReader
(
new
InputStreamReader
(
System
.
in
));
while
(
monitoring
.
get
())
{
System
.
out
.
print
(
"\n> "
);
String
command
=
reader
.
readLine
().
trim
().
toLowerCase
();
switch
(
command
)
{
case
"status"
:
printDeviceStatus
();
break
;
case
"stop"
:
System
.
out
.
println
(
"停止所有设备监控..."
);
monitoring
.
set
(
false
);
break
;
case
"exit"
:
monitoring
.
set
(
false
);
break
;
default
:
System
.
out
.
println
(
"未知命令: "
+
command
);
}
}
// 等待所有监控线程结束
waitForDevicesToStop
();
}
catch
(
Exception
e
)
{
e
.
printStackTrace
();
}
finally
{
System
.
out
.
println
(
"监控系统已关闭"
);
}
}
/**
* 启动多设备监控
*/
public
static
void
startMultiDeviceMonitoring
(
Consumer
<
int
[]>
resultHandler
,
Predicate
<
int
[]>
stopCondition
)
{
System
.
out
.
println
(
"启动设备监控线程..."
);
// 使用线程池管理27个设备线程
ExecutorService
executor
=
Executors
.
newFixedThreadPool
(
DEVICE_COUNT
);
for
(
int
deviceId
=
1
;
deviceId
<=
DEVICE_COUNT
;
deviceId
++)
{
final
int
devId
=
deviceId
;
executor
.
submit
(()
->
{
activeDevices
.
incrementAndGet
();
try
{
startDeviceMonitor
(
devId
,
resultHandler
,
stopCondition
);
}
finally
{
activeDevices
.
decrementAndGet
();
}
});
}
// 关闭线程池接受新任务,但不影响已提交任务
executor
.
shutdown
();
}
/**
* 启动单个设备监控
*/
public
static
void
startDeviceMonitor
(
int
deviceId
,
Consumer
<
int
[]>
resultHandler
,
Predicate
<
int
[]>
stopCondition
)
{
ModbusMaster
threadMaster
=
null
;
try
{
// 1. 创建线程专用的Modbus连接
threadMaster
=
createModbusMaster
();
DeviceStats
stats
=
deviceStats
.
get
(
deviceId
);
stats
.
setStatus
(
"运行中"
);
System
.
out
.
printf
(
"设备 %02d: 监控启动 [%s]%n"
,
deviceId
,
Thread
.
currentThread
().
getName
());
int
cycle
=
0
;
while
(
monitoring
.
get
())
{
cycle
++;
stats
.
incrementReadCount
();
// 2. 读取数据(带重试)
int
[]
result
=
readWithConditionalRetry
(
threadMaster
,
deviceId
,
stats
);
// 3. 更新统计信息
if
(
result
!=
null
&&
result
.
length
>=
2
)
{
stats
.
updateLastValue
(
result
[
1
]);
}
// 4. 回调处理结果
resultHandler
.
accept
(
result
);
// 5. 检查是否应该停止监控
if
(
stopCondition
.
test
(
result
))
{
stats
.
setStatus
(
"条件满足停止"
);
stats
.
setConditionMet
(
true
);
System
.
out
.
printf
(
"设备 %02d: ✅ 停止条件满足%n"
,
deviceId
);
break
;
}
// 6. 监控间隔
try
{
Thread
.
sleep
(
MONITOR_INTERVAL
);
}
catch
(
InterruptedException
e
)
{
Thread
.
currentThread
().
interrupt
();
break
;
}
}
}
catch
(
Exception
e
)
{
System
.
err
.
printf
(
"设备 %02d 监控异常: %s%n"
,
deviceId
,
e
.
getMessage
());
deviceStats
.
get
(
deviceId
).
setStatus
(
"异常: "
+
e
.
getClass
().
getSimpleName
());
}
finally
{
// 7. 关闭线程专用的Modbus连接
if
(
threadMaster
!=
null
)
{
threadMaster
.
destroy
();
}
if
(!
deviceStats
.
get
(
deviceId
).
isConditionMet
())
{
deviceStats
.
get
(
deviceId
).
setStatus
(
"手动停止"
);
}
System
.
out
.
printf
(
"设备 %02d: 监控结束%n"
,
deviceId
);
}
}
/**
* 打印设备状态
*/
private
static
void
printDeviceStatus
()
{
System
.
out
.
println
(
"\n设备状态概览:"
);
System
.
out
.
println
(
"+-------+-----------+-----------+-----------+-----------------+---------+"
);
System
.
out
.
println
(
"| 设备ID| 读取次数 | 成功次数 | 失败次数 | 最后值(寄存器2) | 状态 |"
);
System
.
out
.
println
(
"+-------+-----------+-----------+-----------+-----------------+---------+"
);
for
(
int
i
=
1
;
i
<=
DEVICE_COUNT
;
i
++)
{
DeviceStats
stats
=
deviceStats
.
get
(
i
);
System
.
out
.
printf
(
"| %5d | %9d | %9d | %9d | %15d | %-7s |%n"
,
i
,
stats
.
getReadCount
(),
stats
.
getSuccessCount
(),
stats
.
getErrorCount
(),
stats
.
getLastValue
(),
stats
.
getStatus
());
}
System
.
out
.
println
(
"+-------+-----------+-----------+-----------+-----------------+---------+"
);
System
.
out
.
printf
(
"活动设备: %d/%d%n"
,
activeDevices
.
get
(),
DEVICE_COUNT
);
}
/**
* 等待所有设备停止
*/
private
static
void
waitForDevicesToStop
()
{
System
.
out
.
print
(
"等待设备停止"
);
int
waitCount
=
0
;
while
(
activeDevices
.
get
()
>
0
&&
waitCount
<
30
)
{
System
.
out
.
print
(
"."
);
try
{
Thread
.
sleep
(
500
);
}
catch
(
InterruptedException
e
)
{
Thread
.
currentThread
().
interrupt
();
}
waitCount
++;
}
System
.
out
.
println
();
if
(
activeDevices
.
get
()
>
0
)
{
System
.
out
.
printf
(
"警告: %d个设备未正常停止%n"
,
activeDevices
.
get
());
}
}
/**
* 创建新的Modbus连接
*/
private
static
ModbusMaster
createModbusMaster
()
throws
ModbusInitException
{
IpParameters
params
=
new
IpParameters
();
params
.
setHost
(
HOST
);
params
.
setPort
(
PORT
);
ModbusMaster
master
=
modbusFactory
.
createTcpMaster
(
params
,
true
);
master
.
setTimeout
(
3000
);
master
.
setRetries
(
1
);
master
.
init
();
return
master
;
}
/**
* 带自定义条件的重试读取
*/
private
static
int
[]
readWithConditionalRetry
(
ModbusMaster
master
,
int
deviceId
,
DeviceStats
stats
)
{
int
[]
result
=
null
;
int
attempt
=
0
;
boolean
success
=
false
;
while
(
attempt
<=
MAX_RETRIES
&&
monitoring
.
get
())
{
attempt
++;
try
{
// 读取数据
result
=
readDeviceRegisters
(
master
,
deviceId
,
START_ADDRESS
,
REGISTER_COUNT
);
stats
.
incrementSuccessCount
();
success
=
true
;
break
;
}
catch
(
Exception
e
)
{
stats
.
incrementErrorCount
();
System
.
err
.
printf
(
"设备 %02d 尝试 %d/%d 失败: %s%n"
,
deviceId
,
attempt
,
MAX_RETRIES
,
e
.
getMessage
());
if
(
attempt
<=
MAX_RETRIES
)
{
try
{
Thread
.
sleep
(
RETRY_DELAY
);
}
catch
(
InterruptedException
ie
)
{
Thread
.
currentThread
().
interrupt
();
break
;
}
}
}
}
if
(!
success
)
{
System
.
err
.
printf
(
"设备 %02d: ⚠️ 达到最大重试次数(%d)%n"
,
deviceId
,
MAX_RETRIES
);
}
return
result
!=
null
?
result
:
new
int
[
REGISTER_COUNT
];
}
/**
* 读取设备寄存器
*/
private
static
int
[]
readDeviceRegisters
(
ModbusMaster
master
,
int
deviceId
,
int
startAddress
,
int
count
)
throws
ModbusTransportException
,
ErrorResponseException
{
// 创建读取请求
ReadHoldingRegistersRequest
request
=
new
ReadHoldingRegistersRequest
(
deviceId
,
startAddress
,
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
;
}
/**
* 设备统计信息类
*/
static
class
DeviceStats
{
private
int
readCount
=
0
;
private
int
successCount
=
0
;
private
int
errorCount
=
0
;
private
int
lastValue
=
-
1
;
private
String
status
=
"未启动"
;
private
boolean
conditionMet
=
false
;
public
void
incrementReadCount
()
{
readCount
++;
}
public
void
incrementSuccessCount
()
{
successCount
++;
}
public
void
incrementErrorCount
()
{
errorCount
++;
}
public
void
updateLastValue
(
int
value
)
{
this
.
lastValue
=
value
;
}
// Getter 和 Setter 方法
public
int
getReadCount
()
{
return
readCount
;
}
public
int
getSuccessCount
()
{
return
successCount
;
}
public
int
getErrorCount
()
{
return
errorCount
;
}
public
int
getLastValue
()
{
return
lastValue
;
}
public
String
getStatus
()
{
return
status
;
}
public
void
setStatus
(
String
status
)
{
this
.
status
=
status
;
}
public
boolean
isConditionMet
()
{
return
conditionMet
;
}
public
void
setConditionMet
(
boolean
conditionMet
)
{
this
.
conditionMet
=
conditionMet
;
}
}
}
\ No newline at end of file
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