Listing 2: Implementation of supporting classes
#include "misc.h" // CLASS HEADERS AND MISCELLANEOUS INCLUDES
#pragma hdrstop // STOP PRECOMPILED HEADERS
#include "sdi12.h"
//-----------------------------------------------------------------------
//-----------------------------------------------------------------------
// class SDI12_UART
// Descends from PolledSerialPort class.
// copyright David Perelman-Hall 1995
//-----------------------------------------------------------------------
//-----------------------------------------------------------------------
void SDI12_UART::Setup( const String& portStr )
{
// SET UP THE SDI-12 COMMUNICATIONS PARAMETERS
SetPort( portStr ); // port is variable between COM1 and COM4
SetBaud( "1200" );
SetData( "7" );
SetParity( "even" );
SetStop( "1" );
// CALL PolledSerialPort::OpenPort TO PHYSICALLY SET UP THE UART REGISTERS
OpenPort();
}
void SDI12_UART::SendCommand( const String& command )
const
{
timer.Pause18thSeconds(1); // THIS ENSURES THAT BREAK STARTS OK
BreakOn(); // SEND BREAK AND MARK TO WAKE UP INSTRUMENT
timer.Pause18thSeconds(1);
BreakOff(); // STOP BREAK AND MARK
timer.Pause18thSeconds(1);
SendString( command ); // SEND COMMAND STRING
// CHECK FOR 1 18TH OF SECOND THAT CHARACTER IS COMING IN FROM SENSOR
// IF NO CHARACTER IS PRESENT, THEN RETRY THE COMMAND
long currentTime = timer.Get18thSeconds();
while( !timer.Past18thSeconds(currentTime,1) ) {
if( CharWaiting() )
return; // RETURN IMMEDIATELY TO COLLECT THE DATA FROM SENSOR
}
// REISSUE THE COMMAND AND RETURN FROM FUNCTION TO TRY TO COLLECT THE DATA
if( !CharWaiting() )
SendString( command );
}
int SDI12_UART::GetTerminatedString( String& str, int delayTime, char
terminator, int length )
const
{
char *buf = new char[length+1];
if( buf==0 )
assert( !"char allocation failed" );
register char c = terminator-1;
register int i=0;
int currentTime = timer.GetSeconds();
while( !timer.PastSeconds(currentTime,delayTime) && i<length &&
c!=terminator ) {
if( CharWaiting() ) {
buf[i] = c = ReadChar();
i++;
}
}
buf[i] = 0;
str = buf;
delete [] buf;
if( c==terminator )
return 1;
return 0;
}
//-----------------------------------------------------------------------
//-----------------------------------------------------------------------
// class SDI12_Logger
// Inherits privately from template class
// copyright David Perelman-Hall 1995
//-----------------------------------------------------------------------
//-----------------------------------------------------------------------
String SDI12_Logger::Verify()
const
{
return TalkToSDI(address+"V!");
}
String SDI12_Logger::Acknowledge()
const
{
return TalkToSDI( address + "!");
}
String SDI12_Logger::Identify()
{
return identity = TalkToSDI( address + "I!");
}
String SDI12_Logger::Data( const String& consecutive )
{
return value = TalkToSDI( address + "D" + consecutive + "!" );
}
String SDI12_Logger::Measure( const String& consecutive )
const
{
return TalkToSDI( address + "M" + consecutive + "!" );
}
SDI12_Logger::SDI12_REPLY SDI12_Logger::DataReady(const String& str)
{
// THIS FUNCTION SEEKS A SERVICE REQUEST FROM THE SDI12 OBJECT
// A SERVICE REQUEST CONSISTS OF "address<CR><LF>"
// AND IT MUST COME BEFORE THE MAXIMUM TIME THE SENSOR WILL TAKE
// BEFORE DATA IS READY, AS REPORTED IN REPLY TO A MEAASURE COMMAND.
// PARSE STRING TO SEE HOW LONG SHOULD WAIT BEFORE TIMING OUT
if( !ParseReply( str ) )
return SDI12_Logger::REPLY_FAIL;
// SEEK THE DATA READY MESSAGE, WAITING timeOut SECONDS FOR IT
String reply;
if( method->GetTerminatedString( reply, serviceRequestTime, LF,
MAX_BUF_SIZE ) ) {
if( reply.contains(replyTerminal) )
return SDI12_Logger::REPLY_OK;
else
return SDI12_Logger::REPLY_FAIL;
}
else
return SDI12_Logger::REPLY_TIMEOUT;
}
int SDI12_Logger::ParseReply( const String& str )
{
cout << "Parsing reply of " << str << endl;
String s = str;
// PARSE OUT THE TIME TILL DATA IS AVAILABLE
// AND PARSE OUT THE NUMBER OF DATA VALUES
char time[4] = {0,0,0,0}, numVals[2] = {0,0}; // zero out these arrays
s.copy( time, 2, s.length() - 4 );
s.copy( numVals, 1, s.length() - 1 );
serviceRequestTime = atoi( time );
numberValues = atoi( numVals );
cout << "Service request within " << serviceRequestTime << " seconds.\n";
cout << numberValues << " data value(s) will be available.\n";
// DO A SANITY CHECK TO ENSURE DATA WAS OK
if( serviceRequestTime && numberValues )
return 1;
return 0;
}
// STATIC PORT MEMBER--SO ALL PORTS CAN ACCESS IT
StringList PolledSerialPort::portsInUse;
//**************************************************************************
//**************************************************************************
//************** POLLED SERIAL PORT CLASS FUNCTIONALITY ********************
//**************************************************************************
//**************************************************************************
ostream& operator << (ostream& os, const PolledSerialPort& sp)
{
os << "SERIAL PORT.....: " << sp.PortText()
<< "\t\tBAUD RATE.......: " << sp.BaudText() << endl;
if( sp.data == _DATABITS8 )
os << "DATA BITS.......: 8";
else if( sp.data == (_DATABITS7 | 8) )
os << "DATA BITS.......: 7";
else
os << "DATA BITS.......: ?";
if( sp.stop == STOPBITS1 )
os << "\t\tSTOP BITS.......: 1\n";
else if( sp.stop == STOPBITS2 )
os << "\t\tSTOP BITS.......: 2\n";
else
os << "\t\tSTOP BITS.......: ?\n";
if( sp.parity == 0 )
os << "PARITY..........: NONE\n";
else if( sp.parity == 16 )
os << "PARITY..........: EVEN\n";
else
os << "PARITY..........: UNKNOWN\n";
return os;
}
PolledSerialPort::PolledSerialPort(WORD p, unsigned b):
baudAsText("undefined"), portAsText(""),
baseAddress(p), data(1000), parity(1000), stop(1000),
baud(b), portIsOpen(0) {}
istream& operator >> (istream& is, PolledSerialPort& sp)
{
String portStr, baudStr, dataStr, parityStr, stopStr;
// GRAB THE PORT NUMBER INFORMATION
is >> portStr;
// GRAB THE BAUD RATE ASSOCIATED WITH THE BUBBLER
is >> baudStr;
// READ IN THE PARITY, STOP BIT, AND DATA BIT
is >> parityStr >> stopStr >> dataStr;
// LOAD THE PORT
sp.SetPort( portStr );
// LOAD THE BAUD RATE
sp.SetBaud( baudStr );
// LOAD THE DATA BITS
sp.SetData( dataStr );
// LOAD THE STOP BIT
sp.SetStop( stopStr );
// LOAD PARITY
sp.SetParity( parityStr );
// SET PORT SETTINGS INTO THE HARDWARE REGISTERS
sp.OpenPort();
return is;
}
void PolledSerialPort::SetBaud( const String& baudStr )
{
// LOAD THE BAUD RATE
if( baudStr=="300" ) {
baud = BAUD300;
baudAsText = "300";
}
else if( baudStr=="1200") {
baud = BAUD1200;
baudAsText = "1200";
}
else if( baudStr=="2400" ) {
baud = BAUD2400;
baudAsText = "2400";
}
else if( baudStr=="4800" ) {
baud = BAUD4800;
baudAsText = "4800";
}
else if( baudStr=="9600" ) {
baud = BAUD9600;
baudAsText = "9600";
}
else {
baud = BAUD2400; // default to 2400
baudAsText = "2400";
}
}
void PolledSerialPort::SetStop( const String& stopStr )
{
// LOAD THE STOP BIT
if( stopStr=="1" )
stop = STOPBITS1;
else if( stopStr=="2" )
stop = STOPBITS2;
else
stop = STOPBITS1; // default
}
void PolledSerialPort::SetData( const String& dataStr )
{
// LOAD DATA BITS
if( dataStr=="8" )
data = _DATABITS8;
else if( dataStr=="7" )
data = _DATABITS7 | 8; // this also enables parity
else
data = _DATABITS8; // default
}
void PolledSerialPort::SetParity( const String& parityStr )
{
// LOAD PARITY
if( parityStr=="none" )
parity=0;
else if( parityStr=="odd" )
parity=0;
else if( parityStr=="even" )
parity=16;
}
void PolledSerialPort::SetPort( const String& port )
{
// FIND OUT HOW MANY PORTS THERE ARE
int numPorts=0;
int equipment=biosequip();
// KEEP MASKING FOR MORE AND MORE PORTS
if( (equipment & BIOS_1PORTS_MASK)==BIOS_1PORTS_MASK )
numPorts=1;
if( (equipment & BIOS_2PORTS_MASK)==BIOS_2PORTS_MASK )
numPorts=2;
if( (equipment & BIOS_3PORTS_MASK)==BIOS_3PORTS_MASK )
numPorts=3;
if( (equipment & BIOS_4PORTS_MASK)==BIOS_4PORTS_MASK )
numPorts=4;
cout << "Found " << numPorts << " ports\n";
// CHANGE CASE FOR EQUALITY TEST
String portStr = port;
portStr.to_upper();
// LOAD THE PORT NUMBER
// USE COM1 PORT
if( portStr == "COM1" ) {
if( numPorts < 1 ) {
cerr << "Can't add serial I/O to non-existent COM1\n";
}
if( !portsInUse.contains("COM1") ) {
portsInUse+="COM1";
portAsText = "COM1";
}
else {
cerr << "Can't assign 2 ports to COM1\n";
}
SetAddress( (WORD) 0X3F8 );
}
// USE COM2 PORT
else if( portStr == "COM2" ) {
if( numPorts < 2 ) {
cerr << "Can't add serial I/O to non-existent COM2\n";
}
if( !portsInUse.contains("COM2") ) {
portsInUse+="COM2";
portAsText = "COM2";
}
else {
cerr << "Can't assign 2 ports to COM2\n";
}
SetAddress( (WORD) 0X2F8 );
}
// USE COM3 PORT
else if( portStr == "COM3" ) {
if( numPorts < 3 ) {
cerr << "Can't add serial I/O to non-existent COM3\n";
}
if( !portsInUse.contains("COM3") ) {
portsInUse+="COM3";
portAsText = "COM3";
}
else {
cerr << "Can't assign 2 ports to COM3\n";
}
SetAddress( (WORD) 0X3E8 );
}
// USE COM4 PORT
else if( portStr == "COM4" ) {
if( numPorts < 4 ) {
cerr << "Can't add serial I/O to non-existent COM4\n";
}
if( !portsInUse.contains("COM4") ) {
portsInUse+="COM4";
portAsText = "COM4";
}
else {
cerr << "Can't assign 2 ports to COM4\n";
}
SetAddress( (WORD) 0X2E8 );
}
else {
cerr << "COM port designation is not COM1-COM4\n";
}
}
void PolledSerialPort::ClearPort()
const
{
// READ PORT FOR 1 SECOND TO CLEAR ANYTHING IN IT OUT
int time=timer.GetSeconds();
while( !timer.PastSeconds(time,1) ) {
if( CharWaiting() )
ReadChar();
}
}
void PolledSerialPort::BreakOn()
const
{
unsigned char val=inportb(LCR());
val|=BREAK_ON;
outportb(LCR(), val);
}
void PolledSerialPort::BreakOff()
const
{
unsigned char val=inportb(LCR());
val&=BREAK_OFF;
outportb(LCR(), val);
}
void PolledSerialPort::SendString( const String& str )
const
{
for( int i=0; i<str.length(); i++ ) {
while( !ReadyToSendChar() )
{}
SendChar( str[i] );
}
}
void PolledSerialPort::SetBaudRate()
{
//cout << "Setting baud rate to " << baudAsText << endl;
// SET DLAB FLAG=ONE TO SIGNAL THAT VALUES AT DLL AND DLM RELATED TO BAUD
unsigned char oldLCR=inportb(LCR());
outportb(LCR(),oldLCR | _DLAB);
// LOAD THE DIVISOR LATCHES FOR SPEED
unsigned char hi=(baud & 0xFF00) >> 8;
unsigned char lo=baud & 0x00FF;
// SEND BAUD MESSAGE & CLEAR THE DLAB FLAG TO ZERO
outportb(DLAB(),lo);
outportb(DLBM(),hi);
outportb(LCR(),oldLCR & !_DLAB);
}
void PolledSerialPort::OpenPort()
{
assert( baudAsText != "undefined" );
assert( baseAddress != 0 );
assert( baud != 0 );
assert( data != 1000 );
assert( parity != 1000 );
assert( stop != 1000 );
// SET THE BAUD RATE
SetBaudRate();
// DISABLE INTERRUPTS
outportb( IER(),0 );
// SET DATA, PARITY AND STOP BITS
outportb( LCR(), data | parity );
// ENABLE DTR & RTS
outportb( MCR(), _DTR | _RTS );
// CLEAR ANY GARBAGE FROM RBR AND LSR
unsigned char garbage = inport(RBR());
++garbage = inport( LSR() );
// SET PORT OPEN FLAG
portIsOpen = 1;
}