Com порт в асинхронном режиме на Java

Com порт в асинхронном режиме на JavaТема COM порта в асинхронном режиме уже подымалась на нашем ресурсе. Данный пост это продолжение поднятой темы только с применением технологии Java. Итак, чем примечателен асинхронный режим работы COM порта? Прежде всего тем, что позволяет вести прием непрерывного пакета байт с переменной длиной или проще говоря принимать в каждый цикл приема различное, заранее неизвестное количество байт.

И тем не менее для надежного прима информационного пакета в каждый отдельный цикл прима, передающая сторона должна передавать в начале  пакета синхросимволы с последующим указанием, например в двух байтах,   длины в байтах всего пакета. Обычно для этих целей применяется последовательность синхросимволов, например 0xA1 0xa2 или 0x55 0xaa или 0xFF, 0xFF, 0xFF, 0xFF, 0xFF. Такой простейший прием дает возможность надежной прграммной синхронизации начала приема, а наличие в принимаемом пакете, данных о его длине,  возможность формирования программного флага конца приема.

В предыдущем посте для загрузки в программный буфер всего информационного пакета предлагался вариант с таймаутом устанавливаемым в обработчике события появления данных в приемном буфере COM порта и формированием ответного пакета после окончания таймаута. Но если, допустим, необходимо сформировать ответ сразу после окончания прима, то такой вариант не подойдет. Для организация алгоритма обмена типа пинг-понг можно воспользоваться синхросимволами начала принимаемого пакета и данными об общем количестве байт принимаемой информации. В этом случае информацию в программный буфер из буфера COM порта можно перегружать порциями при каждом входе в обработчик события появления данных в приемном буфере порта.

Поскольку при каждом входе в обработчик имеется точная информация о количестве байт в приемном буфере COM порта, то с помощью программного счетчика можно подсчитывать общее количество байт накапливаемой информации в программном буфере и формировать флаг окончания прима. По флагу окончания приема можно выполнять отправку ответного пакета. Такой вариант обмена информацией не требует таймаута и работает по принципу пинг-понг, что во многих случаях является самым оптимальным.

По такому принципу и построено тестовое десктопное приложение на Java. Приложение принимает COM пакет переменной длины до 4000 байт и отображает его в HEX виде в окне приема. Скорость 115200 бод. По умолчанию в приложении установлен порт COM1. Для тестирования приложения  используется такой-же вариант как и в предыдущем посте - применение известной программы-монитора COM порта COM Port Toolkit.

COM порт на Java

Java довольно специфический язык программирования. В нем широко используется принцип ООП и программирование имеет свои особенности непохожие ни на какой другой язык программирования. Если еще совсем недавно десктопные приложения на Java нельзя было сравнивать с десктопными приложениями на C++ ни по скорости ни по функциональности, то на сегодняшний день все практически выровнялось.

Ниже приведен код приложения для асинхронного приема  по COM порту на Java. А в конце поста собственно архив проекта для Net Beans 7.2.


package javacomport;

// все необходимые импорты
import java.awt.Color;
import java.awt.Container;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import jssc.SerialPort;
import jssc.SerialPortEvent;
import jssc.SerialPortEventListener;
import jssc.SerialPortException;
import jssc.SerialPortList;

// Основной класс 
public class JavaComPort {

    // Емкость буфера приема
    public static final int  BUFSIZE_RD = 4096;
    // Строка приема 
    private String readData = null;
    // Флаг открытия порта
    private boolean pOpen = false; 
    // Главный счетчик принимаемых байт
    private short comAllCount = 0;
    // Программный приемный буфер
    private byte [] bufrd = new byte [BUFSIZE_RD];
    // Локальный счетчик принимаемых байт 
    private int bufrdCount = 0;
    // Счетчик циклов приема
    private int countCom = 0; 
    
//----------------------------------------------------------------------
// создание объектов
    
    private String comPort = "COM1";
    private SerialPort serialPort = new SerialPort(comPort);
    private PortReader portReader = new PortReader();
    private JButton stop = new JButton("Stop");
    private JButton start = new JButton("Start");
    private JTextArea input = new JTextArea("input");
    private JScrollPane scrollPaneInput = new JScrollPane(input);
    private JLabel label1 = new JLabel("nbCom");
    private JLabel label2 = new JLabel("Test1");
    private JLabel label3 = new JLabel("Test2");
    private JLabel label4 = new JLabel("Test3");

//----------------------------------------------------------------------
// Inner классы

    // расширение класса JFrame
    // Десктопная форма 
    public class Form extends JFrame{

        // конструктор класса Form (должен иметь тоже имя Form)
        public Form() {
            // инициализация компонентов
            initComponents();
        }
        
        // метод инициализации компонентов формы
        private void initComponents(){
            // положение на экране
            setBounds(150,130,800,600);
            // размер формы
            setSize(830, 600);
            // Закрытие формы 
            setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            // Контейнер для размещения компонентов формы
            Container container = getContentPane();
            // установить разметку
            container.setLayout(null);
            container.setBounds(5,5,800,600);

    //----------------------------------------------------------------------
    // JTextArea

            // Добавление JTextArea input
            input.setLineWrap(true);
            input.setColumns(20);
            input.setRows(5);
            input.setBounds(10,220,790,300);
            container.add(input);
            // Добавление скрола
            scrollPaneInput.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);
            scrollPaneInput.setBounds(10,220,800,300);
            container.add(scrollPaneInput);
            scrollPaneInput.setViewportView(input);  

    //----------------------------------------------------------------------
    // JLabel

            // добавление метки состояния COM порта
            label1.setBounds(10,5,120,20);
            label1.setText(comPort + " Is close");
            // прозрачный фон
            label1.setOpaque(true);
            label1.setForeground(Color.red);
            container.add(label1);
            // 
            label2.setBounds(10,190,70,20);
            label2.setText("BufRd");
            container.add(label2);
            //
            label3.setBounds(600,530,150,20);
            label3.setText("Jast read bytes");
            container.add(label3);
            // 
            label4.setBounds(20,530,250,20);
            label4.setText("The number of info bytes in a packet");
            container.add(label4);

    //----------------------------------------------------------------------
    // JButton

            // зарегистрировать экземпляр класса обработчика события start
            start.addActionListener(new startEventListener());
            // добавить кнопку и ее положение
            start.setBounds(440,155,80,25);
            container.add(start);

            // зарегистрировать экземпляр класса обработчика события stop
            stop.addActionListener(new stopEventListener());
            stop.setBounds(730,155,80,25);
            container.add(stop);
        } 
    }
    
    // клас имплементации события нажатия start
    class startEventListener implements ActionListener {
        @Override
        // обработка события нажатия на button start
        public void actionPerformed(ActionEvent e) {
            if(serialPort.isOpened() == false)
            {    
                try {
                    serialPort = new SerialPort(comPort);
                    label1.setText(comPort + " Is open");
                    label1.setForeground(Color.green);
                    //Открываем порт
                    serialPort.openPort();
                    pOpen = true;
                    //Выставляем параметры
                    serialPort.setParams(SerialPort.BAUDRATE_115200,
                                         SerialPort.DATABITS_8,
                                         SerialPort.STOPBITS_1,
                                         SerialPort.PARITY_NONE);
                    //Устанавливаем ивент лисенер и маску
                    serialPort.addEventListener(portReader, SerialPort.MASK_RXCHAR);
                }
                catch (SerialPortException ex) {
                    System.out.println(ex);
                    pOpen = false;
                }
            }
        }  
    }
    
    // клас имплементации события нажатия stop
    class stopEventListener implements ActionListener {
        @Override
        // обработка события нажатия на button stop
        public void actionPerformed(ActionEvent e) {
                
            // закрытие COM порта
            if(serialPort.isOpened() == true) {
                try {
                    serialPort.purgePort(SerialPort.PURGE_RXCLEAR);
                    serialPort.closePort();
                    label1.setText(comPort + " Is close");
                    label1.setForeground(Color.red);
                    System.out.println("COM остановлен");
                    readData = "";
                } catch (SerialPortException ex) {
                    Logger.getLogger(JavaComPort.class.getName()).log(Level.SEVERE, null, ex);
                }
            }
        }
    }
    
    // клас имплементации события приема данных в com порт
    class PortReader implements SerialPortEventListener {
        
        short comCount;
        short comLenght = 0; 
        private byte[] arrReadComByte = new byte [BUFSIZE_RD];

        @Override
        public void serialEvent(SerialPortEvent event) {

        if(pOpen == true){
            if(event.isRXCHAR() && event.getEventValue() > 0){
                // Получаем ответ от устройства, обрабатываем данные и т.д.
                try {
                    // Количество принятых байт в одном событии 
                    comCount = (short)serialPort.getInputBufferBytesCount();
                    // Счетчик-накопитель принятых байт 
                    comAllCount = (short) (comAllCount + comCount);
                    // Байты из буфера COM в матрицу arrReadComByte
                    arrReadComByte = serialPort.readBytes(comCount);
                    // Вход если в буфере COM имеются данные 
                    if(comCount > 1){
                        // Поиск синхросимволов и данных о длине пакета
                        for(int i = 0; i < comCount; i++){
                            if(arrReadComByte[i] == (byte)0xa1 && arrReadComByte[i + 1] == (byte)0xa2) {
                                comLenght = (short) (((arrReadComByte[i + 2] & 0x00ff) << 8 | (arrReadComByte[i + 3] & 0x00ff)) + 4);
                                System.out.println("comLenght " + comLenght);
                            }
                            else break;
                        }
                    }
                    // Заполнение приемного буфера и строки readData 
                    for (int i = 0; i < comCount; i++){
                        bufrd[bufrdCount] = arrReadComByte[i];
                        readData = readData + String.format("%02X ", bufrd[bufrdCount]);
                        bufrdCount++;
                    }
                    // Принятм весь павкет 
                    if(comAllCount >= comLenght){
                        countCom++;
                        label2.setText(String.format("%d ", countCom));
                        // всего считано байт
                        label3.setText("Jast read bytes  " + Integer.toString(comAllCount));
                        label4.setText("The number of info bytes in a packet  " + Integer.toString(comAllCount - 4));
                        // обнулить глобальный счетчик байт 
                        comAllCount = 0;
                        input.setText(null);
                        // вывод принятых данных в окно input
                        onDataReceived(readData);
                        // очистка строки приема
                        readData = "";
                        bufrdCount = 0;
                    }
                }
                catch (SerialPortException ex) {
                    System.out.println(ex);
                }
                }
            }
        }
        // метод отображения данных в input
        public void onDataReceived(String readData) {
            input.append(readData);
        }
    }

//----------------------------------------------------------------------
// Методы

    // вывод списка портов
    public static void nbport (){
        String[] portNames = SerialPortList.getPortNames();
        for(int i = 0; i < portNames.length; i++){
           System.out.println(portNames[i]);
        }
    }

//----------------------------------------------------------------------
// Метод Main
    
    public static void main(String[] args) {
        
        // Создание объектов 
        JavaComPort javaComPort = new JavaComPort();
        JavaComPort.Form form = javaComPort.new Form();
        
        // Вывод списка COM портов
        nbport();
        // Запуск формы
        form.setVisible(true);

    }
}

Да, для написания приложения  использовалась Java библиотека JSSC2.8. Библиотека встроена в проект. Искать ничего не нужно, библиотеку можно использовать и в любом другом проекте как есть. Обильные комменты в коде помогут  уяснить  как все крутится. Дерзайте и не забывайте оставлять свои комменты в виде предложений,  замечаний и дополнений по теме.    

Top.Mail.Ru