2

我正在通过 USB 连接将数据从我的 Java 应用程序推送到另一端的 Arduino。

Arduino 只能在其末尾缓冲 64 字节的数据,因此我必须限制从我的 Java 应用程序的每个“gulp”中发送的字节数(多余的字节将丢失)。当 Arduino 代码准备好接收更多字节时,它会发送一个简单的 ping 线路。

所以,我扩展BufferedOutputStream了一个类ArduinoBufferedOutputStream,它包装了实际的输出流。从 Java 应用程序的不同部分将任意数量的字节写入流(带有write(byte b)),并且流被偶尔flush编辑。

我需要(我猜)是覆盖BufferedOutputStreams flush 方法,以便在接收来自 Arduino 的 ping 之前,它不会发送超过 64 个字节,此时流应该发送 64 个更多字节(或更少)。

     static class ArduinoBufferedOutputStream extends BufferedOutputStream {

        public static final int WIRE_CAPACITY = 25;
        private byte[] waiting = new byte[0];
        private int onWire = 0;

        public ArduinoBufferedOutputStream(final OutputStream wrapped) throws IOException {
            super(wrapped, 500);
        }

        public void ping() {
            this.onWire = 0;
            this.flush();
        }

        @Override
        public void flush() throws IOException {
            if (this.onWire >= WIRE_CAPACITY) {
                return; // we're exceeding capacity, don't to anything before the next ping
            }
            if (this.count > WIRE_CAPACITY) {
                this.waiting = new byte[this.count - WIRE_CAPACITY];
                System.arraycopy(this.buf, WIRE_CAPACITY, waiting, 0, this.count - WIRE_CAPACITY);
                this.buf = Arrays.copyOfRange(this.buf, 0, WIRE_CAPACITY);
                this.count = WIRE_CAPACITY;
            } else {
                this.waiting = new byte[0];
            }
            onWire += this.count;
            super.flush();
            if (this.waiting.length > 0) {
                System.arraycopy(this.waiting, 0, this.buf, 0, Math.min(this.waiting.length, WIRE_CAPACITY));
                this.count = Math.min(this.waiting.length, WIRE_CAPACITY);
            }
        }
    }

但是,这不能正常工作。如果缓冲区包含多个WIRE_CAPACITY字节,则字节会丢失,如以下测试用例所示:

@Test
public void testStream() throws IOException {
    final ArduinoBufferedOutputStream stream = new ArduinoDisplayOutputStream.ArduinoBufferedOutputStream(System.out);
    stream.write("This is a very, very long string, which must be made even longer to demonstrate the problem".getBytes());
    stream.flush();
    stream.ping();
    stream.ping();
    stream.ping();
    stream.ping();
    stream.ping();
    stream.ping();
    stream.ping();
    stream.ping();
    stream.ping();
}

打印以下字符串:This is a very, very long string, which must be ma,而我显然希望打印整个字符串。

谁能看到我在这里做错了什么?或者更好的是,有人知道现有的图书馆可以满足我的要求吗?

4

3 回答 3

2

诚然,我对 Arduino 没有任何经验,但我在处理 Java 输出/输入流方面有很多经验。对此持保留态度,因为我无法在真正的 Arduino 设备上实际测试它,但如果我处于你的情况,这就是我会做的。

我怀疑您的调用stream.write("This is a very, very long string, which must be made even longer to demonstrate the problem".getBytes());实际上是在缓冲整个字符串。当您随后调用 时stream.flush(),所有字符串数据都会一次全部“写入”到 Arduino 设备。我怀疑这是因为您正在调用BufferedOutputStream大小为 500 字节的超级构造函数。首先,我会尝试将其减少到 64 字节。但是,我怀疑这也可能无法解决问题,因为BufferedOutputStream不知道何时调用ping.

我认为您将要同时覆盖writeand flushflush根据 Java 规范,背后的想法flush是应该强制任何未写入的缓冲字节写入底层流:

http://docs.oracle.com/javase/7/docs/api/java/io/OutputStream.html#flush ()

我也可能只是扩展 plain-oleOutputStream并自己做任何缓冲。我将重写该write方法以一次缓冲多达 64 字节的数据。如果 64 字节空间不足,请调用已super.write()填充的 64 字节缓冲区,然后调用super.flush()and then ping(),然后再缓冲/写入更多数据。在 while 循环中重复此过程,直到您的缓冲区有足够的空间来容纳输入数据的剩余部分。然后重写flush以写入任何未完全填满底层 64 字节缓冲区的数据(因此实际上并没有被先前的调用写入super.write())。

这样,如果您调用write太多数据,您的类将写入底层流,直到最多保留 64 个字节,您随后的调用flush将确保最后几个字节实际写入 Arduino。归根结底,您还将获得一个完全符合 Java 的OutputStream实现,这也很好。

编辑

我使用 64 字节只是因为你说你的 Arduino 一次可以处理那么多。当然,如果需要,可以减少,我只是为了方便使用“64”。

于 2012-11-05T22:12:42.860 回答
1

您的flush()写入WIRE_CAPACITY= 每次调用最多 25 个字节。您调用它两次,总输出为 50 个字节(假设 cp1252 编码)。我认为这没有错。

也许你应该在flush()里面打电话ping()?然后,您的测试将基本上变为:

@Test
public void testStream() throws IOException {
    final ArduinoDisplayOutputStream.ArduinoBufferedOutputStream stream = new ArduinoDisplayOutputStream.ArduinoBufferedOutputStream(System.out);
    stream.write("This is a very, very long string, which must be made even longer to demonstrate the problem".getBytes());
    stream.flush();
    stream.ping();
    stream.ping();
    ... // as many ping()s as written bytes / WIRE_CAPACITY
}
于 2012-11-05T22:15:35.100 回答
0

好吧,我想我说对了,最后。扩展BufferedOutputStream是一个死胡同。相反,我使用了一个单独的缓冲区,就像@ben-lawry 建议的那样。通过使用 aConcurrentLinkedQueue作为该缓冲区,我可以轻松地轮询WIRE CAPACITY每个flush().

有人可能会说效率不是很高,但与 Arduino 处理相比,Java 方面发生的事情仍然会很快。

这是我得到的:

   static class ArduinoBufferedOutputStream extends OutputStream {

        public static final int WIRE_CAPACITY = 25;
        private final ConcurrentLinkedQueue<Byte> buffer = new ConcurrentLinkedQueue<Byte>();
        private int onWire = 0;
        private OutputStream wrapped;

        public ArduinoBufferedOutputStream(final OutputStream wrapped) throws IOException {
            super();
            this.wrapped = wrapped;
        }

        @Override
        public void write(int i) throws IOException {
            this.buffer.add((byte) i);
        }

        @Override
        public void write(byte[] bytes) throws IOException {
            for (byte aByte : bytes) {
                this.buffer.add(aByte);
            }
        }

        @Override
        public void write(byte[] bytes, int off, int len) throws IOException {
            for (int i = off; i < len; i++) {
                this.buffer.add(bytes[i]);
            }
        }

        public void ping() throws IOException {
            synchronized (this.buffer) {
                this.onWire = 0;
            }
            this.flush();
        }

        @Override
        public void flush() throws IOException {
            synchronized (this.buffer) {
                for (; this.onWire < WIRE_CAPACITY && buffer.size() > 0; this.onWire++) {
                    wrapped.write(buffer.poll());
                }
            }
        }
    }

谢谢@gpeche 和@ben-lawry - 这肯定有助于让更多人关注这个问题。

于 2012-11-05T23:52:49.370 回答