fixed bugs in generic SMS driver; added configurable option for serial write block...
authorVictor Kirhenshtein <victor@netxms.org>
Mon, 29 Aug 2016 14:09:07 +0000 (17:09 +0300)
committerVictor Kirhenshtein <victor@netxms.org>
Mon, 29 Aug 2016 14:11:44 +0000 (17:11 +0300)
include/nms_util.h
src/libnetxms/serial.cpp
src/smsdrv/generic/main.cpp

index 4bf38da..77b300b 100644 (file)
@@ -268,6 +268,8 @@ private:
        int m_nStopBits;
        int m_nParity;
        int m_nFlowControl;
+   int m_writeBlockSize;
+   int m_writeDelay;
 
 #ifndef _WIN32
        int m_hPort;
@@ -276,6 +278,8 @@ private:
        HANDLE m_hPort;
 #endif
 
+   bool writeBlock(const char *data, int length);
+
 public:
        Serial();
        ~Serial();
@@ -286,9 +290,11 @@ public:
        int read(char *pBuff, int nSize); /* waits up to timeout and do single read */
        int readAll(char *pBuff, int nSize); /* read until timeout or out of space */
    int readToMark(char *buff, int size, const char **marks, char **occurence);
-       bool write(const char *pBuff, int nSize);
+       bool write(const char *buffer, int length);
        void flush();
        bool set(int nSpeed, int nDataBits, int nParity, int nStopBits, int nFlowControl = FLOW_NONE);
+   void setWriteBlockSize(int bs) { m_writeBlockSize = bs; }
+   void setWriteDelay(int delay) { m_writeDelay = delay; }
        bool restart();
 };
 
index f35e466..cfcf48b 100644 (file)
@@ -58,6 +58,8 @@ Serial::Serial()
        m_nParity = NOPARITY;
        m_nStopBits = ONESTOPBIT;
        m_nFlowControl = FLOW_NONE;
+   m_writeBlockSize = -1;
+   m_writeDelay = 100;
 #ifndef _WIN32
        memset(&m_originalSettings, 0, sizeof(m_originalSettings));
 #endif
@@ -69,7 +71,7 @@ Serial::Serial()
 Serial::~Serial()
 {
        close();
-       safe_free(m_pszPort);
+       free(m_pszPort);
 }
 
 /**
@@ -354,11 +356,24 @@ int Serial::read(char *pBuff, int nSize)
                return -1;
        
 #ifdef _WIN32
+   // Read one byte
        DWORD nDone;
-       
-       if (ReadFile(m_hPort, pBuff, nSize, &nDone, NULL) != 0)
+       if (ReadFile(m_hPort, pBuff, 1, &nDone, NULL))
        {
                nRet = (int)nDone;
+
+      COMSTAT stat;
+      if (ClearCommError(m_hPort, NULL, &stat))
+      {
+         if (stat.cbInQue > 0)
+         {
+            // Read rest of buffered data
+               if (ReadFile(m_hPort, &pBuff[1], min(stat.cbInQue, (DWORD)nSize - 1), &nDone, NULL))
+            {
+               nRet += (int)nDone;
+            }
+         }
+      }
        }
        else
        {
@@ -417,7 +432,7 @@ int Serial::readAll(char *pBuff, int nSize)
 
        offset = 0;
 
-       while (offset < nSize)
+       while(offset < nSize)
        {
                FD_ZERO(&rdfs);
                FD_SET(m_hPort, &rdfs);
@@ -489,21 +504,21 @@ int Serial::readToMark(char *buffer, int size, const char **marks, char **occure
 /**
  * Write character(s) to port
  */
