| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548 |
- package com.mes.component;
- import com.alibaba.fastjson2.JSON;
- import com.alibaba.fastjson2.JSONObject;
- import com.github.xingshuangs.iot.protocol.s7.enums.EPlcType;
- import com.github.xingshuangs.iot.protocol.s7.service.S7PLC;
- import com.mes.ui.CacheData;
- import com.mes.ui.DataUtil;
- import com.mes.ui.MesClient;
- import com.mes.util.JdbcUtils;
- import com.mes.util.SnSlot;
- import org.slf4j.Logger;
- import org.slf4j.LoggerFactory;
- import javax.swing.*;
- import java.awt.*;
- import java.awt.event.MouseAdapter;
- import java.awt.event.MouseEvent;
- import java.util.Timer;
- import java.util.TimerTask;
- /**
- * 总成CNC工位面板:OP130A-E 正面(PLC 92),OP135A-E 反面(PLC 93),A-E 对应 DB7000-DB7004
- *
- * 点位 DB700X:0.0 请求上料 / 0.1 请求下料 / 0.2 运行中 / 0.3 设备状态 / 0.4 MES允许上料
- * 0.5 MES允许下料 / 0.6 心跳 / 0.7 上料到设备;1.0、1.1、2、4 为预留,不读不写
- *
- * 上料:请求上料时从 sourceSlot 取码(正面=扫码槽,反面=反面待上料码),校验通过写 0.4
- * 进设备:0.7 上升沿或 0.2 运行中,码从「上料」移到「加工」
- * 下料:请求下料时上传结果;正面要等反面待上料码为空,上传成功后存码给反面,再写 0.5
- */
- public class WorkJPanelCnc extends JPanel {
- public static final Logger log = LoggerFactory.getLogger(WorkJPanelCnc.class);
- public static final String PLC_FRONT = "192.168.90.92"; // 正面 OP130
- public static final String PLC_BACK = "192.168.90.93"; // 反面 OP135
- private static final String[] DBS = {"DB7000", "DB7001", "DB7002", "DB7003", "DB7004"};
- private static final String[] CN_NO = {"一", "二", "三", "四", "五", "六", "七", "八", "九", "十"};
- public String oprno; // OP130 / OP135
- public String pos; // A-E
- public boolean front; // true=正面
- public String plcIp;
- public String plcDb;
- public String deviceName;
- public SnSlot sourceSlot; // 上料取码来源
- public SnSlot targetSlot; // 下料存码目标,反面为 null
- public S7PLC s7PLC;
- public Integer workWidth = 470;
- public Integer workHeight = 300;
- public Integer tjFeedingFlag = 0; // 0=未取码 1=已取码校验通过
- public Integer tjFlag = 0; // 0=空闲 1=已上料 2=加工中 3=加工结束已上传
- public Integer tjStatus = 0; // 下料上传标识 0=未上传 1=已上传
- public String lastSn = "";
- private boolean feedingReleased = true; // 请求上料复位过,才处理下一次上料
- private boolean lastToDevice = false; // 上一轮 0.7,用于取上升沿
- private boolean plcHeart = false;
- public JButton status_menu;
- public JTextField productSnFeeding; // 上料码
- public JTextField productSn; // 加工码
- public JButton resetFeedingBtn;
- public JButton resetBtn;
- public JLabel feedinglabel; // 0.0 请求上料
- public JLabel feedingmeslabel; // 0.4 MES允许上料
- public JLabel runlabel; // 0.2 运行中
- public JLabel blankinglabel; // 0.1 请求下料
- public JLabel blankingmeslabel; // 0.5 MES允许下料
- public JLabel feedingtodevicelabel; // 0.7 上料到设备
- public ImageIcon imageGreen;
- public ImageIcon imageGray;
- public JButton signalBtn;
- public PlcSignalMonitor signalMonitor;
- public CacheData cacheData;
- public String cacheName;
- private String lastCacheJson = "";
- public Timer oprnoTimer;
- public Timer heartTimer;
- public WorkJPanelCnc(String oprno, String pos) {
- this.setLayout(null);
- this.setBackground(Color.WHITE);
- this.oprno = oprno;
- this.pos = pos;
- this.front = "OP130".equals(oprno);
- int idx = pos.charAt(0) - 'A';
- plcDb = DBS[idx];
- plcIp = front ? PLC_FRONT : PLC_BACK;
- deviceName = "东侧" + CN_NO[front ? idx : idx + 5] + "号机";
- sourceSlot = front ? MesClient.frontSlot : MesClient.backSlot;
- targetSlot = front ? MesClient.backSlot : null;
- s7PLC = new S7PLC(EPlcType.S1500, plcIp);
- s7PLC.setReceiveTimeout(1000);
- s7PLC.setConnectTimeout(1000);
- cacheName = "cache_" + oprno + pos;
- cacheData = JdbcUtils.getCache(cacheName);
- if (cacheData == null) {
- cacheData = new CacheData();
- cacheData.setLastSn("");
- cacheData.setSn("");
- cacheData.setFeedingSn("");
- cacheData.setTjStatus(0);
- cacheData.setTjFlag(0);
- cacheData.setTjFeedingFlag(0);
- cacheData.setError(-1);
- cacheData.setMsg("");
- JdbcUtils.setCache(cacheName, JSON.toJSONString(cacheData));
- }
- initUI();
- formatElementUI();
- startOprnoTimer();
- heartData();
- }
- // 界面 -----------------------------------------------------------------------------------------------------
- private void initUI() {
- JLabel oprnoLabel = new JLabel(oprno + pos);
- oprnoLabel.setHorizontalAlignment(SwingConstants.CENTER);
- oprnoLabel.setForeground(Color.BLACK);
- oprnoLabel.setFont(new Font("微软雅黑", Font.PLAIN, 20));
- oprnoLabel.setBounds(0, 0, workWidth / 5, 40);
- this.add(oprnoLabel);
- JLabel nameLabel = new JLabel(deviceName);
- nameLabel.setHorizontalAlignment(SwingConstants.CENTER);
- nameLabel.setForeground(Color.BLACK);
- nameLabel.setFont(new Font("微软雅黑", Font.PLAIN, 14));
- nameLabel.setBounds(0, 17, workWidth / 5, 40);
- this.add(nameLabel);
- status_menu = new JButton("等待请求上料");
- status_menu.setBounds(workWidth / 5, 0, workWidth * 4 / 5, 40);
- status_menu.setForeground(Color.GREEN);
- status_menu.setFont(new Font("微软雅黑", Font.PLAIN, 16));
- status_menu.setBackground(Color.BLACK);
- this.add(status_menu);
- addRowLabel("上料", 50);
- productSnFeeding = createSnField(50);
- resetFeedingBtn = createResetBtn(50, () -> {
- String sn = productSnFeeding.getText().trim();
- if (confirm("确定刷新上料工件" + sn + "吗?\n刷新后该码不会退回待上料码")) {
- resetFeeding();
- }
- });
- addRowLabel("加工", 100);
- productSn = createSnField(100);
- resetBtn = createResetBtn(100, () -> {
- String sn = productSn.getText().trim();
- String tip = front ? "\n刷新后不上传该件结果,也不会存入反面待上料码" : "\n刷新后不上传该件结果";
- if (confirm("确定刷新加工工件" + sn + "吗?" + tip)) {
- resetBlanking("已手动清除加工工件");
- }
- });
- imageGreen = scaledIcon("/bg/green_dot.png");
- imageGray = scaledIcon("/bg/grey_dot.png");
- feedinglabel = createLight("请求上料", 0, 150);
- feedingmeslabel = createLight("MES允许上料", workWidth / 3, 150);
- runlabel = createLight("运行中", workWidth * 2 / 3, 150);
- blankinglabel = createLight("请求下料", 0, 190);
- blankingmeslabel = createLight("MES允许下料", workWidth / 3, 190);
- feedingtodevicelabel = createLight("上料到设备", workWidth * 2 / 3, 190);
- // 点击 MES 写入的灯可手动切换信号
- feedingmeslabel.addMouseListener(toggleSignal("MES允许上料", plcDb + ".0.4"));
- blankingmeslabel.addMouseListener(toggleSignal("MES允许下料", plcDb + ".0.5"));
- JLabel ipLabel = new JLabel("PLC " + plcIp + " | " + plcDb);
- ipLabel.setFont(new Font("微软雅黑", Font.PLAIN, 12));
- ipLabel.setForeground(Color.DARK_GRAY);
- ipLabel.setBounds(5, 230, workWidth * 2 / 3 + 55, 26);
- this.add(ipLabel);
- signalMonitor = new PlcSignalMonitor(s7PLC, plcDb, oprno + pos, MesClient.mesClientFrame);
- signalBtn = signalMonitor.createSignalButton();
- signalBtn.setBounds(workWidth * 2 / 3 + 63, 230, 90, 26);
- this.add(signalBtn);
- }
- private void addRowLabel(String text, int y) {
- JLabel label = new JLabel(text);
- label.setHorizontalAlignment(SwingConstants.CENTER);
- label.setForeground(Color.BLACK);
- label.setFont(new Font("微软雅黑", Font.PLAIN, 20));
- label.setBounds(0, y, workWidth / 5, 40);
- this.add(label);
- }
- private JTextField createSnField(int y) {
- JTextField field = new JTextField("");
- field.setHorizontalAlignment(SwingConstants.CENTER);
- field.setForeground(Color.BLACK);
- field.setFont(new Font("微软雅黑", Font.PLAIN, 16));
- field.setEditable(false);
- field.setBounds(workWidth / 5, y, workWidth * 3 / 5, 40);
- field.addMouseListener(new MouseAdapter() {
- public void mouseClicked(MouseEvent e) {
- String sn = field.getText().trim();
- if (!sn.isEmpty()) {
- MesClient.createQRCode(sn, oprno + pos);
- }
- }
- });
- this.add(field);
- return field;
- }
- private JButton createResetBtn(int y, Runnable action) {
- JButton btn = new JButton("刷新");
- btn.setFont(new Font("微软雅黑", Font.PLAIN, 22));
- btn.setBounds(workWidth * 4 / 5 + 10, y, workWidth / 5 - 10, 40);
- btn.addActionListener(e -> action.run());
- this.add(btn);
- return btn;
- }
- private JLabel createLight(String text, int x, int y) {
- JLabel label = new JLabel(text);
- label.setHorizontalAlignment(SwingConstants.LEFT);
- label.setIcon(imageGray);
- label.setForeground(Color.BLACK);
- label.setBounds(x, y, workWidth / 3, 30);
- this.add(label);
- return label;
- }
- private MouseAdapter toggleSignal(String name, String addr) {
- return new MouseAdapter() {
- public void mouseClicked(MouseEvent e) {
- boolean ret = readBoolean(addr);
- if (confirm("确定修改" + name + "(" + addr + ")为" + !ret + "吗?")) {
- writeBoolean(addr, !ret);
- }
- }
- };
- }
- private static ImageIcon scaledIcon(String path) {
- ImageIcon icon = new ImageIcon(MesClient.class.getResource(path));
- icon.setImage(icon.getImage().getScaledInstance(20, 20, Image.SCALE_SMOOTH));
- return icon;
- }
- private static boolean confirm(String msg) {
- return JOptionPane.showConfirmDialog(null, msg, "操作确认", JOptionPane.YES_NO_OPTION,
- JOptionPane.QUESTION_MESSAGE) == JOptionPane.YES_OPTION;
- }
- private ImageIcon light(boolean on) {
- return on ? imageGreen : imageGray;
- }
- public void formatElementUI() {
- cacheData = JdbcUtils.getCache(cacheName);
- if (cacheData == null) {
- return;
- }
- productSn.setText(nvl(cacheData.getSn()));
- productSnFeeding.setText(nvl(cacheData.getFeedingSn()));
- tjFlag = nvl(cacheData.getTjFlag(), 0);
- tjFeedingFlag = nvl(cacheData.getTjFeedingFlag(), 0);
- lastSn = nvl(cacheData.getLastSn());
- setMenuStatus(nvl(cacheData.getMsg()), nvl(cacheData.getError(), -1));
- this.repaint();
- }
- public void setMenuStatus(String msg, int error) {
- status_menu.setForeground(error == 0 ? Color.GREEN : Color.RED);
- status_menu.setText(msg);
- cacheData.setMsg(msg);
- cacheData.setError(error);
- cacheData.setTjFlag(tjFlag);
- cacheData.setTjFeedingFlag(tjFeedingFlag);
- cacheData.setTjStatus(tjStatus);
- cacheData.setLastSn(lastSn);
- cacheData.setSn(productSn.getText());
- cacheData.setFeedingSn(productSnFeeding.getText());
- // 内容没变不重复写库(等待取码时每轮都会刷新状态)
- String json = JSON.toJSONString(cacheData);
- if (!json.equals(lastCacheJson)) {
- JdbcUtils.setCache(cacheName, json);
- lastCacheJson = json;
- }
- }
- // 定时任务 -----------------------------------------------------------------------------------------------------
- public void heartData() {
- if (heartTimer != null) {
- heartTimer.cancel();
- }
- heartTimer = new Timer();
- heartTimer.schedule(new TimerTask() {
- public void run() {
- if (MesClient.isLogin == 1) {
- plcHeart = !plcHeart;
- writeBoolean(plcDb + ".0.6", plcHeart, false);
- }
- }
- }, 1000, 1000);
- }
- public void startOprnoTimer() {
- if (oprnoTimer != null) {
- oprnoTimer.cancel();
- }
- oprnoTimer = new Timer();
- oprnoTimer.schedule(new TimerTask() {
- public void run() {
- try {
- if (MesClient.isLogin == 1) {
- cycle();
- }
- } catch (Exception e) {
- log.error("<{}{}>轮询异常", oprno, pos, e);
- }
- }
- }, 100, 3000);
- }
- private void cycle() {
- boolean retFeeding = readBoolean(plcDb + ".0.0");
- boolean retBlanking = readBoolean(plcDb + ".0.1");
- boolean retRunning = readBoolean(plcDb + ".0.2");
- boolean retAllowFeeding = readBoolean(plcDb + ".0.4");
- boolean retAllowBlanking = readBoolean(plcDb + ".0.5");
- boolean retToDevice = readBoolean(plcDb + ".0.7");
- boolean toDeviceRise = retToDevice && !lastToDevice;
- lastToDevice = retToDevice;
- feedinglabel.setIcon(light(retFeeding));
- feedingmeslabel.setIcon(light(retAllowFeeding));
- runlabel.setIcon(light(retRunning));
- blankinglabel.setIcon(light(retBlanking));
- blankingmeslabel.setIcon(light(retAllowBlanking));
- feedingtodevicelabel.setIcon(light(retToDevice));
- // 请求上料撤销后复位 MES允许上料
- if (!retFeeding && retAllowFeeding) {
- writeAllowFeeding(false);
- }
- // ---------- 上料 ----------
- if (!retFeeding) {
- feedingReleased = true;
- }
- if (tjFeedingFlag == 0) {
- if (retFeeding && feedingReleased) {
- takeSn();
- }
- } else if (tjFlag == 0 && (toDeviceRise || retRunning)) {
- // 工件已进设备:码从上料移到加工
- tjFlag = 1;
- tjFeedingFlag = 0;
- productSn.setText(productSnFeeding.getText());
- productSnFeeding.setText("");
- feedingReleased = false; // 本次请求上料复位后才处理下一次,防止拿走下一件的码
- setMenuStatus("工件已上料到设备", 0);
- }
- // ---------- 下料 ----------
- // 加工码为空时直接放行下料(无码件或已上传完成)
- if (retBlanking && productSn.getText().isEmpty() && !retAllowBlanking) {
- writeAllowBlanking(true);
- }
- if (tjFlag == 0 && !retBlanking && retAllowBlanking) {
- writeAllowBlanking(false);
- }
- if (tjFlag == 1 && retRunning) {
- tjFlag = 2;
- writeAllowFeeding(false);
- writeAllowBlanking(false);
- setMenuStatus("工件加工中", 0);
- } else if (tjFlag == 2 && retBlanking) {
- finishWork();
- }
- }
- // 取码并校验:通过后清掉码槽里的码,写 MES允许上料
- private void takeSn() {
- // 同一个码校验失败后不重复请求,等人工刷新或码槽换成新码
- String failedSn = productSnFeeding.getText().trim();
- if (!failedSn.isEmpty() && (sourceSlot.isEmpty() || failedSn.equals(sourceSlot.getSn()))) {
- return;
- }
- if (!sourceSlot.tryLock()) {
- return; // 其他机台正在取码,下一轮再试
- }
- try {
- String sn = sourceSlot.getSn();
- if (sn.isEmpty()) {
- setMenuStatus(front ? "暂无扫码结果,等待扫码" : "反面待上料码为空,等待正面下料", -1);
- return;
- }
- productSnFeeding.setText(sn);
- lastSn = sn;
- JSONObject retObj = DataUtil.checkQuality(sn, oprno + pos, MesClient.user_menu.getText());
- if (!isOk(retObj)) {
- writeAllowFeeding(false);
- setMenuStatus(failMsg(retObj), -1);
- return;
- }
- sourceSlot.clearIf(sn, oprno + pos + "取码");
- tjFeedingFlag = 1;
- writeAllowFeeding(true);
- setMenuStatus("工件可以加工", 0);
- } finally {
- sourceSlot.unlock();
- }
- }
- // 请求下料:上传结果,正面成功后存码给反面
- private void finishWork() {
- String sn = productSn.getText().trim();
- if (targetSlot == null) {
- if (upload(sn)) {
- resetBlanking("工件结果已提交,等待下一件");
- writeAllowBlanking(true);
- }
- return;
- }
- if (!targetSlot.tryLock()) {
- return; // 反面正在取码或其他正面机正在存码,下一轮再试
- }
- try {
- if (!targetSlot.isEmpty()) {
- setMenuStatus("反面待上料码" + targetSlot.getSn() + "未取走,暂不允许下料", -1);
- return;
- }
- if (upload(sn)) {
- targetSlot.put(sn, oprno + pos);
- resetBlanking("结果已提交,码已存入反面待上料码");
- writeAllowBlanking(true);
- }
- } finally {
- targetSlot.unlock();
- }
- }
- // 上传加工结果,失败停在 tjFlag=3 等人工刷新(与原逻辑一致)
- private boolean upload(String sn) {
- tjFlag = 3;
- tjStatus = 1;
- setMenuStatus("工件加工结束,结果上传中", 0);
- log.info("<{}{}>上传加工结果 sn={}", oprno, pos, sn);
- JSONObject retObj = DataUtil.sendQuality(sn, oprno + pos, "OK", MesClient.user_menu.getText());
- if (isOk(retObj)) {
- return true;
- }
- setMenuStatus(failMsg(retObj), -1);
- return false;
- }
- public void resetBlanking(String msg) {
- tjFlag = 0;
- tjStatus = 0;
- productSn.setText("");
- setMenuStatus(msg, 0);
- }
- public void resetFeeding() {
- tjFeedingFlag = 0;
- productSnFeeding.setText("");
- writeAllowFeeding(false);
- setMenuStatus("等待请求上料", 0);
- }
- private static boolean isOk(JSONObject retObj) {
- return retObj != null && "true".equalsIgnoreCase(String.valueOf(retObj.get("result")));
- }
- private static String failMsg(JSONObject retObj) {
- if (retObj == null || retObj.get("result") == null) {
- return "请求失败,请重试";
- }
- String msg = retObj.getString("message");
- return msg == null || msg.isEmpty() ? "校验失败" : msg;
- }
- private static String nvl(String s) {
- return s == null ? "" : s;
- }
- private static int nvl(Integer v, int def) {
- return v == null ? def : v;
- }
- // 与设备交互 -----------------------------------------------------------------------------------------
- public void writeAllowFeeding(boolean b) {
- writeBoolean(plcDb + ".0.4", b);
- }
- public void writeAllowBlanking(boolean b) {
- writeBoolean(plcDb + ".0.5", b);
- }
- public boolean readBoolean(String addr) {
- try {
- boolean ret = s7PLC.readBoolean(addr);
- log.info("<{}{}>读取{}-->成功,结果:{}", oprno, pos, addr, ret);
- return ret;
- } catch (Exception e) {
- log.error("<{}{}>读取{}-->失败:{}", oprno, pos, addr, e.getMessage());
- return false;
- }
- }
- public boolean writeBoolean(String addr, boolean value) {
- return writeBoolean(addr, value, true);
- }
- // 心跳不打成功日志
- private boolean writeBoolean(String addr, boolean value, boolean logOk) {
- try {
- s7PLC.writeBoolean(addr, value);
- if (logOk) {
- log.info("<{}{}>写入{}-->成功,写入内容:{}", oprno, pos, addr, value);
- }
- return true;
- } catch (Exception e) {
- log.error("<{}{}>写入{}-->失败,写入内容:{},{}", oprno, pos, addr, value, e.getMessage());
- return false;
- }
- }
- }
|