untoldwind / KontrolSystem2

Autopilot scripting system for KSP2
Other
54 stars 15 forks source link

Is lost of communication an issue? #139

Closed PhilouDS closed 5 months ago

PhilouDS commented 8 months ago

Hi, I tried to run my script to go to the Mun. When I start my transfer burn, I have no connection with KSC. I don't know if it's related but my staging script doesn't work during that burn! I had no problem during the gravity turn nor during the circularization.

FYI: I'm using the mod CommNext (https://spacedock.info/mod/3560/CommNext). I don't know if it's relevant.

It could be a problem with my craft.

PhilouDS commented 8 months ago

The problem was from my transfer script sorry.

lefouvert commented 8 months ago

Hum, nice, I was seeking for a worthy successor of Remote Tech. Thank you ^^. My Sat Constellation Launcher will be usefull.

untoldwind commented 8 months ago

On that note: Creating bindings for the CommNet is on the todo list. Still trying to figure out how it is working internally though

PhilouDS commented 8 months ago

On that note: Creating bindings for the CommNet is on the todo list. Still trying to figure out how it is working internally though

okay... it seems that my stage problem comes from wait_until(fn() -> vessel.staging.ready) when I lose the communication.

Also, activating the sas with vessel.autopilot.enabled = true doesn't seem to work when the vessel is out of communication. It's less important but good to know. (I try to don't use SAS. In KSP1, some probes don't have SAS (or a limited one). Maybe, this will be the case in KSP2.)

PhilouDS commented 8 months ago

Back again here. Even without wait_until(fn() -> vessel.staging.ready), I have problem sometimes to stage without communication. This is my script.

pub fn check_staging (vessel: Vessel) -> Unit = {
  let check = false
  const engine_in_stage = list_engine_stage(vessel, vessel.staging.current)
  if (vessel.staging.current > 0) {
    if (engine_in_stage.length == 0 ||
      engine_in_stage.filter(fn(eng) -> eng.has_ignited && eng.is_flameout).length != 0) {
      check = true
    }
  }
  yield()
  if (!check && engine_in_stage.filter(fn(eng) -> !eng.has_ignited).length > 0) {
    check = true
  }
  yield()
  if (check) {
    CONSOLE.print_at(15,0,"Staging")
    yield()
    trigger_staging(vessel)
  }
  yield()
  CONSOLE.clear_line(15)
}

pub fn trigger_staging (vessel: Vessel) -> Unit = {
  //wait_until(fn() -> vessel.staging.ready)
  vessel.staging.next()
  yield()
}

fn list_engine_stage (vessel: Vessel, stage_num: int) -> ksp::vessel::ModuleEngine[] = {
  let list_engine: ksp::vessel::ModuleEngine[] = []
  for (prt in vessel.staging.parts_in_stage(stage_num)) {
    if (prt.is_engine) {
      list_engine += prt.engine_module.value
    }
  }
  list_engine
}

Last time the staging didn't occur, it was weird because the CONSOLE.print_at(15,0,"Staging") blinked. That means that the line was printed and then cleared. So trigger_staging actually wasn't trigger. Maybe the lack of communication, maybe something else. Sometimes, it seems random.

lefouvert commented 8 months ago

As far as I know, lost of communication 'issue' is an expected behavior. It's as if the KSC made the computations and send result orders to your probe, which one trigger action on the vessel.

For KOs analogy, it's like if you run your script from Archive.

I don't think KS is think to be one or other way, but it link your script to actual game input. And if you dont have a Kerbal on board, without a working communication web, you can't control an occluded probe. Like without KS mod.

untoldwind commented 8 months ago

I have not checked it yet, but there is the vessel.is_controllable flag. To my understanding of the internal API that one should flip once the vessel is out of comm range.

So maybe wait_until(fn() -> vessel.staging.ready || !vessel.is_controllable) will do the trick? Of course then you still have to deal with an un-controllable vessel somehow ;)

PhilouDS commented 8 months ago

@untoldwind !vessel.is_controllable didn't work. The stage string didn't blink anymore. That means that the script was stuck in the wait_until loop. Using vessel.connection_status == ConnectionNodeStatus.Disconnected was better. The blink stage was here again so the script exists the wait_until loop but the staging is not triggered!

Some screenshots with this little test:

use { sleep, wait_until, yield, current_time } from ksp::game
use { CONSOLE } from ksp::console
use { CONSOLE_WINDOW } from ksp::ui
use { vec2 } from ksp::math
use { Vessel, AutopilotMode, ConnectionNodeStatus } from ksp::vessel

pub fn main_flight (vessel: Vessel) -> Unit = {
  CONSOLE.clear()

  CONSOLE_WINDOW.open()
  CONSOLE_WINDOW.size = vec2(500,600)

  vessel.autopilot.enabled = false
  vessel.autopilot.mode = AutopilotMode.Autopilot

  sleep(0.5)

  vessel.set_throttle(0)

  CONSOLE.print_line($"vessel controllable? {vessel.is_controllable}")
  CONSOLE.print_line($"connection status? {vessel.connection_status}")
  wait_until(fn() -> vessel.staging.ready || vessel.connection_status == ConnectionNodeStatus.Disconnected)

  const t0 = current_time()

  for (i in 0...100) {
    vessel.set_throttle(i)
    CONSOLE.print_at(3,0,i.to_string())
    sleep(0.05)
  }
}

First screenshot: full connection. No problem. image

The second screenshot shows that the vessel is controllable for KS2 but not for KSP. Indeed, I can't control the throttle or the direction manually. I can activate the SAS manually image

