JimB_ Ответов: 2

Почему некоторые команды к последовательному порту принимаются с правильным ответом а другие нет


при нажатии кнопки открыть порт эта кнопка устанавливает настройки бодов, устанавливает имя порта для подключения к устройству и создает evendhandler для отправки команды и получения данных отвечает от порта следующим образом

serialPortForApp.DataReceived += новый SerialDataReceivedEventHandler(SerialPortInApp_DataReceived);

с помощью этих настроек кнопка LoggedDataBtn_Click отлично работает с настройками кнопки open port и с datarecieved eventhandler. но GLPBtn_Click вообще не работает, и приложение выходит из строя.


Но кнопка GLPBtn_Click если дескриптор delete event datarecieved в OpenPortBtn_Click и я удаляю завершите SerialPortInApp_DataReceived работает идеально но мой LoggedDataBtn_Click перестает работать

Мой вопрос таков:
как управлять обработчиком datarecieved для любой команды, которая идет в последовательный порт и получает правильный ответ.

Спасибо Вам за любые предложения и ответы

Что я уже пробовал:

private void LoggedDataBtn_Click(object sender, EventArgs e)
{
//Display request command sent to device in the ShowDataInScreenTxtb

ShowDataInScreenTxtb.Clear();
ShowDataInScreenTxtb.Enabled = true;

//When LoggDataBtn is pressed it Send the command LogToShow to the instrument
string LoggedDataTOshow = "?R\r\n";
string commForMeter = string.Format(LoggedDataTOshow);

try
  {
    if (serialPortForApp.IsOpen)
    {
       Thread.Sleep(2000);
       loggedData.serialPortForApp.Write(LoggedDataTOshow);
     }
   }
   catch (Exception)
   {
     ShowDataInScreenTxtb.Text = "TimeOUT Exception: Error for requested command";
   }
 }

 private void GLPBtn_Click(object sender, EventArgs e)
{
//send command to instrument

 string GLPCommand = "?G\r\n";
string GLPToShow = String.Format(GLPCommand);

 try
 {
   if (serialPortForApp.IsOpen)
   {             
    Thread.Sleep(2000);
    //write command to port
    serialPortForApp.Write(GLPToShow);
     }
 catch
     {
    //MessageBox error if data reading is not posible
    ShowDataInScreenTxtb.Text = "TimeOUT Exception";
   }


            while (DataToSetandGet != "ENDS\r")
            {

                serialPortForApp.Write("\r");
                DataToSetandGet = serialPortForApp.ReadExisting();
 ShowDataInScreenTxtb.AppendText(glpShowInMainForm.DataToSetandGet.Replace("\r", "\r\n"));
}
}


 private void OpenPortBtn_Click(object sender, EventArgs e)
        {
            //Connect Port Button allows to connect the current device base on Port and Raud settings
            try
            {
                if (PortIndexCbx.Text == "" || BaudRateIndexCbx.Text == "")
                {
                    ShowDataInScreenTxtb.Text = "Set Up your Port Settings";                    //Error message to user to set up port connection if empty selection
                }
                else
                {
                    //Get Serial Port index settings to display for users 
                    serialPortForApp.PortName = PortIndexCbx.Text;                       //ComboBox1.Text set to PortName

                    //Display to users the settings for the Baud Rate that will be listened by the Port

                    serialPortForApp.BaudRate = Convert.ToInt32(BaudRateIndexCbx.Text);      //Baudrate is set (fix.) in ComboBox2.Text

                    // Meter default setting listened through the port DataBits, Parity, StopBits,Handshake
                    serialPortForApp.DataBits = 8;                                    
                    serialPortForApp.Parity = Parity.None;                            
                    serialPortForApp.StopBits = StopBits.One;                         
                    serialPortForApp.Handshake = Handshake.XOnXOff;                   

                    //Open serial Port to allow communication between PC and device 
                    //DataReceived Implemented to read from Port sending and recieving the buttons command requested
//To receive data, we will need to create an EventHandler for the "SerialDataReceivedEventHandler"
  serialPortForApp.DataReceived += new SerialDataReceivedEventHandler(SerialPortInApp_DataReceived);

                    
                    serialPortForApp.ReadTimeout = 500;
                    serialPortForApp.WriteTimeout = 500;


                    //Once Serial port and baud rate setting are set, connection is ready to use 
                    //Open serial port for communication between device and PC
                    serialPortForApp.Open();

                    //Connect and close connection buttons Behaviour
                    progressBarPortAccess.Value = 100; 
                    OpenPortBtn.Enabled = false;                    
                    ClosePortBtn.Enabled = true;                     
                    ShowDataInScreenTxtb.Enabled = true;
                    ShowDataInScreenTxtb.Text = "";

                    
                    LoggedDataBtn.Enabled = true;                  
                    DataReadingStatusLbl.Text = "Port is Connected";
                }
            }
            catch (UnauthorizedAccessException)
            {
                ShowDataInScreenTxtb.Text = "Unauthorized Port Access";
            }

        }


 private void SerialPortInApp_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
 try
 {

 // SerialPort1_DataReceived handles to recieve data from device connected to Port
Thread.Sleep(500);
 DataToSetandGet = serialPortForApp.ReadExisting();
  if (this.InvokeRequired)
   {
Invoke(new MethodInvoker(delegate { ShowDataInScreenTxtb.AppendText(DataToSetandGet.Replace("\r", "\r\n")); }));
}
 }
  catch (Exception)
{
ShowDataInScreenTxtb.Text = "TimeOUT Exception: Error Port communincation";
}
 }

Gerry Schmitz

Переформатируйте свой код, если вы хотите, чтобы люди смотрели на него; в противном случае слишком много работы.

2 Ответов

Рейтинг:
8

JimB_

привет всем

Я решаю эту проблему с помощью перечислений

Я создаю (общедоступное перечисление AppButtonState : Byte) и заявил, что вызов в моем случае SerialPortInApp_DataReceived. Поэтому каждый раз, когда кнопка посылает команду, она сначала оценивается перечислением. Он создает независимый вызов для каждой команды, которую я хочу, чтобы событие SerialPortInApp_DataReceived считывало и отвечало.

спасибо


Рейтинг:
20

Jochen Arndt

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

То же самое относится и к DataToSetandGet переменная, используемая там для хранения полученной строки. Вы не можете использовать его из других потоков (например, GUI / main thread, как в вашем GLPBtn_Click() функция), не гарантируя, что только один поток обращается к нему одновременно.

Вы тоже звоните serialPortForApp.ReadExisting() в приведенной выше функции и полученном обработчике. Это тоже не сработает. Спросите себя: какой из двух вызовов read выполняется?

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

Один из способов обработки запросов специфичных для команды before send заключается в реализации Конечная машина - Википедия[^]. Простой пример:

  • Состояние находится в режиме ожидания (можно отправить команду)
  • Отправка команды: состояние ожидает ответа (необязательно конкретного ответа)
  • Ответ: государство имеет ответа
  • Ответ обработан: состояние бездействует (можно отправить следующую команду)

Наконец, нет никакой необходимости звонить Sleep() в правильно написанном многопоточном приложении. Его вообще следует избегать.


CPallini

5.