java - JSSC串行连接设置写入超时

标签 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)");
            }
        }

但是效果不太好,通过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.5 秒!

问题似乎出在main方法中的“syncronized”关键字,为什么?

最佳答案

我也遇到了同样的问题。我通过启动两个线程解决了这个问题:一个用于写入,另一个用于等待特定的时间。根据第一个完成的写入是否成功或超时。以下是我使用的不同类:

ByteWriter:通用字节写入的接口(interface)(我希望能够从 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 并使用带有超时参数的方法来超时写入。

关于java - JSSC串行连接设置写入超时,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/20034470/

相关文章:

java - 如何 JNA 映射指针到数组?我不断收到无效内存访问或垃圾数据

java.lang.IllegalArgumentException : Not supported: indent-number 异常

serial-port - 串行接口(interface)永久拒绝请求

android - 为什么 Android CDD 建议不要使用 CPU 温度传感器以外的温度传感器

java - newDocument( View 模态)时无法预处理面板动态数据源以设置默认值

java - 如何在 Spring Boot 3/Hibernate 6 中使用查询参数值记录 SQL 语句

linux - 如何检测属于 gsm/3g-modem 的 tty 是数据端口还是控制端口?

node.js - 使用 (DR)STRACE 比较 Windows 程序执行

android - 使用 python 自动化 android CTS

C# System.Object 是最终的基类