Projekt

Obecné

Profil

Stáhnout (13.3 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
                catch (Exception e)
270
                {
271
                    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
                }
280
                finally
281
                {
282
                    IsConnected = false;
283
                }
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
                }
291
            }
292
        }
293

    
294

    
295
        private void ReadMessages(BinaryReader reader, BinaryWriter writer)
296
        {
297
            var messageIdentifier = reader.ReadUInt16();
298
            switch (messageIdentifier)
299
            {
300
                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
            }
350
        }
351

    
352
        private void SendProtocolPreamble(BinaryWriter writer)
353
        {
354
            writer.Write(ProtocolMagicId);
355
            writer.Write(ProtocolMagicValue);
356
            writer.Write(ProtocolVersionId);
357
            writer.Write(ProtocolVersionValue);
358
        }
359

    
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
        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
                var x = reader.ReadSingle();
431
                var y = reader.ReadSingle();
432
                var z = reader.ReadSingle();
433
                curve[i] = new Single3(x, y, z);
434
            }
435

    
436
            lock(_curveLock)
437
            {
438
                CurveCounter++;
439
                Curve = curve;
440
            }
441
        }
442

    
443
        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
                IsConnected = false;
457
            }
458
        }
459

    
460
        private void ProcessProtocolVersion(BinaryReader reader)
461
        {
462
            UInt32 value = reader.ReadUInt32();
463
            if (value != ProtocolVersionValue)
464
            {
465
                IsConnected = false;
466
            }
467
        }
468

    
469
        private void ProcessPing(BinaryReader reader, BinaryWriter writer)
470
        {
471
            UInt64 pingValue = reader.ReadUInt64();
472

    
473
            writer.Write(PongId);
474
            writer.Write(pingValue);
475
        }
476
    }
477
}
(5-5/18)