0

我正在用 C# 构建一个程序,用于我在一所大学的一门课程中,以演示异步连接如何使用 RS-232 和两台连接在一起的计算机工作。我的课程不是关于编程,而是关于数据网络,所以我正在寻找连接性。

图 1 - 使用 Visual Studio 2015 的 GUI 示例布局

我想在我的程序中实现的功能之一是显示主从单工连接的工作原理(即程序可以选择作为主机从键盘发送输入;或从机只接收信息并在其上打印一个文本框)。

我已经拥有的是使用特定特性(波特率、数据位、停止位等)初始化串行端口的能力。此功能使用 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. 我认为您应该在调用 _PuertoSerial.Open() 之前附加您的 SerialDataReceivedEventHandler。

    它可能没有效果,因为事件处理程序通常可以动态启用/禁用,但我的建议基于从 MSDN 上 SerialPort 的 .Net 源代码中获取的以下评论。

    // 所有的魔法都发生在对实例的 .Open() 方法的调用中。

    // 在内部,SerialStream 构造函数打开文件句柄,设置设备控制块和关联的 Win32 结构,并开始事件监视周期。

  2. 通过从 DataReceivedHandler 中删除静态修饰符可以解决“对象引用”错误。如果不是,或者如果出于某种原因需要该静态修饰符,那么可能 txtRecieve 控件具有需要更改为内部或公共的私有修饰符。您应该能够在调试模式下使用 Visual Studio 来单步执行 InitializeComponent() 方法并查看 txtRecieve 的实例化位置。

于 2016-12-28T05:59:17.637 回答
0

好吧,我相信我需要阅读更多内容。这就是我解决问题的方法(如果这不是真正的解决方案,至少现在可以工作):

  1. 我在 _PuertoSerial.open(); 之前移动了“SerialDataReceivedEventHandler”行

  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 回答