Java SerialPort return data

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?

  • 2

    Keep in mind that you have a busy-wait loop with the while loop, which will spin up your CPU. You should use wait() instead if you actually want to wait.

    – 

  • Please edit your question to include additional System.out.println() lines, specially at the start of the serialEvent() method and before and after the while() loop. Then add the output you get with the additional println() 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

    – 

Leave a Comment