我需要在串行连接上写一些字节。但是我无法在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秒!
问题似乎是主方法中的“同步”关键字,为什么?
答案 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的本机代码来处理此类情况。