Search code examples
c#uwpwin-universal-appwindows-10-universalwindowsiot

App sometimes freezes/crashes when invoking serial port communication. How to fix that?


I'm currently developing a WIn IoT app for the Raspberry Pi and I have a significant problem there...I could really need some help solving that...

The problem I'm having is that I have different commands I send over the serial port and every command recieves different responses.

After sending a few commands (can be just one or it can be 15...it's basically random -.-) my app just stops accepting the commands to send or completely crashes when I invoke one of the commands. It happens rather randomly and when I restart the app it's working again. At least most of the time... Sometimes the first restart doesn't do anything and I have to restart again...

Since the app will probably be used in a bigger project I want to guarantee flawless execution. I don't want to have a customer (or the tool we use to communicate with the Pi) to restart the whole app. It should just abort it's non-working operation, log an error and then be back up running.

I think it may be a possibility that the buffer overflows for whatever reason... But I don't know of any way to clear the buffer from code in UWP. And I can't seem to find anything regarding that.

If you can help me I would be really happy. I'm trying to get this to work for almost a week but I can't find the problem...

My colleague rewrote some of my code last week. I originally passed every info I needed through tuples but he told me it's not good. So he wrote the class containing the info for me. But since then I run into a lot of problems. But I really don't know why it doesn't...

If you want to know more about my code feel free to ask. I really need this to work :/

Some of the code I'm using:

This is the block containing my data:

public class InfoBlock
{
    public List<InfoBlockValue> Names { get; set; }

    public byte[] ReceivedCrcSum { get; set; }

    public byte[] CalculatedCrcSum { get; set; }

    public bool IsChecksumEqual { get; set; } = false;
}

public class InfoBlockValue
{
    public string InfoString { get; set; }

    public InfoBlockValue(string info)
    {
        this.InfoString = info;
    }

    public override string ToString()
    {
        return InfoString;
    }
}

This is the class for my Read and Write operations:

public class GetInfoBlock
{
    public string SendCommand { get; set; } = "##getnames";

    public async Task<uint> Write()
    {
        byte CarriageReturn = 0x0D;

        byte[] WriteArray = StringToByteArray(SendCommand);

        byte[] WriteArrayCR = new byte[WriteArray.Length + 1];
        WriteArray.CopyTo(WriteArrayCR, 0);
        WriteArrayCR[WriteArray.Length] = CarriageReturn;

        return await ReadWriteAdapter.Current.WriteAsync(WriteArrayCR);
    }

    public async Task<InfoBlock> Read()
    {
        InfoBlock block = new InfoBlock();

        byte[] ListenOut = await ReadWriteAdapter.Current.Listen(5802);

        byte[] NameCrcSource = new byte[5800];
        byte[] NameCrc16 = new byte[2];

        Array.Copy(ListenOut, 0, NameCrcSource, 0, 5800);

        block.Names = ConvertFromBytes(NameCrcSource);
        block.ReceivedCrcSum = new byte[] { ListenOut[5801], ListenOut[5800] };

        block.CalculatedCrcSum = Crc16Ccitt.ComputeChecksumBytes(NameCrcSource);
        block.IsChecksumEqual = ValidateDataCRC.ValidateData(NameCrcSource, block.ReceivedCrcSum);

        return block;
    }

    public List<InfoBlockValue> ConvertFromBytes(byte[] dataFromDrive)
    {
        List<InfoBlockValue> InfoValue = new List<InfoBlockValue>();

        string[] allConvertedIntegers = new String[100];

        int lastReadByte = 0;
        int parameterIndex = 0;
        int parameterByteIndex = 0;

        for (parameterIndex = 0; parameterIndex < 99; parameterIndex++)
        {
            byte[] allBytesOfOneParameter = new byte[28];                                

            Array.Copy(dataFromDrive, lastReadByte + 1, allBytesOfOneParameter, 0, 28);

            InfoValue.Add(new InfoBlockValue(System.Text.Encoding.UTF8.GetString(allBytesOfOneParameter)));

            parameterByteIndex = 0;
            lastReadByte += 29;
        }

        return InfoValue;
    }

    public byte[] StringToByteArray(string str)
    {
        System.Text.ASCIIEncoding enc = new System.Text.ASCIIEncoding();
        return enc.GetBytes(str);
    }
}

This is the code where I use the operations:

public class ReadInfo
{
    private GetInfoBlock InfoBlock = null;

    public async Task<Tuple<string, InfoBlock>> ReadNamesBlock()
    {
        if (InfoBlock == null)
        {
            InfoBlock = new GetInfoBlock();
        }

        await ReadWriteAdapter.semaphore.WaitAsync();

        string returnString = string.Empty;

        uint writeTuple = await InfoBlock.Write();

        try
        {
            InfoBlock readTuple = await NamesBlock.Read();

            bool validationResult = readTuple.IsChecksumEqual;

            if (validationResult)
            {
                returnString += $"Checksum {BitConverter.ToString(readTuple.CalculatedCrcSum)} ReceivedCrcSum: {BitConverter.ToString(readTuple.ReceivedCrcSum)}";

                //await ValidateDataCRC.SendAck();

            }
            else
            {
                returnString += "Checksum error";

                await ValidateDataCRC.SendNack();
            }

            return new Tuple<string, InfoBlock>(returnString, readTuple);
        }
        catch (Exception ex)
        {
            string exception = $"Error while reading the parameternames from the device: {ex.Message}";

            return new Tuple<string, InfoBlock>(exception, null);
        }
        finally
        {
            NamesBlock = null;
            ReadWriteAdapter.semaphore.Release();
        }
    }
}

And the last one is my ReadWriteAdapter:

public class ReadWriteAdapter
{
    public static SemaphoreSlim semaphore = new SemaphoreSlim(1);

    private static readonly Object lockObject = new object();
    private static ReadWriteAdapter instance;

    public static ReadWriteAdapter Current
    {
        get
        {
            if (instance == null)
            {
                lock (lockObject)
                {
                    if (instance == null)
                    {
                        instance = new ReadWriteAdapter();
                    }
                }
            }
            return instance;
        }
    }


    private DataWriter dataWriter = null;
    private DataReader dataReader = null;

    private CancellationTokenSource ReadCancellationTokenSource;
    private SerialDevice serialPort = null;

    public bool IsDeviceInitialized()
    {
        return serialPort != null;
    }


    public async Task<string> Init()
    {
        try
        {
            string aqs = SerialDevice.GetDeviceSelector();

            DeviceInformationCollection devices = await DeviceInformation.FindAllAsync(aqs, null);

            if (devices.Any())
            {
                if (devices[0].Id.Contains("FTDI"))
                {
                    string deviceId = devices[0].Id;
                    await OpenPort(deviceId);
                }
                else
                {
                    string deviceId = devices[1].Id;
                    await OpenPort(deviceId);
                }

                ReadCancellationTokenSource = new CancellationTokenSource();

                dataWriter = new DataWriter(serialPort.OutputStream);
                dataReader = new DataReader(serialPort.InputStream);
                return "found port";
            }
            return "nodevices";
        }
        catch (Exception ex)
        {
            return ex.Message;
        }
    }

    private async Task OpenPort(string deviceId)
    {
        serialPort = await SerialDevice.FromIdAsync(deviceId);

        if (serialPort != null)
        {
            serialPort.WriteTimeout = TimeSpan.FromMilliseconds(500);
            serialPort.ReadTimeout = TimeSpan.FromMilliseconds(500);
            serialPort.BaudRate = 19200;
            serialPort.Parity = SerialParity.None;
            serialPort.StopBits = SerialStopBitCount.One;
            serialPort.DataBits = 8;
            serialPort.Handshake = SerialHandshake.None;
        }
    }


    private async Task<byte[]> ReadAsync(CancellationToken cancellationToken, uint ReadBufferLength)
    {
        Task<uint> loadAsyncTask;
        byte[] returnArray = new byte[ReadBufferLength];

        dataReader.InputStreamOptions = InputStreamOptions.Partial;

        loadAsyncTask = dataReader.LoadAsync(ReadBufferLength).AsTask(cancellationToken);   // Create a task object

        uint bytesRead = await loadAsyncTask;    // Launch the task and wait until buffer would be full

        if (bytesRead > 0)
        {
            dataReader.ReadBytes(returnArray);
        }

        return returnArray;
    }

    public async Task<uint> WriteAsync(byte[] data)
    {
        if (serialPort == null)
        {
            throw new ArgumentNullException("device");
        }
        if (dataWriter == null)
        {
            throw new ArgumentNullException("device");
        }

        if (data.Length != 0)
        {                
            dataWriter.WriteBytes(data);

            // Launch an async task to complete the write operation
            Task<uint> storeAsyncTask = dataWriter.StoreAsync().AsTask();

            return await storeAsyncTask;
        }
        else
        {
            return 0;
        }
    }

    public async Task<byte[]> Listen(uint BufferLength)
    {
        byte[] listen = new byte[BufferLength];

        try
        {
            if (serialPort != null)
            {
                dataReader = new DataReader(serialPort.InputStream);

                listen = await ReadAsync(ReadCancellationTokenSource.Token, BufferLength);
            }
        }
        catch (Exception ex)
        {
            throw ex;
        }
        finally
        {
            if (dataReader != null)    // Cleanup once complete
            {
                dataReader.DetachStream();
                dataReader = null;
            }
        }

        return listen;
    }
}

Solution

  • Found the answer myself...

    I had to dispose the dataReader after I used it. I guess the amount of streams I had on the reader just overflowed or whatever.

    Changed this in ReadWriteAdapter:

    finally
        {
            if (dataReader != null)    // Cleanup once complete
            {
                dataReader.DetachStream();
                dataReader = null;
            }
        }
    

    To this:

    finally
        {
            if (dataReader != null)    // Cleanup once complete
            {
                dataReader.DetachStream();
                dataReader.Dispose();  //Added this part
                dataReader = null;
            }
        }