Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/347.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
java应用程序中通过RxTx从两个串行端口并发读取的问题 关于我的问题的一些背景_Java_Multithreading_Serial Port_Rxtx_Serial Communication - Fatal编程技术网

java应用程序中通过RxTx从两个串行端口并发读取的问题 关于我的问题的一些背景

java应用程序中通过RxTx从两个串行端口并发读取的问题 关于我的问题的一些背景,java,multithreading,serial-port,rxtx,serial-communication,Java,Multithreading,Serial Port,Rxtx,Serial Communication,我有一个从两个串行端口读取数据的应用程序。这些串行端口在我工作的公司定制的两个硬件之间接入消息。一个串行端口接入从硬件A到硬件B的消息,而另一个端口在另一个方向上传输消息。我需要读取、解析和显示这些消息,以帮助调试我们在明显删除和重复消息时遇到的问题最重要的是,我的应用程序必须在消息发送时显示消息(即,按照发送顺序和发送次数),并带有正确的时间戳。 我的应用程序是用java编写的,通过RxTx和串行端口接口。我一直在64位Windows 7 PC上运行我的应用程序 绝大多数(双向)发送的消息都是

我有一个从两个串行端口读取数据的应用程序。这些串行端口在我工作的公司定制的两个硬件之间接入消息。一个串行端口接入从硬件A到硬件B的消息,而另一个端口在另一个方向上传输消息。我需要读取、解析和显示这些消息,以帮助调试我们在明显删除和重复消息时遇到的问题最重要的是,我的应用程序必须在消息发送时显示消息(即,按照发送顺序和发送次数),并带有正确的时间戳。

我的应用程序是用java编写的,通过RxTx和串行端口接口。我一直在64位Windows 7 PC上运行我的应用程序

绝大多数(双向)发送的消息都是请求确认(这些请求的确认)。每个请求消息包含一个序列号,相应的ACK将包含相同的序列号。当系统不忙时,我的程序似乎工作得近乎完美。但是,当消息开始很快出现时,我的程序会遇到问题

我遇到的问题是,我的应用程序似乎从一个串行端口读取多条消息,然后从另一个端口读取,而不是几乎同时从两个端口读取

我的应用程序的核心使用5个线程。其中两个读卡器只从端口读取一个字节,然后将该字节放入队列。每个队列都与另一个线程共享,该线程将这些字节解析为消息。(因此有两个处理线程,每个读卡器一个)。然后,这两个处理线程将这些已解析的消息放入另一个队列。第五个线程管理这些消息的显示,然后从该队列获取消息

(上面未提及的是我的GUI,它通过EDT运行)

在我看来,问题似乎在于(或可能在之前)读线程,它似乎与并发性或共享CPU时间片有关。我只是不知道这种阻塞是发生在操作系统级别(操作系统从一个端口读取而不是从另一个端口读取)、RxTx级别还是我的代码中

我的读者线程代码在这个问题的底部

我的理由是:
  • 我给每个字节添加了一个时间戳,该时间戳包括微秒近似值(通过改编自的技术获得)。当我看到我从一个端口(端口A)读取多条消息,然后从另一个端口(端口B)读取多条消息时,我注意到时间戳(以及读取时间)从端口B读取的第一条消息的第一个字节的时间发生在从端口A读取的任何消息的第一个字节的时间戳之后。换句话说,尽管读取每个字节后调用了
    thread.yield()
    ,端口A读取器线程似乎从未向端口B读取器线程屈服(如本问题底部的读者线程代码所示)。为了举例说明,我看到了如下消息序列:

    [From Hardware A, To Hardware B] [Timestamp: 0 us] Request for 0x01
    [From Hardware A, To Hardware B] [Timestamp: 60 us] ACK for 0x02
    [From Hardware A, To Hardware B] [Timestamp: 120 us] ACK for 0x03
    [From Hardware B, To Hardware A] [Timestamp: 180 us] ACK for 0x01
    [From Hardware B, To Hardware A] [Timestamp: 240 us] Request for 0x02
    [From Hardware B, To Hardware A] [Timestamp: 300 us] Request for 0x03
    
    [From Hardware A, To Hardware B] [Timestamp: 0 us] Request for 0x01
    [From Hardware B, To Hardware A] [Timestamp: 60 us] ACK for 0x01
    [From Hardware B, To Hardware A] [Timestamp: 120 us] Request for 0x02
    [From Hardware A, To Hardware B] [Timestamp: 180 us] ACK for 0x02
    [From Hardware B, To Hardware A] [Timestamp: 240 us] Request for 0x03
    [From Hardware A, To Hardware B] [Timestamp: 300 us] ACK for 0x03  
    
    但我希望这些信息会像这样到达:

    [From Hardware A, To Hardware B] [Timestamp: 0 us] Request for 0x01
    [From Hardware A, To Hardware B] [Timestamp: 60 us] ACK for 0x02
    [From Hardware A, To Hardware B] [Timestamp: 120 us] ACK for 0x03
    [From Hardware B, To Hardware A] [Timestamp: 180 us] ACK for 0x01
    [From Hardware B, To Hardware A] [Timestamp: 240 us] Request for 0x02
    [From Hardware B, To Hardware A] [Timestamp: 300 us] Request for 0x03
    
    [From Hardware A, To Hardware B] [Timestamp: 0 us] Request for 0x01
    [From Hardware B, To Hardware A] [Timestamp: 60 us] ACK for 0x01
    [From Hardware B, To Hardware A] [Timestamp: 120 us] Request for 0x02
    [From Hardware A, To Hardware B] [Timestamp: 180 us] ACK for 0x02
    [From Hardware B, To Hardware A] [Timestamp: 240 us] Request for 0x03
    [From Hardware A, To Hardware B] [Timestamp: 300 us] ACK for 0x03  
    
  • 我知道这个问题存在于我的代码中,而不是其他硬件中,因为我看到的是在ACK响应的请求之前到达的时间戳ACK。这在编程上是不可能的,因为发送ACK的硬件不知道序列号或消息类型如果在发送ACK之前未收到请求,则发送

  • 我已经将读取器线程在队列中放置已读取字节所需的时间减少到大约1.5usec(基于读取和排队500000字节后的平均基准测试)。我所看到的消息传入的最快速度约为每50到60 uSec 1条消息,因此,如果两个读卡器线程在读取一个字节后相互让步,我相信从每个串行端口上的消息读取的第一个字节的时间戳相当准确

  • 底线 我所遇到的解决方案包括更改、以某种方式修复我的代码(如果问题出在代码中)、使用基于linux的操作系统在PC上运行我的应用程序,或者以某种方式至少以低于java的语言重新编写我的应用程序的读取组件

    这些解决方案中的每一个都可能是我需要的解决方案,或者它们可能是一条死胡同和一个巨大的时间浪费。希望你们中的一个人以前处理过这个问题,或者看到了一个我没有看到的问题,这样我就可以减少我在处理这个问题时所浪费的时间

    我的读者逻辑 My reader是一个私有内部类。在其包含类中定义了以下四个常量:

    public static final long BYTE_MASK = 0x7F80000000000000L;
    public static final int BYTE_SHIFT = 55;
    public static final long TIMESTAMP_MASK = 0x007FFFFFFFFFFFFFL;
    public static final long TIMESTAMP_SHIFT = 0;
    
    comOneReader = new SerialPortReader(commInput[0],
                                        comOneInputByteQueue);
    comTwoReader = new SerialPortReader(commInput[1],
                                        comTwoInputByteQueue);
    
    Thread comOne = new Thread(comOneReader, "COM1 Reader");
    Thread comTwo = new Thread(comTwoReader, "COM2 Reader");
    
    comOne.setPriority(Thread.MAX_PRIORITY);
    comTwo.setPriority(Thread.MAX_PRIORITY);
    
    comOne.start();
    comTwo.start();
    
    创建
    SerialPort
    s及其相应的
    InputStream
    s.
    COMM\u PORT\u ID
    是我所需COM端口的
    CommPortIdentifier
    对象数组。
    SerialPort
    是所需COM端口的
    SerialPort
    对象数组。
    commInput
    是用于所需COM端口的
    InputStream
    对象数组。
    所有这些都发生在包含类中

    for (int i = 0; i < 2; i++)
    {
          serialPorts[i] = (SerialPort) COMM_PORT_ID[i].open("SERIAL CONNECTION", 2000);
          serialPorts[i].setSerialPortParams(BAUD_RATE, DATA_BITS, STOP_BITS, PARITY);
          serialPorts[i].setFlowControlMode(SerialPort.FLOWCONTROL_NONE);
          serialPorts[i].enableReceiveTimeout(1000);
          serialPorts[i].setInputBufferSize(3);
          serialPorts[i].disableReceiveThreshold();
          serialPorts[i].notifyOnDataAvailable(false);
          serialPorts[i].notifyOnFramingError(true);
          serialPorts[i].notifyOnOutputEmpty(false);
          serialPorts[i].notifyOnOverrunError(true);
          commInput[i] = serialPorts[i].getInputStream();
    }
    
    读者自己:

    private class SerialPortReader implements Runnable
    {
        private final long startSystemTime;
        private final long startNano;
        private final InputStream input;
        private AtomicBoolean reading; // used to stop and start the reading of bytes
        private final LinkedTransferQueue<Long> toProcessorByteQueue;
        private long holder;
    
        public SerialPortReader(InputStream input,
                                LinkedTransferQueue<Long> toProcessorByteQueue)
        {
            this.input = input;
            this.toProcessorByteQueue = toProcessorByteQueue;
            reading = new AtomicBoolean();
            startSystemTime = System.currentTimeMillis();
            startNano = System.nanoTime();
        }
    
        @Override
        public void run()
        {
            long in = -1;
            reading.set(true);
            while (reading.get())
            {
                try
                {
    
                    in = -1;
    
                    // Read the next byte from the serial port, blocking until one is available
                    if ((in = input.read()) != -1)
                    {   
                        // I store the byte and a timestamp (in microseconds) in a long
                        // Skipping bit 63 (the sign bit), I will store the byte from the serial read
                        // in bits 62 - 55.  Then the remainder (bits 54-0) will be the timestamp                  
                        holder = ((in & 0xFF)) << BYTE_SHIFT;
                        holder |= ((startSystemTime * 1000L) + ((System.nanoTime() - startNano) / 1000L)) & TIMESTAMP_MASK;
    
                        // Add the long to the queue
                        toProcessorByteQueue.add(holder);                      
                    }
    
                    Thread.yield();
                }
                catch (IOException ioe)
                {
                    // Deal with Exception
                }
            }
        }
    
        public void stop()
        {
            reading.set(false);
        }
    
    }
    
    私有类SerialPortReader实现可运行
    {
    专用最终长启动系统时间;
    私人决赛长startNano;
    私有最终输入流输入;
    private AtomicBoolean reading;//用于停止和开始读取字节
    私有最终链接传输队列到ProcessorByteQueue;
    私人多头持有人;
    公共SerialPortReader(InputStream输入,