JSSC串行连接设置写超时

时间:2013-11-17 18:24:54

标签: java serial-port cts jssc

我需要在串行连接上写一些字节。但是我无法在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)");
            }
        }

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

尝试直接写:

    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.5秒!

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

2 个答案:

答案 0 :(得分: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:ByteWriter for Jssc的实现

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参数的方法来写入超时。

答案 1 :(得分:0)

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

考虑使用其他串行端口库,如SCM或修改jssc的本机代码来处理此类情况。