Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

looking for a way to wait until science experiements are ready #125

Closed
fbl100 opened this issue Feb 3, 2024 · 24 comments
Closed

looking for a way to wait until science experiements are ready #125

fbl100 opened this issue Feb 3, 2024 · 24 comments
Labels
question Further information is requested stale Issue has not seen activity for 60 days

Comments

@fbl100
Copy link

fbl100 commented Feb 3, 2024

I've searched through the docs and have yet to figure out how to determine when science experiments are available to run. Obviously the game knows this (the science icon flashes).

I'm hoping to write a script that will automatically start the experiments when the conditions have been met. This is probably not an issue in the code, per se, but in my inability to figure it out from the docs. Any help would be appreciated.

@lefouvert
Copy link

Hi,
Not a solution, but some ideas :

With this ship :
scienceShip
(missing the Z-200 battery in the parts' pic)
I would try this code :

use { CONSOLE } from ksp::console
use { Vessel, ModuleScienceExperiment } from ksp::vessel
use { wait_until, yield } from ksp::game
use { round } from core::math

/// Entry Point
pub fn main_flight(vessel: Vessel) -> Result<Unit, string> = {
    CONSOLE.clear()

    // https://kontrolsystem2.readthedocs.io/en/latest/reference/ksp/vessel.html#part
    const science_modules = vessel.parts
        .filter(fn(part) -> part.science_experiment.defined)
        .map(fn(part) -> part.science_experiment.value)

    // get parachutes
    const parachutes = vessel.parts
        .filter(fn(part) -> part.parachute.defined)
        .map(fn(part) -> part.parachute.value)

    // take off
    vessel.set_throttle(0.55)
    vessel.staging.next()
    wait_until(fn() -> vessel.staging.ready)

    // while going up
    while(round( vessel.up.dot(vessel.surface_velocity) ) >= 0) {
        if(vessel.engines.exists(fn(engine) -> engine.is_flameout)) { // drop engine
            vessel.staging.next()
            wait_until(fn() -> vessel.staging.ready)
        }

        // if their is experiences not running
        if(science_modules.exists(fn(science_module) -> !science_module.is_deployed))
            doScience(science_modules)

        yield() // let Unity do things
    }

    // arm parachutes
    for(parachute in parachutes)
        parachute.armed = true

    return Ok({})
}

fn doScience(science_modules: ModuleScienceExperiment[]) -> bool = {
    // https://kontrolsystem2.readthedocs.io/en/latest/reference/ksp/science.html
    for(science_module in science_modules) {
        return science_module.experiments
            .filter(fn(experiment) -> experiment.condition_met) // experience to do
            .map(fn(runnable_experiment) -> runnable_experiment.run_experiment()) // run experiment and get status being run
            .exists(fn(experiment_running) -> experiment_running) // search for at least one experiment being run
    }
    return false // no expriment to run have been found
}

Should give you a straight vertical flight until 80km apo, then let you fall safely on the ground. Enought to play with GSCM-01 and ASCM-A.

However, as I'm playing in sandbox mode, the science light never blink. I assume it's because researches are already acquired. It implies experiment.condition_met is always false, so experiments never run.
You could also look at experiment.current_experiment_state == ExperimentState.READY and experiment.current_experiment_state != ExperimentState.ALREADYSTORED (dont forget to use { ExperimentState } from ksp::science) to launch your experiments, however, as experiment.condition_met, experiment.current_experiment_state is always true, at least in Sandbox mode. I bet this is not the case in campain mode.

Have fun :)

@untoldwind untoldwind added the question Further information is requested label Feb 5, 2024
@untoldwind
Copy link
Owner

untoldwind commented Feb 5, 2024

The bindings to the science modules are still new, so I am still not entirely sure how everything behaves.
The condition_met flag is taken directly from the internal data-structure of the game, maybe there is some update needed I am not aware of.
In the meantime: Maybe you can check the current_experiment_state value of the experiment. I guess experiment.current_experiment_state == ExperimentState.READY means that it is ready to go?

