📄 sms.cpp
字号:
// SMS.cpp: implementation of the CSMS class.
//
//////////////////////////////////////////////////////////////////////
#include "stdafx.h"
#include "SMS.h"
#ifdef _DEBUG
#undef THIS_FILE
static char THIS_FILE[]=__FILE__;
#define new DEBUG_NEW
#endif
//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////
CSMS::CSMS()
{
m_nSendIn = 0;
m_nSendOut = 0;
m_nRecvIn = 0;
m_nRecvOut = 0;
m_hComm = NULL;
}
CSMS::~CSMS()
{
if (m_Thread!=NULL)
{
StopMonitoring();
}
}
// 读串口
// 输入: pData - 待读的数据缓冲区指针
// nLength - 待读的最大数据长度
// 返回: 实际读出的数据长度
int CSMS::ReadComm(void *pData, int nLength)
{
DWORD dwNumRead; // 串口收到的数据长度
ReadFile(m_hComm, pData, (DWORD)nLength, &dwNumRead, NULL);
return (int)dwNumRead;
}
// 写串口
// 输入: pData - 待写的数据缓冲区指针
// nLength - 待写的数据长度
// 返回: 实际写入的数据长度
int CSMS::WriteComm(void *pData, int nLength)
{
DWORD dwNumWrite; // 串口发出的数据长度
WriteFile(m_hComm, pData, (DWORD)nLength, &dwNumWrite, NULL);
return (int)dwNumWrite;
}
// 关闭串口
BOOL CSMS::CloseComm()
{
return CloseHandle(m_hComm);
}
BOOL CSMS::smsInit(CString strCSCA)
{
char ans[128]; // 应答串
CString str;
int nLength;
//
WriteComm("\0x1a", 3);
// 测试GSM-MODEM的存在性
WriteComm("AT\r", 3);
ReadComm(ans, 128);
if (strstr(ans, "OK") == NULL) return FALSE;
// ECHO OFF
WriteComm("ATE0\r", 5);
ReadComm(ans, 128);
// 设置为TEXT英文模式
WriteComm("AT+CMGF=1\r", 10);
ReadComm(ans, 128);
if (strstr(ans, "OK") == NULL) return FALSE;
str=_T("AT+CMGF=\"GSM\"\r");
nLength=str.GetLength();
WriteComm(&str, 14);
ReadComm(ans, 128);
if (strstr(ans, "OK") == NULL) return FALSE;
//设置信息中心号码
str=_T("AT+CSCA=\"") +strCSCA+ _T("\",145") + _T("\r");
nLength=str.GetLength();
WriteComm(&str, nLength);
ReadComm(ans, 128);
if (strstr(ans, "OK") == NULL) return FALSE;
return TRUE;
}
BOOL CSMS::StartMonitoring()
{
m_nSendIn = 0;
m_nSendOut = 0;
m_nRecvIn = 0;
m_nRecvOut = 0;
m_hKillThreadEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
m_hThreadKilledEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
InitializeCriticalSection(&m_csSend);
InitializeCriticalSection(&m_csRecv);
// 启动子线程
m_Thread=AfxBeginThread(SMSThread,this,THREAD_PRIORITY_NORMAL);
return TRUE;
}
UINT CSMS::SMSThread(LPVOID lParam)
{
CSMS* p=(CSMS *)lParam; // this
int nMsg; // 收到短消息条数
int nDelete; // 目前正在删除的短消息编号
SM_BUFF buff; // 接收短消息列表的缓冲区
SM_PARAM param[256]; // 发送/接收短消息缓冲区
CTime tmOrg, tmNow; // 上次和现在的时间,计算超时用
enum {
stBeginRest, // 开始休息/延时
stContinueRest, // 继续休息/延时
stSendMessageRequest, // 发送短消息
stSendMessageResponse, // 读取短消息列表到缓冲区
stSendMessageWaitIdle, // 发送不成功,等待GSM就绪
stReadMessageRequest, // 发送读取短消息列表的命令
stReadMessageResponse, // 读取短消息列表到缓冲区
stDeleteMessageRequest, // 删除短消息
stDeleteMessageResponse, // 删除短消息
stDeleteMessageWaitIdle, // 删除不成功,等待GSM就绪
stExitThread // 退出
} nState; // 处理过程的状态
// 初始状态
nState = stBeginRest;
// 发送和接收处理的状态循环
while (nState != stExitThread)
{
switch(nState)
{
case stBeginRest: //开始休息
tmOrg = CTime::GetCurrentTime();
nState = stContinueRest;
break;
case stContinueRest: //继续休息
Sleep(300);
tmNow = CTime::GetCurrentTime();
if (p->GetSendMessage(¶m[0]))
{
nState = stSendMessageRequest; // 有待发短消息,就不休息了
}
else if (tmNow - tmOrg >= 5) // 待发短消息队列空,休息5秒
{
nState = stReadMessageRequest; // 转到读取短消息状态
}
break;
case stSendMessageRequest: //发送短消息
p->smsSendMessage(¶m[0]);
memset(&buff, 0, sizeof(buff));
tmOrg = CTime::GetCurrentTime();
nState = stSendMessageResponse;
break;
case stSendMessageResponse:
Sleep(100);
tmNow = CTime::GetCurrentTime();
switch (p->smsGetResponse(&buff))
{
case GSM_OK:
nMsg = p->smsParseMessageList(param, &buff);
if (nMsg > 0)
{
p->PutRecvMessage(param, nMsg);
nDelete = 0;
nState = stDeleteMessageRequest;
}
else
{
nState = stBeginRest;
}
break;
case GSM_ERR:
nState = stBeginRest;
break;
default:
if (tmNow - tmOrg >= 15) // 15秒超时
{
nState = stBeginRest;
}
}
break;
case stSendMessageWaitIdle:
Sleep(500);
nState = stSendMessageRequest; // 直到发送成功为止
break;
case stReadMessageRequest:
p->smsReadMessageList();
memset(&buff, 0, sizeof(buff));
tmOrg = CTime::GetCurrentTime();
nState = stReadMessageResponse;
break;
case stReadMessageResponse:
Sleep(100);
tmNow = CTime::GetCurrentTime();
switch (p->smsGetResponse(&buff))
{
case GSM_OK:
nMsg =p->smsParseMessageList(param, &buff);
if (nMsg > 0)
{
p->PutRecvMessage(param, nMsg);
nDelete = 0;
nState = stDeleteMessageRequest;
}
else
{
nState = stBeginRest;
}
break;
case GSM_ERR:
nState = stBeginRest;
break;
default:
if (tmNow - tmOrg >= 15) // 15秒超时
{
nState = stBeginRest;
}
}
break;
case stDeleteMessageRequest:
if (nDelete < nMsg)
{
p->smsDeleteMessage(param[nDelete].index);
memset(&buff, 0, sizeof(buff));
tmOrg = CTime::GetCurrentTime();
nState = stDeleteMessageResponse;
}
else
{
nState = stBeginRest;
}
break;
case stDeleteMessageResponse:
Sleep(100);
tmNow = CTime::GetCurrentTime();
switch (p->smsGetResponse(&buff))
{
case GSM_OK:
nDelete++;
nState = stDeleteMessageRequest;
break;
case GSM_ERR:
nState = stDeleteMessageWaitIdle;
break;
default:
if (tmNow - tmOrg >= 5) // 5秒超时
{
nState = stBeginRest;
}
}
break;
case stDeleteMessageWaitIdle:
Sleep(500);
nState = stDeleteMessageRequest; // 直到删除成功为止
break;
}
// 检测是否有关闭本线程的信号
DWORD dwEvent = WaitForSingleObject(p->m_hKillThreadEvent, 20);
if (dwEvent == WAIT_OBJECT_0) nState = stExitThread;
}
// 置该线程结束标志
SetEvent(p->m_hThreadKilledEvent);
return 9999;
}
BOOL CSMS::StopMonitoring()
{
SetEvent(m_hKillThreadEvent); // 发出关闭子线程的信号
WaitForSingleObject(m_hThreadKilledEvent, INFINITE); // 等待子线程关闭
DeleteCriticalSection(&m_csSend);
DeleteCriticalSection(&m_csRecv);
CloseHandle(m_hKillThreadEvent);
CloseHandle(m_hThreadKilledEvent);
TRACE("Thread suspended\n");
m_Thread->SuspendThread();
return TRUE;
}
BOOL CSMS::RestartMonitoring()
{
TRACE("Thread resumed\n");
m_Thread->ResumeThread();
return TRUE;
}
// 打开串口
// 输入: pPort - 串口名称或设备路径,可用"COM1"或"\\.\COM1"两种方式,建议用后者
// nBaudRate - 波特率
// nParity - 奇偶校验
// nByteSize - 数据字节宽度
// nStopBits - 停止位
BOOL CSMS::OpenComm(const char *pPort, int nBaudRate, int nParity, int nByteSize, int nStopBits)
{
DCB dcb; // 串口控制块
COMMTIMEOUTS timeouts = { // 串口超时控制参数
100, // 读字符间隔超时时间: 100 ms
1, // 读操作时每字符的时间: 1 ms (n个字符总共为n ms)
500, // 基本的(额外的)读超时时间: 500 ms
1, // 写操作时每字符的时间: 1 ms (n个字符总共为n ms)
100}; // 基本的(额外的)写超时时间: 100 ms
m_hComm = CreateFile(pPort, // 串口名称或设备路径
GENERIC_READ | GENERIC_WRITE, // 读写方式
0, // 共享方式:独占
NULL, // 默认的安全描述符
OPEN_EXISTING, // 创建方式
0, // 不需设置文件属性
NULL); // 不需参照模板文件
if(m_hComm == INVALID_HANDLE_VALUE) return FALSE; // 打开串口失败
GetCommState(m_hComm, &dcb); // 取DCB
dcb.BaudRate = nBaudRate;
dcb.ByteSize = nByteSize;
dcb.Parity = nParity;
dcb.StopBits = nStopBits;
SetCommState(m_hComm, &dcb); // 设置DCB
SetupComm(m_hComm, 4096, 1024); // 设置输入输出缓冲区大小
SetCommTimeouts(m_hComm, &timeouts); // 设置超时
return TRUE;
}
// 将一条短消息放入发送队列
void CSMS::PutSendMessage(SM_PARAM* pparam)
{
EnterCriticalSection(&m_csSend);
memcpy(&m_SmSend[m_nSendIn], pparam, sizeof(SM_PARAM) );
m_nSendIn++;
if (m_nSendIn >= MAX_SM_SEND) m_nSendIn = 0;
LeaveCriticalSection(&m_csSend);
}
// 从发送队列中取一条短消息
BOOL CSMS::GetSendMessage(SM_PARAM* pparam)
{
BOOL fSuccess = FALSE;
EnterCriticalSection(&m_csSend);
if (m_nSendOut != m_nSendIn)
{
memcpy(pparam, &m_SmSend[m_nSendOut], sizeof(SM_PARAM));
m_nSendOut++;
if (m_nSendOut >= MAX_SM_SEND) m_nSendOut = 0;
fSuccess = TRUE;
}
LeaveCriticalSection(&m_csSend);
return fSuccess;
}
// 将短消息放入接收队列
void CSMS::PutRecvMessage(SM_PARAM* pparam,int nCount)
{
EnterCriticalSection(&m_csRecv);
for (int i = 0; i < nCount; i++)
{
memcpy(&m_SmRecv[m_nRecvIn], pparam, sizeof(SM_PARAM) );
m_nRecvIn++;
if (m_nRecvIn >= MAX_SM_RECV) m_nRecvIn = 0;
pparam++;
}
LeaveCriticalSection(&m_csRecv);
}
// 从接收队列中取一条短消息
BOOL CSMS::GetRecvMessage(SM_PARAM* pparam)
{
BOOL fSuccess = FALSE;
EnterCriticalSection(&m_csRecv);
if (m_nRecvOut != m_nRecvIn)
{
memcpy(pparam, &m_SmRecv[m_nRecvOut], sizeof(SM_PARAM) );
m_nRecvOut++;
if (m_nRecvOut >= MAX_SM_RECV) m_nRecvOut = 0;
fSuccess = TRUE;
}
LeaveCriticalSection(&m_csRecv);
return fSuccess;
}
// 发送短消息,仅发送命令,不读取应答
int CSMS::smsSendMessage(SM_PARAM* pparam)
{
int nLength; // 串口收到的数据长度
char cmd[20]; // 命令串
char ans[128]; // 应答串
char sms[140]; //信息串
sprintf(cmd,"AT+CMGS=%s\r",pparam->TPA );
WriteComm(cmd, strlen(cmd)); // 先输出命令串
nLength = ReadComm(ans, 128); // 读应答数据
// 根据能否找到"\r\n> "决定成功与否
if(nLength == 4 && strncmp(ans, "\r\n> ", 4) == 0)
{
sprintf(sms,"%s\r",pparam->TP_UD);
WriteComm(sms,strlen(sms));
return WriteComm("\x01a",1); // 得到肯定回答,继续输出PDU串
}
return 0;
}
int CSMS::smsReadMessageList()
{
return WriteComm("AT+CMGL\r", 8);
}
int CSMS::smsGetResponse(SM_BUFF *pBuff)
{
int nLength; // 串口收到的数据长度
int nState;
// 从串口读数据,追加到缓冲区尾部
nLength = ReadComm(&pBuff->data[pBuff->len], 128);
pBuff->len += nLength;
// 确定GSM MODEM的应答状态
nState = GSM_WAIT;
if ((nLength > 0) && (pBuff->len >= 4))
{
if (strncmp(&pBuff->data[pBuff->len - 4], "OK\r\n", 4) == 0)
{
nState = GSM_OK;
}
else if (strstr(pBuff->data, "+CMGL:") != NULL)
{
nState = GSM_OK;
}
else if (strstr(pBuff->data, "+CMS ERROR") != NULL)
{
nState = GSM_ERR;
}
}
return nState;
}
int CSMS::smsParseMessageList(SM_PARAM *pMsg, SM_BUFF *pBuff)
{
int nMsg; // 短消息计数值
char* ptr; // 内部用的数据指针
char* ptr1;
int nLeng;
nMsg = 0;
ptr = pBuff->data;
// 循环读取每一条短消息, 以"+CMGL:"开头
while((ptr = strstr(ptr, "+CMGL:")) != NULL)
{
ptr+=6;
//得到indx
sscanf(ptr, "%d", &pMsg->index);
//得到电话号码:
ptr=strstr(ptr,"+86");
ptr+=3;
memcpy(&pMsg->TPA ,ptr,11);
//得到信息内容:
ptr = strstr(ptr, "\r\n"); // 找下一行
if (ptr != NULL)
{
ptr += 2; // 跳过"\r\n", 定位到PDU
if((ptr1=strstr(ptr,"OK\r\n"))!=NULL)
{
nLeng=ptr1-ptr;
memcpy(&pMsg->TP_UD ,ptr,nLeng);
pMsg++; // 准备读下一条短消息
nMsg++; // 短消息计数加1
}
}
}
return nMsg;
}
// PDU编码,用于编制、发送短消息
// 输入: pSrc - 源PDU参数指针
// 输出: pDst - 目标PDU串指针
// 返回: 目标PDU串长度
int CSMS::smsDecodeTEXT(const char *pSrc, SM_PARAM *pDst)
{
return 0;
}
// 删除短消息,仅发送命令,不读取应答
// 输入: index - 短消息序号,1-255
int CSMS::smsDeleteMessage(int index)
{
char cmd[16]; // 命令串
sprintf(cmd, "AT+CMGD=%d\r", index); // 生成命令
// 输出命令串
return WriteComm(cmd, strlen(cmd));
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -