Angel-125 / KerbalActuators

A plugin used to create VTOL engines and robotic parts
6 stars 5 forks source link

Add methods to WBIVTOLManager to query capabilities? #3

Open MOARdV opened 6 years ago

MOARdV commented 6 years ago

I've added a basic interface to MAS to support the WBI VTOL Manager (originally, this was going to be a bespoke interface with the flying saucer mod, but a generalized VTOL interface makes it useful for other vessels, too).

I've been able to control both the hover controller features and the thrust vector controller from IVA (including IVA flights from ground to orbit and back). However, for the sake of generalizing the VTOL manager interface in MAS, it would be helpful to have queries in WBIVTOLManager that MAS can use to know when certain features are supported in a particular vessel. These could be as simple as something like

bool HoverControllerEnabled()
{
  return HoverGUI.canDrawHoverControls;
}

That lets MAS query these up front when the VTOL manager is found to suppress commands for unsupported capabilities.

Angel-125 commented 6 years ago

That sounds like a great idea! What interfaces would you need? Some of that interface depends upon the capabilities of the vessel itself (for instance, my tilt-rotors have rotation controllers but the saucer does not). I tried to set up the VTOL Manager to be interface-based, so interfaces it supports are: IThrustVectorController - For setting forward/reverse thrust. The gravitic engine uses this. IHoverController - For hover engines like the gravitic engine IRotationController - Rotation controls for things like the Buffalo's tilt-rotors IAirParkController - Parks vessels in mid-air. ICustomController - Custom drawing code. Crazy Mode is handled by this.

The gravitic engine implements IThrustVectorController, IHoverController, and ICustomController. The mothership will have something like IWarpController too at some point. I don't know how MAS handles API functions, but I can expose some methods for Crazy Mode. Something like SetWarpDirection(WBIWarpDirections direction). Would that work?

MOARdV commented 6 years ago

What I've implemented so far is over here, but to summarize: I use reflection to get the public entry points for methods in WBIVTOLManager's IHoverController and IThrustVectorController, since that the VTOL Manager class manages the relevant controllers (and, by using WBIVTOLManager instead of connecting to the engines directly, I also keep the GUI synchronized to what MAS is asking the engines to do).

As for what I'd like added to WBIVTOLManager - the main thing right now is methods I can call to find out when the controllers actually are controlling something, such as

public bool HoverControllerActive()
{
  return (hoverControllers == null || hoverControllers.Length == 0) ? false : true;
}

with similar methods for the other controllers. That way, I can ask WBIVTOLManager what interfaces it is controlling, and the IVA developer can show or hide features in the IVA depending on what capabilities are available.

For instance, I've got an MFD page I'm developing that is configured to operate the IThrustVectorController and IHoverController, but it'd be nice to suppress that page if it's not applicable. Or, in the case of the Buffalo tilt-rotor - the MFD could configure itself differently for that craft if it knows a rotating engine was installed.

MOARdV commented 6 years ago

The original feature request is something I can do in MAS by simply looking for the appropriate part modules (basically, the same thing WBIVTOLManager does). It's not expensive to do, since it only needs updated when the vessel changes, so I suppose that feature can be skipped.

One thing I can't do easily from MAS, though, is to ask what the current, min, and max rotations for parts implementing IRotationController are. It would be nice if IRotationController had methods to query min, max, and current rotation positions, so MAS can report that info (it would allow graphical representation of current tilt positions on an MFD, for instance).

Angel-125 commented 6 years ago

Ok, these are things I can do this week. Started a new job so things are slow at the moment but I can add methods to query the capabilities that WBIVTOLManager currently has as well as query methods for the rotation controller. Do you need anything specific from the gravitic engine while I'm poking around in the code? :)

Angel-125 commented 6 years ago

For IRotationController: ///

/// Returns the minimum allowed rotation. /// /// A float containing the minimum allowed rotation, in degrees. float GetMinRotation();

    /// <summary>
    /// Returns the minimum allowed rotation.
    /// </summary>
    /// <returns>A float containing the minimum allowed rotation, in degrees.</returns>
    float GetMaxRotation();

    /// <summary>
    /// Returns the current rotation.
    /// </summary>
    /// <returns>A float containing the current rotation, in degrees.</returns>
    float GetCurrentRotation();

For WBIVTOLManager: public bool HoverControllerActive() { return (hoverControllers == null || hoverControllers.Length == 0) ? false : true; }

    public bool RotationControllerActive()
    {
        return (rotationControllers == null || rotationControllers.Length == 0) ? false : true;
    }

    public bool ThrustVectorControllerActive()
    {
        return (thrustVectorControllers == null || thrustVectorControllers.Length == 0) ? false : true;
    }

    public bool AirParkControllerActive()
    {
        return (airParkController == null) ? false : true;
    }

I'm out of time this morning but I need to amend the above so that when there's no restrictions on min/max rotation angle, it returns -1.0. I can also look into API improvements to the translation controller, magnet controller, and light controller. That would let you make IVA controls for robotic arms. :)

Angel-125 commented 6 years ago

One last thing: I'm struggling to get a working camera for my robot arms. If you're willing to shed some light on what I'm doing wrong, I'd greatly appreciate your help. :) With a working arm camera, you could then control the arm cameras from the IVA as well. :)

MOARdV commented 6 years ago

The gravitic engine crazy mode is going to be tricky, I think. Everything I've written so far talks to the WBI VTOL Manager class, since it marshals all of the data, and I want to ensure the GUI updates if I'm tweaking things programatically. For crazy mode, I'd need to have an interface on the gravitic engine that lets me query the current mode, whether crazy mode's available, and to select a mode.

I'd suggest that the no-restrictions rotation case could return -180 (minimum) or +180 (maximum), unless both minimum and maximum values are positive values.

I haven't looked at the other controllers yet (translation, magnet, light) - the flying saucer's kept me busy playing with interfaces (and actually playing around in the game, instead of only modding it) so far. :) But a robotic arm controller with would be pretty cool - I suspect some space shuttle enthusiasts would be interested in that.

As far as the camera - you mean getting WBICameraController working? Yeah, I can help with that - we can discuss it here, or in a PM on the KSP forum, whichever is convenient for you.

Angel-125 commented 6 years ago

Ok, I can add some infrastructure to the engine to help out. It implements IHoverController directly, and I can add an IWarpController interface for Crazy Mode as well. I kind of need that for the FTL engine anyway...

The rotation controller actually operates with positive values between 0 and 360; I wasn't considering +/- 180 degrees when I built the original version..

Yeah, I meant WBICameraController. The idea is that you can open a window from the part action context menu and you'll see a camera view (not in IVA mode). Ideally the camera can be also used in IVA if that's possible. I'm happy to switch to the forums for that as I'm not as good as keeping up with GitHub conversations.

Glad you're getting to have fun as well! :)

Angel-125 commented 6 years ago

I'll have to think about how to pass the crazy mode interface to VTOLManager to make it easier for you. Right now the engine implements ICustomController and hands off the GUI drawing to the object that implements it. That's how I handle all the servo controllers for the arm. But if I have a method that gives you all the ICustomControllers, then you could do something like: if (customController is IWarpController) warpController = (IWarpController)customController;

... and that'll get you what you need. :)

Angel-125 commented 6 years ago

For WBIVTOLManager: ` ///

/// Returns the custom controllers, if any. /// /// An array of ICustomController interfaces if the vessel has custom controllers, or null if not. public ICustomController[] GetCustomControllers()

    /// <summary>
    /// Returns the hover controllers, if any.
    /// </summary>
    /// <returns>An array of IHoverController interfaces if the vessel has hover controllers, or null if not.</returns>
    public IHoverController[] GetHoverControllers()

    /// <summary>
    /// Returns the rotation controllers, if any.
    /// </summary>
    /// <returns>An array of IRotationController interfaces if the vessel has rotation controllers, or null if not.</returns>
    public IRotationController[] GetRotationControllers()

    /// <summary>
    /// Returns the thrust vector controllers, if any.
    /// </summary>
    /// <returns>An array of IThrustVectorController interfaces if the vessel has thrust vector controllers, or null if not.</returns>
    public IThrustVectorController[] GetThrustVectorControllers()

    /// <summary>
    /// Returns the Air Park controller, if any.
    /// </summary>
    /// <returns>An IAirParkController interface if the vessel has an air park controller, or null if not.</returns>
    public IAirParkController GetAirParkController()

`

Angel-125 commented 6 years ago

One more for convenience: /// <summary> /// Returns an array of IGenericController interfaces if there are any. IGenericController is the base interface for controllers like IHoverController and IRotationController. /// </summary> /// <returns>An array containing IGenericController interfaces if there are any controllers, or null if there are none.</returns> public IGenericController[] GetAllControllers() When I originally made KerbalActuators, I only had the rotation controller. Since then it's expanded with different interfaces, so to keep things from getting out of hand, I figured having an IGenericController interface that other controllers derive from would make it a bit easier. Then you can just query the array returned for specific interfaces. :)

Angel-125 commented 6 years ago

Ok, for Crazy Mode, I have:

` public interface IWarpController : IGenericController { ///

/// Returns the current warp direction. /// /// A WBIWarpDirections enumerator describing the current warp direction. WBIWarpDirections GetWarpDirection();

    /// <summary>
    /// Sets the desired warp direction, but only if crazyModeUnlocked = true.
    /// </summary>
    /// <param name="direction">A WBIWarpDirections enumerator specifying the desired direction.</param>
    void SetWarpDirection(WBIWarpDirections direction);
}

/// <summary>
/// Derived from IWarpController, this interface is used to control Crazy Mode.
/// </summary>
public interface ICrazyModeController : IWarpController
{
    /// <summary>
    /// Determines whether or not Crazy Mode has been unlocked.
    /// </summary>
    /// <returns></returns>
    bool IsCrazyModeUnlocked();
}

`

MOARdV commented 6 years ago

The rotation controller actually operates with positive values between 0 and 360; I wasn't considering +/- 180 degrees when I built the original version..

Okay, That's fine - as long as I know the range of values that are expected, I can interpret them correctly.

/// Returns the hover controllers, if any.

I don't know that all of these accessors are needed in my case, beyond determining if they're null or a length greater than zero. What I'm doing is using the methods in WBIVTOLManager directly. I do that for two reasons:

For the custom module interface, as long as the interfaces in the custom modules (IWarpController and ICrazyModeController) also poke the values used in the UI, that will work. It may be less important for Crazy Mode, since that's typically a short burst of activity, but for a long-term warp flight, the UI should reflect what MAS told it to do.

Angel-125 commented 6 years ago

For the gravitic engine's interfaces, the engine itself is responsible for drawing the controls. I had to do it that way because KerbalActuators doesn't know about the flying saucers mod. Anyway, when you control the engine through the ICrazyMode interface, the GUI will also be updated. So if you grab the first ICrazyMode that you find through GetCustomControllers or GetAllControllers, you'll be set- at least until the vessel changes somehow (I can add an event to the VTOL Manager to let you know if it updates its list of controllers since it already detects a vessel change event). You won't have to worry about updating all the other gravitic engines on the vessel; the engine will take care of that for you.

For all the other interfaces, using the VTOL Manager to control things is the right way to go; controls for those interfaces are drawn by the VTOL manager itself. I did that to handle a case where you could have more than one tilt-rotor on a craft and all the tilt-rotors would rotate in sync, but I only wanted one set of controls on the screen. Similarly, if you hit thrust reverse, all the engines will reverse thrust simultaneously.

In retrospect I'm not as thrilled about having the VTOL Manager contain state information, but it is admittedly convenient. What I'd do instead is have the various controllers work more like the gravitic engine where you update the first one you find and it'll update the others in the vessel that match its GroupID. Then you wouldn't worry about who has the state information.

MOARdV commented 6 years ago

So if you grab the first ICrazyMode that you find through GetCustomControllers or GetAllControllers, you'll be set- at least until the vessel changes somehow (I can add an event to the VTOL Manager to let you know if it updates its list of controllers since it already detects a vessel change event). You won't have to worry about updating all the other gravitic engines on the vessel; the engine will take care of that for you.

Okay, that sounds good. And don't worry about notifications - MAS already has to scan the parts list when the vessel changes to figure out which modules are onboard, so it's trivial to look for ICrazyMode instances as well.

IIRC, most of the VTOL Manager doesn't maintain state - I think it was just the hover manager vertical speed. And you could probably eliminate that by having a getter interface in the IHoverController and query it instead of keeping a local copy in the manager.