Projekt

Obecné

Profil

Stáhnout (13 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 sealed class Client
12
    {
13
        // singleton instance
14
        public static readonly Client Instance = new Client();
15

    
16
        // default server
17
        private const string DefaultServer = "127.0.0.1";
18
        private const int DefaultPort = 4242;
19

    
20
        // protocol constants
21
        private const UInt16 ProtocolMagicId = 0x3001;
22
        private const UInt16 ProtocolVersionId = 0x2002;
23
        private const UInt16 PingId = 0x3004;
24
        private const UInt16 PongId = 0x3005;
25
        private const UInt16 EotId = 0xF006;
26
        private const UInt16 CurrentActuatorPositionId = 0x4003;
27
        private const UInt16 CurrentDirectionVectorId = 0x4007;
28
        private const UInt16 DesiredDirectionVectorId = 0x4008;
29
        private const UInt16 CurveId = 0xF009;
30

    
31
        private const UInt16 Size8 = 0x0000;
32
        private const UInt16 Size16 = 0x1000;
33
        private const UInt16 Size32 = 0x2000;
34
        private const UInt16 Size64 = 0x3000;
35
        private const UInt16 Size128 = 0x4000;
36
        private const UInt16 SizeVariable = 0xF000;
37
        
38
        private static readonly byte[] ProtocolMagicValue = Encoding.ASCII.GetBytes("DeltaRVr");
39
        private const UInt32 ProtocolVersionValue = 1;
40

    
41
        private const UInt32 PointLength = 12;
42

    
43
        private const int ReconnectPollMillis = 200;
44
        private const int ReconnectTimeMillis = 3000;
45

    
46
        
47
        private Thread _thread;
48
        
49
        private readonly object _isRunningLock = new object();
50
        private bool _isRunning;
51

    
52
        private readonly object _actuatorPositionLock = new object();
53
        private Single3 _actuatorPosition;
54

    
55
        private readonly object _currentDirectionVectorLock = new object();
56
        private Single3 _currentDirectionVector;
57

    
58
        private readonly object _desiredDirectionVectorLock = new object();
59
        private Single3 _desiredDirectionVector;
60

    
61
        private readonly object _curveLock = new object();
62
        private Single3[] _curve;
63
        private long _curveCounter = 0;
64

    
65
        private volatile bool _isConnected;
66
        private volatile string _eotMsg = string.Empty;
67

    
68
        private readonly object _reconnectTimeLock = new object();
69
        private DateTime _reconnectTime = DateTime.Now;
70

    
71
        static Client()
72
        {
73
        }
74

    
75
        private Client()
76
        {
77
        }
78

    
79
        public void Start()
80
        {
81
            IsRunning = true;
82
            _thread = new Thread(ThreadProcedure);
83
            _thread.Start();
84
        }
85

    
86
        public void Stop()
87
        {
88
            IsRunning = false;
89
            if (_thread != null)
90
            {
91
                _thread.Join();
92
                _thread = null;
93
            }
94
        }
95

    
96
        public string EotMsg
97
        {
98
            get => _eotMsg;
99
            private set => _eotMsg = value;
100
        }
101

    
102
        public bool IsConnected
103
        {
104
            get => _isConnected;
105
            private set => _isConnected = value;
106
        }
107

    
108
        public Single3 ActuatorPosition
109
        {
110
            get
111
            {
112
                lock (_actuatorPositionLock)
113
                {
114
                    return _actuatorPosition;
115
                }
116
            }
117
            private set
118
            {
119
                lock (_actuatorPositionLock)
120
                {
121
                    _actuatorPosition = value;
122
                }
123
            } 
124
        }
125

    
126
        public Single3 CurrentDirectionVector
127
        {
128
            get
129
            {
130
                lock (_currentDirectionVectorLock)
131
                {
132
                    return _currentDirectionVector;
133
                }
134
            }
135
            private set
136
            {
137
                lock (_currentDirectionVectorLock)
138
                {
139
                    _currentDirectionVector = value;
140
                }
141
            }
142
        }
143

    
144
        public Single3 DesiredDirectionVector
145
        {
146
            get
147
            {
148
                lock (_desiredDirectionVectorLock)
149
                {
150
                    return _desiredDirectionVector;
151
                }
152
            }
153
            private set
154
            {
155
                lock (_desiredDirectionVectorLock)
156
                {
157
                    _desiredDirectionVector = value;
158
                }
159
            }
160
        }
161

    
162
        public Single3[] Curve
163
        {
164
            get
165
            {
166
                lock (_curveLock)
167
                {
168
                    return _curve;
169
                }
170
            }
171
            private set
172
            {
173
                lock (_curveLock)
174
                {
175
                    _curve = value;
176
                }
177
            }
178
        }
179

    
180
        public long CurveCounter
181
        {
182
            get
183
            {
184
                lock (_curveLock)
185
                {
186
                    return _curveCounter;
187
                }
188
            }
189
            private set
190
            {
191
                lock (_curveLock)
192
                {
193
                    _curveCounter = value;
194
                }
195
            }
196
        }
197

    
198
        public bool IsRunning
199
        {
200
            get {
201
                lock (_isRunningLock)
202
                {
203
                    return _isRunning;
204
                }
205
            }
206
            private set
207
            {
208
                lock (_isRunningLock)
209
                {
210
                    _isRunning = value;
211
                }
212
            }
213
        }
214

    
215
        public DateTime ReconnectTime
216
        {
217
            get
218
            {
219
                lock (_reconnectTimeLock)
220
                {
221
                    return _reconnectTime;
222
                }
223
            }
224
            private set
225
            {
226
                lock (_reconnectTimeLock)
227
                {
228
                    _reconnectTime = value;
229
                }
230
            }
231
        }
232

    
233
        private void ThreadProcedure()
234
        {
235
            while (IsRunning)
236
            {
237
                try
238
                {
239
                    using var client = new TcpClient(DefaultServer, DefaultPort);
240
                    using var stream = client.GetStream();
241
                    BinaryReader reader = new BinaryReader(stream);
242
                    BinaryWriter writer = new BinaryWriter(stream);
243
                    IsConnected = true;
244

    
245
                    SendProtocolPreamble(writer);
246

    
247
                    // main message reception loop
248
                    while (IsRunning && IsConnected)
249
                    {
250
                        ReadMessages(reader, writer);
251
                    }
252
                }
253
                catch (Exception e)
254
                {
255
                    if (e is SocketException || e is IOException)
256
                    {
257
                        Debug.LogWarning($"Connection error:\n{e}");
258
                    }
259
                    else
260
                    {
261
                        Debug.LogError($"Exception in communication thread:\n{e}");
262
                    }
263
                }
264
                finally
265
                {
266
                    IsConnected = false;
267
                }
268
                
269
                // wait before reconnection - short polls to prevent blocking if application is closed
270
                ReconnectTime = DateTime.Now.AddMilliseconds(ReconnectTimeMillis);
271
                while (IsRunning && DateTime.Now < ReconnectTime)
272
                {
273
                    Thread.Sleep(ReconnectPollMillis);
274
                }
275
            }
276
        }
277

    
278

    
279
        private void ReadMessages(BinaryReader reader, BinaryWriter writer)
280
        {
281
            var messageIdentifier = reader.ReadUInt16();
282
            switch (messageIdentifier)
283
            {
284
                case ProtocolMagicId:
285
                    ProcessProtocolMagic(reader);
286
                    break;
287
                case ProtocolVersionId:
288
                    ProcessProtocolVersion(reader);
289
                    break;
290
                case PingId:
291
                    ProcessPing(reader, writer);
292
                    break;
293
                case EotId:
294
                    ProcessEot(reader);
295
                    break;
296
                case CurrentActuatorPositionId:
297
                    ProcessCurrentActuatorPosition(reader);
298
                    break;
299
                case CurrentDirectionVectorId:
300
                    ProcessCurrentDirectionVector(reader);
301
                    break;
302
                case DesiredDirectionVectorId:
303
                    ProcessDesiredDirectionVector(reader);
304
                    break;
305
                case CurveId:
306
                    ProcessCurve(reader);
307
                    break;
308
                default: // unknown message
309
                    var sizeIdentifier = (UInt16) (messageIdentifier & 0xF000);
310
                    switch (sizeIdentifier)
311
                    {
312
                        case Size8:
313
                            Skip8(reader);
314
                            break;
315
                        case Size16:
316
                            Skip16(reader);
317
                            break;
318
                        case Size32:
319
                            Skip32(reader);
320
                            break;
321
                        case Size64:
322
                            Skip64(reader);
323
                            break;
324
                        case Size128:
325
                            Skip128(reader);
326
                            break;
327
                        case SizeVariable:
328
                            SkipVariableLength(reader);
329
                            break;
330
                    }
331

    
332
                    break;
333
            }
334
        }
335

    
336
        private void SendProtocolPreamble(BinaryWriter writer)
337
        {
338
            writer.Write(ProtocolMagicId);
339
            writer.Write(ProtocolMagicValue);
340
            writer.Write(ProtocolVersionId);
341
            writer.Write(ProtocolVersionValue);
342
        }
343

    
344
        private void Skip8(BinaryReader reader)
345
        {
346
            reader.ReadByte();
347
        }
348

    
349
        private void Skip16(BinaryReader reader)
350
        {
351
            reader.ReadUInt16();
352
        }
353

    
354
        private void Skip32(BinaryReader reader)
355
        {
356
            reader.ReadUInt32();
357
        }
358

    
359
        private void Skip64(BinaryReader reader)
360
        {
361
            reader.ReadUInt64();
362
        }
363

    
364
        private void Skip128(BinaryReader reader)
365
        {
366
            reader.ReadUInt64();
367
            reader.ReadUInt64();
368
        }
369

    
370
        private void SkipVariableLength(BinaryReader reader)
371
        {
372
            UInt32 numberOfBytes = reader.ReadUInt32();
373
            reader.ReadBytes((int) numberOfBytes);
374
        }
375

    
376
        private void ProcessCurrentActuatorPosition(BinaryReader reader)
377
        {
378
            var x = reader.ReadSingle();
379
            var y = reader.ReadSingle();
380
            var z = reader.ReadSingle();
381
            reader.ReadSingle(); // skip unused
382

    
383
            ActuatorPosition = new Single3(x, y, z);
384
        }
385

    
386
        private void ProcessCurrentDirectionVector(BinaryReader reader)
387
        {
388
            var x = reader.ReadSingle();
389
            var y = reader.ReadSingle();
390
            var z = reader.ReadSingle();
391
            reader.ReadSingle(); // skip unused
392

    
393
            CurrentDirectionVector = new Single3(x, y, z);
394
        }
395

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

    
403
            DesiredDirectionVector = new Single3(x, y, z);
404
        }
405

    
406
        private void ProcessCurve(BinaryReader reader)
407
        {
408
            UInt32 numberOfBytes = reader.ReadUInt32();
409
            UInt32 numberOfPoints = numberOfBytes / PointLength;
410
            Single3[] curve = new Single3[numberOfPoints];
411

    
412
            for(int i = 0; i < numberOfPoints; i++)
413
            {
414
                var x = reader.ReadSingle();
415
                var y = reader.ReadSingle();
416
                var z = reader.ReadSingle();
417
                curve[i] = new Single3(x, y, z);
418
            }
419

    
420
            lock(_curveLock)
421
            {
422
                CurveCounter++;
423
                Curve = curve;
424
            }
425
        }
426

    
427
        private void ProcessEot(BinaryReader reader)
428
        {
429
            UInt32 numberOfBytes = reader.ReadUInt32();
430
            Byte[] buffer = reader.ReadBytes((int) numberOfBytes);
431
            EotMsg = Encoding.ASCII.GetString(buffer);
432
            IsConnected = false;
433
        }
434

    
435
        private void ProcessProtocolMagic(BinaryReader reader)
436
        {
437
            byte[] value = reader.ReadBytes(8);
438
            if (!value.SequenceEqual(ProtocolMagicValue))
439
            {
440
                IsConnected = false;
441
            }
442
        }
443

    
444
        private void ProcessProtocolVersion(BinaryReader reader)
445
        {
446
            UInt32 value = reader.ReadUInt32();
447
            if (value != ProtocolVersionValue)
448
            {
449
                IsConnected = false;
450
            }
451
        }
452

    
453
        private void ProcessPing(BinaryReader reader, BinaryWriter writer)
454
        {
455
            UInt64 pingValue = reader.ReadUInt64();
456

    
457
            writer.Write(PongId);
458
            writer.Write(pingValue);
459
        }
460
    }
461
}
(1-1/7)