|
|
@@ -50,6 +50,11 @@ public class WorkJPanelDevice extends JPanel {
|
|
|
|
|
|
public JLabel blankingmeslabel; // 下料MES反馈
|
|
|
|
|
|
+ /** 当班离线模式开关(仅内存,重启默认在线) */
|
|
|
+ public JLabel offlineModeLabel;
|
|
|
+ /** true=离线:跳过校验与服务端提交,仍读写 PLC */
|
|
|
+ public boolean offlineMode = false;
|
|
|
+
|
|
|
public JButton scanBtn;
|
|
|
|
|
|
public JTextField productSn;
|
|
|
@@ -72,9 +77,11 @@ public class WorkJPanelDevice extends JPanel {
|
|
|
|
|
|
public ImageIcon imageGray;
|
|
|
|
|
|
+ public ImageIcon imageRed;
|
|
|
+
|
|
|
public String plcIp = "192.168.15.201";
|
|
|
|
|
|
- /** OP190 拉铆枪 PLC(与机器人 PLC 分离) */
|
|
|
+ /** OP190 拉铆枪 PLC(一拖二共用一台 S1200:A=DB40/41,B=DB50/51) */
|
|
|
public String rivetingPlcIp = "192.168.0.200";
|
|
|
|
|
|
public String deviceIp = "192.168.88.99";
|
|
|
@@ -122,10 +129,11 @@ public class WorkJPanelDevice extends JPanel {
|
|
|
|
|
|
public JLabel oprnoLabel;
|
|
|
|
|
|
- // 拉铆预设数量文本框
|
|
|
+ // 拉铆 A/B 枪预设、完成数量(一拖二,同 LM-1-2)
|
|
|
public JTextField presetField;
|
|
|
- // 拉铆完成数量文本框
|
|
|
public JTextField finishField;
|
|
|
+ public JTextField presetFieldB;
|
|
|
+ public JTextField finishFieldB;
|
|
|
|
|
|
// OP190 冷板绑定
|
|
|
public JTextField productSnLb;
|
|
|
@@ -133,17 +141,23 @@ public class WorkJPanelDevice extends JPanel {
|
|
|
public JButton btnUnbindLb;
|
|
|
public boolean bindLbStatus = false;
|
|
|
private static final String LB_CRAFT = "400006";
|
|
|
- /** 测试开关:true=OP190 测试模式(固定工件码、模拟上料/运行、拉铆直接满16并走完工流程;测试完改回 false) */
|
|
|
+ /** 测试开关:true=OP190 测试模式(固定工件码、模拟上料/运行、双枪打满并走完工流程;测试完改回 false) */
|
|
|
private static final boolean OP190_TEST_SN_ENABLED = false;
|
|
|
private static final String OP190_TEST_SN = "000020022144-0100101426053100009";
|
|
|
- /** OP190 拉铆打满后是否已触发完工提交,防止重复提交 */
|
|
|
+ /** OP190 双枪都打满后是否已触发完工提交,防止重复提交 */
|
|
|
private volatile boolean op190RivetingComplete = false;
|
|
|
/** OP190 质量结果是否已异步提交(拉铆完成时提交,下料时不再重复) */
|
|
|
private volatile boolean op190QualitySubmitted = false;
|
|
|
+ /** 双枪均满后是否已关气,避免重复写 PLC */
|
|
|
+ private volatile boolean bothGunsPoweredOff = false;
|
|
|
|
|
|
- public int aMax = 16; // A枪预设总数
|
|
|
- public int sortA = 0; // A枪当前已读取的数量
|
|
|
- public int aFinish = 0; // A枪合格数量
|
|
|
+ /** A/B 枪预设(各 8 颗;同一台拉铆 PLC:A=DB40/41,B=DB50/51) */
|
|
|
+ public int aMax = 8;
|
|
|
+ public int bMax = 8;
|
|
|
+ public int sortA = 0;
|
|
|
+ public int sortB = 0;
|
|
|
+ public int aFinish = 0;
|
|
|
+ public int bFinish = 0;
|
|
|
|
|
|
// OP170
|
|
|
// public int[] toolNums = { 1,2,3,4,5};
|
|
|
@@ -447,49 +461,35 @@ public class WorkJPanelDevice extends JPanel {
|
|
|
}
|
|
|
});
|
|
|
this.add(blankingmeslabel);
|
|
|
+
|
|
|
+ offlineModeLabel = createIndicatorLabel("离线模式", indicatorFont);
|
|
|
+ offlineModeLabel.setBounds(contentX + colW * 2, y, colW, indicatorRowH);
|
|
|
+ offlineModeLabel.addMouseListener(new MouseAdapter() {
|
|
|
+ public void mouseClicked(MouseEvent e) {
|
|
|
+ toggleOfflineMode();
|
|
|
+ }
|
|
|
+ });
|
|
|
+ this.add(offlineModeLabel);
|
|
|
+ refreshOfflineModeUi();
|
|
|
y += indicatorRowH + gap;
|
|
|
|
|
|
- int rivetPanelH = 72;
|
|
|
- JPanel rivetPanel = new JPanel(new GridLayout(1, 4, 16, 0));
|
|
|
+ int rivetPanelH = 150;
|
|
|
+ JPanel rivetPanel = new JPanel(new GridLayout(1, 2, 20, 0));
|
|
|
rivetPanel.setBackground(new Color(248, 249, 252));
|
|
|
rivetPanel.setBorder(BorderFactory.createTitledBorder(
|
|
|
BorderFactory.createLineBorder(new Color(190, 195, 205)),
|
|
|
- "拉铆进度",
|
|
|
+ "拉铆进度(一拖二)",
|
|
|
TitledBorder.CENTER,
|
|
|
TitledBorder.TOP,
|
|
|
new Font("微软雅黑", Font.BOLD, 16),
|
|
|
new Color(70, 70, 70)));
|
|
|
|
|
|
- Font statLabelFont = new Font("微软雅黑", Font.PLAIN, 18);
|
|
|
- Font statValueFont = new Font("微软雅黑", Font.BOLD, 32);
|
|
|
+ Font gunTitleFont = new Font("微软雅黑", Font.BOLD, 18);
|
|
|
+ Font statLabelFont = new Font("微软雅黑", Font.PLAIN, 16);
|
|
|
+ Font statValueFont = new Font("微软雅黑", Font.BOLD, 28);
|
|
|
|
|
|
- JLabel lblPreset = new JLabel("预设数量", SwingConstants.CENTER);
|
|
|
- lblPreset.setFont(statLabelFont);
|
|
|
- lblPreset.setForeground(new Color(80, 80, 80));
|
|
|
- rivetPanel.add(lblPreset);
|
|
|
-
|
|
|
- presetField = new JTextField(String.valueOf(aMax));
|
|
|
- presetField.setFont(statValueFont);
|
|
|
- presetField.setEditable(false);
|
|
|
- presetField.setBorder(BorderFactory.createEmptyBorder());
|
|
|
- presetField.setBackground(new Color(248, 249, 252));
|
|
|
- presetField.setForeground(new Color(30, 100, 180));
|
|
|
- presetField.setHorizontalAlignment(SwingConstants.CENTER);
|
|
|
- rivetPanel.add(presetField);
|
|
|
-
|
|
|
- JLabel lblFinish = new JLabel("完成数量", SwingConstants.CENTER);
|
|
|
- lblFinish.setFont(statLabelFont);
|
|
|
- lblFinish.setForeground(new Color(80, 80, 80));
|
|
|
- rivetPanel.add(lblFinish);
|
|
|
-
|
|
|
- finishField = new JTextField("0");
|
|
|
- finishField.setFont(statValueFont);
|
|
|
- finishField.setEditable(false);
|
|
|
- finishField.setBorder(BorderFactory.createEmptyBorder());
|
|
|
- finishField.setBackground(new Color(248, 249, 252));
|
|
|
- finishField.setForeground(new Color(30, 130, 70));
|
|
|
- finishField.setHorizontalAlignment(SwingConstants.CENTER);
|
|
|
- rivetPanel.add(finishField);
|
|
|
+ rivetPanel.add(buildGunProgressPanel("A枪", gunTitleFont, statLabelFont, statValueFont, true));
|
|
|
+ rivetPanel.add(buildGunProgressPanel("B枪", gunTitleFont, statLabelFont, statValueFont, false));
|
|
|
|
|
|
rivetPanel.setBounds(contentX, y, contentW, rivetPanelH);
|
|
|
this.add(rivetPanel);
|
|
|
@@ -515,11 +515,71 @@ public class WorkJPanelDevice extends JPanel {
|
|
|
return field;
|
|
|
}
|
|
|
|
|
|
+ /** OP190 单枪进度子面板:标题 + 预设/完成 */
|
|
|
+ private JPanel buildGunProgressPanel(String title, Font titleFont, Font labelFont, Font valueFont, boolean gunA) {
|
|
|
+ JPanel gun = new JPanel(new BorderLayout(0, 6));
|
|
|
+ gun.setOpaque(false);
|
|
|
+ JLabel titleLbl = new JLabel(title, SwingConstants.CENTER);
|
|
|
+ titleLbl.setFont(titleFont);
|
|
|
+ titleLbl.setForeground(new Color(50, 50, 50));
|
|
|
+ gun.add(titleLbl, BorderLayout.NORTH);
|
|
|
+
|
|
|
+ JPanel stats = new JPanel(new GridLayout(2, 2, 8, 4));
|
|
|
+ stats.setOpaque(false);
|
|
|
+ JLabel lblPreset = new JLabel("预设", SwingConstants.CENTER);
|
|
|
+ lblPreset.setFont(labelFont);
|
|
|
+ lblPreset.setForeground(new Color(80, 80, 80));
|
|
|
+ stats.add(lblPreset);
|
|
|
+ JLabel lblFinish = new JLabel("完成", SwingConstants.CENTER);
|
|
|
+ lblFinish.setFont(labelFont);
|
|
|
+ lblFinish.setForeground(new Color(80, 80, 80));
|
|
|
+ stats.add(lblFinish);
|
|
|
+
|
|
|
+ JTextField preset = new JTextField(String.valueOf(gunA ? aMax : bMax));
|
|
|
+ preset.setFont(valueFont);
|
|
|
+ preset.setEditable(false);
|
|
|
+ preset.setBorder(BorderFactory.createEmptyBorder());
|
|
|
+ preset.setBackground(new Color(248, 249, 252));
|
|
|
+ preset.setForeground(new Color(30, 100, 180));
|
|
|
+ preset.setHorizontalAlignment(SwingConstants.CENTER);
|
|
|
+ stats.add(preset);
|
|
|
+
|
|
|
+ JTextField finish = new JTextField("0");
|
|
|
+ finish.setFont(valueFont);
|
|
|
+ finish.setEditable(false);
|
|
|
+ finish.setBorder(BorderFactory.createEmptyBorder());
|
|
|
+ finish.setBackground(new Color(248, 249, 252));
|
|
|
+ finish.setForeground(new Color(30, 130, 70));
|
|
|
+ finish.setHorizontalAlignment(SwingConstants.CENTER);
|
|
|
+ stats.add(finish);
|
|
|
+
|
|
|
+ if (gunA) {
|
|
|
+ presetField = preset;
|
|
|
+ finishField = finish;
|
|
|
+ } else {
|
|
|
+ presetFieldB = preset;
|
|
|
+ finishFieldB = finish;
|
|
|
+ }
|
|
|
+ gun.add(stats, BorderLayout.CENTER);
|
|
|
+ return gun;
|
|
|
+ }
|
|
|
+
|
|
|
private void initOp190IndicatorIcons() {
|
|
|
imageGreen = new ImageIcon(MesClient.class.getResource("/bg/green_dot.png"));
|
|
|
imageGreen.setImage(imageGreen.getImage().getScaledInstance(18, 18, Image.SCALE_SMOOTH));
|
|
|
imageGray = new ImageIcon(MesClient.class.getResource("/bg/grey_dot.png"));
|
|
|
imageGray.setImage(imageGray.getImage().getScaledInstance(18, 18, Image.SCALE_SMOOTH));
|
|
|
+ imageRed = createColorDotIcon(Color.RED, 18);
|
|
|
+ }
|
|
|
+
|
|
|
+ private ImageIcon createColorDotIcon(Color color, int size) {
|
|
|
+ java.awt.image.BufferedImage img = new java.awt.image.BufferedImage(size, size, java.awt.image.BufferedImage.TYPE_INT_ARGB);
|
|
|
+ Graphics2D g = img.createGraphics();
|
|
|
+ g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
|
|
|
+ g.setColor(color);
|
|
|
+ g.fillOval(1, 1, size - 2, size - 2);
|
|
|
+ g.dispose();
|
|
|
+ return new ImageIcon(img);
|
|
|
}
|
|
|
|
|
|
private JLabel createIndicatorLabel(String text, Font font) {
|
|
|
@@ -530,6 +590,52 @@ public class WorkJPanelDevice extends JPanel {
|
|
|
return label;
|
|
|
}
|
|
|
|
|
|
+ /** 密码确认后切换当班离线模式(仅内存,重启默认在线) */
|
|
|
+ private void toggleOfflineMode() {
|
|
|
+ String input = JOptionPane.showInputDialog(null, "请输入密码", "离线模式", JOptionPane.QUESTION_MESSAGE);
|
|
|
+ if (input == null) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ if (!"mes".equals(input.trim())) {
|
|
|
+ JOptionPane.showMessageDialog(null, "密码错误", "离线模式", JOptionPane.WARNING_MESSAGE);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ offlineMode = !offlineMode;
|
|
|
+ refreshOfflineModeUi();
|
|
|
+ log.info("{}{} 离线模式切换为: {}", oprno, pos, offlineMode);
|
|
|
+ if (offlineMode) {
|
|
|
+ setMenuStatus("[离线] 已开启,读码后不校验不提交", 0);
|
|
|
+ } else {
|
|
|
+ setMenuStatus("已切回在线模式", 0);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ private void refreshOfflineModeUi() {
|
|
|
+ if (offlineModeLabel == null) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ if (offlineMode) {
|
|
|
+ offlineModeLabel.setIcon(imageRed != null ? imageRed : imageGray);
|
|
|
+ offlineModeLabel.setForeground(Color.RED);
|
|
|
+ } else {
|
|
|
+ offlineModeLabel.setIcon(imageGray);
|
|
|
+ offlineModeLabel.setForeground(Color.BLACK);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ public void setMenuStatus(String msg, int error) {
|
|
|
+ String text = msg == null ? "" : msg;
|
|
|
+ if (offlineMode && !text.startsWith("[离线]")) {
|
|
|
+ text = "[离线] " + text;
|
|
|
+ }
|
|
|
+ if (error == 0) {
|
|
|
+ this.status_menu.setForeground(Color.GREEN);
|
|
|
+ } else {
|
|
|
+ this.status_menu.setForeground(Color.RED);
|
|
|
+ }
|
|
|
+ this.status_menu.setText(text);
|
|
|
+ }
|
|
|
+
|
|
|
/**
|
|
|
* 非 OP190 工位默认界面
|
|
|
*/
|
|
|
@@ -679,6 +785,7 @@ public class WorkJPanelDevice extends JPanel {
|
|
|
|
|
|
imageGray = new ImageIcon(MesClient.class.getResource("/bg/grey_dot.png"));
|
|
|
imageGray.setImage(imageGray.getImage().getScaledInstance((int)(20 * scale), (int)(20 * scale), Image.SCALE_SMOOTH));
|
|
|
+ imageRed = createColorDotIcon(Color.RED, (int)(20 * scale));
|
|
|
|
|
|
int feedingIndicatorY = (int)(120 * scale);
|
|
|
int blankingIndicatorY = (int)(160 * scale);
|
|
|
@@ -741,6 +848,20 @@ public class WorkJPanelDevice extends JPanel {
|
|
|
}
|
|
|
}
|
|
|
});
|
|
|
+
|
|
|
+ offlineModeLabel = new JLabel("离线模式");
|
|
|
+ offlineModeLabel.setHorizontalAlignment(SwingConstants.LEFT);
|
|
|
+ offlineModeLabel.setIcon(imageGray);
|
|
|
+ offlineModeLabel.setForeground(Color.BLACK);
|
|
|
+ offlineModeLabel.setFont(indicatorFont);
|
|
|
+ offlineModeLabel.setBounds(controlWidth*2/3, blankingIndicatorY, controlWidth/3, (int)(30 * scale));
|
|
|
+ offlineModeLabel.addMouseListener(new MouseAdapter() {
|
|
|
+ public void mouseClicked(MouseEvent e) {
|
|
|
+ toggleOfflineMode();
|
|
|
+ }
|
|
|
+ });
|
|
|
+ this.add(offlineModeLabel);
|
|
|
+ refreshOfflineModeUi();
|
|
|
}
|
|
|
|
|
|
private void showCheckDialog() {
|
|
|
@@ -769,15 +890,6 @@ public class WorkJPanelDevice extends JPanel {
|
|
|
System.out.println("Dialog opened");
|
|
|
}
|
|
|
|
|
|
- public void setMenuStatus(String msg,int error){
|
|
|
- if(error == 0){
|
|
|
- this.status_menu.setForeground(Color.GREEN);
|
|
|
- }else{
|
|
|
- this.status_menu.setForeground(Color.RED);
|
|
|
- }
|
|
|
- this.status_menu.setText(msg);
|
|
|
- }
|
|
|
-
|
|
|
// 定时任务 -----------------------------------------------------------------------------------------------------
|
|
|
|
|
|
public Timer rivetingTimer;
|
|
|
@@ -793,9 +905,9 @@ public class WorkJPanelDevice extends JPanel {
|
|
|
rivetingTimer.schedule(new TimerTask() {
|
|
|
@Override
|
|
|
public void run() {
|
|
|
- // 仅在已登录、非本地模式、已绑定冷板(OP190)且处于加工阶段时轮询
|
|
|
+ // 仅在已登录、非本地模式;OP190 需已绑冷板或离线模式
|
|
|
if (MesClient.isLogin == 1 && isLocal == 0 && tjFeedingFlag >= 1
|
|
|
- && (!oprno.equals("OP190") || bindLbStatus)) {
|
|
|
+ && (!oprno.equals("OP190") || bindLbStatus || offlineMode)) {
|
|
|
try {
|
|
|
pollRivetingData();
|
|
|
} catch (Exception e) {
|
|
|
@@ -814,111 +926,162 @@ public class WorkJPanelDevice extends JPanel {
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
- * 轮询读取拉铆实时数据
|
|
|
- * 获取拉力、行程等参数并保存至数据库
|
|
|
+ * 轮询读取双枪拉铆实时数据(一拖二:A=DB40/41,B=DB50/51,无复判)
|
|
|
*/
|
|
|
private void pollRivetingData() {
|
|
|
if (oprno.equals("OP190") && OP190_TEST_SN_ENABLED) {
|
|
|
- // 测试模式:绑定冷板进入加工后,拉铆完成数直接变为16并触发完工
|
|
|
if (tjFeedingFlag >= 2 && bindLbStatus) {
|
|
|
simulateOp190RivetingComplete();
|
|
|
}
|
|
|
return;
|
|
|
}
|
|
|
if (getRivetingPlc() == null) return;
|
|
|
-
|
|
|
+
|
|
|
S7PLC rivetingPlc = getRivetingPlc();
|
|
|
- // 读取 PLC 当前打钉数
|
|
|
- int cur = rivetingPlc.readInt32("DB41.32");
|
|
|
- if (presetField != null) {
|
|
|
- presetField.setText(String.valueOf(aMax));
|
|
|
- }
|
|
|
-
|
|
|
- // 当打钉数增加时,读取对应参数
|
|
|
- if (sortA < cur) {
|
|
|
- sortA = cur;
|
|
|
- if (cur == 9) {
|
|
|
- rivetingPlc.writeUInt16("DB40.2", 2);
|
|
|
+ pollOneGun(rivetingPlc, true);
|
|
|
+ pollOneGun(rivetingPlc, false);
|
|
|
+ syncDualGunPower(rivetingPlc);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 读取单枪增量数据并落库。gunA=true 用 DB40/41,否则 DB50/51。
|
|
|
+ */
|
|
|
+ private void pollOneGun(S7PLC rivetingPlc, boolean gunA) {
|
|
|
+ String dbCtrl = gunA ? "DB40" : "DB50";
|
|
|
+ String dbData = gunA ? "DB41" : "DB51";
|
|
|
+ int max = gunA ? aMax : bMax;
|
|
|
+ int last = gunA ? sortA : sortB;
|
|
|
+
|
|
|
+ int cur = rivetingPlc.readInt32(dbData + ".32");
|
|
|
+ if (gunA) {
|
|
|
+ if (presetField != null) {
|
|
|
+ presetField.setText(String.valueOf(aMax));
|
|
|
}
|
|
|
-
|
|
|
- // 读取拉力和行程
|
|
|
- int fout = rivetingPlc.readInt32("DB41.24");
|
|
|
- Float sout = rivetingPlc.readFloat32("DB41.28");
|
|
|
+ } else if (presetFieldB != null) {
|
|
|
+ presetFieldB.setText(String.valueOf(bMax));
|
|
|
+ }
|
|
|
|
|
|
- int fmin = rivetingPlc.readInt32("DB41.0") - rivetingPlc.readInt32("DB41.8");
|
|
|
- Float smin = rivetingPlc.readFloat32("DB41.12") - rivetingPlc.readFloat32("DB41.20");
|
|
|
+ if (last >= cur) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
|
|
|
- int fmax = rivetingPlc.readInt32("DB41.0") + rivetingPlc.readInt32("DB41.4");
|
|
|
- Float smax = rivetingPlc.readInt32("DB41.12") + rivetingPlc.readFloat32("DB41.16");
|
|
|
+ if (gunA) {
|
|
|
+ sortA = cur;
|
|
|
+ } else {
|
|
|
+ sortB = cur;
|
|
|
+ }
|
|
|
|
|
|
- String qty = "0";
|
|
|
- String resultDing = "OK";
|
|
|
+ // A 枪历史逻辑:第 9 颗切换任务模式
|
|
|
+ if (gunA && cur == 9) {
|
|
|
try {
|
|
|
- // 读取判定状态,0为NG
|
|
|
- byte retang = rivetingPlc.readByte("DB41.36");
|
|
|
- if ((int) retang == 0) {
|
|
|
- resultDing = "NG";
|
|
|
- }
|
|
|
+ rivetingPlc.writeUInt16(dbCtrl + ".2", 2);
|
|
|
} catch (Exception e) {
|
|
|
- resultDing = "NG";
|
|
|
+ log.warn("写拉铆任务模式失败 gun={}", gunA ? "A" : "B", e);
|
|
|
}
|
|
|
-
|
|
|
- if ("OK".equals(resultDing)) {
|
|
|
- qty = "1";
|
|
|
+ }
|
|
|
+
|
|
|
+ int fout = rivetingPlc.readInt32(dbData + ".24");
|
|
|
+ Float sout = rivetingPlc.readFloat32(dbData + ".28");
|
|
|
+ int fmin = rivetingPlc.readInt32(dbData + ".0") - rivetingPlc.readInt32(dbData + ".8");
|
|
|
+ Float smin = rivetingPlc.readFloat32(dbData + ".12") - rivetingPlc.readFloat32(dbData + ".20");
|
|
|
+ int fmax = rivetingPlc.readInt32(dbData + ".0") + rivetingPlc.readInt32(dbData + ".4");
|
|
|
+ Float smax = rivetingPlc.readFloat32(dbData + ".12") + rivetingPlc.readFloat32(dbData + ".16");
|
|
|
+
|
|
|
+ String qty = "0";
|
|
|
+ String resultDing = "OK";
|
|
|
+ try {
|
|
|
+ byte retang = rivetingPlc.readByte(dbData + ".36");
|
|
|
+ if ((int) retang == 0) {
|
|
|
+ resultDing = "NG";
|
|
|
}
|
|
|
- // 单颗 NG 也计入完成数量,避免 NG 后 aFinish 不足导致流程卡住
|
|
|
+ } catch (Exception e) {
|
|
|
+ resultDing = "NG";
|
|
|
+ }
|
|
|
+ if ("OK".equals(resultDing)) {
|
|
|
+ qty = "1";
|
|
|
+ }
|
|
|
+ // 单颗 NG 也计入完成数,避免流程卡住(无复判弹窗)
|
|
|
+ if (gunA) {
|
|
|
aFinish++;
|
|
|
+ } else {
|
|
|
+ bFinish++;
|
|
|
+ }
|
|
|
|
|
|
- // 格式化数据以展示并保存
|
|
|
- java.text.DecimalFormat decimalFormat = new java.text.DecimalFormat("#.###");
|
|
|
- String soutstr = decimalFormat.format(sout);
|
|
|
- String sminstr = decimalFormat.format(smin);
|
|
|
- String smaxstr = decimalFormat.format(smax);
|
|
|
- String foutstr = fout + "";
|
|
|
- String fminstr = fmin + "";
|
|
|
- String fmaxstr = fmax + "";
|
|
|
+ java.text.DecimalFormat decimalFormat = new java.text.DecimalFormat("#.###");
|
|
|
+ String soutstr = decimalFormat.format(sout);
|
|
|
+ String sminstr = decimalFormat.format(smin);
|
|
|
+ String smaxstr = decimalFormat.format(smax);
|
|
|
+ String foutstr = fout + "";
|
|
|
+ String fminstr = fmin + "";
|
|
|
+ String fmaxstr = fmax + "";
|
|
|
|
|
|
- // 更新 UI 界面(仅完成数量)
|
|
|
+ if (gunA) {
|
|
|
if (finishField != null) {
|
|
|
- finishField.setText(cur + "");
|
|
|
- }
|
|
|
-
|
|
|
- log.info("OP190 拉铆数据 -> cur: {}, fout: {}, sout: {}", cur, foutstr, soutstr);
|
|
|
-
|
|
|
- // 将单颗螺钉数据保存到本地 SQLite,等待批量上传
|
|
|
- if (!productSnFeeding.getText().isEmpty()) {
|
|
|
- JSONObject obj = new JSONObject();
|
|
|
- obj.put("pos", pos);
|
|
|
- obj.put("foutstr", foutstr);
|
|
|
- obj.put("soutstr", soutstr);
|
|
|
- obj.put("fminstr", fminstr);
|
|
|
- obj.put("sminstr", sminstr);
|
|
|
- obj.put("fmaxstr", fmaxstr);
|
|
|
- obj.put("smaxstr", smaxstr);
|
|
|
- obj.put("qty", qty);
|
|
|
- obj.put("cur", cur + "");
|
|
|
- obj.put("user", MesClient.user_menu.getText());
|
|
|
- JdbcUtils.insertParamData(oprno, MesClient.mes_line_sn, productSnFeeding.getText(), obj.toJSONString());
|
|
|
+ finishField.setText(String.valueOf(Math.min(cur, max)));
|
|
|
}
|
|
|
+ } else if (finishFieldB != null) {
|
|
|
+ finishFieldB.setText(String.valueOf(Math.min(cur, max)));
|
|
|
}
|
|
|
-
|
|
|
- // 当完成数量达到预设时:关电源 + OP190 立即异步提交质量;下料信号仅做 MES 反馈
|
|
|
- if (cur == aMax) {
|
|
|
- powerOffRiveting();
|
|
|
+
|
|
|
+ String gun = gunA ? "A" : "B";
|
|
|
+ log.info("OP190 拉铆数据 gun={} -> cur: {}, fout: {}, sout: {}", gun, cur, foutstr, soutstr);
|
|
|
+
|
|
|
+ if (!productSnFeeding.getText().isEmpty() && cur >= 1 && (max <= 0 || cur <= max)) {
|
|
|
+ JdbcUtils.insertProdData(
|
|
|
+ oprno,
|
|
|
+ MesClient.mes_line_sn,
|
|
|
+ productSnFeeding.getText(),
|
|
|
+ gun,
|
|
|
+ foutstr, soutstr, fminstr, sminstr, fmaxstr, smaxstr,
|
|
|
+ qty, cur + "", MesClient.user_menu.getText());
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 双枪气路同步:单枪打满关该枪;双枪都满关总气并提交质量;未满意外断气则重开。
|
|
|
+ */
|
|
|
+ private void syncDualGunPower(S7PLC rivetingPlc) {
|
|
|
+ boolean aDone = sortA >= aMax;
|
|
|
+ boolean bDone = sortB >= bMax;
|
|
|
+
|
|
|
+ if (aDone && bDone) {
|
|
|
+ if (!bothGunsPoweredOff) {
|
|
|
+ powerOffRiveting();
|
|
|
+ bothGunsPoweredOff = true;
|
|
|
+ }
|
|
|
if (oprno.equals("OP190") && !op190RivetingComplete) {
|
|
|
op190RivetingComplete = true;
|
|
|
- submitOp190QualityAsync();
|
|
|
+ if (!offlineMode) {
|
|
|
+ submitOp190QualityAsync();
|
|
|
+ } else {
|
|
|
+ setMenuStatus("双枪拉铆完成(离线不提交),等待下料", 0);
|
|
|
+ log.info("OP190 离线模式:双枪拉铆完成,跳过质量提交");
|
|
|
+ }
|
|
|
}
|
|
|
- } else if (bindLbStatus && tjFeedingFlag >= 1 && sortA < aMax) {
|
|
|
- // 绑定冷板后,若拉铆机意外停机则尝试恢复
|
|
|
- try {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ bothGunsPoweredOff = false;
|
|
|
+ boolean canRecover = (bindLbStatus || offlineMode) && tjFeedingFlag >= 1;
|
|
|
+ try {
|
|
|
+ if (aDone) {
|
|
|
+ rivetingPlc.writeBoolean("DB40.0.0", false);
|
|
|
+ } else if (canRecover) {
|
|
|
boolean powerOn = rivetingPlc.readBoolean("DB40.0.0");
|
|
|
if (!powerOn) {
|
|
|
rivetingPlc.writeBoolean("DB40.0.0", true);
|
|
|
}
|
|
|
- } catch (Exception e) {
|
|
|
- log.error("恢复拉铆机电源失败", e);
|
|
|
}
|
|
|
+ if (bDone) {
|
|
|
+ rivetingPlc.writeBoolean("DB50.0.0", false);
|
|
|
+ } else if (canRecover) {
|
|
|
+ boolean powerOn = rivetingPlc.readBoolean("DB50.0.0");
|
|
|
+ if (!powerOn) {
|
|
|
+ rivetingPlc.writeBoolean("DB50.0.0", true);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.error("同步双枪气路失败", e);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
@@ -926,6 +1089,10 @@ public class WorkJPanelDevice extends JPanel {
|
|
|
* OP190:拉铆完成后立即异步提交质量结果,不阻塞等待下料信号。
|
|
|
*/
|
|
|
private void submitOp190QualityAsync() {
|
|
|
+ if (offlineMode) {
|
|
|
+ log.info("OP190 离线模式:跳过质量提交");
|
|
|
+ return;
|
|
|
+ }
|
|
|
if (op190QualitySubmitted) {
|
|
|
return;
|
|
|
}
|
|
|
@@ -941,29 +1108,38 @@ public class WorkJPanelDevice extends JPanel {
|
|
|
final String user = MesClient.user_menu.getText();
|
|
|
// 与 readResult() 一致:OP190 暂固定 OK
|
|
|
DataUtil.submitQualityAsync(sn, station, "OK", user);
|
|
|
- setMenuStatus("拉铆完成,结果后台提交中,等待下料", 0);
|
|
|
- log.info("OP190 拉铆完成,已异步提交质量 sn={}", sn);
|
|
|
+ setMenuStatus("双枪拉铆完成,结果后台提交中,等待下料", 0);
|
|
|
+ log.info("OP190 双枪拉铆完成,已异步提交质量 sn={}", sn);
|
|
|
}
|
|
|
|
|
|
- /** OP190 测试:拉铆完成数直接变为预设值,并立即异步提交质量 */
|
|
|
+ /** OP190 测试:双枪完成数直接变为预设值,并立即异步提交质量 */
|
|
|
private void simulateOp190RivetingComplete() {
|
|
|
if (op190RivetingComplete) {
|
|
|
return;
|
|
|
}
|
|
|
sortA = aMax;
|
|
|
+ sortB = bMax;
|
|
|
aFinish = aMax;
|
|
|
+ bFinish = bMax;
|
|
|
if (presetField != null) {
|
|
|
presetField.setText(String.valueOf(aMax));
|
|
|
}
|
|
|
if (finishField != null) {
|
|
|
finishField.setText(String.valueOf(aMax));
|
|
|
}
|
|
|
- log.info("OP190 测试模式:拉铆完成数模拟为 {}/{}", aMax, aMax);
|
|
|
+ if (presetFieldB != null) {
|
|
|
+ presetFieldB.setText(String.valueOf(bMax));
|
|
|
+ }
|
|
|
+ if (finishFieldB != null) {
|
|
|
+ finishFieldB.setText(String.valueOf(bMax));
|
|
|
+ }
|
|
|
+ log.info("OP190 测试模式:双枪拉铆完成数模拟为 A={}/{} B={}/{}", aMax, aMax, bMax, bMax);
|
|
|
try {
|
|
|
powerOffRiveting();
|
|
|
} catch (Exception e) {
|
|
|
log.warn("OP190 测试模式:关闭拉铆机电源跳过(无 PLC 时可忽略)", e);
|
|
|
}
|
|
|
+ bothGunsPoweredOff = true;
|
|
|
op190RivetingComplete = true;
|
|
|
submitOp190QualityAsync();
|
|
|
}
|
|
|
@@ -1142,7 +1318,7 @@ public class WorkJPanelDevice extends JPanel {
|
|
|
}
|
|
|
}else if(tjFeedingFlag == 1){
|
|
|
if(retRunning){
|
|
|
- if (oprno.equals("OP190") && !bindLbStatus) {
|
|
|
+ if (oprno.equals("OP190") && !bindLbStatus && !offlineMode) {
|
|
|
String statusText = status_menu.getText();
|
|
|
if (statusText == null || !statusText.contains("绑定失败")
|
|
|
&& !statusText.contains("绑定请求")
|
|
|
@@ -1153,8 +1329,10 @@ public class WorkJPanelDevice extends JPanel {
|
|
|
tjFeedingFlag = 2;
|
|
|
writeAllowFeeding(false); // 工位开始工作后再清除允许上料
|
|
|
resetFeeding();
|
|
|
+ if (oprno.equals("OP190") && offlineMode) {
|
|
|
+ initRivetingTask();
|
|
|
+ }
|
|
|
setMenuStatus("工件加工中", 0);
|
|
|
- getToolLife();
|
|
|
}
|
|
|
}else{
|
|
|
if(!retFeedingAllow){
|
|
|
@@ -1168,7 +1346,12 @@ public class WorkJPanelDevice extends JPanel {
|
|
|
writeAllowBlanking(true);
|
|
|
blankingmeslabel.setIcon(imageGreen);
|
|
|
|
|
|
- if (oprno.equals("OP190")) {
|
|
|
+ if (offlineMode) {
|
|
|
+ tjFeedingFlag = 3;
|
|
|
+ resetBlanking();
|
|
|
+ setMenuStatus("已下料(离线不提交),等待下一件", 0);
|
|
|
+ log.info("{}{} 离线模式:下料完成,跳过服务端提交", oprno, pos);
|
|
|
+ } else if (oprno.equals("OP190")) {
|
|
|
// OP190:质量已在拉铆完成时异步提交;下料仅反馈。若尚未提交则兜底补交
|
|
|
if (!op190QualitySubmitted) {
|
|
|
final String sn = productSnFeeding.getText() == null
|
|
|
@@ -1233,6 +1416,15 @@ public class WorkJPanelDevice extends JPanel {
|
|
|
productSnFeeding.setText(sn);
|
|
|
lastSn = sn;
|
|
|
|
|
|
+ // 离线模式:不做工件码校验,直接允许上料
|
|
|
+ if (offlineMode) {
|
|
|
+ tjFeedingFlag = 1;
|
|
|
+ writeAllowFeeding(true);
|
|
|
+ setMenuStatus("已允许上料(不校验不提交)", 0);
|
|
|
+ log.info("{}{} 离线读码通过 sn={}", oprno, pos, sn);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
// 查询工件质量
|
|
|
JSONObject retObj = DataUtil.checkQuality(sn,oprno+pos,MesClient.user_menu.getText());
|
|
|
if(retObj.get("result")!=null&&retObj.get("result").toString().equalsIgnoreCase("true")) {
|
|
|
@@ -1297,6 +1489,10 @@ public class WorkJPanelDevice extends JPanel {
|
|
|
setMenuStatus("请先登录", -1);
|
|
|
return false;
|
|
|
}
|
|
|
+ if (offlineMode) {
|
|
|
+ setMenuStatus("离线模式不提交服务端", -1);
|
|
|
+ return false;
|
|
|
+ }
|
|
|
final String sn = productSnFeeding.getText() == null
|
|
|
? "" : productSnFeeding.getText().trim();
|
|
|
if (sn.isEmpty()) {
|
|
|
@@ -1343,26 +1539,33 @@ public class WorkJPanelDevice extends JPanel {
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
- * 初始化拉铆工位任务
|
|
|
- * 下发拉铆预设数量并清空 PLC 记录
|
|
|
+ * 初始化双枪拉铆任务(同一 PLC:A=DB40/41,B=DB50/51)
|
|
|
*/
|
|
|
private void initRivetingTask() {
|
|
|
S7PLC rivetingPlc = getRivetingPlc();
|
|
|
if (rivetingPlc == null) return;
|
|
|
try {
|
|
|
- // 远程开机
|
|
|
- rivetingPlc.writeBoolean("DB40.0", true);
|
|
|
- // 设置任务并清零
|
|
|
+ bothGunsPoweredOff = false;
|
|
|
+ // 双枪远程开机
|
|
|
+ rivetingPlc.writeBoolean("DB40.0.0", true);
|
|
|
+ rivetingPlc.writeBoolean("DB50.0.0", true);
|
|
|
+ // A 枪任务
|
|
|
rivetingPlc.writeUInt16("DB40.2", 1);
|
|
|
rivetingPlc.writeUInt16("DB40.4", aMax);
|
|
|
rivetingPlc.writeInt32("DB41.32", 0);
|
|
|
-
|
|
|
- // 重置本地计数和 UI
|
|
|
- sortA = 0;
|
|
|
- aFinish = 0;
|
|
|
- if (finishField != null) {
|
|
|
- finishField.setText("0");
|
|
|
+ // B 枪任务
|
|
|
+ rivetingPlc.writeUInt16("DB50.2", 1);
|
|
|
+ rivetingPlc.writeUInt16("DB50.4", bMax);
|
|
|
+ rivetingPlc.writeInt32("DB51.32", 0);
|
|
|
+
|
|
|
+ resetRivetingCounters();
|
|
|
+ if (presetField != null) {
|
|
|
+ presetField.setText(String.valueOf(aMax));
|
|
|
+ }
|
|
|
+ if (presetFieldB != null) {
|
|
|
+ presetFieldB.setText(String.valueOf(bMax));
|
|
|
}
|
|
|
+ log.info("OP190 双枪拉铆任务已下发 A={} B={}", aMax, bMax);
|
|
|
} catch (Exception e) {
|
|
|
log.error("初始化拉铆任务失败", e);
|
|
|
}
|
|
|
@@ -1522,10 +1725,22 @@ public class WorkJPanelDevice extends JPanel {
|
|
|
|
|
|
private void resetRivetingCounters() {
|
|
|
sortA = 0;
|
|
|
+ sortB = 0;
|
|
|
aFinish = 0;
|
|
|
+ bFinish = 0;
|
|
|
+ bothGunsPoweredOff = false;
|
|
|
if (finishField != null) {
|
|
|
finishField.setText("0");
|
|
|
}
|
|
|
+ if (finishFieldB != null) {
|
|
|
+ finishFieldB.setText("0");
|
|
|
+ }
|
|
|
+ if (presetField != null) {
|
|
|
+ presetField.setText(String.valueOf(aMax));
|
|
|
+ }
|
|
|
+ if (presetFieldB != null) {
|
|
|
+ presetFieldB.setText(String.valueOf(bMax));
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
private void powerOffRiveting() {
|
|
|
@@ -1535,6 +1750,7 @@ public class WorkJPanelDevice extends JPanel {
|
|
|
}
|
|
|
try {
|
|
|
rivetingPlc.writeBoolean("DB40.0.0", false);
|
|
|
+ rivetingPlc.writeBoolean("DB50.0.0", false);
|
|
|
} catch (Exception e) {
|
|
|
log.error("关闭拉铆机电源失败", e);
|
|
|
}
|