// Path: gsm-modem-client-api/src/main/java/fr/romainmoreau/gsmmodem/client/api/GsmModemException.java
// public class GsmModemException extends Exception {
// 	private static final long serialVersionUID = 1L;
// 
// 	public GsmModemException() {
// 		super();
// 	}
// 
// 	public GsmModemException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
// 		super(message, cause, enableSuppression, writableStackTrace);
// 	}
// 
// 	public GsmModemException(String message, Throwable cause) {
// 		super(message, cause);
// 	}
// 
// 	public GsmModemException(String message) {
// 		super(message);
// 	}
// 
// 	public GsmModemException(Throwable cause) {
// 		super(cause);
// 	}
// }
// 
// Path: gsm-modem-client-api/src/main/java/fr/romainmoreau/gsmmodem/client/api/GsmModemResponseException.java
// public class GsmModemResponseException extends GsmModemException {
// 	private static final long serialVersionUID = 1L;
// 
// 	public GsmModemResponseException(String message) {
// 		super(message);
// 	}
// 
// 	public GsmModemResponseException(String message, Exception cause) {
// 		super(message, cause);
// 	}
// }
// 
// Path: gsm-modem-client-common/src/main/java/fr/romainmoreau/gsmmodem/client/common/AbstractGsmModemClient.java
// public abstract class AbstractGsmModemClient implements GsmModemClient {
// }


import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.nio.charset.StandardCharsets;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingDeque;
import java.util.concurrent.TimeUnit;
import java.util.function.Predicate;
import fr.romainmoreau.gsmmodem.client.api.GsmModemException;
import fr.romainmoreau.gsmmodem.client.api.GsmModemResponseException;
import fr.romainmoreau.gsmmodem.client.common.AbstractGsmModemClient;
	private ReadByteListener readByteListener;

	private ReadLineListener readLineListener;

	public AbstractUARTGsmModemClient(long timeout) {
		this.timeout = timeout;
		this.byteArrayOutputStream = new ByteArrayOutputStream();
		this.gsmEventBlockingQueue = new LinkedBlockingDeque<>();
	}

	@Override
	public synchronized void setSMSTextMode() throws IOException, GsmModemException {
		sendString("AT+CMGF=1" + CR);
		waitForFirstOkEventOrErrorEvent();
	}

	@Override
	public synchronized void sendSms(String gsmNumber, String sms) throws IOException, GsmModemException {
		setSMSTextMode();
		sendString("AT+CMGS=\"" + gsmNumber + "\"" + CR);
		waitForFirstPromptEvent();
		sendString(sms + SUB);
		waitForFirstOkEventOrErrorEvent();
	}

	@Override
	public synchronized void sendCommand(String command) throws IOException, GsmModemException {
		sendString(command + CR);
	}

// Path: gsm-modem-client-api/src/main/java/fr/romainmoreau/gsmmodem/client/api/GsmModemException.java
// public class GsmModemException extends Exception {
// 	private static final long serialVersionUID = 1L;
// 
// 	public GsmModemException() {
// 		super();
// 	}
// 
// 	public GsmModemException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
// 		super(message, cause, enableSuppression, writableStackTrace);
// 	}
// 
// 	public GsmModemException(String message, Throwable cause) {
// 		super(message, cause);
// 	}
// 
// 	public GsmModemException(String message) {
// 		super(message);
// 	}
// 
// 	public GsmModemException(Throwable cause) {
// 		super(cause);
// 	}
// }
// 
// Path: gsm-modem-client-api/src/main/java/fr/romainmoreau/gsmmodem/client/api/GsmModemResponseException.java
// public class GsmModemResponseException extends GsmModemException {
// 	private static final long serialVersionUID = 1L;
// 
// 	public GsmModemResponseException(String message) {
// 		super(message);
// 	}
// 
// 	public GsmModemResponseException(String message, Exception cause) {
// 		super(message, cause);
// 	}
// }
// 
// Path: gsm-modem-client-common/src/main/java/fr/romainmoreau/gsmmodem/client/common/AbstractGsmModemClient.java
// public abstract class AbstractGsmModemClient implements GsmModemClient {
// }


// Path: gsm-modem-client-common-uart/src/main/java/fr/romainmoreau/gsmmodem/client/common/uart/AbstractUARTGsmModemClient.java
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.nio.charset.StandardCharsets;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingDeque;
import java.util.concurrent.TimeUnit;
import java.util.function.Predicate;
import fr.romainmoreau.gsmmodem.client.api.GsmModemException;
import fr.romainmoreau.gsmmodem.client.api.GsmModemResponseException;
import fr.romainmoreau.gsmmodem.client.common.AbstractGsmModemClient;

	private ReadByteListener readByteListener;

	private ReadLineListener readLineListener;

	public AbstractUARTGsmModemClient(long timeout) {
		this.timeout = timeout;
		this.byteArrayOutputStream = new ByteArrayOutputStream();
		this.gsmEventBlockingQueue = new LinkedBlockingDeque<>();
	}

	@Override
	public synchronized void setSMSTextMode() throws IOException, GsmModemException {
		sendString("AT+CMGF=1" + CR);
		waitForFirstOkEventOrErrorEvent();
	}

	@Override
	public synchronized void sendSms(String gsmNumber, String sms) throws IOException, GsmModemException {
		setSMSTextMode();
		sendString("AT+CMGS=\"" + gsmNumber + "\"" + CR);
		waitForFirstPromptEvent();
		sendString(sms + SUB);
		waitForFirstOkEventOrErrorEvent();
	}

	@Override
	public synchronized void sendCommand(String command) throws IOException, GsmModemException {
		sendString(command + CR);
	}

	private GsmEvent waitForGsmEvent() throws GsmModemResponseException {