@fbl100
Copy link
Author

fbl100 commented Feb 5, 2024

Thank you for the responses. I will give this a try.

@fbl100
Copy link
Author

fbl100 commented Feb 7, 2024

I have been messing with this for a bit now and am becoming convinced that there's something missing that needs to be exposed. I've tried printing out all of the experiment variables and can't find anything that correlates to when the blue science light flashes.

My setup:

  • Brand new save w/ no science earned.
  • Simple rocket (pod + fuel tank + engine)
  • On the launch pad, I run my script. Crew Observations are performed (yay!)
  • Launch the rocket and monitor the console
  • No variables change, even though I'm now in the atmosphere and can click on the science button to do another crew observation

I was expecting/hoping that a variable on the crew observation would flip as soon as I got into the atmosphere.

Here's my script. The intent is just to run in a loop and execute science when available. Note that the 'can_run' function is just what I was using last. I've tried lots of combinations to no avail.

use { Vessel, ModuleScienceExperiment } from ksp::vessel
use { wait_until, yield } from ksp::game
use { round } from core::math
use { ExperimentState, Experiment, ResearchLocation } from ksp::science
use { format } from core::str

/// Entry Point
pub fn main_flight(vessel: Vessel) -> Result<Unit, string> = {
    CONSOLE.clear()

    // https://kontrolsystem2.readthedocs.io/en/latest/reference/ksp/vessel.html#part
    const science_modules : ModuleScienceExperiment[] = vessel.parts
        .filter(fn(part) -> part.science_experiment.defined)
        .map(fn(part) -> part.science_experiment.value)

    while(true) {
        CONSOLE.clear()
        const experiment = find_experiment(science_modules)
        if(experiment.defined) {
            CONSOLE.print_line("RUNNING EXPERIMENT: " + experiment.value.definition.id)
            experiment.value.run_experiment()
            wait_until(fn () -> experiment.value.current_running_time < 0)
        }
        yield()
    }
}

sync fn find_experiment(science_modules: ModuleScienceExperiment[]) -> Option<Experiment> = {
    for(science_module in science_modules) {
        for(experiment in science_module.experiments) {
            printScience(experiment)
            if(can_run(experiment)) {
                return Some(experiment)
            } 
        }
    }
    return None()
}

sync fn can_run(experiment : Experiment) -> bool = {
    if(experiment.current_experiment_state == ExperimentState.READY && 
       experiment.current_running_time >= 0) {
        return true
    }
    return false
}

sync fn printScience(experiment : Experiment) -> Unit = {
    CONSOLE.print_line(experiment.definition.id)
    CONSOLE.print_line("-----------------------------")
    CONSOLE.print_line(format("  --> Time to Complete: {0:N2}", experiment.time_to_complete))
    CONSOLE.print_line(format("  --> condition_met: {0}", experiment.condition_met))
    CONSOLE.print_line(format("  --> crew_required: {0}", experiment.crew_required))
    CONSOLE.print_line(format("  --> current_experiment_state: {0}", experiment.current_experiment_state))
    CONSOLE.print_line(format("  --> current_running_time: {0:N2}", experiment.current_running_time))
    CONSOLE.print_line(format("  --> previous_experiment_state: {0}", experiment.previous_experiment_state))
    CONSOLE.print_line(format("  --> current_situation_is_valid: {0}", experiment.current_situation_is_valid))
    CONSOLE.print_line(format("  --> time_to_complete: {0}", experiment.time_to_complete))
    CONSOLE.print_line(format("  --> has_enough_resources: {0}", experiment.has_enough_resources))
    CONSOLE.print_line(format("  --> region_required: {0}", experiment.region_required))
}

One thing I noticed is that in the game UI, the blinky science button in in the 'Vessel Actions' panel with the lights, gear, solar panels, etc. With the 0.2 update is science a new action group? It's not listed as an action group in the docs, but I'm not familiar with what's available from the game.

@untoldwind
Copy link
Owner

