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

📄 transportserial.cpp

📁 .net 方面的开发说明资料。
💻 CPP
📖 第 1 页 / 共 2 页
字号:
// ========================================================
// Serial Port Overlapped I/O
//
// http://www.codeproject.com/system/serial.asp
// http://www.naughter.com/serialport.html
// http://www.alumni.caltech.edu/~dank/overlap.htm
// http://iglib.insanegenius.com/
//
// Design and Implementation by Floris van den Berg
// ========================================================

#pragma warning (disable : 4275)
#pragma warning (disable : 4786)

#include <assert.h>
#include <windows.h>

#include "Agent.h"
#include "EventDispatcher.h"
#include "TransportSerial.h"
#include "OpenNet.h"
#include "Utilities.h"

// --------------------------------------------------------
// Internal stuff
// --------------------------------------------------------

enum SEND_RESULT {
	SEND_DONE = 0,
	SEND_FAILED,
	SEND_PENDING,
};

// --------------------------------------------------------
// Serial transport component
// --------------------------------------------------------

class CTransportSerial : public ITransport {
public :
	CTransportSerial();
	virtual ~CTransportSerial();

public :
	virtual bool DLL_CALLCONV Open(PROPERTYGROUP_HANDLE group);
	virtual void DLL_CALLCONV PassHandleIndirect(long handle);
	virtual void DLL_CALLCONV Close();
	virtual void DLL_CALLCONV Connect(const char *host, int port);
	virtual void DLL_CALLCONV Disconnect();
	virtual bool DLL_CALLCONV SupportsSending();
	virtual void DLL_CALLCONV Send(Action *action);
	virtual void* DLL_CALLCONV GetReferenceData(int id);
	virtual bool DLL_CALLCONV GetOption(int option, void *value, int *size);
	virtual bool DLL_CALLCONV SetOption(int option, void *value);
	virtual bool DLL_CALLCONV CanBeBalanced();
	virtual void DLL_CALLCONV PollProgress();
	virtual void DLL_CALLCONV IncActionCount();
	
private :
	bool PollIncomingEvents();
	SEND_RESULT PollSend(Action *ac);
	void HandleEvent(DWORD event);
	bool GetOptionA(int option, void *value, int *size);
	bool GetOptionB(int option, void *value, int *size);

private :
	boost::mutex m_cs;
	Action *m_action;
	HANDLE m_handle;
	bool m_send_pending;
	bool m_wait_pending;
	OVERLAPPED m_overlapped_wait;
	OVERLAPPED m_overlapped_rx;
	OVERLAPPED m_overlapped_tx;
	DWORD m_com_event;
	bool m_delay_before_send;
	int m_idle_timestamp;
	TDCB m_comm_state;
	COMMTIMEOUTS m_timeouts;
	LONG m_action_count;
	bool m_modem;
};

// --------------------------------------------------------
// Constructor
// --------------------------------------------------------

CTransportSerial::CTransportSerial() :
m_cs(),
m_action(NULL),
m_handle(NULL),
m_send_pending(false),
m_wait_pending(false),
m_overlapped_wait(),
m_overlapped_rx(),
m_overlapped_tx(),
m_delay_before_send(false),
m_idle_timestamp(EpGetTickCount()),
m_comm_state(),
m_timeouts(),
m_action_count(0),
m_modem(false) {
}

CTransportSerial::~CTransportSerial() {
	m_send_pending = false;
}

// --------------------------------------------------------
// Open/Close/Connect/Disconnect a Plug
// --------------------------------------------------------

