Search code examples
c#windows-servicesgsm

how to implement retry mechanism when I got gsm modem Exception: No data received from phone?


public class GSMModemConnection { I made a windows service for receiving SMS I'm using Wavecom Gsm modem 1306B. But after some second I got an Exception: No data received from phone. I have searched in StackOverflow I found this link Exception: No data received from phone

someone pointed about to create a retry mechanism but I don't know how to implement it.

static void Main(string[] args)
{
    GSMModemConnection gsm = new GSMModemConnection();
    var result = gsm.OpenPort();
    Console.WriteLine(result.PortName);


    while (true)
    {
        Stopwatch stopwatch = new Stopwatch();
        stopwatch.Start();
        ShortMessage execResult = gsm.ReadSMS(result, "AT+CMGL=\"ALL\"");
        Console.WriteLine(execResult.Message);
        Console.WriteLine(execResult.Status);

    }

}

   public class GSMModemConnection
    {
        public AutoResetEvent receiveNow;
        //string strPortName, string strBaudRate
        public SerialPort OpenPort()
        {
            receiveNow = new AutoResetEvent(false);
            SerialPort port = new SerialPort();
            port.PortName = "COM3";
            port.BaudRate = 115200 /*Convert.ToInt32(strBaudRate)*/;               //updated by Anila (9600)
            port.DataBits = 8;
            port.StopBits = StopBits.One;
            port.Parity = Parity.None;
            port.ReadTimeout = 300;
            port.WriteTimeout = 300;
            port.Encoding = Encoding.GetEncoding("iso-8859-1");
            port.DataReceived += new SerialDataReceivedEventHandler(port_DataReceived);
            port.Open();
            port.DtrEnable = true;
            port.RtsEnable = true;
            return port;
        }

        //Close Port
        public void ClosePort(SerialPort port)
        {
            port.Close();
            port.DataReceived -= new SerialDataReceivedEventHandler(port_DataReceived);
            port = null;
        }

        //Execute AT Command
        public string ExecCommand(SerialPort port, string command, int responseTimeout, string errorMessage)
        {

            try
            {
                //receiveNow = new AutoResetEvent();
                port.DiscardOutBuffer();
                port.DiscardInBuffer();
                receiveNow.Reset();
                port.Write(command + "\r");

                //Thread.Sleep(3000); //3 seconds
                string input = ReadResponse(port, responseTimeout);
                if ((input.Length == 0) || ((!input.EndsWith("\r\n> ")) && (!input.EndsWith("\r\nOK\r\n"))))
                    throw new ApplicationException("No success message was received.");
                return input;
            }
            catch (Exception ex)
            {
                throw new ApplicationException(errorMessage, ex);
            }
        }

        //Receive data from port
        public void port_DataReceived(object sender, SerialDataReceivedEventArgs e)
        {
            if (e.EventType == SerialData.Chars)
                receiveNow.Set();
        }
        public string ReadResponse(SerialPort port, int timeout)
        {
            string buffer = string.Empty;
            do
            {
                if (receiveNow.WaitOne(timeout, false))
                {
                    string t = port.ReadExisting();
                    buffer += t;
                }
                else
                {
                    if (buffer.Length > 0)
                        throw new ApplicationException("Response received is incomplete.");
                    else
                        throw new ApplicationException("No data received from phone.");
                }
            }
            while (!buffer.EndsWith("\r\nOK\r\n") && !buffer.EndsWith("\r\n> ") && !buffer.EndsWith("\r\nERROR\r\n"));
            return buffer;
        }

