Projekt

Obecné

Profil

Stáhnout (13.2 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
        private volatile string _eotMsg;
83
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
                }
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 a9ef8967 Oto Šťáva
                }
287
            }
288 49e751db Oto Šťáva
        }
289
290
291
        private void ReadMessages(BinaryReader reader, BinaryWriter writer)
292
        {
293
            var messageIdentifier = reader.ReadUInt16();
294
            switch (messageIdentifier)
295 a9ef8967 Oto Šťáva
            {
296 49e751db Oto Šťáva
                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 a9ef8967 Oto Šťáva
            }
346
        }
347
348 49e751db Oto Šťáva
        private void SendProtocolPreamble(BinaryWriter writer)
349
        {
350
            writer.Write(ProtocolMagicId);
351
            writer.Write(ProtocolMagicValue);
352
            writer.Write(ProtocolVersionId);
353
            writer.Write(ProtocolVersionValue);
354
        }
355 a9ef8967 Oto Šťáva
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 306eb3ca kovi
        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 3c2987e2 Oto Šťáva
                var x = reader.ReadSingle();
427
                var y = reader.ReadSingle();
428
                var z = reader.ReadSingle();
429 306eb3ca kovi
                curve[i] = new Single3(x, y, z);
430
            }
431
432 3c2987e2 Oto Šťáva
            lock(_curveLock)
433
            {
434
                CurveCounter++;
435
                Curve = curve;
436 306eb3ca kovi
            }
437
        }
438
439 a9ef8967 Oto Šťáva
        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 306eb3ca kovi
                IsConnected = false;
453 a9ef8967 Oto Šťáva
            }
454
        }
455
456
        private void ProcessProtocolVersion(BinaryReader reader)
457
        {
458
            UInt32 value = reader.ReadUInt32();
459
            if (value != ProtocolVersionValue)
460
            {
461 306eb3ca kovi
                IsConnected = false;
462 a9ef8967 Oto Šťáva
            }
463
        }
464
465
        private void ProcessPing(BinaryReader reader, BinaryWriter writer)
466
        {
467
            UInt64 pingValue = reader.ReadUInt64();
468 6d9543aa Jakub Hejman
469
            writer.Write(PongId);
470 a9ef8967 Oto Šťáva
            writer.Write(pingValue);
471
        }
472
    }
473
}