基本环境
- SpringBoot:2.6.13
- PLC4X:0.10.0
Modbus模拟器:
ModbusPalmodbuspal.sourceforge.net/IoTClient Toolgitee.com/zhaopeiym/I…
ModbusPal使用参考 plc4x.apache.org/users/getti… 。
pom.xml
<dependency>
<groupId>org.apache.plc4x</groupId>
<artifactId>plc4j-api</artifactId>
<version>0.10.0</version>
</dependency>
<dependency>
<groupId>org.apache.plc4x</groupId>
<artifactId>plc4j-driver-modbus</artifactId>
<version>0.10.0</version>
<scope>runtime</scope>
</dependency>
其他依赖根据自己需要添加。
模拟一个Modbus slave
添加slave后并启动
再点击眼睛按钮编辑slave,选择Coils(线圈)添加10个地址,其value只能是0/1
读取线圈值
Coil指的是Modbus设备中用于控制开关状态的位数据。这些数据通常表示为可读写的线圈寄存器,可以用于控制诸如继电器和开关等设备。
Modbus地址参考:plc4x.apache.org/users/proto… 需要注意的点是:
官方文档少了一个:,所以正确的格式是:
{memory-Area}:{start-address}:{data-type}[{array-size}]
而且只能填coil,0/0x都会报是不合法的地址
同步读
// 0.10.0版本之前是 modbus://127.0.0.1:502
String connectionString = "modbus-tcp://127.0.0.1:502";
// 1.获取PlcConnection对象
PlcConnection plcConnection = new PlcDriverManager().getConnection(connectionString);
// 2.判断是否可以读取
boolean canRead = plcConnection.getMetadata().canRead();
if (!canRead) {
log.error("该连接不支持读取数据");
return;
}
// 3.创建读取请求
PlcReadRequest.Builder builder = plcConnection.readRequestBuilder();
// 第一个参数是地址别名,第二个参数是 Modbus 地址
// {memory-Area}:{start-address}:{data-type}[{array-size}]
builder.addItem("coil-1", "coil:1:BOOL[1]");
builder.addItem("coil-2", "coil:2:BOOL");
builder.addItem("coil-3", "coil:3");
builder.addItem("coil-4", "coil:4");
builder.addItem("coils-5-10", "coil:5[6]");
PlcReadRequest readRequest = builder.build();
PlcReadResponse response = readRequest.execute().get(5000, TimeUnit.MILLISECONDS);
for (String fieldName : response.getFieldNames()) {
if(response.getResponseCode(fieldName) == PlcResponseCode.OK) {
int numValues = response.getNumberOfValues(fieldName);
if(numValues == 1) {
log.info("Value[" + fieldName + "]: " + response.getObject(fieldName));
}
else {
log.info("Value[" + fieldName + "]:");
for(int i = 0; i < numValues; i++) {
log.info(" - " + response.getObject(fieldName, i));
}
}
}
else {
log.error("Error[" + fieldName + "]: " + response.getResponseCode(fieldName).name());
}
}
其中builder.addItem("coils-5-10", "coil:5[6]");是读取从地址5到10的数据
结果:
异步读
String connectionString = "modbus-tcp://127.0.0.1";
PlcConnection plcConnection = new PlcDriverManager().getConnection(connectionString);
boolean canRead = plcConnection.getMetadata().canRead();
if (!canRead) {
log.error("该连接不支持读取数据");
return;
}
PlcReadRequest.Builder builder = plcConnection.readRequestBuilder();
builder.addItem("coils", "coil:1[10]");
PlcReadRequest readRequest = builder.build();
CompletableFuture<? extends PlcReadResponse> asyncResponse = readRequest.execute();
asyncResponse.whenComplete((response, throwable) -> {
Boolean[] coilArray = new Boolean[10];
response.getAllBooleans("coils").toArray(coilArray);
for (int i = 0; i < coilArray.length; i++) {
log.info("Coil[" + i + "]: " + coilArray[i]);
}
});
System.out.println("这是异步读取,将会出现在打印值之前");
写线圈值
同步写
String connectionString = "modbus-tcp://127.0.0.1";
PlcConnection plcConnection = new PlcDriverManager().getConnection(connectionString);
boolean canWrite = plcConnection.getMetadata().canWrite();
if (!canWrite) {
log.error("该连接不支持写入数据");
return;
}
PlcWriteRequest.Builder builder = plcConnection.writeRequestBuilder();
builder.addItem("value-1", "coil:1", false);
PlcWriteRequest writeRequest = builder.build();
PlcWriteResponse response = writeRequest.execute().get();
for (String fieldName : response.getFieldNames()) {
PlcResponseCode responseCode = response.getResponseCode(fieldName);
if(responseCode == PlcResponseCode.OK) {
log.info("Value[" + fieldName + "]: updated");
}
else {
log.error("Error[" + fieldName + "]: " + responseCode.name());
}
}
结果是写入失败:
暂时没找到原因!!!
异步写
String connectionString = "modbus-tcp://127.0.0.1";
PlcConnection plcConnection = new PlcDriverManager().getConnection(connectionString);
boolean canWrite = plcConnection.getMetadata().canWrite();
if (!canWrite) {
log.error("该连接不支持写入数据");
return;
}
PlcWriteRequest.Builder builder = plcConnection.writeRequestBuilder();
builder.addItem("value-1", "coil:1", false);
PlcWriteRequest writeRequest = builder.build();
CompletableFuture<? extends PlcWriteResponse> asyncResponse = writeRequest.execute();
asyncResponse.whenComplete((response, throwable) -> {
for (String fieldName : response.getFieldNames()) {
PlcResponseCode responseCode = response.getResponseCode(fieldName);
if(responseCode == PlcResponseCode.OK) {
log.info("Value[" + fieldName + "]: updated");
}
else {
log.error("Error[" + fieldName + "]: " + responseCode.name());
}
}
});
System.out.println("这是异步读取,将会出现在打印值之前");
结果同同步写一样,暂时没找到原因。
读取Holding Register
Holding Register(保持寄存器):指的是Modbus设备中用于存储控制和状态数据的16位数据。这些数据通常表示为可读写的保持寄存器,用于保存设备的状态信息、设置参数等。
在操作Holding Register时得先在ModbusPal里加地址,不然会找不到。
同步读
只需要在addItem时调整一下即可:
builder.addItem("value-3", "holding-register:1");
builder.addItem("value-4", "holding-register:3[4]");
异步读
String connectionString = "modbus-tcp://127.0.0.1";
PlcConnection plcConnection = new PlcDriverManager().getConnection(connectionString);
boolean canRead = plcConnection.getMetadata().canRead();
if (!canRead) {
log.error("该连接不支持读取数据");
return;
}
PlcReadRequest.Builder builder = plcConnection.readRequestBuilder();
builder.addItem("values", "holding-register:1[10]");
PlcReadRequest readRequest = builder.build();
CompletableFuture<? extends PlcReadResponse> asyncResponse = readRequest.execute();
asyncResponse.whenComplete((response, throwable) -> {
Short[] holdingRegisterArray = new Short[10];
response.getAllShorts("values").toArray(holdingRegisterArray);
for (int i = 0; i < holdingRegisterArray.length; i++) {
log.info("holding-register[" + i + "]: " + holdingRegisterArray[i]);
}
});
System.out.println("这是异步读取,将会出现在打印值之前");
}
写入Holding Register
同步写
同理在addItem时替换成:builder.addItem("value-1", "holding-register:1", 26);
异步写
String connectionString = "modbus-tcp://127.0.0.1";
PlcConnection plcConnection = new PlcDriverManager().getConnection(connectionString);
boolean canWrite = plcConnection.getMetadata().canWrite();
if (!canWrite) {
log.error("该连接不支持写入数据");
return;
}
PlcWriteRequest.Builder builder = plcConnection.writeRequestBuilder();
for (int i = 1; i <= 10; i++) {
builder.addItem("value-" + i, "holding-register:" + i + ":UINT[1]", 10 - i);
}
PlcWriteRequest writeRequest = builder.build();
CompletableFuture<? extends PlcWriteResponse> asyncResponse = writeRequest.execute();
asyncResponse.whenComplete((response, throwable) -> {
for (String fieldName : response.getFieldNames()) {
PlcResponseCode responseCode = response.getResponseCode(fieldName);
if (responseCode == PlcResponseCode.OK) {
log.info("Value[" + fieldName + "]: updated");
} else {
log.error("Error[" + fieldName + "]: " + responseCode.name());
}
}
});
System.out.println("这是异步读取,将会出现在打印值之前");