93 const SGPath& initfile)
96 string aircraft=
"", prop_name=
"";
97 string notifyPropertyName=
"";
98 Element *element=0, *run_element=0, *event_element=0;
100 Element *notify_element = 0L, *notify_property_element = 0L;
101 double dt = 0.0, value = 0.0;
105 Element* document = XMLFileRead.LoadXMLDocument(script);
109 log <<
"File: " << script <<
" could not be loaded.\n";
113 if (document->
GetName() !=
string(
"runscript")) {
115 log <<
"File: " << script <<
" is not a script file\n";
127 log <<
"No \"run\" element found in script.\n";
133 if (run_element->HasAttribute(
"start"))
134 StartTime = run_element->GetAttributeValueAsNumber(
"start");
138 if (run_element->HasAttribute(
"end")) {
139 EndTime = run_element->GetAttributeValueAsNumber(
"end");
142 log <<
"An end time (duration) for the script must be specified in the script <run> element.\n";
146 if (default_dT == 0.0)
147 dt = run_element->GetAttributeValueAsNumber(
"dt");
151 log <<
"\nOverriding simulation step size from the command line. New step size is: "
152 << default_dT <<
" seconds (" << 1/default_dT <<
" Hz)\n\n";
165 if (!aircraft.empty()) {
170 log <<
"Aircraft must be specified in use element.\n";
174 initialize = SGPath::fromLocal8Bit(element->
GetAttributeValue(
"initialize").c_str());
175 if (initfile.isNull()) {
176 if (initialize.isNull()) {
178 log <<
"Initialization file must be specified in use element.\n";
183 log <<
"\nThe initialization file specified in the script file ("
184 << initialize <<
") has been overridden with a specified file ("
185 << initfile <<
").\n";
186 initialize = initfile;
191 log <<
"No \"use\" directives in the script file.\n";
195 auto IC = FDMExec->
GetIC();
196 if ( ! IC->Load( initialize )) {
198 log <<
"Initialization unsuccessful\n";
205 if (!FDMExec->
GetInput()->Load(element))
213 SGPath scriptDir = SGPath(script.dir());
214 if (scriptDir.isNull())
215 scriptDir = SGPath(
".");
218 if (!FDMExec->
GetOutput()->Load(element, scriptDir))
225 int saved_debug_lvl = debug_lvl;
227 LocalProperties.Load(run_element, PropertyManager.get(),
true);
228 debug_lvl = saved_debug_lvl;
232 event_element = run_element->FindElement(
"event");
233 while (event_element) {
236 struct event *newEvent =
new struct event();
239 newEvent->Name = event_element->GetAttributeValue(
"name");
243 if (event_element->GetAttributeValue(
"persistent") ==
string(
"true")) {
244 newEvent->Persistent =
true;
248 if (event_element->GetAttributeValue(
"continuous") ==
string(
"true")) {
249 newEvent->Continuous =
true;
254 if (condition_element) {
256 newCondition =
new FGCondition(condition_element, PropertyManager);
259 log << LogFormat::RED << e.what() << LogFormat::RESET <<
"\n\n";
263 newEvent->Condition = newCondition;
266 log <<
"No condition specified in script event " << newEvent->Name <<
"\n";
278 newEvent->Delay = 0.0;
281 if ((notify_element = event_element->
FindElement(
"notify")) != 0) {
283 if (notify_element->
GetAttributeValue(
"format") ==
"kml") newEvent->NotifyKML =
true;
285 newEvent->Notify =
true;
287 string notify_description = notify_element->
FindElementValue(
"description");
288 if (!notify_description.empty()) {
289 newEvent->Description = notify_description;
291 notify_property_element = notify_element->
FindElement(
"property");
292 while (notify_property_element) {
293 notifyPropertyName = notify_property_element->
GetDataLine();
295 if (notify_property_element->HasAttribute(
"apply")) {
296 string function_str = notify_property_element->GetAttributeValue(
"apply");
297 auto f = FDMExec->GetTemplateFunc(function_str);
299 newEvent->NotifyProperties.push_back(
new FGFunctionValue(notifyPropertyName, PropertyManager, f,
300 notify_property_element));
302 FGXMLLogging log(notify_property_element, LogLevel::WARN);
303 log << LogFormat::RED << LogFormat::BOLD <<
" No function by the name "
304 << function_str <<
" has been defined. This property will "
305 <<
"not be logged. You should check your configuration file.\n"
310 newEvent->NotifyProperties.push_back(
new FGPropertyValue(notifyPropertyName, PropertyManager,
311 notify_property_element));
313 string caption_attribute = notify_property_element->GetAttributeValue(
"caption");
314 if (caption_attribute.empty()) {
315 newEvent->DisplayString.push_back(notifyPropertyName);
317 newEvent->DisplayString.push_back(caption_attribute);
327 while (set_element) {
329 if (PropertyManager->HasNode(prop_name)) {
330 newEvent->SetParam.push_back( PropertyManager->GetNode(prop_name) );
332 newEvent->SetParam.push_back( 0L );
334 newEvent->SetParamName.push_back( prop_name );
340 newEvent->Functions.push_back(
nullptr);
345 newEvent->SetValue.push_back(value);
346 newEvent->OriginalValue.push_back(0.0);
347 newEvent->newValue.push_back(0.0);
348 newEvent->ValueSpan.push_back(0.0);
350 if (to_lower(tempCompare).find(
"delta") != string::npos) newEvent->Type.push_back(FG_DELTA);
351 else if (to_lower(tempCompare).find(
"bool") != string::npos) newEvent->Type.push_back(FG_BOOL);
352 else if (to_lower(tempCompare).find(
"value") != string::npos) newEvent->Type.push_back(FG_VALUE);
353 else newEvent->Type.push_back(FG_VALUE);
355 if (to_lower(tempCompare).find(
"ramp") != string::npos) newEvent->Action.push_back(FG_RAMP);
356 else if (to_lower(tempCompare).find(
"step") != string::npos) newEvent->Action.push_back(FG_STEP);
357 else if (to_lower(tempCompare).find(
"exp") != string::npos) newEvent->Action.push_back(FG_EXP);
358 else newEvent->Action.push_back(FG_STEP);
363 newEvent->TC.push_back(1.0);
365 newEvent->Transiting.push_back(
false);
369 Events.push_back(*newEvent);
372 event_element = run_element->FindNextElement(
"event");
396 unsigned event_ctr = 0;
399 double newSetValue = 0;
401 if (currentTime > EndTime)
return false;
404 for (
unsigned int ev_ctr=0; ev_ctr < Events.size(); ev_ctr++) {
406 struct event &thisEvent = Events[ev_ctr];
413 if (thisEvent.Condition->Evaluate()) {
414 if (!thisEvent.Triggered) {
418 for (i=0; i<thisEvent.SetValue.size(); i++) {
419 if (thisEvent.SetParam[i] == 0L) {
420 if (PropertyManager->HasNode(thisEvent.SetParamName[i])) {
421 thisEvent.SetParam[i] = PropertyManager->GetNode(thisEvent.SetParamName[i]);
424 err <<
"No property, \"" << thisEvent.SetParamName[i] <<
"\" is defined.\n";
428 thisEvent.OriginalValue[i] = thisEvent.SetParam[i]->getDoubleValue();
429 if (thisEvent.Functions[i] != 0) {
431 thisEvent.SetValue[i] = thisEvent.Functions[i]->GetValue();
434 err <<
"\nA problem occurred in the execution of the script. "
439 switch (thisEvent.Type[i]) {
442 thisEvent.newValue[i] = thisEvent.SetValue[i];
445 thisEvent.newValue[i] = thisEvent.OriginalValue[i] + thisEvent.SetValue[i];
449 log <<
"Invalid Type specified\n";
452 thisEvent.StartTime = currentTime + thisEvent.Delay;
453 thisEvent.ValueSpan[i] = thisEvent.newValue[i] - thisEvent.OriginalValue[i];
454 thisEvent.Transiting[i] =
true;
457 thisEvent.Triggered =
true;
459 }
else if (thisEvent.Persistent) {
460 thisEvent.Triggered =
false;
461 thisEvent.Notified =
false;
462 }
else if (thisEvent.Continuous) {
463 thisEvent.Triggered =
false;
464 thisEvent.Notified =
false;
467 if ((currentTime >= thisEvent.StartTime) && thisEvent.Triggered) {
469 for (i=0; i<thisEvent.SetValue.size(); i++) {
470 if (thisEvent.Transiting[i]) {
471 thisEvent.TimeSpan = currentTime - thisEvent.StartTime;
472 switch (thisEvent.Action[i]) {
474 if (thisEvent.TimeSpan <= thisEvent.TC[i]) {
475 newSetValue = thisEvent.TimeSpan/thisEvent.TC[i] * thisEvent.ValueSpan[i] + thisEvent.OriginalValue[i];
477 newSetValue = thisEvent.newValue[i];
478 if (thisEvent.Continuous !=
true) thisEvent.Transiting[i] =
false;
482 newSetValue = thisEvent.newValue[i];
488 if (thisEvent.Continuous !=
true)
489 thisEvent.Transiting[i] =
false;
490 else if (thisEvent.Functions[i] != 0)
491 newSetValue = thisEvent.Functions[i]->GetValue();
495 newSetValue = (1 - exp( -thisEvent.TimeSpan/thisEvent.TC[i] )) * thisEvent.ValueSpan[i] + thisEvent.OriginalValue[i];
499 log <<
"Invalid Action specified\n";
502 thisEvent.SetParam[i]->setDoubleValue(newSetValue);
507 if (thisEvent.Notify && !thisEvent.Notified) {
508 LogLevel level = thisEvent.NotifyKML ? LogLevel::STDOUT : LogLevel::INFO;
510 if (thisEvent.NotifyKML) {
511 out <<
"\n<Placemark>\n";
512 out <<
" <name> " << currentTime <<
" seconds" <<
" </name>\n";
513 out <<
" <description>\n";
514 out <<
" <![CDATA[\n";
515 out <<
" <b>" << thisEvent.Name <<
" (Event " << event_ctr <<
")"
516 <<
" executed at time: " << currentTime <<
"</b><br/>\n";
518 out <<
"\n" << LogFormat::UNDERLINE_ON << LogFormat::BOLD
519 << thisEvent.Name << LogFormat::NORMAL << LogFormat::UNDERLINE_OFF
520 <<
" (Event " << event_ctr <<
")"
521 <<
" executed at time: " << LogFormat::BOLD << currentTime << LogFormat::NORMAL
524 if (!thisEvent.Description.empty()) {
525 out <<
" " << thisEvent.Description <<
"\n";
527 for (j=0; j<thisEvent.NotifyProperties.size();j++) {
528 out <<
" " << thisEvent.DisplayString[j] <<
" = "
529 << thisEvent.NotifyProperties[j]->getDoubleValue();
530 if (thisEvent.NotifyKML) out <<
" <br/>";
533 if (thisEvent.NotifyKML) {
535 out <<
" </description>\n";
537 out <<
" <altitudeMode> absolute </altitudeMode>\n";
538 out <<
" <extrude> 1 </extrude>\n";
539 out <<
" <coordinates>"
543 <<
"</coordinates>\n";
544 out <<
" </Point>\n";
545 out <<
"</Placemark>\n";
548 thisEvent.Notified =
true;