下面的代码详细说明了当发生串行事件时读取的id,当设备通电时每隔几秒钟生成一个id(串行事件),并且当设备断电时没有接收到串行数据。问题是,我需要在接收到id时发送一次url调用,在不可见时(断电)发送一次。
我相信我很接近,但似乎做不好。如果有人能帮助我,以及如何设置标志和调度程序来实现上述情况,并可能解释我哪里出了问题,我将不胜感激。
int numberOfEmptyIds = 0;
int maxNumberOfAttempts = 5;
boolean urlSent = false;
long timeoutInMillis = 10000; // let's say 10000 millis, equivalent to 10 seconds
Timer timer = null;
public void connect(String portName) throws Exception {
...
scheduleTimer();
}
public void serialEvent(SerialPortEvent evt) {
if(evt.getEventType() == SerialPortEvent.DATA_AVAILABLE) {
try {
while(in.read(buffer) > -1) {
String asHexStr = DatatypeConverter.printHexBinary(buffer);
if(asHexStr.contains("FB1")) {
scheduleTimer();
numberOfEmptyIds = 0;
} else {
numberOfEmtyIds++;
if(numberOfEmptyIds == maxNumberOfAttempts && !urlSent) {
// send the url here
}
}
}
} catch (IOException ex) {
// Log the exception here
}
}
}
private void scheduleTimer() {
timer = new Timer("Timeout");
TimerTask task = new TimerTask() {
@Override
public void run() {
if(!urlSent) {
// send the url here
}
}
};
timer.schedule(task, timeoutInMillis);
}
问题是我需要在收到id时发送一次url调用一次在不可见时(断电)。
第二部分由计时器完成,如果没有数据到达串行端口,则计划任务将发送URL(如果尚未发送)。在我回答你之前的问题时,我忘记了在重新安排任务时取消计时器:
private void scheduleTimer() {
if(timer != null) {
timer.cancel();
}
timer = new Timer("Timeout");
TimerTask task = new TimerTask() {
@Override
public void run() {
if(!urlSent) {
// send the url here
}
}
};
timer.schedule(task, timeoutInMillis);
}
这样就会有一个单独的计划任务。从Timer.cancel()javadoc:
终止此计时器,放弃任何当前计划的任务。做不干扰当前正在执行的任务(如果存在)。一次定时器已终止,其执行线程正常终止,并且不能在其上调度更多的任务。
请注意,从计时器的run方法中调用此方法该计时器调用的任务绝对保证正在执行的任务是将要执行的最后一个任务由该定时器执行。
关于第一部分,您可以像urlSent
一样使用布尔标志来管理它。如果您只需要发送一次URL,那么您可以为ID到达时发送的URL设置一个标志,并为未收到数据(或空ID)而发送的URL提供另一个标志。
编辑
根据你在这里发布的流程图,如下所示:
在此处输入描述http://dl6.fileswap.com/storage_previews/02112014/54cd147697479d29c43c530b93d5fa83/52fe9168/aW1hZ2UvanBlZw%3D%3D/4cf59787af56f18847df6235cdc20816.jpg
您可以使用Timer.scheduleAtFixedRate()方法更改当前的方法,以固定的时间速率检查串行端口是否停止读取ID。由于您只需要发送一次收到的ID通知,因此可以在有效发送此URL时将urlSent
标志设置为true
。此外,我认为如果收到的数据不包含预期的ID,你可以取消检查。类似这样的东西:
boolean urlSent = false;
long lastIdArrivalTime = 0;
long timeTolerance = 60000;
long timeoutInMillis = 300000; // 5 minutes
Timer timer = null;
public void connect(String portName) throws Exception {
...
scheduleTimer();
}
public void serialEvent(SerialPortEvent evt) {
if(evt.getEventType() == SerialPortEvent.DATA_AVAILABLE) {
try {
while(in.read(buffer) > -1) {
String asHexStr = DatatypeConverter.printHexBinary(buffer);
if(asHexStr.contains("FB100000010F0801")) {
lastIdArrivalTime = System.currentTimeMillis();
if(!urlSent) {
// send the URL notifying the ID
urlSent = true; // next time url will not be sent
}
}
}
} catch (IOException ex) {
// Log the exception here
}
}
}
private void scheduleTimer() {
timer = new Timer("Timeout");
TimerTask task = new TimerTask() {
@Override
public void run() {
long currentTime = System.currentTimeMillis();
if((currentTime - lastIdArrivalTime) >= timeTolerance) {
// sent the URL notifying the device is off
urlSent = false; // this way the next ID arrival will be notified
}
}
};
timer.scheduleAtFixedRate(task, timeoutInMillis, timeoutInMillis);
}
一些注意事项:
- 计时器这次只安排一次,因为它将每5分钟执行一次任务。如果你需要关闭连接,不要忘记调用timer.cancel()方法
- 变量
lastIdArrivalTime
保存ID到达时的最后时间(以毫秒为单位) - 变量
timeTolerance
是假定连接断开的最大时间容差。正如您所说,设备会在几秒钟的固定时间内发送ID,因此,如果自上次ID到达后花费了1分钟,则可以假设连接已断开(或设备已关闭)
此处提供了有关代码的一些提示:
- TimerTask实现了Runnable接口,旨在使用Timer类使用,该类将在计划时间到来时创建一个单独的线程来执行此任务,因此不要在新线程中使用
TimerTask
- 不要惹线程除非你确切地知道自己在做什么。制作一个犯了错误,把事情搞砸了