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

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

ただいまの
回答率

90.51%

  • C#

    7100questions

    C#はマルチパラダイムプログラミング言語の1つで、命令形・宣言型・関数型・ジェネリック型・コンポーネント指向・オブジェクティブ指向のプログラミング開発すべてに対応しています。

C# シリアル通信で受信したデータを上位クラスに渡したい

解決済

回答 2

投稿 編集

  • 評価
  • クリップ 1
  • VIEW 304

Caffellattep

score 4

visualStudio2017 でシリアル通信で相手ポートに送信したデータの内容に依って、対応した受信データを受け取る処理を作っています。
(実際はRS232Cで複数接続したセンサに命令を送って返答を非同期で受け取るシステムのです。)
例えば10台センサがあるとして、10回命令を送信します。(測定値を送るというコマンドなど)
すると、10台分の結果を随時受け取り、変数にセットする仕組みです。
センサ側はCOMポートで接続しています。

受信はSerialPortのDataReceivedイベントを使っており、データを受け取る所は確認したのですが、この情報を
上位クラスが持っている変数にセットしたいのです。
送信データを10台文loopしているメソッドで受信データを受け取り、変数に値をセットするというのは可能なのでしょうか。

PressSerial.cs

        // グループNo
        public int GroupNo { private set; get; }
        private Serial mSerial;
        delegate void DisplayTextDelegate();

        public PressSerial(int group)
        {
            GroupNo = group;

            mSerial = new Serial(SerialEventHandler);

        }

        /// シリアル通信クラス コンストラクタ
        public PressSerial()
        {

            mSerial = new Serial(SerialEventHandler);

        }

        public async Task Hell_Async(int gno, int ch_su,List<PressSensInfo> psenList)
        {
            //psenListに10台文の情報が入っている
            var timeout = TimeSpan.FromMilliseconds(1000);
            for (int i = 0; i < ch_su; i++)
            {
               var rc = await Task.WhenAny(
                    Task.Run(async () =>
                    { 
             // 送信コマンドの編集を行って送信
                        CreateSendCommand(psens.Channel); 
                    }));

                try
                {
                    rc.Wait(timeout);
                    sensInfo.Add(new PressSerial(gno, i));
                } catch
                {

                }
        }
    }
        public void CreateSendCommand(int ch)
        {

            byte[] Packet;
…ここで送信データ文字列をbyte型に変換処理などを行っている
            mSerial.SendBuff(Packet);
        }

        void SerialEventHandler(Serial.SerialEventArgs e)
        {
            switch (e.Event)
            {
                case Serial.SerialEventArgs.SERIALEVENT.SERIALEVENT_CONNECT:
                    break;
                case Serial.SerialEventArgs.SERIALEVENT.SERIALEVENT_RECEIVE:
                    /// <summary>
                    /// 受信コマンド
                    /// </summary>
                    void CommandReceive()
                    {

                        string text = System.Text.Encoding.ASCII.GetString(e.ReceiveBody);
                        tmpReceivedByte = e.ReceiveBody; // ここに受信データ


            }
            DisplayTextDelegate d = new DisplayTextDelegate(CommandReceive);
                    d?.Invoke();

                    break;
            }
        }
Serial.cs
        public event SerialEventHandler SerialEvent;// Serialデリゲート
        public delegate void SerialEventHandler(SerialEventArgs e);// イベントハンドラのデリゲート

        public void SendBuff(byte[]sendBuffer)
        {
            byte[] rbuf = new byte[1];
            if (m_Serial.IsOpen == true)
            {
         // 1バイトずつ送信
                for (int c = 0; c < sendBuffer.Length; c++)
                {
                    rbuf[0] = sendBuffer[c];
                    m_Serial.Write(rbuf, 0, rbuf.Length);
                }
            }
        }

        private void Serial_DataReceived(object sender, System.IO.Ports.SerialDataReceivedEventArgs e) 
        {
            byte tbyte = 0;
            // 受信バッファから値を取得する
            tbyte = (byte)m_Serial.BytesToRead;

            byte[] rbuf = new byte[tbyte];

            m_Serial.Read(rbuf, 0, tbyte);

            //受信イベント
            ReceiveEvent(SerialEventArgs.SERIALEVENT.SERIALEVENT_RECEIVE, rbuf);
        }

        /// 受信デリゲート呼び出し処理
        private void ReceiveEvent(SerialEventArgs.SERIALEVENT e, byte[] body)
        {
            //デリゲート呼び出し
            SerialEvent?.Invoke(new SerialEventArgs(e, mNoticeId, body));
        }
PressureSens.cs(ユーザーがアクセスする最上位クラス)
        // シリアル通信クラス。要素はグループ数分。
        private List<PressSerial> PressSerialList;
        // センサ情報リスト
        public List<PressSensInfo> PSensInfoList { private set; get; }

        // コンストラクタ
        public PressureSens()
        {
            PressSerialList = new List<PressSerial>();
            PSensInfoList = new List<PressSensInfo>();
            for (int i = 0; i < 3)  // グループNo(接続したCOMポート数)が3台とする
            {
                PressSerialList.Add(new PressSerial(i);
            }
        }

        // センサ情報、接続台数チェックなど。
        public async Task<Boolean> Initialize()
        {
            Boolean ret = true;

            // センサ応答チェック呼び出し
            await Task.Run(async () =>
            {
                // 実際はグループ毎に非同期で計測する。

                    int gno = 3;   // グループNo(接続したCOMポート数)
                    int ch_su = 10; // 接続台数 
                    for (int i = 0; i < gno; i++)
                    {
                       var rc =  mPressSerial.Hell_Async(i, ch_su, PSensInfoList);
                    }
                }
            });
            return true;
        }
    PressSensInfo.cs(センサデータ格納クラス)
        /// 現在時刻(アラームやセンサ値など状態更新時の時刻)
        public DateTime Time;
        /// グループNo
        public int GroupNo { private set; get; }
        /// センサチャンネル
        public int c { private set; get; }
        /// グローバルID
        /// </summary>
        public int GlobalID { private set; get; }
        /// センサ計測値
        public Double Value = 0.0;
        /// <summary>
        /// センサ状態
        /// </summary>
        public int Status { private set; get; } = 0; // 0:待機 1:計測中 2:データエラー 3: …など

        // センサデータクラスのコンストラクタ
        public PressSensInfo (int group, int ch)
        {
            Status = 0;
            Time = DateTime.Now;
            GroupNo = group;
            Channel = ch;
            GlobalID = group * 100 + ch;
        }


PressSerialクラスのHell_Asyncメソッド内でList配列のセンサ情報をloopでID情報などを送信して、センサ側からの応答を[受け取り、同じ配列データの変数に受信内容をセットするようにしたいのですが、DataReceivedイベントから、デリゲード呼び出しでPressSerialクラスのSerialEventHandler内で受信データを拾うことはできたのですが、この値をどうやってList<PressSensInfo>内にセットすれば良いか、
SerialEventHandlerから上位のメソッドにコールバックする方法が分かりません。

上に[書いたメソッドは直接関係ないところは省略しています。
どうか、宜しくお願いいします。

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

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

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

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 2

checkベストアンサー

+2

●全体的な構造に関して
考え方として、Serial_DataReceivedを使わない方法が良いと思いました。

例えば下記のような方法になります。
https://tomosoft.jp/design/?p=11200

PressSerial.csのHell_Async内の下記が完了すると、COMポートへの送信完了になるため、

var rc = await Task.WhenAny(
                    Task.Run(async () =>
                    { 
             // 送信コマンドの編集を行って送信
                        CreateSendCommand(psens.Channel); 
                    }));


その次に部分(tryの部分)に、下記サイトのbtnconnect_Clickのawait Task.Run(() => readserial());に相当する部分を追加変更すればよいのではないでしょうか?
https://tomosoft.jp/design/?p=11200

●PressSerial.csのSerialのインスタンスについて
現在、インスタンスはmSerialの1つしか存在しないように見えました。
10個シリアルポートが存在し、並列に個別に送信・受信するのであるならば
10個のインスタンスが必要なはずですが、どうなっているんだろうと疑問に思いました。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2018/06/27 12:00

    ご回答ありがとうございます。返答が遅れてしまい申し訳ありません。
    上記の方法で、PressSerial.Hell_Async内で受信することが出来ました。ありがとうございます。

    > ●PressSerial.csのSerialのインスタンスについて
    > 現在、インスタンスはmSerialの1つしか存在しないように見えました。
    > 10個シリアルポートが存在し、並列に個別に送信・受信するのであるならば
    > 10個のインスタンスが必要なはずですが、どうなっているんだろうと疑問に思いました。

    ご指摘ありがとうございます。実はPressureSens.csのコンストラクタでシリアルポート数分インスタンスを生成しています(省略していました)

    キャンセル

+1

いろいろ方法がありますが、上位クラスでList<PressSensInfo>のプロパティを作り、下位クラスから値を設定していけばいいのでは。
で、どうやって設定するのか、というのはPressSensInfoクラスが示されてないために詳しいことは言えませんが、

list.Add(new PressSensInfo(受信結果));

という形になると思われます

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2018/06/26 06:00

    ご回答ありがとうございます。情報が足りなかった為、ユーザがアクセスする最上位クラス(PresserSenseクラス)とセンサ情報を格納用のPressSensInfo.csを追加しました。
    実際は、複数のCOMポートにそれぞれ複数のセンサがぶら下がっている状態です。
    ユーザがPresserSenseのHell_Asyncメソッドを呼び出して、センサに接続されているか、測定値を求めたりなど色々と命令を送信します。センサからの応答を非同期で受け取るという流れです。
    非同期処理やら、通信処理がごちゃごちゃになってどうやって複数のセンサの応答を受け取り(又は、応答が無い時はタイムアウトエラー)センサ情報格納リストにセットしていけばよいかわかりません。
    現状の作りでは、PressSerialのSerialEventHandlerで受信データを受け取り処理を行っていますが、本当はHell_Asyncで受け取りたいです。sensInfo.Add(new PressSensInfo(グループno, チャンネル));の形で設定するので、受信データがどのグループNoでチャンネルのを聞いたのかが分からくなるので。
    分かりにくい説明ですみません。

    キャンセル

  • 2018/06/26 07:34

    グループNoとチャンネルとコマンド種別、受信結果で構成されるクラスを作って、そのリストを親クラスのプロパティとして設定し、下位クラスから追加していく、でいいかと思いますが、なにか問題はあるでしょうか

    キャンセル

  • 2018/06/26 09:03

    >下位クラスから追加していく
    すみません。この場合の下位クラスとはどこになるのでしょうか。今はPressSerialクラスのHell_Asyncメソッドで追加しようとしています。

    キャンセル

  • 2018/06/26 09:20

    PressSerial.cs のクラスにPressureSens.cs のインスタンスを持たせるようにしとけば、そこからPSensInfoListにアクセスできるでしょ

    キャンセル

  • 2018/06/26 15:07 編集

    重ね重ねすみません。
    >PressSerial.cs のクラスにPressureSens.cs のインスタンスを持たせる
    PressSerial.cs のクラス内でPressureSens.cs のインスタンスを渡してみました。

    PressSens = new PressureSens();

     データ受信のコールバックで
    PrSens.PSensInfoList.Add(new PressSensInfo(グループno, チャンネル));
    をすると、データが追加されました。このやり方で良かったのでしょうか。
    只、グループNoが判断できない為また試行錯誤中です

    キャンセル

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

  • ただいまの回答率 90.51%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

  • 受付中

    arduinoと温度センサ

    arduinoと温度センサ arduinoで温度センサを自作しようと考えています。 そこで、7セグLED温度センサのPICをarduinoで代用出来ないかと考えました。マイコン→A

  • 受付中

    WPFでawait Task.RunにしたらSTAエラーになっています

    こんにちは。  Windows10でWPFのアプリケーションを開発しています。  Visual Studio 2015 Communityを使っています。  前提・実現したいこ

  • 解決済

    Unity2Dでクリックしたオブジェクトの座標を取得したい

    前提・実現したいこと Unity2Dでドラッグ&ドロップではめていくパズルゲームを作成中です。 現在、パズルが出てくるスポーンが一つだけなのですが、三つに増やしたいと考えてお

  • 解決済

    WPF 複数の動画を順番に再生したい

    お世話になっております。 行いたいこと 動画を順番に再生したいと思っています。 仕様 ・再生したい動画を指定(動画1,動画2,動画3,…,動画N)→ボタンを押す→新しいウィ

  • 解決済

    ListView ヘッダ部の背景色の指定

    C#(visual studio 2010)にて、ListViewをつかった表を作成しております。 ヘッダ部およびデータ部へ情報の表示はできたのですが、 ListViewヘッダ部の

  • 解決済

    DOTweenを使用したSpriteAnimation

    お世話になっております。  前提・実現したいこと UnityのDOTweenを使用してSpriteAnimationのように Spriteを連続して切り替えて、コマ送りのアニメ

  • 解決済

    C#+SeleniumでStaleElementReferenceError

    C#でSeleniumを使用して自動化処理を書いています。 (ブラウザはChromeです。) OS: Windows10 IDE: Visual Studio 2015 Sel

  • 解決済

    staticなクラス変数について

    非常に初歩的な質問で恐縮なのですが、例えば生成されたインスタンスの数などの、Count的なstaticクラス変数をもたせたい場合、 その値はどのようにして更新すればいいのでしょうか

同じタグがついた質問を見る

  • C#

    7100questions

    C#はマルチパラダイムプログラミング言語の1つで、命令形・宣言型・関数型・ジェネリック型・コンポーネント指向・オブジェクティブ指向のプログラミング開発すべてに対応しています。