PlcService.java 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224
  1. package com.mes.util;
  2. import com.github.xingshuangs.iot.protocol.s7.enums.EPlcType;
  3. import com.github.xingshuangs.iot.protocol.s7.service.S7PLC;
  4. import org.slf4j.Logger;
  5. import org.slf4j.LoggerFactory;
  6. import java.nio.charset.StandardCharsets;
  7. /**
  8. * 西门子 S7-200 SMART PLC 通信服务。
  9. * <p>
  10. * 使用 iot-communication 库的 S200_SMART 协议(支持 V 区原生地址,如 V2800.3、VB3000)。
  11. * s7connector 不支持 S7-200 SMART,请勿混用。
  12. */
  13. public class PlcService {
  14. public static final Logger log = LoggerFactory.getLogger(PlcService.class);
  15. private static S7PLC s7PLC;
  16. private static String plcIp = "192.168.2.1";
  17. private static int rack = 0;
  18. private static int slot = 0;
  19. private static volatile boolean connected = false;
  20. public static void setPlcIp(String ip) {
  21. if (ip != null && !ip.trim().isEmpty()) {
  22. plcIp = ip.trim();
  23. }
  24. }
  25. public static void setConnectionParams(int rackNo, int slotNo) {
  26. rack = rackNo;
  27. slot = slotNo;
  28. }
  29. public static String getPlcIp() {
  30. return plcIp;
  31. }
  32. public static boolean isConnected() {
  33. return connected;
  34. }
  35. public static synchronized boolean connect() {
  36. disconnect();
  37. try {
  38. s7PLC = new S7PLC(EPlcType.S200_SMART, plcIp, 102, rack, slot);
  39. s7PLC.setPersistence(true);
  40. s7PLC.setConnectTimeout(3000);
  41. s7PLC.setReceiveTimeout(3000);
  42. s7PLC.connect();
  43. s7PLC.readBoolean(addr(QmPlcPoint.STATUS_BYTE, QmPlcPoint.DATA_READY_BIT));
  44. connected = true;
  45. log.info("S7-200 SMART PLC连接成功: {} rack={} slot={}", plcIp, rack, slot);
  46. return true;
  47. } catch (Exception e) {
  48. connected = false;
  49. closeQuietly();
  50. log.info("S7-200 SMART PLC连接失败: {} - {}", plcIp, e.getMessage());
  51. return false;
  52. }
  53. }
  54. public static synchronized void disconnect() {
  55. connected = false;
  56. closeQuietly();
  57. s7PLC = null;
  58. }
  59. private static void closeQuietly() {
  60. if (s7PLC != null) {
  61. try {
  62. s7PLC.close();
  63. } catch (Exception ignored) {
  64. }
  65. }
  66. }
  67. public static synchronized boolean ensureConnected() {
  68. if (connected && s7PLC != null) {
  69. try {
  70. if (s7PLC.checkConnected()) {
  71. s7PLC.readBoolean(addr(QmPlcPoint.STATUS_BYTE, QmPlcPoint.DATA_READY_BIT));
  72. return true;
  73. }
  74. } catch (Exception e) {
  75. connected = false;
  76. }
  77. }
  78. return connect();
  79. }
  80. public static byte[] readStatusByte() {
  81. ensureConnectedOrThrow();
  82. return s7PLC.readByte("V" + QmPlcPoint.STATUS_BYTE, 1);
  83. }
  84. public static byte[] readMesDataBlock() {
  85. ensureConnectedOrThrow();
  86. return s7PLC.readByte("V" + QmPlcPoint.DATA_OFFSET, QmPlcPoint.DATA_LENGTH);
  87. }
  88. public static boolean isDataReady(byte[] statusByte) {
  89. return QmPlcPoint.readBool(statusByte, 0, QmPlcPoint.DATA_READY_BIT);
  90. }
  91. public static boolean readDataReady() {
  92. ensureConnectedOrThrow();
  93. return s7PLC.readBoolean(addr(QmPlcPoint.STATUS_BYTE, QmPlcPoint.DATA_READY_BIT));
  94. }
  95. public static boolean setCollectComplete(boolean complete) {
  96. return writeVBit(QmPlcPoint.STATUS_BYTE, QmPlcPoint.COLLECT_COMPLETE_BIT, complete);
  97. }
  98. /**
  99. * 上位机采集完成反馈:V2800.3 写1并保持1~3秒后复位为0。
  100. */
  101. public static boolean pulseCollectComplete() {
  102. if (!setCollectComplete(true)) {
  103. return false;
  104. }
  105. try {
  106. Thread.sleep(QmPlcPoint.COLLECT_COMPLETE_PULSE_MS);
  107. } catch (InterruptedException e) {
  108. Thread.currentThread().interrupt();
  109. log.info("V2800.3脉冲被中断");
  110. return false;
  111. }
  112. return setCollectComplete(false);
  113. }
  114. public static boolean isQrMatchComplete(byte[] statusByte) {
  115. return QmPlcPoint.readBool(statusByte, 0, QmPlcPoint.QR_MATCH_BIT);
  116. }
  117. public static boolean setMesConnected(boolean connectedFlag) {
  118. return writeVBit(QmPlcPoint.STATUS_BYTE, QmPlcPoint.MES_CONNECTED_BIT, connectedFlag);
  119. }
  120. public static String readMesData() {
  121. byte[] data = readMesDataBlock();
  122. if (data == null) {
  123. log.warn("读取VB3000失败: 返回null");
  124. return "";
  125. }
  126. log.info("读取VB3000完成, byteLength={}", data.length);
  127. return QmPlcPoint.readPlcString(data);
  128. }
  129. /**
  130. * 质量校验通过后,将扫码内容写入 VB3500(纯 ASCII 原始字符串,无长度字节前缀)。
  131. */
  132. public static boolean writeScanCode(String barcode) {
  133. if (!ensureConnected()) {
  134. return false;
  135. }
  136. if (barcode == null || barcode.trim().isEmpty()) {
  137. log.info("二维码为空,无法写入VB3500");
  138. return false;
  139. }
  140. try {
  141. String code = barcode.trim();
  142. byte[] codeBytes = code.getBytes(StandardCharsets.US_ASCII);
  143. byte[] buffer = new byte[QmPlcPoint.SCAN_CODE_LENGTH];
  144. int copyLen = Math.min(codeBytes.length, buffer.length);
  145. if (codeBytes.length > buffer.length) {
  146. log.info("二维码长度超过VB3500容量({}字节),已截断", QmPlcPoint.SCAN_CODE_LENGTH);
  147. }
  148. System.arraycopy(codeBytes, 0, buffer, 0, copyLen);
  149. s7PLC.writeByte("V" + QmPlcPoint.SCAN_CODE_OFFSET, buffer);
  150. log.info("S7-200 SMART 写入 VB3500 二维码(ASCII): {}", code);
  151. return true;
  152. } catch (Exception e) {
  153. connected = false;
  154. log.info("写入VB3500失败: {}", e.getMessage());
  155. return false;
  156. }
  157. }
  158. /** 复位 VB3500 扫码区 */
  159. public static boolean clearScanCode() {
  160. if (!ensureConnected()) {
  161. return false;
  162. }
  163. try {
  164. byte[] empty = new byte[QmPlcPoint.SCAN_CODE_LENGTH];
  165. s7PLC.writeByte("V" + QmPlcPoint.SCAN_CODE_OFFSET, empty);
  166. log.info("S7-200 SMART 已清空 VB3500");
  167. return true;
  168. } catch (Exception e) {
  169. connected = false;
  170. log.info("清空VB3500失败: {}", e.getMessage());
  171. return false;
  172. }
  173. }
  174. private static boolean writeVBit(int byteOffset, int bitOffset, boolean value) {
  175. if (!ensureConnected()) {
  176. return false;
  177. }
  178. try {
  179. String address = addr(byteOffset, bitOffset);
  180. s7PLC.writeBoolean(address, value);
  181. log.info("S7-200 SMART 写入 {} = {}", address, value);
  182. return true;
  183. } catch (Exception e) {
  184. connected = false;
  185. log.info("S7-200 SMART PLC写入失败: {}", e.getMessage());
  186. return false;
  187. }
  188. }
  189. private static String addr(int byteOffset, int bitOffset) {
  190. return "V" + byteOffset + "." + bitOffset;
  191. }
  192. private static void ensureConnectedOrThrow() {
  193. if (!ensureConnected()) {
  194. throw new IllegalStateException("PLC未连接");
  195. }
  196. }
  197. }