📄 serialcommlayer.cpp
字号:
DataBufferLength = 0;
DataCurPos = 0;
ReceiveBufferLength = 0;
ReceiveCurPos = 0;
SendBufferLength = 0;
PrevReceFrameNo = 255;
PrevSendFrameNo = 255;
LastFrameClass = 0;
memset(MDMProtocol,0,20);
memset(MDMCompress,0,20);
MDMCarrier = 0;
MDMConnect = 0;
MDMResult = MDM_NONE;
BytesPerBlock = 2048;
frmErrorNums = 0;
BaudRate = 28800;
}
CRCCComm::~CRCCComm()
{
if(hCommFile != INVALID_HANDLE)
Close();
}
BEGIN_MESSAGE_MAP(CRCCComm, CWnd)
//{{AFX_MSG_MAP(CRCCComm)
// NOTE - the ClassWizard will add and remove mapping macros here.
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
/////////////////////////////////////////////////////////////////////////////
// CRCCComm message handlers
int CRCCComm::SearchHead()
{
BYTE frmType = 0xFF;
int i;
for ( i = 0; i < ReceiveBufferLength;i++)
{
if(ReceiveBuffer[i]==FrameFlag)
frmType = ReceiveBuffer[i+1]&0x0F;
else
continue;
if ((frmType >= frmMin) && (frmType <= frmMax))
break;
else
continue;
}
return i; //返回所找到的帧头在接收缓冲区内的偏移
}
BOOL CRCCComm::StartComm(void) //启动COMM口
{
HANDLE hNewCommFile;
// 如果已经启动COMM口,则返回
if (hCommFile != INVALID_HANDLE)
{
LastError = RCC_ERROR_ALREADY_OPEN;
return FALSE;
}
if(CommNo <= 0)
{
LastError = RCC_ERROR_COMMNO;
return FALSE;
}
char szPort[20];
sprintf(szPort,"\\\\.\\COM%d",CommNo);
hNewCommFile = CreateFile( szPort,
GENERIC_READ|GENERIC_WRITE,
0,NULL,OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL|FILE_FLAG_OVERLAPPED,
0 );
if (hNewCommFile == INVALID_HANDLE_VALUE)
{
LastError = RCC_ERROR_OPEN;
return FALSE;
}
// 检查新句柄是否是一个适当的设备
if (GetFileType( hNewCommFile ) != FILE_TYPE_CHAR)
{
CloseHandle(hNewCommFile);
LastError = RCC_ERROR_INVALID_COMM;
return FALSE;
}
if(!SetupComm( hNewCommFile, 8192, 8192 ))
{
CloseHandle( hNewCommFile );
LastError = RCC_ERROR_SET_BUFFER;
return FALSE;
}
hCommFile = hNewCommFile;
// 删除系统缓冲区内的任何东西
PurgeComm( hCommFile, PURGE_TXABORT | PURGE_RXABORT |
PURGE_TXCLEAR | PURGE_RXCLEAR ) ;
// _SetCommState;
this->SetCommState(NULL);
//设置底层超时参数
COMMTIMEOUTS commtimeouts;
GetCommTimeouts(hCommFile, &commtimeouts);
commtimeouts.ReadIntervalTimeout = 20;
commtimeouts.ReadTotalTimeoutMultiplier = 0;
commtimeouts.ReadTotalTimeoutConstant = 0;
commtimeouts.WriteTotalTimeoutMultiplier = 30;
commtimeouts.WriteTotalTimeoutConstant = 400;
SetCommTimeouts(hCommFile, &commtimeouts);
EscapeCommFunction(hCommFile,SETDTR);
// 创建读写线程关闭事件.
hCloseEvent = CreateEvent( NULL, TRUE, FALSE, NULL );
if (hCloseEvent == INVALID_HANDLE)
{
CloseHandle( hCommFile );
hCommFile = INVALID_HANDLE;
LastError = RCC_ERROR_CREATE_CLOSE_EVENT;
return FALSE;
}
// 创建串口读写线程.
ReadThread = new CCommReadThread();
WriteThread = new CCommWriteThread();
if(ReadThread == NULL || WriteThread == NULL)
{
LastError = RCC_ERROR_CREATE_THREAD;
if(ReadThread)
{
delete ReadThread;
ReadThread = NULL;
}
if(WriteThread)
{
delete WriteThread;
WriteThread = NULL;
}
CloseHandle( hCloseEvent );
hCloseEvent=INVALID_HANDLE;
CloseHandle( hCommFile );
hCommFile=INVALID_HANDLE;
return FALSE;
}
if(!ReadThread->CreateThread(CREATE_SUSPENDED)||
!WriteThread->CreateThread(CREATE_SUSPENDED))
{
LastError = RCC_ERROR_CREATE_THREAD;
if(ReadThread)
{
delete ReadThread;
ReadThread = NULL;
}
if(WriteThread)
{
delete WriteThread;
WriteThread = NULL;
}
CloseHandle( hCloseEvent );
hCloseEvent=INVALID_HANDLE;
CloseHandle( hCommFile );
hCommFile=INVALID_HANDLE;
return FALSE;
}
ReadThread -> SetThreadPriority(THREAD_PRIORITY_HIGHEST);
WriteThread -> SetThreadPriority(THREAD_PRIORITY_HIGHEST);
ReadThread->hCommFile = hCommFile;
ReadThread->hCloseEvent = hCloseEvent;
ReadThread->hComm32Window = m_hWnd;
ReadThread->m_bAutoDelete = FALSE;
WriteThread->hCommFile = hCommFile;
WriteThread->hCloseEvent = hCloseEvent;
WriteThread->hComm32Window = m_hWnd;
WriteThread->m_bAutoDelete = FALSE;
SetTimer(3,100,NULL); //设置超时时钟,用于发送超时
//启动线程
ReadThread->ResumeThread();
WriteThread->ResumeThread();
return TRUE;
}
BOOL CRCCComm::StopComm(void) //关闭COMM口
{
// 如果当前状态不在通讯,则可直接返回.
if (hCommFile == INVALID_HANDLE)
return TRUE;
// 关闭读写线程.
CloseReadThread();
CloseWriteThread();
// 关闭事件句柄.
CloseHandle( hCloseEvent );
hCloseEvent = INVALID_HANDLE;
//关闭超时处理
KillTimer(3);
// 关闭通讯端口.
CloseHandle( hCommFile );
hCommFile = INVALID_HANDLE;
return TRUE;
}
//
// 函数: CloseWriteThread CloseReadThread
// 用途: 关闭读写线程.
// 参数: 无
// 返回值:无
// 备注:
// 关闭串口读写线程,并且清除在串口上的任何写操作
// 如果强制关闭一个线程可能导致内存汇漏,但是本程序是通过
// 关闭事件触发的方式去关闭线程,线程在检测到关闭事件触发
// 时会自动退出,因此不会出现强制关闭一个线程的情况
//
void CRCCComm::CloseReadThread(void) //关闭读线程
{
if (ReadThread != NULL)
{
// 触发事件去关闭工作线程.
SetEvent(hCloseEvent);
// 清除在串口上的任何写操作.
PurgeComm(hCommFile, PURGE_TXABORT|PURGE_TXCLEAR);
// 等待10秒线程关闭,因为不会发生,所以注释掉.
if (WaitForSingleObject( ReadThread->m_hThread, 10000 ) == WAIT_TIMEOUT)
TerminateThread(ReadThread->m_hThread,-1);
delete ReadThread;
ReadThread = NULL;
}
}
void CRCCComm::CloseWriteThread(void) //关闭写线程
{
if (WriteThread != NULL)
{
// 触发事件去关闭工作线程.
SetEvent(hCloseEvent);
// 清除在串口上的任何写操作.
PurgeComm(hCommFile, PURGE_TXABORT|PURGE_TXCLEAR);
// 等待10秒线程关闭,因为不会发生,所以注释掉.
if (WaitForSingleObject( WriteThread->m_hThread, 10000 ) == WAIT_TIMEOUT)
TerminateThread(WriteThread->m_hThread,-1);
// if (WaitForSingleObject( WriteThread->m_hThread, 10000 ) == WAIT_TIMEOUT)
// WriteThread->Terminate;
delete WriteThread;
WriteThread = NULL;
}
}
//
// 函数: int CRCCComm::Send(char *Buffer,int BufLen)
// 用途: 向串口行发送数据.
// 参数:
// Buffer 需要发送数据的缓冲区
// BufLen 需要发送的数据长度
// 返回值:
// -1 表示发送失败,具体失败原因可由GetLastError得到
// 其它表示发送的字节数(包括零)
// 备注:
// 对该函数的理解,可参照CAsyncSocket::Send函数
// 应用程序一般在接收到OnSend消息里调用该函数
// 失败原因: RCC_ERROR_NOCONNECT 当前不处在通讯状态
// RCC_ERROR_INVAL 参数不正确
//
int CRCCComm::Send(char *Buffer,int BufLen)
{
if(RCCStatus == RCC_STAT_INIT || RCCStatus == RCC_STAT_ABORT)
{
LastError = RCC_ERROR_NOCONNECT;
return -1;
}
if(Buffer == NULL || BufLen <= 0)
{
LastError = RCC_ERROR_INVAL;
return -1;
}
int SendLength = min(SendBufferMaxLength - SendBufferLength,BufLen);
if(SendLength == 0)
return SendLength;
memcpy(&SendBuffer[SendBufferLength],Buffer,SendLength);
SendBufferLength+=SendLength;
if(RCCStatus == RCC_STAT_COMM)
{
GenOneFrame(); //生成一帧数据帧
SendDataFrame(); //发送一帧数据帧
RCCStatus = RCC_STAT_DATA;
LastFrameClass = 1;
}
return SendLength;
}
//
// 函数: int CRCCComm::Receive(char *Buffer,int BufLen)
// 用途: 得到串行口数据(经过RCC解释过).
// 参数:
// Buffer 接收数据的缓冲区
// BufLen 接收缓冲区的长度
// 返回值:
// -1 表示接收失败,具体失败原因可由GetLastError得到
// 其它表示接收的字节数(包括零)
// 备注:
// 对该函数的理解,可参照CAsyncSocket::Receive函数
// 应用程序一般在接收到OnReceive消息里调用该函数
// 失败原因: RCC_ERROR_NOCONNECT 当前不处在通讯状态
// RCC_ERROR_INVAL 参数不正确
//
int CRCCComm::Receive(char *Buffer,int BufLen)
{
if(RCCStatus == RCC_STAT_INIT || RCCStatus == RCC_STAT_ABORT)
{
LastError = RCC_ERROR_NOCONNECT;
return -1;
}
if(Buffer == NULL || BufLen <= 0)
{
LastError = RCC_ERROR_INVAL;
return -1;
}
int ReceLength = min(DataBufferLength,BufLen);
memcpy(Buffer,DataBuffer,ReceLength);
DelDealtData(DataBuffer,DataBufferLength,ReceLength);
return ReceLength;
}
//
// 函数: int CRCCComm::Connect(char *PhoneNo)
// 用途: 与指定电话号码进行联接.
// 参数:
// PhoneNo 电话号码,最长40个字节
// 返回值:
// 0 表示成功,即MODEM已经开始进行链接
// 其它表示失败
// 备注:
// 对该函数的理解,可参照CAsyncSocket::Connect函数
// 该处返回成功,表示与MODEM的操作成功,真正链接成功处理
// 在OnConnect()事件内,在RS232方式下,只做内部缓冲区初始
// 化操作,电话号码无用处
//
int CRCCComm::Connect(char *PhoneNo)
{
if(hCommFile == INVALID_HANDLE)
StartComm();
Init();
if(RCCMode == MODE_RS232 )
return 0;
MDMResult = MDM_NONE;
WriteCommand("S0=0");
WaitForCommandResult(300);
MDMResult = MDM_NONE;
WriteCommand("");
WaitForCommandResult(300);
if(MDMResult != MDM_OK)
{
MDMResult = MDM_NONE;
WriteCommand("");
WaitForCommandResult(300);
if(MDMResult != MDM_OK)
return 1;
}
MDMResult = MDM_NONE;
WriteCommand("E0V1");
WaitForCommandResult(300);
char szDial[40];
EscapeCommFunction(hCommFile,SETDTR);
strcpy(szDial,"DT");
strncpy(&szDial[2],PhoneNo,37);
szDial[39] = 0;
if(WriteCommand(szDial))
{ //写入失败
// LastError = RCC_ERROR_CONNECT;
return 1;
}
return 0;
}
//
// 函数: int CRCCComm::Listen(void)
// 用途: 侦听客户端来的拔号请求.
// 参数: 无
// 返回值:
// 0 表示成功,即MODEM已经开始侦听
// 其它表示失败
// 备注:
// 对该函数的理解,可参照CAsyncSocket::Listen函数
// 在MODEM方式下,返回成功,表示MODEM已经处在AutoAnswer状态之下,
// 在RS232方式下,只做内部缓冲区初始化操作。
//
int CRCCComm::Listen(void)
{
if(hCommFile == INVALID_HANDLE)
StartComm();
Init();
EscapeCommFunction(hCommFile,SETDTR);
if(RCCMode == MODE_RS232)
return 0;
if(WriteCommand("S0=1"))
{ //写入失败
return 1;
}
return 0;
}
//
// 函数: int CRCCComm::Close(void)
// 用途: 关闭串口链接,重新初始化缓冲区,同时关闭串口.
// 参数: 无
// 返回值:
// 0 表示成功,即MODEM关闭成功进行
// 其它表示失败
// 备注:
// 对该函数的理解,可参照CAsyncSocket::Close函数
// 在MODEM连线方式下,需要将该MODEM转入命令状态之下,再进行
// 挂断操作;在RS232方式下,只做内部缓冲区初始化操作。
//
int CRCCComm::Close(void)
{
if(hCommFile == INVALID_HANDLE)
return 0;
EscapeCommFunction(hCommFile,CLRDTR);
RCCStatus = RCC_STAT_INIT;
PostRCCMessage(RCC_CLOSE,0);
Init();
StopComm();
return 0;
/*
DWORD ModemStatus;
if(!GetCommModemStatus(hCommFile,&ModemStatus))
{
RCCStatus = RCC_STAT_INIT;
return 1;
}
PurgeComm(hCommFile,PURGE_TXABORT|PURGE_TXCLEAR|PURGE_RXABORT|PURGE_RXCLEAR);
if(ModemStatus & MS_RLSD_ON)
{ //如果CD灯亮的话
WaitForCommandResult(200); //休息200ms,以备足够的SDU时间来返送转义序列
MDMResult = MDM_NONE;
WriteCommand("+++",TRUE);
WaitForCommandResult(300);
}
MDMResult = MDM_NONE;
WriteCommand("H0"); //挂起电话
WaitForCommandResult(300);
MDMResult = MDM_NONE;
WriteCommand("Z0"); //重新初始化MODEM
WaitForCommandResult(300);
*/
}
//
// 函数: int CRCCComm::WriteCommand(char *CmdStr,BOOL Direct)
// 用途: 向MODEM写入命令.
// 参数:
// CmdStr 命令字符串
// Direct 是否直接写入,为FALSE则需要加AT前缀
// 返回值:
// 0 表示成功
// 其它表示失败
// 备注:
// 在MODEM连线方式下,向MODEM写入命令串,在RS232方式下,直接成功返回
//
int CRCCComm::WriteCommand(char *CmdStr,BOOL Direct)
{
if(RCCMode == MODE_RS232)
return 0;
char cCmdStr[80];
if(Direct)
sprintf(cCmdStr,"%s\r",CmdStr);
else
sprintf(cCmdStr,"AT%s\r",CmdStr);
// sprintf(cCmdStr,"ATQ0E1V1%s\r",CmdStr);
int dwNumberOfBytesToWrite = strlen(cCmdStr);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -