RS232 / USB read/write?
Mike James
foo at bar.com
Mon Feb 1 05:19:50 PST 2010
Brian Hay Wrote:
> I'm a bit green with D. I've done some basic D1 + Tango + Derelict stuff
> but am keen to learn more about D2 + Phobos.
>
> As a learning project I thought I'd like to try reading/writing data
> streams from/to RS232 and/or USB devices, something I've never done
> before in any language.
>
> Can anyone give me some pointers and online references? Are there any D
> libraries for this?
This something I wrote a while ago for D1 + Tango, it will probably need a bit of tweeking to get it to the latest compiler version. Sorry for having to include it in the body text...
==============================================
module comms;
private import tango.sys.Common,
tango.time.Time,
tango.stdc.stdint,
tango.io.Stdout,
StdC = tango.stdc.String,
Integer = tango.text.convert.Integer,
Float = tango.text.convert.Float,
tango.text.Util,
tango.stdc.Stringz,
tango.core.Thread,
tango.io.device.ThreadConduit;
private import ascii;
public class Comms {
/*
* Constants
*/
private const uint RX_CHAR_COUNT = 1;
private const char[] ALT_NAME_PREFIX = r"\\.\";
private const char[] PORT_NOT_OPEN_EXCEPTION = "Serial Port not Open";
public const uint INFINITE_TIMEOUT = uint.max;
/*
* Enumerations
*/
public enum Parity : ubyte {
None = cast(ubyte)PARITY_NONE,
Odd = cast(ubyte)PARITY_ODD,
Even = cast(ubyte)PARITY_EVEN,
Mark = cast(ubyte)PARITY_MARK,
Space = cast(ubyte)PARITY_SPACE
}
public enum StopBits : ubyte {
One = cast(ubyte)ONESTOPBIT,
OnePointFive = cast(ubyte)ONE5STOPBITS,
Two = cast(ubyte)TWOSTOPBITS
}
public enum Handshake : int {
None,
Hardware,
Software
}
private enum SigState : int {
Low,
High,
NotUsed
}
private enum WaitFor {
ThreadTerminate,
Event,
MaxSize
}
private HANDLE hComm = null;
private HANDLE hRxThreadStarted = null;
private HANDLE hRxThreadDone = null;
private HANDLE hReadLine = null;
private Thread receiveThread;
private ThreadConduit tcRxConduit;
private bool isOpen_ = false;
private char[] portName_ = "COM1";
private uint baudrate_ = 9600;
private int dataBits_ = 8;
private StopBits stopBits_ = StopBits.One;
private Parity parity_ = Parity.None;
private Handshake handshake_ = Handshake.None;
private char xonChar_ = ASCII.DC1;
private char xoffChar_ = ASCII.DC3;
private char errorChar_ = ASCII.NUL;
private uint rxQueue_ = 0;
private uint txQueue_ = 0;
private SigState rtsState_ = SigState.NotUsed;
private SigState dtrState_ = SigState.NotUsed;
private SigState breakState_ = SigState.NotUsed;
private uint rxTimeout_ = INFINITE_TIMEOUT;
private uint txTimeoutConst_ = 0;
private uint txTimeoutMult_ = 0;
private short xonLowLevel_ = 0;
private short xoffHighLevel_ = 0;
private bool discardNull_ = false;
/*
* Delegates
*/
private void delegate(uint, char[]) dgDataEvent;
private void delegate() dgTxEmptyEvent;
private void delegate() dgBreakEvent;
private void delegate(ModemStatus, ModemStatus) dgStatusChangeEvent;
private void delegate(ErrorStatus) dgErrorEvent;
this() {
commonInit();
}
this(char[] portName, uint baudrate, int dataBits, StopBits stopBits, Parity parity) {
portName_ = portName;
baudrate_ = baudrate;
dataBits_ = dataBits;
stopBits_ = stopBits;
parity_ = parity;
commonInit();
}
this(char[] portName, uint baudrate) {
portName_ = portName;
baudrate_ = baudrate;
commonInit();
}
~this() {
close();
}
private void commonInit() {
tcRxConduit = new ThreadConduit();
}
/*************************************************************
* Read property
* Returns:
* Current state of the serial port.
*/
public bool isOpen() {
return isOpen_;
}
/*************************************************************
* Write property
* Params:
* value = 110, 300, 600, 1200, 2400, 4800, 9600, 14400, 19200, 38400, 57600, 115200, 128000, 256000, default 9600.
* Returns:
* Current Baudrate setting.
*/
public uint baudrate(uint value) {
return baudrate_ = value;
}
/*************************************************************
* Read property
* Returns:
* Baudrate.
*/
public uint baudrate() {
return baudrate_;
}
/*************************************************************
* Write property
* Params:
* value = 5, 6, 7 or 8, default 8.
* Returns:
* Current data bits setting.
*/
public int dataBits(int value) {
return dataBits_ = value;
}
/*************************************************************
* Read property
* Returns:
* Current data bits setting.
*/
public int dataBits() {
return dataBits_;
}
/*************************************************************
* Write property
* Params:
* value = One, OnePointFive or Two, default One.
* Returns:
* Current stop bits setting.
*/
public StopBits stopBits(StopBits value) {
return stopBits_ = value;
}
/*************************************************************
* Read property
* Returns:
* Current stop bits setting.
*/
public StopBits stopBits() {
return stopBits_;
}
/*************************************************************
* Write property
* Params:
* value = None, Odd, Even, Mark or Space, default None.
* Returns:
* Current parity setting.
*/
public Parity parity(Parity value) {
return parity_ = value;
}
/*************************************************************
* Read property
* Returns:
* Current parity setting.
*/
public Parity parity() {
return parity_;
}
/*************************************************************
* Write property
* Params:
* value = port name i.e. COM1, default COM1.
* Returns:
* Current port name setting.
*/
public char[] portName(char[] value) {
return portName_ = value;
}
/*************************************************************
* Read property
* Returns:
* Current port name setting.
*/
public char[] portName() {
return portName_;
}
/*************************************************************
* Write property
* Params:
* value = None, Hardware or Software, default None.
* Returns:
* Current handshake setting.
*/
public Handshake handshake(Handshake value) {
return handshake_ = value;
}
/*************************************************************
* Read property
* Returns:
* Current handshake setting.
*/
public Handshake handshake() {
return handshake_;
}
/*************************************************************
* Write property
* Params:
* value = XON char.
* Returns:
* Current XON char.
*/
public char xonChar(char value) {
return xonChar_ = value;
}
/*************************************************************
* Read property
* Returns:
* Current XON char.
*/
public char xonChar() {
return xonChar_;
}
/*************************************************************
* Write property
* Params:
* value = XOFF char.
* Returns:
* Current XOFF char.
*/
public char xoffChar(char value) {
return xoffChar_ = value;
}
/*************************************************************
* Read property
* Returns:
* Current XOFF char.
*/
public char xoffChar() {
return xoffChar_;
}
/*************************************************************
* Write property
* Params:
* value = size of RX queue.
* Returns:
* Current size of RX queue.
*/
public uint rxQueue(uint value) {
return rxQueue_ = value;
}
/*************************************************************
* Read property
* Returns:
* Current size of RX queue.
*/
public uint rxQueue() {
return rxQueue_;
}
/*************************************************************
* Write property
* Params:
* value = size of TX queue.
* Returns:
* Current size of TX queue.
*/
public uint txQueue(uint value) {
return txQueue_ = value;
}
/*************************************************************
* Read property
* Returns:
* Current size of TX queue.
*/
public uint txQueue() {
return txQueue_;
}
/*************************************************************
* Write property
* Params:
* value = required RTS state.
* Returns:
* Current RTS state.
*/
public bool rts(bool value) {
if (rtsState_ > SigState.High) {
return false;
}
if (value) {
if (EscapeCommFunction(hComm, SETRTS)) {
rtsState_ = SigState.High;
} else {
throw new CommException("EscapeCommFunction [RTS]");
}
} else {
if (EscapeCommFunction(hComm, CLRRTS)) {
rtsState_ = SigState.Low;
} else {
throw new CommException("EscapeCommFunction [RTS]");
}
}
return cast(bool)(rtsState_ == SigState.High);
}
/*************************************************************
* Read property
* Returns:
* Current RTS state.
*/
public bool rts() {
return cast(bool)(rtsState_ == SigState.High);
}
/*************************************************************
* Write property
* Params:
* value = required DTR state.
* Returns:
* Current DTR state.
*/
public bool dtr(bool value) {
if (dtrState_ > SigState.High) {
return false;
}
if (value) {
if (EscapeCommFunction(hComm, SETDTR)) {
dtrState_ = SigState.High;
} else {
throw new CommException("EscapeCommFunction [DTR]");
}
} else {
if (EscapeCommFunction(hComm, CLRDTR)) {
dtrState_ = SigState.Low;
} else {
throw new CommException("EscapeCommFunction [DTR]");
}
}
return cast(bool)(dtrState_ == SigState.High);
}
/*************************************************************
* Read property
* Returns:
* Current DTR state.
*/
public bool dtr() {
return cast(bool)(dtrState_ == SigState.High);
}
/*************************************************************
* Write property
* Params:
* value = required BREAK state.
* Returns:
* Current BREAK state.
*/
public bool brk(bool value) {
if (breakState_ > SigState.High) {
return false;
}
if (value) {
if (EscapeCommFunction(hComm, SETBREAK)) {
breakState_ = SigState.High;
} else {
throw new CommException("EscapeCommFunction [BREAK]");
}
} else {
if (EscapeCommFunction(hComm, CLRBREAK)) {
breakState_ = SigState.Low;
} else {
throw new CommException("EscapeCommFunction [BREAK]");
}
}
return cast(bool)(breakState_ == SigState.High);
}
/*************************************************************
* Read property
* Returns:
* Current BREAK state.
*/
public bool brk() {
return cast(bool)(breakState_ == SigState.High);
}
/*************************************************************
* Write property
* Params:
* value = size of readLine timeout in ms.
* Returns:
* Current size of readLine timeout in ms.
*/
public uint rxTimeout(uint value) {
return rxTimeout_ = value;
}
/*************************************************************
* Read property
* Returns:
* Current size of readLine timeout in ms.
*/
public uint rxTimeout() {
return rxTimeout_;
}
/*************************************************************
* Write property
* Params:
* value = size of WriteTotalTimeoutMultiplier in ms.
* Returns:
* Current size of WriteTotalTimeoutMultiplier in ms.
*/
public uint txTimeoutMult(uint value) {
return txTimeoutMult_ = value;
}
/*************************************************************
* Read property
* Returns:
* Current size of WriteTotalTimeoutMultiplier in ms.
*/
public uint txTimeoutMult() {
return txTimeoutMult_;
}
/*************************************************************
* Write property
* Params:
* value = size of WriteTotalTimeoutConstant in ms.
* Returns:
* Current size of WriteTotalTimeoutConstant in ms.
*/
public uint txTimeoutConst(uint value) {
return txTimeoutConst_ = value;
}
/*************************************************************
* Read property
* Returns:
* Current size of WriteTotalTimeoutConstant in ms.
*/
public uint txTimeoutConst() {
return txTimeoutConst_;
}
/*************************************************************
* Write property
* Params:
* value = minimum number of bytes in input buffer before XON char is sent.
* Returns:
* Current size of XON low level.
*/
public short xonLowLevel(short value) {
return xonLowLevel_ = value;
}
/*************************************************************
* Read property
* Returns:
* Current size of XON low level.
*/
public short xonLowLevel() {
return xonLowLevel_;
}
/*************************************************************
* Write property
* Params:
* value = maximum number of bytes in input buffer before XOFF char is sent.
* Returns:
* Current size of XOFF high level.
*/
public short xoffHighLevel(short value) {
return xoffHighLevel_ = value;
}
/*************************************************************
* Read property
* Returns:
* Current size of XOFF high level.
*/
public short xoffHighLevel() {
return xoffHighLevel_;
}
/*************************************************************
* Write property
* Params:
* value = true or false, default false.
* Returns:
* Current discard null setting.
*/
public bool discardNull(bool value) {
return discardNull_ = value;
}
/*************************************************************
* Read property
* Returns:
* Current discard null setting.
*/
public bool discardNull() {
return discardNull_;
}
/*************************************************************
* Write property
* Params:
* value = required error char, default NULL.
* Returns:
* Current error char.
*/
public char errorChar(char value) {
return errorChar_ = value;
}
/*************************************************************
* Read property
* Returns:
* Current error char.
*/
public char errorChar() {
return errorChar_;
}
/*************************************************************
* Read property
* Returns:
* number of bytes in receive buffer.
*/
public uint bytesToRead() {
return tcRxConduit.remaining;
}
/*************************************************************
* Read property
* Returns:
* modem status.
*/
public ModemStatus modemStatus() {
if (isOpen_) {
uint scStatus = 0;
if (!GetCommModemStatus(hComm, &scStatus)) {
throw new CommException("GetCommModemStatus");
}
return new ModemStatus(scStatus);
} else {
throw new CommException(PORT_NOT_OPEN_EXCEPTION, false);
}
}
/*************************************************************
* Method: opens the serial port.
* Returns:
* none.
*/
public void open() {
if (hComm !is null) {
throw new CommException("Port " ~ portName_ ~ " already open");
}
if ((hComm = CreateFileA(toStringz(portName_),
GENERIC_READ | GENERIC_WRITE,
0,
null,
OPEN_EXISTING,
FILE_FLAG_OVERLAPPED,
null)) == INVALID_HANDLE_VALUE) {
// try with alternate naming
if ((hComm = CreateFileA(toStringz(ALT_NAME_PREFIX ~ portName_),
GENERIC_READ | GENERIC_WRITE,
0,
null,
OPEN_EXISTING,
FILE_FLAG_OVERLAPPED,
null)) == INVALID_HANDLE_VALUE) {
throw new CommException("Failed to open " ~ portName_);
}
}
if (GetFileType(hComm) != FILE_TYPE_CHAR) {
throw new CommException(portName_ ~ " file handle is not valid");
}
DCB dcb = {0};
dcb.DCBlength = dcb.sizeof;
if (GetCommState(hComm, &dcb) == 0) {
throw new CommException("GetCommState");
}
dcb.BaudRate = baudrate_;
dcb.ByteSize = dataBits_;
dcb.Parity = parity_;
dcb.StopBits = stopBits_;
dcb.XonChar = xonChar_;
dcb.XoffChar = xoffChar_;
// flag settings
dcb.flag0 = bm_DCB_fBinary; // must always be set to 1
// handshake flags
switch (handshake_) {
case Handshake.None:
// dcb.fOutxCtsFlow = dcb.fOutxDsrFlow = dcb.fOutX = dcb.fInX = false;
dcb.flag0 |= (RTS_CONTROL_DISABLE << bp_DCB_fRtsControl);
dcb.flag0 |= (DTR_CONTROL_DISABLE << bp_DCB_fDtrControl);
break;
case Handshake.Hardware:
dcb.flag0 |= (1 << bp_DCB_fOutxCtsFlow);
dcb.flag0 |= (1 << bp_DCB_fOutxDsrFlow);
// dcb.fOutX = dcb.fInX = false;
dcb.flag0 |= (RTS_CONTROL_HANDSHAKE << bp_DCB_fRtsControl);
dcb.flag0 |= (DTR_CONTROL_HANDSHAKE << bp_DCB_fDtrControl);
rtsState_ = SigState.High;
dtrState_ = SigState.High;
break;
case Handshake.Software:
// dcb.fOutxCtsFlow = dcb.fOutxDsrFlow = false;
dcb.flag0 |= (1 << bp_DCB_fOutX);
dcb.flag0 |= (1 << bp_DCB_fInX);
dcb.flag0 |= (RTS_CONTROL_DISABLE << bp_DCB_fRtsControl);
dcb.flag0 |= (DTR_CONTROL_DISABLE << bp_DCB_fDtrControl);
break;
default:
throw new CommException("Handshake");
break;
}
if ((xonLowLevel == 0) || (xoffHighLevel == 0)) {
COMMPROP cp;
if (!GetCommProperties(hComm, &cp)) {
throw new CommException("GetCommProperties");
}
const int XON_XOFF_LIMITS_PERCENT = 10;
const short XON_XOFF_DEFAULT_LIMITS = 8;
if (cp.dwCurrentRxQueue > 0) {
dcb.XoffLim = dcb.XonLim = cast(short)(cp.dwCurrentRxQueue / XON_XOFF_LIMITS_PERCENT);
} else {
dcb.XoffLim = dcb.XonLim = XON_XOFF_DEFAULT_LIMITS;
}
} else {
dcb.XoffLim = xoffHighLevel;
dcb.XonLim = xonLowLevel;
}
if (discardNull_) {
dcb.flag0 |= (1 << bp_DCB_fNull);
}
// set the parity flag if parity-checking set
if ((parity_ == Parity.Odd) ||
(parity_ == Parity.Even)) {
dcb.flag0 |= (1 << bp_DCB_fParity);
}
dcb.ErrorChar = errorChar_;
if (SetCommState(hComm, &dcb) == 0) {
throw new CommException("SetCommState");
}
if ((rxQueue != 0) || (txQueue != 0)) {
if (!SetupComm(hComm, rxQueue, txQueue)) {
throw new CommException("SetupComm");
}
}
COMMTIMEOUTS commTimeouts;
commTimeouts.ReadIntervalTimeout = INFINITE_TIMEOUT;
commTimeouts.ReadTotalTimeoutMultiplier = 0;
commTimeouts.ReadTotalTimeoutConstant = 0;
commTimeouts.WriteTotalTimeoutMultiplier = txTimeoutMult_;
commTimeouts.WriteTotalTimeoutConstant = txTimeoutConst_;
if (SetCommTimeouts(hComm, &commTimeouts) == 0) {
throw new CommException("SetCommTimeouts");
}
hRxThreadStarted = CreateEventA(null, false, false, null);
hRxThreadDone = CreateEventA(null, false, false, null);
hReadLine = CreateEventA(null, true, false, null);
receiveThread = new Thread(&receiveHandler);
receiveThread.start();
uint waitForThreadStart = WaitForSingleObject(hRxThreadStarted, INFINITE);
CloseHandle(hRxThreadStarted);
hRxThreadStarted = INVALID_HANDLE_VALUE;
isOpen_ = true;
}
/*************************************************************
* Method: closes the serial port.
* Returns:
* none.
*/
public void close() {
if (isOpen_) {
flush();
tcRxConduit.close();
SetEvent(hRxThreadDone);
receiveThread.join();
CloseHandle(hComm);
hComm = INVALID_HANDLE_VALUE;
}
}
/*************************************************************
* Delegate: called when data is available.
* Returns:
* none.
*/
public void onData(void delegate(uint, char[]) dg) {
dgDataEvent = dg;
}
/*************************************************************
* Delegate: called when transmit buffer is empty.
* Returns:
* none.
*/
public void onTxEmpty(void delegate() dg) {
dgTxEmptyEvent = dg;
}
/*************************************************************
* Delegate: called when a break is detected.
* Returns:
* none.
*/
public void onBreak(void delegate() dg) {
dgBreakEvent = dg;
}
/*************************************************************
* Delegate: called when line status change is detected.
* Returns:
* none.
*/
public void onStatusChange(void delegate(ModemStatus, ModemStatus) dg) {
dgStatusChangeEvent = dg;
}
/*************************************************************
* Delegate: called when error event is detected.
* Returns:
* none.
*/
public void onError(void delegate(ErrorStatus) dg) {
dgErrorEvent = dg;
}
/*************************************************************
* Method: gets a char from the receive buffer.
* Returns:
* int - char if available or -1 if buffer empty.
*/
public int readChar() {
char[1] ch;
if (tcRxConduit.remaining > 0) {
tcRxConduit.input.read(ch);
} else {
// return -1 if end of stream
return -1;
}
return cast(int)ch[0];
}
/*************************************************************
* Method: gets a byte from the receive buffer.
* Returns:
* int - byte if available or -1 if buffer empty.
*/
public int readByte() {
return readChar();
}
/*************************************************************
* Method: gets all chars from the receive buffer.
* Returns:
* char string if available or empty string if not.
*/
public char[] readExisting() {
if (tcRxConduit.remaining > 0) {
char[] str = new char[tcRxConduit.remaining];
tcRxConduit.input.read(str);
return str;
} else {
return "";
}
}
/*************************************************************
* Method: gets a string terminated with a newline from the receive buffer.
* Returns:
* int - char string if available or CommTimeoutException if timedout.
*/
public char[] readLine() {
char[] str;
char[1] ch;
uint n = 0;
str.length = 256;
for (;;) {
switch (WaitForSingleObject(hReadLine, rxTimeout_)) {
ResetEvent(hReadLine);
case WAIT_OBJECT_0:
tcRxConduit.input.read(ch);
switch (ch[0]) {
case '\r':
str.length = n;
return str;
break;
case '\n':
break;
default:
if (n == str.length) {
str.length = str.length + 64;
}
str[n++] = ch[0];
break;
}
break;
case WAIT_TIMEOUT:
throw new CommTimeoutException("Comms Timeout");
break;
case WAIT_FAILED:
throw new CommException("Comms ReadLine Timeout");
break;
}
}
}
/*************************************************************
* Method: transmits a char ch immediately - ignores the transmit buffer.
* Returns:
* none.
*/
public void writeImmediate(char ch) {
if (isOpen_) {
if (!TransmitCommChar(hComm, ch)) {
throw new CommException("TransmitCommChar");
}
} else {
throw new CommException(PORT_NOT_OPEN_EXCEPTION, false);
}
}
/*************************************************************
* Method: transmits the string str.
* Returns:
* none.
*/
public void write(char[] str) {
writeStr(str);
}
/*************************************************************
* Method: transmits the string str appended with a CR/LF.
* Returns:
* none.
*/
public void writeln(char[] str) {
writeStr(str ~ "\r\n");
}
/*************************************************************
* Method: transmits the char ch.
* Returns:
* none.
*/
public void write(char ch) {
char[1] str;
str[0] = ch;
writeStr(str);
}
private void writeStr(char[] str) {
if (isOpen_) {
uint numBytesWritten = 0;
OVERLAPPED ovTransmit = {0};
ovTransmit.hEvent = CreateEventA(null, true, false, null);
if (!WriteFile(hComm, cast(void*)str, str.length, &numBytesWritten, &ovTransmit)) {
if (SysError.lastCode() == ERROR_IO_PENDING) {
switch(WaitForSingleObject(ovTransmit.hEvent, INFINITE_TIMEOUT)) {
case WAIT_OBJECT_0:
if (GetOverlappedResult(hComm, &ovTransmit, &numBytesWritten, false) == 0) {
throw new CommException("GetOverlappedResult");
} else {
if (str.length != numBytesWritten) {
throw new CommException("Write Failed", false);
}
}
break;
case WAIT_FAILED:
throw new CommException("WaitforSingleObject");
break;
}
} else {
throw new CommException("WriteFile");
}
}
CloseHandle(ovTransmit.hEvent);
} else {
throw new CommException(PORT_NOT_OPEN_EXCEPTION, false);
}
}
/*************************************************************
* Method: flushes the transmit buffer.
* Returns:
* none.
*/
public void flush() {
if (isOpen_) {
if (FlushFileBuffers(hComm) == 0) {
throw new CommException("FlushFileBuffers");
}
} else {
throw new CommException(PORT_NOT_OPEN_EXCEPTION, false);
}
}
private void receiveHandler() {
bool receiveThreadActive = true;
bool firstTime = true;
uint evMask = 0;
uint nBytesRead;
byte[] localRxBuffer = new byte[RX_CHAR_COUNT];
HANDLE[WaitFor.MaxSize] hWaitfor;
OVERLAPPED ovReceive;
StdC.memset(&ovReceive, 0x00, ovReceive.sizeof);
ovReceive.hEvent = CreateEventA(null, true, false, null);
hWaitfor[WaitFor.ThreadTerminate] = hRxThreadDone;
try {
while (receiveThreadActive) {
if (!SetCommMask(hComm, EV_RXCHAR | EV_TXEMPTY | EV_ERR | EV_BREAK | EV_CTS | EV_DSR | EV_RLSD | EV_RING)) {
throw new CommException("SetCommMask");
}
if (firstTime) {
SetEvent(hRxThreadStarted);
firstTime = false;
}
evMask = 0;
if (!WaitCommEvent(hComm, &evMask, &ovReceive)) {
if (SysError.lastCode() != ERROR_IO_PENDING) {
throw new CommException("WaitCommEvent");
}
}
hWaitfor[WaitFor.Event] = ovReceive.hEvent;
switch (WaitForMultipleObjects(WaitFor.MaxSize, cast(HANDLE*)hWaitfor, false, INFINITE)) {
case WAIT_OBJECT_0 + WaitFor.ThreadTerminate:
receiveThreadActive = false;
isOpen_ = false;
break;
case WAIT_OBJECT_0 + WaitFor.Event:
if ((evMask & EV_ERR) == EV_ERR) {
uint errors = 0;
if (ClearCommError(hComm, &errors, null)) {
bool isError = false;
if (((errors & CE_FRAME) == CE_FRAME) ||
((errors & CE_IOE) == CE_IOE) ||
((errors & CE_OVERRUN) == CE_OVERRUN) ||
((errors & CE_RXOVER) == CE_RXOVER) ||
((errors & CE_RXPARITY) == CE_RXPARITY) ||
((errors & CE_TXFULL) == CE_TXFULL)) {
isError = true;
}
if (isError) {
if (dgErrorEvent !is null) {
dgErrorEvent(new ErrorStatus(errors));
}
} else {
if (errors == CE_BREAK) {
evMask |= EV_BREAK;
} else {
throw new CommException("Break Error", false);
}
}
} else {
throw new CommException("ClearCommError");
}
}
if ((evMask & EV_RXCHAR) == EV_RXCHAR) {
do {
if (!ReadFile(hComm, cast(void*)localRxBuffer, RX_CHAR_COUNT, &nBytesRead, &ovReceive)) {
throw new CommException("ReadFile");
}
if (nBytesRead == RX_CHAR_COUNT) {
tcRxConduit.output.write(localRxBuffer);
}
} while(nBytesRead > 0);
if (dgDataEvent !is null) {
uint len = tcRxConduit.remaining;
char[] str = new char[len];
tcRxConduit.input.read(str);
dgDataEvent(len, str);
}
SetEvent(hReadLine);
}
if ((evMask & EV_TXEMPTY) == EV_TXEMPTY) {
if (dgTxEmptyEvent !is null) {
dgTxEmptyEvent();
}
}
if ((evMask & EV_BREAK) == EV_BREAK) {
if (dgBreakEvent !is null) {
dgBreakEvent();
}
}
if (dgStatusChangeEvent !is null) {
uint scMask = 0;
if ((evMask & EV_CTS) == EV_CTS) { scMask |= MS_CTS_ON; }
if ((evMask & EV_DSR) == EV_DSR) { scMask |= MS_DSR_ON; }
if ((evMask & EV_RLSD) == EV_RLSD) { scMask |= MS_RLSD_ON; }
if ((evMask & EV_RING) == EV_RING) { scMask |= MS_RING_ON; }
if (scMask != 0) {
uint scStatus = 0;
if (!GetCommModemStatus(hComm, &scStatus)) {
throw new CommException("GetCommModemStatus");
}
dgStatusChangeEvent(new ModemStatus(scMask), new ModemStatus(scStatus));
}
}
break;
case WAIT_FAILED:
throw new CommException("WaitForMultipleObjects");
break;
}
ResetEvent(ovReceive.hEvent);
}
}
catch (CommException e) {
Stdout.format("Comms Exception: {0}", e.toString);
}
}
/*
* Exceptions
*/
private class CommException : Exception {
this(char[] msg, bool showSysErr = true) {
if (showSysErr) {
msg ~= " - " ~ SysError.lastMsg();
}
super(msg);
}
}
private class CommTimeoutException : Exception {
this(char[] msg) {
super(msg);
}
}
}
public class ModemStatus {
private uint status;
this(uint s) {
status = s;
}
public bool cts() {
return cast(bool)((status & MS_CTS_ON) == MS_CTS_ON);
}
public bool dsr() {
return cast(bool)((status & MS_DSR_ON) == MS_DSR_ON);
}
public bool rlsd() {
return cast(bool)((status & MS_RLSD_ON) == MS_RLSD_ON);
}
public bool ri() {
return cast(bool)((status & MS_RING_ON) == MS_RING_ON);
}
}
public class ErrorStatus {
private uint error;
this(uint err) {
error = err;
}
public bool frame() {
return cast(bool)((error & CE_FRAME) == CE_FRAME);
}
public bool io() {
return cast(bool)((error & CE_IOE) == CE_IOE);
}
public bool overrun() {
return cast(bool)((error & CE_OVERRUN) == CE_OVERRUN);
}
public bool rxOverflow() {
return cast(bool)((error & CE_RXOVER) == CE_RXOVER);
}
public bool rxParity() {
return cast(bool)((error & CE_RXPARITY) == CE_RXPARITY);
}
public bool txFull() {
return cast(bool)((error & CE_TXFULL) == CE_TXFULL);
}
}
debug (COMMS) {
private import tango.io.Console,
tango.io.stream.TextFileStream;
int main() {
Comms comms = new Comms("COM1", 38400, 8, Comms.StopBits.One, Comms.Parity.None);
void textIn(uint count, char[] str) {
/*
char ch;
while (comms.bytesToRead > 0) {
switch (ch = cast(char)comms.readChar()) {
case '\r':
Stdout.newline;
break;
default:
Stdout.format("{0}", ch).flush;
break;
}
}
*/
for (uint n = 0; n < count; ++n) {
switch (str[n]) {
case '\r':
Stdout.newline;
break;
default:
Stdout.format("{0}", str[n]).flush;
break;
}
}
}
void txEmptyDetected() {
Stdout.formatln("TX EMPTY");
}
void breakDetected() {
Stdout.formatln("BREAK");
}
void statusChange(ModemStatus mask, ModemStatus status) {
Stdout.formatln("CTS : {0}, {1}", mask.cts, status.cts);
Stdout.formatln("DSR : {0}, {1}", mask.dsr, status.dsr);
Stdout.formatln("RLSD : {0}, {1}", mask.rlsd, status.rlsd);
Stdout.formatln("RI : {0}, {1}", mask.ri, status.ri);
Stdout.newline;
}
void errorsDetected(ErrorStatus es) {
Stdout.formatln("Frame : {0}", es.frame);
Stdout.formatln("IO : {0}", es.io);
Stdout.formatln("Overrun : {0}", es.overrun);
Stdout.formatln("RX Overflow : {0}", es.rxOverflow);
Stdout.formatln("RX Parity : {0}", es.rxParity);
Stdout.formatln("TX Full : {0}", es.txFull);
Stdout.newline;
}
comms.handshake = Comms.Handshake.None;
comms.onData = &textIn;
//comms.onTxEmpty = &txEmptyDetected;
comms.onBreak = &breakDetected;
comms.onStatusChange = &statusChange;
comms.onError = &errorsDetected;
comms.rxTimeout = 1000;
comms.open();
Stdout.formatln("Modem Status : {0}", comms.modemStatus.cts);
char[] str = new char[10];
comms.writeln("Hello World");
comms.writeln("the quick brown fox jumped over the lazy dog");
comms.writeImmediate('!');
//auto input = new TextFileInput("comms.d");
//foreach (line; input) {
// comms.writeln(line);
//}
//input.close;
/*
while (1) {
if (comms.bytesToRead > 0) {
try {
Stdout.formatln("{0}", comms.readLine());
}
catch (Comms.CommTimeoutException e) {
Stdout.formatln("@@");
}
}
}
*/
while ((str = Cin.get) != "q") {
comms.write(str);
}
comms.close();
return 0;
}
}
==============================================
regards,
-=mike=-
More information about the Digitalmars-d-learn
mailing list