Java串口,有效处理serialEvent

标签 java multithreading serial-port rxtx

使用 RXTX 库时,我在串行事件(数据到达时)遇到过这种情况,我不知道如何引用已经运行的主应用程序。

我发现使其工作的方法是在 endOfMessageFlag 到达时关闭串行端口(这样当我之后尝试发送更多数据时,我不会收到“锁定文件已存在错误”)并进行处理消息的类的新实例。

我不喜欢这个“解决方案”,因为我担心效率不是很高,如果许多消息一起到达,我什至可能会遇到 stackoverflow 异常。

我在 raspbian 上使用 rxtx-2.2-pre2。

我有三个类,一个是主类,一个是协议(protocol)类,一个是处理串行端口的类(初始化、发送、(接收)serialEvent)。

主类片段:

package app;
public class MainClass {

    private MyProtocol myProt = new MyProtocol();
    public boolean newCDevices = false;
    public int newDevices = 0;

    public static void main(String[] args) throws Exception {
        System.setProperty("gnu.io.rxtx.SerialPorts", "/dev/ttyAMA0"); 
        MainClass myMain = new MainClass();
        monitorS.findDevices();
    }

    public void messageReceived(byte[] message){
        myProt.processMessage(message);
        myProt.sendAcknowledge();
    }

    private void findDevices(){
        myProt.findNewDevices();
    }
}

协议(protocol)类片段:

package serialcomms;
public class MyProtocol {

    private static SerialComms messageSender = new SerialComms();

    // sends acknowledge onReceive.
    public void sendAcknowledge(){        
        byte[] messageBytes = composeMessage(acknowledge);
        if(messageSender.initialize()){
            messageSender.sendData(messageBytes);
            try { Thread.sleep(2000); } catch (InterruptedException ie) {}
            messageSender.close();
        }
    }

    //sends broadcast message asking all unidentified devices to make contact.
    public void findNewDevices(){
        byte[] messageBytes = composeMessage(findDevicesMessage);
        if(messageSender.initialize()){
            messageSender.sendData(messageBytes);
            try { Thread.sleep(2000); } catch (InterruptedException ie) {}
            messageSender.close();
        }
    }
}

以及处理串口的类: (基于此示例顺便说一句:http://www.drdobbs.com/jvm/control-an-arduino-from-java/240163864)

    package serialcomms;

    import gnu.io.CommPortIdentifier;
    import gnu.io.SerialPort;
    import gnu.io.SerialPortEvent;
    import gnu.io.SerialPortEventListener;
    import java.io.BufferedReader;
    import java.io.InputStreamReader;
    import java.io.OutputStream;
    import java.time.Clock;
    import java.util.Enumeration;

    public class SerialComms implements SerialPortEventListener {

        SerialPort serialPort = null;

        private static final String PORT_NAMES[] = { 
  //      "/dev/tty.usbmodem", // Mac OS X
//        "/dev/usbdev", // Linux
        "/dev/ttyAMA0", // Raspberry
//        "/dev/tty", // Linux
//       "/dev/serial", // Linux
//        "COM3", // Windows
    };

    private String appName;
    private BufferedReader input;
    private OutputStream output;  
    private int tail = 0;

    int lengthArray = 50;
    public byte[] buffer = new byte[lengthArray];

    private static final int TIME_OUT = 1000; // Port open timeout
    private static final int DATA_RATE = 9600; // Arduino serial port

    private static final int BYTE_START = 100;
    private static final int BYTE_END = 120;
    private final byte startOfMessage = (byte)BYTE_START;
    private final byte endOfMessage = (byte)BYTE_END;

    public SerialComms(){
        appName = getClass().getName();
    }

    public boolean initialize() {
        try {
            CommPortIdentifier portId = null;
            Enumeration portEnum = CommPortIdentifier.getPortIdentifiers();

            // Enumerate system ports and try connecting to Arduino over each
            //
            System.out.println( "Trying:");
            while (portId == null && portEnum.hasMoreElements()) {
                // Iterate through your host computer's serial port IDs
                //
                CommPortIdentifier currPortId = (CommPortIdentifier) portEnum.nextElement();
                System.out.println( "   port" + currPortId.getName() );
                for (String portName : PORT_NAMES) {
                    if ( currPortId.getName().equals(portName) 
                      || currPortId.getName().startsWith(portName)) {

                        // Try to connect to the Arduino on this port
                        //
                        // Open serial port
                        serialPort = (SerialPort)currPortId.open(appName, TIME_OUT);
                        portId = currPortId;
                        System.out.println( "Connected on port" + currPortId.getName() );
                        break;
                    }
                }
            }

            if (portId == null || serialPort == null) {
                System.out.println("Oops... Could not connect to Arduino");
                return false;
            }

            // set port parameters
            serialPort.setSerialPortParams(DATA_RATE,
                            SerialPort.DATABITS_8,
                            SerialPort.STOPBITS_1,
                            SerialPort.PARITY_NONE);

            // add event listeners
            serialPort.addEventListener(this);
            serialPort.notifyOnDataAvailable(true);

            // Give the Arduino some time
            try { Thread.sleep(2000); } catch (InterruptedException ie) {}

            return true;
        }
        catch ( Exception e ) { 
            e.printStackTrace();
        }
        return false;
    }

    public void sendData(byte[] data) {
        try {
            System.out.println("Sending data: '" + data +"'");
            // open the streams and send 
            output = serialPort.getOutputStream();
            output.write( data );
        } 
        catch (Exception e) {
            System.err.println(e.toString());
            System.exit(0);
        }
    }

    //
    // This should be called when you stop using the port
    //
    public synchronized void close() {
        if ( serialPort != null ) {
            serialPort.removeEventListener();
            serialPort.close();
        }
    }


    //
    // Handle serial port event
    //
    public synchronized void serialEvent(SerialPortEvent oEvent) {
        try {
            while (oEvent.getEventType() == SerialPortEvent.DATA_AVAILABLE){

                    byte singleData = (byte)serialPort.getInputStream().read();
                    if (singleData == startOfMessage){
                        for(int i = 0; i < tail ; i++){
                            buffer[i] = 0x00;
                        }
                        tail = 0;
                        buffer[tail] = singleData;
                        tail++;

                    } else if(singleData == endOfMessage && tail <= buffer.length){ 
                        buffer[tail] = singleData;  
                        tail++;
                        for(int i = 0; i< tail ; i++){
                            System.out.println(buffer[i]);
                        }
                        close();                                //This are the lines that got it to
                        MainClass newMain = new MainClass();    //work. However I think there must
                        newMain.messageReceived(buffer);        //be a better solution.
                    } else if(tail < buffer.length){  
                        buffer[tail] = singleData;  
                        tail++;  
                    }

                    break;           
               default:
                    break;

    }
        } 
        catch (Exception e) {
            System.err.println(e.toString());
        }
    }
}

编辑了 SerialComms 上的 serialEvent 方法,以便它将接收所有可用数据,而不必为接收到的每个字节而中断。

最佳答案

我找到了一个更好且非常简单的解决方案来解决我的问题,即在我的主类上声明一个静态字节数组并将接收到的消息存储在那里。

我将重新发布修改后的代码部分:

public class MainClass{
private static MyProtocol myProt = new MyProtocol();
    public static byte[] message = new byte[50];
    public boolean newMessage = false;
    private boolean runApp = true;
    public int newColectors = 0;
    private static final int START_OF_MESAGE = 100;


    public static void main(String[] args) throws Exception {
        // TODO code application logic here
        System.setProperty("gnu.io.rxtx.SerialPorts", "/dev/ttyAMA0"); 
        MainClass myApp = new MainClass();

        while(myApp.runApp){
            if(myApp.checkMessageReceived()){
                myProt.processMessage(message);
                myProt.sendAcknowledge();
            }
            myApp.findNewCollectors();

            try { Thread.sleep(2000); } catch (InterruptedException ie) {}
        }
    }

    public boolean checkMessageReceived(){
        if(message[0]== (byte) START_OF_MESAGE){
            newMessage = true;
        }
        return newMessage;
    }

}

并添加串行端口处理程序类:

MainClass.message = buffer;

而不是:

close();                                //This are the lines that got it to
MainClass newMain = new MainClass();    //work. However I think there must
newMain.messageReceived(buffer);        //be a better solution.

数组的数组是我计划用来处理消息并发的实际解决方案。 现在就这样:) 如果有人有更好的答案,我很乐意阅读。

关于Java串口,有效处理serialEvent,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/25741323/

相关文章:

java - 如何将位图附加到电子邮件android

java - Spring 可缓存 - 使用 SpEL 过滤掉空集合

c# - TaskFactory.StartNew() 中的 "cancellationToken"有什么用?

c# - WPF 中的 SerialPort 抛出 I/O 异常

python - 使用 Python 通过串行通信的最小延迟

c++ - 错误的 libserial 接收数据序列

java - Fragments 中的 Android FATAL 异常

java - 异步 API 设计客户端

php - Cs购物车产品查询很慢

java 输入流卡住