Komunikace pomocí sériového portu 2.díl (MFC a C++)

V minulém díle jsme si ukázali některé členské funkce naší třídy CComPort. Dnes tuto třídu rozšíříme o funkce pro zápis a čtení na (z) port(u).

Nejprve si ukážeme, jak vypadá konstruktor a destruktor třídy CComPort.

//Konstruktor
CComPort::CComPort()
{
  m_ComHandle=NULL;
  m_conect=FALSE;
  m_Parent=NULL;
}

//Destruktor
CComPort::~CComPort()
{
  if(m_conect)
    CloseCom();
}

Jak vidíte, obě metody jsou naprosto jednoduché, v konstruktoru se nastaví výchozí stav portu a v destruktoru port uzavřeme vždy, když je otevřený.

Další dvě metody, které bude třída mít, jsou pro získání handle portu, které bude potřeba u některých funkcí a dále funkce, která zjistí, jestli je port připojen:

//Získej handle portu
BOOL CComPort::GetComHandle(HANDLE *handle)
{
  m_cs.Lock();		//Zamči kritickou sekci

  if(!m_conect)		//Je port připojen?
  {
    Chyba("Není připojeno!");
    m_cs.Unlock();
    return FALSE;
  }

  *handle=m_ComHandle;	//Zkopíruj handle
  m_cs.Unlock();		//Odemči kritickou sekci

  return TRUE;
}

//Zjistí, zda je port připojen
BOOL CComPort::IsConnected()
{
  m_cs.Lock();		//Zamčít
  BOOL con=m_conect;	//Zkopírovat stav portu
  m_cs.Unlock();	//Odemčít
  return con;
}

Čtení z portu

Pro čtení dat z portu slouží API funkce ReadFile. Pokud tato funkce zpracovává soubory, tak se chová naprosto očekávaně. Pokud však funkci použijeme na sériový port, který byl navíc otevřen s příznakem OVERLAPPED, tak se chování funkce změní. Jde o to, že funkce nečeká, až se data na port zapíší (nebo přečtou), ale místo toho vrátí jakoby chybu typu ERROR_IO_PENDING, což znamená, že všechna data ještě nebyla na port zapsána (načtena) a že tedy musíme čekat, až se zapíší. Možná se vám tento postup zdá zbytečně složitý, ale je to tak proto, abychom mohli z portu zároveň číst a zároveň na něj zapisovat data. A jak tedy zjistíme, že všechna data už byla zapsána? Ke zjištění použijeme funkci GetOverlappedResult, která určí, v jakém stavu je právě probíhající operace. Zdrojový kód tedy vypadá následovně:

BOOL CComPort::ReadData(char *buffer, int size)
{
  m_cs.Lock();
  if(!m_conect)			//Zkontroluj připojení
  {
    Chyba("Není připojeno!");
    m_cs.Unlock();
    return FALSE;
  }
  m_cs.Unlock();

  DWORD readen;
  ReadFile(m_ComHandle,buffer,size,&readen,&m_OLRead);

  if(GetLastError()==ERROR_IO_PENDING)
  {
    while(!GetOverlappedResult(m_ComHandle,&m_OLRead,&readen,TRUE))
    {
      if(GetLastError()==ERROR_IO_INCOMPLETE)
        continue;
    }
  }

  return TRUE;
}

A nyní něco k popisu. Na začátku se testuje, zda je port připojen a pokud ne, tak se funkce ukončí. Potom je proveden samotný zápis na port a zkontrolování chybového kódu. Pokud je vrácená chyba rovna hodnotě ERROR_IO_PENDING, tak se začne kontrolovat stav operace a dokud je stav ERROR_IO_INCOMPLETE (ještě není hotovo), tak opět proběhne další smyčka cyklu. Pokud je však vrácena jiná hodnota, tak se smyčka ukončí a s ní i celá funkce.

Zápis na port

Na port se zapisuje naprosto stejně jako se z něj čte, pouze se místo funkce ReadFile použije WriteFile. Kód zde nebudu znova popisovat, protože funkce je opravdu naprosto shodná s čtecí.

BOOL CComPort::WriteData(char *buffer, int size)
{
  m_cs.Lock();
  if(!m_conect)
  {
    Chyba("Není připojeno!");
    m_cs.Unlock();
    return FALSE;
  }
  m_cs.Unlock();

  DWORD writen;
  WriteFile(m_ComHandle,buffer,size,&writen,&m_OLWrite);

  if(GetLastError()==ERROR_IO_PENDING)
  {
    while(!GetOverlappedResult(m_ComHandle,&m_OLWrite,&writen,TRUE))
    {
      if(GetLastError()==ERROR_IO_INCOMPLETE)
        continue;
    }
  }

  return TRUE;
}

Nyní už tedy máme celou třídu hotovou, takže si ji celou můžete stáhnout níže. V příštím díle si už konečně vytvoříme aplikaci typu chat, která bude tuto třídu používat.

Stáhnout zdrojový kód (2 kb)

UPOZORNĚNÍ: Jedná se o archiv článků z let 2003 - 2005, uvedené technologie či postupy již mohou být neaktuální.