Projekt

Obecné

Profil

Stáhnout (13.2 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;
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
                
281
                // wait before reconnection - short polls to prevent blocking if application is closed
282
                ReconnectTime = DateTime.Now.AddMilliseconds(ReconnectTimeMillis);
283
                while (IsRunning && DateTime.Now < ReconnectTime)
284
                {
285
                    Thread.Sleep(ReconnectPollMillis);
286
                }
287
            }
288
        }
289

    
290

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

    
344
                    break;
345
            }
346
        }
347

    
348
        private void SendProtocolPreamble(BinaryWriter writer)
349
        {
350
            writer.Write(ProtocolMagicId);
351
            writer.Write(ProtocolMagicValue);
352
            writer.Write(ProtocolVersionId);
353
            writer.Write(ProtocolVersionValue);
354
        }
355

    
356
        private void Skip8(BinaryReader reader)
357
        {
358
            reader.ReadByte();
359
        }
360

    
361
        private void Skip16(BinaryReader reader)
362
        {
363
            reader.ReadUInt16();
364
        }
365

    
366
        private void Skip32(BinaryReader reader)
367
        {
368
            reader.ReadUInt32();
369
        }
370

    
371
        private void Skip64(BinaryReader reader)
372
        {
373
            reader.ReadUInt64();
374
        }
375

    
376
        private void Skip128(BinaryReader reader)
377
        {
378
            reader.ReadUInt64();
379
            reader.ReadUInt64();
380
        }
381

    
382
        private void SkipVariableLength(BinaryReader reader)
383
        {
384
            UInt32 numberOfBytes = reader.ReadUInt32();
385
            reader.ReadBytes((int) numberOfBytes);
386
        }
387

    
388
        private void ProcessCurrentActuatorPosition(BinaryReader reader)
389
        {
390
            var x = reader.ReadSingle();
391
            var y = reader.ReadSingle();
392
            var z = reader.ReadSingle();
393
            reader.ReadSingle(); // skip unused
394

    
395
            ActuatorPosition = new Single3(x, y, z);
396
        }
397

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

    
405
            CurrentDirectionVector = new Single3(x, y, z);
406
        }
407

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

    
415
            DesiredDirectionVector = new Single3(x, y, z);
416
        }
417

    
418
        private void ProcessCurve(BinaryReader reader)
419
        {
420
            UInt32 numberOfBytes = reader.ReadUInt32();
421
            UInt32 numberOfPoints = numberOfBytes / PointLength;
422
            Single3[] curve = new Single3[numberOfPoints];
423

    
424
            for(int i = 0; i < numberOfPoints; i++)
425
            {
426
                var x = reader.ReadSingle();
427
                var y = reader.ReadSingle();
428
                var z = reader.ReadSingle();
429
                curve[i] = new Single3(x, y, z);
430
            }
431

    
432
            lock(_curveLock)
433
            {
434
                CurveCounter++;
435
                Curve = curve;
436
            }
437
        }
438

    
439
        private void ProcessEot(BinaryReader reader)
440
        {
441
            UInt32 numberOfBytes = reader.ReadUInt32();
442
            Byte[] buffer = reader.ReadBytes((int) numberOfBytes);
443
            EotMsg = Encoding.ASCII.GetString(buffer);
444
            IsConnected = false;
445
        }
446

    
447
        private void ProcessProtocolMagic(BinaryReader reader)
448
        {
449
            byte[] value = reader.ReadBytes(8);
450
            if (!value.SequenceEqual(ProtocolMagicValue))
451
            {
452
                IsConnected = false;
453
            }
454
        }
455

    
456
        private void ProcessProtocolVersion(BinaryReader reader)
457
        {
458
            UInt32 value = reader.ReadUInt32();
459
            if (value != ProtocolVersionValue)
460
            {
461
                IsConnected = false;
462
            }
463
        }
464

    
465
        private void ProcessPing(BinaryReader reader, BinaryWriter writer)
466
        {
467
            UInt64 pingValue = reader.ReadUInt64();
468

    
469
            writer.Write(PongId);
470
            writer.Write(pingValue);
471
        }
472
    }
473
}
(5-5/16)