質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.50%
C++

C++はC言語をもとにしてつくられた最もよく使われるマルチパラダイムプログラミング言語の1つです。オブジェクト指向、ジェネリック、命令型など広く対応しており、多目的に使用されています。

Boost

Boost (ブースト)は、C++の先駆的な開発者のコミュニティ、 またそのコミュニティによって公開されているオープンソースライブラリのことを指します。

Q&A

1回答

1551閲覧

boost::Asioのシリアル通信にて、バッファの削除が正常に動作しない

Unity-chan

総合スコア20

C++

C++はC言語をもとにしてつくられた最もよく使われるマルチパラダイムプログラミング言語の1つです。オブジェクト指向、ジェネリック、命令型など広く対応しており、多目的に使用されています。

Boost

Boost (ブースト)は、C++の先駆的な開発者のコミュニティ、 またそのコミュニティによって公開されているオープンソースライブラリのことを指します。

0グッド

0クリップ

投稿2017/12/06 19:09

###前提・実現したいこと
Arduinoと、双方向シリアル通信を、非同期で行いたい。
ArduinoとC++プログラムを同時に開発していくと、正しく動作しない場合にどちらが原因か分からなくなるため、今回は「com0com」で仮想的に二つのCOMポートを繋げつつ「RS232Cテストツール」にデータの送受信を行わせた。

###発生している問題・エラーメッセージ
COM側から前回受信した文字列よりも短い文字列を受信すると、短い文字列の後に前回受信した文字列が残る。(前回のデータに上書きされた状態で表示される)

イメージ説明
1.COM側から「22」を受信 →「22」 が出力される
2.COM側から「555」を受信 →「555」が出力される
3.COM側から「1」を受信 →「155」が出力される(ここで1が出力して欲しかった)
☆文字列長によらず、前回の文字列が出力されないようにしたい

###該当のソースコード
SerialCommunication.h

C++

1#ifndef __SerialCommunication_h__ 2#define __SerialCommunication_h__ 3 4 5// Description: 6// 同期・非同期を指定する 7// 8// Remarks: 9// InitSerialComの引数として用いる 10// 11enum SyncAsync { 12 COM_SYNC = 1, 13 COM_ASYNC = 0, 14}; 15 16 17class SerialCommunication { 18public: 19 //----------------------------------------------------------------------------- 20 // 21 // Name: SerialCommunication() 22 // 23 // Description: 24 // シリアル通信を行うための初期化を行う. 25 // 26 // Arguments: 27 // comPort - 占有するシリアルポートを指定する 28 // 29 // See Also: 30 // ~SerialCommunication() 31 // 32 SerialCommunication(char* comPort); 33 34 //----------------------------------------------------------------------------- 35 // 36 // Name: ~SerialCommunication() 37 // 38 // Description: 39 // 確保したメモリを解放する. 40 // 41 // See Also: 42 // SerialCommunication() 43 // 44 ~SerialCommunication(); 45 46 47 //----------------------------------------------------------------------------- 48 // 49 // Name: InitSerialCom() 50 // 51 // Description: 52 // シリアル通信を行う際に必要な初期設定を行う. 53 // 54 // Arguments: 55 // isSyncro - 同期or非同期を指定する(SyncAsyncを使用) 56 // baudRate - シリアル通信時のボーレートを指定する 57 // byteSize - シリアル通信時のバイト数を指定する 58 // 59 // Remarks: 60 // 現在非同期のみ実装済 61 // baudRateのデフォルト「9600」 62 // byteSizeのデフォルト「8」 63 // 64 void InitSerialCom(SyncAsync isSyncro, int baudRate, int byteSize); 65 void InitSerialCom(SyncAsync isSyncro); 66 67 //----------------------------------------------------------------------------- 68 // 69 // Name: ReceiveSetting() 70 // 71 // Description: 72 // シリアル通信を行う際に必要な追加設定を行う(主に受信). 73 // 74 // Arguments: 75 // isLoop - 受信処理を繰り返し行うか設定する(繰り返す=true) 76 // delimPhrase - 指定文字を受信するまでバッファに貯める 77 // 78 // Remarks: 79 // いつかフロー制御,パリティの設定を追加したい 80 // 81 void ReceiveSetting(bool isLoop, char* delimPhrase); 82 void ReceiveSetting(bool isLoop); 83 84 85 //----------------------------------------------------------------------------- 86 // 87 // Name: Receive() 88 // 89 // Description: 90 // 受信待機状態に入る. 91 // 92 // Remarks: 93 // ReceiveSettingに応じて挙動が変化する 94 // - isLoop = true :データの受信が完了したらもう一度受信待機状態に入る 95 // = false:データの受信が完了したら処理を終了する 96 // - delimPhrase :指定文字列を受信するまで受信バッファに貯め続ける 97 // 指定文字列を受信したら受信が完了する 98 // 99 void Receive(); 100 101 //----------------------------------------------------------------------------- 102 // 103 // Name: GetReceivedData() 104 // 105 // Description: 106 // 受信完了した,最新の文字列を取得する. 107 // 108 // Return: 109 // 受信した文字列の先頭ポインタを返す 110 // 111 char* GetReceivedData(); 112 113 114 //----------------------------------------------------------------------------- 115 // 116 // Name: Send() 117 // 118 // Description: 119 // 指定文字列を送信する. 120 // 121 // Arguments: 122 // sendData - 送信する文字列の先頭ポインタを指定する 123 // 124 void Send(char* sendData); 125 126 127 //----------------------------------------------------------------------------- 128 // 129 // Name: StopLoop() 130 // 131 // Description: 132 // 受信待機状態を解除する. 133 // 134 // Remarks: 135 // ループ状態も解除される 136 // 137 void StopLoop(); 138 139 140 141 142 143private: 144 class SerialCommunication_impl* scmImpl; 145}; 146 147#endif

