SDK Question Saving custom parameters to a scenario

Hlynkacg

Aspiring rocket scientist
Addon Developer
Tutorial Publisher
Donator
Joined
Dec 27, 2010
Messages
1,868
Reaction score
4
Points
0
Location
San Diego
I apologise for spamming the SDK forum with questions but could someone please walk me through the process of saving to and loading custom vessel parameters from a *.SCN file?

I've looked at the available samples in the SDK folder (DG, Atlantis, and ShuttleA) but I don't really understand the underlying logic or how to apply it to my own vessel.
 
When orbiter saves or loads, it calls the functions clbkSaveState resp. clbkLoadState on every vessel. Since your vessel is inherited from the VESSEL class, you can declare those functions and orbiter will call them at the apropriate times. There you can execute all the saving and loading you need to do (the functions will pass you a handle to the scenario file). Look at clbkSaveState and clbkLoadState in the DG example.
 
This is what I took from the cpp examples in the API documentation to handle the default orbiter parameters. The problems begin when I try to add my own.

Code:
// Read vessel status from scenario file
void LEM::clbkLoadStateEx (FILEHANDLE scn, void *status) 
{ 
char *line; while (oapiReadScenario_nextline(scn, line)) 
ParseScenarioLineEx (line, status); 
}

// Write vessel status to scenario file
void LEM::clbkSaveState (FILEHANDLE scn) 
{ 
VESSEL3::clbkSaveState (scn); 
}


For I'm looking at the DG load and save states but don't understand what is actually happening.

I have two enumerators that I want to write to my scenario "STATUS" (current vessel congiguration) and "AUTOPILOT" (current autopilot mode) but the above doesn't give me a whole lot of indication on how to do so.

My (currently inoperative) code is below

Code:
// Read vessel status from scenario file
void LEM::clbkLoadStateEx (FILEHANDLE scn, void *status) 
{ 
	char *line; 
	while (oapiReadScenario_nextline (scn, line)) 
	{
		if (!strnicmp (line, "AUTOPILOT", 9)) 
		{
			sscanf (line+9, "%f", &AUTOPILOT);
		}
		else 
		{ 
			ParseScenarioLineEx (line, status); 
		}
	}
}

// Write vessel status to scenario file
void LEM::clbkSaveState (FILEHANDLE scn) 
{ 
	VESSEL3::clbkSaveState (scn);
	oapiWriteScenario_float (scn, "AUTOPILOT", AUTOPILOT);
}

That or I'm just too much of a C++ luddite to see it.
 
Last edited:
LoadState is ok in its structure, although forcing the result of strnicmp into a boolean might not be the safest thing to do.

Code:
if (strnicmp (line, "AUTOPILOT", 9) == 0)

would be more appropriate.

As for your savestate, you really shouldn't call savestate from within the function itself... No need to go recursive. Just write your stuff to the file handle.
 
Actually, that code should work. I don't see any obvious errors. Basically what it's doing is the while loop will retrieve the next line on each loop cycle and store it in "line." Then you're using strnicmp to compare the first 9 characters of that line to the string "AUTOPILOT". If they match (0 difference) then you're using sscanf to store the first floating point value it finds after the 9th character of the "line" string to the variable called AUTOPILOT. If it doesn't match, then it's being sent to Orbiter to be processed.
For savestate, you're just writing the floating point value stored in AUTOPILOT to the scenario file.
Not sure if that helps, but from looking at your code, that's what it's doing.

As for your savestate, you really shouldn't call savestate from within the function itself... No need to go recursive. Just write your stuff to the file handle.
Actually calling the baseclass savestate function is necessary to make sure Orbiter writes the default status lines I believe.
 
Last edited:
forcing the result of strnicmp into a boolean might not be the safest thing to do.
In C/C++:
Code:
if (strnicmp (line, "AUTOPILOT", 9) == 0)
is equal to:
Code:
if (!strnicmp (line, "AUTOPILOT", 9))
as value of 0 is always equal false, and value different than 0 is true. There's no problem with that conditional.
 
The code you have seems OK. What's the value of AUTOPILOT after clbkLoadStateEx returns, and what did you expect?

