JavaFx, простой терминал COM порта

В жизни embedded разработчика, часто возникает потребность взаимодействия с серийным портом
И пускай RS-232 почти полностью вытеснен современными интерфейсами, UART похоже никуда не собирается уходить

Большинство модулей (WI-FI, IoT и др.), демоплат/одноплатников работают или имеют на борту UART.

6lcxr1osfeofzdzqxegygdrsdxi.png
Терминальных программ огромное множество, самые заметные — Putty и termianl v1.9b
Они отлично справляются с задачами, но сложности начинаются, когда открытых соединений больше одного.

На переключение и поиск нужного окна уходит много времени.

Тогда возникла идея, почему бы не написать терминал, где каждое соединение будет в отдельной вкладке, и каждый пришедший пакет будет на ней сигнализировать. Уверен, это удобнее 4-ех терминалов разбросанных по разным экранам.

Выбор был между node webkit, Qt и javaFx. Node webkit испугал возможной прожорливостью, Qt имхо долгий в разработке/отладке.

tq-0todwfqdm7yh3p6yw8rmg7hw.gif
rp-kbu_loic63saiv7wpqwi2h3u.gif
hjnelhrqceyk24wahdtdjagflkm.gif

Первым делом была набросана блок схема

uljjxetxlvtur45_ohnukyzmo6u.png

И уже к вечеру был относительно рабочий проект.

Наверно нет смысла выкладывать здесь весь код. Проект есть на гите.

Представлю наиболее интересную на мой взгляд часть:

public class ConnectionData {
    @FXML
    public TextArea receiveData;
    @FXML
    public TextField sendData;
    @FXML
    public Button sendButton;

    private StringProperty sendDataProperty = new SimpleStringProperty("");
    BlockingQueue rxDataQueue = new LinkedBlockingQueue<>();

    @FXML
    public void initialize() {
        sendData.textProperty().bindBidirectional(sendDataProperty);
        sendButton.disableProperty().bind(sendDataProperty.isEmpty());

        Task task = new Task() {
            @Override
            public Void call() throws Exception {
                Platform.runLater(() -> new MessageConsumer(rxDataQueue, receiveData, rxDataQueue.size()).start());
                while(true) {
                    if(rxDataQueue.size() != 0) {
                        rxDataQueue.put(rxDataQueue.take().toString());
                        rxDataQueue.remove(0);
                    }
                    Thread.sleep(100);
                }
            }
        };
        new Thread(task).start();
    }

    public class MessageConsumer extends AnimationTimer {
        private final BlockingQueue messageQueue ;
        private final TextArea textArea ;
        private int messagesReceived = 0 ;
        public MessageConsumer(BlockingQueue messageQueue, TextArea textArea, int numMessages) {
            this.messageQueue = messageQueue ;
            this.textArea = textArea ;
        }
        @Override
        public void handle(long now) {
            List messages = new ArrayList<>();
            messagesReceived += messageQueue.drainTo(messages);
            messages.forEach(msg -> textArea.appendText(msg));
        }
    }

    public Button getPropertySendButton() {
        return sendButton;
    }

    public String getSendDataProperty() {
        String sendBuff = sendDataProperty.get();
        sendDataProperty.set("");
        return sendBuff;
    }

    public void clearReceiveData() {
        receiveData.textProperty().setValue("");
    }

    public void setReceiveData(byte[] buffer) {
        try {
            rxDataQueue.add(new String(buffer, "UTF-8"));
        } catch (UnsupportedEncodingException ex) {
            System.err.print(ex);
        }
    }
}


Класс формы для вывода принятого — TextField (sendData).

Если напрямую писать данные в буфер и отправлять сразу в TextField, очень скоро возникнет nullException, т.к. они в разных потоках.

Для этого, как не сложно догадаться, используется очередь — BlockingQueue rxDataQueue.

Данные принимаются с SerialPort (jssc) и помещаются в очередь через вызов setReceiveData.

Как только они помещаются, rxDataQueue.size () — становится (!=isEmpty). Task task заберет первый элемент и после отправки удалит его из очереди.

Что хочется получить дальше и что планируется из функционала:

— наверно требуется сохранение конфигов по портам
— ssh клиент (гибкий и интуитивно понятный!)
— добавление настраиваемого цвета к вкладкам
— довести интерфейс до приятного вида, css, JFoenix

Гит
Собранная сборка

© Habrahabr.ru