Intereting Posts
Прочитать конкретный div из HttpResponse Почему BeginInvoke не возвращает ссылку типа AsyncResult? Невозможно разделить текстовый файл по разделителю | 64-разрядная и 32-разрядная проблема с реестром в Windows (программирование в c #) После обновления SQLite: невозможно найти точку входа с именем «sqlite3_changes_interop» в DLL «SQLite.Interop.dll» Объединение предикатов в Linq-to-entity C # – Как вычислить кодировку ASN.1 DER конкретного алгоритма хеширования? Разделение массива в виде словарных ключей в C # Добавить элементы управления в существующую ленточную группу в Office (VSTO) Командный тест Visual Studio: как установить тестовый оператор «?» С помощью только Asserts () и не использовать какой-либо инструмент utf-8 в верхнем регистре? Url.Link не работает в WebAPI Извлечь имя метода из дерева выражений? Инъекция зависимости, ввод параметров Визуальная студия Selenium Test, запускающая локальный проект

проблема с двумя streamами .NET и доступом к оборудованию

Я создаю приложение, которое общается с устройством через FT2232H конвертер USB / RS232. Для общения я использую библиотеку FTD2XX_NET.dll с сайта FTDI.
Я использую два streamа:

  • первый stream непрерывно считывает данные с устройства
  • второй stream является основным streamом приложения Windows Form

    У меня проблема, когда я пытаюсь записать какие-либо данные на устройство во время работы streamа получателя. Основной stream просто зависает на функции ftdiDevice.Write.

    Я попытался синхронизировать оба streamа, так что только один stream может одновременно использовать функцию чтения / записи, но это не помогло.

    Ниже код, ответственный за сообщение. Обратите внимание, что следующие функции – это методы classа FtdiPort.

  • Поток получателя

    private void receiverLoop() { if (this.DataReceivedHandler == null) { throw new BackendException("dataReceived delegate is not set"); } FTDI.FT_STATUS ftStatus = FTDI.FT_STATUS.FT_OK; byte[] readBytes = new byte[this.ReadBufferSize]; while (true) { lock (FtdiPort.threadLocker) { UInt32 numBytesRead = 0; ftStatus = ftdiDevice.Read(readBytes, this.ReadBufferSize, ref numBytesRead); if (ftStatus == FTDI.FT_STATUS.FT_OK) { this.DataReceivedHandler(readBytes, numBytesRead); } else { Trace.WriteLine(String.Format("Couldn't read data from ftdi: status {0}", ftStatus)); Thread.Sleep(10); } } Thread.Sleep(this.RXThreadDelay); } } 

    Функция записи, вызванная из основного streamа

    
    

      public void Write(byte[] data, int length) { if (this.IsOpened) { uint i = 0; lock (FtdiPort.threadLocker) { this.ftdiDevice.Write(data, length, ref i); } Thread.Sleep(1); if (i != (int)length) { throw new BackendException("Couldnt send all data"); } } else { throw new BackendException("Port is closed"); } } 

    Объект, используемый для синхронизации двух streamов

     static Object threadLocker = new Object(); 

    Метод, который запускает stream получателя

     private void startReceiver() { if (this.DataReceivedHandler == null) { return; } if (this.IsOpened == false) { throw new BackendException("Trying to start listening for raw data while disconnected"); } this.receiverThread = new Thread(this.receiverLoop); //this.receiverThread.Name = "protocolListener"; this.receiverThread.IsBackground = true; this.receiverThread.Start(); } 

    Функция ftdiDevice.Write не зависает, если я прокомментирую следующую строку:

     ftStatus = ftdiDevice.Read(readBytes, this.ReadBufferSize, ref numBytesRead); 

      Несколько вещей:

      1. Проверьте, заблокирован ли ваш запрос на чтение. Если это так, возможно, вы не сможете позвонить в «Запись», когда «Чтение» блокирует ожидание ответа. В документации по API вы можете получить более подробную информацию об этом.

      2. Некоторые API-интерфейсы не поддерживают множество streamов очень хорошо, даже при синхронизации доступа. Если это так, вы можете использовать дизайн, в котором вы делегируете свои команды Write в ваш коммитный stream. Когда я использовал этот шаблон в прошлом, я обычно помещаю в очередь какой-то class Command, содержащий информацию, которую я хочу написать, и либо использую class Threading Signal, чтобы позволить моим вызовам «командные» методы блокировать или предоставлять какие-то асинхронное уведомление.

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

       public FTDISample() { private AutoResetEvent receivedDataEvent; private BackgroundWorker dataReceivedHandler; private FTDI ftdi; public FTDISample(string serialNumber){ ftdi = new FTDI(); FTDI.FT_STATUS status = ftdi.OpenBySerialNumber(serialNumber); receivedDataEvent = new AutoResetEvent(false); status = mFTDI.SetEventNotification(FTDI.FT_EVENTS.FT_EVENT_RXCHAR, receivedDataEvent); dataReceivedHandler = new BackgroundWorker(); dataReceivedHandler.DoWork += ReadData; if (!dataReceivedHandler.IsBusy) { dataReceivedHandler.RunWorkerAsync(); } } private void ReadData(object pSender, DoWorkEventArgs pEventArgs) { UInt32 nrOfBytesAvailable = 0; while (true) { // wait until event is fired this.receivedDataEvent.WaitOne(); // try to recieve data now FTDI.FT_STATUS status = ftdi.GetRxBytesAvailable(ref nrOfBytesAvailable); if (status != FTDI.FT_STATUS.FT_OK) { break; } if (nrOfBytesAvailable > 0) { byte[] readData = new byte[nrOfBytesAvailable]; UInt32 numBytesRead = 0; status = mFTDI.Read(readData, nrOfBytesAvailable, ref numBytesRead); // invoke your own event handler for data received... //InvokeCharacterReceivedEvent(fParsedData); } } } public bool Write(string data) { UInt32 numBytesWritten = 0; ASCIIEncoding enconding = new ASCIIEncoding(); byte[] bytes = enconding.GetBytes(data); FTDI.FT_STATUS status = ftdi.Write(bytes, bytes.Length, ref numBytesWritten); if (status != FTDI.FT_STATUS.FT_OK) { Debug.WriteLine("FTDI Write Status ERROR: " + status); return false; } if (numBytesWritten < data.Length) { Debug.WriteLine("FTDI Write Length ERROR: " + status + " length " + data.Length + " written " + numBytesWritten); return false; } return true; } 

      Я нашел более подробную документацию по API. Действительно, функция ftdiDevice.read блокируется, если вы не установили другое значение readTimeout 0. Установка этого значения таймаута решила проблему.
      Спасибо за ваш быстрый ответ.
      С уважением

      Проверяя API, мне кажется, что драйвер способен имитировать COM-порт. Я вижу, что метод GetComPort () возвращает строку «COMx». Это довольно вероятно, что вы можете использовать class System.IO.Ports.SerialPort. Что уже делает то, что пытается сделать ваша shell, оно поддерживает событие DataReceived. Стоит сделать снимок.