jefffhaynes / XBee

A .NET library for XBee wireless controllers
MIT License
41 stars 17 forks source link

EndOfStreamException when using in UWP app #4

Closed guyeeba closed 7 years ago

guyeeba commented 8 years ago

When I try to use this lib in a UWP app, it throws an EndOfStreamException at System.IO.BinaryReader.ReadByte() at BinarySerialization.Graph.ValueGraph.ValueValueNode.Deserialize(EndianAwareBinaryReader reader, SerializedType serializedType, Nullable1 length)`

The exception is thrown at the very beginning of the process, during FindAndOpen()... actually it happens the first time ReadByte() is called.

The same thing happens with an FTDI-based XBee board, and with a Leonardo-based serial passthrough device. The XBees are accessible from XCTU through both adapters. I tried with a simple S2, and an S2C Pro TH module, both set to 9600 baud, AO=1, and the result was the same: EndOfStreamException.

Do you have any idea what might be wrong?

jefffhaynes commented 8 years ago

It might be a side-effect of UWP but let me take a look tomorrow. I've been porting to .net core and should have something by the end of the week, which might help. Thanks

On Mon, Mar 21, 2016 at 10:40 AM -0700, "guyeeba" notifications@github.com wrote:

When I try to use this lib in a UWP app, it throws an EndOfStreamException

at System.IO.BinaryReader.ReadByte()

at BinarySerialization.Graph.ValueGraph.ValueValueNode.Deserialize(EndianAwareBinaryReader reader, SerializedType serializedType, Nullable1 length)`

The exception is thrown at the very beginning of the process, during FindAndOpen()... actually it happens the first time ReadByte() is called.

The same thing happens with an FTDI-based XBee board, and with a Leonardo-based serial passthrough device. The XBees are accessible from XCTU through both adapters. I tried with a simple S2, and an S2C Pro TH module, both set to 9600 baud, AO=1, and the result was the same: EndOfStreamException.

Do you have any idea what might be wrong?

— You are receiving this because you are subscribed to this thread. Reply to this email directly or view it on GitHub

jefffhaynes commented 8 years ago

Sorry, I'm still working on porting the underlying serializer to .net core. Hope to have something soon...

randomcapital commented 8 years ago

It's okay, thanks for your effors, it's really appreciated.

guyeeba commented 8 years ago

I managed to get closer to solution (once again, UWP app issue).

1) the default value of SerialDevice.DataBits is 7. Explicitly setting it to 8 makes the serial port work perfectly:

        public static async Task<XBeeController> FindAndOpen()
        {
            var controller = new XBeeController();

            string aqs = SerialDevice.GetDeviceSelector();
            var devices = await DeviceInformation.FindAllAsync(aqs);

            foreach (var device in devices)
            {
                try
                {
                    var serialDevice = await SerialDevice.FromIdAsync(device.Id);
                    if (serialDevice == null)
                        return null;
                    else
                    {
                        serialDevice.DataBits = 8;

                        await controller.OpenAsync(serialDevice);
                        return controller;
                    }
                }
                catch (InvalidOperationException)
                {
                }
                catch (UnauthorizedAccessException)
                {
                }
                catch (ArgumentOutOfRangeException)
                {
                }
                catch (ArgumentException)
                {
                }
                catch (TimeoutException)
                {
                }
                catch (IOException)
                {
                }
            }

            return null;
        }

2) The way SerialDevice works in UWP does not allow you to close the port, therefore the open-getHV-close-reopen logic you use in XBeeController.OpenAsync does not work (BinarySerializer is blocked in Stream.Read as the underlying port cannot be closed). Restructuring BinarySerializer to support CancellationTokens would be way too much work for me, so my workaround was to insert an additional Stream layer into the SerialDevice-FrameSerializer path (warning, ugly code, proof-of-concept only!):

SerialConnection.Open:

