Projekt

Obecné

Profil

Stáhnout (13.7 KB) Statistiky
| Větev: | Tag: | Revize:
1
using System;
2
using System.Net.Sockets;
3
using System.Threading;
4
using System.IO;
5
using System.Linq;
6
using System.Text;
7
using UnityEngine;
8

    
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
        private const UInt16 PongId = 0x3005;
41
        private const UInt16 EotId = 0xF006;
42
        private const UInt16 CurrentActuatorPositionId = 0x4003;
43
        private const UInt16 CurrentDirectionVectorId = 0x4007;
44
        private const UInt16 DesiredDirectionVectorId = 0x4008;
45
        private const UInt16 CurveId = 0xF009;
46

    
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
        private const UInt32 PointLength = 12;
58

    
59
        private const int ReconnectPollMillis = 200;
60
        private const int ReconnectTimeMillis = 3000;
61

    
62
        
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
        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
        private volatile bool _isConnected;
82
        private volatile string _eotMsg = string.Empty;
83

    
84
        private readonly object _reconnectTimeLock = new object();
85
        private DateTime _reconnectTime = DateTime.Now;
86

    
87
        static Client()
88
        {
89
        }
90

    
91
        private Client()
92
        {
93
        }
94

    
95
        public void Start()
96
        {
97
            IsRunning = true;
98
            _thread = new Thread(ThreadProcedure);
99
            _thread.Start();
100
        }
101

    
102
        public void Stop()
103
        {
104
            IsRunning = false;
105
            if (_thread != null)
106
            {
107
                _thread.Join();
108
                _thread = null;
109
            }
110
        }
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
        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
        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
        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
        private void ThreadProcedure()
250
        {
251
            while (IsRunning)
252
            {
253
                try
254
                {
255
                    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
                    while (IsRunning && IsConnected)
265
                    {
266
                        ReadMessages(reader, writer);
267
                    }
268

    
269
                    if (client.Connected)
270
                    {
271
                        SendEot(writer, "Client stopped by user");
272
                    }
273
                }
274
                catch (Exception e)
275
                {
276
                    if (e is SocketException || e is IOException)
277
                    {
278
                        Debug.LogWarning($"Connection error:\n{e}");
279
                    }
280
                    else
281
                    {
282
                        Debug.LogError($"Exception in communication thread:\n{e}");
283
                    }
284
                }
285
                finally
286
                {
287
                    IsConnected = false;
288
                }
289
                
290
                // wait before reconnection - short polls to prevent blocking if application is closed
291
                ReconnectTime = DateTime.Now.AddMilliseconds(ReconnectTimeMillis);
292
                while (IsRunning && DateTime.Now < ReconnectTime)
293
                {
294
                    Thread.Sleep(ReconnectPollMillis);
295
                }
296
            }
297
        }
298

    
299

    
300
        private void ReadMessages(BinaryReader reader, BinaryWriter writer)
301
        {
302
            var messageIdentifier = reader.ReadUInt16();
303
            switch (messageIdentifier)
304
            {
305
                case ProtocolMagicId:
306
                    ProcessProtocolMagic(reader);
307
                    break;
308
                case ProtocolVersionId:
309
                    ProcessProtocolVersion(reader);
310
                    break;
311
                case PingId:
312
                    ProcessPing(reader, writer);
313
                    break;
314
                case EotId:
315
                    ProcessEot(reader);
316
                    break;
317
                case CurrentActuatorPositionId:
318
                    ProcessCurrentActuatorPosition(reader);
319
                    break;
320
                case CurrentDirectionVectorId:
321
                    ProcessCurrentDirectionVector(reader);
322
                    break;
323
                case DesiredDirectionVectorId:
324
                    ProcessDesiredDirectionVector(reader);
325
                    break;
326
                case CurveId:
327
                    ProcessCurve(reader);
328
                    break;
329
                default: // unknown message
330
                    var sizeIdentifier = (UInt16) (messageIdentifier & 0xF000);
331
                    switch (sizeIdentifier)
332
                    {
333
                        case Size8:
334
                            Skip8(reader);
335
                            break;
336
                        case Size16:
337
                            Skip16(reader);
338
                            break;
339
                        case Size32:
340
                            Skip32(reader);
341
                            break;
342
                        case Size64:
343
                            Skip64(reader);
344
                            break;
345
                        case Size128:
346
                            Skip128(reader);
347
                            break;
348
                        case SizeVariable:
349
                            SkipVariableLength(reader);
350
                            break;
351
                    }
352

    
353
                    break;
354
            }
355
        }
