Noiredd / PEGAS

Powered Explicit Guidance Ascent System - a KSP & RO autopilot using the Space Shuttle guidance algorithm, UPFG
http://forum.kerbalspaceprogram.com/index.php?/topic/142213-pegas-powered-explicit-guidance-ascent-system-devlog
MIT License
114 stars 31 forks source link

How do I call a delegate? #40

Closed Tonas1997 closed 2 years ago

Tonas1997 commented 3 years ago

I'm trying to setup a custom function that shuts down vapor vents just prior to lift-off, but I can't get PEGAS to recognize it. As recommended by the tutorial, I defined my delegate within pegas.ks near the top of the file:

GLOBAL _PEGAS_VERSION_ IS "v1.3-alpha".

//  Check if all necessary variables have been defined, exit early otherwise.
RUN pegas_precheck.

// Delegates

FUNCTION shutvents
{
    FOR vent IN SHIP:PARTSTAGGEDPATTERN("pp.vv") 
    {
        radEng:SHUTDOWN.
    }
    WAIT 0.
}
SET aaa to shutvents@.

...

and tried to reference it on the boot file through the following sequence:

GLOBAL sequence IS LIST(
    LEXICON("time", -25, "type", "delegate", "function", aaa, "message", "Shutting down vapor vents"),
    LEXICON("time", -7, "type", "stage", "message", "RD-180 ignition"),
    LEXICON("time", 0, "type", "stage", "message", "LIFTOFF"),
    LEXICON("time", 90, "type", "stage", "message", "SRB jettison"),
    LEXICON("time", 180, "type", "jettison", "message", "PLF jettison", "massLost", 1108),
    LEXICON("time", 260, "type", "roll", "angle", 0)
).

This obviously won't compile because aaa is not defined at this point - the boot file runs before PEGAS. What am I doing wrong?

Noiredd commented 3 years ago

We have two problems here. First the obvious one that you noticed: we can't execute an expression referring a symbol that was not defined yet. Solution is seemingly easy: we simply have to define the delegate prior to running PEGAS. I don't know what's the structure of your code, but certainly you don't have to modify pegas.ks (nor should you, really). The way I do it, is to put everything in the boot file.

However, that only solves the problem partially. The second issue is due to some restrictions that kOS places on how you can use delegates, most importantly it prevents you from running a delegate in a different module than it was declared in. In simple terms, this means that if you define your delegate in, let's say, your boot file (and also put it into sequence there), and after that type run pegas. into console, it won't work - because "you typing something into console" and "your previously executed bootfile" are two separate scopes.

The practical solution is to define the delegate and run PEGAS from the same scope, be it your boot file or a regular script file that you execute from the console level.

Hope that helps!

Tonas1997 commented 3 years ago

Hmm, so there are two solutions (correct me if I'm wrong):

  1. Declare the delegate on my boot file and append these instructions to it:
RUN mission.
RUN pegas.

which doesn't allow me to control when the mission "starts" by delaying the execution of PEGAS, or

  1. Create a separate script that has the delegate definition and runs PEGAS, which wouldn't allow me to call the delegate from within sequence.

None of these seem ideal unless I could append to sequence from the separate script in option 2... right?

Patrykz94 commented 3 years ago

I'm pretty sure it would work with the way I use PEGAS.

My vehicle/sequence script is my boot script and in the beginning, before anything else runs, it brings up the terminal window, shows a message on the screen and waits for user input (I need to focus the terminal and press any key for it to continue).

Then it runs the mission file, displays mission parameters on the terminal (it's a good place to verify that I didn't screw something up with the mission) and waits for user input again.

After that, it runs pegas itself. So with this, it will effectively continously be executing from your boot file, while not forcing you to load mission and pegas immediately and it should hopefully let it use the delegate if I understand it correctly.

As for the terminal input code, here's the documentation on that: https://ksp-kos.github.io/KOS/structures/misc/terminalinput.html But you could use an action group as a trigger instead.

Tonas1997 commented 3 years ago

Ohh, so it's possible to condense everything in the boot file! I'll try and do that myself 😄

Noiredd commented 3 years ago
  1. Create a separate script that has the delegate definition and runs PEGAS, which wouldn't allow me to call the delegate from within sequence.

But you still can append (or, more likely, insert) to sequence in this option:

FUNCTION shutvents {
  // do stuff
}
sequence:INSERT(
  0,
  LEXICON("time", -25, "type", "delegate", "function", shutvents@, "message", "Shutting down vapor vents")
).
RUN pegas.

I have done it this way on occasion - you just need to be mindful where to insert the delegate event.

I kind of like Patryk's solution, but one significant issue with it is that you cannot alter the execution state this way - it only gives you delays on executing the whole thing, but you can't interrupt it to make some changes. Not the way you could in a usual scheme, where you can execute arbitrary commands after the boot file loads but before running PEGAS.

This is a mildly annoying thing to have to hack around, but some kOS fundamental limitations are like that.