[...]
                        try
                        {
#if NETCORE
                            _serialReadStream = new ClosableInputStream(_serialPort.InputStream, cancellationToken);

                            Frame frame = _frameSerializer.Deserialize(_serialReadStream);
#else
                            Frame frame = _frameSerializer.Deserialize(_serialPort.BaseStream);
#endif
[...]

where ClosableInputStream is

    public class ClosableInputStream : Stream
    {
        public ClosableInputStream(IInputStream stream, CancellationToken cancellationToken)
        {
            Source = stream;
            _cancellationToken = cancellationToken;
            dataReaderObject = new DataReader(stream);
        }

        public IInputStream Source { get; }
        public CancellationToken _cancellationToken { get; }
        public DataReader dataReaderObject { get; }

        public override bool CanRead => true;
        public override bool CanSeek => false;
        public override bool CanWrite => false;
        public override long Length => 0;
        public override long Position
        {
            get
            { return 0; }
            set
            { }
        }

        public override void Flush()
        {
            throw new NotImplementedException();
        }

        public override int Read(byte[] buffer, int offset, int count)
        {
            _cancellationToken.ThrowIfCancellationRequested();

            dataReaderObject.InputStreamOptions = InputStreamOptions.Partial;

            try
            {
                Task<UInt32> loadAsyncTask = dataReaderObject.LoadAsync((uint)count).AsTask(_cancellationToken);

                loadAsyncTask.Wait(_cancellationToken);
                UInt32 bytesRead = loadAsyncTask.Result;

                if (bytesRead > 0)
                {
                    IBuffer buff = dataReaderObject.ReadBuffer((uint)count);
                    buff.CopyTo(0, buffer, offset, count);
                }

                return (int)bytesRead;
            }
            catch
            {
                throw;
            }
        }

        public override long Seek(long offset, SeekOrigin origin)
        {
            throw new NotImplementedException();
        }

        public override void SetLength(long value)
        {
            throw new NotImplementedException();
        }

        public override void Write(byte[] buffer, int offset, int count)
        {
            throw new NotImplementedException();
        }
    }

So it seems to implement (production-ready) support for UWP needs some restructuring of underlying libraries to support cancellation. Or do you have a better idea how to workaround this issue?

jefffhaynes commented 8 years ago

That's funny, I was just cursing myself a few days ago for going with the open-and-stay-open approach. Let me digest all this and get back to you. Thanks.

On Thu, Apr 21, 2016 at 3:02 AM, guyeeba notifications@github.com wrote:

I managed to get closer to solution (once again, UWP app issue).

1) the default value of SerialDevice.DataBits is 7. Explicitly setting it to 8 makes the serial port work perfectly:

    public static async Task<XBeeController> FindAndOpen()
    {
        var controller = new XBeeController();

        string aqs = SerialDevice.GetDeviceSelector();
        var devices = await DeviceInformation.FindAllAsync(aqs);

        foreach (var device in devices)
        {
            try
            {
                var serialDevice = await SerialDevice.FromIdAsync(device.Id);
                if (serialDevice == null)
                    return null;
                else
                {
                    serialDevice.DataBits = 8;

                    await controller.OpenAsync(serialDevice);
                    return controller;
                }
            }
            catch (InvalidOperationException)
            {
            }
            catch (UnauthorizedAccessException)
            {
            }
            catch (ArgumentOutOfRangeException)
            {
            }
            catch (ArgumentException)
            {
            }
            catch (TimeoutException)
            {
            }
            catch (IOException)
            {
            }
        }

        return null;
    }

2) The way SerialDevice works in UWP does not allow you to close the port, therefore the open-getHV-close-reopen logic you use in XBeeController.OpenAsync does not work (BinarySerializer is blocked in Stream.Read as the underlying port cannot be closed). Restructuring BinarySerializer to support CancellationTokens would be way too much work for me, so my workaround was to insert an additional Stream layer into the SerialDevice-FrameSerializer path (warning, ugly code, proof-of-concept only!):

SerialConnection.Open:

[...] try {

if NETCORE

                        _serialReadStream = new ClosableInputStream(_serialPort.InputStream, cancellationToken);
                        Frame frame = _frameSerializer.Deserialize(_serialReadStream);

else

                        Frame frame = _frameSerializer.Deserialize(_serialPort.BaseStream);

endif

[...]

where ClosableInputStream is

public class ClosableInputStream : Stream
{
    public ClosableInputStream(IInputStream stream, CancellationToken cancellationToken)
    {
        Source = stream;
        _cancellationToken = cancellationToken;
        dataReaderObject = new DataReader(stream);
    }

    public IInputStream Source { get; }
    public CancellationToken _cancellationToken { get; }
    public DataReader dataReaderObject { get; }

    public override bool CanRead => true;
    public override bool CanSeek => false;
    public override bool CanWrite => false;
    public override long Length => 0;
    public override long Position
    {
        get
        { return 0; }
        set
        { }
    }

    public override void Flush()
    {
        throw new NotImplementedException();
    }

    public override int Read(byte[] buffer, int offset, int count)
    {
        _cancellationToken.ThrowIfCancellationRequested();

        dataReaderObject.InputStreamOptions = InputStreamOptions.Partial;

        try
        {
            Task<UInt32> loadAsyncTask = dataReaderObject.LoadAsync((uint)count).AsTask(_cancellationToken);

            loadAsyncTask.Wait(_cancellationToken);
            UInt32 bytesRead = loadAsyncTask.Result;

            if (bytesRead > 0)
            {
                IBuffer buff = dataReaderObject.ReadBuffer((uint)count);
                buff.CopyTo(0, buffer, offset, count);
            }

            return (int)bytesRead;
        }
        catch
        {
            throw;
        }
    }

    public override long Seek(long offset, SeekOrigin origin)
    {
        throw new NotImplementedException();
    }

    public override void SetLength(long value)
    {
        throw new NotImplementedException();
    }

    public override void Write(byte[] buffer, int offset, int count)
    {
        throw new NotImplementedException();
    }
}

So it seems to implement (production-ready) support for UWP needs some restructuring of underlying libraries to support cancellation. Or do you have a better idea how to workaround this issue?

— You are receiving this because you commented. Reply to this email directly or view it on GitHub https://github.com/jefffhaynes/XBee/issues/4#issuecomment-212773392

If you want to build a ship, don't drum up people together to collect wood and don't assign them tasks and work, but rather teach them to long for the endless immensity of the sea.

Antoine de Saint-Exupery

jefffhaynes commented 8 years ago

If you get a chance take a look at my latest check-in and see if it solves anything. I need to load up your code I just haven't had time yet.

guyeeba commented 8 years ago

The latest check-in is not really UWP-aware, I'm going to mess with it in the evening.

I noticed that you added XBeeProS2C to HardwareVersion with value of 0x21. Well, actually I use XBee Pro S2C modules (through-hole mounting scheme), but my devices have hardware version 0x2D (or 0x2D41 to be exact). You might add them, too... :)

