0

RS-232 と接続された 2 台のコンピューターを使用して非同期接続がどのように機能するかを示すために、大学のコースの 1 つで使用される C# でプログラムを作成しています。私のコースはプログラミングではなく、データ ネットワークに関するものなので、接続性は私が探しているものです。

図 1 - Visual Studio 2015 を使用した GUI のサンプル レイアウト

私のプログラムに実装したい機能の 1 つは、マスター/スレーブのシンプレックス接続がどのように機能するかを示すことです (つまり、プログラムは、キーボードから入力を送信するマスターか、情報を受信して​​印刷するだけのスレーブかを選択できます)。テキストボックス)。

私がすでに持っているのは、シリアル ポートを特定の特性 (ボー レート、データ ビット、ストップ ビットなど) で初期化する機能です。この機能は GUI からコンボ ボックスを使用して選択され、ユーザーがボタンをクリックして「ポートを開く」ときにポートに割り当てられます。

私が知らないのは、プログラムの「スレーブ」部分を作成する方法です。私ができることの私の考えは、プログラムを「スレーブ」にすることを選択した後、ポートを開き、入力バッファーにデータが保存されているときに何らかのフラグまたはイベントがトリガーされるのを待つことです。

いくつかのフォーラムを読んでいますが、必要なものに似たものは見つかりません。ただし、必要なものに近づけると信じていた複数の代替案をテストしましたが、結果はほとんどまたはまったくありませんでした. 私は、自分が間違っている可能性のあるアイデアや、この問題に取り組む方法についての提案を求めに来ます。問題のある行は太字(または 2 つ星 ( * ) )で示されています。

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.IO.Ports;

namespace SerialCommTester
{
public partial class frmSerialComm : Form
{
    static SerialPort _PuertoSerial;

    public frmSerialComm()
    {
        InitializeComponent();
        getAvailablePorts();
     }

    //---------------------------------my functions--------------------------------------
    void getAvailablePorts()
    {
        string[] ports = SerialPort.GetPortNames();
        cmbPortList.Items.AddRange(ports);
    }

    void activatePort()
    {
     //Note that all the combo boxes are named somewhat accordingly to what the information they are meant to display.
        if (cmbPortList.Text != "" && cmbBaudRate.Text != "" && cmbParity.Text != "" && cmbStopBits.Text != "")
        {
            _PuertoSerial.PortName = cmbPortList.Text;
            _PuertoSerial.BaudRate = Convert.ToInt32(cmbBaudRate.Text);
            _PuertoSerial.RtsEnable = true;
            _PuertoSerial.DtrEnable = true;

            _PuertoSerial.DataBits = Convert.ToInt32(cmbDataBits.Text);

            if (cmbParity.Text == "Even") { _PuertoSerial.Parity = Parity.Even; }
            else if (cmbParity.Text == "Odd") { _PuertoSerial.Parity = Parity.Odd; }
            else if (cmbParity.Text == "Space") { _PuertoSerial.Parity = Parity.Space; }
            else if (cmbParity.Text == "Mark") { _PuertoSerial.Parity = Parity.Mark; }
            else { _PuertoSerial.Parity = Parity.None; }

            if (cmbStopBits.Text =="2") { _PuertoSerial.StopBits = StopBits.Two; }
            else if (cmbStopBits.Text == "1.5") { _PuertoSerial.StopBits = StopBits.OnePointFive; }
            else { _PuertoSerial.StopBits = StopBits.One; }

            if (cmbHandShake.Text == "Software Flow Control") { _PuertoSerial.Handshake = Handshake.XOnXOff; }
            else if (cmbHandShake.Text == "Hardware Flow Control") { _PuertoSerial.Handshake = Handshake.RequestToSend; }
            else { _PuertoSerial.Handshake = Handshake.None; }

            _PuertoSerial.ReadTimeout = 500; 
            _PuertoSerial.WriteTimeout = 500;

            _PuertoSerial.Open();
//in my understanding, this line of code is needed to handle data being received. Does it trigger a flag or something?
            **_PuertoSerial.DataReceived += new SerialDataReceivedEventHandler(DataReceivedHandler);**
        }
        else
        {
            txtRecieve.Text = "Input selection missing 1 or more characteristics";
        }
    }

