0

I have an activity (with an ISurfaceTextureListener) with an async OnSurfaceTextureUpdated method in which I call the async SendPixel method:

public async void OnSurfaceTextureUpdated(SurfaceTexture surface)
{
    bool sendPixel = false;

    [...]

    if (Settings.Pixel.period >= 0)
    {
        if (stopWatch.ElapsedMilliseconds >= Settings.Pixel.nextSending)
        {
            if (Settings.Pixel.period > 0)
                Settings.Pixel.nextSending += Settings.Pixel.period * ((int)((stopWatch.ElapsedMilliseconds - Settings.Pixel.nextSending) / Settings.Pixel.period) + 1);
            sendPixel = true;
        }
    }

    [...]

    if (sendPixel)
        await SendPixel();
}

Here is the async SendPixel method:

async Task SendPixel()
{
    try
    {
        if (Ev3Messaging.IsConnected())
        {
            Color color = new Color(bm.GetPixel(Settings.Camera.viewport.X_VpToCv(Settings.Pixel.x), Settings.Camera.viewport.X_VpToCv(Settings.Pixel.y)));
            Ev3Messaging.AddNumberMessage("MC_Hue", color.GetHue());
            Ev3Messaging.AddNumberMessage("MC_Brightness", color.GetBrightness());
            Console.WriteLine("SendPixel");
            await Ev3Messaging.SendMessages();
            Console.WriteLine("SendPixelEnd");
        }
    }
    catch (Exception ex)
    {
        Console.WriteLine("Error SendPixel: " + ex.Message);
    }
}

The ISurfaceTextureListener reads a video stream, then the SendPixel method is called many times. The issue is that the console well displays "SendPixel", but doesn't display "SendPixelEnd", and no error is thrown. Afterwards, when I close the activity, every missing "SendPixelEnd" are displayed.

EDIT: The EV3Messaging.SendMessages sends the messages (by Bluetooth) added in the previous lines:

static List<byte> btMessage = new List<byte>();
static public async Task SendMessages()
{
    if (_socket != null)
        await _socket.OutputStream.WriteAsync(btMessage.ToArray(), 0, btMessage.Count);
}

Why does that happen and how can I solve that ?

Thanks in advance.