356

    
357
        private void SendProtocolPreamble(BinaryWriter writer)
358
        {
359
            writer.Write(ProtocolMagicId);
360
            writer.Write(ProtocolMagicValue);
361
            writer.Write(ProtocolVersionId);
362
            writer.Write(ProtocolVersionValue);
363
        }
364

    
365
        private void Skip8(BinaryReader reader)
366
        {
367
            reader.ReadByte();
368
        }
369

    
370
        private void Skip16(BinaryReader reader)
371
        {
372
            reader.ReadUInt16();
373
        }
374

    
375
        private void Skip32(BinaryReader reader)
376
        {
377
            reader.ReadUInt32();
378
        }
379

    
380
        private void Skip64(BinaryReader reader)
381
        {
382
            reader.ReadUInt64();
383
        }
384

    
385
        private void Skip128(BinaryReader reader)
386
        {
387
            reader.ReadUInt64();
388
            reader.ReadUInt64();
389
        }
390

    
391
        private void SkipVariableLength(BinaryReader reader)
392
        {
393
            UInt32 numberOfBytes = reader.ReadUInt32();
394
            reader.ReadBytes((int) numberOfBytes);
395
        }
396

    
397
        private void ProcessCurrentActuatorPosition(BinaryReader reader)
398
        {
399
            var x = reader.ReadSingle();
400
            var y = reader.ReadSingle();
401
            var z = reader.ReadSingle();
402
            reader.ReadSingle(); // skip unused
403

    
404
            ActuatorPosition = new Single3(x, y, z);
405
        }
406

    
407
        private void ProcessCurrentDirectionVector(BinaryReader reader)
408
        {
409
            var x = reader.ReadSingle();
410
            var y = reader.ReadSingle();
411
            var z = reader.ReadSingle();
412
            reader.ReadSingle(); // skip unused
413

    
414
            CurrentDirectionVector = new Single3(x, y, z);
415
        }
416

    
417
        private void ProcessDesiredDirectionVector(BinaryReader reader)
418
        {
419
            var x = reader.ReadSingle();
420
            var y = reader.ReadSingle();
421
            var z = reader.ReadSingle();
422
            reader.ReadSingle(); // skip unused
423

    
424
            DesiredDirectionVector = new Single3(x, y, z);
425
        }
426

    
427
        private void ProcessCurve(BinaryReader reader)
428
        {
429
            UInt32 numberOfBytes = reader.ReadUInt32();
430
            UInt32 numberOfPoints = numberOfBytes / PointLength;
431
            Single3[] curve = new Single3[numberOfPoints];
432

    
433
            for(int i = 0; i < numberOfPoints; i++)
434
            {
435
                var x = reader.ReadSingle();
436
                var y = reader.ReadSingle();
437
                var z = reader.ReadSingle();
438
                curve[i] = new Single3(x, y, z);
439
            }
440

    
441
            lock(_curveLock)
442
            {
443
                CurveCounter++;
444
                Curve = curve;
445
            }
446
        }
447

    
448
        private void ProcessEot(BinaryReader reader)
449
        {
450
            UInt32 numberOfBytes = reader.ReadUInt32();
451
            Byte[] buffer = reader.ReadBytes((int) numberOfBytes);
452
            EotMsg = Encoding.ASCII.GetString(buffer);
453
            IsConnected = false;
454
        }
455

    
456
        private void ProcessProtocolMagic(BinaryReader reader)
457
        {
458
            byte[] value = reader.ReadBytes(8);
459
            if (!value.SequenceEqual(ProtocolMagicValue))
460
            {
461
                IsConnected = false;
462
            }
463
        }
464

    
465
        private void ProcessProtocolVersion(BinaryReader reader)
466
        {
467
            UInt32 value = reader.ReadUInt32();
468
            if (value != ProtocolVersionValue)
469
            {
470
                IsConnected = false;
471
            }
472
        }
473

    
474
        private void ProcessPing(BinaryReader reader, BinaryWriter writer)
475
        {
476
            UInt64 pingValue = reader.ReadUInt64();
477

    
478
            writer.Write(PongId);
479
            writer.Write(pingValue);
480
        }
481

    
482
        private void SendEot(BinaryWriter writer, string reason)
483
        {
484
            writer.Write(EotId);
485
            writer.Write((UInt32) reason.Length);
486
            writer.Write(Encoding.ASCII.GetBytes(reason));
487
        }
488
    }
489
}
(5-5/18)