... I did a bit of deeper digging and noticed that ConditionMet seems to have a different meaning is only updated/changed infrequently. I will probably drop it from the API for now.
The current_exeriment_state is somewhat surprising though and require a bit more testing

@untoldwind
Copy link
Owner

Short observation:
I added this to printScience:

    CONSOLE.print_line(format("  --> region_required: {0}", experiment.region_required))
    if (Some(location) = experiment.experiment_location) {
        CONSOLE.print_line(format("  --> experiment_location.body_name: {0}", location.body_name))
        CONSOLE.print_line(format("  --> experiment_location.science_region: {0}", location.science_region))
        CONSOLE.print_line(format("  --> experiment_location.science_situation: {0}", location.science_situation))
    }
}

It looks like experiment.experiment_location is initially empty and filled with information of the previous run.

In the API I found a field to get the science/research_location the vessel currently is in, i.e. this will work with the next patch:

        const vessel_location = vessel.research_location
        CONSOLE.print_line(format("  --> vessel.location.body_name: {0}", vessel_location.body_name))
        CONSOLE.print_line(format("  --> vessel.location.science_region: {0}", vessel_location.science_region))
        CONSOLE.print_line(format("  --> vessel.location.science_situation: {0}", vessel_location.science_situation))

So I guess the logic would be:
Experiment shall run if:

  • experiment.current_experiment_state == READY
    • and
      • experiment.experiment_location is empty
      • or experiment.experiment_location is defined but differs from vessel.research_location

This is a bit convoluted and I am still looking for a way to make the research inventory accessible as well.

@fbl100
Copy link
Author

fbl100 commented Feb 8, 2024

Thanks for taking a look. I agree that it's a bit convoluted, but.. early access.

Is the 'research inventory' that you're looking into the list of science/research that you've already completed?

I'll give this a whirl when the next patch comes out.

@untoldwind
Copy link
Owner

After some testing I made vessel.research_location as it might be undefined.
Here is some test code that should work with 0.5.2.7:

        if(Some( vessel_location) = vessel.research_location) {
            CONSOLE.print_line(format("  --> vessel.location.body_name: {0}", vessel_location.body_name))
            CONSOLE.print_line(format("  --> vessel.location.science_region: {0}", vessel_location.science_region))
            CONSOLE.print_line(format("  --> vessel.location.science_situation: {0}", vessel_location.science_situation))
        }

Additionally there is vessel.science_storage to access the research inventory and trigger transmission of experiment data.

    const storage = vessel.science_storage.value

    for(report in storage.research_reports) {
        CONSOLE.print_line(report.definition.id)
        CONSOLE.print_line(report.definition.display_name)
        CONSOLE.print_line(report.transmission_percentage.to_string())
        CONSOLE.print_line(report.time_required.to_string())
        CONSOLE.print_line(format("  --> experiment_location.body_name: {0}", report.research_location.body_name))
        CONSOLE.print_line(format("  --> experiment_location.science_region: {0}", report.research_location.science_region))
        CONSOLE.print_line(format("  --> experiment_location.science_situation: {0}", report.research_location.science_situation))
    }

    storage.start_transmit_all()

@lefouvert
Copy link

lefouvert commented Feb 20, 2024

version 0.5.2.8 (Ckan)

As I was boring last night, I take a look to science.
In anycase it could be usefull, with the same ugly vessel (Except, I change the Z-200 battery pack for a Z-1K battery pack) thoses functions seems to do the job.

_gitreport::basicscience.to2

use { CONSOLE } from ksp::console
use { Vessel, ModuleScienceExperiment, VesselSituation } from ksp::vessel
use { ExperimentState, Experiment, ResearchLocation, ResearchReport, ScienceSituation } from ksp::science
use { wait_until, yield } from ksp::game
use { round } from core::math