-bool Serial::write(const char *pBuff, int nSize)
+bool Serial::writeBlock(const char *data, int length)
 {
        bool bRet = false;
        
        if (m_hPort == INVALID_HANDLE_VALUE)
                return false;
        
-   ThreadSleepMs(100);
+   ThreadSleepMs(m_writeDelay);
 
 #ifdef _WIN32
        
        DWORD nDone;
-       if (WriteFile(m_hPort, pBuff, nSize, &nDone, NULL) != 0)
+       if (WriteFile(m_hPort, data, length, &nDone, NULL) != 0)
        {
-               if (nDone == (DWORD)nSize)
+               if (nDone == (DWORD)length)
                {
                        bRet = true;
                }
@@ -515,7 +530,7 @@ bool Serial::write(const char *pBuff, int nSize)
        
 #else // UNIX
        
-       if (::write(m_hPort, (char *)pBuff, nSize) == nSize)
+       if (::write(m_hPort, (char *)data, length) == length)
        {
                bRet = true;
        }
@@ -530,6 +545,29 @@ bool Serial::write(const char *pBuff, int nSize)
 }
 
 /**
+ * Write character(s) to port
+ */
+bool Serial::write(const char *data, int length)
+{
+   if (m_writeBlockSize > 0)
+   {
+      int pos = 0;
+      while(pos < length)
+      {
+         int bs = min(m_writeBlockSize, length - pos);
+         if (!writeBlock(&data[pos], bs))
+            return false;
+         pos += bs;
+      }
+      return true;
+   }
+   else
+   {
+      return writeBlock(data, length);
+   }
+}
+
+/**
  * Flush buffer
  */
 void Serial::flush()
index 5d24b60..859b3c3 100644 (file)
@@ -35,6 +35,16 @@ static enum { OM_TEXT, OM_PDU } s_operationMode = OM_TEXT;
 static bool s_cmgsUseQuotes = true;
 
 /**
+ * Normalize text buffer (remove non-printable characters)
+ */
+static void NormalizeText(char *text)
+{
+   for(int i = 0; text[i] != 0; i++)
+      if (text[i] < ' ')
+         text[i] = ' ';
+}
+
+/**
  * Read input to OK
  */
 static bool ReadToOK(Serial *serial, char *data = NULL)
@@ -50,6 +60,8 @@ static bool ReadToOK(Serial *serial, char *data = NULL)
          nxlog_debug(5, _T("SMS: ReadToOK: readToMark returned %d"), rc);
          return false;
       }
+      NormalizeText(buffer);
+      nxlog_debug(5, _T("SMS: ReadToOK buffer content: '%hs'"), buffer);
       if (mark != NULL) 
       {
          if (data != NULL)
@@ -96,7 +108,7 @@ static bool InitModem(Serial *serial)
 /**
  * Initialize driver
  *
- * pszInitArgs format: portname,speed,databits,parity,stopbits,mode
+ * pszInitArgs format: portname,speed,databits,parity,stopbits,mode,blocksize,writedelay
  */
 extern "C" bool EXPORT SMSDriverInit(const TCHAR *pszInitArgs, Config *config)
 {
@@ -123,6 +135,8 @@ extern "C" bool EXPORT SMSDriverInit(const TCHAR *pszInitArgs, Config *config)
        int dataBits = 8;
        int parity = NOPARITY;
        int stopBits = ONESTOPBIT;
+   int blockSize = 8;
+   int writeDelay = 100;
        
        if ((p = _tcschr(portName, _T(','))) != NULL)
        {
@@ -175,15 +189,42 @@ extern "C" bool EXPORT SMSDriverInit(const TCHAR *pszInitArgs, Config *config)
                         {
                                                                        s_operationMode = OM_TEXT;
                         }
-                                                               else if (*p == _T('N'))
-                        {
-                                                                       s_operationMode = OM_TEXT;
-                           s_cmgsUseQuotes = false;
-                        }
                                                                else if (*p == _T('P'))
                         {
                                                                        s_operationMode = OM_PDU;
                         }
+
+                        // Use quotes
+                                                          if ((p = _tcschr(p, _T(','))) != NULL)
+                                                          {
+                                                                  *p = 0; p++;
+                                                                  if (*p == _T('N'))
+                           {
+                              s_cmgsUseQuotes = false;
+                           }
+                           else
+                           {
+                              s_cmgsUseQuotes = true;
+                           }
+
+                           // block size
+                                                             if ((p = _tcschr(p, _T(','))) != NULL)
+                                                             {
+                                                                  *p = 0; p++;
+                              blockSize = _tcstol(p, NULL, 10);
+                              if ((blockSize < 1) || (blockSize > 256))
+                                 blockSize = 8;
+
+                              // write delay
+                                                                if ((p = _tcschr(p, _T(','))) != NULL)
+                                                                {
+                                                                     *p = 0; p++;
+                                 writeDelay = _tcstol(p, NULL, 10);
+                                 if ((writeDelay < 1) || (writeDelay > 10000))
+                                    writeDelay = 100;
+                              }
+                           }
+                        }
                                                        }
                                                }
                                        }
