より実践的なRTCの作成


例としてCOMポートを制御するRTCを作成します.C++版で作ります.

1. RTC Builderによるコードの生成

RTC Builderで下記のようにRTCを生成します.

タブ 変数 設定値
基本 Module name SerialPortRTC
Module description Serial Port RTC
Module version 1.0.0
Module vender あなたの名前
Module category TEST
Component type STATIC
Component Kind Data Flow
Component’s activity type PERIODIC
Number of maximum instance 1
Execution Type Periodic Execution Context
Execution Rate 100.0
Abstract 不要
RTC Type 不要
Output Project SerialPortRTC
アクティビティ onActivated チェックボックスをONに
onDeactivated チェックボックスをONに
onReset チェックボックスをONに
onExecute チェックボックスをONに
データポート

(OutPort)

PortName out
DataType TimedOctetSeq
VarName out
Disp. Position RIGHT (ポートの表示方向)
データポート

(InPort)

PortName in
DataType TimedOctetSeq
VarName in
Disp. Position RIGHT (ポートの表示方向)
コンフィグレーション 名称 debug
DataType int
VarName debug
コンフィグレーション 名称 filename
DataType std::string
VarName filename
コンフィグレーション 名称 baudrate
DataType int
VarName baudrate
言語・環境 言語 C++

ひとまずファイル名とボーレートが変更できるようにコンフィグレーションを追加しましたが,フロー制御などが必要な場合は改造してみてください.

2. コードの編集

cmakeしてコードの編集をしますが,その前に・・・

2.1 SerialPortクラスのコードの追加

Visual C++の場合,同じフォルダ内にSerialPort.hとSerialPort.cppというテキストファイルを作成し,これらをプロジェクトに含めます.今回のSerialPortRTCとSerialPortRTCCompという二つのプロジェクトでは同じソースコードを参照しているため,SerialPort.h SerialPort.cppも同一のファイルをプロジェクトに追加します.

まず,include/SerialPortRTCフォルダを開き,右クリックのメニューからテキストファイルを新規作成し,SerialPort.hと名付けます.
同様にsrc内でSerialPort.cppも作成して下さい.

Unix系なら,srcディレクトリ内で,
$ touch SerialPort.cpp
のように空のファイルを作成できます.

同様に,include/SerialPortRTC/内で
$ touch SerialPort.h
とします.

次に,src/CMakeLists.txtを編集します.
最初の行のset(comp_src SerialPortRTC.cpp)の行に,SerialPort.cppを追加します.つまり,
set(comp_src SerilaPortRTC.cpp SerialPort.cpp)のようになります.これでCMakeで生成したプロジェクトも追加したソースコードが追加されています.

さて,SerialPort.hにはSerialPortクラスの宣言を,SerialPort.cppにはSerialPortクラスの実装を記述します.

SerialPortクラスはLinuxからも同様の機能が使えるように#ifdef文でOS毎の機能定義をしています.
見難いかもしれませんが,こういうコードを一度作ってしまうと非常に便利です.

以下のコードは分かりやすさ重視で,ベストなコードではないです.
本来は例外を定義してエラーを追跡しやすくしますが,コード量を抑えるために下記のような記述です.

とりあえず全文載せます.

SerialPort.h

/********************************************************
 * SerialPort.h
 *
 * Portable Serial Port Class Library for Windows and Unix.
 * @author ysuga@ysuga.net
 * @date 2010/11/02
 ********************************************************/
 
#ifndef SERIAL_PORT_HEADER_INCLUDED
#define SERIAL_PORT_HEADER_INCLUDED
 
#ifdef WIN32
#include <windows.h>
#endif
 
namespace net {
	namespace ysuga {
 
		/***************************************************
		 * SerialPort
		 *
		 * @brief Portable Serial Port Class
		 ***************************************************/
		class SerialPort
		{
		private:
#ifdef WIN32
 
			HANDLE m_hComm;
#else
 
			/**
			 * @brief file descriptor
			 */
			int m_Fd;
#endif
 
	public:
			int IsValid() {
#ifdef WIN32
				if(m_hComm == 0) {
					return 0;
				} else {
					return 1;
				}
#else
				if(m_Fd < 0) {
					return 0;
				} else {
					return 1;
				}
#endif
			}
 
		public:
			/**
			 * @brief Constructor
			 * 
			 * @param filename Filename of Serial Port (eg., "COM0", "/dev/tty0")
			 * @baudrate baudrate. (eg., 9600, 115200)
			 */
			SerialPort(const char* filename, int baudrate);
 
			/**
			 * @brief Destructor
			 */
			~SerialPort();
 
		public:
			/**
			 * @brief flush receive buffer.
			 * @return zero if success.
			 */
			int FlushRxBuffer();
 
			/**
			 * @brief flush transmit buffer.
			 * @return zero if success.
			 */
			int FlushTxBuffer();
 
		public:
			/**
			 * @brief Get stored datasize of in Rx Buffer
			 * @return Stored Data Size of Rx Buffer;
			 */
			int GetSizeInRxBuffer();
 
			/**
			 * @brief write data to Tx Buffer of Serial Port.
			 *
			 */
			int Write(const void* src, const unsigned int size);
 
			/**
			 * @brief read data from RxBuffer of Serial Port 
			 			 */
			int Read(void *dst, const unsigned int size);
 
		};
 
	};//namespace ysuga
};//namespace org
 
#endif
 
/*******************************************************
 * Copyright  2010, ysuga.net all rights reserved.
 *******************************************************/

SerialPort.cpp

/********************************************************
 * SerialPort.cpp
 *
 * Portable Serial Port Class Library for Windows and Unix.
 * @author ysuga@ysuga.net
 * @date 2010/11/02
 * @copyright Yuki Suga (ysuga.net), 2010 all rights reserved.
 ********************************************************/
 
/*************************************************
 * Header Including Division
 */
#ifdef WIN32
 
#else
 
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/ioctl.h>
#include <fcntl.h>
#include <termio.h>
#include <errno.h>
#include <signal.h>
#define _POSIX_SOURCE 1
 
#endif
 
#include "SerialPort.h"
 
/* Header includeing division
 ************************************************/
 
using namespace net::ysuga;
 
/******************************
 */
SerialPort::SerialPort(const char* filename, const int baudrate)
{
 
#ifdef WIN32
	DCB dcb;
	m_hComm = 0;
	m_hComm = CreateFileA(filename,	GENERIC_READ | GENERIC_WRITE,
		0, NULL, OPEN_EXISTING,	0, NULL );
	if(m_hComm == INVALID_HANDLE_VALUE) {
		m_hComm = 0;
	}
 
	if(!GetCommState (m_hComm, &dcb)) {
		CloseHandle(m_hComm); 
		m_hComm = 0;
	}
 
	dcb.BaudRate           = baudrate;
    dcb.fParity            = 0;
    dcb.fOutxCtsFlow       = 0;
    dcb.fOutxDsrFlow       = 0;
    dcb.fDtrControl        = RTS_CONTROL_DISABLE;
    dcb.fDsrSensitivity    = 0;
    dcb.fTXContinueOnXoff  = 0;
    dcb.fErrorChar         = 0;
    dcb.fNull              = 0;
    dcb.fRtsControl        = RTS_CONTROL_DISABLE;
    dcb.fAbortOnError      = 0;
    dcb.ByteSize           = 8;
    dcb.Parity             = NOPARITY;
    dcb.StopBits           = ONESTOPBIT;
 
    if (!SetCommState (m_hComm, &dcb)) {
      CloseHandle(m_hComm); 
	  m_hComm = 0;
    }
 
#else
    if((m_Fd = open(filename, O_RDWR /*| O_NOCTTY |O_NONBLOCK*/)) < 0) {
		m_Fd = -1;
    }
    struct termios tio;
	memset(&tio, 0, sizeof(tio));
	cfsetspeed(&tio, baudrate);
	tio.c_cflag |= CS8 | CLOCAL | CREAD;
	tcsetattr(m_Fd, TCSANOW, &tio);
#endif
}
 
/******************************
 */
SerialPort::~SerialPort()
{
#ifdef WIN32
	if(m_hComm) {
		CloseHandle(m_hComm);
	}
#else
	if(m_Fd >= 0) {
		close(m_Fd);
	}
#endif
}
 
/*******************************
 */
int SerialPort::FlushRxBuffer()
{
#ifdef WIN32
	if(!PurgeComm(m_hComm, PURGE_RXCLEAR)) {
		return -1;
	}
#else
	if(tcflush( m_Fd, TCIFLUSH) < 0) {
		return -1;
	}
#endif
	return 0;
}
 
/*******************************
 */
int SerialPort::FlushTxBuffer()
{
#ifdef WIN32
	if(!PurgeComm(m_hComm, PURGE_TXCLEAR)) {
		return -1;
	}
#else
	if(tcflush(m_Fd, TCOFLUSH) < 0) {
		return -1;
	}
#endif
	return 0;
}
 
/********************************/
int SerialPort::GetSizeInRxBuffer()
{
#ifdef WIN32
    COMSTAT         stat;
    DWORD           lper;
 
	if(ClearCommError (m_hComm, &lper, &stat) == 0) {
		return -1;
	}
    return stat.cbInQue;
#else
	struct timeval timeout;
	int nread;
	timeout.tv_sec = 0, timeout.tv_usec = 0;
	fd_set fds, dmy;
	FD_ZERO(&fds);
	FD_ZERO(&dmy);
	FD_SET(m_Fd, &fds);
	int res = select(FD_SETSIZE, &fds, &dmy, &dmy, &timeout);
	switch(res) {
	case 0: //timeout
		return 0;
	case -1: //Error
		return -1;
	default:
		if(FD_ISSET(m_Fd, &fds)) {
		ioctl(m_Fd, FIONREAD, &nread);
			return nread;
		}
	}
	return 0;
#endif
}
 
/*******************************
 */
int SerialPort::Write(const void* src, const unsigned int size)
{
#ifdef WIN32
	DWORD WrittenBytes;
	if(!WriteFile(m_hComm, src, size, &WrittenBytes, NULL)) {
		return -1;
	}
 
	return WrittenBytes;
#else
	int ret;
	if((ret = write(m_Fd, src, size)) < 0) {
		return -1;
	}
	return ret;
#endif
}
 
/*******************************
 */
int SerialPort::Read(void *dst, const unsigned int size)
{
#ifdef WIN32
	DWORD ReadBytes;
	if(!ReadFile(m_hComm, dst, size, &ReadBytes, NULL)) {
		return -1;
	}
 
	return ReadBytes;
#else
	int ret;
	if((ret = read(m_Fd, dst, size)) < 0) {
		return -1;
	}
	return ret;
#endif
}

2.2 RTCの実装

RTCから上記のSerialPortクラスを使えるようにします.

2.2.1 SerialPortRTC.h

まず,先頭部分で,ヘッダーファイルをインクルードします.

#include <rtm/idl/ExtendedDataTypesSkel.h>
#include <rtm/idl/InterfaceDataTypesSkel.h>
 
// Service implementation headers
// 
 
// 
 
// Service Consumer stub headers
// 
 
#include "SerialPort.h" // この行を追加
 
// 
 
using namespace RTC;

次に,SerialPortRTCクラス宣言の最後の方に,SerialPortクラスのオブジェクトに対するポインタを宣言します.

  // 
 
  // 
 
  // 
	 net::ysuga::CSerialPort *m_pSerialPort; // この行を追加
};
 
extern "C"
{

2.2.2 SerialPortRTC.cpp

ここからはコマンドハンドラです.

onActivated

RTC::ReturnCode_t SerialPortRTC::onActivated(RTC::UniqueId ec_id)
{
        // シリアルポートを開きます.有効なシリアルポートを開けなけらばRTC_ERRORを返します
	m_pSerialPort = new net::ysuga::SerialPort(m_filename.c_str(), m_baudrate);
	if(!m_pSerialPort->IsValid()) {
		delete m_pSerialPort; m_pSerialPort = NULL;
		return RTC::RTC_ERROR;
	}
  return RTC::RTC_OK;
}

onDeactivated

RTC::ReturnCode_t SerialPortRTC::onDeactivated(RTC::UniqueId ec_id)
{
        // シリアルポートを閉じます
	delete m_pSerialPort;
	m_pSerialPort = NULL;
 
	return RTC::RTC_OK;
}

onExecute

RTC::ReturnCode_t SerialPortRTC::onExecute(RTC::UniqueId ec_id)
{
        // データがシリアルポートに送られてきたなら,出力ポートからm_packet_size毎に区切って送信します.
	if(m_pSerialPort->GetSizeInRxBuffer() > m_packet_size) {
		unsigned char* buffer = new unsigned char[m_packet_size];
		if(m_pSerialPort->Read(buffer, m_packet_size) < 0) { 			return RTC::RTC_ERROR; 		} 		m_out.data.length(m_packet_size); 		memcpy(&(m_out.data[0]), buffer, m_packet_size); 		m_outOut.write(); 		delete buffer; 	}         // データが入力ポートに来ていたら,データパケットごとシリアルポートに書き込みます 	if(m_inIn.isNew()) { 		m_inIn.read(); 		unsigned char* buffer = new unsigned char[m_in.data.length()]; 		memcpy(buffer, &(m_in.data[0]), m_in.data.length()); 		if(m_pSerialPort->Write(buffer, m_in.data.length()) < 0) {
			return RTC::RTC_ERROR;
		}
		delete buffer;
	}
 
	return RTC::RTC_OK;
}