commit 49e751db82d394ec2c112031182a19c6cc42f980
Author: Oto Šťáva <oto.stava@gmail.com>
Date:   Wed May 12 08:53:59 2021 +0200

    Re #8900 - Implement client reconnection

diff --git a/deltarobot-vr/Assets/DeltaRobotVr/Client.cs b/deltarobot-vr/Assets/DeltaRobotVr/Client.cs
index 0851fdb..45cfe9f 100644
--- a/deltarobot-vr/Assets/DeltaRobotVr/Client.cs
+++ b/deltarobot-vr/Assets/DeltaRobotVr/Client.cs
@@ -4,6 +4,7 @@ using System.Threading;
 using System.IO;
 using System.Linq;
 using System.Text;
+using UnityEngine;
 
 namespace DeltaRobotVr
 {
@@ -55,6 +56,9 @@ namespace DeltaRobotVr
 
         private const UInt32 PointLength = 12;
 
+        private const int ReconnectPollMillis = 200;
+        private const int ReconnectTimeMillis = 3000;
+
         
         private Thread _thread;
         
@@ -77,6 +81,9 @@ namespace DeltaRobotVr
         private volatile bool _isConnected;
         private volatile string _eotMsg;
 
+        private readonly object _reconnectTimeLock = new object();
+        private DateTime _reconnectTime = DateTime.Now;
+
         static Client()
         {
         }
@@ -217,93 +224,130 @@ namespace DeltaRobotVr
             }
         }
 
+        public DateTime ReconnectTime
+        {
+            get
+            {
+                lock (_reconnectTimeLock)
+                {
+                    return _reconnectTime;
+                }
+            }
+            private set
+            {
+                lock (_reconnectTimeLock)
+                {
+                    _reconnectTime = value;
+                }
+            }
+        }
+
         private void ThreadProcedure()
         {
-            try
+            while (IsRunning)
             {
-                var client = new TcpClient(DefaultServer, DefaultPort);
-                var stream = client.GetStream();
-                BinaryReader reader = new BinaryReader(stream);
-                BinaryWriter writer = new BinaryWriter(stream);
-                IsConnected = true;
-                
-                writer.Write(ProtocolMagicId);
-                writer.Write(ProtocolMagicValue);
-                writer.Write(ProtocolVersionId);
-                writer.Write(ProtocolVersionValue);
-                
                 try
                 {
+                    using var client = new TcpClient(DefaultServer, DefaultPort);
+                    using var stream = client.GetStream();
+                    BinaryReader reader = new BinaryReader(stream);
+                    BinaryWriter writer = new BinaryWriter(stream);
+                    IsConnected = true;
+
+                    SendProtocolPreamble(writer);
+
+                    // main message reception loop
                     while (IsRunning && IsConnected)
                     {
-                        var messageIdentifier = reader.ReadUInt16();
-                        switch (messageIdentifier)
-                        {
-                            case ProtocolMagicId:
-                                ProcessProtocolMagic(reader);
-                                break;
-                            case ProtocolVersionId:
-                                ProcessProtocolVersion(reader);
-                                break;
-                            case PingId:
-                                ProcessPing(reader, writer);
-                                break;
-                            case EotId:
-                                ProcessEot(reader);
-                                break;
-                            case CurrentActuatorPositionId:
-                                ProcessCurrentActuatorPosition(reader);
-                                break;
-                            case CurrentDirectionVectorId:
-                                ProcessCurrentDirectionVector(reader);
-                                break;
-                            case DesiredDirectionVectorId:
-                                ProcessDesiredDirectionVector(reader);
-                                break;
-                            case CurveId:
-                                ProcessCurve(reader);
-                                break;
-                            default: //client tuto zpravu nezna
-                                var sizeIdentifier = (UInt16) (messageIdentifier & 0xF000);
-                                switch (sizeIdentifier)
-                                {
-                                    case Size8:
-                                        Skip8(reader);
-                                        break;
-                                    case Size16:
-                                        Skip16(reader);
-                                        break;
-                                    case Size32:
-                                        Skip32(reader);
-                                        break;
-                                    case Size64:
-                                        Skip64(reader);
-                                        break;
-                                    case Size128:
-                                        Skip128(reader);
-                                        break;
-                                    case SizeVariable:
-                                        SkipVariableLength(reader);
-                                        break;
-                                }
-
-                                break;
-                        }
+                        ReadMessages(reader, writer);
                     }
                 }
-                finally
+                catch (Exception e)
                 {
-                    stream.Close();
-                    client.Close();
-                    IsConnected = true;
+                    if (e is SocketException || e is IOException)
+                    {
+                        Debug.LogWarning($"Connection error:\n{e}");
+                    }
+                    else
+                    {
+                        Debug.LogError($"Exception in communication thread:\n{e}");
+                    }
+                }
+                
+                // wait before reconnection - short polls to prevent blocking if application is closed
+                ReconnectTime = DateTime.Now.AddMilliseconds(ReconnectTimeMillis);
+                while (IsRunning && DateTime.Now < ReconnectTime)
+                {
+                    Thread.Sleep(ReconnectPollMillis);
                 }
             }
-            catch (Exception)
+        }
+
+
+        private void ReadMessages(BinaryReader reader, BinaryWriter writer)
+        {
+            var messageIdentifier = reader.ReadUInt16();
+            switch (messageIdentifier)
             {
-                Console.WriteLine("failed to connect");
+                case ProtocolMagicId:
+                    ProcessProtocolMagic(reader);
+                    break;
+                case ProtocolVersionId:
+                    ProcessProtocolVersion(reader);
+                    break;
+                case PingId:
+                    ProcessPing(reader, writer);
+                    break;
+                case EotId:
+                    ProcessEot(reader);
+                    break;
+                case CurrentActuatorPositionId:
+                    ProcessCurrentActuatorPosition(reader);
+                    break;
+                case CurrentDirectionVectorId:
+                    ProcessCurrentDirectionVector(reader);
+                    break;
+                case DesiredDirectionVectorId:
+                    ProcessDesiredDirectionVector(reader);
+                    break;
+                case CurveId:
+                    ProcessCurve(reader);
+                    break;
+                default: // unknown message
+                    var sizeIdentifier = (UInt16) (messageIdentifier & 0xF000);
+                    switch (sizeIdentifier)
+                    {
+                        case Size8:
+                            Skip8(reader);
+                            break;
+                        case Size16:
+                            Skip16(reader);
+                            break;
+                        case Size32:
+                            Skip32(reader);
+                            break;
+                        case Size64:
+                            Skip64(reader);
+                            break;
+                        case Size128:
+                            Skip128(reader);
+                            break;
+                        case SizeVariable:
+                            SkipVariableLength(reader);
+                            break;
+                    }
+
+                    break;
             }
         }
 
+        private void SendProtocolPreamble(BinaryWriter writer)
+        {
+            writer.Write(ProtocolMagicId);
+            writer.Write(ProtocolMagicValue);
+            writer.Write(ProtocolVersionId);
+            writer.Write(ProtocolVersionValue);
+        }
 
         private void Skip8(BinaryReader reader)
         {
