例として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; } |