I suspect the problem might be that
Code:
sscanf (line+9, "%f", &AUTOPILOT);
reads the data as a float, whereas the AUTOPILOT variable is an int. You might want to use "%d" instead of "%f" (Full list of format specifiers: http://en.cppreference.com/w/cpp/io/c/fscanf) Similarly, you're writing the data as a float, when it should be an int (all this assumes the AUTOPILOT variable is an int, not a float/double).
 
Last edited:
The code you have seems OK. What's the value of AUTOPILOT after clbkLoadStateEx returns, and what did you expect?

I suspect the problem might be that
Code:
sscanf (line+9, "%f", &AUTOPILOT);
reads the data as a float, whereas the AUTOPILOT variable is an int. You might want to use "%d" instead of "%f" (Full list of format specifiers: http://en.cppreference.com/w/cpp/io/c/fscanf) Similarly, you're writing the data as a float, when it should be an int (all this assumes the AUTOPILOT variable is an int, not a float/double).

Thank you, that was the problem.

Now i get back to work

---------- Post added 05-16-12 at 04:12 AM ---------- Previous post was 05-15-12 at 11:56 PM ----------

Ok, new problem.

Since I started overloading the clbkLoadStateEx and SaveState, my vessel has stopped saving the amount of fuel in it's secondary and RCS propellant tanks.

Is there a simple explanation or do I have to start saving propellant as a custom parameter?
 
Last edited:
It could be a number of things. How do you know it's not saving? Is there a scenario file entry for that tank? How have you overrided the two methods? Have you done anything with the RCS fuel tank in these methods? Are you making sure to call the baseclass savestate method in your vessel's savestate method?
 
Using C++ stringstreams is more elegant and less error prone, because you don't need to count the number of characters in a tag:

Code:
void LaunchMFD::ReadStatus (FILEHANDLE scn)
{
    char *line;
    while (oapiReadScenario_nextline (scn, line))
    {
        std::istringstream ss;
        ss.str(line);
        std::string id;
        if ( ss >> id )
        {
            if ( id == "BOOLALT" )
                ss >> m_data->m_ManualAlt;
            else if ( id == "BOOLENG" )
                ss >> m_data->CutEngines;
            else if ( id == "BOOLDAS" )
                ss >> m_data->m_daSynchroOrbit;
            else if ( id == "PVIEW" )
            {
                int view;
                ss >> view;
                m_data->pageView = (View)view;
            }
            else if ( id == "INCL" )
                ss >> m_data->GetTgtParam().incl;
            else if ( id == "TGT" )
            {
                std::string tgt;
                ss >> tgt;
                std::string replaced = CharManipulations().replace(tgt, ">", " ");
                m_data->SetTargetStr( replaced );
            }
        }
    }
}

The first >> accessor to "ss" extracts the tag ("id") and second extracts the value itself, no matter what type it is. The >> operator returns false on failure (like a variable with incorrect type or no value to extract)
 
Last edited:
@ Enjo: I'm sorry but that's all greek to me, assume you're explaining it to a 6th grader. :confused:

@ Zatnikitelman: After some further testing i've found the the problem is tied to the stage seperation routine. If the vessel is in it's initial/default configuration everything saves normally. The problem is that on stage seperation i'm the stage's propellant and thruster handles get deleted and this is throwing the scenario save/load functions into disarray because "prop tank 3" is suddenly "prop tank 2" as far as the *.scn file is concerned.

---------- Post added at 07:21 AM ---------- Previous post was at 06:34 AM ----------

OK, I seem to have solved the problem by re-aranging the order in which my propellant and thruster handles are defined.
 
@ Enjo: I'm sorry but that's all greek to me, assume you're explaining it to a 6th grader. :confused:
Did you write a standard, most basic C++ "Hello word!" program using streams (standard output stream - std::cout), and another example reading from the standard input stream (std::cin)?

`ss.str (line)` makes a string stream using contents of `line` character buffer. `ss >> id` reads the first word from `ss` (first word of `line`) into `id` string. If `id` hasn't been read it returns false, otherwise it is compared to supported by the add-on custom scenario status keywords. Each `ss >> m_data->something` reads the following word into a field of some class or structure (it can be a plain variable instead of it), with using the type of that variable for format of its representation in the stream for reading (e.g. int, float, double, string, etc.).
 
@ Enjo: I'm sorry but that's all greek to me, assume you're explaining it to a 6th grader. :confused:

When being in Rome, talk like a Roman. ;)

Or: When coding in C++, use C++ functions. In this case:

  1. Create an input string stream object (std::istringstream ss). That is a data stream like a file or stdin/cin, but operating on a single string. The opposite, an output string stream, is used for formatting data into a string, analog to files or stdout/cout.
  2. Initialize the string stream with the c-string line from Orbiter (ss.str(line)).
  3. Read the first word from the stream by streaming a C++-string from it (ss >> id). This reads all characters from the string stream until the first whitespace. The >>-operation returns false, if the operation failed to read the string from the string stream.
  4. compare the id-string with your scenario value keys.
  5. If you want to read data following the id, read them from the string stream.
 
Actually calling the baseclass savestate function is necessary to make sure Orbiter writes the default status lines I believe.

Woud you look at this, I never even noticed... and had the line in there all the time. :facepalm:
 
Back
Top