Projekt

Obecné

Profil

Stáhnout (13.3 KB) Statistiky
| Větev: | Tag: | Revize:
1 a9ef8967 Oto Šťáva
using System;
2
using System.Net.Sockets;
3
using System.Threading;
4
using System.IO;
5
using System.Linq;
6
using System.Text;
7 49e751db Oto Šťáva
using UnityEngine;
8 a9ef8967 Oto Šťáva
9
namespace DeltaRobotVr
10
{
11
    public readonly struct Single3
12
    {
13
        public float X { get; }
14
        public float Y { get; }
15
        public float Z { get; }
16
17
        public Single3(float x, float y, float z)
18
        {
19
            X = x;
20
            Y = y;
21
            Z = z;
22
        }
23
24
        public override string ToString() => "Single3[ " + X + " " + Y + " " + Z + " ]";
25
    }
26
27
    public sealed class Client
28
    {
29
        // singleton instance
30
        public static readonly Client Instance = new Client();
31
32
        // default server
33
        private const string DefaultServer = "127.0.0.1";
34
        private const int DefaultPort = 4242;
35
36
        // protocol constants
37
        private const UInt16 ProtocolMagicId = 0x3001;
38
        private const UInt16 ProtocolVersionId = 0x2002;
39
        private const UInt16 PingId = 0x3004;
40 6d9543aa Jakub Hejman
        private const UInt16 PongId = 0x3005;
41 a9ef8967 Oto Šťáva
        private const UInt16 EotId = 0xF006;
42
        private const UInt16 CurrentActuatorPositionId = 0x4003;
43 306eb3ca kovi
        private const UInt16 CurrentDirectionVectorId = 0x4007;
44
        private const UInt16 DesiredDirectionVectorId = 0x4008;
45
        private const UInt16 CurveId = 0xF009;
46 a9ef8967 Oto Šťáva
47
        private const UInt16 Size8 = 0x0000;
48
        private const UInt16 Size16 = 0x1000;
49
        private const UInt16 Size32 = 0x2000;
50
        private const UInt16 Size64 = 0x3000;
51
        private const UInt16 Size128 = 0x4000;
52
        private const UInt16 SizeVariable = 0xF000;
53
        
54
        private static readonly byte[] ProtocolMagicValue = Encoding.ASCII.GetBytes("DeltaRVr");
55
        private const UInt32 ProtocolVersionValue = 1;
56
57 306eb3ca kovi
        private const UInt32 PointLength = 12;
58
59 49e751db Oto Šťáva
        private const int ReconnectPollMillis = 200;
60
        private const int ReconnectTimeMillis = 3000;
61
62 a9ef8967 Oto Šťáva
        
63
        private Thread _thread;
64
        
65
        private readonly object _isRunningLock = new object();
66
        private bool _isRunning;
67
68
        private readonly object _actuatorPositionLock = new object();
69
        private Single3 _actuatorPosition;
70
71 306eb3ca kovi
        private readonly object _currentDirectionVectorLock = new object();
72
        private Single3 _currentDirectionVector;
73
74
        private readonly object _desiredDirectionVectorLock = new object();
75
        private Single3 _desiredDirectionVector;
76
77
        private readonly object _curveLock = new object();
78
        private Single3[] _curve;
79
        private long _curveCounter = 0;
80
81 a9ef8967 Oto Šťáva
        private volatile bool _isConnected;
82 8017b1ae kovi
        private volatile string _eotMsg = string.Empty;
83 a9ef8967 Oto Šťáva
84 49e751db Oto Šťáva
        private readonly object _reconnectTimeLock = new object();
85
        private DateTime _reconnectTime = DateTime.Now;
86
87 a9ef8967 Oto Šťáva
        static Client()
88
        {
89
        }
90
91
        private Client()
92
        {
93
        }
94
95
        public void Start()
96
        {
97
            IsRunning = true;
98 f4e5d893 Oto Šťáva
            _thread = new Thread(ThreadProcedure);
99 a9ef8967 Oto Šťáva
            _thread.Start();
100
        }
101
102
        public void Stop()
103
        {
104
            IsRunning = false;
105 f4e5d893 Oto Šťáva
            if (_thread != null)
106
            {
107
                _thread.Join();
108
                _thread = null;
109
            }
110 a9ef8967 Oto Šťáva
        }
111
112
        public string EotMsg
113
        {
114
            get => _eotMsg;
115
            private set => _eotMsg = value;
116
        }
117
118
        public bool IsConnected
119
        {
120
            get => _isConnected;
121
            private set => _isConnected = value;
122
        }
123
124
        public Single3 ActuatorPosition
125
        {
126
            get
127
            {
128
                lock (_actuatorPositionLock)
129
                {
130
                    return _actuatorPosition;
131
                }
132
            }
133
            private set
134
            {
135
                lock (_actuatorPositionLock)
136
                {
137
                    _actuatorPosition = value;
138
                }
139
            } 
140
        }
141
142 306eb3ca kovi
        public Single3 CurrentDirectionVector
143
        {
144
            get
145
            {
146
                lock (_currentDirectionVectorLock)
147
                {
148
                    return _currentDirectionVector;
149
                }
150
            }
151
            private set
152
            {
153
                lock (_currentDirectionVectorLock)
154
                {
155
                    _currentDirectionVector = value;
156
                }
157
            }
158
        }
159
160
        public Single3 DesiredDirectionVector
161
        {
162
            get
163
            {
164
                lock (_desiredDirectionVectorLock)
165
                {
166
                    return _desiredDirectionVector;
167
                }
168
            }
169
            private set
170
            {
171
                lock (_desiredDirectionVectorLock)
172
                {
173
                    _desiredDirectionVector = value;
174
                }
175
            }
176
        }
177
178
        public Single3[] Curve
179
        {
180
            get
181
            {
182
                lock (_curveLock)
183
                {
184
                    return _curve;
185
                }
186
            }
187
            private set
188
            {
189
                lock (_curveLock)
190
                {
191
                    _curve = value;
192
                }
193
            }
194
        }
195
196
        public long CurveCounter
197
        {
198
            get
199
            {
200
                lock (_curveLock)
201
                {
202
                    return _curveCounter;
203
                }
204
            }
205
            private set
206
            {
207
                lock (_curveLock)
208
                {
209
                    _curveCounter = value;
210
                }
211
            }
212
        }
213
214 a9ef8967 Oto Šťáva
        public bool IsRunning
215
        {
216
            get {
217
                lock (_isRunningLock)
218
                {
219
                    return _isRunning;
220
                }
221
            }
222
            private set
223
            {
224
                lock (_isRunningLock)
225
                {
226
                    _isRunning = value;
227
                }
228
            }
229
        }
230
231 49e751db Oto Šťáva
        public DateTime ReconnectTime
232
        {
233
            get
234
            {
235
                lock (_reconnectTimeLock)
236
                {
237
                    return _reconnectTime;
238
                }
239
            }
240
            private set
241
            {
242
                lock (_reconnectTimeLock)
243
                {
244
                    _reconnectTime = value;
245
                }
246
            }
247
        }
248
249 a9ef8967 Oto Šťáva
        private void ThreadProcedure()
250
        {
251 49e751db Oto Šťáva
            while (IsRunning)
252 a9ef8967 Oto Šťáva
            {
253
                try
254
                {
255 49e751db Oto Šťáva
                    using var client = new TcpClient(DefaultServer, DefaultPort);
256
                    using var stream = client.GetStream();
257
                    BinaryReader reader = new BinaryReader(stream);
258
                    BinaryWriter writer = new BinaryWriter(stream);
259
                    IsConnected = true;
260
261
                    SendProtocolPreamble(writer);
262
263
                    // main message reception loop
264 a9ef8967 Oto Šťáva
                    while (IsRunning && IsConnected)
265
                    {
266 49e751db Oto Šťáva
                        ReadMessages(reader, writer);
267 a9ef8967 Oto Šťáva
                    }
268
                }
269 49e751db Oto Šťáva
                catch (Exception e)
270 a9ef8967 Oto Šťáva
                {
271 49e751db Oto Šťáva
                    if (e is SocketException || e is IOException)
272
                    {
273
                        Debug.LogWarning($"Connection error:\n{e}");
274
                    }
275
                    else
276
                    {
277
                        Debug.LogError($"Exception in communication thread:\n{e}");
278
                    }
279 8017b1ae kovi
                }
280
                finally
281
                {
282
                    IsConnected = false;
283 49e751db Oto Šťáva
                }
284
                
285
                // wait before reconnection - short polls to prevent blocking if application is closed
286
                ReconnectTime = DateTime.Now.AddMilliseconds(ReconnectTimeMillis);
287
                while (IsRunning && DateTime.Now < ReconnectTime)
288
                {
289
                    Thread.Sleep(ReconnectPollMillis);
290 a9ef8967 Oto Šťáva
                }
291
            }
292 49e751db Oto Šťáva
        }
293
294
295
        private void ReadMessages(BinaryReader reader, BinaryWriter writer)
296
        {
297
            var messageIdentifier = reader.ReadUInt16();
298
            switch (messageIdentifier)
299 a9ef8967 Oto Šťáva
            {
300 49e751db Oto Šťáva
                case ProtocolMagicId:
301
                    ProcessProtocolMagic(reader);
302
                    break;
303
                case ProtocolVersionId:
304
                    ProcessProtocolVersion(reader);
305
                    break;
306
                case PingId:
307
                    ProcessPing(reader, writer);
308
                    break;
309
                case EotId:
310
                    ProcessEot(reader);
311
                    break;
312
                case CurrentActuatorPositionId:
313
                    ProcessCurrentActuatorPosition(reader);
314
                    break;
315
                case CurrentDirectionVectorId:
316
                    ProcessCurrentDirectionVector(reader);
317
                    break;
318
                case DesiredDirectionVectorId:
319
                    ProcessDesiredDirectionVector(reader);
320
                    break;
321
                case CurveId:
322
                    ProcessCurve(reader);
323
                    break;
324
                default: // unknown message
325
                    var sizeIdentifier = (UInt16) (messageIdentifier & 0xF000);
326
                    switch (sizeIdentifier)
327
                    {
328
                        case Size8:
329
                            Skip8(reader);
330
                            break;
331
                        case Size16:
332
                            Skip16(reader);
333
                            break;
334
                        case Size32:
335
                            Skip32(reader);
336
                            break;
337
                        case Size64:
338
                            Skip64(reader);
339
                            break;
340
                        case Size128:
341
                            Skip128(reader);
342
                            break;
343
                        case SizeVariable:
344
                            SkipVariableLength(reader);
345
                            break;
346
                    }
347
348
                    break;
349 a9ef8967 Oto Šťáva
            }
350
        }
351
352 49e751db Oto Šťáva
        private void SendProtocolPreamble(BinaryWriter writer)
353
        {
354
            writer.Write(ProtocolMagicId);
355
            writer.Write(ProtocolMagicValue);
356
            writer.Write(ProtocolVersionId);
357
            writer.Write(ProtocolVersionValue);
358
        }
359 a9ef8967 Oto Šťáva
360
        private void Skip8(BinaryReader reader)
361
        {
362
            reader.ReadByte();
363
        }
364
365
        private void Skip16(BinaryReader reader)
366
        {
367
            reader.ReadUInt16();
368
        }
369
370
        private void Skip32(BinaryReader reader)
371
        {
372
            reader.ReadUInt32();
373
        }
374
375
        private void Skip64(BinaryReader reader)
376
        {
377
            reader.ReadUInt64();
378
        }
379
380
        private void Skip128(BinaryReader reader)
381
        {
382
            reader.ReadUInt64();
383
            reader.ReadUInt64();
384
        }
385
386
        private void SkipVariableLength(BinaryReader reader)
387
        {
388
            UInt32 numberOfBytes = reader.ReadUInt32();
389
            reader.ReadBytes((int) numberOfBytes);
390
        }
391
392
        private void ProcessCurrentActuatorPosition(BinaryReader reader)
393
        {
394
            var x = reader.ReadSingle();
395
            var y = reader.ReadSingle();
396
            var z = reader.ReadSingle();
397
            reader.ReadSingle(); // skip unused
398
399
            ActuatorPosition = new Single3(x, y, z);
400
        }
401
402 306eb3ca kovi
        private void ProcessCurrentDirectionVector(BinaryReader reader)
403
        {
404
            var x = reader.ReadSingle();
405
            var y = reader.ReadSingle();
406
            var z = reader.ReadSingle();
407
            reader.ReadSingle(); // skip unused
408
409
            CurrentDirectionVector = new Single3(x, y, z);
410
        }
411
412
        private void ProcessDesiredDirectionVector(BinaryReader reader)
413
        {
414
            var x = reader.ReadSingle();
415
            var y = reader.ReadSingle();
416
            var z = reader.ReadSingle();
417
            reader.ReadSingle(); // skip unused
418
419
            DesiredDirectionVector = new Single3(x, y, z);
420
        }
421
422
        private void ProcessCurve(BinaryReader reader)
423
        {
424
            UInt32 numberOfBytes = reader.ReadUInt32();
425
            UInt32 numberOfPoints = numberOfBytes / PointLength;
426
            Single3[] curve = new Single3[numberOfPoints];
427
428
            for(int i = 0; i < numberOfPoints; i++)
429
            {
430 3c2987e2 Oto Šťáva
                var x = reader.ReadSingle();
431
                var y = reader.ReadSingle();
432
                var z = reader.ReadSingle();
433 306eb3ca kovi
                curve[i] = new Single3(x, y, z);
434
            }
435
436 3c2987e2 Oto Šťáva
            lock(_curveLock)
437
            {
438
                CurveCounter++;
439
                Curve = curve;
440 306eb3ca kovi
            }
441
        }
442
443 a9ef8967 Oto Šťáva
        private void ProcessEot(BinaryReader reader)
444
        {
445
            UInt32 numberOfBytes = reader.ReadUInt32();
446
            Byte[] buffer = reader.ReadBytes((int) numberOfBytes);
447
            EotMsg = Encoding.ASCII.GetString(buffer);
448
            IsConnected = false;
449
        }
450
451
        private void ProcessProtocolMagic(BinaryReader reader)
452
        {
453
            byte[] value = reader.ReadBytes(8);
454
            if (!value.SequenceEqual(ProtocolMagicValue))
455
            {
456 306eb3ca kovi
                IsConnected = false;
457 a9ef8967 Oto Šťáva
            }
458
        }
459
460
        private void ProcessProtocolVersion(BinaryReader reader)
461
        {
462
            UInt32 value = reader.ReadUInt32();
463
            if (value != ProtocolVersionValue)
464
            {
465 306eb3ca kovi
                IsConnected = false;
466 a9ef8967 Oto Šťáva
            }
467
        }
468
469
        private void ProcessPing(BinaryReader reader, BinaryWriter writer)
470
        {
471
            UInt64 pingValue = reader.ReadUInt64();
472 6d9543aa Jakub Hejman
473
            writer.Write(PongId);
474 a9ef8967 Oto Šťáva
            writer.Write(pingValue);
475
        }
476
    }
477
}