| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550 |
- package com.mes.component;
- import com.mes.ui.DataUtil;
- import com.mes.ui.MesClient;
- import com.mes.ui.Oprno;
- import org.slf4j.Logger;
- import org.slf4j.LoggerFactory;
- import javax.swing.*;
- import java.awt.*;
- import java.awt.event.ActionEvent;
- import java.awt.event.ActionListener;
- import java.awt.event.MouseAdapter;
- import java.awt.event.MouseEvent;
- import java.util.Timer;
- import java.util.TimerTask;
- /**
- * OP170 单台 CNC 面板:独立轮询、独立工件数据。
- * <p>
- * 状态:0 等待上料 → 1 质量检查 → 2 加工中等下料 → 3 已下料等 OK/NG → 4 已交结果并写下料反馈
- */
- public class MesClientComponent extends JPanel {
- public static final Logger log = LoggerFactory.getLogger(MesClientComponent.class);
- public Oprno oprnoClass = new Oprno();
- public JTextField productSn;
- public JButton status_menu;
- public static Integer workWidth = 255;
- public static Integer workHeight = 320;
- public JButton scanBtn;
- public JButton resetBtn;
- /**
- * 0:等待上料完成
- * 1:已读码,质量检查中
- * 2:上料完成反馈已写,加工中,等待下料完成
- * 3:已见下料,等待 OK/NG 并提交 MES
- * 4:结果已提交且下料反馈已写,等待信号复位
- */
- public Integer work_status = 0;
- /** 上料完成反馈(可手动点击翻转) */
- public JLabel sllabel1;
- /** 下料完成反馈(可手动点击翻转) */
- public JLabel sllabel2;
- /** 上料完成信号(仅展示,不可点击) */
- public JLabel sllabelLoad;
- /** 下料完成信号(仅展示,不可点击) */
- public JLabel sllabelUnload;
- /** 工位状态:正常/故障 */
- public JLabel stationStatusLabel;
- /** 加工结果展示 */
- public JLabel resultLabel;
- private boolean loadEdgeHandled = false;
- private boolean resultHandled = false;
- private boolean unloadEdgeHandled = false;
- private boolean lastLoadComplete = false;
- private boolean lastUnloadComplete = false;
- /** 本轮是否已发送过 AQDW,避免每秒重复发 */
- private boolean aqdwSent = false;
- /** 上一次工位状态(用于故障恢复后还原状态栏) */
- private Boolean lastStationOk = null;
- public MesClientComponent(Oprno opno) {
- oprnoClass = opno;
- int w = workWidth;
- int h = workHeight;
- this.setLayout(null);
- this.setBackground(Color.WHITE);
- this.setBorder(BorderFactory.createLineBorder(Color.GRAY));
- int margin = Math.max(8, w / 40);
- int hTitle = Math.max(36, (int) Math.round(h * 0.12));
- int hStatus = Math.max(32, (int) Math.round(h * 0.12));
- int hSn = Math.max(32, (int) Math.round(h * 0.12));
- int hBtn = Math.max(40, (int) Math.round(h * 0.12));
- int hLab = Math.max(28, (int) Math.round(h * 0.1));
- int hFeedback = Math.max(44, (int) Math.round(h * 0.14)); // 反馈行加高,保证圆点完整显示
- int gap = Math.max(4, margin / 2);
- int y = 0;
- JLabel oprnoLabel = new JLabel(displayOprno(oprnoClass.getOprno()));
- oprnoLabel.setHorizontalAlignment(SwingConstants.CENTER);
- oprnoLabel.setForeground(Color.BLACK);
- int titleFont = Math.max(16, Math.min(36, w / 11));
- oprnoLabel.setFont(new Font("微软雅黑", Font.PLAIN, titleFont));
- oprnoLabel.setBounds(0, y, w, hTitle);
- this.add(oprnoLabel);
- y += hTitle;
- status_menu = new JButton("等待上料完成");
- status_menu.setBounds(margin, y, w - 2 * margin, hStatus);
- status_menu.setForeground(Color.GREEN);
- status_menu.addActionListener(new ActionListener() {
- public void actionPerformed(ActionEvent e) {
- }
- });
- int statusFont = Math.max(14, Math.min(22, w / 16));
- status_menu.setFont(new Font("微软雅黑", Font.PLAIN, statusFont));
- status_menu.setBackground(Color.BLACK);
- this.add(status_menu);
- y += hStatus + gap;
- productSn = new JTextField("");
- // 过长时右对齐,尾部顶满格,开头被裁切,便于看清后缀
- productSn.setHorizontalAlignment(SwingConstants.RIGHT);
- productSn.setForeground(Color.BLACK);
- int snFont = Math.max(14, Math.min(22, w / 16));
- productSn.setFont(new Font("微软雅黑", Font.PLAIN, snFont));
- productSn.setEditable(false);
- productSn.setBounds(margin, y, w - 2 * margin, hSn);
- productSn.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));
- productSn.setToolTipText("点击显示二维码");
- productSn.setColumns(10);
- productSn.addMouseListener(new MouseAdapter() {
- @Override
- public void mousePressed(MouseEvent e) {
- // 与 op300 一致:可点;工控触摸屏上 pressed 比 clicked 更稳
- String sn = productSn.getText() == null ? "" : productSn.getText().trim();
- if (!sn.isEmpty()) {
- MesClient.createQRCode(sn, oprnoClass.getOprno());
- }
- }
- });
- this.add(productSn);
- y += hSn + gap;
- // 工位状态(station_status)暂不显示:现场点位未就绪时会一直报故障
- stationStatusLabel = new JLabel("");
- stationStatusLabel.setVisible(false);
- resultLabel = new JLabel("结果: --");
- resultLabel.setHorizontalAlignment(SwingConstants.CENTER);
- resultLabel.setFont(new Font("微软雅黑", Font.PLAIN, Math.max(12, w / 18)));
- resultLabel.setBounds(margin, y, w - 2 * margin, hLab);
- this.add(resultLabel);
- y += hLab + gap;
- scanBtn = new JButton("读码");
- final MesClientComponent thisComponent = this;
- scanBtn.addActionListener(new ActionListener() {
- public void actionPerformed(ActionEvent e) {
- thisComponent.scanBtnClick();
- }
- });
- try {
- ImageIcon raw = new ImageIcon(MesClient.class.getResource("/bg/scan_barcode.png"));
- Image scaled = raw.getImage().getScaledInstance(18, 18, Image.SCALE_SMOOTH);
- scanBtn.setIcon(new ImageIcon(scaled));
- } catch (Exception ignored) {
- }
- int btnFont = Math.max(14, Math.min(18, w / 14));
- scanBtn.setFont(new Font("微软雅黑", Font.PLAIN, btnFont));
- scanBtn.setMargin(new Insets(2, 4, 2, 4));
- scanBtn.setIconTextGap(4);
- int btnW = (w - 2 * margin - gap) / 2;
- scanBtn.setBounds(margin, y, btnW, hBtn);
- this.add(scanBtn);
- resetBtn = new JButton("刷新");
- resetBtn.addActionListener(new ActionListener() {
- public void actionPerformed(ActionEvent e) {
- resetBtnClick();
- }
- });
- try {
- ImageIcon raw = new ImageIcon(MesClient.class.getResource("/bg/reset_logo.png"));
- Image scaled = raw.getImage().getScaledInstance(18, 18, Image.SCALE_SMOOTH);
- resetBtn.setIcon(new ImageIcon(scaled));
- } catch (Exception ignored) {
- }
- resetBtn.setFont(new Font("微软雅黑", Font.PLAIN, btnFont));
- resetBtn.setMargin(new Insets(2, 4, 2, 4));
- resetBtn.setIconTextGap(4);
- resetBtn.setBounds(margin + btnW + gap, y, btnW, hBtn);
- this.add(resetBtn);
- y += hBtn + gap;
- sllabel1 = new JLabel("上料完成反馈");
- sllabel1.setHorizontalAlignment(SwingConstants.CENTER);
- sllabel1.setVerticalAlignment(SwingConstants.CENTER);
- sllabel1.setIcon(dot(false));
- sllabel1.setForeground(Color.BLACK);
- sllabel1.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));
- sllabel1.setToolTipText("点击手动翻转上料完成反馈信号");
- sllabel1.setBounds(0, y, w / 2, hFeedback);
- sllabel1.addMouseListener(new MouseAdapter() {
- @Override
- public void mouseClicked(MouseEvent e) {
- toggleFeedback(true);
- }
- });
- this.add(sllabel1);
- sllabel2 = new JLabel("下料完成反馈");
- sllabel2.setHorizontalAlignment(SwingConstants.CENTER);
- sllabel2.setVerticalAlignment(SwingConstants.CENTER);
- sllabel2.setIcon(dot(false));
- sllabel2.setForeground(Color.BLACK);
- sllabel2.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));
- sllabel2.setToolTipText("点击手动翻转下料完成反馈信号");
- sllabel2.setBounds(w / 2, y, w - w / 2, hFeedback);
- sllabel2.addMouseListener(new MouseAdapter() {
- @Override
- public void mouseClicked(MouseEvent e) {
- toggleFeedback(false);
- }
- });
- this.add(sllabel2);
- y += hFeedback + gap;
- // 上料/下料信号:只读展示 PLC→MES 完成位,不可点击
- sllabelLoad = new JLabel("上料信号");
- sllabelLoad.setHorizontalAlignment(SwingConstants.CENTER);
- sllabelLoad.setVerticalAlignment(SwingConstants.CENTER);
- sllabelLoad.setIcon(dot(false));
- sllabelLoad.setForeground(Color.BLACK);
- sllabelLoad.setToolTipText("上料完成信号(只读)");
- sllabelLoad.setBounds(0, y, w / 2, hFeedback);
- this.add(sllabelLoad);
- sllabelUnload = new JLabel("下料信号");
- sllabelUnload.setHorizontalAlignment(SwingConstants.CENTER);
- sllabelUnload.setVerticalAlignment(SwingConstants.CENTER);
- sllabelUnload.setIcon(dot(false));
- sllabelUnload.setForeground(Color.BLACK);
- sllabelUnload.setToolTipText("下料完成信号(只读)");
- sllabelUnload.setBounds(w / 2, y, w - w / 2, hFeedback);
- this.add(sllabelUnload);
- this.startOprnoTimer();
- }
- /** 面板标题:MES工位号 + CNC序号 */
- private static String displayOprno(String oprno) {
- if ("OP170D".equals(oprno)) {
- return "OP170D / CNC4";
- }
- if ("OP170E".equals(oprno)) {
- return "OP170E / CNC5";
- }
- if ("OP170F".equals(oprno)) {
- return "OP170F / CNC6";
- }
- return oprno;
- }
- private ImageIcon dot(boolean on) {
- try {
- String path = on ? "/bg/green_dot.png" : "/bg/grey_dot.png";
- return new ImageIcon(MesClient.class.getResource(path));
- } catch (Exception e) {
- return null;
- }
- }
- /** 手动翻转上料/下料完成反馈(需输入操作密码) */
- private void toggleFeedback(boolean load) {
- if (!MesClient.verifyManualPlcPassword(this)) {
- return;
- }
- try {
- String addr = load ? oprnoClass.getLoad_feedback() : oprnoClass.getUnload_feedback();
- boolean cur = MesClient.s7PLC.readBoolean(addr);
- boolean next = !cur;
- MesClient.s7PLC.writeBoolean(addr, next);
- if (load) {
- sllabel1.setIcon(dot(next));
- setMenuStatus("手动设置上料完成反馈=" + next, 0);
- } else {
- sllabel2.setIcon(dot(next));
- setMenuStatus("手动设置下料完成反馈=" + next, 0);
- }
- } catch (Exception ex) {
- log.error("手动翻转反馈失败", ex);
- setMenuStatus("手动写 PLC 失败", -1);
- }
- }
- public void refreshFeedbackIcons() {
- try {
- boolean lf = MesClient.s7PLC.readBoolean(oprnoClass.getLoad_feedback());
- boolean uf = MesClient.s7PLC.readBoolean(oprnoClass.getUnload_feedback());
- boolean lc = MesClient.s7PLC.readBoolean(oprnoClass.getLoad_complete());
- boolean uc = MesClient.s7PLC.readBoolean(oprnoClass.getUnload_complete());
- sllabel1.setIcon(dot(lf));
- sllabel2.setIcon(dot(uf));
- sllabelLoad.setIcon(dot(lc));
- sllabelUnload.setIcon(dot(uc));
- } catch (Exception ignored) {
- }
- }
- /** 手动读本机二维码并进入质量检查(需输入操作密码) */
- public void scanBtnClick() {
- if (!MesClient.verifyManualPlcPassword(this)) {
- return;
- }
- if (work_status != 0 && work_status != 1) {
- status_menu.setText("工件加工中,请勿重复读码");
- return;
- }
- try {
- String sn = MesClient.s7PLC.readString(oprnoClass.getProduct_sn());
- if (sn == null || sn.trim().isEmpty()) {
- setMenuStatus("扫码结果为空", -1);
- return;
- }
- sn = sn.trim();
- productSn.setText(sn);
- work_status = 1;
- loadEdgeHandled = true;
- aqdwSent = false;
- setMenuStatus("已读取工件码,等待校验", 0);
- } catch (Exception e) {
- log.error("读取 PLC 工件码失败", e);
- setMenuStatus("读取 PLC 失败", -1);
- }
- }
- public Timer oprnoTimer;
- public void startOprnoTimer() {
- if (oprnoTimer != null) {
- oprnoTimer.cancel();
- }
- oprnoTimer = new Timer();
- oprnoTimer.schedule(new TimerTask() {
- public void run() {
- try {
- pollOnce();
- } catch (Exception e) {
- log.error(oprnoClass.getOprno() + " 轮询异常", e);
- }
- }
- }, 100, 1000);
- }
- private void pollOnce() {
- refreshFeedbackIcons();
- boolean loadComplete = false;
- boolean unloadComplete = false;
- try {
- loadComplete = MesClient.s7PLC.readBoolean(oprnoClass.getLoad_complete());
- unloadComplete = MesClient.s7PLC.readBoolean(oprnoClass.getUnload_complete());
- } catch (Exception e) {
- return;
- }
- // 上料完成下降沿:仅复位本轮软件标记(不清 PLC 反馈,由 PLC 自行复位)
- if (lastLoadComplete && !loadComplete) {
- loadEdgeHandled = false;
- }
- lastLoadComplete = loadComplete;
- // 下料完成下降沿:仅复位软件状态;已完成的一轮清码回到 0(不清 PLC 反馈)
- if (lastUnloadComplete && !unloadComplete) {
- unloadEdgeHandled = false;
- if (work_status == 4) {
- resetCycleSoft();
- }
- }
- lastUnloadComplete = unloadComplete;
- if (work_status == 0) {
- // 面板尚无码时按电平触发,避免 loadEdgeHandled 卡住后 PLC 已有上料完成+码却一直“等待上料完成”
- String curSn = productSn.getText();
- boolean noSnYet = curSn == null || curSn.trim().isEmpty();
- if (loadComplete && (!loadEdgeHandled || noSnYet)) {
- String sn = "";
- try {
- sn = MesClient.s7PLC.readString(oprnoClass.getProduct_sn());
- } catch (Exception e) {
- setMenuStatus("读二维码失败", -1);
- return;
- }
- if (sn == null || sn.trim().isEmpty()) {
- setMenuStatus("二维码为空", -1);
- return;
- }
- sn = sn.trim();
- productSn.setText(sn);
- resultLabel.setText("结果: --");
- resultLabel.setForeground(Color.BLACK);
- loadEdgeHandled = true;
- resultHandled = false;
- unloadEdgeHandled = false;
- aqdwSent = false;
- work_status = 1;
- setMenuStatus("已读码,质量检查中", 0);
- }
- } else if (work_status == 1) {
- String sn = productSn.getText();
- if (sn == null || sn.trim().isEmpty()) {
- setMenuStatus("读码结果为空", -1);
- productSn.setText("");
- work_status = 0;
- loadEdgeHandled = false;
- return;
- }
- if (!aqdwSent) {
- Boolean ok = DataUtil.checkQualityByGw(MesClient.nettyClient, sn, MesClient.user20, oprnoClass.getOprno());
- if (ok) {
- aqdwSent = true;
- setMenuStatus("质量检查已发送,等待回复", 0);
- } else {
- setMenuStatus("质量检查消息发送失败", -1);
- }
- }
- // 等 MesRevice 回包后切到 status=2
- } else if (work_status == 2) {
- // 加工中:先等下料完成,再进结果提交(不下料反馈)
- if (unloadComplete) {
- work_status = 3;
- setMenuStatus("已下料,等待工位 OK/NG", 0);
- } else {
- setMenuStatus("加工中,等待下料", 0);
- }
- } else if (work_status == 3) {
- // 已见下料:等 OK/NG → 提交 MES → 再写下料反馈
- try {
- boolean okBit = MesClient.s7PLC.readBoolean(oprnoClass.getProcess_result_ok());
- boolean ngBit = MesClient.s7PLC.readBoolean(oprnoClass.getProcess_result_ng());
- if (!resultHandled && (okBit || ngBit)) {
- String ret = ngBit ? "NG" : "OK";
- String sn = productSn.getText();
- Boolean sent = DataUtil.sendQualityByGW(MesClient.nettyClient, sn, ret, MesClient.user20, oprnoClass.getOprno());
- if (sent) {
- resultHandled = true;
- final String show = ret;
- SwingUtilities.invokeLater(() -> {
- resultLabel.setText("结果: " + show);
- resultLabel.setForeground("NG".equals(show) ? Color.RED : new Color(0, 128, 0));
- });
- setMenuStatus("结果已提交(" + ret + "),写下料反馈", 0);
- writeUnloadFeedback();
- } else {
- setMenuStatus("结果提交失败,请重试", -1);
- }
- }
- } catch (Exception e) {
- log.error("读取工位结果失败", e);
- }
- } else if (work_status == 4) {
- setMenuStatus("下料完成反馈已写,等待复位", 0);
- }
- }
- /** 结果已交 MES 后再写下料完成反馈 */
- private void writeUnloadFeedback() {
- try {
- MesClient.s7PLC.writeBoolean(oprnoClass.getUnload_feedback(), true);
- unloadEdgeHandled = true;
- work_status = 4;
- setMenuStatus("结果已提交,下料反馈已写", 0);
- refreshFeedbackIcons();
- } catch (Exception e) {
- log.error("写下料完成反馈失败", e);
- setMenuStatus("写下料完成反馈失败", -1);
- }
- }
- /** 质量检查通过后由 MesRevice 调用:写上料完成反馈 + MKSW 记开始时间 */
- public void onQualityPass(String sn) {
- try {
- MesClient.s7PLC.writeBoolean(oprnoClass.getLoad_feedback(), true);
- DataUtil.startWorkByGw(MesClient.nettyClient, sn, MesClient.user20, oprnoClass.getOprno());
- work_status = 2;
- setMenuStatus("上料完成反馈已写,加工中", 0);
- refreshFeedbackIcons();
- } catch (Exception e) {
- log.error("写上料完成反馈失败", e);
- setMenuStatus("写上料完成反馈失败", -1);
- }
- }
- public void onQualityFail(String msg) {
- aqdwSent = false;
- setMenuStatus(msg + ",自动重试中…", -1);
- // 不写上料反馈;aqdwSent 已清,下一轮轮询会再次发质量检查
- }
- /** 工位从故障恢复后,按当前流程阶段还原状态栏文案 */
- private void restoreStatusAfterFault() {
- switch (work_status) {
- case 0:
- setMenuStatus("等待上料完成", 0);
- break;
- case 1:
- setMenuStatus(aqdwSent ? "质量检查已发送,等待回复" : "已读码,质量检查中", 0);
- break;
- case 2:
- setMenuStatus("加工中,等待下料", 0);
- break;
- case 3:
- setMenuStatus("已下料,等待工位 OK/NG", 0);
- break;
- case 4:
- setMenuStatus("结果已提交,下料反馈已写", 0);
- break;
- default:
- break;
- }
- }
- private void resetCycleSoft() {
- productSn.setText("");
- resultLabel.setText("结果: --");
- resultLabel.setForeground(Color.BLACK);
- work_status = 0;
- loadEdgeHandled = false;
- unloadEdgeHandled = false;
- resultHandled = false;
- aqdwSent = false;
- setMenuStatus("等待上料完成", 0);
- }
- /** 刷新复位本机面板状态(需输入操作密码):清空 SN/结果,状态回到等待上料,允许重新跑一轮 */
- public void resetBtnClick() {
- if (!MesClient.verifyManualPlcPassword(this)) {
- return;
- }
- productSn.setText("");
- resultLabel.setText("结果: --");
- resultLabel.setForeground(Color.BLACK);
- work_status = 0;
- loadEdgeHandled = false;
- resultHandled = false;
- unloadEdgeHandled = false;
- aqdwSent = false;
- lastStationOk = null;
- setMenuStatus("等待上料完成", 0);
- }
- public void setMenuStatus(String msg, int error) {
- SwingUtilities.invokeLater(() -> {
- if (error == 0) {
- this.status_menu.setForeground(Color.GREEN);
- } else {
- this.status_menu.setForeground(Color.RED);
- }
- this.status_menu.setText(msg);
- });
- }
- }
|