-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathFSMonitor.cs
321 lines (280 loc) · 10.3 KB
/
FSMonitor.cs
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
using System;
using Microsoft.FlightSimulator.SimConnect;
using System.Runtime.InteropServices;
using System.Threading;
using System.Collections.Generic;
using System.ComponentModel;
using System.Runtime.CompilerServices;
using System.Json;
using System.Net;
using System.Net.Http;
using System.Text;
namespace FlightMonitor
{
public enum DEFINITION
{
Dummy = 0
};
public enum REQUEST
{
Dummy = 0,
Struct1
};
// String properties must be packed inside of a struct
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 1)]
struct Struct1
{
// this is how you declare a fixed size string
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)]
public String value;
// other definitions can be added to this struct
// ...
};
public class SimvarRequest
{
public DEFINITION Def = DEFINITION.Dummy;
public REQUEST Request = REQUEST.Dummy;
public string Name { get; set; }
public bool IsString = false;
public double NumValue = 0.0;
public string StrValue = "";
public string Units;
public bool Pending = true;
public string ShortName;
private static int _req = 0;
private static int _def = 0;
public SimvarRequest(String name, String shortName, String units)
{
this.Name = name;
this.ShortName = shortName;
this.Units = units;
this.Request = (REQUEST)(_req++);
this.Def = (DEFINITION)(_def++);
}
};
public class FSMonitor
{
private SimConnect _simConnect = null;
private Timer _timer;
public bool Connected
{
get {return _connected;}
}
private bool _connected;
/// User-defined win32 event
public const int WM_USER_SIMCONNECT = 0x0402;
private List<SimvarRequest> _simvarRequests;
public FSMonitor()
{
_connected = false;
}
public void Start()
{
InitRequests();
Connect();
}
public void Dispatch()
{
_simConnect?.ReceiveMessage();
}
private void InitRequests()
{
_simvarRequests = new List<SimvarRequest>
{
new SimvarRequest("FUEL TOTAL QUANTITY WEIGHT", "fuelWeight", "Pounds"),
new SimvarRequest("GROUND VELOCITY", "GS", "Knots"),
new SimvarRequest("AIRSPEED TRUE", "TAS", "Knots"),
new SimvarRequest("AIRSPEED INDICATED", "IAS", "Knots"),
new SimvarRequest("GPS FLIGHTPLAN TOTAL DISTANCE", "totalDistance", "Meters"),
new SimvarRequest("GPS ETE", "ETE", "Seconds"),
new SimvarRequest("PRESSURE ALTITUDE", "altitude", "Feet"),
new SimvarRequest("PLANE LATITUDE", "latitude", "Degrees"),
new SimvarRequest("PLANE LONGITUDE", "longitude", "Degrees"),
new SimvarRequest("PLANE HEADING DEGREES MAGNETIC", "headingMagnetic", "Degrees"),
new SimvarRequest("PLANE HEADING DEGREES TRUE", "headingTrue", "Degrees")
};
}
private void Connect()
{
while (_simConnect == null)
{
try
{
_simConnect = new SimConnect("FSMonitor", IntPtr.Zero, WM_USER_SIMCONNECT, null, 0);
_simConnect.OnRecvOpen += new SimConnect.RecvOpenEventHandler(OnRecvOpen);
_simConnect.OnRecvQuit += new SimConnect.RecvQuitEventHandler(OnRecvQuit);
_simConnect.OnRecvException += new SimConnect.RecvExceptionEventHandler(OnRecvException);
_simConnect.OnRecvSimobjectDataBytype += new SimConnect.RecvSimobjectDataBytypeEventHandler(OnRecvSimobjectDataBytype);
}
catch (COMException e)
{
if (_simConnect != null)
{
_simConnect.Dispose();
_simConnect = null;
}
}
Thread.Sleep(60 * 1000);
}
}
private void Disconnect()
{
if (_timer != null)
{
_timer.Dispose();
_timer = null;
}
if (_simConnect != null)
{
_simConnect.Dispose();
_simConnect = null;
}
_connected = false;
Console.WriteLine("Disconnected.");
}
private bool RegisterToSimConnect(SimvarRequest request)
{
if (_simConnect != null)
{
if (request.IsString)
{
/// Define a data structure containing string value
_simConnect.AddToDataDefinition(request.Def, request.Name, "", SIMCONNECT_DATATYPE.STRING256, 0.0f, SimConnect.SIMCONNECT_UNUSED);
/// IMPORTANT: Register it with the simconnect managed wrapper marshaller
/// If you skip this step, you will only receive a uint in the .dwData field.
_simConnect.RegisterDataDefineStruct<Struct1>(request.Def);
}
else
{
/// Define a data structure containing numerical value
_simConnect.AddToDataDefinition(request.Def, request.Name, request.Units, SIMCONNECT_DATATYPE.FLOAT64, 0.0f, SimConnect.SIMCONNECT_UNUSED);
/// IMPORTANT: Register it with the simconnect managed wrapper marshaller
/// If you skip this step, you will only receive a uint in the .dwData field.
_simConnect.RegisterDataDefineStruct<double>(request.Def);
}
return true;
}
else
{
return false;
}
}
private void OnRecvOpen(SimConnect sender, SIMCONNECT_RECV_OPEN data)
{
Console.WriteLine("Connected.");
_connected = true;
// Register pending requests
foreach (SimvarRequest request in _simvarRequests)
{
if (!RegisterToSimConnect(request))
{
Console.WriteLine("Request register failed: " + request.Name);
}
}
_timer = new Timer(OnTick, null, 3000, 3000);
}
/// The case where the user closes game
private void OnRecvQuit(SimConnect sender, SIMCONNECT_RECV data)
{
Console.WriteLine("Simulator quit.");
Disconnect();
// retry connecting
Thread.Sleep(60 * 1000);
Connect();
}
private void OnRecvException(SimConnect sender, SIMCONNECT_RECV_EXCEPTION data)
{
SIMCONNECT_EXCEPTION exception = (SIMCONNECT_EXCEPTION)data.dwException;
Console.WriteLine("SimConnect_OnRecvException: " + exception.ToString());
}
private void OnRecvSimobjectDataBytype(SimConnect sender, SIMCONNECT_RECV_SIMOBJECT_DATA_BYTYPE data)
{
uint requestId = data.dwRequestID;
uint objectId = data.dwObjectID;
//Console.WriteLine("SimConnect_OnRecvSimobjectDataBytype ID=" + requestId);
foreach (SimvarRequest request in _simvarRequests)
{
if (requestId == (uint)request.Request)
{
if (request.IsString)
{
Struct1 result = (Struct1)data.dwData[0];
request.NumValue = 0;
request.StrValue = result.value;
}
else
{
double value = (double)data.dwData[0];
request.NumValue = value;
request.StrValue = value.ToString("F9");
}
request.Pending = false;
}
}
if (AllReceived())
{
SendResult();
}
}
private void OnTick(object _)
{
//Console.WriteLine("OnTick: Requesting vars");
foreach (SimvarRequest request in _simvarRequests)
{
_simConnect?.RequestDataOnSimObjectType(request.Request, request.Def, 0, SIMCONNECT_SIMOBJECT_TYPE.USER);
request.Pending = true;
}
}
private bool AllReceived()
{
foreach (SimvarRequest request in _simvarRequests)
{
if (request.Pending)
{
return false;
}
}
return true;
}
double _lastFuel = 0;
DateTime _lastTime;
private void SendResult()
{
JsonObject json = new JsonObject();
DateTime now = DateTime.Now;
json["timestamp"] = now.ToString();
foreach (SimvarRequest request in _simvarRequests)
{
if (request.IsString)
{
json[request.ShortName] = request.StrValue;
} else
{
json[request.ShortName] = request.NumValue;
}
}
// calculate remaining distance
double distance = json["GS"] * json["ETE"] / 3600.0;
json["distance"] = distance;
// calculate fuel per hour
double fuelRate = 0;
double fuel = json["fuelWeight"];
if (_lastFuel > 0)
{
TimeSpan timeSpan = now - _lastTime;
fuelRate = (_lastFuel - fuel) * 3600 / timeSpan.TotalSeconds;
}
json["fuelPerHour"] = fuelRate;
_lastFuel = fuel;
_lastTime = now;
PostData(json.ToString());
}
private void PostData(string data)
{
var postData = new StringContent(data, Encoding.UTF8, "application/json");
const string url = "https://fm.bill.tt/";
HttpClient client = new HttpClient();
client.PostAsync(url, postData);
}
}
}