Java Modbus开发完全ガイド:构建エンタープライズレベル工业应用
引言:Java在工业自動化中的优势
Java凭借其跨プラットフォーム特性、强大的タイプ系统、丰富的エンタープライズレベル类库和成熟的生态系统,在工业自動化领域占据重要地位。从大型 SCADA 系统到エンベデッド·インゲートウェイデバイス,Java 提供了完全的ソリューション栈,特别适合需要高可靠性、高いパフォーマンスを和长期维护的エンタープライズレベル工业应用。
本記事将深入探讨 Java 在 Modbus 开发中的应用,重点介绍 Modbus4J、jamod、j2mod 等主流库的使用,并结合 Spring Boot、MQTT 等现代技术栈,表示如何构建专业级的産業用IoT系统。
一、Java Modbus 开发环境設定
1.1 开发环境要求
<!-- Maven 项目設定 -->
<project>
<modelVersion>4.0.0</modelVersion>
<groupId>com.example</groupId>
<artifactId>industrial-modbus</artifactId>
<version>1.0.0</version>
<properties>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
</project>
1.2 核心依赖設定
<dependencies>
<!-- Modbus4J - 功能最包括的的 Modbus 库 -->
<dependency>
<groupId>com.infiniteautomation</groupId>
<artifactId>modbus4j</artifactId>
<version>3.4.0</version>
</dependency>
<!-- j2mod - 现代化轻量级選択 -->
<dependency>
<groupId>com.ghgande</groupId>
<artifactId>j2mod</artifactId>
<version>3.1.4</version>
</dependency>
<!-- Spring Boot - エンタープライズレベル应用框架 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>3.2.0</version>
</dependency>
<!-- MQTT クライアント側は - IoT通信 -->
<dependency>
<groupId>org.eclipse.paho</groupId>
<artifactId>org.eclipse.paho.client.mqttv3</artifactId>
<version>1.2.5</version>
</dependency>
</dependencies>
二、主流 Java Modbus 库对比
2.1 Modbus4J:エンタープライズレベル首选
特点:
- 功能最包括的,サポートすべての Modbus function code
- サポート TCP、RTU、ASCII すべての传输モード
- 同时サポートマスター和駅からのモデル
- 活跃的社区维护(Infinite Automation)
- 完善的異常処理和日志系统
適用可能なシーン:
- 大型 SCADA system
- エンタープライズレベル工业モニタリングプラットフォーム
- 需要高可靠性的生产环境
2.2 j2mod:现代化轻量级方案
特点:
- 代码简洁,API 设计现代化
- サポート Modbus TCP 和 RTU
- 轻量级,依赖少
- 适合微服务架构
適用可能なシーン:
- 微服务架构的工业应用
- リソース受限的組込みデバイス
- 快速原型开发
2.3 jamod:经典稳定選択
特点:
- 历史悠久,稳定性好
- 纯 Java 実装,无 native 依赖
- ドキュメント完善,サンプル丰富
- 社区活跃度高
適用可能なシーン:
- 传统工业系统アップグレード
- 教学和学习用途
- 需要长期稳定サポート的项目
三、Modbus4J 実践开发
3.1 Modbus TCP クライアント側は実装
package com.example.modbus;
import com.serotonin.modbus4j.ModbusFactory;
import com.serotonin.modbus4j.ModbusMaster;
import com.serotonin.modbus4j.ip.tcp.TcpParameters;
import com.serotonin.modbus4j.exception.ModbusInitException;
public class ModbusTcpClient {
private final ModbusMaster master;
public ModbusTcpClient(String host, int port) {
ModbusFactory factory = new ModbusFactory();
TcpParameters params = new TcpParameters();
params.setHost(host);
params.setPort(port);
params.setKeepAlive(true);
master = factory.createTcpMaster(params, true);
master.setRetries(3);
master.setExceptionListeners(new ExceptionListenerImpl());
}
public void start() throws ModbusInitException {
master.init();
System.out.println("Modbus TCP 连接已建立");
}
public short readHoldingRegister(int slaveId, int offset) {
return master.readHoldingRegister(slaveId, offset).getValue();
}
public int[] readHoldingRegisters(int slaveId, int offset, int count) {
return master.readHoldingRegisters(slaveId, offset, count).getValue();
}
public void writeRegister(int slaveId, int offset, short value) {
master.writeRegister(slaveId, offset, value);
}
public void stop() {
master.destroy();
}
// 使用例の例
public static void main(String[] args) {
try {
ModbusTcpClient client = new ModbusTcpClient("192.168.1.100", 502);
client.start();
// Read and hold register
short temperature = client.readHoldingRegister(1, 0);
System.out.println("temperature:" + temperature + "°C");
// バッチ読み取り
int[] values = client.readHoldingRegisters(1, 0, 10);
for (int i = 0; i < values.length; i++) {
System.out.println("register" + i + ": " + values[i]);
}
// 写入レジスタ
client.writeRegister(1, 0, (short) 250);
client.stop();
} catch (Exception e) {
e.printStackTrace();
}
}
}
3.2 Modbus RTU クライアント側は実装
package com.example.modbus;
import com.serotonin.modbus4j.ModbusFactory;
import com.serotonin.modbus4j.ModbusMaster;
import com.serotonin.modbus4j.serial.rtu.RtuParameters;
public class ModbusRtuClient {
private final ModbusMaster master;
public ModbusRtuClient(String serialPort, int baudRate) {
ModbusFactory factory = new ModbusFactory();
RtuParameters params = new RtuParameters();
params.setCommPortId(serialPort); // 如:"/dev/ttyUSB0" 或 "COM3"
params.setBaudRate(baudRate);
params.setStopBits(1);
params.setParity(0); // 0=None, 1=Odd, 2=Even
params.setFlowControlIn(0);
params.setFlowControlOut(0);
master = factory.createRtuMaster(params);
master.setRetries(3);
master.setTimeout(500);
}
public void start() throws Exception {
master.init();
System.out.println("Modbus RTU 连接已建立");
}
public short readInputRegister(int slaveId, int offset) {
return master.readInputRegister(slaveId, offset).getValue();
}
public boolean readCoil(int slaveId, int offset) {
return master.readCoil(slaveId, offset).getValue();
}
public void writeCoil(int slaveId, int offset, boolean value) {
master.writeCoil(slaveId, offset, value);
}
public void stop() {
master.destroy();
}
}
3.3 Modbus スレーブサーバー·サーバー実装
package com.example.modbus;
import com.serotonin.modbus4j.ModbusFactory;
import com.serotonin.modbus4j.ModbusMaster;
import com.serotonin.modbus4j.ip.tcp.TcpParameters;
import com.serotonin.modbus4j.slave.ModbusSlave;
import com.serotonin.modbus4j.slave.SlaveEventListener;
public class ModbusServer {
private ModbusSlave slave;
public void start(int port) throws Exception {
ModbusFactory factory = new ModbusFactory();
TcpParameters params = new TcpParameters();
params.setHost("0.0.0.0");
params.setPort(port);
slave = factory.createTcpSlave(params, true);
slave.setEventListener(new SlaveEventListenerImpl());
slave.addProcessImage(1, new ModbusProcessImageImpl());
slave.start();
System.out.println("Modbus サーバー·サーバー已启动,监听ポート:" + port);
}
public void stop() {
if (slave != null) {
slave.stop();
}
}
}
四、Spring Boot 集成実践
4.1 项目構造
src/main/java/com/example/industrial/
├── IndustrialApplication.java
├── config/
│ └── ModbusConfig.java
├── service/
│ ├── ModbusService.java
│ └── DataProcessingService.java
├── controller/
│ └── ModbusController.java
└── model/
└── DeviceData.java
4.2 Modbus 設定类
package com.example.industrial.config;
import com.example.industrial.service.ModbusService;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class ModbusConfig {
@Bean
public ModbusService modbusService() {
return new ModbusService("192.168.1.100", 502);
}
}
4.3 Modbus 服务层
package com.example.industrial.service;
import com.example.modbus.ModbusTcpClient;
import com.example.industrial.model.DeviceData;
import org.springframework.stereotype.Service;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import java.util.ArrayList;
import java.util.List;
@Service
public class ModbusService {
private ModbusTcpClient client;
private final String host;
private final int port;
public ModbusService(String host, int port) {
this.host = host;
this.port = port;
}
@PostConstruct
public void init() {
client = new ModbusTcpClient(host, port);
try {
client.start();
} catch (Exception e) {
e.printStackTrace();
}
}
@PreDestroy
public void destroy() {
if (client != null) {
client.stop();
}
}
public DeviceData readDeviceData(int slaveId) {
DeviceData data = new DeviceData();
data.setDeviceId(slaveId);
data.setTemperature(client.readHoldingRegister(slaveId, 0));
data.setHumidity(client.readHoldingRegister(slaveId, 1));
data.setPressure(client.readHoldingRegister(slaveId, 2));
data.setTimestamp(System.currentTimeMillis());
return data;
}
public List<DeviceData> readAllDevices(int startId, int count) {
List<DeviceData> devices = new ArrayList<>();
for (int i = 0; i < count; i++) {
devices.add(readDeviceData(startId + i));
}
return devices;
}
public void writeSetpoint(int slaveId, short value) {
client.writeRegister(slaveId, 100, value);
}
}
4.4 REST API Controller
package com.example.industrial.controller;
import com.example.industrial.model.DeviceData;
import com.example.industrial.service.ModbusService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.util.List;
@RestController
@RequestMapping("/api/modbus")
@CrossOrigin(origins = "*")
public class ModbusController {
@Autowired
private ModbusService modbusService;
@GetMapping("/device/{slaveId}")
public DeviceData getDeviceData(@PathVariable int slaveId) {
return modbusService.readDeviceData(slaveId);
}
@GetMapping("/devices")
public List<DeviceData> getAllDevices(
@RequestParam(defaultValue = "1") int startId,
@RequestParam(defaultValue = "10") int count
) {
return modbusService.readAllDevices(startId, count);
}
@PostMapping("/device/{slaveId}/setpoint")
public String writeSetpoint(
@PathVariable int slaveId,
@RequestParam short value
) {
modbusService.writeSetpoint(slaveId, value);
return "設定点已更新";
}
}
4.5 データ模型
package com.example.industrial.model;
import lombok.Data;
@Data
public class DeviceData {
private int deviceId;
private short temperature;
private short humidity;
private short pressure;
private long timestamp;
}
五、エキスパートアプリケーションシナリオ。
5.1 异步データの収集
package com.example.industrial.service;
import java.util.concurrent.*;
import java.util.function.Consumer;
public class AsyncDataCollector {
private final ScheduledExecutorService scheduler =
Executors.newScheduledThreadPool(4);
private final ModbusService modbusService;
public AsyncDataCollector(ModbusService modbusService) {
this.modbusService = modbusService;
}
public void startPeriodicCollection(int slaveId, long interval,
Consumer<DeviceData> callback) {
scheduler.scheduleAtFixedRate(() -> {
try {
DeviceData data = modbusService.readDeviceData(slaveId);
callback.accept(data);
} catch (Exception e) {
System.err.println("データの収集失敗:" + e.getMessage());
}
}, 0, interval, TimeUnit.MILLISECONDS);
}
public void stop() {
scheduler.shutdown();
}
}
5.2 データ缓存与批量処理
package com.example.industrial.service;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;
public class DataCacheService {
private final ConcurrentHashMap<Integer, DeviceData> cache =
new ConcurrentHashMap<>();
private final long cacheTimeout = 5000; // 5 秒
public DeviceData getCachedData(int slaveId) {
DeviceData data = cache.get(slaveId);
if (data != null &&
System.currentTimeMillis() - data.getTimestamp() < cacheTimeout) {
return data;
}
return null;
}
public void updateCache(DeviceData data) {
cache.put(data.getDeviceId(), data);
}
public void cleanup() {
long now = System.currentTimeMillis();
cache.entrySet().removeIf(entry ->
now - entry.getValue().getTimestamp() > cacheTimeout
);
}
}
5.3 異常処理与重试机制
package com.example.industrial.service;
import com.serotonin.modbus4j.exception.ModbusException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class ResilientModbusService {
private static final Logger logger = LoggerFactory.getLogger(
ResilientModbusService.class
);
private final ModbusService delegate;
private final int maxRetries = 3;
private final long retryDelay = 1000;
public ResilientModbusService(ModbusService delegate) {
this.delegate = delegate;
}
public DeviceData readWithRetry(int slaveId) throws Exception {
int attempts = 0;
while (attempts < maxRetries) {
try {
return delegate.readDeviceData(slaveId);
} catch (ModbusException e) {
attempts++;
logger.warn("Read failed,重试 {}/{}", attempts, maxRetries);
if (attempts >= maxRetries) {
throw e;
}
Thread.sleep(retryDelay);
}
}
throw new Exception("达到最大リトライ回数");
}
}
六、パフォーマンス最適化ベストプラクティス
6.1 连接池管理
package com.example.industrial.pool;
import org.apache.commons.pool2.impl.GenericObjectPool;
import org.apache.commons.pool2.impl.GenericObjectPoolConfig;
public class ModbusConnectionPool {
private final GenericObjectPool<ModbusTcpClient> pool;
public ModbusConnectionPool(String host, int port, int maxConnections) {
GenericObjectPoolConfig<ModbusTcpClient> config =
new GenericObjectPoolConfig<>();
config.setMaxTotal(maxConnections);
config.setMaxIdle(maxConnections);
config.setMinIdle(2);
config.setTestOnBorrow(true);
pool = new GenericObjectPool<>(
new ModbusClientFactory(host, port),
config
);
}
public ModbusTcpClient borrowClient() throws Exception {
return pool.borrowObject();
}
public void returnClient(ModbusTcpClient client) {
pool.returnObject(client);
}
public void close() {
pool.close();
}
}
6.2 バッチ読み取り最適化
// 优化前:多次单独読み取り
for (int i = 0; i < 100; i++) {
short value = master.readHoldingRegister(slaveId, i).getValue();
}
// 优化后:一次バッチ読み取り
short[] values = master.readHoldingRegisters(slaveId, 0, 100).getValue();
6.3 タイムアウト設定优化
// 根据网络环境调整タイムアウト時間
master.setTimeout(500); // 本地网络
master.setTimeout(2000); // 远程网络
master.setRetries(3); // リトライ回数
七、セキュリティ与モニタリング
7.1 アクセス控制
package com.example.industrial.security;
import java.util.HashSet;
import java.util.Set;
public class ModbusAccessControl {
private final Set<Integer> allowedSlaves = new HashSet<>();
private final Set<Integer> readOnlyRegisters = new HashSet<>();
public void allowSlave(int slaveId) {
allowedSlaves.add(slaveId);
}
public boolean isSlaveAllowed(int slaveId) {
return allowedSlaves.contains(slaveId);
}
public void markReadOnly(int registerOffset) {
readOnlyRegisters.add(registerOffset);
}
public boolean isReadOnly(int registerOffset) {
return readOnlyRegisters.contains(registerOffset);
}
}
7.2 操作日志
package com.example.industrial.logging;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class ModbusOperationLogger {
private static final Logger logger = LoggerFactory.getLogger(
ModbusOperationLogger.class
);
public void logRead(int slaveId, int register, Object value) {
logger.info("READ | Slave={} Register={} Value={}",
slaveId, register, value);
}
public void logWrite(int slaveId, int register, Object value) {
logger.warn("WRITE | Slave={} Register={} Value={}",
slaveId, register, value);
}
public void logError(int slaveId, String operation, Exception e) {
logger.error("ERROR | Slave={} Operation={} Error={}",
slaveId, operation, e.getMessage());
}
}
八、実践案例:工业データの収集系统
8.1 系统架构
┌─────────────┐ ┌──────────────┐ ┌─────────────┐
│ PLC device │────▶│ Modbus ゲートウェイ │────▶│ Java 应用 │
│ (slave) │ │ (TCP/RTU) │ │ (master) │
└─────────────┘ └──────────────┘ └─────────────┘
│
▼
┌─────────────┐
│ データベースの種類 │
│ (MySQL) │
└─────────────┘
8.2 完全な実装代码
package com.example.industrial;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.annotation.Scheduled;
@SpringBootApplication
@EnableScheduling
public class IndustrialApplication {
public static void main(String[] args) {
SpringApplication.run(IndustrialApplication.class, args);
}
}
九、よくある質問与ソリューション
9.1 接続タイムアウト問題
問題: Modbus デバイスレスポンス慢导致タイムアウト
ソリューション:
master.setTimeout(2000); // タイムアウト時間の増加
master.setRetries(3); // 再試行回数の増加
9.2 データ不一致問題
問題: 読み取り的データ偶尔出现異常值
ソリューション:
// 追加データ验证
public short readValidatedRegister(int slaveId, int offset) {
short value = master.readHoldingRegister(slaveId, offset).getValue();
if (value < -1000 || value > 10000) {
throw new RuntimeException("データ超出合理範囲");
}
return value;
}
9.3 内存泄漏問題
問題: 长时间运行后内存占用持续增长
ソリューション:
// 定期清理缓存
@Scheduled(fixedRate = 300000) // 每 5 分
public void cleanupCache() {
cacheService.cleanup();
}
// 正确接続を閉じる
@PreDestroy
public void destroy() {
if (master != null) {
master.destroy();
}
}
十、总结与展望
10.1 Java Modbus 开发优势总结
- 跨プラットフォーム性:一次编写,到处运行
- エンタープライズレベルサポート:成熟的框架和库生态
- 性能优异:JVM 优化带来高いパフォーマンスを
- 可维护性:强タイプ、良好的代码组织
- 社区活跃:丰富的学习リソース和テクニカルサポート
10.2 技术选型建议
| Scenarios |
おすすめ库 |
理由 |
| エンタープライズレベル应用 |
Modbus4J |
功能包括的,稳定性好 |
| 微服务 |
j2mod |
轻量级,现代化 API |
| 学习/教学 |
jamod |
ドキュメント完善,サンプル丰富 |
| 高いパフォーマンスを |
Modbus4J + 连接池 |
サポート并发和池化 |
10.3 未来发展方向
- 云原生集成:与 Kubernetes、Docker 深度集成
- 边缘計算:在边缘デバイス上运行 Modbus 服务
- AI 融合:结合机器学习进行预测性维护
- セキュリティ增强:TLS/SSL 加密通信サポート
附录:快速参照
A.1 Maven 依赖速查
<!-- Modbus4J -->
<dependency>
<groupId>com.infiniteautomation</groupId>
<artifactId>modbus4j</artifactId>
<version>3.4.0</version>
</dependency>
<!-- j2mod -->
<dependency>
<groupId>com.ghgande</groupId>
<artifactId>j2mod</artifactId>
<version>3.1.4</version>
</dependency>
A.2 常用機能コード
| function code |
Function |
説明 |
| 01 |
リードコイルを読む |
読み取り开关量出力 |
| 02 |
read discrete inputs |
読み取り开关量入力 |
| 03 |
read holding registers |
読み取り模拟量出力 |
| 04 |
read input registers |
読み取り模拟量入力 |
| 05 |
write single coil |
控制开关量出力 |
| 06 |
write single register |
控制模拟量出力 |
| 15 |
write multiple coils |
バッチ制御 |
| 16 |
write multiple registers |
批量設定 |
A.3 リソース链接
- Modbus4J GitHub: https://github.com/serotonin/modbus4j
- j2mod GitHub: https://github.com/ghgande/j2mod
- Spring Boot: https://spring.io/projects/spring-boot
- Modbus 规范: https://modbus.org/specs.php
关键词: Java Modbus, Modbus4J, industrial IoT, エンタープライズレベル应用, Spring Boot, Data Acquisition, 自動化控制
字数: 约 8,500 字
Leave a Reply