I have a class for sending data to a jSerialComm.SerialPort and then read the received data from it. The write method is working by writing to OutputStream which works correctly and then a DataListener will be added to the serial port to listen for the input data from it. The read value is supposed to change the method variable response[0] in method “pay” to break an while loop for counting timeout, but it’s not changed or is changed very very late in different systems when it is served as a jar file which I guess is a Multithread issue.
import com.fazecast.jSerialComm.SerialPort;
import com.fazecast.jSerialComm.SerialPortDataListener;
import com.fazecast.jSerialComm.SerialPortEvent;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
import org.springframework.validation.annotation.Validated;
import javax.validation.constraints.NotNull;
import java.nio.charset.StandardCharsets;
import java.time.LocalDateTime;
import java.util.concurrent.TimeoutException;
@Service
@Slf4j
@Validated
public class PaymentService {
public String pay(@NotNull String amount, String portName) {
SerialPort serialPort = SerialPort.getCommPort(portName);
try {
if (!serialPort.isOpen()) {
log.info("Failed to open port " + portName);
throw new RuntimeException("Failed to open port " + portName);
}
serialPort.getOutputStream().write(amount.getBytes());
serialPort.getOutputStream().flush();
final String[] response = new String[1];
serialPort.addDataListener(new SerialPortDataListener() {
@Override
public int getListeningEvents() {
return SerialPort.LISTENING_EVENT_DATA_AVAILABLE;
}
@Override
public void serialEvent(SerialPortEvent event) {
if (event.getEventType() != SerialPort.LISTENING_EVENT_DATA_AVAILABLE)
return;
byte[] newData = new byte[serialPort.bytesAvailable()];
serialPort.readBytes(newData, newData.length);
response[0] = new String(newData, StandardCharsets.UTF_8);
System.out.println("Read " + response[0] + " for " + amount);
}
});
// calculate timeout from now for 60 seconds
long startTime = System.currentTimeMillis();
while ((System.currentTimeMillis() - startTime) < 60000) {
if (response[0] != null) {
// This condition does not happen most of the times
log.info("Loop broke after reading " + response[0] + " at " + LocalDateTime.now());
break;
}
}
if (StringUtils.isEmpty(response[0]))
throw new TimeoutException("No response received from port in expected time");
closePort(serialPort, portName);
return response[0];
} catch (Exception e) {
log.info(e.getMessage());
throw new RuntimeException(e.getMessage());
} finally {
closePort(serialPort, portName);
}
}
private void closePort(SerialPort serialPort, String portName) {
if (serialPort.closePort()) {
log.debug("Port {} is closed ", portName);
} else {
log.info("Failed to close port {}", portName);
}
}
}
The problem is that a method variable like response[0] will not be changed in serialEvent of the SerialPortDataListener or any other event I guess. There is no other way to notify the loop to stop.
The problem is that I need to read response[0] out of serialEvent method and break the while loop.
How can I do this?
Keep in mind that you have a busy-wait loop with the
while
loop, which will spin up your CPU. You should usewait()
instead if you actually want to wait.Please edit your question to include additional
System.out.println()
lines, specially at the start of theserialEvent()
method and before and after thewhile()
loop. Then add the output you get with the additionalprintln()
statements to your question as well.Ditch the listener and use the input stream, after setting the timeout you want.
Using any kind of wait() in the while or instead of while, throws java.lang.IllegalMonitorStateException
@Progman Your idea gave me a clue to use Thread.sleep(500) and the problem seems to be solved