McPlcClient.java 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135
  1. package com.mes.plc;
  2. import com.github.xingshuangs.iot.protocol.melsec.model.McDeviceAddress;
  3. import com.github.xingshuangs.iot.protocol.melsec.model.McDeviceContent;
  4. import com.github.xingshuangs.iot.protocol.melsec.service.McNetwork;
  5. import org.slf4j.Logger;
  6. import org.slf4j.LoggerFactory;
  7. import java.util.Collections;
  8. /**
  9. * 三菱 MC 3E 协议封装:读写位软元件、读 D 区字符串
  10. * 地址写法:M205、D5000
  11. */
  12. public class McPlcClient {
  13. private static final Logger log = LoggerFactory.getLogger(McPlcClient.class);
  14. private final String ip;
  15. private final int port;
  16. private McNetwork mc;
  17. private volatile boolean connected = false;
  18. public McPlcClient(String ip, int port) {
  19. this.ip = ip;
  20. this.port = port;
  21. }
  22. public synchronized void connect() {
  23. if (connected) {
  24. return;
  25. }
  26. try {
  27. mc = new McNetwork(ip, port);
  28. mc.setPersistence(true);
  29. mc.setEnableReconnect(false);
  30. mc.setConnectTimeout(3000);
  31. mc.setReceiveTimeout(3000);
  32. mc.connect();
  33. connected = true;
  34. log.info("PLC 连接成功 " + ip + ":" + port);
  35. } catch (Exception e) {
  36. closeQuietly();
  37. connected = false;
  38. mc = null;
  39. log.info("PLC 连接失败 " + ip + ":" + port + " " + e.getMessage());
  40. }
  41. }
  42. public synchronized void disconnect() {
  43. closeQuietly();
  44. connected = false;
  45. mc = null;
  46. }
  47. public boolean isConnected() {
  48. return connected;
  49. }
  50. /** 读单个位,失败返回 false 并标记断开 */
  51. public synchronized boolean readBit(String address) {
  52. try {
  53. checkMc();
  54. McDeviceContent content = mc.readDeviceBatchInBit(McDeviceAddress.createBy(address, 1));
  55. return mc.getBooleansBy(content.getData()).get(0);
  56. } catch (Exception e) {
  57. onError("读 " + address, e);
  58. return false;
  59. }
  60. }
  61. /** 写单个位 */
  62. public synchronized boolean writeBit(String address, boolean value) {
  63. try {
  64. checkMc();
  65. McDeviceAddress addr = McDeviceAddress.createBy(address, 1);
  66. byte[] data = mc.getBytesBy(Collections.singletonList(value));
  67. mc.writeDeviceBatchInBit(new McDeviceContent(addr.getDeviceCode(), addr.getHeadDeviceNumber(), 1, data));
  68. return true;
  69. } catch (Exception e) {
  70. onError("写 " + address, e);
  71. return false;
  72. }
  73. }
  74. /**
  75. * 读 D 区字符串(ASCII/GBK),charCount 为字符数
  76. * 三菱 D 区每个字存 2 个字符且字节序相反,这里做高低字节交换
  77. */
  78. public synchronized String readString(String address, int charCount) {
  79. try {
  80. checkMc();
  81. int words = (charCount + 1) / 2;
  82. McDeviceContent content = mc.readDeviceBatchInWord(McDeviceAddress.createBy(address, words));
  83. byte[] raw = content.getData();
  84. byte[] swapped = new byte[raw.length];
  85. for (int i = 0; i + 1 < raw.length; i += 2) {
  86. swapped[i] = raw[i + 1];
  87. swapped[i + 1] = raw[i];
  88. }
  89. String s = new String(swapped, "GBK");
  90. int end = s.indexOf('\0');
  91. if (end >= 0) {
  92. s = s.substring(0, end);
  93. }
  94. return s.trim();
  95. } catch (Exception e) {
  96. onError("读 " + address, e);
  97. return "";
  98. }
  99. }
  100. private void checkMc() {
  101. if (!connected || mc == null) {
  102. throw new IllegalStateException("PLC 未连接");
  103. }
  104. }
  105. private void onError(String action, Exception e) {
  106. log.info("PLC " + action + " 失败: " + e.getMessage());
  107. // 通信异常后关闭旧连接,由外层重连
  108. closeQuietly();
  109. connected = false;
  110. mc = null;
  111. }
  112. private void closeQuietly() {
  113. try {
  114. if (mc != null) {
  115. mc.close();
  116. }
  117. } catch (Exception ignored) {
  118. }
  119. }
  120. }