SerialCommunication.cpp

C++

1#define PIMP(x) scmImpl->x // Pimplイディオムにアクセスするためのマクロ 2 3#include <boost/asio.hpp> 4#include <boost/bind.hpp> 5#include <boost/thread.hpp> 6#include <boost/array.hpp> 7#include <iostream> 8#include "SerialCommunication.h" 9 10using namespace boost::asio; 11using std::cout; 12using std::endl; 13using std::string; 14 15 16class SerialCommunication_impl { 17public: 18 io_service io; 19 serial_port sPort; 20 streambuf buf; 21 SyncAsync syncFlag; 22 boost::thread thrReceive; 23 string writeData; 24 string delim; 25 bool loopFlag; 26 string receiveData; 27 28 // 変数を初期化するコンストラクタ 29 SerialCommunication_impl(char* comPort) : 30 sPort(io, comPort), 31 thrReceive(boost::bind(&io_service::run, &io)) {} 32 33 34 // [同期/非同期]、ボーレート、通信バイトサイズを設定する 35 void Initialize(SyncAsync isSync, int baudRate, int byteSize) { 36 syncFlag = isSync; 37 38 sPort.set_option(serial_port_base::baud_rate(baudRate)); 39 sPort.set_option(serial_port_base::character_size(byteSize)); 40 } 41 42 // 通信(主に受信)における設定を変更する 43 void ReceiveSetting(bool isLoop, char* delimPhrase) { 44 //sPort.set_option(serial_port_base::flow_control(serial_port_base::flow_control::none)); 45 //sPort.set_option(serial_port_base::parity(serial_port_base::parity::none)); 46 //sPort.set_option(serial_port_base::stop_bits(serial_port_base::stop_bits::one)); 47 48 loopFlag = isLoop; 49 delim = delimPhrase; 50 } 51 52 53 // 非同期受信 54 void AsyncReceive() { 55 // コールバック関数をbindする 56 auto cbOnSend( 57 boost::bind(&SerialCommunication_impl::ReadCallBack, this, 58 placeholders::error, 59 placeholders::bytes_transferred)); 60 61 62 // 区切り文字が設定されていたらその文字が現れるまで受信バッファにため込む、未設定の場合は通信1回ごとに受信データをバッファから取得する(設定は"ReceiveSetting") 63 if(delim == "") 64 async_read(sPort, buf, transfer_at_least(1), cbOnSend); 65 else 66 async_read_until(sPort, buf, delim.c_str(), cbOnSend); 67 } 68 69 // 受信におけるコールバック 70 void ReadCallBack(const boost::system::error_code& ec, std::size_t size) { 71 // 72 if (!ec) { 73 // バッファは次の受信で使うので、違う変数に入れてバッファを空にする 74 receiveData = const_cast<char*>(buffer_cast<const char*>(buf.data())); 75 buf.consume(size); 76 77 // 受信データの確認用 78 //cout << receiveData << size << endl; 79 80 if (loopFlag) 81 AsyncReceive(); 82 } 83 else { 84 // std::cout << "Read Error." << ec.message() << std::endl; 85 } 86 } 87 88 89 // 非同期送信 90 void AsyncWrite(char* sendData) { 91 // 送信するデータをstringに変換 92 writeData = sendData; 93 94 // コールバック関数をbindする 95 auto cbOnSend( 96 boost::bind(&SerialCommunication_impl::WriteCallback, this, 97 placeholders::error, 98 placeholders::bytes_transferred)); 99 100 // 送信する 101 async_write(sPort, buffer(writeData), cbOnSend); 102 } 103 104 // 送信におけるコールバック 105 void WriteCallback(const boost::system::error_code& ec, std::size_t size) 106 { 107 if (!ec); 108// cout << "送信したぞ" << endl; 109 else 110 cout << "送信失敗" << endl; 111 } 112 113 void ThreadStop() 114 { 115 // スレッドを中断する 116 thrReceive.interrupt(); 117 118 // async_readが終了後までinterruptは適応されないため、手動で停止。 119 sPort.cancel(); 120 } 121}; 122 123 124// コンストラクタ 125SerialCommunication::SerialCommunication(char* comPort) { 126 scmImpl = new SerialCommunication_impl(comPort); 127} 128// デストラクタ 129SerialCommunication::~SerialCommunication() { 130 delete scmImpl; 131 132 cout << "Finalize SerialCommunication." << endl; 133} 134 135 136// [同期/非同期]、ボーレート、通信バイトサイズを設定する 137void SerialCommunication::InitSerialCom(SyncAsync isSyncro, int baudRate, int byteSize) { 138 PIMP(Initialize(isSyncro, baudRate, byteSize)); 139 140} 141void SerialCommunication::InitSerialCom(SyncAsync isSyncro) { 142 InitSerialCom(isSyncro, 9600, 8); 143} 144 145 146// 受信のループ設定、区切り文字の指定 147void SerialCommunication::ReceiveSetting(bool isLoop, char* delimPhrase) { 148 PIMP(ReceiveSetting(isLoop, delimPhrase)); 149} 150void SerialCommunication::ReceiveSetting(bool isLoop) { 151 PIMP(ReceiveSetting(isLoop, "")); 152} 153 154 155// 受信状態に移行する 156void SerialCommunication::Receive() { 157 if (PIMP(syncFlag)) 158 ; 159 else 160 PIMP(AsyncReceive()); 161} 162 163// 受信したデータを取得する 164char* SerialCommunication::GetReceivedData() { 165 // 「string -> const char* -> char*」にキャストした 166 return const_cast<char*>(PIMP(receiveData).c_str()); 167} 168 169// 値を送信する 170void SerialCommunication::Send(char* sendData) { 171 if (PIMP(syncFlag)) 172 ; 173 else 174 PIMP(AsyncWrite(sendData)); 175} 176 177// 受信のループを止める 178void SerialCommunication::StopLoop() { 179 PIMP(ThreadStop()); 180} 181

Sample.cpp

C++

1#include <iostream> 2#include <Windows.h> 3#include "SerialCommunication.h" 4 5using std::cout; 6using std::endl; 7 8int main(void) { 9 // COM5を初期化 10 SerialCommunication serial("COM5"); 11 12 serial.InitSerialCom( COM_ASYNC , 9600 , 8 ); 13 serial.ReceiveSetting(true); 14 serial.Receive(); 15 16 17 int i = 0; 18 while (true) { 19 // 経過秒数のカウント 20 cout << i++ << "秒経過" << endl; 21 22 // 10秒経ったら終わり 23 Sleep(1000); 24 if (i > 10) { 25 serial.StopLoop(); 26 break; 27 } 28 29 30 // 送信テスト 31 serial.Send("test123テスト"); 32 33 // 受信したデータの確認 34 cout << "受信データ:"<<serial.GetReceivedData() << endl; 35 } 36 getchar(); 37 38 39 return 0; 40}

###試したこと
〇ReadCallBackにある"buf.consume(size)"によってbufの中身が正常に消費されていることを確認した。
buf.consume(size)の前後におけるバッファのデータ変化を確認することで、バッファの消費が正常に実行されているか検証した。

C++

1 void ReadCallBack(const boost::system::error_code& ec, std::size_t size) { 2 // 3 if (!ec) { 4 // バッファは次の受信で使うので、違う変数に入れてバッファを空にする 5 receiveData = const_cast<char*>(buffer_cast<const char*>(buf.data())); 6 cout << receiveData << size << endl; 7 8 buf.consume(size); 9 10 cout << receiveData << size << endl; 11 12 if (loopFlag) 13 AsyncReceive(); 14 } 15 else { 16 // std::cout << "Read Error." << ec.message() << std::endl; 17 } 18 }

###補足情報(言語/FW/ツール等のバージョンなど)
Visual Studio 2017
Boost 1.65.1

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答1

0

こんにちは。

C言語文字列を使っているとよくやらかすのですが、最後のNULL終端を入れ忘れているということはないでしょうか? バッファは最初に0クリアしていて長い文字列は自動的にNULL終端されているとか。

ソース見てないので外れているかも。その時はごめんなさい。

投稿2017/12/07 03:26

Chironian

総合スコア23272

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

まだベストアンサーが選ばれていません

会員登録して回答してみよう

アカウントをお持ちの方は

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.50%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問