none
SerialDevice .NET Core determine number of characters received? RRS feed

  • Question

  • Is there a way to determine the number of characters that has been received by the SerialDevice before trying to use a DataReaderLoad operation and DataReader?  It does not seem like SerialDevice.Size is an available method and the device that is transmitting back to me has an undeterminate amount of characters. How can you handle this when you have to tell DataReader how many characters to read?

    Also I noted that ReadTimeout and WriteTimeout are not implemented with DataReader.LoadAsync...had to use a CancellationToken to actually break the LoadAsync after an amount of time...this all seems really kludgy...any suggestions?

    Thank you


    Dan DeMerchant


    • Edited by danoplus Sunday, October 20, 2019 6:47 PM
    Saturday, October 19, 2019 1:34 PM

Answers

  • That's pretty much the same solution.  I don't know why we even need to use DataReader.  Shouldn't the people working on Core actually implement SerialPort.ReadTimeout and SerialPort.WriteTimeout?  Also also provide a method that tells you how many characters arrived to SerialPort?  This is especially true since we are working on low level hardware interfaces with WinIOT over PCs?  I mean waiting for a timeout is really not good when if you had a method to know how many characters came across after a transmission, you could just grab them and not wait for the timeout...

    Dan DeMerchant




    • Marked as answer by danoplus Thursday, October 24, 2019 12:49 AM
    • Edited by danoplus Thursday, October 24, 2019 12:52 AM
    Thursday, October 24, 2019 12:49 AM

All replies

  • Hello Dan,

    There is no property or method to get the size of received data before trying to use a DataReaderLoad operation and DataReader. Not sure your real purpose, the property UnconsumedBufferLength of DataReader shows the size of the buffer that has not been read, in bytes. This seems to be what you want.

    Best Regards,

    Michael


    MSDN Community Support Please remember to click "Mark as Answer" the responses that resolved your issue, and to click "Unmark as Answer" if not. This can be beneficial to other community members reading this thread. If you have any compliments or complaints to MSDN Support, feel free to contact MSDNFSF@microsoft.com.

    Monday, October 21, 2019 3:13 AM
    Moderator
  • The problem with using UnconsumedBufferLength is that you have to call DataReader.LoadAsync(int numberOfCharacters) to move the incoming characters from the SerialPort to the DataReader object.  If you don't know the number of characters that you are waiting for in the response, and you choose more characters than what is returned...then the LoadAsync call will block forever.  This is especially true because it does not seem like the ReadTimeout and WriteTimeout parameters in SerialPort are actually implemented in Core...at least these parameters do not have an affect on LoadAsync from what I can determine.  A read using LoadAsync with more characters than arrived never times out on its own, it just hangs in Awaited state forever. 

    Unlike regular .NET where you can look at buffer.Length, where buffer is part of the underlying StreamReader to the SerialPort...to determine how many characters arrived to the port, there does not seem to be an equivalent way to do this in Core 2.1.  You also don't have to pre-load the characters into StreamReader like you have to with DataReader...the characters just arrive in the StreamReader buffer, and you can peek them or get a count of how many arrived with standard .NET.  Apparently you always need to know how many characters are returned in the Core SerialPort implementation; you don't have an equivalent to the StreamReader.Peek in Core.  Also in Core, if you call DataReader.LoadAsync, and you load less characters than arrived, lets say for example you load 5 characters from the SerialPort but 10 arrived, then UnconsumedBufferLength will = 5...not 10.  

    I am working with an EMV chip credit card reader, which is serial based.  The communication protocol to the reader does not have a standard termination character.  The length of the return changes based on the type of credit card, so you never know how many characters are coming back to you after sending an ADPU (EMV chip data block).  

    I finally over the weekend figured out a way to handle this scenario with Core, but it's not a super clean solution and I am not sure if the SerialPort implementation in the current Core framework is complete, but I ended up using a CancellationToken to implement a SerialPort ReadTimeout.  With my communication routine, if you know the amount of characters that you are looking for on the serial port, you can specify that and after receiving the specified amount of characters, the receive routine reports the incoming message back.  If you don't know the number of characters coming back, you can put a large integer into the NumberOfCharactersToWaitFor property, and the routine will timeout if the characters never arrive:

      async Task<byte[]> SendCommandAsync(byte Command, byte[] Data,int NumberOfCharactersToWaitFor)
            {
                //SEND THE COMMAND TO THE HARDWARE
                byte[] FrameToSend = BuildDataFrame(Command, Data);
                
                //DEBUGGING Output.Text += "OUT: " + BitConverter.ToString(FrameToSend) + "\n";
                try
                    {
                        using (var cts = new CancellationTokenSource(RS232Connection.WriteTimeout))
                        {
                            await RS232Connection.OutputStream.WriteAsync(FrameToSend.AsBuffer()).AsTask(cts.Token);
                        }
                    }
                    catch (TaskCanceledException) { return new byte[] { }; }
                    catch (Exception) { return new byte[] { }; }
          
                await Task.Delay(DelayAfterTransmission); //wait for an amount of data to come in
    
                //GET BACK THE RESPONSE FROM THE HARDWARE
                List<byte> Message = new List<byte>();
                try
                {
                    using (var cts = new CancellationTokenSource(RS232Connection.ReadTimeout))
                    {
                        do
                        {
                            IBuffer Character = (new byte[1]).AsBuffer();
                            await RS232Connection.InputStream.ReadAsync(Character, 1, InputStreamOptions.Partial).AsTask(cts.Token);
                            Message.Add(Character.GetByte(0));
                            NumberOfCharactersToWaitFor--;
                        } while (NumberOfCharactersToWaitFor>0);
                        if (Message.Count > 0) return Message.ToArray(); else return new byte[] { };
                    }
                }
                catch (TaskCanceledException) { /*Output.Text += "CANCELLED WAIT";Output.Text += " IN: " + BitConverter.ToString(Message.ToArray()) + "\n";*/ if (Message.Count > 0) return Message.ToArray(); else return new byte[] { }; }
                catch (Exception) { if (Message.Count > 0) return Message.ToArray();  }
            }

    Originally, I had just a true loop to retrieve each character and relied on a timeout to exit the loop, but this made the communications slow having to wait for a timeout for every response.  For responses where I actually knew the amount of data coming back or the piece of data I was waiting for, I specify the number of characters to avoid the timeout delay, speeding up the overall communications.

    Should we be waiting for a better SerialPort implementation in a future Core or I'm I completely missing something?  I posted this code for anyone that might find it helpful to do a SerialPort implementation on a piece of IOT hardware...

    If there is something that I don't know about, please fill in the blanks for me...

    Thank you


    Dan DeMerchant








    • Edited by danoplus Monday, October 21, 2019 8:52 PM
    Monday, October 21, 2019 7:26 PM
  • Hello Dan,

    I'm not sure why you avoided using DataReader.LoadAsync method. As far as i know, there is no other method to get the size of received data in buffer in UWP. You can create a buffer  in your app, put the received data in this buffer when data arrived, and then check the size of this buffer.

    Best Regards,

    Michael


    MSDN Community Support Please remember to click "Mark as Answer" the responses that resolved your issue, and to click "Unmark as Answer" if not. This can be beneficial to other community members reading this thread. If you have any compliments or complaints to MSDN Support, feel free to contact MSDNFSF@microsoft.com.

    Tuesday, October 22, 2019 7:32 AM
    Moderator
  • I can't use DataReader.LoadAsync method because this method requires you to provide the number of characters.  If you provide a number of characters that is larger than what arrives in the serial port, the application will hang stuck in an awaited mode waiting for more characters...because the ReadTimout for SerialPort does not seem to be implemented...it does not stop DataReader.LoadAsync from staying awaited. 

    Dan DeMerchant


    • Edited by danoplus Tuesday, October 22, 2019 2:09 PM
    Tuesday, October 22, 2019 1:00 PM
  • Hello Dan,

    You might refer to following code. In this code snippet, it sets ReadBufferLength as a for LoadAsync method, so that if there is a byte arrived, the app will read it and not block at LoadAsync. In other word, the app will read the data from serial port one by one byte.

            private async Task ReadAsync(CancellationToken cancellationToken)
            {
                Task<UInt32> loadAsyncTask;
                List<byte> receivedBuffer = new List<byte>();
    
                uint ReadBufferLength = 1;
    
                // Don't start any IO if we canceled the task
                lock (ReadCancelLock)
                {
                    cancellationToken.ThrowIfCancellationRequested();
    
                    // Cancellation Token will be used so we can stop the task operation explicitly
                    // The completion function should still be called so that we can properly handle a canceled task
                    DataReaderObject.InputStreamOptions = InputStreamOptions.Partial;
                    loadAsyncTask = DataReaderObject.LoadAsync(ReadBufferLength).AsTask(cancellationToken);
                }
    
                await loadAsyncTask;
    
                while(DataReaderObject.UnconsumedBufferLength > 0)
                {
                    receivedBuffer.Add(DataReaderObject.ReadByte());
                    await loadAsyncTask;
                }
            }

    Best Regards,

    Michael


    MSDN Community Support Please remember to click "Mark as Answer" the responses that resolved your issue, and to click "Unmark as Answer" if not. This can be beneficial to other community members reading this thread. If you have any compliments or complaints to MSDN Support, feel free to contact MSDNFSF@microsoft.com.



    Wednesday, October 23, 2019 2:39 AM
    Moderator
  • That's pretty much the same solution.  I don't know why we even need to use DataReader.  Shouldn't the people working on Core actually implement SerialPort.ReadTimeout and SerialPort.WriteTimeout?  Also also provide a method that tells you how many characters arrived to SerialPort?  This is especially true since we are working on low level hardware interfaces with WinIOT over PCs?  I mean waiting for a timeout is really not good when if you had a method to know how many characters came across after a transmission, you could just grab them and not wait for the timeout...

    Dan DeMerchant




    • Marked as answer by danoplus Thursday, October 24, 2019 12:49 AM
    • Edited by danoplus Thursday, October 24, 2019 12:52 AM
    Thursday, October 24, 2019 12:49 AM