|
@@ -1,361 +0,0 @@
|
|
|
-
|
|
|
-package com.github.zuihou.plc;
|
|
|
-import org.apache.plc4x.java.PlcDriverManager;
|
|
|
-import org.apache.plc4x.java.api.PlcConnection;
|
|
|
-import org.apache.plc4x.java.api.exceptions.PlcConnectionException;
|
|
|
-import org.apache.plc4x.java.api.exceptions.PlcRuntimeException;
|
|
|
-import org.apache.plc4x.java.api.messages.*;
|
|
|
-import org.apache.plc4x.java.api.types.PlcResponseCode;
|
|
|
-
|
|
|
-import java.util.HashMap;
|
|
|
-import java.util.Map;
|
|
|
-import java.util.concurrent.CompletableFuture;
|
|
|
-import java.util.concurrent.TimeUnit;
|
|
|
-
|
|
|
-/**
|
|
|
- * S7 PLC 通信工具类 (修复响应码问题)
|
|
|
- * 支持读写各种数字类型:INT, WORD, SHORT, DINT, REAL等
|
|
|
- */
|
|
|
-public class S7PlcHelper {
|
|
|
- private PlcConnection connection;
|
|
|
- private final String plcIp;
|
|
|
- private final int rack;
|
|
|
- private final int slot;
|
|
|
- private boolean isConnected = false;
|
|
|
-
|
|
|
- // 数据类型常量
|
|
|
- public static final String TYPE_BOOL = "BOOL";
|
|
|
- public static final String TYPE_BYTE = "BYTE";
|
|
|
- public static final String TYPE_WORD = "WORD";
|
|
|
- public static final String TYPE_DWORD = "DWORD";
|
|
|
- public static final String TYPE_INT = "INT";
|
|
|
- public static final String TYPE_DINT = "DINT";
|
|
|
- public static final String TYPE_REAL = "REAL";
|
|
|
- public static final String TYPE_STRING = "STRING";
|
|
|
-
|
|
|
- /**
|
|
|
- * 构造函数
|
|
|
- * @param plcIp PLC的IP地址
|
|
|
- * @param rack 机架号 (通常为0)
|
|
|
- * @param slot 槽号 (通常为1)
|
|
|
- */
|
|
|
- public S7PlcHelper(String plcIp, int rack, int slot) {
|
|
|
- this.plcIp = plcIp;
|
|
|
- this.rack = rack;
|
|
|
- this.slot = slot;
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * 连接到PLC
|
|
|
- * @throws PlcConnectionException 如果连接失败
|
|
|
- */
|
|
|
- public void connect() throws PlcConnectionException {
|
|
|
- if (isConnected) {
|
|
|
- return;
|
|
|
- }
|
|
|
-
|
|
|
- String connectionString = String.format(
|
|
|
- "s7://%s?remote-rack=%d&remote-slot=%d&controller-type=S7_1500",
|
|
|
- plcIp, rack, slot
|
|
|
- );
|
|
|
-
|
|
|
- connection = new PlcDriverManager().getConnection(connectionString);
|
|
|
- isConnected = true;
|
|
|
- System.out.println("成功连接到S7-1500 PLC: " + plcIp);
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * 断开与PLC的连接
|
|
|
- */
|
|
|
- public void disconnect() {
|
|
|
- if (isConnected && connection != null) {
|
|
|
- try {
|
|
|
- connection.close();
|
|
|
- isConnected = false;
|
|
|
- System.out.println("已断开与PLC的连接");
|
|
|
- } catch (Exception e) {
|
|
|
- System.err.println("断开连接时出错: " + e.getMessage());
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * 检查连接状态
|
|
|
- * @return 是否已连接
|
|
|
- */
|
|
|
- public boolean isConnected() {
|
|
|
- return isConnected;
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * 构建地址字符串
|
|
|
- * @param dbNumber DB块号
|
|
|
- * @param offset 偏移量
|
|
|
- * @param dataType 数据类型
|
|
|
- * @return 完整的地址字符串
|
|
|
- */
|
|
|
- private String buildAddress(int dbNumber, int offset, String dataType) {
|
|
|
- switch (dataType) {
|
|
|
- case TYPE_BOOL:
|
|
|
- // 对于BOOL类型,偏移量应包含字节和位偏移
|
|
|
- int byteOffset = offset / 8;
|
|
|
- int bitOffset = offset % 8;
|
|
|
- return String.format("%s%d.DBX%d.%d:%s", "%DB", dbNumber, byteOffset, bitOffset, TYPE_BOOL);
|
|
|
- case TYPE_BYTE:
|
|
|
- return String.format("%s%d.DBB%d:%s", "%DB", dbNumber, offset, TYPE_BYTE);
|
|
|
- case TYPE_WORD:
|
|
|
- return String.format("%s%d.DBW%d:%s", "%DB", dbNumber, offset, TYPE_WORD);
|
|
|
- case TYPE_DWORD:
|
|
|
- return String.format("%s%d.DBD%d:%s", "%DB", dbNumber, offset, TYPE_DWORD);
|
|
|
- case TYPE_INT:
|
|
|
- return String.format("%s%d.DBW%d:%s", "%DB", dbNumber, offset, TYPE_INT);
|
|
|
- case TYPE_DINT:
|
|
|
- return String.format("%s%d.DBD%d:%s", "%DB", dbNumber, offset, TYPE_DINT);
|
|
|
- case TYPE_REAL:
|
|
|
- return String.format("%s%d.DBD%d:%s", "%DB", dbNumber, offset, TYPE_REAL);
|
|
|
- case TYPE_STRING:
|
|
|
- return String.format("%s%d.DBB%d:STRING(%d)", "%DB", dbNumber, offset, 80); // 字符串格式修复
|
|
|
- default:
|
|
|
- throw new IllegalArgumentException("不支持的数据类型: " + dataType);
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * 检查读取响应是否成功
|
|
|
- * @param response 读取响应对象
|
|
|
- * @param fieldName 字段名称
|
|
|
- * @return 是否成功
|
|
|
- */
|
|
|
- private boolean isReadSuccess(PlcReadResponse response, String fieldName) {
|
|
|
- return response.getResponseCode(fieldName) == PlcResponseCode.OK;
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * 检查写入响应是否成功
|
|
|
- * @param response 写入响应对象
|
|
|
- * @param fieldName 字段名称
|
|
|
- * @return 是否成功
|
|
|
- */
|
|
|
- private boolean isWriteSuccess(PlcWriteResponse response, String fieldName) {
|
|
|
- return response.getResponseCode(fieldName) == PlcResponseCode.OK;
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * 读取单个值
|
|
|
- * @param dbNumber DB块号
|
|
|
- * @param offset 偏移量
|
|
|
- * @param dataType 数据类型
|
|
|
- * @return 读取到的值
|
|
|
- * @throws Exception 读取失败时抛出异常
|
|
|
- */
|
|
|
- public Object readValue(int dbNumber, int offset, String dataType) throws Exception {
|
|
|
- if (!isConnected) {
|
|
|
- throw new IllegalStateException("未连接到PLC");
|
|
|
- }
|
|
|
-
|
|
|
- String address = buildAddress(dbNumber, offset, dataType);
|
|
|
- System.out.println("生成地址:" + address);
|
|
|
- PlcReadRequest readRequest = connection.readRequestBuilder()
|
|
|
- .addItem("value", address)
|
|
|
- .build();
|
|
|
-
|
|
|
- PlcReadResponse response = readRequest.execute().get(5, TimeUnit.SECONDS);
|
|
|
-
|
|
|
- if (!isReadSuccess(response, "value")) {
|
|
|
- throw new Exception("读取失败: " + response.getResponseCode("value") +
|
|
|
- ", 地址: " + address);
|
|
|
- }
|
|
|
-
|
|
|
- switch (dataType) {
|
|
|
- case TYPE_BOOL:
|
|
|
- return response.getBoolean("value");
|
|
|
- case TYPE_BYTE:
|
|
|
- return response.getByte("value");
|
|
|
- case TYPE_WORD:
|
|
|
- return response.getInteger("value"); // WORD在Java中用int表示
|
|
|
- case TYPE_DWORD:
|
|
|
- return response.getLong("value"); // DWORD在Java中用long表示
|
|
|
- case TYPE_INT:
|
|
|
- return response.getShort("value");
|
|
|
- case TYPE_DINT:
|
|
|
- return response.getInteger("value");
|
|
|
- case TYPE_REAL:
|
|
|
- return response.getFloat("value");
|
|
|
- case TYPE_STRING:
|
|
|
- return response.getString("value");
|
|
|
- default:
|
|
|
- throw new IllegalArgumentException("不支持的数据类型: " + dataType);
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * 写入单个值
|
|
|
- * @param dbNumber DB块号
|
|
|
- * @param offset 偏移量
|
|
|
- * @param dataType 数据类型
|
|
|
- * @param value 要写入的值
|
|
|
- * @throws Exception 写入失败时抛出异常
|
|
|
- */
|
|
|
- public void writeValue(int dbNumber, int offset, String dataType, Object value) throws Exception {
|
|
|
- if (!isConnected) {
|
|
|
- throw new IllegalStateException("未连接到PLC");
|
|
|
- }
|
|
|
-
|
|
|
- String address = buildAddress(dbNumber, offset, dataType);
|
|
|
- PlcWriteRequest writeRequest = connection.writeRequestBuilder()
|
|
|
- .addItem("value", address, value)
|
|
|
- .build();
|
|
|
-
|
|
|
- PlcWriteResponse response = writeRequest.execute().get(5, TimeUnit.SECONDS);
|
|
|
-
|
|
|
- if (!isWriteSuccess(response, "value")) {
|
|
|
- throw new Exception("写入失败: " + response.getResponseCode("value") +
|
|
|
- ", 地址: " + address);
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * 批量读取多个值
|
|
|
- * @param addresses 地址映射 (key: 变量名, value: 地址字符串)
|
|
|
- * @return 读取结果映射 (key: 变量名, value: 读取到的值)
|
|
|
- * @throws Exception 读取失败时抛出异常
|
|
|
- */
|
|
|
- public Map<String, Object> batchRead(Map<String, String> addresses) throws Exception {
|
|
|
- if (!isConnected) {
|
|
|
- throw new IllegalStateException("未连接到PLC");
|
|
|
- }
|
|
|
-
|
|
|
- PlcReadRequest.Builder builder = connection.readRequestBuilder();
|
|
|
- for (Map.Entry<String, String> entry : addresses.entrySet()) {
|
|
|
- builder.addItem(entry.getKey(), entry.getValue());
|
|
|
- }
|
|
|
-
|
|
|
- PlcReadRequest readRequest = builder.build();
|
|
|
- PlcReadResponse response = readRequest.execute().get(10, TimeUnit.SECONDS);
|
|
|
-
|
|
|
- Map<String, Object> result = new HashMap<>();
|
|
|
- for (String fieldName : addresses.keySet()) {
|
|
|
- if (isReadSuccess(response, fieldName)) {
|
|
|
- result.put(fieldName, response.getObject(fieldName));
|
|
|
- } else {
|
|
|
- result.put(fieldName, null);
|
|
|
- }
|
|
|
- }
|
|
|
- return result;
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * 异步读取值
|
|
|
- * @param dbNumber DB块号
|
|
|
- * @param offset 偏移量
|
|
|
- * @param dataType 数据类型
|
|
|
- * @return CompletableFuture对象,包含读取结果
|
|
|
- */
|
|
|
- public CompletableFuture<Object> readValueAsync(int dbNumber, int offset, String dataType) {
|
|
|
- if (!isConnected) {
|
|
|
- return CompletableFuture.completedFuture(new IllegalStateException("未连接到PLC"));
|
|
|
- }
|
|
|
-
|
|
|
- String address = buildAddress(dbNumber, offset, dataType);
|
|
|
- PlcReadRequest readRequest = connection.readRequestBuilder()
|
|
|
- .addItem("value", address)
|
|
|
- .build();
|
|
|
-
|
|
|
- return readRequest.execute()
|
|
|
- .thenApply(response -> {
|
|
|
- if (!isReadSuccess(response, "value")) {
|
|
|
- throw new PlcRuntimeException("异步读取失败: " +
|
|
|
- response.getResponseCode("value") + ", 地址: " + address);
|
|
|
- }
|
|
|
-
|
|
|
- switch (dataType) {
|
|
|
- case TYPE_BOOL: return response.getBoolean("value");
|
|
|
- case TYPE_BYTE: return response.getByte("value");
|
|
|
- case TYPE_WORD: return response.getInteger("value");
|
|
|
- case TYPE_DWORD: return response.getLong("value");
|
|
|
- case TYPE_INT: return response.getShort("value");
|
|
|
- case TYPE_DINT: return response.getInteger("value");
|
|
|
- case TYPE_REAL: return response.getFloat("value");
|
|
|
- case TYPE_STRING: return response.getString("value");
|
|
|
- default: throw new PlcRuntimeException("不支持的数据类型: " + dataType);
|
|
|
- }
|
|
|
- });
|
|
|
- }
|
|
|
-
|
|
|
- // 以下为类型特定的便捷方法
|
|
|
- public int readInt(int dbNumber, int offset) throws Exception {
|
|
|
- return (int) readValue(dbNumber, offset, TYPE_INT);
|
|
|
- }
|
|
|
-
|
|
|
- public int readWord(int dbNumber, int offset) throws Exception {
|
|
|
- return (int) readValue(dbNumber, offset, TYPE_WORD);
|
|
|
- }
|
|
|
-
|
|
|
- public short readShort(int dbNumber, int offset) throws Exception {
|
|
|
- return (short) readValue(dbNumber, offset, TYPE_INT);
|
|
|
- }
|
|
|
-
|
|
|
- public int readDInt(int dbNumber, int offset) throws Exception {
|
|
|
- return (int) readValue(dbNumber, offset, TYPE_DINT);
|
|
|
- }
|
|
|
-
|
|
|
- public float readReal(int dbNumber, int offset) throws Exception {
|
|
|
- return (float) readValue(dbNumber, offset, TYPE_REAL);
|
|
|
- }
|
|
|
-
|
|
|
- public boolean readBool(int dbNumber, int byteOffset, int bitOffset) throws Exception {
|
|
|
- return (boolean) readValue(dbNumber, byteOffset * 8 + bitOffset, TYPE_BOOL);
|
|
|
- }
|
|
|
-
|
|
|
- public void writeInt(int dbNumber, int offset, int value) throws Exception {
|
|
|
- writeValue(dbNumber, offset, TYPE_INT, value);
|
|
|
- }
|
|
|
-
|
|
|
- public void writeWord(int dbNumber, int offset, int value) throws Exception {
|
|
|
- writeValue(dbNumber, offset, TYPE_WORD, value);
|
|
|
- }
|
|
|
-
|
|
|
- public void writeShort(int dbNumber, int offset, short value) throws Exception {
|
|
|
- writeValue(dbNumber, offset, TYPE_INT, value);
|
|
|
- }
|
|
|
-
|
|
|
- public void writeDInt(int dbNumber, int offset, int value) throws Exception {
|
|
|
- writeValue(dbNumber, offset, TYPE_DINT, value);
|
|
|
- }
|
|
|
-
|
|
|
- public void writeReal(int dbNumber, int offset, float value) throws Exception {
|
|
|
- writeValue(dbNumber, offset, TYPE_REAL, value);
|
|
|
- }
|
|
|
-
|
|
|
- public void writeBool(int dbNumber, int byteOffset, int bitOffset, boolean value) throws Exception {
|
|
|
- writeValue(dbNumber, byteOffset * 8 + bitOffset, TYPE_BOOL, value);
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * 测试示例
|
|
|
- */
|
|
|
- public static void main(String[] args) {
|
|
|
- // 创建连接助手实例
|
|
|
- S7PlcHelper plcHelper = new S7PlcHelper("192.168.10.200", 0, 0);
|
|
|
-
|
|
|
- try {
|
|
|
- // 连接到PLC
|
|
|
- plcHelper.connect();
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
- // 读取WORD值
|
|
|
- int wordValue = plcHelper.readWord(1, 2);
|
|
|
- System.out.println("DB1.DBW2 的WORD值: " + wordValue);
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
- } catch (Exception e) {
|
|
|
- System.err.println("PLC操作出错: " + e.getMessage());
|
|
|
- e.printStackTrace();
|
|
|
- } finally {
|
|
|
- // 断开连接
|
|
|
- plcHelper.disconnect();
|
|
|
- }
|
|
|
- }
|
|
|
-}
|