浏览代码

重点问题:
typedef struct _COMMTIMEOUTS {
DWORD ReadIntervalTimeout; /* Maximum time between read chars. */
DWORD ReadTotalTimeoutMultiplier; /* Multiplier of characters. */
DWORD ReadTotalTimeoutConstant; /* Constant in milliseconds. */
DWORD WriteTotalTimeoutMultiplier; /* Multiplier of characters. */
DWORD WriteTotalTimeoutConstant; /* Constant in milliseconds. */
} COMMTIMEOUTS,*LPCOMMTIMEOUTS;

ReadIntervalTimeout如果设置为0,表示该参数不起作用,此时,会导致ReadFile异常:如果不延长等待时间,ReadFile会失败,不管是同步还是异步的情况下。
所以,这里的所有值,都要设置清楚,不然ReadFile会产生异想不到的结果。

sat23 4 年之前
父节点
当前提交
be39920e0c

+ 19 - 3
Serail-Demo/Demo/Demo.cpp

@@ -28,11 +28,27 @@ int _tmain(int argc, TCHAR* argv[], TCHAR* envp[])
 	}
 	else
 	{
-		std::vector<CommandParam> vtCommandParams;
-		parse_cmd_param_from_file("F:\\source\\scbc_repos\\Serail-Demo\\Demo\\command.data", vtCommandParams);
 		// TODO: 在此处为应用程序的行为编写代码。
 		CBaseSerial sb;
-		sb.Open(23, _T("baud=115200 parity=N data=8 stop=1") );
+		if ( sb.Open(23, _T("baud=115200 parity=N data=8 stop=1") ) )
+		{
+			sb.SetTimeouts();
+			std::vector<CommandParam> vtCommandParams;
+			parse_cmd_param_from_file("F:\\source\\scbc_repos\\Serail-Demo\\Demo\\command.data", vtCommandParams);
+			for ( std::vector<CommandParam>::iterator it = vtCommandParams.begin(); it != vtCommandParams.end(); it++ )
+			{
+				byte szData[1024] = {0};
+				std::string cmd = PackingCommand(*it, _T(""), 0);
+				//sb.Read(szData, 1024, 100);
+				if ( sb.Write((void*)cmd.c_str(), cmd.size()) )
+				{
+					Sleep(it->read_wait_time);
+					int c = sb.Read(szData, 1024);
+
+					int a = 0;
+				}
+			}
+		}
 
 
 		system("pause");

+ 98 - 24
Serail-Demo/Demo/Serial.cpp

@@ -1,6 +1,21 @@
 #include "stdafx.h"
 #include "Serial.h"
 
+VOID _dprintf(CHAR* pszStr, ...)
+{
+	const int LOGLEN = 3072;
+	char szData[LOGLEN] = { 0 };
+	_stprintf_s(szData, _T("[%s %s]\n\t"), _T("Db"), CTime::GetCurrentTime().Format(_T("%H:%M:%S")).GetString());
+	int len = strlen(szData);
+	va_list args;
+	va_start(args, pszStr);
+	_vsnprintf_s(szData + len, LOGLEN - len, LOGLEN - len, pszStr, args);
+	va_end(args);
+	if (szData[strlen(szData) - 1] != '\n')
+		strcat_s(szData, "\n");
+	OutputDebugStringA(szData);
+}
+
 DCB *CBaseSerial::GetState()
 {
 	return IsOpen() && ::GetCommState(_hCommHandle, &_DCB) == TRUE ? &_DCB : NULL;
@@ -45,8 +60,9 @@ LPCOMMTIMEOUTS CBaseSerial::GetTimeouts()
 	return IsOpen() && ::GetCommTimeouts(_hCommHandle, &_CO) == TRUE ? &_CO : NULL;
 }
 
-bool CBaseSerial::SetTimeouts(COMMTIMEOUTS CO)
+bool CBaseSerial::SetTimeouts(DWORD ReadIntervalTimeout /* = 0 */, DWORD ReadTotalTimeoutMultiplier /* = 10 */, DWORD ReadTotalTimeoutConstant /* = 1500 */, DWORD WriteTotalTimeoutMultiplier /* = 10 */, DWORD WriteTotalTimeoutConstant /* = 1500 */)
 {
+	COMMTIMEOUTS CO = {ReadIntervalTimeout, ReadTotalTimeoutMultiplier, ReadTotalTimeoutConstant, WriteTotalTimeoutMultiplier, WriteTotalTimeoutConstant};
 	return IsOpen() ? ::SetCommTimeouts(_hCommHandle, &CO) == TRUE : false;
 }
 
@@ -113,8 +129,7 @@ DWORD CBaseSerial::Read(LPVOID Buffer, DWORD dwBufferLength, DWORD dwWaitTime /*
 
 	COMSTAT Stat;
 	DWORD dwError;
-	if (::ClearCommError(_hCommHandle, &dwError, &Stat) && dwError > 0)
-	{
+	if (::ClearCommError(_hCommHandle, &dwError, &Stat) && dwError > 0) {
 		::PurgeComm(_hCommHandle, PURGE_RXABORT | PURGE_RXCLEAR);	// 清空输入缓冲区(PURGE_RXCLEAR)
 		return 0;
 	}
@@ -124,25 +139,33 @@ DWORD CBaseSerial::Read(LPVOID Buffer, DWORD dwBufferLength, DWORD dwWaitTime /*
 	if (!Stat.cbInQue)
 		return 0; // 缓冲区无数据
 #endif
+	_dprintf("缓冲区数据量:%ld", Stat.cbInQue);
 
 	unsigned long uReadLength = 0;
 	// dwBufferLength = dwBufferLength > Stat.cbInQue ? Stat.cbInQue : dwBufferLength;//会越界;
-	if (!::ReadFile(_hCommHandle, Buffer, dwBufferLength, &uReadLength, &_ReadOverlapped)) // 可能Buffer不够大,装不完;
-	{
-		if (::GetLastError() == ERROR_IO_PENDING)
-		{
-			WaitForSingleObject(_ReadOverlapped.hEvent, dwWaitTime);
-			// 结束异步I/O
-			while (!::GetOverlappedResult(_hCommHandle, &_ReadOverlapped, &uReadLength, false))
-			{
-				Sleep(50);
+	if (!::ReadFile(_hCommHandle, Buffer, dwBufferLength, &uReadLength, &_ReadOverlapped))  { // 可能Buffer不够大,装不完;
+		// WaitForSingleObject(_ReadOverlapped.hEvent, dwWaitTime); // 没必要这里等;
+		if (::GetLastError() == ERROR_IO_PENDING) {
+			while (!::GetOverlappedResult(_hCommHandle, &_ReadOverlapped, &uReadLength, false)) {
+				dwError = ::GetLastError();
+				if ( dwError == ERROR_IO_PENDING ) {
+					Sleep(50);
+					_dprintf("读等待:%ld", dwError);
+				} else if ( dwError == ERROR_SUCCESS || dwError == ERROR_IO_INCOMPLETE ) {
+					_dprintf("读完成:%ld,%ld,%ld", dwError, uReadLength,_ReadOverlapped.InternalHigh);
+					// 奇葩:_ReadOverlapped并不能马上出长度,在超时后才会刷新出来(不知是否与Win平台有关);
+					if ( _ReadOverlapped.InternalHigh )
+						uReadLength = _ReadOverlapped.InternalHigh;
+					else
+						uReadLength = Stat.cbInQue;
+					break;
+				} else {
+					_dprintf("读错误:%ld", dwError);
+					uReadLength = 0;
+					break;
+				}
 			}
-
-			if (::GetLastError() != ERROR_IO_INCOMPLETE)
-				uReadLength = 0;
 		}
-		else
-			uReadLength = 0;
 	}
 
 	return uReadLength;
@@ -166,8 +189,24 @@ DWORD CBaseSerial::Write(LPVOID Buffer, DWORD dwBufferLength)
 	
 	unsigned long uWriteLength = 0;
 	if (!::WriteFile(_hCommHandle, Buffer, dwBufferLength, &uWriteLength, &_WriteOverlapped)) {
-		if (::GetLastError() != ERROR_IO_PENDING) {			
-			uWriteLength = 0;
+		if (ERROR_IO_PENDING == GetLastError()) {
+			while (!GetOverlappedResult(_hCommHandle, &_WriteOverlapped, &uWriteLength, FALSE)) {
+				_dprintf("GetOverlappedResult");
+				dwError = GetLastError();
+				if (ERROR_IO_PENDING == dwError) {
+					_dprintf("写等待");
+					continue;
+				} else if (dwError == ERROR_SUCCESS || dwError == ERROR_IO_INCOMPLETE){
+					uWriteLength = _WriteOverlapped.InternalHigh;
+					_dprintf("写完成:%ld", dwError);
+					break;
+				} else {
+					_dprintf("写出错:%ld",dwError);
+					uWriteLength = 0;
+					ClearCommError(_hCommHandle, &dwError, NULL);
+					break;
+				}
+			}
 		}
 	}
 
@@ -185,15 +224,46 @@ DWORD CBaseSerial::ReadSync(LPVOID Buffer, DWORD dwBufferLength)
 	if (!IsOpen())
 		return 0;
 	
+	COMSTAT Stat;
 	DWORD dwError;
-	if (::ClearCommError(_hCommHandle, &dwError, NULL) && dwError > 0)
-	{
-		::PurgeComm(_hCommHandle, PURGE_RXABORT | PURGE_RXCLEAR);	// 清空输入缓冲区(PURGE_RXCLEAR)
+
+	DWORD dwLastLen = 0;
+	ULONGLONG ulTick = GetTickCount();	
+	// 直到有数据为止,超时3秒;
+	while (true) {// cbInQue表示输入缓冲区的字节数; 
+		if (GetTickCount() - ulTick > 3000) {
+			_dprintf("读出错: 超过3秒仍未读完");
+			break;
+		}
+
+		if ( ::ClearCommError(_hCommHandle, &dwError, &Stat) && dwError > 0 ) {
+			::PurgeComm(_hCommHandle, PURGE_RXABORT | PURGE_RXCLEAR);	// 清空输入缓冲区(PURGE_RXCLEAR)
+			_dprintf("读出错: ClearCommError出错");
+			break;
+		}
+
+		// 防止读一次没读完,再读一次;二次长度一样表示读完;
+		if (Stat.cbInQue != 0 && dwLastLen == Stat.cbInQue)
+			break;
+		dwLastLen = Stat.cbInQue;
+		Sleep(100);
+	}
+
+	if (Stat.cbInQue == 0) {
+		// 串口超时,无数据返回;
 		return 0;
 	}
 
+	if (dwBufferLength < Stat.cbInQue) {
+		_dprintf("读出错: 缓冲数据过大 %ld > %ld", dwBufferLength, Stat.cbInQue);
+	}
+
 	DWORD uReadLength = 0;
-	::ReadFile(_hCommHandle, Buffer, dwBufferLength, &uReadLength, NULL);
+	if ( !::ReadFile(_hCommHandle, Buffer, dwBufferLength, &uReadLength, NULL) )
+	{
+		DWORD dwError = GetLastError();
+		_dprintf("读出错:%ld", dwError);
+	}
 
 	return uReadLength;
 }
@@ -208,7 +278,11 @@ DWORD CBaseSerial::WriteSync(LPVOID Buffer, DWORD dwBufferLength)
 		::PurgeComm(_hCommHandle, PURGE_TXABORT | PURGE_TXCLEAR);	// 清空输出缓冲区(PURGE_TXCLEAR)
 	
 	unsigned long uWriteLength = 0;
-	::WriteFile(_hCommHandle, Buffer, dwBufferLength, &uWriteLength, NULL);
+	if ( !::WriteFile(_hCommHandle, Buffer, dwBufferLength, &uWriteLength, NULL) )
+	{
+		DWORD dwError = GetLastError();
+		_dprintf("写出错:%ld", dwError);
+	}
 
 	return uWriteLength;
 }

+ 6 - 1
Serail-Demo/Demo/Serial.h

@@ -57,7 +57,12 @@ public:
     // 获得超时结构
     LPCOMMTIMEOUTS GetTimeouts(void);    
     // 设置超时
-	bool SetTimeouts(COMMTIMEOUTS CO /*= {0, 10, 1500, 10, 1500}*/);
+	bool SetTimeouts(
+		DWORD ReadIntervalTimeout = 5, 
+		DWORD ReadTotalTimeoutMultiplier = 10, 
+		DWORD ReadTotalTimeoutConstant = 500, 
+		DWORD WriteTotalTimeoutMultiplier = 10, 
+		DWORD WriteTotalTimeoutConstant = 500);
     bool SetTimeouts(LPCOMMTIMEOUTS lpCO);
     // 设置串口的I/O缓冲区大小
     bool SetBufferSize(DWORD dwInputSize, DWORD dwOutputSize);

+ 1 - 1
Serail-Demo/Demo/command.data

@@ -1,9 +1,9 @@
 # Get Info;
 Name=EnterFactory; HeadCode=AA; Command=10; CMDParam=01; MultiParams=false; ReadWaitTime=100ms; CMDWaitTime=100ms
+Name=GetSoftVersion; HeadCode=AA; Command=57; CMDParam=00; MultiParams=false; ReadWaitTime=100ms; CMDWaitTime=100ms
 Name=LeaveFactory; HeadCode=AA; Command=10; CMDParam=00; MultiParams=false; ReadWaitTime=100ms; CMDWaitTime=100ms
 Name=WBInit; HeadCode=AA; Command=16; CMDParam=02; MultiParams=false; ReadWaitTime=100ms; CMDWaitTime=100ms
 Name=GetProjectID; HeadCode=AA; Command=84; CMDParam=00; MultiParams=false; ReadWaitTime=100ms; CMDWaitTime=100ms
-Name=GetSoftVersion; HeadCode=AA; Command=57; CMDParam=00; MultiParams=false; ReadWaitTime=100ms; CMDWaitTime=100ms
 Name=GetDeviceId; HeadCode=AA; Command=BE; CMDParam=01; MultiParams=false; ReadWaitTime=100ms; CMDWaitTime=100ms
 Name=GetClientType; HeadCode=AA; Command=8C; CMDParam=00; MultiParams=false; ReadWaitTime=100ms; CMDWaitTime=100ms
 Name=GetMAC; HeadCode=AA; Command=BE; CMDParam=00; MultiParams=true; ReadWaitTime=100ms; CMDWaitTime=100ms

+ 81 - 29
Serail-Demo/Demo/stdafx.cpp

@@ -96,7 +96,7 @@ int parse_cmd_param_from_file(TCHAR *file_name, std::vector<CommandParam> &vtCom
 
 	int ret = -1;
 	FILE *fp = NULL;
-	
+
 	if(!file_name || file_name[0] == '\0' )
 		return ret;
 
@@ -125,27 +125,27 @@ int parse_cmd_param_from_file(TCHAR *file_name, std::vector<CommandParam> &vtCom
 		if ( _stscanf_s(buf, "%[^;];%[^;];%[^;];%[^;];%[^;];%[^;];%s", name, MAX_PATH, head, MAX_PATH, code, MAX_PATH, param, MAX_PATH, multicode, MAX_PATH, read_wait_time, MAX_PATH, cmd_wait_time, MAX_PATH) == 7)
 #endif
 #if _MSC_VER >= 1200 && _MSC_VER < 1500 // VC6.0~8.0 
-		if ( sscanf(buf, "%[^;];%[^;];%[^;];%[^;];%[^;];%[^;];%[^;]", name, head, code, param, multicode, read_wait_time, cmd_wait_time) == 7)
+			if ( sscanf(buf, "%[^;];%[^;];%[^;];%[^;];%[^;];%[^;];%[^;]", name, head, code, param, multicode, read_wait_time, cmd_wait_time) == 7)
 #endif
-		{
-			CommandParam cp;
-			parse_key(cp.name, name, _T("Name"));
-			parse_key(cp.head, head, _T("HeadCode"));
-			parse_key(cp.code, code, _T("Command"));
-			parse_key(cp.param, param, _T("CMDParam"));
-			
-			std::string value;
-			parse_key(value, multicode, _T("MultiParams"));
-			cp.bMulticode = !_tcsicmp(value.c_str(), _T("true")) ? true : false;
-
-			parse_key(value, read_wait_time, _T("ReadWaitTime"));
-			cp.read_wait_time = pares_time_value(value.c_str());
-
-			parse_key(value, cmd_wait_time, _T("CMDWaitTime"));
-			cp.cmd_wait_time = pares_time_value(value.c_str());
-
-			vtCommandParams.push_back(cp);
-		}
+			{
+				CommandParam cp;
+				parse_key(cp.name, name, _T("Name"));
+				parse_key(cp.head, head, _T("HeadCode"));
+				parse_key(cp.code, code, _T("Command"));
+				parse_key(cp.param, param, _T("CMDParam"));
+
+				std::string value;
+				parse_key(value, multicode, _T("MultiParams"));
+				cp.bMulticode = !_tcsicmp(value.c_str(), _T("true")) ? true : false;
+
+				parse_key(value, read_wait_time, _T("ReadWaitTime"));
+				cp.read_wait_time = pares_time_value(value.c_str());
+
+				parse_key(value, cmd_wait_time, _T("CMDWaitTime"));
+				cp.cmd_wait_time = pares_time_value(value.c_str());
+
+				vtCommandParams.push_back(cp);
+			}
 	}
 
 	ret = 0;
@@ -157,8 +157,9 @@ EXIT:
 
 unsigned char TwoHexCharToInteger(char high, char low)
 {
-	if (!isalpha(high) && isdigit(high) || !isalpha(low) && isdigit(low)) {
-		// 应用输出日志;
+	std::string str = "0123456789AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz";
+	if ( str.find_first_of(high) == std::string::npos || str.find_first_of(low) == std::string::npos )
+	{
 		return 0;
 	}
 
@@ -175,12 +176,11 @@ const unsigned short CRC16_TABLE[16] = {
 
 unsigned short CRC16Calculate(byte *pBuffer, unsigned int wordLength)
 {
-	unsigned short wordCRC;
-	byte byteTemp;
+	byte byteTemp(0);
+	unsigned short wordCRC(0);
 
 	wordCRC = 0xFFFF;
-	while (wordLength--)
-	{
+	while (wordLength--) {
 		byteTemp = (byte)(wordCRC >> 0x0C);
 		wordCRC <<= 4;
 		wordCRC ^= CRC16_TABLE[byteTemp ^ ((*pBuffer) >> 0x04)];
@@ -219,7 +219,7 @@ std::string BytesToHexString(const unsigned char *pbuffer, int nLen, char chSpac
 	{
 		memset(szhex, 0, 5);
 #if _MSC_VER >= 1200 && _MSC_VER < 1500
-	sprintf(szhex, "%02X%c", pbuffer[i], chSpace);
+		sprintf(szhex, "%02X%c", pbuffer[i], chSpace);
 #endif
 #if _MSC_VER >= 1500
 		_stprintf_s(szhex, "%02X%c", pbuffer[i], chSpace);
@@ -237,7 +237,7 @@ std::string BytesToHexString(const unsigned char *pbuffer, int nLen)
 	for (int i = 0; i < nLen; i++)
 	{
 		memset(szhex, 0, 5);
-		
+
 #if _MSC_VER >= 1200 && _MSC_VER < 1500
 		sprintf(szhex, "%02X", pbuffer[i]);
 #endif
@@ -287,4 +287,56 @@ std::string &trim(std::string &str)
 	}
 #endif
 	return str;
+}
+
+std::string PackingCommand(CommandParam &cmdPara, std::string data, const int &dataLen)
+{
+	std::string command;
+	// 命令头标识位;
+	command.append(HexStringToBytes(cmdPara.head, 2).c_str(), cmdPara.head.size() / 2);
+	// 命令码;
+	command.append(HexStringToBytes(cmdPara.code, 2).c_str(), cmdPara.code.size() / 2);
+	// 命令码参数;
+	command.append(HexStringToBytes(cmdPara.param, 2).c_str(), cmdPara.param.size() / 2);
+	// 附加的数据;
+	if ( dataLen > 0)
+		command.append(data.c_str(), dataLen);
+
+	int len(0);
+	// 长度:可能1字节表示,超过255用2字节表示;
+	byte szlen[2] = {0};
+	//if ((byte)command[1] == 0xFE)
+	//if ( cmdPara.head.size() >= 4 && cmdPara.head.find("FE", 2, 2) != std::string::npos )
+	if ( _tcsicmp(_T("AAFE"), cmdPara.head.c_str()) == 0 )
+	{// 长度超过255,需要2字节表示;
+		len = command.size() + 4;	// 2位crc + 2位长度;
+		szlen[0] = (len >> 8) & 0xFF;
+		szlen[1] = len & 0xFF;
+		command.insert(2, (char *)szlen, 2);
+	} else {
+		// 2位crc + 1位长度;
+		len = command.size() + 3;	
+		//if ( _tcsicmp(cmdPara.code.c_str(), "99 00") == 0 )
+		//	len -= 2;
+
+		if ( len > 255 ) {// 长度超过255,多一个占位符;
+			++len;
+			szlen[0] = (len >> 8) & 0xFF;
+			szlen[1] = len  & 0xFF;
+			command.insert(1, (char *)szlen, 2);
+		} else {
+			szlen[0] = len & 0xFF;
+			command.insert(1, (char *)szlen, 1);
+		}
+	}
+
+	// crc校验;
+	byte szcrc[2] = {0};
+	//WORD usCRCValue = CRC16Calculate((byte *)command.c_str(), command.size()); // 如果有0断开有危险;
+	WORD usCRCValue = CRC16Calculate((byte *)command.c_str(), len);
+	szcrc[0] = (usCRCValue >> 8) & 0xFF;
+	szcrc[1] = usCRCValue & 0xFF;
+	command.append((char *)szcrc, 2);
+
+	return command;
 }

+ 1 - 0
Serail-Demo/Demo/stdafx.h

@@ -45,4 +45,5 @@ int pares_time_value(std::string strTime);
 bool parse_key(std::string &value, std::string strLine, TCHAR *lpszText);
 int parse_cmd_param_from_file(char *file_name, std::vector<CommandParam> &vtCommandParams);
 unsigned char TwoHexCharToInteger(char high, char low);
+std::string PackingCommand(CommandParam &cmdPara, std::string data, const int &dataLen);
 // TODO: 在此处引用程序需要的其他头文件