Newer
Older
IoTP / src / main / java / info / istlab / IoTP / SerialWindow.java
@motoki miura motoki miura on 6 Apr 2023 13 KB d
package info.istlab.IoTP;

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Point;
import java.awt.Toolkit;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.FocusEvent;
import java.awt.event.FocusListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.event.WindowEvent;
import java.awt.event.WindowListener;
import java.util.ArrayList;
import java.util.Hashtable;

import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.JTextField;

import com.fazecast.jSerialComm.SerialPort;
import com.fazecast.jSerialComm.SerialPortDataListener;
import com.fazecast.jSerialComm.SerialPortEvent;

public class SerialWindow extends JFrame
        implements SerialPortDataListener, ActionListener, Runnable, WindowListener, KeyListener {

    SerialPort sp;
    JTextField jtf;
    JTextArea jta;
    Thread thread;

    static SerialChecker serialChecker;

    public static Hashtable<String, SerialWindow> hash;
    static {
        hash = new Hashtable<String, SerialWindow>();
    }

    /**
     * チェックして、App.serialName にポート名をセットするだけ。
     */
    public static void check(boolean popup_if_zero) {
        if (App.serialName == null) {
            if (serialChecker == null)
                serialChecker = Launcher.theapp.serialChecker;
            serialChecker.updateSerialNames();
        }
        if (serialChecker.validSerialNames.size() == 0) {
            if (popup_if_zero)
                JOptionPane.showMessageDialog(Launcher.theapp, "No available serial ports.");
        }
        // SerialPort[] ports = SerialPort.getCommPorts();
        // ArrayList<SerialPort> splist_valid = new ArrayList<SerialPort>();
        // for (SerialPort sp : ports) {
        // String name = sp.getSystemPortName();
        // if (name.startsWith("cu.usbserial") || name.startsWith("COM")) {
        // splist_valid.add(sp);
        // }
        // }
        // if (splist_valid.size() == 1) {
        // SerialPort sp = splist_valid.get(0);
        // App.serialName = sp.getSystemPortName();

        // } else if (splist_valid.size() == 0) {
        // if (popup_if_zero)
        // JOptionPane.showMessageDialog(Launcher.theapp, "No available serial ports.");
        // } else {
        // if (App.serialName == null) SerialSelector.show(splist_valid.toArray(new
        // SerialPort[] {}));
        // }
    }

    public static void invoke(boolean popup_if_zero) {
        if (serialChecker == null)
            serialChecker = Launcher.theapp.serialChecker;
        serialChecker.updateSerialNames();
        if (serialChecker.validSerialNames.size() == 0) {
            if (popup_if_zero)
                JOptionPane.showMessageDialog(Launcher.theapp, "No available serial ports.");
            return;
        }
        SerialWindow swin = hash.get(App.serialName);
        if (swin == null) {
            SerialPort sp = serialChecker.portHash.get(App.serialName);
            swin = new SerialWindow(sp);
        } else {
            swin.setVisible(true);
            swin.reconnect();
        }
        // SerialPort[] ports = SerialPort.getCommPorts();
        // ArrayList<SerialPort> splist_valid = new ArrayList<SerialPort>();
        // for (SerialPort sp : ports) {
        // String name = sp.getSystemPortName();
        // if (name.startsWith("cu.usbserial") || name.startsWith("COM")) {
        // splist_valid.add(sp);
        // }
        // }
        // if (splist_valid.size() == 1) {
        // SerialPort sp = splist_valid.get(0);
        // new SerialWindow(sp);
        // } else if (splist_valid.size() == 0) {
        // JOptionPane.showMessageDialog(Launcher.theapp, "No available serial ports.");
        // } else {
        // if (App.serialName == null) SerialSelector.show(splist_valid.toArray(new
        // SerialPort[] {}));
        // SerialPort sp = Launcher.theapp.serialChecker.portHash.get(App.serialName);
        // new SerialWindow(sp);
        // }
    }

    public SerialWindow(SerialPort _sp) {
        super("SerialMonitor " + _sp.getSystemPortName() + " (ボーレート:115200)");
        sp = _sp;
        sp.setBaudRate(115200);
        sp.addDataListener(this);
        System.out.println(sp.getSystemPortName());
        if (!sp.openPort()) {
            JOptionPane.showMessageDialog(Launcher.theapp,
                    "他のウィンドウまたはプログラムがシリアルポート " + sp.getSystemPortName() + " を使用しています。");
            System.out.println("Unable to open the port.");
            return;
        }
        hash.put(sp.getSystemPortName(), this);

        jtf = new JTextField("(ここをクリックして、入力して、Enterを押すと、シリアル送信します)");
        jtf.setForeground(Color.GRAY);
        jtf.addFocusListener(new FocusListener() {
            @Override
            public void focusGained(FocusEvent e) {
                if (jtf.getText().equals("(ここをクリックして、入力して、Enterを押すと、シリアル送信します)")) {
                    jtf.setText("");
                    jtf.setForeground(Color.BLACK);
                }
            }

            @Override
            public void focusLost(FocusEvent e) {
                if (jtf.getText().isEmpty()) {
                    jtf.setForeground(Color.GRAY);
                    jtf.setText("(ここをクリックして、入力して、Enterを押すと、シリアル送信します)");
                }
            }
        });
        jtf.setBackground(new Color(210, 255, 255));

        getContentPane().add(jtf, BorderLayout.NORTH);
        jta = new JTextArea();
        getContentPane().add(new JScrollPane(jta), BorderLayout.CENTER);
        // compileB = new JButton("compile");
        // compileB.addActionListener(e -> {
        // compile(e.getActionCommand());
        // });
        // getContentPane().add(compileB, BorderLayout.SOUTH);

        setSize(500, 400);
        setLocation(centerOfScreen(getSize()));
        setVisible(true);
        setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE); // この下のWindowListenerで処理する
        jtf.addActionListener(this);
        addWindowListener(this); // Xをおしたらserial closeする

        // フォーカスをあえて外す
        jta.requestFocus();

        jtf.addKeyListener(this);
        jta.addKeyListener(this);
    }

    @Override
    public int getListeningEvents() {
        return SerialPort.LISTENING_EVENT_DATA_AVAILABLE | SerialPort.LISTENING_EVENT_PORT_DISCONNECTED;
    }

    @Override
    public void serialEvent(SerialPortEvent event) {
        try {
            int evt = event.getEventType();

            // System.out.println("Event " + evt + " received");

            if (evt == SerialPort.LISTENING_EVENT_DATA_AVAILABLE) {

                int bytesToRead = sp.bytesAvailable();
                if (bytesToRead == -1) {
                    System.out.println("-1 means port is closed.");
                    closeSerialPort("切断されました");
                    return;
                }
                byte[] newData = new byte[bytesToRead];
                sp.readBytes(newData, bytesToRead);
                String s = new String(newData, "UTF8");
                appendToJTA(s);
            } else if (evt == SerialPort.LISTENING_EVENT_PORT_DISCONNECTED) {
                System.out.println("Port disconnected.");
                // System.out.println(event.toString());
                closeSerialPort("切断されました");
                // SerialWindow.closeAll("切断されました");
            }

        } catch (Exception ex) {
            System.err.println(ex.getMessage());
            closeSerialPort("切断されました");
        }
    }

    void appendToJTA(String s) {
        if (jta == null)
            return;
        if (s.contains("waiting for download")) {
            closeSerialPort(null); // 切断のみ。再接続はしない
        }
        jta.append(s);
        int len = jta.getDocument().getLength();
        if (jta.getSelectedText() == null)
            jta.setCaretPosition(len);
    }

    // ここで、画面中心にウィンドウ表示するため、いろいろ計算する
    public static Point centerOfScreen(Dimension winSize) {
        Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
        return new Point((screenSize.width - winSize.width) / 2, (screenSize.height - winSize.height) / 2);
    }

    // ここで、画面中心にウィンドウ表示するため、いろいろ計算する
    public static Point leftOfScreen(Dimension winSize) {
        Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
        return new Point(10, (screenSize.height - winSize.height) / 2);
    }

    @Override
    public void actionPerformed(ActionEvent e) {
        String action = e.getActionCommand();
        System.out.println(action);
        try {
            String message = action + "\n";
            byte[] b = message.getBytes();
            System.out.println("send " + b.length + " bytes");
            sp.writeBytes(b, b.length);
            jtf.setText("");
        } catch (Exception ex) {
            System.out.println(ex.getMessage());
        }
    }

    /**
     * 再接続用(つかっていない)
     */
    @Override
    public void run() {
        appendToJTA("(Waiting to reconnect.)\n");
        try {
            Thread.sleep(10000);
        } catch (InterruptedException e) {
        }
        while (thread != null) {
            if (!sp.openPort()) {
                System.out.println("Unable to open the port.");
                return;
            } else {
                thread = null;
                appendToJTA("Reconnected.\n");
            }
            try {
                Thread.sleep(2000);
            } catch (InterruptedException e) {
            }

        }
    }

    @Override
    public void windowOpened(WindowEvent e) {
    }

    @Override
    public void windowClosing(WindowEvent e) {
        // closeSerialPort("シリアルモニタウィンドウを閉じます");
        disposeWin();
    }

    @Override
    public void windowClosed(WindowEvent e) {
        disposeWin();
    }

    @Override
    public void windowIconified(WindowEvent e) {
    }

    @Override
    public void windowDeiconified(WindowEvent e) {
    }

    @Override
    public void windowActivated(WindowEvent e) {
    }

    @Override
    public void windowDeactivated(WindowEvent e) {
    }

    public static void disposeAll() {
        ArrayList<SerialWindow> toberemoved = new ArrayList<SerialWindow>();
        SerialWindow.hash.entrySet().forEach(ent -> {
            // String key = ent.getKey();
            SerialWindow swin = ent.getValue();
            if (swin != null) {
                toberemoved.add(swin);
            }
        });
        for (SerialWindow swin : toberemoved) {
            swin.disposeWin();
        }
        // SerialWindow.hash.clear();
    }
    public static void openAll() {
        serialChecker.portHash.entrySet().forEach(ent -> {
            String key = ent.getKey();
            SerialPort sp = ent.getValue();
            if (hash.get(key)!= null){
                SerialWindow swin = hash.get(key);
                swin.setVisible(true);
                swin.reconnect();
            } else {
                new SerialWindow(sp);
            }
        });
    }


    public static void disposeByName(String serialName) {
        for (String key : SerialWindow.hash.keySet()) {
            if (!key.equals(serialName))
                continue;
            SerialWindow swin = SerialWindow.hash.get(key);
            if (swin != null) {
                swin.disposeWin();
            }
        }
    }

    // public static void closeAll(String mes) {
    // for (String key : SerialWindow.hash.keySet()) {
    // SerialWindow swin = SerialWindow.hash.get(key);
    // if (swin != null) {
    // if (mes == null)
    // mes = "Uploadに備え、切断しました";
    // swin.closeSerialPort(mes);
    // }
    // }
    // }

    /**
     * シリアルポート接続を閉じる
     * 
     * @param mes
     */
    void closeSerialPort(String mes) {
        if (sp != null) {
            if (sp.isOpen()) {
                System.out.println("close Serial Port");
                sp.closePort();
                jta.setBackground(Color.lightGray);
                if (mes == null)
                    appendToJTA("Uploadに備えて、切断しました。");
                else
                    appendToJTA(mes);
            }
        }
        // 再接続に備えて、sp = null はしない
    }

    /**
     * 再接続
     */
    public void reconnect() {
        sp.openPort();
        jta.setBackground(Color.white);
    }

    /**
     * ウィンドウを閉じる前に、シリアルポート接続を閉じる
     */
    public void disposeWin() {
        closeSerialPort("シリアルモニタウィンドウを閉じます");

        SerialWindow.hash.remove(this.sp.getSystemPortName());
        dispose();
    }

    @Override
    public void keyTyped(KeyEvent e) {
    }

    @Override
    public void keyPressed(KeyEvent e) {
        if (e.isControlDown() || e.isAltDown() || e.isAltGraphDown() || e.isMetaDown()) {
            if (e.getKeyCode() == 87) { // W
                closeSerialPort("シリアルモニタウィンドウを閉じます");
                disposeWin();
            }
        }
    }

    @Override
    public void keyReleased(KeyEvent e) {
    }


}