General Question Adding a vessel's state vector in a running scenario... from a file?

Boxx

Mars Addict
Addon Developer
Donator
Joined
Nov 15, 2009
Messages
174
Reaction score
123
Points
58
Location
Paris Area
The "Scenario Editor" allows to add (in an already running scenario) a vessel with its state vector that must be input manually, at the exact time when "Apply" is clicked. Well, I want to do the same but not manually, by reading/loading a vessel's state vector from a ".scn" file, as produced by the button "saved". Of course, I don't want to quit the running scenario.

I guess it is possible to do that by modiying the code of the ScenarioEditor. Before considering this option, has anyone a good idea, a script, a command line...?
 

Mr Martian

Orbinaut/Addon dev/Donator
Addon Developer
Donator
Joined
Jun 6, 2012
Messages
288
Reaction score
67
Points
28
Location
Sydney, Australia, Earth, Sol
Website
www.orbithangar.com
Are you hoping to load in entire scenario, or just a particular vessel? Also, are you hoping to load the state vectors from an actual scenario file, or do you want to load them from your own defined text document?
 

Face

Well-known member
Orbiter Contributor
Addon Developer
Beta Tester
Joined
Mar 18, 2008
Messages
4,390
Reaction score
576
Points
153
Location
Vienna
The "Scenario Editor" allows to add (in an already running scenario) a vessel with its state vector that must be input manually, at the exact time when "Apply" is clicked. Well, I want to do the same but not manually, by reading/loading a vessel's state vector from a ".scn" file, as produced by the button "saved". Of course, I don't want to quit the running scenario.

I guess it is possible to do that by modiying the code of the ScenarioEditor. Before considering this option, has anyone a good idea, a script, a command line...?
DISCLAIMER: the following code was taken from my OMPU project, and edited right in here. Although I tried my best to adjust it properly, it might contain bugs.
Code:
	VESSELSTATUS2 state;
	memset(&state, 0, sizeof(VESSELSTATUS2));
	state.flag = 0;
	state.version = 2;
	auto rbody = oapiGetObjectByName("Earth"); //Put your GBody name here
	if (rbody == nullptr)
	{
		rbody = oapiGetObjectByIndex(0);
	}
	state.rbody = rbody;	
	VECTOR3 rpos, rvel, arot, vrot; //Put your data here
	state.rpos = rpos;
	state.rvel = rvel;
	//Prohibit spawning inside the GBody
	auto temp = length(state.rpos);
	if (!(temp > 0e0)) temp = FLT_MIN;
	if (oapiGetSize(state.rbody) + 0.1 > temp)
	{
		state.rvel = state.rpos*0.5 / temp;
		state.rpos *= (oapiGetSize(state.rbody) + 0.15) / temp;
	}
	state.status = status;  //Put your status data here
	state.arot = state.status == 0 ? arot : _V(10, 0, 0); //Freaky magic number taken from ScnEditor source
	state.vrot = vrot;	
	state.surf_lat = surfLat;  //Put your latitude data here, necessary for landed status
	state.surf_lng = surfLng; //Put your longitude data here, necessary for landed status
	state.surf_hdg = surfHdg; //Put your heading data here, necessary for landed status
	vessel->DefSetStateEx(&state); // If you already have a vessel created. If not, just do oapiCreateVesselEx("VesselName", "VesselClass", &state) here and skip the next 2 lines
	vessel->SetGlobalOrientation(arot);
	vessel->SetAngularVel(vrot);

Do it inside of
Code:
void PreStep(double SimT, double SimDT, double mjd)

This is without acceleration, because normally the Orbiter core propagates it from thruster settings. If you have a specific vessel class, you need to set the specific thruster setting (depending on your file format). If you want to apply save settings just as written in a SCN file, you can do this trick:
Code:
	// "vessel" is your vessel interface pointer
	// "save" is the relative path to a temp file (starting from your Orbiter root directory) containing just the SCN lines for the vessel
	// "state" is VESSELSTATUS2 from above
	// "created" signals if the vessel has just been created or not
	if (vessel->Version() > 1)
	{
		FILEHANDLE stateFile = oapiOpenFile(save, FILE_IN, ROOT);
		((VESSEL2*)vessel)->clbkLoadStateEx(stateFile, state);
		oapiCloseFile(stateFile, FILE_IN);
		if (created) ((VESSEL2*)vessel)->clbkPostCreation();
	}
	// at this point you can delete the temp file again

If you just want the acceleration to be applied without setting class-specific internals, you can do it via a fake thruster. But this is a bit too complicated to fit in here. Drop me a note if you want to know more.
 

Boxx

Mars Addict
Addon Developer
Donator
Joined
Nov 15, 2009
Messages
174
Reaction score
123
Points
58
Location
Paris Area
Do it inside of

+ ok for the disclaimer ;) I see that you've had to address the topic already, and no surprise with your OMPU project! :salute: then your suggestion is to go to the code... ok...

Thank you for pointing the various properties to modify, it will greatly help. No PreStep callback exists in the ScnEditor. Do you suggest to create one there? (or in an existing vessel or MFD class... then watching for a temp file to trigger the creation of the vessel, for instance...).

BTW: I could recompile ScnEditor or DeltaGlider or create and compile a new MFD from MFDTemplate, but I have not been able to recompile your OMP 0.8.2 so far, due to an issue with .NET in VS2019 (and I have not tried to recompile Orbiter's core yet!!!)

If you just want the acceleration to be applied without setting class-specific internals, you can do it via a fake thruster.
oups... indeed, I did not see that coming.... you're right, thank you!! let's go step-by-step... I will come back to you then.
 
Last edited:

Boxx

Mars Addict
Addon Developer
Donator
Joined
Nov 15, 2009
Messages
174
Reaction score
123
Points
58
Location
Paris Area
Are you hoping to load in entire scenario, or just a particular vessel? Also, are you hoping to load the state vectors from an actual scenario file, or do you want to load them from your own defined text document?
Hi! I don't mind any particular approach if something already exists. Here is some context: working on a persistent universe with the existing solutions, we have re-installed a server with Orbiter + OMP (not publicly yet, sorry). Then, when a user disconnects, his/her vessel's status should be passed from the remote client to another instance of Orbiter (on the server) to keep propagating, then back to the user when he/she reconnects. The server's instance of Orbiter is always running (24/7) and is requested to create and delete vessels (of the disconnected users) on the fly.

Ideally only the lines for a specific vessel (and epoch) as written in an SCN should be input, like Face's trick with "clbkLoadStateEx". Ideally (again!), a "load" button in ScnEditor should be created....
 

hagiasophia420

New member
Joined
Jun 11, 2022
Messages
9
Reaction score
6
Points
3
Location
Brazil
Why not create an Orbiter module that acts as a client to your server, and then you don't need to worry about starting scenarios. The client could receive the state vector info directly and then use oapiCreateVessel or DefSetState.
 

Boxx

Mars Addict
Addon Developer
Donator
Joined
Nov 15, 2009
Messages
174
Reaction score
123
Points
58
Location
Paris Area
Indeed, after some attempts, I decided to create a new MFD and I implemented @Face 's suggestions. It is still very experimental and not satisfactory at all (not operational): on the server machine, I've got a client running. From it, I track the remote client's vessel and save it in the server's machine, then the remote client can disconnect... without talking yet about reconnection later :aga: .

Doing so, I only retrieve the properties of the state vector that are accessible via OMP's track command (location and velocities): that's a good start but I don't get the rest of the properties on the remote client (vessel's class, remaining propellant, thrust position....). Ideally I should implement a query via UDP protocol => hence, how to do that?! what includes/librairies + what part of the OMP code I should get inspiration from?

In the meantime, I considered several alternatives (the remote client to send an e-mail - for instance - with the full state vector and then re-propagate in the server and re-synchronize in parallel... but more complicated). Then, I guess the dialog with UDP is the best approach, with an enriched state vector "on request".
 

Face

Well-known member
Orbiter Contributor
Addon Developer
Beta Tester
Joined
Mar 18, 2008
Messages
4,390
Reaction score
576
Points
153
Location
Vienna
OMP already has a save file transmission system. What it does is saving a client vessel just as if it was stored in a scenario file, then transmitting this information via TCP to everyone. Every other client can then parse that information and send it to the remote vessel just as if it was loaded from a scenario file. Since this is no streaming information, but rather a punctual information at specific events, I can't recommend UDP here, though.

If you want to know how it works, search for the "S" key trigger in the code base. This will send a GINFO event to the server, which in turn sends a REQST back for the focused vessel. This will trigger the client to do the save and transfer via GINFO lines, which the server simply broadcasts to every other client, which every other client will receive and load into the remote vessel. On the server, you could take those lines, store them in a file, and hand out on startup instead of the location scenario lines for the given vessel.
 

Boxx

Mars Addict
Addon Developer
Donator
Joined
Nov 15, 2009
Messages
174
Reaction score
123
Points
58
Location
Paris Area
I would like to re-use or modify the save feature. I found the following, where dinfo.statusPingKey = OAPI_KEY_S, but what does call that, I can't find...:
Code:
int keyhandler(void *null, char *keystate)
{
    [...]
    if (KEYDOWN(keystate, dinfo.statusPingKey))
    {
        if (dinfo.statusPinger==NULL) dinfo.statusPinger=focobj;
        return 1;
    }
but where is the save & transfer code? or do you mean: the "save" menu in the Client generates a save & transfer via GINFO lines? then, I would have to intercept these lines in another client... then I need to "read" a received TCP content => where to satrt from?
 

Face

Well-known member
Orbiter Contributor
Addon Developer
Beta Tester
Joined
Mar 18, 2008
Messages
4,390
Reaction score
576
Points
153
Location
Vienna
The keyhandler marks the beginning of a process. It is like so:
  1. keyhandler sets marker.
  2. statustimer (in the "recorded event transmission" section) sees marker and fires a GINFO event message to the server. The content of this message is irrelevant besides the triggering of step 3.
  3. Server receives the GINFO event message from a client and immediately sends a REQST message to that client.
  4. Client receives the REQST message in receiverthread and sends a REQST response by means of multiple GINFO lines that resemble class information, vessel save state and animation information. There (at approx. line 5332 of OMPClient.cpp) you can see how to generate save states for transportation.
  5. Server receives the GINFO lines and immediately broadcasts it to all clients via TCP.
  6. Other clients receive those lines in their receiverthread and interpret the states accordingly. There (at approx. line 5515 of OMPClient.cpp) you can see how to decode the lines to vessel states again. Note that with the case "|" subcommand of GINFO the lines are appended into a temp file, and then with case "-" subcommand (marking the end of the GINFO save state transmission), the temp file is fed into the clbkLoadStateEx() method of the appropriate remote vessel.
You can see this system in action if you open a puttytel session in parallel to the client session. Everytime somebody e.g. presses "G", the event is triggered and the full vessel state is broadcast on TCP.
 

Face

Well-known member
Orbiter Contributor
Addon Developer
Beta Tester
Joined
Mar 18, 2008
Messages
4,390
Reaction score
576
Points
153
Location
Vienna
Example for the above:
Code:
                    Welcome to OMP
----------------------------------------------------------

Administrator shell.
(12:00:42)>>> "Face" joined the server <<<

REQST 33

GINFO 33"Ctest"deltaglider
GINFO 33"N00119305F001 000
GINFO 33"D0
GINFO 33:|ALT 2.47
GINFO 33:|AFCMODE 7
GINFO 33:|PRPLEVEL 0:1 1:1
GINFO 33:|NAVFREQ 402 94 0 0
GINFO 33:|XPDR 0
GINFO 33:|HOVERHOLD 0 1 0.0000e+000 0.0000e+000
GINFO 33:|GEAR 1.0000 0.0000
GINFO 33:|AAP 0:0 0:0 0:0
GINFO 33:-
GINFO 33:^0 9A9999999999D93F 9A9999999999D93F 0 0 0 0 E03F F03F F03F F03F 0 0 0 0 0 0
GINFO 33:^10 0 0 0 E03F 666666666666E63F E03F E03F E03F E03F E03F

"C" encodes name and class
"N" encodes navigation instrument information
"D" encodes docking information
"|" is a save state line
"-" is the save state end
"^" is animation state information
 

Boxx

Mars Addict
Addon Developer
Donator
Joined
Nov 15, 2009
Messages
174
Reaction score
123
Points
58
Location
Paris Area
Okayyyyy... still doing some tests but I am starting to figure it out. Good news in the meantime: I was able to recompile your OMPClient in VisualStudio 2019 (which was not trivial, and still not all .dll are produced... I will open a thread about that): this paves the way for new features. As your code was made open source, I guess you won't mind if I fork it, but I prefer to tell you here.
 

Face

Well-known member
Orbiter Contributor
Addon Developer
Beta Tester
Joined
Mar 18, 2008
Messages
4,390
Reaction score
576
Points
153
Location
Vienna
As your code was made open source, I guess you won't mind if I fork it, but I prefer to tell you here.
Thanks for letting me know. I don't mind it being forked and certainly don't mind somebody developing it further. Just be aware that I will probably have no time to support whatever arises from your fork.
 

Boxx

Mars Addict
Addon Developer
Donator
Joined
Nov 15, 2009
Messages
174
Reaction score
123
Points
58
Location
Paris Area
Happy to share this: I could recompile Face's OMP code entirely for C++ in VS2019, providing a few fixes. At least in "Debug", you can generate the whole solution by selecting the "Mixed Platforms": it will produce all 9 output files and will put them in a subfolder called "addon" in the solution folder itself, along with the necessary files that come with the Client & Server distributable package. Reports and suggestions are of course welcome.

As said previously I forked OMP for my own projects. The modified source code is released here on GitLab. (The initial code is the very first commit in the same repo).

OMP is open-source under the terms of the GNU GPL v2, and so is this modified code. It can be interesting for those, including myself, who want to take over this development. On my side, I will focus on the transfer of state vessls between Clients whenever a user is disconnected.
 

Boxx

Mars Addict
Addon Developer
Donator
Joined
Nov 15, 2009
Messages
174
Reaction score
123
Points
58
Location
Paris Area
Still coding the transfer of a vessel from a client to another client, I am wandering how the CHashTable(s) are managed.

State of progress: the new feature will be in OMP Client (not at server level). User A (who wants to disconnect) will request user B (who keeps connected) to take over A's vessels. I already can duplicate the vesselstatus and its anim and config (as given by GINFO and "|", "^" marks) from A to B, then request A to "kill" its own vessels. But I get overlapping copies of vessels in A and B and some missing / outdated states (gears, indicators in cockpit, etc...). Nevertheless it could be a viable option.

What I want : considering that the whole vessels from A are already created in B's client, it would be simpler and cleaner to just transfer the "ownership" of A's vessels to B and avoid duplications. Then, my idea is to change clientData.GlobalsByHandle in B about A's vessel into a clientData.LocalID. But I get a mess when I'm trying to do so from B's client:

Code:
struct IDload* entry = (struct IDload*)clientData.GlobalsByHandle->get(<A_vessel_handle_in_B>)
struct IDload* newEntry = entry;
newEntry->ID = clientData.LocalID++;
sprintf(nextline, "%d", entry->ID);
if (clientData.LocalsByID->get(nextline)  == NULL) clientData.LocalsByID->put(nextline, entry); // creating a new "local" entry
clientData.GlobalsByHandle->del(<A_vessel_handle_in_B>); // removing the former "global" entry in B (and requesting A to do the same with TCP/UDP request)

I'm confused about the various catalogs in xthreaddata->user->GlobalsByHandle (TCP, but also in UDP threads), client.Data.GlobalsByHandle... and also I see that I should not copy a pointer "entry" as a new pointer "newEntry" as the pointed data could be changed (killed?) later in the code.... Indeed, a few seconds after transferring the vessel to B, I got a CTD on B and docked vessels in A dancing like crazy until B crashes and kills its connections!!

Is my idea wrong? Can you give some insights about the management of CHashTables?
 
Last edited:

Boxx

Mars Addict
Addon Developer
Donator
Joined
Nov 15, 2009
Messages
174
Reaction score
123
Points
58
Location
Paris Area
Considering all suggestions above, I managed to extend the concept of transferring vessel state vectors in OMP from a Client to another.

I did it at code level in OMX. For OMP developers: OMX v1.1.0 includes now 2 possible "GINFO" (the way the Client and the server synchronize the state vectors in TCP): either the minimum status is transferred (like the "save" feature) or an "extended" status is transferred (fuel & thrust are added). This comes in addition of the "Status Information" (SI) that is sent via UDP. The nex structure allows to add more properties of the vessels, included the "Landed" status which was causing tumbling/drifting vessels.
 
Top