    **
 private static void DataReceivedHandler(object sender, SerialDataReceivedEventArgs e)
    { 
        SerialPort testing = (SerialPort)sender;
        txtRecieve.AppendText(testing.ReadExisting());  //txtRecieve cannot be reached within this function. It indicates the following error: "An object reference is required for the non-static field, method, or property 'frmSerialComm.txtRecieve'
    }
    **

    void enableDisableGUI(bool[] input)
    {
        grpConnection.Enabled = input[0];
        grpCharacteristics.Enabled = input[1];
        btnOpenPort.Enabled = input[2];
        btnClosePort.Enabled = input[3];
        txtSend.Enabled = ((cmbControlMasterSlave.Text == "Slave") ? false : true);
    }

    //----------------------------C# objects / functions--------------------------------------
    private void btnOpenPort_Click(object sender, EventArgs e)
    {
        try
        {
            _PuertoSerial = new SerialPort();
            activatePort();
        }
        catch(Exception ex)
        {
            MessageBox.Show(ex.Message, "Message ", MessageBoxButtons.OK, MessageBoxIcon.Error);
        }

        bool[] format = { false, false, false, true};
        enableDisableGUI(format);
    }

    private void btnClosePort_Click(object sender, EventArgs e)
    {
        _PuertoSerial.Close();
        bool[] format = { true, true, true, false};
        enableDisableGUI(format);
    }

    private void txtSend_KeyPress(object sender, KeyPressEventArgs e)
    {
       _PuertoSerial.Write(e.KeyChar.ToString()); //this is how I send data through the serial port.
    }

    private void btnClearTxts_Click(object sender, EventArgs e)
    {
        txtRecieve.Clear();
        txtSend.Clear();
    }

} //class closes
} //program closes

私は経験豊富なプログラマーではありません。学生に役立つものを作成したいだけです。建設的な批判は高く評価されます。

4

2 に答える 2

0

うーん、もっと読む必要があったと思います。これが私が問題を解決した方法です(これが本当の解決策でない場合、少なくとも今のところ機能しています):

  1. 「SerialDataReceivedEventHandler」行を _PuertoSerial.open(); の前に移動しました。

  2. この記事の提案に従いました。

https://msdn.microsoft.com/query/dev14.query?appId=Dev14IDEF1&l=EN-US&k=k(EHInvalidOperation.WinForms.IllegalCrossThreadCall);k(TargetFrameworkMoniker-.NETFramework,Version%3Dv4.5.2);k(DevLang -csharp)&rd=true

したがって、私の関数 (既存のもの + 新しいもの) は次のようになります。

    void DataReceivedHandler(object sender, SerialDataReceivedEventArgs e)
    {
        printReceivedText(_PuertoSerial.ReadExisting());
    }

    private void printReceivedText(string text)
    {
        if (this.txtSend.InvokeRequired)
        {
            SetTextCallback d = new SetTextCallback(printReceivedText);
            this.Invoke(d, new object[] { text });
        }
        else
        {
            this.txtRecieve.AppendText(text);
            _PuertoSerial.DiscardInBuffer();
        }
    }

今のところ、問題なく動作しているようです。最終テストは、別の端末を接続して、プログラムが相互に対話しているのを確認するときに行われます。

于 2016-12-28T21:49:20.630 に答える
0

私はあなたに決定的な答えを持っていません。あなたのコードは、考えられる 2 つの不具合を乗り越えれば、必要なものを提供するように見えます。

  1. _PuertoSerial.Open() を呼び出す前に、SerialDataReceivedEventHandler をアタッチする必要があると思います。

    通常、イベント ハンドラーは動的に有効化/無効化できるため、効果がない場合がありますが、MSDN の SerialPort の .Net ソース コードから引用した次のコメントに基づいてアドバイスします。

    // すべての魔法は、インスタンスの .Open() メソッドの呼び出しで発生します。

    // 内部的に、SerialStream コンストラクターはファイル ハンドルを開き、デバイス コントロール ブロックと関連する Win32 構造体を設定し、イベント監視サイクルを開始します。

  2. 「オブジェクト参照」エラーは、DataReceivedHandler から static 修飾子を削除することで解決される場合があります。そうでない場合、またはその静的修飾子が何らかの理由で必要な場合、おそらく txtRecieve コントロールには内部またはパブリックに変更する必要があるプライベート修飾子があります。Visual Studio をデバッグ モードで使用して、InitializeComponent() メソッドにステップインし、txtRecieve がインスタンス化されている場所を確認できるはずです。

于 2016-12-28T05:59:17.637 に答える