jefffhaynes commented 8 years ago

Yeah, I'm actually surprised you got it working on UWP at all. I didn't think BinarySerializer would work yet. I really need to devote more time to it.  Thanks for the hardware info. I'll add it. 

On Thu, Apr 21, 2016 at 11:33 PM -0700, "guyeeba" notifications@github.com wrote:

The latest check-in is not really UWP-aware, I'm going to mess with it in the evening.

I noticed that you added XBeeProS2C to HardwareVersion with value of 0x21. Well, actually I use XBee Pro S2C modules (through-hole mounting scheme), but my devices have hardware version 0x2D (or 0x2D41 to be exact). You might add them, too... :)

— You are receiving this because you commented. Reply to this email directly or view it on GitHub

alexiordan commented 7 years ago

Any news regarding UWP support?

jefffhaynes commented 7 years ago

Sorry, I've been working on other things. I'll try to take another whack at it over the holidays. 


I much prefer the sharpest criticism of a single intelligent man to the thoughtless approval of the masses - Johannes Kepler

On Sun, Dec 4, 2016 at 1:16 PM -0500, "Alex" notifications@github.com wrote:

Any news regarding UWP support?

— You are receiving this because you commented. Reply to this email directly, view it on GitHub, or mute the thread.

jefffhaynes commented 7 years ago

I'm making progress on the underlying serializer using VS 2017 RC. I should be able to have something before too long but there still seem to be some issues with .NET Standard in the RC.

alexiordan commented 7 years ago

Any updates on this?

jefffhaynes commented 7 years ago

No but the underlying serializer is now working on UWP. Today I pushed a version that supports async, which will make supporting xbee on UWP easier. I think I need to change to a dependency injection approach though so I can break out the serial device. I'll try to get to it soon.


I much prefer the sharpest criticism of a single intelligent man to the thoughtless approval of the masses - Johannes Kepler


From: Alex notifications@github.com Sent: Sunday, May 7, 2017 11:54:35 PM To: jefffhaynes/XBee Cc: Jeff Haynes; Comment Subject: Re: [jefffhaynes/XBee] EndOfStreamException when using in UWP app (#4)

Any updates on this?

— You are receiving this because you commented. Reply to this email directly, view it on GitHubhttps://github.com/jefffhaynes/XBee/issues/4#issuecomment-299767239, or mute the threadhttps://github.com/notifications/unsubscribe-auth/AJSKR0MiMqj1kwxM-TaUQSBRbx515bYCks5r3pH7gaJpZM4H1Yjo.

jefffhaynes commented 7 years ago

I'm struggling a little with the correct way to go about UWP support as they just released .net core 2.0 preview with serial support. The idea of doing everything as pure .net core is tempting but I don't know if (or when) that will translate to IoT core support, for example. Need to do some testing.

jefffhaynes commented 7 years ago

Talked to an MS guy and he said don't use SerialPort on IoT so I'm targeting SerialDevice. I have it somewhat working but the SerialDevice behavior seems even more unreliable than SerialPort so far...

jefffhaynes commented 7 years ago

If you want, give this a try. It will require some refactoring but look at the "core" branch for an example.

https://www.nuget.org/packages/XBee.Universal/

jefffhaynes commented 7 years ago

The 5.0+ library includes UWP support. Please let me know if you have any issues with it.