Несколько последовательных портов в C# / Проблемы с использованием списка ‹›

У меня есть система, которая отправляет команду «at» на последовательный порт и отображает результат в MessageBox. Но мне нужно было сделать это во всех доступных последовательных портах. Итак, я создал список и добавляю в него все порты. Мне удалось отправить команду, но я не смог продолжить остальную часть кода, чтобы поймать возврат, потому что у меня возникли проблемы с обработкой списков. Я новичок в C#. Ниже мой текущий код. Часть, которая закомментирована, - это то, что я изо всех сил пытаюсь продолжить. Эта часть относится к старому коду (когда был всего один последовательный порт).

public partial class Form1 : Form
    {
        List<SerialPort> serialPort = new List<SerialPort>();

        // delegate is used to write to a UI control from a non-UI thread
        private delegate void SetTextDeleg(string text);

        public Form1()
        {
            InitializeComponent();
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            var portNames = SerialPort.GetPortNames();
            foreach (var port in portNames) {
                SerialPort sp;
                sp = new SerialPort(port, 19200, Parity.None, 8, StopBits.One);
                sp.Handshake = Handshake.None;
                //sp.DataReceived += new SerialDataReceivedEventHandler(sp_DataReceived);
                sp.ReadTimeout = 500;
                sp.WriteTimeout = 500;

                serialPort.Add(sp);
                listPorts.Items.Add(port);
            }
        }

        private void listPorts_SelectedIndexChanged(object sender, EventArgs e)
        {

        }

        private void button1_Click(object sender, EventArgs e)
        {
            foreach (var sp in serialPort) {
                // Open port
                try
                {
                    if (!sp.IsOpen)
                        sp.Open();

                    MessageBox.Show(sp.PortName + " aberto!");
                    sp.Write("at\r\n");
                }
                catch (Exception ex)
                {
                    MessageBox.Show("Error opening/writing to serial port :: " + ex.Message, "Error!");
                }
            }
        }

        /* HELP START

        void sp_DataReceived(object sender, SerialDataReceivedEventArgs e)
        {
            Thread.Sleep(500);
            string data = sp.ReadLine();
            this.BeginInvoke(new SetTextDeleg(si_DataReceived), new object[] { data });
        }

        private void si_DataReceived(string data)
        {
            String retorno = data.Trim();
            MessageBox.Show(retorno);
            // Fecha a porta após pegar o retorno
            sp.Close();
        }

        HELP END */

    }

Что поставить вместо 'sp.ReadLine();' и 'sp.Close();'? Я не знаю, сделать это из-за списка ‹>


person Community    schedule 08.07.2013    source источник


Ответы (3)


Самый простой подход — использовать лямбда-выражение, которое зафиксирует используемый вами порт. Лямбда-выражение — это способ создания «встроенного» делегата, который может использовать локальные переменные из метода, в котором вы его объявляете.

Например:

foreach (var port in portNames)
{
    // Object initializer to simplify setting properties
    SerialPort sp = new SerialPort(port, 19200, Parity.None, 8, StopBits.One)
    {
        Handshake = Hanshake.None,
        ReadTimeout = 500,
        WriteTimeout = 500
    };
    sp.DataReceived += (sender, args) =>
    {
        Thread.Sleep(500); // Not sure you need this...
        string data = sp.ReadLine();
        Action action = () => {
            MessageBox.Show(data.Trim());
            sp.Close();
        };
        BeginInvoke(action);
    };
    serialPort.Add(sp);
    listPorts.Items.Add(port);
}

Несколько замечаний по этому поводу:

  • Тот факт, что были получены некоторые данные, не означает, что была получена вся строка, поэтому ReadLine все еще может блокировать
  • Если вам нужно только показать окно сообщения, вам может не понадобиться Control.BeginInvoke. (Если вам нужно сделать больше здесь, вы можете выделить большую часть этого кода в отдельный метод, который просто принимает строку, а затем создать действие, которое будет вызывать этот метод.)
  • Вы уверены, что хотите закрыть последовательный порт, как только будет получена первая строка?
person Jon Skeet    schedule 08.07.2013
comment
Благодарю вас! Это сработало, но проблема в том, что MessageBox кажется пустым. Почему это происходит? Правильным возвратом будет «ОК», но MessageBox ничего не отображает. См. отдачу от Putty и C#: Putty › i.imgur.com/AZY7aRY.png / C# › i.imgur.com/wgGyT2x.png - person ; 08.07.2013
comment
@JuninhoChr: Возможно, он сначала читает пустую строку, а затем закрывает порт? Трудно сказать наверняка, если честно. На самом деле я бы начал с консольного приложения — оно значительно упростит отладку подобных вещей, IMO. - person Jon Skeet; 08.07.2013
comment
Благодарю вас! буду следовать советам! Я не закрываю дверь, но проблема остается - person ; 08.07.2013

Вы можете изменить свой метод sp_DataReceived следующим образом:

       void sp_DataReceived(object sender, SerialDataReceivedEventArgs e)
        {
            Thread.Sleep(500);
            SerialPort sp = (SerialPort)sender;
            string data = sp.ReadLine();
            this.BeginInvoke(new SetTextDeleg(si_DataReceived), new object[] { data });
            sp.Close();
        }

и удалите метод sp.Close(); из si_DataReceived.

person Rezoan    schedule 08.07.2013

Если вы хотите иметь значение Serial port в вашем методе si_DataReceived, вы должны передать его туда:

  // First, add port into your delegate
  private delegate void SetTextDeleg(SerialPort port, string text);
  ...
  /* HELP START */

  void sp_DataReceived(object sender, SerialDataReceivedEventArgs e)
  {
    Thread.Sleep(500);
    SerialPort sp = (SerialPort) sender; // <- Obtain the serial port
    string data = sp.ReadLine();

    // Pass the serial port into  si_DataReceived: SetTextDeleg(sp, ...
    this.BeginInvoke(new SetTextDeleg(sp, si_DataReceived), new object[] { data }); 
  }

  // "SerialPort sp" is added
  private void si_DataReceived(SerialPort sp, string data) {
    String retorno = data.Trim();
    MessageBox.Show(retorno);
    // Fecha a porta após pegar o retorno
    sp.Close();
  }

  /* HELP END */

Смотрите также:

http://msdn.microsoft.com/library/system.io.ports.serialport.datareceived.aspx

person Dmitry Bychenko    schedule 08.07.2013
comment
Было бы лучше преобразовать sender в SerialPort. Таким образом, если преобразование завершится ошибкой, причина сразу станет очевидной: NullReferenceException в следующей строке неясно, а также может быть вызвано тем, что sender по какой-то причине является null. as следует использовать только тогда, когда вы действительно готовы справиться с ошибкой преобразования. - person Jon Skeet; 08.07.2013