        public ShortMessage ReadSMS(SerialPort port, string p_strCommand)
        {

            // Set up the phone and read the messages
            ShortMessage messages = null;
            try
            {

                #region Execute Command
                // Check connection
                var a = ExecCommand(port, "AT", 300, "No phone connected");
                // Use message format "Text mode"
                var b = ExecCommand(port, "AT+CMGF=1\r", 300, "Failed to set message format.");
                // Use character set "PCCP437"
                //var c = ExecCommand(port, "AT+CSCS=\"PCCP437\"", 300, "Failed to set character set.");
                // Select SIM storage
                //var d = ExecCommand(port, "AT+CPMS=\"SM\"", 300, "Failed to select message storage.");
                // Read the messages
                string input = ExecCommand(port, p_strCommand, 5000, "Failed to read the messages.");
                #endregion

                #region Parse messages
                messages = ParseMessages(input);
                #endregion

            }
            catch (Exception ex)
            {
                throw ex;
            }

            if (messages != null)
                return messages;
            else
                return null;

        }
        public ShortMessage ParseMessages(string input)
        {
            ShortMessage msg = new ShortMessage();

            //ShortMessageCollection messages = new ShortMessageCollection();
            try
            {
                Regex r = new Regex(@"\+CMGL: (\d+),""(.+)"",""(.+)"",(.*),""(.+)""\r\n(.+)\r\n");
                Match m = r.Match(input);
                while (m.Success)
                {
                    //msg.Index = int.Parse(m.Groups[1].Value);
                    msg.Index = int.Parse(m.Groups[1].Value);
                    msg.Status = m.Groups[2].Value;
                    msg.Sender = m.Groups[3].Value;
                    msg.Alphabet = m.Groups[4].Value;
                    msg.Sent = m.Groups[5].Value;
                    msg.Message = m.Groups[6].Value;
                    //messages.Add(msg);

                    m = m.NextMatch();
                }

            }
            catch (Exception ex)
            {
                throw ex;
            }
            return msg;
        }

    }

Solution

  • You DO have a retry mechanism in place with the DO-WHILE loop from the following expression:

    public string ReadResponse(SerialPort port, int timeout)
        {
            string buffer = string.Empty;
            do
            {
                if (receiveNow.WaitOne(timeout, false))
                {
                    string t = port.ReadExisting();
                    buffer += t;
                }
                else
                {
                    if (buffer.Length > 0)
                        throw new ApplicationException("Response received is incomplete.");
                    else
                        throw new ApplicationException("No data received from phone.");
                }
            }
            while (!buffer.EndsWith("\r\nOK\r\n") && !buffer.EndsWith("\r\n> ") && !buffer.EndsWith("\r\nERROR\r\n"));
            return buffer;
        }
    

    What you can do is reduce the granularity of the error report mechanism. In this case, for example, the line throw new ApplicationException() will break the loop and get captured in the Exec() function, then thrown again. If you just want to wait for the loop to close and the DO-WHILE loop to get executed, I would replace the following part of the code:

    else
                {
                    if (buffer.Length > 0)
                        throw new ApplicationException("Response received is incomplete.");
                    else
                        throw new ApplicationException("No data received from phone.");
                }
    

    With:

    else
                {
                    if (buffer.Length > 0)
                        bufferError += "Response received is incomplete.\n";
                    else
                        bufferError += "No data received from phone.\n";
                }
    

    Why? After the DO-WHILE loop, the buffer will either return string.Empty or some value. Up in the code, you have:

     string input = ReadResponse(port, responseTimeout);
                if ((input.Length == 0) || ((!input.EndsWith("\r\n> ")) && (!input.EndsWith("\r\nOK\r\n"))))
                    throw new ApplicationException("No success message was received.");
                return input;
    

    Basically, if the returned buffer is string.Empty an Exception will be thrown again.

    By returning the buffer Error, you can decide later how you want to handle it but the DO-WHILE loop will run at least once up until the condition from the WHILE expression is met. Rest of the code should look like this:

    //Thread.Sleep(3000); //3 seconds
                string bufferErrors = string.Empty;
                string input = ReadResponse(port, responseTimeout, bufferErrors);
                if ((input.Length == 0) || ((!input.EndsWith("\r\n> ")) && (!input.EndsWith("\r\nOK\r\n"))))
                    throw new ApplicationException("No success message was received.");
                if (!string.IsNullOrWhiteSpace(bufferErrors))
                {
                    //Handle bufferErrors
                }
                return input;
    

    Remember to declare the out parameter in ReadResponse

    public string ReadResponse(SerialPort port, int timeout, out string bufferError)
        {
            string buffer = string.Empty;
            bufferError = string.Empty;