3rd screenshot. No more power in the batteries. I can't control anything manually, not even the SAS. KS2 says the vessel is controllable. image

Throttle: When the vessel is disconnected, KS2 can set the throttle only to 1 or 0!

My opinion:

lefouvert commented 8 months ago

@PhilouDS for KOs analogy : yes your script still running after communiction loss. But that was a part of the numerous memory leak. Once the script is in game loaded, it stay available while it shouldn't. That's how onboard script variables can be confused with Archive scripts variables. In short, most of us had happily exploited a bug. For me, a running script run from where it's loaded. Elsewhere, there is no point to have onboard script limitation. 5Ko hard drive, but infinite RAM ? Don't seems legit. Link it to real life : You can code new program for your probe from Earth, you can send it to your probe, but you can't exceed probe abilities. I promise you I would like to be able to run some software a place, and it «magicaly» act else where. In facts, it's possible, but this magic is supported by heavy networks ^^ Or commNet in KSP :P

For the 0-100 thrust toggle, isn't this related to the intermediate comm's status «poor connexion» which allow only a few controls ? (I don't think we have access to this state in KS). I believe there was something like this in KSP1 and I wouldn't be surprised if it was reintroduced in KSP2

For Kontrol System, as thoses considerations are not in the scope, I can only link it to game inputs abilities. You have network or kerbals in the vessel : it's fully controlable, you don't have at least one of them, you can't control your probe. Should it change ? Don't know.

What concerns me is that the script does not resume after reacquiring communications.

untoldwind commented 8 months ago

This is quite interesting, I guess I have to do some digging how the underlying VesselComponent.IsControllable is actually calculated. Maybe you can checkout if at least the vessel.control_status field gives proper results.

PhilouDS commented 8 months ago

@untoldwind the two results I had with control_status were Disconnected or Connected and they seem to be reliable during my test.

@lefouvert If I wanted to do like IRL, i'll play RP1 😅 but I see what you mean... and that means that it will be more difficult to create my first keostationary network. If I ask untoldwind to create parts like kOS, they will rip my head off 😂 It could be doable to patch the probes and capsule to had some memory value to load a script inside the memory probe and run it from the probe directly.

@untoldwind How does to2 load library? I try to explain: if I write use {function} from library to use a function. Does to2 "load it in the script" and when it sees it in the script, it uses it... Or, when it reaches a line using function, only then it looks at the line use {function} to see the origin library?

untoldwind commented 8 months ago

Ad 1): There are actually two status-fields on vessel vessel.control_status and vessel.connection_status (and the aforementioned vessel.is_controllable). I think at least one of these is somewhat redundant and probably should be dropped to avoid confusion.

Ad 2): There are two answers to this.

PhilouDS commented 8 months ago

With full control: image Communication lost: image No more power: image

So control_status seems to work well.

And I don't understand but throttle works well too. It changes from 0 to 1 without comnet, even without power... But in my script, it didn't 😠

for (i in 0...100) {
    vessel.set_throttle(i.to_float/100)
    CONSOLE.print_at(3,0,i.to_string())
    sleep(0.1)
  }
untoldwind commented 8 months ago

Internally vessel.set_throttle hooks up an autopilot that is active until released (i.e. the release method on the returned ThrottleManager).

In theory an autopilot should behave the same as regular pilot input ... but maybe there is something that bypasses the control_status check.

PhilouDS commented 8 months ago

What is the best to use in a while loop? set_throttle or manage_throttle. Are there big differences?

lefouvert commented 8 months ago

.set_throttle. manage_throttle take a function, which means it autoupdate, which is the purpose of your while loop.

update an autoupdate function in this context don't make sense. (I don't mean your question don't make sense, I guess you didn't know)

In most case, you can assume than something which take a function as parameter will autoupdate in function of... your function, and your function is supposed to describe the evolution of the value you would put into a set_something. So, except if your context changes, their is no point to update a manage_something.

untoldwind commented 8 months ago

If vessel.set_throttle is called the previously hooked up ThrustManager is updated (i.e. there should not be multiple autopilots concurrent autopilots).

Though the preferred pattern is this.

const throttle_manager = vessel.manage_throttle(fn(deltaT) -> ... some magic code to calculate the desired thrust ...)

... some code to wait for the some criteria ...

throttle_manager.release() // Throttle control is given back to the pilot (should reset to 0)

Alternative pattern with while loop:

const throttle_manager = vessel.set_throttle(0.5) // set some starting throttle, e.g. 0.5 = half thrust

while(... some condition ...) {
  throttle_manager.throttle = ... updated throttle ...
  yield() // or sleep()
}

throttle_manager.release() // Throttle control is given back to the pilot (should reset to 0)
PhilouDS commented 8 months ago

Okay, I see. Thanks.

2 more questions 😅 1- What's set_throttle_provider for? 2- What's deltaT for in manage_throttle? Is this for the time? Can I use it inside the magic code?

untoldwind commented 8 months ago

If you create a the ThrottleManager via vessel.manage_throttle(...), the set_throttle_provider(...) is a way to replace the original function with a new one. As the name suggests: throttle-provider is a function that returns/provides the desired throttle.

The deltaT is the time that has passed since the last call of that function, which is helpful if you are using some kind of PID loop to calculate a throttle.

Some "simple" examples from the std:: lib. E.g.

github-actions[bot] commented 6 months ago

This issue is stale because it has been open for 60 days with no activity.

github-actions[bot] commented 5 months ago

This issue was closed because it has been inactive for 14 days since being marked as stale.