bool 
CTransportSerial::Open(PROPERTYGROUP_HANDLE group) {
	// create the transport

	if (group) {
		std::string port("", 1024);
		EpPropGetValue(group, "port", &port[0]);

		m_handle = CreateFile(port.c_str(), GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, 0);

		if (m_handle != INVALID_HANDLE_VALUE) {
			if (SetupComm(m_handle, 2048, 2048)) {
				if (GetCommState(m_handle, (DCB *)&m_comm_state)) {
					DWORD parity = 0xFFFFFFFF;
					DWORD baudrate = 0xFFFFFFFF;
					DWORD bytesize = 0xFFFFFFFF;
					DWORD stopbits = 0xFFFFFFFF;
					DWORD options = 0xFFFFFFFF;					

					EpPropGetValueAsInt(group, "parity", (int *)&parity);
					EpPropGetValueAsInt(group, "baudrate", (int *)&baudrate);
					EpPropGetValueAsInt(group, "bytesize", (int *)&bytesize);
					EpPropGetValueAsInt(group, "stopbits", (int *)&stopbits);
					EpPropGetValueAsInt(group, "options", (int *)&options);
					EpPropGetValueAsBool(group, "modem", &m_modem);

					TDCB dcb;
					memset(&dcb, 0, sizeof(TDCB));
					dcb.DCBlength = sizeof(TDCB);
					dcb.Parity = parity != 0xFFFFFFFF ? parity : m_comm_state.Parity;
					dcb.BaudRate = baudrate != 0xFFFFFFFF ? baudrate : m_comm_state.BaudRate;
					dcb.ByteSize = bytesize != 0xFFFFFFFF ? bytesize : m_comm_state.ByteSize;
					dcb.StopBits = stopbits != 0xFFFFFFFF ? stopbits : m_comm_state.StopBits;
					dcb.Options = options != 0xFFFFFFFF ? options : m_comm_state.Options;
					((DCB *)&dcb)->fBinary = TRUE;

					if (SetCommState(m_handle, (DCB *)&dcb)) {
						// these timeout settings are reported to be the only ones
						// fully working on both Windows 95/98/ME and Windows NT/2k/XP

						if (GetCommTimeouts(m_handle, &m_timeouts)) {
							COMMTIMEOUTS timeouts;
							timeouts.ReadIntervalTimeout         = 1;
							timeouts.ReadTotalTimeoutMultiplier  = 0;
							timeouts.ReadTotalTimeoutConstant    = 0;
							timeouts.WriteTotalTimeoutMultiplier = 0;
							timeouts.WriteTotalTimeoutConstant   = 0;

							if (SetCommTimeouts(m_handle, &timeouts)) {
								if (SetCommMask(m_handle, EV_BREAK | EV_CTS | EV_DSR | EV_ERR | EV_RING | EV_RLSD | EV_RXCHAR | EV_TXEMPTY)) {
									if (PurgeComm(m_handle, PURGE_TXCLEAR | PURGE_RXCLEAR | PURGE_TXABORT | PURGE_RXABORT)) {
										// create an overlapped struct for the WaitCommEvent								

										memset(&m_overlapped_wait, 0, sizeof(OVERLAPPED));
										m_overlapped_wait.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);

										// create an overlapped struct for reading

										memset(&m_overlapped_rx, 0, sizeof(OVERLAPPED));
										m_overlapped_rx.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);

										// create an overlapped struct for writing

										memset(&m_overlapped_tx, 0, sizeof(OVERLAPPED));
										m_overlapped_tx.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
										return true;
									}									
								}
								
								SetCommMask(m_handle, 0);
							}

							SetCommTimeouts(m_handle, &m_timeouts);
						}

						SetCommState(m_handle, (DCB *)&m_comm_state);						
					}
				}
			}

			CloseHandle(m_handle);
		}
	}

	m_handle = NULL;
	return false;
}

void 
CTransportSerial::PassHandleIndirect(long handle) {
}

void 
CTransportSerial::Close() {
	if (m_handle) {
		DWORD error = 0;

		// vars

		m_action = NULL;
		m_send_pending = false;
		m_action_count = 0;
		m_delay_before_send = false;

		// drop DTR and RTS

		EscapeCommFunction(m_handle, CLRDTR);
		EscapeCommFunction(m_handle, CLRRTS);

		// flush any remaining data on the input buffer

		PurgeComm(m_handle, PURGE_RXCLEAR);

		// test

		ClearCommError(m_handle, &error, NULL);

		// restore timeouts

		SetCommTimeouts(m_handle, &m_timeouts);

		// restore the mask

		SetCommMask(m_handle, 0);

		// restore the DCB

		SetCommState(m_handle, (DCB *)&m_comm_state);

		// delete the com port sync events

		CloseHandle(m_overlapped_rx.hEvent);
		CloseHandle(m_overlapped_tx.hEvent);
		CloseHandle(m_overlapped_wait.hEvent);

		// and close the handle

		CloseHandle(m_handle);
	}
}

