Как узнать, что входной буфер последовательного порта имеет информацию на С#?

Я создаю программу на C#, которую буду использовать на одном из своих курсов в колледже, чтобы продемонстрировать, как работают асинхронные соединения с использованием RS-232 и двух компьютеров, соединенных вместе. Мой курс не о программировании, а о сетях передачи данных, поэтому подключение — это то, что я ищу.

рисунок 1 — пример макета графического интерфейса с использованием Visual Studio 2015

Одна из функций, которую я хочу реализовать в своей программе, состоит в том, чтобы показать, как работает симплексное соединение ведущий-ведомый (т. е. программа может выбирать между тем, чтобы быть ведущим для отправки ввода с клавиатуры или подчиненным, чтобы только получать информацию и печатать ее на текстовое поле).

Что у меня уже есть, так это возможность инициализировать последовательный порт с определенными характеристиками (скорость передачи данных, биты данных, стоповые биты и т. д.). Эти функции выбираются с помощью полей со списком в графическом интерфейсе и назначаются порту, когда пользователь нажимает кнопку, чтобы «открыть порт».

Чего я не знаю, так это как создать "ведомую" часть программы. Моя идея о том, что я мог бы сделать, заключается в том, что после того, как вы выберете программу в качестве «ведомой», вы откроете порт, ожидая запуска какого-либо флага или события, когда во входном буфере сохранены данные.

Перечитал несколько форумов и не нашел ничего похожего на то, что мне нужно. Однако я протестировал несколько альтернатив, которые, как я полагал, приблизят меня к тому, что мне нужно, практически безрезультатно. Я прихожу, чтобы спросить, что я могу делать неправильно, или посоветовать, как решить эту проблему. Проблемные строки выделены жирным шрифтом (или 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

Я не опытный программист, я просто хочу создать что-то полезное для своих учеников. Любая конструктивная критика будет высоко оценена.


person DonPandon    schedule 28.12.2016    source источник


Ответы (2)


У меня нет для вас окончательных ответов. Ваш код выглядит так, как будто он должен предоставить то, что вам нужно, как только вы преодолеете два возможных сбоя.

  1. Я думаю, вы должны прикрепить свой SerialDataReceivedEventHandler ПЕРЕД вызовом _PuertoSerial.Open().

    Это может не иметь никакого эффекта, поскольку обработчики событий обычно могут быть включены/отключены динамически, но я основываю совет на следующем комментарии, взятом из исходного кода .Net для SerialPort в MSDN.

    // все волшебство происходит при вызове метода экземпляра .Open().

    // Внутри конструктор SerialStream открывает дескриптор файла, устанавливает блок управления устройством и связанные структуры Win32 и начинает цикл наблюдения за событиями.

  2. Ошибка «ссылка на объект» может быть устранена путем удаления модификатора static из вашего DataReceivedHandler. Если нет, или если этот статический модификатор необходим по какой-то причине, то, возможно, элемент управления txtRecieve имеет частный модификатор, который необходимо изменить на внутренний или общедоступный. Вы должны иметь возможность использовать Visual Studio в режиме отладки, чтобы перейти к методу InitializeComponent() и посмотреть, где создается экземпляр txtRecieve.

person Community    schedule 28.12.2016
comment
Спасибо за помощь! - person DonPandon; 29.12.2016

Ну, я считаю, что мне нужно было больше читать. Вот как я решил проблему (если это не настоящее решение, по крайней мере, пока работает):

  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();
        }
    }

На данный момент, кажется, работает нормально. Окончательное тестирование будет, когда я подключу другой терминал и увижу, как программы взаимодействуют друг с другом.

person DonPandon    schedule 28.12.2016