WorkJPanelScan.java 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570
  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.nio.charset.StandardCharsets;
  14. import java.util.Timer;
  15. import java.util.TimerTask;
  16. /**
  17. * 扫码面板(每台 PLC 一个,92 发 1、2 号枪,93 发 3 号枪,由 PLC 决定)
  18. * DB7010.0.0 请求扫码 / DB7010.0.1 扫码完成反馈 / DB7010.6(INT) 扫码枪号;0.2、2、4 为预留,不读不写
  19. * 枪号 0=未发;正常同一时间 92、93 只有一个非 0,PLC 收到扫码完成反馈后清零
  20. * 请求扫码时 PLC 给几号就触发几号枪,读到码(不校验长度)存入本 PLC 的正面扫码码
  21. * 92 扫的码给 OP130A-E 取,93 扫的码给 OP130F(93 DB7004)取
  22. * 推码给 PLC(pushAddrs 按枪号对应 S7String 地址):扫到码后留存 15s 写入,到期清空,独立定时器,失败只记日志
  23. */
  24. public class WorkJPanelScan extends JPanel {
  25. public static final Logger log = LoggerFactory.getLogger(WorkJPanelScan.class);
  26. public String oprno = "OP130";
  27. public String plcIp;
  28. public String plcName; // 92 / 93,用于界面和日志
  29. public String plcDb = "DB7010";
  30. public String gunAddr = "DB7010.6";
  31. public S7PLC s7PLC;
  32. public SnSlot slot;
  33. public Integer workWidth = 470;
  34. public Integer workHeight = 300;
  35. private boolean scanDone = false; // 本次请求已扫码成功,等 PLC 复位请求
  36. private String lastGunState = ""; // 上次读到的枪号状态,变化时才记日志
  37. public JButton status_menu;
  38. public JTextField productSnFeeding; // 扫码槽当前码
  39. public JTextField gunField; // 本次扫码使用的枪
  40. public JButton resetFeedingBtn;
  41. public JLabel feedinglabel; // 0.0 请求扫码
  42. public JLabel feedingmeslabel; // 0.1 扫码完成反馈
  43. public JLabel gunNoLabel; // DB7010.6 实时枪号
  44. // 手动输入(参考 auto3,模拟扫码枪),由 设置->扫码手动输入 开关控制显示
  45. public JLabel manualLabel;
  46. public JTextField manualSnField;
  47. public JButton manualSubmitBtn;
  48. public volatile String pendingManualSn = ""; // 待使用的手动码,下次请求扫码时代替扫码枪
  49. public ImageIcon imageGreen;
  50. public ImageIcon imageGray;
  51. public JButton signalBtn;
  52. public PlcSignalMonitor02 signalMonitor;
  53. public Timer oprnoTimer;
  54. // 推码给 PLC:S7String 布局 [最大长度][实际长度][字符...]
  55. public static final int PUSH_SN_LENGTH = 24;
  56. public static final long PUSH_HOLD_MS = 15000;
  57. public PushPort[] pushPorts;
  58. public Timer pushTimer;
  59. // 一个推码地址的状态,holdSn/holdUntil 由扫码线程写,其余仅推码定时器线程使用
  60. public static class PushPort {
  61. public final int gunNo;
  62. public final String addr; // 起始地址,如 DB7020.1034
  63. public final String lenAddr; // 实际长度字节,起始地址+1
  64. public volatile String holdSn = "";
  65. public volatile long holdUntil = 0;
  66. public String writtenSn = null; // 初始 null,登录后首次运行必定清零一次,清掉上次残留
  67. public JTextField echoField;
  68. PushPort(int gunNo, String addr) {
  69. this.gunNo = gunNo;
  70. this.addr = addr;
  71. int dot = addr.lastIndexOf('.');
  72. this.lenAddr = addr.substring(0, dot + 1) + (Integer.parseInt(addr.substring(dot + 1)) + 1);
  73. }
  74. }
  75. /**
  76. * @param plcIp PLC 地址
  77. * @param slot 扫到的码存入的码槽(92 给 92 正面机,93 给 93 DB7004 正面机)
  78. * @param pushAddrs 推码地址,第 i 个对应 i+1 号枪;不传则不推码
  79. */
  80. public WorkJPanelScan(String plcIp, SnSlot slot, String... pushAddrs) {
  81. this.setLayout(null);
  82. this.setBackground(Color.WHITE);
  83. this.plcIp = plcIp;
  84. this.plcName = plcIp.substring(plcIp.lastIndexOf('.') + 1);
  85. this.slot = slot;
  86. this.pushPorts = new PushPort[pushAddrs.length];
  87. for (int i = 0; i < pushAddrs.length; i++) {
  88. pushPorts[i] = new PushPort(i + 1, pushAddrs[i]);
  89. }
  90. s7PLC = new S7PLC(EPlcType.S1500, plcIp);
  91. s7PLC.setConnectTimeout(1000);
  92. s7PLC.setReceiveTimeout(1000);
  93. initUI();
  94. productSnFeeding.setText(slot.getSn());
  95. setMenuStatus("等待请求扫码", 0);
  96. startOprnoTimer();
  97. if (pushPorts.length > 0) {
  98. startPushTimer();
  99. }
  100. }
  101. private String tag() {
  102. return oprno + "扫码" + plcName;
  103. }
  104. private void initUI() {
  105. JLabel oprnoLabel = new JLabel(oprno);
  106. oprnoLabel.setHorizontalAlignment(SwingConstants.CENTER);
  107. oprnoLabel.setForeground(Color.BLACK);
  108. oprnoLabel.setFont(new Font("微软雅黑", Font.PLAIN, 20));
  109. oprnoLabel.setBounds(0, 0, workWidth / 5, 40);
  110. this.add(oprnoLabel);
  111. JLabel nameLabel = new JLabel(plcName + "扫码");
  112. nameLabel.setHorizontalAlignment(SwingConstants.CENTER);
  113. nameLabel.setForeground(Color.BLACK);
  114. nameLabel.setFont(new Font("微软雅黑", Font.PLAIN, 14));
  115. nameLabel.setBounds(0, 17, workWidth / 5, 40);
  116. this.add(nameLabel);
  117. status_menu = new JButton("等待请求扫码");
  118. status_menu.setBounds(workWidth / 5, 0, workWidth * 4 / 5, 40);
  119. status_menu.setForeground(Color.GREEN);
  120. status_menu.setFont(new Font("微软雅黑", Font.PLAIN, 16));
  121. status_menu.setBackground(Color.BLACK);
  122. this.add(status_menu);
  123. addRowLabel("扫码", 50);
  124. productSnFeeding = createField(50, workWidth * 3 / 5);
  125. productSnFeeding.addMouseListener(new MouseAdapter() {
  126. public void mouseClicked(MouseEvent e) {
  127. String sn = productSnFeeding.getText().trim();
  128. if (!sn.isEmpty()) {
  129. MesClient.createQRCode(sn, tag());
  130. }
  131. }
  132. });
  133. resetFeedingBtn = new JButton("清空");
  134. resetFeedingBtn.setFont(new Font("微软雅黑", Font.PLAIN, 22));
  135. resetFeedingBtn.setBounds(workWidth * 4 / 5 + 10, 50, workWidth / 5 - 10, 40);
  136. resetFeedingBtn.addActionListener(e -> {
  137. String sn = slot.getSn();
  138. if (sn.isEmpty()) {
  139. return;
  140. }
  141. if (confirm("确定清空扫码结果" + sn + "吗?\n请求扫码仍在时会重新扫码")) {
  142. resetScan();
  143. }
  144. });
  145. this.add(resetFeedingBtn);
  146. addRowLabel("扫码枪", 100);
  147. gunField = createField(100, workWidth * 4 / 5);
  148. gunField.setFont(new Font("微软雅黑", Font.PLAIN, 14));
  149. gunField.setText("未扫码");
  150. imageGreen = scaledIcon("/bg/green_dot.png");
  151. imageGray = scaledIcon("/bg/grey_dot.png");
  152. feedinglabel = createLight("请求扫码", 0, 150);
  153. feedingmeslabel = createLight("扫码完成反馈", workWidth / 3, 150);
  154. feedingmeslabel.addMouseListener(new MouseAdapter() {
  155. public void mouseClicked(MouseEvent e) {
  156. boolean ret = readBoolean(plcDb + ".0.1");
  157. if (confirm("确定修改" + plcName + "扫码完成反馈(" + plcDb + ".0.1)为" + !ret + "吗?")) {
  158. writeScanFinish(!ret);
  159. }
  160. }
  161. });
  162. // 实时显示 PLC 给的枪号(DB7010.6),每轮刷新
  163. gunNoLabel = new JLabel("PLC枪号:-");
  164. gunNoLabel.setHorizontalAlignment(SwingConstants.LEFT);
  165. gunNoLabel.setFont(new Font("微软雅黑", Font.BOLD, 14));
  166. gunNoLabel.setForeground(Color.DARK_GRAY);
  167. gunNoLabel.setBounds(workWidth * 2 / 3, 150, workWidth / 3, 30);
  168. this.add(gunNoLabel);
  169. // 手动输入行
  170. manualLabel = new JLabel("手动");
  171. manualLabel.setHorizontalAlignment(SwingConstants.CENTER);
  172. manualLabel.setForeground(Color.BLACK);
  173. manualLabel.setFont(new Font("微软雅黑", Font.PLAIN, 20));
  174. manualLabel.setBounds(0, 185, workWidth / 5, 40);
  175. this.add(manualLabel);
  176. manualSnField = new JTextField("");
  177. manualSnField.setHorizontalAlignment(SwingConstants.CENTER);
  178. manualSnField.setForeground(Color.BLACK);
  179. manualSnField.setFont(new Font("微软雅黑", Font.PLAIN, 16));
  180. manualSnField.setBounds(workWidth / 5, 185, workWidth * 3 / 5, 40);
  181. manualSnField.addActionListener(e -> submitManualSn()); // 回车提交
  182. this.add(manualSnField);
  183. manualSubmitBtn = new JButton("确认");
  184. manualSubmitBtn.setFont(new Font("微软雅黑", Font.PLAIN, 22));
  185. manualSubmitBtn.setBounds(workWidth * 4 / 5 + 10, 185, workWidth / 5 - 10, 40);
  186. manualSubmitBtn.addActionListener(e -> submitManualSn());
  187. this.add(manualSubmitBtn);
  188. setManualMode(MesClient.manualScanEnabled);
  189. // 回显行:每个推码地址一个框,显示从 PLC 读回的值
  190. int footerY = 230;
  191. if (pushPorts.length > 0) {
  192. JLabel echoLabel = new JLabel("回显");
  193. echoLabel.setHorizontalAlignment(SwingConstants.CENTER);
  194. echoLabel.setForeground(Color.BLACK);
  195. echoLabel.setFont(new Font("微软雅黑", Font.PLAIN, 20));
  196. echoLabel.setBounds(0, 230, workWidth / 5, 32);
  197. this.add(echoLabel);
  198. int w = (workWidth * 4 / 5 - 5 * (pushPorts.length - 1)) / pushPorts.length;
  199. for (int i = 0; i < pushPorts.length; i++) {
  200. PushPort p = pushPorts[i];
  201. p.echoField = new JTextField("");
  202. p.echoField.setHorizontalAlignment(SwingConstants.CENTER);
  203. p.echoField.setForeground(Color.BLACK);
  204. p.echoField.setFont(new Font("微软雅黑", Font.PLAIN, 12));
  205. p.echoField.setEditable(false);
  206. p.echoField.setToolTipText(p.gunNo + "号枪 → " + p.addr);
  207. p.echoField.setBounds(workWidth / 5 + i * (w + 5), 230, w, 32);
  208. this.add(p.echoField);
  209. }
  210. footerY = 268;
  211. }
  212. JLabel ipLabel = new JLabel("PLC " + plcIp + " | " + plcDb + " | 枪号 " + gunAddr);
  213. ipLabel.setFont(new Font("微软雅黑", Font.PLAIN, 12));
  214. ipLabel.setForeground(Color.DARK_GRAY);
  215. ipLabel.setBounds(5, footerY, workWidth * 2 / 3 + 55, 26);
  216. this.add(ipLabel);
  217. signalMonitor = new PlcSignalMonitor02(s7PLC, plcDb, gunAddr, tag(), MesClient.mesClientFrame);
  218. signalBtn = signalMonitor.createSignalButton();
  219. signalBtn.setBounds(workWidth * 2 / 3 + 63, footerY, 90, 26);
  220. this.add(signalBtn);
  221. }
  222. private void addRowLabel(String text, int y) {
  223. JLabel label = new JLabel(text);
  224. label.setHorizontalAlignment(SwingConstants.CENTER);
  225. label.setForeground(Color.BLACK);
  226. label.setFont(new Font("微软雅黑", Font.PLAIN, 20));
  227. label.setBounds(0, y, workWidth / 5, 40);
  228. this.add(label);
  229. }
  230. private JTextField createField(int y, int width) {
  231. JTextField field = new JTextField("");
  232. field.setHorizontalAlignment(SwingConstants.CENTER);
  233. field.setForeground(Color.BLACK);
  234. field.setFont(new Font("微软雅黑", Font.PLAIN, 16));
  235. field.setEditable(false);
  236. field.setBounds(workWidth / 5, y, width, 40);
  237. this.add(field);
  238. return field;
  239. }
  240. private JLabel createLight(String text, int x, int y) {
  241. JLabel label = new JLabel(text);
  242. label.setHorizontalAlignment(SwingConstants.LEFT);
  243. label.setIcon(imageGray);
  244. label.setForeground(Color.BLACK);
  245. label.setBounds(x, y, workWidth / 3, 30);
  246. this.add(label);
  247. return label;
  248. }
  249. private static ImageIcon scaledIcon(String path) {
  250. ImageIcon icon = new ImageIcon(MesClient.class.getResource(path));
  251. icon.setImage(icon.getImage().getScaledInstance(20, 20, Image.SCALE_SMOOTH));
  252. return icon;
  253. }
  254. private static boolean confirm(String msg) {
  255. return JOptionPane.showConfirmDialog(null, msg, "操作确认", JOptionPane.YES_NO_OPTION,
  256. JOptionPane.QUESTION_MESSAGE) == JOptionPane.YES_OPTION;
  257. }
  258. public void setMenuStatus(String msg, int error) {
  259. status_menu.setForeground(error == 0 ? Color.GREEN : Color.RED);
  260. status_menu.setText(msg);
  261. }
  262. // 显示/隐藏手动输入行;关闭时丢弃未使用的手动码
  263. public void setManualMode(boolean enabled) {
  264. manualLabel.setVisible(enabled);
  265. manualSnField.setVisible(enabled);
  266. manualSubmitBtn.setVisible(enabled);
  267. if (enabled) {
  268. return;
  269. }
  270. manualSnField.setText("");
  271. if (!pendingManualSn.isEmpty()) {
  272. pendingManualSn = "";
  273. setMenuStatus("已关闭手动输入,未使用的手动码已丢弃", 0);
  274. }
  275. }
  276. // 提交手动码:非空即挂起,等下一次请求扫码时当作扫码结果
  277. public void submitManualSn() {
  278. String sn = manualSnField.getText().trim();
  279. if (sn.isEmpty()) {
  280. setMenuStatus("手动码不能为空", -1);
  281. return;
  282. }
  283. pendingManualSn = sn;
  284. manualSnField.setText("");
  285. log.info("<{}>手动输入工件码:{},操作人:{}", tag(), sn, MesClient.user_menu.getText());
  286. setMenuStatus("手动码已就绪,等待请求扫码:" + sn, 0);
  287. }
  288. // scan.ips 里配了这把枪即有效,PLC 发几号就用几号
  289. private boolean isValidGun(int no) {
  290. return no >= 1 && no <= MesClient.scanClients.length;
  291. }
  292. // 定时任务 -----------------------------------------------------------------------------------------------------
  293. public void startOprnoTimer() {
  294. if (oprnoTimer != null) {
  295. oprnoTimer.cancel();
  296. }
  297. oprnoTimer = new Timer();
  298. oprnoTimer.schedule(new TimerTask() {
  299. public void run() {
  300. try {
  301. if (MesClient.isLogin == 1) {
  302. cycle();
  303. }
  304. } catch (Exception e) {
  305. log.error("<{}>轮询异常", tag(), e);
  306. }
  307. }
  308. }, 100, 1500);
  309. }
  310. // 推码定时器(独立于扫码主流程):留存期内写码,到期清空,并读回显
  311. public void startPushTimer() {
  312. if (pushTimer != null) {
  313. pushTimer.cancel();
  314. }
  315. pushTimer = new Timer();
  316. pushTimer.schedule(new TimerTask() {
  317. public void run() {
  318. if (MesClient.isLogin != 1) {
  319. return;
  320. }
  321. for (PushPort p : pushPorts) {
  322. pushCycle(p);
  323. }
  324. }
  325. }, 1000, 500);
  326. }
  327. private void pushCycle(PushPort p) {
  328. try {
  329. String target = System.currentTimeMillis() <= p.holdUntil ? p.holdSn : "";
  330. if (!target.equals(p.writtenSn)) {
  331. if (target.isEmpty()) {
  332. // writeString 不支持空串:从实际长度字节起连同字符一起写 0,最大长度字节不动
  333. s7PLC.writeByte(p.lenAddr, new byte[1 + PUSH_SN_LENGTH]);
  334. } else {
  335. s7PLC.writeString(p.addr, target);
  336. }
  337. p.writtenSn = target;
  338. log.info("<{}>推码{}-->成功,写入内容:{}", tag(), p.addr, target.isEmpty() ? "清空" : target);
  339. }
  340. p.echoField.setText(readPushSn(p));
  341. } catch (Exception e) {
  342. p.echoField.setText("通讯失败");
  343. log.error("<{}>推码{}-->失败:{}", tag(), p.addr, e.getMessage());
  344. }
  345. }
  346. // 按原始字节读 S7String,实际长度为 0 时返回空串(不用 readString)
  347. private String readPushSn(PushPort p) {
  348. byte[] bs = s7PLC.readByte(p.addr, 2 + PUSH_SN_LENGTH);
  349. int len = Math.min(bs[1] & 0xFF, PUSH_SN_LENGTH);
  350. return new String(bs, 2, len, StandardCharsets.US_ASCII).trim();
  351. }
  352. // 留存扫到的码,只改内存不碰 PLC;没有对应推码地址的枪号不推
  353. private void holdPushSn(int gunNo, String sn) {
  354. if (gunNo < 1 || gunNo > pushPorts.length) {
  355. return;
  356. }
  357. PushPort p = pushPorts[gunNo - 1];
  358. p.holdSn = sn; // 先写码再写截止时间,定时器不会读到新时间配旧码
  359. p.holdUntil = System.currentTimeMillis() + PUSH_HOLD_MS;
  360. }
  361. private void cycle() {
  362. boolean retScan = readBoolean(plcDb + ".0.0");
  363. boolean retFinish = readBoolean(plcDb + ".0.1");
  364. feedinglabel.setIcon(retScan ? imageGreen : imageGray);
  365. feedingmeslabel.setIcon(retFinish ? imageGreen : imageGray);
  366. Integer plcGunNo = readGunNo();
  367. showGunNo(plcGunNo);
  368. // 显示扫码槽当前码,被正面机台取走后变空
  369. productSnFeeding.setText(slot.getSn());
  370. if (!retScan) {
  371. scanDone = false;
  372. if (retFinish) {
  373. writeScanFinish(false);
  374. }
  375. return;
  376. }
  377. if (scanDone) {
  378. if (!retFinish) {
  379. writeScanFinish(true);
  380. }
  381. return;
  382. }
  383. doScan(plcGunNo);
  384. }
  385. // 刷新界面上的 PLC 枪号:0=未发(灰),1-3 绿色,其他值/读不到红色
  386. private void showGunNo(Integer no) {
  387. if (no == null) {
  388. gunNoLabel.setForeground(Color.RED);
  389. gunNoLabel.setText("PLC枪号:读取失败");
  390. gunNoLabel.setToolTipText(gunAddr + " 读取失败,不扫码");
  391. return;
  392. }
  393. if (no == 0) {
  394. gunNoLabel.setForeground(Color.GRAY);
  395. gunNoLabel.setText("PLC枪号:0(未发)");
  396. gunNoLabel.setToolTipText("本PLC未发枪号");
  397. return;
  398. }
  399. boolean valid = isValidGun(no);
  400. gunNoLabel.setForeground(valid ? new Color(0, 128, 0) : Color.RED);
  401. gunNoLabel.setText("PLC枪号:" + no + (valid ? "" : "(无效)"));
  402. gunNoLabel.setToolTipText(valid ? no + "号枪 " + MesClient.scanClients[no - 1].serverIP : "有效范围 1-" + MesClient.scanClients.length);
  403. }
  404. private void doScan(Integer plcGunNo) {
  405. // 有手动码时优先使用并跳过本次扫码枪,用完即清(同 auto3)
  406. String manualSn = pendingManualSn;
  407. if (!manualSn.isEmpty()) {
  408. pendingManualSn = "";
  409. log.info("<{}>手动码执行:{}", tag(), manualSn);
  410. gunField.setText("手动输入");
  411. // 手动码按 PLC 当前枪号推码,枪号 0 或读不到则不推
  412. saveScan(manualSn, "手动输入(" + plcName + ")", plcGunNo == null ? 0 : plcGunNo);
  413. return;
  414. }
  415. // 读不到枪号不扫码
  416. if (plcGunNo == null) {
  417. gunField.setText("未读到枪号");
  418. setMenuStatus("读取" + gunAddr + "失败,不扫码", -1);
  419. return;
  420. }
  421. int gunNo = plcGunNo;
  422. // 0 = PLC 还没发枪号,等下一轮
  423. if (gunNo == 0) {
  424. setMenuStatus("请求扫码,等待PLC发枪号", 0);
  425. return;
  426. }
  427. if (!isValidGun(gunNo)) {
  428. gunField.setText("枪号 " + gunNo + " 无效");
  429. setMenuStatus("扫码枪号无效:" + gunNo + "(" + gunAddr + " 应为 1-" + MesClient.scanClients.length + ")", -1);
  430. return;
  431. }
  432. TCPClient client = MesClient.scanClients[gunNo - 1];
  433. gunField.setText(gunNo + "号枪 " + client.serverIP + ":" + client.serverPort);
  434. setMenuStatus(gunNo + "号枪扫码中...", 0);
  435. String sn = MesClient.scanQrcode(gunNo);
  436. if (sn.isEmpty()) {
  437. setMenuStatus(gunNo + "号枪扫码失败(未读到码),重试中", -1);
  438. return;
  439. }
  440. saveScan(sn, gunNo + "号枪(" + plcName + ")", gunNo);
  441. }
  442. // 存入正面扫码码并回写扫码完成反馈,同时留存推码
  443. private void saveScan(String sn, String source, int gunNo) {
  444. slot.put(sn, source);
  445. productSnFeeding.setText(sn);
  446. writeScanFinish(true);
  447. holdPushSn(gunNo, sn);
  448. scanDone = true;
  449. setMenuStatus("扫码成功" + (source.startsWith("手动") ? "(手动码)" : ""), 0);
  450. }
  451. public void resetScan() {
  452. slot.clear("人工清空(" + plcName + ")");
  453. productSnFeeding.setText("");
  454. scanDone = false;
  455. writeScanFinish(false);
  456. setMenuStatus("扫码结果已清空,等待请求扫码", 0);
  457. }
  458. // 与设备交互 -----------------------------------------------------------------------------------------
  459. // 读扫码枪号,读失败返回 null;每轮都读,只在值变化时记日志
  460. private Integer readGunNo() {
  461. Integer no = null;
  462. String err = "";
  463. try {
  464. no = (int) s7PLC.readInt16(gunAddr);
  465. } catch (Exception e) {
  466. err = e.getMessage();
  467. }
  468. String state = no == null ? "ERR" : String.valueOf(no);
  469. if (!state.equals(lastGunState)) {
  470. lastGunState = state;
  471. if (no == null) {
  472. log.warn("<{}>读取{}-->失败,不扫码:{}", tag(), gunAddr, err);
  473. } else {
  474. log.info("<{}>读取{}-->枪号变为:{}", tag(), gunAddr, no);
  475. }
  476. }
  477. return no;
  478. }
  479. public void writeScanFinish(boolean b) {
  480. writeBoolean(plcDb + ".0.1", b);
  481. }
  482. public boolean readBoolean(String addr) {
  483. try {
  484. return s7PLC.readBoolean(addr);
  485. } catch (Exception e) {
  486. log.error("<{}>读取{}-->失败:{}", tag(), addr, e.getMessage());
  487. return false;
  488. }
  489. }
  490. public boolean writeBoolean(String addr, boolean value) {
  491. try {
  492. s7PLC.writeBoolean(addr, value);
  493. log.info("<{}>写入{}-->成功,写入内容:{}", tag(), addr, value);
  494. return true;
  495. } catch (Exception e) {
  496. log.error("<{}>写入{}-->失败,写入内容:{},{}", tag(), addr, value, e.getMessage());
  497. return false;
  498. }
  499. }
  500. }