JavaFx, простой терминал COM порта
В жизни embedded разработчика, часто возникает потребность взаимодействия с серийным портом
И пускай RS-232 почти полностью вытеснен современными интерфейсами, UART похоже никуда не собирается уходить
Большинство модулей (WI-FI, IoT и др.), демоплат/одноплатников работают или имеют на борту UART.
Терминальных программ огромное множество, самые заметные — Putty и termianl v1.9b
Они отлично справляются с задачами, но сложности начинаются, когда открытых соединений больше одного.
На переключение и поиск нужного окна уходит много времени.
Тогда возникла идея, почему бы не написать терминал, где каждое соединение будет в отдельной вкладке, и каждый пришедший пакет будет на ней сигнализировать. Уверен, это удобнее 4-ех терминалов разбросанных по разным экранам.
Выбор был между node webkit, Qt и javaFx. Node webkit испугал возможной прожорливостью, Qt имхо долгий в разработке/отладке.
Первым делом была набросана блок схема
И уже к вечеру был относительно рабочий проект.
Наверно нет смысла выкладывать здесь весь код. Проект есть на гите.
Представлю наиболее интересную на мой взгляд часть:
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
Гит
Собранная сборка