/// Entry Point
pub fn main_flight(vessel: Vessel) -> Result<Unit, string> = {
    CONSOLE.clear()

    // https://kontrolsystem2.readthedocs.io/en/latest/reference/ksp/vessel.html#part
    const science_modules = vessel.parts
        .filter(fn(part) -> part.science_experiment.defined)
        .map(fn(part) -> part.science_experiment.value)

    // get parachutes
    const parachutes = vessel.parts
        .filter(fn(part) -> part.parachute.defined)
        .map(fn(part) -> part.parachute.value)

    check_science(vessel)
    wait_until(fn() -> (!science_running(vessel)))
    check_science(vessel)

    // take off
    vessel.set_throttle(0.55)
    vessel.staging.next()
    wait_until(fn() -> vessel.staging.ready)



    // while going up
    while(round( vessel.up.dot(vessel.surface_velocity) ) >= 0) {
        if(vessel.engines.exists(fn(engine) -> engine.is_flameout)) { // drop engine
            vessel.staging.next()
            wait_until(fn() -> vessel.staging.ready)
        }

        check_science(vessel)

        yield() // let Unity do things
    }

    // arm parachutes
    for(parachute in parachutes)
        parachute.armed = true

    // while going down
    while(vessel.situation != VesselSituation.Landed){
        check_science(vessel)
        yield() // let Unity do things
    }

    // just in case anything new could be catch
    check_science(vessel)
    wait_until(fn() -> (!science_running(vessel)))
    check_science(vessel)

    CONSOLE.print_line(vessel.name + ": end of mission")
}

fn check_science(vessel: Vessel, autoSend: bool = true, verbose: bool = true) -> Unit = {
    const locationComparison = fn(locA: ResearchLocation, locB: ResearchLocation) -> {
        (locA.body_name == locB.body_name || locA.body_name.length == 0 || locB.body_name.length == 0)
        && (locA.science_region == locB.science_region || (!locA.requires_region) || (!locB.requires_region))
        && (locA.science_situation == locB.science_situation || locA.science_situation == ScienceSituation.None || locB.science_situation == ScienceSituation.None)
    }
    const storedReports =
        if(vessel.science_storage.defined)
            vessel.science_storage.value.research_reports
        else
            <ResearchReport>[]

    if(Some(shipLocation) = vessel.research_location) {
        const newExperiments = vessel.parts
            .filter(fn(p) -> p.science_experiment.defined)
            .map(fn(p) -> p.science_experiment.value.experiments
                .filter(fn(e) -> e.current_experiment_state == ExperimentState.READY
                    && ((!e.experiment_location.defined) || (!locationComparison(e.experiment_location.value, shipLocation))))
            ).reduce(<Experiment>[], fn(flat, arr) -> flat + arr)
            .filter(fn(e) -> !storedReports.exists(fn(r) -> r.definition.id == e.definition.id && (locationComparison(r.research_location, shipLocation))))
        for(e in newExperiments) {
            if(verbose)
                CONSOLE.print_line("Starting " + e.definition.id + " at " + shipLocation.body_name + "/" + shipLocation.science_situation.to_string() + "/" + shipLocation.science_region)
            e.run_experiment()
        }
    }
    if(autoSend) {
        for(r in storedReports.filter(fn(r) -> r.transmission_percentage == 0.0 && r.transmission_size > 0.0 && (!r.transmission_status))) {
            if(verbose)
                CONSOLE.print_line("Emitting " + r.definition.id + " from " + r.research_location.body_name + "/" + r.research_location.science_situation.to_string() + "/" + r.research_location.science_region)
            r.start_transmit()
        }
    }
}

sync fn science_running(vessel: Vessel) -> bool = {
    vessel.parts
        .filter(fn(p) -> p.science_experiment.defined)
        .map(fn(p) -> p.science_experiment.value)
        .exists(fn(m) -> m.experiments.exists(fn(e) -> e.current_experiment_state == ExperimentState.RUNNING))
}

It trigger research for any new compatible location.
Caution, it doesn't look at battery capacity, so it's easy to be short on juice.
I think the code is simple enought to split the emission part if needed.

Edit : typo