void 
CTransportSerial::Connect(const char *host, int port) {
	boost::mutex::scoped_lock scoped_lock(m_cs);

	// make sure no data is pending on the comport buffer

	PurgeComm(m_handle, PURGE_TXCLEAR | PURGE_RXCLEAR);

	// change the DTR and RTS signals

	EscapeCommFunction(m_handle, SETDTR);
	EscapeCommFunction(m_handle, SETRTS);
}

void 
CTransportSerial::Disconnect() {
	boost::mutex::scoped_lock scoped_lock(m_cs);

	if (m_handle) {
		// clear the m_action so that no data is sent anymore

		m_action = NULL;
		m_delay_before_send = false;
		m_action_count = 0;
		m_send_pending = false;

		// drop DTR and RTS

		EscapeCommFunction(m_handle, CLRDTR);
		EscapeCommFunction(m_handle, CLRRTS);

		// flush any remaining data on the output buffer

		FlushFileBuffers(m_handle);

		// flush any remaining data on the input buffer

		PurgeComm(m_handle, PURGE_RXCLEAR);
	}
}

bool 
CTransportSerial::SupportsSending() {
	return true;
}

void 
CTransportSerial::Send(Action *ac) {
	boost::mutex::scoped_lock scoped_lock(m_cs);

	assert(m_action == NULL);

	m_action = ac;
}

void * 
CTransportSerial::GetReferenceData(int id) {
	return NULL;
}

bool 
CTransportSerial::GetOption(int option, void *value, int *size) {
	boost::mutex::scoped_lock scoped_lock(m_cs);

	if ((value == 0) && (size != 0))
		return GetOptionA(option, value, size);
	else
		return GetOptionB(option, value, size);
}

bool 
CTransportSerial::SetOption(int option, void *value) {
	boost::mutex::scoped_lock scoped_lock(m_cs);

	switch(option) {
		case SERIAL_SET_DCB :
			return (SetCommState(m_handle, (DCB *)value) != 0);

		case SERIAL_CLEAR_DTR :
			return (EscapeCommFunction(m_handle, CLRDTR) != 0);

		case SERIAL_CLEAR_RTS :
			return (EscapeCommFunction(m_handle, CLRRTS) != 0);

		case SERIAL_SET_DTR :
			return (EscapeCommFunction(m_handle, SETDTR) != 0);

		case SERIAL_SET_RTS :
			return (EscapeCommFunction(m_handle, SETRTS) != 0);

		case SERIAL_SET_XOFF :
			return (EscapeCommFunction(m_handle, SETXOFF) != 0);

		case SERIAL_SET_XON :
			return (EscapeCommFunction(m_handle, SETXON) != 0);

		case SERIAL_SET_BREAK :
			return (EscapeCommFunction(m_handle, SETBREAK) != 0);

		case SERIAL_CLEAR_BREAK :
			return (EscapeCommFunction(m_handle, CLRBREAK) != 0);
	};

	return false;
}

bool 
CTransportSerial::CanBeBalanced() {
	return false;
}

void 
CTransportSerial::PollProgress() {
	boost::mutex::scoped_lock scoped_lock(m_cs);

	// check for a new action to send, if we aren't sending anything

	if ((m_action == NULL) && (!m_send_pending) && (m_action_count > 0)) {
		--m_action_count;
		m_action = EpGetNextAction(this);
	}

	// handle send actions

	EpEvent pm_event;
	pm_event.reference_id = 0;
	pm_event.protocol = CLSID_SYSTEM_PROTOCOL;
	pm_event.size = 0;
	pm_event.data = NULL;

	if (m_action) {
		// if we just connected, we must wait 2 seconds first to give the modem
		// some time to do the DSR switching. we could just blatantly do Sleep(2000)
		// but then it's possible we wait way longer than 2 seconds. Instead we use
		// the same mechanism to wait as in the timeout manager (using a timestamp).
		// when the user tries to send anything there has already been some time
		// passed, so we sleep the remaining time...

		if (m_delay_before_send) {
			int tick = EpGetTickCount();

			if (tick - m_idle_timestamp < 2000) {
				Sleep(2000 - (tick - m_idle_timestamp));

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -