1

我需要向串行连接写入一些字节。但是我在JSSC库中找不到设置写入超时的内容。我需要这个超时,因为如果我设置了硬件流控制并移除了电缆,我的应用程序就会卡住等待 CTS 信号。

更新

我用 Future 对象尝试了这个解决方法:

    ExecutorService executor = Executors.newSingleThreadExecutor();
    ...
    public synchronized void write(byte[] content, int timeout) throws InterruptedException, SerialPortException{
            long starttime = System.currentTimeMillis();
            Future<Boolean> future = executor.submit(new Callable<Boolean>() {
                public Boolean call() throws Exception {
                    serialPort.writeBytes(content);
                    return new Boolean(true);
                }
            });
            try {
                future.get(timeout, TimeUnit.MILLISECONDS);
                log.debug("Duration: {}",DurationFormatUtils.formatDuration(System.currentTimeMillis() - starttime, "mm:ss.SS"));
            } catch (ExecutionException e) {
                throw new HardwareException(e.getMessage());
            } catch (TimeoutException e) {
                throw new HardwareException("Impossibile scrivere nella porta seriale (timeout)");
            }
        }

但是效果不是很好,通过COM口256000波特写550byte需要4s...

尝试直接写:

    public synchronized void write(byte[] content, int timeout) throws InterruptedException, SerialPortException{
        try {
            long starttime = System.currentTimeMillis();
            serialPort.writeBytes(content);
            log.debug("Duration: {}",DurationFormatUtils.formatDuration(System.currentTimeMillis() - starttime, "mm:ss.SS"));
        } catch (SerialPortException e) {
            throw new HardwareException(e.getMessage());
        }
    }

正如预期的那样花了0.5s!

问题似乎是 main 方法中的“同步”关键字,为什么?

4

2 回答 2

1

我有同样的问题。我通过启动两个线程解决了这个问题:一个写一个等待特定的时间。根据完成的第一个,写入是成功还是超时。这是我使用的不同类:

ByteWriter:通用字节写入的接口(我希望能够从 JSSC 切换到任何其他框架

package net.femtoparsec.jssc;

import java.io.IOException;

public interface ByteWriter {

    void write(byte[] bytes) throws IOException;

    void write(byte oneByte) throws IOException;

    void write(byte[] bytes, long timeout) throws IOException, InterruptedException;

    void write(byte oneByte, long timeout) throws IOException, InterruptedException;

    void cancelWrite() throws IOException;

}

JsscByteWriter : Jssc 的 ByteWriter 实现

package net.femtoparsec.jssc;

import jssc.SerialPort;
import jssc.SerialPortException;

import java.io.IOException;

public class JsscByteWriter implements ByteWriter {

    private final SerialPort serialPort;

    public JsscByteWriter(SerialPort serialPort) {
        this.serialPort = serialPort;
    }

    @Override
    public void cancelWrite() throws IOException {
        try {
            serialPort.purgePort(SerialPort.PURGE_TXABORT);
            serialPort.purgePort(SerialPort.PURGE_TXCLEAR);
        } catch (SerialPortException e) {
            throw new IOException(e);
        }
    }

    @Override
    public void write(byte[] bytes) throws IOException {
        try {
            serialPort.writeBytes(bytes);
        } catch (SerialPortException e) {
            throw new IOException(e);
        }
    }

    @Override
    public void write(byte oneByte) throws IOException {
        try {
            serialPort.writeByte(oneByte);
        } catch (SerialPortException e) {
            throw new IOException(e);
        }
    }

    @Override
    public void write(byte[] bytes, long timeout) throws IOException, InterruptedException {
        if (timeout <= 0) {
            this.write(bytes);
        }
        else {
            new TimedOutByteWriting(this, bytes, timeout).write();
        }
    }

    @Override
    public void write(byte oneByte, long timeout) throws IOException, InterruptedException {
        if (timeout <= 0) {
            this.write(oneByte);
        }
        else {
            new TimedOutByteWriting(this, oneByte, timeout).write();
        }
    }


}

TimedOutByteWriting :执行写入超时的类。

package net.femtoparsec.jssc;

import java.io.IOException;
import java.util.Objects;
import java.util.concurrent.*;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

class TimedOutByteWriting {

    private final ByteWriter byteWriter;

    private final boolean onlyOneByte;

    private final byte oneByte;

    private final byte[] bytes;

    private final long timeout;

    private static final ExecutorService EXECUTOR_SERVICE = Executors.newCachedThreadPool(r -> {
        Thread t = new Thread(r, "TimedOutByteWriting Thread");
        t.setDaemon(true);
        return t;
    });

    TimedOutByteWriting(ByteWriter byteWriter, byte oneByte, long timeout) {
        if (timeout <= 0) {
            throw new IllegalArgumentException("Invalid time out value : "+timeout+". Must be greater than 0");
        }
        this.byteWriter = Objects.requireNonNull(byteWriter, "byteWriter");
        this.bytes = null;
        this.oneByte = oneByte;
        this.timeout = timeout;
        this.onlyOneByte = true;
    }

    TimedOutByteWriting(ByteWriter byteWriter, byte[] bytes, long timeout) {
        if (timeout <= 0) {
            throw new IllegalArgumentException("Invalid time out value : "+timeout+". Must be greater than 0");
        }
        this.byteWriter = Objects.requireNonNull(byteWriter, "byteWriter");
        this.bytes = Objects.requireNonNull(bytes, "bytes");
        this.timeout = timeout;
        this.oneByte = 0;
        this.onlyOneByte = false;
    }

    void write() throws IOException, InterruptedException {
        final Lock lock = new ReentrantLock();
        final Condition condition = lock.newCondition();
        final Result result = new Result();

        final Future<?> writeThread = EXECUTOR_SERVICE.submit(new WriteRunnable(result, lock, condition));
        final Future<?> timeoutThread = EXECUTOR_SERVICE.submit(new TimeoutRunnable(result, lock, condition));

        lock.lock();
        try {
            if (!result.timedout && !result.writeDone) {
                try {
                    condition.await();
                } catch (InterruptedException e) {
                    writeThread.cancel(true);
                    timeoutThread.cancel(true);
                    throw e;
                }
            }
            if (!result.writeDone) {
                byteWriter.cancelWrite();
            }
            else {
                timeoutThread.cancel(true);
            }
        }
        finally {
            lock.unlock();
        }

        result.handleResult();
    }

    private abstract class TimedOutByteWritingRunnable implements Runnable {

        protected final Result result;

        final Lock lock;

        final Condition condition;

        TimedOutByteWritingRunnable(Result result, Lock lock, Condition condition) {
            this.result = result;
            this.lock = lock;
            this.condition = condition;
        }
    }

    private class WriteRunnable extends TimedOutByteWritingRunnable {

        private WriteRunnable(Result result, Lock lock, Condition condition) {
            super(result, lock, condition);
        }

        @Override
        public void run() {
            IOException exception;
            try {
                if (onlyOneByte) {
                    byteWriter.write(oneByte);
                } else {
                    byteWriter.write(bytes);
                }
                exception = null;
            } catch (IOException e) {
                exception = e;
            }
            lock.lock();
            try {
                result.writeException = exception;
                result.writeDone = exception == null;
                condition.signalAll();
            } finally {
                lock.unlock();
            }
        }
    }

    private class TimeoutRunnable extends TimedOutByteWritingRunnable {

        private TimeoutRunnable(Result result, Lock lock, Condition condition) {
            super(result, lock, condition);
        }

        @Override
        public void run() {
            boolean interrupted;
            try {
                TimeUnit.MILLISECONDS.sleep(timeout);
                interrupted = false;
            } catch (InterruptedException e) {
                interrupted = true;
            }

            lock.lock();
            try {
                result.timedout = !interrupted;
                condition.signalAll();
            } finally {
                lock.unlock();
            }
        }
    }


    private static class Result {

        IOException writeException;

        boolean writeDone = false;

        boolean timedout = false;

        void handleResult() throws IOException {
            if (writeDone) {
                return;
            }
            if (timedout) {
                throw new TimeoutException("Write timed out");
            }
            else if (writeException != null) {
                throw writeException;
            }
        }
    }

}

和 TimeOutException

package net.femtoparsec.jssc;

import java.io.IOException;

public class TimeoutException extends IOException {

    public TimeoutException(String message) {
        super(message);
    }
}

然后,只需创建一个 JsscByteWriter 并使用带有 timeout 参数的方法来写入超时。

于 2016-05-06T15:47:27.543 回答
0

使用流控制时,如果达到阈值,写入将阻塞以防止缓冲区溢出。例如,如果接收到 XOFF 字符,则驱动程序或操作系统将不允许串行端口向远程端发送数据。如果使用重叠IO(windows),上述取消线程的方法可能会使串口操作处于不一致状态。我们在 java 层中操作东西,但是原生层呢?如果我遗漏了什么,请纠正我。

考虑使用其他串口库如单片机或修改jssc的本机代码来处理这种情况。

于 2016-05-21T12:55:26.570 回答