@untoldwind
Copy link
Owner

I guess the localComparison condition and the newExperiments filter could be add as helper functions to the std:: lib.

But first I have to do a bit more testing with the current pre-release (pretty much every file was touched by the refactoring session last weekend ;) )

@PhilouDS
Copy link
Contributor

Hi everyone!
I had a similar script (badly written because I don't know C# language and I didn't recall the .map and .filter method).
I didn't thought at the empty location as a condition to run an experiment! Nice touch! Thanks for that.

I'm going farther with the initial question: is it possible to know if an experiment was already conducted in a previous flight? KSP's latest version corrected that (the button doesn't blink anymore) when the experiment has been already done. Science Archive Mod also keeps track of the done experiment from one flight to an other. I assume something in KSP's API allows that.

Thanks a lot for that mod. More complex than kOS but I'm having lot of fun to figure out how things work!

@lefouvert
Copy link

To my knowledge, there is (still) no way to know what is already studied in KSC.
Note KOS scripts are not C#. As far as I know, the mod is written in C#, easiest way to talk to Unity, the game engine, but KOS script seems to be more 'Rust' inspired language (maybe I'm wrong, it's pure gessing)

As a candy, to wait, I can give you a well splited, slightly simpler version of the previous code :

use { CONSOLE } from ksp::console
use { Vessel, ModuleScienceExperiment, VesselSituation } from ksp::vessel
use { ExperimentState, Experiment, ResearchLocation, ResearchReport, ScienceSituation } from ksp::science
use { ResourceData } from ksp::resource
use { wait_until, yield } from ksp::game
use { round } from core::math

pub type ResourceConst = int

pub const Resource: (MP: ResourceConst, SF: ResourceConst, Air: ResourceConst, H: ResourceConst, CH4: ResourceConst, Ox: ResourceConst, EC: ResourceConst, Xe: ResourceConst) = (
    MP: 1,
    SF: 2,
    Air: 3,
    H: 6,
    CH4: 7,
    Ox: 8,
    EC: 12,
    Xe: 13
)

pub fn check_science(vessel: Vessel, autoSend: bool = true, verbose: bool = true) -> Unit = {
    const locationComparison = fn(locA: ResearchLocation, locB: ResearchLocation) -> {
        (locA.body_name == locB.body_name || locA.body_name.length == 0 || locB.body_name.length == 0)
        && (locA.science_region == locB.science_region || (!locA.requires_region) || (!locB.requires_region))
        && (locA.science_situation == locB.science_situation || locA.science_situation == ScienceSituation.None || locB.science_situation == ScienceSituation.None)
    }
    const storedReports =
        if(vessel.science_storage.defined)
            vessel.science_storage.value.research_reports
        else
            <ResearchReport>[]
    if(Some(shipLocation) = vessel.research_location) {
        const newExperiments = vessel.parts
            .filter(fn(p) -> p.science_experiment.defined)
            .map(fn(p) -> p.science_experiment.value.experiments
                .filter(fn(e) -> e.current_experiment_state == ExperimentState.READY
                    && e.current_experiment_state != ExperimentState.ALREADYSTORED
                    && ((!e.experiment_location.defined) || (!locationComparison(e.experiment_location.value, shipLocation))))
            ).reduce(<Experiment>[], fn(flat, arr) -> flat + arr)
        run_science(vessel, newExperiments, verbose)
    }
    if(autoSend)
        emmit_science(vessel, storedReports, verbose)
}

pub sync fn science_running(vessel: Vessel) -> bool = {
    vessel.parts
        .filter(fn(p) -> p.science_experiment.defined)
        .map(fn(p) -> p.science_experiment.value)
        .exists(fn(m) -> m.experiments.exists(fn(e) -> e.current_experiment_state == ExperimentState.RUNNING))
}

fn run_science(vessel: Vessel, experiments: Experiment[], verbose: bool = true) -> bool = {
    if(Some(shipLocation) = vessel.research_location) {
        if(verbose) {
            for (e in experiments)
                CONSOLE.print_line("Starting " + e.definition.id + " at " + shipLocation.body_name + "/" + shipLocation.science_situation.to_string() + "/" + shipLocation.science_region)
        }
        return experiments
            .map(fn(e) -> e.run_experiment())
            .exists(fn(done) -> done)
    }
    return false
}

fn emmit_science(vessel: Vessel, reports: ResearchReport[], verbose: bool = true) -> bool = {
    const toSendReports = reports
        .filter(fn(r) -> r.transmission_percentage == 0.0 && r.transmission_size > 0.0 && (!r.transmission_status))
        .reduce((reports: <ResearchReport>[], consumption: 0.0), fn(agg, r) -> if(agg.consumption + r.ec_required < available_power(vessel)) (reports: agg.reports + r, consumption: agg.consumption + r.ec_required) else (reports: agg.reports, consumption: agg.consumption))
    if(verbose) {
        for (r in toSendReports.reports)
                CONSOLE.print_line("Emitting " + r.definition.id + " from " + r.research_location.body_name + "/" + r.research_location.science_situation.to_string() + "/" + r.research_location.science_region)
    }
    return toSendReports.reports
        .map(fn(r) -> r.start_transmit()) // ugly, but not sure if exists short the search at the first true
        .exists(fn(done) -> done)
}

sync fn available_power(vessel: Vessel) -> float = {
    vessel.parts
        .reduce(<ResourceData>[], fn(flat, p) -> flat + p.resources.list.filter(fn(r) -> r.resource.id == Resource.EC))
        .reduce(0.0, fn(sum, r) -> sum + r.stored_units)
}

It consider to not emiting a report if EC resource are not enought.
It DON'T check for running experience since there (still ?) is no numeric data for the EC consumption (only a has_enough_resources which is not enough to know if multiple experiences are runnable.) or solar panels max supply.

I'm not totaly confident with the e.current_experiment_state != ExperimentState.ALREADYSTORED instead of .filter(fn(e) -> !storedReports.exists(fn(r) -> r.definition.id == e.definition.id && (locationComparison(r.research_location, shipLocation))) but it's simpler to write, to read, and did give good result last ten flight...
Had take a (short) look at ExperimentState.INVALIDLOCATION and ExperimentState.LOCATIONCHANGED and results are not convincing, so at the time, I'll keep the locationComparison lamda.

@PhilouDS
Copy link
Contributor

THanks for your script @lefouvert
I have one "solution" but I don't know how to do.
Each time an experiment is done and data transmitted, I want to write the name of that experiment (and its location/region...) in a txt file (a CSV file might be better).
Then, I just have to check if the science is in the text file. This way, I have access to all the science from flight to flight.
But I don't know how to create a new file and how to read it :(
Is it possible to do that with Kontrol System?

@lefouvert
Copy link

lefouvert commented Feb 24, 2024

The two only ways I found to wrote a file are

  • saving a telemetry series (ksp::telemetry::save_time_series), but you don't have the ability to read it back. (maybe i'll give it a try to have «blackbox logs» of my flights somedays)
  • write a debug message via core::logging functions, and still, you dont have the ability to read them.

