Mssm
Mssm

Reputation: 787

Picking sms from gsm modem with 2 threads

Trying to perform an application that reads sms from gsm modem each a period of time.

Thought about this solution:

Got 2 Threads in my application.

T1)- GSMModemHandler which is a handler for serial communications. T2)- SMSPicker that requests for sms each period of time and perform some string algorithms on them.

I want my application to do so:

A)- T2 asks for sms using readAllMessages(), a method from the GSMModemHandler class and then keeps blocked.

B)- T1 has got a SerialEventListener, so it listens for the response to the request sent from the GSM-Modem, and sends it back to T2.

C)- Once the response is available in a list from the T2 class, T2 resume its task concerning the string algorithms and then do again the same operations from A after waiting a certain period of time.

I've tried to code that, when i launch the application, it does its work for some time and then blocks, i guess the problem come from a missunderstanding between the 2 Threads, but can't find where the problem is and how to solve it.

Here's my code, and the result:

public class GSMModemHandler extends SerialPort implements 
SerialPortEventListener{

private static final String
        COMMAND_REMISE_A_ZERO = "ATZ",
        COMMAND_SMS_MODE_TEXT = "AT+CMGF=1",
        COMMAND_DETAILED_ERRORS = "AT+CMEE=1",
        COMMAND_SET_UP_MEMORIES = "AT+CPMS=\"MT\",\"MT\",\"MT\"",

        COMMAND_LIST_SUPPORTED_STORAGE_MODES = "AT+CPMS=?",

        COMMAND_ENVOIE_SMS = "AT+CMGS=",

        COMMAND_GET_ALL_SMS = "AT+CMGL=\"ALL\"",
        COMMAND_GET_NEW_SMS = "AT+CMGL=\"REC UNREAD\"",

        COMMAND_DELETE_ALL_MESSAGES = "AT+CMGD=0[,4]",
        COMMAND_DELETE_READ_MESSAGES = "AT+CMGD=0[,1]";

private SMSPicker smsPicker = null;
private String response = "";

public GSMModemHandler(String port) throws SerialPortException{
    super(port);        
    this.openPort();
    this.setParams(9600,SerialPort.DATABITS_8,SerialPort.STOPBITS_1,SerialPort.PARITY_NONE);
    this.addEventListener(this);
    this.startGsm();    
}

public void startGsm() throws SerialPortException{
    this.writeString(GSMModemHandler.COMMAND_REMISE_A_ZERO + "\r\n");
    this.writeString(GSMModemHandler.COMMAND_SMS_MODE_TEXT + "\r\n");
    this.writeString(GSMModemHandler.COMMAND_DETAILED_ERRORS + "\r\n");
    this.writeString(GSMModemHandler.COMMAND_SET_UP_MEMORIES + "\r\n");
}

public void sendMessage(SMS sms){
    try{
        if(this.isOpened()){ 
            this.writeString(GSMModemHandler.COMMAND_ENVOIE_SMS + "\"" + sms.getCorrespondantSms() + "\"\r\n");
            this.writeString(sms.getContenuSms() + '\032');
        }
    }
    catch(SerialPortException exp){
        exp.printStackTrace();
    }
}

public void readAllMessages(){
    try{
        if(this.isOpened())
            this.writeString(GSMModemHandler.COMMAND_GET_ALL_SMS + "\r\n");

    }
    catch(SerialPortException exp){
        exp.printStackTrace();
    }
}

public void readUnreadMessages(){
    try{
        if(this.isOpened())
            this.writeString(GSMModemHandler.COMMAND_GET_NEW_SMS + "\r\n");
    }
    catch(SerialPortException exp){
        exp.printStackTrace();
    }
}

public void deleteAllMessages(){
    try{
        if(this.isOpened())
            this.writeString(GSMModemHandler.COMMAND_DELETE_ALL_MESSAGES + "\r\n");
    }
    catch(SerialPortException exp){
        exp.printStackTrace();
    }
}

public void deleteReadMessages(){
    try{
        if(this.isOpened())
            this.writeString(GSMModemHandler.COMMAND_DELETE_READ_MESSAGES + "\r\n");
    }
    catch(SerialPortException exp){
        exp.printStackTrace();
    }
}

public synchronized void fermerConnexion(){
    try{
        this.closePort();
    }
    catch(SerialPortException exp){
        exp.printStackTrace();
    }
}

AtomicBoolean nextResponseIsSms = new AtomicBoolean(false);

@Override
public void serialEvent(SerialPortEvent spe) {
        try {
            String reponse = this.readString();

            System.out.println("GSM response = " + reponse);

            // If the next response contains the wanted sms
            if(reponse != null && reponse.contains("AT+CMGL=")){
                this.nextResponseIsSms.set(true);
                System.out.println("nextResponseIsSms = true");
            }

            // if the response contains sms
            else if(this.nextResponseIsSms.get()){

                this.smsPicker.getResponse().add(reponse);

                System.out.println("response sent !");

                this.deleteAllMessages(); // deleting the sms in the gsm modem

                System.out.println("messages deleted");

                this.nextResponseIsSms.set(false);

                System.out.println("nextResponseIsSms = false");

                // gives the SMSPicker the hand to treat the response
                synchronized(this.smsPicker){ this.smsPicker.notify(); } 

                System.out.println("smsPicker notified");
            }

    } catch (SerialPortException ex) {
        Logger.getLogger(GSMModemHandler.class.getName()).log(Level.SEVERE, null, ex);
    } 
}
/**
 * @return the smsPicker
 */
public SMSPicker getSmsPicker() {
    return smsPicker;
}

/**
 * @param smsPicker the smsPicker to set
 */
public void setSmsPicker(SMSPicker smsPicker) {
    this.smsPicker = smsPicker;
}

}

