API Question Vessel params and MFD

slaver0110

Member
Joined
Mar 21, 2011
Messages
72
Reaction score
2
Points
6
I have a Vessel coded and working properly, which has an ON/OFF flag which enables/disables the aft thrusters, using the "L"-key as a toggle.

The flag is defined as "PodFlag" and is an INT, which is either 1 or 0, found in the class definition. (I might just change it to a BOOL in the future, but for now it works).

I've been creating an MFD in parallel, which would be customized for this particular class of Vessel. In all honesty, I've found MFD coding to be another beast altogether, but I'm managing to put it together one line at a time.

Where I'm stuck is, I want to be able to change the "PodFlag" variable in the Vessel from the MFD. I've been looking through the API Guide and tried a few things from there, but to no avail.
I realize that certain parameters can be changed with "Vessel->...", but I need to be able to change that custom variable from the MFD.

I suppose what I'm basically asking is if there's a 'generic' way to do this.

AFTERTHOUGHT: I appreciate all the help this forum has given me, and the patience of the resident coders who put up with questions like this.

Cheers, and thanks!!
 

asbjos

tuanibrO
Addon Developer
Joined
Jun 22, 2011
Messages
696
Reaction score
259
Points
78
Location
This place called "home".
What about using the ConsumeButton handle to return a letter with ConsumeKeyBuffered?

You probably already use it for the button clicks, so just append something like this to your code:
PHP:
bool MyMFD::ConsumeButton (int bt, int event)
{
	if (!(event & PANEL_MOUSE_LBDOWN)) return false;

	if (bt == 0)
	{
		return ConsumeKeyBuffered (OAPI_KEY_L);
	}
	else return false;
}
 

Face

Well-known member
Orbiter Contributor
Addon Developer
Beta Tester
Joined
Mar 18, 2008
Messages
4,403
Reaction score
581
Points
153
Location
Vienna
I realize that certain parameters can be changed with "Vessel->...", but I need to be able to change that custom variable from the MFD.

For this I'd suggest to include the vessel class header in the MFD code and cast the vessel pointer to the custom vessel class. I'd also secure that cast by means of checking the vessel class name first.

Be aware that this method bears some pitfalls - especially version problems - when MFD and vessel binaries are separately distributed.

Another possible way is to use clbkGeneric with some custom exchange protocol. In essence it is a callback every vessel can implement, where you can exchange e.g. a string serializing the flags content and allowing commands to be executed.
 

slaver0110

Member
Joined
Mar 21, 2011
Messages
72
Reaction score
2
Points
6
Another possible way is to use clbkGeneric with some custom exchange protocol. In essence it is a callback every vessel can implement, where you can exchange e.g. a string serializing the flags content and allowing commands to be executed.

From an amateur coder's perspective, this is over my head.
I looked up clbkGeneric, but I'm afraid that the implementation is lost on me.
I'm not asking anyone to do my homework for me, but could you provide an example of what you mean?

Cheers!:cheers:
 
Last edited:

Face

Well-known member
Orbiter Contributor
Addon Developer
Beta Tester
Joined
Mar 18, 2008
Messages
4,403
Reaction score
581
Points
153
Location
Vienna
From an amateur coder's perspective, this is over my head.
I looked up clbkGeneric, but I'm afraid that the implementation is lost on me.
I'm not asking anyone to do my homework for me, but could you provide an example of what you mean?

What about the first suggestion? If the clbkGeneric approach sounds too complicated, the class header include is a much easier thing to do. Start with that.
 

fred18

Addon Developer
Addon Developer
Donator
Joined
Feb 2, 2012
Messages
1,667
Reaction score
104
Points
78
The options mentioned by face are absolutely correct, as usual!

if you need to check an example, even if the code may be not so easy to read, but Multistage2015 and multistage2015 mfd use exactly the second approach you're discussing. You need to include the header in the mfd project and then do a cast of the pointer (it sounds difficult, but if you see the example it is not).

Actually also the clbkgeneric seems difficult but it's not so hard, once you get to know how it works. That's in the code as well, is inside the validvessel method, just have a look at it if you need.
 

kuddel

Donator
Donator
Joined
Apr 1, 2008
Messages
2,064
Reaction score
507
Points
113
Hi,

the general idea of a MFD<->Vessel connection is, that you know the signature of the Vessel, so the MFD can "connect" to it and call a (or several) public methods.

The MFD code has to do something like this (pseudo code):
PHP:
#include "MyVessel.h" // <= to be able to cast (to our 'known' Vessel class)

class MyMFD : MFD2
{

public:
  MyMFD() : MFD2(), m_pVessel(NULL) {}
  ~MyMFD() { m_pVessel = NULL; }

private:
  MyVessel *m_pVessel; // Not NULL if "connected"
 
  void someCheckingMethod () {
    // ...
    VESSEL *pV =  oapiGetFocusInterface ();
    if (pV) {
      // Check if it is one of "our" vessels
      if (pV->GetClassName() == "MyVessel") {
        // Jipie! Let's store this
        m_pVessel = static_cast<MyVessel*>(pV);
      }
    }
  }

  void sendToVessel (msg_t *msg) {
    if (m_pVessel) {
      m_pVessel->ReceiveMessage(msg);
    }
  }
    // ...
}

The MyVessel class "just" needs to have a public Method named "ReceiveMessage" (with the according signature)

For messages in the other direction (Vessel to MFD) you could give the Vessel instance the "this" pointer via a dedicated method ( MyVessel::SetMfd(MyMFD *pmfd) )
 
Last edited:

slaver0110

Member
Joined
Mar 21, 2011
Messages
72
Reaction score
2
Points
6
Ok, I have a lot to parse into something usable here.
I'm taking a look at the Multistage2015 code, and I think I'm beginning to see what you're all talking about.

As per the "class header" (vessel.h) method, from an IDE perspective, should I copy the "Tug1.h" (the vessel in question) file to the MFD project folder?

Again, thanks for taking the time to help.

Cheers!!
 

Face

Well-known member
Orbiter Contributor
Addon Developer
Beta Tester
Joined
Mar 18, 2008
Messages
4,403
Reaction score
581
Points
153
Location
Vienna
the general idea of a MFD<->Vessel connection is, that you know the signature of the Vessel, so the MFD can "connect" to it and call a (or several) public methods.

That's a bit misleading. You can't call public methods of a class implemented in a different DLL just by using the declaration (header file). You'll have to either declare the public methods virtual - so there is a method pointer in the declaration struct - or export and import the methods explicitly.

The OP talked about a flag - supposedly a field - in the class structure. This would be no problem with the header include approach. Calling public methods is another problem.

---------- Post added at 08:56 ---------- Previous post was at 08:53 ----------

As per the "class header" (vessel.h) method, from an IDE perspective, should I copy the "Tug1.h" (the vessel in question) file to the MFD project folder?

You can do that, link it or use relative include syntax. I'd suggest a way that involves only one copy of the header file, because otherwise you'd have to keep the copies in sync in order to avoid strange compiler errors.
 

kuddel

Donator
Donator
Joined
Apr 1, 2008
Messages
2,064
Reaction score
507
Points
113
That's a bit misleading. You can't call public methods of a class implemented in a different DLL just by using the declaration (header file). You'll have to either declare the public methods virtual - so there is a method pointer in the declaration struct - or export and import the methods explicitly.
Sure, else the linker wouldn't be happy ;) DLL-export/import was what I had in mind, but missed to stress that.

But that was just a kind of "general idea" code. A compilable example would introduce more confusion than it would shed light on the question (I thought). At least if posted here...

Maybe a "real" example can help. I would recommend NASSP (with it's Connector classes), but again that introduces yet another layer that might hide the basic idea.

A extremely basic project/solution ("Hello World"-MFD & -Vessel) would definitely be better and also very helpful for future developers/questions....any volunteers? :p

I'd suggest a way that involves only one copy of the header file, because otherwise you'd have to keep the copies in sync in order to avoid strange compiler errors.

Yes I highly recommend that, too. You should release the MFD and the Vessel as "one pack" anyway, so having only one -correct- header avoids lots of additional strangeness :thumbup:
 

Face

Well-known member
Orbiter Contributor
Addon Developer
Beta Tester
Joined
Mar 18, 2008
Messages
4,403
Reaction score
581
Points
153
Location
Vienna
A extremely basic project/solution ("Hello World"-MFD & -Vessel) would definitely be better and also very helpful for future developers/questions

For a similar question, I've once done so: https://www.orbiter-forum.com/showthread.php?p=546050&postcount=16 . Within it, you'll find examples for the include approach as well as the clbkGeneric approach. It is for a different use-case, though, and I never got much feedback.

Perhaps that's because such basic examples posted in specific question threads are not easily found by future developers with the same or similar questions. Therefore I don't see much sense in doing it here again.
 
Last edited:

slaver0110

Member
Joined
Mar 21, 2011
Messages
72
Reaction score
2
Points
6
The OP talked about a flag - supposedly a field - in the class structure. This would be no problem with the header include approach. Calling public methods is another problem.

Yes, the class method in question is defined in the vessel class, under Public.
Code:
void changePodFlag();

The method is is as follows:
Code:
void Tug1::changePodFlag()
{
	if (PodFlag == 0) { PodFlag = 1; }
	else if (PodFlag == 1) { PodFlag = 0; }
}

I've started playing with the "#include "Tug1.h", using Kuddel's example pseudo-code to work by. As I stated before, I'm slowly piecing together how this is supposed to work; after an hour or so, I finally got the compiler to stop screaming at me...:thumbup:

Kuddel, I'm not sure what you meant by this (from the code example above)
Code:
void sendToVessel (msg_t *msg) {
    if (m_pVessel) {
      m_pVessel->ReceiveMessage(msg);
    }

I'm at the point where I can call the "changePodFlag" method from the MFD code, but that doesn't affect the flag in the simulation.
I'm still on it, and again, I thank everybody for putting in the time to help.

Cheers!
 

Face

Well-known member
Orbiter Contributor
Addon Developer
Beta Tester
Joined
Mar 18, 2008
Messages
4,403
Reaction score
581
Points
153
Location
Vienna
Yes, the class method in question is defined in the vessel class, under Public.

But I wrote about a field, not a method. If your method is not declared virtual, you can't call it across DLL boundaries, because the linker wouldn't know what to do. You can manipulate public fields, though.
 

slaver0110

Member
Joined
Mar 21, 2011
Messages
72
Reaction score
2
Points
6
Okay, I've been arguing with the MFD code for a few days now, and alas, I can't seem to make it work.

My own fault, of course. I think that I got too ambitious too soon.:facepalm:
The help from the Board Members here has been extraordinary, and I really appreciate everyone taking the time to help.

The bottom line is, I'm just not far enough into learning C++ to understand all the casting and virtual-methods yet. Sooner or later, I'll get it.

However, I'm not giving up...:)

For now, the flag I need to change is easily done from the Vessel side, so I have that to work with for the time being.

I've gone through many of the sample-MFDs code, and I just can't find anything clear enough that I can pick-apart for the sake of understanding.
It's not just calling the "changePod" method that this is about, but once I understand how that works (read: how the MFD communicates with the vessel in simple terms) I can apply the same principle to other vessel-functions, and hopefully gain a further insight into how this all works together.

When last I left off, I had the "Tug1.h" included in the MFD code. After that, it became a mess of trying to parse what I was being instructed to do, as well as the constant complaints from the compiler in my direction). Again, just inexperience on my part, to be sure; I'm still at the "I need it explained to me in Dr.Seuss terms" phase of learning the language.

At one point, I was able to call the "changePod" method from the MFD code, but in the simulator it didn't affect the vessel. So, I started fresh.

Again, thanks for all the help thus far. I really appreciate all the input from everyone.

Cheers everyone!!:cheers:
 

slaver0110

Member
Joined
Mar 21, 2011
Messages
72
Reaction score
2
Points
6
Eureka moment!!
Finally got it to work. Just a little patience, a lot of perseverance, and a six-pack.:cheers:

Thanks to Face and Kuddel, I've managed to parse it out, and as I figured, it was looking me in the face the whole time.

After adding the vessel-header to the MFD code, the rest was surprisingly simple.

MFD header:
Code:
Tug1 *pVessel;
OBJHANDLE hVessel;
VESSEL *v;

CPP file:
Code:
v = oapiGetFocusInterface();
pVessel = static_cast<Tug1*>(v);

Thanks to everyone for the help, and the examples.
I can't yet successfully call a vessel-method, but I'm looking into Face's advice about setting the methods virtual.

Cheers everyone!!
 
Top