forked from Fragtality/DynamicLOD
-
Notifications
You must be signed in to change notification settings - Fork 1
/
MobiSimConnect.cs.orig
459 lines (406 loc) · 17.7 KB
/
MobiSimConnect.cs.orig
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
using Microsoft.FlightSimulator.SimConnect;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Threading;
namespace DynamicLOD_ResetEdition
{
public class MobiSimConnect : IDisposable
{
public const string MOBIFLIGHT_CLIENT_DATA_NAME_COMMAND = "MobiFlight.Command";
public const string MOBIFLIGHT_CLIENT_DATA_NAME_RESPONSE = "MobiFlight.Response";
public const uint MOBIFLIGHT_MESSAGE_SIZE = 1024;
public const uint WM_PILOTSDECK_SIMCONNECT = 0x1989;
public const string CLIENT_NAME = "DynamicLOD_ResetEdition";
public const string PILOTSDECK_CLIENT_DATA_NAME_SIMVAR = $"{CLIENT_NAME}.LVars";
public const string PILOTSDECK_CLIENT_DATA_NAME_COMMAND = $"{CLIENT_NAME}.Command";
public const string PILOTSDECK_CLIENT_DATA_NAME_RESPONSE = $"{CLIENT_NAME}.Response";
protected SimConnect simConnect = null;
protected IntPtr simConnectHandle = IntPtr.Zero;
protected Thread simConnectThread = null;
private static bool cancelThread = false;
protected bool isSimConnected = false;
protected bool isMobiConnected = false;
protected bool isReceiveRunning = false;
public bool IsConnected { get { return isSimConnected && isMobiConnected; } }
public bool IsReady { get { return IsConnected && isReceiveRunning; } }
public bool SimIsPaused { get; private set; }
public bool SimIsRunning { get; private set; }
private const int fpsLen = 60;
private float[] fpsStatistic;
private int fpsIndex;
protected uint nextID = 1;
protected const int reorderTreshold = 150;
protected Dictionary<string, uint> addressToIndex = new();
protected Dictionary<uint, float> simVars = new();
public MobiSimConnect()
{
fpsIndex = -1;
fpsStatistic = new float[fpsLen];
}
public float GetAverageFPS()
{
if (fpsIndex == -1)
return 0;
else
{
return fpsStatistic.Sum() / fpsLen;
}
}
public bool Connect()
{
try
{
if (isSimConnected)
return true;
simConnect = new SimConnect(CLIENT_NAME, simConnectHandle, WM_PILOTSDECK_SIMCONNECT, null, 0);
simConnect.OnRecvOpen += new SimConnect.RecvOpenEventHandler(SimConnect_OnOpen);
simConnect.OnRecvQuit += new SimConnect.RecvQuitEventHandler(SimConnect_OnQuit);
simConnect.OnRecvException += new SimConnect.RecvExceptionEventHandler(SimConnect_OnException);
cancelThread = false;
simConnectThread = new(new ThreadStart(SimConnect_ReceiveThread))
{
IsBackground = true
};
simConnectHandle = new IntPtr(simConnectThread.ManagedThreadId);
simConnectThread.Start();
Logger.Log(LogLevel.Information, "MobiSimConnect:Connect", $"SimConnect Connection open");
return true;
}
catch (Exception ex)
{
simConnectThread = null;
simConnectHandle = IntPtr.Zero;
cancelThread = true;
simConnect = null;
Logger.Log(LogLevel.Error, "MobiSimConnect:Connect", $"Exception while opening SimConnect! (Exception: {ex.GetType()} {ex.Message})");
}
return false;
}
protected void SimConnect_OnOpen(SimConnect sender, SIMCONNECT_RECV_OPEN data)
{
try
{
isSimConnected = true;
simConnect.OnRecvClientData += new SimConnect.RecvClientDataEventHandler(SimConnect_OnClientData);
simConnect.OnRecvEvent += new SimConnect.RecvEventEventHandler(SimConnect_OnReceiveEvent);
simConnect.OnRecvEventFrame += new SimConnect.RecvEventFrameEventHandler(Simconnect_OnRecvEventFrame);
CreateDataAreaDefaultChannel();
CreateEventSubscription();
Logger.Log(LogLevel.Information, "MobiSimConnect:SimConnect_OnOpen", $"SimConnect OnOpen received");
}
catch (Exception ex)
{
Logger.Log(LogLevel.Error, "MobiSimConnect:SimConnect_OnOpen", $"Exception during SimConnect OnOpen! (Exception: {ex.GetType()} {ex.Message})");
}
}
protected void SimConnect_ReceiveThread()
{
ulong ticks = 0;
int delay = 100;
int repeat = 5000 / delay;
int errors = 0;
isReceiveRunning = true;
while (!cancelThread && simConnect != null && isReceiveRunning)
{
try
{
simConnect.ReceiveMessage();
if (isSimConnected && !isMobiConnected && ticks % (ulong)repeat == 0)
{
Logger.Log(LogLevel.Debug, "MobiSimConnect:SimConnect_ReceiveThread", $"Sending Ping to MobiFlight WASM Module");
SendMobiWasmCmd("MF.DummyCmd");
SendMobiWasmCmd("MF.Ping");
}
}
catch (Exception ex)
{
errors++;
if (errors > 6)
{
isReceiveRunning = false;
Logger.Log(LogLevel.Error, "MobiSimConnect:SimConnect_ReceiveThread", $"Maximum Errors reached, closing Receive Thread! (Exception: {ex.GetType()})");
return;
}
}
Thread.Sleep(delay);
ticks++;
}
isReceiveRunning = false;
return;
}
protected void CreateEventSubscription()
{
//simConnect.MapClientEventToSimEvent(SIM_EVENTS.SIM, "SIM");
//simConnect.AddClientEventToNotificationGroup(NOTFIY_GROUP.GROUP0, SIM_EVENTS.SIM, false);
//simConnect.MapClientEventToSimEvent(SIM_EVENTS.PAUSE, "PAUSE");
//simConnect.AddClientEventToNotificationGroup(NOTFIY_GROUP.GROUP0, SIM_EVENTS.PAUSE, false);
//simConnect.MapClientEventToSimEvent(SIM_EVENTS.FRAME, "FRAME");
//simConnect.AddClientEventToNotificationGroup(NOTFIY_GROUP.GROUP0, SIM_EVENTS.FRAME, false);
simConnect.SubscribeToSystemEvent(SIM_EVENTS.SIM, "sim");
simConnect.SubscribeToSystemEvent(SIM_EVENTS.PAUSE, "pause");
simConnect.SubscribeToSystemEvent(SIM_EVENTS.FRAME, "frame");
}
protected void CreateDataAreaDefaultChannel()
{
simConnect.MapClientDataNameToID(MOBIFLIGHT_CLIENT_DATA_NAME_COMMAND, MOBIFLIGHT_CLIENT_DATA_ID.MOBIFLIGHT_CMD);
simConnect.MapClientDataNameToID(MOBIFLIGHT_CLIENT_DATA_NAME_RESPONSE, MOBIFLIGHT_CLIENT_DATA_ID.MOBIFLIGHT_RESPONSE);
simConnect.AddToClientDataDefinition((SIMCONNECT_DEFINE_ID)0, 0, MOBIFLIGHT_MESSAGE_SIZE, 0, 0);
simConnect.RegisterStruct<SIMCONNECT_RECV_CLIENT_DATA, ResponseString>((SIMCONNECT_DEFINE_ID)0);
simConnect.RequestClientData(MOBIFLIGHT_CLIENT_DATA_ID.MOBIFLIGHT_RESPONSE,
(SIMCONNECT_REQUEST_ID)0,
(SIMCONNECT_DEFINE_ID)0,
SIMCONNECT_CLIENT_DATA_PERIOD.ON_SET,
SIMCONNECT_CLIENT_DATA_REQUEST_FLAG.CHANGED,
0,
0,
0);
}
protected void CreateDataAreaClientChannel()
{
simConnect.MapClientDataNameToID(PILOTSDECK_CLIENT_DATA_NAME_COMMAND, PILOTSDECK_CLIENT_DATA_ID.MOBIFLIGHT_CMD);
simConnect.MapClientDataNameToID(PILOTSDECK_CLIENT_DATA_NAME_RESPONSE, PILOTSDECK_CLIENT_DATA_ID.MOBIFLIGHT_RESPONSE);
simConnect.MapClientDataNameToID(PILOTSDECK_CLIENT_DATA_NAME_SIMVAR, PILOTSDECK_CLIENT_DATA_ID.MOBIFLIGHT_LVARS);
simConnect.AddToClientDataDefinition((SIMCONNECT_DEFINE_ID)0, 0, MOBIFLIGHT_MESSAGE_SIZE, 0, 0);
simConnect.RegisterStruct<SIMCONNECT_RECV_CLIENT_DATA, ResponseString>((SIMCONNECT_DEFINE_ID)0);
simConnect.RequestClientData(PILOTSDECK_CLIENT_DATA_ID.MOBIFLIGHT_RESPONSE,
(SIMCONNECT_REQUEST_ID)0,
(SIMCONNECT_DEFINE_ID)0,
SIMCONNECT_CLIENT_DATA_PERIOD.ON_SET,
SIMCONNECT_CLIENT_DATA_REQUEST_FLAG.CHANGED,
0,
0,
0);
}
protected void SimConnect_OnClientData(SimConnect sender, SIMCONNECT_RECV_CLIENT_DATA data)
{
try
{
if (data.dwRequestID == 0)
{
var request = (ResponseString)data.dwData[0];
if (request.Data == "MF.Pong")
{
if (!isMobiConnected)
{
Logger.Log(LogLevel.Information, "MobiSimConnect:SimConnect_OnClientData", $"MobiFlight WASM Ping acknowledged - opening Client Connection");
SendMobiWasmCmd($"MF.Clients.Add.{CLIENT_NAME}");
}
}
if (request.Data == $"MF.Clients.Add.{CLIENT_NAME}.Finished")
{
CreateDataAreaClientChannel();
isMobiConnected = true;
SendClientWasmCmd("MF.SimVars.Clear");
SendClientWasmCmd($"MF.Config.MAX_VARS_PER_FRAME.Set.{ServiceModel.MfLvarsPerFrame}");
Logger.Log(LogLevel.Information, "MobiSimConnect:SimConnect_OnClientData", $"MobiFlight WASM Client Connection opened");
}
}
else
{
var simData = (ClientDataValue)data.dwData[0];
if (simVars.ContainsKey(data.dwRequestID))
{
simVars[data.dwRequestID] = simData.data;
}
else
Logger.Log(LogLevel.Warning, "MobiSimConnect:SimConnect_OnClientData", $"The received ID '{data.dwRequestID}' is not subscribed! (Data: {data})");
}
}
catch (Exception ex)
{
Logger.Log(LogLevel.Error, "MobiSimConnect:SimConnect_OnClientData", $"Exception during SimConnect OnClientData! (Exception: {ex.GetType()}) (Data: {data})");
}
}
protected void SimConnect_OnQuit(SimConnect sender, SIMCONNECT_RECV data)
{
Disconnect();
}
public void Disconnect()
{
try
{
if (isMobiConnected)
SendClientWasmCmd("MF.SimVars.Clear");
cancelThread = true;
if (simConnectThread != null)
{
simConnectThread.Interrupt();
simConnectThread.Join(500);
simConnectThread = null;
}
if (simConnect != null)
{
simConnect.Dispose();
simConnect = null;
simConnectHandle = IntPtr.Zero;
}
isSimConnected = false;
isMobiConnected = false;
nextID = 1;
simVars.Clear();
addressToIndex.Clear();
Logger.Log(LogLevel.Information, "MobiSimConnect:Disconnect", $"SimConnect Connection closed");
}
catch (Exception ex)
{
Logger.Log(LogLevel.Error, "MobiSimConnect:Disconnect", $"Exception during disconnecting from SimConnect! (Exception: {ex.GetType()} {ex.Message})");
}
}
private void Simconnect_OnRecvEventFrame(SimConnect sender, SIMCONNECT_RECV_EVENT_FRAME recEvent)
{
if (SimIsRunning && !SimIsPaused && recEvent != null)
{
if (fpsIndex == -1)
{
fpsIndex = 1;
for (int i = 0; i < fpsStatistic.Length; i++)
{
fpsStatistic[i] = recEvent.fFrameRate;
}
}
else
{
fpsStatistic[fpsIndex] = recEvent.fFrameRate;
fpsIndex++;
if (fpsIndex >= fpsStatistic.Length)
fpsIndex = 0;
}
}
}
private void SimConnect_OnReceiveEvent(SimConnect sender, SIMCONNECT_RECV_EVENT recEvent)
{
if (recEvent != null)
{
if (recEvent.uEventID == (uint)SIM_EVENTS.PAUSE)
{
if (recEvent.dwData == 1)
SimIsPaused = true;
else
SimIsPaused = false;
}
else if (recEvent.uEventID == (uint)SIM_EVENTS.SIM)
{
if (recEvent.dwData == 1)
SimIsRunning = true;
else
SimIsRunning = false;
}
}
}
public void Dispose()
{
Disconnect();
GC.SuppressFinalize(this);
}
private void SendClientWasmCmd(string command)
{
SendWasmCmd(PILOTSDECK_CLIENT_DATA_ID.MOBIFLIGHT_CMD, (MOBIFLIGHT_CLIENT_DATA_ID)0, command);
}
private void SendClientWasmDummyCmd()
{
SendWasmCmd(PILOTSDECK_CLIENT_DATA_ID.MOBIFLIGHT_CMD, (MOBIFLIGHT_CLIENT_DATA_ID)0, "MF.DummyCmd");
}
private void SendMobiWasmCmd(string command)
{
SendWasmCmd(MOBIFLIGHT_CLIENT_DATA_ID.MOBIFLIGHT_CMD, (MOBIFLIGHT_CLIENT_DATA_ID)0, command);
}
private void SendWasmCmd(Enum cmdChannelId, Enum cmdId, string command)
{
simConnect.SetClientData(cmdChannelId, cmdId, SIMCONNECT_CLIENT_DATA_SET_FLAG.DEFAULT, 0, new ClientDataString(command));
}
protected void SimConnect_OnException(SimConnect sender, SIMCONNECT_RECV_EXCEPTION data)
{
if (data.dwException != 3 && data.dwException != 29)
Logger.Log(LogLevel.Error, "MobiSimConnect:SimConnect_OnException", $"Exception received: (Exception: {data.dwException})");
}
public void SubscribeLvar(string address)
{
SubscribeVariable($"(L:{address})");
}
public void SubscribeSimVar(string name, string unit)
{
SubscribeVariable($"(A:{name}, {unit})");
}
protected void SubscribeVariable(string address)
{
try
{
if (!addressToIndex.ContainsKey(address))
{
RegisterVariable(nextID, address);
simVars.Add(nextID, 0.0f);
addressToIndex.Add(address, nextID);
nextID++;
}
else
Logger.Log(LogLevel.Warning, "MobiSimConnect:SubscribeAddress", $"The Address '{address}' is already subscribed");
}
catch (Exception ex)
{
Logger.Log(LogLevel.Error, "MobiSimConnect:SubscribeAddress", $"Exception while subscribing SimVar '{address}'! (Exception: {ex.GetType()}) (Message: {ex.Message})");
}
}
protected void RegisterVariable(uint ID, string address)
{
simConnect.AddToClientDataDefinition(
(SIMCONNECT_DEFINE_ID)ID,
(ID - 1) * sizeof(float),
sizeof(float),
0,
0);
simConnect?.RegisterStruct<SIMCONNECT_RECV_CLIENT_DATA, ClientDataValue>((SIMCONNECT_DEFINE_ID)ID);
simConnect?.RequestClientData(
PILOTSDECK_CLIENT_DATA_ID.MOBIFLIGHT_LVARS,
(SIMCONNECT_REQUEST_ID)ID,
(SIMCONNECT_DEFINE_ID)ID,
SIMCONNECT_CLIENT_DATA_PERIOD.ON_SET,
SIMCONNECT_CLIENT_DATA_REQUEST_FLAG.CHANGED,
0,
0,
0
);
SendClientWasmCmd($"MF.SimVars.Add.{address}");
}
public void UnsubscribeAll()
{
try
{
SendClientWasmCmd("MF.SimVars.Clear");
nextID = 1;
simVars.Clear();
addressToIndex.Clear();
}
catch (Exception ex)
{
Logger.Log(LogLevel.Error, "MobiSimConnect:UnsubscribeAll", $"Exception while unsubscribing SimVars! (Exception: {ex.GetType()}) (Message: {ex.Message})");
}
}
public float ReadLvar(string address)
{
if (addressToIndex.TryGetValue($"(L:{address})", out uint index) && simVars.TryGetValue(index, out float value))
return value;
else
return 0;
}
public float ReadSimVar(string name, string unit)
{
string address = $"(A:{name}, {unit})";
if (addressToIndex.TryGetValue(address, out uint index) && simVars.TryGetValue(index, out float value))
return value;
else
return 0;
}
public void WriteLvar(string address, float value)
{
SendClientWasmCmd($"MF.SimVars.Set.{string.Format(new CultureInfo("en-US").NumberFormat, "{0:G}", value)} (>L:{address})");
SendClientWasmDummyCmd();
}
public void ExecuteCode(string code)
{
SendClientWasmCmd($"MF.SimVars.Set.{code}");
SendClientWasmDummyCmd();
}
}
}