|
|
@@ -0,0 +1,402 @@
|
|
|
+package com.mes.ui;
|
|
|
+
|
|
|
+import com.mes.util.DateLocalUtils;
|
|
|
+import com.mes.util.JdbcUtils;
|
|
|
+import org.slf4j.Logger;
|
|
|
+import org.slf4j.LoggerFactory;
|
|
|
+
|
|
|
+import javax.swing.*;
|
|
|
+import javax.swing.border.EmptyBorder;
|
|
|
+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.io.BufferedReader;
|
|
|
+import java.io.IOException;
|
|
|
+import java.io.InputStream;
|
|
|
+import java.io.InputStreamReader;
|
|
|
+import java.time.LocalDateTime;
|
|
|
+import java.util.List;
|
|
|
+import java.util.Properties;
|
|
|
+import java.util.Timer;
|
|
|
+import java.util.TimerTask;
|
|
|
+import java.util.concurrent.ExecutorService;
|
|
|
+import java.util.concurrent.Executors;
|
|
|
+
|
|
|
+/**
|
|
|
+ * 底护板清洗 + 入库 客户端
|
|
|
+ * 工位:520-OP285 / 610-OP290(按底护板码前缀路由)
|
|
|
+ *
|
|
|
+ * 流程:
|
|
|
+ * 扫底护板码 → 按前缀路由到 520 或 610 → POST /mesProductDhbKc/in → 显示结果
|
|
|
+ * 网络异常时本地 SQLite 暂存,后台 5 秒一次重试
|
|
|
+ */
|
|
|
+public class MesClient extends JFrame {
|
|
|
+ public static final Logger log = LoggerFactory.getLogger(MesClient.class);
|
|
|
+
|
|
|
+ public static int mes_auth = 0;
|
|
|
+ public static String mes_gw = ""; // 本机工位号(仅显示用)
|
|
|
+ public static String mes_gw_des = "";
|
|
|
+ public static String mes_server_ip = ""; // 兼容字段:登录用主服务器 IP
|
|
|
+ public static int mes_tcp_port = 8980;
|
|
|
+ public static String mes_line_sn = "";
|
|
|
+
|
|
|
+ // 兼容字段(被 LoginFarme/legacy 引用,新逻辑下无作用)
|
|
|
+ public static Object nettyClient = null;
|
|
|
+ public static boolean tcp_connect_flag = true;
|
|
|
+ public static boolean connect_request_flag = false;
|
|
|
+ public static String sessionid = "";
|
|
|
+
|
|
|
+ public static JPanel contentPane;
|
|
|
+ public static MesClient mesClientFrame;
|
|
|
+ public static JTabbedPane tabbedPane;
|
|
|
+
|
|
|
+ public static JButton status_menu; // 顶部状态文字
|
|
|
+ public static JButton user_menu;
|
|
|
+ public static JButton server520_dot; // 520 在线指示
|
|
|
+ public static JButton server610_dot; // 610 在线指示
|
|
|
+
|
|
|
+ public static JTextField scan_input; // 底护板码输入框
|
|
|
+ public static JButton scan_button;
|
|
|
+ public static JLabel route_label; // 显示当前码路由到哪个服务器
|
|
|
+
|
|
|
+ public static JTable record520Table;
|
|
|
+ public static JTable record610Table;
|
|
|
+ public static Object[] recordColumns = {"底护板码", "工位", "产线", "操作员", "状态", "消息", "时间"};
|
|
|
+
|
|
|
+ public static JFrame welcomeWin;
|
|
|
+ public static String user20 = "";
|
|
|
+
|
|
|
+ public static int userLoginHours;
|
|
|
+ public static boolean mes_flag = false;
|
|
|
+ public static JButton mesbtn;
|
|
|
+
|
|
|
+ // ============ 入口 ============
|
|
|
+ public static void main(String[] args) {
|
|
|
+ if (LockUtil.getInstance().isAppActive()) return;
|
|
|
+ EventQueue.invokeLater(() -> {
|
|
|
+ try {
|
|
|
+ readProperty();
|
|
|
+ JdbcUtils.getConn();
|
|
|
+
|
|
|
+ mesClientFrame = new MesClient();
|
|
|
+ mesClientFrame.setVisible(false);
|
|
|
+
|
|
|
+ welcomeWin = new LoginFarme();
|
|
|
+ welcomeWin.setVisible(true);
|
|
|
+
|
|
|
+ startBackgroundTasks();
|
|
|
+ } catch (Exception e) {
|
|
|
+ e.printStackTrace();
|
|
|
+ }
|
|
|
+ });
|
|
|
+ }
|
|
|
+
|
|
|
+ private static void readProperty() throws IOException {
|
|
|
+ InputStream is = ClassLoader.getSystemResourceAsStream("config/config.properties");
|
|
|
+ Properties pro = new Properties();
|
|
|
+ BufferedReader br = new BufferedReader(new InputStreamReader(is, "UTF-8"));
|
|
|
+ pro.load(br);
|
|
|
+ mes_gw = pro.getProperty("mes.gw", "OP290").trim();
|
|
|
+ mes_line_sn = pro.getProperty("mes.line_sn", "XT").trim();
|
|
|
+ mes_gw_des = OprnoUtil.getGwDes(mes_line_sn, mes_gw);
|
|
|
+
|
|
|
+ // 多服务器配置
|
|
|
+ ServerRoute.init(pro);
|
|
|
+
|
|
|
+ // 登录用主服务器(默认 520)
|
|
|
+ ServerRoute.Server login = ServerRoute.login();
|
|
|
+ if (login != null) {
|
|
|
+ mes_server_ip = login.ip;
|
|
|
+ mes_tcp_port = login.port;
|
|
|
+ }
|
|
|
+ log.info("入库客户端启动 gw={} lineSn={} loginServer={}", mes_gw, mes_line_sn, mes_server_ip);
|
|
|
+ }
|
|
|
+
|
|
|
+ /** 启动后台任务:健康检查 + 失败重试 */
|
|
|
+ private static void startBackgroundTasks() {
|
|
|
+ // 健康检查 5 秒一次
|
|
|
+ Timer t = new Timer(true);
|
|
|
+ t.schedule(new TimerTask() {
|
|
|
+ public void run() {
|
|
|
+ for (ServerRoute.Server s : ServerRoute.all()) {
|
|
|
+ s.online = DhbKcUtil.ping(s);
|
|
|
+ }
|
|
|
+ refreshServerDots();
|
|
|
+ }
|
|
|
+ }, 1000, 5000);
|
|
|
+
|
|
|
+ // 失败重试 5 秒一次
|
|
|
+ Timer t2 = new Timer(true);
|
|
|
+ t2.schedule(new TimerTask() {
|
|
|
+ public void run() {
|
|
|
+ int n = DhbKcUtil.retryPending();
|
|
|
+ if (n > 0) refreshRecordTables();
|
|
|
+ }
|
|
|
+ }, 3000, 5000);
|
|
|
+ }
|
|
|
+
|
|
|
+ private static void refreshServerDots() {
|
|
|
+ if (server520_dot == null || server610_dot == null) return;
|
|
|
+ EventQueue.invokeLater(() -> {
|
|
|
+ ServerRoute.Server s520 = ServerRoute.get("520");
|
|
|
+ ServerRoute.Server s610 = ServerRoute.get("610");
|
|
|
+ applyDot(server520_dot, "520", s520 != null && s520.online);
|
|
|
+ applyDot(server610_dot, "610", s610 != null && s610.online);
|
|
|
+ });
|
|
|
+ }
|
|
|
+
|
|
|
+ private static void applyDot(JButton btn, String label, boolean online) {
|
|
|
+ if (online) {
|
|
|
+ btn.setIcon(new ImageIcon(MesClient.class.getResource("/bg/green_dot.png")));
|
|
|
+ btn.setText(label + " 在线");
|
|
|
+ btn.setForeground(new Color(0, 128, 0));
|
|
|
+ } else {
|
|
|
+ btn.setIcon(new ImageIcon(MesClient.class.getResource("/bg/grey_dot.png")));
|
|
|
+ btn.setText(label + " 离线");
|
|
|
+ btn.setForeground(Color.RED);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ public static void setMenuStatus(String msg, int error) {
|
|
|
+ if (status_menu == null) return;
|
|
|
+ status_menu.setForeground(error == 0 ? new Color(0, 128, 0) : Color.RED);
|
|
|
+ status_menu.setText(msg);
|
|
|
+ }
|
|
|
+
|
|
|
+ public static void logoff() {
|
|
|
+ welcomeWin.setVisible(true);
|
|
|
+ mesClientFrame.setVisible(false);
|
|
|
+ }
|
|
|
+
|
|
|
+ // ============ 主界面 ============
|
|
|
+ public MesClient() {
|
|
|
+ setIconImage(Toolkit.getDefaultToolkit().getImage(MesClient.class.getResource("/bg/logo.png")));
|
|
|
+ setTitle("底护板清洗+录入客户端:" + mes_gw + " - " + mes_gw_des);
|
|
|
+ setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
|
|
|
+ setBounds(0, 0, 1024, 768);
|
|
|
+
|
|
|
+ // ---- 菜单栏 ----
|
|
|
+ JMenuBar menuBar = new JMenuBar();
|
|
|
+ menuBar.setFont(new Font("Microsoft YaHei UI", Font.PLAIN, 20));
|
|
|
+ setJMenuBar(menuBar);
|
|
|
+
|
|
|
+ JMenu fileMenu = new JMenu("用户");
|
|
|
+ fileMenu.setIcon(new ImageIcon(MesClient.class.getResource("/bg/user.png")));
|
|
|
+ fileMenu.setFont(new Font("微软雅黑", Font.PLAIN, 18));
|
|
|
+ menuBar.add(fileMenu);
|
|
|
+
|
|
|
+ JMenuItem exitMenuItem = new JMenuItem("退出登录");
|
|
|
+ exitMenuItem.setIcon(new ImageIcon(MesClient.class.getResource("/bg/logoff.png")));
|
|
|
+ exitMenuItem.setFont(new Font("微软雅黑", Font.PLAIN, 18));
|
|
|
+ fileMenu.add(exitMenuItem);
|
|
|
+ exitMenuItem.addMouseListener(new MouseAdapter() {
|
|
|
+ public void mousePressed(MouseEvent e) { logoff(); }
|
|
|
+ });
|
|
|
+
|
|
|
+ // ---- 顶部状态栏 ----
|
|
|
+ contentPane = new JPanel();
|
|
|
+ contentPane.setBorder(new EmptyBorder(5, 5, 5, 5));
|
|
|
+ setContentPane(contentPane);
|
|
|
+ contentPane.setLayout(new BorderLayout(0, 0));
|
|
|
+
|
|
|
+ JToolBar toolBar = new JToolBar();
|
|
|
+ contentPane.add(toolBar, BorderLayout.NORTH);
|
|
|
+
|
|
|
+ JLabel lblState = new JLabel("状态:");
|
|
|
+ lblState.setFont(new Font("微软雅黑", Font.PLAIN, 18));
|
|
|
+ toolBar.add(lblState);
|
|
|
+
|
|
|
+ status_menu = new JButton("等待登录");
|
|
|
+ status_menu.setFont(new Font("微软雅黑", Font.PLAIN, 18));
|
|
|
+ status_menu.setForeground(new Color(0, 128, 0));
|
|
|
+ toolBar.add(status_menu);
|
|
|
+
|
|
|
+ toolBar.add(new JLabel(" "));
|
|
|
+
|
|
|
+ server520_dot = new JButton("520 检测中");
|
|
|
+ server520_dot.setFont(new Font("微软雅黑", Font.PLAIN, 18));
|
|
|
+ server520_dot.setIcon(new ImageIcon(MesClient.class.getResource("/bg/grey_dot.png")));
|
|
|
+ toolBar.add(server520_dot);
|
|
|
+
|
|
|
+ server610_dot = new JButton("610 检测中");
|
|
|
+ server610_dot.setFont(new Font("微软雅黑", Font.PLAIN, 18));
|
|
|
+ server610_dot.setIcon(new ImageIcon(MesClient.class.getResource("/bg/grey_dot.png")));
|
|
|
+ toolBar.add(server610_dot);
|
|
|
+
|
|
|
+ toolBar.add(new JLabel(" "));
|
|
|
+
|
|
|
+ JLabel lblUser = new JLabel("登录用户:");
|
|
|
+ lblUser.setFont(new Font("微软雅黑", Font.PLAIN, 18));
|
|
|
+ toolBar.add(lblUser);
|
|
|
+
|
|
|
+ user_menu = new JButton("--");
|
|
|
+ user_menu.setFont(new Font("微软雅黑", Font.PLAIN, 18));
|
|
|
+ toolBar.add(user_menu);
|
|
|
+
|
|
|
+ // 兼容:旧代码引用了 mesbtn
|
|
|
+ mesbtn = new JButton("");
|
|
|
+ mesbtn.setVisible(false);
|
|
|
+
|
|
|
+ // ---- Tab ----
|
|
|
+ tabbedPane = new JTabbedPane(JTabbedPane.TOP);
|
|
|
+ tabbedPane.setFont(new Font("微软雅黑", Font.BOLD, 18));
|
|
|
+ contentPane.add(tabbedPane);
|
|
|
+
|
|
|
+ tabbedPane.addTab("录入", new ImageIcon(MesClient.class.getResource("/bg/a_side.png")), buildScanPanel(), null);
|
|
|
+ tabbedPane.addTab("520 工作记录", new ImageIcon(MesClient.class.getResource("/bg/menu_data_preprocess.png")), buildRecordPanel("520"), null);
|
|
|
+ tabbedPane.addTab("610 工作记录", new ImageIcon(MesClient.class.getResource("/bg/menu_data_preprocess.png")), buildRecordPanel("610"), null);
|
|
|
+
|
|
|
+ tabbedPane.addChangeListener(e -> refreshRecordTables());
|
|
|
+ }
|
|
|
+
|
|
|
+ private JPanel buildScanPanel() {
|
|
|
+ JPanel root = new JPanel();
|
|
|
+ root.setLayout(null);
|
|
|
+
|
|
|
+ JLabel title = new JLabel("扫底护板码录入");
|
|
|
+ title.setFont(new Font("微软雅黑", Font.BOLD, 36));
|
|
|
+ title.setHorizontalAlignment(SwingConstants.CENTER);
|
|
|
+ title.setBounds(0, 30, 1000, 60);
|
|
|
+ root.add(title);
|
|
|
+
|
|
|
+ scan_input = new JTextField();
|
|
|
+ scan_input.setHorizontalAlignment(SwingConstants.CENTER);
|
|
|
+ scan_input.setFont(new Font("微软雅黑", Font.PLAIN, 32));
|
|
|
+ scan_input.setBounds(80, 130, 600, 80);
|
|
|
+ root.add(scan_input);
|
|
|
+
|
|
|
+ scan_button = new JButton("录入");
|
|
|
+ scan_button.setIcon(new ImageIcon(MesClient.class.getResource("/bg/scan_barcode.png")));
|
|
|
+ scan_button.setFont(new Font("微软雅黑", Font.PLAIN, 28));
|
|
|
+ scan_button.setBounds(700, 130, 200, 80);
|
|
|
+ root.add(scan_button);
|
|
|
+
|
|
|
+ route_label = new JLabel(" ");
|
|
|
+ route_label.setFont(new Font("微软雅黑", Font.PLAIN, 20));
|
|
|
+ route_label.setBounds(80, 220, 820, 30);
|
|
|
+ route_label.setHorizontalAlignment(SwingConstants.CENTER);
|
|
|
+ root.add(route_label);
|
|
|
+
|
|
|
+ JLabel hint = new JLabel("提示:底护板码以 +KB77 开头入 520,+KB78 开头入 610。重复扫码不影响。");
|
|
|
+ hint.setFont(new Font("微软雅黑", Font.PLAIN, 14));
|
|
|
+ hint.setForeground(Color.GRAY);
|
|
|
+ hint.setBounds(80, 260, 820, 30);
|
|
|
+ hint.setHorizontalAlignment(SwingConstants.CENTER);
|
|
|
+ root.add(hint);
|
|
|
+
|
|
|
+ // 输入框监听:实时显示路由结果
|
|
|
+ scan_input.getDocument().addDocumentListener(new javax.swing.event.DocumentListener() {
|
|
|
+ public void insertUpdate(javax.swing.event.DocumentEvent e) { updateRouteHint(); }
|
|
|
+ public void removeUpdate(javax.swing.event.DocumentEvent e) { updateRouteHint(); }
|
|
|
+ public void changedUpdate(javax.swing.event.DocumentEvent e) { updateRouteHint(); }
|
|
|
+ });
|
|
|
+ scan_input.addActionListener(e -> doScan());
|
|
|
+ scan_button.addActionListener(e -> doScan());
|
|
|
+
|
|
|
+ return root;
|
|
|
+ }
|
|
|
+
|
|
|
+ private static void updateRouteHint() {
|
|
|
+ String code = scan_input.getText().trim();
|
|
|
+ if (code.isEmpty()) { route_label.setText(" "); return; }
|
|
|
+ ServerRoute.Server s = ServerRoute.route(code);
|
|
|
+ if (s == null) {
|
|
|
+ route_label.setForeground(Color.RED);
|
|
|
+ route_label.setText("⚠ 未识别的前缀,无法路由");
|
|
|
+ } else {
|
|
|
+ route_label.setForeground(new Color(0, 128, 0));
|
|
|
+ route_label.setText("→ 将提交到 " + s.label() + " 工位 " + s.oprno);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ private static void doScan() {
|
|
|
+ String code = scan_input.getText().trim();
|
|
|
+ if (code.isEmpty()) return;
|
|
|
+
|
|
|
+ ServerRoute.Server srv = ServerRoute.route(code);
|
|
|
+ if (srv == null) {
|
|
|
+ setMenuStatus("未识别的底护板码前缀: " + code, -1);
|
|
|
+ scan_input.selectAll();
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ DhbKcUtil.Result r = DhbKcUtil.in(code, user20.trim(), mes_line_sn);
|
|
|
+ if (r.ok) {
|
|
|
+ setMenuStatus("[" + srv.key + "] " + r.message, 0);
|
|
|
+ } else {
|
|
|
+ setMenuStatus("[" + srv.key + "] " + r.message, -1);
|
|
|
+ }
|
|
|
+ scan_input.setText("");
|
|
|
+ scan_input.requestFocus();
|
|
|
+ refreshRecordTables();
|
|
|
+ }
|
|
|
+
|
|
|
+ private JPanel buildRecordPanel(String serverKey) {
|
|
|
+ JPanel root = new JPanel(new BorderLayout());
|
|
|
+ JTable table = new JTable(new Object[0][7], recordColumns);
|
|
|
+ table.setFont(new Font("微软雅黑", Font.PLAIN, 14));
|
|
|
+ table.setRowHeight(28);
|
|
|
+ table.getTableHeader().setFont(new Font("微软雅黑", Font.BOLD, 14));
|
|
|
+ table.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
|
|
|
+ // 列宽:底护板码 / 工位 / 产线 / 操作员 / 状态 / 消息 / 时间
|
|
|
+ int[] widths = {260, 80, 70, 100, 100, 260, 160};
|
|
|
+ for (int i = 0; i < widths.length && i < table.getColumnCount(); i++) {
|
|
|
+ table.getColumnModel().getColumn(i).setPreferredWidth(widths[i]);
|
|
|
+ }
|
|
|
+ if ("520".equals(serverKey)) record520Table = table;
|
|
|
+ else record610Table = table;
|
|
|
+ root.add(new JScrollPane(table), BorderLayout.CENTER);
|
|
|
+
|
|
|
+ JButton refresh = new JButton("刷新");
|
|
|
+ refresh.setFont(new Font("微软雅黑", Font.PLAIN, 14));
|
|
|
+ refresh.addActionListener(e -> refreshRecordTables());
|
|
|
+ JPanel south = new JPanel(new FlowLayout(FlowLayout.LEFT));
|
|
|
+ south.add(refresh);
|
|
|
+ root.add(south, BorderLayout.SOUTH);
|
|
|
+ return root;
|
|
|
+ }
|
|
|
+
|
|
|
+ public static void refreshRecordTables() {
|
|
|
+ EventQueue.invokeLater(() -> {
|
|
|
+ fillTable(record520Table, "520");
|
|
|
+ fillTable(record610Table, "610");
|
|
|
+ });
|
|
|
+ }
|
|
|
+
|
|
|
+ private static void fillTable(JTable table, String serverKey) {
|
|
|
+ if (table == null) return;
|
|
|
+ List<String[]> rows = DhbKcUtil.listLocal(serverKey, 200);
|
|
|
+ Object[][] data = new Object[rows.size()][recordColumns.length];
|
|
|
+ for (int i = 0; i < rows.size(); i++) {
|
|
|
+ data[i] = rows.get(i);
|
|
|
+ }
|
|
|
+ // 仅更新数据,保留列宽配置
|
|
|
+ javax.swing.table.DefaultTableModel model = new javax.swing.table.DefaultTableModel(data, recordColumns) {
|
|
|
+ @Override
|
|
|
+ public boolean isCellEditable(int row, int column) { return false; }
|
|
|
+ };
|
|
|
+ // 保存列宽
|
|
|
+ int[] widths = new int[table.getColumnCount()];
|
|
|
+ for (int i = 0; i < widths.length; i++) {
|
|
|
+ widths[i] = table.getColumnModel().getColumn(i).getPreferredWidth();
|
|
|
+ }
|
|
|
+ table.setModel(model);
|
|
|
+ // 恢复列宽
|
|
|
+ for (int i = 0; i < widths.length && i < table.getColumnCount(); i++) {
|
|
|
+ table.getColumnModel().getColumn(i).setPreferredWidth(widths[i]);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /** 入库工位无物料绑定,不做事 */
|
|
|
+ public static void initWarehouseData() {
|
|
|
+ setMenuStatus("已就绪,请扫底护板码", 0);
|
|
|
+ if (scan_input != null) scan_input.requestFocus();
|
|
|
+ refreshRecordTables();
|
|
|
+ }
|
|
|
+
|
|
|
+ public static void getMaterailData() {
|
|
|
+ // 入库工位无物料绑定,不做事
|
|
|
+ }
|
|
|
+}
|