⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 sms.cpp

📁 封装的了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(&param[0]))
				{
					nState = stSendMessageRequest;	// 有待发短消息,就不休息了
				}
				else if (tmNow - tmOrg >= 5)		// 待发短消息队列空,休息5秒
				{
					nState = stReadMessageRequest;	// 转到读取短消息状态
				}
				break;

			case stSendMessageRequest:		//发送短消息
				p->smsSendMessage(&param[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 + -