#ifndef __XBEE_H__ #define __XBEE_H__ #include "util/endian.h" struct XBeeAPIProtocol { static const unsigned char header[]; static const unsigned int header_size; static const unsigned int capsule_head_size; static const unsigned int capsule_tail_size; static const unsigned int capsule_size; typedef unsigned short v_u16_t; typedef unsigned char v_u8_t; template static v_u8_t calc_checksum( Container &target, const unsigned int &size, unsigned int offset){ v_u8_t checksum_u8(0xFF); for(int i(0); i < size; i++){ checksum_u8 -= *(unsigned char *)&(target[offset++]); } return checksum_u8; } /** * エンコードを行う関数 * */ struct Encoder { /** * エスケープなしのエンコード済みフレームサイズを求める * * @param payload_size データサイズ * @return (unsigned int) フレームサイズ */ static const unsigned int frame_size( const unsigned int &data_size){ return capsule_size + data_size; } /** * エンコードの前処理 * * @param target エンコード結果を格納する先 * @param data_size データサイズ * @return (unsigne int) データの先頭となるべきオフセット量 */ template static unsigned int preprocess( Container &target, const unsigned int &data_size){ unsigned int data_start_offset(capsule_head_size); // ヘッダの書き込み for(int i(0); i < header_size; i++){ target[i] = header[i]; } // サイズの書きこみ v_u16_t data_size_u16(data_size); v_u16_t data_size_u16_be( be_char2_2_num(*(char *)&data_size_u16)); target[data_start_offset - 2] = *(unsigned char *)&data_size_u16_be; target[data_start_offset - 1] = *(((unsigned char *)&data_size_u16_be) + 1); return data_start_offset; } /** * エンコードの後処理 * * @param target エンコード結果を格納する先 * @param frame_size エスケープなしのフレームサイズ * @return (unsigned int) 最終的なフレームサイズ */ template static unsigned int postprocess( Container &target, unsigned int frame_size, const bool &escape = false){ // checksumの計算 v_u8_t checksum_u8(calc_checksum( target, frame_size - capsule_tail_size - capsule_head_size, capsule_head_size)); if(escape){ unsigned int offset(0); // エスケープ処理が必要な数を数える for(int i(capsule_head_size); i < frame_size - capsule_tail_size; i++){ switch((unsigned char)target[i]){ case 0x7E: case 0x7D: case 0x11: case 0x13: offset++; } } // エスケープ処理を行う(後ろから) for(int i(frame_size - capsule_tail_size - 1), j(offset); j > 0; i--){ unsigned char c(target[i]); target[i + j] = c; switch(c){ case 0x7E: case 0x7D: case 0x11: case 0x13: target[i + j] = c ^ 0x20; target[i + (--j)] = 0x7D; } } frame_size += offset; } // checksumの付加 target[frame_size - sizeof(v_u8_t)] = *(unsigned char *)&checksum_u8; #if DEBUG > 3 std::cerr << "--write--" << std::endl; std::cerr << hex; for(int i = 0; i < frame_size; i++){ std::cerr << setfill('0') << setw(2) << (unsigned int)((unsigned char)target[i]) << ' '; } std::cerr << std::endl << dec; #endif return frame_size; } }; struct Decorder { /** * 先頭チェックを行う * * @param target 入力ストリーム(最低でもヘッダの読み出しができること) * @return (bool) 先頭チェックが完了した場合true */ template static bool valid_head(Container &target){ // ヘッダ確認 for(int i(0); i < header_size; i++){ if((unsigned char)target[i] != header[i]){ return false; } } return true; } /** * データサイズを求める * * @param target 入力ストリーム(最低でもヘッダ、データサイズの読み出しができること) * @return (unsigned int) データサイズ */ template static unsigned int data_size(Container &target){ // データサイズ確認 unsigned char buf[] = { (unsigned char)target[header_size], (unsigned char)target[header_size + 1]}; return be_char2_2_num(*(const char *)buf); } /** * 最小フレームサイズを求める * * @param target 入力ストリーム(最低でもヘッダ、データサイズ、フッタの読み出しができること) * @param escaped エスケープされている場合true(デフォルトfalse) * @return (unsigned int) フレームサイズ */ template static unsigned int minimum_frame_size(Container &target, const bool &escaped = false){ unsigned int res(data_size(target) + capsule_size); if(escaped){ for(int i(capsule_head_size), j(i); j < res - capsule_tail_size; i++, j++){ // エスケープ文字を見つけてはサイズを足していく if((unsigned char)target[i] == 0x7D){ i++; res++; } } } return res; } /** * データを取り出す * * @param target 入力ストリーム(最低でもフレームを構成する分の読み出しができること) * @param whole_size フレームサイズ * @param buf データの取り出し先 * @param extract_size データサイズ * @param escaped エスケープされている場合true(デフォルトfalse) */ template static void extract_data( Container &target, const unsigned int &whole_size, BufferT &buf, const unsigned int &extract_size, const bool &escaped = false){ #if DEBUG > 3 std::cerr << "--read--" << std::endl; std::cerr << hex; for(int i(0); i < whole_size; i++){ std::cerr << setfill('0') << setw(2) << (unsigned int)((unsigned char)target[i]) << ' '; } std::cerr << std::endl << dec; #endif for(int i(0), j(capsule_head_size); i < extract_size; i++, j++){ unsigned char c((unsigned char)target[j]); if(escaped && (c == 0x7D)){ // エスケープを考慮 c = ((unsigned char)target[++j]) ^ 0x20; } buf[i] = c; } } /** * データの妥当性を検証する * * @param target 入力ストリーム(最低でもフレームを構成する分の読み出しができること) * @param whole_size フレームサイズ * @param data 取り出されたデータ * @param data_size データサイズ * @return (bool) 妥当な場合はtrue */ template static bool validate( Container &target, const unsigned int &whole_size, BufferT &data, const unsigned int &data_size){ // checksum確認 v_u8_t checksum8_orig((unsigned char)target[whole_size - capsule_tail_size]), checksum8_calc(calc_checksum(data, data_size, 0)); #if DEBUG > 3 std::cerr << "! " << hex << setfill('0') << setw(2) << (unsigned int)checksum8_orig << " vs " << setfill('0') << setw(2) << (unsigned int)checksum8_calc << std::endl; #endif return checksum8_orig == checksum8_calc; } }; }; const unsigned char XBeeAPIProtocol::header[] = {0xF7}; const unsigned int XBeeAPIProtocol::header_size = sizeof(XBeeAPIProtocol::header); const unsigned int XBeeAPIProtocol::capsule_head_size = XBeeAPIProtocol::header_size + sizeof(XBeeAPIProtocol::v_u16_t); const unsigned int XBeeAPIProtocol::capsule_tail_size = sizeof(XBeeAPIProtocol::v_u8_t); const unsigned int XBeeAPIProtocol::capsule_size = XBeeAPIProtocol::capsule_head_size + XBeeAPIProtocol::capsule_tail_size; #ifndef __TI_COMPILER_VERSION__ // DSP向けの場合、stream/STL関連の機能はカットしておく #include #include #include #include #include template< class _Elem, class _Traits> class basic_XBeeStreambuf_out : public std::basic_streambuf<_Elem, _Traits>{ protected: typedef std::basic_streambuf<_Elem, _Traits> super_t; typedef std::streamsize streamsize; typedef typename super_t::int_type int_type; std::ostream &out; unsigned int data_size, frame_size; _Elem *frame; unsigned int frame_bufsize; bool use_escape; using super_t::pbase; using super_t::pptr; using super_t::epptr; using super_t::pbump; using super_t::setp; int_type overflow(int_type c = _Traits::eof()){ // エンコードを担当 //std::cerr << "overflow()" << std::endl; if(c != _Traits::eof()){ *epptr() = _Traits::to_char_type(c); // ヘッダ、データ、フッタの順に送信する out.write(frame, XBeeAPIProtocol::Encoder::postprocess(frame, frame_size, use_escape)); if(out.good()){ setp(pbase(), epptr()); return true; } } return _Traits::eof(); } public: void set_data_size(const unsigned int &new_size){ data_size = new_size; frame_size = XBeeAPIProtocol::Encoder::frame_size(data_size); if(frame_bufsize < frame_size){ delete [] frame; frame_bufsize = frame_size * 2; // 2倍確保 frame = new _Elem[frame_bufsize]; } _Elem *data_head(frame + XBeeAPIProtocol::Encoder::preprocess( frame, data_size)); setp(data_head, data_head + data_size - 1); } /** * コンストラクタ * * 出力フィルタとして使用する場合。この時、エンコーダが活性化する。 * * @param out 出力ストリーム * @param escape エスケープを行う場合true(デフォルトfalse) */ basic_XBeeStreambuf_out( std::ostream &_out, const bool &escape = false) : out(_out), data_size(0x10), frame_size(0), frame(NULL), frame_bufsize(0), use_escape(escape){ set_data_size(data_size); } ~basic_XBeeStreambuf_out(){ delete [] frame; } }; template< class _Elem, class _Traits> class basic_XBeeStreambuf_in : public std::basic_streambuf<_Elem, _Traits>{ public: class container_t : public std::deque<_Elem> { typedef std::deque<_Elem> super_t; protected: std::istream ∈ public: container_t(std::istream &_in) : super_t(), in(_in) {} ~container_t(){} bool pull(unsigned int n){ while(n--){ _Elem c; in.get(c); if(!in.good()){return false;} super_t::push_back(c); } return true; } void skip(unsigned int n){ typename super_t::iterator it1(super_t::begin()), it2(it1); std::advance(it2, n); super_t::erase(it1, it2); } typename super_t::size_type stored() const { return super_t::size(); } }; protected: typedef std::basic_streambuf<_Elem, _Traits> super_t; typedef std::streamsize streamsize; typedef typename super_t::int_type int_type; container_t buffer; unsigned int data_size; _Elem *data; unsigned int data_bufsize; bool use_escape; void prepair_data_buf(const unsigned int &min_size){ if(data_bufsize < min_size){ delete [] data; data_bufsize = min_size; data = new _Elem[data_bufsize]; } } using super_t::eback; using super_t::gptr; using super_t::egptr; using super_t::setg; protected: int_type underflow(){ // デコードを担当 //std::cerr << "underflow()" << std::endl; unsigned int buffer_size_min(XBeeAPIProtocol::capsule_size); bool header_checked(false); while(true){ if(buffer.stored() < buffer_size_min){ if(!buffer.pull(buffer_size_min - buffer.stored())){ return _Traits::eof(); } } if(!header_checked){ if(!XBeeAPIProtocol::Decorder::valid_head(buffer)){ buffer.skip(1); }else{ header_checked = true; data_size = XBeeAPIProtocol::Decorder::data_size(buffer); buffer_size_min += data_size; } continue; } // 真の(エスケープを考慮した)フレームサイズとの突合せ unsigned int real_frame_size(XBeeAPIProtocol::Decorder::minimum_frame_size(buffer, use_escape)); if(buffer_size_min < real_frame_size){ buffer_size_min = real_frame_size; continue; } prepair_data_buf(data_size); XBeeAPIProtocol::Decorder::extract_data(buffer, buffer_size_min, data, data_size, use_escape); if(XBeeAPIProtocol::Decorder::validate(buffer, buffer_size_min, data, data_size)){ buffer.skip(buffer_size_min); if(data_size){break;} }else{ buffer.skip(1); } buffer_size_min = XBeeAPIProtocol::capsule_size; header_checked = false; } setg(data, data, data + data_size); buffer.skip(buffer_size_min); return _Traits::to_int_type(*gptr()); } public: /** * コンストラクタ * * デコーダと協調する * * @param in 入力ストリーム * @param escape エスケープを行っている場合true(デフォルトfalse) */ basic_XBeeStreambuf_in( std::istream &_in, const bool &escape = false) : buffer(_in), data_size(0), data(NULL), data_bufsize(0), use_escape(escape) { setg(data, data, data); } ~basic_XBeeStreambuf_in(){ delete [] data; } }; typedef basic_XBeeStreambuf_in > XBeeStreambuf_in; typedef basic_XBeeStreambuf_out > XBeeStreambuf_out; class XBeeIStream : public std::istream { public: typedef XBeeStreambuf_in buf_t; protected: typedef std::istream super_t; buf_t buf; public: XBeeIStream(std::istream &in, const bool &escape = false) : buf(in, escape), super_t(&buf){} ~XBeeIStream(){} }; class XBeeOStream : public std::ostream { public: typedef XBeeStreambuf_out buf_t; protected: typedef std::ostream super_t; buf_t buf; public: XBeeOStream(std::ostream &out, const bool &escape = false) : buf(out, escape), super_t(&buf){} ~XBeeOStream(){} void set_data_size(const unsigned int &new_size){ buf.set_data_size(new_size); } }; #endif #endif /* __XBEE_H__ */