PS: Sorry for my poor English ^^

  • Without the code on `Ev3Messaging.SendMessages` is not possible to determine the cause. – Gusman Jul 31 '17 at 20:12
  • Oops, sorry, I am going to add it. – Antoine Martin Jul 31 '17 at 20:21
  • Well, it seems that the bluetooth stream is not working and when you close the app as the socket is closed all the operations end. Are you receiving on the connected device the messages? – Gusman Jul 31 '17 at 20:29
  • Only some of them are received (and I just realized that some "SendPixelEnd" are displayed between the "SendPixel"s, certainly associated with the received messages...). And when the activity is closed (not the app), the Bluetooth device receives all the sent messages. – Antoine Martin Jul 31 '17 at 20:47
  • For what I see OutputStream is not thread safe, may be if you are trying to send a message before the previous one has finished it hangs in some way the stream. To test it easily add a lock (an async lock, like this one: https://github.com/neosmart/AsyncLock) wherever you use the bt outputstream (using the same lock object of course), if in this way works I would create a queue to send messages instead of sending them directly to get the best performance. – Gusman Aug 01 '17 at 09:41
  • I made some tests... I didn't post the whole code, and I was using a System.Timers.Timer to read the input stream (thanks to the socket.InputStream.ReadAsync), and by removing this timer, it works well. Then how should I read the InputStream without disturbing the outputstream ? Maybe I should start a new topic about this question, or could you suggest me something ? Thank you for your attention. – Antoine Martin Aug 01 '17 at 10:29
  • 1
    Try to not to use the InputStream and OutputStream properties, retrieve those only once and use the retrieved instances everywhere, you don't know what the properties do under the hood, those may be constructing a new stream each time you call them. Also, don't use a timer to read, create a thread or task and read continuously until the connection has been closed. Reading from the input stream should not interfere on the output stream. – Gusman Aug 01 '17 at 11:35
  • I replaced the timer with a task, and the incoming messages are well received. But about the OutputStream instance, I tried to save the instance and use the saved instance, but the video stream (and the pixel sending) are freezing for some seconds, working for some seconds, freezing for some seconds, ... If I keep the given code, the main issue is still there... (replacing the InputStream system didn't fix the issue) – Antoine Martin Aug 01 '17 at 20:49
  • May be you have some problem on your receiver device, if I don't recall it wrong when you write to the BT socket the data must be acknowledged by the receiver, if the receiver isn't acknowledging it in a proper time then it may lock the output stream. If you don't need to ensure the data has been received before continuing your code you can use a BufferedOutputStream, in that way you will write to the stream and it will flush the data as it's consumed by the real OutputStream without locking your code. – Gusman Aug 02 '17 at 08:49
  • I am communicating with a Lego Mindstorms device (I hope you already heard of it ^^). It already exists an advanced class that sends Bluetooth message directly with the code "_socket.OutputStream.WriteAsync(data, 0, data.Length)", so it should works with this device... I still looked at the BufferedOutputStream class, but I didn't find it (I didn't manage to import it), and I don't understand how I should integrate it into the code... (Sorry for my lack of knowledge :-/). – Antoine Martin Aug 02 '17 at 10:28
  • The Bluetooth device seems to get the sent message, but also old messages which continue being sent, as if the smartphone thinks the messages are not received by the device (lack of acknowlegment ^^), and then sends them again and again... But the code which sends messages is the same as the one in the official class. – Antoine Martin Aug 02 '17 at 11:03
  • Yeah, got it ! I just have forgotten to clear the btMessage List each time a send the data... As a consequence, each time he sends data, he sends the old data again, and he sends more and more bytes, which brings this bad behaviour. Thanks for the tips, and for your attention again ^^. – Antoine Martin Aug 02 '17 at 12:22
  • Glad you found it. Complete your question (as the code you showed doesn't shows wehere you add the data) and add yourself the answer. – Gusman Aug 02 '17 at 12:26

1 Answers1

1

SOLVED: I have forgotten to clear the btMessage List each time I send the data, then this results in this strange behaviour. This issue may also be because of the bad way used to receive data from the Bluetooth device.

Then the SendMessages method becomes:

static public async Task SendMessages()
{
    if (_socket != null)
        await _socket.OutputStream.WriteAsync(btMessage.ToArray(), 0, btMessage.Count);
    btMessage.Clear();
}

since each time I add a message, the new frame is added at the end of the btMessage List.

Also, to get the incoming message, I use this code:

public static void InputMessagesManager(Action<BtMessage> incomingMessageEvent)
{
    _tokenSource = new CancellationTokenSource();
    Task t = Task.Factory.StartNew(async () =>
    {
        Stream inputStream = _socket.InputStream;

        while (!_tokenSource.IsCancellationRequested)
        {
            if (inputStream == null)
                inputStream = _socket.InputStream;
            if (inputStream != null && inputStream.CanRead)
            {
                await inputStream.ReadAsync(_sizeBuffer, 0, _sizeBuffer.Length);

                short size = (short)(_sizeBuffer[0] | _sizeBuffer[1] << 8);
                if (size != 0)
                {
                    byte[] frame = new byte[size];
                    await inputStream.ReadAsync(frame, 0, frame.Length);
                    BtMessage btMes = new BtMessage(frame);
                    incomingMessageEvent(btMes);
                    Console.WriteLine("Received: " + btMes.name + " -> " + btMes.getValueAsNumber());
                }
            }
        }
    }, _tokenSource2.Token, TaskCreationOptions.LongRunning, TaskScheduler.Current);
}