IMHO, none of thoses ways are usable.

@untoldwind
Copy link
Owner

There are some (rough) guidelines what a KSP2 mod is allowed and not allowed to do, so reading and writing files has to be done with care. I guess the save_lime_series is already on the edge of a gray area in this regard.

What probably would be within the allowed bounds would be some kind of persistent key-value store somewhere in the module directory ... just like the scripts themselves. I think I will add this to the todo-list.

For the problem at hand though. For the next release I added a get_completed_research_reports() to ksp::science to access everthing that has been handed in so far.
From what I can tell internally the value of an experiment is matched via experiment_id, research_location_id and research_type (... made of those available as well).

@lefouvert
Copy link

@untoldwind Out of curiosity, where thoses guidelines can be found ?

@untoldwind
Copy link
Owner

I guess this one: https://forum.kerbalspaceprogram.com/topic/154851-add-on-posting-rules-april-13-2021/

@untoldwind
Copy link
Owner

untoldwind commented Feb 24, 2024

In 0.5.3.2 this should work:

use { Vessel } from ksp::vessel
use { get_completed_research_reports } from ksp::science
use { CONSOLE } from ksp::console

/// Entry Point
pub fn main_flight(vessel: Vessel) -> Result<Unit, string> = {
    CONSOLE.clear()

    const completed = get_completed_research_reports()

    for(report in completed) {
        CONSOLE.print_line(report.definition.id)
        CONSOLE.print_line(report.experiment_id)
        CONSOLE.print_line(report.research_location_id)
        CONSOLE.print_line(report.science_value.to_string())
    }
}

