-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathx52.cpp
483 lines (451 loc) · 17.2 KB
/
x52.cpp
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
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
/*
Copyright (C) 2023 Csaba K Molnár
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#include "x52.h"
void X52::logg(std::string status, std::string func, std::string msg) {
std::time_t now_c = std::chrono::system_clock::to_time_t(std::chrono::system_clock::now());
m_log_file
<< std::put_time(std::localtime(&now_c), "%Y-%m-%d %H.%M.%S")
<< " [" << status << "]"
<< " (" << func << ")"
<< " " << msg
<< std::endl;
}
void X52::set_simconnect_handle(HANDLE handle) {
hSimConnect = handle;
}
void X52::set_wasimconnect_instance(WASimCommander::Client::WASimClient & client) {
wasimclient = &client;
}
void X52::set_x52HID(x52HID& instance) {
x52hid = &instance;
}
void X52::set_xmlfile(boost::property_tree::ptree* file) {
xml_file = file;
}
void X52::heartbeat() {
if (X52_RUN_TIME - HEARTBEAT_TIME > 60) {
// Restart command handler as this script has been inactive for over a minute
start_command_handler();
logg("INFO", "x52.cpp:" + std::to_string(__LINE__), "Command handler will be restarted");
}
if (X52_RUN_TIME - HEARTBEAT_TIME > 30 ) {
// Send a heartbeat to the command handler so it doesn't stop
m_cmd_file << "heartbeat" << std::endl;
HEARTBEAT_TIME = X52_RUN_TIME;
}
}
void X52::heartbeatReset() {
HEARTBEAT_TIME = X52_RUN_TIME;
}
void X52::write_to_mfd(std::string line1, std::string line2, std::string line3) {
if (MFD_ON_JOY[0] != line1) {
x52hid->setMFDTextLine(0, line1);
}
if (MFD_ON_JOY[1] != line2) {
x52hid->setMFDTextLine(1, line2);
}
if (MFD_ON_JOY[2] != line3) {
x52hid->setMFDTextLine(2, line3);
}
MFD_ON_JOY[0] = line1;
MFD_ON_JOY[1] = line2;
MFD_ON_JOY[2] = line3;
}
void X52::write_led(std::string led, std::string color) {
if (CURRENT_LED_COLOR.empty())
{
// Initially all leds are off
CURRENT_LED_COLOR = {
{ "fire", "" },
{ "a", "" },
{ "b", "" },
{ "d", "" },
{ "e", "" },
{ "t1", "" },
{ "t2", "" },
{ "t3", "" },
{ "pov", "" },
{ "clutch", "" },
{ "throttle", "" }
};
}
if (CURRENT_LED_COLOR[led] != color) {
if (x52hid->setLedColor(led, color))
{
CURRENT_LED_COLOR[led] = color;
}
}
}
void X52::update_led(std::string led, std::string light, std::string current_light, boost::property_tree::ptree &state, bool force) {
int mypos;
// Are there sequences defined in the XML file?
if (xml_file->count("sequences") != 0) {
for (boost::property_tree::ptree::value_type& v : xml_file->get_child("sequences"))
{
if (v.first == "sequence" && light == v.second.get<std::string>("<xmlattr>.name"))
{
// Found sequence tag with the name in the light variable
if (!state.empty())
{
// Count up time stamp with 1/speeds part of a second
if ( state.get<double>("<xmlattr>.timestamp",0.0) == 0.0 || // Is this the first time we encounter this sequence?
(X52_RUN_TIME - state.get<double>("<xmlattr>.timestamp",0.0)) > (1 / v.second.get<double>("<xmlattr>.speed")) ) // Is this the time to move to a next pos in the sequence?
{
// Time to move to next pos in sequence or first time.
if (v.second.get("<xmlattr>.loop","nil") == "nil")
{
v.second.put("<xmlattr>.loop", "0"); // Loop attribute in sequnce should default to 0 (repeat forever) if omitted
}
if (state.get("<xmlattr>.loop_count","nil") == "nil")
{
state.put("<xmlattr>.loop_count", 0); // First time
}
if (state.get("<xmlattr>.pos","nil") != "nil") // Position already has a value?
{
mypos = state.get<int>("<xmlattr>.pos");
mypos++;
state.put<int>("<xmlattr>.pos", mypos); // Move to next pos
}
else
{
state.put("<xmlattr>.pos", 1); // Start at first pos
}
// Have we went past the last position of the sequence?
if (state.get<int>("<xmlattr>.pos") > v.second.get<std::string>("<xmlattr>.pattern").length())
{
// End of pattern, move to first and increase loop-count
state.put("<xmlattr>.pos", 1);
state.put("<xmlattr>.loop_count", state.get<int>("<xmlattr>.loop_count") + 1);
}
// Are we looping forever or have we not exceeded maximum number of loops?
if (v.second.get<std::string>("<xmlattr>.loop") == "0" || state.get<int>("<xmlattr>.loop_count") < ( v.second.get<int>("<xmlattr>.loop") + 1 ) )
{
std::string b_light;
std::string pattern = v.second.get<std::string>("<xmlattr>.pattern").substr(state.get<int>("<xmlattr>.pos") - 1,1);
if (pattern == " ") b_light = "off";
else if (pattern == "a") b_light = "amber";
else if (pattern == "g") b_light = "green";
else if (pattern == "r") b_light = "red";
else if (pattern == "o") b_light = "on";
write_led(led, b_light);
if (state.get<double>("<xmlattr>.timestamp",0.0) == 0.0)
{
// If the states sequence has been non active, timestamp needs to be set for proper timing
state.put<double>("<xmlattr>.timestamp", X52_RUN_TIME);
}
else
{
state.put<double>("<xmlattr>.timestamp", state.get<double>("<xmlattr>.timestamp") + (1/v.second.get<double>("<xmlattr>.speed")));
}
}
}
}
return; // If matching sequence was found, no need to loop further
}
}
}
if (light != current_light || force) write_led(led, light);
}
bool X52::evaluate_xml_op(double simvarvalue, std::string op) {
std::string oper, value;
oper = op.substr(0, 2);
value = op.substr(2);
if (oper == "==") {
return(std::stod(value) == simvarvalue);
} else if(oper == "--") {
return(simvarvalue < std::stod(value));
} else if (oper == "++") {
return(simvarvalue > std::stod(value));
} else {
return false;
}
}
void X52::execute_button_press(boost::property_tree::ptree &xmltree, int btn) {
std::string type = xmltree.get<std::string>("<xmlattr>.type","");
if (type == "trigger_pos" ||
type == "hold" ||
type == "") {
if ( xmltree.get<std::string>("<xmlattr>.pressed", "") != "true") {
if (xmltree.get<std::string>("<xmlattr>.custom_command", "") != "") {
} else if (xmltree.get<std::string>("<xmlattr>.command", "") != "") {
int clienteventid = xmltree.get<int>("<xmlattr>.clienteventid", 0);
if (clienteventid == 0)
{
xmltree.put<int>("<xmlattr>.clienteventid",++lastClientEventId);
clienteventid = lastClientEventId;
SimConnect_MapClientEventToSimEvent(hSimConnect, clienteventid, xmltree.get<std::string>("<xmlattr>.command").c_str());
}
SimConnect_TransmitClientEvent(hSimConnect,
SIMCONNECT_OBJECT_ID_USER, // Invoke InputEvent on the user's aircraft
clienteventid, // Event_ID
xmltree.get<DWORD>("<xmlattr>.on", 0), // dwData - Optional data for the InputEvent
SIMCONNECT_GROUP_PRIORITY_HIGHEST, // GroupID - special case, we're not using a group, but a priority and we specify the "GroupID is a Priority" flag below
SIMCONNECT_EVENT_FLAG_GROUPID_IS_PRIORITY
);
SimConnect_GetLastSentPacketID(hSimConnect, &lastsentpacket.pdwSendID);
lastsentpacket.message = "TransmitClientEvent: EventName=" + xmltree.get<std::string>("<xmlattr>.command") + " Data=" + std::to_string(xmltree.get<double>("<xmlattr>.on", 0));
} else if (xmltree.get<std::string>("<xmlattr>.dataref", "") != "") {
std::string attr = xmltree.get<std::string>("<xmlattr>.dataref");
size_t separatorpos = attr.find("%");
std::string dataref = attr.substr(0, separatorpos);
std::string unit = attr.substr(separatorpos + 1);
SimConnect_ClearDataDefinition(hSimConnect, 10);
SimConnect_AddToDataDefinition(hSimConnect, 10, dataref.c_str(), unit.c_str(), SIMCONNECT_DATATYPE_FLOAT64);
SingleDataref datarefstruct;
datarefstruct.dataref[0] = xmltree.get<double>("<xmlattr>.on");
SimConnect_SetDataOnSimObject(hSimConnect,
10, // Definition ID
SIMCONNECT_OBJECT_ID_USER, // Set data on the user's aircraft
0, // Flags
0, // ArrayCount: Number of elements in the data array. A count of zero is interpreted as one element.
sizeof(datarefstruct), // size of each element in the data array in bytes
&datarefstruct );
} else if (xmltree.get<std::string>("<xmlattr>.calculator_code", "") != "") {
std::string calc_code = xmltree.get<std::string>("<xmlattr>.calculator_code");
double fResult = 0.;
std::string sResult {};
if (wasimclient->executeCalculatorCode(calc_code, WASimCommander::Enums::CalcResultType::Double, &fResult, &sResult) == S_OK) {
std::cout << "Calculator code " << quoted(calc_code) << " returned: " << fResult << " and " << quoted(sResult) << std::endl;
}
else {
std::cout << "Calculator code " << quoted(calc_code) << " could not be executed. Returned: " << fResult << " and " << quoted(sResult) << std::endl;
}
}
xmltree.put("<xmlattr>.pressed","true");
}
}
}
void X52::execute_button_release(boost::property_tree::ptree& xmltree, int btn) {
xmltree.put("<xmlattr>.press_time","");
xmltree.put("<xmlattr>.pressed","");
xmltree.put("<xmlattr>.in_repeat","");
}
bool X52::assignment_button_action(boost::property_tree::ptree &xmltree, int btn, std::string status) {
try
{
for (boost::property_tree::ptree::value_type &v : xmltree)
{
if (v.first == "button") // button tag (first level child of assignments)
{
if (status == "released" && btn == v.second.get<int>("<xmlattr>.nr")) {
if (!assignment_button_action(v.second, btn, "released")) { // If there is no shifted_button tag under this button tag then do the release process for this button tag.
execute_button_release(v.second, btn);
v.second.put("<xmlattr>.press_type","");
return true;
}
}
else if(status == "pressed" && btn == v.second.get<int>("<xmlattr>.nr")) {
if (v.second.get<std::string>("<xmlattr>.press_type","") == "normal" ||
!assignment_button_action(v.second, btn, "pressed")) { // If there is no shifted_button tag under this button tag then execute the command in this button tag.
execute_button_press(v.second, btn);
v.second.put("<xmlattr>.press_type","normal");
return true;
}
}
}
else if (v.first == "shifted_button") // shifted_button tag (child of button tag)
{
if (status == "pressed" && (CUR_SHIFT_STATE == v.second.get<std::string>("<xmlattr>.shift_state","") || v.second.get<std::string>("<xmlattr>.press_type","") == "shift")) {
execute_button_press(v.second, btn);
v.second.put("<xmlattr>.press_type","shift");
return true;
}
else if(status == "released" && v.second.get<std::string>("<xmlattr>.press_type") == "shift") {
execute_button_release(v.second, btn);
v.second.put("<xmlattr>.press_type","");
return true;
}
}
}
return false;
}
catch (const boost::property_tree::ptree_error&)
{
return false;
}
}
std::string X52::dataref_ind_action(std::string tagname, boost::property_tree::ptree &xmltree, std::string led, std::string current_light, bool force) {
std::string r;
boost::property_tree::ptree empty_ptree;
try
{
for (boost::property_tree::ptree::value_type& v : xmltree)
{
if (v.first == "led") // a led tag which contains multiple state tags
{
if (v.second.count("state") != 0) // This led has a state declared
{
r = dataref_ind_action(v.first, v.second, v.second.get<std::string>("<xmlattr>.id"), v.second.get<std::string>("<xmlattr>.current_light",""), force);
if (r == "") { // No state evaluates to true, set led to off
update_led(v.second.get<std::string>("<xmlattr>.id"), "off", v.second.get<std::string>("<xmlattr>.current_light"), v.second, force);
v.second.put("<xmlattr>.current_light", "off");
} else {
v.second.put("<xmlattr>.current_light", r);
}
} else {
// No declared states for led, set to off
if (v.second.get("<xmlattr>.current_light", "") != "off") {
update_led(v.second.get<std::string>("<xmlattr>.id"), "off", "", empty_ptree, force);
v.second.put("<xmlattr>.current_light", "off");
}
}
} else if (v.first == "state") { // a state tag below a led tag or below another state tag
r = dataref_ind_action(v.first, v.second, led, current_light, force);
if (r != "") { // Higher prio state was set, so reset this state's sequence timing and abort
xmltree.put("<xmlattr>.pos", 0);
xmltree.put("<xmlattr>.loop_count", 0);
xmltree.put<double>("<xmlattr>.timestamp", 0.0);
return r;
}
}
}
// No subitems to loop into, we're at bottom (innermost state)
if (tagname == "state") {
if (xmltree.get<double>("<xmlattr>.timestamp", 0.0) == 0.0 || force) // Reset sequence timing if this is first time or when forced to update
{
xmltree.put("<xmlattr>.pos", 89);
xmltree.put("<xmlattr>.loop_count", 0);
xmltree.put<double>("<xmlattr>.timestamp", 0.0);
}
double fResult = 0.;
std::string attr, dataref, unit;
size_t separatorpos;
attr = xmltree.get<std::string>("<xmlattr>.dataref");
separatorpos = attr.find("%");
dataref = attr.substr(0, separatorpos);
unit = attr.substr(separatorpos + 1);
// TODO Separate index from dataref name
wasimclient->getVariable(WASimCommander::Client::VariableRequest(dataref, unit, 0), &fResult);
// Evaluates dataref with op from the xml file.
if (evaluate_xml_op(fResult, xmltree.get<std::string>("<xmlattr>.op"))) {
update_led(led, xmltree.get<std::string>("<xmlattr>.light"), current_light, xmltree, force);
return xmltree.get<std::string>("<xmlattr>.light");
} else {
// Dataref doesn't evaluate
xmltree.put("<xmlattr>.pos", 0);
xmltree.put("<xmlattr>.loop_count", 0);
xmltree.put<double>("<xmlattr>.timestamp", 0.0);
return "";
}
}
}
catch (const boost::property_tree::ptree_error&)
{
return "";
}
return "";
}
void X52::all_on(std::string id, bool on) {
if (on) { // On!
if (id == "led") {
dataref_ind_action("", xml_file->get_child("indicators"), "", "", true); // Force update for all defined leds
}
else
{
// X52.dataref_mfd_action(X52.ACTIVE_PAGE, true) // Force update for active page
}
}
else
{ // Off!
if (id == "led") {
write_led("fire" , "off");
write_led("a" , "off");
write_led("b" , "off");
write_led("d" , "off");
write_led("e" , "off");
write_led("t1" , "off");
write_led("t2" , "off");
write_led("t3" , "off");
write_led("pov" , "off");
write_led("clutch" , "off");
write_led("throttle", "off");
}
else
{
x52hid->clearMFDTextLine(0);
x52hid->clearMFDTextLine(1);
x52hid->clearMFDTextLine(2);
}
}
}
bool X52::shift_state_active(const boost::property_tree::ptree xmltree) {
for (const boost::property_tree::ptree::value_type &v : xmltree) { // Read all children of shift_states tag
if (v.first == "shift_state") // only process shift_state tags
{
// If the button in this shift_state tag is pressed, check the nested shift_state tag.
if (joybuttonstates[std::stoi(v.second.get<std::string>("<xmlattr>.button"))-1]) {
if (!shift_state_active(v.second)) {
// nested shift button is not pressed, so this state is the current active
// Is this newly found state different from the current one?
if (CUR_SHIFT_STATE != v.second.get<std::string>("<xmlattr>.name")) {
CUR_SHIFT_STATE = v.second.get<std::string>("<xmlattr>.name");
logg("INFO", "x52.cpp:" + std::to_string(__LINE__), "New shift state: " + CUR_SHIFT_STATE);
if(mfd_on) {
// X52.activate_page(X52.ACTIVE_PAGE)
}
x52hid->setShift("on");
}
}
return true;
}
}
}
return false;
}
void X52::shift_state_action(const boost::property_tree::ptree xml_file) {
try
{
if (!shift_state_active(xml_file.get_child("shift_states")) && !CUR_SHIFT_STATE.empty() )
{
x52hid->setShift("off");
CUR_SHIFT_STATE.clear();
logg("INFO", "x52.cpp:" + std::to_string(__LINE__), "Shift state was cleared.");
if (mfd_on) {
// X52.activate_page(X52.ACTIVE_PAGE)
}
}
}
catch (const boost::property_tree::ptree_error&)
{
}
}
void X52::start_command_handler() {
// Command handler file
m_cmd_file.open("cmds");
// Command handling script
logg("INFO", "x52.cpp:" + std::to_string(__LINE__), "Command handler started with: start \"X52LuaOut Command handler\" /min /low luajit.exe cmd_handler.lua cmds");
std::system("start \"X52LuaOut Command handler\" /min /low luajit.exe cmd_handler.lua cmds");
}
bool X52::construct_successful() {
if (m_cmd_file.is_open() &&
m_log_file.is_open()) {
return true;
}
return false;
}
X52::X52() {
// Open log file
m_log_file.open("x52msfsout_log.txt");
start_command_handler();
// Detect OS: https://stackoverflow.com/questions/5919996/
#if defined(__linux__) || defined(__APPLE__)
MAX_MFD_LEN = 16;
#elif _WIN32
MAX_MFD_LEN = 15;
#endif
}
X52::~X52() {
m_cmd_file.close();
m_log_file.close();
}