@@ -204,11 +245,14 @@ extern "C" bool EXPORT SMSDriverInit(const TCHAR *pszInitArgs, Config *config)
                        parityAsText = _T("NONE");
                        break;
        }
-       nxlog_debug(2, _T("SMS init: port=\"%s\", speed=%d, data=%d, parity=%s, stop=%d, pduMode=%s, numberInQuotes=%s"),
+       nxlog_debug(2, _T("SMS init: port=\"%s\", speed=%d, data=%d, parity=%s, stop=%d, pduMode=%s, numberInQuotes=%s blockSize=%d writeDelay=%d"),
                  portName, portSpeed, dataBits, parityAsText, stopBits == TWOSTOPBITS ? 2 : 1, 
              (s_operationMode == OM_PDU) ? _T("true") : _T("false"),
-             s_cmgsUseQuotes ? _T("true") : _T("false"));
+             s_cmgsUseQuotes ? _T("true") : _T("false"), blockSize, writeDelay);
        
+   s_serial.setWriteBlockSize(blockSize);
+   s_serial.setWriteDelay(writeDelay);
+
        if (s_serial.open(portName))
        {
                nxlog_debug(5, _T("SMS: port opened"));
@@ -299,12 +343,13 @@ extern "C" bool EXPORT SMSDriverSend(const TCHAR *pszPhoneNumber, const TCHAR *p
          goto cleanup;
       if ((mark == NULL) || (*mark != '>'))
       {
+         NormalizeText(buffer);
           nxlog_debug(5, _T("SMS: wrong response to AT+CMGS=%d (%hs)"), (int)strlen(pduBuffer) / 2 - 1, mark);
          goto cleanup;
       }
 
       s_serial.write(pduBuffer, (int)strlen(pduBuffer)); // send PDU
-      s_serial.write("\x1A\r\n", 3); // send ^Z
+      s_serial.write("\x1A", 1); // send ^Z
    }
    else
    {
@@ -335,6 +380,7 @@ extern "C" bool EXPORT SMSDriverSend(const TCHAR *pszPhoneNumber, const TCHAR *p
          goto cleanup;
       if ((mark == NULL) || (*mark != '>'))
       {
+         NormalizeText(buffer);
           nxlog_debug(5, _T("SMS: wrong response to AT+CMGS=\"%hs\" (%hs)"), pszPhoneNumber, mark);
          goto cleanup;
       }
@@ -343,16 +389,16 @@ extern "C" bool EXPORT SMSDriverSend(const TCHAR *pszPhoneNumber, const TCHAR *p
           char mbText[161];
           WideCharToMultiByte(CP_ACP, WC_DEFAULTCHAR | WC_COMPOSITECHECK, pszText, -1, mbText, 161, NULL, NULL);
           mbText[160] = 0;
-      snprintf(buffer, sizeof(buffer), "%s\x1A\r\n", mbText);
+      snprintf(buffer, sizeof(buffer), "%s\x1A", mbText);
 #else
       if (strlen(pszText) <= 160)
       {
-         snprintf(buffer, sizeof(buffer), "%s\x1A\r\n", pszText);
+         snprintf(buffer, sizeof(buffer), "%s\x1A", pszText);
       }
       else
       {
          strncpy(buffer, pszText, 160);
-         strcpy(&buffer[160], "\x1A\r\n");
+         strcpy(&buffer[160], "\x1A");
       }
 #endif
           s_serial.write(buffer, (int)strlen(buffer)); // send text, end with ^Z
@@ -368,6 +414,7 @@ extern "C" bool EXPORT SMSDriverSend(const TCHAR *pszPhoneNumber, const TCHAR *p
 cleanup:
    s_serial.setTimeout(2000);
    s_serial.close();
+   nxlog_debug(5, _T("SMS: serial port closed"));
        return success;
 }