📄 transportserial.cpp
字号:
// ========================================================
// 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 + -