public class SMSPicker extends ControlledThread{

private GSMModemHandler modemGsm;
private SMSQueueToDatabase smsQueueHandler;
private volatile Queue<String> responses = new LinkedList<String>();

public SMSPicker(double frequency, GSMModemHandler gsmModem){
    super(frequency);
    this.modemGsm = gsmModem;
    this.modemGsm.setSmsPicker(this);
    this.smsQueueHandler = new SMSQueueToDatabase(frequency);
}

@Override
public void whatToDoBeforeTheLoop(){
    this.smsQueueHandler.start();

    try {
        this.wait(2 * this.waitingPeriod.get());
    } catch (InterruptedException ex) {
        Logger.getLogger(SMSPicker.class.getName()).log(Level.SEVERE, null, ex);
    }
}

@Override
public void whatToDoDuringTheLoop() throws NullPointerException{
    synchronized(this){ 
        try {
            System.out.println("I'm going to launch the request !");

            // Sending the sms read request to the gsm modem
            this.modemGsm.readAllMessages();

            System.out.println("i'm going to be stopped!");

            // wait till we get the answer
            this.wait();

            System.out.println("I've been stopped and now resuming");
        } 
        catch (InterruptedException ex) {
            Logger.getLogger(SMSPicker.class.getName()).log(Level.SEVERE, null, ex);
        }
    }

    // Treating the response in order to extract sms from it
    while(!this.responses.isEmpty()){
        String longMessage = this.responses.poll();

        if(longMessage != null){
            String[] shortMessages = null;
            shortMessages = longMessage.split("\\+CMGL: [0-9]*,\"");

            if(shortMessages == null) continue;

            for(String shortMessage: shortMessages){
                int indexLastOK = shortMessage.lastIndexOf("OK");

                if(indexLastOK != -1 && shortMessage.contains("+")) 
                    this.smsQueueHandler.getSmsFifo().add(this.fromStringToSms(shortMessage
                            .substring(0,shortMessage.lastIndexOf("OK") - 2))); // if it is the last sms
                else if(shortMessage.contains("REC")) // if it is not the last one
                    this.smsQueueHandler.getSmsFifo().add(this.fromStringToSms(shortMessage));
            }
        }
    }    
}

private SMS fromStringToSms(String stringSms){  
    String[] smsParts = stringSms.split(",");

    String correspondantSms = smsParts[1].replaceAll("\"", "");        
    String dateSms = smsParts[3].replace("\"","").replaceAll("/", "-");
    String heureSms = smsParts[4].substring(0,smsParts[4].lastIndexOf("\"")).substring(0, 8);
    String contenuSms = stringSms.substring(stringSms.lastIndexOf("\"") + 3);

    LocalDateTime momentSms = LocalDateTime.parse("20" + dateSms + "T" + heureSms);

    return new SMS(correspondantSms,contenuSms,momentSms);
}

@Override
public void whatToDoAfterTheLoop() {
}

/**
 * @return the modemGsm
 */
public GSMModemHandler getModemGsm() {
    return modemGsm;
}

/**
 * @param modemGsm the modemGsm to set
 */
public void setModemGsm(GSMModemHandler modemGsm) {
    this.modemGsm = modemGsm;
}

/**
 * @return the smsQueueHandler
 */
public SMSQueueToDatabase getSmsQueueHandler() {
    return smsQueueHandler;
}

/**
 * @param smsQueueHandler the smsQueueHandler to set
 */
public void setSmsQueueHandler(SMSQueueToDatabase smsQueueHandler) {
    this.smsQueueHandler = smsQueueHandler;
}

/**
 * @return the response
 */
public Queue<String> getResponse() {
    return responses;
}

/**
 * @param response the response to set
 */
public void setResponse(Queue<String> responses) {
    this.responses = responses;
}

}

public abstract class ControlledThread extends Thread{

protected AtomicBoolean workable = null;
protected AtomicLong waitingPeriod = null;

public ControlledThread(double frequency){
    super();
    this.workable = new AtomicBoolean(true);
    this.waitingPeriod = new AtomicLong(((long)(1000 / frequency)));
}

@Override
public synchronized void run() {
    this.whatToDoBeforeTheLoop();
    while(this.workable.get()){
        try{
            this.whatToDoDuringTheLoop();
            this.wait(this.waitingPeriod.get());
        }
        catch(InterruptedException exp){
            exp.printStackTrace();
        }
    }
    this.whatToDoAfterTheLoop();
}

public void stopWorking(){
    this.workable.set(false);
}

public synchronized boolean isWorking(){
    return this.workable.get();
}

public abstract void whatToDoBeforeTheLoop();
public abstract void whatToDoDuringTheLoop();
public abstract void whatToDoAfterTheLoop();
}

Result: enter image description here

Note: The blocking state happens at the red line (BUILD STOPPED is just a result of the fact that i stopped the application by a kill)

Thanks in advance !

Upvotes: 0

Views: 91

Answers (1)

bowmore
bowmore

Reputation: 11280

Most likely, you're experiencing a missed signal : you start waiting for a notify() that has already happened.

This is because you start waiting unconditionally. You should, however, always wait from within a loop that checks its wait condition.

In your case the contion to keep waiting is probably until an answer has been supplied.

so :

while (!hasAnswer()) {
    this.wait();
}

You must also make sure that the monitor you synchronize on (the SMSPicker in your case) properly guards the state that determines the condition. Since you simply seem expose the response queue, it think it's likely not the case, but I'm missing too many details to say for sure.

For a more detailed explanation look here.

Upvotes: 1

Related Questions