--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/hti/PC_Tools/DataGateway/SRC/SerialComm.cpp Wed Oct 13 16:17:58 2010 +0300
@@ -0,0 +1,714 @@
+/*
+* Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies).
+* All rights reserved.
+* This component and the accompanying materials are made available
+* under the terms of "Eclipse Public License v1.0"
+* which accompanies this distribution, and is available
+* at the URL "http://www.eclipse.org/legal/epl-v10.html".
+*
+* Initial Contributors:
+* Nokia Corporation - initial contribution.
+*
+* Contributors:
+*
+* Description:
+* This file contains implementation of SerialComm communication channel plugin objects. These classes are SerialComm, SerialCommPlugin and
+* SerialCommIoThread classes.
+*/
+
+// INCLUDES
+#include "SerialCommPlugin.h"
+#include "util.h"
+
+#define HIGH_COM_PORT "\\\\.\\"
+const static int g_SerialMaxResendNumber = 2;
+
+//**********************************************************************************
+// Class SerialCommPlugin
+//
+// This class implements a CommChannelPlugin used in serial communication with device
+//**********************************************************************************
+
+SerialCommPlugin::SerialCommPlugin(const CommChannelPluginObserver* observer)
+ : CommChannelPlugin(observer),
+ m_TxQueue(),
+ m_RxQueue(),
+ m_PropertySerialCommTimeout(g_SerialCommDefaultTimeout),
+ m_PropertySerialMaxSendSize(0),
+ m_ProperySerialSendPause(0),
+ m_PropertySerialHwFlowControl(false)
+{
+ m_IoThread = NULL;
+}
+
+SerialCommPlugin::~SerialCommPlugin()
+{
+ Util::Debug("SerialCommPlugin::~SerialCommPlugin()");
+ if ( m_Open )
+ {
+ Close();
+ }
+ if ( m_IoThread != NULL )
+ {
+ delete m_IoThread;
+ m_IoThread = NULL;
+ }
+}
+
+/*
+ * This method initializes SerialCommPlugin and starts SerialCommPluginIoThread
+ */
+DWORD SerialCommPlugin::Init()
+{
+ Util::Debug("SerialCommPlugin::Init()");
+
+ std::string filename = SERIAL_INI_FILE_NAME;
+ //read properties from serialplugin.ini file
+ Util::ReadProperties(filename.c_str(), m_SerialCommPluginProperties);
+
+ CheckProperties(m_SerialCommPluginProperties);
+
+ CommChannelPlugin::Init();
+
+ m_IoThread = new SerialCommIoThread(
+ &m_RxQueue,
+ &m_TxQueue,
+ m_PropertySerialCommInitString,
+ m_PropertySerialCommPort,
+ m_PropertySerialCommTimeout,
+ m_PropertySerialHwFlowControl,
+ m_ProperySerialSendPause,
+ m_PropertySerialMaxSendSize);
+
+ m_IoThread->SetTimeout(m_PropertySerialCommTimeout);
+
+ m_IoThread->Start();
+
+ Event e = m_IoThread->GetOpenedEvent();
+ if ( e.Wait(m_PropertySerialCommTimeout) == WAIT_TIMEOUT )
+ {
+ Util::Debug("SerialCommPlugin::Init() SerialComm opening timed out");
+ m_Open = false;
+ return ERR_DG_COMM_OPEN_TIMEOUT;
+ }
+ Util::Debug("SerialCommPlugin::Init() SerialComm opened");
+ m_Open = true;
+ Util::Debug("SerialCommPlugin::Init() OK");
+ return NO_ERRORS;
+}
+
+/*
+ * This method initializes class member variables from values in map
+ */
+void SerialCommPlugin::CheckProperties(map<string, string>& props)
+{
+ char tmp[256];
+
+ // Init values for connection
+ m_PropertySerialCommInitString = props[SERIAL_INI_INIT_PARAM];
+ Util::CheckCommandlineParam( PARAM_SWITCH_INIT_PARAM, m_PropertySerialCommInitString );
+ if ( m_PropertySerialCommInitString.empty() )
+ {
+ m_PropertySerialCommInitString = SERIAL_DEFAULT_INIT_STRING;
+ }
+ sprintf(tmp, "Connection Init : '%s'", m_PropertySerialCommInitString.c_str());
+ string s(tmp);
+ Util::Debug(s);
+
+ // comm port
+ m_PropertySerialCommPort = props[SERIAL_INI_COMPORT_PARAM];
+ Util::CheckCommandlineParam( PARAM_SWITCH_COMPORT_PARAM, m_PropertySerialCommPort );
+ if ( m_PropertySerialCommPort.empty() )
+ {
+ m_PropertySerialCommPort = SERIAL_DEFAULT_COMPORT;
+ }
+ sprintf(tmp, "Connection port : '%s'", m_PropertySerialCommPort.c_str());
+ s = tmp;
+ Util::Debug(s);
+
+ // Serial operations timeout
+ string val = props[SERIAL_INI_COMPORT_TIMEOUT];
+ Util::CheckCommandlineParam( PARAM_SWITCH_COMPORT_TIMEOUT, val );
+ m_PropertySerialCommTimeout = 0;
+ if ( !val.empty() )
+ {
+ m_PropertySerialCommTimeout = atol(val.c_str());
+ }
+
+ if ( m_PropertySerialCommTimeout <= 0 )
+ {
+ m_PropertySerialCommTimeout = g_SerialCommDefaultTimeout;
+ }
+ sprintf(tmp, "Timeout: '%d'", m_PropertySerialCommTimeout);
+ s = tmp;
+ Util::Debug(s);
+
+ // flow control
+ m_PropertySerialHwFlowControl = false;
+ val = props[SERIAL_INI_USE_HW_FLOW_CONTROL];
+ Util::CheckCommandlineParam( PARAM_SWITCH_USE_HW_FLOW_CONTROL, val );
+ if ( !val.empty() && val == "1" )
+ {
+ m_PropertySerialHwFlowControl = true;
+ Util::Debug("Use serial HW flow control");
+ }
+ else
+ {
+ Util::Debug("Do not use serial HW flow control");
+ }
+
+ // send pause
+ val = props[SERIAL_INI_SEND_SLEEP_PAUSE];
+ Util::CheckCommandlineParam( PARAM_SWITCH_SEND_SLEEP_PAUSE, val );
+ if ( !val.empty() )
+ {
+ m_ProperySerialSendPause = atol(val.c_str());
+ }
+ if ( m_ProperySerialSendPause <= 0 )
+ {
+ m_ProperySerialSendPause = g_SerialCommDefaultSendPause;
+ }
+ sprintf(tmp, "Send pause: '%d'", m_ProperySerialSendPause);
+ s = tmp;
+ Util::Debug(s);
+
+ // send size
+ val = props[SERIAL_INI_SEND_SIZE];
+ Util::CheckCommandlineParam( PARAM_SWITCH_SEND_SIZE, val );
+ if ( !val.empty() )
+ {
+ m_PropertySerialMaxSendSize = atol(val.c_str());
+ }
+ if ( m_PropertySerialMaxSendSize <= 0 )
+ {
+ m_PropertySerialMaxSendSize = 0;
+ }
+ sprintf(tmp, "Send max size: '%d'", m_PropertySerialMaxSendSize);
+ s = tmp;
+ Util::Debug(s);
+}
+
+/*
+ * This method checks if data is available on incoming queue
+ */
+bool SerialCommPlugin::IsDataAvailable()
+{
+ return ( !m_RxQueue.empty() );
+}
+
+/*
+ * This method is used to:
+ * -push given data to outgoing queue
+ * -wait for data to become available in incoming queue
+ * -pop the first Data object from queue and store it to the Data object given as parameter
+ */
+DWORD SerialCommPlugin::SendReceive(Data* data_in, Data** data_out, long timeout)
+{
+ DWORD res;
+ if ( ( res = Send(data_in, timeout)) == NO_ERRORS &&
+ ( res = ReceiveWait(data_out, timeout)) == NO_ERRORS )
+ {
+ return NO_ERRORS;
+ }
+ cout << "SerialCommPlugin::SendReceive: error" << endl;
+ return res;
+}
+
+/*
+ * This method pushes the given Data object(of type Data::EData) to outgoing queue
+ */
+DWORD SerialCommPlugin::Send(Data* data_in, long timeout)
+{
+ Data::DataType type = data_in->GetType();
+ if ( type == Data::EData )
+ {
+ DWORD length = data_in->GetLength();
+ m_TxQueue.push(data_in);
+ return NO_ERRORS;
+ }
+ else if ( type == Data::EControl )
+ {
+ Util::Debug("SerialCommPlugin::Send: Control Message Received");
+ return NO_ERRORS;
+ }
+ return ERR_DG_COMM_DATA_SEND;
+}
+
+/*
+ * This method is used to wait for data to become available in incoming queue
+ * and then pop the first Data object from the queue and and store it to Data object given as parameter.
+ */
+DWORD SerialCommPlugin::ReceiveWait(Data** data_out, long timeout)
+{
+ long elapsed = 0;
+ while (elapsed < timeout && !IsDataAvailable())
+ {
+ elapsed += 25;
+ Sleep(25);
+ }
+ if ( elapsed >= timeout )
+ {
+ return ERR_DG_COMM_DATA_RECV_TIMEOUT;
+ }
+ return Receive(data_out, timeout);
+}
+
+/*
+ * This method is used to pop the first data object from incoming queue and store it to data object given as parameter
+ */
+DWORD SerialCommPlugin::Receive(Data** data_out, long timeout)
+{
+ if ( !m_RxQueue.empty() )
+ {
+ *data_out = m_RxQueue.front();
+ m_RxQueue.pop();
+ return NO_ERRORS;
+ }
+ return ERR_DG_COMM_DATA_RECV;
+}
+
+
+DWORD SerialCommPlugin::Open()
+{
+ return ( m_Open ? NO_ERRORS : ERR_DG_COMM_OPEN );
+}
+
+/*
+ * This method stops SerialCommIoThread and waits for it to be in signaled state
+ */
+DWORD SerialCommPlugin::Close()
+{
+ m_IoThread->Stop();
+ WaitForSingleObject( m_IoThread->ThreadHandle(), g_MaximumShutdownWaitTime );
+ return NO_ERRORS;
+}
+
+//**********************************************************************************
+// Class SerialComm
+//
+// This class implements the actual serial communication using Windows API
+//**********************************************************************************
+
+SerialComm::SerialComm()
+ : m_Open(false),
+ m_SendPause(0),
+ m_SendMaxSize(0)
+{
+
+}
+
+SerialComm::~SerialComm()
+{
+ Util::Debug("SerialComm::~SerialComm()");
+ if ( m_Open )
+ {
+ Close();
+ }
+}
+/*
+ * This method opens a communication device that uses serial communication and
+ * configures it according initialization parameters
+ */
+WORD SerialComm::Open( const string& commPort, const string& initParameters,
+ long timeout,
+ bool hwFlowControl, long sendPause, long sendMaxSize)
+{
+ Util::Debug("SerialComm::Open()");
+
+ m_SendPause = sendPause;
+
+ if ( sendMaxSize>0 )
+ {
+ m_SendMaxSize = sendMaxSize;
+ }
+
+ DWORD err;
+ //check commPort and add "\\.\" prefix if port is larger than 9
+ string localCommPort(commPort);
+ static const basic_string <char>::size_type npos = -1;
+ if ( localCommPort.find(HIGH_COM_PORT) == npos &&
+ localCommPort.find("COM") == 0 &&
+ localCommPort.length() > 4 //two digit port number
+ )
+ {
+ localCommPort.insert(0, HIGH_COM_PORT);
+ }
+ Util::Debug(localCommPort.c_str());
+
+ // If COM-port opening fails, it's retried until DataGateway is killed.
+ m_Com = INVALID_HANDLE_VALUE;
+ while ( m_Com == INVALID_HANDLE_VALUE )
+ {
+ // Open file (COM port)
+ m_Com = CreateFile( localCommPort.c_str(),
+ GENERIC_READ | GENERIC_WRITE,
+ 0, //excusive access
+ NULL, //no security
+ OPEN_EXISTING, //must use
+ 0, //not overlapped i/o
+ NULL // must be NULL for comm
+ );
+ if ( m_Com == INVALID_HANDLE_VALUE )
+ {
+ err = GetLastError();
+ Util::Error("[SerialComm] Failed to open COM port", err);
+ Util::Info("[SerialComm] Retrying...");
+ Sleep(5000);
+ }
+ }
+
+ // Serial port cfg
+ Util::Debug(initParameters.c_str());
+ DCB dcb;
+
+ FillMemory(&dcb, sizeof(dcb), 0);
+ dcb.DCBlength = sizeof(dcb);
+ if ( !BuildCommDCB(initParameters.c_str(), &dcb) )
+ {
+ err = GetLastError();
+ Util::Error("[SerialComm] Failed to build port settings ", err);
+ return ERR_DG_COM_INIT;
+ }
+
+ dcb.fBinary = TRUE ;
+
+ //set hw handshaking
+ dcb.fOutX = FALSE;
+ dcb.fInX = FALSE;
+ dcb.fNull = FALSE;
+ dcb.fAbortOnError = TRUE;
+
+ dcb.fOutxDsrFlow = FALSE;
+ dcb.fDtrControl = DTR_CONTROL_DISABLE;
+ dcb.fDsrSensitivity= FALSE;
+
+ if ( hwFlowControl )
+ {
+ dcb.fOutxCtsFlow = TRUE;
+ dcb.fRtsControl = RTS_CONTROL_HANDSHAKE;
+ }
+ else
+ {
+ dcb.fOutxCtsFlow = FALSE;
+ dcb.fRtsControl = RTS_CONTROL_ENABLE;
+ }
+ //Configure communication device according to the specifications in a device-control block (a DCB structure)
+ if ( !SetCommState(m_Com, &dcb) )
+ {
+ err = GetLastError();
+ Util::Error("[SerialComm] Failed to set port settings ", err);
+ return ERR_DG_COM_INIT;
+ }
+
+ COMMTIMEOUTS timeouts;
+
+ timeouts.ReadIntervalTimeout = MAXDWORD;
+
+ timeouts.ReadTotalTimeoutMultiplier = timeout;
+ timeouts.ReadTotalTimeoutConstant = 1;
+ timeouts.WriteTotalTimeoutMultiplier = 1;
+ timeouts.WriteTotalTimeoutConstant = timeout;
+
+ //Set the time-out parameters for all read and write operations on communications device
+ if ( !SetCommTimeouts(m_Com, &timeouts) )
+ {
+ // Error setting time-outs.
+ err = GetLastError();
+ Util::Error("[SerialComm] Failed to set port timeouts ", err);
+ return ERR_DG_COM_INIT;
+ }
+
+ m_Open = true;
+ Util::Debug("SerialComm::Open() OK");
+ Util::Info("[SerialComm] COM port open");
+ return NO_ERRORS;
+}
+
+/*
+ * This method is used to send Data object using serial communication
+ */
+DWORD SerialComm::Send(const Data& data_in)
+{
+ Util::Debug("SerialComm::Send()");
+
+ if ( data_in.GetLength() > 0 )
+ {
+ DWORD sentBytes = 0;
+ void* data = data_in.GetData();
+ int numOfTries = 0;
+ //loop until whole Data object is sent
+ while ( sentBytes < data_in.GetLength() )
+ {
+ //Util::Debug("while");
+ DWORD sent;
+ BOOL r;
+ if ( m_SendMaxSize>0 )
+ {
+ //Write data to the communication device
+ r = WriteFile(m_Com,
+ ((char*)data) + sentBytes,
+ //data_in.GetLength() - sentBytes,
+ min(data_in.GetLength() - sentBytes, m_SendMaxSize),
+ &sent,
+ NULL);
+ }
+ else
+ {
+ r = WriteFile(m_Com,
+ ((char*)data) + sentBytes,
+ data_in.GetLength() - sentBytes,
+ &sent,
+ NULL);
+ }
+
+ if ( r && sent > 0 )
+ {
+ if (Util::GetVerboseLevel() == Util::debug)
+ {
+ printf("Send %d bytes\r\n", sent);
+ Util::Hex(((char*)data) + sentBytes, min(sent,16));
+ }
+ sentBytes+=sent;
+ }
+ else
+ {
+ if ( !r )
+ {
+ DWORD err = GetLastError();
+ Util::Error("[SerialComm] Send error", err);
+ }
+ else
+ {
+ Util::Error("[SerialComm] Send failed");
+ }
+ if ( numOfTries++==g_SerialMaxResendNumber )
+ {
+ return ERR_DG_COMM_DATA_RECV;
+ }
+ }
+ //used to "solve" overflow problem
+ if ( m_SendPause > 0 )
+ {
+ Sleep(m_SendPause);
+ }
+ }
+ }
+
+ Util::Debug("SerialComm::Send() OK");
+ return NO_ERRORS;
+}
+
+/*
+ * This method reads all available data from communication media
+ * and returns it in the Data object given as parameter
+ */
+DWORD SerialComm::Receive(Data* data_out)
+{
+ Util::Debug("SerialComm::Receive()");
+
+ //get available bytes in incoming communication channel
+ DWORD rxBytes = GetRxBytesAvailable();
+ //printf("Rx bytes: %d \r\n", rxBytes);
+
+ if ( rxBytes > 0 )
+ {
+ char* data = new char[rxBytes];
+ DWORD readBytes = 0;
+ //read data until all available bytes have been red
+ do
+ {
+ DWORD reallyRead;
+ if ( ReadFile(m_Com,
+ data + readBytes,
+ rxBytes - readBytes,
+ &reallyRead,
+ NULL) )
+ {
+ if (Util::GetVerboseLevel() == Util::debug)
+ {
+ printf("Received %d bytes\r\n", reallyRead);
+ Util::Hex(data + readBytes, min(16,reallyRead));
+ }
+ readBytes += reallyRead;
+ }
+ else
+ {
+ delete[] data;
+ DWORD err = GetLastError();
+ Util::Error("[SerialComm] Error %d", err);
+ return ERR_DG_COMM_DATA_RECV;
+ }
+ if ( readBytes > rxBytes )
+ {
+ Util::Error("[SerialComm] !!! readBytes > rxBytes !!!");
+ }
+ }
+ while( readBytes < rxBytes );
+
+ data_out->SetData( data, rxBytes, Data::EData);
+ delete[] data;
+ }
+ else if ( rxBytes<0 ) //err
+ {
+ return ERR_DG_COMM_DATA_RECV;
+ }
+ return NO_ERRORS;
+}
+
+/*
+ * This method returns number of bytes available in incoming queue or
+ * -1 if communication error has occurred
+ */
+LONG SerialComm::GetRxBytesAvailable()
+{
+ COMSTAT res;
+ DWORD err;
+
+ if ( ClearCommError(m_Com, &err, &res ) )
+ {
+ return res.cbInQue;
+ }
+ else
+ {
+ DWORD err = GetLastError();
+ Util::Error("[SerialComm] Error getting Rx bytes %d", err);
+ return -1;
+ }
+}
+
+DWORD SerialComm::Close()
+{
+ Util::Debug("SerialComm::Close()");
+ m_Open = false;
+
+ if ( m_Com != INVALID_HANDLE_VALUE )
+ {
+ CloseHandle(m_Com);
+ m_Com = INVALID_HANDLE_VALUE;
+ }
+
+ Util::Debug("SerialComm::Close() OK");
+ return NO_ERRORS;
+}
+
+//**********************************************************************************
+// Class SerialCommIoThread
+//
+// This thread is used to send data from outgoing queue to serial port
+// and read bytes from serial port and push it to incoming queue
+//**********************************************************************************
+
+SerialCommIoThread::SerialCommIoThread(SafeQueue<Data*>* in,
+ SafeQueue<Data*>* out,
+ const string& init, const string& commPort, long timeout,
+ bool hwFlowControl, long sendPause, long sendMaxSize)
+ : m_InitString(init),
+ m_CommPort( commPort ),
+ m_PropertySerialCommTimeout( timeout ),
+ m_PropertySerialHwFlowControl( hwFlowControl ),
+ m_ProperySerialSendPause(sendPause),
+ m_PropertySerialMaxSendSize(sendMaxSize)
+{
+ m_InQueue = in;
+ m_OutQueue = out;
+ m_Running = true;
+ m_PropertySerialCommTimeout = g_SerialCommDefaultTimeout;
+ m_OpenedEvent.Reset();
+}
+
+/*
+ * Main execution loop which is used to send Data from outgoing queue to serial port
+ * and read bytes from serial port, encapsulate it to Data objects and push them to incoming queue
+ */
+void SerialCommIoThread::Run()
+{
+ while ( m_Running )
+ {
+ Util::Debug("SerialCommIoThread::Run()");
+ SerialComm m_SerialComm;
+
+ if ( m_SerialComm.Open(m_CommPort, m_InitString, m_PropertySerialCommTimeout,
+ m_PropertySerialHwFlowControl, m_ProperySerialSendPause,
+ m_PropertySerialMaxSendSize) != NO_ERRORS )
+ {
+ m_Running = false;
+ return;
+ }
+ m_OpenedEvent.Set();
+
+ Util::Debug("SerialCommIoThread::Run() start loop");
+ while ( m_Running )
+ {
+ // Sending data
+ try
+ {
+ Data* out = m_OutQueue->front(30);
+ if ( m_SerialComm.Send(*out) != NO_ERRORS )
+ {
+ Data* d = new Data("Error sending data", 19, Data::EControl);
+ m_InQueue->push(d);
+ d = NULL;
+ }
+ m_OutQueue->pop();
+ delete out;
+ out = NULL;
+ } catch (TimeoutException te) {}
+
+ // Receiving data
+ LONG rxBytes = 0;
+ while ( ( rxBytes = m_SerialComm.GetRxBytesAvailable() ) > 0 )
+ {
+ //printf("rx %d\r\n", rxBytes);
+ Data* in = new Data;
+ if ( m_SerialComm.Receive(in) == NO_ERRORS )
+ {
+ m_InQueue->push(in);
+ in = NULL;
+ }
+ else
+ {
+ delete in;
+ in = NULL;
+ }
+ }
+ if ( rxBytes < 0 )
+ {
+ Util::Info("[SerialCommIoThread] COM-port lost. Trying to close & reopen");
+ break; // break out but keep the outermost while loop running
+ }
+ }
+ m_OpenedEvent.Reset();
+ m_SerialComm.Close();
+ }
+ Util::Debug("SerialCommIoThread::Run() exiting");
+}
+
+void SerialCommIoThread::Stop()
+{
+ Util::Debug("SerialCommIoThread::Stop()");
+ m_Running = false;
+}
+
+Event SerialCommIoThread::GetOpenedEvent()
+{
+ return m_OpenedEvent;
+}
+
+void SerialCommIoThread::SetTimeout(long timeout)
+{
+ if ( timeout > 0 )
+ {
+ m_PropertySerialCommTimeout = timeout;
+ }
+ else
+ {
+ m_PropertySerialCommTimeout = g_SerialCommDefaultTimeout;
+ }
+}
+
+long SerialCommIoThread::GetTimeout() const
+{
+ return m_PropertySerialCommTimeout;
+}
+
+// End of the file
\ No newline at end of file