Avionics
Dropship Simulator
ASE.cpp
Go to the documentation of this file.
1 #include "../../rapidxml-1.13/rapidxml.hpp"
2 
3 #include "Module.h"
4 
5 #include <stdio.h>
6 #include <curl/curl.h>
7 
8 #include "../Library/MD5.h"
9 
10 #include <io.h>
11 #include <fcntl.h>
12 #include <share.h>
13 #include <sys/stat.h>
14 
15 Ase::Ase(Bus* prmBus, Logger* prmLogger, int prmSoundDevice, SVocalware prmVocalware) : Module(prmBus)
16 {
17  bus = prmBus;
18  logger = prmLogger;
19  soundDevice = prmSoundDevice;
20  vocalware = prmVocalware;
21 }
22 
24 {
25  bass = prmBass;
26 }
27 
28 inline bool FileExists(const std::string name)
29 {
30  struct stat buffer;
31  return (stat(name.c_str(), &buffer) == 0);
32 }
33 
34 void Ase::FrameMove(float fElapsedTime)
35 {
36  if (!bass) return;
37 
39  for (UINT i = 0; i < bus->commandStream.size(); i++)
40  {
41  Command command = bus->commandStream.at(i);
42  if (command.delay != 0.0f) continue;
43 
44  if (command.name.substr(0, 10) == "Scenarios/")
45  {
47  LoadScenario(command.name);
48  currentStep = command.ivalue;
49 
50  char msg[99];
51  sprintf_s(msg, 99, "ASE:FrameMove Scenario %s loaded, step %i command 0", command.name.substr(10).c_str(), command.ivalue);
52  logger->Log(msg);
53 
54  bus->commandStream.erase(bus->commandStream.begin() + i);
55 
56  char randId[7];
57  static const char alphanum[] =
58  "346789"
59  "ABCDEFGHIJKLMNPQRTUVWXY";
60  for (i = 0; i < 6; i++)
61  randId[i] = alphanum[rand() % (sizeof(alphanum) - 1)];
62  randId[6] = 0;
63  bus->receiptNumber = randId;
64  }
65  else if (command.name == "EndScenario")
66  {
67  bus->ClearHelp();
68  currentStep = subStep = 0;
69  bus->waypoints.clear();
70  scenarioLoaded = false;
71  bus->commandStream.erase(bus->commandStream.begin() + i);
72  Command newcommand;
73  newcommand.name = "Viewport-MFD";
74  strcpy_s(newcommand.svalue, 64, "Nav");
75  bus->commandStream.push_back(newcommand);
76 
77  Command command3;
78  command3.name = "Display-Reset"; // reset
79  bus->commandStream.push_back(command3);
80 
81  Command command2;
82  command2.name = "Display-Event";
83  command2.ivalue = 2; // start music
84  bus->commandStream.push_back(command2);
85  }
86  else if (command.name == "ContinueScenario")
87  {
88  if (scenarioLoaded)
90  bus->commandStream.erase(bus->commandStream.begin() + i);
91  }
92  }
93 
94  if (!scenarioLoaded)
95  return;
96 
97  bus->ScenarioTimer += fElapsedTime;
98 
99  static float verticalStabilized = 0.0f;
100  float pitchdiff = fabsf(bus->AFCS.DesiredPitch - bus->PitchAttitude);
101  if (pitchdiff < D3DXToRadian(2.5f))
102  verticalStabilized += fElapsedTime;
103  else
104  verticalStabilized = 0.0f;
105 
106  // 2.5 - 0 is 2.5... 2.5-2.5 is 0... 2.5-5 is -2.5 clamped to 0
108  bus->ScenarioScore += max(2.5f - D3DXToDegree(pitchdiff), 0.0f) * 2.0f * fElapsedTime * bus->occupantPitchControlAuthority; // 5 per second max
109 
110  static float lateralStabilized = 0.0f;
111  float rolldiff = fabsf(bus->AFCS.DesiredRoll - bus->RollAttitude);
112  if (rolldiff < D3DXToRadian(5.0f))
113  lateralStabilized += fElapsedTime;
114  else
115  lateralStabilized = 0.0f;
116 
117  // 2.5 - 0 is 2.5... 2.5-2.5 is 0... 2.5-5 is -2.5 clamped to 0
119  bus->ScenarioScore += max(5.0f - D3DXToDegree(rolldiff), 0.0f) * fElapsedTime * bus->occupantRollControlAuthority; // 5 per second max
120 
121  // decrease what they can do the further it gets from where they should be... so 0/15 is 0
123  {
124  bus->occupantRollControlAuthority = max(0, 1.0f - rolldiff / D3DXToRadian(5.0f)) * occupantControlAuthority;
125  bus->occupantPitchControlAuthority = max(0, 1.0f - pitchdiff / D3DXToRadian(2.5f)) * occupantControlAuthority;
126  }
127  else
128  {
130  }
131 
132 
133  SCommand command = steps.at(currentStep).commands.at(subStep);
134 
136  if (!command.vmu.empty())
137  {
138  if (stream)
139  {
140  // forces it to wait until previous speech is finished (supports back to back VMU's)
142  return;
143  BASS_StreamFree(stream);
144  stream = 0;
145  }
146  std::string path;
147  if (command.voice == 0)
148  path = "Scenarios/Audio/" + md5(command.vmu) + ".mp3";
149  else
150  {
151  SVoice voice = vocalware.voices.at(command.voice);
152  std::string cs = voice.eid + voice.lid + voice.vid + command.vmu + voice.fxtype + voice.fxlevel;
153  path = "Scenarios/Audio/" + std::to_string(command.voice) + "/" + md5(cs) + ".mp3";
154  }
155  if (!FileExists(path))
156  {
157  GenerateTextToSpeech(command.vmu, command.voice, path);
158  }
159  stream = bass->PlayStream(path);
160  }
161  // something to do
162  else if (!command.name.empty())
163  {
164  if (command.name == "ModulateOccupantControlAuthority")
165  {
166  occupantControlAuthority *= command.fvalue;
167  char msg[99];
168  sprintf_s(msg, 99, "ASE:FrameMove Handling command: %s", command.name.c_str());
169  logger->Log(msg);
170  }
171  else if (command.name == "SetOccupantControlAuthority")
172  {
174  char msg[99];
175  sprintf_s(msg, 99, "ASE:FrameMove Handling command: %s", command.name.c_str());
176  logger->Log(msg);
177  }
178  else
179  {
180  // something to add to the command stream on the bus
181  Command busCommand;
182  busCommand.name = command.name;
183  memcpy(busCommand.svalue, command.svalue, 64); // covers whole union
184  bus->commandStream.push_back(busCommand);
185  char msg[99];
186  sprintf_s(msg, 99, "ASE:FrameMove Dispatching command: %s", command.name.c_str());
187  logger->Log(msg);
188  }
189  }
190  // help subsystem
191  else if (!command.help.empty())
192  {
193  // manipulating the help system
194  if (command.help == "Clear")
195  {
196  bus->ClearHelp();
197  }
198  else if (command.help == "Highlight")
199  {
200  bus->AddHighlight(command.element);
201  }
202  else if (command.help == "Dim")
203  {
204  bus->RemoveHighlight(command.element);
205  }
206  }
207  // wait for conditions
208  else if (!command.wait.empty())
209  {
210  // see if condition met, then continue
211  bool conditionMet = false;
212 
213  // we want both users to click the button at some point within the timeout period
214  for (UINT i = 0; i < bus->commandStream.size(); i++)
215  {
216  Command busCommand = bus->commandStream.at(i);
217  if (busCommand.delay != 0.0f) continue;
218 
219  if (command.wait == "BothPilotsPressedTrigger" && busCommand.name == "JoystickTrigger")
220  {
221  steps.at(currentStep).commands.at(subStep).conditionsMet |= (busCommand.ivalue + 1); // 1 and 2
222  bus->commandStream.erase(bus->commandStream.begin() + i);
223  if (steps.at(currentStep).commands.at(subStep).conditionsMet == 3)
224  conditionMet = true;
225  }
226  else if (command.wait == "EitherPilotPressedTrigger" && busCommand.name == "JoystickTrigger")
227  {
228  bus->commandStream.erase(bus->commandStream.begin() + i);
229  conditionMet = true;
230  }
231  }
232 
233  if (command.wait == "StabilizedFlight" && lateralStabilized >= 10.0f && verticalStabilized >= 10.0f)
234  conditionMet = true;
235  else if (command.wait == "WaypointsLeft" && bus->waypoints.size() == static_cast<UINT>(command.ivalue))
236  conditionMet = true;
237  else if (command.wait == "WaypointDistance" && bus->waypoints.size() > 0)
238  {
239  D3DXVECTOR3 diff3 = bus->Location - bus->waypoints.at(0).absoluteLocation();
240  D3DXVECTOR2 diff2 = D3DXVECTOR2(diff3.x, diff3.y);
241  float dist = D3DXVec2Length(&diff2);
242  if (dist <= command.fvalue)
243  conditionMet = true;
244  }
245  else if (command.wait == "ThrustSetting")
246  {
248  {
249  conditionMet = true;
250  }
251  else if (fabsf(bus->EngineThrustCommand[0] - command.fvalue) <= 0.03f && fabsf(bus->EngineThrustCommand[1] - command.fvalue) <= 0.03f) // 67 to 73
252  {
253  conditionMet = true;
254  }
255  }
256 
257  if (!conditionMet)
258  {
259  // see if timeout elapsed, then continue
260  steps.at(currentStep).commands.at(subStep).timeoutLeft -= fElapsedTime;
261  if (steps.at(currentStep).commands.at(subStep).timeoutLeft > 0)
262  {
263  return; // don't proceed to next step
264  }
265  else if (!steps.at(currentStep).commands.at(subStep).timeoutStep.empty())
266  {
267  currentStep = FindStep(steps.at(currentStep).commands.at(subStep).timeoutStep);
268  subStep = -1; // because we are about to increment it
269  }
270  }
271  }
272  // jump to another step
273  else if (!command.jump.empty())
274  {
275  currentStep = FindStep(command.jump);
276  subStep = -1; // because we are about to increment it
277  }
278 
280 }
281 
283 {
284  // advance to next step
285  subStep++;
286  if (static_cast<UINT>(subStep) >= steps.at(currentStep).commands.size())
287  {
288  subStep = 0;
289  currentStep++;
290  if (currentStep >= steps.size())
291  {
292  // we are done!
293  currentStep = 0;
294  scenarioLoaded = false;
295  logger->Log("ASE::AdvanceToNextStep Scenario completed!");
296  return;
297  }
298  }
299 
300  // restore the step's timeout, conditionMet
301  steps.at(currentStep).commands.at(subStep).timeoutLeft = steps.at(currentStep).commands.at(subStep).timeout;
302  steps.at(currentStep).commands.at(subStep).conditionsMet = 0;
303 
304  char msg[99];
305  sprintf_s(msg, 99, "ASE:AdvanceToNextStep Scenario step %i command %i", currentStep, subStep);
306  logger->Log(msg);
307 }
308 
310 size_t write_data(void* ptr, size_t size, size_t nmemb, FILE* stream)
311 {
312  size_t written = fwrite(ptr, size, nmemb, stream);
313  return written;
314 }
315 
316 bool Ase::GenerateTextToSpeech(std::string read, UINT voiceId, std::string outputPath)
317 {
318  CURL* curl = curl_easy_init();
319  if (curl)
320  {
321  FILE* fp;
322  if (fopen_s(&fp, outputPath.c_str(), "wb") != 0)
323  {
324  logger->Log("Ase::GenerateTextToSpeech fopen failed!", Logger::Fatal, errno);
325  return false;
326  }
327 
328  SVoice voice = vocalware.voices.at(voiceId);
329 
330  std::string cs = voice.eid + voice.lid + voice.vid + read + voice.fxtype + voice.fxlevel + vocalware.acc + vocalware.api + vocalware.secret;
331  std::string encodedRead = curl_easy_escape(curl, read.c_str(), read.length());
332  std::string url = "http://www.vocalware.com/tts/gen.php?EID=" + voice.eid + "&LID=" + voice.lid + "&VID=" + voice.vid + "&TXT=" + encodedRead + "&FX_TYPE=" + voice.fxtype + "&FX_LEVEL=" + voice.fxlevel;
333  url += "&ACC=" + vocalware.acc + "&API=" + vocalware.api + "&CS=" + md5(cs);
334 
335  curl_easy_setopt(curl, CURLOPT_URL, url.c_str());
336  curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_data);
337  curl_easy_setopt(curl, CURLOPT_WRITEDATA, fp);
338  curl_easy_perform(curl);
339  curl_easy_cleanup(curl);
340  fclose(fp);
341  return true;
342  }
343  logger->Log("Ase::GenerateTextToSpeech curl_easy_init failed!", Logger::Fatal);
344  return false;
345 }
346 
347 void Ase::LoadScenario(std::string path)
348 {
349  scenarioLoaded = false;
350  currentStep = 0;
351  subStep = 0;
352  steps.clear();
353 
354  if (path.length() == 0)
355  {
356  logger->Log("Ase::LoadScenario scenario file path was empty!", Logger::Fatal);
357  exit(0);
358  }
359 
360  int configFile;
361  _sopen_s(&configFile, path.c_str(), _O_RDONLY | _O_BINARY | _O_SEQUENTIAL, SH_DENYWR, S_IREAD);
362  if (configFile == -1)
363  {
364  char msg[99];
365  sprintf_s(msg, 99, "Ase::LoadScenario could not open file: %S", path.c_str());
366  logger->Log(msg, Logger::Error, errno);
367  exit(0);
368  }
369 
371  int fileLength = _filelength(configFile);
372  char* buffer = new char[fileLength + 1]; // null terminator
373  _read(configFile, buffer, fileLength);
374  _close(configFile);
375  buffer[fileLength] = 0; // null terminate
376 
377  using namespace rapidxml;
378  xml_document<> doc; // character type defaults to char
379  doc.parse<0>(buffer); // 0 means default parse flags
380 
381  xml_node<>* scenario = doc.first_node("Scenario");
382 
383  for (xml_node<>* stepNode = scenario->first_node(); stepNode; stepNode = stepNode->next_sibling())
384  {
385  if (_strcmpi(stepNode->name(), "Step") != 0) continue;
386 
387  SStep step;
388 
389  xml_attribute<>* name = stepNode->first_attribute("name");
390  if (name->value()) step.name = name->value();
391 
392  for (xml_node<>* node = stepNode->first_node(); node; node = node->next_sibling())
393  {
394  SCommand command;
395 
396  xml_attribute<>* commandName = node->first_attribute("name");
397  if (commandName) command.name = commandName->value();
398  xml_attribute<>* svalue = node->first_attribute("svalue");
399  if (svalue)
400  strcpy_s(command.svalue, 64, svalue->value());
401  xml_attribute<>* ivalue = node->first_attribute("ivalue");
402  if (ivalue)
403  command.ivalue = atoi(ivalue->value());
404  xml_attribute<>* fvalue = node->first_attribute("fvalue");
405  if (fvalue)
406  command.fvalue = static_cast<float>(atof(fvalue->value()));
407  xml_attribute<>* bvalue = node->first_attribute("bvalue");
408  if (bvalue)
409  command.bvalue = atoi(bvalue->value()) == 1;
410  xml_attribute<>* vmu = node->first_attribute("vmu");
411  if (vmu) command.vmu = vmu->value();
412  xml_attribute<>* voice = node->first_attribute("voice");
413  if (voice) command.voice = atoi(voice->value());
414  xml_attribute<>* wait = node->first_attribute("wait");
415  if (wait) command.wait = wait->value();
416  xml_attribute<>* timeout = node->first_attribute("timeout");
417  if (timeout) command.timeout = static_cast<float>(atof(timeout->value()));
419  xml_attribute<>* timeoutStep = node->first_attribute("timeoutStep");
420  if (timeoutStep) command.timeoutStep = timeoutStep->value();
421  xml_attribute<>* jump = node->first_attribute("jump");
422  if (jump) command.jump = jump->value();
423  xml_attribute<>* help = node->first_attribute("help");
424  if (help) command.help = help->value();
425  xml_attribute<>* element = node->first_attribute("element");
426  if (element) command.element = element->value();
427 
428  step.commands.push_back(command);
429  }
430 
431  if (step.commands.size() > 0)
432  steps.push_back(step);
433  }
434 
435  SAFE_DELETE(buffer);
436 
437  scenarioLoaded = true;
438  bus->ScenarioTimer = 0.0f; // reset
439  bus->ScenarioScore = 0.0f; // reset
440 
441 
442  occupantControlAuthority = 1.0f; // initially 100%, if they fail the first test it goes to 50, then to 25?
443 
444 
446  bus->waypoints.clear();
447 
449  Waypoint butch = Waypoint(&bus->vehicles.at(1));
450  butch.FixId = "BUTCH";
451  butch.type = Waypoint::WaypointType::FlyoverFix;
452  butch.relativeLocation = D3DXVECTOR3(-0.015f, -0.097f + 0.015f, 0.042f); // left runway, 50 feet (15 m) down the runway, coming in from aft of station
453  butch.relativeLocation.y -= 20.0f; // 20km out
454  bus->waypoints.push_back(butch);
455 
457  Waypoint cssdy = Waypoint(&bus->vehicles.at(1));
458  cssdy.FixId = "CASDY";
459  cssdy.type = Waypoint::WaypointType::FinalApproachFix;
460  cssdy.relativeLocation = D3DXVECTOR3(-0.015f, -0.097f + 0.015f, 0.042f); // left runway, 50 feet (15 m) down the runway, coming in from aft of station
461  cssdy.relativeLocation.y -= 8.828f; // 8.828 km out ...
462  cssdy.relativeLocation.z += 0.458f; // .042 + .458 is .5 ... FL355 is the FAF with sin(3 deg) means 8.282km out
463  bus->waypoints.push_back(cssdy);
464 
466  Waypoint e419 = Waypoint(&bus->vehicles.at(1));
467  e419.FixId = "E419";
468  e419.type = Waypoint::WaypointType::TouchdownZone;
469  e419.relativeLocation = D3DXVECTOR3(-0.015f, -0.097f + 0.015f, 0.042f); // left runway, 50 feet (15 m) down the runway, coming in from aft of station
470  //e419.relativeLocation.y -= 0.2f; // move it back a bit
471  bus->waypoints.push_back(e419);
472 }
473 
474 UINT Ase::FindStep(std::string stepName)
475 {
476  for (UINT i = 0; i < steps.size(); i++)
477  {
478  if (steps.at(i).name == stepName)
479  return i;
480  }
481  return 0;
482 }
float EngineThrustCommand[enginesC]
(57) Thrust command (when an information source is installed);
Definition: Bus.h:231
float occupantControlAuthority
Definition: Module.h:284
std::string wait
Definition: Module.h:304
bool FileExists(const std::string name)
Definition: ASE.cpp:28
size_t write_data(void *ptr, size_t size, size_t nmemb, FILE *stream)
Callback function for CURL.
Definition: ASE.cpp:310
float occupantPitchControlAuthority
Definition: Bus.h:298
void ClearHelp()
Definition: Bus.h:344
float occupantRollControlAuthority
Definition: Bus.h:299
std::vector< Command > commandStream
Definition: Bus.h:20
std::string lid
Definition: Module.h:266
Definition: Logger.h:5
bool GenerateTextToSpeech(std::string url, UINT voice, std::string outputPath)
Definition: ASE.cpp:316
int soundDevice
Definition: Module.h:334
UINT FindStep(std::string stepName)
Definition: ASE.cpp:474
std::string element
Definition: Module.h:311
std::string vid
Definition: Module.h:267
std::vector< SCommand > commands
Definition: Module.h:317
std::vector< Vehicle > vehicles
for TCAS, GPWS, FMS
Definition: Bus.h:366
std::vector< Waypoint > waypoints
Definition: Bus.h:367
char svalue[64]
Definition: Command.h:24
WaypointType type
Definition: Waypoint.h:21
float PitchAttitude
(6) Pitch attitude;
Definition: Bus.h:49
UINT currentStep
Definition: Module.h:281
float DesiredPitch
Definition: Bus.h:123
std::string fxlevel
Definition: Module.h:269
int subStep
Definition: Module.h:282
bool AutothrottleEngaged
Definition: Bus.h:81
bool bvalue
Definition: Module.h:298
std::string vmu
Definition: Module.h:289
Definition: Bus.h:12
std::string name
command name
Definition: Command.h:11
Ase(Bus *prmBus, Logger *prmLogger, int prmSoundDevice, SVocalware vocalware)
Definition: ASE.cpp:15
float RollAttitude
(7) Roll attitude;
Definition: Bus.h:51
std::string help
Definition: Module.h:310
float DesiredRoll
Definition: Bus.h:122
std::vector< SVoice > voices
Definition: Module.h:277
D3DXVECTOR3 relativeLocation
Definition: Waypoint.h:22
Abstract base class for modules By definition, instruments don&#39;t do any of the work (they don&#39;t modif...
Definition: Module.h:11
void FrameMove(float fElapsedTime) override
Definition: ASE.cpp:34
Definition: Command.h:5
std::string secret
Definition: Module.h:276
Logger * logger
Definition: Module.h:324
struct Bus::Afcs AFCS
HSTREAM PlayStream(std::string filename) const
Definition: BASS.cpp:226
HSTREAM stream
Definition: Module.h:285
void Log(const char *msg, Level level=Info, int errorCode=0)
These have to be in this order.
Definition: Logger.cpp:16
void AdvanceToNextStep()
Definition: ASE.cpp:282
std::string receiptNumber
Definition: Bus.h:293
UINT voice
Definition: Module.h:290
std::string FixId
Definition: Waypoint.h:20
SVocalware vocalware
Definition: Module.h:326
int ivalue
Definition: Command.h:21
std::string eid
Definition: Module.h:265
float timeout
Definition: Module.h:305
std::string timeoutStep
Definition: Module.h:307
std::string name
Definition: Module.h:316
char svalue[64]
Definition: Module.h:299
bool StreamIsPlaying(HSTREAM streamHandle) const
Definition: BASS.cpp:247
bool AutopilotEngaged
Definition: Bus.h:80
void Initialize(Devices::Bass *prmBass)
Definition: ASE.cpp:23
void AddHighlight(std::string elementName)
Definition: Bus.h:332
std::string jump
Definition: Module.h:302
std::string acc
Definition: Module.h:274
float delay
wait number of seconds before executing command
Definition: Command.h:8
std::vector< SStep > steps
Definition: Module.h:320
void LoadScenario(std::string path)
Definition: ASE.cpp:347
float fvalue
Definition: Module.h:297
float ScenarioScore
Definition: Bus.h:294
float ScenarioTimer
Definition: Bus.h:292
std::string api
Definition: Module.h:275
std::string md5(const std::string str)
Definition: MD5.h:431
void RemoveHighlight(std::string elementName)
Definition: Bus.h:336
int ivalue
Definition: Module.h:296
Devices::Bass * bass
Definition: Module.h:325
std::string name
Definition: Module.h:292
bool scenarioLoaded
Definition: Module.h:283
D3DXVECTOR3 Location
Definition: Bus.h:359
std::string fxtype
Definition: Module.h:268
Bus * bus
Definition: Module.h:323