WorkJPanelScan.java 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379
  1. package com.mes.component;
  2. import com.github.xingshuangs.iot.protocol.s7.enums.EPlcType;
  3. import com.github.xingshuangs.iot.protocol.s7.service.S7PLC;
  4. import com.mes.ui.MesClient;
  5. import com.mes.util.SnSlot;
  6. import com.mes.util.TCPClient;
  7. import org.slf4j.Logger;
  8. import org.slf4j.LoggerFactory;
  9. import javax.swing.*;
  10. import java.awt.*;
  11. import java.awt.event.MouseAdapter;
  12. import java.awt.event.MouseEvent;
  13. import java.util.Timer;
  14. import java.util.TimerTask;
  15. /**
  16. * 扫码面板(每台 PLC 一个,92 发 1、2 号枪,93 发 3 号枪,由 PLC 决定)
  17. * DB7010.0.0 请求扫码 / DB7010.0.1 扫码完成反馈 / DB7010.6(INT) 扫码枪号;0.2、2、4 为预留,不读不写
  18. * 枪号 0=未发;正常同一时间 92、93 只有一个非 0,PLC 收到扫码完成反馈后清零
  19. * 请求扫码时 PLC 给几号就触发几号枪,读到 16 位码存入正面扫码槽,正面机台请求上料时取用
  20. */
  21. public class WorkJPanelScan extends JPanel {
  22. public static final Logger log = LoggerFactory.getLogger(WorkJPanelScan.class);
  23. public static final int SN_LENGTH = 16;
  24. public String oprno = "OP130";
  25. public String plcIp;
  26. public String plcName; // 92 / 93,用于界面和日志
  27. public String plcDb = "DB7010";
  28. public String gunAddr = "DB7010.6";
  29. public S7PLC s7PLC;
  30. public SnSlot slot;
  31. public Integer workWidth = 470;
  32. public Integer workHeight = 300;
  33. private boolean scanDone = false; // 本次请求已扫码成功,等 PLC 复位请求
  34. private String lastGunState = ""; // 上次读到的枪号状态,变化时才记日志
  35. public JButton status_menu;
  36. public JTextField productSnFeeding; // 扫码槽当前码
  37. public JTextField gunField; // 本次扫码使用的枪
  38. public JButton resetFeedingBtn;
  39. public JLabel feedinglabel; // 0.0 请求扫码
  40. public JLabel feedingmeslabel; // 0.1 扫码完成反馈
  41. public JLabel gunNoLabel; // DB7010.6 实时枪号
  42. public ImageIcon imageGreen;
  43. public ImageIcon imageGray;
  44. public JButton signalBtn;
  45. public PlcSignalMonitor02 signalMonitor;
  46. public Timer oprnoTimer;
  47. public WorkJPanelScan(String plcIp) {
  48. this.setLayout(null);
  49. this.setBackground(Color.WHITE);
  50. this.plcIp = plcIp;
  51. this.plcName = plcIp.substring(plcIp.lastIndexOf('.') + 1);
  52. this.slot = MesClient.frontSlot;
  53. s7PLC = new S7PLC(EPlcType.S1500, plcIp);
  54. s7PLC.setConnectTimeout(1000);
  55. s7PLC.setReceiveTimeout(1000);
  56. initUI();
  57. productSnFeeding.setText(slot.getSn());
  58. setMenuStatus("等待请求扫码", 0);
  59. startOprnoTimer();
  60. }
  61. private String tag() {
  62. return oprno + "扫码" + plcName;
  63. }
  64. private void initUI() {
  65. JLabel oprnoLabel = new JLabel(oprno);
  66. oprnoLabel.setHorizontalAlignment(SwingConstants.CENTER);
  67. oprnoLabel.setForeground(Color.BLACK);
  68. oprnoLabel.setFont(new Font("微软雅黑", Font.PLAIN, 20));
  69. oprnoLabel.setBounds(0, 0, workWidth / 5, 40);
  70. this.add(oprnoLabel);
  71. JLabel nameLabel = new JLabel(plcName + "扫码");
  72. nameLabel.setHorizontalAlignment(SwingConstants.CENTER);
  73. nameLabel.setForeground(Color.BLACK);
  74. nameLabel.setFont(new Font("微软雅黑", Font.PLAIN, 14));
  75. nameLabel.setBounds(0, 17, workWidth / 5, 40);
  76. this.add(nameLabel);
  77. status_menu = new JButton("等待请求扫码");
  78. status_menu.setBounds(workWidth / 5, 0, workWidth * 4 / 5, 40);
  79. status_menu.setForeground(Color.GREEN);
  80. status_menu.setFont(new Font("微软雅黑", Font.PLAIN, 16));
  81. status_menu.setBackground(Color.BLACK);
  82. this.add(status_menu);
  83. addRowLabel("扫码", 50);
  84. productSnFeeding = createField(50, workWidth * 3 / 5);
  85. productSnFeeding.addMouseListener(new MouseAdapter() {
  86. public void mouseClicked(MouseEvent e) {
  87. String sn = productSnFeeding.getText().trim();
  88. if (!sn.isEmpty()) {
  89. MesClient.createQRCode(sn, tag());
  90. }
  91. }
  92. });
  93. resetFeedingBtn = new JButton("清空");
  94. resetFeedingBtn.setFont(new Font("微软雅黑", Font.PLAIN, 22));
  95. resetFeedingBtn.setBounds(workWidth * 4 / 5 + 10, 50, workWidth / 5 - 10, 40);
  96. resetFeedingBtn.addActionListener(e -> {
  97. String sn = slot.getSn();
  98. if (sn.isEmpty()) {
  99. return;
  100. }
  101. if (confirm("确定清空扫码结果" + sn + "吗?\n请求扫码仍在时会重新扫码")) {
  102. resetScan();
  103. }
  104. });
  105. this.add(resetFeedingBtn);
  106. addRowLabel("扫码枪", 100);
  107. gunField = createField(100, workWidth * 4 / 5);
  108. gunField.setFont(new Font("微软雅黑", Font.PLAIN, 14));
  109. gunField.setText("未扫码");
  110. imageGreen = scaledIcon("/bg/green_dot.png");
  111. imageGray = scaledIcon("/bg/grey_dot.png");
  112. feedinglabel = createLight("请求扫码", 0, 150);
  113. feedingmeslabel = createLight("扫码完成反馈", workWidth / 3, 150);
  114. feedingmeslabel.addMouseListener(new MouseAdapter() {
  115. public void mouseClicked(MouseEvent e) {
  116. boolean ret = readBoolean(plcDb + ".0.1");
  117. if (confirm("确定修改" + plcName + "扫码完成反馈(" + plcDb + ".0.1)为" + !ret + "吗?")) {
  118. writeScanFinish(!ret);
  119. }
  120. }
  121. });
  122. // 实时显示 PLC 给的枪号(DB7010.6),每轮刷新
  123. gunNoLabel = new JLabel("PLC枪号:-");
  124. gunNoLabel.setHorizontalAlignment(SwingConstants.LEFT);
  125. gunNoLabel.setFont(new Font("微软雅黑", Font.BOLD, 14));
  126. gunNoLabel.setForeground(Color.DARK_GRAY);
  127. gunNoLabel.setBounds(workWidth * 2 / 3, 150, workWidth / 3, 30);
  128. this.add(gunNoLabel);
  129. JLabel ipLabel = new JLabel("PLC " + plcIp + " | " + plcDb + " | 枪号 " + gunAddr);
  130. ipLabel.setFont(new Font("微软雅黑", Font.PLAIN, 12));
  131. ipLabel.setForeground(Color.DARK_GRAY);
  132. ipLabel.setBounds(5, 230, workWidth * 2 / 3 + 55, 26);
  133. this.add(ipLabel);
  134. signalMonitor = new PlcSignalMonitor02(s7PLC, plcDb, gunAddr, tag(), MesClient.mesClientFrame);
  135. signalBtn = signalMonitor.createSignalButton();
  136. signalBtn.setBounds(workWidth * 2 / 3 + 63, 230, 90, 26);
  137. this.add(signalBtn);
  138. }
  139. private void addRowLabel(String text, int y) {
  140. JLabel label = new JLabel(text);
  141. label.setHorizontalAlignment(SwingConstants.CENTER);
  142. label.setForeground(Color.BLACK);
  143. label.setFont(new Font("微软雅黑", Font.PLAIN, 20));
  144. label.setBounds(0, y, workWidth / 5, 40);
  145. this.add(label);
  146. }
  147. private JTextField createField(int y, int width) {
  148. JTextField field = new JTextField("");
  149. field.setHorizontalAlignment(SwingConstants.CENTER);
  150. field.setForeground(Color.BLACK);
  151. field.setFont(new Font("微软雅黑", Font.PLAIN, 16));
  152. field.setEditable(false);
  153. field.setBounds(workWidth / 5, y, width, 40);
  154. this.add(field);
  155. return field;
  156. }
  157. private JLabel createLight(String text, int x, int y) {
  158. JLabel label = new JLabel(text);
  159. label.setHorizontalAlignment(SwingConstants.LEFT);
  160. label.setIcon(imageGray);
  161. label.setForeground(Color.BLACK);
  162. label.setBounds(x, y, workWidth / 3, 30);
  163. this.add(label);
  164. return label;
  165. }
  166. private static ImageIcon scaledIcon(String path) {
  167. ImageIcon icon = new ImageIcon(MesClient.class.getResource(path));
  168. icon.setImage(icon.getImage().getScaledInstance(20, 20, Image.SCALE_SMOOTH));
  169. return icon;
  170. }
  171. private static boolean confirm(String msg) {
  172. return JOptionPane.showConfirmDialog(null, msg, "操作确认", JOptionPane.YES_NO_OPTION,
  173. JOptionPane.QUESTION_MESSAGE) == JOptionPane.YES_OPTION;
  174. }
  175. public void setMenuStatus(String msg, int error) {
  176. status_menu.setForeground(error == 0 ? Color.GREEN : Color.RED);
  177. status_menu.setText(msg);
  178. }
  179. // scan.ips 里配了这把枪即有效,PLC 发几号就用几号
  180. private boolean isValidGun(int no) {
  181. return no >= 1 && no <= MesClient.scanClients.length;
  182. }
  183. // 定时任务 -----------------------------------------------------------------------------------------------------
  184. public void startOprnoTimer() {
  185. if (oprnoTimer != null) {
  186. oprnoTimer.cancel();
  187. }
  188. oprnoTimer = new Timer();
  189. oprnoTimer.schedule(new TimerTask() {
  190. public void run() {
  191. try {
  192. if (MesClient.isLogin == 1) {
  193. cycle();
  194. }
  195. } catch (Exception e) {
  196. log.error("<{}>轮询异常", tag(), e);
  197. }
  198. }
  199. }, 100, 1500);
  200. }
  201. private void cycle() {
  202. boolean retScan = readBoolean(plcDb + ".0.0");
  203. boolean retFinish = readBoolean(plcDb + ".0.1");
  204. feedinglabel.setIcon(retScan ? imageGreen : imageGray);
  205. feedingmeslabel.setIcon(retFinish ? imageGreen : imageGray);
  206. Integer plcGunNo = readGunNo();
  207. showGunNo(plcGunNo);
  208. // 显示扫码槽当前码,被正面机台取走后变空
  209. productSnFeeding.setText(slot.getSn());
  210. if (!retScan) {
  211. scanDone = false;
  212. if (retFinish) {
  213. writeScanFinish(false);
  214. }
  215. return;
  216. }
  217. if (scanDone) {
  218. if (!retFinish) {
  219. writeScanFinish(true);
  220. }
  221. return;
  222. }
  223. doScan(plcGunNo);
  224. }
  225. // 刷新界面上的 PLC 枪号:0=未发(灰),1-3 绿色,其他值/读不到红色
  226. private void showGunNo(Integer no) {
  227. if (no == null) {
  228. gunNoLabel.setForeground(Color.RED);
  229. gunNoLabel.setText("PLC枪号:读取失败");
  230. gunNoLabel.setToolTipText(gunAddr + " 读取失败,不扫码");
  231. return;
  232. }
  233. if (no == 0) {
  234. gunNoLabel.setForeground(Color.GRAY);
  235. gunNoLabel.setText("PLC枪号:0(未发)");
  236. gunNoLabel.setToolTipText("本PLC未发枪号");
  237. return;
  238. }
  239. boolean valid = isValidGun(no);
  240. gunNoLabel.setForeground(valid ? new Color(0, 128, 0) : Color.RED);
  241. gunNoLabel.setText("PLC枪号:" + no + (valid ? "" : "(无效)"));
  242. gunNoLabel.setToolTipText(valid ? no + "号枪 " + MesClient.scanClients[no - 1].serverIP : "有效范围 1-" + MesClient.scanClients.length);
  243. }
  244. private void doScan(Integer plcGunNo) {
  245. // 读不到枪号不扫码
  246. if (plcGunNo == null) {
  247. gunField.setText("未读到枪号");
  248. setMenuStatus("读取" + gunAddr + "失败,不扫码", -1);
  249. return;
  250. }
  251. int gunNo = plcGunNo;
  252. // 0 = PLC 还没发枪号,等下一轮
  253. if (gunNo == 0) {
  254. setMenuStatus("请求扫码,等待PLC发枪号", 0);
  255. return;
  256. }
  257. if (!isValidGun(gunNo)) {
  258. gunField.setText("枪号 " + gunNo + " 无效");
  259. setMenuStatus("扫码枪号无效:" + gunNo + "(" + gunAddr + " 应为 1-" + MesClient.scanClients.length + ")", -1);
  260. return;
  261. }
  262. TCPClient client = MesClient.scanClients[gunNo - 1];
  263. gunField.setText(gunNo + "号枪 " + client.serverIP + ":" + client.serverPort);
  264. setMenuStatus(gunNo + "号枪扫码中...", 0);
  265. String sn = MesClient.scanQrcode(gunNo);
  266. if (sn.length() != SN_LENGTH) {
  267. String reason = sn.isEmpty() ? "未读到码" : "码长度" + sn.length() + "位";
  268. setMenuStatus(gunNo + "号枪扫码失败(" + reason + "),重试中", -1);
  269. return;
  270. }
  271. slot.put(sn, gunNo + "号枪(" + plcName + ")");
  272. productSnFeeding.setText(sn);
  273. writeScanFinish(true);
  274. scanDone = true;
  275. setMenuStatus("扫码成功", 0);
  276. }
  277. public void resetScan() {
  278. slot.clear("人工清空(" + plcName + ")");
  279. productSnFeeding.setText("");
  280. scanDone = false;
  281. writeScanFinish(false);
  282. setMenuStatus("扫码结果已清空,等待请求扫码", 0);
  283. }
  284. // 与设备交互 -----------------------------------------------------------------------------------------
  285. // 读扫码枪号,读失败返回 null;每轮都读,只在值变化时记日志
  286. private Integer readGunNo() {
  287. Integer no = null;
  288. String err = "";
  289. try {
  290. no = (int) s7PLC.readInt16(gunAddr);
  291. } catch (Exception e) {
  292. err = e.getMessage();
  293. }
  294. String state = no == null ? "ERR" : String.valueOf(no);
  295. if (!state.equals(lastGunState)) {
  296. lastGunState = state;
  297. if (no == null) {
  298. log.warn("<{}>读取{}-->失败,不扫码:{}", tag(), gunAddr, err);
  299. } else {
  300. log.info("<{}>读取{}-->枪号变为:{}", tag(), gunAddr, no);
  301. }
  302. }
  303. return no;
  304. }
  305. public void writeScanFinish(boolean b) {
  306. writeBoolean(plcDb + ".0.1", b);
  307. }
  308. public boolean readBoolean(String addr) {
  309. try {
  310. return s7PLC.readBoolean(addr);
  311. } catch (Exception e) {
  312. log.error("<{}>读取{}-->失败:{}", tag(), addr, e.getMessage());
  313. return false;
  314. }
  315. }
  316. public boolean writeBoolean(String addr, boolean value) {
  317. try {
  318. s7PLC.writeBoolean(addr, value);
  319. log.info("<{}>写入{}-->成功,写入内容:{}", tag(), addr, value);
  320. return true;
  321. } catch (Exception e) {
  322. log.error("<{}>写入{}-->失败,写入内容:{},{}", tag(), addr, value, e.getMessage());
  323. return false;
  324. }
  325. }
  326. }