从具有 2 个线程的 gsm 调制解调器中选择短信

Picking sms from gsm modem with 2 threads

尝试执行一个应用程序,每隔一段时间从 gsm 调制解调器读取短信。

想到这个解决方案:

我的应用程序中有 2 个线程。

T1)- GSMModemHandler,它是串行通信的处理程序。 T2)- SMSPicker,每段时间请求短信并对其执行一些字符串算法。

我希望我的应用程序这样做:

A)- T2 使用 readAllMessages() 请求短信,这是 GSMModemHandler class 的一种方法,然后保持阻塞状态。

B)- T1 有一个 SerialEventListener,因此它会监听 GSM-Modem 发送的请求的响应,并将其发送回 T2。

C)- 一旦响应在来自 T2 class 的列表中可用,T2 恢复其有关字符串算法的任务,然后在等待一段时间后再次从 A 执行相同的操作。

我已经尝试编写代码,当我启动应用程序时,它会工作一段时间然后阻塞,我猜问题来自两个线程之间的误解,但找不到问题是以及如何解决它。

这是我的代码,结果是:

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() + '2');
        }
    }
    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();
}

结果:

注意:阻塞状态发生在红线处(BUILD STOPPED 只是我通过 kill 停止应用程序的结果)

提前致谢!

很可能,您遇到了错过的信号:您开始等待已经发生的 notify()

这是因为你开始无条件地等待。但是,您应该始终在检查其等待条件的循环中等待。

在您的情况下,继续等待的条件可能是直到提供答案为止。

所以 :

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

您还必须确保您同步的监视器(在您的情况下为 SMSPicker)正确保护确定条件的状态。由于您似乎只是公开了响应队列,所以它认为情况可能并非如此,但我遗漏了太多细节无法确定。

更详细的解释看here.