experiment_id and research_location_id should correspond to the id fields in ExperimentDefinition and ResearchLocation

@PhilouDS
Copy link
Contributor

It works very well! Fantastic! Thanks

@PhilouDS
Copy link
Contributor

PhilouDS commented Feb 25, 2024

It looks like you don't have access do the details (region, situation) of Research Location for the completed research report. So we can't ue the locationComparison from @lefouvert.
The id seems to be in the form body_situation_region or body_situation when region is not required. So I tried this:

fn exp_loc_id (craft: Vessel, exp: Experiment, short: bool = false) -> string = {
  let exp_body_id = craft.research_location.value.body_name
  
  let exp_situation_id = exp.valid_locations.filter(
    fn(loc) -> loc.id == ("_" + craft.research_location.value.science_situation.to_string())
  )[0].id
  
  let exp_region_id = "_" + craft.research_location.value.science_region

  if (short) {
    return (exp_body_id + exp_situation_id)
  }
  else {
    return (exp_body_id + exp_situation_id + exp_region_id)
  }
}

Then, I use this to compare an experiment with the experiment already reported:

fn reported_previous_flight (craft: Vessel, exp: Experiment) -> bool = {
  const science_completed = get_completed_research_reports()
  let rep_previous = false

  if (science_completed.length != 0) {
    for (rep in science_completed) {
      if ((rep.experiment_id == exp.definition.id) &&
          ((rep.research_location_id == exp_loc_id(craft, exp)) ||
          (rep.research_location_id == exp_loc_id(craft, exp, true)))) {
        rep_previous = true
      }
    }
  }

  return rep_previous
}

Because experiments done during the actual flight are not in the completed research reports, I have the same kind of function for the actual flight:

fn reported_this_flight (craft: Vessel, exp: Experiment) -> bool = {
  const research_inventory = craft.science_storage.value.research_reports
  let rep_this = false
  
  if (research_inventory.length != 0) {
    for (rep in research_inventory) {
      if ((rep.experiment_id == exp.definition.id) &&
          ((rep.research_location_id == exp_loc_id(craft, exp)) ||
          (rep.research_location_id == exp_loc_id(craft, exp, true)))) {
        rep_this = true
      }
    }
  }

  return rep_this
}

and I finish with this to know if the exp is new:

fn check_new_exp (craft: Vessel, exp: Experiment) -> bool = {
  return (!reported_previous_flight(craft, exp) && !reported_this_flight(craft, exp))
}

All seem to work but I didn't try with multiple experiments in Exploration mode.

@lefouvert
Copy link

lefouvert commented Feb 26, 2024

@untoldwind Did ResearchReport lost the ability .start_transmit() to be individualy transmited with v0.5.3.2 or v.0.5.3.3 ?
I only found the .start_transmit_all() on ScienceStorage.

@untoldwind
Copy link
Owner

Just realized that it accidentally dropped out during one of the refactoring.
Created a hotfix: 0.5.3.4 which should contain it again

Copy link

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

@github-actions github-actions bot added the stale Issue has not seen activity for 60 days label Apr 27, 2024
Copy link

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
question Further information is requested stale Issue has not seen activity for 60 days
Projects
None yet
Development

No branches or pull requests

4 participants